made the service more resilient against out of order and simply incorrect messages
[oweals/gnunet.git] / src / pt / gnunet-daemon-pt.c
1 /*
2      This file is part of GNUnet.
3      (C) 2010, 2012 Christian Grothoff
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20
21 /**
22  * @file pt/gnunet-daemon-pt.c
23  * @brief tool to manipulate DNS and VPN services to perform protocol translation (IPvX over GNUnet)
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "gnunet_dns_service.h"
29 #include "gnunet_dnsparser_lib.h"
30 #include "gnunet_mesh_service.h"
31 #include "gnunet_tun_lib.h"
32 #include "gnunet_dht_service.h"
33 #include "gnunet_vpn_service.h"
34 #include "gnunet_statistics_service.h"
35 #include "gnunet_applications.h"
36 #include "block_dns.h"
37
38
39 /**
40  * After how long do we time out if we could not get an IP from VPN or MESH?
41  */
42 #define TIMEOUT GNUNET_TIME_UNIT_MINUTES
43
44 /**
45  * How many bytes of payload do we allow at most for a DNS reply?
46  * Given that this is pretty much limited to loopback, we can be
47  * pretty high (Linux loopback defaults to 16k, most local UDP packets
48  * should survive up to 9k (NFS), so 8k should be pretty safe in
49  * general).
50  */
51 #define MAX_DNS_SIZE (8 * 1024)
52
53 /**
54  * How many tunnels do we open at most at the same time?
55  */
56 #define MAX_OPEN_TUNNELS 4
57
58
59 /**
60  * Which group of DNS records are we currently processing?
61  */
62 enum RequestGroup
63 {
64   /**
65    * DNS answers
66    */
67   ANSWERS = 0,
68
69   /**
70    * DNS authority records
71    */
72   AUTHORITY_RECORDS = 1,
73
74   /**
75    * DNS additional records
76    */
77   ADDITIONAL_RECORDS = 2,
78
79   /**
80    * We're done processing.
81    */
82   END = 3
83 };
84
85
86 /**
87  * Information tracked per DNS reply that we are processing.
88  */
89 struct ReplyContext
90 {
91   /**
92    * Handle to submit the final result.
93    */
94   struct GNUNET_DNS_RequestHandle *rh;
95
96   /**
97    * DNS packet that is being modified.
98    */
99   struct GNUNET_DNSPARSER_Packet *dns;
100
101   /**
102    * Active redirection request with the VPN.
103    */
104   struct GNUNET_VPN_RedirectionRequest *rr;
105
106   /**
107    * Record for which we have an active redirection request.
108    */
109   struct GNUNET_DNSPARSER_Record *rec;
110
111   /**
112    * Offset in the current record group that is being modified.
113    */
114   unsigned int offset;
115
116   /**
117    * Group that is being modified
118    */
119   enum RequestGroup group;
120
121 };
122
123
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 /**
198  * State we keep for a request that is going out via MESH.
199  */
200 struct RequestContext
201 {
202   /**
203    * We keep these in a DLL.
204    */
205   struct RequestContext *next;
206
207   /**
208    * We keep these in a DLL.
209    */
210   struct RequestContext *prev;
211
212   /**
213    * Exit that was chosen for this request.
214    */
215   struct MeshExit *exit;
216
217   /**
218    * Handle for interaction with DNS service.
219    */
220   struct GNUNET_DNS_RequestHandle *rh;
221
222   /**
223    * Message we're sending out via MESH, allocated at the
224    * end of this struct.
225    */
226   const struct GNUNET_MessageHeader *mesh_message;
227
228   /**
229    * Task used to abort this operation with timeout.
230    */
231   GNUNET_SCHEDULER_TaskIdentifier timeout_task;
232
233   /**
234    * Length of the request message that follows this struct.
235    */
236   uint16_t mlen;
237
238   /**
239    * ID of the original DNS request (used to match the reply).
240    */
241   uint16_t dns_id;
242
243   /**
244    * #GNUNET_NO if this request is still in the transmit_queue,
245    * #GNUNET_YES if we are in the receive_queue.
246    */
247   int16_t was_transmitted;
248
249 };
250
251
252 /**
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).
256  */
257 static struct MeshExit *exit_head;
258
259 /**
260  * Tail of DLL of mesh exits.
261  */
262 static struct MeshExit *exit_tail;
263
264 /**
265  * The handle to the configuration used throughout the process
266  */
267 static const struct GNUNET_CONFIGURATION_Handle *cfg;
268
269 /**
270  * The handle to the VPN
271  */
272 static struct GNUNET_VPN_Handle *vpn_handle;
273
274 /**
275  * The handle to the MESH service
276  */
277 static struct GNUNET_MESH_Handle *mesh_handle;
278
279 /**
280  * Statistics.
281  */
282 static struct GNUNET_STATISTICS_Handle *stats;
283
284 /**
285  * The handle to DNS post-resolution modifications.
286  */
287 static struct GNUNET_DNS_Handle *dns_post_handle;
288
289 /**
290  * The handle to DNS pre-resolution modifications.
291  */
292 static struct GNUNET_DNS_Handle *dns_pre_handle;
293
294 /**
295  * Handle to access the DHT.
296  */
297 static struct GNUNET_DHT_Handle *dht;
298
299 /**
300  * Our DHT GET operation to find DNS exits.
301  */
302 static struct GNUNET_DHT_GetHandle *dht_get;
303
304 /**
305  * Are we doing IPv4-pt?
306  */
307 static int ipv4_pt;
308
309 /**
310  * Are we doing IPv6-pt?
311  */
312 static int ipv6_pt;
313
314 /**
315  * Are we tunneling DNS queries?
316  */
317 static int dns_tunnel;
318
319 /**
320  * Number of DNS exit peers we currently have in the mesh tunnel.
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.
323  */
324 static unsigned int dns_exit_available;
325
326
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 /**
465  * We're done modifying all records in the response.  Submit the reply
466  * and free the resources of the rc.
467  *
468  * @param rc context to process
469  */
470 static void
471 finish_request (struct ReplyContext *rc)
472 {
473   char *buf;
474   size_t buf_len;
475
476   if (GNUNET_SYSERR ==
477       GNUNET_DNSPARSER_pack (rc->dns,
478                              MAX_DNS_SIZE,
479                              &buf,
480                              &buf_len))
481   {
482     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
483                 _("Failed to pack DNS request.  Dropping.\n"));
484     GNUNET_DNS_request_drop (rc->rh);
485   }
486   else
487   {
488     GNUNET_STATISTICS_update (stats,
489                               gettext_noop ("# DNS requests mapped to VPN"),
490                               1, GNUNET_NO);
491     GNUNET_DNS_request_answer (rc->rh,
492                                buf_len, buf);
493     GNUNET_free (buf);
494   }
495   GNUNET_DNSPARSER_free_packet (rc->dns);
496   GNUNET_free (rc);
497 }
498
499
500 /**
501  * Process the next record of the given request context.
502  * When done, submit the reply and free the resources of
503  * the rc.
504  *
505  * @param rc context to process
506  */
507 static void
508 submit_request (struct ReplyContext *rc);
509
510
511 /**
512  * Callback invoked from the VPN service once a redirection is
513  * available.  Provides the IP address that can now be used to
514  * reach the requested destination.  We substitute the active
515  * record and then continue with 'submit_request' to look at
516  * the other records.
517  *
518  * @param cls our `struct ReplyContext`
519  * @param af address family, AF_INET or AF_INET6; AF_UNSPEC on error;
520  *                will match 'result_af' from the request
521  * @param address IP address (struct in_addr or struct in_addr6, depending on 'af')
522  *                that the VPN allocated for the redirection;
523  *                traffic to this IP will now be redirected to the
524  *                specified target peer; NULL on error
525  */
526 static void
527 vpn_allocation_callback (void *cls,
528                          int af,
529                          const void *address)
530 {
531   struct ReplyContext *rc = cls;
532
533   rc->rr = NULL;
534   if (af == AF_UNSPEC)
535   {
536     GNUNET_DNS_request_drop (rc->rh);
537     GNUNET_DNSPARSER_free_packet (rc->dns);
538     GNUNET_free (rc);
539     return;
540   }
541   GNUNET_STATISTICS_update (stats,
542                             gettext_noop ("# DNS records modified"),
543                             1, GNUNET_NO);
544   switch (rc->rec->type)
545   {
546   case GNUNET_DNSPARSER_TYPE_A:
547     GNUNET_assert (AF_INET == af);
548     memcpy (rc->rec->data.raw.data, address, sizeof (struct in_addr));
549     break;
550   case GNUNET_DNSPARSER_TYPE_AAAA:
551     GNUNET_assert (AF_INET6 == af);
552     memcpy (rc->rec->data.raw.data, address, sizeof (struct in6_addr));
553     break;
554   default:
555     GNUNET_assert (0);
556     return;
557   }
558   rc->rec = NULL;
559   submit_request (rc);
560 }
561
562
563 /**
564  * Modify the given DNS record by asking VPN to create a tunnel
565  * to the given address.  When done, continue with submitting
566  * other records from the request context ('submit_request' is
567  * our continuation).
568  *
569  * @param rc context to process
570  * @param rec record to modify
571  */
572 static void
573 modify_address (struct ReplyContext *rc,
574                 struct GNUNET_DNSPARSER_Record *rec)
575 {
576   int af;
577
578   switch (rec->type)
579   {
580   case GNUNET_DNSPARSER_TYPE_A:
581     af = AF_INET;
582     GNUNET_assert (rec->data.raw.data_len == sizeof (struct in_addr));
583     break;
584   case GNUNET_DNSPARSER_TYPE_AAAA:
585     af = AF_INET6;
586     GNUNET_assert (rec->data.raw.data_len == sizeof (struct in6_addr));
587     break;
588   default:
589     GNUNET_assert (0);
590     return;
591   }
592   rc->rec = rec;
593   rc->rr = GNUNET_VPN_redirect_to_ip (vpn_handle,
594                                       af, af,
595                                       rec->data.raw.data,
596                                       GNUNET_TIME_relative_to_absolute (TIMEOUT),
597                                       &vpn_allocation_callback,
598                                       rc);
599 }
600
601
602 /**
603  * Process the next record of the given request context.
604  * When done, submit the reply and free the resources of
605  * the rc.
606  *
607  * @param rc context to process
608  */
609 static void
610 submit_request (struct ReplyContext *rc)
611 {
612   struct GNUNET_DNSPARSER_Record *ra;
613   unsigned int ra_len;
614   unsigned int i;
615
616   while (1)
617   {
618     switch (rc->group)
619     {
620     case ANSWERS:
621       ra = rc->dns->answers;
622       ra_len = rc->dns->num_answers;
623       break;
624     case AUTHORITY_RECORDS:
625       ra = rc->dns->authority_records;
626       ra_len = rc->dns->num_authority_records;
627       break;
628     case ADDITIONAL_RECORDS:
629       ra = rc->dns->additional_records;
630       ra_len = rc->dns->num_additional_records;
631       break;
632     case END:
633       finish_request (rc);
634       return;
635     default:
636       GNUNET_assert (0);
637     }
638     for (i=rc->offset;i<ra_len;i++)
639     {
640       switch (ra[i].type)
641       {
642       case GNUNET_DNSPARSER_TYPE_A:
643         if (ipv4_pt)
644         {
645           rc->offset = i + 1;
646           modify_address (rc, &ra[i]);
647           return;
648         }
649         break;
650       case GNUNET_DNSPARSER_TYPE_AAAA:
651         if (ipv6_pt)
652         {
653           rc->offset = i + 1;
654           modify_address (rc, &ra[i]);
655           return;
656         }
657         break;
658       }
659     }
660     rc->group++;
661   }
662 }
663
664
665 /**
666  * Test if any of the given records need protocol-translation work.
667  *
668  * @param ra array of records
669  * @param ra_len number of entries in @a ra
670  * @return #GNUNET_YES if any of the given records require protocol-translation
671  */
672 static int
673 work_test (const struct GNUNET_DNSPARSER_Record *ra,
674            unsigned int ra_len)
675 {
676   unsigned int i;
677
678   for (i=0;i<ra_len;i++)
679   {
680     switch (ra[i].type)
681     {
682     case GNUNET_DNSPARSER_TYPE_A:
683       if (ipv4_pt)
684         return GNUNET_YES;
685       break;
686     case GNUNET_DNSPARSER_TYPE_AAAA:
687       if (ipv6_pt)
688         return GNUNET_YES;
689       break;
690     }
691   }
692   return GNUNET_NO;
693 }
694
695
696 /**
697  * This function is called AFTER we got an IP address for a
698  * DNS request.  Now, the PT daemon has the chance to substitute
699  * the IP address with one from the VPN range to tunnel requests
700  * destined for this IP address via VPN and MESH.
701  *
702  * @param cls closure
703  * @param rh request handle to user for reply
704  * @param request_length number of bytes in request
705  * @param request udp payload of the DNS request
706  */
707 static void
708 dns_post_request_handler (void *cls,
709                           struct GNUNET_DNS_RequestHandle *rh,
710                           size_t request_length,
711                           const char *request)
712 {
713   struct GNUNET_DNSPARSER_Packet *dns;
714   struct ReplyContext *rc;
715   int work;
716
717   GNUNET_STATISTICS_update (stats,
718                             gettext_noop ("# DNS replies intercepted"),
719                             1, GNUNET_NO);
720   dns = GNUNET_DNSPARSER_parse (request, request_length);
721   if (NULL == dns)
722   {
723     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
724                 _("Failed to parse DNS request.  Dropping.\n"));
725     GNUNET_DNS_request_drop (rh);
726     return;
727   }
728   work = GNUNET_NO;
729   work |= work_test (dns->answers, dns->num_answers);
730   work |= work_test (dns->authority_records, dns->num_authority_records);
731   work |= work_test (dns->additional_records, dns->num_additional_records);
732   if (! work)
733   {
734     GNUNET_DNS_request_forward (rh);
735     GNUNET_DNSPARSER_free_packet (dns);
736     return;
737   }
738   rc = GNUNET_new (struct ReplyContext);
739   rc->rh = rh;
740   rc->dns = dns;
741   rc->offset = 0;
742   rc->group = ANSWERS;
743   submit_request (rc);
744 }
745
746
747 /**
748  * Transmit a DNS request via MESH and move the request
749  * handle to the receive queue.
750  *
751  * @param cls the `struct MeshExit`
752  * @param size number of bytes available in buf
753  * @param buf where to copy the message
754  * @return number of bytes written to buf
755  */
756 static size_t
757 transmit_dns_request_to_mesh (void *cls,
758                               size_t size,
759                               void *buf)
760 {
761   struct MeshExit *exit = cls;
762   struct RequestContext *rc;
763   size_t mlen;
764
765   exit->mesh_th = NULL;
766   if (NULL == (rc = exit->transmit_queue_head))
767     return 0;
768   mlen = rc->mlen;
769   if (mlen > size)
770   {
771     exit->mesh_th = GNUNET_MESH_notify_transmit_ready (exit->mesh_tunnel,
772                                                        GNUNET_NO,
773                                                        TIMEOUT,
774                                                        mlen,
775                                                        &transmit_dns_request_to_mesh,
776                                                        exit);
777     return 0;
778   }
779   GNUNET_assert (GNUNET_NO == rc->was_transmitted);
780   memcpy (buf, rc->mesh_message, mlen);
781   GNUNET_CONTAINER_DLL_remove (exit->transmit_queue_head,
782                                exit->transmit_queue_tail,
783                                rc);
784   rc->was_transmitted = GNUNET_YES;
785   GNUNET_CONTAINER_DLL_insert (exit->receive_queue_head,
786                                exit->receive_queue_tail,
787                                rc);
788   rc = exit->transmit_queue_head;
789   if (NULL != rc)
790     exit->mesh_th = GNUNET_MESH_notify_transmit_ready (exit->mesh_tunnel,
791                                                        GNUNET_NO,
792                                                        TIMEOUT,
793                                                        rc->mlen,
794                                                        &transmit_dns_request_to_mesh,
795                                                        exit);
796   return mlen;
797 }
798
799
800 /**
801  * Task run if the time to answer a DNS request via MESH is over.
802  *
803  * @param cls the `struct RequestContext` to abort
804  * @param tc scheduler context
805  */
806 static void
807 timeout_request (void *cls,
808                  const struct GNUNET_SCHEDULER_TaskContext *tc)
809 {
810   struct RequestContext *rc = cls;
811   struct MeshExit *exit = rc->exit;
812
813   if (rc->was_transmitted)
814   {
815     exit->num_transmitted++;
816     GNUNET_CONTAINER_DLL_remove (exit->receive_queue_head,
817                                  exit->receive_queue_tail,
818                                  rc);
819   }
820   else
821   {
822     GNUNET_CONTAINER_DLL_remove (exit->transmit_queue_head,
823                                  exit->transmit_queue_tail,
824                                  rc);
825   }
826   GNUNET_STATISTICS_update (stats,
827                             gettext_noop ("# DNS requests dropped (timeout)"),
828                             1, GNUNET_NO);
829   GNUNET_DNS_request_drop (rc->rh);
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   }
857 }
858
859
860 /**
861  * This function is called *before* the DNS request has been
862  * given to a "local" DNS resolver.  Tunneling for DNS requests
863  * was enabled, so we now need to send the request via some MESH
864  * tunnel to a DNS EXIT for resolution.
865  *
866  * @param cls closure
867  * @param rh request handle to user for reply
868  * @param request_length number of bytes in request
869  * @param request udp payload of the DNS request
870  */
871 static void
872 dns_pre_request_handler (void *cls,
873                          struct GNUNET_DNS_RequestHandle *rh,
874                          size_t request_length,
875                          const char *request)
876 {
877   struct RequestContext *rc;
878   size_t mlen;
879   struct GNUNET_MessageHeader hdr;
880   struct GNUNET_TUN_DnsHeader dns;
881   struct MeshExit *exit;
882
883   GNUNET_STATISTICS_update (stats,
884                             gettext_noop ("# DNS requests intercepted"),
885                             1, GNUNET_NO);
886   if (0 == dns_exit_available)
887   {
888     GNUNET_STATISTICS_update (stats,
889                               gettext_noop ("# DNS requests dropped (DNS mesh tunnel down)"),
890                               1, GNUNET_NO);
891     GNUNET_DNS_request_drop (rh);
892     return;
893   }
894   if (request_length < sizeof (dns))
895   {
896     GNUNET_STATISTICS_update (stats,
897                               gettext_noop ("# DNS requests dropped (malformed)"),
898                               1, GNUNET_NO);
899     GNUNET_DNS_request_drop (rh);
900     return;
901   }
902   memcpy (&dns, request, sizeof (dns));
903   mlen = sizeof (struct GNUNET_MessageHeader) + request_length;
904   exit = choose_exit ();
905   GNUNET_assert (NULL != exit);
906   GNUNET_assert (NULL != exit->mesh_tunnel);
907   rc = GNUNET_malloc (sizeof (struct RequestContext) + mlen);
908   rc->exit = exit;
909   rc->rh = rh;
910   rc->mesh_message = (const struct GNUNET_MessageHeader*) &rc[1];
911   rc->timeout_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT,
912                                                    &timeout_request,
913                                                    rc);
914   rc->dns_id = dns.id;
915   rc->mlen = mlen;
916   hdr.type = htons (GNUNET_MESSAGE_TYPE_VPN_DNS_TO_INTERNET);
917   hdr.size = htons (mlen);
918   memcpy (&rc[1], &hdr, sizeof (struct GNUNET_MessageHeader));
919   memcpy (&(((char*)&rc[1])[sizeof (struct GNUNET_MessageHeader)]),
920           request,
921           request_length);
922   GNUNET_CONTAINER_DLL_insert_tail (exit->transmit_queue_head,
923                                     exit->transmit_queue_tail,
924                                     rc);
925   if (NULL == exit->mesh_th)
926     exit->mesh_th = GNUNET_MESH_notify_transmit_ready (exit->mesh_tunnel,
927                                                        GNUNET_NO,
928                                                        TIMEOUT,
929                                                        mlen,
930                                                        &transmit_dns_request_to_mesh,
931                                                        exit);
932 }
933
934
935 /**
936  * Process a request via mesh to perform a DNS query.
937  *
938  * @param cls NULL
939  * @param tunnel connection to the other end
940  * @param tunnel_ctx pointer to our `struct MeshExit`
941  * @param message the actual message
942  * @return #GNUNET_OK to keep the connection open,
943  *         #GNUNET_SYSERR to close it (signal serious error)
944  */
945 static int
946 receive_dns_response (void *cls,
947                       struct GNUNET_MESH_Tunnel *tunnel,
948                       void **tunnel_ctx,
949                       const struct GNUNET_MessageHeader *message)
950 {
951   struct MeshExit *exit = *tunnel_ctx;
952   struct GNUNET_TUN_DnsHeader dns;
953   size_t mlen;
954   struct RequestContext *rc;
955
956   mlen = ntohs (message->size);
957   mlen -= sizeof (struct GNUNET_MessageHeader);
958   if (mlen < sizeof (struct GNUNET_TUN_DnsHeader))
959   {
960     GNUNET_break_op (0);
961     return GNUNET_SYSERR;
962   }
963   memcpy (&dns, &message[1], sizeof (dns));
964   for (rc = exit->receive_queue_head; NULL != rc; rc = rc->next)
965   {
966     GNUNET_assert (GNUNET_YES == rc->was_transmitted);
967     if (dns.id == rc->dns_id)
968     {
969       GNUNET_STATISTICS_update (stats,
970                                 gettext_noop ("# DNS replies received"),
971                                 1, GNUNET_NO);
972       GNUNET_DNS_request_answer (rc->rh,
973                                  mlen,
974                                  (const void*) &message[1]);
975       GNUNET_CONTAINER_DLL_remove (exit->receive_queue_head,
976                                    exit->receive_queue_tail,
977                                    rc);
978       GNUNET_SCHEDULER_cancel (rc->timeout_task);
979       GNUNET_free (rc);
980       exit->num_answered++;
981       exit->num_transmitted++;
982       return GNUNET_OK;
983     }
984   }
985   GNUNET_STATISTICS_update (stats,
986                             gettext_noop ("# DNS replies dropped (too late?)"),
987                             1, GNUNET_NO);
988   return GNUNET_OK;
989 }
990
991
992 /**
993  * Abort all pending DNS requests with the given mesh exit.
994  *
995  * @param exit mesh exit to abort requests for
996  */
997 static void
998 abort_all_requests (struct MeshExit *exit)
999 {
1000   struct RequestContext *rc;
1001
1002   while (NULL != (rc = exit->receive_queue_head))
1003   {
1004     GNUNET_CONTAINER_DLL_remove (exit->receive_queue_head,
1005                                  exit->receive_queue_tail,
1006                                  rc);
1007     GNUNET_DNS_request_drop (rc->rh);
1008     GNUNET_SCHEDULER_cancel (rc->timeout_task);
1009     GNUNET_free (rc);
1010   }
1011   while (NULL != (rc = exit->transmit_queue_head))
1012   {
1013     GNUNET_CONTAINER_DLL_remove (exit->transmit_queue_head,
1014                                  exit->transmit_queue_tail,
1015                                  rc);
1016     GNUNET_DNS_request_drop (rc->rh);
1017     GNUNET_SCHEDULER_cancel (rc->timeout_task);
1018     GNUNET_free (rc);
1019   }
1020 }
1021
1022
1023 /**
1024  * Function scheduled as very last function, cleans up after us
1025  *
1026  * @param cls closure, NULL
1027  * @param tskctx scheduler context, unused
1028  */
1029 static void
1030 cleanup (void *cls,
1031          const struct GNUNET_SCHEDULER_TaskContext *tskctx)
1032 {
1033   struct MeshExit *exit;
1034
1035   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1036               "Protocol translation daemon is shutting down now\n");
1037   if (NULL != vpn_handle)
1038   {
1039     GNUNET_VPN_disconnect (vpn_handle);
1040     vpn_handle = NULL;
1041   }
1042   while (NULL != (exit = exit_head))
1043   {
1044     GNUNET_CONTAINER_DLL_remove (exit_head,
1045                                  exit_tail,
1046                                  exit);
1047     if (NULL != exit->mesh_th)
1048     {
1049       GNUNET_MESH_notify_transmit_ready_cancel (exit->mesh_th);
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);
1059   }
1060   if (NULL != mesh_handle)
1061   {
1062     GNUNET_MESH_disconnect (mesh_handle);
1063     mesh_handle = NULL;
1064   }
1065   if (NULL != dns_post_handle)
1066   {
1067     GNUNET_DNS_disconnect (dns_post_handle);
1068     dns_post_handle = NULL;
1069   }
1070   if (NULL != dns_pre_handle)
1071   {
1072     GNUNET_DNS_disconnect (dns_pre_handle);
1073     dns_pre_handle = NULL;
1074   }
1075   if (NULL != stats)
1076   {
1077     GNUNET_STATISTICS_destroy (stats, GNUNET_YES);
1078     stats = NULL;
1079   }
1080   if (NULL != dht_get)
1081   {
1082     GNUNET_DHT_get_stop (dht_get);
1083     dht_get = NULL;
1084   }
1085   if (NULL != dht)
1086   {
1087     GNUNET_DHT_disconnect (dht);
1088     dht = NULL;
1089   }
1090 }
1091
1092
1093 /**
1094  * Function called whenever a tunnel is destroyed.  Should clean up
1095  * the associated state and attempt to build a new one.
1096  *
1097  * It must NOT call #GNUNET_MESH_tunnel_destroy on the tunnel.
1098  *
1099  * @param cls closure (the `struct MeshExit` set from #GNUNET_MESH_connect)
1100  * @param tunnel connection to the other end (henceforth invalid)
1101  * @param tunnel_ctx place where local state associated
1102  *                   with the tunnel is stored
1103  */
1104 static void
1105 mesh_tunnel_end_cb (void *cls,
1106                     const struct GNUNET_MESH_Tunnel *tunnel,
1107                     void *tunnel_ctx)
1108 {
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);
1162 }
1163
1164
1165 /**
1166  * Function called whenever we find an advertisement for a
1167  * DNS exit in the DHT.  If we don't have a mesh tunnel,
1168  * we should build one; otherwise, we should save the
1169  * advertisement for later use.
1170  *
1171  * @param cls closure
1172  * @param exp when will this value expire
1173  * @param key key of the result
1174  * @param get_path peers on reply path (or NULL if not recorded)
1175  *                 [0] = datastore's first neighbor, [length - 1] = local peer
1176  * @param get_path_length number of entries in @a get_path
1177  * @param put_path peers on the PUT path (or NULL if not recorded)
1178  *                 [0] = origin, [length - 1] = datastore
1179  * @param put_path_length number of entries in @a put_path
1180  * @param type type of the result
1181  * @param size number of bytes in @a data
1182  * @param data pointer to the result data
1183  */
1184 static void
1185 handle_dht_result (void *cls,
1186                    struct GNUNET_TIME_Absolute exp,
1187                    const struct GNUNET_HashCode *key,
1188                    const struct GNUNET_PeerIdentity *get_path,
1189                    unsigned int get_path_length,
1190                    const struct GNUNET_PeerIdentity *put_path,
1191                    unsigned int put_path_length,
1192                    enum GNUNET_BLOCK_Type type,
1193                    size_t size, const void *data)
1194 {
1195   const struct GNUNET_DNS_Advertisement *ad;
1196   struct MeshExit *exit;
1197
1198   if (sizeof (struct GNUNET_DNS_Advertisement) != size)
1199   {
1200     GNUNET_break (0);
1201     return;
1202   }
1203   ad = data;
1204   for (exit = exit_head; NULL != exit; exit = exit->next)
1205     if (0 == memcmp (&ad->peer,
1206                      &exit->peer,
1207                      sizeof (struct GNUNET_PeerIdentity)))
1208       break;
1209   if (NULL == exit)
1210   {
1211     exit = GNUNET_new (struct MeshExit);
1212     exit->peer = ad->peer;
1213     /* tunnel is closed, so insert at the end */
1214     GNUNET_CONTAINER_DLL_insert_tail (exit_head,
1215                                       exit_tail,
1216                                       exit);
1217   }
1218   exit->expiration = GNUNET_TIME_absolute_max (exit->expiration,
1219                                                GNUNET_TIME_absolute_ntoh (ad->expiration_time));
1220   if (dns_exit_available < MAX_OPEN_TUNNELS)
1221     try_open_exit ();
1222 }
1223
1224
1225 /**
1226  * @brief Main function that will be run by the scheduler.
1227  *
1228  * @param cls closure
1229  * @param args remaining command-line arguments
1230  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1231  * @param cfg_ configuration
1232  */
1233 static void
1234 run (void *cls, char *const *args GNUNET_UNUSED,
1235      const char *cfgfile GNUNET_UNUSED,
1236      const struct GNUNET_CONFIGURATION_Handle *cfg_)
1237 {
1238   struct GNUNET_HashCode dns_key;
1239
1240   cfg = cfg_;
1241   stats = GNUNET_STATISTICS_create ("pt", cfg);
1242   ipv4_pt = GNUNET_CONFIGURATION_get_value_yesno (cfg, "pt", "TUNNEL_IPV4");
1243   ipv6_pt = GNUNET_CONFIGURATION_get_value_yesno (cfg, "pt", "TUNNEL_IPV6");
1244   dns_tunnel = GNUNET_CONFIGURATION_get_value_yesno (cfg, "pt", "TUNNEL_DNS");
1245   if (! (ipv4_pt || ipv6_pt || dns_tunnel))
1246   {
1247     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1248                 _("No useful service enabled.  Exiting.\n"));
1249     GNUNET_SCHEDULER_shutdown ();
1250     return;
1251   }
1252   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &cleanup, cls);
1253   if (ipv4_pt || ipv6_pt)
1254   {
1255     dns_post_handle
1256       = GNUNET_DNS_connect (cfg,
1257                             GNUNET_DNS_FLAG_POST_RESOLUTION,
1258                             &dns_post_request_handler, NULL);
1259     if (NULL == dns_post_handle)
1260     {
1261       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1262                   _("Failed to connect to %s service.  Exiting.\n"),
1263                   "DNS");
1264       GNUNET_SCHEDULER_shutdown ();
1265       return;
1266     }
1267     vpn_handle = GNUNET_VPN_connect (cfg);
1268     if (NULL == vpn_handle)
1269     {
1270       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1271                   _("Failed to connect to %s service.  Exiting.\n"),
1272                   "VPN");
1273       GNUNET_SCHEDULER_shutdown ();
1274       return;
1275     }
1276   }
1277   if (dns_tunnel)
1278   {
1279     static struct GNUNET_MESH_MessageHandler mesh_handlers[] = {
1280       {&receive_dns_response, GNUNET_MESSAGE_TYPE_VPN_DNS_FROM_INTERNET, 0},
1281       {NULL, 0, 0}
1282     };
1283
1284     dns_pre_handle
1285       = GNUNET_DNS_connect (cfg,
1286                             GNUNET_DNS_FLAG_PRE_RESOLUTION,
1287                             &dns_pre_request_handler, NULL);
1288     if (NULL == dns_pre_handle)
1289     {
1290       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1291                   _("Failed to connect to %s service.  Exiting.\n"),
1292                   "DNS");
1293       GNUNET_SCHEDULER_shutdown ();
1294       return;
1295     }
1296     mesh_handle = GNUNET_MESH_connect (cfg, NULL, NULL,
1297                                        &mesh_tunnel_end_cb,
1298                                        mesh_handlers, NULL);
1299     if (NULL == mesh_handle)
1300     {
1301       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1302                   _("Failed to connect to %s service.  Exiting.\n"),
1303                   "MESH");
1304       GNUNET_SCHEDULER_shutdown ();
1305       return;
1306     }
1307     dht = GNUNET_DHT_connect (cfg, 1);
1308     if (NULL == dht)
1309     {
1310       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1311                   _("Failed to connect to %s service.  Exiting.\n"),
1312                   "DHT");
1313       GNUNET_SCHEDULER_shutdown ();
1314       return;
1315     }
1316     GNUNET_CRYPTO_hash ("dns", strlen ("dns"), &dns_key);
1317     dht_get = GNUNET_DHT_get_start (dht,
1318                                     GNUNET_BLOCK_TYPE_DNS,
1319                                     &dns_key,
1320                                     1,
1321                                     GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE,
1322                                     NULL, 0,
1323                                     &handle_dht_result, NULL);
1324   }
1325 }
1326
1327
1328 /**
1329  * The main function
1330  *
1331  * @param argc number of arguments from the command line
1332  * @param argv command line arguments
1333  * @return 0 ok, 1 on error
1334  */
1335 int
1336 main (int argc, char *const *argv)
1337 {
1338   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
1339     GNUNET_GETOPT_OPTION_END
1340   };
1341   int ret;
1342
1343   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
1344     return 2;
1345   ret = (GNUNET_OK ==
1346          GNUNET_PROGRAM_run (argc, argv, "gnunet-daemon-pt",
1347                              gettext_noop
1348                              ("Daemon to run to perform IP protocol translation to GNUnet"),
1349                              options, &run, NULL)) ? 0 : 1;
1350   GNUNET_free ((void*) argv);
1351   return ret;
1352 }
1353
1354
1355 /* end of gnunet-daemon-pt.c */