]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/ypserv/yp_dblookup.c
Optionally bind ktls threads to NUMA domains
[FreeBSD/FreeBSD.git] / usr.sbin / ypserv / yp_dblookup.c
1 /*-
2  * SPDX-License-Identifier: BSD-4-Clause
3  *
4  * Copyright (c) 1995
5  *      Bill Paul <wpaul@ctr.columbia.edu>.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *      This product includes software developed by Bill Paul.
18  * 4. Neither the name of the author nor the names of any co-contributors
19  *    may be used to endorse or promote products derived from this software
20  *    without specific prior written permission.
21  *
22  * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34
35 #include <sys/cdefs.h>
36 __FBSDID("$FreeBSD$");
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 TAILQ_HEAD(circlehead, circleq_entry) qhead;
83
84 struct circleq_entry {
85         struct dbent *dbptr;
86         TAILQ_ENTRY(circleq_entry) links;
87 };
88
89 /*
90  * Initialize the circular queue.
91  */
92 void
93 yp_init_dbs(void)
94 {
95         TAILQ_INIT(&qhead);
96         return;
97 }
98
99 /*
100  * Dynamically allocate an entry for the circular queue.
101  * Return a NULL pointer on failure.
102  */
103 static struct circleq_entry *
104 yp_malloc_qent(void)
105 {
106         register struct circleq_entry *q;
107
108         q = malloc(sizeof(struct circleq_entry));
109         if (q == NULL) {
110                 yp_error("failed to malloc() circleq entry");
111                 return(NULL);
112         }
113         bzero((char *)q, sizeof(struct circleq_entry));
114         q->dbptr = malloc(sizeof(struct dbent));
115         if (q->dbptr == NULL) {
116                 yp_error("failed to malloc() circleq entry");
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
130 yp_free_qent(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
168 yp_flush(void)
169 {
170         register struct circleq_entry *qptr;
171
172         qptr = TAILQ_LAST(&qhead, circlehead);
173         TAILQ_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  */
183 void
184 yp_flush_all(void)
185 {
186         register struct circleq_entry *qptr;
187
188         while (!TAILQ_EMPTY(&qhead)) {
189                 qptr = TAILQ_FIRST(&qhead); /* save this */
190                 TAILQ_REMOVE(&qhead, qptr, links);
191                 yp_free_qent(qptr);
192         }
193         numdbs = 0;
194
195         return;
196 }
197
198 static char *inter_string = "YP_INTERDOMAIN";
199 static char *secure_string = "YP_SECURE";
200 static int inter_sz = sizeof("YP_INTERDOMAIN") - 1;
201 static int secure_sz = sizeof("YP_SECURE") - 1;
202
203 static int
204 yp_setflags(DB *dbp)
205 {
206         DBT key = { NULL, 0 }, data = { NULL, 0 };
207         int flags = 0;
208
209         key.data = inter_string;
210         key.size = inter_sz;
211
212         if (!(dbp->get)(dbp, &key, &data, 0))
213                 flags |= YP_INTERDOMAIN;
214
215         key.data = secure_string;
216         key.size = secure_sz;
217
218         if (!(dbp->get)(dbp, &key, &data, 0))
219                 flags |= YP_SECURE;
220
221         return(flags);
222 }
223
224 int
225 yp_testflag(char *map, char *domain, int flag)
226 {
227         char buf[MAXPATHLEN + 2];
228         register struct circleq_entry *qptr;
229
230         if (map == NULL || domain == NULL)
231                 return(0);
232
233         strcpy(buf, domain);
234         strcat(buf, "/");
235         strcat(buf, map);
236
237         TAILQ_FOREACH(qptr, &qhead, links) {
238                 if (!strcmp(qptr->dbptr->name, buf)) {
239                         if (qptr->dbptr->flags & flag)
240                                 return(1);
241                         else
242                                 return(0);
243                 }
244         }
245
246         if (yp_open_db_cache(domain, map, NULL, 0) == NULL)
247                 return(0);
248
249         if (TAILQ_FIRST(&qhead)->dbptr->flags & flag)
250                 return(1);
251
252         return(0);
253 }
254
255 /*
256  * Add a DB handle and database name to the cache. We only maintain
257  * fixed number of entries in the cache, so if we're asked to store
258  * a new entry when all our slots are already filled, we have to kick
259  * out the entry in the last slot to make room.
260  */
261 static int
262 yp_cache_db(DB *dbp, char *name, int size)
263 {
264         register struct circleq_entry *qptr;
265
266         if (numdbs == MAXDBS) {
267                 if (ypdb_debug)
268                         yp_error("queue overflow -- releasing last slot");
269                 yp_flush();
270         }
271
272         /*
273          * Allocate a new queue entry.
274          */
275
276         if ((qptr = yp_malloc_qent()) == NULL) {
277                 yp_error("failed to allocate a new cache entry");
278                 return(1);
279         }
280
281         qptr->dbptr->dbp = dbp;
282         qptr->dbptr->name = strdup(name);
283         qptr->dbptr->size = size;
284         qptr->dbptr->key = NULL;
285
286         qptr->dbptr->flags = yp_setflags(dbp);
287
288         TAILQ_INSERT_HEAD(&qhead, qptr, links);
289         numdbs++;
290
291         return(0);
292 }
293
294 /*
295  * Search the list for a database matching 'name.' If we find it,
296  * move it to the head of the list and return its DB handle. If
297  * not, just fail: yp_open_db_cache() will subsequently try to open
298  * the database itself and call yp_cache_db() to add it to the
299  * list.
300  *
301  * The search works like this:
302  *
303  * - The caller specifies the name of a database to locate. We try to
304  *   find an entry in our queue with a matching name.
305  *
306  * - If the caller doesn't specify a key or size, we assume that the
307  *   first entry that we encounter with a matching name is returned.
308  *   This will result in matches regardless of the key/size values
309  *   stored in the queue entry.
310  *
311  * - If the caller also specifies a key and length, we check to see
312  *   if the key and length saved in the queue entry also matches.
313  *   This lets us return a DB handle that's already positioned at the
314  *   correct location within a database.
315  *
316  * - Once we have a match, it gets migrated to the top of the queue
317  *   so that it will be easier to find if another request for
318  *   the same database comes in later.
319  */
320 static DB *
321 yp_find_db(const char *name, const char *key, int size)
322 {
323         register struct circleq_entry *qptr;
324
325         TAILQ_FOREACH(qptr, &qhead, links) {
326                 if (!strcmp(qptr->dbptr->name, name)) {
327                         if (size) {
328                                 if (size != qptr->dbptr->size ||
329                                    strncmp(qptr->dbptr->key, key, size))
330                                         continue;
331                         } else {
332                                 if (qptr->dbptr->size)
333                                         continue;
334                         }
335                         if (qptr != TAILQ_FIRST(&qhead)) {
336                                 TAILQ_REMOVE(&qhead, qptr, links);
337                                 TAILQ_INSERT_HEAD(&qhead, qptr, links);
338                         }
339                         return(qptr->dbptr->dbp);
340                 }
341         }
342
343         return(NULL);
344 }
345
346 /*
347  * Open a DB database and cache the handle for later use. We first
348  * check the cache to see if the required database is already open.
349  * If so, we fetch the handle from the cache. If not, we try to open
350  * the database and save the handle in the cache for later use.
351  */
352 DB *
353 yp_open_db_cache(const char *domain, const char *map, const char *key,
354     const int size)
355 {
356         DB *dbp = NULL;
357         char buf[MAXPATHLEN + 2];
358 /*
359         snprintf(buf, sizeof(buf), "%s/%s", domain, map);
360 */
361         yp_errno = YP_TRUE;
362
363         strcpy(buf, domain);
364         strcat(buf, "/");
365         strcat(buf, map);
366
367         if ((dbp = yp_find_db(buf, key, size)) != NULL) {
368                 return(dbp);
369         } else {
370                 if ((dbp = yp_open_db(domain, map)) != NULL) {
371                         if (yp_cache_db(dbp, buf, size)) {
372                                 (void)(dbp->close)(dbp);
373                                 yp_errno = YP_YPERR;
374                                 return(NULL);
375                         }
376                 }
377         }
378
379         return (dbp);
380 }
381 #endif
382
383 /*
384  * Open a DB database.
385  */
386 DB *
387 yp_open_db(const char *domain, const char *map)
388 {
389         DB *dbp = NULL;
390         char buf[MAXPATHLEN + 2];
391
392         yp_errno = YP_TRUE;
393
394         if (map[0] == '.' || strchr(map, '/')) {
395                 yp_errno = YP_BADARGS;
396                 return (NULL);
397         }
398
399 #ifdef DB_CACHE
400         if (yp_validdomain(domain)) {
401                 yp_errno = YP_NODOM;
402                 return(NULL);
403         }
404 #endif
405         snprintf(buf, sizeof(buf), "%s/%s/%s", yp_dir, domain, map);
406
407 #ifdef DB_CACHE
408 again:
409 #endif
410         dbp = dbopen(buf, O_RDONLY, PERM_SECURE, DB_HASH, NULL);
411
412         if (dbp == NULL) {
413                 switch (errno) {
414 #ifdef DB_CACHE
415                 case ENFILE:
416                         /*
417                          * We ran out of file descriptors. Nuke an
418                          * open one and try again.
419                          */
420                         yp_error("ran out of file descriptors");
421                         yp_flush();
422                         goto again;
423                         break;
424 #endif
425                 case ENOENT:
426                         yp_errno = YP_NOMAP;
427                         break;
428                 case EFTYPE:
429                         yp_errno = YP_BADDB;
430                         break;
431                 default:
432                         yp_errno = YP_YPERR;
433                         break;
434                 }
435         }
436
437         return (dbp);
438 }
439
440 /*
441  * Database access routines.
442  *
443  * - yp_get_record(): retrieve an arbitrary key/data pair given one key
444  *                 to match against.
445  *
446  * - yp_first_record(): retrieve first key/data base in a database.
447  *
448  * - yp_next_record(): retrieve key/data pair that sequentially follows
449  *                   the supplied key value in the database.
450  */
451
452 #ifdef DB_CACHE
453 int
454 yp_get_record(DB *dbp, const DBT *key, DBT *data, int allow)
455 #else
456 int
457 yp_get_record(const char *domain, const char *map,
458     const DBT *key, DBT *data, int allow)
459 #endif
460 {
461 #ifndef DB_CACHE
462         DB *dbp;
463 #endif
464         int rval = 0;
465 #ifndef DB_CACHE
466         static unsigned char buf[YPMAXRECORD];
467 #endif
468
469         if (ypdb_debug)
470                 yp_error("looking up key [%.*s]",
471                     (int)key->size, (char *)key->data);
472
473         /*
474          * Avoid passing back magic "YP_*" entries unless
475          * the caller specifically requested them by setting
476          * the 'allow' flag.
477          */
478         if (!allow && !strncmp(key->data, "YP_", 3))
479                 return(YP_NOKEY);
480
481 #ifndef DB_CACHE
482         if ((dbp = yp_open_db(domain, map)) == NULL) {
483                 return(yp_errno);
484         }
485 #endif
486
487         if ((rval = (dbp->get)(dbp, key, data, 0)) != 0) {
488 #ifdef DB_CACHE
489                 TAILQ_FIRST(&qhead)->dbptr->size = 0;
490 #else
491                 (void)(dbp->close)(dbp);
492 #endif
493                 if (rval == 1)
494                         return(YP_NOKEY);
495                 else
496                         return(YP_BADDB);
497         }
498
499         if (ypdb_debug)
500                 yp_error("result of lookup: key: [%.*s] data: [%.*s]",
501                     (int)key->size, (char *)key->data,
502                     (int)data->size, (char *)data->data);
503
504 #ifdef DB_CACHE
505         if (TAILQ_FIRST(&qhead)->dbptr->size) {
506                 TAILQ_FIRST(&qhead)->dbptr->key = "";
507                 TAILQ_FIRST(&qhead)->dbptr->size = 0;
508         }
509 #else
510         bcopy(data->data, &buf, data->size);
511         data->data = &buf;
512         (void)(dbp->close)(dbp);
513 #endif
514
515         return(YP_TRUE);
516 }
517
518 int
519 yp_first_record(const DB *dbp, DBT *key, DBT *data, int allow)
520 {
521         int rval;
522 #ifndef DB_CACHE
523         static unsigned char buf[YPMAXRECORD];
524 #endif
525
526         if (ypdb_debug)
527                 yp_error("retrieving first key in map");
528
529         if ((rval = (dbp->seq)(dbp,key,data,R_FIRST)) != 0) {
530 #ifdef DB_CACHE
531                 TAILQ_FIRST(&qhead)->dbptr->size = 0;
532 #endif
533                 if (rval == 1)
534                         return(YP_NOKEY);
535                 else
536                         return(YP_BADDB);
537         }
538
539         /* Avoid passing back magic "YP_*" records. */
540         while (!strncmp(key->data, "YP_", 3) && !allow) {
541                 if ((rval = (dbp->seq)(dbp,key,data,R_NEXT)) != 0) {
542 #ifdef DB_CACHE
543                         TAILQ_FIRST(&qhead)->dbptr->size = 0;
544 #endif
545                         if (rval == 1)
546                                 return(YP_NOKEY);
547                         else
548                                 return(YP_BADDB);
549                 }
550         }
551
552         if (ypdb_debug)
553                 yp_error("result of lookup: key: [%.*s] data: [%.*s]",
554                     (int)key->size, (char *)key->data,
555                     (int)data->size, (char *)data->data);
556
557 #ifdef DB_CACHE
558         if (TAILQ_FIRST(&qhead)->dbptr->size) {
559                 TAILQ_FIRST(&qhead)->dbptr->key = key->data;
560                 TAILQ_FIRST(&qhead)->dbptr->size = key->size;
561         }
562 #else
563         bcopy(data->data, &buf, data->size);
564         data->data = &buf;
565 #endif
566
567         return(YP_TRUE);
568 }
569
570 int
571 yp_next_record(const DB *dbp, DBT *key, DBT *data, int all, int allow)
572 {
573         static DBT lkey = { NULL, 0 };
574         static DBT ldata = { NULL, 0 };
575         int rval;
576 #ifndef DB_CACHE
577         static unsigned char keybuf[YPMAXRECORD];
578         static unsigned char datbuf[YPMAXRECORD];
579 #endif
580
581         if (key == NULL || !key->size || key->data == NULL) {
582                 rval = yp_first_record(dbp,key,data,allow);
583                 if (rval == YP_NOKEY)
584                         return(YP_NOMORE);
585                 else {
586 #ifdef DB_CACHE
587                         TAILQ_FIRST(&qhead)->dbptr->key = key->data;
588                         TAILQ_FIRST(&qhead)->dbptr->size = key->size;
589 #endif
590                         return(rval);
591                 }
592         }
593
594         if (ypdb_debug)
595                 yp_error("retrieving next key, previous was: [%.*s]",
596                     (int)key->size, (char *)key->data);
597
598         if (!all) {
599 #ifdef DB_CACHE
600                 if (TAILQ_FIRST(&qhead)->dbptr->key == NULL) {
601 #endif
602                         (dbp->seq)(dbp,&lkey,&ldata,R_FIRST);
603                         while (key->size != lkey.size ||
604                             strncmp(key->data, lkey.data,
605                             (int)key->size))
606                                 if ((dbp->seq)(dbp,&lkey,&ldata,R_NEXT)) {
607 #ifdef DB_CACHE
608                                         TAILQ_FIRST(&qhead)->dbptr->size = 0;
609 #endif
610                                         return(YP_NOKEY);
611                                 }
612
613 #ifdef DB_CACHE
614                 }
615 #endif
616         }
617
618         if ((dbp->seq)(dbp,key,data,R_NEXT)) {
619 #ifdef DB_CACHE
620                 TAILQ_FIRST(&qhead)->dbptr->size = 0;
621 #endif
622                 return(YP_NOMORE);
623         }
624
625         /* Avoid passing back magic "YP_*" records. */
626         while (!strncmp(key->data, "YP_", 3) && !allow)
627                 if ((dbp->seq)(dbp,key,data,R_NEXT)) {
628 #ifdef DB_CACHE
629                 TAILQ_FIRST(&qhead)->dbptr->size = 0;
630 #endif
631                         return(YP_NOMORE);
632                 }
633
634         if (ypdb_debug)
635                 yp_error("result of lookup: key: [%.*s] data: [%.*s]",
636                     (int)key->size, (char *)key->data,
637                     (int)data->size, (char *)data->data);
638
639 #ifdef DB_CACHE
640         if (TAILQ_FIRST(&qhead)->dbptr->size) {
641                 TAILQ_FIRST(&qhead)->dbptr->key = key->data;
642                 TAILQ_FIRST(&qhead)->dbptr->size = key->size;
643         }
644 #else
645         bcopy(key->data, &keybuf, key->size);
646         lkey.data = &keybuf;
647         lkey.size = key->size;
648         bcopy(data->data, &datbuf, data->size);
649         data->data = &datbuf;
650 #endif
651
652         return(YP_TRUE);
653 }
654
655 #ifdef DB_CACHE
656 /*
657  * Database glue functions.
658  */
659
660 static DB *yp_currmap_db = NULL;
661 static int yp_allow_db = 0;
662
663 ypstat
664 yp_select_map(char *map, char *domain, keydat *key, int allow)
665 {
666         if (key == NULL)
667                 yp_currmap_db = yp_open_db_cache(domain, map, NULL, 0);
668         else
669                 yp_currmap_db = yp_open_db_cache(domain, map,
670                                                  key->keydat_val,
671                                                  key->keydat_len);
672
673         yp_allow_db = allow;
674         return(yp_errno);
675 }
676
677 ypstat
678 yp_getbykey(keydat *key, valdat *val)
679 {
680         DBT db_key = { NULL, 0 }, db_val = { NULL, 0 };
681         ypstat rval;
682
683         db_key.data = key->keydat_val;
684         db_key.size = key->keydat_len;
685
686         rval = yp_get_record(yp_currmap_db,
687                                 &db_key, &db_val, yp_allow_db);
688
689         if (rval == YP_TRUE) {
690                 val->valdat_val = db_val.data;
691                 val->valdat_len = db_val.size;
692         }
693
694         return(rval);
695 }
696
697 ypstat
698 yp_firstbykey(keydat *key, valdat *val)
699 {
700         DBT db_key = { NULL, 0 }, db_val = { NULL, 0 };
701         ypstat rval;
702
703         rval = yp_first_record(yp_currmap_db, &db_key, &db_val, yp_allow_db);
704
705         if (rval == YP_TRUE) {
706                 key->keydat_val = db_key.data;
707                 key->keydat_len = db_key.size;
708                 val->valdat_val = db_val.data;
709                 val->valdat_len = db_val.size;
710         }
711
712         return(rval);
713 }
714
715 ypstat
716 yp_nextbykey(keydat *key, valdat *val)
717 {
718         DBT db_key = { NULL, 0 }, db_val = { NULL, 0 };
719         ypstat rval;
720
721         db_key.data = key->keydat_val;
722         db_key.size = key->keydat_len;
723
724         rval = yp_next_record(yp_currmap_db, &db_key, &db_val, 0, yp_allow_db);
725
726         if (rval == YP_TRUE) {
727                 key->keydat_val = db_key.data;
728                 key->keydat_len = db_key.size;
729                 val->valdat_val = db_val.data;
730                 val->valdat_len = db_val.size;
731         }
732
733         return(rval);
734 }
735 #endif