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