Initial import from FreeBSD RELENG_4:
[games.git] / usr.sbin / ypserv / yp_dblookup.c
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
34 static const char rcsid[] =
35   "$FreeBSD: src/usr.sbin/ypserv/yp_dblookup.c,v 1.17.2.1 2002/02/15 00:47:00 des 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
52 int ypdb_debug = 0;
53 enum ypstat yp_errno = YP_TRUE;
54
55 #define PERM_SECURE (S_IRUSR|S_IWUSR)
56 HASHINFO 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
72 static int numdbs = 0;
73
74 struct dbent {
75         DB *dbp;
76         char *name;
77         char *key;
78         int size;
79         int flags;
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");
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  */
127 static 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  */
165 static 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  */
180 void 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
194 static char *inter_string = "YP_INTERDOMAIN";
195 static char *secure_string = "YP_SECURE";
196 static int inter_sz = sizeof("YP_INTERDOMAIN") - 1;
197 static int secure_sz = sizeof("YP_SECURE") - 1;
198
199 static 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
220 int 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  */
260 static 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  */
321 static 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  */
356 DB *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  */
392 DB *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
415 again:
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
460 int yp_get_record(dbp,key,data,allow)
461         DB *dbp;
462 #else
463 int 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
527 int 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
581 int 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
674 static DB *yp_currmap_db = NULL;
675 static int yp_allow_db = 0;
676
677 ypstat 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
694 ypstat 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
715 ypstat 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
734 ypstat 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