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