2012-01-03 23:33:31 +01:00
|
|
|
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
2015-12-13 14:51:43 +01:00
|
|
|
// Copyright (c) 2009-2015 The Bitcoin Core developers
|
2014-12-13 05:09:33 +01:00
|
|
|
// Distributed under the MIT software license, see the accompanying
|
2012-05-18 16:02:28 +02:00
|
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
2012-01-03 23:33:31 +01:00
|
|
|
|
2014-06-26 20:55:39 +02:00
|
|
|
#ifdef HAVE_CONFIG_H
|
2015-04-03 00:51:08 +02:00
|
|
|
#include "config/dash-config.h"
|
2014-06-26 20:55:39 +02:00
|
|
|
#endif
|
|
|
|
|
2012-01-03 23:33:31 +01:00
|
|
|
#include "netbase.h"
|
2013-04-13 07:13:08 +02:00
|
|
|
|
2012-12-18 20:56:21 +01:00
|
|
|
#include "hash.h"
|
2013-04-13 07:13:08 +02:00
|
|
|
#include "sync.h"
|
|
|
|
#include "uint256.h"
|
2015-03-16 16:30:49 +01:00
|
|
|
#include "random.h"
|
2013-04-13 07:13:08 +02:00
|
|
|
#include "util.h"
|
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.
2014-08-21 16:11:09 +02:00
|
|
|
#include "utilstrencodings.h"
|
2013-04-13 07:13:08 +02:00
|
|
|
|
2017-08-09 18:06:31 +02:00
|
|
|
#include <atomic>
|
2013-04-13 07:13:08 +02:00
|
|
|
|
2012-01-03 23:33:31 +01:00
|
|
|
#ifndef WIN32
|
2013-07-17 10:51:40 +02:00
|
|
|
#include <fcntl.h>
|
2012-01-03 23:33:31 +01:00
|
|
|
#endif
|
|
|
|
|
2012-05-13 00:40:30 +02:00
|
|
|
#include <boost/algorithm/string/case_conv.hpp> // for to_lower()
|
2012-10-02 21:36:39 +02:00
|
|
|
#include <boost/algorithm/string/predicate.hpp> // for startswith() and endswith()
|
2012-01-03 23:33:31 +01:00
|
|
|
|
2017-03-16 12:02:54 +01:00
|
|
|
#if !defined(HAVE_MSG_NOSIGNAL)
|
2013-05-28 01:55:01 +02:00
|
|
|
#define MSG_NOSIGNAL 0
|
|
|
|
#endif
|
|
|
|
|
2012-01-03 23:33:31 +01:00
|
|
|
// Settings
|
2012-05-24 19:02:21 +02:00
|
|
|
static proxyType proxyInfo[NET_MAX];
|
2015-03-16 16:30:49 +01:00
|
|
|
static proxyType nameProxy;
|
2012-09-23 12:55:05 +02:00
|
|
|
static CCriticalSection cs_proxyInfos;
|
2014-09-25 09:01:54 +02:00
|
|
|
int nConnectTimeout = DEFAULT_CONNECT_TIMEOUT;
|
2015-11-09 19:16:38 +01:00
|
|
|
bool fNameLookup = DEFAULT_NAME_LOOKUP;
|
2012-01-03 23:33:31 +01:00
|
|
|
|
2014-09-08 13:49:56 +02:00
|
|
|
// Need ample time for negotiation for very slow proxies such as Tor (milliseconds)
|
|
|
|
static const int SOCKS5_RECV_TIMEOUT = 20 * 1000;
|
2017-08-09 18:06:31 +02:00
|
|
|
static std::atomic<bool> interruptSocks5Recv(false);
|
2014-09-08 13:49:56 +02:00
|
|
|
|
2012-04-10 20:22:04 +02:00
|
|
|
enum Network ParseNetwork(std::string net) {
|
2012-05-13 00:40:30 +02:00
|
|
|
boost::to_lower(net);
|
2012-04-10 20:22:04 +02:00
|
|
|
if (net == "ipv4") return NET_IPV4;
|
|
|
|
if (net == "ipv6") return NET_IPV6;
|
2014-07-30 15:27:03 +02:00
|
|
|
if (net == "tor" || net == "onion") return NET_TOR;
|
2012-04-10 20:22:04 +02:00
|
|
|
return NET_UNROUTABLE;
|
|
|
|
}
|
|
|
|
|
2014-07-30 15:32:36 +02:00
|
|
|
std::string GetNetworkName(enum Network net) {
|
|
|
|
switch(net)
|
|
|
|
{
|
|
|
|
case NET_IPV4: return "ipv4";
|
|
|
|
case NET_IPV6: return "ipv6";
|
|
|
|
case NET_TOR: return "onion";
|
|
|
|
default: return "";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-04-15 22:58:32 +02:00
|
|
|
bool static LookupIntern(const char *pszName, std::vector<CNetAddr>& vIP, unsigned int nMaxSolutions, bool fAllowLookup)
|
2012-01-03 23:33:31 +01:00
|
|
|
{
|
|
|
|
vIP.clear();
|
2012-04-29 02:11:56 +02:00
|
|
|
|
|
|
|
{
|
|
|
|
CNetAddr addr;
|
|
|
|
if (addr.SetSpecial(std::string(pszName))) {
|
|
|
|
vIP.push_back(addr);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-01-13 02:02:47 +01:00
|
|
|
struct addrinfo aiHint;
|
|
|
|
memset(&aiHint, 0, sizeof(struct addrinfo));
|
2016-12-02 05:48:13 +01:00
|
|
|
|
2012-01-03 23:33:31 +01:00
|
|
|
aiHint.ai_socktype = SOCK_STREAM;
|
|
|
|
aiHint.ai_protocol = IPPROTO_TCP;
|
|
|
|
aiHint.ai_family = AF_UNSPEC;
|
2013-04-27 14:27:05 +02:00
|
|
|
#ifdef WIN32
|
2012-06-19 02:22:09 +02:00
|
|
|
aiHint.ai_flags = fAllowLookup ? 0 : AI_NUMERICHOST;
|
2012-01-03 23:33:31 +01:00
|
|
|
#else
|
2012-06-19 02:22:09 +02:00
|
|
|
aiHint.ai_flags = fAllowLookup ? AI_ADDRCONFIG : AI_NUMERICHOST;
|
2012-01-03 23:33:31 +01:00
|
|
|
#endif
|
2019-08-06 05:08:33 +02:00
|
|
|
struct addrinfo *aiRes = nullptr;
|
|
|
|
int nErr = getaddrinfo(pszName, nullptr, &aiHint, &aiRes);
|
2012-01-03 23:33:31 +01:00
|
|
|
if (nErr)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
struct addrinfo *aiTrav = aiRes;
|
2019-08-06 05:08:33 +02:00
|
|
|
while (aiTrav != nullptr && (nMaxSolutions == 0 || vIP.size() < nMaxSolutions))
|
2012-01-03 23:33:31 +01:00
|
|
|
{
|
2017-06-24 12:16:41 +02:00
|
|
|
CNetAddr resolved;
|
2012-01-03 23:33:31 +01:00
|
|
|
if (aiTrav->ai_family == AF_INET)
|
|
|
|
{
|
|
|
|
assert(aiTrav->ai_addrlen >= sizeof(sockaddr_in));
|
2017-06-24 12:16:41 +02:00
|
|
|
resolved = CNetAddr(((struct sockaddr_in*)(aiTrav->ai_addr))->sin_addr);
|
2012-01-03 23:33:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if (aiTrav->ai_family == AF_INET6)
|
|
|
|
{
|
|
|
|
assert(aiTrav->ai_addrlen >= sizeof(sockaddr_in6));
|
2016-04-08 14:11:33 +02:00
|
|
|
struct sockaddr_in6* s6 = (struct sockaddr_in6*) aiTrav->ai_addr;
|
2017-06-24 12:16:41 +02:00
|
|
|
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);
|
2012-01-03 23:33:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
aiTrav = aiTrav->ai_next;
|
|
|
|
}
|
|
|
|
|
|
|
|
freeaddrinfo(aiRes);
|
|
|
|
|
|
|
|
return (vIP.size() > 0);
|
|
|
|
}
|
|
|
|
|
2012-04-15 22:58:32 +02:00
|
|
|
bool LookupHost(const char *pszName, std::vector<CNetAddr>& vIP, unsigned int nMaxSolutions, bool fAllowLookup)
|
2012-01-03 23:33:31 +01:00
|
|
|
{
|
2013-04-27 14:27:05 +02:00
|
|
|
std::string strHost(pszName);
|
|
|
|
if (strHost.empty())
|
2012-01-03 23:33:31 +01:00
|
|
|
return false;
|
2013-04-27 14:27:05 +02:00
|
|
|
if (boost::algorithm::starts_with(strHost, "[") && boost::algorithm::ends_with(strHost, "]"))
|
2012-01-03 23:33:31 +01:00
|
|
|
{
|
2013-04-27 14:27:05 +02:00
|
|
|
strHost = strHost.substr(1, strHost.size() - 2);
|
2012-01-03 23:33:31 +01:00
|
|
|
}
|
|
|
|
|
2012-10-02 21:36:39 +02:00
|
|
|
return LookupIntern(strHost.c_str(), vIP, nMaxSolutions, fAllowLookup);
|
2012-01-03 23:33:31 +01:00
|
|
|
}
|
|
|
|
|
2017-09-03 15:29:10 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2012-04-15 22:58:32 +02:00
|
|
|
bool Lookup(const char *pszName, std::vector<CService>& vAddr, int portDefault, bool fAllowLookup, unsigned int nMaxSolutions)
|
2012-01-03 23:33:31 +01:00
|
|
|
{
|
|
|
|
if (pszName[0] == 0)
|
|
|
|
return false;
|
|
|
|
int port = portDefault;
|
2012-05-30 22:44:23 +02:00
|
|
|
std::string hostname = "";
|
|
|
|
SplitHostPort(std::string(pszName), port, hostname);
|
2012-01-03 23:33:31 +01:00
|
|
|
|
|
|
|
std::vector<CNetAddr> vIP;
|
2012-05-30 22:44:23 +02:00
|
|
|
bool fRet = LookupIntern(hostname.c_str(), vIP, nMaxSolutions, fAllowLookup);
|
2011-12-17 01:48:03 +01:00
|
|
|
if (!fRet)
|
|
|
|
return false;
|
|
|
|
vAddr.resize(vIP.size());
|
2012-04-15 22:52:09 +02:00
|
|
|
for (unsigned int i = 0; i < vIP.size(); i++)
|
2011-12-17 01:48:03 +01:00
|
|
|
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);
|
2012-01-03 23:33:31 +01:00
|
|
|
if (!fRet)
|
|
|
|
return false;
|
2011-12-17 01:48:03 +01:00
|
|
|
addr = vService[0];
|
2012-01-03 23:33:31 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-09-03 15:29:10 +02:00
|
|
|
CService LookupNumeric(const char *pszName, int portDefault)
|
2012-01-03 23:33:31 +01:00
|
|
|
{
|
2017-09-03 15:29:10 +02:00
|
|
|
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;
|
2012-01-03 23:33:31 +01:00
|
|
|
}
|
|
|
|
|
2015-08-25 20:12:08 +02:00
|
|
|
struct timeval MillisToTimeval(int64_t nTimeout)
|
2014-09-08 13:49:56 +02:00
|
|
|
{
|
|
|
|
struct timeval timeout;
|
|
|
|
timeout.tv_sec = nTimeout / 1000;
|
|
|
|
timeout.tv_usec = (nTimeout % 1000) * 1000;
|
|
|
|
return timeout;
|
|
|
|
}
|
|
|
|
|
2017-09-28 05:09:49 +02:00
|
|
|
/** SOCKS version */
|
|
|
|
enum SOCKSVersion: uint8_t {
|
|
|
|
SOCKS4 = 0x04,
|
|
|
|
SOCKS5 = 0x05
|
|
|
|
};
|
|
|
|
|
|
|
|
/** Values defined for METHOD in RFC1928 */
|
|
|
|
enum SOCKS5Method: uint8_t {
|
|
|
|
NOAUTH = 0x00, //! No authentication required
|
|
|
|
GSSAPI = 0x01, //! GSSAPI
|
|
|
|
USER_PASS = 0x02, //! Username/password
|
|
|
|
NO_ACCEPTABLE = 0xff, //! No acceptable methods
|
|
|
|
};
|
|
|
|
|
|
|
|
/** Values defined for CMD in RFC1928 */
|
|
|
|
enum SOCKS5Command: uint8_t {
|
|
|
|
CONNECT = 0x01,
|
|
|
|
BIND = 0x02,
|
|
|
|
UDP_ASSOCIATE = 0x03
|
|
|
|
};
|
|
|
|
|
|
|
|
/** Values defined for REP in RFC1928 */
|
|
|
|
enum SOCKS5Reply: uint8_t {
|
|
|
|
SUCCEEDED = 0x00, //! Succeeded
|
|
|
|
GENFAILURE = 0x01, //! General failure
|
|
|
|
NOTALLOWED = 0x02, //! Connection not allowed by ruleset
|
|
|
|
NETUNREACHABLE = 0x03, //! Network unreachable
|
|
|
|
HOSTUNREACHABLE = 0x04, //! Network unreachable
|
|
|
|
CONNREFUSED = 0x05, //! Connection refused
|
|
|
|
TTLEXPIRED = 0x06, //! TTL expired
|
|
|
|
CMDUNSUPPORTED = 0x07, //! Command not supported
|
|
|
|
ATYPEUNSUPPORTED = 0x08, //! Address type not supported
|
|
|
|
};
|
|
|
|
|
|
|
|
/** Values defined for ATYPE in RFC1928 */
|
|
|
|
enum SOCKS5Atyp: uint8_t {
|
|
|
|
IPV4 = 0x01,
|
|
|
|
DOMAINNAME = 0x03,
|
|
|
|
IPV6 = 0x04,
|
|
|
|
};
|
|
|
|
|
|
|
|
/** Status codes that can be returned by InterruptibleRecv */
|
2019-01-03 10:18:47 +01:00
|
|
|
enum class IntrRecvError {
|
|
|
|
OK,
|
|
|
|
Timeout,
|
|
|
|
Disconnected,
|
|
|
|
NetworkError,
|
|
|
|
Interrupted
|
|
|
|
};
|
|
|
|
|
2014-09-08 13:49:56 +02:00
|
|
|
/**
|
|
|
|
* Read bytes from socket. This will either read the full number of bytes requested
|
|
|
|
* or return False on error or timeout.
|
2017-08-09 18:06:31 +02:00
|
|
|
* This function can be interrupted by calling InterruptSocks5()
|
2014-09-08 13:49:56 +02:00
|
|
|
*
|
|
|
|
* @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.
|
|
|
|
*/
|
2017-09-28 05:09:49 +02:00
|
|
|
static IntrRecvError InterruptibleRecv(uint8_t* data, size_t len, int timeout, const SOCKET& hSocket)
|
2014-09-08 13:49:56 +02:00
|
|
|
{
|
|
|
|
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) {
|
2017-09-28 05:09:49 +02:00
|
|
|
ssize_t ret = recv(hSocket, (char*)data, len, 0); // Optimistically try the recv first
|
2014-09-08 13:49:56 +02:00
|
|
|
if (ret > 0) {
|
|
|
|
len -= ret;
|
|
|
|
data += ret;
|
|
|
|
} else if (ret == 0) { // Unexpected disconnection
|
2019-01-03 10:18:47 +01:00
|
|
|
return IntrRecvError::Disconnected;
|
2014-09-08 13:49:56 +02:00
|
|
|
} else { // Other error or blocking
|
|
|
|
int nErr = WSAGetLastError();
|
|
|
|
if (nErr == WSAEINPROGRESS || nErr == WSAEWOULDBLOCK || nErr == WSAEINVAL) {
|
2015-07-10 00:23:27 +02:00
|
|
|
if (!IsSelectableSocket(hSocket)) {
|
2019-01-03 10:18:47 +01:00
|
|
|
return IntrRecvError::NetworkError;
|
2015-07-10 00:23:27 +02:00
|
|
|
}
|
2014-09-08 13:49:56 +02:00
|
|
|
struct timeval tval = MillisToTimeval(std::min(endTime - curTime, maxWait));
|
|
|
|
fd_set fdset;
|
|
|
|
FD_ZERO(&fdset);
|
|
|
|
FD_SET(hSocket, &fdset);
|
2019-08-06 05:08:33 +02:00
|
|
|
int nRet = select(hSocket + 1, &fdset, nullptr, nullptr, &tval);
|
2014-09-08 13:49:56 +02:00
|
|
|
if (nRet == SOCKET_ERROR) {
|
2019-01-03 10:18:47 +01:00
|
|
|
return IntrRecvError::NetworkError;
|
2014-09-08 13:49:56 +02:00
|
|
|
}
|
|
|
|
} else {
|
2019-01-03 10:18:47 +01:00
|
|
|
return IntrRecvError::NetworkError;
|
2014-09-08 13:49:56 +02:00
|
|
|
}
|
|
|
|
}
|
2017-08-09 18:06:31 +02:00
|
|
|
if (interruptSocks5Recv)
|
2019-01-03 10:18:47 +01:00
|
|
|
return IntrRecvError::Interrupted;
|
2014-09-08 13:49:56 +02:00
|
|
|
curTime = GetTimeMillis();
|
2012-04-01 20:25:48 +02:00
|
|
|
}
|
2019-01-03 10:18:47 +01:00
|
|
|
return len == 0 ? IntrRecvError::OK : IntrRecvError::Timeout;
|
2012-04-01 20:25:48 +02:00
|
|
|
}
|
|
|
|
|
2017-09-28 05:09:49 +02:00
|
|
|
/** Credentials for proxy authentication */
|
2015-03-16 16:30:49 +01:00
|
|
|
struct ProxyCredentials
|
|
|
|
{
|
|
|
|
std::string username;
|
|
|
|
std::string password;
|
|
|
|
};
|
|
|
|
|
2017-10-18 17:01:17 +02:00
|
|
|
/** Convert SOCKS5 reply to an error message */
|
2017-09-28 05:09:49 +02:00
|
|
|
std::string Socks5ErrorString(uint8_t err)
|
2016-05-19 08:45:27 +02:00
|
|
|
{
|
|
|
|
switch(err) {
|
2017-09-28 05:09:49 +02:00
|
|
|
case SOCKS5Reply::GENFAILURE:
|
|
|
|
return "general failure";
|
|
|
|
case SOCKS5Reply::NOTALLOWED:
|
|
|
|
return "connection not allowed";
|
|
|
|
case SOCKS5Reply::NETUNREACHABLE:
|
|
|
|
return "network unreachable";
|
|
|
|
case SOCKS5Reply::HOSTUNREACHABLE:
|
|
|
|
return "host unreachable";
|
|
|
|
case SOCKS5Reply::CONNREFUSED:
|
|
|
|
return "connection refused";
|
|
|
|
case SOCKS5Reply::TTLEXPIRED:
|
|
|
|
return "TTL expired";
|
|
|
|
case SOCKS5Reply::CMDUNSUPPORTED:
|
|
|
|
return "protocol error";
|
|
|
|
case SOCKS5Reply::ATYPEUNSUPPORTED:
|
|
|
|
return "address type not supported";
|
|
|
|
default:
|
|
|
|
return "unknown";
|
2016-05-19 08:45:27 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-03-16 16:30:49 +01:00
|
|
|
/** Connect using SOCKS5 (as described in RFC1928) */
|
2017-12-13 05:09:57 +01:00
|
|
|
static bool Socks5(const std::string& strDest, int port, const ProxyCredentials *auth, const SOCKET& hSocket)
|
2012-04-01 20:25:48 +02:00
|
|
|
{
|
2019-01-03 10:18:47 +01:00
|
|
|
IntrRecvError recvr;
|
2019-05-22 23:51:39 +02:00
|
|
|
LogPrint(BCLog::NET, "SOCKS5 connecting %s\n", strDest);
|
2015-03-16 16:30:49 +01:00
|
|
|
if (strDest.size() > 255) {
|
2012-04-19 17:02:21 +02:00
|
|
|
return error("Hostname too long");
|
|
|
|
}
|
2015-03-16 16:30:49 +01:00
|
|
|
// Accepted authentication methods
|
|
|
|
std::vector<uint8_t> vSocks5Init;
|
2017-09-28 05:09:49 +02:00
|
|
|
vSocks5Init.push_back(SOCKSVersion::SOCKS5);
|
2015-03-16 16:30:49 +01:00
|
|
|
if (auth) {
|
2017-09-28 05:09:49 +02:00
|
|
|
vSocks5Init.push_back(0x02); // Number of methods
|
|
|
|
vSocks5Init.push_back(SOCKS5Method::NOAUTH);
|
|
|
|
vSocks5Init.push_back(SOCKS5Method::USER_PASS);
|
2015-03-16 16:30:49 +01:00
|
|
|
} else {
|
2017-09-28 05:09:49 +02:00
|
|
|
vSocks5Init.push_back(0x01); // Number of methods
|
|
|
|
vSocks5Init.push_back(SOCKS5Method::NOAUTH);
|
2015-03-16 16:30:49 +01:00
|
|
|
}
|
2016-12-13 12:20:26 +01:00
|
|
|
ssize_t ret = send(hSocket, (const char*)vSocks5Init.data(), vSocks5Init.size(), MSG_NOSIGNAL);
|
2015-03-16 16:30:49 +01:00
|
|
|
if (ret != (ssize_t)vSocks5Init.size()) {
|
2012-04-01 20:25:48 +02:00
|
|
|
return error("Error sending to proxy");
|
|
|
|
}
|
2017-09-28 05:09:49 +02:00
|
|
|
uint8_t pchRet1[2];
|
2019-01-03 10:18:47 +01:00
|
|
|
if ((recvr = InterruptibleRecv(pchRet1, 2, SOCKS5_RECV_TIMEOUT, hSocket)) != IntrRecvError::OK) {
|
2016-05-19 08:45:27 +02:00
|
|
|
LogPrintf("Socks5() connect to %s:%d failed: InterruptibleRecv() timeout or other failure\n", strDest, port);
|
|
|
|
return false;
|
2012-04-01 20:25:48 +02:00
|
|
|
}
|
2017-09-28 05:09:49 +02:00
|
|
|
if (pchRet1[0] != SOCKSVersion::SOCKS5) {
|
2012-04-01 20:25:48 +02:00
|
|
|
return error("Proxy failed to initialize");
|
|
|
|
}
|
2017-09-28 05:09:49 +02:00
|
|
|
if (pchRet1[1] == SOCKS5Method::USER_PASS && auth) {
|
2015-03-16 16:30:49 +01:00
|
|
|
// Perform username/password authentication (as described in RFC1929)
|
|
|
|
std::vector<uint8_t> vAuth;
|
2017-09-28 05:09:49 +02:00
|
|
|
vAuth.push_back(0x01); // Current (and only) version of user/pass subnegotiation
|
2015-03-16 16:30:49 +01:00
|
|
|
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());
|
2016-12-13 12:20:26 +01:00
|
|
|
ret = send(hSocket, (const char*)vAuth.data(), vAuth.size(), MSG_NOSIGNAL);
|
2015-03-16 16:30:49 +01:00
|
|
|
if (ret != (ssize_t)vAuth.size()) {
|
|
|
|
return error("Error sending authentication to proxy");
|
|
|
|
}
|
2019-05-22 23:51:39 +02:00
|
|
|
LogPrint(BCLog::PROXY, "SOCKS5 sending proxy authentication %s:%s\n", auth->username, auth->password);
|
2017-09-28 05:09:49 +02:00
|
|
|
uint8_t pchRetA[2];
|
2019-01-03 10:18:47 +01:00
|
|
|
if ((recvr = InterruptibleRecv(pchRetA, 2, SOCKS5_RECV_TIMEOUT, hSocket)) != IntrRecvError::OK) {
|
2015-03-16 16:30:49 +01:00
|
|
|
return error("Error reading proxy authentication response");
|
|
|
|
}
|
|
|
|
if (pchRetA[0] != 0x01 || pchRetA[1] != 0x00) {
|
2015-08-09 01:17:27 +02:00
|
|
|
return error("Proxy authentication unsuccessful");
|
2015-03-16 16:30:49 +01:00
|
|
|
}
|
2017-09-28 05:09:49 +02:00
|
|
|
} else if (pchRet1[1] == SOCKS5Method::NOAUTH) {
|
2015-03-16 16:30:49 +01:00
|
|
|
// Perform no authentication
|
|
|
|
} else {
|
|
|
|
return error("Proxy requested wrong authentication method %02x", pchRet1[1]);
|
|
|
|
}
|
|
|
|
std::vector<uint8_t> vSocks5;
|
2017-09-28 05:09:49 +02:00
|
|
|
vSocks5.push_back(SOCKSVersion::SOCKS5); // VER protocol version
|
|
|
|
vSocks5.push_back(SOCKS5Command::CONNECT); // CMD CONNECT
|
|
|
|
vSocks5.push_back(0x00); // RSV Reserved must be 0
|
|
|
|
vSocks5.push_back(SOCKS5Atyp::DOMAINNAME); // ATYP DOMAINNAME
|
2015-03-16 16:30:49 +01:00
|
|
|
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);
|
2016-12-13 12:20:26 +01:00
|
|
|
ret = send(hSocket, (const char*)vSocks5.data(), vSocks5.size(), MSG_NOSIGNAL);
|
2015-03-16 16:30:49 +01:00
|
|
|
if (ret != (ssize_t)vSocks5.size()) {
|
2012-04-01 20:25:48 +02:00
|
|
|
return error("Error sending to proxy");
|
|
|
|
}
|
2017-09-28 05:09:49 +02:00
|
|
|
uint8_t pchRet2[4];
|
2019-01-03 10:18:47 +01:00
|
|
|
if ((recvr = InterruptibleRecv(pchRet2, 4, SOCKS5_RECV_TIMEOUT, hSocket)) != 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
|
|
|
|
* error message. */
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
return error("Error while reading proxy response");
|
|
|
|
}
|
2012-04-01 20:25:48 +02:00
|
|
|
}
|
2017-09-28 05:09:49 +02:00
|
|
|
if (pchRet2[0] != SOCKSVersion::SOCKS5) {
|
2012-04-01 20:25:48 +02:00
|
|
|
return error("Proxy failed to accept request");
|
|
|
|
}
|
2017-09-28 05:09:49 +02:00
|
|
|
if (pchRet2[1] != SOCKS5Reply::SUCCEEDED) {
|
2016-05-19 08:45:27 +02:00
|
|
|
// Failures to connect to a peer that are not proxy errors
|
|
|
|
LogPrintf("Socks5() connect to %s:%d failed: %s\n", strDest, port, Socks5ErrorString(pchRet2[1]));
|
|
|
|
return false;
|
2012-04-01 20:25:48 +02:00
|
|
|
}
|
2017-09-28 05:09:49 +02:00
|
|
|
if (pchRet2[2] != 0x00) { // Reserved field must be 0
|
2012-04-01 20:25:48 +02:00
|
|
|
return error("Error: malformed proxy response");
|
|
|
|
}
|
2017-09-28 05:09:49 +02:00
|
|
|
uint8_t pchRet3[256];
|
2012-04-01 20:25:48 +02:00
|
|
|
switch (pchRet2[3])
|
|
|
|
{
|
2017-09-28 05:09:49 +02:00
|
|
|
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::DOMAINNAME:
|
2012-04-01 20:25:48 +02:00
|
|
|
{
|
2019-01-03 10:18:47 +01:00
|
|
|
recvr = InterruptibleRecv(pchRet3, 1, SOCKS5_RECV_TIMEOUT, hSocket);
|
|
|
|
if (recvr != IntrRecvError::OK) {
|
2012-04-01 20:25:48 +02:00
|
|
|
return error("Error reading from proxy");
|
2014-04-10 02:09:17 +02:00
|
|
|
}
|
2012-04-01 20:25:48 +02:00
|
|
|
int nRecv = pchRet3[0];
|
2019-01-03 10:18:47 +01:00
|
|
|
recvr = InterruptibleRecv(pchRet3, nRecv, SOCKS5_RECV_TIMEOUT, hSocket);
|
2012-04-01 20:25:48 +02:00
|
|
|
break;
|
|
|
|
}
|
2017-12-13 05:09:57 +01:00
|
|
|
default: return error("Error: malformed proxy response");
|
2012-04-01 20:25:48 +02:00
|
|
|
}
|
2019-01-03 10:18:47 +01:00
|
|
|
if (recvr != IntrRecvError::OK) {
|
2012-04-01 20:25:48 +02:00
|
|
|
return error("Error reading from proxy");
|
|
|
|
}
|
2019-01-03 10:18:47 +01:00
|
|
|
if ((recvr = InterruptibleRecv(pchRet3, 2, SOCKS5_RECV_TIMEOUT, hSocket)) != IntrRecvError::OK) {
|
2012-04-01 20:25:48 +02:00
|
|
|
return error("Error reading from proxy");
|
|
|
|
}
|
2019-05-22 23:51:39 +02:00
|
|
|
LogPrint(BCLog::NET, "SOCKS5 connected %s\n", strDest);
|
2012-04-01 20:25:48 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2017-12-13 05:09:57 +01:00
|
|
|
SOCKET CreateSocket(const CService &addrConnect)
|
2012-01-03 23:33:31 +01:00
|
|
|
{
|
2012-05-11 15:28:59 +02:00
|
|
|
struct sockaddr_storage sockaddr;
|
|
|
|
socklen_t len = sizeof(sockaddr);
|
|
|
|
if (!addrConnect.GetSockAddr((struct sockaddr*)&sockaddr, &len)) {
|
2017-12-13 05:09:57 +01:00
|
|
|
LogPrintf("Cannot create socket for %s: unsupported network\n", addrConnect.ToString());
|
|
|
|
return INVALID_SOCKET;
|
2012-03-31 17:58:25 +02:00
|
|
|
}
|
|
|
|
|
2012-05-11 15:28:59 +02:00
|
|
|
SOCKET hSocket = socket(((struct sockaddr*)&sockaddr)->sa_family, SOCK_STREAM, IPPROTO_TCP);
|
2012-01-03 23:33:31 +01:00
|
|
|
if (hSocket == INVALID_SOCKET)
|
2017-12-13 05:09:57 +01:00
|
|
|
return INVALID_SOCKET;
|
|
|
|
|
|
|
|
if (!IsSelectableSocket(hSocket)) {
|
|
|
|
CloseSocket(hSocket);
|
|
|
|
LogPrintf("Cannot create connection: non-selectable socket created (fd >= FD_SETSIZE ?)\n");
|
|
|
|
return INVALID_SOCKET;
|
|
|
|
}
|
2014-07-09 11:00:00 +02:00
|
|
|
|
2015-10-22 01:52:29 +02:00
|
|
|
#ifdef SO_NOSIGPIPE
|
2017-05-18 02:26:54 +02:00
|
|
|
int set = 1;
|
2014-06-16 15:55:39 +02:00
|
|
|
// Different way of disabling SIGPIPE on BSD
|
2012-01-03 23:33:31 +01:00
|
|
|
setsockopt(hSocket, SOL_SOCKET, SO_NOSIGPIPE, (void*)&set, sizeof(int));
|
|
|
|
#endif
|
|
|
|
|
2015-10-22 01:52:29 +02:00
|
|
|
//Disable Nagle's algorithm
|
2017-05-18 02:26:54 +02:00
|
|
|
SetSocketNoDelay(hSocket);
|
2015-10-22 01:52:29 +02:00
|
|
|
|
2014-07-09 11:00:00 +02:00
|
|
|
// Set to non-blocking
|
2017-07-24 14:58:25 +02:00
|
|
|
if (!SetSocketNonBlocking(hSocket, true)) {
|
|
|
|
CloseSocket(hSocket);
|
2017-12-13 05:09:57 +01:00
|
|
|
LogPrintf("ConnectSocketDirectly: Setting socket to non-blocking failed, error %s\n", NetworkErrorString(WSAGetLastError()));
|
2017-07-24 14:58:25 +02:00
|
|
|
}
|
2017-12-13 05:09:57 +01:00
|
|
|
return hSocket;
|
|
|
|
}
|
2012-01-03 23:33:31 +01:00
|
|
|
|
2017-12-13 05:09:57 +01:00
|
|
|
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;
|
|
|
|
}
|
2012-05-11 15:28:59 +02:00
|
|
|
if (connect(hSocket, (struct sockaddr*)&sockaddr, len) == SOCKET_ERROR)
|
2012-01-03 23:33:31 +01:00
|
|
|
{
|
2014-06-22 20:17:15 +02:00
|
|
|
int nErr = WSAGetLastError();
|
2012-01-03 23:33:31 +01:00
|
|
|
// WSAEINVAL is here because some legacy version of winsock uses it
|
2014-06-22 20:17:15 +02:00
|
|
|
if (nErr == WSAEINPROGRESS || nErr == WSAEWOULDBLOCK || nErr == WSAEINVAL)
|
2012-01-03 23:33:31 +01:00
|
|
|
{
|
2014-09-08 13:49:56 +02:00
|
|
|
struct timeval timeout = MillisToTimeval(nTimeout);
|
2012-01-03 23:33:31 +01:00
|
|
|
fd_set fdset;
|
|
|
|
FD_ZERO(&fdset);
|
|
|
|
FD_SET(hSocket, &fdset);
|
2019-08-06 05:08:33 +02:00
|
|
|
int nRet = select(hSocket + 1, nullptr, &fdset, nullptr, &timeout);
|
2012-01-03 23:33:31 +01:00
|
|
|
if (nRet == 0)
|
|
|
|
{
|
2019-05-22 23:51:39 +02:00
|
|
|
LogPrint(BCLog::NET, "connection to %s timeout\n", addrConnect.ToString());
|
2012-01-03 23:33:31 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (nRet == SOCKET_ERROR)
|
|
|
|
{
|
2014-05-08 14:15:19 +02:00
|
|
|
LogPrintf("select() for %s failed: %s\n", addrConnect.ToString(), NetworkErrorString(WSAGetLastError()));
|
2012-01-03 23:33:31 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
socklen_t nRetSize = sizeof(nRet);
|
|
|
|
#ifdef WIN32
|
|
|
|
if (getsockopt(hSocket, SOL_SOCKET, SO_ERROR, (char*)(&nRet), &nRetSize) == SOCKET_ERROR)
|
|
|
|
#else
|
|
|
|
if (getsockopt(hSocket, SOL_SOCKET, SO_ERROR, &nRet, &nRetSize) == SOCKET_ERROR)
|
|
|
|
#endif
|
|
|
|
{
|
2014-05-08 14:15:19 +02:00
|
|
|
LogPrintf("getsockopt() for %s failed: %s\n", addrConnect.ToString(), NetworkErrorString(WSAGetLastError()));
|
2012-01-03 23:33:31 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
if (nRet != 0)
|
|
|
|
{
|
2014-05-08 14:15:19 +02:00
|
|
|
LogPrintf("connect() to %s failed after select(): %s\n", addrConnect.ToString(), NetworkErrorString(nRet));
|
2012-01-03 23:33:31 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#ifdef WIN32
|
|
|
|
else if (WSAGetLastError() != WSAEISCONN)
|
|
|
|
#else
|
|
|
|
else
|
|
|
|
#endif
|
|
|
|
{
|
2014-05-08 14:15:19 +02:00
|
|
|
LogPrintf("connect() to %s failed: %s\n", addrConnect.ToString(), NetworkErrorString(WSAGetLastError()));
|
2012-01-03 23:33:31 +01:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
2012-04-19 17:02:21 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-03-16 16:30:49 +01:00
|
|
|
bool SetProxy(enum Network net, const proxyType &addrProxy) {
|
2012-05-24 19:02:21 +02:00
|
|
|
assert(net >= 0 && net < NET_MAX);
|
2014-06-11 13:20:59 +02:00
|
|
|
if (!addrProxy.IsValid())
|
2012-05-24 19:02:21 +02:00
|
|
|
return false;
|
2012-09-23 12:55:05 +02:00
|
|
|
LOCK(cs_proxyInfos);
|
2014-06-11 13:20:59 +02:00
|
|
|
proxyInfo[net] = addrProxy;
|
2012-05-24 19:02:21 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-09-23 12:55:05 +02:00
|
|
|
bool GetProxy(enum Network net, proxyType &proxyInfoOut) {
|
2012-05-24 19:02:21 +02:00
|
|
|
assert(net >= 0 && net < NET_MAX);
|
2012-09-23 12:55:05 +02:00
|
|
|
LOCK(cs_proxyInfos);
|
2014-06-11 13:20:59 +02:00
|
|
|
if (!proxyInfo[net].IsValid())
|
2012-05-24 19:02:21 +02:00
|
|
|
return false;
|
2012-09-23 12:55:05 +02:00
|
|
|
proxyInfoOut = proxyInfo[net];
|
2012-05-24 19:02:21 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-03-16 16:30:49 +01:00
|
|
|
bool SetNameProxy(const proxyType &addrProxy) {
|
2014-06-11 13:20:59 +02:00
|
|
|
if (!addrProxy.IsValid())
|
2012-05-24 19:02:21 +02:00
|
|
|
return false;
|
2012-09-23 12:55:05 +02:00
|
|
|
LOCK(cs_proxyInfos);
|
2014-06-11 13:20:59 +02:00
|
|
|
nameProxy = addrProxy;
|
2012-05-24 19:02:21 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2015-03-16 16:30:49 +01:00
|
|
|
bool GetNameProxy(proxyType &nameProxyOut) {
|
2012-09-23 12:55:05 +02:00
|
|
|
LOCK(cs_proxyInfos);
|
2014-06-11 13:20:59 +02:00
|
|
|
if(!nameProxy.IsValid())
|
2012-09-23 12:55:05 +02:00
|
|
|
return false;
|
2014-06-11 13:20:59 +02:00
|
|
|
nameProxyOut = nameProxy;
|
2012-09-23 12:55:05 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool HaveNameProxy() {
|
|
|
|
LOCK(cs_proxyInfos);
|
2014-06-11 13:20:59 +02:00
|
|
|
return nameProxy.IsValid();
|
2012-05-24 19:02:21 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
bool IsProxy(const CNetAddr &addr) {
|
2012-09-23 12:55:05 +02:00
|
|
|
LOCK(cs_proxyInfos);
|
|
|
|
for (int i = 0; i < NET_MAX; i++) {
|
2015-03-16 16:30:49 +01:00
|
|
|
if (addr == (CNetAddr)proxyInfo[i].proxy)
|
2012-05-24 19:02:21 +02:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-12-13 05:09:57 +01:00
|
|
|
bool ConnectThroughProxy(const proxyType &proxy, const std::string& strDest, int port, const SOCKET& hSocket, int nTimeout, bool *outProxyConnectionFailed)
|
2012-04-19 17:02:21 +02:00
|
|
|
{
|
2012-05-24 19:02:21 +02:00
|
|
|
// first connect to proxy server
|
2015-03-16 16:30:49 +01:00
|
|
|
if (!ConnectSocketDirectly(proxy.proxy, hSocket, nTimeout)) {
|
2014-12-02 17:43:42 +01:00
|
|
|
if (outProxyConnectionFailed)
|
|
|
|
*outProxyConnectionFailed = true;
|
2012-05-24 19:02:21 +02:00
|
|
|
return false;
|
2014-12-02 17:43:42 +01:00
|
|
|
}
|
2012-05-24 19:02:21 +02:00
|
|
|
// do socks negotiation
|
2015-03-16 16:30:49 +01:00
|
|
|
if (proxy.randomize_credentials) {
|
|
|
|
ProxyCredentials random_auth;
|
2017-07-14 23:33:10 +02:00
|
|
|
static std::atomic_int counter(0);
|
2016-10-18 15:38:44 +02:00
|
|
|
random_auth.username = random_auth.password = strprintf("%i", counter++);
|
2017-12-13 05:09:57 +01:00
|
|
|
if (!Socks5(strDest, (unsigned short)port, &random_auth, hSocket)) {
|
2015-03-16 16:30:49 +01:00
|
|
|
return false;
|
2017-12-13 05:09:57 +01:00
|
|
|
}
|
2015-03-16 16:30:49 +01:00
|
|
|
} else {
|
2017-12-13 05:09:57 +01:00
|
|
|
if (!Socks5(strDest, (unsigned short)port, 0, hSocket)) {
|
2015-03-16 16:30:49 +01:00
|
|
|
return false;
|
2017-12-13 05:09:57 +01:00
|
|
|
}
|
2015-03-16 16:30:49 +01:00
|
|
|
}
|
2012-01-03 23:33:31 +01:00
|
|
|
return true;
|
|
|
|
}
|
2017-09-03 15:29:10 +02:00
|
|
|
bool LookupSubNet(const char* pszName, CSubNet& ret)
|
2014-04-28 11:08:57 +02:00
|
|
|
{
|
2017-09-03 15:29:10 +02:00
|
|
|
std::string strSubnet(pszName);
|
2014-04-28 11:08:57 +02:00
|
|
|
size_t slash = strSubnet.find_last_of('/');
|
|
|
|
std::vector<CNetAddr> vIP;
|
|
|
|
|
|
|
|
std::string strAddress = strSubnet.substr(0, slash);
|
2017-09-02 22:07:11 +02:00
|
|
|
if (LookupHost(strAddress.c_str(), vIP, 1, false))
|
2014-04-28 11:08:57 +02:00
|
|
|
{
|
2017-09-03 15:29:10 +02:00
|
|
|
CNetAddr network = vIP[0];
|
2014-04-28 11:08:57 +02:00
|
|
|
if (slash != strSubnet.npos)
|
|
|
|
{
|
|
|
|
std::string strNetmask = strSubnet.substr(slash + 1);
|
|
|
|
int32_t n;
|
|
|
|
// IPv4 addresses start at offset 12, and first 12 bytes must match, so just offset n
|
2017-09-03 15:29:10 +02:00
|
|
|
if (ParseInt32(strNetmask, &n)) { // If valid number, assume /24 syntax
|
|
|
|
ret = CSubNet(network, n);
|
|
|
|
return ret.IsValid();
|
2014-04-28 11:08:57 +02:00
|
|
|
}
|
|
|
|
else // If not a valid number, try full netmask syntax
|
|
|
|
{
|
2017-09-03 15:29:10 +02:00
|
|
|
// Never allow lookup for netmask
|
|
|
|
if (LookupHost(strNetmask.c_str(), vIP, 1, false)) {
|
|
|
|
ret = CSubNet(network, vIP[0]);
|
|
|
|
return ret.IsValid();
|
2014-04-28 11:08:57 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2015-06-25 10:47:34 +02:00
|
|
|
else
|
2017-09-03 15:29:10 +02:00
|
|
|
{
|
|
|
|
ret = CSubNet(network);
|
|
|
|
return ret.IsValid();
|
|
|
|
}
|
2015-06-25 10:47:34 +02:00
|
|
|
}
|
2017-09-03 15:29:10 +02:00
|
|
|
return false;
|
2015-05-25 20:03:51 +02:00
|
|
|
}
|
|
|
|
|
2014-05-08 14:15:19 +02:00
|
|
|
#ifdef WIN32
|
|
|
|
std::string NetworkErrorString(int err)
|
|
|
|
{
|
|
|
|
char buf[256];
|
|
|
|
buf[0] = 0;
|
|
|
|
if(FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK,
|
2019-08-06 05:08:33 +02:00
|
|
|
nullptr, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
|
|
|
buf, sizeof(buf), nullptr))
|
2014-05-08 14:15:19 +02:00
|
|
|
{
|
|
|
|
return strprintf("%s (%d)", 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. */
|
2017-05-23 19:37:52 +02:00
|
|
|
const char *s;
|
2014-05-08 14:15:19 +02:00
|
|
|
#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 */
|
2017-05-23 19:37:52 +02:00
|
|
|
s = buf;
|
2014-07-07 17:34:00 +02:00
|
|
|
if (strerror_r(err, buf, sizeof(buf)))
|
|
|
|
buf[0] = 0;
|
2014-05-08 14:15:19 +02:00
|
|
|
#endif
|
|
|
|
return strprintf("%s (%d)", s, err);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2014-07-10 12:13:03 +02:00
|
|
|
bool CloseSocket(SOCKET& hSocket)
|
|
|
|
{
|
|
|
|
if (hSocket == INVALID_SOCKET)
|
|
|
|
return false;
|
|
|
|
#ifdef WIN32
|
|
|
|
int ret = closesocket(hSocket);
|
|
|
|
#else
|
|
|
|
int ret = close(hSocket);
|
|
|
|
#endif
|
|
|
|
hSocket = INVALID_SOCKET;
|
|
|
|
return ret != SOCKET_ERROR;
|
|
|
|
}
|
2014-07-09 11:00:00 +02:00
|
|
|
|
2017-07-24 14:58:25 +02:00
|
|
|
bool SetSocketNonBlocking(const SOCKET& hSocket, bool fNonBlocking)
|
2014-07-09 11:00:00 +02:00
|
|
|
{
|
|
|
|
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
|
|
|
|
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
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2017-08-09 18:06:31 +02:00
|
|
|
|
2017-07-24 14:58:25 +02:00
|
|
|
bool SetSocketNoDelay(const SOCKET& hSocket)
|
2017-05-18 02:26:54 +02:00
|
|
|
{
|
|
|
|
int set = 1;
|
|
|
|
int rc = setsockopt(hSocket, IPPROTO_TCP, TCP_NODELAY, (const char*)&set, sizeof(int));
|
|
|
|
return rc == 0;
|
|
|
|
}
|
|
|
|
|
2017-08-09 18:06:31 +02:00
|
|
|
void InterruptSocks5(bool interrupt)
|
|
|
|
{
|
|
|
|
interruptSocks5Recv = interrupt;
|
|
|
|
}
|