*************************************************/
/* Copyright (c) University of Cambridge 1995 - 2018 */
+/* Copyright (c) The Exim Maintainers 2020 */
/* See the file NOTICE for conditions of use and distribution. */
/* The main code for delivering a message. */
deliver_localpart = addr->local_part;
deliver_localpart_prefix = addr->prefix;
+ deliver_localpart_prefix_v = addr->prefix_v;
deliver_localpart_suffix = addr->suffix;
+ deliver_localpart_suffix_v = addr->suffix_v;
for (addr_orig = addr; addr_orig->parent; addr_orig = addr_orig->parent) ;
deliver_domain_orig = addr_orig->domain;
else if (deliver_localpart[0] == '|') address_pipe = addr->local_part;
deliver_localpart = addr->parent->local_part;
deliver_localpart_prefix = addr->parent->prefix;
+ deliver_localpart_prefix_v = addr->parent->prefix_v;
deliver_localpart_suffix = addr->parent->suffix;
+ deliver_localpart_suffix_v = addr->parent->suffix_v;
}
}
addr2->transport_return = addr->transport_return;
addr2->basic_errno = addr->basic_errno;
addr2->more_errno = addr->more_errno;
- addr2->delivery_usec = addr->delivery_usec;
+ addr2->delivery_time = addr->delivery_time;
addr2->special_action = addr->special_action;
addr2->message = addr->message;
addr2->user_message = addr->user_message;
{
if (testflag(addr, af_pipelining))
g = string_catn(g, US" L", 2);
-#ifdef SUPPORT_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
if (testflag(addr, af_early_pipe))
g = string_catn(g, US"*", 1);
#endif
string_timesince(&received_time));
if (LOGGING(deliver_time))
- {
- struct timeval diff = {.tv_sec = addr->more_errno, .tv_usec = addr->delivery_usec};
- g = string_append(g, 2, US" DT=", string_timediff(&diff));
- }
+ g = string_append(g, 2, US" DT=", string_timediff(&addr->delivery_time));
/* string_cat() always leaves room for the terminator. Release the
store we used to build the line after writing it. */
}
}
+if (LOGGING(deliver_time))
+ g = string_append(g, 2, US" DT=", string_timediff(&addr->delivery_time));
+
if (addr->message)
g = string_append(g, 2, US": ", addr->message);
if (addr->message)
g = string_append(g, 2, US": ", addr->message);
+if (LOGGING(deliver_time))
+ g = string_append(g, 2, US" DT=", string_timediff(&addr->delivery_time));
+
(void) string_from_gstring(g);
/* Do the logging. For the message log, "routing failed" for those cases,
tls_out.peercert = addr->peercert;
addr->peercert = NULL;
+ tls_out.ver = addr->tlsver;
tls_out.cipher = addr->cipher;
tls_out.peerdn = addr->peerdn;
tls_out.ocsp = addr->ocsp;
#ifndef DISABLE_TLS
tls_free_cert(&tls_out.ourcert);
tls_free_cert(&tls_out.peercert);
+ tls_out.ver = NULL;
tls_out.cipher = NULL;
tls_out.peerdn = NULL;
tls_out.ocsp = OCSP_NOT_REQ;
search_tidyup();
-if ((pid = fork()) == 0)
+if ((pid = exim_fork(US"delivery-local")) == 0)
{
BOOL replicate = TRUE;
|| (ret = write(pfd[pipe_write], &addr2->flags, sizeof(addr2->flags))) != sizeof(addr2->flags)
|| (ret = write(pfd[pipe_write], &addr2->basic_errno, sizeof(int))) != sizeof(int)
|| (ret = write(pfd[pipe_write], &addr2->more_errno, sizeof(int))) != sizeof(int)
- || (ret = write(pfd[pipe_write], &addr2->delivery_usec, sizeof(int))) != sizeof(int)
+ || (ret = write(pfd[pipe_write], &addr2->delivery_time, sizeof(struct timeval))) != sizeof(struct timeval)
|| (ret = write(pfd[pipe_write], &addr2->special_action, sizeof(int))) != sizeof(int)
|| (ret = write(pfd[pipe_write], &addr2->transport,
sizeof(transport_instance *))) != sizeof(transport_instance *)
len = read(pfd[pipe_read], &addr2->flags, sizeof(addr2->flags));
len = read(pfd[pipe_read], &addr2->basic_errno, sizeof(int));
len = read(pfd[pipe_read], &addr2->more_errno, sizeof(int));
- len = read(pfd[pipe_read], &addr2->delivery_usec, sizeof(int));
+ len = read(pfd[pipe_read], &addr2->delivery_time, sizeof(struct timeval));
len = read(pfd[pipe_read], &addr2->special_action, sizeof(int));
len = read(pfd[pipe_read], &addr2->transport,
sizeof(transport_instance *));
"message for %s transport): %s", addr->transport->warn_message,
addr->transport->name, expand_string_message);
- else if ((pid = child_open_exim(&fd)) > 0)
+ else if ((pid = child_open_exim(&fd, US"tpt-warning-message")) > 0)
{
FILE *f = fdopen(fd, "wb");
if (errors_reply_to && !contains_header(US"Reply-To", warn_message))
/* Done with this address */
- if (result == OK)
- {
- addr2->more_errno = deliver_time.tv_sec;
- addr2->delivery_usec = deliver_time.tv_usec;
- }
+ addr2->delivery_time = deliver_time;
post_process_one(addr2, result, logflags, EXIM_DTYPE_TRANSPORT, logchar);
/* If a pipe delivery generated text to be sent back, the result may be
switch (*subid)
{
case '1':
- addr->cipher = NULL;
- addr->peerdn = NULL;
+ addr->tlsver = addr->cipher = addr->peerdn = NULL;
if (*ptr)
+ {
addr->cipher = string_copy(ptr);
+ addr->tlsver = string_copyn(ptr, Ustrchr(ptr, ':') - ptr);
+ }
while (*ptr++);
if (*ptr)
addr->peerdn = string_copy(ptr);
case 'L':
switch (*subid)
{
-#ifdef SUPPORT_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
case 2: setflag(addr, af_early_pipe); /*FALLTHROUGH*/
#endif
case 1: setflag(addr, af_pipelining); break;
ptr += sizeof(addr->basic_errno);
memcpy(&addr->more_errno, ptr, sizeof(addr->more_errno));
ptr += sizeof(addr->more_errno);
- memcpy(&addr->delivery_usec, ptr, sizeof(addr->delivery_usec));
- ptr += sizeof(addr->delivery_usec);
+ memcpy(&addr->delivery_time, ptr, sizeof(addr->delivery_time));
+ ptr += sizeof(addr->delivery_time);
memcpy(&addr->flags, ptr, sizeof(addr->flags));
ptr += sizeof(addr->flags);
addr->message = *ptr ? string_copy(ptr) : NULL;
search_tidyup();
- if ((pid = fork()) == 0)
+ if ((pid = exim_fork(US"transport")) == 0)
{
int fd = pfd[pipe_write];
host_item *h;
/* Show pids on debug output if parallelism possible */
if (parmax > 1 && (parcount > 0 || addr_remote))
- {
DEBUG(D_any|D_v) debug_selector |= D_pid;
- DEBUG(D_deliver) debug_printf("Remote delivery process started\n");
- }
/* Reset the random number generator, so different processes don't all
have the same sequence. In the test harness we want different, but
#endif
if (testflag(addr, af_pipelining))
-#ifdef SUPPORT_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
if (testflag(addr, af_early_pipe))
rmt_dlv_checked_write(fd, 'L', '2', NULL, 0);
else
ptr += sizeof(addr->basic_errno);
memcpy(ptr, &addr->more_errno, sizeof(addr->more_errno));
ptr += sizeof(addr->more_errno);
- memcpy(ptr, &addr->delivery_usec, sizeof(addr->delivery_usec));
- ptr += sizeof(addr->delivery_usec);
+ memcpy(ptr, &addr->delivery_time, sizeof(addr->delivery_time));
+ ptr += sizeof(addr->delivery_time);
memcpy(ptr, &addr->flags, sizeof(addr->flags));
ptr += sizeof(addr->flags);
static void
print_dsn_diagnostic_code(const address_item *addr, FILE *f)
{
-uschar *s = testflag(addr, af_pass_message) ? addr->message : NULL;
+uschar * s = testflag(addr, af_pass_message) ? addr->message : NULL;
+unsigned cnt;
/* af_pass_message and addr->message set ? print remote host answer */
if (s)
if (!(s = Ustrstr(addr->message, ": ")))
return; /* not found, bail out */
s += 2; /* skip ": " */
- fprintf(f, "Diagnostic-Code: smtp; ");
+ cnt = fprintf(f, "Diagnostic-Code: smtp; ");
}
/* no message available. do nothing */
else return;
while (*s)
+ {
+ if (cnt > 950) /* RFC line length limit: 998 */
+ {
+ DEBUG(D_deliver) debug_printf("print_dsn_diagnostic_code() truncated line\n");
+ fputs("[truncated]", f);
+ break;
+ }
+
if (*s == '\\' && s[1] == 'n')
{
fputs("\n ", f); /* as defined in RFC 3461 */
s += 2;
+ cnt += 2;
}
else
+ {
fputc(*s++, f);
+ cnt++;
+ }
+ }
fputc('\n', f);
}
}
+
+/* When running in the test harness, there's an option that allows us to
+fudge this time so as to get repeatability of the tests. Take the first
+time off the list. In queue runs, the list pointer gets updated in the
+calling process. */
+
+int
+test_harness_fudged_queue_time(int actual_time)
+{
+int qt;
+if ( f.running_in_test_harness && *fudged_queue_times
+ && (qt = readconf_readtime(fudged_queue_times, '/', FALSE)) >= 0)
+ {
+ DEBUG(D_deliver) debug_printf("fudged queue_times = %s\n",
+ fudged_queue_times);
+ return qt;
+ }
+return actual_time;
+}
+
+/************************************************/
+
+static FILE *
+expand_open(const uschar * filename,
+ const uschar * varname, const uschar * reason)
+{
+const uschar * s = expand_cstring(filename);
+FILE * fp = NULL;
+
+if (!s || !*s)
+ log_write(0, LOG_MAIN|LOG_PANIC,
+ "Failed to expand %s: '%s'\n", varname, filename);
+else if (*s != '/' || is_tainted(s))
+ log_write(0, LOG_MAIN|LOG_PANIC,
+ "%s is not %s after expansion: '%s'\n",
+ varname, *s == '/' ? "untainted" : "absolute", s);
+else if (!(fp = Ufopen(s, "rb")))
+ log_write(0, LOG_MAIN|LOG_PANIC, "Failed to open %s for %s "
+ "message texts: %s", s, reason, strerror(errno));
+return fp;
+}
+
/*************************************************
* Deliver one message *
*************************************************/
new->onetime_parent = recipients_list[r->pno].address;
/* If DSN support is enabled, set the dsn flags and the original receipt
- to be passed on to other DSN enabled MTAs */
+ to be passed on to other DSN enabled MTAs */
+
new->dsn_flags = r->dsn_flags & rf_dsnflags;
new->dsn_orcpt = r->orcpt;
DEBUG(D_deliver) debug_printf("DSN: set orcpt: %s flags: 0x%x\n",
/* Precompile some regex that are used to recognize parameters in response
to an EHLO command, if they aren't already compiled. */
- deliver_init();
+ smtp_deliver_init();
/* Now sort the addresses if required, and do the deliveries. The yield of
do_remote_deliveries is FALSE when mua_wrapper is set and all addresses
);
/* send report if next hop not DSN aware or a router flagged "last DSN hop"
- and a report was requested */
- if ( ( a->dsn_aware != dsn_support_yes
- || a->dsn_flags & rf_dsnlasthop
- )
+ and a report was requested */
+
+ if ( (a->dsn_aware != dsn_support_yes || a->dsn_flags & rf_dsnlasthop)
&& a->dsn_flags & rf_notify_success
)
{
int fd;
/* create exim process to send message */
- pid = child_open_exim(&fd);
+ pid = child_open_exim(&fd, US"DSN");
DEBUG(D_deliver) debug_printf("DSN: child_open_exim returns: %d\n", pid);
/* Make a subprocess to send a message */
- if ((pid = child_open_exim(&fd)) < 0)
+ if ((pid = child_open_exim(&fd, US"bounce-message")) < 0)
log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Process %d (parent %d) failed to "
"create child process to send failure message: %s", getpid(),
getppid(), strerror(errno));
carry on - default texts will be used. */
if (bounce_message_file)
- if (!(emf = Ufopen(bounce_message_file, "rb")))
- log_write(0, LOG_MAIN|LOG_PANIC, "Failed to open %s for error "
- "message texts: %s", bounce_message_file, strerror(errno));
+ emf = expand_open(bounce_message_file,
+ US"bounce_message_file", US"error");
/* Quietly copy to configured additional addresses if required. */
fprintf(fp, "Remote-MTA: X-ip; [%s]%s\n", hu->address, p);
}
if ((s = addr->smtp_greeting) && *s)
- fprintf(fp, "X-Remote-MTA-smtp-greeting: X-str; %s\n", s);
+ fprintf(fp, "X-Remote-MTA-smtp-greeting: X-str; %.900s\n", s);
if ((s = addr->helo_response) && *s)
- fprintf(fp, "X-Remote-MTA-helo-response: X-str; %s\n", s);
+ fprintf(fp, "X-Remote-MTA-helo-response: X-str; %.900s\n", s);
if ((s = addr->message) && *s)
- fprintf(fp, "X-Exim-Diagnostic: X-str; %s\n", s);
+ fprintf(fp, "X-Exim-Diagnostic: X-str; %.900s\n", s);
}
#endif
print_dsn_diagnostic_code(addr, fp);
(void)fclose(fp);
rc = child_close(pid, 0); /* Waits for child to close, no timeout */
- /* In the test harness, let the child do it's thing first. */
-
- testharness_pause_ms(500);
-
/* If the process failed, there was some disaster in setting up the
error message. Unless the message is very old, ensure that addr_defer
is non-null, which will have the effect of leaving the message on the
int show_time;
int queue_time = time(NULL) - received_time.tv_sec;
- /* When running in the test harness, there's an option that allows us to
- fudge this time so as to get repeatability of the tests. Take the first
- time off the list. In queue runs, the list pointer gets updated in the
- calling process. */
-
- if (f.running_in_test_harness && fudged_queue_times[0] != 0)
- {
- int qt = readconf_readtime(fudged_queue_times, '/', FALSE);
- if (qt >= 0)
- {
- DEBUG(D_deliver) debug_printf("fudged queue_times = %s\n",
- fudged_queue_times);
- queue_time = qt;
- }
- }
+ queue_time = test_harness_fudged_queue_time(queue_time);
/* See how many warnings we should have sent by now */
{
header_line *h;
int fd;
- pid_t pid = child_open_exim(&fd);
+ pid_t pid = child_open_exim(&fd, US"delay-warning-message");
if (pid > 0)
{
- uschar *wmf_text;
- FILE *wmf = NULL;
- FILE *f = fdopen(fd, "wb");
+ uschar * wmf_text;
+ FILE * wmf = NULL;
+ FILE * f = fdopen(fd, "wb");
uschar * bound;
transport_ctx tctx = {{0}};
if (warn_message_file)
- if (!(wmf = Ufopen(warn_message_file, "rb")))
- log_write(0, LOG_MAIN|LOG_PANIC, "Failed to open %s for warning "
- "message texts: %s", warn_message_file, strerror(errno));
+ wmf = expand_open(warn_message_file,
+ US"warn_message_file", US"warning");
warnmsg_recipients = recipients;
warnmsg_delay = queue_time < 120*60
void
-deliver_init(void)
+tcp_init(void)
{
#ifdef EXIM_TFO_PROBE
tfo_probe();
#else
f.tcp_fastopen_ok = TRUE;
#endif
-
-
-if (!regex_PIPELINING) regex_PIPELINING =
- regex_must_compile(US"\\n250[\\s\\-]PIPELINING(\\s|\\n|$)", FALSE, TRUE);
-
-if (!regex_SIZE) regex_SIZE =
- regex_must_compile(US"\\n250[\\s\\-]SIZE(\\s|\\n|$)", FALSE, TRUE);
-
-if (!regex_AUTH) regex_AUTH =
- regex_must_compile(AUTHS_REGEX, FALSE, TRUE);
-
-#ifndef DISABLE_TLS
-if (!regex_STARTTLS) regex_STARTTLS =
- regex_must_compile(US"\\n250[\\s\\-]STARTTLS(\\s|\\n|$)", FALSE, TRUE);
-#endif
-
-if (!regex_CHUNKING) regex_CHUNKING =
- regex_must_compile(US"\\n250[\\s\\-]CHUNKING(\\s|\\n|$)", FALSE, TRUE);
-
-#ifndef DISABLE_PRDR
-if (!regex_PRDR) regex_PRDR =
- regex_must_compile(US"\\n250[\\s\\-]PRDR(\\s|\\n|$)", FALSE, TRUE);
-#endif
-
-#ifdef SUPPORT_I18N
-if (!regex_UTF8) regex_UTF8 =
- regex_must_compile(US"\\n250[\\s\\-]SMTPUTF8(\\s|\\n|$)", FALSE, TRUE);
-#endif
-
-if (!regex_DSN) regex_DSN =
- regex_must_compile(US"\\n250[\\s\\-]DSN(\\s|\\n|$)", FALSE, TRUE);
-
-if (!regex_IGNOREQUOTA) regex_IGNOREQUOTA =
- regex_must_compile(US"\\n250[\\s\\-]IGNOREQUOTA(\\s|\\n|$)", FALSE, TRUE);
-
-#ifdef SUPPORT_PIPE_CONNECT
-if (!regex_EARLY_PIPE) regex_EARLY_PIPE =
- regex_must_compile(US"\\n250[\\s\\-]" EARLY_PIPE_FEATURE_NAME "(\\s|\\n|$)", FALSE, TRUE);
-#endif
}
goto fail;
where = US"fork";
- if ((pid = fork()) < 0)
+ testharness_pause_ms(150);
+ if ((pid = exim_fork(US"tls-proxy-interproc")) < 0)
goto fail;
- else if (pid == 0) /* child: fork again to totally disconnect */
+ if (pid == 0) /* child: will fork again to totally disconnect */
{
- testharness_pause_ms(100); /* let parent debug out */
- /* does not return */
smtp_proxy_tls(cutthrough.cctx.tls_ctx, big_buffer, big_buffer_size,
pfd, 5*60);
+ /* does not return */
}
- DEBUG(D_transport) debug_printf("proxy-proc inter-pid %d\n", pid);
close(pfd[0]);
waitpid(pid, NULL, 0);
(void) close(channel_fd); /* release the client socket */