diff --git a/api/src/main/java/net/md_5/bungee/api/event/PlayerHandshakeEvent.java b/api/src/main/java/net/md_5/bungee/api/event/PlayerHandshakeEvent.java index 06db41e1..2f7b38d9 100644 --- a/api/src/main/java/net/md_5/bungee/api/event/PlayerHandshakeEvent.java +++ b/api/src/main/java/net/md_5/bungee/api/event/PlayerHandshakeEvent.java @@ -4,7 +4,6 @@ import lombok.Data; import lombok.EqualsAndHashCode; import lombok.ToString; import net.md_5.bungee.api.connection.PendingConnection; -import net.md_5.bungee.protocol.packet.Packet2Handshake; import net.md_5.bungee.api.plugin.Event; import net.md_5.bungee.protocol.packet.Handshake; diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/MinecraftCodec.java b/protocol/src/main/java/net/md_5/bungee/protocol/MinecraftCodec.java index 530253ea..fca9ffc9 100644 --- a/protocol/src/main/java/net/md_5/bungee/protocol/MinecraftCodec.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/MinecraftCodec.java @@ -5,31 +5,38 @@ import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToMessageCodec; import java.util.List; import lombok.AllArgsConstructor; +import lombok.Setter; @AllArgsConstructor public class MinecraftCodec extends MessageToMessageCodec { + @Setter private Protocol protocol; + private boolean server; @Override protected void encode(ChannelHandlerContext ctx, DefinedPacket msg, List out) throws Exception { + Protocol.ProtocolDirection prot = ( server ) ? protocol.TO_CLIENT : protocol.TO_SERVER; + ByteBuf buf = ctx.alloc().buffer(); - DefinedPacket.writeVarInt( protocol.getId( msg.getClass() ), buf ); + DefinedPacket.writeVarInt( prot.getId( msg.getClass() ), buf ); msg.write( buf ); } @Override protected void decode(ChannelHandlerContext ctx, ByteBuf msg, List out) throws Exception { + Protocol.ProtocolDirection prot = ( server ) ? protocol.TO_SERVER : protocol.TO_CLIENT; + int packetId = DefinedPacket.readVarInt( msg ); ByteBuf copy = msg.copy(); DefinedPacket packet = null; - if ( protocol.hasPacket( packetId ) ) + if ( prot.hasPacket( packetId ) ) { - packet = protocol.createPacket( packetId ); + packet = prot.createPacket( packetId ); packet.read( msg ); } diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/Protocol.java b/protocol/src/main/java/net/md_5/bungee/protocol/Protocol.java index fdcce8b7..2bc5abf5 100644 --- a/protocol/src/main/java/net/md_5/bungee/protocol/Protocol.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/Protocol.java @@ -26,74 +26,56 @@ public enum Protocol { // Undef - SERVER_HANDSHAKE + HANDSHAKE { { - registerPacket( 0x00, Handshake.class ); + TO_SERVER.registerPacket( 0x00, Handshake.class ); } }, // 0 - CLIENT_GAME + GAME { { - registerPacket( 0x00, KeepAlive.class ); - registerPacket( 0x01, Login.class ); - registerPacket( 0x02, Chat.class ); - registerPacket( 0x07, Respawn.class ); - registerPacket( 0x3B, PlayerListItem.class ); - registerPacket( 0x3D, TabComplete.class ); - registerPacket( 0x3E, ScoreboardObjective.class ); - registerPacket( 0x3F, ScoreboardScore.class ); - registerPacket( 0x40, ScoreboardDisplay.class ); - registerPacket( 0x41, Team.class ); - registerPacket( 0x42, PluginMessage.class ); - registerPacket( 0x43, Kick.class ); - } - }, - // 0 - SERVER_GAME - { - - { - registerPacket( 0x00, KeepAlive.class ); - registerPacket( 0x14, TabComplete.class ); - registerPacket( 0x15, ClientSettings.class ); - registerPacket( 0x17, PluginMessage.class ); + TO_CLIENT.registerPacket( 0x00, KeepAlive.class ); + TO_CLIENT.registerPacket( 0x01, Login.class ); + TO_CLIENT.registerPacket( 0x02, Chat.class ); + TO_CLIENT.registerPacket( 0x07, Respawn.class ); + TO_CLIENT.registerPacket( 0x3B, PlayerListItem.class ); + TO_CLIENT.registerPacket( 0x3D, TabComplete.class ); + TO_CLIENT.registerPacket( 0x3E, ScoreboardObjective.class ); + TO_CLIENT.registerPacket( 0x3F, ScoreboardScore.class ); + TO_CLIENT.registerPacket( 0x40, ScoreboardDisplay.class ); + TO_CLIENT.registerPacket( 0x41, Team.class ); + TO_CLIENT.registerPacket( 0x42, PluginMessage.class ); + TO_CLIENT.registerPacket( 0x43, Kick.class ); + + + TO_SERVER.registerPacket( 0x00, KeepAlive.class ); + TO_SERVER.registerPacket( 0x14, TabComplete.class ); + TO_SERVER.registerPacket( 0x15, ClientSettings.class ); + TO_SERVER.registerPacket( 0x17, PluginMessage.class ); } }, // 1 - CLIENT_STATUS + STATUS { { } }, - // 1 - SERVER_STATUS + //2 + LOGIN { { - } - }, - // 2 - CLIENT_LOGIN - { - - { - registerPacket( 0x00, Kick.class ); - registerPacket( 0x01, EncryptionRequest.class ); - registerPacket( 0x02, LoginSuccess.class ); - } - }, - // 2 - SERVER_LOGIN - { - - { - registerPacket( 0x00, LoginRequest.class ); - registerPacket( 0x01, EncryptionResponse.class ); + TO_CLIENT.registerPacket( 0x00, Kick.class ); + TO_CLIENT.registerPacket( 0x01, EncryptionRequest.class ); + TO_CLIENT.registerPacket( 0x02, LoginSuccess.class ); + + TO_SERVER.registerPacket( 0x00, LoginRequest.class ); + TO_SERVER.registerPacket( 0x01, EncryptionResponse.class ); } }; /*========================================================================*/ @@ -101,57 +83,64 @@ public enum Protocol public static final int PROTOCOL_VERSION = 0x00; public static final String MINECRAFT_VERSION = "13w41a"; /*========================================================================*/ - private final TObjectIntMap> packetMap = new TObjectIntHashMap<>( MAX_PACKET_ID ); - private final Class[] packetClasses = new Class[ MAX_PACKET_ID ]; - private final Constructor[] packetConstructors = new Constructor[ MAX_PACKET_ID ]; + public final ProtocolDirection TO_SERVER = new ProtocolDirection(); + public final ProtocolDirection TO_CLIENT = new ProtocolDirection(); - public boolean hasPacket(int id) + public class ProtocolDirection { - return id < MAX_PACKET_ID && packetConstructors[id] != null; - } - public final DefinedPacket createPacket(int id) - { - if ( id > MAX_PACKET_ID ) + private final TObjectIntMap> packetMap = new TObjectIntHashMap<>( MAX_PACKET_ID ); + private final Class[] packetClasses = new Class[ MAX_PACKET_ID ]; + private final Constructor[] packetConstructors = new Constructor[ MAX_PACKET_ID ]; + + public boolean hasPacket(int id) { - throw new BadPacketException( "Packet with id " + id + " outside of range " ); - } - if ( packetConstructors[id] == null ) - { - throw new BadPacketException( "No packet with id " + id ); + return id < MAX_PACKET_ID && packetConstructors[id] != null; } - try + public final DefinedPacket createPacket(int id) { - return packetClasses[id].newInstance(); - } catch ( ReflectiveOperationException ex ) - { - throw new BadPacketException( "Could not construct packet with id " + id, ex ); + if ( id > MAX_PACKET_ID ) + { + throw new BadPacketException( "Packet with id " + id + " outside of range " ); + } + if ( packetConstructors[id] == null ) + { + throw new BadPacketException( "No packet with id " + id ); + } + + try + { + return packetClasses[id].newInstance(); + } catch ( ReflectiveOperationException ex ) + { + throw new BadPacketException( "Could not construct packet with id " + id, ex ); + } } - } - protected final void registerPacket(int id, Class packetClass) - { - try + protected final void registerPacket(int id, Class packetClass) { - packetConstructors[id] = packetClass.getDeclaredConstructor(); - } catch ( NoSuchMethodException ex ) - { - throw new BadPacketException( "No NoArgsConstructor for packet class " + packetClass ); + try + { + packetConstructors[id] = packetClass.getDeclaredConstructor(); + } catch ( NoSuchMethodException ex ) + { + throw new BadPacketException( "No NoArgsConstructor for packet class " + packetClass ); + } + packetClasses[id] = packetClass; + packetMap.put( packetClass, id ); } - packetClasses[id] = packetClass; - packetMap.put( packetClass, id ); - } - protected final void unregisterPacket(int id) - { - packetMap.remove( packetClasses[id] ); - packetClasses[id] = null; - packetConstructors[id] = null; - } + protected final void unregisterPacket(int id) + { + packetMap.remove( packetClasses[id] ); + packetClasses[id] = null; + packetConstructors[id] = null; + } - final int getId(Class packet) - { - return packetMap.get( packet ); + final int getId(Class packet) + { + return packetMap.get( packet ); + } } } diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/Varint21FrameDecoder.java b/protocol/src/main/java/net/md_5/bungee/protocol/Varint21FrameDecoder.java new file mode 100644 index 00000000..c6441445 --- /dev/null +++ b/protocol/src/main/java/net/md_5/bungee/protocol/Varint21FrameDecoder.java @@ -0,0 +1,47 @@ +package net.md_5.bungee.protocol; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.ByteToMessageDecoder; +import io.netty.handler.codec.CorruptedFrameException; + +import java.util.List; + +public class Varint21FrameDecoder extends ByteToMessageDecoder +{ + + @Override + protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception + { + in.markReaderIndex(); + + final byte[] buf = new byte[ 3 ]; + for ( int i = 0; i < buf.length; i++ ) + { + if ( !in.isReadable() ) + { + in.resetReaderIndex(); + return; + } + + buf[i] = in.readByte(); + if ( buf[i] >= 0 ) + { + int length = DefinedPacket.readVarInt( Unpooled.wrappedBuffer( buf ) ); + + if ( in.readableBytes() < length ) + { + in.resetReaderIndex(); + return; + } else + { + out.add( in.readBytes( length ) ); + return; + } + } + } + + throw new CorruptedFrameException( "length wider than 21-bit" ); + } +} diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/Varint21LengthFieldPrepender.java b/protocol/src/main/java/net/md_5/bungee/protocol/Varint21LengthFieldPrepender.java new file mode 100644 index 00000000..417adf38 --- /dev/null +++ b/protocol/src/main/java/net/md_5/bungee/protocol/Varint21LengthFieldPrepender.java @@ -0,0 +1,43 @@ +package net.md_5.bungee.protocol; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToByteEncoder; +import io.netty.channel.ChannelHandler; + +@ChannelHandler.Sharable +public class Varint21LengthFieldPrepender extends MessageToByteEncoder +{ + + @Override + protected void encode(ChannelHandlerContext ctx, ByteBuf msg, ByteBuf out) throws Exception + { + int bodyLen = msg.readableBytes(); + int headerLen = varintSize( bodyLen ); + out.ensureWritable( headerLen + bodyLen ); + + DefinedPacket.writeVarInt( bodyLen, out ); + out.writeBytes( msg, msg.readerIndex(), bodyLen ); + } + + private static int varintSize(int paramInt) + { + if ( ( paramInt & 0xFFFFFF80 ) == 0 ) + { + return 1; + } + if ( ( paramInt & 0xFFFFC000 ) == 0 ) + { + return 2; + } + if ( ( paramInt & 0xFFE00000 ) == 0 ) + { + return 3; + } + if ( ( paramInt & 0xF0000000 ) == 0 ) + { + return 4; + } + return 5; + } +} 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 475d7695..5fd22518 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 @@ -48,6 +48,7 @@ import net.md_5.bungee.protocol.packet.EncryptionRequest; import net.md_5.bungee.protocol.packet.Kick; import net.md_5.bungee.api.AbstractReconnectHandler; import net.md_5.bungee.api.event.PlayerHandshakeEvent; +import net.md_5.bungee.protocol.Protocol; import net.md_5.bungee.protocol.packet.LoginRequest; @RequiredArgsConstructor @@ -166,7 +167,7 @@ public class InitialHandler extends PacketHandler implements PendingConnection forced.ping( pingBack ); } else { - // pingBack.done( new ServerPing( bungee.getProtocolVersion(), bungee.getGameVersion(), motd, bungee.getOnlineCount(), listener.getMaxPlayers() ), null ); + // pingBack.done( new ServerPing( bungee.getProtocolVersion(), bungee.getGameVersion(), motd, bungee.getOnlineCount(), listener.getMaxPlayers() ), null ); } } /* @@ -201,6 +202,19 @@ public class InitialHandler extends PacketHandler implements PendingConnection disconnect( bungee.getTranslation( "outdated_client" ) ); } + switch ( handshake.getRequestedProtocol() ) + { + case 1: + // Ping + ch.setProtocol( Protocol.STATUS ); + break; + case 2: + ch.setProtocol( Protocol.LOGIN ); + // Login + break; + default: + throw new IllegalArgumentException( "Cannot request protocol " + handshake.getRequestedProtocol() ); + } } @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 b4e9fc1d..492c1d00 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 @@ -6,6 +6,8 @@ import io.netty.channel.Channel; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; import lombok.Getter; +import net.md_5.bungee.protocol.MinecraftCodec; +import net.md_5.bungee.protocol.Protocol; public class ChannelWrapper { @@ -19,6 +21,11 @@ public class ChannelWrapper this.ch = ctx.channel(); } + public void setProtocol(Protocol protocol) + { + ch.pipeline().get( MinecraftCodec.class ).setProtocol( protocol ); + } + public synchronized void write(Object packet) { if ( !closed ) 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 fc80d169..56f0ade4 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 @@ -12,6 +12,8 @@ import net.md_5.bungee.connection.CancelSendSignal; import net.md_5.bungee.connection.InitialHandler; import net.md_5.bungee.connection.PingHandler; import net.md_5.bungee.protocol.BadPacketException; +import net.md_5.bungee.protocol.MinecraftCodec; +import net.md_5.bungee.protocol.Protocol; /** * This class is a primitive wrapper for {@link PacketHandler} instances tied to 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 3c001a89..f1c2d1e7 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 @@ -4,7 +4,6 @@ import io.netty.channel.Channel; import io.netty.channel.ChannelException; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; -import io.netty.handler.codec.protobuf.ProtobufVarint32FrameDecoder; import io.netty.handler.timeout.ReadTimeoutHandler; import io.netty.util.AttributeKey; import java.net.InetSocketAddress; @@ -18,6 +17,8 @@ import net.md_5.bungee.api.ProxyServer; import net.md_5.bungee.api.config.ListenerInfo; import net.md_5.bungee.protocol.MinecraftCodec; import net.md_5.bungee.protocol.Protocol; +import net.md_5.bungee.protocol.Varint21FrameDecoder; +import net.md_5.bungee.protocol.Varint21LengthFieldPrepender; public class PipelineUtils { @@ -38,6 +39,7 @@ public class PipelineUtils } BASE.initChannel( ch ); + ch.pipeline().addAfter( FRAME_DECODER, PACKET_CODEC, new MinecraftCodec( Protocol.HANDSHAKE, true ) ); ch.pipeline().get( HandlerBoss.class ).setHandler( new InitialHandler( ProxyServer.getInstance(), ch.attr( LISTENER ).get() ) ); } }; @@ -47,12 +49,12 @@ public class PipelineUtils protected void initChannel(Channel ch) throws Exception { BASE.initChannel( ch ); + ch.pipeline().addAfter( FRAME_DECODER, PACKET_CODEC, new MinecraftCodec( Protocol.LOGIN, false) ); ch.pipeline().get( HandlerBoss.class ).setHandler( new ServerConnector( ProxyServer.getInstance(), ch.attr( USER ).get(), ch.attr( TARGET ).get() ) ); } }; public static final Base BASE = new Base(); - private static final ProtobufVarint32FrameDecoder frameDecoder = new ProtobufVarint32FrameDecoder(); - private static final ProtobufVarint32FrameDecoder framePrepender = new ProtobufVarint32FrameDecoder(); + private static final Varint21LengthFieldPrepender framePrepender = new Varint21LengthFieldPrepender(); public static String TIMEOUT_HANDLER = "timeout"; public static String PACKET_CODEC = "packet-codec"; public static String BOSS_HANDLER = "inbound-boss"; @@ -76,8 +78,7 @@ public class PipelineUtils } ch.pipeline().addLast( TIMEOUT_HANDLER, new ReadTimeoutHandler( BungeeCord.getInstance().config.getTimeout(), TimeUnit.MILLISECONDS ) ); - ch.pipeline().addLast( FRAME_DECODER, frameDecoder ); - ch.pipeline().addLast( PACKET_CODEC, new MinecraftCodec( Protocol.SERVER_HANDSHAKE ) ); + ch.pipeline().addLast( FRAME_DECODER, new Varint21FrameDecoder() ); ch.pipeline().addLast( FRAME_PREPENDER, framePrepender ); ch.pipeline().addLast( BOSS_HANDLER, new HandlerBoss() ); }