uschar *message = NULL;
uschar new_message_id[MESSAGE_ID_LENGTH + 1];
-smtp_context sx;
+smtp_context * sx = store_get(sizeof(*sx), TRUE); /* tainted, for the data buffers */
gettimeofday(&start_delivery_time, NULL);
suppress_tls = suppress_tls; /* stop compiler warning when no TLS support */
*message_defer = FALSE;
-sx.addrlist = addrlist;
-sx.conn_args.host = host;
-sx.conn_args.host_af = host_af,
-sx.port = defport;
-sx.conn_args.interface = interface;
-sx.helo_data = NULL;
-sx.conn_args.tblock = tblock;
-sx.verify = FALSE;
-sx.sync_addr = sx.first_addr = addrlist;
+sx->addrlist = addrlist;
+sx->conn_args.host = host;
+sx->conn_args.host_af = host_af,
+sx->port = defport;
+sx->conn_args.interface = interface;
+sx->helo_data = NULL;
+sx->conn_args.tblock = tblock;
+sx->verify = FALSE;
+sx->sync_addr = sx->first_addr = addrlist;
/* Get the channel set up ready for a message (MAIL FROM being the next
SMTP command to send */
-if ((rc = smtp_setup_conn(&sx, suppress_tls)) != OK)
+if ((rc = smtp_setup_conn(sx, suppress_tls)) != OK)
return rc;
/* If there is a filter command specified for this transport, we can now
if ( transport_filter_argv
&& *transport_filter_argv
&& **transport_filter_argv
- && sx.peer_offered & OPTION_CHUNKING
+ && sx->peer_offered & OPTION_CHUNKING
)
{
- sx.peer_offered &= ~OPTION_CHUNKING;
+ sx->peer_offered &= ~OPTION_CHUNKING;
DEBUG(D_transport) debug_printf("CHUNKING not usable due to transport filter\n");
}
}
transaction to handle. */
SEND_MESSAGE:
-sx.from_addr = return_path;
-sx.sync_addr = sx.first_addr;
-sx.ok = FALSE;
-sx.send_rset = TRUE;
-sx.completed_addr = FALSE;
+sx->from_addr = return_path;
+sx->sync_addr = sx->first_addr;
+sx->ok = FALSE;
+sx->send_rset = TRUE;
+sx->completed_addr = FALSE;
/* If we are a continued-connection-after-verify the MAIL and RCPT
if (continue_hostname && continue_sequence == 1)
{
- sx.peer_offered = smtp_peer_options;
- sx.pending_MAIL = FALSE;
- sx.ok = TRUE;
- sx.next_addr = NULL;
+ sx->peer_offered = smtp_peer_options;
+ sx->pending_MAIL = FALSE;
+ sx->ok = TRUE;
+ sx->next_addr = NULL;
for (address_item * addr = addrlist; addr; addr = addr->next)
addr->transport_return = PENDING_OK;
{
/* Initiate a message transfer. */
- switch(smtp_write_mail_and_rcpt_cmds(&sx, &yield))
+ switch(smtp_write_mail_and_rcpt_cmds(sx, &yield))
{
case 0: break;
case -1: case -2: goto RESPONSE_FAILED;
address_item * a;
unsigned cnt;
- for (a = sx.first_addr, cnt = 0; a && cnt < sx.max_rcpt; a = a->next, cnt++)
+ for (a = sx->first_addr, cnt = 0; a && cnt < sx->max_rcpt; a = a->next, cnt++)
if (a->transport_return != PENDING_OK)
{
/*XXX could we find a better errno than 0 here? */
set_errno_nohost(addrlist, 0, a->message, FAIL,
testflag(a, af_pass_message));
- sx.ok = FALSE;
+ sx->ok = FALSE;
break;
}
}
If using CHUNKING, do not send a BDAT until we know how big a chunk we want
to send is. */
-if ( !(sx.peer_offered & OPTION_CHUNKING)
- && (sx.ok || (pipelining_active && !mua_wrapper)))
+if ( !(sx->peer_offered & OPTION_CHUNKING)
+ && (sx->ok || (pipelining_active && !mua_wrapper)))
{
- int count = smtp_write_command(&sx, SCMD_FLUSH, "DATA\r\n");
+ int count = smtp_write_command(sx, SCMD_FLUSH, "DATA\r\n");
if (count < 0) goto SEND_FAILED;
- switch(sync_responses(&sx, count, sx.ok ? +1 : -1))
+ switch(sync_responses(sx, count, sx->ok ? +1 : -1))
{
- case 3: sx.ok = TRUE; /* 2xx & 5xx => OK & progress made */
- case 2: sx.completed_addr = TRUE; /* 5xx (only) => progress made */
+ case 3: sx->ok = TRUE; /* 2xx & 5xx => OK & progress made */
+ case 2: sx->completed_addr = TRUE; /* 5xx (only) => progress made */
break;
- case 1: sx.ok = TRUE; /* 2xx (only) => OK, but if LMTP, */
- if (!sx.lmtp) sx.completed_addr = TRUE; /* can't tell about progress yet */
+ case 1: sx->ok = TRUE; /* 2xx (only) => OK, but if LMTP, */
+ if (!sx->lmtp) sx->completed_addr = TRUE; /* can't tell about progress yet */
case 0: break; /* No 2xx or 5xx, but no probs */
case -1: goto END_OFF; /* Timeout on RCPT */
well as body. Set the appropriate timeout value to be used for each chunk.
(Haven't been able to make it work using select() for writing yet.) */
-if ( !sx.ok
- && (!(sx.peer_offered & OPTION_CHUNKING) || !pipelining_active))
+if ( !sx->ok
+ && (!(sx->peer_offered & OPTION_CHUNKING) || !pipelining_active))
{
/* Save the first address of the next batch. */
- sx.first_addr = sx.next_addr;
+ sx->first_addr = sx->next_addr;
- sx.ok = TRUE;
+ sx->ok = TRUE;
}
else
{
transport_ctx tctx = {
- .u = {.fd = sx.cctx.sock}, /*XXX will this need TLS info? */
+ .u = {.fd = sx->cctx.sock}, /*XXX will this need TLS info? */
.tblock = tblock,
.addr = addrlist,
.check_string = US".",
of responses. The callback needs a whole bunch of state so set up
a transport-context structure to be passed around. */
- if (sx.peer_offered & OPTION_CHUNKING)
+ if (sx->peer_offered & OPTION_CHUNKING)
{
tctx.check_string = tctx.escape_string = NULL;
tctx.options |= topt_use_bdat;
tctx.chunk_cb = smtp_chunk_cmd_callback;
- sx.pending_BDAT = FALSE;
- sx.good_RCPT = sx.ok;
- sx.cmd_count = 0;
- tctx.smtp_context = &sx;
+ sx->pending_BDAT = FALSE;
+ sx->good_RCPT = sx->ok;
+ sx->cmd_count = 0;
+ tctx.smtp_context = sx;
}
else
tctx.options |= topt_end_dot;
/* Save the first address of the next batch. */
- sx.first_addr = sx.next_addr;
+ sx->first_addr = sx->next_addr;
/* Responses from CHUNKING commands go in buffer. Otherwise,
there has not been a response. */
- sx.buffer[0] = 0;
+ sx->buffer[0] = 0;
sigalrm_seen = FALSE;
transport_write_timeout = ob->data_timeout;
smtp_command = US"sending data block"; /* For error messages */
DEBUG(D_transport|D_v)
- if (sx.peer_offered & OPTION_CHUNKING)
+ if (sx->peer_offered & OPTION_CHUNKING)
debug_printf(" will write message using CHUNKING\n");
else
debug_printf(" SMTP>> writing message and terminating \".\"\n");
if (!f.expand_string_forcedfail)
{
message = US"failed to expand arc_sign";
- sx.ok = FALSE;
+ sx->ok = FALSE;
goto SEND_FAILED;
}
}
report_time_since(&t0, US"dkim_exim_sign_init (delta)");
# endif
}
- sx.ok = dkim_transport_write_message(&tctx, &ob->dkim, CUSS &message);
+ sx->ok = dkim_transport_write_message(&tctx, &ob->dkim, CUSS &message);
#else
- sx.ok = transport_write_message(&tctx, 0);
+ sx->ok = transport_write_message(&tctx, 0);
#endif
/* transport_write_message() uses write() because it is called from other
or the failure of a transport filter or the expansion of added headers.
Or, when CHUNKING, it can be a protocol-detected failure. */
- if (!sx.ok)
+ if (!sx->ok)
if (message) goto SEND_FAILED;
else goto RESPONSE_FAILED;
smtp_command = US"end of data";
- if (sx.peer_offered & OPTION_CHUNKING && sx.cmd_count > 1)
+ if (sx->peer_offered & OPTION_CHUNKING && sx->cmd_count > 1)
{
/* Reap any outstanding MAIL & RCPT commands, but not a DATA-go-ahead */
- switch(sync_responses(&sx, sx.cmd_count-1, 0))
+ switch(sync_responses(sx, sx->cmd_count-1, 0))
{
- case 3: sx.ok = TRUE; /* 2xx & 5xx => OK & progress made */
- case 2: sx.completed_addr = TRUE; /* 5xx (only) => progress made */
+ case 3: sx->ok = TRUE; /* 2xx & 5xx => OK & progress made */
+ case 2: sx->completed_addr = TRUE; /* 5xx (only) => progress made */
break;
- case 1: sx.ok = TRUE; /* 2xx (only) => OK, but if LMTP, */
- if (!sx.lmtp) sx.completed_addr = TRUE; /* can't tell about progress yet */
+ case 1: sx->ok = TRUE; /* 2xx (only) => OK, but if LMTP, */
+ if (!sx->lmtp) sx->completed_addr = TRUE; /* can't tell about progress yet */
case 0: break; /* No 2xx or 5xx, but no probs */
case -1: goto END_OFF; /* Timeout on RCPT */
individual responses, before going on with the overall response. If we don't
get the warning then deal with per non-PRDR. */
- if(sx.prdr_active)
+ if(sx->prdr_active)
{
- sx.ok = smtp_read_response(&sx, sx.buffer, sizeof(sx.buffer), '3', ob->final_timeout);
- if (!sx.ok && errno == 0) switch(sx.buffer[0])
+ sx->ok = smtp_read_response(sx, sx->buffer, sizeof(sx->buffer), '3', ob->final_timeout);
+ if (!sx->ok && errno == 0) switch(sx->buffer[0])
{
- case '2': sx.prdr_active = FALSE;
- sx.ok = TRUE;
+ case '2': sx->prdr_active = FALSE;
+ sx->ok = TRUE;
break;
case '4': errno = ERRNO_DATA4XX;
addrlist->more_errno |=
- ((sx.buffer[1] - '0')*10 + sx.buffer[2] - '0') << 8;
+ ((sx->buffer[1] - '0')*10 + sx->buffer[2] - '0') << 8;
break;
}
}
/* For non-PRDR SMTP, we now read a single response that applies to the
whole message. If it is OK, then all the addresses have been delivered. */
- if (!sx.lmtp)
+ if (!sx->lmtp)
{
- sx.ok = smtp_read_response(&sx, sx.buffer, sizeof(sx.buffer), '2',
+ sx->ok = smtp_read_response(sx, sx->buffer, sizeof(sx->buffer), '2',
ob->final_timeout);
- if (!sx.ok && errno == 0 && sx.buffer[0] == '4')
+ if (!sx->ok && errno == 0 && sx->buffer[0] == '4')
{
errno = ERRNO_DATA4XX;
- addrlist->more_errno |= ((sx.buffer[1] - '0')*10 + sx.buffer[2] - '0') << 8;
+ addrlist->more_errno |= ((sx->buffer[1] - '0')*10 + sx->buffer[2] - '0') << 8;
}
}
software before the spool gets updated. Also record the final SMTP
confirmation if needed (for SMTP only). */
- if (sx.ok)
+ if (sx->ok)
{
int flag = '=';
struct timeval delivery_time;
uschar * conf = NULL;
timesince(&delivery_time, &start_delivery_time);
- sx.send_rset = FALSE;
+ sx->send_rset = FALSE;
pipelining_active = FALSE;
/* Set up confirmation if needed - applies only to SMTP */
#ifdef DISABLE_EVENT
LOGGING(smtp_confirmation) &&
#endif
- !sx.lmtp
+ !sx->lmtp
)
{
- const uschar *s = string_printing(sx.buffer);
+ const uschar *s = string_printing(sx->buffer);
/* deconst cast ok here as string_printing was checked to have alloc'n'copied */
- conf = (s == sx.buffer)? US string_copy(s) : US s;
+ conf = (s == sx->buffer)? US string_copy(s) : US s;
}
/* Process all transported addresses - for LMTP or PRDR, read a status for
each one. */
- for (address_item * addr = addrlist; addr != sx.first_addr; addr = addr->next)
+ for (address_item * addr = addrlist; addr != sx->first_addr; addr = addr->next)
{
if (addr->transport_return != PENDING_OK) continue;
it doesn't get tried again too soon. */
#ifndef DISABLE_PRDR
- if (sx.lmtp || sx.prdr_active)
+ if (sx->lmtp || sx->prdr_active)
#else
- if (sx.lmtp)
+ if (sx->lmtp)
#endif
{
- if (!smtp_read_response(&sx, sx.buffer, sizeof(sx.buffer), '2',
+ if (!smtp_read_response(sx, sx->buffer, sizeof(sx->buffer), '2',
ob->final_timeout))
{
- if (errno != 0 || sx.buffer[0] == 0) goto RESPONSE_FAILED;
+ if (errno != 0 || sx->buffer[0] == 0) goto RESPONSE_FAILED;
addr->message = string_sprintf(
#ifndef DISABLE_PRDR
- "%s error after %s: %s", sx.prdr_active ? "PRDR":"LMTP",
+ "%s error after %s: %s", sx->prdr_active ? "PRDR":"LMTP",
#else
"LMTP error after %s: %s",
#endif
- data_command, string_printing(sx.buffer));
+ data_command, string_printing(sx->buffer));
setflag(addr, af_pass_message); /* Allow message to go to user */
- if (sx.buffer[0] == '5')
+ if (sx->buffer[0] == '5')
addr->transport_return = FAIL;
else
{
errno = ERRNO_DATA4XX;
- addr->more_errno |= ((sx.buffer[1] - '0')*10 + sx.buffer[2] - '0') << 8;
+ addr->more_errno |= ((sx->buffer[1] - '0')*10 + sx->buffer[2] - '0') << 8;
addr->transport_return = DEFER;
#ifndef DISABLE_PRDR
- if (!sx.prdr_active)
+ if (!sx->prdr_active)
#endif
retry_add_item(addr, addr->address_retry_key, 0);
}
continue;
}
- sx.completed_addr = TRUE; /* NOW we can set this flag */
+ sx->completed_addr = TRUE; /* NOW we can set this flag */
if (LOGGING(smtp_confirmation))
{
- const uschar *s = string_printing(sx.buffer);
+ const uschar *s = string_printing(sx->buffer);
/* deconst cast ok here as string_printing was checked to have alloc'n'copied */
- conf = (s == sx.buffer) ? US string_copy(s) : US s;
+ conf = (s == sx->buffer) ? US string_copy(s) : US s;
}
}
if (tcp_out_fastopen >= TFO_USED_NODATA) setflag(addr, af_tcp_fastopen);
if (tcp_out_fastopen >= TFO_USED_DATA) setflag(addr, af_tcp_fastopen_data);
}
- if (sx.pipelining_used) setflag(addr, af_pipelining);
+ if (sx->pipelining_used) setflag(addr, af_pipelining);
#ifdef SUPPORT_PIPE_CONNECT
- if (sx.early_pipe_active) setflag(addr, af_early_pipe);
+ if (sx->early_pipe_active) setflag(addr, af_early_pipe);
#endif
#ifndef DISABLE_PRDR
- if (sx.prdr_active) setflag(addr, af_prdr_used);
+ if (sx->prdr_active) setflag(addr, af_prdr_used);
#endif
- if (sx.peer_offered & OPTION_CHUNKING) setflag(addr, af_chunking_used);
+ if (sx->peer_offered & OPTION_CHUNKING) setflag(addr, af_chunking_used);
flag = '-';
#ifndef DISABLE_PRDR
- if (!sx.prdr_active)
+ if (!sx->prdr_active)
#endif
{
/* Update the journal. For homonymic addresses, use the base address plus
write error, as it may prove possible to update the spool file later. */
if (testflag(addr, af_homonym))
- sprintf(CS sx.buffer, "%.500s/%s\n", addr->unique + 3, tblock->name);
+ sprintf(CS sx->buffer, "%.500s/%s\n", addr->unique + 3, tblock->name);
else
- sprintf(CS sx.buffer, "%.500s\n", addr->unique);
+ sprintf(CS sx->buffer, "%.500s\n", addr->unique);
- DEBUG(D_deliver) debug_printf("S:journalling %s\n", sx.buffer);
- len = Ustrlen(CS sx.buffer);
- if (write(journal_fd, sx.buffer, len) != len)
+ DEBUG(D_deliver) debug_printf("S:journalling %s\n", sx->buffer);
+ len = Ustrlen(CS sx->buffer);
+ if (write(journal_fd, sx->buffer, len) != len)
log_write(0, LOG_MAIN|LOG_PANIC, "failed to write journal for "
- "%s: %s", sx.buffer, strerror(errno));
+ "%s: %s", sx->buffer, strerror(errno));
}
}
#ifndef DISABLE_PRDR
- if (sx.prdr_active)
+ if (sx->prdr_active)
{
const uschar * overall_message;
/* PRDR - get the final, overall response. For any non-success
upgrade all the address statuses. */
- sx.ok = smtp_read_response(&sx, sx.buffer, sizeof(sx.buffer), '2',
+ sx->ok = smtp_read_response(sx, sx->buffer, sizeof(sx->buffer), '2',
ob->final_timeout);
- if (!sx.ok)
+ if (!sx->ok)
{
- if(errno == 0 && sx.buffer[0] == '4')
+ if(errno == 0 && sx->buffer[0] == '4')
{
errno = ERRNO_DATA4XX;
- addrlist->more_errno |= ((sx.buffer[1] - '0')*10 + sx.buffer[2] - '0') << 8;
+ addrlist->more_errno |= ((sx->buffer[1] - '0')*10 + sx->buffer[2] - '0') << 8;
}
- for (address_item * addr = addrlist; addr != sx.first_addr; addr = addr->next)
- if (sx.buffer[0] == '5' || addr->transport_return == OK)
+ for (address_item * addr = addrlist; addr != sx->first_addr; addr = addr->next)
+ if (sx->buffer[0] == '5' || addr->transport_return == OK)
addr->transport_return = PENDING_OK; /* allow set_errno action */
goto RESPONSE_FAILED;
}
/* Append the overall response to the individual PRDR response for logging
and update the journal, or setup retry. */
- overall_message = string_printing(sx.buffer);
- for (address_item * addr = addrlist; addr != sx.first_addr; addr = addr->next)
+ overall_message = string_printing(sx->buffer);
+ for (address_item * addr = addrlist; addr != sx->first_addr; addr = addr->next)
if (addr->transport_return == OK)
addr->message = string_sprintf("%s\\n%s", addr->message, overall_message);
- for (address_item * addr = addrlist; addr != sx.first_addr; addr = addr->next)
+ for (address_item * addr = addrlist; addr != sx->first_addr; addr = addr->next)
if (addr->transport_return == OK)
{
if (testflag(addr, af_homonym))
- sprintf(CS sx.buffer, "%.500s/%s\n", addr->unique + 3, tblock->name);
+ sprintf(CS sx->buffer, "%.500s/%s\n", addr->unique + 3, tblock->name);
else
- sprintf(CS sx.buffer, "%.500s\n", addr->unique);
+ sprintf(CS sx->buffer, "%.500s\n", addr->unique);
- DEBUG(D_deliver) debug_printf("journalling(PRDR) %s\n", sx.buffer);
- len = Ustrlen(CS sx.buffer);
- if (write(journal_fd, sx.buffer, len) != len)
+ DEBUG(D_deliver) debug_printf("journalling(PRDR) %s\n", sx->buffer);
+ len = Ustrlen(CS sx->buffer);
+ if (write(journal_fd, sx->buffer, len) != len)
log_write(0, LOG_MAIN|LOG_PANIC, "failed to write journal for "
- "%s: %s", sx.buffer, strerror(errno));
+ "%s: %s", sx->buffer, strerror(errno));
}
else if (addr->transport_return == DEFER)
retry_add_item(addr, addr->address_retry_key, -2);
here during the setting up phase (i.e. before MAIL FROM) then always defer, as
the problem is not related to this specific message. */
-if (!sx.ok)
+if (!sx->ok)
{
int code, set_rc;
uschar * set_message;
{
save_errno = errno;
message = NULL;
- sx.send_quit = check_response(host, &save_errno, addrlist->more_errno,
- sx.buffer, &code, &message, &pass_message);
+ sx->send_quit = check_response(host, &save_errno, addrlist->more_errno,
+ sx->buffer, &code, &message, &pass_message);
goto FAILED;
}
code = '4';
message = string_sprintf("send() to %s [%s] failed: %s",
host->name, host->address, message ? message : US strerror(save_errno));
- sx.send_quit = FALSE;
+ sx->send_quit = FALSE;
goto FAILED;
}
{
BOOL message_error;
- sx.ok = FALSE; /* For when reached by GOTO */
+ sx->ok = FALSE; /* For when reached by GOTO */
set_message = message;
/* We want to handle timeouts after MAIL or "." and loss of connection after
if (save_errno > 0)
message = US string_sprintf("%s: %s", message, strerror(save_errno));
- write_logs(host, message, sx.first_addr ? sx.first_addr->basic_errno : 0);
+ write_logs(host, message, sx->first_addr ? sx->first_addr->basic_errno : 0);
*message_defer = TRUE;
}
set_errno(addrlist, save_errno, set_message, set_rc, pass_message, host
#ifdef EXPERIMENTAL_DSN_INFO
- , sx.smtp_greeting, sx.helo_response
+ , sx->smtp_greeting, sx->helo_response
#endif
);
}
DEBUG(D_transport)
debug_printf("ok=%d send_quit=%d send_rset=%d continue_more=%d "
- "yield=%d first_address is %sNULL\n", sx.ok, sx.send_quit,
- sx.send_rset, f.continue_more, yield, sx.first_addr ? "not " : "");
+ "yield=%d first_address is %sNULL\n", sx->ok, sx->send_quit,
+ sx->send_rset, f.continue_more, yield, sx->first_addr ? "not " : "");
-if (sx.completed_addr && sx.ok && sx.send_quit)
+if (sx->completed_addr && sx->ok && sx->send_quit)
{
BOOL more;
smtp_compare_t t_compare;
t_compare.tblock = tblock;
t_compare.current_sender_address = sender_address;
- if ( sx.first_addr != NULL
+ if ( sx->first_addr != NULL
|| f.continue_more
|| (
#ifndef DISABLE_TLS
uschar *msg;
BOOL pass_message;
- if (sx.send_rset)
- if (! (sx.ok = smtp_write_command(&sx, SCMD_FLUSH, "RSET\r\n") >= 0))
+ if (sx->send_rset)
+ if (! (sx->ok = smtp_write_command(sx, SCMD_FLUSH, "RSET\r\n") >= 0))
{
msg = US string_sprintf("send() to %s [%s] failed: %s", host->name,
host->address, strerror(errno));
- sx.send_quit = FALSE;
+ sx->send_quit = FALSE;
}
- else if (! (sx.ok = smtp_read_response(&sx, sx.buffer, sizeof(sx.buffer),
+ else if (! (sx->ok = smtp_read_response(sx, sx->buffer, sizeof(sx->buffer),
'2', ob->command_timeout)))
{
int code;
- sx.send_quit = check_response(host, &errno, 0, sx.buffer, &code, &msg,
+ sx->send_quit = check_response(host, &errno, 0, sx->buffer, &code, &msg,
&pass_message);
- if (!sx.send_quit)
+ if (!sx->send_quit)
{
DEBUG(D_transport) debug_printf("H=%s [%s] %s\n",
host->name, host->address, msg);
/* Either RSET was not needed, or it succeeded */
- if (sx.ok)
+ if (sx->ok)
{
#ifndef DISABLE_TLS
int pfd[2];
#endif
- int socket_fd = sx.cctx.sock;
+ int socket_fd = sx->cctx.sock;
- if (sx.first_addr != NULL) /* More addresses still to be sent */
+ if (sx->first_addr != NULL) /* More addresses still to be sent */
{ /* in this run of the transport */
continue_sequence++; /* Causes * in logging */
goto SEND_MESSAGE;
a new EHLO. If we don't get a good response, we don't attempt to pass
the socket on. */
- tls_close(sx.cctx.tls_ctx, TLS_SHUTDOWN_WAIT);
- sx.cctx.tls_ctx = NULL;
+ tls_close(sx->cctx.tls_ctx, TLS_SHUTDOWN_WAIT);
+ sx->cctx.tls_ctx = NULL;
smtp_peer_options = smtp_peer_options_wrap;
- sx.ok = !sx.smtps
- && smtp_write_command(&sx, SCMD_FLUSH, "EHLO %s\r\n", sx.helo_data)
+ sx->ok = !sx->smtps
+ && smtp_write_command(sx, SCMD_FLUSH, "EHLO %s\r\n", sx->helo_data)
>= 0
- && smtp_read_response(&sx, sx.buffer, sizeof(sx.buffer),
+ && smtp_read_response(sx, sx->buffer, sizeof(sx->buffer),
'2', ob->command_timeout);
- if (sx.ok && f.continue_more)
+ if (sx->ok && f.continue_more)
return yield; /* More addresses for another run */
}
else
/* Set up a pipe for proxying TLS for the new transport process */
smtp_peer_options |= OPTION_TLS;
- if ((sx.ok = socketpair(AF_UNIX, SOCK_STREAM, 0, pfd) == 0))
+ if ((sx->ok = socketpair(AF_UNIX, SOCK_STREAM, 0, pfd) == 0))
socket_fd = pfd[1];
else
- set_errno(sx.first_addr, errno, US"internal allocation problem",
+ set_errno(sx->first_addr, errno, US"internal allocation problem",
DEFER, FALSE, host
# ifdef EXPERIMENTAL_DSN_INFO
- , sx.smtp_greeting, sx.helo_response
+ , sx->smtp_greeting, sx->helo_response
# endif
);
}
/*XXX DSN_INFO: assume likely to do new HELO; but for greet we'll want to
propagate it from the initial
*/
- if (sx.ok && transport_pass_socket(tblock->name, host->name,
+ if (sx->ok && transport_pass_socket(tblock->name, host->name,
host->address, new_message_id, socket_fd))
{
- sx.send_quit = FALSE;
+ sx->send_quit = FALSE;
/* We have passed the client socket to a fresh transport process.
If TLS is still active, we need to proxy it for the transport we
{
testharness_pause_ms(100); /* let parent debug out */
/* does not return */
- smtp_proxy_tls(sx.cctx.tls_ctx, sx.buffer, sizeof(sx.buffer), pfd,
+ smtp_proxy_tls(sx->cctx.tls_ctx, sx->buffer, sizeof(sx->buffer), pfd,
ob->command_timeout);
}
close(pfd[0]);
/* tidy the inter-proc to disconn the proxy proc */
waitpid(pid, NULL, 0);
- tls_close(sx.cctx.tls_ctx, TLS_NO_SHUTDOWN);
- sx.cctx.tls_ctx = NULL;
- (void)close(sx.cctx.sock);
- sx.cctx.sock = -1;
+ tls_close(sx->cctx.tls_ctx, TLS_NO_SHUTDOWN);
+ sx->cctx.tls_ctx = NULL;
+ (void)close(sx->cctx.sock);
+ sx->cctx.sock = -1;
continue_transport = NULL;
continue_hostname = NULL;
return yield;
/* If RSET failed and there are addresses left, they get deferred. */
else
- set_errno(sx.first_addr, errno, msg, DEFER, FALSE, host
+ set_errno(sx->first_addr, errno, msg, DEFER, FALSE, host
#ifdef EXPERIMENTAL_DSN_INFO
- , sx.smtp_greeting, sx.helo_response
+ , sx->smtp_greeting, sx->helo_response
#endif
);
}
SEND_QUIT:
#ifdef TCP_CORK
-(void) setsockopt(sx.cctx.sock, IPPROTO_TCP, TCP_CORK, US &on, sizeof(on));
+(void) setsockopt(sx->cctx.sock, IPPROTO_TCP, TCP_CORK, US &on, sizeof(on));
#endif
-if (sx.send_quit) (void)smtp_write_command(&sx, SCMD_FLUSH, "QUIT\r\n");
+if (sx->send_quit) (void)smtp_write_command(sx, SCMD_FLUSH, "QUIT\r\n");
END_OFF:
#ifndef DISABLE_TLS
-tls_close(sx.cctx.tls_ctx, TLS_SHUTDOWN_NOWAIT);
-sx.cctx.tls_ctx = NULL;
+tls_close(sx->cctx.tls_ctx, TLS_SHUTDOWN_NOWAIT);
+sx->cctx.tls_ctx = NULL;
#endif
/* Close the socket, and return the appropriate value, first setting
case continue_more won't get set. */
HDEBUG(D_transport|D_acl|D_v) debug_printf_indent(" SMTP(close)>>\n");
-if (sx.send_quit)
+if (sx->send_quit)
{
- shutdown(sx.cctx.sock, SHUT_WR);
+ shutdown(sx->cctx.sock, SHUT_WR);
millisleep(20);
testharness_pause_ms(200);
- if (fcntl(sx.cctx.sock, F_SETFL, O_NONBLOCK) == 0)
- for (int i = 16; read(sx.cctx.sock, sx.inbuffer, sizeof(sx.inbuffer)) > 0 && i > 0;)
+ if (fcntl(sx->cctx.sock, F_SETFL, O_NONBLOCK) == 0)
+ for (int i = 16; read(sx->cctx.sock, sx->inbuffer, sizeof(sx->inbuffer)) > 0 && i > 0;)
i--; /* drain socket */
}
-(void)close(sx.cctx.sock);
+(void)close(sx->cctx.sock);
#ifndef DISABLE_EVENT
(void) event_raise(tblock->event_action, US"tcp:close", NULL);
{
int host_af;
int port = 25;
- uschar *interface = NULL; /* Outgoing interface to use; NULL => any */
- smtp_context sx;
+ uschar * interface = NULL; /* Outgoing interface to use; NULL => any */
+ smtp_context * sx = store_get(sizeof(*sx), TRUE); /* tainted buffers */
if (!host->address)
{
log_write(0, LOG_MAIN|LOG_PANIC, "<%s>: %s", addr->address,
addr->message);
- sx.addrlist = addr;
- sx.conn_args.host = host;
- sx.conn_args.host_af = host_af,
- sx.port = port;
- sx.conn_args.interface = interface;
- sx.helo_data = tf->helo_data;
- sx.conn_args.tblock = addr->transport;
- sx.verify = TRUE;
+ sx->addrlist = addr;
+ sx->conn_args.host = host;
+ sx->conn_args.host_af = host_af,
+ sx->port = port;
+ sx->conn_args.interface = interface;
+ sx->helo_data = tf->helo_data;
+ sx->conn_args.tblock = addr->transport;
+ sx->verify = TRUE;
tls_retry_connection:
/* Set the address state so that errors are recorded in it */
SMTP command to send. If we tried TLS but it failed, try again without
if permitted */
- yield = smtp_setup_conn(&sx, FALSE);
+ yield = smtp_setup_conn(sx, FALSE);
#ifndef DISABLE_TLS
if ( yield == DEFER
&& addr->basic_errno == ERRNO_TLSFAILURE
"%s: callout unencrypted to %s [%s] (not in hosts_require_tls)",
addr->message, host->name, host->address);
addr->transport_return = PENDING_DEFER;
- yield = smtp_setup_conn(&sx, TRUE);
+ yield = smtp_setup_conn(sx, TRUE);
}
#endif
if (yield != OK)
addr->authenticator = client_authenticator;
addr->auth_id = client_authenticated_id;
- sx.from_addr = from_address;
- sx.first_addr = sx.sync_addr = addr;
- sx.ok = FALSE; /*XXX these 3 last might not be needed for verify? */
- sx.send_rset = TRUE;
- sx.completed_addr = FALSE;
+ sx->from_addr = from_address;
+ sx->first_addr = sx->sync_addr = addr;
+ sx->ok = FALSE; /*XXX these 3 last might not be needed for verify? */
+ sx->send_rset = TRUE;
+ sx->completed_addr = FALSE;
new_domain_record.result = old_domain_cache_result == ccache_reject_mfnull
? ccache_reject_mfnull : ccache_accept;
Avoid using a SIZE option on the MAIL for all random-rcpt checks.
*/
- sx.avoid_option = OPTION_SIZE;
+ sx->avoid_option = OPTION_SIZE;
/* Remember when we last did a random test */
new_domain_record.random_stamp = time(NULL);
- if (smtp_write_mail_and_rcpt_cmds(&sx, &yield) == 0)
+ if (smtp_write_mail_and_rcpt_cmds(sx, &yield) == 0)
switch(addr->transport_return)
{
case PENDING_OK: /* random was accepted, unfortunately */
goto no_conn;
case FAIL: /* rejected: the preferred result */
new_domain_record.random_result = ccache_reject;
- sx.avoid_option = 0;
+ sx->avoid_option = 0;
/* Between each check, issue RSET, because some servers accept only
one recipient after MAIL FROM:<>.
XXX We don't care about that for postmaster_full. Should we? */
if ((done =
- smtp_write_command(&sx, SCMD_FLUSH, "RSET\r\n") >= 0 &&
- smtp_read_response(&sx, sx.buffer, sizeof(sx.buffer), '2', callout)))
+ smtp_write_command(sx, SCMD_FLUSH, "RSET\r\n") >= 0 &&
+ smtp_read_response(sx, sx->buffer, sizeof(sx->buffer), '2', callout)))
break;
HDEBUG(D_acl|D_v)
debug_printf_indent("problem after random/rset/mfrom; reopen conn\n");
random_local_part = NULL;
#ifndef DISABLE_TLS
- tls_close(sx.cctx.tls_ctx, TLS_SHUTDOWN_NOWAIT);
+ tls_close(sx->cctx.tls_ctx, TLS_SHUTDOWN_NOWAIT);
#endif
HDEBUG(D_transport|D_acl|D_v) debug_printf_indent(" SMTP(close)>>\n");
- (void)close(sx.cctx.sock);
- sx.cctx.sock = -1;
+ (void)close(sx->cctx.sock);
+ sx->cctx.sock = -1;
#ifndef DISABLE_EVENT
(void) event_raise(addr->transport->event_action,
US"tcp:close", NULL);
#endif
addr->address = main_address;
addr->transport_return = PENDING_DEFER;
- sx.first_addr = sx.sync_addr = addr;
- sx.ok = FALSE;
- sx.send_rset = TRUE;
- sx.completed_addr = FALSE;
+ sx->first_addr = sx->sync_addr = addr;
+ sx->ok = FALSE;
+ sx->send_rset = TRUE;
+ sx->completed_addr = FALSE;
goto tls_retry_connection;
case DEFER: /* 4xx response to random */
break; /* Just to be clear. ccache_unknown, !done. */
/* Re-setup for main verify, or for the error message when failing */
addr->address = main_address;
addr->transport_return = PENDING_DEFER;
- sx.first_addr = sx.sync_addr = addr;
- sx.ok = FALSE;
- sx.send_rset = TRUE;
- sx.completed_addr = FALSE;
+ sx->first_addr = sx->sync_addr = addr;
+ sx->ok = FALSE;
+ sx->send_rset = TRUE;
+ sx->completed_addr = FALSE;
}
else
done = TRUE;
if (done)
{
if (!(options & vopt_is_recipient && options & vopt_callout_no_cache))
- sx.avoid_option = OPTION_SIZE;
+ sx->avoid_option = OPTION_SIZE;
done = FALSE;
- switch(smtp_write_mail_and_rcpt_cmds(&sx, &yield))
+ switch(smtp_write_mail_and_rcpt_cmds(sx, &yield))
{
case 0: switch(addr->transport_return) /* ok so far */
{
case -1: /* MAIL response error */
*failure_ptr = US"mail";
- if (errno == 0 && sx.buffer[0] == '5')
+ if (errno == 0 && sx->buffer[0] == '5')
{
setflag(addr, af_verify_nsfail);
if (from_address[0] == 0)
cancel_cutthrough_connection(TRUE, US"postmaster verify");
HDEBUG(D_acl|D_v) debug_printf_indent("Cutthrough cancelled by presence of postmaster verify\n");
- done = smtp_write_command(&sx, SCMD_FLUSH, "RSET\r\n") >= 0
- && smtp_read_response(&sx, sx.buffer, sizeof(sx.buffer), '2', callout);
+ done = smtp_write_command(sx, SCMD_FLUSH, "RSET\r\n") >= 0
+ && smtp_read_response(sx, sx->buffer, sizeof(sx->buffer), '2', callout);
if (done)
{
addr->address = string_sprintf("postmaster@%.1000s", addr->domain);
addr->transport_return = PENDING_DEFER;
- sx.from_addr = pm_mailfrom;
- sx.first_addr = sx.sync_addr = addr;
- sx.ok = FALSE;
- sx.send_rset = TRUE;
- sx.completed_addr = FALSE;
- sx.avoid_option = OPTION_SIZE;
+ sx->from_addr = pm_mailfrom;
+ sx->first_addr = sx->sync_addr = addr;
+ sx->ok = FALSE;
+ sx->send_rset = TRUE;
+ sx->completed_addr = FALSE;
+ sx->avoid_option = OPTION_SIZE;
- if( smtp_write_mail_and_rcpt_cmds(&sx, &yield) == 0
+ if( smtp_write_mail_and_rcpt_cmds(sx, &yield) == 0
&& addr->transport_return == PENDING_OK
)
done = TRUE;
else
done = (options & vopt_callout_fullpm) != 0
- && smtp_write_command(&sx, SCMD_FLUSH,
+ && smtp_write_command(sx, SCMD_FLUSH,
"RCPT TO:<postmaster>\r\n") >= 0
- && smtp_read_response(&sx, sx.buffer,
- sizeof(sx.buffer), '2', callout);
+ && smtp_read_response(sx, sx->buffer,
+ sizeof(sx->buffer), '2', callout);
/* Sort out the cache record */
if (done)
new_domain_record.postmaster_result = ccache_accept;
- else if (errno == 0 && sx.buffer[0] == '5')
+ else if (errno == 0 && sx->buffer[0] == '5')
{
*failure_ptr = US"postmaster";
setflag(addr, af_verify_pmfail);
{
case ETIMEDOUT:
HDEBUG(D_verify) debug_printf("SMTP timeout\n");
- sx.send_quit = FALSE;
+ sx->send_quit = FALSE;
break;
#ifdef SUPPORT_I18N
break;
#endif
case ECONNREFUSED:
- sx.send_quit = FALSE;
+ sx->send_quit = FALSE;
break;
case 0:
- if (*sx.buffer == 0) Ustrcpy(sx.buffer, US"connection dropped");
+ if (*sx->buffer == 0) Ustrcpy(sx->buffer, US"connection dropped");
/*XXX test here is ugly; seem to have a split of responsibility for
building this message. Need to rationalise. Where is it done
*/
if (!addr->message) addr->message =
string_sprintf("response to \"%s\" was: %s",
- big_buffer, string_printing(sx.buffer));
+ big_buffer, string_printing(sx->buffer));
/* RFC 5321 section 4.2: the text portion of the response may have only
HT, SP, Printable US-ASCII. Deal with awkward chars by cutting the
just become a multiline response (but wrapped in the error code we
produce). */
- for (uschar * s = sx.buffer;
- *s && s < sx.buffer + sizeof(sx.buffer);
+ for (uschar * s = sx->buffer;
+ *s && s < sx->buffer + sizeof(sx->buffer);
s++)
{
uschar c = *s;
if (c != '\t' && c != '\n' && (c < ' ' || c > '~'))
{
- if (s - sx.buffer < sizeof(sx.buffer) - 12)
+ if (s - sx->buffer < sizeof(sx->buffer) - 12)
memcpy(s, "(truncated)", 12);
else
*s = '\0';
}
}
addr->user_message = options & vopt_is_recipient
- ? string_sprintf("Callout verification failed:\n%s", sx.buffer)
+ ? string_sprintf("Callout verification failed:\n%s", sx->buffer)
: string_sprintf("Called: %s\nSent: %s\nResponse: %s",
- host->address, big_buffer, sx.buffer);
+ host->address, big_buffer, sx->buffer);
/* Hard rejection ends the process */
- if (sx.buffer[0] == '5') /* Address rejected */
+ if (sx->buffer[0] == '5') /* Address rejected */
{
yield = FAIL;
done = TRUE;
&& !random_local_part
&& !pm_mailfrom
&& cutthrough.cctx.sock < 0
- && !sx.lmtp
+ && !sx->lmtp
)
{
HDEBUG(D_acl|D_v) debug_printf_indent("holding verify callout open for %s\n",
cutthrough.callout_hold_only = !cutthrough.delivery;
cutthrough.is_tls = tls_out.active.sock >= 0;
/* We assume no buffer in use in the outblock */
- cutthrough.cctx = sx.cctx;
+ cutthrough.cctx = sx->cctx;
cutthrough.nrcpt = 1;
cutthrough.transport = addr->transport->name;
cutthrough.interface = interface;
/* Ensure no cutthrough on multiple verifies that were incompatible */
if (options & vopt_callout_recipsender)
cancel_cutthrough_connection(TRUE, US"not usable for cutthrough");
- if (sx.send_quit)
- if (smtp_write_command(&sx, SCMD_FLUSH, "QUIT\r\n") != -1)
+ if (sx->send_quit)
+ if (smtp_write_command(sx, SCMD_FLUSH, "QUIT\r\n") != -1)
/* Wait a short time for response, and discard it */
- smtp_read_response(&sx, sx.buffer, sizeof(sx.buffer), '2', 1);
+ smtp_read_response(sx, sx->buffer, sizeof(sx->buffer), '2', 1);
- if (sx.cctx.sock >= 0)
+ if (sx->cctx.sock >= 0)
{
#ifndef DISABLE_TLS
- if (sx.cctx.tls_ctx)
+ if (sx->cctx.tls_ctx)
{
- tls_close(sx.cctx.tls_ctx, TLS_SHUTDOWN_NOWAIT);
- sx.cctx.tls_ctx = NULL;
+ tls_close(sx->cctx.tls_ctx, TLS_SHUTDOWN_NOWAIT);
+ sx->cctx.tls_ctx = NULL;
}
#endif
HDEBUG(D_transport|D_acl|D_v) debug_printf_indent(" SMTP(close)>>\n");
- (void)close(sx.cctx.sock);
- sx.cctx.sock = -1;
+ (void)close(sx->cctx.sock);
+ sx->cctx.sock = -1;
#ifndef DISABLE_EVENT
(void) event_raise(addr->transport->event_action, US"tcp:close", NULL);
#endif