b2c8fda666cbd4b6ebb0a86f91518aa72bdd127a
[oweals/gnunet.git] / src / util / resolver_api.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009 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 2, 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 /**
22  * @file util/resolver_api.c
23  * @brief resolver for writing a tool
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet_getopt_lib.h"
28 #include "gnunet_client_lib.h"
29 #include "gnunet_protocols.h"
30 #include "gnunet_resolver_service.h"
31 #include "gnunet_server_lib.h"
32 #include "resolver.h"
33
34
35 /**
36  * FIXME.
37  */
38 struct GetAddressContext
39 {
40
41   /**
42    * FIXME.
43    */
44   GNUNET_RESOLVER_AddressCallback callback;
45
46   /**
47    * Closure for "callback".
48    */
49   void *cls;
50
51   /**
52    * FIXME.
53    */
54   struct GNUNET_RESOLVER_GetMessage *msg;
55
56   /**
57    * FIXME.
58    */
59   struct GNUNET_CLIENT_Connection *client;
60
61   /**
62    * FIXME.
63    */
64   struct GNUNET_TIME_Absolute timeout;
65 };
66
67
68 /**
69  * Possible hostnames for "loopback".
70  */
71 static const char *loopback[] = {
72   "localhost",
73   "ip6-localnet",
74   NULL
75 };
76
77
78 /**
79  * Check that the resolver service runs on localhost
80  * (or equivalent).
81  */
82 static void
83 check_config (const struct GNUNET_CONFIGURATION_Handle *cfg)
84 {
85   char *hostname;
86   unsigned int i;
87   struct in_addr v4;
88   struct in6_addr v6;
89
90   if (GNUNET_OK !=
91       GNUNET_CONFIGURATION_get_value_string (cfg,
92                                              "resolver",
93                                              "HOSTNAME",
94                                              &hostname))
95     {
96       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
97                   _("Must specify `%s' for `%s' in configuration!\n"),
98                   "HOSTNAME",
99                   "resolver");
100       GNUNET_assert (0);
101     }
102   if ( (0 == inet_pton (AF_INET,
103                         hostname,
104                         &v4)) ||
105        (0 == inet_pton (AF_INET6,
106                         hostname,
107                         &v6)) )
108     return;
109   i = 0;
110   while (loopback[i] != NULL)
111     if (0 == strcmp (loopback[i++], hostname))
112       {
113         GNUNET_free (hostname); 
114         return;
115       }
116   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
117               _("Must specify `%s' for `%s' in configuration!\n"),
118               "localhost",
119               "resolver");
120   GNUNET_free (hostname); 
121   GNUNET_assert (0); 
122 }
123
124
125 /**
126  * Convert IP address to string without DNS resolution.
127  *
128  * @param sa the address 
129  * @param salen number of bytes in sa
130  * @return address as a string, NULL on error
131  */
132 static char *
133 no_resolve (const struct sockaddr *sa, socklen_t salen)
134 {
135   char *ret;
136   char inet4[INET_ADDRSTRLEN];
137   char inet6[INET6_ADDRSTRLEN];
138
139   if (salen < sizeof (struct sockaddr))
140     return NULL;
141   switch (sa->sa_family)
142     {
143     case AF_INET:
144       if (salen != sizeof (struct sockaddr_in))
145         return NULL;
146       inet_ntop (AF_INET,
147                  &((struct sockaddr_in *) sa)->sin_addr,
148                  inet4, INET_ADDRSTRLEN);
149       ret = GNUNET_strdup (inet4);
150       break;
151     case AF_INET6:
152       if (salen != sizeof (struct sockaddr_in6))
153         return NULL;
154       inet_ntop (AF_INET6,
155                  &((struct sockaddr_in6 *) sa)->sin6_addr,
156                  inet6, INET6_ADDRSTRLEN);
157       ret = GNUNET_strdup (inet6);
158       break;
159     default:
160       ret = NULL;
161       break;
162     }
163   return ret;
164 }
165
166
167 /**
168  * FIXME
169  *
170  * @param cls FIXME
171  * @param msg FIXME
172  */
173 static void
174 handle_address_response (void *cls, const struct GNUNET_MessageHeader *msg)
175 {
176   struct GetAddressContext *gac = cls;
177   uint16_t size;
178   const struct sockaddr *sa;
179   socklen_t salen;
180
181
182   if (msg == NULL)
183     {
184       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
185                   _("Timeout trying to resolve hostname.\n"));
186       gac->callback (gac->cls, NULL, 0);
187       GNUNET_CLIENT_disconnect (gac->client);
188       GNUNET_free (gac);
189       return;
190     }
191   if (GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE != ntohs (msg->type))
192     {
193       GNUNET_break (0);
194       gac->callback (gac->cls, NULL, 0);
195       GNUNET_CLIENT_disconnect (gac->client);
196       GNUNET_free (gac);
197       return;
198     }
199
200   size = ntohs (msg->size);
201   if (size == sizeof (struct GNUNET_MessageHeader))
202     {
203 #if DEBUG_RESOLVER
204       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
205                   _("Received end message resolving hostname.\n"));
206 #endif
207       gac->callback (gac->cls, NULL, 0);
208       GNUNET_CLIENT_disconnect (gac->client);
209       GNUNET_free (gac);
210       return;
211     }
212   sa = (const struct sockaddr *) &msg[1];
213   salen = size - sizeof (struct GNUNET_MessageHeader);
214   if (salen < sizeof (struct sockaddr))
215     {
216       GNUNET_break (0);
217       gac->callback (gac->cls, NULL, 0);
218       GNUNET_CLIENT_disconnect (gac->client);
219       GNUNET_free (gac);
220       return;
221     }
222 #if DEBUG_RESOLVER
223   {
224     char *ips = no_resolve (sa, salen);
225     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Resolver returns `%s'.\n"), ips);
226     GNUNET_free (ips);
227   }
228 #endif
229   gac->callback (gac->cls, sa, salen);
230   GNUNET_CLIENT_receive (gac->client,
231                          &handle_address_response,
232                          gac,
233                          GNUNET_TIME_absolute_get_remaining (gac->timeout));
234 }
235
236
237 /**
238  * FIXME
239  *
240  * @param cls FIXME
241  * @param size number of bytes available in buf
242  * @param buf target buffer, NULL on error
243  * @return number of bytes written to buf
244  */
245 static size_t
246 transmit_get_ip (void *cls, size_t size, void *buf)
247 {
248   struct GetAddressContext *actx = cls;
249   uint16_t ms;
250
251   if (buf == NULL)
252     {
253       /* timeout / error */
254       GNUNET_free (actx->msg);
255       actx->callback (actx->cls, NULL, 0);
256       GNUNET_CLIENT_disconnect (actx->client);
257       GNUNET_free (actx);
258       return 0;
259     }
260   ms = ntohs (actx->msg->header.size);
261   GNUNET_assert (size >= ms);
262   memcpy (buf, actx->msg, ms);
263   GNUNET_free (actx->msg);
264   actx->msg = NULL;
265   GNUNET_CLIENT_receive (actx->client,
266                          &handle_address_response,
267                          actx,
268                          GNUNET_TIME_absolute_get_remaining (actx->timeout));
269   return ms;
270 }
271
272
273
274 /**
275  * Convert a string to one or more IP addresses.
276  *
277  * @param sched scheduler to use
278  * @param cfg configuration to use
279  * @param hostname the hostname to resolve
280  * @param domain AF_INET or AF_INET6; use AF_UNSPEC for "any"
281  * @param callback function to call with addresses
282  * @param callback_cls closure for callback
283  * @param timeout how long to try resolving
284  */
285 void
286 GNUNET_RESOLVER_ip_get (struct GNUNET_SCHEDULER_Handle *sched,
287                         const struct GNUNET_CONFIGURATION_Handle *cfg,
288                         const char *hostname,
289                         int domain,
290                         struct GNUNET_TIME_Relative timeout,
291                         GNUNET_RESOLVER_AddressCallback callback, 
292                         void *callback_cls)
293 {
294   struct GNUNET_CLIENT_Connection *client;
295   struct GNUNET_RESOLVER_GetMessage *msg;
296   struct GetAddressContext *actx;
297   size_t slen;
298   unsigned int i;
299   struct sockaddr_in v4;
300   struct sockaddr_in6 v6;
301
302   memset (&v4, 0, sizeof(v4));
303   v4.sin_addr.s_addr = htonl (INADDR_LOOPBACK);  
304   v4.sin_family = AF_INET;
305 #if HAVE_SOCKADDR_IN_SIN_LEN
306   v4.sin_len = sizeof(v4);
307 #endif
308   memset (&v6, 0, sizeof(v6)); 
309   v6.sin6_family = AF_INET6;
310 #if HAVE_SOCKADDR_IN_SIN_LEN
311   v6.sin6_len = sizeof(v6);
312 #endif
313   /* first, check if this is a numeric address */
314   if ( ( (domain == AF_UNSPEC) || (domain == AF_INET) ) && 
315        (0 == inet_pton (AF_INET,
316                         hostname,
317                         &v4.sin_addr)) )
318     {
319       callback (callback_cls,
320                 (const struct sockaddr*) &v4,
321                 sizeof(v4));
322       if ( (domain == AF_UNSPEC) && 
323            (0 == inet_pton (AF_INET6,
324                             hostname,
325                             &v6.sin6_addr)) )
326         {
327           /* this can happen on some systems IF "hostname" is "localhost" */
328           callback (callback_cls,
329                     (const struct sockaddr*) &v6,
330                     sizeof(v6));
331         }
332       callback (callback_cls, NULL, 0);      
333       return;
334     }
335   if ( ( (domain == AF_UNSPEC) ||(domain == AF_INET) ) && 
336        (0 == inet_pton (AF_INET6,
337                         hostname,
338                         &v6.sin6_addr)) )
339     {
340       callback (callback_cls,
341                 (const struct sockaddr*) &v6,
342                 sizeof(v6));
343       callback (callback_cls, NULL, 0);
344       return;
345     }
346   check_config (cfg);
347   /* then, check if this is a loopback address */
348   i = 0;
349   while (loopback[i] != NULL)
350     if (0 == strcmp (loopback[i++], hostname))
351       {
352         v4.sin_addr.s_addr = htonl (INADDR_LOOPBACK);  
353         v6.sin6_addr = in6addr_loopback;
354         switch (domain)
355           {
356           case AF_INET:
357             callback (callback_cls, 
358                       (const struct sockaddr*) &v4,
359                       sizeof(v4));
360             break;
361           case AF_INET6:
362             callback (callback_cls, 
363                       (const struct sockaddr*) &v6,
364                       sizeof(v6));
365             break;
366           case AF_UNSPEC:
367             callback (callback_cls, 
368                       (const struct sockaddr*) &v6,
369                       sizeof(v6));
370             callback (callback_cls, 
371                       (const struct sockaddr*) &v4,
372                       sizeof(v4));
373             break;
374           }
375         callback (callback_cls, NULL, 0);
376         return;
377       }
378   slen = strlen (hostname) + 1;
379   if (slen + sizeof (struct GNUNET_RESOLVER_GetMessage) >
380       GNUNET_SERVER_MAX_MESSAGE_SIZE)
381     {
382       GNUNET_break (0);
383       callback (callback_cls, NULL, 0);
384       return;
385     }
386   client = GNUNET_CLIENT_connect (sched, "resolver", cfg);
387   if (client == NULL)
388     {
389       callback (callback_cls, NULL, 0);
390       return;
391     }
392   msg = GNUNET_malloc (sizeof (struct GNUNET_RESOLVER_GetMessage) + slen);
393   msg->header.size =
394     htons (sizeof (struct GNUNET_RESOLVER_GetMessage) + slen);
395   msg->header.type = htons (GNUNET_MESSAGE_TYPE_RESOLVER_REQUEST);
396   msg->direction = htonl (GNUNET_NO);
397   msg->domain = htonl (domain);
398   memcpy (&msg[1], hostname, slen);
399   actx = GNUNET_malloc (sizeof (struct GetAddressContext));
400   actx->callback = callback;
401   actx->cls = callback_cls;
402   actx->client = client;
403   actx->timeout = GNUNET_TIME_relative_to_absolute (timeout);
404   actx->msg = msg;
405
406 #if DEBUG_RESOLVER
407   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
408               _("Resolver requests DNS resolution of hostname `%s'.\n"),
409               hostname);
410 #endif
411   if (NULL ==
412       GNUNET_CLIENT_notify_transmit_ready (client,
413                                            slen +
414                                            sizeof (struct
415                                                    GNUNET_RESOLVER_GetMessage),
416                                            timeout, &transmit_get_ip, actx))
417     {
418       GNUNET_free (msg);
419       GNUNET_free (actx);
420       callback (callback_cls, NULL, 0);
421       GNUNET_CLIENT_disconnect (client);
422       return;
423     }
424 }
425
426
427 /**
428  * FIXME.
429  */
430 struct GetHostnameContext
431 {
432
433   /**
434    * FIXME.
435    */
436   GNUNET_RESOLVER_HostnameCallback callback;
437   
438   /**
439    * FIXME.
440    */
441   void *cls;
442   
443   /**
444    * FIXME.
445    */ 
446   struct GNUNET_RESOLVER_GetMessage *msg;
447   
448   /**
449    * FIXME.
450    */  
451   struct GNUNET_CLIENT_Connection *client;
452   
453   /**
454    * FIXME.
455    */ 
456   struct GNUNET_TIME_Absolute timeout;
457 };
458
459
460 /**
461  * FIXME.
462  *
463  * @param cls FIXME
464  * @param msg FIXME
465  */
466 static void
467 handle_hostname_response (void *cls, const struct GNUNET_MessageHeader *msg)
468 {
469   struct GetHostnameContext *ghc = cls;
470   uint16_t size;
471   const char *hostname;
472
473   if (msg == NULL)
474     {
475       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
476                   _("Timeout trying to resolve IP address.\n"));
477       ghc->callback (ghc->cls, NULL);
478       GNUNET_CLIENT_disconnect (ghc->client);
479       GNUNET_free (ghc);
480       return;
481     }
482   size = ntohs (msg->size);
483   if (size == sizeof (struct GNUNET_MessageHeader))
484     {
485 #if DEBUG_RESOLVER
486       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
487                   _("Received end message resolving IP address.\n"));
488 #endif
489       ghc->callback (ghc->cls, NULL);
490       GNUNET_CLIENT_disconnect (ghc->client);
491       GNUNET_free (ghc);
492       return;
493     }
494   hostname = (const char *) &msg[1];
495   if (hostname[size - sizeof (struct GNUNET_MessageHeader) - 1] != '\0')
496     {
497       GNUNET_break (0);
498       ghc->callback (ghc->cls, NULL);
499       GNUNET_CLIENT_disconnect (ghc->client);
500       GNUNET_free (ghc);
501       return;
502     }
503 #if DEBUG_RESOLVER
504   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
505               _("Resolver returns `%s'.\n"), hostname);
506 #endif
507   ghc->callback (ghc->cls, hostname);
508   GNUNET_CLIENT_receive (ghc->client,
509                          &handle_hostname_response,
510                          ghc,
511                          GNUNET_TIME_absolute_get_remaining (ghc->timeout));
512 }
513
514
515 /**
516  * FIXME
517  *
518  * @param cls FIXME
519  * @param size number of bytes available in buf
520  * @param buf target buffer, NULL on error
521  * @return number of bytes written to buf
522  */
523 static size_t
524 transmit_get_hostname (void *cls, size_t size, void *buf)
525 {
526   struct GetHostnameContext *hctx = cls;
527   uint16_t msize;
528
529   if (buf == NULL)
530     {
531       GNUNET_free (hctx->msg);
532       hctx->callback (hctx->cls, NULL);
533       GNUNET_CLIENT_disconnect (hctx->client);
534       GNUNET_free (hctx);
535       return 0;
536     }
537   msize = ntohs (hctx->msg->header.size);
538   GNUNET_assert (size >= msize);
539   memcpy (buf, hctx->msg, msize);
540   GNUNET_free (hctx->msg);
541   hctx->msg = NULL;
542   GNUNET_CLIENT_receive (hctx->client,
543                          &handle_hostname_response,
544                          hctx,
545                          GNUNET_TIME_absolute_get_remaining (hctx->timeout));
546   return msize;
547 }
548
549
550
551
552 /**
553  * Get an IP address as a string.
554  *
555  * @param sched scheduler to use
556  * @param cfg configuration to use
557  * @param sa host address
558  * @param salen length of host address
559  * @param do_resolve use GNUNET_NO to return numeric hostname
560  * @param timeout how long to try resolving
561  * @param callback function to call with hostnames
562  * @param cls closure for callback
563  */
564 void
565 GNUNET_RESOLVER_hostname_get (struct GNUNET_SCHEDULER_Handle *sched,
566                               const struct GNUNET_CONFIGURATION_Handle *cfg,
567                               const struct sockaddr *sa,
568                               socklen_t salen,
569                               int do_resolve,
570                               struct GNUNET_TIME_Relative timeout,
571                               GNUNET_RESOLVER_HostnameCallback callback,
572                               void *cls)
573 {
574   char *result;
575   struct GNUNET_CLIENT_Connection *client;
576   struct GNUNET_RESOLVER_GetMessage *msg;
577   struct GetHostnameContext *hctx;
578
579   check_config (cfg);
580   if (GNUNET_NO == do_resolve)
581     {
582       result = no_resolve (sa, salen);
583 #if DEBUG_RESOLVER
584       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
585                   _("Resolver returns `%s'.\n"), result);
586 #endif
587       callback (cls, result);
588       if (result != NULL)
589         {
590           GNUNET_free (result);
591           callback (cls, NULL);
592         }
593       return;
594     }
595   if (salen + sizeof (struct GNUNET_RESOLVER_GetMessage) >
596       GNUNET_SERVER_MAX_MESSAGE_SIZE)
597     {
598       GNUNET_break (0);
599       callback (cls, NULL);
600       return;
601     }
602   client = GNUNET_CLIENT_connect (sched, "resolver", cfg);
603   if (client == NULL)
604     {
605       callback (cls, NULL);
606       return;
607     }
608   msg = GNUNET_malloc (sizeof (struct GNUNET_RESOLVER_GetMessage) + salen);
609   msg->header.size =
610     htons (sizeof (struct GNUNET_RESOLVER_GetMessage) + salen);
611   msg->header.type = htons (GNUNET_MESSAGE_TYPE_RESOLVER_REQUEST);
612   msg->direction = htonl (GNUNET_YES);
613   msg->domain = htonl (sa->sa_family);
614   memcpy (&msg[1], sa, salen);
615 #if DEBUG_RESOLVER
616   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
617               _("Resolver requests DNS resolution of IP address.\n"));
618 #endif
619   hctx = GNUNET_malloc (sizeof (struct GetHostnameContext));
620   hctx->callback = callback;
621   hctx->cls = cls;
622   hctx->client = client;
623   hctx->timeout = GNUNET_TIME_relative_to_absolute (timeout);
624   hctx->msg = msg;
625   if (NULL ==
626       GNUNET_CLIENT_notify_transmit_ready (client,
627                                            sizeof (struct
628                                                    GNUNET_RESOLVER_GetMessage)
629                                            + salen, timeout,
630                                            &transmit_get_hostname, hctx))
631     {
632       GNUNET_free (msg);
633       callback (cls, NULL);
634       GNUNET_CLIENT_disconnect (client);
635       GNUNET_free (hctx);
636     }
637 }
638
639 /**
640  * Maximum supported length of hostname
641  */
642 #define MAX_HOSTNAME 1024
643
644
645 /**
646  * Resolve our hostname to an IP address.
647  *
648  * @param sched scheduler to use
649  * @param cfg configuration to use
650  * @param domain AF_INET or AF_INET6; use AF_UNSPEC for "any"
651  * @param callback function to call with addresses
652  * @param cls closure for callback
653  * @param timeout how long to try resolving
654  */
655 void
656 GNUNET_RESOLVER_hostname_resolve (struct GNUNET_SCHEDULER_Handle *sched,
657                                   const struct GNUNET_CONFIGURATION_Handle *cfg,
658                                   int domain,
659                                   struct GNUNET_TIME_Relative timeout,
660                                   GNUNET_RESOLVER_AddressCallback callback,
661                                   void *cls)
662 {
663   char hostname[MAX_HOSTNAME];
664
665   check_config (cfg);
666   if (0 != gethostname (hostname, sizeof (hostname) - 1))
667     {
668       GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR |
669                            GNUNET_ERROR_TYPE_BULK, "gethostname");
670       callback (cls, NULL, 0);
671       return;
672     }
673 #if DEBUG_RESOLVER
674   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
675               _("Resolving our hostname `%s'\n"), hostname);
676 #endif
677   GNUNET_RESOLVER_ip_get (sched,
678                           cfg, hostname, domain, timeout, callback, cls);
679 }
680
681
682
683
684 /* end of resolver_api.c */