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
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.
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.
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.
22 * @brief DNS stub resolver which sends DNS requests to an actual resolver
23 * @author Christian Grothoff
26 #include "gnunet_util_lib.h"
27 #include "gnunet_tun_lib.h"
28 #include "gnunet_dnsstub_lib.h"
31 * Timeout for retrying DNS queries.
33 #define DNS_RETRANSMIT_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 250)
37 * DNS Server used for resolution.
43 * UDP socket we are using for sending DNS requests to the Internet.
45 struct GNUNET_DNSSTUB_RequestSocket
49 * UDP socket we use for this request for IPv4
51 struct GNUNET_NETWORK_Handle *dnsout4;
54 * UDP socket we use for this request for IPv6
56 struct GNUNET_NETWORK_Handle *dnsout6;
59 * Function to call with result.
61 GNUNET_DNSSTUB_ResultCallback rc;
69 * Task for reading from dnsout4 and dnsout6.
71 struct GNUNET_SCHEDULER_Task *read_task;
74 * Task for retrying transmission of the query.
76 struct GNUNET_SCHEDULER_Task *retry_task;
79 * Next address we sent the DNS request to.
81 struct DnsServer *ds_pos;
84 * Context this request executes in.
86 struct GNUNET_DNSSTUB_Context *ctx;
89 * Query we sent to @e addr.
94 * Number of bytes in @a request.
102 * DNS Server used for resolution.
110 struct DnsServer *next;
115 struct DnsServer *prev;
118 * IP address of the DNS resolver.
120 struct sockaddr_storage ss;
125 * Handle to the stub resolver.
127 struct GNUNET_DNSSTUB_Context
131 * Array of all open sockets for DNS requests.
133 struct GNUNET_DNSSTUB_RequestSocket *sockets;
136 * DLL of DNS resolvers we use.
138 struct DnsServer *dns_head;
141 * DLL of DNS resolvers we use.
143 struct DnsServer *dns_tail;
146 * How frequently do we retry requests?
148 struct GNUNET_TIME_Relative retry_freq;
151 * Length of @e sockets array.
153 unsigned int num_sockets;
159 * We're done with a `struct GNUNET_DNSSTUB_RequestSocket`, close it for now.
161 * @param rs request socket to clean up
164 cleanup_rs (struct GNUNET_DNSSTUB_RequestSocket *rs)
166 if (NULL != rs->dnsout4)
168 GNUNET_NETWORK_socket_close (rs->dnsout4);
171 if (NULL != rs->dnsout6)
173 GNUNET_NETWORK_socket_close (rs->dnsout6);
176 if (NULL != rs->read_task)
178 GNUNET_SCHEDULER_cancel (rs->read_task);
179 rs->read_task = NULL;
181 if (NULL != rs->retry_task)
183 GNUNET_SCHEDULER_cancel (rs->retry_task);
184 rs->retry_task = NULL;
186 if (NULL != rs->request)
188 GNUNET_free (rs->request);
195 * Open source port for sending DNS requests
197 * @param af AF_INET or AF_INET6
198 * @return #GNUNET_OK on success
200 static struct GNUNET_NETWORK_Handle *
203 struct sockaddr_in a4;
204 struct sockaddr_in6 a6;
207 struct GNUNET_NETWORK_Handle *ret;
209 ret = GNUNET_NETWORK_socket_create (af, SOCK_DGRAM, 0);
215 memset (&a4, 0, alen = sizeof (struct sockaddr_in));
216 sa = (struct sockaddr *) &a4;
219 memset (&a6, 0, alen = sizeof (struct sockaddr_in6));
220 sa = (struct sockaddr *) &a6;
224 GNUNET_NETWORK_socket_close (ret);
228 if (GNUNET_OK != GNUNET_NETWORK_socket_bind (ret,
232 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
233 _("Could not bind to any port: %s\n"),
235 GNUNET_NETWORK_socket_close (ret);
243 * Get a socket of the specified address family to send out a
244 * UDP DNS request to the Internet.
246 * @param ctx the DNSSTUB context
247 * @return NULL on error
249 static struct GNUNET_DNSSTUB_RequestSocket *
250 get_request_socket (struct GNUNET_DNSSTUB_Context *ctx)
252 struct GNUNET_DNSSTUB_RequestSocket *rs;
254 for (unsigned int i=0;i<256;i++)
256 rs = &ctx->sockets[GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE,
263 /* signal "failure" */
269 if (NULL != rs->read_task)
271 GNUNET_SCHEDULER_cancel (rs->read_task);
272 rs->read_task = NULL;
274 if (NULL != rs->retry_task)
276 GNUNET_SCHEDULER_cancel (rs->retry_task);
277 rs->retry_task = NULL;
279 if (NULL != rs->request)
281 GNUNET_free (rs->request);
290 * Actually do the reading of a DNS packet from our UDP socket and see
291 * if we have a valid, matching, pending request.
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)
298 do_dns_read (struct GNUNET_DNSSTUB_RequestSocket *rs,
299 struct GNUNET_NETWORK_Handle *dnsout)
301 struct GNUNET_DNSSTUB_Context *ctx = rs->ctx;
306 if (0 != ioctl (GNUNET_NETWORK_get_fd (dnsout),
310 /* conservative choice: */
314 /* port the code above? */
317 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
318 "Receiving %d byte DNS reply\n",
321 unsigned char buf[len] GNUNET_ALIGN;
323 struct sockaddr_storage addr;
325 struct GNUNET_TUN_DnsHeader *dns;
327 addrlen = sizeof (addr);
331 r = GNUNET_NETWORK_socket_recvfrom (dnsout,
334 (struct sockaddr*) &addr,
338 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
340 GNUNET_NETWORK_socket_close (dnsout);
341 return GNUNET_SYSERR;
344 for (struct DnsServer *ds = ctx->dns_head; NULL != ds; ds = ds->next)
346 if (0 == memcmp (&addr,
348 GNUNET_MIN (sizeof (struct sockaddr_storage),
355 if (GNUNET_NO == found)
357 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
358 "Received DNS response from server we never asked (ignored)");
361 if (sizeof (struct GNUNET_TUN_DnsHeader) > r)
363 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
364 _("Received DNS response that is too small (%u bytes)"),
368 dns = (struct GNUNET_TUN_DnsHeader *) buf;
371 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
372 "Request timeout or cancelled; ignoring reply\n");
384 * Read a DNS response from the (unhindered) UDP-Socket
386 * @param cls socket to read from
389 read_response (void *cls);
393 * Schedule #read_response() task for @a rs.
395 * @param rs request to schedule read operation for
398 schedule_read (struct GNUNET_DNSSTUB_RequestSocket *rs)
400 struct GNUNET_NETWORK_FDSet *rset;
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,
408 if (NULL != rs->dnsout6)
409 GNUNET_NETWORK_fdset_set (rset,
411 rs->read_task = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
412 GNUNET_TIME_UNIT_FOREVER_REL,
417 GNUNET_NETWORK_fdset_destroy (rset);
422 * Read a DNS response from the (unhindered) UDP-Socket
424 * @param cls `struct GNUNET_DNSSTUB_RequestSocket` to read from
427 read_response (void *cls)
429 struct GNUNET_DNSSTUB_RequestSocket *rs = cls;
430 const struct GNUNET_SCHEDULER_TaskContext *tc;
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,
442 if ( (NULL != rs->dnsout6) &&
443 (GNUNET_NETWORK_fdset_isset (tc->read_ready,
449 /* re-schedule read task */
455 * Task to (re)transmit the DNS query, possibly repeatedly until
458 * @param cls our `struct GNUNET_DNSSTUB_RequestSocket *`
461 transmit_query (void *cls)
463 struct GNUNET_DNSSTUB_RequestSocket *rs = cls;
464 struct GNUNET_DNSSTUB_Context *ctx = rs->ctx;
465 const struct sockaddr *sa;
467 struct DnsServer *ds;
468 struct GNUNET_NETWORK_Handle *dnsout;
470 rs->retry_task = GNUNET_SCHEDULER_add_delayed (ctx->retry_freq,
474 rs->ds_pos = ds->next;
475 if (NULL == rs->ds_pos)
476 rs->ds_pos = ctx->dns_head;
477 GNUNET_assert (NULL != ds);
479 switch (ds->ss.ss_family)
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);
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);
500 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
501 "Unable to use configure DNS server, skipping\n");
505 GNUNET_NETWORK_socket_sendto (dnsout,
510 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
511 _("Failed to send DNS request to %s\n"),
515 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
516 _("Sent DNS request to %s\n"),
524 * Perform DNS resolution using our default IP from init.
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
533 struct GNUNET_DNSSTUB_RequestSocket *
534 GNUNET_DNSSTUB_resolve (struct GNUNET_DNSSTUB_Context *ctx,
537 GNUNET_DNSSTUB_ResultCallback rc,
540 struct GNUNET_DNSSTUB_RequestSocket *rs;
542 if (NULL == ctx->dns_head)
544 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
545 "No DNS server configured for resolution\n");
548 if (NULL == (rs = get_request_socket (ctx)))
550 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
551 "No request socket available for DNS resolution\n");
554 rs->ds_pos = ctx->dns_head;
557 rs->request = GNUNET_memdup (request,
559 rs->request_len = request_len;
560 rs->retry_task = GNUNET_SCHEDULER_add_now (&transmit_query,
567 * Cancel DNS resolution.
569 * @param rs resolution to cancel
572 GNUNET_DNSSTUB_resolve_cancel (struct GNUNET_DNSSTUB_RequestSocket *rs)
575 if (NULL != rs->retry_task)
577 GNUNET_SCHEDULER_cancel (rs->retry_task);
578 rs->retry_task = NULL;
580 if (NULL != rs->read_task)
582 GNUNET_SCHEDULER_cancel (rs->read_task);
583 rs->read_task = NULL;
589 * Start a DNS stub resolver.
591 * @param num_sockets how many sockets should we open
592 * in parallel for DNS queries for this stub?
593 * @return NULL on error
595 struct GNUNET_DNSSTUB_Context *
596 GNUNET_DNSSTUB_start (unsigned int num_sockets)
598 struct GNUNET_DNSSTUB_Context *ctx;
600 if (0 == num_sockets)
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;
615 * Add nameserver for use by the DNSSTUB. We will use
616 * all provided nameservers for resolution (round-robin).
618 * @param ctx resolver context to modify
619 * @param dns_ip target IP address to use (as string)
620 * @return #GNUNET_OK on success
623 GNUNET_DNSSTUB_add_dns_ip (struct GNUNET_DNSSTUB_Context *ctx,
626 struct DnsServer *ds;
630 ds = GNUNET_new (struct DnsServer);
631 if (1 == inet_pton (AF_INET,
635 struct sockaddr_in *s4 = (struct sockaddr_in *) &ds->ss;
637 s4->sin_family = AF_INET;
638 s4->sin_port = htons (53);
640 #if HAVE_SOCKADDR_IN_SIN_LEN
641 s4->sin_len = (u_char) sizeof (struct sockaddr_in);
644 else if (1 == inet_pton (AF_INET6,
648 struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) &ds->ss;
650 s6->sin6_family = AF_INET6;
651 s6->sin6_port = htons (53);
653 #if HAVE_SOCKADDR_IN_SIN_LEN
654 s6->sin6_len = (u_char) sizeof (struct sockaddr_in6);
659 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
660 "Malformed IP address `%s' for DNS server\n",
663 return GNUNET_SYSERR;
665 GNUNET_CONTAINER_DLL_insert (ctx->dns_head,
673 * Add nameserver for use by the DNSSTUB. We will use
674 * all provided nameservers for resolution (round-robin).
676 * @param ctx resolver context to modify
677 * @param sa socket address of DNS resolver to use
678 * @return #GNUNET_OK on success
681 GNUNET_DNSSTUB_add_dns_sa (struct GNUNET_DNSSTUB_Context *ctx,
682 const struct sockaddr *sa)
684 struct DnsServer *ds;
686 ds = GNUNET_new (struct DnsServer);
687 switch (sa->sa_family)
692 sizeof (struct sockaddr_in));
697 sizeof (struct sockaddr_in6));
702 return GNUNET_SYSERR;
704 GNUNET_CONTAINER_DLL_insert (ctx->dns_head,
712 * How long should we try requests before timing out?
713 * Only effective for requests issued after this call.
715 * @param ctx resolver context to modify
716 * @param retry_freq how long to wait between retries
719 GNUNET_DNSSTUB_set_retry (struct GNUNET_DNSSTUB_Context *ctx,
720 struct GNUNET_TIME_Relative retry_freq)
722 ctx->retry_freq = retry_freq;
727 * Cleanup DNSSTUB resolver.
729 * @param ctx stub resolver to clean up
732 GNUNET_DNSSTUB_stop (struct GNUNET_DNSSTUB_Context *ctx)
734 struct DnsServer *ds;
736 while (NULL != (ds = ctx->dns_head))
738 GNUNET_CONTAINER_DLL_remove (ctx->dns_head,
743 for (unsigned int i=0;i<ctx->num_sockets;i++)
744 cleanup_rs (&ctx->sockets[i]);
745 GNUNET_free (ctx->sockets);
750 /* end of dnsstub.c */