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