add missing files
[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
358     case GNUNET_DNSPARSER_TYPE_AAAA:
359       GNUNET_assert (sizeof(struct in6_addr) == rd[i].data_size);
360       rec.name = GNUNET_strdup (packet->queries[0].name);
361       rec.data.raw.data = GNUNET_new (struct in6_addr);
362       rec.dns_traffic_class = GNUNET_TUN_DNS_CLASS_INTERNET;
363       rec.type = GNUNET_DNSPARSER_TYPE_AAAA;
364       GNUNET_memcpy (rec.data.raw.data,
365                      rd[i].data,
366                      rd[i].data_size);
367       rec.data.raw.data_len = sizeof(struct in6_addr);
368       GNUNET_array_append (packet->answers,
369                            packet->num_answers,
370                            rec);
371       break;
372
373     case GNUNET_DNSPARSER_TYPE_CNAME:
374       rec.name = GNUNET_strdup (packet->queries[0].name);
375       rec.data.hostname = GNUNET_strdup (rd[i].data);
376       rec.dns_traffic_class = GNUNET_TUN_DNS_CLASS_INTERNET;
377       rec.type = GNUNET_DNSPARSER_TYPE_CNAME;
378       GNUNET_memcpy (rec.data.hostname,
379                      rd[i].data,
380                      rd[i].data_size);
381       GNUNET_array_append (packet->answers,
382                            packet->num_answers,
383                            rec);
384       break;
385
386     default:
387       /* skip */
388       break;
389     }
390   }
391   send_response (request);
392 }
393
394
395 /**
396  * Handle DNS request.
397  *
398  * @param lsock socket to use for sending the reply
399  * @param addr address to use for sending the reply
400  * @param addr_len number of bytes in @a addr
401  * @param udp_msg DNS request payload
402  * @param udp_msg_size number of bytes in @a udp_msg
403  */
404 static void
405 handle_request (struct GNUNET_NETWORK_Handle *lsock,
406                 const void *addr,
407                 size_t addr_len,
408                 const char *udp_msg,
409                 size_t udp_msg_size)
410 {
411   struct Request *request;
412   struct GNUNET_DNSPARSER_Packet *packet;
413
414   packet = GNUNET_DNSPARSER_parse (udp_msg,
415                                    udp_msg_size);
416   if (NULL == packet)
417   {
418     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
419                 _ ("Cannot parse DNS request from %s\n"),
420                 GNUNET_a2s (addr, addr_len));
421     return;
422   }
423   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
424               "Received request for `%s' with flags %u, #answers %d, #auth %d, #additional %d\n",
425               packet->queries[0].name,
426               (unsigned int) packet->flags.query_or_response,
427               (int) packet->num_answers,
428               (int) packet->num_authority_records,
429               (int) packet->num_additional_records);
430   if ((0 != packet->flags.query_or_response) ||
431       (0 != packet->num_answers) ||
432       (0 != packet->num_authority_records))
433   {
434     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
435                 _ ("Received malformed DNS request from %s\n"),
436                 GNUNET_a2s (addr, addr_len));
437     GNUNET_DNSPARSER_free_packet (packet);
438     return;
439   }
440   if ((1 != packet->num_queries))
441   {
442     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
443                 _ ("Received unsupported DNS request from %s\n"),
444                 GNUNET_a2s (addr,
445                             addr_len));
446     GNUNET_DNSPARSER_free_packet (packet);
447     return;
448   }
449   request = GNUNET_malloc (sizeof(struct Request) + addr_len);
450   request->lsock = lsock;
451   request->packet = packet;
452   request->addr = &request[1];
453   request->addr_len = addr_len;
454   GNUNET_memcpy (&request[1],
455                  addr,
456                  addr_len);
457   request->udp_msg_size = udp_msg_size;
458   request->udp_msg = GNUNET_memdup (udp_msg,
459                                     udp_msg_size);
460   request->timeout_task = GNUNET_SCHEDULER_add_delayed (TIMEOUT,
461                                                         &do_timeout,
462                                                         request);
463   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
464               "Calling GNS on `%s'\n",
465               packet->queries[0].name);
466   request->lookup = GNUNET_GNS_lookup_with_tld (gns,
467                                                 packet->queries[0].name,
468                                                 packet->queries[0].type,
469                                                 GNUNET_NO,
470                                                 &result_processor,
471                                                 request);
472 }
473
474
475 /**
476  * Task to read IPv4 DNS packets.
477  *
478  * @param cls the 'listen_socket4'
479  */
480 static void
481 read_dns4 (void *cls)
482 {
483   struct sockaddr_in v4;
484   socklen_t addrlen;
485   ssize_t size;
486   const struct GNUNET_SCHEDULER_TaskContext *tc;
487
488   GNUNET_assert (listen_socket4 == cls);
489   t4 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
490                                       listen_socket4,
491                                       &read_dns4,
492                                       listen_socket4);
493   tc = GNUNET_SCHEDULER_get_task_context ();
494   if (0 == (GNUNET_SCHEDULER_REASON_READ_READY & tc->reason))
495     return; /* shutdown? */
496   size = GNUNET_NETWORK_socket_recvfrom_amount (listen_socket4);
497   if (0 > size)
498   {
499     GNUNET_break (0);
500     return;   /* read error!? */
501   }
502   {
503     char buf[size + 1];
504     ssize_t sret;
505
506     addrlen = sizeof(v4);
507     sret = GNUNET_NETWORK_socket_recvfrom (listen_socket4,
508                                            buf,
509                                            size + 1,
510                                            (struct sockaddr *) &v4,
511                                            &addrlen);
512     if (0 > sret)
513     {
514       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
515                            "recvfrom");
516       return;
517     }
518     GNUNET_break (size == sret);
519     handle_request (listen_socket4,
520                     &v4,
521                     addrlen,
522                     buf,
523                     size);
524   }
525 }
526
527
528 /**
529  * Task to read IPv6 DNS packets.
530  *
531  * @param cls the 'listen_socket6'
532  */
533 static void
534 read_dns6 (void *cls)
535 {
536   struct sockaddr_in6 v6;
537   socklen_t addrlen;
538   ssize_t size;
539   const struct GNUNET_SCHEDULER_TaskContext *tc;
540
541   GNUNET_assert (listen_socket6 == cls);
542   t6 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
543                                       listen_socket6,
544                                       &read_dns6,
545                                       listen_socket6);
546   tc = GNUNET_SCHEDULER_get_task_context ();
547   if (0 == (GNUNET_SCHEDULER_REASON_READ_READY & tc->reason))
548     return; /* shutdown? */
549   size = GNUNET_NETWORK_socket_recvfrom_amount (listen_socket6);
550   if (0 > size)
551   {
552     GNUNET_break (0);
553     return;   /* read error!? */
554   }
555   {
556     char buf[size];
557     ssize_t sret;
558
559     addrlen = sizeof(v6);
560     sret = GNUNET_NETWORK_socket_recvfrom (listen_socket6,
561                                            buf,
562                                            size,
563                                            (struct sockaddr *) &v6,
564                                            &addrlen);
565     if (0 > sret)
566     {
567       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
568                            "recvfrom");
569       return;
570     }
571     GNUNET_break (size == sret);
572     handle_request (listen_socket6,
573                     &v6,
574                     addrlen,
575                     buf,
576                     size);
577   }
578 }
579
580
581 /**
582  * Main function that will be run.
583  *
584  * @param cls closure
585  * @param args remaining command-line arguments
586  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
587  * @param c configuration
588  */
589 static void
590 run (void *cls,
591      char *const *args,
592      const char *cfgfile,
593      const struct GNUNET_CONFIGURATION_Handle *c)
594 {
595   char *addr_str;
596
597   (void) cls;
598   (void) args;
599   (void) cfgfile;
600   cfg = c;
601   if (NULL == dns_ip)
602   {
603     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
604                 _ ("No DNS server specified!\n"));
605     return;
606   }
607   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
608                                  NULL);
609   if (NULL == (gns = GNUNET_GNS_connect (cfg)))
610     return;
611   GNUNET_assert (NULL != (dns_stub = GNUNET_DNSSTUB_start (128)));
612   if (GNUNET_OK !=
613       GNUNET_DNSSTUB_add_dns_ip (dns_stub,
614                                  dns_ip))
615   {
616     GNUNET_DNSSTUB_stop (dns_stub);
617     GNUNET_GNS_disconnect (gns);
618     gns = NULL;
619     return;
620   }
621
622   /* Get address to bind to */
623   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (c, "dns2gns",
624                                                           "BIND_TO",
625                                                           &addr_str))
626   {
627     // No address specified
628     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
629                 "Don't know what to bind to...\n");
630     GNUNET_free (addr_str);
631     GNUNET_SCHEDULER_shutdown ();
632     return;
633   }
634   if (1 != inet_pton (AF_INET, addr_str, &address))
635   {
636     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
637                 "Unable to parse address %s\n",
638                 addr_str);
639     GNUNET_free (addr_str);
640     GNUNET_SCHEDULER_shutdown ();
641     return;
642   }
643   GNUNET_free (addr_str);
644   /* Get address to bind to */
645   if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (c, "dns2gns",
646                                                           "BIND_TO6",
647                                                           &addr_str))
648   {
649     // No address specified
650     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
651                 "Don't know what to bind6 to...\n");
652     GNUNET_free (addr_str);
653     GNUNET_SCHEDULER_shutdown ();
654     return;
655   }
656   if (1 != inet_pton (AF_INET6, addr_str, &address6))
657   {
658     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
659                 "Unable to parse IPv6 address %s\n",
660                 addr_str);
661     GNUNET_free (addr_str);
662     GNUNET_SCHEDULER_shutdown ();
663     return;
664   }
665   GNUNET_free (addr_str);
666
667   listen_socket4 = GNUNET_NETWORK_socket_create (PF_INET,
668                                                  SOCK_DGRAM,
669                                                  IPPROTO_UDP);
670   if (NULL != listen_socket4)
671   {
672     struct sockaddr_in v4;
673
674     memset (&v4, 0, sizeof(v4));
675     v4.sin_family = AF_INET;
676     v4.sin_addr.s_addr = address;
677 #if HAVE_SOCKADDR_IN_SIN_LEN
678     v4.sin_len = sizeof(v4);
679 #endif
680     v4.sin_port = htons (listen_port);
681     if (GNUNET_OK !=
682         GNUNET_NETWORK_socket_bind (listen_socket4,
683                                     (struct sockaddr *) &v4,
684                                     sizeof(v4)))
685     {
686       GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "bind");
687       GNUNET_NETWORK_socket_close (listen_socket4);
688       listen_socket4 = NULL;
689     }
690   }
691   listen_socket6 = GNUNET_NETWORK_socket_create (PF_INET6,
692                                                  SOCK_DGRAM,
693                                                  IPPROTO_UDP);
694   if (NULL != listen_socket6)
695   {
696     struct sockaddr_in6 v6;
697
698     memset (&v6, 0, sizeof(v6));
699     v6.sin6_family = AF_INET6;
700     v6.sin6_addr = address6;
701 #if HAVE_SOCKADDR_IN_SIN_LEN
702     v6.sin6_len = sizeof(v6);
703 #endif
704     v6.sin6_port = htons (listen_port);
705     if (GNUNET_OK !=
706         GNUNET_NETWORK_socket_bind (listen_socket6,
707                                     (struct sockaddr *) &v6,
708                                     sizeof(v6)))
709     {
710       GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "bind");
711       GNUNET_NETWORK_socket_close (listen_socket6);
712       listen_socket6 = NULL;
713     }
714   }
715   if ((NULL == listen_socket4) &&
716       (NULL == listen_socket6))
717   {
718     GNUNET_GNS_disconnect (gns);
719     gns = NULL;
720     GNUNET_DNSSTUB_stop (dns_stub);
721     dns_stub = NULL;
722     return;
723   }
724   if (NULL != listen_socket4)
725     t4 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
726                                         listen_socket4,
727                                         &read_dns4,
728                                         listen_socket4);
729   if (NULL != listen_socket6)
730     t6 = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
731                                         listen_socket6,
732                                         &read_dns6,
733                                         listen_socket6);
734 }
735
736
737 /**
738  * The main function for the dns2gns daemon.
739  *
740  * @param argc number of arguments from the command line
741  * @param argv command line arguments
742  * @return 0 ok, 1 on error
743  */
744 int
745 main (int argc,
746       char *const *argv)
747 {
748   struct GNUNET_GETOPT_CommandLineOption options[] = {
749     GNUNET_GETOPT_option_string ('d',
750                                  "dns",
751                                  "IP",
752                                  gettext_noop (
753                                    "IP of recursive DNS resolver to use (required)"),
754                                  &dns_ip),
755     GNUNET_GETOPT_option_uint ('p',
756                                "port",
757                                "UDPPORT",
758                                gettext_noop (
759                                  "UDP port to listen on for inbound DNS requests; default: 2853"),
760                                &listen_port),
761     GNUNET_GETOPT_OPTION_END
762   };
763   int ret;
764
765   if (GNUNET_OK !=
766       GNUNET_STRINGS_get_utf8_args (argc, argv,
767                                     &argc, &argv))
768     return 2;
769   GNUNET_log_setup ("gnunet-dns2gns",
770                     "WARNING",
771                     NULL);
772   ret =
773     (GNUNET_OK ==
774      GNUNET_PROGRAM_run (argc, argv,
775                          "gnunet-dns2gns",
776                          _ ("GNUnet DNS-to-GNS proxy (a DNS server)"),
777                          options,
778                          &run, NULL)) ? 0 : 1;
779   GNUNET_free ((void*) argv);
780   return ret;
781 }
782
783 /* end of gnunet-dns2gns.c */