diff options
| author | tobtoht <tob@featherwallet.org> | 2026-04-20 07:49:04 +0000 |
|---|---|---|
| committer | tobtoht <tob@featherwallet.org> | 2026-04-20 07:49:04 +0000 |
| commit | 8b6be66bef8cce6e074074b7329be9509b93dbb8 (patch) | |
| tree | 6dee62eec590447f49e6a29d649c294f4757a2a4 /src/net/parse.cpp | |
| parent | e75125a30b1f70e74eded99d3fc4f3e98938c6d5 (diff) | |
| parent | ce5437f0677966c15d2946bc7c068975aa6bfd83 (diff) | |
| download | monzero-core-8b6be66bef8cce6e074074b7329be9509b93dbb8.tar.gz monzero-core-8b6be66bef8cce6e074074b7329be9509b93dbb8.tar.xz monzero-core-8b6be66bef8cce6e074074b7329be9509b93dbb8.zip | |
Merge pull request #10411
ce5437f Add Socks v5 support to daemon and wallet (Lee *!* Clagett)
Diffstat (limited to 'src/net/parse.cpp')
| -rw-r--r-- | src/net/parse.cpp | 135 |
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; + } + } } |
