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