aboutsummaryrefslogtreecommitdiff
path: root/src/util/dnsstub.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/util/dnsstub.c')
-rw-r--r--src/util/dnsstub.c747
1 files changed, 747 insertions, 0 deletions
diff --git a/src/util/dnsstub.c b/src/util/dnsstub.c
new file mode 100644
index 000000000..5b84e6e63
--- /dev/null
+++ b/src/util/dnsstub.c
@@ -0,0 +1,747 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012, 2018 GNUnet e.V.
4
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.
9
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.
14
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/>.
17*/
18/**
19 * @file dns/dnsstub.c
20 * @brief DNS stub resolver which sends DNS requests to an actual resolver
21 * @author Christian Grothoff
22 */
23#include "platform.h"
24#include "gnunet_util_lib.h"
25
26/**
27 * Timeout for retrying DNS queries.
28 */
29#define DNS_RETRANSMIT_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 250)
30
31
32/**
33 * DNS Server used for resolution.
34 */
35struct DnsServer;
36
37
38/**
39 * UDP socket we are using for sending DNS requests to the Internet.
40 */
41struct GNUNET_DNSSTUB_RequestSocket
42{
43
44 /**
45 * UDP socket we use for this request for IPv4
46 */
47 struct GNUNET_NETWORK_Handle *dnsout4;
48
49 /**
50 * UDP socket we use for this request for IPv6
51 */
52 struct GNUNET_NETWORK_Handle *dnsout6;
53
54 /**
55 * Function to call with result.
56 */
57 GNUNET_DNSSTUB_ResultCallback rc;
58
59 /**
60 * Closure for @e rc.
61 */
62 void *rc_cls;
63
64 /**
65 * Task for reading from dnsout4 and dnsout6.
66 */
67 struct GNUNET_SCHEDULER_Task *read_task;
68
69 /**
70 * Task for retrying transmission of the query.
71 */
72 struct GNUNET_SCHEDULER_Task *retry_task;
73
74 /**
75 * Next address we sent the DNS request to.
76 */
77 struct DnsServer *ds_pos;
78
79 /**
80 * Context this request executes in.
81 */
82 struct GNUNET_DNSSTUB_Context *ctx;
83
84 /**
85 * Query we sent to @e addr.
86 */
87 void *request;
88
89 /**
90 * Number of bytes in @a request.
91 */
92 size_t request_len;
93
94};
95
96
97/**
98 * DNS Server used for resolution.
99 */
100struct DnsServer
101{
102
103 /**
104 * Kept in a DLL.
105 */
106 struct DnsServer *next;
107
108 /**
109 * Kept in a DLL.
110 */
111 struct DnsServer *prev;
112
113 /**
114 * IP address of the DNS resolver.
115 */
116 struct sockaddr_storage ss;
117};
118
119
120/**
121 * Handle to the stub resolver.
122 */
123struct GNUNET_DNSSTUB_Context
124{
125
126 /**
127 * Array of all open sockets for DNS requests.
128 */
129 struct GNUNET_DNSSTUB_RequestSocket *sockets;
130
131 /**
132 * DLL of DNS resolvers we use.
133 */
134 struct DnsServer *dns_head;
135
136 /**
137 * DLL of DNS resolvers we use.
138 */
139 struct DnsServer *dns_tail;
140
141 /**
142 * How frequently do we retry requests?
143 */
144 struct GNUNET_TIME_Relative retry_freq;
145
146 /**
147 * Length of @e sockets array.
148 */
149 unsigned int num_sockets;
150
151};
152
153
154/**
155 * We're done with a `struct GNUNET_DNSSTUB_RequestSocket`, close it for now.
156 *
157 * @param rs request socket to clean up
158 */
159static void
160cleanup_rs (struct GNUNET_DNSSTUB_RequestSocket *rs)
161{
162 if (NULL != rs->dnsout4)
163 {
164 GNUNET_NETWORK_socket_close (rs->dnsout4);
165 rs->dnsout4 = NULL;
166 }
167 if (NULL != rs->dnsout6)
168 {
169 GNUNET_NETWORK_socket_close (rs->dnsout6);
170 rs->dnsout6 = NULL;
171 }
172 if (NULL != rs->read_task)
173 {
174 GNUNET_SCHEDULER_cancel (rs->read_task);
175 rs->read_task = NULL;
176 }
177 if (NULL != rs->retry_task)
178 {
179 GNUNET_SCHEDULER_cancel (rs->retry_task);
180 rs->retry_task = NULL;
181 }
182 if (NULL != rs->request)
183 {
184 GNUNET_free (rs->request);
185 rs->request = NULL;
186 }
187}
188
189
190/**
191 * Open source port for sending DNS requests
192 *
193 * @param af AF_INET or AF_INET6
194 * @return #GNUNET_OK on success
195 */
196static struct GNUNET_NETWORK_Handle *
197open_socket (int af)
198{
199 struct sockaddr_in a4;
200 struct sockaddr_in6 a6;
201 struct sockaddr *sa;
202 socklen_t alen;
203 struct GNUNET_NETWORK_Handle *ret;
204
205 ret = GNUNET_NETWORK_socket_create (af, SOCK_DGRAM, 0);
206 if (NULL == ret)
207 return NULL;
208 switch (af)
209 {
210 case AF_INET:
211 memset (&a4, 0, alen = sizeof (struct sockaddr_in));
212 sa = (struct sockaddr *) &a4;
213 break;
214 case AF_INET6:
215 memset (&a6, 0, alen = sizeof (struct sockaddr_in6));
216 sa = (struct sockaddr *) &a6;
217 break;
218 default:
219 GNUNET_break (0);
220 GNUNET_NETWORK_socket_close (ret);
221 return NULL;
222 }
223 sa->sa_family = af;
224 if (GNUNET_OK != GNUNET_NETWORK_socket_bind (ret,
225 sa,
226 alen))
227 {
228 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
229 _("Could not bind to any port: %s\n"),
230 STRERROR (errno));
231 GNUNET_NETWORK_socket_close (ret);
232 return NULL;
233 }
234 return ret;
235}
236
237
238/**
239 * Get a socket of the specified address family to send out a
240 * UDP DNS request to the Internet.
241 *
242 * @param ctx the DNSSTUB context
243 * @return NULL on error
244 */
245static struct GNUNET_DNSSTUB_RequestSocket *
246get_request_socket (struct GNUNET_DNSSTUB_Context *ctx)
247{
248 struct GNUNET_DNSSTUB_RequestSocket *rs;
249
250 for (unsigned int i=0;i<256;i++)
251 {
252 rs = &ctx->sockets[GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_NONCE,
253 ctx->num_sockets)];
254 if (NULL == rs->rc)
255 break;
256 }
257 if (NULL != rs->rc)
258 {
259 /* signal "failure" */
260 rs->rc (rs->rc_cls,
261 NULL,
262 0);
263 rs->rc = NULL;
264 }
265 if (NULL != rs->read_task)
266 {
267 GNUNET_SCHEDULER_cancel (rs->read_task);
268 rs->read_task = NULL;
269 }
270 if (NULL != rs->retry_task)
271 {
272 GNUNET_SCHEDULER_cancel (rs->retry_task);
273 rs->retry_task = NULL;
274 }
275 if (NULL != rs->request)
276 {
277 GNUNET_free (rs->request);
278 rs->request = NULL;
279 }
280 rs->ctx = ctx;
281 return rs;
282}
283
284
285/**
286 * Actually do the reading of a DNS packet from our UDP socket and see
287 * if we have a valid, matching, pending request.
288 *
289 * @param rs request socket with callback details
290 * @param dnsout socket to read from
291 * @return #GNUNET_OK on success, #GNUNET_NO on drop, #GNUNET_SYSERR on IO-errors (closed socket)
292 */
293static int
294do_dns_read (struct GNUNET_DNSSTUB_RequestSocket *rs,
295 struct GNUNET_NETWORK_Handle *dnsout)
296{
297 struct GNUNET_DNSSTUB_Context *ctx = rs->ctx;
298 ssize_t r;
299 int len;
300
301#ifndef MINGW
302 if (0 != ioctl (GNUNET_NETWORK_get_fd (dnsout),
303 FIONREAD,
304 &len))
305 {
306 /* conservative choice: */
307 len = UINT16_MAX;
308 }
309#else
310 /* port the code above? */
311 len = UINT16_MAX;
312#endif
313 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
314 "Receiving %d byte DNS reply\n",
315 len);
316 {
317 unsigned char buf[len] GNUNET_ALIGN;
318 int found;
319 struct sockaddr_storage addr;
320 socklen_t addrlen;
321 struct GNUNET_TUN_DnsHeader *dns;
322
323 addrlen = sizeof (addr);
324 memset (&addr,
325 0,
326 sizeof (addr));
327 r = GNUNET_NETWORK_socket_recvfrom (dnsout,
328 buf,
329 sizeof (buf),
330 (struct sockaddr*) &addr,
331 &addrlen);
332 if (-1 == r)
333 {
334 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
335 "recvfrom");
336 GNUNET_NETWORK_socket_close (dnsout);
337 return GNUNET_SYSERR;
338 }
339 found = GNUNET_NO;
340 for (struct DnsServer *ds = ctx->dns_head; NULL != ds; ds = ds->next)
341 {
342 if (0 == memcmp (&addr,
343 &ds->ss,
344 GNUNET_MIN (sizeof (struct sockaddr_storage),
345 addrlen)))
346 {
347 found = GNUNET_YES;
348 break;
349 }
350 }
351 if (GNUNET_NO == found)
352 {
353 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
354 "Received DNS response from server we never asked (ignored)");
355 return GNUNET_NO;
356 }
357 if (sizeof (struct GNUNET_TUN_DnsHeader) > (size_t) r)
358 {
359 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
360 _("Received DNS response that is too small (%u bytes)"),
361 (unsigned int) r);
362 return GNUNET_NO;
363 }
364 dns = (struct GNUNET_TUN_DnsHeader *) buf;
365 if (NULL == rs->rc)
366 {
367 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
368 "Request timeout or cancelled; ignoring reply\n");
369 return GNUNET_NO;
370 }
371 rs->rc (rs->rc_cls,
372 dns,
373 r);
374 }
375 return GNUNET_OK;
376}
377
378
379/**
380 * Read a DNS response from the (unhindered) UDP-Socket
381 *
382 * @param cls socket to read from
383 */
384static void
385read_response (void *cls);
386
387
388/**
389 * Schedule #read_response() task for @a rs.
390 *
391 * @param rs request to schedule read operation for
392 */
393static void
394schedule_read (struct GNUNET_DNSSTUB_RequestSocket *rs)
395{
396 struct GNUNET_NETWORK_FDSet *rset;
397
398 if (NULL != rs->read_task)
399 GNUNET_SCHEDULER_cancel (rs->read_task);
400 rset = GNUNET_NETWORK_fdset_create ();
401 if (NULL != rs->dnsout4)
402 GNUNET_NETWORK_fdset_set (rset,
403 rs->dnsout4);
404 if (NULL != rs->dnsout6)
405 GNUNET_NETWORK_fdset_set (rset,
406 rs->dnsout6);
407 rs->read_task = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
408 GNUNET_TIME_UNIT_FOREVER_REL,
409 rset,
410 NULL,
411 &read_response,
412 rs);
413 GNUNET_NETWORK_fdset_destroy (rset);
414}
415
416
417/**
418 * Read a DNS response from the (unhindered) UDP-Socket
419 *
420 * @param cls `struct GNUNET_DNSSTUB_RequestSocket` to read from
421 */
422static void
423read_response (void *cls)
424{
425 struct GNUNET_DNSSTUB_RequestSocket *rs = cls;
426 const struct GNUNET_SCHEDULER_TaskContext *tc;
427
428 rs->read_task = NULL;
429 tc = GNUNET_SCHEDULER_get_task_context ();
430 /* read and process ready sockets */
431 if ( (NULL != rs->dnsout4) &&
432 (GNUNET_NETWORK_fdset_isset (tc->read_ready,
433 rs->dnsout4)) &&
434 (GNUNET_SYSERR ==
435 do_dns_read (rs,
436 rs->dnsout4)) )
437 rs->dnsout4 = NULL;
438 if ( (NULL != rs->dnsout6) &&
439 (GNUNET_NETWORK_fdset_isset (tc->read_ready,
440 rs->dnsout6)) &&
441 (GNUNET_SYSERR ==
442 do_dns_read (rs,
443 rs->dnsout6)) )
444 rs->dnsout6 = NULL;
445 /* re-schedule read task */
446 schedule_read (rs);
447}
448
449
450/**
451 * Task to (re)transmit the DNS query, possibly repeatedly until
452 * we succeed.
453 *
454 * @param cls our `struct GNUNET_DNSSTUB_RequestSocket *`
455 */
456static void
457transmit_query (void *cls)
458{
459 struct GNUNET_DNSSTUB_RequestSocket *rs = cls;
460 struct GNUNET_DNSSTUB_Context *ctx = rs->ctx;
461 const struct sockaddr *sa;
462 socklen_t salen;
463 struct DnsServer *ds;
464 struct GNUNET_NETWORK_Handle *dnsout;
465
466 rs->retry_task = GNUNET_SCHEDULER_add_delayed (ctx->retry_freq,
467 &transmit_query,
468 rs);
469 ds = rs->ds_pos;
470 rs->ds_pos = ds->next;
471 if (NULL == rs->ds_pos)
472 rs->ds_pos = ctx->dns_head;
473 GNUNET_assert (NULL != ds);
474 dnsout = NULL;
475 switch (ds->ss.ss_family)
476 {
477 case AF_INET:
478 if (NULL == rs->dnsout4)
479 rs->dnsout4 = open_socket (AF_INET);
480 dnsout = rs->dnsout4;
481 sa = (const struct sockaddr *) &ds->ss;
482 salen = sizeof (struct sockaddr_in);
483 break;
484 case AF_INET6:
485 if (NULL == rs->dnsout6)
486 rs->dnsout6 = open_socket (AF_INET6);
487 dnsout = rs->dnsout6;
488 sa = (const struct sockaddr *) &ds->ss;
489 salen = sizeof (struct sockaddr_in6);
490 break;
491 default:
492 return;
493 }
494 if (NULL == dnsout)
495 {
496 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
497 "Unable to use configure DNS server, skipping\n");
498 return;
499 }
500 if (GNUNET_SYSERR ==
501 GNUNET_NETWORK_socket_sendto (dnsout,
502 rs->request,
503 rs->request_len,
504 sa,
505 salen))
506 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
507 _("Failed to send DNS request to %s: %s\n"),
508 GNUNET_a2s (sa,
509 salen),
510 STRERROR (errno));
511 else
512 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
513 _("Sent DNS request to %s\n"),
514 GNUNET_a2s (sa,
515 salen));
516 schedule_read (rs);
517}
518
519
520/**
521 * Perform DNS resolution using our default IP from init.
522 *
523 * @param ctx stub resolver to use
524 * @param request DNS request to transmit
525 * @param request_len number of bytes in msg
526 * @param rc function to call with result
527 * @param rc_cls closure for 'rc'
528 * @return socket used for the request, NULL on error
529 */
530struct GNUNET_DNSSTUB_RequestSocket *
531GNUNET_DNSSTUB_resolve (struct GNUNET_DNSSTUB_Context *ctx,
532 const void *request,
533 size_t request_len,
534 GNUNET_DNSSTUB_ResultCallback rc,
535 void *rc_cls)
536{
537 struct GNUNET_DNSSTUB_RequestSocket *rs;
538
539 if (NULL == ctx->dns_head)
540 {
541 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
542 "No DNS server configured for resolution\n");
543 return NULL;
544 }
545 if (NULL == (rs = get_request_socket (ctx)))
546 {
547 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
548 "No request socket available for DNS resolution\n");
549 return NULL;
550 }
551 rs->ds_pos = ctx->dns_head;
552 rs->rc = rc;
553 rs->rc_cls = rc_cls;
554 rs->request = GNUNET_memdup (request,
555 request_len);
556 rs->request_len = request_len;
557 rs->retry_task = GNUNET_SCHEDULER_add_now (&transmit_query,
558 rs);
559 return rs;
560}
561
562
563/**
564 * Cancel DNS resolution.
565 *
566 * @param rs resolution to cancel
567 */
568void
569GNUNET_DNSSTUB_resolve_cancel (struct GNUNET_DNSSTUB_RequestSocket *rs)
570{
571 rs->rc = NULL;
572 if (NULL != rs->retry_task)
573 {
574 GNUNET_SCHEDULER_cancel (rs->retry_task);
575 rs->retry_task = NULL;
576 }
577 if (NULL != rs->read_task)
578 {
579 GNUNET_SCHEDULER_cancel (rs->read_task);
580 rs->read_task = NULL;
581 }
582}
583
584
585/**
586 * Start a DNS stub resolver.
587 *
588 * @param num_sockets how many sockets should we open
589 * in parallel for DNS queries for this stub?
590 * @return NULL on error
591 */
592struct GNUNET_DNSSTUB_Context *
593GNUNET_DNSSTUB_start (unsigned int num_sockets)
594{
595 struct GNUNET_DNSSTUB_Context *ctx;
596
597 if (0 == num_sockets)
598 {
599 GNUNET_break (0);
600 return NULL;
601 }
602 ctx = GNUNET_new (struct GNUNET_DNSSTUB_Context);
603 ctx->num_sockets = num_sockets;
604 ctx->sockets = GNUNET_new_array (num_sockets,
605 struct GNUNET_DNSSTUB_RequestSocket);
606 ctx->retry_freq = DNS_RETRANSMIT_DELAY;
607 return ctx;
608}
609
610
611/**
612 * Add nameserver for use by the DNSSTUB. We will use
613 * all provided nameservers for resolution (round-robin).
614 *
615 * @param ctx resolver context to modify
616 * @param dns_ip target IP address to use (as string)
617 * @return #GNUNET_OK on success
618 */
619int
620GNUNET_DNSSTUB_add_dns_ip (struct GNUNET_DNSSTUB_Context *ctx,
621 const char *dns_ip)
622{
623 struct DnsServer *ds;
624 struct in_addr i4;
625 struct in6_addr i6;
626
627 ds = GNUNET_new (struct DnsServer);
628 if (1 == inet_pton (AF_INET,
629 dns_ip,
630 &i4))
631 {
632 struct sockaddr_in *s4 = (struct sockaddr_in *) &ds->ss;
633
634 s4->sin_family = AF_INET;
635 s4->sin_port = htons (53);
636 s4->sin_addr = i4;
637#if HAVE_SOCKADDR_IN_SIN_LEN
638 s4->sin_len = (u_char) sizeof (struct sockaddr_in);
639#endif
640 }
641 else if (1 == inet_pton (AF_INET6,
642 dns_ip,
643 &i6))
644 {
645 struct sockaddr_in6 *s6 = (struct sockaddr_in6 *) &ds->ss;
646
647 s6->sin6_family = AF_INET6;
648 s6->sin6_port = htons (53);
649 s6->sin6_addr = i6;
650#if HAVE_SOCKADDR_IN_SIN_LEN
651 s6->sin6_len = (u_char) sizeof (struct sockaddr_in6);
652#endif
653 }
654 else
655 {
656 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
657 "Malformed IP address `%s' for DNS server\n",
658 dns_ip);
659 GNUNET_free (ds);
660 return GNUNET_SYSERR;
661 }
662 GNUNET_CONTAINER_DLL_insert (ctx->dns_head,
663 ctx->dns_tail,
664 ds);
665 return GNUNET_OK;
666}
667
668
669/**
670 * Add nameserver for use by the DNSSTUB. We will use
671 * all provided nameservers for resolution (round-robin).
672 *
673 * @param ctx resolver context to modify
674 * @param sa socket address of DNS resolver to use
675 * @return #GNUNET_OK on success
676 */
677int
678GNUNET_DNSSTUB_add_dns_sa (struct GNUNET_DNSSTUB_Context *ctx,
679 const struct sockaddr *sa)
680{
681 struct DnsServer *ds;
682
683 ds = GNUNET_new (struct DnsServer);
684 switch (sa->sa_family)
685 {
686 case AF_INET:
687 GNUNET_memcpy (&ds->ss,
688 sa,
689 sizeof (struct sockaddr_in));
690 break;
691 case AF_INET6:
692 GNUNET_memcpy (&ds->ss,
693 sa,
694 sizeof (struct sockaddr_in6));
695 break;
696 default:
697 GNUNET_break (0);
698 GNUNET_free (ds);
699 return GNUNET_SYSERR;
700 }
701 GNUNET_CONTAINER_DLL_insert (ctx->dns_head,
702 ctx->dns_tail,
703 ds);
704 return GNUNET_OK;
705}
706
707
708/**
709 * How long should we try requests before timing out?
710 * Only effective for requests issued after this call.
711 *
712 * @param ctx resolver context to modify
713 * @param retry_freq how long to wait between retries
714 */
715void
716GNUNET_DNSSTUB_set_retry (struct GNUNET_DNSSTUB_Context *ctx,
717 struct GNUNET_TIME_Relative retry_freq)
718{
719 ctx->retry_freq = retry_freq;
720}
721
722
723/**
724 * Cleanup DNSSTUB resolver.
725 *
726 * @param ctx stub resolver to clean up
727 */
728void
729GNUNET_DNSSTUB_stop (struct GNUNET_DNSSTUB_Context *ctx)
730{
731 struct DnsServer *ds;
732
733 while (NULL != (ds = ctx->dns_head))
734 {
735 GNUNET_CONTAINER_DLL_remove (ctx->dns_head,
736 ctx->dns_tail,
737 ds);
738 GNUNET_free (ds);
739 }
740 for (unsigned int i=0;i<ctx->num_sockets;i++)
741 cleanup_rs (&ctx->sockets[i]);
742 GNUNET_free (ctx->sockets);
743 GNUNET_free (ctx);
744}
745
746
747/* end of dnsstub.c */