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