aboutsummaryrefslogtreecommitdiff
path: root/src/pt
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2013-09-22 19:18:15 +0000
committerChristian Grothoff <christian@grothoff.org>2013-09-22 19:18:15 +0000
commit3b1601c14f00f204e26706799ff85ed8bc132569 (patch)
tree97b20ba1d1fc1c8ba208ea91b3e07e5509c42a1d /src/pt
parentdc07a7575ed89a6d3ab63912a76e8bffed767128 (diff)
downloadgnunet-3b1601c14f00f204e26706799ff85ed8bc132569.tar.gz
gnunet-3b1601c14f00f204e26706799ff85ed8bc132569.zip
#2915: supporting multiple DNS exits and ranking among them
Diffstat (limited to 'src/pt')
-rw-r--r--src/pt/gnunet-daemon-pt.c535
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 */
129struct 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 */
122struct RequestContext 200struct 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 */
167static const struct GNUNET_CONFIGURATION_Handle *cfg; 257static struct MeshExit *exit_head;
168 258
169/** 259/**
170 * The handle to the VPN 260 * Tail of DLL of mesh exits.
171 */ 261 */
172static struct GNUNET_VPN_Handle *vpn_handle; 262static 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 */
177static 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 */
184static struct GNUNET_MESH_Tunnel *mesh_tunnel;
185
186/**
187 * Active transmission request with MESH (or NULL).
188 */
189static struct GNUNET_MESH_TransmitHandle *mesh_th;
190
191/**
192 * Head of DLL of requests to be transmitted to mesh_tunnel.
193 */
194static struct RequestContext *transmit_queue_head;
195
196/**
197 * Tail of DLL of requests to be transmitted to mesh_tunnel.
198 */ 266 */
199static struct RequestContext *transmit_queue_tail; 267static 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 */
204static struct RequestContext *receive_queue_head; 272static 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 */
209static struct RequestContext *receive_queue_tail; 277static 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 */
255static unsigned int dns_exit_available; 324static unsigned int dns_exit_available;
256 325
257 326
258/** 327/**
328 * We are short on mesh exits, try to open another one.
329 */
330static void
331try_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 */
385static uint32_t
386get_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 */
420static struct MeshExit *
421choose_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 */
599static void 806static 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 */
701static int 945static int
702receive_dns_response (void *cls GNUNET_UNUSED, struct GNUNET_MESH_Tunnel *tunnel, 946receive_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 */
748static void 997static void
749abort_all_requests () 998abort_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
787cleanup (void *cls, 1030cleanup (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