diff options
Diffstat (limited to 'toxcore/network.c')
-rw-r--r-- | toxcore/network.c | 604 |
1 files changed, 581 insertions, 23 deletions
diff --git a/toxcore/network.c b/toxcore/network.c index ed3dff8a..0b5eba61 100644 --- a/toxcore/network.c +++ b/toxcore/network.c | |||
@@ -26,6 +26,7 @@ | |||
26 | #endif | 26 | #endif |
27 | 27 | ||
28 | #include "network.h" | 28 | #include "network.h" |
29 | #include "util.h" | ||
29 | 30 | ||
30 | /* return current UNIX time in microseconds (us). */ | 31 | /* return current UNIX time in microseconds (us). */ |
31 | uint64_t current_time(void) | 32 | uint64_t current_time(void) |
@@ -61,17 +62,76 @@ uint32_t random_int(void) | |||
61 | #endif | 62 | #endif |
62 | } | 63 | } |
63 | 64 | ||
65 | #ifdef LOGGING | ||
66 | static void loglogdata(char *message, uint8_t *buffer, size_t buflen, IP_Port *ip_port, ssize_t res); | ||
67 | #endif | ||
68 | |||
64 | /* Basic network functions: | 69 | /* Basic network functions: |
65 | * Function to send packet(data) of length length to ip_port. | 70 | * Function to send packet(data) of length length to ip_port. |
66 | */ | 71 | */ |
67 | #ifdef WIN32 | 72 | int sendpacket(Networking_Core *net, IP_Port ip_port, uint8_t *data, uint32_t length) |
68 | int sendpacket(unsigned int sock, IP_Port ip_port, uint8_t *data, uint32_t length) | 73 | { |
74 | #ifdef TOX_ENABLE_IPV6 | ||
75 | /* socket AF_INET, but target IP NOT: can't send */ | ||
76 | if ((net->family == AF_INET) && (ip_port.ip.family != AF_INET)) | ||
77 | return 0; | ||
78 | #endif | ||
79 | |||
80 | struct sockaddr_storage addr; | ||
81 | size_t addrsize = 0; | ||
82 | |||
83 | #ifdef TOX_ENABLE_IPV6 | ||
84 | if (ip_port.ip.family == AF_INET) { | ||
85 | if (net->family == AF_INET6) { | ||
86 | /* must convert to IPV4-in-IPV6 address */ | ||
87 | addrsize = sizeof(struct sockaddr_in6); | ||
88 | struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&addr; | ||
89 | addr6->sin6_family = AF_INET6; | ||
90 | addr6->sin6_port = ip_port.port; | ||
91 | |||
92 | /* there should be a macro for this in a standards compliant | ||
93 | * environment, not found */ | ||
94 | addr6->sin6_addr.s6_addr32[0] = 0; | ||
95 | addr6->sin6_addr.s6_addr32[1] = 0; | ||
96 | addr6->sin6_addr.s6_addr32[2] = htonl(0xFFFF); | ||
97 | addr6->sin6_addr.s6_addr32[3] = ip_port.ip.ip4.uint32; | ||
98 | |||
99 | addr6->sin6_flowinfo = 0; | ||
100 | addr6->sin6_scope_id = 0; | ||
101 | } | ||
102 | else { | ||
103 | IP4 ip4 = ip_port.ip.ip4; | ||
69 | #else | 104 | #else |
70 | int sendpacket(int sock, IP_Port ip_port, uint8_t *data, uint32_t length) | 105 | IP4 ip4 = ip_port.ip; |
71 | #endif | 106 | #endif |
72 | { | 107 | addrsize = sizeof(struct sockaddr_in); |
73 | ADDR addr = {AF_INET, ip_port.port, ip_port.ip, {0}}; | 108 | struct sockaddr_in *addr4 = (struct sockaddr_in *)&addr; |
74 | return sendto(sock, (char *) data, length, 0, (struct sockaddr *)&addr, sizeof(addr)); | 109 | addr4->sin_family = AF_INET; |
110 | addr4->sin_addr = ip4.in_addr; | ||
111 | addr4->sin_port = ip_port.port; | ||
112 | #ifdef TOX_ENABLE_IPV6 | ||
113 | } | ||
114 | } | ||
115 | else if (ip_port.ip.family == AF_INET6) { | ||
116 | addrsize = sizeof(struct sockaddr_in6); | ||
117 | struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&addr; | ||
118 | addr6->sin6_family = AF_INET6; | ||
119 | addr6->sin6_port = ip_port.port; | ||
120 | addr6->sin6_addr = ip_port.ip.ip6; | ||
121 | |||
122 | addr6->sin6_flowinfo = 0; | ||
123 | addr6->sin6_scope_id = 0; | ||
124 | } else { | ||
125 | /* unknown address type*/ | ||
126 | return 0; | ||
127 | } | ||
128 | #endif | ||
129 | |||
130 | int res = sendto(net->sock, (char *) data, length, 0, (struct sockaddr *)&addr, addrsize); | ||
131 | #ifdef LOGGING | ||
132 | loglogdata("O=>", data, length, &ip_port, res); | ||
133 | #endif | ||
134 | return res; | ||
75 | } | 135 | } |
76 | 136 | ||
77 | /* Function to receive data | 137 | /* Function to receive data |
@@ -80,13 +140,9 @@ int sendpacket(int sock, IP_Port ip_port, uint8_t *data, uint32_t length) | |||
80 | * Packet length is put into length. | 140 | * Packet length is put into length. |
81 | * Dump all empty packets. | 141 | * Dump all empty packets. |
82 | */ | 142 | */ |
83 | #ifdef WIN32 | 143 | static int receivepacket(sock_t sock, IP_Port *ip_port, uint8_t *data, uint32_t *length) |
84 | static int receivepacket(unsigned int sock, IP_Port *ip_port, uint8_t *data, uint32_t *length) | ||
85 | #else | ||
86 | static int receivepacket(int sock, IP_Port *ip_port, uint8_t *data, uint32_t *length) | ||
87 | #endif | ||
88 | { | 144 | { |
89 | ADDR addr; | 145 | struct sockaddr_storage addr; |
90 | #ifdef WIN32 | 146 | #ifdef WIN32 |
91 | int addrlen = sizeof(addr); | 147 | int addrlen = sizeof(addr); |
92 | #else | 148 | #else |
@@ -94,11 +150,43 @@ static int receivepacket(int sock, IP_Port *ip_port, uint8_t *data, uint32_t *le | |||
94 | #endif | 150 | #endif |
95 | (*(int32_t *)length) = recvfrom(sock, (char *) data, MAX_UDP_PACKET_SIZE, 0, (struct sockaddr *)&addr, &addrlen); | 151 | (*(int32_t *)length) = recvfrom(sock, (char *) data, MAX_UDP_PACKET_SIZE, 0, (struct sockaddr *)&addr, &addrlen); |
96 | 152 | ||
97 | if (*(int32_t *)length <= 0) | 153 | if (*(int32_t *)length <= 0) { |
154 | #ifdef LOGGING | ||
155 | if ((length < 0) && (errno != EWOULDBLOCK)) | ||
156 | sprintf(logbuffer, "Unexpected error reading from socket: %u, %s\n", errno, strerror(errno)); | ||
157 | #endif | ||
98 | return -1; /* Nothing received or empty packet. */ | 158 | return -1; /* Nothing received or empty packet. */ |
159 | } | ||
160 | |||
161 | #ifdef TOX_ENABLE_IPV6 | ||
162 | if (addr.ss_family == AF_INET) { | ||
163 | struct sockaddr_in *addr_in = (struct sockaddr_in *)&addr; | ||
164 | ip_port->ip.family = addr_in->sin_family; | ||
165 | ip_port->ip.ip4.in_addr = addr_in->sin_addr; | ||
166 | ip_port->port = addr_in->sin_port; | ||
167 | } | ||
168 | else if (addr.ss_family == AF_INET6) { | ||
169 | struct sockaddr_in6 *addr_in6 = (struct sockaddr_in6 *)&addr; | ||
170 | ip_port->ip.family = addr_in6->sin6_family; | ||
171 | ip_port->ip.ip6 = addr_in6->sin6_addr; | ||
172 | ip_port->port = addr_in6->sin6_port; | ||
173 | } | ||
174 | else | ||
175 | return -1; | ||
176 | #else | ||
177 | if (addr.ss_family == AF_INET) { | ||
178 | struct sockaddr_in *addr_in = (struct sockaddr_in *)&addr; | ||
179 | ip_port->ip.in_addr = addr_in->sin_addr; | ||
180 | ip_port->port = addr_in->sin_port; | ||
181 | } | ||
182 | else | ||
183 | return -1; | ||
184 | #endif | ||
185 | |||
186 | #ifdef LOGGING | ||
187 | loglogdata("=>O", data, *length, ip_port, 0); | ||
188 | #endif | ||
99 | 189 | ||
100 | ip_port->ip = addr.ip; | ||
101 | ip_port->port = addr.port; | ||
102 | return 0; | 190 | return 0; |
103 | } | 191 | } |
104 | 192 | ||
@@ -162,16 +250,31 @@ static void at_shutdown(void) | |||
162 | */ | 250 | */ |
163 | Networking_Core *new_networking(IP ip, uint16_t port) | 251 | Networking_Core *new_networking(IP ip, uint16_t port) |
164 | { | 252 | { |
253 | #ifdef TOX_ENABLE_IPV6 | ||
254 | /* maybe check for invalid IPs like 224+.x.y.z? if there is any IP set ever */ | ||
255 | if (ip.family != AF_INET && ip.family != AF_INET6) | ||
256 | return NULL; | ||
257 | #endif | ||
258 | |||
165 | if (at_startup() != 0) | 259 | if (at_startup() != 0) |
166 | return NULL; | 260 | return NULL; |
167 | 261 | ||
168 | /* Initialize our socket. */ | ||
169 | Networking_Core *temp = calloc(1, sizeof(Networking_Core)); | 262 | Networking_Core *temp = calloc(1, sizeof(Networking_Core)); |
170 | |||
171 | if (temp == NULL) | 263 | if (temp == NULL) |
172 | return NULL; | 264 | return NULL; |
173 | 265 | ||
174 | temp->sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); | 266 | sa_family_t family = 0; |
267 | #ifdef TOX_ENABLE_IPV6 | ||
268 | family = ip.family; | ||
269 | #else | ||
270 | family = AF_INET; | ||
271 | #endif | ||
272 | temp->family = family; | ||
273 | temp->port = 0; | ||
274 | |||
275 | /* Initialize our socket. */ | ||
276 | /* add log message what we're creating */ | ||
277 | temp->sock = socket(temp->family, SOCK_DGRAM, IPPROTO_UDP); | ||
175 | 278 | ||
176 | /* Check for socket error. */ | 279 | /* Check for socket error. */ |
177 | #ifdef WIN32 | 280 | #ifdef WIN32 |
@@ -204,7 +307,7 @@ Networking_Core *new_networking(IP ip, uint16_t port) | |||
204 | return -1; | 307 | return -1; |
205 | */ | 308 | */ |
206 | 309 | ||
207 | /* Enable broadcast on socket. */ | 310 | /* Enable broadcast on socket? */ |
208 | int broadcast = 1; | 311 | int broadcast = 1; |
209 | setsockopt(temp->sock, SOL_SOCKET, SO_BROADCAST, (char *)&broadcast, sizeof(broadcast)); | 312 | setsockopt(temp->sock, SOL_SOCKET, SO_BROADCAST, (char *)&broadcast, sizeof(broadcast)); |
210 | 313 | ||
@@ -218,10 +321,106 @@ Networking_Core *new_networking(IP ip, uint16_t port) | |||
218 | fcntl(temp->sock, F_SETFL, O_NONBLOCK, 1); | 321 | fcntl(temp->sock, F_SETFL, O_NONBLOCK, 1); |
219 | #endif | 322 | #endif |
220 | 323 | ||
221 | /* Bind our socket to port PORT and address 0.0.0.0 */ | 324 | #ifdef LOGGING |
222 | ADDR addr = {AF_INET, htons(port), ip, {0}}; | 325 | loginit(ntohs(port)); |
223 | bind(temp->sock, (struct sockaddr *)&addr, sizeof(addr)); | 326 | #endif |
224 | return temp; | 327 | |
328 | /* Bind our socket to port PORT and the given IP address (usually 0.0.0.0 or ::) */ | ||
329 | uint16_t *portptr = NULL; | ||
330 | struct sockaddr_storage addr; | ||
331 | size_t addrsize; | ||
332 | #ifdef TOX_ENABLE_IPV6 | ||
333 | if (temp->family == AF_INET) | ||
334 | { | ||
335 | IP4 ip4 = ip.ip4; | ||
336 | #else | ||
337 | IP4 ip4 = ip; | ||
338 | #endif | ||
339 | addrsize = sizeof(struct sockaddr_in); | ||
340 | struct sockaddr_in *addr4 = (struct sockaddr_in *)&addr; | ||
341 | addr4->sin_family = AF_INET; | ||
342 | addr4->sin_port = htons(port); | ||
343 | addr4->sin_addr = ip4.in_addr; | ||
344 | |||
345 | portptr = &addr4->sin_port; | ||
346 | #ifdef TOX_ENABLE_IPV6 | ||
347 | } | ||
348 | else if (temp->family == AF_INET6) | ||
349 | { | ||
350 | addrsize = sizeof(struct sockaddr_in6); | ||
351 | struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&addr; | ||
352 | addr6->sin6_family = AF_INET6; | ||
353 | addr6->sin6_port = htons(port); | ||
354 | addr6->sin6_addr = ip.ip6; | ||
355 | |||
356 | addr6->sin6_flowinfo = 0; | ||
357 | addr6->sin6_scope_id = 0; | ||
358 | |||
359 | portptr = &addr6->sin6_port; | ||
360 | } | ||
361 | else | ||
362 | return NULL; | ||
363 | |||
364 | if (ip.family == AF_INET6) { | ||
365 | char ipv6only = 0; | ||
366 | int res = setsockopt(temp->sock, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&ipv6only, sizeof(ipv6only)); | ||
367 | #ifdef LOGGING | ||
368 | if (res < 0) { | ||
369 | sprintf(logbuffer, "Failed to enable dual-stack on IPv6 socket, won't be able to receive from/send to IPv4 addresses. (%u, %s)\n", | ||
370 | errno, strerror(errno)); | ||
371 | loglog(logbuffer); | ||
372 | } | ||
373 | else | ||
374 | loglog("Embedded IPv4 addresses enabled successfully.\n"); | ||
375 | #endif | ||
376 | |||
377 | /* multicast local nodes */ | ||
378 | struct ipv6_mreq mreq; | ||
379 | memset(&mreq, 0, sizeof(mreq)); | ||
380 | mreq.ipv6mr_multiaddr.s6_addr[ 0] = 0xFF; | ||
381 | mreq.ipv6mr_multiaddr.s6_addr[ 1] = 0x02; | ||
382 | mreq.ipv6mr_multiaddr.s6_addr[15] = 0x01; | ||
383 | mreq.ipv6mr_interface = 0; | ||
384 | res = setsockopt(temp->sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, sizeof(mreq)); | ||
385 | #ifdef LOGGING | ||
386 | if (res < 0) { | ||
387 | sprintf(logbuffer, "Failed to activate local multicast membership. (%u, %s)\n", | ||
388 | errno, strerror(errno)); | ||
389 | loglog(logbuffer); | ||
390 | } | ||
391 | else | ||
392 | loglog("Local multicast group FF02::1 joined successfully.\n"); | ||
393 | #endif | ||
394 | } | ||
395 | #endif | ||
396 | |||
397 | /* a hanging program or a different user might block the standard port; | ||
398 | * as long as it isn't a parameter coming from the commandline, | ||
399 | * try a few ports after it, to see if we can find a "free" one | ||
400 | */ | ||
401 | int tries, res; | ||
402 | for(tries = 0; tries < 9; tries++) | ||
403 | { | ||
404 | res = bind(temp->sock, (struct sockaddr *)&addr, addrsize); | ||
405 | if (!res) | ||
406 | { | ||
407 | temp->port = *portptr; | ||
408 | #ifdef LOGGING | ||
409 | sprintf(logbuffer, "Bound successfully to %s:%u.\n", ip_ntoa(&ip), ntohs(temp->port)); | ||
410 | loglog(logbuffer); | ||
411 | #endif | ||
412 | return temp; | ||
413 | } | ||
414 | |||
415 | uint16_t port = ntohs(*portptr); | ||
416 | port++; | ||
417 | *portptr = htons(port); | ||
418 | } | ||
419 | |||
420 | fprintf(stderr, "Failed to bind socket: %u, %s (IP/Port: %s:%u\n", errno, | ||
421 | strerror(errno), ip_ntoa(&ip), port); | ||
422 | free(temp); | ||
423 | return NULL; | ||
225 | } | 424 | } |
226 | 425 | ||
227 | /* Function to cleanup networking stuff. */ | 426 | /* Function to cleanup networking stuff. */ |
@@ -235,3 +434,362 @@ void kill_networking(Networking_Core *net) | |||
235 | free(net); | 434 | free(net); |
236 | return; | 435 | return; |
237 | } | 436 | } |
437 | |||
438 | /* ip_equal | ||
439 | * compares two IPAny structures | ||
440 | * unset means unequal | ||
441 | * | ||
442 | * returns 0 when not equal or when uninitialized | ||
443 | */ | ||
444 | int ip_equal(IP *a, IP *b) | ||
445 | { | ||
446 | if (!a || !b) | ||
447 | return 0; | ||
448 | |||
449 | #ifdef TOX_ENABLE_IPV6 | ||
450 | if (a->family == AF_INET) | ||
451 | return (a->ip4.in_addr.s_addr == b->ip4.in_addr.s_addr); | ||
452 | |||
453 | if (a->family == AF_INET6) | ||
454 | return IN6_ARE_ADDR_EQUAL(&a->ip6, &b->ip6); | ||
455 | |||
456 | return 0; | ||
457 | #else | ||
458 | return (a->uint32 == b->uint32); | ||
459 | #endif | ||
460 | }; | ||
461 | |||
462 | /* ipport_equal | ||
463 | * compares two IPAny_Port structures | ||
464 | * unset means unequal | ||
465 | * | ||
466 | * returns 0 when not equal or when uninitialized | ||
467 | */ | ||
468 | int ipport_equal(IP_Port *a, IP_Port *b) | ||
469 | { | ||
470 | if (!a || !b) | ||
471 | return 0; | ||
472 | |||
473 | if (!a->port || (a->port != b->port)) | ||
474 | return 0; | ||
475 | |||
476 | return ip_equal(&a->ip, &b->ip); | ||
477 | }; | ||
478 | |||
479 | /* nulls out ip */ | ||
480 | void ip_reset(IP *ip) | ||
481 | { | ||
482 | if (!ip) | ||
483 | return; | ||
484 | |||
485 | #ifdef TOX_ENABLE_IPV6 | ||
486 | memset(ip, 0, sizeof(IP)); | ||
487 | #else | ||
488 | ip->uint32 = 0; | ||
489 | #endif | ||
490 | }; | ||
491 | |||
492 | /* nulls out ip, sets family according to flag */ | ||
493 | void ip_init(IP *ip, uint8_t ipv6enabled) | ||
494 | { | ||
495 | if (!ip) | ||
496 | return; | ||
497 | |||
498 | #ifdef TOX_ENABLE_IPV6 | ||
499 | memset(ip, 0, sizeof(IP)); | ||
500 | ip->family = ipv6enabled ? AF_INET6 : AF_INET; | ||
501 | #else | ||
502 | ip->uint32 = 0; | ||
503 | #endif | ||
504 | }; | ||
505 | |||
506 | /* checks if ip is valid */ | ||
507 | int ip_isset(IP *ip) | ||
508 | { | ||
509 | if (!ip) | ||
510 | return 0; | ||
511 | |||
512 | #ifdef TOX_ENABLE_IPV6 | ||
513 | return (ip->family != 0); | ||
514 | #else | ||
515 | return (ip->uint32 != 0); | ||
516 | #endif | ||
517 | }; | ||
518 | |||
519 | /* checks if ip is valid */ | ||
520 | int ipport_isset(IP_Port *ipport) | ||
521 | { | ||
522 | if (!ipport) | ||
523 | return 0; | ||
524 | |||
525 | if (!ipport->port) | ||
526 | return 0; | ||
527 | |||
528 | return ip_isset(&ipport->ip); | ||
529 | }; | ||
530 | |||
531 | /* copies an ip structure (careful about direction!) */ | ||
532 | void ip_copy(IP *target, IP *source) | ||
533 | { | ||
534 | if (!source || !target) | ||
535 | return; | ||
536 | |||
537 | memcpy(target, source, sizeof(IP)); | ||
538 | }; | ||
539 | |||
540 | /* copies an ip_port structure (careful about direction!) */ | ||
541 | void ipport_copy(IP_Port *target, IP_Port *source) | ||
542 | { | ||
543 | if (!source || !target) | ||
544 | return; | ||
545 | |||
546 | memcpy(target, source, sizeof(IP_Port)); | ||
547 | }; | ||
548 | |||
549 | /* ip_ntoa | ||
550 | * converts ip into a string | ||
551 | * uses a static buffer, so mustn't used multiple times in the same output | ||
552 | */ | ||
553 | /* there would be INET6_ADDRSTRLEN, but it might be too short for the error message */ | ||
554 | static char addresstext[96]; | ||
555 | const char *ip_ntoa(IP *ip) | ||
556 | { | ||
557 | if (ip) { | ||
558 | #ifdef TOX_ENABLE_IPV6 | ||
559 | if (ip->family == AF_INET) { | ||
560 | addresstext[0] = 0; | ||
561 | struct in_addr *addr = (struct in_addr *)&ip->ip4; | ||
562 | inet_ntop(ip->family, addr, addresstext, sizeof(addresstext)); | ||
563 | } | ||
564 | else if (ip->family == AF_INET6) { | ||
565 | addresstext[0] = '['; | ||
566 | struct in6_addr *addr = (struct in6_addr *)&ip->ip6; | ||
567 | inet_ntop(ip->family, addr, &addresstext[1], sizeof(addresstext) - 3); | ||
568 | size_t len = strlen(addresstext); | ||
569 | addresstext[len] = ']'; | ||
570 | addresstext[len + 1] = 0; | ||
571 | } | ||
572 | else | ||
573 | snprintf(addresstext, sizeof(addresstext), "(IP invalid, family %u)", ip->family); | ||
574 | #else | ||
575 | addresstext[0] = 0; | ||
576 | struct in_addr *addr = (struct in_addr *)&ip; | ||
577 | inet_ntop(AF_INET, addr, addresstext, sizeof(addresstext)); | ||
578 | #endif | ||
579 | } | ||
580 | else | ||
581 | snprintf(addresstext, sizeof(addresstext), "(IP invalid: NULL)"); | ||
582 | |||
583 | addresstext[INET6_ADDRSTRLEN + 2] = 0; | ||
584 | return addresstext; | ||
585 | }; | ||
586 | |||
587 | /* | ||
588 | * addr_parse_ip | ||
589 | * directly parses the input into an IP structure | ||
590 | * tries IPv4 first, then IPv6 | ||
591 | * | ||
592 | * input | ||
593 | * address: dotted notation (IPv4: quad, IPv6: 16) or colon notation (IPv6) | ||
594 | * | ||
595 | * output | ||
596 | * IP: family and the value is set on success | ||
597 | * | ||
598 | * returns 1 on success, 0 on failure | ||
599 | */ | ||
600 | |||
601 | int addr_parse_ip(const char *address, IP *to) | ||
602 | { | ||
603 | if (!address || !to) | ||
604 | return 0; | ||
605 | |||
606 | #ifdef TOX_ENABLE_IPV6 | ||
607 | struct in_addr addr4; | ||
608 | if (1 == inet_pton(AF_INET, address, &addr4)) { | ||
609 | to->family = AF_INET; | ||
610 | to->ip4.in_addr = addr4; | ||
611 | return 1; | ||
612 | }; | ||
613 | |||
614 | struct in6_addr addr6; | ||
615 | if (1 == inet_pton(AF_INET6, address, &addr6)) { | ||
616 | to->family = AF_INET6; | ||
617 | to->ip6 = addr6; | ||
618 | return 1; | ||
619 | }; | ||
620 | #else | ||
621 | struct in_addr addr4; | ||
622 | if (1 == inet_pton(AF_INET, address, &addr4)) { | ||
623 | to->in_addr = addr4; | ||
624 | return 1; | ||
625 | }; | ||
626 | #endif | ||
627 | |||
628 | return 0; | ||
629 | }; | ||
630 | |||
631 | /* | ||
632 | * addr_resolve(): | ||
633 | * uses getaddrinfo to resolve an address into an IP address | ||
634 | * uses the first IPv4/IPv6 addresses returned by getaddrinfo | ||
635 | * | ||
636 | * input | ||
637 | * address: a hostname (or something parseable to an IP address) | ||
638 | * ip: ip.family MUST be initialized, either set to a specific IP version | ||
639 | * (AF_INET/AF_INET6) or to the unspecified AF_UNSPEC (= 0), if both | ||
640 | * IP versions are acceptable | ||
641 | * | ||
642 | * returns in ip a valid IPAny (v4/v6), | ||
643 | * prefers v6 if ip.family was AF_UNSPEC and both available | ||
644 | * returns 0 on failure | ||
645 | */ | ||
646 | |||
647 | int addr_resolve(const char *address, IP *to) | ||
648 | { | ||
649 | if (!address || !to) | ||
650 | return 0; | ||
651 | |||
652 | sa_family_t family; | ||
653 | #ifdef TOX_ENABLE_IPV6 | ||
654 | family = to->family; | ||
655 | #else | ||
656 | family = AF_INET; | ||
657 | #endif | ||
658 | |||
659 | struct addrinfo *server = NULL; | ||
660 | struct addrinfo *walker = NULL; | ||
661 | struct addrinfo hints; | ||
662 | int rc; | ||
663 | |||
664 | memset(&hints, 0, sizeof(hints)); | ||
665 | hints.ai_family = family; | ||
666 | hints.ai_socktype = SOCK_DGRAM; // type of socket Tox uses. | ||
667 | |||
668 | #ifdef __WIN32__ | ||
669 | WSADATA wsa_data; | ||
670 | |||
671 | /* CLEANUP: really not the best place to put this */ | ||
672 | rc = WSAStartup(MAKEWORD(2, 2), &wsa_data); | ||
673 | |||
674 | if (rc != 0) { | ||
675 | return 0; | ||
676 | } | ||
677 | |||
678 | #endif | ||
679 | |||
680 | rc = getaddrinfo(address, NULL, &hints, &server); | ||
681 | // Lookup failed. | ||
682 | if (rc != 0) { | ||
683 | #ifdef __WIN32__ | ||
684 | WSACleanup(); | ||
685 | #endif | ||
686 | return 0; | ||
687 | } | ||
688 | |||
689 | #ifdef TOX_ENABLE_IPV6 | ||
690 | IP4 ip4; | ||
691 | memset(&ip4, 0, sizeof(ip4)); | ||
692 | IP6 ip6; | ||
693 | memset(&ip6, 0, sizeof(ip6)); | ||
694 | #endif | ||
695 | |||
696 | walker = server; | ||
697 | while (walker && (rc != 3)) { | ||
698 | if (family != AF_UNSPEC) { | ||
699 | if (walker->ai_family == family) { | ||
700 | if (family == AF_INET) { | ||
701 | if (walker->ai_addrlen == sizeof(struct sockaddr_in)) { | ||
702 | struct sockaddr_in *addr = (struct sockaddr_in *)walker->ai_addr; | ||
703 | #ifdef TOX_ENABLE_IPV6 | ||
704 | to->ip4.in_addr = addr->sin_addr; | ||
705 | #else | ||
706 | to->in_addr = addr->sin_addr; | ||
707 | #endif | ||
708 | rc = 3; | ||
709 | } | ||
710 | } | ||
711 | #ifdef TOX_ENABLE_IPV6 | ||
712 | else if (family == AF_INET6) { | ||
713 | if (walker->ai_addrlen == sizeof(struct sockaddr_in6)) { | ||
714 | struct sockaddr_in6 *addr = (struct sockaddr_in6 *)walker->ai_addr; | ||
715 | to->ip6 = addr->sin6_addr; | ||
716 | rc = 3; | ||
717 | } | ||
718 | } | ||
719 | #endif | ||
720 | } | ||
721 | } | ||
722 | #ifdef TOX_ENABLE_IPV6 | ||
723 | else { | ||
724 | if (walker->ai_family == AF_INET) { | ||
725 | if (walker->ai_addrlen == sizeof(struct sockaddr_in)) { | ||
726 | struct sockaddr_in *addr = (struct sockaddr_in *)walker->ai_addr; | ||
727 | ip4.in_addr = addr->sin_addr; | ||
728 | rc |= 1; | ||
729 | } | ||
730 | } | ||
731 | else if (walker->ai_family == AF_INET6) { | ||
732 | if (walker->ai_addrlen == sizeof(struct sockaddr_in6)) { | ||
733 | struct sockaddr_in6 *addr = (struct sockaddr_in6 *)walker->ai_addr; | ||
734 | ip6 = addr->sin6_addr; | ||
735 | rc |= 2; | ||
736 | } | ||
737 | } | ||
738 | } | ||
739 | #endif | ||
740 | |||
741 | walker = walker->ai_next; | ||
742 | } | ||
743 | |||
744 | #ifdef TOX_ENABLE_IPV6 | ||
745 | if (to->family == AF_UNSPEC) { | ||
746 | if (rc & 2) { | ||
747 | to->family = AF_INET6; | ||
748 | to->ip6 = ip6; | ||
749 | } | ||
750 | else if (rc & 1) { | ||
751 | to->family = AF_INET; | ||
752 | to->ip4 = ip4; | ||
753 | } | ||
754 | else | ||
755 | rc = 0; | ||
756 | } | ||
757 | #endif | ||
758 | |||
759 | |||
760 | freeaddrinfo(server); | ||
761 | #ifdef __WIN32__ | ||
762 | WSACleanup(); | ||
763 | #endif | ||
764 | return rc; | ||
765 | } | ||
766 | |||
767 | /* | ||
768 | * addr_resolve_or_parse_ip | ||
769 | * resolves string into an IP address | ||
770 | * | ||
771 | * to->family MUST be set (AF_UNSPEC, AF_INET, AF_INET6) | ||
772 | * returns 1 on success, 0 on failure | ||
773 | */ | ||
774 | int addr_resolve_or_parse_ip(const char *address, IP *to) | ||
775 | { | ||
776 | if (!addr_resolve(address, to)) | ||
777 | if (!addr_parse_ip(address, to)) | ||
778 | return 0; | ||
779 | |||
780 | return 1; | ||
781 | }; | ||
782 | |||
783 | #ifdef LOGGING | ||
784 | static void loglogdata(char *message, uint8_t *buffer, size_t buflen, IP_Port *ip_port, ssize_t res) | ||
785 | { | ||
786 | snprintf(logbuffer, sizeof(logbuffer), "[%2u] %3u%c %s %s:%u (%u: %s) | %04x%04x\n", | ||
787 | buffer[0], res < 0 ? (buflen & 0xFF) : res, | ||
788 | res < 0 ? '-' : (res == buflen ? '=' : '+'), | ||
789 | message, ip_ntoa(&ip_port->ip), ntohs(ip_port->port), res < 0 ? errno : 0, | ||
790 | res < 0 ? strerror(errno) : "OK", buflen > 4 ? ntohl(*(uint32_t *)&buffer[1]) : 0, | ||
791 | buflen > 7 ? ntohl(*(uint32_t *)(&buffer[5])) : 0); | ||
792 | logbuffer[sizeof(logbuffer) - 1] = 0; | ||
793 | loglog(logbuffer); | ||
794 | } | ||
795 | #endif | ||