parse.c 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691
  1. /** BEGIN COPYRIGHT BLOCK
  2. * Copyright (C) 2025 Red Hat, Inc.
  3. * All rights reserved.
  4. *
  5. * License: GPL (version 3 or any later version).
  6. * See LICENSE for details.
  7. * END COPYRIGHT BLOCK **/
  8. #include "../../test_slapd.h"
  9. #include <string.h>
  10. #include <haproxy.h>
  11. typedef struct test_input {
  12. const char *input_str;
  13. int expected_result;
  14. size_t expected_len;
  15. int expected_proxy_connection;
  16. PRNetAddr expected_pr_netaddr_from;
  17. PRNetAddr expected_pr_netaddr_dest;
  18. } test_input;
  19. test_input test_cases[] = {
  20. {
  21. .input_str = "PROXY TCP4 192.168.0.1 192.168.0.2 12345 389\r\n",
  22. .expected_result = HAPROXY_HEADER_PARSED,
  23. .expected_len = 39,
  24. .expected_proxy_connection = 1,
  25. /* We need to support both big-endian (x390x) and little-endian (x86) architectures,
  26. * it's better to dynamically adjust the byte order in our test cases based on
  27. * the architecture of the system executing the tests.*/
  28. #ifdef __s390x__
  29. .expected_pr_netaddr_from = { .inet = { .family = PR_AF_INET, .ip = 0xC0A80001, .port = 0x3039 }},
  30. .expected_pr_netaddr_dest = { .inet = { .family = PR_AF_INET, .ip = 0xC0A80002, .port = 0x0185 }}
  31. #else
  32. .expected_pr_netaddr_from = { .inet = { .family = PR_AF_INET, .ip = 0x0100A8C0, .port = 0x3930 }},
  33. .expected_pr_netaddr_dest = { .inet = { .family = PR_AF_INET, .ip = 0x0200A8C0, .port = 0x8501 }}
  34. #endif
  35. },
  36. {
  37. .input_str = "PROXY TCP6 2001:db8::1 2001:db8::2 12345 389\r\n",
  38. .expected_result = HAPROXY_HEADER_PARSED,
  39. .expected_len = 46,
  40. .expected_proxy_connection = 1,
  41. #ifdef __s390x__
  42. .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 }},
  43. .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 }}
  44. #else
  45. .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 }},
  46. .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 }}
  47. #endif
  48. },
  49. {
  50. .input_str = "PROXY TCP6 ::ffff:192.168.0.1 ::ffff:192.168.0.2 12345 389\r\n",
  51. .expected_result = HAPROXY_HEADER_PARSED,
  52. .expected_len = 54,
  53. .expected_proxy_connection = 1,
  54. #ifdef __s390x__
  55. .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 }},
  56. .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 }}
  57. #else
  58. .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 }},
  59. .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 }}
  60. #endif
  61. },
  62. /* Invalid IP */
  63. {
  64. .input_str = "PROXY TCP4 256.168.0.1 192.168.0.2 12345 389\r\n",
  65. .expected_result = HAPROXY_ERROR,
  66. .expected_proxy_connection = 0,
  67. },
  68. /* Invalid port */
  69. {
  70. .input_str = "PROXY TCP4 192.168.0.1 192.168.0.2 123456 389\r\n",
  71. .expected_result = HAPROXY_ERROR,
  72. .expected_proxy_connection = 0,
  73. },
  74. /* One port */
  75. {
  76. .input_str = "PROXY TCP4 192.168.0.1 192.168.0.2 12345\r\n",
  77. .expected_result = HAPROXY_ERROR,
  78. .expected_proxy_connection = 0,
  79. },
  80. /* No ports */
  81. {
  82. .input_str = "PROXY TCP4 192.168.0.1 192.168.0.2\r\n",
  83. .expected_result = HAPROXY_ERROR,
  84. .expected_proxy_connection = 0,
  85. },
  86. /* Empty string */
  87. {
  88. .input_str = "",
  89. .expected_result = HAPROXY_NOT_A_HEADER,
  90. .expected_proxy_connection = 0,
  91. },
  92. /* Invalid protocol */
  93. {
  94. .input_str = "PROXY TCP3 192.168.0.1 192.168.0.2 12345 389\r\n",
  95. .expected_result = HAPROXY_ERROR,
  96. .expected_proxy_connection = 0,
  97. },
  98. /* Missing protocol */
  99. {
  100. .input_str = "PROXY 192.168.0.1 192.168.0.2 12345 389\r\n",
  101. .expected_result = HAPROXY_ERROR,
  102. .expected_proxy_connection = 0,
  103. },
  104. };
  105. size_t num_tests = sizeof(test_cases) / sizeof(test_cases[0]);
  106. void test_libslapd_haproxy_v1(void **state) {
  107. (void)state;
  108. int result = 0;
  109. for (size_t i = 0; i < num_tests; i++) {
  110. int proxy_connection = 0;
  111. PRNetAddr pr_netaddr_from = {{0}};
  112. PRNetAddr pr_netaddr_dest = {{0}};
  113. size_t str_len = strlen(test_cases[i].input_str);
  114. result = haproxy_parse_v1_hdr(test_cases[i].input_str, &str_len, &proxy_connection, &pr_netaddr_from, &pr_netaddr_dest);
  115. assert_int_equal(result, test_cases[i].expected_result);
  116. assert_int_equal(proxy_connection, test_cases[i].expected_proxy_connection);
  117. if (test_cases[i].expected_result == 0) {
  118. // slapi_log_error(SLAPI_LOG_ERR, "haproxy_parse_v1_hdr", "Expected pr_netaddr_from: ");
  119. // slapi_log_prnetaddr(&test_cases[i].expected_pr_netaddr_from);
  120. // slapi_log_error(SLAPI_LOG_ERR, "haproxy_parse_v1_hdr", "Actual pr_netaddr_from: ");
  121. // slapi_log_prnetaddr(&pr_netaddr_from);
  122. // slapi_log_error(SLAPI_LOG_ERR, "haproxy_parse_v1_hdr", "Expected pr_netaddr_dest: ");
  123. // slapi_log_prnetaddr(&test_cases[i].expected_pr_netaddr_dest);
  124. // slapi_log_error(SLAPI_LOG_ERR, "haproxy_parse_v1_hdr", "Actual pr_netaddr_dest: ");
  125. // slapi_log_prnetaddr(&pr_netaddr_dest);
  126. assert_memory_equal(&test_cases[i].expected_pr_netaddr_from, &pr_netaddr_from, sizeof(PRNetAddr));
  127. assert_memory_equal(&test_cases[i].expected_pr_netaddr_dest, &pr_netaddr_dest, sizeof(PRNetAddr));
  128. }
  129. }
  130. }
  131. void test_libslapd_haproxy_v2_invalid(void **state) {
  132. (void) state; // Unused
  133. struct {
  134. char *desc;
  135. char *str;
  136. struct proxy_hdr_v2 hdr_v2;
  137. size_t str_len;
  138. int expected_result;
  139. int expected_proxy_connection;
  140. } tests[] = {
  141. {"short header",
  142. "short",
  143. {},
  144. sizeof("short"),
  145. HAPROXY_NOT_A_HEADER,
  146. 0},
  147. {"invalid header",
  148. "invalid_signature",
  149. {},
  150. sizeof("invalid_signature"),
  151. HAPROXY_NOT_A_HEADER,
  152. 0},
  153. {"invalid signature",
  154. NULL,
  155. {"INVALID", PP2_VERSION | PP2_VER_CMD_PROXY, PP2_FAM_INET | PP2_TRANS_STREAM, htons(PP2_ADDR_LEN_INET)},
  156. PP2_HEADER_LEN + PP2_ADDR_LEN_INET,
  157. HAPROXY_NOT_A_HEADER,
  158. 0},
  159. {"unsupported family",
  160. NULL,
  161. {PP2_SIGNATURE, PP2_VERSION | PP2_VER_CMD_PROXY, 0x30 | PP2_TRANS_STREAM, htons(0)},
  162. PP2_HEADER_LEN,
  163. HAPROXY_ERROR,
  164. 0},
  165. {"unsupported protocol",
  166. NULL,
  167. {PP2_SIGNATURE, PP2_VERSION | PP2_VER_CMD_PROXY, PP2_FAM_INET | 0x30, htons(0)},
  168. PP2_HEADER_LEN,
  169. HAPROXY_ERROR,
  170. 0},
  171. {"invalid version",
  172. NULL,
  173. {PP2_SIGNATURE, (PP2_VERSION ^ 0xF0) | PP2_VER_CMD_PROXY, PP2_FAM_INET | PP2_TRANS_STREAM, htons(PP2_ADDR_LEN_INET)},
  174. PP2_HEADER_LEN + PP2_ADDR_LEN_INET,
  175. HAPROXY_ERROR,
  176. 0},
  177. {"valid header, wrong command",
  178. NULL,
  179. {PP2_SIGNATURE, PP2_VERSION | (PP2_VER_CMD_PROXY ^ 0xF0), PP2_FAM_INET | PP2_TRANS_STREAM, htons(PP2_ADDR_LEN_INET)},
  180. PP2_HEADER_LEN + PP2_ADDR_LEN_INET,
  181. HAPROXY_ERROR,
  182. 0},
  183. {"valid header, too long",
  184. NULL,
  185. {PP2_SIGNATURE, PP2_VERSION | PP2_VER_CMD_PROXY, PP2_FAM_INET | PP2_TRANS_STREAM, htons(PP2_ADDR_LEN_INET * 2)},
  186. PP2_HEADER_LEN + PP2_ADDR_LEN_INET,
  187. HAPROXY_ERROR,
  188. 0}
  189. };
  190. for (size_t i = 0; i < sizeof(tests) / sizeof(tests[0]); i++) {
  191. int proxy_connection;
  192. PRNetAddr pr_netaddr_from;
  193. PRNetAddr pr_netaddr_dest;
  194. char *str_to_test = tests[i].str ? tests[i].str : (char *) &tests[i].hdr_v2;
  195. int result = haproxy_parse_v2_hdr(str_to_test, &tests[i].str_len, &proxy_connection, &pr_netaddr_from, &pr_netaddr_dest);
  196. assert_int_equal(result, tests[i].expected_result);
  197. assert_int_equal(proxy_connection, tests[i].expected_proxy_connection);
  198. }
  199. }
  200. // Test for a valid proxy header v2 with unsupported transport protocol
  201. void test_libslapd_haproxy_v2_unsupported_protocol(void **state) {
  202. (void) state; // Unused
  203. // Create a sample string with valid proxy header v2 and unsupported transport protocol
  204. struct proxy_hdr_v2 hdr_v2;
  205. memcpy(hdr_v2.sig, PP2_SIGNATURE, PP2_SIGNATURE_LEN);
  206. hdr_v2.ver_cmd = PP2_VERSION | PP2_VER_CMD_PROXY;
  207. hdr_v2.fam = PP2_FAM_INET | 0x30; // 0x30 is unsupported
  208. hdr_v2.len = htons(0);
  209. size_t str_len = PP2_HEADER_LEN;
  210. int proxy_connection;
  211. PRNetAddr pr_netaddr_from;
  212. PRNetAddr pr_netaddr_dest;
  213. int result = haproxy_parse_v2_hdr((const char *) &hdr_v2, &str_len, &proxy_connection, &pr_netaddr_from, &pr_netaddr_dest);
  214. assert_int_equal(result, HAPROXY_ERROR);
  215. assert_int_equal(proxy_connection, 0);
  216. }
  217. // Test for the case when the protocol version is invalid
  218. void test_libslapd_haproxy_v2_invalid_version(void **state) {
  219. (void) state; // Unused
  220. // Create a sample string with an invalid protocol version
  221. struct proxy_hdr_v2 hdr_v2;
  222. memcpy(hdr_v2.sig, PP2_SIGNATURE, PP2_SIGNATURE_LEN);
  223. hdr_v2.ver_cmd = (PP2_VERSION ^ 0xF0) | PP2_VER_CMD_PROXY;
  224. hdr_v2.fam = PP2_FAM_INET | PP2_TRANS_STREAM;
  225. hdr_v2.len = htons(PP2_ADDR_LEN_INET);
  226. size_t str_len = PP2_HEADER_LEN + PP2_ADDR_LEN_INET;
  227. int proxy_connection;
  228. PRNetAddr pr_netaddr_from;
  229. PRNetAddr pr_netaddr_dest;
  230. int result = haproxy_parse_v2_hdr((const char *) &hdr_v2, &str_len, &proxy_connection, &pr_netaddr_from, &pr_netaddr_dest);
  231. assert_int_equal(result, HAPROXY_ERROR);
  232. assert_int_equal(proxy_connection, 0);
  233. }
  234. // Test for the case when the protocol command is valid - IPv4 and IPv6
  235. void test_libslapd_haproxy_v2_valid(void **state) {
  236. (void) state; // Unused
  237. // We need only first two test cases as they are valid
  238. size_t num_tests = 2;
  239. for (size_t i = 0; i < num_tests; i++) {
  240. struct proxy_hdr_v2 hdr_v2;
  241. memcpy(hdr_v2.sig, PP2_SIGNATURE, PP2_SIGNATURE_LEN);
  242. hdr_v2.ver_cmd = PP2_VERSION | PP2_VER_CMD_PROXY;
  243. size_t str_len;
  244. int proxy_connection;
  245. PRNetAddr pr_netaddr_from = {0};
  246. PRNetAddr pr_netaddr_dest = {0};
  247. if (i == 0) { // IPv4 test case
  248. hdr_v2.fam = PP2_FAM_INET | PP2_TRANS_STREAM;
  249. hdr_v2.len = htons(PP2_ADDR_LEN_INET);
  250. uint32_t src_addr = test_cases[i].expected_pr_netaddr_from.inet.ip;
  251. uint32_t dst_addr = test_cases[i].expected_pr_netaddr_dest.inet.ip;
  252. uint16_t src_port = test_cases[i].expected_pr_netaddr_from.inet.port;
  253. uint16_t dst_port = test_cases[i].expected_pr_netaddr_dest.inet.port;
  254. memcpy(&hdr_v2.addr.ip4.src_addr, &src_addr, sizeof(src_addr));
  255. memcpy(&hdr_v2.addr.ip4.dst_addr, &dst_addr, sizeof(dst_addr));
  256. memcpy(&hdr_v2.addr.ip4.src_port, &src_port, sizeof(src_port));
  257. memcpy(&hdr_v2.addr.ip4.dst_port, &dst_port, sizeof(dst_port));
  258. str_len = PP2_HEADER_LEN + PP2_ADDR_LEN_INET;
  259. } else { // IPv6 test case
  260. hdr_v2.fam = PP2_FAM_INET6 | PP2_TRANS_STREAM;
  261. hdr_v2.len = htons(PP2_ADDR_LEN_INET6);
  262. uint8_t src_addr[16];
  263. uint8_t dst_addr[16];
  264. memcpy(src_addr, &test_cases[i].expected_pr_netaddr_from.ipv6.ip, sizeof(src_addr));
  265. memcpy(dst_addr, &test_cases[i].expected_pr_netaddr_dest.ipv6.ip, sizeof(dst_addr));
  266. uint16_t src_port = test_cases[i].expected_pr_netaddr_from.ipv6.port;
  267. uint16_t dst_port = test_cases[i].expected_pr_netaddr_dest.ipv6.port;
  268. memcpy(&hdr_v2.addr.ip6.src_addr, src_addr, sizeof(src_addr));
  269. memcpy(&hdr_v2.addr.ip6.dst_addr, dst_addr, sizeof(dst_addr));
  270. memcpy(&hdr_v2.addr.ip6.src_port, &src_port, sizeof(src_port));
  271. memcpy(&hdr_v2.addr.ip6.dst_port, &dst_port, sizeof(dst_port));
  272. str_len = PP2_HEADER_LEN + PP2_ADDR_LEN_INET6;
  273. }
  274. int rc = haproxy_parse_v2_hdr((const char *) &hdr_v2, &str_len, &proxy_connection, &pr_netaddr_from, &pr_netaddr_dest);
  275. assert_int_equal(rc, HAPROXY_HEADER_PARSED);
  276. assert_int_equal(proxy_connection, 1);
  277. assert_memory_equal(&pr_netaddr_from, &test_cases[i].expected_pr_netaddr_from, sizeof(PRNetAddr));
  278. assert_memory_equal(&pr_netaddr_dest, &test_cases[i].expected_pr_netaddr_dest, sizeof(PRNetAddr));
  279. }
  280. }
  281. // Test for a valid proxy header v2 with LOCAL command
  282. void test_libslapd_haproxy_v2_valid_local(void **state) {
  283. (void) state; // Unused
  284. // Create a sample string with valid proxy header v2 and LOCAL command
  285. struct proxy_hdr_v2 hdr_v2;
  286. memcpy(hdr_v2.sig, PP2_SIGNATURE, PP2_SIGNATURE_LEN);
  287. hdr_v2.ver_cmd = PP2_VERSION | PP2_VER_CMD_LOCAL;
  288. hdr_v2.fam = PP2_FAM_INET | PP2_TRANS_STREAM;
  289. hdr_v2.len = htons(0);
  290. size_t str_len = PP2_HEADER_LEN;
  291. int proxy_connection;
  292. PRNetAddr pr_netaddr_from;
  293. PRNetAddr pr_netaddr_dest;
  294. int result = haproxy_parse_v2_hdr((const char *) &hdr_v2, &str_len, &proxy_connection, &pr_netaddr_from, &pr_netaddr_dest);
  295. assert_int_equal(result, HAPROXY_HEADER_PARSED);
  296. assert_int_equal(proxy_connection, 0);
  297. }
  298. /* Test IPv4 subnet matching */
  299. void test_haproxy_ipv4_subnet_matching(void **state) {
  300. PRNetAddr ip1, ip2, network;
  301. (void)state; /* Unused */
  302. /* Test /24 subnet */
  303. PR_StringToNetAddr("192.168.1.50", &ip1);
  304. PR_StringToNetAddr("192.168.1.0", &network);
  305. assert_int_equal(haproxy_ipv4_in_subnet(ip1.inet.ip, network.inet.ip, 24), 1);
  306. /* Test IP outside /24 subnet */
  307. PR_StringToNetAddr("192.168.2.50", &ip2);
  308. assert_int_equal(haproxy_ipv4_in_subnet(ip2.inet.ip, network.inet.ip, 24), 0);
  309. /* Test /32 exact match */
  310. PR_StringToNetAddr("192.168.1.0", &ip1);
  311. assert_int_equal(haproxy_ipv4_in_subnet(ip1.inet.ip, network.inet.ip, 32), 1);
  312. /* Test /32 non-match */
  313. PR_StringToNetAddr("192.168.1.1", &ip1);
  314. assert_int_equal(haproxy_ipv4_in_subnet(ip1.inet.ip, network.inet.ip, 32), 0);
  315. /* Test /16 subnet */
  316. PR_StringToNetAddr("192.168.0.0", &network);
  317. PR_StringToNetAddr("192.168.255.255", &ip1);
  318. assert_int_equal(haproxy_ipv4_in_subnet(ip1.inet.ip, network.inet.ip, 16), 1);
  319. /* Test /0 matches everything */
  320. PR_StringToNetAddr("1.2.3.4", &ip1);
  321. assert_int_equal(haproxy_ipv4_in_subnet(ip1.inet.ip, network.inet.ip, 0), 1);
  322. }
  323. /* Test IPv6 subnet matching */
  324. void test_haproxy_ipv6_subnet_matching(void **state) {
  325. PRNetAddr ip1, network;
  326. struct in6_addr *ip_v6, *net_v6;
  327. (void)state; /* Unused */
  328. /* Test /64 subnet */
  329. PR_StringToNetAddr("2001:db8::1234", &ip1);
  330. PR_StringToNetAddr("2001:db8::", &network);
  331. ip_v6 = (struct in6_addr *)&ip1.ipv6.ip;
  332. net_v6 = (struct in6_addr *)&network.ipv6.ip;
  333. assert_int_equal(haproxy_ipv6_in_subnet(ip_v6, net_v6, 64), 1);
  334. /* Test IP outside /64 subnet */
  335. PR_StringToNetAddr("2001:db9::1234", &ip1);
  336. ip_v6 = (struct in6_addr *)&ip1.ipv6.ip;
  337. assert_int_equal(haproxy_ipv6_in_subnet(ip_v6, net_v6, 64), 0);
  338. /* Test /32 subnet */
  339. PR_StringToNetAddr("2001:db8:abcd::1234", &ip1);
  340. PR_StringToNetAddr("2001:db8::", &network);
  341. ip_v6 = (struct in6_addr *)&ip1.ipv6.ip;
  342. net_v6 = (struct in6_addr *)&network.ipv6.ip;
  343. assert_int_equal(haproxy_ipv6_in_subnet(ip_v6, net_v6, 32), 1);
  344. /* Test /128 exact match */
  345. PR_StringToNetAddr("2001:db8::1", &ip1);
  346. PR_StringToNetAddr("2001:db8::1", &network);
  347. ip_v6 = (struct in6_addr *)&ip1.ipv6.ip;
  348. net_v6 = (struct in6_addr *)&network.ipv6.ip;
  349. assert_int_equal(haproxy_ipv6_in_subnet(ip_v6, net_v6, 128), 1);
  350. /* Test /0 matches everything */
  351. PR_StringToNetAddr("fe80::1", &ip1);
  352. ip_v6 = (struct in6_addr *)&ip1.ipv6.ip;
  353. assert_int_equal(haproxy_ipv6_in_subnet(ip_v6, net_v6, 0), 1);
  354. }
  355. /* Test trusted IP parsing and binary matching */
  356. void test_haproxy_trusted_ip_parsing(void **state) {
  357. struct berval bv1 = {.bv_val = "192.168.1.0/24", .bv_len = 14};
  358. struct berval bv2 = {.bv_val = "10.0.0.5", .bv_len = 8};
  359. struct berval bv3 = {.bv_val = "2001:db8::/32", .bv_len = 13};
  360. struct berval bv4 = {.bv_val = "fe80::1", .bv_len = 7};
  361. struct berval *bvals[] = {&bv1, &bv2, &bv3, &bv4, NULL};
  362. size_t count = 0;
  363. char errorbuf[1024];
  364. haproxy_trusted_entry_t *parsed;
  365. PRNetAddr test_ip1, test_ip2, test_ip3, test_ip4, test_ip5, test_ip6, test_ip7;
  366. (void)state; /* Unused */
  367. /* Create test configuration with mixed IPs and subnets */
  368. /* Parse into binary format */
  369. parsed = haproxy_parse_trusted_ips(bvals, &count, errorbuf);
  370. assert_non_null(parsed);
  371. assert_int_equal(count, 4);
  372. /* Verify parsed structure for IPv4 subnet */
  373. assert_int_equal(parsed[0].is_subnet, 1);
  374. assert_int_equal(parsed[0].prefix_len, 24);
  375. assert_int_equal(parsed[0].network.raw.family, PR_AF_INET);
  376. /* Verify parsed structure for IPv4 single IP */
  377. assert_int_equal(parsed[1].is_subnet, 0);
  378. assert_int_equal(parsed[1].prefix_len, -1);
  379. assert_int_equal(parsed[1].network.raw.family, PR_AF_INET);
  380. /* Verify parsed structure for IPv6 subnet */
  381. assert_int_equal(parsed[2].is_subnet, 1);
  382. assert_int_equal(parsed[2].prefix_len, 32);
  383. assert_int_equal(parsed[2].network.raw.family, PR_AF_INET6);
  384. /* Verify parsed structure for IPv6 single IP */
  385. assert_int_equal(parsed[3].is_subnet, 0);
  386. assert_int_equal(parsed[3].prefix_len, -1);
  387. assert_int_equal(parsed[3].network.raw.family, PR_AF_INET6);
  388. /* Test binary matching with parsed entries */
  389. /* Test 1: IP in IPv4 subnet should match */
  390. PR_StringToNetAddr("192.168.1.100", &test_ip1);
  391. assert_int_equal(haproxy_ip_matches_parsed(&test_ip1, parsed, count), 1);
  392. /* Test 2: IP outside IPv4 subnet should not match */
  393. PR_StringToNetAddr("192.168.2.100", &test_ip2);
  394. assert_int_equal(haproxy_ip_matches_parsed(&test_ip2, parsed, count), 0);
  395. /* Test 3: Exact IPv4 match */
  396. PR_StringToNetAddr("10.0.0.5", &test_ip3);
  397. assert_int_equal(haproxy_ip_matches_parsed(&test_ip3, parsed, count), 1);
  398. /* Test 4: IP in IPv6 subnet should match */
  399. PR_StringToNetAddr("2001:db8::1234", &test_ip4);
  400. assert_int_equal(haproxy_ip_matches_parsed(&test_ip4, parsed, count), 1);
  401. /* Test 5: IP outside IPv6 subnet should not match */
  402. PR_StringToNetAddr("2001:db9::1234", &test_ip5);
  403. assert_int_equal(haproxy_ip_matches_parsed(&test_ip5, parsed, count), 0);
  404. /* Test 6: Exact IPv6 match */
  405. PR_StringToNetAddr("fe80::1", &test_ip6);
  406. assert_int_equal(haproxy_ip_matches_parsed(&test_ip6, parsed, count), 1);
  407. /* Test 7: IPv4-mapped IPv6 address normalization */
  408. PR_StringToNetAddr("::ffff:192.168.1.50", &test_ip7);
  409. /* Should match the 192.168.1.0/24 subnet after normalization */
  410. assert_int_equal(haproxy_ip_matches_parsed(&test_ip7, parsed, count), 1);
  411. /* Cleanup */
  412. slapi_ch_free((void **)&parsed);
  413. }
  414. /* Test edge cases and error handling in parsing */
  415. void test_haproxy_parsing_edge_cases(void **state) {
  416. size_t count = 0;
  417. char errorbuf[1024];
  418. haproxy_trusted_entry_t *parsed;
  419. struct berval *empty[] = {NULL};
  420. struct berval bv_8 = {.bv_val = "10.0.0.0/8", .bv_len = 10};
  421. struct berval bv_16 = {.bv_val = "172.16.0.0/16", .bv_len = 13};
  422. struct berval bv_32 = {.bv_val = "203.0.113.1/32", .bv_len = 14};
  423. struct berval *prefixes[] = {&bv_8, &bv_16, &bv_32, NULL};
  424. PRNetAddr test_ip;
  425. (void)state; /* Unused */
  426. /* Test NULL input */
  427. parsed = haproxy_parse_trusted_ips(NULL, &count, errorbuf);
  428. assert_null(parsed);
  429. assert_int_equal(count, 0);
  430. /* Test empty array */
  431. parsed = haproxy_parse_trusted_ips(empty, &count, errorbuf);
  432. assert_null(parsed);
  433. assert_int_equal(count, 0);
  434. /* Test various CIDR prefix lengths */
  435. parsed = haproxy_parse_trusted_ips(prefixes, &count, errorbuf);
  436. assert_non_null(parsed);
  437. assert_int_equal(count, 3);
  438. /* /8 should match wide range */
  439. PR_StringToNetAddr("10.255.255.255", &test_ip);
  440. assert_int_equal(haproxy_ip_matches_parsed(&test_ip, parsed, count), 1);
  441. /* /16 should match within range */
  442. PR_StringToNetAddr("172.16.99.99", &test_ip);
  443. assert_int_equal(haproxy_ip_matches_parsed(&test_ip, parsed, count), 1);
  444. /* Outside /16 */
  445. PR_StringToNetAddr("172.17.0.1", &test_ip);
  446. assert_int_equal(haproxy_ip_matches_parsed(&test_ip, parsed, count), 0);
  447. /* /32 exact match only */
  448. PR_StringToNetAddr("203.0.113.1", &test_ip);
  449. assert_int_equal(haproxy_ip_matches_parsed(&test_ip, parsed, count), 1);
  450. PR_StringToNetAddr("203.0.113.2", &test_ip);
  451. assert_int_equal(haproxy_ip_matches_parsed(&test_ip, parsed, count), 0);
  452. slapi_ch_free((void **)&parsed);
  453. }
  454. /* Test netmask precomputation during parsing */
  455. void test_haproxy_netmask_precomputation(void **state) {
  456. struct berval bv1 = {.bv_val = "192.168.1.0/24", .bv_len = 14};
  457. struct berval *bvals[] = {&bv1, NULL};
  458. struct berval bv2 = {.bv_val = "2001:db8:abcd::/48", .bv_len = 18};
  459. struct berval *bvals2[] = {&bv2, NULL};
  460. struct berval bv3 = {.bv_val = "2001:db8::/33", .bv_len = 13};
  461. struct berval *bvals3[] = {&bv3, NULL};
  462. size_t count = 0;
  463. char errorbuf[1024];
  464. haproxy_trusted_entry_t *parsed;
  465. uint32_t expected_mask;
  466. int i;
  467. (void)state; /* Unused */
  468. /* IPv4 /24 netmask should be 0xFFFFFF00 (255.255.255.0) */
  469. parsed = haproxy_parse_trusted_ips(bvals, &count, errorbuf);
  470. assert_non_null(parsed);
  471. assert_int_equal(count, 1);
  472. /* Verify netmask was pre-computed */
  473. expected_mask = htonl(0xFFFFFF00);
  474. assert_int_equal(parsed[0].netmask.inet.ip, expected_mask);
  475. slapi_ch_free((void **)&parsed);
  476. /* IPv6 /48 netmask - first 6 bytes should be 0xFF, rest 0x00 */
  477. parsed = haproxy_parse_trusted_ips(bvals2, &count, errorbuf);
  478. assert_non_null(parsed);
  479. assert_int_equal(count, 1);
  480. /* Verify IPv6 netmask bytes */
  481. for (i = 0; i < 6; i++) {
  482. assert_int_equal(parsed[0].netmask.ipv6.ip.pr_s6_addr[i], 0xFF);
  483. }
  484. for (i = 6; i < 16; i++) {
  485. assert_int_equal(parsed[0].netmask.ipv6.ip.pr_s6_addr[i], 0x00);
  486. }
  487. slapi_ch_free((void **)&parsed);
  488. /* IPv6 /33 - partial byte mask (first 4 bytes + 1 bit) */
  489. parsed = haproxy_parse_trusted_ips(bvals3, &count, errorbuf);
  490. assert_non_null(parsed);
  491. /* First 4 bytes should be 0xFF */
  492. for (i = 0; i < 4; i++) {
  493. assert_int_equal(parsed[0].netmask.ipv6.ip.pr_s6_addr[i], 0xFF);
  494. }
  495. /* 5th byte should be 0x80 (10000000 binary - 1 bit set) */
  496. assert_int_equal(parsed[0].netmask.ipv6.ip.pr_s6_addr[4], 0x80);
  497. /* Remaining bytes should be 0x00 */
  498. for (i = 5; i < 16; i++) {
  499. assert_int_equal(parsed[0].netmask.ipv6.ip.pr_s6_addr[i], 0x00);
  500. }
  501. slapi_ch_free((void **)&parsed);
  502. }
  503. /* Test CIDR string matching */
  504. void test_haproxy_ip_matches_cidr(void **state) {
  505. (void)state; /* Unused */
  506. /* IPv4 subnet matching */
  507. assert_int_equal(haproxy_ip_matches_cidr("192.168.1.50", "192.168.1.0/24"), 1);
  508. assert_int_equal(haproxy_ip_matches_cidr("192.168.2.50", "192.168.1.0/24"), 0);
  509. /* IPv4 exact match (no CIDR) */
  510. assert_int_equal(haproxy_ip_matches_cidr("10.0.0.1", "10.0.0.1"), 1);
  511. assert_int_equal(haproxy_ip_matches_cidr("10.0.0.1", "10.0.0.2"), 0);
  512. /* IPv6 subnet matching */
  513. assert_int_equal(haproxy_ip_matches_cidr("2001:db8::1234", "2001:db8::/32"), 1);
  514. assert_int_equal(haproxy_ip_matches_cidr("2001:db9::1234", "2001:db8::/32"), 0);
  515. /* IPv6 exact match */
  516. assert_int_equal(haproxy_ip_matches_cidr("fe80::1", "fe80::1"), 1);
  517. assert_int_equal(haproxy_ip_matches_cidr("fe80::1", "fe80::2"), 0);
  518. /* Case insensitivity for IPv6 */
  519. assert_int_equal(haproxy_ip_matches_cidr("2001:DB8::1", "2001:db8::1"), 1);
  520. }
  521. /* Test IPv4 mask calculation edge cases and undefined behavior prevention */
  522. void test_haproxy_ipv4_mask_edge_cases(void **state) {
  523. PRNetAddr ip1, ip2, network;
  524. (void)state; /* Unused */
  525. /* Test prefix_len = 0 (should match everything without undefined behavior) */
  526. PR_StringToNetAddr("192.168.1.1", &ip1);
  527. PR_StringToNetAddr("10.0.0.0", &network);
  528. assert_int_equal(haproxy_ipv4_in_subnet(ip1.inet.ip, network.inet.ip, 0), 1);
  529. /* Test with completely different IPs - /0 should still match */
  530. PR_StringToNetAddr("255.255.255.255", &ip1);
  531. PR_StringToNetAddr("0.0.0.0", &network);
  532. assert_int_equal(haproxy_ipv4_in_subnet(ip1.inet.ip, network.inet.ip, 0), 1);
  533. /* Test prefix_len = 32 (exact match only) */
  534. PR_StringToNetAddr("192.168.1.1", &ip1);
  535. PR_StringToNetAddr("192.168.1.1", &network);
  536. assert_int_equal(haproxy_ipv4_in_subnet(ip1.inet.ip, network.inet.ip, 32), 1);
  537. /* Test prefix_len = 32 with different IPs */
  538. PR_StringToNetAddr("192.168.1.2", &ip2);
  539. assert_int_equal(haproxy_ipv4_in_subnet(ip2.inet.ip, network.inet.ip, 32), 0);
  540. /* Test prefix_len = 1 (half of all IPs) */
  541. PR_StringToNetAddr("127.255.255.255", &ip1);
  542. PR_StringToNetAddr("0.0.0.0", &network);
  543. assert_int_equal(haproxy_ipv4_in_subnet(ip1.inet.ip, network.inet.ip, 1), 1);
  544. PR_StringToNetAddr("128.0.0.0", &ip2);
  545. assert_int_equal(haproxy_ipv4_in_subnet(ip2.inet.ip, network.inet.ip, 1), 0);
  546. /* Test prefix_len = 31 (smallest non-trivial subnet) */
  547. PR_StringToNetAddr("192.168.1.0", &network);
  548. PR_StringToNetAddr("192.168.1.0", &ip1);
  549. PR_StringToNetAddr("192.168.1.1", &ip2);
  550. assert_int_equal(haproxy_ipv4_in_subnet(ip1.inet.ip, network.inet.ip, 31), 1);
  551. assert_int_equal(haproxy_ipv4_in_subnet(ip2.inet.ip, network.inet.ip, 31), 1);
  552. PR_StringToNetAddr("192.168.1.2", &ip1);
  553. assert_int_equal(haproxy_ipv4_in_subnet(ip1.inet.ip, network.inet.ip, 31), 0);
  554. /* Test invalid prefix lengths */
  555. PR_StringToNetAddr("192.168.1.1", &ip1);
  556. PR_StringToNetAddr("192.168.1.0", &network);
  557. assert_int_equal(haproxy_ipv4_in_subnet(ip1.inet.ip, network.inet.ip, -1), 0);
  558. assert_int_equal(haproxy_ipv4_in_subnet(ip1.inet.ip, network.inet.ip, 33), 0);
  559. assert_int_equal(haproxy_ipv4_in_subnet(ip1.inet.ip, network.inet.ip, 255), 0);
  560. /* Test various prefix lengths to ensure no undefined behavior */
  561. PR_StringToNetAddr("10.0.0.0", &network);
  562. for (int prefix = 0; prefix <= 32; prefix++) {
  563. PR_StringToNetAddr("10.255.255.255", &ip1);
  564. /* Should not crash or exhibit undefined behavior */
  565. int result = haproxy_ipv4_in_subnet(ip1.inet.ip, network.inet.ip, (int)prefix);
  566. /* For /8, 10.255.255.255 should match 10.0.0.0 */
  567. if (prefix <= 8) {
  568. assert_int_equal(result, 1);
  569. } else if (prefix == 32) {
  570. assert_int_equal(result, 0); /* Not exact match */
  571. }
  572. }
  573. }