#ifndef BOOST_TEST_DYN_LINK #define BOOST_TEST_DYN_LINK #endif #define BOOST_TEST_NO_MAIN #include #include "iputils.hh" #include "proxy-protocol.hh" using namespace boost; using std::string; BOOST_AUTO_TEST_SUITE(test_proxy_protocol_cc) #define BINARY(s) (std::string(s, sizeof(s) - 1)) #define PROXYMAGIC "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A" #define PROXYMAGICLEN sizeof(PROXYMAGIC)-1 static string proxymagic(PROXYMAGIC, PROXYMAGICLEN); BOOST_AUTO_TEST_CASE(test_roundtrip) { std::vector values; string proxyheader; bool ptcp = true; ComboAddress src("65.66.67.68:18762"); // 18762 = 0x494a = "IJ" ComboAddress dest("69.70.71.72:19276"); // 19276 = 0x4b4c = "KL" proxyheader = makeProxyHeader(ptcp, src, dest, values); BOOST_CHECK_EQUAL(proxyheader, BINARY( PROXYMAGIC "\x21" // version | command "\x11" // ipv4=0x10 | TCP=0x1 "\x00\x0c" // 4 bytes IPv4 * 2 + 2 port numbers = 8 + 2 * 2 =12 = 0xc "ABCD" // 65.66.67.68 "EFGH" // 69.70.71.72 "IJ" // src port "KL" // dst port )); bool proxy; bool ptcp2; ComboAddress src2, dest2; BOOST_CHECK_EQUAL(parseProxyHeader(proxyheader, proxy, src2, dest2, ptcp2, values), 28); BOOST_CHECK_EQUAL(proxy, true); BOOST_CHECK_EQUAL(ptcp2, true); BOOST_CHECK(src2 == src); BOOST_CHECK(dest2 == dest); } BOOST_AUTO_TEST_CASE(test_local_proxy_header) { auto payload = makeLocalProxyHeader(); BOOST_CHECK_EQUAL(payload, BINARY( PROXYMAGIC "\x20" // version | command "\x00" // protocol family and address are set to 0 "\x00\x00" // no content )); bool proxy; bool tcp = false; ComboAddress src, dest; std::vector values; BOOST_CHECK_EQUAL(parseProxyHeader(payload, proxy, src, dest, tcp, values), 16); BOOST_CHECK_EQUAL(proxy, false); BOOST_CHECK_EQUAL(tcp, false); BOOST_CHECK_EQUAL(values.size(), 0U); } BOOST_AUTO_TEST_CASE(test_tlv_values_content_len_signedness) { std::string largeValue; /* this value will make the content length parsing fail in case of signedness mistake */ largeValue.resize(65128, 'A'); const std::vector values = { { "foo", 0 }, { largeValue, 255 }}; const bool tcp = false; const ComboAddress src("[2001:db8::1]:0"); const ComboAddress dest("[::1]:65535"); const auto payload = makeProxyHeader(tcp, src, dest, values); bool proxy; bool tcp2; ComboAddress src2; ComboAddress dest2; std::vector parsedValues; BOOST_CHECK_EQUAL(parseProxyHeader(payload, proxy, src2, dest2, tcp2, parsedValues), 16 + 36 + 6 + 65131); BOOST_CHECK_EQUAL(proxy, true); BOOST_CHECK_EQUAL(tcp2, tcp); BOOST_CHECK(src2 == src); BOOST_CHECK(dest2 == dest); BOOST_REQUIRE_EQUAL(parsedValues.size(), values.size()); for (size_t idx = 0; idx < values.size(); idx++) { BOOST_CHECK_EQUAL(parsedValues.at(idx).type, values.at(idx).type); BOOST_CHECK_EQUAL(parsedValues.at(idx).content, values.at(idx).content); } } BOOST_AUTO_TEST_CASE(test_payload_too_large) { const bool tcp = false; const ComboAddress src("[2001:db8::1]:0"); const ComboAddress dest("[::1]:65535"); std::string largeValue; /* this value is larger than the maximum size for a TLV */ largeValue.resize(65536, 'A'); const std::vector values = {{ largeValue, 255 }}; BOOST_CHECK_THROW(makeProxyHeader(tcp, src, dest, values), std::runtime_error); } BOOST_AUTO_TEST_CASE(test_tlv_values_length_signedness) { std::string largeValue; /* this value will make the TLV length parsing fail in case of signedness mistake */ largeValue.resize(65000, 'A'); const std::vector values = { { "foo", 0 }, { largeValue, 255 }}; const bool tcp = false; const ComboAddress src("[2001:db8::1]:0"); const ComboAddress dest("[::1]:65535"); const auto payload = makeProxyHeader(tcp, src, dest, values); bool proxy; bool tcp2; ComboAddress src2; ComboAddress dest2; std::vector parsedValues; BOOST_CHECK_EQUAL(parseProxyHeader(payload, proxy, src2, dest2, tcp2, parsedValues), 16 + 36 + 6 + 65003); BOOST_CHECK_EQUAL(proxy, true); BOOST_CHECK_EQUAL(tcp2, tcp); BOOST_CHECK(src2 == src); BOOST_CHECK(dest2 == dest); BOOST_REQUIRE_EQUAL(parsedValues.size(), values.size()); for (size_t idx = 0; idx < values.size(); idx++) { BOOST_CHECK_EQUAL(parsedValues.at(idx).type, values.at(idx).type); BOOST_CHECK_EQUAL(parsedValues.at(idx).content, values.at(idx).content); } } BOOST_AUTO_TEST_CASE(test_parsing_invalid_headers) { const std::vector noValues; const bool tcp = false; const ComboAddress src("[2001:db8::1]:0"); const ComboAddress dest("[::1]:65535"); const auto payload = makeProxyHeader(tcp, src, dest, noValues); bool proxy; bool tcp2; ComboAddress src2; ComboAddress dest2; std::vector values; { /* just checking that everything works */ BOOST_CHECK_EQUAL(parseProxyHeader(payload, proxy, src2, dest2, tcp2, values), 52); BOOST_CHECK_EQUAL(proxy, true); BOOST_CHECK_EQUAL(tcp2, tcp); BOOST_CHECK(src2 == src); BOOST_CHECK(dest2 == dest); BOOST_CHECK_EQUAL(values.size(), 0U); } { /* too short (not even full header) */ std::string truncated = payload; truncated.resize(15); BOOST_CHECK_EQUAL(parseProxyHeader(truncated, proxy, src2, dest2, tcp2, values), -1); } { /* too short (missing address part) */ std::string truncated = payload; truncated.resize(/* full header */ 16 + /* two IPv6s + port */ 36 - /* truncation */ 1); BOOST_CHECK_EQUAL(parseProxyHeader(truncated, proxy, src2, dest2, tcp2, values), -1); } { /* too short (missing TLV) */ values = { { "foo", 0 }, { "bar", 255 }} ; const auto payloadWithValues = makeProxyHeader(tcp, src, dest, values); std::string truncated = payloadWithValues; truncated.resize(/* full header */ 16 + /* two IPv6s + port */ 36 + /* TLV 1 */ 6 + /* TLV 2 */ 6 - /* truncation */ 2); BOOST_CHECK_EQUAL(parseProxyHeader(truncated, proxy, src2, dest2, tcp2, values), -2); } { /* invalid magic */ std::string invalid = payload; invalid.at(4) = 42; BOOST_CHECK_EQUAL(parseProxyHeader(invalid, proxy, src2, dest2, tcp2, values), 0); } { /* invalid version */ std::string invalid = payload; invalid.at(12) = 0x10 | 0x01; BOOST_CHECK_EQUAL(parseProxyHeader(invalid, proxy, src2, dest2, tcp2, values), 0); } { /* invalid command */ std::string invalid = payload; invalid.at(12) = 0x20 | 0x02; BOOST_CHECK_EQUAL(parseProxyHeader(invalid, proxy, src2, dest2, tcp2, values), 0); } { /* invalid family */ std::string invalid = payload; invalid.at(13) = (0x04 << 4) | 0x01 /* STREAM */; BOOST_CHECK_EQUAL(parseProxyHeader(invalid, proxy, src2, dest2, tcp2, values), 0); } { /* invalid address */ std::string invalid = payload; invalid.at(13) = (0x02 /* AF_INET */ << 4) | 0x03; BOOST_CHECK_EQUAL(parseProxyHeader(invalid, proxy, src2, dest2, tcp2, values), 0); } { /* TLV advertised len gets out of bounds */ values = { { "foo", 0 }, { "bar", 255 }} ; const auto payloadWithValues = makeProxyHeader(tcp, src, dest, values); std::string invalid = payloadWithValues; /* full header (16) + two IPv6s + port (36) + TLV (6) TLV 2 (6) */ invalid.at(59) += 1; BOOST_CHECK_EQUAL(parseProxyHeader(invalid, proxy, src2, dest2, tcp2, values), 0); } } BOOST_AUTO_TEST_SUITE_END()