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)
static int
find_control(const uschar * name, control_def * ol, int last)
{
-int first = 0;
-while (last > first)
+for (int first = 0; last > first; )
{
int middle = (first + last)/2;
uschar * s = ol[middle].name;
static int
acl_checkcondition(uschar * name, condition_def * list, int end)
{
-int start = 0;
-while (start < end)
+for (int start = 0; start < end; )
{
int mid = (start + end)/2;
int c = Ustrcmp(name, list[mid].name);
static int
acl_checkname(uschar *name, uschar **list, int end)
{
-int start = 0;
-
-while (start < end)
+for (int start = 0; start < end; )
{
int mid = (start + end)/2;
int c = Ustrcmp(name, list[mid]);
acl_block *this = NULL;
acl_condition_block *cond;
acl_condition_block **condp = NULL;
-uschar *s;
+uschar * s;
*error = NULL;
fn_hdrs_added(void)
{
gstring * g = NULL;
-header_line * h = acl_added_headers;
-uschar * s;
-uschar * cp;
-if (!h) return NULL;
-
-do
+for (header_line * 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);
/* Search previously logged warnings. They are kept in malloc
store so they can be freed at the start of a new message. */
- for (logged = acl_warn_logged; logged != NULL; logged = logged->next)
+ for (logged = acl_warn_logged; logged; logged = logged->next)
if (Ustrcmp(logged->text, text) == 0) break;
- if (logged == NULL)
+ if (!logged)
{
int length = Ustrlen(text) + 1;
log_write(0, LOG_MAIN, "%s", text);
/* If there's no user message, we are done. */
-if (user_message == NULL) return;
+if (!user_message) return;
/* If this isn't a message ACL, we can't do anything with a user message.
Log an error. */
if ((rc = host_name_lookup()) != OK)
{
- *log_msgptr = (rc == DEFER)?
- US"host lookup deferred for reverse lookup check"
- :
- string_sprintf("host lookup failed for reverse lookup check%s",
- host_lookup_msg);
+ *log_msgptr = rc == DEFER
+ ? US"host lookup deferred for reverse lookup check"
+ : string_sprintf("host lookup failed for reverse lookup check%s",
+ host_lookup_msg);
return rc; /* DEFER or FAIL */
}
acl_verify_csa_address(dns_answer *dnsa, dns_scan *dnss, int reset,
uschar *target)
{
-dns_record *rr;
-dns_address *da;
+int rc = CSA_FAIL_NOADDR;
-BOOL target_found = FALSE;
-
-for (rr = dns_next_rr(dnsa, dnss, reset);
- rr != NULL;
+for (dns_record * rr = dns_next_rr(dnsa, dnss, reset);
+ rr;
rr = dns_next_rr(dnsa, dnss, RESET_NEXT))
{
/* Check this is an address RR for the target hostname. */
if (strcmpic(target, rr->name) != 0) continue;
- target_found = TRUE;
+ rc = CSA_FAIL_MISMATCH;
/* Turn the target address RR into a list of textual IP addresses and scan
the list. There may be more than one if it is an A6 RR. */
- for (da = dns_address_from_rr(dnsa, rr); da != NULL; da = da->next)
+ for (dns_address * da = dns_address_from_rr(dnsa, rr); da; da = da->next)
{
/* If the client IP address matches the target IP address, it's good! */
using an unauthorized IP address, otherwise the target has no authorized IP
addresses. */
-if (target_found) return CSA_FAIL_MISMATCH;
-else return CSA_FAIL_NOADDR;
+return rc;
}
/* If we didn't break the loop then no appropriate records were found. */
-if (rr == NULL) return t->data.val = CSA_UNKNOWN;
+if (!rr) return t->data.val = CSA_UNKNOWN;
/* Do not check addresses if the target is ".", in accordance with RFC 2782.
A target of "." indicates there are no valid addresses, so the client cannot
/* Handle name/address consistency verification in a separate function. */
-for (vp= verify_type_list;
+for (vp = verify_type_list;
CS vp < CS verify_type_list + sizeof(verify_type_list);
vp++
)
/* 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
uschar * cond;
if (!(arc_state = acl_verify_arc())) return DEFER;
- DEBUG(D_acl) debug_printf_indent("ARC verify result %s\n", arc_state);
+ 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)))
/* Remaining items are optional; they apply to sender and recipient
verification, including "header sender" verification. */
-while ((ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size))
- != NULL)
+while ((ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size)))
{
if (strcmpic(ss, US"defer_ok") == 0) defer_ok = TRUE;
else if (strcmpic(ss, US"no_details") == 0) no_details = TRUE;
{
const uschar * sublist = ss;
int optsep = ',';
- uschar *opt;
uschar buffer[256];
- while (isspace(*sublist)) sublist++;
+ uschar * opt;
+ while (isspace(*sublist)) sublist++;
while ((opt = string_nextinlist(&sublist, &optsep, buffer, sizeof(buffer))))
{
callout_opt_t * op;
{
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;
log_msgptr for error messages
format format string
... supplementary arguments
- ss ratelimit option name
- where ACL_WHERE_xxxx indicating which ACL this is
Returns: ERROR
*/
ratelimit_error(uschar **log_msgptr, const char *format, ...)
{
va_list ap;
-uschar buffer[STRING_SPRINTF_BUFFER_SIZE];
+gstring * g =
+ string_cat(NULL, US"error in arguments to \"ratelimit\" condition: ");
+
va_start(ap, format);
-if (!string_vformat(buffer, sizeof(buffer), format, ap))
- log_write(0, LOG_MAIN|LOG_PANIC_DIE,
- "string_sprintf expansion was longer than " SIZE_T_FMT, sizeof(buffer));
+g = string_vformat(g, TRUE, format, ap);
va_end(ap);
-*log_msgptr = string_sprintf(
- "error in arguments to \"ratelimit\" condition: %s", buffer);
+
+gstring_reset_unused(g);
+*log_msgptr = string_from_gstring(g);
return ERROR;
}
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
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 */
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:
if (*p == '/')
{
const uschar *pp = p + 1;
- while (*pp != 0) pp++;
+ while (*pp) pp++;
fake_response_text = expand_string(string_copyn(p+1, pp-p-1));
p = pp;
}
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)
{
const uschar *pp = p + 8;
- while (*pp != 0 && *pp != '/') pp++;
+ while (*pp && *pp != '/') pp++;
submission_domain = string_copyn(p+8, pp-p-8);
p = pp;
}
else if (Ustrncmp(p, "/name=", 6) == 0)
{
const uschar *pp = p + 6;
- while (*pp != 0) pp++;
+ while (*pp) pp++;
submission_name = string_copy(parse_fix_phrase(p+6, pp-p-6,
big_buffer, big_buffer_size));
p = pp;
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 && !f.disable_delay_flush)
mac_smtp_fflush();
#if !defined(NO_POLL_H) && defined (POLLRDHUP)
HDEBUG(D_acl) debug_printf_indent("delay cancelled by peer close\n");
}
#else
- /* It appears to be impossible to detect that a TCP/IP connection has
- gone away without reading from it. This means that we cannot shorten
- the delay below if the client goes away, because we cannot discover
- that the client has closed its end of the connection. (The connection
- is actually in a half-closed state, waiting for the server to close its
- end.) It would be nice to be able to detect this state, so that the
- Exim process is not held up unnecessarily. However, it seems that we
- can't. The poll() function does not do the right thing, and in any case
- it is not always available.
- */
+ /* Lacking POLLRDHUP it appears to be impossible to detect that a
+ TCP/IP connection has gone away without reading from it. This means
+ that we cannot shorten the delay below if the client goes away,
+ because we cannot discover that the client has closed its end of the
+ connection. (The connection is actually in a half-closed state,
+ waiting for the server to close its end.) It would be nice to be able
+ to detect this state, so that the Exim process is not held up
+ unnecessarily. However, it seems that we can't. The poll() function
+ does not do the right thing, and in any case it is not always
+ available. */
while (delay > 0) delay = sleep(delay);
#endif
#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),
int logbits = 0;
int sep = 0;
const uschar *s = arg;
- uschar *ss;
+ uschar * ss;
while ((ss = string_nextinlist(&s, &sep, big_buffer, big_buffer_size)))
{
if (Ustrcmp(ss, "main") == 0) logbits |= LOG_MAIN;
}
while (isspace(*s)) s++;
-
if (logbits == 0) logbits = LOG_MAIN;
log_write(0, logbits, "%s", string_printing(s));
}
{
/* Separate the regular expression and any optional parameters. */
const uschar * list = arg;
- uschar *ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size);
- uschar *opt;
+ uschar * ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size);
+ uschar * opt;
BOOL defer_ok = FALSE;
int timeout = 0;
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 (*acl_text == 0) return NULL; /* No more data */
yield = acl_text; /* Potential data line */
- while (*acl_text != 0 && *acl_text != '\n') acl_text++;
+ while (*acl_text && *acl_text != '\n') acl_text++;
/* If we hit the end before a newline, we have the whole logical line. If
it's a comment, there's no more data to be given. Otherwise, yield it. */
{
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;
in the ACL tree, having read it into the POOL_PERM store pool so that it
persists between multiple messages. */
-if (acl == NULL)
+if (!acl)
{
int old_pool = store_pool;
if (fd >= 0) store_pool = POOL_PERM;
acl = acl_read(acl_getline, log_msgptr);
store_pool = old_pool;
- if (acl == NULL && *log_msgptr != NULL) return ERROR;
+ if (!acl && *log_msgptr) return ERROR;
if (fd >= 0)
{
tree_node *t = store_get_perm(sizeof(tree_node) + Ustrlen(ss));
/* Now we have an ACL to use. It's possible it may be NULL. */
-while (acl != NULL)
+while (acl)
{
int cond;
int basic_errno = 0;
&& (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. */