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