cb7abe6ec91a3bdf26f4d7689c533be480326ca6
[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   GNUNET_DNSPARSER_free_packet (request->packet);
179   GNUNET_GNS_cancel_lookup_request (request->lookup);
180   GNUNET_free (request);
181 }
182
183
184 /**
185  * Iterator called on obtained result for a GNS
186  * lookup
187  *
188  * @param cls closure
189  * @param name "name" of the original lookup
190  * @param rd_count number of records
191  * @param rd the records in reply
192  */
193 static void
194 result_processor (void *cls,
195                   uint32_t rd_count,
196                   const struct GNUNET_NAMESTORE_RecordData *rd)
197 {
198   struct Request *request = cls;
199
200   // FIXME: is 'processor' called only once or
201   // possibly more than once?
202   request->lookup = NULL;
203   GNUNET_break (0);
204   // FIXME: convert 'rd' to response here...
205   send_response (request);
206 }
207
208
209 /**
210  * Handle DNS request.
211  *
212  * @param lsock socket to use for sending the reply
213  * @param addr address to use for sending the reply
214  * @param addr_len number of bytes in addr
215  * @param udp_msg DNS request payload
216  * @param udp_msg_size number of bytes in udp_msg 
217  */
218 static void
219 handle_request (struct GNUNET_NETWORK_Handle *lsock,
220                 const void *addr,
221                 size_t addr_len,
222                 const char *udp_msg,
223                 size_t udp_msg_size)
224 {
225   struct Request *request;
226   struct GNUNET_DNSPARSER_Packet *packet;
227
228   packet = GNUNET_DNSPARSER_parse (udp_msg, udp_msg_size);
229   if (NULL == packet)
230     {
231       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
232                   _("Received malformed DNS request from %s\n"),
233                   GNUNET_a2s (addr, addr_len));
234       return;
235     }
236   request = GNUNET_malloc (sizeof (struct Request) + addr_len);
237   request->lsock = lsock;
238   request->packet = packet;
239   request->addr = &request[1];
240   request->addr_len = addr_len;
241   memcpy (&request[1], addr, addr_len);
242   request->timeout_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT,
243                                                         &do_timeout,
244                                                         request);
245   // FIXME: extract name and type from 'request->packet'
246   const char *name = "foo";
247   enum GNUNET_GNS_RecordType type = GNUNET_GNS_RECORD_A;
248   request->lookup = GNUNET_GNS_lookup (gns,
249                                        name,
250                                        type,
251                                        GNUNET_NO,
252                                        NULL,
253                                        &result_processor,
254                                        request);
255 }
256
257
258 /**
259  * Task to read IPv4 DNS packets.
260  *
261  * @param cls the 'listen_socket4'
262  * @param tc scheduler context
263  */ 
264 static void
265 read_dns4 (void *cls,
266            const struct GNUNET_SCHEDULER_TaskContext *tc)
267 {
268   struct sockaddr_in v4;
269   socklen_t addrlen;
270   ssize_t size;
271
272   GNUNET_assert (listen_socket4 == cls);
273   t4 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
274                                       listen_socket4,
275                                       &read_dns4,
276                                       listen_socket4);
277   if (0 == (GNUNET_SCHEDULER_REASON_READ_READY & tc->reason))
278     return; /* shutdown? */
279   size = GNUNET_NETWORK_socket_recvfrom_amount (listen_socket4);
280   if (0 > size)
281     {
282       GNUNET_break (0);
283       return; /* read error!? */
284     }
285   {
286     char buf[size];
287     
288     addrlen = sizeof (v4);
289     GNUNET_break (size == 
290                   GNUNET_NETWORK_socket_recvfrom (listen_socket4,
291                                                   buf,
292                                                   size,
293                                                   (struct sockaddr *) &v4,
294                                                   &addrlen));
295     handle_request (listen_socket4, &v4, addrlen,
296                     buf, size);
297   }
298 }
299
300
301 /**
302  * Task to read IPv6 DNS packets.
303  *
304  * @param cls the 'listen_socket6'
305  * @param tc scheduler context
306  */ 
307 static void
308 read_dns6 (void *cls,
309            const struct GNUNET_SCHEDULER_TaskContext *tc)
310 {
311   struct sockaddr_in6 v6;
312   socklen_t addrlen;
313   ssize_t size;
314
315   GNUNET_assert (listen_socket6 == cls);
316   t6 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
317                                       listen_socket6,
318                                       &read_dns6,
319                                       listen_socket6);
320   if (0 == (GNUNET_SCHEDULER_REASON_READ_READY & tc->reason))
321     return; /* shutdown? */
322   size = GNUNET_NETWORK_socket_recvfrom_amount (listen_socket6);
323   if (0 > size)
324     {
325       GNUNET_break (0);
326       return; /* read error!? */
327     }
328   {
329     char buf[size];
330     
331     addrlen = sizeof (v6);
332     GNUNET_break (size == 
333                   GNUNET_NETWORK_socket_recvfrom (listen_socket6,
334                                                   buf,
335                                                   size,
336                                                   (struct sockaddr *) &v6,
337                                                   &addrlen));
338     handle_request (listen_socket6, &v6, addrlen,
339                     buf, size);
340   }
341 }
342
343
344 /**
345  * Main function that will be run.
346  *
347  * @param cls closure
348  * @param args remaining command-line arguments
349  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
350  * @param cfg configuration
351  */
352 static void
353 run (void *cls, char *const *args, const char *cfgfile,
354      const struct GNUNET_CONFIGURATION_Handle *cfg)
355 {
356   gns = GNUNET_GNS_connect (cfg);
357   if (NULL == gns)
358     return;
359   listen_socket4 = GNUNET_NETWORK_socket_create (PF_INET,
360                                                  SOCK_DGRAM, 
361                                                  IPPROTO_UDP);
362   if (NULL != listen_socket4)
363     {
364       struct sockaddr_in v4;
365
366       memset (&v4, 0, sizeof (v4));
367       v4.sin_family = AF_INET;
368 #if HAVE_SOCKADDR_IN_SIN_LEN
369       v4.sin_len = sizeof (v4);
370 #endif
371       v4.sin_port = htons (53);
372       if (GNUNET_OK !=
373           GNUNET_NETWORK_socket_bind (listen_socket4,
374                                       (struct sockaddr *) &v4,
375                                       sizeof (v4)))
376         {
377           GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "bind");
378           GNUNET_NETWORK_socket_close (listen_socket4);
379           listen_socket4 = NULL;
380         }
381     }
382   listen_socket6 = GNUNET_NETWORK_socket_create (PF_INET6,
383                                                 SOCK_DGRAM, 
384                                                 IPPROTO_UDP);
385   if (NULL != listen_socket6)
386     {
387       struct sockaddr_in6 v6;
388
389       memset (&v6, 0, sizeof (v6));
390       v6.sin6_family = AF_INET6;
391 #if HAVE_SOCKADDR_IN_SIN_LEN
392       v6.sin6_len = sizeof (v6);
393 #endif
394       v6.sin6_port = htons (53);
395       if (GNUNET_OK !=
396           GNUNET_NETWORK_socket_bind (listen_socket6,
397                                       (struct sockaddr *) &v6,
398                                       sizeof (v6)))
399         {
400           GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "bind");
401           GNUNET_NETWORK_socket_close (listen_socket6);
402           listen_socket6 = NULL;
403         }
404     }
405   if ( (NULL == listen_socket4) &&
406        (NULL == listen_socket6) )
407     return;
408   if (NULL != listen_socket4)
409     t4 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
410                                         listen_socket4,
411                                         &read_dns4,
412                                         listen_socket4);
413   if (NULL != listen_socket6)
414     t6 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
415                                         listen_socket6,
416                                         &read_dns6,
417                                         listen_socket6);
418
419   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
420                                 &do_shutdown, NULL);
421 }
422
423
424 /**
425  * The main function for the fcfs daemon.
426  *
427  * @param argc number of arguments from the command line
428  * @param argv command line arguments
429  * @return 0 ok, 1 on error
430  */
431 int
432 main (int argc, 
433       char *const *argv)
434 {
435   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
436     GNUNET_GETOPT_OPTION_END
437   };
438   int ret;
439
440   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv,
441                                                  &argc, &argv))
442     return 2;
443   GNUNET_log_setup ("gnunet-dns2gns", "WARNING", NULL);
444   ret =
445       (GNUNET_OK ==
446        GNUNET_PROGRAM_run (argc, argv, "gnunet-dns2gns",
447                            _("GNUnet DNS-to-GNS proxy (a DNS server)"), 
448                            options,
449                            &run, NULL)) ? 0 : 1;
450
451   return ret;
452 }
453
454 /* end of gnunet-dns2gns.c */