]>
Commit | Line | Data |
---|---|---|
1b4f8140 | 1 | /* |
5e0de328 | 2 | * Copyright (C) 2009-2012 the libgit2 contributors |
1b4f8140 | 3 | * |
bb742ede VM |
4 | * This file is part of libgit2, distributed under the GNU GPL v2 with |
5 | * a Linking Exception. For full terms see the included COPYING file. | |
1b4f8140 | 6 | */ |
39cdf272 | 7 | #ifndef _WIN32 |
6e34111e VM |
8 | # include <sys/types.h> |
9 | # include <sys/socket.h> | |
10 | # include <sys/select.h> | |
31ffc141 | 11 | # include <sys/time.h> |
6e34111e | 12 | # include <netdb.h> |
66798ad0 | 13 | # include <arpa/inet.h> |
4e95ef02 | 14 | #else |
c1318f71 | 15 | # include <ws2tcpip.h> |
6e34111e | 16 | # ifdef _MSC_VER |
41fb1ca0 | 17 | # pragma comment(lib, "ws2_32") |
6e34111e | 18 | # endif |
4e95ef02 | 19 | #endif |
1b4f8140 | 20 | |
d3e1367f | 21 | #ifdef GIT_SSL |
a6f24a5b | 22 | # include <openssl/ssl.h> |
41fb1ca0 | 23 | # include <openssl/err.h> |
dbb36e1b | 24 | # include <openssl/x509v3.h> |
66024c7c | 25 | #endif |
6e34111e | 26 | |
dbb36e1b | 27 | #include <ctype.h> |
1b4f8140 CMN |
28 | #include "git2/errors.h" |
29 | ||
30 | #include "common.h" | |
31 | #include "netops.h" | |
34bfb4b0 | 32 | #include "posix.h" |
bd6585a7 CMN |
33 | #include "buffer.h" |
34 | ||
35 | #ifdef GIT_WIN32 | |
36 | static void net_set_error(const char *str) | |
37 | { | |
38 | int size, error = WSAGetLastError(); | |
39 | LPSTR err_str = NULL; | |
40 | ||
41 | size = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, | |
42 | 0, error, 0, (LPSTR)&err_str, 0, 0); | |
43 | ||
41fb1ca0 PK |
44 | GIT_UNUSED(size); |
45 | ||
65ca81a6 | 46 | giterr_set(GITERR_NET, "%s: %s", str, err_str); |
bd6585a7 CMN |
47 | LocalFree(err_str); |
48 | } | |
49 | #else | |
50 | static void net_set_error(const char *str) | |
51 | { | |
52 | giterr_set(GITERR_NET, "%s: %s", str, strerror(errno)); | |
53 | } | |
54 | #endif | |
1b4f8140 | 55 | |
d3e1367f | 56 | #ifdef GIT_SSL |
a6f24a5b CMN |
57 | static int ssl_set_error(gitno_ssl *ssl, int error) |
58 | { | |
59 | int err; | |
65ac67fb MS |
60 | unsigned long e; |
61 | ||
a6f24a5b | 62 | err = SSL_get_error(ssl->ssl, error); |
65ac67fb MS |
63 | |
64 | assert(err != SSL_ERROR_WANT_READ); | |
65 | assert(err != SSL_ERROR_WANT_WRITE); | |
66 | ||
67 | switch (err) { | |
68 | case SSL_ERROR_WANT_CONNECT: | |
69 | case SSL_ERROR_WANT_ACCEPT: | |
70 | giterr_set(GITERR_NET, "SSL error: connection failure\n"); | |
71 | break; | |
72 | case SSL_ERROR_WANT_X509_LOOKUP: | |
73 | giterr_set(GITERR_NET, "SSL error: x509 error\n"); | |
74 | break; | |
75 | case SSL_ERROR_SYSCALL: | |
76 | e = ERR_get_error(); | |
77 | if (e > 0) { | |
78 | giterr_set(GITERR_NET, "SSL error: %s", | |
79 | ERR_error_string(e, NULL)); | |
80 | break; | |
81 | } else if (error < 0) { | |
82 | giterr_set(GITERR_OS, "SSL error: syscall failure"); | |
83 | break; | |
84 | } | |
85 | giterr_set(GITERR_NET, "SSL error: received early EOF"); | |
86 | break; | |
87 | case SSL_ERROR_SSL: | |
88 | e = ERR_get_error(); | |
89 | giterr_set(GITERR_NET, "SSL error: %s", | |
90 | ERR_error_string(e, NULL)); | |
91 | break; | |
92 | case SSL_ERROR_NONE: | |
93 | case SSL_ERROR_ZERO_RETURN: | |
94 | default: | |
95 | giterr_set(GITERR_NET, "SSL error: unknown error"); | |
96 | break; | |
97 | } | |
a6f24a5b CMN |
98 | return -1; |
99 | } | |
66024c7c CMN |
100 | #endif |
101 | ||
b49c8f71 CMN |
102 | int gitno_recv(gitno_buffer *buf) |
103 | { | |
104 | return buf->recv(buf); | |
66024c7c CMN |
105 | } |
106 | ||
d3e1367f | 107 | #ifdef GIT_SSL |
8861d32f | 108 | static int gitno__recv_ssl(gitno_buffer *buf) |
a6f24a5b CMN |
109 | { |
110 | int ret; | |
111 | ||
112 | do { | |
41fb1ca0 PK |
113 | ret = SSL_read(buf->socket->ssl.ssl, buf->data + buf->offset, buf->len - buf->offset); |
114 | } while (SSL_get_error(buf->socket->ssl.ssl, ret) == SSL_ERROR_WANT_READ); | |
a6f24a5b | 115 | |
8861d32f CMN |
116 | if (ret < 0) { |
117 | net_set_error("Error receiving socket data"); | |
118 | return -1; | |
119 | } | |
a6f24a5b | 120 | |
8861d32f | 121 | buf->offset += ret; |
a6f24a5b | 122 | return ret; |
ea7a5452 | 123 | } |
a6f24a5b | 124 | #endif |
ea7a5452 | 125 | |
41fb1ca0 | 126 | static int gitno__recv(gitno_buffer *buf) |
ea7a5452 CMN |
127 | { |
128 | int ret; | |
129 | ||
41fb1ca0 | 130 | ret = p_recv(buf->socket->socket, buf->data + buf->offset, buf->len - buf->offset, 0); |
56b7df10 | 131 | if (ret < 0) { |
44ef8b1b | 132 | net_set_error("Error receiving socket data"); |
56b7df10 CMN |
133 | return -1; |
134 | } | |
ea7a5452 CMN |
135 | |
136 | buf->offset += ret; | |
ea7a5452 CMN |
137 | return ret; |
138 | } | |
139 | ||
e25dda51 | 140 | void gitno_buffer_setup_callback( |
41fb1ca0 | 141 | gitno_socket *socket, |
e25dda51 VM |
142 | gitno_buffer *buf, |
143 | char *data, | |
144 | size_t len, | |
145 | int (*recv)(gitno_buffer *buf), void *cb_data) | |
8861d32f | 146 | { |
8861d32f CMN |
147 | memset(data, 0x0, len); |
148 | buf->data = data; | |
149 | buf->len = len; | |
150 | buf->offset = 0; | |
41fb1ca0 | 151 | buf->socket = socket; |
8861d32f CMN |
152 | buf->recv = recv; |
153 | buf->cb_data = cb_data; | |
154 | } | |
155 | ||
41fb1ca0 | 156 | void gitno_buffer_setup(gitno_socket *socket, gitno_buffer *buf, char *data, size_t len) |
8861d32f CMN |
157 | { |
158 | #ifdef GIT_SSL | |
41fb1ca0 PK |
159 | if (socket->ssl.ctx) { |
160 | gitno_buffer_setup_callback(socket, buf, data, len, gitno__recv_ssl, NULL); | |
161 | return; | |
162 | } | |
8861d32f | 163 | #endif |
41fb1ca0 PK |
164 | |
165 | gitno_buffer_setup_callback(socket, buf, data, len, gitno__recv, NULL); | |
8861d32f CMN |
166 | } |
167 | ||
ea7a5452 | 168 | /* Consume up to ptr and move the rest of the buffer to the beginning */ |
c7c787ce | 169 | void gitno_consume(gitno_buffer *buf, const char *ptr) |
ea7a5452 | 170 | { |
0bd594b6 | 171 | size_t consumed; |
ea7a5452 | 172 | |
0bd594b6 | 173 | assert(ptr - buf->data >= 0); |
ea7a5452 CMN |
174 | assert(ptr - buf->data <= (int) buf->len); |
175 | ||
c7c787ce | 176 | consumed = ptr - buf->data; |
ea7a5452 | 177 | |
c7c787ce CMN |
178 | memmove(buf->data, ptr, buf->offset - consumed); |
179 | memset(buf->data + buf->offset, 0x0, buf->len - buf->offset); | |
180 | buf->offset -= consumed; | |
ea7a5452 CMN |
181 | } |
182 | ||
183 | /* Consume const bytes and move the rest of the buffer to the beginning */ | |
0bd594b6 | 184 | void gitno_consume_n(gitno_buffer *buf, size_t cons) |
ea7a5452 CMN |
185 | { |
186 | memmove(buf->data, buf->data + cons, buf->len - buf->offset); | |
187 | memset(buf->data + cons, 0x0, buf->len - buf->offset); | |
188 | buf->offset -= cons; | |
189 | } | |
190 | ||
d3e1367f | 191 | #ifdef GIT_SSL |
89460f3f | 192 | |
41fb1ca0 PK |
193 | static int gitno_ssl_teardown(gitno_ssl *ssl) |
194 | { | |
195 | int ret; | |
196 | ||
89460f3f | 197 | do { |
41fb1ca0 | 198 | ret = SSL_shutdown(ssl->ssl); |
89460f3f CMN |
199 | } while (ret == 0); |
200 | if (ret < 0) | |
41fb1ca0 | 201 | return ssl_set_error(ssl, ret); |
89460f3f | 202 | |
41fb1ca0 PK |
203 | SSL_free(ssl->ssl); |
204 | SSL_CTX_free(ssl->ctx); | |
89460f3f CMN |
205 | return 0; |
206 | } | |
207 | ||
16768191 | 208 | /* Match host names according to RFC 2818 rules */ |
dbb36e1b CMN |
209 | static int match_host(const char *pattern, const char *host) |
210 | { | |
211 | for (;;) { | |
16768191 | 212 | char c = tolower(*pattern++); |
dbb36e1b CMN |
213 | |
214 | if (c == '\0') | |
215 | return *host ? -1 : 0; | |
216 | ||
217 | if (c == '*') { | |
218 | c = *pattern; | |
219 | /* '*' at the end matches everything left */ | |
220 | if (c == '\0') | |
221 | return 0; | |
222 | ||
16768191 CMN |
223 | /* |
224 | * We've found a pattern, so move towards the next matching | |
225 | * char. The '.' is handled specially because wildcards aren't | |
226 | * allowed to cross subdomains. | |
227 | */ | |
228 | ||
229 | while(*host) { | |
230 | char h = tolower(*host); | |
231 | if (c == h) | |
232 | return match_host(pattern, host++); | |
233 | if (h == '.') | |
234 | return match_host(pattern, host); | |
235 | host++; | |
dbb36e1b | 236 | } |
16768191 | 237 | return -1; |
dbb36e1b CMN |
238 | } |
239 | ||
16768191 | 240 | if (c != tolower(*host++)) |
dbb36e1b CMN |
241 | return -1; |
242 | } | |
243 | ||
244 | return -1; | |
245 | } | |
246 | ||
247 | static int check_host_name(const char *name, const char *host) | |
248 | { | |
249 | if (!strcasecmp(name, host)) | |
250 | return 0; | |
251 | ||
252 | if (match_host(name, host) < 0) | |
253 | return -1; | |
254 | ||
255 | return 0; | |
256 | } | |
257 | ||
41fb1ca0 | 258 | static int verify_server_cert(gitno_ssl *ssl, const char *host) |
dbb36e1b CMN |
259 | { |
260 | X509 *cert; | |
261 | X509_NAME *peer_name; | |
441df990 CMN |
262 | ASN1_STRING *str; |
263 | unsigned char *peer_cn = NULL; | |
3f9eb1e5 | 264 | int matched = -1, type = GEN_DNS; |
dbb36e1b | 265 | GENERAL_NAMES *alts; |
3f9eb1e5 CMN |
266 | struct in6_addr addr6; |
267 | struct in_addr addr4; | |
268 | void *addr; | |
441df990 CMN |
269 | int i = -1,j; |
270 | ||
41fb1ca0 | 271 | if (SSL_get_verify_result(ssl->ssl) != X509_V_OK) { |
0d5dce26 CMN |
272 | giterr_set(GITERR_SSL, "The SSL certificate is invalid"); |
273 | return -1; | |
274 | } | |
3f9eb1e5 CMN |
275 | |
276 | /* Try to parse the host as an IP address to see if it is */ | |
d1a69d0f | 277 | if (gitno_inet_pton(AF_INET, host, &addr4)) { |
3f9eb1e5 CMN |
278 | type = GEN_IPADD; |
279 | addr = &addr4; | |
280 | } else { | |
d1a69d0f | 281 | if(gitno_inet_pton(AF_INET6, host, &addr6)) { |
3f9eb1e5 CMN |
282 | type = GEN_IPADD; |
283 | addr = &addr6; | |
284 | } | |
285 | } | |
286 | ||
dbb36e1b | 287 | |
41fb1ca0 | 288 | cert = SSL_get_peer_certificate(ssl->ssl); |
dbb36e1b CMN |
289 | |
290 | /* Check the alternative names */ | |
291 | alts = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); | |
292 | if (alts) { | |
441df990 | 293 | int num; |
dbb36e1b CMN |
294 | |
295 | num = sk_GENERAL_NAME_num(alts); | |
296 | for (i = 0; i < num && matched != 1; i++) { | |
297 | const GENERAL_NAME *gn = sk_GENERAL_NAME_value(alts, i); | |
298 | const char *name = (char *) ASN1_STRING_data(gn->d.ia5); | |
299 | size_t namelen = (size_t) ASN1_STRING_length(gn->d.ia5); | |
300 | ||
3f9eb1e5 CMN |
301 | /* Skip any names of a type we're not looking for */ |
302 | if (gn->type != type) | |
dbb36e1b CMN |
303 | continue; |
304 | ||
3f9eb1e5 CMN |
305 | if (type == GEN_DNS) { |
306 | /* If it contains embedded NULs, don't even try */ | |
441df990 | 307 | if (memchr(name, '\0', namelen)) |
3f9eb1e5 CMN |
308 | continue; |
309 | ||
310 | if (check_host_name(name, host) < 0) | |
311 | matched = 0; | |
312 | else | |
313 | matched = 1; | |
314 | } else if (type == GEN_IPADD) { | |
315 | /* Here name isn't so much a name but a binary representation of the IP */ | |
316 | matched = !!memcmp(name, addr, namelen); | |
317 | } | |
dbb36e1b CMN |
318 | } |
319 | } | |
320 | GENERAL_NAMES_free(alts); | |
321 | ||
441df990 | 322 | if (matched == 0) |
0d5dce26 | 323 | goto cert_fail; |
441df990 | 324 | |
dbb36e1b CMN |
325 | if (matched == 1) |
326 | return 0; | |
327 | ||
328 | /* If no alternative names are available, check the common name */ | |
329 | peer_name = X509_get_subject_name(cert); | |
441df990 CMN |
330 | if (peer_name == NULL) |
331 | goto on_error; | |
332 | ||
333 | if (peer_name) { | |
334 | /* Get the index of the last CN entry */ | |
335 | while ((j = X509_NAME_get_index_by_NID(peer_name, NID_commonName, i)) >= 0) | |
336 | i = j; | |
337 | } | |
338 | ||
339 | if (i < 0) | |
340 | goto on_error; | |
341 | ||
342 | str = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(peer_name, i)); | |
343 | if (str == NULL) | |
344 | goto on_error; | |
345 | ||
346 | /* Work around a bug in OpenSSL whereby ASN1_STRING_to_UTF8 fails if it's already in utf-8 */ | |
347 | if (ASN1_STRING_type(str) == V_ASN1_UTF8STRING) { | |
348 | int size = ASN1_STRING_length(str); | |
349 | ||
350 | if (size > 0) { | |
351 | peer_cn = OPENSSL_malloc(size + 1); | |
352 | GITERR_CHECK_ALLOC(peer_cn); | |
353 | memcpy(peer_cn, ASN1_STRING_data(str), size); | |
354 | peer_cn[size] = '\0'; | |
355 | } | |
356 | } else { | |
357 | int size = ASN1_STRING_to_UTF8(&peer_cn, str); | |
358 | GITERR_CHECK_ALLOC(peer_cn); | |
359 | if (memchr(peer_cn, '\0', size)) | |
360 | goto cert_fail; | |
dbb36e1b CMN |
361 | } |
362 | ||
441df990 CMN |
363 | if (check_host_name((char *)peer_cn, host) < 0) |
364 | goto cert_fail; | |
365 | ||
366 | OPENSSL_free(peer_cn); | |
367 | ||
dbb36e1b | 368 | return 0; |
441df990 CMN |
369 | |
370 | on_error: | |
371 | OPENSSL_free(peer_cn); | |
41fb1ca0 | 372 | return ssl_set_error(ssl, 0); |
441df990 CMN |
373 | |
374 | cert_fail: | |
375 | OPENSSL_free(peer_cn); | |
376 | giterr_set(GITERR_SSL, "Certificate host name check failed"); | |
377 | return -1; | |
dbb36e1b | 378 | } |
dbb36e1b | 379 | |
41fb1ca0 | 380 | static int ssl_setup(gitno_socket *socket, const char *host, int flags) |
66024c7c | 381 | { |
a6f24a5b CMN |
382 | int ret; |
383 | ||
384 | SSL_library_init(); | |
385 | SSL_load_error_strings(); | |
41fb1ca0 PK |
386 | socket->ssl.ctx = SSL_CTX_new(SSLv23_method()); |
387 | if (socket->ssl.ctx == NULL) | |
388 | return ssl_set_error(&socket->ssl, 0); | |
a6f24a5b | 389 | |
41fb1ca0 PK |
390 | SSL_CTX_set_mode(socket->ssl.ctx, SSL_MODE_AUTO_RETRY); |
391 | SSL_CTX_set_verify(socket->ssl.ctx, SSL_VERIFY_NONE, NULL); | |
392 | if (!SSL_CTX_set_default_verify_paths(socket->ssl.ctx)) | |
393 | return ssl_set_error(&socket->ssl, 0); | |
a6f24a5b | 394 | |
41fb1ca0 PK |
395 | socket->ssl.ssl = SSL_new(socket->ssl.ctx); |
396 | if (socket->ssl.ssl == NULL) | |
397 | return ssl_set_error(&socket->ssl, 0); | |
a6f24a5b | 398 | |
41fb1ca0 PK |
399 | if((ret = SSL_set_fd(socket->ssl.ssl, socket->socket)) == 0) |
400 | return ssl_set_error(&socket->ssl, ret); | |
a6f24a5b | 401 | |
41fb1ca0 PK |
402 | if ((ret = SSL_connect(socket->ssl.ssl)) <= 0) |
403 | return ssl_set_error(&socket->ssl, ret); | |
a6f24a5b | 404 | |
41fb1ca0 | 405 | if ((GITNO_CONNECT_SSL_NO_CHECK_CERT & flags) || verify_server_cert(&socket->ssl, host) < 0) |
dbb36e1b CMN |
406 | return -1; |
407 | ||
a6f24a5b | 408 | return 0; |
d3e1367f | 409 | } |
41fb1ca0 PK |
410 | #endif |
411 | ||
412 | static int gitno__close(GIT_SOCKET s) | |
d3e1367f | 413 | { |
41fb1ca0 PK |
414 | #ifdef GIT_WIN32 |
415 | if (SOCKET_ERROR == closesocket(s)) | |
416 | return -1; | |
417 | ||
418 | if (0 != WSACleanup()) { | |
419 | giterr_set(GITERR_OS, "Winsock cleanup failed"); | |
420 | return -1; | |
421 | } | |
422 | ||
89460f3f | 423 | return 0; |
41fb1ca0 PK |
424 | #else |
425 | return close(s); | |
d3e1367f | 426 | #endif |
41fb1ca0 | 427 | } |
d3e1367f | 428 | |
41fb1ca0 | 429 | int gitno_connect(gitno_socket *s_out, const char *host, const char *port, int flags) |
1b4f8140 | 430 | { |
44ef8b1b | 431 | struct addrinfo *info = NULL, *p; |
1b4f8140 | 432 | struct addrinfo hints; |
44ef8b1b | 433 | GIT_SOCKET s = INVALID_SOCKET; |
41fb1ca0 PK |
434 | int ret; |
435 | ||
436 | #ifdef GIT_WIN32 | |
437 | /* on win32, the WSA context needs to be initialized | |
438 | * before any socket calls can be performed */ | |
439 | WSADATA wsd; | |
440 | ||
441 | if (WSAStartup(MAKEWORD(2,2), &wsd) != 0) { | |
442 | giterr_set(GITERR_OS, "Winsock init failed"); | |
443 | return -1; | |
444 | } | |
445 | ||
446 | if (LOBYTE(wsd.wVersion) != 2 || HIBYTE(wsd.wVersion) != 2) { | |
447 | WSACleanup(); | |
448 | giterr_set(GITERR_OS, "Winsock init failed"); | |
449 | return -1; | |
450 | } | |
451 | #endif | |
452 | ||
453 | /* Zero the socket structure provided */ | |
454 | memset(s_out, 0x0, sizeof(gitno_socket)); | |
1b4f8140 CMN |
455 | |
456 | memset(&hints, 0x0, sizeof(struct addrinfo)); | |
1b4f8140 | 457 | hints.ai_socktype = SOCK_STREAM; |
a8df98c6 | 458 | hints.ai_family = AF_UNSPEC; |
1b4f8140 | 459 | |
798e4d53 VM |
460 | if ((ret = p_getaddrinfo(host, port, &hints, &info)) < 0) { |
461 | giterr_set(GITERR_NET, | |
462 | "Failed to resolve address for %s: %s", host, p_gai_strerror(ret)); | |
cd58c15c | 463 | return -1; |
1b4f8140 CMN |
464 | } |
465 | ||
466 | for (p = info; p != NULL; p = p->ai_next) { | |
467 | s = socket(p->ai_family, p->ai_socktype, p->ai_protocol); | |
8d18f1f7 | 468 | |
ccc9872d | 469 | if (s == INVALID_SOCKET) { |
44ef8b1b RB |
470 | net_set_error("error creating socket"); |
471 | break; | |
1b4f8140 CMN |
472 | } |
473 | ||
44ef8b1b RB |
474 | if (connect(s, p->ai_addr, (socklen_t)p->ai_addrlen) == 0) |
475 | break; | |
1b4f8140 | 476 | |
44ef8b1b | 477 | /* If we can't connect, try the next one */ |
41fb1ca0 | 478 | gitno__close(s); |
44ef8b1b | 479 | s = INVALID_SOCKET; |
1b4f8140 CMN |
480 | } |
481 | ||
482 | /* Oops, we couldn't connect to any address */ | |
cd58c15c | 483 | if (s == INVALID_SOCKET && p == NULL) { |
44ef8b1b | 484 | giterr_set(GITERR_OS, "Failed to connect to %s", host); |
cd58c15c VM |
485 | return -1; |
486 | } | |
44ef8b1b | 487 | |
41fb1ca0 | 488 | s_out->socket = s; |
798e4d53 | 489 | p_freeaddrinfo(info); |
66024c7c | 490 | |
41fb1ca0 PK |
491 | #ifdef GIT_SSL |
492 | if ((flags & GITNO_CONNECT_SSL) && ssl_setup(s_out, host, flags) < 0) | |
66024c7c | 493 | return -1; |
41fb1ca0 PK |
494 | #else |
495 | /* SSL is not supported */ | |
496 | if (flags & GITNO_CONNECT_SSL) { | |
497 | giterr_set(GITERR_OS, "SSL is not supported by this copy of libgit2."); | |
498 | return -1; | |
499 | } | |
500 | #endif | |
66024c7c | 501 | |
cd58c15c | 502 | return 0; |
1b4f8140 | 503 | } |
4e95ef02 | 504 | |
d3e1367f | 505 | #ifdef GIT_SSL |
41fb1ca0 | 506 | static int gitno_send_ssl(gitno_ssl *ssl, const char *msg, size_t len, int flags) |
4e95ef02 | 507 | { |
0bd594b6 VM |
508 | int ret; |
509 | size_t off = 0; | |
4e95ef02 | 510 | |
41fb1ca0 PK |
511 | GIT_UNUSED(flags); |
512 | ||
4e95ef02 | 513 | while (off < len) { |
a6f24a5b | 514 | ret = SSL_write(ssl->ssl, msg + off, len - off); |
4deda91b | 515 | if (ret <= 0 && ret != SSL_ERROR_WANT_WRITE) |
a6f24a5b CMN |
516 | return ssl_set_error(ssl, ret); |
517 | ||
518 | off += ret; | |
41fb1ca0 | 519 | } |
ccc9872d | 520 | |
a6f24a5b CMN |
521 | return off; |
522 | } | |
66024c7c CMN |
523 | #endif |
524 | ||
41fb1ca0 | 525 | int gitno_send(gitno_socket *socket, const char *msg, size_t len, int flags) |
66024c7c CMN |
526 | { |
527 | int ret; | |
528 | size_t off = 0; | |
529 | ||
a6f24a5b | 530 | #ifdef GIT_SSL |
41fb1ca0 PK |
531 | if (socket->ssl.ctx) |
532 | return gitno_send_ssl(&socket->ssl, msg, len, flags); | |
66024c7c CMN |
533 | #endif |
534 | ||
535 | while (off < len) { | |
536 | errno = 0; | |
41fb1ca0 | 537 | ret = p_send(socket->socket, msg + off, len - off, flags); |
56b7df10 | 538 | if (ret < 0) { |
bd6585a7 | 539 | net_set_error("Error sending data"); |
56b7df10 CMN |
540 | return -1; |
541 | } | |
4e95ef02 CMN |
542 | |
543 | off += ret; | |
544 | } | |
545 | ||
44ef8b1b | 546 | return (int)off; |
4e95ef02 | 547 | } |
74bd343a | 548 | |
41fb1ca0 | 549 | int gitno_close(gitno_socket *s) |
bad53552 | 550 | { |
41fb1ca0 PK |
551 | #ifdef GIT_SSL |
552 | if (s->ssl.ctx && | |
553 | gitno_ssl_teardown(&s->ssl) < 0) | |
554 | return -1; | |
bad53552 CMN |
555 | #endif |
556 | ||
41fb1ca0 PK |
557 | return gitno__close(s->socket); |
558 | } | |
559 | ||
74bd343a CMN |
560 | int gitno_select_in(gitno_buffer *buf, long int sec, long int usec) |
561 | { | |
562 | fd_set fds; | |
563 | struct timeval tv; | |
564 | ||
565 | tv.tv_sec = sec; | |
566 | tv.tv_usec = usec; | |
567 | ||
568 | FD_ZERO(&fds); | |
41fb1ca0 | 569 | FD_SET(buf->socket->socket, &fds); |
74bd343a CMN |
570 | |
571 | /* The select(2) interface is silly */ | |
41fb1ca0 | 572 | return select((int)buf->socket->socket + 1, &fds, NULL, NULL, &tv); |
74bd343a | 573 | } |
db84b798 CMN |
574 | |
575 | int gitno_extract_host_and_port(char **host, char **port, const char *url, const char *default_port) | |
576 | { | |
577 | char *colon, *slash, *delim; | |
db84b798 CMN |
578 | |
579 | colon = strchr(url, ':'); | |
580 | slash = strchr(url, '/'); | |
581 | ||
56b7df10 | 582 | if (slash == NULL) { |
44ef8b1b RB |
583 | giterr_set(GITERR_NET, "Malformed URL: missing /"); |
584 | return -1; | |
56b7df10 | 585 | } |
db84b798 CMN |
586 | |
587 | if (colon == NULL) { | |
588 | *port = git__strdup(default_port); | |
589 | } else { | |
590 | *port = git__strndup(colon + 1, slash - colon - 1); | |
591 | } | |
56b7df10 | 592 | GITERR_CHECK_ALLOC(*port); |
db84b798 CMN |
593 | |
594 | delim = colon == NULL ? slash : colon; | |
595 | *host = git__strndup(url, delim - url); | |
56b7df10 | 596 | GITERR_CHECK_ALLOC(*host); |
db84b798 | 597 | |
56b7df10 | 598 | return 0; |
db84b798 | 599 | } |
d1a69d0f EB |
600 | |
601 | int gitno_inet_pton(int af, const char* src, void* dst) | |
602 | { | |
603 | /* inet_pton is only available in Windows Vista or later | |
604 | * mingw32 and cygwin give compile errors */ | |
605 | #ifndef GIT_WIN32 | |
606 | return inet_pton(af, src, dst); | |
607 | #else | |
608 | union { | |
609 | struct sockaddr_in6 sin6; | |
610 | struct sockaddr_in sin; | |
611 | } sa; | |
612 | size_t srcsize; | |
613 | ||
614 | switch(af) | |
615 | { | |
616 | case AF_INET: | |
617 | sa.sin.sin_family = AF_INET; | |
618 | srcsize = sizeof (sa.sin); | |
619 | break; | |
620 | case AF_INET6: | |
621 | sa.sin6.sin6_family = AF_INET6; | |
622 | srcsize = sizeof (sa.sin6); | |
623 | break; | |
624 | default: | |
625 | errno = WSAEPFNOSUPPORT; | |
626 | return -1; | |
627 | } | |
628 | ||
629 | if (WSAStringToAddress(src, af, NULL, (struct sockaddr *) &sa, &srcsize) != 0) | |
630 | { | |
631 | errno = WSAGetLastError(); | |
632 | return -1; | |
633 | } | |
634 | ||
635 | switch(af) | |
636 | { | |
637 | case AF_INET: | |
638 | memcpy(dst, &sa.sin.sin_addr, sizeof(sa.sin.sin_addr)); | |
639 | break; | |
640 | case AF_INET6: | |
641 | memcpy(dst, &sa.sin6.sin6_addr, sizeof(sa.sin6.sin6_addr)); | |
642 | break; | |
643 | } | |
644 | ||
645 | return 1; | |
646 | #endif | |
647 | } |