1 /* $OpenBSD: getaddrinfo_async.c,v 1.6 2012/09/05 15:56:13 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 #include <sys/types.h> 18 #include <sys/uio.h> 19 20 #include <arpa/nameser.h> 21 22 #include <err.h> 23 #include <errno.h> 24 #include <stdlib.h> 25 #include <string.h> 26 #include <unistd.h> 27 28 #include "asr.h" 29 #include "asr_private.h" 30 31 struct match { 32 int family; 33 int socktype; 34 int protocol; 35 }; 36 37 static int getaddrinfo_async_run(struct async *, struct async_res *); 38 static int get_port(const char *, const char *, int); 39 static int iter_family(struct async *, int); 40 static int add_sockaddr(struct async *, struct sockaddr *, const char *); 41 42 static const struct match matches[] = { 43 { PF_INET, SOCK_DGRAM, IPPROTO_UDP }, 44 { PF_INET, SOCK_STREAM, IPPROTO_TCP }, 45 { PF_INET, SOCK_RAW, 0 }, 46 { PF_INET6, SOCK_DGRAM, IPPROTO_UDP }, 47 { PF_INET6, SOCK_STREAM, IPPROTO_TCP }, 48 { PF_INET6, SOCK_RAW, 0 }, 49 { -1, 0, 0, }, 50 }; 51 52 #define MATCH_FAMILY(a, b) ((a) == matches[(b)].family || (a) == PF_UNSPEC) 53 #define MATCH_PROTO(a, b) ((a) == matches[(b)].protocol || (a) == 0) 54 /* Do not match SOCK_RAW unless explicitely specified */ 55 #define MATCH_SOCKTYPE(a, b) ((a) == matches[(b)].socktype || ((a) == 0 && \ 56 matches[(b)].socktype != SOCK_RAW)) 57 58 struct async * 59 getaddrinfo_async(const char *hostname, const char *servname, 60 const struct addrinfo *hints, struct asr *asr) 61 { 62 struct asr_ctx *ac; 63 struct async *as; 64 65 ac = asr_use_resolver(asr); 66 if ((as = async_new(ac, ASR_GETADDRINFO)) == NULL) 67 goto abort; /* errno set */ 68 as->as_run = getaddrinfo_async_run; 69 70 if (hostname && (as->as.ai.hostname = strdup(hostname)) == NULL) 71 goto abort; /* errno set */ 72 if (servname && (as->as.ai.servname = strdup(servname)) == NULL) 73 goto abort; /* errno set */ 74 if (hints) 75 memmove(&as->as.ai.hints, hints, sizeof *hints); 76 else { 77 memset(&as->as.ai.hints, 0, sizeof as->as.ai.hints); 78 as->as.ai.hints.ai_family = PF_UNSPEC; 79 } 80 81 asr_ctx_unref(ac); 82 return (as); 83 abort: 84 if (as) 85 async_free(as); 86 asr_ctx_unref(ac); 87 return (NULL); 88 } 89 90 static int 91 getaddrinfo_async_run(struct async *as, struct async_res *ar) 92 { 93 const char *str; 94 struct addrinfo *ai; 95 int i, family, r; 96 char fqdn[MAXDNAME]; 97 union { 98 struct sockaddr sa; 99 struct sockaddr_in sain; 100 struct sockaddr_in6 sain6; 101 } sa; 102 103 next: 104 switch(as->as_state) { 105 106 case ASR_STATE_INIT: 107 108 /* 109 * First, make sure the parameters are valid. 110 */ 111 112 as->as_count = 0; 113 114 if (as->as.ai.hostname == NULL && 115 as->as.ai.servname == NULL) { 116 ar->ar_gai_errno = EAI_NONAME; 117 async_set_state(as, ASR_STATE_HALT); 118 break; 119 } 120 121 ai = &as->as.ai.hints; 122 123 if (ai->ai_addrlen || 124 ai->ai_canonname || 125 ai->ai_addr || 126 ai->ai_next) { 127 ar->ar_gai_errno = EAI_BADHINTS; 128 async_set_state(as, ASR_STATE_HALT); 129 break; 130 } 131 132 if (ai->ai_flags & ~AI_MASK || 133 (ai->ai_flags & AI_CANONNAME && ai->ai_flags & AI_FQDN)) { 134 ar->ar_gai_errno = EAI_BADFLAGS; 135 async_set_state(as, ASR_STATE_HALT); 136 break; 137 } 138 139 if (ai->ai_family != PF_UNSPEC && 140 ai->ai_family != PF_INET && 141 ai->ai_family != PF_INET6) { 142 ar->ar_gai_errno = EAI_FAMILY; 143 async_set_state(as, ASR_STATE_HALT); 144 break; 145 } 146 147 if (ai->ai_socktype && 148 ai->ai_socktype != SOCK_DGRAM && 149 ai->ai_socktype != SOCK_STREAM && 150 ai->ai_socktype != SOCK_RAW) { 151 ar->ar_gai_errno = EAI_SOCKTYPE; 152 async_set_state(as, ASR_STATE_HALT); 153 break; 154 } 155 156 if (ai->ai_protocol && 157 ai->ai_protocol != IPPROTO_UDP && 158 ai->ai_protocol != IPPROTO_TCP) { 159 ar->ar_gai_errno = EAI_PROTOCOL; 160 async_set_state(as, ASR_STATE_HALT); 161 break; 162 } 163 164 if (ai->ai_socktype == SOCK_RAW && 165 as->as.ai.servname != NULL) { 166 ar->ar_gai_errno = EAI_SERVICE; 167 async_set_state(as, ASR_STATE_HALT); 168 break; 169 } 170 171 /* Make sure there is at least a valid combination */ 172 for (i = 0; matches[i].family != -1; i++) 173 if (MATCH_FAMILY(ai->ai_family, i) && 174 MATCH_SOCKTYPE(ai->ai_socktype, i) && 175 MATCH_PROTO(ai->ai_protocol, i)) 176 break; 177 if (matches[i].family == -1) { 178 ar->ar_gai_errno = EAI_BADHINTS; 179 async_set_state(as, ASR_STATE_HALT); 180 break; 181 } 182 183 if (ai->ai_protocol == 0 || ai->ai_protocol == IPPROTO_UDP) 184 as->as.ai.port_udp = get_port(as->as.ai.servname, "udp", 185 as->as.ai.hints.ai_flags & AI_NUMERICSERV); 186 if (ai->ai_protocol == 0 || ai->ai_protocol == IPPROTO_TCP) 187 as->as.ai.port_tcp = get_port(as->as.ai.servname, "tcp", 188 as->as.ai.hints.ai_flags & AI_NUMERICSERV); 189 if (as->as.ai.port_tcp == -2 || as->as.ai.port_udp == -2 || 190 (as->as.ai.port_tcp == -1 && as->as.ai.port_udp == -1) || 191 (ai->ai_protocol && (as->as.ai.port_udp == -1 || 192 as->as.ai.port_tcp == -1))) { 193 ar->ar_gai_errno = EAI_SERVICE; 194 async_set_state(as, ASR_STATE_HALT); 195 break; 196 } 197 198 ar->ar_gai_errno = 0; 199 200 /* If hostname is NULL, use local address */ 201 if (as->as.ai.hostname == NULL) { 202 for(family = iter_family(as, 1); 203 family != -1; 204 family = iter_family(as, 0)) { 205 /* 206 * We could use statically built sockaddrs for 207 * those, rather than parsing over and over. 208 */ 209 if (family == PF_INET) 210 str = (ai->ai_flags & AI_PASSIVE) ? \ 211 "0.0.0.0" : "127.0.0.1"; 212 else /* PF_INET6 */ 213 str = (ai->ai_flags & AI_PASSIVE) ? \ 214 "::" : "::1"; 215 /* This can't fail */ 216 sockaddr_from_str(&sa.sa, family, str); 217 if ((r = add_sockaddr(as, &sa.sa, NULL))) { 218 ar->ar_gai_errno = r; 219 break; 220 } 221 } 222 if (ar->ar_gai_errno == 0 && as->as_count == 0) { 223 ar->ar_gai_errno = EAI_NODATA; 224 } 225 async_set_state(as, ASR_STATE_HALT); 226 break; 227 } 228 229 /* Try numeric addresses first */ 230 for(family = iter_family(as, 1); 231 family != -1; 232 family = iter_family(as, 0)) { 233 234 if (sockaddr_from_str(&sa.sa, family, 235 as->as.ai.hostname) == -1) 236 continue; 237 238 if ((r = add_sockaddr(as, &sa.sa, NULL))) { 239 ar->ar_gai_errno = r; 240 } 241 break; 242 } 243 if (ar->ar_gai_errno || as->as_count) { 244 async_set_state(as, ASR_STATE_HALT); 245 break; 246 } 247 248 if (ai->ai_flags & AI_NUMERICHOST) { 249 ar->ar_gai_errno = EAI_FAIL; 250 async_set_state(as, ASR_STATE_HALT); 251 break; 252 } 253 254 /* Starting domain lookup */ 255 async_set_state(as, ASR_STATE_SEARCH_DOMAIN); 256 break; 257 258 case ASR_STATE_SEARCH_DOMAIN: 259 260 r = asr_iter_domain(as, as->as.ai.hostname, fqdn, sizeof(fqdn)); 261 if (r == -1) { 262 async_set_state(as, ASR_STATE_NOT_FOUND); 263 break; 264 } 265 if (r > (int)sizeof(fqdn)) { 266 ar->ar_gai_errno = EAI_OVERFLOW; 267 async_set_state(as, ASR_STATE_HALT); 268 break; 269 } 270 271 /* 272 * Create a subquery to lookup the host addresses. 273 * We use the special hostaddr_async() API, which has the 274 * nice property of honoring the "lookup" and "family" keyword 275 * in the configuration, thus returning the right address 276 * families in the right order, and thus fixing the current 277 * getaddrinfo() feature documented in the BUGS section of 278 * resolver.conf(5). 279 */ 280 as->as.ai.subq = hostaddr_async_ctx(fqdn, 281 as->as.ai.hints.ai_family, as->as.ai.hints.ai_flags, 282 as->as_ctx); 283 if (as->as.ai.subq == NULL) { 284 if (errno == EINVAL) { 285 ar->ar_gai_errno = EAI_FAIL; 286 } else { 287 ar->ar_gai_errno = EAI_MEMORY; 288 } 289 async_set_state(as, ASR_STATE_HALT); 290 break; 291 } 292 async_set_state(as, ASR_STATE_LOOKUP_DOMAIN); 293 break; 294 295 case ASR_STATE_LOOKUP_DOMAIN: 296 297 /* Run the subquery */ 298 if ((r = async_run(as->as.ai.subq, ar)) == ASYNC_COND) 299 return (ASYNC_COND); 300 301 /* 302 * The subquery is done. Stop there if we have at least one 303 * answer. 304 */ 305 as->as.ai.subq = NULL; 306 if (ar->ar_count == 0) { 307 /* 308 * No anwser for this domain, but we might be suggested 309 * to try again later, so remember this. Then search 310 * the next domain. 311 */ 312 if (ar->ar_gai_errno == EAI_AGAIN) 313 as->as.ai.flags |= ASYNC_AGAIN; 314 async_set_state(as, ASR_STATE_SEARCH_DOMAIN); 315 break; 316 } 317 318 /* iterate over and expand results */ 319 320 for(ai = ar->ar_addrinfo; ai; ai = ai->ai_next) { 321 r = add_sockaddr(as, ai->ai_addr, ai->ai_canonname); 322 if (r) 323 break; 324 } 325 freeaddrinfo(ar->ar_addrinfo); 326 ar->ar_gai_errno = r; 327 async_set_state(as, ASR_STATE_HALT); 328 break; 329 330 case ASR_STATE_NOT_FOUND: 331 332 /* 333 * No result found. Maybe we can try again. 334 */ 335 if (as->as.ai.flags & ASYNC_AGAIN) { 336 ar->ar_gai_errno = EAI_AGAIN; 337 } else { 338 ar->ar_gai_errno = EAI_NODATA; 339 } 340 async_set_state(as, ASR_STATE_HALT); 341 break; 342 343 case ASR_STATE_HALT: 344 345 /* Set the results. */ 346 347 if (ar->ar_gai_errno == 0) { 348 ar->ar_count = as->as_count; 349 ar->ar_addrinfo = as->as.ai.aifirst; 350 as->as.ai.aifirst = NULL; 351 } else { 352 ar->ar_count = 0; 353 ar->ar_addrinfo = NULL; 354 } 355 return (ASYNC_DONE); 356 357 default: 358 ar->ar_errno = EOPNOTSUPP; 359 ar->ar_gai_errno = EAI_SYSTEM; 360 async_set_state(as, ASR_STATE_HALT); 361 break; 362 } 363 goto next; 364 } 365 366 /* 367 * Retreive the port number for the service name "servname" and 368 * the protocol "proto". 369 */ 370 static int 371 get_port(const char *servname, const char *proto, int numonly) 372 { 373 struct servent se; 374 struct servent_data sed; 375 int port, r; 376 const char* e; 377 378 if (servname == NULL) 379 return (0); 380 381 e = NULL; 382 port = strtonum(servname, 0, USHRT_MAX, &e); 383 if (e == NULL) 384 return (port); 385 if (errno == ERANGE) 386 return (-2); /* invalid */ 387 if (numonly) 388 return (-2); 389 390 memset(&sed, 0, sizeof(sed)); 391 r = getservbyname_r(servname, proto, &se, &sed); 392 port = ntohs(se.s_port); 393 endservent_r(&sed); 394 395 if (r == -1) 396 return (-1); /* not found */ 397 398 return (port); 399 } 400 401 /* 402 * Iterate over the address families that are to be queried. Use the 403 * list on the async context, unless a specific family was given in hints. 404 */ 405 static int 406 iter_family(struct async *as, int first) 407 { 408 if (first) { 409 as->as_family_idx = 0; 410 if (as->as.ai.hints.ai_family != PF_UNSPEC) 411 return as->as.ai.hints.ai_family; 412 return AS_FAMILY(as); 413 } 414 415 if (as->as.ai.hints.ai_family != PF_UNSPEC) 416 return (-1); 417 418 as->as_family_idx++; 419 420 return AS_FAMILY(as); 421 } 422 423 /* 424 * Use the sockaddr at "sa" to extend the result list on the "as" context, 425 * with the specified canonical name "cname". This function adds one 426 * entry per protocol/socktype match. 427 */ 428 static int 429 add_sockaddr(struct async *as, struct sockaddr *sa, const char *cname) 430 { 431 struct addrinfo *ai; 432 int i, port; 433 434 for(i = 0; matches[i].family != -1; i++) { 435 if (matches[i].family != sa->sa_family || 436 !MATCH_SOCKTYPE(as->as.ai.hints.ai_socktype, i) || 437 !MATCH_PROTO(as->as.ai.hints.ai_protocol, i)) 438 continue; 439 440 if (matches[i].protocol == IPPROTO_TCP) 441 port = as->as.ai.port_tcp; 442 else if (matches[i].protocol == IPPROTO_UDP) 443 port = as->as.ai.port_udp; 444 else 445 port = 0; 446 447 /* servname specified, but not defined for this protocol */ 448 if (port == -1) 449 continue; 450 451 ai = calloc(1, sizeof(*ai) + sa->sa_len); 452 if (ai == NULL) 453 return (EAI_MEMORY); 454 ai->ai_family = sa->sa_family; 455 ai->ai_socktype = matches[i].socktype; 456 ai->ai_protocol = matches[i].protocol; 457 ai->ai_addrlen = sa->sa_len; 458 ai->ai_addr = (void*)(ai + 1); 459 if (cname && 460 as->as.ai.hints.ai_flags & (AI_CANONNAME | AI_FQDN)) { 461 if ((ai->ai_canonname = strdup(cname)) == NULL) { 462 free(ai); 463 return (EAI_MEMORY); 464 } 465 } 466 memmove(ai->ai_addr, sa, sa->sa_len); 467 if (sa->sa_family == PF_INET) 468 ((struct sockaddr_in *)ai->ai_addr)->sin_port = 469 htons(port); 470 else if (sa->sa_family == PF_INET6) 471 ((struct sockaddr_in6 *)ai->ai_addr)->sin6_port = 472 htons(port); 473 474 if (as->as.ai.aifirst == NULL) 475 as->as.ai.aifirst = ai; 476 if (as->as.ai.ailast) 477 as->as.ai.ailast->ai_next = ai; 478 as->as.ai.ailast = ai; 479 as->as_count += 1; 480 } 481 482 return (0); 483 } 484