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