indentation
[oweals/gnunet.git] / src / util / gnunet-service-resolver.c
1 /*
2      This file is part of GNUnet.
3      (C) 2007, 2008, 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/gnunet-service-resolver.c
23  * @brief code to do DNS resolution
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet_disk_lib.h"
28 #include "gnunet_getopt_lib.h"
29 #include "gnunet_protocols.h"
30 #include "gnunet_service_lib.h"
31 #include "gnunet_statistics_service.h"
32 #include "gnunet_strings_lib.h"
33 #include "gnunet_time_lib.h"
34 #include "resolver.h"
35
36 /**
37  * A cached DNS lookup result.
38  */
39 struct IPCache
40 {
41   /**
42    * This is a linked list.
43    */
44   struct IPCache *next;
45
46   /**
47    * Hostname in human-readable form.
48    */
49   char *addr;
50
51   /**
52    * Hostname in binary format.
53    */
54   struct sockaddr *sa;
55
56   /**
57    * Last time this entry was updated.
58    */
59   struct GNUNET_TIME_Absolute last_refresh;
60
61   /**
62    * Last time this entry was requested.
63    */
64   struct GNUNET_TIME_Absolute last_request;
65
66   /**
67    * Number of bytes in sa.
68    */
69   socklen_t salen;
70 };
71
72
73 /**
74  * Start of the linked list of cached DNS lookup results.
75  */
76 static struct IPCache *head;
77
78
79 #if HAVE_GETNAMEINFO
80 /**
81  * Resolve the given request using getnameinfo
82  *
83  * @param cache the request to resolve (and where to store the result)
84  */
85 static void
86 getnameinfo_resolve (struct IPCache *cache)
87 {
88   char hostname[256];
89
90   if (0 == getnameinfo (cache->sa,
91                         cache->salen, hostname, sizeof (hostname), NULL, 0, 0))
92     cache->addr = GNUNET_strdup (hostname);
93 }
94 #endif
95
96
97 #if HAVE_GETHOSTBYADDR
98 /**
99  * Resolve the given request using gethostbyaddr
100  *
101  * @param cache the request to resolve (and where to store the result)
102  */
103 static void
104 gethostbyaddr_resolve (struct IPCache *cache)
105 {
106   struct hostent *ent;
107
108   switch (cache->sa->sa_family)
109   {
110   case AF_INET:
111     ent = gethostbyaddr (&((struct sockaddr_in *) cache->sa)->sin_addr,
112                          sizeof (struct in_addr), AF_INET);
113     break;
114   case AF_INET6:
115     ent = gethostbyaddr (&((struct sockaddr_in6 *) cache->sa)->sin6_addr,
116                          sizeof (struct in6_addr), AF_INET6);
117     break;
118   default:
119     ent = NULL;
120   }
121   if (ent != NULL)
122     cache->addr = GNUNET_strdup (ent->h_name);
123 }
124 #endif
125
126 /**
127  * Resolve the given request using the available methods.
128  *
129  * @param cache the request to resolve (and where to store the result)
130  */
131 static void
132 cache_resolve (struct IPCache *cache)
133 {
134 #if HAVE_GETNAMEINFO
135   if (cache->addr == NULL)
136     getnameinfo_resolve (cache);
137 #endif
138 #if HAVE_GETHOSTBYADDR
139   if (cache->addr == NULL)
140     gethostbyaddr_resolve (cache);
141 #endif
142 }
143
144
145
146 /**
147  * Get an IP address as a string (works for both IPv4 and IPv6).  Note
148  * that the resolution happens asynchronously and that the first call
149  * may not immediately result in the FQN (but instead in a
150  * human-readable IP address).
151  *
152  * @param client handle to the client making the request (for sending the reply)
153  * @param sa should be of type "struct sockaddr*"
154  * @param salen number of bytes in sa
155  */
156 static void
157 get_ip_as_string (struct GNUNET_SERVER_Client *client,
158                   const struct sockaddr *sa, socklen_t salen)
159 {
160   struct IPCache *cache;
161   struct IPCache *prev;
162   struct GNUNET_TIME_Absolute now;
163   struct GNUNET_SERVER_TransmitContext *tc;
164
165   if (salen < sizeof (struct sockaddr))
166   {
167     GNUNET_break (0);
168     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
169     return;
170   }
171   now = GNUNET_TIME_absolute_get ();
172   cache = head;
173   prev = NULL;
174   while ((cache != NULL) &&
175          ((cache->salen != salen) || (0 != memcmp (cache->sa, sa, salen))))
176   {
177     if (GNUNET_TIME_absolute_get_duration (cache->last_request).rel_value <
178         60 * 60 * 1000)
179     {
180       if (prev != NULL)
181       {
182         prev->next = cache->next;
183         GNUNET_free_non_null (cache->addr);
184         GNUNET_free (cache->sa);
185         GNUNET_free (cache);
186         cache = prev->next;
187       }
188       else
189       {
190         head = cache->next;
191         GNUNET_free_non_null (cache->addr);
192         GNUNET_free (cache->sa);
193         GNUNET_free (cache);
194         cache = head;
195       }
196       continue;
197     }
198     prev = cache;
199     cache = cache->next;
200   }
201   if (cache != NULL)
202   {
203     cache->last_request = now;
204     if (GNUNET_TIME_absolute_get_duration (cache->last_request).rel_value <
205         60 * 60 * 1000)
206     {
207       GNUNET_free_non_null (cache->addr);
208       cache->addr = NULL;
209       cache->salen = 0;
210       cache_resolve (cache);
211     }
212   }
213   else
214   {
215     cache = GNUNET_malloc (sizeof (struct IPCache));
216     cache->next = head;
217     cache->salen = salen;
218     cache->sa = GNUNET_malloc (salen);
219     memcpy (cache->sa, sa, salen);
220     cache->last_request = GNUNET_TIME_absolute_get ();
221     cache->last_refresh = GNUNET_TIME_absolute_get ();
222     cache->addr = NULL;
223     cache_resolve (cache);
224     head = cache;
225   }
226   tc = GNUNET_SERVER_transmit_context_create (client);
227   if (cache->addr != NULL)
228     GNUNET_SERVER_transmit_context_append_data (tc,
229                                                 cache->addr,
230                                                 strlen (cache->addr) + 1,
231                                                 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
232   GNUNET_SERVER_transmit_context_append_data (tc, NULL, 0,
233                                               GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
234   GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL);
235 }
236
237
238 #if HAVE_GETADDRINFO
239 static int
240 getaddrinfo_resolve (struct GNUNET_SERVER_TransmitContext *tc,
241                      const char *hostname, int domain)
242 {
243   int s;
244   struct addrinfo hints;
245   struct addrinfo *result;
246   struct addrinfo *pos;
247
248   memset (&hints, 0, sizeof (struct addrinfo));
249 // FIXME in PlibC
250 #ifndef MINGW
251   hints.ai_family = domain;
252 #else
253   hints.ai_family = AF_INET;
254 #endif
255   hints.ai_socktype = SOCK_STREAM;      /* go for TCP */
256
257   if (0 != (s = getaddrinfo (hostname, NULL, &hints, &result)))
258   {
259     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
260                 _("Could not resolve `%s' (%s): %s\n"), hostname,
261                 (domain ==
262                  AF_INET) ? "IPv4" : ((domain ==
263                                        AF_INET6) ? "IPv6" : "any"),
264                 gai_strerror (s));
265     if ((s == EAI_BADFLAGS) || (s == EAI_MEMORY)
266 #ifndef MINGW
267         || (s == EAI_SYSTEM)
268 #else
269         // FIXME NILS
270         || 1
271 #endif
272         )
273       return GNUNET_NO;         /* other function may still succeed */
274     return GNUNET_SYSERR;
275   }
276   if (result == NULL)
277     return GNUNET_SYSERR;
278   pos = result;
279   while (pos != NULL)
280   {
281     GNUNET_SERVER_transmit_context_append_data (tc,
282                                                 pos->ai_addr,
283                                                 pos->ai_addrlen,
284                                                 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
285     pos = pos->ai_next;
286   }
287   freeaddrinfo (result);
288   return GNUNET_OK;
289 }
290 #endif
291
292 #if HAVE_GETHOSTBYNAME2
293 static int
294 gethostbyname2_resolve (struct GNUNET_SERVER_TransmitContext *tc,
295                         const char *hostname, int domain)
296 {
297   struct hostent *hp;
298   struct sockaddr_in a4;
299   struct sockaddr_in6 a6;
300   int ret1;
301   int ret2;
302
303   if (domain == AF_UNSPEC)
304   {
305     ret1 = gethostbyname2_resolve (tc, hostname, AF_INET);
306     ret2 = gethostbyname2_resolve (tc, hostname, AF_INET6);
307     if ((ret1 == GNUNET_OK) || (ret2 == GNUNET_OK))
308       return GNUNET_OK;
309     if ((ret1 == GNUNET_SYSERR) || (ret2 == GNUNET_SYSERR))
310       return GNUNET_SYSERR;
311     return GNUNET_NO;
312   }
313   hp = gethostbyname2 (hostname, domain);
314   if (hp == NULL)
315   {
316     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
317                 _("Could not find IP of host `%s': %s\n"),
318                 hostname, hstrerror (h_errno));
319     return GNUNET_SYSERR;
320   }
321   GNUNET_assert (hp->h_addrtype == domain);
322   if (domain == AF_INET)
323   {
324     GNUNET_assert (hp->h_length == sizeof (struct in_addr));
325     memset (&a4, 0, sizeof (a4));
326     a4.sin_family = AF_INET;
327 #if HAVE_SOCKADDR_IN_SIN_LEN
328     a4.sin_len = (u_char) sizeof (struct sockaddr_in);
329 #endif
330     memcpy (&a4.sin_addr, hp->h_addr_list[0], hp->h_length);
331     GNUNET_SERVER_transmit_context_append_data (tc,
332                                                 &a4,
333                                                 sizeof (a4),
334                                                 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
335   }
336   else
337   {
338     GNUNET_assert (hp->h_length == sizeof (struct in6_addr));
339     memset (&a6, 0, sizeof (a6));
340     a6.sin6_family = AF_INET6;
341 #if HAVE_SOCKADDR_IN_SIN_LEN
342     a6.sin6_len = (u_char) sizeof (struct sockaddr_in6);
343 #endif
344     memcpy (&a6.sin6_addr, hp->h_addr_list[0], hp->h_length);
345     GNUNET_SERVER_transmit_context_append_data (tc,
346                                                 &a6,
347                                                 sizeof (a6),
348                                                 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
349   }
350   return GNUNET_OK;
351 }
352 #endif
353
354 #if HAVE_GETHOSTBYNAME
355 static int
356 gethostbyname_resolve (struct GNUNET_SERVER_TransmitContext *tc,
357                        const char *hostname)
358 {
359   struct hostent *hp;
360   struct sockaddr_in addr;
361
362   hp = GETHOSTBYNAME (hostname);
363   if (hp == NULL)
364   {
365     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
366                 _("Could not find IP of host `%s': %s\n"),
367                 hostname, hstrerror (h_errno));
368     return GNUNET_SYSERR;
369   }
370   if (hp->h_addrtype != AF_INET)
371   {
372     GNUNET_break (0);
373     return GNUNET_SYSERR;
374   }
375   GNUNET_assert (hp->h_length == sizeof (struct in_addr));
376   memset (&addr, 0, sizeof (addr));
377   addr.sin_family = AF_INET;
378 #if HAVE_SOCKADDR_IN_SIN_LEN
379   addr.sin_len = (u_char) sizeof (struct sockaddr_in);
380 #endif
381   memcpy (&addr.sin_addr, hp->h_addr_list[0], hp->h_length);
382   GNUNET_SERVER_transmit_context_append_data (tc,
383                                               &addr,
384                                               sizeof (addr),
385                                               GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
386   return GNUNET_OK;
387 }
388 #endif
389
390
391 /**
392  * Convert a string to an IP address.
393  *
394  * @param client where to send the IP address
395  * @param hostname the hostname to resolve
396  * @param domain AF_INET or AF_INET6; use AF_UNSPEC for "any"
397  */
398 static void
399 get_ip_from_hostname (struct GNUNET_SERVER_Client *client,
400                       const char *hostname, int domain)
401 {
402   int ret;
403   struct GNUNET_SERVER_TransmitContext *tc;
404
405   tc = GNUNET_SERVER_transmit_context_create (client);
406   ret = GNUNET_NO;
407 #if HAVE_GETADDRINFO
408   if (ret == GNUNET_NO)
409     ret = getaddrinfo_resolve (tc, hostname, domain);
410 #endif
411 #if HAVE_GETHOSTBYNAME2
412   if (ret == GNUNET_NO)
413     ret = gethostbyname2_resolve (tc, hostname, domain);
414 #endif
415 #if HAVE_GETHOSTBYNAME
416   if ((ret == GNUNET_NO) && ((domain == AF_UNSPEC) || (domain == PF_INET)))
417     gethostbyname_resolve (tc, hostname);
418 #endif
419   GNUNET_SERVER_transmit_context_append_data (tc, NULL, 0,
420                                               GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
421   GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL);
422 }
423
424
425 /**
426  * Handle GET-message.
427  *
428  * @param cls closure
429  * @param client identification of the client
430  * @param message the actual message
431  */
432 static void
433 handle_get (void *cls,
434             struct GNUNET_SERVER_Client *client,
435             const struct GNUNET_MessageHeader *message)
436 {
437   uint16_t msize;
438   const struct GNUNET_RESOLVER_GetMessage *msg;
439   const char *hostname;
440   const struct sockaddr *sa;
441   uint16_t size;
442   int direction;
443   int domain;
444
445   msize = ntohs (message->size);
446   if (msize < sizeof (struct GNUNET_RESOLVER_GetMessage))
447   {
448     GNUNET_break (0);
449     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
450     return;
451   }
452   msg = (const struct GNUNET_RESOLVER_GetMessage *) message;
453   size = msize - sizeof (struct GNUNET_RESOLVER_GetMessage);
454   direction = ntohl (msg->direction);
455   domain = ntohl (msg->domain);
456   if (direction == GNUNET_NO)
457   {
458     /* IP from hostname */
459     hostname = (const char *) &msg[1];
460     if (hostname[size - 1] != '\0')
461     {
462       GNUNET_break (0);
463       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
464       return;
465     }
466 #if DEBUG_RESOLVER
467     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
468                 _("Resolver asked to look up `%s'.\n"), hostname);
469 #endif
470     get_ip_from_hostname (client, hostname, domain);
471   }
472   else
473   {
474 #if DEBUG_RESOLVER
475     char buf[INET6_ADDRSTRLEN];
476 #endif
477     if (size < sizeof (struct sockaddr))
478     {
479       GNUNET_break (0);
480       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
481       return;
482     }
483     sa = (const struct sockaddr *) &msg[1];
484     switch (sa->sa_family)
485     {
486     case AF_INET:
487       if (size != sizeof (struct sockaddr_in))
488       {
489         GNUNET_break (0);
490         GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
491         return;
492       }
493 #if DEBUG_RESOLVER
494       inet_ntop (AF_INET, sa, buf, size);
495 #endif
496       break;
497     case AF_INET6:
498       if (size != sizeof (struct sockaddr_in6))
499       {
500         GNUNET_break (0);
501         GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
502         return;
503       }
504 #if DEBUG_RESOLVER
505       inet_ntop (AF_INET6, sa, buf, size);
506 #endif
507       break;
508     default:
509       GNUNET_break (0);
510       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
511       return;
512     }
513 #if DEBUG_RESOLVER
514     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
515                 _("Resolver asked to look up IP address `%s'.\n"), buf);
516 #endif
517     get_ip_as_string (client, sa, size);
518   }
519 }
520
521
522 /**
523  * Process resolver requests.
524  *
525  * @param cls closure
526  * @param server the initialized server
527  * @param cfg configuration to use
528  */
529 static void
530 run (void *cls,
531      struct GNUNET_SERVER_Handle *server,
532      const struct GNUNET_CONFIGURATION_Handle *cfg)
533 {
534   static const struct GNUNET_SERVER_MessageHandler handlers[] = {
535     {&handle_get, NULL, GNUNET_MESSAGE_TYPE_RESOLVER_REQUEST, 0},
536     {NULL, NULL, 0, 0}
537   };
538   GNUNET_SERVER_add_handlers (server, handlers);
539 }
540
541
542 /**
543  * The main function for the resolver service.
544  *
545  * @param argc number of arguments from the command line
546  * @param argv command line arguments
547  * @return 0 ok, 1 on error
548  */
549 int
550 main (int argc, char *const *argv)
551 {
552   int ret;
553   struct IPCache *pos;
554
555   ret = (GNUNET_OK ==
556          GNUNET_SERVICE_run (argc,
557                              argv,
558                              "resolver", GNUNET_SERVICE_OPTION_NONE,
559                              &run, NULL)) ? 0 : 1;
560
561   while (head != NULL)
562   {
563     pos = head->next;
564     GNUNET_free_non_null (head->addr);
565     GNUNET_free (head->sa);
566     GNUNET_free (head);
567     head = pos;
568   }
569   return ret;
570 }
571
572 /* end of gnunet-service-resolver.c */