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 0 : 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 0 : 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 0 : 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 2 : return;
389 : }
390 0 : watch = watch->next;
391 : }
392 : }
393 :
394 : static int
395 15 : resolv_ctx_destructor(struct resolv_ctx *ctx)
396 : {
397 : ares_channel channel;
398 :
399 15 : 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 15 : channel = ctx->channel;
407 15 : ctx->channel = NULL;
408 15 : ares_destroy(channel);
409 :
410 15 : 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 10 : for (i = 0; i < len; i++) {
547 5 : ret->aliases[i] = talloc_strdup(ret->aliases, src->h_aliases[i]);
548 5 : 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 : tevent_req_error(req, ENOMEM);
698 0 : goto done;
699 : }
700 :
701 5 : state->resolv_ctx = ctx;
702 5 : state->name = name;
703 5 : state->rhostent = NULL;
704 5 : state->family = family;
705 :
706 5 : DEBUG(SSSDBG_CONF_SETTINGS,
707 : "Trying to resolve %s record of '%s' in files\n",
708 : state->family == AF_INET ? "A" : "AAAA", state->name);
709 :
710 15 : state->status = ares_gethostbyname_file(state->resolv_ctx->channel,
711 10 : state->name, state->family,
712 : &hostent);
713 :
714 5 : if (state->status == ARES_SUCCESS) {
715 1 : state->rhostent = resolv_copy_hostent(state, hostent);
716 1 : if (state->rhostent == NULL) {
717 0 : tevent_req_error(req, ENOMEM);
718 0 : goto done;
719 : }
720 4 : } else if (state->status == ARES_ENOTFOUND ||
721 0 : state->status == ARES_ENODATA) {
722 : /* Just say we didn't find anything and let the caller decide
723 : * about retrying */
724 4 : tevent_req_error(req, ENOENT);
725 4 : goto done;
726 : } else {
727 0 : tevent_req_error(req, return_code(state->status));
728 0 : goto done;
729 : }
730 :
731 1 : tevent_req_done(req);
732 : done:
733 5 : if (hostent) ares_free_hostent(hostent);
734 5 : tevent_req_post(req, ev);
735 5 : return req;
736 : }
737 :
738 : static errno_t
739 5 : resolv_gethostbyname_files_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
740 : int *status, struct resolv_hostent **rhostent)
741 : {
742 5 : struct gethostbyname_files_state *state = tevent_req_data(req,
743 : struct gethostbyname_files_state);
744 :
745 : /* Fill in even in case of error as status contains the
746 : * c-ares return code */
747 5 : if (status) {
748 5 : *status = state->status;
749 : }
750 5 : if (rhostent) {
751 5 : *rhostent = talloc_steal(mem_ctx, state->rhostent);
752 : }
753 :
754 9 : TEVENT_REQ_RETURN_ON_ERROR(req);
755 :
756 1 : return EOK;
757 : }
758 :
759 : /* ==================== Resolve host name in DNS =========================*/
760 : struct gethostbyname_dns_state {
761 : struct resolv_ctx *resolv_ctx;
762 : struct tevent_context *ev;
763 :
764 : /* Part of the query. */
765 : const char *name;
766 : int family;
767 :
768 : /* query result */
769 : struct resolv_hostent *rhostent;
770 :
771 : /* These are returned by ares. */
772 : int status;
773 : int timeouts;
774 : int retrying;
775 : };
776 :
777 : static void
778 : resolv_gethostbyname_dns_wakeup(struct tevent_req *subreq);
779 : static void
780 : resolv_gethostbyname_dns_query(struct tevent_req *req,
781 : struct gethostbyname_dns_state *state);
782 : static void
783 : resolv_gethostbyname_dns_query_done(void *arg, int status, int timeouts,
784 : unsigned char *abuf, int alen);
785 : static int
786 : resolv_gethostbyname_dns_parse(struct gethostbyname_dns_state *state,
787 : int status, unsigned char *abuf, int alen);
788 :
789 : static struct tevent_req *
790 2 : resolv_gethostbyname_dns_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
791 : struct resolv_ctx *ctx, const char *name,
792 : int family)
793 : {
794 : struct tevent_req *req, *subreq;
795 : struct gethostbyname_dns_state *state;
796 2 : struct timeval tv = { 0, 0 };
797 :
798 2 : if (ctx->channel == NULL) {
799 0 : DEBUG(SSSDBG_CRIT_FAILURE,
800 : "Invalid ares channel - this is likely a bug\n");
801 0 : return NULL;
802 : }
803 :
804 2 : req = tevent_req_create(mem_ctx, &state, struct gethostbyname_dns_state);
805 2 : if (req == NULL) {
806 0 : return NULL;
807 : }
808 :
809 2 : state->resolv_ctx = ctx;
810 2 : state->ev = ev;
811 2 : state->name = name;
812 2 : state->rhostent = NULL;
813 2 : state->status = 0;
814 2 : state->timeouts = 0;
815 2 : state->retrying = 0;
816 2 : state->family = family;
817 :
818 : /* We need to have a wrapper around ares async calls, because
819 : * they can in some cases call it's callback immediately.
820 : * This would not let our caller to set a callback for req. */
821 2 : subreq = tevent_wakeup_send(req, ev, tv);
822 2 : if (subreq == NULL) {
823 0 : DEBUG(SSSDBG_CRIT_FAILURE,
824 : "Failed to add critical timer to run next operation!\n");
825 0 : talloc_zfree(req);
826 0 : return NULL;
827 : }
828 2 : tevent_req_set_callback(subreq, resolv_gethostbyname_dns_wakeup, req);
829 :
830 2 : return req;
831 : }
832 :
833 : static void
834 2 : resolv_gethostbyname_dns_wakeup(struct tevent_req *subreq)
835 : {
836 2 : struct tevent_req *req = tevent_req_callback_data(subreq,
837 : struct tevent_req);
838 2 : struct gethostbyname_dns_state *state = tevent_req_data(req,
839 : struct gethostbyname_dns_state);
840 :
841 2 : if (!tevent_wakeup_recv(subreq)) {
842 0 : tevent_req_error(req, EIO);
843 0 : return;
844 : }
845 2 : talloc_zfree(subreq);
846 :
847 2 : if (state->resolv_ctx->channel == NULL) {
848 0 : DEBUG(SSSDBG_CRIT_FAILURE,
849 : "Invalid ares channel - this is likely a bug\n");
850 0 : tevent_req_error(req, EIO);
851 0 : return;
852 : }
853 :
854 2 : resolv_gethostbyname_dns_query(req, state);
855 : }
856 :
857 : static void
858 2 : resolv_gethostbyname_dns_query(struct tevent_req *req,
859 : struct gethostbyname_dns_state *state)
860 : {
861 : struct resolv_request *rreq;
862 :
863 2 : DEBUG(SSSDBG_CONF_SETTINGS, "Trying to resolve %s record of '%s' in DNS\n",
864 : state->family == AF_INET ? "A" : "AAAA", state->name);
865 :
866 2 : rreq = schedule_timeout_watcher(state->ev, state->resolv_ctx, req);
867 2 : if (!rreq) {
868 0 : tevent_req_error(req, ENOMEM);
869 0 : return;
870 : }
871 :
872 2 : ares_search(state->resolv_ctx->channel,
873 : state->name, ns_c_in,
874 2 : (state->family == AF_INET) ? ns_t_a : ns_t_aaaa,
875 : resolv_gethostbyname_dns_query_done, rreq);
876 : }
877 :
878 : static void
879 2 : resolv_gethostbyname_dns_query_done(void *arg, int status, int timeouts,
880 : unsigned char *abuf, int alen)
881 : {
882 : errno_t ret;
883 : struct gethostbyname_dns_state *state;
884 2 : struct resolv_request *rreq = talloc_get_type(arg, struct resolv_request);
885 : struct tevent_req *req;
886 :
887 :
888 2 : if (rreq->rwatch == NULL) {
889 : /* The tevent request was cancelled while the ares call was still in
890 : * progress so nobody cares about the result now. Quit. */
891 0 : unschedule_timeout_watcher(rreq->ctx, rreq);
892 0 : return;
893 : }
894 :
895 2 : req = rreq->rwatch->req;
896 2 : unschedule_timeout_watcher(rreq->ctx, rreq);
897 :
898 2 : state = tevent_req_data(req, struct gethostbyname_dns_state);
899 :
900 2 : state->status = status;
901 2 : state->timeouts = timeouts;
902 :
903 : /* If resolv.conf changed during processing of a request we might
904 : * destroy the old channel before the request has a chance to finish.
905 : * We must resend the request in this case */
906 2 : if (state->retrying == 0 && status == ARES_EDESTRUCTION
907 0 : && state->resolv_ctx->channel != NULL) {
908 0 : state->retrying = 1;
909 0 : resolv_gethostbyname_dns_query(req, state);
910 0 : return;
911 : }
912 :
913 2 : if (status == ARES_ENOTFOUND || status == ARES_ENODATA) {
914 : /* Just say we didn't find anything and let the caller decide
915 : * about retrying */
916 0 : tevent_req_error(req, ENOENT);
917 0 : return;
918 : }
919 :
920 2 : if (status != ARES_SUCCESS) {
921 : /* Any other error indicates a server error,
922 : * so don't bother trying again
923 : */
924 0 : tevent_req_error(req, return_code(status));
925 0 : return;
926 : }
927 :
928 2 : ret = resolv_gethostbyname_dns_parse(state, status, abuf, alen);
929 2 : if (ret != EOK) {
930 0 : tevent_req_error(req, ret);
931 0 : return;
932 : }
933 :
934 2 : tevent_req_done(req);
935 : }
936 :
937 : static int
938 2 : resolv_gethostbyname_dns_parse(struct gethostbyname_dns_state *state,
939 : int status, unsigned char *abuf, int alen)
940 : {
941 : struct hostent *hostent;
942 : int naddrttls;
943 : errno_t ret;
944 2 : void *addr = NULL;
945 :
946 2 : naddrttls = DNS_HEADER_ANCOUNT(abuf);
947 :
948 2 : switch (state->family) {
949 : case AF_INET:
950 2 : DEBUG(SSSDBG_TRACE_LIBS, "Parsing an A reply\n");
951 :
952 2 : addr = talloc_array(state, struct ares_addrttl, naddrttls);
953 2 : if (!addr) {
954 0 : ret = ENOMEM;
955 0 : goto fail;
956 : }
957 :
958 2 : status = ares_parse_a_reply(abuf, alen, &hostent,
959 : (struct ares_addrttl *) addr,
960 : &naddrttls);
961 2 : break;
962 : case AF_INET6:
963 0 : DEBUG(SSSDBG_TRACE_LIBS, "Parsing an AAAA reply\n");
964 :
965 0 : addr = talloc_array(state, struct ares_addr6ttl, naddrttls);
966 0 : if (!addr) {
967 0 : ret = ENOMEM;
968 0 : goto fail;
969 : }
970 :
971 0 : status = ares_parse_aaaa_reply(abuf, alen, &hostent,
972 : (struct ares_addr6ttl *) addr,
973 : &naddrttls);
974 0 : break;
975 : default:
976 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Unknown family %d\n", state->family);
977 0 : ret = EAFNOSUPPORT;
978 0 : goto fail;
979 : }
980 :
981 2 : if (hostent != NULL) {
982 2 : state->rhostent = resolv_copy_hostent_ares(state, hostent,
983 : state->family,
984 : addr, naddrttls);
985 2 : ares_free_hostent(hostent);
986 2 : if (state->rhostent == NULL) {
987 0 : ret = ENOMEM;
988 0 : goto fail;
989 : }
990 :
991 : /* The address list is NULL. This is probably a bug in
992 : * c-ares, but we need to handle it gracefully.
993 : */
994 2 : if (state->rhostent->addr_list == NULL) {
995 0 : talloc_zfree(state->rhostent);
996 0 : return ENOENT;
997 : }
998 : }
999 :
1000 2 : talloc_free(addr);
1001 2 : return return_code(status);
1002 :
1003 : fail:
1004 0 : talloc_free(addr);
1005 0 : return ret;
1006 : }
1007 :
1008 : static int
1009 2 : resolv_gethostbyname_dns_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
1010 : int *status, int *timeouts,
1011 : struct resolv_hostent **rhostent)
1012 : {
1013 2 : struct gethostbyname_dns_state *state = tevent_req_data(req,
1014 : struct gethostbyname_dns_state);
1015 :
1016 : /* Fill in even in case of error as status contains the
1017 : * c-ares return code */
1018 2 : if (status) {
1019 2 : *status = state->status;
1020 : }
1021 2 : if (timeouts) {
1022 2 : *timeouts = state->timeouts;
1023 : }
1024 :
1025 2 : TEVENT_REQ_RETURN_ON_ERROR(req);
1026 :
1027 2 : if (rhostent) {
1028 2 : *rhostent = talloc_steal(mem_ctx, state->rhostent);
1029 : }
1030 :
1031 2 : return EOK;
1032 : }
1033 :
1034 : /*******************************************************************
1035 : * Get host by name. *
1036 : *******************************************************************/
1037 :
1038 : struct gethostbyname_state {
1039 : struct resolv_ctx *resolv_ctx;
1040 : struct tevent_context *ev;
1041 :
1042 : /* Part of the query. */
1043 : const char *name;
1044 : int family;
1045 :
1046 : /* In which order to use IPv4, or v6 */
1047 : enum restrict_family family_order;
1048 :
1049 : /* Known hosts databases and index to the current one */
1050 : enum host_database *db;
1051 : int dbi;
1052 :
1053 : /* These are returned by ares. The hostent struct will be freed
1054 : * when the user callback returns. */
1055 : struct resolv_hostent *rhostent;
1056 : int status;
1057 : int timeouts;
1058 : int retrying;
1059 : };
1060 :
1061 : static errno_t
1062 : resolv_gethostbyname_address(TALLOC_CTX *mem_ctx, const char *address,
1063 : struct resolv_hostent **_rhostent);
1064 : static inline int
1065 : resolv_gethostbyname_family_init(enum restrict_family family_order);
1066 : static bool
1067 : resolv_is_address(const char *name);
1068 : static errno_t
1069 : resolv_gethostbyname_step(struct tevent_req *req);
1070 :
1071 : struct tevent_req *
1072 5 : resolv_gethostbyname_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
1073 : struct resolv_ctx *ctx, const char *name,
1074 : enum restrict_family family_order,
1075 : enum host_database *db)
1076 : {
1077 : struct tevent_req *req;
1078 : struct gethostbyname_state *state;
1079 : errno_t ret;
1080 :
1081 5 : if (ctx->channel == NULL) {
1082 0 : DEBUG(SSSDBG_CRIT_FAILURE,
1083 : "Invalid ares channel - this is likely a bug\n");
1084 0 : return NULL;
1085 : }
1086 :
1087 5 : req = tevent_req_create(mem_ctx, &state, struct gethostbyname_state);
1088 5 : if (req == NULL) {
1089 0 : return NULL;
1090 : }
1091 :
1092 5 : state->resolv_ctx = ctx;
1093 5 : state->ev = ev;
1094 5 : state->name = talloc_strdup(state, name);
1095 5 : if (state->name == NULL) {
1096 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_strdup() failed\n");
1097 0 : goto fail;
1098 : }
1099 :
1100 5 : state->rhostent = NULL;
1101 5 : state->status = 0;
1102 5 : state->timeouts = 0;
1103 5 : state->retrying = 0;
1104 5 : state->family_order = family_order;
1105 5 : state->family = resolv_gethostbyname_family_init(state->family_order);
1106 5 : state->db = db;
1107 5 : state->dbi = 0;
1108 :
1109 : /* Do not attempt to resolve IP addresses */
1110 5 : if (resolv_is_address(state->name)) {
1111 2 : ret = resolv_gethostbyname_address(state, state->name,
1112 2 : &state->rhostent);
1113 2 : if (ret != EOK) {
1114 0 : DEBUG(SSSDBG_CRIT_FAILURE,
1115 : "Canot create a fake hostent structure\n");
1116 0 : goto fail;
1117 : }
1118 :
1119 2 : tevent_req_done(req);
1120 2 : tevent_req_post(req, ev);
1121 2 : return req;
1122 : }
1123 :
1124 3 : ret = resolv_gethostbyname_step(req);
1125 3 : if (ret != EOK) {
1126 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Cannot start the resolving\n");
1127 0 : goto fail;
1128 : }
1129 :
1130 3 : return req;
1131 :
1132 : fail:
1133 0 : talloc_zfree(req);
1134 0 : return NULL;
1135 : }
1136 :
1137 : static bool
1138 5 : resolv_is_address(const char *name)
1139 : {
1140 : struct addrinfo hints;
1141 5 : struct addrinfo *res = NULL;
1142 : int ret;
1143 :
1144 5 : memset((void *) &hints, 0, sizeof(struct addrinfo));
1145 5 : hints.ai_family = AF_UNSPEC;
1146 5 : hints.ai_flags = AI_NUMERICHOST; /* No network lookups */
1147 :
1148 5 : ret = getaddrinfo(name, NULL, &hints, &res);
1149 5 : if (ret != 0) {
1150 3 : if (ret == -2) {
1151 3 : DEBUG(SSSDBG_TRACE_ALL,
1152 : "[%s] does not look like an IP address\n", name);
1153 : } else {
1154 0 : DEBUG(SSSDBG_OP_FAILURE, "getaddrinfo failed [%d]: %s\n",
1155 : ret, gai_strerror(ret));
1156 : }
1157 : } else { /* ret == 0 */
1158 2 : freeaddrinfo(res);
1159 : }
1160 :
1161 5 : return ret == 0;
1162 : }
1163 :
1164 : static errno_t
1165 2 : resolv_gethostbyname_address(TALLOC_CTX *mem_ctx, const char *address,
1166 : struct resolv_hostent **_rhostent)
1167 : {
1168 : struct resolv_hostent *rhostent;
1169 : TALLOC_CTX *tmp_ctx;
1170 : errno_t ret;
1171 : int family;
1172 :
1173 2 : tmp_ctx = talloc_new(NULL);
1174 2 : if (!tmp_ctx) return ENOMEM;
1175 :
1176 2 : rhostent = talloc_zero(tmp_ctx, struct resolv_hostent);
1177 2 : if (!rhostent) {
1178 0 : ret = ENOMEM;
1179 0 : goto done;
1180 : }
1181 :
1182 2 : rhostent->name = talloc_strdup(rhostent, address);
1183 2 : rhostent->addr_list = talloc_array(rhostent, struct resolv_addr *, 2);
1184 :
1185 4 : if (!rhostent->name ||
1186 2 : !rhostent->addr_list) {
1187 0 : ret = ENOMEM;
1188 0 : goto done;
1189 : }
1190 :
1191 2 : rhostent->addr_list[0] = talloc_zero(rhostent->addr_list,
1192 : struct resolv_addr);
1193 2 : if (!rhostent->addr_list[0]) {
1194 0 : ret = ENOMEM;
1195 0 : goto done;
1196 : }
1197 2 : rhostent->addr_list[0]->ipaddr = talloc_array(rhostent->addr_list[0],
1198 : uint8_t,
1199 : sizeof(struct in6_addr));
1200 2 : if (!rhostent->addr_list[0]->ipaddr) {
1201 0 : ret = ENOMEM;
1202 0 : goto done;
1203 : }
1204 :
1205 2 : family = AF_INET;
1206 2 : ret = inet_pton(family, address,
1207 2 : rhostent->addr_list[0]->ipaddr);
1208 2 : if (ret != 1) {
1209 0 : family = AF_INET6;
1210 0 : ret = inet_pton(family, address,
1211 0 : rhostent->addr_list[0]->ipaddr);
1212 0 : if (ret != 1) {
1213 0 : DEBUG(SSSDBG_CRIT_FAILURE,
1214 : "Could not parse address as neither v4 nor v6\n");
1215 0 : ret = EINVAL;
1216 0 : goto done;
1217 : }
1218 : }
1219 :
1220 2 : rhostent->addr_list[0]->ttl = RESOLV_DEFAULT_TTL;
1221 2 : rhostent->addr_list[1] = NULL;
1222 2 : rhostent->family = family;
1223 2 : rhostent->aliases = NULL;
1224 :
1225 2 : *_rhostent = talloc_move(mem_ctx, &rhostent);
1226 2 : ret = EOK;
1227 : done:
1228 2 : talloc_free(tmp_ctx);
1229 2 : return ret;
1230 : }
1231 :
1232 : static inline int
1233 7 : resolv_gethostbyname_family_init(enum restrict_family family_order)
1234 : {
1235 7 : switch(family_order) {
1236 : case IPV4_ONLY:
1237 : case IPV4_FIRST:
1238 7 : return AF_INET;
1239 : case IPV6_ONLY:
1240 : case IPV6_FIRST:
1241 0 : return AF_INET6;
1242 : }
1243 :
1244 0 : DEBUG(SSSDBG_CRIT_FAILURE,
1245 : "Unknown address family order %d\n", family_order);
1246 0 : return -1;
1247 : }
1248 :
1249 : static int
1250 4 : resolv_gethostbyname_next(struct gethostbyname_state *state)
1251 : {
1252 8 : if (state->family_order == IPV4_FIRST &&
1253 4 : state->family == AF_INET) {
1254 2 : state->family = AF_INET6;
1255 2 : return EOK;
1256 2 : } else if (state->family_order == IPV6_FIRST &&
1257 0 : state->family == AF_INET6) {
1258 0 : state->family = AF_INET;
1259 0 : return EOK;
1260 : } else {
1261 : /* No more address families for this DB, check if
1262 : * there is another DB to try */
1263 2 : DEBUG(SSSDBG_FUNC_DATA, "No more address families to retry\n");
1264 2 : state->dbi++;
1265 2 : if (state->db[state->dbi] != DB_SENTINEL) {
1266 2 : state->family = resolv_gethostbyname_family_init(
1267 : state->family_order);
1268 2 : return EOK;
1269 : }
1270 : }
1271 :
1272 0 : DEBUG(SSSDBG_CONF_SETTINGS, "No more hosts databases to retry\n");
1273 0 : return ENOENT;
1274 : }
1275 :
1276 : static void
1277 : resolv_gethostbyname_done(struct tevent_req *subreq);
1278 :
1279 : static errno_t
1280 7 : resolv_gethostbyname_step(struct tevent_req *req)
1281 : {
1282 7 : struct gethostbyname_state *state = tevent_req_data(req,
1283 : struct gethostbyname_state);
1284 : struct tevent_req *subreq;
1285 :
1286 7 : switch(state->db[state->dbi]) {
1287 : case DB_FILES:
1288 5 : DEBUG(SSSDBG_TRACE_INTERNAL, "Querying files\n");
1289 5 : subreq = resolv_gethostbyname_files_send(state, state->ev,
1290 : state->resolv_ctx,
1291 : state->name,
1292 : state->family);
1293 5 : break;
1294 : case DB_DNS:
1295 2 : DEBUG(SSSDBG_TRACE_INTERNAL, "Querying DNS\n");
1296 2 : subreq = resolv_gethostbyname_dns_send(state, state->ev,
1297 : state->resolv_ctx,
1298 : state->name,
1299 : state->family);
1300 2 : break;
1301 : default:
1302 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Invalid hosts database\n");
1303 0 : return EINVAL;
1304 : }
1305 :
1306 7 : if (subreq == NULL) {
1307 0 : return ENOMEM;
1308 : }
1309 :
1310 7 : tevent_req_set_callback(subreq, resolv_gethostbyname_done, req);
1311 7 : return EOK;
1312 : }
1313 :
1314 : static void
1315 7 : resolv_gethostbyname_done(struct tevent_req *subreq)
1316 : {
1317 7 : struct tevent_req *req = tevent_req_callback_data(subreq,
1318 : struct tevent_req);
1319 7 : struct gethostbyname_state *state = tevent_req_data(req,
1320 : struct gethostbyname_state);
1321 : errno_t ret;
1322 :
1323 7 : switch(state->db[state->dbi]) {
1324 : case DB_FILES:
1325 5 : ret = resolv_gethostbyname_files_recv(subreq, state,
1326 : &state->status,
1327 : &state->rhostent);
1328 : /* files is synchronous, there can be no timeouts */
1329 5 : state->timeouts = 0;
1330 5 : break;
1331 : case DB_DNS:
1332 2 : ret = resolv_gethostbyname_dns_recv(subreq, state,
1333 : &state->status, &state->timeouts,
1334 : &state->rhostent);
1335 2 : break;
1336 : default:
1337 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Invalid hosts database\n");
1338 0 : tevent_req_error(req, EINVAL);
1339 0 : return;
1340 : }
1341 :
1342 7 : talloc_zfree(subreq);
1343 :
1344 7 : if (ret == ENOENT) {
1345 4 : ret = resolv_gethostbyname_next(state);
1346 4 : if (ret == EOK) {
1347 4 : ret = resolv_gethostbyname_step(req);
1348 4 : if (ret != EOK) {
1349 0 : tevent_req_error(req, ret);
1350 : }
1351 4 : return;
1352 : }
1353 :
1354 : /* No more databases and/or address families */
1355 0 : tevent_req_error(req, ENOENT);
1356 0 : return;
1357 3 : } else if (ret == ETIMEDOUT) {
1358 : /* In case we killed the request before c-ares answered */
1359 0 : state->status = ARES_ETIMEOUT;
1360 : }
1361 :
1362 3 : if (ret != EOK) {
1363 0 : DEBUG(SSSDBG_OP_FAILURE, "querying hosts database failed [%d]: %s\n",
1364 : ret, strerror(ret));
1365 0 : tevent_req_error(req, ret);
1366 0 : return;
1367 : }
1368 :
1369 3 : tevent_req_done(req);
1370 : }
1371 :
1372 : int
1373 3 : resolv_gethostbyname_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx,
1374 : int *status, int *timeouts,
1375 : struct resolv_hostent **rhostent)
1376 : {
1377 3 : struct gethostbyname_state *state = tevent_req_data(req, struct gethostbyname_state);
1378 :
1379 : /* Fill in even in case of error as status contains the
1380 : * c-ares return code */
1381 3 : if (status) {
1382 3 : *status = state->status;
1383 : }
1384 3 : if (timeouts) {
1385 0 : *timeouts = state->timeouts;
1386 : }
1387 3 : if (rhostent) {
1388 3 : *rhostent = talloc_steal(mem_ctx, state->rhostent);
1389 : }
1390 :
1391 3 : TEVENT_REQ_RETURN_ON_ERROR(req);
1392 :
1393 3 : return EOK;
1394 : }
1395 :
1396 : char *
1397 2 : resolv_get_string_address_index(TALLOC_CTX *mem_ctx,
1398 : struct resolv_hostent *hostent,
1399 : unsigned int addrindex)
1400 : {
1401 : char *address;
1402 :
1403 2 : if (!hostent) return NULL;
1404 :
1405 2 : address = talloc_zero_size(mem_ctx, 128);
1406 2 : if (address == NULL) {
1407 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero failed.\n");
1408 0 : return NULL;
1409 : }
1410 :
1411 2 : errno = 0;
1412 2 : if (inet_ntop(hostent->family, hostent->addr_list[addrindex]->ipaddr,
1413 : address, 128) == NULL) {
1414 0 : DEBUG(SSSDBG_CRIT_FAILURE,
1415 : "inet_ntop failed [%d][%s].\n", errno, strerror(errno));
1416 0 : talloc_free(address);
1417 0 : return NULL;
1418 : }
1419 :
1420 2 : return address;
1421 : }
1422 :
1423 : char *
1424 2 : resolv_get_string_ptr_address(TALLOC_CTX *mem_ctx,
1425 : int family, uint8_t *address)
1426 : {
1427 : char *straddr;
1428 :
1429 2 : if (family == AF_INET6) {
1430 : int i;
1431 : char hexbyte[3];
1432 :
1433 1 : straddr = talloc_strdup(mem_ctx, "\0");
1434 1 : if (!straddr) {
1435 0 : return NULL;
1436 : }
1437 :
1438 17 : for (i = 15; i >= 0; i--) {
1439 16 : snprintf(hexbyte, 3, "%02x", address[i]);
1440 32 : straddr = talloc_asprintf_append(straddr, "%c.%c.",
1441 32 : hexbyte[1], hexbyte[0]);
1442 : }
1443 1 : straddr = talloc_asprintf_append(straddr, "ip6.arpa.");
1444 1 : } else if (family == AF_INET) {
1445 4 : straddr = talloc_asprintf(mem_ctx,
1446 : "%u.%u.%u.%u.in-addr.arpa.",
1447 1 : (address[3]),
1448 1 : (address[2]),
1449 1 : (address[1]),
1450 1 : (address[0]));
1451 : } else {
1452 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Unknown address family\n");
1453 0 : return NULL;
1454 : }
1455 :
1456 2 : return straddr;
1457 : }
1458 :
1459 : struct sockaddr_storage *
1460 0 : resolv_get_sockaddr_address_index(TALLOC_CTX *mem_ctx,
1461 : struct resolv_hostent *hostent,
1462 : int port, int addrindex)
1463 : {
1464 : struct sockaddr_storage *sockaddr;
1465 :
1466 0 : if (!hostent) return NULL;
1467 :
1468 0 : sockaddr = talloc_zero(mem_ctx, struct sockaddr_storage);
1469 0 : if (sockaddr == NULL) {
1470 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero failed.\n");
1471 0 : return NULL;
1472 : }
1473 :
1474 0 : switch(hostent->family) {
1475 : case AF_INET:
1476 0 : sockaddr->ss_family = AF_INET;
1477 0 : memcpy(&((struct sockaddr_in *) sockaddr)->sin_addr,
1478 0 : hostent->addr_list[addrindex]->ipaddr,
1479 : sizeof(struct in_addr));
1480 0 : ((struct sockaddr_in *) sockaddr)->sin_port = (in_port_t) htons(port);
1481 :
1482 0 : break;
1483 : case AF_INET6:
1484 0 : sockaddr->ss_family = AF_INET6;
1485 0 : memcpy(&((struct sockaddr_in6 *) sockaddr)->sin6_addr,
1486 0 : hostent->addr_list[addrindex]->ipaddr,
1487 : sizeof(struct in6_addr));
1488 0 : ((struct sockaddr_in6 *) sockaddr)->sin6_port = (in_port_t) htons(port);
1489 0 : break;
1490 : default:
1491 0 : DEBUG(SSSDBG_CRIT_FAILURE,
1492 : "Unknown address family %d\n", hostent->family);
1493 0 : return NULL;
1494 : }
1495 :
1496 0 : return sockaddr;
1497 : }
1498 :
1499 : /*
1500 : * A simple helper function that will take an array of struct ares_srv_reply that
1501 : * was allocated by malloc() in c-ares and copies it using talloc. The old one
1502 : * is freed and the talloc one is put into 'reply_list' instead.
1503 : */
1504 : static int
1505 1 : rewrite_talloc_srv_reply(TALLOC_CTX *mem_ctx, struct ares_srv_reply **reply_list)
1506 : {
1507 1 : struct ares_srv_reply *ptr = NULL;
1508 1 : struct ares_srv_reply *new_list = NULL;
1509 1 : struct ares_srv_reply *old_list = *reply_list;
1510 :
1511 : /* Nothing to do, but not an error */
1512 1 : if (!old_list) {
1513 0 : return EOK;
1514 : }
1515 :
1516 : /* Copy the linked list */
1517 4 : while (old_list) {
1518 : /* Special case for the first node */
1519 2 : if (!new_list) {
1520 1 : new_list = talloc_zero(mem_ctx, struct ares_srv_reply);
1521 1 : if (new_list == NULL) {
1522 0 : ares_free_data(*reply_list);
1523 0 : return ENOMEM;
1524 : }
1525 1 : ptr = new_list;
1526 : } else {
1527 1 : ptr->next = talloc_zero(new_list, struct ares_srv_reply);
1528 1 : if (ptr->next == NULL) {
1529 0 : ares_free_data(*reply_list);
1530 0 : talloc_free(new_list);
1531 0 : return ENOMEM;
1532 : }
1533 1 : ptr = ptr->next;
1534 : }
1535 :
1536 2 : ptr->weight = old_list->weight;
1537 2 : ptr->priority = old_list->priority;
1538 2 : ptr->port = old_list->port;
1539 2 : ptr->host = talloc_strdup(ptr, old_list->host);
1540 2 : if (ptr->host == NULL) {
1541 0 : ares_free_data(*reply_list);
1542 0 : talloc_free(new_list);
1543 0 : return ENOMEM;
1544 : }
1545 :
1546 2 : old_list = old_list->next;
1547 : }
1548 :
1549 : /* Free the old one (uses malloc). */
1550 1 : ares_free_data(*reply_list);
1551 :
1552 : /* And now put our own new_list in place. */
1553 1 : *reply_list = new_list;
1554 :
1555 1 : return EOK;
1556 : }
1557 :
1558 : /*******************************************************************
1559 : * Get SRV record *
1560 : *******************************************************************/
1561 :
1562 : struct getsrv_state {
1563 : struct tevent_context *ev;
1564 : struct resolv_ctx *resolv_ctx;
1565 : /* the SRV query - for example _ldap._tcp.example.com */
1566 : const char *query;
1567 :
1568 : /* parsed data returned by ares */
1569 : struct ares_srv_reply *reply_list;
1570 : uint32_t ttl;
1571 : int status;
1572 : int timeouts;
1573 : int retrying;
1574 : };
1575 :
1576 : static void
1577 : ares_getsrv_wakeup(struct tevent_req *subreq);
1578 : static void
1579 : resolv_getsrv_query(struct tevent_req *req,
1580 : struct getsrv_state *state);
1581 :
1582 : struct tevent_req *
1583 1 : resolv_getsrv_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
1584 : struct resolv_ctx *ctx, const char *query)
1585 : {
1586 : struct tevent_req *req, *subreq;
1587 : struct getsrv_state *state;
1588 1 : struct timeval tv = { 0, 0 };
1589 :
1590 1 : DEBUG(SSSDBG_CONF_SETTINGS,
1591 : "Trying to resolve SRV record of '%s'\n", query);
1592 :
1593 1 : if (ctx->channel == NULL) {
1594 0 : DEBUG(SSSDBG_CRIT_FAILURE,
1595 : "Invalid ares channel - this is likely a bug\n");
1596 0 : return NULL;
1597 : }
1598 :
1599 1 : req = tevent_req_create(mem_ctx, &state, struct getsrv_state);
1600 1 : if (req == NULL)
1601 0 : return NULL;
1602 :
1603 1 : state->resolv_ctx = ctx;
1604 1 : state->query = query;
1605 1 : state->reply_list = NULL;
1606 1 : state->ttl = 0;
1607 1 : state->status = 0;
1608 1 : state->timeouts = 0;
1609 1 : state->retrying = 0;
1610 1 : state->ev = ev;
1611 :
1612 1 : subreq = tevent_wakeup_send(req, ev, tv);
1613 1 : if (subreq == NULL) {
1614 0 : DEBUG(SSSDBG_CRIT_FAILURE,
1615 : "Failed to add critical timer to run next operation!\n");
1616 0 : talloc_zfree(req);
1617 0 : return NULL;
1618 : }
1619 1 : tevent_req_set_callback(subreq, ares_getsrv_wakeup, req);
1620 :
1621 1 : return req;
1622 : }
1623 :
1624 : /*
1625 : * Implemented based on http://tools.ietf.org/html/rfc2181#section-5
1626 : *
1627 : * Especially:
1628 : * 5.2. TTLs of RRs in an RRSet
1629 : * Consequently the use of differing TTLs in an RRSet is hereby
1630 : * deprecated, the TTLs of all RRs in an RRSet must be the same.
1631 : * ...
1632 : * Should an authoritative source send such a malformed RRSet, the
1633 : * client should treat the RRs for all purposes as if all TTLs in the
1634 : * RRSet had been set to the value of the lowest TTL in the RRSet.
1635 : *
1636 : * On success, returns true and sets the TTL in the _ttl parameter. On
1637 : * failure, returns false and _ttl is undefined.
1638 : */
1639 : static bool
1640 1 : resolv_get_ttl(const unsigned char *abuf, const int alen, uint32_t *_ttl)
1641 : {
1642 : const unsigned char *aptr;
1643 : int ret;
1644 1 : char *name = NULL;
1645 : long len;
1646 1 : uint32_t ttl = 0;
1647 : uint32_t rr_ttl;
1648 : unsigned int rr_len;
1649 : unsigned int ancount;
1650 : unsigned int i;
1651 :
1652 : /* Read the number of RRs and then skip past the header */
1653 1 : if (alen < NS_HFIXEDSZ) {
1654 0 : return false;
1655 : }
1656 :
1657 1 : ancount = DNS_HEADER_ANCOUNT(abuf);
1658 1 : if (ancount == 0) {
1659 0 : return false;
1660 : }
1661 :
1662 1 : aptr = abuf + NS_HFIXEDSZ;
1663 :
1664 : /* We only care about len from the question data,
1665 : * so that we can move past hostname */
1666 1 : ret = ares_expand_name(aptr, abuf, alen, &name, &len);
1667 1 : ares_free_string(name);
1668 1 : if (ret != ARES_SUCCESS) {
1669 0 : return false;
1670 : }
1671 :
1672 : /* Skip past the question */
1673 1 : aptr += len + NS_QFIXEDSZ;
1674 1 : if (aptr > abuf + alen) {
1675 0 : return false;
1676 : }
1677 :
1678 : /* Examine each RR in turn and read the lowest TTL */
1679 3 : for (i = 0; i < ancount; i++) {
1680 : /* Decode the RR up to the data field. */
1681 2 : ret = ares_expand_name(aptr, abuf, alen, &name, &len);
1682 2 : ares_free_string(name);
1683 2 : if (ret != ARES_SUCCESS) {
1684 0 : return false;
1685 : }
1686 :
1687 2 : aptr += len;
1688 2 : if (aptr + NS_RRFIXEDSZ > abuf + alen) {
1689 0 : return false;
1690 : }
1691 :
1692 2 : rr_len = DNS_RR_LEN(aptr);
1693 2 : rr_ttl = DNS_RR_TTL(aptr);
1694 2 : if (aptr + rr_len > abuf + alen) {
1695 0 : return false;
1696 : }
1697 2 : aptr += NS_RRFIXEDSZ + rr_len;
1698 :
1699 2 : if (ttl > 0) {
1700 1 : ttl = MIN(ttl, rr_ttl);
1701 : } else {
1702 1 : ttl = rr_ttl; /* special-case for first TTL */
1703 : }
1704 : }
1705 :
1706 1 : *_ttl = ttl;
1707 1 : return true;
1708 : }
1709 :
1710 : static void
1711 1 : resolv_getsrv_done(void *arg, int status, int timeouts, unsigned char *abuf, int alen)
1712 : {
1713 1 : struct resolv_request *rreq = talloc_get_type(arg, struct resolv_request);
1714 : struct tevent_req *req;
1715 : struct getsrv_state *state;
1716 : int ret;
1717 : bool ok;
1718 : struct ares_srv_reply *reply_list;
1719 :
1720 1 : if (rreq->rwatch == NULL) {
1721 : /* The tevent request was cancelled while the ares call was still in
1722 : * progress so nobody cares about the result now. Quit. */
1723 0 : unschedule_timeout_watcher(rreq->ctx, rreq);
1724 0 : return;
1725 : }
1726 :
1727 1 : req = rreq->rwatch->req;
1728 1 : unschedule_timeout_watcher(rreq->ctx, rreq);
1729 1 : state = tevent_req_data(req, struct getsrv_state);
1730 :
1731 1 : if (state->retrying == 0 && status == ARES_EDESTRUCTION
1732 0 : && state->resolv_ctx->channel != NULL) {
1733 0 : state->retrying = 1;
1734 0 : resolv_getsrv_query(req, state);
1735 0 : return;
1736 : }
1737 :
1738 1 : state->status = status;
1739 1 : state->timeouts = timeouts;
1740 :
1741 1 : if (status != ARES_SUCCESS) {
1742 0 : ret = return_code(status);
1743 0 : goto fail;
1744 : }
1745 :
1746 1 : ret = ares_parse_srv_reply(abuf, alen, &reply_list);
1747 1 : if (ret != ARES_SUCCESS) {
1748 0 : DEBUG(SSSDBG_OP_FAILURE,
1749 : "SRV record parsing failed: %d: %s\n", ret, ares_strerror(ret));
1750 0 : ret = return_code(ret);
1751 0 : goto fail;
1752 : }
1753 1 : ret = rewrite_talloc_srv_reply(req, &reply_list);
1754 1 : if (ret != EOK) {
1755 0 : goto fail;
1756 : }
1757 1 : state->reply_list = reply_list;
1758 1 : ok = resolv_get_ttl(abuf, alen, &state->ttl);
1759 1 : if (ok == false) {
1760 0 : DEBUG(SSSDBG_MINOR_FAILURE, "Could not read TTL, using the default..\n");
1761 0 : state->ttl = RESOLV_DEFAULT_SRV_TTL;
1762 : }
1763 1 : DEBUG(SSSDBG_TRACE_LIBS, "Using TTL [%"PRIu32"]\n", state->ttl);
1764 :
1765 1 : tevent_req_done(req);
1766 1 : return;
1767 :
1768 : fail:
1769 0 : state->reply_list = NULL;
1770 0 : tevent_req_error(req, ret);
1771 : }
1772 :
1773 : int
1774 1 : resolv_getsrv_recv(TALLOC_CTX *mem_ctx, struct tevent_req *req, int *status,
1775 : int *timeouts, struct ares_srv_reply **reply_list,
1776 : uint32_t *ttl)
1777 : {
1778 1 : struct getsrv_state *state = tevent_req_data(req, struct getsrv_state);
1779 :
1780 1 : if (status)
1781 1 : *status = state->status;
1782 1 : if (timeouts)
1783 0 : *timeouts = state->timeouts;
1784 1 : if (reply_list)
1785 1 : *reply_list = talloc_steal(mem_ctx, state->reply_list);
1786 1 : if (ttl) {
1787 1 : *ttl = state->ttl;
1788 : }
1789 :
1790 1 : TEVENT_REQ_RETURN_ON_ERROR(req);
1791 :
1792 1 : return EOK;
1793 : }
1794 :
1795 : static void
1796 1 : ares_getsrv_wakeup(struct tevent_req *subreq)
1797 : {
1798 1 : struct tevent_req *req = tevent_req_callback_data(subreq,
1799 : struct tevent_req);
1800 1 : struct getsrv_state *state = tevent_req_data(req,
1801 : struct getsrv_state);
1802 :
1803 1 : if (!tevent_wakeup_recv(subreq)) {
1804 0 : return;
1805 : }
1806 1 : talloc_zfree(subreq);
1807 :
1808 1 : if (state->resolv_ctx->channel == NULL) {
1809 0 : DEBUG(SSSDBG_CRIT_FAILURE,
1810 : "Invalid ares channel - this is likely a bug\n");
1811 0 : tevent_req_error(req, EIO);
1812 0 : return;
1813 : }
1814 :
1815 1 : return resolv_getsrv_query(req, state);
1816 : }
1817 :
1818 : static void
1819 1 : resolv_getsrv_query(struct tevent_req *req,
1820 : struct getsrv_state *state)
1821 : {
1822 : struct resolv_request *rreq;
1823 :
1824 1 : rreq = schedule_timeout_watcher(state->ev, state->resolv_ctx, req);
1825 1 : if (!rreq) {
1826 0 : tevent_req_error(req, ENOMEM);
1827 0 : return;
1828 : }
1829 :
1830 1 : ares_query(state->resolv_ctx->channel, state->query,
1831 : ns_c_in, ns_t_srv, resolv_getsrv_done, rreq);
1832 : }
1833 :
1834 : /* TXT parsing is not used anywhere in the code yet, so we disable it
1835 : * for now
1836 : */
1837 : #ifdef BUILD_TXT
1838 :
1839 : /*
1840 : * A simple helper function that will take an array of struct txt_reply that
1841 : * was allocated by malloc() in c-ares and copies it using talloc. The old one
1842 : * is freed and the talloc one is put into 'reply_list' instead.
1843 : */
1844 : static int
1845 0 : rewrite_talloc_txt_reply(TALLOC_CTX *mem_ctx, struct ares_txt_reply **reply_list)
1846 : {
1847 0 : struct ares_txt_reply *ptr = NULL;
1848 0 : struct ares_txt_reply *new_list = NULL;
1849 0 : struct ares_txt_reply *old_list = *reply_list;
1850 :
1851 : /* Nothing to do, but not an error */
1852 0 : if (!old_list) {
1853 0 : return EOK;
1854 : }
1855 :
1856 : /* Copy the linked list */
1857 0 : while (old_list) {
1858 :
1859 : /* Special case for the first node */
1860 0 : if (!new_list) {
1861 0 : new_list = talloc_zero(mem_ctx, struct ares_txt_reply);
1862 0 : if (new_list == NULL) {
1863 0 : ares_free_data(*reply_list);
1864 0 : talloc_free(new_list);
1865 0 : return ENOMEM;
1866 : }
1867 0 : ptr = new_list;
1868 : } else {
1869 0 : ptr->next = talloc_zero(new_list, struct ares_txt_reply);
1870 0 : if (ptr->next == NULL) {
1871 0 : ares_free_data(*reply_list);
1872 0 : talloc_free(new_list);
1873 0 : return ENOMEM;
1874 : }
1875 0 : ptr = ptr->next;
1876 : }
1877 :
1878 0 : ptr->length = old_list->length;
1879 0 : ptr->txt = talloc_memdup(ptr, old_list->txt,
1880 : old_list->length);
1881 0 : if (ptr->txt == NULL) {
1882 0 : ares_free_data(*reply_list);
1883 0 : talloc_free(new_list);
1884 0 : return ENOMEM;
1885 : }
1886 :
1887 0 : old_list = old_list->next;
1888 : }
1889 :
1890 0 : ares_free_data(*reply_list);
1891 :
1892 : /* And now put our own new_list in place. */
1893 0 : *reply_list = new_list;
1894 :
1895 0 : return EOK;
1896 : }
1897 :
1898 : /*******************************************************************
1899 : * Get TXT record *
1900 : *******************************************************************/
1901 :
1902 : struct gettxt_state {
1903 : struct tevent_context *ev;
1904 : struct resolv_ctx *resolv_ctx;
1905 : /* the TXT query */
1906 : const char *query;
1907 :
1908 : /* parsed data returned by ares */
1909 : struct ares_txt_reply *reply_list;
1910 : int status;
1911 : int timeouts;
1912 : int retrying;
1913 : };
1914 :
1915 : static void
1916 : ares_gettxt_wakeup(struct tevent_req *subreq);
1917 : static void
1918 : resolv_gettxt_query(struct tevent_req *req,
1919 : struct gettxt_state *state);
1920 :
1921 : struct tevent_req *
1922 0 : resolv_gettxt_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev,
1923 : struct resolv_ctx *ctx, const char *query)
1924 : {
1925 : struct tevent_req *req, *subreq;
1926 : struct gettxt_state *state;
1927 0 : struct timeval tv = { 0, 0 };
1928 :
1929 0 : DEBUG(SSSDBG_CONF_SETTINGS,
1930 : "Trying to resolve TXT record of '%s'\n", query);
1931 :
1932 0 : if (ctx->channel == NULL) {
1933 0 : DEBUG(SSSDBG_CRIT_FAILURE,
1934 : "Invalid ares channel - this is likely a bug\n");
1935 0 : return NULL;
1936 : }
1937 :
1938 0 : req = tevent_req_create(mem_ctx, &state, struct gettxt_state);
1939 0 : if (req == NULL)
1940 0 : return NULL;
1941 :
1942 0 : state->resolv_ctx = ctx;
1943 0 : state->query = query;
1944 0 : state->reply_list = NULL;
1945 0 : state->status = 0;
1946 0 : state->timeouts = 0;
1947 0 : state->retrying = 0;
1948 0 : state->ev = ev;
1949 :
1950 0 : subreq = tevent_wakeup_send(req, ev, tv);
1951 0 : if (subreq == NULL) {
1952 0 : DEBUG(SSSDBG_CRIT_FAILURE,
1953 : "Failed to add critical timer to run next operation!\n");
1954 0 : talloc_zfree(req);
1955 0 : return NULL;
1956 : }
1957 0 : tevent_req_set_callback(subreq, ares_gettxt_wakeup, req);
1958 :
1959 0 : return req;
1960 : }
1961 :
1962 : static void
1963 0 : resolv_gettxt_done(void *arg, int status, int timeouts, unsigned char *abuf, int alen)
1964 : {
1965 0 : struct resolv_request *rreq = talloc_get_type(arg, struct resolv_request);
1966 : struct tevent_req *req;
1967 : struct gettxt_state *state;
1968 : int ret;
1969 : struct ares_txt_reply *reply_list;
1970 :
1971 0 : if (rreq->rwatch == NULL) {
1972 : /* The tevent request was cancelled while the ares call was still in
1973 : * progress so nobody cares about the result now. Quit. */
1974 0 : unschedule_timeout_watcher(rreq->ctx, rreq);
1975 0 : return;
1976 : }
1977 :
1978 0 : req = rreq->rwatch->req;
1979 0 : unschedule_timeout_watcher(rreq->ctx, rreq);
1980 0 : state = tevent_req_data(req, struct gettxt_state);
1981 :
1982 0 : if (state->retrying == 0 && status == ARES_EDESTRUCTION
1983 0 : && state->resolv_ctx->channel != NULL) {
1984 0 : state->retrying = 1;
1985 0 : ares_query(state->resolv_ctx->channel, state->query,
1986 : ns_c_in, ns_t_txt, resolv_gettxt_done, req);
1987 0 : return;
1988 : }
1989 :
1990 0 : state->status = status;
1991 0 : state->timeouts = timeouts;
1992 :
1993 0 : if (status != ARES_SUCCESS) {
1994 0 : ret = return_code(status);
1995 0 : goto fail;
1996 : }
1997 :
1998 0 : ret = ares_parse_txt_reply(abuf, alen, &reply_list);
1999 0 : if (status != ARES_SUCCESS) {
2000 0 : DEBUG(SSSDBG_OP_FAILURE,
2001 : "TXT record parsing failed: %d: %s\n", ret, ares_strerror(ret));
2002 0 : ret = return_code(ret);
2003 0 : goto fail;
2004 : }
2005 0 : ret = rewrite_talloc_txt_reply(req, &reply_list);
2006 0 : if (ret != EOK) {
2007 0 : goto fail;
2008 : }
2009 0 : state->reply_list = reply_list;
2010 :
2011 0 : tevent_req_done(req);
2012 0 : return;
2013 :
2014 : fail:
2015 0 : state->reply_list = NULL;
2016 0 : tevent_req_error(req, ret);
2017 : }
2018 :
2019 : int
2020 0 : resolv_gettxt_recv(TALLOC_CTX *mem_ctx, struct tevent_req *req, int *status,
2021 : int *timeouts, struct ares_txt_reply **reply_list)
2022 : {
2023 0 : struct gettxt_state *state = tevent_req_data(req, struct gettxt_state);
2024 :
2025 0 : if (status)
2026 0 : *status = state->status;
2027 0 : if (timeouts)
2028 0 : *timeouts = state->timeouts;
2029 0 : if (reply_list)
2030 0 : *reply_list = talloc_steal(mem_ctx, state->reply_list);
2031 :
2032 0 : TEVENT_REQ_RETURN_ON_ERROR(req);
2033 :
2034 0 : return EOK;
2035 : }
2036 :
2037 : static void
2038 0 : ares_gettxt_wakeup(struct tevent_req *subreq)
2039 : {
2040 0 : struct tevent_req *req = tevent_req_callback_data(subreq,
2041 : struct tevent_req);
2042 0 : struct gettxt_state *state = tevent_req_data(req,
2043 : struct gettxt_state);
2044 :
2045 0 : if (!tevent_wakeup_recv(subreq)) {
2046 0 : return;
2047 : }
2048 0 : talloc_zfree(subreq);
2049 :
2050 0 : if (state->resolv_ctx->channel == NULL) {
2051 0 : DEBUG(SSSDBG_CRIT_FAILURE,
2052 : "Invalid ares channel - this is likely a bug\n");
2053 0 : tevent_req_error(req, EIO);
2054 0 : return;
2055 : }
2056 :
2057 0 : return resolv_gettxt_query(req, state);
2058 : }
2059 :
2060 : static void
2061 0 : resolv_gettxt_query(struct tevent_req *req,
2062 : struct gettxt_state *state)
2063 : {
2064 : struct resolv_request *rreq;
2065 :
2066 0 : rreq = schedule_timeout_watcher(state->ev, state->resolv_ctx, req);
2067 0 : if (!rreq) {
2068 0 : tevent_req_error(req, ENOMEM);
2069 0 : return;
2070 : }
2071 :
2072 0 : ares_query(state->resolv_ctx->channel, state->query,
2073 : ns_c_in, ns_t_txt, resolv_gettxt_done, rreq);
2074 : }
2075 :
2076 : #endif
2077 :
2078 9 : static struct ares_srv_reply *split_reply_list(struct ares_srv_reply *list)
2079 : {
2080 : struct ares_srv_reply *single_step, *double_step, *prev;
2081 :
2082 9 : if (!list) {
2083 0 : return NULL;
2084 : }
2085 :
2086 9 : prev = list;
2087 9 : single_step = list->next;
2088 9 : double_step = single_step->next;
2089 :
2090 20 : while (double_step && double_step->next) {
2091 2 : prev = single_step;
2092 2 : single_step = single_step->next;
2093 2 : double_step = double_step->next->next;
2094 : }
2095 :
2096 9 : prev->next = NULL;
2097 9 : return single_step;
2098 : }
2099 :
2100 9 : static struct ares_srv_reply *merge_reply_list(struct ares_srv_reply *left,
2101 : struct ares_srv_reply *right)
2102 : {
2103 : struct ares_srv_reply *l, *r;
2104 : struct ares_srv_reply *res, *res_start;
2105 :
2106 9 : if (!left)
2107 0 : return right;
2108 9 : if (!right)
2109 0 : return left;
2110 :
2111 9 : if (left->priority < right->priority) {
2112 3 : res_start = left;
2113 3 : l = left->next;
2114 3 : r = right;
2115 : } else {
2116 6 : res_start = right;
2117 6 : l = left;
2118 6 : r = right->next;
2119 : }
2120 :
2121 9 : res = res_start;
2122 :
2123 24 : while(l && r) {
2124 6 : if (l->priority < r->priority) {
2125 4 : res->next = l;
2126 4 : res = l;
2127 4 : l = l->next;
2128 : } else {
2129 2 : res->next = r;
2130 2 : res = r;
2131 2 : r = r->next;
2132 : }
2133 : }
2134 :
2135 9 : res->next = l ? l : r;
2136 :
2137 9 : return res_start;
2138 : }
2139 :
2140 : /**
2141 : * sort linked list of struct ares_srv_reply by priority using merge sort.
2142 : *
2143 : * Merge sort is ideal for sorting linked lists as there is no problem
2144 : * with absence of random access into the list. The complexity is O(n log n)
2145 : *
2146 : * For reference, see Robert Sedgewick's "Algorithms in C", Addison-Wesley,
2147 : * ISBN 0-201-51425
2148 : */
2149 21 : static struct ares_srv_reply *reply_priority_sort(struct ares_srv_reply *list)
2150 : {
2151 : struct ares_srv_reply *half;
2152 :
2153 21 : if (!list || !list->next)
2154 12 : return list;
2155 :
2156 9 : half = split_reply_list(list);
2157 9 : list = merge_reply_list(reply_priority_sort(list),
2158 : reply_priority_sort(half));
2159 :
2160 9 : return list;
2161 : }
2162 :
2163 8 : static int reply_weight_rearrange(int len,
2164 : struct ares_srv_reply **start,
2165 : struct ares_srv_reply **end)
2166 : {
2167 : int i;
2168 : int total, selected;
2169 : int *totals;
2170 : struct ares_srv_reply *r, *prev, *tmp;
2171 8 : struct ares_srv_reply *new_start = NULL;
2172 8 : struct ares_srv_reply *new_end = NULL;
2173 : int ret;
2174 :
2175 8 : if (len <= 1) {
2176 4 : return EOK;
2177 : }
2178 :
2179 4 : totals = talloc_array(NULL, int, len);
2180 4 : if (!totals) {
2181 0 : return ENOMEM;
2182 : }
2183 :
2184 4 : srand(time(NULL) * getpid());
2185 :
2186 : /* promote all servers with weight==0 to the top */
2187 4 : r = *(start);
2188 4 : prev = NULL;
2189 16 : while (r != NULL) {
2190 8 : if (r->weight == 0 && r != *start) {
2191 : /* remove from the old list */
2192 4 : prev->next = r->next;
2193 :
2194 : /* add to the head of the new list */
2195 4 : tmp = r;
2196 4 : r = r->next;
2197 :
2198 4 : tmp->next = *start;
2199 4 : *start = tmp;
2200 : } else {
2201 4 : prev = r;
2202 4 : r = r->next;
2203 : }
2204 : }
2205 4 : *end = prev ? prev : *start;
2206 :
2207 16 : while (*start != NULL) {
2208 : /* Commpute the sum of the weights of those RRs, and with each RR
2209 : * associate the running sum in the selected order.
2210 : */
2211 8 : total = 0;
2212 8 : memset(totals, -1, sizeof(int) * len);
2213 20 : for (i = 0, r = *start; r != NULL; r=r->next, ++i) {
2214 12 : totals[i] = r->weight + total;
2215 12 : total = totals[i];
2216 : }
2217 :
2218 : /* choose a uniform random number between 0 and the sum computed
2219 : * (inclusive), and select the RR whose running sum value is the
2220 : * first in the selected order which is greater than or equal to
2221 : * the random number selected.
2222 : */
2223 8 : selected = (int)((total + 1) * (rand()/(RAND_MAX + 1.0)));
2224 9 : for (i = 0, r = *start, prev = NULL; r != NULL; r=r->next, ++i) {
2225 9 : if (totals[i] >= selected)
2226 8 : break;
2227 :
2228 1 : prev = r;
2229 : }
2230 :
2231 8 : if (r == NULL || totals[i] == -1) {
2232 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Bug: did not select any server!\n");
2233 0 : ret = EIO;
2234 0 : goto done;
2235 : }
2236 :
2237 : /* remove r from the old list */
2238 8 : if (prev) {
2239 1 : prev->next = r->next;
2240 : } else {
2241 7 : *start = r->next;
2242 : }
2243 :
2244 : /* add r to the end of the new list */
2245 8 : if (!new_start) {
2246 4 : new_start = r;
2247 4 : new_end = r;
2248 : } else {
2249 4 : new_end->next = r;
2250 4 : new_end = r;
2251 : }
2252 : }
2253 4 : new_end->next = NULL;
2254 :
2255 : /* return the rearranged list */
2256 4 : *start = new_start;
2257 4 : *end = new_end;
2258 4 : ret = EOK;
2259 :
2260 : done:
2261 4 : talloc_free(totals);
2262 4 : return ret;
2263 : }
2264 :
2265 : int
2266 3 : resolv_sort_srv_reply(struct ares_srv_reply **reply)
2267 : {
2268 : int ret;
2269 : struct ares_srv_reply *pri_start, *pri_end, *next, *prev_end;
2270 : int len;
2271 :
2272 : /* RFC 2782 says: If there is precisely one SRV RR, and its Target is "."
2273 : * (the root domain), abort.
2274 : */
2275 3 : if (*reply && !(*reply)->next && strcmp((*reply)->host, ".") == 0) {
2276 0 : DEBUG(SSSDBG_CRIT_FAILURE,
2277 : "DNS returned only the root domain, aborting\n");
2278 0 : return EIO;
2279 : }
2280 :
2281 : /* sort the list by priority */
2282 3 : *reply = reply_priority_sort(*reply);
2283 :
2284 3 : pri_start = *reply;
2285 3 : prev_end = NULL;
2286 :
2287 14 : while (pri_start) {
2288 8 : pri_end = pri_start;
2289 :
2290 : /* Find nodes with the same priority */
2291 8 : len = 1;
2292 20 : while (pri_end->next && pri_end->priority == pri_end->next->priority) {
2293 4 : pri_end = pri_end->next;
2294 4 : len++;
2295 : }
2296 :
2297 : /* rearrange each priority level according to the weight field */
2298 8 : next = pri_end->next;
2299 8 : pri_end->next = NULL;
2300 8 : ret = reply_weight_rearrange(len, &pri_start, &pri_end);
2301 8 : if (ret) {
2302 0 : DEBUG(SSSDBG_CRIT_FAILURE,
2303 : "Error rearranging priority level [%d]: %s\n",
2304 : ret, strerror(ret));
2305 0 : return ret;
2306 : }
2307 :
2308 : /* Hook the level back into the list */
2309 8 : if (prev_end) {
2310 5 : prev_end->next = pri_start;
2311 : } else {
2312 3 : *reply = pri_start;
2313 : }
2314 8 : pri_end->next = next;
2315 :
2316 : /* Move on to the next level */
2317 8 : prev_end = pri_end;
2318 8 : pri_start = next;
2319 : }
2320 :
2321 3 : return EOK;
2322 : }
|