use the asynchronous DNS resolution API (getaddrinfo_a) in the resolver module
[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  * Pipe for asynchronously notifying about resolve result
89  */
90 static struct GNUNET_DISK_PipeHandle *resolve_result_pipe;
91
92 /**
93  * Task for reading from resolve_result_pipe
94  */
95 static struct GNUNET_SCHEDULER_Task *resolve_result_pipe_task;
96
97
98 #if HAVE_GETNAMEINFO
99 /**
100  * Resolve the given request using getnameinfo
101  *
102  * @param cache the request to resolve (and where to store the result)
103  */
104 static void
105 getnameinfo_resolve (struct IPCache *cache)
106 {
107   char hostname[256];
108   const struct sockaddr *sa;
109   struct sockaddr_in v4;
110   struct sockaddr_in6 v6;
111   size_t salen;
112   int ret;
113
114   switch (cache->af)
115   {
116   case AF_INET:
117     GNUNET_assert (cache->ip_len == sizeof (struct in_addr));
118     sa = (const struct sockaddr*) &v4;
119     memset (&v4, 0, sizeof (v4));
120     v4.sin_addr = * (const struct in_addr*) cache->ip;
121     v4.sin_family = AF_INET;
122 #if HAVE_SOCKADDR_IN_SIN_LEN
123     v4.sin_len = sizeof (v4);
124 #endif
125     salen = sizeof (v4);
126     break;
127   case AF_INET6:
128     GNUNET_assert (cache->ip_len == sizeof (struct in6_addr));
129     sa = (const struct sockaddr*) &v6;
130     memset (&v6, 0, sizeof (v6));
131     v6.sin6_addr = * (const struct in6_addr*) cache->ip;
132     v6.sin6_family = AF_INET6;
133 #if HAVE_SOCKADDR_IN_SIN_LEN
134     v6.sin6_len = sizeof (v6);
135 #endif
136     salen = sizeof (v6);
137     break;
138   default:
139     GNUNET_assert (0);
140   }
141
142   if (0 ==
143       (ret = getnameinfo (sa, salen,
144                           hostname, sizeof (hostname),
145                           NULL,
146                           0, 0)))
147   {
148     cache->addr = GNUNET_strdup (hostname);
149   }
150   else
151   {
152     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
153                 "getnameinfo failed: %s\n",
154                 gai_strerror (ret));
155   }
156 }
157 #endif
158
159
160 #if HAVE_GETHOSTBYADDR
161 /**
162  * Resolve the given request using gethostbyaddr
163  *
164  * @param cache the request to resolve (and where to store the result)
165  */
166 static void
167 gethostbyaddr_resolve (struct IPCache *cache)
168 {
169   struct hostent *ent;
170
171   ent = gethostbyaddr (cache->ip,
172                        cache->ip_len,
173                        cache->af);
174   if (NULL != ent)
175   {
176     cache->addr = GNUNET_strdup (ent->h_name);
177   }
178   else
179   {
180     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
181                 "gethostbyaddr failed: %s\n",
182                 hstrerror (h_errno));
183   }
184 }
185 #endif
186
187
188 /**
189  * Resolve the given request using the available methods.
190  *
191  * @param cache the request to resolve (and where to store the result)
192  */
193 static void
194 cache_resolve (struct IPCache *cache)
195 {
196 #if HAVE_GETNAMEINFO
197   if (NULL == cache->addr)
198     getnameinfo_resolve (cache);
199 #endif
200 #if HAVE_GETHOSTBYADDR
201   if (NULL == cache->addr)
202     gethostbyaddr_resolve (cache);
203 #endif
204 }
205
206
207 /**
208  * Function called after the replies for the request have all
209  * been transmitted to the client, and we can now read the next
210  * request from the client.
211  *
212  * @param cls the `struct GNUNET_SERVICE_Client` to continue with
213  */
214 static void
215 notify_service_client_done (void *cls)
216 {
217   struct GNUNET_SERVICE_Client *client = cls;
218
219   GNUNET_SERVICE_client_continue (client);
220 }
221
222
223 /**
224  * Get an IP address as a string (works for both IPv4 and IPv6).  Note
225  * that the resolution happens asynchronously and that the first call
226  * may not immediately result in the FQN (but instead in a
227  * human-readable IP address).
228  *
229  * @param client handle to the client making the request (for sending the reply)
230  * @param af AF_INET or AF_INET6
231  * @param ip `struct in_addr` or `struct in6_addr`
232  */
233 static void
234 get_ip_as_string (struct GNUNET_SERVICE_Client *client,
235                   int af,
236                   const void *ip,
237                   uint32_t request_id)
238 {
239   struct IPCache *pos;
240   struct IPCache *next;
241   struct GNUNET_TIME_Absolute now;
242   struct GNUNET_MQ_Envelope *env;
243   struct GNUNET_MQ_Handle *mq;
244   struct GNUNET_RESOLVER_ResponseMessage *msg;
245   size_t ip_len;
246   struct in6_addr ix;
247   size_t alen;
248
249   switch (af)
250   {
251   case AF_INET:
252     ip_len = sizeof (struct in_addr);
253     break;
254   case AF_INET6:
255     ip_len = sizeof (struct in6_addr);
256     break;
257   default:
258     GNUNET_assert (0);
259   }
260   now = GNUNET_TIME_absolute_get ();
261   next = cache_head;
262   while ( (NULL != (pos = next)) &&
263           ( (pos->af != af) ||
264             (pos->ip_len != ip_len) ||
265             (0 != memcmp (pos->ip, ip, ip_len))) )
266   {
267     next = pos->next;
268     if (GNUNET_TIME_absolute_get_duration (pos->last_request).rel_value_us <
269         60 * 60 * 1000 * 1000LL)
270     {
271       GNUNET_CONTAINER_DLL_remove (cache_head,
272                                    cache_tail,
273                                    pos);
274       GNUNET_free_non_null (pos->addr);
275       GNUNET_free (pos);
276       continue;
277     }
278   }
279   if (NULL != pos)
280   {
281     if ( (1 == inet_pton (af,
282                           pos->ip,
283                           &ix)) &&
284          (GNUNET_TIME_absolute_get_duration (pos->last_request).rel_value_us >
285           120 * 1000 * 1000LL) )
286     {
287       /* try again if still numeric AND 2 minutes have expired */
288       GNUNET_free_non_null (pos->addr);
289       pos->addr = NULL;
290       cache_resolve (pos);
291       pos->last_request = now;
292     }
293   }
294   else
295   {
296     pos = GNUNET_malloc (sizeof (struct IPCache) + ip_len);
297     pos->ip = &pos[1];
298     GNUNET_memcpy (&pos[1],
299                    ip,
300                    ip_len);
301     pos->last_request = now;
302     pos->last_refresh = now;
303     pos->ip_len = ip_len;
304     pos->af = af;
305     GNUNET_CONTAINER_DLL_insert (cache_head,
306                                  cache_tail,
307                                  pos);
308     cache_resolve (pos);
309   }
310   if (NULL != pos->addr)
311     alen = strlen (pos->addr) + 1;
312   else
313     alen = 0;
314   mq = GNUNET_SERVICE_client_get_mq (client);
315   env = GNUNET_MQ_msg_extra (msg,
316                              alen,
317                              GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
318   msg->id = request_id;
319   GNUNET_memcpy (&msg[1],
320                  pos->addr,
321                  alen);
322   GNUNET_MQ_send (mq,
323                   env);
324   // send end message
325   env = GNUNET_MQ_msg (msg,
326                        GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
327   msg->id = request_id;
328   GNUNET_MQ_notify_sent (env,
329                          &notify_service_client_done,
330                          client);
331   GNUNET_MQ_send (mq,
332                   env);
333 }
334
335
336 #if HAVE_GETADDRINFO_A
337 struct AsyncCls
338 {
339   struct gaicb *host;
340   struct sigevent *sig;
341   struct GNUNET_MQ_Handle *mq;
342   uint32_t request_id;
343 };
344
345
346 static void
347 resolve_result_pipe_cb (void *cls)
348 {
349   struct AsyncCls *async_cls;
350   struct gaicb *host;
351   struct GNUNET_RESOLVER_ResponseMessage *msg;
352   struct GNUNET_MQ_Envelope *env;
353
354   GNUNET_DISK_file_read (GNUNET_DISK_pipe_handle (resolve_result_pipe,
355                                                   GNUNET_DISK_PIPE_END_READ),
356                          &async_cls,
357                          sizeof (struct AsyncCls *));
358   resolve_result_pipe_task =
359     GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
360                                     GNUNET_DISK_pipe_handle (resolve_result_pipe,
361                                                              GNUNET_DISK_PIPE_END_READ),
362                                     &resolve_result_pipe_cb,
363                                     NULL);
364   host = async_cls->host;
365   for (struct addrinfo *pos = host->ar_result; pos != NULL; pos = pos->ai_next)
366   {
367     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
368                 "Lookup result for hostname %s: %s (request ID %u)\n",
369                 host->ar_name,
370                 GNUNET_a2s (pos->ai_addr, pos->ai_addrlen),
371                 async_cls->request_id);
372     switch (pos->ai_family)
373     {
374     case AF_INET:
375       env = GNUNET_MQ_msg_extra (msg,
376                                  sizeof (struct in_addr),
377                                  GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
378       msg->id = async_cls->request_id;
379       GNUNET_memcpy (&msg[1],
380                      &((struct sockaddr_in*) pos->ai_addr)->sin_addr,
381                      sizeof (struct in_addr));
382       GNUNET_MQ_send (async_cls->mq,
383                       env);
384       break;
385     case AF_INET6:
386       env = GNUNET_MQ_msg_extra (msg,
387                                  sizeof (struct in6_addr),
388                                  GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
389       msg->id = async_cls->request_id;
390       GNUNET_memcpy (&msg[1],
391                      &((struct sockaddr_in6*) pos->ai_addr)->sin6_addr,
392                      sizeof (struct in6_addr));
393       GNUNET_MQ_send (async_cls->mq,
394                       env);
395       break;
396     default:
397       /* unsupported, skip */
398       break;
399     }
400   }
401   // send end message
402   env = GNUNET_MQ_msg (msg,
403                        GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
404   msg->id = async_cls->request_id;
405   GNUNET_MQ_send (async_cls->mq,
406                   env);
407   freeaddrinfo (host->ar_result);
408   GNUNET_free ((struct gaicb *)host->ar_request); // free hints
409   GNUNET_free (host);
410   GNUNET_free (async_cls->sig);
411   GNUNET_free (async_cls);
412 }
413
414
415 static void
416 handle_async_result (union sigval val) 
417 {
418   GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle (resolve_result_pipe,
419                                                    GNUNET_DISK_PIPE_END_WRITE),
420                           &val.sival_ptr,
421                           sizeof (val.sival_ptr));
422 }
423
424
425 static int
426 getaddrinfo_a_resolve (struct GNUNET_MQ_Handle *mq,
427                        const char *hostname,
428                        int af,
429                        uint32_t request_id)
430 {
431   int ret;
432   struct gaicb *host;
433   struct addrinfo *hints; 
434   struct sigevent *sig;
435   struct AsyncCls *async_cls;
436
437   host = GNUNET_new (struct gaicb);
438   hints = GNUNET_new (struct addrinfo);
439   sig = GNUNET_new (struct sigevent);
440   async_cls = GNUNET_new (struct AsyncCls);
441   memset (hints,
442           0,
443           sizeof (struct addrinfo));
444   memset (sig,
445           0,
446           sizeof (struct sigevent));
447   hints->ai_family = af;
448   hints->ai_socktype = SOCK_STREAM;      /* go for TCP */
449   host->ar_name = hostname;
450   host->ar_service = NULL;
451   host->ar_request = hints;
452   host->ar_result = NULL;
453   sig->sigev_notify = SIGEV_THREAD;
454   sig->sigev_value.sival_ptr = async_cls;
455   sig->sigev_notify_function = &handle_async_result; 
456   async_cls->host = host;
457   async_cls->sig = sig;
458   async_cls->mq = mq;
459   async_cls->request_id = request_id;
460   ret = getaddrinfo_a (GAI_NOWAIT,
461                        &host,
462                        1,
463                        sig);
464   if (0 != ret)
465     return GNUNET_SYSERR;
466   return GNUNET_OK;
467 }
468
469
470 #elif HAVE_GETADDRINFO
471 static int
472 getaddrinfo_resolve (struct GNUNET_MQ_Handle *mq,
473                      const char *hostname,
474                      int af,
475                      uint32_t request_id)
476 {
477   int s;
478   struct addrinfo hints;
479   struct addrinfo *result;
480   struct addrinfo *pos;
481   struct GNUNET_RESOLVER_ResponseMessage *msg;
482   struct GNUNET_MQ_Envelope *env;
483
484 #ifdef WINDOWS
485   /* Due to a bug, getaddrinfo will not return a mix of different families */
486   if (AF_UNSPEC == af)
487   {
488     int ret1;
489     int ret2;
490     ret1 = getaddrinfo_resolve (mq,
491                                 hostname,
492                                 AF_INET,
493                                 request_id);
494     ret2 = getaddrinfo_resolve (mq,
495                                 hostname,
496                                 AF_INET6,
497                                 request_id);
498     if ( (ret1 == GNUNET_OK) ||
499          (ret2 == GNUNET_OK) )
500       return GNUNET_OK;
501     if ( (ret1 == GNUNET_SYSERR) ||
502          (ret2 == GNUNET_SYSERR) )
503       return GNUNET_SYSERR;
504     return GNUNET_NO;
505   }
506 #endif
507
508   memset (&hints,
509           0,
510           sizeof (struct addrinfo));
511   hints.ai_family = af;
512   hints.ai_socktype = SOCK_STREAM;      /* go for TCP */
513
514   if (0 != (s = getaddrinfo (hostname,
515                              NULL,
516                              &hints,
517                              &result)))
518   {
519     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
520                 _("Could not resolve `%s' (%s): %s\n"),
521                 hostname,
522                 (af ==
523                  AF_INET) ? "IPv4" : ((af == AF_INET6) ? "IPv6" : "any"),
524                 gai_strerror (s));
525     if ( (s == EAI_BADFLAGS) ||
526 #ifndef WINDOWS
527          (s == EAI_SYSTEM) ||
528 #endif
529          (s == EAI_MEMORY) )
530       return GNUNET_NO;         /* other function may still succeed */
531     return GNUNET_SYSERR;
532   }
533   if (NULL == result)
534     return GNUNET_SYSERR;
535   for (pos = result; pos != NULL; pos = pos->ai_next)
536   {
537     switch (pos->ai_family)
538     {
539     case AF_INET:
540       env = GNUNET_MQ_msg_extra (msg,
541                                  sizeof (struct in_addr),
542                                  GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
543       msg->id = request_id;
544       GNUNET_memcpy (&msg[1],
545                      &((struct sockaddr_in*) pos->ai_addr)->sin_addr,
546                      sizeof (struct in_addr));
547       GNUNET_MQ_send (mq,
548                       env);
549       break;
550     case AF_INET6:
551       env = GNUNET_MQ_msg_extra (msg,
552                                  sizeof (struct in6_addr),
553                                  GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
554       msg->id = request_id;
555       GNUNET_memcpy (&msg[1],
556                      &((struct sockaddr_in6*) pos->ai_addr)->sin6_addr,
557                      sizeof (struct in6_addr));
558       GNUNET_MQ_send (mq,
559                       env);
560       break;
561     default:
562       /* unsupported, skip */
563       break;
564     }
565   }
566   freeaddrinfo (result);
567   return GNUNET_OK;
568 }
569
570
571 #elif HAVE_GETHOSTBYNAME2
572
573
574 static int
575 gethostbyname2_resolve (struct GNUNET_MQ_Handle *mq,
576                         const char *hostname,
577                         int af,
578                         uint32_t request_id)
579 {
580   struct hostent *hp;
581   int ret1;
582   int ret2;
583   struct GNUNET_MQ_Envelope *env;
584   struct GNUNET_RESOLVER_ResponseMessage *msg;
585
586 #ifdef WINDOWS
587   /* gethostbyname2() in plibc is a compat dummy that calls gethostbyname(). */
588   return GNUNET_NO;
589 #endif
590
591   if (af == AF_UNSPEC)
592   {
593     ret1 = gethostbyname2_resolve (mq,
594                                    hostname,
595                                    AF_INET,
596                                    request_id);
597     ret2 = gethostbyname2_resolve (mq,
598                                    hostname,
599                                    AF_INET6,
600                                    request_id);
601     if ( (ret1 == GNUNET_OK) ||
602          (ret2 == GNUNET_OK) )
603       return GNUNET_OK;
604     if ( (ret1 == GNUNET_SYSERR) ||
605          (ret2 == GNUNET_SYSERR) )
606       return GNUNET_SYSERR;
607     return GNUNET_NO;
608   }
609   hp = gethostbyname2 (hostname,
610                        af);
611   if (hp == NULL)
612   {
613     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
614                 _("Could not find IP of host `%s': %s\n"),
615                 hostname,
616                 hstrerror (h_errno));
617     return GNUNET_SYSERR;
618   }
619   GNUNET_assert (hp->h_addrtype == af);
620   switch (af)
621   {
622   case AF_INET:
623     GNUNET_assert (hp->h_length == sizeof (struct in_addr));
624     env = GNUNET_MQ_msg_extra (msg,
625                                hp->h_length,
626                                GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
627     msg->id = request_id;
628     GNUNET_memcpy (&msg[1],
629                    hp->h_addr_list[0],
630                    hp->h_length);
631     GNUNET_MQ_send (mq,
632                     env);
633     break;
634   case AF_INET6:
635     GNUNET_assert (hp->h_length == sizeof (struct in6_addr));
636     env = GNUNET_MQ_msg_extra (msg,
637                                hp->h_length,
638                                GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
639     msg->id = request_id;
640     GNUNET_memcpy (&msg[1],
641                    hp->h_addr_list[0],
642                    hp->h_length);
643     GNUNET_MQ_send (mq,
644                     env);
645     break;
646   default:
647     GNUNET_break (0);
648     return GNUNET_SYSERR;
649   }
650   return GNUNET_OK;
651 }
652
653 #elif HAVE_GETHOSTBYNAME
654
655
656 static int
657 gethostbyname_resolve (struct GNUNET_MQ_Handle *mq,
658                        const char *hostname,
659                        uint32_t request_id)
660 {
661   struct hostent *hp;
662   struct GNUNET_RESOLVER_ResponseMessage *msg;
663   struct GNUNET_MQ_Envelope *env;
664
665   hp = GETHOSTBYNAME (hostname);
666   if (NULL == hp)
667   {
668     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
669                 _("Could not find IP of host `%s': %s\n"),
670                 hostname,
671                 hstrerror (h_errno));
672     return GNUNET_SYSERR;
673   }
674   if (hp->h_addrtype != AF_INET)
675   {
676     GNUNET_break (0);
677     return GNUNET_SYSERR;
678   }
679   GNUNET_assert (hp->h_length == sizeof (struct in_addr));
680   env = GNUNET_MQ_msg_extra (msg,
681                              hp->h_length,
682                              GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
683   msg->id = request_id;
684   GNUNET_memcpy (&msg[1],
685                  hp->h_addr_list[0],
686                  hp->h_length);
687   GNUNET_MQ_send (mq,
688                   env);
689   return GNUNET_OK;
690 }
691 #endif
692
693
694 /**
695  * Convert a string to an IP address.
696  *
697  * @param client where to send the IP address
698  * @param hostname the hostname to resolve
699  * @param af AF_INET or AF_INET6; use AF_UNSPEC for "any"
700  */
701 static void
702 get_ip_from_hostname (struct GNUNET_SERVICE_Client *client,
703                       const char *hostname,
704                       int af,
705                       uint32_t request_id)
706 {
707   struct GNUNET_MQ_Envelope *env;
708   struct GNUNET_RESOLVER_ResponseMessage *msg;
709   struct GNUNET_MQ_Handle *mq;
710
711   mq = GNUNET_SERVICE_client_get_mq (client);
712 #if HAVE_GETADDRINFO_A
713   getaddrinfo_a_resolve (mq,
714                          hostname,
715                          af,
716                          request_id);
717   GNUNET_SERVICE_client_continue (client);
718   return;
719 #elif HAVE_GETADDRINFO
720   getaddrinfo_resolve (mq,
721                        hostname,
722                        af,
723                        request_id);
724 #elif HAVE_GETHOSTBYNAME2
725   gethostbyname2_resolve (mq,
726                           hostname,
727                           af,
728                           request_id);
729 #elif HAVE_GETHOSTBYNAME
730   if ( ( (af == AF_UNSPEC) ||
731          (af == PF_INET) ) )
732     gethostbyname_resolve (mq,
733                            hostname,
734                            request_id);
735 #endif
736   // send end message
737   env = GNUNET_MQ_msg (msg,
738                        GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
739   msg->id = request_id;
740   GNUNET_MQ_notify_sent (env,
741                          &notify_service_client_done,
742                          client);
743   GNUNET_MQ_send (mq,
744                   env);
745 }
746
747
748 /**
749  * Verify well-formedness of GET-message.
750  *
751  * @param cls closure, unused
752  * @param get the actual message
753  * @return #GNUNET_OK if @a get is well-formed
754  */
755 static int
756 check_get (void *cls,
757            const struct GNUNET_RESOLVER_GetMessage *get)
758 {
759   uint16_t size;
760   int direction;
761   int af;
762
763   (void) cls;
764   size = ntohs (get->header.size) - sizeof (*get);
765   direction = ntohl (get->direction);
766   if (GNUNET_NO == direction)
767   {
768     /* IP from hostname */
769     const char *hostname;
770
771     hostname = (const char *) &get[1];
772     if (hostname[size - 1] != '\0')
773     {
774       GNUNET_break (0);
775       return GNUNET_SYSERR;
776     }
777     return GNUNET_OK;
778   }
779   af = ntohl (get->af);
780   switch (af)
781   {
782   case AF_INET:
783     if (size != sizeof (struct in_addr))
784     {
785       GNUNET_break (0);
786       return GNUNET_SYSERR;
787     }
788     break;
789   case AF_INET6:
790     if (size != sizeof (struct in6_addr))
791     {
792       GNUNET_break (0);
793       return GNUNET_SYSERR;
794     }
795     break;
796   default:
797     GNUNET_break (0);
798     return GNUNET_SYSERR;
799   }
800   return GNUNET_OK;
801 }
802
803
804 /**
805  * Handle GET-message.
806  *
807  * @param cls identification of the client
808  * @param msg the actual message
809  */
810 static void
811 handle_get (void *cls,
812             const struct GNUNET_RESOLVER_GetMessage *msg)
813 {
814   struct GNUNET_SERVICE_Client *client = cls;
815   const void *ip;
816   int direction;
817   int af;
818   uint32_t id;
819
820   direction = ntohl (msg->direction);
821   af = ntohl (msg->af);
822   id = ntohl (msg->id);
823   if (GNUNET_NO == direction)
824   {
825     /* IP from hostname */
826     const char *hostname;
827
828     hostname = (const char *) &msg[1];
829     get_ip_from_hostname (client,
830                           hostname,
831                           af,
832                           id);
833     return;
834   }
835   ip = &msg[1];
836
837 #if !defined(GNUNET_CULL_LOGGING)
838   {
839     char buf[INET6_ADDRSTRLEN];
840
841     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
842                 "Resolver asked to look up IP address `%s (request ID %u)'.\n",
843                 inet_ntop (af,
844                            ip,
845                            buf,
846                            sizeof (buf)),
847                 id);
848   }
849 #endif
850   get_ip_as_string (client,
851                     af,
852                     ip,
853                     id);
854 }
855
856
857 /**
858  * Callback called when a client connects to the service.
859  *
860  * @param cls closure for the service, unused
861  * @param c the new client that connected to the service
862  * @param mq the message queue used to send messages to the client
863  * @return @a c
864  */
865 static void *
866 connect_cb (void *cls,
867             struct GNUNET_SERVICE_Client *c,
868             struct GNUNET_MQ_Handle *mq)
869 {
870   (void) cls;
871   (void) mq;
872
873 #if HAVE_GETADDRINFO_A
874   resolve_result_pipe = GNUNET_DISK_pipe (GNUNET_NO,
875                                           GNUNET_NO,
876                                           GNUNET_NO,
877                                           GNUNET_NO);
878   GNUNET_assert (NULL != resolve_result_pipe);
879   resolve_result_pipe_task =
880     GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
881                                     GNUNET_DISK_pipe_handle (resolve_result_pipe,
882                                                              GNUNET_DISK_PIPE_END_READ),
883                                     &resolve_result_pipe_cb,
884                                     NULL);
885 #endif
886   return c;
887 }
888
889
890 /**
891  * Callback called when a client disconnected from the service
892  *
893  * @param cls closure for the service
894  * @param c the client that disconnected
895  * @param internal_cls should be equal to @a c
896  */
897 static void
898 disconnect_cb (void *cls,
899                struct GNUNET_SERVICE_Client *c,
900                void *internal_cls)
901 {
902   (void) cls;
903
904 #if HAVE_GETADDRINFO_A
905   if (NULL != resolve_result_pipe_task)
906   {
907     GNUNET_SCHEDULER_cancel (resolve_result_pipe_task);
908     resolve_result_pipe_task = NULL;
909   }
910   if (NULL != resolve_result_pipe)
911   {
912     GNUNET_DISK_pipe_close (resolve_result_pipe);
913     resolve_result_pipe = NULL;
914   }
915 #endif
916   GNUNET_assert (c == internal_cls);
917 }
918
919
920 /**
921  * Define "main" method using service macro.
922  */
923 GNUNET_SERVICE_MAIN
924 ("resolver",
925  GNUNET_SERVICE_OPTION_NONE,
926  NULL,
927  &connect_cb,
928  &disconnect_cb,
929  NULL,
930  GNUNET_MQ_hd_var_size (get,
931                         GNUNET_MESSAGE_TYPE_RESOLVER_REQUEST,
932                         struct GNUNET_RESOLVER_GetMessage,
933                         NULL),
934  GNUNET_MQ_handler_end ());
935
936
937 #if defined(LINUX) && defined(__GLIBC__)
938 #include <malloc.h>
939
940 /**
941  * MINIMIZE heap size (way below 128k) since this process doesn't need much.
942  */
943 void __attribute__ ((constructor))
944 GNUNET_RESOLVER_memory_init ()
945 {
946   mallopt (M_TRIM_THRESHOLD, 4 * 1024);
947   mallopt (M_TOP_PAD, 1 * 1024);
948   malloc_trim (0);
949 }
950 #endif
951
952
953 /**
954  * Free globals on exit.
955  */
956 void __attribute__ ((destructor))
957 GNUNET_RESOLVER_memory_done ()
958 {
959   struct IPCache *pos;
960
961   while (NULL != (pos = cache_head))
962   {
963     GNUNET_CONTAINER_DLL_remove (cache_head,
964                                  cache_tail,
965                                  pos);
966     GNUNET_free_non_null (pos->addr);
967     GNUNET_free (pos);
968   }
969 }
970
971
972 /* end of gnunet-service-resolver.c */