-moving DNS API into the new direction outlined in my last comment
[oweals/gnunet.git] / src / dns / gnunet-service-dns_new.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_new.c
23  * @author Christian Grothoff
24  */
25 // current thoughts:
26 // - for full compatibility to DNS and to avoid going insane here parsing/generating DNS packets,
27 //   how about literally attaching the "original" DNS packet (request/response) to the IPC traffic?
28 //   that way, clients can literally do arbitrary modifications and we are done with that issue here.
29 //   All we'd do in here is add the IP/UDP headers and be DONE with it.
30 // => minor modifications to API and IPC protocol
31 // => minor modifications to our data structures
32 // => major gains in terms of simplicity here and what can (at least theoretically) be done with the service
33 // => can test much more quickly
34 // => but: need to really write a good libgnunetdnsparse to avoid making MANY clients really complicated
35 //    (not the worst of worlds either, other than deferring this mess some...)
36 //    -> also positive: can be tested independently of the rest of the mess
37
38
39 #include "platform.h"
40 #include "gnunet_util_lib.h"
41 #include "gnunet_constants.h"
42 #include "gnunet_protocols.h"
43 //#include "gnunet_dnsparser_lib.h"
44 #include "gnunet_signatures.h"
45 #include "dns_new.h"
46
47 GNUNET_NETWORK_STRUCT_BEGIN
48 struct ip4_hdr
49 {
50   unsigned hdr_lngth:4 GNUNET_PACKED;
51   unsigned version:4 GNUNET_PACKED;
52
53   uint8_t diff_serv;
54   uint16_t tot_lngth GNUNET_PACKED;
55
56   uint16_t ident GNUNET_PACKED;
57   unsigned flags:3 GNUNET_PACKED;
58   unsigned frag_off:13 GNUNET_PACKED;
59
60   uint8_t ttl;
61   uint8_t proto;
62   uint16_t chks GNUNET_PACKED;
63
64   struct in_addr sadr GNUNET_PACKED;
65   struct in_addr dadr GNUNET_PACKED;
66 };
67
68 struct ip6_hdr
69 {
70   unsigned tclass_h:4 GNUNET_PACKED;
71   unsigned version:4 GNUNET_PACKED;
72   unsigned tclass_l:4 GNUNET_PACKED;
73   unsigned flowlbl:20 GNUNET_PACKED;
74   uint16_t paylgth GNUNET_PACKED;
75   uint8_t nxthdr;
76   uint8_t hoplmt;
77   struct in6_addr sadr GNUNET_PACKED;
78   struct in6_addr dadr GNUNET_PACKED;
79 };
80
81 struct udp_pkt
82 {
83   uint16_t spt GNUNET_PACKED;
84   uint16_t dpt GNUNET_PACKED;
85   uint16_t len GNUNET_PACKED;
86   uint16_t crc GNUNET_PACKED;
87 };
88
89 struct dns_pkt
90 {
91   uint16_t id GNUNET_PACKED;
92
93   unsigned rd:1 GNUNET_PACKED;  // recursion desired (client -> server)
94   unsigned tc:1 GNUNET_PACKED;  // message is truncated
95   unsigned aa:1 GNUNET_PACKED;  // authoritative answer
96   unsigned op:4 GNUNET_PACKED;  // query:0, inverse q.:1, status: 2
97   unsigned qr:1 GNUNET_PACKED;  // query:0, response:1
98
99   unsigned rcode:4 GNUNET_PACKED;       // 0 No error
100   // 1 Format error
101   // 2 Server failure
102   // 3 Name Error
103   // 4 Not Implemented
104   // 5 Refused
105   unsigned z:3 GNUNET_PACKED;   // reserved
106   unsigned ra:1 GNUNET_PACKED;  // recursion available (server -> client)
107
108   uint16_t qdcount GNUNET_PACKED;       // number of questions
109   uint16_t ancount GNUNET_PACKED;       // number of answers
110   uint16_t nscount GNUNET_PACKED;       // number of authority-records
111   uint16_t arcount GNUNET_PACKED;       // number of additional records
112 };
113
114 struct dns_query_line
115 {
116   uint16_t type;
117   uint16_t class;
118 };
119
120 struct dns_record_line
121 {
122   uint16_t type;
123   uint16_t class;
124   uint32_t ttl;
125   uint16_t data_len;
126 };
127 GNUNET_NETWORK_STRUCT_END
128
129
130 /**
131  * Entry we keep for each client.
132  */ 
133 struct ClientRecord
134 {
135   /**
136    * Kept in doubly-linked list.
137    */ 
138   struct ClientRecord *next;
139
140   /**
141    * Kept in doubly-linked list.
142    */ 
143   struct ClientRecord *prev;
144
145   /**
146    * Handle to the client.
147    */ 
148   struct GNUNET_SERVER_Client *client;
149
150 };
151
152
153 /**
154  * Entry we keep for each active request.
155  */ 
156 struct RequestRecord
157 {
158
159   /**
160    * Name for the request.
161    */
162   char *name;
163
164   /**
165    * Response data, or NULL if not known.
166    */ 
167   char *rdata;
168
169   /**
170    * List of clients that still need to see this request (each entry
171    * is set to NULL when the client is done).
172    */
173   struct ClientRecord **client_wait_list;
174
175   /**
176    * Length of the client wait list.
177    */
178   unsigned int client_wait_list_length;
179
180   /**
181    * Source address of the original request (for sending response).
182    */
183   struct sockaddr_storage src_addr;
184
185   /**
186    * Destination address of the original request (for potential use as exit).
187    */
188   struct sockaddr_storage dst_addr;
189
190   /**
191    * ID of this request, also basis for hashing.  Lowest 16 bit will
192    * be our message ID when doing a global DNS request and our index
193    * into the 'requests' array.
194    */
195   uint64_t request_id;
196
197   /**
198    * TTL if we know it, or 0.
199    */
200   uint32_t dns_ttl;
201
202   /**
203    * Number of bytes in rdata.
204    */ 
205   uint16_t rdata_length;
206
207   /**
208    * Length of the 'name' string, including 0-terminator.
209    */
210   uint16_t name_length;
211
212   /**
213    * The DNS type (i.e. 1 == 'A').
214    */
215   uint16_t dns_type;
216
217   /**
218    * The DNS class (i.e. 1 == Internet)
219    */
220   uint16_t dns_class;
221
222   /**
223    * Original DNS Id we got from the client.
224    */
225   uint16_t original_dns_id;
226
227 };
228
229
230 /**
231  * The IPv4 UDP-Socket through which DNS-Resolves will be sent if they are not to be
232  * sent through gnunet. The port of this socket will not be hijacked.
233  */
234 static struct GNUNET_NETWORK_Handle *dnsout4;
235
236 /**
237  * The IPv6 UDP-Socket through which DNS-Resolves will be sent if they are not to be
238  * sent through gnunet. The port of this socket will not be hijacked.
239  */
240 static struct GNUNET_NETWORK_Handle *dnsout6;
241
242 /**
243  * Task for reading from dnsout4.
244  */
245 static GNUNET_SCHEDULER_TaskIdentifier read4_task;
246
247 /**
248  * Task for reading from dnsout6.
249  */
250 static GNUNET_SCHEDULER_TaskIdentifier read6_task;
251
252 /**
253  * The port bound to the socket dnsout (and/or dnsout6).  We always (try) to bind
254  * both sockets to the same port.
255  */
256 static uint16_t dnsoutport;
257
258 /**
259  * The configuration to use
260  */
261 static const struct GNUNET_CONFIGURATION_Handle *cfg;
262
263 /**
264  * Handle to DNS hijacker helper process ("gnunet-helper-dns").
265  */
266 static struct GNUNET_HELPER_Handle *hijacker;
267
268 /**
269  * Command-line arguments we are giving to the hijacker process.
270  */
271 static char *helper_argv[8];
272
273 /**
274  * Head of DLL of clients we consult.
275  */
276 static struct ClientRecord *clients_head;
277
278 /**
279  * Tail of DLL of clients we consult.
280  */
281 static struct ClientRecord *clients_tail;
282
283 /**
284  * Array of all open requests.
285  */
286 static struct RequestRecord requests[UINT16_MAX];
287
288 /**
289  * Generator for unique request IDs.
290  */
291 static uint64_t request_id_gen;
292
293
294 /**
295  * Task run during shutdown.
296  *
297  * @param cls unused
298  * @param tc unused
299  */
300 static void
301 cleanup_task (void *cls GNUNET_UNUSED,
302               const struct GNUNET_SCHEDULER_TaskContext *tc)
303 {
304   unsigned int i;
305   struct RequestRecord *rr;
306
307   GNUNET_HELPER_stop (hijacker);
308   hijacker = NULL;
309   for (i=0;i<8;i++)
310     GNUNET_free_non_null (helper_argv[i]);
311   if (NULL != dnsout4)
312   {
313     GNUNET_NETWORK_socket_close (dnsout4);
314     dnsout4 = NULL;
315   }
316   if (GNUNET_SCHEDULER_NO_TASK != read4_task)
317   {
318     GNUNET_SCHEDULER_cancel (read4_task);
319     read4_task = GNUNET_SCHEDULER_NO_TASK;
320   }
321   if (NULL != dnsout6)
322   {
323     GNUNET_NETWORK_socket_close (dnsout6);
324     dnsout6 = NULL;
325   }
326   if (GNUNET_SCHEDULER_NO_TASK != read6_task)
327   {
328     GNUNET_SCHEDULER_cancel (read6_task);
329     read6_task = GNUNET_SCHEDULER_NO_TASK;
330   }
331   for (i=0;i<65536;i++)
332   {
333     rr = &requests[i];
334     GNUNET_free (rr->name);
335     GNUNET_free_non_null (rr->rdata);
336     GNUNET_array_grow (rr->client_wait_list,
337                        rr->client_wait_list_length,
338                        0);
339   }
340 }
341
342
343 /**
344  * We're done with some request, finish processing.
345  */
346 static void
347 request_done (struct RequestRecord *rr)
348 {
349   struct GNUNET_MessageHeader *hdr;
350   size_t reply_len;
351   uint16_t spt;
352   uint16_t dpt;
353
354   GNUNET_array_grow (rr->client_wait_list,
355                      rr->client_wait_list_length,
356                      0); 
357   if (NULL == rr->rdata)
358   {
359     /* no response, drop */
360     GNUNET_free (rr->name);
361     rr->name = NULL;
362     return;
363   }
364   
365   /* send response via hijacker */
366   reply_len = sizeof (struct GNUNET_MessageHeader);
367   switch (rr->src_addr.ss_family)
368   {
369   case AF_INET:
370     reply_len += sizeof (struct ip4_hdr);
371     break;
372   case AF_INET6:
373     reply_len += sizeof (struct ip6_hdr);
374     break;
375   default:
376     GNUNET_break (0);
377     GNUNET_free (rr->name);
378     rr->name = NULL;
379     return;   
380   }
381   reply_len += sizeof (struct udp_pkt);
382   reply_len += sizeof (struct dns_pkt);
383   reply_len += rr->name_length;
384   reply_len += sizeof (struct dns_record_line);
385   reply_len += rr->rdata_length;
386   if (reply_len >= GNUNET_SERVER_MAX_MESSAGE_SIZE)
387   {
388     /* response too big, drop */
389     GNUNET_break (0); /* how can this be? */
390     GNUNET_free (rr->name);
391     rr->name = NULL;
392     return;    
393   }
394   {
395     char buf[reply_len];
396     size_t off;
397
398     /* first, GNUnet message header */
399     hdr = (struct GNUNET_MessageHeader*) buf;
400     hdr->type = htons (GNUNET_MESSAGE_TYPE_DNS_HELPER);
401     hdr->size = htons ((uint16_t) reply_len);
402     off = sizeof (struct GNUNET_MessageHeader);
403
404     /* now IP header */
405     switch (rr->src_addr.ss_family)
406     {
407     case AF_INET:
408       {
409         struct sockaddr_in *src = (struct sockaddr_in *) &rr->src_addr;
410         struct sockaddr_in *dst = (struct sockaddr_in *) &rr->dst_addr;
411         struct ip4_hdr ip;
412         
413         spt = dst->sin_port;
414         dpt = src->sin_port;
415         // FIXME: fill in IP header!
416         memcpy (&buf[off], &ip, sizeof (ip));
417         off += sizeof (ip);
418         break;
419       }
420     case AF_INET6:
421       {
422         struct sockaddr_in6 *src = (struct sockaddr_in6 *) &rr->src_addr;
423         struct sockaddr_in6 *dst = (struct sockaddr_in6 *) &rr->dst_addr;
424         struct ip6_hdr ip;
425
426         spt = dst->sin6_port;
427         dpt = src->sin6_port;
428         // FIXME: fill in IP header!
429         memcpy (&buf[off], &ip, sizeof (ip));
430         off += sizeof (ip);
431       }
432       break;
433     default:
434       GNUNET_assert (0);
435     }
436     /* now UDP header */
437     {
438       struct udp_pkt udp;
439
440       udp.spt = spt;
441       udp.dpt = dpt;
442       udp.len = htons (reply_len - off);
443       udp.crc = 0; /* checksum is optional */
444       memcpy (&buf[off], &udp, sizeof (udp));
445       off += sizeof (udp);
446     }
447
448     /* now DNS header */
449     {
450       struct dns_pkt dns;
451
452       dns.id = rr->original_dns_id;
453       dns.rd = 1; /* recursion desired / supported */
454       dns.tc = 0; /* not truncated */
455       dns.aa = 1; /* are we authoritative!? I say yes. */
456       dns.op = 0; /* standard query */
457       dns.qr = 1; /* this is a response */
458       dns.rcode = 0; /* no error */
459       dns.z = 0; /* reserved */
460       dns.ra = 1; /* recursion available */
461       dns.qdcount = htons (0); /* no queries */
462       dns.ancount = htons (1); /* one answer */
463       dns.nscount = htons (0); /* no authorities yet (fixme) */
464       dns.arcount = htons (0); /* no additinal records yet (fixme) */
465       memcpy (&buf[off], &dns, sizeof (dns));
466       off += sizeof (dns);
467     }
468
469     /* now DNS name */
470     {
471       // FIXME: fill in DNS name!
472       off += rr->name_length;
473     }
474
475
476     /* now DNS record line */
477     {
478       struct dns_record_line drl;
479       
480       drl.type = htons (rr->dns_type);
481       drl.class = htons (rr->dns_class);
482       drl.ttl = htonl (rr->dns_ttl);
483       drl.data_len = htons (rr->rdata_length);
484       memcpy (&buf[off], &drl, sizeof (drl));
485       off += sizeof (drl);
486     }
487
488     /* now DNS rdata */
489     {
490       memcpy (&buf[off], rr->rdata, rr->rdata_length);
491       off += rr->rdata_length;
492     }
493     
494     /* final checks & sending */
495     GNUNET_assert (off == reply_len);
496     GNUNET_HELPER_send (hijacker,
497                         hdr,
498                         GNUNET_YES,
499                         NULL, NULL);
500   }
501   /* clean up, we're done */
502   GNUNET_free (rr->name);
503   rr->name = NULL;
504 }
505
506
507 /**
508  * A client disconnected, clean up after it.
509  *
510  * @param cls unused
511  * @param client handle of client that disconnected
512  */ 
513 static void
514 client_disconnect (void *cls, struct GNUNET_SERVER_Client *client)
515 {
516   struct ClientRecord *cr;
517   struct RequestRecord *rr;
518   unsigned int i;
519   unsigned int j;
520   int az;
521
522   for (cr = clients_head; NULL != cr; cr = cr->next)
523   {
524     if (cr->client == client)
525     {
526       GNUNET_SERVER_client_drop (client);
527       GNUNET_CONTAINER_DLL_remove (clients_head,
528                                    clients_tail,
529                                    cr);
530       for (i=0;i<UINT16_MAX;i++)
531       {
532         rr = &requests[i];
533         if (0 == rr->client_wait_list_length)
534           continue; /* not in use */
535         az = 1;
536         for (j=0;j<rr->client_wait_list_length;j++)
537         {
538           if (rr->client_wait_list[j] == cr)
539             rr->client_wait_list[j] = NULL;
540           if (rr->client_wait_list[j] != NULL)
541             az = 0;
542         }
543         if (1 == az)
544           request_done (rr); /* this was the last client... */
545       }
546       GNUNET_free (cr);
547       return;
548     }
549   }
550 }
551
552
553 /**
554  * Read a DNS response from the (unhindered) UDP-Socket
555  *
556  * @param cls socket to read from
557  * @param tc scheduler context (must be shutdown or read ready)
558  */
559 static void
560 read_response (void *cls,
561                const struct GNUNET_SCHEDULER_TaskContext *tc)
562 {
563   struct GNUNET_NETWORK_Handle *dnsout = cls;
564   struct sockaddr_in addr4;
565   struct sockaddr_in6 addr6;
566   struct sockaddr *addr;
567   socklen_t addrlen;
568   ssize_t r;
569   int len;
570
571   if (dnsout == dnsout4)
572   {
573     addrlen = sizeof (struct sockaddr_in);
574     addr = (struct sockaddr* ) &addr4;
575     read4_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, 
576                                                 dnsout,
577                                                 &read_response, 
578                                                 dnsout);
579   }
580   else
581   {
582     addrlen = sizeof (struct sockaddr_in6);
583     addr = (struct sockaddr* ) &addr6;
584     read6_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, 
585                                                 dnsout,
586                                                 &read_response, 
587                                                 dnsout);
588   }
589   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
590     return;
591
592 #ifndef MINGW
593   if (0 != ioctl (GNUNET_NETWORK_get_fd (dnsout), FIONREAD, &len))
594   {
595     /* conservative choice: */
596     len = 65536;
597   }
598 #else
599   /* port the code above? */
600   len = 65536;
601 #endif
602
603   {
604     unsigned char buf[len];
605
606     memset (addr, 0, addrlen);  
607     r = GNUNET_NETWORK_socket_recvfrom (dnsout, 
608                                         buf, sizeof (buf),
609                                         addr, &addrlen);
610     if (-1 == r)
611     {
612       GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "recvfrom");
613       return;
614     }
615     // NOTE: struct dns_pkt *dns = (struct dns_pkt *) buf;
616     // FIXME: handle_response (buf, r, addr, addrlen);
617   }
618 }
619
620
621 /**
622  * Open source port for sending DNS request on IPv4.
623  *
624  * @return GNUNET_OK on success
625  */ 
626 static int
627 open_port4 ()
628 {
629   struct sockaddr_in addr;
630   socklen_t addrlen;
631
632   dnsout4 = GNUNET_NETWORK_socket_create (AF_INET, SOCK_DGRAM, 0);
633   if (dnsout4 == NULL)
634     return GNUNET_SYSERR;
635
636   memset (&addr, 0, sizeof (struct sockaddr_in));
637   addr.sin_family = AF_INET;
638   int err = GNUNET_NETWORK_socket_bind (dnsout4,
639                                         (struct sockaddr *) &addr,
640                                         sizeof (struct sockaddr_in));
641
642   if (err != GNUNET_OK)
643   {
644     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 
645                 _("Could not bind to any port: %s\n"),
646                 STRERROR (errno));
647     GNUNET_NETWORK_socket_close (dnsout4);
648     dnsout4 = NULL;
649     return GNUNET_SYSERR;
650   }
651
652   /* Read the port we bound to */
653   addrlen = sizeof (struct sockaddr_in);
654   if (0 != getsockname (GNUNET_NETWORK_get_fd (dnsout4), 
655                         (struct sockaddr *) &addr,
656                         &addrlen))
657   {
658     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 
659                 _("Could not determine port I got: %s\n"),
660                 STRERROR (errno));
661     GNUNET_NETWORK_socket_close (dnsout4);
662     dnsout4 = NULL;
663     return GNUNET_SYSERR;
664   }
665   dnsoutport = htons (addr.sin_port);
666
667   GNUNET_log (GNUNET_ERROR_TYPE_INFO, 
668               _("GNUnet DNS will exit on source port %u\n"),
669               (unsigned int) dnsoutport);
670   read4_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, 
671                                               dnsout4,
672                                               &read_response, dnsout4);
673   return GNUNET_OK;
674 }
675
676
677 /**
678  * Open source port for sending DNS request on IPv6.  Should be 
679  * called AFTER open_port4.
680  *
681  * @return GNUNET_OK on success
682  */ 
683 static int
684 open_port6 ()
685 {
686   struct sockaddr_in6 addr;
687   socklen_t addrlen;
688
689   dnsout6 = GNUNET_NETWORK_socket_create (AF_INET6, SOCK_DGRAM, 0);
690   if (dnsout6 == NULL)
691   {
692     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 
693                 _("Could not create IPv6 socket: %s\n"),
694                 STRERROR (errno));
695     return GNUNET_SYSERR;
696   }
697   memset (&addr, 0, sizeof (struct sockaddr_in6));
698   addr.sin6_family = AF_INET6;
699   addr.sin6_port = htons (dnsoutport);
700   int err = GNUNET_NETWORK_socket_bind (dnsout6,
701                                         (struct sockaddr *) &addr,
702                                         sizeof (struct sockaddr_in6));
703
704   if (err != GNUNET_OK)
705   {
706     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
707                 _("Could not bind to port %u: %s\n"),
708                 (unsigned int) dnsoutport,
709                 STRERROR (errno));
710     GNUNET_NETWORK_socket_close (dnsout6);
711     dnsout6 = NULL;
712     return GNUNET_SYSERR;
713   }
714   if (0 == dnsoutport)
715   {
716     addrlen = sizeof (struct sockaddr_in6);
717     if (0 != getsockname (GNUNET_NETWORK_get_fd (dnsout6), 
718                           (struct sockaddr *) &addr,
719                           &addrlen))
720     {
721       GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 
722                   _("Could not determine port I got: %s\n"),
723                   STRERROR (errno));
724       GNUNET_NETWORK_socket_close (dnsout6);
725       dnsout6 = NULL;
726       return GNUNET_SYSERR;
727     }
728   }
729   dnsoutport = htons (addr.sin6_port);
730   read6_task = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
731                                               dnsout6,
732                                               &read_response, dnsout6);
733   return GNUNET_YES;
734 }
735
736
737 /**
738  * We got a new client.  Make sure all new DNS requests pass by its desk.
739  *
740  * @param cls unused
741  * @param client the new client
742  * @param message the init message (unused)
743  */
744 static void
745 handle_client_init (void *cls GNUNET_UNUSED, 
746                     struct GNUNET_SERVER_Client *client,
747                     const struct GNUNET_MessageHeader *message GNUNET_UNUSED)
748 {
749   struct ClientRecord *cr;
750
751   cr = GNUNET_malloc (sizeof (struct ClientRecord));
752   cr->client = client;
753   GNUNET_SERVER_client_keep (client);
754   GNUNET_CONTAINER_DLL_insert (clients_head,
755                                clients_tail,
756                                cr);
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 GNUNET_UNUSED)
772 {
773   // FIXME: validate and parse response, process response
774   GNUNET_SERVER_receive_done (client, GNUNET_OK);
775 }
776
777
778 /**
779  * Functions with this signature are called whenever a complete
780  * message is received by the tokenizer from the DNS hijack process.
781  *
782  * @param cls closure
783  * @param client identification of the client
784  * @param message the actual message, a DNS request we should handle
785  */
786 static void
787 process_helper_messages (void *cls, void *client,
788                          const struct GNUNET_MessageHeader *message)
789 {
790   /* FIXME: parse message, create record, start processing! */
791   /* FIXME: put request into queue for clients / system DNS */
792   request_id_gen++;
793 }
794
795
796 /**
797  * @param cls closure
798  * @param server the initialized server
799  * @param cfg_ configuration to use
800  */
801 static void
802 run (void *cls, struct GNUNET_SERVER_Handle *server,
803      const struct GNUNET_CONFIGURATION_Handle *cfg_)
804 {
805   static const struct GNUNET_SERVER_MessageHandler handlers[] = {
806     /* callback, cls, type, size */
807     {&handle_client_init, NULL, GNUNET_MESSAGE_TYPE_DNS_CLIENT_INIT, sizeof (struct GNUNET_MessageHeader)},
808     {&handle_client_response, NULL, GNUNET_MESSAGE_TYPE_DNS_CLIENT_RESPONSE, 0},
809     {NULL, NULL, 0, 0}
810   };
811   char port_s[6];
812   char *ifc_name;
813   char *ipv4addr;
814   char *ipv4mask;
815   char *ipv6addr;
816   char *ipv6prefix;
817
818   cfg = cfg_;
819   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &cleanup_task,
820                                 cls);
821   if (GNUNET_YES ==
822       GNUNET_CONFIGURATION_get_value_yesno (cfg_, "dns", "PROVIDE_EXIT"))
823   {
824     if ( (GNUNET_OK != open_port4 ()) &&
825          (GNUNET_OK != open_port6 ()) )
826     {
827       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
828                   _("Failed to open any port to provide DNS exit\n"));
829       GNUNET_SCHEDULER_shutdown ();
830       return;
831     }
832   }
833
834   helper_argv[0] = GNUNET_strdup ("gnunet-dns");
835   if (GNUNET_SYSERR ==
836       GNUNET_CONFIGURATION_get_value_string (cfg, "dns", "IFNAME", &ifc_name))
837   {
838     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
839                 "No entry 'IFNAME' in configuration!\n");
840     GNUNET_SCHEDULER_shutdown ();
841     return;
842   }
843   helper_argv[1] = ifc_name;
844   if ( (GNUNET_SYSERR ==
845         GNUNET_CONFIGURATION_get_value_string (cfg, "exit", "IPV6ADDR",
846                                                &ipv6addr)) )
847   {
848     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
849                 "No entry 'IPV6ADDR' in configuration!\n");
850     GNUNET_SCHEDULER_shutdown ();
851     return;
852   }
853   helper_argv[2] = ipv6addr;
854   if (GNUNET_SYSERR ==
855       GNUNET_CONFIGURATION_get_value_string (cfg, "exit", "IPV6PREFIX",
856                                              &ipv6prefix))
857   {
858     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
859                 "No entry 'IPV6PREFIX' in configuration!\n");
860     GNUNET_SCHEDULER_shutdown ();
861     return;
862   }
863   helper_argv[3] = ipv6prefix;
864
865   if (GNUNET_SYSERR ==
866       GNUNET_CONFIGURATION_get_value_string (cfg, "exit", "IPV4ADDR",
867                                              &ipv4addr))
868   {
869     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
870                 "No entry 'IPV4ADDR' in configuration!\n");
871     GNUNET_SCHEDULER_shutdown ();
872     return;
873   }
874   helper_argv[4] = ipv4addr;
875   if (GNUNET_SYSERR ==
876       GNUNET_CONFIGURATION_get_value_string (cfg, "exit", "IPV4MASK",
877                                              &ipv4mask))
878   {
879     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
880                 "No entry 'IPV4MASK' in configuration!\n");
881     GNUNET_SCHEDULER_shutdown ();
882     return;
883   }
884   helper_argv[5] = ipv4mask;
885   GNUNET_snprintf (port_s, 
886                    sizeof (port_s), 
887                    "%u", 
888                    (unsigned int) dnsoutport);
889   helper_argv[6] = GNUNET_strdup (port_s);
890   helper_argv[7] = NULL;
891   hijacker = GNUNET_HELPER_start ("gnunet-helper-dns",
892                                   helper_argv,
893                                   &process_helper_messages,
894                                   NULL);
895   GNUNET_SERVER_add_handlers (server, handlers);
896   GNUNET_SERVER_disconnect_notify (server, &client_disconnect, NULL);
897 }
898
899
900 /**
901  * The main function for the dns service.
902  *
903  * @param argc number of arguments from the command line
904  * @param argv command line arguments
905  * @return 0 ok, 1 on error
906  */
907 int
908 main (int argc, char *const *argv)
909 {
910   return (GNUNET_OK ==
911           GNUNET_SERVICE_run (argc, argv, "dns", GNUNET_SERVICE_OPTION_NONE,
912                               &run, NULL)) ? 0 : 1;
913 }
914
915
916 /* end of gnunet-service-dns_new.c */