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