]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - usr.sbin/ypserv/yp_dblookup.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.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 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
35
36 #include <db.h>
37 #include <errno.h>
38 #include <fcntl.h>
39 #include <limits.h>
40 #include <paths.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <unistd.h>
45 #include <sys/stat.h>
46 #include <sys/param.h>
47 #include <rpcsvc/yp.h>
48 #include "yp_extern.h"
49
50 int ypdb_debug = 0;
51 enum ypstat yp_errno = YP_TRUE;
52
53 #define PERM_SECURE (S_IRUSR|S_IWUSR)
54 HASHINFO openinfo = {
55         4096,           /* bsize */
56         32,             /* ffactor */
57         256,            /* nelem */
58         2048 * 512,     /* cachesize */
59         NULL,           /* hash */
60         0,              /* lorder */
61 };
62
63 #ifdef DB_CACHE
64 #include <sys/queue.h>
65
66 #ifndef MAXDBS
67 #define MAXDBS 20
68 #endif
69
70 static int numdbs = 0;
71
72 struct dbent {
73         DB *dbp;
74         char *name;
75         char *key;
76         int size;
77         int flags;
78 };
79
80 static TAILQ_HEAD(circlehead, circleq_entry) qhead;
81
82 struct circleq_entry {
83         struct dbent *dbptr;
84         TAILQ_ENTRY(circleq_entry) links;
85 };
86
87 /*
88  * Initialize the circular queue.
89  */
90 void
91 yp_init_dbs(void)
92 {
93         TAILQ_INIT(&qhead);
94         return;
95 }
96
97 /*
98  * Dynamically allocate an entry for the circular queue.
99  * Return a NULL pointer on failure.
100  */
101 static struct circleq_entry *
102 yp_malloc_qent(void)
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
128 yp_free_qent(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
166 yp_flush(void)
167 {
168         register struct circleq_entry *qptr;
169
170         qptr = TAILQ_LAST(&qhead, circlehead);
171         TAILQ_REMOVE(&qhead, qptr, links);
172         yp_free_qent(qptr);
173         numdbs--;
174
175         return;
176 }
177
178 /*
179  * Close all databases, erase all database names and empty the queue.
180  */
181 void
182 yp_flush_all(void)
183 {
184         register struct circleq_entry *qptr;
185
186         while (!TAILQ_EMPTY(&qhead)) {
187                 qptr = TAILQ_FIRST(&qhead); /* save this */
188                 TAILQ_REMOVE(&qhead, qptr, links);
189                 yp_free_qent(qptr);
190         }
191         numdbs = 0;
192
193         return;
194 }
195
196 static char *inter_string = "YP_INTERDOMAIN";
197 static char *secure_string = "YP_SECURE";
198 static int inter_sz = sizeof("YP_INTERDOMAIN") - 1;
199 static int secure_sz = sizeof("YP_SECURE") - 1;
200
201 static int
202 yp_setflags(DB *dbp)
203 {
204         DBT key = { NULL, 0 }, data = { NULL, 0 };
205         int flags = 0;
206
207         key.data = inter_string;
208         key.size = inter_sz;
209
210         if (!(dbp->get)(dbp, &key, &data, 0))
211                 flags |= YP_INTERDOMAIN;
212
213         key.data = secure_string;
214         key.size = secure_sz;
215
216         if (!(dbp->get)(dbp, &key, &data, 0))
217                 flags |= YP_SECURE;
218
219         return(flags);
220 }
221
222 int
223 yp_testflag(char *map, char *domain, 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  */
259 static int
260 yp_cache_db(DB *dbp, char *name, int size)
261 {
262         register struct circleq_entry *qptr;
263
264         if (numdbs == MAXDBS) {
265                 if (ypdb_debug)
266                         yp_error("queue overflow -- releasing last slot");
267                 yp_flush();
268         }
269
270         /*
271          * Allocate a new queue entry.
272          */
273
274         if ((qptr = yp_malloc_qent()) == NULL) {
275                 yp_error("failed to allocate a new cache entry");
276                 return(1);
277         }
278
279         qptr->dbptr->dbp = dbp;
280         qptr->dbptr->name = strdup(name);
281         qptr->dbptr->size = size;
282         qptr->dbptr->key = NULL;
283
284         qptr->dbptr->flags = yp_setflags(dbp);
285
286         TAILQ_INSERT_HEAD(&qhead, qptr, links);
287         numdbs++;
288
289         return(0);
290 }
291
292 /*
293  * Search the list for a database matching 'name.' If we find it,
294  * move it to the head of the list and return its DB handle. If
295  * not, just fail: yp_open_db_cache() will subsequently try to open
296  * the database itself and call yp_cache_db() to add it to the
297  * list.
298  *
299  * The search works like this:
300  *
301  * - The caller specifies the name of a database to locate. We try to
302  *   find an entry in our queue with a matching name.
303  *
304  * - If the caller doesn't specify a key or size, we assume that the
305  *   first entry that we encounter with a matching name is returned.
306  *   This will result in matches regardless of the key/size values
307  *   stored in the queue entry.
308  *
309  * - If the caller also specifies a key and length, we check to see
310  *   if the key and length saved in the queue entry also matches.
311  *   This lets us return a DB handle that's already positioned at the
312  *   correct location within a database.
313  *
314  * - Once we have a match, it gets migrated to the top of the queue
315  *   so that it will be easier to find if another request for
316  *   the same database comes in later.
317  */
318 static DB *
319 yp_find_db(const char *name, const char *key, int size)
320 {
321         register struct circleq_entry *qptr;
322
323         TAILQ_FOREACH(qptr, &qhead, links) {
324                 if (!strcmp(qptr->dbptr->name, name)) {
325                         if (size) {
326                                 if (size != qptr->dbptr->size ||
327                                    strncmp(qptr->dbptr->key, key, size))
328                                         continue;
329                         } else {
330                                 if (qptr->dbptr->size)
331                                         continue;
332                         }
333                         if (qptr != TAILQ_FIRST(&qhead)) {
334                                 TAILQ_REMOVE(&qhead, qptr, links);
335                                 TAILQ_INSERT_HEAD(&qhead, qptr, links);
336                         }
337                         return(qptr->dbptr->dbp);
338                 }
339         }
340
341         return(NULL);
342 }
343
344 /*
345  * Open a DB database and cache the handle for later use. We first
346  * check the cache to see if the required database is already open.
347  * If so, we fetch the handle from the cache. If not, we try to open
348  * the database and save the handle in the cache for later use.
349  */
350 DB *
351 yp_open_db_cache(const char *domain, const char *map, const char *key,
352     const int size)
353 {
354         DB *dbp = NULL;
355         char buf[MAXPATHLEN + 2];
356 /*
357         snprintf(buf, sizeof(buf), "%s/%s", domain, map);
358 */
359         yp_errno = YP_TRUE;
360
361         strcpy(buf, domain);
362         strcat(buf, "/");
363         strcat(buf, map);
364
365         if ((dbp = yp_find_db(buf, key, size)) != NULL) {
366                 return(dbp);
367         } else {
368                 if ((dbp = yp_open_db(domain, map)) != NULL) {
369                         if (yp_cache_db(dbp, buf, size)) {
370                                 (void)(dbp->close)(dbp);
371                                 yp_errno = YP_YPERR;
372                                 return(NULL);
373                         }
374                 }
375         }
376
377         return (dbp);
378 }
379 #endif
380
381 /*
382  * Open a DB database.
383  */
384 DB *
385 yp_open_db(const char *domain, const char *map)
386 {
387         DB *dbp = NULL;
388         char buf[MAXPATHLEN + 2];
389
390         yp_errno = YP_TRUE;
391
392         if (map[0] == '.' || strchr(map, '/')) {
393                 yp_errno = YP_BADARGS;
394                 return (NULL);
395         }
396
397 #ifdef DB_CACHE
398         if (yp_validdomain(domain)) {
399                 yp_errno = YP_NODOM;
400                 return(NULL);
401         }
402 #endif
403         snprintf(buf, sizeof(buf), "%s/%s/%s", yp_dir, domain, map);
404
405 #ifdef DB_CACHE
406 again:
407 #endif
408         dbp = dbopen(buf,O_RDONLY, PERM_SECURE, DB_HASH, NULL);
409
410         if (dbp == NULL) {
411                 switch (errno) {
412 #ifdef DB_CACHE
413                 case ENFILE:
414                         /*
415                          * We ran out of file descriptors. Nuke an
416                          * open one and try again.
417                          */
418                         yp_error("ran out of file descriptors");
419                         yp_flush();
420                         goto again;
421                         break;
422 #endif
423                 case ENOENT:
424                         yp_errno = YP_NOMAP;
425                         break;
426                 case EFTYPE:
427                         yp_errno = YP_BADDB;
428                         break;
429                 default:
430                         yp_errno = YP_YPERR;
431                         break;
432                 }
433         }
434
435         return (dbp);
436 }
437
438 /*
439  * Database access routines.
440  *
441  * - yp_get_record(): retrieve an arbitrary key/data pair given one key
442  *                 to match against.
443  *
444  * - yp_first_record(): retrieve first key/data base in a database.
445  *
446  * - yp_next_record(): retrieve key/data pair that sequentially follows
447  *                   the supplied key value in the database.
448  */
449
450 #ifdef DB_CACHE
451 int
452 yp_get_record(DB *dbp, const DBT *key, DBT *data, int allow)
453 #else
454 int
455 yp_get_record(const char *domain, const char *map,
456     const DBT *key, DBT *data, int allow)
457 #endif
458 {
459 #ifndef DB_CACHE
460         DB *dbp;
461 #endif
462         int rval = 0;
463 #ifndef DB_CACHE
464         static unsigned char buf[YPMAXRECORD];
465 #endif
466
467         if (ypdb_debug)
468                 yp_error("looking up key [%.*s]",
469                     (int)key->size, (char *)key->data);
470
471         /*
472          * Avoid passing back magic "YP_*" entries unless
473          * the caller specifically requested them by setting
474          * the 'allow' flag.
475          */
476         if (!allow && !strncmp(key->data, "YP_", 3))
477                 return(YP_NOKEY);
478
479 #ifndef DB_CACHE
480         if ((dbp = yp_open_db(domain, map)) == NULL) {
481                 return(yp_errno);
482         }
483 #endif
484
485         if ((rval = (dbp->get)(dbp, key, data, 0)) != 0) {
486 #ifdef DB_CACHE
487                 TAILQ_FIRST(&qhead)->dbptr->size = 0;
488 #else
489                 (void)(dbp->close)(dbp);
490 #endif
491                 if (rval == 1)
492                         return(YP_NOKEY);
493                 else
494                         return(YP_BADDB);
495         }
496
497         if (ypdb_debug)
498                 yp_error("result of lookup: key: [%.*s] data: [%.*s]",
499                     (int)key->size, (char *)key->data,
500                     (int)data->size, (char *)data->data);
501
502 #ifdef DB_CACHE
503         if (TAILQ_FIRST(&qhead)->dbptr->size) {
504                 TAILQ_FIRST(&qhead)->dbptr->key = "";
505                 TAILQ_FIRST(&qhead)->dbptr->size = 0;
506         }
507 #else
508         bcopy(data->data, &buf, data->size);
509         data->data = &buf;
510         (void)(dbp->close)(dbp);
511 #endif
512
513         return(YP_TRUE);
514 }
515
516 int
517 yp_first_record(const DB *dbp, DBT *key, DBT *data, int allow)
518 {
519         int rval;
520 #ifndef DB_CACHE
521         static unsigned char buf[YPMAXRECORD];
522 #endif
523
524         if (ypdb_debug)
525                 yp_error("retrieving first key in map");
526
527         if ((rval = (dbp->seq)(dbp,key,data,R_FIRST)) != 0) {
528 #ifdef DB_CACHE
529                 TAILQ_FIRST(&qhead)->dbptr->size = 0;
530 #endif
531                 if (rval == 1)
532                         return(YP_NOKEY);
533                 else
534                         return(YP_BADDB);
535         }
536
537         /* Avoid passing back magic "YP_*" records. */
538         while (!strncmp(key->data, "YP_", 3) && !allow) {
539                 if ((rval = (dbp->seq)(dbp,key,data,R_NEXT)) != 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
550         if (ypdb_debug)
551                 yp_error("result of lookup: key: [%.*s] data: [%.*s]",
552                     (int)key->size, (char *)key->data,
553                     (int)data->size, (char *)data->data);
554
555 #ifdef DB_CACHE
556         if (TAILQ_FIRST(&qhead)->dbptr->size) {
557                 TAILQ_FIRST(&qhead)->dbptr->key = key->data;
558                 TAILQ_FIRST(&qhead)->dbptr->size = key->size;
559         }
560 #else
561         bcopy(data->data, &buf, data->size);
562         data->data = &buf;
563 #endif
564
565         return(YP_TRUE);
566 }
567
568 int
569 yp_next_record(const DB *dbp, DBT *key, DBT *data, int all, int allow)
570 {
571         static DBT lkey = { NULL, 0 };
572         static DBT ldata = { NULL, 0 };
573         int rval;
574 #ifndef DB_CACHE
575         static unsigned char keybuf[YPMAXRECORD];
576         static unsigned char datbuf[YPMAXRECORD];
577 #endif
578
579         if (key == NULL || !key->size || key->data == NULL) {
580                 rval = yp_first_record(dbp,key,data,allow);
581                 if (rval == YP_NOKEY)
582                         return(YP_NOMORE);
583                 else {
584 #ifdef DB_CACHE
585                         TAILQ_FIRST(&qhead)->dbptr->key = key->data;
586                         TAILQ_FIRST(&qhead)->dbptr->size = key->size;
587 #endif
588                         return(rval);
589                 }
590         }
591
592         if (ypdb_debug)
593                 yp_error("retrieving next key, previous was: [%.*s]",
594                     (int)key->size, (char *)key->data);
595
596         if (!all) {
597 #ifdef DB_CACHE
598                 if (TAILQ_FIRST(&qhead)->dbptr->key == NULL) {
599 #endif
600                         (dbp->seq)(dbp,&lkey,&ldata,R_FIRST);
601                         while (key->size != lkey.size ||
602                             strncmp(key->data, lkey.data,
603                             (int)key->size))
604                                 if ((dbp->seq)(dbp,&lkey,&ldata,R_NEXT)) {
605 #ifdef DB_CACHE
606                                         TAILQ_FIRST(&qhead)->dbptr->size = 0;
607 #endif
608                                         return(YP_NOKEY);
609                                 }
610
611 #ifdef DB_CACHE
612                 }
613 #endif
614         }
615
616         if ((dbp->seq)(dbp,key,data,R_NEXT)) {
617 #ifdef DB_CACHE
618                 TAILQ_FIRST(&qhead)->dbptr->size = 0;
619 #endif
620                 return(YP_NOMORE);
621         }
622
623         /* Avoid passing back magic "YP_*" records. */
624         while (!strncmp(key->data, "YP_", 3) && !allow)
625                 if ((dbp->seq)(dbp,key,data,R_NEXT)) {
626 #ifdef DB_CACHE
627                 TAILQ_FIRST(&qhead)->dbptr->size = 0;
628 #endif
629                         return(YP_NOMORE);
630                 }
631
632         if (ypdb_debug)
633                 yp_error("result of lookup: key: [%.*s] data: [%.*s]",
634                     (int)key->size, (char *)key->data,
635                     (int)data->size, (char *)data->data);
636
637 #ifdef DB_CACHE
638         if (TAILQ_FIRST(&qhead)->dbptr->size) {
639                 TAILQ_FIRST(&qhead)->dbptr->key = key->data;
640                 TAILQ_FIRST(&qhead)->dbptr->size = key->size;
641         }
642 #else
643         bcopy(key->data, &keybuf, key->size);
644         lkey.data = &keybuf;
645         lkey.size = key->size;
646         bcopy(data->data, &datbuf, data->size);
647         data->data = &datbuf;
648 #endif
649
650         return(YP_TRUE);
651 }
652
653 #ifdef DB_CACHE
654 /*
655  * Database glue functions.
656  */
657
658 static DB *yp_currmap_db = NULL;
659 static int yp_allow_db = 0;
660
661 ypstat
662 yp_select_map(char *map, char *domain, keydat *key, int allow)
663 {
664         if (key == NULL)
665                 yp_currmap_db = yp_open_db_cache(domain, map, NULL, 0);
666         else
667                 yp_currmap_db = yp_open_db_cache(domain, map,
668                                                  key->keydat_val,
669                                                  key->keydat_len);
670
671         yp_allow_db = allow;
672         return(yp_errno);
673 }
674
675 ypstat
676 yp_getbykey(keydat *key, valdat *val)
677 {
678         DBT db_key = { NULL, 0 }, db_val = { NULL, 0 };
679         ypstat rval;
680
681         db_key.data = key->keydat_val;
682         db_key.size = key->keydat_len;
683
684         rval = yp_get_record(yp_currmap_db,
685                                 &db_key, &db_val, yp_allow_db);
686
687         if (rval == YP_TRUE) {
688                 val->valdat_val = db_val.data;
689                 val->valdat_len = db_val.size;
690         }
691
692         return(rval);
693 }
694
695 ypstat
696 yp_firstbykey(keydat *key, valdat *val)
697 {
698         DBT db_key = { NULL, 0 }, db_val = { NULL, 0 };
699         ypstat rval;
700
701         rval = yp_first_record(yp_currmap_db, &db_key, &db_val, yp_allow_db);
702
703         if (rval == YP_TRUE) {
704                 key->keydat_val = db_key.data;
705                 key->keydat_len = db_key.size;
706                 val->valdat_val = db_val.data;
707                 val->valdat_len = db_val.size;
708         }
709
710         return(rval);
711 }
712
713 ypstat
714 yp_nextbykey(keydat *key, valdat *val)
715 {
716         DBT db_key = { NULL, 0 }, db_val = { NULL, 0 };
717         ypstat rval;
718
719         db_key.data = key->keydat_val;
720         db_key.size = key->keydat_len;
721
722         rval = yp_next_record(yp_currmap_db, &db_key, &db_val, 0, 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 #endif