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 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 /* 26 * gethostent6.c 27 */ 28 29 #pragma ident "%Z%%M% %I% %E% SMI" 30 31 /* 32 * This is the DNS backend for IPv6 addresses. 33 * getbyname() is a local routine, but getbyaddr() actually shares the 34 * same codes as the one in gethostent.c. 35 */ 36 37 #define endhostent res_endhostent 38 39 #include <malloc.h> 40 #include <stddef.h> 41 #include <string.h> 42 #include "dns_common.h" 43 44 /* 45 * If the DNS name service switch routines are used in a binary that depends 46 * on an older libresolv (libresolv.so.1, say), then having nss_dns.so.1 or 47 * libnss_dns.a depend on a newer libresolv (libresolv.so.2) will cause 48 * relocation problems. In particular, copy relocation of the _res structure 49 * (which changes in size from libresolv.so.1 to libresolv.so.2) could 50 * cause corruption, and result in a number of strange problems, including 51 * core dumps. Hence, we check if a libresolv is already loaded. 52 */ 53 54 55 #pragma weak res_endhostent 56 57 extern struct hostent *_gethostbyname(int *, const char *); 58 extern struct hostent *_nss_dns_gethostbyname2(int *, const char *); 59 60 typedef union { 61 long al; 62 char ac; 63 } align; 64 65 66 static void 67 _endhostent(errp) 68 nss_status_t *errp; 69 { 70 int ret; 71 72 ret = endhostent(); 73 if (ret == 0) 74 *errp = NSS_SUCCESS; 75 else 76 *errp = NSS_UNAVAIL; 77 } 78 79 80 #ifdef RNDUP 81 #undef RNDUP 82 #endif 83 #define RNDUP(x) ((1 + (((x)-1)/sizeof (void *))) * sizeof (void *)) 84 85 #ifdef PTROFF 86 #undef PTROFF 87 #endif 88 #define PTROFF(p, o) (((o) == 0) ? 0 : (void *)((char *)(p) + (o))) 89 90 91 /* 92 * Make a copy of h->h_name. 93 */ 94 static char * 95 cloneName(struct hostent *h, int *outerr) { 96 97 char *name; 98 int len; 99 int error, *errp; 100 101 if (outerr) 102 errp = outerr; 103 else 104 errp = &error; 105 106 if (h == 0 || h->h_name == 0) { 107 *errp = 0; 108 return (0); 109 } 110 111 len = strlen(h->h_name); 112 113 if ((name = malloc(len+1)) == 0) { 114 *errp = 1; 115 return (0); 116 } 117 118 (void) memcpy(name, h->h_name, len+1); 119 120 *errp = 0; 121 return (name); 122 } 123 124 125 /* 126 * Copy the h->h_addr_list[] array to a new array, and append the 127 * moreAddrs[] list. If h->h_addr_list[] contains IPv4 addresses, 128 * convert them to v4 mapped IPv6 addresses. 129 * 130 * Note: The pointers to the addresses in the moreAddrs[] array are copied, 131 * but not the IP addresses themselves. 132 */ 133 static struct in6_addr ** 134 cloneAddrList(struct hostent *h, struct in6_addr **moreAddrs, int *outerr) { 135 136 struct in6_addr **addrArray, *addrList; 137 int domap, addrlen, i, j, addrCount, moreAddrCount = 0; 138 139 int error, *errp; 140 141 if (outerr) 142 errp = outerr; 143 else 144 errp = &error; 145 146 if (h == 0 || h->h_addr_list == 0) { 147 *errp = 0; 148 return (0); 149 } 150 151 /* Should we map v4 to IPv6 ? */ 152 domap = (h->h_length == sizeof (struct in_addr)) && 153 (h->h_addrtype == AF_INET); 154 155 /* If mapping, make sure we allocate enough memory for addresses */ 156 addrlen = h->h_length; 157 if (domap && addrlen < sizeof (struct in6_addr)) 158 addrlen = sizeof (struct in6_addr); 159 160 for (addrCount = 0; h->h_addr_list[addrCount]; addrCount++); 161 162 if (moreAddrs != 0) { 163 for (moreAddrCount = 0; moreAddrs[moreAddrCount]; 164 moreAddrCount++); 165 } 166 167 if ((addrArray = malloc((addrCount+moreAddrCount+1)*sizeof (addrList) + 168 addrCount*addrlen)) == 0) { 169 *errp = 1; 170 return (0); 171 } 172 173 addrList = PTROFF(addrArray, (addrCount+moreAddrCount+1) * 174 sizeof (addrList)); 175 176 for (i = 0; i < addrCount; i++) { 177 addrArray[i] = addrList; 178 if (domap) { 179 /* LINTED: E_BAD_PTR_CAST_ALIGN */ 180 IN6_INADDR_TO_V4MAPPED( 181 (struct in_addr *)h->h_addr_list[i], addrArray[i]); 182 } else { 183 (void) memcpy(addrArray[i], h->h_addr_list[i], 184 addrlen); 185 } 186 addrList = PTROFF(addrList, addrlen); 187 } 188 189 for (j = 0; j < moreAddrCount; j++, i++) { 190 addrArray[i] = moreAddrs[j]; 191 } 192 193 /* Last pointer should be NULL */ 194 addrArray[i] = 0; 195 196 *errp = 0; 197 return (addrArray); 198 } 199 200 201 /* 202 * Create a new alias array that is is a copy of h->h_aliases[] plus 203 * the aliases in mergeAliases[] which aren't duplicates of any alias 204 * in h->h_aliases[]. 205 * 206 * Note 1: Only the string pointers (NOT the strings) in the mergeAliases[] 207 * array are copied. 208 * 209 * Note 2: The duplicate aliases in mergeAliases[] are replaced by NULL 210 * pointers. 211 */ 212 static char ** 213 cloneAliasList(struct hostent *h, char **mergeAliases, int *outerr) { 214 215 char **aliasArray, *aliasList; 216 int i, j, aliasCount, mergeAliasCount = 0, realMac = 0; 217 int stringSize = 0; 218 int error, *errp; 219 220 if (outerr) 221 errp = outerr; 222 else 223 errp = &error; 224 225 226 if (h == 0 || h->h_aliases == 0) { 227 *errp = 0; 228 return (0); 229 } 230 231 for (aliasCount = 0; h->h_aliases[aliasCount]; aliasCount++) { 232 stringSize += RNDUP(strlen(h->h_aliases[aliasCount])+1); 233 } 234 235 if (mergeAliases != 0) { 236 for (; mergeAliases[mergeAliasCount]; mergeAliasCount++) { 237 int countThis = 1; 238 /* Skip duplicates */ 239 for (j = 0; j < aliasCount; j++) { 240 if (strcmp(mergeAliases[mergeAliasCount], 241 h->h_aliases[j]) == 0) { 242 countThis = 0; 243 break; 244 } 245 } 246 if (countThis) 247 realMac++; 248 else 249 mergeAliases[mergeAliasCount] = 0; 250 } 251 } 252 253 if ((aliasArray = malloc((aliasCount+realMac+1)*sizeof (char **)+ 254 stringSize)) == 0) { 255 *errp = 1; 256 return (0); 257 } 258 259 aliasList = PTROFF(aliasArray, 260 (aliasCount+realMac+1)*sizeof (char **)); 261 for (i = 0; i < aliasCount; i++) { 262 int len = strlen(h->h_aliases[i]); 263 aliasArray[i] = aliasList; 264 (void) memcpy(aliasArray[i], h->h_aliases[i], len+1); 265 aliasList = PTROFF(aliasList, RNDUP(len+1)); 266 } 267 268 for (j = 0; j < mergeAliasCount; j++) { 269 if (mergeAliases[j] != 0) { 270 aliasArray[i++] = mergeAliases[j]; 271 } 272 } 273 274 aliasArray[i] = 0; 275 276 *errp = 0; 277 return (aliasArray); 278 } 279 280 /*ARGSUSED*/ 281 static nss_status_t 282 getbyname(be, a) 283 dns_backend_ptr_t be; 284 void *a; 285 { 286 struct hostent *he = NULL; 287 nss_XbyY_args_t *argp = (nss_XbyY_args_t *)a; 288 int ret, mt_disabled; 289 sigset_t oldmask; 290 int converr = 0, gotv6 = 0; 291 struct hostent v6he; 292 struct hostent mhe; 293 char *v6Name = 0; 294 struct in6_addr **v6Addrs = 0, **mergeAddrs = 0; 295 char **v6Aliases = 0, **mergeAliases = 0; 296 int v6_h_errno; 297 int old_retry; 298 int af = argp->key.ipnode.af_family; 299 int flags = argp->key.ipnode.flags; 300 301 switch_resolver_setup(&mt_disabled, &oldmask, &old_retry); 302 303 /* Now get the AAAA records */ 304 if (af == AF_INET6) 305 he = _nss_dns_gethostbyname2(&argp->h_errno, 306 argp->key.ipnode.name); 307 if (he != NULL) { 308 /* 309 * pointer in "he" is part of a static pthread key in libresolv 310 * It should be treated as read only. 311 * So clone a copy first. 312 */ 313 v6Name = cloneName(he, &converr); 314 if (converr) { 315 argp->h_errno = HOST_NOT_FOUND; 316 argp->erange = 1; 317 switch_resolver_reset(mt_disabled, oldmask, old_retry); 318 return (_herrno2nss(argp->h_errno)); 319 } 320 v6Addrs = cloneAddrList(he, 0, &converr); 321 if (converr) { 322 if (v6Name != 0) 323 free(v6Name); 324 argp->h_errno = HOST_NOT_FOUND; 325 argp->erange = 1; 326 switch_resolver_reset(mt_disabled, oldmask, old_retry); 327 return (_herrno2nss(argp->h_errno)); 328 } 329 v6Aliases = cloneAliasList(he, 0, &converr); 330 if (converr) { 331 if (v6Name != 0) 332 free(v6Name); 333 if (v6Addrs != 0) 334 free(v6Addrs); 335 argp->h_errno = HOST_NOT_FOUND; 336 argp->erange = 1; 337 switch_resolver_reset(mt_disabled, oldmask, old_retry); 338 return (_herrno2nss(argp->h_errno)); 339 } 340 v6_h_errno = argp->h_errno; 341 gotv6 = 1; 342 } 343 344 /* 345 * The conditions to search "A" records: 346 * 1. af is AF_INET 347 * 2. if af is AF_INET6 348 * then flags are either 349 * 1) (AI_ALL | AI_V4MAPPED) or 350 * 2) AI_V4MAPPED and he == NULL 351 * (No V6 addresses found or no search for V6 at all) 352 */ 353 354 /* Get the A records, and store the information */ 355 if ((af == AF_INET) || 356 ((af == AF_INET6) && 357 ((flags & (AI_ALL | AI_V4MAPPED)) || 358 ((flags & AI_V4MAPPED) && he == NULL)))) 359 he = _gethostbyname(&argp->h_errno, argp->key.ipnode.name); 360 else 361 he = NULL; 362 363 /* Merge the results */ 364 if (he != NULL) { 365 mhe = *he; 366 mergeAddrs = cloneAddrList(he, v6Addrs, &converr); 367 if (converr) { 368 if (v6Name != 0) 369 free(v6Name); 370 if (v6Addrs != 0) 371 free(v6Addrs); 372 if (v6Aliases != 0) 373 free(v6Aliases); 374 argp->h_errno = HOST_NOT_FOUND; 375 argp->erange = 1; 376 switch_resolver_reset(mt_disabled, oldmask, 377 old_retry); 378 return (_herrno2nss(argp->h_errno)); 379 } 380 mhe.h_addr_list = (char **)mergeAddrs; 381 382 mergeAliases = cloneAliasList(he, v6Aliases, &converr); 383 if (converr) { 384 if (v6Name != 0) 385 free(v6Name); 386 if (v6Addrs != 0) 387 free(v6Addrs); 388 if (v6Aliases != 0) 389 free(v6Aliases); 390 if (mergeAddrs != 0) 391 free(mergeAddrs); 392 argp->h_errno = HOST_NOT_FOUND; 393 argp->erange = 1; 394 switch_resolver_reset(mt_disabled, oldmask, 395 old_retry); 396 return (_herrno2nss(argp->h_errno)); 397 } 398 mhe.h_aliases = mergeAliases; 399 400 /* reset h_length, h_addrtype */ 401 mhe.h_length = sizeof (struct in6_addr); 402 mhe.h_addrtype = AF_INET6; 403 he = &mhe; 404 405 } else if (gotv6) { 406 v6he.h_name = v6Name; 407 v6he.h_length = sizeof (struct in6_addr); 408 v6he.h_addrtype = AF_INET6; 409 v6he.h_addr_list = (char **)v6Addrs; 410 v6he.h_aliases = v6Aliases; 411 he = &v6he; 412 argp->h_errno = v6_h_errno; 413 } 414 415 if (he != NULL) { 416 /* 417 * if asked to return data in string, 418 * convert the hostent structure into 419 * string data 420 */ 421 if (argp->buf.result == NULL) { 422 ret = ent2str(he, a, AF_INET6); 423 if (ret == NSS_STR_PARSE_SUCCESS) 424 argp->returnval = argp->buf.buffer; 425 } else { 426 ret = ent2result(he, a, AF_INET6); 427 if (ret == NSS_STR_PARSE_SUCCESS) 428 argp->returnval = argp->buf.result; 429 } 430 431 if (ret != NSS_STR_PARSE_SUCCESS) { 432 argp->h_errno = HOST_NOT_FOUND; 433 if (ret == NSS_STR_PARSE_ERANGE) { 434 argp->erange = 1; 435 } 436 } 437 } 438 439 if (v6Name != 0) 440 free(v6Name); 441 if (v6Addrs != 0) 442 free(v6Addrs); 443 if (v6Aliases != 0) 444 free(v6Aliases); 445 if (mergeAddrs != 0) 446 free(mergeAddrs); 447 if (mergeAliases != 0) 448 free(mergeAliases); 449 450 switch_resolver_reset(mt_disabled, oldmask, old_retry); 451 452 return (_herrno2nss(argp->h_errno)); 453 } 454 455 456 extern nss_status_t __nss_dns_getbyaddr(dns_backend_ptr_t, void *); 457 458 static nss_status_t 459 getbyaddr(be, a) 460 dns_backend_ptr_t be; 461 void *a; 462 { 463 /* uses the same getbyaddr from IPv4 */ 464 return (__nss_dns_getbyaddr(be, a)); 465 } 466 467 468 /*ARGSUSED*/ 469 static nss_status_t 470 _nss_dns_getent(be, args) 471 dns_backend_ptr_t be; 472 void *args; 473 { 474 return (NSS_UNAVAIL); 475 } 476 477 478 /*ARGSUSED*/ 479 static nss_status_t 480 _nss_dns_setent(be, dummy) 481 dns_backend_ptr_t be; 482 void *dummy; 483 { 484 /* XXXX not implemented at this point */ 485 return (NSS_UNAVAIL); 486 } 487 488 489 /*ARGSUSED*/ 490 static nss_status_t 491 _nss_dns_endent(be, dummy) 492 dns_backend_ptr_t be; 493 void *dummy; 494 { 495 /* XXXX not implemented at this point */ 496 return (NSS_UNAVAIL); 497 } 498 499 500 /*ARGSUSED*/ 501 static nss_status_t 502 _nss_dns_destr(be, dummy) 503 dns_backend_ptr_t be; 504 void *dummy; 505 { 506 nss_status_t errp; 507 508 if (be != 0) { 509 /* === Should change to invoke ops[ENDENT] ? */ 510 sigset_t oldmask, newmask; 511 int mt_disabled = 1; 512 513 if (enable_mt == 0 || (mt_disabled = (*enable_mt)()) != 0) { 514 (void) sigfillset(&newmask); 515 _thr_sigsetmask(SIG_SETMASK, &newmask, &oldmask); 516 _mutex_lock(&one_lane); 517 } 518 519 _endhostent(&errp); 520 521 if (mt_disabled) { 522 _mutex_unlock(&one_lane); 523 _thr_sigsetmask(SIG_SETMASK, &oldmask, NULL); 524 } else { 525 (void) (*disable_mt)(); 526 } 527 528 free(be); 529 } 530 return (NSS_SUCCESS); /* In case anyone is dumb enough to check */ 531 } 532 533 534 535 static dns_backend_op_t ipnodes_ops[] = { 536 _nss_dns_destr, 537 _nss_dns_endent, 538 _nss_dns_setent, 539 _nss_dns_getent, 540 getbyname, 541 getbyaddr, 542 }; 543 544 /*ARGSUSED*/ 545 nss_backend_t * 546 _nss_dns_ipnodes_constr(dummy1, dummy2, dummy3) 547 const char *dummy1, *dummy2, *dummy3; 548 { 549 return (_nss_dns_constr(ipnodes_ops, 550 sizeof (ipnodes_ops) / sizeof (ipnodes_ops[0]))); 551 } 552 553 /* 554 * optional NSS2 packed backend gethostsbyipnode with ttl 555 * entry point. 556 * 557 * Returns: 558 * NSS_SUCCESS - successful 559 * NSS_NOTFOUND - successful but nothing found 560 * NSS_ERROR - fallback to NSS backend lookup mode 561 * If successful, buffer will be filled with valid data 562 * 563 */ 564 565 /*ARGSUSED*/ 566 nss_status_t 567 _nss_get_dns_ipnodes_name(dns_backend_ptr_t *be, void **bufp, size_t *sizep) 568 { 569 return (_nss_dns_gethost_withttl(*bufp, *sizep, 1)); 570 } 571