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