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