-doxygen
[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   memset (&hints, 0, sizeof (struct addrinfo));
278   hints.ai_family = af;
279   hints.ai_socktype = SOCK_STREAM;      /* go for TCP */
280
281   if (0 != (s = getaddrinfo (hostname, NULL, &hints, &result)))
282   {
283     GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Could not resolve `%s' (%s): %s\n"),
284                 hostname,
285                 (af ==
286                  AF_INET) ? "IPv4" : ((af == AF_INET6) ? "IPv6" : "any"),
287                 gai_strerror (s));
288     if ((s == EAI_BADFLAGS) || (s == EAI_MEMORY)
289 #ifndef WINDOWS
290         || (s == EAI_SYSTEM)
291 #else
292         || 1
293 #endif
294         )
295       return GNUNET_NO;         /* other function may still succeed */
296     return GNUNET_SYSERR;
297   }
298   if (result == NULL)
299     return GNUNET_SYSERR;
300   for (pos = result; pos != NULL; pos = pos->ai_next)
301   {
302     switch (pos->ai_family)
303     {
304     case AF_INET:
305       GNUNET_SERVER_transmit_context_append_data (tc,
306                                                   &((struct sockaddr_in*) pos->ai_addr)->sin_addr,
307                                                   sizeof (struct in_addr),
308                                                   GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
309       break;
310     case AF_INET6:
311       GNUNET_SERVER_transmit_context_append_data (tc,
312                                                   &((struct sockaddr_in6*) pos->ai_addr)->sin6_addr,
313                                                   sizeof (struct in6_addr),
314                                                   GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
315       break;
316     default:
317       /* unsupported, skip */
318       break;
319     }
320   }
321   freeaddrinfo (result);
322   return GNUNET_OK;
323 }
324 #endif
325
326
327 #if HAVE_GETHOSTBYNAME2
328 static int
329 gethostbyname2_resolve (struct GNUNET_SERVER_TransmitContext *tc,
330                         const char *hostname, int af)
331 {
332   struct hostent *hp;
333   int ret1;
334   int ret2;
335
336   if (af == AF_UNSPEC)
337   {
338     ret1 = gethostbyname2_resolve (tc, hostname, AF_INET);
339     ret2 = gethostbyname2_resolve (tc, hostname, AF_INET6);
340     if ((ret1 == GNUNET_OK) || (ret2 == GNUNET_OK))
341       return GNUNET_OK;
342     if ((ret1 == GNUNET_SYSERR) || (ret2 == GNUNET_SYSERR))
343       return GNUNET_SYSERR;
344     return GNUNET_NO;
345   }
346   hp = gethostbyname2 (hostname, af);
347   if (hp == NULL)
348   {
349     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
350                 _("Could not find IP of host `%s': %s\n"), hostname,
351                 hstrerror (h_errno));
352     return GNUNET_SYSERR;
353   }
354   GNUNET_assert (hp->h_addrtype == af);
355   switch (af)
356   {
357   case AF_INET:
358     GNUNET_assert (hp->h_length == sizeof (struct in_addr));
359     GNUNET_SERVER_transmit_context_append_data (tc,
360                                                 hp->h_addr_list[0],
361                                                 hp->h_length,
362                                                 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
363     break;
364   case AF_INET6:
365     GNUNET_assert (hp->h_length == sizeof (struct in6_addr));
366     GNUNET_SERVER_transmit_context_append_data (tc,
367                                                 hp->h_addr_list[0],
368                                                 hp->h_length,
369                                                 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
370     break;
371   default:
372     GNUNET_break (0);
373     return GNUNET_SYSERR;
374   }
375   return GNUNET_OK;
376 }
377 #endif
378
379
380 #if HAVE_GETHOSTBYNAME
381 static int
382 gethostbyname_resolve (struct GNUNET_SERVER_TransmitContext *tc,
383                        const char *hostname)
384 {
385   struct hostent *hp;
386
387   hp = GETHOSTBYNAME (hostname);
388   if (hp == NULL)
389   {
390     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
391                 _("Could not find IP of host `%s': %s\n"), hostname,
392                 hstrerror (h_errno));
393     return GNUNET_SYSERR;
394   }
395   if (hp->h_addrtype != AF_INET)
396   {
397     GNUNET_break (0);
398     return GNUNET_SYSERR;
399   }
400   GNUNET_assert (hp->h_length == sizeof (struct in_addr));
401   GNUNET_SERVER_transmit_context_append_data (tc,
402                                               hp->h_addr_list[0],
403                                               hp->h_length,
404                                               GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
405   return GNUNET_OK;
406 }
407 #endif
408
409
410 /**
411  * Convert a string to an IP address.
412  *
413  * @param client where to send the IP address
414  * @param hostname the hostname to resolve
415  * @param af AF_INET or AF_INET6; use AF_UNSPEC for "any"
416  */
417 static void
418 get_ip_from_hostname (struct GNUNET_SERVER_Client *client, const char *hostname,
419                       int af)
420 {
421   int ret;
422   struct GNUNET_SERVER_TransmitContext *tc;
423
424   tc = GNUNET_SERVER_transmit_context_create (client);
425   ret = GNUNET_NO;
426 #if HAVE_GETADDRINFO
427   if (ret == GNUNET_NO)
428     ret = getaddrinfo_resolve (tc, hostname, af);
429 #endif
430 #if HAVE_GETHOSTBYNAME2
431   if (ret == GNUNET_NO)
432     ret = gethostbyname2_resolve (tc, hostname, af);
433 #endif
434 #if HAVE_GETHOSTBYNAME
435   if ((ret == GNUNET_NO) && ((af == AF_UNSPEC) || (af == PF_INET)))
436     gethostbyname_resolve (tc, hostname);
437 #endif
438   GNUNET_SERVER_transmit_context_append_data (tc, NULL, 0,
439                                               GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
440   GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL);
441 }
442
443
444 /**
445  * Handle GET-message.
446  *
447  * @param cls closure
448  * @param client identification of the client
449  * @param message the actual message
450  */
451 static void
452 handle_get (void *cls, struct GNUNET_SERVER_Client *client,
453             const struct GNUNET_MessageHeader *message)
454 {
455   uint16_t msize;
456   const struct GNUNET_RESOLVER_GetMessage *msg;
457   const void *ip;
458   uint16_t size;
459   int direction;
460   int af;
461
462   msize = ntohs (message->size);
463   if (msize < sizeof (struct GNUNET_RESOLVER_GetMessage))
464   {
465     GNUNET_break (0);
466     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
467     return;
468   }
469   msg = (const struct GNUNET_RESOLVER_GetMessage *) message;
470   size = msize - sizeof (struct GNUNET_RESOLVER_GetMessage);
471   direction = ntohl (msg->direction);
472   af = ntohl (msg->af);
473   if (direction == GNUNET_NO)
474   {
475     /* IP from hostname */
476     const char *hostname;
477
478     hostname = (const char *) &msg[1];
479     if (hostname[size - 1] != '\0')
480     {
481       GNUNET_break (0);
482       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
483       return;
484     }
485     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Resolver asked to look up `%s'.\n",
486                 hostname);
487     get_ip_from_hostname (client, hostname, af);
488     return;
489   }
490   ip = &msg[1];
491   switch (af)
492   {
493   case AF_INET:
494     if (size != sizeof (struct in_addr))
495     {
496       GNUNET_break (0);
497       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
498       return;
499     }
500     break;
501   case AF_INET6:
502     if (size != sizeof (struct in6_addr))
503     {
504       GNUNET_break (0);
505       GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
506       return;
507     }
508     break;
509   default:
510     GNUNET_break (0);
511     GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
512     return;
513   }
514   {
515     char buf[INET6_ADDRSTRLEN];
516
517     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
518                 "Resolver asked to look up IP address `%s'.\n",
519                 inet_ntop (af, ip, buf, sizeof (buf)));
520   }
521   get_ip_as_string (client, af, ip);
522 }
523
524
525 /**
526  * Process resolver requests.
527  *
528  * @param cls closure
529  * @param server the initialized server
530  * @param cfg configuration to use
531  */
532 static void
533 run (void *cls, struct GNUNET_SERVER_Handle *server,
534      const struct GNUNET_CONFIGURATION_Handle *cfg)
535 {
536   static const struct GNUNET_SERVER_MessageHandler handlers[] = {
537     {&handle_get, NULL, GNUNET_MESSAGE_TYPE_RESOLVER_REQUEST, 0},
538     {NULL, NULL, 0, 0}
539   };
540   GNUNET_SERVER_add_handlers (server, handlers);
541 }
542
543
544 /**
545  * The main function for the resolver service.
546  *
547  * @param argc number of arguments from the command line
548  * @param argv command line arguments
549  * @return 0 ok, 1 on error
550  */
551 int
552 main (int argc, char *const *argv)
553 {
554   struct IPCache *pos;
555   int ret;
556
557   ret =
558       (GNUNET_OK ==
559        GNUNET_SERVICE_run (argc, argv, "resolver", GNUNET_SERVICE_OPTION_NONE,
560                            &run, NULL)) ? 0 : 1;
561   while (NULL != (pos = cache_head))
562   {
563     GNUNET_CONTAINER_DLL_remove (cache_head,
564                                  cache_tail,
565                                  pos);
566     GNUNET_free_non_null (pos->addr);
567     GNUNET_free (pos);
568   }
569   return ret;
570 }
571
572 #ifdef LINUX
573 #include <malloc.h>
574
575 /**
576  * MINIMIZE heap size (way below 128k) since this process doesn't need much.
577  */
578 void __attribute__ ((constructor)) GNUNET_ARM_memory_init ()
579 {
580   mallopt (M_TRIM_THRESHOLD, 4 * 1024);
581   mallopt (M_TOP_PAD, 1 * 1024);
582   malloc_trim (0);
583 }
584 #endif
585
586
587 /* end of gnunet-service-resolver.c */