1 /* $NetBSD: haproxy_srvr.c,v 1.3 2022/10/08 16:12:45 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 const 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 if (str == 0) 207 return (-1); 208 #ifdef AF_INET6 209 if (strcasecmp(str, "TCP6") == 0) { 210 if (strchr((char *) proto_info->sa_family_list, AF_INET6) != 0) { 211 *addr_family = AF_INET6; 212 return (0); 213 } 214 } else 215 #endif 216 if (strcasecmp(str, "TCP4") == 0) { 217 if (strchr((char *) proto_info->sa_family_list, AF_INET) != 0) { 218 *addr_family = AF_INET; 219 return (0); 220 } 221 } 222 return (-1); 223 } 224 225 /* haproxy_srvr_parse_addr - extract and validate IP address */ 226 227 static int haproxy_srvr_parse_addr(const char *str, MAI_HOSTADDR_STR *addr, 228 int addr_family) 229 { 230 struct addrinfo *res = 0; 231 int err; 232 233 if (msg_verbose) 234 msg_info("haproxy_srvr_parse: addr=%s proto=%d", 235 STR_OR_NULL(str), addr_family); 236 237 if (str == 0 || strlen(str) >= sizeof(MAI_HOSTADDR_STR)) 238 return (-1); 239 240 switch (addr_family) { 241 #ifdef AF_INET6 242 case AF_INET6: 243 err = !valid_ipv6_hostaddr(str, DONT_GRIPE); 244 break; 245 #endif 246 case AF_INET: 247 err = !valid_ipv4_hostaddr(str, DONT_GRIPE); 248 break; 249 default: 250 msg_panic("haproxy_srvr_parse: unexpected address family: %d", 251 addr_family); 252 } 253 if (err == 0) 254 err = (hostaddr_to_sockaddr(str, (char *) 0, 0, &res) 255 || sockaddr_to_hostaddr(res->ai_addr, res->ai_addrlen, 256 addr, (MAI_SERVPORT_STR *) 0, 0)); 257 if (res) 258 freeaddrinfo(res); 259 if (err) 260 return (-1); 261 if (addr->buf[0] == ':' && strncasecmp("::ffff:", addr->buf, 7) == 0 262 && strchr((char *) proto_info->sa_family_list, AF_INET) != 0) 263 memmove(addr->buf, addr->buf + 7, strlen(addr->buf) + 1 - 7); 264 return (0); 265 } 266 267 /* haproxy_srvr_parse_port - extract and validate TCP port */ 268 269 static int haproxy_srvr_parse_port(const char *str, MAI_SERVPORT_STR *port) 270 { 271 if (msg_verbose) 272 msg_info("haproxy_srvr_parse: port=%s", STR_OR_NULL(str)); 273 if (str == 0 || strlen(str) >= sizeof(MAI_SERVPORT_STR) 274 || !valid_hostport(str, DONT_GRIPE)) { 275 return (-1); 276 } else { 277 memcpy(port->buf, str, strlen(str) + 1); 278 return (0); 279 } 280 } 281 282 /* haproxy_srvr_parse_v2_addr_v4 - parse IPv4 info from v2 header */ 283 284 static int haproxy_srvr_parse_v2_addr_v4(uint32_t sin_addr, 285 unsigned sin_port, 286 MAI_HOSTADDR_STR *addr, 287 MAI_SERVPORT_STR *port) 288 { 289 struct sockaddr_in sin; 290 291 memset((void *) &sin, 0, sizeof(sin)); 292 sin.sin_family = AF_INET; 293 sin.sin_addr.s_addr = sin_addr; 294 sin.sin_port = sin_port; 295 if (sockaddr_to_hostaddr((struct sockaddr *) &sin, sizeof(sin), 296 addr, port, 0) < 0) 297 return (-1); 298 return (0); 299 } 300 301 #ifdef AF_INET6 302 303 /* haproxy_srvr_parse_v2_addr_v6 - parse IPv6 info from v2 header */ 304 305 static int haproxy_srvr_parse_v2_addr_v6(uint8_t *sin6_addr, 306 unsigned sin6_port, 307 MAI_HOSTADDR_STR *addr, 308 MAI_SERVPORT_STR *port) 309 { 310 struct sockaddr_in6 sin6; 311 312 memset((void *) &sin6, 0, sizeof(sin6)); 313 sin6.sin6_family = AF_INET6; 314 memcpy(&sin6.sin6_addr, sin6_addr, 16); 315 sin6.sin6_port = sin6_port; 316 if (sockaddr_to_hostaddr((struct sockaddr *) &sin6, 317 sizeof(sin6), addr, port, 0) < 0) 318 return (-1); 319 if (addr->buf[0] == ':' 320 && strncasecmp("::ffff:", addr->buf, 7) == 0 321 && strchr((char *) proto_info->sa_family_list, AF_INET) != 0) 322 memmove(addr->buf, addr->buf + 7, 323 strlen(addr->buf) + 1 - 7); 324 return (0); 325 } 326 327 #endif 328 329 /* haproxy_srvr_parse_v2_hdr - parse v2 header address info */ 330 331 static const char *haproxy_srvr_parse_v2_hdr(const char *str, ssize_t *str_len, 332 int *non_proxy, 333 MAI_HOSTADDR_STR *smtp_client_addr, 334 MAI_SERVPORT_STR *smtp_client_port, 335 MAI_HOSTADDR_STR *smtp_server_addr, 336 MAI_SERVPORT_STR *smtp_server_port) 337 { 338 const char myname[] = "haproxy_srvr_parse_v2_hdr"; 339 struct proxy_hdr_v2 *hdr_v2; 340 341 if (*str_len < PP2_HEADER_LEN) 342 return ("short protocol header"); 343 hdr_v2 = (struct proxy_hdr_v2 *) str; 344 if (memcmp(hdr_v2->sig, PP2_SIGNATURE, PP2_SIGNATURE_LEN) != 0) 345 return ("unrecognized protocol header"); 346 if ((hdr_v2->ver_cmd & PP2_VERSION_MASK) != PP2_VERSION) 347 return ("unrecognized protocol version"); 348 if (*str_len < PP2_HEADER_LEN + ntohs(hdr_v2->len)) 349 return ("short version 2 protocol header"); 350 351 switch (hdr_v2->ver_cmd & PP2_CMD_MASK) { 352 353 /* 354 * Proxied connection, use the proxy-provided connection info. 355 */ 356 case PP2_CMD_PROXY: 357 switch (hdr_v2->fam) { 358 case PP2_FAM_INET | PP2_TRANS_STREAM:{ /* TCP4 */ 359 if (strchr((char *) proto_info->sa_family_list, AF_INET) == 0) 360 return ("Postfix IPv4 support is disabled"); 361 if (ntohs(hdr_v2->len) < PP2_ADDR_LEN_INET) 362 return ("short address field"); 363 if (haproxy_srvr_parse_v2_addr_v4(hdr_v2->addr.ip4.src_addr, 364 hdr_v2->addr.ip4.src_port, 365 smtp_client_addr, smtp_client_port) < 0) 366 return ("client network address conversion error"); 367 if (msg_verbose) 368 msg_info("%s: smtp_client_addr=%s smtp_client_port=%s", 369 myname, smtp_client_addr->buf, smtp_client_port->buf); 370 if (haproxy_srvr_parse_v2_addr_v4(hdr_v2->addr.ip4.dst_addr, 371 hdr_v2->addr.ip4.dst_port, 372 smtp_server_addr, smtp_server_port) < 0) 373 return ("server network address conversion error"); 374 if (msg_verbose) 375 msg_info("%s: smtp_server_addr=%s smtp_server_port=%s", 376 myname, smtp_server_addr->buf, smtp_server_port->buf); 377 break; 378 } 379 case PP2_FAM_INET6 | PP2_TRANS_STREAM:{/* TCP6 */ 380 #ifdef AF_INET6 381 if (strchr((char *) proto_info->sa_family_list, AF_INET6) == 0) 382 return ("Postfix IPv6 support is disabled"); 383 if (ntohs(hdr_v2->len) < PP2_ADDR_LEN_INET6) 384 return ("short address field"); 385 if (haproxy_srvr_parse_v2_addr_v6(hdr_v2->addr.ip6.src_addr, 386 hdr_v2->addr.ip6.src_port, 387 smtp_client_addr, 388 smtp_client_port) < 0) 389 return ("client network address conversion error"); 390 if (msg_verbose) 391 msg_info("%s: smtp_client_addr=%s smtp_client_port=%s", 392 myname, smtp_client_addr->buf, smtp_client_port->buf); 393 if (haproxy_srvr_parse_v2_addr_v6(hdr_v2->addr.ip6.dst_addr, 394 hdr_v2->addr.ip6.dst_port, 395 smtp_server_addr, 396 smtp_server_port) < 0) 397 return ("server network address conversion error"); 398 if (msg_verbose) 399 msg_info("%s: smtp_server_addr=%s smtp_server_port=%s", 400 myname, smtp_server_addr->buf, smtp_server_port->buf); 401 break; 402 #else 403 return ("Postfix IPv6 support is not compiled in"); 404 #endif 405 } 406 default: 407 return ("unsupported network protocol"); 408 } 409 /* For now, skip and ignore TLVs. */ 410 *str_len = PP2_HEADER_LEN + ntohs(hdr_v2->len); 411 return (0); 412 413 /* 414 * Non-proxied connection, use the proxy-to-server connection info. 415 */ 416 case PP2_CMD_LOCAL: 417 /* For now, skip and ignore TLVs. */ 418 *non_proxy = 1; 419 *str_len = PP2_HEADER_LEN + ntohs(hdr_v2->len); 420 return (0); 421 default: 422 return ("bad command in proxy header"); 423 } 424 } 425 426 /* haproxy_srvr_parse - parse haproxy line */ 427 428 const char *haproxy_srvr_parse(const char *str, ssize_t *str_len, 429 int *non_proxy, 430 MAI_HOSTADDR_STR *smtp_client_addr, 431 MAI_SERVPORT_STR *smtp_client_port, 432 MAI_HOSTADDR_STR *smtp_server_addr, 433 MAI_SERVPORT_STR *smtp_server_port) 434 { 435 const char *err; 436 437 if (proto_info == 0) 438 proto_info = inet_proto_info(); 439 440 *non_proxy = 0; 441 442 /* 443 * XXX We don't accept connections with the "UNKNOWN" protocol type, 444 * because those would sidestep address-based access control mechanisms. 445 */ 446 447 /* 448 * Try version 1 protocol. 449 */ 450 if (strncmp(str, "PROXY ", 6) == 0) { 451 char *saved_str = mystrndup(str, *str_len); 452 char *cp = saved_str; 453 char *beyond_header = split_at(saved_str, '\n'); 454 int addr_family; 455 456 #define NEXT_TOKEN mystrtok(&cp, " \r") 457 if (beyond_header == 0) 458 err = "missing protocol header terminator"; 459 else if (haproxy_srvr_parse_lit(NEXT_TOKEN, "PROXY", (char *) 0) < 0) 460 err = "bad or missing protocol header"; 461 else if (haproxy_srvr_parse_proto(NEXT_TOKEN, &addr_family) < 0) 462 err = "bad or missing protocol type"; 463 else if (haproxy_srvr_parse_addr(NEXT_TOKEN, smtp_client_addr, 464 addr_family) < 0) 465 err = "bad or missing client address"; 466 else if (haproxy_srvr_parse_addr(NEXT_TOKEN, smtp_server_addr, 467 addr_family) < 0) 468 err = "bad or missing server address"; 469 else if (haproxy_srvr_parse_port(NEXT_TOKEN, smtp_client_port) < 0) 470 err = "bad or missing client port"; 471 else if (haproxy_srvr_parse_port(NEXT_TOKEN, smtp_server_port) < 0) 472 err = "bad or missing server port"; 473 else { 474 err = 0; 475 *str_len = beyond_header - saved_str; 476 } 477 myfree(saved_str); 478 479 return (err); 480 } 481 482 /* 483 * Try version 2 protocol. 484 */ 485 else { 486 return (haproxy_srvr_parse_v2_hdr(str, str_len, non_proxy, 487 smtp_client_addr, smtp_client_port, 488 smtp_server_addr, smtp_server_port)); 489 } 490 } 491 492 /* haproxy_srvr_receive - receive and parse haproxy protocol handshake */ 493 494 int haproxy_srvr_receive(int fd, int *non_proxy, 495 MAI_HOSTADDR_STR *smtp_client_addr, 496 MAI_SERVPORT_STR *smtp_client_port, 497 MAI_HOSTADDR_STR *smtp_server_addr, 498 MAI_SERVPORT_STR *smtp_server_port) 499 { 500 const char *err; 501 VSTRING *escape_buf; 502 char read_buf[HAPROXY_HEADER_MAX_LEN + 1]; 503 ssize_t read_len; 504 505 /* 506 * We must not read(2) past the end of the HaProxy handshake. The v2 507 * protocol assumes that the handshake will never be fragmented, 508 * therefore we peek, parse the entire input, then read(2) only the 509 * number of bytes parsed. 510 */ 511 if ((read_len = recv(fd, read_buf, sizeof(read_buf) - 1, MSG_PEEK)) <= 0) { 512 msg_warn("haproxy read: EOF"); 513 return (-1); 514 } 515 516 /* 517 * Parse the haproxy handshake, and determine the handshake length. 518 */ 519 read_buf[read_len] = 0; 520 521 if ((err = haproxy_srvr_parse(read_buf, &read_len, non_proxy, 522 smtp_client_addr, smtp_client_port, 523 smtp_server_addr, smtp_server_port)) != 0) { 524 escape_buf = vstring_alloc(read_len * 2); 525 escape(escape_buf, read_buf, read_len); 526 msg_warn("haproxy read: %s: %s", err, vstring_str(escape_buf)); 527 vstring_free(escape_buf); 528 return (-1); 529 } 530 531 /* 532 * Try to pop the haproxy handshake off the input queue. 533 */ 534 if (recv(fd, read_buf, read_len, 0) != read_len) { 535 msg_warn("haproxy read: %m"); 536 return (-1); 537 } 538 return (0); 539 } 540 541 /* 542 * Test program. 543 */ 544 #ifdef TEST 545 546 /* 547 * Test cases with inputs and expected outputs. A request may contain 548 * trailing garbage, and it may be too short. A v1 request may also contain 549 * malformed address or port information. 550 */ 551 typedef struct TEST_CASE { 552 const char *haproxy_request; /* v1 or v2 request including thrash */ 553 ssize_t haproxy_req_len; /* request length including thrash */ 554 ssize_t exp_req_len; /* parsed request length */ 555 int exp_non_proxy; /* request is not proxied */ 556 const char *exp_return; /* expected error string */ 557 const char *exp_client_addr; /* expected client address string */ 558 const char *exp_server_addr; /* expected client port string */ 559 const char *exp_client_port; /* expected client address string */ 560 const char *exp_server_port; /* expected server port string */ 561 } TEST_CASE; 562 static TEST_CASE v1_test_cases[] = { 563 /* IPv6. */ 564 {"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"}, 565 {"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"}, 566 {"PROXY TCP6 1.2.3.4 4.3.2.1 123 321\n", 0, 0, 0, "bad or missing client address"}, 567 {"PROXY TCP6 fc:00:00:00:1:2:3:4 4.3.2.1 123 321\n", 0, 0, 0, "bad or missing server address"}, 568 /* IPv4 in IPv6. */ 569 {"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"}, 570 {"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"}, 571 {"PROXY TCP4 ::ffff:1.2.3.4 ::ffff:4.3.2.1 123 321\n", 0, 0, 0, "bad or missing client address"}, 572 {"PROXY TCP4 1.2.3.4 ::ffff:4.3.2.1 123 321\n", 0, 0, 0, "bad or missing server address"}, 573 /* IPv4. */ 574 {"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"}, 575 {"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"}, 576 {"PROXY TCP4 1.2.3.4 4.3.2.1 123456 321\n", 0, 0, 0, "bad or missing client port"}, 577 {"PROXY TCP4 1.2.3.4 4.3.2.1 123 654321\n", 0, 0, 0, "bad or missing server port"}, 578 {"PROXY TCP4 1.2.3.4 4.3.2.1 0123 321\n", 0, 0, 0, "bad or missing client port"}, 579 {"PROXY TCP4 1.2.3.4 4.3.2.1 123 0321\n", 0, 0, 0, "bad or missing server port"}, 580 /* Missing fields. */ 581 {"PROXY TCP6 fc:00:00:00:1:2:3:4 fc:00:00:00:4:3:2:1 123\n", 0, 0, 0, "bad or missing server port"}, 582 {"PROXY TCP6 fc:00:00:00:1:2:3:4 fc:00:00:00:4:3:2:1\n", 0, 0, 0, "bad or missing client port"}, 583 {"PROXY TCP6 fc:00:00:00:1:2:3:4\n", 0, 0, 0, "bad or missing server address"}, 584 {"PROXY TCP6\n", 0, 0, 0, "bad or missing client address"}, 585 {"PROXY TCP4 1.2.3.4 4.3.2.1 123\n", 0, 0, 0, "bad or missing server port"}, 586 {"PROXY TCP4 1.2.3.4 4.3.2.1\n", 0, 0, 0, "bad or missing client port"}, 587 {"PROXY TCP4 1.2.3.4\n", 0, 0, 0, "bad or missing server address"}, 588 {"PROXY TCP4\n", 0, 0, 0, "bad or missing client address"}, 589 /* Other. */ 590 {"PROXY BLAH\n", 0, 0, 0, "bad or missing protocol type"}, 591 {"PROXY\n", 0, 0, 0, "short protocol header"}, 592 {"BLAH\n", 0, 0, 0, "short protocol header"}, 593 {"\n", 0, 0, 0, "short protocol header"}, 594 {"", 0, 0, 0, "short protocol header"}, 595 0, 596 }; 597 598 static struct proxy_hdr_v2 v2_local_request = { 599 PP2_SIGNATURE, PP2_VERSION | PP2_CMD_LOCAL, 600 }; 601 static TEST_CASE v2_non_proxy_test = { 602 (char *) &v2_local_request, PP2_HEADER_LEN, PP2_HEADER_LEN, 1, 603 }; 604 605 #define STR(x) vstring_str(x) 606 #define LEN(x) VSTRING_LEN(x) 607 608 /* evaluate_test_case - evaluate one test case */ 609 610 static int evaluate_test_case(const char *test_label, 611 const TEST_CASE *test_case) 612 { 613 /* Actual results. */ 614 const char *act_return; 615 ssize_t act_req_len; 616 int act_non_proxy; 617 MAI_HOSTADDR_STR act_smtp_client_addr; 618 MAI_HOSTADDR_STR act_smtp_server_addr; 619 MAI_SERVPORT_STR act_smtp_client_port; 620 MAI_SERVPORT_STR act_smtp_server_port; 621 int test_failed; 622 623 if (msg_verbose) 624 msg_info("test case=%s exp_client_addr=%s exp_server_addr=%s " 625 "exp_client_port=%s exp_server_port=%s", 626 test_label, STR_OR_NULL(test_case->exp_client_addr), 627 STR_OR_NULL(test_case->exp_server_addr), 628 STR_OR_NULL(test_case->exp_client_port), 629 STR_OR_NULL(test_case->exp_server_port)); 630 631 /* 632 * Start the test. 633 */ 634 test_failed = 0; 635 act_req_len = test_case->haproxy_req_len; 636 act_return = 637 haproxy_srvr_parse(test_case->haproxy_request, &act_req_len, 638 &act_non_proxy, 639 &act_smtp_client_addr, &act_smtp_client_port, 640 &act_smtp_server_addr, &act_smtp_server_port); 641 if (act_return != test_case->exp_return) { 642 msg_warn("test case %s return expected=%s actual=%s", 643 test_label, STR_OR_NULL(test_case->exp_return), 644 STR_OR_NULL(act_return)); 645 test_failed = 1; 646 return (test_failed); 647 } 648 if (act_req_len != test_case->exp_req_len) { 649 msg_warn("test case %s str_len expected=%ld actual=%ld", 650 test_label, 651 (long) test_case->exp_req_len, (long) act_req_len); 652 test_failed = 1; 653 return (test_failed); 654 } 655 if (act_non_proxy != test_case->exp_non_proxy) { 656 msg_warn("test case %s non_proxy expected=%d actual=%d", 657 test_label, 658 test_case->exp_non_proxy, act_non_proxy); 659 test_failed = 1; 660 return (test_failed); 661 } 662 if (test_case->exp_non_proxy || test_case->exp_return != 0) 663 /* No expected address/port results. */ 664 return (test_failed); 665 666 /* 667 * Compare address/port results against expected results. 668 */ 669 if (strcmp(test_case->exp_client_addr, act_smtp_client_addr.buf)) { 670 msg_warn("test case %s client_addr expected=%s actual=%s", 671 test_label, 672 test_case->exp_client_addr, act_smtp_client_addr.buf); 673 test_failed = 1; 674 } 675 if (strcmp(test_case->exp_server_addr, act_smtp_server_addr.buf)) { 676 msg_warn("test case %s server_addr expected=%s actual=%s", 677 test_label, 678 test_case->exp_server_addr, act_smtp_server_addr.buf); 679 test_failed = 1; 680 } 681 if (strcmp(test_case->exp_client_port, act_smtp_client_port.buf)) { 682 msg_warn("test case %s client_port expected=%s actual=%s", 683 test_label, 684 test_case->exp_client_port, act_smtp_client_port.buf); 685 test_failed = 1; 686 } 687 if (strcmp(test_case->exp_server_port, act_smtp_server_port.buf)) { 688 msg_warn("test case %s server_port expected=%s actual=%s", 689 test_label, 690 test_case->exp_server_port, act_smtp_server_port.buf); 691 test_failed = 1; 692 } 693 return (test_failed); 694 } 695 696 /* convert_v1_proxy_req_to_v2 - convert well-formed v1 proxy request to v2 */ 697 698 static void convert_v1_proxy_req_to_v2(VSTRING *buf, const char *req, 699 ssize_t req_len) 700 { 701 const char myname[] = "convert_v1_proxy_req_to_v2"; 702 const char *err; 703 int non_proxy; 704 MAI_HOSTADDR_STR smtp_client_addr; 705 MAI_SERVPORT_STR smtp_client_port; 706 MAI_HOSTADDR_STR smtp_server_addr; 707 MAI_SERVPORT_STR smtp_server_port; 708 struct proxy_hdr_v2 *hdr_v2; 709 struct addrinfo *src_res; 710 struct addrinfo *dst_res; 711 712 /* 713 * Allocate buffer space for the largest possible protocol header, so we 714 * don't have to worry about hidden realloc() calls. 715 */ 716 VSTRING_RESET(buf); 717 VSTRING_SPACE(buf, sizeof(struct proxy_hdr_v2)); 718 hdr_v2 = (struct proxy_hdr_v2 *) STR(buf); 719 720 /* 721 * Fill in the header, 722 */ 723 memcpy(hdr_v2->sig, PP2_SIGNATURE, PP2_SIGNATURE_LEN); 724 hdr_v2->ver_cmd = PP2_VERSION | PP2_CMD_PROXY; 725 if ((err = haproxy_srvr_parse(req, &req_len, &non_proxy, &smtp_client_addr, 726 &smtp_client_port, &smtp_server_addr, 727 &smtp_server_port)) != 0 || non_proxy) 728 msg_fatal("%s: malformed or non-proxy request: %s", 729 myname, req); 730 731 if (hostaddr_to_sockaddr(smtp_client_addr.buf, smtp_client_port.buf, 0, 732 &src_res) != 0) 733 msg_fatal("%s: unable to convert source address %s port %s", 734 myname, smtp_client_addr.buf, smtp_client_port.buf); 735 if (hostaddr_to_sockaddr(smtp_server_addr.buf, smtp_server_port.buf, 0, 736 &dst_res) != 0) 737 msg_fatal("%s: unable to convert destination address %s port %s", 738 myname, smtp_server_addr.buf, smtp_server_port.buf); 739 if (src_res->ai_family != dst_res->ai_family) 740 msg_fatal("%s: mixed source/destination address families", myname); 741 #ifdef AF_INET6 742 if (src_res->ai_family == PF_INET6) { 743 hdr_v2->fam = PP2_FAM_INET6 | PP2_TRANS_STREAM; 744 hdr_v2->len = htons(PP2_ADDR_LEN_INET6); 745 memcpy(hdr_v2->addr.ip6.src_addr, 746 &SOCK_ADDR_IN6_ADDR(src_res->ai_addr), 747 sizeof(hdr_v2->addr.ip6.src_addr)); 748 hdr_v2->addr.ip6.src_port = SOCK_ADDR_IN6_PORT(src_res->ai_addr); 749 memcpy(hdr_v2->addr.ip6.dst_addr, 750 &SOCK_ADDR_IN6_ADDR(dst_res->ai_addr), 751 sizeof(hdr_v2->addr.ip6.dst_addr)); 752 hdr_v2->addr.ip6.dst_port = SOCK_ADDR_IN6_PORT(dst_res->ai_addr); 753 } else 754 #endif 755 if (src_res->ai_family == PF_INET) { 756 hdr_v2->fam = PP2_FAM_INET | PP2_TRANS_STREAM; 757 hdr_v2->len = htons(PP2_ADDR_LEN_INET); 758 hdr_v2->addr.ip4.src_addr = SOCK_ADDR_IN_ADDR(src_res->ai_addr).s_addr; 759 hdr_v2->addr.ip4.src_port = SOCK_ADDR_IN_PORT(src_res->ai_addr); 760 hdr_v2->addr.ip4.dst_addr = SOCK_ADDR_IN_ADDR(dst_res->ai_addr).s_addr; 761 hdr_v2->addr.ip4.dst_port = SOCK_ADDR_IN_PORT(dst_res->ai_addr); 762 } else { 763 msg_panic("unknown address family 0x%x", src_res->ai_family); 764 } 765 vstring_set_payload_size(buf, PP2_SIGNATURE_LEN + ntohs(hdr_v2->len)); 766 freeaddrinfo(src_res); 767 freeaddrinfo(dst_res); 768 } 769 770 int main(int argc, char **argv) 771 { 772 VSTRING *test_label; 773 TEST_CASE *v1_test_case; 774 TEST_CASE v2_test_case; 775 TEST_CASE mutated_test_case; 776 VSTRING *v2_request_buf; 777 VSTRING *mutated_request_buf; 778 779 /* Findings. */ 780 int tests_failed = 0; 781 int test_failed; 782 783 test_label = vstring_alloc(100); 784 v2_request_buf = vstring_alloc(100); 785 mutated_request_buf = vstring_alloc(100); 786 787 for (tests_failed = 0, v1_test_case = v1_test_cases; 788 v1_test_case->haproxy_request != 0; 789 tests_failed += test_failed, v1_test_case++) { 790 791 /* 792 * Fill in missing string length info in v1 test data. 793 */ 794 if (v1_test_case->haproxy_req_len == 0) 795 v1_test_case->haproxy_req_len = 796 strlen(v1_test_case->haproxy_request); 797 if (v1_test_case->exp_req_len == 0) 798 v1_test_case->exp_req_len = v1_test_case->haproxy_req_len; 799 800 /* 801 * Evaluate each v1 test case. 802 */ 803 vstring_sprintf(test_label, "%d", (int) (v1_test_case - v1_test_cases)); 804 test_failed = evaluate_test_case(STR(test_label), v1_test_case); 805 806 /* 807 * If the v1 test input is malformed, skip the mutation tests. 808 */ 809 if (v1_test_case->exp_return != 0) 810 continue; 811 812 /* 813 * Mutation test: a well-formed v1 test case should still pass after 814 * appending a byte, and should return the actual parsed header 815 * length. The test uses the implicit VSTRING null safety byte. 816 */ 817 vstring_sprintf(test_label, "%d (one byte appended)", 818 (int) (v1_test_case - v1_test_cases)); 819 mutated_test_case = *v1_test_case; 820 mutated_test_case.haproxy_req_len += 1; 821 /* reuse v1_test_case->exp_req_len */ 822 test_failed += evaluate_test_case(STR(test_label), &mutated_test_case); 823 824 /* 825 * Mutation test: a well-formed v1 test case should fail after 826 * stripping the terminator. 827 */ 828 vstring_sprintf(test_label, "%d (last byte stripped)", 829 (int) (v1_test_case - v1_test_cases)); 830 mutated_test_case = *v1_test_case; 831 mutated_test_case.exp_return = "missing protocol header terminator"; 832 mutated_test_case.haproxy_req_len -= 1; 833 mutated_test_case.exp_req_len = mutated_test_case.haproxy_req_len; 834 test_failed += evaluate_test_case(STR(test_label), &mutated_test_case); 835 836 /* 837 * A 'well-formed' v1 test case should pass after conversion to v2. 838 */ 839 vstring_sprintf(test_label, "%d (converted to v2)", 840 (int) (v1_test_case - v1_test_cases)); 841 v2_test_case = *v1_test_case; 842 convert_v1_proxy_req_to_v2(v2_request_buf, 843 v1_test_case->haproxy_request, 844 v1_test_case->haproxy_req_len); 845 v2_test_case.haproxy_request = STR(v2_request_buf); 846 v2_test_case.haproxy_req_len = PP2_HEADER_LEN 847 + ntohs(((struct proxy_hdr_v2 *) STR(v2_request_buf))->len); 848 v2_test_case.exp_req_len = v2_test_case.haproxy_req_len; 849 test_failed += evaluate_test_case(STR(test_label), &v2_test_case); 850 851 /* 852 * Mutation test: a well-formed v2 test case should still pass after 853 * appending a byte, and should return the actual parsed header 854 * length. The test uses the implicit VSTRING null safety byte. 855 */ 856 vstring_sprintf(test_label, "%d (converted to v2, one byte appended)", 857 (int) (v1_test_case - v1_test_cases)); 858 mutated_test_case = v2_test_case; 859 mutated_test_case.haproxy_req_len += 1; 860 /* reuse v2_test_case->exp_req_len */ 861 test_failed += evaluate_test_case(STR(test_label), &mutated_test_case); 862 863 /* 864 * Mutation test: a well-formed v2 test case should fail after 865 * stripping one byte 866 */ 867 vstring_sprintf(test_label, "%d (converted to v2, last byte stripped)", 868 (int) (v1_test_case - v1_test_cases)); 869 mutated_test_case = v2_test_case; 870 mutated_test_case.haproxy_req_len -= 1; 871 mutated_test_case.exp_req_len = mutated_test_case.haproxy_req_len; 872 mutated_test_case.exp_return = "short version 2 protocol header"; 873 test_failed += evaluate_test_case(STR(test_label), &mutated_test_case); 874 } 875 876 /* 877 * Additional V2-only tests. 878 */ 879 test_failed += 880 evaluate_test_case("v2 non-proxy request", &v2_non_proxy_test); 881 882 /* 883 * Clean up. 884 */ 885 vstring_free(v2_request_buf); 886 vstring_free(mutated_request_buf); 887 vstring_free(test_label); 888 if (tests_failed) 889 msg_info("tests failed: %d", tests_failed); 890 exit(tests_failed != 0); 891 } 892 893 #endif 894