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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 1993-2000, 2003 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 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 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 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 IN6_INADDR_TO_V4MAPPED( 180 (struct in_addr *)h->h_addr_list[i], addrArray[i]); 181 } else { 182 memcpy(addrArray[i], h->h_addr_list[i], 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, k, 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 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 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 char *v6Name = 0; 291 struct in6_addr **v6Addrs = 0, **mergeAddrs = 0; 292 char **v6Aliases = 0, **mergeAliases = 0; 293 int v6_h_errno; 294 int old_retry; 295 int af = argp->key.ipnode.af_family; 296 int flags = argp->key.ipnode.flags; 297 298 switch_resolver_setup(&mt_disabled, &oldmask, &old_retry); 299 300 /* Now get the AAAA records */ 301 if (af == AF_INET6) 302 he = _nss_dns_gethostbyname2(&argp->h_errno, 303 argp->key.ipnode.name); 304 if (he != NULL) { 305 /* 306 * pointer in "he" is part of a static pthread key in libresolv 307 * It should be treated as read only. 308 * So clone a copy first. 309 */ 310 v6Name = cloneName(he, &converr); 311 if (converr) { 312 argp->h_errno = HOST_NOT_FOUND; 313 argp->erange = 1; 314 switch_resolver_reset(mt_disabled, oldmask, old_retry); 315 return (_herrno2nss(argp->h_errno)); 316 } 317 v6Addrs = cloneAddrList(he, 0, &converr); 318 if (converr) { 319 if (v6Name != 0) 320 free(v6Name); 321 argp->h_errno = HOST_NOT_FOUND; 322 argp->erange = 1; 323 switch_resolver_reset(mt_disabled, oldmask, old_retry); 324 return (_herrno2nss(argp->h_errno)); 325 } 326 v6Aliases = cloneAliasList(he, 0, &converr); 327 if (converr) { 328 if (v6Name != 0) 329 free(v6Name); 330 if (v6Addrs != 0) 331 free(v6Addrs); 332 argp->h_errno = HOST_NOT_FOUND; 333 argp->erange = 1; 334 switch_resolver_reset(mt_disabled, oldmask, old_retry); 335 return (_herrno2nss(argp->h_errno)); 336 } 337 v6_h_errno = argp->h_errno; 338 gotv6 = 1; 339 } 340 341 /* 342 * The conditions to search "A" records: 343 * 1. af is AF_INET 344 * 2. if af is AF_INET6 345 * then flags are either 346 * 1) (AI_ALL | AI_V4MAPPED) or 347 * 2) AI_V4MAPPED and he == NULL 348 * (No V6 addresses found or no search for V6 at all) 349 */ 350 351 /* Get the A records, and store the information */ 352 if ((af == AF_INET) || 353 ((af == AF_INET6) && 354 ((flags & (AI_ALL | AI_V4MAPPED)) || 355 ((flags & AI_V4MAPPED) && he == NULL)))) 356 he = _gethostbyname(&argp->h_errno, argp->key.ipnode.name); 357 else 358 he = NULL; 359 360 /* Merge the results */ 361 if (he != NULL) { 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 he->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 he->h_aliases = mergeAliases; 395 396 /* reset h_length, h_addrtype */ 397 he->h_length = sizeof (struct in6_addr); 398 he->h_addrtype = AF_INET6; 399 400 } else if (gotv6) { 401 v6he.h_name = v6Name; 402 v6he.h_length = sizeof (struct in6_addr); 403 v6he.h_addrtype = AF_INET6; 404 v6he.h_addr_list = (char **)v6Addrs; 405 v6he.h_aliases = v6Aliases; 406 he = &v6he; 407 argp->h_errno = v6_h_errno; 408 } 409 410 if (he != 0) { 411 ret = ent2result(he, a, AF_INET6); 412 if (ret == NSS_STR_PARSE_SUCCESS) { 413 argp->returnval = argp->buf.result; 414 } else { 415 argp->h_errno = HOST_NOT_FOUND; 416 if (ret == NSS_STR_PARSE_ERANGE) { 417 argp->erange = 1; 418 } 419 } 420 } 421 422 if (v6Name != 0) 423 free(v6Name); 424 if (v6Addrs != 0) 425 free(v6Addrs); 426 if (v6Aliases != 0) 427 free(v6Aliases); 428 if (mergeAddrs != 0) 429 free(mergeAddrs); 430 if (mergeAliases != 0) 431 free(mergeAliases); 432 433 switch_resolver_reset(mt_disabled, oldmask, old_retry); 434 435 return (_herrno2nss(argp->h_errno)); 436 } 437 438 439 extern nss_status_t __nss_dns_getbyaddr(dns_backend_ptr_t, void *); 440 441 static nss_status_t 442 getbyaddr(be, a) 443 dns_backend_ptr_t be; 444 void *a; 445 { 446 /* uses the same getbyaddr from IPv4 */ 447 return (__nss_dns_getbyaddr(be, a)); 448 } 449 450 451 /*ARGSUSED*/ 452 static nss_status_t 453 _nss_dns_getent(be, args) 454 dns_backend_ptr_t be; 455 void *args; 456 { 457 return (NSS_UNAVAIL); 458 } 459 460 461 /*ARGSUSED*/ 462 static nss_status_t 463 _nss_dns_setent(be, dummy) 464 dns_backend_ptr_t be; 465 void *dummy; 466 { 467 /* XXXX not implemented at this point */ 468 return (NSS_UNAVAIL); 469 } 470 471 472 /*ARGSUSED*/ 473 static nss_status_t 474 _nss_dns_endent(be, dummy) 475 dns_backend_ptr_t be; 476 void *dummy; 477 { 478 /* XXXX not implemented at this point */ 479 return (NSS_UNAVAIL); 480 } 481 482 483 /*ARGSUSED*/ 484 static nss_status_t 485 _nss_dns_destr(be, dummy) 486 dns_backend_ptr_t be; 487 void *dummy; 488 { 489 nss_status_t errp; 490 491 if (be != 0) { 492 /* === Should change to invoke ops[ENDENT] ? */ 493 sigset_t oldmask, newmask; 494 int mt_disabled = 1; 495 496 if (enable_mt == 0 || (mt_disabled = (*enable_mt)()) != 0) { 497 (void) sigfillset(&newmask); 498 _thr_sigsetmask(SIG_SETMASK, &newmask, &oldmask); 499 _mutex_lock(&one_lane); 500 } 501 502 _endhostent(&errp); 503 504 if (mt_disabled) { 505 _mutex_unlock(&one_lane); 506 _thr_sigsetmask(SIG_SETMASK, &oldmask, NULL); 507 } else { 508 (void) (*disable_mt)(); 509 } 510 511 free(be); 512 } 513 return (NSS_SUCCESS); /* In case anyone is dumb enough to check */ 514 } 515 516 517 518 static dns_backend_op_t ipnodes_ops[] = { 519 _nss_dns_destr, 520 _nss_dns_endent, 521 _nss_dns_setent, 522 _nss_dns_getent, 523 getbyname, 524 getbyaddr, 525 }; 526 527 /*ARGSUSED*/ 528 nss_backend_t * 529 _nss_dns_ipnodes_constr(dummy1, dummy2, dummy3) 530 const char *dummy1, *dummy2, *dummy3; 531 { 532 return (_nss_dns_constr(ipnodes_ops, 533 sizeof (ipnodes_ops) / sizeof (ipnodes_ops[0]))); 534 } 535