1717#include < memory>
1818#include < string>
1919
20- BOOST_FIXTURE_TEST_SUITE (i2p_tests, BasicTestingSetup)
20+ // / Save the log level and the value of CreateSock and restore them when the test ends.
21+ class EnvTestingSetup : public BasicTestingSetup
22+ {
23+ public:
24+ explicit EnvTestingSetup (const std::string& chainType = CBaseChainParams::MAIN,
25+ const std::vector<const char *>& extra_args = {})
26+ : BasicTestingSetup{chainType, extra_args},
27+ m_prev_log_level{LogInstance ().LogLevel ()},
28+ m_create_sock_orig{CreateSock}
29+ {
30+ LogInstance ().SetLogLevel (BCLog::Level::Trace);
31+ }
32+
33+ ~EnvTestingSetup ()
34+ {
35+ CreateSock = m_create_sock_orig;
36+ LogInstance ().SetLogLevel (m_prev_log_level);
37+ }
38+
39+ private:
40+ const BCLog::Level m_prev_log_level;
41+ const std::function<std::unique_ptr<Sock>(const CService&)> m_create_sock_orig;
42+ };
43+
44+ BOOST_FIXTURE_TEST_SUITE (i2p_tests, EnvTestingSetup)
2145
2246BOOST_AUTO_TEST_CASE(unlimited_recv)
2347{
24- const auto prev_log_level{LogInstance ().LogLevel ()};
25- LogInstance ().SetLogLevel (BCLog::Level::Trace);
26- auto CreateSockOrig = CreateSock;
27-
2848 // Mock CreateSock() to create MockSock.
2949 CreateSock = [](const CService&) {
3050 return std::make_unique<StaticContentsSock>(std::string (i2p::sam::MAX_MSG_SIZE + 1 , ' a' ));
@@ -41,9 +61,69 @@ BOOST_AUTO_TEST_CASE(unlimited_recv)
4161 bool proxy_error;
4262 BOOST_REQUIRE (!session.Connect (CService{}, conn, proxy_error));
4363 }
64+ }
65+
66+ BOOST_AUTO_TEST_CASE (listen_ok_accept_fail)
67+ {
68+ size_t num_sockets{0 };
69+ CreateSock = [&num_sockets](const CService&) {
70+ // clang-format off
71+ ++num_sockets;
72+ // First socket is the control socket for creating the session.
73+ if (num_sockets == 1 ) {
74+ return std::make_unique<StaticContentsSock>(
75+ // reply to HELLO
76+ " HELLO REPLY RESULT=OK VERSION=3.1\n "
77+ // reply to DEST GENERATE
78+ "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"
79+ // reply to SESSION CREATE
80+ " SESSION STATUS RESULT=OK\n "
81+ // dummy to avoid reporting EOF on the socket
82+ " a"
83+ );
84+ }
85+ // Subsequent sockets are for recreating the session or for listening and accepting incoming connections.
86+ if (num_sockets % 2 == 0 ) {
87+ // Replies to Listen() and Accept()
88+ return std::make_unique<StaticContentsSock>(
89+ // reply to HELLO
90+ " HELLO REPLY RESULT=OK VERSION=3.1\n "
91+ // reply to STREAM ACCEPT
92+ " STREAM STATUS RESULT=OK\n "
93+ // continued reply to STREAM ACCEPT, violating the protocol described at
94+ // https://geti2p.net/en/docs/api/samv3#Accept%20Response
95+ // should be base64, something like
96+ // "IchV608baDoXbqzQKSqFDmTXPVgoDbPAhZJvNRXXxi4hyFXrTxtoOhdurNApKoUOZNc9WCgNs8CFkm81FdfGLiHIVetPG2g6F26s0CkqhQ5k1z1YKA2zwIWSbzUV18YuIchV608baDoXbqzQKSqFDmTXPVgoDbPAhZJvNRXXxi4hyFXrTxtoOhdurNApKoUOZNc9WCgNs8CFkm81FdfGLiHIVetPG2g6F26s0CkqhQ5k1z1YKA2zwIWSbzUV18YuIchV608baDoXbqzQKSqFDmTXPVgoDbPAhZJvNRXXxi4hyFXrTxtoOhdurNApKoUOZNc9WCgNs8CFkm81FdfGLiHIVetPG2g6F26s0CkqhQ5k1z1YKA2zwIWSbzUV18YuIchV608baDoXbqzQKSqFDmTXPVgoDbPAhZJvNRXXxi4hyFXrTxtoOhdurNApKoUOZNc9WCgNs8CFkm81FdfGLlSreVaCuCS5sdb-8ToWULWP7kt~lRPDeUNxQMq3cRSBBQAEAAcAAA==\n"
97+ " STREAM STATUS RESULT=I2P_ERROR MESSAGE=\" Session was closed\"\n "
98+ );
99+ } else {
100+ // Another control socket, but without creating a destination (it is cached in the session).
101+ return std::make_unique<StaticContentsSock>(
102+ // reply to HELLO
103+ " HELLO REPLY RESULT=OK VERSION=3.1\n "
104+ // reply to SESSION CREATE
105+ " SESSION STATUS RESULT=OK\n "
106+ // dummy to avoid reporting EOF on the socket
107+ " a"
108+ );
109+ }
110+ // clang-format on
111+ };
44112
45- CreateSock = CreateSockOrig;
46- LogInstance ().SetLogLevel (prev_log_level);
113+ CThreadInterrupt interrupt;
114+ i2p::sam::Session session (gArgs .GetDataDirNet () / " test_i2p_private_key" ,
115+ CService{in6_addr (IN6ADDR_LOOPBACK_INIT), /* port=*/ 7656 },
116+ &interrupt);
117+
118+ i2p::Connection conn;
119+ for (size_t i = 0 ; i < 5 ; ++i) {
120+ ASSERT_DEBUG_LOG (" Creating persistent SAM session" );
121+ ASSERT_DEBUG_LOG (" Persistent SAM session" /* ... created */ );
122+ ASSERT_DEBUG_LOG (" Error accepting" );
123+ ASSERT_DEBUG_LOG (" Destroying SAM session" );
124+ BOOST_REQUIRE (session.Listen (conn));
125+ BOOST_REQUIRE (!session.Accept (conn));
126+ }
47127}
48128
49129BOOST_AUTO_TEST_SUITE_END ()
0 commit comments