/*++ /* NAME /* verify 8 /* SUMMARY /* Postfix address verification server /* SYNOPSIS /* \fBverify\fR [generic Postfix daemon options] /* DESCRIPTION /* The \fBverify\fR(8) address verification server maintains a record /* of what recipient addresses are known to be deliverable or /* undeliverable. /* /* Addresses are verified by injecting probe messages into the /* Postfix queue. Probe messages are run through all the routing /* and rewriting machinery except for final delivery, and are /* discarded rather than being deferred or bounced. /* /* Address verification relies on the answer from the nearest /* MTA for the specified address, and will therefore not detect /* all undeliverable addresses. /* /* The \fBverify\fR(8) server is designed to run under control /* by the Postfix /* master server. It maintains an optional persistent database. /* To avoid being interrupted by "postfix stop" in the middle /* of a database update, the process runs in a separate process /* group. /* /* The \fBverify\fR(8) server implements the following requests: /* .IP "\fBupdate\fI address status text\fR" /* Update the status and text of the specified address. /* .IP "\fBquery\fI address\fR" /* Look up the \fIstatus\fR and \fItext\fR for the specified /* \fIaddress\fR. /* If the status is unknown, a probe is sent and an "in progress" /* status is returned. /* SECURITY /* .ad /* .fi /* The address verification server is not security-sensitive. It does /* not talk to the network, and it does not talk to local users. /* The verify server can run chrooted at fixed low privilege. /* /* The address verification server can be coerced to store /* unlimited amounts of garbage. Limiting the cache expiry /* time /* trades one problem (disk space exhaustion) for another /* one (poor response time to client requests). /* /* With Postfix version 2.5 and later, the \fBverify\fR(8) /* server no longer uses root privileges when opening the /* \fBaddress_verify_map\fR cache file. The file should now /* be stored under the Postfix-owned \fBdata_directory\fR. As /* a migration aid, an attempt to open a cache file under a /* non-Postfix directory is redirected to the Postfix-owned /* \fBdata_directory\fR, and a warning is logged. /* DIAGNOSTICS /* Problems and transactions are logged to \fBsyslogd\fR(8) /* or \fBpostlogd\fR(8). /* BUGS /* Address verification probe messages add additional traffic /* to the mail queue. /* Recipient verification may cause an increased load on /* down-stream servers in the case of a dictionary attack or /* a flood of backscatter bounces. /* Sender address verification may cause your site to be /* denylisted by some providers. /* /* If the persistent database ever gets corrupted then the world /* comes to an end and human intervention is needed. This violates /* a basic Postfix principle. /* CONFIGURATION PARAMETERS /* .ad /* .fi /* Changes to \fBmain.cf\fR are not picked up automatically, /* as \fBverify\fR(8) /* processes are long-lived. Use the command "\fBpostfix reload\fR" after /* a configuration change. /* /* The text below provides only a parameter summary. See /* \fBpostconf\fR(5) for more details including examples. /* PROBE MESSAGE CONTROLS /* .ad /* .fi /* .IP "\fBaddress_verify_sender ($double_bounce_sender)\fR" /* The sender address to use in address verification probes; prior /* to Postfix 2.5 the default was "postmaster". /* .PP /* Available with Postfix 2.9 and later: /* .IP "\fBaddress_verify_sender_ttl (0s)\fR" /* The time between changes in the time-dependent portion of address /* verification probe sender addresses. /* CACHE CONTROLS /* .ad /* .fi /* .IP "\fBaddress_verify_map (see 'postconf -d' output)\fR" /* Lookup table for persistent address verification status /* storage. /* .IP "\fBaddress_verify_positive_expire_time (31d)\fR" /* The time after which a successful probe expires from the address /* verification cache. /* .IP "\fBaddress_verify_positive_refresh_time (7d)\fR" /* The time after which a successful address verification probe needs /* to be refreshed. /* .IP "\fBaddress_verify_negative_cache (yes)\fR" /* Enable caching of failed address verification probe results. /* .IP "\fBaddress_verify_negative_expire_time (3d)\fR" /* The time after which a failed probe expires from the address /* verification cache. /* .IP "\fBaddress_verify_negative_refresh_time (3h)\fR" /* The time after which a failed address verification probe needs to /* be refreshed. /* .PP /* Available with Postfix 2.7 and later: /* .IP "\fBaddress_verify_cache_cleanup_interval (12h)\fR" /* The amount of time between \fBverify\fR(8) address verification /* database cleanup runs. /* PROBE MESSAGE ROUTING CONTROLS /* .ad /* .fi /* By default, probe messages are delivered via the same route /* as regular messages. The following parameters can be used to /* override specific message routing mechanisms. /* .IP "\fBaddress_verify_relayhost ($relayhost)\fR" /* Overrides the relayhost parameter setting for address verification /* probes. /* .IP "\fBaddress_verify_transport_maps ($transport_maps)\fR" /* Overrides the transport_maps parameter setting for address verification /* probes. /* .IP "\fBaddress_verify_local_transport ($local_transport)\fR" /* Overrides the local_transport parameter setting for address /* verification probes. /* .IP "\fBaddress_verify_virtual_transport ($virtual_transport)\fR" /* Overrides the virtual_transport parameter setting for address /* verification probes. /* .IP "\fBaddress_verify_relay_transport ($relay_transport)\fR" /* Overrides the relay_transport parameter setting for address /* verification probes. /* .IP "\fBaddress_verify_default_transport ($default_transport)\fR" /* Overrides the default_transport parameter setting for address /* verification probes. /* .PP /* Available in Postfix 2.3 and later: /* .IP "\fBaddress_verify_sender_dependent_relayhost_maps ($sender_dependent_relayhost_maps)\fR" /* Overrides the sender_dependent_relayhost_maps parameter setting for address /* verification probes. /* .PP /* Available in Postfix 2.7 and later: /* .IP "\fBaddress_verify_sender_dependent_default_transport_maps ($sender_dependent_default_transport_maps)\fR" /* Overrides the sender_dependent_default_transport_maps parameter /* setting for address verification probes. /* SMTPUTF8 CONTROLS /* .ad /* .fi /* Preliminary SMTPUTF8 support is introduced with Postfix 3.0. /* .IP "\fBsmtputf8_autodetect_classes (sendmail, verify)\fR" /* Detect that a message requires SMTPUTF8 support for the specified /* mail origin classes. /* .PP /* Available in Postfix version 3.2 and later: /* .IP "\fBenable_idna2003_compatibility (no)\fR" /* Enable 'transitional' compatibility between IDNA2003 and IDNA2008, /* when converting UTF-8 domain names to/from the ASCII form that is /* used for DNS lookups. /* MISCELLANEOUS CONTROLS /* .ad /* .fi /* .IP "\fBconfig_directory (see 'postconf -d' output)\fR" /* The default location of the Postfix main.cf and master.cf /* configuration files. /* .IP "\fBdaemon_timeout (18000s)\fR" /* How much time a Postfix daemon process may take to handle a /* request before it is terminated by a built-in watchdog timer. /* .IP "\fBipc_timeout (3600s)\fR" /* The time limit for sending or receiving information over an internal /* communication channel. /* .IP "\fBprocess_id (read-only)\fR" /* The process ID of a Postfix command or daemon process. /* .IP "\fBprocess_name (read-only)\fR" /* The process name of a Postfix command or daemon process. /* .IP "\fBqueue_directory (see 'postconf -d' output)\fR" /* The location of the Postfix top-level queue directory. /* .IP "\fBsyslog_facility (mail)\fR" /* The syslog facility of Postfix logging. /* .IP "\fBsyslog_name (see 'postconf -d' output)\fR" /* A prefix that is prepended to the process name in syslog /* records, so that, for example, "smtpd" becomes "prefix/smtpd". /* .PP /* Available in Postfix 3.3 and later: /* .IP "\fBservice_name (read-only)\fR" /* The master.cf service name of a Postfix daemon process. /* SEE ALSO /* smtpd(8), Postfix SMTP server /* cleanup(8), enqueue Postfix message /* postconf(5), configuration parameters /* postlogd(8), Postfix logging /* syslogd(8), system logging /* README FILES /* .ad /* .fi /* Use "\fBpostconf readme_directory\fR" or /* "\fBpostconf html_directory\fR" to locate this information. /* .na /* .nf /* ADDRESS_VERIFICATION_README, address verification howto /* LICENSE /* .ad /* .fi /* The Secure Mailer license must be distributed with this software. /* HISTORY /* .ad /* .fi /* This service was introduced with Postfix version 2.1. /* AUTHOR(S) /* Wietse Venema /* IBM T.J. Watson Research /* P.O. Box 704 /* Yorktown Heights, NY 10598, USA /* /* Wietse Venema /* Google, Inc. /* 111 8th Avenue /* New York, NY 10011, USA /*--*/ /* System library. */ #include #include #include #include #include #include /* Utility library. */ #include #include #include #include #include #include #include #include #include /* Global library. */ #include #include #include #include #include #include #include #include /* Server skeleton. */ #include /* Application-specific. */ /* * Tunable parameters. */ char *var_verify_map; int var_verify_pos_exp; int var_verify_pos_try; int var_verify_neg_exp; int var_verify_neg_try; int var_verify_scan_cache; /* * State. */ static DICT_CACHE *verify_map; /* * Silly little macros. */ #define STR(x) vstring_str(x) #define STREQ(x,y) (strcmp(x,y) == 0) /* * The address verification database consists of (address, data) tuples. The * format of the data field is "status:probed:updated:text". The meaning of * each field is: * * status: one of the four recipient status codes (OK, DEFER, BOUNCE or TODO). * In the case of TODO, we have no information about the address, and the * address is being probed. * * probed: if non-zero, the time the currently outstanding address probe was * sent. If zero, there is no outstanding address probe. * * updated: if non-zero, the time the address probe result was received. If * zero, we have no information about the address, and the address is being * probed. * * text: descriptive text from delivery agents etc. */ /* * Quick test to see status without parsing the whole entry. */ #define STATUS_FROM_RAW_ENTRY(e) atoi(e) /* verify_make_entry - construct table entry */ static void verify_make_entry(VSTRING *buf, int status, long probed, long updated, const char *text) { vstring_sprintf(buf, "%d:%ld:%ld:%s", status, probed, updated, text); } /* verify_parse_entry - parse table entry */ static int verify_parse_entry(char *buf, int *status, long *probed, long *updated, char **text) { char *probed_text; char *updated_text; if ((probed_text = split_at(buf, ':')) != 0 && (updated_text = split_at(probed_text, ':')) != 0 && (*text = split_at(updated_text, ':')) != 0 && alldig(buf) && alldig(probed_text) && alldig(updated_text)) { *probed = atol(probed_text); *updated = atol(updated_text); *status = atoi(buf); /* * Coverity 200604: the code incorrectly tested (probed || updated), * so that the sanity check never detected all-zero time stamps. Such * records are never written. If we read a record with all-zero time * stamps, then something is badly broken. */ if ((*status == DEL_RCPT_STAT_OK || *status == DEL_RCPT_STAT_DEFER || *status == DEL_RCPT_STAT_BOUNCE || *status == DEL_RCPT_STAT_TODO) && (*probed || *updated)) return (0); } msg_warn("bad address verify table entry: %.100s", buf); return (-1); } /* verify_stat2name - status to name */ static const char *verify_stat2name(int addr_status) { if (addr_status == DEL_RCPT_STAT_OK) return ("deliverable"); if (addr_status == DEL_RCPT_STAT_DEFER) return ("undeliverable"); if (addr_status == DEL_RCPT_STAT_BOUNCE) return ("undeliverable"); return (0); } /* verify_update_service - update address service */ static void verify_update_service(VSTREAM *client_stream) { VSTRING *buf = vstring_alloc(10); VSTRING *addr = vstring_alloc(10); int addr_status; VSTRING *text = vstring_alloc(10); const char *status_name; const char *raw_data; long probed; long updated; if (attr_scan(client_stream, ATTR_FLAG_STRICT, RECV_ATTR_STR(MAIL_ATTR_ADDR, addr), RECV_ATTR_INT(MAIL_ATTR_ADDR_STATUS, &addr_status), RECV_ATTR_STR(MAIL_ATTR_WHY, text), ATTR_TYPE_END) == 3) { /* FIX 200501 IPv6 patch did not neuter ":" in address literals. */ translit(STR(addr), ":", "_"); if ((status_name = verify_stat2name(addr_status)) == 0) { msg_warn("bad recipient status %d for recipient %s", addr_status, STR(addr)); attr_print(client_stream, ATTR_FLAG_NONE, SEND_ATTR_INT(MAIL_ATTR_STATUS, VRFY_STAT_BAD), ATTR_TYPE_END); } else { /* * Robustness: don't allow a failed probe to clobber an OK * address before it expires. The failed probe is ignored so that * the address will be re-probed upon the next query. As long as * some probes succeed the address will remain cached as OK. */ if (addr_status == DEL_RCPT_STAT_OK || (raw_data = dict_cache_lookup(verify_map, STR(addr))) == 0 || STATUS_FROM_RAW_ENTRY(raw_data) != DEL_RCPT_STAT_OK) { probed = 0; updated = (long) time((time_t *) 0); printable(STR(text), '?'); verify_make_entry(buf, addr_status, probed, updated, STR(text)); if (msg_verbose) msg_info("PUT %s status=%d probed=%ld updated=%ld text=%s", STR(addr), addr_status, probed, updated, STR(text)); dict_cache_update(verify_map, STR(addr), STR(buf)); } attr_print(client_stream, ATTR_FLAG_NONE, SEND_ATTR_INT(MAIL_ATTR_STATUS, VRFY_STAT_OK), ATTR_TYPE_END); } } vstring_free(buf); vstring_free(addr); vstring_free(text); } /* verify_post_mail_fclose_action - callback */ static void verify_post_mail_fclose_action(int unused_status, void *unused_context) { /* no code here, we just need to avoid blocking in post_mail_fclose() */ } /* verify_post_mail_action - callback */ static void verify_post_mail_action(VSTREAM *stream, void *context) { /* * Probe messages need no body content, because they are never delivered, * deferred, or bounced. */ if (stream != 0) post_mail_fclose_async(stream, verify_post_mail_fclose_action, context); } /* verify_query_service - query address status */ static void verify_query_service(VSTREAM *client_stream) { VSTRING *addr = vstring_alloc(10); VSTRING *get_buf = 0; VSTRING *put_buf = 0; const char *raw_data; int addr_status; long probed; long updated; char *text; if (attr_scan(client_stream, ATTR_FLAG_STRICT, RECV_ATTR_STR(MAIL_ATTR_ADDR, addr), ATTR_TYPE_END) == 1) { long now = (long) time((time_t *) 0); /* * Produce a default record when no usable record exists. * * If negative caching is disabled, purge an expired record from the * database. * * XXX Assume that a probe is lost if no response is received in 1000 * seconds. If this number is too small the queue will slowly fill up * with delayed probes. * * XXX Maintain a moving average for the probe turnaround time, and * allow probe "retransmission" when a probe is outstanding for, say * some minimal amount of time (1000 sec) plus several times the * observed probe turnaround time. This causes probing to back off * when the mail system becomes congested. */ #define POSITIVE_ENTRY_EXPIRED(addr_status, updated) \ (addr_status == DEL_RCPT_STAT_OK && updated + var_verify_pos_exp < now) #define NEGATIVE_ENTRY_EXPIRED(addr_status, updated) \ (addr_status != DEL_RCPT_STAT_OK && updated + var_verify_neg_exp < now) #define PROBE_TTL 1000 /* FIX 200501 IPv6 patch did not neuter ":" in address literals. */ translit(STR(addr), ":", "_"); if ((raw_data = dict_cache_lookup(verify_map, STR(addr))) == 0 /* not found */ || ((get_buf = vstring_alloc(10)), vstring_strcpy(get_buf, raw_data), /* malformed */ verify_parse_entry(STR(get_buf), &addr_status, &probed, &updated, &text) < 0) || (now - probed > PROBE_TTL /* safe to probe */ && (POSITIVE_ENTRY_EXPIRED(addr_status, updated) || NEGATIVE_ENTRY_EXPIRED(addr_status, updated)))) { addr_status = DEL_RCPT_STAT_TODO; probed = 0; updated = 0; text = "Address verification in progress"; if (raw_data != 0 && var_verify_neg_cache == 0) dict_cache_delete(verify_map, STR(addr)); } if (msg_verbose) msg_info("GOT %s status=%d probed=%ld updated=%ld text=%s", STR(addr), addr_status, probed, updated, text); /* * Respond to the client. */ attr_print(client_stream, ATTR_FLAG_NONE, SEND_ATTR_INT(MAIL_ATTR_STATUS, VRFY_STAT_OK), SEND_ATTR_INT(MAIL_ATTR_ADDR_STATUS, addr_status), SEND_ATTR_STR(MAIL_ATTR_WHY, text), ATTR_TYPE_END); /* * Send a new probe when the information needs to be refreshed. * * XXX For an initial proof of concept implementation, use synchronous * mail submission. This needs to be made async for high-volume * sites, which makes it even more interesting to eliminate duplicate * queries while a probe is being built. * * If negative caching is turned off, update the database only when * refreshing an existing entry. */ #define POSITIVE_REFRESH_NEEDED(addr_status, updated) \ (addr_status == DEL_RCPT_STAT_OK && updated + var_verify_pos_try < now) #define NEGATIVE_REFRESH_NEEDED(addr_status, updated) \ (addr_status != DEL_RCPT_STAT_OK && updated + var_verify_neg_try < now) if (now - probed > PROBE_TTL && (POSITIVE_REFRESH_NEEDED(addr_status, updated) || NEGATIVE_REFRESH_NEEDED(addr_status, updated))) { if (msg_verbose) msg_info("PROBE %s status=%d probed=%ld updated=%ld", STR(addr), addr_status, now, updated); post_mail_fopen_async(make_verify_sender_addr(), STR(addr), MAIL_SRC_MASK_VERIFY, DEL_REQ_FLAG_MTA_VRFY, SMTPUTF8_FLAG_NONE, (VSTRING *) 0, verify_post_mail_action, (void *) 0); if (updated != 0 || var_verify_neg_cache != 0) { put_buf = vstring_alloc(10); verify_make_entry(put_buf, addr_status, now, updated, text); if (msg_verbose) msg_info("PUT %s status=%d probed=%ld updated=%ld text=%s", STR(addr), addr_status, now, updated, text); dict_cache_update(verify_map, STR(addr), STR(put_buf)); } } } vstring_free(addr); if (get_buf) vstring_free(get_buf); if (put_buf) vstring_free(put_buf); } /* verify_cache_validator - cache cleanup validator */ static int verify_cache_validator(const char *addr, const char *raw_data, void *context) { VSTRING *get_buf = (VSTRING *) context; int addr_status; long probed; long updated; char *text; long now = (long) event_time(); #define POS_OR_NEG_ENTRY_EXPIRED(stat, stamp) \ (POSITIVE_ENTRY_EXPIRED((stat), (stamp)) \ || NEGATIVE_ENTRY_EXPIRED((stat), (stamp))) vstring_strcpy(get_buf, raw_data); return (verify_parse_entry(STR(get_buf), &addr_status, /* syntax OK */ &probed, &updated, &text) == 0 && (now - probed < PROBE_TTL /* probe in progress */ || !POS_OR_NEG_ENTRY_EXPIRED(addr_status, updated))); } /* verify_service - perform service for client */ static void verify_service(VSTREAM *client_stream, char *unused_service, char **argv) { VSTRING *request = vstring_alloc(10); /* * Sanity check. This service takes no command-line arguments. */ if (argv[0]) msg_fatal("unexpected command-line argument: %s", argv[0]); /* * This routine runs whenever a client connects to the socket dedicated * to the address verification service. All connection-management stuff * is handled by the common code in multi_server.c. */ if (attr_scan(client_stream, ATTR_FLAG_MORE | ATTR_FLAG_STRICT, RECV_ATTR_STR(MAIL_ATTR_REQ, request), ATTR_TYPE_END) == 1) { if (STREQ(STR(request), VRFY_REQ_UPDATE)) { verify_update_service(client_stream); } else if (STREQ(STR(request), VRFY_REQ_QUERY)) { verify_query_service(client_stream); } else { msg_warn("unrecognized request: \"%s\", ignored", STR(request)); attr_print(client_stream, ATTR_FLAG_NONE, SEND_ATTR_INT(MAIL_ATTR_STATUS, VRFY_STAT_BAD), ATTR_TYPE_END); } } vstream_fflush(client_stream); vstring_free(request); } /* verify_dump - dump some statistics */ static void verify_dump(char *unused_name, char **unused_argv) { /* * Dump preliminary cache cleanup statistics when the process commits * suicide while a cache cleanup run is in progress. We can't currently * distinguish between "postfix reload" (we should restart) or "maximal * idle time reached" (we could finish the cache cleanup first). */ dict_cache_close(verify_map); verify_map = 0; } /* post_jail_init - post-jail initialization */ static void post_jail_init(char *unused_name, char **unused_argv) { /* * If the database is in volatile memory only, prevent automatic process * suicide after a limited number of client requests or after a limited * amount of idle time. */ if (*var_verify_map == 0) { var_use_limit = 0; var_idle_limit = 0; } /* * Start the cache cleanup thread. */ if (var_verify_scan_cache > 0) { int cache_flags; cache_flags = DICT_CACHE_FLAG_STATISTICS; if (msg_verbose) cache_flags |= DICT_CACHE_FLAG_VERBOSE; dict_cache_control(verify_map, CA_DICT_CACHE_CTL_FLAGS(cache_flags), CA_DICT_CACHE_CTL_INTERVAL(var_verify_scan_cache), CA_DICT_CACHE_CTL_VALIDATOR(verify_cache_validator), CA_DICT_CACHE_CTL_CONTEXT((void *) vstring_alloc(100)), CA_DICT_CACHE_CTL_END); } } /* pre_jail_init - pre-jail initialization */ static void pre_jail_init(char *unused_name, char **unused_argv) { mode_t saved_mask; VSTRING *redirect; /* * Never, ever, get killed by a master signal, as that would corrupt the * database when we're in the middle of an update. */ setsid(); /* * Security: don't create root-owned files that contain untrusted data. * And don't create Postfix-owned files in root-owned directories, * either. We want a correct relationship between (file/directory) * ownership and (file/directory) content. * * XXX Non-root open can violate the principle of least surprise: Postfix * can't open an *SQL config file for database read-write access, even * though it can open that same control file for database read-only * access. * * The solution is to query a map type and obtain its properties before * opening it. A clean solution is to add a dict_info() API that is * similar to dict_open() except it returns properties (dict flags) only. * A pragmatic solution is to overload the existing API and have * dict_open() return a dummy map when given a null map name. * * However, the proxymap daemon has been opening *SQL maps as non-root for * years now without anyone complaining, let's not solve a problem that * doesn't exist. */ SAVE_AND_SET_EUGID(var_owner_uid, var_owner_gid); redirect = vstring_alloc(100); /* * Keep state in persistent (external) or volatile (internal) map. * * Start the cache cleanup thread after permanently dropping privileges. */ #define VERIFY_DICT_OPEN_FLAGS (DICT_FLAG_DUP_REPLACE | DICT_FLAG_SYNC_UPDATE \ | DICT_FLAG_OPEN_LOCK | DICT_FLAG_UTF8_REQUEST) saved_mask = umask(022); verify_map = dict_cache_open(*var_verify_map ? data_redirect_map(redirect, var_verify_map) : "internal:verify", O_CREAT | O_RDWR, VERIFY_DICT_OPEN_FLAGS); (void) umask(saved_mask); /* * Clean up and restore privilege. */ vstring_free(redirect); RESTORE_SAVED_EUGID(); } /* post_accept_init - announce our protocol */ static void post_accept_init(VSTREAM *stream, char *unused_name, char **unused_argv, HTABLE *unused_table) { /* * Announce the protocol. */ attr_print(stream, ATTR_FLAG_NONE, SEND_ATTR_STR(MAIL_ATTR_PROTO, MAIL_ATTR_PROTO_VERIFY), ATTR_TYPE_END); (void) vstream_fflush(stream); } MAIL_VERSION_STAMP_DECLARE; /* main - pass control to the multi-threaded skeleton */ int main(int argc, char **argv) { static const CONFIG_STR_TABLE str_table[] = { VAR_VERIFY_MAP, DEF_VERIFY_MAP, &var_verify_map, 0, 0, VAR_VERIFY_SENDER, DEF_VERIFY_SENDER, &var_verify_sender, 0, 0, 0, }; static const CONFIG_TIME_TABLE time_table[] = { VAR_VERIFY_POS_EXP, DEF_VERIFY_POS_EXP, &var_verify_pos_exp, 1, 0, VAR_VERIFY_POS_TRY, DEF_VERIFY_POS_TRY, &var_verify_pos_try, 1, 0, VAR_VERIFY_NEG_EXP, DEF_VERIFY_NEG_EXP, &var_verify_neg_exp, 1, 0, VAR_VERIFY_NEG_TRY, DEF_VERIFY_NEG_TRY, &var_verify_neg_try, 1, 0, VAR_VERIFY_SCAN_CACHE, DEF_VERIFY_SCAN_CACHE, &var_verify_scan_cache, 0, 0, VAR_VERIFY_SENDER_TTL, DEF_VERIFY_SENDER_TTL, &var_verify_sender_ttl, 0, 0, 0, }; /* * Fingerprint executables and core dumps. */ MAIL_VERSION_STAMP_ALLOCATE; multi_server_main(argc, argv, verify_service, CA_MAIL_SERVER_STR_TABLE(str_table), CA_MAIL_SERVER_TIME_TABLE(time_table), CA_MAIL_SERVER_PRE_INIT(pre_jail_init), CA_MAIL_SERVER_POST_INIT(post_jail_init), CA_MAIL_SERVER_POST_ACCEPT(post_accept_init), CA_MAIL_SERVER_SOLITARY, CA_MAIL_SERVER_EXIT(verify_dump), 0); }