1 /* $NetBSD: smtpd_haproxy.c,v 1.2 2017/02/14 01:16:48 christos Exp $ */ 2 3 /*++ 4 /* NAME 5 /* smtpd_haproxy 3 6 /* SUMMARY 7 /* Postfix SMTP server haproxy adapter 8 /* SYNOPSIS 9 /* #include "smtpd.h" 10 /* 11 /* int smtpd_peer_from_haproxy(state) 12 /* SMTPD_STATE *state; 13 /* DESCRIPTION 14 /* smtpd_peer_from_haproxy() receives endpoint address and 15 /* port information via the haproxy protocol. 16 /* 17 /* The following summarizes what the Postfix SMTP server expects 18 /* from an up-stream proxy adapter. 19 /* .IP \(bu 20 /* Validate protocol, address and port syntax. Permit only 21 /* protocols that are configured with the main.cf:inet_protocols 22 /* setting. 23 /* .IP \(bu 24 /* Convert IPv4-in-IPv6 address syntax to IPv4 syntax when 25 /* both IPv6 and IPv4 support are enabled with main.cf:inet_protocols. 26 /* .IP \(bu 27 /* Update the following session context fields: addr, port, 28 /* rfc_addr, addr_family, dest_addr. The addr_family field 29 /* applies to the client address. 30 /* .IP \(bu 31 /* Dynamically allocate storage for string information with 32 /* mystrdup(). In case of error, leave unassigned string fields 33 /* at their initial zero value. 34 /* .IP \(bu 35 /* Log a clear warning message that explains why a request 36 /* fails. 37 /* .IP \(bu 38 /* Never talk to the remote SMTP client. 39 /* .PP 40 /* Arguments: 41 /* .IP state 42 /* Session context. 43 /* DIAGNOSTICS 44 /* Warnings: I/O errors, malformed haproxy line. 45 /* 46 /* The result value is 0 in case of success, -1 in case of 47 /* error. 48 /* LICENSE 49 /* .ad 50 /* .fi 51 /* The Secure Mailer license must be distributed with this software. 52 /* AUTHOR(S) 53 /* Wietse Venema 54 /* IBM T.J. Watson Research 55 /* P.O. Box 704 56 /* Yorktown Heights, NY 10598, USA 57 /*--*/ 58 59 /* System library. */ 60 61 #include <sys_defs.h> 62 #include <sys/socket.h> 63 64 /* Utility library. */ 65 66 #include <msg.h> 67 #include <myaddrinfo.h> 68 #include <mymalloc.h> 69 #include <stringops.h> 70 71 /* Global library. */ 72 73 #include <smtp_stream.h> 74 #include <mail_params.h> 75 #include <valid_mailhost_addr.h> 76 #include <haproxy_srvr.h> 77 78 /* Application-specific. */ 79 80 #include <smtpd.h> 81 82 /* SLMs. */ 83 84 #define STR(x) vstring_str(x) 85 #define LEN(x) VSTRING_LEN(x) 86 87 /* smtpd_peer_from_haproxy - initialize peer information from haproxy */ 88 89 int smtpd_peer_from_haproxy(SMTPD_STATE *state) 90 { 91 const char *myname = "smtpd_peer_from_haproxy"; 92 MAI_HOSTADDR_STR smtp_client_addr; 93 MAI_SERVPORT_STR smtp_client_port; 94 MAI_HOSTADDR_STR smtp_server_addr; 95 MAI_SERVPORT_STR smtp_server_port; 96 const char *proxy_err; 97 int io_err; 98 VSTRING *escape_buf; 99 100 /* 101 * While reading HAProxy handshake information, don't buffer input beyond 102 * the end-of-line. That would break the TLS wrappermode handshake. 103 */ 104 vstream_control(state->client, 105 VSTREAM_CTL_BUFSIZE, 1, 106 VSTREAM_CTL_END); 107 108 /* 109 * Note: the haproxy_srvr_parse() routine performs address protocol 110 * checks, address and port syntax checks, and converts IPv4-in-IPv6 111 * address string syntax (:ffff::1.2.3.4) to IPv4 syntax where permitted 112 * by the main.cf:inet_protocols setting, but logs no warnings. 113 */ 114 #define ENABLE_DEADLINE 1 115 116 smtp_stream_setup(state->client, var_smtpd_uproxy_tmout, ENABLE_DEADLINE); 117 switch (io_err = vstream_setjmp(state->client)) { 118 default: 119 msg_panic("%s: unhandled I/O error %d", myname, io_err); 120 case SMTP_ERR_EOF: 121 msg_warn("haproxy read: unexpected EOF"); 122 return (-1); 123 case SMTP_ERR_TIME: 124 msg_warn("haproxy read: timeout error"); 125 return (-1); 126 case 0: 127 if (smtp_get(state->buffer, state->client, HAPROXY_MAX_LEN, 128 SMTP_GET_FLAG_NONE) != '\n') { 129 msg_warn("haproxy read: line > %d characters", HAPROXY_MAX_LEN); 130 return (-1); 131 } 132 if ((proxy_err = haproxy_srvr_parse(STR(state->buffer), 133 &smtp_client_addr, &smtp_client_port, 134 &smtp_server_addr, &smtp_server_port)) != 0) { 135 escape_buf = vstring_alloc(HAPROXY_MAX_LEN + 2); 136 escape(escape_buf, STR(state->buffer), LEN(state->buffer)); 137 msg_warn("haproxy read: %s: %s", proxy_err, STR(escape_buf)); 138 vstring_free(escape_buf); 139 return (-1); 140 } 141 state->addr = mystrdup(smtp_client_addr.buf); 142 if (strrchr(state->addr, ':') != 0) { 143 state->rfc_addr = concatenate(IPV6_COL, state->addr, (char *) 0); 144 state->addr_family = AF_INET6; 145 } else { 146 state->rfc_addr = mystrdup(state->addr); 147 state->addr_family = AF_INET; 148 } 149 state->port = mystrdup(smtp_client_port.buf); 150 151 /* 152 * Avoid surprises in the Dovecot authentication server. 153 */ 154 state->dest_addr = mystrdup(smtp_server_addr.buf); 155 156 /* 157 * Enable normal buffering. 158 */ 159 vstream_control(state->client, 160 VSTREAM_CTL_BUFSIZE, VSTREAM_BUFSIZE, 161 VSTREAM_CTL_END); 162 return (0); 163 } 164 } 165