8ede2a18f01bc753d0da2d415ca2fa456784fe0d
[oweals/gnunet.git] / src / gns / gnunet-dns2gns.c
1 /*
2      This file is part of GNUnet.
3      (C) 2012 Christian Grothoff (and other contributing authors)
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  * @file gnunet-dns2gns.c
22  * @brief DNS server that translates DNS requests to GNS
23  * @author Christian Grothoff
24  */
25 #include "platform.h"
26 #include <gnunet_util_lib.h>
27 #include <gnunet_dnsparser_lib.h>
28 #include <gnunet_gns_service.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_LookupRequest *lookup;
60
61   /**
62    * Task run on timeout or shutdown to clean up without
63    * response.
64    */
65   GNUNET_SCHEDULER_TaskIdentifier timeout_task;
66
67   /**
68    * Number of bytes in 'addr'.
69    */ 
70   size_t addr_len;
71
72 };
73
74
75 /**
76  * Handle to GNS resolver.
77  */
78 struct GNUNET_GNS_Handle *gns;
79
80 /**
81  * Listen socket for IPv4.
82  */
83 static struct GNUNET_NETWORK_Handle *listen_socket4;
84
85 /**
86  * Listen socket for IPv6.
87  */
88 static struct GNUNET_NETWORK_Handle *listen_socket6;
89
90 /**
91  * Task for IPv4 socket.
92  */
93 static GNUNET_SCHEDULER_TaskIdentifier t4;
94
95 /**
96  * Task for IPv6 socket.
97  */
98 static GNUNET_SCHEDULER_TaskIdentifier t6;
99
100
101 /**
102  * Task run on shutdown.  Cleans up everything.
103  *
104  * @param cls unused
105  * @param tc scheduler context
106  */
107 static void
108 do_shutdown (void *cls,
109              const struct GNUNET_SCHEDULER_TaskContext *tc)
110 {
111   if (GNUNET_SCHEDULER_NO_TASK != t4)
112     GNUNET_SCHEDULER_cancel (t4);
113   if (GNUNET_SCHEDULER_NO_TASK != t6)
114     GNUNET_SCHEDULER_cancel (t6);
115   if (NULL != listen_socket4)
116   {
117     GNUNET_NETWORK_socket_close (listen_socket4);
118     listen_socket4 = NULL;
119   }
120   if (NULL != listen_socket6)
121   {
122     GNUNET_NETWORK_socket_close (listen_socket6);
123     listen_socket6 = NULL;
124   }
125   GNUNET_GNS_disconnect (gns);
126   gns = NULL;
127 }
128
129
130 /**
131  * Send the response for the given request and clean up.
132  *
133  * @param request context for the request.
134  */
135 static void
136 send_response (struct Request *request)
137 {
138   char *buf;
139   size_t size;
140   
141   if (GNUNET_SYSERR ==
142       GNUNET_DNSPARSER_pack (request->packet,
143                              UINT16_MAX /* is this not too much? */,
144                              &buf,
145                              &size))
146     {
147       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
148                   _("Failed to pack DNS response into UDP packet!\n"));
149     }
150   else
151     {
152       if (size !=
153           GNUNET_NETWORK_socket_sendto (request->lsock,
154                                         buf, size,
155                                         request->addr,
156                                         request->addr_len))
157         GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "sendto");
158       GNUNET_free (buf);
159     }
160   GNUNET_SCHEDULER_cancel (request->timeout_task);
161   GNUNET_DNSPARSER_free_packet (request->packet);
162   GNUNET_free (request);
163 }
164
165
166 /**
167  * Task run on timeout.  Cleans up request.
168  *
169  * @param cls 'struct Request' of the request to clean up
170  * @param tc scheduler context
171  */
172 static void
173 do_timeout (void *cls,
174             const struct GNUNET_SCHEDULER_TaskContext *tc)
175 {
176   struct Request *request = cls;
177
178   if (NULL != request->packet)
179     GNUNET_DNSPARSER_free_packet (request->packet);
180   if (NULL != request->lookup)
181     GNUNET_GNS_cancel_lookup_request (request->lookup);
182   GNUNET_free (request);
183 }
184
185
186 /**
187  * Iterator called on obtained result for a GNS
188  * lookup
189  *
190  * @param cls closure
191  * @param rd_count number of records
192  * @param rd the records in reply
193  */
194 static void
195 result_processor (void *cls,
196                   uint32_t rd_count,
197                   const struct GNUNET_NAMESTORE_RecordData *rd)
198 {
199   struct Request *request = cls;
200   struct GNUNET_DNSPARSER_Packet *packet;
201   uint32_t i;
202   struct GNUNET_DNSPARSER_Record rec;
203
204   request->lookup = NULL;
205   packet = request->packet;
206   packet->flags.query_or_response = 1;
207   packet->flags.return_code = GNUNET_DNSPARSER_RETURN_CODE_NO_ERROR;
208   packet->flags.checking_disabled = 0;
209   packet->flags.authenticated_data = 1;
210   packet->flags.zero = 0;
211   packet->flags.recursion_available = 1;
212   packet->flags.message_truncated = 0;
213   packet->flags.authoritative_answer = 1;
214   packet->flags.opcode = GNUNET_DNSPARSER_OPCODE_QUERY;
215   packet->num_additional_records = 0;
216   for (i=0;i<rd_count;i++)
217     {
218       switch (rd[i].record_type)
219         {
220         case GNUNET_DNSPARSER_TYPE_A:
221           GNUNET_assert (sizeof (struct in_addr) == rd[i].data_size);
222           rec.name = GNUNET_strdup (packet->queries[0].name);
223           rec.class = GNUNET_DNSPARSER_CLASS_INTERNET;
224           rec.type = GNUNET_DNSPARSER_TYPE_A;
225           rec.data.raw.data = GNUNET_malloc (sizeof (struct in_addr));
226           memcpy (rec.data.raw.data,
227                   rd[i].data,
228                   rd[i].data_size);
229           rec.data.raw.data_len = sizeof (struct in_addr);
230           GNUNET_array_append (packet->answers,
231                                packet->num_answers,
232                                rec);
233           break;
234         case GNUNET_DNSPARSER_TYPE_AAAA:
235           GNUNET_assert (sizeof (struct in6_addr) == rd[i].data_size);
236           rec.name = GNUNET_strdup (packet->queries[0].name);
237           rec.data.raw.data = GNUNET_malloc (sizeof (struct in6_addr));
238           rec.class = GNUNET_DNSPARSER_CLASS_INTERNET;
239           rec.type = GNUNET_DNSPARSER_TYPE_AAAA;
240           memcpy (rec.data.raw.data,
241                   rd[i].data,
242                   rd[i].data_size);
243           rec.data.raw.data_len = sizeof (struct in6_addr);
244           GNUNET_array_append (packet->answers,
245                                packet->num_answers,
246                                rec);
247           break;
248         case GNUNET_DNSPARSER_TYPE_CNAME:
249           rec.name = GNUNET_strdup (packet->queries[0].name);
250           rec.data.hostname = strdup (rd[i].data);
251           rec.class = GNUNET_DNSPARSER_CLASS_INTERNET;
252           rec.type = GNUNET_DNSPARSER_TYPE_CNAME;
253           memcpy (rec.data.hostname,
254                   rd[i].data,
255                   rd[i].data_size);
256           GNUNET_array_append (packet->answers,
257                                packet->num_answers,
258                                rec);
259           break;
260         default:
261           /* skip */
262           break;
263         }
264     }
265   send_response (request);
266 }
267
268
269 /**
270  * Handle DNS request.
271  *
272  * @param lsock socket to use for sending the reply
273  * @param addr address to use for sending the reply
274  * @param addr_len number of bytes in addr
275  * @param udp_msg DNS request payload
276  * @param udp_msg_size number of bytes in udp_msg 
277  */
278 static void
279 handle_request (struct GNUNET_NETWORK_Handle *lsock,
280                 const void *addr,
281                 size_t addr_len,
282                 const char *udp_msg,
283                 size_t udp_msg_size)
284 {
285   struct Request *request;
286   struct GNUNET_DNSPARSER_Packet *packet;
287   char *name;
288   size_t name_len;
289   enum GNUNET_GNS_RecordType type;
290   int use_gns;
291
292   packet = GNUNET_DNSPARSER_parse (udp_msg, udp_msg_size);
293   if (NULL == packet)
294     {
295       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
296                   _("Cannot parse DNS request from %s\n"),
297                   GNUNET_a2s (addr, addr_len));
298       return;
299     }
300   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
301                   "%d, %d, %d, %d\n",
302                   packet->flags.query_or_response,
303                   packet->num_answers,
304                   packet->num_authority_records,
305                   packet->num_additional_records);
306   if ( (0 != packet->flags.query_or_response) || 
307        (0 != packet->num_answers) ||
308        (0 != packet->num_authority_records))
309     {
310       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
311                   _("Received malformed DNS request from %s\n"),
312                   GNUNET_a2s (addr, addr_len));
313       GNUNET_DNSPARSER_free_packet (packet);
314       return;
315     }
316   if ( (1 != packet->num_queries) )
317     {
318       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
319                   _("Received unsupported DNS request from %s\n"),
320                   GNUNET_a2s (addr, addr_len));
321       GNUNET_DNSPARSER_free_packet (packet);
322       return;
323     }
324   request = GNUNET_malloc (sizeof (struct Request) + addr_len);
325   request->lsock = lsock;
326   request->packet = packet;
327   request->addr = &request[1];
328   request->addr_len = addr_len;
329   memcpy (&request[1], addr, addr_len);
330   request->timeout_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT,
331                                                         &do_timeout,
332                                                         request);
333   name = GNUNET_strdup (packet->queries[0].name);
334   name_len = strlen (name);
335   use_gns = GNUNET_NO;
336   if ( (name_len > strlen (".zkey.eu")) &&
337        (0 == strcasecmp (".zkey.eu",
338                          &name[name_len - strlen (".zkey.eu")])) )
339     {
340       if (0 == strcasecmp ("fcfs.zkey.eu",
341                            &name[name_len - strlen ("fcfs.zkey.eu")]))
342       {
343         name[name_len - strlen ("zkey.eu")] = '\0';
344         strcat (name, ".gnunet");
345       }
346       else
347         name[name_len - strlen (".eu")] = '\0';
348       name_len = strlen (name);
349     }
350   if ( (name_len > strlen (".gnunet")) &&
351        (0 == strcasecmp (".gnunet",
352                          &name[name_len - strlen (".gnunet")])) )
353     use_gns = GNUNET_YES;
354
355   if ( (name_len > strlen (".zkey")) &&
356        (0 == strcasecmp (".zkey",
357                          &name[name_len - strlen (".zkey")])) )
358     use_gns = GNUNET_YES;
359
360   if (GNUNET_YES == use_gns)
361   {
362       type = packet->queries[0].type;
363       request->lookup = GNUNET_GNS_lookup (gns,
364                                            name,
365                                            type,
366                                            GNUNET_NO,
367                                            NULL,
368                                            &result_processor,
369                                            request);
370     }
371   else
372     {
373       /* FIXME: do traditional *DNS* lookup; note that
374          gnunet-service-dns already has code to do this;
375          factor into library to share! Why not use GNUNET_RESOLVER here?*/
376       GNUNET_break (0);
377     }
378   GNUNET_free (name);
379 }
380
381
382 /**
383  * Task to read IPv4 DNS packets.
384  *
385  * @param cls the 'listen_socket4'
386  * @param tc scheduler context
387  */ 
388 static void
389 read_dns4 (void *cls,
390            const struct GNUNET_SCHEDULER_TaskContext *tc)
391 {
392   struct sockaddr_in v4;
393   socklen_t addrlen;
394   ssize_t size;
395
396   GNUNET_assert (listen_socket4 == cls);
397   t4 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
398                                       listen_socket4,
399                                       &read_dns4,
400                                       listen_socket4);
401   if (0 == (GNUNET_SCHEDULER_REASON_READ_READY & tc->reason))
402     return; /* shutdown? */
403   size = GNUNET_NETWORK_socket_recvfrom_amount (listen_socket4);
404   if (0 > size)
405     {
406       GNUNET_break (0);
407       return; /* read error!? */
408     }
409   {
410     char buf[size];
411     
412     addrlen = sizeof (v4);
413     GNUNET_break (size == 
414                   GNUNET_NETWORK_socket_recvfrom (listen_socket4,
415                                                   buf,
416                                                   size,
417                                                   (struct sockaddr *) &v4,
418                                                   &addrlen));
419     handle_request (listen_socket4, &v4, addrlen,
420                     buf, size);
421   }
422 }
423
424
425 /**
426  * Task to read IPv6 DNS packets.
427  *
428  * @param cls the 'listen_socket6'
429  * @param tc scheduler context
430  */ 
431 static void
432 read_dns6 (void *cls,
433            const struct GNUNET_SCHEDULER_TaskContext *tc)
434 {
435   struct sockaddr_in6 v6;
436   socklen_t addrlen;
437   ssize_t size;
438
439   GNUNET_assert (listen_socket6 == cls);
440   t6 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
441                                       listen_socket6,
442                                       &read_dns6,
443                                       listen_socket6);
444   if (0 == (GNUNET_SCHEDULER_REASON_READ_READY & tc->reason))
445     return; /* shutdown? */
446   size = GNUNET_NETWORK_socket_recvfrom_amount (listen_socket6);
447   if (0 > size)
448     {
449       GNUNET_break (0);
450       return; /* read error!? */
451     }
452   {
453     char buf[size];
454     
455     addrlen = sizeof (v6);
456     GNUNET_break (size == 
457                   GNUNET_NETWORK_socket_recvfrom (listen_socket6,
458                                                   buf,
459                                                   size,
460                                                   (struct sockaddr *) &v6,
461                                                   &addrlen));
462     handle_request (listen_socket6, &v6, addrlen,
463                     buf, size);
464   }
465 }
466
467
468 /**
469  * Main function that will be run.
470  *
471  * @param cls closure
472  * @param args remaining command-line arguments
473  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
474  * @param cfg configuration
475  */
476 static void
477 run (void *cls, char *const *args, const char *cfgfile,
478      const struct GNUNET_CONFIGURATION_Handle *cfg)
479 {
480   gns = GNUNET_GNS_connect (cfg);
481   if (NULL == gns)
482     return;
483   listen_socket4 = GNUNET_NETWORK_socket_create (PF_INET,
484                                                  SOCK_DGRAM, 
485                                                  IPPROTO_UDP);
486   if (NULL != listen_socket4)
487     {
488       struct sockaddr_in v4;
489
490       memset (&v4, 0, sizeof (v4));
491       v4.sin_family = AF_INET;
492 #if HAVE_SOCKADDR_IN_SIN_LEN
493       v4.sin_len = sizeof (v4);
494 #endif
495       v4.sin_port = htons (53);
496       if (GNUNET_OK !=
497           GNUNET_NETWORK_socket_bind (listen_socket4,
498                                       (struct sockaddr *) &v4,
499                                       sizeof (v4)))
500         {
501           GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "bind");
502           GNUNET_NETWORK_socket_close (listen_socket4);
503           listen_socket4 = NULL;
504         }
505     }
506   listen_socket6 = GNUNET_NETWORK_socket_create (PF_INET6,
507                                                 SOCK_DGRAM, 
508                                                 IPPROTO_UDP);
509   if (NULL != listen_socket6)
510     {
511       struct sockaddr_in6 v6;
512
513       memset (&v6, 0, sizeof (v6));
514       v6.sin6_family = AF_INET6;
515 #if HAVE_SOCKADDR_IN_SIN_LEN
516       v6.sin6_len = sizeof (v6);
517 #endif
518       v6.sin6_port = htons (53);
519       if (GNUNET_OK !=
520           GNUNET_NETWORK_socket_bind (listen_socket6,
521                                       (struct sockaddr *) &v6,
522                                       sizeof (v6)))
523         {
524           GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "bind");
525           GNUNET_NETWORK_socket_close (listen_socket6);
526           listen_socket6 = NULL;
527         }
528     }
529   if ( (NULL == listen_socket4) &&
530        (NULL == listen_socket6) )
531     return;
532   if (NULL != listen_socket4)
533     t4 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
534                                         listen_socket4,
535                                         &read_dns4,
536                                         listen_socket4);
537   if (NULL != listen_socket6)
538     t6 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
539                                         listen_socket6,
540                                         &read_dns6,
541                                         listen_socket6);
542
543   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
544                                 &do_shutdown, NULL);
545 }
546
547
548 /**
549  * The main function for the fcfs daemon.
550  *
551  * @param argc number of arguments from the command line
552  * @param argv command line arguments
553  * @return 0 ok, 1 on error
554  */
555 int
556 main (int argc, 
557       char *const *argv)
558 {
559   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
560     GNUNET_GETOPT_OPTION_END
561   };
562   int ret;
563
564   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv,
565                                                  &argc, &argv))
566     return 2;
567   GNUNET_log_setup ("gnunet-dns2gns", "WARNING", NULL);
568   ret =
569       (GNUNET_OK ==
570        GNUNET_PROGRAM_run (argc, argv, "gnunet-dns2gns",
571                            _("GNUnet DNS-to-GNS proxy (a DNS server)"), 
572                            options,
573                            &run, NULL)) ? 0 : 1;
574
575   return ret;
576 }
577
578 /* end of gnunet-dns2gns.c */