/*++ /* NAME /* bounce 8 /* SUMMARY /* Postfix delivery status reports /* SYNOPSIS /* \fBbounce\fR [generic Postfix daemon options] /* DESCRIPTION /* The \fBbounce\fR(8) daemon maintains per-message log files with /* delivery status information. Each log file is named after the /* queue file that it corresponds to, and is kept in a queue subdirectory /* named after the service name in the \fBmaster.cf\fR file (either /* \fBbounce\fR, \fBdefer\fR or \fBtrace\fR). /* This program expects to be run from the \fBmaster\fR(8) process /* manager. /* /* The \fBbounce\fR(8) daemon processes two types of service requests: /* .IP \(bu /* Append a recipient (non-)delivery status record to a per-message /* log file. /* .IP \(bu /* Enqueue a delivery status notification message, with a copy /* of a per-message log file and of the corresponding message. /* When the delivery status notification message is /* enqueued successfully, the per-message log file is deleted. /* .PP /* The software does a best notification effort. A non-delivery /* notification is sent even when the log file or the original /* message cannot be read. /* /* Optionally, a bounce (defer, trace) client can request that the /* per-message log file be deleted when the requested operation fails. /* This is used by clients that cannot retry transactions by /* themselves, and that depend on retry logic in their own client. /* STANDARDS /* RFC 822 (ARPA Internet Text Messages) /* RFC 2045 (Format of Internet Message Bodies) /* RFC 2822 (Internet Message Format) /* RFC 3462 (Delivery Status Notifications) /* RFC 3464 (Delivery Status Notifications) /* RFC 3834 (Auto-Submitted: message header) /* RFC 5322 (Internet Message Format) /* RFC 6531 (Internationalized SMTP) /* RFC 6532 (Internationalized Message Format) /* RFC 6533 (Internationalized Delivery Status Notifications) /* DIAGNOSTICS /* Problems and transactions are logged to \fBsyslogd\fR(8) /* or \fBpostlogd\fR(8). /* CONFIGURATION PARAMETERS /* .ad /* .fi /* Changes to \fBmain.cf\fR are picked up automatically, as \fBbounce\fR(8) /* processes run for only a limited amount of time. Use the command /* "\fBpostfix reload\fR" to speed up a change. /* /* The text below provides only a parameter summary. See /* \fBpostconf\fR(5) for more details including examples. /* .IP "\fB2bounce_notice_recipient (postmaster)\fR" /* The recipient of undeliverable mail that cannot be returned to /* the sender. /* .IP "\fBbackwards_bounce_logfile_compatibility (yes)\fR" /* Produce additional \fBbounce\fR(8) logfile records that can be read by /* Postfix versions before 2.0. /* .IP "\fBbounce_notice_recipient (postmaster)\fR" /* The recipient of postmaster notifications with the message headers /* of mail that Postfix did not deliver and of SMTP conversation /* transcripts of mail that Postfix did not receive. /* .IP "\fBbounce_size_limit (50000)\fR" /* The maximal amount of original message text that is sent in a /* non-delivery notification. /* .IP "\fBbounce_template_file (empty)\fR" /* Pathname of a configuration file with bounce message templates. /* .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 "\fBdelay_notice_recipient (postmaster)\fR" /* The recipient of postmaster notifications with the message headers /* of mail that cannot be delivered within $delay_warning_time time /* units. /* .IP "\fBdeliver_lock_attempts (20)\fR" /* The maximal number of attempts to acquire an exclusive lock on a /* mailbox file or \fBbounce\fR(8) logfile. /* .IP "\fBdeliver_lock_delay (1s)\fR" /* The time between attempts to acquire an exclusive lock on a mailbox /* file or \fBbounce\fR(8) logfile. /* .IP "\fBipc_timeout (3600s)\fR" /* The time limit for sending or receiving information over an internal /* communication channel. /* .IP "\fBinternal_mail_filter_classes (empty)\fR" /* What categories of Postfix-generated mail are subject to /* before-queue content inspection by non_smtpd_milters, header_checks /* and body_checks. /* .IP "\fBmail_name (Postfix)\fR" /* The mail system name that is displayed in Received: headers, in /* the SMTP greeting banner, and in bounced mail. /* .IP "\fBmax_idle (100s)\fR" /* The maximum amount of time that an idle Postfix daemon process waits /* for an incoming connection before terminating voluntarily. /* .IP "\fBmax_use (100)\fR" /* The maximal number of incoming connections that a Postfix daemon /* process will service before terminating voluntarily. /* .IP "\fBnotify_classes (resource, software)\fR" /* The list of error classes that are reported to the postmaster. /* .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.0 and later: /* .IP "\fBsmtputf8_autodetect_classes (sendmail, verify)\fR" /* Detect that a message requires SMTPUTF8 support for the specified /* mail origin classes. /* .PP /* Available in Postfix 3.3 and later: /* .IP "\fBservice_name (read-only)\fR" /* The master.cf service name of a Postfix daemon process. /* .PP /* Available in Postfix 3.6 and later: /* .IP "\fBenable_threaded_bounces (no)\fR" /* Enable non-delivery, success, and delay notifications that link /* to the original message by including a References: and In-Reply-To: /* header with the original Message-ID value. /* .PP /* Available in Postfix 3.7 and later: /* .IP "\fBheader_from_format (standard)\fR" /* The format of the Postfix-generated \fBFrom:\fR header. /* FILES /* /var/spool/postfix/bounce/* non-delivery records /* /var/spool/postfix/defer/* non-delivery records /* /var/spool/postfix/trace/* delivery status records /* SEE ALSO /* bounce(5), bounce message template format /* qmgr(8), queue manager /* postconf(5), configuration parameters /* master(5), generic daemon options /* master(8), process manager /* postlogd(8), Postfix logging /* syslogd(8), system logging /* LICENSE /* .ad /* .fi /* The Secure Mailer license must be distributed with this software. /* 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 /* Utility library. */ #include #include #include #include #include /* Global library. */ #include #include #include #include #include #include #include #include #include #include /* Single-threaded server skeleton. */ #include /* Application-specific. */ #include /* * Tunables. */ int var_bounce_limit; int var_max_queue_time; int var_delay_warn_time; char *var_notify_classes; char *var_bounce_rcpt; char *var_2bounce_rcpt; char *var_delay_rcpt; char *var_bounce_tmpl; bool var_threaded_bounce; char *var_hfrom_format; /* header_from_format */ /* * We're single threaded, so we can avoid some memory allocation overhead. */ static VSTRING *queue_id; static VSTRING *queue_name; static RCPT_BUF *rcpt_buf; static VSTRING *encoding; static VSTRING *sender; static VSTRING *dsn_envid; static VSTRING *verp_delims; static DSN_BUF *dsn_buf; /* * Templates. */ BOUNCE_TEMPLATES *bounce_templates; /* * From: header format. */ int bounce_hfrom_format; #define STR vstring_str #define VS_NEUTER(s) printable(vstring_str(s), '?') /* bounce_append_proto - bounce_append server protocol */ static int bounce_append_proto(char *service_name, VSTREAM *client) { const char *myname = "bounce_append_proto"; int flags; /* * Read and validate the client request. */ if (mail_command_server(client, RECV_ATTR_INT(MAIL_ATTR_FLAGS, &flags), RECV_ATTR_STR(MAIL_ATTR_QUEUEID, queue_id), RECV_ATTR_FUNC(rcpb_scan, (void *) rcpt_buf), RECV_ATTR_FUNC(dsb_scan, (void *) dsn_buf), ATTR_TYPE_END) != 4) { msg_warn("malformed request"); return (-1); } /* * Sanitize input. */ if (mail_queue_id_ok(STR(queue_id)) == 0) { msg_warn("malformed queue id: %s", printable(STR(queue_id), '?')); return (-1); } VS_NEUTER(rcpt_buf->address); VS_NEUTER(rcpt_buf->orig_addr); VS_NEUTER(rcpt_buf->dsn_orcpt); VS_NEUTER(dsn_buf->status); VS_NEUTER(dsn_buf->action); VS_NEUTER(dsn_buf->reason); VS_NEUTER(dsn_buf->dtype); VS_NEUTER(dsn_buf->dtext); VS_NEUTER(dsn_buf->mtype); VS_NEUTER(dsn_buf->mname); (void) RECIPIENT_FROM_RCPT_BUF(rcpt_buf); (void) DSN_FROM_DSN_BUF(dsn_buf); /* * Beware: some DSN or RECIPIENT fields may be null; access dsn_buf and * rcpt_buf buffers instead. See DSN_FROM_DSN_BUF() and * RECIPIENT_FROM_RCPT_BUF(). */ if (msg_verbose) msg_info("%s: flags=0x%x service=%s id=%s org_to=%s to=%s off=%ld dsn_org=%s, notif=0x%x stat=%s act=%s why=%s", myname, flags, service_name, STR(queue_id), STR(rcpt_buf->orig_addr), STR(rcpt_buf->address), rcpt_buf->offset, STR(rcpt_buf->dsn_orcpt), rcpt_buf->dsn_notify, STR(dsn_buf->status), STR(dsn_buf->action), STR(dsn_buf->reason)); /* * On request by the client, set up a trap to delete the log file in case * of errors. */ if (flags & BOUNCE_FLAG_CLEAN) bounce_cleanup_register(service_name, STR(queue_id)); /* * Execute the request. */ return (bounce_append_service(flags, service_name, STR(queue_id), &rcpt_buf->rcpt, &dsn_buf->dsn)); } /* bounce_notify_proto - bounce_notify server protocol */ static int bounce_notify_proto(char *service_name, VSTREAM *client, int (*service) (int, char *, char *, char *, char *, int, char *, char *, int, BOUNCE_TEMPLATES *)) { const char *myname = "bounce_notify_proto"; int flags; int smtputf8; int dsn_ret; /* * Read and validate the client request. */ if (mail_command_server(client, RECV_ATTR_INT(MAIL_ATTR_FLAGS, &flags), RECV_ATTR_STR(MAIL_ATTR_QUEUE, queue_name), RECV_ATTR_STR(MAIL_ATTR_QUEUEID, queue_id), RECV_ATTR_STR(MAIL_ATTR_ENCODING, encoding), RECV_ATTR_INT(MAIL_ATTR_SMTPUTF8, &smtputf8), RECV_ATTR_STR(MAIL_ATTR_SENDER, sender), RECV_ATTR_STR(MAIL_ATTR_DSN_ENVID, dsn_envid), RECV_ATTR_INT(MAIL_ATTR_DSN_RET, &dsn_ret), ATTR_TYPE_END) != 8) { msg_warn("malformed request"); return (-1); } /* * Sanitize input. */ if (mail_queue_name_ok(STR(queue_name)) == 0) { msg_warn("malformed queue name: %s", printable(STR(queue_name), '?')); return (-1); } if (mail_queue_id_ok(STR(queue_id)) == 0) { msg_warn("malformed queue id: %s", printable(STR(queue_id), '?')); return (-1); } VS_NEUTER(encoding); VS_NEUTER(sender); VS_NEUTER(dsn_envid); if (msg_verbose) msg_info("%s: flags=0x%x service=%s queue=%s id=%s encoding=%s smtputf8=%d sender=%s envid=%s ret=0x%x", myname, flags, service_name, STR(queue_name), STR(queue_id), STR(encoding), smtputf8, STR(sender), STR(dsn_envid), dsn_ret); /* * On request by the client, set up a trap to delete the log file in case * of errors. */ if (flags & BOUNCE_FLAG_CLEAN) bounce_cleanup_register(service_name, STR(queue_id)); /* * Execute the request. */ return (service(flags, service_name, STR(queue_name), STR(queue_id), STR(encoding), smtputf8, STR(sender), STR(dsn_envid), dsn_ret, bounce_templates)); } /* bounce_verp_proto - bounce_notify server protocol, VERP style */ static int bounce_verp_proto(char *service_name, VSTREAM *client) { const char *myname = "bounce_verp_proto"; int flags; int smtputf8; int dsn_ret; /* * Read and validate the client request. */ if (mail_command_server(client, RECV_ATTR_INT(MAIL_ATTR_FLAGS, &flags), RECV_ATTR_STR(MAIL_ATTR_QUEUE, queue_name), RECV_ATTR_STR(MAIL_ATTR_QUEUEID, queue_id), RECV_ATTR_STR(MAIL_ATTR_ENCODING, encoding), RECV_ATTR_INT(MAIL_ATTR_SMTPUTF8, &smtputf8), RECV_ATTR_STR(MAIL_ATTR_SENDER, sender), RECV_ATTR_STR(MAIL_ATTR_DSN_ENVID, dsn_envid), RECV_ATTR_INT(MAIL_ATTR_DSN_RET, &dsn_ret), RECV_ATTR_STR(MAIL_ATTR_VERPDL, verp_delims), ATTR_TYPE_END) != 9) { msg_warn("malformed request"); return (-1); } /* * Sanitize input. */ if (mail_queue_name_ok(STR(queue_name)) == 0) { msg_warn("malformed queue name: %s", printable(STR(queue_name), '?')); return (-1); } if (mail_queue_id_ok(STR(queue_id)) == 0) { msg_warn("malformed queue id: %s", printable(STR(queue_id), '?')); return (-1); } VS_NEUTER(encoding); VS_NEUTER(sender); VS_NEUTER(dsn_envid); VS_NEUTER(verp_delims); if (strlen(STR(verp_delims)) != 2) { msg_warn("malformed verp delimiter string: %s", STR(verp_delims)); return (-1); } if (msg_verbose) msg_info("%s: flags=0x%x service=%s queue=%s id=%s encoding=%s smtputf8=%d sender=%s envid=%s ret=0x%x delim=%s", myname, flags, service_name, STR(queue_name), STR(queue_id), STR(encoding), smtputf8, STR(sender), STR(dsn_envid), dsn_ret, STR(verp_delims)); /* * On request by the client, set up a trap to delete the log file in case * of errors. */ if (flags & BOUNCE_FLAG_CLEAN) bounce_cleanup_register(service_name, STR(queue_id)); /* * Execute the request. Fall back to traditional notification if a bounce * was returned as undeliverable, because we don't want to VERPify those. */ if (!*STR(sender) || !strcasecmp_utf8(STR(sender), mail_addr_double_bounce())) { msg_warn("request to send VERP-style notification of bounced mail"); return (bounce_notify_service(flags, service_name, STR(queue_name), STR(queue_id), STR(encoding), smtputf8, STR(sender), STR(dsn_envid), dsn_ret, bounce_templates)); } else return (bounce_notify_verp(flags, service_name, STR(queue_name), STR(queue_id), STR(encoding), smtputf8, STR(sender), STR(dsn_envid), dsn_ret, STR(verp_delims), bounce_templates)); } /* bounce_one_proto - bounce_one server protocol */ static int bounce_one_proto(char *service_name, VSTREAM *client) { const char *myname = "bounce_one_proto"; int flags; int smtputf8; int dsn_ret; /* * Read and validate the client request. */ if (mail_command_server(client, RECV_ATTR_INT(MAIL_ATTR_FLAGS, &flags), RECV_ATTR_STR(MAIL_ATTR_QUEUE, queue_name), RECV_ATTR_STR(MAIL_ATTR_QUEUEID, queue_id), RECV_ATTR_STR(MAIL_ATTR_ENCODING, encoding), RECV_ATTR_INT(MAIL_ATTR_SMTPUTF8, &smtputf8), RECV_ATTR_STR(MAIL_ATTR_SENDER, sender), RECV_ATTR_STR(MAIL_ATTR_DSN_ENVID, dsn_envid), RECV_ATTR_INT(MAIL_ATTR_DSN_RET, &dsn_ret), RECV_ATTR_FUNC(rcpb_scan, (void *) rcpt_buf), RECV_ATTR_FUNC(dsb_scan, (void *) dsn_buf), ATTR_TYPE_END) != 10) { msg_warn("malformed request"); return (-1); } /* * Sanitize input. */ if (strcmp(service_name, MAIL_SERVICE_BOUNCE) != 0) { msg_warn("wrong service name \"%s\" for one-recipient bouncing", service_name); return (-1); } if (mail_queue_name_ok(STR(queue_name)) == 0) { msg_warn("malformed queue name: %s", printable(STR(queue_name), '?')); return (-1); } if (mail_queue_id_ok(STR(queue_id)) == 0) { msg_warn("malformed queue id: %s", printable(STR(queue_id), '?')); return (-1); } VS_NEUTER(encoding); VS_NEUTER(sender); VS_NEUTER(dsn_envid); VS_NEUTER(rcpt_buf->address); VS_NEUTER(rcpt_buf->orig_addr); VS_NEUTER(rcpt_buf->dsn_orcpt); VS_NEUTER(dsn_buf->status); VS_NEUTER(dsn_buf->action); VS_NEUTER(dsn_buf->reason); VS_NEUTER(dsn_buf->dtype); VS_NEUTER(dsn_buf->dtext); VS_NEUTER(dsn_buf->mtype); VS_NEUTER(dsn_buf->mname); (void) RECIPIENT_FROM_RCPT_BUF(rcpt_buf); (void) DSN_FROM_DSN_BUF(dsn_buf); /* * Beware: some DSN or RECIPIENT fields may be null; access dsn_buf and * rcpt_buf buffers instead. See DSN_FROM_DSN_BUF() and * RECIPIENT_FROM_RCPT_BUF(). */ if (msg_verbose) msg_info("%s: flags=0x%x queue=%s id=%s encoding=%s smtputf8=%d sender=%s envid=%s dsn_ret=0x%x orig_to=%s to=%s off=%ld dsn_orig=%s notif=0x%x stat=%s act=%s why=%s", myname, flags, STR(queue_name), STR(queue_id), STR(encoding), smtputf8, STR(sender), STR(dsn_envid), dsn_ret, STR(rcpt_buf->orig_addr), STR(rcpt_buf->address), rcpt_buf->offset, STR(rcpt_buf->dsn_orcpt), rcpt_buf->dsn_notify, STR(dsn_buf->status), STR(dsn_buf->action), STR(dsn_buf->reason)); /* * Execute the request. */ return (bounce_one_service(flags, STR(queue_name), STR(queue_id), STR(encoding), smtputf8, STR(sender), STR(dsn_envid), dsn_ret, rcpt_buf, dsn_buf, bounce_templates)); } /* bounce_service - parse bounce command type and delegate */ static void bounce_service(VSTREAM *client, char *service_name, char **argv) { int command; int status; /* * Sanity check. This service takes no command-line arguments. The * service name should be usable as a subdirectory name. */ if (argv[0]) msg_fatal("unexpected command-line argument: %s", argv[0]); if (mail_queue_name_ok(service_name) == 0) msg_fatal("malformed service name: %s", service_name); /* * Announce the protocol. */ attr_print(client, ATTR_FLAG_NONE, SEND_ATTR_STR(MAIL_ATTR_PROTO, MAIL_ATTR_PROTO_BOUNCE), ATTR_TYPE_END); (void) vstream_fflush(client); /* * Read and validate the first parameter of the client request. Let the * request-specific protocol routines take care of the remainder. */ if (attr_scan(client, ATTR_FLAG_STRICT | ATTR_FLAG_MORE, RECV_ATTR_INT(MAIL_ATTR_NREQ, &command), 0) != 1) { msg_warn("malformed request"); status = -1; } else if (command == BOUNCE_CMD_VERP) { status = bounce_verp_proto(service_name, client); } else if (command == BOUNCE_CMD_FLUSH) { status = bounce_notify_proto(service_name, client, bounce_notify_service); } else if (command == BOUNCE_CMD_WARN) { status = bounce_notify_proto(service_name, client, bounce_warn_service); } else if (command == BOUNCE_CMD_TRACE) { status = bounce_notify_proto(service_name, client, bounce_trace_service); } else if (command == BOUNCE_CMD_APPEND) { status = bounce_append_proto(service_name, client); } else if (command == BOUNCE_CMD_ONE) { status = bounce_one_proto(service_name, client); } else { msg_warn("unknown command: %d", command); status = -1; } /* * When the request has completed, send the completion status to the * client. */ attr_print(client, ATTR_FLAG_NONE, SEND_ATTR_INT(MAIL_ATTR_STATUS, status), ATTR_TYPE_END); vstream_fflush(client); /* * When a cleanup trap was set, delete the log file in case of error. * This includes errors while sending the completion status to the * client. */ if (bounce_cleanup_path) { if (status || vstream_ferror(client)) bounce_cleanup_log(); bounce_cleanup_unregister(); } } static void load_helper(VSTREAM *stream, void *context) { BOUNCE_TEMPLATES *templates = (BOUNCE_TEMPLATES *) context; bounce_templates_load(stream, templates); } /* pre_jail_init - pre-jail initialization */ static void pre_jail_init(char *unused_name, char **unused_argv) { /* * Bundle up a bunch of bounce template information. */ bounce_templates = bounce_templates_create(); /* * Load the alternate message files (if specified) before entering the * chroot jail. */ if (*var_bounce_tmpl) load_file(var_bounce_tmpl, load_helper, (void *) bounce_templates); } /* post_jail_init - initialize after entering chroot jail */ static void post_jail_init(char *service_name, char **unused_argv) { bounce_hfrom_format = hfrom_format_parse(VAR_HFROM_FORMAT, var_hfrom_format); /* * Special case: dump bounce templates. This is not part of the master(5) * public interface. This internal interface is used by the postconf * command. It was implemented before bounce templates were isolated into * modules that could have been called directly. */ if (strcmp(service_name, "dump_templates") == 0) { bounce_templates_dump(VSTREAM_OUT, bounce_templates); vstream_fflush(VSTREAM_OUT); exit(0); } if (strcmp(service_name, "expand_templates") == 0) { bounce_templates_expand(VSTREAM_OUT, bounce_templates); vstream_fflush(VSTREAM_OUT); exit(0); } /* * Initialize. We're single threaded so we can reuse some memory upon * successive requests. */ queue_id = vstring_alloc(10); queue_name = vstring_alloc(10); rcpt_buf = rcpb_create(); encoding = vstring_alloc(10); sender = vstring_alloc(10); dsn_envid = vstring_alloc(10); verp_delims = vstring_alloc(10); dsn_buf = dsb_create(); } MAIL_VERSION_STAMP_DECLARE; /* main - the main program */ int main(int argc, char **argv) { static const CONFIG_INT_TABLE int_table[] = { VAR_BOUNCE_LIMIT, DEF_BOUNCE_LIMIT, &var_bounce_limit, 1, 0, 0, }; static const CONFIG_TIME_TABLE time_table[] = { VAR_MAX_QUEUE_TIME, DEF_MAX_QUEUE_TIME, &var_max_queue_time, 0, 8640000, VAR_DELAY_WARN_TIME, DEF_DELAY_WARN_TIME, &var_delay_warn_time, 0, 0, 0, }; static const CONFIG_STR_TABLE str_table[] = { VAR_NOTIFY_CLASSES, DEF_NOTIFY_CLASSES, &var_notify_classes, 0, 0, VAR_BOUNCE_RCPT, DEF_BOUNCE_RCPT, &var_bounce_rcpt, 1, 0, VAR_2BOUNCE_RCPT, DEF_2BOUNCE_RCPT, &var_2bounce_rcpt, 1, 0, VAR_DELAY_RCPT, DEF_DELAY_RCPT, &var_delay_rcpt, 1, 0, VAR_BOUNCE_TMPL, DEF_BOUNCE_TMPL, &var_bounce_tmpl, 0, 0, VAR_HFROM_FORMAT, DEF_HFROM_FORMAT, &var_hfrom_format, 1, 0, 0, }; static const CONFIG_NBOOL_TABLE nbool_table[] = { VAR_THREADED_BOUNCE, DEF_THREADED_BOUNCE, &var_threaded_bounce, 0, }; /* * Fingerprint executables and core dumps. */ MAIL_VERSION_STAMP_ALLOCATE; /* * Pass control to the single-threaded service skeleton. */ single_server_main(argc, argv, bounce_service, CA_MAIL_SERVER_INT_TABLE(int_table), CA_MAIL_SERVER_STR_TABLE(str_table), CA_MAIL_SERVER_TIME_TABLE(time_table), CA_MAIL_SERVER_NBOOL_TABLE(nbool_table), CA_MAIL_SERVER_PRE_INIT(pre_jail_init), CA_MAIL_SERVER_POST_INIT(post_jail_init), CA_MAIL_SERVER_UNLIMITED, 0); }