- refactor kx sending, unify under send_kx
[oweals/gnunet.git] / src / util / resolver_api.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2009-2014 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 struct GNUNET_SCHEDULER_Task * 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 struct GNUNET_SCHEDULER_Task * 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 making reply callbacks in numeric lookups
132    * asynchronous, and for timeout handling.
133    */
134   struct GNUNET_SCHEDULER_Task * task;
135
136   /**
137    * Desired address family.
138    */
139   int af;
140
141   /**
142    * Has this request been transmitted to the service?
143    * #GNUNET_YES if transmitted
144    * #GNUNET_YES if not transmitted
145    * #GNUNET_SYSERR when request was canceled
146    */
147   int was_transmitted;
148
149   /**
150    * Did we add this request to the queue?
151    */
152   int was_queued;
153
154   /**
155    * Desired direction (IP to name or name to IP)
156    */
157   int direction;
158
159   /**
160    * #GNUNET_YES if a response was received
161    */
162   int received_response;
163
164   /**
165    * Length of the data that follows this struct.
166    */
167   size_t data_len;
168 };
169
170
171 /**
172  * Check that the resolver service runs on localhost
173  * (or equivalent).
174  */
175 static void
176 check_config ()
177 {
178   char *hostname;
179   unsigned int i;
180   struct sockaddr_in v4;
181   struct sockaddr_in6 v6;
182
183   memset (&v4, 0, sizeof (v4));
184   v4.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
185   v4.sin_family = AF_INET;
186 #if HAVE_SOCKADDR_IN_SIN_LEN
187   v4.sin_len = sizeof (v4);
188 #endif
189   memset (&v6, 0, sizeof (v6));
190   v6.sin6_family = AF_INET6;
191 #if HAVE_SOCKADDR_IN_SIN_LEN
192   v6.sin6_len = sizeof (v6);
193 #endif
194   if (GNUNET_OK !=
195       GNUNET_CONFIGURATION_get_value_string (resolver_cfg,
196                                              "resolver",
197                                              "HOSTNAME",
198                                              &hostname))
199   {
200     LOG (GNUNET_ERROR_TYPE_ERROR,
201          _("Must specify `%s' for `%s' in configuration!\n"),
202          "HOSTNAME",
203          "resolver");
204     GNUNET_assert (0);
205   }
206   if ((1 != inet_pton (AF_INET, hostname, &v4)) ||
207       (1 != inet_pton (AF_INET6, hostname, &v6)))
208   {
209     GNUNET_free (hostname);
210     return;
211   }
212   i = 0;
213   while (NULL != loopback[i])
214     if (0 == strcasecmp (loopback[i++], hostname))
215     {
216       GNUNET_free (hostname);
217       return;
218     }
219   LOG (GNUNET_ERROR_TYPE_ERROR,
220        _("Must specify `%s' or numeric IP address for `%s' of `%s' in configuration!\n"),
221        "localhost",
222        "HOSTNAME",
223        "resolver");
224   GNUNET_free (hostname);
225   GNUNET_assert (0);
226 }
227
228
229 /**
230  * Create the connection to the resolver service.
231  *
232  * @param cfg configuration to use
233  */
234 void
235 GNUNET_RESOLVER_connect (const struct GNUNET_CONFIGURATION_Handle *cfg)
236 {
237   GNUNET_assert (NULL != cfg);
238   backoff = GNUNET_TIME_UNIT_MILLISECONDS;
239   resolver_cfg = cfg;
240   check_config ();
241 }
242
243
244 /**
245  * Destroy the connection to the resolver service.
246  */
247 void
248 GNUNET_RESOLVER_disconnect ()
249 {
250   GNUNET_assert (NULL == req_head);
251   GNUNET_assert (NULL == req_tail);
252   if (NULL != client)
253   {
254     LOG (GNUNET_ERROR_TYPE_DEBUG,
255          "Disconnecting from DNS service\n");
256     GNUNET_CLIENT_disconnect (client);
257     client = NULL;
258   }
259   if (NULL != r_task)
260   {
261     GNUNET_SCHEDULER_cancel (r_task);
262     r_task = NULL;
263   }
264   if (NULL != s_task)
265   {
266     GNUNET_SCHEDULER_cancel (s_task);
267     s_task = NULL;
268   }
269 }
270
271
272 /**
273  * Convert IP address to string without DNS resolution.
274  *
275  * @param af address family
276  * @param ip the address
277  * @param ip_len number of bytes in @a ip
278  * @return address as a string, NULL on error
279  */
280 static char *
281 no_resolve (int af,
282             const void *ip, socklen_t ip_len)
283 {
284   char buf[INET6_ADDRSTRLEN];
285
286   switch (af)
287   {
288   case AF_INET:
289     if (ip_len != sizeof (struct in_addr))
290       return NULL;
291     if (NULL ==
292         inet_ntop (AF_INET, ip, buf, sizeof (buf)))
293     {
294       LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING,
295                     "inet_ntop");
296       return NULL;
297     }
298     break;
299   case AF_INET6:
300     if (ip_len != sizeof (struct in6_addr))
301       return NULL;
302     if (NULL ==
303         inet_ntop (AF_INET6, ip, buf, sizeof (buf)))
304     {
305       LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING,
306                     "inet_ntop");
307       return NULL;
308     }
309     break;
310   default:
311     GNUNET_break (0);
312     return NULL;
313   }
314   return GNUNET_strdup (buf);
315 }
316
317
318 /**
319  * Adjust exponential back-off and reconnect to the service.
320  */
321 static void
322 reconnect (void);
323
324
325 /**
326  * Process pending requests to the resolver.
327  */
328 static void
329 process_requests (void);
330
331
332 /**
333  * Process response with a hostname for a DNS lookup.
334  *
335  * @param cls our `struct GNUNET_RESOLVER_RequestHandle *` context
336  * @param msg message with the hostname, NULL on error
337  */
338 static void
339 handle_response (void *cls,
340                  const struct GNUNET_MessageHeader *msg)
341 {
342   struct GNUNET_RESOLVER_RequestHandle *rh = cls;
343   uint16_t size;
344   char *nret;
345
346   LOG (GNUNET_ERROR_TYPE_DEBUG,
347        "Receiving response from DNS service\n");
348   if (NULL == msg)
349   {
350     char buf[INET6_ADDRSTRLEN];
351
352     if (NULL != rh->name_callback)
353       LOG (GNUNET_ERROR_TYPE_INFO,
354            _("Timeout trying to resolve IP address `%s'.\n"),
355            inet_ntop (rh->af,
356                       (const void *) &rh[1],
357                       buf,
358                       sizeof(buf)));
359     else
360       LOG (GNUNET_ERROR_TYPE_INFO,
361            _("Timeout trying to resolve hostname `%s'.\n"),
362            (const char *) &rh[1]);
363     /* check if request was canceled */
364     if (GNUNET_SYSERR != rh->was_transmitted)
365     {
366       if (NULL != rh->name_callback)
367       {
368         /* no reverse lookup was successful, return IP as string */
369         if (GNUNET_NO == rh->received_response)
370         {
371           nret = no_resolve (rh->af,
372                              &rh[1],
373                              rh->data_len);
374           rh->name_callback (rh->cls, nret);
375           GNUNET_free (nret);
376         }
377         /* finally, make termination call */
378         rh->name_callback (rh->cls,
379                            NULL);
380       }
381       if (NULL != rh->addr_callback)
382         rh->addr_callback (rh->cls,
383                            NULL,
384                            0);
385     }
386     rh->was_transmitted = GNUNET_NO;
387     GNUNET_RESOLVER_request_cancel (rh);
388     GNUNET_CLIENT_disconnect (client);
389     client = NULL;
390     reconnect ();
391     return;
392   }
393   if (GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE != ntohs (msg->type))
394   {
395     GNUNET_break (0);
396     GNUNET_CLIENT_disconnect (client);
397     client = NULL;
398     reconnect ();
399     return;
400   }
401   size = ntohs (msg->size);
402   if (size == sizeof (struct GNUNET_MessageHeader))
403   {
404     /* message contains not data, just header; end of replies */
405     /* check if request was canceled */
406     if (GNUNET_SYSERR != rh->was_transmitted)
407     {
408       if (NULL != rh->name_callback)
409         rh->name_callback (rh->cls,
410                            NULL);
411       if (NULL != rh->addr_callback)
412         rh->addr_callback (rh->cls,
413                            NULL,
414                            0);
415     }
416     rh->was_transmitted = GNUNET_NO;
417     GNUNET_RESOLVER_request_cancel (rh);
418     process_requests ();
419     return;
420   }
421   /* return reverse lookup results to caller */
422   if (NULL != rh->name_callback)
423   {
424     const char *hostname;
425
426     hostname = (const char *) &msg[1];
427     if (hostname[size - sizeof (struct GNUNET_MessageHeader) - 1] != '\0')
428     {
429       GNUNET_break (0);
430       if (GNUNET_SYSERR != rh->was_transmitted)
431         rh->name_callback (rh->cls,
432                            NULL);
433       rh->was_transmitted = GNUNET_NO;
434       GNUNET_RESOLVER_request_cancel (rh);
435       GNUNET_CLIENT_disconnect (client);
436       client = NULL;
437       reconnect ();
438       return;
439     }
440     LOG (GNUNET_ERROR_TYPE_DEBUG,
441          "Resolver returns `%s' for IP `%s'.\n",
442          hostname,
443          GNUNET_a2s ((const void *) &rh[1],
444                      rh->data_len));
445     if (rh->was_transmitted != GNUNET_SYSERR)
446       rh->name_callback (rh->cls,
447                          hostname);
448     rh->received_response = GNUNET_YES;
449   }
450   /* return lookup results to caller */
451   if (NULL != rh->addr_callback)
452   {
453     struct sockaddr_in v4;
454     struct sockaddr_in6 v6;
455     const struct sockaddr *sa;
456     socklen_t salen;
457     const void *ip;
458     size_t ip_len;
459
460     ip = &msg[1];
461     ip_len = size - sizeof (struct GNUNET_MessageHeader);
462     if (ip_len == sizeof (struct in_addr))
463     {
464       memset (&v4, 0, sizeof (v4));
465       v4.sin_family = AF_INET;
466       v4.sin_addr = *(struct in_addr*) ip;
467 #if HAVE_SOCKADDR_IN_SIN_LEN
468       v4.sin_len = sizeof (v4);
469 #endif
470       salen = sizeof (v4);
471       sa = (const struct sockaddr *) &v4;
472     }
473     else if (ip_len == sizeof (struct in6_addr))
474     {
475       memset (&v6, 0, sizeof (v6));
476       v6.sin6_family = AF_INET6;
477       v6.sin6_addr = *(struct in6_addr*) ip;
478 #if HAVE_SOCKADDR_IN_SIN_LEN
479       v6.sin6_len = sizeof (v6);
480 #endif
481       salen = sizeof (v6);
482       sa = (const struct sockaddr *) &v6;
483     }
484     else
485     {
486       GNUNET_break (0);
487       if (GNUNET_SYSERR != rh->was_transmitted)
488         rh->addr_callback (rh->cls,
489                            NULL,
490                            0);
491       rh->was_transmitted = GNUNET_NO;
492       GNUNET_RESOLVER_request_cancel (rh);
493       GNUNET_CLIENT_disconnect (client);
494       client = NULL;
495       reconnect ();
496       return;
497     }
498     if (GNUNET_SYSERR != rh->was_transmitted)
499       rh->addr_callback (rh->cls,
500                          sa,
501                          salen);
502   }
503   GNUNET_CLIENT_receive (client,
504                          &handle_response,
505                          rh,
506                          GNUNET_TIME_absolute_get_remaining (rh->timeout));
507 }
508
509
510 /**
511  * We've been asked to lookup the address for a hostname and were
512  * given a valid numeric string.  Perform the callbacks for the
513  * numeric addresses.
514  *
515  * @param cls `struct GNUNET_RESOLVER_RequestHandle` for the request
516  * @param tc unused scheduler context
517  */
518 static void
519 numeric_resolution (void *cls,
520                     const struct GNUNET_SCHEDULER_TaskContext *tc)
521 {
522   struct GNUNET_RESOLVER_RequestHandle *rh = cls;
523   struct sockaddr_in v4;
524   struct sockaddr_in6 v6;
525   const char *hostname;
526
527   rh->task = NULL;
528   memset (&v4, 0, sizeof (v4));
529   v4.sin_family = AF_INET;
530 #if HAVE_SOCKADDR_IN_SIN_LEN
531   v4.sin_len = sizeof (v4);
532 #endif
533   memset (&v6, 0, sizeof (v6));
534   v6.sin6_family = AF_INET6;
535 #if HAVE_SOCKADDR_IN_SIN_LEN
536   v6.sin6_len = sizeof (v6);
537 #endif
538   hostname = (const char *) &rh[1];
539   if (((rh->af == AF_UNSPEC) || (rh->af == AF_INET)) &&
540       (1 == inet_pton (AF_INET, hostname, &v4.sin_addr)))
541   {
542     rh->addr_callback (rh->cls,
543                        (const struct sockaddr *) &v4,
544                        sizeof (v4));
545     if ((rh->af == AF_UNSPEC) &&
546         (1 == inet_pton (AF_INET6, hostname, &v6.sin6_addr)))
547     {
548       /* this can happen on some systems IF "hostname" is "localhost" */
549       rh->addr_callback (rh->cls,
550                          (const struct sockaddr *) &v6,
551                          sizeof (v6));
552     }
553     rh->addr_callback (rh->cls,
554                        NULL,
555                        0);
556     GNUNET_free (rh);
557     return;
558   }
559   if ( ( (rh->af == AF_UNSPEC) ||
560          (rh->af == AF_INET6) ) &&
561        (1 == inet_pton (AF_INET6, hostname, &v6.sin6_addr) ) )
562   {
563     rh->addr_callback (rh->cls,
564                        (const struct sockaddr *) &v6,
565                        sizeof (v6));
566     rh->addr_callback (rh->cls,
567                        NULL,
568                        0);
569     GNUNET_free (rh);
570     return;
571   }
572   /* why are we here? this task should not have been scheduled! */
573   GNUNET_assert (0);
574   GNUNET_free (rh);
575 }
576
577
578 /**
579  * We've been asked to lookup the address for a hostname and were
580  * given a variant of "loopback".  Perform the callbacks for the
581  * respective loopback numeric addresses.
582  *
583  * @param cls `struct GNUNET_RESOLVER_RequestHandle` for the request
584  * @param tc unused scheduler context
585  */
586 static void
587 loopback_resolution (void *cls,
588                      const struct GNUNET_SCHEDULER_TaskContext *tc)
589 {
590   struct GNUNET_RESOLVER_RequestHandle *rh = cls;
591   struct sockaddr_in v4;
592   struct sockaddr_in6 v6;
593
594   rh->task = NULL;
595   memset (&v4, 0, sizeof (v4));
596   v4.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
597   v4.sin_family = AF_INET;
598 #if HAVE_SOCKADDR_IN_SIN_LEN
599   v4.sin_len = sizeof (v4);
600 #endif
601   memset (&v6, 0, sizeof (v6));
602   v6.sin6_family = AF_INET6;
603 #if HAVE_SOCKADDR_IN_SIN_LEN
604   v6.sin6_len = sizeof (v6);
605 #endif
606   v6.sin6_addr = in6addr_loopback;
607   switch (rh->af)
608   {
609   case AF_INET:
610     rh->addr_callback (rh->cls,
611                        (const struct sockaddr *) &v4,
612                        sizeof (v4));
613     break;
614   case AF_INET6:
615     rh->addr_callback (rh->cls,
616                        (const struct sockaddr *) &v6,
617                        sizeof (v6));
618     break;
619   case AF_UNSPEC:
620     rh->addr_callback (rh->cls,
621                        (const struct sockaddr *) &v6,
622                        sizeof (v6));
623     rh->addr_callback (rh->cls,
624                        (const struct sockaddr *) &v4,
625                        sizeof (v4));
626     break;
627   default:
628     GNUNET_break (0);
629     break;
630   }
631   rh->addr_callback (rh->cls,
632                      NULL,
633                      0);
634   GNUNET_free (rh);
635 }
636
637
638 /**
639  * Task executed on system shutdown.
640  */
641 static void
642 shutdown_task (void *cls,
643                const struct GNUNET_SCHEDULER_TaskContext *tc)
644 {
645   s_task = NULL;
646   GNUNET_RESOLVER_disconnect ();
647   backoff = GNUNET_TIME_UNIT_MILLISECONDS;
648 }
649
650
651 /**
652  * Process pending requests to the resolver.
653  */
654 static void
655 process_requests ()
656 {
657   struct GNUNET_RESOLVER_GetMessage *msg;
658   char buf[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1] GNUNET_ALIGN;
659   struct GNUNET_RESOLVER_RequestHandle *rh;
660
661   if (NULL == client)
662   {
663     reconnect ();
664     return;
665   }
666   rh = req_head;
667   if (NULL == rh)
668   {
669     /* nothing to do, release socket really soon if there is nothing
670      * else happening... */
671     s_task =
672         GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS,
673                                       &shutdown_task,
674                                       NULL);
675     return;
676   }
677   if (GNUNET_YES == rh->was_transmitted)
678     return;                     /* waiting for reply */
679   msg = (struct GNUNET_RESOLVER_GetMessage *) buf;
680   msg->header.size =
681       htons (sizeof (struct GNUNET_RESOLVER_GetMessage) + rh->data_len);
682   msg->header.type = htons (GNUNET_MESSAGE_TYPE_RESOLVER_REQUEST);
683   msg->direction = htonl (rh->direction);
684   msg->af = htonl (rh->af);
685   memcpy (&msg[1],
686           &rh[1],
687           rh->data_len);
688   LOG (GNUNET_ERROR_TYPE_DEBUG,
689        "Transmitting DNS resolution request to DNS service\n");
690   if (GNUNET_OK !=
691       GNUNET_CLIENT_transmit_and_get_response (client,
692                                                &msg->header,
693                                                GNUNET_TIME_absolute_get_remaining (rh->timeout),
694                                                GNUNET_YES,
695                                                &handle_response,
696                                                rh))
697   {
698     GNUNET_CLIENT_disconnect (client);
699     client = NULL;
700     GNUNET_break (0);
701     reconnect ();
702     return;
703   }
704   rh->was_transmitted = GNUNET_YES;
705 }
706
707
708 /**
709  * Now try to reconnect to the resolver service.
710  *
711  * @param cls NULL
712  * @param tc scheduler context
713  */
714 static void
715 reconnect_task (void *cls,
716                 const struct GNUNET_SCHEDULER_TaskContext *tc)
717 {
718   r_task = NULL;
719   if (NULL == req_head)
720     return;                     /* no work pending */
721   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
722     return;
723   LOG (GNUNET_ERROR_TYPE_DEBUG,
724        "Trying to connect to DNS service\n");
725   client = GNUNET_CLIENT_connect ("resolver",
726                                   resolver_cfg);
727   if (NULL == client)
728   {
729     LOG (GNUNET_ERROR_TYPE_DEBUG,
730          "Failed to connect, will try again later\n");
731     reconnect ();
732     return;
733   }
734   process_requests ();
735 }
736
737
738 /**
739  * Adjust exponential back-off and reconnect to the service.
740  */
741 static void
742 reconnect ()
743 {
744   struct GNUNET_RESOLVER_RequestHandle *rh;
745
746   if (NULL != r_task)
747     return;
748   GNUNET_assert (NULL == client);
749   if (NULL != (rh = req_head))
750   {
751     switch (rh->was_transmitted)
752     {
753     case GNUNET_NO:
754       /* nothing more to do */
755       break;
756     case GNUNET_YES:
757       /* disconnected, transmit again! */
758       rh->was_transmitted = GNUNET_NO;
759       break;
760     case GNUNET_SYSERR:
761       /* request was cancelled, remove entirely */
762       GNUNET_CONTAINER_DLL_remove (req_head, req_tail, rh);
763       GNUNET_free (rh);
764       break;
765     default:
766       GNUNET_assert (0);
767       break;
768     }
769   }
770   LOG (GNUNET_ERROR_TYPE_DEBUG,
771        "Will try to connect to DNS service in %s\n",
772        GNUNET_STRINGS_relative_time_to_string (backoff,
773                                                GNUNET_YES));
774   GNUNET_assert (NULL != resolver_cfg);
775   r_task = GNUNET_SCHEDULER_add_delayed (backoff,
776                                          &reconnect_task,
777                                          NULL);
778   backoff = GNUNET_TIME_STD_BACKOFF (backoff);
779 }
780
781
782 /**
783  * A DNS resolution timed out. Notify the application.
784  *
785  * @param cls the `struct GNUNET_RESOLVER_RequestHandle *`
786  * @param tc scheduler context
787  */
788 static void
789 handle_lookup_timeout (void *cls,
790                        const struct GNUNET_SCHEDULER_TaskContext *tc)
791 {
792   struct GNUNET_RESOLVER_RequestHandle *rh = cls;
793
794   rh->task = NULL;
795   rh->addr_callback (rh->cls,
796                      NULL,
797                      0);
798   GNUNET_RESOLVER_request_cancel (rh);
799 }
800
801
802 /**
803  * Convert a string to one or more IP addresses.
804  *
805  * @param hostname the hostname to resolve
806  * @param af AF_INET or AF_INET6; use AF_UNSPEC for "any"
807  * @param callback function to call with addresses
808  * @param callback_cls closure for @a callback
809  * @param timeout how long to try resolving
810  * @return handle that can be used to cancel the request, NULL on error
811  */
812 struct GNUNET_RESOLVER_RequestHandle *
813 GNUNET_RESOLVER_ip_get (const char *hostname, int af,
814                         struct GNUNET_TIME_Relative timeout,
815                         GNUNET_RESOLVER_AddressCallback callback,
816                         void *callback_cls)
817 {
818   struct GNUNET_RESOLVER_RequestHandle *rh;
819   size_t slen;
820   unsigned int i;
821   struct in_addr v4;
822   struct in6_addr v6;
823
824   slen = strlen (hostname) + 1;
825   if (slen + sizeof (struct GNUNET_RESOLVER_GetMessage) >=
826       GNUNET_SERVER_MAX_MESSAGE_SIZE)
827   {
828     GNUNET_break (0);
829     return NULL;
830   }
831   rh = GNUNET_malloc (sizeof (struct GNUNET_RESOLVER_RequestHandle) + slen);
832   rh->af = af;
833   rh->addr_callback = callback;
834   rh->cls = callback_cls;
835   memcpy (&rh[1],
836           hostname,
837           slen);
838   rh->data_len = slen;
839   rh->timeout = GNUNET_TIME_relative_to_absolute (timeout);
840   rh->direction = GNUNET_NO;
841   /* first, check if this is a numeric address */
842   if (((1 == inet_pton (AF_INET, hostname, &v4)) &&
843        ((af == AF_INET) || (af == AF_UNSPEC))) ||
844       ((1 == inet_pton (AF_INET6, hostname, &v6)) &&
845        ((af == AF_INET6) || (af == AF_UNSPEC))))
846   {
847     rh->task = GNUNET_SCHEDULER_add_now (&numeric_resolution,
848                                          rh);
849     return rh;
850   }
851   /* then, check if this is a loopback address */
852   i = 0;
853   while (NULL != loopback[i])
854     if (0 == strcasecmp (loopback[i++],
855                          hostname))
856     {
857       rh->task = GNUNET_SCHEDULER_add_now (&loopback_resolution,
858                                            rh);
859       return rh;
860     }
861   rh->task = GNUNET_SCHEDULER_add_delayed (timeout,
862                                            &handle_lookup_timeout,
863                                            rh);
864   GNUNET_CONTAINER_DLL_insert_tail (req_head,
865                                     req_tail,
866                                     rh);
867   rh->was_queued = GNUNET_YES;
868   if (NULL != s_task)
869   {
870     GNUNET_SCHEDULER_cancel (s_task);
871     s_task = NULL;
872   }
873   process_requests ();
874   return rh;
875 }
876
877
878 /**
879  * We've been asked to convert an address to a string without
880  * a reverse lookup, either because the client asked for it
881  * or because the DNS lookup hit a timeout.  Do the numeric
882  * conversion and invoke the callback.
883  *
884  * @param cls `struct GNUNET_RESOLVER_RequestHandle` for the request
885  * @param tc unused scheduler context
886  */
887 static void
888 numeric_reverse (void *cls,
889                  const struct GNUNET_SCHEDULER_TaskContext *tc)
890 {
891   struct GNUNET_RESOLVER_RequestHandle *rh = cls;
892   char *result;
893
894   rh->task = NULL;
895   result = no_resolve (rh->af,
896                        &rh[1],
897                        rh->data_len);
898   LOG (GNUNET_ERROR_TYPE_DEBUG,
899        "Resolver returns `%s'.\n",
900        result);
901   if (NULL != result)
902   {
903     rh->name_callback (rh->cls,
904                        result);
905     GNUNET_free (result);
906   }
907   rh->name_callback (rh->cls,
908                      NULL);
909   GNUNET_free (rh);
910 }
911
912
913 /**
914  * Get an IP address as a string.
915  *
916  * @param sa host address
917  * @param salen length of host address in @a sa
918  * @param do_resolve use #GNUNET_NO to return numeric hostname
919  * @param timeout how long to try resolving
920  * @param callback function to call with hostnames
921  *        last callback is NULL when finished
922  * @param cls closure for @a callback
923  * @return handle that can be used to cancel the request
924  */
925 struct GNUNET_RESOLVER_RequestHandle *
926 GNUNET_RESOLVER_hostname_get (const struct sockaddr *sa,
927                               socklen_t salen,
928                               int do_resolve,
929                               struct GNUNET_TIME_Relative timeout,
930                               GNUNET_RESOLVER_HostnameCallback callback,
931                               void *cls)
932 {
933   struct GNUNET_RESOLVER_RequestHandle *rh;
934   size_t ip_len;
935   const void *ip;
936
937   check_config ();
938   switch (sa->sa_family)
939   {
940   case AF_INET:
941     ip_len = sizeof (struct in_addr);
942     ip = &((const struct sockaddr_in*)sa)->sin_addr;
943     break;
944   case AF_INET6:
945     ip_len = sizeof (struct in6_addr);
946     ip = &((const struct sockaddr_in6*)sa)->sin6_addr;
947     break;
948   default:
949     GNUNET_break (0);
950     return NULL;
951   }
952   rh = GNUNET_malloc (sizeof (struct GNUNET_RESOLVER_RequestHandle) + salen);
953   rh->name_callback = callback;
954   rh->cls = cls;
955   rh->af = sa->sa_family;
956   rh->timeout = GNUNET_TIME_relative_to_absolute (timeout);
957   memcpy (&rh[1], ip, ip_len);
958   rh->data_len = ip_len;
959   rh->direction = GNUNET_YES;
960   rh->received_response = GNUNET_NO;
961   if (GNUNET_NO == do_resolve)
962   {
963     rh->task = GNUNET_SCHEDULER_add_now (&numeric_reverse, rh);
964     return rh;
965   }
966   GNUNET_CONTAINER_DLL_insert_tail (req_head,
967                                     req_tail,
968                                     rh);
969   rh->was_queued = GNUNET_YES;
970   if (NULL != s_task)
971   {
972     GNUNET_SCHEDULER_cancel (s_task);
973     s_task = NULL;
974   }
975   process_requests ();
976   return rh;
977 }
978
979
980 /**
981  * Get local fully qualified af name
982  *
983  * @return fqdn
984  */
985 char *
986 GNUNET_RESOLVER_local_fqdn_get ()
987 {
988   struct hostent *host;
989   char hostname[GNUNET_OS_get_hostname_max_length () + 1];
990
991   if (0 != gethostname (hostname, sizeof (hostname) - 1))
992   {
993     LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
994                   "gethostname");
995     return NULL;
996   }
997   LOG (GNUNET_ERROR_TYPE_DEBUG,
998        "Resolving our FQDN `%s'\n",
999        hostname);
1000   host = gethostbyname (hostname);
1001   if (NULL == host)
1002   {
1003     LOG (GNUNET_ERROR_TYPE_ERROR,
1004          _("Could not resolve our FQDN : %s\n"),
1005          hstrerror (h_errno));
1006     return NULL;
1007   }
1008   return GNUNET_strdup (host->h_name);
1009 }
1010
1011
1012 /**
1013  * Looking our own hostname.
1014  *
1015  * @param af AF_INET or AF_INET6; use AF_UNSPEC for "any"
1016  * @param timeout how long to try resolving
1017  * @param callback function to call with addresses
1018  * @param cls closure for @a callback
1019  * @return handle that can be used to cancel the request, NULL on error
1020  */
1021 struct GNUNET_RESOLVER_RequestHandle *
1022 GNUNET_RESOLVER_hostname_resolve (int af,
1023                                   struct GNUNET_TIME_Relative timeout,
1024                                   GNUNET_RESOLVER_AddressCallback callback,
1025                                   void *cls)
1026 {
1027   char hostname[GNUNET_OS_get_hostname_max_length () + 1];
1028
1029   if (0 != gethostname (hostname, sizeof (hostname) - 1))
1030   {
1031     LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1032                   "gethostname");
1033     return NULL;
1034   }
1035   LOG (GNUNET_ERROR_TYPE_DEBUG,
1036        "Resolving our hostname `%s'\n",
1037        hostname);
1038   return GNUNET_RESOLVER_ip_get (hostname,
1039                                  af,
1040                                  timeout,
1041                                  callback,
1042                                  cls);
1043 }
1044
1045
1046 /**
1047  * Cancel a request that is still pending with the resolver.
1048  * Note that a client MUST NOT cancel a request that has
1049  * been completed (i.e, the callback has been called to
1050  * signal timeout or the final result).
1051  *
1052  * @param rh handle of request to cancel
1053  */
1054 void
1055 GNUNET_RESOLVER_request_cancel (struct GNUNET_RESOLVER_RequestHandle *rh)
1056 {
1057   if (NULL != rh->task)
1058   {
1059     GNUNET_SCHEDULER_cancel (rh->task);
1060     rh->task = NULL;
1061   }
1062   if (GNUNET_NO == rh->was_transmitted)
1063   {
1064     if (GNUNET_YES == rh->was_queued)
1065       GNUNET_CONTAINER_DLL_remove (req_head,
1066                                    req_tail,
1067                                    rh);
1068     GNUNET_free (rh);
1069     return;
1070   }
1071   GNUNET_assert (GNUNET_YES == rh->was_transmitted);
1072   rh->was_transmitted = GNUNET_SYSERR;  /* mark as cancelled */
1073 }
1074
1075
1076 /* end of resolver_api.c */