- minor renamings
[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 channels 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 channels 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    * Channel we use for DNS requests over MESH, NULL if we did
144    * not initialze a channel to this peer yet.
145    */
146   struct GNUNET_MESH_Channel *mesh_channel;
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_channel.
165    */
166   struct RequestContext *transmit_queue_head;
167
168   /**
169    * Tail of DLL of requests to be transmitted to a mesh_channel.
170    */
171   struct RequestContext *transmit_queue_tail;
172
173   /**
174    * Active transmission request for this channel (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 channel?
185    */
186   unsigned int num_transmitted;
187
188   /**
189    * How many DNS requests were answered via this channel?
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 channel 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 channeling DNS queries?
316  */
317 static int dns_channel;
318
319 /**
320  * Number of DNS exit peers we currently have in the mesh channel.
321  * Used to see if using the mesh channel makes any sense right now,
322  * as well as to decide if we should open new channels.
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_channel)
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_channel)
346     {
347       candidate_count++;
348       if (candidate_selected < candidate_count)
349       {
350         /* move to the head of the DLL */
351         pos->mesh_channel = GNUNET_MESH_channel_create (mesh_handle,
352                                                       pos,
353                                                       &pos->peer,
354                                                       GNUNET_APPLICATION_TYPE_INTERNET_RESOLVER,
355                                                       GNUNET_MESH_OPTION_DEFAULT);
356         if (NULL == pos->mesh_channel)
357         {
358           GNUNET_break (0);
359           continue;
360         }
361         GNUNET_CONTAINER_DLL_remove (exit_head,
362                                      exit_tail,
363                                      pos);
364         GNUNET_CONTAINER_DLL_insert (exit_head,
365                                      exit_tail,
366                                      pos);
367         dns_exit_available++;
368         return;
369       }
370     }
371   GNUNET_assert (NULL == exit_head);
372 }
373
374
375 /**
376  * Compute the weight of the given exit.  The higher the weight,
377  * the more likely it will be that the channel will be chosen.
378  * A weigt of zero means that we should close the channel as it
379  * is so bad, that we should not use it.
380  *
381  * @param exit exit to calculate the weight for
382  * @return weight of the channel
383  */
384 static uint32_t
385 get_channel_weight (struct MeshExit *exit)
386 {
387   uint32_t dropped;
388   uint32_t drop_percent;
389   uint32_t good_percent;
390
391   GNUNET_assert (exit->num_transmitted >= exit->num_answered);
392   dropped = exit->num_transmitted - exit->num_answered;
393   if (exit->num_transmitted > 0)
394     drop_percent = (uint32_t) ((100LL * dropped) / exit->num_transmitted);
395   else
396     drop_percent = 50; /* no data */
397   if ( (exit->num_transmitted > 20) &&
398        (drop_percent > 25) )
399     return 0; /* statistically significant, and > 25% loss, die */
400   good_percent = 100 - drop_percent;
401   GNUNET_assert (0 != good_percent);
402   if ( UINT32_MAX / good_percent / good_percent < exit->num_transmitted)
403     return UINT32_MAX; /* formula below would overflow */
404   return 1 + good_percent * good_percent * exit->num_transmitted;
405 }
406
407
408 /**
409  * Choose a mesh exit for a DNS request.  We try to use a channel
410  * that is reliable and currently available.  All existing
411  * channels are given a base weight of 1, plus a score relating
412  * to the total number of queries answered in relation to the
413  * total number of queries we sent to that channel.  That
414  * score is doubled if the channel is currently idle.
415  *
416  * @return NULL if no exit is known, otherwise the
417  *         exit that we should use to queue a message with
418  */
419 static struct MeshExit *
420 choose_exit ()
421 {
422   struct MeshExit *pos;
423   uint64_t total_transmitted;
424   uint64_t selected_offset;
425   uint32_t channel_weight;
426
427   total_transmitted = 0;
428   for (pos = exit_head; NULL != pos; pos = pos->next)
429   {
430     if (NULL == pos->mesh_channel)
431       break;
432     channel_weight = get_channel_weight (pos);
433     total_transmitted += channel_weight;
434     /* double weight for idle channels */
435     if (NULL == pos->mesh_th)
436       total_transmitted += channel_weight;
437   }
438   if (0 == total_transmitted)
439   {
440     /* no channels available, or only a very bad one... */
441     return exit_head;
442   }
443   selected_offset = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
444                                               total_transmitted);
445   total_transmitted = 0;
446   for (pos = exit_head; NULL != pos; pos = pos->next)
447   {
448     if (NULL == pos->mesh_channel)
449       break;
450     channel_weight = get_channel_weight (pos);
451     total_transmitted += channel_weight;
452     /* double weight for idle channels */
453     if (NULL == pos->mesh_th)
454       total_transmitted += channel_weight;
455     if (total_transmitted > selected_offset)
456       return pos;
457   }
458   GNUNET_break (0);
459   return NULL;
460 }
461
462
463 /**
464  * We're done modifying all records in the response.  Submit the reply
465  * and free the resources of the rc.
466  *
467  * @param rc context to process
468  */
469 static void
470 finish_request (struct ReplyContext *rc)
471 {
472   char *buf;
473   size_t buf_len;
474
475   if (GNUNET_SYSERR ==
476       GNUNET_DNSPARSER_pack (rc->dns,
477                              MAX_DNS_SIZE,
478                              &buf,
479                              &buf_len))
480   {
481     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
482                 _("Failed to pack DNS request.  Dropping.\n"));
483     GNUNET_DNS_request_drop (rc->rh);
484   }
485   else
486   {
487     GNUNET_STATISTICS_update (stats,
488                               gettext_noop ("# DNS requests mapped to VPN"),
489                               1, GNUNET_NO);
490     GNUNET_DNS_request_answer (rc->rh,
491                                buf_len, buf);
492     GNUNET_free (buf);
493   }
494   GNUNET_DNSPARSER_free_packet (rc->dns);
495   GNUNET_free (rc);
496 }
497
498
499 /**
500  * Process the next record of the given request context.
501  * When done, submit the reply and free the resources of
502  * the rc.
503  *
504  * @param rc context to process
505  */
506 static void
507 submit_request (struct ReplyContext *rc);
508
509
510 /**
511  * Callback invoked from the VPN service once a redirection is
512  * available.  Provides the IP address that can now be used to
513  * reach the requested destination.  We substitute the active
514  * record and then continue with 'submit_request' to look at
515  * the other records.
516  *
517  * @param cls our `struct ReplyContext`
518  * @param af address family, AF_INET or AF_INET6; AF_UNSPEC on error;
519  *                will match 'result_af' from the request
520  * @param address IP address (struct in_addr or struct in_addr6, depending on 'af')
521  *                that the VPN allocated for the redirection;
522  *                traffic to this IP will now be redirected to the
523  *                specified target peer; NULL on error
524  */
525 static void
526 vpn_allocation_callback (void *cls,
527                          int af,
528                          const void *address)
529 {
530   struct ReplyContext *rc = cls;
531
532   rc->rr = NULL;
533   if (af == AF_UNSPEC)
534   {
535     GNUNET_DNS_request_drop (rc->rh);
536     GNUNET_DNSPARSER_free_packet (rc->dns);
537     GNUNET_free (rc);
538     return;
539   }
540   GNUNET_STATISTICS_update (stats,
541                             gettext_noop ("# DNS records modified"),
542                             1, GNUNET_NO);
543   switch (rc->rec->type)
544   {
545   case GNUNET_DNSPARSER_TYPE_A:
546     GNUNET_assert (AF_INET == af);
547     memcpy (rc->rec->data.raw.data, address, sizeof (struct in_addr));
548     break;
549   case GNUNET_DNSPARSER_TYPE_AAAA:
550     GNUNET_assert (AF_INET6 == af);
551     memcpy (rc->rec->data.raw.data, address, sizeof (struct in6_addr));
552     break;
553   default:
554     GNUNET_assert (0);
555     return;
556   }
557   rc->rec = NULL;
558   submit_request (rc);
559 }
560
561
562 /**
563  * Modify the given DNS record by asking VPN to create a channel
564  * to the given address.  When done, continue with submitting
565  * other records from the request context ('submit_request' is
566  * our continuation).
567  *
568  * @param rc context to process
569  * @param rec record to modify
570  */
571 static void
572 modify_address (struct ReplyContext *rc,
573                 struct GNUNET_DNSPARSER_Record *rec)
574 {
575   int af;
576
577   switch (rec->type)
578   {
579   case GNUNET_DNSPARSER_TYPE_A:
580     af = AF_INET;
581     GNUNET_assert (rec->data.raw.data_len == sizeof (struct in_addr));
582     break;
583   case GNUNET_DNSPARSER_TYPE_AAAA:
584     af = AF_INET6;
585     GNUNET_assert (rec->data.raw.data_len == sizeof (struct in6_addr));
586     break;
587   default:
588     GNUNET_assert (0);
589     return;
590   }
591   rc->rec = rec;
592   rc->rr = GNUNET_VPN_redirect_to_ip (vpn_handle,
593                                       af, af,
594                                       rec->data.raw.data,
595                                       GNUNET_TIME_relative_to_absolute (TIMEOUT),
596                                       &vpn_allocation_callback,
597                                       rc);
598 }
599
600
601 /**
602  * Process the next record of the given request context.
603  * When done, submit the reply and free the resources of
604  * the rc.
605  *
606  * @param rc context to process
607  */
608 static void
609 submit_request (struct ReplyContext *rc)
610 {
611   struct GNUNET_DNSPARSER_Record *ra;
612   unsigned int ra_len;
613   unsigned int i;
614
615   while (1)
616   {
617     switch (rc->group)
618     {
619     case ANSWERS:
620       ra = rc->dns->answers;
621       ra_len = rc->dns->num_answers;
622       break;
623     case AUTHORITY_RECORDS:
624       ra = rc->dns->authority_records;
625       ra_len = rc->dns->num_authority_records;
626       break;
627     case ADDITIONAL_RECORDS:
628       ra = rc->dns->additional_records;
629       ra_len = rc->dns->num_additional_records;
630       break;
631     case END:
632       finish_request (rc);
633       return;
634     default:
635       GNUNET_assert (0);
636     }
637     for (i=rc->offset;i<ra_len;i++)
638     {
639       switch (ra[i].type)
640       {
641       case GNUNET_DNSPARSER_TYPE_A:
642         if (ipv4_pt)
643         {
644           rc->offset = i + 1;
645           modify_address (rc, &ra[i]);
646           return;
647         }
648         break;
649       case GNUNET_DNSPARSER_TYPE_AAAA:
650         if (ipv6_pt)
651         {
652           rc->offset = i + 1;
653           modify_address (rc, &ra[i]);
654           return;
655         }
656         break;
657       }
658     }
659     rc->group++;
660   }
661 }
662
663
664 /**
665  * Test if any of the given records need protocol-translation work.
666  *
667  * @param ra array of records
668  * @param ra_len number of entries in @a ra
669  * @return #GNUNET_YES if any of the given records require protocol-translation
670  */
671 static int
672 work_test (const struct GNUNET_DNSPARSER_Record *ra,
673            unsigned int ra_len)
674 {
675   unsigned int i;
676
677   for (i=0;i<ra_len;i++)
678   {
679     switch (ra[i].type)
680     {
681     case GNUNET_DNSPARSER_TYPE_A:
682       if (ipv4_pt)
683         return GNUNET_YES;
684       break;
685     case GNUNET_DNSPARSER_TYPE_AAAA:
686       if (ipv6_pt)
687         return GNUNET_YES;
688       break;
689     }
690   }
691   return GNUNET_NO;
692 }
693
694
695 /**
696  * This function is called AFTER we got an IP address for a
697  * DNS request.  Now, the PT daemon has the chance to substitute
698  * the IP address with one from the VPN range to channel requests
699  * destined for this IP address via VPN and MESH.
700  *
701  * @param cls closure
702  * @param rh request handle to user for reply
703  * @param request_length number of bytes in request
704  * @param request udp payload of the DNS request
705  */
706 static void
707 dns_post_request_handler (void *cls,
708                           struct GNUNET_DNS_RequestHandle *rh,
709                           size_t request_length,
710                           const char *request)
711 {
712   struct GNUNET_DNSPARSER_Packet *dns;
713   struct ReplyContext *rc;
714   int work;
715
716   GNUNET_STATISTICS_update (stats,
717                             gettext_noop ("# DNS replies intercepted"),
718                             1, GNUNET_NO);
719   dns = GNUNET_DNSPARSER_parse (request, request_length);
720   if (NULL == dns)
721   {
722     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
723                 _("Failed to parse DNS request.  Dropping.\n"));
724     GNUNET_DNS_request_drop (rh);
725     return;
726   }
727   work = GNUNET_NO;
728   work |= work_test (dns->answers, dns->num_answers);
729   work |= work_test (dns->authority_records, dns->num_authority_records);
730   work |= work_test (dns->additional_records, dns->num_additional_records);
731   if (! work)
732   {
733     GNUNET_DNS_request_forward (rh);
734     GNUNET_DNSPARSER_free_packet (dns);
735     return;
736   }
737   rc = GNUNET_new (struct ReplyContext);
738   rc->rh = rh;
739   rc->dns = dns;
740   rc->offset = 0;
741   rc->group = ANSWERS;
742   submit_request (rc);
743 }
744
745
746 /**
747  * Transmit a DNS request via MESH and move the request
748  * handle to the receive queue.
749  *
750  * @param cls the `struct MeshExit`
751  * @param size number of bytes available in buf
752  * @param buf where to copy the message
753  * @return number of bytes written to buf
754  */
755 static size_t
756 transmit_dns_request_to_mesh (void *cls,
757                               size_t size,
758                               void *buf)
759 {
760   struct MeshExit *exit = cls;
761   struct RequestContext *rc;
762   size_t mlen;
763
764   exit->mesh_th = NULL;
765   if (NULL == (rc = exit->transmit_queue_head))
766     return 0;
767   mlen = rc->mlen;
768   if (mlen > size)
769   {
770     exit->mesh_th = GNUNET_MESH_notify_transmit_ready (exit->mesh_channel,
771                                                        GNUNET_NO,
772                                                        TIMEOUT,
773                                                        mlen,
774                                                        &transmit_dns_request_to_mesh,
775                                                        exit);
776     return 0;
777   }
778   GNUNET_assert (GNUNET_NO == rc->was_transmitted);
779   memcpy (buf, rc->mesh_message, mlen);
780   GNUNET_CONTAINER_DLL_remove (exit->transmit_queue_head,
781                                exit->transmit_queue_tail,
782                                rc);
783   rc->was_transmitted = GNUNET_YES;
784   GNUNET_CONTAINER_DLL_insert (exit->receive_queue_head,
785                                exit->receive_queue_tail,
786                                rc);
787   rc = exit->transmit_queue_head;
788   if (NULL != rc)
789     exit->mesh_th = GNUNET_MESH_notify_transmit_ready (exit->mesh_channel,
790                                                        GNUNET_NO,
791                                                        TIMEOUT,
792                                                        rc->mlen,
793                                                        &transmit_dns_request_to_mesh,
794                                                        exit);
795   return mlen;
796 }
797
798
799 /**
800  * Task run if the time to answer a DNS request via MESH is over.
801  *
802  * @param cls the `struct RequestContext` to abort
803  * @param tc scheduler context
804  */
805 static void
806 timeout_request (void *cls,
807                  const struct GNUNET_SCHEDULER_TaskContext *tc)
808 {
809   struct RequestContext *rc = cls;
810   struct MeshExit *exit = rc->exit;
811
812   if (rc->was_transmitted)
813   {
814     exit->num_transmitted++;
815     GNUNET_CONTAINER_DLL_remove (exit->receive_queue_head,
816                                  exit->receive_queue_tail,
817                                  rc);
818   }
819   else
820   {
821     GNUNET_CONTAINER_DLL_remove (exit->transmit_queue_head,
822                                  exit->transmit_queue_tail,
823                                  rc);
824   }
825   GNUNET_STATISTICS_update (stats,
826                             gettext_noop ("# DNS requests dropped (timeout)"),
827                             1, GNUNET_NO);
828   GNUNET_DNS_request_drop (rc->rh);
829   GNUNET_free (rc);
830   if ( (0 == get_channel_weight (exit)) &&
831        (NULL == exit->receive_queue_head) &&
832        (NULL == exit->transmit_queue_head) )
833   {
834     /* this straw broke the camel's back: this channel now has
835        such a low score that it will not be used; close it! */
836     GNUNET_assert (NULL == exit->mesh_th);
837     GNUNET_MESH_channel_destroy (exit->mesh_channel);
838     exit->mesh_channel = NULL;
839     GNUNET_CONTAINER_DLL_remove (exit_head,
840                                  exit_tail,
841                                  exit);
842     GNUNET_CONTAINER_DLL_insert_tail (exit_head,
843                                       exit_tail,
844                                       exit);
845     /* go back to semi-innocent: mark as not great, but
846        avoid a prohibitively negative score (see
847        #get_channel_weight, which checks for a certain
848        minimum number of transmissions before making
849        up an opinion) */
850     exit->num_transmitted = 5;
851     exit->num_answered = 0;
852     dns_exit_available--;
853     /* now try to open an alternative exit */
854     try_open_exit ();
855   }
856 }
857
858
859 /**
860  * This function is called *before* the DNS request has been
861  * given to a "local" DNS resolver.  Channeling for DNS requests
862  * was enabled, so we now need to send the request via some MESH
863  * channel to a DNS EXIT for resolution.
864  *
865  * @param cls closure
866  * @param rh request handle to user for reply
867  * @param request_length number of bytes in request
868  * @param request udp payload of the DNS request
869  */
870 static void
871 dns_pre_request_handler (void *cls,
872                          struct GNUNET_DNS_RequestHandle *rh,
873                          size_t request_length,
874                          const char *request)
875 {
876   struct RequestContext *rc;
877   size_t mlen;
878   struct GNUNET_MessageHeader hdr;
879   struct GNUNET_TUN_DnsHeader dns;
880   struct MeshExit *exit;
881
882   GNUNET_STATISTICS_update (stats,
883                             gettext_noop ("# DNS requests intercepted"),
884                             1, GNUNET_NO);
885   if (0 == dns_exit_available)
886   {
887     GNUNET_STATISTICS_update (stats,
888                               gettext_noop ("# DNS requests dropped (DNS mesh channel down)"),
889                               1, GNUNET_NO);
890     GNUNET_DNS_request_drop (rh);
891     return;
892   }
893   if (request_length < sizeof (dns))
894   {
895     GNUNET_STATISTICS_update (stats,
896                               gettext_noop ("# DNS requests dropped (malformed)"),
897                               1, GNUNET_NO);
898     GNUNET_DNS_request_drop (rh);
899     return;
900   }
901   memcpy (&dns, request, sizeof (dns));
902   mlen = sizeof (struct GNUNET_MessageHeader) + request_length;
903   exit = choose_exit ();
904   GNUNET_assert (NULL != exit);
905   GNUNET_assert (NULL != exit->mesh_channel);
906   rc = GNUNET_malloc (sizeof (struct RequestContext) + mlen);
907   rc->exit = exit;
908   rc->rh = rh;
909   rc->mesh_message = (const struct GNUNET_MessageHeader*) &rc[1];
910   rc->timeout_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT,
911                                                    &timeout_request,
912                                                    rc);
913   rc->dns_id = dns.id;
914   rc->mlen = mlen;
915   hdr.type = htons (GNUNET_MESSAGE_TYPE_VPN_DNS_TO_INTERNET);
916   hdr.size = htons (mlen);
917   memcpy (&rc[1], &hdr, sizeof (struct GNUNET_MessageHeader));
918   memcpy (&(((char*)&rc[1])[sizeof (struct GNUNET_MessageHeader)]),
919           request,
920           request_length);
921   GNUNET_CONTAINER_DLL_insert_tail (exit->transmit_queue_head,
922                                     exit->transmit_queue_tail,
923                                     rc);
924   if (NULL == exit->mesh_th)
925     exit->mesh_th = GNUNET_MESH_notify_transmit_ready (exit->mesh_channel,
926                                                        GNUNET_NO,
927                                                        TIMEOUT,
928                                                        mlen,
929                                                        &transmit_dns_request_to_mesh,
930                                                        exit);
931 }
932
933
934 /**
935  * Process a request via mesh to perform a DNS query.
936  *
937  * @param cls NULL
938  * @param channel connection to the other end
939  * @param channel_ctx pointer to our `struct MeshExit`
940  * @param message the actual message
941  * @return #GNUNET_OK to keep the connection open,
942  *         #GNUNET_SYSERR to close it (signal serious error)
943  */
944 static int
945 receive_dns_response (void *cls,
946                       struct GNUNET_MESH_Channel *channel,
947                       void **channel_ctx,
948                       const struct GNUNET_MessageHeader *message)
949 {
950   struct MeshExit *exit = *channel_ctx;
951   struct GNUNET_TUN_DnsHeader dns;
952   size_t mlen;
953   struct RequestContext *rc;
954
955   mlen = ntohs (message->size);
956   mlen -= sizeof (struct GNUNET_MessageHeader);
957   if (mlen < sizeof (struct GNUNET_TUN_DnsHeader))
958   {
959     GNUNET_break_op (0);
960     return GNUNET_SYSERR;
961   }
962   memcpy (&dns, &message[1], sizeof (dns));
963   for (rc = exit->receive_queue_head; NULL != rc; rc = rc->next)
964   {
965     GNUNET_assert (GNUNET_YES == rc->was_transmitted);
966     if (dns.id == rc->dns_id)
967     {
968       GNUNET_STATISTICS_update (stats,
969                                 gettext_noop ("# DNS replies received"),
970                                 1, GNUNET_NO);
971       GNUNET_DNS_request_answer (rc->rh,
972                                  mlen,
973                                  (const void*) &message[1]);
974       GNUNET_CONTAINER_DLL_remove (exit->receive_queue_head,
975                                    exit->receive_queue_tail,
976                                    rc);
977       GNUNET_SCHEDULER_cancel (rc->timeout_task);
978       GNUNET_free (rc);
979       exit->num_answered++;
980       exit->num_transmitted++;
981       return GNUNET_OK;
982     }
983   }
984   GNUNET_STATISTICS_update (stats,
985                             gettext_noop ("# DNS replies dropped (too late?)"),
986                             1, GNUNET_NO);
987   return GNUNET_OK;
988 }
989
990
991 /**
992  * Abort all pending DNS requests with the given mesh exit.
993  *
994  * @param exit mesh exit to abort requests for
995  */
996 static void
997 abort_all_requests (struct MeshExit *exit)
998 {
999   struct RequestContext *rc;
1000
1001   while (NULL != (rc = exit->receive_queue_head))
1002   {
1003     GNUNET_CONTAINER_DLL_remove (exit->receive_queue_head,
1004                                  exit->receive_queue_tail,
1005                                  rc);
1006     GNUNET_DNS_request_drop (rc->rh);
1007     GNUNET_SCHEDULER_cancel (rc->timeout_task);
1008     GNUNET_free (rc);
1009   }
1010   while (NULL != (rc = exit->transmit_queue_head))
1011   {
1012     GNUNET_CONTAINER_DLL_remove (exit->transmit_queue_head,
1013                                  exit->transmit_queue_tail,
1014                                  rc);
1015     GNUNET_DNS_request_drop (rc->rh);
1016     GNUNET_SCHEDULER_cancel (rc->timeout_task);
1017     GNUNET_free (rc);
1018   }
1019 }
1020
1021
1022 /**
1023  * Function scheduled as very last function, cleans up after us
1024  *
1025  * @param cls closure, NULL
1026  * @param tskctx scheduler context, unused
1027  */
1028 static void
1029 cleanup (void *cls,
1030          const struct GNUNET_SCHEDULER_TaskContext *tskctx)
1031 {
1032   struct MeshExit *exit;
1033
1034   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1035               "Protocol translation daemon is shutting down now\n");
1036   if (NULL != vpn_handle)
1037   {
1038     GNUNET_VPN_disconnect (vpn_handle);
1039     vpn_handle = NULL;
1040   }
1041   while (NULL != (exit = exit_head))
1042   {
1043     GNUNET_CONTAINER_DLL_remove (exit_head,
1044                                  exit_tail,
1045                                  exit);
1046     if (NULL != exit->mesh_th)
1047     {
1048       GNUNET_MESH_notify_transmit_ready_cancel (exit->mesh_th);
1049       exit->mesh_th = NULL;
1050     }
1051     if (NULL != exit->mesh_channel)
1052     {
1053       GNUNET_MESH_channel_destroy (exit->mesh_channel);
1054       exit->mesh_channel = NULL;
1055     }
1056     abort_all_requests (exit);
1057     GNUNET_free (exit);
1058   }
1059   if (NULL != mesh_handle)
1060   {
1061     GNUNET_MESH_disconnect (mesh_handle);
1062     mesh_handle = NULL;
1063   }
1064   if (NULL != dns_post_handle)
1065   {
1066     GNUNET_DNS_disconnect (dns_post_handle);
1067     dns_post_handle = NULL;
1068   }
1069   if (NULL != dns_pre_handle)
1070   {
1071     GNUNET_DNS_disconnect (dns_pre_handle);
1072     dns_pre_handle = NULL;
1073   }
1074   if (NULL != stats)
1075   {
1076     GNUNET_STATISTICS_destroy (stats, GNUNET_YES);
1077     stats = NULL;
1078   }
1079   if (NULL != dht_get)
1080   {
1081     GNUNET_DHT_get_stop (dht_get);
1082     dht_get = NULL;
1083   }
1084   if (NULL != dht)
1085   {
1086     GNUNET_DHT_disconnect (dht);
1087     dht = NULL;
1088   }
1089 }
1090
1091
1092 /**
1093  * Function called whenever a channel is destroyed.  Should clean up
1094  * the associated state and attempt to build a new one.
1095  *
1096  * It must NOT call #GNUNET_MESH_channel_destroy on the channel.
1097  *
1098  * @param cls closure (the `struct MeshExit` set from #GNUNET_MESH_connect)
1099  * @param channel connection to the other end (henceforth invalid)
1100  * @param channel_ctx place where local state associated
1101  *                   with the channel is stored
1102  */
1103 static void
1104 mesh_channel_end_cb (void *cls,
1105                     const struct GNUNET_MESH_Channel *channel,
1106                     void *channel_ctx)
1107 {
1108   struct MeshExit *exit = channel_ctx;
1109   struct MeshExit *alt;
1110   struct RequestContext *rc;
1111
1112   if (NULL != exit->mesh_th)
1113   {
1114     GNUNET_MESH_notify_transmit_ready_cancel (exit->mesh_th);
1115     exit->mesh_th = NULL;
1116   }
1117   exit->mesh_channel = NULL;
1118   dns_exit_available--;
1119   /* open alternative channels */
1120   try_open_exit ();
1121   if (NULL == exit->mesh_channel)
1122   {
1123     /* our channel is now closed, move our requests to an alternative
1124        channel */
1125     alt = choose_exit ();
1126     while (NULL != (rc = exit->transmit_queue_head))
1127     {
1128       GNUNET_CONTAINER_DLL_remove (exit->transmit_queue_head,
1129                                    exit->transmit_queue_tail,
1130                                    rc);
1131       rc->exit = alt;
1132       GNUNET_CONTAINER_DLL_insert (alt->transmit_queue_head,
1133                                    alt->transmit_queue_tail,
1134                                    rc);
1135     }
1136     while (NULL != (rc = exit->receive_queue_head))
1137     {
1138       GNUNET_CONTAINER_DLL_remove (exit->receive_queue_head,
1139                                    exit->receive_queue_tail,
1140                                    rc);
1141       rc->was_transmitted = GNUNET_NO;
1142       rc->exit = alt;
1143       GNUNET_CONTAINER_DLL_insert (alt->transmit_queue_head,
1144                                    alt->transmit_queue_tail,
1145                                    rc);
1146     }
1147   }
1148   else
1149   {
1150     /* the same peer was chosen, just make sure the queue processing is restarted */
1151     alt = exit;
1152   }
1153   if ( (NULL == alt->mesh_th) &&
1154        (NULL != (rc = alt->transmit_queue_head)) )
1155     alt->mesh_th = GNUNET_MESH_notify_transmit_ready (alt->mesh_channel,
1156                                                       GNUNET_NO,
1157                                                       TIMEOUT,
1158                                                       rc->mlen,
1159                                                       &transmit_dns_request_to_mesh,
1160                                                       alt);
1161 }
1162
1163
1164 /**
1165  * Function called whenever we find an advertisement for a
1166  * DNS exit in the DHT.  If we don't have a mesh channel,
1167  * we should build one; otherwise, we should save the
1168  * advertisement for later use.
1169  *
1170  * @param cls closure
1171  * @param exp when will this value expire
1172  * @param key key of the result
1173  * @param get_path peers on reply path (or NULL if not recorded)
1174  *                 [0] = datastore's first neighbor, [length - 1] = local peer
1175  * @param get_path_length number of entries in @a get_path
1176  * @param put_path peers on the PUT path (or NULL if not recorded)
1177  *                 [0] = origin, [length - 1] = datastore
1178  * @param put_path_length number of entries in @a put_path
1179  * @param type type of the result
1180  * @param size number of bytes in @a data
1181  * @param data pointer to the result data
1182  */
1183 static void
1184 handle_dht_result (void *cls,
1185                    struct GNUNET_TIME_Absolute exp,
1186                    const struct GNUNET_HashCode *key,
1187                    const struct GNUNET_PeerIdentity *get_path,
1188                    unsigned int get_path_length,
1189                    const struct GNUNET_PeerIdentity *put_path,
1190                    unsigned int put_path_length,
1191                    enum GNUNET_BLOCK_Type type,
1192                    size_t size, const void *data)
1193 {
1194   const struct GNUNET_DNS_Advertisement *ad;
1195   struct MeshExit *exit;
1196
1197   if (sizeof (struct GNUNET_DNS_Advertisement) != size)
1198   {
1199     GNUNET_break (0);
1200     return;
1201   }
1202   ad = data;
1203   for (exit = exit_head; NULL != exit; exit = exit->next)
1204     if (0 == memcmp (&ad->peer,
1205                      &exit->peer,
1206                      sizeof (struct GNUNET_PeerIdentity)))
1207       break;
1208   if (NULL == exit)
1209   {
1210     exit = GNUNET_new (struct MeshExit);
1211     exit->peer = ad->peer;
1212     /* channel is closed, so insert at the end */
1213     GNUNET_CONTAINER_DLL_insert_tail (exit_head,
1214                                       exit_tail,
1215                                       exit);
1216   }
1217   exit->expiration = GNUNET_TIME_absolute_max (exit->expiration,
1218                                                GNUNET_TIME_absolute_ntoh (ad->expiration_time));
1219   if (dns_exit_available < MAX_OPEN_TUNNELS)
1220     try_open_exit ();
1221 }
1222
1223
1224 /**
1225  * @brief Main function that will be run by the scheduler.
1226  *
1227  * @param cls closure
1228  * @param args remaining command-line arguments
1229  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1230  * @param cfg_ configuration
1231  */
1232 static void
1233 run (void *cls, char *const *args GNUNET_UNUSED,
1234      const char *cfgfile GNUNET_UNUSED,
1235      const struct GNUNET_CONFIGURATION_Handle *cfg_)
1236 {
1237   struct GNUNET_HashCode dns_key;
1238
1239   cfg = cfg_;
1240   stats = GNUNET_STATISTICS_create ("pt", cfg);
1241   ipv4_pt = GNUNET_CONFIGURATION_get_value_yesno (cfg, "pt", "TUNNEL_IPV4");
1242   ipv6_pt = GNUNET_CONFIGURATION_get_value_yesno (cfg, "pt", "TUNNEL_IPV6");
1243   dns_channel = GNUNET_CONFIGURATION_get_value_yesno (cfg, "pt", "TUNNEL_DNS");
1244   if (! (ipv4_pt || ipv6_pt || dns_channel))
1245   {
1246     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1247                 _("No useful service enabled.  Exiting.\n"));
1248     GNUNET_SCHEDULER_shutdown ();
1249     return;
1250   }
1251   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &cleanup, cls);
1252   if (ipv4_pt || ipv6_pt)
1253   {
1254     dns_post_handle
1255       = GNUNET_DNS_connect (cfg,
1256                             GNUNET_DNS_FLAG_POST_RESOLUTION,
1257                             &dns_post_request_handler, NULL);
1258     if (NULL == dns_post_handle)
1259     {
1260       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1261                   _("Failed to connect to %s service.  Exiting.\n"),
1262                   "DNS");
1263       GNUNET_SCHEDULER_shutdown ();
1264       return;
1265     }
1266     vpn_handle = GNUNET_VPN_connect (cfg);
1267     if (NULL == vpn_handle)
1268     {
1269       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1270                   _("Failed to connect to %s service.  Exiting.\n"),
1271                   "VPN");
1272       GNUNET_SCHEDULER_shutdown ();
1273       return;
1274     }
1275   }
1276   if (dns_channel)
1277   {
1278     static struct GNUNET_MESH_MessageHandler mesh_handlers[] = {
1279       {&receive_dns_response, GNUNET_MESSAGE_TYPE_VPN_DNS_FROM_INTERNET, 0},
1280       {NULL, 0, 0}
1281     };
1282
1283     dns_pre_handle
1284       = GNUNET_DNS_connect (cfg,
1285                             GNUNET_DNS_FLAG_PRE_RESOLUTION,
1286                             &dns_pre_request_handler, NULL);
1287     if (NULL == dns_pre_handle)
1288     {
1289       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1290                   _("Failed to connect to %s service.  Exiting.\n"),
1291                   "DNS");
1292       GNUNET_SCHEDULER_shutdown ();
1293       return;
1294     }
1295     mesh_handle = GNUNET_MESH_connect (cfg, NULL, NULL,
1296                                        &mesh_channel_end_cb,
1297                                        mesh_handlers, NULL);
1298     if (NULL == mesh_handle)
1299     {
1300       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1301                   _("Failed to connect to %s service.  Exiting.\n"),
1302                   "MESH");
1303       GNUNET_SCHEDULER_shutdown ();
1304       return;
1305     }
1306     dht = GNUNET_DHT_connect (cfg, 1);
1307     if (NULL == dht)
1308     {
1309       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1310                   _("Failed to connect to %s service.  Exiting.\n"),
1311                   "DHT");
1312       GNUNET_SCHEDULER_shutdown ();
1313       return;
1314     }
1315     GNUNET_CRYPTO_hash ("dns", strlen ("dns"), &dns_key);
1316     dht_get = GNUNET_DHT_get_start (dht,
1317                                     GNUNET_BLOCK_TYPE_DNS,
1318                                     &dns_key,
1319                                     1,
1320                                     GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE,
1321                                     NULL, 0,
1322                                     &handle_dht_result, NULL);
1323   }
1324 }
1325
1326
1327 /**
1328  * The main function
1329  *
1330  * @param argc number of arguments from the command line
1331  * @param argv command line arguments
1332  * @return 0 ok, 1 on error
1333  */
1334 int
1335 main (int argc, char *const *argv)
1336 {
1337   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
1338     GNUNET_GETOPT_OPTION_END
1339   };
1340   int ret;
1341
1342   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
1343     return 2;
1344   ret = (GNUNET_OK ==
1345          GNUNET_PROGRAM_run (argc, argv, "gnunet-daemon-pt",
1346                              gettext_noop
1347                              ("Daemon to run to perform IP protocol translation to GNUnet"),
1348                              options, &run, NULL)) ? 0 : 1;
1349   GNUNET_free ((void*) argv);
1350   return ret;
1351 }
1352
1353
1354 /* end of gnunet-daemon-pt.c */