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