1*46ab4803Seric /* $OpenBSD: res_search_async.c,v 1.2 2012/09/09 09:42:06 eric Exp $ */ 2b44da627Seric /* 3b44da627Seric * Copyright (c) 2012 Eric Faurot <eric@openbsd.org> 4b44da627Seric * 5b44da627Seric * Permission to use, copy, modify, and distribute this software for any 6b44da627Seric * purpose with or without fee is hereby granted, provided that the above 7b44da627Seric * copyright notice and this permission notice appear in all copies. 8b44da627Seric * 9b44da627Seric * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10b44da627Seric * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11b44da627Seric * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12b44da627Seric * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13b44da627Seric * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14b44da627Seric * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15b44da627Seric * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16b44da627Seric */ 17b44da627Seric 18b44da627Seric #include <sys/types.h> 19b44da627Seric #include <sys/uio.h> 20b44da627Seric 21b44da627Seric #include <arpa/nameser.h> 22b44da627Seric 23b44da627Seric #include <err.h> 24b44da627Seric #include <errno.h> 25b44da627Seric #include <stdlib.h> 26b44da627Seric #include <string.h> 27b44da627Seric #include <unistd.h> 28b44da627Seric 29b44da627Seric /* 30b44da627Seric * TODO: 31b44da627Seric * 32b44da627Seric * - make it possible to reuse ibuf if it was NULL when first called, 33b44da627Seric * to avoid reallocating buffers everytime. 34b44da627Seric */ 35b44da627Seric 36b44da627Seric #include "asr.h" 37b44da627Seric #include "asr_private.h" 38b44da627Seric 39b44da627Seric static int res_search_async_run(struct async *, struct async_res *); 40b44da627Seric 41b44da627Seric /* 42b44da627Seric * Unlike res_query_async(), this function returns a valid packet only if 43b44da627Seric * h_errno is NETDB_SUCCESS. 44b44da627Seric */ 45b44da627Seric struct async * 46b44da627Seric res_search_async(const char *name, int class, int type, unsigned char *ans, 47b44da627Seric int anslen, struct asr *asr) 48b44da627Seric { 49b44da627Seric struct asr_ctx *ac; 50b44da627Seric struct async *as; 51*46ab4803Seric 52*46ab4803Seric DPRINT("asr: res_search_async(\"%s\", %i, %i)\n", name, class, type); 53*46ab4803Seric 54b44da627Seric ac = asr_use_resolver(asr); 55b44da627Seric as = res_search_async_ctx(name, class, type, ans, anslen, ac); 56b44da627Seric asr_ctx_unref(ac); 57b44da627Seric 58b44da627Seric return (as); 59b44da627Seric } 60b44da627Seric 61b44da627Seric struct async * 62b44da627Seric res_search_async_ctx(const char *name, int class, int type, unsigned char *ans, 63b44da627Seric int anslen, struct asr_ctx *ac) 64b44da627Seric { 65b44da627Seric struct async *as; 66b44da627Seric 67*46ab4803Seric DPRINT("asr: res_search_async_ctx(\"%s\", %i, %i)\n", name, class, type); 68b44da627Seric 69b44da627Seric if ((as = async_new(ac, ASR_SEARCH)) == NULL) 70b44da627Seric goto err; /* errno set */ 71b44da627Seric as->as_run = res_search_async_run; 72b44da627Seric if ((as->as.search.name = strdup(name)) == NULL) 73b44da627Seric goto err; /* errno set */ 74b44da627Seric 75b44da627Seric if (ans) { 76b44da627Seric as->as.search.flags |= ASYNC_EXTIBUF; 77b44da627Seric as->as.search.ibuf = ans; 78b44da627Seric as->as.search.ibufsize = anslen; 79b44da627Seric } else { 80b44da627Seric as->as.search.ibuf = NULL; 81b44da627Seric as->as.search.ibufsize = 0; 82b44da627Seric } 83b44da627Seric as->as.search.ibuflen = 0; 84b44da627Seric 85b44da627Seric as->as.search.class = class; 86b44da627Seric as->as.search.type = type; 87b44da627Seric 88b44da627Seric return (as); 89b44da627Seric err: 90b44da627Seric if (as) 91b44da627Seric async_free(as); 92b44da627Seric return (NULL); 93b44da627Seric } 94b44da627Seric 95b44da627Seric #define HERRNO_UNSET -2 96b44da627Seric 97b44da627Seric static int 98b44da627Seric res_search_async_run(struct async *as, struct async_res *ar) 99b44da627Seric { 100b44da627Seric int r; 101b44da627Seric char fqdn[MAXDNAME]; 102b44da627Seric 103b44da627Seric next: 104b44da627Seric switch(as->as_state) { 105b44da627Seric 106b44da627Seric case ASR_STATE_INIT: 107b44da627Seric 108b44da627Seric as->as.search.saved_h_errno = HERRNO_UNSET; 109b44da627Seric async_set_state(as, ASR_STATE_NEXT_DOMAIN); 110b44da627Seric break; 111b44da627Seric 112b44da627Seric case ASR_STATE_NEXT_DOMAIN: 113b44da627Seric 114b44da627Seric /* Reset flags to be able to identify the case in STATE_SUBQUERY. */ 115b44da627Seric as->as_dom_flags = 0; 116b44da627Seric 117b44da627Seric r = asr_iter_domain(as, as->as.search.name, fqdn, sizeof(fqdn)); 118b44da627Seric if (r == -1) { 119b44da627Seric async_set_state(as, ASR_STATE_NOT_FOUND); 120b44da627Seric break; 121b44da627Seric } 122b44da627Seric if (r > sizeof(fqdn)) { 123b44da627Seric ar->ar_errno = EINVAL; 124b44da627Seric ar->ar_h_errno = NO_RECOVERY; 125b44da627Seric ar->ar_datalen = -1; 126b44da627Seric ar->ar_data = NULL; 127b44da627Seric async_set_state(as, ASR_STATE_HALT); 128b44da627Seric break; 129b44da627Seric } 130b44da627Seric as->as.search.subq = res_query_async_ctx(fqdn, 131b44da627Seric as->as.search.class, as->as.search.type, 132b44da627Seric as->as.search.ibuf, as->as.search.ibufsize, as->as_ctx); 133b44da627Seric if (as->as.search.subq == NULL) { 134b44da627Seric ar->ar_errno = errno; 135b44da627Seric if (errno == EINVAL) 136b44da627Seric ar->ar_h_errno = NO_RECOVERY; 137b44da627Seric else 138b44da627Seric ar->ar_h_errno = NETDB_INTERNAL; 139b44da627Seric ar->ar_datalen = -1; 140b44da627Seric ar->ar_data = NULL; 141b44da627Seric async_set_state(as, ASR_STATE_HALT); 142b44da627Seric break; 143b44da627Seric } 144b44da627Seric async_set_state(as, ASR_STATE_SUBQUERY); 145b44da627Seric break; 146b44da627Seric 147b44da627Seric case ASR_STATE_SUBQUERY: 148b44da627Seric 149b44da627Seric if ((r = async_run(as->as.search.subq, ar)) == ASYNC_COND) 150b44da627Seric return (ASYNC_COND); 151b44da627Seric as->as.search.subq = NULL; 152b44da627Seric 153b44da627Seric if (ar->ar_h_errno == NETDB_SUCCESS) { 154b44da627Seric async_set_state(as, ASR_STATE_HALT); 155b44da627Seric break; 156b44da627Seric } 157b44da627Seric 158b44da627Seric /* 159b44da627Seric * The original res_search() does this in the domain search 160b44da627Seric * loop, but only for ECONNREFUSED. I think we can do better 161b44da627Seric * because technically if we get an errno, it means 162b44da627Seric * we couldn't reach any nameserver, so there is no point 163b44da627Seric * in trying further. 164b44da627Seric */ 165b44da627Seric if (ar->ar_errno) { 166b44da627Seric async_set_state(as, ASR_STATE_HALT); 167b44da627Seric break; 168b44da627Seric } 169b44da627Seric 170b44da627Seric /* 171b44da627Seric * If we don't use an external buffer, the packet was allocated 172b44da627Seric * by the subquery and it must be freed now. 173b44da627Seric */ 174b44da627Seric if ((as->as.search.flags & ASYNC_EXTIBUF) == 0) 175b44da627Seric free(ar->ar_data); 176b44da627Seric 177b44da627Seric /* 178b44da627Seric * The original resolver does something like this, to 179b44da627Seric */ 180b44da627Seric if (as->as_dom_flags & (ASYNC_DOM_NDOTS | ASYNC_DOM_ASIS)) 181b44da627Seric as->as.search.saved_h_errno = ar->ar_h_errno; 182b44da627Seric 183b44da627Seric if (as->as_dom_flags & ASYNC_DOM_DOMAIN) { 184b44da627Seric if (ar->ar_h_errno == NO_DATA) 185b44da627Seric as->as.search.flags |= ASYNC_NODATA; 186b44da627Seric else if (ar->ar_h_errno == TRY_AGAIN) 187b44da627Seric as->as.search.flags |= ASYNC_AGAIN; 188b44da627Seric } 189b44da627Seric 190b44da627Seric async_set_state(as, ASR_STATE_NEXT_DOMAIN); 191b44da627Seric break; 192b44da627Seric 193b44da627Seric case ASR_STATE_NOT_FOUND: 194b44da627Seric 195b44da627Seric if (as->as.search.saved_h_errno != HERRNO_UNSET) 196b44da627Seric ar->ar_h_errno = as->as.search.saved_h_errno; 197b44da627Seric else if (as->as.search.flags & ASYNC_NODATA) 198b44da627Seric ar->ar_h_errno = NO_DATA; 199b44da627Seric else if (as->as.search.flags & ASYNC_AGAIN) 200b44da627Seric ar->ar_h_errno = TRY_AGAIN; 201b44da627Seric /* 202b44da627Seric * Else, we got the ar_h_errno value set by res_query_async() 203b44da627Seric * for the last domain. 204b44da627Seric */ 205b44da627Seric ar->ar_datalen = -1; 206b44da627Seric ar->ar_data = NULL; 207b44da627Seric async_set_state(as, ASR_STATE_HALT); 208b44da627Seric break; 209b44da627Seric 210b44da627Seric case ASR_STATE_HALT: 211b44da627Seric 212b44da627Seric return (ASYNC_DONE); 213b44da627Seric 214b44da627Seric default: 215b44da627Seric ar->ar_errno = EOPNOTSUPP; 216b44da627Seric ar->ar_h_errno = NETDB_INTERNAL; 217b44da627Seric async_set_state(as, ASR_STATE_HALT); 218b44da627Seric break; 219b44da627Seric } 220b44da627Seric goto next; 221b44da627Seric } 222