yp_dblookup.c revision 45671
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	"$Id: yp_dblookup.c,v 1.15 1998/02/11 19:15:32 wpaul Exp $";
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 CIRCLEQ_HEAD(circlehead, circleq_entry) qhead;
83
84struct circleq_entry {
85	struct dbent *dbptr;
86	CIRCLEQ_ENTRY(circleq_entry) links;
87};
88
89/*
90 * Initialize the circular queue.
91 */
92void 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 */
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 = 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 */
180void 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
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	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 */
260static 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 */
321static 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 */
356DB *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 */
392DB *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
415again:
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
460int yp_get_record(dbp,key,data,allow)
461	DB *dbp;
462#else
463int 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
527int 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
581int 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
674static DB *yp_currmap_db = NULL;
675static int yp_allow_db = 0;
676
677ypstat 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
694ypstat 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
715ypstat 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
734ypstat 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