]> git.proxmox.com Git - libgit2.git/blame - src/netops.c
Fix compilation for mingw32 and cygwin
[libgit2.git] / src / netops.c
CommitLineData
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
36static 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
50static 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
57static 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
102int gitno_recv(gitno_buffer *buf)
103{
104 return buf->recv(buf);
66024c7c
CMN
105}
106
d3e1367f 107#ifdef GIT_SSL
8861d32f 108static 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 126static 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 140void 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 156void 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 169void 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 184void 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
193static 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
209static 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
247static 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 258static 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
370on_error:
371 OPENSSL_free(peer_cn);
41fb1ca0 372 return ssl_set_error(ssl, 0);
441df990
CMN
373
374cert_fail:
375 OPENSSL_free(peer_cn);
376 giterr_set(GITERR_SSL, "Certificate host name check failed");
377 return -1;
dbb36e1b 378}
dbb36e1b 379
41fb1ca0 380static 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
412static 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 429int 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 506static 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 525int 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 549int 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
560int 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
575int 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
601int 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}