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 CIRCLEQ_HEAD(circlehead, circleq_entry) qhead; 83 84 struct circleq_entry { 85 struct dbent *dbptr; 86 CIRCLEQ_ENTRY(circleq_entry) links; 87 }; 88 89 /* 90 * Initialize the circular queue. 91 */ 92 void yp_init_dbs() 93 { 94 CIRCLEQ_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 = qhead.cqh_last; 170 CIRCLEQ_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(qhead.cqh_first != (void *)&qhead) { 185 qptr = qhead.cqh_first; /* save this */ 186 CIRCLEQ_REMOVE(&qhead, qhead.cqh_first, 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 for (qptr = qhead.cqh_first; qptr != (void *)&qhead; 236 qptr = qptr->links.cqe_next) { 237 if (!strcmp(qptr->dbptr->name, buf)) { 238 if (qptr->dbptr->flags & flag) 239 return(1); 240 else 241 return(0); 242 } 243 } 244 245 if (yp_open_db_cache(domain, map, NULL, 0) == NULL) 246 return(0); 247 248 if (qhead.cqh_first->dbptr->flags & flag) 249 return(1); 250 251 return(0); 252 } 253 254 /* 255 * Add a DB handle and database name to the cache. We only maintain 256 * fixed number of entries in the cache, so if we're asked to store 257 * a new entry when all our slots are already filled, we have to kick 258 * out the entry in the last slot to make room. 259 */ 260 static int yp_cache_db(dbp, name, size) 261 DB *dbp; 262 char *name; 263 int size; 264 { 265 register struct circleq_entry *qptr; 266 267 if (numdbs == MAXDBS) { 268 if (ypdb_debug) 269 yp_error("queue overflow -- releasing last slot"); 270 yp_flush(); 271 } 272 273 /* 274 * Allocate a new queue entry. 275 */ 276 277 if ((qptr = yp_malloc_qent()) == NULL) { 278 yp_error("failed to allocate a new cache entry"); 279 return(1); 280 } 281 282 qptr->dbptr->dbp = dbp; 283 qptr->dbptr->name = strdup(name); 284 qptr->dbptr->size = size; 285 qptr->dbptr->key = NULL; 286 287 qptr->dbptr->flags = yp_setflags(dbp); 288 289 CIRCLEQ_INSERT_HEAD(&qhead, qptr, links); 290 numdbs++; 291 292 return(0); 293 } 294 295 /* 296 * Search the list for a database matching 'name.' If we find it, 297 * move it to the head of the list and return its DB handle. If 298 * not, just fail: yp_open_db_cache() will subsequently try to open 299 * the database itself and call yp_cache_db() to add it to the 300 * list. 301 * 302 * The search works like this: 303 * 304 * - The caller specifies the name of a database to locate. We try to 305 * find an entry in our queue with a matching name. 306 * 307 * - If the caller doesn't specify a key or size, we assume that the 308 * first entry that we encounter with a matching name is returned. 309 * This will result in matches regardless of the key/size values 310 * stored in the queue entry. 311 * 312 * - If the caller also specifies a key and length, we check to see 313 * if the key and length saved in the queue entry also matches. 314 * This lets us return a DB handle that's already positioned at the 315 * correct location within a database. 316 * 317 * - Once we have a match, it gets migrated to the top of the queue 318 * so that it will be easier to find if another request for 319 * the same database comes in later. 320 */ 321 static DB *yp_find_db(name, key, size) 322 char *name; 323 char *key; 324 int size; 325 { 326 register struct circleq_entry *qptr; 327 328 for (qptr = qhead.cqh_first; qptr != (void *)&qhead; 329 qptr = qptr->links.cqe_next) { 330 if (!strcmp(qptr->dbptr->name, name)) { 331 if (size) { 332 if (size != qptr->dbptr->size || 333 strncmp(qptr->dbptr->key, key, size)) 334 continue; 335 } else { 336 if (qptr->dbptr->size) 337 continue; 338 } 339 if (qptr != qhead.cqh_first) { 340 CIRCLEQ_REMOVE(&qhead, qptr, links); 341 CIRCLEQ_INSERT_HEAD(&qhead, qptr, links); 342 } 343 return(qptr->dbptr->dbp); 344 } 345 } 346 347 return(NULL); 348 } 349 350 /* 351 * Open a DB database and cache the handle for later use. We first 352 * check the cache to see if the required database is already open. 353 * If so, we fetch the handle from the cache. If not, we try to open 354 * the database and save the handle in the cache for later use. 355 */ 356 DB *yp_open_db_cache(domain, map, key, size) 357 const char *domain; 358 const char *map; 359 const char *key; 360 const int size; 361 { 362 DB *dbp = NULL; 363 char buf[MAXPATHLEN + 2]; 364 /* 365 snprintf(buf, sizeof(buf), "%s/%s", domain, map); 366 */ 367 yp_errno = YP_TRUE; 368 369 strcpy(buf, domain); 370 strcat(buf, "/"); 371 strcat(buf, map); 372 373 if ((dbp = yp_find_db((char *)&buf, key, size)) != NULL) { 374 return(dbp); 375 } else { 376 if ((dbp = yp_open_db(domain, map)) != NULL) { 377 if (yp_cache_db(dbp, (char *)&buf, size)) { 378 (void)(dbp->close)(dbp); 379 yp_errno = YP_YPERR; 380 return(NULL); 381 } 382 } 383 } 384 385 return (dbp); 386 } 387 #endif 388 389 /* 390 * Open a DB database. 391 */ 392 DB *yp_open_db(domain, map) 393 const char *domain; 394 const char *map; 395 { 396 DB *dbp = NULL; 397 char buf[MAXPATHLEN + 2]; 398 399 yp_errno = YP_TRUE; 400 401 if (map[0] == '.' || strchr(map, '/')) { 402 yp_errno = YP_BADARGS; 403 return (NULL); 404 } 405 406 #ifdef DB_CACHE 407 if (yp_validdomain(domain)) { 408 yp_errno = YP_NODOM; 409 return(NULL); 410 } 411 #endif 412 snprintf(buf, sizeof(buf), "%s/%s/%s", yp_dir, domain, map); 413 414 #ifdef DB_CACHE 415 again: 416 #endif 417 dbp = dbopen(buf,O_RDONLY, PERM_SECURE, DB_HASH, NULL); 418 419 if (dbp == NULL) { 420 switch(errno) { 421 #ifdef DB_CACHE 422 case ENFILE: 423 /* 424 * We ran out of file descriptors. Nuke an 425 * open one and try again. 426 */ 427 yp_error("ran out of file descriptors"); 428 yp_flush(); 429 goto again; 430 break; 431 #endif 432 case ENOENT: 433 yp_errno = YP_NOMAP; 434 break; 435 case EFTYPE: 436 yp_errno = YP_BADDB; 437 break; 438 default: 439 yp_errno = YP_YPERR; 440 break; 441 } 442 } 443 444 return (dbp); 445 } 446 447 /* 448 * Database access routines. 449 * 450 * - yp_get_record(): retrieve an arbitrary key/data pair given one key 451 * to match against. 452 * 453 * - yp_first_record(): retrieve first key/data base in a database. 454 * 455 * - yp_next_record(): retrieve key/data pair that sequentially follows 456 * the supplied key value in the database. 457 */ 458 459 #ifdef DB_CACHE 460 int yp_get_record(dbp,key,data,allow) 461 DB *dbp; 462 #else 463 int yp_get_record(domain,map,key,data,allow) 464 const char *domain; 465 const char *map; 466 #endif 467 const DBT *key; 468 DBT *data; 469 int allow; 470 { 471 #ifndef DB_CACHE 472 DB *dbp; 473 #endif 474 int rval = 0; 475 #ifndef DB_CACHE 476 static unsigned char buf[YPMAXRECORD]; 477 #endif 478 479 if (ypdb_debug) 480 yp_error("looking up key [%.*s]", 481 key->size, key->data); 482 483 /* 484 * Avoid passing back magic "YP_*" entries unless 485 * the caller specifically requested them by setting 486 * the 'allow' flag. 487 */ 488 if (!allow && !strncmp(key->data, "YP_", 3)) 489 return(YP_NOKEY); 490 491 #ifndef DB_CACHE 492 if ((dbp = yp_open_db(domain, map)) == NULL) { 493 return(yp_errno); 494 } 495 #endif 496 497 if ((rval = (dbp->get)(dbp, key, data, 0)) != 0) { 498 #ifdef DB_CACHE 499 qhead.cqh_first->dbptr->size = 0; 500 #else 501 (void)(dbp->close)(dbp); 502 #endif 503 if (rval == 1) 504 return(YP_NOKEY); 505 else 506 return(YP_BADDB); 507 } 508 509 if (ypdb_debug) 510 yp_error("result of lookup: key: [%.*s] data: [%.*s]", 511 key->size, key->data, data->size, data->data); 512 513 #ifdef DB_CACHE 514 if (qhead.cqh_first->dbptr->size) { 515 qhead.cqh_first->dbptr->key = ""; 516 qhead.cqh_first->dbptr->size = 0; 517 } 518 #else 519 bcopy((char *)data->data, (char *)&buf, data->size); 520 data->data = (void *)&buf; 521 (void)(dbp->close)(dbp); 522 #endif 523 524 return(YP_TRUE); 525 } 526 527 int yp_first_record(dbp,key,data,allow) 528 const DB *dbp; 529 DBT *key; 530 DBT *data; 531 int allow; 532 { 533 int rval; 534 #ifndef DB_CACHE 535 static unsigned char buf[YPMAXRECORD]; 536 #endif 537 538 if (ypdb_debug) 539 yp_error("retrieving first key in map"); 540 541 if ((rval = (dbp->seq)(dbp,key,data,R_FIRST)) != 0) { 542 #ifdef DB_CACHE 543 qhead.cqh_first->dbptr->size = 0; 544 #endif 545 if (rval == 1) 546 return(YP_NOKEY); 547 else 548 return(YP_BADDB); 549 } 550 551 /* Avoid passing back magic "YP_*" records. */ 552 while (!strncmp(key->data, "YP_", 3) && !allow) { 553 if ((rval = (dbp->seq)(dbp,key,data,R_NEXT)) != 0) { 554 #ifdef DB_CACHE 555 qhead.cqh_first->dbptr->size = 0; 556 #endif 557 if (rval == 1) 558 return(YP_NOKEY); 559 else 560 return(YP_BADDB); 561 } 562 } 563 564 if (ypdb_debug) 565 yp_error("result of lookup: key: [%.*s] data: [%.*s]", 566 key->size, key->data, data->size, data->data); 567 568 #ifdef DB_CACHE 569 if (qhead.cqh_first->dbptr->size) { 570 qhead.cqh_first->dbptr->key = key->data; 571 qhead.cqh_first->dbptr->size = key->size; 572 } 573 #else 574 bcopy((char *)data->data, (char *)&buf, data->size); 575 data->data = (void *)&buf; 576 #endif 577 578 return(YP_TRUE); 579 } 580 581 int yp_next_record(dbp,key,data,all,allow) 582 const DB *dbp; 583 DBT *key; 584 DBT *data; 585 int all; 586 int allow; 587 { 588 static DBT lkey = { NULL, 0 }; 589 static DBT ldata = { NULL, 0 }; 590 int rval; 591 #ifndef DB_CACHE 592 static unsigned char keybuf[YPMAXRECORD]; 593 static unsigned char datbuf[YPMAXRECORD]; 594 #endif 595 596 if (key == NULL || !key->size || key->data == NULL) { 597 rval = yp_first_record(dbp,key,data,allow); 598 if (rval == YP_NOKEY) 599 return(YP_NOMORE); 600 else { 601 #ifdef DB_CACHE 602 qhead.cqh_first->dbptr->key = key->data; 603 qhead.cqh_first->dbptr->size = key->size; 604 #endif 605 return(rval); 606 } 607 } 608 609 if (ypdb_debug) 610 yp_error("retrieving next key, previous was: [%.*s]", 611 key->size, key->data); 612 613 if (!all) { 614 #ifdef DB_CACHE 615 if (qhead.cqh_first->dbptr->key == NULL) { 616 #endif 617 (dbp->seq)(dbp,&lkey,&ldata,R_FIRST); 618 while (key->size != lkey.size || 619 strncmp((char *)key->data, lkey.data, 620 (int)key->size)) 621 if ((dbp->seq)(dbp,&lkey,&ldata,R_NEXT)) { 622 #ifdef DB_CACHE 623 qhead.cqh_first->dbptr->size = 0; 624 #endif 625 return(YP_NOKEY); 626 } 627 628 #ifdef DB_CACHE 629 } 630 #endif 631 } 632 633 if ((dbp->seq)(dbp,key,data,R_NEXT)) { 634 #ifdef DB_CACHE 635 qhead.cqh_first->dbptr->size = 0; 636 #endif 637 return(YP_NOMORE); 638 } 639 640 /* Avoid passing back magic "YP_*" records. */ 641 while (!strncmp(key->data, "YP_", 3) && !allow) 642 if ((dbp->seq)(dbp,key,data,R_NEXT)) { 643 #ifdef DB_CACHE 644 qhead.cqh_first->dbptr->size = 0; 645 #endif 646 return(YP_NOMORE); 647 } 648 649 if (ypdb_debug) 650 yp_error("result of lookup: key: [%.*s] data: [%.*s]", 651 key->size, key->data, data->size, data->data); 652 653 #ifdef DB_CACHE 654 if (qhead.cqh_first->dbptr->size) { 655 qhead.cqh_first->dbptr->key = key->data; 656 qhead.cqh_first->dbptr->size = key->size; 657 } 658 #else 659 bcopy((char *)key->data, (char *)&keybuf, key->size); 660 lkey.data = (void *)&keybuf; 661 lkey.size = key->size; 662 bcopy((char *)data->data, (char *)&datbuf, data->size); 663 data->data = (void *)&datbuf; 664 #endif 665 666 return(YP_TRUE); 667 } 668 669 #ifdef DB_CACHE 670 /* 671 * Database glue functions. 672 */ 673 674 static DB *yp_currmap_db = NULL; 675 static int yp_allow_db = 0; 676 677 ypstat yp_select_map(map, domain, key, allow) 678 char *map; 679 char *domain; 680 keydat *key; 681 int allow; 682 { 683 if (key == NULL) 684 yp_currmap_db = yp_open_db_cache(domain, map, NULL, 0); 685 else 686 yp_currmap_db = yp_open_db_cache(domain, map, 687 key->keydat_val, 688 key->keydat_len); 689 690 yp_allow_db = allow; 691 return(yp_errno); 692 } 693 694 ypstat yp_getbykey(key, val) 695 keydat *key; 696 valdat *val; 697 { 698 DBT db_key = { NULL, 0 }, db_val = { NULL, 0 }; 699 ypstat rval; 700 701 db_key.data = key->keydat_val; 702 db_key.size = key->keydat_len; 703 704 rval = yp_get_record(yp_currmap_db, 705 &db_key, &db_val, yp_allow_db); 706 707 if (rval == YP_TRUE) { 708 val->valdat_val = db_val.data; 709 val->valdat_len = db_val.size; 710 } 711 712 return(rval); 713 } 714 715 ypstat yp_firstbykey(key, val) 716 keydat *key; 717 valdat *val; 718 { 719 DBT db_key = { NULL, 0 }, db_val = { NULL, 0 }; 720 ypstat rval; 721 722 rval = yp_first_record(yp_currmap_db, &db_key, &db_val, 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 734 ypstat yp_nextbykey(key, val) 735 keydat *key; 736 valdat *val; 737 { 738 DBT db_key = { NULL, 0 }, db_val = { NULL, 0 }; 739 ypstat rval; 740 741 db_key.data = key->keydat_val; 742 db_key.size = key->keydat_len; 743 744 rval = yp_next_record(yp_currmap_db, &db_key, &db_val, 0, yp_allow_db); 745 746 if (rval == YP_TRUE) { 747 key->keydat_val = db_key.data; 748 key->keydat_len = db_key.size; 749 val->valdat_val = db_val.data; 750 val->valdat_len = db_val.size; 751 } 752 753 return(rval); 754 } 755 #endif 756