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