xref: /netbsd-src/external/ibm-public/postfix/dist/src/global/haproxy_srvr.c (revision 122b5006ee1bd67145794b4cde92f4fe4781a5ec)
1 /*	$NetBSD: haproxy_srvr.c,v 1.2 2020/03/18 19:05:16 christos Exp $	*/
2 
3 /*++
4 /* NAME
5 /*	haproxy_srvr 3
6 /* SUMMARY
7 /*	server-side haproxy protocol support
8 /* SYNOPSIS
9 /*	#include <haproxy_srvr.h>
10 /*
11 /*	const char *haproxy_srvr_parse(str, str_len, non_proxy,
12 /*			smtp_client_addr, smtp_client_port,
13 /*			smtp_server_addr, smtp_server_port)
14 /*	const char *str;
15 /*	ssize_t	*str_len;
16 /*	int	*non_proxy;
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 /*
22 /*	const char *haproxy_srvr_receive(fd, non_proxy,
23 /*			smtp_client_addr, smtp_client_port,
24 /*			smtp_server_addr, smtp_server_port)
25 /*	int	fd;
26 /*	int	*non_proxy;
27 /*	MAI_HOSTADDR_STR *smtp_client_addr,
28 /*	MAI_SERVPORT_STR *smtp_client_port,
29 /*	MAI_HOSTADDR_STR *smtp_server_addr,
30 /*	MAI_SERVPORT_STR *smtp_server_port;
31 /* DESCRIPTION
32 /*	haproxy_srvr_parse() parses a haproxy v1 or v2 protocol
33 /*	message. The result is null in case of success, a pointer
34 /*	to text (with the error type) in case of error. If both
35 /*	IPv6 and IPv4 support are enabled, IPV4_IN_IPV6 address
36 /*	form (::ffff:1.2.3.4) is converted to IPV4 form. In case
37 /*	of success, the str_len argument is updated with the number
38 /*	of bytes parsed, and the non_proxy argument is true or false
39 /*	if the haproxy message specifies a non-proxied connection.
40 /*
41 /*	haproxy_srvr_receive() receives and parses a haproxy protocol
42 /*	handshake. This must be called before any I/O is done on
43 /*	the specified file descriptor. The result is 0 in case of
44 /*	success, -1 in case of error. All errors are logged.
45 /*
46 /*	The haproxy v2 protocol support is limited to TCP over IPv4,
47 /*	TCP over IPv6, and non-proxied connections. In the latter
48 /*	case, the caller is responsible for any local or remote
49 /*	address/port lookup.
50 /* LICENSE
51 /* .ad
52 /* .fi
53 /*	The Secure Mailer license must be distributed with this software.
54 /* AUTHOR(S)
55 /*	Wietse Venema
56 /*	IBM T.J. Watson Research
57 /*	P.O. Box 704
58 /*	Yorktown Heights, NY 10598, USA
59 /*
60 /*	Wietse Venema
61 /*	Google, Inc.
62 /*	111 8th Avenue
63 /*	New York, NY 10011, USA
64 /*--*/
65 
66 /* System library. */
67 
68 #include <sys_defs.h>
69 #include <stdarg.h>
70 #include <stdlib.h>
71 #include <string.h>
72 
73 #ifdef STRCASECMP_IN_STRINGS_H
74 #include <strings.h>
75 #endif
76 
77 /* Utility library. */
78 
79 #include <msg.h>
80 #include <myaddrinfo.h>
81 #include <valid_hostname.h>
82 #include <stringops.h>
83 #include <mymalloc.h>
84 #include <inet_proto.h>
85 #include <split_at.h>
86 #include <sock_addr.h>
87 
88 /* Global library. */
89 
90 #include <haproxy_srvr.h>
91 
92 /* Application-specific. */
93 
94  /*
95   * The haproxy protocol assumes that a haproxy header will normally not
96   * exceed the default IPv4 TCP MSS, i.e. 576-40=536 bytes (the IPv6 default
97   * is larger: 1280-60=1220). With a proxy header that contains IPv6
98   * addresses, that leaves room for 536-52=484 bytes of TLVs. The Postfix
99   * implementation does not support headers with UNIX-domain addresses.
100   */
101 #define HAPROXY_HEADER_MAX_LEN		536
102 
103  /*
104   * Begin protocol v2 definitions from haproxy/include/types/connection.h.
105   */
106 #define PP2_SIGNATURE		"\r\n\r\n\0\r\nQUIT\n"
107 #define PP2_SIGNATURE_LEN	12
108 #define PP2_HEADER_LEN		16
109 
110 /* ver_cmd byte */
111 #define PP2_CMD_LOCAL		0x00
112 #define PP2_CMD_PROXY		0x01
113 #define PP2_CMD_MASK		0x0F
114 
115 #define PP2_VERSION		0x20
116 #define PP2_VERSION_MASK	0xF0
117 
118 /* fam byte */
119 #define PP2_TRANS_UNSPEC	0x00
120 #define PP2_TRANS_STREAM	0x01
121 #define PP2_TRANS_DGRAM		0x02
122 #define PP2_TRANS_MASK		0x0F
123 
124 #define PP2_FAM_UNSPEC		0x00
125 #define PP2_FAM_INET		0x10
126 #define PP2_FAM_INET6		0x20
127 #define PP2_FAM_UNIX		0x30
128 #define PP2_FAM_MASK		0xF0
129 
130 /* len field (2 bytes) */
131 #define PP2_ADDR_LEN_UNSPEC	(0)
132 #define PP2_ADDR_LEN_INET	(4 + 4 + 2 + 2)
133 #define PP2_ADDR_LEN_INET6	(16 + 16 + 2 + 2)
134 #define PP2_ADDR_LEN_UNIX	(108 + 108)
135 
136 #define PP2_HDR_LEN_UNSPEC	(PP2_HEADER_LEN + PP2_ADDR_LEN_UNSPEC)
137 #define PP2_HDR_LEN_INET	(PP2_HEADER_LEN + PP2_ADDR_LEN_INET)
138 #define PP2_HDR_LEN_INET6	(PP2_HEADER_LEN + PP2_ADDR_LEN_INET6)
139 #define PP2_HDR_LEN_UNIX	(PP2_HEADER_LEN + PP2_ADDR_LEN_UNIX)
140 
141 struct proxy_hdr_v2 {
142     uint8_t sig[PP2_SIGNATURE_LEN];	/* PP2_SIGNATURE */
143     uint8_t ver_cmd;			/* protocol version | command */
144     uint8_t fam;			/* protocol family and transport */
145     uint16_t len;			/* length of remainder */
146     union {
147 	struct {			/* for TCP/UDP over IPv4, len = 12 */
148 	    uint32_t src_addr;
149 	    uint32_t dst_addr;
150 	    uint16_t src_port;
151 	    uint16_t dst_port;
152 	}       ip4;
153 	struct {			/* for TCP/UDP over IPv6, len = 36 */
154 	    uint8_t src_addr[16];
155 	    uint8_t dst_addr[16];
156 	    uint16_t src_port;
157 	    uint16_t dst_port;
158 	}       ip6;
159 	struct {			/* for AF_UNIX sockets, len = 216 */
160 	    uint8_t src_addr[108];
161 	    uint8_t dst_addr[108];
162 	}       unx;
163     }       addr;
164 };
165 
166  /*
167   * End protocol v2 definitions from haproxy/include/types/connection.h.
168   */
169 
170 static INET_PROTO_INFO *proto_info;
171 
172 #define STR_OR_NULL(str) ((str) ? (str) : "(null)")
173 
174 /* haproxy_srvr_parse_lit - extract and validate string literal */
175 
176 static int haproxy_srvr_parse_lit(const char *str,...)
177 {
178     va_list ap;
179     const char *cp;
180     int     result = -1;
181     int     count;
182 
183     if (msg_verbose)
184 	msg_info("haproxy_srvr_parse: %s", STR_OR_NULL(str));
185 
186     if (str != 0) {
187 	va_start(ap, str);
188 	for (count = 0; (cp = va_arg(ap, const char *)) != 0; count++) {
189 	    if (strcmp(str, cp) == 0) {
190 		result = count;
191 		break;
192 	    }
193 	}
194 	va_end(ap);
195     }
196     return (result);
197 }
198 
199 /* haproxy_srvr_parse_proto - parse and validate the protocol type */
200 
201 static int haproxy_srvr_parse_proto(const char *str, int *addr_family)
202 {
203     if (msg_verbose)
204 	msg_info("haproxy_srvr_parse: proto=%s", STR_OR_NULL(str));
205 
206 #ifdef AF_INET6
207     if (strcasecmp(str, "TCP6") == 0) {
208 	if (strchr((char *) proto_info->sa_family_list, AF_INET6) != 0) {
209 	    *addr_family = AF_INET6;
210 	    return (0);
211 	}
212     } else
213 #endif
214     if (strcasecmp(str, "TCP4") == 0) {
215 	if (strchr((char *) proto_info->sa_family_list, AF_INET) != 0) {
216 	    *addr_family = AF_INET;
217 	    return (0);
218 	}
219     }
220     return (-1);
221 }
222 
223 /* haproxy_srvr_parse_addr - extract and validate IP address */
224 
225 static int haproxy_srvr_parse_addr(const char *str, MAI_HOSTADDR_STR *addr,
226 				           int addr_family)
227 {
228     struct addrinfo *res = 0;
229     int     err;
230 
231     if (msg_verbose)
232 	msg_info("haproxy_srvr_parse: addr=%s proto=%d",
233 		 STR_OR_NULL(str), addr_family);
234 
235     if (str == 0 || strlen(str) >= sizeof(MAI_HOSTADDR_STR))
236 	return (-1);
237 
238     switch (addr_family) {
239 #ifdef AF_INET6
240     case AF_INET6:
241 	err = !valid_ipv6_hostaddr(str, DONT_GRIPE);
242 	break;
243 #endif
244     case AF_INET:
245 	err = !valid_ipv4_hostaddr(str, DONT_GRIPE);
246 	break;
247     default:
248 	msg_panic("haproxy_srvr_parse: unexpected address family: %d",
249 		  addr_family);
250     }
251     if (err == 0)
252 	err = (hostaddr_to_sockaddr(str, (char *) 0, 0, &res)
253 	       || sockaddr_to_hostaddr(res->ai_addr, res->ai_addrlen,
254 				       addr, (MAI_SERVPORT_STR *) 0, 0));
255     if (res)
256 	freeaddrinfo(res);
257     if (err)
258 	return (-1);
259     if (addr->buf[0] == ':' && strncasecmp("::ffff:", addr->buf, 7) == 0
260 	&& strchr((char *) proto_info->sa_family_list, AF_INET) != 0)
261 	memmove(addr->buf, addr->buf + 7, strlen(addr->buf) + 1 - 7);
262     return (0);
263 }
264 
265 /* haproxy_srvr_parse_port - extract and validate TCP port */
266 
267 static int haproxy_srvr_parse_port(const char *str, MAI_SERVPORT_STR *port)
268 {
269     if (msg_verbose)
270 	msg_info("haproxy_srvr_parse: port=%s", STR_OR_NULL(str));
271     if (str == 0 || strlen(str) >= sizeof(MAI_SERVPORT_STR)
272 	|| !valid_hostport(str, DONT_GRIPE)) {
273 	return (-1);
274     } else {
275 	memcpy(port->buf, str, strlen(str) + 1);
276 	return (0);
277     }
278 }
279 
280 /* haproxy_srvr_parse_v2_addr_v4 - parse IPv4 info from v2 header */
281 
282 static int haproxy_srvr_parse_v2_addr_v4(uint32_t sin_addr,
283 					         unsigned sin_port,
284 					         MAI_HOSTADDR_STR *addr,
285 					         MAI_SERVPORT_STR *port)
286 {
287     struct sockaddr_in sin;
288 
289     memset((void *) &sin, 0, sizeof(sin));
290     sin.sin_family = AF_INET;
291     sin.sin_addr.s_addr = sin_addr;
292     sin.sin_port = sin_port;
293     if (sockaddr_to_hostaddr((struct sockaddr *) &sin, sizeof(sin),
294 			     addr, port, 0) < 0)
295 	return (-1);
296     return (0);
297 }
298 
299 #ifdef AF_INET6
300 
301 /* haproxy_srvr_parse_v2_addr_v6 - parse IPv6 info from v2 header */
302 
303 static int haproxy_srvr_parse_v2_addr_v6(uint8_t *sin6_addr,
304 					         unsigned sin6_port,
305 					         MAI_HOSTADDR_STR *addr,
306 					         MAI_SERVPORT_STR *port)
307 {
308     struct sockaddr_in6 sin6;
309 
310     memset((void *) &sin6, 0, sizeof(sin6));
311     sin6.sin6_family = AF_INET6;
312     memcpy(&sin6.sin6_addr, sin6_addr, 16);
313     sin6.sin6_port = sin6_port;
314     if (sockaddr_to_hostaddr((struct sockaddr *) &sin6,
315 			     sizeof(sin6), addr, port, 0) < 0)
316 	return (-1);
317     if (addr->buf[0] == ':'
318 	&& strncasecmp("::ffff:", addr->buf, 7) == 0
319 	&& strchr((char *) proto_info->sa_family_list, AF_INET) != 0)
320 	memmove(addr->buf, addr->buf + 7,
321 		strlen(addr->buf) + 1 - 7);
322     return (0);
323 }
324 
325 #endif
326 
327 /* haproxy_srvr_parse_v2_hdr - parse v2 header address info */
328 
329 static const char *haproxy_srvr_parse_v2_hdr(const char *str, ssize_t *str_len,
330 					             int *non_proxy,
331 				         MAI_HOSTADDR_STR *smtp_client_addr,
332 				         MAI_SERVPORT_STR *smtp_client_port,
333 				         MAI_HOSTADDR_STR *smtp_server_addr,
334 				         MAI_SERVPORT_STR *smtp_server_port)
335 {
336     const char myname[] = "haproxy_srvr_parse_v2_hdr";
337     struct proxy_hdr_v2 *hdr_v2;
338 
339     if (*str_len < PP2_HEADER_LEN)
340 	return ("short protocol header");
341     hdr_v2 = (struct proxy_hdr_v2 *) str;
342     if (memcmp(hdr_v2->sig, PP2_SIGNATURE, PP2_SIGNATURE_LEN) != 0)
343 	return ("unrecognized protocol header");
344     if ((hdr_v2->ver_cmd & PP2_VERSION_MASK) != PP2_VERSION)
345 	return ("unrecognized protocol version");
346     if (*str_len < PP2_HEADER_LEN + ntohs(hdr_v2->len))
347 	return ("short version 2 protocol header");
348 
349     switch (hdr_v2->ver_cmd & PP2_CMD_MASK) {
350 
351 	/*
352 	 * Proxied connection, use the proxy-provided connection info.
353 	 */
354     case PP2_CMD_PROXY:
355 	switch (hdr_v2->fam) {
356 	case PP2_FAM_INET | PP2_TRANS_STREAM:{	/* TCP4 */
357 		if (strchr((char *) proto_info->sa_family_list, AF_INET) == 0)
358 		    return ("Postfix IPv4 support is disabled");
359 		if (ntohs(hdr_v2->len) < PP2_ADDR_LEN_INET)
360 		    return ("short address field");
361 		if (haproxy_srvr_parse_v2_addr_v4(hdr_v2->addr.ip4.src_addr,
362 						  hdr_v2->addr.ip4.src_port,
363 				    smtp_client_addr, smtp_client_port) < 0)
364 		    return ("client network address conversion error");
365 		if (msg_verbose)
366 		    msg_info("%s: smtp_client_addr=%s smtp_client_port=%s",
367 		      myname, smtp_client_addr->buf, smtp_client_port->buf);
368 		if (haproxy_srvr_parse_v2_addr_v4(hdr_v2->addr.ip4.dst_addr,
369 						  hdr_v2->addr.ip4.dst_port,
370 				    smtp_server_addr, smtp_server_port) < 0)
371 		    return ("server network address conversion error");
372 		if (msg_verbose)
373 		    msg_info("%s: smtp_server_addr=%s smtp_server_port=%s",
374 		      myname, smtp_server_addr->buf, smtp_server_port->buf);
375 		break;
376 	    }
377 	case PP2_FAM_INET6 | PP2_TRANS_STREAM:{/* TCP6 */
378 #ifdef AF_INET6
379 		if (strchr((char *) proto_info->sa_family_list, AF_INET6) == 0)
380 		    return ("Postfix IPv6 support is disabled");
381 		if (ntohs(hdr_v2->len) < PP2_ADDR_LEN_INET6)
382 		    return ("short address field");
383 		if (haproxy_srvr_parse_v2_addr_v6(hdr_v2->addr.ip6.src_addr,
384 						  hdr_v2->addr.ip6.src_port,
385 						  smtp_client_addr,
386 						  smtp_client_port) < 0)
387 		    return ("client network address conversion error");
388 		if (msg_verbose)
389 		    msg_info("%s: smtp_client_addr=%s smtp_client_port=%s",
390 		      myname, smtp_client_addr->buf, smtp_client_port->buf);
391 		if (haproxy_srvr_parse_v2_addr_v6(hdr_v2->addr.ip6.dst_addr,
392 						  hdr_v2->addr.ip6.dst_port,
393 						  smtp_server_addr,
394 						  smtp_server_port) < 0)
395 		    return ("server network address conversion error");
396 		if (msg_verbose)
397 		    msg_info("%s: smtp_server_addr=%s smtp_server_port=%s",
398 		      myname, smtp_server_addr->buf, smtp_server_port->buf);
399 		break;
400 #else
401 		return ("Postfix IPv6 support is not compiled in");
402 #endif
403 	    }
404 	default:
405 	    return ("unsupported network protocol");
406 	}
407 	/* For now, skip and ignore TLVs. */
408 	*non_proxy = 0;
409 	*str_len = PP2_HEADER_LEN + ntohs(hdr_v2->len);
410 	return (0);
411 
412 	/*
413 	 * Non-proxied connection, use the proxy-to-server connection info.
414 	 */
415     case PP2_CMD_LOCAL:
416 	/* For now, skip and ignore TLVs. */
417 	*non_proxy = 1;
418 	*str_len = PP2_HEADER_LEN + ntohs(hdr_v2->len);
419 	return (0);
420     default:
421 	return ("bad command in proxy header");
422     }
423 }
424 
425 /* haproxy_srvr_parse - parse haproxy line */
426 
427 const char *haproxy_srvr_parse(const char *str, ssize_t *str_len,
428 			               int *non_proxy,
429 			               MAI_HOSTADDR_STR *smtp_client_addr,
430 			               MAI_SERVPORT_STR *smtp_client_port,
431 			               MAI_HOSTADDR_STR *smtp_server_addr,
432 			               MAI_SERVPORT_STR *smtp_server_port)
433 {
434     const char *err;
435 
436     if (proto_info == 0)
437 	proto_info = inet_proto_info();
438 
439     /*
440      * XXX We don't accept connections with the "UNKNOWN" protocol type,
441      * because those would sidestep address-based access control mechanisms.
442      */
443 
444     /*
445      * Try version 1 protocol.
446      */
447     if (strncmp(str, "PROXY ", 6) == 0) {
448 	char   *saved_str = mystrndup(str, *str_len);
449 	char   *cp = saved_str;
450 	char   *beyond_header = split_at(saved_str, '\n');
451 	int     addr_family;
452 
453 #define NEXT_TOKEN mystrtok(&cp, " \r")
454 	if (beyond_header == 0)
455 	    err = "missing protocol header terminator";
456 	else if (haproxy_srvr_parse_lit(NEXT_TOKEN, "PROXY", (char *) 0) < 0)
457 	    err = "unexpected protocol header";
458 	else if (haproxy_srvr_parse_proto(NEXT_TOKEN, &addr_family) < 0)
459 	    err = "unsupported protocol type";
460 	else if (haproxy_srvr_parse_addr(NEXT_TOKEN, smtp_client_addr,
461 					 addr_family) < 0)
462 	    err = "unexpected client address syntax";
463 	else if (haproxy_srvr_parse_addr(NEXT_TOKEN, smtp_server_addr,
464 					 addr_family) < 0)
465 	    err = "unexpected server address syntax";
466 	else if (haproxy_srvr_parse_port(NEXT_TOKEN, smtp_client_port) < 0)
467 	    err = "unexpected client port syntax";
468 	else if (haproxy_srvr_parse_port(NEXT_TOKEN, smtp_server_port) < 0)
469 	    err = "unexpected server port syntax";
470 	else {
471 	    err = 0;
472 	    *str_len = beyond_header - saved_str;
473 	}
474 	myfree(saved_str);
475 
476 	*non_proxy = 0;
477 	return (err);
478     }
479 
480     /*
481      * Try version 2 protocol.
482      */
483     else {
484 	return (haproxy_srvr_parse_v2_hdr(str, str_len, non_proxy,
485 					  smtp_client_addr, smtp_client_port,
486 				       smtp_server_addr, smtp_server_port));
487     }
488 }
489 
490 /* haproxy_srvr_receive - receive and parse haproxy protocol handshake */
491 
492 int     haproxy_srvr_receive(int fd, int *non_proxy,
493 			             MAI_HOSTADDR_STR *smtp_client_addr,
494 			             MAI_SERVPORT_STR *smtp_client_port,
495 			             MAI_HOSTADDR_STR *smtp_server_addr,
496 			             MAI_SERVPORT_STR *smtp_server_port)
497 {
498     const char *err;
499     VSTRING *escape_buf;
500     char    read_buf[HAPROXY_HEADER_MAX_LEN + 1];
501     ssize_t read_len;
502 
503     /*
504      * We must not read(2) past the end of the HaProxy handshake. The v2
505      * protocol assumes that the handshake will never be fragmented,
506      * therefore we peek, parse the entire input, then read(2) only the
507      * number of bytes parsed.
508      */
509     if ((read_len = recv(fd, read_buf, sizeof(read_buf) - 1, MSG_PEEK)) <= 0) {
510 	msg_warn("haproxy read: EOF");
511 	return (-1);
512     }
513 
514     /*
515      * Parse the haproxy handshake, and determine the handshake length.
516      */
517     read_buf[read_len] = 0;
518 
519     if ((err = haproxy_srvr_parse(read_buf, &read_len, non_proxy,
520 				  smtp_client_addr, smtp_client_port,
521 				smtp_server_addr, smtp_server_port)) != 0) {
522 	escape_buf = vstring_alloc(read_len * 2);
523 	escape(escape_buf, read_buf, read_len);
524 	msg_warn("haproxy read: %s: %s", err, vstring_str(escape_buf));
525 	vstring_free(escape_buf);
526 	return (-1);
527     }
528 
529     /*
530      * Try to pop the haproxy handshake off the input queue.
531      */
532     if (recv(fd, read_buf, read_len, 0) != read_len) {
533 	msg_warn("haproxy read: %m");
534 	return (-1);
535     }
536     return (0);
537 }
538 
539  /*
540   * Test program.
541   */
542 #ifdef TEST
543 
544  /*
545   * Test cases with inputs and expected outputs. A request may contain
546   * trailing garbage, and it may be too short. A v1 request may also contain
547   * malformed address or port information.
548   */
549 typedef struct TEST_CASE {
550     const char *haproxy_request;	/* v1 or v2 request including thrash */
551     ssize_t haproxy_req_len;		/* request length including thrash */
552     ssize_t exp_req_len;		/* parsed request length */
553     int     exp_non_proxy;		/* request is not proxied */
554     const char *exp_return;		/* expected error string */
555     const char *exp_client_addr;	/* expected client address string */
556     const char *exp_server_addr;	/* expected client port string */
557     const char *exp_client_port;	/* expected client address string */
558     const char *exp_server_port;	/* expected server port string */
559 } TEST_CASE;
560 static TEST_CASE v1_test_cases[] = {
561     /* IPv6. */
562     {"PROXY TCP6 fc:00:00:00:1:2:3:4 fc:00:00:00:4:3:2:1 123 321\n", 0, 0, 0, 0, "fc::1:2:3:4", "fc::4:3:2:1", "123", "321"},
563     {"PROXY TCP6 FC:00:00:00:1:2:3:4 FC:00:00:00:4:3:2:1 123 321\n", 0, 0, 0, 0, "fc::1:2:3:4", "fc::4:3:2:1", "123", "321"},
564     {"PROXY TCP6 1.2.3.4 4.3.2.1 123 321\n", 0, 0, 0, "unexpected client address syntax"},
565     {"PROXY TCP6 fc:00:00:00:1:2:3:4 4.3.2.1 123 321\n", 0, 0, 0, "unexpected server address syntax"},
566     /* IPv4 in IPv6. */
567     {"PROXY TCP6 ::ffff:1.2.3.4 ::ffff:4.3.2.1 123 321\n", 0, 0, 0, 0, "1.2.3.4", "4.3.2.1", "123", "321"},
568     {"PROXY TCP6 ::FFFF:1.2.3.4 ::FFFF:4.3.2.1 123 321\n", 0, 0, 0, 0, "1.2.3.4", "4.3.2.1", "123", "321"},
569     {"PROXY TCP4 ::ffff:1.2.3.4 ::ffff:4.3.2.1 123 321\n", 0, 0, 0, "unexpected client address syntax"},
570     {"PROXY TCP4 1.2.3.4 ::ffff:4.3.2.1 123 321\n", 0, 0, 0, "unexpected server address syntax"},
571     /* IPv4. */
572     {"PROXY TCP4 1.2.3.4 4.3.2.1 123 321\n", 0, 0, 0, 0, "1.2.3.4", "4.3.2.1", "123", "321"},
573     {"PROXY TCP4 01.02.03.04 04.03.02.01 123 321\n", 0, 0, 0, 0, "1.2.3.4", "4.3.2.1", "123", "321"},
574     {"PROXY TCP4 1.2.3.4 4.3.2.1 123456 321\n", 0, 0, 0, "unexpected client port syntax"},
575     {"PROXY TCP4 1.2.3.4 4.3.2.1 123 654321\n", 0, 0, 0, "unexpected server port syntax"},
576     {"PROXY TCP4 1.2.3.4 4.3.2.1 0123 321\n", 0, 0, 0, "unexpected client port syntax"},
577     {"PROXY TCP4 1.2.3.4 4.3.2.1 123 0321\n", 0, 0, 0, "unexpected server port syntax"},
578     /* Missing fields. */
579     {"PROXY TCP6 fc:00:00:00:1:2:3:4 fc:00:00:00:4:3:2:1 123\n", 0, 0, 0, "unexpected server port syntax"},
580     {"PROXY TCP6 fc:00:00:00:1:2:3:4 fc:00:00:00:4:3:2:1\n", 0, 0, 0, "unexpected client port syntax"},
581     {"PROXY TCP6 fc:00:00:00:1:2:3:4\n", 0, 0, 0, "unexpected server address syntax"},
582     {"PROXY TCP6\n", 0, 0, 0, "unexpected client address syntax"},
583     {"PROXY TCP4 1.2.3.4 4.3.2.1 123\n", 0, 0, 0, "unexpected server port syntax"},
584     {"PROXY TCP4 1.2.3.4 4.3.2.1\n", 0, 0, 0, "unexpected client port syntax"},
585     {"PROXY TCP4 1.2.3.4\n", 0, 0, 0, "unexpected server address syntax"},
586     {"PROXY TCP4\n", 0, 0, 0, "unexpected client address syntax"},
587     /* Other. */
588     {"PROXY BLAH\n", 0, 0, 0, "unsupported protocol type"},
589     {"BLAH\n", 0, 0, 0, "short protocol header"},
590     0,
591 };
592 
593 static struct proxy_hdr_v2 v2_local_request = {
594     PP2_SIGNATURE, PP2_VERSION | PP2_CMD_LOCAL,
595 };
596 static TEST_CASE v2_non_proxy_test = {
597     (char *) &v2_local_request, PP2_HEADER_LEN, PP2_HEADER_LEN, 1,
598 };
599 
600 #define STR(x)	vstring_str(x)
601 #define LEN(x)	VSTRING_LEN(x)
602 
603 /* evaluate_test_case - evaluate one test case */
604 
605 static int evaluate_test_case(const char *test_label,
606 			              const TEST_CASE *test_case)
607 {
608     /* Actual results. */
609     const char *act_return;
610     ssize_t act_req_len;
611     int     act_non_proxy;
612     MAI_HOSTADDR_STR act_smtp_client_addr;
613     MAI_HOSTADDR_STR act_smtp_server_addr;
614     MAI_SERVPORT_STR act_smtp_client_port;
615     MAI_SERVPORT_STR act_smtp_server_port;
616     int     test_failed;
617 
618     if (msg_verbose)
619 	msg_info("test case=%s exp_client_addr=%s exp_server_addr=%s "
620 		 "exp_client_port=%s exp_server_port=%s",
621 		 test_label, STR_OR_NULL(test_case->exp_client_addr),
622 		 STR_OR_NULL(test_case->exp_server_addr),
623 		 STR_OR_NULL(test_case->exp_client_port),
624 		 STR_OR_NULL(test_case->exp_server_port));
625 
626     /*
627      * Start the test.
628      */
629     test_failed = 0;
630     act_req_len = test_case->haproxy_req_len;
631     act_return =
632 	haproxy_srvr_parse(test_case->haproxy_request, &act_req_len,
633 			   &act_non_proxy,
634 			   &act_smtp_client_addr, &act_smtp_client_port,
635 			   &act_smtp_server_addr, &act_smtp_server_port);
636     if (act_return != test_case->exp_return) {
637 	msg_warn("test case %s return expected=%s actual=%s",
638 		 test_label, STR_OR_NULL(test_case->exp_return),
639 		 STR_OR_NULL(act_return));
640 	test_failed = 1;
641 	return (test_failed);
642     }
643     if (act_req_len != test_case->exp_req_len) {
644 	msg_warn("test case %s str_len expected=%ld actual=%ld",
645 		 test_label,
646 		 (long) test_case->exp_req_len, (long) act_req_len);
647 	test_failed = 1;
648 	return (test_failed);
649     }
650     if (act_non_proxy != test_case->exp_non_proxy) {
651 	msg_warn("test case %s non_proxy expected=%d actual=%d",
652 		 test_label,
653 		 test_case->exp_non_proxy, act_non_proxy);
654 	test_failed = 1;
655 	return (test_failed);
656     }
657     if (test_case->exp_non_proxy || test_case->exp_return != 0)
658 	/* No expected address/port results. */
659 	return (test_failed);
660 
661     /*
662      * Compare address/port results against expected results.
663      */
664     if (strcmp(test_case->exp_client_addr, act_smtp_client_addr.buf)) {
665 	msg_warn("test case %s client_addr  expected=%s actual=%s",
666 		 test_label,
667 		 test_case->exp_client_addr, act_smtp_client_addr.buf);
668 	test_failed = 1;
669     }
670     if (strcmp(test_case->exp_server_addr, act_smtp_server_addr.buf)) {
671 	msg_warn("test case %s server_addr  expected=%s actual=%s",
672 		 test_label,
673 		 test_case->exp_server_addr, act_smtp_server_addr.buf);
674 	test_failed = 1;
675     }
676     if (strcmp(test_case->exp_client_port, act_smtp_client_port.buf)) {
677 	msg_warn("test case %s client_port  expected=%s actual=%s",
678 		 test_label,
679 		 test_case->exp_client_port, act_smtp_client_port.buf);
680 	test_failed = 1;
681     }
682     if (strcmp(test_case->exp_server_port, act_smtp_server_port.buf)) {
683 	msg_warn("test case %s server_port  expected=%s actual=%s",
684 		 test_label,
685 		 test_case->exp_server_port, act_smtp_server_port.buf);
686 	test_failed = 1;
687     }
688     return (test_failed);
689 }
690 
691 /* convert_v1_proxy_req_to_v2 - convert well-formed v1 proxy request to v2 */
692 
693 static void convert_v1_proxy_req_to_v2(VSTRING *buf, const char *req,
694 				               ssize_t req_len)
695 {
696     const char myname[] = "convert_v1_proxy_req_to_v2";
697     const char *err;
698     int     non_proxy;
699     MAI_HOSTADDR_STR smtp_client_addr;
700     MAI_SERVPORT_STR smtp_client_port;
701     MAI_HOSTADDR_STR smtp_server_addr;
702     MAI_SERVPORT_STR smtp_server_port;
703     struct proxy_hdr_v2 *hdr_v2;
704     struct addrinfo *src_res;
705     struct addrinfo *dst_res;
706 
707     /*
708      * Allocate buffer space for the largest possible protocol header, so we
709      * don't have to worry about hidden realloc() calls.
710      */
711     VSTRING_RESET(buf);
712     VSTRING_SPACE(buf, sizeof(struct proxy_hdr_v2));
713     hdr_v2 = (struct proxy_hdr_v2 *) STR(buf);
714 
715     /*
716      * Fill in the header,
717      */
718     memcpy(hdr_v2->sig, PP2_SIGNATURE, PP2_SIGNATURE_LEN);
719     hdr_v2->ver_cmd = PP2_VERSION | PP2_CMD_PROXY;
720     if ((err = haproxy_srvr_parse(req, &req_len, &non_proxy, &smtp_client_addr,
721 				  &smtp_client_port, &smtp_server_addr,
722 				  &smtp_server_port)) != 0 || non_proxy)
723 	msg_fatal("%s: malformed or non-proxy request: %s",
724 		  myname, req);
725 
726     if (hostaddr_to_sockaddr(smtp_client_addr.buf, smtp_client_port.buf, 0,
727 			     &src_res) != 0)
728 	msg_fatal("%s: unable to convert source address %s port %s",
729 		  myname, smtp_client_addr.buf, smtp_client_port.buf);
730     if (hostaddr_to_sockaddr(smtp_server_addr.buf, smtp_server_port.buf, 0,
731 			     &dst_res) != 0)
732 	msg_fatal("%s: unable to convert destination address %s port %s",
733 		  myname, smtp_server_addr.buf, smtp_server_port.buf);
734     if (src_res->ai_family != dst_res->ai_family)
735 	msg_fatal("%s: mixed source/destination address families", myname);
736 #ifdef AF_INET6
737     if (src_res->ai_family == PF_INET6) {
738 	hdr_v2->fam = PP2_FAM_INET6 | PP2_TRANS_STREAM;
739 	hdr_v2->len = htons(PP2_ADDR_LEN_INET6);
740 	memcpy(hdr_v2->addr.ip6.src_addr,
741 	       &SOCK_ADDR_IN6_ADDR(src_res->ai_addr),
742 	       sizeof(hdr_v2->addr.ip6.src_addr));
743 	hdr_v2->addr.ip6.src_port = SOCK_ADDR_IN6_PORT(src_res->ai_addr);
744 	memcpy(hdr_v2->addr.ip6.dst_addr,
745 	       &SOCK_ADDR_IN6_ADDR(dst_res->ai_addr),
746 	       sizeof(hdr_v2->addr.ip6.dst_addr));
747 	hdr_v2->addr.ip6.dst_port = SOCK_ADDR_IN6_PORT(dst_res->ai_addr);
748     } else
749 #endif
750     if (src_res->ai_family == PF_INET) {
751 	hdr_v2->fam = PP2_FAM_INET | PP2_TRANS_STREAM;
752 	hdr_v2->len = htons(PP2_ADDR_LEN_INET);
753 	hdr_v2->addr.ip4.src_addr = SOCK_ADDR_IN_ADDR(src_res->ai_addr).s_addr;
754 	hdr_v2->addr.ip4.src_port = SOCK_ADDR_IN_PORT(src_res->ai_addr);
755 	hdr_v2->addr.ip4.dst_addr = SOCK_ADDR_IN_ADDR(dst_res->ai_addr).s_addr;
756 	hdr_v2->addr.ip4.dst_port = SOCK_ADDR_IN_PORT(dst_res->ai_addr);
757     } else {
758 	msg_panic("unknown address family 0x%x", src_res->ai_family);
759     }
760     vstring_set_payload_size(buf, PP2_SIGNATURE_LEN + ntohs(hdr_v2->len));
761     freeaddrinfo(src_res);
762     freeaddrinfo(dst_res);
763 }
764 
765 int     main(int argc, char **argv)
766 {
767     VSTRING *test_label;
768     TEST_CASE *v1_test_case;
769     TEST_CASE v2_test_case;
770     TEST_CASE mutated_test_case;
771     VSTRING *v2_request_buf;
772     VSTRING *mutated_request_buf;
773 
774     /* Findings. */
775     int     tests_failed = 0;
776     int     test_failed;
777 
778     test_label = vstring_alloc(100);
779     v2_request_buf = vstring_alloc(100);
780     mutated_request_buf = vstring_alloc(100);
781 
782     for (tests_failed = 0, v1_test_case = v1_test_cases;
783 	 v1_test_case->haproxy_request != 0;
784 	 tests_failed += test_failed, v1_test_case++) {
785 
786 	/*
787 	 * Fill in missing string length info in v1 test data.
788 	 */
789 	if (v1_test_case->haproxy_req_len == 0)
790 	    v1_test_case->haproxy_req_len =
791 		strlen(v1_test_case->haproxy_request);
792 	if (v1_test_case->exp_req_len == 0)
793 	    v1_test_case->exp_req_len = v1_test_case->haproxy_req_len;
794 
795 	/*
796 	 * Evaluate each v1 test case.
797 	 */
798 	vstring_sprintf(test_label, "%d", (int) (v1_test_case - v1_test_cases));
799 	test_failed = evaluate_test_case(STR(test_label), v1_test_case);
800 
801 	/*
802 	 * If the v1 test input is malformed, skip the mutation tests.
803 	 */
804 	if (v1_test_case->exp_return != 0)
805 	    continue;
806 
807 	/*
808 	 * Mutation test: a well-formed v1 test case should still pass after
809 	 * appending a byte, and should return the actual parsed header
810 	 * length. The test uses the implicit VSTRING null safety byte.
811 	 */
812 	vstring_sprintf(test_label, "%d (one byte appended)",
813 			(int) (v1_test_case - v1_test_cases));
814 	mutated_test_case = *v1_test_case;
815 	mutated_test_case.haproxy_req_len += 1;
816 	/* reuse v1_test_case->exp_req_len */
817 	test_failed += evaluate_test_case(STR(test_label), &mutated_test_case);
818 
819 	/*
820 	 * Mutation test: a well-formed v1 test case should fail after
821 	 * stripping the terminator.
822 	 */
823 	vstring_sprintf(test_label, "%d (last byte stripped)",
824 			(int) (v1_test_case - v1_test_cases));
825 	mutated_test_case = *v1_test_case;
826 	mutated_test_case.exp_return = "missing protocol header terminator";
827 	mutated_test_case.haproxy_req_len -= 1;
828 	mutated_test_case.exp_req_len = mutated_test_case.haproxy_req_len;
829 	test_failed += evaluate_test_case(STR(test_label), &mutated_test_case);
830 
831 	/*
832 	 * A 'well-formed' v1 test case should pass after conversion to v2.
833 	 */
834 	vstring_sprintf(test_label, "%d (converted to v2)",
835 			(int) (v1_test_case - v1_test_cases));
836 	v2_test_case = *v1_test_case;
837 	convert_v1_proxy_req_to_v2(v2_request_buf,
838 				   v1_test_case->haproxy_request,
839 				   v1_test_case->haproxy_req_len);
840 	v2_test_case.haproxy_request = STR(v2_request_buf);
841 	v2_test_case.haproxy_req_len = PP2_HEADER_LEN
842 	    + ntohs(((struct proxy_hdr_v2 *) STR(v2_request_buf))->len);
843 	v2_test_case.exp_req_len = v2_test_case.haproxy_req_len;
844 	test_failed += evaluate_test_case(STR(test_label), &v2_test_case);
845 
846 	/*
847 	 * Mutation test: a well-formed v2 test case should still pass after
848 	 * appending a byte, and should return the actual parsed header
849 	 * length. The test uses the implicit VSTRING null safety byte.
850 	 */
851 	vstring_sprintf(test_label, "%d (converted to v2, one byte appended)",
852 			(int) (v1_test_case - v1_test_cases));
853 	mutated_test_case = v2_test_case;
854 	mutated_test_case.haproxy_req_len += 1;
855 	/* reuse v2_test_case->exp_req_len */
856 	test_failed += evaluate_test_case(STR(test_label), &mutated_test_case);
857 
858 	/*
859 	 * Mutation test: a well-formed v2 test case should fail after
860 	 * stripping one byte
861 	 */
862 	vstring_sprintf(test_label, "%d (converted to v2, last byte stripped)",
863 			(int) (v1_test_case - v1_test_cases));
864 	mutated_test_case = v2_test_case;
865 	mutated_test_case.haproxy_req_len -= 1;
866 	mutated_test_case.exp_req_len = mutated_test_case.haproxy_req_len;
867 	mutated_test_case.exp_return = "short version 2 protocol header";
868 	test_failed += evaluate_test_case(STR(test_label), &mutated_test_case);
869     }
870 
871     /*
872      * Additional V2-only tests.
873      */
874     test_failed +=
875 	evaluate_test_case("v2 non-proxy request", &v2_non_proxy_test);
876 
877     /*
878      * Clean up.
879      */
880     vstring_free(v2_request_buf);
881     vstring_free(mutated_request_buf);
882     vstring_free(test_label);
883     if (tests_failed)
884 	msg_info("tests failed: %d", tests_failed);
885     exit(tests_failed != 0);
886 }
887 
888 #endif
889