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 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  * @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       callback (callback_cls, NULL, 0);
323       return;
324     }
325   if ( ( (domain == AF_UNSPEC) ||(domain == AF_INET) ) && 
326        (0 == inet_pton (AF_INET6,
327                         hostname,
328                         &v6.sin6_addr)) )
329     {
330       callback (callback_cls,
331                 (const struct sockaddr*) &v6,
332                 sizeof(v6));
333       callback (callback_cls, NULL, 0);
334       return;
335     }
336   check_config (cfg);
337   /* then, check if this is a loopback address */
338   i = 0;
339   while (loopback[i] != NULL)
340     if (0 == strcmp (loopback[i++], hostname))
341       {
342         v4.sin_addr.s_addr = htonl (INADDR_LOOPBACK);  
343         v6.sin6_addr = in6addr_loopback;
344         switch (domain)
345           {
346           case AF_INET:
347             callback (callback_cls, 
348                       (const struct sockaddr*) &v4,
349                       sizeof(v4));
350             break;
351           case AF_INET6:
352             callback (callback_cls, 
353                       (const struct sockaddr*) &v6,
354                       sizeof(v6));
355             break;
356           case AF_UNSPEC:
357             callback (callback_cls, 
358                       (const struct sockaddr*) &v6,
359                       sizeof(v6));
360             callback (callback_cls, 
361                       (const struct sockaddr*) &v4,
362                       sizeof(v4));
363             break;
364           }
365         callback (callback_cls, NULL, 0);
366         return;
367       }
368   slen = strlen (hostname) + 1;
369   if (slen + sizeof (struct GNUNET_RESOLVER_GetMessage) >
370       GNUNET_SERVER_MAX_MESSAGE_SIZE)
371     {
372       GNUNET_break (0);
373       callback (callback_cls, NULL, 0);
374       return;
375     }
376   client = GNUNET_CLIENT_connect (sched, "resolver", cfg);
377   if (client == NULL)
378     {
379       callback (callback_cls, NULL, 0);
380       return;
381     }
382   msg = GNUNET_malloc (sizeof (struct GNUNET_RESOLVER_GetMessage) + slen);
383   msg->header.size =
384     htons (sizeof (struct GNUNET_RESOLVER_GetMessage) + slen);
385   msg->header.type = htons (GNUNET_MESSAGE_TYPE_RESOLVER_REQUEST);
386   msg->direction = htonl (GNUNET_NO);
387   msg->domain = htonl (domain);
388   memcpy (&msg[1], hostname, slen);
389   actx = GNUNET_malloc (sizeof (struct GetAddressContext));
390   actx->callback = callback;
391   actx->cls = callback_cls;
392   actx->client = client;
393   actx->timeout = GNUNET_TIME_relative_to_absolute (timeout);
394   actx->msg = msg;
395
396 #if DEBUG_RESOLVER
397   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
398               _("Resolver requests DNS resolution of hostname `%s'.\n"),
399               hostname);
400 #endif
401   if (NULL ==
402       GNUNET_CLIENT_notify_transmit_ready (client,
403                                            slen +
404                                            sizeof (struct
405                                                    GNUNET_RESOLVER_GetMessage),
406                                            timeout, &transmit_get_ip, actx))
407     {
408       GNUNET_free (msg);
409       GNUNET_free (actx);
410       callback (callback_cls, NULL, 0);
411       GNUNET_CLIENT_disconnect (client);
412       return;
413     }
414 }
415
416
417 /**
418  * FIXME.
419  */
420 struct GetHostnameContext
421 {
422
423   /**
424    * FIXME.
425    */
426   GNUNET_RESOLVER_HostnameCallback callback;
427   
428   /**
429    * FIXME.
430    */
431   void *cls;
432   
433   /**
434    * FIXME.
435    */ 
436   struct GNUNET_RESOLVER_GetMessage *msg;
437   
438   /**
439    * FIXME.
440    */  
441   struct GNUNET_CLIENT_Connection *client;
442   
443   /**
444    * FIXME.
445    */ 
446   struct GNUNET_TIME_Absolute timeout;
447 };
448
449
450 /**
451  * FIXME.
452  *
453  * @param cls FIXME
454  * @param msg FIXME
455  */
456 static void
457 handle_hostname_response (void *cls, const struct GNUNET_MessageHeader *msg)
458 {
459   struct GetHostnameContext *ghc = cls;
460   uint16_t size;
461   const char *hostname;
462
463   if (msg == NULL)
464     {
465       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
466                   _("Timeout trying to resolve IP address.\n"));
467       ghc->callback (ghc->cls, NULL);
468       GNUNET_CLIENT_disconnect (ghc->client);
469       GNUNET_free (ghc);
470       return;
471     }
472   size = ntohs (msg->size);
473   if (size == sizeof (struct GNUNET_MessageHeader))
474     {
475 #if DEBUG_RESOLVER
476       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
477                   _("Received end message resolving IP address.\n"));
478 #endif
479       ghc->callback (ghc->cls, NULL);
480       GNUNET_CLIENT_disconnect (ghc->client);
481       GNUNET_free (ghc);
482       return;
483     }
484   hostname = (const char *) &msg[1];
485   if (hostname[size - sizeof (struct GNUNET_MessageHeader) - 1] != '\0')
486     {
487       GNUNET_break (0);
488       ghc->callback (ghc->cls, NULL);
489       GNUNET_CLIENT_disconnect (ghc->client);
490       GNUNET_free (ghc);
491       return;
492     }
493 #if DEBUG_RESOLVER
494   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
495               _("Resolver returns `%s'.\n"), hostname);
496 #endif
497   ghc->callback (ghc->cls, hostname);
498   GNUNET_CLIENT_receive (ghc->client,
499                          &handle_hostname_response,
500                          ghc,
501                          GNUNET_TIME_absolute_get_remaining (ghc->timeout));
502 }
503
504
505 /**
506  * FIXME
507  *
508  * @param cls FIXME
509  * @param size number of bytes available in buf
510  * @param buf target buffer, NULL on error
511  * @return number of bytes written to buf
512  */
513 static size_t
514 transmit_get_hostname (void *cls, size_t size, void *buf)
515 {
516   struct GetHostnameContext *hctx = cls;
517   uint16_t msize;
518
519   if (buf == NULL)
520     {
521       GNUNET_free (hctx->msg);
522       hctx->callback (hctx->cls, NULL);
523       GNUNET_CLIENT_disconnect (hctx->client);
524       GNUNET_free (hctx);
525       return 0;
526     }
527   msize = ntohs (hctx->msg->header.size);
528   GNUNET_assert (size >= msize);
529   memcpy (buf, hctx->msg, msize);
530   GNUNET_free (hctx->msg);
531   hctx->msg = NULL;
532   GNUNET_CLIENT_receive (hctx->client,
533                          &handle_hostname_response,
534                          hctx,
535                          GNUNET_TIME_absolute_get_remaining (hctx->timeout));
536   return msize;
537 }
538
539
540
541
542 /**
543  * Get an IP address as a string.
544  *
545  * @param sched scheduler to use
546  * @param cfg configuration to use
547  * @param sa host address
548  * @param salen length of host address
549  * @param do_resolve use GNUNET_NO to return numeric hostname
550  * @param timeout how long to try resolving
551  * @param callback function to call with hostnames
552  * @param cls closure for callback
553  */
554 void
555 GNUNET_RESOLVER_hostname_get (struct GNUNET_SCHEDULER_Handle *sched,
556                               const struct GNUNET_CONFIGURATION_Handle *cfg,
557                               const struct sockaddr *sa,
558                               socklen_t salen,
559                               int do_resolve,
560                               struct GNUNET_TIME_Relative timeout,
561                               GNUNET_RESOLVER_HostnameCallback callback,
562                               void *cls)
563 {
564   char *result;
565   struct GNUNET_CLIENT_Connection *client;
566   struct GNUNET_RESOLVER_GetMessage *msg;
567   struct GetHostnameContext *hctx;
568
569   check_config (cfg);
570   if (GNUNET_NO == do_resolve)
571     {
572       result = no_resolve (sa, salen);
573 #if DEBUG_RESOLVER
574       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
575                   _("Resolver returns `%s'.\n"), result);
576 #endif
577       callback (cls, result);
578       if (result != NULL)
579         {
580           GNUNET_free (result);
581           callback (cls, NULL);
582         }
583       return;
584     }
585   if (salen + sizeof (struct GNUNET_RESOLVER_GetMessage) >
586       GNUNET_SERVER_MAX_MESSAGE_SIZE)
587     {
588       GNUNET_break (0);
589       callback (cls, NULL);
590       return;
591     }
592   client = GNUNET_CLIENT_connect (sched, "resolver", cfg);
593   if (client == NULL)
594     {
595       callback (cls, NULL);
596       return;
597     }
598   msg = GNUNET_malloc (sizeof (struct GNUNET_RESOLVER_GetMessage) + salen);
599   msg->header.size =
600     htons (sizeof (struct GNUNET_RESOLVER_GetMessage) + salen);
601   msg->header.type = htons (GNUNET_MESSAGE_TYPE_RESOLVER_REQUEST);
602   msg->direction = htonl (GNUNET_YES);
603   msg->domain = htonl (sa->sa_family);
604   memcpy (&msg[1], sa, salen);
605 #if DEBUG_RESOLVER
606   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
607               _("Resolver requests DNS resolution of IP address.\n"));
608 #endif
609   hctx = GNUNET_malloc (sizeof (struct GetHostnameContext));
610   hctx->callback = callback;
611   hctx->cls = cls;
612   hctx->client = client;
613   hctx->timeout = GNUNET_TIME_relative_to_absolute (timeout);
614   hctx->msg = msg;
615   if (NULL ==
616       GNUNET_CLIENT_notify_transmit_ready (client,
617                                            sizeof (struct
618                                                    GNUNET_RESOLVER_GetMessage)
619                                            + salen, timeout,
620                                            &transmit_get_hostname, hctx))
621     {
622       GNUNET_free (msg);
623       callback (cls, NULL);
624       GNUNET_CLIENT_disconnect (client);
625       GNUNET_free (hctx);
626     }
627 }
628
629 /**
630  * Maximum supported length of hostname
631  */
632 #define MAX_HOSTNAME 1024
633
634
635 /**
636  * Resolve our hostname to an IP address.
637  *
638  * @param sched scheduler to use
639  * @param cfg configuration to use
640  * @param domain AF_INET or AF_INET6; use AF_UNSPEC for "any"
641  * @param callback function to call with addresses
642  * @param cls closure for callback
643  * @param timeout how long to try resolving
644  */
645 void
646 GNUNET_RESOLVER_hostname_resolve (struct GNUNET_SCHEDULER_Handle *sched,
647                                   const struct GNUNET_CONFIGURATION_Handle *cfg,
648                                   int domain,
649                                   struct GNUNET_TIME_Relative timeout,
650                                   GNUNET_RESOLVER_AddressCallback callback,
651                                   void *cls)
652 {
653   char hostname[MAX_HOSTNAME];
654
655   check_config (cfg);
656   if (0 != gethostname (hostname, sizeof (hostname) - 1))
657     {
658       GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR |
659                            GNUNET_ERROR_TYPE_BULK, "gethostname");
660       callback (cls, NULL, 0);
661       return;
662     }
663 #if DEBUG_RESOLVER
664   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
665               _("Resolving our hostname `%s'\n"), hostname);
666 #endif
667   GNUNET_RESOLVER_ip_get (sched,
668                           cfg, hostname, domain, timeout, callback, cls);
669 }
670
671
672
673
674 /* end of resolver_api.c */