summaryrefslogtreecommitdiffstats
path: root/src/smtpd/smtpd_haproxy.c
blob: 3bcbffc6aebe5740b04c3ed127866ee04cc8da75 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
/*++
/* NAME
/*	smtpd_haproxy 3
/* SUMMARY
/*	Postfix SMTP server haproxy adapter
/* SYNOPSIS
/*	#include "smtpd.h"
/*
/*	int	smtpd_peer_from_haproxy(state)
/*	SMTPD_STATE *state;
/* DESCRIPTION
/*	smtpd_peer_from_haproxy() receives endpoint address and
/*	port information via the haproxy protocol.
/*
/*	The following summarizes what the Postfix SMTP server expects
/*	from an up-stream proxy adapter.
/* .IP \(bu
/*	Validate protocol, address and port syntax. Permit only
/*	protocols that are configured with the main.cf:inet_protocols
/*	setting.
/* .IP \(bu
/*	Convert IPv4-in-IPv6 address syntax to IPv4 syntax when
/*	both IPv6 and IPv4 support are enabled with main.cf:inet_protocols.
/* .IP \(bu
/*	Update the following session context fields: addr, port,
/*	rfc_addr, addr_family, dest_addr, dest_port. The addr_family
/*	field applies to the client address.
/* .IP \(bu
/*	Dynamically allocate storage for string information with
/*	mystrdup(). In case of error, leave unassigned string fields
/*	at their initial zero value.
/* .IP \(bu
/*	Log a clear warning message that explains why a request
/*	fails.
/* .IP \(bu
/*	Never talk to the remote SMTP client.
/* .PP
/*	Arguments:
/* .IP state
/*	Session context.
/* DIAGNOSTICS
/*	Warnings: I/O errors, malformed haproxy line.
/*
/*	The result value is 0 in case of success, -1 in case of
/*	error.
/* LICENSE
/* .ad
/* .fi
/*	The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/*	Wietse Venema
/*	IBM T.J. Watson Research
/*	P.O. Box 704
/*	Yorktown Heights, NY 10598, USA
/*
/*	Wietse Venema
/*	Google, Inc.
/*	111 8th Avenue
/*	New York, NY 10011, USA
/*--*/

/* System library. */

#include <sys_defs.h>
#include <sys/socket.h>

/* Utility library. */

#include <msg.h>
#include <myaddrinfo.h>
#include <mymalloc.h>
#include <stringops.h>

/* Global library. */

#include <smtp_stream.h>
#include <mail_params.h>
#include <valid_mailhost_addr.h>
#include <haproxy_srvr.h>

/* Application-specific. */

#include <smtpd.h>

/* SLMs. */

#define STR(x)	vstring_str(x)
#define LEN(x)	VSTRING_LEN(x)

/* smtpd_peer_from_haproxy - initialize peer information from haproxy */

int     smtpd_peer_from_haproxy(SMTPD_STATE *state)
{
    const char *myname = "smtpd_peer_from_haproxy";
    MAI_HOSTADDR_STR smtp_client_addr;
    MAI_SERVPORT_STR smtp_client_port;
    MAI_HOSTADDR_STR smtp_server_addr;
    MAI_SERVPORT_STR smtp_server_port;
    const char *proxy_err;
    int     io_err;
    VSTRING *escape_buf;

    /*
     * While reading HAProxy handshake information, don't buffer input beyond
     * the end-of-line. That would break the TLS wrappermode handshake.
     */
    vstream_control(state->client,
		    VSTREAM_CTL_BUFSIZE, 1,
		    VSTREAM_CTL_END);

    /*
     * Note: the haproxy_srvr_parse() routine performs address protocol
     * checks, address and port syntax checks, and converts IPv4-in-IPv6
     * address string syntax (::ffff:1.2.3.4) to IPv4 syntax where permitted
     * by the main.cf:inet_protocols setting, but logs no warnings.
     */
#define ENABLE_DEADLINE	1

    smtp_stream_setup(state->client, var_smtpd_uproxy_tmout, ENABLE_DEADLINE);
    switch (io_err = vstream_setjmp(state->client)) {
    default:
	msg_panic("%s: unhandled I/O error %d", myname, io_err);
    case SMTP_ERR_EOF:
	msg_warn("haproxy read: unexpected EOF");
	return (-1);
    case SMTP_ERR_TIME:
	msg_warn("haproxy read: timeout error");
	return (-1);
    case 0:
	if (smtp_get(state->buffer, state->client, HAPROXY_MAX_LEN,
		     SMTP_GET_FLAG_NONE) != '\n') {
	    msg_warn("haproxy read: line > %d characters", HAPROXY_MAX_LEN);
	    return (-1);
	}
	if ((proxy_err = haproxy_srvr_parse(STR(state->buffer),
				       &smtp_client_addr, &smtp_client_port,
			      &smtp_server_addr, &smtp_server_port)) != 0) {
	    escape_buf = vstring_alloc(HAPROXY_MAX_LEN + 2);
	    escape(escape_buf, STR(state->buffer), LEN(state->buffer));
	    msg_warn("haproxy read: %s: %s", proxy_err, STR(escape_buf));
	    vstring_free(escape_buf);
	    return (-1);
	}
	state->addr = mystrdup(smtp_client_addr.buf);
	if (strrchr(state->addr, ':') != 0) {
	    state->rfc_addr = concatenate(IPV6_COL, state->addr, (char *) 0);
	    state->addr_family = AF_INET6;
	} else {
	    state->rfc_addr = mystrdup(state->addr);
	    state->addr_family = AF_INET;
	}
	state->port = mystrdup(smtp_client_port.buf);

	/*
	 * The Dovecot authentication server needs the server IP address.
	 */
	state->dest_addr = mystrdup(smtp_server_addr.buf);
	state->dest_port = mystrdup(smtp_server_port.buf);

	/*
	 * Enable normal buffering.
	 */
	vstream_control(state->client,
			VSTREAM_CTL_BUFSIZE, VSTREAM_BUFSIZE,
			VSTREAM_CTL_END);
	return (0);
    }
}