yp_dblookup.c revision 70493
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
34static const char rcsid[] =
35  "$FreeBSD: head/usr.sbin/ypserv/yp_dblookup.c 70493 2000-12-29 20:33:28Z phk $";
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
52int ypdb_debug = 0;
53enum ypstat yp_errno = YP_TRUE;
54
55#define PERM_SECURE (S_IRUSR|S_IWUSR)
56HASHINFO 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
72static int numdbs = 0;
73
74struct dbent {
75	DB *dbp;
76	char *name;
77	char *key;
78	int size;
79	int flags;
80};
81
82static TAILQ_HEAD(circlehead, circleq_entry) qhead;
83
84struct circleq_entry {
85	struct dbent *dbptr;
86	TAILQ_ENTRY(circleq_entry) links;
87};
88
89/*
90 * Initialize the circular queue.
91 */
92void yp_init_dbs()
93{
94	TAILQ_INIT(&qhead);
95	return;
96}
97
98/*
99 * Dynamically allocate an entry for the circular queue.
100 * Return a NULL pointer on failure.
101 */
102static 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 */
127static 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 */
165static void yp_flush()
166{
167	register struct circleq_entry *qptr;
168
169	qptr = TAILQ_LAST(&qhead, circlehead);
170	TAILQ_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 */
180void yp_flush_all()
181{
182	register struct circleq_entry *qptr;
183
184	while(!TAILQ_EMPTY(&qhead)) {
185		qptr = TAILQ_FIRST(&qhead); /* save this */
186		TAILQ_REMOVE(&qhead, qptr, links);
187		yp_free_qent(qptr);
188	}
189	numdbs = 0;
190
191	return;
192}
193
194static char *inter_string = "YP_INTERDOMAIN";
195static char *secure_string = "YP_SECURE";
196static int inter_sz = sizeof("YP_INTERDOMAIN") - 1;
197static int secure_sz = sizeof("YP_SECURE") - 1;
198
199static 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
220int 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	TAILQ_FOREACH(qptr, &qhead, links) {
236		if (!strcmp(qptr->dbptr->name, buf)) {
237			if (qptr->dbptr->flags & flag)
238				return(1);
239			else
240				return(0);
241		}
242	}
243
244	if (yp_open_db_cache(domain, map, NULL, 0) == NULL)
245		return(0);
246
247	if (TAILQ_FIRST(&qhead)->dbptr->flags & flag)
248		return(1);
249
250	return(0);
251}
252
253/*
254 * Add a DB handle and database name to the cache. We only maintain
255 * fixed number of entries in the cache, so if we're asked to store
256 * a new entry when all our slots are already filled, we have to kick
257 * out the entry in the last slot to make room.
258 */
259static int yp_cache_db(dbp, name, size)
260	DB *dbp;
261	char *name;
262	int size;
263{
264	register struct circleq_entry *qptr;
265
266	if (numdbs == MAXDBS) {
267		if (ypdb_debug)
268			yp_error("queue overflow -- releasing last slot");
269		yp_flush();
270	}
271
272	/*
273	 * Allocate a new queue entry.
274	 */
275
276	if ((qptr = yp_malloc_qent()) == NULL) {
277		yp_error("failed to allocate a new cache entry");
278		return(1);
279	}
280
281	qptr->dbptr->dbp = dbp;
282	qptr->dbptr->name = strdup(name);
283	qptr->dbptr->size = size;
284	qptr->dbptr->key = NULL;
285
286	qptr->dbptr->flags = yp_setflags(dbp);
287
288	TAILQ_INSERT_HEAD(&qhead, qptr, links);
289	numdbs++;
290
291	return(0);
292}
293
294/*
295 * Search the list for a database matching 'name.' If we find it,
296 * move it to the head of the list and return its DB handle. If
297 * not, just fail: yp_open_db_cache() will subsequently try to open
298 * the database itself and call yp_cache_db() to add it to the
299 * list.
300 *
301 * The search works like this:
302 *
303 * - The caller specifies the name of a database to locate. We try to
304 *   find an entry in our queue with a matching name.
305 *
306 * - If the caller doesn't specify a key or size, we assume that the
307 *   first entry that we encounter with a matching name is returned.
308 *   This will result in matches regardless of the key/size values
309 *   stored in the queue entry.
310 *
311 * - If the caller also specifies a key and length, we check to see
312 *   if the key and length saved in the queue entry also matches.
313 *   This lets us return a DB handle that's already positioned at the
314 *   correct location within a database.
315 *
316 * - Once we have a match, it gets migrated to the top of the queue
317 *   so that it will be easier to find if another request for
318 *   the same database comes in later.
319 */
320static DB *yp_find_db(name, key, size)
321	char *name;
322	char *key;
323	int size;
324{
325	register struct circleq_entry *qptr;
326
327	TAILQ_FOREACH(qptr, &qhead, links) {
328		if (!strcmp(qptr->dbptr->name, name)) {
329			if (size) {
330				if (size != qptr->dbptr->size ||
331				   strncmp(qptr->dbptr->key, key, size))
332					continue;
333			} else {
334				if (qptr->dbptr->size)
335					continue;
336			}
337			if (qptr != TAILQ_FIRST(&qhead)) {
338				TAILQ_REMOVE(&qhead, qptr, links);
339				TAILQ_INSERT_HEAD(&qhead, qptr, links);
340			}
341			return(qptr->dbptr->dbp);
342		}
343	}
344
345	return(NULL);
346}
347
348/*
349 * Open a DB database and cache the handle for later use. We first
350 * check the cache to see if the required database is already open.
351 * If so, we fetch the handle from the cache. If not, we try to open
352 * the database and save the handle in the cache for later use.
353 */
354DB *yp_open_db_cache(domain, map, key, size)
355	const char *domain;
356	const char *map;
357	const char *key;
358	const int size;
359{
360	DB *dbp = NULL;
361	char buf[MAXPATHLEN + 2];
362/*
363	snprintf(buf, sizeof(buf), "%s/%s", domain, map);
364*/
365	yp_errno = YP_TRUE;
366
367	strcpy(buf, domain);
368	strcat(buf, "/");
369	strcat(buf, map);
370
371	if ((dbp = yp_find_db((char *)&buf, key, size)) != NULL) {
372		return(dbp);
373	} else {
374		if ((dbp = yp_open_db(domain, map)) != NULL) {
375			if (yp_cache_db(dbp, (char *)&buf, size)) {
376				(void)(dbp->close)(dbp);
377				yp_errno = YP_YPERR;
378				return(NULL);
379			}
380		}
381	}
382
383	return (dbp);
384}
385#endif
386
387/*
388 * Open a DB database.
389 */
390DB *yp_open_db(domain, map)
391	const char *domain;
392	const char *map;
393{
394	DB *dbp = NULL;
395	char buf[MAXPATHLEN + 2];
396
397	yp_errno = YP_TRUE;
398
399	if (map[0] == '.' || strchr(map, '/')) {
400		yp_errno = YP_BADARGS;
401		return (NULL);
402	}
403
404#ifdef DB_CACHE
405	if (yp_validdomain(domain)) {
406		yp_errno = YP_NODOM;
407		return(NULL);
408	}
409#endif
410	snprintf(buf, sizeof(buf), "%s/%s/%s", yp_dir, domain, map);
411
412#ifdef DB_CACHE
413again:
414#endif
415	dbp = dbopen(buf,O_RDONLY, PERM_SECURE, DB_HASH, NULL);
416
417	if (dbp == NULL) {
418		switch(errno) {
419#ifdef DB_CACHE
420		case ENFILE:
421			/*
422			 * We ran out of file descriptors. Nuke an
423			 * open one and try again.
424			 */
425			yp_error("ran out of file descriptors");
426			yp_flush();
427			goto again;
428			break;
429#endif
430		case ENOENT:
431			yp_errno = YP_NOMAP;
432			break;
433		case EFTYPE:
434			yp_errno = YP_BADDB;
435			break;
436		default:
437			yp_errno = YP_YPERR;
438			break;
439		}
440	}
441
442	return (dbp);
443}
444
445/*
446 * Database access routines.
447 *
448 * - yp_get_record(): retrieve an arbitrary key/data pair given one key
449 *                 to match against.
450 *
451 * - yp_first_record(): retrieve first key/data base in a database.
452 *
453 * - yp_next_record(): retrieve key/data pair that sequentially follows
454 *                   the supplied key value in the database.
455 */
456
457#ifdef DB_CACHE
458int yp_get_record(dbp,key,data,allow)
459	DB *dbp;
460#else
461int yp_get_record(domain,map,key,data,allow)
462	const char *domain;
463	const char *map;
464#endif
465	const DBT *key;
466	DBT *data;
467	int allow;
468{
469#ifndef DB_CACHE
470	DB *dbp;
471#endif
472	int rval = 0;
473#ifndef DB_CACHE
474	static unsigned char buf[YPMAXRECORD];
475#endif
476
477	if (ypdb_debug)
478		yp_error("looking up key [%.*s]",
479			  key->size, key->data);
480
481	/*
482	 * Avoid passing back magic "YP_*" entries unless
483	 * the caller specifically requested them by setting
484	 * the 'allow' flag.
485	 */
486	if (!allow && !strncmp(key->data, "YP_", 3))
487		return(YP_NOKEY);
488
489#ifndef DB_CACHE
490	if ((dbp = yp_open_db(domain, map)) == NULL) {
491		return(yp_errno);
492	}
493#endif
494
495	if ((rval = (dbp->get)(dbp, key, data, 0)) != 0) {
496#ifdef DB_CACHE
497		TAILQ_FIRST(&qhead)->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 (TAILQ_FIRST(&qhead)->dbptr->size) {
513		TAILQ_FIRST(&qhead)->dbptr->key = "";
514		TAILQ_FIRST(&qhead)->dbptr->size = 0;
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
525int 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		TAILQ_FIRST(&qhead)->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			TAILQ_FIRST(&qhead)->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 (TAILQ_FIRST(&qhead)->dbptr->size) {
568		TAILQ_FIRST(&qhead)->dbptr->key = key->data;
569		TAILQ_FIRST(&qhead)->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
579int 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->size || key->data == NULL) {
595		rval = yp_first_record(dbp,key,data,allow);
596		if (rval == YP_NOKEY)
597			return(YP_NOMORE);
598		else {
599#ifdef DB_CACHE
600			TAILQ_FIRST(&qhead)->dbptr->key = key->data;
601			TAILQ_FIRST(&qhead)->dbptr->size = key->size;
602#endif
603			return(rval);
604		}
605	}
606
607	if (ypdb_debug)
608		yp_error("retrieving next key, previous was: [%.*s]",
609			  key->size, key->data);
610
611	if (!all) {
612#ifdef DB_CACHE
613		if (TAILQ_FIRST(&qhead)->dbptr->key == NULL) {
614#endif
615			(dbp->seq)(dbp,&lkey,&ldata,R_FIRST);
616			while (key->size != lkey.size ||
617			    strncmp((char *)key->data, lkey.data,
618			    (int)key->size))
619				if ((dbp->seq)(dbp,&lkey,&ldata,R_NEXT)) {
620#ifdef DB_CACHE
621					TAILQ_FIRST(&qhead)->dbptr->size = 0;
622#endif
623					return(YP_NOKEY);
624				}
625
626#ifdef DB_CACHE
627		}
628#endif
629	}
630
631	if ((dbp->seq)(dbp,key,data,R_NEXT)) {
632#ifdef DB_CACHE
633		TAILQ_FIRST(&qhead)->dbptr->size = 0;
634#endif
635		return(YP_NOMORE);
636	}
637
638	/* Avoid passing back magic "YP_*" records. */
639	while (!strncmp(key->data, "YP_", 3) && !allow)
640		if ((dbp->seq)(dbp,key,data,R_NEXT)) {
641#ifdef DB_CACHE
642		TAILQ_FIRST(&qhead)->dbptr->size = 0;
643#endif
644			return(YP_NOMORE);
645		}
646
647	if (ypdb_debug)
648		yp_error("result of lookup: key: [%.*s] data: [%.*s]",
649			 key->size, key->data, data->size, data->data);
650
651#ifdef DB_CACHE
652	if (TAILQ_FIRST(&qhead)->dbptr->size) {
653		TAILQ_FIRST(&qhead)->dbptr->key = key->data;
654		TAILQ_FIRST(&qhead)->dbptr->size = key->size;
655	}
656#else
657	bcopy((char *)key->data, (char *)&keybuf, key->size);
658	lkey.data = (void *)&keybuf;
659	lkey.size = key->size;
660	bcopy((char *)data->data, (char *)&datbuf, data->size);
661	data->data = (void *)&datbuf;
662#endif
663
664	return(YP_TRUE);
665}
666
667#ifdef DB_CACHE
668/*
669 * Database glue functions.
670 */
671
672static DB *yp_currmap_db = NULL;
673static int yp_allow_db = 0;
674
675ypstat yp_select_map(map, domain, key, allow)
676	char *map;
677	char *domain;
678	keydat *key;
679	int allow;
680{
681	if (key == NULL)
682		yp_currmap_db = yp_open_db_cache(domain, map, NULL, 0);
683	else
684		yp_currmap_db = yp_open_db_cache(domain, map,
685						 key->keydat_val,
686						 key->keydat_len);
687
688	yp_allow_db = allow;
689	return(yp_errno);
690}
691
692ypstat yp_getbykey(key, val)
693	keydat *key;
694	valdat *val;
695{
696	DBT db_key = { NULL, 0 }, db_val = { NULL, 0 };
697	ypstat rval;
698
699	db_key.data = key->keydat_val;
700	db_key.size = key->keydat_len;
701
702	rval = yp_get_record(yp_currmap_db,
703				&db_key, &db_val, yp_allow_db);
704
705	if (rval == YP_TRUE) {
706		val->valdat_val = db_val.data;
707		val->valdat_len = db_val.size;
708	}
709
710	return(rval);
711}
712
713ypstat yp_firstbykey(key, val)
714	keydat *key;
715	valdat *val;
716{
717	DBT db_key = { NULL, 0 }, db_val = { NULL, 0 };
718	ypstat rval;
719
720	rval = yp_first_record(yp_currmap_db, &db_key, &db_val, yp_allow_db);
721
722	if (rval == YP_TRUE) {
723		key->keydat_val = db_key.data;
724		key->keydat_len = db_key.size;
725		val->valdat_val = db_val.data;
726		val->valdat_len = db_val.size;
727	}
728
729	return(rval);
730}
731
732ypstat yp_nextbykey(key, val)
733	keydat *key;
734	valdat *val;
735{
736	DBT db_key = { NULL, 0 }, db_val = { NULL, 0 };
737	ypstat rval;
738
739	db_key.data = key->keydat_val;
740	db_key.size = key->keydat_len;
741
742	rval = yp_next_record(yp_currmap_db, &db_key, &db_val, 0, yp_allow_db);
743
744	if (rval == YP_TRUE) {
745		key->keydat_val = db_key.data;
746		key->keydat_len = db_key.size;
747		val->valdat_val = db_val.data;
748		val->valdat_len = db_val.size;
749	}
750
751	return(rval);
752}
753#endif
754