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