xref: /freebsd/usr.sbin/ypserv/yp_dblookup.c (revision 9ee40678bbdcedc6a3ac1e311abe740018911cf1)
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