From 28ab1bea9c515b1d34296846ab2c5d1e2b5dfe65 Mon Sep 17 00:00:00 2001 From: Martin Michelsen Date: Tue, 17 Jun 2025 01:19:21 -0700 Subject: [PATCH] add IPv6 support in proxy --- src/AsyncUtils.hh | 8 ++++++++ src/CommandFormats.hh | 14 ++++++++++++++ src/ProxyCommands.cc | 3 +++ 3 files changed, 25 insertions(+) diff --git a/src/AsyncUtils.hh b/src/AsyncUtils.hh index 53680283..1d531047 100644 --- a/src/AsyncUtils.hh +++ b/src/AsyncUtils.hh @@ -224,6 +224,14 @@ inline asio::ip::tcp::endpoint make_endpoint_ipv4(uint32_t addr, uint16_t port) return asio::ip::tcp::endpoint(asio::ip::address_v4(addr), port); } +inline asio::ip::tcp::endpoint make_endpoint_ipv6(const void* addr, uint16_t port) { + std::array bytes; + for (size_t z = 0; z < 0x10; z++) { + bytes[z] = reinterpret_cast(addr)[z]; + } + return asio::ip::tcp::endpoint(asio::ip::address_v6(bytes), port); +} + inline std::string str_for_endpoint(const asio::ip::tcp::endpoint& ep) { return ep.address().to_string() + std::format(":{}", ep.port()); } diff --git a/src/CommandFormats.hh b/src/CommandFormats.hh index 6cdcbccd..75bee8d0 100644 --- a/src/CommandFormats.hh +++ b/src/CommandFormats.hh @@ -807,6 +807,20 @@ struct C_WriteFileConfirmation_V3_BB_13_A7 { using S_Reconnect_19 = S_ReconnectT; check_struct_size(S_Reconnect_19, 8); +// Sylverant implements an IPv6 version of this command, but it's not obvious +// why. IPv6 technically did exist as a draft standard at the time of PSO's +// development, but it wasn't widely used until over a decade later. IPv6 +// support is not implemented in any version of the PSO client that I've seen, +// but we implement Sylverant's version of this command anyway because newserv +// may connect to Sylverant via IPv6 when using the proxy. Sylverant sends the +// value 6 in header.flag in this case, presumably to indicate the protocol. + +struct S_ReconnectIPv6_Extension_19 { + parray address; + le_uint16_t port = 0; + le_uint16_t unused = 0; +} __packed_ws__(S_ReconnectIPv6_Extension_19, 0x14); + // Because PSO PC and some versions of PSO DC/GC use the same port but different // protocols, we use a specially-crafted 19 command to send them to two // different ports depending on the client version. I first saw this technique diff --git a/src/ProxyCommands.cc b/src/ProxyCommands.cc index c8a9215c..59abc546 100644 --- a/src/ProxyCommands.cc +++ b/src/ProxyCommands.cc @@ -779,6 +779,9 @@ static asio::awaitable S_19_U_14(shared_ptr c, Channel::M if (is_patch(c->version())) { auto& cmd = msg.check_size_t(); new_ep = make_endpoint_ipv4(cmd.address, cmd.port); + } else if (msg.flag == 6 && msg.data.size() >= sizeof(S_ReconnectIPv6_Extension_19)) { + auto& cmd = msg.check_size_t(0xFFFF); + new_ep = make_endpoint_ipv6(cmd.address.data(), cmd.port); } else { // This weird maximum size is here to properly handle the version-split // command that some servers (including newserv) use on port 9100