paragraph for gnunet devs that don't know how to use the web
[oweals/gnunet.git] / src / gns / gnunet-dns2gns.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2012-2013 GNUnet e.V.
4
5      GNUnet is free software: you can redistribute it and/or modify it
6      under the terms of the GNU Affero General Public License as published
7      by the Free Software Foundation, either version 3 of the License,
8      or (at your option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      Affero General Public License for more details.
14     
15      You should have received a copy of the GNU Affero General Public License
16      along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18 /**
19  * @file gnunet-dns2gns.c
20  * @brief DNS server that translates DNS requests to GNS
21  * @author Christian Grothoff
22  */
23 #include "platform.h"
24 #include <gnunet_util_lib.h>
25 #include <gnunet_dnsparser_lib.h>
26 #include <gnunet_gns_service.h>
27 #include <gnunet_dnsstub_lib.h>
28 #include "gns.h"
29
30 /**
31  * Timeout for DNS requests.
32  */
33 #define TIMEOUT GNUNET_TIME_UNIT_MINUTES
34
35 /**
36  * Data kept per request.
37  */
38 struct Request
39 {
40   /**
41    * Socket to use for sending the reply.
42    */
43   struct GNUNET_NETWORK_Handle *lsock;
44
45   /**
46    * Destination address to use.
47    */
48   const void *addr;
49
50   /**
51    * Initially, this is the DNS request, it will then be
52    * converted to the DNS response.
53    */
54   struct GNUNET_DNSPARSER_Packet *packet;
55
56   /**
57    * Our GNS request handle.
58    */
59   struct GNUNET_GNS_LookupWithTldRequest *lookup;
60
61   /**
62    * Our DNS request handle
63    */
64   struct GNUNET_DNSSTUB_RequestSocket *dns_lookup;
65
66   /**
67    * Task run on timeout or shutdown to clean up without
68    * response.
69    */
70   struct GNUNET_SCHEDULER_Task *timeout_task;
71
72   /**
73    * Original UDP request message.
74    */
75   char *udp_msg;
76
77   /**
78    * Number of bytes in @e addr.
79    */
80   size_t addr_len;
81
82   /**
83    * Number of bytes in @e udp_msg.
84    */
85   size_t udp_msg_size;
86
87   /**
88    * ID of the original request.
89    */
90   uint16_t original_request_id;
91 };
92
93
94 /**
95  * Handle to GNS resolver.
96  */
97 struct GNUNET_GNS_Handle *gns;
98
99 /**
100  * Stub resolver
101  */
102 struct GNUNET_DNSSTUB_Context *dns_stub;
103
104 /**
105  * Listen socket for IPv4.
106  */
107 static struct GNUNET_NETWORK_Handle *listen_socket4;
108
109 /**
110  * Listen socket for IPv6.
111  */
112 static struct GNUNET_NETWORK_Handle *listen_socket6;
113
114 /**
115  * Task for IPv4 socket.
116  */
117 static struct GNUNET_SCHEDULER_Task *t4;
118
119 /**
120  * Task for IPv6 socket.
121  */
122 static struct GNUNET_SCHEDULER_Task *t6;
123
124 /**
125  * IP of DNS server
126  */
127 static char *dns_ip;
128
129 /**
130  * UDP Port we listen on for inbound DNS requests.
131  */
132 static unsigned int listen_port = 53;
133
134 /**
135  * Configuration to use.
136  */
137 static const struct GNUNET_CONFIGURATION_Handle *cfg;
138
139
140 /**
141  * Task run on shutdown.  Cleans up everything.
142  *
143  * @param cls unused
144  */
145 static void
146 do_shutdown (void *cls)
147 {
148   (void) cls;
149   if (NULL != t4)
150   {
151     GNUNET_SCHEDULER_cancel (t4);
152     t4 = NULL;
153   }
154   if (NULL != t6)
155   {
156     GNUNET_SCHEDULER_cancel (t6);
157     t6 = NULL;
158   }
159   if (NULL != listen_socket4)
160   {
161     GNUNET_NETWORK_socket_close (listen_socket4);
162     listen_socket4 = NULL;
163   }
164   if (NULL != listen_socket6)
165   {
166     GNUNET_NETWORK_socket_close (listen_socket6);
167     listen_socket6 = NULL;
168   }
169   if (NULL != gns)
170   {
171     GNUNET_GNS_disconnect (gns);
172     gns = NULL;
173   }
174   if (NULL != dns_stub)
175   {
176     GNUNET_DNSSTUB_stop (dns_stub);
177     dns_stub = NULL;
178   }
179 }
180
181
182 /**
183  * Send the response for the given request and clean up.
184  *
185  * @param request context for the request.
186  */
187 static void
188 send_response (struct Request *request)
189 {
190   char *buf;
191   size_t size;
192   ssize_t sret;
193
194   if (GNUNET_SYSERR ==
195       GNUNET_DNSPARSER_pack (request->packet,
196                              UINT16_MAX /* is this not too much? */,
197                              &buf,
198                              &size))
199     {
200       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
201                   _("Failed to pack DNS response into UDP packet!\n"));
202     }
203   else
204     {
205       sret = GNUNET_NETWORK_socket_sendto (request->lsock,
206                                            buf,
207                                            size,
208                                            request->addr,
209                                            request->addr_len);
210       if ( (sret < 0) ||
211            (size != (size_t) sret) )
212         GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
213                              "sendto");
214       GNUNET_free (buf);
215     }
216   GNUNET_SCHEDULER_cancel (request->timeout_task);
217   GNUNET_DNSPARSER_free_packet (request->packet);
218   GNUNET_free (request->udp_msg);
219   GNUNET_free (request);
220 }
221
222
223 /**
224  * Task run on timeout.  Cleans up request.
225  *
226  * @param cls `struct Request *` of the request to clean up
227  */
228 static void
229 do_timeout (void *cls)
230 {
231   struct Request *request = cls;
232
233   if (NULL != request->packet)
234     GNUNET_DNSPARSER_free_packet (request->packet);
235   if (NULL != request->lookup)
236     GNUNET_GNS_lookup_with_tld_cancel (request->lookup);
237   if (NULL != request->dns_lookup)
238     GNUNET_DNSSTUB_resolve_cancel (request->dns_lookup);
239   GNUNET_free (request->udp_msg);
240   GNUNET_free (request);
241 }
242
243
244 /**
245  * Iterator called on obtained result for a DNS lookup
246  *
247  * @param cls closure
248  * @param dns the DNS udp payload
249  * @param r size of the DNS payload
250  */
251 static void
252 dns_result_processor (void *cls,
253                       const struct GNUNET_TUN_DnsHeader *dns,
254                       size_t r)
255 {
256   struct Request *request = cls;
257
258   if (NULL == dns)
259   {
260     /* DNSSTUB gave up, so we trigger timeout early */
261     GNUNET_SCHEDULER_cancel (request->timeout_task);
262     do_timeout (request);
263     return;
264   }
265   if (request->original_request_id != dns->id)
266   {
267     /* for a another query, ignore */
268     return;
269   }
270   request->packet = GNUNET_DNSPARSER_parse ((char*)dns,
271                                             r);
272   send_response (request);
273 }
274
275
276 /**
277  * Iterator called on obtained result for a GNS lookup.
278  *
279  * @param cls closure
280  * @param was_gns #GNUNET_NO if the TLD is not configured for GNS
281  * @param rd_count number of records in @a rd
282  * @param rd the records in reply
283  */
284 static void
285 result_processor (void *cls,
286                   int was_gns,
287                   uint32_t rd_count,
288                   const struct GNUNET_GNSRECORD_Data *rd)
289 {
290   struct Request *request = cls;
291   struct GNUNET_DNSPARSER_Packet *packet;
292   struct GNUNET_DNSPARSER_Record rec;
293
294   request->lookup = NULL;
295   if (GNUNET_NO == was_gns)
296   {
297     /* TLD not configured for GNS, fall back to DNS */
298     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
299                 "Using DNS resolver IP `%s' to resolve `%s'\n",
300                 dns_ip,
301                 request->packet->queries[0].name);
302     request->original_request_id = request->packet->id;
303     GNUNET_DNSPARSER_free_packet (request->packet);
304     request->packet = NULL;
305     request->dns_lookup = GNUNET_DNSSTUB_resolve (dns_stub,
306                                                   request->udp_msg,
307                                                   request->udp_msg_size,
308                                                   &dns_result_processor,
309                                                   request);
310     return;
311   }
312   packet = request->packet;
313   packet->flags.query_or_response = 1;
314   packet->flags.return_code = GNUNET_TUN_DNS_RETURN_CODE_NO_ERROR;
315   packet->flags.checking_disabled = 0;
316   packet->flags.authenticated_data = 1;
317   packet->flags.zero = 0;
318   packet->flags.recursion_available = 1;
319   packet->flags.message_truncated = 0;
320   packet->flags.authoritative_answer = 0;
321   //packet->flags.opcode = GNUNET_TUN_DNS_OPCODE_STATUS; // ???
322   for (uint32_t i=0;i<rd_count;i++)
323     {
324       // FIXME: do we need to hanlde #GNUNET_GNSRECORD_RF_SHADOW_RECORD
325       // here? Or should we do this in libgnunetgns?
326       rec.expiration_time.abs_value_us = rd[i].expiration_time;
327       switch (rd[i].record_type)
328         {
329         case GNUNET_DNSPARSER_TYPE_A:
330           GNUNET_assert (sizeof (struct in_addr) == rd[i].data_size);
331           rec.name = GNUNET_strdup (packet->queries[0].name);
332           rec.dns_traffic_class = GNUNET_TUN_DNS_CLASS_INTERNET;
333           rec.type = GNUNET_DNSPARSER_TYPE_A;
334           rec.data.raw.data = GNUNET_new (struct in_addr);
335           GNUNET_memcpy (rec.data.raw.data,
336                   rd[i].data,
337                   rd[i].data_size);
338           rec.data.raw.data_len = sizeof (struct in_addr);
339           GNUNET_array_append (packet->answers,
340                                packet->num_answers,
341                                rec);
342           break;
343         case GNUNET_DNSPARSER_TYPE_AAAA:
344           GNUNET_assert (sizeof (struct in6_addr) == rd[i].data_size);
345           rec.name = GNUNET_strdup (packet->queries[0].name);
346           rec.data.raw.data = GNUNET_new (struct in6_addr);
347           rec.dns_traffic_class = GNUNET_TUN_DNS_CLASS_INTERNET;
348           rec.type = GNUNET_DNSPARSER_TYPE_AAAA;
349           GNUNET_memcpy (rec.data.raw.data,
350                   rd[i].data,
351                   rd[i].data_size);
352           rec.data.raw.data_len = sizeof (struct in6_addr);
353           GNUNET_array_append (packet->answers,
354                                packet->num_answers,
355                                rec);
356           break;
357         case GNUNET_DNSPARSER_TYPE_CNAME:
358           rec.name = GNUNET_strdup (packet->queries[0].name);
359           rec.data.hostname = GNUNET_strdup (rd[i].data);
360           rec.dns_traffic_class = GNUNET_TUN_DNS_CLASS_INTERNET;
361           rec.type = GNUNET_DNSPARSER_TYPE_CNAME;
362           GNUNET_memcpy (rec.data.hostname,
363                   rd[i].data,
364                   rd[i].data_size);
365           GNUNET_array_append (packet->answers,
366                                packet->num_answers,
367                                rec);
368           break;
369         default:
370           /* skip */
371           break;
372         }
373     }
374   send_response (request);
375 }
376
377
378 /**
379  * Handle DNS request.
380  *
381  * @param lsock socket to use for sending the reply
382  * @param addr address to use for sending the reply
383  * @param addr_len number of bytes in @a addr
384  * @param udp_msg DNS request payload
385  * @param udp_msg_size number of bytes in @a udp_msg
386  */
387 static void
388 handle_request (struct GNUNET_NETWORK_Handle *lsock,
389                 const void *addr,
390                 size_t addr_len,
391                 const char *udp_msg,
392                 size_t udp_msg_size)
393 {
394   struct Request *request;
395   struct GNUNET_DNSPARSER_Packet *packet;
396
397   packet = GNUNET_DNSPARSER_parse (udp_msg,
398                                    udp_msg_size);
399   if (NULL == packet)
400   {
401     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
402                 _("Cannot parse DNS request from %s\n"),
403                 GNUNET_a2s (addr, addr_len));
404     return;
405   }
406   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
407               "Received request for `%s' with flags %u, #answers %d, #auth %d, #additional %d\n",
408               packet->queries[0].name,
409               (unsigned int) packet->flags.query_or_response,
410               (int) packet->num_answers,
411               (int) packet->num_authority_records,
412               (int) packet->num_additional_records);
413   if ( (0 != packet->flags.query_or_response) ||
414        (0 != packet->num_answers) ||
415        (0 != packet->num_authority_records))
416   {
417     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
418                 _("Received malformed DNS request from %s\n"),
419                 GNUNET_a2s (addr, addr_len));
420     GNUNET_DNSPARSER_free_packet (packet);
421     return;
422   }
423   if ( (1 != packet->num_queries) )
424   {
425     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
426                 _("Received unsupported DNS request from %s\n"),
427                 GNUNET_a2s (addr,
428                             addr_len));
429     GNUNET_DNSPARSER_free_packet (packet);
430     return;
431   }
432   request = GNUNET_malloc (sizeof (struct Request) + addr_len);
433   request->lsock = lsock;
434   request->packet = packet;
435   request->addr = &request[1];
436   request->addr_len = addr_len;
437   GNUNET_memcpy (&request[1],
438                  addr,
439                  addr_len);
440   request->udp_msg_size = udp_msg_size;
441   request->udp_msg = GNUNET_memdup (udp_msg,
442                                     udp_msg_size);
443   request->timeout_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT,
444                                                         &do_timeout,
445                                                         request);
446   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
447               "Calling GNS on `%s'\n",
448               packet->queries[0].name);
449   request->lookup = GNUNET_GNS_lookup_with_tld (gns,
450                                                 packet->queries[0].name,
451                                                 packet->queries[0].type,
452                                                 GNUNET_NO,
453                                                 &result_processor,
454                                                 request);
455 }
456
457
458 /**
459  * Task to read IPv4 DNS packets.
460  *
461  * @param cls the 'listen_socket4'
462  */
463 static void
464 read_dns4 (void *cls)
465 {
466   struct sockaddr_in v4;
467   socklen_t addrlen;
468   ssize_t size;
469   const struct GNUNET_SCHEDULER_TaskContext *tc;
470
471   GNUNET_assert (listen_socket4 == cls);
472   t4 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
473                                       listen_socket4,
474                                       &read_dns4,
475                                       listen_socket4);
476   tc = GNUNET_SCHEDULER_get_task_context ();
477   if (0 == (GNUNET_SCHEDULER_REASON_READ_READY & tc->reason))
478     return; /* shutdown? */
479   size = GNUNET_NETWORK_socket_recvfrom_amount (listen_socket4);
480   if (0 > size)
481     {
482       GNUNET_break (0);
483       return; /* read error!? */
484     }
485   {
486     char buf[size + 1];
487     ssize_t sret;
488
489     addrlen = sizeof (v4);
490     sret = GNUNET_NETWORK_socket_recvfrom (listen_socket4,
491                                            buf,
492                                            size + 1,
493                                            (struct sockaddr *) &v4,
494                                            &addrlen);
495     if (0 > sret)
496     {
497       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
498                            "recvfrom");
499       return;
500     }
501     GNUNET_break (size == sret);
502     handle_request (listen_socket4,
503                     &v4,
504                     addrlen,
505                     buf,
506                     size);
507   }
508 }
509
510
511 /**
512  * Task to read IPv6 DNS packets.
513  *
514  * @param cls the 'listen_socket6'
515  */
516 static void
517 read_dns6 (void *cls)
518 {
519   struct sockaddr_in6 v6;
520   socklen_t addrlen;
521   ssize_t size;
522   const struct GNUNET_SCHEDULER_TaskContext *tc;
523
524   GNUNET_assert (listen_socket6 == cls);
525   t6 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
526                                       listen_socket6,
527                                       &read_dns6,
528                                       listen_socket6);
529   tc = GNUNET_SCHEDULER_get_task_context ();
530   if (0 == (GNUNET_SCHEDULER_REASON_READ_READY & tc->reason))
531     return; /* shutdown? */
532   size = GNUNET_NETWORK_socket_recvfrom_amount (listen_socket6);
533   if (0 > size)
534     {
535       GNUNET_break (0);
536       return; /* read error!? */
537     }
538   {
539     char buf[size];
540     ssize_t sret;
541
542     addrlen = sizeof (v6);
543     sret = GNUNET_NETWORK_socket_recvfrom (listen_socket6,
544                                            buf,
545                                            size,
546                                            (struct sockaddr *) &v6,
547                                            &addrlen);
548     if (0 > sret)
549     {
550       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
551                            "recvfrom");
552       return;
553     }
554     GNUNET_break (size == sret);
555     handle_request (listen_socket6,
556                     &v6,
557                     addrlen,
558                     buf,
559                     size);
560   }
561 }
562
563
564 /**
565  * Main function that will be run.
566  *
567  * @param cls closure
568  * @param args remaining command-line arguments
569  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
570  * @param c configuration
571  */
572 static void
573 run (void *cls,
574      char *const *args,
575      const char *cfgfile,
576      const struct GNUNET_CONFIGURATION_Handle *c)
577 {
578   (void) cls;
579   (void) args;
580   (void) cfgfile;
581   cfg = c;
582   if (NULL == dns_ip)
583   {
584     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
585                 _("No DNS server specified!\n"));
586     return;
587   }
588   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
589                                  NULL);
590   if (NULL == (gns = GNUNET_GNS_connect (cfg)))
591     return;
592   GNUNET_assert (NULL != (dns_stub = GNUNET_DNSSTUB_start (128)));
593   if (GNUNET_OK !=
594       GNUNET_DNSSTUB_add_dns_ip (dns_stub,
595                                  dns_ip))
596   {
597     GNUNET_DNSSTUB_stop (dns_stub);
598     GNUNET_GNS_disconnect (gns);
599     gns = NULL;
600     return;
601   }
602   listen_socket4 = GNUNET_NETWORK_socket_create (PF_INET,
603                                                  SOCK_DGRAM,
604                                                  IPPROTO_UDP);
605   if (NULL != listen_socket4)
606   {
607     struct sockaddr_in v4;
608
609     memset (&v4, 0, sizeof (v4));
610     v4.sin_family = AF_INET;
611 #if HAVE_SOCKADDR_IN_SIN_LEN
612     v4.sin_len = sizeof (v4);
613 #endif
614     v4.sin_port = htons (listen_port);
615     if (GNUNET_OK !=
616         GNUNET_NETWORK_socket_bind (listen_socket4,
617                                     (struct sockaddr *) &v4,
618                                     sizeof (v4)))
619     {
620       GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "bind");
621       GNUNET_NETWORK_socket_close (listen_socket4);
622       listen_socket4 = NULL;
623     }
624   }
625   listen_socket6 = GNUNET_NETWORK_socket_create (PF_INET6,
626                                                 SOCK_DGRAM,
627                                                 IPPROTO_UDP);
628   if (NULL != listen_socket6)
629   {
630     struct sockaddr_in6 v6;
631
632     memset (&v6, 0, sizeof (v6));
633     v6.sin6_family = AF_INET6;
634 #if HAVE_SOCKADDR_IN_SIN_LEN
635     v6.sin6_len = sizeof (v6);
636 #endif
637     v6.sin6_port = htons (listen_port);
638     if (GNUNET_OK !=
639         GNUNET_NETWORK_socket_bind (listen_socket6,
640                                     (struct sockaddr *) &v6,
641                                     sizeof (v6)))
642     {
643       GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "bind");
644       GNUNET_NETWORK_socket_close (listen_socket6);
645       listen_socket6 = NULL;
646     }
647   }
648   if ( (NULL == listen_socket4) &&
649        (NULL == listen_socket6) )
650   {
651     GNUNET_GNS_disconnect (gns);
652     gns = NULL;
653     GNUNET_DNSSTUB_stop (dns_stub);
654     dns_stub = NULL;
655     return;
656   }
657   if (NULL != listen_socket4)
658     t4 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
659                                         listen_socket4,
660                                         &read_dns4,
661                                         listen_socket4);
662   if (NULL != listen_socket6)
663     t6 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
664                                         listen_socket6,
665                                         &read_dns6,
666                                         listen_socket6);
667 }
668
669
670 /**
671  * The main function for the dns2gns daemon.
672  *
673  * @param argc number of arguments from the command line
674  * @param argv command line arguments
675  * @return 0 ok, 1 on error
676  */
677 int
678 main (int argc,
679       char *const *argv)
680 {
681   struct GNUNET_GETOPT_CommandLineOption options[] = {
682     GNUNET_GETOPT_option_string ('d',
683                                  "dns",
684                                  "IP",
685                                  gettext_noop ("IP of recursive DNS resolver to use (required)"),
686                                  &dns_ip),
687     GNUNET_GETOPT_option_uint ('p',
688                                "port",
689                                "UDPPORT",
690                                gettext_noop ("UDP port to listen on for inbound DNS requests; default: 2853"),
691                                &listen_port),
692     GNUNET_GETOPT_OPTION_END
693   };
694   int ret;
695
696   if (GNUNET_OK !=
697       GNUNET_STRINGS_get_utf8_args (argc, argv,
698                                     &argc, &argv))
699     return 2;
700   GNUNET_log_setup ("gnunet-dns2gns",
701                     "WARNING",
702                     NULL);
703   ret =
704       (GNUNET_OK ==
705        GNUNET_PROGRAM_run (argc, argv,
706                            "gnunet-dns2gns",
707                            _("GNUnet DNS-to-GNS proxy (a DNS server)"),
708                            options,
709                            &run, NULL)) ? 0 : 1;
710   GNUNET_free ((void*) argv);
711   return ret;
712 }
713
714 /* end of gnunet-dns2gns.c */