Testsuite: keep noqualify testcase from using external DNS
[exim.git] / src / src / exim_dbutil.c
CommitLineData
059ec3d9
PH
1/*************************************************
2* Exim - an Internet mail transport agent *
3*************************************************/
4
f9ba5e22 5/* Copyright (c) University of Cambridge 1995 - 2018 */
059ec3d9
PH
6/* See the file NOTICE for conditions of use and distribution. */
7
8
9/* This single source file is used to compile three utility programs for
10maintaining Exim hints databases.
11
12 exim_dumpdb dumps out the contents
13 exim_fixdb patches the database (really for Exim maintenance/testing)
14 exim_tidydb removed obsolete data
15
16In all cases, the first argument is the name of the spool directory. The second
17argument is the name of the database file. The available names are:
18
19 retry: retry delivery information
20 misc: miscellaneous hints data
21 wait-<t>: message waiting information; <t> is a transport name
22 callout: callout verification cache
b10c87b3 23 tls: TLS session resumption cache
059ec3d9
PH
24
25There are a number of common subroutines, followed by three main programs,
26whose inclusion is controlled by -D on the compilation command. */
27
28
c99ce5c9 29#include "exim.h"
059ec3d9
PH
30
31
32/* Identifiers for the different database types. */
33
870f6ba8
TF
34#define type_retry 1
35#define type_wait 2
36#define type_misc 3
37#define type_callout 4
38#define type_ratelimit 5
b10c87b3 39#define type_tls 6
059ec3d9
PH
40
41
c99ce5c9
TF
42/* This is used by our cut-down dbfn_open(). */
43
44uschar *spool_directory;
45
059ec3d9 46
65766f1b
JH
47/******************************************************************************/
48 /* dummies needed by Solaris build */
49gstring *
50string_vformat_trc(gstring * g, const uschar * func, unsigned line,
51 unsigned size_limit, unsigned flags, const char *format, va_list ap)
52{ return NULL; }
53/******************************************************************************/
54
059ec3d9
PH
55
56/*************************************************
57* Berkeley DB error callback *
58*************************************************/
59
60/* For Berkeley DB >= 2, we can define a function to be called in case of DB
61errors. This should help with debugging strange DB problems, e.g. getting "File
1f922db1 62exists" when you try to open a db file. The API changed at release 4.3. */
059ec3d9
PH
63
64#if defined(USE_DB) && defined(DB_VERSION_STRING)
65void
1f922db1
PH
66#if DB_VERSION_MAJOR > 4 || (DB_VERSION_MAJOR == 4 && DB_VERSION_MINOR >= 3)
67dbfn_bdb_error_callback(const DB_ENV *dbenv, const char *pfx, const char *msg)
68{
69dbenv = dbenv;
70#else
059ec3d9
PH
71dbfn_bdb_error_callback(const char *pfx, char *msg)
72{
1f922db1 73#endif
059ec3d9
PH
74pfx = pfx;
75printf("Berkeley DB error: %s\n", msg);
76}
77#endif
78
79
80
81/*************************************************
82* SIGALRM handler *
83*************************************************/
84
c99ce5c9 85SIGNAL_BOOL sigalrm_seen;
059ec3d9
PH
86
87void
88sigalrm_handler(int sig)
89{
90sig = sig; /* Keep picky compilers happy */
91sigalrm_seen = 1;
92}
93
94
95
96/*************************************************
97* Output usage message and exit *
98*************************************************/
99
100static void
101usage(uschar *name, uschar *options)
102{
103printf("Usage: exim_%s%s <spool-directory> <database-name>\n", name, options);
b10c87b3 104printf(" <database-name> = retry | misc | wait-<transport-name> | callout | ratelimit | tls\n");
059ec3d9
PH
105exit(1);
106}
107
108
109
110/*************************************************
111* Sort out the command arguments *
112*************************************************/
113
114/* This function checks that there are exactly 2 arguments, and checks the
115second of them to be sure it is a known database name. */
116
117static int
118check_args(int argc, uschar **argv, uschar *name, uschar *options)
119{
120if (argc == 3)
121 {
122 if (Ustrcmp(argv[2], "retry") == 0) return type_retry;
123 if (Ustrcmp(argv[2], "misc") == 0) return type_misc;
124 if (Ustrncmp(argv[2], "wait-", 5) == 0) return type_wait;
125 if (Ustrcmp(argv[2], "callout") == 0) return type_callout;
870f6ba8 126 if (Ustrcmp(argv[2], "ratelimit") == 0) return type_ratelimit;
b10c87b3 127 if (Ustrcmp(argv[2], "tls") == 0) return type_tls;
059ec3d9
PH
128 }
129usage(name, options);
130return -1; /* Never obeyed */
131}
132
133
134
135/*************************************************
136* Handle attempts to write the log *
137*************************************************/
138
139/* The message gets written to stderr when log_write() is called from a
140utility. The message always gets '\n' added on the end of it. These calls come
141from modules such as store.c when things go drastically wrong (e.g. malloc()
142failing). In normal use they won't get obeyed.
143
144Arguments:
145 selector not relevant when running a utility
146 flags not relevant when running a utility
147 format a printf() format
148 ... arguments for format
149
150Returns: nothing
151*/
152
153void
c99ce5c9 154log_write(unsigned int selector, int flags, const char *format, ...)
059ec3d9
PH
155{
156va_list ap;
157va_start(ap, format);
158vfprintf(stderr, format, ap);
159fprintf(stderr, "\n");
160va_end(ap);
161selector = selector; /* Keep picky compilers happy */
162flags = flags;
163}
164
165
166
167/*************************************************
168* Format a time value for printing *
169*************************************************/
170
171static uschar time_buffer[sizeof("09-xxx-1999 hh:mm:ss ")];
172
173uschar *
174print_time(time_t t)
175{
176struct tm *tmstr = localtime(&t);
177Ustrftime(time_buffer, sizeof(time_buffer), "%d-%b-%Y %H:%M:%S", tmstr);
178return time_buffer;
179}
180
181
182
183/*************************************************
184* Format a cache value for printing *
185*************************************************/
186
187uschar *
188print_cache(int value)
189{
190return (value == ccache_accept)? US"accept" :
191 (value == ccache_reject)? US"reject" :
192 US"unknown";
193}
194
195
196#ifdef EXIM_FIXDB
197/*************************************************
198* Read time value *
199*************************************************/
200
201static time_t
202read_time(uschar *s)
203{
059ec3d9
PH
204int field = 0;
205int value;
206time_t now = time(NULL);
207struct tm *tm = localtime(&now);
208
209tm->tm_sec = 0;
210tm->tm_isdst = -1;
211
d7978c0f 212for (uschar * t = s + Ustrlen(s) - 1; t >= s; t--)
059ec3d9
PH
213 {
214 if (*t == ':') continue;
215 if (!isdigit((uschar)*t)) return -1;
216
217 value = *t - '0';
218 if (--t >= s)
219 {
220 if (!isdigit((uschar)*t)) return -1;
221 value = value + (*t - '0')*10;
222 }
223
224 switch (field++)
225 {
226 case 0: tm->tm_min = value; break;
227 case 1: tm->tm_hour = value; break;
228 case 2: tm->tm_mday = value; break;
229 case 3: tm->tm_mon = value - 1; break;
230 case 4: tm->tm_year = (value < 90)? value + 100 : value; break;
231 default: return -1;
232 }
233 }
234
235return mktime(tm);
236}
237#endif /* EXIM_FIXDB */
238
239
240
241/*************************************************
242* Open and lock a database file *
243*************************************************/
244
245/* This is a cut-down version from the function in dbfn.h that Exim itself
246uses. We assume the database exists, and therefore give up if we cannot open
247the lock file.
248
249Arguments:
059ec3d9
PH
250 name The single-component name of one of Exim's database files.
251 flags O_RDONLY or O_RDWR
252 dbblock Points to an open_db block to be filled in.
c99ce5c9 253 lof Unused.
b10c87b3 254 panic Unused
059ec3d9
PH
255
256Returns: NULL if the open failed, or the locking failed.
257 On success, dbblock is returned. This contains the dbm pointer and
258 the fd of the locked lock file.
259*/
260
c99ce5c9 261open_db *
b10c87b3 262dbfn_open(uschar *name, int flags, open_db *dbblock, BOOL lof, BOOL panic)
059ec3d9
PH
263{
264int rc;
265struct flock lock_data;
266BOOL read_only = flags == O_RDONLY;
ebda598a 267uschar * dirname, * filename;
059ec3d9
PH
268
269/* The first thing to do is to open a separate file on which to lock. This
270ensures that Exim has exclusive use of the database before it even tries to
271open it. If there is a database, there should be a lock file in existence. */
272
ebda598a 273#ifdef COMPILE_UTILITY
5d520e57
JH
274if ( asprintf(CSS &dirname, "%s/db", spool_directory) < 0
275 || asprintf(CSS &filename, "%s/%s.lockfile", dirname, name) < 0)
276 return NULL;
ebda598a
JH
277#else
278dirname = string_sprintf("%s/db", spool_directory);
279filename = string_sprintf("%s/%s.lockfile", dirname, name);
280#endif
059ec3d9 281
cfb9cf20 282dbblock->lockfd = Uopen(filename, flags, 0);
059ec3d9
PH
283if (dbblock->lockfd < 0)
284 {
cfb9cf20 285 printf("** Failed to open database lock file %s: %s\n", filename,
059ec3d9
PH
286 strerror(errno));
287 return NULL;
288 }
289
290/* Now we must get a lock on the opened lock file; do this with a blocking
291lock that times out. */
292
ebda598a 293lock_data.l_type = read_only ? F_RDLCK : F_WRLCK;
059ec3d9
PH
294lock_data.l_whence = lock_data.l_start = lock_data.l_len = 0;
295
296sigalrm_seen = FALSE;
297os_non_restarting_signal(SIGALRM, sigalrm_handler);
c2a1bba0 298ALARM(EXIMDB_LOCK_TIMEOUT);
059ec3d9 299rc = fcntl(dbblock->lockfd, F_SETLKW, &lock_data);
c2a1bba0 300ALARM_CLR(0);
059ec3d9
PH
301
302if (sigalrm_seen) errno = ETIMEDOUT;
303if (rc < 0)
304 {
305 printf("** Failed to get %s lock for %s: %s",
d4ff61d1 306 flags & O_WRONLY ? "write" : "read",
cfb9cf20 307 filename,
d4ff61d1 308 errno == ETIMEDOUT ? "timed out" : strerror(errno));
f1e894f3 309 (void)close(dbblock->lockfd);
059ec3d9
PH
310 return NULL;
311 }
312
313/* At this point we have an opened and locked separate lock file, that is,
314exclusive access to the database, so we can go ahead and open it. */
315
ebda598a 316#ifdef COMPILE_UTILITY
5d520e57 317if (asprintf(CSS &filename, "%s/%s", dirname, name) < 0) return NULL;
ebda598a
JH
318#else
319filename = string_sprintf("%s/%s", dirname, name);
320#endif
cfb9cf20 321EXIM_DBOPEN(filename, dirname, flags, 0, &(dbblock->dbptr));
059ec3d9 322
13c7874e 323if (!dbblock->dbptr)
059ec3d9 324 {
cfb9cf20 325 printf("** Failed to open DBM file %s for %s:\n %s%s\n", filename,
059ec3d9
PH
326 read_only? "reading" : "writing", strerror(errno),
327 #ifdef USE_DB
328 " (or Berkeley DB error while opening)"
329 #else
330 ""
331 #endif
332 );
f1e894f3 333 (void)close(dbblock->lockfd);
059ec3d9
PH
334 return NULL;
335 }
336
337return dbblock;
338}
339
340
341
342
343/*************************************************
344* Unlock and close a database file *
345*************************************************/
346
347/* Closing a file automatically unlocks it, so after closing the database, just
348close the lock file.
349
350Argument: a pointer to an open database block
351Returns: nothing
352*/
353
c99ce5c9 354void
059ec3d9
PH
355dbfn_close(open_db *dbblock)
356{
357EXIM_DBCLOSE(dbblock->dbptr);
f1e894f3 358(void)close(dbblock->lockfd);
059ec3d9
PH
359}
360
361
362
363
364/*************************************************
365* Read from database file *
366*************************************************/
367
368/* Passing back the pointer unchanged is useless, because there is no guarantee
369of alignment. Since all the records used by Exim need to be properly aligned to
370pick out the timestamps, etc., do the copying centrally here.
371
372Arguments:
373 dbblock a pointer to an open database block
374 key the key of the record to be read
375 length where to put the length (or NULL if length not wanted)
376
377Returns: a pointer to the retrieved record, or
378 NULL if the record is not found
379*/
380
c99ce5c9 381void *
55414b25 382dbfn_read_with_length(open_db *dbblock, const uschar *key, int *length)
059ec3d9
PH
383{
384void *yield;
385EXIM_DATUM key_datum, result_datum;
55414b25 386int klen = Ustrlen(key) + 1;
f3ebb786 387uschar * key_copy = store_get(klen, is_tainted(key));
55414b25
JH
388
389memcpy(key_copy, key, klen);
059ec3d9
PH
390
391EXIM_DATUM_INIT(key_datum); /* Some DBM libraries require the datum */
392EXIM_DATUM_INIT(result_datum); /* to be cleared before use. */
55414b25
JH
393EXIM_DATUM_DATA(key_datum) = CS key_copy;
394EXIM_DATUM_SIZE(key_datum) = klen;
059ec3d9
PH
395
396if (!EXIM_DBGET(dbblock->dbptr, key_datum, result_datum)) return NULL;
397
f3ebb786
JH
398/* Assume for now that anything stored could have been tainted. Properly
399we should store the taint status along with the data. */
400
401yield = store_get(EXIM_DATUM_SIZE(result_datum), TRUE);
059ec3d9
PH
402memcpy(yield, EXIM_DATUM_DATA(result_datum), EXIM_DATUM_SIZE(result_datum));
403if (length != NULL) *length = EXIM_DATUM_SIZE(result_datum);
404
405EXIM_DATUM_FREE(result_datum); /* Some DBM libs require freeing */
406return yield;
407}
408
409
410
411#if defined(EXIM_TIDYDB) || defined(EXIM_FIXDB)
412
413/*************************************************
414* Write to database file *
415*************************************************/
416
417/*
418Arguments:
419 dbblock a pointer to an open database block
420 key the key of the record to be written
421 ptr a pointer to the record to be written
422 length the length of the record to be written
423
424Returns: the yield of the underlying dbm or db "write" function. If this
425 is dbm, the value is zero for OK.
426*/
427
c99ce5c9 428int
55414b25 429dbfn_write(open_db *dbblock, const uschar *key, void *ptr, int length)
059ec3d9
PH
430{
431EXIM_DATUM key_datum, value_datum;
432dbdata_generic *gptr = (dbdata_generic *)ptr;
55414b25 433int klen = Ustrlen(key) + 1;
f3ebb786 434uschar * key_copy = store_get(klen, is_tainted(key));
55414b25
JH
435
436memcpy(key_copy, key, klen);
059ec3d9
PH
437gptr->time_stamp = time(NULL);
438
439EXIM_DATUM_INIT(key_datum); /* Some DBM libraries require the datum */
440EXIM_DATUM_INIT(value_datum); /* to be cleared before use. */
55414b25
JH
441EXIM_DATUM_DATA(key_datum) = CS key_copy;
442EXIM_DATUM_SIZE(key_datum) = klen;
059ec3d9
PH
443EXIM_DATUM_DATA(value_datum) = CS ptr;
444EXIM_DATUM_SIZE(value_datum) = length;
445return EXIM_DBPUT(dbblock->dbptr, key_datum, value_datum);
446}
447
448
449
450/*************************************************
451* Delete record from database file *
452*************************************************/
453
454/*
455Arguments:
456 dbblock a pointer to an open database block
457 key the key of the record to be deleted
458
459Returns: the yield of the underlying dbm or db "delete" function.
460*/
461
c99ce5c9 462int
55414b25 463dbfn_delete(open_db *dbblock, const uschar *key)
059ec3d9 464{
55414b25 465int klen = Ustrlen(key) + 1;
f3ebb786 466uschar * key_copy = store_get(klen, is_tainted(key));
55414b25
JH
467
468memcpy(key_copy, key, klen);
059ec3d9
PH
469EXIM_DATUM key_datum;
470EXIM_DATUM_INIT(key_datum); /* Some DBM libraries require clearing */
55414b25
JH
471EXIM_DATUM_DATA(key_datum) = CS key_copy;
472EXIM_DATUM_SIZE(key_datum) = klen;
059ec3d9
PH
473return EXIM_DBDEL(dbblock->dbptr, key_datum);
474}
475
476#endif /* EXIM_TIDYDB || EXIM_FIXDB */
477
478
479
480#if defined(EXIM_DUMPDB) || defined(EXIM_TIDYDB)
481/*************************************************
482* Scan the keys of a database file *
483*************************************************/
484
485/*
486Arguments:
487 dbblock a pointer to an open database block
488 start TRUE if starting a new scan
489 FALSE if continuing with the current scan
490 cursor a pointer to a pointer to a cursor anchor, for those dbm libraries
491 that use the notion of a cursor
492
493Returns: the next record from the file, or
494 NULL if there are no more
495*/
496
c99ce5c9 497uschar *
059ec3d9
PH
498dbfn_scan(open_db *dbblock, BOOL start, EXIM_CURSOR **cursor)
499{
500EXIM_DATUM key_datum, value_datum;
501uschar *yield;
502value_datum = value_datum; /* dummy; not all db libraries use this */
503
504/* Some dbm require an initialization */
505
506if (start) EXIM_DBCREATE_CURSOR(dbblock->dbptr, cursor);
507
508EXIM_DATUM_INIT(key_datum); /* Some DBM libraries require the datum */
509EXIM_DATUM_INIT(value_datum); /* to be cleared before use. */
510
511yield = (EXIM_DBSCAN(dbblock->dbptr, key_datum, value_datum, start, *cursor))?
512 US EXIM_DATUM_DATA(key_datum) : NULL;
513
514/* Some dbm require a termination */
515
516if (!yield) EXIM_DBDELETE_CURSOR(*cursor);
517return yield;
518}
519#endif /* EXIM_DUMPDB || EXIM_TIDYDB */
520
521
522
523#ifdef EXIM_DUMPDB
524/*************************************************
525* The exim_dumpdb main program *
526*************************************************/
527
528int
529main(int argc, char **cargv)
530{
531int dbdata_type = 0;
532int yield = 0;
533open_db dbblock;
534open_db *dbm;
535EXIM_CURSOR *cursor;
536uschar **argv = USS cargv;
059ec3d9
PH
537uschar keybuffer[1024];
538
539/* Check the arguments, and open the database */
540
541dbdata_type = check_args(argc, argv, US"dumpdb", US"");
c99ce5c9 542spool_directory = argv[1];
b10c87b3 543if (!(dbm = dbfn_open(argv[2], O_RDONLY, &dbblock, FALSE, TRUE)))
0a6c178c 544 exit(1);
059ec3d9
PH
545
546/* Scan the file, formatting the information for each entry. Note
547that data is returned in a malloc'ed block, in order that it be
548correctly aligned. */
549
d7978c0f 550for (uschar * key = dbfn_scan(dbm, TRUE, &cursor);
0a6c178c
JH
551 key;
552 key = dbfn_scan(dbm, FALSE, &cursor))
059ec3d9
PH
553 {
554 dbdata_retry *retry;
555 dbdata_wait *wait;
556 dbdata_callout_cache *callout;
870f6ba8 557 dbdata_ratelimit *ratelimit;
c99ce5c9 558 dbdata_ratelimit_unique *rate_unique;
b10c87b3 559 dbdata_tls_session *session;
059ec3d9 560 int count_bad = 0;
d7978c0f 561 int length;
059ec3d9
PH
562 uschar *t;
563 uschar name[MESSAGE_ID_LENGTH + 1];
564 void *value;
f3ebb786 565 rmark reset_point = store_mark();
059ec3d9
PH
566
567 /* Keep a copy of the key separate, as in some DBM's the pointer is into data
568 which might change. */
569
570 if (Ustrlen(key) > sizeof(keybuffer) - 1)
571 {
572 printf("**** Overlong key encountered: %s\n", key);
573 return 1;
574 }
575 Ustrcpy(keybuffer, key);
059ec3d9 576
0a6c178c 577 if (!(value = dbfn_read_with_length(dbm, keybuffer, &length)))
059ec3d9
PH
578 fprintf(stderr, "**** Entry \"%s\" was in the key scan, but the record "
579 "was not found in the file - something is wrong!\n",
580 CS keybuffer);
581 else
582 {
583 /* Note: don't use print_time more than once in one statement, since
584 it uses a single buffer. */
585
586 switch(dbdata_type)
587 {
588 case type_retry:
d7978c0f
JH
589 retry = (dbdata_retry *)value;
590 printf(" %s %d %d %s\n%s ", keybuffer, retry->basic_errno,
591 retry->more_errno, retry->text,
592 print_time(retry->first_failed));
593 printf("%s ", print_time(retry->last_try));
594 printf("%s %s\n", print_time(retry->next_try),
595 (retry->expired)? "*" : "");
596 break;
059ec3d9
PH
597
598 case type_wait:
d7978c0f
JH
599 wait = (dbdata_wait *)value;
600 printf("%s ", keybuffer);
601 t = wait->text;
602 name[MESSAGE_ID_LENGTH] = 0;
603
604 if (wait->count > WAIT_NAME_MAX)
605 {
606 fprintf(stderr,
607 "**** Data for %s corrupted\n count=%d=0x%x max=%d\n",
608 CS keybuffer, wait->count, wait->count, WAIT_NAME_MAX);
609 wait->count = WAIT_NAME_MAX;
610 yield = count_bad = 1;
611 }
612 for (int i = 1; i <= wait->count; i++)
613 {
614 Ustrncpy(name, t, MESSAGE_ID_LENGTH);
615 if (count_bad && name[0] == 0) break;
616 if (Ustrlen(name) != MESSAGE_ID_LENGTH ||
617 Ustrspn(name, "0123456789"
618 "abcdefghijklmnopqrstuvwxyz"
619 "ABCDEFGHIJKLMNOPQRSTUVWXYZ-") != MESSAGE_ID_LENGTH)
620 {
621 fprintf(stderr,
622 "**** Data for %s corrupted: bad character in message id\n",
623 CS keybuffer);
624 for (int j = 0; j < MESSAGE_ID_LENGTH; j++)
625 fprintf(stderr, "%02x ", name[j]);
626 fprintf(stderr, "\n");
627 yield = 1;
628 break;
629 }
630 printf("%s ", name);
631 t += MESSAGE_ID_LENGTH;
632 }
633 printf("\n");
634 break;
059ec3d9
PH
635
636 case type_misc:
d7978c0f
JH
637 printf("%s %s\n", print_time(((dbdata_generic *)value)->time_stamp),
638 keybuffer);
639 break;
059ec3d9
PH
640
641 case type_callout:
d7978c0f
JH
642 callout = (dbdata_callout_cache *)value;
643
644 /* New-style address record */
645
646 if (length == sizeof(dbdata_callout_cache_address))
647 {
648 printf("%s %s callout=%s\n",
649 print_time(((dbdata_generic *)value)->time_stamp),
650 keybuffer,
651 print_cache(callout->result));
652 }
653
654 /* New-style domain record */
655
656 else if (length == sizeof(dbdata_callout_cache))
657 {
658 printf("%s %s callout=%s postmaster=%s",
659 print_time(((dbdata_generic *)value)->time_stamp),
660 keybuffer,
661 print_cache(callout->result),
662 print_cache(callout->postmaster_result));
663 if (callout->postmaster_result != ccache_unknown)
664 printf(" (%s)", print_time(callout->postmaster_stamp));
665 printf(" random=%s", print_cache(callout->random_result));
666 if (callout->random_result != ccache_unknown)
667 printf(" (%s)", print_time(callout->random_stamp));
668 printf("\n");
669 }
670
671 break;
870f6ba8
TF
672
673 case type_ratelimit:
d7978c0f
JH
674 if (Ustrstr(key, "/unique/") != NULL && length >= sizeof(*rate_unique))
675 {
676 ratelimit = (dbdata_ratelimit *)value;
677 rate_unique = (dbdata_ratelimit_unique *)value;
678 printf("%s.%06d rate: %10.3f epoch: %s size: %u key: %s\n",
679 print_time(ratelimit->time_stamp),
680 ratelimit->time_usec, ratelimit->rate,
681 print_time(rate_unique->bloom_epoch), rate_unique->bloom_size,
682 keybuffer);
683 }
684 else
685 {
686 ratelimit = (dbdata_ratelimit *)value;
687 printf("%s.%06d rate: %10.3f key: %s\n",
688 print_time(ratelimit->time_stamp),
689 ratelimit->time_usec, ratelimit->rate,
690 keybuffer);
691 }
692 break;
b10c87b3
JH
693
694 case type_tls:
695 session = (dbdata_tls_session *)value;
696 printf(" %s %.*s\n", keybuffer, length, session->session);
697 break;
059ec3d9 698 }
059ec3d9 699 }
f3ebb786 700 store_reset(reset_point);
059ec3d9
PH
701 }
702
703dbfn_close(dbm);
704return yield;
705}
706
707#endif /* EXIM_DUMPDB */
708
709
710
711
712#ifdef EXIM_FIXDB
713/*************************************************
714* The exim_fixdb main program *
715*************************************************/
716
717/* In order not to hold the database lock any longer than is necessary, each
718operation on the database uses a separate open/close call. This is expensive,
719but then using this utility is not expected to be very common. Its main use is
720to provide a way of patching up hints databases in order to run tests.
721
722Syntax of commands:
723
724(1) <record name>
725 This causes the data from the given record to be displayed, or "not found"
726 to be output. Note that in the retry database, destination names are
727 preceded by R: or T: for router or transport retry info.
728
729(2) <record name> d
730 This causes the given record to be deleted or "not found" to be output.
731
732(3) <record name> <field number> <value>
733 This sets the given value into the given field, identified by a number
734 which is output by the display command. Not all types of record can
735 be changed.
736
737(4) q
738 This exits from exim_fixdb.
739
740If the record name is omitted from (2) or (3), the previously used record name
741is re-used. */
742
743
744int main(int argc, char **cargv)
745{
746int dbdata_type;
747uschar **argv = USS cargv;
748uschar buffer[256];
749uschar name[256];
f3ebb786 750rmark reset_point;
059ec3d9
PH
751
752name[0] = 0; /* No name set */
753
754/* Sort out the database type, verify what we are working on and then process
755user requests */
756
757dbdata_type = check_args(argc, argv, US"fixdb", US"");
758printf("Modifying Exim hints database %s/db/%s\n", argv[1], argv[2]);
759
f3ebb786 760for(; (reset_point = store_mark()); store_reset(reset_point))
059ec3d9
PH
761 {
762 open_db dbblock;
763 open_db *dbm;
764 void *record;
765 dbdata_retry *retry;
766 dbdata_wait *wait;
767 dbdata_callout_cache *callout;
870f6ba8 768 dbdata_ratelimit *ratelimit;
c99ce5c9 769 dbdata_ratelimit_unique *rate_unique;
b10c87b3 770 dbdata_tls_session *session;
d7978c0f 771 int oldlength;
059ec3d9
PH
772 uschar *t;
773 uschar field[256], value[256];
774
059ec3d9
PH
775 printf("> ");
776 if (Ufgets(buffer, 256, stdin) == NULL) break;
777
778 buffer[Ustrlen(buffer)-1] = 0;
779 field[0] = value[0] = 0;
780
781 /* If the buffer contains just one digit, or just consists of "d", use the
782 previous name for an update. */
783
2695e54e 784 if ((isdigit((uschar)buffer[0]) && (buffer[1] == ' ' || buffer[1] == '\0'))
2e88a017 785 || Ustrcmp(buffer, "d") == 0)
059ec3d9
PH
786 {
787 if (name[0] == 0)
788 {
789 printf("No previous record name is set\n");
790 continue;
791 }
ff790e47 792 (void)sscanf(CS buffer, "%s %s", field, value);
059ec3d9
PH
793 }
794 else
795 {
796 name[0] = 0;
ff790e47 797 (void)sscanf(CS buffer, "%s %s %s", name, field, value);
059ec3d9
PH
798 }
799
800 /* Handle an update request */
801
802 if (field[0] != 0)
803 {
804 int verify = 1;
c99ce5c9 805 spool_directory = argv[1];
0a6c178c 806
b10c87b3 807 if (!(dbm = dbfn_open(argv[2], O_RDWR, &dbblock, FALSE, TRUE)))
0a6c178c 808 continue;
059ec3d9
PH
809
810 if (Ustrcmp(field, "d") == 0)
811 {
812 if (value[0] != 0) printf("unexpected value after \"d\"\n");
813 else printf("%s\n", (dbfn_delete(dbm, name) < 0)?
814 "not found" : "deleted");
815 dbfn_close(dbm);
816 continue;
817 }
818
819 else if (isdigit((uschar)field[0]))
820 {
821 int fieldno = Uatoi(field);
822 if (value[0] == 0)
823 {
824 printf("value missing\n");
825 dbfn_close(dbm);
826 continue;
827 }
828 else
829 {
830 record = dbfn_read_with_length(dbm, name, &oldlength);
831 if (record == NULL) printf("not found\n"); else
832 {
833 time_t tt;
4dc2379a 834 /*int length = 0; Stops compiler warning */
059ec3d9
PH
835
836 switch(dbdata_type)
837 {
838 case type_retry:
d7978c0f
JH
839 retry = (dbdata_retry *)record;
840 /* length = sizeof(dbdata_retry) + Ustrlen(retry->text); */
841
842 switch(fieldno)
843 {
844 case 0: retry->basic_errno = Uatoi(value);
845 break;
846 case 1: retry->more_errno = Uatoi(value);
847 break;
848 case 2: if ((tt = read_time(value)) > 0) retry->first_failed = tt;
849 else printf("bad time value\n");
850 break;
851 case 3: if ((tt = read_time(value)) > 0) retry->last_try = tt;
852 else printf("bad time value\n");
853 break;
854 case 4: if ((tt = read_time(value)) > 0) retry->next_try = tt;
855 else printf("bad time value\n");
856 break;
857 case 5: if (Ustrcmp(value, "yes") == 0) retry->expired = TRUE;
858 else if (Ustrcmp(value, "no") == 0) retry->expired = FALSE;
859 else printf("\"yes\" or \"no\" expected=n");
860 break;
861 default: printf("unknown field number\n");
862 verify = 0;
863 break;
864 }
865 break;
059ec3d9
PH
866
867 case type_wait:
d7978c0f
JH
868 printf("Can't change contents of wait database record\n");
869 break;
059ec3d9
PH
870
871 case type_misc:
d7978c0f
JH
872 printf("Can't change contents of misc database record\n");
873 break;
059ec3d9
PH
874
875 case type_callout:
d7978c0f
JH
876 callout = (dbdata_callout_cache *)record;
877 /* length = sizeof(dbdata_callout_cache); */
878 switch(fieldno)
879 {
880 case 0: callout->result = Uatoi(value);
881 break;
882 case 1: callout->postmaster_result = Uatoi(value);
883 break;
884 case 2: callout->random_result = Uatoi(value);
885 break;
886 default: printf("unknown field number\n");
887 verify = 0;
888 break;
889 }
890 break;
870f6ba8
TF
891
892 case type_ratelimit:
d7978c0f
JH
893 ratelimit = (dbdata_ratelimit *)record;
894 switch(fieldno)
895 {
896 case 0: if ((tt = read_time(value)) > 0) ratelimit->time_stamp = tt;
897 else printf("bad time value\n");
898 break;
899 case 1: ratelimit->time_usec = Uatoi(value);
900 break;
901 case 2: ratelimit->rate = Ustrtod(value, NULL);
902 break;
903 case 3: if (Ustrstr(name, "/unique/") != NULL
904 && oldlength >= sizeof(dbdata_ratelimit_unique))
905 {
906 rate_unique = (dbdata_ratelimit_unique *)record;
907 if ((tt = read_time(value)) > 0) rate_unique->bloom_epoch = tt;
908 else printf("bad time value\n");
909 break;
910 }
911 /* else fall through */
912 case 4:
913 case 5: if (Ustrstr(name, "/unique/") != NULL
914 && oldlength >= sizeof(dbdata_ratelimit_unique))
915 {
916 /* see acl.c */
917 BOOL seen;
918 unsigned hash, hinc;
919 uschar md5sum[16];
920 md5 md5info;
921 md5_start(&md5info);
922 md5_end(&md5info, value, Ustrlen(value), md5sum);
923 hash = md5sum[0] << 0 | md5sum[1] << 8
924 | md5sum[2] << 16 | md5sum[3] << 24;
925 hinc = md5sum[4] << 0 | md5sum[5] << 8
926 | md5sum[6] << 16 | md5sum[7] << 24;
927 rate_unique = (dbdata_ratelimit_unique *)record;
928 seen = TRUE;
929 for (unsigned n = 0; n < 8; n++, hash += hinc)
930 {
931 int bit = 1 << (hash % 8);
932 int byte = (hash / 8) % rate_unique->bloom_size;
933 if ((rate_unique->bloom[byte] & bit) == 0)
934 {
935 seen = FALSE;
936 if (fieldno == 5) rate_unique->bloom[byte] |= bit;
937 }
938 }
939 printf("%s %s\n",
940 seen ? "seen" : fieldno == 5 ? "added" : "unseen", value);
941 break;
942 }
943 /* else fall through */
944 default: printf("unknown field number\n");
945 verify = 0;
946 break;
947 }
948 break;
b10c87b3
JH
949
950 case type_tls:
951 printf("Can't change contents of tls database record\n");
952 break;
059ec3d9
PH
953 }
954
c99ce5c9 955 dbfn_write(dbm, name, record, oldlength);
059ec3d9
PH
956 }
957 }
958 }
959
960 else
961 {
962 printf("field number or d expected\n");
963 verify = 0;
964 }
965
966 dbfn_close(dbm);
967 if (!verify) continue;
968 }
969
970 /* The "name" q causes an exit */
971
972 else if (Ustrcmp(name, "q") == 0) return 0;
973
974 /* Handle a read request, or verify after an update. */
975
c99ce5c9 976 spool_directory = argv[1];
b10c87b3 977 if (!(dbm = dbfn_open(argv[2], O_RDONLY, &dbblock, FALSE, TRUE)))
0a6c178c 978 continue;
059ec3d9 979
0a6c178c 980 if (!(record = dbfn_read_with_length(dbm, name, &oldlength)))
059ec3d9
PH
981 {
982 printf("record %s not found\n", name);
983 name[0] = 0;
984 }
985 else
986 {
987 int count_bad = 0;
988 printf("%s\n", CS print_time(((dbdata_generic *)record)->time_stamp));
989 switch(dbdata_type)
990 {
991 case type_retry:
d7978c0f
JH
992 retry = (dbdata_retry *)record;
993 printf("0 error number: %d %s\n", retry->basic_errno, retry->text);
994 printf("1 extra data: %d\n", retry->more_errno);
995 printf("2 first failed: %s\n", print_time(retry->first_failed));
996 printf("3 last try: %s\n", print_time(retry->last_try));
997 printf("4 next try: %s\n", print_time(retry->next_try));
998 printf("5 expired: %s\n", (retry->expired)? "yes" : "no");
999 break;
059ec3d9
PH
1000
1001 case type_wait:
d7978c0f
JH
1002 wait = (dbdata_wait *)record;
1003 t = wait->text;
1004 printf("Sequence: %d\n", wait->sequence);
1005 if (wait->count > WAIT_NAME_MAX)
1006 {
1007 printf("**** Data corrupted: count=%d=0x%x max=%d ****\n", wait->count,
1008 wait->count, WAIT_NAME_MAX);
1009 wait->count = WAIT_NAME_MAX;
1010 count_bad = 1;
1011 }
1012 for (int i = 1; i <= wait->count; i++)
1013 {
1014 Ustrncpy(value, t, MESSAGE_ID_LENGTH);
1015 value[MESSAGE_ID_LENGTH] = 0;
1016 if (count_bad && value[0] == 0) break;
1017 if (Ustrlen(value) != MESSAGE_ID_LENGTH ||
1018 Ustrspn(value, "0123456789"
1019 "abcdefghijklmnopqrstuvwxyz"
1020 "ABCDEFGHIJKLMNOPQRSTUVWXYZ-") != MESSAGE_ID_LENGTH)
1021 {
1022 printf("\n**** Data corrupted: bad character in message id ****\n");
1023 for (int j = 0; j < MESSAGE_ID_LENGTH; j++)
1024 printf("%02x ", value[j]);
1025 printf("\n");
1026 break;
1027 }
1028 printf("%s ", value);
1029 t += MESSAGE_ID_LENGTH;
1030 }
1031 printf("\n");
1032 break;
059ec3d9
PH
1033
1034 case type_misc:
d7978c0f 1035 break;
059ec3d9
PH
1036
1037 case type_callout:
d7978c0f
JH
1038 callout = (dbdata_callout_cache *)record;
1039 printf("0 callout: %s (%d)\n", print_cache(callout->result),
1040 callout->result);
1041 if (oldlength > sizeof(dbdata_callout_cache_address))
1042 {
1043 printf("1 postmaster: %s (%d)\n", print_cache(callout->postmaster_result),
1044 callout->postmaster_result);
1045 printf("2 random: %s (%d)\n", print_cache(callout->random_result),
1046 callout->random_result);
1047 }
1048 break;
870f6ba8
TF
1049
1050 case type_ratelimit:
d7978c0f
JH
1051 ratelimit = (dbdata_ratelimit *)record;
1052 printf("0 time stamp: %s\n", print_time(ratelimit->time_stamp));
1053 printf("1 fract. time: .%06d\n", ratelimit->time_usec);
1054 printf("2 sender rate: % .3f\n", ratelimit->rate);
1055 if (Ustrstr(name, "/unique/") != NULL
1056 && oldlength >= sizeof(dbdata_ratelimit_unique))
1057 {
1058 rate_unique = (dbdata_ratelimit_unique *)record;
1059 printf("3 filter epoch: %s\n", print_time(rate_unique->bloom_epoch));
1060 printf("4 test filter membership\n");
1061 printf("5 add element to filter\n");
1062 }
1063 break;
b10c87b3
JH
1064
1065 case type_tls:
1066 session = (dbdata_tls_session *)value;
1067 printf("0 time stamp: %s\n", print_time(session->time_stamp));
1068 printf("1 session: .%s\n", session->session);
1069 break;
059ec3d9
PH
1070 }
1071 }
1072
1073 /* The database is closed after each request */
1074
1075 dbfn_close(dbm);
1076 }
1077
1078printf("\n");
1079return 0;
1080}
1081
1082#endif /* EXIM_FIXDB */
1083
1084
1085
1086#ifdef EXIM_TIDYDB
1087/*************************************************
1088* The exim_tidydb main program *
1089*************************************************/
1090
1091
1092/* Utility program to tidy the contents of an exim database file. There is one
1093option:
1094
1095 -t <time> expiry time for old records - default 30 days
1096
1097For backwards compatibility, an -f option is recognized and ignored. (It used
1098to request a "full" tidy. This version always does the whole job.) */
1099
1100
1101typedef struct key_item {
1102 struct key_item *next;
1103 uschar key[1];
1104} key_item;
1105
1106
1107int main(int argc, char **cargv)
1108{
1109struct stat statbuf;
1110int maxkeep = 30 * 24 * 60 * 60;
1111int dbdata_type, i, oldest, path_len;
1112key_item *keychain = NULL;
f3ebb786 1113rmark reset_point;
059ec3d9
PH
1114open_db dbblock;
1115open_db *dbm;
1116EXIM_CURSOR *cursor;
1117uschar **argv = USS cargv;
1118uschar buffer[256];
1119uschar *key;
1120
1121/* Scan the options */
1122
1123for (i = 1; i < argc; i++)
1124 {
1125 if (argv[i][0] != '-') break;
1126 if (Ustrcmp(argv[i], "-f") == 0) continue;
1127 if (Ustrcmp(argv[i], "-t") == 0)
1128 {
1129 uschar *s;
1130 s = argv[++i];
1131 maxkeep = 0;
1132 while (*s != 0)
1133 {
1134 int value, count;
1135 if (!isdigit(*s)) usage(US"tidydb", US" [-t <time>]");
1136 (void)sscanf(CS s, "%d%n", &value, &count);
1137 s += count;
1138 switch (*s)
1139 {
1140 case 'w': value *= 7;
1141 case 'd': value *= 24;
1142 case 'h': value *= 60;
1143 case 'm': value *= 60;
1144 case 's': s++;
1145 break;
1146 default: usage(US"tidydb", US" [-t <time>]");
1147 }
1148 maxkeep += value;
1149 }
1150 }
1151 else usage(US"tidydb", US" [-t <time>]");
1152 }
1153
1154/* Adjust argument values and process arguments */
1155
1156argc -= --i;
1157argv += i;
1158
1159dbdata_type = check_args(argc, argv, US"tidydb", US" [-t <time>]");
1160
1161/* Compute the oldest keep time, verify what we are doing, and open the
1162database */
1163
1164oldest = time(NULL) - maxkeep;
1165printf("Tidying Exim hints database %s/db/%s\n", argv[1], argv[2]);
1166
c99ce5c9 1167spool_directory = argv[1];
b10c87b3 1168if (!(dbm = dbfn_open(argv[2], O_RDWR, &dbblock, FALSE, TRUE)))
0a6c178c 1169 exit(1);
059ec3d9
PH
1170
1171/* Prepare for building file names */
1172
1173sprintf(CS buffer, "%s/input/", argv[1]);
1174path_len = Ustrlen(buffer);
1175
1176
1177/* It appears, by experiment, that it is a bad idea to make changes
1178to the file while scanning it. Pity the man page doesn't warn you about that.
1179Therefore, we scan and build a list of all the keys. Then we use that to
1180read the records and possibly update them. */
1181
0a6c178c
JH
1182for (key = dbfn_scan(dbm, TRUE, &cursor);
1183 key;
1184 key = dbfn_scan(dbm, FALSE, &cursor))
059ec3d9 1185 {
f3ebb786 1186 key_item *k = store_get(sizeof(key_item) + Ustrlen(key), is_tainted(key));
059ec3d9
PH
1187 k->next = keychain;
1188 keychain = k;
1189 Ustrcpy(k->key, key);
059ec3d9
PH
1190 }
1191
1192/* Now scan the collected keys and operate on the records, resetting
1193the store each time round. */
1194
f3ebb786 1195for (; keychain && (reset_point = store_mark()); store_reset(reset_point))
059ec3d9
PH
1196 {
1197 dbdata_generic *value;
1198
059ec3d9
PH
1199 key = keychain->key;
1200 keychain = keychain->next;
1201 value = dbfn_read_with_length(dbm, key, NULL);
1202
1203 /* A continuation record may have been deleted or renamed already, so
1204 non-existence is not serious. */
1205
1206 if (value == NULL) continue;
1207
1208 /* Delete if too old */
1209
1210 if (value->time_stamp < oldest)
1211 {
1212 printf("deleted %s (too old)\n", key);
1213 dbfn_delete(dbm, key);
1214 continue;
1215 }
1216
1217 /* Do database-specific tidying for wait databases, and message-
1218 specific tidying for the retry database. */
1219
1220 if (dbdata_type == type_wait)
1221 {
1222 dbdata_wait *wait = (dbdata_wait *)value;
1223 BOOL update = FALSE;
1224
1225 /* Leave corrupt records alone */
1226
1227 if (wait->count > WAIT_NAME_MAX)
1228 {
1229 printf("**** Data for %s corrupted\n count=%d=0x%x max=%d\n",
1230 key, wait->count, wait->count, WAIT_NAME_MAX);
1231 continue;
1232 }
1233
1234 /* Loop for renamed continuation records. For each message id,
1235 check to see if the message exists, and if not, remove its entry
1236 from the record. Because of the possibility of split input directories,
1237 we must look in both possible places for a -D file. */
1238
1239 for (;;)
1240 {
059ec3d9
PH
1241 int length = wait->count * MESSAGE_ID_LENGTH;
1242
d7978c0f 1243 for (int offset = length - MESSAGE_ID_LENGTH;
059ec3d9
PH
1244 offset >= 0; offset -= MESSAGE_ID_LENGTH)
1245 {
1246 Ustrncpy(buffer+path_len, wait->text + offset, MESSAGE_ID_LENGTH);
1247 sprintf(CS(buffer+path_len + MESSAGE_ID_LENGTH), "-D");
1248
1249 if (Ustat(buffer, &statbuf) != 0)
1250 {
1251 buffer[path_len] = wait->text[offset+5];
1252 buffer[path_len+1] = '/';
1253 Ustrncpy(buffer+path_len+2, wait->text + offset, MESSAGE_ID_LENGTH);
1254 sprintf(CS(buffer+path_len+2 + MESSAGE_ID_LENGTH), "-D");
1255
1256 if (Ustat(buffer, &statbuf) != 0)
1257 {
1258 int left = length - offset - MESSAGE_ID_LENGTH;
1259 if (left > 0) Ustrncpy(wait->text + offset,
1260 wait->text + offset + MESSAGE_ID_LENGTH, left);
1261 wait->count--;
1262 length -= MESSAGE_ID_LENGTH;
1263 update = TRUE;
1264 }
1265 }
1266 }
1267
1268 /* If record is empty and the main record, either delete it or rename
1269 the next continuation, repeating if that is also empty. */
1270
1271 if (wait->count == 0 && Ustrchr(key, ':') == NULL)
1272 {
1273 while (wait->count == 0 && wait->sequence > 0)
1274 {
1275 uschar newkey[256];
1276 dbdata_generic *newvalue;
1277 sprintf(CS newkey, "%s:%d", key, wait->sequence - 1);
1278 newvalue = dbfn_read_with_length(dbm, newkey, NULL);
1279 if (newvalue != NULL)
1280 {
1281 value = newvalue;
1282 wait = (dbdata_wait *)newvalue;
1283 dbfn_delete(dbm, newkey);
1284 printf("renamed %s\n", newkey);
1285 update = TRUE;
1286 }
1287 else wait->sequence--;
1288 }
1289
1290 /* If we have ended up with an empty main record, delete it
1291 and break the loop. Otherwise the new record will be scanned. */
1292
1293 if (wait->count == 0 && wait->sequence == 0)
1294 {
1295 dbfn_delete(dbm, key);
1296 printf("deleted %s (empty)\n", key);
1297 update = FALSE;
1298 break;
1299 }
1300 }
1301
1302 /* If not an empty main record, break the loop */
1303
1304 else break;
1305 }
1306
1307 /* Re-write the record if required */
1308
1309 if (update)
1310 {
1311 printf("updated %s\n", key);
1312 dbfn_write(dbm, key, wait, sizeof(dbdata_wait) +
1313 wait->count * MESSAGE_ID_LENGTH);
1314 }
1315 }
1316
1317 /* If a retry record's key ends with a message-id, check that that message
1318 still exists; if not, remove this record. */
1319
1320 else if (dbdata_type == type_retry)
1321 {
1322 uschar *id;
1323 int len = Ustrlen(key);
1324
1325 if (len < MESSAGE_ID_LENGTH + 1) continue;
1326 id = key + len - MESSAGE_ID_LENGTH - 1;
1327 if (*id++ != ':') continue;
1328
1329 for (i = 0; i < MESSAGE_ID_LENGTH; i++)
059ec3d9
PH
1330 if (i == 6 || i == 13)
1331 { if (id[i] != '-') break; }
1332 else
1333 { if (!isalnum(id[i])) break; }
059ec3d9
PH
1334 if (i < MESSAGE_ID_LENGTH) continue;
1335
1336 Ustrncpy(buffer + path_len, id, MESSAGE_ID_LENGTH);
1337 sprintf(CS(buffer + path_len + MESSAGE_ID_LENGTH), "-D");
1338
1339 if (Ustat(buffer, &statbuf) != 0)
1340 {
0ec020ea
PH
1341 sprintf(CS(buffer + path_len), "%c/%s-D", id[5], id);
1342 if (Ustat(buffer, &statbuf) != 0)
1343 {
1344 dbfn_delete(dbm, key);
1345 printf("deleted %s (no message)\n", key);
1346 }
059ec3d9
PH
1347 }
1348 }
1349 }
1350
1351dbfn_close(dbm);
1352printf("Tidying complete\n");
1353return 0;
1354}
1355
1356#endif /* EXIM_TIDYDB */
1357
1358/* End of exim_dbutil.c */