- delayed requests correctly when in 'begin' round
[oweals/gnunet.git] / src / dns / dnsstub.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 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 'rc'.
64    */
65   void *rc_cls;
66
67   /**
68    * Task for reading from dnsout4 and dnsout6.
69    */
70   GNUNET_SCHEDULER_TaskIdentifier 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 '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 mesh); otherwise NULL.
104    */
105   char *dns_exit;
106 };
107
108
109
110 /**
111  * We're done with a 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 (GNUNET_SCHEDULER_NO_TASK != rs->read_task)
129   {
130     GNUNET_SCHEDULER_cancel (rs->read_task);
131     rs->read_task = GNUNET_SCHEDULER_NO_TASK;
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                                                0))
174   {
175     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 
176                 _("Could not bind to any port: %s\n"),
177                 STRERROR (errno));
178     GNUNET_NETWORK_socket_close (ret);
179     return NULL;
180   }
181   return ret;
182 }
183
184
185 /**
186  * Read a DNS response from the (unhindered) UDP-Socket
187  *
188  * @param cls socket to read from
189  * @param tc scheduler context (must be shutdown or read ready)
190  */
191 static void
192 read_response (void *cls,
193                const struct GNUNET_SCHEDULER_TaskContext *tc);
194
195
196 /**
197  * Get a socket of the specified address family to send out a
198  * UDP DNS request to the Internet.  
199  *
200  * @param ctx the DNSSTUB context
201  * @param af desired address family
202  * @return NULL on error (given AF not "supported")
203  */
204 static struct GNUNET_DNSSTUB_RequestSocket *
205 get_request_socket (struct GNUNET_DNSSTUB_Context *ctx,
206                     int af)
207 {
208   struct GNUNET_DNSSTUB_RequestSocket *rs;
209   struct GNUNET_NETWORK_FDSet *rset;
210
211   rs = &ctx->sockets[GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, 
212                                                DNS_SOCKET_MAX)];
213   rs->timeout = GNUNET_TIME_relative_to_absolute (REQUEST_TIMEOUT);
214   switch (af)
215   {
216   case AF_INET:
217     if (NULL == rs->dnsout4)
218       rs->dnsout4 = open_socket (AF_INET);
219     break;
220   case AF_INET6:
221     if (NULL == rs->dnsout6)
222       rs->dnsout6 = open_socket (AF_INET6);
223     break;
224   default:
225     return NULL;
226   }  
227   if (GNUNET_SCHEDULER_NO_TASK != rs->read_task)
228   {
229     GNUNET_SCHEDULER_cancel (rs->read_task);
230     rs->read_task = GNUNET_SCHEDULER_NO_TASK;
231   }
232   if ( (NULL == rs->dnsout4) &&
233        (NULL == rs->dnsout6) )
234     return NULL;
235   rset = GNUNET_NETWORK_fdset_create ();
236   if (NULL != rs->dnsout4)
237     GNUNET_NETWORK_fdset_set (rset, rs->dnsout4);
238   if (NULL != rs->dnsout6)
239     GNUNET_NETWORK_fdset_set (rset, rs->dnsout6);
240   rs->read_task = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
241                                                REQUEST_TIMEOUT,
242                                                rset,
243                                                NULL,
244                                                &read_response, rs);
245   GNUNET_NETWORK_fdset_destroy (rset);
246   return rs;
247 }
248
249
250 /**
251  * Perform DNS resolution.
252  *
253  * @param ctx stub resolver to use
254  * @param sa the socket address
255  * @param sa_len the socket length
256  * @param request DNS request to transmit
257  * @param request_len number of bytes in msg
258  * @param rc function to call with result
259  * @param rc_cls closure for 'rc'
260  * @return socket used for the request, NULL on error
261  */
262 struct GNUNET_DNSSTUB_RequestSocket *
263 GNUNET_DNSSTUB_resolve (struct GNUNET_DNSSTUB_Context *ctx,
264                         const struct sockaddr *sa,
265                         socklen_t sa_len,
266                         const void *request,
267                         size_t request_len,
268                         GNUNET_DNSSTUB_ResultCallback rc,
269                         void *rc_cls)
270 {
271   struct GNUNET_DNSSTUB_RequestSocket *rs;
272   struct GNUNET_NETWORK_Handle *ret;
273   int af;
274
275   af = sa->sa_family;
276   if (NULL == (rs = get_request_socket (ctx, af)))
277     return NULL;
278   if (NULL != rs->dnsout4)
279     ret = rs->dnsout4;
280   else
281     ret = rs->dnsout6;
282   GNUNET_assert (NULL != ret);
283   memcpy (&rs->addr,
284           sa,
285           sa_len);
286   rs->addrlen = sa_len;
287   rs->rc = rc;
288   rs->rc_cls = rc_cls;
289   if (GNUNET_SYSERR == 
290       GNUNET_NETWORK_socket_sendto (ret,
291                                     request,
292                                     request_len,
293                                     sa,
294                                     sa_len))
295     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
296                 _("Failed to send DNS request to %s\n"),
297                 GNUNET_a2s (sa, sa_len));
298   else
299     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
300                 _("Sent DNS request to %s\n"),
301                 GNUNET_a2s (sa, sa_len));
302   return rs;
303 }
304
305
306 /**
307  * Perform DNS resolution using our default IP from init.
308  *
309  * @param ctx stub resolver to use
310  * @param request DNS request to transmit
311  * @param request_len number of bytes in msg
312  * @param rc function to call with result
313  * @param rc_cls closure for 'rc'
314  * @return socket used for the request, NULL on error
315  */
316 struct GNUNET_DNSSTUB_RequestSocket *
317 GNUNET_DNSSTUB_resolve2 (struct GNUNET_DNSSTUB_Context *ctx,
318                          const void *request,
319                          size_t request_len,
320                          GNUNET_DNSSTUB_ResultCallback rc,
321                          void *rc_cls)
322 {
323   int af;
324   struct sockaddr_in v4;
325   struct sockaddr_in6 v6;
326   struct sockaddr *sa;
327   socklen_t salen;
328   struct GNUNET_NETWORK_Handle *dnsout;
329   struct GNUNET_DNSSTUB_RequestSocket *rs;
330
331   memset (&v4, 0, sizeof (v4));
332   memset (&v6, 0, sizeof (v6));
333   if (1 == inet_pton (AF_INET, ctx->dns_exit, &v4.sin_addr))
334   {
335     salen = sizeof (v4);
336     v4.sin_family = AF_INET;
337     v4.sin_port = htons (53);
338 #if HAVE_SOCKADDR_IN_SIN_LEN
339     v4.sin_len = (u_char) salen;
340 #endif
341     sa = (struct sockaddr *) &v4;
342     af = AF_INET;
343   }
344   else if (1 == inet_pton (AF_INET6, ctx->dns_exit, &v6.sin6_addr))
345   {
346     salen = sizeof (v6);
347     v6.sin6_family = AF_INET6;
348     v6.sin6_port = htons (53);
349 #if HAVE_SOCKADDR_IN_SIN_LEN
350     v6.sin6_len = (u_char) salen;
351 #endif
352     sa = (struct sockaddr *) &v6;
353     af = AF_INET6;
354   }  
355   else
356   {
357     GNUNET_break (0);
358     return NULL;
359   }
360   if (NULL == (rs = get_request_socket (ctx, af)))
361     return NULL;
362   if (NULL != rs->dnsout4)
363     dnsout = rs->dnsout4;
364   else
365     dnsout = rs->dnsout6;
366   if (NULL == dnsout)
367   {
368     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
369                 _("Configured DNS exit `%s' is not working / valid.\n"),
370                 ctx->dns_exit);
371     return NULL;
372   }
373   memcpy (&rs->addr,
374           sa,
375           salen);
376   rs->addrlen = salen;
377   rs->rc = rc;
378   rs->rc_cls = rc_cls;
379   if (GNUNET_SYSERR ==
380       GNUNET_NETWORK_socket_sendto (dnsout,
381                                     request,
382                                     request_len, sa, salen))
383     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
384                 _("Failed to send DNS request to %s\n"),
385                 GNUNET_a2s (sa, salen));
386   rs->timeout = GNUNET_TIME_relative_to_absolute (REQUEST_TIMEOUT);
387   
388   return rs;
389
390 }
391
392
393 /**
394  * Actually do the reading of a DNS packet from our UDP socket and see
395  * if we have a valid, matching, pending request.
396  *
397  * @param rs request socket with callback details
398  * @param dnsout socket to read from
399  * @return GNUNET_OK on success, GNUNET_NO on drop, GNUNET_SYSERR on IO-errors (closed socket)
400  */
401 static int
402 do_dns_read (struct GNUNET_DNSSTUB_RequestSocket *rs,
403              struct GNUNET_NETWORK_Handle *dnsout)
404 {
405   struct sockaddr_storage addr;
406   socklen_t addrlen;
407   struct GNUNET_TUN_DnsHeader *dns;
408   ssize_t r;
409   int len;
410
411 #ifndef MINGW
412   if (0 != ioctl (GNUNET_NETWORK_get_fd (dnsout), FIONREAD, &len))
413   {
414     /* conservative choice: */
415     len = UINT16_MAX;
416   }
417 #else
418   /* port the code above? */
419   len = UINT16_MAX;
420 #endif
421   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
422               "Receiving %d byte DNS reply\n",
423               len); 
424   {
425     unsigned char buf[len] GNUNET_ALIGN;
426
427     addrlen = sizeof (addr);
428     memset (&addr, 0, sizeof (addr));  
429     r = GNUNET_NETWORK_socket_recvfrom (dnsout, 
430                                         buf, sizeof (buf),
431                                         (struct sockaddr*) &addr, &addrlen);
432     if (-1 == r)
433     {
434       GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "recvfrom");
435       GNUNET_NETWORK_socket_close (dnsout);
436       return GNUNET_SYSERR;
437     }
438     if (sizeof (struct GNUNET_TUN_DnsHeader) > r)
439     {
440       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 
441                   _("Received DNS response that is too small (%u bytes)"),
442                   r);
443       return GNUNET_NO;
444     }
445     dns = (struct GNUNET_TUN_DnsHeader *) buf;
446     if ( (addrlen != rs->addrlen) ||
447          (0 != memcmp (&rs->addr,
448                        &addr,
449                        addrlen)) ||      
450        (0 == GNUNET_TIME_absolute_get_remaining (rs->timeout).rel_value_us) )
451     {
452       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
453                   "Request timeout or invalid sender address; ignoring reply\n"); 
454       return GNUNET_NO;
455     }
456     if (NULL != rs->rc)
457       rs->rc (rs->rc_cls,
458               rs,
459               dns,
460               r);
461   }  
462   return GNUNET_OK;
463 }
464
465
466 /**
467  * Read a DNS response from the (unhindered) UDP-Socket
468  *
469  * @param cls socket to read from
470  * @param tc scheduler context (must be shutdown or read ready)
471  */
472 static void
473 read_response (void *cls,
474                const struct GNUNET_SCHEDULER_TaskContext *tc)
475 {
476   struct GNUNET_DNSSTUB_RequestSocket *rs = cls;
477   struct GNUNET_NETWORK_FDSet *rset;
478
479   rs->read_task = GNUNET_SCHEDULER_NO_TASK;
480   if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_READ_READY))
481   {
482     /* timeout or shutdown */
483     cleanup_rs (rs);
484     return;
485   }
486   /* read and process ready sockets */
487   if ((NULL != rs->dnsout4) &&
488       (GNUNET_NETWORK_fdset_isset (tc->read_ready, rs->dnsout4)) &&
489       (GNUNET_SYSERR == do_dns_read (rs, rs->dnsout4)))
490     rs->dnsout4 = NULL;
491   if ((NULL != rs->dnsout6) &&
492       (GNUNET_NETWORK_fdset_isset (tc->read_ready, rs->dnsout6)) &&
493       (GNUNET_SYSERR == do_dns_read (rs, rs->dnsout6)))
494     rs->dnsout6 = NULL;
495
496   /* re-schedule read task */
497   rset = GNUNET_NETWORK_fdset_create ();
498   if (NULL != rs->dnsout4)
499     GNUNET_NETWORK_fdset_set (rset, rs->dnsout4);
500   if (NULL != rs->dnsout6)
501     GNUNET_NETWORK_fdset_set (rset, rs->dnsout6);
502   rs->read_task = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
503                                                GNUNET_TIME_absolute_get_remaining (rs->timeout),
504                                                rset,
505                                                NULL,
506                                                &read_response, rs);
507   GNUNET_NETWORK_fdset_destroy (rset);
508 }
509
510
511 /**
512  * Cancel DNS resolution.
513  *
514  * @param rs resolution to cancel
515  */
516 void
517 GNUNET_DNSSTUB_resolve_cancel (struct GNUNET_DNSSTUB_RequestSocket *rs)
518 {
519   rs->rc = NULL;
520 }
521
522
523 /**
524  * Start a DNS stub resolver.
525  *
526  * @param dns_ip target IP address to use
527  * @return NULL on error
528  */
529 struct GNUNET_DNSSTUB_Context *
530 GNUNET_DNSSTUB_start (const char *dns_ip)
531 {
532   struct GNUNET_DNSSTUB_Context *ctx;
533   
534   ctx = GNUNET_malloc (sizeof (struct GNUNET_DNSSTUB_Context));
535   if (NULL != dns_ip)
536     ctx->dns_exit = GNUNET_strdup (dns_ip);
537   return ctx;
538 }
539
540
541 /**
542  * Cleanup DNSSTUB resolver.
543  *
544  * @param ctx stub resolver to clean up
545  */
546 void
547 GNUNET_DNSSTUB_stop (struct GNUNET_DNSSTUB_Context *ctx)
548 {
549   unsigned int i;
550
551   for (i=0;i<DNS_SOCKET_MAX;i++)
552     cleanup_rs (&ctx->sockets[i]);
553   if (NULL != ctx->dns_exit)
554   {
555     GNUNET_free (ctx->dns_exit);
556     ctx->dns_exit = NULL;
557   }
558   GNUNET_free (ctx);
559 }
560
561
562 /* end of dnsstub.c */