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