xref: /freebsd/usr.sbin/ypserv/yp_dblookup.c (revision e627b39baccd1ec9129690167cf5e6d860509655)
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 $
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 $";
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 };
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: %s",
109 							strerror(errno));
110 		return(NULL);
111 	}
112 	bzero((char *)q, sizeof(struct circleq_entry));
113 	q->dbptr = (struct dbent *)malloc(sizeof(struct dbent));
114 	if (q->dbptr == NULL) {
115 		yp_error("failed to malloc() circleq entry: %s",
116 							strerror(errno));
117 		free(q);
118 		return(NULL);
119 	}
120 	bzero((char *)q->dbptr, sizeof(struct dbent));
121 
122 	return(q);
123 }
124 
125 /*
126  * Free a previously allocated circular queue
127  * entry.
128  */
129 static void yp_free_qent(q)
130 	struct circleq_entry *q;
131 {
132 	/*
133 	 * First, close the database. In theory, this is also
134 	 * supposed to free the resources allocated by the DB
135 	 * package, including the memory pointed to by q->dbptr->key.
136 	 * This means we don't have to free q->dbptr->key here.
137 	 */
138 	if (q->dbptr->dbp) {
139 		(void)(q->dbptr->dbp->close)(q->dbptr->dbp);
140 		q->dbptr->dbp = NULL;
141 	}
142 	/*
143 	 * Then free the database name, which was strdup()'ed.
144 	 */
145 	free(q->dbptr->name);
146 
147 	/*
148 	 * Free the rest of the dbent struct.
149 	 */
150 	free(q->dbptr);
151 	q->dbptr = NULL;
152 
153 	/*
154 	 * Free the circleq struct.
155 	 */
156 	free(q);
157 	q = NULL;
158 
159 	return;
160 }
161 
162 /*
163  * Zorch a single entry in the dbent queue and release
164  * all its resources. (This always removes the last entry
165  * in the queue.)
166  */
167 static void yp_flush()
168 {
169 	register struct circleq_entry *qptr;
170 
171 	qptr = qhead.cqh_last;
172 	CIRCLEQ_REMOVE(&qhead, qptr, links);
173 	yp_free_qent(qptr);
174 	numdbs--;
175 
176 	return;
177 }
178 
179 /*
180  * Close all databases, erase all database names and empty the queue.
181  */
182 void yp_flush_all()
183 {
184 	register struct circleq_entry *qptr;
185 
186 	while(qhead.cqh_first != (void *)&qhead) {
187 		qptr = qhead.cqh_first; /* save this */
188 		CIRCLEQ_REMOVE(&qhead, qhead.cqh_first, links);
189 		yp_free_qent(qptr);
190 	}
191 	numdbs = 0;
192 
193 	return;
194 }
195 
196 
197 /*
198  * Add a DB handle and database name to the cache. We only maintain
199  * fixed number of entries in the cache, so if we're asked to store
200  * a new entry when all our slots are already filled, we have to kick
201  * out the entry in the last slot to make room.
202  */
203 static int yp_cache_db(dbp, name, size)
204 	DB *dbp;
205 	char *name;
206 	int size;
207 {
208 	register struct circleq_entry *qptr;
209 
210 	if (numdbs == MAXDBS) {
211 		if (ypdb_debug)
212 			yp_error("queue overflow -- releasing last slot");
213 		yp_flush();
214 	}
215 
216 	/*
217 	 * Allocate a new queue entry.
218 	 */
219 
220 	if ((qptr = yp_malloc_qent()) == NULL) {
221 		yp_error("failed to allocate a new cache entry");
222 		return(1);
223 	}
224 
225 	qptr->dbptr->dbp = dbp;
226 	qptr->dbptr->name = strdup(name);
227 	qptr->dbptr->size = size;
228 	qptr->dbptr->key = NULL;
229 
230 	CIRCLEQ_INSERT_HEAD(&qhead, qptr, links);
231 	numdbs++;
232 
233 	return(0);
234 }
235 
236 /*
237  * Search the list for a database matching 'name.' If we find it,
238  * move it to the head of the list and return its DB handle. If
239  * not, just fail: yp_open_db_cache() will subsequently try to open
240  * the database itself and call yp_cache_db() to add it to the
241  * list.
242  *
243  * The search works like this:
244  *
245  * - The caller specifies the name of a database to locate. We try to
246  *   find an entry in our queue with a matching name.
247  *
248  * - If the caller doesn't specify a key or size, we assume that the
249  *   first entry that we encounter with a matching name is returned.
250  *   This will result in matches regardless of the key/size values
251  *   stored in the queue entry.
252  *
253  * - If the caller also specifies a key and length, we check to see
254  *   if the key and length saved in the queue entry also matches.
255  *   This lets us return a DB handle that's already positioned at the
256  *   correct location within a database.
257  *
258  * - Once we have a match, it gets migrated to the top of the queue
259  *   so that it will be easier to find if another request for
260  *   the same database comes in later.
261  */
262 static DB *yp_find_db(name, key, size)
263 	char *name;
264 	char *key;
265 	int size;
266 {
267 	register struct circleq_entry *qptr;
268 
269 	for (qptr = qhead.cqh_first; qptr != (void *)&qhead;
270 						qptr = qptr->links.cqe_next) {
271 		if (!strcmp(qptr->dbptr->name, name)) {
272 			if (size) {
273 				if (size != qptr->dbptr->size ||
274 				   strncmp(qptr->dbptr->key, key, size))
275 					continue;
276 			} else {
277 				if (qptr->dbptr->size)
278 					continue;
279 			}
280 			if (qptr != qhead.cqh_first) {
281 				CIRCLEQ_REMOVE(&qhead, qptr, links);
282 				CIRCLEQ_INSERT_HEAD(&qhead, qptr, links);
283 			}
284 			return(qptr->dbptr->dbp);
285 		}
286 	}
287 
288 	return(NULL);
289 }
290 
291 /*
292  * Open a DB database and cache the handle for later use. We first
293  * check the cache to see if the required database is already open.
294  * If so, we fetch the handle from the cache. If not, we try to open
295  * the database and save the handle in the cache for later use.
296  */
297 DB *yp_open_db_cache(domain, map, key, size)
298 	const char *domain;
299 	const char *map;
300 	const char *key;
301 	const int size;
302 {
303 	DB *dbp = NULL;
304 	char buf[MAXPATHLEN + 2];
305 /*
306 	snprintf(buf, sizeof(buf), "%s/%s", domain, map);
307 */
308 	yp_errno = YP_TRUE;
309 
310 	strcpy(buf, domain);
311 	strcat(buf, "/");
312 	strcat(buf, map);
313 
314 	if ((dbp = yp_find_db((char *)&buf, key, size)) != NULL) {
315 		return(dbp);
316 	} else {
317 		if ((dbp = yp_open_db(domain, map)) != NULL) {
318 			if (yp_cache_db(dbp, (char *)&buf, size)) {
319 				(void)(dbp->close)(dbp);
320 				yp_errno = YP_YPERR;
321 				return(NULL);
322 			}
323 		}
324 	}
325 
326 	return (dbp);
327 }
328 #endif
329 
330 /*
331  * Open a DB database.
332  */
333 DB *yp_open_db(domain, map)
334 	const char *domain;
335 	const char *map;
336 {
337 	DB *dbp = NULL;
338 	char buf[MAXPATHLEN + 2];
339 
340 	yp_errno = YP_TRUE;
341 
342 	if (map[0] == '.' || strchr(map, '/')) {
343 		yp_errno = YP_BADARGS;
344 		return (NULL);
345 	}
346 
347 #ifdef DB_CACHE
348 	if (yp_validdomain(domain)) {
349 		yp_errno = YP_NODOM;
350 		return(NULL);
351 	}
352 #endif
353 	snprintf(buf, sizeof(buf), "%s/%s/%s", yp_dir, domain, map);
354 
355 #ifdef DB_CACHE
356 again:
357 #endif
358 	dbp = dbopen(buf,O_RDONLY, PERM_SECURE, DB_HASH, NULL);
359 
360 	if (dbp == NULL) {
361 		switch(errno) {
362 #ifdef DB_CACHE
363 		case ENFILE:
364 			/*
365 			 * We ran out of file descriptors. Nuke an
366 			 * open one and try again.
367 			 */
368 			yp_error("ran out of file descriptors");
369 			yp_flush();
370 			goto again;
371 			break;
372 #endif
373 		case ENOENT:
374 			yp_errno = YP_NOMAP;
375 			break;
376 		case EFTYPE:
377 			yp_errno = YP_BADDB;
378 			break;
379 		default:
380 			yp_errno = YP_YPERR;
381 			break;
382 		}
383 	}
384 
385 	return (dbp);
386 }
387 
388 /*
389  * Database access routines.
390  *
391  * - yp_get_record(): retrieve an arbitrary key/data pair given one key
392  *                 to match against.
393  *
394  * - yp_first_record(): retrieve first key/data base in a database.
395  *
396  * - yp_next_record(): retrieve key/data pair that sequentially follows
397  *                   the supplied key value in the database.
398  */
399 
400 int yp_get_record(domain,map,key,data,allow)
401 	const char *domain;
402 	const char *map;
403 	const DBT *key;
404 	DBT *data;
405 	int allow;
406 {
407 	DB *dbp;
408 	int rval = 0;
409 #ifndef DB_CACHE
410 	static unsigned char buf[YPMAXRECORD];
411 #endif
412 
413 	if (ypdb_debug)
414 		yp_error("Looking up key [%.*s] in map [%s]",
415 			  key->size, key->data, map);
416 
417 	/*
418 	 * Avoid passing back magic "YP_*" entries unless
419 	 * the caller specifically requested them by setting
420 	 * the 'allow' flag.
421 	 */
422 	if (!allow && !strncmp(key->data, "YP_", 3))
423 		return(YP_NOKEY);
424 
425 #ifdef DB_CACHE
426 	if ((dbp = yp_open_db_cache(domain, map, NULL, 0)) == NULL) {
427 #else
428 	if ((dbp = yp_open_db(domain, map)) == NULL) {
429 #endif
430 		return(yp_errno);
431 	}
432 
433 	if ((rval = (dbp->get)(dbp, key, data, 0)) != 0) {
434 #ifdef DB_CACHE
435 		qhead.cqh_first->dbptr->size = 0;
436 #else
437 		(void)(dbp->close)(dbp);
438 #endif
439 		if (rval == 1)
440 			return(YP_NOKEY);
441 		else
442 			return(YP_BADDB);
443 	}
444 
445 	if (ypdb_debug)
446 		yp_error("Result of lookup: key: [%.*s] data: [%.*s]",
447 			 key->size, key->data, data->size, data->data);
448 
449 #ifdef DB_CACHE
450 	if (qhead.cqh_first->dbptr->size) {
451 		qhead.cqh_first->dbptr->key = key->data;
452 		qhead.cqh_first->dbptr->size = key->size;
453 	}
454 #else
455 	bcopy((char *)data->data, (char *)&buf, data->size);
456 	data->data = (void *)&buf;
457 	(void)(dbp->close)(dbp);
458 #endif
459 
460 	return(YP_TRUE);
461 }
462 
463 int yp_first_record(dbp,key,data,allow)
464 	const DB *dbp;
465 	DBT *key;
466 	DBT *data;
467 	int allow;
468 {
469 	int rval;
470 #ifndef DB_CACHE
471 	static unsigned char buf[YPMAXRECORD];
472 #endif
473 
474 	if (ypdb_debug)
475 		yp_error("Retrieving first key in map.");
476 
477 	if ((rval = (dbp->seq)(dbp,key,data,R_FIRST)) != 0) {
478 #ifdef DB_CACHE
479 		qhead.cqh_first->dbptr->size = 0;
480 #endif
481 		if (rval == 1)
482 			return(YP_NOKEY);
483 		else
484 			return(YP_BADDB);
485 	}
486 
487 	/* Avoid passing back magic "YP_*" records. */
488 	while (!strncmp(key->data, "YP_", 3) && !allow) {
489 		if ((rval = (dbp->seq)(dbp,key,data,R_NEXT)) != 0) {
490 #ifdef DB_CACHE
491 			qhead.cqh_first->dbptr->size = 0;
492 #endif
493 			if (rval == 1)
494 				return(YP_NOKEY);
495 			else
496 				return(YP_BADDB);
497 		}
498 	}
499 
500 	if (ypdb_debug)
501 		yp_error("Result of lookup: key: [%.*s] data: [%.*s]",
502 			 key->size, key->data, data->size, data->data);
503 
504 #ifdef DB_CACHE
505 	if (qhead.cqh_first->dbptr->size) {
506 		qhead.cqh_first->dbptr->key = key->data;
507 		qhead.cqh_first->dbptr->size = key->size;
508 	}
509 #else
510 	bcopy((char *)data->data, (char *)&buf, data->size);
511 	data->data = (void *)&buf;
512 #endif
513 
514 	return(YP_TRUE);
515 }
516 
517 int yp_next_record(dbp,key,data,all,allow)
518 	const DB *dbp;
519 	DBT *key;
520 	DBT *data;
521 	int all;
522 	int allow;
523 {
524 	static DBT lkey = { NULL, 0 };
525 	static DBT ldata = { NULL, 0 };
526 	int rval;
527 #ifndef DB_CACHE
528 	static unsigned char keybuf[YPMAXRECORD];
529 	static unsigned char datbuf[YPMAXRECORD];
530 #endif
531 
532 	if (key == NULL || key->data == NULL) {
533 		rval = yp_first_record(dbp,key,data,allow);
534 		if (rval == YP_NOKEY)
535 			return(YP_NOMORE);
536 		else
537 			return(rval);
538 	}
539 
540 	if (ypdb_debug)
541 		yp_error("Retreiving next key, previous was: [%.*s]",
542 			  key->size, key->data);
543 
544 	if (!all) {
545 #ifdef DB_CACHE
546 		if (qhead.cqh_first->dbptr->key == NULL) {
547 #endif
548 			(dbp->seq)(dbp,&lkey,&ldata,R_FIRST);
549 			while(strncmp((char *)key->data,lkey.data,
550 				(int)key->size) || key->size != lkey.size)
551 				if ((dbp->seq)(dbp,&lkey,&ldata,R_NEXT)) {
552 #ifdef DB_CACHE
553 					qhead.cqh_first->dbptr->size = 0;
554 #endif
555 					return(YP_NOKEY);
556 				}
557 
558 #ifdef DB_CACHE
559 		}
560 #endif
561 	}
562 
563 	if ((dbp->seq)(dbp,key,data,R_NEXT)) {
564 #ifdef DB_CACHE
565 		qhead.cqh_first->dbptr->size = 0;
566 #endif
567 		return(YP_NOMORE);
568 	}
569 
570 	/* Avoid passing back magic "YP_*" records. */
571 	while (!strncmp(key->data, "YP_", 3) && !allow)
572 		if ((dbp->seq)(dbp,key,data,R_NEXT)) {
573 #ifdef DB_CACHE
574 		qhead.cqh_first->dbptr->size = 0;
575 #endif
576 			return(YP_NOMORE);
577 		}
578 
579 	if (ypdb_debug)
580 		yp_error("Result of lookup: key: [%.*s] data: [%.*s]",
581 			 key->size, key->data, data->size, data->data);
582 
583 #ifdef DB_CACHE
584 	if (qhead.cqh_first->dbptr->size) {
585 		qhead.cqh_first->dbptr->key = key->data;
586 		qhead.cqh_first->dbptr->size = key->size;
587 	}
588 #else
589 	bcopy((char *)key->data, (char *)&keybuf, key->size);
590 	lkey.data = (void *)&keybuf;
591 	lkey.size = key->size;
592 	bcopy((char *)data->data, (char *)&datbuf, data->size);
593 	data->data = (void *)&datbuf;
594 #endif
595 
596 	return(YP_TRUE);
597 }
598