|
|
|
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
|
|
|
// Copyright (c) 2009-2015 The Bitcoin Core developers
|
|
|
|
// Copyright (c) 2016-2024 The Hush developers
|
|
|
|
// Distributed under the GPLv3 software license, see the accompanying
|
|
|
|
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include "config/bitcoin-config.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "netbase.h"
|
|
|
|
|
|
|
|
#include "hash.h"
|
|
|
|
#include "sync.h"
|
|
|
|
#include "uint256.h"
|
|
|
|
#include "random.h"
|
|
|
|
#include "util.h"
|
|
|
|
#include "util/strencodings.h"
|
|
|
|
#include "utiltime.h"
|
|
|
|
|
|
|
|
#ifdef HAVE_GETADDRINFO_A
|
|
|
|
#include <netdb.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef WIN32
|
|
|
|
#if HAVE_INET_PTON
|
|
|
|
#include <arpa/inet.h>
|
|
|
|
#endif
|
|
|
|
#include <fcntl.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <boost/algorithm/string/case_conv.hpp> // for to_lower()
|
|
|
|
#include <boost/algorithm/string/predicate.hpp> // for startswith() and endswith()
|
Split up util.cpp/h
Split up util.cpp/h into:
- string utilities (hex, base32, base64): no internal dependencies, no dependency on boost (apart from foreach)
- money utilities (parsesmoney, formatmoney)
- time utilities (gettime*, sleep, format date):
- and the rest (logging, argument parsing, config file parsing)
The latter is basically the environment and OS handling,
and is stripped of all utility functions, so we may want to
rename it to something else than util.cpp/h for clarity (Matt suggested
osinterface).
Breaks dependency of sha256.cpp on all the things pulled in by util.
10 years ago
|
|
|
#include <boost/thread.hpp>
|
|
|
|
|
|
|
|
#if !defined(HAVE_MSG_NOSIGNAL) && !defined(MSG_NOSIGNAL)
|
|
|
|
#define MSG_NOSIGNAL 0
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Settings
|
|
|
|
static proxyType proxyInfo[NET_MAX];
|
|
|
|
static proxyType nameProxy;
|
|
|
|
static CCriticalSection cs_proxyInfos;
|
|
|
|
int nConnectTimeout = DEFAULT_CONNECT_TIMEOUT;
|
|
|
|
bool fNameLookup = DEFAULT_NAME_LOOKUP;
|
|
|
|
|
|
|
|
// Need ample time for negotiation for very slow proxies such as Tor (milliseconds)
|
|
|
|
static const int SOCKS5_RECV_TIMEOUT = 20 * 1000;
|
|
|
|
|
|
|
|
enum Network ParseNetwork(std::string net) {
|
|
|
|
boost::to_lower(net);
|
|
|
|
if (net == "ipv4") return NET_IPV4;
|
|
|
|
if (net == "ipv6") return NET_IPV6;
|
|
|
|
if (net == "i2p") return NET_I2P;
|
|
|
|
if (net == "tor" || net == "onion") return NET_ONION;
|
|
|
|
return NET_UNROUTABLE;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string GetNetworkName(enum Network net) {
|
|
|
|
switch(net)
|
|
|
|
{
|
|
|
|
case NET_IPV4: return "ipv4";
|
|
|
|
case NET_IPV6: return "ipv6";
|
|
|
|
case NET_I2P: return "i2p";
|
|
|
|
case NET_ONION: return "onion";
|
|
|
|
default: return "";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool static LookupIntern(const char *pszName, std::vector<CNetAddr>& vIP, unsigned int nMaxSolutions, bool fAllowLookup)
|
|
|
|
{
|
|
|
|
vIP.clear();
|
|
|
|
|
|
|
|
{
|
|
|
|
CNetAddr addr;
|
|
|
|
if (addr.SetSpecial(std::string(pszName))) {
|
|
|
|
vIP.push_back(addr);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct addrinfo aiHint;
|
|
|
|
memset(&aiHint, 0, sizeof(struct addrinfo));
|
|
|
|
|
|
|
|
aiHint.ai_socktype = SOCK_STREAM;
|
|
|
|
aiHint.ai_protocol = IPPROTO_TCP;
|
|
|
|
aiHint.ai_family = AF_UNSPEC;
|
|
|
|
#ifdef WIN32
|
|
|
|
aiHint.ai_flags = fAllowLookup ? 0 : AI_NUMERICHOST;
|
|
|
|
#else
|
|
|
|
aiHint.ai_flags = fAllowLookup ? AI_ADDRCONFIG : AI_NUMERICHOST;
|
|
|
|
#endif
|
|
|
|
struct addrinfo *aiRes = NULL;
|
|
|
|
int nErr = getaddrinfo(pszName, NULL, &aiHint, &aiRes);
|
|
|
|
if (nErr)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
struct addrinfo *aiTrav = aiRes;
|
|
|
|
while (aiTrav != NULL && (nMaxSolutions == 0 || vIP.size() < nMaxSolutions))
|
|
|
|
{
|
|
|
|
CNetAddr resolved;
|
|
|
|
if (aiTrav->ai_family == AF_INET)
|
|
|
|
{
|
|
|
|
assert(aiTrav->ai_addrlen >= sizeof(sockaddr_in));
|
|
|
|
resolved = CNetAddr(((struct sockaddr_in*)(aiTrav->ai_addr))->sin_addr);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (aiTrav->ai_family == AF_INET6)
|
|
|
|
{
|
|
|
|
assert(aiTrav->ai_addrlen >= sizeof(sockaddr_in6));
|
|
|
|
struct sockaddr_in6* s6 = (struct sockaddr_in6*) aiTrav->ai_addr;
|
|
|
|
resolved = CNetAddr(s6->sin6_addr, s6->sin6_scope_id);
|
|
|
|
}
|
|
|
|
/* Never allow resolving to an internal address. Consider any such result invalid */
|
|
|
|
if (!resolved.IsInternal()) {
|
|
|
|
vIP.push_back(resolved);
|
|
|
|
}
|
|
|
|
|
|
|
|
aiTrav = aiTrav->ai_next;
|
|
|
|
}
|
|
|
|
|
|
|
|
freeaddrinfo(aiRes);
|
|
|
|
|
|
|
|
return (vIP.size() > 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool LookupHost(const char *pszName, std::vector<CNetAddr>& vIP, unsigned int nMaxSolutions, bool fAllowLookup)
|
|
|
|
{
|
|
|
|
std::string strHost(pszName);
|
|
|
|
if (strHost.empty())
|
|
|
|
return false;
|
|
|
|
if (boost::algorithm::starts_with(strHost, "[") && boost::algorithm::ends_with(strHost, "]"))
|
|
|
|
{
|
|
|
|
strHost = strHost.substr(1, strHost.size() - 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
return LookupIntern(strHost.c_str(), vIP, nMaxSolutions, fAllowLookup);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool LookupHost(const char *pszName, CNetAddr& addr, bool fAllowLookup)
|
|
|
|
{
|
|
|
|
std::vector<CNetAddr> vIP;
|
|
|
|
LookupHost(pszName, vIP, 1, fAllowLookup);
|
|
|
|
if(vIP.empty())
|
|
|
|
return false;
|
|
|
|
addr = vIP.front();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Lookup(const char *pszName, std::vector<CService>& vAddr, int portDefault, bool fAllowLookup, unsigned int nMaxSolutions)
|
|
|
|
{
|
|
|
|
if (pszName[0] == 0)
|
|
|
|
return false;
|
|
|
|
int port = portDefault;
|
|
|
|
std::string hostname = "";
|
|
|
|
SplitHostPort(std::string(pszName), port, hostname);
|
|
|
|
|
|
|
|
std::vector<CNetAddr> vIP;
|
|
|
|
bool fRet = LookupIntern(hostname.c_str(), vIP, nMaxSolutions, fAllowLookup);
|
|
|
|
if (!fRet)
|
|
|
|
return false;
|
|
|
|
vAddr.resize(vIP.size());
|
|
|
|
for (unsigned int i = 0; i < vIP.size(); i++)
|
|
|
|
vAddr[i] = CService(vIP[i], port);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Lookup(const char *pszName, CService& addr, int portDefault, bool fAllowLookup)
|
|
|
|
{
|
|
|
|
std::vector<CService> vService;
|
|
|
|
bool fRet = Lookup(pszName, vService, portDefault, fAllowLookup, 1);
|
|
|
|
if (!fRet)
|
|
|
|
return false;
|
|
|
|
addr = vService[0];
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
CService LookupNumeric(const char *pszName, int portDefault)
|
|
|
|
{
|
|
|
|
CService addr;
|
|
|
|
// "1.2:345" will fail to resolve the ip, but will still set the port.
|
|
|
|
// If the ip fails to resolve, re-init the result.
|
|
|
|
if(!Lookup(pszName, addr, portDefault, false))
|
|
|
|
addr = CService();
|
|
|
|
return addr;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Read bytes from socket. This will either read the full number of bytes requested
|
|
|
|
* or return False on error or timeout.
|
|
|
|
* This function can be interrupted by boost thread interrupt.
|
|
|
|
*
|
|
|
|
* @param data Buffer to receive into
|
|
|
|
* @param len Length of data to receive
|
|
|
|
* @param timeout Timeout in milliseconds for receive operation
|
|
|
|
*
|
|
|
|
* @note This function requires that hSocket is in non-blocking mode.
|
|
|
|
*/
|
|
|
|
bool static InterruptibleRecv(char* data, size_t len, int timeout, SOCKET& hSocket)
|
|
|
|
{
|
|
|
|
int64_t curTime = GetTimeMillis();
|
|
|
|
int64_t endTime = curTime + timeout;
|
|
|
|
// Maximum time to wait in one select call. It will take up until this time (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, data, len, 0); // Optimistically try the recv first
|
|
|
|
if (ret > 0) {
|
|
|
|
len -= ret;
|
|
|
|
data += ret;
|
|
|
|
} else if (ret == 0) { // Unexpected disconnection
|
|
|
|
return false;
|
|
|
|
} else { // Other error or blocking
|
|
|
|
int nErr = WSAGetLastError();
|
|
|
|
if (nErr == WSAEINPROGRESS || nErr == WSAEWOULDBLOCK || nErr == WSAEINVAL) {
|
|
|
|
if (!IsSelectableSocket(hSocket)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
struct timeval tval = MillisToTimeval(std::min(endTime - curTime, maxWait));
|
|
|
|
fd_set fdset;
|
|
|
|
FD_ZERO(&fdset);
|
|
|
|
FD_SET(hSocket, &fdset);
|
|
|
|
int nRet = select(hSocket + 1, &fdset, NULL, NULL, &tval);
|
|
|
|
if (nRet == SOCKET_ERROR) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
boost::this_thread::interruption_point();
|
|
|
|
curTime = GetTimeMillis();
|
|
|
|
}
|
|
|
|
return len == 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct ProxyCredentials
|
|
|
|
{
|
|
|
|
std::string username;
|
|
|
|
std::string password;
|
|
|
|
};
|
|
|
|
|
|
|
|
std::string Socks5ErrorString(int err)
|
|
|
|
{
|
|
|
|
switch(err) {
|
|
|
|
case 0x01: return "general failure";
|
|
|
|
case 0x02: return "connection not allowed";
|
|
|
|
case 0x03: return "network unreachable";
|
|
|
|
case 0x04: return "host unreachable";
|
|
|
|
case 0x05: return "connection refused";
|
|
|
|
case 0x06: return "TTL expired";
|
|
|
|
case 0x07: return "protocol error";
|
|
|
|
case 0x08: return "address type not supported";
|
|
|
|
default: return "unknown";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Connect using SOCKS5 (as described in RFC1928) */
|
|
|
|
static bool Socks5(const std::string& strDest, int port, const ProxyCredentials *auth, SOCKET& hSocket)
|
|
|
|
{
|
|
|
|
LogPrint("net", "SOCKS5 connecting %s\n", strDest);
|
|
|
|
if (strDest.size() > 255) {
|
|
|
|
CloseSocket(hSocket);
|
|
|
|
return error("Hostname too long");
|
|
|
|
}
|
|
|
|
// Accepted authentication methods
|
|
|
|
std::vector<uint8_t> vSocks5Init;
|
|
|
|
vSocks5Init.push_back(0x05);
|
|
|
|
if (auth) {
|
|
|
|
vSocks5Init.push_back(0x02); // # METHODS
|
|
|
|
vSocks5Init.push_back(0x00); // X'00' NO AUTHENTICATION REQUIRED
|
|
|
|
vSocks5Init.push_back(0x02); // X'02' USERNAME/PASSWORD (RFC1929)
|
|
|
|
} else {
|
|
|
|
vSocks5Init.push_back(0x01); // # METHODS
|
|
|
|
vSocks5Init.push_back(0x00); // X'00' NO AUTHENTICATION REQUIRED
|
|
|
|
}
|
|
|
|
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");
|
|
|
|
}
|
|
|
|
char pchRet1[2];
|
|
|
|
if (!InterruptibleRecv(pchRet1, 2, SOCKS5_RECV_TIMEOUT, hSocket)) {
|
|
|
|
CloseSocket(hSocket);
|
|
|
|
LogPrintf("Socks5() connect to %s:%d failed: InterruptibleRecv() timeout or other failure\n", strDest, port);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (pchRet1[0] != 0x05) {
|
|
|
|
CloseSocket(hSocket);
|
|
|
|
return error("Proxy failed to initialize");
|
|
|
|
}
|
|
|
|
if (pchRet1[1] == 0x02 && auth) {
|
|
|
|
// Perform username/password authentication (as described in RFC1929)
|
|
|
|
std::vector<uint8_t> vAuth;
|
|
|
|
vAuth.push_back(0x01);
|
|
|
|
if (auth->username.size() > 255 || auth->password.size() > 255)
|
|
|
|
return error("Proxy username or password too long");
|
|
|
|
vAuth.push_back(auth->username.size());
|
|
|
|
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);
|
|
|
|
if (ret != (ssize_t)vAuth.size()) {
|
|
|
|
CloseSocket(hSocket);
|
|
|
|
return error("Error sending authentication to proxy");
|
|
|
|
}
|
|
|
|
LogPrint("proxy", "SOCKS5 sending proxy authentication %s:%s\n", auth->username, auth->password);
|
|
|
|
char pchRetA[2];
|
|
|
|
if (!InterruptibleRecv(pchRetA, 2, SOCKS5_RECV_TIMEOUT, hSocket)) {
|
|
|
|
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] == 0x00) {
|
|
|
|
// Perform no authentication
|
|
|
|
} else {
|
|
|
|
CloseSocket(hSocket);
|
|
|
|
return error("Proxy requested wrong authentication method %02x", pchRet1[1]);
|
|
|
|
}
|
|
|
|
std::vector<uint8_t> vSocks5;
|
|
|
|
vSocks5.push_back(0x05); // VER protocol version
|
|
|
|
vSocks5.push_back(0x01); // CMD CONNECT
|
|
|
|
vSocks5.push_back(0x00); // RSV Reserved
|
|
|
|
vSocks5.push_back(0x03); // ATYP DOMAINNAME
|
|
|
|
vSocks5.push_back(strDest.size()); // Length<=255 is checked at beginning of function
|
|
|
|
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);
|
|
|
|
if (ret != (ssize_t)vSocks5.size()) {
|
|
|
|
CloseSocket(hSocket);
|
|
|
|
return error("Error sending to proxy");
|
|
|
|
}
|
|
|
|
char pchRet2[4];
|
|
|
|
if (!InterruptibleRecv(pchRet2, 4, SOCKS5_RECV_TIMEOUT, hSocket)) {
|
|
|
|
CloseSocket(hSocket);
|
|
|
|
return error("Error reading proxy response");
|
|
|
|
}
|
|
|
|
if (pchRet2[0] != 0x05) {
|
|
|
|
CloseSocket(hSocket);
|
|
|
|
return error("Proxy failed to accept request");
|
|
|
|
}
|
|
|
|
if (pchRet2[1] != 0x00) {
|
|
|
|
// 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) {
|
|
|
|
CloseSocket(hSocket);
|
|
|
|
return error("Error: malformed proxy response");
|
|
|
|
}
|
|
|
|
char pchRet3[256];
|
|
|
|
switch (pchRet2[3])
|
|
|
|
{
|
|
|
|
case 0x01: ret = InterruptibleRecv(pchRet3, 4, SOCKS5_RECV_TIMEOUT, hSocket); break;
|
|
|
|
case 0x04: ret = InterruptibleRecv(pchRet3, 16, SOCKS5_RECV_TIMEOUT, hSocket); break;
|
|
|
|
case 0x03:
|
|
|
|
{
|
|
|
|
ret = InterruptibleRecv(pchRet3, 1, SOCKS5_RECV_TIMEOUT, hSocket);
|
|
|
|
if (!ret) {
|
|
|
|
CloseSocket(hSocket);
|
|
|
|
return error("Error reading from proxy");
|
|
|
|
}
|
|
|
|
int nRecv = pchRet3[0];
|
|
|
|
ret = InterruptibleRecv(pchRet3, nRecv, SOCKS5_RECV_TIMEOUT, hSocket);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default: CloseSocket(hSocket); return error("Error: malformed proxy response");
|
|
|
|
}
|
|
|
|
if (!ret) {
|
|
|
|
CloseSocket(hSocket);
|
|
|
|
return error("Error reading from proxy");
|
|
|
|
}
|
|
|
|
if (!InterruptibleRecv(pchRet3, 2, SOCKS5_RECV_TIMEOUT, hSocket)) {
|
|
|
|
CloseSocket(hSocket);
|
|
|
|
return error("Error reading from proxy");
|
|
|
|
}
|
|
|
|
LogPrint("net", "SOCKS5 connected %s\n", strDest);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
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 (!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 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 nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef SO_NOSIGPIPE
|
|
|
|
int set = 1;
|
|
|
|
// Set the no-sigpipe option on the socket for BSD systems, other UNIXes
|
|
|
|
// should use the MSG_NOSIGNAL flag for every send.
|
|
|
|
setsockopt(hSocket, SOL_SOCKET, SO_NOSIGPIPE, (void*)&set, sizeof(int));
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Set the no-delay option (disable Nagle's algorithm) on the TCP socket.
|
|
|
|
SetSocketNoDelay(hSocket);
|
|
|
|
|
|
|
|
// Set the non-blocking option on the socket.
|
|
|
|
if (!SetSocketNonBlocking(hSocket, true)) {
|
|
|
|
CloseSocket(hSocket);
|
|
|
|
LogPrintf("Error setting socket to non-blocking: %s\n", NetworkErrorString(WSAGetLastError()));
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
return std::unique_ptr<Sock>(new Sock(hSocket));
|
|
|
|
}
|
|
|
|
|
|
|
|
std::function<std::unique_ptr<Sock>(const CService&)> CreateSock = CreateSockTCP;
|
|
|
|
|
|
|
|
bool ConnectSocketDirectly(const CService &addrConnect, const Sock& sock, int nTimeout)
|
|
|
|
{
|
|
|
|
// Create a sockaddr from the specified service.
|
|
|
|
struct sockaddr_storage sockaddr;
|
|
|
|
socklen_t len = sizeof(sockaddr);
|
|
|
|
if (sock.Get() == INVALID_SOCKET) {
|
|
|
|
LogPrint("net","Cannot connect to %s: invalid socket\n", addrConnect.ToString());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (!addrConnect.GetSockAddr((struct sockaddr*)&sockaddr, &len)) {
|
|
|
|
LogPrint("net","Cannot connect to %s: unsupported network\n", addrConnect.ToString());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Connect to the addrConnect service on the hSocket socket.
|
|
|
|
if (sock.Connect(reinterpret_cast<struct sockaddr*>(&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)
|
|
|
|
{
|
|
|
|
// Connection didn't actually fail, but is being established
|
|
|
|
// asynchronously. Thus, use async I/O api (select/poll)
|
|
|
|
// 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)) {
|
|
|
|
LogPrint("net","wait for connect to %s failed: %s\n",
|
|
|
|
addrConnect.ToString(),
|
|
|
|
NetworkErrorString(WSAGetLastError()));
|
|
|
|
return false;
|
|
|
|
} else if (occurred == 0) {
|
|
|
|
LogPrint("net", "connection attempt to %s timed out\n", addrConnect.ToString());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Even if the wait was successful, the connect might not
|
|
|
|
// 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
|
|
|
|
// sockerr here.
|
|
|
|
int sockerr;
|
|
|
|
socklen_t sockerr_len = sizeof(sockerr);
|
|
|
|
if (sock.GetSockOpt(SOL_SOCKET, SO_ERROR, (sockopt_arg_type)&sockerr, &sockerr_len) ==
|
|
|
|
SOCKET_ERROR) {
|
|
|
|
LogPrint("net","getsockopt() for %s failed\n", addrConnect.ToString());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (sockerr != 0) {
|
|
|
|
LogPrint("net", "connect() to %s failed", addrConnect.ToString());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#ifdef WIN32
|
|
|
|
else if (WSAGetLastError() != WSAEISCONN)
|
|
|
|
#else
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
LogPrint("net", "connect() to %s failed", addrConnect.ToString());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRet, int nTimeout)
|
|
|
|
{
|
|
|
|
hSocketRet = INVALID_SOCKET;
|
|
|
|
|
|
|
|
struct sockaddr_storage sockaddr;
|
|
|
|
socklen_t len = sizeof(sockaddr);
|
|
|
|
if (!addrConnect.GetSockAddr((struct sockaddr*)&sockaddr, &len)) {
|
|
|
|
LogPrint("net","Cannot connect to %s: unsupported network\n", addrConnect.ToString());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
SOCKET hSocket = socket(((struct sockaddr*)&sockaddr)->sa_family, SOCK_STREAM, IPPROTO_TCP);
|
|
|
|
if (hSocket == INVALID_SOCKET)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
int set = 1;
|
|
|
|
#ifdef SO_NOSIGPIPE
|
|
|
|
// Different way of disabling SIGPIPE on BSD
|
|
|
|
setsockopt(hSocket, SOL_SOCKET, SO_NOSIGPIPE, (void*)&set, sizeof(int));
|
|
|
|
#endif
|
|
|
|
|
|
|
|
//Disable Nagle's algorithm
|
|
|
|
#ifdef WIN32
|
|
|
|
setsockopt(hSocket, IPPROTO_TCP, TCP_NODELAY, (const char*)&set, sizeof(int));
|
|
|
|
#else
|
|
|
|
setsockopt(hSocket, IPPROTO_TCP, TCP_NODELAY, (void*)&set, sizeof(int));
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Set to non-blocking
|
|
|
|
if (!SetSocketNonBlocking(hSocket, true))
|
|
|
|
return error("ConnectSocketDirectly: Setting socket to non-blocking failed, error %s\n", NetworkErrorString(WSAGetLastError()));
|
|
|
|
|
|
|
|
if (connect(hSocket, (struct sockaddr*)&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)
|
|
|
|
{
|
|
|
|
struct timeval timeout = MillisToTimeval(nTimeout);
|
|
|
|
fd_set fdset;
|
|
|
|
FD_ZERO(&fdset);
|
|
|
|
FD_SET(hSocket, &fdset);
|
|
|
|
int nRet = select(hSocket + 1, NULL, &fdset, NULL, &timeout);
|
|
|
|
if (nRet == 0)
|
|
|
|
{
|
|
|
|
LogPrint("net", "connection to %s timeout\n", addrConnect.ToString());
|
|
|
|
CloseSocket(hSocket);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (nRet == SOCKET_ERROR)
|
|
|
|
{
|
|
|
|
LogPrint("net","select() for %s failed: %s\n", addrConnect.ToString(), NetworkErrorString(WSAGetLastError()));
|
|
|
|
CloseSocket(hSocket);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
socklen_t nRetSize = sizeof(nRet);
|
|
|
|
if (getsockopt(hSocket, SOL_SOCKET, SO_ERROR, (sockopt_arg_type)&nRet, &nRetSize) == SOCKET_ERROR)
|
|
|
|
{
|
|
|
|
LogPrint("net","getsockopt() for %s failed: %s\n", addrConnect.ToString(), NetworkErrorString(WSAGetLastError()));
|
|
|
|
CloseSocket(hSocket);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (nRet != 0)
|
|
|
|
{
|
|
|
|
LogPrint("net","connect() to %s failed after select(): %s\n", addrConnect.ToString(), NetworkErrorString(nRet));
|
|
|
|
CloseSocket(hSocket);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#ifdef WIN32
|
|
|
|
else if (WSAGetLastError() != WSAEISCONN)
|
|
|
|
#else
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
LogPrint("net","connect() to %s failed: %s\n", addrConnect.ToString(), NetworkErrorString(WSAGetLastError()));
|
|
|
|
CloseSocket(hSocket);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
hSocketRet = hSocket;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SetProxy(enum Network net, const proxyType &addrProxy) {
|
|
|
|
assert(net >= 0 && net < NET_MAX);
|
|
|
|
if (!addrProxy.IsValid())
|
|
|
|
return false;
|
|
|
|
LOCK(cs_proxyInfos);
|
|
|
|
proxyInfo[net] = addrProxy;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool GetProxy(enum Network net, proxyType &proxyInfoOut) {
|
|
|
|
assert(net >= 0 && net < NET_MAX);
|
|
|
|
LOCK(cs_proxyInfos);
|
|
|
|
if (!proxyInfo[net].IsValid())
|
|
|
|
return false;
|
|
|
|
proxyInfoOut = proxyInfo[net];
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SetNameProxy(const proxyType &addrProxy) {
|
|
|
|
if (!addrProxy.IsValid())
|
|
|
|
return false;
|
|
|
|
LOCK(cs_proxyInfos);
|
|
|
|
nameProxy = addrProxy;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool GetNameProxy(proxyType &nameProxyOut) {
|
|
|
|
LOCK(cs_proxyInfos);
|
|
|
|
if(!nameProxy.IsValid())
|
|
|
|
return false;
|
|
|
|
nameProxyOut = nameProxy;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool HaveNameProxy() {
|
|
|
|
LOCK(cs_proxyInfos);
|
|
|
|
return nameProxy.IsValid();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IsProxy(const CNetAddr &addr) {
|
|
|
|
LOCK(cs_proxyInfos);
|
|
|
|
for (int i = 0; i < NET_MAX; i++) {
|
|
|
|
if (addr == static_cast<CNetAddr>(proxyInfo[i].proxy))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool ConnectThroughProxy(const proxyType &proxy, const std::string& strDest, int port, SOCKET& hSocketRet, int nTimeout, bool *outProxyConnectionFailed)
|
|
|
|
{
|
|
|
|
SOCKET hSocket = INVALID_SOCKET;
|
|
|
|
// first connect to proxy server
|
|
|
|
if (!ConnectSocketDirectly(proxy.proxy, hSocket, nTimeout)) {
|
|
|
|
if (outProxyConnectionFailed)
|
|
|
|
*outProxyConnectionFailed = true;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// do socks negotiation
|
|
|
|
if (proxy.randomize_credentials) {
|
|
|
|
ProxyCredentials random_auth;
|
|
|
|
random_auth.username = strprintf("%i", insecure_rand());
|
|
|
|
random_auth.password = strprintf("%i", insecure_rand());
|
|
|
|
if (!Socks5(strDest, (unsigned short)port, &random_auth, hSocket))
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
if (!Socks5(strDest, (unsigned short)port, 0, hSocket))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
hSocketRet = hSocket;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ConnectSocket(const CService &addrDest, SOCKET& hSocketRet, int nTimeout, bool *outProxyConnectionFailed)
|
|
|
|
{
|
|
|
|
proxyType proxy;
|
|
|
|
if (outProxyConnectionFailed)
|
|
|
|
*outProxyConnectionFailed = false;
|
|
|
|
|
|
|
|
if (GetProxy(addrDest.GetNetwork(), proxy))
|
|
|
|
return ConnectThroughProxy(proxy, addrDest.ToStringIP(), addrDest.GetPort(), hSocketRet, nTimeout, outProxyConnectionFailed);
|
|
|
|
else // no proxy needed (none set for target network)
|
|
|
|
return ConnectSocketDirectly(addrDest, hSocketRet, nTimeout);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ConnectSocketByName(CService &addr, SOCKET& hSocketRet, const char *pszDest, int portDefault, int nTimeout, bool *outProxyConnectionFailed)
|
|
|
|
{
|
|
|
|
std::string strDest;
|
|
|
|
int port = portDefault;
|
|
|
|
|
|
|
|
if (outProxyConnectionFailed)
|
|
|
|
*outProxyConnectionFailed = false;
|
|
|
|
|
|
|
|
SplitHostPort(std::string(pszDest), port, strDest);
|
|
|
|
|
|
|
|
proxyType proxy;
|
|
|
|
GetNameProxy(proxy);
|
|
|
|
|
|
|
|
std::vector<CService> addrResolved;
|
|
|
|
if (Lookup(strDest.c_str(), addrResolved, port, fNameLookup && !HaveNameProxy(), 256)) {
|
|
|
|
if (addrResolved.size() > 0) {
|
|
|
|
addr = addrResolved[GetRand(addrResolved.size())];
|
|
|
|
return ConnectSocket(addr, hSocketRet, nTimeout);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
addr = CService();
|
|
|
|
|
|
|
|
if (!HaveNameProxy())
|
|
|
|
return false;
|
|
|
|
return ConnectThroughProxy(proxy, strDest, port, hSocketRet, nTimeout, outProxyConnectionFailed);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool LookupSubNet(const char* pszName, CSubNet& ret)
|
|
|
|
{
|
|
|
|
std::string strSubnet(pszName);
|
|
|
|
size_t slash = strSubnet.find_last_of('/');
|
|
|
|
std::vector<CNetAddr> vIP;
|
|
|
|
|
|
|
|
std::string strAddress = strSubnet.substr(0, slash);
|
|
|
|
if (LookupHost(strAddress.c_str(), vIP, 1, false))
|
|
|
|
{
|
|
|
|
CNetAddr network = vIP[0];
|
|
|
|
if (slash != strSubnet.npos)
|
|
|
|
{
|
|
|
|
std::string strNetmask = strSubnet.substr(slash + 1);
|
|
|
|
uint8_t n;
|
|
|
|
if (ParseUInt8(strNetmask, &n)) {
|
|
|
|
// If valid number, assume CIDR variable-length subnet masking
|
|
|
|
ret = CSubNet(network, n);
|
|
|
|
return ret.IsValid();
|
|
|
|
}
|
|
|
|
else // If not a valid number, try full netmask syntax
|
|
|
|
{
|
|
|
|
// Never allow lookup for netmask
|
|
|
|
if (LookupHost(strNetmask.c_str(), vIP, 1, false)) {
|
|
|
|
ret = CSubNet(network, vIP[0]);
|
|
|
|
return ret.IsValid();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ret = CSubNet(network);
|
|
|
|
return ret.IsValid();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SetSocketNonBlocking(SOCKET& hSocket, bool fNonBlocking)
|
|
|
|
{
|
|
|
|
if (fNonBlocking) {
|
|
|
|
#ifdef WIN32
|
|
|
|
u_long nOne = 1;
|
|
|
|
if (ioctlsocket(hSocket, FIONBIO, &nOne) == SOCKET_ERROR) {
|
|
|
|
#else
|
|
|
|
int fFlags = fcntl(hSocket, F_GETFL, 0);
|
|
|
|
if (fcntl(hSocket, F_SETFL, fFlags | O_NONBLOCK) == SOCKET_ERROR) {
|
|
|
|
#endif
|
|
|
|
CloseSocket(hSocket);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
#ifdef WIN32
|
|
|
|
u_long nZero = 0;
|
|
|
|
if (ioctlsocket(hSocket, FIONBIO, &nZero) == SOCKET_ERROR) {
|
|
|
|
#else
|
|
|
|
int fFlags = fcntl(hSocket, F_GETFL, 0);
|
|
|
|
if (fcntl(hSocket, F_SETFL, fFlags & ~O_NONBLOCK) == SOCKET_ERROR) {
|
|
|
|
#endif
|
|
|
|
CloseSocket(hSocket);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SetSocketNoDelay(const SOCKET& hSocket)
|
|
|
|
{
|
|
|
|
int set = 1;
|
|
|
|
int rc = setsockopt(hSocket, IPPROTO_TCP, TCP_NODELAY, (const char*)&set, sizeof(int));
|
|
|
|
return rc == 0;
|
|
|
|
}
|