diff options
author | Christian Grothoff <christian@grothoff.org> | 2013-09-22 19:18:15 +0000 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2013-09-22 19:18:15 +0000 |
commit | 3b1601c14f00f204e26706799ff85ed8bc132569 (patch) | |
tree | 97b20ba1d1fc1c8ba208ea91b3e07e5509c42a1d /src/pt/gnunet-daemon-pt.c | |
parent | dc07a7575ed89a6d3ab63912a76e8bffed767128 (diff) | |
download | gnunet-3b1601c14f00f204e26706799ff85ed8bc132569.tar.gz gnunet-3b1601c14f00f204e26706799ff85ed8bc132569.zip |
#2915: supporting multiple DNS exits and ranking among them
Diffstat (limited to 'src/pt/gnunet-daemon-pt.c')
-rw-r--r-- | src/pt/gnunet-daemon-pt.c | 535 |
1 files changed, 424 insertions, 111 deletions
diff --git a/src/pt/gnunet-daemon-pt.c b/src/pt/gnunet-daemon-pt.c index 326233633..eee851faf 100644 --- a/src/pt/gnunet-daemon-pt.c +++ b/src/pt/gnunet-daemon-pt.c | |||
@@ -50,6 +50,11 @@ | |||
50 | */ | 50 | */ |
51 | #define MAX_DNS_SIZE (8 * 1024) | 51 | #define MAX_DNS_SIZE (8 * 1024) |
52 | 52 | ||
53 | /** | ||
54 | * How many tunnels do we open at most at the same time? | ||
55 | */ | ||
56 | #define MAX_OPEN_TUNNELS 4 | ||
57 | |||
53 | 58 | ||
54 | /** | 59 | /** |
55 | * Which group of DNS records are we currently processing? | 60 | * Which group of DNS records are we currently processing? |
@@ -117,6 +122,79 @@ struct ReplyContext | |||
117 | 122 | ||
118 | 123 | ||
119 | /** | 124 | /** |
125 | * Handle to a peer that advertised that it is willing to serve | ||
126 | * as a DNS exit. We try to keep a few tunnels open and a few | ||
127 | * peers in reserve. | ||
128 | */ | ||
129 | struct MeshExit | ||
130 | { | ||
131 | |||
132 | /** | ||
133 | * Kept in a DLL. | ||
134 | */ | ||
135 | struct MeshExit *next; | ||
136 | |||
137 | /** | ||
138 | * Kept in a DLL. | ||
139 | */ | ||
140 | struct MeshExit *prev; | ||
141 | |||
142 | /** | ||
143 | * Tunnel we use for DNS requests over MESH, NULL if we did | ||
144 | * not initialze a tunnel to this peer yet. | ||
145 | */ | ||
146 | struct GNUNET_MESH_Tunnel *mesh_tunnel; | ||
147 | |||
148 | /** | ||
149 | * At what time did the peer's advertisement expire? | ||
150 | */ | ||
151 | struct GNUNET_TIME_Absolute expiration; | ||
152 | |||
153 | /** | ||
154 | * Head of DLL of requests waiting for a response. | ||
155 | */ | ||
156 | struct RequestContext *receive_queue_head; | ||
157 | |||
158 | /** | ||
159 | * Tail of DLL of requests waiting for a response. | ||
160 | */ | ||
161 | struct RequestContext *receive_queue_tail; | ||
162 | |||
163 | /** | ||
164 | * Head of DLL of requests to be transmitted to a mesh_tunnel. | ||
165 | */ | ||
166 | struct RequestContext *transmit_queue_head; | ||
167 | |||
168 | /** | ||
169 | * Tail of DLL of requests to be transmitted to a mesh_tunnel. | ||
170 | */ | ||
171 | struct RequestContext *transmit_queue_tail; | ||
172 | |||
173 | /** | ||
174 | * Active transmission request for this tunnel (or NULL). | ||
175 | */ | ||
176 | struct GNUNET_MESH_TransmitHandle *mesh_th; | ||
177 | |||
178 | /** | ||
179 | * Identity of the peer that is providing the exit for us. | ||
180 | */ | ||
181 | struct GNUNET_PeerIdentity peer; | ||
182 | |||
183 | /** | ||
184 | * How many DNS requests did we transmit via this tunnel? | ||
185 | */ | ||
186 | unsigned int num_transmitted; | ||
187 | |||
188 | /** | ||
189 | * How many DNS requests were answered via this tunnel? | ||
190 | */ | ||
191 | unsigned int num_answered; | ||
192 | |||
193 | }; | ||
194 | |||
195 | |||
196 | |||
197 | /** | ||
120 | * State we keep for a request that is going out via MESH. | 198 | * State we keep for a request that is going out via MESH. |
121 | */ | 199 | */ |
122 | struct RequestContext | 200 | struct RequestContext |
@@ -132,6 +210,11 @@ struct RequestContext | |||
132 | struct RequestContext *prev; | 210 | struct RequestContext *prev; |
133 | 211 | ||
134 | /** | 212 | /** |
213 | * Exit that was chosen for this request. | ||
214 | */ | ||
215 | struct MeshExit *exit; | ||
216 | |||
217 | /** | ||
135 | * Handle for interaction with DNS service. | 218 | * Handle for interaction with DNS service. |
136 | */ | 219 | */ |
137 | struct GNUNET_DNS_RequestHandle *rh; | 220 | struct GNUNET_DNS_RequestHandle *rh; |
@@ -148,6 +231,11 @@ struct RequestContext | |||
148 | GNUNET_SCHEDULER_TaskIdentifier timeout_task; | 231 | GNUNET_SCHEDULER_TaskIdentifier timeout_task; |
149 | 232 | ||
150 | /** | 233 | /** |
234 | * Length of the request message that follows this struct. | ||
235 | */ | ||
236 | uint16_t mlen; | ||
237 | |||
238 | /** | ||
151 | * ID of the original DNS request (used to match the reply). | 239 | * ID of the original DNS request (used to match the reply). |
152 | */ | 240 | */ |
153 | uint16_t dns_id; | 241 | uint16_t dns_id; |
@@ -162,51 +250,31 @@ struct RequestContext | |||
162 | 250 | ||
163 | 251 | ||
164 | /** | 252 | /** |
165 | * The handle to the configuration used throughout the process | 253 | * Head of DLL of mesh exits. Mesh exits with an open tunnel are |
254 | * always at the beginning (so we do not have to traverse the entire | ||
255 | * list to find them). | ||
166 | */ | 256 | */ |
167 | static const struct GNUNET_CONFIGURATION_Handle *cfg; | 257 | static struct MeshExit *exit_head; |
168 | 258 | ||
169 | /** | 259 | /** |
170 | * The handle to the VPN | 260 | * Tail of DLL of mesh exits. |
171 | */ | 261 | */ |
172 | static struct GNUNET_VPN_Handle *vpn_handle; | 262 | static struct MeshExit *exit_tail; |
173 | 263 | ||
174 | /** | 264 | /** |
175 | * The handle to the MESH service | 265 | * The handle to the configuration used throughout the process |
176 | */ | ||
177 | static struct GNUNET_MESH_Handle *mesh_handle; | ||
178 | |||
179 | /** | ||
180 | * Tunnel we use for DNS requests over MESH. | ||
181 | * FIXME: we might want to keep multiple tunnels open | ||
182 | * at all times... | ||
183 | */ | ||
184 | static struct GNUNET_MESH_Tunnel *mesh_tunnel; | ||
185 | |||
186 | /** | ||
187 | * Active transmission request with MESH (or NULL). | ||
188 | */ | ||
189 | static struct GNUNET_MESH_TransmitHandle *mesh_th; | ||
190 | |||
191 | /** | ||
192 | * Head of DLL of requests to be transmitted to mesh_tunnel. | ||
193 | */ | ||
194 | static struct RequestContext *transmit_queue_head; | ||
195 | |||
196 | /** | ||
197 | * Tail of DLL of requests to be transmitted to mesh_tunnel. | ||
198 | */ | 266 | */ |
199 | static struct RequestContext *transmit_queue_tail; | 267 | static const struct GNUNET_CONFIGURATION_Handle *cfg; |
200 | 268 | ||
201 | /** | 269 | /** |
202 | * Head of DLL of requests waiting for a response. | 270 | * The handle to the VPN |
203 | */ | 271 | */ |
204 | static struct RequestContext *receive_queue_head; | 272 | static struct GNUNET_VPN_Handle *vpn_handle; |
205 | 273 | ||
206 | /** | 274 | /** |
207 | * Tail of DLL of requests waiting for a response. | 275 | * The handle to the MESH service |
208 | */ | 276 | */ |
209 | static struct RequestContext *receive_queue_tail; | 277 | static struct GNUNET_MESH_Handle *mesh_handle; |
210 | 278 | ||
211 | /** | 279 | /** |
212 | * Statistics. | 280 | * Statistics. |
@@ -250,12 +318,150 @@ static int dns_tunnel; | |||
250 | 318 | ||
251 | /** | 319 | /** |
252 | * Number of DNS exit peers we currently have in the mesh tunnel. | 320 | * Number of DNS exit peers we currently have in the mesh tunnel. |
253 | * Used to see if using the mesh tunnel makes any sense right now. | 321 | * Used to see if using the mesh tunnel makes any sense right now, |
322 | * as well as to decide if we should open new tunnels. | ||
254 | */ | 323 | */ |
255 | static unsigned int dns_exit_available; | 324 | static unsigned int dns_exit_available; |
256 | 325 | ||
257 | 326 | ||
258 | /** | 327 | /** |
328 | * We are short on mesh exits, try to open another one. | ||
329 | */ | ||
330 | static void | ||
331 | try_open_exit () | ||
332 | { | ||
333 | struct MeshExit *pos; | ||
334 | uint32_t candidate_count; | ||
335 | uint32_t candidate_selected; | ||
336 | |||
337 | candidate_count = 0; | ||
338 | for (pos = exit_head; NULL != pos; pos = pos->next) | ||
339 | if (NULL == pos->mesh_tunnel) | ||
340 | candidate_count++; | ||
341 | candidate_selected = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK, | ||
342 | candidate_count); | ||
343 | candidate_count = 0; | ||
344 | for (pos = exit_head; NULL != pos; pos = pos->next) | ||
345 | if (NULL == pos->mesh_tunnel) | ||
346 | { | ||
347 | candidate_count++; | ||
348 | if (candidate_selected < candidate_count) | ||
349 | { | ||
350 | /* move to the head of the DLL */ | ||
351 | pos->mesh_tunnel = GNUNET_MESH_tunnel_create (mesh_handle, | ||
352 | pos, | ||
353 | &pos->peer, | ||
354 | GNUNET_APPLICATION_TYPE_INTERNET_RESOLVER, | ||
355 | GNUNET_YES /* no buffer */, | ||
356 | GNUNET_NO /* reliable */); | ||
357 | if (NULL == pos->mesh_tunnel) | ||
358 | { | ||
359 | GNUNET_break (0); | ||
360 | continue; | ||
361 | } | ||
362 | GNUNET_CONTAINER_DLL_remove (exit_head, | ||
363 | exit_tail, | ||
364 | pos); | ||
365 | GNUNET_CONTAINER_DLL_insert (exit_head, | ||
366 | exit_tail, | ||
367 | pos); | ||
368 | dns_exit_available++; | ||
369 | return; | ||
370 | } | ||
371 | } | ||
372 | GNUNET_assert (NULL == exit_head); | ||
373 | } | ||
374 | |||
375 | |||
376 | /** | ||
377 | * Compute the weight of the given exit. The higher the weight, | ||
378 | * the more likely it will be that the tunnel will be chosen. | ||
379 | * A weigt of zero means that we should close the tunnel as it | ||
380 | * is so bad, that we should not use it. | ||
381 | * | ||
382 | * @param exit exit to calculate the weight for | ||
383 | * @return weight of the tunnel | ||
384 | */ | ||
385 | static uint32_t | ||
386 | get_tunnel_weight (struct MeshExit *exit) | ||
387 | { | ||
388 | uint32_t dropped; | ||
389 | uint32_t drop_percent; | ||
390 | uint32_t good_percent; | ||
391 | |||
392 | GNUNET_assert (exit->num_transmitted >= exit->num_answered); | ||
393 | dropped = exit->num_transmitted - exit->num_answered; | ||
394 | if (exit->num_transmitted > 0) | ||
395 | drop_percent = (uint32_t) ((100LL * dropped) / exit->num_transmitted); | ||
396 | else | ||
397 | drop_percent = 50; /* no data */ | ||
398 | if ( (exit->num_transmitted > 20) && | ||
399 | (drop_percent > 25) ) | ||
400 | return 0; /* statistically significant, and > 25% loss, die */ | ||
401 | good_percent = 100 - drop_percent; | ||
402 | GNUNET_assert (0 != good_percent); | ||
403 | if ( UINT32_MAX / good_percent / good_percent < exit->num_transmitted) | ||
404 | return UINT32_MAX; /* formula below would overflow */ | ||
405 | return 1 + good_percent * good_percent * exit->num_transmitted; | ||
406 | } | ||
407 | |||
408 | |||
409 | /** | ||
410 | * Choose a mesh exit for a DNS request. We try to use a tunnel | ||
411 | * that is reliable and currently available. All existing | ||
412 | * tunnels are given a base weight of 1, plus a score relating | ||
413 | * to the total number of queries answered in relation to the | ||
414 | * total number of queries we sent to that tunnel. That | ||
415 | * score is doubled if the tunnel is currently idle. | ||
416 | * | ||
417 | * @return NULL if no exit is known, otherwise the | ||
418 | * exit that we should use to queue a message with | ||
419 | */ | ||
420 | static struct MeshExit * | ||
421 | choose_exit () | ||
422 | { | ||
423 | struct MeshExit *pos; | ||
424 | uint64_t total_transmitted; | ||
425 | uint64_t selected_offset; | ||
426 | uint32_t tunnel_weight; | ||
427 | |||
428 | total_transmitted = 0; | ||
429 | for (pos = exit_head; NULL != pos; pos = pos->next) | ||
430 | { | ||
431 | if (NULL == pos->mesh_tunnel) | ||
432 | break; | ||
433 | tunnel_weight = get_tunnel_weight (pos); | ||
434 | total_transmitted += tunnel_weight; | ||
435 | /* double weight for idle tunnels */ | ||
436 | if (NULL == pos->mesh_th) | ||
437 | total_transmitted += tunnel_weight; | ||
438 | } | ||
439 | if (0 == total_transmitted) | ||
440 | { | ||
441 | /* no tunnels available, or only a very bad one... */ | ||
442 | return exit_head; | ||
443 | } | ||
444 | selected_offset = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK, | ||
445 | total_transmitted); | ||
446 | total_transmitted = 0; | ||
447 | for (pos = exit_head; NULL != pos; pos = pos->next) | ||
448 | { | ||
449 | if (NULL == pos->mesh_tunnel) | ||
450 | break; | ||
451 | tunnel_weight = get_tunnel_weight (pos); | ||
452 | total_transmitted += tunnel_weight; | ||
453 | /* double weight for idle tunnels */ | ||
454 | if (NULL == pos->mesh_th) | ||
455 | total_transmitted += tunnel_weight; | ||
456 | if (total_transmitted > selected_offset) | ||
457 | return pos; | ||
458 | } | ||
459 | GNUNET_break (0); | ||
460 | return NULL; | ||
461 | } | ||
462 | |||
463 | |||
464 | /** | ||
259 | * We're done modifying all records in the response. Submit the reply | 465 | * We're done modifying all records in the response. Submit the reply |
260 | * and free the resources of the rc. | 466 | * and free the resources of the rc. |
261 | * | 467 | * |
@@ -542,7 +748,7 @@ dns_post_request_handler (void *cls, | |||
542 | * Transmit a DNS request via MESH and move the request | 748 | * Transmit a DNS request via MESH and move the request |
543 | * handle to the receive queue. | 749 | * handle to the receive queue. |
544 | * | 750 | * |
545 | * @param cls NULL | 751 | * @param cls the `struct MeshExit` |
546 | * @param size number of bytes available in buf | 752 | * @param size number of bytes available in buf |
547 | * @param buf where to copy the message | 753 | * @param buf where to copy the message |
548 | * @return number of bytes written to buf | 754 | * @return number of bytes written to buf |
@@ -552,40 +758,41 @@ transmit_dns_request_to_mesh (void *cls, | |||
552 | size_t size, | 758 | size_t size, |
553 | void *buf) | 759 | void *buf) |
554 | { | 760 | { |
761 | struct MeshExit *exit = cls; | ||
555 | struct RequestContext *rc; | 762 | struct RequestContext *rc; |
556 | size_t mlen; | 763 | size_t mlen; |
557 | 764 | ||
558 | mesh_th = NULL; | 765 | exit->mesh_th = NULL; |
559 | if (NULL == (rc = transmit_queue_head)) | 766 | if (NULL == (rc = exit->transmit_queue_head)) |
560 | return 0; | 767 | return 0; |
561 | mlen = ntohs (rc->mesh_message->size); | 768 | mlen = rc->mlen; |
562 | if (mlen > size) | 769 | if (mlen > size) |
563 | { | 770 | { |
564 | mesh_th = GNUNET_MESH_notify_transmit_ready (mesh_tunnel, | 771 | exit->mesh_th = GNUNET_MESH_notify_transmit_ready (exit->mesh_tunnel, |
565 | GNUNET_NO, | 772 | GNUNET_NO, |
566 | TIMEOUT, | 773 | TIMEOUT, |
567 | mlen, | 774 | mlen, |
568 | &transmit_dns_request_to_mesh, | 775 | &transmit_dns_request_to_mesh, |
569 | NULL); | 776 | exit); |
570 | return 0; | 777 | return 0; |
571 | } | 778 | } |
572 | GNUNET_assert (GNUNET_NO == rc->was_transmitted); | 779 | GNUNET_assert (GNUNET_NO == rc->was_transmitted); |
573 | memcpy (buf, rc->mesh_message, mlen); | 780 | memcpy (buf, rc->mesh_message, mlen); |
574 | GNUNET_CONTAINER_DLL_remove (transmit_queue_head, | 781 | GNUNET_CONTAINER_DLL_remove (exit->transmit_queue_head, |
575 | transmit_queue_tail, | 782 | exit->transmit_queue_tail, |
576 | rc); | 783 | rc); |
577 | rc->was_transmitted = GNUNET_YES; | 784 | rc->was_transmitted = GNUNET_YES; |
578 | GNUNET_CONTAINER_DLL_insert (receive_queue_head, | 785 | GNUNET_CONTAINER_DLL_insert (exit->receive_queue_head, |
579 | receive_queue_tail, | 786 | exit->receive_queue_tail, |
580 | rc); | 787 | rc); |
581 | rc = transmit_queue_head; | 788 | rc = exit->transmit_queue_head; |
582 | if (NULL != rc) | 789 | if (NULL != rc) |
583 | mesh_th = GNUNET_MESH_notify_transmit_ready (mesh_tunnel, | 790 | exit->mesh_th = GNUNET_MESH_notify_transmit_ready (exit->mesh_tunnel, |
584 | GNUNET_NO, | 791 | GNUNET_NO, |
585 | TIMEOUT, | 792 | TIMEOUT, |
586 | ntohs (rc->mesh_message->size), | 793 | rc->mlen, |
587 | &transmit_dns_request_to_mesh, | 794 | &transmit_dns_request_to_mesh, |
588 | NULL); | 795 | exit); |
589 | return mlen; | 796 | return mlen; |
590 | } | 797 | } |
591 | 798 | ||
@@ -593,7 +800,7 @@ transmit_dns_request_to_mesh (void *cls, | |||
593 | /** | 800 | /** |
594 | * Task run if the time to answer a DNS request via MESH is over. | 801 | * Task run if the time to answer a DNS request via MESH is over. |
595 | * | 802 | * |
596 | * @param cls the 'struct RequestContext' to abort | 803 | * @param cls the `struct RequestContext` to abort |
597 | * @param tc scheduler context | 804 | * @param tc scheduler context |
598 | */ | 805 | */ |
599 | static void | 806 | static void |
@@ -601,20 +808,52 @@ timeout_request (void *cls, | |||
601 | const struct GNUNET_SCHEDULER_TaskContext *tc) | 808 | const struct GNUNET_SCHEDULER_TaskContext *tc) |
602 | { | 809 | { |
603 | struct RequestContext *rc = cls; | 810 | struct RequestContext *rc = cls; |
811 | struct MeshExit *exit = rc->exit; | ||
604 | 812 | ||
605 | if (rc->was_transmitted) | 813 | if (rc->was_transmitted) |
606 | GNUNET_CONTAINER_DLL_remove (receive_queue_head, | 814 | { |
607 | receive_queue_tail, | 815 | exit->num_transmitted++; |
816 | GNUNET_CONTAINER_DLL_remove (exit->receive_queue_head, | ||
817 | exit->receive_queue_tail, | ||
608 | rc); | 818 | rc); |
819 | } | ||
609 | else | 820 | else |
610 | GNUNET_CONTAINER_DLL_remove (transmit_queue_head, | 821 | { |
611 | transmit_queue_tail, | 822 | GNUNET_CONTAINER_DLL_remove (exit->transmit_queue_head, |
823 | exit->transmit_queue_tail, | ||
612 | rc); | 824 | rc); |
825 | } | ||
613 | GNUNET_STATISTICS_update (stats, | 826 | GNUNET_STATISTICS_update (stats, |
614 | gettext_noop ("# DNS requests dropped (timeout)"), | 827 | gettext_noop ("# DNS requests dropped (timeout)"), |
615 | 1, GNUNET_NO); | 828 | 1, GNUNET_NO); |
616 | GNUNET_DNS_request_drop (rc->rh); | 829 | GNUNET_DNS_request_drop (rc->rh); |
617 | GNUNET_free (rc); | 830 | GNUNET_free (rc); |
831 | if ( (0 == get_tunnel_weight (exit)) && | ||
832 | (NULL == exit->receive_queue_head) && | ||
833 | (NULL == exit->transmit_queue_head) ) | ||
834 | { | ||
835 | /* this straw broke the camel's back: this tunnel now has | ||
836 | such a low score that it will not be used; close it! */ | ||
837 | GNUNET_assert (NULL == exit->mesh_th); | ||
838 | GNUNET_MESH_tunnel_destroy (exit->mesh_tunnel); | ||
839 | exit->mesh_tunnel = NULL; | ||
840 | GNUNET_CONTAINER_DLL_remove (exit_head, | ||
841 | exit_tail, | ||
842 | exit); | ||
843 | GNUNET_CONTAINER_DLL_insert_tail (exit_head, | ||
844 | exit_tail, | ||
845 | exit); | ||
846 | /* go back to semi-innocent: mark as not great, but | ||
847 | avoid a prohibitively negative score (see | ||
848 | #get_tunnel_weight, which checks for a certain | ||
849 | minimum number of transmissions before making | ||
850 | up an opinion) */ | ||
851 | exit->num_transmitted = 5; | ||
852 | exit->num_answered = 0; | ||
853 | dns_exit_available--; | ||
854 | /* now try to open an alternative exit */ | ||
855 | try_open_exit (); | ||
856 | } | ||
618 | } | 857 | } |
619 | 858 | ||
620 | 859 | ||
@@ -639,6 +878,7 @@ dns_pre_request_handler (void *cls, | |||
639 | size_t mlen; | 878 | size_t mlen; |
640 | struct GNUNET_MessageHeader hdr; | 879 | struct GNUNET_MessageHeader hdr; |
641 | struct GNUNET_TUN_DnsHeader dns; | 880 | struct GNUNET_TUN_DnsHeader dns; |
881 | struct MeshExit *exit; | ||
642 | 882 | ||
643 | GNUNET_STATISTICS_update (stats, | 883 | GNUNET_STATISTICS_update (stats, |
644 | gettext_noop ("# DNS requests intercepted"), | 884 | gettext_noop ("# DNS requests intercepted"), |
@@ -660,49 +900,55 @@ dns_pre_request_handler (void *cls, | |||
660 | return; | 900 | return; |
661 | } | 901 | } |
662 | memcpy (&dns, request, sizeof (dns)); | 902 | memcpy (&dns, request, sizeof (dns)); |
663 | GNUNET_assert (NULL != mesh_tunnel); | ||
664 | mlen = sizeof (struct GNUNET_MessageHeader) + request_length; | 903 | mlen = sizeof (struct GNUNET_MessageHeader) + request_length; |
904 | exit = choose_exit (); | ||
905 | GNUNET_assert (NULL != exit); | ||
906 | GNUNET_assert (NULL != exit->mesh_tunnel); | ||
665 | rc = GNUNET_malloc (sizeof (struct RequestContext) + mlen); | 907 | rc = GNUNET_malloc (sizeof (struct RequestContext) + mlen); |
908 | rc->exit = exit; | ||
666 | rc->rh = rh; | 909 | rc->rh = rh; |
667 | rc->mesh_message = (const struct GNUNET_MessageHeader*) &rc[1]; | 910 | rc->mesh_message = (const struct GNUNET_MessageHeader*) &rc[1]; |
668 | rc->timeout_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, | 911 | rc->timeout_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT, |
669 | &timeout_request, | 912 | &timeout_request, |
670 | rc); | 913 | rc); |
671 | rc->dns_id = dns.id; | 914 | rc->dns_id = dns.id; |
915 | rc->mlen = mlen; | ||
672 | hdr.type = htons (GNUNET_MESSAGE_TYPE_VPN_DNS_TO_INTERNET); | 916 | hdr.type = htons (GNUNET_MESSAGE_TYPE_VPN_DNS_TO_INTERNET); |
673 | hdr.size = htons (mlen); | 917 | hdr.size = htons (mlen); |
674 | memcpy (&rc[1], &hdr, sizeof (struct GNUNET_MessageHeader)); | 918 | memcpy (&rc[1], &hdr, sizeof (struct GNUNET_MessageHeader)); |
675 | memcpy (&(((char*)&rc[1])[sizeof (struct GNUNET_MessageHeader)]), | 919 | memcpy (&(((char*)&rc[1])[sizeof (struct GNUNET_MessageHeader)]), |
676 | request, | 920 | request, |
677 | request_length); | 921 | request_length); |
678 | GNUNET_CONTAINER_DLL_insert_tail (transmit_queue_head, | 922 | GNUNET_CONTAINER_DLL_insert_tail (exit->transmit_queue_head, |
679 | transmit_queue_tail, | 923 | exit->transmit_queue_tail, |
680 | rc); | 924 | rc); |
681 | if (NULL == mesh_th) | 925 | if (NULL == exit->mesh_th) |
682 | mesh_th = GNUNET_MESH_notify_transmit_ready (mesh_tunnel, | 926 | exit->mesh_th = GNUNET_MESH_notify_transmit_ready (exit->mesh_tunnel, |
683 | GNUNET_NO, | 927 | GNUNET_NO, |
684 | TIMEOUT, | 928 | TIMEOUT, |
685 | mlen, | 929 | mlen, |
686 | &transmit_dns_request_to_mesh, | 930 | &transmit_dns_request_to_mesh, |
687 | NULL); | 931 | exit); |
688 | } | 932 | } |
689 | 933 | ||
690 | 934 | ||
691 | /** | 935 | /** |
692 | * Process a request via mesh to perform a DNS query. | 936 | * Process a request via mesh to perform a DNS query. |
693 | * | 937 | * |
694 | * @param cls closure, NULL | 938 | * @param cls NULL |
695 | * @param tunnel connection to the other end | 939 | * @param tunnel connection to the other end |
696 | * @param tunnel_ctx FIXME | 940 | * @param tunnel_ctx pointer to our `struct MeshExit` |
697 | * @param message the actual message | 941 | * @param message the actual message |
698 | * @return #GNUNET_OK to keep the connection open, | 942 | * @return #GNUNET_OK to keep the connection open, |
699 | * #GNUNET_SYSERR to close it (signal serious error) | 943 | * #GNUNET_SYSERR to close it (signal serious error) |
700 | */ | 944 | */ |
701 | static int | 945 | static int |
702 | receive_dns_response (void *cls GNUNET_UNUSED, struct GNUNET_MESH_Tunnel *tunnel, | 946 | receive_dns_response (void *cls, |
947 | struct GNUNET_MESH_Tunnel *tunnel, | ||
703 | void **tunnel_ctx, | 948 | void **tunnel_ctx, |
704 | const struct GNUNET_MessageHeader *message) | 949 | const struct GNUNET_MessageHeader *message) |
705 | { | 950 | { |
951 | struct MeshExit *exit = *tunnel_ctx; | ||
706 | struct GNUNET_TUN_DnsHeader dns; | 952 | struct GNUNET_TUN_DnsHeader dns; |
707 | size_t mlen; | 953 | size_t mlen; |
708 | struct RequestContext *rc; | 954 | struct RequestContext *rc; |
@@ -715,7 +961,7 @@ receive_dns_response (void *cls GNUNET_UNUSED, struct GNUNET_MESH_Tunnel *tunnel | |||
715 | return GNUNET_SYSERR; | 961 | return GNUNET_SYSERR; |
716 | } | 962 | } |
717 | memcpy (&dns, &message[1], sizeof (dns)); | 963 | memcpy (&dns, &message[1], sizeof (dns)); |
718 | for (rc = receive_queue_head; NULL != rc; rc = rc->next) | 964 | for (rc = exit->receive_queue_head; NULL != rc; rc = rc->next) |
719 | { | 965 | { |
720 | GNUNET_assert (GNUNET_YES == rc->was_transmitted); | 966 | GNUNET_assert (GNUNET_YES == rc->was_transmitted); |
721 | if (dns.id == rc->dns_id) | 967 | if (dns.id == rc->dns_id) |
@@ -726,11 +972,13 @@ receive_dns_response (void *cls GNUNET_UNUSED, struct GNUNET_MESH_Tunnel *tunnel | |||
726 | GNUNET_DNS_request_answer (rc->rh, | 972 | GNUNET_DNS_request_answer (rc->rh, |
727 | mlen, | 973 | mlen, |
728 | (const void*) &message[1]); | 974 | (const void*) &message[1]); |
729 | GNUNET_CONTAINER_DLL_remove (receive_queue_head, | 975 | GNUNET_CONTAINER_DLL_remove (exit->receive_queue_head, |
730 | receive_queue_tail, | 976 | exit->receive_queue_tail, |
731 | rc); | 977 | rc); |
732 | GNUNET_SCHEDULER_cancel (rc->timeout_task); | 978 | GNUNET_SCHEDULER_cancel (rc->timeout_task); |
733 | GNUNET_free (rc); | 979 | GNUNET_free (rc); |
980 | exit->num_answered++; | ||
981 | exit->num_transmitted++; | ||
734 | return GNUNET_OK; | 982 | return GNUNET_OK; |
735 | } | 983 | } |
736 | } | 984 | } |
@@ -742,33 +990,28 @@ receive_dns_response (void *cls GNUNET_UNUSED, struct GNUNET_MESH_Tunnel *tunnel | |||
742 | 990 | ||
743 | 991 | ||
744 | /** | 992 | /** |
745 | * The MESH DNS tunnel went down. Abort all pending DNS | 993 | * Abort all pending DNS requests with the given mesh exit. |
746 | * requests (we're unlikely to get an answer in time). | 994 | * |
995 | * @param exit mesh exit to abort requests for | ||
747 | */ | 996 | */ |
748 | static void | 997 | static void |
749 | abort_all_requests () | 998 | abort_all_requests (struct MeshExit *exit) |
750 | { | 999 | { |
751 | struct RequestContext *rc; | 1000 | struct RequestContext *rc; |
752 | 1001 | ||
753 | while (NULL != (rc = receive_queue_head)) | 1002 | while (NULL != (rc = exit->receive_queue_head)) |
754 | { | 1003 | { |
755 | GNUNET_STATISTICS_update (stats, | 1004 | GNUNET_CONTAINER_DLL_remove (exit->receive_queue_head, |
756 | gettext_noop ("# DNS requests aborted (tunnel down)"), | 1005 | exit->receive_queue_tail, |
757 | 1, GNUNET_NO); | ||
758 | GNUNET_CONTAINER_DLL_remove (receive_queue_head, | ||
759 | receive_queue_tail, | ||
760 | rc); | 1006 | rc); |
761 | GNUNET_DNS_request_drop (rc->rh); | 1007 | GNUNET_DNS_request_drop (rc->rh); |
762 | GNUNET_SCHEDULER_cancel (rc->timeout_task); | 1008 | GNUNET_SCHEDULER_cancel (rc->timeout_task); |
763 | GNUNET_free (rc); | 1009 | GNUNET_free (rc); |
764 | } | 1010 | } |
765 | while (NULL != (rc = transmit_queue_head)) | 1011 | while (NULL != (rc = exit->transmit_queue_head)) |
766 | { | 1012 | { |
767 | GNUNET_STATISTICS_update (stats, | 1013 | GNUNET_CONTAINER_DLL_remove (exit->transmit_queue_head, |
768 | gettext_noop ("# DNS requests aborted (tunnel down)"), | 1014 | exit->transmit_queue_tail, |
769 | 1, GNUNET_NO); | ||
770 | GNUNET_CONTAINER_DLL_remove (transmit_queue_head, | ||
771 | transmit_queue_tail, | ||
772 | rc); | 1015 | rc); |
773 | GNUNET_DNS_request_drop (rc->rh); | 1016 | GNUNET_DNS_request_drop (rc->rh); |
774 | GNUNET_SCHEDULER_cancel (rc->timeout_task); | 1017 | GNUNET_SCHEDULER_cancel (rc->timeout_task); |
@@ -787,6 +1030,8 @@ static void | |||
787 | cleanup (void *cls, | 1030 | cleanup (void *cls, |
788 | const struct GNUNET_SCHEDULER_TaskContext *tskctx) | 1031 | const struct GNUNET_SCHEDULER_TaskContext *tskctx) |
789 | { | 1032 | { |
1033 | struct MeshExit *exit; | ||
1034 | |||
790 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | 1035 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, |
791 | "Protocol translation daemon is shutting down now\n"); | 1036 | "Protocol translation daemon is shutting down now\n"); |
792 | if (NULL != vpn_handle) | 1037 | if (NULL != vpn_handle) |
@@ -794,22 +1039,29 @@ cleanup (void *cls, | |||
794 | GNUNET_VPN_disconnect (vpn_handle); | 1039 | GNUNET_VPN_disconnect (vpn_handle); |
795 | vpn_handle = NULL; | 1040 | vpn_handle = NULL; |
796 | } | 1041 | } |
797 | if (NULL != mesh_th) | 1042 | while (NULL != (exit = exit_head)) |
798 | { | 1043 | { |
799 | GNUNET_MESH_notify_transmit_ready_cancel (mesh_th); | 1044 | GNUNET_CONTAINER_DLL_remove (exit_head, |
800 | mesh_th = NULL; | 1045 | exit_tail, |
801 | } | 1046 | exit); |
802 | if (NULL != mesh_tunnel) | 1047 | if (NULL != exit->mesh_th) |
803 | { | 1048 | { |
804 | GNUNET_MESH_tunnel_destroy (mesh_tunnel); | 1049 | GNUNET_MESH_notify_transmit_ready_cancel (exit->mesh_th); |
805 | mesh_tunnel = NULL; | 1050 | exit->mesh_th = NULL; |
1051 | } | ||
1052 | if (NULL != exit->mesh_tunnel) | ||
1053 | { | ||
1054 | GNUNET_MESH_tunnel_destroy (exit->mesh_tunnel); | ||
1055 | exit->mesh_tunnel = NULL; | ||
1056 | } | ||
1057 | abort_all_requests (exit); | ||
1058 | GNUNET_free (exit); | ||
806 | } | 1059 | } |
807 | if (NULL != mesh_handle) | 1060 | if (NULL != mesh_handle) |
808 | { | 1061 | { |
809 | GNUNET_MESH_disconnect (mesh_handle); | 1062 | GNUNET_MESH_disconnect (mesh_handle); |
810 | mesh_handle = NULL; | 1063 | mesh_handle = NULL; |
811 | } | 1064 | } |
812 | abort_all_requests (); | ||
813 | if (NULL != dns_post_handle) | 1065 | if (NULL != dns_post_handle) |
814 | { | 1066 | { |
815 | GNUNET_DNS_disconnect (dns_post_handle); | 1067 | GNUNET_DNS_disconnect (dns_post_handle); |
@@ -838,14 +1090,13 @@ cleanup (void *cls, | |||
838 | } | 1090 | } |
839 | 1091 | ||
840 | 1092 | ||
841 | |||
842 | /** | 1093 | /** |
843 | * Function called whenever a tunnel is destroyed. Should clean up | 1094 | * Function called whenever a tunnel is destroyed. Should clean up |
844 | * the associated state and attempt to build a new one. | 1095 | * the associated state and attempt to build a new one. |
845 | * | 1096 | * |
846 | * It must NOT call #GNUNET_MESH_tunnel_destroy on the tunnel. | 1097 | * It must NOT call #GNUNET_MESH_tunnel_destroy on the tunnel. |
847 | * | 1098 | * |
848 | * @param cls closure (set from #GNUNET_MESH_connect) | 1099 | * @param cls closure (the `struct MeshExit` set from #GNUNET_MESH_connect) |
849 | * @param tunnel connection to the other end (henceforth invalid) | 1100 | * @param tunnel connection to the other end (henceforth invalid) |
850 | * @param tunnel_ctx place where local state associated | 1101 | * @param tunnel_ctx place where local state associated |
851 | * with the tunnel is stored | 1102 | * with the tunnel is stored |
@@ -855,7 +1106,59 @@ mesh_tunnel_end_cb (void *cls, | |||
855 | const struct GNUNET_MESH_Tunnel *tunnel, | 1106 | const struct GNUNET_MESH_Tunnel *tunnel, |
856 | void *tunnel_ctx) | 1107 | void *tunnel_ctx) |
857 | { | 1108 | { |
858 | // FIXME: do cleanup here! | 1109 | struct MeshExit *exit = tunnel_ctx; |
1110 | struct MeshExit *alt; | ||
1111 | struct RequestContext *rc; | ||
1112 | |||
1113 | if (NULL != exit->mesh_th) | ||
1114 | { | ||
1115 | GNUNET_MESH_notify_transmit_ready_cancel (exit->mesh_th); | ||
1116 | exit->mesh_th = NULL; | ||
1117 | } | ||
1118 | exit->mesh_tunnel = NULL; | ||
1119 | dns_exit_available--; | ||
1120 | /* open alternative tunnels */ | ||
1121 | try_open_exit (); | ||
1122 | if (NULL == exit->mesh_tunnel) | ||
1123 | { | ||
1124 | /* our tunnel is now closed, move our requests to an alternative | ||
1125 | tunnel */ | ||
1126 | alt = choose_exit (); | ||
1127 | while (NULL != (rc = exit->transmit_queue_head)) | ||
1128 | { | ||
1129 | GNUNET_CONTAINER_DLL_remove (exit->transmit_queue_head, | ||
1130 | exit->transmit_queue_tail, | ||
1131 | rc); | ||
1132 | rc->exit = alt; | ||
1133 | GNUNET_CONTAINER_DLL_insert (alt->transmit_queue_head, | ||
1134 | alt->transmit_queue_tail, | ||
1135 | rc); | ||
1136 | } | ||
1137 | while (NULL != (rc = exit->receive_queue_head)) | ||
1138 | { | ||
1139 | GNUNET_CONTAINER_DLL_remove (exit->receive_queue_head, | ||
1140 | exit->receive_queue_tail, | ||
1141 | rc); | ||
1142 | rc->was_transmitted = GNUNET_NO; | ||
1143 | rc->exit = alt; | ||
1144 | GNUNET_CONTAINER_DLL_insert (alt->transmit_queue_head, | ||
1145 | alt->transmit_queue_tail, | ||
1146 | rc); | ||
1147 | } | ||
1148 | } | ||
1149 | else | ||
1150 | { | ||
1151 | /* the same peer was chosen, just make sure the queue processing is restarted */ | ||
1152 | alt = exit; | ||
1153 | } | ||
1154 | if ( (NULL == alt->mesh_th) && | ||
1155 | (NULL != (rc = alt->transmit_queue_head)) ) | ||
1156 | alt->mesh_th = GNUNET_MESH_notify_transmit_ready (alt->mesh_tunnel, | ||
1157 | GNUNET_NO, | ||
1158 | TIMEOUT, | ||
1159 | rc->mlen, | ||
1160 | &transmit_dns_request_to_mesh, | ||
1161 | alt); | ||
859 | } | 1162 | } |
860 | 1163 | ||
861 | 1164 | ||
@@ -891,6 +1194,7 @@ handle_dht_result (void *cls, | |||
891 | { | 1194 | { |
892 | const struct GNUNET_DNS_Advertisement *ad; | 1195 | const struct GNUNET_DNS_Advertisement *ad; |
893 | struct GNUNET_PeerIdentity pid; | 1196 | struct GNUNET_PeerIdentity pid; |
1197 | struct MeshExit *exit; | ||
894 | 1198 | ||
895 | if (sizeof (struct GNUNET_DNS_Advertisement) != size) | 1199 | if (sizeof (struct GNUNET_DNS_Advertisement) != size) |
896 | { | 1200 | { |
@@ -901,15 +1205,24 @@ handle_dht_result (void *cls, | |||
901 | GNUNET_CRYPTO_hash (&ad->peer, | 1205 | GNUNET_CRYPTO_hash (&ad->peer, |
902 | sizeof (struct GNUNET_CRYPTO_EccPublicSignKey), | 1206 | sizeof (struct GNUNET_CRYPTO_EccPublicSignKey), |
903 | &pid.hashPubKey); | 1207 | &pid.hashPubKey); |
904 | /* FIXME: decide between creating more mesh tunnels and | 1208 | for (exit = exit_head; NULL != exit; exit = exit->next) |
905 | just remembering the peer ID */ | 1209 | if (0 == memcmp (&pid, |
906 | mesh_tunnel = GNUNET_MESH_tunnel_create (mesh_handle, | 1210 | &exit->peer, |
907 | NULL /* FIXME: tunnel ctx */, | 1211 | sizeof (struct GNUNET_PeerIdentity))) |
908 | &pid, | 1212 | break; |
909 | GNUNET_APPLICATION_TYPE_INTERNET_RESOLVER, | 1213 | if (NULL == exit) |
910 | GNUNET_YES /* no buffer */, | 1214 | { |
911 | GNUNET_NO /* reliable */); | 1215 | exit = GNUNET_new (struct MeshExit); |
912 | 1216 | exit->peer = pid; | |
1217 | /* tunnel is closed, so insert at the end */ | ||
1218 | GNUNET_CONTAINER_DLL_insert_tail (exit_head, | ||
1219 | exit_tail, | ||
1220 | exit); | ||
1221 | } | ||
1222 | exit->expiration = GNUNET_TIME_absolute_max (exit->expiration, | ||
1223 | GNUNET_TIME_absolute_ntoh (ad->expiration_time)); | ||
1224 | if (dns_exit_available < MAX_OPEN_TUNNELS) | ||
1225 | try_open_exit (); | ||
913 | } | 1226 | } |
914 | 1227 | ||
915 | 1228 | ||