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