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