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