Line data Source code
1 : // Copyright (c) 2021-2021 The Bitcoin Core developers 2 : // Distributed under the MIT software license, see the accompanying 3 : // file COPYING or http://www.opensource.org/licenses/mit-license.php. 4 : 5 : #include <i2p.h> 6 : #include <logging.h> 7 : #include <netaddress.h> 8 : #include <netbase.h> 9 : #include <test/util/logging.h> 10 : #include <test/util/net.h> 11 : #include <test/util/setup_common.h> 12 : #include <util/readwritefile.h> 13 : #include <util/system.h> 14 : #include <util/threadinterrupt.h> 15 : 16 : #include <boost/test/unit_test.hpp> 17 : 18 : #include <memory> 19 : #include <string> 20 : 21 : /// Save the log level and the value of CreateSock and restore them when the test ends. 22 : class EnvTestingSetup : public BasicTestingSetup 23 : { 24 : public: 25 3 : explicit EnvTestingSetup(const std::string& chainType = CBaseChainParams::MAIN, 26 : const std::vector<const char*>& extra_args = {}) 27 3 : : BasicTestingSetup{chainType, extra_args}, 28 3 : m_prev_log_level{LogInstance().LogLevel()}, 29 3 : m_create_sock_orig{CreateSock} 30 : { 31 3 : LogInstance().SetLogLevel(BCLog::Level::Trace); 32 3 : } 33 : 34 3 : ~EnvTestingSetup() 35 : { 36 3 : CreateSock = m_create_sock_orig; 37 3 : LogInstance().SetLogLevel(m_prev_log_level); 38 3 : } 39 : 40 : private: 41 : const BCLog::Level m_prev_log_level; 42 : const std::function<std::unique_ptr<Sock>(const sa_family_t&)> m_create_sock_orig; 43 : }; 44 : 45 146 : BOOST_FIXTURE_TEST_SUITE(i2p_tests, EnvTestingSetup) 46 : 47 149 : BOOST_AUTO_TEST_CASE(unlimited_recv) 48 : { 49 : // Mock CreateSock() to create MockSock. 50 2 : CreateSock = [](const sa_family_t&) { 51 1 : return std::make_unique<StaticContentsSock>(std::string(i2p::sam::MAX_MSG_SIZE + 1, 'a')); 52 0 : }; 53 : 54 1 : CThreadInterrupt interrupt; 55 1 : const std::optional<CService> addr{Lookup("127.0.0.1", 9000, false)}; 56 1 : const Proxy sam_proxy(addr.value(), false); 57 1 : i2p::sam::Session session(gArgs.GetDataDirNet() / "test_i2p_private_key", sam_proxy, &interrupt); 58 : 59 : { 60 1 : ASSERT_DEBUG_LOG("Creating persistent SAM session"); 61 1 : ASSERT_DEBUG_LOG("too many bytes without a terminator"); 62 : 63 1 : i2p::Connection conn; 64 : bool proxy_error; 65 1 : BOOST_REQUIRE(!session.Connect(CService{}, conn, proxy_error)); 66 1 : } 67 1 : } 68 : 69 149 : BOOST_AUTO_TEST_CASE(listen_ok_accept_fail) 70 : { 71 1 : size_t num_sockets{0}; 72 11 : CreateSock = [&num_sockets](const sa_family_t&) { 73 : // clang-format off 74 10 : ++num_sockets; 75 : // First socket is the control socket for creating the session. 76 10 : if (num_sockets == 1) { 77 1 : return std::make_unique<StaticContentsSock>( 78 : // reply to HELLO 79 : "HELLO REPLY RESULT=OK VERSION=3.1\n" 80 : // reply to DEST GENERATE 81 : "DEST REPLY PUB=WnGOLXRBqHQhdVjFlWqRxJwz9hxx~2~wGc2Vplta1KhacY4tdEGodCF1WMWVapHEnDP2HHH~b~AZzZWmW1rUqFpxji10Qah0IXVYxZVqkcScM~Yccf9v8BnNlaZbWtSoWnGOLXRBqHQhdVjFlWqRxJwz9hxx~2~wGc2Vplta1KhacY4tdEGodCF1WMWVapHEnDP2HHH~b~AZzZWmW1rUqFpxji10Qah0IXVYxZVqkcScM~Yccf9v8BnNlaZbWtSoWnGOLXRBqHQhdVjFlWqRxJwz9hxx~2~wGc2Vplta1KhacY4tdEGodCF1WMWVapHEnDP2HHH~b~AZzZWmW1rUqFpxji10Qah0IXVYxZVqkcScM~Yccf9v8BnNlaZbWtSoWnGOLXRBqHQhdVjFlWqRxJwz9hxx~2~wGc2Vplta1KhacY4tdEGodCF1WMWVapHEnDP2HHH~b~AZzZWmW1rUqLE4SD-yjT48UNI7qiTUfIPiDitCoiTTz2cr4QGfw89rBQAEAAcAAA== PRIV=WnGOLXRBqHQhdVjFlWqRxJwz9hxx~2~wGc2Vplta1KhacY4tdEGodCF1WMWVapHEnDP2HHH~b~AZzZWmW1rUqFpxji10Qah0IXVYxZVqkcScM~Yccf9v8BnNlaZbWtSoWnGOLXRBqHQhdVjFlWqRxJwz9hxx~2~wGc2Vplta1KhacY4tdEGodCF1WMWVapHEnDP2HHH~b~AZzZWmW1rUqFpxji10Qah0IXVYxZVqkcScM~Yccf9v8BnNlaZbWtSoWnGOLXRBqHQhdVjFlWqRxJwz9hxx~2~wGc2Vplta1KhacY4tdEGodCF1WMWVapHEnDP2HHH~b~AZzZWmW1rUqFpxji10Qah0IXVYxZVqkcScM~Yccf9v8BnNlaZbWtSoWnGOLXRBqHQhdVjFlWqRxJwz9hxx~2~wGc2Vplta1KhacY4tdEGodCF1WMWVapHEnDP2HHH~b~AZzZWmW1rUqLE4SD-yjT48UNI7qiTUfIPiDitCoiTTz2cr4QGfw89rBQAEAAcAAOvuCIKTyv5f~1QgGq7XQl-IqBULTB5WzB3gw5yGPtd1p0AeoADrq1ccZggLPQ4ZLUsGK-HVw373rcTfvxrcuwenqVjiN4tbbYLWtP7xXGWj6fM6HyORhU63GphrjEePpMUHDHXd3o7pWGM-ieVVQSK~1MzF9P93pQWI3Do52EeNAayz4HbpPjNhVBzG1hUEFwznfPmUZBPuaOR4-uBm1NEWEuONlNOCctE4-U0Ukh94z-Qb55U5vXjR5G4apmBblr68t6Wm1TKlzpgFHzSqLryh3stWqrOKY1H0z9eZ2z1EkHFOpD5LyF6nf51e-lV7HLMl44TYzoEHK8RRVodtLcW9lacVdBpv~tOzlZERIiDziZODPETENZMz5oy9DQ7UUw==\n" 82 : // reply to SESSION CREATE 83 : "SESSION STATUS RESULT=OK\n" 84 : // dummy to avoid reporting EOF on the socket 85 : "a" 86 : ); 87 : } 88 : // Subsequent sockets are for recreating the session or for listening and accepting incoming connections. 89 9 : if (num_sockets % 2 == 0) { 90 : // Replies to Listen() and Accept() 91 5 : return std::make_unique<StaticContentsSock>( 92 : // reply to HELLO 93 : "HELLO REPLY RESULT=OK VERSION=3.1\n" 94 : // reply to STREAM ACCEPT 95 : "STREAM STATUS RESULT=OK\n" 96 : // continued reply to STREAM ACCEPT, violating the protocol described at 97 : // https://geti2p.net/en/docs/api/samv3#Accept%20Response 98 : // should be base64, something like 99 : // "IchV608baDoXbqzQKSqFDmTXPVgoDbPAhZJvNRXXxi4hyFXrTxtoOhdurNApKoUOZNc9WCgNs8CFkm81FdfGLiHIVetPG2g6F26s0CkqhQ5k1z1YKA2zwIWSbzUV18YuIchV608baDoXbqzQKSqFDmTXPVgoDbPAhZJvNRXXxi4hyFXrTxtoOhdurNApKoUOZNc9WCgNs8CFkm81FdfGLiHIVetPG2g6F26s0CkqhQ5k1z1YKA2zwIWSbzUV18YuIchV608baDoXbqzQKSqFDmTXPVgoDbPAhZJvNRXXxi4hyFXrTxtoOhdurNApKoUOZNc9WCgNs8CFkm81FdfGLiHIVetPG2g6F26s0CkqhQ5k1z1YKA2zwIWSbzUV18YuIchV608baDoXbqzQKSqFDmTXPVgoDbPAhZJvNRXXxi4hyFXrTxtoOhdurNApKoUOZNc9WCgNs8CFkm81FdfGLlSreVaCuCS5sdb-8ToWULWP7kt~lRPDeUNxQMq3cRSBBQAEAAcAAA==\n" 100 : "STREAM STATUS RESULT=I2P_ERROR MESSAGE=\"Session was closed\"\n" 101 : ); 102 : } else { 103 : // Another control socket, but without creating a destination (it is cached in the session). 104 4 : return std::make_unique<StaticContentsSock>( 105 : // reply to HELLO 106 : "HELLO REPLY RESULT=OK VERSION=3.1\n" 107 : // reply to SESSION CREATE 108 : "SESSION STATUS RESULT=OK\n" 109 : // dummy to avoid reporting EOF on the socket 110 : "a" 111 : ); 112 : } 113 : // clang-format on 114 10 : }; 115 : 116 1 : CThreadInterrupt interrupt; 117 1 : const CService addr{in6_addr(IN6ADDR_LOOPBACK_INIT), /*port=*/7656}; 118 1 : const Proxy sam_proxy(addr, false); 119 1 : i2p::sam::Session session(gArgs.GetDataDirNet() / "test_i2p_private_key", 120 : sam_proxy, 121 : &interrupt); 122 : 123 1 : i2p::Connection conn; 124 6 : for (size_t i = 0; i < 5; ++i) { 125 5 : ASSERT_DEBUG_LOG("Creating persistent SAM session"); 126 5 : ASSERT_DEBUG_LOG("Persistent SAM session" /* ... created */); 127 5 : ASSERT_DEBUG_LOG("Error accepting"); 128 5 : ASSERT_DEBUG_LOG("Destroying SAM session"); 129 5 : BOOST_REQUIRE(session.Listen(conn)); 130 5 : BOOST_REQUIRE(!session.Accept(conn)); 131 5 : } 132 1 : } 133 : 134 149 : BOOST_AUTO_TEST_CASE(damaged_private_key) 135 : { 136 1 : const auto CreateSockOrig = CreateSock; 137 : 138 6 : CreateSock = [](const sa_family_t&) { 139 5 : return std::make_unique<StaticContentsSock>("HELLO REPLY RESULT=OK VERSION=3.1\n" 140 : "SESSION STATUS RESULT=OK DESTINATION=\n"); 141 : }; 142 : 143 1 : const auto i2p_private_key_file = m_args.GetDataDirNet() / "test_i2p_private_key_damaged"; 144 : 145 19 : for (const auto& [file_contents, expected_error] : std::vector<std::tuple<std::string, std::string>>{ 146 1 : {"", "The private key is too short (0 < 387)"}, 147 : 148 1 : {"abcd", "The private key is too short (4 < 387)"}, 149 : 150 1 : {std::string(386, '\0'), "The private key is too short (386 < 387)"}, 151 : 152 1 : {std::string(385, '\0') + '\0' + '\1', 153 : "Certificate length (1) designates that the private key should be 388 bytes, but it is only " 154 : "387 bytes"}, 155 : 156 1 : {std::string(385, '\0') + '\0' + '\5' + "abcd", 157 : "Certificate length (5) designates that the private key should be 392 bytes, but it is only " 158 : "391 bytes"}}) { 159 5 : BOOST_REQUIRE(WriteBinaryFile(i2p_private_key_file, file_contents)); 160 : 161 5 : CThreadInterrupt interrupt; 162 5 : const CService addr{in6_addr(IN6ADDR_LOOPBACK_INIT), /*port=*/7656}; 163 5 : const Proxy sam_proxy{addr, false}; 164 5 : i2p::sam::Session session(i2p_private_key_file, sam_proxy, &interrupt); 165 : 166 : { 167 5 : ASSERT_DEBUG_LOG("Creating persistent SAM session"); 168 5 : ASSERT_DEBUG_LOG(expected_error); 169 : 170 5 : i2p::Connection conn; 171 : bool proxy_error; 172 5 : BOOST_CHECK(!session.Connect(CService{}, conn, proxy_error)); 173 5 : } 174 5 : } 175 : 176 1 : CreateSock = CreateSockOrig; 177 1 : } 178 : 179 146 : BOOST_AUTO_TEST_SUITE_END()