* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) University of Cambridge 1995 - 2012 */
+/* Copyright (c) University of Cambridge 1995 - 2014 */
/* See the file NOTICE for conditions of use and distribution. */
/* Code for receiving a message and setting up spool files. */
extern int dcc_ok;
#endif
+#ifdef EXPERIMENTAL_DMARC
+#include "dmarc.h"
+#endif /* EXPERIMENTAL_DMARC */
+
/*************************************************
* Local static variables *
*************************************************/
void
receive_bomb_out(uschar *reason, uschar *msg)
{
+ static BOOL already_bombing_out;
+/* The smtp_notquit_exit() below can call ACLs which can trigger recursive
+timeouts, if someone has something slow in their quit ACL. Since the only
+things we should be doing are to close down cleanly ASAP, on the second
+pass we also close down stuff that might be opened again, before bypassing
+the ACL call and exiting. */
+
/* If spool_name is set, it contains the name of the data file that is being
written. Unlink it before closing so that it cannot be picked up by a delivery
process. Ensure that any header file is also removed. */
-if (spool_name[0] != 0)
+if (spool_name[0] != '\0')
{
Uunlink(spool_name);
spool_name[Ustrlen(spool_name) - 1] = 'H';
Uunlink(spool_name);
+ spool_name[0] = '\0';
}
/* Now close the file if it is open, either as a fd or a stream. */
-if (data_file != NULL) (void)fclose(data_file);
- else if (data_fd >= 0) (void)close(data_fd);
+if (data_file != NULL)
+ {
+ (void)fclose(data_file);
+ data_file = NULL;
+} else if (data_fd >= 0) {
+ (void)close(data_fd);
+ data_fd = -1;
+ }
/* Attempt to close down an SMTP connection tidily. For non-batched SMTP, call
smtp_notquit_exit(), which runs the NOTQUIT ACL, if present, and handles the
SMTP response. */
-if (smtp_input)
+if (!already_bombing_out)
{
- if (smtp_batched_input)
- moan_smtp_batch(NULL, "421 %s - message abandoned", msg); /* No return */
- smtp_notquit_exit(reason, US"421", US"%s %s - closing connection.",
- smtp_active_hostname, msg);
+ already_bombing_out = TRUE;
+ if (smtp_input)
+ {
+ if (smtp_batched_input)
+ moan_smtp_batch(NULL, "421 %s - message abandoned", msg); /* No return */
+ smtp_notquit_exit(reason, US"421", US"%s %s - closing connection.",
+ smtp_active_hostname, msg);
+ }
}
/* Exit from the program (non-BSMTP cases) */
/*************************************************
* Send user response message *
*************************************************/
-
+
/* This function is passed a default response code and a user message. It calls
smtp_message_code() to check and possibly modify the response code, and then
calls smtp_respond() to transmit the response. I put this into a function
just to avoid a lot of repetition.
-
-Arguments:
+
+Arguments:
code the response code
user_msg the user message
Returns: nothing
-*/
-
-static void
+*/
+
+#ifdef EXPERIMENTAL_PRDR
+static void
smtp_user_msg(uschar *code, uschar *user_msg)
-{
+{
int len = 3;
smtp_message_code(&code, &len, &user_msg, NULL);
smtp_respond(code, len, TRUE, user_msg);
-}
-
-
-
-
+}
+#endif
+
+
+
+
/*************************************************
* Remove a recipient from the list *
*/
static void
-add_acl_headers(uschar *acl_name)
+add_acl_headers(int where, uschar *acl_name)
{
header_line *h, *next;
header_line *last_received = NULL;
+switch(where)
+ {
+ case ACL_WHERE_DKIM:
+ case ACL_WHERE_MIME:
+ case ACL_WHERE_DATA: if (cutthrough_fd >= 0 && (acl_removed_headers || acl_added_headers))
+ {
+ log_write(0, LOG_MAIN|LOG_PANIC, "Header modification in data ACLs"
+ "will not take effect on cutthrough deliveries");
+ return;
+ }
+ }
+
if (acl_removed_headers != NULL)
{
DEBUG(D_receive|D_acl) debug_printf(">>Headers removed by %s ACL:\n", acl_name);
}
END_MIME_ACL:
-add_acl_headers(US"MIME");
+add_acl_headers(ACL_WHERE_MIME, US"MIME");
if (rc == DISCARD)
{
recipients_count = 0;
#ifdef EXPERIMENTAL_DCC
dcc_ok = 0;
#endif
- if (smtp_handle_acl_fail(ACL_WHERE_MIME, rc, user_msg, log_msg) != 0)
+ if (smtp_input && smtp_handle_acl_fail(ACL_WHERE_MIME, rc, user_msg, log_msg) != 0) {
*smtp_yield_ptr = FALSE; /* No more messsages after dropped connection */
- *smtp_reply_ptr = US""; /* Indicate reply already sent */
+ *smtp_reply_ptr = US""; /* Indicate reply already sent */
+ }
message_id[0] = 0; /* Indicate no message accepted */
return FALSE; /* Cause skip to end of receive function */
}
uschar *resent_prefix = US"";
uschar *blackholed_by = NULL;
uschar *blackhole_log_msg = US"";
-int cutthrough_done = 0;
+enum {NOT_TRIED, TMP_REJ, PERM_REJ, ACCEPTED} cutthrough_done = NOT_TRIED;
flock_t lock_data;
error_block *bad_addresses = NULL;
header_line *msgid_header = NULL;
header_line *received_header;
+#ifdef EXPERIMENTAL_DMARC
+int dmarc_up = 0;
+#endif /* EXPERIMENTAL_DMARC */
+
/* Variables for use when building the Received: header. */
uschar *timestamp;
if (smtp_input && !smtp_batched_input && !dkim_disable_verify) dkim_exim_verify_init();
#endif
+#ifdef EXPERIMENTAL_DMARC
+/* initialize libopendmarc */
+dmarc_up = dmarc_init();
+#endif
+
/* Remember the time of reception. Exim uses time+pid for uniqueness of message
ids, and fractions of a second are required. See the comments that precede the
message id creation below. */
}
}
-
/* If there are any rewriting rules, apply them to the sender address, unless
it has already been rewritten as part of verification for SMTP input. */
goto TIDYUP; /* Skip to end of function */
}
received_header_gen();
- add_acl_headers(US"MAIL or RCPT");
+ add_acl_headers(ACL_WHERE_RCPT, US"MAIL or RCPT");
(void) cutthrough_headers_send();
}
-
+
/* Open a new spool file for the data portion of the message. We need
to access it both via a file descriptor and a stream. Try to make the
/* If an ACL from any RCPT commands set up any warning headers to add, do so
now, before running the DATA ACL. */
- add_acl_headers(US"MAIL or RCPT");
+ add_acl_headers(ACL_WHERE_RCPT, US"MAIL or RCPT");
}
else
message_body_size = (fstat(data_fd, &statbuf) == 0)?
uschar seen_item_buf[256];
uschar *seen_items_list = seen_items;
int seen_this_item = 0;
-
+
while ((seen_item = string_nextinlist(&seen_items_list, &sep,
seen_item_buf,
sizeof(seen_item_buf))) != NULL)
{
seen_this_item = 1;
break;
- }
+ }
}
if (seen_this_item > 0)
debug_printf("acl_smtp_dkim: skipping signer %s, already seen\n", item);
continue;
}
-
+
seen_items = string_append(seen_items,&seen_items_size,&seen_items_offset,1,":");
}
break;
}
}
- add_acl_headers(US"DKIM");
+ add_acl_headers(ACL_WHERE_DKIM, US"DKIM");
if (rc == DISCARD)
{
recipients_count = 0;
goto TIDYUP;
#endif /* WITH_CONTENT_SCAN */
+#ifdef EXPERIMENTAL_DMARC
+ dmarc_up = dmarc_store_data(from_header);
+#endif /* EXPERIMENTAL_DMARC */
+
#ifdef EXPERIMENTAL_PRDR
if (prdr_requested && recipients_count > 1 && acl_smtp_data_prdr != NULL )
{
}
if (log_msg) log_write(0, LOG_MAIN, "PRDR %s %s", addr, log_msg);
else if (user_msg) log_write(0, LOG_MAIN, "PRDR %s %s", addr, user_msg);
- else log_write(0, LOG_MAIN, CS msg);
+ else log_write(0, LOG_MAIN, "%s", CS msg);
if (rc != OK) { receive_remove_recipient(addr); c--; }
}
if (acl_smtp_data != NULL && recipients_count > 0)
{
rc = acl_check(ACL_WHERE_DATA, NULL, acl_smtp_data, &user_msg, &log_msg);
- add_acl_headers(US"DATA");
+ add_acl_headers(ACL_WHERE_DATA, US"DATA");
if (rc == DISCARD)
{
recipients_count = 0;
/* Does not return */
}
}
- add_acl_headers(US"non-SMTP");
+ add_acl_headers(ACL_WHERE_NOTSMTP, US"non-SMTP");
}
}
if (prdr_requested)
s = string_append(s, &size, &sptr, 1, US" PRDR");
#endif
+#ifdef EXPERIMENTAL_PROXY
+if (proxy_session &&
+ (log_extra_selector & LX_proxy) != 0)
+ {
+ s = string_append(s, &size, &sptr, 2, US" PRX=", proxy_host_address);
+ }
+#endif
sprintf(CS big_buffer, "%d", msg_size);
s = string_append(s, &size, &sptr, 2, US" S=", big_buffer);
XXX We do not handle queue-only, freezing, or blackholes.
*/
-cutthrough_done = 0;
if(cutthrough_fd >= 0)
{
uschar * msg= cutthrough_finaldot(); /* Ask the target system to accept the messsage */
switch(msg[0])
{
case '2': /* Accept. Do the same to the source; dump any spoolfiles. */
- cutthrough_done = 3;
+ cutthrough_done = ACCEPTED;
break; /* message_id needed for SMTP accept below */
-
+
default: /* Unknown response, or error. Treat as temp-reject. */
case '4': /* Temp-reject. Keep spoolfiles and accept. */
- cutthrough_done = 1; /* Avoid the usual immediate delivery attempt */
+ cutthrough_done = TMP_REJ; /* Avoid the usual immediate delivery attempt */
break; /* message_id needed for SMTP accept below */
-
+
case '5': /* Perm-reject. Do the same to the source. Dump any spoolfiles */
smtp_reply= msg; /* Pass on the exact error */
- cutthrough_done = 2;
+ cutthrough_done = PERM_REJ;
break;
}
}
switch (cutthrough_done)
{
- case 3: log_write(0, LOG_MAIN, "Completed"); /* Delivery was done */
- case 2: { /* Delete spool files */
+ case ACCEPTED: log_write(0, LOG_MAIN, "Completed");/* Delivery was done */
+ case PERM_REJ: { /* Delete spool files */
sprintf(CS spool_name, "%s/input/%s/%s-D", spool_directory,
message_subdir, message_id);
Uunlink(spool_name);
message_subdir, message_id);
Uunlink(spool_name);
}
- case 1: message_id[0] = 0; /* Prevent a delivery from starting */
+ case TMP_REJ: message_id[0] = 0; /* Prevent a delivery from starting */
default:break;
}
cutthrough_delivery = FALSE;