1 /* $OpenBSD: getaddrinfo_async.c,v 1.4 2012/07/13 14:05:12 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 async_set_state(as, ASR_STATE_HALT); 114 ar->ar_errno = 0; 115 ar->ar_h_errno = NETDB_SUCCESS; 116 ar->ar_gai_errno = 0; 117 118 if (as->as.ai.hostname == NULL && 119 as->as.ai.servname == NULL) { 120 ar->ar_h_errno = NO_RECOVERY; 121 ar->ar_gai_errno = EAI_NONAME; 122 break; 123 } 124 125 ai = &as->as.ai.hints; 126 127 if (ai->ai_addrlen || 128 ai->ai_canonname || 129 ai->ai_addr || 130 ai->ai_next) { 131 ar->ar_h_errno = NO_RECOVERY; 132 ar->ar_gai_errno = EAI_BADHINTS; 133 break; 134 } 135 136 if (ai->ai_flags & ~AI_MASK || 137 (ai->ai_flags & AI_CANONNAME && ai->ai_flags & AI_FQDN)) { 138 ar->ar_h_errno = NO_RECOVERY; 139 ar->ar_gai_errno = EAI_BADFLAGS; 140 break; 141 } 142 143 if (ai->ai_family != PF_UNSPEC && 144 ai->ai_family != PF_INET && 145 ai->ai_family != PF_INET6) { 146 ar->ar_h_errno = NO_RECOVERY; 147 ar->ar_gai_errno = EAI_FAMILY; 148 break; 149 } 150 151 if (ai->ai_socktype && 152 ai->ai_socktype != SOCK_DGRAM && 153 ai->ai_socktype != SOCK_STREAM && 154 ai->ai_socktype != SOCK_RAW) { 155 ar->ar_h_errno = NO_RECOVERY; 156 ar->ar_gai_errno = EAI_SOCKTYPE; 157 break; 158 } 159 160 if (ai->ai_protocol && 161 ai->ai_protocol != IPPROTO_UDP && 162 ai->ai_protocol != IPPROTO_TCP) { 163 ar->ar_h_errno = NO_RECOVERY; 164 ar->ar_gai_errno = EAI_PROTOCOL; 165 break; 166 } 167 168 if (ai->ai_socktype == SOCK_RAW && 169 as->as.ai.servname != NULL) { 170 ar->ar_h_errno = NO_RECOVERY; 171 ar->ar_gai_errno = EAI_SERVICE; 172 break; 173 } 174 175 /* Make sure there is at least a valid combination */ 176 for (i = 0; matches[i].family != -1; i++) 177 if (MATCH_FAMILY(ai->ai_family, i) && 178 MATCH_SOCKTYPE(ai->ai_socktype, i) && 179 MATCH_PROTO(ai->ai_protocol, i)) 180 break; 181 if (matches[i].family == -1) { 182 ar->ar_h_errno = NO_RECOVERY; 183 ar->ar_gai_errno = EAI_BADHINTS; 184 break; 185 } 186 187 if (ai->ai_protocol == 0 || ai->ai_protocol == IPPROTO_UDP) 188 as->as.ai.port_udp = get_port(as->as.ai.servname, "udp", 189 as->as.ai.hints.ai_flags & AI_NUMERICSERV); 190 if (ai->ai_protocol == 0 || ai->ai_protocol == IPPROTO_TCP) 191 as->as.ai.port_tcp = get_port(as->as.ai.servname, "tcp", 192 as->as.ai.hints.ai_flags & AI_NUMERICSERV); 193 if (as->as.ai.port_tcp == -2 || as->as.ai.port_udp == -2 || 194 (as->as.ai.port_tcp == -1 && as->as.ai.port_udp == -1) || 195 (ai->ai_protocol && (as->as.ai.port_udp == -1 || 196 as->as.ai.port_tcp == -1))) { 197 ar->ar_h_errno = NO_RECOVERY; 198 ar->ar_gai_errno = EAI_SERVICE; 199 break; 200 } 201 202 /* If hostname is NULL, use local address */ 203 if (as->as.ai.hostname == NULL) { 204 for(family = iter_family(as, 1); 205 family != -1; 206 family = iter_family(as, 0)) { 207 /* 208 * We could use statically built sockaddrs for 209 * those, rather than parsing over and over. 210 */ 211 if (family == PF_INET) 212 str = (ai->ai_flags & AI_PASSIVE) ? \ 213 "0.0.0.0" : "127.0.0.1"; 214 else /* PF_INET6 */ 215 str = (ai->ai_flags & AI_PASSIVE) ? \ 216 "::" : "::1"; 217 /* This can't fail */ 218 sockaddr_from_str(&sa.sa, family, str); 219 if ((r = add_sockaddr(as, &sa.sa, NULL))) { 220 ar->ar_errno = errno; 221 ar->ar_h_errno = NETDB_INTERNAL; 222 ar->ar_gai_errno = r; 223 async_set_state(as, ASR_STATE_HALT); 224 break; 225 } 226 } 227 if (ar->ar_gai_errno == 0 && as->as_count == 0) { 228 ar->ar_h_errno = NO_DATA; 229 ar->ar_gai_errno = EAI_NODATA; 230 } 231 break; 232 } 233 234 /* Try numeric addresses first */ 235 for(family = iter_family(as, 1); 236 family != -1; 237 family = iter_family(as, 0)) { 238 239 if (sockaddr_from_str(&sa.sa, family, 240 as->as.ai.hostname) == -1) 241 continue; 242 243 if ((r = add_sockaddr(as, &sa.sa, NULL))) { 244 ar->ar_errno = errno; 245 ar->ar_h_errno = NETDB_INTERNAL; 246 ar->ar_gai_errno = r; 247 async_set_state(as, ASR_STATE_HALT); 248 break; 249 } 250 251 async_set_state(as, ASR_STATE_HALT); 252 break; 253 } 254 if (ar->ar_gai_errno || as->as_count) 255 break; 256 257 if (ai->ai_flags & AI_NUMERICHOST) { 258 ar->ar_h_errno = NO_RECOVERY; 259 ar->ar_gai_errno = EAI_FAIL; 260 async_set_state(as, ASR_STATE_HALT); 261 break; 262 } 263 264 /* Starting domain lookup */ 265 async_set_state(as, ASR_STATE_SEARCH_DOMAIN); 266 break; 267 268 case ASR_STATE_SEARCH_DOMAIN: 269 270 r = asr_iter_domain(as, as->as.ai.hostname, fqdn, sizeof(fqdn)); 271 if (r == -1) { 272 async_set_state(as, ASR_STATE_NOT_FOUND); 273 break; 274 } 275 if (r > (int)sizeof(fqdn)) { 276 ar->ar_errno = EINVAL; 277 ar->ar_h_errno = NO_RECOVERY; 278 ar->ar_gai_errno = EAI_OVERFLOW; 279 async_set_state(as, ASR_STATE_HALT); 280 break; 281 } 282 283 /* 284 * Create a subquery to lookup the host addresses. 285 * We use the special hostaddr_async() API, which has the 286 * nice property of honoring the "lookup" and "family" keyword 287 * in the configuration, thus returning the right address 288 * families in the right order, and thus fixing the current 289 * getaddrinfo() feature documented in the BUGS section of 290 * resolver.conf(5). 291 */ 292 as->as.ai.subq = hostaddr_async_ctx(fqdn, 293 as->as.ai.hints.ai_family, as->as.ai.hints.ai_flags, 294 as->as_ctx); 295 if (as->as.ai.subq == NULL) { 296 ar->ar_errno = errno; 297 if (errno == EINVAL) { 298 ar->ar_h_errno = NO_RECOVERY; 299 ar->ar_gai_errno = EAI_FAIL; 300 } else { 301 ar->ar_h_errno = NETDB_INTERNAL; 302 ar->ar_gai_errno = EAI_MEMORY; 303 } 304 async_set_state(as, ASR_STATE_HALT); 305 break; 306 } 307 async_set_state(as, ASR_STATE_LOOKUP_DOMAIN); 308 break; 309 310 case ASR_STATE_LOOKUP_DOMAIN: 311 312 /* Run the subquery */ 313 if ((r = async_run(as->as.ai.subq, ar)) == ASYNC_COND) 314 return (ASYNC_COND); 315 316 /* Got one more address, use it to extend the result list. */ 317 if (r == ASYNC_YIELD) { 318 if ((r = add_sockaddr(as, &ar->ar_sa.sa, 319 ar->ar_cname))) { 320 ar->ar_errno = errno; 321 ar->ar_h_errno = NETDB_INTERNAL; 322 ar->ar_gai_errno = r; 323 async_set_state(as, ASR_STATE_HALT); 324 } 325 if (ar->ar_cname) 326 free(ar->ar_cname); 327 break; 328 } 329 330 /* 331 * The subquery is done. Stop there if we have at least one 332 * answer. 333 */ 334 as->as.ai.subq = NULL; 335 if (ar->ar_count) { 336 async_set_state(as, ASR_STATE_HALT); 337 break; 338 } 339 340 /* 341 * No anwser for this domain, but we might be suggested to 342 * try again later, so remember this. Then search the next 343 * domain. 344 */ 345 if (ar->ar_gai_errno == EAI_AGAIN) 346 as->as.ai.flags |= ASYNC_AGAIN; 347 async_set_state(as, ASR_STATE_SEARCH_DOMAIN); 348 break; 349 350 case ASR_STATE_NOT_FOUND: 351 352 /* 353 * No result found. Maybe we can try again. 354 */ 355 ar->ar_errno = 0; 356 if (as->as.ai.flags & ASYNC_AGAIN) { 357 ar->ar_h_errno = TRY_AGAIN; 358 ar->ar_gai_errno = EAI_AGAIN; 359 } else { 360 ar->ar_h_errno = NO_DATA; 361 ar->ar_gai_errno = EAI_NODATA; 362 } 363 async_set_state(as, ASR_STATE_HALT); 364 break; 365 366 case ASR_STATE_HALT: 367 368 /* Set the results. */ 369 370 if (ar->ar_gai_errno == 0) { 371 ar->ar_count = as->as_count; 372 ar->ar_addrinfo = as->as.ai.aifirst; 373 as->as.ai.aifirst = NULL; 374 } else { 375 ar->ar_count = 0; 376 ar->ar_addrinfo = NULL; 377 } 378 return (ASYNC_DONE); 379 380 default: 381 ar->ar_errno = EOPNOTSUPP; 382 ar->ar_h_errno = NETDB_INTERNAL; 383 ar->ar_gai_errno = EAI_SYSTEM; 384 async_set_state(as, ASR_STATE_HALT); 385 break; 386 } 387 goto next; 388 } 389 390 /* 391 * Retreive the port number for the service name "servname" and 392 * the protocol "proto". 393 */ 394 static int 395 get_port(const char *servname, const char *proto, int numonly) 396 { 397 struct servent se; 398 struct servent_data sed; 399 int port, r; 400 const char* e; 401 402 if (servname == NULL) 403 return (0); 404 405 e = NULL; 406 port = strtonum(servname, 0, USHRT_MAX, &e); 407 if (e == NULL) 408 return (port); 409 if (errno == ERANGE) 410 return (-2); /* invalid */ 411 if (numonly) 412 return (-2); 413 414 memset(&sed, 0, sizeof(sed)); 415 r = getservbyname_r(servname, proto, &se, &sed); 416 port = ntohs(se.s_port); 417 endservent_r(&sed); 418 419 if (r == -1) 420 return (-1); /* not found */ 421 422 return (port); 423 } 424 425 /* 426 * Iterate over the address families that are to be queried. Use the 427 * list on the async context, unless a specific family was given in hints. 428 */ 429 static int 430 iter_family(struct async *as, int first) 431 { 432 if (first) { 433 as->as_family_idx = 0; 434 if (as->as.ai.hints.ai_family != PF_UNSPEC) 435 return as->as.ai.hints.ai_family; 436 return AS_FAMILY(as); 437 } 438 439 if (as->as.ai.hints.ai_family != PF_UNSPEC) 440 return (-1); 441 442 as->as_family_idx++; 443 444 return AS_FAMILY(as); 445 } 446 447 /* 448 * Use the sockaddr at "sa" to extend the result list on the "as" context, 449 * with the specified canonical name "cname". This function adds one 450 * entry per protocol/socktype match. 451 */ 452 static int 453 add_sockaddr(struct async *as, struct sockaddr *sa, const char *cname) 454 { 455 struct addrinfo *ai; 456 int i, port; 457 458 for(i = 0; matches[i].family != -1; i++) { 459 if (matches[i].family != sa->sa_family || 460 !MATCH_SOCKTYPE(as->as.ai.hints.ai_socktype, i) || 461 !MATCH_PROTO(as->as.ai.hints.ai_protocol, i)) 462 continue; 463 464 if (matches[i].protocol == IPPROTO_TCP) 465 port = as->as.ai.port_tcp; 466 else if (matches[i].protocol == IPPROTO_UDP) 467 port = as->as.ai.port_udp; 468 else 469 port = 0; 470 471 /* servname specified, but not defined for this protocol */ 472 if (port == -1) 473 continue; 474 475 ai = calloc(1, sizeof(*ai) + sa->sa_len); 476 if (ai == NULL) 477 return (EAI_MEMORY); 478 ai->ai_family = sa->sa_family; 479 ai->ai_socktype = matches[i].socktype; 480 ai->ai_protocol = matches[i].protocol; 481 ai->ai_addrlen = sa->sa_len; 482 ai->ai_addr = (void*)(ai + 1); 483 if (cname && 484 as->as.ai.hints.ai_flags & (AI_CANONNAME | AI_FQDN)) { 485 if ((ai->ai_canonname = strdup(cname)) == NULL) { 486 free(ai); 487 return (EAI_MEMORY); 488 } 489 } 490 memmove(ai->ai_addr, sa, sa->sa_len); 491 if (sa->sa_family == PF_INET) 492 ((struct sockaddr_in *)ai->ai_addr)->sin_port = 493 htons(port); 494 else if (sa->sa_family == PF_INET6) 495 ((struct sockaddr_in6 *)ai->ai_addr)->sin6_port = 496 htons(port); 497 498 if (as->as.ai.aifirst == NULL) 499 as->as.ai.aifirst = ai; 500 if (as->as.ai.ailast) 501 as->as.ai.ailast->ai_next = ai; 502 as->as.ai.ailast = ai; 503 as->as_count += 1; 504 } 505 506 return (0); 507 } 508