1 /* 2 * Copyright (c) 1995 3 * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 3. All advertising materials mentioning features or use of this software 14 * must display the following acknowledgement: 15 * This product includes software developed by Bill Paul. 16 * 4. Neither the name of the author nor the names of any co-contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 #include <sys/cdefs.h> 34 __FBSDID("$FreeBSD$"); 35 36 #include <db.h> 37 #include <errno.h> 38 #include <fcntl.h> 39 #include <limits.h> 40 #include <paths.h> 41 #include <stdio.h> 42 #include <stdlib.h> 43 #include <string.h> 44 #include <unistd.h> 45 #include <sys/stat.h> 46 #include <sys/param.h> 47 #include <rpcsvc/yp.h> 48 #include "yp_extern.h" 49 50 int ypdb_debug = 0; 51 enum ypstat yp_errno = YP_TRUE; 52 53 #define PERM_SECURE (S_IRUSR|S_IWUSR) 54 HASHINFO openinfo = { 55 4096, /* bsize */ 56 32, /* ffactor */ 57 256, /* nelem */ 58 2048 * 512, /* cachesize */ 59 NULL, /* hash */ 60 0, /* lorder */ 61 }; 62 63 #ifdef DB_CACHE 64 #include <sys/queue.h> 65 66 #ifndef MAXDBS 67 #define MAXDBS 20 68 #endif 69 70 static int numdbs = 0; 71 72 struct dbent { 73 DB *dbp; 74 char *name; 75 char *key; 76 int size; 77 int flags; 78 }; 79 80 static TAILQ_HEAD(circlehead, circleq_entry) qhead; 81 82 struct circleq_entry { 83 struct dbent *dbptr; 84 TAILQ_ENTRY(circleq_entry) links; 85 }; 86 87 /* 88 * Initialize the circular queue. 89 */ 90 void 91 yp_init_dbs(void) 92 { 93 TAILQ_INIT(&qhead); 94 return; 95 } 96 97 /* 98 * Dynamically allocate an entry for the circular queue. 99 * Return a NULL pointer on failure. 100 */ 101 static struct circleq_entry * 102 yp_malloc_qent(void) 103 { 104 register struct circleq_entry *q; 105 106 q = malloc(sizeof(struct circleq_entry)); 107 if (q == NULL) { 108 yp_error("failed to malloc() circleq entry"); 109 return(NULL); 110 } 111 bzero((char *)q, sizeof(struct circleq_entry)); 112 q->dbptr = malloc(sizeof(struct dbent)); 113 if (q->dbptr == NULL) { 114 yp_error("failed to malloc() circleq entry"); 115 free(q); 116 return(NULL); 117 } 118 bzero((char *)q->dbptr, sizeof(struct dbent)); 119 120 return(q); 121 } 122 123 /* 124 * Free a previously allocated circular queue 125 * entry. 126 */ 127 static void 128 yp_free_qent(struct circleq_entry *q) 129 { 130 /* 131 * First, close the database. In theory, this is also 132 * supposed to free the resources allocated by the DB 133 * package, including the memory pointed to by q->dbptr->key. 134 * This means we don't have to free q->dbptr->key here. 135 */ 136 if (q->dbptr->dbp) { 137 (void)(q->dbptr->dbp->close)(q->dbptr->dbp); 138 q->dbptr->dbp = NULL; 139 } 140 /* 141 * Then free the database name, which was strdup()'ed. 142 */ 143 free(q->dbptr->name); 144 145 /* 146 * Free the rest of the dbent struct. 147 */ 148 free(q->dbptr); 149 q->dbptr = NULL; 150 151 /* 152 * Free the circleq struct. 153 */ 154 free(q); 155 q = NULL; 156 157 return; 158 } 159 160 /* 161 * Zorch a single entry in the dbent queue and release 162 * all its resources. (This always removes the last entry 163 * in the queue.) 164 */ 165 static void 166 yp_flush(void) 167 { 168 register struct circleq_entry *qptr; 169 170 qptr = TAILQ_LAST(&qhead, circlehead); 171 TAILQ_REMOVE(&qhead, qptr, links); 172 yp_free_qent(qptr); 173 numdbs--; 174 175 return; 176 } 177 178 /* 179 * Close all databases, erase all database names and empty the queue. 180 */ 181 void 182 yp_flush_all(void) 183 { 184 register struct circleq_entry *qptr; 185 186 while (!TAILQ_EMPTY(&qhead)) { 187 qptr = TAILQ_FIRST(&qhead); /* save this */ 188 TAILQ_REMOVE(&qhead, qptr, links); 189 yp_free_qent(qptr); 190 } 191 numdbs = 0; 192 193 return; 194 } 195 196 static char *inter_string = "YP_INTERDOMAIN"; 197 static char *secure_string = "YP_SECURE"; 198 static int inter_sz = sizeof("YP_INTERDOMAIN") - 1; 199 static int secure_sz = sizeof("YP_SECURE") - 1; 200 201 static int 202 yp_setflags(DB *dbp) 203 { 204 DBT key = { NULL, 0 }, data = { NULL, 0 }; 205 int flags = 0; 206 207 key.data = inter_string; 208 key.size = inter_sz; 209 210 if (!(dbp->get)(dbp, &key, &data, 0)) 211 flags |= YP_INTERDOMAIN; 212 213 key.data = secure_string; 214 key.size = secure_sz; 215 216 if (!(dbp->get)(dbp, &key, &data, 0)) 217 flags |= YP_SECURE; 218 219 return(flags); 220 } 221 222 int 223 yp_testflag(char *map, char *domain, int flag) 224 { 225 char buf[MAXPATHLEN + 2]; 226 register struct circleq_entry *qptr; 227 228 if (map == NULL || domain == NULL) 229 return(0); 230 231 strcpy(buf, domain); 232 strcat(buf, "/"); 233 strcat(buf, map); 234 235 TAILQ_FOREACH(qptr, &qhead, links) { 236 if (!strcmp(qptr->dbptr->name, buf)) { 237 if (qptr->dbptr->flags & flag) 238 return(1); 239 else 240 return(0); 241 } 242 } 243 244 if (yp_open_db_cache(domain, map, NULL, 0) == NULL) 245 return(0); 246 247 if (TAILQ_FIRST(&qhead)->dbptr->flags & flag) 248 return(1); 249 250 return(0); 251 } 252 253 /* 254 * Add a DB handle and database name to the cache. We only maintain 255 * fixed number of entries in the cache, so if we're asked to store 256 * a new entry when all our slots are already filled, we have to kick 257 * out the entry in the last slot to make room. 258 */ 259 static int 260 yp_cache_db(DB *dbp, char *name, int size) 261 { 262 register struct circleq_entry *qptr; 263 264 if (numdbs == MAXDBS) { 265 if (ypdb_debug) 266 yp_error("queue overflow -- releasing last slot"); 267 yp_flush(); 268 } 269 270 /* 271 * Allocate a new queue entry. 272 */ 273 274 if ((qptr = yp_malloc_qent()) == NULL) { 275 yp_error("failed to allocate a new cache entry"); 276 return(1); 277 } 278 279 qptr->dbptr->dbp = dbp; 280 qptr->dbptr->name = strdup(name); 281 qptr->dbptr->size = size; 282 qptr->dbptr->key = NULL; 283 284 qptr->dbptr->flags = yp_setflags(dbp); 285 286 TAILQ_INSERT_HEAD(&qhead, qptr, links); 287 numdbs++; 288 289 return(0); 290 } 291 292 /* 293 * Search the list for a database matching 'name.' If we find it, 294 * move it to the head of the list and return its DB handle. If 295 * not, just fail: yp_open_db_cache() will subsequently try to open 296 * the database itself and call yp_cache_db() to add it to the 297 * list. 298 * 299 * The search works like this: 300 * 301 * - The caller specifies the name of a database to locate. We try to 302 * find an entry in our queue with a matching name. 303 * 304 * - If the caller doesn't specify a key or size, we assume that the 305 * first entry that we encounter with a matching name is returned. 306 * This will result in matches regardless of the key/size values 307 * stored in the queue entry. 308 * 309 * - If the caller also specifies a key and length, we check to see 310 * if the key and length saved in the queue entry also matches. 311 * This lets us return a DB handle that's already positioned at the 312 * correct location within a database. 313 * 314 * - Once we have a match, it gets migrated to the top of the queue 315 * so that it will be easier to find if another request for 316 * the same database comes in later. 317 */ 318 static DB * 319 yp_find_db(const char *name, const char *key, int size) 320 { 321 register struct circleq_entry *qptr; 322 323 TAILQ_FOREACH(qptr, &qhead, links) { 324 if (!strcmp(qptr->dbptr->name, name)) { 325 if (size) { 326 if (size != qptr->dbptr->size || 327 strncmp(qptr->dbptr->key, key, size)) 328 continue; 329 } else { 330 if (qptr->dbptr->size) 331 continue; 332 } 333 if (qptr != TAILQ_FIRST(&qhead)) { 334 TAILQ_REMOVE(&qhead, qptr, links); 335 TAILQ_INSERT_HEAD(&qhead, qptr, links); 336 } 337 return(qptr->dbptr->dbp); 338 } 339 } 340 341 return(NULL); 342 } 343 344 /* 345 * Open a DB database and cache the handle for later use. We first 346 * check the cache to see if the required database is already open. 347 * If so, we fetch the handle from the cache. If not, we try to open 348 * the database and save the handle in the cache for later use. 349 */ 350 DB * 351 yp_open_db_cache(const char *domain, const char *map, const char *key, 352 const int size) 353 { 354 DB *dbp = NULL; 355 char buf[MAXPATHLEN + 2]; 356 /* 357 snprintf(buf, sizeof(buf), "%s/%s", domain, map); 358 */ 359 yp_errno = YP_TRUE; 360 361 strcpy(buf, domain); 362 strcat(buf, "/"); 363 strcat(buf, map); 364 365 if ((dbp = yp_find_db(buf, key, size)) != NULL) { 366 return(dbp); 367 } else { 368 if ((dbp = yp_open_db(domain, map)) != NULL) { 369 if (yp_cache_db(dbp, buf, size)) { 370 (void)(dbp->close)(dbp); 371 yp_errno = YP_YPERR; 372 return(NULL); 373 } 374 } 375 } 376 377 return (dbp); 378 } 379 #endif 380 381 /* 382 * Open a DB database. 383 */ 384 DB * 385 yp_open_db(const char *domain, const char *map) 386 { 387 DB *dbp = NULL; 388 char buf[MAXPATHLEN + 2]; 389 390 yp_errno = YP_TRUE; 391 392 if (map[0] == '.' || strchr(map, '/')) { 393 yp_errno = YP_BADARGS; 394 return (NULL); 395 } 396 397 #ifdef DB_CACHE 398 if (yp_validdomain(domain)) { 399 yp_errno = YP_NODOM; 400 return(NULL); 401 } 402 #endif 403 snprintf(buf, sizeof(buf), "%s/%s/%s", yp_dir, domain, map); 404 405 #ifdef DB_CACHE 406 again: 407 #endif 408 dbp = dbopen(buf, O_RDONLY, PERM_SECURE, DB_HASH, NULL); 409 410 if (dbp == NULL) { 411 switch (errno) { 412 #ifdef DB_CACHE 413 case ENFILE: 414 /* 415 * We ran out of file descriptors. Nuke an 416 * open one and try again. 417 */ 418 yp_error("ran out of file descriptors"); 419 yp_flush(); 420 goto again; 421 break; 422 #endif 423 case ENOENT: 424 yp_errno = YP_NOMAP; 425 break; 426 case EFTYPE: 427 yp_errno = YP_BADDB; 428 break; 429 default: 430 yp_errno = YP_YPERR; 431 break; 432 } 433 } 434 435 return (dbp); 436 } 437 438 /* 439 * Database access routines. 440 * 441 * - yp_get_record(): retrieve an arbitrary key/data pair given one key 442 * to match against. 443 * 444 * - yp_first_record(): retrieve first key/data base in a database. 445 * 446 * - yp_next_record(): retrieve key/data pair that sequentially follows 447 * the supplied key value in the database. 448 */ 449 450 #ifdef DB_CACHE 451 int 452 yp_get_record(DB *dbp, const DBT *key, DBT *data, int allow) 453 #else 454 int 455 yp_get_record(const char *domain, const char *map, 456 const DBT *key, DBT *data, int allow) 457 #endif 458 { 459 #ifndef DB_CACHE 460 DB *dbp; 461 #endif 462 int rval = 0; 463 #ifndef DB_CACHE 464 static unsigned char buf[YPMAXRECORD]; 465 #endif 466 467 if (ypdb_debug) 468 yp_error("looking up key [%.*s]", 469 (int)key->size, (char *)key->data); 470 471 /* 472 * Avoid passing back magic "YP_*" entries unless 473 * the caller specifically requested them by setting 474 * the 'allow' flag. 475 */ 476 if (!allow && !strncmp(key->data, "YP_", 3)) 477 return(YP_NOKEY); 478 479 #ifndef DB_CACHE 480 if ((dbp = yp_open_db(domain, map)) == NULL) { 481 return(yp_errno); 482 } 483 #endif 484 485 if ((rval = (dbp->get)(dbp, key, data, 0)) != 0) { 486 #ifdef DB_CACHE 487 TAILQ_FIRST(&qhead)->dbptr->size = 0; 488 #else 489 (void)(dbp->close)(dbp); 490 #endif 491 if (rval == 1) 492 return(YP_NOKEY); 493 else 494 return(YP_BADDB); 495 } 496 497 if (ypdb_debug) 498 yp_error("result of lookup: key: [%.*s] data: [%.*s]", 499 (int)key->size, (char *)key->data, 500 (int)data->size, (char *)data->data); 501 502 #ifdef DB_CACHE 503 if (TAILQ_FIRST(&qhead)->dbptr->size) { 504 TAILQ_FIRST(&qhead)->dbptr->key = ""; 505 TAILQ_FIRST(&qhead)->dbptr->size = 0; 506 } 507 #else 508 bcopy(data->data, &buf, data->size); 509 data->data = &buf; 510 (void)(dbp->close)(dbp); 511 #endif 512 513 return(YP_TRUE); 514 } 515 516 int 517 yp_first_record(const DB *dbp, DBT *key, DBT *data, int allow) 518 { 519 int rval; 520 #ifndef DB_CACHE 521 static unsigned char buf[YPMAXRECORD]; 522 #endif 523 524 if (ypdb_debug) 525 yp_error("retrieving first key in map"); 526 527 if ((rval = (dbp->seq)(dbp,key,data,R_FIRST)) != 0) { 528 #ifdef DB_CACHE 529 TAILQ_FIRST(&qhead)->dbptr->size = 0; 530 #endif 531 if (rval == 1) 532 return(YP_NOKEY); 533 else 534 return(YP_BADDB); 535 } 536 537 /* Avoid passing back magic "YP_*" records. */ 538 while (!strncmp(key->data, "YP_", 3) && !allow) { 539 if ((rval = (dbp->seq)(dbp,key,data,R_NEXT)) != 0) { 540 #ifdef DB_CACHE 541 TAILQ_FIRST(&qhead)->dbptr->size = 0; 542 #endif 543 if (rval == 1) 544 return(YP_NOKEY); 545 else 546 return(YP_BADDB); 547 } 548 } 549 550 if (ypdb_debug) 551 yp_error("result of lookup: key: [%.*s] data: [%.*s]", 552 (int)key->size, (char *)key->data, 553 (int)data->size, (char *)data->data); 554 555 #ifdef DB_CACHE 556 if (TAILQ_FIRST(&qhead)->dbptr->size) { 557 TAILQ_FIRST(&qhead)->dbptr->key = key->data; 558 TAILQ_FIRST(&qhead)->dbptr->size = key->size; 559 } 560 #else 561 bcopy(data->data, &buf, data->size); 562 data->data = &buf; 563 #endif 564 565 return(YP_TRUE); 566 } 567 568 int 569 yp_next_record(const DB *dbp, DBT *key, DBT *data, int all, int allow) 570 { 571 static DBT lkey = { NULL, 0 }; 572 static DBT ldata = { NULL, 0 }; 573 int rval; 574 #ifndef DB_CACHE 575 static unsigned char keybuf[YPMAXRECORD]; 576 static unsigned char datbuf[YPMAXRECORD]; 577 #endif 578 579 if (key == NULL || !key->size || key->data == NULL) { 580 rval = yp_first_record(dbp,key,data,allow); 581 if (rval == YP_NOKEY) 582 return(YP_NOMORE); 583 else { 584 #ifdef DB_CACHE 585 TAILQ_FIRST(&qhead)->dbptr->key = key->data; 586 TAILQ_FIRST(&qhead)->dbptr->size = key->size; 587 #endif 588 return(rval); 589 } 590 } 591 592 if (ypdb_debug) 593 yp_error("retrieving next key, previous was: [%.*s]", 594 (int)key->size, (char *)key->data); 595 596 if (!all) { 597 #ifdef DB_CACHE 598 if (TAILQ_FIRST(&qhead)->dbptr->key == NULL) { 599 #endif 600 (dbp->seq)(dbp,&lkey,&ldata,R_FIRST); 601 while (key->size != lkey.size || 602 strncmp(key->data, lkey.data, 603 (int)key->size)) 604 if ((dbp->seq)(dbp,&lkey,&ldata,R_NEXT)) { 605 #ifdef DB_CACHE 606 TAILQ_FIRST(&qhead)->dbptr->size = 0; 607 #endif 608 return(YP_NOKEY); 609 } 610 611 #ifdef DB_CACHE 612 } 613 #endif 614 } 615 616 if ((dbp->seq)(dbp,key,data,R_NEXT)) { 617 #ifdef DB_CACHE 618 TAILQ_FIRST(&qhead)->dbptr->size = 0; 619 #endif 620 return(YP_NOMORE); 621 } 622 623 /* Avoid passing back magic "YP_*" records. */ 624 while (!strncmp(key->data, "YP_", 3) && !allow) 625 if ((dbp->seq)(dbp,key,data,R_NEXT)) { 626 #ifdef DB_CACHE 627 TAILQ_FIRST(&qhead)->dbptr->size = 0; 628 #endif 629 return(YP_NOMORE); 630 } 631 632 if (ypdb_debug) 633 yp_error("result of lookup: key: [%.*s] data: [%.*s]", 634 (int)key->size, (char *)key->data, 635 (int)data->size, (char *)data->data); 636 637 #ifdef DB_CACHE 638 if (TAILQ_FIRST(&qhead)->dbptr->size) { 639 TAILQ_FIRST(&qhead)->dbptr->key = key->data; 640 TAILQ_FIRST(&qhead)->dbptr->size = key->size; 641 } 642 #else 643 bcopy(key->data, &keybuf, key->size); 644 lkey.data = &keybuf; 645 lkey.size = key->size; 646 bcopy(data->data, &datbuf, data->size); 647 data->data = &datbuf; 648 #endif 649 650 return(YP_TRUE); 651 } 652 653 #ifdef DB_CACHE 654 /* 655 * Database glue functions. 656 */ 657 658 static DB *yp_currmap_db = NULL; 659 static int yp_allow_db = 0; 660 661 ypstat 662 yp_select_map(char *map, char *domain, keydat *key, int allow) 663 { 664 if (key == NULL) 665 yp_currmap_db = yp_open_db_cache(domain, map, NULL, 0); 666 else 667 yp_currmap_db = yp_open_db_cache(domain, map, 668 key->keydat_val, 669 key->keydat_len); 670 671 yp_allow_db = allow; 672 return(yp_errno); 673 } 674 675 ypstat 676 yp_getbykey(keydat *key, valdat *val) 677 { 678 DBT db_key = { NULL, 0 }, db_val = { NULL, 0 }; 679 ypstat rval; 680 681 db_key.data = key->keydat_val; 682 db_key.size = key->keydat_len; 683 684 rval = yp_get_record(yp_currmap_db, 685 &db_key, &db_val, yp_allow_db); 686 687 if (rval == YP_TRUE) { 688 val->valdat_val = db_val.data; 689 val->valdat_len = db_val.size; 690 } 691 692 return(rval); 693 } 694 695 ypstat 696 yp_firstbykey(keydat *key, valdat *val) 697 { 698 DBT db_key = { NULL, 0 }, db_val = { NULL, 0 }; 699 ypstat rval; 700 701 rval = yp_first_record(yp_currmap_db, &db_key, &db_val, yp_allow_db); 702 703 if (rval == YP_TRUE) { 704 key->keydat_val = db_key.data; 705 key->keydat_len = db_key.size; 706 val->valdat_val = db_val.data; 707 val->valdat_len = db_val.size; 708 } 709 710 return(rval); 711 } 712 713 ypstat 714 yp_nextbykey(keydat *key, valdat *val) 715 { 716 DBT db_key = { NULL, 0 }, db_val = { NULL, 0 }; 717 ypstat rval; 718 719 db_key.data = key->keydat_val; 720 db_key.size = key->keydat_len; 721 722 rval = yp_next_record(yp_currmap_db, &db_key, &db_val, 0, yp_allow_db); 723 724 if (rval == YP_TRUE) { 725 key->keydat_val = db_key.data; 726 key->keydat_len = db_key.size; 727 val->valdat_val = db_val.data; 728 val->valdat_len = db_val.size; 729 } 730 731 return(rval); 732 } 733 #endif 734