2 This file is part of GNUnet.
3 Copyright (C) 2012 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 an external (Internet-DNS) DNS resolution
33 #define REQUEST_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
36 * Timeout for retrying DNS queries.
38 #define DNS_RETRANSMIT_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 250)
41 * How many DNS sockets do we open at most at the same time?
42 * (technical socket maximum is this number x2 for IPv4+IPv6)
44 #define DNS_SOCKET_MAX 128
48 * UDP socket we are using for sending DNS requests to the Internet.
50 struct GNUNET_DNSSTUB_RequestSocket
54 * UDP socket we use for this request for IPv4
56 struct GNUNET_NETWORK_Handle *dnsout4;
59 * UDP socket we use for this request for IPv6
61 struct GNUNET_NETWORK_Handle *dnsout6;
64 * Function to call with result.
66 GNUNET_DNSSTUB_ResultCallback rc;
74 * Task for reading from dnsout4 and dnsout6.
76 struct GNUNET_SCHEDULER_Task *read_task;
79 * Task for retrying transmission of the query.
81 struct GNUNET_SCHEDULER_Task *retry_task;
84 * When should this request time out?
86 struct GNUNET_TIME_Absolute timeout;
89 * Address we sent the DNS request to.
91 struct sockaddr_storage addr;
94 * Number of bytes in @e addr.
99 * Query we sent to @e addr.
104 * Number of bytes in @a request.
112 * Handle to the stub resolver.
114 struct GNUNET_DNSSTUB_Context
118 * Array of all open sockets for DNS requests.
120 struct GNUNET_DNSSTUB_RequestSocket sockets[DNS_SOCKET_MAX];
123 * IP address to use for the DNS server if we are a DNS exit service
124 * (for VPN via cadet); otherwise NULL.
131 * We're done with a `struct GNUNET_DNSSTUB_RequestSocket`, close it for now.
133 * @param rs request socket to clean up
136 cleanup_rs (struct GNUNET_DNSSTUB_RequestSocket *rs)
138 if (NULL != rs->dnsout4)
140 GNUNET_NETWORK_socket_close (rs->dnsout4);
143 if (NULL != rs->dnsout6)
145 GNUNET_NETWORK_socket_close (rs->dnsout6);
148 if (NULL != rs->read_task)
150 GNUNET_SCHEDULER_cancel (rs->read_task);
151 rs->read_task = NULL;
153 if (NULL != rs->retry_task)
155 GNUNET_SCHEDULER_cancel (rs->retry_task);
156 rs->retry_task = NULL;
158 if (NULL != rs->request)
160 GNUNET_free (rs->request);
167 * Open source port for sending DNS requests
169 * @param af AF_INET or AF_INET6
170 * @return #GNUNET_OK on success
172 static struct GNUNET_NETWORK_Handle *
175 struct sockaddr_in a4;
176 struct sockaddr_in6 a6;
179 struct GNUNET_NETWORK_Handle *ret;
181 ret = GNUNET_NETWORK_socket_create (af, SOCK_DGRAM, 0);
187 memset (&a4, 0, alen = sizeof (struct sockaddr_in));
188 sa = (struct sockaddr *) &a4;
191 memset (&a6, 0, alen = sizeof (struct sockaddr_in6));
192 sa = (struct sockaddr *) &a6;
196 GNUNET_NETWORK_socket_close (ret);
200 if (GNUNET_OK != GNUNET_NETWORK_socket_bind (ret,
204 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
205 _("Could not bind to any port: %s\n"),
207 GNUNET_NETWORK_socket_close (ret);
215 * Read a DNS response from the (unhindered) UDP-Socket
217 * @param cls socket to read from
220 read_response (void *cls);
224 * Get a socket of the specified address family to send out a
225 * UDP DNS request to the Internet.
227 * @param ctx the DNSSTUB context
228 * @param af desired address family
229 * @return NULL on error (given AF not "supported")
231 static struct GNUNET_DNSSTUB_RequestSocket *
232 get_request_socket (struct GNUNET_DNSSTUB_Context *ctx,
235 struct GNUNET_DNSSTUB_RequestSocket *rs;
236 struct GNUNET_NETWORK_FDSet *rset;
238 rs = &ctx->sockets[GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE,
240 rs->timeout = GNUNET_TIME_relative_to_absolute (REQUEST_TIMEOUT);
244 if (NULL == rs->dnsout4)
245 rs->dnsout4 = open_socket (AF_INET);
248 if (NULL == rs->dnsout6)
249 rs->dnsout6 = open_socket (AF_INET6);
254 if (NULL != rs->read_task)
256 GNUNET_SCHEDULER_cancel (rs->read_task);
257 rs->read_task = NULL;
259 if (NULL != rs->retry_task)
261 GNUNET_SCHEDULER_cancel (rs->retry_task);
262 rs->retry_task = NULL;
264 if (NULL != rs->request)
266 GNUNET_free (rs->request);
269 if ( (NULL == rs->dnsout4) &&
270 (NULL == rs->dnsout6) )
272 rset = GNUNET_NETWORK_fdset_create ();
273 if (NULL != rs->dnsout4)
274 GNUNET_NETWORK_fdset_set (rset, rs->dnsout4);
275 if (NULL != rs->dnsout6)
276 GNUNET_NETWORK_fdset_set (rset, rs->dnsout6);
277 rs->read_task = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
283 GNUNET_NETWORK_fdset_destroy (rset);
289 * Task to (re)transmit the DNS query, possibly repeatedly until
292 * @param cls our `struct GNUNET_DNSSTUB_RequestSocket *`
295 transmit_query (void *cls)
297 struct GNUNET_DNSSTUB_RequestSocket *rs = cls;
298 struct GNUNET_NETWORK_Handle *ret;
300 rs->retry_task = NULL;
301 ret = (NULL != rs->dnsout4) ? rs->dnsout4 : rs->dnsout6;
302 GNUNET_assert (NULL != ret);
304 GNUNET_NETWORK_socket_sendto (ret,
307 (struct sockaddr *) &rs->addr,
309 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
310 _("Failed to send DNS request to %s\n"),
311 GNUNET_a2s ((struct sockaddr *) &rs->addr,
314 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
315 _("Sent DNS request to %s\n"),
316 GNUNET_a2s ((struct sockaddr *) &rs->addr,
318 rs->retry_task = GNUNET_SCHEDULER_add_delayed (DNS_RETRANSMIT_DELAY,
325 * Perform DNS resolution.
327 * @param ctx stub resolver to use
328 * @param sa the socket address
329 * @param sa_len the socket length
330 * @param request DNS request to transmit
331 * @param request_len number of bytes in msg
332 * @param rc function to call with result
333 * @param rc_cls closure for 'rc'
334 * @return socket used for the request, NULL on error
336 struct GNUNET_DNSSTUB_RequestSocket *
337 GNUNET_DNSSTUB_resolve (struct GNUNET_DNSSTUB_Context *ctx,
338 const struct sockaddr *sa,
342 GNUNET_DNSSTUB_ResultCallback rc,
345 struct GNUNET_DNSSTUB_RequestSocket *rs;
347 if (NULL == (rs = get_request_socket (ctx,
350 GNUNET_memcpy (&rs->addr,
353 rs->addrlen = sa_len;
356 rs->request = GNUNET_memdup (request,
358 rs->request_len = request_len;
359 rs->retry_task = GNUNET_SCHEDULER_add_now (&transmit_query,
366 * Perform DNS resolution using our default IP from init.
368 * @param ctx stub resolver to use
369 * @param request DNS request to transmit
370 * @param request_len number of bytes in msg
371 * @param rc function to call with result
372 * @param rc_cls closure for 'rc'
373 * @return socket used for the request, NULL on error
375 struct GNUNET_DNSSTUB_RequestSocket *
376 GNUNET_DNSSTUB_resolve2 (struct GNUNET_DNSSTUB_Context *ctx,
379 GNUNET_DNSSTUB_ResultCallback rc,
383 struct sockaddr_in v4;
384 struct sockaddr_in6 v6;
387 struct GNUNET_NETWORK_Handle *dnsout;
388 struct GNUNET_DNSSTUB_RequestSocket *rs;
390 memset (&v4, 0, sizeof (v4));
391 memset (&v6, 0, sizeof (v6));
392 if (1 == inet_pton (AF_INET, ctx->dns_exit, &v4.sin_addr))
395 v4.sin_family = AF_INET;
396 v4.sin_port = htons (53);
397 #if HAVE_SOCKADDR_IN_SIN_LEN
398 v4.sin_len = (u_char) salen;
400 sa = (struct sockaddr *) &v4;
403 else if (1 == inet_pton (AF_INET6, ctx->dns_exit, &v6.sin6_addr))
406 v6.sin6_family = AF_INET6;
407 v6.sin6_port = htons (53);
408 #if HAVE_SOCKADDR_IN_SIN_LEN
409 v6.sin6_len = (u_char) salen;
411 sa = (struct sockaddr *) &v6;
419 if (NULL == (rs = get_request_socket (ctx, af)))
421 if (NULL != rs->dnsout4)
422 dnsout = rs->dnsout4;
424 dnsout = rs->dnsout6;
427 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
428 _("Configured DNS exit `%s' is not working / valid.\n"),
432 GNUNET_memcpy (&rs->addr,
439 GNUNET_NETWORK_socket_sendto (dnsout,
441 request_len, sa, salen))
442 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
443 _("Failed to send DNS request to %s\n"),
444 GNUNET_a2s (sa, salen));
445 rs->timeout = GNUNET_TIME_relative_to_absolute (REQUEST_TIMEOUT);
451 * Actually do the reading of a DNS packet from our UDP socket and see
452 * if we have a valid, matching, pending request.
454 * @param rs request socket with callback details
455 * @param dnsout socket to read from
456 * @return #GNUNET_OK on success, #GNUNET_NO on drop, #GNUNET_SYSERR on IO-errors (closed socket)
459 do_dns_read (struct GNUNET_DNSSTUB_RequestSocket *rs,
460 struct GNUNET_NETWORK_Handle *dnsout)
462 struct sockaddr_storage addr;
464 struct GNUNET_TUN_DnsHeader *dns;
469 if (0 != ioctl (GNUNET_NETWORK_get_fd (dnsout), FIONREAD, &len))
471 /* conservative choice: */
475 /* port the code above? */
478 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
479 "Receiving %d byte DNS reply\n",
482 unsigned char buf[len] GNUNET_ALIGN;
484 addrlen = sizeof (addr);
485 memset (&addr, 0, sizeof (addr));
486 r = GNUNET_NETWORK_socket_recvfrom (dnsout,
488 (struct sockaddr*) &addr, &addrlen);
491 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "recvfrom");
492 GNUNET_NETWORK_socket_close (dnsout);
493 return GNUNET_SYSERR;
495 if (sizeof (struct GNUNET_TUN_DnsHeader) > r)
497 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
498 _("Received DNS response that is too small (%u bytes)"),
502 dns = (struct GNUNET_TUN_DnsHeader *) buf;
503 if ( (addrlen != rs->addrlen) ||
505 GNUNET_TUN_sockaddr_cmp ((struct sockaddr *) &rs->addr,
506 (struct sockaddr *) &addr,
508 (0 == GNUNET_TIME_absolute_get_remaining (rs->timeout).rel_value_us) )
510 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
511 "Request timeout or invalid sender address; ignoring reply\n");
525 * Read a DNS response from the (unhindered) UDP-Socket
527 * @param cls socket to read from
530 read_response (void *cls)
532 struct GNUNET_DNSSTUB_RequestSocket *rs = cls;
533 struct GNUNET_NETWORK_FDSet *rset;
534 const struct GNUNET_SCHEDULER_TaskContext *tc;
536 rs->read_task = NULL;
537 tc = GNUNET_SCHEDULER_get_task_context ();
538 if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_READ_READY))
544 /* read and process ready sockets */
545 if ((NULL != rs->dnsout4) &&
546 (GNUNET_NETWORK_fdset_isset (tc->read_ready, rs->dnsout4)) &&
547 (GNUNET_SYSERR == do_dns_read (rs, rs->dnsout4)))
549 if ((NULL != rs->dnsout6) &&
550 (GNUNET_NETWORK_fdset_isset (tc->read_ready, rs->dnsout6)) &&
551 (GNUNET_SYSERR == do_dns_read (rs, rs->dnsout6)))
554 /* re-schedule read task */
555 rset = GNUNET_NETWORK_fdset_create ();
556 if (NULL != rs->dnsout4)
557 GNUNET_NETWORK_fdset_set (rset, rs->dnsout4);
558 if (NULL != rs->dnsout6)
559 GNUNET_NETWORK_fdset_set (rset, rs->dnsout6);
560 rs->read_task = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
561 GNUNET_TIME_absolute_get_remaining (rs->timeout),
565 GNUNET_NETWORK_fdset_destroy (rset);
570 * Cancel DNS resolution.
572 * @param rs resolution to cancel
575 GNUNET_DNSSTUB_resolve_cancel (struct GNUNET_DNSSTUB_RequestSocket *rs)
582 * Start a DNS stub resolver.
584 * @param dns_ip target IP address to use
585 * @return NULL on error
587 struct GNUNET_DNSSTUB_Context *
588 GNUNET_DNSSTUB_start (const char *dns_ip)
590 struct GNUNET_DNSSTUB_Context *ctx;
592 ctx = GNUNET_new (struct GNUNET_DNSSTUB_Context);
594 ctx->dns_exit = GNUNET_strdup (dns_ip);
600 * Cleanup DNSSTUB resolver.
602 * @param ctx stub resolver to clean up
605 GNUNET_DNSSTUB_stop (struct GNUNET_DNSSTUB_Context *ctx)
609 for (i=0;i<DNS_SOCKET_MAX;i++)
610 cleanup_rs (&ctx->sockets[i]);
611 if (NULL != ctx->dns_exit)
613 GNUNET_free (ctx->dns_exit);
614 ctx->dns_exit = NULL;
620 /* end of dnsstub.c */