xref: /netbsd-src/external/ibm-public/postfix/dist/src/postscreen/postscreen_haproxy.c (revision bdc22b2e01993381dcefeff2bc9b56ca75a4235c)
1 /*	$NetBSD: postscreen_haproxy.c,v 1.2 2017/02/14 01:16:47 christos Exp $	*/
2 
3 /*++
4 /* NAME
5 /*	postscreen_haproxy 3
6 /* SUMMARY
7 /*	haproxy protocol adapter
8 /* SYNOPSIS
9 /*	#include <postscreen_haproxy.h>
10 /*
11 /*	void	psc_endpt_haproxy_lookup(smtp_client_stream, lookup_done)
12 /*	VSTRING	*smtp_client_stream;
13 /*	void	(*lookup_done)(status, smtp_client_stream,
14 /*				smtp_client_addr, smtp_client_port,
15 /*				smtp_server_addr, smtp_server_port)
16 /*	int	status;
17 /*	MAI_HOSTADDR_STR *smtp_client_addr;
18 /*	MAI_SERVPORT_STR *smtp_client_port;
19 /*	MAI_HOSTADDR_STR *smtp_server_addr;
20 /*	MAI_SERVPORT_STR *smtp_server_port;
21 /* DESCRIPTION
22 /*	psc_endpt_haproxy_lookup() looks up connection endpoint
23 /*	information via the haproxy protocol.  Arguments and results
24 /*	conform to the postscreen_endpt(3) API.
25 /* LICENSE
26 /* .ad
27 /* .fi
28 /*	The Secure Mailer license must be distributed with this software.
29 /* AUTHOR(S)
30 /*	Wietse Venema
31 /*	IBM T.J. Watson Research
32 /*	P.O. Box 704
33 /*	Yorktown Heights, NY 10598, USA
34 /*--*/
35 
36 /* System library. */
37 
38 #include <sys_defs.h>
39 #include <stdio.h>
40 #include <stdarg.h>
41 #include <stdlib.h>
42 
43 /* Utility library. */
44 
45 #include <msg.h>
46 #include <mymalloc.h>
47 #include <events.h>
48 #include <myaddrinfo.h>
49 #include <vstream.h>
50 #include <vstring.h>
51 #include <stringops.h>
52 
53 /* Global library. */
54 
55 #include <haproxy_srvr.h>
56 #include <mail_params.h>
57 
58 /* Application-specific. */
59 
60 #include <postscreen.h>
61 #include <postscreen_haproxy.h>
62 
63  /*
64   * Per-session state.
65   */
66 typedef struct {
67     VSTREAM *stream;
68     PSC_ENDPT_LOOKUP_FN notify;
69     VSTRING *buffer;
70 } PSC_HAPROXY_STATE;
71 
72 /* psc_endpt_haproxy_event - read or time event */
73 
74 static void psc_endpt_haproxy_event(int event, void *context)
75 {
76     const char *myname = "psc_endpt_haproxy_event";
77     PSC_HAPROXY_STATE *state = (PSC_HAPROXY_STATE *) context;
78     int     status = 0;
79     MAI_HOSTADDR_STR smtp_client_addr;
80     MAI_SERVPORT_STR smtp_client_port;
81     MAI_HOSTADDR_STR smtp_server_addr;
82     MAI_SERVPORT_STR smtp_server_port;
83     int     last_char = 0;
84     const char *err;
85     VSTRING *escape_buf;
86     char    read_buf[HAPROXY_MAX_LEN];
87     ssize_t read_len;
88     char   *cp;
89 
90     /*
91      * We must not read(2) past the <CR><LF> that terminates the haproxy
92      * line. For efficiency reasons we read the entire haproxy line in one
93      * read(2) call when we know that the line is unfragmented. In the rare
94      * case that the line is fragmented, we fall back and read(2) it one
95      * character at a time.
96      */
97     switch (event) {
98     case EVENT_TIME:
99 	msg_warn("haproxy read: time limit exceeded");
100 	status = -1;
101 	break;
102     case EVENT_READ:
103 	/* Determine the initial VSTREAM read(2) buffer size. */
104 	if (VSTRING_LEN(state->buffer) == 0) {
105 	    if ((read_len = recv(vstream_fileno(state->stream),
106 			      read_buf, sizeof(read_buf) - 1, MSG_PEEK)) > 0
107 		&& ((cp = memchr(read_buf, '\n', read_len)) != 0)) {
108 		read_len = cp - read_buf + 1;
109 	    } else {
110 		read_len = 1;
111 	    }
112 	    vstream_control(state->stream, CA_VSTREAM_CTL_BUFSIZE(read_len),
113 			    CA_VSTREAM_CTL_END);
114 	}
115 	/* Drain the VSTREAM buffer, otherwise this pseudo-thread will hang. */
116 	do {
117 	    if ((last_char = VSTREAM_GETC(state->stream)) == VSTREAM_EOF) {
118 		if (vstream_ferror(state->stream))
119 		    msg_warn("haproxy read: %m");
120 		else
121 		    msg_warn("haproxy read: lost connection");
122 		status = -1;
123 		break;
124 	    }
125 	    if (VSTRING_LEN(state->buffer) >= HAPROXY_MAX_LEN) {
126 		msg_warn("haproxy read: line too long");
127 		status = -1;
128 		break;
129 	    }
130 	    VSTRING_ADDCH(state->buffer, last_char);
131 	} while (vstream_peek(state->stream) > 0);
132 	break;
133     }
134 
135     /*
136      * Parse the haproxy line. Note: the haproxy_srvr_parse() routine
137      * performs address protocol checks, address and port syntax checks, and
138      * converts IPv4-in-IPv6 address string syntax (:ffff::1.2.3.4) to IPv4
139      * syntax where permitted by the main.cf:inet_protocols setting.
140      */
141     if (status == 0 && last_char == '\n') {
142 	VSTRING_TERMINATE(state->buffer);
143 	if ((err = haproxy_srvr_parse(vstring_str(state->buffer),
144 				      &smtp_client_addr, &smtp_client_port,
145 			      &smtp_server_addr, &smtp_server_port)) != 0) {
146 	    escape_buf = vstring_alloc(HAPROXY_MAX_LEN + 2);
147 	    escape(escape_buf, vstring_str(state->buffer),
148 		   VSTRING_LEN(state->buffer));
149 	    msg_warn("haproxy read: %s: %s", err, vstring_str(escape_buf));
150 	    status = -1;
151 	    vstring_free(escape_buf);
152 	}
153     }
154 
155     /*
156      * Are we done yet?
157      */
158     if (status < 0 || last_char == '\n') {
159 	PSC_CLEAR_EVENT_REQUEST(vstream_fileno(state->stream),
160 				psc_endpt_haproxy_event, context);
161 	vstream_control(state->stream,
162 			CA_VSTREAM_CTL_BUFSIZE(VSTREAM_BUFSIZE),
163 			CA_VSTREAM_CTL_END);
164 	state->notify(status, state->stream,
165 		      &smtp_client_addr, &smtp_client_port,
166 		      &smtp_server_addr, &smtp_server_port);
167 	/* Note: the stream may be closed at this point. */
168 	vstring_free(state->buffer);
169 	myfree((void *) state);
170     }
171 }
172 
173 /* psc_endpt_haproxy_lookup - event-driven haproxy client */
174 
175 void    psc_endpt_haproxy_lookup(VSTREAM *stream,
176 				         PSC_ENDPT_LOOKUP_FN notify)
177 {
178     const char *myname = "psc_endpt_haproxy_lookup";
179     PSC_HAPROXY_STATE *state;
180 
181     /*
182      * Prepare the per-session state. XXX To improve overload behavior,
183      * maintain a pool of these so that we can reduce memory allocator
184      * activity.
185      */
186     state = (PSC_HAPROXY_STATE *) mymalloc(sizeof(*state));
187     state->stream = stream;
188     state->notify = notify;
189     state->buffer = vstring_alloc(100);
190 
191     /*
192      * Read the haproxy line.
193      */
194     PSC_READ_EVENT_REQUEST(vstream_fileno(stream), psc_endpt_haproxy_event,
195 			   (void *) state, var_psc_uproxy_tmout);
196 }
197