diff --git a/api/src/main/java/net/md_5/bungee/api/config/ListenerInfo.java b/api/src/main/java/net/md_5/bungee/api/config/ListenerInfo.java index bab9d727..42b84063 100644 --- a/api/src/main/java/net/md_5/bungee/api/config/ListenerInfo.java +++ b/api/src/main/java/net/md_5/bungee/api/config/ListenerInfo.java @@ -3,15 +3,15 @@ package net.md_5.bungee.api.config; import java.net.InetSocketAddress; import java.util.List; import java.util.Map; -import lombok.AccessLevel; +import lombok.AllArgsConstructor; import lombok.Data; -import lombok.Getter; /** * Class representing the configuration of a server listener. Used for allowing * multiple listeners on different ports. */ @Data +@AllArgsConstructor public class ListenerInfo { @@ -67,6 +67,16 @@ public class ListenerInfo * Whether to enable udp query. */ private final boolean queryEnabled; + /** + * Whether to support HAProxy PROXY protocol. + */ + private final boolean proxyProtocol; + + @Deprecated + public ListenerInfo(InetSocketAddress host, String motd, int maxPlayers, int tabListSize, List serverPriority, boolean forceDefault, Map forcedHosts, String tabListType, boolean setLocalAddress, boolean pingPassthrough, int queryPort, boolean queryEnabled) + { + this( host, motd, maxPlayers, tabListSize, serverPriority, forceDefault, forcedHosts, tabListType, setLocalAddress, pingPassthrough, queryPort, queryEnabled, false ); + } /** * Gets the highest priority server to join. diff --git a/proxy/pom.xml b/proxy/pom.xml index 3b36612c..1eae8733 100644 --- a/proxy/pom.xml +++ b/proxy/pom.xml @@ -29,6 +29,12 @@ 1.0.0 compile + + io.netty + netty-codec-haproxy + ${netty.version} + compile + io.netty netty-codec-http diff --git a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java index 84d8ce39..0d11201e 100644 --- a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java +++ b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java @@ -296,6 +296,11 @@ public class BungeeCord extends ProxyServer { for ( final ListenerInfo info : config.getListeners() ) { + if ( info.isProxyProtocol() ) + { + getLogger().log( Level.WARNING, "Using PROXY protocol for listener {0}, please ensure this listener is adequately firewalled.", info.getHost() ); + } + ChannelFutureListener listener = new ChannelFutureListener() { @Override diff --git a/proxy/src/main/java/net/md_5/bungee/UserConnection.java b/proxy/src/main/java/net/md_5/bungee/UserConnection.java index b8955a8a..c45f8412 100644 --- a/proxy/src/main/java/net/md_5/bungee/UserConnection.java +++ b/proxy/src/main/java/net/md_5/bungee/UserConnection.java @@ -452,7 +452,7 @@ public final class UserConnection implements ProxiedPlayer @Override public InetSocketAddress getAddress() { - return (InetSocketAddress) ch.getHandle().remoteAddress(); + return ch.getRemoteAddress(); } @Override diff --git a/proxy/src/main/java/net/md_5/bungee/conf/YamlConfig.java b/proxy/src/main/java/net/md_5/bungee/conf/YamlConfig.java index c7547c7f..8d79b5ab 100644 --- a/proxy/src/main/java/net/md_5/bungee/conf/YamlConfig.java +++ b/proxy/src/main/java/net/md_5/bungee/conf/YamlConfig.java @@ -247,6 +247,7 @@ public class YamlConfig implements ConfigurationAdapter boolean query = get( "query_enabled", false, val ); int queryPort = get( "query_port", 25577, val ); + boolean proxyProtocol = get( "proxy_protocol", false, val ); List serverPriority = new ArrayList<>( get( "priorities", Collections.EMPTY_LIST, val ) ); // Default server list migration @@ -271,7 +272,7 @@ public class YamlConfig implements ConfigurationAdapter } set( "priorities", serverPriority, val ); - ListenerInfo info = new ListenerInfo( address, motd, maxPlayers, tabListSize, serverPriority, forceDefault, forced, value.toString(), setLocalAddress, pingPassthrough, queryPort, query ); + ListenerInfo info = new ListenerInfo( address, motd, maxPlayers, tabListSize, serverPriority, forceDefault, forced, value.toString(), setLocalAddress, pingPassthrough, queryPort, query, proxyProtocol ); ret.add( info ); } diff --git a/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java b/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java index 68aa3f85..cb140d4a 100644 --- a/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java +++ b/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java @@ -574,7 +574,7 @@ public class InitialHandler extends PacketHandler implements PendingConnection @Override public InetSocketAddress getAddress() { - return (InetSocketAddress) ch.getHandle().remoteAddress(); + return ch.getRemoteAddress(); } @Override diff --git a/proxy/src/main/java/net/md_5/bungee/netty/ChannelWrapper.java b/proxy/src/main/java/net/md_5/bungee/netty/ChannelWrapper.java index 9050534a..2b2d15b3 100644 --- a/proxy/src/main/java/net/md_5/bungee/netty/ChannelWrapper.java +++ b/proxy/src/main/java/net/md_5/bungee/netty/ChannelWrapper.java @@ -5,8 +5,10 @@ import io.netty.channel.Channel; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; +import java.net.InetSocketAddress; import java.util.concurrent.TimeUnit; import lombok.Getter; +import lombok.Setter; import net.md_5.bungee.compress.PacketCompressor; import net.md_5.bungee.compress.PacketDecompressor; import net.md_5.bungee.protocol.MinecraftDecoder; @@ -20,6 +22,9 @@ public class ChannelWrapper private final Channel ch; @Getter + @Setter + private InetSocketAddress remoteAddress; + @Getter private volatile boolean closed; @Getter private volatile boolean closing; @@ -27,6 +32,7 @@ public class ChannelWrapper public ChannelWrapper(ChannelHandlerContext ctx) { this.ch = ctx.channel(); + this.remoteAddress = (InetSocketAddress) this.ch.remoteAddress(); } public void setProtocol(Protocol protocol) diff --git a/proxy/src/main/java/net/md_5/bungee/netty/HandlerBoss.java b/proxy/src/main/java/net/md_5/bungee/netty/HandlerBoss.java index cbdd976b..93776626 100644 --- a/proxy/src/main/java/net/md_5/bungee/netty/HandlerBoss.java +++ b/proxy/src/main/java/net/md_5/bungee/netty/HandlerBoss.java @@ -4,8 +4,10 @@ import com.google.common.base.Preconditions; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.handler.codec.DecoderException; +import io.netty.handler.codec.haproxy.HAProxyMessage; import io.netty.handler.timeout.ReadTimeoutException; import java.io.IOException; +import java.net.InetSocketAddress; import java.util.logging.Level; import net.md_5.bungee.api.ProxyServer; import net.md_5.bungee.connection.CancelSendSignal; @@ -65,6 +67,20 @@ public class HandlerBoss extends ChannelInboundHandlerAdapter @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + if ( msg instanceof HAProxyMessage ) + { + HAProxyMessage proxy = (HAProxyMessage) msg; + InetSocketAddress newAddress = new InetSocketAddress( proxy.sourceAddress(), proxy.sourcePort() ); + + ProxyServer.getInstance().getLogger().log( Level.FINE, "Set remote address via PROXY {0} -> {1}", new Object[] + { + channel.getRemoteAddress(), newAddress + } ); + + channel.setRemoteAddress( newAddress ); + return; + } + if ( handler != null ) { PacketWrapper packet = (PacketWrapper) msg; diff --git a/proxy/src/main/java/net/md_5/bungee/netty/PipelineUtils.java b/proxy/src/main/java/net/md_5/bungee/netty/PipelineUtils.java index 7fd2ff75..f239c7d8 100644 --- a/proxy/src/main/java/net/md_5/bungee/netty/PipelineUtils.java +++ b/proxy/src/main/java/net/md_5/bungee/netty/PipelineUtils.java @@ -16,6 +16,7 @@ import io.netty.channel.nio.NioEventLoopGroup; 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 io.netty.util.AttributeKey; import io.netty.util.internal.PlatformDependent; @@ -48,12 +49,19 @@ public class PipelineUtils @Override protected void initChannel(Channel ch) throws Exception { + ListenerInfo listener = ch.attr( LISTENER ).get(); + BASE.initChannel( ch ); ch.pipeline().addBefore( FRAME_DECODER, LEGACY_DECODER, new LegacyDecoder() ); ch.pipeline().addAfter( FRAME_DECODER, PACKET_DECODER, new MinecraftDecoder( Protocol.HANDSHAKE, true, ProxyServer.getInstance().getProtocolVersion() ) ); ch.pipeline().addAfter( FRAME_PREPENDER, PACKET_ENCODER, new MinecraftEncoder( Protocol.HANDSHAKE, true, ProxyServer.getInstance().getProtocolVersion() ) ); ch.pipeline().addBefore( FRAME_PREPENDER, LEGACY_KICKER, new KickStringWriter() ); - ch.pipeline().get( HandlerBoss.class ).setHandler( new InitialHandler( BungeeCord.getInstance(), ch.attr( LISTENER ).get() ) ); + ch.pipeline().get( HandlerBoss.class ).setHandler( new InitialHandler( BungeeCord.getInstance(), listener ) ); + + if ( listener.isProxyProtocol() ) + { + ch.pipeline().addFirst( new HAProxyMessageDecoder() ); + } } }; public static final Base BASE = new Base();