yp_dblookup.c revision 22997
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$
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
50static const char rcsid[] = "$Id$";
51#endif
52
53int ypdb_debug = 0;
54enum ypstat yp_errno = YP_TRUE;
55
56#define PERM_SECURE (S_IRUSR|S_IWUSR)
57HASHINFO 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
73static int numdbs = 0;
74
75struct dbent {
76	DB *dbp;
77	char *name;
78	char *key;
79	int size;
80	int flags;
81};
82
83static CIRCLEQ_HEAD(circlehead, circleq_entry) qhead;
84
85struct circleq_entry {
86	struct dbent *dbptr;
87	CIRCLEQ_ENTRY(circleq_entry) links;
88};
89
90/*
91 * Initialize the circular queue.
92 */
93void 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 */
103static 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 */
130static 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 */
168static 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 */
183void 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
197static char *inter_string = "YP_INTERDOMAIN";
198static char *secure_string = "YP_SECURE";
199static int inter_sz = sizeof("YP_INTERDOMAIN") - 1;
200static int secure_sz = sizeof("YP_SECURE") - 1;
201
202static 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
223int 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 */
263static 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 */
324static 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 */
359DB *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 */
395DB *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
418again:
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#ifdef DB_CACHE
463int yp_get_record(dbp,key,data,allow)
464	DB *dbp;
465#else
466int yp_get_record(domain,map,key,data,allow)
467	const char *domain;
468	const char *map;
469#endif
470	const DBT *key;
471	DBT *data;
472	int allow;
473{
474#ifndef DB_CACHE
475	DB *dbp;
476#endif
477	int rval = 0;
478#ifndef DB_CACHE
479	static unsigned char buf[YPMAXRECORD];
480#endif
481
482	if (ypdb_debug)
483		yp_error("Looking up key [%.*s]",
484			  key->size, key->data);
485
486	/*
487	 * Avoid passing back magic "YP_*" entries unless
488	 * the caller specifically requested them by setting
489	 * the 'allow' flag.
490	 */
491	if (!allow && !strncmp(key->data, "YP_", 3))
492		return(YP_NOKEY);
493
494#ifndef DB_CACHE
495	if ((dbp = yp_open_db(domain, map)) == NULL) {
496		return(yp_errno);
497	}
498#endif
499
500	if ((rval = (dbp->get)(dbp, key, data, 0)) != 0) {
501#ifdef DB_CACHE
502		qhead.cqh_first->dbptr->size = 0;
503#else
504		(void)(dbp->close)(dbp);
505#endif
506		if (rval == 1)
507			return(YP_NOKEY);
508		else
509			return(YP_BADDB);
510	}
511
512	if (ypdb_debug)
513		yp_error("Result of lookup: key: [%.*s] data: [%.*s]",
514			 key->size, key->data, data->size, data->data);
515
516#ifdef DB_CACHE
517	if (qhead.cqh_first->dbptr->size) {
518		qhead.cqh_first->dbptr->key = key->data;
519		qhead.cqh_first->dbptr->size = key->size;
520	}
521#else
522	bcopy((char *)data->data, (char *)&buf, data->size);
523	data->data = (void *)&buf;
524	(void)(dbp->close)(dbp);
525#endif
526
527	return(YP_TRUE);
528}
529
530int yp_first_record(dbp,key,data,allow)
531	const DB *dbp;
532	DBT *key;
533	DBT *data;
534	int allow;
535{
536	int rval;
537#ifndef DB_CACHE
538	static unsigned char buf[YPMAXRECORD];
539#endif
540
541	if (ypdb_debug)
542		yp_error("Retrieving first key in map.");
543
544	if ((rval = (dbp->seq)(dbp,key,data,R_FIRST)) != 0) {
545#ifdef DB_CACHE
546		qhead.cqh_first->dbptr->size = 0;
547#endif
548		if (rval == 1)
549			return(YP_NOKEY);
550		else
551			return(YP_BADDB);
552	}
553
554	/* Avoid passing back magic "YP_*" records. */
555	while (!strncmp(key->data, "YP_", 3) && !allow) {
556		if ((rval = (dbp->seq)(dbp,key,data,R_NEXT)) != 0) {
557#ifdef DB_CACHE
558			qhead.cqh_first->dbptr->size = 0;
559#endif
560			if (rval == 1)
561				return(YP_NOKEY);
562			else
563				return(YP_BADDB);
564		}
565	}
566
567	if (ypdb_debug)
568		yp_error("Result of lookup: key: [%.*s] data: [%.*s]",
569			 key->size, key->data, data->size, data->data);
570
571#ifdef DB_CACHE
572	if (qhead.cqh_first->dbptr->size) {
573		qhead.cqh_first->dbptr->key = key->data;
574		qhead.cqh_first->dbptr->size = key->size;
575	}
576#else
577	bcopy((char *)data->data, (char *)&buf, data->size);
578	data->data = (void *)&buf;
579#endif
580
581	return(YP_TRUE);
582}
583
584int yp_next_record(dbp,key,data,all,allow)
585	const DB *dbp;
586	DBT *key;
587	DBT *data;
588	int all;
589	int allow;
590{
591	static DBT lkey = { NULL, 0 };
592	static DBT ldata = { NULL, 0 };
593	int rval;
594#ifndef DB_CACHE
595	static unsigned char keybuf[YPMAXRECORD];
596	static unsigned char datbuf[YPMAXRECORD];
597#endif
598
599	if (key == NULL || !key->size || key->data == NULL) {
600		rval = yp_first_record(dbp,key,data,allow);
601		if (rval == YP_NOKEY)
602			return(YP_NOMORE);
603		else {
604#ifdef DB_CACHE
605			qhead.cqh_first->dbptr->key = key->data;
606			qhead.cqh_first->dbptr->size = key->size;
607#endif
608			return(rval);
609		}
610	}
611
612	if (ypdb_debug)
613		yp_error("Retreiving next key, previous was: [%.*s]",
614			  key->size, key->data);
615
616	if (!all) {
617#ifdef DB_CACHE
618		if (qhead.cqh_first->dbptr->key == NULL) {
619#endif
620			(dbp->seq)(dbp,&lkey,&ldata,R_FIRST);
621			while(strncmp((char *)key->data,lkey.data,
622				(int)key->size) || key->size != lkey.size)
623				if ((dbp->seq)(dbp,&lkey,&ldata,R_NEXT)) {
624#ifdef DB_CACHE
625					qhead.cqh_first->dbptr->size = 0;
626#endif
627					return(YP_NOKEY);
628				}
629
630#ifdef DB_CACHE
631		}
632#endif
633	}
634
635	if ((dbp->seq)(dbp,key,data,R_NEXT)) {
636#ifdef DB_CACHE
637		qhead.cqh_first->dbptr->size = 0;
638#endif
639		return(YP_NOMORE);
640	}
641
642	/* Avoid passing back magic "YP_*" records. */
643	while (!strncmp(key->data, "YP_", 3) && !allow)
644		if ((dbp->seq)(dbp,key,data,R_NEXT)) {
645#ifdef DB_CACHE
646		qhead.cqh_first->dbptr->size = 0;
647#endif
648			return(YP_NOMORE);
649		}
650
651	if (ypdb_debug)
652		yp_error("Result of lookup: key: [%.*s] data: [%.*s]",
653			 key->size, key->data, data->size, data->data);
654
655#ifdef DB_CACHE
656	if (qhead.cqh_first->dbptr->size) {
657		qhead.cqh_first->dbptr->key = key->data;
658		qhead.cqh_first->dbptr->size = key->size;
659	}
660#else
661	bcopy((char *)key->data, (char *)&keybuf, key->size);
662	lkey.data = (void *)&keybuf;
663	lkey.size = key->size;
664	bcopy((char *)data->data, (char *)&datbuf, data->size);
665	data->data = (void *)&datbuf;
666#endif
667
668	return(YP_TRUE);
669}
670
671#ifdef DB_CACHE
672/*
673 * Database glue functions.
674 */
675
676static DB *yp_currmap_db = NULL;
677static int yp_allow_db = 0;
678
679ypstat yp_select_map(map, domain, key, allow)
680	char *map;
681	char *domain;
682	keydat *key;
683	int allow;
684{
685	yp_currmap_db = yp_open_db_cache(domain, map, 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