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