add integer overflow guards and avoid (unlimited) stack allocation
[oweals/gnunet.git] / src / pt / gnunet-daemon-pt.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2010, 2012, 2017 Christian Grothoff
4
5      GNUnet is free software: you can redistribute it and/or modify it
6      under the terms of the GNU Affero General Public License as published
7      by the Free Software Foundation, either version 3 of the License,
8      or (at your 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      Affero General Public License for more details.
14
15      You should have received a copy of the GNU Affero General Public License
16      along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18      SPDX-License-Identifier: AGPL3.0-or-later
19  */
20 /**
21  * @file pt/gnunet-daemon-pt.c
22  * @brief tool to manipulate DNS and VPN services to perform protocol translation (IPvX over GNUnet)
23  * @author Christian Grothoff
24  */
25 #include "platform.h"
26 #include "gnunet_util_lib.h"
27 #include "gnunet_dns_service.h"
28 #include "gnunet_dnsparser_lib.h"
29 #include "gnunet_cadet_service.h"
30 #include "gnunet_tun_lib.h"
31 #include "gnunet_dht_service.h"
32 #include "gnunet_vpn_service.h"
33 #include "gnunet_statistics_service.h"
34 #include "gnunet_applications.h"
35 #include "block_dns.h"
36
37
38 /**
39  * After how long do we time out if we could not get an IP from VPN or CADET?
40  */
41 #define TIMEOUT GNUNET_TIME_UNIT_MINUTES
42
43 /**
44  * How many bytes of payload do we allow at most for a DNS reply?
45  * Given that this is pretty much limited to loopback, we can be
46  * pretty high (Linux loopback defaults to 16k, most local UDP packets
47  * should survive up to 9k (NFS), so 8k should be pretty safe in
48  * general).
49  */
50 #define MAX_DNS_SIZE (8 * 1024)
51
52 /**
53  * How many channels do we open at most at the same time?
54  */
55 #define MAX_OPEN_TUNNELS 4
56
57
58 /**
59  * Which group of DNS records are we currently processing?
60  */
61 enum RequestGroup
62 {
63   /**
64    * DNS answers
65    */
66   ANSWERS = 0,
67
68   /**
69    * DNS authority records
70    */
71   AUTHORITY_RECORDS = 1,
72
73   /**
74    * DNS additional records
75    */
76   ADDITIONAL_RECORDS = 2,
77
78   /**
79    * We're done processing.
80    */
81   END = 3
82 };
83
84
85 /**
86  * Information tracked per DNS reply that we are processing.
87  */
88 struct ReplyContext
89 {
90   /**
91    * Handle to submit the final result.
92    */
93   struct GNUNET_DNS_RequestHandle *rh;
94
95   /**
96    * DNS packet that is being modified.
97    */
98   struct GNUNET_DNSPARSER_Packet *dns;
99
100   /**
101    * Active redirection request with the VPN.
102    */
103   struct GNUNET_VPN_RedirectionRequest *rr;
104
105   /**
106    * Record for which we have an active redirection request.
107    */
108   struct GNUNET_DNSPARSER_Record *rec;
109
110   /**
111    * Offset in the current record group that is being modified.
112    */
113   unsigned int offset;
114
115   /**
116    * Group that is being modified
117    */
118   enum RequestGroup group;
119 };
120
121
122 /**
123  * Handle to a peer that advertised that it is willing to serve
124  * as a DNS exit.  We try to keep a few channels open and a few
125  * peers in reserve.
126  */
127 struct CadetExit
128 {
129   /**
130    * Kept in a DLL.
131    */
132   struct CadetExit *next;
133
134   /**
135    * Kept in a DLL.
136    */
137   struct CadetExit *prev;
138
139   /**
140    * Channel we use for DNS requests over CADET, NULL if we did
141    * not initialze a channel to this peer yet.
142    */
143   struct GNUNET_CADET_Channel *cadet_channel;
144
145   /**
146    * At what time did the peer's advertisement expire?
147    */
148   struct GNUNET_TIME_Absolute expiration;
149
150   /**
151    * Head of DLL of requests waiting for a response.
152    */
153   struct RequestContext *receive_queue_head;
154
155   /**
156    * Tail of DLL of requests waiting for a response.
157    */
158   struct RequestContext *receive_queue_tail;
159
160   /**
161    * Identity of the peer that is providing the exit for us.
162    */
163   struct GNUNET_PeerIdentity peer;
164
165   /**
166    * How many DNS requests did we transmit via this channel?
167    */
168   unsigned int num_transmitted;
169
170   /**
171    * How many DNS requests were answered via this channel?
172    */
173   unsigned int num_answered;
174
175   /**
176    * Size of the window, 0 if we are busy.
177    */
178   /* unsigned */ int idle;
179 };
180
181
182 /**
183  * State we keep for a request that is going out via CADET.
184  */
185 struct RequestContext
186 {
187   /**
188    * We keep these in a DLL.
189    */
190   struct RequestContext *next;
191
192   /**
193    * We keep these in a DLL.
194    */
195   struct RequestContext *prev;
196
197   /**
198    * Exit that was chosen for this request.
199    */
200   struct CadetExit *exit;
201
202   /**
203    * Handle for interaction with DNS service.
204    */
205   struct GNUNET_DNS_RequestHandle *rh;
206
207   /**
208    * Envelope with the request we are transmitting.
209    */
210   struct GNUNET_MQ_Envelope *env;
211
212   /**
213    * Task used to abort this operation with timeout.
214    */
215   struct GNUNET_SCHEDULER_Task *timeout_task;
216
217   /**
218    * Length of the request message that follows this struct.
219    */
220   uint16_t mlen;
221
222   /**
223    * ID of the original DNS request (used to match the reply).
224    */
225   uint16_t dns_id;
226 };
227
228
229 /**
230  * Head of DLL of cadet exits.  Cadet exits with an open channel are
231  * always at the beginning (so we do not have to traverse the entire
232  * list to find them).
233  */
234 static struct CadetExit *exit_head;
235
236 /**
237  * Tail of DLL of cadet exits.
238  */
239 static struct CadetExit *exit_tail;
240
241 /**
242  * The handle to the configuration used throughout the process
243  */
244 static const struct GNUNET_CONFIGURATION_Handle *cfg;
245
246 /**
247  * The handle to the VPN
248  */
249 static struct GNUNET_VPN_Handle *vpn_handle;
250
251 /**
252  * The handle to the CADET service
253  */
254 static struct GNUNET_CADET_Handle *cadet_handle;
255
256 /**
257  * Statistics.
258  */
259 static struct GNUNET_STATISTICS_Handle *stats;
260
261 /**
262  * The handle to DNS post-resolution modifications.
263  */
264 static struct GNUNET_DNS_Handle *dns_post_handle;
265
266 /**
267  * The handle to DNS pre-resolution modifications.
268  */
269 static struct GNUNET_DNS_Handle *dns_pre_handle;
270
271 /**
272  * Handle to access the DHT.
273  */
274 static struct GNUNET_DHT_Handle *dht;
275
276 /**
277  * Our DHT GET operation to find DNS exits.
278  */
279 static struct GNUNET_DHT_GetHandle *dht_get;
280
281 /**
282  * Are we doing IPv4-pt?
283  */
284 static int ipv4_pt;
285
286 /**
287  * Are we doing IPv6-pt?
288  */
289 static int ipv6_pt;
290
291 /**
292  * Are we channeling DNS queries?
293  */
294 static int dns_channel;
295
296 /**
297  * Number of DNS exit peers we currently have in the cadet channel.
298  * Used to see if using the cadet channel makes any sense right now,
299  * as well as to decide if we should open new channels.
300  */
301 static unsigned int dns_exit_available;
302
303
304 /**
305  * We are short on cadet exits, try to open another one.
306  */
307 static void
308 try_open_exit (void);
309
310
311 /**
312  * Compute the weight of the given exit.  The higher the weight,
313  * the more likely it will be that the channel will be chosen.
314  * A weigt of zero means that we should close the channel as it
315  * is so bad, that we should not use it.
316  *
317  * @param exit exit to calculate the weight for
318  * @return weight of the channel
319  */
320 static uint32_t
321 get_channel_weight (struct CadetExit *exit)
322 {
323   uint32_t dropped;
324   uint32_t drop_percent;
325   uint32_t good_percent;
326
327   GNUNET_assert (exit->num_transmitted >= exit->num_answered);
328   dropped = exit->num_transmitted - exit->num_answered;
329   if (exit->num_transmitted > 0)
330     drop_percent = (uint32_t) ((100LL * dropped) / exit->num_transmitted);
331   else
332     drop_percent = 50; /* no data */
333   if ((exit->num_transmitted > 20) &&
334       (drop_percent > 25))
335     return 0; /* statistically significant, and > 25% loss, die */
336   good_percent = 100 - drop_percent;
337   GNUNET_assert (0 != good_percent);
338   if (UINT32_MAX / good_percent / good_percent < exit->num_transmitted)
339     return UINT32_MAX; /* formula below would overflow */
340   return 1 + good_percent * good_percent * exit->num_transmitted;
341 }
342
343
344 /**
345  * Choose a cadet exit for a DNS request.  We try to use a channel
346  * that is reliable and currently available.  All existing
347  * channels are given a base weight of 1, plus a score relating
348  * to the total number of queries answered in relation to the
349  * total number of queries we sent to that channel.  That
350  * score is doubled if the channel is currently idle.
351  *
352  * @return NULL if no exit is known, otherwise the
353  *         exit that we should use to queue a message with
354  */
355 static struct CadetExit *
356 choose_exit ()
357 {
358   struct CadetExit *pos;
359   uint64_t total_transmitted;
360   uint64_t selected_offset;
361   uint32_t channel_weight;
362
363   total_transmitted = 0;
364   for (pos = exit_head; NULL != pos; pos = pos->next)
365   {
366     if (NULL == pos->cadet_channel)
367       break;
368     channel_weight = get_channel_weight (pos);
369     total_transmitted += channel_weight;
370     /* double weight for idle channels */
371     if (0 != pos->idle)
372       total_transmitted += channel_weight;
373   }
374   if (0 == total_transmitted)
375   {
376     /* no channels available, or only a very bad one... */
377     return exit_head;
378   }
379   selected_offset = GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
380                                               total_transmitted);
381   total_transmitted = 0;
382   for (pos = exit_head; NULL != pos; pos = pos->next)
383   {
384     if (NULL == pos->cadet_channel)
385       break;
386     channel_weight = get_channel_weight (pos);
387     total_transmitted += channel_weight;
388     /* double weight for idle channels */
389     if (0 != pos->idle)
390       total_transmitted += channel_weight;
391     if (total_transmitted > selected_offset)
392       return pos;
393   }
394   GNUNET_break (0);
395   return NULL;
396 }
397
398
399 /**
400  * We're done modifying all records in the response.  Submit the reply
401  * and free the resources of the rc.
402  *
403  * @param rc context to process
404  */
405 static void
406 finish_request (struct ReplyContext *rc)
407 {
408   char *buf;
409   size_t buf_len;
410
411   if (GNUNET_SYSERR ==
412       GNUNET_DNSPARSER_pack (rc->dns,
413                              MAX_DNS_SIZE,
414                              &buf,
415                              &buf_len))
416   {
417     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
418                 _ ("Failed to pack DNS request.  Dropping.\n"));
419     GNUNET_DNS_request_drop (rc->rh);
420   }
421   else
422   {
423     GNUNET_STATISTICS_update (stats,
424                               gettext_noop ("# DNS requests mapped to VPN"),
425                               1, GNUNET_NO);
426     GNUNET_DNS_request_answer (rc->rh,
427                                buf_len,
428                                buf);
429     GNUNET_free (buf);
430   }
431   GNUNET_DNSPARSER_free_packet (rc->dns);
432   GNUNET_free (rc);
433 }
434
435
436 /**
437  * Process the next record of the given request context.
438  * When done, submit the reply and free the resources of
439  * the rc.
440  *
441  * @param rc context to process
442  */
443 static void
444 submit_request (struct ReplyContext *rc);
445
446
447 /**
448  * Callback invoked from the VPN service once a redirection is
449  * available.  Provides the IP address that can now be used to
450  * reach the requested destination.  We substitute the active
451  * record and then continue with 'submit_request' to look at
452  * the other records.
453  *
454  * @param cls our `struct ReplyContext`
455  * @param af address family, AF_INET or AF_INET6; AF_UNSPEC on error;
456  *                will match 'result_af' from the request
457  * @param address IP address (struct in_addr or struct in_addr6, depending on 'af')
458  *                that the VPN allocated for the redirection;
459  *                traffic to this IP will now be redirected to the
460  *                specified target peer; NULL on error
461  */
462 static void
463 vpn_allocation_callback (void *cls,
464                          int af,
465                          const void *address)
466 {
467   struct ReplyContext *rc = cls;
468
469   rc->rr = NULL;
470   if (af == AF_UNSPEC)
471   {
472     GNUNET_DNS_request_drop (rc->rh);
473     GNUNET_DNSPARSER_free_packet (rc->dns);
474     GNUNET_free (rc);
475     return;
476   }
477   GNUNET_STATISTICS_update (stats,
478                             gettext_noop ("# DNS records modified"),
479                             1,
480                             GNUNET_NO);
481   switch (rc->rec->type)
482   {
483   case GNUNET_DNSPARSER_TYPE_A:
484     GNUNET_assert (AF_INET == af);
485     GNUNET_memcpy (rc->rec->data.raw.data,
486                    address,
487                    sizeof(struct in_addr));
488     break;
489
490   case GNUNET_DNSPARSER_TYPE_AAAA:
491     GNUNET_assert (AF_INET6 == af);
492     GNUNET_memcpy (rc->rec->data.raw.data,
493                    address,
494                    sizeof(struct in6_addr));
495     break;
496
497   default:
498     GNUNET_assert (0);
499     return;
500   }
501   rc->rec = NULL;
502   submit_request (rc);
503 }
504
505
506 /**
507  * Modify the given DNS record by asking VPN to create a channel
508  * to the given address.  When done, continue with submitting
509  * other records from the request context ('submit_request' is
510  * our continuation).
511  *
512  * @param rc context to process
513  * @param rec record to modify
514  */
515 static void
516 modify_address (struct ReplyContext *rc,
517                 struct GNUNET_DNSPARSER_Record *rec)
518 {
519   int af;
520
521   switch (rec->type)
522   {
523   case GNUNET_DNSPARSER_TYPE_A:
524     af = AF_INET;
525     GNUNET_assert (rec->data.raw.data_len == sizeof(struct in_addr));
526     break;
527
528   case GNUNET_DNSPARSER_TYPE_AAAA:
529     af = AF_INET6;
530     GNUNET_assert (rec->data.raw.data_len == sizeof(struct in6_addr));
531     break;
532
533   default:
534     GNUNET_assert (0);
535     return;
536   }
537   rc->rec = rec;
538   rc->rr = GNUNET_VPN_redirect_to_ip (vpn_handle,
539                                       af,
540                                       af,
541                                       rec->data.raw.data,
542                                       GNUNET_TIME_relative_to_absolute (
543                                         TIMEOUT),
544                                       &vpn_allocation_callback,
545                                       rc);
546 }
547
548
549 /**
550  * Process the next record of the given request context.
551  * When done, submit the reply and free the resources of
552  * the rc.
553  *
554  * @param rc context to process
555  */
556 static void
557 submit_request (struct ReplyContext *rc)
558 {
559   struct GNUNET_DNSPARSER_Record *ra;
560   unsigned int ra_len;
561   unsigned int i;
562
563   while (1)
564   {
565     switch (rc->group)
566     {
567     case ANSWERS:
568       ra = rc->dns->answers;
569       ra_len = rc->dns->num_answers;
570       break;
571
572     case AUTHORITY_RECORDS:
573       ra = rc->dns->authority_records;
574       ra_len = rc->dns->num_authority_records;
575       break;
576
577     case ADDITIONAL_RECORDS:
578       ra = rc->dns->additional_records;
579       ra_len = rc->dns->num_additional_records;
580       break;
581
582     case END:
583       finish_request (rc);
584       return;
585
586     default:
587       GNUNET_assert (0);
588     }
589     for (i = rc->offset; i < ra_len; i++)
590     {
591       switch (ra[i].type)
592       {
593       case GNUNET_DNSPARSER_TYPE_A:
594         if (ipv4_pt)
595         {
596           rc->offset = i + 1;
597           modify_address (rc,
598                           &ra[i]);
599           return;
600         }
601         break;
602
603       case GNUNET_DNSPARSER_TYPE_AAAA:
604         if (ipv6_pt)
605         {
606           rc->offset = i + 1;
607           modify_address (rc,
608                           &ra[i]);
609           return;
610         }
611         break;
612       }
613     }
614     rc->group++;
615   }
616 }
617
618
619 /**
620  * Test if any of the given records need protocol-translation work.
621  *
622  * @param ra array of records
623  * @param ra_len number of entries in @a ra
624  * @return #GNUNET_YES if any of the given records require protocol-translation
625  */
626 static int
627 work_test (const struct GNUNET_DNSPARSER_Record *ra,
628            unsigned int ra_len)
629 {
630   unsigned int i;
631
632   for (i = 0; i < ra_len; i++)
633   {
634     switch (ra[i].type)
635     {
636     case GNUNET_DNSPARSER_TYPE_A:
637       if (ipv4_pt)
638         return GNUNET_YES;
639       break;
640
641     case GNUNET_DNSPARSER_TYPE_AAAA:
642       if (ipv6_pt)
643         return GNUNET_YES;
644       break;
645     }
646   }
647   return GNUNET_NO;
648 }
649
650
651 /**
652  * This function is called AFTER we got an IP address for a
653  * DNS request.  Now, the PT daemon has the chance to substitute
654  * the IP address with one from the VPN range to channel requests
655  * destined for this IP address via VPN and CADET.
656  *
657  * @param cls closure
658  * @param rh request handle to user for reply
659  * @param request_length number of bytes in request
660  * @param request udp payload of the DNS request
661  */
662 static void
663 dns_post_request_handler (void *cls,
664                           struct GNUNET_DNS_RequestHandle *rh,
665                           size_t request_length,
666                           const char *request)
667 {
668   struct GNUNET_DNSPARSER_Packet *dns;
669   struct ReplyContext *rc;
670   int work;
671
672   GNUNET_STATISTICS_update (stats,
673                             gettext_noop ("# DNS replies intercepted"),
674                             1, GNUNET_NO);
675   dns = GNUNET_DNSPARSER_parse (request,
676                                 request_length);
677   if (NULL == dns)
678   {
679     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
680                 _ ("Failed to parse DNS request.  Dropping.\n"));
681     GNUNET_DNS_request_drop (rh);
682     return;
683   }
684   work = GNUNET_NO;
685   work |= work_test (dns->answers,
686                      dns->num_answers);
687   work |= work_test (dns->authority_records,
688                      dns->num_authority_records);
689   work |= work_test (dns->additional_records,
690                      dns->num_additional_records);
691   if (! work)
692   {
693     GNUNET_DNS_request_forward (rh);
694     GNUNET_DNSPARSER_free_packet (dns);
695     return;
696   }
697   rc = GNUNET_new (struct ReplyContext);
698   rc->rh = rh;
699   rc->dns = dns;
700   rc->offset = 0;
701   rc->group = ANSWERS;
702   submit_request (rc);
703 }
704
705
706 /**
707  * Task run if the time to answer a DNS request via CADET is over.
708  *
709  * @param cls the `struct RequestContext` to abort
710  */
711 static void
712 timeout_request (void *cls)
713 {
714   struct RequestContext *rc = cls;
715   struct CadetExit *exit = rc->exit;
716
717   GNUNET_STATISTICS_update (stats,
718                             gettext_noop ("# DNS requests dropped (timeout)"),
719                             1,
720                             GNUNET_NO);
721   GNUNET_DNS_request_drop (rc->rh);
722   GNUNET_free (rc);
723   if ((0 == get_channel_weight (exit)) &&
724       (NULL == exit->receive_queue_head))
725   {
726     /* this straw broke the camel's back: this channel now has
727        such a low score that it will not be used; close it! */
728     GNUNET_CADET_channel_destroy (exit->cadet_channel);
729     exit->cadet_channel = NULL;
730     GNUNET_CONTAINER_DLL_remove (exit_head,
731                                  exit_tail,
732                                  exit);
733     GNUNET_CONTAINER_DLL_insert_tail (exit_head,
734                                       exit_tail,
735                                       exit);
736     /* go back to semi-innocent: mark as not great, but
737        avoid a prohibitively negative score (see
738      #get_channel_weight(), which checks for a certain
739        minimum number of transmissions before making
740        up an opinion) */exit->num_transmitted = 5;
741     exit->num_answered = 0;
742     dns_exit_available--;
743     /* now try to open an alternative exit */
744     try_open_exit ();
745   }
746 }
747
748
749 /**
750  * This function is called *before* the DNS request has been
751  * given to a "local" DNS resolver.  Channeling for DNS requests
752  * was enabled, so we now need to send the request via some CADET
753  * channel to a DNS EXIT for resolution.
754  *
755  * @param cls closure
756  * @param rh request handle to user for reply
757  * @param request_length number of bytes in request
758  * @param request udp payload of the DNS request
759  */
760 static void
761 dns_pre_request_handler (void *cls,
762                          struct GNUNET_DNS_RequestHandle *rh,
763                          size_t request_length,
764                          const char *request)
765 {
766   struct RequestContext *rc;
767   struct GNUNET_MQ_Envelope *env;
768   struct GNUNET_MessageHeader *hdr;
769   struct GNUNET_TUN_DnsHeader dns;
770   struct CadetExit *exit;
771
772   GNUNET_STATISTICS_update (stats,
773                             gettext_noop ("# DNS requests intercepted"),
774                             1, GNUNET_NO);
775   if (0 == dns_exit_available)
776   {
777     GNUNET_STATISTICS_update (stats,
778                               gettext_noop (
779                                 "# DNS requests dropped (DNS cadet channel down)"),
780                               1, GNUNET_NO);
781     GNUNET_DNS_request_drop (rh);
782     return;
783   }
784   if (request_length < sizeof(dns))
785   {
786     GNUNET_STATISTICS_update (stats,
787                               gettext_noop (
788                                 "# DNS requests dropped (malformed)"),
789                               1, GNUNET_NO);
790     GNUNET_DNS_request_drop (rh);
791     return;
792   }
793   exit = choose_exit ();
794   GNUNET_assert (NULL != exit);
795   GNUNET_assert (NULL != exit->cadet_channel);
796
797   env = GNUNET_MQ_msg_extra (hdr,
798                              request_length,
799                              GNUNET_MESSAGE_TYPE_VPN_DNS_TO_INTERNET);
800   GNUNET_memcpy (&hdr[1],
801                  request,
802                  request_length);
803   rc = GNUNET_new (struct RequestContext);
804   rc->exit = exit;
805   rc->rh = rh;
806   rc->timeout_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT,
807                                                    &timeout_request,
808                                                    rc);
809   GNUNET_memcpy (&dns,
810                  request,
811                  sizeof(dns));
812   rc->dns_id = dns.id;
813   rc->env = env;
814   GNUNET_CONTAINER_DLL_remove (exit->receive_queue_head,
815                                exit->receive_queue_tail,
816                                rc);
817   if (0 < exit->idle)
818     exit->idle--;
819   exit->num_transmitted++;
820   GNUNET_MQ_send (GNUNET_CADET_get_mq (exit->cadet_channel),
821                   GNUNET_MQ_env_copy (env));
822 }
823
824
825 GNUNET_NETWORK_STRUCT_BEGIN
826
827 /**
828  * Message with a DNS response.
829  */
830 struct DnsResponseMessage
831 {
832   /**
833    * GNUnet header, of type #GNUNET_MESSAGE_TYPE_VPN_DNS_FROM_INTERNET
834    */
835   struct GNUNET_MessageHeader header;
836
837   /**
838    * DNS header.
839    */
840   struct GNUNET_TUN_DnsHeader dns;
841
842   /* Followed by more DNS payload */
843 };
844
845 GNUNET_NETWORK_STRUCT_END
846
847 /**
848  * Process a request via cadet to perform a DNS query.
849  *
850  * @param cls the `struct CadetExit` which got the message
851  * @param msg the actual message
852  * @return #GNUNET_OK to keep the connection open,
853  *         #GNUNET_SYSERR to close it (signal serious error)
854  */
855 static int
856 check_dns_response (void *cls,
857                     const struct DnsResponseMessage *msg)
858 {
859   return GNUNET_OK; /* all OK */
860 }
861
862
863 /**
864  * Process a request via cadet to perform a DNS query.
865  *
866  * @param cls the `struct CadetExit` which got the message
867  * @param msg the actual message
868  */
869 static void
870 handle_dns_response (void *cls,
871                      const struct DnsResponseMessage *msg)
872 {
873   struct CadetExit *exit = cls;
874   size_t mlen;
875   struct RequestContext *rc;
876
877   mlen = ntohs (msg->header.size) - sizeof(*msg);
878   for (rc = exit->receive_queue_head; NULL != rc; rc = rc->next)
879   {
880     if (msg->dns.id == rc->dns_id)
881     {
882       GNUNET_STATISTICS_update (stats,
883                                 gettext_noop ("# DNS replies received"),
884                                 1,
885                                 GNUNET_NO);
886       GNUNET_DNS_request_answer (rc->rh,
887                                  mlen + sizeof(struct GNUNET_TUN_DnsHeader),
888                                  (const void *) &msg->dns);
889       GNUNET_CONTAINER_DLL_remove (exit->receive_queue_head,
890                                    exit->receive_queue_tail,
891                                    rc);
892       GNUNET_SCHEDULER_cancel (rc->timeout_task);
893       GNUNET_MQ_discard (rc->env);
894       GNUNET_free (rc);
895       exit->num_answered++;
896       return;
897     }
898   }
899   GNUNET_STATISTICS_update (stats,
900                             gettext_noop ("# DNS replies dropped (too late?)"),
901                             1, GNUNET_NO);
902 }
903
904
905 /**
906  * Abort all pending DNS requests with the given cadet exit.
907  *
908  * @param exit cadet exit to abort requests for
909  */
910 static void
911 abort_all_requests (struct CadetExit *exit)
912 {
913   struct RequestContext *rc;
914
915   while (NULL != (rc = exit->receive_queue_head))
916   {
917     GNUNET_CONTAINER_DLL_remove (exit->receive_queue_head,
918                                  exit->receive_queue_tail,
919                                  rc);
920     GNUNET_DNS_request_drop (rc->rh);
921     GNUNET_SCHEDULER_cancel (rc->timeout_task);
922     GNUNET_MQ_discard (rc->env);
923     GNUNET_free (rc);
924   }
925 }
926
927
928 /**
929  * Function scheduled as very last function, cleans up after us
930  *
931  * @param cls closure, NULL
932  */
933 static void
934 cleanup (void *cls)
935 {
936   struct CadetExit *exit;
937
938   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
939               "Protocol translation daemon is shutting down now\n");
940   if (NULL != vpn_handle)
941   {
942     GNUNET_VPN_disconnect (vpn_handle);
943     vpn_handle = NULL;
944   }
945   while (NULL != (exit = exit_head))
946   {
947     GNUNET_CONTAINER_DLL_remove (exit_head,
948                                  exit_tail,
949                                  exit);
950     if (NULL != exit->cadet_channel)
951     {
952       GNUNET_CADET_channel_destroy (exit->cadet_channel);
953       exit->cadet_channel = NULL;
954     }
955     abort_all_requests (exit);
956     GNUNET_free (exit);
957   }
958   if (NULL != cadet_handle)
959   {
960     GNUNET_CADET_disconnect (cadet_handle);
961     cadet_handle = NULL;
962   }
963   if (NULL != dns_post_handle)
964   {
965     GNUNET_DNS_disconnect (dns_post_handle);
966     dns_post_handle = NULL;
967   }
968   if (NULL != dns_pre_handle)
969   {
970     GNUNET_DNS_disconnect (dns_pre_handle);
971     dns_pre_handle = NULL;
972   }
973   if (NULL != stats)
974   {
975     GNUNET_STATISTICS_destroy (stats, GNUNET_YES);
976     stats = NULL;
977   }
978   if (NULL != dht_get)
979   {
980     GNUNET_DHT_get_stop (dht_get);
981     dht_get = NULL;
982   }
983   if (NULL != dht)
984   {
985     GNUNET_DHT_disconnect (dht);
986     dht = NULL;
987   }
988 }
989
990
991 /**
992  * Function called whenever a channel is destroyed.  Should clean up
993  * the associated state and attempt to build a new one.
994  *
995  * It must NOT call #GNUNET_CADET_channel_destroy on the channel.
996  *
997  * @param cls closure (the `struct CadetExit` set from #GNUNET_CADET_connect)
998  * @param channel connection to the other end (henceforth invalid)
999  * @param channel_ctx place where local state associated
1000  *                   with the channel is stored
1001  */
1002 static void
1003 cadet_channel_end_cb (void *cls,
1004                       const struct GNUNET_CADET_Channel *channel)
1005 {
1006   struct CadetExit *exit = cls;
1007   struct CadetExit *alt;
1008   struct RequestContext *rc;
1009
1010   exit->cadet_channel = NULL;
1011   dns_exit_available--;
1012   /* open alternative channels */
1013   /* our channel is now closed, move our requests to an alternative
1014      channel */
1015   alt = choose_exit ();
1016   while (NULL != (rc = exit->receive_queue_head))
1017   {
1018     GNUNET_CONTAINER_DLL_remove (exit->receive_queue_head,
1019                                  exit->receive_queue_tail,
1020                                  rc);
1021     rc->exit = alt;
1022     GNUNET_CONTAINER_DLL_insert (alt->receive_queue_head,
1023                                  alt->receive_queue_tail,
1024                                  rc);
1025     GNUNET_MQ_send (GNUNET_CADET_get_mq (alt->cadet_channel),
1026                     GNUNET_MQ_env_copy (rc->env));
1027   }
1028   try_open_exit ();
1029 }
1030
1031
1032 /**
1033  * Function called whenever a channel has excess capacity.
1034  *
1035  * @param cls the `struct CadetExit`
1036  * @param channel connection to the other end
1037  * @param window_size how much capacity do we have
1038  */
1039 static void
1040 channel_idle_notify_cb (void *cls,
1041                         const struct GNUNET_CADET_Channel *channel,
1042                         int window_size)
1043 {
1044   struct CadetExit *pos = cls;
1045
1046   pos->idle = window_size;
1047 }
1048
1049
1050 /**
1051  * We are short on cadet exits, try to open another one.
1052  */
1053 static void
1054 try_open_exit ()
1055 {
1056   struct CadetExit *pos;
1057   uint32_t candidate_count;
1058   uint32_t candidate_selected;
1059   struct GNUNET_HashCode port;
1060
1061   GNUNET_CRYPTO_hash (GNUNET_APPLICATION_PORT_INTERNET_RESOLVER,
1062                       strlen (GNUNET_APPLICATION_PORT_INTERNET_RESOLVER),
1063                       &port);
1064   candidate_count = 0;
1065   for (pos = exit_head; NULL != pos; pos = pos->next)
1066     if (NULL == pos->cadet_channel)
1067       candidate_count++;
1068   if (0 == candidate_count)
1069   {
1070     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1071                 "No DNS exits available yet.\n");
1072     return;
1073   }
1074   candidate_selected = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
1075                                                  candidate_count);
1076   candidate_count = 0;
1077   for (pos = exit_head; NULL != pos; pos = pos->next)
1078     if (NULL == pos->cadet_channel)
1079     {
1080       candidate_count++;
1081       if (candidate_selected < candidate_count)
1082       {
1083         struct GNUNET_MQ_MessageHandler cadet_handlers[] = {
1084           GNUNET_MQ_hd_var_size (dns_response,
1085                                  GNUNET_MESSAGE_TYPE_VPN_DNS_FROM_INTERNET,
1086                                  struct DnsResponseMessage,
1087                                  pos),
1088           GNUNET_MQ_handler_end ()
1089         };
1090
1091
1092         /* move to the head of the DLL */
1093         pos->cadet_channel
1094           = GNUNET_CADET_channel_create (cadet_handle,
1095                                          pos,
1096                                          &pos->peer,
1097                                          &port,
1098                                          &channel_idle_notify_cb,
1099                                          &cadet_channel_end_cb,
1100                                          cadet_handlers);
1101         if (NULL == pos->cadet_channel)
1102         {
1103           GNUNET_break (0);
1104           continue;
1105         }
1106         GNUNET_CONTAINER_DLL_remove (exit_head,
1107                                      exit_tail,
1108                                      pos);
1109         GNUNET_CONTAINER_DLL_insert (exit_head,
1110                                      exit_tail,
1111                                      pos);
1112         dns_exit_available++;
1113         return;
1114       }
1115     }
1116   GNUNET_assert (NULL == exit_head);
1117 }
1118
1119
1120 /**
1121  * Function called whenever we find an advertisement for a
1122  * DNS exit in the DHT.  If we don't have a cadet channel,
1123  * we should build one; otherwise, we should save the
1124  * advertisement for later use.
1125  *
1126  * @param cls closure
1127  * @param exp when will this value expire
1128  * @param key key of the result
1129  * @param get_path peers on reply path (or NULL if not recorded)
1130  *                 [0] = datastore's first neighbor, [length - 1] = local peer
1131  * @param get_path_length number of entries in @a get_path
1132  * @param put_path peers on the PUT path (or NULL if not recorded)
1133  *                 [0] = origin, [length - 1] = datastore
1134  * @param put_path_length number of entries in @a put_path
1135  * @param type type of the result
1136  * @param size number of bytes in @a data
1137  * @param data pointer to the result data
1138  */
1139 static void
1140 handle_dht_result (void *cls,
1141                    struct GNUNET_TIME_Absolute exp,
1142                    const struct GNUNET_HashCode *key,
1143                    const struct GNUNET_PeerIdentity *get_path,
1144                    unsigned int get_path_length,
1145                    const struct GNUNET_PeerIdentity *put_path,
1146                    unsigned int put_path_length,
1147                    enum GNUNET_BLOCK_Type type,
1148                    size_t size, const void *data)
1149 {
1150   const struct GNUNET_DNS_Advertisement *ad;
1151   struct CadetExit *exit;
1152
1153   if (sizeof(struct GNUNET_DNS_Advertisement) != size)
1154   {
1155     GNUNET_break (0);
1156     return;
1157   }
1158   ad = data;
1159   for (exit = exit_head; NULL != exit; exit = exit->next)
1160     if (0 == GNUNET_memcmp (&ad->peer,
1161                             &exit->peer))
1162       break;
1163   if (NULL == exit)
1164   {
1165     exit = GNUNET_new (struct CadetExit);
1166     exit->peer = ad->peer;
1167     /* channel is closed, so insert at the end */
1168     GNUNET_CONTAINER_DLL_insert_tail (exit_head,
1169                                       exit_tail,
1170                                       exit);
1171   }
1172   exit->expiration = GNUNET_TIME_absolute_max (exit->expiration,
1173                                                GNUNET_TIME_absolute_ntoh (
1174                                                  ad->expiration_time));
1175   if (dns_exit_available < MAX_OPEN_TUNNELS)
1176     try_open_exit ();
1177 }
1178
1179
1180 /**
1181  * @brief Main function that will be run by the scheduler.
1182  *
1183  * @param cls closure
1184  * @param args remaining command-line arguments
1185  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1186  * @param cfg_ configuration
1187  */
1188 static void
1189 run (void *cls, char *const *args GNUNET_UNUSED,
1190      const char *cfgfile GNUNET_UNUSED,
1191      const struct GNUNET_CONFIGURATION_Handle *cfg_)
1192 {
1193   struct GNUNET_HashCode dns_key;
1194
1195   cfg = cfg_;
1196   stats = GNUNET_STATISTICS_create ("pt",
1197                                     cfg);
1198   ipv4_pt = GNUNET_CONFIGURATION_get_value_yesno (cfg,
1199                                                   "pt",
1200                                                   "TUNNEL_IPV4");
1201   ipv6_pt = GNUNET_CONFIGURATION_get_value_yesno (cfg,
1202                                                   "pt",
1203                                                   "TUNNEL_IPV6");
1204   dns_channel = GNUNET_CONFIGURATION_get_value_yesno (cfg,
1205                                                       "pt",
1206                                                       "TUNNEL_DNS");
1207   if (! (ipv4_pt || ipv6_pt || dns_channel))
1208   {
1209     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1210                 _ ("No useful service enabled.  Exiting.\n"));
1211     GNUNET_SCHEDULER_shutdown ();
1212     return;
1213   }
1214   GNUNET_SCHEDULER_add_shutdown (&cleanup, cls);
1215   if (ipv4_pt || ipv6_pt)
1216   {
1217     dns_post_handle
1218       = GNUNET_DNS_connect (cfg,
1219                             GNUNET_DNS_FLAG_POST_RESOLUTION,
1220                             &dns_post_request_handler,
1221                             NULL);
1222     if (NULL == dns_post_handle)
1223     {
1224       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1225                   _ ("Failed to connect to %s service.  Exiting.\n"),
1226                   "DNS");
1227       GNUNET_SCHEDULER_shutdown ();
1228       return;
1229     }
1230     vpn_handle = GNUNET_VPN_connect (cfg);
1231     if (NULL == vpn_handle)
1232     {
1233       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1234                   _ ("Failed to connect to %s service.  Exiting.\n"),
1235                   "VPN");
1236       GNUNET_SCHEDULER_shutdown ();
1237       return;
1238     }
1239   }
1240   if (dns_channel)
1241   {
1242     dns_pre_handle
1243       = GNUNET_DNS_connect (cfg,
1244                             GNUNET_DNS_FLAG_PRE_RESOLUTION,
1245                             &dns_pre_request_handler,
1246                             NULL);
1247     if (NULL == dns_pre_handle)
1248     {
1249       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1250                   _ ("Failed to connect to %s service.  Exiting.\n"),
1251                   "DNS");
1252       GNUNET_SCHEDULER_shutdown ();
1253       return;
1254     }
1255     cadet_handle = GNUNET_CADET_connect (cfg);
1256     if (NULL == cadet_handle)
1257     {
1258       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1259                   _ ("Failed to connect to %s service.  Exiting.\n"),
1260                   "CADET");
1261       GNUNET_SCHEDULER_shutdown ();
1262       return;
1263     }
1264     dht = GNUNET_DHT_connect (cfg, 1);
1265     if (NULL == dht)
1266     {
1267       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1268                   _ ("Failed to connect to %s service.  Exiting.\n"),
1269                   "DHT");
1270       GNUNET_SCHEDULER_shutdown ();
1271       return;
1272     }
1273     GNUNET_CRYPTO_hash ("dns",
1274                         strlen ("dns"),
1275                         &dns_key);
1276     dht_get = GNUNET_DHT_get_start (dht,
1277                                     GNUNET_BLOCK_TYPE_DNS,
1278                                     &dns_key,
1279                                     1,
1280                                     GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE,
1281                                     NULL, 0,
1282                                     &handle_dht_result,
1283                                     NULL);
1284   }
1285 }
1286
1287
1288 /**
1289  * The main function
1290  *
1291  * @param argc number of arguments from the command line
1292  * @param argv command line arguments
1293  * @return 0 ok, 1 on error
1294  */
1295 int
1296 main (int argc,
1297       char *const *argv)
1298 {
1299   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
1300     GNUNET_GETOPT_OPTION_END
1301   };
1302   int ret;
1303
1304   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc,
1305                                                  argv,
1306                                                  &argc,
1307                                                  &argv))
1308     return 2;
1309   ret = (GNUNET_OK ==
1310          GNUNET_PROGRAM_run (argc,
1311                              argv,
1312                              "gnunet-daemon-pt",
1313                              gettext_noop (
1314                                "Daemon to run to perform IP protocol translation to GNUnet"),
1315                              options,
1316                              &run,
1317                              NULL))
1318         ? 0
1319         : 1;
1320   GNUNET_free ((void *) argv);
1321   return ret;
1322 }
1323
1324
1325 /* end of gnunet-daemon-pt.c */