-minor code cleanup
[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 /**
55  * Which group of DNS records are we currently processing?
56  */
57 enum RequestGroup
58 {
59   /**
60    * DNS answers
61    */
62   ANSWERS = 0, 
63
64   /**
65    * DNS authority records
66    */
67   AUTHORITY_RECORDS = 1,
68
69   /**
70    * DNS additional records
71    */
72   ADDITIONAL_RECORDS = 2,
73
74   /**
75    * We're done processing.
76    */
77   END = 3
78 };
79
80
81 /**
82  * Information tracked per DNS reply that we are processing.
83  */
84 struct ReplyContext
85 {
86   /**
87    * Handle to submit the final result.
88    */
89   struct GNUNET_DNS_RequestHandle *rh;
90   
91   /**
92    * DNS packet that is being modified.
93    */
94   struct GNUNET_DNSPARSER_Packet *dns;
95
96   /**
97    * Active redirection request with the VPN.
98    */
99   struct GNUNET_VPN_RedirectionRequest *rr;
100
101   /**
102    * Record for which we have an active redirection request.
103    */
104   struct GNUNET_DNSPARSER_Record *rec;
105
106   /**
107    * Offset in the current record group that is being modified.
108    */
109   unsigned int offset;
110
111   /**
112    * Group that is being modified
113    */
114   enum RequestGroup group;
115   
116 };
117
118
119 /**
120  * State we keep for a request that is going out via MESH.
121  */
122 struct RequestContext
123 {
124   /**
125    * We keep these in a DLL.
126    */
127   struct RequestContext *next;
128
129   /**
130    * We keep these in a DLL.
131    */
132   struct RequestContext *prev;
133
134   /**
135    * Handle for interaction with DNS service.
136    */
137   struct GNUNET_DNS_RequestHandle *rh;
138   
139   /**
140    * Message we're sending out via MESH, allocated at the
141    * end of this struct.
142    */
143   const struct GNUNET_MessageHeader *mesh_message;
144
145   /**
146    * Task used to abort this operation with timeout.
147    */
148   GNUNET_SCHEDULER_TaskIdentifier timeout_task;
149
150   /**
151    * ID of the original DNS request (used to match the reply).
152    */
153   uint16_t dns_id;
154
155   /**
156    * #GNUNET_NO if this request is still in the transmit_queue,
157    * #GNUNET_YES if we are in the receive_queue.
158    */ 
159   int16_t was_transmitted;
160
161 };
162
163
164 /**
165  * The handle to the configuration used throughout the process
166  */
167 static const struct GNUNET_CONFIGURATION_Handle *cfg;
168
169 /**
170  * The handle to the VPN
171  */
172 static struct GNUNET_VPN_Handle *vpn_handle;
173
174 /**
175  * The handle to the MESH service
176  */
177 static struct GNUNET_MESH_Handle *mesh_handle;
178
179 /**
180  * Tunnel we use for DNS requests over MESH.
181  * FIXME: we might want to keep multiple tunnels open
182  * at all times...
183  */
184 static struct GNUNET_MESH_Tunnel *mesh_tunnel;
185
186 /**
187  * Active transmission request with MESH (or NULL).
188  */
189 static struct GNUNET_MESH_TransmitHandle *mesh_th;
190
191 /**
192  * Head of DLL of requests to be transmitted to mesh_tunnel.
193  */
194 static struct RequestContext *transmit_queue_head;
195
196 /**
197  * Tail of DLL of requests to be transmitted to mesh_tunnel.
198  */
199 static struct RequestContext *transmit_queue_tail;
200
201 /**
202  * Head of DLL of requests waiting for a response.
203  */
204 static struct RequestContext *receive_queue_head;
205
206 /**
207  * Tail of DLL of requests waiting for a response.
208  */
209 static struct RequestContext *receive_queue_tail;
210
211 /**
212  * Statistics.
213  */
214 static struct GNUNET_STATISTICS_Handle *stats;
215
216 /**
217  * The handle to DNS post-resolution modifications.
218  */
219 static struct GNUNET_DNS_Handle *dns_post_handle;
220
221 /**
222  * The handle to DNS pre-resolution modifications.
223  */
224 static struct GNUNET_DNS_Handle *dns_pre_handle;
225
226 /**
227  * Handle to access the DHT.
228  */
229 static struct GNUNET_DHT_Handle *dht;
230
231 /**
232  * Our DHT GET operation to find DNS exits.
233  */
234 static struct GNUNET_DHT_GetHandle *dht_get;
235
236 /**
237  * Are we doing IPv4-pt?
238  */
239 static int ipv4_pt;
240
241 /**
242  * Are we doing IPv6-pt?
243  */
244 static int ipv6_pt;
245
246 /**
247  * Are we tunneling DNS queries?
248  */
249 static int dns_tunnel;
250
251 /**
252  * Number of DNS exit peers we currently have in the mesh tunnel.
253  * Used to see if using the mesh tunnel makes any sense right now.
254  */
255 static unsigned int dns_exit_available;
256
257
258 /**
259  * We're done modifying all records in the response.  Submit the reply
260  * and free the resources of the rc.
261  *
262  * @param rc context to process
263  */
264 static void
265 finish_request (struct ReplyContext *rc)
266 {
267   char *buf;
268   size_t buf_len;
269
270   if (GNUNET_SYSERR ==
271       GNUNET_DNSPARSER_pack (rc->dns,
272                              MAX_DNS_SIZE,
273                              &buf,
274                              &buf_len))
275   {
276     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
277                 _("Failed to pack DNS request.  Dropping.\n"));
278     GNUNET_DNS_request_drop (rc->rh);
279   }
280   else
281   {
282     GNUNET_STATISTICS_update (stats,
283                               gettext_noop ("# DNS requests mapped to VPN"),
284                               1, GNUNET_NO);
285     GNUNET_DNS_request_answer (rc->rh,
286                                buf_len, buf);
287     GNUNET_free (buf);
288   }
289   GNUNET_DNSPARSER_free_packet (rc->dns);
290   GNUNET_free (rc);
291 }
292
293
294 /**
295  * Process the next record of the given request context.
296  * When done, submit the reply and free the resources of
297  * the rc.
298  *
299  * @param rc context to process
300  */
301 static void
302 submit_request (struct ReplyContext *rc);
303
304
305 /**
306  * Callback invoked from the VPN service once a redirection is
307  * available.  Provides the IP address that can now be used to
308  * reach the requested destination.  We substitute the active
309  * record and then continue with 'submit_request' to look at
310  * the other records.
311  *
312  * @param cls our `struct ReplyContext`
313  * @param af address family, AF_INET or AF_INET6; AF_UNSPEC on error;
314  *                will match 'result_af' from the request
315  * @param address IP address (struct in_addr or struct in_addr6, depending on 'af')
316  *                that the VPN allocated for the redirection;
317  *                traffic to this IP will now be redirected to the 
318  *                specified target peer; NULL on error
319  */
320 static void
321 vpn_allocation_callback (void *cls,
322                          int af,
323                          const void *address)
324 {
325   struct ReplyContext *rc = cls;
326
327   rc->rr = NULL;
328   if (af == AF_UNSPEC)
329   {
330     GNUNET_DNS_request_drop (rc->rh);
331     GNUNET_DNSPARSER_free_packet (rc->dns);
332     GNUNET_free (rc);
333     return;
334   }
335   GNUNET_STATISTICS_update (stats,
336                             gettext_noop ("# DNS records modified"),
337                             1, GNUNET_NO);
338   switch (rc->rec->type)
339   {
340   case GNUNET_DNSPARSER_TYPE_A:
341     GNUNET_assert (AF_INET == af);
342     memcpy (rc->rec->data.raw.data, address, sizeof (struct in_addr));
343     break;
344   case GNUNET_DNSPARSER_TYPE_AAAA:
345     GNUNET_assert (AF_INET6 == af);
346     memcpy (rc->rec->data.raw.data, address, sizeof (struct in6_addr));
347     break;
348   default:
349     GNUNET_assert (0);
350     return;
351   }
352   rc->rec = NULL;
353   submit_request (rc);
354 }
355
356
357 /**
358  * Modify the given DNS record by asking VPN to create a tunnel
359  * to the given address.  When done, continue with submitting
360  * other records from the request context ('submit_request' is
361  * our continuation).
362  *
363  * @param rc context to process
364  * @param rec record to modify
365  */
366 static void
367 modify_address (struct ReplyContext *rc,
368                 struct GNUNET_DNSPARSER_Record *rec)
369 {
370   int af;
371
372   switch (rec->type)
373   {
374   case GNUNET_DNSPARSER_TYPE_A:
375     af = AF_INET;
376     GNUNET_assert (rec->data.raw.data_len == sizeof (struct in_addr));
377     break;
378   case GNUNET_DNSPARSER_TYPE_AAAA:
379     af = AF_INET6;
380     GNUNET_assert (rec->data.raw.data_len == sizeof (struct in6_addr));
381     break;
382   default:
383     GNUNET_assert (0);
384     return;
385   }
386   rc->rec = rec;
387   rc->rr = GNUNET_VPN_redirect_to_ip (vpn_handle,
388                                       af, af,
389                                       rec->data.raw.data,
390                                       GNUNET_TIME_relative_to_absolute (TIMEOUT),
391                                       &vpn_allocation_callback,
392                                       rc);
393 }
394
395
396 /**
397  * Process the next record of the given request context.
398  * When done, submit the reply and free the resources of
399  * the rc.
400  *
401  * @param rc context to process
402  */
403 static void
404 submit_request (struct ReplyContext *rc)
405 {
406   struct GNUNET_DNSPARSER_Record *ra;
407   unsigned int ra_len;
408   unsigned int i;
409
410   while (1)
411   {
412     switch (rc->group)
413     {
414     case ANSWERS:
415       ra = rc->dns->answers;
416       ra_len = rc->dns->num_answers;
417       break;
418     case AUTHORITY_RECORDS:
419       ra = rc->dns->authority_records;
420       ra_len = rc->dns->num_authority_records;
421       break;
422     case ADDITIONAL_RECORDS:
423       ra = rc->dns->additional_records;
424       ra_len = rc->dns->num_additional_records;
425       break;
426     case END:
427       finish_request (rc);
428       return;
429     default:
430       GNUNET_assert (0);      
431     }
432     for (i=rc->offset;i<ra_len;i++)
433     {
434       switch (ra[i].type)
435       {
436       case GNUNET_DNSPARSER_TYPE_A:
437         if (ipv4_pt)
438         {
439           rc->offset = i + 1;
440           modify_address (rc, &ra[i]);
441           return;
442         }
443         break;
444       case GNUNET_DNSPARSER_TYPE_AAAA:
445         if (ipv6_pt)
446         {
447           rc->offset = i + 1;
448           modify_address (rc, &ra[i]);
449           return;
450         }
451         break;
452       }
453     }
454     rc->group++;
455   }
456 }
457
458
459 /**
460  * Test if any of the given records need protocol-translation work.
461  *
462  * @param ra array of records
463  * @param ra_len number of entries in @a ra
464  * @return #GNUNET_YES if any of the given records require protocol-translation
465  */
466 static int
467 work_test (const struct GNUNET_DNSPARSER_Record *ra,
468            unsigned int ra_len)
469 {
470   unsigned int i;
471
472   for (i=0;i<ra_len;i++)
473   {
474     switch (ra[i].type)
475     {
476     case GNUNET_DNSPARSER_TYPE_A:
477       if (ipv4_pt)
478         return GNUNET_YES;
479       break;
480     case GNUNET_DNSPARSER_TYPE_AAAA:
481       if (ipv6_pt)
482         return GNUNET_YES;
483       break;
484     }
485   }
486   return GNUNET_NO;
487 }
488
489
490 /**
491  * This function is called AFTER we got an IP address for a 
492  * DNS request.  Now, the PT daemon has the chance to substitute
493  * the IP address with one from the VPN range to tunnel requests
494  * destined for this IP address via VPN and MESH.
495  *
496  * @param cls closure
497  * @param rh request handle to user for reply
498  * @param request_length number of bytes in request
499  * @param request udp payload of the DNS request
500  */
501 static void 
502 dns_post_request_handler (void *cls,
503                           struct GNUNET_DNS_RequestHandle *rh,
504                           size_t request_length,
505                           const char *request)
506 {
507   struct GNUNET_DNSPARSER_Packet *dns;
508   struct ReplyContext *rc;
509   int work;
510
511   GNUNET_STATISTICS_update (stats,
512                             gettext_noop ("# DNS replies intercepted"),
513                             1, GNUNET_NO);
514   dns = GNUNET_DNSPARSER_parse (request, request_length);
515   if (NULL == dns)
516   {
517     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
518                 _("Failed to parse DNS request.  Dropping.\n"));
519     GNUNET_DNS_request_drop (rh);
520     return;
521   }
522   work = GNUNET_NO;
523   work |= work_test (dns->answers, dns->num_answers);
524   work |= work_test (dns->authority_records, dns->num_authority_records);
525   work |= work_test (dns->additional_records, dns->num_additional_records);
526   if (! work)
527   {
528     GNUNET_DNS_request_forward (rh);
529     GNUNET_DNSPARSER_free_packet (dns);
530     return;
531   }
532   rc = GNUNET_new (struct ReplyContext);
533   rc->rh = rh;
534   rc->dns = dns;
535   rc->offset = 0;
536   rc->group = ANSWERS;
537   submit_request (rc);
538 }
539
540
541 /**
542  * Transmit a DNS request via MESH and move the request
543  * handle to the receive queue.
544  *
545  * @param cls NULL
546  * @param size number of bytes available in buf
547  * @param buf where to copy the message
548  * @return number of bytes written to buf
549  */
550 static size_t
551 transmit_dns_request_to_mesh (void *cls,
552                               size_t size,
553                               void *buf)
554 {
555   struct RequestContext *rc;
556   size_t mlen;
557
558   mesh_th = NULL;
559   if (NULL == (rc = transmit_queue_head))
560     return 0;
561   mlen = ntohs (rc->mesh_message->size);
562   if (mlen > size)
563   {
564     mesh_th = GNUNET_MESH_notify_transmit_ready (mesh_tunnel,
565                                                  GNUNET_NO,
566                                                  TIMEOUT,
567                                                  mlen,
568                                                  &transmit_dns_request_to_mesh,
569                                                  NULL);
570     return 0;
571   }
572   GNUNET_assert (GNUNET_NO == rc->was_transmitted);
573   memcpy (buf, rc->mesh_message, mlen);
574   GNUNET_CONTAINER_DLL_remove (transmit_queue_head,
575                                transmit_queue_tail,
576                                rc);
577   rc->was_transmitted = GNUNET_YES;
578   GNUNET_CONTAINER_DLL_insert (receive_queue_head,
579                                receive_queue_tail,
580                                rc);
581   rc = transmit_queue_head;
582   if (NULL != rc)
583     mesh_th = GNUNET_MESH_notify_transmit_ready (mesh_tunnel,
584                                                  GNUNET_NO,
585                                                  TIMEOUT,
586                                                  ntohs (rc->mesh_message->size),
587                                                  &transmit_dns_request_to_mesh,
588                                                  NULL);
589   return mlen;
590 }
591
592
593 /**
594  * Task run if the time to answer a DNS request via MESH is over.
595  *
596  * @param cls the 'struct RequestContext' to abort
597  * @param tc scheduler context
598  */
599 static void
600 timeout_request (void *cls,
601                  const struct GNUNET_SCHEDULER_TaskContext *tc)
602 {
603   struct RequestContext *rc = cls;
604   
605   if (rc->was_transmitted)
606     GNUNET_CONTAINER_DLL_remove (receive_queue_head,
607                                  receive_queue_tail,
608                                  rc);
609   else
610     GNUNET_CONTAINER_DLL_remove (transmit_queue_head,
611                                  transmit_queue_tail,
612                                  rc);
613   GNUNET_STATISTICS_update (stats,
614                             gettext_noop ("# DNS requests dropped (timeout)"),
615                             1, GNUNET_NO);
616   GNUNET_DNS_request_drop (rc->rh);
617   GNUNET_free (rc);
618 }
619
620
621 /**
622  * This function is called *before* the DNS request has been 
623  * given to a "local" DNS resolver.  Tunneling for DNS requests
624  * was enabled, so we now need to send the request via some MESH
625  * tunnel to a DNS EXIT for resolution.
626  *
627  * @param cls closure
628  * @param rh request handle to user for reply
629  * @param request_length number of bytes in request
630  * @param request udp payload of the DNS request
631  */
632 static void 
633 dns_pre_request_handler (void *cls,
634                          struct GNUNET_DNS_RequestHandle *rh,
635                          size_t request_length,
636                          const char *request)
637 {
638   struct RequestContext *rc;
639   size_t mlen;
640   struct GNUNET_MessageHeader hdr;
641   struct GNUNET_TUN_DnsHeader dns;
642
643   GNUNET_STATISTICS_update (stats,
644                             gettext_noop ("# DNS requests intercepted"),
645                             1, GNUNET_NO);
646   if (0 == dns_exit_available)
647   {
648     GNUNET_STATISTICS_update (stats,
649                               gettext_noop ("# DNS requests dropped (DNS mesh tunnel down)"),
650                               1, GNUNET_NO);
651     GNUNET_DNS_request_drop (rh);
652     return;
653   }
654   if (request_length < sizeof (dns))
655   {
656     GNUNET_STATISTICS_update (stats,
657                               gettext_noop ("# DNS requests dropped (malformed)"),
658                               1, GNUNET_NO);
659     GNUNET_DNS_request_drop (rh);
660     return;
661   }
662   memcpy (&dns, request, sizeof (dns));
663   GNUNET_assert (NULL != mesh_tunnel);
664   mlen = sizeof (struct GNUNET_MessageHeader) + request_length;
665   rc = GNUNET_malloc (sizeof (struct RequestContext) + mlen);
666   rc->rh = rh;
667   rc->mesh_message = (const struct GNUNET_MessageHeader*) &rc[1];
668   rc->timeout_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT,
669                                                    &timeout_request,
670                                                    rc);
671   rc->dns_id = dns.id;
672   hdr.type = htons (GNUNET_MESSAGE_TYPE_VPN_DNS_TO_INTERNET);
673   hdr.size = htons (mlen);
674   memcpy (&rc[1], &hdr, sizeof (struct GNUNET_MessageHeader));
675   memcpy (&(((char*)&rc[1])[sizeof (struct GNUNET_MessageHeader)]),
676           request,
677           request_length);
678   GNUNET_CONTAINER_DLL_insert_tail (transmit_queue_head,
679                                     transmit_queue_tail,
680                                     rc);
681   if (NULL == mesh_th)
682     mesh_th = GNUNET_MESH_notify_transmit_ready (mesh_tunnel,
683                                                  GNUNET_NO,
684                                                  TIMEOUT,
685                                                  mlen,
686                                                  &transmit_dns_request_to_mesh,
687                                                  NULL);
688 }
689
690
691 /**
692  * Process a request via mesh to perform a DNS query.
693  *
694  * @param cls closure, NULL
695  * @param tunnel connection to the other end
696  * @param tunnel_ctx FIXME
697  * @param message the actual message
698  * @return #GNUNET_OK to keep the connection open,
699  *         #GNUNET_SYSERR to close it (signal serious error)
700  */
701 static int
702 receive_dns_response (void *cls GNUNET_UNUSED, struct GNUNET_MESH_Tunnel *tunnel,
703                       void **tunnel_ctx,
704                       const struct GNUNET_MessageHeader *message)
705 {
706   struct GNUNET_TUN_DnsHeader dns;
707   size_t mlen;
708   struct RequestContext *rc;
709
710   mlen = ntohs (message->size);
711   mlen -= sizeof (struct GNUNET_MessageHeader);
712   if (mlen < sizeof (struct GNUNET_TUN_DnsHeader))
713   {
714     GNUNET_break_op (0);
715     return GNUNET_SYSERR;
716   }
717   memcpy (&dns, &message[1], sizeof (dns));
718   for (rc = receive_queue_head; NULL != rc; rc = rc->next)
719   {
720     GNUNET_assert (GNUNET_YES == rc->was_transmitted);
721     if (dns.id == rc->dns_id)
722     {
723       GNUNET_STATISTICS_update (stats,
724                                 gettext_noop ("# DNS replies received"),
725                                 1, GNUNET_NO);
726       GNUNET_DNS_request_answer (rc->rh,
727                                  mlen,
728                                  (const void*) &message[1]);
729       GNUNET_CONTAINER_DLL_remove (receive_queue_head,
730                                    receive_queue_tail,
731                                    rc);
732       GNUNET_SCHEDULER_cancel (rc->timeout_task);
733       GNUNET_free (rc);
734       return GNUNET_OK;      
735     }
736   }
737   GNUNET_STATISTICS_update (stats,
738                             gettext_noop ("# DNS replies dropped (too late?)"),
739                             1, GNUNET_NO);
740   return GNUNET_OK;
741 }
742
743
744 /**
745  * The MESH DNS tunnel went down.  Abort all pending DNS
746  * requests (we're unlikely to get an answer in time).
747  */ 
748 static void
749 abort_all_requests ()
750 {
751   struct RequestContext *rc;
752
753   while (NULL != (rc = receive_queue_head))
754   {
755     GNUNET_STATISTICS_update (stats,
756                               gettext_noop ("# DNS requests aborted (tunnel down)"),
757                               1, GNUNET_NO);
758     GNUNET_CONTAINER_DLL_remove (receive_queue_head,
759                                  receive_queue_tail,
760                                  rc);
761     GNUNET_DNS_request_drop (rc->rh);
762     GNUNET_SCHEDULER_cancel (rc->timeout_task);
763     GNUNET_free (rc);    
764   }
765   while (NULL != (rc = transmit_queue_head))
766   {
767     GNUNET_STATISTICS_update (stats,
768                               gettext_noop ("# DNS requests aborted (tunnel down)"),
769                               1, GNUNET_NO);
770     GNUNET_CONTAINER_DLL_remove (transmit_queue_head,
771                                  transmit_queue_tail,
772                                  rc);
773     GNUNET_DNS_request_drop (rc->rh);
774     GNUNET_SCHEDULER_cancel (rc->timeout_task);
775     GNUNET_free (rc);    
776   }
777 }
778
779
780 /**
781  * Function scheduled as very last function, cleans up after us
782  *
783  * @param cls closure, NULL
784  * @param tskctx scheduler context, unused
785  */
786 static void
787 cleanup (void *cls,
788          const struct GNUNET_SCHEDULER_TaskContext *tskctx)
789 {
790   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
791               "Protocol translation daemon is shutting down now\n");
792   if (NULL != vpn_handle)
793   {
794     GNUNET_VPN_disconnect (vpn_handle);
795     vpn_handle = NULL;
796   }
797   if (NULL != mesh_th)
798   {
799     GNUNET_MESH_notify_transmit_ready_cancel (mesh_th);
800     mesh_th = NULL;
801   }
802   if (NULL != mesh_tunnel)
803   {
804     GNUNET_MESH_tunnel_destroy (mesh_tunnel);
805     mesh_tunnel = NULL;
806   }
807   if (NULL != mesh_handle)
808   {
809     GNUNET_MESH_disconnect (mesh_handle);
810     mesh_handle = NULL;
811   }
812   abort_all_requests ();
813   if (NULL != dns_post_handle)
814   {
815     GNUNET_DNS_disconnect (dns_post_handle);
816     dns_post_handle = NULL;
817   }
818   if (NULL != dns_pre_handle)
819   {
820     GNUNET_DNS_disconnect (dns_pre_handle);
821     dns_pre_handle = NULL;
822   }
823   if (NULL != stats)
824   {
825     GNUNET_STATISTICS_destroy (stats, GNUNET_YES);
826     stats = NULL;
827   }
828   if (NULL != dht_get)
829   {
830     GNUNET_DHT_get_stop (dht_get);
831     dht_get = NULL;
832   }
833   if (NULL != dht)
834   {
835     GNUNET_DHT_disconnect (dht);
836     dht = NULL;
837   }
838 }
839
840
841
842 /**
843  * Function called whenever a tunnel is destroyed.  Should clean up
844  * the associated state and attempt to build a new one.
845  * 
846  * It must NOT call #GNUNET_MESH_tunnel_destroy on the tunnel.
847  *
848  * @param cls closure (set from #GNUNET_MESH_connect)
849  * @param tunnel connection to the other end (henceforth invalid)
850  * @param tunnel_ctx place where local state associated
851  *                   with the tunnel is stored
852  */
853 static void
854 mesh_tunnel_end_cb (void *cls,
855                     const struct GNUNET_MESH_Tunnel *tunnel, 
856                     void *tunnel_ctx)
857 {
858   // FIXME: do cleanup here!
859 }
860
861
862 /**
863  * Function called whenever we find an advertisement for a 
864  * DNS exit in the DHT.  If we don't have a mesh tunnel,
865  * we should build one; otherwise, we should save the
866  * advertisement for later use.
867  *
868  * @param cls closure
869  * @param exp when will this value expire
870  * @param key key of the result
871  * @param get_path peers on reply path (or NULL if not recorded)
872  *                 [0] = datastore's first neighbor, [length - 1] = local peer
873  * @param get_path_length number of entries in @a get_path
874  * @param put_path peers on the PUT path (or NULL if not recorded)
875  *                 [0] = origin, [length - 1] = datastore
876  * @param put_path_length number of entries in @a put_path
877  * @param type type of the result
878  * @param size number of bytes in @a data
879  * @param data pointer to the result data
880  */
881 static void
882 handle_dht_result (void *cls,
883                    struct GNUNET_TIME_Absolute exp,
884                    const struct GNUNET_HashCode *key,
885                    const struct GNUNET_PeerIdentity *get_path, 
886                    unsigned int get_path_length,
887                    const struct GNUNET_PeerIdentity *put_path,
888                    unsigned int put_path_length,
889                    enum GNUNET_BLOCK_Type type,
890                    size_t size, const void *data)
891 {
892   const struct GNUNET_DNS_Advertisement *ad;
893   struct GNUNET_PeerIdentity pid;
894
895   if (sizeof (struct GNUNET_DNS_Advertisement) != size)
896   {
897     GNUNET_break (0);
898     return;
899   }
900   ad = data;
901   GNUNET_CRYPTO_hash (&ad->peer,
902                       sizeof (struct GNUNET_CRYPTO_EccPublicSignKey),
903                       &pid.hashPubKey);
904   /* FIXME: decide between creating more mesh tunnels and
905      just remembering the peer ID */
906   mesh_tunnel = GNUNET_MESH_tunnel_create (mesh_handle,
907                                            NULL /* FIXME: tunnel ctx */,
908                                            &pid,
909                                            GNUNET_APPLICATION_TYPE_INTERNET_RESOLVER,
910                                            GNUNET_YES /* no buffer */,
911                                            GNUNET_NO /* reliable */);
912
913 }
914
915
916 /**
917  * @brief Main function that will be run by the scheduler.
918  *
919  * @param cls closure
920  * @param args remaining command-line arguments
921  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
922  * @param cfg_ configuration
923  */
924 static void
925 run (void *cls, char *const *args GNUNET_UNUSED,
926      const char *cfgfile GNUNET_UNUSED,
927      const struct GNUNET_CONFIGURATION_Handle *cfg_)
928 {
929   struct GNUNET_HashCode dns_key;
930
931   cfg = cfg_;
932   stats = GNUNET_STATISTICS_create ("pt", cfg);
933   ipv4_pt = GNUNET_CONFIGURATION_get_value_yesno (cfg, "pt", "TUNNEL_IPV4");
934   ipv6_pt = GNUNET_CONFIGURATION_get_value_yesno (cfg, "pt", "TUNNEL_IPV6"); 
935   dns_tunnel = GNUNET_CONFIGURATION_get_value_yesno (cfg, "pt", "TUNNEL_DNS"); 
936   if (! (ipv4_pt || ipv6_pt || dns_tunnel))
937   {
938     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
939                 _("No useful service enabled.  Exiting.\n"));
940     GNUNET_SCHEDULER_shutdown ();
941     return;    
942   }
943   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &cleanup, cls);
944   if (ipv4_pt || ipv6_pt)
945   {
946     dns_post_handle 
947       = GNUNET_DNS_connect (cfg, 
948                             GNUNET_DNS_FLAG_POST_RESOLUTION,
949                             &dns_post_request_handler, NULL);
950     if (NULL == dns_post_handle)       
951     {
952       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
953                   _("Failed to connect to %s service.  Exiting.\n"),
954                   "DNS");
955       GNUNET_SCHEDULER_shutdown ();
956       return;
957     }
958     vpn_handle = GNUNET_VPN_connect (cfg);
959     if (NULL == vpn_handle)
960     {
961       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
962                   _("Failed to connect to %s service.  Exiting.\n"),
963                   "VPN");
964       GNUNET_SCHEDULER_shutdown ();
965       return;
966     }
967   }
968   if (dns_tunnel)
969   {
970     static struct GNUNET_MESH_MessageHandler mesh_handlers[] = {
971       {&receive_dns_response, GNUNET_MESSAGE_TYPE_VPN_DNS_FROM_INTERNET, 0},
972       {NULL, 0, 0}
973     };
974
975     dns_pre_handle 
976       = GNUNET_DNS_connect (cfg, 
977                             GNUNET_DNS_FLAG_PRE_RESOLUTION,
978                             &dns_pre_request_handler, NULL);
979     if (NULL == dns_pre_handle)       
980     {
981       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
982                   _("Failed to connect to %s service.  Exiting.\n"),
983                   "DNS");
984       GNUNET_SCHEDULER_shutdown ();
985       return;
986     }
987     mesh_handle = GNUNET_MESH_connect (cfg, NULL, NULL,
988                                        &mesh_tunnel_end_cb,
989                                        mesh_handlers, NULL);
990     if (NULL == mesh_handle)
991     {
992       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
993                   _("Failed to connect to %s service.  Exiting.\n"),
994                   "MESH");
995       GNUNET_SCHEDULER_shutdown ();
996       return;
997     }
998     dht = GNUNET_DHT_connect (cfg, 1);
999     if (NULL == dht)
1000     {
1001       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1002                   _("Failed to connect to %s service.  Exiting.\n"),
1003                   "DHT");
1004       GNUNET_SCHEDULER_shutdown ();
1005       return;
1006     }
1007     GNUNET_CRYPTO_hash ("dns", strlen ("dns"), &dns_key);
1008     dht_get = GNUNET_DHT_get_start (dht,
1009                                     GNUNET_BLOCK_TYPE_DNS,
1010                                     &dns_key,
1011                                     1,
1012                                     GNUNET_DHT_RO_DEMULTIPLEX_EVERYWHERE,
1013                                     NULL, 0,
1014                                     &handle_dht_result, NULL);
1015   }
1016 }
1017
1018
1019 /**
1020  * The main function
1021  *
1022  * @param argc number of arguments from the command line
1023  * @param argv command line arguments
1024  * @return 0 ok, 1 on error
1025  */
1026 int
1027 main (int argc, char *const *argv)
1028 {
1029   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
1030     GNUNET_GETOPT_OPTION_END
1031   };
1032   int ret;
1033
1034   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
1035     return 2;
1036   ret = (GNUNET_OK ==
1037          GNUNET_PROGRAM_run (argc, argv, "gnunet-daemon-pt",
1038                              gettext_noop
1039                              ("Daemon to run to perform IP protocol translation to GNUnet"),
1040                              options, &run, NULL)) ? 0 : 1;
1041   GNUNET_free ((void*) argv);
1042   return ret;
1043 }
1044
1045
1046 /* end of gnunet-daemon-pt.c */