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.
17 * @brief DNS stub resolver which sends DNS requests to an actual resolver
18 * @author Christian Grothoff
21 #include "gnunet_util_lib.h"
22 #include "gnunet_tun_lib.h"
23 #include "gnunet_dnsstub_lib.h"
26 * Timeout for retrying DNS queries.
28 #define DNS_RETRANSMIT_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 250)
32 * DNS Server used for resolution.
38 * UDP socket we are using for sending DNS requests to the Internet.
40 struct GNUNET_DNSSTUB_RequestSocket
44 * UDP socket we use for this request for IPv4
46 struct GNUNET_NETWORK_Handle *dnsout4;
49 * UDP socket we use for this request for IPv6
51 struct GNUNET_NETWORK_Handle *dnsout6;
54 * Function to call with result.
56 GNUNET_DNSSTUB_ResultCallback rc;
64 * Task for reading from dnsout4 and dnsout6.
66 struct GNUNET_SCHEDULER_Task *read_task;
69 * Task for retrying transmission of the query.
71 struct GNUNET_SCHEDULER_Task *retry_task;
74 * Next address we sent the DNS request to.
76 struct DnsServer *ds_pos;
79 * Context this request executes in.
81 struct GNUNET_DNSSTUB_Context *ctx;
84 * Query we sent to @e addr.
89 * Number of bytes in @a request.
97 * DNS Server used for resolution.
105 struct DnsServer *next;
110 struct DnsServer *prev;
113 * IP address of the DNS resolver.
115 struct sockaddr_storage ss;
120 * Handle to the stub resolver.
122 struct GNUNET_DNSSTUB_Context
126 * Array of all open sockets for DNS requests.
128 struct GNUNET_DNSSTUB_RequestSocket *sockets;
131 * DLL of DNS resolvers we use.
133 struct DnsServer *dns_head;
136 * DLL of DNS resolvers we use.
138 struct DnsServer *dns_tail;
141 * How frequently do we retry requests?
143 struct GNUNET_TIME_Relative retry_freq;
146 * Length of @e sockets array.
148 unsigned int num_sockets;
154 * We're done with a `struct GNUNET_DNSSTUB_RequestSocket`, close it for now.
156 * @param rs request socket to clean up
159 cleanup_rs (struct GNUNET_DNSSTUB_RequestSocket *rs)
161 if (NULL != rs->dnsout4)
163 GNUNET_NETWORK_socket_close (rs->dnsout4);
166 if (NULL != rs->dnsout6)
168 GNUNET_NETWORK_socket_close (rs->dnsout6);
171 if (NULL != rs->read_task)
173 GNUNET_SCHEDULER_cancel (rs->read_task);
174 rs->read_task = NULL;
176 if (NULL != rs->retry_task)
178 GNUNET_SCHEDULER_cancel (rs->retry_task);
179 rs->retry_task = NULL;
181 if (NULL != rs->request)
183 GNUNET_free (rs->request);
190 * Open source port for sending DNS requests
192 * @param af AF_INET or AF_INET6
193 * @return #GNUNET_OK on success
195 static struct GNUNET_NETWORK_Handle *
198 struct sockaddr_in a4;
199 struct sockaddr_in6 a6;
202 struct GNUNET_NETWORK_Handle *ret;
204 ret = GNUNET_NETWORK_socket_create (af, SOCK_DGRAM, 0);
210 memset (&a4, 0, alen = sizeof (struct sockaddr_in));
211 sa = (struct sockaddr *) &a4;
214 memset (&a6, 0, alen = sizeof (struct sockaddr_in6));
215 sa = (struct sockaddr *) &a6;
219 GNUNET_NETWORK_socket_close (ret);
223 if (GNUNET_OK != GNUNET_NETWORK_socket_bind (ret,
227 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
228 _("Could not bind to any port: %s\n"),
230 GNUNET_NETWORK_socket_close (ret);
238 * Get a socket of the specified address family to send out a
239 * UDP DNS request to the Internet.
241 * @param ctx the DNSSTUB context
242 * @return NULL on error
244 static struct GNUNET_DNSSTUB_RequestSocket *
245 get_request_socket (struct GNUNET_DNSSTUB_Context *ctx)
247 struct GNUNET_DNSSTUB_RequestSocket *rs;
249 for (unsigned int i=0;i<256;i++)
251 rs = &ctx->sockets[GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE,
258 /* signal "failure" */
264 if (NULL != rs->read_task)
266 GNUNET_SCHEDULER_cancel (rs->read_task);
267 rs->read_task = NULL;
269 if (NULL != rs->retry_task)
271 GNUNET_SCHEDULER_cancel (rs->retry_task);
272 rs->retry_task = NULL;
274 if (NULL != rs->request)
276 GNUNET_free (rs->request);
285 * Actually do the reading of a DNS packet from our UDP socket and see
286 * if we have a valid, matching, pending request.
288 * @param rs request socket with callback details
289 * @param dnsout socket to read from
290 * @return #GNUNET_OK on success, #GNUNET_NO on drop, #GNUNET_SYSERR on IO-errors (closed socket)
293 do_dns_read (struct GNUNET_DNSSTUB_RequestSocket *rs,
294 struct GNUNET_NETWORK_Handle *dnsout)
296 struct GNUNET_DNSSTUB_Context *ctx = rs->ctx;
301 if (0 != ioctl (GNUNET_NETWORK_get_fd (dnsout),
305 /* conservative choice: */
309 /* port the code above? */
312 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
313 "Receiving %d byte DNS reply\n",
316 unsigned char buf[len] GNUNET_ALIGN;
318 struct sockaddr_storage addr;
320 struct GNUNET_TUN_DnsHeader *dns;
322 addrlen = sizeof (addr);
326 r = GNUNET_NETWORK_socket_recvfrom (dnsout,
329 (struct sockaddr*) &addr,
333 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
335 GNUNET_NETWORK_socket_close (dnsout);
336 return GNUNET_SYSERR;
339 for (struct DnsServer *ds = ctx->dns_head; NULL != ds; ds = ds->next)
341 if (0 == memcmp (&addr,
343 GNUNET_MIN (sizeof (struct sockaddr_storage),
350 if (GNUNET_NO == found)
352 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
353 "Received DNS response from server we never asked (ignored)");
356 if (sizeof (struct GNUNET_TUN_DnsHeader) > r)
358 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
359 _("Received DNS response that is too small (%u bytes)"),
363 dns = (struct GNUNET_TUN_DnsHeader *) buf;
366 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
367 "Request timeout or cancelled; ignoring reply\n");
379 * Read a DNS response from the (unhindered) UDP-Socket
381 * @param cls socket to read from
384 read_response (void *cls);
388 * Schedule #read_response() task for @a rs.
390 * @param rs request to schedule read operation for
393 schedule_read (struct GNUNET_DNSSTUB_RequestSocket *rs)
395 struct GNUNET_NETWORK_FDSet *rset;
397 if (NULL != rs->read_task)
398 GNUNET_SCHEDULER_cancel (rs->read_task);
399 rset = GNUNET_NETWORK_fdset_create ();
400 if (NULL != rs->dnsout4)
401 GNUNET_NETWORK_fdset_set (rset,
403 if (NULL != rs->dnsout6)
404 GNUNET_NETWORK_fdset_set (rset,
406 rs->read_task = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
407 GNUNET_TIME_UNIT_FOREVER_REL,
412 GNUNET_NETWORK_fdset_destroy (rset);
417 * Read a DNS response from the (unhindered) UDP-Socket
419 * @param cls `struct GNUNET_DNSSTUB_RequestSocket` to read from
422 read_response (void *cls)
424 struct GNUNET_DNSSTUB_RequestSocket *rs = cls;
425 const struct GNUNET_SCHEDULER_TaskContext *tc;
427 rs->read_task = NULL;
428 tc = GNUNET_SCHEDULER_get_task_context ();
429 /* read and process ready sockets */
430 if ( (NULL != rs->dnsout4) &&
431 (GNUNET_NETWORK_fdset_isset (tc->read_ready,
437 if ( (NULL != rs->dnsout6) &&
438 (GNUNET_NETWORK_fdset_isset (tc->read_ready,
444 /* re-schedule read task */
450 * Task to (re)transmit the DNS query, possibly repeatedly until
453 * @param cls our `struct GNUNET_DNSSTUB_RequestSocket *`
456 transmit_query (void *cls)
458 struct GNUNET_DNSSTUB_RequestSocket *rs = cls;
459 struct GNUNET_DNSSTUB_Context *ctx = rs->ctx;
460 const struct sockaddr *sa;
462 struct DnsServer *ds;
463 struct GNUNET_NETWORK_Handle *dnsout;
465 rs->retry_task = GNUNET_SCHEDULER_add_delayed (ctx->retry_freq,
469 rs->ds_pos = ds->next;
470 if (NULL == rs->ds_pos)
471 rs->ds_pos = ctx->dns_head;
472 GNUNET_assert (NULL != ds);
474 switch (ds->ss.ss_family)
477 if (NULL == rs->dnsout4)
478 rs->dnsout4 = open_socket (AF_INET);
479 dnsout = rs->dnsout4;
480 sa = (const struct sockaddr *) &ds->ss;
481 salen = sizeof (struct sockaddr_in);
484 if (NULL == rs->dnsout6)
485 rs->dnsout6 = open_socket (AF_INET6);
486 dnsout = rs->dnsout6;
487 sa = (const struct sockaddr *) &ds->ss;
488 salen = sizeof (struct sockaddr_in6);
495 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
496 "Unable to use configure DNS server, skipping\n");
500 GNUNET_NETWORK_socket_sendto (dnsout,
505 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
506 _("Failed to send DNS request to %s: %s\n"),
511 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
512 _("Sent DNS request to %s\n"),
520 * Perform DNS resolution using our default IP from init.
522 * @param ctx stub resolver to use
523 * @param request DNS request to transmit
524 * @param request_len number of bytes in msg
525 * @param rc function to call with result
526 * @param rc_cls closure for 'rc'
527 * @return socket used for the request, NULL on error
529 struct GNUNET_DNSSTUB_RequestSocket *
530 GNUNET_DNSSTUB_resolve (struct GNUNET_DNSSTUB_Context *ctx,
533 GNUNET_DNSSTUB_ResultCallback rc,
536 struct GNUNET_DNSSTUB_RequestSocket *rs;
538 if (NULL == ctx->dns_head)
540 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
541 "No DNS server configured for resolution\n");
544 if (NULL == (rs = get_request_socket (ctx)))
546 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
547 "No request socket available for DNS resolution\n");
550 rs->ds_pos = ctx->dns_head;
553 rs->request = GNUNET_memdup (request,
555 rs->request_len = request_len;
556 rs->retry_task = GNUNET_SCHEDULER_add_now (&transmit_query,
563 * Cancel DNS resolution.
565 * @param rs resolution to cancel
568 GNUNET_DNSSTUB_resolve_cancel (struct GNUNET_DNSSTUB_RequestSocket *rs)
571 if (NULL != rs->retry_task)
573 GNUNET_SCHEDULER_cancel (rs->retry_task);
574 rs->retry_task = NULL;
576 if (NULL != rs->read_task)
578 GNUNET_SCHEDULER_cancel (rs->read_task);
579 rs->read_task = NULL;
585 * Start a DNS stub resolver.
587 * @param num_sockets how many sockets should we open
588 * in parallel for DNS queries for this stub?
589 * @return NULL on error
591 struct GNUNET_DNSSTUB_Context *
592 GNUNET_DNSSTUB_start (unsigned int num_sockets)
594 struct GNUNET_DNSSTUB_Context *ctx;
596 if (0 == num_sockets)
601 ctx = GNUNET_new (struct GNUNET_DNSSTUB_Context);
602 ctx->num_sockets = num_sockets;
603 ctx->sockets = GNUNET_new_array (num_sockets,
604 struct GNUNET_DNSSTUB_RequestSocket);
605 ctx->retry_freq = DNS_RETRANSMIT_DELAY;
611 * Add nameserver for use by the DNSSTUB. We will use
612 * all provided nameservers for resolution (round-robin).
614 * @param ctx resolver context to modify
615 * @param dns_ip target IP address to use (as string)
616 * @return #GNUNET_OK on success
619 GNUNET_DNSSTUB_add_dns_ip (struct GNUNET_DNSSTUB_Context *ctx,
622 struct DnsServer *ds;
626 ds = GNUNET_new (struct DnsServer);
627 if (1 == inet_pton (AF_INET,
631 struct sockaddr_in *s4 = (struct sockaddr_in *) &ds->ss;
633 s4->sin_family = AF_INET;
634 s4->sin_port = htons (53);
636 #if HAVE_SOCKADDR_IN_SIN_LEN
637 s4->sin_len = (u_char) sizeof (struct sockaddr_in);
640 else if (1 == inet_pton (AF_INET6,
644 struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) &ds->ss;
646 s6->sin6_family = AF_INET6;
647 s6->sin6_port = htons (53);
649 #if HAVE_SOCKADDR_IN_SIN_LEN
650 s6->sin6_len = (u_char) sizeof (struct sockaddr_in6);
655 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
656 "Malformed IP address `%s' for DNS server\n",
659 return GNUNET_SYSERR;
661 GNUNET_CONTAINER_DLL_insert (ctx->dns_head,
669 * Add nameserver for use by the DNSSTUB. We will use
670 * all provided nameservers for resolution (round-robin).
672 * @param ctx resolver context to modify
673 * @param sa socket address of DNS resolver to use
674 * @return #GNUNET_OK on success
677 GNUNET_DNSSTUB_add_dns_sa (struct GNUNET_DNSSTUB_Context *ctx,
678 const struct sockaddr *sa)
680 struct DnsServer *ds;
682 ds = GNUNET_new (struct DnsServer);
683 switch (sa->sa_family)
686 GNUNET_memcpy (&ds->ss,
688 sizeof (struct sockaddr_in));
691 GNUNET_memcpy (&ds->ss,
693 sizeof (struct sockaddr_in6));
698 return GNUNET_SYSERR;
700 GNUNET_CONTAINER_DLL_insert (ctx->dns_head,
708 * How long should we try requests before timing out?
709 * Only effective for requests issued after this call.
711 * @param ctx resolver context to modify
712 * @param retry_freq how long to wait between retries
715 GNUNET_DNSSTUB_set_retry (struct GNUNET_DNSSTUB_Context *ctx,
716 struct GNUNET_TIME_Relative retry_freq)
718 ctx->retry_freq = retry_freq;
723 * Cleanup DNSSTUB resolver.
725 * @param ctx stub resolver to clean up
728 GNUNET_DNSSTUB_stop (struct GNUNET_DNSSTUB_Context *ctx)
730 struct DnsServer *ds;
732 while (NULL != (ds = ctx->dns_head))
734 GNUNET_CONTAINER_DLL_remove (ctx->dns_head,
739 for (unsigned int i=0;i<ctx->num_sockets;i++)
740 cleanup_rs (&ctx->sockets[i]);
741 GNUNET_free (ctx->sockets);
746 /* end of dnsstub.c */