HELO_CMD, EHLO_CMD, DATA_CMD, /* These are listed in the pipelining */
VRFY_CMD, EXPN_CMD, NOOP_CMD, /* RFC as requiring synchronization */
- ETRN_CMD, /* This by analogy with TURN from the RFC */
+ ATRN_CMD, ETRN_CMD, /* This by analogy with TURN from the RFC */
STARTTLS_CMD, /* Required by the STARTTLS RFC */
TLS_AUTH_CMD, /* auto-command at start of SSL */
#ifdef EXPERIMENTAL_XCLIENT
{ "bdat", sizeof("bdat")-1, BDAT_CMD, TRUE, TRUE },
{ "quit", sizeof("quit")-1, QUIT_CMD, FALSE, TRUE },
{ "noop", sizeof("noop")-1, NOOP_CMD, TRUE, FALSE },
+ { "atrn", sizeof("atrn")-1, ATRN_CMD, TRUE, FALSE },
{ "etrn", sizeof("etrn")-1, ETRN_CMD, TRUE, FALSE },
{ "vrfy", sizeof("vrfy")-1, VRFY_CMD, TRUE, FALSE },
{ "expn", sizeof("expn")-1, EXPN_CMD, TRUE, FALSE },
+#ifndef DISABLE_DKIM
+/* Feed received message data to the dkim module */
+/*XXX maybe a global dkim_info? */
+void
+smtp_verify_feed(const uschar * s, unsigned n)
+{
+static misc_module_info * dkim_mi = NULL;
+typedef void (*fn_t)(const uschar *, int);
+
+if (!dkim_mi && !(dkim_mi = misc_mod_findonly(US"dkim")))
+ return;
+
+(((fn_t *) dkim_mi->functions)[DKIM_VERIFY_FEED]) (s, n);
+}
+#endif
+
+
/* Refill the buffer, and notify DKIM verification code.
Return false for error or EOF.
*/
return FALSE;
}
#ifndef DISABLE_DKIM
-dkim_exim_verify_feed(smtp_inbuffer, rc);
+smtp_verify_feed(smtp_inbuffer, rc);
#endif
smtp_inend = smtp_inbuffer + rc;
smtp_inptr = smtp_inbuffer;
if (n > lim)
n = lim;
if (n > 0)
- dkim_exim_verify_feed(smtp_inptr, n);
+ smtp_verify_feed(smtp_inptr, n);
#endif
}
uschar * user_msg = NULL;
uschar * log_msg;
-for(;;)
- {
#ifndef DISABLE_DKIM
- unsigned dkim_save;
+misc_module_info * dkim_info = misc_mod_findonly(US"dkim");
+typedef void (*dkim_pause_t)(BOOL);
+dkim_pause_t dkim_pause;
+
+dkim_pause = dkim_info
+ ? ((dkim_pause_t *) dkim_info->functions)[DKIM_VERIFY_PAUSE] : NULL;
#endif
+for(;;)
+ {
+
if (chunking_data_left > 0)
return lwr_receive_getc(chunking_data_left--);
bdat_pop_receive_functions();
#ifndef DISABLE_DKIM
- dkim_save = dkim_collect_input;
- dkim_collect_input = 0;
+ if (dkim_pause) dkim_pause(TRUE);
#endif
/* Unless PIPELINING was offered, there should be no next command
if (chunking_state == CHUNKING_LAST)
{
#ifndef DISABLE_DKIM
- dkim_collect_input = dkim_save;
- dkim_exim_verify_feed(NULL, 0); /* notify EOD */
- dkim_collect_input = 0;
+ smtp_verify_feed(NULL, 0); /* notify EOD */
#endif
return EOD;
}
bdat_push_receive_functions();
#ifndef DISABLE_DKIM
- dkim_collect_input = dkim_save;
+ if (dkim_pause) dkim_pause(FALSE);
#endif
break; /* to top of main loop */
}
bmi_verdicts = NULL;
#endif
dnslist_domain = dnslist_matched = NULL;
-#ifdef SUPPORT_SPF
-spf_header_comment = spf_received = spf_result = spf_smtp_comment = NULL;
-spf_result_guessed = FALSE;
-#endif
-#ifndef DISABLE_DKIM
-dkim_cur_signer = dkim_signers =
-dkim_signing_domain = dkim_signing_selector = dkim_signatures = NULL;
-f.dkim_disable_verify = FALSE;
-dkim_collect_input = 0;
-dkim_verify_overall = dkim_verify_status = dkim_verify_reason = NULL;
-dkim_key_length = 0;
-#endif
-#ifdef SUPPORT_DMARC
-f.dmarc_has_been_checked = f.dmarc_disable_verify = f.dmarc_enable_forensic = FALSE;
-dmarc_domain_policy = dmarc_status = dmarc_status_text =
-dmarc_used_domain = NULL;
-#endif
-#ifdef EXPERIMENTAL_ARC
-arc_state = arc_state_reason = NULL;
-arc_received_instance = 0;
-#endif
+
dsn_ret = 0;
dsn_envid = NULL;
deliver_host = deliver_host_address = NULL; /* Can be set by ACL */
store_free(this);
}
+misc_mod_smtp_reset();
message_tidyup();
store_reset(reset_point);
break;
- /* The VRFY, EXPN, HELP, ETRN, and NOOP commands are ignored. */
+ /* The VRFY, EXPN, HELP, ETRN, ATRN and NOOP commands are ignored. */
- case VRFY_CMD:
- case EXPN_CMD:
- case HELP_CMD:
- case NOOP_CMD:
- case ETRN_CMD:
+ case VRFY_CMD: case EXPN_CMD: case HELP_CMD: case NOOP_CMD:
+ case ETRN_CMD: case ATRN_CMD:
#ifndef DISABLE_WELLKNOWN
case WELLKNOWN_CMD:
#endif
{
int yield = -1;
+#ifndef DISABLE_EVENT
+event_raise(event_action,
+ L_smtp_syntax_error ? US"smtp:fail:syntax" : US"smtp:fail:protocol",
+ errmess, NULL);
+#endif
+
log_write(type, LOG_MAIN, "SMTP %s error in \"%s\" %s %s",
type == L_smtp_syntax_error ? "syntax" : "protocol",
string_printing(smtp_cmd_buffer), host_and_ident(TRUE), errmess);
/* Set up globals for error messages */
-authenticator_name = au->name;
-driver_srcfile = au->srcfile;
-driver_srcline = au->srcline;
+authenticator_name = au->drinst.name;
+driver_srcfile = au->drinst.srcfile;
+driver_srcline = au->drinst.srcline;
/* Run the checking code, passing the remainder of the command line as
data. Initials the $auth<n> variables as empty. Initialize $0 empty and set
expand_nmax = 0;
expand_nlength[0] = 0; /* $0 contains nothing */
-rc = (au->info->servercode)(au, smtp_cmd_data);
+ {
+ auth_info * ai = au->drinst.info;
+ rc = (ai->servercode)(au, smtp_cmd_data);
+ }
if (au->set_id) set_id = expand_string(au->set_id);
expand_nmax = -1; /* Reset numeric variables */
for (int i = 0; i < AUTH_VARS; i++) auth_vars[i] = NULL; /* Reset $auth<n> */
if (!au->set_id || set_id) /* Complete success */
{
if (set_id) authenticated_id = string_copy_perm(set_id, TRUE);
- sender_host_authenticated = au->name;
+ sender_host_authenticated = au->drinst.name;
sender_host_auth_pubname = au->public_name;
authentication_failed = FALSE;
authenticated_fail_id = NULL; /* Impossible to already be set? */
{
cmd_list[CL_TLAU].is_mail_cmd = FALSE;
- for (auth_instance * au = auths; au; au = au->next)
- if (strcmpic(US"tls", au->driver_name) == 0)
+ for (auth_instance * au = auths; au; au = au->drinst.next)
+ if (strcmpic(US"tls", au->drinst.driver_name) == 0)
{
GET_OPTION("acl_smtp_auth");
if ( acl_smtp_auth
#ifndef DISABLE_EVENT
{
uschar * save_name = sender_host_authenticated, * logmsg;
- sender_host_authenticated = au->name;
+ sender_host_authenticated = au->drinst.name;
if ((logmsg = event_raise(event_action, US"auth:fail", s, NULL)))
log_write(0, LOG_MAIN, "%s", logmsg);
sender_host_authenticated = save_name;
auth_instance * au;
uschar * smtp_resp, * errmsg;
- for (au = auths; au; au = au->next)
+ for (au = auths; au; au = au->drinst.next)
if (strcmpic(s, au->public_name) == 0 && au->server &&
(au->advertised || f.allow_auth_unadvertised))
break;
uschar * logmsg = NULL;
#ifndef DISABLE_EVENT
{uschar * save_name = sender_host_authenticated;
- sender_host_authenticated = au->name;
+ sender_host_authenticated = au->drinst.name;
logmsg = event_raise(event_action, US"auth:fail", smtp_resp, NULL);
sender_host_authenticated = save_name;
}
log_write(0, LOG_MAIN|LOG_REJECT, "%s", logmsg);
else
log_write(0, LOG_MAIN|LOG_REJECT, "%s authenticator failed for %s: %s",
- au->name, host_and_ident(FALSE), errmsg);
+ au->drinst.name, host_and_ident(FALSE), errmsg);
}
}
else
}
}
-#ifdef SUPPORT_SPF
- /* set up SPF context */
- spf_conn_init(sender_helo_name, sender_host_address);
-#endif
+ /* For any misc-module having a connection-init routine, call it. */
+
+ if (misc_mod_conn_init(sender_helo_name, sender_host_address) != OK)
+ {
+ DEBUG(D_receive) debug_printf("A module conn-init routine failed\n");
+ done = 1;
+ break;
+ }
/* Apply an ACL check if one is defined; afterwards, recheck
synchronization in case the client started sending in a delay. */
fl.dsn_advertised = TRUE;
}
- /* Advertise ETRN/VRFY/EXPN if there's are ACL checking whether a host is
- permitted to issue them; a check is made when any host actually tries. */
+ /* Advertise ATRN/ETRN/VRFY/EXPN if there's are ACL checking whether a
+ host is permitted to issue them; a check is made when any host actually
+ tries. */
+ GET_OPTION("acl_smtp_atrn");
+ if (acl_smtp_atrn)
+ {
+ const uschar * s = expand_cstring(acl_smtp_atrn);
+ if (s && *s)
+ {
+ g = string_catn(g, smtp_code, 3);
+ g = string_catn(g, US"-ATRN\r\n", 7);
+ }
+ }
GET_OPTION("acl_smtp_etrn");
if (acl_smtp_etrn)
{
)
{
BOOL first = TRUE;
- for (auth_instance * au = auths; au; au = au->next)
+ for (auth_instance * au = auths; au; au = au->drinst.next)
{
au->advertised = FALSE;
if (au->server)
{
DEBUG(D_auth+D_expand) debug_printf_indent(
"Evaluating advertise_condition for %s %s athenticator\n",
- au->name, au->public_name);
+ au->drinst.name, au->public_name);
if ( !au->advertise_condition
- || expand_check_condition(au->advertise_condition, au->name,
- US"authenticator")
+ || expand_check_condition(au->advertise_condition,
+ au->drinst.name, US"authenticator")
)
{
int saveptr;
if (authenticated_by == NULL ||
authenticated_by->mail_auth_condition == NULL ||
expand_check_condition(authenticated_by->mail_auth_condition,
- authenticated_by->name, US"authenticator"))
+ authenticated_by->drinst.name, US"authenticator"))
break; /* Accept the AUTH */
ignore_msg = US"server_mail_auth_condition failed";
#endif
smtp_printf(" HELO EHLO MAIL RCPT DATA BDAT", SP_MORE);
smtp_printf(" NOOP QUIT RSET HELP", SP_MORE);
+ if (acl_smtp_atrn) smtp_printf(" ATRN", SP_MORE);
if (acl_smtp_etrn) smtp_printf(" ETRN", SP_MORE);
if (acl_smtp_expn) smtp_printf(" EXPN", SP_MORE);
if (acl_smtp_vrfy) smtp_printf(" VRFY", SP_MORE);
break;
+ case ATRN_CMD:
+ {
+ uschar * exp_acl = NULL;
+ const uschar * list;
+ int sep = 0;
+ gstring * g = NULL;
+ qrunner q = {0};
+
+ HAD(SCH_ATRN);
+ /*XXX could we used a cached value for "advertised"? */
+ GET_OPTION("acl_smtp_atrn");
+ if (acl_smtp_atrn
+ && (exp_acl = expand_string(acl_smtp_atrn)) && !*exp_acl)
+ exp_acl = NULL;
+ if (!exp_acl || !authenticated_id || sender_address)
+ {
+ done = synprot_error(L_smtp_protocol_error,
+ !exp_acl ? 502 : !authenticated_id ? 530 : 503,
+ NULL,
+ !exp_acl ? US"ATRN command used when not advertised"
+ : !authenticated_id ? US"ATRN is not permitted without authentication"
+ : US"ATRN is not permitted inside a transaction"
+ );
+ break;
+ }
+
+ log_write(L_etrn, LOG_MAIN, "ATRN '%s' received from %s",
+ smtp_cmd_argument, host_and_ident(FALSE));
+
+ if ( (rc = acl_check(ACL_WHERE_ATRN, NULL, exp_acl, &user_msg, &log_msg))
+ != OK)
+ {
+ done = smtp_handle_acl_fail(ACL_WHERE_ATRN, rc, user_msg, log_msg);
+ break;
+ }
+
+ /* want to do a qrun for the given domain(s), using the already open channel.
+ TODO: alternate named queue
+ TODO: docs
+
+ /* ACK the command, record the connection details
+ and turn the line around */
+
+ smtp_printf("250 ODMR server turning line around\r\n", SP_NO_MORE);
+ atrn_host = string_sprintf("[%s]:%d",
+ sender_host_address, sender_host_port);
+
+#ifndef DISABLE_TLS
+ if (tls_in.active.sock >= 0)
+ tls_turnaround(0, sender_host_address, sender_host_port);
+#endif
+ fflush(smtp_out);
+ force_fd(fileno(smtp_in), 0);
+ smtp_in = smtp_out = NULL;
+
+ /* Set up a onetime queue run, filtering for messages with the
+ given domains. Later filtering will leave out addresses for other domains
+ on these messages. */
+
+ continue_transport = US"ATRN-client";
+ continue_hostname = continue_host_address = sender_host_address;
+
+ q.next_tick = time(NULL);
+ q.run_max = 1;
+ q.queue_2stage = TRUE;
+
+ /* Convert the domainlist to a regex, as the existing queue-selection
+ facilities support that but not a list */
+
+ list = atrn_domains;
+ for (const uschar * ele; ele = string_nextinlist(&list, &sep, NULL, 0); )
+ g = string_append_listele(g, '|', ele);
+ deliver_selectstring = string_sprintf("@(%Y)", g);
+ f.deliver_selectstring_regex = TRUE;
+
+ single_queue_run(&q , NULL, NULL);
+ exim_exit(EXIT_SUCCESS);
+ }
+
case ETRN_CMD:
HAD(SCH_ETRN);
if (sender_address)