From 038782a60d1d065e87ece873790ba9aa00b5d4eb Mon Sep 17 00:00:00 2001 From: RaphiMC <50594595+RaphiMC@users.noreply.github.com> Date: Wed, 5 Apr 2023 15:05:46 +0200 Subject: [PATCH] Implemented HAProxy protocol for backend connections --- .../raphimc/viaproxy/cli/options/Options.java | 5 ++++- .../client2proxy/Client2ProxyHandler.java | 19 +++++++++++++++++-- .../Proxy2ServerChannelInitializer.java | 11 ++++++++--- 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/src/main/java/net/raphimc/viaproxy/cli/options/Options.java b/src/main/java/net/raphimc/viaproxy/cli/options/Options.java index 16db506..ec756b3 100644 --- a/src/main/java/net/raphimc/viaproxy/cli/options/Options.java +++ b/src/main/java/net/raphimc/viaproxy/cli/options/Options.java @@ -44,6 +44,7 @@ public class Options { public static boolean ONLINE_MODE; public static boolean OPENAUTHMOD_AUTH; public static boolean BETACRAFT_AUTH; + public static URI PROXY_URL; // Example: type://address:port or type://username:password@address:port // GUI only config options public static Account MC_ACCOUNT; @@ -56,7 +57,7 @@ public class Options { public static boolean INTERNAL_SRV_MODE; // Example: ip\7port\7version\7mppass public static boolean LOCAL_SOCKET_AUTH; public static String RESOURCE_PACK_URL; // Example: http://example.com/resourcepack.zip - public static URI PROXY_URL; // Example: type://address:port or type://username:password@address:port + public static boolean HAPROXY_PROTOCOL; public static void parse(final String[] args) throws IOException { final OptionParser parser = new OptionParser(); @@ -76,6 +77,7 @@ public class Options { final OptionSpec betaCraftAuth = parser.accepts("betacraft_auth", "Use BetaCraft authentication servers for classic"); final OptionSpec resourcePackUrl = parser.acceptsAll(asList("resource_pack_url", "resource_pack", "rpu", "rp"), "URL of a resource pack which all connecting clients can optionally download").withRequiredArg().ofType(String.class); final OptionSpec proxyUrl = parser.acceptsAll(asList("proxy_url", "proxy"), "URL of a SOCKS(4/5)/HTTP(S) proxy which will be used for TCP connections").withRequiredArg().ofType(String.class); + final OptionSpec haProxyProtocol = parser.acceptsAll(asList("haproxy-protocol", "haproxy"), "Send HAProxy protocol messages to the backend server"); PluginManager.EVENT_MANAGER.call(new PreOptionsParseEvent(parser)); final OptionSet options = parser.parse(args); @@ -113,6 +115,7 @@ public class Options { System.exit(1); } } + HAPROXY_PROTOCOL = options.has(haProxyProtocol); PluginManager.EVENT_MANAGER.call(new PostOptionsParseEvent(options)); } diff --git a/src/main/java/net/raphimc/viaproxy/proxy/client2proxy/Client2ProxyHandler.java b/src/main/java/net/raphimc/viaproxy/proxy/client2proxy/Client2ProxyHandler.java index 9beffa8..727d937 100644 --- a/src/main/java/net/raphimc/viaproxy/proxy/client2proxy/Client2ProxyHandler.java +++ b/src/main/java/net/raphimc/viaproxy/proxy/client2proxy/Client2ProxyHandler.java @@ -26,6 +26,7 @@ import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.handler.codec.haproxy.*; import net.raphimc.netminecraft.constants.ConnectionState; import net.raphimc.netminecraft.constants.MCPackets; import net.raphimc.netminecraft.constants.MCPipeline; @@ -61,6 +62,8 @@ import net.raphimc.viaproxy.util.logging.Logger; import javax.crypto.SecretKey; import java.math.BigInteger; import java.net.ConnectException; +import java.net.Inet4Address; +import java.net.InetSocketAddress; import java.nio.channels.UnresolvedAddressException; import java.nio.charset.StandardCharsets; import java.security.GeneralSecurityException; @@ -69,6 +72,7 @@ import java.security.NoSuchAlgorithmException; import java.security.spec.InvalidKeySpecException; import java.time.Instant; import java.util.Arrays; +import java.util.Collections; import java.util.Random; import java.util.function.Supplier; import java.util.regex.Pattern; @@ -239,8 +243,6 @@ public class Client2ProxyHandler extends SimpleChannelInboundHandler { Logger.u_info("connect", this.proxyConnection.getC2P().remoteAddress(), this.proxyConnection.getGameProfile(), "[" + clientVersion.getName() + " <-> " + serverVersion.getName() + "] Connecting to " + serverAddress.getAddress() + ":" + serverAddress.getPort()); try { this.proxyConnection.connectToServer(serverAddress, serverVersion); - this.proxyConnection.getChannel().writeAndFlush(new C2SHandshakePacket(clientVersion.getOriginalVersion(), serverAddress.getAddress(), serverAddress.getPort(), packet.intendedState)).await().addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE); - this.proxyConnection.setConnectionState(packet.intendedState); } catch (Throwable e) { if (e instanceof ConnectException || e instanceof UnresolvedAddressException) { // Trust me, this is not always false this.proxyConnection.kickClient("§cCould not connect to the backend server!\n§cTry again in a few seconds."); @@ -249,6 +251,19 @@ public class Client2ProxyHandler extends SimpleChannelInboundHandler { this.proxyConnection.kickClient("§cAn error occurred while connecting to the backend server: " + e.getMessage() + "\n§cCheck the console for more information."); } } + + if (Options.HAPROXY_PROTOCOL) { + final InetSocketAddress sourceAddress = (InetSocketAddress) this.proxyConnection.getC2P().remoteAddress(); + final InetSocketAddress targetAddress = (InetSocketAddress) this.proxyConnection.getChannel().remoteAddress(); + final HAProxyProxiedProtocol protocol = sourceAddress.getAddress() instanceof Inet4Address ? HAProxyProxiedProtocol.TCP4 : HAProxyProxiedProtocol.TCP6; + final HAProxyTLV tlv = new HAProxyTLV((byte) 0xE0, Unpooled.buffer().writeInt(clientVersion.getOriginalVersion())); + + final HAProxyMessage haProxyMessage = new HAProxyMessage(HAProxyProtocolVersion.V2, HAProxyCommand.PROXY, protocol, sourceAddress.getAddress().getHostAddress(), targetAddress.getAddress().getHostAddress(), sourceAddress.getPort(), targetAddress.getPort(), Collections.singletonList(tlv)); + this.proxyConnection.getChannel().writeAndFlush(haProxyMessage).addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE); + } + + this.proxyConnection.getChannel().writeAndFlush(new C2SHandshakePacket(clientVersion.getOriginalVersion(), serverAddress.getAddress(), serverAddress.getPort(), packet.intendedState)).await().addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE); + this.proxyConnection.setConnectionState(packet.intendedState); } private void handleLoginHello(C2SLoginHelloPacket1_7 packet) throws NoSuchAlgorithmException, InvalidKeySpecException, AuthenticationException { diff --git a/src/main/java/net/raphimc/viaproxy/proxy/proxy2server/Proxy2ServerChannelInitializer.java b/src/main/java/net/raphimc/viaproxy/proxy/proxy2server/Proxy2ServerChannelInitializer.java index 32b6ea9..2fd527b 100644 --- a/src/main/java/net/raphimc/viaproxy/proxy/proxy2server/Proxy2ServerChannelInitializer.java +++ b/src/main/java/net/raphimc/viaproxy/proxy/proxy2server/Proxy2ServerChannelInitializer.java @@ -22,6 +22,7 @@ import com.viaversion.viaversion.connection.UserConnectionImpl; import com.viaversion.viaversion.protocol.ProtocolPipelineImpl; import io.netty.channel.Channel; import io.netty.channel.ChannelHandler; +import io.netty.handler.codec.haproxy.HAProxyMessageEncoder; import io.netty.handler.proxy.HttpProxyHandler; import io.netty.handler.proxy.ProxyHandler; import io.netty.handler.proxy.Socks4ProxyHandler; @@ -29,11 +30,11 @@ import io.netty.handler.proxy.Socks5ProxyHandler; import net.raphimc.netminecraft.constants.MCPipeline; import net.raphimc.netminecraft.netty.connection.MinecraftChannelInitializer; import net.raphimc.netminecraft.packet.registry.PacketRegistryUtil; +import net.raphimc.viaprotocolhack.util.VersionEnum; import net.raphimc.viaproxy.cli.options.Options; import net.raphimc.viaproxy.plugins.PluginManager; import net.raphimc.viaproxy.plugins.events.Proxy2ServerChannelInitializeEvent; import net.raphimc.viaproxy.plugins.events.types.ITyped; -import net.raphimc.viaproxy.protocolhack.impl.ViaProxyVPHPipeline; import net.raphimc.viaproxy.proxy.session.ProxyConnection; import java.net.InetSocketAddress; @@ -53,14 +54,18 @@ public class Proxy2ServerChannelInitializer extends MinecraftChannelInitializer channel.close(); return; } + final ProxyConnection proxyConnection = ProxyConnection.fromChannel(channel); final UserConnection user = new UserConnectionImpl(channel, true); new ProtocolPipelineImpl(user); - ProxyConnection.fromChannel(channel).setUserConnection(user); + proxyConnection.setUserConnection(user); - if (Options.PROXY_URL != null) { + if (Options.PROXY_URL != null && !proxyConnection.getServerVersion().equals(VersionEnum.bedrockLatest)) { channel.pipeline().addLast("viaproxy-proxy-handler", this.getProxyHandler()); } + if (Options.HAPROXY_PROTOCOL) { + channel.pipeline().addLast("viaproxy-haproxy-encoder", HAProxyMessageEncoder.INSTANCE); + } super.initChannel(channel); channel.attr(MCPipeline.PACKET_REGISTRY_ATTRIBUTE_KEY).set(PacketRegistryUtil.getHandshakeRegistry(true));