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 {
46 * UDP socket we use for this request for IPv4
48 struct GNUNET_NETWORK_Handle *dnsout4;
51 * UDP socket we use for this request for IPv6
53 struct GNUNET_NETWORK_Handle *dnsout6;
56 * Function to call with result.
58 GNUNET_DNSSTUB_ResultCallback rc;
66 * Task for reading from dnsout4 and dnsout6.
68 struct GNUNET_SCHEDULER_Task *read_task;
71 * Task for retrying transmission of the query.
73 struct GNUNET_SCHEDULER_Task *retry_task;
76 * Next address we sent the DNS request to.
78 struct DnsServer *ds_pos;
81 * Context this request executes in.
83 struct GNUNET_DNSSTUB_Context *ctx;
86 * Query we sent to @e addr.
91 * Number of bytes in @a request.
98 * DNS Server used for resolution.
104 struct DnsServer *next;
109 struct DnsServer *prev;
112 * IP address of the DNS resolver.
114 struct sockaddr_storage ss;
119 * Handle to the stub resolver.
121 struct GNUNET_DNSSTUB_Context {
123 * Array of all open sockets for DNS requests.
125 struct GNUNET_DNSSTUB_RequestSocket *sockets;
128 * DLL of DNS resolvers we use.
130 struct DnsServer *dns_head;
133 * DLL of DNS resolvers we use.
135 struct DnsServer *dns_tail;
138 * How frequently do we retry requests?
140 struct GNUNET_TIME_Relative retry_freq;
143 * Length of @e sockets array.
145 unsigned int num_sockets;
150 * We're done with a `struct GNUNET_DNSSTUB_RequestSocket`, close it for now.
152 * @param rs request socket to clean up
155 cleanup_rs(struct GNUNET_DNSSTUB_RequestSocket *rs)
157 if (NULL != rs->dnsout4)
159 GNUNET_NETWORK_socket_close(rs->dnsout4);
162 if (NULL != rs->dnsout6)
164 GNUNET_NETWORK_socket_close(rs->dnsout6);
167 if (NULL != rs->read_task)
169 GNUNET_SCHEDULER_cancel(rs->read_task);
170 rs->read_task = NULL;
172 if (NULL != rs->retry_task)
174 GNUNET_SCHEDULER_cancel(rs->retry_task);
175 rs->retry_task = NULL;
177 if (NULL != rs->request)
179 GNUNET_free(rs->request);
186 * Open source port for sending DNS requests
188 * @param af AF_INET or AF_INET6
189 * @return #GNUNET_OK on success
191 static struct GNUNET_NETWORK_Handle *
194 struct sockaddr_in a4;
195 struct sockaddr_in6 a6;
198 struct GNUNET_NETWORK_Handle *ret;
200 ret = GNUNET_NETWORK_socket_create(af, SOCK_DGRAM, 0);
206 memset(&a4, 0, alen = sizeof(struct sockaddr_in));
207 sa = (struct sockaddr *)&a4;
211 memset(&a6, 0, alen = sizeof(struct sockaddr_in6));
212 sa = (struct sockaddr *)&a6;
217 GNUNET_NETWORK_socket_close(ret);
221 if (GNUNET_OK != GNUNET_NETWORK_socket_bind(ret, sa, alen))
223 GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
224 _("Could not bind to any port: %s\n"),
226 GNUNET_NETWORK_socket_close(ret);
234 * Get a socket of the specified address family to send out a
235 * UDP DNS request to the Internet.
237 * @param ctx the DNSSTUB context
238 * @return NULL on error
240 static struct GNUNET_DNSSTUB_RequestSocket *
241 get_request_socket(struct GNUNET_DNSSTUB_Context *ctx)
243 struct GNUNET_DNSSTUB_RequestSocket *rs;
245 for (unsigned int i = 0; i < 256; i++)
247 rs = &ctx->sockets[GNUNET_CRYPTO_random_u32(GNUNET_CRYPTO_QUALITY_NONCE,
254 /* signal "failure" */
255 rs->rc(rs->rc_cls, NULL, 0);
258 if (NULL != rs->read_task)
260 GNUNET_SCHEDULER_cancel(rs->read_task);
261 rs->read_task = NULL;
263 if (NULL != rs->retry_task)
265 GNUNET_SCHEDULER_cancel(rs->retry_task);
266 rs->retry_task = NULL;
268 if (NULL != rs->request)
270 GNUNET_free(rs->request);
279 * Actually do the reading of a DNS packet from our UDP socket and see
280 * if we have a valid, matching, pending request.
282 * @param rs request socket with callback details
283 * @param dnsout socket to read from
284 * @return #GNUNET_OK on success, #GNUNET_NO on drop, #GNUNET_SYSERR on IO-errors (closed socket)
287 do_dns_read(struct GNUNET_DNSSTUB_RequestSocket *rs,
288 struct GNUNET_NETWORK_Handle *dnsout)
290 struct GNUNET_DNSSTUB_Context *ctx = rs->ctx;
295 if (0 != ioctl(GNUNET_NETWORK_get_fd(dnsout), FIONREAD, &len))
297 /* conservative choice: */
301 /* port the code above? */
304 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Receiving %d byte DNS reply\n", len);
306 unsigned char buf[len] GNUNET_ALIGN;
308 struct sockaddr_storage addr;
310 struct GNUNET_TUN_DnsHeader *dns;
312 addrlen = sizeof(addr);
313 memset(&addr, 0, sizeof(addr));
314 r = GNUNET_NETWORK_socket_recvfrom(dnsout,
317 (struct sockaddr *)&addr,
321 GNUNET_log_strerror(GNUNET_ERROR_TYPE_ERROR, "recvfrom");
322 GNUNET_NETWORK_socket_close(dnsout);
323 return GNUNET_SYSERR;
326 for (struct DnsServer *ds = ctx->dns_head; NULL != ds; ds = ds->next)
328 if (0 == memcmp(&addr,
330 GNUNET_MIN(sizeof(struct sockaddr_storage), addrlen)))
336 if (GNUNET_NO == found)
338 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
339 "Received DNS response from server we never asked (ignored)");
342 if (sizeof(struct GNUNET_TUN_DnsHeader) > (size_t)r)
344 GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
345 _("Received DNS response that is too small (%u bytes)"),
349 dns = (struct GNUNET_TUN_DnsHeader *)buf;
352 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
353 "Request timeout or cancelled; ignoring reply\n");
356 rs->rc(rs->rc_cls, dns, r);
363 * Read a DNS response from the (unhindered) UDP-Socket
365 * @param cls socket to read from
368 read_response(void *cls);
372 * Schedule #read_response() task for @a rs.
374 * @param rs request to schedule read operation for
377 schedule_read(struct GNUNET_DNSSTUB_RequestSocket *rs)
379 struct GNUNET_NETWORK_FDSet *rset;
381 if (NULL != rs->read_task)
382 GNUNET_SCHEDULER_cancel(rs->read_task);
383 rset = GNUNET_NETWORK_fdset_create();
384 if (NULL != rs->dnsout4)
385 GNUNET_NETWORK_fdset_set(rset, rs->dnsout4);
386 if (NULL != rs->dnsout6)
387 GNUNET_NETWORK_fdset_set(rset, rs->dnsout6);
389 GNUNET_SCHEDULER_add_select(GNUNET_SCHEDULER_PRIORITY_DEFAULT,
390 GNUNET_TIME_UNIT_FOREVER_REL,
395 GNUNET_NETWORK_fdset_destroy(rset);
400 * Read a DNS response from the (unhindered) UDP-Socket
402 * @param cls `struct GNUNET_DNSSTUB_RequestSocket` to read from
405 read_response(void *cls)
407 struct GNUNET_DNSSTUB_RequestSocket *rs = cls;
408 const struct GNUNET_SCHEDULER_TaskContext *tc;
410 rs->read_task = NULL;
411 tc = GNUNET_SCHEDULER_get_task_context();
412 /* read and process ready sockets */
413 if ((NULL != rs->dnsout4) &&
414 (GNUNET_NETWORK_fdset_isset(tc->read_ready, rs->dnsout4)) &&
415 (GNUNET_SYSERR == do_dns_read(rs, rs->dnsout4)))
417 if ((NULL != rs->dnsout6) &&
418 (GNUNET_NETWORK_fdset_isset(tc->read_ready, rs->dnsout6)) &&
419 (GNUNET_SYSERR == do_dns_read(rs, rs->dnsout6)))
421 /* re-schedule read task */
427 * Task to (re)transmit the DNS query, possibly repeatedly until
430 * @param cls our `struct GNUNET_DNSSTUB_RequestSocket *`
433 transmit_query(void *cls)
435 struct GNUNET_DNSSTUB_RequestSocket *rs = cls;
436 struct GNUNET_DNSSTUB_Context *ctx = rs->ctx;
437 const struct sockaddr *sa;
439 struct DnsServer *ds;
440 struct GNUNET_NETWORK_Handle *dnsout;
443 GNUNET_SCHEDULER_add_delayed(ctx->retry_freq, &transmit_query, rs);
445 rs->ds_pos = ds->next;
446 if (NULL == rs->ds_pos)
447 rs->ds_pos = ctx->dns_head;
448 GNUNET_assert(NULL != ds);
450 switch (ds->ss.ss_family)
453 if (NULL == rs->dnsout4)
454 rs->dnsout4 = open_socket(AF_INET);
455 dnsout = rs->dnsout4;
456 sa = (const struct sockaddr *)&ds->ss;
457 salen = sizeof(struct sockaddr_in);
461 if (NULL == rs->dnsout6)
462 rs->dnsout6 = open_socket(AF_INET6);
463 dnsout = rs->dnsout6;
464 sa = (const struct sockaddr *)&ds->ss;
465 salen = sizeof(struct sockaddr_in6);
473 GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
474 "Unable to use configure DNS server, skipping\n");
477 if (GNUNET_SYSERR == GNUNET_NETWORK_socket_sendto(dnsout,
482 GNUNET_log(GNUNET_ERROR_TYPE_WARNING,
483 _("Failed to send DNS request to %s: %s\n"),
484 GNUNET_a2s(sa, salen),
487 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
488 _("Sent DNS request to %s\n"),
489 GNUNET_a2s(sa, salen));
495 * Perform DNS resolution using our default IP from init.
497 * @param ctx stub resolver to use
498 * @param request DNS request to transmit
499 * @param request_len number of bytes in msg
500 * @param rc function to call with result
501 * @param rc_cls closure for 'rc'
502 * @return socket used for the request, NULL on error
504 struct GNUNET_DNSSTUB_RequestSocket *
505 GNUNET_DNSSTUB_resolve(struct GNUNET_DNSSTUB_Context *ctx,
508 GNUNET_DNSSTUB_ResultCallback rc,
511 struct GNUNET_DNSSTUB_RequestSocket *rs;
513 if (NULL == ctx->dns_head)
515 GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
516 "No DNS server configured for resolution\n");
519 if (NULL == (rs = get_request_socket(ctx)))
521 GNUNET_log(GNUNET_ERROR_TYPE_ERROR,
522 "No request socket available for DNS resolution\n");
525 rs->ds_pos = ctx->dns_head;
528 rs->request = GNUNET_memdup(request, request_len);
529 rs->request_len = request_len;
530 rs->retry_task = GNUNET_SCHEDULER_add_now(&transmit_query, rs);
536 * Cancel DNS resolution.
538 * @param rs resolution to cancel
541 GNUNET_DNSSTUB_resolve_cancel(struct GNUNET_DNSSTUB_RequestSocket *rs)
544 if (NULL != rs->retry_task)
546 GNUNET_SCHEDULER_cancel(rs->retry_task);
547 rs->retry_task = NULL;
549 if (NULL != rs->read_task)
551 GNUNET_SCHEDULER_cancel(rs->read_task);
552 rs->read_task = NULL;
558 * Start a DNS stub resolver.
560 * @param num_sockets how many sockets should we open
561 * in parallel for DNS queries for this stub?
562 * @return NULL on error
564 struct GNUNET_DNSSTUB_Context *
565 GNUNET_DNSSTUB_start(unsigned int num_sockets)
567 struct GNUNET_DNSSTUB_Context *ctx;
569 if (0 == num_sockets)
574 ctx = GNUNET_new(struct GNUNET_DNSSTUB_Context);
575 ctx->num_sockets = num_sockets;
577 GNUNET_new_array(num_sockets, struct GNUNET_DNSSTUB_RequestSocket);
578 ctx->retry_freq = DNS_RETRANSMIT_DELAY;
584 * Add nameserver for use by the DNSSTUB. We will use
585 * all provided nameservers for resolution (round-robin).
587 * @param ctx resolver context to modify
588 * @param dns_ip target IP address to use (as string)
589 * @return #GNUNET_OK on success
592 GNUNET_DNSSTUB_add_dns_ip(struct GNUNET_DNSSTUB_Context *ctx,
595 struct DnsServer *ds;
599 ds = GNUNET_new(struct DnsServer);
600 if (1 == inet_pton(AF_INET, dns_ip, &i4))
602 struct sockaddr_in *s4 = (struct sockaddr_in *)&ds->ss;
604 s4->sin_family = AF_INET;
605 s4->sin_port = htons(53);
607 #if HAVE_SOCKADDR_IN_SIN_LEN
608 s4->sin_len = (u_char)sizeof(struct sockaddr_in);
611 else if (1 == inet_pton(AF_INET6, dns_ip, &i6))
613 struct sockaddr_in6 *s6 = (struct sockaddr_in6 *)&ds->ss;
615 s6->sin6_family = AF_INET6;
616 s6->sin6_port = htons(53);
618 #if HAVE_SOCKADDR_IN_SIN_LEN
619 s6->sin6_len = (u_char)sizeof(struct sockaddr_in6);
624 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
625 "Malformed IP address `%s' for DNS server\n",
628 return GNUNET_SYSERR;
630 GNUNET_CONTAINER_DLL_insert(ctx->dns_head, ctx->dns_tail, ds);
636 * Add nameserver for use by the DNSSTUB. We will use
637 * all provided nameservers for resolution (round-robin).
639 * @param ctx resolver context to modify
640 * @param sa socket address of DNS resolver to use
641 * @return #GNUNET_OK on success
644 GNUNET_DNSSTUB_add_dns_sa(struct GNUNET_DNSSTUB_Context *ctx,
645 const struct sockaddr *sa)
647 struct DnsServer *ds;
649 ds = GNUNET_new(struct DnsServer);
650 switch (sa->sa_family)
653 GNUNET_memcpy(&ds->ss, sa, sizeof(struct sockaddr_in));
657 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 */