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