aboutsummaryrefslogtreecommitdiff
path: root/src/lib/util/resolver_api.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/util/resolver_api.c')
-rw-r--r--src/lib/util/resolver_api.c1295
1 files changed, 1295 insertions, 0 deletions
diff --git a/src/lib/util/resolver_api.c b/src/lib/util/resolver_api.c
new file mode 100644
index 000000000..130363c77
--- /dev/null
+++ b/src/lib/util/resolver_api.c
@@ -0,0 +1,1295 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2009-2018 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 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file util/resolver_api.c
23 * @brief resolver for writing a tool
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include "gnunet_util_lib.h"
29#include "gnunet_protocols.h"
30#include "gnunet_resolver_service.h"
31#include "resolver.h"
32
33#define LOG(kind, ...) GNUNET_log_from (kind, "util-resolver-api", __VA_ARGS__)
34
35#define LOG_STRERROR(kind, syscall) GNUNET_log_from_strerror (kind, \
36 "util-resolver-api", \
37 syscall)
38
39/**
40 * Maximum supported length for a hostname
41 */
42#define MAX_HOSTNAME 1024
43
44
45/**
46 * Possible hostnames for "loopback".
47 */
48static const char *loopback[] = {
49 "localhost",
50 "ip6-localnet",
51 NULL
52};
53
54
55/**
56 * Configuration.
57 */
58static const struct GNUNET_CONFIGURATION_Handle *resolver_cfg;
59
60/**
61 * Our connection to the resolver service, created on-demand, but then
62 * persists until error or shutdown.
63 */
64static struct GNUNET_MQ_Handle *mq;
65
66/**
67 * Head of DLL of requests.
68 */
69static struct GNUNET_RESOLVER_RequestHandle *req_head;
70
71/**
72 * Tail of DLL of requests.
73 */
74static struct GNUNET_RESOLVER_RequestHandle *req_tail;
75
76/**
77 * ID of the last request we sent to the service
78 */
79static uint32_t last_request_id;
80
81/**
82 * How long should we wait to reconnect?
83 */
84static struct GNUNET_TIME_Relative backoff;
85
86/**
87 * Task for reconnecting.
88 */
89static struct GNUNET_SCHEDULER_Task *r_task;
90
91/**
92 * Task ID of shutdown task; only present while we have a
93 * connection to the resolver service.
94 */
95static struct GNUNET_SCHEDULER_Task *s_task;
96
97
98/**
99 * Handle to a request given to the resolver. Can be used to cancel
100 * the request prior to the timeout or successful execution. Also
101 * used to track our internal state for the request.
102 */
103struct GNUNET_RESOLVER_RequestHandle
104{
105 /**
106 * Next entry in DLL of requests.
107 */
108 struct GNUNET_RESOLVER_RequestHandle *next;
109
110 /**
111 * Previous entry in DLL of requests.
112 */
113 struct GNUNET_RESOLVER_RequestHandle *prev;
114
115 /**
116 * Callback if this is an name resolution request,
117 * otherwise NULL.
118 */
119 GNUNET_RESOLVER_AddressCallback addr_callback;
120
121 /**
122 * Callback if this is a reverse lookup request,
123 * otherwise NULL.
124 */
125 GNUNET_RESOLVER_HostnameCallback name_callback;
126
127 /**
128 * Closure for the callbacks.
129 */
130 void *cls;
131
132 /**
133 * When should this request time out?
134 */
135 struct GNUNET_TIME_Absolute timeout;
136
137 /**
138 * Task handle for making reply callbacks in numeric lookups
139 * asynchronous, and for timeout handling.
140 */
141 struct GNUNET_SCHEDULER_Task *task;
142
143 /**
144 * Desired address family.
145 */
146 int af;
147
148 /**
149 * Identifies the request. The response will contain this id.
150 */
151 uint32_t id;
152
153 /**
154 * Has this request been transmitted to the service?
155 * #GNUNET_YES if transmitted
156 * #GNUNET_NO if not transmitted
157 * #GNUNET_SYSERR when request was canceled
158 */
159 int was_transmitted;
160
161 /**
162 * Did we add this request to the queue?
163 */
164 int was_queued;
165
166 /**
167 * Desired direction (IP to name or name to IP)
168 */
169 int direction;
170
171 /**
172 * #GNUNET_YES if a response was received
173 */
174 int received_response;
175
176 /**
177 * Length of the data that follows this struct.
178 */
179 size_t data_len;
180};
181
182
183/**
184 * Check that the resolver service runs on localhost
185 * (or equivalent).
186 *
187 * @return #GNUNET_OK if the resolver is properly configured,
188 * #GNUNET_SYSERR otherwise.
189 */
190static int
191check_config ()
192{
193 char *hostname;
194 struct sockaddr_in v4;
195 struct sockaddr_in6 v6;
196
197 if (GNUNET_OK ==
198 GNUNET_CONFIGURATION_have_value (resolver_cfg,
199 "resolver",
200 "UNIXPATH"))
201 return GNUNET_OK;
202 memset (&v4, 0, sizeof(v4));
203 v4.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
204 v4.sin_family = AF_INET;
205#if HAVE_SOCKADDR_IN_SIN_LEN
206 v4.sin_len = sizeof(v4);
207#endif
208 memset (&v6, 0, sizeof(v6));
209 v6.sin6_family = AF_INET6;
210#if HAVE_SOCKADDR_IN_SIN_LEN
211 v6.sin6_len = sizeof(v6);
212#endif
213 if (GNUNET_OK !=
214 GNUNET_CONFIGURATION_get_value_string (resolver_cfg,
215 "resolver",
216 "HOSTNAME",
217 &hostname))
218 {
219 LOG (GNUNET_ERROR_TYPE_INFO,
220 _ (
221 "Missing `%s' for `%s' in configuration, DNS resolution will be unavailable.\n"),
222 "HOSTNAME",
223 "resolver");
224 return GNUNET_SYSERR;
225 }
226 if ((1 == inet_pton (AF_INET, hostname, &v4)) ||
227 (1 == inet_pton (AF_INET6, hostname, &v6)))
228 {
229 GNUNET_free (hostname);
230 return GNUNET_OK;
231 }
232 for (unsigned int i = 0;
233 NULL != loopback[i];
234 i++)
235 if (0 == strcasecmp (loopback[i],
236 hostname))
237 {
238 GNUNET_free (hostname);
239 return GNUNET_OK;
240 }
241 LOG (GNUNET_ERROR_TYPE_INFO,
242 _ (
243 "Missing `%s' or numeric IP address for `%s' of `%s' in configuration, DNS resolution will be unavailable.\n"),
244 "localhost",
245 "HOSTNAME",
246 "resolver");
247 GNUNET_free (hostname);
248 return GNUNET_SYSERR;
249}
250
251
252/**
253 * Create the connection to the resolver service.
254 *
255 * @param cfg configuration to use
256 */
257void
258GNUNET_RESOLVER_connect (const struct GNUNET_CONFIGURATION_Handle *cfg)
259{
260 GNUNET_assert (NULL != cfg);
261 backoff = GNUNET_TIME_UNIT_MILLISECONDS;
262 resolver_cfg = cfg;
263}
264
265
266/**
267 * Destroy the connection to the resolver service.
268 */
269void
270GNUNET_RESOLVER_disconnect ()
271{
272 struct GNUNET_RESOLVER_RequestHandle *rh;
273
274 while (NULL != (rh = req_head))
275 {
276 GNUNET_assert (GNUNET_SYSERR == rh->was_transmitted);
277 GNUNET_CONTAINER_DLL_remove (req_head,
278 req_tail,
279 rh);
280 GNUNET_free (rh);
281 }
282 if (NULL != mq)
283 {
284 LOG (GNUNET_ERROR_TYPE_DEBUG,
285 "Disconnecting from DNS service\n");
286 GNUNET_MQ_destroy (mq);
287 mq = NULL;
288 }
289 if (NULL != r_task)
290 {
291 GNUNET_SCHEDULER_cancel (r_task);
292 r_task = NULL;
293 }
294 if (NULL != s_task)
295 {
296 GNUNET_SCHEDULER_cancel (s_task);
297 s_task = NULL;
298 }
299}
300
301
302/**
303 * Task executed on system shutdown.
304 */
305static void
306shutdown_task (void *cls)
307{
308 (void) cls;
309 s_task = NULL;
310 GNUNET_RESOLVER_disconnect ();
311 backoff = GNUNET_TIME_UNIT_MILLISECONDS;
312}
313
314
315/**
316 * Consider disconnecting if we have no further requests pending.
317 */
318static void
319check_disconnect ()
320{
321 for (struct GNUNET_RESOLVER_RequestHandle *rh = req_head;
322 NULL != rh;
323 rh = rh->next)
324 if (GNUNET_SYSERR != rh->was_transmitted)
325 return;
326 if (NULL != r_task)
327 {
328 GNUNET_SCHEDULER_cancel (r_task);
329 r_task = NULL;
330 }
331 if (NULL != s_task)
332 return;
333 s_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS,
334 &shutdown_task,
335 NULL);
336}
337
338
339/**
340 * Convert IP address to string without DNS resolution.
341 *
342 * @param af address family
343 * @param ip the address
344 * @param ip_len number of bytes in @a ip
345 * @return address as a string, NULL on error
346 */
347static char *
348no_resolve (int af,
349 const void *ip,
350 socklen_t ip_len)
351{
352 char buf[INET6_ADDRSTRLEN];
353
354 switch (af)
355 {
356 case AF_INET:
357 if (ip_len != sizeof(struct in_addr))
358 return NULL;
359 if (NULL ==
360 inet_ntop (AF_INET,
361 ip,
362 buf,
363 sizeof(buf)))
364 {
365 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING,
366 "inet_ntop");
367 return NULL;
368 }
369 break;
370
371 case AF_INET6:
372 if (ip_len != sizeof(struct in6_addr))
373 return NULL;
374 if (NULL ==
375 inet_ntop (AF_INET6,
376 ip,
377 buf,
378 sizeof(buf)))
379 {
380 LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING,
381 "inet_ntop");
382 return NULL;
383 }
384 break;
385
386 default:
387 GNUNET_break (0);
388 return NULL;
389 }
390 return GNUNET_strdup (buf);
391}
392
393
394/**
395 * Adjust exponential back-off and reconnect to the service.
396 */
397static void
398reconnect (void);
399
400
401/**
402 * Generic error handler, called with the appropriate error code and
403 * the same closure specified at the creation of the message queue.
404 * Not every message queue implementation supports an error handler.
405 *
406 * @param cls NULL
407 * @param error error code
408 */
409static void
410mq_error_handler (void *cls,
411 enum GNUNET_MQ_Error error)
412{
413 (void) cls;
414 GNUNET_MQ_destroy (mq);
415 mq = NULL;
416 LOG (GNUNET_ERROR_TYPE_DEBUG,
417 "MQ error %d, reconnecting\n",
418 error);
419 reconnect ();
420}
421
422
423/**
424 * Process pending requests to the resolver.
425 */
426static void
427process_requests ()
428{
429 struct GNUNET_RESOLVER_GetMessage *msg;
430 struct GNUNET_MQ_Envelope *env;
431 struct GNUNET_RESOLVER_RequestHandle *rh = req_head;
432
433 if (NULL == mq)
434 {
435 reconnect ();
436 return;
437 }
438 if (NULL == rh)
439 {
440 /* nothing to do, release socket really soon if there is nothing
441 * else happening... */
442 if (NULL == s_task)
443 s_task =
444 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS,
445 &shutdown_task,
446 NULL);
447 return;
448 }
449 if (GNUNET_NO != rh->was_transmitted)
450 return; /* waiting for reply */
451 env = GNUNET_MQ_msg_extra (msg,
452 rh->data_len,
453 GNUNET_MESSAGE_TYPE_RESOLVER_REQUEST);
454 msg->direction = htonl (rh->direction);
455 msg->af = htonl (rh->af);
456 msg->client_id = rh->id;
457 GNUNET_memcpy (&msg[1],
458 &rh[1],
459 rh->data_len);
460 LOG (GNUNET_ERROR_TYPE_DEBUG,
461 "Transmitting DNS resolution request (ID %u) to DNS service\n",
462 rh->id);
463 GNUNET_MQ_send (mq,
464 env);
465 rh->was_transmitted = GNUNET_YES;
466}
467
468
469/**
470 * Check validity of response with a hostname for a DNS lookup.
471 *
472 * @param cls NULL
473 * @param msg message with the hostname
474 */
475static int
476check_response (void *cls,
477 const struct GNUNET_RESOLVER_ResponseMessage *msg)
478{
479 (void) cls;
480 (void) msg;
481
482 /* implemented in #handle_response() for now */
483 return GNUNET_OK;
484}
485
486
487/**
488 * Check validity of response with a hostname for a DNS lookup.
489 * NOTE: right now rather messy, might want to use different
490 * message types for different response formats in the future.
491 *
492 * @param cls NULL
493 * @param msg message with the response
494 */
495static void
496handle_response (void *cls,
497 const struct GNUNET_RESOLVER_ResponseMessage *msg)
498{
499 struct GNUNET_RESOLVER_RequestHandle *rh = req_head;
500 uint16_t size;
501 char *nret;
502 uint32_t client_request_id = msg->client_id;
503
504 for (; rh != NULL; rh = rh->next)
505 {
506 if (rh->id == client_request_id)
507 break;
508 }
509
510 (void) cls;
511 if (NULL == rh)
512 {
513 /* Resolver service sent extra replies to query (after terminator)? Bad! */
514 GNUNET_break (0);
515 GNUNET_MQ_destroy (mq);
516 mq = NULL;
517 reconnect ();
518 return;
519 }
520 size = ntohs (msg->header.size);
521 if (size == sizeof(struct GNUNET_RESOLVER_ResponseMessage))
522 {
523 LOG (GNUNET_ERROR_TYPE_DEBUG,
524 "Received empty response from DNS service\n");
525 /* message contains not data, just header; end of replies */
526 /* check if request was canceled */
527 if (GNUNET_SYSERR != rh->was_transmitted)
528 {
529 /* no reverse lookup was successful, return IP as string */
530 if (NULL != rh->name_callback)
531 {
532 if (GNUNET_NO == rh->received_response)
533 {
534 nret = no_resolve (rh->af,
535 &rh[1],
536 rh->data_len);
537 rh->name_callback (rh->cls, nret);
538 GNUNET_free (nret);
539 }
540 /* finally, make termination call */
541 if (GNUNET_SYSERR != rh->was_transmitted)
542 rh->name_callback (rh->cls,
543 NULL);
544 }
545 if ((NULL != rh->addr_callback) &&
546 (GNUNET_SYSERR != rh->was_transmitted))
547 rh->addr_callback (rh->cls,
548 NULL,
549 0);
550 }
551 rh->was_transmitted = GNUNET_NO;
552 GNUNET_RESOLVER_request_cancel (rh);
553 process_requests ();
554 return;
555 }
556 /* return reverse lookup results to caller */
557 if (NULL != rh->name_callback)
558 {
559 const char *hostname;
560
561 hostname = (const char *) &msg[1];
562 if (hostname[size - sizeof(struct GNUNET_RESOLVER_ResponseMessage) - 1] !=
563 '\0')
564 {
565 GNUNET_break (0);
566 if (GNUNET_SYSERR != rh->was_transmitted)
567 rh->name_callback (rh->cls,
568 NULL);
569 rh->was_transmitted = GNUNET_NO;
570 GNUNET_RESOLVER_request_cancel (rh);
571 GNUNET_MQ_destroy (mq);
572 mq = NULL;
573 reconnect ();
574 return;
575 }
576 LOG (GNUNET_ERROR_TYPE_DEBUG,
577 "Resolver returns `%s' for IP `%s'.\n",
578 hostname,
579 GNUNET_a2s ((const void *) &rh[1],
580 rh->data_len));
581 if (rh->was_transmitted != GNUNET_SYSERR)
582 rh->name_callback (rh->cls,
583 hostname);
584 rh->received_response = GNUNET_YES;
585 }
586 /* return lookup results to caller */
587 if (NULL != rh->addr_callback)
588 {
589 struct sockaddr_in v4;
590 struct sockaddr_in6 v6;
591 const struct sockaddr *sa;
592 socklen_t salen;
593 const void *ip;
594 size_t ip_len;
595
596 ip = &msg[1];
597 ip_len = size - sizeof(struct GNUNET_RESOLVER_ResponseMessage);
598 if (ip_len == sizeof(struct in_addr))
599 {
600 memset (&v4, 0, sizeof(v4));
601 v4.sin_family = AF_INET;
602 v4.sin_addr = *(struct in_addr*) ip;
603#if HAVE_SOCKADDR_IN_SIN_LEN
604 v4.sin_len = sizeof(v4);
605#endif
606 salen = sizeof(v4);
607 sa = (const struct sockaddr *) &v4;
608 }
609 else if (ip_len == sizeof(struct in6_addr))
610 {
611 memset (&v6, 0, sizeof(v6));
612 v6.sin6_family = AF_INET6;
613 v6.sin6_addr = *(struct in6_addr*) ip;
614#if HAVE_SOCKADDR_IN_SIN_LEN
615 v6.sin6_len = sizeof(v6);
616#endif
617 salen = sizeof(v6);
618 sa = (const struct sockaddr *) &v6;
619 }
620 else
621 {
622 GNUNET_break (0);
623 if (GNUNET_SYSERR != rh->was_transmitted)
624 rh->addr_callback (rh->cls,
625 NULL,
626 0);
627 rh->was_transmitted = GNUNET_NO;
628 GNUNET_RESOLVER_request_cancel (rh);
629 GNUNET_MQ_destroy (mq);
630 mq = NULL;
631 reconnect ();
632 return;
633 }
634 LOG (GNUNET_ERROR_TYPE_DEBUG,
635 "Received IP from DNS service\n");
636 if (GNUNET_SYSERR != rh->was_transmitted)
637 rh->addr_callback (rh->cls,
638 sa,
639 salen);
640 }
641}
642
643
644/**
645 * We've been asked to lookup the address for a hostname and were
646 * given a valid numeric string. Perform the callbacks for the
647 * numeric addresses.
648 *
649 * @param cls `struct GNUNET_RESOLVER_RequestHandle` for the request
650 */
651static void
652numeric_resolution (void *cls)
653{
654 struct GNUNET_RESOLVER_RequestHandle *rh = cls;
655 struct sockaddr_in v4;
656 struct sockaddr_in6 v6;
657 const char *hostname;
658
659 rh->task = NULL;
660 memset (&v4, 0, sizeof(v4));
661 v4.sin_family = AF_INET;
662#if HAVE_SOCKADDR_IN_SIN_LEN
663 v4.sin_len = sizeof(v4);
664#endif
665 memset (&v6, 0, sizeof(v6));
666 v6.sin6_family = AF_INET6;
667#if HAVE_SOCKADDR_IN_SIN_LEN
668 v6.sin6_len = sizeof(v6);
669#endif
670 hostname = (const char *) &rh[1];
671 if (((rh->af == AF_UNSPEC) ||
672 (rh->af == AF_INET)) &&
673 (1 == inet_pton (AF_INET,
674 hostname,
675 &v4.sin_addr)))
676 {
677 rh->addr_callback (rh->cls,
678 (const struct sockaddr *) &v4,
679 sizeof(v4));
680 if ((rh->af == AF_UNSPEC) &&
681 (GNUNET_SYSERR != rh->was_transmitted) &&
682 (1 == inet_pton (AF_INET6,
683 hostname,
684 &v6.sin6_addr)))
685 {
686 /* this can happen on some systems IF "hostname" is "localhost" */
687 rh->addr_callback (rh->cls,
688 (const struct sockaddr *) &v6,
689 sizeof(v6));
690 }
691 if (GNUNET_SYSERR != rh->was_transmitted)
692 rh->addr_callback (rh->cls,
693 NULL,
694 0);
695 GNUNET_free (rh);
696 return;
697 }
698 if (((rh->af == AF_UNSPEC) ||
699 (rh->af == AF_INET6)) &&
700 (1 == inet_pton (AF_INET6,
701 hostname,
702 &v6.sin6_addr)))
703 {
704 rh->addr_callback (rh->cls,
705 (const struct sockaddr *) &v6,
706 sizeof(v6));
707 if (GNUNET_SYSERR != rh->was_transmitted)
708 rh->addr_callback (rh->cls,
709 NULL,
710 0);
711 GNUNET_free (rh);
712 return;
713 }
714 /* why are we here? this task should not have been scheduled! */
715 GNUNET_assert (0);
716 GNUNET_free (rh);
717}
718
719
720/**
721 * We've been asked to lookup the address for a hostname and were
722 * given a variant of "loopback". Perform the callbacks for the
723 * respective loopback numeric addresses.
724 *
725 * @param cls `struct GNUNET_RESOLVER_RequestHandle` for the request
726 */
727static void
728loopback_resolution (void *cls)
729{
730 struct GNUNET_RESOLVER_RequestHandle *rh = cls;
731 struct sockaddr_in v4;
732 struct sockaddr_in6 v6;
733
734 rh->task = NULL;
735 memset (&v4, 0, sizeof(v4));
736 v4.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
737 v4.sin_family = AF_INET;
738#if HAVE_SOCKADDR_IN_SIN_LEN
739 v4.sin_len = sizeof(v4);
740#endif
741 memset (&v6, 0, sizeof(v6));
742 v6.sin6_family = AF_INET6;
743#if HAVE_SOCKADDR_IN_SIN_LEN
744 v6.sin6_len = sizeof(v6);
745#endif
746 v6.sin6_addr = in6addr_loopback;
747 switch (rh->af)
748 {
749 case AF_INET:
750 rh->addr_callback (rh->cls,
751 (const struct sockaddr *) &v4,
752 sizeof(v4));
753 break;
754
755 case AF_INET6:
756 rh->addr_callback (rh->cls,
757 (const struct sockaddr *) &v6,
758 sizeof(v6));
759 break;
760
761 case AF_UNSPEC:
762 rh->addr_callback (rh->cls,
763 (const struct sockaddr *) &v6,
764 sizeof(v6));
765 rh->addr_callback (rh->cls,
766 (const struct sockaddr *) &v4,
767 sizeof(v4));
768
769 break;
770
771 default:
772 GNUNET_break (0);
773 break;
774 }
775 if (GNUNET_SYSERR != rh->was_transmitted)
776 rh->addr_callback (rh->cls,
777 NULL,
778 0);
779 LOG (GNUNET_ERROR_TYPE_DEBUG,
780 "Finished resolving hostname `%s'.\n",
781 (const char *) &rh[1]);
782 GNUNET_free (rh);
783}
784
785
786/**
787 * Now try to reconnect to the resolver service.
788 *
789 * @param cls NULL
790 */
791static void
792reconnect_task (void *cls)
793{
794 struct GNUNET_MQ_MessageHandler handlers[] = {
795 GNUNET_MQ_hd_var_size (response,
796 GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE,
797 struct GNUNET_RESOLVER_ResponseMessage,
798 NULL),
799 GNUNET_MQ_handler_end ()
800 };
801
802 (void) cls;
803 r_task = NULL;
804 if (NULL == req_head)
805 return; /* no work pending */
806 LOG (GNUNET_ERROR_TYPE_DEBUG,
807 "Trying to connect to DNS service\n");
808 mq = GNUNET_CLIENT_connect (resolver_cfg,
809 "resolver",
810 handlers,
811 &mq_error_handler,
812 NULL);
813 if (NULL == mq)
814 {
815 LOG (GNUNET_ERROR_TYPE_DEBUG,
816 "Failed to connect, will try again later\n");
817 reconnect ();
818 return;
819 }
820 process_requests ();
821}
822
823
824/**
825 * Adjust exponential back-off and reconnect to the service.
826 */
827static void
828reconnect ()
829{
830 struct GNUNET_RESOLVER_RequestHandle *rh;
831
832 if (NULL != r_task)
833 return;
834 GNUNET_assert (NULL == mq);
835 if (NULL != (rh = req_head))
836 {
837 switch (rh->was_transmitted)
838 {
839 case GNUNET_NO:
840 /* nothing more to do */
841 break;
842
843 case GNUNET_YES:
844 /* disconnected, transmit again! */
845 rh->was_transmitted = GNUNET_NO;
846 break;
847
848 case GNUNET_SYSERR:
849 /* request was cancelled, remove entirely */
850 GNUNET_CONTAINER_DLL_remove (req_head,
851 req_tail,
852 rh);
853 GNUNET_free (rh);
854 check_disconnect ();
855 break;
856
857 default:
858 GNUNET_assert (0);
859 break;
860 }
861 }
862 LOG (GNUNET_ERROR_TYPE_DEBUG,
863 "Will try to connect to DNS service in %s\n",
864 GNUNET_STRINGS_relative_time_to_string (backoff,
865 GNUNET_YES));
866 GNUNET_assert (NULL != resolver_cfg);
867 r_task = GNUNET_SCHEDULER_add_delayed (backoff,
868 &reconnect_task,
869 NULL);
870 backoff = GNUNET_TIME_STD_BACKOFF (backoff);
871}
872
873
874/**
875 * A DNS resolution timed out. Notify the application.
876 *
877 * @param cls the `struct GNUNET_RESOLVER_RequestHandle *`
878 */
879static void
880handle_lookup_timeout (void *cls)
881{
882 struct GNUNET_RESOLVER_RequestHandle *rh = cls;
883
884 rh->task = NULL;
885 if (GNUNET_NO == rh->direction)
886 {
887 LOG (GNUNET_ERROR_TYPE_INFO,
888 _ ("Timeout trying to resolve hostname `%s'.\n"),
889 (const char *) &rh[1]);
890 if (NULL != rh->addr_callback)
891 rh->addr_callback (rh->cls,
892 NULL,
893 0);
894 }
895 else
896 {
897#if ! defined(GNUNET_CULL_LOGGING)
898 char buf[INET6_ADDRSTRLEN];
899
900 LOG (GNUNET_ERROR_TYPE_INFO,
901 _ ("Timeout trying to resolve IP address `%s'.\n"),
902 inet_ntop (rh->af,
903 (const void *) &rh[1],
904 buf,
905 sizeof(buf)));
906#endif
907 if (GNUNET_NO == rh->received_response)
908 {
909 char *nret;
910
911 nret = no_resolve (rh->af,
912 &rh[1],
913 rh->data_len);
914 if (NULL != rh->name_callback)
915 rh->name_callback (rh->cls, nret);
916 GNUNET_free (nret);
917 }
918 /* finally, make termination call */
919 if (NULL != rh->name_callback)
920 rh->name_callback (rh->cls,
921 NULL);
922 }
923 rh->was_transmitted = GNUNET_NO;
924 GNUNET_RESOLVER_request_cancel (rh);
925 process_requests ();
926}
927
928
929/**
930 * Convert a string to one or more IP addresses.
931 *
932 * @param hostname the hostname to resolve
933 * @param af AF_INET or AF_INET6; use AF_UNSPEC for "any"
934 * @param callback function to call with addresses
935 * @param callback_cls closure for @a callback
936 * @param timeout how long to try resolving
937 * @return handle that can be used to cancel the request, NULL on error
938 */
939struct GNUNET_RESOLVER_RequestHandle *
940GNUNET_RESOLVER_ip_get (const char *hostname,
941 int af,
942 struct GNUNET_TIME_Relative timeout,
943 GNUNET_RESOLVER_AddressCallback callback,
944 void *callback_cls)
945{
946 struct GNUNET_RESOLVER_RequestHandle *rh;
947 size_t slen;
948 struct in_addr v4;
949 struct in6_addr v6;
950
951 slen = strlen (hostname) + 1;
952 if (slen + sizeof(struct GNUNET_RESOLVER_GetMessage) >=
953 GNUNET_MAX_MESSAGE_SIZE)
954 {
955 GNUNET_break (0);
956 return NULL;
957 }
958 LOG (GNUNET_ERROR_TYPE_DEBUG,
959 "Trying to resolve hostname `%s'.\n",
960 hostname);
961 rh = GNUNET_malloc (sizeof(struct GNUNET_RESOLVER_RequestHandle) + slen);
962 rh->af = af;
963 rh->id = ++last_request_id;
964 rh->addr_callback = callback;
965 rh->cls = callback_cls;
966 GNUNET_memcpy (&rh[1],
967 hostname,
968 slen);
969 rh->data_len = slen;
970 rh->timeout = GNUNET_TIME_relative_to_absolute (timeout);
971 rh->direction = GNUNET_NO;
972 /* first, check if this is a numeric address */
973 if (((1 == inet_pton (AF_INET,
974 hostname,
975 &v4)) &&
976 ((af == AF_INET) ||
977 (af == AF_UNSPEC))) ||
978 ((1 == inet_pton (AF_INET6,
979 hostname,
980 &v6)) &&
981 ((af == AF_INET6) ||
982 (af == AF_UNSPEC))))
983 {
984 rh->task = GNUNET_SCHEDULER_add_now (&numeric_resolution,
985 rh);
986 return rh;
987 }
988 /* then, check if this is a loopback address */
989 for (unsigned int i = 0;
990 NULL != loopback[i];
991 i++)
992 if (0 == strcasecmp (loopback[i],
993 hostname))
994 {
995 rh->task = GNUNET_SCHEDULER_add_now (&loopback_resolution,
996 rh);
997 return rh;
998 }
999 if (GNUNET_OK != check_config ())
1000 {
1001 GNUNET_free (rh);
1002 return NULL;
1003 }
1004 rh->task = GNUNET_SCHEDULER_add_delayed (timeout,
1005 &handle_lookup_timeout,
1006 rh);
1007 GNUNET_CONTAINER_DLL_insert_tail (req_head,
1008 req_tail,
1009 rh);
1010 rh->was_queued = GNUNET_YES;
1011 if (NULL != s_task)
1012 {
1013 GNUNET_SCHEDULER_cancel (s_task);
1014 s_task = NULL;
1015 }
1016 process_requests ();
1017 return rh;
1018}
1019
1020
1021/**
1022 * We've been asked to convert an address to a string without
1023 * a reverse lookup, either because the client asked for it
1024 * or because the DNS lookup hit a timeout. Do the numeric
1025 * conversion and invoke the callback.
1026 *
1027 * @param cls `struct GNUNET_RESOLVER_RequestHandle` for the request
1028 */
1029static void
1030numeric_reverse (void *cls)
1031{
1032 struct GNUNET_RESOLVER_RequestHandle *rh = cls;
1033 char *result;
1034
1035 rh->task = NULL;
1036 result = no_resolve (rh->af,
1037 &rh[1],
1038 rh->data_len);
1039 LOG (GNUNET_ERROR_TYPE_DEBUG,
1040 "Resolver returns `%s'.\n",
1041 result);
1042 if (NULL != result)
1043 {
1044 rh->name_callback (rh->cls,
1045 result);
1046 GNUNET_free (result);
1047 }
1048 rh->name_callback (rh->cls,
1049 NULL);
1050 if (NULL != rh->task)
1051 {
1052 GNUNET_SCHEDULER_cancel (rh->task);
1053 rh->task = NULL;
1054 }
1055 GNUNET_free (rh);
1056}
1057
1058
1059/**
1060 * Get an IP address as a string.
1061 *
1062 * @param sa host address
1063 * @param salen length of host address in @a sa
1064 * @param do_resolve use #GNUNET_NO to return numeric hostname
1065 * @param timeout how long to try resolving
1066 * @param callback function to call with hostnames
1067 * last callback is NULL when finished
1068 * @param cls closure for @a callback
1069 * @return handle that can be used to cancel the request
1070 */
1071struct GNUNET_RESOLVER_RequestHandle *
1072GNUNET_RESOLVER_hostname_get (const struct sockaddr *sa,
1073 socklen_t salen,
1074 int do_resolve,
1075 struct GNUNET_TIME_Relative timeout,
1076 GNUNET_RESOLVER_HostnameCallback callback,
1077 void *cls)
1078{
1079 struct GNUNET_RESOLVER_RequestHandle *rh;
1080 size_t ip_len;
1081 const void *ip;
1082
1083 if (GNUNET_OK != check_config ())
1084 {
1085 LOG (GNUNET_ERROR_TYPE_ERROR,
1086 _ ("Resolver not configured correctly.\n"));
1087 return NULL;
1088 }
1089
1090 switch (sa->sa_family)
1091 {
1092 case AF_INET:
1093 GNUNET_assert (salen == sizeof(struct sockaddr_in));
1094 ip_len = sizeof(struct in_addr);
1095 ip = &((const struct sockaddr_in*) sa)->sin_addr;
1096 break;
1097
1098 case AF_INET6:
1099 GNUNET_assert (salen == sizeof(struct sockaddr_in6));
1100 ip_len = sizeof(struct in6_addr);
1101 ip = &((const struct sockaddr_in6*) sa)->sin6_addr;
1102 break;
1103
1104 default:
1105 GNUNET_break (0);
1106 return NULL;
1107 }
1108 rh = GNUNET_malloc (sizeof(struct GNUNET_RESOLVER_RequestHandle) + salen);
1109 rh->name_callback = callback;
1110 rh->cls = cls;
1111 rh->af = sa->sa_family;
1112 rh->id = ++last_request_id;
1113 rh->timeout = GNUNET_TIME_relative_to_absolute (timeout);
1114 GNUNET_memcpy (&rh[1],
1115 ip,
1116 ip_len);
1117 rh->data_len = ip_len;
1118 rh->direction = GNUNET_YES;
1119 rh->received_response = GNUNET_NO;
1120 if (GNUNET_NO == do_resolve)
1121 {
1122 rh->task = GNUNET_SCHEDULER_add_now (&numeric_reverse,
1123 rh);
1124 return rh;
1125 }
1126 rh->task = GNUNET_SCHEDULER_add_delayed (timeout,
1127 &handle_lookup_timeout,
1128 rh);
1129 GNUNET_CONTAINER_DLL_insert_tail (req_head,
1130 req_tail,
1131 rh);
1132 rh->was_queued = GNUNET_YES;
1133 if (NULL != s_task)
1134 {
1135 GNUNET_SCHEDULER_cancel (s_task);
1136 s_task = NULL;
1137 }
1138 process_requests ();
1139 return rh;
1140}
1141
1142
1143/**
1144 * Get local fully qualified af name
1145 *
1146 * @return fqdn
1147 */
1148char *
1149GNUNET_RESOLVER_local_fqdn_get ()
1150{
1151 char hostname[GNUNET_OS_get_hostname_max_length () + 1];
1152
1153 if (0 != gethostname (hostname,
1154 sizeof(hostname) - 1))
1155 {
1156 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1157 "gethostname");
1158 return NULL;
1159 }
1160 LOG (GNUNET_ERROR_TYPE_DEBUG,
1161 "Resolving our FQDN `%s'\n",
1162 hostname);
1163#if HAVE_GETADDRINFO
1164 {
1165 struct addrinfo *ai;
1166 int ret;
1167 char *rval;
1168
1169 if (0 != (ret = getaddrinfo (hostname,
1170 NULL,
1171 NULL,
1172 &ai)))
1173 {
1174 LOG (GNUNET_ERROR_TYPE_ERROR,
1175 _ ("Could not resolve our FQDN: %s\n"),
1176 gai_strerror (ret));
1177 return NULL;
1178 }
1179 if (NULL != ai->ai_canonname)
1180 rval = GNUNET_strdup (ai->ai_canonname);
1181 else
1182 rval = GNUNET_strdup (hostname);
1183 freeaddrinfo (ai);
1184 return rval;
1185 }
1186#elif HAVE_GETHOSTBYNAME2
1187 {
1188 struct hostent *host;
1189
1190 host = gethostbyname2 (hostname,
1191 AF_INET);
1192 if (NULL == host)
1193 host = gethostbyname2 (hostname,
1194 AF_INET6);
1195 if (NULL == host)
1196 {
1197 LOG (GNUNET_ERROR_TYPE_ERROR,
1198 _ ("Could not resolve our FQDN: %s\n"),
1199 hstrerror (h_errno));
1200 return NULL;
1201 }
1202 return GNUNET_strdup (host->h_name);
1203 }
1204#elif HAVE_GETHOSTBYNAME
1205 {
1206 struct hostent *host;
1207
1208 host = gethostbyname (hostname);
1209 if (NULL == host)
1210 {
1211 LOG (GNUNET_ERROR_TYPE_ERROR,
1212 _ ("Could not resolve our FQDN: %s\n"),
1213 hstrerror (h_errno));
1214 return NULL;
1215 }
1216 return GNUNET_strdup (host->h_name);
1217 }
1218#else
1219 /* fallback: just hope name is already FQDN */
1220 return GNUNET_strdup (hostname);
1221#endif
1222}
1223
1224
1225/**
1226 * Looking our own hostname.
1227 *
1228 * @param af AF_INET or AF_INET6; use AF_UNSPEC for "any"
1229 * @param timeout how long to try resolving
1230 * @param callback function to call with addresses
1231 * @param cls closure for @a callback
1232 * @return handle that can be used to cancel the request, NULL on error
1233 */
1234struct GNUNET_RESOLVER_RequestHandle *
1235GNUNET_RESOLVER_hostname_resolve (int af,
1236 struct GNUNET_TIME_Relative timeout,
1237 GNUNET_RESOLVER_AddressCallback callback,
1238 void *cls)
1239{
1240 char hostname[GNUNET_OS_get_hostname_max_length () + 1];
1241
1242 if (0 != gethostname (hostname, sizeof(hostname) - 1))
1243 {
1244 LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
1245 "gethostname");
1246 return NULL;
1247 }
1248 LOG (GNUNET_ERROR_TYPE_DEBUG,
1249 "Resolving our hostname `%s'\n",
1250 hostname);
1251 return GNUNET_RESOLVER_ip_get (hostname,
1252 af,
1253 timeout,
1254 callback,
1255 cls);
1256}
1257
1258
1259/**
1260 * Cancel a request that is still pending with the resolver.
1261 * Note that a client MUST NOT cancel a request that has
1262 * been completed (i.e, the callback has been called to
1263 * signal timeout or the final result).
1264 *
1265 * @param rh handle of request to cancel
1266 */
1267void
1268GNUNET_RESOLVER_request_cancel (struct GNUNET_RESOLVER_RequestHandle *rh)
1269{
1270 if (GNUNET_NO == rh->direction)
1271 LOG (GNUNET_ERROR_TYPE_DEBUG,
1272 "Asked to cancel request to resolve hostname `%s'.\n",
1273 (const char *) &rh[1]);
1274 if (NULL != rh->task)
1275 {
1276 GNUNET_SCHEDULER_cancel (rh->task);
1277 rh->task = NULL;
1278 }
1279 if (GNUNET_NO == rh->was_transmitted)
1280 {
1281 if (GNUNET_YES == rh->was_queued)
1282 GNUNET_CONTAINER_DLL_remove (req_head,
1283 req_tail,
1284 rh);
1285 GNUNET_free (rh);
1286 check_disconnect ();
1287 return;
1288 }
1289 GNUNET_assert (GNUNET_YES == rh->was_transmitted);
1290 rh->was_transmitted = GNUNET_SYSERR; /* mark as cancelled */
1291 check_disconnect ();
1292}
1293
1294
1295/* end of resolver_api.c */