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 * $Id: yp_dblookup.c,v 1.4 1996/07/07 19:04:33 wpaul Exp wpaul $ 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[] = "$Id: yp_dblookup.c,v 1.4 1996/07/07 19:04:33 wpaul Exp wpaul $"; 51 #endif 52 53 int ypdb_debug = 0; 54 int 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 int yp_get_record(domain,map,key,data,allow) 463 const char *domain; 464 const char *map; 465 const DBT *key; 466 DBT *data; 467 int allow; 468 { 469 DB *dbp; 470 int rval = 0; 471 #ifndef DB_CACHE 472 static unsigned char buf[YPMAXRECORD]; 473 #endif 474 475 if (ypdb_debug) 476 yp_error("Looking up key [%.*s] in map [%s]", 477 key->size, key->data, map); 478 479 /* 480 * Avoid passing back magic "YP_*" entries unless 481 * the caller specifically requested them by setting 482 * the 'allow' flag. 483 */ 484 if (!allow && !strncmp(key->data, "YP_", 3)) 485 return(YP_NOKEY); 486 487 #ifdef DB_CACHE 488 if ((dbp = yp_open_db_cache(domain, map, NULL, 0)) == NULL) { 489 #else 490 if ((dbp = yp_open_db(domain, map)) == NULL) { 491 #endif 492 return(yp_errno); 493 } 494 495 if ((rval = (dbp->get)(dbp, key, data, 0)) != 0) { 496 #ifdef DB_CACHE 497 qhead.cqh_first->dbptr->size = 0; 498 #else 499 (void)(dbp->close)(dbp); 500 #endif 501 if (rval == 1) 502 return(YP_NOKEY); 503 else 504 return(YP_BADDB); 505 } 506 507 if (ypdb_debug) 508 yp_error("Result of lookup: key: [%.*s] data: [%.*s]", 509 key->size, key->data, data->size, data->data); 510 511 #ifdef DB_CACHE 512 if (qhead.cqh_first->dbptr->size) { 513 qhead.cqh_first->dbptr->key = key->data; 514 qhead.cqh_first->dbptr->size = key->size; 515 } 516 #else 517 bcopy((char *)data->data, (char *)&buf, data->size); 518 data->data = (void *)&buf; 519 (void)(dbp->close)(dbp); 520 #endif 521 522 return(YP_TRUE); 523 } 524 525 int yp_first_record(dbp,key,data,allow) 526 const DB *dbp; 527 DBT *key; 528 DBT *data; 529 int allow; 530 { 531 int rval; 532 #ifndef DB_CACHE 533 static unsigned char buf[YPMAXRECORD]; 534 #endif 535 536 if (ypdb_debug) 537 yp_error("Retrieving first key in map."); 538 539 if ((rval = (dbp->seq)(dbp,key,data,R_FIRST)) != 0) { 540 #ifdef DB_CACHE 541 qhead.cqh_first->dbptr->size = 0; 542 #endif 543 if (rval == 1) 544 return(YP_NOKEY); 545 else 546 return(YP_BADDB); 547 } 548 549 /* Avoid passing back magic "YP_*" records. */ 550 while (!strncmp(key->data, "YP_", 3) && !allow) { 551 if ((rval = (dbp->seq)(dbp,key,data,R_NEXT)) != 0) { 552 #ifdef DB_CACHE 553 qhead.cqh_first->dbptr->size = 0; 554 #endif 555 if (rval == 1) 556 return(YP_NOKEY); 557 else 558 return(YP_BADDB); 559 } 560 } 561 562 if (ypdb_debug) 563 yp_error("Result of lookup: key: [%.*s] data: [%.*s]", 564 key->size, key->data, data->size, data->data); 565 566 #ifdef DB_CACHE 567 if (qhead.cqh_first->dbptr->size) { 568 qhead.cqh_first->dbptr->key = key->data; 569 qhead.cqh_first->dbptr->size = key->size; 570 } 571 #else 572 bcopy((char *)data->data, (char *)&buf, data->size); 573 data->data = (void *)&buf; 574 #endif 575 576 return(YP_TRUE); 577 } 578 579 int yp_next_record(dbp,key,data,all,allow) 580 const DB *dbp; 581 DBT *key; 582 DBT *data; 583 int all; 584 int allow; 585 { 586 static DBT lkey = { NULL, 0 }; 587 static DBT ldata = { NULL, 0 }; 588 int rval; 589 #ifndef DB_CACHE 590 static unsigned char keybuf[YPMAXRECORD]; 591 static unsigned char datbuf[YPMAXRECORD]; 592 #endif 593 594 if (key == NULL || key->data == NULL) { 595 rval = yp_first_record(dbp,key,data,allow); 596 if (rval == YP_NOKEY) 597 return(YP_NOMORE); 598 else 599 return(rval); 600 } 601 602 if (ypdb_debug) 603 yp_error("Retreiving next key, previous was: [%.*s]", 604 key->size, key->data); 605 606 if (!all) { 607 #ifdef DB_CACHE 608 if (qhead.cqh_first->dbptr->key == NULL) { 609 #endif 610 (dbp->seq)(dbp,&lkey,&ldata,R_FIRST); 611 while(strncmp((char *)key->data,lkey.data, 612 (int)key->size) || key->size != lkey.size) 613 if ((dbp->seq)(dbp,&lkey,&ldata,R_NEXT)) { 614 #ifdef DB_CACHE 615 qhead.cqh_first->dbptr->size = 0; 616 #endif 617 return(YP_NOKEY); 618 } 619 620 #ifdef DB_CACHE 621 } 622 #endif 623 } 624 625 if ((dbp->seq)(dbp,key,data,R_NEXT)) { 626 #ifdef DB_CACHE 627 qhead.cqh_first->dbptr->size = 0; 628 #endif 629 return(YP_NOMORE); 630 } 631 632 /* Avoid passing back magic "YP_*" records. */ 633 while (!strncmp(key->data, "YP_", 3) && !allow) 634 if ((dbp->seq)(dbp,key,data,R_NEXT)) { 635 #ifdef DB_CACHE 636 qhead.cqh_first->dbptr->size = 0; 637 #endif 638 return(YP_NOMORE); 639 } 640 641 if (ypdb_debug) 642 yp_error("Result of lookup: key: [%.*s] data: [%.*s]", 643 key->size, key->data, data->size, data->data); 644 645 #ifdef DB_CACHE 646 if (qhead.cqh_first->dbptr->size) { 647 qhead.cqh_first->dbptr->key = key->data; 648 qhead.cqh_first->dbptr->size = key->size; 649 } 650 #else 651 bcopy((char *)key->data, (char *)&keybuf, key->size); 652 lkey.data = (void *)&keybuf; 653 lkey.size = key->size; 654 bcopy((char *)data->data, (char *)&datbuf, data->size); 655 data->data = (void *)&datbuf; 656 #endif 657 658 return(YP_TRUE); 659 } 660