aboutsummaryrefslogtreecommitdiff
path: root/src/netbase.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/netbase.cpp')
-rw-r--r--src/netbase.cpp152
1 files changed, 39 insertions, 113 deletions
diff --git a/src/netbase.cpp b/src/netbase.cpp
index 264029d8a2..24188f83c6 100644
--- a/src/netbase.cpp
+++ b/src/netbase.cpp
@@ -7,13 +7,17 @@
#include <sync.h>
#include <tinyformat.h>
+#include <util/sock.h>
#include <util/strencodings.h>
#include <util/string.h>
#include <util/system.h>
+#include <util/time.h>
#include <atomic>
#include <cstdint>
+#include <functional>
#include <limits>
+#include <memory>
#ifndef WIN32
#include <fcntl.h>
@@ -271,14 +275,6 @@ CService LookupNumeric(const std::string& name, int portDefault)
return addr;
}
-struct timeval MillisToTimeval(int64_t nTimeout)
-{
- struct timeval timeout;
- timeout.tv_sec = nTimeout / 1000;
- timeout.tv_usec = (nTimeout % 1000) * 1000;
- return timeout;
-}
-
/** SOCKS version */
enum SOCKSVersion: uint8_t {
SOCKS4 = 0x04,
@@ -336,8 +332,7 @@ enum class IntrRecvError {
* @param data The buffer where the read bytes should be stored.
* @param len The number of bytes to read into the specified buffer.
* @param timeout The total timeout in milliseconds for this read.
- * @param hSocket The socket (has to be in non-blocking mode) from which to read
- * bytes.
+ * @param sock The socket (has to be in non-blocking mode) from which to read bytes.
*
* @returns An IntrRecvError indicating the resulting status of this read.
* IntrRecvError::OK only if all of the specified number of bytes were
@@ -347,7 +342,7 @@ enum class IntrRecvError {
* Sockets can be made non-blocking with SetSocketNonBlocking(const
* SOCKET&, bool).
*/
-static IntrRecvError InterruptibleRecv(uint8_t* data, size_t len, int timeout, const SOCKET& hSocket)
+static IntrRecvError InterruptibleRecv(uint8_t* data, size_t len, int timeout, const Sock& sock)
{
int64_t curTime = GetTimeMillis();
int64_t endTime = curTime + timeout;
@@ -355,7 +350,7 @@ static IntrRecvError InterruptibleRecv(uint8_t* data, size_t len, int timeout, c
// (in millis) to break off in case of an interruption.
const int64_t maxWait = 1000;
while (len > 0 && curTime < endTime) {
- ssize_t ret = recv(hSocket, (char*)data, len, 0); // Optimistically try the recv first
+ ssize_t ret = sock.Recv(data, len, 0); // Optimistically try the recv first
if (ret > 0) {
len -= ret;
data += ret;
@@ -364,25 +359,10 @@ static IntrRecvError InterruptibleRecv(uint8_t* data, size_t len, int timeout, c
} else { // Other error or blocking
int nErr = WSAGetLastError();
if (nErr == WSAEINPROGRESS || nErr == WSAEWOULDBLOCK || nErr == WSAEINVAL) {
- if (!IsSelectableSocket(hSocket)) {
- return IntrRecvError::NetworkError;
- }
// Only wait at most maxWait milliseconds at a time, unless
// we're approaching the end of the specified total timeout
int timeout_ms = std::min(endTime - curTime, maxWait);
-#ifdef USE_POLL
- struct pollfd pollfd = {};
- pollfd.fd = hSocket;
- pollfd.events = POLLIN;
- int nRet = poll(&pollfd, 1, timeout_ms);
-#else
- struct timeval tval = MillisToTimeval(timeout_ms);
- fd_set fdset;
- FD_ZERO(&fdset);
- FD_SET(hSocket, &fdset);
- int nRet = select(hSocket + 1, &fdset, nullptr, nullptr, &tval);
-#endif
- if (nRet == SOCKET_ERROR) {
+ if (!sock.Wait(std::chrono::milliseconds{timeout_ms}, Sock::RECV)) {
return IntrRecvError::NetworkError;
}
} else {
@@ -436,7 +416,7 @@ static std::string Socks5ErrorString(uint8_t err)
* @param port The destination port.
* @param auth The credentials with which to authenticate with the specified
* SOCKS5 proxy.
- * @param hSocket The SOCKS5 proxy socket.
+ * @param sock The SOCKS5 proxy socket.
*
* @returns Whether or not the operation succeeded.
*
@@ -446,7 +426,7 @@ static std::string Socks5ErrorString(uint8_t err)
* @see <a href="https://www.ietf.org/rfc/rfc1928.txt">RFC1928: SOCKS Protocol
* Version 5</a>
*/
-static bool Socks5(const std::string& strDest, int port, const ProxyCredentials *auth, const SOCKET& hSocket)
+static bool Socks5(const std::string& strDest, int port, const ProxyCredentials* auth, const Sock& sock)
{
IntrRecvError recvr;
LogPrint(BCLog::NET, "SOCKS5 connecting %s\n", strDest);
@@ -464,12 +444,12 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials
vSocks5Init.push_back(0x01); // 1 method identifier follows...
vSocks5Init.push_back(SOCKS5Method::NOAUTH);
}
- ssize_t ret = send(hSocket, (const char*)vSocks5Init.data(), vSocks5Init.size(), MSG_NOSIGNAL);
+ ssize_t ret = sock.Send(vSocks5Init.data(), vSocks5Init.size(), MSG_NOSIGNAL);
if (ret != (ssize_t)vSocks5Init.size()) {
return error("Error sending to proxy");
}
uint8_t pchRet1[2];
- if ((recvr = InterruptibleRecv(pchRet1, 2, SOCKS5_RECV_TIMEOUT, hSocket)) != IntrRecvError::OK) {
+ if ((recvr = InterruptibleRecv(pchRet1, 2, SOCKS5_RECV_TIMEOUT, sock)) != IntrRecvError::OK) {
LogPrintf("Socks5() connect to %s:%d failed: InterruptibleRecv() timeout or other failure\n", strDest, port);
return false;
}
@@ -486,13 +466,13 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials
vAuth.insert(vAuth.end(), auth->username.begin(), auth->username.end());
vAuth.push_back(auth->password.size());
vAuth.insert(vAuth.end(), auth->password.begin(), auth->password.end());
- ret = send(hSocket, (const char*)vAuth.data(), vAuth.size(), MSG_NOSIGNAL);
+ ret = sock.Send(vAuth.data(), vAuth.size(), MSG_NOSIGNAL);
if (ret != (ssize_t)vAuth.size()) {
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) {
+ if ((recvr = InterruptibleRecv(pchRetA, 2, SOCKS5_RECV_TIMEOUT, sock)) != IntrRecvError::OK) {
return error("Error reading proxy authentication response");
}
if (pchRetA[0] != 0x01 || pchRetA[1] != 0x00) {
@@ -512,12 +492,12 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials
vSocks5.insert(vSocks5.end(), strDest.begin(), strDest.end());
vSocks5.push_back((port >> 8) & 0xFF);
vSocks5.push_back((port >> 0) & 0xFF);
- ret = send(hSocket, (const char*)vSocks5.data(), vSocks5.size(), MSG_NOSIGNAL);
+ ret = sock.Send(vSocks5.data(), vSocks5.size(), MSG_NOSIGNAL);
if (ret != (ssize_t)vSocks5.size()) {
return error("Error sending to proxy");
}
uint8_t pchRet2[4];
- if ((recvr = InterruptibleRecv(pchRet2, 4, SOCKS5_RECV_TIMEOUT, hSocket)) != IntrRecvError::OK) {
+ if ((recvr = InterruptibleRecv(pchRet2, 4, SOCKS5_RECV_TIMEOUT, sock)) != IntrRecvError::OK) {
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
@@ -541,16 +521,16 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials
uint8_t pchRet3[256];
switch (pchRet2[3])
{
- case SOCKS5Atyp::IPV4: recvr = InterruptibleRecv(pchRet3, 4, SOCKS5_RECV_TIMEOUT, hSocket); break;
- case SOCKS5Atyp::IPV6: recvr = InterruptibleRecv(pchRet3, 16, SOCKS5_RECV_TIMEOUT, hSocket); break;
+ case SOCKS5Atyp::IPV4: recvr = InterruptibleRecv(pchRet3, 4, SOCKS5_RECV_TIMEOUT, sock); break;
+ case SOCKS5Atyp::IPV6: recvr = InterruptibleRecv(pchRet3, 16, SOCKS5_RECV_TIMEOUT, sock); break;
case SOCKS5Atyp::DOMAINNAME:
{
- recvr = InterruptibleRecv(pchRet3, 1, SOCKS5_RECV_TIMEOUT, hSocket);
+ recvr = InterruptibleRecv(pchRet3, 1, SOCKS5_RECV_TIMEOUT, sock);
if (recvr != IntrRecvError::OK) {
return error("Error reading from proxy");
}
int nRecv = pchRet3[0];
- recvr = InterruptibleRecv(pchRet3, nRecv, SOCKS5_RECV_TIMEOUT, hSocket);
+ recvr = InterruptibleRecv(pchRet3, nRecv, SOCKS5_RECV_TIMEOUT, sock);
break;
}
default: return error("Error: malformed proxy response");
@@ -558,41 +538,35 @@ static bool Socks5(const std::string& strDest, int port, const ProxyCredentials
if (recvr != IntrRecvError::OK) {
return error("Error reading from proxy");
}
- if ((recvr = InterruptibleRecv(pchRet3, 2, SOCKS5_RECV_TIMEOUT, hSocket)) != IntrRecvError::OK) {
+ if ((recvr = InterruptibleRecv(pchRet3, 2, SOCKS5_RECV_TIMEOUT, sock)) != IntrRecvError::OK) {
return error("Error reading from proxy");
}
LogPrint(BCLog::NET, "SOCKS5 connected %s\n", strDest);
return true;
}
-/**
- * Try to create a socket file descriptor with specific properties in the
- * communications domain (address family) of the specified service.
- *
- * For details on the desired properties, see the inline comments in the source
- * code.
- */
-SOCKET CreateSocket(const CService &addrConnect)
+std::unique_ptr<Sock> CreateSockTCP(const CService& address_family)
{
// Create a sockaddr from the specified service.
struct sockaddr_storage sockaddr;
socklen_t len = sizeof(sockaddr);
- if (!addrConnect.GetSockAddr((struct sockaddr*)&sockaddr, &len)) {
- LogPrintf("Cannot create socket for %s: unsupported network\n", addrConnect.ToString());
- return INVALID_SOCKET;
+ if (!address_family.GetSockAddr((struct sockaddr*)&sockaddr, &len)) {
+ LogPrintf("Cannot create socket for %s: unsupported network\n", address_family.ToString());
+ return nullptr;
}
// Create a TCP socket in the address family of the specified service.
SOCKET hSocket = socket(((struct sockaddr*)&sockaddr)->sa_family, SOCK_STREAM, IPPROTO_TCP);
- if (hSocket == INVALID_SOCKET)
- return INVALID_SOCKET;
+ if (hSocket == INVALID_SOCKET) {
+ return nullptr;
+ }
// Ensure that waiting for I/O on this socket won't result in undefined
// behavior.
if (!IsSelectableSocket(hSocket)) {
CloseSocket(hSocket);
LogPrintf("Cannot create connection: non-selectable socket created (fd >= FD_SETSIZE ?)\n");
- return INVALID_SOCKET;
+ return nullptr;
}
#ifdef SO_NOSIGPIPE
@@ -608,11 +582,14 @@ SOCKET CreateSocket(const CService &addrConnect)
// Set the non-blocking option on the socket.
if (!SetSocketNonBlocking(hSocket, true)) {
CloseSocket(hSocket);
- LogPrintf("CreateSocket: Setting socket to non-blocking failed, error %s\n", NetworkErrorString(WSAGetLastError()));
+ LogPrintf("Error setting socket to non-blocking: %s\n", NetworkErrorString(WSAGetLastError()));
+ return nullptr;
}
- return hSocket;
+ return std::make_unique<Sock>(hSocket);
}
+std::function<std::unique_ptr<Sock>(const CService&)> CreateSock = CreateSockTCP;
+
template<typename... Args>
static void LogConnectFailure(bool manual_connection, const char* fmt, const Args&... args) {
std::string error_message = tfm::format(fmt, args...);
@@ -786,7 +763,7 @@ bool IsProxy(const CNetAddr &addr) {
* @param proxy The SOCKS5 proxy.
* @param strDest The destination service to which to connect.
* @param port The destination port.
- * @param hSocket The socket on which to connect to the SOCKS5 proxy.
+ * @param sock The socket on which to connect to the SOCKS5 proxy.
* @param nTimeout Wait this many milliseconds for the connection to the SOCKS5
* proxy to be established.
* @param[out] outProxyConnectionFailed Whether or not the connection to the
@@ -794,10 +771,10 @@ bool IsProxy(const CNetAddr &addr) {
*
* @returns Whether or not the operation succeeded.
*/
-bool ConnectThroughProxy(const proxyType &proxy, const std::string& strDest, int port, const SOCKET& hSocket, int nTimeout, bool& outProxyConnectionFailed)
+bool ConnectThroughProxy(const proxyType& proxy, const std::string& strDest, int port, const Sock& sock, int nTimeout, bool& outProxyConnectionFailed)
{
// first connect to proxy server
- if (!ConnectSocketDirectly(proxy.proxy, hSocket, nTimeout, true)) {
+ if (!ConnectSocketDirectly(proxy.proxy, sock.Get(), nTimeout, true)) {
outProxyConnectionFailed = true;
return false;
}
@@ -806,11 +783,11 @@ 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, (uint16_t)port, &random_auth, hSocket)) {
+ if (!Socks5(strDest, (uint16_t)port, &random_auth, sock)) {
return false;
}
} else {
- if (!Socks5(strDest, (uint16_t)port, 0, hSocket)) {
+ if (!Socks5(strDest, (uint16_t)port, 0, sock)) {
return false;
}
}
@@ -869,57 +846,6 @@ bool LookupSubNet(const std::string& strSubnet, CSubNet& ret)
return false;
}
-#ifdef WIN32
-std::string NetworkErrorString(int err)
-{
- wchar_t buf[256];
- buf[0] = 0;
- if(FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK,
- nullptr, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
- buf, ARRAYSIZE(buf), nullptr))
- {
- return strprintf("%s (%d)", std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>,wchar_t>().to_bytes(buf), err);
- }
- else
- {
- return strprintf("Unknown error (%d)", err);
- }
-}
-#else
-std::string NetworkErrorString(int err)
-{
- char buf[256];
- buf[0] = 0;
- /* Too bad there are two incompatible implementations of the
- * thread-safe strerror. */
- const char *s;
-#ifdef STRERROR_R_CHAR_P /* GNU variant can return a pointer outside the passed buffer */
- s = strerror_r(err, buf, sizeof(buf));
-#else /* POSIX variant always returns message in buffer */
- s = buf;
- if (strerror_r(err, buf, sizeof(buf)))
- buf[0] = 0;
-#endif
- return strprintf("%s (%d)", s, err);
-}
-#endif
-
-bool CloseSocket(SOCKET& hSocket)
-{
- if (hSocket == INVALID_SOCKET)
- return false;
-#ifdef WIN32
- int ret = closesocket(hSocket);
-#else
- int ret = close(hSocket);
-#endif
- if (ret) {
- LogPrintf("Socket close failed: %d. Error: %s\n", hSocket, NetworkErrorString(WSAGetLastError()));
- }
- hSocket = INVALID_SOCKET;
- return ret != SOCKET_ERROR;
-}
-
bool SetSocketNonBlocking(const SOCKET& hSocket, bool fNonBlocking)
{
if (fNonBlocking) {