diff --git a/proxy/build.gradle b/proxy/build.gradle index 47f174565..de26f9648 100644 --- a/proxy/build.gradle +++ b/proxy/build.gradle @@ -29,6 +29,7 @@ dependencies { compile project(':velocity-native') compile "io.netty:netty-codec:${nettyVersion}" + compile "io.netty:netty-codec-haproxy:${nettyVersion}" compile "io.netty:netty-codec-http:${nettyVersion}" compile "io.netty:netty-handler:${nettyVersion}" compile "io.netty:netty-transport-native-epoll:${nettyVersion}" diff --git a/proxy/src/main/java/com/velocitypowered/proxy/config/VelocityConfiguration.java b/proxy/src/main/java/com/velocitypowered/proxy/config/VelocityConfiguration.java index f42cd28ce..4c1e64d2b 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/config/VelocityConfiguration.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/config/VelocityConfiguration.java @@ -280,6 +280,10 @@ public class VelocityConfiguration extends AnnotatedConfig implements ProxyConfi return advanced.getReadTimeout(); } + public boolean isProxyProtocol() { + return advanced.isProxyProtocol(); + } + @Override public String toString() { return MoreObjects.toStringHelper(this) @@ -433,6 +437,9 @@ public class VelocityConfiguration extends AnnotatedConfig implements ProxyConfi @Comment({"Specify a read timeout for connections here. The default is 30 seconds."}) @ConfigKey("read-timeout") private int readTimeout = 30000; + @Comment("Enables compatibility with HAProxy.") + @ConfigKey("proxy-protocol") + private boolean proxyProtocol = false; private Advanced() { } @@ -444,6 +451,7 @@ public class VelocityConfiguration extends AnnotatedConfig implements ProxyConfi this.loginRatelimit = toml.getLong("login-ratelimit", 3000L).intValue(); this.connectionTimeout = toml.getLong("connection-timeout", 5000L).intValue(); this.readTimeout = toml.getLong("read-timeout", 30000L).intValue(); + this.proxyProtocol = toml.getBoolean("proxy-protocol", false); } } @@ -467,6 +475,10 @@ public class VelocityConfiguration extends AnnotatedConfig implements ProxyConfi return readTimeout; } + public boolean isProxyProtocol() { + return proxyProtocol; + } + @Override public String toString() { return "Advanced{" + @@ -475,6 +487,7 @@ public class VelocityConfiguration extends AnnotatedConfig implements ProxyConfi ", loginRatelimit=" + loginRatelimit + ", connectionTimeout=" + connectionTimeout + ", readTimeout=" + readTimeout + + ", proxyProtocol=" + proxyProtocol + '}'; } } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/MinecraftConnection.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/MinecraftConnection.java index 18e0b7307..6fd8aca54 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/MinecraftConnection.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/MinecraftConnection.java @@ -12,12 +12,15 @@ import com.velocitypowered.proxy.protocol.StateRegistry; import com.velocitypowered.proxy.protocol.netty.*; import io.netty.buffer.ByteBuf; import io.netty.channel.*; +import io.netty.handler.codec.haproxy.HAProxyMessage; import io.netty.util.ReferenceCountUtil; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; +import java.net.InetSocketAddress; +import java.net.SocketAddress; import java.security.GeneralSecurityException; import static com.velocitypowered.proxy.network.Connections.*; @@ -30,6 +33,7 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter { private static final Logger logger = LogManager.getLogger(MinecraftConnection.class); private final Channel channel; + private SocketAddress remoteAddress; private StateRegistry state; private MinecraftSessionHandler sessionHandler; private int protocolVersion; @@ -40,6 +44,7 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter { public MinecraftConnection(Channel channel, VelocityServer server) { this.channel = channel; + this.remoteAddress = channel.remoteAddress(); this.server = server; this.state = StateRegistry.HANDSHAKE; } @@ -77,6 +82,13 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter { if (!pkt.handle(sessionHandler)) { sessionHandler.handleGeneric((MinecraftPacket) msg); } + } else if (msg instanceof HAProxyMessage) { + if (sessionHandler.beforeHandle()) { + return; + } + + HAProxyMessage proxyMessage = (HAProxyMessage) msg; + this.remoteAddress = new InetSocketAddress(proxyMessage.sourceAddress(), proxyMessage.sourcePort()); } else if (msg instanceof ByteBuf) { try { if (sessionHandler.beforeHandle()) { @@ -153,6 +165,10 @@ public class MinecraftConnection extends ChannelInboundHandlerAdapter { return !channel.isActive(); } + public SocketAddress getRemoteAddress() { + return remoteAddress; + } + public StateRegistry getState() { return state; } diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java index 7d87aba2a..b16c64878 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/ConnectedPlayer.java @@ -122,7 +122,7 @@ public class ConnectedPlayer implements MinecraftConnectionAssociation, Player { @Override public InetSocketAddress getRemoteAddress() { - return (InetSocketAddress) connection.getChannel().remoteAddress(); + return (InetSocketAddress) connection.getRemoteAddress(); } @Override diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/HandshakeSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/HandshakeSessionHandler.java index 859d70782..dd5edcfe7 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/HandshakeSessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/HandshakeSessionHandler.java @@ -77,7 +77,7 @@ public class HandshakeSessionHandler implements MinecraftSessionHandler { return true; } - InetAddress address = ((InetSocketAddress) connection.getChannel().remoteAddress()).getAddress(); + InetAddress address = ((InetSocketAddress) connection.getRemoteAddress()).getAddress(); if (!server.getIpAttemptLimiter().attempt(address)) { connection.closeWith(Disconnect.create(TextComponent.of("You are logging in too fast, try again later."))); return true; @@ -129,7 +129,7 @@ public class HandshakeSessionHandler implements MinecraftSessionHandler { @Override public InetSocketAddress getRemoteAddress() { - return (InetSocketAddress) connection.getChannel().remoteAddress(); + return (InetSocketAddress) connection.getRemoteAddress(); } @Override diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/InitialInboundConnection.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/InitialInboundConnection.java index 6c4ba9069..1518eb5d5 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/InitialInboundConnection.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/InitialInboundConnection.java @@ -18,7 +18,7 @@ class InitialInboundConnection implements InboundConnection { @Override public InetSocketAddress getRemoteAddress() { - return (InetSocketAddress) connection.getChannel().remoteAddress(); + return (InetSocketAddress) connection.getRemoteAddress(); } @Override diff --git a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/LoginSessionHandler.java b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/LoginSessionHandler.java index 6111f8d27..5f42779ab 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/connection/client/LoginSessionHandler.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/connection/client/LoginSessionHandler.java @@ -101,7 +101,7 @@ public class LoginSessionHandler implements MinecraftSessionHandler { byte[] decryptedSharedSecret = EncryptionUtils.decryptRsa(serverKeyPair, packet.getSharedSecret()); String serverId = EncryptionUtils.generateServerId(decryptedSharedSecret, serverKeyPair.getPublic()); - String playerIp = ((InetSocketAddress) inbound.getChannel().remoteAddress()).getHostString(); + String playerIp = ((InetSocketAddress) inbound.getRemoteAddress()).getHostString(); server.getHttpClient() .get(new URL(String.format(MOJANG_SERVER_AUTH_URL, login.getUsername(), serverId, playerIp))) .thenAcceptAsync(profileResponse -> { diff --git a/proxy/src/main/java/com/velocitypowered/proxy/network/ConnectionManager.java b/proxy/src/main/java/com/velocitypowered/proxy/network/ConnectionManager.java index 65301cd77..584843717 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/network/ConnectionManager.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/network/ConnectionManager.java @@ -20,6 +20,7 @@ import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioDatagramChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; +import io.netty.handler.codec.haproxy.HAProxyMessageDecoder; import io.netty.handler.timeout.ReadTimeoutHandler; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -77,6 +78,10 @@ public final class ConnectionManager { connection.setState(StateRegistry.HANDSHAKE); connection.setSessionHandler(new HandshakeSessionHandler(connection, server)); ch.pipeline().addLast(Connections.HANDLER, connection); + + if (server.getConfiguration().isProxyProtocol()) { + ch.pipeline().addFirst(new HAProxyMessageDecoder()); + } } }) .childOption(ChannelOption.TCP_NODELAY, true)