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 50int ypdb_debug = 0; 51enum ypstat yp_errno = YP_TRUE; 52 53#define PERM_SECURE (S_IRUSR|S_IWUSR) 54HASHINFO 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 70static int numdbs = 0; 71 72struct dbent { 73 DB *dbp; 74 char *name; 75 char *key; 76 int size; 77 int flags; 78}; 79 80static TAILQ_HEAD(circlehead, circleq_entry) qhead; 81 82struct circleq_entry { 83 struct dbent *dbptr; 84 TAILQ_ENTRY(circleq_entry) links; 85}; 86 87/* 88 * Initialize the circular queue. 89 */ 90void 91yp_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 */ 101static struct circleq_entry * 102yp_malloc_qent(void) 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 */ 127static void 128yp_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 */ 165static void 166yp_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 */ 181void 182yp_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 196static char *inter_string = "YP_INTERDOMAIN"; 197static char *secure_string = "YP_SECURE"; 198static int inter_sz = sizeof("YP_INTERDOMAIN") - 1; 199static int secure_sz = sizeof("YP_SECURE") - 1; 200 201static int 202yp_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 222int 223yp_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 */ 259static int 260yp_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 */ 318static DB * 319yp_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 */ 350DB * 351yp_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 */ 384DB * 385yp_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 406again: 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 451int 452yp_get_record(DB *dbp, const DBT *key, DBT *data, int allow) 453#else 454int 455yp_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 516int 517yp_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 568int 569yp_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 658static DB *yp_currmap_db = NULL; 659static int yp_allow_db = 0; 660 661ypstat 662yp_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 675ypstat 676yp_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 695ypstat 696yp_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 713ypstat 714yp_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