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