88b4d8920c1155a8d380256513dd20c2794ed793
[exim.git] / src / src / dbfn.c
1 /*************************************************
2 *     Exim - an Internet mail transport agent    *
3 *************************************************/
4
5 /* Copyright (c) The Exim Maintainers 2020 - 2024 */
6 /* Copyright (c) University of Cambridge 1995 - 2018 */
7 /* See the file NOTICE for conditions of use and distribution. */
8 /* SPDX-License-Identifier: GPL-2.0-or-later */
9
10
11 #include "exim.h"
12
13 /* We have buffers holding path names for database files.
14 PATH_MAX could be used here, but would be wasting memory, as we deal
15 with database files like $spooldirectory/db/<name> */
16 #define PATHLEN 256
17
18
19 /* Functions for accessing Exim's hints database, which consists of a number of
20 different DBM files. This module does not contain code for reading DBM files
21 for (e.g.) alias expansion. That is all contained within the general search
22 functions. As Exim now has support for several DBM interfaces, all the relevant
23 functions are called as inlinable functions from an included file.
24
25 All the data in Exim's database is in the nature of *hints*. Therefore it
26 doesn't matter if it gets destroyed by accident. These functions are not
27 supposed to implement a "safe" database.
28
29 Keys are passed in as C strings, and the terminating zero *is* used when
30 building the dbm files. This just makes life easier when scanning the files
31 sequentially.
32
33 Synchronization is required on the database files, and this is achieved by
34 means of locking on independent lock files. (Earlier attempts to lock on the
35 DBM files themselves were never completely successful.) Since callers may in
36 general want to do more than one read or write while holding the lock, there
37 are separate open and close functions. However, the calling modules should
38 arrange to hold the locks for the bare minimum of time.
39
40 API:
41   dbfn_open
42   dbfn_close
43   dbfn_read_with_length
44   dbfn_read_enforce_length
45   dbfn_write
46   dbfn_delete
47   dbfn_scan                             unused; ifdeffout out
48
49 Users:
50   ACL ratelimit & seen conditions
51   delivery retry handling
52   delivery serialization
53   TLS session resumption
54   peer capability cache
55   callout & quota cache
56 */
57
58
59
60 /*************************************************
61 *          Open and lock a database file         *
62 *************************************************/
63
64 /* Lock a file to protect the DB.  Return TRUE for success */
65
66 static inline BOOL
67 lockfile_take(open_db * dbblock, const uschar * filename, BOOL rdonly, BOOL panic)
68 {
69 flock_t lock_data;
70 int rc;
71
72 priv_drop_temp(exim_uid, exim_gid);
73 if ((dbblock->lockfd = Uopen(filename, O_RDWR, EXIMDB_LOCKFILE_MODE)) < 0)
74   {
75   (void)directory_make(spool_directory, US"db", EXIMDB_DIRECTORY_MODE, panic);
76   dbblock->lockfd = Uopen(filename, O_RDWR|O_CREAT, EXIMDB_LOCKFILE_MODE);
77   }
78 priv_restore();
79
80 if (dbblock->lockfd < 0)
81   {
82   log_write(0, LOG_MAIN, "%s",
83     string_open_failed("database lock file %s", filename));
84   errno = 0;      /* Indicates locking failure */
85   return FALSE;
86   }
87
88 /* Now we must get a lock on the opened lock file; do this with a blocking
89 lock that times out. */
90
91 lock_data.l_type = rdonly ? F_RDLCK : F_WRLCK;
92 lock_data.l_whence = lock_data.l_start = lock_data.l_len = 0;
93
94 DEBUG(D_hints_lookup|D_retry|D_route|D_deliver)
95   debug_printf_indent("locking %s\n", filename);
96
97 sigalrm_seen = FALSE;
98 ALARM(EXIMDB_LOCK_TIMEOUT);
99 rc = fcntl(dbblock->lockfd, F_SETLKW, &lock_data);
100 ALARM_CLR(0);
101
102 if (sigalrm_seen) errno = ETIMEDOUT;
103 if (rc < 0)
104   {
105   log_write(0, LOG_MAIN|LOG_PANIC, "Failed to get %s lock for %s: %s",
106     rdonly ? "read" : "write", filename,
107     errno == ETIMEDOUT ? "timed out" : strerror(errno));
108   (void)close(dbblock->lockfd);
109   errno = 0;       /* Indicates locking failure */
110   return FALSE;
111   }
112
113 DEBUG(D_hints_lookup) debug_printf_indent("locked  %s\n", filename);
114 return TRUE;
115 }
116
117 /* Used for accessing Exim's hints databases.
118
119 Arguments:
120   name     The single-component name of one of Exim's database files.
121   flags    Either O_RDONLY or O_RDWR, indicating the type of open required;
122              O_RDWR implies "create if necessary"
123   dbblock  Points to an open_db block to be filled in.
124   lof      If TRUE, write to the log for actual open failures (locking failures
125            are always logged).
126   panic    If TRUE, panic on failure to create the db directory
127
128 Returns:   NULL if the open failed, or the locking failed. After locking
129            failures, errno is zero.
130
131            On success, dbblock is returned. This contains the dbm pointer and
132            the fd of the locked lock file.
133 */
134
135 open_db *
136 dbfn_open(const uschar * name, int flags, open_db * dbblock,
137   BOOL lof, BOOL panic)
138 {
139 int rc, save_errno;
140 BOOL read_only = flags & O_RDONLY;
141 uschar dirname[PATHLEN], filename[PATHLEN];
142
143 DEBUG(D_hints_lookup) acl_level++;
144
145 /* The first thing to do is to open a separate file on which to lock. This
146 ensures that Exim has exclusive use of the database before it even tries to
147 open it. Early versions tried to lock on the open database itself, but that
148 gave rise to mysterious problems from time to time - it was suspected that some
149 DB libraries "do things" on their open() calls which break the interlocking.
150 The lock file is never written to, but we open it for writing so we can get a
151 write lock if required. If it does not exist, we create it. This is done
152 separately so we know when we have done it, because when running as root we
153 need to change the ownership - see the bottom of this function. We also try to
154 make the directory as well, just in case. We won't be doing this many times
155 unnecessarily, because usually the lock file will be there. If the directory
156 exists, there is no error. */
157
158 snprintf(CS dirname, sizeof(dirname), "%s/db", spool_directory);
159 snprintf(CS filename, sizeof(filename), "%s/%s.lockfile", dirname, name);
160
161 dbblock->lockfd = -1;
162 if (!lockfile_take(dbblock, filename, flags == O_RDONLY, panic))
163   {
164   DEBUG(D_hints_lookup) acl_level--;
165   return NULL;
166   }
167
168 /* At this point we have an opened and locked separate lock file,
169 that is, exclusive access to the database, so we can go ahead and open it. If we
170 are expected to create it, don't do so at first, again so that we can detect
171 whether we need to change its ownership (see comments about the lock file
172 above.) There have been regular reports of crashes while opening hints
173 databases - often this is caused by non-matching db.h and the library. To make
174 it easy to pin this down, there are now debug statements on either side of the
175 open call. */
176
177 flags &= O_RDONLY | O_RDWR;
178 snprintf(CS filename, sizeof(filename), "%s/%s", dirname, name);
179
180 priv_drop_temp(exim_uid, exim_gid);
181 dbblock->dbptr = exim_dbopen(filename, dirname, flags, EXIMDB_MODE);
182 if (!dbblock->dbptr && errno == ENOENT && flags == O_RDWR)
183   {
184   DEBUG(D_hints_lookup)
185     debug_printf_indent("%s appears not to exist: trying to create\n", filename);
186   dbblock->dbptr = exim_dbopen(filename, dirname, flags|O_CREAT, EXIMDB_MODE);
187   }
188 save_errno = errno;
189 priv_restore();
190
191 /* If the open has failed, return NULL, leaving errno set. If lof is TRUE,
192 log the event - also for debugging - but debug only if the file just doesn't
193 exist. */
194
195 if (!dbblock->dbptr)
196   {
197   errno = save_errno;
198   if (lof && save_errno != ENOENT)
199     log_write(0, LOG_MAIN, "%s", string_open_failed("DB file %s",
200         filename));
201   else
202     DEBUG(D_hints_lookup)
203       debug_printf_indent("%s\n", CS string_open_failed("DB file %s",
204           filename));
205   (void)close(dbblock->lockfd);
206   errno = save_errno;
207   DEBUG(D_hints_lookup) acl_level--;
208   return NULL;
209   }
210
211 DEBUG(D_hints_lookup)
212   debug_printf_indent("opened hints database %s: flags=%s\n", filename,
213     flags == O_RDONLY ? "O_RDONLY"
214     : flags == O_RDWR ? "O_RDWR"
215     : "??");
216
217 /* Pass back the block containing the opened database handle and the open fd
218 for the lock. */
219
220 return dbblock;
221 }
222
223
224
225
226 /*************************************************
227 *         Unlock and close a database file       *
228 *************************************************/
229
230 /* Closing a file automatically unlocks it, so after closing the database, just
231 close the lock file.
232
233 Argument: a pointer to an open database block
234 Returns:  nothing
235 */
236
237 void
238 dbfn_close(open_db *dbblock)
239 {
240 exim_dbclose(dbblock->dbptr);
241 (void)close(dbblock->lockfd);
242 DEBUG(D_hints_lookup)
243   { debug_printf_indent("closed hints database and lockfile\n"); acl_level--; }
244 }
245
246
247
248
249 /*************************************************
250 *             Read from database file            *
251 *************************************************/
252
253 /* Passing back the pointer unchanged is useless, because there is
254 no guarantee of alignment. Since all the records used by Exim need
255 to be properly aligned to pick out the timestamps, etc., we might as
256 well do the copying centrally here.
257
258 Most calls don't need the length, so there is a macro called dbfn_read which
259 has two arguments; it calls this function adding NULL as the third.
260
261 Arguments:
262   dbblock   a pointer to an open database block
263   key       the key of the record to be read
264   length    a pointer to an int into which to return the length, if not NULL
265
266 Returns: a pointer to the retrieved record, or
267          NULL if the record is not found
268 */
269
270 void *
271 dbfn_read_with_length(open_db * dbblock, const uschar * key, int * length)
272 {
273 void * yield;
274 EXIM_DATUM key_datum, result_datum;
275 int klen = Ustrlen(key) + 1;
276 uschar * key_copy = store_get(klen, key);
277 unsigned dlen;
278
279 memcpy(key_copy, key, klen);
280
281 DEBUG(D_hints_lookup) debug_printf_indent("dbfn_read: key=%s\n", key);
282
283 exim_datum_init(&key_datum);         /* Some DBM libraries require the datum */
284 exim_datum_init(&result_datum);      /* to be cleared before use. */
285 exim_datum_data_set(&key_datum, key_copy);
286 exim_datum_size_set(&key_datum, klen);
287
288 if (!exim_dbget(dbblock->dbptr, &key_datum, &result_datum))
289   {
290   DEBUG(D_hints_lookup) debug_printf_indent("dbfn_read: null return\n");
291   return NULL;
292   }
293
294 /* Assume the data store could have been tainted.  Properly, we should
295 store the taint status with the data. */
296
297 dlen = exim_datum_size_get(&result_datum);
298 yield = store_get(dlen, GET_TAINTED);
299 memcpy(yield, exim_datum_data_get(&result_datum), dlen);
300 DEBUG(D_hints_lookup) debug_printf_indent("dbfn_read: size %u return\n", dlen);
301 if (length) *length = dlen;
302
303 exim_datum_free(&result_datum);    /* Some DBM libs require freeing */
304 return yield;
305 }
306
307
308 /* Read a record.  If the length is not as expected then delete it, write
309 an error log line, delete the record and return NULL.
310 Use this for fixed-size records (so not retry or wait records).
311
312 Arguments:
313   dbblock   a pointer to an open database block
314   key       the key of the record to be read
315   length    the expected record length
316
317 Returns: a pointer to the retrieved record, or
318          NULL if the record is not found/bad
319 */
320
321 void *
322 dbfn_read_enforce_length(open_db * dbblock, const uschar * key, size_t length)
323 {
324 int rlen;
325 void * yield = dbfn_read_with_length(dbblock, key, &rlen);
326
327 if (yield)
328   {
329   if (rlen == length) return yield;
330   log_write(0, LOG_MAIN|LOG_PANIC, "Bad db record size for '%s'", key);
331   dbfn_delete(dbblock, key);
332   }
333 return NULL;
334 }
335
336
337 /*************************************************
338 *             Write to database file             *
339 *************************************************/
340
341 /*
342 Arguments:
343   dbblock   a pointer to an open database block
344   key       the key of the record to be written
345   ptr       a pointer to the record to be written
346   length    the length of the record to be written
347
348 Returns:    the yield of the underlying dbm or db "write" function. If this
349             is dbm, the value is zero for OK.
350 */
351
352 int
353 dbfn_write(open_db *dbblock, const uschar *key, void *ptr, int length)
354 {
355 EXIM_DATUM key_datum, value_datum;
356 dbdata_generic *gptr = (dbdata_generic *)ptr;
357 int klen = Ustrlen(key) + 1;
358 uschar * key_copy = store_get(klen, key);
359
360 memcpy(key_copy, key, klen);
361 gptr->time_stamp = time(NULL);
362
363 DEBUG(D_hints_lookup)
364   debug_printf_indent("dbfn_write: key=%s datalen %d\n", key, length);
365
366 exim_datum_init(&key_datum);         /* Some DBM libraries require the datum */
367 exim_datum_init(&value_datum);       /* to be cleared before use. */
368 exim_datum_data_set(&key_datum, key_copy);
369 exim_datum_size_set(&key_datum, klen);
370 exim_datum_data_set(&value_datum, ptr);
371 exim_datum_size_set(&value_datum, length);
372 return exim_dbput(dbblock->dbptr, &key_datum, &value_datum);
373 }
374
375
376
377 /*************************************************
378 *           Delete record from database file     *
379 *************************************************/
380
381 /*
382 Arguments:
383   dbblock    a pointer to an open database block
384   key        the key of the record to be deleted
385
386 Returns: the yield of the underlying dbm or db "delete" function.
387 */
388
389 int
390 dbfn_delete(open_db *dbblock, const uschar *key)
391 {
392 int klen = Ustrlen(key) + 1;
393 uschar * key_copy = store_get(klen, key);
394 EXIM_DATUM key_datum;
395
396 DEBUG(D_hints_lookup) debug_printf_indent("dbfn_delete: key=%s\n", key);
397
398 memcpy(key_copy, key, klen);
399 exim_datum_init(&key_datum);         /* Some DBM libraries require clearing */
400 exim_datum_data_set(&key_datum, key_copy);
401 exim_datum_size_set(&key_datum, klen);
402 return exim_dbdel(dbblock->dbptr, &key_datum);
403 }
404
405
406
407 #ifdef notdef
408 /* XXX This appears to be unused.  There's a separate implementation
409 in dbutils.c for dumpdb and fixdb, using the same underlying support.
410 */
411
412 /*************************************************
413 *         Scan the keys of a database file       *
414 *************************************************/
415
416 /*
417 Arguments:
418   dbblock  a pointer to an open database block
419   start    TRUE if starting a new scan
420            FALSE if continuing with the current scan
421   cursor   a pointer to a pointer to a cursor anchor, for those dbm libraries
422            that use the notion of a cursor
423
424 Returns:   the next record from the file, or
425            NULL if there are no more
426 */
427
428 uschar *
429 dbfn_scan(open_db *dbblock, BOOL start, EXIM_CURSOR **cursor)
430 {
431 EXIM_DATUM key_datum, value_datum;
432 uschar *yield;
433
434 DEBUG(D_hints_lookup) debug_printf_indent("dbfn_scan\n");
435
436 /* Some dbm require an initialization */
437
438 if (start) *cursor = exim_dbcreate_cursor(dbblock->dbptr);
439
440 exim_datum_init(&key_datum);         /* Some DBM libraries require the datum */
441 exim_datum_init(&value_datum);       /* to be cleared before use. */
442
443 yield = exim_dbscan(dbblock->dbptr, &key_datum, &value_datum, start, *cursor)
444   ? US exim_datum_data_get(&key_datum) : NULL;
445
446 /* Some dbm require a termination */
447
448 if (!yield) exim_dbdelete_cursor(*cursor);
449 return yield;
450 }
451 #endif
452
453
454
455 /*************************************************
456 **************************************************
457 *             Stand-alone test program           *
458 **************************************************
459 *************************************************/
460
461 #ifdef STAND_ALONE
462
463 int
464 main(int argc, char **cargv)
465 {
466 open_db dbblock[8];
467 int max_db = sizeof(dbblock)/sizeof(open_db);
468 int current = -1;
469 int showtime = 0;
470 int i;
471 dbdata_wait *dbwait = NULL;
472 uschar **argv = USS cargv;
473 uschar buffer[256];
474 uschar structbuffer[1024];
475
476 if (argc != 2)
477   {
478   printf("Usage: test_dbfn directory\n");
479   printf("The subdirectory called \"db\" in the given directory is used for\n");
480   printf("the files used in this test program.\n");
481   return 1;
482   }
483
484 /* Initialize */
485
486 spool_directory = argv[1];
487 debug_selector = D_all - D_memory;
488 debug_file = stderr;
489 big_buffer = malloc(big_buffer_size);
490
491 for (i = 0; i < max_db; i++) dbblock[i].dbptr = NULL;
492
493 printf("\nExim's db functions tester: interface type is %s\n", EXIM_DBTYPE);
494 printf("DBM library: ");
495
496 #ifdef DB_VERSION_STRING
497 printf("Berkeley DB: %s\n", DB_VERSION_STRING);
498 #elif defined(BTREEVERSION) && defined(HASHVERSION)
499   #ifdef USE_DB
500   printf("probably Berkeley DB version 1.8x (native mode)\n");
501   #else
502   printf("probably Berkeley DB version 1.8x (compatibility mode)\n");
503   #endif
504 #elif defined(_DBM_RDONLY) || defined(dbm_dirfno)
505 printf("probably ndbm\n");
506 #elif defined(USE_TDB)
507 printf("using tdb\n");
508 #else
509   #ifdef USE_GDBM
510   printf("probably GDBM (native mode)\n");
511   #else
512   printf("probably GDBM (compatibility mode)\n");
513   #endif
514 #endif
515
516 /* Test the functions */
517
518 printf("\nTest the functions\n> ");
519
520 while (Ufgets(buffer, 256, stdin) != NULL)
521   {
522   int len = Ustrlen(buffer);
523   int count = 1;
524   clock_t start = 1;
525   clock_t stop = 0;
526   uschar *cmd = buffer;
527   while (len > 0 && isspace((uschar)buffer[len-1])) len--;
528   buffer[len] = 0;
529
530   if (isdigit((uschar)*cmd))
531     {
532     count = Uatoi(cmd);
533     while (isdigit((uschar)*cmd)) cmd++;
534     Uskip_whitespace(&cmd);
535     }
536
537   if (Ustrncmp(cmd, "open", 4) == 0)
538     {
539     int i;
540     open_db *odb;
541     uschar *s = cmd + 4;
542     Uskip_whitespace(&s);
543
544     for (i = 0; i < max_db; i++)
545       if (dbblock[i].dbptr == NULL) break;
546
547     if (i >= max_db)
548       {
549       printf("Too many open databases\n> ");
550       continue;
551       }
552
553     start = clock();
554     odb = dbfn_open(s, O_RDWR, dbblock + i, TRUE, TRUE);
555     stop = clock();
556
557     if (odb)
558       {
559       current = i;
560       printf("opened %d\n", current);
561       }
562     /* Other error cases will have written messages */
563     else if (errno == ENOENT)
564       {
565       printf("open failed: %s%s\n", strerror(errno),
566         #ifdef USE_DB
567         " (or other Berkeley DB error)"
568         #else
569         ""
570         #endif
571         );
572       }
573     }
574
575   else if (Ustrncmp(cmd, "write", 5) == 0)
576     {
577     int rc = 0;
578     uschar * key = cmd + 5, * data;
579
580     if (current < 0)
581       {
582       printf("No current database\n");
583       continue;
584       }
585
586     Uskip_whitespace(&key);
587     data = key;
588     Uskip_nonwhite(&data);
589     *data++ = '\0';
590     Uskip_whitespace(&data);
591
592     dbwait = (dbdata_wait *)(&structbuffer);
593     Ustrcpy(dbwait->text, data);
594
595     start = clock();
596     while (count-- > 0)
597       rc = dbfn_write(dbblock + current, key, dbwait,
598         Ustrlen(data) + sizeof(dbdata_wait));
599     stop = clock();
600     if (rc != 0) printf("Failed: %s\n", strerror(errno));
601     }
602
603   else if (Ustrncmp(cmd, "read", 4) == 0)
604     {
605     uschar * key = cmd + 4;
606     if (current < 0)
607       {
608       printf("No current database\n");
609       continue;
610       }
611     Uskip_whitespace(&key);
612     start = clock();
613     while (count-- > 0)
614       dbwait = (dbdata_wait *)dbfn_read_with_length(dbblock+ current, key, NULL);
615     stop = clock();
616     printf("%s\n", (dbwait == NULL)? "<not found>" : CS dbwait->text);
617     }
618
619   else if (Ustrncmp(cmd, "delete", 6) == 0)
620     {
621     uschar * key = cmd + 6;
622     if (current < 0)
623       {
624       printf("No current database\n");
625       continue;
626       }
627     Uskip_whitespace(&key);
628     dbfn_delete(dbblock + current, key);
629     }
630
631   else if (Ustrncmp(cmd, "scan", 4) == 0)
632     {
633     EXIM_CURSOR *cursor;
634     BOOL startflag = TRUE;
635     uschar *key;
636     uschar keybuffer[256];
637     if (current < 0)
638       {
639       printf("No current database\n");
640       continue;
641       }
642     start = clock();
643     while ((key = dbfn_scan(dbblock + current, startflag, &cursor)) != NULL)
644       {
645       startflag = FALSE;
646       Ustrcpy(keybuffer, key);
647       dbwait = (dbdata_wait *)dbfn_read_with_length(dbblock + current,
648         keybuffer, NULL);
649       printf("%s: %s\n", keybuffer, dbwait->text);
650       }
651     stop = clock();
652     printf("End of scan\n");
653     }
654
655   else if (Ustrncmp(cmd, "close", 5) == 0)
656     {
657     uschar * s = cmd + 5;
658     Uskip_whitespace(&s);
659     i = Uatoi(s);
660     if (i >= max_db || dbblock[i].dbptr == NULL) printf("Not open\n"); else
661       {
662       start = clock();
663       dbfn_close(dbblock + i);
664       stop = clock();
665       dbblock[i].dbptr = NULL;
666       if (i == current) current = -1;
667       }
668     }
669
670   else if (Ustrncmp(cmd, "file", 4) == 0)
671     {
672     uschar * s = cmd + 4;
673     Uskip_whitespace(&s);
674     i = Uatoi(s);
675     if (i >= max_db || dbblock[i].dbptr == NULL) printf("Not open\n");
676       else current = i;
677     }
678
679   else if (Ustrncmp(cmd, "time", 4) == 0)
680     {
681     showtime = ~showtime;
682     printf("Timing %s\n", showtime? "on" : "off");
683     }
684
685   else if (Ustrcmp(cmd, "q") == 0 || Ustrncmp(cmd, "quit", 4) == 0) break;
686
687   else if (Ustrncmp(cmd, "help", 4) == 0)
688     {
689     printf("close  [<number>]              close file [<number>]\n");
690     printf("delete <key>                   remove record from current file\n");
691     printf("file   <number>                make file <number> current\n");
692     printf("open   <name>                  open db file\n");
693     printf("q[uit]                         exit program\n");
694     printf("read   <key>                   read record from current file\n");
695     printf("scan                           scan current file\n");
696     printf("time                           time display on/off\n");
697     printf("write  <key> <rest-of-line>    write record to current file\n");
698     }
699
700   else printf("Eh?\n");
701
702   if (showtime && stop >= start)
703     printf("start=%d stop=%d difference=%d\n", (int)start, (int)stop,
704      (int)(stop - start));
705
706   printf("> ");
707   }
708
709 for (i = 0; i < max_db; i++)
710   {
711   if (dbblock[i].dbptr != NULL)
712     {
713     printf("\nClosing %d", i);
714     dbfn_close(dbblock + i);
715     }
716   }
717
718 printf("\n");
719 return 0;
720 }
721
722 #endif
723
724 /* End of dbfn.c */
725 /* vi: aw ai sw=2
726 */