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 * How many DNS sockets do we open at most at the same time?
37 * (technical socket maximum is this number x2 for IPv4+IPv6)
39 #define DNS_SOCKET_MAX 128
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 * When should this request time out?
76 struct GNUNET_TIME_Absolute timeout;
79 * Address we sent the DNS request to.
81 struct sockaddr_storage addr;
84 * Number of bytes in @e addr.
92 * Handle to the stub resolver.
94 struct GNUNET_DNSSTUB_Context
98 * Array of all open sockets for DNS requests.
100 struct GNUNET_DNSSTUB_RequestSocket sockets[DNS_SOCKET_MAX];
103 * IP address to use for the DNS server if we are a DNS exit service
104 * (for VPN via cadet); otherwise NULL.
112 * We're done with a `struct GNUNET_DNSSTUB_RequestSocket`, close it for now.
114 * @param rs request socket to clean up
117 cleanup_rs (struct GNUNET_DNSSTUB_RequestSocket *rs)
119 if (NULL != rs->dnsout4)
121 GNUNET_NETWORK_socket_close (rs->dnsout4);
124 if (NULL != rs->dnsout6)
126 GNUNET_NETWORK_socket_close (rs->dnsout6);
129 if (NULL != rs->read_task)
131 GNUNET_SCHEDULER_cancel (rs->read_task);
132 rs->read_task = NULL;
138 * Open source port for sending DNS requests
140 * @param af AF_INET or AF_INET6
141 * @return #GNUNET_OK on success
143 static struct GNUNET_NETWORK_Handle *
146 struct sockaddr_in a4;
147 struct sockaddr_in6 a6;
150 struct GNUNET_NETWORK_Handle *ret;
152 ret = GNUNET_NETWORK_socket_create (af, SOCK_DGRAM, 0);
158 memset (&a4, 0, alen = sizeof (struct sockaddr_in));
159 sa = (struct sockaddr *) &a4;
162 memset (&a6, 0, alen = sizeof (struct sockaddr_in6));
163 sa = (struct sockaddr *) &a6;
167 GNUNET_NETWORK_socket_close (ret);
171 if (GNUNET_OK != GNUNET_NETWORK_socket_bind (ret,
175 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
176 _("Could not bind to any port: %s\n"),
178 GNUNET_NETWORK_socket_close (ret);
186 * Read a DNS response from the (unhindered) UDP-Socket
188 * @param cls socket to read from
191 read_response (void *cls);
195 * Get a socket of the specified address family to send out a
196 * UDP DNS request to the Internet.
198 * @param ctx the DNSSTUB context
199 * @param af desired address family
200 * @return NULL on error (given AF not "supported")
202 static struct GNUNET_DNSSTUB_RequestSocket *
203 get_request_socket (struct GNUNET_DNSSTUB_Context *ctx,
206 struct GNUNET_DNSSTUB_RequestSocket *rs;
207 struct GNUNET_NETWORK_FDSet *rset;
209 rs = &ctx->sockets[GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE,
211 rs->timeout = GNUNET_TIME_relative_to_absolute (REQUEST_TIMEOUT);
215 if (NULL == rs->dnsout4)
216 rs->dnsout4 = open_socket (AF_INET);
219 if (NULL == rs->dnsout6)
220 rs->dnsout6 = open_socket (AF_INET6);
225 if (NULL != rs->read_task)
227 GNUNET_SCHEDULER_cancel (rs->read_task);
228 rs->read_task = NULL;
230 if ( (NULL == rs->dnsout4) &&
231 (NULL == rs->dnsout6) )
233 rset = GNUNET_NETWORK_fdset_create ();
234 if (NULL != rs->dnsout4)
235 GNUNET_NETWORK_fdset_set (rset, rs->dnsout4);
236 if (NULL != rs->dnsout6)
237 GNUNET_NETWORK_fdset_set (rset, rs->dnsout6);
238 rs->read_task = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
243 GNUNET_NETWORK_fdset_destroy (rset);
249 * Perform DNS resolution.
251 * @param ctx stub resolver to use
252 * @param sa the socket address
253 * @param sa_len the socket length
254 * @param request DNS request to transmit
255 * @param request_len number of bytes in msg
256 * @param rc function to call with result
257 * @param rc_cls closure for 'rc'
258 * @return socket used for the request, NULL on error
260 struct GNUNET_DNSSTUB_RequestSocket *
261 GNUNET_DNSSTUB_resolve (struct GNUNET_DNSSTUB_Context *ctx,
262 const struct sockaddr *sa,
266 GNUNET_DNSSTUB_ResultCallback rc,
269 struct GNUNET_DNSSTUB_RequestSocket *rs;
270 struct GNUNET_NETWORK_Handle *ret;
274 if (NULL == (rs = get_request_socket (ctx, af)))
276 if (NULL != rs->dnsout4)
280 GNUNET_assert (NULL != ret);
281 GNUNET_memcpy (&rs->addr,
284 rs->addrlen = sa_len;
288 GNUNET_NETWORK_socket_sendto (ret,
293 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
294 _("Failed to send DNS request to %s\n"),
295 GNUNET_a2s (sa, sa_len));
297 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
298 _("Sent DNS request to %s\n"),
299 GNUNET_a2s (sa, sa_len));
305 * Perform DNS resolution using our default IP from init.
307 * @param ctx stub resolver to use
308 * @param request DNS request to transmit
309 * @param request_len number of bytes in msg
310 * @param rc function to call with result
311 * @param rc_cls closure for 'rc'
312 * @return socket used for the request, NULL on error
314 struct GNUNET_DNSSTUB_RequestSocket *
315 GNUNET_DNSSTUB_resolve2 (struct GNUNET_DNSSTUB_Context *ctx,
318 GNUNET_DNSSTUB_ResultCallback rc,
322 struct sockaddr_in v4;
323 struct sockaddr_in6 v6;
326 struct GNUNET_NETWORK_Handle *dnsout;
327 struct GNUNET_DNSSTUB_RequestSocket *rs;
329 memset (&v4, 0, sizeof (v4));
330 memset (&v6, 0, sizeof (v6));
331 if (1 == inet_pton (AF_INET, ctx->dns_exit, &v4.sin_addr))
334 v4.sin_family = AF_INET;
335 v4.sin_port = htons (53);
336 #if HAVE_SOCKADDR_IN_SIN_LEN
337 v4.sin_len = (u_char) salen;
339 sa = (struct sockaddr *) &v4;
342 else if (1 == inet_pton (AF_INET6, ctx->dns_exit, &v6.sin6_addr))
345 v6.sin6_family = AF_INET6;
346 v6.sin6_port = htons (53);
347 #if HAVE_SOCKADDR_IN_SIN_LEN
348 v6.sin6_len = (u_char) salen;
350 sa = (struct sockaddr *) &v6;
358 if (NULL == (rs = get_request_socket (ctx, af)))
360 if (NULL != rs->dnsout4)
361 dnsout = rs->dnsout4;
363 dnsout = rs->dnsout6;
366 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
367 _("Configured DNS exit `%s' is not working / valid.\n"),
371 GNUNET_memcpy (&rs->addr,
378 GNUNET_NETWORK_socket_sendto (dnsout,
380 request_len, sa, salen))
381 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
382 _("Failed to send DNS request to %s\n"),
383 GNUNET_a2s (sa, salen));
384 rs->timeout = GNUNET_TIME_relative_to_absolute (REQUEST_TIMEOUT);
390 * Actually do the reading of a DNS packet from our UDP socket and see
391 * if we have a valid, matching, pending request.
393 * @param rs request socket with callback details
394 * @param dnsout socket to read from
395 * @return #GNUNET_OK on success, #GNUNET_NO on drop, #GNUNET_SYSERR on IO-errors (closed socket)
398 do_dns_read (struct GNUNET_DNSSTUB_RequestSocket *rs,
399 struct GNUNET_NETWORK_Handle *dnsout)
401 struct sockaddr_storage addr;
403 struct GNUNET_TUN_DnsHeader *dns;
408 if (0 != ioctl (GNUNET_NETWORK_get_fd (dnsout), FIONREAD, &len))
410 /* conservative choice: */
414 /* port the code above? */
417 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
418 "Receiving %d byte DNS reply\n",
421 unsigned char buf[len] GNUNET_ALIGN;
423 addrlen = sizeof (addr);
424 memset (&addr, 0, sizeof (addr));
425 r = GNUNET_NETWORK_socket_recvfrom (dnsout,
427 (struct sockaddr*) &addr, &addrlen);
430 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "recvfrom");
431 GNUNET_NETWORK_socket_close (dnsout);
432 return GNUNET_SYSERR;
434 if (sizeof (struct GNUNET_TUN_DnsHeader) > r)
436 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
437 _("Received DNS response that is too small (%u bytes)"),
441 dns = (struct GNUNET_TUN_DnsHeader *) buf;
442 if ( (addrlen != rs->addrlen) ||
444 GNUNET_TUN_sockaddr_cmp ((struct sockaddr *) &rs->addr,
445 (struct sockaddr *) &addr,
447 (0 == GNUNET_TIME_absolute_get_remaining (rs->timeout).rel_value_us) )
449 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
450 "Request timeout or invalid sender address; ignoring reply\n");
464 * Read a DNS response from the (unhindered) UDP-Socket
466 * @param cls socket to read from
469 read_response (void *cls)
471 struct GNUNET_DNSSTUB_RequestSocket *rs = cls;
472 struct GNUNET_NETWORK_FDSet *rset;
473 const struct GNUNET_SCHEDULER_TaskContext *tc;
475 rs->read_task = NULL;
476 tc = GNUNET_SCHEDULER_get_task_context ();
477 if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_READ_READY))
483 /* read and process ready sockets */
484 if ((NULL != rs->dnsout4) &&
485 (GNUNET_NETWORK_fdset_isset (tc->read_ready, rs->dnsout4)) &&
486 (GNUNET_SYSERR == do_dns_read (rs, rs->dnsout4)))
488 if ((NULL != rs->dnsout6) &&
489 (GNUNET_NETWORK_fdset_isset (tc->read_ready, rs->dnsout6)) &&
490 (GNUNET_SYSERR == do_dns_read (rs, rs->dnsout6)))
493 /* re-schedule read task */
494 rset = GNUNET_NETWORK_fdset_create ();
495 if (NULL != rs->dnsout4)
496 GNUNET_NETWORK_fdset_set (rset, rs->dnsout4);
497 if (NULL != rs->dnsout6)
498 GNUNET_NETWORK_fdset_set (rset, rs->dnsout6);
499 rs->read_task = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
500 GNUNET_TIME_absolute_get_remaining (rs->timeout),
504 GNUNET_NETWORK_fdset_destroy (rset);
509 * Cancel DNS resolution.
511 * @param rs resolution to cancel
514 GNUNET_DNSSTUB_resolve_cancel (struct GNUNET_DNSSTUB_RequestSocket *rs)
521 * Start a DNS stub resolver.
523 * @param dns_ip target IP address to use
524 * @return NULL on error
526 struct GNUNET_DNSSTUB_Context *
527 GNUNET_DNSSTUB_start (const char *dns_ip)
529 struct GNUNET_DNSSTUB_Context *ctx;
531 ctx = GNUNET_new (struct GNUNET_DNSSTUB_Context);
533 ctx->dns_exit = GNUNET_strdup (dns_ip);
539 * Cleanup DNSSTUB resolver.
541 * @param ctx stub resolver to clean up
544 GNUNET_DNSSTUB_stop (struct GNUNET_DNSSTUB_Context *ctx)
548 for (i=0;i<DNS_SOCKET_MAX;i++)
549 cleanup_rs (&ctx->sockets[i]);
550 if (NULL != ctx->dns_exit)
552 GNUNET_free (ctx->dns_exit);
553 ctx->dns_exit = NULL;
559 /* end of dnsstub.c */