CONTROL_NO_PIPELINING,
CONTROL_QUEUE_ONLY,
+#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS)
+ CONTROL_REQUIRETLS,
+#endif
CONTROL_SUBMISSION,
CONTROL_SUPPRESS_LOCAL_FIXUPS,
#ifdef SUPPORT_I18N
// ACL_BIT_PRDR| /* Not allow one user to freeze for all */
ACL_BIT_NOTSMTP | ACL_BIT_MIME)
},
+
+
+#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS)
+[CONTROL_REQUIRETLS] =
+ { US"requiretls", FALSE,
+ (unsigned)
+ ~(ACL_BIT_MAIL | ACL_BIT_RCPT | ACL_BIT_PREDATA |
+ ACL_BIT_DATA | ACL_BIT_MIME |
+ ACL_BIT_NOTSMTP)
+ },
+#endif
+
[CONTROL_SUBMISSION] =
{ US"submission", TRUE,
(unsigned)
},
#ifdef SUPPORT_I18N
[CONTROL_UTF8_DOWNCONVERT] =
- { US"utf8_downconvert", TRUE, 0 }
+ { US"utf8_downconvert", TRUE, (unsigned) ~(ACL_BIT_RCPT | ACL_BIT_VRFY)
+ }
#endif
};
/* Loop for multiple header lines, taking care about continuations */
-for (p = q; *p != 0; )
+for (p = q; *p; p = q)
{
const uschar *s;
uschar * hdr;
for (;;)
{
q = Ustrchr(q, '\n'); /* we know there was a newline */
- if (*(++q) != ' ' && *q != '\t') break;
+ if (*++q != ' ' && *q != '\t') break;
}
/* If the line starts with a colon, interpret the instruction for where to
to the front of it. */
for (s = p; s < q - 1; s++)
- {
if (*s == ':' || !isgraph(*s)) break;
- }
- hdr = string_sprintf("%s%.*s", (*s == ':')? "" : "X-ACL-Warn: ", (int) (q - p), p);
+ hdr = string_sprintf("%s%.*s", *s == ':' ? "" : "X-ACL-Warn: ", (int) (q - p), p);
hlen = Ustrlen(hdr);
/* See if this line has already been added */
- while (*hptr != NULL)
+ while (*hptr)
{
if (Ustrncmp((*hptr)->text, hdr, hlen) == 0) break;
- hptr = &((*hptr)->next);
+ hptr = &(*hptr)->next;
}
/* Add if not previously present */
- if (*hptr == NULL)
+ if (!*hptr)
{
header_line *h = store_get(sizeof(header_line));
h->text = hdr;
h->type = newtype;
h->slen = hlen;
*hptr = h;
- hptr = &(h->next);
+ hptr = &h->next;
}
-
- /* Advance for next header line within the string */
-
- p = q;
}
}
fn_hdrs_added(void)
{
gstring * g = NULL;
-header_line * h = acl_added_headers;
-uschar * s;
-uschar * cp;
-
-if (!h) return NULL;
+header_line * h;
-do
+for (h = acl_added_headers; h; h = h->next)
{
- s = h->text;
- while ((cp = Ustrchr(s, '\n')) != NULL)
- {
- if (cp[1] == '\0') break;
-
- /* contains embedded newline; needs doubling */
- g = string_catn(g, s, cp-s+1);
- g = string_catn(g, US"\n", 1);
- s = cp+1;
- }
- /* last bit of header */
-
-/*XXX could we use add_listele? */
- g = string_catn(g, s, cp-s+1); /* newline-sep list */
+ int i = h->slen;
+ if (h->text[i-1] == '\n') i--;
+ g = string_append_listele_n(g, '\n', h->text, i);
}
-while((h = h->next));
-g->s[g->ptr - 1] = '\0'; /* overwrite last newline */
-return g->s;
+return g ? g->s : NULL;
}
static void
setup_remove_header(const uschar *hnames)
{
-if (*hnames != 0)
+if (*hnames)
acl_removed_headers = acl_removed_headers
? string_sprintf("%s : %s", acl_removed_headers, hnames)
: string_copy(hnames);
{ US"recipient", VERIFY_RCPT, ACL_BIT_RCPT, FALSE, 0 },
{ US"header_names_ascii", VERIFY_HDR_NAMES_ASCII, ACL_BIT_DATA | ACL_BIT_NOTSMTP, TRUE, 0 },
#ifdef EXPERIMENTAL_ARC
- { US"arc", VERIFY_ARC, ACL_BIT_DATA, TRUE , 0 },
+ { US"arc", VERIFY_ARC, ACL_BIT_DATA, FALSE , 0 },
#endif
};
switch(vp->value)
{
case VERIFY_REV_HOST_LKUP:
- if (sender_host_address == NULL) return OK;
+ if (!sender_host_address) return OK;
if ((rc = acl_verify_reverse(user_msgptr, log_msgptr)) == DEFER)
- while ((ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size)))
+ while ((ss = string_nextinlist(&list, &sep, NULL, 0)))
if (strcmpic(ss, US"defer_ok") == 0)
return OK;
return rc;
/* We can test the result of optional HELO verification that might have
occurred earlier. If not, we can attempt the verification now. */
- if (!helo_verified && !helo_verify_failed) smtp_verify_helo();
- return helo_verified? OK : FAIL;
+ if (!f.helo_verified && !f.helo_verify_failed) smtp_verify_helo();
+ return f.helo_verified ? OK : FAIL;
case VERIFY_CSA:
/* Do Client SMTP Authorization checks in a separate function, and turn the
DEBUG(D_acl) debug_printf_indent("CSA result %s\n", csa_status);
return csa_return_code[rc];
+#ifdef EXPERIMENTAL_ARC
+ case VERIFY_ARC:
+ { /* Do Authenticated Received Chain checks in a separate function. */
+ const uschar * condlist = CUS string_nextinlist(&list, &sep, NULL, 0);
+ int csep = 0;
+ uschar * cond;
+
+ if (!(arc_state = acl_verify_arc())) return DEFER;
+ DEBUG(D_acl) debug_printf_indent("ARC verify result %s %s%s%s\n", arc_state,
+ arc_state_reason ? "(":"", arc_state_reason, arc_state_reason ? ")":"");
+
+ if (!condlist) condlist = US"none:pass";
+ while ((cond = string_nextinlist(&condlist, &csep, NULL, 0)))
+ if (Ustrcmp(arc_state, cond) == 0) return OK;
+ return FAIL;
+ }
+#endif
+
case VERIFY_HDR_SYNTAX:
/* Check that all relevant header lines have the correct 5322-syntax. If there is
a syntax error, we return details of the error to the sender if configured to
See RFC 5322, 2.2. and RFC 6532, 3. */
rc = verify_check_header_names_ascii(log_msgptr);
- if (rc != OK && smtp_return_error_details && *log_msgptr != NULL)
+ if (rc != OK && smtp_return_error_details && *log_msgptr)
*user_msgptr = string_sprintf("Rejected after DATA: %s", *log_msgptr);
return rc;
/* Check that no recipient of this message is "blind", that is, every envelope
recipient must be mentioned in either To: or Cc:. */
- rc = verify_check_notblind();
- if (rc != OK)
+ if ((rc = verify_check_notblind()) != OK)
{
*log_msgptr = string_sprintf("bcc recipient detected");
if (smtp_return_error_details)
{
if (!*user_msgptr && *log_msgptr)
*user_msgptr = string_sprintf("Rejected after DATA: %s", *log_msgptr);
- if (rc == DEFER) acl_temp_details = TRUE;
+ if (rc == DEFER) f.acl_temp_details = TRUE;
}
}
}
addr2.user_message : addr2.message;
/* Allow details for temporary error if the address is so flagged. */
- if (testflag((&addr2), af_pass_message)) acl_temp_details = TRUE;
+ if (testflag((&addr2), af_pass_message)) f.acl_temp_details = TRUE;
/* Make $address_data visible */
deliver_address_data = addr2.prop.address_data;
size, which must be greater than or equal to zero. Zero is useful for
rate measurement as opposed to rate limiting. */
-sender_rate_limit = string_nextinlist(&arg, &sep, NULL, 0);
-if (sender_rate_limit == NULL)
+if (!(sender_rate_limit = string_nextinlist(&arg, &sep, NULL, 0)))
return ratelimit_error(log_msgptr, "sender rate limit not set");
limit = Ustrtod(sender_rate_limit, &ss);
constant. This must be strictly greater than zero, because zero leads to
run-time division errors. */
-sender_rate_period = string_nextinlist(&arg, &sep, NULL, 0);
-if (sender_rate_period == NULL) period = -1.0;
-else period = readconf_readtime(sender_rate_period, 0, FALSE);
+period = !(sender_rate_period = string_nextinlist(&arg, &sep, NULL, 0))
+ ? -1.0 : readconf_readtime(sender_rate_period, 0, FALSE);
if (period <= 0.0)
return ratelimit_error(log_msgptr,
"\"%s\" is not a time value", sender_rate_period);
/* Parse the other options. */
-while ((ss = string_nextinlist(&arg, &sep, big_buffer, big_buffer_size))
- != NULL)
+while ((ss = string_nextinlist(&arg, &sep, big_buffer, big_buffer_size)))
{
if (strcmpic(ss, US"leaky") == 0) leaky = TRUE;
else if (strcmpic(ss, US"strict") == 0) strict = TRUE;
zero and let the recorded rate decay as if nothing happened. */
RATE_SET(mode, PER_MAIL);
if (where > ACL_WHERE_NOTSMTP) badacl = TRUE;
- else count = message_size < 0 ? 0.0 : (double)message_size;
+ else count = message_size < 0 ? 0.0 : (double)message_size;
}
else if (strcmpic(ss, US"per_addr") == 0)
{
RATE_SET(mode, PER_RCPT);
if (where != ACL_WHERE_RCPT) badacl = TRUE, unique = US"*";
- else unique = string_sprintf("%s@%s", deliver_localpart, deliver_domain);
+ else unique = string_sprintf("%s@%s", deliver_localpart, deliver_domain);
}
else if (strncmpic(ss, US"count=", 6) == 0)
{
uschar *e;
count = Ustrtod(ss+6, &e);
if (count < 0.0 || *e != '\0')
- return ratelimit_error(log_msgptr,
- "\"%s\" is not a positive number", ss);
+ return ratelimit_error(log_msgptr, "\"%s\" is not a positive number", ss);
}
else if (strncmpic(ss, US"unique=", 7) == 0)
unique = string_copy(ss + 7);
- else if (key == NULL)
+ else if (!key)
key = string_copy(ss);
else
key = string_sprintf("%s/%s", key, ss);
return ratelimit_error(log_msgptr, "conflicting update modes");
if (badacl && (leaky || strict) && !noupdate)
return ratelimit_error(log_msgptr,
- "\"%s\" must not have /leaky or /strict option in %s ACL",
+ "\"%s\" must not have /leaky or /strict option, or cannot be used in %s ACL",
ratelimit_option_string[mode], acl_wherenames[where]);
/* Set the default values of any unset options. In readonly mode we
omit it. The smoothing constant (sender_rate_period) and the per_xxx options
are added to the key because they alter the meaning of the stored data. */
-if (key == NULL)
- key = (sender_host_address == NULL)? US"" : sender_host_address;
+if (!key)
+ key = !sender_host_address ? US"" : sender_host_address;
key = string_sprintf("%s/%s/%s%s",
sender_rate_period,
if (readonly)
anchor = &ratelimiters_cmd;
-else switch(mode) {
-case RATE_PER_CONN:
- anchor = &ratelimiters_conn;
- store_pool = POOL_PERM;
- break;
-case RATE_PER_BYTE:
-case RATE_PER_MAIL:
-case RATE_PER_ALLRCPTS:
- anchor = &ratelimiters_mail;
- break;
-case RATE_PER_ADDR:
-case RATE_PER_CMD:
-case RATE_PER_RCPT:
- anchor = &ratelimiters_cmd;
- break;
-default:
- anchor = NULL; /* silence an "unused" complaint */
- log_write(0, LOG_MAIN|LOG_PANIC_DIE,
- "internal ACL error: unknown ratelimit mode %d", mode);
- break;
-}
+else switch(mode)
+ {
+ case RATE_PER_CONN:
+ anchor = &ratelimiters_conn;
+ store_pool = POOL_PERM;
+ break;
+ case RATE_PER_BYTE:
+ case RATE_PER_MAIL:
+ case RATE_PER_ALLRCPTS:
+ anchor = &ratelimiters_mail;
+ break;
+ case RATE_PER_ADDR:
+ case RATE_PER_CMD:
+ case RATE_PER_RCPT:
+ anchor = &ratelimiters_cmd;
+ break;
+ default:
+ anchor = NULL; /* silence an "unused" complaint */
+ log_write(0, LOG_MAIN|LOG_PANIC_DIE,
+ "internal ACL error: unknown ratelimit mode %d", mode);
+ break;
+ }
-t = tree_search(*anchor, key);
-if (t != NULL)
+if ((t = tree_search(*anchor, key)))
{
dbd = t->data.ptr;
/* The following few lines duplicate some of the code below. */
gettimeofday(&tv, NULL);
-if (dbdb != NULL)
+if (dbdb)
{
/* Locate the basic ratelimit block inside the DB data. */
HDEBUG(D_acl) debug_printf_indent("ratelimit found key in database\n");
filter because we want its size to change if the limit changes. Note that
we keep the dbd pointer for copying the rate into the new data block. */
- if(unique != NULL && tv.tv_sec > dbdb->bloom_epoch + period)
+ if(unique && tv.tv_sec > dbdb->bloom_epoch + period)
{
HDEBUG(D_acl) debug_printf_indent("ratelimit discarding old Bloom filter\n");
dbdb = NULL;
/* Sanity check. */
- if(unique != NULL && dbdb_size < sizeof(*dbdb))
+ if(unique && dbdb_size < sizeof(*dbdb))
{
HDEBUG(D_acl) debug_printf_indent("ratelimit discarding undersize Bloom filter\n");
dbdb = NULL;
/* Allocate a new data block if the database lookup failed
or the Bloom filter passed its age limit. */
-if (dbdb == NULL)
+if (!dbdb)
{
- if (unique == NULL)
+ if (!unique)
{
/* No Bloom filter. This basic ratelimit block is initialized below. */
HDEBUG(D_acl) debug_printf_indent("ratelimit creating new rate data block\n");
/* Preserve any basic ratelimit data (which is our longer-term memory)
by copying it from the discarded block. */
- if (dbd != NULL)
+ if (dbd)
{
dbdb->dbd = *dbd;
dbd = &dbdb->dbd;
changes to the filter will be discarded and because count is already set to
zero. */
-if (unique != NULL && !readonly)
+if (unique && !readonly)
{
/* We identify unique events using a Bloom filter. (You can find my
notes on Bloom filters at http://fanf.livejournal.com/81696.html)
the new one, otherwise update the block from the database. The initial rate
is what would be computed by the code below for an infinite interval. */
-if (dbd == NULL)
+if (!dbd)
{
HDEBUG(D_acl) debug_printf_indent("ratelimit initializing new key's rate data\n");
dbd = &dbdb->dbd;
This matters for edge cases such as a limit of zero, when the client
should be completely blocked. */
-rc = (dbd->rate < limit)? FAIL : OK;
+rc = dbd->rate < limit ? FAIL : OK;
/* Update the state if the rate is low or if we are being strict. If we
are in leaky mode and the sender's rate is too high, we do not update
int sep = -'/';
#endif
-for (; cb != NULL; cb = cb->next)
+for (; cb; cb = cb->next)
{
const uschar *arg;
int control_type;
arg = cb->arg;
else if (!(arg = expand_string(cb->arg)))
{
- if (expand_string_forcedfail) continue;
+ if (f.expand_string_forcedfail) continue;
*log_msgptr = string_sprintf("failed to expand ACL string \"%s\": %s",
cb->arg, expand_string_message);
- return search_find_defer ? DEFER : ERROR;
+ return f.search_find_defer ? DEFER : ERROR;
}
/* Show condition, and expanded condition if it's different */
break;
case ACLC_AUTHENTICATED:
- rc = (sender_host_authenticated == NULL)? FAIL :
- match_isinlist(sender_host_authenticated, &arg, 0, NULL, NULL, MCL_STRING,
- TRUE, NULL);
+ rc = sender_host_authenticated ? match_isinlist(sender_host_authenticated,
+ &arg, 0, NULL, NULL, MCL_STRING, TRUE, NULL) : FAIL;
break;
#ifdef EXPERIMENTAL_BRIGHTMAIL
switch(control_type)
{
case CONTROL_AUTH_UNADVERTISED:
- allow_auth_unadvertised = TRUE;
+ f.allow_auth_unadvertised = TRUE;
break;
#ifdef EXPERIMENTAL_BRIGHTMAIL
#ifndef DISABLE_DKIM
case CONTROL_DKIM_VERIFY:
- dkim_disable_verify = TRUE;
+ f.dkim_disable_verify = TRUE;
#ifdef EXPERIMENTAL_DMARC
/* Since DKIM was blocked, skip DMARC too */
- dmarc_disable_verify = TRUE;
- dmarc_enable_forensic = FALSE;
+ f.dmarc_disable_verify = TRUE;
+ f.dmarc_enable_forensic = FALSE;
#endif
break;
#endif
#ifdef EXPERIMENTAL_DMARC
case CONTROL_DMARC_VERIFY:
- dmarc_disable_verify = TRUE;
+ f.dmarc_disable_verify = TRUE;
break;
case CONTROL_DMARC_FORENSIC:
- dmarc_enable_forensic = TRUE;
+ f.dmarc_enable_forensic = TRUE;
break;
#endif
#ifdef WITH_CONTENT_SCAN
case CONTROL_NO_MBOX_UNSPOOL:
- no_mbox_unspool = TRUE;
+ f.no_mbox_unspool = TRUE;
break;
#endif
case CONTROL_NO_MULTILINE:
- no_multiline_responses = TRUE;
+ f.no_multiline_responses = TRUE;
break;
case CONTROL_NO_PIPELINING:
- pipelining_enable = FALSE;
+ f.pipelining_enable = FALSE;
break;
case CONTROL_NO_DELAY_FLUSH:
- disable_delay_flush = TRUE;
+ f.disable_delay_flush = TRUE;
break;
case CONTROL_NO_CALLOUT_FLUSH:
- disable_callout_flush = TRUE;
+ f.disable_callout_flush = TRUE;
break;
case CONTROL_FAKEREJECT:
break;
case CONTROL_FREEZE:
- deliver_freeze = TRUE;
+ f.deliver_freeze = TRUE;
deliver_frozen_at = time(NULL);
freeze_tell = freeze_tell_config; /* Reset to configured value */
if (Ustrncmp(p, "/no_tell", 8) == 0)
break;
case CONTROL_QUEUE_ONLY:
- queue_only_policy = TRUE;
+ f.queue_only_policy = TRUE;
cancel_cutthrough_connection(TRUE, US"queueing forced");
break;
+#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS)
+ case CONTROL_REQUIRETLS:
+ tls_requiretls |= REQUIRETLS_MSG;
+ break;
+#endif
case CONTROL_SUBMISSION:
originator_name = US"";
- submission_mode = TRUE;
+ f.submission_mode = TRUE;
while (*p == '/')
{
if (Ustrncmp(p, "/sender_retain", 14) == 0)
{
p += 14;
- active_local_sender_retain = TRUE;
- active_local_from_check = FALSE;
+ f.active_local_sender_retain = TRUE;
+ f.active_local_from_check = FALSE;
}
else if (Ustrncmp(p, "/domain=", 8) == 0)
{
break;
case CONTROL_SUPPRESS_LOCAL_FIXUPS:
- suppress_local_fixups = TRUE;
+ f.suppress_local_fixups = TRUE;
break;
case CONTROL_CUTTHROUGH_DELIVERY:
ignored = US"PRDR active";
else
{
- if (deliver_freeze)
+ if (f.deliver_freeze)
ignored = US"frozen";
- else if (queue_only_policy)
+ else if (f.queue_only_policy)
ignored = US"queue-only";
else if (fake_response == FAIL)
ignored = US"fakereject";
else
{
- if (smtp_out != NULL && !disable_delay_flush)
+ if (smtp_out != NULL && !f.disable_delay_flush)
mac_smtp_fflush();
#if !defined(NO_POLL_H) && defined (POLLRDHUP)
#ifdef EXPERIMENTAL_DMARC
case ACLC_DMARC_STATUS:
- if (!dmarc_has_been_checked)
+ if (!f.dmarc_has_been_checked)
dmarc_process();
- dmarc_has_been_checked = TRUE;
+ f.dmarc_has_been_checked = TRUE;
/* used long way of dmarc_exim_expand_query() in case we need more
* view into the process in the future. */
rc = match_isinlist(dmarc_exim_expand_query(DMARC_VERIFY_STATUS),
expmessage = expand_string(user_message);
if (!expmessage)
{
- if (!expand_string_forcedfail)
+ if (!f.expand_string_forcedfail)
log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand ACL message \"%s\": %s",
user_message, expand_string_message);
}
expmessage = expand_string(log_message);
if (!expmessage)
{
- if (!expand_string_forcedfail)
+ if (!f.expand_string_forcedfail)
log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand ACL message \"%s\": %s",
log_message, expand_string_message);
}
{
if (!(ss = expand_string(s)))
{
- if (expand_string_forcedfail) return OK;
+ if (f.expand_string_forcedfail) return OK;
*log_msgptr = string_sprintf("failed to expand ACL string \"%s\": %s", s,
expand_string_message);
return ERROR;
&& (where == ACL_WHERE_QUIT || where == ACL_WHERE_NOTQUIT);
*log_msgptr = *user_msgptr = NULL;
- acl_temp_details = FALSE;
+ f.acl_temp_details = FALSE;
HDEBUG(D_acl) debug_printf_indent("processing \"%s\"\n", verbs[acl->verb]);
{
if (search_error_message != NULL && *search_error_message != 0)
*log_msgptr = search_error_message;
- if (smtp_return_error_details) acl_temp_details = TRUE;
+ if (smtp_return_error_details) f.acl_temp_details = TRUE;
}
else
- {
- acl_temp_details = TRUE;
- }
+ f.acl_temp_details = TRUE;
if (acl->verb != ACL_WARN) return DEFER;
break;
{
HDEBUG(D_acl) debug_printf_indent("end of %s: DEFER\n", acl_name);
if (acl_quit_check) goto badquit;
- acl_temp_details = TRUE;
+ f.acl_temp_details = TRUE;
return DEFER;
}
break;
return ret;
bad:
-if (expand_string_forcedfail) return ERROR;
+if (f.expand_string_forcedfail) return ERROR;
*log_msgptr = string_sprintf("failed to expand ACL string \"%s\": %s",
tmp, expand_string_message);
-return search_find_defer?DEFER:ERROR;
+return f.search_find_defer ? DEFER : ERROR;
}
case ACL_WHERE_PRDR:
#endif
- if (host_checking_callout) /* -bhc mode */
+ if (f.host_checking_callout) /* -bhc mode */
cancel_cutthrough_connection(TRUE, US"host-checking mode");
else if ( rc == OK
while (*s) s++;
do --s; while (!isdigit(*s));
if (*--s && isdigit(*s) && *--s && isdigit(*s)) *user_msgptr = s;
- acl_temp_details = TRUE;
+ f.acl_temp_details = TRUE;
}
else
{
}
deliver_domain = deliver_localpart = deliver_address_data =
- sender_address_data = NULL;
+ deliver_domain_data = sender_address_data = NULL;
/* A DISCARD response is permitted only for message ACLs, excluding the PREDATA
ACL, which is really in the middle of an SMTP command. */