55d6cc0153821d1d8dd00449af05127c17404f48
[oweals/gnunet.git] / src / gns / gnunet-dns2gns.c
1 /*
2      This file is part of GNUnet.
3      (C) 2012-2013 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  * @file gnunet-dns2gns.c
22  * @brief DNS server that translates DNS requests to GNS
23  * @author Christian Grothoff
24  */
25 #include "platform.h"
26 #include <gnunet_util_lib.h>
27 #include <gnunet_dnsparser_lib.h>
28 #include <gnunet_gns_service.h>
29 #include <gnunet_dnsstub_lib.h>
30 #include "gns.h"
31
32 /**
33  * Timeout for DNS requests.
34  */
35 #define TIMEOUT GNUNET_TIME_UNIT_MINUTES
36
37 /**
38  * Default suffix
39  */
40 #define DNS_SUFFIX ".zkey.eu"
41
42 /**
43  * FCFS suffix
44  */
45 #define FCFS_SUFFIX "fcfs.zkey.eu"
46
47 /**
48  * Data kept per request.
49  */
50 struct Request
51 {
52   /**
53    * Socket to use for sending the reply.
54    */
55   struct GNUNET_NETWORK_Handle *lsock;
56
57   /**
58    * Destination address to use.
59    */
60   const void *addr;
61
62   /**
63    * Initially, this is the DNS request, it will then be
64    * converted to the DNS response.
65    */
66   struct GNUNET_DNSPARSER_Packet *packet;
67   
68   /**
69    * Our GNS request handle.
70    */
71   struct GNUNET_GNS_LookupRequest *lookup;
72
73   /**
74    * Our DNS request handle
75    */
76   struct GNUNET_DNSSTUB_RequestSocket *dns_lookup;
77
78   /**
79    * Task run on timeout or shutdown to clean up without
80    * response.
81    */
82   GNUNET_SCHEDULER_TaskIdentifier timeout_task;
83
84   /**
85    * Number of bytes in 'addr'.
86    */ 
87   size_t addr_len;
88
89 };
90
91
92 /**
93  * Handle to GNS resolver.
94  */
95 struct GNUNET_GNS_Handle *gns;
96
97 /**
98  * Stub resolver
99  */
100 struct GNUNET_DNSSTUB_Context *dns_stub;
101
102 /**
103  * Listen socket for IPv4.
104  */
105 static struct GNUNET_NETWORK_Handle *listen_socket4;
106
107 /**
108  * Listen socket for IPv6.
109  */
110 static struct GNUNET_NETWORK_Handle *listen_socket6;
111
112 /**
113  * Task for IPv4 socket.
114  */
115 static GNUNET_SCHEDULER_TaskIdentifier t4;
116
117 /**
118  * Task for IPv6 socket.
119  */
120 static GNUNET_SCHEDULER_TaskIdentifier t6;
121
122 /**
123  * DNS suffix, suffix of this gateway in DNS; defaults to '.zkey.eu'
124  */
125 static char *dns_suffix;
126
127 /**
128  * FCFS suffix, suffix of FCFS-authority in DNS; defaults to 'fcfs.zkey.eu'.
129  */
130 static char *fcfs_suffix;
131
132 /**
133  * IP of DNS server
134  */
135 static char *dns_ip;
136
137 /**
138  * UDP Port we listen on for inbound DNS requests.
139  */
140 static unsigned int listen_port = 53;
141
142 /**
143  * Which GNS zone do we translate incoming DNS requests to?
144  */
145 static struct GNUNET_CRYPTO_EccPublicKey my_zone;
146
147 /**
148  * '-z' option with the main zone to use.
149  */
150 static char *gns_zone_str;
151
152
153 /**
154  * Task run on shutdown.  Cleans up everything.
155  *
156  * @param cls unused
157  * @param tc scheduler context
158  */
159 static void
160 do_shutdown (void *cls,
161              const struct GNUNET_SCHEDULER_TaskContext *tc)
162 {
163   if (GNUNET_SCHEDULER_NO_TASK != t4)
164     GNUNET_SCHEDULER_cancel (t4);
165   if (GNUNET_SCHEDULER_NO_TASK != t6)
166     GNUNET_SCHEDULER_cancel (t6);
167   if (NULL != listen_socket4)
168   {
169     GNUNET_NETWORK_socket_close (listen_socket4);
170     listen_socket4 = NULL;
171   }
172   if (NULL != listen_socket6)
173   {
174     GNUNET_NETWORK_socket_close (listen_socket6);
175     listen_socket6 = NULL;
176   }
177   GNUNET_GNS_disconnect (gns);
178   gns = NULL;
179   GNUNET_DNSSTUB_stop (dns_stub);
180   dns_stub = NULL;
181 }
182
183
184 /**
185  * Send the response for the given request and clean up.
186  *
187  * @param request context for the request.
188  */
189 static void
190 send_response (struct Request *request)
191 {
192   char *buf;
193   size_t size;
194   
195   if (GNUNET_SYSERR ==
196       GNUNET_DNSPARSER_pack (request->packet,
197                              UINT16_MAX /* is this not too much? */,
198                              &buf,
199                              &size))
200     {
201       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
202                   _("Failed to pack DNS response into UDP packet!\n"));
203     }
204   else
205     {
206       if (size !=
207           GNUNET_NETWORK_socket_sendto (request->lsock,
208                                         buf, size,
209                                         request->addr,
210                                         request->addr_len))
211         GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "sendto");
212       GNUNET_free (buf);
213     }
214   GNUNET_SCHEDULER_cancel (request->timeout_task);
215   GNUNET_DNSPARSER_free_packet (request->packet);
216   GNUNET_free (request);
217 }
218
219
220 /**
221  * Task run on timeout.  Cleans up request.
222  *
223  * @param cls 'struct Request' of the request to clean up
224  * @param tc scheduler context
225  */
226 static void
227 do_timeout (void *cls,
228             const struct GNUNET_SCHEDULER_TaskContext *tc)
229 {
230   struct Request *request = cls;
231
232   if (NULL != request->packet)
233     GNUNET_DNSPARSER_free_packet (request->packet);
234   if (NULL != request->lookup)
235     GNUNET_GNS_lookup_cancel (request->lookup);
236   if (NULL != request->dns_lookup)
237     GNUNET_DNSSTUB_resolve_cancel (request->dns_lookup);
238   GNUNET_free (request);
239 }
240
241
242 /**
243  * Iterator called on obtained result for a DNS lookup
244  *
245  * @param cls closure
246  * @param rs the request socket
247  * @param dns the DNS udp payload
248  * @param r size of the DNS payload
249  */
250 static void
251 dns_result_processor (void *cls,
252                       struct GNUNET_DNSSTUB_RequestSocket *rs,
253                       const struct GNUNET_TUN_DnsHeader *dns,
254                       size_t r)
255 {
256   struct Request *request = cls;
257
258   request->packet = GNUNET_DNSPARSER_parse ((char*)dns, r);
259   send_response (request);
260 }
261
262
263 /**
264  * Iterator called on obtained result for a GNS
265  * lookup
266  *
267  * @param cls closure
268  * @param rd_count number of records
269  * @param rd the records in reply
270  */
271 static void
272 result_processor (void *cls,
273                   uint32_t rd_count,
274                   const struct GNUNET_NAMESTORE_RecordData *rd)
275 {
276   struct Request *request = cls;
277   struct GNUNET_DNSPARSER_Packet *packet;
278   uint32_t i;
279   struct GNUNET_DNSPARSER_Record rec;
280
281   request->lookup = NULL;
282   packet = request->packet;
283   packet->flags.query_or_response = 1;
284   packet->flags.return_code = GNUNET_DNSPARSER_RETURN_CODE_NO_ERROR;
285   packet->flags.checking_disabled = 0;
286   packet->flags.authenticated_data = 1;
287   packet->flags.zero = 0;
288   packet->flags.recursion_available = 1;
289   packet->flags.message_truncated = 0;
290   packet->flags.authoritative_answer = 0;
291   //packet->flags.opcode = GNUNET_DNSPARSER_OPCODE_STATUS; // ???
292   for (i=0;i<rd_count;i++)
293     {
294       rec.expiration_time.abs_value_us = rd[i].expiration_time;
295       switch (rd[i].record_type)
296         {
297         case GNUNET_DNSPARSER_TYPE_A:
298           GNUNET_assert (sizeof (struct in_addr) == rd[i].data_size);
299           rec.name = GNUNET_strdup (packet->queries[0].name);
300           rec.class = GNUNET_DNSPARSER_CLASS_INTERNET;
301           rec.type = GNUNET_DNSPARSER_TYPE_A;
302           rec.data.raw.data = GNUNET_malloc (sizeof (struct in_addr));
303           memcpy (rec.data.raw.data,
304                   rd[i].data,
305                   rd[i].data_size);
306           rec.data.raw.data_len = sizeof (struct in_addr);
307           GNUNET_array_append (packet->answers,
308                                packet->num_answers,
309                                rec);
310           break;
311         case GNUNET_DNSPARSER_TYPE_AAAA:
312           GNUNET_assert (sizeof (struct in6_addr) == rd[i].data_size);
313           rec.name = GNUNET_strdup (packet->queries[0].name);
314           rec.data.raw.data = GNUNET_malloc (sizeof (struct in6_addr));
315           rec.class = GNUNET_DNSPARSER_CLASS_INTERNET;
316           rec.type = GNUNET_DNSPARSER_TYPE_AAAA;
317           memcpy (rec.data.raw.data,
318                   rd[i].data,
319                   rd[i].data_size);
320           rec.data.raw.data_len = sizeof (struct in6_addr);
321           GNUNET_array_append (packet->answers,
322                                packet->num_answers,
323                                rec);
324           break;
325         case GNUNET_DNSPARSER_TYPE_CNAME:
326           rec.name = GNUNET_strdup (packet->queries[0].name);
327           rec.data.hostname = strdup (rd[i].data);
328           rec.class = GNUNET_DNSPARSER_CLASS_INTERNET;
329           rec.type = GNUNET_DNSPARSER_TYPE_CNAME;
330           memcpy (rec.data.hostname,
331                   rd[i].data,
332                   rd[i].data_size);
333           GNUNET_array_append (packet->answers,
334                                packet->num_answers,
335                                rec);
336           break;
337         default:
338           /* skip */
339           break;
340         }
341     }
342   send_response (request);
343 }
344
345
346 /**
347  * Handle DNS request.
348  *
349  * @param lsock socket to use for sending the reply
350  * @param addr address to use for sending the reply
351  * @param addr_len number of bytes in addr
352  * @param udp_msg DNS request payload
353  * @param udp_msg_size number of bytes in udp_msg 
354  */
355 static void
356 handle_request (struct GNUNET_NETWORK_Handle *lsock,
357                 const void *addr,
358                 size_t addr_len,
359                 const char *udp_msg,
360                 size_t udp_msg_size)
361 {
362   struct Request *request;
363   struct GNUNET_DNSPARSER_Packet *packet;
364   char *name;
365   char *dot;
366   char *nname;
367   size_t name_len;
368   int type;
369   int use_gns;
370   struct GNUNET_CRYPTO_ShortHashCode zone;
371
372   packet = GNUNET_DNSPARSER_parse (udp_msg, udp_msg_size);
373   if (NULL == packet)
374     {
375       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
376                   _("Cannot parse DNS request from %s\n"),
377                   GNUNET_a2s (addr, addr_len));
378       return;
379     }
380   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
381               "Received request for `%s' with flags %u, #answers %d, #auth %d, #additional %d\n",
382               packet->queries[0].name,
383               (unsigned int) packet->flags.query_or_response,
384               (int) packet->num_answers,
385               (int) packet->num_authority_records,
386               (int) packet->num_additional_records);
387   if ( (0 != packet->flags.query_or_response) || 
388        (0 != packet->num_answers) ||
389        (0 != packet->num_authority_records))
390     {
391       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
392                   _("Received malformed DNS request from %s\n"),
393                   GNUNET_a2s (addr, addr_len));
394       GNUNET_DNSPARSER_free_packet (packet);
395       return;
396     }
397   if ( (1 != packet->num_queries) )
398     {
399       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
400                   _("Received unsupported DNS request from %s\n"),
401                   GNUNET_a2s (addr, addr_len));
402       GNUNET_DNSPARSER_free_packet (packet);
403       return;
404     }
405   request = GNUNET_malloc (sizeof (struct Request) + addr_len);
406   request->lsock = lsock;
407   request->packet = packet;
408   request->addr = &request[1];
409   request->addr_len = addr_len;
410   memcpy (&request[1], addr, addr_len);
411   request->timeout_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT,
412                                                         &do_timeout,
413                                                         request);
414   name = GNUNET_strdup (packet->queries[0].name);
415   name_len = strlen (name);
416   use_gns = GNUNET_NO;
417   if ( (name_len > strlen (dns_suffix)) &&
418        (0 == strcasecmp (dns_suffix,
419                          &name[name_len - strlen (dns_suffix)])) )
420     {
421       /* Test if '.zkey' was requested */
422       name[name_len - strlen (dns_suffix)] = '\0';
423       dot = strrchr (name, (int) '.');
424       if ( (NULL != dot) &&
425            (GNUNET_OK ==
426             GNUNET_CRYPTO_short_hash_from_string (dot + 1, &zone)) )
427       {
428         /* valid '.zkey' name */
429         GNUNET_asprintf (&nname, 
430                          "%s.%s", 
431                          name, 
432                          GNUNET_GNS_TLD_ZKEY);
433         GNUNET_free (name);
434         name = nname;
435       }
436       else
437       { 
438         /* try '.gads' name */
439         GNUNET_asprintf (&nname, 
440                          "%s.%s", 
441                          name, 
442                          GNUNET_GNS_TLD);
443         GNUNET_free (name);
444         name = nname;
445       }
446       name_len = strlen (name);
447     }
448   if ( (name_len >= strlen ((GNUNET_GNS_TLD))) &&
449        (0 == strcasecmp (GNUNET_GNS_TLD,
450                          &name[name_len - strlen (GNUNET_GNS_TLD)])) )
451     use_gns = GNUNET_YES;
452
453   if ( (name_len > strlen (GNUNET_GNS_TLD_ZKEY)) &&
454        (0 == strcasecmp (GNUNET_GNS_TLD_ZKEY,
455                          &name[name_len - strlen (GNUNET_GNS_TLD_ZKEY)])) )
456     use_gns = GNUNET_YES;
457
458   if (GNUNET_YES == use_gns)
459   {
460       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
461                    "Calling GNS\n");
462       type = packet->queries[0].type;
463       request->lookup = GNUNET_GNS_lookup (gns,
464                                            name,
465                                            &my_zone,
466                                            type,
467                                            GNUNET_NO,
468                                            NULL /* no shorten */,
469                                            &result_processor,
470                                            request);
471     }
472   else
473     {
474       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
475                    "Calling DNS at %s\n", dns_ip);
476       GNUNET_DNSPARSER_free_packet (request->packet);
477       request->packet = NULL;
478       request->dns_lookup = GNUNET_DNSSTUB_resolve2 (dns_stub,
479                                                      udp_msg,
480                                                      udp_msg_size,
481                                                      &dns_result_processor,
482                                                      request);
483     }
484   GNUNET_free (name);
485 }
486
487
488 /**
489  * Task to read IPv4 DNS packets.
490  *
491  * @param cls the 'listen_socket4'
492  * @param tc scheduler context
493  */ 
494 static void
495 read_dns4 (void *cls,
496            const struct GNUNET_SCHEDULER_TaskContext *tc)
497 {
498   struct sockaddr_in v4;
499   socklen_t addrlen;
500   ssize_t size;
501
502   GNUNET_assert (listen_socket4 == cls);
503   t4 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
504                                       listen_socket4,
505                                       &read_dns4,
506                                       listen_socket4);
507   if (0 == (GNUNET_SCHEDULER_REASON_READ_READY & tc->reason))
508     return; /* shutdown? */
509   size = GNUNET_NETWORK_socket_recvfrom_amount (listen_socket4);
510   if (0 > size)
511     {
512       GNUNET_break (0);
513       return; /* read error!? */
514     }
515   {
516     char buf[size];
517     
518     addrlen = sizeof (v4);
519     GNUNET_break (size == 
520                   GNUNET_NETWORK_socket_recvfrom (listen_socket4,
521                                                   buf,
522                                                   size,
523                                                   (struct sockaddr *) &v4,
524                                                   &addrlen));
525     handle_request (listen_socket4, &v4, addrlen,
526                     buf, size);
527   }
528 }
529
530
531 /**
532  * Task to read IPv6 DNS packets.
533  *
534  * @param cls the 'listen_socket6'
535  * @param tc scheduler context
536  */ 
537 static void
538 read_dns6 (void *cls,
539            const struct GNUNET_SCHEDULER_TaskContext *tc)
540 {
541   struct sockaddr_in6 v6;
542   socklen_t addrlen;
543   ssize_t size;
544
545   GNUNET_assert (listen_socket6 == cls);
546   t6 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
547                                       listen_socket6,
548                                       &read_dns6,
549                                       listen_socket6);
550   if (0 == (GNUNET_SCHEDULER_REASON_READ_READY & tc->reason))
551     return; /* shutdown? */
552   size = GNUNET_NETWORK_socket_recvfrom_amount (listen_socket6);
553   if (0 > size)
554     {
555       GNUNET_break (0);
556       return; /* read error!? */
557     }
558   {
559     char buf[size];
560     
561     addrlen = sizeof (v6);
562     GNUNET_break (size == 
563                   GNUNET_NETWORK_socket_recvfrom (listen_socket6,
564                                                   buf,
565                                                   size,
566                                                   (struct sockaddr *) &v6,
567                                                   &addrlen));
568     handle_request (listen_socket6, &v6, addrlen,
569                     buf, size);
570   }
571 }
572
573
574 /**
575  * Main function that will be run.
576  *
577  * @param cls closure
578  * @param args remaining command-line arguments
579  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
580  * @param cfg configuration
581  */
582 static void
583 run (void *cls, char *const *args, const char *cfgfile,
584      const struct GNUNET_CONFIGURATION_Handle *cfg)
585 {
586   if (NULL == dns_ip)
587   {
588     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
589                 _("No DNS server specified!\n"));
590     return;
591   }
592   if ( (NULL == gns_zone_str) ||
593        (GNUNET_OK !=
594         GNUNET_CRYPTO_ecc_public_key_from_string (gns_zone_str,
595                                                   strlen (gns_zone_str),
596                                                   &my_zone)) )
597   {
598     GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 
599                 _("No valid GNS zone specified!\n"));
600     return;
601   }
602
603   if (NULL == dns_suffix)
604     dns_suffix = DNS_SUFFIX;
605   if (NULL == fcfs_suffix)
606     fcfs_suffix = FCFS_SUFFIX;
607   if (NULL == (gns = GNUNET_GNS_connect (cfg)))
608     return;
609   if (NULL == (dns_stub = GNUNET_DNSSTUB_start (dns_ip)))
610   {
611     GNUNET_GNS_disconnect (gns);
612     gns = NULL;
613     return;
614   }
615   listen_socket4 = GNUNET_NETWORK_socket_create (PF_INET,
616                                                  SOCK_DGRAM, 
617                                                  IPPROTO_UDP);
618   if (NULL != listen_socket4)
619     {
620       struct sockaddr_in v4;
621
622       memset (&v4, 0, sizeof (v4));
623       v4.sin_family = AF_INET;
624 #if HAVE_SOCKADDR_IN_SIN_LEN
625       v4.sin_len = sizeof (v4);
626 #endif
627       v4.sin_port = htons (listen_port);
628       if (GNUNET_OK !=
629           GNUNET_NETWORK_socket_bind (listen_socket4,
630                                       (struct sockaddr *) &v4,
631                                       sizeof (v4),
632                                       0))
633         {
634           GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "bind");
635           GNUNET_NETWORK_socket_close (listen_socket4);
636           listen_socket4 = NULL;
637         }
638     }
639   listen_socket6 = GNUNET_NETWORK_socket_create (PF_INET6,
640                                                 SOCK_DGRAM, 
641                                                 IPPROTO_UDP);
642   if (NULL != listen_socket6)
643     {
644       struct sockaddr_in6 v6;
645
646       memset (&v6, 0, sizeof (v6));
647       v6.sin6_family = AF_INET6;
648 #if HAVE_SOCKADDR_IN_SIN_LEN
649       v6.sin6_len = sizeof (v6);
650 #endif
651       v6.sin6_port = htons (listen_port);
652       if (GNUNET_OK !=
653           GNUNET_NETWORK_socket_bind (listen_socket6,
654                                       (struct sockaddr *) &v6,
655                                       sizeof (v6),
656                                       0))
657         {
658           GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "bind");
659           GNUNET_NETWORK_socket_close (listen_socket6);
660           listen_socket6 = NULL;
661         }
662     }
663   if ( (NULL == listen_socket4) &&
664        (NULL == listen_socket6) )
665     {
666       GNUNET_GNS_disconnect (gns);
667       gns = NULL;
668       GNUNET_DNSSTUB_stop (dns_stub);
669       dns_stub = NULL;
670       return;
671     }
672   if (NULL != listen_socket4)
673     t4 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
674                                         listen_socket4,
675                                         &read_dns4,
676                                         listen_socket4);
677   if (NULL != listen_socket6)
678     t6 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
679                                         listen_socket6,
680                                         &read_dns6,
681                                         listen_socket6);
682
683   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
684                                 &do_shutdown, NULL);
685 }
686
687
688 /**
689  * The main function for the fcfs daemon.
690  *
691  * @param argc number of arguments from the command line
692  * @param argv command line arguments
693  * @return 0 ok, 1 on error
694  */
695 int
696 main (int argc, 
697       char *const *argv)
698 {
699   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
700     {'d', "dns", "IP",
701       gettext_noop ("IP of recursive DNS resolver to use (required)"), 1,
702       &GNUNET_GETOPT_set_string, &dns_ip},
703     {'f', "fcfs", "NAME",
704       gettext_noop ("Authoritative FCFS suffix to use (optional); default: fcfs.zkey.eu"), 1,
705       &GNUNET_GETOPT_set_string, &fcfs_suffix},
706     {'s', "suffix", "SUFFIX",
707       gettext_noop ("Authoritative DNS suffix to use (optional); default: zkey.eu"), 1,
708       &GNUNET_GETOPT_set_string, &dns_suffix},
709     {'p', "port", "UDPPORT",
710       gettext_noop ("UDP port to listen on for inbound DNS requests; default: 53"), 1,
711       &GNUNET_GETOPT_set_uint, &listen_port},
712     {'z', "zone", "PUBLICKEY",
713       gettext_noop ("Public key of the GNS zone to use (required)"), 1,
714       &GNUNET_GETOPT_set_string, &gns_zone_str},
715     GNUNET_GETOPT_OPTION_END
716   };
717   int ret;
718
719   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv,
720                                                  &argc, &argv))
721     return 2;
722   GNUNET_log_setup ("gnunet-dns2gns", "WARNING", NULL);
723   ret =
724       (GNUNET_OK ==
725        GNUNET_PROGRAM_run (argc, argv, "gnunet-dns2gns",
726                            _("GNUnet DNS-to-GNS proxy (a DNS server)"), 
727                            options,
728                            &run, NULL)) ? 0 : 1;
729   GNUNET_free ((void*) argv);
730   return ret;
731 }
732
733 /* end of gnunet-dns2gns.c */