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