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