xref: /netbsd-src/external/ibm-public/postfix/dist/src/smtpd/smtpd_haproxy.c (revision bdc22b2e01993381dcefeff2bc9b56ca75a4235c)
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