2 This file is part of GNUnet.
3 (C) 2012 Christian Grothoff (and other contributing authors)
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
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 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
22 * @file dns/gnunet-service-dns_new.c
23 * @author Christian Grothoff
26 // - for full compatibility to DNS and to avoid going insane here parsing/generating DNS packets,
27 // how about literally attaching the "original" DNS packet (request/response) to the IPC traffic?
28 // that way, clients can literally do arbitrary modifications and we are done with that issue here.
29 // All we'd do in here is add the IP/UDP headers and be DONE with it.
30 // => minor modifications to API and IPC protocol
31 // => minor modifications to our data structures
32 // => major gains in terms of simplicity here and what can (at least theoretically) be done with the service
33 // => can test much more quickly
34 // => but: need to really write a good libgnunetdnsparse to avoid making MANY clients really complicated
35 // (not the worst of worlds either, other than deferring this mess some...)
36 // -> also positive: can be tested independently of the rest of the mess
40 #include "gnunet_util_lib.h"
41 #include "gnunet_constants.h"
42 #include "gnunet_protocols.h"
43 //#include "gnunet_dnsparser_lib.h"
44 #include "gnunet_signatures.h"
47 GNUNET_NETWORK_STRUCT_BEGIN
50 unsigned hdr_lngth:4 GNUNET_PACKED;
51 unsigned version:4 GNUNET_PACKED;
54 uint16_t tot_lngth GNUNET_PACKED;
56 uint16_t ident GNUNET_PACKED;
57 unsigned flags:3 GNUNET_PACKED;
58 unsigned frag_off:13 GNUNET_PACKED;
62 uint16_t chks GNUNET_PACKED;
64 struct in_addr sadr GNUNET_PACKED;
65 struct in_addr dadr GNUNET_PACKED;
70 unsigned tclass_h:4 GNUNET_PACKED;
71 unsigned version:4 GNUNET_PACKED;
72 unsigned tclass_l:4 GNUNET_PACKED;
73 unsigned flowlbl:20 GNUNET_PACKED;
74 uint16_t paylgth GNUNET_PACKED;
77 struct in6_addr sadr GNUNET_PACKED;
78 struct in6_addr dadr GNUNET_PACKED;
83 uint16_t spt GNUNET_PACKED;
84 uint16_t dpt GNUNET_PACKED;
85 uint16_t len GNUNET_PACKED;
86 uint16_t crc GNUNET_PACKED;
91 uint16_t id GNUNET_PACKED;
93 unsigned rd:1 GNUNET_PACKED; // recursion desired (client -> server)
94 unsigned tc:1 GNUNET_PACKED; // message is truncated
95 unsigned aa:1 GNUNET_PACKED; // authoritative answer
96 unsigned op:4 GNUNET_PACKED; // query:0, inverse q.:1, status: 2
97 unsigned qr:1 GNUNET_PACKED; // query:0, response:1
99 unsigned rcode:4 GNUNET_PACKED; // 0 No error
105 unsigned z:3 GNUNET_PACKED; // reserved
106 unsigned ra:1 GNUNET_PACKED; // recursion available (server -> client)
108 uint16_t qdcount GNUNET_PACKED; // number of questions
109 uint16_t ancount GNUNET_PACKED; // number of answers
110 uint16_t nscount GNUNET_PACKED; // number of authority-records
111 uint16_t arcount GNUNET_PACKED; // number of additional records
114 struct dns_query_line
120 struct dns_record_line
127 GNUNET_NETWORK_STRUCT_END
131 * Entry we keep for each client.
136 * Kept in doubly-linked list.
138 struct ClientRecord *next;
141 * Kept in doubly-linked list.
143 struct ClientRecord *prev;
146 * Handle to the client.
148 struct GNUNET_SERVER_Client *client;
154 * Entry we keep for each active request.
160 * Name for the request.
165 * Response data, or NULL if not known.
170 * List of clients that still need to see this request (each entry
171 * is set to NULL when the client is done).
173 struct ClientRecord **client_wait_list;
176 * Length of the client wait list.
178 unsigned int client_wait_list_length;
181 * Source address of the original request (for sending response).
183 struct sockaddr_storage src_addr;
186 * Destination address of the original request (for potential use as exit).
188 struct sockaddr_storage dst_addr;
191 * ID of this request, also basis for hashing. Lowest 16 bit will
192 * be our message ID when doing a global DNS request and our index
193 * into the 'requests' array.
198 * TTL if we know it, or 0.
203 * Number of bytes in rdata.
205 uint16_t rdata_length;
208 * Length of the 'name' string, including 0-terminator.
210 uint16_t name_length;
213 * The DNS type (i.e. 1 == 'A').
218 * The DNS class (i.e. 1 == Internet)
223 * Original DNS Id we got from the client.
225 uint16_t original_dns_id;
231 * The IPv4 UDP-Socket through which DNS-Resolves will be sent if they are not to be
232 * sent through gnunet. The port of this socket will not be hijacked.
234 static struct GNUNET_NETWORK_Handle *dnsout4;
237 * The IPv6 UDP-Socket through which DNS-Resolves will be sent if they are not to be
238 * sent through gnunet. The port of this socket will not be hijacked.
240 static struct GNUNET_NETWORK_Handle *dnsout6;
243 * Task for reading from dnsout4.
245 static GNUNET_SCHEDULER_TaskIdentifier read4_task;
248 * Task for reading from dnsout6.
250 static GNUNET_SCHEDULER_TaskIdentifier read6_task;
253 * The port bound to the socket dnsout (and/or dnsout6). We always (try) to bind
254 * both sockets to the same port.
256 static uint16_t dnsoutport;
259 * The configuration to use
261 static const struct GNUNET_CONFIGURATION_Handle *cfg;
264 * Handle to DNS hijacker helper process ("gnunet-helper-dns").
266 static struct GNUNET_HELPER_Handle *hijacker;
269 * Command-line arguments we are giving to the hijacker process.
271 static char *helper_argv[8];
274 * Head of DLL of clients we consult.
276 static struct ClientRecord *clients_head;
279 * Tail of DLL of clients we consult.
281 static struct ClientRecord *clients_tail;
284 * Array of all open requests.
286 static struct RequestRecord requests[UINT16_MAX];
289 * Generator for unique request IDs.
291 static uint64_t request_id_gen;
295 * Task run during shutdown.
301 cleanup_task (void *cls GNUNET_UNUSED,
302 const struct GNUNET_SCHEDULER_TaskContext *tc)
305 struct RequestRecord *rr;
307 GNUNET_HELPER_stop (hijacker);
310 GNUNET_free_non_null (helper_argv[i]);
313 GNUNET_NETWORK_socket_close (dnsout4);
316 if (GNUNET_SCHEDULER_NO_TASK != read4_task)
318 GNUNET_SCHEDULER_cancel (read4_task);
319 read4_task = GNUNET_SCHEDULER_NO_TASK;
323 GNUNET_NETWORK_socket_close (dnsout6);
326 if (GNUNET_SCHEDULER_NO_TASK != read6_task)
328 GNUNET_SCHEDULER_cancel (read6_task);
329 read6_task = GNUNET_SCHEDULER_NO_TASK;
331 for (i=0;i<65536;i++)
334 GNUNET_free (rr->name);
335 GNUNET_free_non_null (rr->rdata);
336 GNUNET_array_grow (rr->client_wait_list,
337 rr->client_wait_list_length,
344 * We're done with some request, finish processing.
347 request_done (struct RequestRecord *rr)
349 struct GNUNET_MessageHeader *hdr;
354 GNUNET_array_grow (rr->client_wait_list,
355 rr->client_wait_list_length,
357 if (NULL == rr->rdata)
359 /* no response, drop */
360 GNUNET_free (rr->name);
365 /* send response via hijacker */
366 reply_len = sizeof (struct GNUNET_MessageHeader);
367 switch (rr->src_addr.ss_family)
370 reply_len += sizeof (struct ip4_hdr);
373 reply_len += sizeof (struct ip6_hdr);
377 GNUNET_free (rr->name);
381 reply_len += sizeof (struct udp_pkt);
382 reply_len += sizeof (struct dns_pkt);
383 reply_len += rr->name_length;
384 reply_len += sizeof (struct dns_record_line);
385 reply_len += rr->rdata_length;
386 if (reply_len >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
388 /* response too big, drop */
389 GNUNET_break (0); /* how can this be? */
390 GNUNET_free (rr->name);
398 /* first, GNUnet message header */
399 hdr = (struct GNUNET_MessageHeader*) buf;
400 hdr->type = htons (GNUNET_MESSAGE_TYPE_DNS_HELPER);
401 hdr->size = htons ((uint16_t) reply_len);
402 off = sizeof (struct GNUNET_MessageHeader);
405 switch (rr->src_addr.ss_family)
409 struct sockaddr_in *src = (struct sockaddr_in *) &rr->src_addr;
410 struct sockaddr_in *dst = (struct sockaddr_in *) &rr->dst_addr;
415 // FIXME: fill in IP header!
416 memcpy (&buf[off], &ip, sizeof (ip));
422 struct sockaddr_in6 *src = (struct sockaddr_in6 *) &rr->src_addr;
423 struct sockaddr_in6 *dst = (struct sockaddr_in6 *) &rr->dst_addr;
426 spt = dst->sin6_port;
427 dpt = src->sin6_port;
428 // FIXME: fill in IP header!
429 memcpy (&buf[off], &ip, sizeof (ip));
442 udp.len = htons (reply_len - off);
443 udp.crc = 0; /* checksum is optional */
444 memcpy (&buf[off], &udp, sizeof (udp));
452 dns.id = rr->original_dns_id;
453 dns.rd = 1; /* recursion desired / supported */
454 dns.tc = 0; /* not truncated */
455 dns.aa = 1; /* are we authoritative!? I say yes. */
456 dns.op = 0; /* standard query */
457 dns.qr = 1; /* this is a response */
458 dns.rcode = 0; /* no error */
459 dns.z = 0; /* reserved */
460 dns.ra = 1; /* recursion available */
461 dns.qdcount = htons (0); /* no queries */
462 dns.ancount = htons (1); /* one answer */
463 dns.nscount = htons (0); /* no authorities yet (fixme) */
464 dns.arcount = htons (0); /* no additinal records yet (fixme) */
465 memcpy (&buf[off], &dns, sizeof (dns));
471 // FIXME: fill in DNS name!
472 off += rr->name_length;
476 /* now DNS record line */
478 struct dns_record_line drl;
480 drl.type = htons (rr->dns_type);
481 drl.class = htons (rr->dns_class);
482 drl.ttl = htonl (rr->dns_ttl);
483 drl.data_len = htons (rr->rdata_length);
484 memcpy (&buf[off], &drl, sizeof (drl));
490 memcpy (&buf[off], rr->rdata, rr->rdata_length);
491 off += rr->rdata_length;
494 /* final checks & sending */
495 GNUNET_assert (off == reply_len);
496 GNUNET_HELPER_send (hijacker,
501 /* clean up, we're done */
502 GNUNET_free (rr->name);
508 * A client disconnected, clean up after it.
511 * @param client handle of client that disconnected
514 client_disconnect (void *cls, struct GNUNET_SERVER_Client *client)
516 struct ClientRecord *cr;
517 struct RequestRecord *rr;
522 for (cr = clients_head; NULL != cr; cr = cr->next)
524 if (cr->client == client)
526 GNUNET_SERVER_client_drop (client);
527 GNUNET_CONTAINER_DLL_remove (clients_head,
530 for (i=0;i<UINT16_MAX;i++)
533 if (0 == rr->client_wait_list_length)
534 continue; /* not in use */
536 for (j=0;j<rr->client_wait_list_length;j++)
538 if (rr->client_wait_list[j] == cr)
539 rr->client_wait_list[j] = NULL;
540 if (rr->client_wait_list[j] != NULL)
544 request_done (rr); /* this was the last client... */
554 * Read a DNS response from the (unhindered) UDP-Socket
556 * @param cls socket to read from
557 * @param tc scheduler context (must be shutdown or read ready)
560 read_response (void *cls,
561 const struct GNUNET_SCHEDULER_TaskContext *tc)
563 struct GNUNET_NETWORK_Handle *dnsout = cls;
564 struct sockaddr_in addr4;
565 struct sockaddr_in6 addr6;
566 struct sockaddr *addr;
571 if (dnsout == dnsout4)
573 addrlen = sizeof (struct sockaddr_in);
574 addr = (struct sockaddr* ) &addr4;
575 read4_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
582 addrlen = sizeof (struct sockaddr_in6);
583 addr = (struct sockaddr* ) &addr6;
584 read6_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
589 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
593 if (0 != ioctl (GNUNET_NETWORK_get_fd (dnsout), FIONREAD, &len))
595 /* conservative choice: */
599 /* port the code above? */
604 unsigned char buf[len];
606 memset (addr, 0, addrlen);
607 r = GNUNET_NETWORK_socket_recvfrom (dnsout,
612 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "recvfrom");
615 // NOTE: struct dns_pkt *dns = (struct dns_pkt *) buf;
616 // FIXME: handle_response (buf, r, addr, addrlen);
622 * Open source port for sending DNS request on IPv4.
624 * @return GNUNET_OK on success
629 struct sockaddr_in addr;
632 dnsout4 = GNUNET_NETWORK_socket_create (AF_INET, SOCK_DGRAM, 0);
634 return GNUNET_SYSERR;
636 memset (&addr, 0, sizeof (struct sockaddr_in));
637 addr.sin_family = AF_INET;
638 int err = GNUNET_NETWORK_socket_bind (dnsout4,
639 (struct sockaddr *) &addr,
640 sizeof (struct sockaddr_in));
642 if (err != GNUNET_OK)
644 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
645 _("Could not bind to any port: %s\n"),
647 GNUNET_NETWORK_socket_close (dnsout4);
649 return GNUNET_SYSERR;
652 /* Read the port we bound to */
653 addrlen = sizeof (struct sockaddr_in);
654 if (0 != getsockname (GNUNET_NETWORK_get_fd (dnsout4),
655 (struct sockaddr *) &addr,
658 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
659 _("Could not determine port I got: %s\n"),
661 GNUNET_NETWORK_socket_close (dnsout4);
663 return GNUNET_SYSERR;
665 dnsoutport = htons (addr.sin_port);
667 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
668 _("GNUnet DNS will exit on source port %u\n"),
669 (unsigned int) dnsoutport);
670 read4_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
672 &read_response, dnsout4);
678 * Open source port for sending DNS request on IPv6. Should be
679 * called AFTER open_port4.
681 * @return GNUNET_OK on success
686 struct sockaddr_in6 addr;
689 dnsout6 = GNUNET_NETWORK_socket_create (AF_INET6, SOCK_DGRAM, 0);
692 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
693 _("Could not create IPv6 socket: %s\n"),
695 return GNUNET_SYSERR;
697 memset (&addr, 0, sizeof (struct sockaddr_in6));
698 addr.sin6_family = AF_INET6;
699 addr.sin6_port = htons (dnsoutport);
700 int err = GNUNET_NETWORK_socket_bind (dnsout6,
701 (struct sockaddr *) &addr,
702 sizeof (struct sockaddr_in6));
704 if (err != GNUNET_OK)
706 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
707 _("Could not bind to port %u: %s\n"),
708 (unsigned int) dnsoutport,
710 GNUNET_NETWORK_socket_close (dnsout6);
712 return GNUNET_SYSERR;
716 addrlen = sizeof (struct sockaddr_in6);
717 if (0 != getsockname (GNUNET_NETWORK_get_fd (dnsout6),
718 (struct sockaddr *) &addr,
721 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
722 _("Could not determine port I got: %s\n"),
724 GNUNET_NETWORK_socket_close (dnsout6);
726 return GNUNET_SYSERR;
729 dnsoutport = htons (addr.sin6_port);
730 read6_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
732 &read_response, dnsout6);
738 * We got a new client. Make sure all new DNS requests pass by its desk.
741 * @param client the new client
742 * @param message the init message (unused)
745 handle_client_init (void *cls GNUNET_UNUSED,
746 struct GNUNET_SERVER_Client *client,
747 const struct GNUNET_MessageHeader *message GNUNET_UNUSED)
749 struct ClientRecord *cr;
751 cr = GNUNET_malloc (sizeof (struct ClientRecord));
753 GNUNET_SERVER_client_keep (client);
754 GNUNET_CONTAINER_DLL_insert (clients_head,
757 GNUNET_SERVER_receive_done (client, GNUNET_OK);
762 * We got a response from a client.
765 * @param client the client
766 * @param message the response
769 handle_client_response (void *cls GNUNET_UNUSED,
770 struct GNUNET_SERVER_Client *client,
771 const struct GNUNET_MessageHeader *message GNUNET_UNUSED)
773 // FIXME: validate and parse response, process response
774 GNUNET_SERVER_receive_done (client, GNUNET_OK);
779 * Functions with this signature are called whenever a complete
780 * message is received by the tokenizer from the DNS hijack process.
783 * @param client identification of the client
784 * @param message the actual message, a DNS request we should handle
787 process_helper_messages (void *cls, void *client,
788 const struct GNUNET_MessageHeader *message)
790 /* FIXME: parse message, create record, start processing! */
791 /* FIXME: put request into queue for clients / system DNS */
798 * @param server the initialized server
799 * @param cfg_ configuration to use
802 run (void *cls, struct GNUNET_SERVER_Handle *server,
803 const struct GNUNET_CONFIGURATION_Handle *cfg_)
805 static const struct GNUNET_SERVER_MessageHandler handlers[] = {
806 /* callback, cls, type, size */
807 {&handle_client_init, NULL, GNUNET_MESSAGE_TYPE_DNS_CLIENT_INIT, sizeof (struct GNUNET_MessageHeader)},
808 {&handle_client_response, NULL, GNUNET_MESSAGE_TYPE_DNS_CLIENT_RESPONSE, 0},
819 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &cleanup_task,
822 GNUNET_CONFIGURATION_get_value_yesno (cfg_, "dns", "PROVIDE_EXIT"))
824 if ( (GNUNET_OK != open_port4 ()) &&
825 (GNUNET_OK != open_port6 ()) )
827 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
828 _("Failed to open any port to provide DNS exit\n"));
829 GNUNET_SCHEDULER_shutdown ();
834 helper_argv[0] = GNUNET_strdup ("gnunet-dns");
836 GNUNET_CONFIGURATION_get_value_string (cfg, "dns", "IFNAME", &ifc_name))
838 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
839 "No entry 'IFNAME' in configuration!\n");
840 GNUNET_SCHEDULER_shutdown ();
843 helper_argv[1] = ifc_name;
844 if ( (GNUNET_SYSERR ==
845 GNUNET_CONFIGURATION_get_value_string (cfg, "exit", "IPV6ADDR",
848 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
849 "No entry 'IPV6ADDR' in configuration!\n");
850 GNUNET_SCHEDULER_shutdown ();
853 helper_argv[2] = ipv6addr;
855 GNUNET_CONFIGURATION_get_value_string (cfg, "exit", "IPV6PREFIX",
858 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
859 "No entry 'IPV6PREFIX' in configuration!\n");
860 GNUNET_SCHEDULER_shutdown ();
863 helper_argv[3] = ipv6prefix;
866 GNUNET_CONFIGURATION_get_value_string (cfg, "exit", "IPV4ADDR",
869 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
870 "No entry 'IPV4ADDR' in configuration!\n");
871 GNUNET_SCHEDULER_shutdown ();
874 helper_argv[4] = ipv4addr;
876 GNUNET_CONFIGURATION_get_value_string (cfg, "exit", "IPV4MASK",
879 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
880 "No entry 'IPV4MASK' in configuration!\n");
881 GNUNET_SCHEDULER_shutdown ();
884 helper_argv[5] = ipv4mask;
885 GNUNET_snprintf (port_s,
888 (unsigned int) dnsoutport);
889 helper_argv[6] = GNUNET_strdup (port_s);
890 helper_argv[7] = NULL;
891 hijacker = GNUNET_HELPER_start ("gnunet-helper-dns",
893 &process_helper_messages,
895 GNUNET_SERVER_add_handlers (server, handlers);
896 GNUNET_SERVER_disconnect_notify (server, &client_disconnect, NULL);
901 * The main function for the dns service.
903 * @param argc number of arguments from the command line
904 * @param argv command line arguments
905 * @return 0 ok, 1 on error
908 main (int argc, char *const *argv)
911 GNUNET_SERVICE_run (argc, argv, "dns", GNUNET_SERVICE_OPTION_NONE,
912 &run, NULL)) ? 0 : 1;
916 /* end of gnunet-service-dns_new.c */