avoid freeing uninit ptr on error path
[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  * How many DNS sockets do we open at most at the same time?
37  * (technical socket maximum is this number x2 for IPv4+IPv6)
38  */
39 #define DNS_SOCKET_MAX 128
40
41
42 /**
43  * UDP socket we are using for sending DNS requests to the Internet.
44  */
45 struct GNUNET_DNSSTUB_RequestSocket
46 {
47
48   /**
49    * UDP socket we use for this request for IPv4
50    */
51   struct GNUNET_NETWORK_Handle *dnsout4;
52
53   /**
54    * UDP socket we use for this request for IPv6
55    */
56   struct GNUNET_NETWORK_Handle *dnsout6;
57
58   /**
59    * Function to call with result.
60    */
61   GNUNET_DNSSTUB_ResultCallback rc;
62
63   /**
64    * Closure for @e rc.
65    */
66   void *rc_cls;
67
68   /**
69    * Task for reading from dnsout4 and dnsout6.
70    */
71   struct GNUNET_SCHEDULER_Task * read_task;
72
73   /**
74    * When should this request time out?
75    */
76   struct GNUNET_TIME_Absolute timeout;
77
78   /**
79    * Address we sent the DNS request to.
80    */
81   struct sockaddr_storage addr;
82
83   /**
84    * Number of bytes in @e addr.
85    */
86   socklen_t addrlen;
87
88 };
89
90
91 /**
92  * Handle to the stub resolver.
93  */
94 struct GNUNET_DNSSTUB_Context
95 {
96
97   /**
98    * Array of all open sockets for DNS requests.
99    */
100   struct GNUNET_DNSSTUB_RequestSocket sockets[DNS_SOCKET_MAX];
101
102   /**
103    * IP address to use for the DNS server if we are a DNS exit service
104    * (for VPN via cadet); otherwise NULL.
105    */
106   char *dns_exit;
107 };
108
109
110
111 /**
112  * We're done with a `struct GNUNET_DNSSTUB_RequestSocket`, close it for now.
113  *
114  * @param rs request socket to clean up
115  */
116 static void
117 cleanup_rs (struct GNUNET_DNSSTUB_RequestSocket *rs)
118 {
119   if (NULL != rs->dnsout4)
120   {
121     GNUNET_NETWORK_socket_close (rs->dnsout4);
122     rs->dnsout4 = NULL;
123   }
124   if (NULL != rs->dnsout6)
125   {
126     GNUNET_NETWORK_socket_close (rs->dnsout6);
127     rs->dnsout6 = NULL;
128   }
129   if (NULL != rs->read_task)
130   {
131     GNUNET_SCHEDULER_cancel (rs->read_task);
132     rs->read_task = NULL;
133   }
134 }
135
136
137 /**
138  * Open source port for sending DNS requests
139  *
140  * @param af AF_INET or AF_INET6
141  * @return #GNUNET_OK on success
142  */
143 static struct GNUNET_NETWORK_Handle *
144 open_socket (int af)
145 {
146   struct sockaddr_in a4;
147   struct sockaddr_in6 a6;
148   struct sockaddr *sa;
149   socklen_t alen;
150   struct GNUNET_NETWORK_Handle *ret;
151
152   ret = GNUNET_NETWORK_socket_create (af, SOCK_DGRAM, 0);
153   if (NULL == ret)
154     return NULL;
155   switch (af)
156   {
157   case AF_INET:
158     memset (&a4, 0, alen = sizeof (struct sockaddr_in));
159     sa = (struct sockaddr *) &a4;
160     break;
161   case AF_INET6:
162     memset (&a6, 0, alen = sizeof (struct sockaddr_in6));
163     sa = (struct sockaddr *) &a6;
164     break;
165   default:
166     GNUNET_break (0);
167     GNUNET_NETWORK_socket_close (ret);
168     return NULL;
169   }
170   sa->sa_family = af;
171   if (GNUNET_OK != GNUNET_NETWORK_socket_bind (ret,
172                                                sa,
173                                                alen))
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  */
190 static void
191 read_response (void *cls);
192
193
194 /**
195  * Get a socket of the specified address family to send out a
196  * UDP DNS request to the Internet.
197  *
198  * @param ctx the DNSSTUB context
199  * @param af desired address family
200  * @return NULL on error (given AF not "supported")
201  */
202 static struct GNUNET_DNSSTUB_RequestSocket *
203 get_request_socket (struct GNUNET_DNSSTUB_Context *ctx,
204                     int af)
205 {
206   struct GNUNET_DNSSTUB_RequestSocket *rs;
207   struct GNUNET_NETWORK_FDSet *rset;
208
209   rs = &ctx->sockets[GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE,
210                                                DNS_SOCKET_MAX)];
211   rs->timeout = GNUNET_TIME_relative_to_absolute (REQUEST_TIMEOUT);
212   switch (af)
213   {
214   case AF_INET:
215     if (NULL == rs->dnsout4)
216       rs->dnsout4 = open_socket (AF_INET);
217     break;
218   case AF_INET6:
219     if (NULL == rs->dnsout6)
220       rs->dnsout6 = open_socket (AF_INET6);
221     break;
222   default:
223     return NULL;
224   }
225   if (NULL != rs->read_task)
226   {
227     GNUNET_SCHEDULER_cancel (rs->read_task);
228     rs->read_task = NULL;
229   }
230   if ( (NULL == rs->dnsout4) &&
231        (NULL == rs->dnsout6) )
232     return NULL;
233   rset = GNUNET_NETWORK_fdset_create ();
234   if (NULL != rs->dnsout4)
235     GNUNET_NETWORK_fdset_set (rset, rs->dnsout4);
236   if (NULL != rs->dnsout6)
237     GNUNET_NETWORK_fdset_set (rset, rs->dnsout6);
238   rs->read_task = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
239                                                REQUEST_TIMEOUT,
240                                                rset,
241                                                NULL,
242                                                &read_response, rs);
243   GNUNET_NETWORK_fdset_destroy (rset);
244   return rs;
245 }
246
247
248 /**
249  * Perform DNS resolution.
250  *
251  * @param ctx stub resolver to use
252  * @param sa the socket address
253  * @param sa_len the socket length
254  * @param request DNS request to transmit
255  * @param request_len number of bytes in msg
256  * @param rc function to call with result
257  * @param rc_cls closure for 'rc'
258  * @return socket used for the request, NULL on error
259  */
260 struct GNUNET_DNSSTUB_RequestSocket *
261 GNUNET_DNSSTUB_resolve (struct GNUNET_DNSSTUB_Context *ctx,
262                         const struct sockaddr *sa,
263                         socklen_t sa_len,
264                         const void *request,
265                         size_t request_len,
266                         GNUNET_DNSSTUB_ResultCallback rc,
267                         void *rc_cls)
268 {
269   struct GNUNET_DNSSTUB_RequestSocket *rs;
270   struct GNUNET_NETWORK_Handle *ret;
271   int af;
272
273   af = sa->sa_family;
274   if (NULL == (rs = get_request_socket (ctx, af)))
275     return NULL;
276   if (NULL != rs->dnsout4)
277     ret = rs->dnsout4;
278   else
279     ret = rs->dnsout6;
280   GNUNET_assert (NULL != ret);
281   GNUNET_memcpy (&rs->addr,
282           sa,
283           sa_len);
284   rs->addrlen = sa_len;
285   rs->rc = rc;
286   rs->rc_cls = rc_cls;
287   if (GNUNET_SYSERR ==
288       GNUNET_NETWORK_socket_sendto (ret,
289                                     request,
290                                     request_len,
291                                     sa,
292                                     sa_len))
293     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
294                 _("Failed to send DNS request to %s\n"),
295                 GNUNET_a2s (sa, sa_len));
296   else
297     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
298                 _("Sent DNS request to %s\n"),
299                 GNUNET_a2s (sa, sa_len));
300   return rs;
301 }
302
303
304 /**
305  * Perform DNS resolution using our default IP from init.
306  *
307  * @param ctx stub resolver to use
308  * @param request DNS request to transmit
309  * @param request_len number of bytes in msg
310  * @param rc function to call with result
311  * @param rc_cls closure for 'rc'
312  * @return socket used for the request, NULL on error
313  */
314 struct GNUNET_DNSSTUB_RequestSocket *
315 GNUNET_DNSSTUB_resolve2 (struct GNUNET_DNSSTUB_Context *ctx,
316                          const void *request,
317                          size_t request_len,
318                          GNUNET_DNSSTUB_ResultCallback rc,
319                          void *rc_cls)
320 {
321   int af;
322   struct sockaddr_in v4;
323   struct sockaddr_in6 v6;
324   struct sockaddr *sa;
325   socklen_t salen;
326   struct GNUNET_NETWORK_Handle *dnsout;
327   struct GNUNET_DNSSTUB_RequestSocket *rs;
328
329   memset (&v4, 0, sizeof (v4));
330   memset (&v6, 0, sizeof (v6));
331   if (1 == inet_pton (AF_INET, ctx->dns_exit, &v4.sin_addr))
332   {
333     salen = sizeof (v4);
334     v4.sin_family = AF_INET;
335     v4.sin_port = htons (53);
336 #if HAVE_SOCKADDR_IN_SIN_LEN
337     v4.sin_len = (u_char) salen;
338 #endif
339     sa = (struct sockaddr *) &v4;
340     af = AF_INET;
341   }
342   else if (1 == inet_pton (AF_INET6, ctx->dns_exit, &v6.sin6_addr))
343   {
344     salen = sizeof (v6);
345     v6.sin6_family = AF_INET6;
346     v6.sin6_port = htons (53);
347 #if HAVE_SOCKADDR_IN_SIN_LEN
348     v6.sin6_len = (u_char) salen;
349 #endif
350     sa = (struct sockaddr *) &v6;
351     af = AF_INET6;
352   }
353   else
354   {
355     GNUNET_break (0);
356     return NULL;
357   }
358   if (NULL == (rs = get_request_socket (ctx, af)))
359     return NULL;
360   if (NULL != rs->dnsout4)
361     dnsout = rs->dnsout4;
362   else
363     dnsout = rs->dnsout6;
364   if (NULL == dnsout)
365   {
366     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
367                 _("Configured DNS exit `%s' is not working / valid.\n"),
368                 ctx->dns_exit);
369     return NULL;
370   }
371   GNUNET_memcpy (&rs->addr,
372           sa,
373           salen);
374   rs->addrlen = salen;
375   rs->rc = rc;
376   rs->rc_cls = rc_cls;
377   if (GNUNET_SYSERR ==
378       GNUNET_NETWORK_socket_sendto (dnsout,
379                                     request,
380                                     request_len, sa, salen))
381     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
382                 _("Failed to send DNS request to %s\n"),
383                 GNUNET_a2s (sa, salen));
384   rs->timeout = GNUNET_TIME_relative_to_absolute (REQUEST_TIMEOUT);
385   return rs;
386 }
387
388
389 /**
390  * Actually do the reading of a DNS packet from our UDP socket and see
391  * if we have a valid, matching, pending request.
392  *
393  * @param rs request socket with callback details
394  * @param dnsout socket to read from
395  * @return #GNUNET_OK on success, #GNUNET_NO on drop, #GNUNET_SYSERR on IO-errors (closed socket)
396  */
397 static int
398 do_dns_read (struct GNUNET_DNSSTUB_RequestSocket *rs,
399              struct GNUNET_NETWORK_Handle *dnsout)
400 {
401   struct sockaddr_storage addr;
402   socklen_t addrlen;
403   struct GNUNET_TUN_DnsHeader *dns;
404   ssize_t r;
405   int len;
406
407 #ifndef MINGW
408   if (0 != ioctl (GNUNET_NETWORK_get_fd (dnsout), FIONREAD, &len))
409   {
410     /* conservative choice: */
411     len = UINT16_MAX;
412   }
413 #else
414   /* port the code above? */
415   len = UINT16_MAX;
416 #endif
417   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
418               "Receiving %d byte DNS reply\n",
419               len);
420   {
421     unsigned char buf[len] GNUNET_ALIGN;
422
423     addrlen = sizeof (addr);
424     memset (&addr, 0, sizeof (addr));
425     r = GNUNET_NETWORK_socket_recvfrom (dnsout,
426                                         buf, sizeof (buf),
427                                         (struct sockaddr*) &addr, &addrlen);
428     if (-1 == r)
429     {
430       GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "recvfrom");
431       GNUNET_NETWORK_socket_close (dnsout);
432       return GNUNET_SYSERR;
433     }
434     if (sizeof (struct GNUNET_TUN_DnsHeader) > r)
435     {
436       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
437                   _("Received DNS response that is too small (%u bytes)"),
438                   (unsigned int) r);
439       return GNUNET_NO;
440     }
441     dns = (struct GNUNET_TUN_DnsHeader *) buf;
442     if ( (addrlen != rs->addrlen) ||
443          (GNUNET_YES !=
444           GNUNET_TUN_sockaddr_cmp ((struct sockaddr *) &rs->addr,
445                                    (struct sockaddr *) &addr,
446                                    GNUNET_YES)) ||
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 */