first step to remove plibc
[oweals/gnunet.git] / src / util / dnsstub.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2012, 2018 GNUnet e.V.
4
5      GNUnet is free software: you can redistribute it and/or modify it
6      under the terms of the GNU Affero General Public License as published
7      by the Free Software Foundation, either version 3 of the License,
8      or (at your 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      Affero General Public License for more details.
14     
15      You should have received a copy of the GNU Affero General Public License
16      along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18      SPDX-License-Identifier: AGPL3.0-or-later
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
28 /**
29  * Timeout for retrying DNS queries.
30  */
31 #define DNS_RETRANSMIT_DELAY \
32   GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 250)
33
34
35 /**
36  * DNS Server used for resolution.
37  */
38 struct DnsServer;
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    * Task for retrying transmission of the query.
74    */
75   struct GNUNET_SCHEDULER_Task *retry_task;
76
77   /**
78    * Next address we sent the DNS request to.
79    */
80   struct DnsServer *ds_pos;
81
82   /**
83    * Context this request executes in.
84    */
85   struct GNUNET_DNSSTUB_Context *ctx;
86
87   /**
88    * Query we sent to @e addr.
89    */
90   void *request;
91
92   /**
93    * Number of bytes in @a request.
94    */
95   size_t request_len;
96 };
97
98
99 /**
100  * DNS Server used for resolution.
101  */
102 struct DnsServer
103 {
104
105   /**
106    * Kept in a DLL.
107    */
108   struct DnsServer *next;
109
110   /**
111    * Kept in a DLL.
112    */
113   struct DnsServer *prev;
114
115   /**
116    * IP address of the DNS resolver.
117    */
118   struct sockaddr_storage ss;
119 };
120
121
122 /**
123  * Handle to the stub resolver.
124  */
125 struct GNUNET_DNSSTUB_Context
126 {
127
128   /**
129    * Array of all open sockets for DNS requests.
130    */
131   struct GNUNET_DNSSTUB_RequestSocket *sockets;
132
133   /**
134    * DLL of DNS resolvers we use.
135    */
136   struct DnsServer *dns_head;
137
138   /**
139    * DLL of DNS resolvers we use.
140    */
141   struct DnsServer *dns_tail;
142
143   /**
144    * How frequently do we retry requests?
145    */
146   struct GNUNET_TIME_Relative retry_freq;
147
148   /**
149    * Length of @e sockets array.
150    */
151   unsigned int num_sockets;
152 };
153
154
155 /**
156  * We're done with a `struct GNUNET_DNSSTUB_RequestSocket`, close it for now.
157  *
158  * @param rs request socket to clean up
159  */
160 static void
161 cleanup_rs (struct GNUNET_DNSSTUB_RequestSocket *rs)
162 {
163   if (NULL != rs->dnsout4)
164   {
165     GNUNET_NETWORK_socket_close (rs->dnsout4);
166     rs->dnsout4 = NULL;
167   }
168   if (NULL != rs->dnsout6)
169   {
170     GNUNET_NETWORK_socket_close (rs->dnsout6);
171     rs->dnsout6 = NULL;
172   }
173   if (NULL != rs->read_task)
174   {
175     GNUNET_SCHEDULER_cancel (rs->read_task);
176     rs->read_task = NULL;
177   }
178   if (NULL != rs->retry_task)
179   {
180     GNUNET_SCHEDULER_cancel (rs->retry_task);
181     rs->retry_task = NULL;
182   }
183   if (NULL != rs->request)
184   {
185     GNUNET_free (rs->request);
186     rs->request = NULL;
187   }
188 }
189
190
191 /**
192  * Open source port for sending DNS requests
193  *
194  * @param af AF_INET or AF_INET6
195  * @return #GNUNET_OK on success
196  */
197 static struct GNUNET_NETWORK_Handle *
198 open_socket (int af)
199 {
200   struct sockaddr_in a4;
201   struct sockaddr_in6 a6;
202   struct sockaddr *sa;
203   socklen_t alen;
204   struct GNUNET_NETWORK_Handle *ret;
205
206   ret = GNUNET_NETWORK_socket_create (af, SOCK_DGRAM, 0);
207   if (NULL == ret)
208     return NULL;
209   switch (af)
210   {
211   case AF_INET:
212     memset (&a4, 0, alen = sizeof (struct sockaddr_in));
213     sa = (struct sockaddr *) &a4;
214     break;
215   case AF_INET6:
216     memset (&a6, 0, alen = sizeof (struct sockaddr_in6));
217     sa = (struct sockaddr *) &a6;
218     break;
219   default:
220     GNUNET_break (0);
221     GNUNET_NETWORK_socket_close (ret);
222     return NULL;
223   }
224   sa->sa_family = af;
225   if (GNUNET_OK != GNUNET_NETWORK_socket_bind (ret, sa, alen))
226   {
227     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
228                 _ ("Could not bind to any port: %s\n"),
229                 strerror (errno));
230     GNUNET_NETWORK_socket_close (ret);
231     return NULL;
232   }
233   return ret;
234 }
235
236
237 /**
238  * Get a socket of the specified address family to send out a
239  * UDP DNS request to the Internet.
240  *
241  * @param ctx the DNSSTUB context
242  * @return NULL on error
243  */
244 static struct GNUNET_DNSSTUB_RequestSocket *
245 get_request_socket (struct GNUNET_DNSSTUB_Context *ctx)
246 {
247   struct GNUNET_DNSSTUB_RequestSocket *rs;
248
249   for (unsigned int i = 0; i < 256; i++)
250   {
251     rs = &ctx->sockets[GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE,
252                                                  ctx->num_sockets)];
253     if (NULL == rs->rc)
254       break;
255   }
256   if (NULL != rs->rc)
257   {
258     /* signal "failure" */
259     rs->rc (rs->rc_cls, NULL, 0);
260     rs->rc = NULL;
261   }
262   if (NULL != rs->read_task)
263   {
264     GNUNET_SCHEDULER_cancel (rs->read_task);
265     rs->read_task = NULL;
266   }
267   if (NULL != rs->retry_task)
268   {
269     GNUNET_SCHEDULER_cancel (rs->retry_task);
270     rs->retry_task = NULL;
271   }
272   if (NULL != rs->request)
273   {
274     GNUNET_free (rs->request);
275     rs->request = NULL;
276   }
277   rs->ctx = ctx;
278   return rs;
279 }
280
281
282 /**
283  * Actually do the reading of a DNS packet from our UDP socket and see
284  * if we have a valid, matching, pending request.
285  *
286  * @param rs request socket with callback details
287  * @param dnsout socket to read from
288  * @return #GNUNET_OK on success, #GNUNET_NO on drop, #GNUNET_SYSERR on IO-errors (closed socket)
289  */
290 static int
291 do_dns_read (struct GNUNET_DNSSTUB_RequestSocket *rs,
292              struct GNUNET_NETWORK_Handle *dnsout)
293 {
294   struct GNUNET_DNSSTUB_Context *ctx = rs->ctx;
295   ssize_t r;
296   int len;
297
298 #ifndef MINGW
299   if (0 != ioctl (GNUNET_NETWORK_get_fd (dnsout), FIONREAD, &len))
300   {
301     /* conservative choice: */
302     len = UINT16_MAX;
303   }
304 #else
305   /* port the code above? */
306   len = UINT16_MAX;
307 #endif
308   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Receiving %d byte DNS reply\n", len);
309   {
310     unsigned char buf[len] GNUNET_ALIGN;
311     int found;
312     struct sockaddr_storage addr;
313     socklen_t addrlen;
314     struct GNUNET_TUN_DnsHeader *dns;
315
316     addrlen = sizeof (addr);
317     memset (&addr, 0, sizeof (addr));
318     r = GNUNET_NETWORK_socket_recvfrom (dnsout,
319                                         buf,
320                                         sizeof (buf),
321                                         (struct sockaddr *) &addr,
322                                         &addrlen);
323     if (-1 == r)
324     {
325       GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "recvfrom");
326       GNUNET_NETWORK_socket_close (dnsout);
327       return GNUNET_SYSERR;
328     }
329     found = GNUNET_NO;
330     for (struct DnsServer *ds = ctx->dns_head; NULL != ds; ds = ds->next)
331     {
332       if (0 == memcmp (&addr,
333                        &ds->ss,
334                        GNUNET_MIN (sizeof (struct sockaddr_storage), addrlen)))
335       {
336         found = GNUNET_YES;
337         break;
338       }
339     }
340     if (GNUNET_NO == found)
341     {
342       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
343                   "Received DNS response from server we never asked (ignored)");
344       return GNUNET_NO;
345     }
346     if (sizeof (struct GNUNET_TUN_DnsHeader) > (size_t) r)
347     {
348       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
349                   _ ("Received DNS response that is too small (%u bytes)"),
350                   (unsigned int) r);
351       return GNUNET_NO;
352     }
353     dns = (struct GNUNET_TUN_DnsHeader *) buf;
354     if (NULL == rs->rc)
355     {
356       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
357                   "Request timeout or cancelled; ignoring reply\n");
358       return GNUNET_NO;
359     }
360     rs->rc (rs->rc_cls, dns, r);
361   }
362   return GNUNET_OK;
363 }
364
365
366 /**
367  * Read a DNS response from the (unhindered) UDP-Socket
368  *
369  * @param cls socket to read from
370  */
371 static void
372 read_response (void *cls);
373
374
375 /**
376  * Schedule #read_response() task for @a rs.
377  *
378  * @param rs request to schedule read operation for
379  */
380 static void
381 schedule_read (struct GNUNET_DNSSTUB_RequestSocket *rs)
382 {
383   struct GNUNET_NETWORK_FDSet *rset;
384
385   if (NULL != rs->read_task)
386     GNUNET_SCHEDULER_cancel (rs->read_task);
387   rset = GNUNET_NETWORK_fdset_create ();
388   if (NULL != rs->dnsout4)
389     GNUNET_NETWORK_fdset_set (rset, rs->dnsout4);
390   if (NULL != rs->dnsout6)
391     GNUNET_NETWORK_fdset_set (rset, rs->dnsout6);
392   rs->read_task =
393     GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
394                                  GNUNET_TIME_UNIT_FOREVER_REL,
395                                  rset,
396                                  NULL,
397                                  &read_response,
398                                  rs);
399   GNUNET_NETWORK_fdset_destroy (rset);
400 }
401
402
403 /**
404  * Read a DNS response from the (unhindered) UDP-Socket
405  *
406  * @param cls `struct GNUNET_DNSSTUB_RequestSocket` to read from
407  */
408 static void
409 read_response (void *cls)
410 {
411   struct GNUNET_DNSSTUB_RequestSocket *rs = cls;
412   const struct GNUNET_SCHEDULER_TaskContext *tc;
413
414   rs->read_task = NULL;
415   tc = GNUNET_SCHEDULER_get_task_context ();
416   /* read and process ready sockets */
417   if ((NULL != rs->dnsout4) &&
418       (GNUNET_NETWORK_fdset_isset (tc->read_ready, rs->dnsout4)) &&
419       (GNUNET_SYSERR == do_dns_read (rs, rs->dnsout4)))
420     rs->dnsout4 = NULL;
421   if ((NULL != rs->dnsout6) &&
422       (GNUNET_NETWORK_fdset_isset (tc->read_ready, rs->dnsout6)) &&
423       (GNUNET_SYSERR == do_dns_read (rs, rs->dnsout6)))
424     rs->dnsout6 = NULL;
425   /* re-schedule read task */
426   schedule_read (rs);
427 }
428
429
430 /**
431  * Task to (re)transmit the DNS query, possibly repeatedly until
432  * we succeed.
433  *
434  * @param cls our `struct GNUNET_DNSSTUB_RequestSocket *`
435  */
436 static void
437 transmit_query (void *cls)
438 {
439   struct GNUNET_DNSSTUB_RequestSocket *rs = cls;
440   struct GNUNET_DNSSTUB_Context *ctx = rs->ctx;
441   const struct sockaddr *sa;
442   socklen_t salen;
443   struct DnsServer *ds;
444   struct GNUNET_NETWORK_Handle *dnsout;
445
446   rs->retry_task =
447     GNUNET_SCHEDULER_add_delayed (ctx->retry_freq, &transmit_query, rs);
448   ds = rs->ds_pos;
449   rs->ds_pos = ds->next;
450   if (NULL == rs->ds_pos)
451     rs->ds_pos = ctx->dns_head;
452   GNUNET_assert (NULL != ds);
453   dnsout = NULL;
454   switch (ds->ss.ss_family)
455   {
456   case AF_INET:
457     if (NULL == rs->dnsout4)
458       rs->dnsout4 = open_socket (AF_INET);
459     dnsout = rs->dnsout4;
460     sa = (const struct sockaddr *) &ds->ss;
461     salen = sizeof (struct sockaddr_in);
462     break;
463   case AF_INET6:
464     if (NULL == rs->dnsout6)
465       rs->dnsout6 = open_socket (AF_INET6);
466     dnsout = rs->dnsout6;
467     sa = (const struct sockaddr *) &ds->ss;
468     salen = sizeof (struct sockaddr_in6);
469     break;
470   default:
471     return;
472   }
473   if (NULL == dnsout)
474   {
475     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
476                 "Unable to use configure DNS server, skipping\n");
477     return;
478   }
479   if (GNUNET_SYSERR == GNUNET_NETWORK_socket_sendto (dnsout,
480                                                      rs->request,
481                                                      rs->request_len,
482                                                      sa,
483                                                      salen))
484     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
485                 _ ("Failed to send DNS request to %s: %s\n"),
486                 GNUNET_a2s (sa, salen),
487                 strerror (errno));
488   else
489     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
490                 _ ("Sent DNS request to %s\n"),
491                 GNUNET_a2s (sa, salen));
492   schedule_read (rs);
493 }
494
495
496 /**
497  * Perform DNS resolution using our default IP from init.
498  *
499  * @param ctx stub resolver to use
500  * @param request DNS request to transmit
501  * @param request_len number of bytes in msg
502  * @param rc function to call with result
503  * @param rc_cls closure for 'rc'
504  * @return socket used for the request, NULL on error
505  */
506 struct GNUNET_DNSSTUB_RequestSocket *
507 GNUNET_DNSSTUB_resolve (struct GNUNET_DNSSTUB_Context *ctx,
508                         const void *request,
509                         size_t request_len,
510                         GNUNET_DNSSTUB_ResultCallback rc,
511                         void *rc_cls)
512 {
513   struct GNUNET_DNSSTUB_RequestSocket *rs;
514
515   if (NULL == ctx->dns_head)
516   {
517     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
518                 "No DNS server configured for resolution\n");
519     return NULL;
520   }
521   if (NULL == (rs = get_request_socket (ctx)))
522   {
523     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
524                 "No request socket available for DNS resolution\n");
525     return NULL;
526   }
527   rs->ds_pos = ctx->dns_head;
528   rs->rc = rc;
529   rs->rc_cls = rc_cls;
530   rs->request = GNUNET_memdup (request, request_len);
531   rs->request_len = request_len;
532   rs->retry_task = GNUNET_SCHEDULER_add_now (&transmit_query, rs);
533   return rs;
534 }
535
536
537 /**
538  * Cancel DNS resolution.
539  *
540  * @param rs resolution to cancel
541  */
542 void
543 GNUNET_DNSSTUB_resolve_cancel (struct GNUNET_DNSSTUB_RequestSocket *rs)
544 {
545   rs->rc = NULL;
546   if (NULL != rs->retry_task)
547   {
548     GNUNET_SCHEDULER_cancel (rs->retry_task);
549     rs->retry_task = NULL;
550   }
551   if (NULL != rs->read_task)
552   {
553     GNUNET_SCHEDULER_cancel (rs->read_task);
554     rs->read_task = NULL;
555   }
556 }
557
558
559 /**
560  * Start a DNS stub resolver.
561  *
562  * @param num_sockets how many sockets should we open
563  *        in parallel for DNS queries for this stub?
564  * @return NULL on error
565  */
566 struct GNUNET_DNSSTUB_Context *
567 GNUNET_DNSSTUB_start (unsigned int num_sockets)
568 {
569   struct GNUNET_DNSSTUB_Context *ctx;
570
571   if (0 == num_sockets)
572   {
573     GNUNET_break (0);
574     return NULL;
575   }
576   ctx = GNUNET_new (struct GNUNET_DNSSTUB_Context);
577   ctx->num_sockets = num_sockets;
578   ctx->sockets =
579     GNUNET_new_array (num_sockets, struct GNUNET_DNSSTUB_RequestSocket);
580   ctx->retry_freq = DNS_RETRANSMIT_DELAY;
581   return ctx;
582 }
583
584
585 /**
586  * Add nameserver for use by the DNSSTUB.  We will use
587  * all provided nameservers for resolution (round-robin).
588  *
589  * @param ctx resolver context to modify
590  * @param dns_ip target IP address to use (as string)
591  * @return #GNUNET_OK on success
592  */
593 int
594 GNUNET_DNSSTUB_add_dns_ip (struct GNUNET_DNSSTUB_Context *ctx,
595                            const char *dns_ip)
596 {
597   struct DnsServer *ds;
598   struct in_addr i4;
599   struct in6_addr i6;
600
601   ds = GNUNET_new (struct DnsServer);
602   if (1 == inet_pton (AF_INET, dns_ip, &i4))
603   {
604     struct sockaddr_in *s4 = (struct sockaddr_in *) &ds->ss;
605
606     s4->sin_family = AF_INET;
607     s4->sin_port = htons (53);
608     s4->sin_addr = i4;
609 #if HAVE_SOCKADDR_IN_SIN_LEN
610     s4->sin_len = (u_char) sizeof (struct sockaddr_in);
611 #endif
612   }
613   else if (1 == inet_pton (AF_INET6, dns_ip, &i6))
614   {
615     struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) &ds->ss;
616
617     s6->sin6_family = AF_INET6;
618     s6->sin6_port = htons (53);
619     s6->sin6_addr = i6;
620 #if HAVE_SOCKADDR_IN_SIN_LEN
621     s6->sin6_len = (u_char) sizeof (struct sockaddr_in6);
622 #endif
623   }
624   else
625   {
626     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
627                 "Malformed IP address `%s' for DNS server\n",
628                 dns_ip);
629     GNUNET_free (ds);
630     return GNUNET_SYSERR;
631   }
632   GNUNET_CONTAINER_DLL_insert (ctx->dns_head, ctx->dns_tail, ds);
633   return GNUNET_OK;
634 }
635
636
637 /**
638  * Add nameserver for use by the DNSSTUB.  We will use
639  * all provided nameservers for resolution (round-robin).
640  *
641  * @param ctx resolver context to modify
642  * @param sa socket address of DNS resolver to use
643  * @return #GNUNET_OK on success
644  */
645 int
646 GNUNET_DNSSTUB_add_dns_sa (struct GNUNET_DNSSTUB_Context *ctx,
647                            const struct sockaddr *sa)
648 {
649   struct DnsServer *ds;
650
651   ds = GNUNET_new (struct DnsServer);
652   switch (sa->sa_family)
653   {
654   case AF_INET:
655     GNUNET_memcpy (&ds->ss, sa, sizeof (struct sockaddr_in));
656     break;
657   case AF_INET6:
658     GNUNET_memcpy (&ds->ss, sa, sizeof (struct sockaddr_in6));
659     break;
660   default:
661     GNUNET_break (0);
662     GNUNET_free (ds);
663     return GNUNET_SYSERR;
664   }
665   GNUNET_CONTAINER_DLL_insert (ctx->dns_head, ctx->dns_tail, ds);
666   return GNUNET_OK;
667 }
668
669
670 /**
671  * How long should we try requests before timing out?
672  * Only effective for requests issued after this call.
673  *
674  * @param ctx resolver context to modify
675  * @param retry_freq how long to wait between retries
676  */
677 void
678 GNUNET_DNSSTUB_set_retry (struct GNUNET_DNSSTUB_Context *ctx,
679                           struct GNUNET_TIME_Relative retry_freq)
680 {
681   ctx->retry_freq = retry_freq;
682 }
683
684
685 /**
686  * Cleanup DNSSTUB resolver.
687  *
688  * @param ctx stub resolver to clean up
689  */
690 void
691 GNUNET_DNSSTUB_stop (struct GNUNET_DNSSTUB_Context *ctx)
692 {
693   struct DnsServer *ds;
694
695   while (NULL != (ds = ctx->dns_head))
696   {
697     GNUNET_CONTAINER_DLL_remove (ctx->dns_head, ctx->dns_tail, ds);
698     GNUNET_free (ds);
699   }
700   for (unsigned int i = 0; i < ctx->num_sockets; i++)
701     cleanup_rs (&ctx->sockets[i]);
702   GNUNET_free (ctx->sockets);
703   GNUNET_free (ctx);
704 }
705
706
707 /* end of dnsstub.c */