1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 /* 26 * Copyright (c) 2013, Joyent, Inc. All rights reserved. 27 * Copyright 2023 Oxide Computer Company 28 */ 29 30 /* 31 * dns_common.c 32 */ 33 34 #include "dns_common.h" 35 #include <sys/types.h> 36 #include <sys/socket.h> 37 #include <ifaddrs.h> 38 #include <net/if.h> 39 40 #pragma weak dn_expand 41 #pragma weak res_ninit 42 #pragma weak res_ndestroy 43 #pragma weak res_nsearch 44 #pragma weak res_nclose 45 #pragma weak ns_get16 46 #pragma weak ns_get32 47 #pragma weak __ns_get16 48 #pragma weak __ns_get32 49 50 #define DNS_ALIASES 0 51 #define DNS_ADDRLIST 1 52 #define DNS_MAPDLIST 2 53 54 #ifndef tolower 55 #define tolower(c) ((c) >= 'A' && (c) <= 'Z' ? (c) | 0x20 : (c)) 56 #endif 57 58 static int 59 dns_netdb_aliases(char **from_list, char **to_list, char **aliaspp, int type, 60 int *count, int af_type) 61 { 62 char *fstr; 63 int cnt = 0; 64 size_t len; 65 66 *count = 0; 67 if ((char *)to_list >= *aliaspp) 68 return (NSS_STR_PARSE_ERANGE); 69 70 for (fstr = from_list[cnt]; fstr != NULL; fstr = from_list[cnt]) { 71 if (type == DNS_ALIASES) { 72 len = strlen(fstr) + 1; 73 } else { 74 len = (af_type == AF_INET) ? sizeof (struct in_addr) : 75 sizeof (struct in6_addr); 76 } 77 *aliaspp -= len; 78 to_list[cnt] = *aliaspp; 79 if (*aliaspp <= (char *)&to_list[cnt+1]) 80 return (NSS_STR_PARSE_ERANGE); 81 if (type == DNS_MAPDLIST) { 82 /* LINTED: E_BAD_PTR_CAST_ALIGN */ 83 struct in6_addr *addr6p = (struct in6_addr *)*aliaspp; 84 85 (void) memset(addr6p, '\0', sizeof (struct in6_addr)); 86 (void) memcpy(&addr6p->s6_addr[12], fstr, 87 sizeof (struct in_addr)); 88 addr6p->s6_addr[10] = 0xffU; 89 addr6p->s6_addr[11] = 0xffU; 90 ++cnt; 91 } else { 92 (void) memcpy (*aliaspp, fstr, len); 93 ++cnt; 94 } 95 } 96 to_list[cnt] = NULL; 97 98 *count = cnt; 99 if (cnt == 0) 100 return (NSS_STR_PARSE_PARSE); 101 102 return (NSS_STR_PARSE_SUCCESS); 103 } 104 105 106 int 107 ent2result(struct hostent *he, nss_XbyY_args_t *argp, int af_type) 108 { 109 char *buffer, *limit; 110 int buflen = argp->buf.buflen; 111 int ret, count; 112 size_t len; 113 struct hostent *host; 114 struct in_addr *addrp; 115 struct in6_addr *addrp6; 116 117 limit = argp->buf.buffer + buflen; 118 host = (struct hostent *)argp->buf.result; 119 buffer = argp->buf.buffer; 120 121 /* h_addrtype and h_length */ 122 host->h_addrtype = af_type; 123 host->h_length = (af_type == AF_INET) ? sizeof (struct in_addr) : 124 sizeof (struct in6_addr); 125 126 /* h_name */ 127 len = strlen(he->h_name) + 1; 128 host->h_name = buffer; 129 if (host->h_name + len >= limit) 130 return (NSS_STR_PARSE_ERANGE); 131 (void) memcpy(host->h_name, he->h_name, len); 132 buffer += len; 133 134 /* h_addr_list */ 135 if (af_type == AF_INET) { 136 addrp = (struct in_addr *)ROUND_DOWN(limit, sizeof (*addrp)); 137 host->h_addr_list = (char **)ROUND_UP(buffer, sizeof (char **)); 138 ret = dns_netdb_aliases(he->h_addr_list, host->h_addr_list, 139 (char **)&addrp, DNS_ADDRLIST, &count, af_type); 140 if (ret != NSS_STR_PARSE_SUCCESS) 141 return (ret); 142 /* h_aliases */ 143 host->h_aliases = host->h_addr_list + count + 1; 144 ret = dns_netdb_aliases(he->h_aliases, host->h_aliases, 145 (char **)&addrp, DNS_ALIASES, &count, af_type); 146 } else { 147 addrp6 = (struct in6_addr *)ROUND_DOWN(limit, sizeof (*addrp6)); 148 host->h_addr_list = (char **)ROUND_UP(buffer, sizeof (char **)); 149 if (he->h_addrtype == AF_INET && af_type == AF_INET6) { 150 ret = dns_netdb_aliases(he->h_addr_list, 151 host->h_addr_list, (char **)&addrp6, 152 DNS_MAPDLIST, &count, af_type); 153 } else { 154 ret = dns_netdb_aliases(he->h_addr_list, 155 host->h_addr_list, (char **)&addrp6, 156 DNS_ADDRLIST, &count, af_type); 157 } 158 if (ret != NSS_STR_PARSE_SUCCESS) 159 return (ret); 160 /* h_aliases */ 161 host->h_aliases = host->h_addr_list + count + 1; 162 ret = dns_netdb_aliases(he->h_aliases, host->h_aliases, 163 (char **)&addrp6, DNS_ALIASES, &count, af_type); 164 } 165 if (ret == NSS_STR_PARSE_PARSE) 166 ret = NSS_STR_PARSE_SUCCESS; 167 168 return (ret); 169 } 170 171 /* 172 * Convert the hostent structure into string in the following 173 * format: 174 * 175 * IP-address official-host-name nicknames ... 176 * 177 * If more than one IP-addresses matches the official-host-name, 178 * the above line will be followed by: 179 * IP-address-1 official-host-name 180 * IP-address-2 official-host-name 181 * ... 182 * 183 * This is so that the str2hostent function in libnsl 184 * can convert the string back to the original hostent 185 * data. 186 */ 187 int 188 ent2str(struct hostent *hp, nss_XbyY_args_t *ap, int af_type) 189 { 190 char **p; 191 char obuf[INET6_ADDRSTRLEN]; 192 void *addr; 193 struct in_addr in4; 194 int af; 195 int n; 196 const char *res; 197 char **q; 198 int l = ap->buf.buflen; 199 char *s = ap->buf.buffer; 200 201 /* 202 * for "hosts" lookup, we only want address type of 203 * AF_INET. For "ipnodes", we can have both AF_INET 204 * and AF_INET6. 205 */ 206 if (af_type == AF_INET && hp->h_addrtype != AF_INET) 207 return (NSS_STR_PARSE_PARSE); 208 209 for (p = hp->h_addr_list; *p != 0; p++) { 210 211 if (p != hp->h_addr_list) { 212 *s = '\n'; 213 s++; 214 l--; 215 } 216 217 if (hp->h_addrtype == AF_INET6) { 218 /* LINTED: E_BAD_PTR_CAST_ALIGN */ 219 if (IN6_IS_ADDR_V4MAPPED((struct in6_addr *)*p)) { 220 /* LINTED: E_BAD_PTR_CAST_ALIGN */ 221 IN6_V4MAPPED_TO_INADDR((struct in6_addr *)*p, 222 &in4); 223 af = AF_INET; 224 addr = &in4; 225 } else { 226 af = AF_INET6; 227 addr = *p; 228 } 229 } else { 230 af = AF_INET; 231 addr = *p; 232 } 233 res = inet_ntop(af, addr, obuf, sizeof (obuf)); 234 if (res == NULL) 235 return (NSS_STR_PARSE_PARSE); 236 237 if ((n = snprintf(s, l, "%s", res)) >= l) 238 return (NSS_STR_PARSE_ERANGE); 239 l -= n; 240 s += n; 241 if (hp->h_name != NULL && *hp->h_name != '\0') { 242 if ((n = snprintf(s, l, " %s", hp->h_name)) >= l) 243 return (NSS_STR_PARSE_ERANGE); 244 l -= n; 245 s += n; 246 } 247 if (p == hp->h_addr_list) { 248 for (q = hp->h_aliases; q && *q; q++) { 249 if ((n = snprintf(s, l, " %s", *q)) >= l) 250 return (NSS_STR_PARSE_ERANGE); 251 l -= n; 252 s += n; 253 } 254 } 255 } 256 257 ap->returnlen = s - ap->buf.buffer; 258 return (NSS_STR_PARSE_SUCCESS); 259 } 260 261 nss_backend_t * 262 _nss_dns_constr(dns_backend_op_t ops[], int n_ops) 263 { 264 dns_backend_ptr_t be; 265 266 if ((be = (dns_backend_ptr_t)malloc(sizeof (*be))) == 0) 267 return (0); 268 269 be->ops = ops; 270 be->n_ops = n_ops; 271 return ((nss_backend_t *)be); 272 } 273 274 /* 275 * name_is_alias(aliases_ptr, name_ptr) 276 * Verify name matches an alias in the provided aliases list. 277 * 278 * Within DNS there should be only one canonical name, aliases should 279 * all refer to the one canonical. However alias chains do occur and 280 * pre BIND 9 servers may also respond with multiple CNAMEs. This 281 * routine checks if a given name has been provided as a CNAME in the 282 * response. This assumes that the chains have been sent in-order. 283 * 284 * INPUT: 285 * aliases_ptr: space separated list of alias names. 286 * name_ptr: name to look for in aliases_ptr list. 287 * RETURNS: NSS_SUCCESS or NSS_NOTFOUND 288 * NSS_SUCCESS indicates that the name is listed in the collected aliases. 289 */ 290 static nss_status_t 291 name_is_alias(char *aliases_ptr, char *name_ptr) 292 { 293 char *host_ptr; 294 /* Loop through alias string and compare it against host string. */ 295 while (*aliases_ptr != '\0') { 296 host_ptr = name_ptr; 297 298 /* Compare name with alias. */ 299 while (tolower(*host_ptr) == tolower(*aliases_ptr) && 300 *host_ptr != '\0') { 301 host_ptr++; 302 aliases_ptr++; 303 } 304 305 /* 306 * If name was exhausted and the next character in the 307 * alias is either the end-of-string or space 308 * character then we have a match. 309 */ 310 if (*host_ptr == '\0' && 311 (*aliases_ptr == '\0' || *aliases_ptr == ' ')) { 312 return (NSS_SUCCESS); 313 } 314 315 /* Alias did not match, step over remainder of alias. */ 316 while (*aliases_ptr != ' ' && *aliases_ptr != '\0') 317 aliases_ptr++; 318 /* Step over separator character. */ 319 while (*aliases_ptr == ' ') aliases_ptr++; 320 } 321 return (NSS_NOTFOUND); 322 } 323 324 static int 325 _nss_has_interfaces(boolean_t *v4, boolean_t *v6) 326 { 327 struct ifaddrs *ifp, *i; 328 struct in_addr in4; 329 struct in6_addr in6; 330 const struct in6_addr in6addr_any = IN6ADDR_ANY_INIT; 331 332 *v4 = *v6 = B_FALSE; 333 334 if (getifaddrs(&ifp) != 0) 335 return (-1); 336 337 for (i = ifp; i != NULL; i = i->ifa_next) { 338 if (i->ifa_flags & IFF_LOOPBACK) 339 continue; 340 if ((i->ifa_flags & IFF_UP) == 0) 341 continue; 342 343 if (i->ifa_addr->sa_family == AF_INET) { 344 if (*v4 != B_FALSE) 345 continue; 346 347 if (((struct sockaddr_in *)i->ifa_addr)-> 348 sin_addr.s_addr == INADDR_ANY) 349 continue; 350 *v4 = B_TRUE; 351 } 352 353 if (i->ifa_addr->sa_family == AF_INET6) { 354 if (*v6 != B_FALSE) 355 continue; 356 357 if (memcmp(&in6addr_any, 358 &((struct sockaddr_in6 *)i->ifa_addr)->sin6_addr, 359 sizeof (struct in6_addr)) == 0) 360 continue; 361 *v6 = B_TRUE; 362 } 363 } 364 365 freeifaddrs(ifp); 366 return (0); 367 } 368 369 /* 370 * nss_dns_gethost_withttl(void *buffer, size_t bufsize, int ipnode) 371 * nss2 get hosts/ipnodes with ttl backend DNS search engine. 372 * 373 * This API is given a pointer to a packed buffer, and the buffer size 374 * It's job is to perform the appropriate res_nsearch, extract the 375 * results and build a unmarshalled hosts/ipnodes result buffer. 376 * Additionally in the extended results a nssuint_t ttl is placed. 377 * This ttl is the lessor of the ttl's extracted from the result. 378 * 379 * RETURNS: NSS_SUCCESS or NSS_ERROR 380 * If an NSS_ERROR result is returned, nscd is expected 381 * to resubmit the gethosts request using the old style 382 * nsswitch lookup format. 383 */ 384 385 nss_status_t 386 _nss_dns_gethost_withttl(void *buffer, size_t bufsize, int ipnode) 387 { 388 /* nss buffer variables */ 389 nss_pheader_t *pbuf = (nss_pheader_t *)buffer; 390 nss_XbyY_args_t arg; 391 char *dbname; 392 int dbop; 393 nss_status_t sret; 394 size_t bsize, blen; 395 char *bptr; 396 /* resolver query variables */ 397 struct __res_state stat, *statp; /* dns state block */ 398 union msg { 399 uchar_t buf[NS_MAXMSG]; /* max legal DNS answer size */ 400 HEADER h; 401 } resbuf; 402 char aliases[NS_MAXMSG]; /* set of aliases */ 403 const char *name; 404 int qtype; 405 /* answer parsing variables */ 406 HEADER *hp; 407 uchar_t *cp; /* current location in message */ 408 uchar_t *bom; /* start of message */ 409 uchar_t *eom; /* end of message */ 410 uchar_t *eor; /* end of record */ 411 int ancount, qdcount; 412 int type, class; 413 nssuint_t nttl, ttl, *pttl; /* The purpose of this API */ 414 int n, ret; 415 const char *np; 416 /* temporary buffers */ 417 char nbuf[INET6_ADDRSTRLEN]; /* address parser */ 418 char host[MAXHOSTNAMELEN]; /* result host name */ 419 char ans[MAXHOSTNAMELEN]; /* record name */ 420 char aname[MAXHOSTNAMELEN]; /* alias result (C_NAME) */ 421 /* misc variables */ 422 int af; 423 char *ap, *apc; 424 int hlen = 0, alen, iplen, len, isans; 425 boolean_t has_v4 = B_FALSE, has_v6 = B_FALSE; 426 int flags, family, pass2 = 0; 427 428 statp = &stat; 429 (void) memset(statp, '\0', sizeof (struct __res_state)); 430 if (res_ninit(statp) == -1) { 431 return (NSS_ERROR); 432 } 433 434 ap = apc = (char *)aliases; 435 alen = 0; 436 ttl = (nssuint_t)0xFFFFFFF; /* start w/max, find smaller */ 437 438 /* save space for ttl otherwise, why bother... */ 439 bsize = pbuf->data_len - sizeof (nssuint_t); 440 bptr = (char *)buffer + pbuf->data_off; 441 blen = 0; 442 sret = nss_packed_getkey(buffer, bufsize, &dbname, &dbop, &arg); 443 if (sret != NSS_SUCCESS) { 444 res_ndestroy(statp); 445 return (NSS_ERROR); 446 } 447 448 /* 449 * There may be flags set when we are handling ipnode. There are three 450 * different values for flags: 451 * 452 * o AI_V4MAPPED 453 * o AI_ALL 454 * o AI_ADDRCONFIG 455 * 456 * The first two only have a meaning when af_family is ipv6. The latter 457 * means something in both cases. These flags are documented in 458 * getipnodebyname(3SOCKET), though the combinations leave a little 459 * something to be desired. It would be great if we could actually use 460 * getipnodebyname directly here since it already knows how to handle 461 * this kind of logic; however, we're not quite so lucky. Ideally we 462 * would add such an interface to libresolv.so.2 to handle this kind of 463 * thing, but that's rather painful as well. We'll summarize what has to 464 * happen below: 465 * 466 * AI_ALL is only meaningful when AI_V4MAPPED is also specified. Both 467 * are ignored if the family is not AF_INET6 468 * 469 * family == AF_INET, flags | AI_ADDRCONFIG 470 * - lookup A records iff we have v4 plumbed 471 * family == AF_INET, !(flags | AI_ADDRCONFIG) 472 * - lookup A records 473 * family == AF_INET6, flags == 0 || flags == AI_ALL 474 * - lookup AAAA records 475 * family == AF_INET6, flags | AI_V4MAPPED 476 * - lookup AAAA, if none, lookup A 477 * family == AF_INET6, flags | AI_ADDRCONFIG 478 * - lookup AAAA records if ipv6 479 * family == AF_INET6, flags | AI_V4MAPPED && flags | AI_ALL 480 * - lookup AAAA records, lookup A records 481 * family == AF_INET6, flags | AI_V4MAPPED && flags | AI_ADDRCONFIG 482 * - lookup AAAA records if ipv6 483 * - If no AAAA && ipv4 exists, lookup A 484 * family == AF_INET6, flags | AI_V4MAPPED && flags | AI_ADDRCONFIG && 485 * flags | AI_ALL 486 * - lookup AAAA records if ipv6 487 * - loookup A records if ipv4 488 */ 489 if (ipnode) { 490 /* initially only handle the simple cases */ 491 name = arg.key.ipnode.name; 492 flags = arg.key.ipnode.flags; 493 family = arg.key.ipnode.af_family; 494 if (flags != 0) { 495 /* 496 * Figure out our first pass. We'll determine if we need 497 * to do a second pass afterwards once we successfully 498 * finish our first pass. 499 */ 500 if ((flags & AI_ADDRCONFIG) != 0) { 501 if (_nss_has_interfaces(&has_v4, &has_v6) != 502 0) { 503 res_ndestroy(statp); 504 return (NSS_ERROR); 505 } 506 /* Impossible situations... */ 507 if (family == AF_INET && has_v4 == B_FALSE) { 508 res_ndestroy(statp); 509 return (NSS_NOTFOUND); 510 } 511 if (family == AF_INET6 && has_v6 == B_FALSE && 512 !(flags & AI_V4MAPPED)) { 513 res_ndestroy(statp); 514 return (NSS_NOTFOUND); 515 } 516 if (family == AF_INET6 && has_v6) 517 qtype = T_AAAA; 518 if (family == AF_INET || (family == AF_INET6 && 519 has_v6 == B_FALSE && flags & AI_V4MAPPED)) 520 qtype = T_A; 521 } else { 522 has_v4 = has_v6 = B_TRUE; 523 if (family == AF_INET6) 524 qtype = T_AAAA; 525 else 526 qtype = T_A; 527 } 528 } else { 529 if (family == AF_INET6) 530 qtype = T_AAAA; 531 else 532 qtype = T_A; 533 } 534 } else { 535 name = arg.key.name; 536 qtype = T_A; 537 } 538 539 searchagain: 540 ret = res_nsearch(statp, name, C_IN, qtype, resbuf.buf, NS_MAXMSG); 541 if (ret == -1) { 542 /* 543 * We want to continue on unless we got NO_RECOVERY. Otherwise, 544 * HOST_NOT_FOUND, TRY_AGAIN, and NO_DATA all suggest to me that 545 * we should keep going. 546 */ 547 if (statp->res_h_errno == NO_RECOVERY) { 548 /* else lookup error - handle in general code */ 549 res_ndestroy(statp); 550 return (NSS_ERROR); 551 } 552 553 /* 554 * We found something on our first pass. Make sure that we do 555 * not clobber this information. This ultimately means that we 556 * were successful. 557 */ 558 if (pass2 == 2) 559 goto out; 560 561 /* 562 * If we're on the second pass (eg. we need to check both for A 563 * and AAAA records), or we were only ever doing a search for 564 * one type of record and are not supposed to do a second pass, 565 * then we need to return that we couldn't find anything to the 566 * user. 567 */ 568 if (pass2 == 1 || flags == 0 || family == AF_INET || 569 (family == AF_INET6 && !(flags & AI_V4MAPPED))) { 570 pbuf->p_herrno = HOST_NOT_FOUND; 571 pbuf->p_status = NSS_NOTFOUND; 572 pbuf->data_len = 0; 573 res_ndestroy(statp); 574 return (NSS_NOTFOUND); 575 } 576 577 /* 578 * If we were only requested to search for flags on an IPv6 579 * interface or we have no IPv4 interface, we stick to only 580 * doing a single pass and bail now. 581 */ 582 if ((flags & AI_ADDRCONFIG) && !(flags & AI_ALL) && 583 has_v4 == B_FALSE) { 584 pbuf->p_herrno = HOST_NOT_FOUND; 585 pbuf->p_status = NSS_NOTFOUND; 586 pbuf->data_len = 0; 587 res_ndestroy(statp); 588 return (NSS_NOTFOUND); 589 } 590 qtype = T_A; 591 flags = 0; 592 pass2 = 1; 593 goto searchagain; 594 } 595 596 cp = resbuf.buf; 597 hp = (HEADER *)&resbuf.h; 598 bom = cp; 599 eom = cp + ret; 600 601 ancount = ntohs(hp->ancount); 602 qdcount = ntohs(hp->qdcount); 603 cp += HFIXEDSZ; 604 if (qdcount != 1) { 605 res_ndestroy(statp); 606 return (NSS_ERROR); 607 } 608 n = dn_expand(bom, eom, cp, host, MAXHOSTNAMELEN); 609 if (n < 0) { 610 res_ndestroy(statp); 611 return (NSS_ERROR); 612 } else 613 hlen = strlen(host); 614 /* no host name is an error, return */ 615 if (hlen <= 0) { 616 res_ndestroy(statp); 617 return (NSS_ERROR); 618 } 619 cp += n + QFIXEDSZ; 620 if (cp > eom) { 621 res_ndestroy(statp); 622 return (NSS_ERROR); 623 } 624 while (ancount-- > 0 && cp < eom && blen < bsize) { 625 n = dn_expand(bom, eom, cp, ans, MAXHOSTNAMELEN); 626 if (n > 0) { 627 /* 628 * Check that the expanded name is either the 629 * name we asked for or a learned alias. 630 */ 631 if ((isans = strncasecmp(host, ans, hlen)) != 0 && 632 (alen == 0 || name_is_alias(aliases, ans) 633 == NSS_NOTFOUND)) { 634 res_ndestroy(statp); 635 return (NSS_ERROR); /* spoof? */ 636 } 637 } 638 cp += n; 639 /* bounds check */ 640 type = ns_get16(cp); /* type */ 641 cp += INT16SZ; 642 class = ns_get16(cp); /* class */ 643 cp += INT16SZ; 644 nttl = (nssuint_t)ns_get32(cp); /* ttl in sec */ 645 if (nttl < ttl) 646 ttl = nttl; 647 cp += INT32SZ; 648 n = ns_get16(cp); /* len */ 649 cp += INT16SZ; 650 if (class != C_IN) { 651 cp += n; 652 continue; 653 } 654 eor = cp + n; 655 if (type == T_CNAME) { 656 /* 657 * The name looked up is really an alias and the 658 * canonical name should be in the RDATA. 659 * A canonical name may have several aliases but an 660 * alias should only have one canonical name. 661 * However multiple CNAMEs and CNAME chains do exist! 662 * 663 * Just error out on attempted buffer overflow exploit, 664 * generic code will syslog. 665 * 666 */ 667 n = dn_expand(bom, eor, cp, aname, MAXHOSTNAMELEN); 668 if (n > 0 && (len = strlen(aname)) > 0) { 669 if (isans == 0) { /* host matched ans. */ 670 /* 671 * Append host to alias list. 672 */ 673 if (alen + hlen + 2 > NS_MAXMSG) { 674 res_ndestroy(statp); 675 return (NSS_ERROR); 676 } 677 *apc++ = ' '; 678 alen++; 679 (void) strlcpy(apc, host, 680 NS_MAXMSG - alen); 681 alen += hlen; 682 apc += hlen; 683 } 684 /* 685 * Overwrite host with canonical name. 686 */ 687 if (strlcpy(host, aname, MAXHOSTNAMELEN) >= 688 MAXHOSTNAMELEN) { 689 res_ndestroy(statp); 690 return (NSS_ERROR); 691 } 692 hlen = len; 693 } 694 cp += n; 695 continue; 696 } 697 if (type != qtype) { 698 cp += n; 699 continue; 700 } 701 /* check data size */ 702 if ((type == T_A && n != INADDRSZ) || 703 (type == T_AAAA && n != IN6ADDRSZ)) { 704 cp += n; 705 continue; 706 } 707 af = (type == T_A ? AF_INET : AF_INET6); 708 np = inet_ntop(af, (void *)cp, nbuf, INET6_ADDRSTRLEN); 709 if (np == NULL) { 710 res_ndestroy(statp); 711 return (NSS_ERROR); 712 } 713 cp += n; 714 /* append IP host aliases to results */ 715 iplen = strlen(np); 716 /* ip <SP> hostname [<SP>][aliases] */ 717 len = iplen + 2 + hlen + alen; 718 if (alen > 0) 719 len++; 720 if (blen + len > bsize) { 721 res_ndestroy(statp); 722 return (NSS_ERROR); 723 } 724 (void) strlcpy(bptr, np, bsize - blen); 725 blen += iplen; 726 bptr += iplen; 727 *bptr++ = ' '; 728 blen++; 729 (void) strlcpy(bptr, host, bsize - blen); 730 blen += hlen; 731 bptr += hlen; 732 if (alen > 0) { 733 *bptr++ = ' '; 734 blen++; 735 (void) strlcpy(bptr, ap, bsize - blen); 736 blen += alen; 737 bptr += alen; 738 } 739 *bptr++ = '\n'; 740 blen++; 741 } 742 743 /* Depending on our flags we may need to go back another time. */ 744 if (qtype == T_AAAA && family == AF_INET6 && 745 ((flags & AI_V4MAPPED) != 0) && ((flags & AI_ALL) != 0) && 746 has_v4 == B_TRUE) { 747 qtype = T_A; 748 pass2 = 2; /* Indicate that we found data this pass */ 749 goto searchagain; 750 } 751 752 out: 753 /* Presumably the buffer is now filled. */ 754 len = ROUND_UP(blen, sizeof (nssuint_t)); 755 /* still room? */ 756 if (len + sizeof (nssuint_t) > pbuf->data_len) { 757 /* sigh, no, what happened? */ 758 res_ndestroy(statp); 759 return (NSS_ERROR); 760 } 761 762 pbuf->ext_off = pbuf->data_off + len; 763 pbuf->ext_len = sizeof (nssuint_t); 764 pbuf->data_len = blen; 765 pttl = (nssuint_t *)((void *)((char *)pbuf + pbuf->ext_off)); 766 *pttl = ttl; 767 res_ndestroy(statp); 768 return (NSS_SUCCESS); 769 } 770