2cb62447c8a73f3e32acd205882c12ef1ab5637a
[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 struct GNUNET_DNSSTUB_Context
91 {
92
93   /**
94    * Array of all open sockets for DNS requests. 
95    */
96   struct GNUNET_DNSSTUB_RequestSocket sockets[DNS_SOCKET_MAX];
97
98   /**
99    * IP address to use for the DNS server if we are a DNS exit service
100    * (for VPN via mesh); otherwise NULL.
101    */
102   char *dns_exit;
103 };
104
105
106
107 /**
108  * We're done with a GNUNET_DNSSTUB_RequestSocket, close it for now.
109  *
110  * @param rs request socket to clean up
111  */
112 static void
113 cleanup_rs (struct GNUNET_DNSSTUB_RequestSocket *rs)
114 {
115   if (NULL != rs->dnsout4)
116   {
117     GNUNET_NETWORK_socket_close (rs->dnsout4);
118     rs->dnsout4 = NULL;
119   }
120   if (NULL != rs->dnsout6)
121   {
122     GNUNET_NETWORK_socket_close (rs->dnsout6);
123     rs->dnsout6 = NULL;
124   }
125   if (GNUNET_SCHEDULER_NO_TASK != rs->read_task)
126   {
127     GNUNET_SCHEDULER_cancel (rs->read_task);
128     rs->read_task = GNUNET_SCHEDULER_NO_TASK;
129   }
130 }
131
132
133 /**
134  * Open source port for sending DNS requests
135  *
136  * @param af AF_INET or AF_INET6
137  * @return GNUNET_OK on success
138  */ 
139 static struct GNUNET_NETWORK_Handle *
140 open_socket (int af)
141 {
142   struct sockaddr_in a4;
143   struct sockaddr_in6 a6;
144   struct sockaddr *sa;
145   socklen_t alen;
146   struct GNUNET_NETWORK_Handle *ret;
147
148   ret = GNUNET_NETWORK_socket_create (af, SOCK_DGRAM, 0);
149   if (NULL == ret)
150     return NULL;
151   switch (af)
152   {
153   case AF_INET:
154     memset (&a4, 0, alen = sizeof (struct sockaddr_in));
155     sa = (struct sockaddr *) &a4;
156     break;
157   case AF_INET6:
158     memset (&a6, 0, alen = sizeof (struct sockaddr_in6));
159     sa = (struct sockaddr *) &a6;
160     break;
161   default:
162     GNUNET_break (0);
163     GNUNET_NETWORK_socket_close (ret);
164     return NULL;
165   }
166   sa->sa_family = af;
167   if (GNUNET_OK != GNUNET_NETWORK_socket_bind (ret,
168                                                sa, 
169                                                alen))
170   {
171     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 
172                 _("Could not bind to any port: %s\n"),
173                 STRERROR (errno));
174     GNUNET_NETWORK_socket_close (ret);
175     return NULL;
176   }
177   return ret;
178 }
179
180
181 /**
182  * Read a DNS response from the (unhindered) UDP-Socket
183  *
184  * @param cls socket to read from
185  * @param tc scheduler context (must be shutdown or read ready)
186  */
187 static void
188 read_response (void *cls,
189                const struct GNUNET_SCHEDULER_TaskContext *tc);
190
191
192 /**
193  * Get a socket of the specified address family to send out a
194  * UDP DNS request to the Internet.  
195  *
196  * @param af desired address family
197  * @return NULL on error (given AF not "supported")
198  */
199 static struct GNUNET_DNSSTUB_RequestSocket *
200 get_request_socket (struct GNUNET_DNSSTUB_Context *ctx,
201                     int af)
202 {
203   struct GNUNET_DNSSTUB_RequestSocket *rs;
204   struct GNUNET_NETWORK_FDSet *rset;
205
206   rs = &ctx->sockets[GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE, 
207                                                DNS_SOCKET_MAX)];
208   rs->timeout = GNUNET_TIME_relative_to_absolute (REQUEST_TIMEOUT);
209   switch (af)
210   {
211   case AF_INET:
212     if (NULL == rs->dnsout4)
213       rs->dnsout4 = open_socket (AF_INET);
214     break;
215   case AF_INET6:
216     if (NULL == rs->dnsout6)
217       rs->dnsout6 = open_socket (AF_INET6);
218     break;
219   default:
220     return NULL;
221   }  
222   if (GNUNET_SCHEDULER_NO_TASK != rs->read_task)
223   {
224     GNUNET_SCHEDULER_cancel (rs->read_task);
225     rs->read_task = GNUNET_SCHEDULER_NO_TASK;
226   }
227   if ( (NULL == rs->dnsout4) &&
228        (NULL == rs->dnsout6) )
229     return NULL;
230   rset = GNUNET_NETWORK_fdset_create ();
231   if (NULL != rs->dnsout4)
232     GNUNET_NETWORK_fdset_set (rset, rs->dnsout4);
233   if (NULL != rs->dnsout6)
234     GNUNET_NETWORK_fdset_set (rset, rs->dnsout6);
235   rs->read_task = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
236                                                REQUEST_TIMEOUT,
237                                                rset,
238                                                NULL,
239                                                &read_response, rs);
240   GNUNET_NETWORK_fdset_destroy (rset);
241   return rs;
242 }
243
244
245 /**
246  * Perform DNS resolution.
247  *
248  * @param ctx stub resolver to use
249  * @param af address family to use
250  * @param request DNS request to transmit
251  * @param request_len number of bytes in msg
252  * @param rc function to call with result
253  * @param rc_cls closure for 'rc'
254  * @return socket used for the request, NULL on error
255  */
256 struct GNUNET_DNSSTUB_RequestSocket *
257 GNUNET_DNSSTUB_resolve (struct GNUNET_DNSSTUB_Context *ctx,
258                         const struct sockaddr *sa,
259                         socklen_t sa_len,
260                         const void *request,
261                         size_t request_len,
262                         GNUNET_DNSSTUB_ResultCallback rc,
263                         void *rc_cls)
264 {
265   struct GNUNET_DNSSTUB_RequestSocket *rs;
266   struct GNUNET_NETWORK_Handle *ret;
267   int af;
268
269   af = sa->sa_family;
270   if (NULL == (rs = get_request_socket (ctx, af)))
271     return NULL;
272   if (NULL != rs->dnsout4)
273     ret = rs->dnsout4;
274   else
275     ret = rs->dnsout6;
276   GNUNET_assert (NULL != ret);
277   rs->rc = rc;
278   rs->rc_cls = rc_cls;
279   GNUNET_NETWORK_socket_sendto (ret,
280                                 request,
281                                 request_len,
282                                 sa,
283                                 sa_len);
284   return rs;
285 }
286
287
288 /**
289  * Perform DNS resolution using our default IP from init.
290  *
291  * @param ctx stub resolver to use
292  * @param request DNS request to transmit
293  * @param request_len number of bytes in msg
294  * @param rc function to call with result
295  * @param rc_cls closure for 'rc'
296  * @return socket used for the request, NULL on error
297  */
298 struct GNUNET_DNSSTUB_RequestSocket *
299 GNUNET_DNSSTUB_resolve2 (struct GNUNET_DNSSTUB_Context *ctx,
300                          const void *request,
301                          size_t request_len,
302                          GNUNET_DNSSTUB_ResultCallback rc,
303                          void *rc_cls)
304 {
305   int af;
306   struct sockaddr_in v4;
307   struct sockaddr_in6 v6;
308   struct sockaddr *so;
309   socklen_t salen;
310   struct GNUNET_NETWORK_Handle *dnsout;
311   struct GNUNET_DNSSTUB_RequestSocket *rs;
312
313   memset (&v4, 0, sizeof (v4));
314   memset (&v6, 0, sizeof (v6));
315   if (1 == inet_pton (AF_INET, ctx->dns_exit, &v4.sin_addr))
316   {
317     salen = sizeof (v4);
318     v4.sin_family = AF_INET;
319     v4.sin_port = htons (53);
320 #if HAVE_SOCKADDR_IN_SIN_LEN
321     v4.sin_len = (u_char) salen;
322 #endif
323     so = (struct sockaddr *) &v4;
324     af = AF_INET;
325   }
326   else if (1 == inet_pton (AF_INET6, ctx->dns_exit, &v6.sin6_addr))
327   {
328     salen = sizeof (v6);
329     v6.sin6_family = AF_INET6;
330     v6.sin6_port = htons (53);
331 #if HAVE_SOCKADDR_IN_SIN_LEN
332     v6.sin6_len = (u_char) salen;
333 #endif
334     so = (struct sockaddr *) &v6;
335     af = AF_INET6;
336   }  
337   else
338   {
339     GNUNET_break (0);
340     return NULL;
341   }
342   if (NULL == (rs = get_request_socket (ctx, af)))
343     return NULL;
344   if (NULL != rs->dnsout4)
345     dnsout = rs->dnsout4;
346   else
347     dnsout = rs->dnsout6;
348   if (NULL == dnsout)
349   {
350     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
351                 _("Configured DNS exit `%s' is not working / valid.\n"),
352                 ctx->dns_exit);
353     return NULL;
354   }
355   memcpy (&rs->addr,
356           so,
357           salen);
358   rs->addrlen = salen;
359   rs->rc = rc;
360   rs->rc_cls = rc_cls;
361   GNUNET_NETWORK_socket_sendto (dnsout,
362                                 request,
363                                 request_len, so, salen); 
364   rs->timeout = GNUNET_TIME_relative_to_absolute (REQUEST_TIMEOUT);
365   
366   return rs;
367
368 }
369
370
371 /**
372  * Actually do the reading of a DNS packet from our UDP socket and see
373  * if we have a valid, matching, pending request.
374  *
375  * @param rs request socket with callback details
376  * @param dnsout socket to read from
377  * @return GNUNET_OK on success, GNUNET_NO on drop, GNUNET_SYSERR on IO-errors (closed socket)
378  */
379 static int
380 do_dns_read (struct GNUNET_DNSSTUB_RequestSocket *rs,
381              struct GNUNET_NETWORK_Handle *dnsout)
382 {
383   struct sockaddr_storage addr;
384   socklen_t addrlen;
385   struct GNUNET_TUN_DnsHeader *dns;
386   ssize_t r;
387   int len;
388
389 #ifndef MINGW
390   if (0 != ioctl (GNUNET_NETWORK_get_fd (dnsout), FIONREAD, &len))
391   {
392     /* conservative choice: */
393     len = UINT16_MAX;
394   }
395 #else
396   /* port the code above? */
397   len = UINT16_MAX;
398 #endif
399
400   {
401     unsigned char buf[len] GNUNET_ALIGN;
402
403     addrlen = sizeof (addr);
404     memset (&addr, 0, sizeof (addr));  
405     r = GNUNET_NETWORK_socket_recvfrom (dnsout, 
406                                         buf, sizeof (buf),
407                                         (struct sockaddr*) &addr, &addrlen);
408     if (-1 == r)
409     {
410       GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "recvfrom");
411       GNUNET_NETWORK_socket_close (dnsout);
412       return GNUNET_SYSERR;
413     }
414     if (sizeof (struct GNUNET_TUN_DnsHeader) > r)
415     {
416       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 
417                   _("Received DNS response that is too small (%u bytes)"),
418                   r);
419       return GNUNET_NO;
420     }
421     dns = (struct GNUNET_TUN_DnsHeader *) buf;
422     if ( (addrlen != rs->addrlen) ||
423          (0 != memcmp (&rs->addr,
424                        &addr,
425                        addrlen)) ||      
426        (0 == GNUNET_TIME_absolute_get_remaining (rs->timeout).rel_value) )
427       return GNUNET_NO;
428     rs->rc (rs->rc_cls,
429             rs,
430             dns,
431             r);
432   }  
433   return GNUNET_OK;
434 }
435
436
437 /**
438  * Read a DNS response from the (unhindered) UDP-Socket
439  *
440  * @param cls socket to read from
441  * @param tc scheduler context (must be shutdown or read ready)
442  */
443 static void
444 read_response (void *cls,
445                const struct GNUNET_SCHEDULER_TaskContext *tc)
446 {
447   struct GNUNET_DNSSTUB_RequestSocket *rs = cls;
448   struct GNUNET_NETWORK_FDSet *rset;
449
450   rs->read_task = GNUNET_SCHEDULER_NO_TASK;
451   if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_READ_READY))
452   {
453     /* timeout or shutdown */
454     cleanup_rs (rs);
455     return;
456   }
457   /* read and process ready sockets */
458   if ((NULL != rs->dnsout4) &&
459       (GNUNET_NETWORK_fdset_isset (tc->read_ready, rs->dnsout4)) &&
460       (GNUNET_SYSERR == do_dns_read (rs, rs->dnsout4)))
461     rs->dnsout4 = NULL;
462   if ((NULL != rs->dnsout6) &&
463       (GNUNET_NETWORK_fdset_isset (tc->read_ready, rs->dnsout6)) &&
464       (GNUNET_SYSERR == do_dns_read (rs, rs->dnsout6)))
465     rs->dnsout6 = NULL;
466
467   /* re-schedule read task */
468   rset = GNUNET_NETWORK_fdset_create ();
469   if (NULL != rs->dnsout4)
470     GNUNET_NETWORK_fdset_set (rset, rs->dnsout4);
471   if (NULL != rs->dnsout6)
472     GNUNET_NETWORK_fdset_set (rset, rs->dnsout6);
473   rs->read_task = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
474                                                GNUNET_TIME_absolute_get_remaining (rs->timeout),
475                                                rset,
476                                                NULL,
477                                                &read_response, rs);
478   GNUNET_NETWORK_fdset_destroy (rset);
479 }
480
481
482
483 /**
484  * Start a DNS stub resolver.
485  *
486  * @param dns_ip target IP address to use
487  * @return NULL on error
488  */
489 struct GNUNET_DNSSTUB_Context *
490 GNUNET_DNSSTUB_start (const char *dns_ip)
491 {
492   struct GNUNET_DNSSTUB_Context *ctx;
493   
494   ctx = GNUNET_malloc (sizeof (struct GNUNET_DNSSTUB_Context));
495   if (NULL != dns_ip)
496     ctx->dns_exit = GNUNET_strdup (dns_ip);
497   return ctx;
498 }
499
500
501 /**
502  * Cleanup DNSSTUB resolver.
503  *
504  * @param ctx stub resolver to clean up
505  */
506 void
507 GNUNET_DNSSTUB_stop (struct GNUNET_DNSSTUB_Context *ctx)
508 {
509   unsigned int i;
510
511   for (i=0;i<=UINT16_MAX;i++)
512     cleanup_rs (&ctx->sockets[i]);
513   if (NULL != ctx->dns_exit)
514   {
515     GNUNET_free (ctx->dns_exit);
516     ctx->dns_exit = NULL;
517   }
518   GNUNET_free (ctx);
519 }
520
521
522 /* end of dnsstub.c */