Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Expose ChannelInitializerHolder in protocol module #3776

Closed
Closed
53 changes: 53 additions & 0 deletions api/src/main/java/net/md_5/bungee/api/ProxyServer.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import net.md_5.bungee.api.plugin.Plugin;
import net.md_5.bungee.api.plugin.PluginManager;
import net.md_5.bungee.api.scheduler.TaskScheduler;
import net.md_5.bungee.protocol.channel.BungeeChannelInitializer;

public abstract class ProxyServer
{
Expand Down Expand Up @@ -311,4 +312,56 @@ public static void setInstance(ProxyServer instance)
*/
public abstract Title createTitle();

/**
* Get the unsafe methods of this class.
*
* @return the unsafe method interface
*/
public abstract Unsafe unsafe();

public interface Unsafe
{
/**
* Gets the frontend channel initializer
*
* @return the frontend channel initializer
*/
BungeeChannelInitializer getFrontendChannelInitializer();

/**
* Gets the backend channel initializer
*
* @return the backend channel initializer
*/
BungeeChannelInitializer getBackendChannelInitializer();

/**
* Gets the server info channel initializer
*
* @return the server info channel initializer
*/
BungeeChannelInitializer getServerInfoChannelInitializer();

/**
* Set the frontend channel initializer of this proxy
*
* @param channelInitializer the frontend channelInitializer to set
*/
void setFrontendChannelInitializer(BungeeChannelInitializer channelInitializer);

/**
* Set the backend channel initializer of this proxy
*
* @param channelInitializer the backend channelInitializer to set
*/
void setBackendChannelInitializer(BungeeChannelInitializer channelInitializer);

/**
* Set the server info channel initializer of this proxy
*
* @param channelInitializer the server info channelInitializer to set
*/
void setServerInfoChannelInitializer(BungeeChannelInitializer channelInitializer);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package net.md_5.bungee.protocol.channel;

import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;

/**
* This class hold a netty channel initializer that calls the given {@link ChannelAcceptor}
* Use {@link BungeeChannelInitializer#create(ChannelAcceptor)} to create a new instance
* <p>
* Please note that this API is unsafe and doesn't provide any guarantees about the stability of the
* channel pipeline. Use at your own risk.
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public abstract class BungeeChannelInitializer
{
public abstract ChannelAcceptor getChannelAcceptor();
public abstract ChannelInitializer<Channel> getChannelInitializer();

/**
* Creates a new instance of BungeeChannelInitializer
*
* @param acceptor the {@link ChannelAcceptor} that will accept the channel and initializer the pipeline
* @return {@link BungeeChannelInitializer} containing a cached {@link ChannelInitializer} that will call the acceptor
*/
public static BungeeChannelInitializer create(ChannelAcceptor acceptor)
{
return new BungeeChannelInitializer()
{
@Getter
private final ChannelAcceptor channelAcceptor = acceptor;

@Getter // cache the ChannelInitializer
private final ChannelInitializer<Channel> channelInitializer = new ChannelInitializer<Channel>()
{
@Override
protected void initChannel(Channel channel) throws Exception
{
if ( !getChannelAcceptor().accept( channel ) )
{
channel.close();
}
}
};
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package net.md_5.bungee.protocol.channel;

import io.netty.channel.Channel;

@FunctionalInterface
public interface ChannelAcceptor
{
/**
* Inside this method the pipeline should be initialized.
*
* @param channel the channel to be accepted and initialized
* @return if the channel was accepted
*/
boolean accept(Channel channel);
}
24 changes: 23 additions & 1 deletion proxy/src/main/java/net/md_5/bungee/BungeeCord.java
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@
import net.md_5.bungee.netty.PipelineUtils;
import net.md_5.bungee.protocol.DefinedPacket;
import net.md_5.bungee.protocol.ProtocolConstants;
import net.md_5.bungee.protocol.channel.BungeeChannelInitializer;
import net.md_5.bungee.protocol.packet.PluginMessage;
import net.md_5.bungee.query.RemoteQuery;
import net.md_5.bungee.scheduler.BungeeScheduler;
Expand Down Expand Up @@ -188,6 +189,21 @@ public static BungeeCord getInstance()
return (BungeeCord) ProxyServer.getInstance();
}

private final Unsafe unsafe = new Unsafe()
{
@Getter
@Setter
private BungeeChannelInitializer frontendChannelInitializer;

@Getter
@Setter
private BungeeChannelInitializer backendChannelInitializer;

@Getter
@Setter
private BungeeChannelInitializer serverInfoChannelInitializer;
};

@SuppressFBWarnings("DM_DEFAULT_ENCODING")
public BungeeCord() throws IOException
{
Expand Down Expand Up @@ -360,7 +376,7 @@ public void operationComplete(ChannelFuture future) throws Exception
.channel( PipelineUtils.getServerChannel( info.getSocketAddress() ) )
.option( ChannelOption.SO_REUSEADDR, true ) // TODO: Move this elsewhere!
.childAttr( PipelineUtils.LISTENER, info )
.childHandler( PipelineUtils.SERVER_CHILD )
.childHandler( ProxyServer.getInstance().unsafe().getFrontendChannelInitializer().getChannelInitializer() )
.group( eventLoops )
.localAddress( info.getSocketAddress() )
.bind().addListener( listener );
Expand Down Expand Up @@ -831,4 +847,10 @@ public Title createTitle()
{
return new BungeeTitle();
}

@Override
public Unsafe unsafe()
{
return unsafe;
}
}
2 changes: 1 addition & 1 deletion proxy/src/main/java/net/md_5/bungee/BungeeServerInfo.java
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ public void operationComplete(ChannelFuture future) throws Exception
new Bootstrap()
.channel( PipelineUtils.getChannel( socketAddress ) )
.group( BungeeCord.getInstance().eventLoops )
.handler( PipelineUtils.BASE_SERVERSIDE )
.handler( ProxyServer.getInstance().unsafe().getServerInfoChannelInitializer().getChannelInitializer() )
.option( ChannelOption.CONNECT_TIMEOUT_MILLIS, BungeeCord.getInstance().getConfig().getRemotePingTimeout() )
.remoteAddress( socketAddress )
.connect()
Expand Down
1 change: 1 addition & 0 deletions proxy/src/main/java/net/md_5/bungee/ServerConnector.java
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ public void exception(Throwable t) throws Exception
@Override
public void connected(ChannelWrapper channel) throws Exception
{
channel.setVersion( user.getPendingConnection().getVersion() );
this.ch = channel;

this.handshakeHandler = new ForgeServerHandler( user, ch, target );
Expand Down
20 changes: 4 additions & 16 deletions proxy/src/main/java/net/md_5/bungee/UserConnection.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.util.internal.PlatformDependent;
import java.net.InetSocketAddress;
Expand Down Expand Up @@ -50,8 +48,6 @@
import net.md_5.bungee.netty.HandlerBoss;
import net.md_5.bungee.netty.PipelineUtils;
import net.md_5.bungee.protocol.DefinedPacket;
import net.md_5.bungee.protocol.MinecraftDecoder;
import net.md_5.bungee.protocol.MinecraftEncoder;
import net.md_5.bungee.protocol.PacketWrapper;
import net.md_5.bungee.protocol.Protocol;
import net.md_5.bungee.protocol.ProtocolConstants;
Expand Down Expand Up @@ -362,17 +358,6 @@ private void connect0(final ServerConnectRequest request)

pendingConnects.add( target );

ChannelInitializer initializer = new ChannelInitializer()
{
@Override
protected void initChannel(Channel ch) throws Exception
{
PipelineUtils.BASE_SERVERSIDE.initChannel( ch );
ch.pipeline().addAfter( PipelineUtils.FRAME_DECODER, PipelineUtils.PACKET_DECODER, new MinecraftDecoder( Protocol.HANDSHAKE, false, getPendingConnection().getVersion() ) );
ch.pipeline().addAfter( PipelineUtils.FRAME_PREPENDER, PipelineUtils.PACKET_ENCODER, new MinecraftEncoder( Protocol.HANDSHAKE, false, getPendingConnection().getVersion() ) );
ch.pipeline().get( HandlerBoss.class ).setHandler( new ServerConnector( bungee, UserConnection.this, target ) );
}
};
ChannelFutureListener listener = new ChannelFutureListener()
{
@Override
Expand Down Expand Up @@ -401,13 +386,16 @@ public void operationComplete(ChannelFuture future) throws Exception
{
sendMessage( bungee.getTranslation( "fallback_kick", connectionFailMessage( future.cause() ) ) );
}
} else
{
future.channel().pipeline().get( HandlerBoss.class ).setHandler( new ServerConnector( bungee, UserConnection.this, target ) );
}
}
};
Bootstrap b = new Bootstrap()
.channel( PipelineUtils.getChannel( target.getAddress() ) )
.group( ch.getHandle().eventLoop() )
.handler( initializer )
.handler( ProxyServer.getInstance().unsafe().getBackendChannelInitializer().getChannelInitializer() )
.option( ChannelOption.CONNECT_TIMEOUT_MILLIS, request.getConnectTimeout() )
.remoteAddress( target.getAddress() );
// Windows is bugged, multi homed users will just have to live with random connecting IPs
Expand Down
44 changes: 29 additions & 15 deletions proxy/src/main/java/net/md_5/bungee/netty/PipelineUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.Channel;
import io.netty.channel.ChannelException;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.ServerChannel;
Expand Down Expand Up @@ -51,33 +50,33 @@
import net.md_5.bungee.protocol.Varint21FrameDecoder;
import net.md_5.bungee.protocol.Varint21LengthFieldExtraBufPrepender;
import net.md_5.bungee.protocol.Varint21LengthFieldPrepender;
import net.md_5.bungee.protocol.channel.BungeeChannelInitializer;
import net.md_5.bungee.protocol.channel.ChannelAcceptor;

public class PipelineUtils
{

public static final AttributeKey<ListenerInfo> LISTENER = AttributeKey.valueOf( "ListerInfo" );
public static final ChannelInitializer<Channel> SERVER_CHILD = new ChannelInitializer<Channel>()

private static void setChannelInitializerHolders()
{
@Override
protected void initChannel(Channel ch) throws Exception
ProxyServer.getInstance().unsafe().setFrontendChannelInitializer( BungeeChannelInitializer.create( ch ->
{
SocketAddress remoteAddress = ( ch.remoteAddress() == null ) ? ch.parent().localAddress() : ch.remoteAddress();

if ( BungeeCord.getInstance().getConnectionThrottle() != null && BungeeCord.getInstance().getConnectionThrottle().throttle( remoteAddress ) )
{
ch.close();
return;
return false;
}

ListenerInfo listener = ch.attr( LISTENER ).get();

if ( BungeeCord.getInstance().getPluginManager().callEvent( new ClientConnectEvent( remoteAddress, listener ) ).isCancelled() )
{
ch.close();
return;
return false;
}

BASE.initChannel( ch );
BASE.accept( 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() ) );
Expand All @@ -88,10 +87,22 @@ protected void initChannel(Channel ch) throws Exception
{
ch.pipeline().addFirst( new HAProxyMessageDecoder() );
}
}
};
public static final Base BASE = new Base( false );
public static final Base BASE_SERVERSIDE = new Base( true );
return true;
} ) );

ProxyServer.getInstance().unsafe().setBackendChannelInitializer( BungeeChannelInitializer.create( ch ->
{
PipelineUtils.BASE_SERVERSIDE.accept( ch );
ch.pipeline().addAfter( PipelineUtils.FRAME_DECODER, PipelineUtils.PACKET_DECODER, new MinecraftDecoder( Protocol.HANDSHAKE, false, ProxyServer.getInstance().getProtocolVersion() ) );
ch.pipeline().addAfter( PipelineUtils.FRAME_PREPENDER, PipelineUtils.PACKET_ENCODER, new MinecraftEncoder( Protocol.HANDSHAKE, false, ProxyServer.getInstance().getProtocolVersion() ) );
return true;
} ) );

ProxyServer.getInstance().unsafe().setServerInfoChannelInitializer( BungeeChannelInitializer.create( BASE_SERVERSIDE ) );
}

