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 2003 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 * Routines to handle gethost* calls in nscd 31 */ 32 33 #include <assert.h> 34 #include <errno.h> 35 #include <memory.h> 36 #include <signal.h> 37 #include <stdlib.h> 38 #include <stdio.h> 39 #include <string.h> 40 #include <sys/door.h> 41 #include <sys/stat.h> 42 #include <sys/time.h> 43 #include <sys/types.h> 44 #include <sys/wait.h> 45 #include <thread.h> 46 #include <unistd.h> 47 #include <ucred.h> 48 #include <nss_common.h> 49 50 #include "getxby_door.h" 51 #include "server_door.h" 52 #include "nscd.h" 53 54 static hash_t *addr_hash; 55 static hash_t *hnam_hash; 56 static mutex_t host_lock = DEFAULTMUTEX; 57 static waiter_t host_wait; 58 59 static void gethost_addrkeepalive(int keep, int interval); 60 static void gethost_invalidate_unlocked(void); 61 static void gethost_namekeepalive(int keep, int interval); 62 static int addr_to_int(char *addr); 63 static int int_to_addr(int h); 64 static void update_host_bucket(nsc_bucket_t **old, nsc_bucket_t *new, 65 int callnumber); 66 static nsc_bucket_t *fixbuffer(nsc_return_t *in, int maxlen); 67 static void do_findhaddrs(nsc_bucket_t *ptr, int *table, int intaddr); 68 static void do_findhnams(nsc_bucket_t *ptr, int *table, char *name); 69 static void do_invalidate(nsc_bucket_t **ptr, int callnumber); 70 71 static int 72 addr_to_int(char *addr) 73 { 74 union { 75 char data[4]; 76 int hashval; 77 } u; 78 79 /* 80 * following code is byte order dependant, but since all we use this for 81 * is hashing this works out just fine. 82 */ 83 u.data[0] = *addr++; 84 u.data[1] = *addr++; 85 u.data[2] = *addr++; 86 u.data[3] = *addr++; 87 88 return (u.hashval); 89 } 90 91 static int 92 int_to_addr(int h) 93 { 94 union { 95 char data[4]; 96 int hashval; 97 } u; 98 99 /* 100 * following code is byte order dependant, but since all we use this for 101 * is hashing this works out just fine. 102 */ 103 u.hashval = h; 104 return (* ((int *)u.data)); 105 } 106 107 void 108 gethost_init(void) 109 { 110 addr_hash = make_ihash(current_admin.host.nsc_suggestedsize); 111 hnam_hash = make_hash(current_admin.host.nsc_suggestedsize); 112 } 113 114 static void 115 do_invalidate(nsc_bucket_t ** ptr, int callnumber) 116 { 117 if (*ptr != NULL && *ptr != (nsc_bucket_t *)-1) { 118 /* leave pending calls alone */ 119 update_host_bucket(ptr, NULL, callnumber); 120 } 121 } 122 123 static void 124 do_findhnams(nsc_bucket_t *ptr, int *table, char *name) 125 { 126 /* 127 * be careful with ptr - it may be -1 or NULL. 128 */ 129 130 if (ptr != NULL && ptr != (nsc_bucket_t *)-1) { 131 /* leave pending calls alone */ 132 char *tmp = (char *)insertn(table, ptr->nsc_hits, 133 (int)strdup(name)); 134 if (tmp != (char *)-1) 135 free(tmp); 136 } 137 } 138 139 static void 140 do_findhaddrs(nsc_bucket_t *ptr, int *table, int intaddr) 141 { 142 if (ptr != NULL && ptr != (nsc_bucket_t *)-1) { 143 /* leave pending calls alone */ 144 insertn(table, ptr->nsc_hits, int_to_addr(intaddr)); 145 } 146 } 147 148 void 149 gethost_revalidate(void) 150 { 151 for (;;) { 152 int slp; 153 int interval; 154 int count; 155 156 slp = current_admin.host.nsc_pos_ttl; 157 158 if (slp < 60) 159 slp = 60; 160 count = current_admin.host.nsc_keephot; 161 if (count != 0) { 162 interval = (slp/2)/count; 163 if (interval == 0) interval = 1; 164 sleep(slp*2/3); 165 gethost_namekeepalive(count, interval); 166 gethost_addrkeepalive(count, interval); 167 } else { 168 sleep(slp); 169 } 170 } 171 } 172 173 static void 174 gethost_namekeepalive(int keep, int interval) 175 { 176 int *table; 177 union { 178 nsc_data_t ping; 179 char space[sizeof (nsc_data_t) + NSCDMAXNAMELEN]; 180 } u; 181 182 int i; 183 184 if (!keep) 185 return; 186 187 table = maken(keep); 188 mutex_lock(&host_lock); 189 operate_hash(hnam_hash, do_findhnams, (char *)table); 190 mutex_unlock(&host_lock); 191 192 for (i = 1; i <= keep; i++) { 193 char *tmp; 194 u.ping.nsc_call.nsc_callnumber = GETHOSTBYNAME; 195 196 if ((tmp = (char *)table[keep + 1 + i]) == (char *)-1) 197 continue; /* unused slot in table */ 198 if (current_admin.debug_level >= DBG_ALL) 199 logit("keepalive: reviving host %s\n", tmp); 200 strcpy(u.ping.nsc_call.nsc_u.name, tmp); 201 202 launch_update(&u.ping.nsc_call); 203 sleep(interval); 204 } 205 206 for (i = 1; i <= keep; i++) { 207 char *tmp; 208 if ((tmp = (char *)table[keep + 1 + i]) != (char *)-1) 209 free(tmp); 210 } 211 212 free(table); 213 } 214 215 static void 216 gethost_addrkeepalive(int keep, int interval) 217 { 218 int *table; 219 union { 220 nsc_data_t ping; 221 char space[sizeof (nsc_data_t) + 80]; 222 } u; 223 224 int i; 225 226 if (!keep) 227 return; 228 229 table = maken(keep); 230 mutex_lock(&host_lock); 231 operate_hash(addr_hash, do_findhaddrs, (char *)table); 232 mutex_unlock(&host_lock); 233 234 for (i = 1; i <= keep; i++) { 235 int tmp; 236 u.ping.nsc_call.nsc_callnumber = GETHOSTBYADDR; 237 238 if ((tmp = table[keep + 1 + i]) == -1) 239 continue; /* unused slot in table */ 240 u.ping.nsc_call.nsc_u.addr.a_type = AF_INET; 241 u.ping.nsc_call.nsc_u.addr.a_length = sizeof (int); 242 memcpy(u.ping.nsc_call.nsc_u.addr.a_data, &tmp, sizeof (int)); 243 launch_update(&u.ping.nsc_call); 244 sleep(interval); 245 } 246 247 free(table); 248 } 249 250 /* 251 * This routine marks all entries as invalid 252 * 253 */ 254 void 255 gethost_invalidate(void) 256 { 257 mutex_lock(&host_lock); 258 gethost_invalidate_unlocked(); 259 mutex_unlock(&host_lock); 260 } 261 262 static void 263 gethost_invalidate_unlocked(void) 264 { 265 operate_hash_addr(hnam_hash, do_invalidate, (char *)GETHOSTBYNAME); 266 operate_hash_addr(addr_hash, do_invalidate, (char *)GETHOSTBYADDR); 267 current_admin.host.nsc_invalidate_count++; 268 } 269 270 void 271 gethost_lookup(nsc_return_t *out, int maxsize, nsc_call_t *in, time_t now) 272 { 273 int out_of_date; 274 nsc_bucket_t *retb; 275 char **bucket; 276 277 static time_t lastmod; 278 279 int bufferspace = maxsize - sizeof (nsc_return_t); 280 281 if (current_admin.host.nsc_enabled == 0) { 282 out->nsc_return_code = NOSERVER; 283 out->nsc_bufferbytesused = sizeof (*out); 284 return; 285 } 286 287 mutex_lock(&host_lock); 288 289 if (current_admin.host.nsc_check_files) { 290 struct stat buf; 291 292 if (stat("/etc/hosts", &buf) < 0) { 293 /*EMPTY*/; 294 } else if (lastmod == 0) { 295 lastmod = buf.st_mtime; 296 } else if (lastmod < buf.st_mtime) { 297 gethost_invalidate_unlocked(); 298 lastmod = buf.st_mtime; 299 } 300 } 301 302 303 if (current_admin.debug_level >= DBG_ALL) { 304 if (MASKUPDATEBIT(in->nsc_callnumber) == GETHOSTBYADDR) { 305 logit("gethost_lookup: looking for address %s\n", 306 inet_ntoa(*((struct in_addr *)in->nsc_u.addr.a_data))); 307 } else { 308 logit("gethost_lookup: looking for hostname %s\n", 309 in->nsc_u.name); 310 } 311 } 312 313 for (;;) { 314 if (MASKUPDATEBIT(in->nsc_callnumber) == GETHOSTBYADDR) { 315 bucket = get_hash(addr_hash, 316 (char *)addr_to_int(in->nsc_u.addr.a_data)); 317 } else { /* bounce excessively long requests */ 318 if (strlen(in->nsc_u.name) > NSCDMAXNAMELEN) { 319 ucred_t *uc = NULL; 320 321 if (door_ucred(&uc) != 0) { 322 logit("gethost_lookup: Name too long, " 323 "but no user credential: %s\n", 324 strerror(errno)); 325 } else { 326 logit("gethost_lookup: Name too long " 327 "from pid %d uid %d\n", 328 ucred_getpid(uc), 329 ucred_getruid(uc)); 330 ucred_free(uc); 331 } 332 333 out->nsc_errno = NSS_NOTFOUND; 334 out->nsc_return_code = NOTFOUND; 335 out->nsc_bufferbytesused = sizeof (*out); 336 goto getout; 337 } 338 bucket = get_hash(hnam_hash, in->nsc_u.name); 339 } 340 341 if (*bucket == (char *)-1) { /* pending lookup */ 342 if (get_clearance(in->nsc_callnumber) != 0) { 343 /* no threads available */ 344 out->nsc_return_code = NOSERVER; 345 /* cannot process now */ 346 out->nsc_bufferbytesused = sizeof (*out); 347 current_admin.host.nsc_throttle_count++; 348 goto getout; 349 } 350 nscd_wait(&host_wait, &host_lock, bucket); 351 release_clearance(in->nsc_callnumber); 352 continue; /* go back and relookup hash bucket */ 353 } 354 break; 355 } 356 357 /* 358 * check for no name_service mode 359 */ 360 361 if (*bucket == NULL && current_admin.avoid_nameservice) { 362 out->nsc_return_code = NOTFOUND; 363 out->nsc_bufferbytesused = sizeof (*out); 364 } else if ((*bucket == NULL) || /* New entry in name service */ 365 (in->nsc_callnumber & UPDATEBIT) || /* needs updating */ 366 (out_of_date = (!current_admin.avoid_nameservice && 367 (current_admin.host.nsc_old_data_ok == 0) && 368 (((nsc_bucket_t *)*bucket)->nsc_timestamp < now)))) { 369 /* time has expired */ 370 int saved_errno; 371 int saved_hits = 0; 372 struct hostent *p; 373 374 if (get_clearance(in->nsc_callnumber) != 0) { 375 /* no threads available */ 376 out->nsc_return_code = NOSERVER; 377 /* cannot process now */ 378 out->nsc_bufferbytesused = sizeof (*out); 379 current_admin.host.nsc_throttle_count++; 380 goto getout; 381 } 382 383 if (*bucket != NULL) { 384 saved_hits = ((nsc_bucket_t *)*bucket)->nsc_hits; 385 } 386 387 /* 388 * block any threads accessing this bucket if data is 389 * non-existent or out of date 390 */ 391 392 if (*bucket == NULL || out_of_date) { 393 update_host_bucket((nsc_bucket_t **)bucket, 394 (nsc_bucket_t *)-1, 395 in->nsc_callnumber); 396 } else { 397 /* 398 * if still not -1 bucket we are doing update... mark 399 * to prevent pileups of threads if the name service 400 * is hanging.... 401 */ 402 ((nsc_bucket_t *)(*bucket))->nsc_status |= 403 ST_UPDATE_PENDING; 404 /* cleared by deletion of old data */ 405 } 406 mutex_unlock(&host_lock); 407 408 if (MASKUPDATEBIT(in->nsc_callnumber) == GETHOSTBYADDR) { 409 p = _uncached_gethostbyaddr_r(in->nsc_u.addr.a_data, 410 in->nsc_u.addr.a_length, 411 in->nsc_u.addr.a_type, 412 &out->nsc_u.hst, 413 out->nsc_u.buff+sizeof (struct hostent), 414 bufferspace, 415 &saved_errno); 416 } else { 417 p = _uncached_gethostbyname_r(in->nsc_u.name, 418 &out->nsc_u.hst, 419 out->nsc_u.buff+sizeof (struct hostent), 420 bufferspace, 421 &saved_errno); 422 } 423 424 mutex_lock(&host_lock); 425 426 release_clearance(in->nsc_callnumber); 427 428 if (p == NULL) { /* data not found */ 429 if (current_admin.debug_level >= DBG_CANT_FIND) { 430 if (MASKUPDATEBIT(in->nsc_callnumber) == 431 GETHOSTBYADDR) { 432 logit("gethost_lookup: nscd COULDN'T FIND address %s\n", 433 inet_ntoa(*((struct in_addr *)in->nsc_u.addr.a_data))); 434 } else { 435 logit("gethost_lookup: nscd COULDN'T FIND host name %s\n", 436 in->nsc_u.name); 437 } 438 } 439 440 if (!(UPDATEBIT & in->nsc_callnumber)) 441 current_admin.host.nsc_neg_cache_misses++; 442 443 retb = (nsc_bucket_t *)malloc(sizeof (nsc_bucket_t)); 444 445 retb->nsc_refcount = 1; 446 retb->nsc_data.nsc_return_code = NOTFOUND; 447 retb->nsc_data.nsc_bufferbytesused = 448 sizeof (nsc_return_t); 449 retb->nsc_data.nsc_errno = saved_errno; 450 memcpy(out, &(retb->nsc_data), 451 retb->nsc_data.nsc_bufferbytesused); 452 update_host_bucket((nsc_bucket_t **)bucket, retb, 453 in->nsc_callnumber); 454 goto getout; 455 } else { 456 if (current_admin.debug_level >= DBG_ALL) { 457 if (MASKUPDATEBIT(in->nsc_callnumber) == 458 GETHOSTBYADDR) { 459 logit("gethost_lookup: nscd FOUND addr %s\n", 460 inet_ntoa(*((struct in_addr *)in->nsc_u.addr.a_data))); 461 } else { 462 logit("gethost_lookup: nscd FOUND host name %s\n", 463 in->nsc_u.name); 464 } 465 } 466 if (!(UPDATEBIT & in->nsc_callnumber)) 467 current_admin.host.nsc_pos_cache_misses++; 468 469 retb = fixbuffer(out, bufferspace); 470 471 update_host_bucket((nsc_bucket_t **)bucket, retb, 472 in->nsc_callnumber); 473 if (saved_hits) 474 retb->nsc_hits = saved_hits; 475 } 476 } else { /* found entry in cache */ 477 retb = (nsc_bucket_t *)*bucket; 478 479 retb->nsc_hits++; 480 481 memcpy(out, &(retb->nsc_data), 482 retb->nsc_data.nsc_bufferbytesused); 483 484 if (out->nsc_return_code == SUCCESS) { 485 if (!(UPDATEBIT & in->nsc_callnumber)) 486 current_admin.host.nsc_pos_cache_hits++; 487 if (current_admin.debug_level >= DBG_ALL) { 488 if (MASKUPDATEBIT(in->nsc_callnumber) == 489 GETHOSTBYADDR) { 490 logit("gethost_lookup: found address %s in cache\n", 491 inet_ntoa(*((struct in_addr *)in->nsc_u.addr.a_data))); 492 } else { 493 logit("gethost_lookup: found host name %s in cache\n", 494 in->nsc_u.name); 495 } 496 } 497 } else { 498 if (!(UPDATEBIT & in->nsc_callnumber)) 499 current_admin.host.nsc_neg_cache_hits++; 500 if (current_admin.debug_level >= DBG_ALL) { 501 if (MASKUPDATEBIT(in->nsc_callnumber) == 502 GETHOSTBYADDR) { 503 logit("gethost_lookup: %s marked as NOT FOUND in cache.\n", 504 inet_ntoa(*((struct in_addr *)in->nsc_u.addr.a_data))); 505 } else { 506 logit("gethost_lookup: %s marked as NOT FOUND in cache.\n", 507 in->nsc_u.name); 508 } 509 } 510 } 511 512 if ((retb->nsc_timestamp < now) && 513 !(in->nsc_callnumber & UPDATEBIT) && 514 !(retb->nsc_status & ST_UPDATE_PENDING)) { 515 logit("launch update since time = %d\n", retb->nsc_timestamp); 516 /* cleared by deletion of old data */ 517 retb->nsc_status |= ST_UPDATE_PENDING; 518 launch_update(in); 519 } 520 } 521 522 getout: 523 524 mutex_unlock(&host_lock); 525 } 526 527 /*ARGSUSED*/ 528 static void 529 update_host_bucket(nsc_bucket_t **old, nsc_bucket_t *new, int callnumber) 530 { 531 if (*old != NULL && *old != (nsc_bucket_t *)-1) { 532 /* old data exists */ 533 free(*old); 534 current_admin.host.nsc_entries--; 535 } 536 537 /* 538 * we can do this before reseting *old since we're holding the lock 539 */ 540 541 else if (*old == (nsc_bucket_t *)-1) { 542 nscd_signal(&host_wait, (char **)old); 543 } 544 545 546 547 *old = new; 548 549 if ((new != NULL) && 550 (new != (nsc_bucket_t *)-1)) { 551 /* real data, not just update pending or invalidate */ 552 553 new->nsc_hits = 1; 554 new->nsc_status = 0; 555 new->nsc_refcount = 1; 556 current_admin.host.nsc_entries++; 557 558 if (new->nsc_data.nsc_return_code == SUCCESS) { 559 new->nsc_timestamp = time(NULL) + 560 current_admin.host.nsc_pos_ttl; 561 } else { 562 new->nsc_timestamp = time(NULL) + 563 current_admin.host.nsc_neg_ttl; 564 } 565 } 566 } 567 568 569 /*ARGSUSED*/ 570 static nsc_bucket_t * 571 fixbuffer(nsc_return_t *in, int maxlen) 572 { 573 nsc_return_t *out; 574 nsc_bucket_t *retb; 575 char *dest; 576 char **aliaseslist; 577 char **addrlist; 578 int offset; 579 int strs; 580 int i; 581 int numaliases; 582 int numaddrs; 583 584 /* 585 * find out the size of the data block we're going to need 586 */ 587 588 strs = 1 + strlen(in->nsc_u.hst.h_name); 589 for (numaliases = 0; in->nsc_u.hst.h_aliases[numaliases]; numaliases++) 590 strs += 1 + strlen(in->nsc_u.hst.h_aliases[numaliases]); 591 strs += sizeof (char *) * (numaliases+1); 592 for (numaddrs = 0; in->nsc_u.hst.h_addr_list[numaddrs]; numaddrs++) 593 strs += in->nsc_u.hst.h_length; 594 strs += sizeof (char *) * (numaddrs+1+3); 595 596 /* 597 * allocate it and copy it in 598 * code doesn't assume packing order in original buffer 599 */ 600 601 if ((retb = (nsc_bucket_t *)malloc(sizeof (*retb) + strs)) == NULL) { 602 return (NULL); 603 } 604 605 out = &(retb->nsc_data); 606 out->nsc_bufferbytesused = sizeof (*in) + strs; 607 out->nsc_return_code = SUCCESS; 608 out->nsc_errno = 0; 609 610 611 dest = retb->nsc_data.nsc_u.buff + sizeof (struct hostent); 612 613 offset = (int)dest; 614 615 /* 616 * allocat the h_aliases list and the h_addr_list first to align 'em. 617 */ 618 aliaseslist = (char **)dest; 619 620 dest += sizeof (char *) * (numaliases+1); 621 622 addrlist = (char **)dest; 623 624 dest += sizeof (char *) * (numaddrs+1); 625 626 strcpy(dest, in->nsc_u.hst.h_name); 627 strs = 1 + strlen(in->nsc_u.hst.h_name); 628 out->nsc_u.hst.h_name = dest - offset; 629 dest += strs; 630 631 632 /* 633 * fill out the h_aliases list 634 */ 635 for (i = 0; i < numaliases; i++) { 636 strcpy(dest, in->nsc_u.hst.h_aliases[i]); 637 strs = 1 + strlen(in->nsc_u.hst.h_aliases[i]); 638 aliaseslist[i] = dest - offset; 639 dest += strs; 640 } 641 aliaseslist[i] = 0; /* null term ptr chain */ 642 643 out->nsc_u.hst.h_aliases = (char **)((int)aliaseslist-offset); 644 645 /* 646 * fill out the h_addr list 647 */ 648 649 dest = (char *)(((int)dest + 3) & ~3); 650 651 for (i = 0; i < numaddrs; i++) { 652 memcpy(dest, in->nsc_u.hst.h_addr_list[i], 653 in->nsc_u.hst.h_length); 654 strs = in->nsc_u.hst.h_length; 655 addrlist[i] = dest - offset; 656 dest += strs; 657 dest = (char *)(((int)dest + 3) & ~3); 658 } 659 660 addrlist[i] = 0; /* null term ptr chain */ 661 662 out->nsc_u.hst.h_addr_list = (char **)((int)addrlist-offset); 663 664 out->nsc_u.hst.h_length = in->nsc_u.hst.h_length; 665 out->nsc_u.hst.h_addrtype = in->nsc_u.hst.h_addrtype; 666 667 memcpy(in, &(retb->nsc_data), retb->nsc_data.nsc_bufferbytesused); 668 669 return (retb); 670 671 } 672 673 void 674 gethost_nam_reaper() 675 { 676 nsc_reaper("gethost_nam", hnam_hash, ¤t_admin.host, &host_lock); 677 } 678 679 void 680 gethost_addr_reaper() 681 { 682 nsc_reaper("gethost_addr", addr_hash, ¤t_admin.host, &host_lock); 683 } 684