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