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