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