Home | History | Annotate | Line # | Download | only in trivial-rewrite
      1 /*	$NetBSD: trivial-rewrite.c,v 1.6 2026/05/09 18:49:21 christos Exp $	*/
      2 
      3 /*++
      4 /* NAME
      5 /*	trivial-rewrite 8
      6 /* SUMMARY
      7 /*	Postfix address rewriting and resolving daemon
      8 /* SYNOPSIS
      9 /*	\fBtrivial-rewrite\fR [generic Postfix daemon options]
     10 /* DESCRIPTION
     11 /*	The \fBtrivial-rewrite\fR(8) daemon processes three types of client
     12 /*	service requests:
     13 /* .IP "\fBrewrite \fIcontext address\fR"
     14 /*	Rewrite an address to standard form, according to the
     15 /*	address rewriting context:
     16 /* .RS
     17 /* .IP \fBlocal\fR
     18 /*	Append the domain names specified with \fB$myorigin\fR or
     19 /*	\fB$mydomain\fR to incomplete addresses; do \fBswap_bangpath\fR
     20 /*	and \fBallow_percent_hack\fR processing as described below, and
     21 /*	strip source routed addresses (\fI@site,@site:user@domain\fR)
     22 /*	to \fIuser@domain\fR form.
     23 /* .IP \fBremote\fR
     24 /*	Append the domain name specified with
     25 /*	\fB$remote_header_rewrite_domain\fR to incomplete
     26 /*	addresses. Otherwise the result is identical to that of
     27 /*	the \fBlocal\fR address rewriting context. This prevents
     28 /*	Postfix from appending the local domain to spam from poorly
     29 /*	written remote clients.
     30 /* .RE
     31 /* .IP "\fBresolve \fIsender\fR \fIaddress\fR"
     32 /*	Resolve the address to a (\fItransport\fR, \fInexthop\fR,
     33 /*	\fIrecipient\fR, \fIflags\fR) quadruple. The meaning of
     34 /*	the results is as follows:
     35 /* .RS
     36 /* .IP \fItransport\fR
     37 /*	The delivery agent to use. This is the first field of an entry
     38 /*	in the \fBmaster.cf\fR file.
     39 /* .IP \fInexthop\fR
     40 /*	The host to send to and optional delivery method information.
     41 /* .IP \fIrecipient\fR
     42 /*	The envelope recipient address that is passed on to \fInexthop\fR.
     43 /* .IP \fIflags\fR
     44 /*	The address class, whether the address requires relaying,
     45 /*	whether the address has problems, and whether the request failed.
     46 /* .RE
     47 /* .IP "\fBverify \fIsender\fR \fIaddress\fR"
     48 /*	Resolve the address for address verification purposes.
     49 /* SERVER PROCESS MANAGEMENT
     50 /* .ad
     51 /* .fi
     52 /*	The \fBtrivial-rewrite\fR(8) servers run under control by
     53 /*	the Postfix master(8)
     54 /*	server.  Each server can handle multiple simultaneous connections.
     55 /*	When all servers are busy while a client connects, the master
     56 /*	creates a new server process, provided that the trivial-rewrite
     57 /*	server process limit is not exceeded.
     58 /*	Each trivial-rewrite server terminates after
     59 /*	serving at least \fB$max_use\fR clients of after \fB$max_idle\fR
     60 /*	seconds of idle time.
     61 /* STANDARDS
     62 /* .ad
     63 /* .fi
     64 /*	None. The command does not interact with the outside world.
     65 /* SECURITY
     66 /* .ad
     67 /* .fi
     68 /*	The \fBtrivial-rewrite\fR(8) daemon is not security sensitive.
     69 /*	By default, this daemon does not talk to remote or local users.
     70 /*	It can run at a fixed low privilege in a chrooted environment.
     71 /* DIAGNOSTICS
     72 /*	Problems and transactions are logged to \fBsyslogd\fR(8)
     73 /*	or \fBpostlogd\fR(8).
     74 /* CONFIGURATION PARAMETERS
     75 /* .ad
     76 /* .fi
     77 /*	On busy mail systems a long time may pass before a \fBmain.cf\fR
     78 /*	change affecting \fBtrivial-rewrite\fR(8) is picked up. Use the command
     79 /*	"\fBpostfix reload\fR" to speed up a change.
     80 /*
     81 /*	The text below provides only a parameter summary. See
     82 /*	\fBpostconf\fR(5) for more details including examples.
     83 /* COMPATIBILITY CONTROLS
     84 /* .ad
     85 /* .fi
     86 /* .IP "\fBresolve_dequoted_address (yes)\fR"
     87 /*	Resolve a recipient address safely instead of correctly, by
     88 /*	looking inside quotes.
     89 /* .PP
     90 /*	Available with Postfix version 2.1 and later:
     91 /* .IP "\fBresolve_null_domain (no)\fR"
     92 /*	Resolve an address that ends in the "@" null domain as if the
     93 /*	local hostname were specified, instead of rejecting the address as
     94 /*	invalid.
     95 /* .PP
     96 /*	Available with Postfix version 2.3 and later:
     97 /* .IP "\fBresolve_numeric_domain (no)\fR"
     98 /*	Resolve "user@ipaddress" as "user@[ipaddress]", instead of
     99 /*	rejecting the address as invalid.
    100 /* .PP
    101 /*	Available with Postfix version 2.5 and later:
    102 /* .IP "\fBallow_min_user (no)\fR"
    103 /*	Allow a sender or recipient address to have `-' as the first
    104 /*	character.
    105 /* ADDRESS REWRITING CONTROLS
    106 /* .ad
    107 /* .fi
    108 /* .IP "\fBmyorigin ($myhostname)\fR"
    109 /*	The domain name that locally-posted mail appears to come
    110 /*	from, and that locally posted mail is delivered to.
    111 /* .IP "\fBallow_percent_hack (yes)\fR"
    112 /*	Enable the rewriting of the form "user%domain" to "user@domain".
    113 /* .IP "\fBappend_at_myorigin (yes)\fR"
    114 /*	With locally submitted mail, append the string "@$myorigin" to mail
    115 /*	addresses without domain information.
    116 /* .IP "\fBappend_dot_mydomain (Postfix >= 3.0: no, Postfix < 3.0: yes)\fR"
    117 /*	With locally submitted mail, append the string ".$mydomain" to
    118 /*	addresses that have no ".domain" information.
    119 /* .IP "\fBrecipient_delimiter (empty)\fR"
    120 /*	The set of characters that can separate an email address
    121 /*	localpart, user name, or a .forward file name from its extension.
    122 /* .IP "\fBswap_bangpath (yes)\fR"
    123 /*	Enable the rewriting of "site!user" into "user@site".
    124 /* .PP
    125 /*	Available in Postfix 2.2 and later:
    126 /* .IP "\fBremote_header_rewrite_domain (empty)\fR"
    127 /*	Rewrite or add message headers in mail from remote clients if
    128 /*	the remote_header_rewrite_domain parameter value is non-empty,
    129 /*	updating incomplete addresses with the domain specified in the
    130 /*	remote_header_rewrite_domain parameter, and adding missing headers.
    131 /* ROUTING CONTROLS
    132 /* .ad
    133 /* .fi
    134 /*	The following is applicable to Postfix version 2.0 and later.
    135 /*	Earlier versions do not have support for: virtual_transport,
    136 /*	relay_transport, virtual_alias_domains, virtual_mailbox_domains
    137 /*	or proxy_interfaces.
    138 /* .IP "\fBlocal_transport (local:$myhostname)\fR"
    139 /*	The default mail delivery transport and next-hop destination
    140 /*	for final delivery to domains listed with mydestination, and for
    141 /*	[ipaddress] destinations that match $inet_interfaces or $proxy_interfaces.
    142 /* .IP "\fBvirtual_transport (virtual)\fR"
    143 /*	The default mail delivery transport and next-hop destination for
    144 /*	final delivery to domains listed with $virtual_mailbox_domains.
    145 /* .IP "\fBrelay_transport (relay)\fR"
    146 /*	The default mail delivery transport and next-hop destination for
    147 /*	the relay domain address class: recipient domains that match
    148 /*	$relay_domains.
    149 /* .IP "\fBdefault_transport (smtp)\fR"
    150 /*	The default mail delivery transport and next-hop destination for
    151 /*	the default domain class: recipient domains that do not match
    152 /*	$mydestination, $inet_interfaces,
    153 /*	$proxy_interfaces, $virtual_alias_domains, $virtual_mailbox_domains,
    154 /*	or $relay_domains.
    155 /* .IP "\fBparent_domain_matches_subdomains (see 'postconf -d' output)\fR"
    156 /*	A list of Postfix features where the pattern "example.com" also
    157 /*	matches subdomains of example.com,
    158 /*	instead of requiring an explicit ".example.com" pattern.
    159 /* .IP "\fBrelayhost (empty)\fR"
    160 /*	The next-hop destination(s) for non-local mail; takes precedence
    161 /*	over non-local domains in recipient addresses.
    162 /* .IP "\fBtransport_maps (empty)\fR"
    163 /*	Optional lookup tables with mappings from recipient address to
    164 /*	(message delivery transport, next-hop destination).
    165 /* .PP
    166 /*	Available in Postfix version 2.3 and later:
    167 /* .IP "\fBsender_dependent_relayhost_maps (empty)\fR"
    168 /*	A sender-dependent override for the global relayhost parameter
    169 /*	setting.
    170 /* .PP
    171 /*	Available in Postfix version 2.5 and later:
    172 /* .IP "\fBempty_address_relayhost_maps_lookup_key (<>)\fR"
    173 /*	The sender_dependent_relayhost_maps search string that will be
    174 /*	used instead of the null sender address.
    175 /* .PP
    176 /*	Available in Postfix version 2.7 and later:
    177 /* .IP "\fBempty_address_default_transport_maps_lookup_key (<>)\fR"
    178 /*	The sender_dependent_default_transport_maps search string that
    179 /*	will be used instead of the null sender address.
    180 /* .IP "\fBsender_dependent_default_transport_maps (empty)\fR"
    181 /*	A sender-dependent override for the global default_transport
    182 /*	parameter setting.
    183 /* ADDRESS VERIFICATION CONTROLS
    184 /* .ad
    185 /* .fi
    186 /*	Postfix version 2.1 introduces sender and recipient address verification.
    187 /*	This feature is implemented by sending probe email messages that
    188 /*	are not actually delivered.
    189 /*	By default, address verification probes use the same route
    190 /*	as regular mail. To override specific aspects of message
    191 /*	routing for address verification probes, specify one or more
    192 /*	of the following:
    193 /* .IP "\fBaddress_verify_local_transport ($local_transport)\fR"
    194 /*	Overrides the local_transport parameter setting for address
    195 /*	verification probes.
    196 /* .IP "\fBaddress_verify_virtual_transport ($virtual_transport)\fR"
    197 /*	Overrides the virtual_transport parameter setting for address
    198 /*	verification probes.
    199 /* .IP "\fBaddress_verify_relay_transport ($relay_transport)\fR"
    200 /*	Overrides the relay_transport parameter setting for address
    201 /*	verification probes.
    202 /* .IP "\fBaddress_verify_default_transport ($default_transport)\fR"
    203 /*	Overrides the default_transport parameter setting for address
    204 /*	verification probes.
    205 /* .IP "\fBaddress_verify_relayhost ($relayhost)\fR"
    206 /*	Overrides the relayhost parameter setting for address verification
    207 /*	probes.
    208 /* .IP "\fBaddress_verify_transport_maps ($transport_maps)\fR"
    209 /*	Overrides the transport_maps parameter setting for address verification
    210 /*	probes.
    211 /* .PP
    212 /*	Available in Postfix version 2.3 and later:
    213 /* .IP "\fBaddress_verify_sender_dependent_relayhost_maps ($sender_dependent_relayhost_maps)\fR"
    214 /*	Overrides the sender_dependent_relayhost_maps parameter setting for address
    215 /*	verification probes.
    216 /* .PP
    217 /*	Available in Postfix version 2.7 and later:
    218 /* .IP "\fBaddress_verify_sender_dependent_default_transport_maps ($sender_dependent_default_transport_maps)\fR"
    219 /*	Overrides the sender_dependent_default_transport_maps parameter
    220 /*	setting for address verification probes.
    221 /* MISCELLANEOUS CONTROLS
    222 /* .ad
    223 /* .fi
    224 /* .IP "\fBconfig_directory (see 'postconf -d' output)\fR"
    225 /*	The default location of the Postfix main.cf and master.cf
    226 /*	configuration files.
    227 /* .IP "\fBdaemon_timeout (18000s)\fR"
    228 /*	How much time a Postfix daemon process may take to handle a
    229 /*	request before it is terminated by a built-in watchdog timer.
    230 /* .IP "\fBempty_address_recipient (MAILER-DAEMON)\fR"
    231 /*	The recipient of mail addressed to the null address.
    232 /* .IP "\fBipc_timeout (3600s)\fR"
    233 /*	The time limit for sending or receiving information over an internal
    234 /*	communication channel.
    235 /* .IP "\fBmax_idle (100s)\fR"
    236 /*	The maximum amount of time that an idle Postfix daemon process waits
    237 /*	for an incoming connection before terminating voluntarily.
    238 /* .IP "\fBmax_use (100)\fR"
    239 /*	The maximal number of incoming connections that a Postfix daemon
    240 /*	process will service before terminating voluntarily.
    241 /* .IP "\fBrelocated_maps (empty)\fR"
    242 /*	Optional lookup tables with new contact information for users or
    243 /*	domains that no longer exist.
    244 /* .IP "\fBprocess_id (read-only)\fR"
    245 /*	The process ID of a Postfix command or daemon process.
    246 /* .IP "\fBprocess_name (read-only)\fR"
    247 /*	The process name of a Postfix command or daemon process.
    248 /* .IP "\fBqueue_directory (see 'postconf -d' output)\fR"
    249 /*	The location of the Postfix top-level queue directory.
    250 /* .IP "\fBshow_user_unknown_table_name (yes)\fR"
    251 /*	Display the name of the recipient table in the "User unknown"
    252 /*	responses.
    253 /* .IP "\fBsyslog_facility (mail)\fR"
    254 /*	The syslog facility of Postfix logging.
    255 /* .IP "\fBsyslog_name (see 'postconf -d' output)\fR"
    256 /*	A prefix that is prepended to the process name in syslog
    257 /*	records, so that, for example, "smtpd" becomes "prefix/smtpd".
    258 /* .PP
    259 /*	Available in Postfix version 2.0 and later:
    260 /* .IP "\fBhelpful_warnings (yes)\fR"
    261 /*	Log warnings about problematic configuration settings, and provide
    262 /*	helpful suggestions.
    263 /* .PP
    264 /*	Available in Postfix 3.3 and later:
    265 /* .IP "\fBservice_name (read-only)\fR"
    266 /*	The master.cf service name of a Postfix daemon process.
    267 /* SEE ALSO
    268 /*	postconf(5), configuration parameters
    269 /*	transport(5), transport table format
    270 /*	relocated(5), format of the "user has moved" table
    271 /*	master(8), process manager
    272 /*	postlogd(8), Postfix logging
    273 /*	syslogd(8), system logging
    274 /* README FILES
    275 /* .ad
    276 /* .fi
    277 /*	Use "\fBpostconf readme_directory\fR" or
    278 /*	"\fBpostconf html_directory\fR" to locate this information.
    279 /* .na
    280 /* .nf
    281 /*	ADDRESS_CLASS_README, Postfix address classes howto
    282 /*	ADDRESS_VERIFICATION_README, Postfix address verification
    283 /* LICENSE
    284 /* .ad
    285 /* .fi
    286 /*	The Secure Mailer license must be distributed with this software.
    287 /* AUTHOR(S)
    288 /*	Wietse Venema
    289 /*	IBM T.J. Watson Research
    290 /*	P.O. Box 704
    291 /*	Yorktown Heights, NY 10598, USA
    292 /*
    293 /*	Wietse Venema
    294 /*	Google, Inc.
    295 /*	111 8th Avenue
    296 /*	New York, NY 10011, USA
    297 /*--*/
    298 
    299 /* System library. */
    300 
    301 #include <sys_defs.h>
    302 #include <unistd.h>
    303 #include <stdlib.h>
    304 #include <string.h>
    305 
    306 /* Utility library. */
    307 
    308 #include <msg.h>
    309 #include <vstring.h>
    310 #include <vstream.h>
    311 #include <vstring_vstream.h>
    312 #include <split_at.h>
    313 #include <stringops.h>
    314 #include <dict.h>
    315 #include <events.h>
    316 
    317 /* Global library. */
    318 
    319 #include <mail_params.h>
    320 #include <mail_version.h>
    321 #include <mail_proto.h>
    322 #include <resolve_local.h>
    323 #include <mail_conf.h>
    324 #include <resolve_clnt.h>
    325 #include <rewrite_clnt.h>
    326 #include <tok822.h>
    327 #include <mail_addr.h>
    328 
    329 /* Multi server skeleton. */
    330 
    331 #include <mail_server.h>
    332 
    333 /* Application-specific. */
    334 
    335 #include <trivial-rewrite.h>
    336 #include <transport.h>
    337 
    338 static VSTRING *command;
    339 
    340  /*
    341   * Tunable parameters.
    342   */
    343 char   *var_transport_maps;
    344 bool    var_swap_bangpath;
    345 bool    var_append_dot_mydomain;
    346 bool    var_append_at_myorigin;
    347 bool    var_percent_hack;
    348 char   *var_local_transport;
    349 char   *var_virt_transport;
    350 char   *var_relay_transport;
    351 bool    var_resolve_dequoted;
    352 char   *var_virt_alias_maps;		/* XXX virtual_alias_domains */
    353 char   *var_virt_mailbox_maps;		/* XXX virtual_mailbox_domains */
    354 char   *var_virt_alias_doms;
    355 char   *var_virt_mailbox_doms;
    356 char   *var_relocated_maps;
    357 bool    var_enb_relocated_pfx;
    358 char   *var_def_transport;
    359 char   *var_snd_def_xport_maps;
    360 char   *var_empty_addr;
    361 bool    var_show_unk_rcpt_table;
    362 bool    var_resolve_nulldom;
    363 char   *var_remote_rwr_domain;
    364 char   *var_snd_relay_maps;
    365 char   *var_null_relay_maps_key;
    366 char   *var_null_def_xport_maps_key;
    367 bool    var_resolve_num_dom;
    368 bool    var_allow_min_user;
    369 
    370  /*
    371   * Shadow personality for address verification.
    372   */
    373 char   *var_vrfy_xport_maps;
    374 char   *var_vrfy_local_xport;
    375 char   *var_vrfy_virt_xport;
    376 char   *var_vrfy_relay_xport;
    377 char   *var_vrfy_def_xport;
    378 char   *var_vrfy_snd_def_xport_maps;
    379 char   *var_vrfy_relayhost;
    380 char   *var_vrfy_relay_maps;
    381 
    382  /*
    383   * Different resolver personalities depending on the kind of request.
    384   */
    385 RES_CONTEXT resolve_regular = {
    386     VAR_LOCAL_TRANSPORT, &var_local_transport,
    387     VAR_VIRT_TRANSPORT, &var_virt_transport,
    388     VAR_RELAY_TRANSPORT, &var_relay_transport,
    389     VAR_DEF_TRANSPORT, &var_def_transport,
    390     VAR_SND_DEF_XPORT_MAPS, &var_snd_def_xport_maps, 0,
    391     VAR_RELAYHOST, &var_relayhost,
    392     VAR_SND_RELAY_MAPS, &var_snd_relay_maps, 0,
    393     VAR_TRANSPORT_MAPS, &var_transport_maps, 0
    394 };
    395 
    396 RES_CONTEXT resolve_verify = {
    397     VAR_VRFY_LOCAL_XPORT, &var_vrfy_local_xport,
    398     VAR_VRFY_VIRT_XPORT, &var_vrfy_virt_xport,
    399     VAR_VRFY_RELAY_XPORT, &var_vrfy_relay_xport,
    400     VAR_VRFY_DEF_XPORT, &var_vrfy_def_xport,
    401     VAR_VRFY_SND_DEF_XPORT_MAPS, &var_vrfy_snd_def_xport_maps, 0,
    402     VAR_VRFY_RELAYHOST, &var_vrfy_relayhost,
    403     VAR_VRFY_RELAY_MAPS, &var_vrfy_relay_maps, 0,
    404     VAR_VRFY_XPORT_MAPS, &var_vrfy_xport_maps, 0
    405 };
    406 
    407  /*
    408   * Connection management. When file-based lookup tables change we should
    409   * restart at our convenience, but avoid client read errors. We restart
    410   * rather than reopen, because the process may be chrooted (and if it isn't
    411   * we still need code that handles the chrooted case anyway).
    412   *
    413   * Three variants are implemented. Only one should be used.
    414   *
    415   * ifdef DETACH_AND_ASK_CLIENTS_TO_RECONNECT
    416   *
    417   * This code detaches the trivial-rewrite process from the master, stops
    418   * accepting new clients, and handles established clients in the background,
    419   * asking them to reconnect the next time they send a request. The master
    420   * creates a new process that accepts connections. This is reasonably safe
    421   * because the number of trivial-rewrite server processes is small compared
    422   * to the number of trivial-rewrite client processes. The few extra
    423   * background processes should not make a difference in Postfix's footprint.
    424   * However, once a daemon detaches from the master, its exit status will be
    425   * lost, and abnormal termination may remain undetected. Timely restart is
    426   * achieved by checking the table changed status every 10 seconds or so
    427   * before responding to a client request.
    428   *
    429   * ifdef CHECK_TABLE_STATS_PERIODICALLY
    430   *
    431   * This code runs every 10 seconds and terminates the process when lookup
    432   * tables have changed. This is subject to race conditions when established
    433   * clients send a request while the server exits; those clients may read EOF
    434   * instead of a server reply. If the experience with the oldest option
    435   * (below) is anything to go by, however, then this is unlikely to be a
    436   * problem during real deployment.
    437   *
    438   * ifdef CHECK_TABLE_STATS_BEFORE_ACCEPT
    439   *
    440   * This is the old code. It checks the table changed status when a new client
    441   * connects (i.e. before the server calls accept()), and terminates
    442   * immediately. This is invisible for the connecting client, but is subject
    443   * to race conditions when established clients send a request while the
    444   * server exits; those clients may read EOF instead of a server reply. This
    445   * has, however, not been a problem in real deployment. With the old code,
    446   * timely restart is achieved by setting the ipc_ttl parameter to 60
    447   * seconds, so that the table change status is checked several times a
    448   * minute.
    449   */
    450 int     server_flags;
    451 
    452  /*
    453   * Define exactly one of these.
    454   */
    455 /* #define DETACH_AND_ASK_CLIENTS_TO_RECONNECT	/* correct and complex */
    456 #define CHECK_TABLE_STATS_PERIODICALLY	/* quick */
    457 /* #define CHECK_TABLE_STATS_BEFORE_ACCEPT	/* slow */
    458 
    459 /* rewrite_service - read request and send reply */
    460 
    461 static void rewrite_service(VSTREAM *stream, char *unused_service, char **argv)
    462 {
    463     int     status = -1;
    464 
    465 #ifdef DETACH_AND_ASK_CLIENTS_TO_RECONNECT
    466     static time_t last;
    467     time_t  now;
    468     const char *table;
    469 
    470 #endif
    471 
    472     /*
    473      * Sanity check. This service takes no command-line arguments.
    474      */
    475     if (argv[0])
    476 	msg_fatal("unexpected command-line argument: %s", argv[0]);
    477 
    478     /*
    479      * Client connections are long-lived. Be sure to refesh timely.
    480      */
    481 #ifdef DETACH_AND_ASK_CLIENTS_TO_RECONNECT
    482     if (server_flags == 0 && (now = event_time()) - last > 10) {
    483 	if ((table = dict_changed_name()) != 0) {
    484 	    msg_info("table %s has changed -- restarting", table);
    485 	    if (multi_server_drain() == 0)
    486 		server_flags = 1;
    487 	}
    488 	last = now;
    489     }
    490 #endif
    491 
    492     /*
    493      * This routine runs whenever a client connects to the UNIX-domain socket
    494      * dedicated to address rewriting. All connection-management stuff is
    495      * handled by the common code in multi_server.c.
    496      */
    497     if (attr_scan(stream, ATTR_FLAG_STRICT | ATTR_FLAG_MORE,
    498 		  RECV_ATTR_STR(MAIL_ATTR_REQ, command),
    499 		  ATTR_TYPE_END) == 1) {
    500 	if (strcmp(vstring_str(command), REWRITE_ADDR) == 0) {
    501 	    status = rewrite_proto(stream);
    502 	} else if (strcmp(vstring_str(command), RESOLVE_REGULAR) == 0) {
    503 	    status = resolve_proto(&resolve_regular, stream);
    504 	} else if (strcmp(vstring_str(command), RESOLVE_VERIFY) == 0) {
    505 	    status = resolve_proto(&resolve_verify, stream);
    506 	} else {
    507 	    msg_warn("bad command %.30s", printable(vstring_str(command), '?'));
    508 	}
    509     }
    510     if (status < 0)
    511 	multi_server_disconnect(stream);
    512 }
    513 
    514 /* pre_accept - see if tables have changed */
    515 
    516 #ifdef CHECK_TABLE_STATS_BEFORE_ACCEPT
    517 
    518 static void pre_accept(char *unused_name, char **unused_argv)
    519 {
    520     const char *table;
    521 
    522     if ((table = dict_changed_name()) != 0) {
    523 	msg_info("table %s has changed -- restarting", table);
    524 	exit(0);
    525     }
    526 }
    527 
    528 #endif
    529 
    530 /* post_accept - announce our protocol name */
    531 
    532 static void post_accept(VSTREAM *stream, char *unused_name, char **unused_argv,
    533 			        HTABLE *unused_attr)
    534 {
    535 
    536     /*
    537      * Announce the protocol.
    538      */
    539     attr_print(stream, ATTR_FLAG_NONE,
    540 	       SEND_ATTR_STR(MAIL_ATTR_PROTO, MAIL_ATTR_PROTO_TRIVIAL),
    541 	       ATTR_TYPE_END);
    542     (void) vstream_fflush(stream);
    543 }
    544 
    545 static void check_table_stats(int unused_event, void *unused_context)
    546 {
    547     const char *table;
    548 
    549     if ((table = dict_changed_name()) != 0) {
    550 	msg_info("table %s has changed -- restarting", table);
    551 	exit(0);
    552     }
    553     event_request_timer(check_table_stats, (void *) 0, 10);
    554 }
    555 
    556 /* pre_jail_init - initialize before entering chroot jail */
    557 
    558 static void pre_jail_init(char *unused_name, char **unused_argv)
    559 {
    560     command = vstring_alloc(100);
    561     rewrite_init();
    562     resolve_init();
    563     if (*RES_PARAM_VALUE(resolve_regular.transport_maps))
    564 	resolve_regular.transport_info =
    565 	    transport_pre_init(resolve_regular.transport_maps_name,
    566 			   RES_PARAM_VALUE(resolve_regular.transport_maps));
    567     if (*RES_PARAM_VALUE(resolve_verify.transport_maps))
    568 	resolve_verify.transport_info =
    569 	    transport_pre_init(resolve_verify.transport_maps_name,
    570 			    RES_PARAM_VALUE(resolve_verify.transport_maps));
    571     if (*RES_PARAM_VALUE(resolve_regular.snd_relay_maps))
    572 	resolve_regular.snd_relay_info =
    573 	    maps_create(resolve_regular.snd_relay_maps_name,
    574 			RES_PARAM_VALUE(resolve_regular.snd_relay_maps),
    575 			DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX
    576 			| DICT_FLAG_NO_REGSUB | DICT_FLAG_UTF8_REQUEST);
    577     if (*RES_PARAM_VALUE(resolve_verify.snd_relay_maps))
    578 	resolve_verify.snd_relay_info =
    579 	    maps_create(resolve_verify.snd_relay_maps_name,
    580 			RES_PARAM_VALUE(resolve_verify.snd_relay_maps),
    581 			DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX
    582 			| DICT_FLAG_NO_REGSUB | DICT_FLAG_UTF8_REQUEST);
    583     if (*RES_PARAM_VALUE(resolve_regular.snd_def_xp_maps))
    584 	resolve_regular.snd_def_xp_info =
    585 	    maps_create(resolve_regular.snd_def_xp_maps_name,
    586 			RES_PARAM_VALUE(resolve_regular.snd_def_xp_maps),
    587 			DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX
    588 			| DICT_FLAG_NO_REGSUB | DICT_FLAG_UTF8_REQUEST);
    589     if (*RES_PARAM_VALUE(resolve_verify.snd_def_xp_maps))
    590 	resolve_verify.snd_def_xp_info =
    591 	    maps_create(resolve_verify.snd_def_xp_maps_name,
    592 			RES_PARAM_VALUE(resolve_verify.snd_def_xp_maps),
    593 			DICT_FLAG_LOCK | DICT_FLAG_FOLD_FIX
    594 			| DICT_FLAG_NO_REGSUB | DICT_FLAG_UTF8_REQUEST);
    595 }
    596 
    597 /* post_jail_init - initialize after entering chroot jail */
    598 
    599 static void post_jail_init(char *unused_name, char **unused_argv)
    600 {
    601     if (resolve_regular.transport_info)
    602 	transport_post_init(resolve_regular.transport_info);
    603     if (resolve_verify.transport_info)
    604 	transport_post_init(resolve_verify.transport_info);
    605     check_table_stats(0, (void *) 0);
    606 }
    607 
    608 MAIL_VERSION_STAMP_DECLARE;
    609 
    610 /* main - pass control to the multi-threaded skeleton code */
    611 
    612 int     main(int argc, char **argv)
    613 {
    614     static const CONFIG_STR_TABLE str_table[] = {
    615 	VAR_TRANSPORT_MAPS, DEF_TRANSPORT_MAPS, &var_transport_maps, 0, 0,
    616 	VAR_LOCAL_TRANSPORT, DEF_LOCAL_TRANSPORT, &var_local_transport, 1, 0,
    617 	VAR_VIRT_TRANSPORT, DEF_VIRT_TRANSPORT, &var_virt_transport, 1, 0,
    618 	VAR_RELAY_TRANSPORT, DEF_RELAY_TRANSPORT, &var_relay_transport, 1, 0,
    619 	VAR_DEF_TRANSPORT, DEF_DEF_TRANSPORT, &var_def_transport, 1, 0,
    620 	VAR_VIRT_ALIAS_MAPS, DEF_VIRT_ALIAS_MAPS, &var_virt_alias_maps, 0, 0,
    621 	VAR_VIRT_ALIAS_DOMS, DEF_VIRT_ALIAS_DOMS, &var_virt_alias_doms, 0, 0,
    622 	VAR_VIRT_MAILBOX_MAPS, DEF_VIRT_MAILBOX_MAPS, &var_virt_mailbox_maps, 0, 0,
    623 	VAR_VIRT_MAILBOX_DOMS, DEF_VIRT_MAILBOX_DOMS, &var_virt_mailbox_doms, 0, 0,
    624 	VAR_RELOCATED_MAPS, DEF_RELOCATED_MAPS, &var_relocated_maps, 0, 0,
    625 	VAR_EMPTY_ADDR, DEF_EMPTY_ADDR, &var_empty_addr, 1, 0,
    626 	VAR_VRFY_XPORT_MAPS, DEF_VRFY_XPORT_MAPS, &var_vrfy_xport_maps, 0, 0,
    627 	VAR_VRFY_LOCAL_XPORT, DEF_VRFY_LOCAL_XPORT, &var_vrfy_local_xport, 1, 0,
    628 	VAR_VRFY_VIRT_XPORT, DEF_VRFY_VIRT_XPORT, &var_vrfy_virt_xport, 1, 0,
    629 	VAR_VRFY_RELAY_XPORT, DEF_VRFY_RELAY_XPORT, &var_vrfy_relay_xport, 1, 0,
    630 	VAR_VRFY_DEF_XPORT, DEF_VRFY_DEF_XPORT, &var_vrfy_def_xport, 1, 0,
    631 	VAR_VRFY_RELAYHOST, DEF_VRFY_RELAYHOST, &var_vrfy_relayhost, 0, 0,
    632 	VAR_REM_RWR_DOMAIN, DEF_REM_RWR_DOMAIN, &var_remote_rwr_domain, 0, 0,
    633 	VAR_SND_RELAY_MAPS, DEF_SND_RELAY_MAPS, &var_snd_relay_maps, 0, 0,
    634 	VAR_NULL_RELAY_MAPS_KEY, DEF_NULL_RELAY_MAPS_KEY, &var_null_relay_maps_key, 1, 0,
    635 	VAR_VRFY_RELAY_MAPS, DEF_VRFY_RELAY_MAPS, &var_vrfy_relay_maps, 0, 0,
    636 	VAR_SND_DEF_XPORT_MAPS, DEF_SND_DEF_XPORT_MAPS, &var_snd_def_xport_maps, 0, 0,
    637 	VAR_NULL_DEF_XPORT_MAPS_KEY, DEF_NULL_DEF_XPORT_MAPS_KEY, &var_null_def_xport_maps_key, 1, 0,
    638 	VAR_VRFY_SND_DEF_XPORT_MAPS, DEF_VRFY_SND_DEF_XPORT_MAPS, &var_vrfy_snd_def_xport_maps, 0, 0,
    639 	0,
    640     };
    641     static const CONFIG_BOOL_TABLE bool_table[] = {
    642 	VAR_SWAP_BANGPATH, DEF_SWAP_BANGPATH, &var_swap_bangpath,
    643 	VAR_APP_AT_MYORIGIN, DEF_APP_AT_MYORIGIN, &var_append_at_myorigin,
    644 	VAR_PERCENT_HACK, DEF_PERCENT_HACK, &var_percent_hack,
    645 	VAR_RESOLVE_DEQUOTED, DEF_RESOLVE_DEQUOTED, &var_resolve_dequoted,
    646 	VAR_SHOW_UNK_RCPT_TABLE, DEF_SHOW_UNK_RCPT_TABLE, &var_show_unk_rcpt_table,
    647 	VAR_RESOLVE_NULLDOM, DEF_RESOLVE_NULLDOM, &var_resolve_nulldom,
    648 	VAR_RESOLVE_NUM_DOM, DEF_RESOLVE_NUM_DOM, &var_resolve_num_dom,
    649 	VAR_ALLOW_MIN_USER, DEF_ALLOW_MIN_USER, &var_allow_min_user,
    650 	0,
    651     };
    652     static const CONFIG_NBOOL_TABLE nbool_table[] = {
    653 	VAR_APP_DOT_MYDOMAIN, DEF_APP_DOT_MYDOMAIN, &var_append_dot_mydomain,
    654 	VAR_ENB_RELOCATED_PFX, DEF_ENB_RELOCATED_PFX, &var_enb_relocated_pfx,
    655 	0,
    656     };
    657 
    658     /*
    659      * Fingerprint executables and core dumps.
    660      */
    661     MAIL_VERSION_STAMP_ALLOCATE;
    662 
    663     multi_server_main(argc, argv, rewrite_service,
    664 		      CA_MAIL_SERVER_STR_TABLE(str_table),
    665 		      CA_MAIL_SERVER_BOOL_TABLE(bool_table),
    666 		      CA_MAIL_SERVER_NBOOL_TABLE(nbool_table),
    667 		      CA_MAIL_SERVER_PRE_INIT(pre_jail_init),
    668 		      CA_MAIL_SERVER_POST_INIT(post_jail_init),
    669 #ifdef CHECK_TABLE_STATS_BEFORE_ACCEPT
    670 		      CA_MAIL_SERVER_PRE_ACCEPT(pre_accept),
    671 #endif
    672 		      CA_MAIL_SERVER_POST_ACCEPT(post_accept),
    673 		      0);
    674 }
    675