mirror of
https://github.com/bitcoin/bitcoin.git
synced 2024-11-20 10:38:42 +01:00
Merge #11363: net: Split socket create/connect
3830b6e
net: use CreateSocket for binds (Cory Fields)df3bcf8
net: pass socket closing responsibility up to caller for outgoing connections (Cory Fields)9e3b2f5
net: Move IsSelectableSocket check into socket creation (Cory Fields)1729c29
net: split socket creation out of connection (Cory Fields) Pull request description: Requirement for #11227. We'll need to create sockets and perform the actual connect in separate steps, so break them up. #11227 adds an RAII wrapper around connection attempts, as a belt-and-suspenders in case a CloseSocket is missed. Tree-SHA512: de675bb718cc56d68893c303b8057ca062c7431eaa17ae7c4829caed119fa3f15b404d8f52aca22a6bca6e73a26fb79e898b335d090ab015bf6456cf417fc694
This commit is contained in:
commit
ba2f19504c
70
src/net.cpp
70
src/net.cpp
@ -415,39 +415,48 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
|
||||
if (addrConnect.IsValid()) {
|
||||
bool proxyConnectionFailed = false;
|
||||
|
||||
if (GetProxy(addrConnect.GetNetwork(), proxy))
|
||||
if (GetProxy(addrConnect.GetNetwork(), proxy)) {
|
||||
hSocket = CreateSocket(proxy.proxy);
|
||||
if (hSocket == INVALID_SOCKET) {
|
||||
return nullptr;
|
||||
}
|
||||
connected = ConnectThroughProxy(proxy, addrConnect.ToStringIP(), addrConnect.GetPort(), hSocket, nConnectTimeout, &proxyConnectionFailed);
|
||||
else // no proxy needed (none set for target network)
|
||||
} else {
|
||||
// no proxy needed (none set for target network)
|
||||
hSocket = CreateSocket(addrConnect);
|
||||
if (hSocket == INVALID_SOCKET) {
|
||||
return nullptr;
|
||||
}
|
||||
connected = ConnectSocketDirectly(addrConnect, hSocket, nConnectTimeout);
|
||||
}
|
||||
if (!proxyConnectionFailed) {
|
||||
// If a connection to the node was attempted, and failure (if any) is not caused by a problem connecting to
|
||||
// the proxy, mark this as an attempt.
|
||||
addrman.Attempt(addrConnect, fCountFailure);
|
||||
}
|
||||
} else if (pszDest && GetNameProxy(proxy)) {
|
||||
hSocket = CreateSocket(proxy.proxy);
|
||||
if (hSocket == INVALID_SOCKET) {
|
||||
return nullptr;
|
||||
}
|
||||
std::string host;
|
||||
int port = default_port;
|
||||
SplitHostPort(std::string(pszDest), port, host);
|
||||
connected = ConnectThroughProxy(proxy, host, port, hSocket, nConnectTimeout, nullptr);
|
||||
}
|
||||
if (connected) {
|
||||
if (!IsSelectableSocket(hSocket)) {
|
||||
LogPrintf("Cannot create connection: non-selectable socket created (fd >= FD_SETSIZE ?)\n");
|
||||
CloseSocket(hSocket);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Add node
|
||||
NodeId id = GetNewNodeId();
|
||||
uint64_t nonce = GetDeterministicRandomizer(RANDOMIZER_ID_LOCALHOSTNONCE).Write(id).Finalize();
|
||||
CAddress addr_bind = GetBindAddress(hSocket);
|
||||
CNode* pnode = new CNode(id, nLocalServices, GetBestHeight(), hSocket, addrConnect, CalculateKeyedNetGroup(addrConnect), nonce, addr_bind, pszDest ? pszDest : "", false);
|
||||
pnode->AddRef();
|
||||
|
||||
return pnode;
|
||||
if (!connected) {
|
||||
CloseSocket(hSocket);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
// Add node
|
||||
NodeId id = GetNewNodeId();
|
||||
uint64_t nonce = GetDeterministicRandomizer(RANDOMIZER_ID_LOCALHOSTNONCE).Write(id).Finalize();
|
||||
CAddress addr_bind = GetBindAddress(hSocket);
|
||||
CNode* pnode = new CNode(id, nLocalServices, GetBestHeight(), hSocket, addrConnect, CalculateKeyedNetGroup(addrConnect), nonce, addr_bind, pszDest ? pszDest : "", false);
|
||||
pnode->AddRef();
|
||||
|
||||
return pnode;
|
||||
}
|
||||
|
||||
void CConnman::DumpBanlist()
|
||||
@ -2056,44 +2065,21 @@ bool CConnman::BindListenPort(const CService &addrBind, std::string& strError, b
|
||||
return false;
|
||||
}
|
||||
|
||||
SOCKET hListenSocket = socket(((struct sockaddr*)&sockaddr)->sa_family, SOCK_STREAM, IPPROTO_TCP);
|
||||
SOCKET hListenSocket = CreateSocket(addrBind);
|
||||
if (hListenSocket == INVALID_SOCKET)
|
||||
{
|
||||
strError = strprintf("Error: Couldn't open socket for incoming connections (socket returned error %s)", NetworkErrorString(WSAGetLastError()));
|
||||
LogPrintf("%s\n", strError);
|
||||
return false;
|
||||
}
|
||||
if (!IsSelectableSocket(hListenSocket))
|
||||
{
|
||||
strError = "Error: Couldn't create a listenable socket for incoming connections";
|
||||
LogPrintf("%s\n", strError);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
#ifndef WIN32
|
||||
#ifdef SO_NOSIGPIPE
|
||||
// Different way of disabling SIGPIPE on BSD
|
||||
setsockopt(hListenSocket, SOL_SOCKET, SO_NOSIGPIPE, (void*)&nOne, sizeof(int));
|
||||
#endif
|
||||
// Allow binding if the port is still in TIME_WAIT state after
|
||||
// the program was closed and restarted.
|
||||
setsockopt(hListenSocket, SOL_SOCKET, SO_REUSEADDR, (void*)&nOne, sizeof(int));
|
||||
// Disable Nagle's algorithm
|
||||
setsockopt(hListenSocket, IPPROTO_TCP, TCP_NODELAY, (void*)&nOne, sizeof(int));
|
||||
#else
|
||||
setsockopt(hListenSocket, SOL_SOCKET, SO_REUSEADDR, (const char*)&nOne, sizeof(int));
|
||||
setsockopt(hListenSocket, IPPROTO_TCP, TCP_NODELAY, (const char*)&nOne, sizeof(int));
|
||||
#endif
|
||||
|
||||
// Set to non-blocking, incoming connections will also inherit this
|
||||
if (!SetSocketNonBlocking(hListenSocket, true)) {
|
||||
CloseSocket(hListenSocket);
|
||||
strError = strprintf("BindListenPort: Setting listening socket to non-blocking failed, error %s\n", NetworkErrorString(WSAGetLastError()));
|
||||
LogPrintf("%s\n", strError);
|
||||
return false;
|
||||
}
|
||||
|
||||
// some systems don't have IPV6_V6ONLY but are always v6only; others do have the option
|
||||
// and enable it by default or not. Try to enable it, if possible.
|
||||
if (addrBind.IsIPv6()) {
|
||||
|
@ -313,12 +313,11 @@ std::string Socks5ErrorString(uint8_t err)
|
||||
}
|
||||
|
||||
/** Connect using SOCKS5 (as described in RFC1928) */
|
||||
static bool Socks5(const std::string& strDest, int port, const ProxyCredentials *auth, SOCKET& hSocket)
|
||||
static bool Socks5(const std::string& strDest, int port, const ProxyCredentials *auth, const SOCKET& hSocket)
|
||||
{
|
||||
IntrRecvError recvr;
|
||||
LogPrint(BCLog::NET, "SOCKS5 connecting %s\n", strDest);
|
||||
if (strDest.size() > 255) {
|
||||
CloseSocket(hSocket);
|
||||
return error("Hostname too long");
|
||||
}
|
||||
// Accepted authentication methods
|
||||
@ -334,17 +333,14 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials
|
||||
}
|
||||
ssize_t ret = send(hSocket, (const char*)vSocks5Init.data(), vSocks5Init.size(), MSG_NOSIGNAL);
|
||||
if (ret != (ssize_t)vSocks5Init.size()) {
|
||||
CloseSocket(hSocket);
|
||||
return error("Error sending to proxy");
|
||||
}
|
||||
uint8_t pchRet1[2];
|
||||
if ((recvr = InterruptibleRecv(pchRet1, 2, SOCKS5_RECV_TIMEOUT, hSocket)) != IntrRecvError::OK) {
|
||||
CloseSocket(hSocket);
|
||||
LogPrintf("Socks5() connect to %s:%d failed: InterruptibleRecv() timeout or other failure\n", strDest, port);
|
||||
return false;
|
||||
}
|
||||
if (pchRet1[0] != SOCKSVersion::SOCKS5) {
|
||||
CloseSocket(hSocket);
|
||||
return error("Proxy failed to initialize");
|
||||
}
|
||||
if (pchRet1[1] == SOCKS5Method::USER_PASS && auth) {
|
||||
@ -359,23 +355,19 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials
|
||||
vAuth.insert(vAuth.end(), auth->password.begin(), auth->password.end());
|
||||
ret = send(hSocket, (const char*)vAuth.data(), vAuth.size(), MSG_NOSIGNAL);
|
||||
if (ret != (ssize_t)vAuth.size()) {
|
||||
CloseSocket(hSocket);
|
||||
return error("Error sending authentication to proxy");
|
||||
}
|
||||
LogPrint(BCLog::PROXY, "SOCKS5 sending proxy authentication %s:%s\n", auth->username, auth->password);
|
||||
uint8_t pchRetA[2];
|
||||
if ((recvr = InterruptibleRecv(pchRetA, 2, SOCKS5_RECV_TIMEOUT, hSocket)) != IntrRecvError::OK) {
|
||||
CloseSocket(hSocket);
|
||||
return error("Error reading proxy authentication response");
|
||||
}
|
||||
if (pchRetA[0] != 0x01 || pchRetA[1] != 0x00) {
|
||||
CloseSocket(hSocket);
|
||||
return error("Proxy authentication unsuccessful");
|
||||
}
|
||||
} else if (pchRet1[1] == SOCKS5Method::NOAUTH) {
|
||||
// Perform no authentication
|
||||
} else {
|
||||
CloseSocket(hSocket);
|
||||
return error("Proxy requested wrong authentication method %02x", pchRet1[1]);
|
||||
}
|
||||
std::vector<uint8_t> vSocks5;
|
||||
@ -389,12 +381,10 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials
|
||||
vSocks5.push_back((port >> 0) & 0xFF);
|
||||
ret = send(hSocket, (const char*)vSocks5.data(), vSocks5.size(), MSG_NOSIGNAL);
|
||||
if (ret != (ssize_t)vSocks5.size()) {
|
||||
CloseSocket(hSocket);
|
||||
return error("Error sending to proxy");
|
||||
}
|
||||
uint8_t pchRet2[4];
|
||||
if ((recvr = InterruptibleRecv(pchRet2, 4, SOCKS5_RECV_TIMEOUT, hSocket)) != IntrRecvError::OK) {
|
||||
CloseSocket(hSocket);
|
||||
if (recvr == IntrRecvError::Timeout) {
|
||||
/* If a timeout happens here, this effectively means we timed out while connecting
|
||||
* to the remote node. This is very common for Tor, so do not print an
|
||||
@ -405,17 +395,14 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials
|
||||
}
|
||||
}
|
||||
if (pchRet2[0] != SOCKSVersion::SOCKS5) {
|
||||
CloseSocket(hSocket);
|
||||
return error("Proxy failed to accept request");
|
||||
}
|
||||
if (pchRet2[1] != SOCKS5Reply::SUCCEEDED) {
|
||||
// Failures to connect to a peer that are not proxy errors
|
||||
CloseSocket(hSocket);
|
||||
LogPrintf("Socks5() connect to %s:%d failed: %s\n", strDest, port, Socks5ErrorString(pchRet2[1]));
|
||||
return false;
|
||||
}
|
||||
if (pchRet2[2] != 0x00) { // Reserved field must be 0
|
||||
CloseSocket(hSocket);
|
||||
return error("Error: malformed proxy response");
|
||||
}
|
||||
uint8_t pchRet3[256];
|
||||
@ -427,41 +414,42 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials
|
||||
{
|
||||
recvr = InterruptibleRecv(pchRet3, 1, SOCKS5_RECV_TIMEOUT, hSocket);
|
||||
if (recvr != IntrRecvError::OK) {
|
||||
CloseSocket(hSocket);
|
||||
return error("Error reading from proxy");
|
||||
}
|
||||
int nRecv = pchRet3[0];
|
||||
recvr = InterruptibleRecv(pchRet3, nRecv, SOCKS5_RECV_TIMEOUT, hSocket);
|
||||
break;
|
||||
}
|
||||
default: CloseSocket(hSocket); return error("Error: malformed proxy response");
|
||||
default: return error("Error: malformed proxy response");
|
||||
}
|
||||
if (recvr != IntrRecvError::OK) {
|
||||
CloseSocket(hSocket);
|
||||
return error("Error reading from proxy");
|
||||
}
|
||||
if ((recvr = InterruptibleRecv(pchRet3, 2, SOCKS5_RECV_TIMEOUT, hSocket)) != IntrRecvError::OK) {
|
||||
CloseSocket(hSocket);
|
||||
return error("Error reading from proxy");
|
||||
}
|
||||
LogPrint(BCLog::NET, "SOCKS5 connected %s\n", strDest);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRet, int nTimeout)
|
||||
SOCKET CreateSocket(const CService &addrConnect)
|
||||
{
|
||||
hSocketRet = INVALID_SOCKET;
|
||||
|
||||
struct sockaddr_storage sockaddr;
|
||||
socklen_t len = sizeof(sockaddr);
|
||||
if (!addrConnect.GetSockAddr((struct sockaddr*)&sockaddr, &len)) {
|
||||
LogPrintf("Cannot connect to %s: unsupported network\n", addrConnect.ToString());
|
||||
return false;
|
||||
LogPrintf("Cannot create socket for %s: unsupported network\n", addrConnect.ToString());
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
SOCKET hSocket = socket(((struct sockaddr*)&sockaddr)->sa_family, SOCK_STREAM, IPPROTO_TCP);
|
||||
if (hSocket == INVALID_SOCKET)
|
||||
return false;
|
||||
return INVALID_SOCKET;
|
||||
|
||||
if (!IsSelectableSocket(hSocket)) {
|
||||
CloseSocket(hSocket);
|
||||
LogPrintf("Cannot create connection: non-selectable socket created (fd >= FD_SETSIZE ?)\n");
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
#ifdef SO_NOSIGPIPE
|
||||
int set = 1;
|
||||
@ -475,9 +463,23 @@ bool ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRet, int
|
||||
// Set to non-blocking
|
||||
if (!SetSocketNonBlocking(hSocket, true)) {
|
||||
CloseSocket(hSocket);
|
||||
return error("ConnectSocketDirectly: Setting socket to non-blocking failed, error %s\n", NetworkErrorString(WSAGetLastError()));
|
||||
LogPrintf("ConnectSocketDirectly: Setting socket to non-blocking failed, error %s\n", NetworkErrorString(WSAGetLastError()));
|
||||
}
|
||||
return hSocket;
|
||||
}
|
||||
|
||||
bool ConnectSocketDirectly(const CService &addrConnect, const SOCKET& hSocket, int nTimeout)
|
||||
{
|
||||
struct sockaddr_storage sockaddr;
|
||||
socklen_t len = sizeof(sockaddr);
|
||||
if (hSocket == INVALID_SOCKET) {
|
||||
LogPrintf("Cannot connect to %s: invalid socket\n", addrConnect.ToString());
|
||||
return false;
|
||||
}
|
||||
if (!addrConnect.GetSockAddr((struct sockaddr*)&sockaddr, &len)) {
|
||||
LogPrintf("Cannot connect to %s: unsupported network\n", addrConnect.ToString());
|
||||
return false;
|
||||
}
|
||||
if (connect(hSocket, (struct sockaddr*)&sockaddr, len) == SOCKET_ERROR)
|
||||
{
|
||||
int nErr = WSAGetLastError();
|
||||
@ -492,13 +494,11 @@ bool ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRet, int
|
||||
if (nRet == 0)
|
||||
{
|
||||
LogPrint(BCLog::NET, "connection to %s timeout\n", addrConnect.ToString());
|
||||
CloseSocket(hSocket);
|
||||
return false;
|
||||
}
|
||||
if (nRet == SOCKET_ERROR)
|
||||
{
|
||||
LogPrintf("select() for %s failed: %s\n", addrConnect.ToString(), NetworkErrorString(WSAGetLastError()));
|
||||
CloseSocket(hSocket);
|
||||
return false;
|
||||
}
|
||||
socklen_t nRetSize = sizeof(nRet);
|
||||
@ -509,13 +509,11 @@ bool ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRet, int
|
||||
#endif
|
||||
{
|
||||
LogPrintf("getsockopt() for %s failed: %s\n", addrConnect.ToString(), NetworkErrorString(WSAGetLastError()));
|
||||
CloseSocket(hSocket);
|
||||
return false;
|
||||
}
|
||||
if (nRet != 0)
|
||||
{
|
||||
LogPrintf("connect() to %s failed after select(): %s\n", addrConnect.ToString(), NetworkErrorString(nRet));
|
||||
CloseSocket(hSocket);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -526,12 +524,9 @@ bool ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRet, int
|
||||
#endif
|
||||
{
|
||||
LogPrintf("connect() to %s failed: %s\n", addrConnect.ToString(), NetworkErrorString(WSAGetLastError()));
|
||||
CloseSocket(hSocket);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
hSocketRet = hSocket;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -583,9 +578,8 @@ bool IsProxy(const CNetAddr &addr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ConnectThroughProxy(const proxyType &proxy, const std::string& strDest, int port, SOCKET& hSocketRet, int nTimeout, bool *outProxyConnectionFailed)
|
||||
bool ConnectThroughProxy(const proxyType &proxy, const std::string& strDest, int port, const SOCKET& hSocket, int nTimeout, bool *outProxyConnectionFailed)
|
||||
{
|
||||
SOCKET hSocket = INVALID_SOCKET;
|
||||
// first connect to proxy server
|
||||
if (!ConnectSocketDirectly(proxy.proxy, hSocket, nTimeout)) {
|
||||
if (outProxyConnectionFailed)
|
||||
@ -597,14 +591,14 @@ bool ConnectThroughProxy(const proxyType &proxy, const std::string& strDest, int
|
||||
ProxyCredentials random_auth;
|
||||
static std::atomic_int counter(0);
|
||||
random_auth.username = random_auth.password = strprintf("%i", counter++);
|
||||
if (!Socks5(strDest, (unsigned short)port, &random_auth, hSocket))
|
||||
if (!Socks5(strDest, (unsigned short)port, &random_auth, hSocket)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!Socks5(strDest, (unsigned short)port, 0, hSocket))
|
||||
if (!Socks5(strDest, (unsigned short)port, 0, hSocket)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
hSocketRet = hSocket;
|
||||
return true;
|
||||
}
|
||||
bool LookupSubNet(const char* pszName, CSubNet& ret)
|
||||
|
@ -51,8 +51,9 @@ bool Lookup(const char *pszName, CService& addr, int portDefault, bool fAllowLoo
|
||||
bool Lookup(const char *pszName, std::vector<CService>& vAddr, int portDefault, bool fAllowLookup, unsigned int nMaxSolutions);
|
||||
CService LookupNumeric(const char *pszName, int portDefault = 0);
|
||||
bool LookupSubNet(const char *pszName, CSubNet& subnet);
|
||||
bool ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRet, int nTimeout);
|
||||
bool ConnectThroughProxy(const proxyType &proxy, const std::string& strDest, int port, SOCKET& hSocketRet, int nTimeout, bool *outProxyConnectionFailed);
|
||||
SOCKET CreateSocket(const CService &addrConnect);
|
||||
bool ConnectSocketDirectly(const CService &addrConnect, const SOCKET& hSocketRet, int nTimeout);
|
||||
bool ConnectThroughProxy(const proxyType &proxy, const std::string& strDest, int port, const SOCKET& hSocketRet, int nTimeout, bool *outProxyConnectionFailed);
|
||||
/** Return readable error string for a network error code */
|
||||
std::string NetworkErrorString(int err);
|
||||
/** Close socket and set hSocket to INVALID_SOCKET */
|
||||
|
Loading…
Reference in New Issue
Block a user