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