aboutsummaryrefslogtreecommitdiff
path: root/src/net/parse.cpp
diff options
context:
space:
mode:
authorLee *!* Clagett <code@leeclagett.com>2024-08-17 18:37:10 -0400
committerlschomaker1 <lucas.schomaker12@gmail.com>2026-04-16 15:52:05 -0500
commitce5437f0677966c15d2946bc7c068975aa6bfd83 (patch)
treefe34f64934e00f27f4308f6747a0c32efd2edfcd /src/net/parse.cpp
parent54b803603cf0fb450a9b9436b9fe85452e75966f (diff)
downloadmonzero-core-ce5437f0677966c15d2946bc7c068975aa6bfd83.tar.gz
monzero-core-ce5437f0677966c15d2946bc7c068975aa6bfd83.tar.xz
monzero-core-ce5437f0677966c15d2946bc7c068975aa6bfd83.zip
Add Socks v5 support to daemon and wallet
(cherry picked from commit 23e29a5074f01cbb781f7e76b0c17ad2b6797af0)
Diffstat (limited to 'src/net/parse.cpp')
-rw-r--r--src/net/parse.cpp135
1 files changed, 134 insertions, 1 deletions
diff --git a/src/net/parse.cpp b/src/net/parse.cpp
index f989d7de4..27fd5db43 100644
--- a/src/net/parse.cpp
+++ b/src/net/parse.cpp
@@ -29,6 +29,9 @@
#include "parse.h"
+#include <type_traits>
+#include "hex.h"
+#include "net/socks.h"
#include "net/tor_address.h"
#include "net/i2p_address.h"
#include "string_tools.h"
@@ -36,6 +39,95 @@
namespace net
{
+ namespace
+ {
+ bool percent_decoding(std::string& out)
+ {
+ auto pos = out.find('%');
+ while (pos != std::string::npos)
+ {
+ if (out.size() - pos < 3)
+ return false;
+ if (!epee::from_hex::to_buffer(epee::as_mut_byte_span(out[pos]), {out.data() + pos + 1, 2}))
+ return false;
+ out.erase(pos + 1, 2);
+ pos = out.find('%', pos + 1);
+ }
+
+ return true;
+ }
+ } // anonymous
+
+ scheme_and_authority::scheme_and_authority(boost::string_ref uri)
+ : scheme(), authority()
+ {
+ static_assert(std::is_same<std::string::size_type, boost::string_ref::size_type>());
+
+ // Stop at scheme end or path begin. URN not supported
+ const auto split = uri.find_first_of(":/");
+ if (split != boost::string_ref::npos && uri.substr(split).starts_with("://"))
+ {
+ scheme.assign(uri.data(), split);
+ uri = uri.substr(split + 3);
+ }
+
+ uri = uri.substr(0, uri.find('/'));
+ authority.assign(uri.data(), uri.size());
+ }
+
+ userinfo_and_hostport::userinfo_and_hostport(boost::string_ref authority)
+ : userinfo(), hostport()
+ {
+ static_assert(std::is_same<std::string::size_type, boost::string_ref::size_type>());
+
+ const auto split = authority.find('@');
+ if (split != boost::string_ref::npos)
+ {
+ userinfo.assign(authority.data(), split);
+ authority = authority.substr(split + 1);
+ }
+
+ hostport.assign(authority.data(), authority.size());
+ }
+
+ boost::optional<user_and_pass> user_and_pass::get(boost::string_ref userinfo)
+ {
+ static_assert(std::is_same<std::string::size_type, boost::string_ref::size_type>());
+ boost::optional<user_and_pass> out = user_and_pass{};
+
+ const auto split = userinfo.find(':');
+ if (split != boost::string_ref::npos)
+ {
+ out->user.assign(userinfo.data(), split);
+ userinfo = userinfo.substr(split + 1);
+ }
+ else
+ {
+ out->user.assign(userinfo.data(), userinfo.size());
+ userinfo = {};
+ }
+
+ out->pass.assign(userinfo.data(), userinfo.size());
+ if (percent_decoding(out->user) && percent_decoding(out->pass))
+ return out;
+ return boost::none;
+ }
+
+ boost::optional<uri_components> uri_components::get(const boost::string_ref uri)
+ {
+ scheme_and_authority result1{uri};
+ userinfo_and_hostport result2{result1.authority};
+ auto result3 = user_and_pass::get(result2.userinfo);
+ if (!result3)
+ return boost::none;
+
+ boost::optional<uri_components> out = uri_components{};
+ out->scheme = std::move(result1.scheme);
+ out->userinfo = std::move(*result3);
+ out->hostport = std::move(result2.hostport);
+ return out;
+ }
+
void get_network_address_host_and_port(const std::string& address, std::string& host, std::string& port)
{
// If IPv6 address format with port "[addr:addr:addr:...:addr]:port"
@@ -104,7 +196,6 @@ namespace net
if (epee::string_tools::get_ip_int32_from_string(ip, host_str))
return {epee::net_utils::ipv4_network_address{ip, port}};
}
-
return make_error_code(net::error::unsupported_address);
}
@@ -165,4 +256,46 @@ namespace net
return result;
}
+
+ namespace socks
+ {
+ endpoint::endpoint()
+ : endpoint(boost::asio::ip::tcp::endpoint{})
+ {}
+
+ endpoint::endpoint(const boost::asio::ip::tcp::endpoint& address)
+ : address(address), userinfo(), ver(version::v4a)
+ {}
+
+ expect<endpoint> endpoint::get(const boost::string_ref uri)
+ {
+ auto components = uri_components::get(uri);
+ if (!components)
+ return {net::error::invalid_encoding};
+ auto tcp_endpoint = get_tcp_endpoint(components->hostport);
+ if (!tcp_endpoint)
+ return tcp_endpoint.error();
+
+ endpoint out{};
+ if (components->scheme.empty() || components->scheme == "socks" || components->scheme == "socks4a")
+ out.ver = version::v4a;
+ else if (components->scheme == "socks4")
+ out.ver = version::v4;
+ else if (components->scheme == "socks5")
+ out.ver = version::v5;
+ else
+ return {net::error::invalid_scheme};
+
+ // Only version 5 supports user/pass authentication
+ if (!components->userinfo.user.empty() || !components->userinfo.pass.empty())
+ {
+ if (out.ver != version::v5)
+ return {net::error::unexpected_userinfo};
+ }
+
+ out.address = std::move(*tcp_endpoint);
+ out.userinfo = std::move(components->userinfo);
+ return out;
+ }
+ }
}