Line data Source code
1 : /*
2 : SSSD
3 :
4 : Async resolver
5 :
6 : Authors:
7 : Martin Nagy <mnagy@redhat.com>
8 : Jakub Hrozek <jhrozek@redhat.com>
9 :
10 : Copyright (C) Red Hat, Inc 2009
11 :
12 : This program is free software; you can redistribute it and/or modify
13 : it under the terms of the GNU General Public License as published by
14 : the Free Software Foundation; either version 3 of the License, or
15 : (at your option) any later version.
16 :
17 : This program is distributed in the hope that it will be useful,
18 : but WITHOUT ANY WARRANTY; without even the implied warranty of
19 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 : GNU General Public License for more details.
21 :
22 : You should have received a copy of the GNU General Public License
23 : along with this program. If not, see <http://www.gnu.org/licenses/>.
24 : */
25 :
26 : #include <sys/select.h>
27 : #include <arpa/inet.h>
28 : #include <arpa/nameser.h>
29 :
30 : #include <ares.h>
31 : #include <talloc.h>
32 : #include <tevent.h>
33 :
34 : #include <errno.h>
35 : #include <netdb.h>
36 : #include <stddef.h>
37 : #include <string.h>
38 : #include <unistd.h>
39 :
40 : #include "config.h"
41 : #include "resolv/async_resolv.h"
42 : #include "util/dlinklist.h"
43 : #include "util/util.h"
44 :
45 : #define DNS__16BIT(p) (((p)[0] << 8) | (p)[1])
46 :
47 : /*
48 : * Macro DNS__32BIT reads a network long (32 bit) given in network
49 : * byte order, and returns its value as an unsigned int. Copied
50 : * from c-ares source code.
51 : */
52 : #define DNS__32BIT(p) ((unsigned int) \
53 : (((unsigned int)((unsigned char)(p)[0]) << 24U) | \
54 : ((unsigned int)((unsigned char)(p)[1]) << 16U) | \
55 : ((unsigned int)((unsigned char)(p)[2]) << 8U) | \
56 : ((unsigned int)((unsigned char)(p)[3]))))
57 :
58 : #define DNS_HEADER_ANCOUNT(h) DNS__16BIT((h) + 6)
59 : #define DNS_RR_LEN(r) DNS__16BIT((r) + 8)
60 : #define DNS_RR_TTL(r) DNS__32BIT((r) + 4)
61 :
62 : #define RESOLV_TIMEOUTMS 2000
63 :
64 : enum host_database default_host_dbs[] = { DB_FILES, DB_DNS, DB_SENTINEL };
65 :
66 : struct fd_watch {
67 : struct fd_watch *prev;
68 : struct fd_watch *next;
69 :
70 : int fd;
71 : struct resolv_ctx *ctx;
72 : struct tevent_fd *fde;
73 : };
74 :
75 : struct resolv_ctx {
76 : struct tevent_context *ev_ctx;
77 : ares_channel channel;
78 :
79 : /* List of file descriptors that are watched by tevent. */
80 : struct fd_watch *fds;
81 :
82 : /* Time in milliseconds before canceling a DNS request */
83 : int timeout;
84 :
85 : /* The timeout watcher periodically calls ares_process_fd() to check
86 : * if our pending requests didn't timeout. */
87 : int pending_requests;
88 : struct tevent_timer *timeout_watcher;
89 : };
90 :
91 : struct request_watch {
92 : struct tevent_req *req;
93 : struct resolv_request *rr;
94 : };
95 :
96 : struct resolv_request {
97 : struct resolv_ctx *ctx;
98 : struct request_watch *rwatch;
99 : struct tevent_timer *request_timeout;
100 : };
101 :
102 : static int
103 2 : return_code(int ares_code)
104 : {
105 2 : switch (ares_code) {
106 : case ARES_SUCCESS:
107 2 : return EOK;
108 : case ARES_ENOMEM:
109 0 : return ENOMEM;
110 : case ARES_EFILE:
111 : default:
112 0 : return EIO;
113 : }
114 : }
115 :
116 : const char *
117 0 : resolv_strerror(int ares_code)
118 : {
119 0 : return ares_strerror(ares_code);
120 : }
121 :
122 : static int
123 2 : fd_watch_destructor(struct fd_watch *f)
124 : {
125 2 : DLIST_REMOVE(f->ctx->fds, f);
126 2 : f->fd = -1;
127 :
128 2 : return 0;
129 : }
130 :
131 : static void
132 2 : fd_input_available(struct tevent_context *ev, struct tevent_fd *fde,
133 : uint16_t flags, void *data)
134 : {
135 2 : struct fd_watch *watch = talloc_get_type(data, struct fd_watch);
136 :
137 2 : if (watch->ctx->channel == NULL) {
138 0 : DEBUG(SSSDBG_CRIT_FAILURE,
139 : "Invalid ares channel - this is likely a bug\n");
140 2 : return;
141 : }
142 :
143 2 : if (flags & TEVENT_FD_READ) {
144 2 : ares_process_fd(watch->ctx->channel, watch->fd, ARES_SOCKET_BAD);
145 : }
146 2 : if (flags & TEVENT_FD_WRITE) {
147 0 : ares_process_fd(watch->ctx->channel, ARES_SOCKET_BAD, watch->fd);
148 : }
149 : }
150 :
151 : static void
152 : check_fd_timeouts(struct tevent_context *ev, struct tevent_timer *te,
153 : struct timeval current_time, void *private_data);
154 :
155 : static void
156 3 : add_timeout_timer(struct tevent_context *ev, struct resolv_ctx *ctx)
157 : {
158 3 : struct timeval tv = { 0, 0 };
159 : struct timeval *tvp;
160 :
161 3 : if (ctx->timeout_watcher) {
162 3 : return;
163 : }
164 :
165 3 : tvp = ares_timeout(ctx->channel, NULL, &tv);
166 :
167 3 : if (tvp == NULL) {
168 3 : tvp = &tv;
169 : }
170 :
171 : /* Enforce a minimum of 1 second. */
172 3 : if (tvp->tv_sec < 1) {
173 3 : tv = tevent_timeval_current_ofs(1, 0);
174 : } else {
175 0 : tv = tevent_timeval_current_ofs(tvp->tv_sec, tvp->tv_usec);
176 : }
177 :
178 3 : ctx->timeout_watcher = tevent_add_timer(ev, ctx, tv, check_fd_timeouts,
179 : ctx);
180 3 : if (ctx->timeout_watcher == NULL) {
181 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory\n");
182 : }
183 : }
184 :
185 : static void
186 0 : check_fd_timeouts(struct tevent_context *ev, struct tevent_timer *te,
187 : struct timeval current_time, void *private_data)
188 : {
189 0 : struct resolv_ctx *ctx = talloc_get_type(private_data, struct resolv_ctx);
190 :
191 0 : DEBUG(SSSDBG_TRACE_ALL, "Checking for DNS timeouts\n");
192 :
193 : /* NULLify the timeout_watcher so we don't
194 : * free it in the _done() function if it
195 : * gets called. Now that we're already in
196 : * the handler, tevent will take care of
197 : * freeing it when it returns.
198 : */
199 0 : ctx->timeout_watcher = NULL;
200 :
201 0 : ares_process_fd(ctx->channel, ARES_SOCKET_BAD, ARES_SOCKET_BAD);
202 :
203 0 : if (ctx->pending_requests > 0) {
204 0 : add_timeout_timer(ev, ctx);
205 : }
206 0 : }
207 :
208 : static void
209 0 : resolv_request_timeout(struct tevent_context *ev,
210 : struct tevent_timer *te,
211 : struct timeval tv, void *pvt)
212 : {
213 : struct resolv_request *rreq;
214 :
215 0 : DEBUG(SSSDBG_MINOR_FAILURE, "The resolve request timed out\n");
216 :
217 0 : rreq = talloc_get_type(pvt, struct resolv_request);
218 0 : if (rreq->rwatch == NULL) {
219 0 : DEBUG(SSSDBG_CRIT_FAILURE, "The request already completed\n");
220 0 : return;
221 : }
222 :
223 0 : tevent_req_error(rreq->rwatch->req, ETIMEDOUT);
224 0 : rreq->rwatch = NULL;
225 : }
226 :
227 : static int
228 3 : request_watch_destructor(struct request_watch *rwatch)
229 : {
230 3 : DEBUG(SSSDBG_TRACE_FUNC, "Deleting request watch\n");
231 3 : if (rwatch->rr) rwatch->rr->rwatch = NULL;
232 3 : return 0;
233 : }
234 :
235 : static struct resolv_request *
236 3 : schedule_request_timeout(struct tevent_context *ev, struct resolv_ctx *ctx,
237 : struct tevent_req *req)
238 : {
239 : struct resolv_request *rreq;
240 : struct timeval tv;
241 :
242 3 : DEBUG(SSSDBG_TRACE_INTERNAL, "Scheduling a timeout of %d seconds\n",
243 : ctx->timeout);
244 3 : tv = tevent_timeval_current_ofs(ctx->timeout, 0);
245 :
246 : /* Intentionally allocating on ctx, because the request might go away
247 : * before c-ares returns */
248 3 : rreq = talloc(ctx, struct resolv_request);
249 3 : if (!rreq) {
250 0 : talloc_zfree(req);
251 0 : return NULL;
252 : }
253 3 : rreq->ctx = ctx;
254 3 : rreq->request_timeout = tevent_add_timer(ev, rreq, tv,
255 : resolv_request_timeout,
256 : rreq);
257 3 : if (rreq->request_timeout == NULL) {
258 0 : talloc_free(rreq);
259 0 : return NULL;
260 : }
261 :
262 : /* The watch will go away when the request finishes */
263 3 : rreq->rwatch = talloc(req, struct request_watch);
264 3 : if (!rreq->rwatch) {
265 0 : talloc_zfree(req);
266 0 : return NULL;
267 : }
268 :
269 3 : rreq->rwatch->req = req;
270 3 : rreq->rwatch->rr = rreq;
271 3 : talloc_set_destructor(rreq->rwatch, request_watch_destructor);
272 :
273 3 : return rreq;
274 : }
275 :
276 : static struct resolv_request *
277 3 : schedule_timeout_watcher(struct tevent_context *ev, struct resolv_ctx *ctx,
278 : struct tevent_req *req)
279 : {
280 : struct resolv_request *rreq;
281 :
282 3 : rreq = schedule_request_timeout(ev, ctx, req);
283 3 : if (!rreq) return NULL;
284 :
285 3 : ctx->pending_requests++;
286 :
287 3 : DEBUG(SSSDBG_TRACE_INTERNAL, "Scheduling DNS timeout watcher\n");
288 3 : add_timeout_timer(ev, ctx);
289 3 : return rreq;
290 : }
291 :
292 : static void
293 3 : unschedule_timeout_watcher(struct resolv_ctx *ctx, struct resolv_request *rreq)
294 : {
295 : /* Unlink the watch if the request is still active */
296 3 : if (rreq->rwatch) {
297 3 : rreq->rwatch->rr = NULL;
298 : }
299 3 : talloc_free(rreq); /* Cancels the tevent timeout as well */
300 :
301 3 : if (ctx->pending_requests <= 0) {
302 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Pending DNS requests mismatch\n");
303 3 : return;
304 : }
305 :
306 3 : ctx->pending_requests--;
307 3 : if (ctx->pending_requests == 0) {
308 3 : DEBUG(SSSDBG_TRACE_ALL, "Unscheduling DNS timeout watcher\n");
309 3 : talloc_zfree(ctx->timeout_watcher);
310 : }
311 : }
312 :
313 : static void fd_event_add(struct resolv_ctx *ctx, int s, int flags);
314 : static void fd_event_close(struct resolv_ctx *ctx, int s);
315 :
316 : /*
317 : * When ares is ready to read or write to a file descriptor, it will
318 : * call this callback. If both read and write are 0, it means that ares
319 : * will soon close the socket. We are mainly using this function to register
320 : * new file descriptors with tevent.
321 : */
322 : static void
323 4 : fd_event(void *data, int s, int fd_read, int fd_write)
324 : {
325 4 : struct resolv_ctx *ctx = talloc_get_type(data, struct resolv_ctx);
326 : struct fd_watch *watch;
327 : int flags;
328 :
329 : /* The socket is about to get closed. */
330 4 : if (fd_read == 0 && fd_write == 0) {
331 2 : fd_event_close(ctx, s);
332 2 : return;
333 : }
334 :
335 2 : flags = fd_read ? TEVENT_FD_READ : 0;
336 2 : flags |= fd_write ? TEVENT_FD_WRITE : 0;
337 :
338 : /* Are we already watching this file descriptor? */
339 2 : watch = ctx->fds;
340 4 : while (watch) {
341 0 : if (watch->fd == s) {
342 0 : tevent_fd_set_flags(watch->fde, flags);
343 0 : return;
344 : }
345 0 : watch = watch->next;
346 : }
347 :
348 2 : fd_event_add(ctx, s, flags);
349 : }
350 :
351 : static void
352 2 : fd_event_add(struct resolv_ctx *ctx, int s, int flags)
353 : {
354 : struct fd_watch *watch;
355 :
356 : /* The file descriptor is new, register it with tevent. */
357 2 : watch = talloc(ctx, struct fd_watch);
358 2 : if (watch == NULL) {
359 0 : DEBUG(SSSDBG_CRIT_FAILURE,
360 : "Out of memory allocating fd_watch structure\n");
361 0 : return;
362 : }
363 2 : talloc_set_destructor(watch, fd_watch_destructor);
364 :
365 2 : watch->fd = s;
366 2 : watch->ctx = ctx;
367 :
368 2 : watch->fde = tevent_add_fd(ctx->ev_ctx, watch, s, flags,
369 : fd_input_available, watch);
370 2 : if (watch->fde == NULL) {
371 0 : DEBUG(SSSDBG_CRIT_FAILURE, "tevent_add_fd() failed\n");
372 0 : talloc_free(watch);
373 0 : return;
374 : }
375 2 : DLIST_ADD(ctx->fds, watch);
376 : }
377 :
378 : static void
379 2 : fd_event_close(struct resolv_ctx *ctx, int s)
380 : {
381 : struct fd_watch *watch;
382 :
383 : /* Remove the socket from list */
384 2 : watch = ctx->fds;
385 4 : while (watch) {
386 2 : if (watch->fd == s) {
387 2 : talloc_free(watch);
388 4 : return;
389 : }
390 0 : watch = watch->next;
391 : }
392 : }
393 :
394 : static int
395 8 : resolv_ctx_destructor(struct resolv_ctx *ctx)
396 : {
397 : ares_channel channel;
398 :
399 8 : if (ctx->channel == NULL) {
400 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Ares channel already destroyed?\n");
401 0 : return -1;
402 : }
403 :
404 : /* Set ctx->channel to NULL first, so that callbacks that get
405 : * ARES_EDESTRUCTION won't retry. */
406 8 : channel = ctx->channel;
407 8 : ctx->channel = NULL;
408 8 : ares_destroy(channel);
409 :
410 8 : return 0;
411 : }
412 :
413 : static int
414 15 : recreate_ares_channel(struct resolv_ctx *ctx)
415 : {
416 : int ret;
417 : ares_channel new_channel;
418 : ares_channel old_channel;
419 : struct ares_options options;
420 :
421 15 : DEBUG(SSSDBG_CONF_SETTINGS, "Initializing new c-ares channel\n");
422 : /* FIXME: the options would contain
423 : * the nameservers to contact, the domains
424 : * to search... => get from confdb
425 : */
426 15 : options.sock_state_cb = fd_event;
427 15 : options.sock_state_cb_data = ctx;
428 15 : options.timeout = RESOLV_TIMEOUTMS;
429 : /* Only affects ares_gethostbyname */
430 15 : options.lookups = discard_const("f");
431 15 : options.tries = 1;
432 15 : ret = ares_init_options(&new_channel, &options,
433 : ARES_OPT_SOCK_STATE_CB |
434 : ARES_OPT_TIMEOUTMS |
435 : ARES_OPT_LOOKUPS |
436 : ARES_OPT_TRIES);
437 15 : if (ret != ARES_SUCCESS) {
438 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Failed to initialize ares channel: %s\n",
439 : resolv_strerror(ret));
440 0 : return return_code(ret);
441 : }
442 :
443 15 : old_channel = ctx->channel;
444 15 : ctx->channel = new_channel;
445 15 : if (old_channel != NULL) {
446 0 : DEBUG(SSSDBG_CONF_SETTINGS, "Destroying the old c-ares channel\n");
447 0 : ares_destroy(old_channel);
448 : }
449 :
450 15 : return EOK;
451 : }
452 :
453 : int
454 15 : resolv_init(TALLOC_CTX *mem_ctx, struct tevent_context *ev_ctx,
455 : int timeout, struct resolv_ctx **ctxp)
456 : {
457 : int ret;
458 : struct resolv_ctx *ctx;
459 :
460 15 : if (timeout < 1) {
461 0 : DEBUG(SSSDBG_MINOR_FAILURE,
462 : "The timeout is too short, DNS operations are going to fail. "
463 : "This is a bug outside unit tests\n");
464 : }
465 :
466 15 : ctx = talloc_zero(mem_ctx, struct resolv_ctx);
467 15 : if (ctx == NULL)
468 0 : return ENOMEM;
469 :
470 15 : ctx->ev_ctx = ev_ctx;
471 15 : ctx->timeout = timeout;
472 :
473 15 : ret = recreate_ares_channel(ctx);
474 15 : if (ret != EOK) {
475 0 : goto done;
476 : }
477 :
478 15 : talloc_set_destructor(ctx, resolv_ctx_destructor);
479 :
480 15 : *ctxp = ctx;
481 15 : return EOK;
482 :
483 : done:
484 0 : talloc_free(ctx);
485 0 : return ret;
486 : }
487 :
488 : void
489 0 : resolv_reread_configuration(struct resolv_ctx *ctx)
490 : {
491 0 : recreate_ares_channel(ctx);
492 0 : }
493 :
494 : static errno_t
495 4 : resolv_copy_in_addr(TALLOC_CTX *mem_ctx, struct resolv_addr *ret,
496 : struct ares_addrttl *attl)
497 : {
498 4 : ret->ipaddr = talloc_array(mem_ctx, uint8_t, sizeof(struct in_addr));
499 4 : if (!ret->ipaddr) return ENOMEM;
500 :
501 4 : memcpy(ret->ipaddr, &attl->ipaddr, sizeof(struct in_addr));
502 4 : ret->ttl = attl->ttl;
503 :
504 4 : return EOK;
505 : }
506 :
507 : static errno_t
508 0 : resolv_copy_in6_addr(TALLOC_CTX *mem_ctx, struct resolv_addr *ret,
509 : struct ares_addr6ttl *a6ttl)
510 : {
511 0 : ret->ipaddr = talloc_array(mem_ctx, uint8_t, sizeof(struct in6_addr));
512 0 : if (!ret->ipaddr) return ENOMEM;
513 :
514 0 : memcpy(ret->ipaddr, &a6ttl->ip6addr, sizeof(struct in6_addr));
515 0 : ret->ttl = a6ttl->ttl;
516 :
517 0 : return EOK;
518 : }
519 :
520 : static struct resolv_hostent *
521 5 : resolv_copy_hostent_common(TALLOC_CTX *mem_ctx, struct hostent *src)
522 : {
523 : struct resolv_hostent *ret;
524 : int len;
525 : int i;
526 :
527 5 : ret = talloc_zero(mem_ctx, struct resolv_hostent);
528 5 : if (ret == NULL) {
529 0 : return NULL;
530 : }
531 :
532 5 : if (src->h_name != NULL) {
533 5 : ret->name = talloc_strdup(ret, src->h_name);
534 5 : if (ret->name == NULL) {
535 0 : goto fail;
536 : }
537 : }
538 5 : if (src->h_aliases != NULL) {
539 5 : for (len = 0; src->h_aliases[len] != NULL; len++);
540 :
541 5 : ret->aliases = talloc_array(ret, char *, len + 1);
542 5 : if (ret->aliases == NULL) {
543 0 : goto fail;
544 : }
545 :
546 12 : for (i = 0; i < len; i++) {
547 7 : ret->aliases[i] = talloc_strdup(ret->aliases, src->h_aliases[i]);
548 7 : if (ret->aliases[i] == NULL) {
549 0 : goto fail;
550 : }
551 : }
552 5 : ret->aliases[len] = NULL;
553 : }
554 :
555 5 : ret->family = src->h_addrtype;
556 5 : return ret;
557 :
558 : fail:
559 0 : talloc_free(ret);
560 0 : return NULL;
561 : }
562 :
563 : struct resolv_hostent *
564 2 : resolv_copy_hostent(TALLOC_CTX *mem_ctx, struct hostent *src)
565 : {
566 : struct resolv_hostent *ret;
567 : int len;
568 : int i;
569 :
570 2 : ret = resolv_copy_hostent_common(mem_ctx, src);
571 2 : if (ret == NULL) {
572 0 : return NULL;
573 : }
574 :
575 2 : if (src->h_addr_list != NULL) {
576 2 : for (len = 0; src->h_addr_list[len] != NULL; len++);
577 :
578 2 : ret->addr_list = talloc_array(ret, struct resolv_addr *, len + 1);
579 2 : if (ret->addr_list == NULL) {
580 0 : goto fail;
581 : }
582 :
583 5 : for (i = 0; i < len; i++) {
584 3 : ret->addr_list[i] = talloc_zero(ret->addr_list,
585 : struct resolv_addr);
586 3 : if (ret->addr_list[i] == NULL) {
587 0 : goto fail;
588 : }
589 :
590 3 : ret->addr_list[i]->ipaddr = talloc_memdup(ret->addr_list[i],
591 : src->h_addr_list[i],
592 : src->h_length);
593 3 : if (ret->addr_list[i]->ipaddr == NULL) {
594 0 : goto fail;
595 : }
596 3 : ret->addr_list[i]->ttl = RESOLV_DEFAULT_TTL;
597 : }
598 2 : ret->addr_list[len] = NULL;
599 : }
600 2 : return ret;
601 :
602 : fail:
603 0 : talloc_free(ret);
604 0 : return NULL;
605 : }
606 :
607 : struct resolv_hostent *
608 3 : resolv_copy_hostent_ares(TALLOC_CTX *mem_ctx, struct hostent *src,
609 : int family, void *ares_ttl_data,
610 : int num_ares_ttl_data)
611 : {
612 : struct resolv_hostent *ret;
613 : errno_t cret;
614 : int i;
615 :
616 3 : ret = resolv_copy_hostent_common(mem_ctx, src);
617 3 : if (ret == NULL) {
618 0 : return NULL;
619 : }
620 :
621 3 : if (num_ares_ttl_data > 0) {
622 3 : ret->addr_list = talloc_array(ret, struct resolv_addr *,
623 : num_ares_ttl_data + 1);
624 3 : if (ret->addr_list == NULL) {
625 0 : goto fail;
626 : }
627 :
628 7 : for (i = 0; i < num_ares_ttl_data; i++) {
629 4 : ret->addr_list[i] = talloc_zero(ret->addr_list,
630 : struct resolv_addr);
631 4 : if (ret->addr_list[i] == NULL) {
632 0 : goto fail;
633 : }
634 :
635 4 : switch (family) {
636 : case AF_INET:
637 8 : cret = resolv_copy_in_addr(ret->addr_list, ret->addr_list[i],
638 4 : &((struct ares_addrttl *) ares_ttl_data)[i]);
639 4 : break;
640 : case AF_INET6:
641 0 : cret = resolv_copy_in6_addr(ret->addr_list, ret->addr_list[i],
642 0 : &((struct ares_addr6ttl *) ares_ttl_data)[i]);
643 0 : break;
644 : default:
645 0 : DEBUG(SSSDBG_CRIT_FAILURE,
646 : "Unknown address family %d\n", family);
647 0 : goto fail;
648 : }
649 :
650 4 : if (cret != EOK) {
651 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Could not copy address\n");
652 0 : goto fail;
653 : }
654 : }
655 3 : ret->addr_list[num_ares_ttl_data] = NULL;
656 : }
657 :
658 3 : ret->family = family;
659 3 : return ret;
660 :
661 : fail:
662 0 : talloc_free(ret);
663 0 : return NULL;
664 : }
665 :
666 : /* =================== Resolve host name in files =========================*/
667 : struct gethostbyname_files_state {
668 : struct resolv_ctx *resolv_ctx;
669 :
670 : /* Part of the query. */
671 : const char *name;
672 : int family;
673 :
674 : /* query result */
675 : struct resolv_hostent *rhostent;
676 :
677 : /* returned by ares. */
678 : int status;
679 : };
680 :
681 : /* Fake up an async interface even though files would
682 : * always be blocking */
683 : static struct tevent_req *
684 5 : resolv_gethostbyname_files_send(TALLOC_CTX *mem_ctx,
685 : struct tevent_context *ev,
686 : struct resolv_ctx *ctx,
687 : const char *name,
688 : int family)
689 : {
690 : struct tevent_req *req;
691 : struct gethostbyname_files_state *state;
692 5 : struct hostent *hostent = NULL;
693 :
694 5 : req = tevent_req_create(mem_ctx, &state,
695 : struct gethostbyname_files_state);
696 5 : if (req == NULL) {
697 0 : return NULL;
698 : }
699 :
700 5 : state->resolv_ctx = ctx;
701 5 : state->name = name;
702 5 : state->rhostent = NULL;
703 5 : state->family = family;
704 :
705 5 : DEBUG(SSSDBG_CONF_SETTINGS,
706 : "Trying to resolve %s record of '%s' in files\n",
707 : state->family == AF_INET ? "A" : "AAAA", state->name);
708 :
709 15 : state->status = ares_gethostbyname_file(state->resolv_ctx->channel,
710 10 : state->name, state->family,
711 : &hostent);
712 :
713 5 : if (state->status == ARES_SUCCESS) {
714 1 : state->rhostent = resolv_copy_hostent(state, hostent);
715 1 : if (state->rhostent == NULL) {
716 0 : tevent_req_error(req, ENOMEM);
717 0 : goto done;
718 : }
719 4 : } else if (state->status == ARES_ENOTFOUND ||
720 0 : state->status == ARES_ENODATA) {
721 : /* Just say we didn't find anything and let the caller decide
722 : * about retrying */
723 4 : tevent_req_error(req, ENOENT);
724 4 : goto done;
725 : } else {
726 0 : tevent_req_error(req, return_code(state->status));
727 0 : goto done;
728 : }
729 :
730 1 : tevent_req_done(req);
731 : done:
732 5 : if (hostent) ares_free_hostent(hostent);
733 5 : tevent_req_post(req, ev);
734 5 : return req;
735 : }
736 :
737 : static errno_t
738 5 : resolv_gethostbyname_files_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
739 : int *status, struct resolv_hostent **rhostent)
740 : {
741 5 : struct gethostbyname_files_state *state = tevent_req_data(req,
742 : struct gethostbyname_files_state);
743 :
744 : /* Fill in even in case of error as status contains the
745 : * c-ares return code */
746 5 : if (status) {
747 5 : *status = state->status;
748 : }
749 5 : if (rhostent) {
750 5 : *rhostent = talloc_steal(mem_ctx, state->rhostent);
751 : }
752 :
753 9 : TEVENT_REQ_RETURN_ON_ERROR(req);
754 :
755 1 : return EOK;
756 : }
757 :
758 : /* ==================== Resolve host name in DNS =========================*/
759 : struct gethostbyname_dns_state {
760 : struct resolv_ctx *resolv_ctx;
761 : struct tevent_context *ev;
762 :
763 : /* Part of the query. */
764 : const char *name;
765 : int family;
766 :
767 : /* query result */
768 : struct resolv_hostent *rhostent;
769 :
770 : /* These are returned by ares. */
771 : int status;
772 : int timeouts;
773 : int retrying;
774 : };
775 :
776 : static void
777 : resolv_gethostbyname_dns_wakeup(struct tevent_req *subreq);
778 : static void
779 : resolv_gethostbyname_dns_query(struct tevent_req *req,
780 : struct gethostbyname_dns_state *state);
781 : static void
782 : resolv_gethostbyname_dns_query_done(void *arg, int status, int timeouts,
783 : unsigned char *abuf, int alen);
784 : static int
785 : resolv_gethostbyname_dns_parse(struct gethostbyname_dns_state *state,
786 : int status, unsigned char *abuf, int alen);
787 :
788 : static struct tevent_req *
789 2 : resolv_gethostbyname_dns_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
790 : struct resolv_ctx *ctx, const char *name,
791 : int family)
792 : {
793 : struct tevent_req *req, *subreq;
794 : struct gethostbyname_dns_state *state;
795 2 : struct timeval tv = { 0, 0 };
796 :
797 2 : if (ctx->channel == NULL) {
798 0 : DEBUG(SSSDBG_CRIT_FAILURE,
799 : "Invalid ares channel - this is likely a bug\n");
800 0 : return NULL;
801 : }
802 :
803 2 : req = tevent_req_create(mem_ctx, &state, struct gethostbyname_dns_state);
804 2 : if (req == NULL) {
805 0 : return NULL;
806 : }
807 :
808 2 : state->resolv_ctx = ctx;
809 2 : state->ev = ev;
810 2 : state->name = name;
811 2 : state->rhostent = NULL;
812 2 : state->status = 0;
813 2 : state->timeouts = 0;
814 2 : state->retrying = 0;
815 2 : state->family = family;
816 :
817 : /* We need to have a wrapper around ares async calls, because
818 : * they can in some cases call it's callback immediately.
819 : * This would not let our caller to set a callback for req. */
820 2 : subreq = tevent_wakeup_send(req, ev, tv);
821 2 : if (subreq == NULL) {
822 0 : DEBUG(SSSDBG_CRIT_FAILURE,
823 : "Failed to add critical timer to run next operation!\n");
824 0 : talloc_zfree(req);
825 0 : return NULL;
826 : }
827 2 : tevent_req_set_callback(subreq, resolv_gethostbyname_dns_wakeup, req);
828 :
829 2 : return req;
830 : }
831 :
832 : static void
833 2 : resolv_gethostbyname_dns_wakeup(struct tevent_req *subreq)
834 : {
835 2 : struct tevent_req *req = tevent_req_callback_data(subreq,
836 : struct tevent_req);
837 2 : struct gethostbyname_dns_state *state = tevent_req_data(req,
838 : struct gethostbyname_dns_state);
839 :
840 2 : if (!tevent_wakeup_recv(subreq)) {
841 0 : tevent_req_error(req, EIO);
842 0 : return;
843 : }
844 2 : talloc_zfree(subreq);
845 :
846 2 : if (state->resolv_ctx->channel == NULL) {
847 0 : DEBUG(SSSDBG_CRIT_FAILURE,
848 : "Invalid ares channel - this is likely a bug\n");
849 0 : tevent_req_error(req, EIO);
850 0 : return;
851 : }
852 :
853 2 : resolv_gethostbyname_dns_query(req, state);
854 : }
855 :
856 : static void
857 2 : resolv_gethostbyname_dns_query(struct tevent_req *req,
858 : struct gethostbyname_dns_state *state)
859 : {
860 : struct resolv_request *rreq;
861 :
862 2 : DEBUG(SSSDBG_CONF_SETTINGS, "Trying to resolve %s record of '%s' in DNS\n",
863 : state->family == AF_INET ? "A" : "AAAA", state->name);
864 :
865 2 : rreq = schedule_timeout_watcher(state->ev, state->resolv_ctx, req);
866 2 : if (!rreq) {
867 0 : tevent_req_error(req, ENOMEM);
868 2 : return;
869 : }
870 :
871 2 : ares_search(state->resolv_ctx->channel,
872 : state->name, ns_c_in,
873 2 : (state->family == AF_INET) ? ns_t_a : ns_t_aaaa,
874 : resolv_gethostbyname_dns_query_done, rreq);
875 : }
876 :
877 : static void
878 2 : resolv_gethostbyname_dns_query_done(void *arg, int status, int timeouts,
879 : unsigned char *abuf, int alen)
880 : {
881 : errno_t ret;
882 : struct gethostbyname_dns_state *state;
883 2 : struct resolv_request *rreq = talloc_get_type(arg, struct resolv_request);
884 : struct tevent_req *req;
885 :
886 :
887 2 : if (rreq->rwatch == NULL) {
888 : /* The tevent request was cancelled while the ares call was still in
889 : * progress so nobody cares about the result now. Quit. */
890 0 : unschedule_timeout_watcher(rreq->ctx, rreq);
891 0 : return;
892 : }
893 :
894 2 : req = rreq->rwatch->req;
895 2 : unschedule_timeout_watcher(rreq->ctx, rreq);
896 :
897 2 : state = tevent_req_data(req, struct gethostbyname_dns_state);
898 :
899 2 : state->status = status;
900 2 : state->timeouts = timeouts;
901 :
902 : /* If resolv.conf changed during processing of a request we might
903 : * destroy the old channel before the request has a chance to finish.
904 : * We must resend the request in this case */
905 2 : if (state->retrying == 0 && status == ARES_EDESTRUCTION
906 0 : && state->resolv_ctx->channel != NULL) {
907 0 : state->retrying = 1;
908 0 : resolv_gethostbyname_dns_query(req, state);
909 0 : return;
910 : }
911 :
912 2 : if (status == ARES_ENOTFOUND || status == ARES_ENODATA) {
913 : /* Just say we didn't find anything and let the caller decide
914 : * about retrying */
915 0 : tevent_req_error(req, ENOENT);
916 0 : return;
917 : }
918 :
919 2 : if (status != ARES_SUCCESS) {
920 : /* Any other error indicates a server error,
921 : * so don't bother trying again
922 : */
923 0 : tevent_req_error(req, return_code(status));
924 0 : return;
925 : }
926 :
927 2 : ret = resolv_gethostbyname_dns_parse(state, status, abuf, alen);
928 2 : if (ret != EOK) {
929 0 : tevent_req_error(req, ret);
930 0 : return;
931 : }
932 :
933 2 : tevent_req_done(req);
934 : }
935 :
936 : static int
937 2 : resolv_gethostbyname_dns_parse(struct gethostbyname_dns_state *state,
938 : int status, unsigned char *abuf, int alen)
939 : {
940 : struct hostent *hostent;
941 : int naddrttls;
942 : errno_t ret;
943 2 : void *addr = NULL;
944 :
945 2 : naddrttls = DNS_HEADER_ANCOUNT(abuf);
946 :
947 2 : switch (state->family) {
948 : case AF_INET:
949 2 : DEBUG(SSSDBG_TRACE_LIBS, "Parsing an A reply\n");
950 :
951 2 : addr = talloc_array(state, struct ares_addrttl, naddrttls);
952 2 : if (!addr) {
953 0 : ret = ENOMEM;
954 0 : goto fail;
955 : }
956 :
957 2 : status = ares_parse_a_reply(abuf, alen, &hostent,
958 : (struct ares_addrttl *) addr,
959 : &naddrttls);
960 2 : break;
961 : case AF_INET6:
962 0 : DEBUG(SSSDBG_TRACE_LIBS, "Parsing an AAAA reply\n");
963 :
964 0 : addr = talloc_array(state, struct ares_addr6ttl, naddrttls);
965 0 : if (!addr) {
966 0 : ret = ENOMEM;
967 0 : goto fail;
968 : }
969 :
970 0 : status = ares_parse_aaaa_reply(abuf, alen, &hostent,
971 : (struct ares_addr6ttl *) addr,
972 : &naddrttls);
973 0 : break;
974 : default:
975 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Unknown family %d\n", state->family);
976 0 : ret = EAFNOSUPPORT;
977 0 : goto fail;
978 : }
979 :
980 2 : if (hostent != NULL) {
981 2 : state->rhostent = resolv_copy_hostent_ares(state, hostent,
982 : state->family,
983 : addr, naddrttls);
984 2 : ares_free_hostent(hostent);
985 2 : if (state->rhostent == NULL) {
986 0 : ret = ENOMEM;
987 0 : goto fail;
988 : }
989 :
990 : /* The address list is NULL. This is probably a bug in
991 : * c-ares, but we need to handle it gracefully.
992 : */
993 2 : if (state->rhostent->addr_list == NULL) {
994 0 : talloc_zfree(state->rhostent);
995 0 : return ENOENT;
996 : }
997 : }
998 :
999 2 : talloc_free(addr);
1000 2 : return return_code(status);
1001 :
1002 : fail:
1003 0 : talloc_free(addr);
1004 0 : return ret;
1005 : }
1006 :
1007 : static int
1008 2 : resolv_gethostbyname_dns_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
1009 : int *status, int *timeouts,
1010 : struct resolv_hostent **rhostent)
1011 : {
1012 2 : struct gethostbyname_dns_state *state = tevent_req_data(req,
1013 : struct gethostbyname_dns_state);
1014 :
1015 : /* Fill in even in case of error as status contains the
1016 : * c-ares return code */
1017 2 : if (status) {
1018 2 : *status = state->status;
1019 : }
1020 2 : if (timeouts) {
1021 2 : *timeouts = state->timeouts;
1022 : }
1023 :
1024 2 : TEVENT_REQ_RETURN_ON_ERROR(req);
1025 :
1026 2 : if (rhostent) {
1027 2 : *rhostent = talloc_steal(mem_ctx, state->rhostent);
1028 : }
1029 :
1030 2 : return EOK;
1031 : }
1032 :
1033 : /*******************************************************************
1034 : * Get host by name. *
1035 : *******************************************************************/
1036 :
1037 : struct gethostbyname_state {
1038 : struct resolv_ctx *resolv_ctx;
1039 : struct tevent_context *ev;
1040 :
1041 : /* Part of the query. */
1042 : const char *name;
1043 : int family;
1044 :
1045 : /* In which order to use IPv4, or v6 */
1046 : enum restrict_family family_order;
1047 :
1048 : /* Known hosts databases and index to the current one */
1049 : enum host_database *db;
1050 : int dbi;
1051 :
1052 : /* These are returned by ares. The hostent struct will be freed
1053 : * when the user callback returns. */
1054 : struct resolv_hostent *rhostent;
1055 : int status;
1056 : int timeouts;
1057 : int retrying;
1058 : };
1059 :
1060 : static errno_t
1061 : resolv_gethostbyname_address(TALLOC_CTX *mem_ctx, const char *address,
1062 : struct resolv_hostent **_rhostent);
1063 : static inline int
1064 : resolv_gethostbyname_family_init(enum restrict_family family_order);
1065 : static bool
1066 : resolv_is_address(const char *name);
1067 : static errno_t
1068 : resolv_gethostbyname_step(struct tevent_req *req);
1069 :
1070 : struct tevent_req *
1071 5 : resolv_gethostbyname_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
1072 : struct resolv_ctx *ctx, const char *name,
1073 : enum restrict_family family_order,
1074 : enum host_database *db)
1075 : {
1076 : struct tevent_req *req;
1077 : struct gethostbyname_state *state;
1078 : errno_t ret;
1079 :
1080 5 : if (ctx->channel == NULL) {
1081 0 : DEBUG(SSSDBG_CRIT_FAILURE,
1082 : "Invalid ares channel - this is likely a bug\n");
1083 0 : return NULL;
1084 : }
1085 :
1086 5 : req = tevent_req_create(mem_ctx, &state, struct gethostbyname_state);
1087 5 : if (req == NULL) {
1088 0 : return NULL;
1089 : }
1090 :
1091 5 : state->resolv_ctx = ctx;
1092 5 : state->ev = ev;
1093 5 : state->name = talloc_strdup(state, name);
1094 5 : if (state->name == NULL) {
1095 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup() failed\n");
1096 0 : goto fail;
1097 : }
1098 :
1099 5 : state->rhostent = NULL;
1100 5 : state->status = 0;
1101 5 : state->timeouts = 0;
1102 5 : state->retrying = 0;
1103 5 : state->family_order = family_order;
1104 5 : state->family = resolv_gethostbyname_family_init(state->family_order);
1105 5 : state->db = db;
1106 5 : state->dbi = 0;
1107 :
1108 : /* Do not attempt to resolve IP addresses */
1109 5 : if (resolv_is_address(state->name)) {
1110 2 : ret = resolv_gethostbyname_address(state, state->name,
1111 2 : &state->rhostent);
1112 2 : if (ret != EOK) {
1113 0 : DEBUG(SSSDBG_CRIT_FAILURE,
1114 : "Canot create a fake hostent structure\n");
1115 0 : goto fail;
1116 : }
1117 :
1118 2 : tevent_req_done(req);
1119 2 : tevent_req_post(req, ev);
1120 2 : return req;
1121 : }
1122 :
1123 3 : ret = resolv_gethostbyname_step(req);
1124 3 : if (ret != EOK) {
1125 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Cannot start the resolving\n");
1126 0 : goto fail;
1127 : }
1128 :
1129 3 : return req;
1130 :
1131 : fail:
1132 0 : talloc_zfree(req);
1133 0 : return NULL;
1134 : }
1135 :
1136 : static bool
1137 5 : resolv_is_address(const char *name)
1138 : {
1139 : struct addrinfo hints;
1140 5 : struct addrinfo *res = NULL;
1141 : int ret;
1142 :
1143 5 : memset((void *) &hints, 0, sizeof(struct addrinfo));
1144 5 : hints.ai_family = AF_UNSPEC;
1145 5 : hints.ai_flags = AI_NUMERICHOST; /* No network lookups */
1146 :
1147 5 : ret = getaddrinfo(name, NULL, &hints, &res);
1148 5 : if (ret != 0) {
1149 3 : if (ret == -2) {
1150 3 : DEBUG(SSSDBG_TRACE_ALL,
1151 : "[%s] does not look like an IP address\n", name);
1152 : } else {
1153 0 : DEBUG(SSSDBG_OP_FAILURE, "getaddrinfo failed [%d]: %s\n",
1154 : ret, gai_strerror(ret));
1155 : }
1156 : } else { /* ret == 0 */
1157 2 : freeaddrinfo(res);
1158 : }
1159 :
1160 5 : return ret == 0;
1161 : }
1162 :
1163 : static errno_t
1164 2 : resolv_gethostbyname_address(TALLOC_CTX *mem_ctx, const char *address,
1165 : struct resolv_hostent **_rhostent)
1166 : {
1167 : struct resolv_hostent *rhostent;
1168 : TALLOC_CTX *tmp_ctx;
1169 : errno_t ret;
1170 : int family;
1171 :
1172 2 : tmp_ctx = talloc_new(NULL);
1173 2 : if (!tmp_ctx) return ENOMEM;
1174 :
1175 2 : rhostent = talloc_zero(tmp_ctx, struct resolv_hostent);
1176 2 : if (!rhostent) {
1177 0 : ret = ENOMEM;
1178 0 : goto done;
1179 : }
1180 :
1181 2 : rhostent->name = talloc_strdup(rhostent, address);
1182 2 : rhostent->addr_list = talloc_array(rhostent, struct resolv_addr *, 2);
1183 :
1184 4 : if (!rhostent->name ||
1185 2 : !rhostent->addr_list) {
1186 0 : ret = ENOMEM;
1187 0 : goto done;
1188 : }
1189 :
1190 2 : rhostent->addr_list[0] = talloc_zero(rhostent->addr_list,
1191 : struct resolv_addr);
1192 2 : if (!rhostent->addr_list[0]) {
1193 0 : ret = ENOMEM;
1194 0 : goto done;
1195 : }
1196 2 : rhostent->addr_list[0]->ipaddr = talloc_array(rhostent->addr_list[0],
1197 : uint8_t,
1198 : sizeof(struct in6_addr));
1199 2 : if (!rhostent->addr_list[0]->ipaddr) {
1200 0 : ret = ENOMEM;
1201 0 : goto done;
1202 : }
1203 :
1204 2 : family = AF_INET;
1205 2 : ret = inet_pton(family, address,
1206 2 : rhostent->addr_list[0]->ipaddr);
1207 2 : if (ret != 1) {
1208 0 : family = AF_INET6;
1209 0 : ret = inet_pton(family, address,
1210 0 : rhostent->addr_list[0]->ipaddr);
1211 0 : if (ret != 1) {
1212 0 : DEBUG(SSSDBG_CRIT_FAILURE,
1213 : "Could not parse address as neither v4 nor v6\n");
1214 0 : ret = EINVAL;
1215 0 : goto done;
1216 : }
1217 : }
1218 :
1219 2 : rhostent->addr_list[0]->ttl = RESOLV_DEFAULT_TTL;
1220 2 : rhostent->addr_list[1] = NULL;
1221 2 : rhostent->family = family;
1222 2 : rhostent->aliases = NULL;
1223 :
1224 2 : *_rhostent = talloc_move(mem_ctx, &rhostent);
1225 2 : ret = EOK;
1226 : done:
1227 2 : talloc_free(tmp_ctx);
1228 2 : return ret;
1229 : }
1230 :
1231 : static inline int
1232 7 : resolv_gethostbyname_family_init(enum restrict_family family_order)
1233 : {
1234 7 : switch(family_order) {
1235 : case IPV4_ONLY:
1236 : case IPV4_FIRST:
1237 7 : return AF_INET;
1238 : case IPV6_ONLY:
1239 : case IPV6_FIRST:
1240 0 : return AF_INET6;
1241 : }
1242 :
1243 0 : DEBUG(SSSDBG_CRIT_FAILURE,
1244 : "Unknown address family order %d\n", family_order);
1245 0 : return -1;
1246 : }
1247 :
1248 : static int
1249 4 : resolv_gethostbyname_next(struct gethostbyname_state *state)
1250 : {
1251 8 : if (state->family_order == IPV4_FIRST &&
1252 4 : state->family == AF_INET) {
1253 2 : state->family = AF_INET6;
1254 2 : return EOK;
1255 2 : } else if (state->family_order == IPV6_FIRST &&
1256 0 : state->family == AF_INET6) {
1257 0 : state->family = AF_INET;
1258 0 : return EOK;
1259 : } else {
1260 : /* No more address families for this DB, check if
1261 : * there is another DB to try */
1262 2 : DEBUG(SSSDBG_FUNC_DATA, "No more address families to retry\n");
1263 2 : state->dbi++;
1264 2 : if (state->db[state->dbi] != DB_SENTINEL) {
1265 2 : state->family = resolv_gethostbyname_family_init(
1266 : state->family_order);
1267 2 : return EOK;
1268 : }
1269 : }
1270 :
1271 0 : DEBUG(SSSDBG_CONF_SETTINGS, "No more hosts databases to retry\n");
1272 0 : return ENOENT;
1273 : }
1274 :
1275 : static void
1276 : resolv_gethostbyname_done(struct tevent_req *subreq);
1277 :
1278 : static errno_t
1279 7 : resolv_gethostbyname_step(struct tevent_req *req)
1280 : {
1281 7 : struct gethostbyname_state *state = tevent_req_data(req,
1282 : struct gethostbyname_state);
1283 : struct tevent_req *subreq;
1284 :
1285 7 : switch(state->db[state->dbi]) {
1286 : case DB_FILES:
1287 5 : DEBUG(SSSDBG_TRACE_INTERNAL, "Querying files\n");
1288 5 : subreq = resolv_gethostbyname_files_send(state, state->ev,
1289 : state->resolv_ctx,
1290 : state->name,
1291 : state->family);
1292 5 : break;
1293 : case DB_DNS:
1294 2 : DEBUG(SSSDBG_TRACE_INTERNAL, "Querying DNS\n");
1295 2 : subreq = resolv_gethostbyname_dns_send(state, state->ev,
1296 : state->resolv_ctx,
1297 : state->name,
1298 : state->family);
1299 2 : break;
1300 : default:
1301 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Invalid hosts database\n");
1302 0 : return EINVAL;
1303 : }
1304 :
1305 7 : if (subreq == NULL) {
1306 0 : return ENOMEM;
1307 : }
1308 :
1309 7 : tevent_req_set_callback(subreq, resolv_gethostbyname_done, req);
1310 7 : return EOK;
1311 : }
1312 :
1313 : static void
1314 7 : resolv_gethostbyname_done(struct tevent_req *subreq)
1315 : {
1316 7 : struct tevent_req *req = tevent_req_callback_data(subreq,
1317 : struct tevent_req);
1318 7 : struct gethostbyname_state *state = tevent_req_data(req,
1319 : struct gethostbyname_state);
1320 : errno_t ret;
1321 :
1322 7 : switch(state->db[state->dbi]) {
1323 : case DB_FILES:
1324 5 : ret = resolv_gethostbyname_files_recv(subreq, state,
1325 : &state->status,
1326 : &state->rhostent);
1327 : /* files is synchronous, there can be no timeouts */
1328 5 : state->timeouts = 0;
1329 5 : break;
1330 : case DB_DNS:
1331 2 : ret = resolv_gethostbyname_dns_recv(subreq, state,
1332 : &state->status, &state->timeouts,
1333 : &state->rhostent);
1334 2 : break;
1335 : default:
1336 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Invalid hosts database\n");
1337 0 : tevent_req_error(req, EINVAL);
1338 0 : return;
1339 : }
1340 :
1341 7 : talloc_zfree(subreq);
1342 :
1343 7 : if (ret == ENOENT) {
1344 4 : ret = resolv_gethostbyname_next(state);
1345 4 : if (ret == EOK) {
1346 4 : ret = resolv_gethostbyname_step(req);
1347 4 : if (ret != EOK) {
1348 0 : tevent_req_error(req, ret);
1349 : }
1350 4 : return;
1351 : }
1352 :
1353 : /* No more databases and/or address families */
1354 0 : tevent_req_error(req, ENOENT);
1355 0 : return;
1356 3 : } else if (ret == ETIMEDOUT) {
1357 : /* In case we killed the request before c-ares answered */
1358 0 : state->status = ARES_ETIMEOUT;
1359 : }
1360 :
1361 3 : if (ret != EOK) {
1362 0 : DEBUG(SSSDBG_OP_FAILURE, "querying hosts database failed [%d]: %s\n",
1363 : ret, strerror(ret));
1364 0 : tevent_req_error(req, ret);
1365 0 : return;
1366 : }
1367 :
1368 3 : tevent_req_done(req);
1369 : }
1370 :
1371 : int
1372 3 : resolv_gethostbyname_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
1373 : int *status, int *timeouts,
1374 : struct resolv_hostent **rhostent)
1375 : {
1376 3 : struct gethostbyname_state *state = tevent_req_data(req, struct gethostbyname_state);
1377 :
1378 : /* Fill in even in case of error as status contains the
1379 : * c-ares return code */
1380 3 : if (status) {
1381 3 : *status = state->status;
1382 : }
1383 3 : if (timeouts) {
1384 0 : *timeouts = state->timeouts;
1385 : }
1386 3 : if (rhostent) {
1387 3 : *rhostent = talloc_steal(mem_ctx, state->rhostent);
1388 : }
1389 :
1390 3 : TEVENT_REQ_RETURN_ON_ERROR(req);
1391 :
1392 3 : return EOK;
1393 : }
1394 :
1395 : char *
1396 2 : resolv_get_string_address_index(TALLOC_CTX *mem_ctx,
1397 : struct resolv_hostent *hostent,
1398 : unsigned int addrindex)
1399 : {
1400 : char *address;
1401 :
1402 2 : if (!hostent) return NULL;
1403 :
1404 2 : address = talloc_zero_size(mem_ctx, 128);
1405 2 : if (address == NULL) {
1406 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero failed.\n");
1407 0 : return NULL;
1408 : }
1409 :
1410 2 : errno = 0;
1411 2 : if (inet_ntop(hostent->family, hostent->addr_list[addrindex]->ipaddr,
1412 : address, 128) == NULL) {
1413 0 : DEBUG(SSSDBG_CRIT_FAILURE,
1414 : "inet_ntop failed [%d][%s].\n", errno, strerror(errno));
1415 0 : talloc_free(address);
1416 0 : return NULL;
1417 : }
1418 :
1419 2 : return address;
1420 : }
1421 :
1422 : char *
1423 2 : resolv_get_string_ptr_address(TALLOC_CTX *mem_ctx,
1424 : int family, uint8_t *address)
1425 : {
1426 : char *straddr;
1427 :
1428 2 : if (family == AF_INET6) {
1429 : int i;
1430 : char hexbyte[3];
1431 :
1432 1 : straddr = talloc_strdup(mem_ctx, "\0");
1433 1 : if (!straddr) {
1434 0 : return NULL;
1435 : }
1436 :
1437 17 : for (i = 15; i >= 0; i--) {
1438 16 : snprintf(hexbyte, 3, "%02x", address[i]);
1439 32 : straddr = talloc_asprintf_append(straddr, "%c.%c.",
1440 32 : hexbyte[1], hexbyte[0]);
1441 : }
1442 1 : straddr = talloc_asprintf_append(straddr, "ip6.arpa.");
1443 1 : } else if (family == AF_INET) {
1444 4 : straddr = talloc_asprintf(mem_ctx,
1445 : "%u.%u.%u.%u.in-addr.arpa.",
1446 1 : (address[3]),
1447 1 : (address[2]),
1448 1 : (address[1]),
1449 1 : (address[0]));
1450 : } else {
1451 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Unknown address family\n");
1452 0 : return NULL;
1453 : }
1454 :
1455 2 : return straddr;
1456 : }
1457 :
1458 : struct sockaddr_storage *
1459 0 : resolv_get_sockaddr_address_index(TALLOC_CTX *mem_ctx,
1460 : struct resolv_hostent *hostent,
1461 : int port, int addrindex)
1462 : {
1463 : struct sockaddr_storage *sockaddr;
1464 :
1465 0 : if (!hostent) return NULL;
1466 :
1467 0 : sockaddr = talloc_zero(mem_ctx, struct sockaddr_storage);
1468 0 : if (sockaddr == NULL) {
1469 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero failed.\n");
1470 0 : return NULL;
1471 : }
1472 :
1473 0 : switch(hostent->family) {
1474 : case AF_INET:
1475 0 : sockaddr->ss_family = AF_INET;
1476 0 : memcpy(&((struct sockaddr_in *) sockaddr)->sin_addr,
1477 0 : hostent->addr_list[addrindex]->ipaddr,
1478 : sizeof(struct in_addr));
1479 0 : ((struct sockaddr_in *) sockaddr)->sin_port = (in_port_t) htons(port);
1480 :
1481 0 : break;
1482 : case AF_INET6:
1483 0 : sockaddr->ss_family = AF_INET6;
1484 0 : memcpy(&((struct sockaddr_in6 *) sockaddr)->sin6_addr,
1485 0 : hostent->addr_list[addrindex]->ipaddr,
1486 : sizeof(struct in6_addr));
1487 0 : ((struct sockaddr_in6 *) sockaddr)->sin6_port = (in_port_t) htons(port);
1488 0 : break;
1489 : default:
1490 0 : DEBUG(SSSDBG_CRIT_FAILURE,
1491 : "Unknown address family %d\n", hostent->family);
1492 0 : return NULL;
1493 : }
1494 :
1495 0 : return sockaddr;
1496 : }
1497 :
1498 : /*
1499 : * A simple helper function that will take an array of struct ares_srv_reply that
1500 : * was allocated by malloc() in c-ares and copies it using talloc. The old one
1501 : * is freed and the talloc one is put into 'reply_list' instead.
1502 : */
1503 : static int
1504 1 : rewrite_talloc_srv_reply(TALLOC_CTX *mem_ctx, struct ares_srv_reply **reply_list)
1505 : {
1506 1 : struct ares_srv_reply *ptr = NULL;
1507 1 : struct ares_srv_reply *new_list = NULL;
1508 1 : struct ares_srv_reply *old_list = *reply_list;
1509 :
1510 : /* Nothing to do, but not an error */
1511 1 : if (!old_list) {
1512 0 : return EOK;
1513 : }
1514 :
1515 : /* Copy the linked list */
1516 4 : while (old_list) {
1517 : /* Special case for the first node */
1518 2 : if (!new_list) {
1519 1 : new_list = talloc_zero(mem_ctx, struct ares_srv_reply);
1520 1 : if (new_list == NULL) {
1521 0 : ares_free_data(*reply_list);
1522 0 : return ENOMEM;
1523 : }
1524 1 : ptr = new_list;
1525 : } else {
1526 1 : ptr->next = talloc_zero(new_list, struct ares_srv_reply);
1527 1 : if (ptr->next == NULL) {
1528 0 : ares_free_data(*reply_list);
1529 0 : talloc_free(new_list);
1530 0 : return ENOMEM;
1531 : }
1532 1 : ptr = ptr->next;
1533 : }
1534 :
1535 2 : ptr->weight = old_list->weight;
1536 2 : ptr->priority = old_list->priority;
1537 2 : ptr->port = old_list->port;
1538 2 : ptr->host = talloc_strdup(ptr, old_list->host);
1539 2 : if (ptr->host == NULL) {
1540 0 : ares_free_data(*reply_list);
1541 0 : talloc_free(new_list);
1542 0 : return ENOMEM;
1543 : }
1544 :
1545 2 : old_list = old_list->next;
1546 : }
1547 :
1548 : /* Free the old one (uses malloc). */
1549 1 : ares_free_data(*reply_list);
1550 :
1551 : /* And now put our own new_list in place. */
1552 1 : *reply_list = new_list;
1553 :
1554 1 : return EOK;
1555 : }
1556 :
1557 : /*******************************************************************
1558 : * Get SRV record *
1559 : *******************************************************************/
1560 :
1561 : struct getsrv_state {
1562 : struct tevent_context *ev;
1563 : struct resolv_ctx *resolv_ctx;
1564 : /* the SRV query - for example _ldap._tcp.example.com */
1565 : const char *query;
1566 :
1567 : /* parsed data returned by ares */
1568 : struct ares_srv_reply *reply_list;
1569 : uint32_t ttl;
1570 : int status;
1571 : int timeouts;
1572 : int retrying;
1573 : };
1574 :
1575 : static void
1576 : ares_getsrv_wakeup(struct tevent_req *subreq);
1577 : static void
1578 : resolv_getsrv_query(struct tevent_req *req,
1579 : struct getsrv_state *state);
1580 :
1581 : struct tevent_req *
1582 1 : resolv_getsrv_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
1583 : struct resolv_ctx *ctx, const char *query)
1584 : {
1585 : struct tevent_req *req, *subreq;
1586 : struct getsrv_state *state;
1587 1 : struct timeval tv = { 0, 0 };
1588 :
1589 1 : DEBUG(SSSDBG_CONF_SETTINGS,
1590 : "Trying to resolve SRV record of '%s'\n", query);
1591 :
1592 1 : if (ctx->channel == NULL) {
1593 0 : DEBUG(SSSDBG_CRIT_FAILURE,
1594 : "Invalid ares channel - this is likely a bug\n");
1595 0 : return NULL;
1596 : }
1597 :
1598 1 : req = tevent_req_create(mem_ctx, &state, struct getsrv_state);
1599 1 : if (req == NULL)
1600 0 : return NULL;
1601 :
1602 1 : state->resolv_ctx = ctx;
1603 1 : state->query = query;
1604 1 : state->reply_list = NULL;
1605 1 : state->ttl = 0;
1606 1 : state->status = 0;
1607 1 : state->timeouts = 0;
1608 1 : state->retrying = 0;
1609 1 : state->ev = ev;
1610 :
1611 1 : subreq = tevent_wakeup_send(req, ev, tv);
1612 1 : if (subreq == NULL) {
1613 0 : DEBUG(SSSDBG_CRIT_FAILURE,
1614 : "Failed to add critical timer to run next operation!\n");
1615 0 : talloc_zfree(req);
1616 0 : return NULL;
1617 : }
1618 1 : tevent_req_set_callback(subreq, ares_getsrv_wakeup, req);
1619 :
1620 1 : return req;
1621 : }
1622 :
1623 : /*
1624 : * Implemented based on http://tools.ietf.org/html/rfc2181#section-5
1625 : *
1626 : * Especially:
1627 : * 5.2. TTLs of RRs in an RRSet
1628 : * Consequently the use of differing TTLs in an RRSet is hereby
1629 : * deprecated, the TTLs of all RRs in an RRSet must be the same.
1630 : * ...
1631 : * Should an authoritative source send such a malformed RRSet, the
1632 : * client should treat the RRs for all purposes as if all TTLs in the
1633 : * RRSet had been set to the value of the lowest TTL in the RRSet.
1634 : *
1635 : * On success, returns true and sets the TTL in the _ttl parameter. On
1636 : * failure, returns false and _ttl is undefined.
1637 : */
1638 : static bool
1639 1 : resolv_get_ttl(const unsigned char *abuf, const int alen, uint32_t *_ttl)
1640 : {
1641 : const unsigned char *aptr;
1642 : int ret;
1643 1 : char *name = NULL;
1644 : long len;
1645 1 : uint32_t ttl = 0;
1646 : uint32_t rr_ttl;
1647 : unsigned int rr_len;
1648 : unsigned int ancount;
1649 : unsigned int i;
1650 :
1651 : /* Read the number of RRs and then skip past the header */
1652 1 : if (alen < NS_HFIXEDSZ) {
1653 0 : return false;
1654 : }
1655 :
1656 1 : ancount = DNS_HEADER_ANCOUNT(abuf);
1657 1 : if (ancount == 0) {
1658 0 : return false;
1659 : }
1660 :
1661 1 : aptr = abuf + NS_HFIXEDSZ;
1662 :
1663 : /* We only care about len from the question data,
1664 : * so that we can move past hostname */
1665 1 : ret = ares_expand_name(aptr, abuf, alen, &name, &len);
1666 1 : ares_free_string(name);
1667 1 : if (ret != ARES_SUCCESS) {
1668 0 : return false;
1669 : }
1670 :
1671 : /* Skip past the question */
1672 1 : aptr += len + NS_QFIXEDSZ;
1673 1 : if (aptr > abuf + alen) {
1674 0 : return false;
1675 : }
1676 :
1677 : /* Examine each RR in turn and read the lowest TTL */
1678 3 : for (i = 0; i < ancount; i++) {
1679 : /* Decode the RR up to the data field. */
1680 2 : ret = ares_expand_name(aptr, abuf, alen, &name, &len);
1681 2 : ares_free_string(name);
1682 2 : if (ret != ARES_SUCCESS) {
1683 0 : return false;
1684 : }
1685 :
1686 2 : aptr += len;
1687 2 : if (aptr + NS_RRFIXEDSZ > abuf + alen) {
1688 0 : return false;
1689 : }
1690 :
1691 2 : rr_len = DNS_RR_LEN(aptr);
1692 2 : rr_ttl = DNS_RR_TTL(aptr);
1693 2 : if (aptr + rr_len > abuf + alen) {
1694 0 : return false;
1695 : }
1696 2 : aptr += NS_RRFIXEDSZ + rr_len;
1697 :
1698 2 : if (ttl > 0) {
1699 1 : ttl = MIN(ttl, rr_ttl);
1700 : } else {
1701 1 : ttl = rr_ttl; /* special-case for first TTL */
1702 : }
1703 : }
1704 :
1705 1 : *_ttl = ttl;
1706 1 : return true;
1707 : }
1708 :
1709 : static void
1710 1 : resolv_getsrv_done(void *arg, int status, int timeouts, unsigned char *abuf, int alen)
1711 : {
1712 1 : struct resolv_request *rreq = talloc_get_type(arg, struct resolv_request);
1713 : struct tevent_req *req;
1714 : struct getsrv_state *state;
1715 : int ret;
1716 : bool ok;
1717 : struct ares_srv_reply *reply_list;
1718 :
1719 1 : if (rreq->rwatch == NULL) {
1720 : /* The tevent request was cancelled while the ares call was still in
1721 : * progress so nobody cares about the result now. Quit. */
1722 0 : unschedule_timeout_watcher(rreq->ctx, rreq);
1723 0 : return;
1724 : }
1725 :
1726 1 : req = rreq->rwatch->req;
1727 1 : unschedule_timeout_watcher(rreq->ctx, rreq);
1728 1 : state = tevent_req_data(req, struct getsrv_state);
1729 :
1730 1 : if (state->retrying == 0 && status == ARES_EDESTRUCTION
1731 0 : && state->resolv_ctx->channel != NULL) {
1732 0 : state->retrying = 1;
1733 0 : resolv_getsrv_query(req, state);
1734 0 : return;
1735 : }
1736 :
1737 1 : state->status = status;
1738 1 : state->timeouts = timeouts;
1739 :
1740 1 : if (status != ARES_SUCCESS) {
1741 0 : ret = return_code(status);
1742 0 : goto fail;
1743 : }
1744 :
1745 1 : ret = ares_parse_srv_reply(abuf, alen, &reply_list);
1746 1 : if (ret != ARES_SUCCESS) {
1747 0 : DEBUG(SSSDBG_OP_FAILURE,
1748 : "SRV record parsing failed: %d: %s\n", ret, ares_strerror(ret));
1749 0 : ret = return_code(ret);
1750 0 : goto fail;
1751 : }
1752 1 : ret = rewrite_talloc_srv_reply(req, &reply_list);
1753 1 : if (ret != EOK) {
1754 0 : goto fail;
1755 : }
1756 1 : state->reply_list = reply_list;
1757 1 : ok = resolv_get_ttl(abuf, alen, &state->ttl);
1758 1 : if (ok == false) {
1759 0 : DEBUG(SSSDBG_MINOR_FAILURE, "Could not read TTL, using the default..\n");
1760 0 : state->ttl = RESOLV_DEFAULT_SRV_TTL;
1761 : }
1762 1 : DEBUG(SSSDBG_TRACE_LIBS, "Using TTL [%"PRIu32"]\n", state->ttl);
1763 :
1764 1 : tevent_req_done(req);
1765 1 : return;
1766 :
1767 : fail:
1768 0 : state->reply_list = NULL;
1769 0 : tevent_req_error(req, ret);
1770 : }
1771 :
1772 : int
1773 1 : resolv_getsrv_recv(TALLOC_CTX *mem_ctx, struct tevent_req *req, int *status,
1774 : int *timeouts, struct ares_srv_reply **reply_list,
1775 : uint32_t *ttl)
1776 : {
1777 1 : struct getsrv_state *state = tevent_req_data(req, struct getsrv_state);
1778 :
1779 1 : if (status)
1780 1 : *status = state->status;
1781 1 : if (timeouts)
1782 0 : *timeouts = state->timeouts;
1783 1 : if (reply_list)
1784 1 : *reply_list = talloc_steal(mem_ctx, state->reply_list);
1785 1 : if (ttl) {
1786 1 : *ttl = state->ttl;
1787 : }
1788 :
1789 1 : TEVENT_REQ_RETURN_ON_ERROR(req);
1790 :
1791 1 : return EOK;
1792 : }
1793 :
1794 : static void
1795 1 : ares_getsrv_wakeup(struct tevent_req *subreq)
1796 : {
1797 1 : struct tevent_req *req = tevent_req_callback_data(subreq,
1798 : struct tevent_req);
1799 1 : struct getsrv_state *state = tevent_req_data(req,
1800 : struct getsrv_state);
1801 :
1802 1 : if (!tevent_wakeup_recv(subreq)) {
1803 0 : return;
1804 : }
1805 1 : talloc_zfree(subreq);
1806 :
1807 1 : if (state->resolv_ctx->channel == NULL) {
1808 0 : DEBUG(SSSDBG_CRIT_FAILURE,
1809 : "Invalid ares channel - this is likely a bug\n");
1810 0 : tevent_req_error(req, EIO);
1811 0 : return;
1812 : }
1813 :
1814 1 : return resolv_getsrv_query(req, state);
1815 : }
1816 :
1817 : static void
1818 1 : resolv_getsrv_query(struct tevent_req *req,
1819 : struct getsrv_state *state)
1820 : {
1821 : struct resolv_request *rreq;
1822 :
1823 1 : rreq = schedule_timeout_watcher(state->ev, state->resolv_ctx, req);
1824 1 : if (!rreq) {
1825 0 : tevent_req_error(req, ENOMEM);
1826 1 : return;
1827 : }
1828 :
1829 1 : ares_query(state->resolv_ctx->channel, state->query,
1830 : ns_c_in, ns_t_srv, resolv_getsrv_done, rreq);
1831 : }
1832 :
1833 : /* TXT parsing is not used anywhere in the code yet, so we disable it
1834 : * for now
1835 : */
1836 : #ifdef BUILD_TXT
1837 :
1838 : /*
1839 : * A simple helper function that will take an array of struct txt_reply that
1840 : * was allocated by malloc() in c-ares and copies it using talloc. The old one
1841 : * is freed and the talloc one is put into 'reply_list' instead.
1842 : */
1843 : static int
1844 0 : rewrite_talloc_txt_reply(TALLOC_CTX *mem_ctx, struct ares_txt_reply **reply_list)
1845 : {
1846 0 : struct ares_txt_reply *ptr = NULL;
1847 0 : struct ares_txt_reply *new_list = NULL;
1848 0 : struct ares_txt_reply *old_list = *reply_list;
1849 :
1850 : /* Nothing to do, but not an error */
1851 0 : if (!old_list) {
1852 0 : return EOK;
1853 : }
1854 :
1855 : /* Copy the linked list */
1856 0 : while (old_list) {
1857 :
1858 : /* Special case for the first node */
1859 0 : if (!new_list) {
1860 0 : new_list = talloc_zero(mem_ctx, struct ares_txt_reply);
1861 0 : if (new_list == NULL) {
1862 0 : ares_free_data(*reply_list);
1863 0 : talloc_free(new_list);
1864 0 : return ENOMEM;
1865 : }
1866 0 : ptr = new_list;
1867 : } else {
1868 0 : ptr->next = talloc_zero(new_list, struct ares_txt_reply);
1869 0 : if (ptr->next == NULL) {
1870 0 : ares_free_data(*reply_list);
1871 0 : talloc_free(new_list);
1872 0 : return ENOMEM;
1873 : }
1874 0 : ptr = ptr->next;
1875 : }
1876 :
1877 0 : ptr->length = old_list->length;
1878 0 : ptr->txt = talloc_memdup(ptr, old_list->txt,
1879 : old_list->length);
1880 0 : if (ptr->txt == NULL) {
1881 0 : ares_free_data(*reply_list);
1882 0 : talloc_free(new_list);
1883 0 : return ENOMEM;
1884 : }
1885 :
1886 0 : old_list = old_list->next;
1887 : }
1888 :
1889 0 : ares_free_data(*reply_list);
1890 :
1891 : /* And now put our own new_list in place. */
1892 0 : *reply_list = new_list;
1893 :
1894 0 : return EOK;
1895 : }
1896 :
1897 : /*******************************************************************
1898 : * Get TXT record *
1899 : *******************************************************************/
1900 :
1901 : struct gettxt_state {
1902 : struct tevent_context *ev;
1903 : struct resolv_ctx *resolv_ctx;
1904 : /* the TXT query */
1905 : const char *query;
1906 :
1907 : /* parsed data returned by ares */
1908 : struct ares_txt_reply *reply_list;
1909 : int status;
1910 : int timeouts;
1911 : int retrying;
1912 : };
1913 :
1914 : static void
1915 : ares_gettxt_wakeup(struct tevent_req *subreq);
1916 : static void
1917 : resolv_gettxt_query(struct tevent_req *req,
1918 : struct gettxt_state *state);
1919 :
1920 : struct tevent_req *
1921 0 : resolv_gettxt_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
1922 : struct resolv_ctx *ctx, const char *query)
1923 : {
1924 : struct tevent_req *req, *subreq;
1925 : struct gettxt_state *state;
1926 0 : struct timeval tv = { 0, 0 };
1927 :
1928 0 : DEBUG(SSSDBG_CONF_SETTINGS,
1929 : "Trying to resolve TXT record of '%s'\n", query);
1930 :
1931 0 : if (ctx->channel == NULL) {
1932 0 : DEBUG(SSSDBG_CRIT_FAILURE,
1933 : "Invalid ares channel - this is likely a bug\n");
1934 0 : return NULL;
1935 : }
1936 :
1937 0 : req = tevent_req_create(mem_ctx, &state, struct gettxt_state);
1938 0 : if (req == NULL)
1939 0 : return NULL;
1940 :
1941 0 : state->resolv_ctx = ctx;
1942 0 : state->query = query;
1943 0 : state->reply_list = NULL;
1944 0 : state->status = 0;
1945 0 : state->timeouts = 0;
1946 0 : state->retrying = 0;
1947 0 : state->ev = ev;
1948 :
1949 0 : subreq = tevent_wakeup_send(req, ev, tv);
1950 0 : if (subreq == NULL) {
1951 0 : DEBUG(SSSDBG_CRIT_FAILURE,
1952 : "Failed to add critical timer to run next operation!\n");
1953 0 : talloc_zfree(req);
1954 0 : return NULL;
1955 : }
1956 0 : tevent_req_set_callback(subreq, ares_gettxt_wakeup, req);
1957 :
1958 0 : return req;
1959 : }
1960 :
1961 : static void
1962 0 : resolv_gettxt_done(void *arg, int status, int timeouts, unsigned char *abuf, int alen)
1963 : {
1964 0 : struct resolv_request *rreq = talloc_get_type(arg, struct resolv_request);
1965 : struct tevent_req *req;
1966 : struct gettxt_state *state;
1967 : int ret;
1968 : struct ares_txt_reply *reply_list;
1969 :
1970 0 : if (rreq->rwatch == NULL) {
1971 : /* The tevent request was cancelled while the ares call was still in
1972 : * progress so nobody cares about the result now. Quit. */
1973 0 : unschedule_timeout_watcher(rreq->ctx, rreq);
1974 0 : return;
1975 : }
1976 :
1977 0 : req = rreq->rwatch->req;
1978 0 : unschedule_timeout_watcher(rreq->ctx, rreq);
1979 0 : state = tevent_req_data(req, struct gettxt_state);
1980 :
1981 0 : if (state->retrying == 0 && status == ARES_EDESTRUCTION
1982 0 : && state->resolv_ctx->channel != NULL) {
1983 0 : state->retrying = 1;
1984 0 : ares_query(state->resolv_ctx->channel, state->query,
1985 : ns_c_in, ns_t_txt, resolv_gettxt_done, req);
1986 0 : return;
1987 : }
1988 :
1989 0 : state->status = status;
1990 0 : state->timeouts = timeouts;
1991 :
1992 0 : if (status != ARES_SUCCESS) {
1993 0 : ret = return_code(status);
1994 0 : goto fail;
1995 : }
1996 :
1997 0 : ret = ares_parse_txt_reply(abuf, alen, &reply_list);
1998 0 : if (status != ARES_SUCCESS) {
1999 0 : DEBUG(SSSDBG_OP_FAILURE,
2000 : "TXT record parsing failed: %d: %s\n", ret, ares_strerror(ret));
2001 0 : ret = return_code(ret);
2002 0 : goto fail;
2003 : }
2004 0 : ret = rewrite_talloc_txt_reply(req, &reply_list);
2005 0 : if (ret != EOK) {
2006 0 : goto fail;
2007 : }
2008 0 : state->reply_list = reply_list;
2009 :
2010 0 : tevent_req_done(req);
2011 0 : return;
2012 :
2013 : fail:
2014 0 : state->reply_list = NULL;
2015 0 : tevent_req_error(req, ret);
2016 : }
2017 :
2018 : int
2019 0 : resolv_gettxt_recv(TALLOC_CTX *mem_ctx, struct tevent_req *req, int *status,
2020 : int *timeouts, struct ares_txt_reply **reply_list)
2021 : {
2022 0 : struct gettxt_state *state = tevent_req_data(req, struct gettxt_state);
2023 :
2024 0 : if (status)
2025 0 : *status = state->status;
2026 0 : if (timeouts)
2027 0 : *timeouts = state->timeouts;
2028 0 : if (reply_list)
2029 0 : *reply_list = talloc_steal(mem_ctx, state->reply_list);
2030 :
2031 0 : TEVENT_REQ_RETURN_ON_ERROR(req);
2032 :
2033 0 : return EOK;
2034 : }
2035 :
2036 : static void
2037 0 : ares_gettxt_wakeup(struct tevent_req *subreq)
2038 : {
2039 0 : struct tevent_req *req = tevent_req_callback_data(subreq,
2040 : struct tevent_req);
2041 0 : struct gettxt_state *state = tevent_req_data(req,
2042 : struct gettxt_state);
2043 :
2044 0 : if (!tevent_wakeup_recv(subreq)) {
2045 0 : return;
2046 : }
2047 0 : talloc_zfree(subreq);
2048 :
2049 0 : if (state->resolv_ctx->channel == NULL) {
2050 0 : DEBUG(SSSDBG_CRIT_FAILURE,
2051 : "Invalid ares channel - this is likely a bug\n");
2052 0 : tevent_req_error(req, EIO);
2053 0 : return;
2054 : }
2055 :
2056 0 : return resolv_gettxt_query(req, state);
2057 : }
2058 :
2059 : static void
2060 0 : resolv_gettxt_query(struct tevent_req *req,
2061 : struct gettxt_state *state)
2062 : {
2063 : struct resolv_request *rreq;
2064 :
2065 0 : rreq = schedule_timeout_watcher(state->ev, state->resolv_ctx, req);
2066 0 : if (!rreq) {
2067 0 : tevent_req_error(req, ENOMEM);
2068 0 : return;
2069 : }
2070 :
2071 0 : ares_query(state->resolv_ctx->channel, state->query,
2072 : ns_c_in, ns_t_txt, resolv_gettxt_done, rreq);
2073 : }
2074 :
2075 : #endif
2076 :
2077 9 : static struct ares_srv_reply *split_reply_list(struct ares_srv_reply *list)
2078 : {
2079 : struct ares_srv_reply *single_step, *double_step, *prev;
2080 :
2081 9 : if (!list) {
2082 0 : return NULL;
2083 : }
2084 :
2085 9 : prev = list;
2086 9 : single_step = list->next;
2087 9 : double_step = single_step->next;
2088 :
2089 20 : while (double_step && double_step->next) {
2090 2 : prev = single_step;
2091 2 : single_step = single_step->next;
2092 2 : double_step = double_step->next->next;
2093 : }
2094 :
2095 9 : prev->next = NULL;
2096 9 : return single_step;
2097 : }
2098 :
2099 9 : static struct ares_srv_reply *merge_reply_list(struct ares_srv_reply *left,
2100 : struct ares_srv_reply *right)
2101 : {
2102 : struct ares_srv_reply *l, *r;
2103 : struct ares_srv_reply *res, *res_start;
2104 :
2105 9 : if (!left)
2106 0 : return right;
2107 9 : if (!right)
2108 0 : return left;
2109 :
2110 9 : if (left->priority < right->priority) {
2111 3 : res_start = left;
2112 3 : l = left->next;
2113 3 : r = right;
2114 : } else {
2115 6 : res_start = right;
2116 6 : l = left;
2117 6 : r = right->next;
2118 : }
2119 :
2120 9 : res = res_start;
2121 :
2122 24 : while(l && r) {
2123 6 : if (l->priority < r->priority) {
2124 4 : res->next = l;
2125 4 : res = l;
2126 4 : l = l->next;
2127 : } else {
2128 2 : res->next = r;
2129 2 : res = r;
2130 2 : r = r->next;
2131 : }
2132 : }
2133 :
2134 9 : res->next = l ? l : r;
2135 :
2136 9 : return res_start;
2137 : }
2138 :
2139 : /**
2140 : * sort linked list of struct ares_srv_reply by priority using merge sort.
2141 : *
2142 : * Merge sort is ideal for sorting linked lists as there is no problem
2143 : * with absence of random access into the list. The complexity is O(n log n)
2144 : *
2145 : * For reference, see Robert Sedgewick's "Algorithms in C", Addison-Wesley,
2146 : * ISBN 0-201-51425
2147 : */
2148 21 : static struct ares_srv_reply *reply_priority_sort(struct ares_srv_reply *list)
2149 : {
2150 : struct ares_srv_reply *half;
2151 :
2152 21 : if (!list || !list->next)
2153 12 : return list;
2154 :
2155 9 : half = split_reply_list(list);
2156 9 : list = merge_reply_list(reply_priority_sort(list),
2157 : reply_priority_sort(half));
2158 :
2159 9 : return list;
2160 : }
2161 :
2162 8 : static int reply_weight_rearrange(int len,
2163 : struct ares_srv_reply **start,
2164 : struct ares_srv_reply **end)
2165 : {
2166 : int i;
2167 : int total, selected;
2168 : int *totals;
2169 : struct ares_srv_reply *r, *prev, *tmp;
2170 8 : struct ares_srv_reply *new_start = NULL;
2171 8 : struct ares_srv_reply *new_end = NULL;
2172 : int ret;
2173 :
2174 8 : if (len <= 1) {
2175 4 : return EOK;
2176 : }
2177 :
2178 4 : totals = talloc_array(NULL, int, len);
2179 4 : if (!totals) {
2180 0 : return ENOMEM;
2181 : }
2182 :
2183 4 : srand(time(NULL) * getpid());
2184 :
2185 : /* promote all servers with weight==0 to the top */
2186 4 : r = *(start);
2187 4 : prev = NULL;
2188 16 : while (r != NULL) {
2189 8 : if (r->weight == 0 && r != *start) {
2190 : /* remove from the old list */
2191 4 : prev->next = r->next;
2192 :
2193 : /* add to the head of the new list */
2194 4 : tmp = r;
2195 4 : r = r->next;
2196 :
2197 4 : tmp->next = *start;
2198 4 : *start = tmp;
2199 : } else {
2200 4 : prev = r;
2201 4 : r = r->next;
2202 : }
2203 : }
2204 4 : *end = prev ? prev : *start;
2205 :
2206 16 : while (*start != NULL) {
2207 : /* Commpute the sum of the weights of those RRs, and with each RR
2208 : * associate the running sum in the selected order.
2209 : */
2210 8 : total = 0;
2211 8 : memset(totals, -1, sizeof(int) * len);
2212 20 : for (i = 0, r = *start; r != NULL; r=r->next, ++i) {
2213 12 : totals[i] = r->weight + total;
2214 12 : total = totals[i];
2215 : }
2216 :
2217 : /* choose a uniform random number between 0 and the sum computed
2218 : * (inclusive), and select the RR whose running sum value is the
2219 : * first in the selected order which is greater than or equal to
2220 : * the random number selected.
2221 : */
2222 8 : selected = (int)((total + 1) * (rand()/(RAND_MAX + 1.0)));
2223 8 : for (i = 0, r = *start, prev = NULL; r != NULL; r=r->next, ++i) {
2224 8 : if (totals[i] >= selected)
2225 8 : break;
2226 :
2227 0 : prev = r;
2228 : }
2229 :
2230 8 : if (r == NULL || totals[i] == -1) {
2231 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Bug: did not select any server!\n");
2232 0 : ret = EIO;
2233 0 : goto done;
2234 : }
2235 :
2236 : /* remove r from the old list */
2237 8 : if (prev) {
2238 0 : prev->next = r->next;
2239 : } else {
2240 8 : *start = r->next;
2241 : }
2242 :
2243 : /* add r to the end of the new list */
2244 8 : if (!new_start) {
2245 4 : new_start = r;
2246 4 : new_end = r;
2247 : } else {
2248 4 : new_end->next = r;
2249 4 : new_end = r;
2250 : }
2251 : }
2252 4 : new_end->next = NULL;
2253 :
2254 : /* return the rearranged list */
2255 4 : *start = new_start;
2256 4 : *end = new_end;
2257 4 : ret = EOK;
2258 :
2259 : done:
2260 4 : talloc_free(totals);
2261 4 : return ret;
2262 : }
2263 :
2264 : int
2265 3 : resolv_sort_srv_reply(struct ares_srv_reply **reply)
2266 : {
2267 : int ret;
2268 : struct ares_srv_reply *pri_start, *pri_end, *next, *prev_end;
2269 : int len;
2270 :
2271 : /* RFC 2782 says: If there is precisely one SRV RR, and its Target is "."
2272 : * (the root domain), abort.
2273 : */
2274 3 : if (*reply && !(*reply)->next && strcmp((*reply)->host, ".") == 0) {
2275 0 : DEBUG(SSSDBG_CRIT_FAILURE,
2276 : "DNS returned only the root domain, aborting\n");
2277 0 : return EIO;
2278 : }
2279 :
2280 : /* sort the list by priority */
2281 3 : *reply = reply_priority_sort(*reply);
2282 :
2283 3 : pri_start = *reply;
2284 3 : prev_end = NULL;
2285 :
2286 14 : while (pri_start) {
2287 8 : pri_end = pri_start;
2288 :
2289 : /* Find nodes with the same priority */
2290 8 : len = 1;
2291 20 : while (pri_end->next && pri_end->priority == pri_end->next->priority) {
2292 4 : pri_end = pri_end->next;
2293 4 : len++;
2294 : }
2295 :
2296 : /* rearrange each priority level according to the weight field */
2297 8 : next = pri_end->next;
2298 8 : pri_end->next = NULL;
2299 8 : ret = reply_weight_rearrange(len, &pri_start, &pri_end);
2300 8 : if (ret) {
2301 0 : DEBUG(SSSDBG_CRIT_FAILURE,
2302 : "Error rearranging priority level [%d]: %s\n",
2303 : ret, strerror(ret));
2304 0 : return ret;
2305 : }
2306 :
2307 : /* Hook the level back into the list */
2308 8 : if (prev_end) {
2309 5 : prev_end->next = pri_start;
2310 : } else {
2311 3 : *reply = pri_start;
2312 : }
2313 8 : pri_end->next = next;
2314 :
2315 : /* Move on to the next level */
2316 8 : prev_end = pri_end;
2317 8 : pri_start = next;
2318 : }
2319 :
2320 3 : return EOK;
2321 : }
|