X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/8ccd00b14ecc7c3c806882a54a9216f531571716..fc6b96fae1afd051f9d34fa3e86169be4e05e5b2:/src/src/smtp_in.c diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index aad778eef..54dcaa5e5 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -121,6 +121,9 @@ static BOOL auth_advertised; #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; @@ -216,6 +219,9 @@ enum { ENV_MAIL_OPT_SIZE, ENV_MAIL_OPT_BODY, ENV_MAIL_OPT_AUTH, #ifndef DISABLE_PRDR ENV_MAIL_OPT_PRDR, +#endif +#ifdef EXPERIMENTAL_DSN + ENV_MAIL_OPT_RET, ENV_MAIL_OPT_ENVID, #endif ENV_MAIL_OPT_NULL }; @@ -231,6 +237,10 @@ static env_mail_type_t env_mail_type_list[] = { { US"AUTH", ENV_MAIL_OPT_AUTH, TRUE }, #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 } }; @@ -623,10 +633,9 @@ union { } v1; struct { uschar sig[12]; - uschar ver; - uschar cmd; - uschar fam; - uschar len; + uint8_t ver_cmd; + uint8_t fam; + uint16_t len; union { struct { /* TCP/UDP over IPv4, len = 12 */ uint32_t src_addr; @@ -657,7 +666,7 @@ struct sockaddr_in6 tmpaddr6; int get_ok = 0; int size, ret, fd; -const char v2sig[13] = "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A\x02"; +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; @@ -693,16 +702,32 @@ if (ret == -1) } if (ret >= 16 && - memcmp(&hdr.v2, v2sig, 13) == 0) + 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\n"); + DEBUG(D_receive) debug_printf("Truncated or too large PROXYv2 header (%d/%d)\n", + ret, size); goto proxyfail; } - switch (hdr.v2.cmd) + switch (cmd) { case 0x01: /* PROXY command */ switch (hdr.v2.fam) @@ -772,8 +797,7 @@ if (ret >= 16 && break; default: DEBUG(D_receive) - debug_printf("Unsupported PROXYv2 command: 0x%02x\n", - hdr.v2.cmd); + debug_printf("Unsupported PROXYv2 command: 0x%x\n", cmd); goto proxyfail; } } @@ -1293,7 +1317,8 @@ for (i = 0; i < smtp_ch_index; i++) 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); } @@ -1474,6 +1499,13 @@ sender_address_unrewritten = NULL; /* Set only after verify rewrite */ 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; @@ -1823,6 +1855,9 @@ 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 */ @@ -3112,6 +3147,10 @@ while (done <= 0) int ptr, size, rc; int c, i; auth_instance *au; +#ifdef EXPERIMENTAL_DSN + uschar *orcpt = NULL; + int flags; +#endif switch(smtp_read_command(TRUE)) { @@ -3456,6 +3495,9 @@ while (done <= 0) #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) @@ -3539,6 +3581,16 @@ while (done <= 0) 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. */ @@ -3794,6 +3846,45 @@ while (done <= 0) 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 @@ -4070,6 +4161,91 @@ while (done <= 0) 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; + + if (!extract_option(&name, &value)) + break; + + if (dsn_advertised && strcmpic(name, US"ORCPT") == 0) + { + /* Check whether orcpt has been already set */ + if (orcpt) + { + 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 */ @@ -4184,6 +4360,21 @@ while (done <= 0) 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 */