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