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