- Allocate buffer large enough to contain UNIX_PATH_MAX size pathnames in case of...
[oweals/gnunet.git] / src / util / resolver_api.c
1 /*
2      This file is part of GNUnet.
3      (C) 2009-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., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20
21 /**
22  * @file util/resolver_api.c
23  * @brief resolver for writing a tool
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet_util_lib.h"
28 #include "gnunet_protocols.h"
29 #include "gnunet_resolver_service.h"
30 #include "resolver.h"
31
32 #define LOG(kind,...) GNUNET_log_from (kind, "resolver-api", __VA_ARGS__)
33
34 #define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "resolver-api", syscall)
35
36 /**
37  * Maximum supported length for a hostname
38  */
39 #define MAX_HOSTNAME 1024
40
41
42 /**
43  * Possible hostnames for "loopback".
44  */
45 static const char *loopback[] = {
46   "localhost",
47   "ip6-localnet",
48   NULL
49 };
50
51
52 /**
53  * Configuration.
54  */
55 static const struct GNUNET_CONFIGURATION_Handle *resolver_cfg;
56
57 /**
58  * Our connection to the resolver service, created on-demand, but then
59  * persists until error or shutdown.
60  */
61 static struct GNUNET_CLIENT_Connection *client;
62
63 /**
64  * Head of DLL of requests.
65  */
66 static struct GNUNET_RESOLVER_RequestHandle *req_head;
67
68 /**
69  * Tail of DLL of requests.
70  */
71 static struct GNUNET_RESOLVER_RequestHandle *req_tail;
72
73 /**
74  * How long should we wait to reconnect?
75  */
76 static struct GNUNET_TIME_Relative backoff;
77
78 /**
79  * Task for reconnecting.
80  */
81 static GNUNET_SCHEDULER_TaskIdentifier r_task;
82
83 /**
84  * Task ID of shutdown task; only present while we have a
85  * connection to the resolver service.
86  */
87 static GNUNET_SCHEDULER_TaskIdentifier s_task;
88
89
90 /**
91  * Handle to a request given to the resolver.  Can be used to cancel
92  * the request prior to the timeout or successful execution.  Also
93  * used to track our internal state for the request.
94  */
95 struct GNUNET_RESOLVER_RequestHandle
96 {
97
98   /**
99    * Next entry in DLL of requests.
100    */
101   struct GNUNET_RESOLVER_RequestHandle *next;
102
103   /**
104    * Previous entry in DLL of requests.
105    */
106   struct GNUNET_RESOLVER_RequestHandle *prev;
107
108   /**
109    * Callback if this is an name resolution request,
110    * otherwise NULL.
111    */
112   GNUNET_RESOLVER_AddressCallback addr_callback;
113
114   /**
115    * Callback if this is a reverse lookup request,
116    * otherwise NULL.
117    */
118   GNUNET_RESOLVER_HostnameCallback name_callback;
119
120   /**
121    * Closure for the respective "callback".
122    */
123   void *cls;
124
125   /**
126    * When should this request time out?
127    */
128   struct GNUNET_TIME_Absolute timeout;
129
130   /**
131    * Task handle for numeric lookups.
132    */
133   GNUNET_SCHEDULER_TaskIdentifier task;
134
135   /**
136    * Desired address family.
137    */
138   int af;
139
140   /**
141    * Has this request been transmitted to the service?
142    * #GNUNET_YES if transmitted
143    * #GNUNET_YES if not transmitted
144    * #GNUNET_SYSERR when request was canceled
145    */
146   int was_transmitted;
147
148   /**
149    * Did we add this request to the queue?
150    */
151   int was_queued;
152
153   /**
154    * Desired direction (IP to name or name to IP)
155    */
156   int direction;
157
158   /**
159    * #GNUNET_YES if a response was received
160    */
161   int received_response;
162
163   /**
164    * Length of the data that follows this struct.
165    */
166   size_t data_len;
167 };
168
169
170 /**
171  * Check that the resolver service runs on localhost
172  * (or equivalent).
173  */
174 static void
175 check_config ()
176 {
177   char *hostname;
178   unsigned int i;
179   struct sockaddr_in v4;
180   struct sockaddr_in6 v6;
181
182   memset (&v4, 0, sizeof (v4));
183   v4.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
184   v4.sin_family = AF_INET;
185 #if HAVE_SOCKADDR_IN_SIN_LEN
186   v4.sin_len = sizeof (v4);
187 #endif
188   memset (&v6, 0, sizeof (v6));
189   v6.sin6_family = AF_INET6;
190 #if HAVE_SOCKADDR_IN_SIN_LEN
191   v6.sin6_len = sizeof (v6);
192 #endif
193   if (GNUNET_OK !=
194       GNUNET_CONFIGURATION_get_value_string (resolver_cfg, "resolver",
195                                              "HOSTNAME", &hostname))
196   {
197     LOG (GNUNET_ERROR_TYPE_ERROR,
198          _("Must specify `%s' for `%s' in configuration!\n"), "HOSTNAME",
199          "resolver");
200     GNUNET_assert (0);
201   }
202   if ((1 != inet_pton (AF_INET, hostname, &v4)) ||
203       (1 != inet_pton (AF_INET6, hostname, &v6)))
204   {
205     GNUNET_free (hostname);
206     return;
207   }
208   i = 0;
209   while (NULL != loopback[i])
210     if (0 == strcasecmp (loopback[i++], hostname))
211     {
212       GNUNET_free (hostname);
213       return;
214     }
215   LOG (GNUNET_ERROR_TYPE_ERROR,
216        _("Must specify `%s' or numeric IP address for `%s' of `%s' in configuration!\n"),
217        "localhost", "HOSTNAME", "resolver");
218   GNUNET_free (hostname);
219   GNUNET_assert (0);
220 }
221
222
223 /**
224  * Create the connection to the resolver service.
225  *
226  * @param cfg configuration to use
227  */
228 void
229 GNUNET_RESOLVER_connect (const struct GNUNET_CONFIGURATION_Handle *cfg)
230 {
231   GNUNET_assert (NULL != cfg);
232   backoff = GNUNET_TIME_UNIT_MILLISECONDS;
233   resolver_cfg = cfg;
234   check_config ();
235 }
236
237
238 /**
239  * Destroy the connection to the resolver service.
240  */
241 void
242 GNUNET_RESOLVER_disconnect ()
243 {
244   GNUNET_assert (NULL == req_head);
245   GNUNET_assert (NULL == req_tail);
246   if (NULL != client)
247   {
248     LOG (GNUNET_ERROR_TYPE_DEBUG,
249          "Disconnecting from DNS service\n");
250     GNUNET_CLIENT_disconnect (client);
251     client = NULL;
252   }
253   if (GNUNET_SCHEDULER_NO_TASK != r_task)
254   {
255     GNUNET_SCHEDULER_cancel (r_task);
256     r_task = GNUNET_SCHEDULER_NO_TASK;
257   }
258   if (GNUNET_SCHEDULER_NO_TASK != s_task)
259   {
260     GNUNET_SCHEDULER_cancel (s_task);
261     s_task = GNUNET_SCHEDULER_NO_TASK;
262   }
263 }
264
265
266 /**
267  * Convert IP address to string without DNS resolution.
268  *
269  * @param af address family
270  * @param ip the address
271  * @param ip_len number of bytes in ip
272  * @return address as a string, NULL on error
273  */
274 static char *
275 no_resolve (int af,
276             const void *ip, socklen_t ip_len)
277 {
278   char buf[INET6_ADDRSTRLEN];
279
280   switch (af)
281   {
282   case AF_INET:
283     if (ip_len != sizeof (struct in_addr))
284       return NULL;
285     if (NULL ==
286         inet_ntop (AF_INET, ip, buf, sizeof (buf)))
287     {
288       LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING,
289                     "inet_ntop");
290       return NULL;
291     }
292     break;
293   case AF_INET6:
294     if (ip_len != sizeof (struct in6_addr))
295       return NULL;
296     if (NULL ==
297         inet_ntop (AF_INET6, ip, buf, sizeof (buf)))
298     {
299       LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING,
300                     "inet_ntop");
301       return NULL;
302     }
303     break;
304   default:
305     GNUNET_break (0);
306     return NULL;
307   }
308   return GNUNET_strdup (buf);
309 }
310
311
312 /**
313  * Adjust exponential back-off and reconnect to the service.
314  */
315 static void
316 reconnect ();
317
318
319 /**
320  * Process pending requests to the resolver.
321  */
322 static void
323 process_requests ();
324
325
326 /**
327  * Process response with a hostname for a DNS lookup.
328  *
329  * @param cls our `struct GNUNET_RESOLVER_RequestHandle` context
330  * @param msg message with the hostname, NULL on error
331  */
332 static void
333 handle_response (void *cls,
334                  const struct GNUNET_MessageHeader *msg)
335 {
336   struct GNUNET_RESOLVER_RequestHandle *rh = cls;
337   uint16_t size;
338   char *nret;
339
340   LOG (GNUNET_ERROR_TYPE_DEBUG,
341        "Receiving response from DNS service\n");
342   if (NULL == msg)
343   {
344     char buf[INET6_ADDRSTRLEN];
345
346     if (NULL != rh->name_callback)
347       LOG (GNUNET_ERROR_TYPE_INFO,
348            _("Timeout trying to resolve IP address `%s'.\n"),
349            inet_ntop (rh->af, (const void *) &rh[1], buf, sizeof(buf)));
350     else
351       LOG (GNUNET_ERROR_TYPE_INFO,
352            _("Timeout trying to resolve hostname `%s'.\n"),
353            (const char *) &rh[1]);
354     /* check if request was canceled */
355     if (GNUNET_SYSERR != rh->was_transmitted)
356     {
357       if (NULL != rh->name_callback)
358       {
359         /* no reverse lookup was successful, return ip as string */
360         if (rh->received_response == GNUNET_NO)
361         {
362           nret = no_resolve (rh->af, &rh[1], rh->data_len);
363           rh->name_callback (rh->cls, nret);
364           GNUNET_free (nret);
365           rh->name_callback (rh->cls, NULL);
366         }
367         /* at least one reverse lookup was successful */
368         else
369           rh->name_callback (rh->cls, NULL);
370       }
371       if (NULL != rh->addr_callback)
372         rh->addr_callback (rh->cls, NULL, 0);
373     }
374     GNUNET_CONTAINER_DLL_remove (req_head, req_tail, rh);
375     GNUNET_free (rh);
376     GNUNET_CLIENT_disconnect (client);
377     client = NULL;
378     reconnect ();
379     return;
380   }
381   if (GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE != ntohs (msg->type))
382   {
383     GNUNET_break (0);
384     GNUNET_CLIENT_disconnect (client);
385     client = NULL;
386     reconnect ();
387     return;
388   }
389   size = ntohs (msg->size);
390   /* message contains not data, just header */
391   if (size == sizeof (struct GNUNET_MessageHeader))
392   {
393     /* check if request was canceled */
394     if (GNUNET_SYSERR != rh->was_transmitted)
395     {
396       if (NULL != rh->name_callback)
397         rh->name_callback (rh->cls, NULL);
398       if (NULL != rh->addr_callback)
399         rh->addr_callback (rh->cls, NULL, 0);
400     }
401     GNUNET_CONTAINER_DLL_remove (req_head, req_tail, rh);
402     GNUNET_free (rh);
403     process_requests ();
404     return;
405   }
406   /* return reverse lookup results to caller */
407   if (NULL != rh->name_callback)
408   {
409     const char *hostname;
410
411     hostname = (const char *) &msg[1];
412     if (hostname[size - sizeof (struct GNUNET_MessageHeader) - 1] != '\0')
413     {
414       GNUNET_break (0);
415       if (rh->was_transmitted != GNUNET_SYSERR)
416         rh->name_callback (rh->cls, NULL);
417       GNUNET_CONTAINER_DLL_remove (req_head, req_tail, rh);
418       GNUNET_free (rh);
419       GNUNET_CLIENT_disconnect (client);
420       client = NULL;
421       reconnect ();
422       return;
423     }
424     LOG (GNUNET_ERROR_TYPE_DEBUG,
425          "Resolver returns `%s' for IP `%s'.\n",
426          hostname,
427          GNUNET_a2s ((const void *) &rh[1],
428                      rh->data_len));
429     if (rh->was_transmitted != GNUNET_SYSERR)
430       rh->name_callback (rh->cls, hostname);
431     rh->received_response = GNUNET_YES;
432     GNUNET_CLIENT_receive (client, &handle_response, rh,
433                            GNUNET_TIME_absolute_get_remaining (rh->timeout));
434   }
435   /* return lookup results to caller */
436   if (NULL != rh->addr_callback)
437   {
438     struct sockaddr_in v4;
439     struct sockaddr_in6 v6;
440     const struct sockaddr *sa;
441     socklen_t salen;
442     const void *ip;
443     size_t ip_len;
444
445     ip = &msg[1];
446     ip_len = size - sizeof (struct GNUNET_MessageHeader);
447     if (ip_len == sizeof (struct in_addr))
448     {
449       memset (&v4, 0, sizeof (v4));
450       v4.sin_family = AF_INET;
451       v4.sin_addr = *(struct in_addr*) ip;
452 #if HAVE_SOCKADDR_IN_SIN_LEN
453       v4.sin_len = sizeof (v4);
454 #endif
455       salen = sizeof (v4);
456       sa = (const struct sockaddr *) &v4;
457     }
458     else if (ip_len == sizeof (struct in6_addr))
459     {
460       memset (&v6, 0, sizeof (v6));
461       v6.sin6_family = AF_INET6;
462       v6.sin6_addr = *(struct in6_addr*) ip;
463 #if HAVE_SOCKADDR_IN_SIN_LEN
464       v6.sin6_len = sizeof (v6);
465 #endif
466       salen = sizeof (v6);
467       sa = (const struct sockaddr *) &v6;
468     }
469     else
470     {
471       GNUNET_break (0);
472       if (rh->was_transmitted != GNUNET_SYSERR)
473         rh->addr_callback (rh->cls, NULL, 0);
474       GNUNET_CONTAINER_DLL_remove (req_head, req_tail, rh);
475       GNUNET_free (rh);
476       GNUNET_CLIENT_disconnect (client);
477       client = NULL;
478       reconnect ();
479       return;
480     }
481     rh->addr_callback (rh->cls, sa, salen);
482     GNUNET_CLIENT_receive (client, &handle_response, rh,
483                            GNUNET_TIME_absolute_get_remaining (rh->timeout));
484   }
485 }
486
487
488 /**
489  * We've been asked to lookup the address for a hostname and were
490  * given a valid numeric string.  Perform the callbacks for the
491  * numeric addresses.
492  *
493  * @param cls `struct GNUNET_RESOLVER_RequestHandle` for the request
494  * @param tc unused scheduler context
495  */
496 static void
497 numeric_resolution (void *cls,
498                     const struct GNUNET_SCHEDULER_TaskContext *tc)
499 {
500   struct GNUNET_RESOLVER_RequestHandle *rh = cls;
501   struct sockaddr_in v4;
502   struct sockaddr_in6 v6;
503   const char *hostname;
504
505   memset (&v4, 0, sizeof (v4));
506   v4.sin_family = AF_INET;
507 #if HAVE_SOCKADDR_IN_SIN_LEN
508   v4.sin_len = sizeof (v4);
509 #endif
510   memset (&v6, 0, sizeof (v6));
511   v6.sin6_family = AF_INET6;
512 #if HAVE_SOCKADDR_IN_SIN_LEN
513   v6.sin6_len = sizeof (v6);
514 #endif
515   hostname = (const char *) &rh[1];
516   if (((rh->af == AF_UNSPEC) || (rh->af == AF_INET)) &&
517       (1 == inet_pton (AF_INET, hostname, &v4.sin_addr)))
518   {
519     rh->addr_callback (rh->cls, (const struct sockaddr *) &v4, sizeof (v4));
520     if ((rh->af == AF_UNSPEC) &&
521         (1 == inet_pton (AF_INET6, hostname, &v6.sin6_addr)))
522     {
523       /* this can happen on some systems IF "hostname" is "localhost" */
524       rh->addr_callback (rh->cls, (const struct sockaddr *) &v6, sizeof (v6));
525     }
526     rh->addr_callback (rh->cls, NULL, 0);
527     GNUNET_free (rh);
528     return;
529   }
530   if (((rh->af == AF_UNSPEC) || (rh->af == AF_INET6)) &&
531       (1 == inet_pton (AF_INET6, hostname, &v6.sin6_addr)))
532   {
533     rh->addr_callback (rh->cls, (const struct sockaddr *) &v6, sizeof (v6));
534     rh->addr_callback (rh->cls, NULL, 0);
535     GNUNET_free (rh);
536     return;
537   }
538   /* why are we here? this task should not have been scheduled! */
539   GNUNET_assert (0);
540   GNUNET_free (rh);
541 }
542
543
544 /**
545  * We've been asked to lookup the address for a hostname and were
546  * given a variant of "loopback".  Perform the callbacks for the
547  * respective loopback numeric addresses.
548  *
549  * @param cls `struct GNUNET_RESOLVER_RequestHandle` for the request
550  * @param tc unused scheduler context
551  */
552 static void
553 loopback_resolution (void *cls,
554                      const struct GNUNET_SCHEDULER_TaskContext *tc)
555 {
556   struct GNUNET_RESOLVER_RequestHandle *rh = cls;
557   struct sockaddr_in v4;
558   struct sockaddr_in6 v6;
559
560   memset (&v4, 0, sizeof (v4));
561   v4.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
562   v4.sin_family = AF_INET;
563 #if HAVE_SOCKADDR_IN_SIN_LEN
564   v4.sin_len = sizeof (v4);
565 #endif
566   memset (&v6, 0, sizeof (v6));
567   v6.sin6_family = AF_INET6;
568 #if HAVE_SOCKADDR_IN_SIN_LEN
569   v6.sin6_len = sizeof (v6);
570 #endif
571   v6.sin6_addr = in6addr_loopback;
572   switch (rh->af)
573   {
574   case AF_INET:
575     rh->addr_callback (rh->cls, (const struct sockaddr *) &v4, sizeof (v4));
576     break;
577   case AF_INET6:
578     rh->addr_callback (rh->cls, (const struct sockaddr *) &v6, sizeof (v6));
579     break;
580   case AF_UNSPEC:
581     rh->addr_callback (rh->cls, (const struct sockaddr *) &v6, sizeof (v6));
582     rh->addr_callback (rh->cls, (const struct sockaddr *) &v4, sizeof (v4));
583     break;
584   default:
585     GNUNET_break (0);
586     break;
587   }
588   rh->addr_callback (rh->cls, NULL, 0);
589   GNUNET_free (rh);
590 }
591
592
593 /**
594  * Task executed on system shutdown.
595  */
596 static void
597 shutdown_task (void *cls,
598                const struct GNUNET_SCHEDULER_TaskContext *tc)
599 {
600   s_task = GNUNET_SCHEDULER_NO_TASK;
601   GNUNET_RESOLVER_disconnect ();
602   backoff = GNUNET_TIME_UNIT_MILLISECONDS;
603 }
604
605
606 /**
607  * Process pending requests to the resolver.
608  */
609 static void
610 process_requests ()
611 {
612   struct GNUNET_RESOLVER_GetMessage *msg;
613   char buf[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1] GNUNET_ALIGN;
614   struct GNUNET_RESOLVER_RequestHandle *rh;
615
616   if (NULL == client)
617   {
618     reconnect ();
619     return;
620   }
621   rh = req_head;
622   if (NULL == rh)
623   {
624     /* nothing to do, release socket really soon if there is nothing
625      * else happening... */
626     s_task =
627         GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS,
628                                       &shutdown_task, NULL);
629     return;
630   }
631   if (GNUNET_YES == rh->was_transmitted)
632     return;                     /* waiting for reply */
633   msg = (struct GNUNET_RESOLVER_GetMessage *) buf;
634   msg->header.size =
635       htons (sizeof (struct GNUNET_RESOLVER_GetMessage) + rh->data_len);
636   msg->header.type = htons (GNUNET_MESSAGE_TYPE_RESOLVER_REQUEST);
637   msg->direction = htonl (rh->direction);
638   msg->af = htonl (rh->af);
639   memcpy (&msg[1], &rh[1], rh->data_len);
640   LOG (GNUNET_ERROR_TYPE_DEBUG,
641        "Transmitting DNS resolution request to DNS service\n");
642   if (GNUNET_OK !=
643       GNUNET_CLIENT_transmit_and_get_response (client, &msg->header,
644                                                GNUNET_TIME_absolute_get_remaining
645                                                (rh->timeout), GNUNET_YES,
646                                                &handle_response, rh))
647   {
648     GNUNET_CLIENT_disconnect (client);
649     client = NULL;
650     GNUNET_break (0);
651     reconnect ();
652     return;
653   }
654   rh->was_transmitted = GNUNET_YES;
655 }
656
657
658 /**
659  * Now try to reconnect to the resolver service.
660  *
661  * @param cls NULL
662  * @param tc scheduler context
663  */
664 static void
665 reconnect_task (void *cls,
666                 const struct GNUNET_SCHEDULER_TaskContext *tc)
667 {
668   r_task = GNUNET_SCHEDULER_NO_TASK;
669   if (NULL == req_head)
670     return;                     /* no work pending */
671   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
672     return;
673   LOG (GNUNET_ERROR_TYPE_DEBUG,
674        "Trying to connect to DNS service\n");
675   client = GNUNET_CLIENT_connect ("resolver", resolver_cfg);
676   if (NULL == client)
677   {
678     LOG (GNUNET_ERROR_TYPE_DEBUG,
679          "Failed to connect, will try again later\n");
680     reconnect ();
681     return;
682   }
683   process_requests ();
684 }
685
686
687 /**
688  * Adjust exponential back-off and reconnect to the service.
689  */
690 static void
691 reconnect ()
692 {
693   struct GNUNET_RESOLVER_RequestHandle *rh;
694
695   if (GNUNET_SCHEDULER_NO_TASK != r_task)
696     return;
697   GNUNET_assert (NULL == client);
698   if (NULL != (rh = req_head))
699   {
700     switch (rh->was_transmitted)
701     {
702     case GNUNET_NO:
703       /* nothing more to do */
704       break;
705     case GNUNET_YES:
706       /* disconnected, transmit again! */
707       rh->was_transmitted = GNUNET_NO;
708       break;
709     case GNUNET_SYSERR:
710       /* request was cancelled, remove entirely */
711       GNUNET_CONTAINER_DLL_remove (req_head, req_tail, rh);
712       GNUNET_free (rh);
713       break;
714     default:
715       GNUNET_assert (0);
716       break;
717     }
718   }
719   LOG (GNUNET_ERROR_TYPE_DEBUG,
720        "Will try to connect to DNS service in %s\n",
721        GNUNET_STRINGS_relative_time_to_string (backoff, GNUNET_YES));
722   GNUNET_assert (NULL != resolver_cfg);
723   r_task = GNUNET_SCHEDULER_add_delayed (backoff, &reconnect_task, NULL);
724   backoff = GNUNET_TIME_STD_BACKOFF (backoff);
725 }
726
727
728 /**
729  * Convert a string to one or more IP addresses.
730  *
731  * @param hostname the hostname to resolve
732  * @param af AF_INET or AF_INET6; use AF_UNSPEC for "any"
733  * @param callback function to call with addresses
734  * @param callback_cls closure for @a callback
735  * @param timeout how long to try resolving
736  * @return handle that can be used to cancel the request, NULL on error
737  */
738 struct GNUNET_RESOLVER_RequestHandle *
739 GNUNET_RESOLVER_ip_get (const char *hostname, int af,
740                         struct GNUNET_TIME_Relative timeout,
741                         GNUNET_RESOLVER_AddressCallback callback,
742                         void *callback_cls)
743 {
744   struct GNUNET_RESOLVER_RequestHandle *rh;
745   size_t slen;
746   unsigned int i;
747   struct in_addr v4;
748   struct in6_addr v6;
749
750   slen = strlen (hostname) + 1;
751   if (slen + sizeof (struct GNUNET_RESOLVER_GetMessage) >=
752       GNUNET_SERVER_MAX_MESSAGE_SIZE)
753   {
754     GNUNET_break (0);
755     return NULL;
756   }
757   rh = GNUNET_malloc (sizeof (struct GNUNET_RESOLVER_RequestHandle) + slen);
758   rh->af = af;
759   rh->addr_callback = callback;
760   rh->cls = callback_cls;
761   memcpy (&rh[1], hostname, slen);
762   rh->data_len = slen;
763   rh->timeout = GNUNET_TIME_relative_to_absolute (timeout);
764   rh->direction = GNUNET_NO;
765   /* first, check if this is a numeric address */
766   if (((1 == inet_pton (AF_INET, hostname, &v4)) &&
767        ((af == AF_INET) || (af == AF_UNSPEC))) ||
768       ((1 == inet_pton (AF_INET6, hostname, &v6)) &&
769        ((af == AF_INET6) || (af == AF_UNSPEC))))
770   {
771     rh->task = GNUNET_SCHEDULER_add_now (&numeric_resolution, rh);
772     return rh;
773   }
774   /* then, check if this is a loopback address */
775   i = 0;
776   while (NULL != loopback[i])
777     if (0 == strcasecmp (loopback[i++], hostname))
778     {
779       rh->task = GNUNET_SCHEDULER_add_now (&loopback_resolution, rh);
780       return rh;
781     }
782   GNUNET_CONTAINER_DLL_insert_tail (req_head, req_tail, rh);
783   rh->was_queued = GNUNET_YES;
784   if (s_task != GNUNET_SCHEDULER_NO_TASK)
785   {
786     GNUNET_SCHEDULER_cancel (s_task);
787     s_task = GNUNET_SCHEDULER_NO_TASK;
788   }
789   process_requests ();
790   return rh;
791 }
792
793
794 /**
795  * We've been asked to convert an address to a string without
796  * a reverse lookup.  Do it.
797  *
798  * @param cls `struct GNUNET_RESOLVER_RequestHandle` for the request
799  * @param tc unused scheduler context
800  */
801 static void
802 numeric_reverse (void *cls,
803                  const struct GNUNET_SCHEDULER_TaskContext *tc)
804 {
805   struct GNUNET_RESOLVER_RequestHandle *rh = cls;
806   char *result;
807
808   result = no_resolve (rh->af, &rh[1], rh->data_len);
809   LOG (GNUNET_ERROR_TYPE_DEBUG,
810        "Resolver returns `%s'.\n",
811        result);
812   if (NULL != result)
813   {
814     rh->name_callback (rh->cls, result);
815     GNUNET_free (result);
816   }
817   rh->name_callback (rh->cls, NULL);
818   GNUNET_free (rh);
819 }
820
821
822 /**
823  * Get an IP address as a string.
824  *
825  * @param sa host address
826  * @param salen length of host address in @a sa
827  * @param do_resolve use #GNUNET_NO to return numeric hostname
828  * @param timeout how long to try resolving
829  * @param callback function to call with hostnames
830  *        last callback is NULL when finished
831  * @param cls closure for @a callback
832  * @return handle that can be used to cancel the request
833  */
834 struct GNUNET_RESOLVER_RequestHandle *
835 GNUNET_RESOLVER_hostname_get (const struct sockaddr *sa,
836                               socklen_t salen,
837                               int do_resolve,
838                               struct GNUNET_TIME_Relative timeout,
839                               GNUNET_RESOLVER_HostnameCallback callback,
840                               void *cls)
841 {
842   struct GNUNET_RESOLVER_RequestHandle *rh;
843   size_t ip_len;
844   const void *ip;
845
846   check_config ();
847   switch (sa->sa_family)
848   {
849   case AF_INET:
850     ip_len = sizeof (struct in_addr);
851     ip = &((const struct sockaddr_in*)sa)->sin_addr;
852     break;
853   case AF_INET6:
854     ip_len = sizeof (struct in6_addr);
855     ip = &((const struct sockaddr_in6*)sa)->sin6_addr;
856     break;
857   default:
858     GNUNET_break (0);
859     return NULL;
860   }
861   rh = GNUNET_malloc (sizeof (struct GNUNET_RESOLVER_RequestHandle) + salen);
862   rh->name_callback = callback;
863   rh->cls = cls;
864   rh->af = sa->sa_family;
865   rh->timeout = GNUNET_TIME_relative_to_absolute (timeout);
866   memcpy (&rh[1], ip, ip_len);
867   rh->data_len = ip_len;
868   rh->direction = GNUNET_YES;
869   rh->received_response = GNUNET_NO;
870   if (GNUNET_NO == do_resolve)
871   {
872     rh->task = GNUNET_SCHEDULER_add_now (&numeric_reverse, rh);
873     return rh;
874   }
875   GNUNET_CONTAINER_DLL_insert_tail (req_head, req_tail, rh);
876   rh->was_queued = GNUNET_YES;
877   if (s_task != GNUNET_SCHEDULER_NO_TASK)
878   {
879     GNUNET_SCHEDULER_cancel (s_task);
880     s_task = GNUNET_SCHEDULER_NO_TASK;
881   }
882   process_requests ();
883   return rh;
884 }
885
886
887 /**
888  * Get local fully qualified af name
889  *
890  * @return fqdn
891  */
892 char *
893 GNUNET_RESOLVER_local_fqdn_get ()
894 {
895   struct hostent *host;
896   char hostname[GNUNET_OS_get_hostname_max_length () + 1];
897
898   if (0 != gethostname (hostname, sizeof (hostname) - 1))
899   {
900     LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
901                   "gethostname");
902     return NULL;
903   }
904   LOG (GNUNET_ERROR_TYPE_DEBUG, "Resolving our FQDN `%s'\n", hostname);
905   host = gethostbyname (hostname);
906   if (NULL == host)
907   {
908     LOG (GNUNET_ERROR_TYPE_ERROR, _("Could not resolve our FQDN : %s\n"),
909          hstrerror (h_errno));
910     return NULL;
911   }
912   return GNUNET_strdup (host->h_name);
913 }
914
915
916 /**
917  * Looking our own hostname.
918  *
919  * @param af AF_INET or AF_INET6; use AF_UNSPEC for "any"
920  * @param callback function to call with addresses
921  * @param cls closure for @a callback
922  * @param timeout how long to try resolving
923  * @return handle that can be used to cancel the request, NULL on error
924  */
925 struct GNUNET_RESOLVER_RequestHandle *
926 GNUNET_RESOLVER_hostname_resolve (int af,
927                                   struct GNUNET_TIME_Relative timeout,
928                                   GNUNET_RESOLVER_AddressCallback callback,
929                                   void *cls)
930 {
931   char hostname[GNUNET_OS_get_hostname_max_length () + 1];
932
933   if (0 != gethostname (hostname, sizeof (hostname) - 1))
934   {
935     LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
936                   "gethostname");
937     return NULL;
938   }
939   LOG (GNUNET_ERROR_TYPE_DEBUG,
940        "Resolving our hostname `%s'\n",
941        hostname);
942   return GNUNET_RESOLVER_ip_get (hostname, af, timeout, callback, cls);
943 }
944
945
946 /**
947  * Cancel a request that is still pending with the resolver.
948  * Note that a client MUST NOT cancel a request that has
949  * been completed (i.e, the callback has been called to
950  * signal timeout or the final result).
951  *
952  * @param rh handle of request to cancel
953  */
954 void
955 GNUNET_RESOLVER_request_cancel (struct GNUNET_RESOLVER_RequestHandle *rh)
956 {
957   if (rh->task != GNUNET_SCHEDULER_NO_TASK)
958   {
959     GNUNET_SCHEDULER_cancel (rh->task);
960     rh->task = GNUNET_SCHEDULER_NO_TASK;
961   }
962   if (rh->was_transmitted == GNUNET_NO)
963   {
964     if (rh->was_queued == GNUNET_YES)
965       GNUNET_CONTAINER_DLL_remove (req_head, req_tail, rh);
966     GNUNET_free (rh);
967     return;
968   }
969   GNUNET_assert (rh->was_transmitted == GNUNET_YES);
970   rh->was_transmitted = GNUNET_SYSERR;  /* mark as cancelled */
971 }
972
973
974 /* end of resolver_api.c */