paragraph for gnunet devs that don't know how to use the web
[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 it
6      under the terms of the GNU Affero General Public License as published
7      by the Free Software Foundation, either version 3 of the License,
8      or (at your 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      Affero General Public License for more details.
14     
15      You should have received a copy of the GNU Affero General Public License
16      along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 /**
20  * @file util/gnunet-service-resolver.c
21  * @brief code to do DNS resolution
22  * @author Christian Grothoff
23  */
24 #include "platform.h"
25 #include "gnunet_util_lib.h"
26 #include "gnunet_protocols.h"
27 #include "gnunet_statistics_service.h"
28 #include "resolver.h"
29
30 /**
31  * A cached DNS lookup result (for reverse lookup).
32  */
33 struct IPCache
34 {
35   /**
36    * This is a doubly linked list.
37    */
38   struct IPCache *next;
39
40   /**
41    * This is a doubly linked list.
42    */
43   struct IPCache *prev;
44
45   /**
46    * Hostname in human-readable form.
47    */
48   char *addr;
49
50   /**
51    * Binary IP address, allocated at the end of this struct.
52    */
53   const void *ip;
54
55   /**
56    * Last time this entry was updated.
57    */
58   struct GNUNET_TIME_Absolute last_refresh;
59
60   /**
61    * Last time this entry was requested.
62    */
63   struct GNUNET_TIME_Absolute last_request;
64
65   /**
66    * Number of bytes in ip.
67    */
68   size_t ip_len;
69
70   /**
71    * Address family of the IP.
72    */
73   int af;
74 };
75
76
77 /**
78  * Start of the linked list of cached DNS lookup results.
79  */
80 static struct IPCache *cache_head;
81
82 /**
83  * Tail of the linked list of cached DNS lookup results.
84  */
85 static struct IPCache *cache_tail;
86
87
88 #if HAVE_GETNAMEINFO
89 /**
90  * Resolve the given request using getnameinfo
91  *
92  * @param cache the request to resolve (and where to store the result)
93  */
94 static void
95 getnameinfo_resolve (struct IPCache *cache)
96 {
97   char hostname[256];
98   const struct sockaddr *sa;
99   struct sockaddr_in v4;
100   struct sockaddr_in6 v6;
101   size_t salen;
102   int ret;
103
104   switch (cache->af)
105   {
106   case AF_INET:
107     GNUNET_assert (cache->ip_len == sizeof (struct in_addr));
108     sa = (const struct sockaddr*) &v4;
109     memset (&v4, 0, sizeof (v4));
110     v4.sin_addr = * (const struct in_addr*) cache->ip;
111     v4.sin_family = AF_INET;
112 #if HAVE_SOCKADDR_IN_SIN_LEN
113     v4.sin_len = sizeof (v4);
114 #endif
115     salen = sizeof (v4);
116     break;
117   case AF_INET6:
118     GNUNET_assert (cache->ip_len == sizeof (struct in6_addr));
119     sa = (const struct sockaddr*) &v6;
120     memset (&v6, 0, sizeof (v6));
121     v6.sin6_addr = * (const struct in6_addr*) cache->ip;
122     v6.sin6_family = AF_INET6;
123 #if HAVE_SOCKADDR_IN_SIN_LEN
124     v6.sin6_len = sizeof (v6);
125 #endif
126     salen = sizeof (v6);
127     break;
128   default:
129     GNUNET_assert (0);
130   }
131
132   if (0 ==
133       (ret = getnameinfo (sa, salen,
134                           hostname, sizeof (hostname),
135                           NULL,
136                           0, 0)))
137   {
138     cache->addr = GNUNET_strdup (hostname);
139   }
140   else
141   {
142     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
143                 "getnameinfo failed: %s\n",
144                 gai_strerror (ret));
145   }
146 }
147 #endif
148
149
150 #if HAVE_GETHOSTBYADDR
151 /**
152  * Resolve the given request using gethostbyaddr
153  *
154  * @param cache the request to resolve (and where to store the result)
155  */
156 static void
157 gethostbyaddr_resolve (struct IPCache *cache)
158 {
159   struct hostent *ent;
160
161   ent = gethostbyaddr (cache->ip,
162                        cache->ip_len,
163                        cache->af);
164   if (NULL != ent)
165   {
166     cache->addr = GNUNET_strdup (ent->h_name);
167   }
168   else
169   {
170     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
171                 "gethostbyaddr failed: %s\n",
172                 hstrerror (h_errno));
173   }
174 }
175 #endif
176
177
178 /**
179  * Resolve the given request using the available methods.
180  *
181  * @param cache the request to resolve (and where to store the result)
182  */
183 static void
184 cache_resolve (struct IPCache *cache)
185 {
186 #if HAVE_GETNAMEINFO
187   if (NULL == cache->addr)
188     getnameinfo_resolve (cache);
189 #endif
190 #if HAVE_GETHOSTBYADDR
191   if (NULL == cache->addr)
192     gethostbyaddr_resolve (cache);
193 #endif
194 }
195
196
197 /**
198  * Function called after the replies for the request have all
199  * been transmitted to the client, and we can now read the next
200  * request from the client.
201  *
202  * @param cls the `struct GNUNET_SERVICE_Client` to continue with
203  */
204 static void
205 notify_service_client_done (void *cls)
206 {
207   struct GNUNET_SERVICE_Client *client = cls;
208
209   GNUNET_SERVICE_client_continue (client);
210 }
211
212
213 /**
214  * Get an IP address as a string (works for both IPv4 and IPv6).  Note
215  * that the resolution happens asynchronously and that the first call
216  * may not immediately result in the FQN (but instead in a
217  * human-readable IP address).
218  *
219  * @param client handle to the client making the request (for sending the reply)
220  * @param af AF_INET or AF_INET6
221  * @param ip `struct in_addr` or `struct in6_addr`
222  */
223 static void
224 get_ip_as_string (struct GNUNET_SERVICE_Client *client,
225                   int af,
226                   const void *ip)
227 {
228   struct IPCache *pos;
229   struct IPCache *next;
230   struct GNUNET_TIME_Absolute now;
231   struct GNUNET_MQ_Envelope *env;
232   struct GNUNET_MQ_Handle *mq;
233   struct GNUNET_MessageHeader *msg;
234   size_t ip_len;
235   struct in6_addr ix;
236   size_t alen;
237
238   switch (af)
239   {
240   case AF_INET:
241     ip_len = sizeof (struct in_addr);
242     break;
243   case AF_INET6:
244     ip_len = sizeof (struct in6_addr);
245     break;
246   default:
247     GNUNET_assert (0);
248   }
249   now = GNUNET_TIME_absolute_get ();
250   next = cache_head;
251   while ( (NULL != (pos = next)) &&
252           ( (pos->af != af) ||
253             (pos->ip_len != ip_len) ||
254             (0 != memcmp (pos->ip, ip, ip_len))) )
255   {
256     next = pos->next;
257     if (GNUNET_TIME_absolute_get_duration (pos->last_request).rel_value_us <
258         60 * 60 * 1000 * 1000LL)
259     {
260       GNUNET_CONTAINER_DLL_remove (cache_head,
261                                    cache_tail,
262                                    pos);
263       GNUNET_free_non_null (pos->addr);
264       GNUNET_free (pos);
265       continue;
266     }
267   }
268   if (NULL != pos)
269   {
270     if ( (1 == inet_pton (af,
271                           pos->ip,
272                           &ix)) &&
273          (GNUNET_TIME_absolute_get_duration (pos->last_request).rel_value_us >
274           120 * 1000 * 1000LL) )
275     {
276       /* try again if still numeric AND 2 minutes have expired */
277       GNUNET_free_non_null (pos->addr);
278       pos->addr = NULL;
279       cache_resolve (pos);
280       pos->last_request = now;
281     }
282   }
283   else
284   {
285     pos = GNUNET_malloc (sizeof (struct IPCache) + ip_len);
286     pos->ip = &pos[1];
287     GNUNET_memcpy (&pos[1],
288                    ip,
289                    ip_len);
290     pos->last_request = now;
291     pos->last_refresh = now;
292     pos->ip_len = ip_len;
293     pos->af = af;
294     GNUNET_CONTAINER_DLL_insert (cache_head,
295                                  cache_tail,
296                                  pos);
297     cache_resolve (pos);
298   }
299   if (NULL != pos->addr)
300     alen = strlen (pos->addr) + 1;
301   else
302     alen = 0;
303   mq = GNUNET_SERVICE_client_get_mq (client);
304   env = GNUNET_MQ_msg_extra (msg,
305                              alen,
306                              GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
307   GNUNET_memcpy (&msg[1],
308                  pos->addr,
309                  alen);
310   GNUNET_MQ_send (mq,
311                   env);
312   env = GNUNET_MQ_msg (msg,
313                        GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
314   GNUNET_MQ_notify_sent (env,
315                          &notify_service_client_done,
316                          client);
317   GNUNET_MQ_send (mq,
318                   env);
319 }
320
321
322 #if HAVE_GETADDRINFO
323 static int
324 getaddrinfo_resolve (struct GNUNET_MQ_Handle *mq,
325                      const char *hostname,
326                      int af)
327 {
328   int s;
329   struct addrinfo hints;
330   struct addrinfo *result;
331   struct addrinfo *pos;
332   struct GNUNET_MessageHeader *msg;
333   struct GNUNET_MQ_Envelope *env;
334
335 #ifdef WINDOWS
336   /* Due to a bug, getaddrinfo will not return a mix of different families */
337   if (AF_UNSPEC == af)
338   {
339     int ret1;
340     int ret2;
341     ret1 = getaddrinfo_resolve (mq,
342                                 hostname,
343                                 AF_INET);
344     ret2 = getaddrinfo_resolve (mq,
345                                 hostname,
346                                 AF_INET6);
347     if ( (ret1 == GNUNET_OK) ||
348          (ret2 == GNUNET_OK) )
349       return GNUNET_OK;
350     if ( (ret1 == GNUNET_SYSERR) ||
351          (ret2 == GNUNET_SYSERR) )
352       return GNUNET_SYSERR;
353     return GNUNET_NO;
354   }
355 #endif
356
357   memset (&hints,
358           0,
359           sizeof (struct addrinfo));
360   hints.ai_family = af;
361   hints.ai_socktype = SOCK_STREAM;      /* go for TCP */
362
363   if (0 != (s = getaddrinfo (hostname,
364                              NULL,
365                              &hints,
366                              &result)))
367   {
368     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
369                 _("Could not resolve `%s' (%s): %s\n"),
370                 hostname,
371                 (af ==
372                  AF_INET) ? "IPv4" : ((af == AF_INET6) ? "IPv6" : "any"),
373                 gai_strerror (s));
374     if ( (s == EAI_BADFLAGS) ||
375 #ifndef WINDOWS
376          (s == EAI_SYSTEM) ||
377 #endif
378          (s == EAI_MEMORY) )
379       return GNUNET_NO;         /* other function may still succeed */
380     return GNUNET_SYSERR;
381   }
382   if (NULL == result)
383     return GNUNET_SYSERR;
384   for (pos = result; pos != NULL; pos = pos->ai_next)
385   {
386     switch (pos->ai_family)
387     {
388     case AF_INET:
389       env = GNUNET_MQ_msg_extra (msg,
390                                  sizeof (struct in_addr),
391                                  GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
392       GNUNET_memcpy (&msg[1],
393                      &((struct sockaddr_in*) pos->ai_addr)->sin_addr,
394                      sizeof (struct in_addr));
395       GNUNET_MQ_send (mq,
396                       env);
397       break;
398     case AF_INET6:
399       env = GNUNET_MQ_msg_extra (msg,
400                                  sizeof (struct in6_addr),
401                                  GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
402       GNUNET_memcpy (&msg[1],
403                      &((struct sockaddr_in6*) pos->ai_addr)->sin6_addr,
404                      sizeof (struct in6_addr));
405       GNUNET_MQ_send (mq,
406                       env);
407       break;
408     default:
409       /* unsupported, skip */
410       break;
411     }
412   }
413   freeaddrinfo (result);
414   return GNUNET_OK;
415 }
416
417
418 #elif HAVE_GETHOSTBYNAME2
419
420
421 static int
422 gethostbyname2_resolve (struct GNUNET_MQ_Handle *mq,
423                         const char *hostname,
424                         int af)
425 {
426   struct hostent *hp;
427   int ret1;
428   int ret2;
429   struct GNUNET_MQ_Envelope *env;
430   struct GNUNET_MessageHeader *msg;
431
432 #ifdef WINDOWS
433   /* gethostbyname2() in plibc is a compat dummy that calls gethostbyname(). */
434   return GNUNET_NO;
435 #endif
436
437   if (af == AF_UNSPEC)
438   {
439     ret1 = gethostbyname2_resolve (mq,
440                                    hostname,
441                                    AF_INET);
442     ret2 = gethostbyname2_resolve (mq,
443                                    hostname,
444                                    AF_INET6);
445     if ( (ret1 == GNUNET_OK) ||
446          (ret2 == GNUNET_OK) )
447       return GNUNET_OK;
448     if ( (ret1 == GNUNET_SYSERR) ||
449          (ret2 == GNUNET_SYSERR) )
450       return GNUNET_SYSERR;
451     return GNUNET_NO;
452   }
453   hp = gethostbyname2 (hostname,
454                        af);
455   if (hp == NULL)
456   {
457     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
458                 _("Could not find IP of host `%s': %s\n"),
459                 hostname,
460                 hstrerror (h_errno));
461     return GNUNET_SYSERR;
462   }
463   GNUNET_assert (hp->h_addrtype == af);
464   switch (af)
465   {
466   case AF_INET:
467     GNUNET_assert (hp->h_length == sizeof (struct in_addr));
468     env = GNUNET_MQ_msg_extra (msg,
469                                hp->h_length,
470                                GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
471     GNUNET_memcpy (&msg[1],
472                    hp->h_addr_list[0],
473                    hp->h_length);
474     GNUNET_MQ_send (mq,
475                     env);
476     break;
477   case AF_INET6:
478     GNUNET_assert (hp->h_length == sizeof (struct in6_addr));
479     env = GNUNET_MQ_msg_extra (msg,
480                                hp->h_length,
481                                GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
482     GNUNET_memcpy (&msg[1],
483                    hp->h_addr_list[0],
484                    hp->h_length);
485     GNUNET_MQ_send (mq,
486                     env);
487     break;
488   default:
489     GNUNET_break (0);
490     return GNUNET_SYSERR;
491   }
492   return GNUNET_OK;
493 }
494
495 #elif HAVE_GETHOSTBYNAME
496
497
498 static int
499 gethostbyname_resolve (struct GNUNET_MQ_Handle *mq,
500                        const char *hostname)
501 {
502   struct hostent *hp;
503   struct GNUNET_MessageHeader *msg;
504   struct GNUNET_MQ_Envelope *env;
505
506   hp = GETHOSTBYNAME (hostname);
507   if (NULL == hp)
508   {
509     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
510                 _("Could not find IP of host `%s': %s\n"),
511                 hostname,
512                 hstrerror (h_errno));
513     return GNUNET_SYSERR;
514   }
515   if (hp->h_addrtype != AF_INET)
516   {
517     GNUNET_break (0);
518     return GNUNET_SYSERR;
519   }
520   GNUNET_assert (hp->h_length == sizeof (struct in_addr));
521   env = GNUNET_MQ_msg_extra (msg,
522                              hp->h_length,
523                              GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
524   GNUNET_memcpy (&msg[1],
525                  hp->h_addr_list[0],
526                  hp->h_length);
527   GNUNET_MQ_send (mq,
528                   env);
529   return GNUNET_OK;
530 }
531 #endif
532
533
534 /**
535  * Convert a string to an IP address.
536  *
537  * @param client where to send the IP address
538  * @param hostname the hostname to resolve
539  * @param af AF_INET or AF_INET6; use AF_UNSPEC for "any"
540  */
541 static void
542 get_ip_from_hostname (struct GNUNET_SERVICE_Client *client,
543                       const char *hostname,
544                       int af)
545 {
546   int ret;
547   struct GNUNET_MQ_Handle *mq;
548   struct GNUNET_MQ_Envelope *env;
549   struct GNUNET_MessageHeader *msg;
550
551   mq = GNUNET_SERVICE_client_get_mq (client);
552   ret = GNUNET_NO;
553 #if HAVE_GETADDRINFO
554   if (ret == GNUNET_NO)
555     ret = getaddrinfo_resolve (mq,
556                                hostname,
557                                af);
558 #elif HAVE_GETHOSTBYNAME2
559   if (ret == GNUNET_NO)
560     ret = gethostbyname2_resolve (mq,
561                                   hostname,
562                                   af);
563 #elif HAVE_GETHOSTBYNAME
564   if ( (ret == GNUNET_NO) &&
565        ( (af == AF_UNSPEC) ||
566          (af == PF_INET) ) )
567     gethostbyname_resolve (mq,
568                            hostname);
569 #endif
570   env = GNUNET_MQ_msg (msg,
571                        GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
572   GNUNET_MQ_notify_sent (env,
573                          &notify_service_client_done,
574                          client);
575   GNUNET_MQ_send (mq,
576                   env);
577 }
578
579
580 /**
581  * Verify well-formedness of GET-message.
582  *
583  * @param cls closure, unused
584  * @param get the actual message
585  * @return #GNUNET_OK if @a get is well-formed
586  */
587 static int
588 check_get (void *cls,
589            const struct GNUNET_RESOLVER_GetMessage *get)
590 {
591   uint16_t size;
592   int direction;
593   int af;
594
595   (void) cls;
596   size = ntohs (get->header.size) - sizeof (*get);
597   direction = ntohl (get->direction);
598   if (GNUNET_NO == direction)
599   {
600     /* IP from hostname */
601     const char *hostname;
602
603     hostname = (const char *) &get[1];
604     if (hostname[size - 1] != '\0')
605     {
606       GNUNET_break (0);
607       return GNUNET_SYSERR;
608     }
609     return GNUNET_OK;
610   }
611   af = ntohl (get->af);
612   switch (af)
613   {
614   case AF_INET:
615     if (size != sizeof (struct in_addr))
616     {
617       GNUNET_break (0);
618       return GNUNET_SYSERR;
619     }
620     break;
621   case AF_INET6:
622     if (size != sizeof (struct in6_addr))
623     {
624       GNUNET_break (0);
625       return GNUNET_SYSERR;
626     }
627     break;
628   default:
629     GNUNET_break (0);
630     return GNUNET_SYSERR;
631   }
632   return GNUNET_OK;
633 }
634
635
636 /**
637  * Handle GET-message.
638  *
639  * @param cls identification of the client
640  * @param msg the actual message
641  */
642 static void
643 handle_get (void *cls,
644             const struct GNUNET_RESOLVER_GetMessage *msg)
645 {
646   struct GNUNET_SERVICE_Client *client = cls;
647   const void *ip;
648   int direction;
649   int af;
650
651   direction = ntohl (msg->direction);
652   af = ntohl (msg->af);
653   if (GNUNET_NO == direction)
654   {
655     /* IP from hostname */
656     const char *hostname;
657
658     hostname = (const char *) &msg[1];
659     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
660                 "Resolver asked to look up `%s'.\n",
661                 hostname);
662     get_ip_from_hostname (client,
663                           hostname,
664                           af);
665     return;
666   }
667   ip = &msg[1];
668
669 #if !defined(GNUNET_CULL_LOGGING)
670   {
671     char buf[INET6_ADDRSTRLEN];
672
673     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
674                 "Resolver asked to look up IP address `%s'.\n",
675                 inet_ntop (af,
676                            ip,
677                            buf,
678                            sizeof (buf)));
679   }
680 #endif
681   get_ip_as_string (client,
682                     af,
683                     ip);
684 }
685
686
687 /**
688  * Callback called when a client connects to the service.
689  *
690  * @param cls closure for the service, unused
691  * @param c the new client that connected to the service
692  * @param mq the message queue used to send messages to the client
693  * @return @a c
694  */
695 static void *
696 connect_cb (void *cls,
697             struct GNUNET_SERVICE_Client *c,
698             struct GNUNET_MQ_Handle *mq)
699 {
700   (void) cls;
701   (void) mq;
702
703   return c;
704 }
705
706
707 /**
708  * Callback called when a client disconnected from the service
709  *
710  * @param cls closure for the service
711  * @param c the client that disconnected
712  * @param internal_cls should be equal to @a c
713  */
714 static void
715 disconnect_cb (void *cls,
716                struct GNUNET_SERVICE_Client *c,
717                void *internal_cls)
718 {
719   (void) cls;
720
721   GNUNET_assert (c == internal_cls);
722 }
723
724
725 /**
726  * Define "main" method using service macro.
727  */
728 GNUNET_SERVICE_MAIN
729 ("resolver",
730  GNUNET_SERVICE_OPTION_NONE,
731  NULL,
732  &connect_cb,
733  &disconnect_cb,
734  NULL,
735  GNUNET_MQ_hd_var_size (get,
736                         GNUNET_MESSAGE_TYPE_RESOLVER_REQUEST,
737                         struct GNUNET_RESOLVER_GetMessage,
738                         NULL),
739  GNUNET_MQ_handler_end ());
740
741
742 #if defined(LINUX) && defined(__GLIBC__)
743 #include <malloc.h>
744
745 /**
746  * MINIMIZE heap size (way below 128k) since this process doesn't need much.
747  */
748 void __attribute__ ((constructor))
749 GNUNET_RESOLVER_memory_init ()
750 {
751   mallopt (M_TRIM_THRESHOLD, 4 * 1024);
752   mallopt (M_TOP_PAD, 1 * 1024);
753   malloc_trim (0);
754 }
755 #endif
756
757
758 /**
759  * Free globals on exit.
760  */
761 void __attribute__ ((destructor))
762 GNUNET_RESOLVER_memory_done ()
763 {
764   struct IPCache *pos;
765
766   while (NULL != (pos = cache_head))
767   {
768     GNUNET_CONTAINER_DLL_remove (cache_head,
769                                  cache_tail,
770                                  pos);
771     GNUNET_free_non_null (pos->addr);
772     GNUNET_free (pos);
773   }
774 }
775
776
777 /* end of gnunet-service-resolver.c */