* 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. */
/* Functions for handling an incoming SMTP call. */
QUIT_CMD, HELP_CMD,
+#ifdef EXPERIMENTAL_PROXY
+ PROXY_FAIL_IGNORE_CMD,
+#endif
+
/* These are specials that don't correspond to actual commands */
EOF_CMD, OTHER_CMD, BADARG_CMD, BADCHAR_CMD, BADSYN_CMD,
#ifdef SUPPORT_TLS
static BOOL tls_advertised;
#endif
+#ifdef EXPERIMENTAL_DSN
+static BOOL dsn_advertised;
+#endif
static BOOL esmtp;
static BOOL helo_required = FALSE;
static BOOL helo_verify = FALSE;
/* Sanity check and validate optional args to MAIL FROM: envelope */
enum {
ENV_MAIL_OPT_SIZE, ENV_MAIL_OPT_BODY, ENV_MAIL_OPT_AUTH,
-#ifdef EXPERIMENTAL_PRDR
+#ifndef DISABLE_PRDR
ENV_MAIL_OPT_PRDR,
+#endif
+#ifdef EXPERIMENTAL_DSN
+ ENV_MAIL_OPT_RET, ENV_MAIL_OPT_ENVID,
#endif
ENV_MAIL_OPT_NULL
};
{ US"SIZE", ENV_MAIL_OPT_SIZE, TRUE },
{ US"BODY", ENV_MAIL_OPT_BODY, TRUE },
{ US"AUTH", ENV_MAIL_OPT_AUTH, TRUE },
-#ifdef EXPERIMENTAL_PRDR
+#ifndef DISABLE_PRDR
{ US"PRDR", ENV_MAIL_OPT_PRDR, FALSE },
+#endif
+#ifdef EXPERIMENTAL_DSN
+ { US"RET", ENV_MAIL_OPT_RET, TRUE },
+ { US"ENVID", ENV_MAIL_OPT_ENVID, TRUE },
#endif
{ US"NULL", ENV_MAIL_OPT_NULL, FALSE }
};
+#ifdef EXPERIMENTAL_PROXY
+/*************************************************
+* Restore socket timeout to previous value *
+*************************************************/
+/* If the previous value was successfully retrieved, restore
+it before returning control to the non-proxy routines
+
+Arguments: fd - File descriptor for input
+ get_ok - Successfully retrieved previous values
+ tvtmp - Time struct with previous values
+ vslen - Length of time struct
+Returns: none
+*/
+static void
+restore_socket_timeout(int fd, int get_ok, struct timeval tvtmp, socklen_t vslen)
+{
+if (get_ok == 0)
+ setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tvtmp, vslen);
+}
+
+/*************************************************
+* Check if host is required proxy host *
+*************************************************/
+/* The function determines if inbound host will be a regular smtp host
+or if it is configured that it must use Proxy Protocol.
+
+Arguments: none
+Returns: bool
+*/
+
+static BOOL
+check_proxy_protocol_host()
+{
+int rc;
+/* Cannot configure local connection as a proxy inbound */
+if (sender_host_address == NULL) return proxy_session;
+
+rc = verify_check_this_host(&proxy_required_hosts, NULL, NULL,
+ sender_host_address, NULL);
+if (rc == OK)
+ {
+ DEBUG(D_receive)
+ debug_printf("Detected proxy protocol configured host\n");
+ proxy_session = TRUE;
+ }
+return proxy_session;
+}
+
+
+/*************************************************
+* Setup host for proxy protocol *
+*************************************************/
+/* The function configures the connection based on a header from the
+inbound host to use Proxy Protocol. The specification is very exact
+so exit with an error if do not find the exact required pieces. This
+includes an incorrect number of spaces separating args.
+
+Arguments: none
+Returns: int
+*/
+
+static BOOL
+setup_proxy_protocol_host()
+{
+union {
+ struct {
+ uschar line[108];
+ } v1;
+ struct {
+ uschar sig[12];
+ uint8_t ver_cmd;
+ uint8_t fam;
+ uint16_t len;
+ union {
+ struct { /* TCP/UDP over IPv4, len = 12 */
+ uint32_t src_addr;
+ uint32_t dst_addr;
+ uint16_t src_port;
+ uint16_t dst_port;
+ } ip4;
+ struct { /* TCP/UDP over IPv6, len = 36 */
+ uint8_t src_addr[16];
+ uint8_t dst_addr[16];
+ uint16_t src_port;
+ uint16_t dst_port;
+ } ip6;
+ struct { /* AF_UNIX sockets, len = 216 */
+ uschar src_addr[108];
+ uschar dst_addr[108];
+ } unx;
+ } addr;
+ } v2;
+} hdr;
+
+/* Temp variables used in PPv2 address:port parsing */
+uint16_t tmpport;
+char tmpip[INET_ADDRSTRLEN];
+struct sockaddr_in tmpaddr;
+char tmpip6[INET6_ADDRSTRLEN];
+struct sockaddr_in6 tmpaddr6;
+
+int get_ok = 0;
+int size, ret, fd;
+const char v2sig[12] = "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A";
+uschar *iptype; /* To display debug info */
+struct timeval tv;
+socklen_t vslen = 0;
+struct timeval tvtmp;
+
+vslen = sizeof(struct timeval);
+
+fd = fileno(smtp_in);
+
+/* Save current socket timeout values */
+get_ok = getsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tvtmp,
+ &vslen);
+
+/* Proxy Protocol host must send header within a short time
+(default 3 seconds) or it's considered invalid */
+tv.tv_sec = PROXY_NEGOTIATION_TIMEOUT_SEC;
+tv.tv_usec = PROXY_NEGOTIATION_TIMEOUT_USEC;
+setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv,
+ sizeof(struct timeval));
+
+do
+ {
+ /* The inbound host was declared to be a Proxy Protocol host, so
+ don't do a PEEK into the data, actually slurp it up. */
+ ret = recv(fd, &hdr, sizeof(hdr), 0);
+ }
+ while (ret == -1 && errno == EINTR);
+
+if (ret == -1)
+ {
+ restore_socket_timeout(fd, get_ok, tvtmp, vslen);
+ return (errno == EAGAIN) ? 0 : ERRNO_PROXYFAIL;
+ }
+
+if (ret >= 16 &&
+ memcmp(&hdr.v2, v2sig, 12) == 0)
+ {
+ uint8_t ver, cmd;
+
+ /* May 2014: haproxy combined the version and command into one byte to
+ allow two full bytes for the length field in order to proxy SSL
+ connections. SSL Proxy is not supported in this version of Exim, but
+ must still seperate values here. */
+ ver = (hdr.v2.ver_cmd & 0xf0) >> 4;
+ cmd = (hdr.v2.ver_cmd & 0x0f);
+
+ if (ver != 0x02)
+ {
+ DEBUG(D_receive) debug_printf("Invalid Proxy Protocol version: %d\n", ver);
+ goto proxyfail;
+ }
+ DEBUG(D_receive) debug_printf("Detected PROXYv2 header\n");
+ /* The v2 header will always be 16 bytes per the spec. */
+ size = 16 + hdr.v2.len;
+ if (ret < size)
+ {
+ DEBUG(D_receive) debug_printf("Truncated or too large PROXYv2 header (%d/%d)\n",
+ ret, size);
+ goto proxyfail;
+ }
+ switch (cmd)
+ {
+ case 0x01: /* PROXY command */
+ switch (hdr.v2.fam)
+ {
+ case 0x11: /* TCPv4 address type */
+ iptype = US"IPv4";
+ tmpaddr.sin_addr.s_addr = hdr.v2.addr.ip4.src_addr;
+ inet_ntop(AF_INET, &(tmpaddr.sin_addr), (char *)&tmpip, sizeof(tmpip));
+ if (!string_is_ip_address(US tmpip,NULL))
+ {
+ DEBUG(D_receive) debug_printf("Invalid %s source IP\n", iptype);
+ return ERRNO_PROXYFAIL;
+ }
+ proxy_host_address = sender_host_address;
+ sender_host_address = string_copy(US tmpip);
+ tmpport = ntohs(hdr.v2.addr.ip4.src_port);
+ proxy_host_port = sender_host_port;
+ sender_host_port = tmpport;
+ /* Save dest ip/port */
+ tmpaddr.sin_addr.s_addr = hdr.v2.addr.ip4.dst_addr;
+ inet_ntop(AF_INET, &(tmpaddr.sin_addr), (char *)&tmpip, sizeof(tmpip));
+ if (!string_is_ip_address(US tmpip,NULL))
+ {
+ DEBUG(D_receive) debug_printf("Invalid %s dest port\n", iptype);
+ return ERRNO_PROXYFAIL;
+ }
+ proxy_target_address = string_copy(US tmpip);
+ tmpport = ntohs(hdr.v2.addr.ip4.dst_port);
+ proxy_target_port = tmpport;
+ goto done;
+ case 0x21: /* TCPv6 address type */
+ iptype = US"IPv6";
+ memmove(tmpaddr6.sin6_addr.s6_addr, hdr.v2.addr.ip6.src_addr, 16);
+ inet_ntop(AF_INET6, &(tmpaddr6.sin6_addr), (char *)&tmpip6, sizeof(tmpip6));
+ if (!string_is_ip_address(US tmpip6,NULL))
+ {
+ DEBUG(D_receive) debug_printf("Invalid %s source IP\n", iptype);
+ return ERRNO_PROXYFAIL;
+ }
+ proxy_host_address = sender_host_address;
+ sender_host_address = string_copy(US tmpip6);
+ tmpport = ntohs(hdr.v2.addr.ip6.src_port);
+ proxy_host_port = sender_host_port;
+ sender_host_port = tmpport;
+ /* Save dest ip/port */
+ memmove(tmpaddr6.sin6_addr.s6_addr, hdr.v2.addr.ip6.dst_addr, 16);
+ inet_ntop(AF_INET6, &(tmpaddr6.sin6_addr), (char *)&tmpip6, sizeof(tmpip6));
+ if (!string_is_ip_address(US tmpip6,NULL))
+ {
+ DEBUG(D_receive) debug_printf("Invalid %s dest port\n", iptype);
+ return ERRNO_PROXYFAIL;
+ }
+ proxy_target_address = string_copy(US tmpip6);
+ tmpport = ntohs(hdr.v2.addr.ip6.dst_port);
+ proxy_target_port = tmpport;
+ goto done;
+ default:
+ DEBUG(D_receive)
+ debug_printf("Unsupported PROXYv2 connection type: 0x%02x\n",
+ hdr.v2.fam);
+ goto proxyfail;
+ }
+ /* Unsupported protocol, keep local connection address */
+ break;
+ case 0x00: /* LOCAL command */
+ /* Keep local connection address for LOCAL */
+ break;
+ default:
+ DEBUG(D_receive)
+ debug_printf("Unsupported PROXYv2 command: 0x%x\n", cmd);
+ goto proxyfail;
+ }
+ }
+else if (ret >= 8 &&
+ memcmp(hdr.v1.line, "PROXY", 5) == 0)
+ {
+ uschar *p = string_copy(hdr.v1.line);
+ uschar *end = memchr(p, '\r', ret - 1);
+ uschar *sp; /* Utility variables follow */
+ int tmp_port;
+ char *endc;
+
+ if (!end || end[1] != '\n')
+ {
+ DEBUG(D_receive) debug_printf("Partial or invalid PROXY header\n");
+ goto proxyfail;
+ }
+ *end = '\0'; /* Terminate the string */
+ size = end + 2 - hdr.v1.line; /* Skip header + CRLF */
+ DEBUG(D_receive) debug_printf("Detected PROXYv1 header\n");
+ /* Step through the string looking for the required fields. Ensure
+ strict adherance to required formatting, exit for any error. */
+ p += 5;
+ if (!isspace(*(p++)))
+ {
+ DEBUG(D_receive) debug_printf("Missing space after PROXY command\n");
+ goto proxyfail;
+ }
+ if (!Ustrncmp(p, CCS"TCP4", 4))
+ iptype = US"IPv4";
+ else if (!Ustrncmp(p,CCS"TCP6", 4))
+ iptype = US"IPv6";
+ else if (!Ustrncmp(p,CCS"UNKNOWN", 7))
+ {
+ iptype = US"Unknown";
+ goto done;
+ }
+ else
+ {
+ DEBUG(D_receive) debug_printf("Invalid TCP type\n");
+ goto proxyfail;
+ }
+
+ p += Ustrlen(iptype);
+ if (!isspace(*(p++)))
+ {
+ DEBUG(D_receive) debug_printf("Missing space after TCP4/6 command\n");
+ goto proxyfail;
+ }
+ /* Find the end of the arg */
+ if ((sp = Ustrchr(p, ' ')) == NULL)
+ {
+ DEBUG(D_receive)
+ debug_printf("Did not find proxied src %s\n", iptype);
+ goto proxyfail;
+ }
+ *sp = '\0';
+ if(!string_is_ip_address(p,NULL))
+ {
+ DEBUG(D_receive)
+ debug_printf("Proxied src arg is not an %s address\n", iptype);
+ goto proxyfail;
+ }
+ proxy_host_address = sender_host_address;
+ sender_host_address = p;
+ p = sp + 1;
+ if ((sp = Ustrchr(p, ' ')) == NULL)
+ {
+ DEBUG(D_receive)
+ debug_printf("Did not find proxy dest %s\n", iptype);
+ goto proxyfail;
+ }
+ *sp = '\0';
+ if(!string_is_ip_address(p,NULL))
+ {
+ DEBUG(D_receive)
+ debug_printf("Proxy dest arg is not an %s address\n", iptype);
+ goto proxyfail;
+ }
+ proxy_target_address = p;
+ p = sp + 1;
+ if ((sp = Ustrchr(p, ' ')) == NULL)
+ {
+ DEBUG(D_receive) debug_printf("Did not find proxied src port\n");
+ goto proxyfail;
+ }
+ *sp = '\0';
+ tmp_port = strtol(CCS p,&endc,10);
+ if (*endc || tmp_port == 0)
+ {
+ DEBUG(D_receive)
+ debug_printf("Proxied src port '%s' not an integer\n", p);
+ goto proxyfail;
+ }
+ proxy_host_port = sender_host_port;
+ sender_host_port = tmp_port;
+ p = sp + 1;
+ if ((sp = Ustrchr(p, '\0')) == NULL)
+ {
+ DEBUG(D_receive) debug_printf("Did not find proxy dest port\n");
+ goto proxyfail;
+ }
+ tmp_port = strtol(CCS p,&endc,10);
+ if (*endc || tmp_port == 0)
+ {
+ DEBUG(D_receive)
+ debug_printf("Proxy dest port '%s' not an integer\n", p);
+ goto proxyfail;
+ }
+ proxy_target_port = tmp_port;
+ /* Already checked for /r /n above. Good V1 header received. */
+ goto done;
+ }
+else
+ {
+ /* Wrong protocol */
+ DEBUG(D_receive) debug_printf("Invalid proxy protocol version negotiation\n");
+ goto proxyfail;
+ }
+
+proxyfail:
+restore_socket_timeout(fd, get_ok, tvtmp, vslen);
+/* Don't flush any potential buffer contents. Any input should cause a
+ synchronization failure */
+return FALSE;
+
+done:
+restore_socket_timeout(fd, get_ok, tvtmp, vslen);
+DEBUG(D_receive)
+ debug_printf("Valid %s sender from Proxy Protocol header\n", iptype);
+return proxy_session;
+}
+#endif
+
/*************************************************
* Read one command line *
*************************************************/
for (p = cmd_list; p < cmd_list_end; p++)
{
+ #ifdef EXPERIMENTAL_PROXY
+ /* Only allow QUIT command if Proxy Protocol parsing failed */
+ if (proxy_session && proxy_session_failed)
+ {
+ if (p->cmd != QUIT_CMD)
+ continue;
+ }
+ #endif
if (strncmpic(smtp_cmd_buffer, US p->name, p->len) == 0 &&
(smtp_cmd_buffer[p->len-1] == ':' || /* "mail from:" or "rcpt to:" */
smtp_cmd_buffer[p->len] == 0 ||
}
}
+#ifdef EXPERIMENTAL_PROXY
+/* Only allow QUIT command if Proxy Protocol parsing failed */
+if (proxy_session && proxy_session_failed)
+ return PROXY_FAIL_IGNORE_CMD;
+#endif
+
/* Enforce synchronization for unknown commands */
if (smtp_inptr < smtp_inend && /* Outstanding input */
+#ifdef SUPPORT_TLS
+/* Append TLS-related information to a log line
+
+Arguments:
+ s String under construction: allocated string to extend, or NULL
+ sizep Pointer to current allocation size (update on return), or NULL
+ ptrp Pointer to index for new entries in string (update on return), or NULL
+
+Returns: Allocated string or NULL
+*/
+static uschar *
+s_tlslog(uschar * s, int * sizep, int * ptrp)
+{
+ int size = sizep ? *sizep : 0;
+ int ptr = ptrp ? *ptrp : 0;
+
+ if ((log_extra_selector & LX_tls_cipher) != 0 && tls_in.cipher != NULL)
+ s = string_append(s, &size, &ptr, 2, US" X=", tls_in.cipher);
+ if ((log_extra_selector & LX_tls_certificate_verified) != 0 &&
+ tls_in.cipher != NULL)
+ s = string_append(s, &size, &ptr, 2, US" CV=",
+ tls_in.certificate_verified? "yes":"no");
+ if ((log_extra_selector & LX_tls_peerdn) != 0 && tls_in.peerdn != NULL)
+ s = string_append(s, &size, &ptr, 3, US" DN=\"",
+ string_printing(tls_in.peerdn), US"\"");
+ if ((log_extra_selector & LX_tls_sni) != 0 && tls_in.sni != NULL)
+ s = string_append(s, &size, &ptr, 3, US" SNI=\"",
+ string_printing(tls_in.sni), US"\"");
+
+ if (s)
+ {
+ s[ptr] = '\0';
+ if (sizep) *sizep = size;
+ if (ptrp) *ptrp = ptr;
+ }
+ return s;
+}
+#endif
+
/*************************************************
* Log lack of MAIL if so configured *
*************************************************/
}
#ifdef SUPPORT_TLS
-if ((log_extra_selector & LX_tls_cipher) != 0 && tls_in.cipher != NULL)
- s = string_append(s, &size, &ptr, 2, US" X=", tls_in.cipher);
-if ((log_extra_selector & LX_tls_certificate_verified) != 0 &&
- tls_in.cipher != NULL)
- s = string_append(s, &size, &ptr, 2, US" CV=",
- tls_in.certificate_verified? "yes":"no");
-if ((log_extra_selector & LX_tls_peerdn) != 0 && tls_in.peerdn != NULL)
- s = string_append(s, &size, &ptr, 3, US" DN=\"",
- string_printing(tls_in.peerdn), US"\"");
-if ((log_extra_selector & LX_tls_sni) != 0 && tls_in.sni != NULL)
- s = string_append(s, &size, &ptr, 3, US" SNI=\"",
- string_printing(tls_in.sni), US"\"");
+s = s_tlslog(s, &size, &ptr);
#endif
sep = (smtp_connection_had[SMTP_HBUFF_SIZE-1] != SCH_NONE)?
if (s != NULL) s[ptr] = 0; else s = US"";
log_write(0, LOG_MAIN, "no MAIL in SMTP connection from %s D=%s%s",
host_and_ident(FALSE),
- readconf_printtime(time(NULL) - smtp_connection_start), s);
+ readconf_printtime( (int) ((long)time(NULL) - (long)smtp_connection_start)),
+ s);
}
sender_verified_list = NULL; /* No senders verified */
memset(sender_address_cache, 0, sizeof(sender_address_cache));
memset(sender_domain_cache, 0, sizeof(sender_domain_cache));
+
+#ifdef EXPERIMENTAL_DSN
+/* Reset the DSN flags */
+dsn_ret = 0;
+dsn_envid = NULL;
+#endif
+
authenticated_sender = NULL;
#ifdef EXPERIMENTAL_BRIGHTMAIL
bmi_run = 0;
#ifdef SUPPORT_TLS
tls_in.cipher = tls_in.peerdn = NULL;
+tls_in.ourcert = tls_in.peercert = NULL;
+tls_in.sni = NULL;
+tls_in.ocsp = OCSP_NOT_REQ;
tls_advertised = FALSE;
#endif
+#ifdef EXPERIMENTAL_DSN
+dsn_advertised = FALSE;
+#endif
/* Reset ACL connection variables */
if (smtp_batched_input) return TRUE;
+#ifdef EXPERIMENTAL_PROXY
+/* If valid Proxy Protocol source is connecting, set up session.
+ * Failure will not allow any SMTP function other than QUIT. */
+proxy_session = FALSE;
+proxy_session_failed = FALSE;
+if (check_proxy_protocol_host())
+ {
+ if (setup_proxy_protocol_host() == FALSE)
+ {
+ proxy_session_failed = TRUE;
+ DEBUG(D_receive)
+ debug_printf("Failure to extract proxied host, only QUIT allowed\n");
+ }
+ else
+ {
+ sender_host_name = NULL;
+ (void)host_name_lookup();
+ host_build_sender_fullhost();
+ }
+ }
+#endif
+
/* Run the ACL if it exists */
user_msg = NULL;
#endif
(where == ACL_WHERE_PREDATA)? US"DATA" :
(where == ACL_WHERE_DATA)? US"after DATA" :
-#ifdef EXPERIMENTAL_PRDR
+#ifndef DISABLE_PRDR
(where == ACL_WHERE_PRDR)? US"after DATA PRDR" :
#endif
(smtp_cmd_data == NULL)?
is closing if required and return 2. */
if (log_reject_target != 0)
- log_write(0, log_reject_target, "%s %s%srejected %s%s",
- host_and_ident(TRUE),
+ {
+#ifdef SUPPORT_TLS
+ uschar * s = s_tlslog(NULL, NULL, NULL);
+ if (!s) s = US"";
+#else
+ uschar * s = US"";
+#endif
+ log_write(0, log_reject_target, "%s%s %s%srejected %s%s",
+ host_and_ident(TRUE), s,
sender_info, (rc == FAIL)? US"" : US"temporarily ", what, log_msg);
+ }
if (!drop) return 0;
-
/*************************************************
* Initialize for SMTP incoming message *
*************************************************/
int ptr, size, rc;
int c, i;
auth_instance *au;
+#ifdef EXPERIMENTAL_DSN
+ uschar *orcpt = NULL;
+ int flags;
+#endif
switch(smtp_read_command(TRUE))
{
#ifdef SUPPORT_TLS
tls_advertised = FALSE;
#endif
+ #ifdef EXPERIMENTAL_DSN
+ dsn_advertised = FALSE;
+ #endif
smtp_code = US"250 "; /* Default response code plus space*/
if (user_msg == NULL)
s = string_cat(s, &size, &ptr, US"-8BITMIME\r\n", 11);
}
+ #ifdef EXPERIMENTAL_DSN
+ /* Advertise DSN support if configured to do so. */
+ if (verify_check_host(&dsn_advertise_hosts) != FAIL)
+ {
+ s = string_cat(s, &size, &ptr, smtp_code, 3);
+ s = string_cat(s, &size, &ptr, US"-DSN\r\n", 6);
+ dsn_advertised = TRUE;
+ }
+ #endif
+
/* Advertise ETRN if there's an ACL checking whether a host is
permitted to issue it; a check is made when any host actually tries. */
}
#endif
- #ifdef EXPERIMENTAL_PRDR
+ #ifndef DISABLE_PRDR
/* Per Recipient Data Response, draft by Eric A. Hall extending RFC */
- if (prdr_enable) {
+ if (prdr_enable)
+ {
s = string_cat(s, &size, &ptr, smtp_code, 3);
s = string_cat(s, &size, &ptr, US"-PRDR\r\n", 7);
- }
+ }
#endif
/* Finish off the multiline reply with one that is always available. */
arg_error = TRUE;
break;
+ #ifdef EXPERIMENTAL_DSN
+
+ /* Handle the two DSN options, but only if configured to do so (which
+ will have caused "DSN" to be given in the EHLO response). The code itself
+ is included only if configured in at build time. */
+
+ case ENV_MAIL_OPT_RET:
+ if (dsn_advertised) {
+ /* Check if RET has already been set */
+ if (dsn_ret > 0) {
+ synprot_error(L_smtp_syntax_error, 501, NULL,
+ US"RET can be specified once only");
+ goto COMMAND_LOOP;
+ }
+ dsn_ret = (strcmpic(value, US"HDRS") == 0)? dsn_ret_hdrs :
+ (strcmpic(value, US"FULL") == 0)? dsn_ret_full : 0;
+ DEBUG(D_receive) debug_printf("DSN_RET: %d\n", dsn_ret);
+ /* Check for invalid invalid value, and exit with error */
+ if (dsn_ret == 0) {
+ synprot_error(L_smtp_syntax_error, 501, NULL,
+ US"Value for RET is invalid");
+ goto COMMAND_LOOP;
+ }
+ }
+ break;
+ case ENV_MAIL_OPT_ENVID:
+ if (dsn_advertised) {
+ /* Check if the dsn envid has been already set */
+ if (dsn_envid != NULL) {
+ synprot_error(L_smtp_syntax_error, 501, NULL,
+ US"ENVID can be specified once only");
+ goto COMMAND_LOOP;
+ }
+ dsn_envid = string_copy(value);
+ DEBUG(D_receive) debug_printf("DSN_ENVID: %s\n", dsn_envid);
+ }
+ break;
+ #endif
+
/* Handle the AUTH extension. If the value given is not "<>" and either
the ACL says "yes" or there is no ACL but the sending host is
authenticated, we set it up as the authenticated sender. However, if the
}
break;
-#ifdef EXPERIMENTAL_PRDR
+#ifndef DISABLE_PRDR
case ENV_MAIL_OPT_PRDR:
- if ( prdr_enable )
+ if (prdr_enable)
prdr_requested = TRUE;
break;
#endif
when pipelining is not advertised, do another sync check in case the ACL
delayed and the client started sending in the meantime. */
- if (acl_smtp_mail == NULL) rc = OK; else
+ if (acl_smtp_mail)
{
rc = acl_check(ACL_WHERE_MAIL, NULL, acl_smtp_mail, &user_msg, &log_msg);
if (rc == OK && !pipelining_advertised && !check_sync())
goto SYNC_FAILURE;
}
+ else
+ rc = OK;
if (rc == OK || rc == DISCARD)
{
- if (user_msg == NULL)
+ if (!user_msg)
smtp_printf("%s%s%s", US"250 OK",
- #ifdef EXPERIMENTAL_PRDR
- prdr_requested == TRUE ? US", PRDR Requested" :
- #endif
+ #ifndef DISABLE_PRDR
+ prdr_requested ? US", PRDR Requested" : US"",
+ #else
US"",
+ #endif
US"\r\n");
else
{
- #ifdef EXPERIMENTAL_PRDR
- if ( prdr_requested == TRUE )
+ #ifndef DISABLE_PRDR
+ if (prdr_requested)
user_msg = string_sprintf("%s%s", user_msg, US", PRDR Requested");
#endif
- smtp_user_msg(US"250",user_msg);
+ smtp_user_msg(US"250", user_msg);
}
smtp_delay_rcpt = smtp_rlr_base;
recipients_discarded = (rc == DISCARD);
rcpt_fail_count++;
break;
}
+
+ #ifdef EXPERIMENTAL_DSN
+ /* Set the DSN flags orcpt and dsn_flags from the session*/
+ orcpt = NULL;
+ flags = 0;
+
+ if (esmtp) for(;;)
+ {
+ uschar *name, *value, *end;
+ int size;
+
+ if (!extract_option(&name, &value))
+ {
+ break;
+ }
+
+ if (dsn_advertised && strcmpic(name, US"ORCPT") == 0)
+ {
+ /* Check whether orcpt has been already set */
+ if (orcpt != NULL) {
+ synprot_error(L_smtp_syntax_error, 501, NULL,
+ US"ORCPT can be specified once only");
+ goto COMMAND_LOOP;
+ }
+ orcpt = string_copy(value);
+ DEBUG(D_receive) debug_printf("DSN orcpt: %s\n", orcpt);
+ }
+
+ else if (dsn_advertised && strcmpic(name, US"NOTIFY") == 0)
+ {
+ /* Check if the notify flags have been already set */
+ if (flags > 0) {
+ synprot_error(L_smtp_syntax_error, 501, NULL,
+ US"NOTIFY can be specified once only");
+ goto COMMAND_LOOP;
+ }
+ if (strcmpic(value, US"NEVER") == 0) flags |= rf_notify_never; else
+ {
+ uschar *p = value;
+ while (*p != 0)
+ {
+ uschar *pp = p;
+ while (*pp != 0 && *pp != ',') pp++;
+ if (*pp == ',') *pp++ = 0;
+ if (strcmpic(p, US"SUCCESS") == 0) {
+ DEBUG(D_receive) debug_printf("DSN: Setting notify success\n");
+ flags |= rf_notify_success;
+ }
+ else if (strcmpic(p, US"FAILURE") == 0) {
+ DEBUG(D_receive) debug_printf("DSN: Setting notify failure\n");
+ flags |= rf_notify_failure;
+ }
+ else if (strcmpic(p, US"DELAY") == 0) {
+ DEBUG(D_receive) debug_printf("DSN: Setting notify delay\n");
+ flags |= rf_notify_delay;
+ }
+ else {
+ /* Catch any strange values */
+ synprot_error(L_smtp_syntax_error, 501, NULL,
+ US"Invalid value for NOTIFY parameter");
+ goto COMMAND_LOOP;
+ }
+ p = pp;
+ }
+ DEBUG(D_receive) debug_printf("DSN Flags: %x\n", flags);
+ }
+ }
+
+ /* Unknown option. Stick back the terminator characters and break
+ the loop. An error for a malformed address will occur. */
+
+ else
+ {
+ DEBUG(D_receive) debug_printf("Invalid RCPT option: %s : %s\n", name, value);
+ name[-1] = ' ';
+ value[-1] = '=';
+ break;
+ }
+ }
+ #endif
/* Apply SMTP rewriting then extract the working address. Don't allow "<>"
as a recipient address */
if (user_msg == NULL) smtp_printf("250 Accepted\r\n");
else smtp_user_msg(US"250", user_msg);
receive_add_recipient(recipient, -1);
+
+ #ifdef EXPERIMENTAL_DSN
+ /* Set the dsn flags in the recipients_list */
+ if (orcpt != NULL)
+ recipients_list[recipients_count-1].orcpt = orcpt;
+ else
+ recipients_list[recipients_count-1].orcpt = NULL;
+
+ if (flags != 0)
+ recipients_list[recipients_count-1].dsn_flags = flags;
+ else
+ recipients_list[recipients_count-1].dsn_flags = 0;
+ DEBUG(D_receive) debug_printf("DSN: orcpt: %s flags: %d\n", recipients_list[recipients_count-1].orcpt, recipients_list[recipients_count-1].dsn_flags);
+ #endif
+
}
/* The recipient was discarded */
done = 1; /* Pretend eof - drops connection */
break;
+ #ifdef EXPERIMENTAL_PROXY
+ case PROXY_FAIL_IGNORE_CMD:
+ smtp_printf("503 Command refused, required Proxy negotiation failed\r\n");
+ break;
+ #endif
default:
if (unknown_command_count++ >= smtp_max_unknown_commands)
return done - 2; /* Convert yield values */
}
+/* vi: aw ai sw=2
+*/
/* End of smtp_in.c */