Bug 1454: Option -oMm for message reference
[exim.git] / src / src / acl.c
index 4f64e0a53e5f2e8140027b28c10dba33a6622615..270a2f2c064240223907bd32a19ad811609b9089 100644 (file)
@@ -2,7 +2,7 @@
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) University of Cambridge 1995 - 2012 */
+/* Copyright (c) University of Cambridge 1995 - 2014 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 /* Code for handling Access Control Lists (ACLs) */
@@ -107,6 +107,7 @@ enum { ACLC_ACL,
        ACLC_SPF,
        ACLC_SPF_GUESS,
 #endif
+       ACLC_UDPSEND,
        ACLC_VERIFY };
 
 /* ACL conditions/modifiers: "delay", "control", "continue", "endpass",
@@ -171,6 +172,7 @@ static uschar *conditions[] = {
   US"spf",
   US"spf_guess",
 #endif
+  US"udpsend",
   US"verify" };
 
 
@@ -315,6 +317,7 @@ static uschar cond_expand_at_top[] = {
   TRUE,    /* spf */
   TRUE,    /* spf_guess */
 #endif
+  TRUE,    /* udpsend */
   TRUE     /* verify */
 };
 
@@ -379,6 +382,7 @@ static uschar cond_modifiers[] = {
   FALSE,   /* spf */
   FALSE,   /* spf_guess */
 #endif
+  TRUE,    /* udpsend */
   FALSE    /* verify */
 };
 
@@ -579,6 +583,8 @@ static unsigned int cond_forbids[] = {
     (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 */
 
@@ -1608,6 +1614,7 @@ else
 DNS_LOOKUP_AGAIN:
 #endif
 
+lookup_dnssec_authenticated = NULL;
 switch (dns_lookup(&dnsa, target, type, NULL))
   {
   /* If something bad happened (most commonly DNS_AGAIN), defer. */
@@ -1644,7 +1651,8 @@ switch (dns_lookup(&dnsa, target, type, NULL))
 *************************************************/
 
 enum { VERIFY_REV_HOST_LKUP, VERIFY_CERT, VERIFY_HELO, VERIFY_CSA, VERIFY_HDR_SYNTAX,
-  VERIFY_NOT_BLIND, VERIFY_HDR_SNDR, VERIFY_SNDR, VERIFY_RCPT
+       VERIFY_NOT_BLIND, VERIFY_HDR_SNDR, VERIFY_SNDR, VERIFY_RCPT,
+       VERIFY_HDR_NAMES_ASCII
   };
 typedef struct {
   uschar * name;
@@ -1664,7 +1672,8 @@ static verify_type_t verify_type_list[] = {
     { US"sender",              VERIFY_SNDR,            (1<<ACL_WHERE_MAIL)|(1<<ACL_WHERE_RCPT)
                        |(1<<ACL_WHERE_PREDATA)|(1<<ACL_WHERE_DATA)|(1<<ACL_WHERE_NOTSMTP),
                                                                                FALSE, 6 },
-    { US"recipient",           VERIFY_RCPT,            (1<<ACL_WHERE_RCPT),    FALSE, 0 }
+    { US"recipient",           VERIFY_RCPT,            (1<<ACL_WHERE_RCPT),    FALSE, 0 },
+    { US"header_names_ascii",  VERIFY_HDR_NAMES_ASCII, (1<<ACL_WHERE_DATA)|(1<<ACL_WHERE_NOTSMTP), TRUE, 0 }
   };
 
 
@@ -1814,6 +1823,15 @@ switch(vp->value)
       *user_msgptr = string_sprintf("Rejected after DATA: %s", *log_msgptr);
     return rc;
 
+  case VERIFY_HDR_NAMES_ASCII:
+    /* Check that all header names are true 7 bit strings
+    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)
+      *user_msgptr = string_sprintf("Rejected after DATA: %s", *log_msgptr);
+    return rc;
+
   case VERIFY_NOT_BLIND:
     /* Check that no recipient of this message is "blind", that is, every envelope
     recipient must be mentioned in either To: or Cc:. */
@@ -2196,8 +2214,8 @@ return rc;
 
 BAD_VERIFY:
 *log_msgptr = string_sprintf("expected \"sender[=address]\", \"recipient\", "
-  "\"helo\", \"header_syntax\", \"header_sender\" or "
-  "\"reverse_host_lookup\" at start of ACL condition "
+  "\"helo\", \"header_syntax\", \"header_sender\", \"header_names_ascii\" "
+  "or \"reverse_host_lookup\" at start of ACL condition "
   "\"verify %s\"", arg);
 return ERROR;
 }
@@ -2816,6 +2834,110 @@ return rc;
 
 
 
+/*************************************************
+*            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 len;
+int r, s;
+uschar * errstr;
+
+hostname = string_nextinlist(&arg, &sep, NULL, 0);
+portstr = string_nextinlist(&arg, &sep, NULL, 0);
+
+if (hostname == NULL)
+  {
+  *log_msgptr = US"missing destination host in \"udpsend\" modifier";
+  return ERROR;
+  }
+if (portstr == NULL)
+  {
+  *log_msgptr = US"missing destination port in \"udpsend\" modifier";
+  return ERROR;
+  }
+if (arg == NULL)
+  {
+  *log_msgptr = US"missing datagram payload in \"udpsend\" modifier";
+  return ERROR;
+  }
+portnum = Ustrtol(portstr, &portend, 10);
+if (*portend != '\0')
+  {
+  *log_msgptr = US"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 = US"DNS lookup failed in \"udpsend\" modifier";
+  return DEFER;
+  }
+
+HDEBUG(D_acl)
+  debug_printf("udpsend [%s]:%d %s\n", h->address, portnum, arg);
+
+r = s = ip_connectedsocket(SOCK_DGRAM, h->address, portnum, portnum,
+               1, NULL, &errstr);
+if (r < 0) goto defer;
+len = Ustrlen(arg);
+r = send(s, arg, len, 0);
+if (r < 0)
+  {
+  errstr = US strerror(errno);
+  close(s);
+  goto defer;
+  }
+close(s);
+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", errstr);
+return DEFER;
+}
+
+
+
 /*************************************************
 *   Handle conditions/modifiers on an ACL item   *
 *************************************************/
@@ -2982,7 +3104,9 @@ for (; cb != NULL; cb = cb->next)
     /* The true/false parsing here should be kept in sync with that used in
     expand.c when dealing with ECOND_BOOL so that we don't have too many
     different definitions of what can be a boolean. */
-    if (Ustrspn(arg, "0123456789") == Ustrlen(arg))     /* Digits, or empty */
+    if (*arg == '-'
+       ? Ustrspn(arg+1, "0123456789") == Ustrlen(arg+1)    /* Negative number */
+       : Ustrspn(arg,   "0123456789") == Ustrlen(arg))     /* Digits, or empty */
       rc = (Uatoi(arg) == 0)? FAIL : OK;
     else
       rc = (strcmpic(arg, US"no") == 0 ||
@@ -3546,6 +3670,10 @@ for (; cb != NULL; cb = cb->next)
     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