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