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 #ifndef lint 34 static const char rcsid[] = 35 "$FreeBSD$"; 36 #endif /* not lint */ 37 38 #include <db.h> 39 #include <errno.h> 40 #include <fcntl.h> 41 #include <limits.h> 42 #include <paths.h> 43 #include <stdio.h> 44 #include <stdlib.h> 45 #include <string.h> 46 #include <unistd.h> 47 #include <sys/stat.h> 48 #include <sys/param.h> 49 #include <rpcsvc/yp.h> 50 #include "yp_extern.h" 51 52 int ypdb_debug = 0; 53 enum ypstat yp_errno = YP_TRUE; 54 55 #define PERM_SECURE (S_IRUSR|S_IWUSR) 56 HASHINFO openinfo = { 57 4096, /* bsize */ 58 32, /* ffactor */ 59 256, /* nelem */ 60 2048 * 512, /* cachesize */ 61 NULL, /* hash */ 62 0, /* lorder */ 63 }; 64 65 #ifdef DB_CACHE 66 #include <sys/queue.h> 67 68 #ifndef MAXDBS 69 #define MAXDBS 20 70 #endif 71 72 static int numdbs = 0; 73 74 struct dbent { 75 DB *dbp; 76 char *name; 77 char *key; 78 int size; 79 int flags; 80 }; 81 82 static TAILQ_HEAD(circlehead, circleq_entry) qhead; 83 84 struct circleq_entry { 85 struct dbent *dbptr; 86 TAILQ_ENTRY(circleq_entry) links; 87 }; 88 89 /* 90 * Initialize the circular queue. 91 */ 92 void yp_init_dbs() 93 { 94 TAILQ_INIT(&qhead); 95 return; 96 } 97 98 /* 99 * Dynamically allocate an entry for the circular queue. 100 * Return a NULL pointer on failure. 101 */ 102 static struct circleq_entry *yp_malloc_qent() 103 { 104 register struct circleq_entry *q; 105 106 q = (struct circleq_entry *)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 = (struct dbent *)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 yp_free_qent(q) 128 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 yp_flush() 166 { 167 register struct circleq_entry *qptr; 168 169 qptr = TAILQ_LAST(&qhead, circlehead); 170 TAILQ_REMOVE(&qhead, qptr, links); 171 yp_free_qent(qptr); 172 numdbs--; 173 174 return; 175 } 176 177 /* 178 * Close all databases, erase all database names and empty the queue. 179 */ 180 void yp_flush_all() 181 { 182 register struct circleq_entry *qptr; 183 184 while(!TAILQ_EMPTY(&qhead)) { 185 qptr = TAILQ_FIRST(&qhead); /* save this */ 186 TAILQ_REMOVE(&qhead, qptr, links); 187 yp_free_qent(qptr); 188 } 189 numdbs = 0; 190 191 return; 192 } 193 194 static char *inter_string = "YP_INTERDOMAIN"; 195 static char *secure_string = "YP_SECURE"; 196 static int inter_sz = sizeof("YP_INTERDOMAIN") - 1; 197 static int secure_sz = sizeof("YP_SECURE") - 1; 198 199 static int yp_setflags(dbp) 200 DB *dbp; 201 { 202 DBT key = { NULL, 0 }, data = { NULL, 0 }; 203 int flags = 0; 204 205 key.data = inter_string; 206 key.size = inter_sz; 207 208 if (!(dbp->get)(dbp, &key, &data, 0)) 209 flags |= YP_INTERDOMAIN; 210 211 key.data = secure_string; 212 key.size = secure_sz; 213 214 if (!(dbp->get)(dbp, &key, &data, 0)) 215 flags |= YP_SECURE; 216 217 return(flags); 218 } 219 220 int yp_testflag(map, domain, flag) 221 char *map; 222 char *domain; 223 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 yp_cache_db(dbp, name, size) 260 DB *dbp; 261 char *name; 262 int size; 263 { 264 register struct circleq_entry *qptr; 265 266 if (numdbs == MAXDBS) { 267 if (ypdb_debug) 268 yp_error("queue overflow -- releasing last slot"); 269 yp_flush(); 270 } 271 272 /* 273 * Allocate a new queue entry. 274 */ 275 276 if ((qptr = yp_malloc_qent()) == NULL) { 277 yp_error("failed to allocate a new cache entry"); 278 return(1); 279 } 280 281 qptr->dbptr->dbp = dbp; 282 qptr->dbptr->name = strdup(name); 283 qptr->dbptr->size = size; 284 qptr->dbptr->key = NULL; 285 286 qptr->dbptr->flags = yp_setflags(dbp); 287 288 TAILQ_INSERT_HEAD(&qhead, qptr, links); 289 numdbs++; 290 291 return(0); 292 } 293 294 /* 295 * Search the list for a database matching 'name.' If we find it, 296 * move it to the head of the list and return its DB handle. If 297 * not, just fail: yp_open_db_cache() will subsequently try to open 298 * the database itself and call yp_cache_db() to add it to the 299 * list. 300 * 301 * The search works like this: 302 * 303 * - The caller specifies the name of a database to locate. We try to 304 * find an entry in our queue with a matching name. 305 * 306 * - If the caller doesn't specify a key or size, we assume that the 307 * first entry that we encounter with a matching name is returned. 308 * This will result in matches regardless of the key/size values 309 * stored in the queue entry. 310 * 311 * - If the caller also specifies a key and length, we check to see 312 * if the key and length saved in the queue entry also matches. 313 * This lets us return a DB handle that's already positioned at the 314 * correct location within a database. 315 * 316 * - Once we have a match, it gets migrated to the top of the queue 317 * so that it will be easier to find if another request for 318 * the same database comes in later. 319 */ 320 static DB *yp_find_db(name, key, size) 321 char *name; 322 char *key; 323 int size; 324 { 325 register struct circleq_entry *qptr; 326 327 TAILQ_FOREACH(qptr, &qhead, links) { 328 if (!strcmp(qptr->dbptr->name, name)) { 329 if (size) { 330 if (size != qptr->dbptr->size || 331 strncmp(qptr->dbptr->key, key, size)) 332 continue; 333 } else { 334 if (qptr->dbptr->size) 335 continue; 336 } 337 if (qptr != TAILQ_FIRST(&qhead)) { 338 TAILQ_REMOVE(&qhead, qptr, links); 339 TAILQ_INSERT_HEAD(&qhead, qptr, links); 340 } 341 return(qptr->dbptr->dbp); 342 } 343 } 344 345 return(NULL); 346 } 347 348 /* 349 * Open a DB database and cache the handle for later use. We first 350 * check the cache to see if the required database is already open. 351 * If so, we fetch the handle from the cache. If not, we try to open 352 * the database and save the handle in the cache for later use. 353 */ 354 DB *yp_open_db_cache(domain, map, key, size) 355 const char *domain; 356 const char *map; 357 const char *key; 358 const int size; 359 { 360 DB *dbp = NULL; 361 char buf[MAXPATHLEN + 2]; 362 /* 363 snprintf(buf, sizeof(buf), "%s/%s", domain, map); 364 */ 365 yp_errno = YP_TRUE; 366 367 strcpy(buf, domain); 368 strcat(buf, "/"); 369 strcat(buf, map); 370 371 if ((dbp = yp_find_db((char *)&buf, key, size)) != NULL) { 372 return(dbp); 373 } else { 374 if ((dbp = yp_open_db(domain, map)) != NULL) { 375 if (yp_cache_db(dbp, (char *)&buf, size)) { 376 (void)(dbp->close)(dbp); 377 yp_errno = YP_YPERR; 378 return(NULL); 379 } 380 } 381 } 382 383 return (dbp); 384 } 385 #endif 386 387 /* 388 * Open a DB database. 389 */ 390 DB *yp_open_db(domain, map) 391 const char *domain; 392 const char *map; 393 { 394 DB *dbp = NULL; 395 char buf[MAXPATHLEN + 2]; 396 397 yp_errno = YP_TRUE; 398 399 if (map[0] == '.' || strchr(map, '/')) { 400 yp_errno = YP_BADARGS; 401 return (NULL); 402 } 403 404 #ifdef DB_CACHE 405 if (yp_validdomain(domain)) { 406 yp_errno = YP_NODOM; 407 return(NULL); 408 } 409 #endif 410 snprintf(buf, sizeof(buf), "%s/%s/%s", yp_dir, domain, map); 411 412 #ifdef DB_CACHE 413 again: 414 #endif 415 dbp = dbopen(buf,O_RDONLY, PERM_SECURE, DB_HASH, NULL); 416 417 if (dbp == NULL) { 418 switch(errno) { 419 #ifdef DB_CACHE 420 case ENFILE: 421 /* 422 * We ran out of file descriptors. Nuke an 423 * open one and try again. 424 */ 425 yp_error("ran out of file descriptors"); 426 yp_flush(); 427 goto again; 428 break; 429 #endif 430 case ENOENT: 431 yp_errno = YP_NOMAP; 432 break; 433 case EFTYPE: 434 yp_errno = YP_BADDB; 435 break; 436 default: 437 yp_errno = YP_YPERR; 438 break; 439 } 440 } 441 442 return (dbp); 443 } 444 445 /* 446 * Database access routines. 447 * 448 * - yp_get_record(): retrieve an arbitrary key/data pair given one key 449 * to match against. 450 * 451 * - yp_first_record(): retrieve first key/data base in a database. 452 * 453 * - yp_next_record(): retrieve key/data pair that sequentially follows 454 * the supplied key value in the database. 455 */ 456 457 #ifdef DB_CACHE 458 int yp_get_record(dbp,key,data,allow) 459 DB *dbp; 460 #else 461 int yp_get_record(domain,map,key,data,allow) 462 const char *domain; 463 const char *map; 464 #endif 465 const DBT *key; 466 DBT *data; 467 int allow; 468 { 469 #ifndef DB_CACHE 470 DB *dbp; 471 #endif 472 int rval = 0; 473 #ifndef DB_CACHE 474 static unsigned char buf[YPMAXRECORD]; 475 #endif 476 477 if (ypdb_debug) 478 yp_error("looking up key [%.*s]", 479 key->size, key->data); 480 481 /* 482 * Avoid passing back magic "YP_*" entries unless 483 * the caller specifically requested them by setting 484 * the 'allow' flag. 485 */ 486 if (!allow && !strncmp(key->data, "YP_", 3)) 487 return(YP_NOKEY); 488 489 #ifndef DB_CACHE 490 if ((dbp = yp_open_db(domain, map)) == NULL) { 491 return(yp_errno); 492 } 493 #endif 494 495 if ((rval = (dbp->get)(dbp, key, data, 0)) != 0) { 496 #ifdef DB_CACHE 497 TAILQ_FIRST(&qhead)->dbptr->size = 0; 498 #else 499 (void)(dbp->close)(dbp); 500 #endif 501 if (rval == 1) 502 return(YP_NOKEY); 503 else 504 return(YP_BADDB); 505 } 506 507 if (ypdb_debug) 508 yp_error("result of lookup: key: [%.*s] data: [%.*s]", 509 key->size, key->data, data->size, data->data); 510 511 #ifdef DB_CACHE 512 if (TAILQ_FIRST(&qhead)->dbptr->size) { 513 TAILQ_FIRST(&qhead)->dbptr->key = ""; 514 TAILQ_FIRST(&qhead)->dbptr->size = 0; 515 } 516 #else 517 bcopy((char *)data->data, (char *)&buf, data->size); 518 data->data = (void *)&buf; 519 (void)(dbp->close)(dbp); 520 #endif 521 522 return(YP_TRUE); 523 } 524 525 int yp_first_record(dbp,key,data,allow) 526 const DB *dbp; 527 DBT *key; 528 DBT *data; 529 int allow; 530 { 531 int rval; 532 #ifndef DB_CACHE 533 static unsigned char buf[YPMAXRECORD]; 534 #endif 535 536 if (ypdb_debug) 537 yp_error("retrieving first key in map"); 538 539 if ((rval = (dbp->seq)(dbp,key,data,R_FIRST)) != 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 /* Avoid passing back magic "YP_*" records. */ 550 while (!strncmp(key->data, "YP_", 3) && !allow) { 551 if ((rval = (dbp->seq)(dbp,key,data,R_NEXT)) != 0) { 552 #ifdef DB_CACHE 553 TAILQ_FIRST(&qhead)->dbptr->size = 0; 554 #endif 555 if (rval == 1) 556 return(YP_NOKEY); 557 else 558 return(YP_BADDB); 559 } 560 } 561 562 if (ypdb_debug) 563 yp_error("result of lookup: key: [%.*s] data: [%.*s]", 564 key->size, key->data, data->size, data->data); 565 566 #ifdef DB_CACHE 567 if (TAILQ_FIRST(&qhead)->dbptr->size) { 568 TAILQ_FIRST(&qhead)->dbptr->key = key->data; 569 TAILQ_FIRST(&qhead)->dbptr->size = key->size; 570 } 571 #else 572 bcopy((char *)data->data, (char *)&buf, data->size); 573 data->data = (void *)&buf; 574 #endif 575 576 return(YP_TRUE); 577 } 578 579 int yp_next_record(dbp,key,data,all,allow) 580 const DB *dbp; 581 DBT *key; 582 DBT *data; 583 int all; 584 int allow; 585 { 586 static DBT lkey = { NULL, 0 }; 587 static DBT ldata = { NULL, 0 }; 588 int rval; 589 #ifndef DB_CACHE 590 static unsigned char keybuf[YPMAXRECORD]; 591 static unsigned char datbuf[YPMAXRECORD]; 592 #endif 593 594 if (key == NULL || !key->size || key->data == NULL) { 595 rval = yp_first_record(dbp,key,data,allow); 596 if (rval == YP_NOKEY) 597 return(YP_NOMORE); 598 else { 599 #ifdef DB_CACHE 600 TAILQ_FIRST(&qhead)->dbptr->key = key->data; 601 TAILQ_FIRST(&qhead)->dbptr->size = key->size; 602 #endif 603 return(rval); 604 } 605 } 606 607 if (ypdb_debug) 608 yp_error("retrieving next key, previous was: [%.*s]", 609 key->size, key->data); 610 611 if (!all) { 612 #ifdef DB_CACHE 613 if (TAILQ_FIRST(&qhead)->dbptr->key == NULL) { 614 #endif 615 (dbp->seq)(dbp,&lkey,&ldata,R_FIRST); 616 while (key->size != lkey.size || 617 strncmp((char *)key->data, lkey.data, 618 (int)key->size)) 619 if ((dbp->seq)(dbp,&lkey,&ldata,R_NEXT)) { 620 #ifdef DB_CACHE 621 TAILQ_FIRST(&qhead)->dbptr->size = 0; 622 #endif 623 return(YP_NOKEY); 624 } 625 626 #ifdef DB_CACHE 627 } 628 #endif 629 } 630 631 if ((dbp->seq)(dbp,key,data,R_NEXT)) { 632 #ifdef DB_CACHE 633 TAILQ_FIRST(&qhead)->dbptr->size = 0; 634 #endif 635 return(YP_NOMORE); 636 } 637 638 /* Avoid passing back magic "YP_*" records. */ 639 while (!strncmp(key->data, "YP_", 3) && !allow) 640 if ((dbp->seq)(dbp,key,data,R_NEXT)) { 641 #ifdef DB_CACHE 642 TAILQ_FIRST(&qhead)->dbptr->size = 0; 643 #endif 644 return(YP_NOMORE); 645 } 646 647 if (ypdb_debug) 648 yp_error("result of lookup: key: [%.*s] data: [%.*s]", 649 key->size, key->data, data->size, data->data); 650 651 #ifdef DB_CACHE 652 if (TAILQ_FIRST(&qhead)->dbptr->size) { 653 TAILQ_FIRST(&qhead)->dbptr->key = key->data; 654 TAILQ_FIRST(&qhead)->dbptr->size = key->size; 655 } 656 #else 657 bcopy((char *)key->data, (char *)&keybuf, key->size); 658 lkey.data = (void *)&keybuf; 659 lkey.size = key->size; 660 bcopy((char *)data->data, (char *)&datbuf, data->size); 661 data->data = (void *)&datbuf; 662 #endif 663 664 return(YP_TRUE); 665 } 666 667 #ifdef DB_CACHE 668 /* 669 * Database glue functions. 670 */ 671 672 static DB *yp_currmap_db = NULL; 673 static int yp_allow_db = 0; 674 675 ypstat yp_select_map(map, domain, key, allow) 676 char *map; 677 char *domain; 678 keydat *key; 679 int allow; 680 { 681 if (key == NULL) 682 yp_currmap_db = yp_open_db_cache(domain, map, NULL, 0); 683 else 684 yp_currmap_db = yp_open_db_cache(domain, map, 685 key->keydat_val, 686 key->keydat_len); 687 688 yp_allow_db = allow; 689 return(yp_errno); 690 } 691 692 ypstat yp_getbykey(key, val) 693 keydat *key; 694 valdat *val; 695 { 696 DBT db_key = { NULL, 0 }, db_val = { NULL, 0 }; 697 ypstat rval; 698 699 db_key.data = key->keydat_val; 700 db_key.size = key->keydat_len; 701 702 rval = yp_get_record(yp_currmap_db, 703 &db_key, &db_val, yp_allow_db); 704 705 if (rval == YP_TRUE) { 706 val->valdat_val = db_val.data; 707 val->valdat_len = db_val.size; 708 } 709 710 return(rval); 711 } 712 713 ypstat yp_firstbykey(key, val) 714 keydat *key; 715 valdat *val; 716 { 717 DBT db_key = { NULL, 0 }, db_val = { NULL, 0 }; 718 ypstat rval; 719 720 rval = yp_first_record(yp_currmap_db, &db_key, &db_val, yp_allow_db); 721 722 if (rval == YP_TRUE) { 723 key->keydat_val = db_key.data; 724 key->keydat_len = db_key.size; 725 val->valdat_val = db_val.data; 726 val->valdat_len = db_val.size; 727 } 728 729 return(rval); 730 } 731 732 ypstat yp_nextbykey(key, val) 733 keydat *key; 734 valdat *val; 735 { 736 DBT db_key = { NULL, 0 }, db_val = { NULL, 0 }; 737 ypstat rval; 738 739 db_key.data = key->keydat_val; 740 db_key.size = key->keydat_len; 741 742 rval = yp_next_record(yp_currmap_db, &db_key, &db_val, 0, yp_allow_db); 743 744 if (rval == YP_TRUE) { 745 key->keydat_val = db_key.data; 746 key->keydat_len = db_key.size; 747 val->valdat_val = db_val.data; 748 val->valdat_len = db_val.size; 749 } 750 751 return(rval); 752 } 753 #endif 754