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 \
32 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 250)
36 * DNS Server used for resolution.
42 * UDP socket we are using for sending DNS requests to the Internet.
44 struct GNUNET_DNSSTUB_RequestSocket
48 * UDP socket we use for this request for IPv4
50 struct GNUNET_NETWORK_Handle *dnsout4;
53 * UDP socket we use for this request for IPv6
55 struct GNUNET_NETWORK_Handle *dnsout6;
58 * Function to call with result.
60 GNUNET_DNSSTUB_ResultCallback rc;
68 * Task for reading from dnsout4 and dnsout6.
70 struct GNUNET_SCHEDULER_Task *read_task;
73 * Task for retrying transmission of the query.
75 struct GNUNET_SCHEDULER_Task *retry_task;
78 * Next address we sent the DNS request to.
80 struct DnsServer *ds_pos;
83 * Context this request executes in.
85 struct GNUNET_DNSSTUB_Context *ctx;
88 * Query we sent to @e addr.
93 * 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;
156 * We're done with a `struct GNUNET_DNSSTUB_RequestSocket`, close it for now.
158 * @param rs request socket to clean up
161 cleanup_rs (struct GNUNET_DNSSTUB_RequestSocket *rs)
163 if (NULL != rs->dnsout4)
165 GNUNET_NETWORK_socket_close (rs->dnsout4);
168 if (NULL != rs->dnsout6)
170 GNUNET_NETWORK_socket_close (rs->dnsout6);
173 if (NULL != rs->read_task)
175 GNUNET_SCHEDULER_cancel (rs->read_task);
176 rs->read_task = NULL;
178 if (NULL != rs->retry_task)
180 GNUNET_SCHEDULER_cancel (rs->retry_task);
181 rs->retry_task = NULL;
183 if (NULL != rs->request)
185 GNUNET_free (rs->request);
192 * Open source port for sending DNS requests
194 * @param af AF_INET or AF_INET6
195 * @return #GNUNET_OK on success
197 static struct GNUNET_NETWORK_Handle *
200 struct sockaddr_in a4;
201 struct sockaddr_in6 a6;
204 struct GNUNET_NETWORK_Handle *ret;
206 ret = GNUNET_NETWORK_socket_create (af, SOCK_DGRAM, 0);
212 memset (&a4, 0, alen = sizeof (struct sockaddr_in));
213 sa = (struct sockaddr *) &a4;
216 memset (&a6, 0, alen = sizeof (struct sockaddr_in6));
217 sa = (struct sockaddr *) &a6;
221 GNUNET_NETWORK_socket_close (ret);
225 if (GNUNET_OK != GNUNET_NETWORK_socket_bind (ret, sa, alen))
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" */
259 rs->rc (rs->rc_cls, NULL, 0);
262 if (NULL != rs->read_task)
264 GNUNET_SCHEDULER_cancel (rs->read_task);
265 rs->read_task = NULL;
267 if (NULL != rs->retry_task)
269 GNUNET_SCHEDULER_cancel (rs->retry_task);
270 rs->retry_task = NULL;
272 if (NULL != rs->request)
274 GNUNET_free (rs->request);
283 * Actually do the reading of a DNS packet from our UDP socket and see
284 * if we have a valid, matching, pending request.
286 * @param rs request socket with callback details
287 * @param dnsout socket to read from
288 * @return #GNUNET_OK on success, #GNUNET_NO on drop, #GNUNET_SYSERR on IO-errors (closed socket)
291 do_dns_read (struct GNUNET_DNSSTUB_RequestSocket *rs,
292 struct GNUNET_NETWORK_Handle *dnsout)
294 struct GNUNET_DNSSTUB_Context *ctx = rs->ctx;
299 if (0 != ioctl (GNUNET_NETWORK_get_fd (dnsout), FIONREAD, &len))
301 /* conservative choice: */
305 /* port the code above? */
308 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Receiving %d byte DNS reply\n", len);
310 unsigned char buf[len] GNUNET_ALIGN;
312 struct sockaddr_storage addr;
314 struct GNUNET_TUN_DnsHeader *dns;
316 addrlen = sizeof (addr);
317 memset (&addr, 0, sizeof (addr));
318 r = GNUNET_NETWORK_socket_recvfrom (dnsout,
321 (struct sockaddr *) &addr,
325 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "recvfrom");
326 GNUNET_NETWORK_socket_close (dnsout);
327 return GNUNET_SYSERR;
330 for (struct DnsServer *ds = ctx->dns_head; NULL != ds; ds = ds->next)
332 if (0 == memcmp (&addr,
334 GNUNET_MIN (sizeof (struct sockaddr_storage), addrlen)))
340 if (GNUNET_NO == found)
342 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
343 "Received DNS response from server we never asked (ignored)");
346 if (sizeof (struct GNUNET_TUN_DnsHeader) > (size_t) r)
348 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
349 _ ("Received DNS response that is too small (%u bytes)"),
353 dns = (struct GNUNET_TUN_DnsHeader *) buf;
356 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
357 "Request timeout or cancelled; ignoring reply\n");
360 rs->rc (rs->rc_cls, dns, r);
367 * Read a DNS response from the (unhindered) UDP-Socket
369 * @param cls socket to read from
372 read_response (void *cls);
376 * Schedule #read_response() task for @a rs.
378 * @param rs request to schedule read operation for
381 schedule_read (struct GNUNET_DNSSTUB_RequestSocket *rs)
383 struct GNUNET_NETWORK_FDSet *rset;
385 if (NULL != rs->read_task)
386 GNUNET_SCHEDULER_cancel (rs->read_task);
387 rset = GNUNET_NETWORK_fdset_create ();
388 if (NULL != rs->dnsout4)
389 GNUNET_NETWORK_fdset_set (rset, rs->dnsout4);
390 if (NULL != rs->dnsout6)
391 GNUNET_NETWORK_fdset_set (rset, rs->dnsout6);
393 GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
394 GNUNET_TIME_UNIT_FOREVER_REL,
399 GNUNET_NETWORK_fdset_destroy (rset);
404 * Read a DNS response from the (unhindered) UDP-Socket
406 * @param cls `struct GNUNET_DNSSTUB_RequestSocket` to read from
409 read_response (void *cls)
411 struct GNUNET_DNSSTUB_RequestSocket *rs = cls;
412 const struct GNUNET_SCHEDULER_TaskContext *tc;
414 rs->read_task = NULL;
415 tc = GNUNET_SCHEDULER_get_task_context ();
416 /* read and process ready sockets */
417 if ((NULL != rs->dnsout4) &&
418 (GNUNET_NETWORK_fdset_isset (tc->read_ready, rs->dnsout4)) &&
419 (GNUNET_SYSERR == do_dns_read (rs, rs->dnsout4)))
421 if ((NULL != rs->dnsout6) &&
422 (GNUNET_NETWORK_fdset_isset (tc->read_ready, rs->dnsout6)) &&
423 (GNUNET_SYSERR == do_dns_read (rs, rs->dnsout6)))
425 /* re-schedule read task */
431 * Task to (re)transmit the DNS query, possibly repeatedly until
434 * @param cls our `struct GNUNET_DNSSTUB_RequestSocket *`
437 transmit_query (void *cls)
439 struct GNUNET_DNSSTUB_RequestSocket *rs = cls;
440 struct GNUNET_DNSSTUB_Context *ctx = rs->ctx;
441 const struct sockaddr *sa;
443 struct DnsServer *ds;
444 struct GNUNET_NETWORK_Handle *dnsout;
447 GNUNET_SCHEDULER_add_delayed (ctx->retry_freq, &transmit_query, rs);
449 rs->ds_pos = ds->next;
450 if (NULL == rs->ds_pos)
451 rs->ds_pos = ctx->dns_head;
452 GNUNET_assert (NULL != ds);
454 switch (ds->ss.ss_family)
457 if (NULL == rs->dnsout4)
458 rs->dnsout4 = open_socket (AF_INET);
459 dnsout = rs->dnsout4;
460 sa = (const struct sockaddr *) &ds->ss;
461 salen = sizeof (struct sockaddr_in);
464 if (NULL == rs->dnsout6)
465 rs->dnsout6 = open_socket (AF_INET6);
466 dnsout = rs->dnsout6;
467 sa = (const struct sockaddr *) &ds->ss;
468 salen = sizeof (struct sockaddr_in6);
475 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
476 "Unable to use configure DNS server, skipping\n");
479 if (GNUNET_SYSERR == GNUNET_NETWORK_socket_sendto (dnsout,
484 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
485 _ ("Failed to send DNS request to %s: %s\n"),
486 GNUNET_a2s (sa, salen),
489 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
490 _ ("Sent DNS request to %s\n"),
491 GNUNET_a2s (sa, salen));
497 * Perform DNS resolution using our default IP from init.
499 * @param ctx stub resolver to use
500 * @param request DNS request to transmit
501 * @param request_len number of bytes in msg
502 * @param rc function to call with result
503 * @param rc_cls closure for 'rc'
504 * @return socket used for the request, NULL on error
506 struct GNUNET_DNSSTUB_RequestSocket *
507 GNUNET_DNSSTUB_resolve (struct GNUNET_DNSSTUB_Context *ctx,
510 GNUNET_DNSSTUB_ResultCallback rc,
513 struct GNUNET_DNSSTUB_RequestSocket *rs;
515 if (NULL == ctx->dns_head)
517 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
518 "No DNS server configured for resolution\n");
521 if (NULL == (rs = get_request_socket (ctx)))
523 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
524 "No request socket available for DNS resolution\n");
527 rs->ds_pos = ctx->dns_head;
530 rs->request = GNUNET_memdup (request, request_len);
531 rs->request_len = request_len;
532 rs->retry_task = GNUNET_SCHEDULER_add_now (&transmit_query, rs);
538 * Cancel DNS resolution.
540 * @param rs resolution to cancel
543 GNUNET_DNSSTUB_resolve_cancel (struct GNUNET_DNSSTUB_RequestSocket *rs)
546 if (NULL != rs->retry_task)
548 GNUNET_SCHEDULER_cancel (rs->retry_task);
549 rs->retry_task = NULL;
551 if (NULL != rs->read_task)
553 GNUNET_SCHEDULER_cancel (rs->read_task);
554 rs->read_task = NULL;
560 * Start a DNS stub resolver.
562 * @param num_sockets how many sockets should we open
563 * in parallel for DNS queries for this stub?
564 * @return NULL on error
566 struct GNUNET_DNSSTUB_Context *
567 GNUNET_DNSSTUB_start (unsigned int num_sockets)
569 struct GNUNET_DNSSTUB_Context *ctx;
571 if (0 == num_sockets)
576 ctx = GNUNET_new (struct GNUNET_DNSSTUB_Context);
577 ctx->num_sockets = num_sockets;
579 GNUNET_new_array (num_sockets, struct GNUNET_DNSSTUB_RequestSocket);
580 ctx->retry_freq = DNS_RETRANSMIT_DELAY;
586 * Add nameserver for use by the DNSSTUB. We will use
587 * all provided nameservers for resolution (round-robin).
589 * @param ctx resolver context to modify
590 * @param dns_ip target IP address to use (as string)
591 * @return #GNUNET_OK on success
594 GNUNET_DNSSTUB_add_dns_ip (struct GNUNET_DNSSTUB_Context *ctx,
597 struct DnsServer *ds;
601 ds = GNUNET_new (struct DnsServer);
602 if (1 == inet_pton (AF_INET, dns_ip, &i4))
604 struct sockaddr_in *s4 = (struct sockaddr_in *) &ds->ss;
606 s4->sin_family = AF_INET;
607 s4->sin_port = htons (53);
609 #if HAVE_SOCKADDR_IN_SIN_LEN
610 s4->sin_len = (u_char) sizeof (struct sockaddr_in);
613 else if (1 == inet_pton (AF_INET6, dns_ip, &i6))
615 struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) &ds->ss;
617 s6->sin6_family = AF_INET6;
618 s6->sin6_port = htons (53);
620 #if HAVE_SOCKADDR_IN_SIN_LEN
621 s6->sin6_len = (u_char) sizeof (struct sockaddr_in6);
626 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
627 "Malformed IP address `%s' for DNS server\n",
630 return GNUNET_SYSERR;
632 GNUNET_CONTAINER_DLL_insert (ctx->dns_head, ctx->dns_tail, ds);
638 * Add nameserver for use by the DNSSTUB. We will use
639 * all provided nameservers for resolution (round-robin).
641 * @param ctx resolver context to modify
642 * @param sa socket address of DNS resolver to use
643 * @return #GNUNET_OK on success
646 GNUNET_DNSSTUB_add_dns_sa (struct GNUNET_DNSSTUB_Context *ctx,
647 const struct sockaddr *sa)
649 struct DnsServer *ds;
651 ds = GNUNET_new (struct DnsServer);
652 switch (sa->sa_family)
655 GNUNET_memcpy (&ds->ss, sa, sizeof (struct sockaddr_in));
658 GNUNET_memcpy (&ds->ss, sa, sizeof (struct sockaddr_in6));
663 return GNUNET_SYSERR;
665 GNUNET_CONTAINER_DLL_insert (ctx->dns_head, ctx->dns_tail, ds);
671 * How long should we try requests before timing out?
672 * Only effective for requests issued after this call.
674 * @param ctx resolver context to modify
675 * @param retry_freq how long to wait between retries
678 GNUNET_DNSSTUB_set_retry (struct GNUNET_DNSSTUB_Context *ctx,
679 struct GNUNET_TIME_Relative retry_freq)
681 ctx->retry_freq = retry_freq;
686 * Cleanup DNSSTUB resolver.
688 * @param ctx stub resolver to clean up
691 GNUNET_DNSSTUB_stop (struct GNUNET_DNSSTUB_Context *ctx)
693 struct DnsServer *ds;
695 while (NULL != (ds = ctx->dns_head))
697 GNUNET_CONTAINER_DLL_remove (ctx->dns_head, ctx->dns_tail, ds);
700 for (unsigned int i = 0; i < ctx->num_sockets; i++)
701 cleanup_rs (&ctx->sockets[i]);
702 GNUNET_free (ctx->sockets);
707 /* end of dnsstub.c */