X-Git-Url: https://vcs.fsf.org/?a=blobdiff_plain;f=src%2Fsrc%2Fsmtp_in.c;h=4ea6cd404db668e3ebde2185dd21cb6ee226477f;hb=6c1c3d1dbe1a62ffd24ad9b3cd9efdfe275c74c5;hp=82a805a21a0f03703b6d838aa87c035b27f2a16c;hpb=4466248715466b6f251454283642b74de65e9d9a;p=exim.git diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index 82a805a21..4ea6cd404 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; @@ -214,8 +217,11 @@ static uschar *protocols[] = { /* 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 }; @@ -229,8 +235,12 @@ static env_mail_type_t env_mail_type_list[] = { { 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 } }; @@ -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; } } @@ -1474,6 +1498,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 +1854,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 */ @@ -2637,7 +2671,7 @@ uschar *what = #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)? @@ -3112,6 +3146,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 +3494,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 +3580,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. */ @@ -3628,12 +3679,13 @@ while (done <= 0) } #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. */ @@ -3793,6 +3845,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 @@ -3862,9 +3953,9 @@ while (done <= 0) } break; -#ifdef EXPERIMENTAL_PRDR +#ifndef DISABLE_PRDR case ENV_MAIL_OPT_PRDR: - if ( prdr_enable ) + if (prdr_enable) prdr_requested = TRUE; break; #endif @@ -3989,29 +4080,32 @@ while (done <= 0) 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); @@ -4066,6 +4160,86 @@ 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, *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 */ @@ -4180,6 +4354,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 */