Changed Namestore API, changed error handling, changed gns record json
[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   GNUNET_DNSSTUB_resolve_cancel (request->dns_lookup);
273   send_response (request);
274 }
275
276
277 /**
278  * Iterator called on obtained result for a GNS lookup.
279  *
280  * @param cls closure
281  * @param was_gns #GNUNET_NO if the TLD is not configured for GNS
282  * @param rd_count number of records in @a rd
283  * @param rd the records in reply
284  */
285 static void
286 result_processor (void *cls,
287                   int was_gns,
288                   uint32_t rd_count,
289                   const struct GNUNET_GNSRECORD_Data *rd)
290 {
291   struct Request *request = cls;
292   struct GNUNET_DNSPARSER_Packet *packet;
293   struct GNUNET_DNSPARSER_Record rec;
294
295   request->lookup = NULL;
296   if (GNUNET_NO == was_gns)
297   {
298     /* TLD not configured for GNS, fall back to DNS */
299     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
300                 "Using DNS resolver IP `%s' to resolve `%s'\n",
301                 dns_ip,
302                 request->packet->queries[0].name);
303     request->original_request_id = request->packet->id;
304     GNUNET_DNSPARSER_free_packet (request->packet);
305     request->packet = NULL;
306     request->dns_lookup = GNUNET_DNSSTUB_resolve (dns_stub,
307                                                   request->udp_msg,
308                                                   request->udp_msg_size,
309                                                   &dns_result_processor,
310                                                   request);
311     return;
312   }
313   packet = request->packet;
314   packet->flags.query_or_response = 1;
315   packet->flags.return_code = GNUNET_TUN_DNS_RETURN_CODE_NO_ERROR;
316   packet->flags.checking_disabled = 0;
317   packet->flags.authenticated_data = 1;
318   packet->flags.zero = 0;
319   packet->flags.recursion_available = 1;
320   packet->flags.message_truncated = 0;
321   packet->flags.authoritative_answer = 0;
322   //packet->flags.opcode = GNUNET_TUN_DNS_OPCODE_STATUS; // ???
323   for (uint32_t i=0;i<rd_count;i++)
324     {
325       // FIXME: do we need to hanlde #GNUNET_GNSRECORD_RF_SHADOW_RECORD
326       // here? Or should we do this in libgnunetgns?
327       rec.expiration_time.abs_value_us = rd[i].expiration_time;
328       switch (rd[i].record_type)
329         {
330         case GNUNET_DNSPARSER_TYPE_A:
331           GNUNET_assert (sizeof (struct in_addr) == rd[i].data_size);
332           rec.name = GNUNET_strdup (packet->queries[0].name);
333           rec.dns_traffic_class = GNUNET_TUN_DNS_CLASS_INTERNET;
334           rec.type = GNUNET_DNSPARSER_TYPE_A;
335           rec.data.raw.data = GNUNET_new (struct in_addr);
336           GNUNET_memcpy (rec.data.raw.data,
337                   rd[i].data,
338                   rd[i].data_size);
339           rec.data.raw.data_len = sizeof (struct in_addr);
340           GNUNET_array_append (packet->answers,
341                                packet->num_answers,
342                                rec);
343           break;
344         case GNUNET_DNSPARSER_TYPE_AAAA:
345           GNUNET_assert (sizeof (struct in6_addr) == rd[i].data_size);
346           rec.name = GNUNET_strdup (packet->queries[0].name);
347           rec.data.raw.data = GNUNET_new (struct in6_addr);
348           rec.dns_traffic_class = GNUNET_TUN_DNS_CLASS_INTERNET;
349           rec.type = GNUNET_DNSPARSER_TYPE_AAAA;
350           GNUNET_memcpy (rec.data.raw.data,
351                   rd[i].data,
352                   rd[i].data_size);
353           rec.data.raw.data_len = sizeof (struct in6_addr);
354           GNUNET_array_append (packet->answers,
355                                packet->num_answers,
356                                rec);
357           break;
358         case GNUNET_DNSPARSER_TYPE_CNAME:
359           rec.name = GNUNET_strdup (packet->queries[0].name);
360           rec.data.hostname = GNUNET_strdup (rd[i].data);
361           rec.dns_traffic_class = GNUNET_TUN_DNS_CLASS_INTERNET;
362           rec.type = GNUNET_DNSPARSER_TYPE_CNAME;
363           GNUNET_memcpy (rec.data.hostname,
364                   rd[i].data,
365                   rd[i].data_size);
366           GNUNET_array_append (packet->answers,
367                                packet->num_answers,
368                                rec);
369           break;
370         default:
371           /* skip */
372           break;
373         }
374     }
375   send_response (request);
376 }
377
378
379 /**
380  * Handle DNS request.
381  *
382  * @param lsock socket to use for sending the reply
383  * @param addr address to use for sending the reply
384  * @param addr_len number of bytes in @a addr
385  * @param udp_msg DNS request payload
386  * @param udp_msg_size number of bytes in @a udp_msg
387  */
388 static void
389 handle_request (struct GNUNET_NETWORK_Handle *lsock,
390                 const void *addr,
391                 size_t addr_len,
392                 const char *udp_msg,
393                 size_t udp_msg_size)
394 {
395   struct Request *request;
396   struct GNUNET_DNSPARSER_Packet *packet;
397
398   packet = GNUNET_DNSPARSER_parse (udp_msg,
399                                    udp_msg_size);
400   if (NULL == packet)
401   {
402     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
403                 _("Cannot parse DNS request from %s\n"),
404                 GNUNET_a2s (addr, addr_len));
405     return;
406   }
407   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
408               "Received request for `%s' with flags %u, #answers %d, #auth %d, #additional %d\n",
409               packet->queries[0].name,
410               (unsigned int) packet->flags.query_or_response,
411               (int) packet->num_answers,
412               (int) packet->num_authority_records,
413               (int) packet->num_additional_records);
414   if ( (0 != packet->flags.query_or_response) ||
415        (0 != packet->num_answers) ||
416        (0 != packet->num_authority_records))
417   {
418     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
419                 _("Received malformed DNS request from %s\n"),
420                 GNUNET_a2s (addr, addr_len));
421     GNUNET_DNSPARSER_free_packet (packet);
422     return;
423   }
424   if ( (1 != packet->num_queries) )
425   {
426     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
427                 _("Received unsupported DNS request from %s\n"),
428                 GNUNET_a2s (addr,
429                             addr_len));
430     GNUNET_DNSPARSER_free_packet (packet);
431     return;
432   }
433   request = GNUNET_malloc (sizeof (struct Request) + addr_len);
434   request->lsock = lsock;
435   request->packet = packet;
436   request->addr = &request[1];
437   request->addr_len = addr_len;
438   GNUNET_memcpy (&request[1],
439                  addr,
440                  addr_len);
441   request->udp_msg_size = udp_msg_size;
442   request->udp_msg = GNUNET_memdup (udp_msg,
443                                     udp_msg_size);
444   request->timeout_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT,
445                                                         &do_timeout,
446                                                         request);
447   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
448               "Calling GNS on `%s'\n",
449               packet->queries[0].name);
450   request->lookup = GNUNET_GNS_lookup_with_tld (gns,
451                                                 packet->queries[0].name,
452                                                 packet->queries[0].type,
453                                                 GNUNET_NO,
454                                                 &result_processor,
455                                                 request);
456 }
457
458
459 /**
460  * Task to read IPv4 DNS packets.
461  *
462  * @param cls the 'listen_socket4'
463  */
464 static void
465 read_dns4 (void *cls)
466 {
467   struct sockaddr_in v4;
468   socklen_t addrlen;
469   ssize_t size;
470   const struct GNUNET_SCHEDULER_TaskContext *tc;
471
472   GNUNET_assert (listen_socket4 == cls);
473   t4 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
474                                       listen_socket4,
475                                       &read_dns4,
476                                       listen_socket4);
477   tc = GNUNET_SCHEDULER_get_task_context ();
478   if (0 == (GNUNET_SCHEDULER_REASON_READ_READY & tc->reason))
479     return; /* shutdown? */
480   size = GNUNET_NETWORK_socket_recvfrom_amount (listen_socket4);
481   if (0 > size)
482     {
483       GNUNET_break (0);
484       return; /* read error!? */
485     }
486   {
487     char buf[size + 1];
488     ssize_t sret;
489
490     addrlen = sizeof (v4);
491     sret = GNUNET_NETWORK_socket_recvfrom (listen_socket4,
492                                            buf,
493                                            size + 1,
494                                            (struct sockaddr *) &v4,
495                                            &addrlen);
496     if (0 > sret)
497     {
498       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
499                            "recvfrom");
500       return;
501     }
502     GNUNET_break (size == sret);
503     handle_request (listen_socket4,
504                     &v4,
505                     addrlen,
506                     buf,
507                     size);
508   }
509 }
510
511
512 /**
513  * Task to read IPv6 DNS packets.
514  *
515  * @param cls the 'listen_socket6'
516  */
517 static void
518 read_dns6 (void *cls)
519 {
520   struct sockaddr_in6 v6;
521   socklen_t addrlen;
522   ssize_t size;
523   const struct GNUNET_SCHEDULER_TaskContext *tc;
524
525   GNUNET_assert (listen_socket6 == cls);
526   t6 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
527                                       listen_socket6,
528                                       &read_dns6,
529                                       listen_socket6);
530   tc = GNUNET_SCHEDULER_get_task_context ();
531   if (0 == (GNUNET_SCHEDULER_REASON_READ_READY & tc->reason))
532     return; /* shutdown? */
533   size = GNUNET_NETWORK_socket_recvfrom_amount (listen_socket6);
534   if (0 > size)
535     {
536       GNUNET_break (0);
537       return; /* read error!? */
538     }
539   {
540     char buf[size];
541     ssize_t sret;
542
543     addrlen = sizeof (v6);
544     sret = GNUNET_NETWORK_socket_recvfrom (listen_socket6,
545                                            buf,
546                                            size,
547                                            (struct sockaddr *) &v6,
548                                            &addrlen);
549     if (0 > sret)
550     {
551       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
552                            "recvfrom");
553       return;
554     }
555     GNUNET_break (size == sret);
556     handle_request (listen_socket6,
557                     &v6,
558                     addrlen,
559                     buf,
560                     size);
561   }
562 }
563
564
565 /**
566  * Main function that will be run.
567  *
568  * @param cls closure
569  * @param args remaining command-line arguments
570  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
571  * @param c configuration
572  */
573 static void
574 run (void *cls,
575      char *const *args,
576      const char *cfgfile,
577      const struct GNUNET_CONFIGURATION_Handle *c)
578 {
579   (void) cls;
580   (void) args;
581   (void) cfgfile;
582   cfg = c;
583   if (NULL == dns_ip)
584   {
585     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
586                 _("No DNS server specified!\n"));
587     return;
588   }
589   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
590                                  NULL);
591   if (NULL == (gns = GNUNET_GNS_connect (cfg)))
592     return;
593   GNUNET_assert (NULL != (dns_stub = GNUNET_DNSSTUB_start (128)));
594   if (GNUNET_OK !=
595       GNUNET_DNSSTUB_add_dns_ip (dns_stub,
596                                  dns_ip))
597   {
598     GNUNET_DNSSTUB_stop (dns_stub);
599     GNUNET_GNS_disconnect (gns);
600     gns = NULL;
601     return;
602   }
603   listen_socket4 = GNUNET_NETWORK_socket_create (PF_INET,
604                                                  SOCK_DGRAM,
605                                                  IPPROTO_UDP);
606   if (NULL != listen_socket4)
607   {
608     struct sockaddr_in v4;
609
610     memset (&v4, 0, sizeof (v4));
611     v4.sin_family = AF_INET;
612 #if HAVE_SOCKADDR_IN_SIN_LEN
613     v4.sin_len = sizeof (v4);
614 #endif
615     v4.sin_port = htons (listen_port);
616     if (GNUNET_OK !=
617         GNUNET_NETWORK_socket_bind (listen_socket4,
618                                     (struct sockaddr *) &v4,
619                                     sizeof (v4)))
620     {
621       GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "bind");
622       GNUNET_NETWORK_socket_close (listen_socket4);
623       listen_socket4 = NULL;
624     }
625   }
626   listen_socket6 = GNUNET_NETWORK_socket_create (PF_INET6,
627                                                 SOCK_DGRAM,
628                                                 IPPROTO_UDP);
629   if (NULL != listen_socket6)
630   {
631     struct sockaddr_in6 v6;
632
633     memset (&v6, 0, sizeof (v6));
634     v6.sin6_family = AF_INET6;
635 #if HAVE_SOCKADDR_IN_SIN_LEN
636     v6.sin6_len = sizeof (v6);
637 #endif
638     v6.sin6_port = htons (listen_port);
639     if (GNUNET_OK !=
640         GNUNET_NETWORK_socket_bind (listen_socket6,
641                                     (struct sockaddr *) &v6,
642                                     sizeof (v6)))
643     {
644       GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "bind");
645       GNUNET_NETWORK_socket_close (listen_socket6);
646       listen_socket6 = NULL;
647     }
648   }
649   if ( (NULL == listen_socket4) &&
650        (NULL == listen_socket6) )
651   {
652     GNUNET_GNS_disconnect (gns);
653     gns = NULL;
654     GNUNET_DNSSTUB_stop (dns_stub);
655     dns_stub = NULL;
656     return;
657   }
658   if (NULL != listen_socket4)
659     t4 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
660                                         listen_socket4,
661                                         &read_dns4,
662                                         listen_socket4);
663   if (NULL != listen_socket6)
664     t6 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
665                                         listen_socket6,
666                                         &read_dns6,
667                                         listen_socket6);
668 }
669
670
671 /**
672  * The main function for the dns2gns daemon.
673  *
674  * @param argc number of arguments from the command line
675  * @param argv command line arguments
676  * @return 0 ok, 1 on error
677  */
678 int
679 main (int argc,
680       char *const *argv)
681 {
682   struct GNUNET_GETOPT_CommandLineOption options[] = {
683     GNUNET_GETOPT_option_string ('d',
684                                  "dns",
685                                  "IP",
686                                  gettext_noop ("IP of recursive DNS resolver to use (required)"),
687                                  &dns_ip),
688     GNUNET_GETOPT_option_uint ('p',
689                                "port",
690                                "UDPPORT",
691                                gettext_noop ("UDP port to listen on for inbound DNS requests; default: 2853"),
692                                &listen_port),
693     GNUNET_GETOPT_OPTION_END
694   };
695   int ret;
696
697   if (GNUNET_OK !=
698       GNUNET_STRINGS_get_utf8_args (argc, argv,
699                                     &argc, &argv))
700     return 2;
701   GNUNET_log_setup ("gnunet-dns2gns",
702                     "WARNING",
703                     NULL);
704   ret =
705       (GNUNET_OK ==
706        GNUNET_PROGRAM_run (argc, argv,
707                            "gnunet-dns2gns",
708                            _("GNUnet DNS-to-GNS proxy (a DNS server)"),
709                            options,
710                            &run, NULL)) ? 0 : 1;
711   GNUNET_free ((void*) argv);
712   return ret;
713 }
714
715 /* end of gnunet-dns2gns.c */