Home | History | Annotate | Line # | Download | only in global
      1 /*	$NetBSD: haproxy_srvr_test.c,v 1.2 2026/05/09 18:49:16 christos Exp $	*/
      2 
      3  /*
      4   * System library.
      5   */
      6 #include <sys_defs.h>
      7 #include <stdlib.h>
      8 
      9  /*
     10   * Utility library.
     11   */
     12 #include <msg.h>
     13 #include <msg_vstream.h>
     14 #include <myaddrinfo.h>
     15 #include <sock_addr.h>
     16 #include <stringops.h>
     17 #include <vstring.h>
     18 
     19  /*
     20   * Global library.
     21   */
     22 #define _HAPROXY_SRVR_INTERNAL_
     23 #include <haproxy_srvr.h>
     24 
     25  /*
     26   * Application-specific.
     27   */
     28 #define STR_OR_NULL(str) ((str) ? (str) : "(null)")
     29 
     30  /*
     31   * Test cases with inputs and expected outputs. A request may contain
     32   * trailing garbage, and it may be too short. A v1 request may also contain
     33   * malformed address or port information. TODO(wietse) add unit test with
     34   * different inet_protocols setting. See normalize_v4mapped_addr_test.c.
     35   */
     36 typedef struct TEST_CASE {
     37     const char *haproxy_request;	/* v1 or v2 request including thrash */
     38     ssize_t haproxy_req_len;		/* request length including thrash */
     39     ssize_t exp_req_len;		/* parsed request length */
     40     int     exp_non_proxy;		/* request is not proxied */
     41     const char *exp_return;		/* expected error string */
     42     const char *exp_client_addr;	/* expected client address string */
     43     const char *exp_server_addr;	/* expected client port string */
     44     const char *exp_client_port;	/* expected client address string */
     45     const char *exp_server_port;	/* expected server port string */
     46 } TEST_CASE;
     47 
     48 static TEST_CASE v1_test_cases[] = {
     49     /* IPv6. */
     50     {"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"},
     51     {"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"},
     52     {"PROXY TCP6 1.2.3.4 4.3.2.1 123 321\n", 0, 0, 0, "bad or missing client address"},
     53     {"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"},
     54     /* IPv4 in IPv6. */
     55     {"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"},
     56     {"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"},
     57     {"PROXY TCP4 ::ffff:1.2.3.4 ::ffff:4.3.2.1 123 321\n", 0, 0, 0, "bad or missing client address"},
     58     {"PROXY TCP4 1.2.3.4 ::ffff:4.3.2.1 123 321\n", 0, 0, 0, "bad or missing server address"},
     59     /* IPv4. */
     60     {"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"},
     61     {"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"},
     62     {"PROXY TCP4 1.2.3.4 4.3.2.1 123456 321\n", 0, 0, 0, "bad or missing client port"},
     63     {"PROXY TCP4 1.2.3.4 4.3.2.1 123 654321\n", 0, 0, 0, "bad or missing server port"},
     64     {"PROXY TCP4 1.2.3.4 4.3.2.1 0123 321\n", 0, 0, 0, "bad or missing client port"},
     65     {"PROXY TCP4 1.2.3.4 4.3.2.1 123 0321\n", 0, 0, 0, "bad or missing server port"},
     66     /* Missing fields. */
     67     {"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"},
     68     {"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"},
     69     {"PROXY TCP6 fc:00:00:00:1:2:3:4\n", 0, 0, 0, "bad or missing server address"},
     70     {"PROXY TCP6\n", 0, 0, 0, "bad or missing client address"},
     71     {"PROXY TCP4 1.2.3.4 4.3.2.1 123\n", 0, 0, 0, "bad or missing server port"},
     72     {"PROXY TCP4 1.2.3.4 4.3.2.1\n", 0, 0, 0, "bad or missing client port"},
     73     {"PROXY TCP4 1.2.3.4\n", 0, 0, 0, "bad or missing server address"},
     74     {"PROXY TCP4\n", 0, 0, 0, "bad or missing client address"},
     75     /* Other. */
     76     {"PROXY BLAH\n", 0, 0, 0, "bad or missing protocol type"},
     77     {"PROXY\n", 0, 0, 0, "short protocol header"},
     78     {"BLAH\n", 0, 0, 0, "short protocol header"},
     79     {"\n", 0, 0, 0, "short protocol header"},
     80     {"", 0, 0, 0, "short protocol header"},
     81     0,
     82 };
     83 
     84 static struct proxy_hdr_v2 v2_local_request = {
     85     PP2_SIGNATURE, PP2_VERSION | PP2_CMD_LOCAL,
     86 };
     87 static TEST_CASE v2_non_proxy_test = {
     88     (char *) &v2_local_request, PP2_HEADER_LEN, PP2_HEADER_LEN, 1,
     89 };
     90 
     91 #define STR(x)	vstring_str(x)
     92 #define LEN(x)	VSTRING_LEN(x)
     93 
     94 #define DO_SOCKADDR_OUTPUT	1
     95 #define NO_SOCKADDR_OUTPUT	0
     96 
     97 static int evaluate_sockaddr(const char *which, struct sockaddr *sa,
     98 			        SOCKADDR_SIZE sa_len, const char *want_addr,
     99 			             const char *want_port)
    100 {
    101     MAI_HOSTADDR_STR act_addr;
    102     MAI_SERVPORT_STR act_port;
    103     int     err;
    104     int     failed = 0;
    105 
    106     if ((err = sockaddr_to_hostaddr(sa, sa_len, &act_addr, &act_port, 0)) != 0) {
    107 	msg_warn("sockaddr_to_hostaddr: %s", MAI_STRERROR(err));
    108 	return (1);
    109     }
    110     if (strcmp(act_addr.buf, want_addr) != 0) {
    111 	msg_warn("got %s address '%s', want '%s'",
    112 		 which, act_addr.buf, want_addr);
    113 	failed = 1;
    114     }
    115     if (strcmp(act_port.buf, want_port) != 0) {
    116 	msg_warn("got %s port '%s', want '%s'",
    117 		 which, act_port.buf, want_port);
    118 	failed = 1;
    119     }
    120     return (failed);
    121 }
    122 
    123 /* evaluate_test_case - evaluate one base or mutated test case */
    124 
    125 static int evaluate_test_case(const char *test_label,
    126 			              const TEST_CASE *test_case,
    127 			              int want_sockaddr_output)
    128 {
    129     /* Actual results. */
    130     const char *act_return;
    131     ssize_t act_req_len;
    132     int     act_non_proxy;
    133     MAI_HOSTADDR_STR act_smtp_client_addr;
    134     MAI_HOSTADDR_STR act_smtp_server_addr;
    135     MAI_SERVPORT_STR act_smtp_client_port;
    136     MAI_SERVPORT_STR act_smtp_server_port;
    137     int     test_failed;
    138 
    139     struct sockaddr_storage client_ss;
    140     struct sockaddr *client_sa;
    141     SOCKADDR_SIZE client_sa_len;
    142 
    143     struct sockaddr_storage server_ss;
    144     struct sockaddr *server_sa;
    145     SOCKADDR_SIZE server_sa_len;
    146 
    147     if (msg_verbose)
    148 	msg_info("test case=%s exp_client_addr=%s exp_server_addr=%s "
    149 		 "exp_client_port=%s exp_server_port=%s",
    150 		 test_label, STR_OR_NULL(test_case->exp_client_addr),
    151 		 STR_OR_NULL(test_case->exp_server_addr),
    152 		 STR_OR_NULL(test_case->exp_client_port),
    153 		 STR_OR_NULL(test_case->exp_server_port));
    154 
    155     if (want_sockaddr_output) {
    156 	client_sa = (struct sockaddr *) &client_ss;
    157 	client_sa_len = sizeof(client_ss);
    158 	server_sa = (struct sockaddr *) &server_ss;
    159 	server_sa_len = sizeof(server_ss);
    160     } else {
    161 	client_sa = 0;
    162 	client_sa_len = 0;
    163 	server_sa = 0;
    164 	server_sa_len = 0;
    165     }
    166 
    167     /*
    168      * Start the test.
    169      */
    170     test_failed = 0;
    171     act_req_len = test_case->haproxy_req_len;
    172     act_return =
    173 	haproxy_srvr_parse_sa(test_case->haproxy_request, &act_req_len,
    174 			      &act_non_proxy,
    175 			      &act_smtp_client_addr, &act_smtp_client_port,
    176 			      &act_smtp_server_addr, &act_smtp_server_port,
    177 			      client_sa, &client_sa_len,
    178 			      server_sa, &server_sa_len);
    179     if (act_return != test_case->exp_return
    180     && strcmp(STR_OR_NULL(act_return), STR_OR_NULL(test_case->exp_return))) {
    181 	msg_warn("test case %s return expected=>%s< actual=>%s<",
    182 		 test_label, STR_OR_NULL(test_case->exp_return),
    183 		 STR_OR_NULL(act_return));
    184 	test_failed = 1;
    185 	return (test_failed);
    186     }
    187     if (act_req_len != test_case->exp_req_len) {
    188 	msg_warn("test case %s str_len expected=%ld actual=%ld",
    189 		 test_label,
    190 		 (long) test_case->exp_req_len, (long) act_req_len);
    191 	test_failed = 1;
    192 	return (test_failed);
    193     }
    194     if (act_non_proxy != test_case->exp_non_proxy) {
    195 	msg_warn("test case %s non_proxy expected=%d actual=%d",
    196 		 test_label,
    197 		 test_case->exp_non_proxy, act_non_proxy);
    198 	test_failed = 1;
    199 	return (test_failed);
    200     }
    201     if (test_case->exp_non_proxy || test_case->exp_return != 0)
    202 	/* No expected address/port results. */
    203 	return (test_failed);
    204 
    205     /*
    206      * Compare address/port results against expected results.
    207      */
    208     if (strcmp(test_case->exp_client_addr, act_smtp_client_addr.buf)) {
    209 	msg_warn("test case %s client_addr  expected=%s actual=%s",
    210 		 test_label,
    211 		 test_case->exp_client_addr, act_smtp_client_addr.buf);
    212 	test_failed = 1;
    213     }
    214     if (strcmp(test_case->exp_server_addr, act_smtp_server_addr.buf)) {
    215 	msg_warn("test case %s server_addr  expected=%s actual=%s",
    216 		 test_label,
    217 		 test_case->exp_server_addr, act_smtp_server_addr.buf);
    218 	test_failed = 1;
    219     }
    220     if (strcmp(test_case->exp_client_port, act_smtp_client_port.buf)) {
    221 	msg_warn("test case %s client_port  expected=%s actual=%s",
    222 		 test_label,
    223 		 test_case->exp_client_port, act_smtp_client_port.buf);
    224 	test_failed = 1;
    225     }
    226     if (strcmp(test_case->exp_server_port, act_smtp_server_port.buf)) {
    227 	msg_warn("test case %s server_port  expected=%s actual=%s",
    228 		 test_label,
    229 		 test_case->exp_server_port, act_smtp_server_port.buf);
    230 	test_failed = 1;
    231     }
    232     if (want_sockaddr_output) {
    233 	if (evaluate_sockaddr("client", client_sa, client_sa_len,
    234 			      test_case->exp_client_addr,
    235 			      test_case->exp_client_port) != 0
    236 	    || evaluate_sockaddr("server", server_sa, server_sa_len,
    237 				 test_case->exp_server_addr,
    238 				 test_case->exp_server_port) != 0)
    239 	    test_failed = 1;
    240     }
    241     return (test_failed);
    242 }
    243 
    244 /* convert_v1_proxy_req_to_v2 - convert well-formed v1 proxy request to v2 */
    245 
    246 static void convert_v1_proxy_req_to_v2(VSTRING *buf, const char *req,
    247 				               ssize_t req_len)
    248 {
    249     const char myname[] = "convert_v1_proxy_req_to_v2";
    250     const char *err;
    251     int     non_proxy;
    252     MAI_HOSTADDR_STR smtp_client_addr;
    253     MAI_SERVPORT_STR smtp_client_port;
    254     MAI_HOSTADDR_STR smtp_server_addr;
    255     MAI_SERVPORT_STR smtp_server_port;
    256     struct proxy_hdr_v2 *hdr_v2;
    257     struct addrinfo *src_res;
    258     struct addrinfo *dst_res;
    259 
    260     /*
    261      * Allocate buffer space for the largest possible protocol header, so we
    262      * don't have to worry about hidden realloc() calls.
    263      */
    264     VSTRING_RESET(buf);
    265     VSTRING_SPACE(buf, sizeof(struct proxy_hdr_v2));
    266     hdr_v2 = (struct proxy_hdr_v2 *) STR(buf);
    267 
    268     /*
    269      * Fill in the header,
    270      */
    271     memcpy(hdr_v2->sig, PP2_SIGNATURE, PP2_SIGNATURE_LEN);
    272     hdr_v2->ver_cmd = PP2_VERSION | PP2_CMD_PROXY;
    273     if ((err = haproxy_srvr_parse(req, &req_len, &non_proxy, &smtp_client_addr,
    274 				  &smtp_client_port, &smtp_server_addr,
    275 				  &smtp_server_port)) != 0 || non_proxy)
    276 	msg_fatal("%s: malformed or non-proxy request: %s",
    277 		  myname, req);
    278 
    279     if (hostaddr_to_sockaddr(smtp_client_addr.buf, smtp_client_port.buf, 0,
    280 			     &src_res) != 0)
    281 	msg_fatal("%s: unable to convert source address %s port %s",
    282 		  myname, smtp_client_addr.buf, smtp_client_port.buf);
    283     if (hostaddr_to_sockaddr(smtp_server_addr.buf, smtp_server_port.buf, 0,
    284 			     &dst_res) != 0)
    285 	msg_fatal("%s: unable to convert destination address %s port %s",
    286 		  myname, smtp_server_addr.buf, smtp_server_port.buf);
    287     if (src_res->ai_family != dst_res->ai_family)
    288 	msg_fatal("%s: mixed source/destination address families", myname);
    289 #ifdef AF_INET6
    290     if (src_res->ai_family == PF_INET6) {
    291 	hdr_v2->fam = PP2_FAM_INET6 | PP2_TRANS_STREAM;
    292 	hdr_v2->len = htons(PP2_ADDR_LEN_INET6);
    293 	memcpy(hdr_v2->addr.ip6.src_addr,
    294 	       &SOCK_ADDR_IN6_ADDR(src_res->ai_addr),
    295 	       sizeof(hdr_v2->addr.ip6.src_addr));
    296 	hdr_v2->addr.ip6.src_port = SOCK_ADDR_IN6_PORT(src_res->ai_addr);
    297 	memcpy(hdr_v2->addr.ip6.dst_addr,
    298 	       &SOCK_ADDR_IN6_ADDR(dst_res->ai_addr),
    299 	       sizeof(hdr_v2->addr.ip6.dst_addr));
    300 	hdr_v2->addr.ip6.dst_port = SOCK_ADDR_IN6_PORT(dst_res->ai_addr);
    301     } else
    302 #endif
    303     if (src_res->ai_family == PF_INET) {
    304 	hdr_v2->fam = PP2_FAM_INET | PP2_TRANS_STREAM;
    305 	hdr_v2->len = htons(PP2_ADDR_LEN_INET);
    306 	hdr_v2->addr.ip4.src_addr = SOCK_ADDR_IN_ADDR(src_res->ai_addr).s_addr;
    307 	hdr_v2->addr.ip4.src_port = SOCK_ADDR_IN_PORT(src_res->ai_addr);
    308 	hdr_v2->addr.ip4.dst_addr = SOCK_ADDR_IN_ADDR(dst_res->ai_addr).s_addr;
    309 	hdr_v2->addr.ip4.dst_port = SOCK_ADDR_IN_PORT(dst_res->ai_addr);
    310     } else {
    311 	msg_panic("unknown address family 0x%x", src_res->ai_family);
    312     }
    313     vstring_set_payload_size(buf, PP2_SIGNATURE_LEN + ntohs(hdr_v2->len));
    314     freeaddrinfo(src_res);
    315     freeaddrinfo(dst_res);
    316 }
    317 
    318 int     main(int argc, char **argv)
    319 {
    320     VSTRING *test_label;
    321     TEST_CASE *v1_test_case;
    322     TEST_CASE v2_test_case;
    323     TEST_CASE mutated_test_case;
    324     VSTRING *v2_request_buf;
    325     VSTRING *mutated_request_buf;
    326 
    327     msg_vstream_init(sane_basename((VSTRING *) 0, argv[0]), VSTREAM_ERR);
    328 
    329     /* Findings. */
    330     int     tests_failed = 0;
    331     int     tests_passed = 0;
    332 
    333     test_label = vstring_alloc(100);
    334     v2_request_buf = vstring_alloc(100);
    335     mutated_request_buf = vstring_alloc(100);
    336 
    337     /*
    338      * Evaluate each case in the test case list. If the test input is
    339      * well-formed, also test a number of mutations based on that test,
    340      * before proceeding with the next test case in the list.
    341      */
    342     for (tests_failed = 0, tests_passed = 0, v1_test_case = v1_test_cases;
    343 	 v1_test_case->haproxy_request != 0; v1_test_case++) {
    344 
    345 	/*
    346 	 * Fill in missing string length info in v1 test data.
    347 	 */
    348 	if (v1_test_case->haproxy_req_len == 0)
    349 	    v1_test_case->haproxy_req_len =
    350 		strlen(v1_test_case->haproxy_request);
    351 	if (v1_test_case->exp_req_len == 0)
    352 	    v1_test_case->exp_req_len = v1_test_case->haproxy_req_len;
    353 
    354 	/*
    355 	 * Evaluate each v1 test case.
    356 	 */
    357 	vstring_sprintf(test_label, "%d (%s%.5s input)",
    358 			(int) (v1_test_case - v1_test_cases),
    359 		     v1_test_case->exp_return ? "malformed" : "well-formed",
    360 	 v1_test_case->exp_return ? "" : v1_test_case->haproxy_request + 5);
    361 	msg_info("RUN  %s", STR(test_label));
    362 	if (evaluate_test_case(STR(test_label), v1_test_case,
    363 			       NO_SOCKADDR_OUTPUT)) {
    364 	    msg_info("FAIL %s", STR(test_label));
    365 	    tests_failed += 1;
    366 	} else {
    367 	    msg_info("PASS %s", STR(test_label));
    368 	    tests_passed += 1;
    369 	}
    370 
    371 	/*
    372 	 * If the v1 test input is malformed, skip the mutation tests.
    373 	 */
    374 	if (v1_test_case->exp_return != 0)
    375 	    continue;
    376 
    377 	/*
    378 	 * Mutation test: a well-formed v1 test case should also pass with
    379 	 * output to sockaddr arguments.
    380 	 */
    381 	vstring_sprintf(test_label, "%d (with sockaddr output)",
    382 			(int) (v1_test_case - v1_test_cases));
    383 	msg_info("RUN  %s", STR(test_label));
    384 	if (evaluate_test_case(STR(test_label), v1_test_case,
    385 			       DO_SOCKADDR_OUTPUT)) {
    386 	    msg_info("FAIL %s", STR(test_label));
    387 	    tests_failed += 1;
    388 	} else {
    389 	    msg_info("PASS %s", STR(test_label));
    390 	    tests_passed += 1;
    391 	}
    392 
    393 	/*
    394 	 * Mutation test: a well-formed v1 test case should still pass after
    395 	 * appending a byte, and should return the actual parsed header
    396 	 * length. The test uses the implicit VSTRING null safety byte.
    397 	 */
    398 	vstring_sprintf(test_label, "%d (one byte appended)",
    399 			(int) (v1_test_case - v1_test_cases));
    400 	mutated_test_case = *v1_test_case;
    401 	mutated_test_case.haproxy_req_len += 1;
    402 	msg_info("RUN  %s", STR(test_label));
    403 	/* reuse v1_test_case->exp_req_len */
    404 	if (evaluate_test_case(STR(test_label), &mutated_test_case,
    405 			       NO_SOCKADDR_OUTPUT)) {
    406 	    msg_info("FAIL %s", STR(test_label));
    407 	    tests_failed += 1;
    408 	} else {
    409 	    msg_info("PASS %s", STR(test_label));
    410 	    tests_passed += 1;
    411 	}
    412 
    413 	/*
    414 	 * Mutation test: a well-formed v1 test case should fail after
    415 	 * stripping the terminator.
    416 	 */
    417 	vstring_sprintf(test_label, "%d (last byte stripped)",
    418 			(int) (v1_test_case - v1_test_cases));
    419 	mutated_test_case = *v1_test_case;
    420 	mutated_test_case.exp_return = "missing protocol header terminator";
    421 	mutated_test_case.haproxy_req_len -= 1;
    422 	mutated_test_case.exp_req_len = mutated_test_case.haproxy_req_len;
    423 	msg_info("RUN  %s", STR(test_label));
    424 	if (evaluate_test_case(STR(test_label), &mutated_test_case,
    425 			       NO_SOCKADDR_OUTPUT)) {
    426 	    msg_info("FAIL %s", STR(test_label));
    427 	    tests_failed += 1;
    428 	} else {
    429 	    msg_info("PASS %s", STR(test_label));
    430 	    tests_passed += 1;
    431 	}
    432 
    433 	/*
    434 	 * A 'well-formed' v1 test case should pass after conversion to v2.
    435 	 */
    436 	vstring_sprintf(test_label, "%d (converted to v2)",
    437 			(int) (v1_test_case - v1_test_cases));
    438 	v2_test_case = *v1_test_case;
    439 	convert_v1_proxy_req_to_v2(v2_request_buf,
    440 				   v1_test_case->haproxy_request,
    441 				   v1_test_case->haproxy_req_len);
    442 	v2_test_case.haproxy_request = STR(v2_request_buf);
    443 	v2_test_case.haproxy_req_len = PP2_HEADER_LEN
    444 	    + ntohs(((struct proxy_hdr_v2 *) STR(v2_request_buf))->len);
    445 	v2_test_case.exp_req_len = v2_test_case.haproxy_req_len;
    446 	msg_info("RUN  %s", STR(test_label));
    447 	if (evaluate_test_case(STR(test_label), &v2_test_case,
    448 			       NO_SOCKADDR_OUTPUT)) {
    449 	    msg_info("FAIL %s", STR(test_label));
    450 	    tests_failed += 1;
    451 	} else {
    452 	    msg_info("PASS %s", STR(test_label));
    453 	    tests_passed += 1;
    454 	}
    455 
    456 	/*
    457 	 * Mutation test: a well-formed v2 test case should also pass with
    458 	 * output to sockaddr arguments.
    459 	 */
    460 	vstring_sprintf(test_label,
    461 			"%d (converted to v2, with sockaddr output)",
    462 			(int) (v1_test_case - v1_test_cases));
    463 	msg_info("RUN  %s", STR(test_label));
    464 	if (evaluate_test_case(STR(test_label), &v2_test_case,
    465 			       DO_SOCKADDR_OUTPUT)) {
    466 	    msg_info("FAIL %s", STR(test_label));
    467 	    tests_failed += 1;
    468 	} else {
    469 	    msg_info("PASS %s", STR(test_label));
    470 	    tests_passed += 1;
    471 	}
    472 
    473 	/*
    474 	 * Mutation test: a well-formed v2 test case should still pass after
    475 	 * appending a byte, and should return the actual parsed header
    476 	 * length. The test uses the implicit VSTRING null safety byte.
    477 	 */
    478 	vstring_sprintf(test_label, "%d (converted to v2, one byte appended)",
    479 			(int) (v1_test_case - v1_test_cases));
    480 	mutated_test_case = v2_test_case;
    481 	mutated_test_case.haproxy_req_len += 1;
    482 	/* reuse v2_test_case->exp_req_len */
    483 	msg_info("RUN  %s", STR(test_label));
    484 	if (evaluate_test_case(STR(test_label), &mutated_test_case,
    485 			       NO_SOCKADDR_OUTPUT)) {
    486 	    msg_info("FAIL %s", STR(test_label));
    487 	    tests_failed += 1;
    488 	} else {
    489 	    msg_info("PASS %s", STR(test_label));
    490 	    tests_passed += 1;
    491 	}
    492 
    493 	/*
    494 	 * Mutation test: a well-formed v2 test case should fail after
    495 	 * stripping one byte
    496 	 */
    497 	vstring_sprintf(test_label, "%d (converted to v2, last byte stripped)",
    498 			(int) (v1_test_case - v1_test_cases));
    499 	mutated_test_case = v2_test_case;
    500 	mutated_test_case.haproxy_req_len -= 1;
    501 	mutated_test_case.exp_req_len = mutated_test_case.haproxy_req_len;
    502 	mutated_test_case.exp_return = "short version 2 protocol header";
    503 	msg_info("RUN  %s", STR(test_label));
    504 	if (evaluate_test_case(STR(test_label), &mutated_test_case,
    505 			       NO_SOCKADDR_OUTPUT)) {
    506 	    msg_info("FAIL %s", STR(test_label));
    507 	    tests_failed += 1;
    508 	} else {
    509 	    msg_info("PASS %s", STR(test_label));
    510 	    tests_passed += 1;
    511 	}
    512     }
    513 
    514     /*
    515      * Additional V2-only tests.
    516      */
    517     vstring_strcpy(test_label, "v2 non-proxy request");
    518     msg_info("RUN  %s", STR(test_label));
    519     if (evaluate_test_case("v2 non-proxy request", &v2_non_proxy_test,
    520 			   NO_SOCKADDR_OUTPUT)) {
    521 	msg_info("FAIL %s", STR(test_label));
    522 	tests_failed += 1;
    523     } else {
    524 	msg_info("PASS %s", STR(test_label));
    525 	tests_passed += 1;
    526     }
    527 
    528     /*
    529      * Clean up.
    530      */
    531     vstring_free(v2_request_buf);
    532     vstring_free(mutated_request_buf);
    533     vstring_free(test_label);
    534     msg_info("PASS=%d FAIL=%d", tests_passed, tests_failed);
    535     exit(tests_failed != 0);
    536 }
    537