private static final ChannelAcceptor BASE = new Base( false );
private static final ChannelAcceptor BASE_SERVERSIDE = new Base( true );
private static final KickStringWriter legacyKicker = new KickStringWriter();
private static final Varint21LengthFieldExtraBufPrepender serverFramePrepender = new Varint21LengthFieldExtraBufPrepender();
public static final String TIMEOUT_HANDLER = "timeout";
Expand Down Expand Up @@ -137,6 +148,8 @@ protected void initChannel(Channel ch) throws Exception
}
}
}

setChannelInitializerHolders();
}

public static EventLoopGroup newEventLoopGroup(int threads, ThreadFactory factory)
Expand Down Expand Up @@ -179,13 +192,13 @@ public static Class<? extends DatagramChannel> getDatagramChannel()

@NoArgsConstructor // for backwards compatibility
@AllArgsConstructor
public static final class Base extends ChannelInitializer<Channel>
public static final class Base implements ChannelAcceptor
{

private boolean toServer = false;

@Override
public void initChannel(Channel ch) throws Exception
public boolean accept(Channel ch)
{
try
{
Expand All @@ -204,6 +217,7 @@ public void initChannel(Channel ch) throws Exception
ch.pipeline().addLast( FRAME_PREPENDER, ( toServer ) ? serverFramePrepender : new Varint21LengthFieldPrepender() );

ch.pipeline().addLast( BOSS_HANDLER, new HandlerBoss() );
return true;
}
}
}