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 2006 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 char *v6Name = 0; 293 struct in6_addr **v6Addrs = 0, **mergeAddrs = 0; 294 char **v6Aliases = 0, **mergeAliases = 0; 295 int v6_h_errno; 296 int old_retry; 297 int af = argp->key.ipnode.af_family; 298 int flags = argp->key.ipnode.flags; 299 300 switch_resolver_setup(&mt_disabled, &oldmask, &old_retry); 301 302 /* Now get the AAAA records */ 303 if (af == AF_INET6) 304 he = _nss_dns_gethostbyname2(&argp->h_errno, 305 argp->key.ipnode.name); 306 if (he != NULL) { 307 /* 308 * pointer in "he" is part of a static pthread key in libresolv 309 * It should be treated as read only. 310 * So clone a copy first. 311 */ 312 v6Name = cloneName(he, &converr); 313 if (converr) { 314 argp->h_errno = HOST_NOT_FOUND; 315 argp->erange = 1; 316 switch_resolver_reset(mt_disabled, oldmask, old_retry); 317 return (_herrno2nss(argp->h_errno)); 318 } 319 v6Addrs = cloneAddrList(he, 0, &converr); 320 if (converr) { 321 if (v6Name != 0) 322 free(v6Name); 323 argp->h_errno = HOST_NOT_FOUND; 324 argp->erange = 1; 325 switch_resolver_reset(mt_disabled, oldmask, old_retry); 326 return (_herrno2nss(argp->h_errno)); 327 } 328 v6Aliases = cloneAliasList(he, 0, &converr); 329 if (converr) { 330 if (v6Name != 0) 331 free(v6Name); 332 if (v6Addrs != 0) 333 free(v6Addrs); 334 argp->h_errno = HOST_NOT_FOUND; 335 argp->erange = 1; 336 switch_resolver_reset(mt_disabled, oldmask, old_retry); 337 return (_herrno2nss(argp->h_errno)); 338 } 339 v6_h_errno = argp->h_errno; 340 gotv6 = 1; 341 } 342 343 /* 344 * The conditions to search "A" records: 345 * 1. af is AF_INET 346 * 2. if af is AF_INET6 347 * then flags are either 348 * 1) (AI_ALL | AI_V4MAPPED) or 349 * 2) AI_V4MAPPED and he == NULL 350 * (No V6 addresses found or no search for V6 at all) 351 */ 352 353 /* Get the A records, and store the information */ 354 if ((af == AF_INET) || 355 ((af == AF_INET6) && 356 ((flags & (AI_ALL | AI_V4MAPPED)) || 357 ((flags & AI_V4MAPPED) && he == NULL)))) 358 he = _gethostbyname(&argp->h_errno, argp->key.ipnode.name); 359 else 360 he = NULL; 361 362 /* Merge the results */ 363 if (he != NULL) { 364 mergeAddrs = cloneAddrList(he, v6Addrs, &converr); 365 if (converr) { 366 if (v6Name != 0) 367 free(v6Name); 368 if (v6Addrs != 0) 369 free(v6Addrs); 370 if (v6Aliases != 0) 371 free(v6Aliases); 372 argp->h_errno = HOST_NOT_FOUND; 373 argp->erange = 1; 374 switch_resolver_reset(mt_disabled, oldmask, 375 old_retry); 376 return (_herrno2nss(argp->h_errno)); 377 } 378 he->h_addr_list = (char **)mergeAddrs; 379 380 mergeAliases = cloneAliasList(he, v6Aliases, &converr); 381 if (converr) { 382 if (v6Name != 0) 383 free(v6Name); 384 if (v6Addrs != 0) 385 free(v6Addrs); 386 if (v6Aliases != 0) 387 free(v6Aliases); 388 if (mergeAddrs != 0) 389 free(mergeAddrs); 390 argp->h_errno = HOST_NOT_FOUND; 391 argp->erange = 1; 392 switch_resolver_reset(mt_disabled, oldmask, 393 old_retry); 394 return (_herrno2nss(argp->h_errno)); 395 } 396 he->h_aliases = mergeAliases; 397 398 /* reset h_length, h_addrtype */ 399 he->h_length = sizeof (struct in6_addr); 400 he->h_addrtype = AF_INET6; 401 402 } else if (gotv6) { 403 v6he.h_name = v6Name; 404 v6he.h_length = sizeof (struct in6_addr); 405 v6he.h_addrtype = AF_INET6; 406 v6he.h_addr_list = (char **)v6Addrs; 407 v6he.h_aliases = v6Aliases; 408 he = &v6he; 409 argp->h_errno = v6_h_errno; 410 } 411 412 if (he != NULL) { 413 /* 414 * if asked to return data in string, 415 * convert the hostent structure into 416 * string data 417 */ 418 if (argp->buf.result == NULL) { 419 ret = ent2str(he, a, AF_INET6); 420 if (ret == NSS_STR_PARSE_SUCCESS) 421 argp->returnval = argp->buf.buffer; 422 } else { 423 ret = ent2result(he, a, AF_INET6); 424 if (ret == NSS_STR_PARSE_SUCCESS) 425 argp->returnval = argp->buf.result; 426 } 427 428 if (ret != NSS_STR_PARSE_SUCCESS) { 429 argp->h_errno = HOST_NOT_FOUND; 430 if (ret == NSS_STR_PARSE_ERANGE) { 431 argp->erange = 1; 432 } 433 } 434 } 435 436 if (v6Name != 0) 437 free(v6Name); 438 if (v6Addrs != 0) 439 free(v6Addrs); 440 if (v6Aliases != 0) 441 free(v6Aliases); 442 if (mergeAddrs != 0) 443 free(mergeAddrs); 444 if (mergeAliases != 0) 445 free(mergeAliases); 446 447 switch_resolver_reset(mt_disabled, oldmask, old_retry); 448 449 return (_herrno2nss(argp->h_errno)); 450 } 451 452 453 extern nss_status_t __nss_dns_getbyaddr(dns_backend_ptr_t, void *); 454 455 static nss_status_t 456 getbyaddr(be, a) 457 dns_backend_ptr_t be; 458 void *a; 459 { 460 /* uses the same getbyaddr from IPv4 */ 461 return (__nss_dns_getbyaddr(be, a)); 462 } 463 464 465 /*ARGSUSED*/ 466 static nss_status_t 467 _nss_dns_getent(be, args) 468 dns_backend_ptr_t be; 469 void *args; 470 { 471 return (NSS_UNAVAIL); 472 } 473 474 475 /*ARGSUSED*/ 476 static nss_status_t 477 _nss_dns_setent(be, dummy) 478 dns_backend_ptr_t be; 479 void *dummy; 480 { 481 /* XXXX not implemented at this point */ 482 return (NSS_UNAVAIL); 483 } 484 485 486 /*ARGSUSED*/ 487 static nss_status_t 488 _nss_dns_endent(be, dummy) 489 dns_backend_ptr_t be; 490 void *dummy; 491 { 492 /* XXXX not implemented at this point */ 493 return (NSS_UNAVAIL); 494 } 495 496 497 /*ARGSUSED*/ 498 static nss_status_t 499 _nss_dns_destr(be, dummy) 500 dns_backend_ptr_t be; 501 void *dummy; 502 { 503 nss_status_t errp; 504 505 if (be != 0) { 506 /* === Should change to invoke ops[ENDENT] ? */ 507 sigset_t oldmask, newmask; 508 int mt_disabled = 1; 509 510 if (enable_mt == 0 || (mt_disabled = (*enable_mt)()) != 0) { 511 (void) sigfillset(&newmask); 512 _thr_sigsetmask(SIG_SETMASK, &newmask, &oldmask); 513 _mutex_lock(&one_lane); 514 } 515 516 _endhostent(&errp); 517 518 if (mt_disabled) { 519 _mutex_unlock(&one_lane); 520 _thr_sigsetmask(SIG_SETMASK, &oldmask, NULL); 521 } else { 522 (void) (*disable_mt)(); 523 } 524 525 free(be); 526 } 527 return (NSS_SUCCESS); /* In case anyone is dumb enough to check */ 528 } 529 530 531 532 static dns_backend_op_t ipnodes_ops[] = { 533 _nss_dns_destr, 534 _nss_dns_endent, 535 _nss_dns_setent, 536 _nss_dns_getent, 537 getbyname, 538 getbyaddr, 539 }; 540 541 /*ARGSUSED*/ 542 nss_backend_t * 543 _nss_dns_ipnodes_constr(dummy1, dummy2, dummy3) 544 const char *dummy1, *dummy2, *dummy3; 545 { 546 return (_nss_dns_constr(ipnodes_ops, 547 sizeof (ipnodes_ops) / sizeof (ipnodes_ops[0]))); 548 } 549 550 /* 551 * optional NSS2 packed backend gethostsbyipnode with ttl 552 * entry point. 553 * 554 * Returns: 555 * NSS_SUCCESS - successful 556 * NSS_NOTFOUND - successful but nothing found 557 * NSS_ERROR - fallback to NSS backend lookup mode 558 * If successful, buffer will be filled with valid data 559 * 560 */ 561 562 /*ARGSUSED*/ 563 nss_status_t 564 _nss_get_dns_ipnodes_name(dns_backend_ptr_t *be, void **bufp, size_t *sizep) 565 { 566 return (_nss_dns_gethost_withttl(*bufp, *sizep, 1)); 567 } 568