fixing compiler warnings
[oweals/gnunet.git] / src / dns / dnsstub.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2012 GNUnet e.V.
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., 51 Franklin Street, Fifth Floor,
18      Boston, MA 02110-1301, USA.
19 */
20 /**
21  * @file dns/dnsstub.c
22  * @brief DNS stub resolver which sends DNS requests to an actual resolver
23  * @author Christian Grothoff
24  */
25 #include "platform.h"
26 #include "gnunet_util_lib.h"
27 #include "gnunet_dnsstub_lib.h"
28
29 /**
30  * Timeout for an external (Internet-DNS) DNS resolution
31  */
32 #define REQUEST_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
33
34 /**
35  * How many DNS sockets do we open at most at the same time?
36  * (technical socket maximum is this number x2 for IPv4+IPv6)
37  */
38 #define DNS_SOCKET_MAX 128
39
40
41 /**
42  * UDP socket we are using for sending DNS requests to the Internet.
43  */
44 struct GNUNET_DNSSTUB_RequestSocket
45 {
46
47   /**
48    * UDP socket we use for this request for IPv4
49    */
50   struct GNUNET_NETWORK_Handle *dnsout4;
51
52   /**
53    * UDP socket we use for this request for IPv6
54    */
55   struct GNUNET_NETWORK_Handle *dnsout6;
56
57   /**
58    * Function to call with result.
59    */
60   GNUNET_DNSSTUB_ResultCallback rc;
61
62   /**
63    * Closure for @e rc.
64    */
65   void *rc_cls;
66
67   /**
68    * Task for reading from dnsout4 and dnsout6.
69    */
70   struct GNUNET_SCHEDULER_Task * read_task;
71
72   /**
73    * When should this request time out?
74    */
75   struct GNUNET_TIME_Absolute timeout;
76
77   /**
78    * Address we sent the DNS request to.
79    */
80   struct sockaddr_storage addr;
81
82   /**
83    * Number of bytes in @e addr.
84    */
85   socklen_t addrlen;
86
87 };
88
89
90 /**
91  * Handle to the stub resolver.
92  */
93 struct GNUNET_DNSSTUB_Context
94 {
95
96   /**
97    * Array of all open sockets for DNS requests.
98    */
99   struct GNUNET_DNSSTUB_RequestSocket sockets[DNS_SOCKET_MAX];
100
101   /**
102    * IP address to use for the DNS server if we are a DNS exit service
103    * (for VPN via cadet); otherwise NULL.
104    */
105   char *dns_exit;
106 };
107
108
109
110 /**
111  * We're done with a `struct GNUNET_DNSSTUB_RequestSocket`, close it for now.
112  *
113  * @param rs request socket to clean up
114  */
115 static void
116 cleanup_rs (struct GNUNET_DNSSTUB_RequestSocket *rs)
117 {
118   if (NULL != rs->dnsout4)
119   {
120     GNUNET_NETWORK_socket_close (rs->dnsout4);
121     rs->dnsout4 = NULL;
122   }
123   if (NULL != rs->dnsout6)
124   {
125     GNUNET_NETWORK_socket_close (rs->dnsout6);
126     rs->dnsout6 = NULL;
127   }
128   if (NULL != rs->read_task)
129   {
130     GNUNET_SCHEDULER_cancel (rs->read_task);
131     rs->read_task = NULL;
132   }
133 }
134
135
136 /**
137  * Open source port for sending DNS requests
138  *
139  * @param af AF_INET or AF_INET6
140  * @return #GNUNET_OK on success
141  */
142 static struct GNUNET_NETWORK_Handle *
143 open_socket (int af)
144 {
145   struct sockaddr_in a4;
146   struct sockaddr_in6 a6;
147   struct sockaddr *sa;
148   socklen_t alen;
149   struct GNUNET_NETWORK_Handle *ret;
150
151   ret = GNUNET_NETWORK_socket_create (af, SOCK_DGRAM, 0);
152   if (NULL == ret)
153     return NULL;
154   switch (af)
155   {
156   case AF_INET:
157     memset (&a4, 0, alen = sizeof (struct sockaddr_in));
158     sa = (struct sockaddr *) &a4;
159     break;
160   case AF_INET6:
161     memset (&a6, 0, alen = sizeof (struct sockaddr_in6));
162     sa = (struct sockaddr *) &a6;
163     break;
164   default:
165     GNUNET_break (0);
166     GNUNET_NETWORK_socket_close (ret);
167     return NULL;
168   }
169   sa->sa_family = af;
170   if (GNUNET_OK != GNUNET_NETWORK_socket_bind (ret,
171                                                sa,
172                                                alen))
173   {
174     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
175                 _("Could not bind to any port: %s\n"),
176                 STRERROR (errno));
177     GNUNET_NETWORK_socket_close (ret);
178     return NULL;
179   }
180   return ret;
181 }
182
183
184 /**
185  * Read a DNS response from the (unhindered) UDP-Socket
186  *
187  * @param cls socket to read from
188  */
189 static void
190 read_response (void *cls);
191
192
193 /**
194  * Get a socket of the specified address family to send out a
195  * UDP DNS request to the Internet.
196  *
197  * @param ctx the DNSSTUB context
198  * @param af desired address family
199  * @return NULL on error (given AF not "supported")
200  */
201 static struct GNUNET_DNSSTUB_RequestSocket *
202 get_request_socket (struct GNUNET_DNSSTUB_Context *ctx,
203                     int af)
204 {
205   struct GNUNET_DNSSTUB_RequestSocket *rs;
206   struct GNUNET_NETWORK_FDSet *rset;
207
208   rs = &ctx->sockets[GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE,
209                                                DNS_SOCKET_MAX)];
210   rs->timeout = GNUNET_TIME_relative_to_absolute (REQUEST_TIMEOUT);
211   switch (af)
212   {
213   case AF_INET:
214     if (NULL == rs->dnsout4)
215       rs->dnsout4 = open_socket (AF_INET);
216     break;
217   case AF_INET6:
218     if (NULL == rs->dnsout6)
219       rs->dnsout6 = open_socket (AF_INET6);
220     break;
221   default:
222     return NULL;
223   }
224   if (NULL != rs->read_task)
225   {
226     GNUNET_SCHEDULER_cancel (rs->read_task);
227     rs->read_task = NULL;
228   }
229   if ( (NULL == rs->dnsout4) &&
230        (NULL == rs->dnsout6) )
231     return NULL;
232   rset = GNUNET_NETWORK_fdset_create ();
233   if (NULL != rs->dnsout4)
234     GNUNET_NETWORK_fdset_set (rset, rs->dnsout4);
235   if (NULL != rs->dnsout6)
236     GNUNET_NETWORK_fdset_set (rset, rs->dnsout6);
237   rs->read_task = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
238                                                REQUEST_TIMEOUT,
239                                                rset,
240                                                NULL,
241                                                &read_response, rs);
242   GNUNET_NETWORK_fdset_destroy (rset);
243   return rs;
244 }
245
246
247 /**
248  * Perform DNS resolution.
249  *
250  * @param ctx stub resolver to use
251  * @param sa the socket address
252  * @param sa_len the socket length
253  * @param request DNS request to transmit
254  * @param request_len number of bytes in msg
255  * @param rc function to call with result
256  * @param rc_cls closure for 'rc'
257  * @return socket used for the request, NULL on error
258  */
259 struct GNUNET_DNSSTUB_RequestSocket *
260 GNUNET_DNSSTUB_resolve (struct GNUNET_DNSSTUB_Context *ctx,
261                         const struct sockaddr *sa,
262                         socklen_t sa_len,
263                         const void *request,
264                         size_t request_len,
265                         GNUNET_DNSSTUB_ResultCallback rc,
266                         void *rc_cls)
267 {
268   struct GNUNET_DNSSTUB_RequestSocket *rs;
269   struct GNUNET_NETWORK_Handle *ret;
270   int af;
271
272   af = sa->sa_family;
273   if (NULL == (rs = get_request_socket (ctx, af)))
274     return NULL;
275   if (NULL != rs->dnsout4)
276     ret = rs->dnsout4;
277   else
278     ret = rs->dnsout6;
279   GNUNET_assert (NULL != ret);
280   memcpy (&rs->addr,
281           sa,
282           sa_len);
283   rs->addrlen = sa_len;
284   rs->rc = rc;
285   rs->rc_cls = rc_cls;
286   if (GNUNET_SYSERR ==
287       GNUNET_NETWORK_socket_sendto (ret,
288                                     request,
289                                     request_len,
290                                     sa,
291                                     sa_len))
292     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
293                 _("Failed to send DNS request to %s\n"),
294                 GNUNET_a2s (sa, sa_len));
295   else
296     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
297                 _("Sent DNS request to %s\n"),
298                 GNUNET_a2s (sa, sa_len));
299   return rs;
300 }
301
302
303 /**
304  * Perform DNS resolution using our default IP from init.
305  *
306  * @param ctx stub resolver to use
307  * @param request DNS request to transmit
308  * @param request_len number of bytes in msg
309  * @param rc function to call with result
310  * @param rc_cls closure for 'rc'
311  * @return socket used for the request, NULL on error
312  */
313 struct GNUNET_DNSSTUB_RequestSocket *
314 GNUNET_DNSSTUB_resolve2 (struct GNUNET_DNSSTUB_Context *ctx,
315                          const void *request,
316                          size_t request_len,
317                          GNUNET_DNSSTUB_ResultCallback rc,
318                          void *rc_cls)
319 {
320   int af;
321   struct sockaddr_in v4;
322   struct sockaddr_in6 v6;
323   struct sockaddr *sa;
324   socklen_t salen;
325   struct GNUNET_NETWORK_Handle *dnsout;
326   struct GNUNET_DNSSTUB_RequestSocket *rs;
327
328   memset (&v4, 0, sizeof (v4));
329   memset (&v6, 0, sizeof (v6));
330   if (1 == inet_pton (AF_INET, ctx->dns_exit, &v4.sin_addr))
331   {
332     salen = sizeof (v4);
333     v4.sin_family = AF_INET;
334     v4.sin_port = htons (53);
335 #if HAVE_SOCKADDR_IN_SIN_LEN
336     v4.sin_len = (u_char) salen;
337 #endif
338     sa = (struct sockaddr *) &v4;
339     af = AF_INET;
340   }
341   else if (1 == inet_pton (AF_INET6, ctx->dns_exit, &v6.sin6_addr))
342   {
343     salen = sizeof (v6);
344     v6.sin6_family = AF_INET6;
345     v6.sin6_port = htons (53);
346 #if HAVE_SOCKADDR_IN_SIN_LEN
347     v6.sin6_len = (u_char) salen;
348 #endif
349     sa = (struct sockaddr *) &v6;
350     af = AF_INET6;
351   }
352   else
353   {
354     GNUNET_break (0);
355     return NULL;
356   }
357   if (NULL == (rs = get_request_socket (ctx, af)))
358     return NULL;
359   if (NULL != rs->dnsout4)
360     dnsout = rs->dnsout4;
361   else
362     dnsout = rs->dnsout6;
363   if (NULL == dnsout)
364   {
365     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
366                 _("Configured DNS exit `%s' is not working / valid.\n"),
367                 ctx->dns_exit);
368     return NULL;
369   }
370   memcpy (&rs->addr,
371           sa,
372           salen);
373   rs->addrlen = salen;
374   rs->rc = rc;
375   rs->rc_cls = rc_cls;
376   if (GNUNET_SYSERR ==
377       GNUNET_NETWORK_socket_sendto (dnsout,
378                                     request,
379                                     request_len, sa, salen))
380     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
381                 _("Failed to send DNS request to %s\n"),
382                 GNUNET_a2s (sa, salen));
383   rs->timeout = GNUNET_TIME_relative_to_absolute (REQUEST_TIMEOUT);
384
385   return rs;
386
387 }
388
389
390 /**
391  * Actually do the reading of a DNS packet from our UDP socket and see
392  * if we have a valid, matching, pending request.
393  *
394  * @param rs request socket with callback details
395  * @param dnsout socket to read from
396  * @return #GNUNET_OK on success, #GNUNET_NO on drop, #GNUNET_SYSERR on IO-errors (closed socket)
397  */
398 static int
399 do_dns_read (struct GNUNET_DNSSTUB_RequestSocket *rs,
400              struct GNUNET_NETWORK_Handle *dnsout)
401 {
402   struct sockaddr_storage addr;
403   socklen_t addrlen;
404   struct GNUNET_TUN_DnsHeader *dns;
405   ssize_t r;
406   int len;
407
408 #ifndef MINGW
409   if (0 != ioctl (GNUNET_NETWORK_get_fd (dnsout), FIONREAD, &len))
410   {
411     /* conservative choice: */
412     len = UINT16_MAX;
413   }
414 #else
415   /* port the code above? */
416   len = UINT16_MAX;
417 #endif
418   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
419               "Receiving %d byte DNS reply\n",
420               len);
421   {
422     unsigned char buf[len] GNUNET_ALIGN;
423
424     addrlen = sizeof (addr);
425     memset (&addr, 0, sizeof (addr));
426     r = GNUNET_NETWORK_socket_recvfrom (dnsout,
427                                         buf, sizeof (buf),
428                                         (struct sockaddr*) &addr, &addrlen);
429     if (-1 == r)
430     {
431       GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "recvfrom");
432       GNUNET_NETWORK_socket_close (dnsout);
433       return GNUNET_SYSERR;
434     }
435     if (sizeof (struct GNUNET_TUN_DnsHeader) > r)
436     {
437       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
438                   _("Received DNS response that is too small (%u bytes)"),
439                   (unsigned int) r);
440       return GNUNET_NO;
441     }
442     dns = (struct GNUNET_TUN_DnsHeader *) buf;
443     if ( (addrlen != rs->addrlen) ||
444          (0 != memcmp (&rs->addr,
445                        &addr,
446                        addrlen)) ||
447        (0 == GNUNET_TIME_absolute_get_remaining (rs->timeout).rel_value_us) )
448     {
449       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
450                   "Request timeout or invalid sender address; ignoring reply\n");
451       return GNUNET_NO;
452     }
453     if (NULL != rs->rc)
454       rs->rc (rs->rc_cls,
455               rs,
456               dns,
457               r);
458   }
459   return GNUNET_OK;
460 }
461
462
463 /**
464  * Read a DNS response from the (unhindered) UDP-Socket
465  *
466  * @param cls socket to read from
467  */
468 static void
469 read_response (void *cls)
470 {
471   struct GNUNET_DNSSTUB_RequestSocket *rs = cls;
472   struct GNUNET_NETWORK_FDSet *rset;
473   const struct GNUNET_SCHEDULER_TaskContext *tc;
474
475   rs->read_task = NULL;
476   tc = GNUNET_SCHEDULER_get_task_context ();
477   if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_READ_READY))
478   {
479     /* timeout */
480     cleanup_rs (rs);
481     return;
482   }
483   /* read and process ready sockets */
484   if ((NULL != rs->dnsout4) &&
485       (GNUNET_NETWORK_fdset_isset (tc->read_ready, rs->dnsout4)) &&
486       (GNUNET_SYSERR == do_dns_read (rs, rs->dnsout4)))
487     rs->dnsout4 = NULL;
488   if ((NULL != rs->dnsout6) &&
489       (GNUNET_NETWORK_fdset_isset (tc->read_ready, rs->dnsout6)) &&
490       (GNUNET_SYSERR == do_dns_read (rs, rs->dnsout6)))
491     rs->dnsout6 = NULL;
492
493   /* re-schedule read task */
494   rset = GNUNET_NETWORK_fdset_create ();
495   if (NULL != rs->dnsout4)
496     GNUNET_NETWORK_fdset_set (rset, rs->dnsout4);
497   if (NULL != rs->dnsout6)
498     GNUNET_NETWORK_fdset_set (rset, rs->dnsout6);
499   rs->read_task = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
500                                                GNUNET_TIME_absolute_get_remaining (rs->timeout),
501                                                rset,
502                                                NULL,
503                                                &read_response, rs);
504   GNUNET_NETWORK_fdset_destroy (rset);
505 }
506
507
508 /**
509  * Cancel DNS resolution.
510  *
511  * @param rs resolution to cancel
512  */
513 void
514 GNUNET_DNSSTUB_resolve_cancel (struct GNUNET_DNSSTUB_RequestSocket *rs)
515 {
516   rs->rc = NULL;
517 }
518
519
520 /**
521  * Start a DNS stub resolver.
522  *
523  * @param dns_ip target IP address to use
524  * @return NULL on error
525  */
526 struct GNUNET_DNSSTUB_Context *
527 GNUNET_DNSSTUB_start (const char *dns_ip)
528 {
529   struct GNUNET_DNSSTUB_Context *ctx;
530
531   ctx = GNUNET_new (struct GNUNET_DNSSTUB_Context);
532   if (NULL != dns_ip)
533     ctx->dns_exit = GNUNET_strdup (dns_ip);
534   return ctx;
535 }
536
537
538 /**
539  * Cleanup DNSSTUB resolver.
540  *
541  * @param ctx stub resolver to clean up
542  */
543 void
544 GNUNET_DNSSTUB_stop (struct GNUNET_DNSSTUB_Context *ctx)
545 {
546   unsigned int i;
547
548   for (i=0;i<DNS_SOCKET_MAX;i++)
549     cleanup_rs (&ctx->sockets[i]);
550   if (NULL != ctx->dns_exit)
551   {
552     GNUNET_free (ctx->dns_exit);
553     ctx->dns_exit = NULL;
554   }
555   GNUNET_free (ctx);
556 }
557
558
559 /* end of dnsstub.c */