{
smtp_transport_options_block *ob =
(smtp_transport_options_block *)addr->transport->options_block;
+ smtp_context * sx = NULL;
/* The information wasn't available in the cache, so we have to do a real
callout and save the result in the cache for next time, unless no_cache is set,
{
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 */
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;
+ if (!sx) sx = store_get(sizeof(*sx), TRUE); /* tainted buffers */
+ memset(sx, 0, sizeof(*sx));
+
+ 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
colon = Ustrchr(h->text, ':');
s = colon + 1;
- while (isspace(*s)) s++;
+ Uskip_whitespace(&s);
/* Loop for multiple addresses in the header, enabling group syntax. Note
that we have to reset this after the header has been scanned. */
{
if (!f.allow_unqualified_recipient) recipient = NULL;
}
- if (recipient == NULL) errmess = US"unqualified address not permitted";
+ if (!recipient) errmess = US"unqualified address not permitted";
}
/* It's an error if no address could be extracted, except for the special
/* Advance to the next address */
s = ss + (terminator ? 1 : 0);
- while (isspace(*s)) s++;
+ Uskip_whitespace(&s);
} /* Next address */
f.parse_allow_group = FALSE;
colon = Ustrchr(h->text, ':');
s = colon + 1;
- while (isspace(*s)) s++;
+ Uskip_whitespace(&s);
/* Loop for multiple addresses in the header, enabling group syntax. Note
that we have to reset this after the header has been scanned. */
/* Advance to the next address */
s = ss + (terminator ? 1:0);
- while (isspace(*s)) s++;
+ Uskip_whitespace(&s);
} /* Next address */
f.parse_allow_group = FALSE;
/* If we found an empty address, just carry on with the next one, but
kill the message. */
- if (address == NULL && Ustrcmp(*log_msgptr, "empty address") == 0)
+ if (!address && Ustrcmp(*log_msgptr, "empty address") == 0)
{
*log_msgptr = NULL;
s = ss;
function, and ensure that the failing address gets added to the error
message. */
- if (address == NULL)
+ if (!address)
{
new_ok = FAIL;
while (ss > s && isspace(ss[-1])) ss--;
BOOL isquery = FALSE;
BOOL isiponly = cb->host_name != NULL && cb->host_name[0] == 0;
const uschar *t;
-uschar *semicolon;
+uschar * semicolon, * endname, * opts;
uschar **aliases;
/* Optimize for the special case when the pattern is "*". */
local host's IP addresses. */
if (*ss == '@')
- {
if (ss[1] == 0)
{
if (isiponly) return ERROR;
if (Ustrcmp(ip->address, cb->host_address) == 0) return OK;
return FAIL;
}
- }
/* If the pattern is an IP address, optionally followed by a bitmask count, do
a (possibly masked) comparison with the current IP address. */
/* See if there is a semicolon in the pattern */
-semicolon = Ustrchr(ss, ';');
+if ((semicolon = Ustrchr(ss, ';')))
+ endname = (opts = Ustrchr(ss, ',')) ? opts : semicolon;
+if (opts)
+ {
+ opts++;
+ opts = string_copyn(opts, semicolon - opts);
+ }
/* If we are doing an IP address only match, then all lookups must be IP
address lookups, even if there is no "net-". */
if (isiponly)
- {
iplookup = semicolon != NULL;
- }
/* Otherwise, if the item is of the form net[n]-lookup;<file|query> then it is
a lookup on a masked IP network, in textual form. We obey this code even if we
From release 4.30, the use of net- for query style is no longer needed, but we
retain it for backward compatibility. */
-if (Ustrncmp(ss, "net", 3) == 0 && semicolon != NULL)
+if (Ustrncmp(ss, "net", 3) == 0 && semicolon)
{
mlen = 0;
for (t = ss + 3; isdigit(*t); t++) mlen = mlen * 10 + *t - '0';
if (mlen == 0 && t == ss+3) mlen = -1; /* No mask supplied */
- iplookup = (*t++ == '-');
+ iplookup = *t++ == '-';
}
else
t = ss;
/* Find the search type */
- search_type = search_findtype(t, semicolon - t);
+ search_type = search_findtype(t, endname - t);
if (search_type < 0) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s",
search_error_message);
if (!(handle = search_open(filename, search_type, 0, NULL, NULL)))
log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s", search_error_message);
- result = search_find(handle, filename, key, -1, NULL, 0, 0, NULL);
+ result = search_find(handle, filename, key, -1, NULL, 0, 0, NULL, opts);
if (valueptr) *valueptr = result;
return result ? OK : f.search_find_defer ? DEFER: FAIL;
}
outgoing hosts, the name is always given explicitly. If it is NULL, it means we
must use sender_host_name and its aliases, looking them up if necessary. */
-if (cb->host_name != NULL) /* Explicit host name given */
+if (cb->host_name) /* Explicit host name given */
return match_check_string(cb->host_name, ss, -1, TRUE, TRUE, TRUE,
valueptr);
$sender_host_name causes it to be looked up, so we don't need to do the lookup
on spec. */
-if ((semicolon = Ustrchr(ss, ';')) != NULL)
+if ((semicolon = Ustrchr(ss, ';')))
{
- const uschar *affix;
+ const uschar * affix, * opts;
int partial, affixlen, starflags, id;
*semicolon = 0;
- id = search_findtype_partial(ss, &partial, &affix, &affixlen, &starflags);
+ id = search_findtype_partial(ss, &partial, &affix, &affixlen, &starflags,
+ &opts);
*semicolon=';';
if (id < 0) /* Unknown lookup type */
tree_node *t;
dnsbl_cache_block *cb;
int old_pool = store_pool;
-uschar query[256]; /* DNS domain max length */
+uschar * query;
+int qlen;
/* Construct the specific query domainname */
-if (!string_format(query, sizeof(query), "%s.%s", prepend, domain))
+query = string_sprintf("%s.%s", prepend, domain);
+if ((qlen = Ustrlen(query)) >= 256)
{
log_write(0, LOG_MAIN|LOG_PANIC, "dnslist query is too long "
"(ignored): %s...", query);
else
{ /* Set up a tree entry to cache the lookup */
- t = store_get(sizeof(tree_node) + Ustrlen(query), is_tainted(query));
+ t = store_get(sizeof(tree_node) + qlen + 1 + 1, is_tainted(query));
Ustrcpy(t->name, query);
t->data.ptr = cb = store_get(sizeof(dnsbl_cache_block), FALSE);
(void)tree_insertnode(&dnsbl_cache, t);
for (da = cb->rhs; da; da = da->next)
{
int ipsep = ',';
- uschar ip[46];
const uschar *ptr = iplist;
uschar *res;
if (!bitmask)
{
- while ((res = string_nextinlist(&ptr, &ipsep, ip, sizeof(ip))))
- if (Ustrcmp(CS da->address, ip) == 0)
+ while ((res = string_nextinlist(&ptr, &ipsep, NULL, 0)))
+ if (Ustrcmp(CS da->address, res) == 0)
break;
}
/* Scan the returned addresses, skipping any that are IPv6 */
- while ((res = string_nextinlist(&ptr, &ipsep, ip, sizeof(ip))))
+ while ((res = string_nextinlist(&ptr, &ipsep, NULL, 0)))
{
- if (host_aton(ip, address) != 1) continue;
+ if (host_aton(res, address) != 1) continue;
if ((address[0] & mask) == address[0]) break;
}
}
int defer_return = FAIL;
const uschar *list = *listptr;
uschar *domain;
-uschar buffer[1024];
uschar revadd[128]; /* Long enough for IPv6 address */
/* Indicate that the inverted IP address is not yet set up */
/* Loop through all the domains supplied, until something matches */
-while ((domain = string_nextinlist(&list, &sep, buffer, sizeof(buffer))))
+while ((domain = string_nextinlist(&list, &sep, NULL, 0)))
{
int rc;
BOOL bitmask = FALSE;