#include <sys_defs.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <time.h>
#include <errno.h>
#include <ctype.h>
#include <signal.h>
#include <stddef.h>
#ifdef STRCASECMP_IN_STRINGS_H
#include <strings.h>
#endif
#include <msg.h>
#include <mymalloc.h>
#include <vstring.h>
#include <vstream.h>
#include <vstring_vstream.h>
#include <stringops.h>
#include <events.h>
#include <smtp_stream.h>
#include <valid_hostname.h>
#include <dict.h>
#include <watchdog.h>
#include <iostuff.h>
#include <split_at.h>
#include <name_code.h>
#include <mail_params.h>
#include <record.h>
#include <rec_type.h>
#include <mail_proto.h>
#include <cleanup_user.h>
#include <mail_date.h>
#include <mail_conf.h>
#include <off_cvt.h>
#include <debug_peer.h>
#include <mail_error.h>
#include <flush_clnt.h>
#include <mail_stream.h>
#include <mail_queue.h>
#include <tok822.h>
#include <verp_sender.h>
#include <string_list.h>
#include <quote_822_local.h>
#include <lex_822.h>
#include <namadr_list.h>
#include <input_transp.h>
#ifdef SNAPSHOT
#include <anvil_clnt.h>
#endif
#include <flush_clnt.h>
#include <pfixtls.h>
#include <mail_server.h>
#include <smtpd_token.h>
#include <smtpd.h>
#include <smtpd_check.h>
#include <smtpd_chat.h>
#include <smtpd_sasl_proto.h>
#include <smtpd_sasl_glue.h>
#include <smtpd_proxy.h>
int var_smtpd_rcpt_limit;
int var_smtpd_tmout;
char *var_relay_ccerts;
int var_smtpd_soft_erlim;
int var_smtpd_hard_erlim;
int var_queue_minfree;
char *var_smtpd_banner;
char *var_notify_classes;
char *var_client_checks;
char *var_helo_checks;
char *var_mail_checks;
char *var_rcpt_checks;
char *var_etrn_checks;
char *var_data_checks;
int var_unk_client_code;
int var_bad_name_code;
int var_unk_name_code;
int var_unk_addr_code;
int var_relay_code;
int var_maps_rbl_code;
int var_access_map_code;
char *var_maps_rbl_domains;
char *var_rbl_reply_maps;
int var_helo_required;
int var_reject_code;
int var_defer_code;
int var_smtpd_err_sleep;
int var_non_fqdn_code;
char *var_error_rcpt;
int var_smtpd_delay_reject;
char *var_rest_classes;
int var_strict_rfc821_env;
bool var_disable_vrfy_cmd;
char *var_canonical_maps;
char *var_rcpt_canon_maps;
char *var_virt_alias_maps;
char *var_virt_mailbox_maps;
char *var_alias_maps;
char *var_local_rcpt_maps;
bool var_allow_untrust_route;
int var_smtpd_junk_cmd_limit;
int var_smtpd_rcpt_overlim;
bool var_smtpd_sasl_enable;
char *var_smtpd_sasl_opts;
char *var_smtpd_sasl_appname;
char *var_smtpd_sasl_realm;
char *var_smtpd_sasl_exceptions_networks;
char *var_filter_xport;
bool var_broken_auth_clients;
char *var_perm_mx_networks;
char *var_smtpd_snd_auth_maps;
char *var_smtpd_noop_cmds;
char *var_smtpd_null_key;
int var_smtpd_hist_thrsh;
char *var_smtpd_exp_filter;
char *var_def_rbl_reply;
int var_unv_from_code;
int var_unv_rcpt_code;
int var_mul_rcpt_code;
char *var_relay_rcpt_maps;
char *var_verify_sender;
int var_local_rcpt_code;
int var_virt_alias_code;
int var_virt_mailbox_code;
int var_relay_rcpt_code;
char *var_verp_clients;
int var_show_unk_rcpt_table;
int var_verify_poll_count;
int var_verify_poll_delay;
char *var_smtpd_proxy_filt;
int var_smtpd_proxy_tmout;
char *var_smtpd_proxy_ehlo;
char *var_input_transp;
int var_smtpd_policy_tmout;
int var_smtpd_policy_idle;
int var_smtpd_policy_ttl;
char *var_xclient_hosts;
char *var_xforward_hosts;
bool var_smtpd_rej_unl_from;
bool var_smtpd_rej_unl_rcpt;
#ifdef SNAPSHOT
int var_smtpd_crate_limit;
int var_smtpd_cconn_limit;
char *var_smtpd_hoggers;
#endif
bool var_smtpd_use_tls;
bool var_smtpd_enforce_tls;
bool var_smtpd_tls_wrappermode;
#ifdef USE_SSL
int var_smtpd_starttls_tmout;
bool var_smtpd_tls_auth_only;
bool var_smtpd_tls_ask_ccert;
bool var_smtpd_tls_req_ccert;
int var_smtpd_tls_ccert_vd;
bool var_smtpd_tls_received_header;
char *var_smtpd_sasl_tls_opts;
#endif
#ifdef __APPLE__
bool var_smtpd_use_pw_server;
char *var_smtpd_pw_server_opts;
#endif
#define STR(x) vstring_str(x)
#define LEN(x) VSTRING_LEN(x)
#define VERP_CMD "XVERP"
#define VERP_CMD_LEN 5
static NAMADR_LIST *verp_clients;
static NAMADR_LIST *xclient_hosts;
static int xclient_allowed;
static NAMADR_LIST *xforward_hosts;
static int xforward_allowed;
#ifdef SNAPSHOT
ANVIL_CLNT *anvil_clnt;
static NAMADR_LIST *hogger_list;
#endif
int smtpd_input_transp_mask;
static void helo_reset(SMTPD_STATE *);
static void mail_reset(SMTPD_STATE *);
static void rcpt_reset(SMTPD_STATE *);
static void chat_reset(SMTPD_STATE *, int);
#define NEUTER_CHARACTERS " <>()\\\";:@"
#ifdef USE_SASL_AUTH
static NAMADR_LIST *sasl_exceptions_networks;
static int sasl_client_exception(SMTPD_STATE *state)
{
int match;
if (sasl_exceptions_networks == 0)
return (0);
match = namadr_list_match(sasl_exceptions_networks,
state->name, state->addr);
if (msg_verbose)
msg_info("sasl_exceptions: %s[%s], match=%d",
state->name, state->addr, match);
return (match);
}
#endif
static void collapse_args(int argc, SMTPD_TOKEN *argv)
{
int i;
for (i = 1; i < argc; i++) {
vstring_strcat(argv[0].vstrval, " ");
vstring_strcat(argv[0].vstrval, argv[i].strval);
}
argv[0].strval = STR(argv[0].vstrval);
}
static int helo_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
{
char *err;
if (argc < 2) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 Syntax: HELO hostname");
return (-1);
}
if (argc > 2)
collapse_args(argc - 1, argv + 1);
if (SMTPD_STAND_ALONE(state) == 0
&& var_smtpd_delay_reject == 0
&& (err = smtpd_check_helo(state, argv[1].strval)) != 0) {
smtpd_chat_reply(state, "%s", err);
return (-1);
}
if (state->helo_name != 0)
helo_reset(state);
chat_reset(state, var_smtpd_hist_thrsh);
mail_reset(state);
rcpt_reset(state);
state->helo_name = mystrdup(printable(argv[1].strval, '?'));
neuter(state->helo_name, NEUTER_CHARACTERS, '?');
if (strcasecmp(state->protocol, MAIL_PROTO_ESMTP) != 0
&& strcasecmp(state->protocol, MAIL_PROTO_SMTP) != 0) {
myfree(state->protocol);
state->protocol = mystrdup(MAIL_PROTO_SMTP);
}
smtpd_chat_reply(state, "250 %s", var_myhostname);
return (0);
}
static int ehlo_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
{
char *err;
if (argc < 2) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 Syntax: EHLO hostname");
return (-1);
}
if (argc > 2)
collapse_args(argc - 1, argv + 1);
if (SMTPD_STAND_ALONE(state) == 0
&& var_smtpd_delay_reject == 0
&& (err = smtpd_check_helo(state, argv[1].strval)) != 0) {
smtpd_chat_reply(state, "%s", err);
return (-1);
}
if (state->helo_name != 0)
helo_reset(state);
chat_reset(state, var_smtpd_hist_thrsh);
mail_reset(state);
rcpt_reset(state);
state->helo_name = mystrdup(printable(argv[1].strval, '?'));
neuter(state->helo_name, NEUTER_CHARACTERS, '?');
if (strcasecmp(state->protocol, MAIL_PROTO_ESMTP) != 0) {
myfree(state->protocol);
state->protocol = mystrdup(MAIL_PROTO_ESMTP);
}
smtpd_chat_reply(state, "250-%s", var_myhostname);
smtpd_chat_reply(state, "250-PIPELINING");
if (var_message_limit)
smtpd_chat_reply(state, "250-SIZE %lu",
(unsigned long) var_message_limit);
else
smtpd_chat_reply(state, "250-SIZE");
if (var_disable_vrfy_cmd == 0)
smtpd_chat_reply(state, "250-VRFY");
smtpd_chat_reply(state, "250-ETRN");
#ifdef USE_SSL
if ((state->tls_use_tls || state->tls_enforce_tls) && (!state->tls_active))
smtpd_chat_reply(state, "250-STARTTLS");
#endif
#ifdef __APPLE__
#ifdef USE_SASL_AUTH
if ( var_smtpd_sasl_enable )
{
#ifdef USE_SSL
if ( !state->tls_auth_only || state->tls_active )
{
#endif
if ( var_smtpd_use_pw_server )
{
if ( state->pw_server_opts )
{
smtpd_chat_reply( state, "250-AUTH%s", state->pw_server_mechanism_list );
if ( var_broken_auth_clients )
{
smtpd_chat_reply(state, "250-AUTH=%s", (char *)&state->pw_server_mechanism_list[1] );
}
}
}
else
{
smtpd_chat_reply(state, "250-AUTH %s", state->sasl_mechanism_list);
if (var_broken_auth_clients)
{
smtpd_chat_reply(state, "250-AUTH=%s", state->sasl_mechanism_list);
}
}
#ifdef USE_SSL
}
#endif
}
#endif
#else
#ifdef USE_SASL_AUTH
if (var_smtpd_sasl_enable && !sasl_client_exception(state)) {
#ifdef USE_SSL
if (!state->tls_auth_only || state->tls_active) {
#endif
smtpd_chat_reply(state, "250-AUTH %s", state->sasl_mechanism_list);
if (var_broken_auth_clients)
smtpd_chat_reply(state, "250-AUTH=%s", state->sasl_mechanism_list);
#ifdef USE_SSL
}
#endif
}
#endif
#endif
if (namadr_list_match(verp_clients, state->name, state->addr))
smtpd_chat_reply(state, "250-%s", VERP_CMD);
if (xclient_allowed)
smtpd_chat_reply(state, "250-" XCLIENT_CMD
" " XCLIENT_NAME " " XCLIENT_ADDR
" " XCLIENT_PROTO " " XCLIENT_HELO);
if (xforward_allowed)
smtpd_chat_reply(state, "250-" XFORWARD_CMD
" " XFORWARD_NAME " " XFORWARD_ADDR
" " XFORWARD_PROTO " " XFORWARD_HELO);
smtpd_chat_reply(state, "250 8BITMIME");
return (0);
}
static void helo_reset(SMTPD_STATE *state)
{
if (state->helo_name)
myfree(state->helo_name);
state->helo_name = 0;
}
static void mail_open_stream(SMTPD_STATE *state)
{
char *postdrop_command;
int cleanup_flags;
cleanup_flags = CLEANUP_FLAG_MASK_EXTERNAL;
if (smtpd_input_transp_mask & INPUT_TRANSP_ADDRESS_MAPPING)
cleanup_flags &= ~(CLEANUP_FLAG_BCC_OK | CLEANUP_FLAG_MAP_OK);
if (smtpd_input_transp_mask & INPUT_TRANSP_HEADER_BODY)
cleanup_flags &= ~CLEANUP_FLAG_FILTER;
if (SMTPD_STAND_ALONE(state) == 0) {
state->dest = mail_stream_service(MAIL_CLASS_PUBLIC,
var_cleanup_service);
if (state->dest == 0
|| attr_print(state->dest->stream, ATTR_FLAG_NONE,
ATTR_TYPE_NUM, MAIL_ATTR_FLAGS, cleanup_flags,
ATTR_TYPE_END) != 0)
msg_fatal("unable to connect to the %s %s service",
MAIL_CLASS_PUBLIC, var_cleanup_service);
}
else {
postdrop_command = concatenate(var_command_dir, "/postdrop",
msg_verbose ? " -v" : (char *) 0, (char *) 0);
state->dest = mail_stream_command(postdrop_command);
if (state->dest == 0)
msg_fatal("unable to execute %s", postdrop_command);
myfree(postdrop_command);
}
state->cleanup = state->dest->stream;
state->queue_id = mystrdup(state->dest->id);
if (SMTPD_STAND_ALONE(state) == 0) {
rec_fprintf(state->cleanup, REC_TYPE_TIME, "%ld", state->time);
if (*var_filter_xport)
rec_fprintf(state->cleanup, REC_TYPE_FILT, "%s", var_filter_xport);
}
rec_fputs(state->cleanup, REC_TYPE_FROM, state->sender);
if (state->encoding != 0)
rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
MAIL_ATTR_ENCODING, state->encoding);
if (SMTPD_STAND_ALONE(state) == 0) {
if (IS_AVAIL_CLIENT_NAME(FORWARD_NAME(state)))
rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
MAIL_ATTR_CLIENT_NAME, FORWARD_NAME(state));
if (IS_AVAIL_CLIENT_ADDR(FORWARD_ADDR(state)))
rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
MAIL_ATTR_CLIENT_ADDR, FORWARD_ADDR(state));
if (IS_AVAIL_CLIENT_NAMADDR(FORWARD_NAMADDR(state)))
rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
MAIL_ATTR_ORIGIN, FORWARD_NAMADDR(state));
if (IS_AVAIL_CLIENT_HELO(FORWARD_HELO(state)))
rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
MAIL_ATTR_HELO_NAME, FORWARD_HELO(state));
if (IS_AVAIL_CLIENT_PROTO(FORWARD_PROTO(state)))
rec_fprintf(state->cleanup, REC_TYPE_ATTR, "%s=%s",
MAIL_ATTR_PROTO_NAME, FORWARD_PROTO(state));
}
if (state->verp_delims)
rec_fputs(state->cleanup, REC_TYPE_VERP, state->verp_delims);
}
static char *extract_addr(SMTPD_STATE *state, SMTPD_TOKEN *arg,
int allow_empty_addr, int strict_rfc821)
{
char *myname = "extract_addr";
TOK822 *tree;
TOK822 *tp;
TOK822 *addr = 0;
int naddr;
int non_addr;
char *err = 0;
char *junk = 0;
char *text;
char *colon;
#define PERMIT_EMPTY_ADDR 1
#define REJECT_EMPTY_ADDR 0
if (msg_verbose)
msg_info("%s: input: %s", myname, STR(arg->vstrval));
if (STR(arg->vstrval)[0] == '<'
&& STR(arg->vstrval)[LEN(arg->vstrval) - 1] == '>') {
junk = text = mystrndup(STR(arg->vstrval) + 1, LEN(arg->vstrval) - 2);
} else
text = STR(arg->vstrval);
if (*text == '@' && (colon = strchr(text, ':')) != 0)
text = colon + 1;
tree = tok822_parse(text);
if (junk)
myfree(junk);
for (naddr = non_addr = 0, tp = tree; tp != 0; tp = tp->next) {
if (tp->type == TOK822_ADDR) {
addr = tp;
naddr += 1;
} else if (tp->type == '<' || tp->type == '>') {
;
} else {
non_addr += 1;
}
}
if (naddr > 1
|| (strict_rfc821 && (non_addr || *STR(arg->vstrval) != '<'))) {
msg_warn("Illegal address syntax from %s in %s command: %s",
state->namaddr, state->where, STR(arg->vstrval));
err = "501 Bad address syntax";
}
if (addr)
tok822_internalize(arg->vstrval, addr->head, TOK822_STR_DEFL);
else
vstring_strcpy(arg->vstrval, "");
arg->strval = STR(arg->vstrval);
if (err == 0)
if ((arg->strval[0] == 0 && !allow_empty_addr)
|| (strict_rfc821 && arg->strval[0] == '@')
|| (SMTPD_STAND_ALONE(state) == 0
&& smtpd_check_addr(STR(arg->vstrval)) != 0)) {
msg_warn("Illegal address syntax from %s in %s command: %s",
state->namaddr, state->where, STR(arg->vstrval));
err = "501 Bad address syntax";
}
tok822_free_tree(tree);
if (msg_verbose)
msg_info("%s: result: %s", myname, STR(arg->vstrval));
return (err);
}
static int mail_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
{
char *err;
int narg;
char *arg;
char *verp_delims = 0;
state->encoding = 0;
if (var_helo_required && state->helo_name == 0) {
state->error_mask |= MAIL_ERROR_POLICY;
smtpd_chat_reply(state, "503 Error: send HELO/EHLO first");
return (-1);
}
#define IN_MAIL_TRANSACTION(state) ((state)->sender != 0)
if (IN_MAIL_TRANSACTION(state)) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "503 Error: nested MAIL command");
return (-1);
}
if (argc < 3
|| strcasecmp(argv[1].strval, "from:") != 0) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 Syntax: MAIL FROM: <address>");
return (-1);
}
if (argv[2].tokval == SMTPD_TOK_ERROR) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 Bad sender address syntax");
return (-1);
}
if ((err = extract_addr(state, argv + 2, PERMIT_EMPTY_ADDR, var_strict_rfc821_env)) != 0) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "%s", err);
return (-1);
}
for (narg = 3; narg < argc; narg++) {
arg = argv[narg].strval;
if (strcasecmp(arg, "BODY=8BITMIME") == 0) {
state->encoding = MAIL_ATTR_ENC_8BIT;
} else if (strcasecmp(arg, "BODY=7BIT") == 0) {
state->encoding = MAIL_ATTR_ENC_7BIT;
} else if (strncasecmp(arg, "SIZE=", 5) == 0) {
if (!alldig(arg + 5)) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 Bad message size syntax");
return (-1);
}
if ((state->msg_size = off_cvt_string(arg + 5)) < 0) {
smtpd_chat_reply(state, "552 Message size exceeds file system imposed limit");
state->error_mask |= MAIL_ERROR_POLICY;
return (-1);
}
#ifdef USE_SASL_AUTH
} else if (var_smtpd_sasl_enable && strncasecmp(arg, "AUTH=", 5) == 0) {
if ((err = smtpd_sasl_mail_opt(state, arg + 5)) != 0) {
smtpd_chat_reply(state, "%s", err);
return (-1);
}
#endif
} else if (namadr_list_match(verp_clients, state->name, state->addr)
&& strncasecmp(arg, VERP_CMD, VERP_CMD_LEN) == 0
&& (arg[VERP_CMD_LEN] == '=' || arg[VERP_CMD_LEN] == 0)) {
if (arg[VERP_CMD_LEN] == 0) {
verp_delims = var_verp_delims;
} else {
verp_delims = arg + VERP_CMD_LEN + 1;
if (verp_delims_verify(verp_delims) != 0) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 Error: %s needs two characters from %s",
VERP_CMD, var_verp_filter);
return (-1);
}
}
} else {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "555 Unsupported option: %s", arg);
return (-1);
}
}
if (verp_delims && argv[2].strval[0] == 0) {
smtpd_chat_reply(state, "503 Error: %s requires non-null sender",
VERP_CMD);
return (-1);
}
if (SMTPD_STAND_ALONE(state) == 0
&& var_smtpd_delay_reject == 0
&& (err = smtpd_check_mail(state, argv[2].strval)) != 0) {
smtpd_chat_reply(state, "%s", err);
mail_reset(state);
return (-1);
}
if (!USE_SMTPD_PROXY(state)) {
if ((err = smtpd_check_size(state, state->msg_size)) != 0) {
smtpd_chat_reply(state, "%s", err);
return (-1);
}
}
state->time = time((time_t *) 0);
state->sender = mystrdup(argv[2].strval);
vstring_sprintf(state->instance, "%x.%lx.%x",
var_pid, (unsigned long) state->time, state->seqno++);
if (verp_delims)
state->verp_delims = mystrdup(verp_delims);
if (USE_SMTPD_PROXY(state))
state->proxy_mail = mystrdup(STR(state->buffer));
smtpd_chat_reply(state, "250 Ok");
return (0);
}
static void mail_reset(SMTPD_STATE *state)
{
state->msg_size = 0;
if (state->cleanup != 0) {
mail_stream_cleanup(state->dest);
state->dest = 0;
state->cleanup = 0;
}
state->err = 0;
if (state->queue_id != 0) {
myfree(state->queue_id);
state->queue_id = 0;
}
if (state->sender) {
myfree(state->sender);
state->sender = 0;
}
if (state->verp_delims) {
myfree(state->verp_delims);
state->verp_delims = 0;
}
if (state->proxy_mail) {
myfree(state->proxy_mail);
state->proxy_mail = 0;
}
if (state->saved_filter) {
myfree(state->saved_filter);
state->saved_filter = 0;
}
if (state->saved_redirect) {
myfree(state->saved_redirect);
state->saved_redirect = 0;
}
state->saved_flags = 0;
#ifdef USE_SASL_AUTH
if (var_smtpd_sasl_enable)
smtpd_sasl_mail_reset(state);
#endif
state->discard = 0;
VSTRING_RESET(state->instance);
VSTRING_TERMINATE(state->instance);
if (state->proxy) {
(void) smtpd_proxy_cmd(state, SMTPD_PROX_WANT_NONE, "QUIT");
smtpd_proxy_close(state);
}
if (state->xforward.flags)
smtpd_xforward_reset(state);
if (state->prepend)
state->prepend = argv_free(state->prepend);
}
static int rcpt_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
{
char *err;
int narg;
char *arg;
if (!IN_MAIL_TRANSACTION(state)) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "503 Error: need MAIL command");
return (-1);
}
if (argc < 3
|| strcasecmp(argv[1].strval, "to:") != 0) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 Syntax: RCPT TO: <address>");
return (-1);
}
if (argv[2].tokval == SMTPD_TOK_ERROR) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 Bad recipient address syntax");
return (-1);
}
if ((err = extract_addr(state, argv + 2, REJECT_EMPTY_ADDR, var_strict_rfc821_env)) != 0) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "%s", err);
return (-1);
}
for (narg = 3; narg < argc; narg++) {
arg = argv[narg].strval;
if (1) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "555 Unsupported option: %s", arg);
return (-1);
}
}
if (var_smtpd_rcpt_limit && state->rcpt_count >= var_smtpd_rcpt_limit) {
smtpd_chat_reply(state, "452 Error: too many recipients");
if (state->rcpt_overshoot++ < var_smtpd_rcpt_overlim)
return (0);
state->error_mask |= MAIL_ERROR_POLICY;
return (-1);
}
if (SMTPD_STAND_ALONE(state) == 0) {
if ((err = smtpd_check_rcpt(state, argv[2].strval)) != 0) {
smtpd_chat_reply(state, "%s", err);
return (-1);
}
}
if (state->proxy == 0 && state->cleanup == 0) {
if (state->proxy_mail) {
if (smtpd_proxy_open(state, var_smtpd_proxy_filt,
var_smtpd_proxy_tmout, var_smtpd_proxy_ehlo,
state->proxy_mail) != 0) {
smtpd_chat_reply(state, "%s", STR(state->proxy_buffer));
return (-1);
}
} else {
mail_open_stream(state);
}
#ifdef USE_SASL_AUTH
if (var_smtpd_sasl_enable)
smtpd_sasl_mail_log(state);
else
#endif
msg_info("%s: client=%s", state->queue_id ?
state->queue_id : "NOQUEUE", FORWARD_NAMADDR(state));
}
if (state->proxy && smtpd_proxy_cmd(state, SMTPD_PROX_WANT_OK,
"%s", STR(state->buffer)) != 0) {
smtpd_chat_reply(state, "%s", STR(state->proxy_buffer));
return (-1);
}
state->rcpt_count++;
if (state->recipient == 0)
state->recipient = mystrdup(argv[2].strval);
if (state->cleanup) {
rec_fputs(state->cleanup, REC_TYPE_RCPT, argv[2].strval);
vstream_fflush(state->cleanup);
}
smtpd_chat_reply(state, "250 Ok");
return (0);
}
static void rcpt_reset(SMTPD_STATE *state)
{
if (state->recipient) {
myfree(state->recipient);
state->recipient = 0;
}
state->rcpt_count = 0;
state->rcpt_overshoot = 0;
}
static void CN_sanitize(char *CNstring)
{
int i;
int len;
int parencount;
len = strlen(CNstring);
for (i = 0; i < len; i++)
if (!((CNstring[i] >= 'A') && (CNstring[i] <='Z')) &&
!((CNstring[i] >= 'a') && (CNstring[i] <='z')) &&
!((CNstring[i] >= '0') && (CNstring[i] <='9')) &&
(CNstring[i] != '(') && (CNstring[i] != ')') &&
(CNstring[i] != '[') && (CNstring[i] != ']') &&
(CNstring[i] != '{') && (CNstring[i] != '}') &&
(CNstring[i] != '<') && (CNstring[i] != '>') &&
(CNstring[i] != '?') && (CNstring[i] != '!') &&
(CNstring[i] != ';') && (CNstring[i] != ':') &&
(CNstring[i] != '"') && (CNstring[i] != '\'') &&
(CNstring[i] != '/') && (CNstring[i] != '|') &&
(CNstring[i] != '+') && (CNstring[i] != '&') &&
(CNstring[i] != '~') && (CNstring[i] != '@') &&
(CNstring[i] != '#') && (CNstring[i] != '$') &&
(CNstring[i] != '%') && (CNstring[i] != '&') &&
(CNstring[i] != '^') && (CNstring[i] != '*') &&
(CNstring[i] != '_') && (CNstring[i] != '-') &&
(CNstring[i] != '.') && (CNstring[i] != ' '))
CNstring[i] = '?';
parencount = 0;
for (i = 0; i < len; i++) {
if (CNstring[i] == '(')
parencount++;
else if (CNstring[i] == ')')
parencount--;
}
if (parencount != 0) {
for (i = 0; i < len; i++)
if ((CNstring[i] == '(') || (CNstring[i] == ')'))
CNstring[i] = '/';
}
}
static int data_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
{
char *err;
char *start;
char *peer_CN;
char *issuer_CN;
int len;
int curr_rec_type;
int prev_rec_type;
int first = 1;
VSTRING *why = 0;
int saved_err;
int (*out_record) (VSTREAM *, int, const char *, int);
int (*out_fprintf) (VSTREAM *, int, const char *,...);
VSTREAM *out_stream;
int out_error;
char **cpp;
if (state->rcpt_count == 0) {
if (!IN_MAIL_TRANSACTION(state)) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "503 Error: need RCPT command");
} else {
smtpd_chat_reply(state, "554 Error: no valid recipients");
}
return (-1);
}
if (argc != 1) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 Syntax: DATA");
return (-1);
}
if (SMTPD_STAND_ALONE(state) == 0 && (err = smtpd_check_data(state)) != 0) {
smtpd_chat_reply(state, "%s", err);
return (-1);
}
if (state->proxy && smtpd_proxy_cmd(state, SMTPD_PROX_WANT_MORE,
"%s", STR(state->buffer)) != 0) {
smtpd_chat_reply(state, "%s", STR(state->proxy_buffer));
return (-1);
}
if (state->proxy) {
out_stream = state->proxy;
out_record = smtpd_proxy_rec_put;
out_fprintf = smtpd_proxy_rec_fprintf;
out_error = CLEANUP_STAT_PROXY;
} else {
out_stream = state->cleanup;
out_record = rec_put;
out_fprintf = rec_fprintf;
out_error = CLEANUP_STAT_WRITE;
}
if (state->cleanup) {
if (state->saved_filter)
rec_fprintf(state->cleanup, REC_TYPE_FILT, "%s", state->saved_filter);
if (state->saved_redirect)
rec_fprintf(state->cleanup, REC_TYPE_RDR, "%s", state->saved_redirect);
if (state->saved_flags)
rec_fprintf(state->cleanup, REC_TYPE_FLGS, "%d", state->saved_flags);
rec_fputs(state->cleanup, REC_TYPE_MESG, "");
}
if (state->prepend)
for (cpp = state->prepend->argv; *cpp; cpp++)
out_fprintf(out_stream, REC_TYPE_NORM, "%s", *cpp);
if (!state->proxy || state->xforward.flags == 0) {
out_fprintf(out_stream, REC_TYPE_NORM,
"Received: from %s (%s [%s])",
state->helo_name ? state->helo_name : state->name,
state->name, state->addr);
#ifdef USE_SSL
if (var_smtpd_tls_received_header && state->tls_active) {
out_fprintf(out_stream, REC_TYPE_NORM,
"\t(using %s with cipher %s (%d/%d bits))",
state->tls_info.protocol, state->tls_info.cipher_name,
state->tls_info.cipher_usebits,
state->tls_info.cipher_algbits);
if (state->tls_info.peer_CN) {
peer_CN = mystrdup(state->tls_info.peer_CN);
CN_sanitize(peer_CN);
issuer_CN = mystrdup(state->tls_info.issuer_CN);
CN_sanitize(issuer_CN);
if (state->tls_info.peer_verified)
out_fprintf(out_stream, REC_TYPE_NORM,
"\t(Client CN \"%s\", Issuer \"%s\" (verified OK))",
peer_CN, issuer_CN);
else
out_fprintf(out_stream, REC_TYPE_NORM,
"\t(Client CN \"%s\", Issuer \"%s\" (not verified))",
peer_CN, issuer_CN);
myfree(issuer_CN);
myfree(peer_CN);
}
else if (var_smtpd_tls_ask_ccert)
out_fprintf(out_stream, REC_TYPE_NORM,
"\t(Client did not present a certificate)");
else
out_fprintf(out_stream, REC_TYPE_NORM,
"\t(No client certificate requested)");
}
#endif
if (state->rcpt_count == 1 && state->recipient) {
out_fprintf(out_stream, REC_TYPE_NORM,
state->cleanup ? "\tby %s (%s) with %s id %s" :
"\tby %s (%s) with %s",
var_myhostname, var_mail_name,
state->protocol, state->queue_id);
quote_822_local(state->buffer, state->recipient);
out_fprintf(out_stream, REC_TYPE_NORM,
"\tfor <%s>; %s", STR(state->buffer), mail_date(state->time));
} else {
out_fprintf(out_stream, REC_TYPE_NORM,
state->cleanup ? "\tby %s (%s) with %s id %s;" :
"\tby %s (%s) with %s;",
var_myhostname, var_mail_name,
state->protocol, state->queue_id);
out_fprintf(out_stream, REC_TYPE_NORM,
"\t%s", mail_date(state->time));
}
#ifdef RECEIVED_ENVELOPE_FROM
quote_822_local(state->buffer, state->sender);
out_fprintf(out_stream, REC_TYPE_NORM,
"\t(envelope-from %s)", STR(state->buffer));
#endif
}
smtpd_chat_reply(state, "354 End data with <CR><LF>.<CR><LF>");
for (prev_rec_type = 0; ; prev_rec_type = curr_rec_type) {
if (smtp_get(state->buffer, state->client, var_line_limit) == '\n')
curr_rec_type = REC_TYPE_NORM;
else
curr_rec_type = REC_TYPE_CONT;
start = vstring_str(state->buffer);
len = VSTRING_LEN(state->buffer);
if (first) {
if (strncmp(start + strspn(start, ">"), "From ", 5) == 0) {
out_fprintf(out_stream, curr_rec_type,
"X-Mailbox-Line: %s", start);
continue;
}
first = 0;
if (len > 0 && IS_SPACE_TAB(start[0]))
out_record(out_stream, REC_TYPE_NORM, "", 0);
}
if (prev_rec_type != REC_TYPE_CONT && *start == '.'
&& (state->proxy == 0 ? (++start, --len) == 0 : len == 1))
break;
if (state->err == CLEANUP_STAT_OK
&& out_record(out_stream, curr_rec_type, start, len) < 0)
state->err = out_error;
}
if (state->proxy) {
if (state->err == CLEANUP_STAT_OK) {
(void) smtpd_proxy_cmd(state, SMTPD_PROX_WANT_ANY, ".");
if (state->err == CLEANUP_STAT_OK &&
*STR(state->proxy_buffer) != '2')
state->err = CLEANUP_STAT_CONT;
} else {
state->error_mask |= MAIL_ERROR_SOFTWARE;
state->err |= CLEANUP_STAT_PROXY;
vstring_sprintf(state->proxy_buffer,
"451 Error: queue file write error");
}
}
else {
if (state->err == CLEANUP_STAT_OK)
if (rec_fputs(state->cleanup, REC_TYPE_XTRA, "") < 0
|| rec_fputs(state->cleanup, REC_TYPE_END, "") < 0
|| vstream_fflush(state->cleanup))
state->err = CLEANUP_STAT_WRITE;
if (state->err == 0) {
why = vstring_alloc(10);
state->err = mail_stream_finish(state->dest, why);
} else
mail_stream_cleanup(state->dest);
state->dest = 0;
state->cleanup = 0;
}
if (state->err == CLEANUP_STAT_OK) {
state->error_count = 0;
state->error_mask = 0;
state->junk_cmds = 0;
if (state->queue_id)
smtpd_chat_reply(state, "250 Ok: queued as %s", state->queue_id);
else
smtpd_chat_reply(state, "%s", STR(state->proxy_buffer));
} else if ((state->err & CLEANUP_STAT_BAD) != 0) {
state->error_mask |= MAIL_ERROR_SOFTWARE;
smtpd_chat_reply(state, "451 Error: internal error %d", state->err);
} else if ((state->err & CLEANUP_STAT_SIZE) != 0) {
state->error_mask |= MAIL_ERROR_BOUNCE;
smtpd_chat_reply(state, "552 Error: message too large");
} else if ((state->err & CLEANUP_STAT_HOPS) != 0) {
state->error_mask |= MAIL_ERROR_BOUNCE;
smtpd_chat_reply(state, "554 Error: too many hops");
} else if ((state->err & CLEANUP_STAT_CONT) != 0) {
state->error_mask |= MAIL_ERROR_POLICY;
if (state->proxy_buffer)
smtpd_chat_reply(state, "%s", STR(state->proxy_buffer));
else
smtpd_chat_reply(state, "550 Error: %s", LEN(why) ?
STR(why) : "content rejected");
} else if ((state->err & CLEANUP_STAT_WRITE) != 0) {
state->error_mask |= MAIL_ERROR_RESOURCE;
smtpd_chat_reply(state, "451 Error: queue file write error");
} else if ((state->err & CLEANUP_STAT_PROXY) != 0) {
state->error_mask |= MAIL_ERROR_SOFTWARE;
smtpd_chat_reply(state, "%s", STR(state->proxy_buffer));
} else {
state->error_mask |= MAIL_ERROR_SOFTWARE;
smtpd_chat_reply(state, "451 Error: internal error %d", state->err);
}
state->where = SMTPD_AFTER_DOT;
saved_err = state->err;
chat_reset(state, var_smtpd_hist_thrsh);
mail_reset(state);
rcpt_reset(state);
if (why)
vstring_free(why);
return (saved_err);
}
static int rset_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
{
if (argc != 1) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 Syntax: RSET");
return (-1);
}
chat_reset(state, var_smtpd_hist_thrsh);
mail_reset(state);
rcpt_reset(state);
smtpd_chat_reply(state, "250 Ok");
return (0);
}
static int noop_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *unused_argv)
{
#ifdef RFC821_SYNTAX
if (argc != 1) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 Syntax: NOOP");
return (-1);
}
#endif
smtpd_chat_reply(state, "250 Ok");
return (0);
}
static int vrfy_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
{
char *err = 0;
#define SLOPPY 0
if (var_disable_vrfy_cmd) {
state->error_mask |= MAIL_ERROR_POLICY;
smtpd_chat_reply(state, "502 VRFY command is disabled");
return (-1);
}
if (argc < 2) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 Syntax: VRFY address");
return (-1);
}
if (argc > 2)
collapse_args(argc - 1, argv + 1);
if ((err = extract_addr(state, argv + 1, REJECT_EMPTY_ADDR, SLOPPY)) != 0) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "%s", err);
return (-1);
}
if (SMTPD_STAND_ALONE(state) == 0
&& (err = smtpd_check_rcpt(state, argv[1].strval)) != 0) {
smtpd_chat_reply(state, "%s", err);
return (-1);
}
smtpd_chat_reply(state, "252 %s", argv[1].strval);
return (0);
}
static int etrn_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
{
char *err;
if (var_helo_required && state->helo_name == 0) {
state->error_mask |= MAIL_ERROR_POLICY;
smtpd_chat_reply(state, "503 Error: send HELO/EHLO first");
return (-1);
}
if (IN_MAIL_TRANSACTION(state)) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "503 Error: MAIL transaction in progress");
return (-1);
}
if (argc != 2) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "500 Syntax: ETRN domain");
return (-1);
}
if (!ISALNUM(argv[1].strval[0]))
argv[1].strval++;
if (!valid_hostname(argv[1].strval, DONT_GRIPE)) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 Error: invalid parameter syntax");
return (-1);
}
if (SMTPD_STAND_ALONE(state)) {
msg_warn("do not use ETRN in \"sendmail -bs\" mode");
smtpd_chat_reply(state, "458 Unable to queue messages");
return (-1);
}
if ((err = smtpd_check_etrn(state, argv[1].strval)) != 0) {
smtpd_chat_reply(state, "%s", err);
return (-1);
}
switch (flush_send(argv[1].strval)) {
case FLUSH_STAT_OK:
smtpd_chat_reply(state, "250 Queuing started");
return (0);
case FLUSH_STAT_DENY:
msg_warn("reject: ETRN %.100s... from %s",
argv[1].strval, state->namaddr);
smtpd_chat_reply(state, "459 <%s>: service unavailable",
argv[1].strval);
return (-1);
case FLUSH_STAT_BAD:
msg_warn("bad ETRN %.100s... from %s", argv[1].strval, state->namaddr);
smtpd_chat_reply(state, "458 Unable to queue messages");
return (-1);
default:
msg_warn("unable to talk to fast flush service");
smtpd_chat_reply(state, "458 Unable to queue messages");
return (-1);
}
}
static int quit_cmd(SMTPD_STATE *state, int unused_argc, SMTPD_TOKEN *unused_argv)
{
smtpd_chat_reply(state, "221 Bye");
vstream_fflush(state->client);
return (0);
}
static int xclient_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
{
SMTPD_TOKEN *argp;
char *attr_value;
char *attr_name;
int update_namaddr = 0;
int peer_code;
static NAME_CODE peer_codes[] = {
XCLIENT_UNAVAILABLE, SMTPD_PEER_CODE_PERM,
XCLIENT_TEMPORARY, SMTPD_PEER_CODE_TEMP,
0, SMTPD_PEER_CODE_OK,
};
static NAME_CODE proto_names[] = {
MAIL_PROTO_SMTP, 1,
MAIL_PROTO_ESMTP, 2,
0, -1,
};
if (IN_MAIL_TRANSACTION(state)) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "503 Error: MAIL transaction in progress");
return (-1);
}
if (argc < 2) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 Syntax: %s attribute=value...",
XCLIENT_CMD);
return (-1);
}
if (!xclient_allowed) {
state->error_mask |= MAIL_ERROR_POLICY;
smtpd_chat_reply(state, "554 Error: insufficient authorization");
return (-1);
}
#define STREQ(x,y) (strcasecmp((x), (y)) == 0)
#define UPDATE_STR(s, v) do { \
if (s) myfree(s); \
s = (v) ? mystrdup(v) : 0; \
} while(0)
for (argp = argv + 1; argp < argv + argc; argp++) {
attr_name = argp->strval;
if ((attr_value = split_at(attr_name, '=')) == 0 || *attr_value == 0) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 Error: attribute=value expected");
return (-1);
}
printable(attr_value, '?');
if (STREQ(attr_name, XCLIENT_NAME)) {
peer_code = name_code(peer_codes, NAME_CODE_FLAG_NONE, attr_value);
if (peer_code != SMTPD_PEER_CODE_OK) {
attr_value = CLIENT_NAME_UNKNOWN;
} else {
if (!valid_hostname(attr_value, DONT_GRIPE)
|| valid_hostaddr(attr_value, DONT_GRIPE)) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 Bad %s syntax: %s",
XCLIENT_NAME, attr_value);
return (-1);
}
}
state->peer_code = peer_code;
UPDATE_STR(state->name, attr_value);
update_namaddr = 1;
}
else if (STREQ(attr_name, XCLIENT_ADDR)) {
if (STREQ(attr_value, XCLIENT_UNAVAILABLE)) {
attr_value = CLIENT_ADDR_UNKNOWN;
} else {
if (!valid_hostaddr(attr_value, DONT_GRIPE)) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 Bad %s syntax: %s",
XCLIENT_ADDR, attr_value);
return (-1);
}
}
UPDATE_STR(state->addr, attr_value);
update_namaddr = 1;
}
else if (STREQ(attr_name, XCLIENT_HELO)) {
if (STREQ(attr_value, XCLIENT_UNAVAILABLE)) {
attr_value = CLIENT_HELO_UNKNOWN;
} else {
if (strlen(attr_value) > VALID_HOSTNAME_LEN) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 Bad %s syntax: %s",
XCLIENT_HELO, attr_value);
return (-1);
}
neuter(attr_value, NEUTER_CHARACTERS, '?');
}
UPDATE_STR(state->helo_name, attr_value);
}
else if (STREQ(attr_name, XCLIENT_PROTO)) {
if (name_code(proto_names, NAME_CODE_FLAG_NONE, attr_value) < 0) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 Bad %s syntax: %s",
XCLIENT_PROTO, attr_value);
return (-1);
}
UPDATE_STR(state->protocol, uppercase(attr_value));
}
else {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 Bad %s attribute name: %s",
XCLIENT_CMD, attr_name);
return (-1);
}
}
if (update_namaddr) {
if (state->namaddr)
myfree(state->namaddr);
state->namaddr =
concatenate(state->name, "[", state->addr, "]", (char *) 0);
}
smtpd_chat_reply(state, "250 Ok");
return (0);
}
static int xforward_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
{
SMTPD_TOKEN *argp;
char *attr_value;
char *attr_name;
int updated = 0;
static NAME_CODE xforward_flags[] = {
XFORWARD_NAME, SMTPD_STATE_XFORWARD_NAME,
XFORWARD_ADDR, SMTPD_STATE_XFORWARD_ADDR,
XFORWARD_PROTO, SMTPD_STATE_XFORWARD_PROTO,
XFORWARD_HELO, SMTPD_STATE_XFORWARD_HELO,
0, 0,
};
int flag;
if (IN_MAIL_TRANSACTION(state)) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "503 Error: MAIL transaction in progress");
return (-1);
}
if (argc < 2) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 Syntax: %s attribute=value...",
XFORWARD_CMD);
return (-1);
}
if (!xforward_allowed) {
state->error_mask |= MAIL_ERROR_POLICY;
smtpd_chat_reply(state, "554 Error: insufficient authorization");
return (-1);
}
if (state->xforward.flags == 0)
smtpd_xforward_preset(state);
for (argp = argv + 1; argp < argv + argc; argp++) {
attr_name = argp->strval;
if ((attr_value = split_at(attr_name, '=')) == 0 || *attr_value == 0) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 Error: attribute=value expected");
return (-1);
}
if (strlen(attr_value) > 255) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 Error: attribute value too long");
return (-1);
}
printable(attr_value, '?');
flag = name_code(xforward_flags, NAME_CODE_FLAG_NONE, attr_name);
switch (flag) {
case SMTPD_STATE_XFORWARD_NAME:
if (STREQ(attr_value, XFORWARD_UNAVAILABLE)) {
attr_value = CLIENT_NAME_UNKNOWN;
} else {
neuter(attr_value, NEUTER_CHARACTERS, '?');
}
UPDATE_STR(state->xforward.name, attr_value);
break;
case SMTPD_STATE_XFORWARD_ADDR:
if (STREQ(attr_value, XFORWARD_UNAVAILABLE)) {
attr_value = CLIENT_ADDR_UNKNOWN;
} else {
neuter(attr_value, NEUTER_CHARACTERS, '?');
}
UPDATE_STR(state->xforward.addr, attr_value);
break;
case SMTPD_STATE_XFORWARD_HELO:
if (STREQ(attr_value, XFORWARD_UNAVAILABLE)) {
attr_value = CLIENT_HELO_UNKNOWN;
} else {
neuter(attr_value, NEUTER_CHARACTERS, '?');
}
UPDATE_STR(state->xforward.helo_name, attr_value);
break;
case SMTPD_STATE_XFORWARD_PROTO:
if (STREQ(attr_value, XFORWARD_UNAVAILABLE)) {
attr_value = CLIENT_PROTO_UNKNOWN;
} else {
if (strlen(attr_value) > 64) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 Bad %s syntax: %s",
XFORWARD_PROTO, attr_value);
return (-1);
}
neuter(attr_value, NEUTER_CHARACTERS, '?');
}
UPDATE_STR(state->xforward.protocol, attr_value);
break;
default:
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 Bad %s attribute name: %s",
XFORWARD_CMD, attr_name);
return (-1);
}
updated |= flag;
}
state->xforward.flags |= updated;
if (updated & (SMTPD_STATE_XFORWARD_NAME | SMTPD_STATE_XFORWARD_ADDR)) {
if (state->xforward.namaddr)
myfree(state->xforward.namaddr);
state->xforward.namaddr =
IS_AVAIL_CLIENT_ADDR(state->xforward.addr) ?
concatenate(state->xforward.name, "[",
state->xforward.addr, "]",
(char *) 0) : mystrdup(state->xforward.name);
}
smtpd_chat_reply(state, "250 Ok");
return (0);
}
static void chat_reset(SMTPD_STATE *state, int threshold)
{
if (state->history != 0 && state->history->argc > threshold) {
if (SMTPD_STAND_ALONE(state) == 0
&& (state->error_mask & state->notify_mask))
smtpd_chat_notify(state);
state->error_mask = 0;
smtpd_chat_reset(state);
}
}
static int starttls_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
{
char *err;
#ifdef USE_SSL
if (argc != 1) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 Syntax: STARTTLS");
return (-1);
}
if (state->tls_active != 0) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "554 Error: TLS already active");
return (-1);
}
if (state->tls_use_tls == 0) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "502 Error: command not implemented");
return (-1);
}
if (!pfixtls_serverengine) {
smtpd_chat_reply(state, "454 TLS not available due to temporary reason");
return (0);
}
smtpd_chat_reply(state, "220 Ready to start TLS");
vstream_fflush(state->client);
if (pfixtls_start_servertls(state->client, var_smtpd_starttls_tmout,
state->name, state->addr, &(state->tls_info),
(var_smtpd_tls_req_ccert && state->tls_enforce_tls))) {
return (-1);
}
state->tls_active = 1;
helo_reset(state);
#ifdef USE_SASL_AUTH
if (var_smtpd_sasl_enable) {
smtpd_sasl_disconnect(state);
smtpd_sasl_connect(state, VAR_SMTPD_SASL_TLS_OPTS,
var_smtpd_sasl_tls_opts);
smtpd_sasl_auth_reset(state);
}
#endif
mail_reset(state);
rcpt_reset(state);
return (0);
#else
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "502 Error: command not implemented");
return (-1);
#endif
}
static void tls_reset(SMTPD_STATE *state)
{
int failure = 0;
if (state->reason && state->where && strcmp(state->where, SMTPD_AFTER_DOT))
failure = 1;
#ifdef USE_SSL
vstream_fflush(state->client);
if (state->tls_active)
pfixtls_stop_servertls(state->client, var_smtpd_starttls_tmout,
failure, &(state->tls_info));
#endif
state->tls_active = 0;
}
typedef struct SMTPD_CMD {
char *name;
int (*action) (SMTPD_STATE *, int, SMTPD_TOKEN *);
int flags;
} SMTPD_CMD;
#define SMTPD_CMD_FLAG_LIMIT (1<<0)
#define SMTPD_CMD_FLAG_FORBID (1<<1)
static SMTPD_CMD smtpd_cmd_table[] = {
"HELO", helo_cmd, SMTPD_CMD_FLAG_LIMIT,
"EHLO", ehlo_cmd, SMTPD_CMD_FLAG_LIMIT,
#ifdef USE_SSL
"STARTTLS", starttls_cmd, 0,
#endif
#ifdef USE_SASL_AUTH
"AUTH", smtpd_sasl_auth_cmd, 0,
#endif
"MAIL", mail_cmd, 0,
"RCPT", rcpt_cmd, 0,
"DATA", data_cmd, 0,
"RSET", rset_cmd, SMTPD_CMD_FLAG_LIMIT,
"NOOP", noop_cmd, SMTPD_CMD_FLAG_LIMIT,
"VRFY", vrfy_cmd, SMTPD_CMD_FLAG_LIMIT,
"ETRN", etrn_cmd, SMTPD_CMD_FLAG_LIMIT,
"QUIT", quit_cmd, 0,
"XCLIENT", xclient_cmd, SMTPD_CMD_FLAG_LIMIT,
"XFORWARD", xforward_cmd, SMTPD_CMD_FLAG_LIMIT,
"Received:", 0, SMTPD_CMD_FLAG_FORBID,
"Reply-To:", 0, SMTPD_CMD_FLAG_FORBID,
"Message-ID:", 0, SMTPD_CMD_FLAG_FORBID,
"Subject:", 0, SMTPD_CMD_FLAG_FORBID,
"From:", 0, SMTPD_CMD_FLAG_FORBID,
"CONNECT", 0, SMTPD_CMD_FLAG_FORBID,
"User-Agent:", 0, SMTPD_CMD_FLAG_FORBID,
0,
};
static STRING_LIST *smtpd_noop_cmds;
static void smtpd_proto(SMTPD_STATE *state, const char *service)
{
int argc;
SMTPD_TOKEN *argv;
SMTPD_CMD *cmdp;
int count;
int crate;
smtp_timeout_setup(state->client, var_smtpd_tmout);
switch (vstream_setjmp(state->client)) {
default:
msg_panic("smtpd_proto: unknown error reading from %s[%s]",
state->name, state->addr);
break;
case SMTP_ERR_TIME:
state->reason = "timeout";
if (vstream_setjmp(state->client) == 0)
smtpd_chat_reply(state, "421 %s Error: timeout exceeded",
var_myhostname);
break;
case SMTP_ERR_EOF:
state->reason = "lost connection";
break;
case 0:
#ifdef SNAPSHOT
if (SMTPD_STAND_ALONE(state) == 0
&& !xclient_allowed
&& anvil_clnt
&& !namadr_list_match(hogger_list, state->name, state->addr)
&& anvil_clnt_connect(anvil_clnt, service, state->addr,
&count, &crate) == ANVIL_STAT_OK) {
if (var_smtpd_cconn_limit > 0 && count > var_smtpd_cconn_limit) {
smtpd_chat_reply(state, "421 %s Error: too many connections from %s",
var_myhostname, state->addr);
msg_warn("Too many connections: %d from %s for service %s",
count, state->namaddr, service);
break;
}
if (var_smtpd_crate_limit > 0 && crate > var_smtpd_crate_limit) {
smtpd_chat_reply(state, "421 %s Error: too many connections from %s",
var_myhostname, state->addr);
msg_warn("Too frequent connections: %d from %s for service %s",
crate, state->namaddr, service);
break;
}
}
#endif
if (SMTPD_STAND_ALONE(state) == 0
&& var_smtpd_delay_reject == 0
&& (state->access_denied = smtpd_check_client(state)) != 0) {
smtpd_chat_reply(state, "%s", state->access_denied);
state->error_count++;
} else {
smtpd_chat_reply(state, "220 %s", var_smtpd_banner);
}
for (;;) {
if (state->error_count >= var_smtpd_hard_erlim) {
state->reason = "too many errors";
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "421 %s Error: too many errors",
var_myhostname);
break;
}
watchdog_pat();
smtpd_chat_query(state);
if ((argc = smtpd_token(vstring_str(state->buffer), &argv)) == 0) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "500 Error: bad syntax");
state->error_count++;
continue;
}
if (*var_smtpd_noop_cmds
&& string_list_match(smtpd_noop_cmds, argv[0].strval)) {
smtpd_chat_reply(state, "250 Ok");
if (state->junk_cmds++ > var_smtpd_junk_cmd_limit)
state->error_count++;
continue;
}
for (cmdp = smtpd_cmd_table; cmdp->name != 0; cmdp++)
if (strcasecmp(argv[0].strval, cmdp->name) == 0)
break;
if (cmdp->name == 0) {
smtpd_chat_reply(state, "502 Error: command not implemented");
state->error_mask |= MAIL_ERROR_PROTOCOL;
state->error_count++;
continue;
}
if (cmdp->flags & SMTPD_CMD_FLAG_FORBID) {
msg_warn("%s sent non-SMTP command: %.100s",
state->namaddr, vstring_str(state->buffer));
smtpd_chat_reply(state, "221 Error: I can break rules, too. Goodbye.");
break;
}
if (state->access_denied && cmdp->action != quit_cmd) {
smtpd_chat_reply(state, "503 Error: access denied for %s",
state->namaddr);
state->error_count++;
continue;
}
if (state->tls_enforce_tls &&
!state->tls_active &&
cmdp->action != starttls_cmd &&
cmdp->action != noop_cmd &&
cmdp->action != ehlo_cmd &&
cmdp->action != quit_cmd) {
smtpd_chat_reply(state,
"530 Must issue a STARTTLS command first");
state->error_count++;
continue;
}
state->where = cmdp->name;
if (cmdp->action(state, argc, argv) != 0) {
state->error_count++;
if (cmdp->action == starttls_cmd)
break;
}
if ((cmdp->flags & SMTPD_CMD_FLAG_LIMIT)
&& state->junk_cmds++ > var_smtpd_junk_cmd_limit)
state->error_count++;
if (cmdp->action == quit_cmd)
break;
}
break;
}
#ifdef SNAPSHOT
if (SMTPD_STAND_ALONE(state) == 0
&& !xclient_allowed
&& anvil_clnt
&& !namadr_list_match(hogger_list, state->name, state->addr))
anvil_clnt_disconnect(anvil_clnt, service, state->addr);
#endif
if (state->reason && state->where
&& (strcmp(state->where, SMTPD_AFTER_DOT)
|| strcmp(state->reason, "lost connection")))
msg_info("%s after %s from %s[%s]",
state->reason, state->where, state->name, state->addr);
tls_reset(state);
helo_reset(state);
#ifdef USE_SASL_AUTH
if (var_smtpd_sasl_enable)
smtpd_sasl_auth_reset(state);
#endif
chat_reset(state, 0);
mail_reset(state);
rcpt_reset(state);
}
static void smtpd_service(VSTREAM *stream, char *service, char **argv)
{
SMTPD_STATE state;
if (argv[0])
msg_fatal("unexpected command-line argument: %s", argv[0]);
smtpd_state_init(&state, stream);
#ifdef USE_SSL
if (SMTPD_STAND_ALONE((&state))) {
state.tls_use_tls = 0;
state.tls_enforce_tls = 0;
state.tls_auth_only = 0;
}
else {
state.tls_use_tls = var_smtpd_use_tls | var_smtpd_enforce_tls;
state.tls_enforce_tls = var_smtpd_enforce_tls;
if (var_smtpd_tls_wrappermode) {
state.tls_use_tls = state.tls_enforce_tls = 1;
if (pfixtls_start_servertls(state.client, var_smtpd_starttls_tmout,
state.name, state.addr, &state.tls_info,
var_smtpd_tls_req_ccert)) {
return;
}
state.tls_active = 1;
#ifdef USE_SASL_AUTH
if (var_smtpd_sasl_enable) {
smtpd_sasl_disconnect(&state);
smtpd_sasl_connect(&state, VAR_SMTPD_SASL_TLS_OPTS,
var_smtpd_sasl_tls_opts);
smtpd_sasl_auth_reset(&state);
}
#endif
}
if (var_smtpd_tls_auth_only || state.tls_enforce_tls)
state.tls_auth_only = 1;
}
#else
state.tls_use_tls = 0;
state.tls_enforce_tls = 0;
state.tls_auth_only = 0;
#endif
msg_info("connect from %s[%s]", state.name, state.addr);
xclient_allowed =
namadr_list_match(xclient_hosts, state.name, state.addr);
xforward_allowed =
namadr_list_match(xforward_hosts, state.name, state.addr);
debug_peer_check(state.name, state.addr);
smtpd_proto(&state, service);
msg_info("disconnect from %s[%s]", state.name, state.addr);
smtpd_state_reset(&state);
debug_peer_restore();
}
static void pre_accept(char *unused_name, char **unused_argv)
{
const char *table;
if ((table = dict_changed_name()) != 0) {
msg_info("table %s has changed -- restarting", table);
exit(0);
}
}
static void pre_jail_init(char *unused_name, char **unused_argv)
{
smtpd_noop_cmds = string_list_init(MATCH_FLAG_NONE, var_smtpd_noop_cmds);
verp_clients = namadr_list_init(MATCH_FLAG_NONE, var_verp_clients);
xclient_hosts = namadr_list_init(MATCH_FLAG_NONE, var_xclient_hosts);
xforward_hosts = namadr_list_init(MATCH_FLAG_NONE, var_xforward_hosts);
#ifdef SNAPSHOT
hogger_list = namadr_list_init(MATCH_FLAG_NONE, var_smtpd_hoggers);
#endif
if (getuid() == 0 || getuid() == var_owner_uid)
smtpd_check_init();
debug_peer_init();
if (var_smtpd_sasl_enable)
#ifdef USE_SASL_AUTH
#ifdef __APPLE__
smtpd_sasl_initialize( var_smtpd_use_pw_server );
#else
smtpd_sasl_initialize();
#endif
if (*var_smtpd_sasl_exceptions_networks)
sasl_exceptions_networks =
namadr_list_init(MATCH_FLAG_NONE,
var_smtpd_sasl_exceptions_networks);
#else
msg_warn("%s is true, but SASL support is not compiled in",
VAR_SMTPD_SASL_ENABLE);
#endif
if (geteuid() == 0) {
if (var_smtpd_use_tls || var_smtpd_enforce_tls
|| var_smtpd_tls_wrappermode)
#ifdef USE_SSL
pfixtls_init_serverengine(var_smtpd_tls_ccert_vd,
var_smtpd_tls_ask_ccert);
#else
msg_warn("TLS has been selected but TLS support is not compiled in");
#endif
}
flush_init();
}
static void post_jail_init(char *unused_name, char **unused_argv)
{
smtpd_input_transp_mask =
input_transp_mask(VAR_INPUT_TRANSP, var_input_transp);
if (var_queue_minfree > 0
&& var_message_limit > 0
&& var_queue_minfree / 1.5 < var_message_limit)
msg_warn("%s(%lu) should be at least 1.5*%s(%lu)",
VAR_QUEUE_MINFREE, (unsigned long) var_queue_minfree,
VAR_MESSAGE_LIMIT, (unsigned long) var_message_limit);
#ifdef SNAPSHOT
if (var_smtpd_crate_limit || var_smtpd_cconn_limit)
anvil_clnt = anvil_clnt_create();
#endif
}
int main(int argc, char **argv)
{
static CONFIG_INT_TABLE int_table[] = {
VAR_SMTPD_RCPT_LIMIT, DEF_SMTPD_RCPT_LIMIT, &var_smtpd_rcpt_limit, 1, 0,
VAR_SMTPD_SOFT_ERLIM, DEF_SMTPD_SOFT_ERLIM, &var_smtpd_soft_erlim, 1, 0,
VAR_SMTPD_HARD_ERLIM, DEF_SMTPD_HARD_ERLIM, &var_smtpd_hard_erlim, 1, 0,
VAR_QUEUE_MINFREE, DEF_QUEUE_MINFREE, &var_queue_minfree, 0, 0,
VAR_UNK_CLIENT_CODE, DEF_UNK_CLIENT_CODE, &var_unk_client_code, 0, 0,
VAR_BAD_NAME_CODE, DEF_BAD_NAME_CODE, &var_bad_name_code, 0, 0,
VAR_UNK_NAME_CODE, DEF_UNK_NAME_CODE, &var_unk_name_code, 0, 0,
VAR_UNK_ADDR_CODE, DEF_UNK_ADDR_CODE, &var_unk_addr_code, 0, 0,
VAR_RELAY_CODE, DEF_RELAY_CODE, &var_relay_code, 0, 0,
VAR_MAPS_RBL_CODE, DEF_MAPS_RBL_CODE, &var_maps_rbl_code, 0, 0,
VAR_ACCESS_MAP_CODE, DEF_ACCESS_MAP_CODE, &var_access_map_code, 0, 0,
VAR_REJECT_CODE, DEF_REJECT_CODE, &var_reject_code, 0, 0,
VAR_DEFER_CODE, DEF_DEFER_CODE, &var_defer_code, 0, 0,
VAR_NON_FQDN_CODE, DEF_NON_FQDN_CODE, &var_non_fqdn_code, 0, 0,
VAR_SMTPD_JUNK_CMD, DEF_SMTPD_JUNK_CMD, &var_smtpd_junk_cmd_limit, 1, 0,
VAR_SMTPD_RCPT_OVERLIM, DEF_SMTPD_RCPT_OVERLIM, &var_smtpd_rcpt_overlim, 1, 0,
VAR_SMTPD_HIST_THRSH, DEF_SMTPD_HIST_THRSH, &var_smtpd_hist_thrsh, 1, 0,
VAR_UNV_FROM_CODE, DEF_UNV_FROM_CODE, &var_unv_from_code, 0, 0,
VAR_UNV_RCPT_CODE, DEF_UNV_RCPT_CODE, &var_unv_rcpt_code, 0, 0,
VAR_MUL_RCPT_CODE, DEF_MUL_RCPT_CODE, &var_mul_rcpt_code, 0, 0,
VAR_LOCAL_RCPT_CODE, DEF_LOCAL_RCPT_CODE, &var_local_rcpt_code, 0, 0,
VAR_VIRT_ALIAS_CODE, DEF_VIRT_ALIAS_CODE, &var_virt_alias_code, 0, 0,
VAR_VIRT_MAILBOX_CODE, DEF_VIRT_MAILBOX_CODE, &var_virt_mailbox_code, 0, 0,
VAR_RELAY_RCPT_CODE, DEF_RELAY_RCPT_CODE, &var_relay_rcpt_code, 0, 0,
VAR_VERIFY_POLL_COUNT, DEF_VERIFY_POLL_COUNT, &var_verify_poll_count, 1, 0,
#ifdef SNAPSHOT
VAR_SMTPD_CRATE_LIMIT, DEF_SMTPD_CRATE_LIMIT, &var_smtpd_crate_limit, 0, 0,
VAR_SMTPD_CCONN_LIMIT, DEF_SMTPD_CCONN_LIMIT, &var_smtpd_cconn_limit, 0, 0,
#endif
#ifdef USE_SSL
VAR_SMTPD_TLS_CCERT_VD, DEF_SMTPD_TLS_CCERT_VD, &var_smtpd_tls_ccert_vd, 0, 0,
#endif
0,
};
static CONFIG_TIME_TABLE time_table[] = {
VAR_SMTPD_TMOUT, DEF_SMTPD_TMOUT, &var_smtpd_tmout, 1, 0,
VAR_SMTPD_ERR_SLEEP, DEF_SMTPD_ERR_SLEEP, &var_smtpd_err_sleep, 0, 0,
VAR_SMTPD_PROXY_TMOUT, DEF_SMTPD_PROXY_TMOUT, &var_smtpd_proxy_tmout, 1, 0,
VAR_VERIFY_POLL_DELAY, DEF_VERIFY_POLL_DELAY, &var_verify_poll_delay, 1, 0,
VAR_SMTPD_POLICY_TMOUT, DEF_SMTPD_POLICY_TMOUT, &var_smtpd_policy_tmout, 1, 0,
VAR_SMTPD_POLICY_IDLE, DEF_SMTPD_POLICY_IDLE, &var_smtpd_policy_idle, 1, 0,
VAR_SMTPD_POLICY_TTL, DEF_SMTPD_POLICY_TTL, &var_smtpd_policy_ttl, 1, 0,
#ifdef USE_SSL
VAR_SMTPD_STARTTLS_TMOUT, DEF_SMTPD_STARTTLS_TMOUT, &var_smtpd_starttls_tmout, 1, 0,
#endif
0,
};
static CONFIG_BOOL_TABLE bool_table[] = {
VAR_HELO_REQUIRED, DEF_HELO_REQUIRED, &var_helo_required,
VAR_SMTPD_DELAY_REJECT, DEF_SMTPD_DELAY_REJECT, &var_smtpd_delay_reject,
VAR_STRICT_RFC821_ENV, DEF_STRICT_RFC821_ENV, &var_strict_rfc821_env,
VAR_DISABLE_VRFY_CMD, DEF_DISABLE_VRFY_CMD, &var_disable_vrfy_cmd,
VAR_ALLOW_UNTRUST_ROUTE, DEF_ALLOW_UNTRUST_ROUTE, &var_allow_untrust_route,
VAR_SMTPD_SASL_ENABLE, DEF_SMTPD_SASL_ENABLE, &var_smtpd_sasl_enable,
VAR_BROKEN_AUTH_CLNTS, DEF_BROKEN_AUTH_CLNTS, &var_broken_auth_clients,
VAR_SHOW_UNK_RCPT_TABLE, DEF_SHOW_UNK_RCPT_TABLE, &var_show_unk_rcpt_table,
VAR_SMTPD_REJ_UNL_FROM, DEF_SMTPD_REJ_UNL_FROM, &var_smtpd_rej_unl_from,
VAR_SMTPD_REJ_UNL_RCPT, DEF_SMTPD_REJ_UNL_RCPT, &var_smtpd_rej_unl_rcpt,
VAR_SMTPD_USE_TLS, DEF_SMTPD_USE_TLS, &var_smtpd_use_tls,
VAR_SMTPD_ENFORCE_TLS, DEF_SMTPD_ENFORCE_TLS, &var_smtpd_enforce_tls,
VAR_SMTPD_TLS_WRAPPER, DEF_SMTPD_TLS_WRAPPER, &var_smtpd_tls_wrappermode,
#ifdef USE_SSL
VAR_SMTPD_TLS_AUTH_ONLY, DEF_SMTPD_TLS_AUTH_ONLY, &var_smtpd_tls_auth_only,
VAR_SMTPD_TLS_ACERT, DEF_SMTPD_TLS_ACERT, &var_smtpd_tls_ask_ccert,
VAR_SMTPD_TLS_RCERT, DEF_SMTPD_TLS_RCERT, &var_smtpd_tls_req_ccert,
VAR_SMTPD_TLS_RECHEAD, DEF_SMTPD_TLS_RECHEAD, &var_smtpd_tls_received_header,
#endif
#ifdef __APPLE__
VAR_SMTPD_USE_PW_SERVER, DEV_SMTPD_USE_PW_SERVER, &var_smtpd_use_pw_server,
#endif
0,
};
static CONFIG_STR_TABLE str_table[] = {
VAR_SMTPD_BANNER, DEF_SMTPD_BANNER, &var_smtpd_banner, 1, 0,
VAR_NOTIFY_CLASSES, DEF_NOTIFY_CLASSES, &var_notify_classes, 0, 0,
VAR_CLIENT_CHECKS, DEF_CLIENT_CHECKS, &var_client_checks, 0, 0,
VAR_HELO_CHECKS, DEF_HELO_CHECKS, &var_helo_checks, 0, 0,
VAR_MAIL_CHECKS, DEF_MAIL_CHECKS, &var_mail_checks, 0, 0,
VAR_RCPT_CHECKS, DEF_RCPT_CHECKS, &var_rcpt_checks, 0, 0,
VAR_ETRN_CHECKS, DEF_ETRN_CHECKS, &var_etrn_checks, 0, 0,
VAR_DATA_CHECKS, DEF_DATA_CHECKS, &var_data_checks, 0, 0,
VAR_MAPS_RBL_DOMAINS, DEF_MAPS_RBL_DOMAINS, &var_maps_rbl_domains, 0, 0,
VAR_RBL_REPLY_MAPS, DEF_RBL_REPLY_MAPS, &var_rbl_reply_maps, 0, 0,
VAR_ERROR_RCPT, DEF_ERROR_RCPT, &var_error_rcpt, 1, 0,
VAR_REST_CLASSES, DEF_REST_CLASSES, &var_rest_classes, 0, 0,
VAR_CANONICAL_MAPS, DEF_CANONICAL_MAPS, &var_canonical_maps, 0, 0,
VAR_RCPT_CANON_MAPS, DEF_RCPT_CANON_MAPS, &var_rcpt_canon_maps, 0, 0,
VAR_VIRT_ALIAS_MAPS, DEF_VIRT_ALIAS_MAPS, &var_virt_alias_maps, 0, 0,
VAR_VIRT_MAILBOX_MAPS, DEF_VIRT_MAILBOX_MAPS, &var_virt_mailbox_maps, 0, 0,
VAR_ALIAS_MAPS, DEF_ALIAS_MAPS, &var_alias_maps, 0, 0,
VAR_LOCAL_RCPT_MAPS, DEF_LOCAL_RCPT_MAPS, &var_local_rcpt_maps, 0, 0,
VAR_SMTPD_SASL_OPTS, DEF_SMTPD_SASL_OPTS, &var_smtpd_sasl_opts, 0, 0,
VAR_SMTPD_SASL_APPNAME, DEF_SMTPD_SASL_APPNAME, &var_smtpd_sasl_appname, 1, 0,
VAR_SMTPD_SASL_REALM, DEF_SMTPD_SASL_REALM, &var_smtpd_sasl_realm, 0, 0,
VAR_SMTPD_SASL_EXCEPTIONS_NETWORKS, DEF_SMTPD_SASL_EXCEPTIONS_NETWORKS, &var_smtpd_sasl_exceptions_networks, 0, 0,
VAR_FILTER_XPORT, DEF_FILTER_XPORT, &var_filter_xport, 0, 0,
VAR_PERM_MX_NETWORKS, DEF_PERM_MX_NETWORKS, &var_perm_mx_networks, 0, 0,
VAR_SMTPD_SND_AUTH_MAPS, DEF_SMTPD_SND_AUTH_MAPS, &var_smtpd_snd_auth_maps, 0, 0,
VAR_SMTPD_NOOP_CMDS, DEF_SMTPD_NOOP_CMDS, &var_smtpd_noop_cmds, 0, 0,
VAR_SMTPD_NULL_KEY, DEF_SMTPD_NULL_KEY, &var_smtpd_null_key, 0, 0,
VAR_RELAY_RCPT_MAPS, DEF_RELAY_RCPT_MAPS, &var_relay_rcpt_maps, 0, 0,
VAR_VERIFY_SENDER, DEF_VERIFY_SENDER, &var_verify_sender, 0, 0,
VAR_VERP_CLIENTS, DEF_VERP_CLIENTS, &var_verp_clients, 0, 0,
VAR_SMTPD_PROXY_FILT, DEF_SMTPD_PROXY_FILT, &var_smtpd_proxy_filt, 0, 0,
VAR_SMTPD_PROXY_EHLO, DEF_SMTPD_PROXY_EHLO, &var_smtpd_proxy_ehlo, 0, 0,
VAR_INPUT_TRANSP, DEF_INPUT_TRANSP, &var_input_transp, 0, 0,
VAR_XCLIENT_HOSTS, DEF_XCLIENT_HOSTS, &var_xclient_hosts, 0, 0,
VAR_XFORWARD_HOSTS, DEF_XFORWARD_HOSTS, &var_xforward_hosts, 0, 0,
#ifdef SNAPSHOT
VAR_SMTPD_HOGGERS, DEF_SMTPD_HOGGERS, &var_smtpd_hoggers, 0, 0,
#endif
#ifdef USE_SSL
VAR_RELAY_CCERTS, DEF_RELAY_CCERTS, &var_relay_ccerts, 0, 0,
VAR_SMTPD_SASL_TLS_OPTS, DEF_SMTPD_SASL_TLS_OPTS, &var_smtpd_sasl_tls_opts, 0, 0,
#endif
#ifdef __APPLE__
VAR_SMTPD_PW_SERVER_OPTS, DEF_SMTPD_PW_SERVER_OPTS, &var_smtpd_pw_server_opts, 0, 0,
#endif
0,
};
static CONFIG_RAW_TABLE raw_table[] = {
VAR_SMTPD_EXP_FILTER, DEF_SMTPD_EXP_FILTER, &var_smtpd_exp_filter, 1, 0,
VAR_DEF_RBL_REPLY, DEF_DEF_RBL_REPLY, &var_def_rbl_reply, 1, 0,
0,
};
single_server_main(argc, argv, smtpd_service,
MAIL_SERVER_INT_TABLE, int_table,
MAIL_SERVER_STR_TABLE, str_table,
MAIL_SERVER_RAW_TABLE, raw_table,
MAIL_SERVER_BOOL_TABLE, bool_table,
MAIL_SERVER_TIME_TABLE, time_table,
MAIL_SERVER_PRE_INIT, pre_jail_init,
MAIL_SERVER_PRE_ACCEPT, pre_accept,
MAIL_SERVER_POST_INIT, post_jail_init,
0);
}