Merge branch 'master' of ssh://gnunet.org/gnunet
[oweals/gnunet.git] / src / util / gnunet-service-resolver.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2007-2016 GNUnet e.V.
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  * Function called after the replies for the request have all
201  * been transmitted to the client, and we can now read the next
202  * request from the client.
203  *
204  * @param cls the `struct GNUNET_SERVICE_Client` to continue with
205  */
206 static void
207 notify_service_client_done (void *cls)
208 {
209   struct GNUNET_SERVICE_Client *client = cls;
210
211   GNUNET_SERVICE_client_continue (client);
212 }
213
214
215 /**
216  * Get an IP address as a string (works for both IPv4 and IPv6).  Note
217  * that the resolution happens asynchronously and that the first call
218  * may not immediately result in the FQN (but instead in a
219  * human-readable IP address).
220  *
221  * @param client handle to the client making the request (for sending the reply)
222  * @param af AF_INET or AF_INET6
223  * @param ip `struct in_addr` or `struct in6_addr`
224  */
225 static void
226 get_ip_as_string (struct GNUNET_SERVICE_Client *client,
227                   int af,
228                   const void *ip)
229 {
230   struct IPCache *pos;
231   struct IPCache *next;
232   struct GNUNET_TIME_Absolute now;
233   struct GNUNET_MQ_Envelope *env;
234   struct GNUNET_MQ_Handle *mq;
235   struct GNUNET_MessageHeader *msg;
236   size_t ip_len;
237   struct in6_addr ix;
238   size_t alen;
239
240   switch (af)
241   {
242   case AF_INET:
243     ip_len = sizeof (struct in_addr);
244     break;
245   case AF_INET6:
246     ip_len = sizeof (struct in6_addr);
247     break;
248   default:
249     GNUNET_assert (0);
250   }
251   now = GNUNET_TIME_absolute_get ();
252   next = cache_head;
253   while ( (NULL != (pos = next)) &&
254           ( (pos->af != af) ||
255             (pos->ip_len != ip_len) ||
256             (0 != memcmp (pos->ip, ip, ip_len))) )
257   {
258     next = pos->next;
259     if (GNUNET_TIME_absolute_get_duration (pos->last_request).rel_value_us <
260         60 * 60 * 1000 * 1000LL)
261     {
262       GNUNET_CONTAINER_DLL_remove (cache_head,
263                                    cache_tail,
264                                    pos);
265       GNUNET_free_non_null (pos->addr);
266       GNUNET_free (pos);
267       continue;
268     }
269   }
270   if (NULL != pos)
271   {
272     if ( (1 == inet_pton (af,
273                           pos->ip,
274                           &ix)) &&
275          (GNUNET_TIME_absolute_get_duration (pos->last_request).rel_value_us >
276           120 * 1000 * 1000LL) )
277     {
278       /* try again if still numeric AND 2 minutes have expired */
279       GNUNET_free_non_null (pos->addr);
280       pos->addr = NULL;
281       cache_resolve (pos);
282       pos->last_request = now;
283     }
284   }
285   else
286   {
287     pos = GNUNET_malloc (sizeof (struct IPCache) + ip_len);
288     pos->ip = &pos[1];
289     GNUNET_memcpy (&pos[1],
290                    ip,
291                    ip_len);
292     pos->last_request = now;
293     pos->last_refresh = now;
294     pos->ip_len = ip_len;
295     pos->af = af;
296     GNUNET_CONTAINER_DLL_insert (cache_head,
297                                  cache_tail,
298                                  pos);
299     cache_resolve (pos);
300   }
301   if (NULL != pos->addr)
302     alen = strlen (pos->addr) + 1;
303   else
304     alen = 0;
305   mq = GNUNET_SERVICE_client_get_mq (client);
306   env = GNUNET_MQ_msg_extra (msg,
307                              alen,
308                              GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
309   GNUNET_memcpy (&msg[1],
310                  pos->addr,
311                  alen);
312   GNUNET_MQ_send (mq,
313                   env);
314   env = GNUNET_MQ_msg (msg,
315                        GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
316   GNUNET_MQ_notify_sent (env,
317                          &notify_service_client_done,
318                          client);
319   GNUNET_MQ_send (mq,
320                   env);
321 }
322
323
324 #if HAVE_GETADDRINFO
325 static int
326 getaddrinfo_resolve (struct GNUNET_MQ_Handle *mq,
327                      const char *hostname,
328                      int af)
329 {
330   int s;
331   struct addrinfo hints;
332   struct addrinfo *result;
333   struct addrinfo *pos;
334   struct GNUNET_MessageHeader *msg;
335   struct GNUNET_MQ_Envelope *env;
336
337 #ifdef WINDOWS
338   /* Due to a bug, getaddrinfo will not return a mix of different families */
339   if (AF_UNSPEC == af)
340   {
341     int ret1;
342     int ret2;
343     ret1 = getaddrinfo_resolve (mq,
344                                 hostname,
345                                 AF_INET);
346     ret2 = getaddrinfo_resolve (mq,
347                                 hostname,
348                                 AF_INET6);
349     if ( (ret1 == GNUNET_OK) ||
350          (ret2 == GNUNET_OK) )
351       return GNUNET_OK;
352     if ( (ret1 == GNUNET_SYSERR) ||
353          (ret2 == GNUNET_SYSERR) )
354       return GNUNET_SYSERR;
355     return GNUNET_NO;
356   }
357 #endif
358
359   memset (&hints,
360           0,
361           sizeof (struct addrinfo));
362   hints.ai_family = af;
363   hints.ai_socktype = SOCK_STREAM;      /* go for TCP */
364
365   if (0 != (s = getaddrinfo (hostname,
366                              NULL,
367                              &hints,
368                              &result)))
369   {
370     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
371                 _("Could not resolve `%s' (%s): %s\n"),
372                 hostname,
373                 (af ==
374                  AF_INET) ? "IPv4" : ((af == AF_INET6) ? "IPv6" : "any"),
375                 gai_strerror (s));
376     if ( (s == EAI_BADFLAGS) ||
377 #ifndef WINDOWS
378          (s == EAI_SYSTEM) ||
379 #endif
380          (s == EAI_MEMORY) )
381       return GNUNET_NO;         /* other function may still succeed */
382     return GNUNET_SYSERR;
383   }
384   if (NULL == result)
385     return GNUNET_SYSERR;
386   for (pos = result; pos != NULL; pos = pos->ai_next)
387   {
388     switch (pos->ai_family)
389     {
390     case AF_INET:
391       env = GNUNET_MQ_msg_extra (msg,
392                                  sizeof (struct in_addr),
393                                  GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
394       GNUNET_memcpy (&msg[1],
395                      &((struct sockaddr_in*) pos->ai_addr)->sin_addr,
396                      sizeof (struct in_addr));
397       GNUNET_MQ_send (mq,
398                       env);
399       break;
400     case AF_INET6:
401       env = GNUNET_MQ_msg_extra (msg,
402                                  sizeof (struct in6_addr),
403                                  GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
404       GNUNET_memcpy (&msg[1],
405                      &((struct sockaddr_in6*) pos->ai_addr)->sin6_addr,
406                      sizeof (struct in6_addr));
407       GNUNET_MQ_send (mq,
408                       env);
409       break;
410     default:
411       /* unsupported, skip */
412       break;
413     }
414   }
415   freeaddrinfo (result);
416   return GNUNET_OK;
417 }
418
419
420 #elif HAVE_GETHOSTBYNAME2
421
422
423 static int
424 gethostbyname2_resolve (struct GNUNET_MQ_Handle *mq,
425                         const char *hostname,
426                         int af)
427 {
428   struct hostent *hp;
429   int ret1;
430   int ret2;
431   struct GNUNET_MQ_Envelope *env;
432   struct GNUNET_MessageHeader *msg;
433
434 #ifdef WINDOWS
435   /* gethostbyname2() in plibc is a compat dummy that calls gethostbyname(). */
436   return GNUNET_NO;
437 #endif
438
439   if (af == AF_UNSPEC)
440   {
441     ret1 = gethostbyname2_resolve (mq,
442                                    hostname,
443                                    AF_INET);
444     ret2 = gethostbyname2_resolve (mq,
445                                    hostname,
446                                    AF_INET6);
447     if ( (ret1 == GNUNET_OK) ||
448          (ret2 == GNUNET_OK) )
449       return GNUNET_OK;
450     if ( (ret1 == GNUNET_SYSERR) ||
451          (ret2 == GNUNET_SYSERR) )
452       return GNUNET_SYSERR;
453     return GNUNET_NO;
454   }
455   hp = gethostbyname2 (hostname,
456                        af);
457   if (hp == NULL)
458   {
459     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
460                 _("Could not find IP of host `%s': %s\n"),
461                 hostname,
462                 hstrerror (h_errno));
463     return GNUNET_SYSERR;
464   }
465   GNUNET_assert (hp->h_addrtype == af);
466   switch (af)
467   {
468   case AF_INET:
469     GNUNET_assert (hp->h_length == sizeof (struct in_addr));
470     env = GNUNET_MQ_msg_extra (msg,
471                                hp->h_length,
472                                GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
473     GNUNET_memcpy (&msg[1],
474                    hp->h_addr_list[0],
475                    hp->h_length);
476     GNUNET_MQ_send (mq,
477                     env);
478     break;
479   case AF_INET6:
480     GNUNET_assert (hp->h_length == sizeof (struct in6_addr));
481     env = GNUNET_MQ_msg_extra (msg,
482                                hp->h_length,
483                                GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
484     GNUNET_memcpy (&msg[1],
485                    hp->h_addr_list[0],
486                    hp->h_length);
487     GNUNET_MQ_send (mq,
488                     env);
489     break;
490   default:
491     GNUNET_break (0);
492     return GNUNET_SYSERR;
493   }
494   return GNUNET_OK;
495 }
496
497 #elif HAVE_GETHOSTBYNAME
498
499
500 static int
501 gethostbyname_resolve (struct GNUNET_MQ_Handle *mq,
502                        const char *hostname)
503 {
504   struct hostent *hp;
505   struct GNUNET_MessageHeader *msg;
506   struct GNUNET_MQ_Envelope *env;
507
508   hp = GETHOSTBYNAME (hostname);
509   if (NULL == hp)
510   {
511     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
512                 _("Could not find IP of host `%s': %s\n"),
513                 hostname,
514                 hstrerror (h_errno));
515     return GNUNET_SYSERR;
516   }
517   if (hp->h_addrtype != AF_INET)
518   {
519     GNUNET_break (0);
520     return GNUNET_SYSERR;
521   }
522   GNUNET_assert (hp->h_length == sizeof (struct in_addr));
523   env = GNUNET_MQ_msg_extra (msg,
524                              hp->h_length,
525                              GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
526   GNUNET_memcpy (&msg[1],
527                  hp->h_addr_list[0],
528                  hp->h_length);
529   GNUNET_MQ_send (mq,
530                   env);
531   return GNUNET_OK;
532 }
533 #endif
534
535
536 /**
537  * Convert a string to an IP address.
538  *
539  * @param client where to send the IP address
540  * @param hostname the hostname to resolve
541  * @param af AF_INET or AF_INET6; use AF_UNSPEC for "any"
542  */
543 static void
544 get_ip_from_hostname (struct GNUNET_SERVICE_Client *client,
545                       const char *hostname,
546                       int af)
547 {
548   int ret;
549   struct GNUNET_MQ_Handle *mq;
550   struct GNUNET_MQ_Envelope *env;
551   struct GNUNET_MessageHeader *msg;
552
553   mq = GNUNET_SERVICE_client_get_mq (client);
554   ret = GNUNET_NO;
555 #if HAVE_GETADDRINFO
556   if (ret == GNUNET_NO)
557     ret = getaddrinfo_resolve (mq,
558                                hostname,
559                                af);
560 #elif HAVE_GETHOSTBYNAME2
561   if (ret == GNUNET_NO)
562     ret = gethostbyname2_resolve (mq,
563                                   hostname,
564                                   af);
565 #elif HAVE_GETHOSTBYNAME
566   if ( (ret == GNUNET_NO) &&
567        ( (af == AF_UNSPEC) ||
568          (af == PF_INET) ) )
569     gethostbyname_resolve (mq,
570                            hostname);
571 #endif
572   env = GNUNET_MQ_msg (msg,
573                        GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
574   GNUNET_MQ_notify_sent (env,
575                          &notify_service_client_done,
576                          client);
577   GNUNET_MQ_send (mq,
578                   env);
579 }
580
581
582 /**
583  * Verify well-formedness of GET-message.
584  *
585  * @param cls closure
586  * @param get the actual message
587  * @return #GNUNET_OK if @a get is well-formed
588  */
589 static int
590 check_get (void *cls,
591            const struct GNUNET_RESOLVER_GetMessage *get)
592 {
593   uint16_t size;
594   int direction;
595   int af;
596
597   size = ntohs (get->header.size) - sizeof (*get);
598   direction = ntohl (get->direction);
599   if (GNUNET_NO == direction)
600   {
601     /* IP from hostname */
602     const char *hostname;
603
604     hostname = (const char *) &get[1];
605     if (hostname[size - 1] != '\0')
606     {
607       GNUNET_break (0);
608       return GNUNET_SYSERR;
609     }
610     return GNUNET_OK;
611   }
612   af = ntohl (get->af);
613   switch (af)
614   {
615   case AF_INET:
616     if (size != sizeof (struct in_addr))
617     {
618       GNUNET_break (0);
619       return GNUNET_SYSERR;
620     }
621     break;
622   case AF_INET6:
623     if (size != sizeof (struct in6_addr))
624     {
625       GNUNET_break (0);
626       return GNUNET_SYSERR;
627     }
628     break;
629   default:
630     GNUNET_break (0);
631     return GNUNET_SYSERR;
632   }
633   return GNUNET_OK;
634 }
635   
636
637 /**
638  * Handle GET-message.
639  *
640  * @param cls identification of the client
641  * @param msg the actual message
642  */
643 static void
644 handle_get (void *cls,
645             const struct GNUNET_RESOLVER_GetMessage *msg)
646 {
647   struct GNUNET_SERVICE_Client *client = cls;
648   const void *ip;
649   int direction;
650   int af;
651
652   direction = ntohl (msg->direction);
653   af = ntohl (msg->af);
654   if (GNUNET_NO == direction)
655   {
656     /* IP from hostname */
657     const char *hostname;
658
659     hostname = (const char *) &msg[1];
660     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
661                 "Resolver asked to look up `%s'.\n",
662                 hostname);
663     get_ip_from_hostname (client,
664                           hostname,
665                           af);
666     return;
667   }
668   ip = &msg[1];
669   {
670     char buf[INET6_ADDRSTRLEN];
671
672     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
673                 "Resolver asked to look up IP address `%s'.\n",
674                 inet_ntop (af,
675                            ip,
676                            buf,
677                            sizeof (buf)));
678   }
679   get_ip_as_string (client,
680                     af,
681                     ip);
682 }
683
684
685 /**
686  * Callback called when a client connects to the service.
687  *
688  * @param cls closure for the service
689  * @param c the new client that connected to the service
690  * @param mq the message queue used to send messages to the client
691  * @return @a c
692  */
693 static void *
694 connect_cb (void *cls,
695             struct GNUNET_SERVICE_Client *c,
696             struct GNUNET_MQ_Handle *mq)
697 {
698   return c;
699 }
700
701
702 /**
703  * Callback called when a client disconnected from the service
704  *
705  * @param cls closure for the service
706  * @param c the client that disconnected
707  * @param internal_cls should be equal to @a c
708  */
709 static void
710 disconnect_cb (void *cls,
711                struct GNUNET_SERVICE_Client *c,
712                void *internal_cls)
713 {
714   GNUNET_assert (c == internal_cls);
715 }
716
717
718 /**
719  * Define "main" method using service macro.
720  */
721 GNUNET_SERVICE_MAIN
722 ("resolver",
723  GNUNET_SERVICE_OPTION_NONE,
724  NULL,
725  &connect_cb,
726  &disconnect_cb,
727  NULL,
728  GNUNET_MQ_hd_var_size (get,
729                         GNUNET_MESSAGE_TYPE_RESOLVER_REQUEST,
730                         struct GNUNET_RESOLVER_GetMessage,
731                         NULL),
732  GNUNET_MQ_handler_end ());
733
734
735 #if defined(LINUX) && defined(__GLIBC__)
736 #include <malloc.h>
737
738 /**
739  * MINIMIZE heap size (way below 128k) since this process doesn't need much.
740  */
741 void __attribute__ ((constructor))
742 GNUNET_RESOLVER_memory_init ()
743 {
744   mallopt (M_TRIM_THRESHOLD, 4 * 1024);
745   mallopt (M_TOP_PAD, 1 * 1024);
746   malloc_trim (0);
747 }
748 #endif
749
750
751 /**
752  * Free globals on exit.
753  */
754 void __attribute__ ((destructor))
755 GNUNET_RESOLVER_memory_done ()
756 {
757   struct IPCache *pos;
758
759   while (NULL != (pos = cache_head))
760   {
761     GNUNET_CONTAINER_DLL_remove (cache_head,
762                                  cache_tail,
763                                  pos);
764     GNUNET_free_non_null (pos->addr);
765     GNUNET_free (pos);
766   }
767 }
768
769
770 /* end of gnunet-service-resolver.c */