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