2 This file is part of GNUnet.
3 Copyright (C) 2012, 2018 GNUnet e.V.
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.
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.
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/>.
18 SPDX-License-Identifier: AGPL3.0-or-later
22 * @brief DNS stub resolver which sends DNS requests to an actual resolver
23 * @author Christian Grothoff
26 #include "gnunet_util_lib.h"
29 * Timeout for retrying DNS queries.
31 #define DNS_RETRANSMIT_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 250)
35 * DNS Server used for resolution.
41 * UDP socket we are using for sending DNS requests to the Internet.
43 struct GNUNET_DNSSTUB_RequestSocket
47 * UDP socket we use for this request for IPv4
49 struct GNUNET_NETWORK_Handle *dnsout4;
52 * UDP socket we use for this request for IPv6
54 struct GNUNET_NETWORK_Handle *dnsout6;
57 * Function to call with result.
59 GNUNET_DNSSTUB_ResultCallback rc;
67 * Task for reading from dnsout4 and dnsout6.
69 struct GNUNET_SCHEDULER_Task *read_task;
72 * Task for retrying transmission of the query.
74 struct GNUNET_SCHEDULER_Task *retry_task;
77 * Next address we sent the DNS request to.
79 struct DnsServer *ds_pos;
82 * Context this request executes in.
84 struct GNUNET_DNSSTUB_Context *ctx;
87 * Query we sent to @e addr.
92 * Number of bytes in @a request.
100 * DNS Server used for resolution.
108 struct DnsServer *next;
113 struct DnsServer *prev;
116 * IP address of the DNS resolver.
118 struct sockaddr_storage ss;
123 * Handle to the stub resolver.
125 struct GNUNET_DNSSTUB_Context
129 * Array of all open sockets for DNS requests.
131 struct GNUNET_DNSSTUB_RequestSocket *sockets;
134 * DLL of DNS resolvers we use.
136 struct DnsServer *dns_head;
139 * DLL of DNS resolvers we use.
141 struct DnsServer *dns_tail;
144 * How frequently do we retry requests?
146 struct GNUNET_TIME_Relative retry_freq;
149 * Length of @e sockets array.
151 unsigned int num_sockets;
157 * We're done with a `struct GNUNET_DNSSTUB_RequestSocket`, close it for now.
159 * @param rs request socket to clean up
162 cleanup_rs (struct GNUNET_DNSSTUB_RequestSocket *rs)
164 if (NULL != rs->dnsout4)
166 GNUNET_NETWORK_socket_close (rs->dnsout4);
169 if (NULL != rs->dnsout6)
171 GNUNET_NETWORK_socket_close (rs->dnsout6);
174 if (NULL != rs->read_task)
176 GNUNET_SCHEDULER_cancel (rs->read_task);
177 rs->read_task = NULL;
179 if (NULL != rs->retry_task)
181 GNUNET_SCHEDULER_cancel (rs->retry_task);
182 rs->retry_task = NULL;
184 if (NULL != rs->request)
186 GNUNET_free (rs->request);
193 * Open source port for sending DNS requests
195 * @param af AF_INET or AF_INET6
196 * @return #GNUNET_OK on success
198 static struct GNUNET_NETWORK_Handle *
201 struct sockaddr_in a4;
202 struct sockaddr_in6 a6;
205 struct GNUNET_NETWORK_Handle *ret;
207 ret = GNUNET_NETWORK_socket_create (af, SOCK_DGRAM, 0);
213 memset (&a4, 0, alen = sizeof (struct sockaddr_in));
214 sa = (struct sockaddr *) &a4;
217 memset (&a6, 0, alen = sizeof (struct sockaddr_in6));
218 sa = (struct sockaddr *) &a6;
222 GNUNET_NETWORK_socket_close (ret);
226 if (GNUNET_OK != GNUNET_NETWORK_socket_bind (ret,
230 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
231 _("Could not bind to any port: %s\n"),
233 GNUNET_NETWORK_socket_close (ret);
241 * Get a socket of the specified address family to send out a
242 * UDP DNS request to the Internet.
244 * @param ctx the DNSSTUB context
245 * @return NULL on error
247 static struct GNUNET_DNSSTUB_RequestSocket *
248 get_request_socket (struct GNUNET_DNSSTUB_Context *ctx)
250 struct GNUNET_DNSSTUB_RequestSocket *rs;
252 for (unsigned int i=0;i<256;i++)
254 rs = &ctx->sockets[GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE,
261 /* signal "failure" */
267 if (NULL != rs->read_task)
269 GNUNET_SCHEDULER_cancel (rs->read_task);
270 rs->read_task = NULL;
272 if (NULL != rs->retry_task)
274 GNUNET_SCHEDULER_cancel (rs->retry_task);
275 rs->retry_task = NULL;
277 if (NULL != rs->request)
279 GNUNET_free (rs->request);
288 * Actually do the reading of a DNS packet from our UDP socket and see
289 * if we have a valid, matching, pending request.
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)
296 do_dns_read (struct GNUNET_DNSSTUB_RequestSocket *rs,
297 struct GNUNET_NETWORK_Handle *dnsout)
299 struct GNUNET_DNSSTUB_Context *ctx = rs->ctx;
304 if (0 != ioctl (GNUNET_NETWORK_get_fd (dnsout),
308 /* conservative choice: */
312 /* port the code above? */
315 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
316 "Receiving %d byte DNS reply\n",
319 unsigned char buf[len] GNUNET_ALIGN;
321 struct sockaddr_storage addr;
323 struct GNUNET_TUN_DnsHeader *dns;
325 addrlen = sizeof (addr);
329 r = GNUNET_NETWORK_socket_recvfrom (dnsout,
332 (struct sockaddr*) &addr,
336 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
338 GNUNET_NETWORK_socket_close (dnsout);
339 return GNUNET_SYSERR;
342 for (struct DnsServer *ds = ctx->dns_head; NULL != ds; ds = ds->next)
344 if (0 == memcmp (&addr,
346 GNUNET_MIN (sizeof (struct sockaddr_storage),
353 if (GNUNET_NO == found)
355 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
356 "Received DNS response from server we never asked (ignored)");
359 if (sizeof (struct GNUNET_TUN_DnsHeader) > (size_t) r)
361 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
362 _("Received DNS response that is too small (%u bytes)"),
366 dns = (struct GNUNET_TUN_DnsHeader *) buf;
369 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
370 "Request timeout or cancelled; ignoring reply\n");
382 * Read a DNS response from the (unhindered) UDP-Socket
384 * @param cls socket to read from
387 read_response (void *cls);
391 * Schedule #read_response() task for @a rs.
393 * @param rs request to schedule read operation for
396 schedule_read (struct GNUNET_DNSSTUB_RequestSocket *rs)
398 struct GNUNET_NETWORK_FDSet *rset;
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,
406 if (NULL != rs->dnsout6)
407 GNUNET_NETWORK_fdset_set (rset,
409 rs->read_task = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
410 GNUNET_TIME_UNIT_FOREVER_REL,
415 GNUNET_NETWORK_fdset_destroy (rset);
420 * Read a DNS response from the (unhindered) UDP-Socket
422 * @param cls `struct GNUNET_DNSSTUB_RequestSocket` to read from
425 read_response (void *cls)
427 struct GNUNET_DNSSTUB_RequestSocket *rs = cls;
428 const struct GNUNET_SCHEDULER_TaskContext *tc;
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,
440 if ( (NULL != rs->dnsout6) &&
441 (GNUNET_NETWORK_fdset_isset (tc->read_ready,
447 /* re-schedule read task */
453 * Task to (re)transmit the DNS query, possibly repeatedly until
456 * @param cls our `struct GNUNET_DNSSTUB_RequestSocket *`
459 transmit_query (void *cls)
461 struct GNUNET_DNSSTUB_RequestSocket *rs = cls;
462 struct GNUNET_DNSSTUB_Context *ctx = rs->ctx;
463 const struct sockaddr *sa;
465 struct DnsServer *ds;
466 struct GNUNET_NETWORK_Handle *dnsout;
468 rs->retry_task = GNUNET_SCHEDULER_add_delayed (ctx->retry_freq,
472 rs->ds_pos = ds->next;
473 if (NULL == rs->ds_pos)
474 rs->ds_pos = ctx->dns_head;
475 GNUNET_assert (NULL != ds);
477 switch (ds->ss.ss_family)
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);
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);
498 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
499 "Unable to use configure DNS server, skipping\n");
503 GNUNET_NETWORK_socket_sendto (dnsout,
508 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
509 _("Failed to send DNS request to %s: %s\n"),
514 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
515 _("Sent DNS request to %s\n"),
523 * Perform DNS resolution using our default IP from init.
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
532 struct GNUNET_DNSSTUB_RequestSocket *
533 GNUNET_DNSSTUB_resolve (struct GNUNET_DNSSTUB_Context *ctx,
536 GNUNET_DNSSTUB_ResultCallback rc,
539 struct GNUNET_DNSSTUB_RequestSocket *rs;
541 if (NULL == ctx->dns_head)
543 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
544 "No DNS server configured for resolution\n");
547 if (NULL == (rs = get_request_socket (ctx)))
549 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
550 "No request socket available for DNS resolution\n");
553 rs->ds_pos = ctx->dns_head;
556 rs->request = GNUNET_memdup (request,
558 rs->request_len = request_len;
559 rs->retry_task = GNUNET_SCHEDULER_add_now (&transmit_query,
566 * Cancel DNS resolution.
568 * @param rs resolution to cancel
571 GNUNET_DNSSTUB_resolve_cancel (struct GNUNET_DNSSTUB_RequestSocket *rs)
574 if (NULL != rs->retry_task)
576 GNUNET_SCHEDULER_cancel (rs->retry_task);
577 rs->retry_task = NULL;
579 if (NULL != rs->read_task)
581 GNUNET_SCHEDULER_cancel (rs->read_task);
582 rs->read_task = NULL;
588 * Start a DNS stub resolver.
590 * @param num_sockets how many sockets should we open
591 * in parallel for DNS queries for this stub?
592 * @return NULL on error
594 struct GNUNET_DNSSTUB_Context *
595 GNUNET_DNSSTUB_start (unsigned int num_sockets)
597 struct GNUNET_DNSSTUB_Context *ctx;
599 if (0 == num_sockets)
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;
614 * Add nameserver for use by the DNSSTUB. We will use
615 * all provided nameservers for resolution (round-robin).
617 * @param ctx resolver context to modify
618 * @param dns_ip target IP address to use (as string)
619 * @return #GNUNET_OK on success
622 GNUNET_DNSSTUB_add_dns_ip (struct GNUNET_DNSSTUB_Context *ctx,
625 struct DnsServer *ds;
629 ds = GNUNET_new (struct DnsServer);
630 if (1 == inet_pton (AF_INET,
634 struct sockaddr_in *s4 = (struct sockaddr_in *) &ds->ss;
636 s4->sin_family = AF_INET;
637 s4->sin_port = htons (53);
639 #if HAVE_SOCKADDR_IN_SIN_LEN
640 s4->sin_len = (u_char) sizeof (struct sockaddr_in);
643 else if (1 == inet_pton (AF_INET6,
647 struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) &ds->ss;
649 s6->sin6_family = AF_INET6;
650 s6->sin6_port = htons (53);
652 #if HAVE_SOCKADDR_IN_SIN_LEN
653 s6->sin6_len = (u_char) sizeof (struct sockaddr_in6);
658 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
659 "Malformed IP address `%s' for DNS server\n",
662 return GNUNET_SYSERR;
664 GNUNET_CONTAINER_DLL_insert (ctx->dns_head,
672 * Add nameserver for use by the DNSSTUB. We will use
673 * all provided nameservers for resolution (round-robin).
675 * @param ctx resolver context to modify
676 * @param sa socket address of DNS resolver to use
677 * @return #GNUNET_OK on success
680 GNUNET_DNSSTUB_add_dns_sa (struct GNUNET_DNSSTUB_Context *ctx,
681 const struct sockaddr *sa)
683 struct DnsServer *ds;
685 ds = GNUNET_new (struct DnsServer);
686 switch (sa->sa_family)
689 GNUNET_memcpy (&ds->ss,
691 sizeof (struct sockaddr_in));
694 GNUNET_memcpy (&ds->ss,
696 sizeof (struct sockaddr_in6));
701 return GNUNET_SYSERR;
703 GNUNET_CONTAINER_DLL_insert (ctx->dns_head,
711 * How long should we try requests before timing out?
712 * Only effective for requests issued after this call.
714 * @param ctx resolver context to modify
715 * @param retry_freq how long to wait between retries
718 GNUNET_DNSSTUB_set_retry (struct GNUNET_DNSSTUB_Context *ctx,
719 struct GNUNET_TIME_Relative retry_freq)
721 ctx->retry_freq = retry_freq;
726 * Cleanup DNSSTUB resolver.
728 * @param ctx stub resolver to clean up
731 GNUNET_DNSSTUB_stop (struct GNUNET_DNSSTUB_Context *ctx)
733 struct DnsServer *ds;
735 while (NULL != (ds = ctx->dns_head))
737 GNUNET_CONTAINER_DLL_remove (ctx->dns_head,
742 for (unsigned int i=0;i<ctx->num_sockets;i++)
743 cleanup_rs (&ctx->sockets[i]);
744 GNUNET_free (ctx->sockets);
749 /* end of dnsstub.c */