mirror of
https://github.com/dashpay/dash.git
synced 2024-12-25 20:12:57 +01:00
merge bitcoin#21387: Refactor sock to add I2P fuzz and unit tests
This commit is contained in:
parent
184bbf5b33
commit
08a6f3f764
@ -106,6 +106,7 @@ BITCOIN_TESTS =\
|
|||||||
test/getarg_tests.cpp \
|
test/getarg_tests.cpp \
|
||||||
test/governance_validators_tests.cpp \
|
test/governance_validators_tests.cpp \
|
||||||
test/hash_tests.cpp \
|
test/hash_tests.cpp \
|
||||||
|
test/i2p_tests.cpp \
|
||||||
test/interfaces_tests.cpp \
|
test/interfaces_tests.cpp \
|
||||||
test/key_io_tests.cpp \
|
test/key_io_tests.cpp \
|
||||||
test/key_tests.cpp \
|
test/key_tests.cpp \
|
||||||
@ -248,6 +249,7 @@ test_fuzz_fuzz_SOURCES = \
|
|||||||
test/fuzz/golomb_rice.cpp \
|
test/fuzz/golomb_rice.cpp \
|
||||||
test/fuzz/hex.cpp \
|
test/fuzz/hex.cpp \
|
||||||
test/fuzz/http_request.cpp \
|
test/fuzz/http_request.cpp \
|
||||||
|
test/fuzz/i2p.cpp \
|
||||||
test/fuzz/integer.cpp \
|
test/fuzz/integer.cpp \
|
||||||
test/fuzz/key.cpp \
|
test/fuzz/key.cpp \
|
||||||
test/fuzz/key_io.cpp \
|
test/fuzz/key_io.cpp \
|
||||||
|
42
src/i2p.cpp
42
src/i2p.cpp
@ -20,6 +20,7 @@
|
|||||||
#include <util/system.h>
|
#include <util/system.h>
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
|
#include <memory>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
@ -115,7 +116,8 @@ namespace sam {
|
|||||||
Session::Session(const fs::path& private_key_file,
|
Session::Session(const fs::path& private_key_file,
|
||||||
const CService& control_host,
|
const CService& control_host,
|
||||||
CThreadInterrupt* interrupt)
|
CThreadInterrupt* interrupt)
|
||||||
: m_private_key_file(private_key_file), m_control_host(control_host), m_interrupt(interrupt)
|
: m_private_key_file(private_key_file), m_control_host(control_host), m_interrupt(interrupt),
|
||||||
|
m_control_sock(std::make_unique<Sock>(INVALID_SOCKET))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,7 +147,7 @@ bool Session::Accept(Connection& conn)
|
|||||||
try {
|
try {
|
||||||
while (!*m_interrupt) {
|
while (!*m_interrupt) {
|
||||||
Sock::Event occurred;
|
Sock::Event occurred;
|
||||||
conn.sock.Wait(MAX_WAIT_FOR_IO, Sock::RECV, &occurred);
|
conn.sock->Wait(MAX_WAIT_FOR_IO, Sock::RECV, &occurred);
|
||||||
|
|
||||||
if ((occurred & Sock::RECV) == 0) {
|
if ((occurred & Sock::RECV) == 0) {
|
||||||
// Timeout, no incoming connections within MAX_WAIT_FOR_IO.
|
// Timeout, no incoming connections within MAX_WAIT_FOR_IO.
|
||||||
@ -153,7 +155,7 @@ bool Session::Accept(Connection& conn)
|
|||||||
}
|
}
|
||||||
|
|
||||||
const std::string& peer_dest =
|
const std::string& peer_dest =
|
||||||
conn.sock.RecvUntilTerminator('\n', MAX_WAIT_FOR_IO, *m_interrupt, MAX_MSG_SIZE);
|
conn.sock->RecvUntilTerminator('\n', MAX_WAIT_FOR_IO, *m_interrupt, MAX_MSG_SIZE);
|
||||||
|
|
||||||
conn.peer = CService(DestB64ToAddr(peer_dest), Params().GetDefaultPort());
|
conn.peer = CService(DestB64ToAddr(peer_dest), Params().GetDefaultPort());
|
||||||
|
|
||||||
@ -171,7 +173,7 @@ bool Session::Connect(const CService& to, Connection& conn, bool& proxy_error)
|
|||||||
proxy_error = true;
|
proxy_error = true;
|
||||||
|
|
||||||
std::string session_id;
|
std::string session_id;
|
||||||
Sock sock;
|
std::unique_ptr<Sock> sock;
|
||||||
conn.peer = to;
|
conn.peer = to;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -184,12 +186,12 @@ bool Session::Connect(const CService& to, Connection& conn, bool& proxy_error)
|
|||||||
}
|
}
|
||||||
|
|
||||||
const Reply& lookup_reply =
|
const Reply& lookup_reply =
|
||||||
SendRequestAndGetReply(sock, strprintf("NAMING LOOKUP NAME=%s", to.ToStringIP()));
|
SendRequestAndGetReply(*sock, strprintf("NAMING LOOKUP NAME=%s", to.ToStringIP()));
|
||||||
|
|
||||||
const std::string& dest = lookup_reply.Get("VALUE");
|
const std::string& dest = lookup_reply.Get("VALUE");
|
||||||
|
|
||||||
const Reply& connect_reply = SendRequestAndGetReply(
|
const Reply& connect_reply = SendRequestAndGetReply(
|
||||||
sock, strprintf("STREAM CONNECT ID=%s DESTINATION=%s SILENT=false", session_id, dest),
|
*sock, strprintf("STREAM CONNECT ID=%s DESTINATION=%s SILENT=false", session_id, dest),
|
||||||
false);
|
false);
|
||||||
|
|
||||||
const std::string& result = connect_reply.Get("RESULT");
|
const std::string& result = connect_reply.Get("RESULT");
|
||||||
@ -271,7 +273,7 @@ Session::Reply Session::SendRequestAndGetReply(const Sock& sock,
|
|||||||
return reply;
|
return reply;
|
||||||
}
|
}
|
||||||
|
|
||||||
Sock Session::Hello() const
|
std::unique_ptr<Sock> Session::Hello() const
|
||||||
{
|
{
|
||||||
auto sock = CreateSock(m_control_host);
|
auto sock = CreateSock(m_control_host);
|
||||||
|
|
||||||
@ -279,13 +281,13 @@ Sock Session::Hello() const
|
|||||||
throw std::runtime_error("Cannot create socket");
|
throw std::runtime_error("Cannot create socket");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ConnectSocketDirectly(m_control_host, sock->Get(), nConnectTimeout, true)) {
|
if (!ConnectSocketDirectly(m_control_host, *sock, nConnectTimeout, true)) {
|
||||||
throw std::runtime_error(strprintf("Cannot connect to %s", m_control_host.ToString()));
|
throw std::runtime_error(strprintf("Cannot connect to %s", m_control_host.ToString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
SendRequestAndGetReply(*sock, "HELLO VERSION MIN=3.1 MAX=3.1");
|
SendRequestAndGetReply(*sock, "HELLO VERSION MIN=3.1 MAX=3.1");
|
||||||
|
|
||||||
return std::move(*sock);
|
return sock;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Session::CheckControlSock()
|
void Session::CheckControlSock()
|
||||||
@ -293,7 +295,7 @@ void Session::CheckControlSock()
|
|||||||
LOCK(m_mutex);
|
LOCK(m_mutex);
|
||||||
|
|
||||||
std::string errmsg;
|
std::string errmsg;
|
||||||
if (!m_control_sock.IsConnected(errmsg)) {
|
if (!m_control_sock->IsConnected(errmsg)) {
|
||||||
Log("Control socket error: %s", errmsg);
|
Log("Control socket error: %s", errmsg);
|
||||||
Disconnect();
|
Disconnect();
|
||||||
}
|
}
|
||||||
@ -341,26 +343,26 @@ Binary Session::MyDestination() const
|
|||||||
void Session::CreateIfNotCreatedAlready()
|
void Session::CreateIfNotCreatedAlready()
|
||||||
{
|
{
|
||||||
std::string errmsg;
|
std::string errmsg;
|
||||||
if (m_control_sock.IsConnected(errmsg)) {
|
if (m_control_sock->IsConnected(errmsg)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Log("Creating SAM session with %s", m_control_host.ToString());
|
Log("Creating SAM session with %s", m_control_host.ToString());
|
||||||
|
|
||||||
Sock sock = Hello();
|
auto sock = Hello();
|
||||||
|
|
||||||
const auto& [read_ok, data] = ReadBinaryFile(m_private_key_file);
|
const auto& [read_ok, data] = ReadBinaryFile(m_private_key_file);
|
||||||
if (read_ok) {
|
if (read_ok) {
|
||||||
m_private_key.assign(data.begin(), data.end());
|
m_private_key.assign(data.begin(), data.end());
|
||||||
} else {
|
} else {
|
||||||
GenerateAndSavePrivateKey(sock);
|
GenerateAndSavePrivateKey(*sock);
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string& session_id = GetRandHash().GetHex().substr(0, 10); // full is an overkill, too verbose in the logs
|
const std::string& session_id = GetRandHash().GetHex().substr(0, 10); // full is an overkill, too verbose in the logs
|
||||||
const std::string& private_key_b64 = SwapBase64(EncodeBase64(m_private_key));
|
const std::string& private_key_b64 = SwapBase64(EncodeBase64(m_private_key));
|
||||||
|
|
||||||
SendRequestAndGetReply(sock, strprintf("SESSION CREATE STYLE=STREAM ID=%s DESTINATION=%s",
|
SendRequestAndGetReply(*sock, strprintf("SESSION CREATE STYLE=STREAM ID=%s DESTINATION=%s",
|
||||||
session_id, private_key_b64));
|
session_id, private_key_b64));
|
||||||
|
|
||||||
m_my_addr = CService(DestBinToAddr(MyDestination()), Params().GetDefaultPort());
|
m_my_addr = CService(DestBinToAddr(MyDestination()), Params().GetDefaultPort());
|
||||||
m_session_id = session_id;
|
m_session_id = session_id;
|
||||||
@ -370,12 +372,12 @@ void Session::CreateIfNotCreatedAlready()
|
|||||||
m_my_addr.ToString());
|
m_my_addr.ToString());
|
||||||
}
|
}
|
||||||
|
|
||||||
Sock Session::StreamAccept()
|
std::unique_ptr<Sock> Session::StreamAccept()
|
||||||
{
|
{
|
||||||
Sock sock = Hello();
|
auto sock = Hello();
|
||||||
|
|
||||||
const Reply& reply = SendRequestAndGetReply(
|
const Reply& reply = SendRequestAndGetReply(
|
||||||
sock, strprintf("STREAM ACCEPT ID=%s SILENT=false", m_session_id), false);
|
*sock, strprintf("STREAM ACCEPT ID=%s SILENT=false", m_session_id), false);
|
||||||
|
|
||||||
const std::string& result = reply.Get("RESULT");
|
const std::string& result = reply.Get("RESULT");
|
||||||
|
|
||||||
@ -393,14 +395,14 @@ Sock Session::StreamAccept()
|
|||||||
|
|
||||||
void Session::Disconnect()
|
void Session::Disconnect()
|
||||||
{
|
{
|
||||||
if (m_control_sock.Get() != INVALID_SOCKET) {
|
if (m_control_sock->Get() != INVALID_SOCKET) {
|
||||||
if (m_session_id.empty()) {
|
if (m_session_id.empty()) {
|
||||||
Log("Destroying incomplete session");
|
Log("Destroying incomplete session");
|
||||||
} else {
|
} else {
|
||||||
Log("Destroying session %s", m_session_id);
|
Log("Destroying session %s", m_session_id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
m_control_sock.Reset();
|
m_control_sock->Reset();
|
||||||
m_session_id.clear();
|
m_session_id.clear();
|
||||||
}
|
}
|
||||||
} // namespace sam
|
} // namespace sam
|
||||||
|
12
src/i2p.h
12
src/i2p.h
@ -12,6 +12,7 @@
|
|||||||
#include <threadinterrupt.h>
|
#include <threadinterrupt.h>
|
||||||
#include <util/sock.h>
|
#include <util/sock.h>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
@ -29,7 +30,7 @@ using Binary = std::vector<uint8_t>;
|
|||||||
*/
|
*/
|
||||||
struct Connection {
|
struct Connection {
|
||||||
/** Connected socket. */
|
/** Connected socket. */
|
||||||
Sock sock;
|
std::unique_ptr<Sock> sock;
|
||||||
|
|
||||||
/** Our I2P address. */
|
/** Our I2P address. */
|
||||||
CService me;
|
CService me;
|
||||||
@ -166,7 +167,7 @@ private:
|
|||||||
* @return a connected socket
|
* @return a connected socket
|
||||||
* @throws std::runtime_error if an error occurs
|
* @throws std::runtime_error if an error occurs
|
||||||
*/
|
*/
|
||||||
Sock Hello() const EXCLUSIVE_LOCKS_REQUIRED(m_mutex);
|
std::unique_ptr<Sock> Hello() const EXCLUSIVE_LOCKS_REQUIRED(m_mutex);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check the control socket for errors and possibly disconnect.
|
* Check the control socket for errors and possibly disconnect.
|
||||||
@ -204,10 +205,11 @@ private:
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Open a new connection to the SAM proxy and issue "STREAM ACCEPT" request using the existing
|
* Open a new connection to the SAM proxy and issue "STREAM ACCEPT" request using the existing
|
||||||
* session id. Return the idle socket that is waiting for a peer to connect to us.
|
* session id.
|
||||||
|
* @return the idle socket that is waiting for a peer to connect to us
|
||||||
* @throws std::runtime_error if an error occurs
|
* @throws std::runtime_error if an error occurs
|
||||||
*/
|
*/
|
||||||
Sock StreamAccept() EXCLUSIVE_LOCKS_REQUIRED(m_mutex);
|
std::unique_ptr<Sock> StreamAccept() EXCLUSIVE_LOCKS_REQUIRED(m_mutex);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Destroy the session, closing the internally used sockets.
|
* Destroy the session, closing the internally used sockets.
|
||||||
@ -248,7 +250,7 @@ private:
|
|||||||
* connections and make outgoing ones.
|
* connections and make outgoing ones.
|
||||||
* See https://geti2p.net/en/docs/api/samv3
|
* See https://geti2p.net/en/docs/api/samv3
|
||||||
*/
|
*/
|
||||||
Sock m_control_sock GUARDED_BY(m_mutex);
|
std::unique_ptr<Sock> m_control_sock GUARDED_BY(m_mutex);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Our .b32.i2p address.
|
* Our .b32.i2p address.
|
||||||
|
@ -119,7 +119,7 @@ void CActiveMasternodeManager::Init(const CBlockIndex* pindex)
|
|||||||
LogPrintf("CActiveMasternodeManager::Init -- ERROR: %s\n", strError);
|
LogPrintf("CActiveMasternodeManager::Init -- ERROR: %s\n", strError);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
bool fConnected = ConnectSocketDirectly(activeMasternodeInfo.service, sock->Get(), nConnectTimeout, true) && IsSelectableSocket(sock->Get());
|
bool fConnected = ConnectSocketDirectly(activeMasternodeInfo.service, *sock, nConnectTimeout, true) && IsSelectableSocket(sock->Get());
|
||||||
sock->Reset();
|
sock->Reset();
|
||||||
|
|
||||||
if (!fConnected && Params().RequireRoutableExternalIP()) {
|
if (!fConnected && Params().RequireRoutableExternalIP()) {
|
||||||
|
@ -468,7 +468,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
|
|||||||
i2p::Connection conn;
|
i2p::Connection conn;
|
||||||
if (m_i2p_sam_session->Connect(addrConnect, conn, proxyConnectionFailed)) {
|
if (m_i2p_sam_session->Connect(addrConnect, conn, proxyConnectionFailed)) {
|
||||||
connected = true;
|
connected = true;
|
||||||
sock = std::make_unique<Sock>(std::move(conn.sock));
|
sock = std::move(conn.sock);
|
||||||
addr_bind = CAddress{conn.me, NODE_NONE};
|
addr_bind = CAddress{conn.me, NODE_NONE};
|
||||||
}
|
}
|
||||||
} else if (GetProxy(addrConnect.GetNetwork(), proxy)) {
|
} else if (GetProxy(addrConnect.GetNetwork(), proxy)) {
|
||||||
@ -484,7 +484,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
|
|||||||
if (!sock) {
|
if (!sock) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
connected = ConnectSocketDirectly(addrConnect, sock->Get(), nConnectTimeout, manual_connection);
|
connected = ConnectSocketDirectly(addrConnect, *sock, nConnectTimeout, manual_connection);
|
||||||
}
|
}
|
||||||
if (!proxyConnectionFailed) {
|
if (!proxyConnectionFailed) {
|
||||||
// If a connection to the node was attempted, and failure (if any) is not caused by a problem connecting to
|
// If a connection to the node was attempted, and failure (if any) is not caused by a problem connecting to
|
||||||
@ -2854,7 +2854,7 @@ void CConnman::ThreadI2PAcceptIncoming()
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
CreateNodeFromAcceptedSocket(conn.sock.Release(), NetPermissionFlags::PF_NONE,
|
CreateNodeFromAcceptedSocket(conn.sock->Release(), NetPermissionFlags::PF_NONE,
|
||||||
CAddress{conn.me, NODE_NONE}, CAddress{conn.peer, NODE_NONE});
|
CAddress{conn.me, NODE_NONE}, CAddress{conn.peer, NODE_NONE});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -536,12 +536,12 @@ static void LogConnectFailure(bool manual_connection, const char* fmt, const Arg
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ConnectSocketDirectly(const CService &addrConnect, const SOCKET& hSocket, int nTimeout, bool manual_connection)
|
bool ConnectSocketDirectly(const CService &addrConnect, const Sock& sock, int nTimeout, bool manual_connection)
|
||||||
{
|
{
|
||||||
// Create a sockaddr from the specified service.
|
// Create a sockaddr from the specified service.
|
||||||
struct sockaddr_storage sockaddr;
|
struct sockaddr_storage sockaddr;
|
||||||
socklen_t len = sizeof(sockaddr);
|
socklen_t len = sizeof(sockaddr);
|
||||||
if (hSocket == INVALID_SOCKET) {
|
if (sock.Get() == INVALID_SOCKET) {
|
||||||
LogPrintf("Cannot connect to %s: invalid socket\n", addrConnect.ToString());
|
LogPrintf("Cannot connect to %s: invalid socket\n", addrConnect.ToString());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -551,8 +551,7 @@ bool ConnectSocketDirectly(const CService &addrConnect, const SOCKET& hSocket, i
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Connect to the addrConnect service on the hSocket socket.
|
// Connect to the addrConnect service on the hSocket socket.
|
||||||
if (connect(hSocket, (struct sockaddr*)&sockaddr, len) == SOCKET_ERROR)
|
if (sock.Connect(reinterpret_cast<struct sockaddr*>(&sockaddr), len) == SOCKET_ERROR) {
|
||||||
{
|
|
||||||
int nErr = WSAGetLastError();
|
int nErr = WSAGetLastError();
|
||||||
// WSAEINVAL is here because some legacy version of winsock uses it
|
// WSAEINVAL is here because some legacy version of winsock uses it
|
||||||
if (nErr == WSAEINPROGRESS || nErr == WSAEWOULDBLOCK || nErr == WSAEINVAL)
|
if (nErr == WSAEINPROGRESS || nErr == WSAEWOULDBLOCK || nErr == WSAEINVAL)
|
||||||
@ -560,46 +559,34 @@ bool ConnectSocketDirectly(const CService &addrConnect, const SOCKET& hSocket, i
|
|||||||
// Connection didn't actually fail, but is being established
|
// Connection didn't actually fail, but is being established
|
||||||
// asynchronously. Thus, use async I/O api (select/poll)
|
// asynchronously. Thus, use async I/O api (select/poll)
|
||||||
// synchronously to check for successful connection with a timeout.
|
// synchronously to check for successful connection with a timeout.
|
||||||
#ifdef USE_POLL
|
const Sock::Event requested = Sock::RECV | Sock::SEND;
|
||||||
struct pollfd pollfd = {};
|
Sock::Event occurred;
|
||||||
pollfd.fd = hSocket;
|
if (!sock.Wait(std::chrono::milliseconds{nTimeout}, requested, &occurred)) {
|
||||||
pollfd.events = POLLIN | POLLOUT;
|
LogPrintf("wait for connect to %s failed: %s\n",
|
||||||
int nRet = poll(&pollfd, 1, nTimeout);
|
addrConnect.ToString(),
|
||||||
#else
|
NetworkErrorString(WSAGetLastError()));
|
||||||
struct timeval timeout = MillisToTimeval(nTimeout);
|
|
||||||
fd_set fdset;
|
|
||||||
FD_ZERO(&fdset);
|
|
||||||
FD_SET(hSocket, &fdset);
|
|
||||||
int nRet = select(hSocket + 1, nullptr, &fdset, nullptr, &timeout);
|
|
||||||
#endif
|
|
||||||
// Upon successful completion, both select and poll return the total
|
|
||||||
// number of file descriptors that have been selected. A value of 0
|
|
||||||
// indicates that the call timed out and no file descriptors have
|
|
||||||
// been selected.
|
|
||||||
if (nRet == 0)
|
|
||||||
{
|
|
||||||
LogPrint(BCLog::NET, "connection to %s timeout\n", addrConnect.ToString());
|
|
||||||
return false;
|
return false;
|
||||||
}
|
} else if (occurred == 0) {
|
||||||
if (nRet == SOCKET_ERROR)
|
LogPrint(BCLog::NET, "connection attempt to %s timed out\n", addrConnect.ToString());
|
||||||
{
|
|
||||||
LogPrintf("select() for %s failed: %s\n", addrConnect.ToString(), NetworkErrorString(WSAGetLastError()));
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Even if the select/poll was successful, the connect might not
|
// Even if the wait was successful, the connect might not
|
||||||
// have been successful. The reason for this failure is hidden away
|
// have been successful. The reason for this failure is hidden away
|
||||||
// in the SO_ERROR for the socket in modern systems. We read it into
|
// in the SO_ERROR for the socket in modern systems. We read it into
|
||||||
// nRet here.
|
// sockerr here.
|
||||||
socklen_t nRetSize = sizeof(nRet);
|
int sockerr;
|
||||||
if (getsockopt(hSocket, SOL_SOCKET, SO_ERROR, (sockopt_arg_type)&nRet, &nRetSize) == SOCKET_ERROR)
|
socklen_t sockerr_len = sizeof(sockerr);
|
||||||
{
|
if (sock.GetSockOpt(SOL_SOCKET, SO_ERROR, (sockopt_arg_type)&sockerr, &sockerr_len) ==
|
||||||
|
SOCKET_ERROR) {
|
||||||
LogPrintf("getsockopt() for %s failed: %s\n", addrConnect.ToString(), NetworkErrorString(WSAGetLastError()));
|
LogPrintf("getsockopt() for %s failed: %s\n", addrConnect.ToString(), NetworkErrorString(WSAGetLastError()));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (nRet != 0)
|
if (sockerr != 0) {
|
||||||
{
|
LogConnectFailure(manual_connection,
|
||||||
LogConnectFailure(manual_connection, "connect() to %s failed after select(): %s", addrConnect.ToString(), NetworkErrorString(nRet));
|
"connect() to %s failed after wait: %s",
|
||||||
|
addrConnect.ToString(),
|
||||||
|
NetworkErrorString(sockerr));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -667,7 +654,7 @@ bool IsProxy(const CNetAddr &addr) {
|
|||||||
bool ConnectThroughProxy(const proxyType& proxy, const std::string& strDest, uint16_t port, const Sock& sock, int nTimeout, bool& outProxyConnectionFailed)
|
bool ConnectThroughProxy(const proxyType& proxy, const std::string& strDest, uint16_t port, const Sock& sock, int nTimeout, bool& outProxyConnectionFailed)
|
||||||
{
|
{
|
||||||
// first connect to proxy server
|
// first connect to proxy server
|
||||||
if (!ConnectSocketDirectly(proxy.proxy, sock.Get(), nTimeout, true)) {
|
if (!ConnectSocketDirectly(proxy.proxy, sock, nTimeout, true)) {
|
||||||
outProxyConnectionFailed = true;
|
outProxyConnectionFailed = true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -178,7 +178,7 @@ extern std::function<std::unique_ptr<Sock>(const CService&)> CreateSock;
|
|||||||
* Try to connect to the specified service on the specified socket.
|
* Try to connect to the specified service on the specified socket.
|
||||||
*
|
*
|
||||||
* @param addrConnect The service to which to connect.
|
* @param addrConnect The service to which to connect.
|
||||||
* @param hSocket The socket on which to connect.
|
* @param sock The socket on which to connect.
|
||||||
* @param nTimeout Wait this many milliseconds for the connection to be
|
* @param nTimeout Wait this many milliseconds for the connection to be
|
||||||
* established.
|
* established.
|
||||||
* @param manual_connection Whether or not the connection was manually requested
|
* @param manual_connection Whether or not the connection was manually requested
|
||||||
@ -186,7 +186,7 @@ extern std::function<std::unique_ptr<Sock>(const CService&)> CreateSock;
|
|||||||
*
|
*
|
||||||
* @returns Whether or not a connection was successfully made.
|
* @returns Whether or not a connection was successfully made.
|
||||||
*/
|
*/
|
||||||
bool ConnectSocketDirectly(const CService &addrConnect, const SOCKET& hSocket, int nTimeout, bool manual_connection);
|
bool ConnectSocketDirectly(const CService &addrConnect, const Sock& sock, int nTimeout, bool manual_connection);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Connect to a specified destination service through a SOCKS5 proxy by first
|
* Connect to a specified destination service through a SOCKS5 proxy by first
|
||||||
|
57
src/test/fuzz/i2p.cpp
Normal file
57
src/test/fuzz/i2p.cpp
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
// Copyright (c) 2020-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 <netbase.h>
|
||||||
|
#include <test/fuzz/FuzzedDataProvider.h>
|
||||||
|
#include <test/fuzz/fuzz.h>
|
||||||
|
#include <test/fuzz/util.h>
|
||||||
|
#include <test/util/setup_common.h>
|
||||||
|
#include <threadinterrupt.h>
|
||||||
|
#include <util/system.h>
|
||||||
|
|
||||||
|
void initialize_i2p()
|
||||||
|
{
|
||||||
|
InitializeFuzzingContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
FUZZ_TARGET_INIT(i2p, initialize_i2p)
|
||||||
|
{
|
||||||
|
FuzzedDataProvider fuzzed_data_provider{buffer.data(), buffer.size()};
|
||||||
|
|
||||||
|
// Mock CreateSock() to create FuzzedSock.
|
||||||
|
auto CreateSockOrig = CreateSock;
|
||||||
|
CreateSock = [&fuzzed_data_provider](const CService&) {
|
||||||
|
return std::make_unique<FuzzedSock>(fuzzed_data_provider);
|
||||||
|
};
|
||||||
|
|
||||||
|
const CService sam_proxy;
|
||||||
|
CThreadInterrupt interrupt;
|
||||||
|
|
||||||
|
i2p::sam::Session sess{GetDataDir() / "fuzzed_i2p_private_key", sam_proxy, &interrupt};
|
||||||
|
|
||||||
|
i2p::Connection conn;
|
||||||
|
|
||||||
|
if (sess.Listen(conn)) {
|
||||||
|
if (sess.Accept(conn)) {
|
||||||
|
try {
|
||||||
|
conn.sock->RecvUntilTerminator('\n', 10ms, interrupt, i2p::sam::MAX_MSG_SIZE);
|
||||||
|
} catch (const std::runtime_error&) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const CService to;
|
||||||
|
bool proxy_error;
|
||||||
|
|
||||||
|
if (sess.Connect(to, conn, proxy_error)) {
|
||||||
|
try {
|
||||||
|
conn.sock->SendComplete("verack\n", 10ms, interrupt);
|
||||||
|
} catch (const std::runtime_error&) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CreateSock = CreateSockOrig;
|
||||||
|
}
|
@ -526,36 +526,37 @@ class FuzzedSock : public Sock
|
|||||||
{
|
{
|
||||||
FuzzedDataProvider& m_fuzzed_data_provider;
|
FuzzedDataProvider& m_fuzzed_data_provider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data to return when `MSG_PEEK` is used as a `Recv()` flag.
|
||||||
|
* If `MSG_PEEK` is used, then our `Recv()` returns some random data as usual, but on the next
|
||||||
|
* `Recv()` call we must return the same data, thus we remember it here.
|
||||||
|
*/
|
||||||
|
mutable std::optional<uint8_t> m_peek_data;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit FuzzedSock(FuzzedDataProvider& fuzzed_data_provider) : m_fuzzed_data_provider{fuzzed_data_provider}
|
explicit FuzzedSock(FuzzedDataProvider& fuzzed_data_provider) : m_fuzzed_data_provider{fuzzed_data_provider}
|
||||||
{
|
{
|
||||||
|
m_socket = fuzzed_data_provider.ConsumeIntegral<SOCKET>();
|
||||||
}
|
}
|
||||||
|
|
||||||
~FuzzedSock() override
|
~FuzzedSock() override
|
||||||
{
|
{
|
||||||
|
// Sock::~Sock() will be called after FuzzedSock::~FuzzedSock() and it will call
|
||||||
|
// Sock::Reset() (not FuzzedSock::Reset()!) which will call CloseSocket(m_socket).
|
||||||
|
// Avoid closing an arbitrary file descriptor (m_socket is just a random number which
|
||||||
|
// may concide with a real opened file descriptor).
|
||||||
|
Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
FuzzedSock& operator=(Sock&& other) override
|
FuzzedSock& operator=(Sock&& other) override
|
||||||
{
|
{
|
||||||
assert(false && "Not implemented yet.");
|
assert(false && "Move of Sock into FuzzedSock not allowed.");
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
SOCKET Get() const override
|
|
||||||
{
|
|
||||||
assert(false && "Not implemented yet.");
|
|
||||||
return INVALID_SOCKET;
|
|
||||||
}
|
|
||||||
|
|
||||||
SOCKET Release() override
|
|
||||||
{
|
|
||||||
assert(false && "Not implemented yet.");
|
|
||||||
return INVALID_SOCKET;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Reset() override
|
void Reset() override
|
||||||
{
|
{
|
||||||
assert(false && "Not implemented yet.");
|
m_socket = INVALID_SOCKET;
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t Send(const void* data, size_t len, int flags) const override
|
ssize_t Send(const void* data, size_t len, int flags) const override
|
||||||
@ -592,10 +593,13 @@ public:
|
|||||||
|
|
||||||
ssize_t Recv(void* buf, size_t len, int flags) const override
|
ssize_t Recv(void* buf, size_t len, int flags) const override
|
||||||
{
|
{
|
||||||
|
// Have a permanent error at recv_errnos[0] because when the fuzzed data is exhausted
|
||||||
|
// SetFuzzedErrNo() will always return the first element and we want to avoid Recv()
|
||||||
|
// returning -1 and setting errno to EAGAIN repeatedly.
|
||||||
constexpr std::array recv_errnos{
|
constexpr std::array recv_errnos{
|
||||||
|
ECONNREFUSED,
|
||||||
EAGAIN,
|
EAGAIN,
|
||||||
EBADF,
|
EBADF,
|
||||||
ECONNREFUSED,
|
|
||||||
EFAULT,
|
EFAULT,
|
||||||
EINTR,
|
EINTR,
|
||||||
EINVAL,
|
EINVAL,
|
||||||
@ -612,8 +616,26 @@ public:
|
|||||||
}
|
}
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
const std::vector<uint8_t> random_bytes = m_fuzzed_data_provider.ConsumeBytes<uint8_t>(
|
std::vector<uint8_t> random_bytes;
|
||||||
m_fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, len));
|
bool pad_to_len_bytes{m_fuzzed_data_provider.ConsumeBool()};
|
||||||
|
if (m_peek_data.has_value()) {
|
||||||
|
// `MSG_PEEK` was used in the preceding `Recv()` call, return `m_peek_data`.
|
||||||
|
random_bytes.assign({m_peek_data.value()});
|
||||||
|
if ((flags & MSG_PEEK) == 0) {
|
||||||
|
m_peek_data.reset();
|
||||||
|
}
|
||||||
|
pad_to_len_bytes = false;
|
||||||
|
} else if ((flags & MSG_PEEK) != 0) {
|
||||||
|
// New call with `MSG_PEEK`.
|
||||||
|
random_bytes = m_fuzzed_data_provider.ConsumeBytes<uint8_t>(1);
|
||||||
|
if (!random_bytes.empty()) {
|
||||||
|
m_peek_data = random_bytes[0];
|
||||||
|
pad_to_len_bytes = false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
random_bytes = m_fuzzed_data_provider.ConsumeBytes<uint8_t>(
|
||||||
|
m_fuzzed_data_provider.ConsumeIntegralInRange<size_t>(0, len));
|
||||||
|
}
|
||||||
if (random_bytes.empty()) {
|
if (random_bytes.empty()) {
|
||||||
const ssize_t r = m_fuzzed_data_provider.ConsumeBool() ? 0 : -1;
|
const ssize_t r = m_fuzzed_data_provider.ConsumeBool() ? 0 : -1;
|
||||||
if (r == -1) {
|
if (r == -1) {
|
||||||
@ -622,7 +644,7 @@ public:
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
std::memcpy(buf, random_bytes.data(), random_bytes.size());
|
std::memcpy(buf, random_bytes.data(), random_bytes.size());
|
||||||
if (m_fuzzed_data_provider.ConsumeBool()) {
|
if (pad_to_len_bytes) {
|
||||||
if (len > random_bytes.size()) {
|
if (len > random_bytes.size()) {
|
||||||
std::memset((char*)buf + random_bytes.size(), 0, len - random_bytes.size());
|
std::memset((char*)buf + random_bytes.size(), 0, len - random_bytes.size());
|
||||||
}
|
}
|
||||||
@ -634,10 +656,59 @@ public:
|
|||||||
return random_bytes.size();
|
return random_bytes.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int Connect(const sockaddr*, socklen_t) const override
|
||||||
|
{
|
||||||
|
// Have a permanent error at connect_errnos[0] because when the fuzzed data is exhausted
|
||||||
|
// SetFuzzedErrNo() will always return the first element and we want to avoid Connect()
|
||||||
|
// returning -1 and setting errno to EAGAIN repeatedly.
|
||||||
|
constexpr std::array connect_errnos{
|
||||||
|
ECONNREFUSED,
|
||||||
|
EAGAIN,
|
||||||
|
ECONNRESET,
|
||||||
|
EHOSTUNREACH,
|
||||||
|
EINPROGRESS,
|
||||||
|
EINTR,
|
||||||
|
ENETUNREACH,
|
||||||
|
ETIMEDOUT,
|
||||||
|
};
|
||||||
|
if (m_fuzzed_data_provider.ConsumeBool()) {
|
||||||
|
SetFuzzedErrNo(m_fuzzed_data_provider, connect_errnos);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetSockOpt(int level, int opt_name, void* opt_val, socklen_t* opt_len) const override
|
||||||
|
{
|
||||||
|
constexpr std::array getsockopt_errnos{
|
||||||
|
ENOMEM,
|
||||||
|
ENOBUFS,
|
||||||
|
};
|
||||||
|
if (m_fuzzed_data_provider.ConsumeBool()) {
|
||||||
|
SetFuzzedErrNo(m_fuzzed_data_provider, getsockopt_errnos);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (opt_val == nullptr) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
std::memcpy(opt_val,
|
||||||
|
ConsumeFixedLengthByteVector(m_fuzzed_data_provider, *opt_len).data(),
|
||||||
|
*opt_len);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
bool Wait(std::chrono::milliseconds timeout, Event requested, Event* occurred = nullptr) const override
|
bool Wait(std::chrono::milliseconds timeout, Event requested, Event* occurred = nullptr) const override
|
||||||
{
|
{
|
||||||
return m_fuzzed_data_provider.ConsumeBool();
|
return m_fuzzed_data_provider.ConsumeBool();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool IsConnected(std::string& errmsg) const override {
|
||||||
|
if (m_fuzzed_data_provider.ConsumeBool()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
errmsg = "disconnected at random by the fuzzer";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
[[nodiscard]] inline FuzzedSock ConsumeSock(FuzzedDataProvider& fuzzed_data_provider)
|
[[nodiscard]] inline FuzzedSock ConsumeSock(FuzzedDataProvider& fuzzed_data_provider)
|
||||||
|
45
src/test/i2p_tests.cpp
Normal file
45
src/test/i2p_tests.cpp
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
// 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 <netbase.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
|
#ifndef BITCOIN_TEST_UTIL_NET_H
|
||||||
#define BITCOIN_TEST_UTIL_NET_H
|
#define BITCOIN_TEST_UTIL_NET_H
|
||||||
|
|
||||||
|
#include <compat.h>
|
||||||
#include <net.h>
|
#include <net.h>
|
||||||
|
#include <util/sock.h>
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstring>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
struct ConnmanTestMsg : public CConnman {
|
struct ConnmanTestMsg : public CConnman {
|
||||||
using CConnman::CConnman;
|
using CConnman::CConnman;
|
||||||
@ -53,4 +59,67 @@ constexpr NetPermissionFlags ALL_NET_PERMISSION_FLAGS[]{
|
|||||||
NetPermissionFlags::PF_ALL,
|
NetPermissionFlags::PF_ALL,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
#endif // BITCOIN_TEST_UTIL_NET_H
|
||||||
|
@ -66,6 +66,16 @@ ssize_t Sock::Recv(void* buf, size_t len, int flags) const
|
|||||||
return recv(m_socket, static_cast<char*>(buf), len, flags);
|
return recv(m_socket, static_cast<char*>(buf), len, flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int Sock::Connect(const sockaddr* addr, socklen_t addr_len) const
|
||||||
|
{
|
||||||
|
return connect(m_socket, addr, addr_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
int Sock::GetSockOpt(int level, int opt_name, void* opt_val, socklen_t* opt_len) const
|
||||||
|
{
|
||||||
|
return getsockopt(m_socket, level, opt_name, static_cast<char*>(opt_val), opt_len);
|
||||||
|
}
|
||||||
|
|
||||||
bool Sock::Wait(std::chrono::milliseconds timeout, Event requested, Event* occurred) const
|
bool Sock::Wait(std::chrono::milliseconds timeout, Event requested, Event* occurred) const
|
||||||
{
|
{
|
||||||
#ifdef USE_POLL
|
#ifdef USE_POLL
|
||||||
|
@ -80,16 +80,29 @@ public:
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* send(2) wrapper. Equivalent to `send(this->Get(), data, len, flags);`. Code that uses this
|
* send(2) wrapper. Equivalent to `send(this->Get(), data, len, flags);`. Code that uses this
|
||||||
* wrapper can be unit-tested if this method is overridden by a mock Sock implementation.
|
* wrapper can be unit tested if this method is overridden by a mock Sock implementation.
|
||||||
*/
|
*/
|
||||||
virtual ssize_t Send(const void* data, size_t len, int flags) const;
|
virtual ssize_t Send(const void* data, size_t len, int flags) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* recv(2) wrapper. Equivalent to `recv(this->Get(), buf, len, flags);`. Code that uses this
|
* recv(2) wrapper. Equivalent to `recv(this->Get(), buf, len, flags);`. Code that uses this
|
||||||
* wrapper can be unit-tested if this method is overridden by a mock Sock implementation.
|
* wrapper can be unit tested if this method is overridden by a mock Sock implementation.
|
||||||
*/
|
*/
|
||||||
virtual ssize_t Recv(void* buf, size_t len, int flags) const;
|
virtual ssize_t Recv(void* buf, size_t len, int flags) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* connect(2) wrapper. Equivalent to `connect(this->Get(), addr, addrlen)`. Code that uses this
|
||||||
|
* wrapper can be unit tested if this method is overridden by a mock Sock implementation.
|
||||||
|
*/
|
||||||
|
virtual int Connect(const sockaddr* addr, socklen_t addr_len) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* getsockopt(2) wrapper. Equivalent to
|
||||||
|
* `getsockopt(this->Get(), level, opt_name, opt_val, opt_len)`. Code that uses this
|
||||||
|
* wrapper can be unit tested if this method is overridden by a mock Sock implementation.
|
||||||
|
*/
|
||||||
|
virtual int GetSockOpt(int level, int opt_name, void* opt_val, socklen_t* opt_len) const;
|
||||||
|
|
||||||
using Event = uint8_t;
|
using Event = uint8_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -153,7 +166,7 @@ public:
|
|||||||
*/
|
*/
|
||||||
virtual bool IsConnected(std::string& errmsg) const;
|
virtual bool IsConnected(std::string& errmsg) const;
|
||||||
|
|
||||||
private:
|
protected:
|
||||||
/**
|
/**
|
||||||
* Contained socket. `INVALID_SOCKET` designates the object is empty.
|
* Contained socket. `INVALID_SOCKET` designates the object is empty.
|
||||||
*/
|
*/
|
||||||
|
Loading…
Reference in New Issue
Block a user