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