-test now passes
[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     return;
518   }
519   rc = GNUNET_malloc (sizeof (struct ReplyContext));
520   rc->rh = rh;
521   rc->dns = dns;
522   rc->offset = 0;
523   rc->group = ANSWERS;
524   submit_request (rc);
525 }
526
527
528 /**
529  * Transmit a DNS request via MESH and move the request
530  * handle to the receive queue.
531  *
532  * @param cls NULL
533  * @param size number of bytes available in buf
534  * @param buf where to copy the message
535  * @return number of bytes written to buf
536  */
537 static size_t
538 transmit_dns_request_to_mesh (void *cls,
539                               size_t size,
540                               void *buf)
541 {
542   struct RequestContext *rc;
543   size_t mlen;
544
545   mesh_th = NULL;
546   if (NULL == (rc = transmit_queue_head))
547     return 0;
548   mlen = ntohs (rc->mesh_message->size);
549   if (mlen > size)
550   {    
551     mesh_th = GNUNET_MESH_notify_transmit_ready (mesh_tunnel,
552                                                  GNUNET_NO, 0,
553                                                  TIMEOUT,
554                                                  NULL, mlen,
555                                                  &transmit_dns_request_to_mesh,
556                                                  NULL);
557     return 0;
558   }
559   GNUNET_assert (GNUNET_NO == rc->was_transmitted);
560   memcpy (buf, rc->mesh_message, mlen);
561   GNUNET_CONTAINER_DLL_remove (transmit_queue_head,
562                                transmit_queue_tail,
563                                rc);
564   rc->was_transmitted = GNUNET_YES;
565   GNUNET_CONTAINER_DLL_insert (receive_queue_head,
566                                receive_queue_tail,
567                                rc);
568   rc = transmit_queue_head;
569   if (NULL != rc)
570     mesh_th = GNUNET_MESH_notify_transmit_ready (mesh_tunnel,
571                                                  GNUNET_NO, 0,
572                                                  TIMEOUT,
573                                                  NULL, ntohs (rc->mesh_message->size),
574                                                  &transmit_dns_request_to_mesh,
575                                                  NULL);
576   return mlen;
577 }
578
579
580 /**
581  * Task run if the time to answer a DNS request via MESH is over.
582  *
583  * @param cls the 'struct RequestContext' to abort
584  * @param tc scheduler context
585  */
586 static void
587 timeout_request (void *cls,
588                  const struct GNUNET_SCHEDULER_TaskContext *tc)
589 {
590   struct RequestContext *rc = cls;
591   
592   if (rc->was_transmitted)
593     GNUNET_CONTAINER_DLL_remove (receive_queue_head,
594                                  receive_queue_tail,
595                                  rc);
596   else
597     GNUNET_CONTAINER_DLL_remove (transmit_queue_head,
598                                  transmit_queue_tail,
599                                  rc);
600   GNUNET_STATISTICS_update (stats,
601                             gettext_noop ("# DNS requests dropped (timeout)"),
602                             1, GNUNET_NO);
603   GNUNET_DNS_request_drop (rc->rh);
604   GNUNET_free (rc);
605 }
606
607
608 /**
609  * This function is called *before* the DNS request has been 
610  * given to a "local" DNS resolver.  Tunneling for DNS requests
611  * was enabled, so we now need to send the request via some MESH
612  * tunnel to a DNS EXIT for resolution.
613  *
614  * @param cls closure
615  * @param rh request handle to user for reply
616  * @param request_length number of bytes in request
617  * @param request udp payload of the DNS request
618  */
619 static void 
620 dns_pre_request_handler (void *cls,
621                          struct GNUNET_DNS_RequestHandle *rh,
622                          size_t request_length,
623                          const char *request)
624 {
625   struct RequestContext *rc;
626   size_t mlen;
627   struct GNUNET_MessageHeader hdr;
628   struct GNUNET_TUN_DnsHeader dns;
629
630   GNUNET_STATISTICS_update (stats,
631                             gettext_noop ("# DNS requests intercepted"),
632                             1, GNUNET_NO);
633   if (0 == dns_exit_available)
634   {
635     GNUNET_STATISTICS_update (stats,
636                               gettext_noop ("# DNS requests dropped (DNS mesh tunnel down)"),
637                               1, GNUNET_NO);
638     GNUNET_DNS_request_drop (rh);
639     return;
640   }
641   if (request_length < sizeof (dns))
642   {
643     GNUNET_STATISTICS_update (stats,
644                               gettext_noop ("# DNS requests dropped (malformed)"),
645                               1, GNUNET_NO);
646     GNUNET_DNS_request_drop (rh);
647     return;
648   }
649   memcpy (&dns, request, sizeof (dns));
650   GNUNET_assert (NULL != mesh_tunnel);
651   mlen = sizeof (struct GNUNET_MessageHeader) + request_length;
652   rc = GNUNET_malloc (sizeof (struct RequestContext) + mlen);
653   rc->rh = rh;
654   rc->mesh_message = (const struct GNUNET_MessageHeader*) &rc[1];
655   rc->timeout_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT,
656                                                    &timeout_request,
657                                                    rc);
658   rc->dns_id = dns.id;
659   hdr.type = htons (GNUNET_MESSAGE_TYPE_VPN_DNS_TO_INTERNET);
660   hdr.size = htons (mlen);
661   memcpy (&rc[1], &hdr, sizeof (struct GNUNET_MessageHeader));
662   memcpy (&(((char*)&rc[1])[sizeof (struct GNUNET_MessageHeader)]),
663           request,
664           request_length);
665   GNUNET_CONTAINER_DLL_insert_tail (transmit_queue_head,
666                                     transmit_queue_tail,
667                                     rc);
668   if (NULL == mesh_th)
669     mesh_th = GNUNET_MESH_notify_transmit_ready (mesh_tunnel,
670                                                  GNUNET_NO, 0,
671                                                  TIMEOUT,
672                                                  NULL, mlen,
673                                                  &transmit_dns_request_to_mesh,
674                                                  NULL);
675 }
676
677
678 /**
679  * Process a request via mesh to perform a DNS query.
680  *
681  * @param cls closure, NULL
682  * @param tunnel connection to the other end
683  * @param tunnel_ctx pointer to our 'struct TunnelState *'
684  * @param sender who sent the message
685  * @param message the actual message
686  * @param atsi performance data for the connection
687  * @return GNUNET_OK to keep the connection open,
688  *         GNUNET_SYSERR to close it (signal serious error)
689  */
690 static int
691 receive_dns_response (void *cls GNUNET_UNUSED, struct GNUNET_MESH_Tunnel *tunnel,
692                       void **tunnel_ctx,
693                       const struct GNUNET_PeerIdentity *sender GNUNET_UNUSED,
694                       const struct GNUNET_MessageHeader *message,
695                       const struct GNUNET_ATS_Information *atsi GNUNET_UNUSED)
696 {
697   struct GNUNET_TUN_DnsHeader dns;
698   size_t mlen;
699   struct RequestContext *rc;
700
701   mlen = ntohs (message->size);
702   mlen -= sizeof (struct GNUNET_MessageHeader);
703   if (mlen < sizeof (struct GNUNET_TUN_DnsHeader))
704   {
705     GNUNET_break_op (0);
706     return GNUNET_SYSERR;
707   }
708   memcpy (&dns, &message[1], sizeof (dns));
709   for (rc = receive_queue_head; NULL != rc; rc = rc->next)
710   {
711     GNUNET_assert (GNUNET_YES == rc->was_transmitted);
712     if (dns.id == rc->dns_id)
713     {
714       GNUNET_STATISTICS_update (stats,
715                                 gettext_noop ("# DNS replies received"),
716                                 1, GNUNET_NO);
717       GNUNET_DNS_request_answer (rc->rh,
718                                  mlen,
719                                  (const void*) &message[1]);
720       GNUNET_CONTAINER_DLL_remove (receive_queue_head,
721                                    receive_queue_tail,
722                                    rc);
723       GNUNET_SCHEDULER_cancel (rc->timeout_task);
724       GNUNET_free (rc);
725       return GNUNET_OK;      
726     }
727   }
728   GNUNET_STATISTICS_update (stats,
729                             gettext_noop ("# DNS replies dropped (too late?)"),
730                             1, GNUNET_NO);
731   return GNUNET_OK;
732 }
733
734
735 /**
736  * The MESH DNS tunnel went down.  Abort all pending DNS
737  * requests (we're unlikely to get an answer in time).
738  */ 
739 static void
740 abort_all_requests ()
741 {
742   struct RequestContext *rc;
743
744   while (NULL != (rc = receive_queue_head))
745   {
746     GNUNET_STATISTICS_update (stats,
747                               gettext_noop ("# DNS requests aborted (tunnel down)"),
748                               1, GNUNET_NO);
749     GNUNET_CONTAINER_DLL_remove (receive_queue_head,
750                                  receive_queue_tail,
751                                  rc);
752     GNUNET_DNS_request_drop (rc->rh);
753     GNUNET_SCHEDULER_cancel (rc->timeout_task);
754     GNUNET_free (rc);    
755   }
756   while (NULL != (rc = transmit_queue_head))
757   {
758     GNUNET_STATISTICS_update (stats,
759                               gettext_noop ("# DNS requests aborted (tunnel down)"),
760                               1, GNUNET_NO);
761     GNUNET_CONTAINER_DLL_remove (transmit_queue_head,
762                                  transmit_queue_tail,
763                                  rc);
764     GNUNET_DNS_request_drop (rc->rh);
765     GNUNET_SCHEDULER_cancel (rc->timeout_task);
766     GNUNET_free (rc);    
767   }
768 }
769
770
771 /**
772  * Method called whenever a peer has disconnected from the tunnel.
773  *
774  * @param cls closure
775  * @param peer peer identity the tunnel stopped working with
776  */
777 static void
778 mesh_disconnect_handler (void *cls,
779                          const struct
780                          GNUNET_PeerIdentity * peer)
781 {
782   GNUNET_assert (dns_exit_available > 0);
783   dns_exit_available--;
784   if (0 == dns_exit_available)
785   {
786     if (NULL != mesh_th)
787     {
788       GNUNET_MESH_notify_transmit_ready_cancel (mesh_th);
789       mesh_th = NULL;
790     }
791     abort_all_requests ();
792   }
793 }
794
795
796 /**
797  * Method called whenever a peer has connected to the tunnel.
798  *
799  * @param cls closure
800  * @param peer peer identity the tunnel was created to, NULL on timeout
801  * @param atsi performance data for the connection
802  */
803 static void
804 mesh_connect_handler (void *cls,
805                       const struct GNUNET_PeerIdentity
806                       * peer,
807                       const struct
808                       GNUNET_ATS_Information * atsi)
809 {
810   dns_exit_available++;
811 }
812
813
814 /**
815  * Function scheduled as very last function, cleans up after us
816  */
817 static void
818 cleanup (void *cls GNUNET_UNUSED,
819          const struct GNUNET_SCHEDULER_TaskContext *tskctx)
820 {
821   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
822               "Protocol translation daemon is shutting down now\n");
823   if (vpn_handle != NULL)
824   {
825     GNUNET_VPN_disconnect (vpn_handle);
826     vpn_handle = NULL;
827   }
828   if (NULL != mesh_th)
829   {
830     GNUNET_MESH_notify_transmit_ready_cancel (mesh_th);
831     mesh_th = NULL;
832   }
833   if (NULL != mesh_tunnel)
834   {
835     GNUNET_MESH_tunnel_destroy (mesh_tunnel);
836     mesh_tunnel = NULL;
837   }
838   if (mesh_handle != NULL)
839   {
840     GNUNET_MESH_disconnect (mesh_handle);
841     mesh_handle = NULL;
842   }
843   abort_all_requests ();
844   if (dns_post_handle != NULL)
845   {
846     GNUNET_DNS_disconnect (dns_post_handle);
847     dns_post_handle = NULL;
848   }
849   if (dns_pre_handle != NULL)
850   {
851     GNUNET_DNS_disconnect (dns_pre_handle);
852     dns_pre_handle = NULL;
853   }
854   if (stats != NULL)
855   {
856     GNUNET_STATISTICS_destroy (stats, GNUNET_YES);
857     stats = NULL;
858   }
859 }
860
861
862 /**
863  * @brief Main function that will be run by the scheduler.
864  *
865  * @param cls closure
866  * @param args remaining command-line arguments
867  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
868  * @param cfg_ configuration
869  */
870 static void
871 run (void *cls, char *const *args GNUNET_UNUSED,
872      const char *cfgfile GNUNET_UNUSED,
873      const struct GNUNET_CONFIGURATION_Handle *cfg_)
874 {
875   cfg = cfg_;
876   stats = GNUNET_STATISTICS_create ("pt", cfg);
877   ipv4_pt = GNUNET_CONFIGURATION_get_value_yesno (cfg, "pt", "TUNNEL_IPV4");
878   ipv6_pt = GNUNET_CONFIGURATION_get_value_yesno (cfg, "pt", "TUNNEL_IPV6"); 
879   dns_tunnel = GNUNET_CONFIGURATION_get_value_yesno (cfg, "pt", "TUNNEL_DNS"); 
880   if (! (ipv4_pt || ipv6_pt || dns_tunnel))
881   {
882     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
883                 _("No useful service enabled.  Exiting.\n"));
884     GNUNET_SCHEDULER_shutdown ();
885     return;    
886   }
887   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &cleanup, cls);
888   if (ipv4_pt || ipv6_pt)
889   {
890     dns_post_handle 
891       = GNUNET_DNS_connect (cfg, 
892                             GNUNET_DNS_FLAG_POST_RESOLUTION,
893                             &dns_post_request_handler, NULL);
894     if (NULL == dns_post_handle)       
895     {
896       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
897                   _("Failed to connect to %s service.  Exiting.\n"),
898                   "DNS");
899       GNUNET_SCHEDULER_shutdown ();
900       return;
901     }
902     vpn_handle = GNUNET_VPN_connect (cfg);
903     if (NULL == vpn_handle)
904     {
905       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
906                   _("Failed to connect to %s service.  Exiting.\n"),
907                   "VPN");
908       GNUNET_SCHEDULER_shutdown ();
909       return;
910     }
911   }
912   if (dns_tunnel)
913   {
914     static struct GNUNET_MESH_MessageHandler mesh_handlers[] = {
915       {&receive_dns_response, GNUNET_MESSAGE_TYPE_VPN_DNS_FROM_INTERNET, 0},
916       {NULL, 0, 0}
917     };
918     static GNUNET_MESH_ApplicationType mesh_types[] = {
919       GNUNET_APPLICATION_TYPE_END
920     };
921
922     dns_pre_handle 
923       = GNUNET_DNS_connect (cfg, 
924                             GNUNET_DNS_FLAG_PRE_RESOLUTION,
925                             &dns_pre_request_handler, NULL);
926     if (NULL == dns_pre_handle)       
927     {
928       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
929                   _("Failed to connect to %s service.  Exiting.\n"),
930                   "DNS");
931       GNUNET_SCHEDULER_shutdown ();
932       return;
933     }
934     mesh_handle = GNUNET_MESH_connect (cfg, 1, NULL, NULL, NULL,
935                                        mesh_handlers, mesh_types);
936     if (NULL == mesh_handle)
937     {
938       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
939                   _("Failed to connect to %s service.  Exiting.\n"),
940                   "MESH");
941       GNUNET_SCHEDULER_shutdown ();
942       return;
943     }
944     mesh_tunnel = GNUNET_MESH_tunnel_create (mesh_handle,
945                                              NULL,
946                                              &mesh_connect_handler,
947                                              &mesh_disconnect_handler,
948                                              NULL);
949     GNUNET_MESH_peer_request_connect_by_type (mesh_tunnel,
950                                               GNUNET_APPLICATION_TYPE_INTERNET_RESOLVER); 
951   }
952 }
953
954
955 /**
956  * The main function
957  *
958  * @param argc number of arguments from the command line
959  * @param argv command line arguments
960  * @return 0 ok, 1 on error
961  */
962 int
963 main (int argc, char *const *argv)
964 {
965   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
966     GNUNET_GETOPT_OPTION_END
967   };
968
969   return (GNUNET_OK ==
970           GNUNET_PROGRAM_run (argc, argv, "gnunet-daemon-pt",
971                               gettext_noop
972                               ("Daemon to run to perform IP protocol translation to GNUnet"),
973                               options, &run, NULL)) ? 0 : 1;
974 }
975
976
977 /* end of gnunet-daemon-pt.c */