diff options
author | Christian Grothoff <christian@grothoff.org> | 2012-02-09 10:12:22 +0000 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2012-02-09 10:12:22 +0000 |
commit | acddb731e03012c7479dec7257b44dc6ed450b3c (patch) | |
tree | 91c25e11254c027b4f1bf7a5b7409fcebcd5e3f5 /src/dns | |
parent | a371ea89023739ba6d2a889ddf1a2f4242ff8605 (diff) | |
download | gnunet-acddb731e03012c7479dec7257b44dc6ed450b3c.tar.gz gnunet-acddb731e03012c7479dec7257b44dc6ed450b3c.zip |
-fixing source port randomization for DNS service
Diffstat (limited to 'src/dns')
-rw-r--r-- | src/dns/Makefile.am | 2 | ||||
-rw-r--r-- | src/dns/gnunet-service-dns.c | 552 |
2 files changed, 306 insertions, 248 deletions
diff --git a/src/dns/Makefile.am b/src/dns/Makefile.am index a9b1d652e..6e25d81bf 100644 --- a/src/dns/Makefile.am +++ b/src/dns/Makefile.am | |||
@@ -22,7 +22,7 @@ install-exec-hook: | |||
22 | $(SUDO_BINARY) chgrp $(GNUNETDNS_GROUP) $(bindir)/gnunet-helper-dns || true | 22 | $(SUDO_BINARY) chgrp $(GNUNETDNS_GROUP) $(bindir)/gnunet-helper-dns || true |
23 | $(SUDO_BINARY) chmod 4750 $(bindir)/gnunet-helper-dns || true | 23 | $(SUDO_BINARY) chmod 4750 $(bindir)/gnunet-helper-dns || true |
24 | $(SUDO_BINARY) chgrp $(GNUNETDNS_GROUP) $(bindir)/gnunet-service-dns || true | 24 | $(SUDO_BINARY) chgrp $(GNUNETDNS_GROUP) $(bindir)/gnunet-service-dns || true |
25 | $(SUDO_BINARY) chmod 2755 $(bindir)/gnunet-service-dns || true | 25 | $(SUDO_BINARY) chmod 2750 $(bindir)/gnunet-service-dns || true |
26 | else | 26 | else |
27 | install-exec-hook: | 27 | install-exec-hook: |
28 | endif | 28 | endif |
diff --git a/src/dns/gnunet-service-dns.c b/src/dns/gnunet-service-dns.c index 1c772d712..46cc1a30c 100644 --- a/src/dns/gnunet-service-dns.c +++ b/src/dns/gnunet-service-dns.c | |||
@@ -22,6 +22,19 @@ | |||
22 | * @file dns/gnunet-service-dns.c | 22 | * @file dns/gnunet-service-dns.c |
23 | * @brief service to intercept and modify DNS queries (and replies) of this system | 23 | * @brief service to intercept and modify DNS queries (and replies) of this system |
24 | * @author Christian Grothoff | 24 | * @author Christian Grothoff |
25 | * | ||
26 | * For "secure" interaction with the legacy DNS system, we permit | ||
27 | * replies only to arrive within a 5s window (and they must match | ||
28 | * ports, IPs and request IDs). Furthermore, we let the OS pick a | ||
29 | * source port, opening up to 128 sockets per address family (IPv4 or | ||
30 | * IPv6). Those sockets are closed if they are not in use for 5s | ||
31 | * (which means they will be freshly randomized afterwards). For new | ||
32 | * requests, we pick a random slot in the array with 128 socket slots | ||
33 | * (and re-use an existing socket if the slot is still in use). Thus | ||
34 | * each request will be given one of 128 random source ports, and the | ||
35 | * 128 random source ports will also change "often" (less often if the | ||
36 | * system is very busy, each time if we are mostly idle). At the same | ||
37 | * time, the system will never use more than 256 UDP sockets. | ||
25 | */ | 38 | */ |
26 | #include "platform.h" | 39 | #include "platform.h" |
27 | #include "gnunet_util_lib.h" | 40 | #include "gnunet_util_lib.h" |
@@ -37,6 +50,17 @@ | |||
37 | 50 | ||
38 | 51 | ||
39 | /** | 52 | /** |
53 | * Timeout for an external (Internet-DNS) DNS resolution | ||
54 | */ | ||
55 | #define REQUEST_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5) | ||
56 | |||
57 | /** | ||
58 | * How many DNS sockets do we open at most at the same time? | ||
59 | * (technical socket maximum is this number x2 for IPv4+IPv6) | ||
60 | */ | ||
61 | #define DNS_SOCKET_MAX 128 | ||
62 | |||
63 | /** | ||
40 | * Phases each request goes through. | 64 | * Phases each request goes through. |
41 | */ | 65 | */ |
42 | enum RequestPhase | 66 | enum RequestPhase |
@@ -112,6 +136,34 @@ struct ClientRecord | |||
112 | 136 | ||
113 | 137 | ||
114 | /** | 138 | /** |
139 | * UDP socket we are using for sending DNS requests to the Internet. | ||
140 | */ | ||
141 | struct RequestSocket | ||
142 | { | ||
143 | |||
144 | /** | ||
145 | * UDP socket we use for this request for IPv4 | ||
146 | */ | ||
147 | struct GNUNET_NETWORK_Handle *dnsout4; | ||
148 | |||
149 | /** | ||
150 | * UDP socket we use for this request for IPv6 | ||
151 | */ | ||
152 | struct GNUNET_NETWORK_Handle *dnsout6; | ||
153 | |||
154 | /** | ||
155 | * Task for reading from dnsout4 and dnsout6. | ||
156 | */ | ||
157 | GNUNET_SCHEDULER_TaskIdentifier read_task; | ||
158 | |||
159 | /** | ||
160 | * When should this socket be closed? | ||
161 | */ | ||
162 | struct GNUNET_TIME_Absolute timeout; | ||
163 | }; | ||
164 | |||
165 | |||
166 | /** | ||
115 | * Entry we keep for each active request. | 167 | * Entry we keep for each active request. |
116 | */ | 168 | */ |
117 | struct RequestRecord | 169 | struct RequestRecord |
@@ -130,6 +182,13 @@ struct RequestRecord | |||
130 | char *payload; | 182 | char *payload; |
131 | 183 | ||
132 | /** | 184 | /** |
185 | * Socket we are using to transmit this request (must match if we receive | ||
186 | * a response). Must NOT be freed as part of this request record (as it | ||
187 | * might be shared with other requests). | ||
188 | */ | ||
189 | struct GNUNET_NETWORK_Handle *dnsout; | ||
190 | |||
191 | /** | ||
133 | * Source address of the original request (for sending response). | 192 | * Source address of the original request (for sending response). |
134 | */ | 193 | */ |
135 | struct sockaddr_storage src_addr; | 194 | struct sockaddr_storage src_addr; |
@@ -140,6 +199,11 @@ struct RequestRecord | |||
140 | struct sockaddr_storage dst_addr; | 199 | struct sockaddr_storage dst_addr; |
141 | 200 | ||
142 | /** | 201 | /** |
202 | * When should this request time out? | ||
203 | */ | ||
204 | struct GNUNET_TIME_Absolute timeout; | ||
205 | |||
206 | /** | ||
143 | * ID of this request, also basis for hashing. Lowest 16 bit will | 207 | * ID of this request, also basis for hashing. Lowest 16 bit will |
144 | * be our message ID when doing a global DNS request and our index | 208 | * be our message ID when doing a global DNS request and our index |
145 | * into the 'requests' array. | 209 | * into the 'requests' array. |
@@ -186,11 +250,23 @@ struct TunnelState | |||
186 | char *reply; | 250 | char *reply; |
187 | 251 | ||
188 | /** | 252 | /** |
253 | * Socket we are using to transmit this request (must match if we receive | ||
254 | * a response). Must NOT be freed as part of this request record (as it | ||
255 | * might be shared with other requests). | ||
256 | */ | ||
257 | struct GNUNET_NETWORK_Handle *dnsout; | ||
258 | |||
259 | /** | ||
189 | * Address we sent the DNS request to. | 260 | * Address we sent the DNS request to. |
190 | */ | 261 | */ |
191 | struct sockaddr_storage addr; | 262 | struct sockaddr_storage addr; |
192 | 263 | ||
193 | /** | 264 | /** |
265 | * When should this request time out? | ||
266 | */ | ||
267 | struct GNUNET_TIME_Absolute timeout; | ||
268 | |||
269 | /** | ||
194 | * Number of bytes in 'addr'. | 270 | * Number of bytes in 'addr'. |
195 | */ | 271 | */ |
196 | socklen_t addrlen; | 272 | socklen_t addrlen; |
@@ -213,28 +289,6 @@ struct TunnelState | |||
213 | 289 | ||
214 | 290 | ||
215 | /** | 291 | /** |
216 | * The IPv4 UDP-Socket through which DNS-Resolves will be sent if they are not to be | ||
217 | * sent through gnunet. The port of this socket will not be hijacked. | ||
218 | */ | ||
219 | static struct GNUNET_NETWORK_Handle *dnsout4; | ||
220 | |||
221 | /** | ||
222 | * The IPv6 UDP-Socket through which DNS-Resolves will be sent if they are not to be | ||
223 | * sent through gnunet. The port of this socket will not be hijacked. | ||
224 | */ | ||
225 | static struct GNUNET_NETWORK_Handle *dnsout6; | ||
226 | |||
227 | /** | ||
228 | * Task for reading from dnsout4. | ||
229 | */ | ||
230 | static GNUNET_SCHEDULER_TaskIdentifier read4_task; | ||
231 | |||
232 | /** | ||
233 | * Task for reading from dnsout6. | ||
234 | */ | ||
235 | static GNUNET_SCHEDULER_TaskIdentifier read6_task; | ||
236 | |||
237 | /** | ||
238 | * The configuration to use | 292 | * The configuration to use |
239 | */ | 293 | */ |
240 | static const struct GNUNET_CONFIGURATION_Handle *cfg; | 294 | static const struct GNUNET_CONFIGURATION_Handle *cfg; |
@@ -280,6 +334,11 @@ static struct RequestRecord requests[UINT16_MAX + 1]; | |||
280 | static struct TunnelState *tunnels[UINT16_MAX + 1]; | 334 | static struct TunnelState *tunnels[UINT16_MAX + 1]; |
281 | 335 | ||
282 | /** | 336 | /** |
337 | * Array of all open sockets for DNS requests. | ||
338 | */ | ||
339 | static struct RequestSocket sockets[DNS_SOCKET_MAX]; | ||
340 | |||
341 | /** | ||
283 | * Generator for unique request IDs. | 342 | * Generator for unique request IDs. |
284 | */ | 343 | */ |
285 | static uint64_t request_id_gen; | 344 | static uint64_t request_id_gen; |
@@ -296,10 +355,31 @@ static char *dns_exit; | |||
296 | */ | 355 | */ |
297 | static struct GNUNET_MESH_Handle *mesh; | 356 | static struct GNUNET_MESH_Handle *mesh; |
298 | 357 | ||
358 | |||
299 | /** | 359 | /** |
300 | * Number of active DNS requests. | 360 | * We're done with a RequestSocket, close it for now. |
361 | * | ||
362 | * @param rr request to clean up | ||
301 | */ | 363 | */ |
302 | static unsigned int dns_active; | 364 | static void |
365 | cleanup_rs (struct RequestSocket *rs) | ||
366 | { | ||
367 | if (NULL != rs->dnsout4) | ||
368 | { | ||
369 | GNUNET_NETWORK_socket_close (rs->dnsout4); | ||
370 | rs->dnsout4 = NULL; | ||
371 | } | ||
372 | if (NULL != rs->dnsout6) | ||
373 | { | ||
374 | GNUNET_NETWORK_socket_close (rs->dnsout6); | ||
375 | rs->dnsout6 = NULL; | ||
376 | } | ||
377 | if (GNUNET_SCHEDULER_NO_TASK != rs->read_task) | ||
378 | { | ||
379 | GNUNET_SCHEDULER_cancel (rs->read_task); | ||
380 | rs->read_task = GNUNET_SCHEDULER_NO_TASK; | ||
381 | } | ||
382 | } | ||
303 | 383 | ||
304 | 384 | ||
305 | /** | 385 | /** |
@@ -335,27 +415,7 @@ cleanup_task (void *cls GNUNET_UNUSED, | |||
335 | hijacker = NULL; | 415 | hijacker = NULL; |
336 | for (i=0;i<7;i++) | 416 | for (i=0;i<7;i++) |
337 | GNUNET_free_non_null (helper_argv[i]); | 417 | GNUNET_free_non_null (helper_argv[i]); |
338 | if (NULL != dnsout4) | 418 | for (i=0;i<=UINT16_MAX;i++) |
339 | { | ||
340 | GNUNET_NETWORK_socket_close (dnsout4); | ||
341 | dnsout4 = NULL; | ||
342 | } | ||
343 | if (GNUNET_SCHEDULER_NO_TASK != read4_task) | ||
344 | { | ||
345 | GNUNET_SCHEDULER_cancel (read4_task); | ||
346 | read4_task = GNUNET_SCHEDULER_NO_TASK; | ||
347 | } | ||
348 | if (NULL != dnsout6) | ||
349 | { | ||
350 | GNUNET_NETWORK_socket_close (dnsout6); | ||
351 | dnsout6 = NULL; | ||
352 | } | ||
353 | if (GNUNET_SCHEDULER_NO_TASK != read6_task) | ||
354 | { | ||
355 | GNUNET_SCHEDULER_cancel (read6_task); | ||
356 | read6_task = GNUNET_SCHEDULER_NO_TASK; | ||
357 | } | ||
358 | for (i=0;i<65536;i++) | ||
359 | cleanup_rr (&requests[i]); | 419 | cleanup_rr (&requests[i]); |
360 | GNUNET_SERVER_notification_context_destroy (nc); | 420 | GNUNET_SERVER_notification_context_destroy (nc); |
361 | nc = NULL; | 421 | nc = NULL; |
@@ -373,6 +433,53 @@ cleanup_task (void *cls GNUNET_UNUSED, | |||
373 | 433 | ||
374 | 434 | ||
375 | /** | 435 | /** |
436 | * Open source port for sending DNS requests | ||
437 | * | ||
438 | * @param af AF_INET or AF_INET6 | ||
439 | * @return GNUNET_OK on success | ||
440 | */ | ||
441 | static struct GNUNET_NETWORK_Handle * | ||
442 | open_socket (int af) | ||
443 | { | ||
444 | struct sockaddr_in a4; | ||
445 | struct sockaddr_in6 a6; | ||
446 | struct sockaddr *sa; | ||
447 | socklen_t alen; | ||
448 | struct GNUNET_NETWORK_Handle *ret; | ||
449 | |||
450 | ret = GNUNET_NETWORK_socket_create (af, SOCK_DGRAM, 0); | ||
451 | if (NULL == ret) | ||
452 | return NULL; | ||
453 | switch (af) | ||
454 | { | ||
455 | case AF_INET: | ||
456 | memset (&a4, 0, alen = sizeof (struct sockaddr_in)); | ||
457 | sa = (struct sockaddr *) &a4; | ||
458 | break; | ||
459 | case AF_INET6: | ||
460 | memset (&a6, 0, alen = sizeof (struct sockaddr_in6)); | ||
461 | sa = (struct sockaddr *) &a6; | ||
462 | break; | ||
463 | default: | ||
464 | GNUNET_break (0); | ||
465 | return NULL; | ||
466 | } | ||
467 | sa->sa_family = af; | ||
468 | if (GNUNET_OK != GNUNET_NETWORK_socket_bind (ret, | ||
469 | sa, | ||
470 | alen)) | ||
471 | { | ||
472 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
473 | _("Could not bind to any port: %s\n"), | ||
474 | STRERROR (errno)); | ||
475 | GNUNET_NETWORK_socket_close (ret); | ||
476 | return NULL; | ||
477 | } | ||
478 | return ret; | ||
479 | } | ||
480 | |||
481 | |||
482 | /** | ||
376 | * We're done with some request, finish processing. | 483 | * We're done with some request, finish processing. |
377 | * | 484 | * |
378 | * @param rr request send to the network or just clean up. | 485 | * @param rr request send to the network or just clean up. |
@@ -559,10 +666,70 @@ send_request_to_client (struct RequestRecord *rr, | |||
559 | 666 | ||
560 | 667 | ||
561 | /** | 668 | /** |
562 | * Try to change the source ports we are bound to. | 669 | * Read a DNS response from the (unhindered) UDP-Socket |
670 | * | ||
671 | * @param cls socket to read from | ||
672 | * @param tc scheduler context (must be shutdown or read ready) | ||
563 | */ | 673 | */ |
564 | static void | 674 | static void |
565 | change_source_ports (); | 675 | read_response (void *cls, |
676 | const struct GNUNET_SCHEDULER_TaskContext *tc); | ||
677 | |||
678 | |||
679 | /** | ||
680 | * Get a socket of the specified address family to send out a | ||
681 | * UDP DNS request to the Internet. | ||
682 | * | ||
683 | * @param af desired address family | ||
684 | * @return NULL on error (given AF not "supported") | ||
685 | */ | ||
686 | static struct GNUNET_NETWORK_Handle * | ||
687 | get_request_socket (int af) | ||
688 | { | ||
689 | struct RequestSocket *rs; | ||
690 | struct GNUNET_NETWORK_FDSet *rset; | ||
691 | static struct GNUNET_NETWORK_Handle *ret; | ||
692 | |||
693 | rs = &sockets[GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, | ||
694 | DNS_SOCKET_MAX)]; | ||
695 | rs->timeout = GNUNET_TIME_relative_to_absolute (REQUEST_TIMEOUT); | ||
696 | switch (af) | ||
697 | { | ||
698 | case AF_INET: | ||
699 | if (NULL == rs->dnsout4) | ||
700 | rs->dnsout4 = open_socket (AF_INET); | ||
701 | ret = rs->dnsout4; | ||
702 | break; | ||
703 | case AF_INET6: | ||
704 | if (NULL == rs->dnsout6) | ||
705 | rs->dnsout6 = open_socket (AF_INET6); | ||
706 | ret = rs->dnsout6; | ||
707 | break; | ||
708 | default: | ||
709 | return NULL; | ||
710 | } | ||
711 | if (GNUNET_SCHEDULER_NO_TASK != rs->read_task) | ||
712 | { | ||
713 | GNUNET_SCHEDULER_cancel (rs->read_task); | ||
714 | rs->read_task = GNUNET_SCHEDULER_NO_TASK; | ||
715 | } | ||
716 | if ( (NULL == rs->dnsout4) && | ||
717 | (NULL == rs->dnsout6) ) | ||
718 | return NULL; | ||
719 | rset = GNUNET_NETWORK_fdset_create (); | ||
720 | if (NULL != rs->dnsout4) | ||
721 | GNUNET_NETWORK_fdset_set (rset, rs->dnsout4); | ||
722 | if (NULL != rs->dnsout6) | ||
723 | GNUNET_NETWORK_fdset_set (rset, rs->dnsout6); | ||
724 | rs->read_task = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT, | ||
725 | GNUNET_SCHEDULER_NO_TASK, | ||
726 | REQUEST_TIMEOUT, | ||
727 | rset, | ||
728 | NULL, | ||
729 | &read_response, rs); | ||
730 | GNUNET_NETWORK_fdset_destroy (rset); | ||
731 | return ret; | ||
732 | } | ||
566 | 733 | ||
567 | 734 | ||
568 | /** | 735 | /** |
@@ -577,7 +744,6 @@ next_phase (struct RequestRecord *rr) | |||
577 | struct ClientRecord *cr; | 744 | struct ClientRecord *cr; |
578 | int nz; | 745 | int nz; |
579 | unsigned int j; | 746 | unsigned int j; |
580 | struct GNUNET_NETWORK_Handle *dnsout; | ||
581 | socklen_t salen; | 747 | socklen_t salen; |
582 | 748 | ||
583 | if (rr->phase == RP_DROP) | 749 | if (rr->phase == RP_DROP) |
@@ -625,41 +791,36 @@ next_phase (struct RequestRecord *rr) | |||
625 | next_phase (rr); | 791 | next_phase (rr); |
626 | return; | 792 | return; |
627 | case RP_QUERY: | 793 | case RP_QUERY: |
628 | rr->phase = RP_INTERNET_DNS; | ||
629 | dns_active++; | ||
630 | switch (rr->dst_addr.ss_family) | 794 | switch (rr->dst_addr.ss_family) |
631 | { | 795 | { |
632 | case AF_INET: | 796 | case AF_INET: |
633 | dnsout = dnsout4; | 797 | salen = sizeof (struct sockaddr_in); |
634 | salen = sizeof (struct GNUNET_TUN_IPv4Header); | ||
635 | break; | 798 | break; |
636 | case AF_INET6: | 799 | case AF_INET6: |
637 | dnsout = dnsout6; | 800 | salen = sizeof (struct sockaddr_in6); |
638 | salen = sizeof (struct GNUNET_TUN_IPv6Header); | ||
639 | break; | 801 | break; |
640 | default: | 802 | default: |
641 | GNUNET_break (0); | 803 | GNUNET_assert (0); |
642 | cleanup_rr (rr); | ||
643 | return; | ||
644 | } | 804 | } |
645 | if (NULL == dnsout) | 805 | |
806 | rr->phase = RP_INTERNET_DNS; | ||
807 | rr->dnsout = get_request_socket (rr->dst_addr.ss_family); | ||
808 | if (NULL == rr->dnsout) | ||
646 | { | 809 | { |
647 | GNUNET_STATISTICS_update (stats, | 810 | GNUNET_STATISTICS_update (stats, |
648 | gettext_noop ("# DNS exit failed (address family not supported)"), | 811 | gettext_noop ("# DNS exit failed (failed to open socket)"), |
649 | 1, GNUNET_NO); | 812 | 1, GNUNET_NO); |
650 | cleanup_rr (rr); | 813 | cleanup_rr (rr); |
651 | return; | 814 | return; |
652 | } | 815 | } |
653 | GNUNET_NETWORK_socket_sendto (dnsout, | 816 | GNUNET_NETWORK_socket_sendto (rr->dnsout, |
654 | rr->payload, | 817 | rr->payload, |
655 | rr->payload_length, | 818 | rr->payload_length, |
656 | (struct sockaddr*) &rr->dst_addr, | 819 | (struct sockaddr*) &rr->dst_addr, |
657 | salen); | 820 | salen); |
821 | rr->timeout = GNUNET_TIME_relative_to_absolute (REQUEST_TIMEOUT); | ||
658 | return; | 822 | return; |
659 | case RP_INTERNET_DNS: | 823 | case RP_INTERNET_DNS: |
660 | dns_active--; | ||
661 | if (0 == dns_active) | ||
662 | change_source_ports (); | ||
663 | rr->phase = RP_MODIFY; | 824 | rr->phase = RP_MODIFY; |
664 | for (cr = clients_head; NULL != cr; cr = cr->next) | 825 | for (cr = clients_head; NULL != cr; cr = cr->next) |
665 | { | 826 | { |
@@ -785,85 +946,65 @@ transmit_reply_to_mesh (void *cls, | |||
785 | 946 | ||
786 | 947 | ||
787 | /** | 948 | /** |
788 | * Read a DNS response from the (unhindered) UDP-Socket | 949 | * Actually do the reading of a DNS packet from our UDP socket and see |
950 | * if we have a valid, matching, pending request. | ||
789 | * | 951 | * |
790 | * @param cls socket to read from | 952 | * @param dnsout socket to read from |
791 | * @param tc scheduler context (must be shutdown or read ready) | 953 | * @param GNUNET_OK on success, GNUNET_NO on drop, GNUNET_SYSERR on IO-errors (closed socket) |
792 | */ | 954 | */ |
793 | static void | 955 | static int |
794 | read_response (void *cls, | 956 | do_dns_read (struct GNUNET_NETWORK_Handle *dnsout) |
795 | const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
796 | { | 957 | { |
797 | struct GNUNET_NETWORK_Handle *dnsout = cls; | 958 | struct sockaddr_storage addr; |
798 | struct sockaddr_in addr4; | ||
799 | struct sockaddr_in6 addr6; | ||
800 | struct sockaddr *addr; | ||
801 | struct GNUNET_TUN_DnsHeader *dns; | ||
802 | socklen_t addrlen; | 959 | socklen_t addrlen; |
960 | struct GNUNET_TUN_DnsHeader *dns; | ||
803 | struct RequestRecord *rr; | 961 | struct RequestRecord *rr; |
804 | struct TunnelState *ts; | 962 | struct TunnelState *ts; |
805 | ssize_t r; | 963 | ssize_t r; |
806 | int len; | 964 | int len; |
807 | 965 | ||
808 | if (dnsout == dnsout4) | ||
809 | { | ||
810 | addrlen = sizeof (struct sockaddr_in); | ||
811 | addr = (struct sockaddr* ) &addr4; | ||
812 | read4_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, | ||
813 | dnsout, | ||
814 | &read_response, | ||
815 | dnsout); | ||
816 | } | ||
817 | else | ||
818 | { | ||
819 | addrlen = sizeof (struct sockaddr_in6); | ||
820 | addr = (struct sockaddr* ) &addr6; | ||
821 | read6_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, | ||
822 | dnsout, | ||
823 | &read_response, | ||
824 | dnsout); | ||
825 | } | ||
826 | if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) | ||
827 | return; | ||
828 | |||
829 | #ifndef MINGW | 966 | #ifndef MINGW |
830 | if (0 != ioctl (GNUNET_NETWORK_get_fd (dnsout), FIONREAD, &len)) | 967 | if (0 != ioctl (GNUNET_NETWORK_get_fd (dnsout), FIONREAD, &len)) |
831 | { | 968 | { |
832 | /* conservative choice: */ | 969 | /* conservative choice: */ |
833 | len = 65536; | 970 | len = UINT16_MAX; |
834 | } | 971 | } |
835 | #else | 972 | #else |
836 | /* port the code above? */ | 973 | /* port the code above? */ |
837 | len = 65536; | 974 | len = UINT16_MAX; |
838 | #endif | 975 | #endif |
839 | 976 | ||
840 | { | 977 | { |
841 | unsigned char buf[len]; | 978 | unsigned char buf[len]; |
842 | 979 | ||
843 | memset (addr, 0, addrlen); | 980 | addrlen = sizeof (addr); |
981 | memset (&addr, 0, sizeof (addr)); | ||
844 | r = GNUNET_NETWORK_socket_recvfrom (dnsout, | 982 | r = GNUNET_NETWORK_socket_recvfrom (dnsout, |
845 | buf, sizeof (buf), | 983 | buf, sizeof (buf), |
846 | addr, &addrlen); | 984 | (struct sockaddr*) &addr, &addrlen); |
847 | if (-1 == r) | 985 | if (-1 == r) |
848 | { | 986 | { |
849 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "recvfrom"); | 987 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "recvfrom"); |
850 | return; | 988 | GNUNET_NETWORK_socket_close (dnsout); |
989 | return GNUNET_SYSERR; | ||
851 | } | 990 | } |
852 | if (sizeof (struct GNUNET_TUN_DnsHeader) > r) | 991 | if (sizeof (struct GNUNET_TUN_DnsHeader) > r) |
853 | { | 992 | { |
854 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | 993 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, |
855 | _("Received DNS response that is too small (%u bytes)"), | 994 | _("Received DNS response that is too small (%u bytes)"), |
856 | r); | 995 | r); |
857 | return; | 996 | return GNUNET_NO; |
858 | } | 997 | } |
859 | dns = (struct GNUNET_TUN_DnsHeader *) buf; | 998 | dns = (struct GNUNET_TUN_DnsHeader *) buf; |
860 | /* Handle case that this is a reply to a request from a MESH DNS tunnel */ | 999 | /* Handle case that this is a reply to a request from a MESH DNS tunnel */ |
861 | ts = tunnels[dns->id]; | 1000 | ts = tunnels[dns->id]; |
862 | if ( (NULL == ts) || | 1001 | if ( (NULL == ts) || |
1002 | (ts->dnsout != dnsout) || | ||
863 | (addrlen != ts->addrlen) || | 1003 | (addrlen != ts->addrlen) || |
864 | (0 != memcmp (&ts->addr, | 1004 | (0 != memcmp (&ts->addr, |
865 | addr, | 1005 | &addr, |
866 | addrlen)) ) | 1006 | addrlen)) || |
1007 | (0 == GNUNET_TIME_absolute_get_remaining (ts->timeout).rel_value) ) | ||
867 | ts = NULL; /* DNS responder address missmatch */ | 1008 | ts = NULL; /* DNS responder address missmatch */ |
868 | if (NULL != ts) | 1009 | if (NULL != ts) |
869 | { | 1010 | { |
@@ -884,7 +1025,12 @@ read_response (void *cls, | |||
884 | } | 1025 | } |
885 | /* Handle case that this is a reply to a local request (intercepted from TUN interface) */ | 1026 | /* Handle case that this is a reply to a local request (intercepted from TUN interface) */ |
886 | rr = &requests[dns->id]; | 1027 | rr = &requests[dns->id]; |
887 | if (rr->phase != RP_INTERNET_DNS) | 1028 | if ( (rr->phase != RP_INTERNET_DNS) || |
1029 | (rr->dnsout != dnsout) || | ||
1030 | (0 != memcmp (&rr->dst_addr, | ||
1031 | &addr, | ||
1032 | addrlen)) || | ||
1033 | (0 == GNUNET_TIME_absolute_get_remaining (rr->timeout).rel_value) ) | ||
888 | { | 1034 | { |
889 | if (NULL == ts) | 1035 | if (NULL == ts) |
890 | { | 1036 | { |
@@ -893,7 +1039,7 @@ read_response (void *cls, | |||
893 | gettext_noop ("# External DNS response discarded (no matching request)"), | 1039 | gettext_noop ("# External DNS response discarded (no matching request)"), |
894 | 1, GNUNET_NO); | 1040 | 1, GNUNET_NO); |
895 | } | 1041 | } |
896 | return; | 1042 | return GNUNET_NO; |
897 | } | 1043 | } |
898 | GNUNET_free_non_null (rr->payload); | 1044 | GNUNET_free_non_null (rr->payload); |
899 | rr->payload = GNUNET_malloc (r); | 1045 | rr->payload = GNUNET_malloc (r); |
@@ -901,131 +1047,53 @@ read_response (void *cls, | |||
901 | rr->payload_length = r; | 1047 | rr->payload_length = r; |
902 | next_phase (rr); | 1048 | next_phase (rr); |
903 | } | 1049 | } |
904 | } | ||
905 | |||
906 | |||
907 | /** | ||
908 | * Open source port for sending DNS request on IPv4. | ||
909 | * | ||
910 | * @return GNUNET_OK on success | ||
911 | */ | ||
912 | static int | ||
913 | open_port4 () | ||
914 | { | ||
915 | struct sockaddr_in addr; | ||
916 | |||
917 | dnsout4 = GNUNET_NETWORK_socket_create (AF_INET, SOCK_DGRAM, 0); | ||
918 | if (dnsout4 == NULL) | ||
919 | return GNUNET_SYSERR; | ||
920 | |||
921 | memset (&addr, 0, sizeof (struct sockaddr_in)); | ||
922 | addr.sin_family = AF_INET; | ||
923 | int err = GNUNET_NETWORK_socket_bind (dnsout4, | ||
924 | (struct sockaddr *) &addr, | ||
925 | sizeof (struct sockaddr_in)); | ||
926 | |||
927 | if (err != GNUNET_OK) | ||
928 | { | ||
929 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
930 | _("Could not bind to any port: %s\n"), | ||
931 | STRERROR (errno)); | ||
932 | GNUNET_NETWORK_socket_close (dnsout4); | ||
933 | dnsout4 = NULL; | ||
934 | return GNUNET_SYSERR; | ||
935 | } | ||
936 | read4_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, | ||
937 | dnsout4, | ||
938 | &read_response, dnsout4); | ||
939 | return GNUNET_OK; | 1050 | return GNUNET_OK; |
940 | } | 1051 | } |
941 | 1052 | ||
942 | 1053 | ||
943 | /** | 1054 | /** |
944 | * Open source port for sending DNS request on IPv6. Should be | 1055 | * Read a DNS response from the (unhindered) UDP-Socket |
945 | * called AFTER open_port4. | ||
946 | * | 1056 | * |
947 | * @return GNUNET_OK on success | 1057 | * @param cls socket to read from |
948 | */ | 1058 | * @param tc scheduler context (must be shutdown or read ready) |
949 | static int | ||
950 | open_port6 () | ||
951 | { | ||
952 | struct sockaddr_in6 addr; | ||
953 | |||
954 | dnsout6 = GNUNET_NETWORK_socket_create (AF_INET6, SOCK_DGRAM, 0); | ||
955 | if (dnsout6 == NULL) | ||
956 | { | ||
957 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
958 | _("Could not create IPv6 socket: %s\n"), | ||
959 | STRERROR (errno)); | ||
960 | return GNUNET_SYSERR; | ||
961 | } | ||
962 | memset (&addr, 0, sizeof (struct sockaddr_in6)); | ||
963 | addr.sin6_family = AF_INET6; | ||
964 | int err = GNUNET_NETWORK_socket_bind (dnsout6, | ||
965 | (struct sockaddr *) &addr, | ||
966 | sizeof (struct sockaddr_in6)); | ||
967 | |||
968 | if (err != GNUNET_OK) | ||
969 | { | ||
970 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
971 | _("Could not bind: %s\n"), | ||
972 | STRERROR (errno)); | ||
973 | GNUNET_NETWORK_socket_close (dnsout6); | ||
974 | dnsout6 = NULL; | ||
975 | return GNUNET_SYSERR; | ||
976 | } | ||
977 | read6_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, | ||
978 | dnsout6, | ||
979 | &read_response, dnsout6); | ||
980 | return GNUNET_YES; | ||
981 | } | ||
982 | |||
983 | |||
984 | /** | ||
985 | * Try to change the source ports we are bound to. | ||
986 | */ | 1059 | */ |
987 | static void | 1060 | static void |
988 | change_source_ports () | 1061 | read_response (void *cls, |
1062 | const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
989 | { | 1063 | { |
990 | struct GNUNET_NETWORK_Handle *old4; | 1064 | struct RequestSocket *rs = cls; |
991 | struct GNUNET_NETWORK_Handle *old6; | 1065 | struct GNUNET_NETWORK_FDSet *rset; |
992 | 1066 | ||
993 | if (GNUNET_SCHEDULER_NO_TASK != read4_task) | 1067 | rs->read_task = GNUNET_SCHEDULER_NO_TASK; |
994 | { | 1068 | if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_READ_READY)) |
995 | GNUNET_SCHEDULER_cancel (read4_task); | ||
996 | read4_task = GNUNET_SCHEDULER_NO_TASK; | ||
997 | } | ||
998 | if (GNUNET_SCHEDULER_NO_TASK != read6_task) | ||
999 | { | ||
1000 | GNUNET_SCHEDULER_cancel (read6_task); | ||
1001 | read6_task = GNUNET_SCHEDULER_NO_TASK; | ||
1002 | } | ||
1003 | old4 = dnsout4; | ||
1004 | if (GNUNET_OK != open_port4 ()) | ||
1005 | { | ||
1006 | dnsout4 = old4; | ||
1007 | read4_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, | ||
1008 | dnsout4, | ||
1009 | &read_response, dnsout4); | ||
1010 | } | ||
1011 | else | ||
1012 | { | ||
1013 | if (NULL != old4) | ||
1014 | GNUNET_NETWORK_socket_close (old4); | ||
1015 | } | ||
1016 | old6 = dnsout6; | ||
1017 | if (GNUNET_OK != open_port6 ()) | ||
1018 | { | ||
1019 | dnsout6 = old6; | ||
1020 | read6_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, | ||
1021 | dnsout6, | ||
1022 | &read_response, dnsout6); | ||
1023 | } | ||
1024 | else | ||
1025 | { | 1069 | { |
1026 | if (NULL != old6) | 1070 | /* timeout or shutdown */ |
1027 | GNUNET_NETWORK_socket_close (old6); | 1071 | cleanup_rs (rs); |
1072 | return; | ||
1028 | } | 1073 | } |
1074 | /* read and process ready sockets */ | ||
1075 | if ((NULL != rs->dnsout4) && | ||
1076 | (GNUNET_NETWORK_fdset_isset (tc->read_ready, rs->dnsout4)) && | ||
1077 | (GNUNET_SYSERR == do_dns_read (rs->dnsout4))) | ||
1078 | rs->dnsout4 = NULL; | ||
1079 | if ((NULL != rs->dnsout6) && | ||
1080 | (GNUNET_NETWORK_fdset_isset (tc->read_ready, rs->dnsout6)) && | ||
1081 | (GNUNET_SYSERR == do_dns_read (rs->dnsout6))) | ||
1082 | rs->dnsout6 = NULL; | ||
1083 | |||
1084 | /* re-schedule read task */ | ||
1085 | rset = GNUNET_NETWORK_fdset_create (); | ||
1086 | if (NULL != rs->dnsout4) | ||
1087 | GNUNET_NETWORK_fdset_set (rset, rs->dnsout4); | ||
1088 | if (NULL != rs->dnsout6) | ||
1089 | GNUNET_NETWORK_fdset_set (rset, rs->dnsout6); | ||
1090 | rs->read_task = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT, | ||
1091 | GNUNET_SCHEDULER_NO_TASK, | ||
1092 | GNUNET_TIME_absolute_get_remaining (rs->timeout), | ||
1093 | rset, | ||
1094 | NULL, | ||
1095 | &read_response, rs); | ||
1096 | GNUNET_NETWORK_fdset_destroy (rset); | ||
1029 | } | 1097 | } |
1030 | 1098 | ||
1031 | 1099 | ||
@@ -1234,12 +1302,6 @@ process_helper_messages (void *cls GNUNET_UNUSED, void *client, | |||
1234 | 1302 | ||
1235 | /* clean up from previous request */ | 1303 | /* clean up from previous request */ |
1236 | GNUNET_free_non_null (rr->payload); | 1304 | GNUNET_free_non_null (rr->payload); |
1237 | if (RP_INTERNET_DNS == rr->phase) | ||
1238 | { | ||
1239 | dns_active--; | ||
1240 | if (0 == dns_active) | ||
1241 | change_source_ports (); | ||
1242 | } | ||
1243 | rr->payload = NULL; | 1305 | rr->payload = NULL; |
1244 | GNUNET_array_grow (rr->client_wait_list, | 1306 | GNUNET_array_grow (rr->client_wait_list, |
1245 | rr->client_wait_list_length, | 1307 | rr->client_wait_list_length, |
@@ -1331,7 +1393,6 @@ receive_dns_request (void *cls GNUNET_UNUSED, struct GNUNET_MESH_Tunnel *tunnel, | |||
1331 | struct sockaddr_in6 v6; | 1393 | struct sockaddr_in6 v6; |
1332 | struct sockaddr *so; | 1394 | struct sockaddr *so; |
1333 | socklen_t salen; | 1395 | socklen_t salen; |
1334 | struct GNUNET_NETWORK_Handle *dnsout; | ||
1335 | 1396 | ||
1336 | if (dlen < sizeof (struct GNUNET_TUN_DnsHeader)) | 1397 | if (dlen < sizeof (struct GNUNET_TUN_DnsHeader)) |
1337 | { | 1398 | { |
@@ -1343,7 +1404,7 @@ receive_dns_request (void *cls GNUNET_UNUSED, struct GNUNET_MESH_Tunnel *tunnel, | |||
1343 | if (tunnels[ts->my_id] == ts) | 1404 | if (tunnels[ts->my_id] == ts) |
1344 | tunnels[ts->my_id] = NULL; | 1405 | tunnels[ts->my_id] = NULL; |
1345 | ts->my_id = (uint16_t) GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, | 1406 | ts->my_id = (uint16_t) GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, |
1346 | 65536); | 1407 | UINT16_MAX + 1); |
1347 | tunnels[ts->my_id] = ts; | 1408 | tunnels[ts->my_id] = ts; |
1348 | memcpy (buf, dns, dlen); | 1409 | memcpy (buf, dns, dlen); |
1349 | dout = (struct GNUNET_TUN_DnsHeader*) buf; | 1410 | dout = (struct GNUNET_TUN_DnsHeader*) buf; |
@@ -1351,7 +1412,6 @@ receive_dns_request (void *cls GNUNET_UNUSED, struct GNUNET_MESH_Tunnel *tunnel, | |||
1351 | 1412 | ||
1352 | memset (&v4, 0, sizeof (v4)); | 1413 | memset (&v4, 0, sizeof (v4)); |
1353 | memset (&v6, 0, sizeof (v6)); | 1414 | memset (&v6, 0, sizeof (v6)); |
1354 | dnsout = NULL; | ||
1355 | if (1 == inet_pton (AF_INET, dns_exit, &v4.sin_addr)) | 1415 | if (1 == inet_pton (AF_INET, dns_exit, &v4.sin_addr)) |
1356 | { | 1416 | { |
1357 | salen = sizeof (v4); | 1417 | salen = sizeof (v4); |
@@ -1361,7 +1421,7 @@ receive_dns_request (void *cls GNUNET_UNUSED, struct GNUNET_MESH_Tunnel *tunnel, | |||
1361 | v4.sin_len = (u_char) salen; | 1421 | v4.sin_len = (u_char) salen; |
1362 | #endif | 1422 | #endif |
1363 | so = (struct sockaddr *) &v4; | 1423 | so = (struct sockaddr *) &v4; |
1364 | dnsout = dnsout4; | 1424 | ts->dnsout = get_request_socket (AF_INET); |
1365 | } | 1425 | } |
1366 | if (1 == inet_pton (AF_INET6, dns_exit, &v6.sin6_addr)) | 1426 | if (1 == inet_pton (AF_INET6, dns_exit, &v6.sin6_addr)) |
1367 | { | 1427 | { |
@@ -1372,9 +1432,9 @@ receive_dns_request (void *cls GNUNET_UNUSED, struct GNUNET_MESH_Tunnel *tunnel, | |||
1372 | v6.sin6_len = (u_char) salen; | 1432 | v6.sin6_len = (u_char) salen; |
1373 | #endif | 1433 | #endif |
1374 | so = (struct sockaddr *) &v6; | 1434 | so = (struct sockaddr *) &v6; |
1375 | dnsout = dnsout6; | 1435 | ts->dnsout = get_request_socket (AF_INET6); |
1376 | } | 1436 | } |
1377 | if (NULL == dnsout) | 1437 | if (NULL == ts->dnsout) |
1378 | { | 1438 | { |
1379 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | 1439 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, |
1380 | _("Configured DNS exit `%s' is not working / valid.\n"), | 1440 | _("Configured DNS exit `%s' is not working / valid.\n"), |
@@ -1385,8 +1445,9 @@ receive_dns_request (void *cls GNUNET_UNUSED, struct GNUNET_MESH_Tunnel *tunnel, | |||
1385 | so, | 1445 | so, |
1386 | salen); | 1446 | salen); |
1387 | ts->addrlen = salen; | 1447 | ts->addrlen = salen; |
1388 | GNUNET_NETWORK_socket_sendto (dnsout, | 1448 | GNUNET_NETWORK_socket_sendto (ts->dnsout, |
1389 | buf, dlen, so, salen); | 1449 | buf, dlen, so, salen); |
1450 | ts->timeout = GNUNET_TIME_relative_to_absolute (REQUEST_TIMEOUT); | ||
1390 | return GNUNET_OK; | 1451 | return GNUNET_OK; |
1391 | } | 1452 | } |
1392 | 1453 | ||
@@ -1464,27 +1525,24 @@ run (void *cls, struct GNUNET_SERVER_Handle *server, | |||
1464 | char *ipv4mask; | 1525 | char *ipv4mask; |
1465 | char *ipv6addr; | 1526 | char *ipv6addr; |
1466 | char *ipv6prefix; | 1527 | char *ipv6prefix; |
1528 | struct in_addr dns_exit4; | ||
1529 | struct in6_addr dns_exit6; | ||
1467 | 1530 | ||
1468 | cfg = cfg_; | 1531 | cfg = cfg_; |
1469 | stats = GNUNET_STATISTICS_create ("dns", cfg); | 1532 | stats = GNUNET_STATISTICS_create ("dns", cfg); |
1470 | nc = GNUNET_SERVER_notification_context_create (server, 1); | 1533 | nc = GNUNET_SERVER_notification_context_create (server, 1); |
1471 | GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &cleanup_task, | 1534 | GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &cleanup_task, |
1472 | cls); | 1535 | cls); |
1473 | (void) GNUNET_CONFIGURATION_get_value_string (cfg, "dns", | 1536 | if ( (GNUNET_YES == |
1474 | "DNS_EXIT", | 1537 | GNUNET_CONFIGURATION_get_value_yesno (cfg_, "dns", "PROVIDE_EXIT")) && |
1475 | &dns_exit); | 1538 | ( (GNUNET_OK != |
1476 | if (GNUNET_YES == | 1539 | GNUNET_CONFIGURATION_get_value_string (cfg, "dns", |
1477 | GNUNET_CONFIGURATION_get_value_yesno (cfg_, "dns", "PROVIDE_EXIT")) | 1540 | "DNS_EXIT", |
1478 | { | 1541 | &dns_exit)) || |
1479 | if ( (GNUNET_OK != open_port4 ()) && | 1542 | ( (1 != inet_pton (AF_INET, dns_exit, &dns_exit4)) && |
1480 | (GNUNET_OK != open_port6 ()) ) | 1543 | (1 != inet_pton (AF_INET6, dns_exit, &dns_exit6)) ) ) ) |
1481 | { | 1544 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, |
1482 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | 1545 | _("Configured to provide DNS exit, but no valid DNS server configured!\n")); |
1483 | _("Failed to open any port to provide DNS exit\n")); | ||
1484 | GNUNET_SCHEDULER_shutdown (); | ||
1485 | return; | ||
1486 | } | ||
1487 | } | ||
1488 | 1546 | ||
1489 | helper_argv[0] = GNUNET_strdup ("gnunet-dns"); | 1547 | helper_argv[0] = GNUNET_strdup ("gnunet-dns"); |
1490 | if (GNUNET_SYSERR == | 1548 | if (GNUNET_SYSERR == |