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