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