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