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