From: Todd Lyons Date: Sat, 30 Nov 2013 19:31:21 +0000 (-0800) Subject: Proxy Protocol - Server support X-Git-Tag: exim-4_83_RC1~95 X-Git-Url: https://git.exim.org/users/heiko/exim.git/commitdiff_plain/a3c8643131ef2a3f8100de7027be6bdf4e2ef3af?hp=5c7706b0c39a7cbb031f1d86594b41e0c96008fa Proxy Protocol - Server support Initial conf setting and expansions Logging setting whether to record proxy host, off by default Put PROXY processing before connect ACL Fix incoming address logging Add Proxy Protocol to ChangeLog Set window for Proxy Protocol header to be sent Update docs and EDITME. --- diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog index 989ec52c5..12369e434 100644 --- a/doc/doc-txt/ChangeLog +++ b/doc/doc-txt/ChangeLog @@ -23,6 +23,10 @@ JH/01 Fix memory-handling in use of acl as a conditional; avoid free of TL/01 LDAP support uses per connection or global context settings, depending upon the detected version of the libraries at build time. +TL/02 Experimental Proxy Protocol support: allows a proxied SMTP connection + to extract and use the src ip:port in logging and expansions as if it + were a direct connection from the outside internet. + Exim version 4.82 ----------------- diff --git a/doc/doc-txt/NewStuff b/doc/doc-txt/NewStuff index c90109724..11cfcffa0 100644 --- a/doc/doc-txt/NewStuff +++ b/doc/doc-txt/NewStuff @@ -9,6 +9,11 @@ the documentation is updated, this file is reduced to a short list. Version 4.83 ------------ + 1. If built with the EXPERIMENTAL_PROXY feature enabled, Exim can be + configured to expect an initial header from a proxy that will make the + actual external source IP:host be used in exim instead of the IP of the + proxy that is connecting to it. + Version 4.82 ------------ diff --git a/doc/doc-txt/experimental-spec.txt b/doc/doc-txt/experimental-spec.txt index b33612f43..92790ae33 100644 --- a/doc/doc-txt/experimental-spec.txt +++ b/doc/doc-txt/experimental-spec.txt @@ -1015,6 +1015,118 @@ Where SPAMMER_SET is a macro and it is defined as set acl_c_spam_host = ${lookup redis{GET...}} +Proxy Protocol Support +-------------------------------------------------------------- + +Exim now has Experimental "Proxy Protocol" support. It was built on +specifications from: +http://haproxy.1wt.eu/download/1.5/doc/proxy-protocol.txt + +The purpose of this function is so that an application load balancer, +such as HAProxy, can sit in front of several Exim servers and Exim +will log the IP that is connecting to the proxy server instead of +the IP of the proxy server when it connects to Exim. It resets the +$sender_address_host and $sender_address_port to the IP:port of the +connection to the proxy. It also re-queries the DNS information for +this new IP address so that the original sender's hostname and IP +get logged in the Exim logfile. There is no logging if a host passes or +fails Proxy Protocol negotiation, but it can easily be determined and +recorded in an ACL (example is below). + +1. To compile Exim with Proxy Protocol support, put this in +Local/Makefile: + +EXPERIMENTAL_PROXY=yes + +2. Global configuration settings: + +proxy_required_hosts = HOSTLIST + +The proxy_required_hosts option will require any IP in that hostlist +to use Proxy Protocol. The specification of Proxy Protocol is very +strict, and if proxy negotiation fails, Exim will not allow any SMTP +command other than QUIT. (See end of this section for an example.) +The option is expanded when used, so it can be a hostlist as well as +string of IP addresses. Since it is expanded, specifying an alternate +separator is supported for ease of use with IPv6 addresses. + +To log the IP of the proxy in the incoming logline, add: + log_selector = +proxy + +A default incoming logline (wrapped for appearance) will look like this: + + 2013-11-04 09:25:06 1VdNti-0001OY-1V <= me@example.net + H=mail.example.net [1.2.3.4] P=esmtp S=433 + +With the log selector enabled, an email that was proxied through a +Proxy Protocol server at 192.168.1.2 will look like this: + + 2013-11-04 09:25:06 1VdNti-0001OY-1V <= me@example.net + H=mail.example.net [1.2.3.4] P=esmtp PRX=192.168.1.2 S=433 + +3. In the ACL's the following expansion variables are available. + +proxy_host The src IP of the proxy server making the connection +proxy_port The src port the proxy server is using +proxy_session Boolean, yes/no, the connected host is required to use + Proxy Protocol. + +There is no expansion for a failed proxy session, however you can detect +it by checking if $proxy_session is true but $proxy_host is empty. As +an example, in my connect ACL, I have: + + warn condition = ${if and{ {bool{$proxy_session}} \ + {eq{$proxy_host}{}} } } + log_message = Failed required proxy protocol negotiation \ + from $sender_host_name [$sender_host_address] + + warn condition = ${if and{ {bool{$proxy_session}} \ + {!eq{$proxy_host}{}} } } + # But don't log health probes from the proxy itself + condition = ${if eq{$proxy_host}{$sender_host_address} \ + {false}{true}} + log_message = Successfully proxied from $sender_host_name \ + [$sender_host_address] through proxy protocol \ + host $proxy_host + +4. Runtime issues to be aware of: + - Since the real connections are all coming from your proxy, and the + per host connection tracking is done before Proxy Protocol is + evaluated, smtp_accept_max_per_host must be set high enough to + handle all of the parallel volume you expect per inbound proxy. + - The proxy has 3 seconds (hard-coded in the source code) to send the + required Proxy Protocol header after it connects. If it does not, + the response to any commands will be: + "503 Command refused, required Proxy negotiation failed" + - If the incoming connection is configured in Exim to be a Proxy + Protocol host, but the proxy is not sending the header, the banner + does not get sent until the timeout occurs. If the sending host + sent any input (before the banner), this causes a standard Exim + synchronization error (i.e. trying to pipeline before PIPELINING + was advertised). + - This is not advised, but is mentioned for completeness if you have + a specific internal configuration that you want this: If the Exim + server only has an internal IP address and no other machines in your + organization will connect to it to try to send email, you may + simply set the hostlist to "*", however, this will prevent local + mail programs from working because that would require mail from + localhost to use Proxy Protocol. Again, not advised! + +5. Example of a refused connection because the Proxy Protocol header was +not sent from a host configured to use Proxy Protocol. In the example, +the 3 second timeout occurred (when a Proxy Protocol banner should have +been sent), the banner was displayed to the user, but all commands are +rejected except for QUIT: + +# nc mail.example.net 25 +220-mail.example.net, ESMTP Exim 4.82+proxy, Mon, 04 Nov 2013 10:45:59 +220 -0800 RFC's enforced +EHLO localhost +503 Command refused, required Proxy negotiation failed +QUIT +221 mail.example.net closing connection + + -------------------------------------------------------------- End of file diff --git a/src/src/EDITME b/src/src/EDITME index 3f818f355..7377af844 100644 --- a/src/src/EDITME +++ b/src/src/EDITME @@ -480,6 +480,9 @@ EXIM_MONITOR=eximon.bin # CFLAGS += -I/usr/local/include # LDFLAGS += -lhiredis +# Uncomment the following line to enable Experimental Proxy Protocol +# EXPERIMENTAL_PROXY=yes + ############################################################################### # THESE ARE THINGS YOU MIGHT WANT TO SPECIFY # diff --git a/src/src/config.h.defaults b/src/src/config.h.defaults index 8c1e799da..f68537a2a 100644 --- a/src/src/config.h.defaults +++ b/src/src/config.h.defaults @@ -169,6 +169,7 @@ it's a default value. */ #define EXPERIMENTAL_DMARC #define EXPERIMENTAL_OCSP #define EXPERIMENTAL_PRDR +#define EXPERIMENTAL_PROXY #define EXPERIMENTAL_REDIS #define EXPERIMENTAL_SPF #define EXPERIMENTAL_SRS diff --git a/src/src/expand.c b/src/src/expand.c index de9f7b5ad..bf1f82c6f 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -556,6 +556,11 @@ static var_entry var_table[] = { { "parent_local_part", vtype_stringptr, &deliver_localpart_parent }, { "pid", vtype_pid, NULL }, { "primary_hostname", vtype_stringptr, &primary_hostname }, +#ifdef EXPERIMENTAL_PROXY + { "proxy_host", vtype_stringptr, &proxy_host }, + { "proxy_port", vtype_int, &proxy_port }, + { "proxy_session", vtype_bool, &proxy_session }, +#endif { "prvscheck_address", vtype_stringptr, &prvscheck_address }, { "prvscheck_keynum", vtype_stringptr, &prvscheck_keynum }, { "prvscheck_result", vtype_stringptr, &prvscheck_result }, diff --git a/src/src/globals.c b/src/src/globals.c index 133a7bf74..ec6700df5 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -797,6 +797,9 @@ bit_table log_options[] = { { US"lost_incoming_connection", L_lost_incoming_connection }, { US"outgoing_port", LX_outgoing_port }, { US"pid", LX_pid }, +#ifdef EXPERIMENTAL_PROXY + { US"proxy", LX_proxy }, +#endif { US"queue_run", L_queue_run }, { US"queue_time", LX_queue_time }, { US"queue_time_overall", LX_queue_time_overall }, @@ -914,6 +917,15 @@ uschar process_info[PROCESS_INFO_SIZE]; int process_info_len = 0; uschar *process_log_path = NULL; BOOL prod_requires_admin = TRUE; + +#ifdef EXPERIMENTAL_PROXY +uschar *proxy_host = US""; +int proxy_port = 0; +uschar *proxy_required_hosts = US""; +BOOL proxy_session = FALSE; +BOOL proxy_session_failed = FALSE; +#endif + uschar *prvscheck_address = NULL; uschar *prvscheck_keynum = NULL; uschar *prvscheck_result = NULL; diff --git a/src/src/globals.h b/src/src/globals.h index 265f94e60..5661489a7 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -592,6 +592,15 @@ extern uschar process_info[]; /* For SIGUSR1 output */ extern int process_info_len; extern uschar *process_log_path; /* Alternate path */ extern BOOL prod_requires_admin; /* TRUE if prodding requires admin */ + +#ifdef EXPERIMENTAL_PROXY +extern uschar *proxy_host; /* IP of proxy server */ +extern int proxy_port; /* Port of proxy server */ +extern uschar *proxy_required_hosts; /* Hostlist which (require) use proxy protocol */ +extern BOOL proxy_session; /* TRUE if receiving mail from valid proxy */ +extern BOOL proxy_session_failed; /* TRUE if required proxy negotiation failed */ +#endif + extern uschar *prvscheck_address; /* Set during prvscheck expansion item */ extern uschar *prvscheck_keynum; /* Set during prvscheck expansion item */ extern uschar *prvscheck_result; /* Set during prvscheck expansion item */ diff --git a/src/src/macros.h b/src/src/macros.h index a73bb0ba6..9f59f7acd 100644 --- a/src/src/macros.h +++ b/src/src/macros.h @@ -177,6 +177,14 @@ record. */ #define WAIT_NAME_MAX 50 +/* Wait this long before determining that a Proxy Protocol configured +host isn't speaking the protocol, and so is disallowed. Can be moved to +runtime configuration if per site settings become needed. */ +#ifdef EXPERIMENTAL_PROXY +#define PROXY_NEGOTIATION_TIMEOUT_SEC 3 +#define PROXY_NEGOTIATION_TIMEOUT_USEC 0 +#endif + /* Fixed option values for all PCRE functions */ #define PCRE_COPT 0 /* compile */ @@ -414,6 +422,7 @@ set all the bits in a multi-word selector. */ #define LX_unknown_in_list 0x81000000 #define LX_8bitmime 0x82000000 #define LX_smtp_mailauth 0x84000000 +#define LX_proxy 0x88000000 #define L_default (L_connection_reject | \ L_delay_delivery | \ @@ -481,6 +490,7 @@ to conflict with system errno values. */ #define ERRNO_RCPT4XX (-44) /* RCPT gave 4xx error */ #define ERRNO_MAIL4XX (-45) /* MAIL gave 4xx error */ #define ERRNO_DATA4XX (-46) /* DATA gave 4xx error */ +#define ERRNO_PROXYFAIL (-47) /* Negotiation failed for proxy configured host */ /* These must be last, so all retry deferments can easily be identified */ diff --git a/src/src/readconf.c b/src/src/readconf.c index 77c798412..c5200de67 100644 --- a/src/src/readconf.c +++ b/src/src/readconf.c @@ -332,6 +332,9 @@ static optionlist optionlist_config[] = { { "print_topbitchars", opt_bool, &print_topbitchars }, { "process_log_path", opt_stringptr, &process_log_path }, { "prod_requires_admin", opt_bool, &prod_requires_admin }, +#ifdef EXPERIMENTAL_PROXY + { "proxy_required_hosts", opt_stringptr, &proxy_required_hosts }, +#endif { "qualify_domain", opt_stringptr, &qualify_domain_sender }, { "qualify_recipient", opt_stringptr, &qualify_domain_recipient }, { "queue_domains", opt_stringptr, &queue_domains }, diff --git a/src/src/receive.c b/src/src/receive.c index 072fee9f1..9205436d3 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -3755,6 +3755,13 @@ if (sender_host_authenticated != NULL) if (prdr_requested) s = string_append(s, &size, &sptr, 1, US" PRDR"); #endif +#ifdef EXPERIMENTAL_PROXY +if (proxy_session && + (log_extra_selector & LX_proxy) != 0) + { + s = string_append(s, &size, &sptr, 2, US" PRX=", proxy_host); + } +#endif sprintf(CS big_buffer, "%d", msg_size); s = string_append(s, &size, &sptr, 2, US" S=", big_buffer); diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index 4740aa5ff..144ad28a2 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -94,6 +94,10 @@ enum { 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, @@ -549,6 +553,294 @@ exim_exit(EXIT_FAILURE); +#ifdef EXPERIMENTAL_PROXY +/************************************************* +* 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; +} + + +/************************************************* +* Flush waiting input string * +*************************************************/ +static void +flush_input() +{ +int rc; + +rc = smtp_getc(); +while (rc != '\n') /* End of input string */ + { + rc = smtp_getc(); + } +} + + +/************************************************* +* 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]; + uschar ver; + uschar cmd; + uschar fam; + uschar 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; + +int size, ret, fd; +uschar *tmpip; +const char v2sig[13] = "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A\x02"; +uschar *iptype; /* To display debug info */ +struct timeval tv; + +fd = fileno(smtp_in); + +/* 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 + { + ret = recv(fd, &hdr, sizeof(hdr), MSG_PEEK); + } + while (ret == -1 && errno == EINTR); + +if (ret == -1) + return (errno == EAGAIN) ? 0 : ERRNO_PROXYFAIL; + +if (ret >= 16 && + memcmp(&hdr.v2, v2sig, 13) == 0) + { + DEBUG(D_receive) debug_printf("Detected PROXYv2 header\n"); + size = 16 + hdr.v2.len; + if (ret < size) + { + DEBUG(D_receive) debug_printf("Truncated or too large PROXYv2 header\n"); + goto proxyfail; + } + switch (hdr.v2.cmd) + { + case 0x01: /* PROXY command */ + switch (hdr.v2.fam) + { + case 0x11: /* TCPv4 */ + tmpip = string_sprintf("%s", hdr.v2.addr.ip4.src_addr); + if (!string_is_ip_address(tmpip,NULL)) + return ERRNO_PROXYFAIL; + sender_host_address = tmpip; + sender_host_port = hdr.v2.addr.ip4.src_port; + goto done; + case 0x21: /* TCPv6 */ + tmpip = string_sprintf("%s", hdr.v2.addr.ip6.src_addr); + if (!string_is_ip_address(tmpip,NULL)) + return ERRNO_PROXYFAIL; + sender_host_address = tmpip; + sender_host_port = hdr.v2.addr.ip6.src_port; + goto done; + } + /* 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\n"); + 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 = 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; + } + /* Should save dest ip somewhere? */ + 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_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; + } + /* Should save dest port somewhere? */ + /* Already checked for /r /n above. Good V1 header received. */ + goto done; + } +else + { + /* Wrong protocol */ + DEBUG(D_receive) debug_printf("Wrong proxy protocol specified\n"); + goto proxyfail; + } + +proxyfail: +/* Don't flush any potential buffer contents. Any input should cause a +synchronization failure or we just don't want to speak SMTP to them */ +return FALSE; + +done: +flush_input(); +DEBUG(D_receive) + debug_printf("Valid %s sender from Proxy Protocol header\n", + iptype); +return proxy_session; +} +#endif + + + /************************************************* * Read one command line * *************************************************/ @@ -622,6 +914,14 @@ if required. */ 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 || @@ -669,6 +969,12 @@ 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) + return PROXY_FAIL_IGNORE_CMD; +#endif + /* Enforce synchronization for unknown commands */ if (smtp_inptr < smtp_inend && /* Outstanding input */ @@ -1832,6 +2138,28 @@ if (!sender_host_unknown) 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; @@ -2594,7 +2922,6 @@ smtp_respond(code, len, TRUE, user_msg); - /************************************************* * Initialize for SMTP incoming message * *************************************************/ @@ -4379,6 +4706,11 @@ while (done <= 0) 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)