- refactor to check messages from both enc systems
[oweals/gnunet.git] / src / dns / gnunet-service-dns.c
1 /*
2      This file is part of GNUnet.
3      Copyright (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   if (NULL != hijacker)
285   {
286     GNUNET_HELPER_stop (hijacker, GNUNET_NO);
287     hijacker = NULL;
288   }
289   for (i=0;i<7;i++)
290     GNUNET_free_non_null (helper_argv[i]);
291   for (i=0;i<=UINT16_MAX;i++)
292     cleanup_rr (&requests[i]);
293   GNUNET_SERVER_notification_context_destroy (nc);
294   nc = NULL;
295   if (stats != NULL)
296   {
297     GNUNET_STATISTICS_destroy (stats, GNUNET_NO);
298     stats = NULL;
299   }
300   if (NULL != dnsstub)
301   {
302     GNUNET_DNSSTUB_stop (dnsstub);
303     dnsstub = NULL;
304   }
305 }
306
307
308 /**
309  * We're done with some request, finish processing.
310  *
311  * @param rr request send to the network or just clean up.
312  */
313 static void
314 request_done (struct RequestRecord *rr)
315 {
316   struct GNUNET_MessageHeader *hdr;
317   size_t reply_len;
318   uint16_t source_port;
319   uint16_t destination_port;
320
321   GNUNET_array_grow (rr->client_wait_list,
322                      rr->client_wait_list_length,
323                      0);
324   if (RP_RESPONSE_MONITOR != rr->phase)
325   {
326     /* no response, drop */
327     LOG (GNUNET_ERROR_TYPE_DEBUG,
328          "Got no response for request %llu, dropping\n",
329          (unsigned long long) rr->request_id);
330     cleanup_rr (rr);
331     return;
332   }
333
334   LOG (GNUNET_ERROR_TYPE_DEBUG,
335        "Transmitting response for request %llu\n",
336        (unsigned long long) rr->request_id);
337   /* send response via hijacker */
338   reply_len = sizeof (struct GNUNET_MessageHeader);
339   reply_len += sizeof (struct GNUNET_TUN_Layer2PacketHeader);
340   switch (rr->src_addr.ss_family)
341   {
342   case AF_INET:
343     reply_len += sizeof (struct GNUNET_TUN_IPv4Header);
344     break;
345   case AF_INET6:
346     reply_len += sizeof (struct GNUNET_TUN_IPv6Header);
347     break;
348   default:
349     GNUNET_break (0);
350     cleanup_rr (rr);
351     return;
352   }
353   reply_len += sizeof (struct GNUNET_TUN_UdpHeader);
354   reply_len += rr->payload_length;
355   if (reply_len >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
356   {
357     /* response too big, drop */
358     GNUNET_break (0); /* how can this be? */
359     cleanup_rr(rr);
360     return;
361   }
362   {
363     char buf[reply_len] GNUNET_ALIGN;
364     size_t off;
365     struct GNUNET_TUN_IPv4Header ip4;
366     struct GNUNET_TUN_IPv6Header ip6;
367
368     /* first, GNUnet message header */
369     hdr = (struct GNUNET_MessageHeader*) buf;
370     hdr->type = htons (GNUNET_MESSAGE_TYPE_DNS_HELPER);
371     hdr->size = htons ((uint16_t) reply_len);
372     off = sizeof (struct GNUNET_MessageHeader);
373
374     /* first, TUN header */
375     {
376       struct GNUNET_TUN_Layer2PacketHeader tun;
377
378       tun.flags = htons (0);
379       if (rr->src_addr.ss_family == AF_INET)
380         tun.proto = htons (ETH_P_IPV4);
381       else
382         tun.proto = htons (ETH_P_IPV6);
383       memcpy (&buf[off], &tun, sizeof (struct GNUNET_TUN_Layer2PacketHeader));
384       off += sizeof (struct GNUNET_TUN_Layer2PacketHeader);
385     }
386
387     /* now IP header */
388     switch (rr->src_addr.ss_family)
389     {
390     case AF_INET:
391       {
392         struct sockaddr_in *src = (struct sockaddr_in *) &rr->src_addr;
393         struct sockaddr_in *dst = (struct sockaddr_in *) &rr->dst_addr;
394
395         source_port = dst->sin_port;
396         destination_port = src->sin_port;
397         GNUNET_TUN_initialize_ipv4_header (&ip4,
398                                            IPPROTO_UDP,
399                                            reply_len - off - sizeof (struct GNUNET_TUN_IPv4Header),
400                                            &dst->sin_addr,
401                                            &src->sin_addr);
402         memcpy (&buf[off], &ip4, sizeof (ip4));
403         off += sizeof (ip4);
404       }
405       break;
406     case AF_INET6:
407       {
408         struct sockaddr_in6 *src = (struct sockaddr_in6 *) &rr->src_addr;
409         struct sockaddr_in6 *dst = (struct sockaddr_in6 *) &rr->dst_addr;
410
411         source_port = dst->sin6_port;
412         destination_port = src->sin6_port;
413         GNUNET_TUN_initialize_ipv6_header (&ip6,
414                                            IPPROTO_UDP,
415                                            reply_len - sizeof (struct GNUNET_TUN_IPv6Header),
416                                            &dst->sin6_addr,
417                                            &src->sin6_addr);
418         memcpy (&buf[off], &ip6, sizeof (ip6));
419         off += sizeof (ip6);
420       }
421       break;
422     default:
423       GNUNET_assert (0);
424     }
425
426     /* now UDP header */
427     {
428       struct GNUNET_TUN_UdpHeader udp;
429
430       udp.source_port = source_port;
431       udp.destination_port = destination_port;
432       udp.len = htons (reply_len - off);
433       if (AF_INET == rr->src_addr.ss_family)
434         GNUNET_TUN_calculate_udp4_checksum (&ip4,
435                                             &udp,
436                                             rr->payload,
437                                             rr->payload_length);
438       else
439         GNUNET_TUN_calculate_udp6_checksum (&ip6,
440                                             &udp,
441                                             rr->payload,
442                                             rr->payload_length);
443       memcpy (&buf[off], &udp, sizeof (udp));
444       off += sizeof (udp);
445     }
446
447     /* now DNS payload */
448     {
449       memcpy (&buf[off], rr->payload, rr->payload_length);
450       off += rr->payload_length;
451     }
452     /* final checks & sending */
453     GNUNET_assert (off == reply_len);
454     (void) GNUNET_HELPER_send (hijacker,
455                                hdr,
456                                GNUNET_YES,
457                                NULL, NULL);
458     GNUNET_STATISTICS_update (stats,
459                               gettext_noop ("# DNS requests answered via TUN interface"),
460                               1, GNUNET_NO);
461   }
462   /* clean up, we're done */
463   cleanup_rr (rr);
464 }
465
466
467 /**
468  * Show the payload of the given request record to the client
469  * (and wait for a response).
470  *
471  * @param rr request to send to client
472  * @param client client to send the response to
473  */
474 static void
475 send_request_to_client (struct RequestRecord *rr,
476                         struct GNUNET_SERVER_Client *client)
477 {
478   char buf[sizeof (struct GNUNET_DNS_Request) + rr->payload_length] GNUNET_ALIGN;
479   struct GNUNET_DNS_Request *req;
480
481   if (sizeof (buf) >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
482   {
483     GNUNET_break (0);
484     cleanup_rr (rr);
485     return;
486   }
487   LOG (GNUNET_ERROR_TYPE_DEBUG,
488        "Sending information about request %llu to local client\n",
489        (unsigned long long) rr->request_id);
490   req = (struct GNUNET_DNS_Request*) buf;
491   req->header.type = htons (GNUNET_MESSAGE_TYPE_DNS_CLIENT_REQUEST);
492   req->header.size = htons (sizeof (buf));
493   req->reserved = htonl (0);
494   req->request_id = rr->request_id;
495   memcpy (&req[1], rr->payload, rr->payload_length);
496   GNUNET_SERVER_notification_context_unicast (nc,
497                                               client,
498                                               &req->header,
499                                               GNUNET_NO);
500 }
501
502
503
504 /**
505  * Callback called from DNSSTUB resolver when a resolution
506  * succeeded.
507  *
508  * @param cls NULL
509  * @param rs the socket that received the response
510  * @param dns the response itself
511  * @param r number of bytes in dns
512  */
513 static void
514 process_dns_result (void *cls,
515                     struct GNUNET_DNSSTUB_RequestSocket *rs,
516                     const struct GNUNET_TUN_DnsHeader *dns,
517                     size_t r);
518
519
520 /**
521  * A client has completed its processing for this
522  * request.  Move on.
523  *
524  * @param rr request to process further
525  */
526 static void
527 next_phase (struct RequestRecord *rr)
528 {
529   struct ClientRecord *cr;
530   int nz;
531   unsigned int j;
532   socklen_t salen;
533
534   if (rr->phase == RP_DROP)
535   {
536     cleanup_rr (rr);
537     return;
538   }
539   nz = -1;
540   for (j=0;j<rr->client_wait_list_length;j++)
541   {
542     if (NULL != rr->client_wait_list[j])
543     {
544       nz = (int) j;
545       break;
546     }
547   }
548   if (-1 != nz)
549   {
550     send_request_to_client (rr, rr->client_wait_list[nz]->client);
551     return;
552   }
553   /* done with current phase, advance! */
554   LOG (GNUNET_ERROR_TYPE_DEBUG,
555        "Request %llu now in phase %d\n",
556        rr->request_id,
557        rr->phase);
558   switch (rr->phase)
559   {
560   case RP_INIT:
561     rr->phase = RP_REQUEST_MONITOR;
562     for (cr = clients_head; NULL != cr; cr = cr->next)
563     {
564       if (0 != (cr->flags & GNUNET_DNS_FLAG_REQUEST_MONITOR))
565         GNUNET_array_append (rr->client_wait_list,
566                              rr->client_wait_list_length,
567                              cr);
568     }
569     next_phase (rr);
570     return;
571   case RP_REQUEST_MONITOR:
572     rr->phase = RP_QUERY;
573     for (cr = clients_head; NULL != cr; cr = cr->next)
574     {
575       if (0 != (cr->flags & GNUNET_DNS_FLAG_PRE_RESOLUTION))
576         GNUNET_array_append (rr->client_wait_list,
577                              rr->client_wait_list_length,
578                              cr);
579     }
580     next_phase (rr);
581     return;
582   case RP_QUERY:
583     switch (rr->dst_addr.ss_family)
584     {
585     case AF_INET:
586       salen = sizeof (struct sockaddr_in);
587       break;
588     case AF_INET6:
589       salen = sizeof (struct sockaddr_in6);
590       break;
591     default:
592       GNUNET_assert (0);
593     }
594
595     rr->phase = RP_INTERNET_DNS;
596     rr->rs = GNUNET_DNSSTUB_resolve (dnsstub,
597                                      (struct sockaddr*) &rr->dst_addr,
598                                      salen,
599                                      rr->payload,
600                                      rr->payload_length,
601                                      &process_dns_result,
602                                      NULL);
603     if (NULL == rr->rs)
604     {
605       GNUNET_STATISTICS_update (stats,
606                                 gettext_noop ("# DNS exit failed (failed to open socket)"),
607                                 1, 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 disconnected, clean up after it.
650  *
651  * @param cls unused
652  * @param client handle of client that disconnected
653  */
654 static void
655 client_disconnect (void *cls, struct GNUNET_SERVER_Client *client)
656 {
657   struct ClientRecord *cr;
658   struct RequestRecord *rr;
659   unsigned int i;
660   unsigned int j;
661
662   for (cr = clients_head; NULL != cr; cr = cr->next)
663   {
664     if (cr->client == client)
665     {
666       GNUNET_SERVER_client_drop (client);
667       GNUNET_CONTAINER_DLL_remove (clients_head,
668                                    clients_tail,
669                                    cr);
670       for (i=0;i<UINT16_MAX;i++)
671       {
672         rr = &requests[i];
673         if (0 == rr->client_wait_list_length)
674           continue; /* not in use */
675         for (j=0;j<rr->client_wait_list_length;j++)
676         {
677           if (rr->client_wait_list[j] == cr)
678           {
679             rr->client_wait_list[j] = NULL;
680             next_phase (rr);
681           }
682         }
683       }
684       GNUNET_free (cr);
685       return;
686     }
687   }
688 }
689
690
691 /**
692  * Callback called from DNSSTUB resolver when a resolution
693  * succeeded.
694  *
695  * @param cls NULL
696  * @param rs the socket that received the response
697  * @param dns the response itself
698  * @param r number of bytes in dns
699  */
700 static void
701 process_dns_result (void *cls,
702                     struct GNUNET_DNSSTUB_RequestSocket *rs,
703                     const struct GNUNET_TUN_DnsHeader *dns,
704                     size_t r)
705 {
706   struct RequestRecord *rr;
707
708   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
709               "Processing DNS result from stub resolver\n");
710   GNUNET_assert (NULL == cls);
711   rr = &requests[dns->id];
712   if ( (rr->phase != RP_INTERNET_DNS) ||
713        (rr->rs != rs) )
714   {
715     /* unexpected / bogus reply */
716     GNUNET_STATISTICS_update (stats,
717                               gettext_noop ("# External DNS response discarded (no matching request)"),
718                               1, GNUNET_NO);
719     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
720                 "Received DNS reply that does not match any pending request.  Dropping.\n");
721     return;
722   }
723   LOG (GNUNET_ERROR_TYPE_DEBUG,
724        "Got a response from the stub resolver for DNS request %llu intercepted locally!\n",
725        (unsigned long long) rr->request_id);
726   GNUNET_free_non_null (rr->payload);
727   rr->payload = GNUNET_malloc (r);
728   memcpy (rr->payload, dns, r);
729   rr->payload_length = r;
730   next_phase (rr);
731 }
732
733
734 /**
735  * We got a new client.  Make sure all new DNS requests pass by its desk.
736  *
737  * @param cls unused
738  * @param client the new client
739  * @param message the init message (unused)
740  */
741 static void
742 handle_client_init (void *cls GNUNET_UNUSED,
743                     struct GNUNET_SERVER_Client *client,
744                     const struct GNUNET_MessageHeader *message)
745 {
746   struct ClientRecord *cr;
747   const struct GNUNET_DNS_Register *reg = (const struct GNUNET_DNS_Register*) message;
748
749   cr = GNUNET_new (struct ClientRecord);
750   cr->client = client;
751   cr->flags = (enum GNUNET_DNS_Flags) ntohl (reg->flags);
752   GNUNET_SERVER_client_keep (client);
753   GNUNET_CONTAINER_DLL_insert (clients_head,
754                                clients_tail,
755                                cr);
756   GNUNET_SERVER_notification_context_add (nc, client);
757   GNUNET_SERVER_receive_done (client, GNUNET_OK);
758 }
759
760
761 /**
762  * We got a response from a client.
763  *
764  * @param cls unused
765  * @param client the client
766  * @param message the response
767  */
768 static void
769 handle_client_response (void *cls GNUNET_UNUSED,
770                         struct GNUNET_SERVER_Client *client,
771                         const struct GNUNET_MessageHeader *message)
772 {
773   const struct GNUNET_DNS_Response *resp;
774   struct RequestRecord *rr;
775   unsigned int i;
776   uint16_t msize;
777   uint16_t off;
778
779   msize = ntohs (message->size);
780   if (msize < sizeof (struct GNUNET_DNS_Response))
781   {
782     GNUNET_break (0);
783     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
784     return;
785   }
786   resp = (const struct GNUNET_DNS_Response*) message;
787   off = (uint16_t) resp->request_id;
788   rr = &requests[off];
789   LOG (GNUNET_ERROR_TYPE_DEBUG,
790        "Received DNS response with ID %llu from local client!\n",
791        (unsigned long long) resp->request_id);
792   if (rr->request_id != resp->request_id)
793   {
794     GNUNET_STATISTICS_update (stats,
795                               gettext_noop ("# Client response discarded (no matching request)"),
796                               1, GNUNET_NO);
797     GNUNET_SERVER_receive_done (client, GNUNET_OK);
798     return;
799   }
800   for (i=0;i<rr->client_wait_list_length;i++)
801   {
802     if (NULL == rr->client_wait_list[i])
803       continue;
804     if (rr->client_wait_list[i]->client != client)
805       continue;
806     rr->client_wait_list[i] = NULL;
807     switch (ntohl (resp->drop_flag))
808     {
809     case 0: /* drop */
810       rr->phase = RP_DROP;
811       break;
812     case 1: /* no change */
813       break;
814     case 2: /* update */
815       msize -= sizeof (struct GNUNET_DNS_Response);
816       if ( (sizeof (struct GNUNET_TUN_DnsHeader) > msize) ||
817            (RP_REQUEST_MONITOR == rr->phase) ||
818            (RP_RESPONSE_MONITOR == rr->phase) )
819       {
820         GNUNET_break (0);
821         GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
822         next_phase (rr);
823         return;
824       }
825       GNUNET_free_non_null (rr->payload);
826       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
827                   "Changing DNS reply according to client specifications\n");
828       rr->payload = GNUNET_malloc (msize);
829       rr->payload_length = msize;
830       memcpy (rr->payload, &resp[1], msize);
831       if (rr->phase == RP_QUERY)
832       {
833         /* clear wait list, we're moving to MODIFY phase next */
834         GNUNET_array_grow (rr->client_wait_list,
835                            rr->client_wait_list_length,
836                            0);
837       }
838       /* if query changed to answer, move past DNS resolution phase... */
839       if ( (RP_QUERY == rr->phase) &&
840            (rr->payload_length > sizeof (struct GNUNET_TUN_DnsHeader)) &&
841            ((struct GNUNET_TUN_DnsFlags*)&(((struct GNUNET_TUN_DnsHeader*) rr->payload)->flags))->query_or_response == 1)
842       {
843         rr->phase = RP_INTERNET_DNS;
844         GNUNET_array_grow (rr->client_wait_list,
845                            rr->client_wait_list_length,
846                            0);
847       }
848       break;
849     }
850     next_phase (rr);
851     GNUNET_SERVER_receive_done (client, GNUNET_OK);
852     return;
853   }
854   /* odd, client was not on our list for the request, that ought
855      to be an error */
856   GNUNET_break (0);
857   GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
858 }
859
860
861 /**
862  * Functions with this signature are called whenever a complete
863  * message is received by the tokenizer from the DNS hijack process.
864  *
865  * @param cls closure
866  * @param client identification of the client
867  * @param message the actual message, a DNS request we should handle
868  */
869 static int
870 process_helper_messages (void *cls GNUNET_UNUSED, void *client,
871                          const struct GNUNET_MessageHeader *message)
872 {
873   uint16_t msize;
874   const struct GNUNET_TUN_Layer2PacketHeader *tun;
875   const struct GNUNET_TUN_IPv4Header *ip4;
876   const struct GNUNET_TUN_IPv6Header *ip6;
877   const struct GNUNET_TUN_UdpHeader *udp;
878   const struct GNUNET_TUN_DnsHeader *dns;
879   struct RequestRecord *rr;
880   struct sockaddr_in *srca4;
881   struct sockaddr_in6 *srca6;
882   struct sockaddr_in *dsta4;
883   struct sockaddr_in6 *dsta6;
884
885   LOG (GNUNET_ERROR_TYPE_DEBUG,
886        "Intercepted message via DNS hijacker\n");
887   msize = ntohs (message->size);
888   if (msize < sizeof (struct GNUNET_MessageHeader) + sizeof (struct GNUNET_TUN_Layer2PacketHeader) + sizeof (struct GNUNET_TUN_IPv4Header))
889   {
890     /* non-IP packet received on TUN!? */
891     GNUNET_break (0);
892     return GNUNET_OK;
893   }
894   msize -= sizeof (struct GNUNET_MessageHeader);
895   tun = (const struct GNUNET_TUN_Layer2PacketHeader *) &message[1];
896   msize -= sizeof (struct GNUNET_TUN_Layer2PacketHeader);
897   switch (ntohs (tun->proto))
898   {
899   case ETH_P_IPV4:
900     ip4 = (const struct GNUNET_TUN_IPv4Header *) &tun[1];
901     ip6 = NULL; /* make compiler happy */
902     if ( (msize < sizeof (struct GNUNET_TUN_IPv4Header)) ||
903          (ip4->version != 4) ||
904          (ip4->header_length != sizeof (struct GNUNET_TUN_IPv4Header) / 4) ||
905          (ntohs(ip4->total_length) != msize) ||
906          (ip4->protocol != IPPROTO_UDP) )
907     {
908       /* non-IP/UDP packet received on TUN (or with options) */
909       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
910                   _("Received malformed IPv4-UDP packet on TUN interface.\n"));
911       return GNUNET_OK;
912     }
913     udp = (const struct GNUNET_TUN_UdpHeader*) &ip4[1];
914     msize -= sizeof (struct GNUNET_TUN_IPv4Header);
915     break;
916   case ETH_P_IPV6:
917     ip4 = NULL; /* make compiler happy */
918     ip6 = (const struct GNUNET_TUN_IPv6Header *) &tun[1];
919     if ( (msize < sizeof (struct GNUNET_TUN_IPv6Header)) ||
920          (ip6->version != 6) ||
921          (ntohs (ip6->payload_length) != msize) ||
922          (ip6->next_header != IPPROTO_UDP) )
923     {
924       /* non-IP/UDP packet received on TUN (or with extensions) */
925       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
926                   _("Received malformed IPv6-UDP packet on TUN interface.\n"));
927       return GNUNET_OK;
928     }
929     udp = (const struct GNUNET_TUN_UdpHeader*) &ip6[1];
930     msize -= sizeof (struct GNUNET_TUN_IPv6Header);
931     break;
932   default:
933     /* non-IP packet received on TUN!? */
934     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
935                 _("Got non-IP packet with %u bytes and protocol %u from TUN\n"),
936                 (unsigned int) msize,
937                 ntohs (tun->proto));
938     return GNUNET_OK;
939   }
940   if ( (msize <= sizeof (struct GNUNET_TUN_UdpHeader) + sizeof (struct GNUNET_TUN_DnsHeader)) ||
941        (DNS_PORT != ntohs (udp->destination_port)) )
942   {
943     /* non-DNS packet received on TUN, ignore */
944     GNUNET_STATISTICS_update (stats,
945                               gettext_noop ("# Non-DNS UDP packet received via TUN interface"),
946                               1, GNUNET_NO);
947     return GNUNET_OK;
948   }
949   msize -= sizeof (struct GNUNET_TUN_UdpHeader);
950   dns = (const struct GNUNET_TUN_DnsHeader*) &udp[1];
951   rr = &requests[dns->id];
952
953   /* clean up from previous request */
954   GNUNET_free_non_null (rr->payload);
955   rr->payload = NULL;
956   GNUNET_array_grow (rr->client_wait_list,
957                      rr->client_wait_list_length,
958                      0);
959
960   /* setup new request */
961   rr->phase = RP_INIT;
962   switch (ntohs (tun->proto))
963   {
964   case ETH_P_IPV4:
965     {
966       srca4 = (struct sockaddr_in*) &rr->src_addr;
967       dsta4 = (struct sockaddr_in*) &rr->dst_addr;
968       memset (srca4, 0, sizeof (struct sockaddr_in));
969       memset (dsta4, 0, sizeof (struct sockaddr_in));
970       srca4->sin_family = AF_INET;
971       dsta4->sin_family = AF_INET;
972       srca4->sin_addr = ip4->source_address;
973       dsta4->sin_addr = ip4->destination_address;
974       srca4->sin_port = udp->source_port;
975       dsta4->sin_port = udp->destination_port;
976 #if HAVE_SOCKADDR_IN_SIN_LEN
977       srca4->sin_len = sizeof (struct sockaddr_in);
978       dsta4->sin_len = sizeof (struct sockaddr_in);
979 #endif
980     }
981     break;
982   case ETH_P_IPV6:
983     {
984       srca6 = (struct sockaddr_in6*) &rr->src_addr;
985       dsta6 = (struct sockaddr_in6*) &rr->dst_addr;
986       memset (srca6, 0, sizeof (struct sockaddr_in6));
987       memset (dsta6, 0, sizeof (struct sockaddr_in6));
988       srca6->sin6_family = AF_INET6;
989       dsta6->sin6_family = AF_INET6;
990       srca6->sin6_addr = ip6->source_address;
991       dsta6->sin6_addr = ip6->destination_address;
992       srca6->sin6_port = udp->source_port;
993       dsta6->sin6_port = udp->destination_port;
994 #if HAVE_SOCKADDR_IN_SIN_LEN
995       srca6->sin6_len = sizeof (struct sockaddr_in6);
996       dsta6->sin6_len = sizeof (struct sockaddr_in6);
997 #endif
998     }
999   break;
1000   default:
1001     GNUNET_assert (0);
1002   }
1003   rr->payload = GNUNET_malloc (msize);
1004   rr->payload_length = msize;
1005   memcpy (rr->payload, dns, msize);
1006   rr->request_id = dns->id | (request_id_gen << 16);
1007   request_id_gen++;
1008   LOG (GNUNET_ERROR_TYPE_DEBUG,
1009        "Creating new DNS request %llu\n",
1010        (unsigned long long) rr->request_id);
1011   GNUNET_STATISTICS_update (stats,
1012                             gettext_noop ("# DNS requests received via TUN interface"),
1013                             1, GNUNET_NO);
1014   /* start request processing state machine */
1015   next_phase (rr);
1016   return GNUNET_OK;
1017 }
1018
1019
1020 /**
1021  * @param cls closure
1022  * @param server the initialized server
1023  * @param cfg_ configuration to use
1024  */
1025 static void
1026 run (void *cls, struct GNUNET_SERVER_Handle *server,
1027      const struct GNUNET_CONFIGURATION_Handle *cfg_)
1028 {
1029   static const struct GNUNET_SERVER_MessageHandler handlers[] = {
1030     /* callback, cls, type, size */
1031     {&handle_client_init, NULL, GNUNET_MESSAGE_TYPE_DNS_CLIENT_INIT,
1032      sizeof (struct GNUNET_DNS_Register)},
1033     {&handle_client_response, NULL, GNUNET_MESSAGE_TYPE_DNS_CLIENT_RESPONSE, 0},
1034     {NULL, NULL, 0, 0}
1035   };
1036   char *ifc_name;
1037   char *ipv4addr;
1038   char *ipv4mask;
1039   char *ipv6addr;
1040   char *ipv6prefix;
1041   struct in_addr dns_exit4;
1042   struct in6_addr dns_exit6;
1043   char *dns_exit;
1044   char *binary;
1045
1046   cfg = cfg_;
1047   stats = GNUNET_STATISTICS_create ("dns", cfg);
1048   nc = GNUNET_SERVER_notification_context_create (server, 1);
1049   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
1050                                 &cleanup_task,
1051                                 cls);
1052   dns_exit = NULL;
1053   if ( ( (GNUNET_OK !=
1054           GNUNET_CONFIGURATION_get_value_string (cfg,
1055                                                  "dns",
1056                                                  "DNS_EXIT",
1057                                                  &dns_exit)) ||
1058          ( (1 != inet_pton (AF_INET, dns_exit, &dns_exit4)) &&
1059            (1 != inet_pton (AF_INET6, dns_exit, &dns_exit6)) ) ) )
1060   {
1061     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
1062                                "dns",
1063                                "DNS_EXIT",
1064                                _("need a valid IPv4 or IPv6 address\n"));
1065     GNUNET_free_non_null (dns_exit);
1066     dns_exit = NULL;
1067   }
1068   dnsstub = GNUNET_DNSSTUB_start (dns_exit);
1069   GNUNET_free_non_null (dns_exit);
1070   GNUNET_SERVER_add_handlers (server,
1071                               handlers);
1072   GNUNET_SERVER_disconnect_notify (server,
1073                                    &client_disconnect,
1074                                    NULL);
1075   binary = GNUNET_OS_get_libexec_binary_path ("gnunet-helper-dns");
1076   if (GNUNET_YES !=
1077       GNUNET_OS_check_helper_binary (binary,
1078                                      GNUNET_YES,
1079                                      NULL)) // TODO: once we have a windows-testcase, add test parameters here
1080   {
1081     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1082                 _("`%s' must be installed SUID, will not run DNS interceptor\n"),
1083                 binary);
1084     global_ret = 1;
1085     GNUNET_free (binary);
1086     return;
1087   }
1088   GNUNET_free (binary);
1089
1090   helper_argv[0] = GNUNET_strdup ("gnunet-dns");
1091   if (GNUNET_SYSERR ==
1092       GNUNET_CONFIGURATION_get_value_string (cfg, "dns", "IFNAME", &ifc_name))
1093   {
1094     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1095                 "No entry 'IFNAME' in configuration!\n");
1096     GNUNET_SCHEDULER_shutdown ();
1097     return;
1098   }
1099   helper_argv[1] = ifc_name;
1100   if ( (GNUNET_SYSERR ==
1101         GNUNET_CONFIGURATION_get_value_string (cfg, "dns", "IPV6ADDR",
1102                                                &ipv6addr)) )
1103   {
1104     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1105                 "No entry 'IPV6ADDR' in configuration!\n");
1106     GNUNET_SCHEDULER_shutdown ();
1107     return;
1108   }
1109   helper_argv[2] = ipv6addr;
1110   if (GNUNET_SYSERR ==
1111       GNUNET_CONFIGURATION_get_value_string (cfg, "dns", "IPV6PREFIX",
1112                                              &ipv6prefix))
1113   {
1114     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1115                 "No entry 'IPV6PREFIX' in configuration!\n");
1116     GNUNET_SCHEDULER_shutdown ();
1117     return;
1118   }
1119   helper_argv[3] = ipv6prefix;
1120
1121   if (GNUNET_SYSERR ==
1122       GNUNET_CONFIGURATION_get_value_string (cfg, "dns", "IPV4ADDR",
1123                                              &ipv4addr))
1124   {
1125     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1126                 "No entry 'IPV4ADDR' in configuration!\n");
1127     GNUNET_SCHEDULER_shutdown ();
1128     return;
1129   }
1130   helper_argv[4] = ipv4addr;
1131   if (GNUNET_SYSERR ==
1132       GNUNET_CONFIGURATION_get_value_string (cfg, "dns", "IPV4MASK",
1133                                              &ipv4mask))
1134   {
1135     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1136                 "No entry 'IPV4MASK' in configuration!\n");
1137     GNUNET_SCHEDULER_shutdown ();
1138     return;
1139   }
1140   helper_argv[5] = ipv4mask;
1141   helper_argv[6] = NULL;
1142   hijacker = GNUNET_HELPER_start (GNUNET_NO,
1143                                   "gnunet-helper-dns",
1144                                   helper_argv,
1145                                   &process_helper_messages,
1146                                   NULL, NULL);
1147 }
1148
1149
1150 /**
1151  * The main function for the dns service.
1152  *
1153  * @param argc number of arguments from the command line
1154  * @param argv command line arguments
1155  * @return 0 ok, 1 on error
1156  */
1157 int
1158 main (int argc, char *const *argv)
1159 {
1160   /* make use of SGID capabilities on POSIX */
1161   /* FIXME: this might need a port on systems without 'getresgid' */
1162 #if HAVE_GETRESGID
1163   gid_t rgid;
1164   gid_t egid;
1165   gid_t sgid;
1166
1167   if (-1 == getresgid (&rgid, &egid, &sgid))
1168   {
1169     fprintf (stderr,
1170              "getresgid failed: %s\n",
1171              strerror (errno));
1172   }
1173   else if (sgid != rgid)
1174   {
1175     if (-1 ==  setregid (sgid, sgid))
1176       fprintf (stderr, "setregid failed: %s\n", strerror (errno));
1177   }
1178 #endif
1179   return (GNUNET_OK ==
1180           GNUNET_SERVICE_run (argc, argv, "dns", GNUNET_SERVICE_OPTION_NONE,
1181                               &run, NULL)) ? global_ret : 1;
1182 }
1183
1184
1185 /* end of gnunet-service-dns.c */