diff options
Diffstat (limited to 'src/lib/util/resolver_api.c')
-rw-r--r-- | src/lib/util/resolver_api.c | 1295 |
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 | */ | ||
48 | static const char *loopback[] = { | ||
49 | "localhost", | ||
50 | "ip6-localnet", | ||
51 | NULL | ||
52 | }; | ||
53 | |||
54 | |||
55 | /** | ||
56 | * Configuration. | ||
57 | */ | ||
58 | static 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 | */ | ||
64 | static struct GNUNET_MQ_Handle *mq; | ||
65 | |||
66 | /** | ||
67 | * Head of DLL of requests. | ||
68 | */ | ||
69 | static struct GNUNET_RESOLVER_RequestHandle *req_head; | ||
70 | |||
71 | /** | ||
72 | * Tail of DLL of requests. | ||
73 | */ | ||
74 | static struct GNUNET_RESOLVER_RequestHandle *req_tail; | ||
75 | |||
76 | /** | ||
77 | * ID of the last request we sent to the service | ||
78 | */ | ||
79 | static uint32_t last_request_id; | ||
80 | |||
81 | /** | ||
82 | * How long should we wait to reconnect? | ||
83 | */ | ||
84 | static struct GNUNET_TIME_Relative backoff; | ||
85 | |||
86 | /** | ||
87 | * Task for reconnecting. | ||
88 | */ | ||
89 | static 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 | */ | ||
95 | static 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 | */ | ||
103 | struct 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 | */ | ||
190 | static int | ||
191 | check_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 | */ | ||
257 | void | ||
258 | GNUNET_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 | */ | ||
269 | void | ||
270 | GNUNET_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 | */ | ||
305 | static void | ||
306 | shutdown_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 | */ | ||
318 | static void | ||
319 | check_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 | */ | ||
347 | static char * | ||
348 | no_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 | */ | ||
397 | static void | ||
398 | reconnect (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 | */ | ||
409 | static void | ||
410 | mq_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 | */ | ||
426 | static void | ||
427 | process_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 | */ | ||
475 | static int | ||
476 | check_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 | */ | ||
495 | static void | ||
496 | handle_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 | */ | ||
651 | static void | ||
652 | numeric_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 | */ | ||
727 | static void | ||
728 | loopback_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 | */ | ||
791 | static void | ||
792 | reconnect_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 | */ | ||
827 | static void | ||
828 | reconnect () | ||
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 | */ | ||
879 | static void | ||
880 | handle_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 | */ | ||
939 | struct GNUNET_RESOLVER_RequestHandle * | ||
940 | GNUNET_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 | */ | ||
1029 | static void | ||
1030 | numeric_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 | */ | ||
1071 | struct GNUNET_RESOLVER_RequestHandle * | ||
1072 | GNUNET_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 | */ | ||
1148 | char * | ||
1149 | GNUNET_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 | */ | ||
1234 | struct GNUNET_RESOLVER_RequestHandle * | ||
1235 | GNUNET_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 | */ | ||
1267 | void | ||
1268 | GNUNET_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 */ | ||