Skip to content

Commit

Permalink
wip: proxy
Browse files Browse the repository at this point in the history
  • Loading branch information
vasild committed Apr 14, 2023
1 parent 44dd4cb commit fd6baa3
Show file tree
Hide file tree
Showing 12 changed files with 147 additions and 72 deletions.
16 changes: 4 additions & 12 deletions src/i2p.cpp
Expand Up @@ -114,7 +114,7 @@ static CNetAddr DestB64ToAddr(const std::string& dest)
namespace sam {

Session::Session(const fs::path& private_key_file,
const CService& control_host,
const Proxy& control_host,
CThreadInterrupt* interrupt)
: m_private_key_file{private_key_file},
m_control_host{control_host},
Expand All @@ -124,7 +124,7 @@ Session::Session(const fs::path& private_key_file,
{
}

Session::Session(const CService& control_host, CThreadInterrupt* interrupt)
Session::Session(const Proxy& control_host, CThreadInterrupt* interrupt)
: m_control_host{control_host},
m_interrupt{interrupt},
m_control_sock{std::make_unique<Sock>(INVALID_SOCKET)},
Expand Down Expand Up @@ -295,15 +295,7 @@ Session::Reply Session::SendRequestAndGetReply(const Sock& sock,

std::unique_ptr<Sock> Session::Hello() const
{
auto sock = CreateSock(m_control_host);

if (!sock) {
throw std::runtime_error("Cannot create socket");
}

if (!ConnectSocketDirectly(m_control_host, *sock, nConnectTimeout, true)) {
throw std::runtime_error(strprintf("Cannot connect to %s", m_control_host.ToStringAddrPort()));
}
auto sock = m_control_host.ConnectToProxy();

SendRequestAndGetReply(*sock, "HELLO VERSION MIN=3.1 MAX=3.1");

Expand Down Expand Up @@ -371,7 +363,7 @@ void Session::CreateIfNotCreatedAlready()
const auto session_type = m_transient ? "transient" : "persistent";
const auto session_id = GetRandHash().GetHex().substr(0, 10); // full is overkill, too verbose in the logs

Log("Creating %s SAM session %s with %s", session_type, session_id, m_control_host.ToStringAddrPort());
Log("Creating %s SAM session %s with %s", session_type, session_id, m_control_host.ToString());

auto sock = Hello();

Expand Down
9 changes: 5 additions & 4 deletions src/i2p.h
Expand Up @@ -7,6 +7,7 @@

#include <compat/compat.h>
#include <netaddress.h>
#include <netbase.h>
#include <sync.h>
#include <util/fs.h>
#include <util/sock.h>
Expand Down Expand Up @@ -67,7 +68,7 @@ class Session
* `Session` object.
*/
Session(const fs::path& private_key_file,
const CService& control_host,
const Proxy& control_host,
CThreadInterrupt* interrupt);

/**
Expand All @@ -81,7 +82,7 @@ class Session
* `CThreadInterrupt` object is saved, so it must not be destroyed earlier than this
* `Session` object.
*/
Session(const CService& control_host, CThreadInterrupt* interrupt);
Session(const Proxy& control_host, CThreadInterrupt* interrupt);

/**
* Destroy the session, closing the internally used sockets. The sockets that have been
Expand Down Expand Up @@ -235,9 +236,9 @@ class Session
const fs::path m_private_key_file;

/**
* The host and port of the SAM control service.
* The SAM control service.
*/
const CService m_control_host;
const Proxy m_control_host;

/**
* Cease network activity when this is signaled.
Expand Down
23 changes: 9 additions & 14 deletions src/init.cpp
Expand Up @@ -1364,12 +1364,7 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
// -noproxy (or -proxy=0) as well as the empty string can be used to not set a proxy, this is the default
std::string proxyArg = args.GetArg("-proxy", "");
if (proxyArg != "" && proxyArg != "0") {
CService proxyAddr;
if (!Lookup(proxyArg, proxyAddr, 9050, fNameLookup)) {
return InitError(strprintf(_("Invalid -proxy address or hostname: '%s'"), proxyArg));
}

Proxy addrProxy = Proxy(proxyAddr, proxyRandomize);
Proxy addrProxy{proxyArg, 9050, proxyRandomize};
if (!addrProxy.IsValid())
return InitError(strprintf(_("Invalid -proxy address or hostname: '%s'"), proxyArg));

Expand All @@ -1395,11 +1390,11 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
"reaching the Tor network is explicitly forbidden: -onion=0"));
}
} else {
CService addr;
if (!Lookup(onionArg, addr, 9050, fNameLookup) || !addr.IsValid()) {
return InitError(strprintf(_("Invalid -onion address or hostname: '%s'"), onionArg));
try {
onion_proxy = Proxy{onionArg, 9050, proxyRandomize};
} catch (const std::runtime_error& e) {
return InitError(strprintf(_("Invalid -onion address or hostname: '%s': %s"), onionArg, e.what()));
}
onion_proxy = Proxy{addr, proxyRandomize};
}
}

Expand Down Expand Up @@ -1838,11 +1833,11 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)

const std::string& i2psam_arg = args.GetArg("-i2psam", "");
if (!i2psam_arg.empty()) {
CService addr;
if (!Lookup(i2psam_arg, addr, 7656, fNameLookup) || !addr.IsValid()) {
return InitError(strprintf(_("Invalid -i2psam address or hostname: '%s'"), i2psam_arg));
try {
SetProxy(NET_I2P, Proxy{i2psam_arg, 7656});
} catch (const std::runtime_error& e) {
return InitError(strprintf(_("Invalid -i2psam address or hostname: '%s': %s"), i2psam_arg, e.what()));
}
SetProxy(NET_I2P, Proxy{addr});
} else {
if (args.IsArgSet("-onlynet") && IsReachable(NET_I2P)) {
return InitError(
Expand Down
23 changes: 10 additions & 13 deletions src/net.cpp
Expand Up @@ -502,7 +502,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
LOCK(m_unused_i2p_sessions_mutex);
if (m_unused_i2p_sessions.empty()) {
i2p_transient_session =
std::make_unique<i2p::sam::Session>(proxy.proxy, &interruptNet);
std::make_unique<i2p::sam::Session>(proxy, &interruptNet);
} else {
i2p_transient_session.swap(m_unused_i2p_sessions.front());
m_unused_i2p_sessions.pop();
Expand All @@ -522,14 +522,13 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
addr_bind = CAddress{conn.me, NODE_NONE};
}
} else if (use_proxy) {
sock = CreateSock(proxy.proxy);
sock = proxy.ConnectToDest(addrConnect.ToStringAddr(),
addrConnect.GetPort(),
std::chrono::milliseconds{nConnectTimeout},
proxyConnectionFailed);
if (!sock) {
LogPrintLevel(BCLog::PROXY, BCLog::Level::Debug, "Failed to create socket to proxy: %s\n", proxy.proxy.ToStringAddr());
return nullptr;
}
LogPrintLevel(BCLog::PROXY, BCLog::Level::Debug, "Using proxy: %s to connect to %s:%s\n", proxy.proxy.ToStringAddr(), addrConnect.ToStringAddr(), addrConnect.GetPort());
connected = ConnectThroughProxy(proxy, addrConnect.ToStringAddr(), addrConnect.GetPort(),
*sock, nConnectTimeout, proxyConnectionFailed);
} else {
// no proxy needed (none set for target network)
sock = CreateSock(addrConnect);
Expand All @@ -545,16 +544,14 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo
addrman.Attempt(addrConnect, fCountFailure);
}
} else if (pszDest && GetNameProxy(proxy)) {
sock = CreateSock(proxy.proxy);
if (!sock) {
return nullptr;
}
std::string host;
uint16_t port{default_port};
SplitHostPort(std::string(pszDest), port, host);
bool proxyConnectionFailed;
connected = ConnectThroughProxy(proxy, host, port, *sock, nConnectTimeout,
proxyConnectionFailed);
sock = proxy.ConnectToDest(host, port, std::chrono::milliseconds{nConnectTimeout}, proxyConnectionFailed);
if (!sock) {
return nullptr;
}
}
if (!connected) {
return nullptr;
Expand Down Expand Up @@ -2321,7 +2318,7 @@ bool CConnman::Start(CScheduler& scheduler, const Options& connOptions)
Proxy i2p_sam;
if (GetProxy(NET_I2P, i2p_sam) && connOptions.m_i2p_accept_incoming) {
m_i2p_sam_session = std::make_unique<i2p::sam::Session>(gArgs.GetDataDirNet() / "i2p_private_key",
i2p_sam.proxy, &interruptNet);
i2p_sam, &interruptNet);
}

for (const auto& strDest : connOptions.vSeedNodes) {
Expand Down
45 changes: 44 additions & 1 deletion src/netbase.cpp
Expand Up @@ -40,6 +40,47 @@ bool fNameLookup = DEFAULT_NAME_LOOKUP;
int g_socks5_recv_timeout = 20 * 1000;
static std::atomic<bool> interruptSocks5Recv(false);

Proxy::Proxy(const std::string& proxy, uint16_t default_port, bool randomize_credentials)
{
}

std::unique_ptr<Sock> Proxy::ConnectToProxy() const
{
return std::make_unique<Sock>();
}

std::unique_ptr<Sock> Proxy::ConnectToDest(const std::string& dest,
uint16_t port,
std::chrono::milliseconds timeout,
bool& proxy_connection_failed) const
{
return std::make_unique<Sock>();
}

std::string Proxy::ToString() const
{
return std::string{};
}

bool Proxy::operator==(const CNetAddr& addr) const
{
return false;
}

bool Proxy::operator==(const CService& service) const
{
return false;
}

bool Proxy::IsValid() const
{
return false;
}

Proxy::Proxy()
{
}

std::vector<CNetAddr> WrappedGetAddrInfo(const std::string& name, bool allow_lookup)
{
addrinfo ai_hint{};
Expand Down Expand Up @@ -675,12 +716,13 @@ bool HaveNameProxy() {
bool IsProxy(const CNetAddr &addr) {
LOCK(g_proxyinfo_mutex);
for (int i = 0; i < NET_MAX; i++) {
if (addr == static_cast<CNetAddr>(proxyInfo[i].proxy))
if (proxyInfo[i] == addr)
return true;
}
return false;
}

#if 0
bool ConnectThroughProxy(const Proxy& proxy, const std::string& strDest, uint16_t port, const Sock& sock, int nTimeout, bool& outProxyConnectionFailed)
{
// first connect to proxy server
Expand All @@ -703,6 +745,7 @@ bool ConnectThroughProxy(const Proxy& proxy, const std::string& strDest, uint16_
}
return true;
}
#endif

bool LookupSubNet(const std::string& subnet_str, CSubNet& subnet_out)
{
Expand Down
60 changes: 55 additions & 5 deletions src/netbase.h
Expand Up @@ -48,13 +48,63 @@ static inline bool operator&(ConnectionDirection a, ConnectionDirection b) {
class Proxy
{
public:
Proxy(): randomize_credentials(false) {}
explicit Proxy(const CService &_proxy, bool _randomize_credentials=false): proxy(_proxy), randomize_credentials(_randomize_credentials) {}
bool m_randomize_credentials;

bool IsValid() const { return proxy.IsValid(); }
/**
* Create a proxy object.
* @param[in] proxy Can be either `host[:port]`, `ip_address[:port]` or `unix:/path/to/socket`.
* @param[in] default_port Use this port if none is present in `proxy` and it is not an UNIX socket.
* @param[in] randomize_credentials If true, then use a random username and password for each
* connection to the proxy.
* @throws std::runtime_error if `proxy_str` cannot be parsed
*/
explicit Proxy(const std::string& proxy, uint16_t default_port, bool randomize_credentials = false);

CService proxy;
bool randomize_credentials;
/**
* Connect to the proxy and return the connected socket.
*/
std::unique_ptr<Sock> ConnectToProxy() const;

/**
* Connect to an address via the proxy, assuming it is SOCKS5 proxy.
* @param[in] addr Destination to connect to, forwarded to the proxy.
* @param[in] port Port to connect to, forwarded to the proxy.
* @param[in] timeout Timeout for connecting.
* @param[out] proxy_connection_failed If cannot connect and this is set to `true`, then the
* failure is due to the proxy itself (not due to `dest` being down).
*/
std::unique_ptr<Sock> ConnectToDest(const std::string& addr,
uint16_t port,
std::chrono::milliseconds timeout,
bool& proxy_connection_failed) const;

/**
* Generate a human readable representation of the proxy.
*/
std::string ToString() const;

/**
* Returns true if the underlying proxy address equals `addr` (ignoring the port).
*/
bool operator==(const CNetAddr& addr) const;

/**
* Returns true if the underlying proxy address equals `service`.
*/
bool operator==(const CService& service) const;

/**
* Returns true the underlying address is valid.
*/
bool IsValid() const;

/**
* Would be nice to remove this (disallow default constructed Proxy and then IsValid() can be removed too).
*/
Proxy();

private:
std::variant<CService, fs::path> m_addr;
};

/** Credentials for proxy authentication */
Expand Down
2 changes: 1 addition & 1 deletion src/qt/clientmodel.cpp
Expand Up @@ -280,7 +280,7 @@ bool ClientModel::getProxyInfo(std::string& ip_port) const
{
Proxy ipv4, ipv6;
if (m_node.getProxy((Network) 1, ipv4) && m_node.getProxy((Network) 2, ipv6)) {
ip_port = ipv4.proxy.ToStringAddrPort();
ip_port = ipv4.ToString();
return true;
}
return false;
Expand Down
9 changes: 4 additions & 5 deletions src/qt/optionsdialog.cpp
Expand Up @@ -414,13 +414,13 @@ void OptionsDialog::updateDefaultProxyNets()
bool has_proxy;

has_proxy = model->node().getProxy(NET_IPV4, proxy);
ui->proxyReachIPv4->setChecked(has_proxy && proxy.proxy == ui_proxy);
ui->proxyReachIPv4->setChecked(has_proxy && proxy == ui_proxy);

has_proxy = model->node().getProxy(NET_IPV6, proxy);
ui->proxyReachIPv6->setChecked(has_proxy && proxy.proxy == ui_proxy);
ui->proxyReachIPv6->setChecked(has_proxy && proxy == ui_proxy);

has_proxy = model->node().getProxy(NET_ONION, proxy);
ui->proxyReachTor->setChecked(has_proxy && proxy.proxy == ui_proxy);
ui->proxyReachTor->setChecked(has_proxy && proxy == ui_proxy);
}

ProxyAddressValidator::ProxyAddressValidator(QObject *parent) :
Expand All @@ -432,8 +432,7 @@ QValidator::State ProxyAddressValidator::validate(QString &input, int &pos) cons
{
Q_UNUSED(pos);
// Validate the proxy
CService serv(LookupNumeric(input.toStdString(), DEFAULT_GUI_PROXY_PORT));
Proxy addrProxy = Proxy(serv, true);
const Proxy addrProxy{input.toStdString(), DEFAULT_GUI_PROXY_PORT};
if (addrProxy.IsValid())
return QValidator::Acceptable;

Expand Down
4 changes: 2 additions & 2 deletions src/rpc/net.cpp
Expand Up @@ -571,8 +571,8 @@ static UniValue GetNetworksInfo()
obj.pushKV("name", GetNetworkName(network));
obj.pushKV("limited", !IsReachable(network));
obj.pushKV("reachable", IsReachable(network));
obj.pushKV("proxy", proxy.IsValid() ? proxy.proxy.ToStringAddrPort() : std::string());
obj.pushKV("proxy_randomize_credentials", proxy.randomize_credentials);
obj.pushKV("proxy", proxy.IsValid() ? proxy.ToString() : std::string());
obj.pushKV("proxy_randomize_credentials", proxy.m_randomize_credentials);
networks.push_back(obj);
}
return networks;
Expand Down
2 changes: 1 addition & 1 deletion src/test/fuzz/i2p.cpp
Expand Up @@ -28,7 +28,7 @@ FUZZ_TARGET_INIT(i2p, initialize_i2p)
return std::make_unique<FuzzedSock>(fuzzed_data_provider);
};

const CService sam_proxy;
const Proxy sam_proxy;
CThreadInterrupt interrupt;

i2p::sam::Session sess{gArgs.GetDataDirNet() / "fuzzed_i2p_private_key", sam_proxy, &interrupt};
Expand Down
2 changes: 1 addition & 1 deletion src/test/i2p_tests.cpp
Expand Up @@ -30,7 +30,7 @@ BOOST_AUTO_TEST_CASE(unlimited_recv)
};

CThreadInterrupt interrupt;
i2p::sam::Session session(gArgs.GetDataDirNet() / "test_i2p_private_key", CService{}, &interrupt);
i2p::sam::Session session(gArgs.GetDataDirNet() / "test_i2p_private_key", Proxy{}, &interrupt);

{
ASSERT_DEBUG_LOG("Creating persistent SAM session");
Expand Down

0 comments on commit fd6baa3

Please sign in to comment.