*************************************************/
/* Copyright (c) University of Cambridge 1995 - 2018 */
+/* Copyright (c) The Exim Maintainers 2020 */
/* See the file NOTICE for conditions of use and distribution. */
/* General functions concerned with transportation, and generic options for all
Arguments:
tctx
- (fd, msg) Either and fd, to write the message to,
+ (fd, msg) Either an fd, to write the message to,
or a string: if null write message to allocated space
otherwire take content as headers.
addr (chain of) addresses (for extra headers), or NULL;
add_delivery_date if TRUE, add a "delivery-date" header
use_crlf if TRUE, turn NL into CR LF
end_dot if TRUE, send a terminating "." line at the end
+ no_flush if TRUE, do not flush at end
no_headers if TRUE, omit the headers
no_body if TRUE, omit the body
check_string a string to check for at the start of lines, or NULL
if (tctx->options & topt_add_return_path)
{
- uschar buffer[ADDRESS_MAXLENGTH + 20];
- int n = sprintf(CS buffer, "Return-path: <%.*s>\n", ADDRESS_MAXLENGTH,
- return_path);
- if (!write_chunk(tctx, buffer, n)) goto bad;
+ int n;
+ uschar * s = string_sprintf("Return-path: <%.*s>\n%n",
+ EXIM_EMAILADDR_MAX, return_path, &n);
+ if (!write_chunk(tctx, s, n)) goto bad;
}
/* Add envelope-to: if requested */
/* Write out any remaining data in the buffer before returning. */
-return (len = chunk_ptr - deliver_out_buffer) <= 0 ||
- transport_write_block(tctx, deliver_out_buffer, len, FALSE);
+return (len = chunk_ptr - deliver_out_buffer) <= 0
+ || transport_write_block(tctx, deliver_out_buffer, len,
+ !!(tctx->options & topt_no_flush));
}
{
int bits = fcntl(tctx->u.fd, F_GETFD);
- (void)fcntl(tctx->u.fd, F_SETFD, bits | FD_CLOEXEC);
+ (void) fcntl(tctx->u.fd, F_SETFD, bits | FD_CLOEXEC);
filter_pid = child_open(USS transport_filter_argv, NULL, 077,
- &fd_write, &fd_read, FALSE);
- (void)fcntl(tctx->u.fd, F_SETFD, bits & ~FD_CLOEXEC);
+ &fd_write, &fd_read, FALSE, US"transport-filter");
+ (void) fcntl(tctx->u.fd, F_SETFD, bits & ~FD_CLOEXEC);
}
if (filter_pid < 0) goto TIDY_UP; /* errno set */
smtp dots, or check string processing. */
if (pipe(pfd) != 0) goto TIDY_UP; /* errno set */
-if ((write_pid = exim_fork(US"transport filter writer")) == 0)
+if ((write_pid = exim_fork(US"tpt-filter-writer")) == 0)
{
BOOL rc;
(void)close(fd_read);
tctx->u.fd = fd_write;
tctx->check_string = tctx->escape_string = NULL;
- tctx->options &= ~(topt_use_crlf | topt_end_dot | topt_use_bdat);
+ tctx->options &= ~(topt_use_crlf | topt_end_dot | topt_use_bdat | topt_no_flush);
rc = internal_transport_write_message(tctx, size_limit);
!= sizeof(struct timeval)
)
rc = FALSE; /* compiler quietening */
- exim_underbar_exit(0, US"tpt-filter writer");
+ exim_underbar_exit(EXIT_SUCCESS);
}
save_errno = errno;
int dummy = read(pfd[pipe_read], (void *)&save_errno, sizeof(int));
dummy = read(pfd[pipe_read], (void *)&tctx->addr->more_errno, sizeof(int));
dummy = read(pfd[pipe_read], (void *)&tctx->addr->delivery_time, sizeof(struct timeval));
- dummy = dummy; /* compiler quietening */
yield = FALSE;
}
}
{
sprintf(CS buffer, "%.200s:%d", host->name, host_record->sequence);
dbfn_write(dbm_file, buffer, host_record, sizeof(dbdata_wait) + host_length);
-#ifdef EXPERIMENTAL_QUEUE_RAMP
+#ifndef DISABLE_QUEUE_RAMP
if (f.queue_2stage && queue_fast_ramp && !queue_run_in_order)
queue_notify_daemon(message_id);
#endif
/* Update the database */
dbfn_write(dbm_file, host->name, host_record, sizeof(dbdata_wait) + host_length);
- DEBUG(D_transport) debug_printf("added to list for %s\n", host->name);
+ DEBUG(D_transport) debug_printf("added %.*s to queue for %s\n",
+ MESSAGE_ID_LENGTH, message_id, host->name);
}
/* All now done */
local_message_max maximum number of messages down one connection
as set by the caller transport
new_message_id set to the message id of a waiting message
- more set TRUE if there are yet more messages waiting
oicf_func function to call to validate if it is ok to send
to this message_id from the current instance.
oicf_data opaque data for oicf_func
BOOL
transport_check_waiting(const uschar *transport_name, const uschar *hostname,
- int local_message_max, uschar *new_message_id, BOOL *more, oicf oicf_func, void *oicf_data)
+ int local_message_max, uschar *new_message_id, oicf oicf_func, void *oicf_data)
{
dbdata_wait *host_record;
int host_length;
int i;
struct stat statbuf;
-*more = FALSE;
-
DEBUG(D_transport)
{
debug_printf("transport_check_waiting entered\n");
debug_printf(" sequence=%d local_max=%d global_max=%d\n",
continue_sequence, local_message_max, connection_max_messages);
+ acl_level++;
}
/* Do nothing if we have hit the maximum number that can be send down one
if (local_message_max > 0 && continue_sequence >= local_message_max)
{
DEBUG(D_transport)
- debug_printf("max messages for one connection reached: returning\n");
- return FALSE;
+ debug_printf_indent("max messages for one connection reached: returning\n");
+ goto retfalse;
}
/* Open the waiting information database. */
if (!(dbm_file = dbfn_open(string_sprintf("wait-%.200s", transport_name),
O_RDWR, &dbblock, TRUE, TRUE)))
- return FALSE;
+ goto retfalse;
/* See if there is a record for this host; if not, there's nothing to do. */
if (!(host_record = dbfn_read(dbm_file, hostname)))
{
dbfn_close(dbm_file);
- DEBUG(D_transport) debug_printf("no messages waiting for %s\n", hostname);
- return FALSE;
+ DEBUG(D_transport) debug_printf_indent("no messages waiting for %s\n", hostname);
+ goto retfalse;
}
/* If the data in the record looks corrupt, just log something and
dbfn_close(dbm_file);
log_write(0, LOG_MAIN|LOG_PANIC, "smtp-wait database entry for %s has bad "
"count=%d (max=%d)", hostname, host_record->count, WAIT_NAME_MAX);
- return FALSE;
+ goto retfalse;
}
/* Scan the message ids in the record from the end towards the beginning,
{
msgq[i].bKeep = TRUE;
- Ustrncpy_nt(msgq[i].message_id, host_record->text + (i * MESSAGE_ID_LENGTH),
+ Ustrncpy_nt(msgq[i].message_id, host_record->text + (i * MESSAGE_ID_LENGTH),
MESSAGE_ID_LENGTH);
msgq[i].message_id[MESSAGE_ID_LENGTH] = 0;
}
if (host_length <= 0)
{
dbfn_close(dbm_file);
- DEBUG(D_transport) debug_printf("waiting messages already delivered\n");
- return FALSE;
+ DEBUG(D_transport) debug_printf_indent("waiting messages already delivered\n");
+ goto retfalse;
}
/* we were not able to find an acceptable message, nor was there a
{
Ustrcpy(new_message_id, message_id);
dbfn_close(dbm_file);
- return FALSE;
+ goto retfalse;
}
} /* we need to process a continuation record */
if (host_length > 0)
{
host_record->count = host_length/MESSAGE_ID_LENGTH;
-
dbfn_write(dbm_file, hostname, host_record, (int)sizeof(dbdata_wait) + host_length);
- *more = TRUE;
}
dbfn_close(dbm_file);
+DEBUG(D_transport) {acl_level--; debug_printf("transport_check_waiting: TRUE\n"); }
return TRUE;
+
+retfalse:
+DEBUG(D_transport) {acl_level--; debug_printf("transport_check_waiting: FALSE\n"); }
+return FALSE;
}
/*************************************************
transport_do_pass_socket(const uschar *transport_name, const uschar *hostname,
const uschar *hostaddress, uschar *id, int socket_fd)
{
-int i = 20;
+int i = 13;
const uschar **argv;
+#ifndef DISABLE_TLS
+if (smtp_peer_options & OPTION_TLS) i += 6;
+#endif
+#ifdef EXPERIMENTAL_ESMTP_LIMITS
+if (continue_limit_mail || continue_limit_rcpt || continue_limit_rcptdom)
+ i += 4;
+#endif
+if (queue_run_pid != (pid_t)0) i += 3;
+#ifdef SUPPORT_SOCKS
+if (proxy_session) i += 5;
+#endif
+
/* Set up the calling arguments; use the standard function for the basics,
but we have a number of extras that may be added. */
argv[i++] = sending_ip_address;
argv[i++] = string_sprintf("%d", sending_port);
argv[i++] = tls_out.active.sock >= 0 ? tls_out.cipher : continue_proxy_cipher;
+
+ if (tls_out.sni)
+ {
+ argv[i++] =
+#ifdef SUPPORT_DANE
+ tls_out.dane_verified ? US"-MCr" :
+#endif
+ US"-MCs";
+ argv[i++] = tls_out.sni;
+ }
}
else
argv[i++] = US"-MCT";
#endif
+#ifdef EXPERIMENTAL_ESMTP_LIMITS
+if (continue_limit_rcpt || continue_limit_rcptdom)
+ {
+ argv[i++] = US"-MCL";
+ argv[i++] = string_sprintf("%u", continue_limit_mail);
+ argv[i++] = string_sprintf("%u", continue_limit_rcpt);
+ argv[i++] = string_sprintf("%u", continue_limit_rcptdom);
+ }
+#endif
+
if (queue_run_pid != (pid_t)0)
{
argv[i++] = US"-MCQ";
argv[i++] = string_sprintf("%d", queue_run_pipe);
}
+#ifdef SUPPORT_SOCKS
+if (proxy_session)
+ {
+ argv[i++] = US"-MCp";
+ argv[i++] = proxy_local_address;
+ argv[i++] = string_sprintf("%d", proxy_local_port);
+ argv[i++] = proxy_external_address;
+ argv[i++] = string_sprintf("%d", proxy_external_port);
+ }
+#endif
+
argv[i++] = US"-MC";
argv[i++] = US transport_name;
argv[i++] = US hostname;
BOOL
transport_pass_socket(const uschar *transport_name, const uschar *hostname,
- const uschar *hostaddress, uschar *id, int socket_fd)
+ const uschar *hostaddress, uschar *id, int socket_fd
+#ifdef EXPERIMENTAL_ESMTP_LIMITS
+ , unsigned peer_limit_mail, unsigned peer_limit_rcpt, unsigned peer_limit_rcptdom
+#endif
+ )
{
pid_t pid;
int status;
DEBUG(D_transport) debug_printf("transport_pass_socket entered\n");
-if ((pid = exim_fork(US"continued-transport interproc")) == 0)
+#ifdef EXPERIMENTAL_ESMTP_LIMITS
+continue_limit_mail = peer_limit_mail;
+continue_limit_rcpt = peer_limit_rcpt;
+continue_limit_rcptdom = peer_limit_rcptdom;
+#endif
+
+if ((pid = exim_fork(US"continued-transport-interproc")) == 0)
{
/* Disconnect entirely from the parent process. If we are running in the
test harness, wait for a bit to allow the previous process time to finish,
automatic comparison. */
if ((pid = exim_fork(US"continued-transport")) != 0)
- {
- DEBUG(D_transport) debug_printf("transport_pass_socket succeeded (final-pid %d)\n", pid);
_exit(EXIT_SUCCESS);
- }
- testharness_pause_ms(1000); /*TTT*/
+ testharness_pause_ms(1000);
transport_do_pass_socket(transport_name, hostname, hostaddress,
id, socket_fd);
{
int rc;
while ((rc = wait(&status)) != pid && (rc >= 0 || errno != ECHILD));
- DEBUG(D_transport) debug_printf("transport_pass_socket succeeded (inter-pid %d)\n", pid);
return TRUE;
}
else
}
/* If we are not just able to replace the slot that contained
- * $address_pipe (address_pipe_argcount == 1)
- * We have to move the existing argv by address_pipe_argcount - 1
- * Visually if address_pipe_argcount == 2:
- * [argv 0][argv 1][argv 2($address_pipe)][argv 3][0]
- * [argv 0][argv 1][ap_arg0][ap_arg1][old argv 3][0]
- */
+ $address_pipe (address_pipe_argcount == 1)
+ We have to move the existing argv by address_pipe_argcount - 1
+ Visually if address_pipe_argcount == 2:
+ [argv 0][argv 1][argv 2($address_pipe)][argv 3][0]
+ [argv 0][argv 1][ap_arg0][ap_arg1][old argv 3][0] */
+
if (address_pipe_argcount > 1)
memmove(
/* current position + additional args */
);
/* Now we fill in the slots we just moved argv out of
- * [argv 0][argv 1][argv 2=pipeargv[0]][argv 3=pipeargv[1]][old argv 3][0]
- */
+ [argv 0][argv 1][argv 2=pipeargv[0]][argv 3=pipeargv[1]][old argv 3][0] */
+
for (int address_pipe_i = 0;
address_pipe_argv[address_pipe_i] != US 0;
- address_pipe_i++)
- {
+ address_pipe_i++, argcount++)
argv[i++] = address_pipe_argv[address_pipe_i];
- argcount++;
- }
/* Subtract one since we replace $address_pipe */
argcount--;