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