| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691 |
- /** BEGIN COPYRIGHT BLOCK
- * Copyright (C) 2025 Red Hat, Inc.
- * All rights reserved.
- *
- * License: GPL (version 3 or any later version).
- * See LICENSE for details.
- * END COPYRIGHT BLOCK **/
- #include "../../test_slapd.h"
- #include <string.h>
- #include <haproxy.h>
- typedef struct test_input {
- const char *input_str;
- int expected_result;
- size_t expected_len;
- int expected_proxy_connection;
- PRNetAddr expected_pr_netaddr_from;
- PRNetAddr expected_pr_netaddr_dest;
- } test_input;
- test_input test_cases[] = {
- {
- .input_str = "PROXY TCP4 192.168.0.1 192.168.0.2 12345 389\r\n",
- .expected_result = HAPROXY_HEADER_PARSED,
- .expected_len = 39,
- .expected_proxy_connection = 1,
- /* We need to support both big-endian (x390x) and little-endian (x86) architectures,
- * it's better to dynamically adjust the byte order in our test cases based on
- * the architecture of the system executing the tests.*/
- #ifdef __s390x__
- .expected_pr_netaddr_from = { .inet = { .family = PR_AF_INET, .ip = 0xC0A80001, .port = 0x3039 }},
- .expected_pr_netaddr_dest = { .inet = { .family = PR_AF_INET, .ip = 0xC0A80002, .port = 0x0185 }}
- #else
- .expected_pr_netaddr_from = { .inet = { .family = PR_AF_INET, .ip = 0x0100A8C0, .port = 0x3930 }},
- .expected_pr_netaddr_dest = { .inet = { .family = PR_AF_INET, .ip = 0x0200A8C0, .port = 0x8501 }}
- #endif
- },
- {
- .input_str = "PROXY TCP6 2001:db8::1 2001:db8::2 12345 389\r\n",
- .expected_result = HAPROXY_HEADER_PARSED,
- .expected_len = 46,
- .expected_proxy_connection = 1,
- #ifdef __s390x__
- .expected_pr_netaddr_from = { .ipv6 = { .family = PR_AF_INET6, .ip = {{{0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}}}, .port = 0x3039 }},
- .expected_pr_netaddr_dest = { .ipv6 = { .family = PR_AF_INET6, .ip = {{{0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}}}, .port = 0x0185 }}
- #else
- .expected_pr_netaddr_from = { .ipv6 = { .family = PR_AF_INET6, .ip = {{{0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01}}}, .port = 0x3930 }},
- .expected_pr_netaddr_dest = { .ipv6 = { .family = PR_AF_INET6, .ip = {{{0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02}}}, .port = 0x8501 }}
- #endif
- },
- {
- .input_str = "PROXY TCP6 ::ffff:192.168.0.1 ::ffff:192.168.0.2 12345 389\r\n",
- .expected_result = HAPROXY_HEADER_PARSED,
- .expected_len = 54,
- .expected_proxy_connection = 1,
- #ifdef __s390x__
- .expected_pr_netaddr_from = { .ipv6 = { .family = PR_AF_INET6, .ip = {{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xc0, 0xa8, 0x00, 0x01}}}, .port = 0x3039 }},
- .expected_pr_netaddr_dest = { .ipv6 = { .family = PR_AF_INET6, .ip = {{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xc0, 0xa8, 0x00, 0x02}}}, .port = 0x0185 }}
- #else
- .expected_pr_netaddr_from = { .ipv6 = { .family = PR_AF_INET6, .ip = {{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xc0, 0xa8, 0x00, 0x01}}}, .port = 0x3930 }},
- .expected_pr_netaddr_dest = { .ipv6 = { .family = PR_AF_INET6, .ip = {{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xc0, 0xa8, 0x00, 0x02}}}, .port = 0x8501 }}
- #endif
- },
- /* Invalid IP */
- {
- .input_str = "PROXY TCP4 256.168.0.1 192.168.0.2 12345 389\r\n",
- .expected_result = HAPROXY_ERROR,
- .expected_proxy_connection = 0,
- },
- /* Invalid port */
- {
- .input_str = "PROXY TCP4 192.168.0.1 192.168.0.2 123456 389\r\n",
- .expected_result = HAPROXY_ERROR,
- .expected_proxy_connection = 0,
- },
- /* One port */
- {
- .input_str = "PROXY TCP4 192.168.0.1 192.168.0.2 12345\r\n",
- .expected_result = HAPROXY_ERROR,
- .expected_proxy_connection = 0,
- },
- /* No ports */
- {
- .input_str = "PROXY TCP4 192.168.0.1 192.168.0.2\r\n",
- .expected_result = HAPROXY_ERROR,
- .expected_proxy_connection = 0,
- },
- /* Empty string */
- {
- .input_str = "",
- .expected_result = HAPROXY_NOT_A_HEADER,
- .expected_proxy_connection = 0,
- },
- /* Invalid protocol */
- {
- .input_str = "PROXY TCP3 192.168.0.1 192.168.0.2 12345 389\r\n",
- .expected_result = HAPROXY_ERROR,
- .expected_proxy_connection = 0,
- },
- /* Missing protocol */
- {
- .input_str = "PROXY 192.168.0.1 192.168.0.2 12345 389\r\n",
- .expected_result = HAPROXY_ERROR,
- .expected_proxy_connection = 0,
- },
- };
- size_t num_tests = sizeof(test_cases) / sizeof(test_cases[0]);
- void test_libslapd_haproxy_v1(void **state) {
- (void)state;
- int result = 0;
- for (size_t i = 0; i < num_tests; i++) {
- int proxy_connection = 0;
- PRNetAddr pr_netaddr_from = {{0}};
- PRNetAddr pr_netaddr_dest = {{0}};
- size_t str_len = strlen(test_cases[i].input_str);
- result = haproxy_parse_v1_hdr(test_cases[i].input_str, &str_len, &proxy_connection, &pr_netaddr_from, &pr_netaddr_dest);
- assert_int_equal(result, test_cases[i].expected_result);
- assert_int_equal(proxy_connection, test_cases[i].expected_proxy_connection);
- if (test_cases[i].expected_result == 0) {
- // slapi_log_error(SLAPI_LOG_ERR, "haproxy_parse_v1_hdr", "Expected pr_netaddr_from: ");
- // slapi_log_prnetaddr(&test_cases[i].expected_pr_netaddr_from);
- // slapi_log_error(SLAPI_LOG_ERR, "haproxy_parse_v1_hdr", "Actual pr_netaddr_from: ");
- // slapi_log_prnetaddr(&pr_netaddr_from);
- // slapi_log_error(SLAPI_LOG_ERR, "haproxy_parse_v1_hdr", "Expected pr_netaddr_dest: ");
- // slapi_log_prnetaddr(&test_cases[i].expected_pr_netaddr_dest);
- // slapi_log_error(SLAPI_LOG_ERR, "haproxy_parse_v1_hdr", "Actual pr_netaddr_dest: ");
- // slapi_log_prnetaddr(&pr_netaddr_dest);
- assert_memory_equal(&test_cases[i].expected_pr_netaddr_from, &pr_netaddr_from, sizeof(PRNetAddr));
- assert_memory_equal(&test_cases[i].expected_pr_netaddr_dest, &pr_netaddr_dest, sizeof(PRNetAddr));
- }
- }
- }
- void test_libslapd_haproxy_v2_invalid(void **state) {
- (void) state; // Unused
- struct {
- char *desc;
- char *str;
- struct proxy_hdr_v2 hdr_v2;
- size_t str_len;
- int expected_result;
- int expected_proxy_connection;
- } tests[] = {
- {"short header",
- "short",
- {},
- sizeof("short"),
- HAPROXY_NOT_A_HEADER,
- 0},
- {"invalid header",
- "invalid_signature",
- {},
- sizeof("invalid_signature"),
- HAPROXY_NOT_A_HEADER,
- 0},
- {"invalid signature",
- NULL,
- {"INVALID", PP2_VERSION | PP2_VER_CMD_PROXY, PP2_FAM_INET | PP2_TRANS_STREAM, htons(PP2_ADDR_LEN_INET)},
- PP2_HEADER_LEN + PP2_ADDR_LEN_INET,
- HAPROXY_NOT_A_HEADER,
- 0},
- {"unsupported family",
- NULL,
- {PP2_SIGNATURE, PP2_VERSION | PP2_VER_CMD_PROXY, 0x30 | PP2_TRANS_STREAM, htons(0)},
- PP2_HEADER_LEN,
- HAPROXY_ERROR,
- 0},
- {"unsupported protocol",
- NULL,
- {PP2_SIGNATURE, PP2_VERSION | PP2_VER_CMD_PROXY, PP2_FAM_INET | 0x30, htons(0)},
- PP2_HEADER_LEN,
- HAPROXY_ERROR,
- 0},
- {"invalid version",
- NULL,
- {PP2_SIGNATURE, (PP2_VERSION ^ 0xF0) | PP2_VER_CMD_PROXY, PP2_FAM_INET | PP2_TRANS_STREAM, htons(PP2_ADDR_LEN_INET)},
- PP2_HEADER_LEN + PP2_ADDR_LEN_INET,
- HAPROXY_ERROR,
- 0},
- {"valid header, wrong command",
- NULL,
- {PP2_SIGNATURE, PP2_VERSION | (PP2_VER_CMD_PROXY ^ 0xF0), PP2_FAM_INET | PP2_TRANS_STREAM, htons(PP2_ADDR_LEN_INET)},
- PP2_HEADER_LEN + PP2_ADDR_LEN_INET,
- HAPROXY_ERROR,
- 0},
- {"valid header, too long",
- NULL,
- {PP2_SIGNATURE, PP2_VERSION | PP2_VER_CMD_PROXY, PP2_FAM_INET | PP2_TRANS_STREAM, htons(PP2_ADDR_LEN_INET * 2)},
- PP2_HEADER_LEN + PP2_ADDR_LEN_INET,
- HAPROXY_ERROR,
- 0}
- };
- for (size_t i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
- int proxy_connection;
- PRNetAddr pr_netaddr_from;
- PRNetAddr pr_netaddr_dest;
- char *str_to_test = tests[i].str ? tests[i].str : (char *) &tests[i].hdr_v2;
- int result = haproxy_parse_v2_hdr(str_to_test, &tests[i].str_len, &proxy_connection, &pr_netaddr_from, &pr_netaddr_dest);
- assert_int_equal(result, tests[i].expected_result);
- assert_int_equal(proxy_connection, tests[i].expected_proxy_connection);
- }
- }
- // Test for a valid proxy header v2 with unsupported transport protocol
- void test_libslapd_haproxy_v2_unsupported_protocol(void **state) {
- (void) state; // Unused
- // Create a sample string with valid proxy header v2 and unsupported transport protocol
- struct proxy_hdr_v2 hdr_v2;
- memcpy(hdr_v2.sig, PP2_SIGNATURE, PP2_SIGNATURE_LEN);
- hdr_v2.ver_cmd = PP2_VERSION | PP2_VER_CMD_PROXY;
- hdr_v2.fam = PP2_FAM_INET | 0x30; // 0x30 is unsupported
- hdr_v2.len = htons(0);
- size_t str_len = PP2_HEADER_LEN;
- int proxy_connection;
- PRNetAddr pr_netaddr_from;
- PRNetAddr pr_netaddr_dest;
- int result = haproxy_parse_v2_hdr((const char *) &hdr_v2, &str_len, &proxy_connection, &pr_netaddr_from, &pr_netaddr_dest);
- assert_int_equal(result, HAPROXY_ERROR);
- assert_int_equal(proxy_connection, 0);
- }
- // Test for the case when the protocol version is invalid
- void test_libslapd_haproxy_v2_invalid_version(void **state) {
- (void) state; // Unused
- // Create a sample string with an invalid protocol version
- struct proxy_hdr_v2 hdr_v2;
- memcpy(hdr_v2.sig, PP2_SIGNATURE, PP2_SIGNATURE_LEN);
- hdr_v2.ver_cmd = (PP2_VERSION ^ 0xF0) | PP2_VER_CMD_PROXY;
- hdr_v2.fam = PP2_FAM_INET | PP2_TRANS_STREAM;
- hdr_v2.len = htons(PP2_ADDR_LEN_INET);
- size_t str_len = PP2_HEADER_LEN + PP2_ADDR_LEN_INET;
- int proxy_connection;
- PRNetAddr pr_netaddr_from;
- PRNetAddr pr_netaddr_dest;
- int result = haproxy_parse_v2_hdr((const char *) &hdr_v2, &str_len, &proxy_connection, &pr_netaddr_from, &pr_netaddr_dest);
- assert_int_equal(result, HAPROXY_ERROR);
- assert_int_equal(proxy_connection, 0);
- }
- // Test for the case when the protocol command is valid - IPv4 and IPv6
- void test_libslapd_haproxy_v2_valid(void **state) {
- (void) state; // Unused
- // We need only first two test cases as they are valid
- size_t num_tests = 2;
- for (size_t i = 0; i < num_tests; i++) {
- struct proxy_hdr_v2 hdr_v2;
- memcpy(hdr_v2.sig, PP2_SIGNATURE, PP2_SIGNATURE_LEN);
- hdr_v2.ver_cmd = PP2_VERSION | PP2_VER_CMD_PROXY;
- size_t str_len;
- int proxy_connection;
- PRNetAddr pr_netaddr_from = {0};
- PRNetAddr pr_netaddr_dest = {0};
- if (i == 0) { // IPv4 test case
- hdr_v2.fam = PP2_FAM_INET | PP2_TRANS_STREAM;
- hdr_v2.len = htons(PP2_ADDR_LEN_INET);
- uint32_t src_addr = test_cases[i].expected_pr_netaddr_from.inet.ip;
- uint32_t dst_addr = test_cases[i].expected_pr_netaddr_dest.inet.ip;
- uint16_t src_port = test_cases[i].expected_pr_netaddr_from.inet.port;
- uint16_t dst_port = test_cases[i].expected_pr_netaddr_dest.inet.port;
- memcpy(&hdr_v2.addr.ip4.src_addr, &src_addr, sizeof(src_addr));
- memcpy(&hdr_v2.addr.ip4.dst_addr, &dst_addr, sizeof(dst_addr));
- memcpy(&hdr_v2.addr.ip4.src_port, &src_port, sizeof(src_port));
- memcpy(&hdr_v2.addr.ip4.dst_port, &dst_port, sizeof(dst_port));
- str_len = PP2_HEADER_LEN + PP2_ADDR_LEN_INET;
- } else { // IPv6 test case
- hdr_v2.fam = PP2_FAM_INET6 | PP2_TRANS_STREAM;
- hdr_v2.len = htons(PP2_ADDR_LEN_INET6);
- uint8_t src_addr[16];
- uint8_t dst_addr[16];
- memcpy(src_addr, &test_cases[i].expected_pr_netaddr_from.ipv6.ip, sizeof(src_addr));
- memcpy(dst_addr, &test_cases[i].expected_pr_netaddr_dest.ipv6.ip, sizeof(dst_addr));
- uint16_t src_port = test_cases[i].expected_pr_netaddr_from.ipv6.port;
- uint16_t dst_port = test_cases[i].expected_pr_netaddr_dest.ipv6.port;
- memcpy(&hdr_v2.addr.ip6.src_addr, src_addr, sizeof(src_addr));
- memcpy(&hdr_v2.addr.ip6.dst_addr, dst_addr, sizeof(dst_addr));
- memcpy(&hdr_v2.addr.ip6.src_port, &src_port, sizeof(src_port));
- memcpy(&hdr_v2.addr.ip6.dst_port, &dst_port, sizeof(dst_port));
- str_len = PP2_HEADER_LEN + PP2_ADDR_LEN_INET6;
- }
- int rc = haproxy_parse_v2_hdr((const char *) &hdr_v2, &str_len, &proxy_connection, &pr_netaddr_from, &pr_netaddr_dest);
- assert_int_equal(rc, HAPROXY_HEADER_PARSED);
- assert_int_equal(proxy_connection, 1);
- assert_memory_equal(&pr_netaddr_from, &test_cases[i].expected_pr_netaddr_from, sizeof(PRNetAddr));
- assert_memory_equal(&pr_netaddr_dest, &test_cases[i].expected_pr_netaddr_dest, sizeof(PRNetAddr));
- }
- }
- // Test for a valid proxy header v2 with LOCAL command
- void test_libslapd_haproxy_v2_valid_local(void **state) {
- (void) state; // Unused
- // Create a sample string with valid proxy header v2 and LOCAL command
- struct proxy_hdr_v2 hdr_v2;
- memcpy(hdr_v2.sig, PP2_SIGNATURE, PP2_SIGNATURE_LEN);
- hdr_v2.ver_cmd = PP2_VERSION | PP2_VER_CMD_LOCAL;
- hdr_v2.fam = PP2_FAM_INET | PP2_TRANS_STREAM;
- hdr_v2.len = htons(0);
- size_t str_len = PP2_HEADER_LEN;
- int proxy_connection;
- PRNetAddr pr_netaddr_from;
- PRNetAddr pr_netaddr_dest;
- int result = haproxy_parse_v2_hdr((const char *) &hdr_v2, &str_len, &proxy_connection, &pr_netaddr_from, &pr_netaddr_dest);
- assert_int_equal(result, HAPROXY_HEADER_PARSED);
- assert_int_equal(proxy_connection, 0);
- }
- /* Test IPv4 subnet matching */
- void test_haproxy_ipv4_subnet_matching(void **state) {
- PRNetAddr ip1, ip2, network;
- (void)state; /* Unused */
- /* Test /24 subnet */
- PR_StringToNetAddr("192.168.1.50", &ip1);
- PR_StringToNetAddr("192.168.1.0", &network);
- assert_int_equal(haproxy_ipv4_in_subnet(ip1.inet.ip, network.inet.ip, 24), 1);
- /* Test IP outside /24 subnet */
- PR_StringToNetAddr("192.168.2.50", &ip2);
- assert_int_equal(haproxy_ipv4_in_subnet(ip2.inet.ip, network.inet.ip, 24), 0);
- /* Test /32 exact match */
- PR_StringToNetAddr("192.168.1.0", &ip1);
- assert_int_equal(haproxy_ipv4_in_subnet(ip1.inet.ip, network.inet.ip, 32), 1);
- /* Test /32 non-match */
- PR_StringToNetAddr("192.168.1.1", &ip1);
- assert_int_equal(haproxy_ipv4_in_subnet(ip1.inet.ip, network.inet.ip, 32), 0);
- /* Test /16 subnet */
- PR_StringToNetAddr("192.168.0.0", &network);
- PR_StringToNetAddr("192.168.255.255", &ip1);
- assert_int_equal(haproxy_ipv4_in_subnet(ip1.inet.ip, network.inet.ip, 16), 1);
- /* Test /0 matches everything */
- PR_StringToNetAddr("1.2.3.4", &ip1);
- assert_int_equal(haproxy_ipv4_in_subnet(ip1.inet.ip, network.inet.ip, 0), 1);
- }
- /* Test IPv6 subnet matching */
- void test_haproxy_ipv6_subnet_matching(void **state) {
- PRNetAddr ip1, network;
- struct in6_addr *ip_v6, *net_v6;
- (void)state; /* Unused */
- /* Test /64 subnet */
- PR_StringToNetAddr("2001:db8::1234", &ip1);
- PR_StringToNetAddr("2001:db8::", &network);
- ip_v6 = (struct in6_addr *)&ip1.ipv6.ip;
- net_v6 = (struct in6_addr *)&network.ipv6.ip;
- assert_int_equal(haproxy_ipv6_in_subnet(ip_v6, net_v6, 64), 1);
- /* Test IP outside /64 subnet */
- PR_StringToNetAddr("2001:db9::1234", &ip1);
- ip_v6 = (struct in6_addr *)&ip1.ipv6.ip;
- assert_int_equal(haproxy_ipv6_in_subnet(ip_v6, net_v6, 64), 0);
- /* Test /32 subnet */
- PR_StringToNetAddr("2001:db8:abcd::1234", &ip1);
- PR_StringToNetAddr("2001:db8::", &network);
- ip_v6 = (struct in6_addr *)&ip1.ipv6.ip;
- net_v6 = (struct in6_addr *)&network.ipv6.ip;
- assert_int_equal(haproxy_ipv6_in_subnet(ip_v6, net_v6, 32), 1);
- /* Test /128 exact match */
- PR_StringToNetAddr("2001:db8::1", &ip1);
- PR_StringToNetAddr("2001:db8::1", &network);
- ip_v6 = (struct in6_addr *)&ip1.ipv6.ip;
- net_v6 = (struct in6_addr *)&network.ipv6.ip;
- assert_int_equal(haproxy_ipv6_in_subnet(ip_v6, net_v6, 128), 1);
- /* Test /0 matches everything */
- PR_StringToNetAddr("fe80::1", &ip1);
- ip_v6 = (struct in6_addr *)&ip1.ipv6.ip;
- assert_int_equal(haproxy_ipv6_in_subnet(ip_v6, net_v6, 0), 1);
- }
- /* Test trusted IP parsing and binary matching */
- void test_haproxy_trusted_ip_parsing(void **state) {
- struct berval bv1 = {.bv_val = "192.168.1.0/24", .bv_len = 14};
- struct berval bv2 = {.bv_val = "10.0.0.5", .bv_len = 8};
- struct berval bv3 = {.bv_val = "2001:db8::/32", .bv_len = 13};
- struct berval bv4 = {.bv_val = "fe80::1", .bv_len = 7};
- struct berval *bvals[] = {&bv1, &bv2, &bv3, &bv4, NULL};
- size_t count = 0;
- char errorbuf[1024];
- haproxy_trusted_entry_t *parsed;
- PRNetAddr test_ip1, test_ip2, test_ip3, test_ip4, test_ip5, test_ip6, test_ip7;
- (void)state; /* Unused */
- /* Create test configuration with mixed IPs and subnets */
- /* Parse into binary format */
- parsed = haproxy_parse_trusted_ips(bvals, &count, errorbuf);
- assert_non_null(parsed);
- assert_int_equal(count, 4);
- /* Verify parsed structure for IPv4 subnet */
- assert_int_equal(parsed[0].is_subnet, 1);
- assert_int_equal(parsed[0].prefix_len, 24);
- assert_int_equal(parsed[0].network.raw.family, PR_AF_INET);
- /* Verify parsed structure for IPv4 single IP */
- assert_int_equal(parsed[1].is_subnet, 0);
- assert_int_equal(parsed[1].prefix_len, -1);
- assert_int_equal(parsed[1].network.raw.family, PR_AF_INET);
- /* Verify parsed structure for IPv6 subnet */
- assert_int_equal(parsed[2].is_subnet, 1);
- assert_int_equal(parsed[2].prefix_len, 32);
- assert_int_equal(parsed[2].network.raw.family, PR_AF_INET6);
- /* Verify parsed structure for IPv6 single IP */
- assert_int_equal(parsed[3].is_subnet, 0);
- assert_int_equal(parsed[3].prefix_len, -1);
- assert_int_equal(parsed[3].network.raw.family, PR_AF_INET6);
- /* Test binary matching with parsed entries */
- /* Test 1: IP in IPv4 subnet should match */
- PR_StringToNetAddr("192.168.1.100", &test_ip1);
- assert_int_equal(haproxy_ip_matches_parsed(&test_ip1, parsed, count), 1);
- /* Test 2: IP outside IPv4 subnet should not match */
- PR_StringToNetAddr("192.168.2.100", &test_ip2);
- assert_int_equal(haproxy_ip_matches_parsed(&test_ip2, parsed, count), 0);
- /* Test 3: Exact IPv4 match */
- PR_StringToNetAddr("10.0.0.5", &test_ip3);
- assert_int_equal(haproxy_ip_matches_parsed(&test_ip3, parsed, count), 1);
- /* Test 4: IP in IPv6 subnet should match */
- PR_StringToNetAddr("2001:db8::1234", &test_ip4);
- assert_int_equal(haproxy_ip_matches_parsed(&test_ip4, parsed, count), 1);
- /* Test 5: IP outside IPv6 subnet should not match */
- PR_StringToNetAddr("2001:db9::1234", &test_ip5);
- assert_int_equal(haproxy_ip_matches_parsed(&test_ip5, parsed, count), 0);
- /* Test 6: Exact IPv6 match */
- PR_StringToNetAddr("fe80::1", &test_ip6);
- assert_int_equal(haproxy_ip_matches_parsed(&test_ip6, parsed, count), 1);
- /* Test 7: IPv4-mapped IPv6 address normalization */
- PR_StringToNetAddr("::ffff:192.168.1.50", &test_ip7);
- /* Should match the 192.168.1.0/24 subnet after normalization */
- assert_int_equal(haproxy_ip_matches_parsed(&test_ip7, parsed, count), 1);
- /* Cleanup */
- slapi_ch_free((void **)&parsed);
- }
- /* Test edge cases and error handling in parsing */
- void test_haproxy_parsing_edge_cases(void **state) {
- size_t count = 0;
- char errorbuf[1024];
- haproxy_trusted_entry_t *parsed;
- struct berval *empty[] = {NULL};
- struct berval bv_8 = {.bv_val = "10.0.0.0/8", .bv_len = 10};
- struct berval bv_16 = {.bv_val = "172.16.0.0/16", .bv_len = 13};
- struct berval bv_32 = {.bv_val = "203.0.113.1/32", .bv_len = 14};
- struct berval *prefixes[] = {&bv_8, &bv_16, &bv_32, NULL};
- PRNetAddr test_ip;
- (void)state; /* Unused */
- /* Test NULL input */
- parsed = haproxy_parse_trusted_ips(NULL, &count, errorbuf);
- assert_null(parsed);
- assert_int_equal(count, 0);
- /* Test empty array */
- parsed = haproxy_parse_trusted_ips(empty, &count, errorbuf);
- assert_null(parsed);
- assert_int_equal(count, 0);
- /* Test various CIDR prefix lengths */
- parsed = haproxy_parse_trusted_ips(prefixes, &count, errorbuf);
- assert_non_null(parsed);
- assert_int_equal(count, 3);
- /* /8 should match wide range */
- PR_StringToNetAddr("10.255.255.255", &test_ip);
- assert_int_equal(haproxy_ip_matches_parsed(&test_ip, parsed, count), 1);
- /* /16 should match within range */
- PR_StringToNetAddr("172.16.99.99", &test_ip);
- assert_int_equal(haproxy_ip_matches_parsed(&test_ip, parsed, count), 1);
- /* Outside /16 */
- PR_StringToNetAddr("172.17.0.1", &test_ip);
- assert_int_equal(haproxy_ip_matches_parsed(&test_ip, parsed, count), 0);
- /* /32 exact match only */
- PR_StringToNetAddr("203.0.113.1", &test_ip);
- assert_int_equal(haproxy_ip_matches_parsed(&test_ip, parsed, count), 1);
- PR_StringToNetAddr("203.0.113.2", &test_ip);
- assert_int_equal(haproxy_ip_matches_parsed(&test_ip, parsed, count), 0);
- slapi_ch_free((void **)&parsed);
- }
- /* Test netmask precomputation during parsing */
- void test_haproxy_netmask_precomputation(void **state) {
- struct berval bv1 = {.bv_val = "192.168.1.0/24", .bv_len = 14};
- struct berval *bvals[] = {&bv1, NULL};
- struct berval bv2 = {.bv_val = "2001:db8:abcd::/48", .bv_len = 18};
- struct berval *bvals2[] = {&bv2, NULL};
- struct berval bv3 = {.bv_val = "2001:db8::/33", .bv_len = 13};
- struct berval *bvals3[] = {&bv3, NULL};
- size_t count = 0;
- char errorbuf[1024];
- haproxy_trusted_entry_t *parsed;
- uint32_t expected_mask;
- int i;
- (void)state; /* Unused */
- /* IPv4 /24 netmask should be 0xFFFFFF00 (255.255.255.0) */
- parsed = haproxy_parse_trusted_ips(bvals, &count, errorbuf);
- assert_non_null(parsed);
- assert_int_equal(count, 1);
- /* Verify netmask was pre-computed */
- expected_mask = htonl(0xFFFFFF00);
- assert_int_equal(parsed[0].netmask.inet.ip, expected_mask);
- slapi_ch_free((void **)&parsed);
- /* IPv6 /48 netmask - first 6 bytes should be 0xFF, rest 0x00 */
- parsed = haproxy_parse_trusted_ips(bvals2, &count, errorbuf);
- assert_non_null(parsed);
- assert_int_equal(count, 1);
- /* Verify IPv6 netmask bytes */
- for (i = 0; i < 6; i++) {
- assert_int_equal(parsed[0].netmask.ipv6.ip.pr_s6_addr[i], 0xFF);
- }
- for (i = 6; i < 16; i++) {
- assert_int_equal(parsed[0].netmask.ipv6.ip.pr_s6_addr[i], 0x00);
- }
- slapi_ch_free((void **)&parsed);
- /* IPv6 /33 - partial byte mask (first 4 bytes + 1 bit) */
- parsed = haproxy_parse_trusted_ips(bvals3, &count, errorbuf);
- assert_non_null(parsed);
- /* First 4 bytes should be 0xFF */
- for (i = 0; i < 4; i++) {
- assert_int_equal(parsed[0].netmask.ipv6.ip.pr_s6_addr[i], 0xFF);
- }
- /* 5th byte should be 0x80 (10000000 binary - 1 bit set) */
- assert_int_equal(parsed[0].netmask.ipv6.ip.pr_s6_addr[4], 0x80);
- /* Remaining bytes should be 0x00 */
- for (i = 5; i < 16; i++) {
- assert_int_equal(parsed[0].netmask.ipv6.ip.pr_s6_addr[i], 0x00);
- }
- slapi_ch_free((void **)&parsed);
- }
- /* Test CIDR string matching */
- void test_haproxy_ip_matches_cidr(void **state) {
- (void)state; /* Unused */
- /* IPv4 subnet matching */
- assert_int_equal(haproxy_ip_matches_cidr("192.168.1.50", "192.168.1.0/24"), 1);
- assert_int_equal(haproxy_ip_matches_cidr("192.168.2.50", "192.168.1.0/24"), 0);
- /* IPv4 exact match (no CIDR) */
- assert_int_equal(haproxy_ip_matches_cidr("10.0.0.1", "10.0.0.1"), 1);
- assert_int_equal(haproxy_ip_matches_cidr("10.0.0.1", "10.0.0.2"), 0);
- /* IPv6 subnet matching */
- assert_int_equal(haproxy_ip_matches_cidr("2001:db8::1234", "2001:db8::/32"), 1);
- assert_int_equal(haproxy_ip_matches_cidr("2001:db9::1234", "2001:db8::/32"), 0);
- /* IPv6 exact match */
- assert_int_equal(haproxy_ip_matches_cidr("fe80::1", "fe80::1"), 1);
- assert_int_equal(haproxy_ip_matches_cidr("fe80::1", "fe80::2"), 0);
- /* Case insensitivity for IPv6 */
- assert_int_equal(haproxy_ip_matches_cidr("2001:DB8::1", "2001:db8::1"), 1);
- }
- /* Test IPv4 mask calculation edge cases and undefined behavior prevention */
- void test_haproxy_ipv4_mask_edge_cases(void **state) {
- PRNetAddr ip1, ip2, network;
- (void)state; /* Unused */
- /* Test prefix_len = 0 (should match everything without undefined behavior) */
- PR_StringToNetAddr("192.168.1.1", &ip1);
- PR_StringToNetAddr("10.0.0.0", &network);
- assert_int_equal(haproxy_ipv4_in_subnet(ip1.inet.ip, network.inet.ip, 0), 1);
- /* Test with completely different IPs - /0 should still match */
- PR_StringToNetAddr("255.255.255.255", &ip1);
- PR_StringToNetAddr("0.0.0.0", &network);
- assert_int_equal(haproxy_ipv4_in_subnet(ip1.inet.ip, network.inet.ip, 0), 1);
- /* Test prefix_len = 32 (exact match only) */
- PR_StringToNetAddr("192.168.1.1", &ip1);
- PR_StringToNetAddr("192.168.1.1", &network);
- assert_int_equal(haproxy_ipv4_in_subnet(ip1.inet.ip, network.inet.ip, 32), 1);
- /* Test prefix_len = 32 with different IPs */
- PR_StringToNetAddr("192.168.1.2", &ip2);
- assert_int_equal(haproxy_ipv4_in_subnet(ip2.inet.ip, network.inet.ip, 32), 0);
- /* Test prefix_len = 1 (half of all IPs) */
- PR_StringToNetAddr("127.255.255.255", &ip1);
- PR_StringToNetAddr("0.0.0.0", &network);
- assert_int_equal(haproxy_ipv4_in_subnet(ip1.inet.ip, network.inet.ip, 1), 1);
- PR_StringToNetAddr("128.0.0.0", &ip2);
- assert_int_equal(haproxy_ipv4_in_subnet(ip2.inet.ip, network.inet.ip, 1), 0);
- /* Test prefix_len = 31 (smallest non-trivial subnet) */
- PR_StringToNetAddr("192.168.1.0", &network);
- PR_StringToNetAddr("192.168.1.0", &ip1);
- PR_StringToNetAddr("192.168.1.1", &ip2);
- assert_int_equal(haproxy_ipv4_in_subnet(ip1.inet.ip, network.inet.ip, 31), 1);
- assert_int_equal(haproxy_ipv4_in_subnet(ip2.inet.ip, network.inet.ip, 31), 1);
- PR_StringToNetAddr("192.168.1.2", &ip1);
- assert_int_equal(haproxy_ipv4_in_subnet(ip1.inet.ip, network.inet.ip, 31), 0);
- /* Test invalid prefix lengths */
- PR_StringToNetAddr("192.168.1.1", &ip1);
- PR_StringToNetAddr("192.168.1.0", &network);
- assert_int_equal(haproxy_ipv4_in_subnet(ip1.inet.ip, network.inet.ip, -1), 0);
- assert_int_equal(haproxy_ipv4_in_subnet(ip1.inet.ip, network.inet.ip, 33), 0);
- assert_int_equal(haproxy_ipv4_in_subnet(ip1.inet.ip, network.inet.ip, 255), 0);
- /* Test various prefix lengths to ensure no undefined behavior */
- PR_StringToNetAddr("10.0.0.0", &network);
- for (int prefix = 0; prefix <= 32; prefix++) {
- PR_StringToNetAddr("10.255.255.255", &ip1);
- /* Should not crash or exhibit undefined behavior */
- int result = haproxy_ipv4_in_subnet(ip1.inet.ip, network.inet.ip, (int)prefix);
- /* For /8, 10.255.255.255 should match 10.0.0.0 */
- if (prefix <= 8) {
- assert_int_equal(result, 1);
- } else if (prefix == 32) {
- assert_int_equal(result, 0); /* Not exact match */
- }
- }
- }
|