aboutsummaryrefslogtreecommitdiff
path: root/src/dns/gnunet-service-dns.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/dns/gnunet-service-dns.c')
-rw-r--r--src/dns/gnunet-service-dns.c1275
1 files changed, 0 insertions, 1275 deletions
diff --git a/src/dns/gnunet-service-dns.c b/src/dns/gnunet-service-dns.c
deleted file mode 100644
index 4840c0c95..000000000
--- a/src/dns/gnunet-service-dns.c
+++ /dev/null
@@ -1,1275 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012 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/**
22 * @file dns/gnunet-service-dns.c
23 * @brief service to intercept and modify DNS queries (and replies) of this system
24 * @author Christian Grothoff
25 *
26 * For "secure" interaction with the legacy DNS system, we permit
27 * replies only to arrive within a 5s window (and they must match
28 * ports, IPs and request IDs). Furthermore, we let the OS pick a
29 * source port, opening up to 128 sockets per address family (IPv4 or
30 * IPv6). Those sockets are closed if they are not in use for 5s
31 * (which means they will be freshly randomized afterwards). For new
32 * requests, we pick a random slot in the array with 128 socket slots
33 * (and re-use an existing socket if the slot is still in use). Thus
34 * each request will be given one of 128 random source ports, and the
35 * 128 random source ports will also change "often" (less often if the
36 * system is very busy, each time if we are mostly idle). At the same
37 * time, the system will never use more than 256 UDP sockets.
38 */
39#include "platform.h"
40#include "gnunet_util_lib.h"
41#include "gnunet_applications.h"
42#include "gnunet_constants.h"
43#include "gnunet_protocols.h"
44#include "gnunet_signatures.h"
45#include "dns.h"
46#include "gnunet_dns_service.h"
47#include "gnunet_dnsparser_lib.h"
48#include "gnunet_dnsstub_lib.h"
49#include "gnunet_statistics_service.h"
50#include "gnunet_tun_lib.h"
51
52/**
53 * Port number for DNS
54 */
55#define DNS_PORT 53
56
57
58/**
59 * Generic logging shorthand
60 */
61#define LOG(kind, ...) \
62 GNUNET_log_from (kind, "dns", __VA_ARGS__);
63
64
65/**
66 * Phases each request goes through.
67 */
68enum RequestPhase
69{
70 /**
71 * Request has just been received.
72 */
73 RP_INIT,
74
75 /**
76 * Showing the request to all monitor clients. If
77 * client list is empty, will enter QUERY phase.
78 */
79 RP_REQUEST_MONITOR,
80
81 /**
82 * Showing the request to PRE-RESOLUTION clients to find an answer.
83 * If client list is empty, will trigger global DNS request.
84 */
85 RP_QUERY,
86
87 /**
88 * Global Internet query is now pending.
89 */
90 RP_INTERNET_DNS,
91
92 /**
93 * Client (or global DNS request) has resulted in a response.
94 * Forward to all POST-RESOLUTION clients. If client list is empty,
95 * will enter RESPONSE_MONITOR phase.
96 */
97 RP_MODIFY,
98
99 /**
100 * Showing the request to all monitor clients. If
101 * client list is empty, give the result to the hijacker (and be done).
102 */
103 RP_RESPONSE_MONITOR,
104
105 /**
106 * Some client has told us to drop the request.
107 */
108 RP_DROP
109};
110
111
112/**
113 * Entry we keep for each client.
114 */
115struct ClientRecord
116{
117 /**
118 * Kept in doubly-linked list.
119 */
120 struct ClientRecord *next;
121
122 /**
123 * Kept in doubly-linked list.
124 */
125 struct ClientRecord *prev;
126
127 /**
128 * Handle to the client.
129 */
130 struct GNUNET_SERVICE_Client *client;
131
132 /**
133 * Message queue to talk to @a client.
134 */
135 struct GNUNET_MQ_Handle *mq;
136
137 /**
138 * Flags for the client.
139 */
140 enum GNUNET_DNS_Flags flags;
141};
142
143
144/**
145 * Entry we keep for each active request.
146 */
147struct RequestRecord
148{
149 /**
150 * List of clients that still need to see this request (each entry
151 * is set to NULL when the client is done).
152 */
153 struct ClientRecord **client_wait_list;
154
155 /**
156 * Payload of the UDP packet (the UDP payload), can be either query
157 * or already the response.
158 */
159 char *payload;
160
161 /**
162 * Socket we are using to transmit this request (must match if we receive
163 * a response).
164 */
165 struct GNUNET_DNSSTUB_RequestSocket *rs;
166
167 /**
168 * Source address of the original request (for sending response).
169 */
170 struct sockaddr_storage src_addr;
171
172 /**
173 * Destination address of the original request (for potential use as exit).
174 */
175 struct sockaddr_storage dst_addr;
176
177 /**
178 * ID of this request, also basis for hashing. Lowest 16 bit will
179 * be our message ID when doing a global DNS request and our index
180 * into the 'requests' array.
181 */
182 uint64_t request_id;
183
184 /**
185 * Number of bytes in payload.
186 */
187 size_t payload_length;
188
189 /**
190 * Length of the @e client_wait_list.
191 */
192 unsigned int client_wait_list_length;
193
194 /**
195 * In which phase this this request?
196 */
197 enum RequestPhase phase;
198};
199
200
201/**
202 * Global return value from 'main'.
203 */
204static int global_ret;
205
206/**
207 * The configuration to use
208 */
209static const struct GNUNET_CONFIGURATION_Handle *cfg;
210
211/**
212 * Statistics.
213 */
214static struct GNUNET_STATISTICS_Handle *stats;
215
216/**
217 * Handle to DNS hijacker helper process ("gnunet-helper-dns").
218 */
219static struct GNUNET_HELPER_Handle *hijacker;
220
221/**
222 * Command-line arguments we are giving to the hijacker process.
223 */
224static char *helper_argv[8];
225
226/**
227 * Head of DLL of clients we consult.
228 */
229static struct ClientRecord *clients_head;
230
231/**
232 * Tail of DLL of clients we consult.
233 */
234static struct ClientRecord *clients_tail;
235
236/**
237 * Array of all open requests.
238 */
239static struct RequestRecord requests[UINT16_MAX + 1];
240
241/**
242 * Generator for unique request IDs.
243 */
244static uint64_t request_id_gen;
245
246/**
247 * Handle to the DNS Stub resolver.
248 */
249static struct GNUNET_DNSSTUB_Context *dnsstub;
250
251
252/**
253 * We're done processing a DNS request, free associated memory.
254 *
255 * @param rr request to clean up
256 */
257static void
258cleanup_rr (struct RequestRecord *rr)
259{
260 GNUNET_free (rr->payload);
261 rr->payload = NULL;
262 rr->payload_length = 0;
263 GNUNET_array_grow (rr->client_wait_list,
264 rr->client_wait_list_length,
265 0);
266}
267
268
269/**
270 * Task run during shutdown.
271 *
272 * @param cls unused
273 */
274static void
275cleanup_task (void *cls GNUNET_UNUSED)
276{
277 if (NULL != hijacker)
278 {
279 GNUNET_HELPER_stop (hijacker, GNUNET_NO);
280 hijacker = NULL;
281 }
282 for (unsigned int i = 0; i < 8; i++)
283 GNUNET_free (helper_argv[i]);
284 for (unsigned int i = 0; i <= UINT16_MAX; i++)
285 cleanup_rr (&requests[i]);
286 if (NULL != stats)
287 {
288 GNUNET_STATISTICS_destroy (stats,
289 GNUNET_NO);
290 stats = NULL;
291 }
292 if (NULL != dnsstub)
293 {
294 GNUNET_DNSSTUB_stop (dnsstub);
295 dnsstub = NULL;
296 }
297}
298
299
300/**
301 * We're done with some request, finish processing.
302 *
303 * @param rr request send to the network or just clean up.
304 */
305static void
306request_done (struct RequestRecord *rr)
307{
308 struct GNUNET_MessageHeader *hdr;
309 size_t reply_len;
310 uint16_t source_port;
311 uint16_t destination_port;
312
313 GNUNET_array_grow (rr->client_wait_list,
314 rr->client_wait_list_length,
315 0);
316 if (RP_RESPONSE_MONITOR != rr->phase)
317 {
318 /* no response, drop */
319 LOG (GNUNET_ERROR_TYPE_DEBUG,
320 "Got no response for request %llu, dropping\n",
321 (unsigned long long) rr->request_id);
322 cleanup_rr (rr);
323 return;
324 }
325
326 LOG (GNUNET_ERROR_TYPE_DEBUG,
327 "Transmitting response for request %llu\n",
328 (unsigned long long) rr->request_id);
329 /* send response via hijacker */
330 reply_len = sizeof(struct GNUNET_MessageHeader);
331 reply_len += sizeof(struct GNUNET_TUN_Layer2PacketHeader);
332 switch (rr->src_addr.ss_family)
333 {
334 case AF_INET:
335 reply_len += sizeof(struct GNUNET_TUN_IPv4Header);
336 break;
337
338 case AF_INET6:
339 reply_len += sizeof(struct GNUNET_TUN_IPv6Header);
340 break;
341
342 default:
343 GNUNET_break (0);
344 cleanup_rr (rr);
345 return;
346 }
347 reply_len += sizeof(struct GNUNET_TUN_UdpHeader);
348 reply_len += rr->payload_length;
349 if (reply_len >= GNUNET_MAX_MESSAGE_SIZE)
350 {
351 /* response too big, drop */
352 GNUNET_break (0); /* how can this be? */
353 cleanup_rr (rr);
354 return;
355 }
356 {
357 char buf[reply_len] GNUNET_ALIGN;
358 size_t off;
359 struct GNUNET_TUN_IPv4Header ip4;
360 struct GNUNET_TUN_IPv6Header ip6;
361
362 /* first, GNUnet message header */
363 hdr = (struct GNUNET_MessageHeader*) buf;
364 hdr->type = htons (GNUNET_MESSAGE_TYPE_DNS_HELPER);
365 hdr->size = htons ((uint16_t) reply_len);
366 off = sizeof(struct GNUNET_MessageHeader);
367
368 /* first, TUN header */
369 {
370 struct GNUNET_TUN_Layer2PacketHeader tun;
371
372 tun.flags = htons (0);
373 if (rr->src_addr.ss_family == AF_INET)
374 tun.proto = htons (ETH_P_IPV4);
375 else
376 tun.proto = htons (ETH_P_IPV6);
377 GNUNET_memcpy (&buf[off],
378 &tun,
379 sizeof(struct GNUNET_TUN_Layer2PacketHeader));
380 off += sizeof(struct GNUNET_TUN_Layer2PacketHeader);
381 }
382
383 /* now IP header */
384 switch (rr->src_addr.ss_family)
385 {
386 case AF_INET:
387 {
388 struct sockaddr_in *src = (struct sockaddr_in *) &rr->src_addr;
389 struct sockaddr_in *dst = (struct sockaddr_in *) &rr->dst_addr;
390
391 source_port = dst->sin_port;
392 destination_port = src->sin_port;
393 GNUNET_TUN_initialize_ipv4_header (&ip4,
394 IPPROTO_UDP,
395 reply_len - off - sizeof(struct
396 GNUNET_TUN_IPv4Header),
397 &dst->sin_addr,
398 &src->sin_addr);
399 GNUNET_memcpy (&buf[off],
400 &ip4,
401 sizeof(ip4));
402 off += sizeof(ip4);
403 }
404 break;
405
406 case AF_INET6:
407 {
408 struct sockaddr_in6 *src = (struct sockaddr_in6 *) &rr->src_addr;
409 struct sockaddr_in6 *dst = (struct sockaddr_in6 *) &rr->dst_addr;
410
411 source_port = dst->sin6_port;
412 destination_port = src->sin6_port;
413 GNUNET_TUN_initialize_ipv6_header (&ip6,
414 IPPROTO_UDP,
415 reply_len - off - sizeof(struct
416 GNUNET_TUN_IPv6Header),
417 &dst->sin6_addr,
418 &src->sin6_addr);
419 GNUNET_memcpy (&buf[off],
420 &ip6,
421 sizeof(ip6));
422 off += sizeof(ip6);
423 }
424 break;
425
426 default:
427 GNUNET_assert (0);
428 }
429
430 /* now UDP header */
431 {
432 struct GNUNET_TUN_UdpHeader udp;
433
434 udp.source_port = source_port;
435 udp.destination_port = destination_port;
436 udp.len = htons (reply_len - off);
437 if (AF_INET == rr->src_addr.ss_family)
438 GNUNET_TUN_calculate_udp4_checksum (&ip4,
439 &udp,
440 rr->payload,
441 rr->payload_length);
442 else
443 GNUNET_TUN_calculate_udp6_checksum (&ip6,
444 &udp,
445 rr->payload,
446 rr->payload_length);
447 GNUNET_memcpy (&buf[off],
448 &udp,
449 sizeof(udp));
450 off += sizeof(udp);
451 }
452
453 /* now DNS payload */
454 {
455 GNUNET_memcpy (&buf[off], rr->payload, rr->payload_length);
456 off += rr->payload_length;
457 }
458 /* final checks & sending */
459 GNUNET_assert (off == reply_len);
460 (void) GNUNET_HELPER_send (hijacker,
461 hdr,
462 GNUNET_YES,
463 NULL, NULL);
464 GNUNET_STATISTICS_update (stats,
465 gettext_noop (
466 "# DNS requests answered via TUN interface"),
467 1, GNUNET_NO);
468 }
469 /* clean up, we're done */
470 cleanup_rr (rr);
471}
472
473
474/**
475 * Show the payload of the given request record to the client
476 * (and wait for a response).
477 *
478 * @param rr request to send to client
479 * @param cr client to send the response to
480 */
481static void
482send_request_to_client (struct RequestRecord *rr,
483 struct ClientRecord *cr)
484{
485 struct GNUNET_MQ_Envelope *env;
486 struct GNUNET_DNS_Request *req;
487
488 if (sizeof(struct GNUNET_DNS_Request) + rr->payload_length >=
489 GNUNET_MAX_MESSAGE_SIZE)
490 {
491 GNUNET_break (0);
492 cleanup_rr (rr);
493 return;
494 }
495 LOG (GNUNET_ERROR_TYPE_DEBUG,
496 "Sending information about request %llu to local client\n",
497 (unsigned long long) rr->request_id);
498 env = GNUNET_MQ_msg_extra (req,
499 rr->payload_length,
500 GNUNET_MESSAGE_TYPE_DNS_CLIENT_REQUEST);
501 req->reserved = htonl (0);
502 req->request_id = rr->request_id;
503 GNUNET_memcpy (&req[1],
504 rr->payload,
505 rr->payload_length);
506 GNUNET_MQ_send (cr->mq,
507 env);
508}
509
510
511/**
512 * Callback called from DNSSTUB resolver when a resolution
513 * succeeded.
514 *
515 * @param cls NULL
516 * @param dns the response itself
517 * @param r number of bytes in dns
518 */
519static void
520process_dns_result (void *cls,
521 const struct GNUNET_TUN_DnsHeader *dns,
522 size_t r);
523
524
525/**
526 * A client has completed its processing for this
527 * request. Move on.
528 *
529 * @param rr request to process further
530 */
531static void
532next_phase (struct RequestRecord *rr)
533{
534 struct ClientRecord *cr;
535 int nz;
536
537 if (rr->phase == RP_DROP)
538 {
539 cleanup_rr (rr);
540 return;
541 }
542 nz = -1;
543 for (unsigned int j = 0; j < rr->client_wait_list_length; j++)
544 {
545 if (NULL != rr->client_wait_list[j])
546 {
547 nz = (int) j;
548 break;
549 }
550 }
551 if (-1 != nz)
552 {
553 send_request_to_client (rr,
554 rr->client_wait_list[nz]);
555 return;
556 }
557 /* done with current phase, advance! */
558 LOG (GNUNET_ERROR_TYPE_DEBUG,
559 "Request %llu now in phase %d\n",
560 (unsigned long long) rr->request_id,
561 rr->phase);
562 switch (rr->phase)
563 {
564 case RP_INIT:
565 rr->phase = RP_REQUEST_MONITOR;
566 for (cr = clients_head; NULL != cr; cr = cr->next)
567 {
568 if (0 != (cr->flags & GNUNET_DNS_FLAG_REQUEST_MONITOR))
569 GNUNET_array_append (rr->client_wait_list,
570 rr->client_wait_list_length,
571 cr);
572 }
573 next_phase (rr);
574 return;
575
576 case RP_REQUEST_MONITOR:
577 rr->phase = RP_QUERY;
578 for (cr = clients_head; NULL != cr; cr = cr->next)
579 {
580 if (0 != (cr->flags & GNUNET_DNS_FLAG_PRE_RESOLUTION))
581 GNUNET_array_append (rr->client_wait_list,
582 rr->client_wait_list_length,
583 cr);
584 }
585 next_phase (rr);
586 return;
587
588 case RP_QUERY:
589#if 0
590 /* TODO: optionally, use this to forward DNS requests to the
591 * original* DNS server instead of the one we have configured...
592 (but then we need to create a fresh dnsstub for each request
593 * and* manage the timeout) */
594 switch (rr->dst_addr.ss_family)
595 {
596 case AF_INET:
597 salen = sizeof(struct sockaddr_in);
598 sa = (const struct sockaddr *) &rr->dst_addr;
599 break;
600
601 case AF_INET6:
602 salen = sizeof(struct sockaddr_in6);
603 sa = (const struct sockaddr *) &rr->dst_addr;
604 break;
605
606 default:
607 GNUNET_assert (0);
608 }
609#endif
610 rr->phase = RP_INTERNET_DNS;
611 rr->rs = GNUNET_DNSSTUB_resolve (dnsstub,
612 rr->payload,
613 rr->payload_length,
614 &process_dns_result,
615 NULL);
616 if (NULL == rr->rs)
617 {
618 GNUNET_STATISTICS_update (stats,
619 gettext_noop (
620 "# DNS exit failed (failed to open socket)"),
621 1,
622 GNUNET_NO);
623 cleanup_rr (rr);
624 return;
625 }
626 return;
627
628 case RP_INTERNET_DNS:
629 rr->phase = RP_MODIFY;
630 for (cr = clients_head; NULL != cr; cr = cr->next)
631 {
632 if (0 != (cr->flags & GNUNET_DNS_FLAG_POST_RESOLUTION))
633 GNUNET_array_append (rr->client_wait_list,
634 rr->client_wait_list_length,
635 cr);
636 }
637 next_phase (rr);
638 return;
639
640 case RP_MODIFY:
641 rr->phase = RP_RESPONSE_MONITOR;
642 for (cr = clients_head; NULL != cr; cr = cr->next)
643 {
644 if (0 != (cr->flags & GNUNET_DNS_FLAG_RESPONSE_MONITOR))
645 GNUNET_array_append (rr->client_wait_list,
646 rr->client_wait_list_length,
647 cr);
648 }
649 next_phase (rr);
650 return;
651
652 case RP_RESPONSE_MONITOR:
653 request_done (rr);
654 break;
655
656 case RP_DROP:
657 cleanup_rr (rr);
658 break;
659
660 default:
661 GNUNET_break (0);
662 cleanup_rr (rr);
663 break;
664 }
665}
666
667
668/**
669 * A client connected, setup our data structures.
670 *
671 * @param cls unused
672 * @param client handle of client that connected
673 * @param mq message queue to talk to @a client
674 * @return our `struct ClientRecord`
675 */
676static void *
677client_connect_cb (void *cls,
678 struct GNUNET_SERVICE_Client *client,
679 struct GNUNET_MQ_Handle *mq)
680{
681 struct ClientRecord *cr = cls;
682
683 cr = GNUNET_new (struct ClientRecord);
684 cr->client = client;
685 cr->mq = mq;
686 GNUNET_CONTAINER_DLL_insert (clients_head,
687 clients_tail,
688 cr);
689 return cr;
690}
691
692
693/**
694 * A client disconnected, clean up after it.
695 *
696 * @param cls unused
697 * @param client handle of client that disconnected
698 * @param app_ctx our `struct ClientRecord`
699 */
700static void
701client_disconnect_cb (void *cls,
702 struct GNUNET_SERVICE_Client *client,
703 void *app_ctx)
704{
705 struct ClientRecord *cr = app_ctx;
706 struct RequestRecord *rr;
707
708 GNUNET_CONTAINER_DLL_remove (clients_head,
709 clients_tail,
710 cr);
711 for (unsigned int i = 0; i < UINT16_MAX; i++)
712 {
713 rr = &requests[i];
714 if (0 == rr->client_wait_list_length)
715 continue; /* not in use */
716 for (unsigned int j = 0; j < rr->client_wait_list_length; j++)
717 {
718 if (rr->client_wait_list[j] == cr)
719 {
720 rr->client_wait_list[j] = NULL;
721 next_phase (rr);
722 }
723 }
724 }
725 GNUNET_free (cr);
726}
727
728
729/**
730 * Callback called from DNSSTUB resolver when a resolution
731 * succeeded.
732 *
733 * @param cls NULL
734 * @param dns the response itself
735 * @param r number of bytes in dns
736 */
737static void
738process_dns_result (void *cls,
739 const struct GNUNET_TUN_DnsHeader *dns,
740 size_t r)
741{
742 struct RequestRecord *rr;
743
744 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
745 "Processing DNS result from stub resolver\n");
746 GNUNET_assert (NULL == cls);
747 if (NULL == dns)
748 return; /* ignore */
749
750 rr = &requests[dns->id];
751 if (rr->phase != RP_INTERNET_DNS)
752 {
753 /* unexpected / bogus reply */
754 GNUNET_STATISTICS_update (stats,
755 gettext_noop (
756 "# External DNS response discarded (no matching request)"),
757 1, GNUNET_NO);
758 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
759 "Received DNS reply that does not match any pending request. Dropping.\n");
760 return;
761 }
762 LOG (GNUNET_ERROR_TYPE_DEBUG,
763 "Got a response from the stub resolver for DNS request %llu intercepted locally!\n",
764 (unsigned long long) rr->request_id);
765 GNUNET_free (rr->payload);
766 rr->payload = GNUNET_malloc (r);
767 GNUNET_memcpy (rr->payload,
768 dns,
769 r);
770 rr->payload_length = r;
771 next_phase (rr);
772}
773
774
775/**
776 * We got a new client. Make sure all new DNS requests pass by its desk.
777 *
778 * @param cls the client
779 * @param reg the init message
780 */
781static void
782handle_client_init (void *cls,
783 const struct GNUNET_DNS_Register *reg)
784{
785 struct ClientRecord *cr = cls;
786
787 cr->flags = (enum GNUNET_DNS_Flags) ntohl (reg->flags);
788 GNUNET_SERVICE_client_continue (cr->client);
789}
790
791
792/**
793 * Check a response from a client.
794 *
795 * @param cls the client
796 * @param resp the response
797 * @return #GNUNET_OK (always fine)
798 */
799static int
800check_client_response (void *cls,
801 const struct GNUNET_DNS_Response *resp)
802{
803 return GNUNET_OK; /* any payload is acceptable */
804}
805
806
807/**
808 * Handle a response from a client.
809 *
810 * @param cls the client
811 * @param resp the response
812 */
813static void
814handle_client_response (void *cls,
815 const struct GNUNET_DNS_Response *resp)
816{
817 struct ClientRecord *cr = cls;
818 struct RequestRecord *rr;
819 uint16_t msize;
820 uint16_t off;
821
822 msize = ntohs (resp->header.size);
823 off = (uint16_t) resp->request_id;
824 rr = &requests[off];
825 LOG (GNUNET_ERROR_TYPE_DEBUG,
826 "Received DNS response with ID %llu from local client!\n",
827 (unsigned long long) resp->request_id);
828 if (rr->request_id != resp->request_id)
829 {
830 GNUNET_STATISTICS_update (stats,
831 gettext_noop (
832 "# Client response discarded (no matching request)"),
833 1,
834 GNUNET_NO);
835 GNUNET_SERVICE_client_continue (cr->client);
836 return;
837 }
838 for (unsigned int i = 0; i < rr->client_wait_list_length; i++)
839 {
840 if (NULL == rr->client_wait_list[i])
841 continue;
842 if (rr->client_wait_list[i] != cr)
843 continue;
844 rr->client_wait_list[i] = NULL;
845 switch (ntohl (resp->drop_flag))
846 {
847 case 0: /* drop */
848 rr->phase = RP_DROP;
849 break;
850
851 case 1: /* no change */
852 break;
853
854 case 2: /* update */
855 msize -= sizeof(struct GNUNET_DNS_Response);
856 if ((sizeof(struct GNUNET_TUN_DnsHeader) > msize) ||
857 (RP_REQUEST_MONITOR == rr->phase) ||
858 (RP_RESPONSE_MONITOR == rr->phase))
859 {
860 GNUNET_break (0);
861 GNUNET_SERVICE_client_drop (cr->client);
862 next_phase (rr);
863 return;
864 }
865 GNUNET_free (rr->payload);
866 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
867 "Changing DNS reply according to client specifications\n");
868 rr->payload = GNUNET_malloc (msize);
869 rr->payload_length = msize;
870 GNUNET_memcpy (rr->payload, &resp[1], msize);
871 if (rr->phase == RP_QUERY)
872 {
873 /* clear wait list, we're moving to MODIFY phase next */
874 GNUNET_array_grow (rr->client_wait_list,
875 rr->client_wait_list_length,
876 0);
877 }
878 /* if query changed to answer, move past DNS resolution phase... */
879 if ((RP_QUERY == rr->phase) &&
880 (rr->payload_length > sizeof(struct GNUNET_TUN_DnsHeader)) &&
881 ( ((struct GNUNET_TUN_DnsFlags*) &(((struct
882 GNUNET_TUN_DnsHeader*) rr->
883 payload)->flags))->
884 query_or_response == 1) )
885 {
886 rr->phase = RP_INTERNET_DNS;
887 GNUNET_array_grow (rr->client_wait_list,
888 rr->client_wait_list_length,
889 0);
890 }
891 break;
892 }
893 next_phase (rr);
894 GNUNET_SERVICE_client_continue (cr->client);
895 return;
896 }
897 /* odd, client was not on our list for the request, that ought
898 to be an error */
899 GNUNET_break (0);
900 GNUNET_SERVICE_client_drop (cr->client);
901}
902
903
904/**
905 * Functions with this signature are called whenever a complete
906 * message is received by the tokenizer from the DNS hijack process.
907 *
908 * @param cls closure
909 * @param message the actual message, a DNS request we should handle
910 */
911static int
912process_helper_messages (void *cls,
913 const struct GNUNET_MessageHeader *message)
914{
915 uint16_t msize;
916 const struct GNUNET_TUN_Layer2PacketHeader *tun;
917 const struct GNUNET_TUN_IPv4Header *ip4;
918 const struct GNUNET_TUN_IPv6Header *ip6;
919 const struct GNUNET_TUN_UdpHeader *udp;
920 const struct GNUNET_TUN_DnsHeader *dns;
921 struct RequestRecord *rr;
922 struct sockaddr_in *srca4;
923 struct sockaddr_in6 *srca6;
924 struct sockaddr_in *dsta4;
925 struct sockaddr_in6 *dsta6;
926
927 LOG (GNUNET_ERROR_TYPE_DEBUG,
928 "Intercepted message via DNS hijacker\n");
929 msize = ntohs (message->size);
930 if (msize < sizeof(struct GNUNET_MessageHeader) + sizeof(struct
931 GNUNET_TUN_Layer2PacketHeader)
932 + sizeof(struct GNUNET_TUN_IPv4Header))
933 {
934 /* non-IP packet received on TUN!? */
935 GNUNET_break (0);
936 return GNUNET_OK;
937 }
938 msize -= sizeof(struct GNUNET_MessageHeader);
939 tun = (const struct GNUNET_TUN_Layer2PacketHeader *) &message[1];
940 msize -= sizeof(struct GNUNET_TUN_Layer2PacketHeader);
941 switch (ntohs (tun->proto))
942 {
943 case ETH_P_IPV4:
944 ip4 = (const struct GNUNET_TUN_IPv4Header *) &tun[1];
945 ip6 = NULL; /* make compiler happy */
946 if ((msize < sizeof(struct GNUNET_TUN_IPv4Header)) ||
947 (ip4->version != 4) ||
948 (ip4->header_length != sizeof(struct GNUNET_TUN_IPv4Header) / 4) ||
949 (ntohs (ip4->total_length) != msize) ||
950 (ip4->protocol != IPPROTO_UDP))
951 {
952 /* non-IP/UDP packet received on TUN (or with options) */
953 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
954 _ ("Received malformed IPv4-UDP packet on TUN interface.\n"));
955 return GNUNET_OK;
956 }
957 udp = (const struct GNUNET_TUN_UdpHeader*) &ip4[1];
958 msize -= sizeof(struct GNUNET_TUN_IPv4Header);
959 break;
960
961 case ETH_P_IPV6:
962 ip4 = NULL; /* make compiler happy */
963 ip6 = (const struct GNUNET_TUN_IPv6Header *) &tun[1];
964 if ((msize < sizeof(struct GNUNET_TUN_IPv6Header)) ||
965 (ip6->version != 6) ||
966 (ntohs (ip6->payload_length) != msize - sizeof(struct
967 GNUNET_TUN_IPv6Header))
968 ||
969 (ip6->next_header != IPPROTO_UDP))
970 {
971 /* non-IP/UDP packet received on TUN (or with extensions) */
972 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
973 _ ("Received malformed IPv6-UDP packet on TUN interface.\n"));
974 return GNUNET_OK;
975 }
976 udp = (const struct GNUNET_TUN_UdpHeader *) &ip6[1];
977 msize -= sizeof(struct GNUNET_TUN_IPv6Header);
978 break;
979
980 default:
981 /* non-IP packet received on TUN!? */
982 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
983 _ (
984 "Got non-IP packet with %u bytes and protocol %u from TUN\n"),
985 (unsigned int) msize,
986 ntohs (tun->proto));
987 return GNUNET_OK;
988 }
989 if ((msize <= sizeof(struct GNUNET_TUN_UdpHeader) + sizeof(struct
990 GNUNET_TUN_DnsHeader))
991 ||
992 (DNS_PORT != ntohs (udp->destination_port)))
993 {
994 /* non-DNS packet received on TUN, ignore */
995 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
996 _ ("DNS interceptor got non-DNS packet (dropped)\n"));
997 GNUNET_STATISTICS_update (stats,
998 gettext_noop (
999 "# Non-DNS UDP packet received via TUN interface"),
1000 1, GNUNET_NO);
1001 return GNUNET_OK;
1002 }
1003 msize -= sizeof(struct GNUNET_TUN_UdpHeader);
1004 dns = (const struct GNUNET_TUN_DnsHeader*) &udp[1];
1005 rr = &requests[dns->id];
1006
1007 /* clean up from previous request */
1008 GNUNET_free (rr->payload);
1009 rr->payload = NULL;
1010 GNUNET_array_grow (rr->client_wait_list,
1011 rr->client_wait_list_length,
1012 0);
1013
1014 /* setup new request */
1015 rr->phase = RP_INIT;
1016 switch (ntohs (tun->proto))
1017 {
1018 case ETH_P_IPV4:
1019 {
1020 srca4 = (struct sockaddr_in*) &rr->src_addr;
1021 dsta4 = (struct sockaddr_in*) &rr->dst_addr;
1022 memset (srca4, 0, sizeof(struct sockaddr_in));
1023 memset (dsta4, 0, sizeof(struct sockaddr_in));
1024 srca4->sin_family = AF_INET;
1025 dsta4->sin_family = AF_INET;
1026 srca4->sin_addr = ip4->source_address;
1027 dsta4->sin_addr = ip4->destination_address;
1028 srca4->sin_port = udp->source_port;
1029 dsta4->sin_port = udp->destination_port;
1030#if HAVE_SOCKADDR_IN_SIN_LEN
1031 srca4->sin_len = sizeof(struct sockaddr_in);
1032 dsta4->sin_len = sizeof(struct sockaddr_in);
1033#endif
1034 }
1035 break;
1036
1037 case ETH_P_IPV6:
1038 {
1039 srca6 = (struct sockaddr_in6*) &rr->src_addr;
1040 dsta6 = (struct sockaddr_in6*) &rr->dst_addr;
1041 memset (srca6, 0, sizeof(struct sockaddr_in6));
1042 memset (dsta6, 0, sizeof(struct sockaddr_in6));
1043 srca6->sin6_family = AF_INET6;
1044 dsta6->sin6_family = AF_INET6;
1045 srca6->sin6_addr = ip6->source_address;
1046 dsta6->sin6_addr = ip6->destination_address;
1047 srca6->sin6_port = udp->source_port;
1048 dsta6->sin6_port = udp->destination_port;
1049#if HAVE_SOCKADDR_IN_SIN_LEN
1050 srca6->sin6_len = sizeof(struct sockaddr_in6);
1051 dsta6->sin6_len = sizeof(struct sockaddr_in6);
1052#endif
1053 }
1054 break;
1055
1056 default:
1057 GNUNET_assert (0);
1058 }
1059 rr->payload = GNUNET_malloc (msize);
1060 rr->payload_length = msize;
1061 GNUNET_memcpy (rr->payload, dns, msize);
1062 rr->request_id = dns->id | (request_id_gen << 16);
1063 request_id_gen++;
1064 LOG (GNUNET_ERROR_TYPE_DEBUG,
1065 "Creating new DNS request %llu\n",
1066 (unsigned long long) rr->request_id);
1067 GNUNET_STATISTICS_update (stats,
1068 gettext_noop (
1069 "# DNS requests received via TUN interface"),
1070 1, GNUNET_NO);
1071 /* start request processing state machine */
1072 next_phase (rr);
1073 return GNUNET_OK;
1074}
1075
1076
1077/**
1078 * @param cls closure
1079 * @param cfg_ configuration to use
1080 * @param service the initialized service
1081 */
1082static void
1083run (void *cls,
1084 const struct GNUNET_CONFIGURATION_Handle *cfg_,
1085 struct GNUNET_SERVICE_Handle *service)
1086{
1087 char *ifc_name;
1088 char *ipv4addr;
1089 char *ipv4mask;
1090 char *ipv6addr;
1091 char *ipv6prefix;
1092 char *dns_exit;
1093 char *binary;
1094 int nortsetup;
1095
1096 cfg = cfg_;
1097 stats = GNUNET_STATISTICS_create ("dns", cfg);
1098 GNUNET_SCHEDULER_add_shutdown (&cleanup_task,
1099 cls);
1100 dnsstub = GNUNET_DNSSTUB_start (128);
1101 /* TODO: support multiple DNS_EXIT servers being configured */
1102 /* TODO: see above TODO on using DNS server from original packet.
1103 Not sure which is best... */
1104 dns_exit = NULL;
1105 if ((GNUNET_OK !=
1106 GNUNET_CONFIGURATION_get_value_string (cfg,
1107 "dns",
1108 "DNS_EXIT",
1109 &dns_exit)) ||
1110 (GNUNET_OK !=
1111 GNUNET_DNSSTUB_add_dns_ip (dnsstub,
1112 dns_exit)))
1113 {
1114 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
1115 "dns",
1116 "DNS_EXIT",
1117 _ ("need a valid IPv4 or IPv6 address\n"));
1118 GNUNET_free (dns_exit);
1119 }
1120 binary = GNUNET_OS_get_suid_binary_path (cfg, "gnunet-helper-dns");
1121
1122 if (GNUNET_YES !=
1123 GNUNET_OS_check_helper_binary (binary,
1124 GNUNET_YES,
1125 NULL)) // TODO: once we have a windows-testcase, add test parameters here
1126 {
1127 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1128 _ ("`%s' is not SUID or the path is invalid, "
1129 "will not run DNS interceptor\n"),
1130 binary);
1131 global_ret = 1;
1132 GNUNET_free (binary);
1133 return;
1134 }
1135 GNUNET_free (binary);
1136
1137 helper_argv[0] = GNUNET_strdup ("gnunet-dns");
1138 if (GNUNET_SYSERR ==
1139 GNUNET_CONFIGURATION_get_value_string (cfg,
1140 "dns",
1141 "IFNAME",
1142 &ifc_name))
1143 {
1144 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1145 "No entry 'IFNAME' in configuration!\n");
1146 GNUNET_free (binary);
1147 GNUNET_SCHEDULER_shutdown ();
1148 return;
1149 }
1150 helper_argv[1] = ifc_name;
1151 if ((GNUNET_SYSERR ==
1152 GNUNET_CONFIGURATION_get_value_string (cfg,
1153 "dns",
1154 "IPV6ADDR",
1155 &ipv6addr)))
1156 {
1157 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1158 "No entry 'IPV6ADDR' in configuration!\n");
1159 GNUNET_free (binary);
1160 GNUNET_SCHEDULER_shutdown ();
1161 return;
1162 }
1163 helper_argv[2] = ipv6addr;
1164 if (GNUNET_SYSERR ==
1165 GNUNET_CONFIGURATION_get_value_string (cfg,
1166 "dns",
1167 "IPV6PREFIX",
1168 &ipv6prefix))
1169 {
1170 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1171 "No entry 'IPV6PREFIX' in configuration!\n");
1172 GNUNET_free (binary);
1173 GNUNET_SCHEDULER_shutdown ();
1174 return;
1175 }
1176 helper_argv[3] = ipv6prefix;
1177
1178 if (GNUNET_SYSERR ==
1179 GNUNET_CONFIGURATION_get_value_string (cfg,
1180 "dns",
1181 "IPV4ADDR",
1182 &ipv4addr))
1183 {
1184 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1185 "No entry 'IPV4ADDR' in configuration!\n");
1186 GNUNET_free (binary);
1187 GNUNET_SCHEDULER_shutdown ();
1188 return;
1189 }
1190 helper_argv[4] = ipv4addr;
1191 if (GNUNET_SYSERR ==
1192 GNUNET_CONFIGURATION_get_value_string (cfg, "dns", "IPV4MASK",
1193 &ipv4mask))
1194 {
1195 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1196 "No entry 'IPV4MASK' in configuration!\n");
1197 GNUNET_free (binary);
1198 GNUNET_SCHEDULER_shutdown ();
1199 return;
1200 }
1201 helper_argv[5] = ipv4mask;
1202
1203 nortsetup = GNUNET_CONFIGURATION_get_value_yesno (cfg, "dns",
1204 "SKIP_ROUTING_SETUP");
1205 if (GNUNET_YES == nortsetup)
1206 helper_argv[6] = GNUNET_strdup ("1");
1207 else
1208 helper_argv[6] = GNUNET_strdup ("0");
1209
1210 helper_argv[7] = NULL;
1211 hijacker = GNUNET_HELPER_start (GNUNET_NO,
1212 binary,
1213 helper_argv,
1214 &process_helper_messages,
1215 NULL, NULL);
1216 GNUNET_free (binary);
1217}
1218
1219
1220/**
1221 * Define "main" method using service macro.
1222 */
1223GNUNET_SERVICE_MAIN
1224 ("dns",
1225 GNUNET_SERVICE_OPTION_NONE,
1226 &run,
1227 &client_connect_cb,
1228 &client_disconnect_cb,
1229 NULL,
1230 GNUNET_MQ_hd_fixed_size (client_init,
1231 GNUNET_MESSAGE_TYPE_DNS_CLIENT_INIT,
1232 struct GNUNET_DNS_Register,
1233 NULL),
1234 GNUNET_MQ_hd_var_size (client_response,
1235 GNUNET_MESSAGE_TYPE_DNS_CLIENT_RESPONSE,
1236 struct GNUNET_DNS_Response,
1237 NULL),
1238 GNUNET_MQ_handler_end ());
1239
1240
1241/* FIXME: this might need a port on systems without 'getresgid' */
1242#if HAVE_GETRESGID
1243/**
1244 * Enable use of SGID capabilities on POSIX
1245 */
1246void __attribute__ ((constructor))
1247GNUNET_DNS_init ()
1248{
1249 gid_t rgid;
1250 gid_t egid;
1251 gid_t sgid;
1252
1253 if (-1 == getresgid (&rgid,
1254 &egid,
1255 &sgid))
1256 {
1257 fprintf (stderr,
1258 "getresgid failed: %s\n",
1259 strerror (errno));
1260 }
1261 else if (sgid != rgid)
1262 {
1263 if (-1 == setregid (sgid,
1264 sgid))
1265 fprintf (stderr,
1266 "setregid failed: %s\n",
1267 strerror (errno));
1268 }
1269}
1270
1271
1272#endif
1273
1274
1275/* end of gnunet-service-dns.c */