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