Branch data Line data Source code
1 : : // Copyright (c) 2021-present 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 <common/args.h>
6 : : #include <compat/compat.h>
7 : : #include <i2p.h>
8 : : #include <logging.h>
9 : : #include <netaddress.h>
10 : : #include <netbase.h>
11 : : #include <test/util/logging.h>
12 : : #include <test/util/net.h>
13 : : #include <test/util/setup_common.h>
14 : : #include <util/readwritefile.h>
15 : : #include <util/threadinterrupt.h>
16 : :
17 : : #include <boost/test/unit_test.hpp>
18 : :
19 : : #include <memory>
20 : : #include <string>
21 : :
22 : : /// Save the log level and the value of CreateSock and restore them when the test ends.
23 : : class EnvTestingSetup : public BasicTestingSetup
24 : : {
25 : : public:
26 : 3 : explicit EnvTestingSetup(const ChainType chainType = ChainType::MAIN,
27 : : TestOpts opts = {})
28 : 3 : : BasicTestingSetup{chainType, opts},
29 [ + - ]: 3 : m_prev_log_level{LogInstance().LogLevel()},
30 [ + - + - : 6 : m_create_sock_orig{CreateSock}
+ - ]
31 : : {
32 [ + - ]: 3 : LogInstance().SetLogLevel(BCLog::Level::Trace);
33 : 3 : }
34 : :
35 : 3 : ~EnvTestingSetup()
36 : 3 : {
37 : 3 : CreateSock = m_create_sock_orig;
38 : 3 : LogInstance().SetLogLevel(m_prev_log_level);
39 : 3 : }
40 : :
41 : : private:
42 : : const BCLog::Level m_prev_log_level;
43 : : const decltype(CreateSock) m_create_sock_orig;
44 : : };
45 : :
46 : : BOOST_FIXTURE_TEST_SUITE(i2p_tests, EnvTestingSetup)
47 : :
48 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(unlimited_recv)
+ - + - -
+ + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- ]
49 : : {
50 : 2 : CreateSock = [](int, int, int) {
51 [ + - ]: 2 : return std::make_unique<StaticContentsSock>(std::string(i2p::sam::MAX_MSG_SIZE + 1, 'a'));
52 : 1 : };
53 : :
54 : 1 : auto interrupt{std::make_shared<CThreadInterrupt>()};
55 [ + - + - : 2 : const std::optional<CService> addr{Lookup("127.0.0.1", 9000, false)};
+ - ]
56 [ + - ]: 1 : const Proxy sam_proxy(addr.value(), /*tor_stream_isolation=*/false);
57 [ + - + - : 5 : i2p::sam::Session session(gArgs.GetDataDirNet() / "test_i2p_private_key", sam_proxy, interrupt);
+ - + - +
- ]
58 : :
59 : 1 : {
60 [ + - + - ]: 2 : ASSERT_DEBUG_LOG("Creating persistent I2P SAM session");
61 [ + - + - ]: 2 : ASSERT_DEBUG_LOG("too many bytes without a terminator");
62 : :
63 [ + - ]: 1 : i2p::Connection conn;
64 : 1 : bool proxy_error;
65 [ + - + - : 2 : BOOST_REQUIRE(!session.Connect(CService{}, conn, proxy_error));
+ - + - ]
66 : 1 : }
67 [ + - ]: 3 : }
68 : :
69 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(listen_ok_accept_fail)
+ - + - -
+ + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- ]
70 : : {
71 : 1 : size_t num_sockets{0};
72 : 11 : CreateSock = [&num_sockets](int, int, int) {
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 : 1 : );
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 : 5 : );
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 : 4 : );
112 : : }
113 : : // clang-format on
114 : 1 : };
115 : :
116 : 1 : auto interrupt{std::make_shared<CThreadInterrupt>()};
117 [ + - ]: 1 : const CService addr{in6_addr(COMPAT_IN6ADDR_LOOPBACK_INIT), /*port=*/7656};
118 : 1 : const Proxy sam_proxy(addr, /*tor_stream_isolation=*/false);
119 [ + - + - : 4 : i2p::sam::Session session(gArgs.GetDataDirNet() / "test_i2p_private_key",
+ - ]
120 : : sam_proxy,
121 [ + - + - ]: 2 : interrupt);
122 : :
123 [ + - ]: 1 : i2p::Connection conn;
124 [ + + ]: 6 : for (size_t i = 0; i < 5; ++i) {
125 [ + - + - ]: 10 : ASSERT_DEBUG_LOG("Creating persistent I2P SAM session");
126 [ + - + - ]: 10 : ASSERT_DEBUG_LOG("Persistent I2P SAM session" /* ... created */);
127 [ + - + - ]: 10 : ASSERT_DEBUG_LOG("Error accepting");
128 [ + - + - ]: 10 : ASSERT_DEBUG_LOG("Destroying I2P SAM session");
129 [ + - + - : 10 : BOOST_REQUIRE(session.Listen(conn));
+ - + - ]
130 [ + - + - : 10 : BOOST_REQUIRE(!session.Accept(conn));
+ - ]
131 : 5 : }
132 [ + - ]: 3 : }
133 : :
134 [ + - + - : 7 : BOOST_AUTO_TEST_CASE(damaged_private_key)
+ - + - -
+ + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- + - + -
+ - + - +
- + - - +
+ - + - +
- + - + -
+ - - + +
- ]
135 : : {
136 : 6 : CreateSock = [](int, int, int) {
137 : 5 : return std::make_unique<StaticContentsSock>("HELLO REPLY RESULT=OK VERSION=3.1\n"
138 : 5 : "SESSION STATUS RESULT=OK DESTINATION=\n");
139 : 1 : };
140 : :
141 [ + - ]: 2 : const auto i2p_private_key_file = m_args.GetDataDirNet() / "test_i2p_private_key_damaged";
142 : :
143 [ + - ]: 6 : for (const auto& [file_contents, expected_error] : std::vector<std::tuple<std::string, std::string>>{
144 : : {"", "The private key is too short (0 < 387)"},
145 : :
146 : : {"abcd", "The private key is too short (4 < 387)"},
147 : :
148 [ + - ]: 1 : {std::string(386, '\0'), "The private key is too short (386 < 387)"},
149 : :
150 [ + - + - ]: 1 : {std::string(385, '\0') + '\0' + '\1',
151 : : "Certificate length (1) designates that the private key should be 388 bytes, but it is only "
152 : : "387 bytes"},
153 : :
154 [ + - + - : 2 : {std::string(385, '\0') + '\0' + '\5' + "abcd",
+ - ]
155 : : "Certificate length (5) designates that the private key should be 392 bytes, but it is only "
156 [ + + + - : 12 : "391 bytes"}}) {
+ + - - ]
157 [ + - + - : 10 : BOOST_REQUIRE(WriteBinaryFile(i2p_private_key_file, file_contents));
+ - + - ]
158 : :
159 [ + - ]: 5 : auto interrupt{std::make_shared<CThreadInterrupt>()};
160 [ + - ]: 5 : const CService addr{in6_addr(COMPAT_IN6ADDR_LOOPBACK_INIT), /*port=*/7656};
161 : 5 : const Proxy sam_proxy{addr, /*tor_stream_isolation=*/false};
162 [ + - + - ]: 10 : i2p::sam::Session session(i2p_private_key_file, sam_proxy, interrupt);
163 : :
164 : 5 : {
165 [ + - + - ]: 10 : ASSERT_DEBUG_LOG("Creating persistent I2P SAM session");
166 [ - + + - ]: 15 : ASSERT_DEBUG_LOG(expected_error);
167 : :
168 [ + - ]: 5 : i2p::Connection conn;
169 : 5 : bool proxy_error;
170 [ + - + - : 10 : BOOST_CHECK(!session.Connect(CService{}, conn, proxy_error));
+ - + - ]
171 : 5 : }
172 [ + - ]: 16 : }
173 [ + - + - : 7 : }
+ - + - +
- - + -
- ]
174 : :
175 : : BOOST_AUTO_TEST_SUITE_END()
|