aboutsummaryrefslogtreecommitdiff
path: root/src/netbase.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/netbase.cpp')
-rw-r--r--src/netbase.cpp215
1 files changed, 156 insertions, 59 deletions
diff --git a/src/netbase.cpp b/src/netbase.cpp
index 9fbd9f7dea..e231766487 100644
--- a/src/netbase.cpp
+++ b/src/netbase.cpp
@@ -3,6 +3,8 @@
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
+#include <config/bitcoin-config.h> // IWYU pragma: keep
+
#include <netbase.h>
#include <compat/compat.h>
@@ -21,6 +23,10 @@
#include <limits>
#include <memory>
+#if HAVE_SOCKADDR_UN
+#include <sys/un.h>
+#endif
+
// Settings
static GlobalMutex g_proxyinfo_mutex;
static Proxy proxyInfo[NET_MAX] GUARDED_BY(g_proxyinfo_mutex);
@@ -208,6 +214,24 @@ CService LookupNumeric(const std::string& name, uint16_t portDefault, DNSLookupF
return Lookup(name, portDefault, /*fAllowLookup=*/false, dns_lookup_function).value_or(CService{});
}
+bool IsUnixSocketPath(const std::string& name)
+{
+#if HAVE_SOCKADDR_UN
+ if (name.find(ADDR_PREFIX_UNIX) != 0) return false;
+
+ // Split off "unix:" prefix
+ std::string str{name.substr(ADDR_PREFIX_UNIX.length())};
+
+ // Path size limit is platform-dependent
+ // see https://manpages.ubuntu.com/manpages/xenial/en/man7/unix.7.html
+ if (str.size() + 1 > sizeof(((sockaddr_un*)nullptr)->sun_path)) return false;
+
+ return true;
+#else
+ return false;
+#endif
+}
+
/** SOCKS version */
enum SOCKSVersion: uint8_t {
SOCKS4 = 0x04,
@@ -338,7 +362,8 @@ bool Socks5(const std::string& strDest, uint16_t port, const ProxyCredentials* a
IntrRecvError recvr;
LogPrint(BCLog::NET, "SOCKS5 connecting %s\n", strDest);
if (strDest.size() > 255) {
- return error("Hostname too long");
+ LogError("Hostname too long\n");
+ return false;
}
// Construct the version identifier/method selection message
std::vector<uint8_t> vSocks5Init;
@@ -358,14 +383,17 @@ bool Socks5(const std::string& strDest, uint16_t port, const ProxyCredentials* a
return false;
}
if (pchRet1[0] != SOCKSVersion::SOCKS5) {
- return error("Proxy failed to initialize");
+ LogError("Proxy failed to initialize\n");
+ return false;
}
if (pchRet1[1] == SOCKS5Method::USER_PASS && auth) {
// Perform username/password authentication (as described in RFC1929)
std::vector<uint8_t> vAuth;
vAuth.push_back(0x01); // Current (and only) version of user/pass subnegotiation
- if (auth->username.size() > 255 || auth->password.size() > 255)
- return error("Proxy username or password too long");
+ if (auth->username.size() > 255 || auth->password.size() > 255) {
+ LogError("Proxy username or password too long\n");
+ return false;
+ }
vAuth.push_back(auth->username.size());
vAuth.insert(vAuth.end(), auth->username.begin(), auth->username.end());
vAuth.push_back(auth->password.size());
@@ -374,15 +402,18 @@ bool Socks5(const std::string& strDest, uint16_t port, const ProxyCredentials* a
LogPrint(BCLog::PROXY, "SOCKS5 sending proxy authentication %s:%s\n", auth->username, auth->password);
uint8_t pchRetA[2];
if (InterruptibleRecv(pchRetA, 2, g_socks5_recv_timeout, sock) != IntrRecvError::OK) {
- return error("Error reading proxy authentication response");
+ LogError("Error reading proxy authentication response\n");
+ return false;
}
if (pchRetA[0] != 0x01 || pchRetA[1] != 0x00) {
- return error("Proxy authentication unsuccessful");
+ LogError("Proxy authentication unsuccessful\n");
+ return false;
}
} else if (pchRet1[1] == SOCKS5Method::NOAUTH) {
// Perform no authentication
} else {
- return error("Proxy requested wrong authentication method %02x", pchRet1[1]);
+ LogError("Proxy requested wrong authentication method %02x\n", pchRet1[1]);
+ return false;
}
std::vector<uint8_t> vSocks5;
vSocks5.push_back(SOCKSVersion::SOCKS5); // VER protocol version
@@ -402,11 +433,13 @@ bool Socks5(const std::string& strDest, uint16_t port, const ProxyCredentials* a
* error message. */
return false;
} else {
- return error("Error while reading proxy response");
+ LogError("Error while reading proxy response\n");
+ return false;
}
}
if (pchRet2[0] != SOCKSVersion::SOCKS5) {
- return error("Proxy failed to accept request");
+ LogError("Proxy failed to accept request\n");
+ return false;
}
if (pchRet2[1] != SOCKS5Reply::SUCCEEDED) {
// Failures to connect to a peer that are not proxy errors
@@ -414,7 +447,8 @@ bool Socks5(const std::string& strDest, uint16_t port, const ProxyCredentials* a
return false;
}
if (pchRet2[2] != 0x00) { // Reserved field must be 0
- return error("Error: malformed proxy response");
+ LogError("Error: malformed proxy response\n");
+ return false;
}
uint8_t pchRet3[256];
switch (pchRet2[3]) {
@@ -423,39 +457,46 @@ bool Socks5(const std::string& strDest, uint16_t port, const ProxyCredentials* a
case SOCKS5Atyp::DOMAINNAME: {
recvr = InterruptibleRecv(pchRet3, 1, g_socks5_recv_timeout, sock);
if (recvr != IntrRecvError::OK) {
- return error("Error reading from proxy");
+ LogError("Error reading from proxy\n");
+ return false;
}
int nRecv = pchRet3[0];
recvr = InterruptibleRecv(pchRet3, nRecv, g_socks5_recv_timeout, sock);
break;
}
- default: return error("Error: malformed proxy response");
+ default: {
+ LogError("Error: malformed proxy response\n");
+ return false;
+ }
}
if (recvr != IntrRecvError::OK) {
- return error("Error reading from proxy");
+ LogError("Error reading from proxy\n");
+ return false;
}
if (InterruptibleRecv(pchRet3, 2, g_socks5_recv_timeout, sock) != IntrRecvError::OK) {
- return error("Error reading from proxy");
+ LogError("Error reading from proxy\n");
+ return false;
}
LogPrint(BCLog::NET, "SOCKS5 connected %s\n", strDest);
return true;
} catch (const std::runtime_error& e) {
- return error("Error during SOCKS5 proxy handshake: %s", e.what());
+ LogError("Error during SOCKS5 proxy handshake: %s\n", e.what());
+ return false;
}
}
-std::unique_ptr<Sock> CreateSockTCP(const CService& address_family)
+std::unique_ptr<Sock> CreateSockOS(sa_family_t address_family)
{
- // Create a sockaddr from the specified service.
- struct sockaddr_storage sockaddr;
- socklen_t len = sizeof(sockaddr);
- if (!address_family.GetSockAddr((struct sockaddr*)&sockaddr, &len)) {
- LogPrintf("Cannot create socket for %s: unsupported network\n", address_family.ToStringAddrPort());
- return nullptr;
- }
+ // Not IPv4, IPv6 or UNIX
+ if (address_family == AF_UNSPEC) return nullptr;
+
+ int protocol{IPPROTO_TCP};
+#if HAVE_SOCKADDR_UN
+ if (address_family == AF_UNIX) protocol = 0;
+#endif
- // Create a TCP socket in the address family of the specified service.
- SOCKET hSocket = socket(((struct sockaddr*)&sockaddr)->sa_family, SOCK_STREAM, IPPROTO_TCP);
+ // Create a socket in the specified address family.
+ SOCKET hSocket = socket(address_family, SOCK_STREAM, protocol);
if (hSocket == INVALID_SOCKET) {
return nullptr;
}
@@ -479,21 +520,25 @@ std::unique_ptr<Sock> CreateSockTCP(const CService& address_family)
}
#endif
- // Set the no-delay option (disable Nagle's algorithm) on the TCP socket.
- const int on{1};
- if (sock->SetSockOpt(IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) == SOCKET_ERROR) {
- LogPrint(BCLog::NET, "Unable to set TCP_NODELAY on a newly created socket, continuing anyway\n");
- }
-
// Set the non-blocking option on the socket.
if (!sock->SetNonBlocking()) {
LogPrintf("Error setting socket to non-blocking: %s\n", NetworkErrorString(WSAGetLastError()));
return nullptr;
}
+
+#if HAVE_SOCKADDR_UN
+ if (address_family == AF_UNIX) return sock;
+#endif
+
+ // Set the no-delay option (disable Nagle's algorithm) on the TCP socket.
+ const int on{1};
+ if (sock->SetSockOpt(IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on)) == SOCKET_ERROR) {
+ LogPrint(BCLog::NET, "Unable to set TCP_NODELAY on a newly created socket, continuing anyway\n");
+ }
return sock;
}
-std::function<std::unique_ptr<Sock>(const CService&)> CreateSock = CreateSockTCP;
+std::function<std::unique_ptr<Sock>(const sa_family_t&)> CreateSock = CreateSockOS;
template<typename... Args>
static void LogConnectFailure(bool manual_connection, const char* fmt, const Args&... args) {
@@ -505,18 +550,10 @@ static void LogConnectFailure(bool manual_connection, const char* fmt, const Arg
}
}
-bool ConnectSocketDirectly(const CService &addrConnect, const Sock& sock, int nTimeout, bool manual_connection)
+static bool ConnectToSocket(const Sock& sock, struct sockaddr* sockaddr, socklen_t len, const std::string& dest_str, bool manual_connection)
{
- // 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 connect to %s: unsupported network\n", addrConnect.ToStringAddrPort());
- return false;
- }
-
- // Connect to the addrConnect service on the hSocket socket.
- if (sock.Connect(reinterpret_cast<struct sockaddr*>(&sockaddr), len) == SOCKET_ERROR) {
+ // Connect to `sockaddr` using `sock`.
+ if (sock.Connect(sockaddr, len) == SOCKET_ERROR) {
int nErr = WSAGetLastError();
// WSAEINVAL is here because some legacy version of winsock uses it
if (nErr == WSAEINPROGRESS || nErr == WSAEWOULDBLOCK || nErr == WSAEINVAL)
@@ -526,13 +563,13 @@ bool ConnectSocketDirectly(const CService &addrConnect, const Sock& sock, int nT
// synchronously to check for successful connection with a timeout.
const Sock::Event requested = Sock::RECV | Sock::SEND;
Sock::Event occurred;
- if (!sock.Wait(std::chrono::milliseconds{nTimeout}, requested, &occurred)) {
+ if (!sock.Wait(std::chrono::milliseconds{nConnectTimeout}, requested, &occurred)) {
LogPrintf("wait for connect to %s failed: %s\n",
- addrConnect.ToStringAddrPort(),
+ dest_str,
NetworkErrorString(WSAGetLastError()));
return false;
} else if (occurred == 0) {
- LogPrint(BCLog::NET, "connection attempt to %s timed out\n", addrConnect.ToStringAddrPort());
+ LogPrint(BCLog::NET, "connection attempt to %s timed out\n", dest_str);
return false;
}
@@ -544,13 +581,13 @@ bool ConnectSocketDirectly(const CService &addrConnect, const Sock& sock, int nT
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.ToStringAddrPort(), NetworkErrorString(WSAGetLastError()));
+ LogPrintf("getsockopt() for %s failed: %s\n", dest_str, NetworkErrorString(WSAGetLastError()));
return false;
}
if (sockerr != 0) {
LogConnectFailure(manual_connection,
"connect() to %s failed after wait: %s",
- addrConnect.ToStringAddrPort(),
+ dest_str,
NetworkErrorString(sockerr));
return false;
}
@@ -561,13 +598,68 @@ bool ConnectSocketDirectly(const CService &addrConnect, const Sock& sock, int nT
else
#endif
{
- LogConnectFailure(manual_connection, "connect() to %s failed: %s", addrConnect.ToStringAddrPort(), NetworkErrorString(WSAGetLastError()));
+ LogConnectFailure(manual_connection, "connect() to %s failed: %s", dest_str, NetworkErrorString(WSAGetLastError()));
return false;
}
}
return true;
}
+std::unique_ptr<Sock> ConnectDirectly(const CService& dest, bool manual_connection)
+{
+ auto sock = CreateSock(dest.GetSAFamily());
+ if (!sock) {
+ LogPrintLevel(BCLog::NET, BCLog::Level::Error, "Cannot create a socket for connecting to %s\n", dest.ToStringAddrPort());
+ return {};
+ }
+
+ // Create a sockaddr from the specified service.
+ struct sockaddr_storage sockaddr;
+ socklen_t len = sizeof(sockaddr);
+ if (!dest.GetSockAddr((struct sockaddr*)&sockaddr, &len)) {
+ LogPrintf("Cannot get sockaddr for %s: unsupported network\n", dest.ToStringAddrPort());
+ return {};
+ }
+
+ if (!ConnectToSocket(*sock, (struct sockaddr*)&sockaddr, len, dest.ToStringAddrPort(), manual_connection)) {
+ return {};
+ }
+
+ return sock;
+}
+
+std::unique_ptr<Sock> Proxy::Connect() const
+{
+ if (!IsValid()) return {};
+
+ if (!m_is_unix_socket) return ConnectDirectly(proxy, /*manual_connection=*/true);
+
+#if HAVE_SOCKADDR_UN
+ auto sock = CreateSock(AF_UNIX);
+ if (!sock) {
+ LogPrintLevel(BCLog::NET, BCLog::Level::Error, "Cannot create a socket for connecting to %s\n", m_unix_socket_path);
+ return {};
+ }
+
+ const std::string path{m_unix_socket_path.substr(ADDR_PREFIX_UNIX.length())};
+
+ struct sockaddr_un addrun;
+ memset(&addrun, 0, sizeof(addrun));
+ addrun.sun_family = AF_UNIX;
+ // leave the last char in addrun.sun_path[] to be always '\0'
+ memcpy(addrun.sun_path, path.c_str(), std::min(sizeof(addrun.sun_path) - 1, path.length()));
+ socklen_t len = sizeof(addrun);
+
+ if(!ConnectToSocket(*sock, (struct sockaddr*)&addrun, len, path, /*manual_connection=*/true)) {
+ return {};
+ }
+
+ return sock;
+#else
+ return {};
+#endif
+}
+
bool SetProxy(enum Network net, const Proxy &addrProxy) {
assert(net >= 0 && net < NET_MAX);
if (!addrProxy.IsValid())
@@ -616,27 +708,32 @@ bool IsProxy(const CNetAddr &addr) {
return false;
}
-bool ConnectThroughProxy(const Proxy& proxy, const std::string& strDest, uint16_t port, const Sock& sock, int nTimeout, bool& outProxyConnectionFailed)
+std::unique_ptr<Sock> ConnectThroughProxy(const Proxy& proxy,
+ const std::string& dest,
+ uint16_t port,
+ bool& proxy_connection_failed)
{
// first connect to proxy server
- if (!ConnectSocketDirectly(proxy.proxy, sock, nTimeout, true)) {
- outProxyConnectionFailed = true;
- return false;
+ auto sock = proxy.Connect();
+ if (!sock) {
+ proxy_connection_failed = true;
+ return {};
}
+
// do socks negotiation
- if (proxy.randomize_credentials) {
+ if (proxy.m_randomize_credentials) {
ProxyCredentials random_auth;
static std::atomic_int counter(0);
random_auth.username = random_auth.password = strprintf("%i", counter++);
- if (!Socks5(strDest, port, &random_auth, sock)) {
- return false;
+ if (!Socks5(dest, port, &random_auth, *sock)) {
+ return {};
}
} else {
- if (!Socks5(strDest, port, nullptr, sock)) {
- return false;
+ if (!Socks5(dest, port, nullptr, *sock)) {
+ return {};
}
}
- return true;
+ return sock;
}
CSubNet LookupSubNet(const std::string& subnet_str)