1 /* $OpenBSD: getaddrinfo_async.c,v 1.7 2012/09/05 21:49: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 addrinfo_add(struct async *, const struct sockaddr *, const char *); 41 static int addrinfo_from_file(struct async *, int, FILE *); 42 static int addrinfo_from_pkt(struct async *, char *, size_t); 43 #ifdef YP 44 static int addrinfo_from_yp(struct async *, int, char *); 45 #endif 46 47 static const struct match matches[] = { 48 { PF_INET, SOCK_DGRAM, IPPROTO_UDP }, 49 { PF_INET, SOCK_STREAM, IPPROTO_TCP }, 50 { PF_INET, SOCK_RAW, 0 }, 51 { PF_INET6, SOCK_DGRAM, IPPROTO_UDP }, 52 { PF_INET6, SOCK_STREAM, IPPROTO_TCP }, 53 { PF_INET6, SOCK_RAW, 0 }, 54 { -1, 0, 0, }, 55 }; 56 57 #define MATCH_FAMILY(a, b) ((a) == matches[(b)].family || (a) == PF_UNSPEC) 58 #define MATCH_PROTO(a, b) ((a) == matches[(b)].protocol || (a) == 0) 59 /* Do not match SOCK_RAW unless explicitely specified */ 60 #define MATCH_SOCKTYPE(a, b) ((a) == matches[(b)].socktype || ((a) == 0 && \ 61 matches[(b)].socktype != SOCK_RAW)) 62 63 struct async * 64 getaddrinfo_async(const char *hostname, const char *servname, 65 const struct addrinfo *hints, struct asr *asr) 66 { 67 struct asr_ctx *ac; 68 struct async *as; 69 70 ac = asr_use_resolver(asr); 71 if ((as = async_new(ac, ASR_GETADDRINFO)) == NULL) 72 goto abort; /* errno set */ 73 as->as_run = getaddrinfo_async_run; 74 75 if (hostname && (as->as.ai.hostname = strdup(hostname)) == NULL) 76 goto abort; /* errno set */ 77 if (servname && (as->as.ai.servname = strdup(servname)) == NULL) 78 goto abort; /* errno set */ 79 if (hints) 80 memmove(&as->as.ai.hints, hints, sizeof *hints); 81 else { 82 memset(&as->as.ai.hints, 0, sizeof as->as.ai.hints); 83 as->as.ai.hints.ai_family = PF_UNSPEC; 84 } 85 86 asr_ctx_unref(ac); 87 return (as); 88 abort: 89 if (as) 90 async_free(as); 91 asr_ctx_unref(ac); 92 return (NULL); 93 } 94 95 static int 96 getaddrinfo_async_run(struct async *as, struct async_res *ar) 97 { 98 #ifdef YP 99 static char *domain = NULL; 100 char *res; 101 int len; 102 #endif 103 const char *str; 104 struct addrinfo *ai; 105 int i, family, r; 106 char fqdn[MAXDNAME]; 107 FILE *f; 108 union { 109 struct sockaddr sa; 110 struct sockaddr_in sain; 111 struct sockaddr_in6 sain6; 112 } sa; 113 114 next: 115 switch(as->as_state) { 116 117 case ASR_STATE_INIT: 118 119 /* 120 * First, make sure the parameters are valid. 121 */ 122 123 as->as_count = 0; 124 125 if (as->as.ai.hostname == NULL && 126 as->as.ai.servname == NULL) { 127 ar->ar_gai_errno = EAI_NONAME; 128 async_set_state(as, ASR_STATE_HALT); 129 break; 130 } 131 132 ai = &as->as.ai.hints; 133 134 if (ai->ai_addrlen || 135 ai->ai_canonname || 136 ai->ai_addr || 137 ai->ai_next) { 138 ar->ar_gai_errno = EAI_BADHINTS; 139 async_set_state(as, ASR_STATE_HALT); 140 break; 141 } 142 143 if (ai->ai_flags & ~AI_MASK || 144 (ai->ai_flags & AI_CANONNAME && ai->ai_flags & AI_FQDN)) { 145 ar->ar_gai_errno = EAI_BADFLAGS; 146 async_set_state(as, ASR_STATE_HALT); 147 break; 148 } 149 150 if (ai->ai_family != PF_UNSPEC && 151 ai->ai_family != PF_INET && 152 ai->ai_family != PF_INET6) { 153 ar->ar_gai_errno = EAI_FAMILY; 154 async_set_state(as, ASR_STATE_HALT); 155 break; 156 } 157 158 if (ai->ai_socktype && 159 ai->ai_socktype != SOCK_DGRAM && 160 ai->ai_socktype != SOCK_STREAM && 161 ai->ai_socktype != SOCK_RAW) { 162 ar->ar_gai_errno = EAI_SOCKTYPE; 163 async_set_state(as, ASR_STATE_HALT); 164 break; 165 } 166 167 if (ai->ai_protocol && 168 ai->ai_protocol != IPPROTO_UDP && 169 ai->ai_protocol != IPPROTO_TCP) { 170 ar->ar_gai_errno = EAI_PROTOCOL; 171 async_set_state(as, ASR_STATE_HALT); 172 break; 173 } 174 175 if (ai->ai_socktype == SOCK_RAW && 176 as->as.ai.servname != NULL) { 177 ar->ar_gai_errno = EAI_SERVICE; 178 async_set_state(as, ASR_STATE_HALT); 179 break; 180 } 181 182 /* Make sure there is at least a valid combination */ 183 for (i = 0; matches[i].family != -1; i++) 184 if (MATCH_FAMILY(ai->ai_family, i) && 185 MATCH_SOCKTYPE(ai->ai_socktype, i) && 186 MATCH_PROTO(ai->ai_protocol, i)) 187 break; 188 if (matches[i].family == -1) { 189 ar->ar_gai_errno = EAI_BADHINTS; 190 async_set_state(as, ASR_STATE_HALT); 191 break; 192 } 193 194 if (ai->ai_protocol == 0 || ai->ai_protocol == IPPROTO_UDP) 195 as->as.ai.port_udp = get_port(as->as.ai.servname, "udp", 196 as->as.ai.hints.ai_flags & AI_NUMERICSERV); 197 if (ai->ai_protocol == 0 || ai->ai_protocol == IPPROTO_TCP) 198 as->as.ai.port_tcp = get_port(as->as.ai.servname, "tcp", 199 as->as.ai.hints.ai_flags & AI_NUMERICSERV); 200 if (as->as.ai.port_tcp == -2 || as->as.ai.port_udp == -2 || 201 (as->as.ai.port_tcp == -1 && as->as.ai.port_udp == -1) || 202 (ai->ai_protocol && (as->as.ai.port_udp == -1 || 203 as->as.ai.port_tcp == -1))) { 204 ar->ar_gai_errno = EAI_SERVICE; 205 async_set_state(as, ASR_STATE_HALT); 206 break; 207 } 208 209 ar->ar_gai_errno = 0; 210 211 /* If hostname is NULL, use local address */ 212 if (as->as.ai.hostname == NULL) { 213 for(family = iter_family(as, 1); 214 family != -1; 215 family = iter_family(as, 0)) { 216 /* 217 * We could use statically built sockaddrs for 218 * those, rather than parsing over and over. 219 */ 220 if (family == PF_INET) 221 str = (ai->ai_flags & AI_PASSIVE) ? \ 222 "0.0.0.0" : "127.0.0.1"; 223 else /* PF_INET6 */ 224 str = (ai->ai_flags & AI_PASSIVE) ? \ 225 "::" : "::1"; 226 /* This can't fail */ 227 sockaddr_from_str(&sa.sa, family, str); 228 if ((r = addrinfo_add(as, &sa.sa, NULL))) { 229 ar->ar_gai_errno = r; 230 break; 231 } 232 } 233 if (ar->ar_gai_errno == 0 && as->as_count == 0) { 234 ar->ar_gai_errno = EAI_NODATA; 235 } 236 async_set_state(as, ASR_STATE_HALT); 237 break; 238 } 239 240 /* Try numeric addresses first */ 241 for(family = iter_family(as, 1); 242 family != -1; 243 family = iter_family(as, 0)) { 244 245 if (sockaddr_from_str(&sa.sa, family, 246 as->as.ai.hostname) == -1) 247 continue; 248 249 if ((r = addrinfo_add(as, &sa.sa, NULL))) { 250 ar->ar_gai_errno = r; 251 } 252 break; 253 } 254 if (ar->ar_gai_errno || as->as_count) { 255 async_set_state(as, ASR_STATE_HALT); 256 break; 257 } 258 259 if (ai->ai_flags & AI_NUMERICHOST) { 260 ar->ar_gai_errno = EAI_FAIL; 261 async_set_state(as, ASR_STATE_HALT); 262 break; 263 } 264 265 /* start domain lookup */ 266 async_set_state(as, ASR_STATE_NEXT_DOMAIN); 267 break; 268 269 case ASR_STATE_NEXT_DOMAIN: 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_gai_errno = EAI_OVERFLOW; 277 async_set_state(as, ASR_STATE_HALT); 278 break; 279 } 280 if (as->as.ai.fqdn) 281 free(as->as.ai.fqdn); 282 if ((as->as.ai.fqdn = strdup(fqdn)) == NULL) { 283 ar->ar_gai_errno = EAI_MEMORY; 284 async_set_state(as, ASR_STATE_HALT); 285 break; 286 } 287 as->as_db_idx = 0; 288 async_set_state(as, ASR_STATE_NEXT_DB); 289 break; 290 291 case ASR_STATE_NEXT_DB: 292 if (asr_iter_db(as) == -1) { 293 async_set_state(as, ASR_STATE_NEXT_DOMAIN); 294 break; 295 } 296 as->as_family_idx = 0; 297 async_set_state(as, ASR_STATE_SAME_DB); 298 break; 299 300 case ASR_STATE_NEXT_FAMILY: 301 as->as_family_idx += 1; 302 if (as->as.ai.hints.ai_family != AF_UNSPEC || 303 AS_FAMILY(as) == -1) { 304 /* The family was specified, or we have tried all 305 * families with this DB. 306 */ 307 if (as->as_count) { 308 ar->ar_gai_errno = 0; 309 async_set_state(as, ASR_STATE_HALT); 310 } else 311 async_set_state(as, ASR_STATE_NEXT_DB); 312 break; 313 } 314 async_set_state(as, ASR_STATE_SAME_DB); 315 break; 316 317 case ASR_STATE_SAME_DB: 318 /* query the current DB again. */ 319 switch(AS_DB(as)) { 320 case ASR_DB_DNS: 321 family = (as->as.ai.hints.ai_family == AF_UNSPEC) ? 322 AS_FAMILY(as) : as->as.ai.hints.ai_family; 323 as->as.ai.subq = res_query_async_ctx(as->as.ai.fqdn, 324 C_IN, (family == AF_INET6) ? T_AAAA : T_A, NULL, 0, 325 as->as_ctx); 326 if (as->as.ai.subq == NULL) { 327 if (errno == ENOMEM) 328 ar->ar_gai_errno = EAI_MEMORY; 329 else 330 ar->ar_gai_errno = EAI_FAIL; 331 async_set_state(as, ASR_STATE_HALT); 332 break; 333 } 334 async_set_state(as, ASR_STATE_SUBQUERY); 335 break; 336 337 case ASR_DB_FILE: 338 f = fopen(as->as_ctx->ac_hostfile, "r"); 339 if (f == NULL) { 340 async_set_state(as, ASR_STATE_NEXT_DB); 341 break; 342 } 343 family = (as->as.ai.hints.ai_family == AF_UNSPEC) ? 344 AS_FAMILY(as) : as->as.ai.hints.ai_family; 345 346 r = addrinfo_from_file(as, family, f); 347 if (r == -1) { 348 if (errno == ENOMEM) 349 ar->ar_gai_errno = EAI_MEMORY; 350 else 351 ar->ar_gai_errno = EAI_FAIL; 352 async_set_state(as, ASR_STATE_HALT); 353 } else 354 async_set_state(as, ASR_STATE_NEXT_FAMILY); 355 fclose(f); 356 break; 357 358 #ifdef YP 359 case ASR_DB_YP: 360 if (!domain && _yp_check(&domain) == 0) { 361 async_set_state(as, ASR_STATE_NEXT_DB); 362 break; 363 } 364 family = (as->as.ai.hints.ai_family == AF_UNSPEC) ? 365 AS_FAMILY(as) : as->as.ai.hints.ai_family; 366 /* XXX 367 * ipnodes.byname could also contain IPv4 address 368 */ 369 r = yp_match(domain, (family == AF_INET6) ? 370 "ipnodes.byname" : "hosts.byname", 371 as->as.ai.hostname, strlen(as->as.ai.hostname), 372 &res, &len); 373 if (r == 0) { 374 r = addrinfo_from_yp(as, family, res); 375 free(res); 376 if (r == -1) { 377 if (errno == ENOMEM) 378 ar->ar_gai_errno = EAI_MEMORY; 379 else 380 ar->ar_gai_errno = EAI_FAIL; 381 async_set_state(as, ASR_STATE_HALT); 382 break; 383 } 384 } 385 async_set_state(as, ASR_STATE_NEXT_FAMILY); 386 break; 387 #endif 388 default: 389 async_set_state(as, ASR_STATE_NEXT_DB); 390 } 391 break; 392 393 case ASR_STATE_SUBQUERY: 394 if ((r = async_run(as->as.ai.subq, ar)) == ASYNC_COND) 395 return (ASYNC_COND); 396 as->as.ai.subq = NULL; 397 398 if (ar->ar_datalen == -1) { 399 async_set_state(as, ASR_STATE_NEXT_DB); 400 break; 401 } 402 403 r = addrinfo_from_pkt(as, ar->ar_data, ar->ar_datalen); 404 if (r == -1) { 405 if (errno == ENOMEM) 406 ar->ar_gai_errno = EAI_MEMORY; 407 else 408 ar->ar_gai_errno = EAI_FAIL; 409 async_set_state(as, ASR_STATE_HALT); 410 } else 411 async_set_state(as, ASR_STATE_NEXT_FAMILY); 412 free(ar->ar_data); 413 break; 414 415 case ASR_STATE_NOT_FOUND: 416 /* No result found. Maybe we can try again. */ 417 if (as->as.ai.flags & ASYNC_AGAIN) 418 ar->ar_gai_errno = EAI_AGAIN; 419 else 420 ar->ar_gai_errno = EAI_NODATA; 421 async_set_state(as, ASR_STATE_HALT); 422 break; 423 424 case ASR_STATE_HALT: 425 if (ar->ar_gai_errno == 0) { 426 ar->ar_count = as->as_count; 427 ar->ar_addrinfo = as->as.ai.aifirst; 428 as->as.ai.aifirst = NULL; 429 } else { 430 ar->ar_count = 0; 431 ar->ar_addrinfo = NULL; 432 } 433 return (ASYNC_DONE); 434 435 default: 436 ar->ar_errno = EOPNOTSUPP; 437 ar->ar_gai_errno = EAI_SYSTEM; 438 async_set_state(as, ASR_STATE_HALT); 439 break; 440 } 441 goto next; 442 } 443 444 /* 445 * Retreive the port number for the service name "servname" and 446 * the protocol "proto". 447 */ 448 static int 449 get_port(const char *servname, const char *proto, int numonly) 450 { 451 struct servent se; 452 struct servent_data sed; 453 int port, r; 454 const char* e; 455 456 if (servname == NULL) 457 return (0); 458 459 e = NULL; 460 port = strtonum(servname, 0, USHRT_MAX, &e); 461 if (e == NULL) 462 return (port); 463 if (errno == ERANGE) 464 return (-2); /* invalid */ 465 if (numonly) 466 return (-2); 467 468 memset(&sed, 0, sizeof(sed)); 469 r = getservbyname_r(servname, proto, &se, &sed); 470 port = ntohs(se.s_port); 471 endservent_r(&sed); 472 473 if (r == -1) 474 return (-1); /* not found */ 475 476 return (port); 477 } 478 479 /* 480 * Iterate over the address families that are to be queried. Use the 481 * list on the async context, unless a specific family was given in hints. 482 */ 483 static int 484 iter_family(struct async *as, int first) 485 { 486 if (first) { 487 as->as_family_idx = 0; 488 if (as->as.ai.hints.ai_family != PF_UNSPEC) 489 return as->as.ai.hints.ai_family; 490 return AS_FAMILY(as); 491 } 492 493 if (as->as.ai.hints.ai_family != PF_UNSPEC) 494 return (-1); 495 496 as->as_family_idx++; 497 498 return AS_FAMILY(as); 499 } 500 501 /* 502 * Use the sockaddr at "sa" to extend the result list on the "as" context, 503 * with the specified canonical name "cname". This function adds one 504 * entry per protocol/socktype match. 505 */ 506 static int 507 addrinfo_add(struct async *as, const struct sockaddr *sa, const char *cname) 508 { 509 struct addrinfo *ai; 510 int i, port; 511 512 for(i = 0; matches[i].family != -1; i++) { 513 if (matches[i].family != sa->sa_family || 514 !MATCH_SOCKTYPE(as->as.ai.hints.ai_socktype, i) || 515 !MATCH_PROTO(as->as.ai.hints.ai_protocol, i)) 516 continue; 517 518 if (matches[i].protocol == IPPROTO_TCP) 519 port = as->as.ai.port_tcp; 520 else if (matches[i].protocol == IPPROTO_UDP) 521 port = as->as.ai.port_udp; 522 else 523 port = 0; 524 525 /* servname specified, but not defined for this protocol */ 526 if (port == -1) 527 continue; 528 529 ai = calloc(1, sizeof(*ai) + sa->sa_len); 530 if (ai == NULL) 531 return (EAI_MEMORY); 532 ai->ai_family = sa->sa_family; 533 ai->ai_socktype = matches[i].socktype; 534 ai->ai_protocol = matches[i].protocol; 535 ai->ai_addrlen = sa->sa_len; 536 ai->ai_addr = (void*)(ai + 1); 537 if (cname && 538 as->as.ai.hints.ai_flags & (AI_CANONNAME | AI_FQDN)) { 539 if ((ai->ai_canonname = strdup(cname)) == NULL) { 540 free(ai); 541 return (EAI_MEMORY); 542 } 543 } 544 memmove(ai->ai_addr, sa, sa->sa_len); 545 if (sa->sa_family == PF_INET) 546 ((struct sockaddr_in *)ai->ai_addr)->sin_port = 547 htons(port); 548 else if (sa->sa_family == PF_INET6) 549 ((struct sockaddr_in6 *)ai->ai_addr)->sin6_port = 550 htons(port); 551 552 if (as->as.ai.aifirst == NULL) 553 as->as.ai.aifirst = ai; 554 if (as->as.ai.ailast) 555 as->as.ai.ailast->ai_next = ai; 556 as->as.ai.ailast = ai; 557 as->as_count += 1; 558 } 559 560 return (0); 561 } 562 563 static int 564 addrinfo_from_file(struct async *as, int family, FILE *f) 565 { 566 char *tokens[MAXTOKEN], *c; 567 int n, i; 568 union { 569 struct sockaddr sa; 570 struct sockaddr_in sain; 571 struct sockaddr_in6 sain6; 572 } u; 573 574 for(;;) { 575 n = asr_parse_namedb_line(f, tokens, MAXTOKEN); 576 if (n == -1) 577 break; /* ignore errors reading the file */ 578 579 for (i = 1; i < n; i++) { 580 if (strcasecmp(as->as.ai.fqdn, tokens[i])) 581 continue; 582 if (sockaddr_from_str(&u.sa, family, tokens[0]) == -1) 583 continue; 584 break; 585 } 586 if (i == n) 587 continue; 588 589 if (as->as.ai.hints.ai_flags & AI_CANONNAME) 590 c = tokens[1]; 591 else if (as->as.ai.hints.ai_flags & AI_FQDN) 592 c = as->as.ai.fqdn; 593 else 594 c = NULL; 595 596 if (addrinfo_add(as, &u.sa, c)) 597 return (-1); /* errno set */ 598 } 599 return (0); 600 } 601 602 static int 603 addrinfo_from_pkt(struct async *as, char *pkt, size_t pktlen) 604 { 605 struct packed p; 606 struct header h; 607 struct query q; 608 struct rr rr; 609 int i; 610 union { 611 struct sockaddr sa; 612 struct sockaddr_in sain; 613 struct sockaddr_in6 sain6; 614 } u; 615 char buf[MAXDNAME], *c; 616 617 packed_init(&p, pkt, pktlen); 618 unpack_header(&p, &h); 619 for(; h.qdcount; h.qdcount--) 620 unpack_query(&p, &q); 621 622 for (i = 0; i < h.ancount; i++) { 623 unpack_rr(&p, &rr); 624 if (rr.rr_type != q.q_type || 625 rr.rr_class != q.q_class) 626 continue; 627 628 memset(&u, 0, sizeof u); 629 if (rr.rr_type == T_A) { 630 u.sain.sin_len = sizeof u.sain; 631 u.sain.sin_family = AF_INET; 632 u.sain.sin_addr = rr.rr.in_a.addr; 633 u.sain.sin_port = 0; 634 } else if (rr.rr_type == T_AAAA) { 635 u.sain6.sin6_len = sizeof u.sain6; 636 u.sain6.sin6_family = AF_INET6; 637 u.sain6.sin6_addr = rr.rr.in_aaaa.addr6; 638 u.sain6.sin6_port = 0; 639 } else 640 continue; 641 642 if (as->as.ai.hints.ai_flags & AI_CANONNAME) { 643 asr_strdname(rr.rr_dname, buf, sizeof buf); 644 buf[strlen(buf) - 1] = '\0'; 645 c = buf; 646 } else if (as->as.ai.hints.ai_flags & AI_FQDN) 647 c = as->as.ai.fqdn; 648 else 649 c = NULL; 650 651 if (addrinfo_add(as, &u.sa, c)) 652 return (-1); /* errno set */ 653 } 654 return (0); 655 } 656 657 #ifdef YP 658 static int 659 strsplit(char *line, char **tokens, int ntokens) 660 { 661 int ntok; 662 char *cp, **tp; 663 664 for(cp = line, tp = tokens, ntok = 0; 665 ntok < ntokens && (*tp = strsep(&cp, " \t")) != NULL; ) 666 if (**tp != '\0') { 667 tp++; 668 ntok++; 669 } 670 671 return (ntok); 672 } 673 674 static int 675 addrinfo_from_yp(struct async *as, int family, char *line) 676 { 677 char *next, *tokens[MAXTOKEN], *c; 678 int i, ntok; 679 union { 680 struct sockaddr sa; 681 struct sockaddr_in sain; 682 struct sockaddr_in6 sain6; 683 } u; 684 685 for(next = line; line; line = next) { 686 if ((next = strchr(line, '\n'))) { 687 *next = '\0'; 688 next += 1; 689 } 690 ntok = strsplit(line, tokens, MAXTOKEN); 691 if (ntok < 2) 692 continue; 693 694 if (sockaddr_from_str(&u.sa, family, tokens[0]) == -1) 695 continue; 696 697 if (as->as.ai.hints.ai_flags & AI_CANONNAME) 698 c = tokens[1]; 699 else if (as->as.ai.hints.ai_flags & AI_FQDN) 700 c = as->as.ai.fqdn; 701 else 702 c = NULL; 703 704 if (addrinfo_add(as, &u.sa, c)) 705 return (-1); /* errno set */ 706 } 707 return (0); 708 } 709 #endif 710