LCOV - code coverage report
Current view: top level - src/test - i2p_tests.cpp (source / functions) Hit Total Coverage
Test: total_coverage.info Lines: 73 74 98.6 %
Date: 2026-06-25 07:23:43 Functions: 29 29 100.0 %

          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()

Generated by: LCOV version 1.16