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