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