/* ACL verbs */
-static uschar *verbs[] =
- { US"accept", US"defer", US"deny", US"discard", US"drop", US"require",
+static uschar *verbs[] = {
+ US"accept",
+ US"defer",
+ US"deny",
+ US"discard",
+ US"drop",
+ US"require",
US"warn" };
/* For each verb, the conditions for which "message" or "log_message" are used
#ifndef DISABLE_DKIM
ACLC_DKIM_SIGNER,
ACLC_DKIM_STATUS,
+#endif
+#ifdef EXPERIMENTAL_DMARC
+ ACLC_DMARC_STATUS,
#endif
ACLC_DNSLISTS,
ACLC_DOMAINS,
ACLC_SPF,
ACLC_SPF_GUESS,
#endif
+ ACLC_UDPSEND,
ACLC_VERIFY };
/* ACL conditions/modifiers: "delay", "control", "continue", "endpass",
#ifndef DISABLE_DKIM
US"dkim_signers",
US"dkim_status",
+#endif
+#ifdef EXPERIMENTAL_DMARC
+ US"dmarc_status",
#endif
US"dnslists",
US"domains",
US"spf",
US"spf_guess",
#endif
+ US"udpsend",
US"verify" };
#ifndef DISABLE_DKIM
CONTROL_DKIM_VERIFY,
#endif
+ #ifdef EXPERIMENTAL_DMARC
+ CONTROL_DMARC_VERIFY,
+ CONTROL_DMARC_FORENSIC,
+ #endif
CONTROL_DSCP,
CONTROL_ERROR,
CONTROL_CASEFUL_LOCAL_PART,
#ifndef DISABLE_DKIM
US"dkim_disable_verify",
#endif
+ #ifdef EXPERIMENTAL_DMARC
+ US"dmarc_disable_verify",
+ US"dmarc_enable_forensic",
+ #endif
US"dscp",
US"error",
US"caseful_local_part",
#ifndef DISABLE_DKIM
TRUE, /* dkim_signers */
TRUE, /* dkim_status */
+#endif
+#ifdef EXPERIMENTAL_DMARC
+ TRUE, /* dmarc_status */
#endif
TRUE, /* dnslists */
FALSE, /* domains */
TRUE, /* spf */
TRUE, /* spf_guess */
#endif
+ TRUE, /* udpsend */
TRUE /* verify */
};
#ifndef DISABLE_DKIM
FALSE, /* dkim_signers */
FALSE, /* dkim_status */
+#endif
+#ifdef EXPERIMENTAL_DMARC
+ FALSE, /* dmarc_status */
#endif
FALSE, /* dnslists */
FALSE, /* domains */
FALSE, /* spf */
FALSE, /* spf_guess */
#endif
+ TRUE, /* udpsend */
FALSE /* verify */
};
(unsigned int)
~((1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_RCPT)| /* add_header */
(1<<ACL_WHERE_PREDATA)|(1<<ACL_WHERE_DATA)|
+ #ifdef EXPERIMENTAL_PRDR
+ (1<<ACL_WHERE_PRDR)|
+ #endif
(1<<ACL_WHERE_MIME)|(1<<ACL_WHERE_NOTSMTP)|
(1<<ACL_WHERE_DKIM)|
(1<<ACL_WHERE_NOTSMTP_START)),
(1<<ACL_WHERE_AUTH)| /* bmi_optin */
(1<<ACL_WHERE_CONNECT)|(1<<ACL_WHERE_HELO)|
(1<<ACL_WHERE_DATA)|(1<<ACL_WHERE_MIME)|
+ #ifdef EXPERIMENTAL_PRDR
+ (1<<ACL_WHERE_PRDR)|
+ #endif
(1<<ACL_WHERE_ETRN)|(1<<ACL_WHERE_EXPN)|
(1<<ACL_WHERE_MAILAUTH)|
(1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_STARTTLS)|
#ifdef EXPERIMENTAL_DCC
(unsigned int)
- ~((1<<ACL_WHERE_DATA)|(1<<ACL_WHERE_NOTSMTP)), /* dcc */
+ ~((1<<ACL_WHERE_DATA)| /* dcc */
+ #ifdef EXPERIMENTAL_PRDR
+ (1<<ACL_WHERE_PRDR)|
+ #endif /* EXPERIMENTAL_PRDR */
+ (1<<ACL_WHERE_NOTSMTP)),
#endif
#ifdef WITH_CONTENT_SCAN
#ifdef WITH_OLD_DEMIME
(unsigned int)
- ~((1<<ACL_WHERE_DATA)|(1<<ACL_WHERE_NOTSMTP)), /* demime */
+ ~((1<<ACL_WHERE_DATA)| /* demime */
+ #ifdef EXPERIMENTAL_PRDR
+ (1<<ACL_WHERE_PRDR)|
+ #endif /* EXPERIMENTAL_PRDR */
+ (1<<ACL_WHERE_NOTSMTP)),
#endif
#ifndef DISABLE_DKIM
~(1<<ACL_WHERE_DKIM), /* dkim_status */
#endif
+ #ifdef EXPERIMENTAL_DMARC
+ (unsigned int)
+ ~(1<<ACL_WHERE_DATA), /* dmarc_status */
+ #endif
+
(1<<ACL_WHERE_NOTSMTP)| /* dnslists */
(1<<ACL_WHERE_NOTSMTP_START),
(unsigned int)
- ~(1<<ACL_WHERE_RCPT), /* domains */
+ ~((1<<ACL_WHERE_RCPT) /* domains */
+ #ifdef EXPERIMENTAL_PRDR
+ |(1<<ACL_WHERE_PRDR)
+ #endif
+ ),
(1<<ACL_WHERE_NOTSMTP)| /* encrypted */
(1<<ACL_WHERE_CONNECT)|
(1<<ACL_WHERE_NOTSMTP_START),
(unsigned int)
- ~(1<<ACL_WHERE_RCPT), /* local_parts */
+ ~((1<<ACL_WHERE_RCPT) /* local_parts */
+ #ifdef EXPERIMENTAL_PRDR
+ |(1<<ACL_WHERE_PRDR)
+ #endif
+ ),
0, /* log_message */
#ifdef WITH_CONTENT_SCAN
(unsigned int)
- ~((1<<ACL_WHERE_DATA)|(1<<ACL_WHERE_NOTSMTP)), /* malware */
+ ~((1<<ACL_WHERE_DATA)| /* malware */
+ #ifdef EXPERIMENTAL_PRDR
+ (1<<ACL_WHERE_PRDR)|
+ #endif /* EXPERIMENTAL_PRDR */
+ (1<<ACL_WHERE_NOTSMTP)),
#endif
0, /* message */
#ifdef WITH_CONTENT_SCAN
(unsigned int)
- ~((1<<ACL_WHERE_DATA)|(1<<ACL_WHERE_NOTSMTP)| /* regex */
+ ~((1<<ACL_WHERE_DATA)| /* regex */
+ #ifdef EXPERIMENTAL_PRDR
+ (1<<ACL_WHERE_PRDR)|
+ #endif /* EXPERIMENTAL_PRDR */
+ (1<<ACL_WHERE_NOTSMTP)|
(1<<ACL_WHERE_MIME)),
#endif
(unsigned int)
~((1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_RCPT)| /* remove_header */
(1<<ACL_WHERE_PREDATA)|(1<<ACL_WHERE_DATA)|
+ #ifdef EXPERIMENTAL_PRDR
+ (1<<ACL_WHERE_PRDR)|
+ #endif
(1<<ACL_WHERE_MIME)|(1<<ACL_WHERE_NOTSMTP)|
(1<<ACL_WHERE_NOTSMTP_START)),
#ifdef WITH_CONTENT_SCAN
(unsigned int)
- ~((1<<ACL_WHERE_DATA)|(1<<ACL_WHERE_NOTSMTP)), /* spam */
+ ~((1<<ACL_WHERE_DATA)| /* spam */
+ #ifdef EXPERIMENTAL_PRDR
+ (1<<ACL_WHERE_PRDR)|
+ #endif /* EXPERIMENTAL_PRDR */
+ (1<<ACL_WHERE_NOTSMTP)),
#endif
#ifdef EXPERIMENTAL_SPF
(1<<ACL_WHERE_NOTSMTP_START),
#endif
+ 0, /* udpsend */
+
/* Certain types of verify are always allowed, so we let it through
always and check in the verify function itself */
#ifndef DISABLE_DKIM
(1<<ACL_WHERE_DATA)|(1<<ACL_WHERE_NOTSMTP)| /* dkim_disable_verify */
+ #ifdef EXPERIMENTAL_PRDR
+ (1<<ACL_WHERE_PRDR)|
+ #endif /* EXPERIMENTAL_PRDR */
+ (1<<ACL_WHERE_NOTSMTP_START),
+ #endif
+
+ #ifdef EXPERIMENTAL_DMARC
+ (1<<ACL_WHERE_DATA)|(1<<ACL_WHERE_NOTSMTP)| /* dmarc_disable_verify */
+ (1<<ACL_WHERE_NOTSMTP_START),
+ (1<<ACL_WHERE_DATA)|(1<<ACL_WHERE_NOTSMTP)| /* dmarc_enable_forensic */
(1<<ACL_WHERE_NOTSMTP_START),
#endif
(unsigned int)
~((1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_RCPT)| /* freeze */
(1<<ACL_WHERE_PREDATA)|(1<<ACL_WHERE_DATA)|
+ // (1<<ACL_WHERE_PRDR)| /* Not allow one user to freeze for all */
(1<<ACL_WHERE_NOTSMTP)|(1<<ACL_WHERE_MIME)),
(unsigned int)
~((1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_RCPT)| /* queue_only */
(1<<ACL_WHERE_PREDATA)|(1<<ACL_WHERE_DATA)|
+ // (1<<ACL_WHERE_PRDR)| /* Not allow one user to freeze for all */
(1<<ACL_WHERE_NOTSMTP)|(1<<ACL_WHERE_MIME)),
(unsigned int)
(unsigned int)
~((1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_RCPT)| /* no_mbox_unspool */
(1<<ACL_WHERE_PREDATA)|(1<<ACL_WHERE_DATA)|
+ // (1<<ACL_WHERE_PRDR)| /* Not allow one user to freeze for all */
(1<<ACL_WHERE_MIME)),
#endif
(unsigned int)
~((1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_RCPT)| /* fakedefer */
(1<<ACL_WHERE_PREDATA)|(1<<ACL_WHERE_DATA)|
+ #ifdef EXPERIMENTAL_PRDR
+ (1<<ACL_WHERE_PRDR)|
+ #endif /* EXPERIMENTAL_PRDR */
(1<<ACL_WHERE_MIME)),
(unsigned int)
~((1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_RCPT)| /* fakereject */
(1<<ACL_WHERE_PREDATA)|(1<<ACL_WHERE_DATA)|
+ #ifdef EXPERIMENTAL_PRDR
+ (1<<ACL_WHERE_PRDR)|
+ #endif /* EXPERIMENTAL_PRDR */
(1<<ACL_WHERE_MIME)),
(1<<ACL_WHERE_NOTSMTP)| /* no_multiline */
{ US"debug", CONTROL_DEBUG, TRUE },
#ifndef DISABLE_DKIM
{ US"dkim_disable_verify", CONTROL_DKIM_VERIFY, FALSE },
+#endif
+#ifdef EXPERIMENTAL_DMARC
+ { US"dmarc_disable_verify", CONTROL_DMARC_VERIFY, FALSE },
+ { US"dmarc_enable_forensic", CONTROL_DMARC_FORENSIC, FALSE },
#endif
{ US"dscp", CONTROL_DSCP, TRUE },
{ US"caseful_local_part", CONTROL_CASEFUL_LOCAL_PART, FALSE },
+/*************************************************
+* The udpsend ACL modifier *
+*************************************************/
+
+/* Called by acl_check_condition() below.
+
+Arguments:
+ arg the option string for udpsend=
+ log_msgptr for error messages
+
+Returns: OK - Completed.
+ DEFER - Problem with DNS lookup.
+ ERROR - Syntax error in options.
+*/
+
+static int
+acl_udpsend(uschar *arg, uschar **log_msgptr)
+{
+int sep = 0;
+uschar *hostname;
+uschar *portstr;
+uschar *portend;
+host_item *h;
+int portnum;
+int host_af;
+int len;
+int r, s;
+
+hostname = string_nextinlist(&arg, &sep, NULL, 0);
+portstr = string_nextinlist(&arg, &sep, NULL, 0);
+
+if (hostname == NULL)
+ {
+ *log_msgptr = "missing destination host in \"udpsend\" modifier";
+ return ERROR;
+ }
+if (portstr == NULL)
+ {
+ *log_msgptr = "missing destination port in \"udpsend\" modifier";
+ return ERROR;
+ }
+if (arg == NULL)
+ {
+ *log_msgptr = "missing datagram payload in \"udpsend\" modifier";
+ return ERROR;
+ }
+portnum = Ustrtol(portstr, &portend, 10);
+if (*portend != '\0')
+ {
+ *log_msgptr = "bad destination port in \"udpsend\" modifier";
+ return ERROR;
+ }
+
+/* Make a single-item host list. */
+h = store_get(sizeof(host_item));
+memset(h, 0, sizeof(host_item));
+h->name = hostname;
+h->port = portnum;
+h->mx = MX_NONE;
+
+if (string_is_ip_address(hostname, NULL))
+ h->address = hostname, r = HOST_FOUND;
+else
+ r = host_find_byname(h, NULL, 0, NULL, FALSE);
+if (r == HOST_FIND_FAILED || r == HOST_FIND_AGAIN)
+ {
+ *log_msgptr = "DNS lookup failed in \"udpsend\" modifier";
+ return DEFER;
+ }
+
+HDEBUG(D_acl)
+ debug_printf("udpsend [%s]:%d %s\n", h->address, portnum, arg);
+
+host_af = (Ustrchr(h->address, ':') == NULL)? AF_INET:AF_INET6;
+r = s = ip_socket(SOCK_DGRAM, host_af);
+if (r < 0) goto defer;
+r = ip_connect(s, host_af, h->address, portnum, 1);
+if (r < 0) goto defer;
+len = strlen(arg);
+r = send(s, arg, len, MSG_NOSIGNAL);
+if (r < 0) goto defer;
+if (r < len)
+ {
+ *log_msgptr =
+ string_sprintf("\"udpsend\" truncated from %d to %d octets", len, r);
+ return DEFER;
+ }
+
+HDEBUG(D_acl)
+ debug_printf("udpsend %d bytes\n", r);
+
+return OK;
+
+defer:
+*log_msgptr = string_sprintf("\"udpsend\" failed: %s", strerror(errno));
+return DEFER;
+}
+
+
+
/*************************************************
* Handle conditions/modifiers on an ACL item *
*************************************************/
#ifndef DISABLE_DKIM
case CONTROL_DKIM_VERIFY:
dkim_disable_verify = TRUE;
+ #ifdef EXPERIMENTAL_DMARC
+ /* Since DKIM was blocked, skip DMARC too */
+ dmarc_disable_verify = TRUE;
+ dmarc_enable_forensic = FALSE;
+ #endif
+ break;
+ #endif
+
+ #ifdef EXPERIMENTAL_DMARC
+ case CONTROL_DMARC_VERIFY:
+ dmarc_disable_verify = TRUE;
+ break;
+
+ case CONTROL_DMARC_FORENSIC:
+ dmarc_enable_forensic = TRUE;
break;
#endif
break;
#endif
+ #ifdef EXPERIMENTAL_DMARC
+ case ACLC_DMARC_STATUS:
+ if (!dmarc_has_been_checked)
+ dmarc_process();
+ 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),
+ &arg,0,NULL,NULL,MCL_STRING,TRUE,NULL);
+ break;
+ #endif
+
case ACLC_DNSLISTS:
rc = verify_check_dnsbl(&arg);
break;
break;
#endif
+ case ACLC_UDPSEND:
+ rc = acl_udpsend(arg, log_msgptr);
+ break;
+
/* If the verb is WARN, discard any user message from verification, because
such messages are SMTP responses, not header additions. The latter come
only from explicit "message" modifiers. However, put the user message into
/* Alternate interface for ACL, used by expansions */
int
-acl_eval(int where, uschar *recipient, uschar *s, uschar **user_msgptr,
- uschar **log_msgptr)
+acl_eval(int where, uschar *s, uschar **user_msgptr, uschar **log_msgptr)
{
-int rc;
address_item adb;
address_item *addr = NULL;
{
adb = address_defaults;
addr = &adb;
- addr->address = recipient;
- if (deliver_split_address(addr) == DEFER)
- {
- *log_msgptr = US"defer in percent_hack_domains check";
- return DEFER;
- }
- deliver_domain = addr->domain;
- deliver_localpart = addr->local_part;
+ addr->address = expand_string(US"$local_part@$domain");
+ addr->domain = deliver_domain;
+ addr->local_part = deliver_localpart;
+ addr->cc_local_part = deliver_localpart;
+ addr->lc_local_part = deliver_localpart;
}
return acl_check_internal(where, addr, s, 0, user_msgptr, log_msgptr);
ratelimiters_cmd = NULL;
log_reject_target = LOG_MAIN|LOG_REJECT;
-if (where == ACL_WHERE_RCPT)
+#ifdef EXPERIMENTAL_PRDR
+if (where == ACL_WHERE_RCPT || where == ACL_WHERE_PRDR )
+#else
+if (where == ACL_WHERE_RCPT )
+#endif
{
adb = address_defaults;
addr = &adb;
switch (where)
{
case ACL_WHERE_RCPT:
+#ifdef EXPERIMENTAL_PRDR
+case ACL_WHERE_PRDR:
+#endif
if( rcpt_count > 1 )
cancel_cutthrough_connection("more than one recipient");
else if (rc == OK && cutthrough_delivery && cutthrough_fd < 0)