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 * Copyright 2003 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ 27 /* All Rights Reserved */ 28 29 /* 30 * Portions of this source code were derived from Berkeley 31 * under license from the Regents of the University of 32 * California. 33 */ 34 35 #pragma ident "%Z%%M% %I% %E% SMI" 36 37 #define NULL 0 38 #include "mt.h" 39 #include "../rpc/rpc_mt.h" 40 #include <rpc/rpc.h> 41 #include <sys/types.h> 42 #include <rpc/trace.h> 43 #include "yp_b.h" 44 #include <rpcsvc/yp_prot.h> 45 #include <rpcsvc/ypclnt.h> 46 #include <stdlib.h> 47 #include <unistd.h> 48 #include <malloc.h> 49 #include <string.h> 50 #include <sys/time.h> 51 52 extern int __yp_dobind_cflookup(char *, struct dom_binding **, int); 53 extern int __yp_dobind_rsvdport_cflookup(char *, struct dom_binding **, int); 54 55 static int domatch(char *, char *, char *, int, struct dom_binding *, 56 struct timeval *, char **, int *); 57 int yp_match_rsvdport(); 58 int yp_match_rsvdport_cflookup(); 59 60 struct cache { 61 struct cache *next; 62 unsigned int birth; 63 char *domain; 64 char *map; 65 char *key; 66 int keylen; 67 char *val; 68 int vallen; 69 }; 70 71 static mutex_t cache_lock = DEFAULTMUTEX; 72 static int generation; /* Incremented when we add to cache */ 73 static struct cache *head; 74 75 #define CACHESZ 16 76 #define CACHETO 600 77 78 static void 79 freenode(n) 80 struct cache *n; 81 { 82 trace1(TR_freenode, 0); 83 if (n->val != 0) 84 free(n->val); 85 if (n->key != 0) 86 free(n->key); 87 if (n->map != 0) 88 free(n->map); 89 if (n->domain != 0) 90 free(n->domain); 91 free((char *)n); 92 trace1(TR_freenode, 1); 93 } 94 95 static struct cache * 96 makenode(domain, map, keylen, vallen) 97 char *domain, *map; 98 int keylen, vallen; 99 { 100 struct cache *n; 101 102 trace3(TR_makenode, 0, keylen, vallen); 103 if ((n = (struct cache *)calloc(1, sizeof (*n))) == 0) { 104 trace1(TR_makenode, 1); 105 return (0); 106 } 107 if (((n->domain = strdup(domain)) == 0) || 108 ((n->map = strdup(map)) == 0) || 109 ((n->key = malloc(keylen)) == 0) || 110 ((n->val = malloc(vallen)) == 0)) { 111 freenode(n); 112 trace1(TR_makenode, 1); 113 return (0); 114 } 115 trace1(TR_makenode, 1); 116 return (n); 117 } 118 119 static int 120 in_cache(domain, map, key, keylen, val, vallen) 121 char *domain; 122 char *map; 123 char *key; 124 int keylen; 125 char **val; /* returns value array */ 126 int *vallen; /* returns bytes in val */ 127 { 128 struct cache *c, **pp; 129 int cnt; 130 struct timeval now; 131 struct timezone tz; 132 133 /* 134 * Assumes that caller (yp_match) has locked the cache 135 */ 136 for (pp = &head, cnt = 0; (c = *pp) != 0; pp = &c->next, cnt++) { 137 if ((c->keylen == keylen) && 138 (memcmp(key, c->key, (size_t)keylen) == 0) && 139 (strcmp(map, c->map) == 0) && 140 (strcmp(domain, c->domain) == 0)) { 141 /* cache hit */ 142 (void) gettimeofday(&now, &tz); 143 if ((now.tv_sec - c->birth) > CACHETO) { 144 /* rats. it is too old to use */ 145 *pp = c->next; 146 freenode(c); 147 break; 148 } else { 149 *val = c->val; 150 *vallen = c->vallen; 151 152 /* Ersatz LRU: Move this entry to the front */ 153 *pp = c->next; 154 c->next = head; 155 head = c; 156 return (1); 157 } 158 } 159 if (cnt >= CACHESZ) { 160 *pp = c->next; 161 freenode(c); 162 break; 163 } 164 } 165 return (0); 166 } 167 168 /* 169 * Requests the yp server associated with a given domain to attempt to match 170 * the passed key datum in the named map, and to return the associated value 171 * datum. This part does parameter checking, and implements the "infinite" 172 * (until success) sleep loop if 'hardlookup' parameter is set. 173 */ 174 int 175 __yp_match_cflookup(domain, map, key, keylen, val, vallen, hardlookup) 176 char *domain; 177 char *map; 178 char *key; 179 int keylen; 180 char **val; /* returns value array */ 181 int *vallen; /* returns bytes in val */ 182 int hardlookup; /* retry lookup until we get an answer */ 183 { 184 size_t domlen; 185 size_t maplen; 186 int reason; 187 struct dom_binding *pdomb; 188 int savesize; 189 struct timeval now; 190 struct timezone tz; 191 char *my_val; 192 int my_vallen; 193 int found_it; 194 int cachegen; 195 196 trace2(TR_yp_match, 0, keylen); 197 if ((map == NULL) || (domain == NULL)) { 198 trace1(TR_yp_match, 1); 199 return (YPERR_BADARGS); 200 } 201 202 domlen = strlen(domain); 203 maplen = strlen(map); 204 205 if ((domlen == 0) || (domlen > YPMAXDOMAIN) || 206 (maplen == 0) || (maplen > YPMAXMAP) || 207 (key == NULL) || (keylen == 0)) { 208 trace1(TR_yp_match, 1); 209 return (YPERR_BADARGS); 210 } 211 212 mutex_lock(&cache_lock); 213 found_it = in_cache(domain, map, key, keylen, &my_val, &my_vallen); 214 cachegen = generation; 215 216 if (found_it) { 217 /* NB: Copy two extra bytes; see below */ 218 savesize = my_vallen + 2; 219 if ((*val = malloc((size_t)savesize)) == 0) { 220 trace1(TR_yp_match, 1); 221 mutex_unlock(&cache_lock); 222 return (YPERR_RESRC); 223 } 224 (void) memcpy(*val, my_val, (size_t)savesize); 225 *vallen = my_vallen; 226 trace1(TR_yp_match, 1); 227 mutex_unlock(&cache_lock); 228 return (0); /* Success */ 229 } 230 mutex_unlock(&cache_lock); 231 232 for (;;) { 233 234 if (reason = __yp_dobind_cflookup(domain, &pdomb, 235 hardlookup)) { 236 trace1(TR_yp_match, 1); 237 return (reason); 238 } 239 240 if (pdomb->dom_binding->ypbind_hi_vers >= YPVERS) { 241 242 reason = domatch(domain, map, key, keylen, pdomb, 243 &_ypserv_timeout, val, vallen); 244 245 __yp_rel_binding(pdomb); 246 if (reason == YPERR_RPC || reason == YPERR_YPSERV || 247 reason == YPERR_BUSY /* as if */) { 248 yp_unbind(domain); 249 if (hardlookup) 250 (void) _sleep(_ypsleeptime); /* retry */ 251 else { 252 trace1(TR_yp_match, 1); 253 return (reason); 254 } 255 } else 256 break; 257 } else { 258 __yp_rel_binding(pdomb); 259 trace1(TR_yp_match, 1); 260 return (YPERR_VERS); 261 } 262 } 263 264 /* add to our cache */ 265 if (reason == 0) { 266 mutex_lock(&cache_lock); 267 /* 268 * Check whether some other annoying thread did the same 269 * thing in parallel with us. I hate it when that happens... 270 */ 271 if (generation != cachegen && 272 in_cache(domain, map, key, keylen, &my_val, &my_vallen)) { 273 /* 274 * Could get cute and update the birth time, but it's 275 * not worth the bother. 276 * It looks strange that we return one val[] array 277 * to the caller and have a different copy of the 278 * val[] array in the cache (presumably with the 279 * same contents), but it should work just fine. 280 * So, do absolutely nothing... 281 */ 282 /* EMPTY */ 283 } else { 284 struct cache *c; 285 /* 286 * NB: allocate and copy extract two bytes of the 287 * value; these are mandatory CR and NULL bytes. 288 */ 289 savesize = *vallen + 2; 290 c = makenode(domain, map, keylen, savesize); 291 if (c != 0) { 292 (void) gettimeofday(&now, &tz); 293 c->birth = now.tv_sec; 294 c->keylen = keylen; 295 c->vallen = *vallen; 296 (void) memcpy(c->key, key, (size_t)keylen); 297 (void) memcpy(c->val, *val, (size_t)savesize); 298 299 c->next = head; 300 head = c; 301 ++generation; 302 } 303 } 304 mutex_unlock(&cache_lock); 305 } else if (reason == YPERR_MAP && geteuid() == 0) { 306 /* 307 * Lookup could be for a secure map; fail over to retry 308 * from a reserved port. Only useful to try this if we're 309 * the super user. 310 */ 311 int rsvdreason; 312 rsvdreason = yp_match_rsvdport(domain, map, key, keylen, val, 313 vallen); 314 if (rsvdreason == 0) 315 reason = rsvdreason; 316 } 317 trace1(TR_yp_match, 1); 318 return (reason); 319 } 320 321 int 322 yp_match( 323 char *domain, 324 char *map, 325 char *key, 326 int keylen, 327 char **val, /* returns value array */ 328 int *vallen) /* returns bytes in val */ 329 330 { 331 /* the traditional yp_match loops forever thus hardlookup is set */ 332 return (__yp_match_cflookup(domain, map, key, keylen, val, vallen, 1)); 333 } 334 335 extern void 336 __empty_yp_cache() 337 { 338 struct cache *p, *n; 339 340 /* Copy the cache pointer and make it ZERO */ 341 mutex_lock(&cache_lock); 342 p = head; 343 head = 0; 344 mutex_unlock(&cache_lock); 345 346 if (p == 0) 347 return; 348 349 /* Empty the cache */ 350 n = p->next; 351 while (p) { 352 freenode(p); 353 p = n; 354 if (p) 355 n = p->next; 356 } 357 } 358 359 /* 360 * Requests the yp server associated with a given domain to attempt to match 361 * the passed key datum in the named map, and to return the associated value 362 * datum. This part does parameter checking, and implements the "infinite" 363 * (until success) sleep loop. 364 * 365 * XXX special version for handling C2 (passwd.adjunct) lookups when we need 366 * a reserved port. 367 * Only difference against yp_match is that this function uses 368 * __yp_dobind_rsvdport(). 369 * 370 * Only called from NIS switch backend. 371 */ 372 int 373 __yp_match_rsvdport_cflookup( 374 char *domain, 375 char *map, 376 char *key, 377 int keylen, 378 char **val, /* returns value array */ 379 int *vallen, /* returns bytes in val */ 380 int hardlookup) /* retry until we can an answer */ 381 { 382 size_t domlen; 383 size_t maplen; 384 int reason; 385 struct dom_binding *pdomb; 386 int savesize; 387 struct timeval now; 388 struct timezone tz; 389 char *my_val; 390 int my_vallen; 391 int found_it; 392 int cachegen; 393 394 trace2(TR_yp_match_rsvdport, 0, keylen); 395 if ((map == NULL) || (domain == NULL)) { 396 trace1(TR_yp_match_rsvdport, 1); 397 return (YPERR_BADARGS); 398 } 399 400 domlen = strlen(domain); 401 maplen = strlen(map); 402 403 if ((domlen == 0) || (domlen > YPMAXDOMAIN) || 404 (maplen == 0) || (maplen > YPMAXMAP) || 405 (key == NULL) || (keylen == 0)) { 406 trace1(TR_yp_match_rsvdport, 1); 407 return (YPERR_BADARGS); 408 } 409 410 mutex_lock(&cache_lock); 411 found_it = in_cache(domain, map, key, keylen, &my_val, &my_vallen); 412 cachegen = generation; 413 if (found_it) { 414 /* NB: Copy two extra bytes; see below */ 415 savesize = my_vallen + 2; 416 if ((*val = malloc((size_t)savesize)) == 0) { 417 trace1(TR_yp_match_rsvdport, 1); 418 mutex_unlock(&cache_lock); 419 return (YPERR_RESRC); 420 } 421 (void) memcpy(*val, my_val, (size_t)savesize); 422 *vallen = my_vallen; 423 trace1(TR_yp_match_rsvdport, 1); 424 mutex_unlock(&cache_lock); 425 return (0); /* Success */ 426 } 427 mutex_unlock(&cache_lock); 428 429 for (;;) { 430 431 if (reason = __yp_dobind_rsvdport_cflookup(domain, &pdomb, 432 hardlookup)) { 433 trace1(TR_yp_match_rsvdport, 1); 434 return (reason); 435 } 436 437 if (pdomb->dom_binding->ypbind_hi_vers >= YPVERS) { 438 439 reason = domatch(domain, map, key, keylen, 440 pdomb, &_ypserv_timeout, val, vallen); 441 442 /* 443 * Have to free the binding since the reserved 444 * port bindings are not cached. 445 */ 446 __yp_rel_binding(pdomb); 447 free_dom_binding(pdomb); 448 449 if (reason == YPERR_RPC || reason == YPERR_YPSERV || 450 reason == YPERR_BUSY /* as if */) { 451 yp_unbind(domain); 452 if (hardlookup) 453 (void) _sleep(_ypsleeptime); /* retry */ 454 else { 455 trace1(TR_yp_match, 1); 456 return (reason); 457 } 458 } else 459 break; 460 } else { 461 /* 462 * Have to free the binding since the reserved 463 * port bindings are not cached. 464 */ 465 __yp_rel_binding(pdomb); 466 free_dom_binding(pdomb); 467 trace1(TR_yp_match_rsvdport, 1); 468 return (YPERR_VERS); 469 } 470 } 471 472 /* add to our cache */ 473 if (reason == 0) { 474 mutex_lock(&cache_lock); 475 /* 476 * Check whether some other annoying thread did the same 477 * thing in parallel with us. I hate it when that happens... 478 */ 479 if (generation != cachegen && 480 in_cache(domain, map, key, keylen, &my_val, &my_vallen)) { 481 /* 482 * Could get cute and update the birth time, but it's 483 * not worth the bother. 484 * It looks strange that we return one val[] array 485 * to the caller and have a different copy of the 486 * val[] array in the cache (presumably with the 487 * same contents), but it should work just fine. 488 * So, do absolutely nothing... 489 */ 490 /* EMPTY */ 491 } else { 492 struct cache *c; 493 /* 494 * NB: allocate and copy extract two bytes of the 495 * value; these are mandatory CR and NULL bytes. 496 */ 497 savesize = *vallen + 2; 498 c = makenode(domain, map, keylen, savesize); 499 if (c != 0) { 500 (void) gettimeofday(&now, &tz); 501 c->birth = now.tv_sec; 502 c->keylen = keylen; 503 c->vallen = *vallen; 504 (void) memcpy(c->key, key, (size_t)keylen); 505 (void) memcpy(c->val, *val, (size_t)savesize); 506 507 c->next = head; 508 head = c; 509 ++generation; 510 } 511 } 512 mutex_unlock(&cache_lock); 513 } 514 trace1(TR_yp_match_rsvdport, 1); 515 return (reason); 516 } 517 518 519 int 520 yp_match_rsvdport( 521 char *domain, 522 char *map, 523 char *key, 524 int keylen, 525 char **val, /* returns value array */ 526 int *vallen) /* returns bytes in val */ 527 { 528 /* traditional yp_match retries forever so set hardlookup */ 529 return (__yp_match_rsvdport_cflookup(domain, map, key, keylen, val, 530 vallen, 1)); 531 } 532 533 534 /* 535 * This talks v3 protocol to ypserv 536 */ 537 static int 538 domatch(char *domain, char *map, char *key, int keylen, 539 struct dom_binding *pdomb, struct timeval *timeoutp, char **val, 540 int *vallen) 541 { 542 struct ypreq_key req; 543 struct ypresp_val resp; 544 unsigned int retval = 0; 545 546 trace2(TR_domatch, 0, keylen); 547 req.domain = domain; 548 req.map = map; 549 req.keydat.dptr = key; 550 req.keydat.dsize = keylen; 551 552 resp.valdat.dptr = NULL; 553 resp.valdat.dsize = 0; 554 (void) memset((char *)&resp, 0, sizeof (struct ypresp_val)); 555 556 /* 557 * Do the match request. If the rpc call failed, return with status 558 * from this point. 559 */ 560 561 switch (clnt_call(pdomb->dom_client, YPPROC_MATCH, 562 (xdrproc_t)xdr_ypreq_key, (char *)&req, 563 (xdrproc_t)xdr_ypresp_val, (char *)&resp, 564 *timeoutp)) { 565 case RPC_SUCCESS: 566 break; 567 case RPC_TIMEDOUT: 568 trace1(TR_domatch, 1); 569 return (YPERR_YPSERV); 570 default: 571 trace1(TR_domatch, 1); 572 return (YPERR_RPC); 573 } 574 575 /* See if the request succeeded */ 576 577 if (resp.status != YP_TRUE) { 578 retval = ypprot_err(resp.status); 579 } 580 581 /* Get some memory which the user can get rid of as he likes */ 582 583 if (!retval && ((*val = malloc((size_t) 584 resp.valdat.dsize + 2)) == NULL)) { 585 retval = YPERR_RESRC; 586 } 587 588 /* Copy the returned value byte string into the new memory */ 589 590 if (!retval) { 591 *vallen = (int)resp.valdat.dsize; 592 (void) memcpy(*val, resp.valdat.dptr, 593 (size_t)resp.valdat.dsize); 594 (*val)[resp.valdat.dsize] = '\n'; 595 (*val)[resp.valdat.dsize + 1] = '\0'; 596 } 597 598 CLNT_FREERES(pdomb->dom_client, 599 (xdrproc_t)xdr_ypresp_val, (char *)&resp); 600 trace1(TR_domatch, 1); 601 return (retval); 602 603 } 604