Use SSH more safely from configure
[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., 51 Franklin Street, Fifth Floor,
18      Boston, MA 02110-1301, 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
369
370 #elif HAVE_GETHOSTBYNAME2
371
372
373 static int
374 gethostbyname2_resolve (struct GNUNET_SERVER_TransmitContext *tc,
375                         const char *hostname,
376                         int af)
377 {
378   struct hostent *hp;
379   int ret1;
380   int ret2;
381
382 #ifdef WINDOWS
383   /* gethostbyname2() in plibc is a compat dummy that calls gethostbyname(). */
384   return GNUNET_NO;
385 #endif
386
387   if (af == AF_UNSPEC)
388   {
389     ret1 = gethostbyname2_resolve (tc, hostname, AF_INET);
390     ret2 = gethostbyname2_resolve (tc, hostname, AF_INET6);
391     if ((ret1 == GNUNET_OK) || (ret2 == GNUNET_OK))
392       return GNUNET_OK;
393     if ((ret1 == GNUNET_SYSERR) || (ret2 == GNUNET_SYSERR))
394       return GNUNET_SYSERR;
395     return GNUNET_NO;
396   }
397   hp = gethostbyname2 (hostname, af);
398   if (hp == NULL)
399   {
400     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
401                 _("Could not find IP of host `%s': %s\n"), hostname,
402                 hstrerror (h_errno));
403     return GNUNET_SYSERR;
404   }
405   GNUNET_assert (hp->h_addrtype == af);
406   switch (af)
407   {
408   case AF_INET:
409     GNUNET_assert (hp->h_length == sizeof (struct in_addr));
410     GNUNET_SERVER_transmit_context_append_data (tc,
411                                                 hp->h_addr_list[0],
412                                                 hp->h_length,
413                                                 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
414     break;
415   case AF_INET6:
416     GNUNET_assert (hp->h_length == sizeof (struct in6_addr));
417     GNUNET_SERVER_transmit_context_append_data (tc,
418                                                 hp->h_addr_list[0],
419                                                 hp->h_length,
420                                                 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
421     break;
422   default:
423     GNUNET_break (0);
424     return GNUNET_SYSERR;
425   }
426   return GNUNET_OK;
427 }
428
429 #elif HAVE_GETHOSTBYNAME
430
431
432 static int
433 gethostbyname_resolve (struct GNUNET_SERVER_TransmitContext *tc,
434                        const char *hostname)
435 {
436   struct hostent *hp;
437
438   hp = GETHOSTBYNAME (hostname);
439   if (NULL == hp)
440   {
441     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
442                 _("Could not find IP of host `%s': %s\n"),
443                 hostname,
444                 hstrerror (h_errno));
445     return GNUNET_SYSERR;
446   }
447   if (hp->h_addrtype != AF_INET)
448   {
449     GNUNET_break (0);
450     return GNUNET_SYSERR;
451   }
452   GNUNET_assert (hp->h_length == sizeof (struct in_addr));
453   GNUNET_SERVER_transmit_context_append_data (tc,
454                                               hp->h_addr_list[0],
455                                               hp->h_length,
456                                               GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
457   return GNUNET_OK;
458 }
459 #endif
460
461
462 /**
463  * Convert a string to an IP address.
464  *
465  * @param client where to send the IP address
466  * @param hostname the hostname to resolve
467  * @param af AF_INET or AF_INET6; use AF_UNSPEC for "any"
468  */
469 static void
470 get_ip_from_hostname (struct GNUNET_SERVER_Client *client,
471                       const char *hostname,
472                       int af)
473 {
474   int ret;
475   struct GNUNET_SERVER_TransmitContext *tc;
476
477   tc = GNUNET_SERVER_transmit_context_create (client);
478   ret = GNUNET_NO;
479 #if HAVE_GETADDRINFO
480   if (ret == GNUNET_NO)
481     ret = getaddrinfo_resolve (tc, hostname, af);
482 #elif HAVE_GETHOSTBYNAME2
483   if (ret == GNUNET_NO)
484     ret = gethostbyname2_resolve (tc, hostname, af);
485 #elif HAVE_GETHOSTBYNAME
486   if ((ret == GNUNET_NO) && ((af == AF_UNSPEC) || (af == PF_INET)))
487     gethostbyname_resolve (tc, hostname);
488 #endif
489   GNUNET_SERVER_transmit_context_append_data (tc, NULL, 0,
490                                               GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
491   GNUNET_SERVER_transmit_context_run (tc, GNUNET_TIME_UNIT_FOREVER_REL);
492 }
493
494
495 /**
496  * Handle GET-message.
497  *
498  * @param cls closure
499  * @param client identification of the client
500  * @param message the actual message
501  */
502 static void
503 handle_get (void *cls,
504             struct GNUNET_SERVER_Client *client,
505             const struct GNUNET_MessageHeader *message)
506 {
507   uint16_t msize;
508   const struct GNUNET_RESOLVER_GetMessage *msg;
509   const void *ip;
510   uint16_t size;
511   int direction;
512   int af;
513
514   msize = ntohs (message->size);
515   if (msize < sizeof (struct GNUNET_RESOLVER_GetMessage))
516   {
517     GNUNET_break (0);
518     GNUNET_SERVER_receive_done (client,
519                                 GNUNET_SYSERR);
520     return;
521   }
522   msg = (const struct GNUNET_RESOLVER_GetMessage *) message;
523   size = msize - sizeof (struct GNUNET_RESOLVER_GetMessage);
524   direction = ntohl (msg->direction);
525   af = ntohl (msg->af);
526   if (GNUNET_NO == direction)
527   {
528     /* IP from hostname */
529     const char *hostname;
530
531     hostname = (const char *) &msg[1];
532     if (hostname[size - 1] != '\0')
533     {
534       GNUNET_break (0);
535       GNUNET_SERVER_receive_done (client,
536                                   GNUNET_SYSERR);
537       return;
538     }
539     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
540                 "Resolver asked to look up `%s'.\n",
541                 hostname);
542     get_ip_from_hostname (client, hostname, af);
543     return;
544   }
545   ip = &msg[1];
546   switch (af)
547   {
548   case AF_INET:
549     if (size != sizeof (struct in_addr))
550     {
551       GNUNET_break (0);
552       GNUNET_SERVER_receive_done (client,
553                                   GNUNET_SYSERR);
554       return;
555     }
556     break;
557   case AF_INET6:
558     if (size != sizeof (struct in6_addr))
559     {
560       GNUNET_break (0);
561       GNUNET_SERVER_receive_done (client,
562                                   GNUNET_SYSERR);
563       return;
564     }
565     break;
566   default:
567     GNUNET_break (0);
568     GNUNET_SERVER_receive_done (client,
569                                 GNUNET_SYSERR);
570     return;
571   }
572   {
573     char buf[INET6_ADDRSTRLEN];
574
575     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
576                 "Resolver asked to look up IP address `%s'.\n",
577                 inet_ntop (af, ip, buf, sizeof (buf)));
578   }
579   get_ip_as_string (client, af, ip);
580 }
581
582
583 /**
584  * Process resolver requests.
585  *
586  * @param cls closure
587  * @param server the initialized server
588  * @param cfg configuration to use
589  */
590 static void
591 run (void *cls, struct GNUNET_SERVER_Handle *server,
592      const struct GNUNET_CONFIGURATION_Handle *cfg)
593 {
594   static const struct GNUNET_SERVER_MessageHandler handlers[] = {
595     {&handle_get, NULL, GNUNET_MESSAGE_TYPE_RESOLVER_REQUEST, 0},
596     {NULL, NULL, 0, 0}
597   };
598   GNUNET_SERVER_add_handlers (server, handlers);
599 }
600
601
602 /**
603  * The main function for the resolver service.
604  *
605  * @param argc number of arguments from the command line
606  * @param argv command line arguments
607  * @return 0 ok, 1 on error
608  */
609 int
610 main (int argc, char *const *argv)
611 {
612   struct IPCache *pos;
613   int ret;
614
615   ret =
616       (GNUNET_OK ==
617        GNUNET_SERVICE_run (argc, argv,
618                            "resolver",
619                            GNUNET_SERVICE_OPTION_NONE,
620                            &run, NULL)) ? 0 : 1;
621   while (NULL != (pos = cache_head))
622   {
623     GNUNET_CONTAINER_DLL_remove (cache_head,
624                                  cache_tail,
625                                  pos);
626     GNUNET_free_non_null (pos->addr);
627     GNUNET_free (pos);
628   }
629   return ret;
630 }
631
632 #if defined(LINUX) && defined(__GLIBC__)
633 #include <malloc.h>
634
635 /**
636  * MINIMIZE heap size (way below 128k) since this process doesn't need much.
637  */
638 void __attribute__ ((constructor))
639 GNUNET_ARM_memory_init ()
640 {
641   mallopt (M_TRIM_THRESHOLD, 4 * 1024);
642   mallopt (M_TOP_PAD, 1 * 1024);
643   malloc_trim (0);
644 }
645 #endif
646
647
648 /* end of gnunet-service-resolver.c */