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