mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-01-11 04:12:36 -03:00
test: add I2P test for a runaway SAM proxy
Add a regression test for https://github.com/bitcoin/bitcoin/pull/21407. The test creates a socket that, upon read, returns some data, but never the expected terminator `\n`, injects that socket into the I2P code and expects `i2p::sam::Session::Connect()` to fail, printing a specific error message to the log.
This commit is contained in:
parent
2d8ac77970
commit
40316a37cb
3 changed files with 114 additions and 0 deletions
|
@ -90,6 +90,7 @@ BITCOIN_TESTS =\
|
|||
test/fs_tests.cpp \
|
||||
test/getarg_tests.cpp \
|
||||
test/hash_tests.cpp \
|
||||
test/i2p_tests.cpp \
|
||||
test/interfaces_tests.cpp \
|
||||
test/key_io_tests.cpp \
|
||||
test/key_tests.cpp \
|
||||
|
|
44
src/test/i2p_tests.cpp
Normal file
44
src/test/i2p_tests.cpp
Normal file
|
@ -0,0 +1,44 @@
|
|||
// Copyright (c) 2021-2021 The Bitcoin Core developers
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
#include <i2p.h>
|
||||
#include <netaddress.h>
|
||||
#include <test/util/logging.h>
|
||||
#include <test/util/net.h>
|
||||
#include <test/util/setup_common.h>
|
||||
#include <threadinterrupt.h>
|
||||
#include <util/system.h>
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
BOOST_FIXTURE_TEST_SUITE(i2p_tests, BasicTestingSetup)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(unlimited_recv)
|
||||
{
|
||||
auto CreateSockOrig = CreateSock;
|
||||
|
||||
// Mock CreateSock() to create MockSock.
|
||||
CreateSock = [](const CService&) {
|
||||
return std::make_unique<StaticContentsSock>(std::string(i2p::sam::MAX_MSG_SIZE + 1, 'a'));
|
||||
};
|
||||
|
||||
CThreadInterrupt interrupt;
|
||||
i2p::sam::Session session(GetDataDir() / "test_i2p_private_key", CService{}, &interrupt);
|
||||
|
||||
{
|
||||
ASSERT_DEBUG_LOG("Creating SAM session");
|
||||
ASSERT_DEBUG_LOG("too many bytes without a terminator");
|
||||
|
||||
i2p::Connection conn;
|
||||
bool proxy_error;
|
||||
BOOST_REQUIRE(!session.Connect(CService{}, conn, proxy_error));
|
||||
}
|
||||
|
||||
CreateSock = CreateSockOrig;
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
|
@ -5,7 +5,13 @@
|
|||
#ifndef BITCOIN_TEST_UTIL_NET_H
|
||||
#define BITCOIN_TEST_UTIL_NET_H
|
||||
|
||||
#include <compat.h>
|
||||
#include <net.h>
|
||||
#include <util/sock.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
struct ConnmanTestMsg : public CConnman {
|
||||
using CConnman::CConnman;
|
||||
|
@ -61,4 +67,67 @@ constexpr ConnectionType ALL_CONNECTION_TYPES[]{
|
|||
ConnectionType::ADDR_FETCH,
|
||||
};
|
||||
|
||||
/**
|
||||
* A mocked Sock alternative that returns a statically contained data upon read and succeeds
|
||||
* and ignores all writes. The data to be returned is given to the constructor and when it is
|
||||
* exhausted an EOF is returned by further reads.
|
||||
*/
|
||||
class StaticContentsSock : public Sock
|
||||
{
|
||||
public:
|
||||
explicit StaticContentsSock(const std::string& contents) : m_contents{contents}, m_consumed{0}
|
||||
{
|
||||
// Just a dummy number that is not INVALID_SOCKET.
|
||||
static_assert(INVALID_SOCKET != 1000);
|
||||
m_socket = 1000;
|
||||
}
|
||||
|
||||
~StaticContentsSock() override { Reset(); }
|
||||
|
||||
StaticContentsSock& operator=(Sock&& other) override
|
||||
{
|
||||
assert(false && "Move of Sock into MockSock not allowed.");
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Reset() override
|
||||
{
|
||||
m_socket = INVALID_SOCKET;
|
||||
}
|
||||
|
||||
ssize_t Send(const void*, size_t len, int) const override { return len; }
|
||||
|
||||
ssize_t Recv(void* buf, size_t len, int flags) const override
|
||||
{
|
||||
const size_t consume_bytes{std::min(len, m_contents.size() - m_consumed)};
|
||||
std::memcpy(buf, m_contents.data() + m_consumed, consume_bytes);
|
||||
if ((flags & MSG_PEEK) == 0) {
|
||||
m_consumed += consume_bytes;
|
||||
}
|
||||
return consume_bytes;
|
||||
}
|
||||
|
||||
int Connect(const sockaddr*, socklen_t) const override { return 0; }
|
||||
|
||||
int GetSockOpt(int level, int opt_name, void* opt_val, socklen_t* opt_len) const override
|
||||
{
|
||||
std::memset(opt_val, 0x0, *opt_len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool Wait(std::chrono::milliseconds timeout,
|
||||
Event requested,
|
||||
Event* occurred = nullptr) const override
|
||||
{
|
||||
if (occurred != nullptr) {
|
||||
*occurred = requested;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
const std::string m_contents;
|
||||
mutable size_t m_consumed;
|
||||
};
|
||||
|
||||
#endif // BITCOIN_TEST_UTIL_NET_H
|
||||
|
|
Loading…
Reference in a new issue