diff --git a/api/src/main/java/net/md_5/bungee/api/connection/Connection.java b/api/src/main/java/net/md_5/bungee/api/connection/Connection.java index 3962b9a9..ba72a96a 100644 --- a/api/src/main/java/net/md_5/bungee/api/connection/Connection.java +++ b/api/src/main/java/net/md_5/bungee/api/connection/Connection.java @@ -16,4 +16,14 @@ public interface Connection * @return the remote address */ public InetSocketAddress getAddress(); + + /** + * Disconnects this end of the connection for the specified reason. If this + * is an {@link ProxiedPlayer} the respective server connection will be + * closed too. + * + * @param reason the reason shown to the player / sent to the server on + * disconnect + */ + public void disconnect(String reason); } diff --git a/api/src/main/java/net/md_5/bungee/api/connection/PendingConnection.java b/api/src/main/java/net/md_5/bungee/api/connection/PendingConnection.java index 4c952f37..0334afc6 100644 --- a/api/src/main/java/net/md_5/bungee/api/connection/PendingConnection.java +++ b/api/src/main/java/net/md_5/bungee/api/connection/PendingConnection.java @@ -30,14 +30,6 @@ public interface PendingConnection extends Connection */ public InetSocketAddress getVirtualHost(); - /** - * Completely kick this user from the proxy and all of its child - * connections. - * - * @param reason the disconnect reason displayed to the player - */ - public void disconnect(String reason); - /** * Get the listener that accepted this connection. * diff --git a/api/src/main/java/net/md_5/bungee/api/connection/ProxiedPlayer.java b/api/src/main/java/net/md_5/bungee/api/connection/ProxiedPlayer.java index 0cc70ab0..b0e45598 100644 --- a/api/src/main/java/net/md_5/bungee/api/connection/ProxiedPlayer.java +++ b/api/src/main/java/net/md_5/bungee/api/connection/ProxiedPlayer.java @@ -48,13 +48,6 @@ public interface ProxiedPlayer extends Connection, CommandSender */ public int getPing(); - /** - * Disconnect (remove) this player from the proxy with the specified reason. - * - * @param reason the reason displayed to the player - */ - public void disconnect(String reason); - /** * Send a plugin message to this player. * diff --git a/api/src/main/java/net/md_5/bungee/api/connection/Server.java b/api/src/main/java/net/md_5/bungee/api/connection/Server.java index f94e5a96..06eb6d1a 100644 --- a/api/src/main/java/net/md_5/bungee/api/connection/Server.java +++ b/api/src/main/java/net/md_5/bungee/api/connection/Server.java @@ -1,7 +1,5 @@ package net.md_5.bungee.api.connection; -import net.md_5.bungee.api.Callback; -import net.md_5.bungee.api.ServerPing; import net.md_5.bungee.api.config.ServerInfo; /** @@ -24,14 +22,4 @@ public interface Server extends Connection * @param data the data to send */ public abstract void sendData(String channel, byte[] data); - - /** - * Asynchronously gets the current player count on this server. - * - * @param callback the callback to call when the count has been retrieved. - * @deprecated use the corresponding method in {@link ServerInfo} for - * clarity - */ - @Deprecated - public abstract void ping(Callback callback); } diff --git a/pom.xml b/pom.xml index 8ff3863a..71af32c1 100644 --- a/pom.xml +++ b/pom.xml @@ -92,7 +92,7 @@ org.apache.maven.plugins maven-compiler-plugin - 3.0 + 2.5.1 1.7 1.7 diff --git a/protocol/pom.xml b/protocol/pom.xml index 25c0e287..89f5f812 100644 --- a/protocol/pom.xml +++ b/protocol/pom.xml @@ -17,4 +17,14 @@ BungeeCord-Protocol Minimal implementation of the Minecraft protocol for use in BungeeCord + + + + io.netty + + netty-all + 4.0.0.Beta3-SNAPSHOT + provided + + diff --git a/protocol/src/main/java/net/md_5/mendax/PacketDefinitions.java b/protocol/src/main/java/net/md_5/bungee/protocol/PacketDefinitions.java similarity index 96% rename from protocol/src/main/java/net/md_5/mendax/PacketDefinitions.java rename to protocol/src/main/java/net/md_5/bungee/protocol/PacketDefinitions.java index 394425bd..48f01260 100644 --- a/protocol/src/main/java/net/md_5/mendax/PacketDefinitions.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/PacketDefinitions.java @@ -1,14 +1,13 @@ -package net.md_5.mendax; +package net.md_5.bungee.protocol; -import static net.md_5.mendax.PacketDefinitions.OpCode.*; +import static net.md_5.bungee.protocol.PacketDefinitions.OpCode.*; public class PacketDefinitions { - private static final int MAX_PACKET = 256; - public static final OpCode[][] opCodes = new OpCode[ MAX_PACKET * 2 ][]; + public static final OpCode[][] opCodes = new OpCode[ 512 ][]; public static final int VANILLA_PROTOCOL = 0; - public static final int FORGE_PROTOCOL = MAX_PACKET; + public static final int FORGE_PROTOCOL = 256; public enum OpCode { @@ -332,7 +331,8 @@ public class PacketDefinitions }; opCodes[0xFE] = new OpCode[] { - }; // Should be byte, screw you too bitchy server admins! + BYTE + }; opCodes[0xFF] = new OpCode[] { STRING diff --git a/protocol/src/main/java/net/md_5/mendax/datainput/BulkChunk.java b/protocol/src/main/java/net/md_5/bungee/protocol/netty/BulkChunk.java similarity index 54% rename from protocol/src/main/java/net/md_5/mendax/datainput/BulkChunk.java rename to protocol/src/main/java/net/md_5/bungee/protocol/netty/BulkChunk.java index 5f1b31b0..54fee12d 100644 --- a/protocol/src/main/java/net/md_5/mendax/datainput/BulkChunk.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/netty/BulkChunk.java @@ -1,17 +1,17 @@ -package net.md_5.mendax.datainput; +package net.md_5.bungee.protocol.netty; -import java.io.DataInput; +import io.netty.buffer.ByteBuf; import java.io.IOException; public class BulkChunk extends Instruction { @Override - void read(DataInput in, byte[] buffer) throws IOException + void read(ByteBuf in) throws IOException { short count = in.readShort(); int size = in.readInt(); in.readBoolean(); - skip( in, buffer, size + count * 12 ); + in.skipBytes( size + count * 12 ); } } diff --git a/protocol/src/main/java/net/md_5/mendax/datainput/ByteHeader.java b/protocol/src/main/java/net/md_5/bungee/protocol/netty/ByteHeader.java similarity index 65% rename from protocol/src/main/java/net/md_5/mendax/datainput/ByteHeader.java rename to protocol/src/main/java/net/md_5/bungee/protocol/netty/ByteHeader.java index a4bc11da..24fd1ac4 100644 --- a/protocol/src/main/java/net/md_5/mendax/datainput/ByteHeader.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/netty/ByteHeader.java @@ -1,6 +1,6 @@ -package net.md_5.mendax.datainput; +package net.md_5.bungee.protocol.netty; -import java.io.DataInput; +import io.netty.buffer.ByteBuf; import java.io.IOException; class ByteHeader extends Instruction @@ -14,12 +14,12 @@ class ByteHeader extends Instruction } @Override - void read(DataInput in, byte[] buffer) throws IOException + void read(ByteBuf in) throws IOException { byte size = in.readByte(); for ( byte b = 0; b < size; b++ ) { - child.read( in, buffer ); + child.read( in ); } } } diff --git a/protocol/src/main/java/net/md_5/mendax/datainput/Instruction.java b/protocol/src/main/java/net/md_5/bungee/protocol/netty/Instruction.java similarity index 83% rename from protocol/src/main/java/net/md_5/mendax/datainput/Instruction.java rename to protocol/src/main/java/net/md_5/bungee/protocol/netty/Instruction.java index 5736d3b8..f6cd82bc 100644 --- a/protocol/src/main/java/net/md_5/mendax/datainput/Instruction.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/netty/Instruction.java @@ -1,6 +1,6 @@ -package net.md_5.mendax.datainput; +package net.md_5.bungee.protocol.netty; -import java.io.DataInput; +import io.netty.buffer.ByteBuf; import java.io.IOException; abstract class Instruction @@ -30,10 +30,5 @@ abstract class Instruction // Custom instructions static final Instruction STRING_ARRAY = new ShortHeader( STRING ); - abstract void read(DataInput in, byte[] buffer) throws IOException; - - final void skip(DataInput in, byte[] buffer, int len) throws IOException - { - in.readFully( buffer, 0, len ); - } + abstract void read(ByteBuf in) throws IOException; } diff --git a/protocol/src/main/java/net/md_5/mendax/datainput/IntHeader.java b/protocol/src/main/java/net/md_5/bungee/protocol/netty/IntHeader.java similarity index 65% rename from protocol/src/main/java/net/md_5/mendax/datainput/IntHeader.java rename to protocol/src/main/java/net/md_5/bungee/protocol/netty/IntHeader.java index 31a5c18d..2f1a011b 100644 --- a/protocol/src/main/java/net/md_5/mendax/datainput/IntHeader.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/netty/IntHeader.java @@ -1,6 +1,6 @@ -package net.md_5.mendax.datainput; +package net.md_5.bungee.protocol.netty; -import java.io.DataInput; +import io.netty.buffer.ByteBuf; import java.io.IOException; class IntHeader extends Instruction @@ -14,12 +14,12 @@ class IntHeader extends Instruction } @Override - void read(DataInput in, byte[] buffer) throws IOException + void read(ByteBuf in) throws IOException { int size = in.readInt(); for ( int i = 0; i < size; i++ ) { - child.read( in, buffer ); + child.read( in ); } } } diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/netty/Item.java b/protocol/src/main/java/net/md_5/bungee/protocol/netty/Item.java new file mode 100644 index 00000000..529be2f3 --- /dev/null +++ b/protocol/src/main/java/net/md_5/bungee/protocol/netty/Item.java @@ -0,0 +1,19 @@ +package net.md_5.bungee.protocol.netty; + +import io.netty.buffer.ByteBuf; +import java.io.IOException; + +class Item extends Instruction +{ + + @Override + void read(ByteBuf in) throws IOException + { + short type = in.readShort(); + if ( type >= 0 ) + { + in.skipBytes( 3 ); + SHORT_BYTE.read( in ); + } + } +} diff --git a/protocol/src/main/java/net/md_5/mendax/datainput/Jump.java b/protocol/src/main/java/net/md_5/bungee/protocol/netty/Jump.java similarity index 62% rename from protocol/src/main/java/net/md_5/mendax/datainput/Jump.java rename to protocol/src/main/java/net/md_5/bungee/protocol/netty/Jump.java index 5bf82fc7..aca0d763 100644 --- a/protocol/src/main/java/net/md_5/mendax/datainput/Jump.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/netty/Jump.java @@ -1,6 +1,6 @@ -package net.md_5.mendax.datainput; +package net.md_5.bungee.protocol.netty; -import java.io.DataInput; +import io.netty.buffer.ByteBuf; import java.io.IOException; class Jump extends Instruction @@ -18,8 +18,8 @@ class Jump extends Instruction } @Override - void read(DataInput in, byte[] buffer) throws IOException + void read(ByteBuf in) throws IOException { - skip( in, buffer, len ); + in.skipBytes( len ); } } diff --git a/protocol/src/main/java/net/md_5/mendax/datainput/MetaData.java b/protocol/src/main/java/net/md_5/bungee/protocol/netty/MetaData.java similarity index 63% rename from protocol/src/main/java/net/md_5/mendax/datainput/MetaData.java rename to protocol/src/main/java/net/md_5/bungee/protocol/netty/MetaData.java index 6dc04584..730f3686 100644 --- a/protocol/src/main/java/net/md_5/mendax/datainput/MetaData.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/netty/MetaData.java @@ -1,13 +1,13 @@ -package net.md_5.mendax.datainput; +package net.md_5.bungee.protocol.netty; -import java.io.DataInput; +import io.netty.buffer.ByteBuf; import java.io.IOException; class MetaData extends Instruction { @Override - void read(DataInput in, byte[] buffer) throws IOException + void read(ByteBuf in) throws IOException { int x = in.readUnsignedByte(); while ( x != 127 ) @@ -16,25 +16,25 @@ class MetaData extends Instruction switch ( type ) { case 0: - BYTE.read( in, buffer ); + BYTE.read( in ); break; case 1: - SHORT.read( in, buffer ); + SHORT.read( in ); break; case 2: - INT.read( in, buffer ); + INT.read( in ); break; case 3: - FLOAT.read( in, buffer ); + FLOAT.read( in ); break; case 4: - STRING.read( in, buffer ); + STRING.read( in ); break; case 5: - ITEM.read( in, buffer ); + ITEM.read( in ); break; case 6: - skip( in, buffer, 12 ); // int, int, int + in.skipBytes( 12 ); // int, int, int break; default: throw new IllegalArgumentException( "Unknown metadata type " + type ); diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/netty/OptionalMotion.java b/protocol/src/main/java/net/md_5/bungee/protocol/netty/OptionalMotion.java new file mode 100644 index 00000000..4e618360 --- /dev/null +++ b/protocol/src/main/java/net/md_5/bungee/protocol/netty/OptionalMotion.java @@ -0,0 +1,18 @@ +package net.md_5.bungee.protocol.netty; + +import io.netty.buffer.ByteBuf; +import java.io.IOException; + +class OptionalMotion extends Instruction +{ + + @Override + void read(ByteBuf in) throws IOException + { + int data = in.readInt(); + if ( data > 0 ) + { + in.skipBytes( 6 ); + } + } +} diff --git a/protocol/src/main/java/net/md_5/mendax/datainput/DataInputPacketReader.java b/protocol/src/main/java/net/md_5/bungee/protocol/netty/PacketReader.java similarity index 80% rename from protocol/src/main/java/net/md_5/mendax/datainput/DataInputPacketReader.java rename to protocol/src/main/java/net/md_5/bungee/protocol/netty/PacketReader.java index c697b9a0..0adfb864 100644 --- a/protocol/src/main/java/net/md_5/mendax/datainput/DataInputPacketReader.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/netty/PacketReader.java @@ -1,16 +1,16 @@ -package net.md_5.mendax.datainput; +package net.md_5.bungee.protocol.netty; -import java.io.DataInput; +import io.netty.buffer.ByteBuf; import java.io.IOException; import java.util.ArrayList; import java.util.List; -import net.md_5.mendax.PacketDefinitions; -import net.md_5.mendax.PacketDefinitions.OpCode; +import net.md_5.bungee.protocol.PacketDefinitions; +import net.md_5.bungee.protocol.PacketDefinitions.OpCode; -public class DataInputPacketReader +public class PacketReader { - private static final Instruction[][] instructions = new Instruction[ 256 ][]; + private static final Instruction[][] instructions = new Instruction[ PacketDefinitions.opCodes.length ][]; static { @@ -59,7 +59,7 @@ public class DataInputPacketReader } } - private static void readPacket(int packetId, DataInput in, byte[] buffer, int protocol) throws IOException + private static void readPacket(int packetId, ByteBuf in, int protocol) throws IOException { Instruction[] packetDef = null; if ( packetId + protocol < instructions.length ) @@ -74,20 +74,20 @@ public class DataInputPacketReader throw new IOException( "Unknown packet id " + packetId ); } else { - readPacket( packetId, in, buffer, PacketDefinitions.VANILLA_PROTOCOL ); + readPacket( packetId, in, PacketDefinitions.VANILLA_PROTOCOL ); return; } } for ( Instruction instruction : packetDef ) { - instruction.read( in, buffer ); + instruction.read( in ); } } - public static void readPacket(DataInput in, byte[] buffer, int protocol) throws IOException + public static void readPacket(ByteBuf in, int protocol) throws IOException { int packetId = in.readUnsignedByte(); - readPacket( packetId, in, buffer, protocol ); + readPacket( packetId, in, protocol ); } } diff --git a/protocol/src/main/java/net/md_5/mendax/datainput/ShortHeader.java b/protocol/src/main/java/net/md_5/bungee/protocol/netty/ShortHeader.java similarity index 66% rename from protocol/src/main/java/net/md_5/mendax/datainput/ShortHeader.java rename to protocol/src/main/java/net/md_5/bungee/protocol/netty/ShortHeader.java index b274af61..ece0ae3c 100644 --- a/protocol/src/main/java/net/md_5/mendax/datainput/ShortHeader.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/netty/ShortHeader.java @@ -1,6 +1,6 @@ -package net.md_5.mendax.datainput; +package net.md_5.bungee.protocol.netty; -import java.io.DataInput; +import io.netty.buffer.ByteBuf; import java.io.IOException; class ShortHeader extends Instruction @@ -14,12 +14,12 @@ class ShortHeader extends Instruction } @Override - void read(DataInput in, byte[] buffer) throws IOException + void read(ByteBuf in) throws IOException { short size = in.readShort(); for ( short s = 0; s < size; s++ ) { - child.read( in, buffer ); + child.read( in ); } } } diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/netty/Team.java b/protocol/src/main/java/net/md_5/bungee/protocol/netty/Team.java new file mode 100644 index 00000000..935aed6f --- /dev/null +++ b/protocol/src/main/java/net/md_5/bungee/protocol/netty/Team.java @@ -0,0 +1,26 @@ +package net.md_5.bungee.protocol.netty; + +import io.netty.buffer.ByteBuf; +import java.io.IOException; + +class Team extends Instruction +{ + + @Override + void read(ByteBuf in) throws IOException + { + STRING.read( in ); + byte mode = in.readByte(); + if ( mode == 0 || mode == 2 ) + { + STRING.read( in ); + STRING.read( in ); + STRING.read( in ); + BYTE.read( in ); + } + if ( mode == 0 || mode == 3 || mode == 4 ) + { + STRING_ARRAY.read( in ); + } + } +} diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/netty/UnsignedShortByte.java b/protocol/src/main/java/net/md_5/bungee/protocol/netty/UnsignedShortByte.java new file mode 100644 index 00000000..1dea256b --- /dev/null +++ b/protocol/src/main/java/net/md_5/bungee/protocol/netty/UnsignedShortByte.java @@ -0,0 +1,15 @@ +package net.md_5.bungee.protocol.netty; + +import io.netty.buffer.ByteBuf; +import java.io.IOException; + +class UnsignedShortByte extends Instruction +{ + + @Override + void read(ByteBuf in) throws IOException + { + int size = in.readUnsignedShort(); + in.skipBytes( size ); + } +} diff --git a/protocol/src/main/java/net/md_5/mendax/datainput/Item.java b/protocol/src/main/java/net/md_5/mendax/datainput/Item.java deleted file mode 100644 index 411c931b..00000000 --- a/protocol/src/main/java/net/md_5/mendax/datainput/Item.java +++ /dev/null @@ -1,19 +0,0 @@ -package net.md_5.mendax.datainput; - -import java.io.DataInput; -import java.io.IOException; - -class Item extends Instruction -{ - - @Override - void read(DataInput in, byte[] buffer) throws IOException - { - short type = in.readShort(); - if ( type >= 0 ) - { - skip( in, buffer, 3 ); - SHORT_BYTE.read( in, buffer ); - } - } -} diff --git a/protocol/src/main/java/net/md_5/mendax/datainput/OptionalMotion.java b/protocol/src/main/java/net/md_5/mendax/datainput/OptionalMotion.java deleted file mode 100644 index 55c36f65..00000000 --- a/protocol/src/main/java/net/md_5/mendax/datainput/OptionalMotion.java +++ /dev/null @@ -1,18 +0,0 @@ -package net.md_5.mendax.datainput; - -import java.io.DataInput; -import java.io.IOException; - -public class OptionalMotion extends Instruction -{ - - @Override - void read(DataInput in, byte[] buffer) throws IOException - { - int data = in.readInt(); - if ( data > 0 ) - { - skip( in, buffer, 6 ); - } - } -} diff --git a/protocol/src/main/java/net/md_5/mendax/datainput/Team.java b/protocol/src/main/java/net/md_5/mendax/datainput/Team.java deleted file mode 100644 index d3cc31f4..00000000 --- a/protocol/src/main/java/net/md_5/mendax/datainput/Team.java +++ /dev/null @@ -1,26 +0,0 @@ -package net.md_5.mendax.datainput; - -import java.io.DataInput; -import java.io.IOException; - -class Team extends Instruction -{ - - @Override - void read(DataInput in, byte[] buffer) throws IOException - { - STRING.read( in, buffer ); - byte mode = in.readByte(); - if ( mode == 0 || mode == 2 ) - { - STRING.read( in, buffer ); - STRING.read( in, buffer ); - STRING.read( in, buffer ); - BYTE.read( in, buffer ); - } - if ( mode == 0 || mode == 3 || mode == 4 ) - { - STRING_ARRAY.read( in, buffer ); - } - } -} diff --git a/protocol/src/main/java/net/md_5/mendax/datainput/UnsignedShortByte.java b/protocol/src/main/java/net/md_5/mendax/datainput/UnsignedShortByte.java deleted file mode 100644 index 4b1db5a2..00000000 --- a/protocol/src/main/java/net/md_5/mendax/datainput/UnsignedShortByte.java +++ /dev/null @@ -1,15 +0,0 @@ -package net.md_5.mendax.datainput; - -import java.io.DataInput; -import java.io.IOException; - -public class UnsignedShortByte extends Instruction -{ - - @Override - void read(DataInput in, byte[] buffer) throws IOException - { - int size = in.readUnsignedShort(); - skip( in, buffer, size ); - } -} diff --git a/proxy/pom.xml b/proxy/pom.xml index 29f252a0..145835cb 100644 --- a/proxy/pom.xml +++ b/proxy/pom.xml @@ -19,6 +19,11 @@ Proxy component of the Elastic Portal Suite + + io.netty + netty-all + 4.0.0.Beta3-SNAPSHOT + net.md-5 bungeecord-protocol @@ -29,6 +34,11 @@ bungeecord-api ${project.version} + + net.sf.trove4j + trove4j + 3.0.3 + mysql mysql-connector-java 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 326145cb..e4fe73cf 100644 --- a/proxy/src/main/java/net/md_5/bungee/BungeeCord.java +++ b/proxy/src/main/java/net/md_5/bungee/BungeeCord.java @@ -1,23 +1,26 @@ package net.md_5.bungee; +import com.google.common.util.concurrent.ThreadFactoryBuilder; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.Channel; +import io.netty.channel.ChannelException; +import io.netty.channel.MultithreadEventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.nio.NioServerSocketChannel; import net.md_5.bungee.config.Configuration; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStreamReader; import java.net.InetSocketAddress; -import java.net.Socket; import java.util.Calendar; import java.util.Collection; import java.util.Collections; import java.util.HashSet; -import java.util.List; import java.util.Map; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; @@ -37,6 +40,7 @@ import net.md_5.bungee.api.plugin.Plugin; import net.md_5.bungee.api.plugin.PluginManager; import net.md_5.bungee.command.*; import net.md_5.bungee.config.YamlConfig; +import net.md_5.bungee.netty.PipelineUtils; import net.md_5.bungee.packet.DefinedPacket; import net.md_5.bungee.packet.PacketFAPluginMessage; @@ -65,7 +69,7 @@ public class BungeeCord extends ProxyServer /** * Thread pool. */ - public final ExecutorService threadPool = Executors.newCachedThreadPool(); + public final MultithreadEventLoopGroup eventLoops = new NioEventLoopGroup( 8, new ThreadFactoryBuilder().setNameFormat( "Netty IO Thread - %1$d" ).build() ); /** * locations.yml save thread. */ @@ -73,7 +77,7 @@ public class BungeeCord extends ProxyServer /** * Server socket listener. */ - private Collection listeners = new HashSet<>(); + private Collection listeners = new HashSet<>(); /** * Fully qualified connections. */ @@ -161,6 +165,7 @@ public class BungeeCord extends ProxyServer * * @throws IOException */ + @Override public void start() throws IOException { File plugins = new File( "plugins" ); @@ -193,30 +198,28 @@ public class BungeeCord extends ProxyServer { for ( ListenerInfo info : config.getListeners() ) { - try - { - ListenThread listener = new ListenThread( info ); - listener.start(); - listeners.add( listener ); - $().info( "Listening on " + info.getHost() ); - } catch ( IOException ex ) - { - $().log( Level.SEVERE, "Could not start listener " + info, ex ); - } + Channel server = new ServerBootstrap() + .channel( NioServerSocketChannel.class ) + .childAttr( PipelineUtils.LISTENER, info ) + .childHandler( PipelineUtils.SERVER_CHILD ) + .group( eventLoops ) + .localAddress( info.getHost() ) + .bind().channel(); + listeners.add( server ); + + $().info( "Listening on " + info.getHost() ); } } public void stopListeners() { - for ( ListenThread listener : listeners ) + for ( Channel listener : listeners ) { - $().log( Level.INFO, "Closing listen thread {0}", listener.socket ); + $().log( Level.INFO, "Closing listener {0}", listener ); try { - listener.interrupt(); - listener.socket.close(); - listener.join(); - } catch ( InterruptedException | IOException ex ) + listener.close().syncUninterruptibly(); + } catch ( ChannelException ex ) { $().severe( "Could not close listen thread" ); } @@ -231,7 +234,6 @@ public class BungeeCord extends ProxyServer stopListeners(); $().info( "Closing pending connections" ); - threadPool.shutdown(); $().info( "Disconnecting " + connections.size() + " connections" ); for ( UserConnection user : connections.values() ) @@ -239,6 +241,9 @@ public class BungeeCord extends ProxyServer user.disconnect( "Proxy restarting, brb." ); } + $().info( "Closing IO threads" ); + eventLoops.shutdown(); + $().info( "Saving reconnect locations" ); reconnectHandler.save(); saveThread.cancel(); @@ -253,20 +258,6 @@ public class BungeeCord extends ProxyServer System.exit( 0 ); } - /** - * Miscellaneous method to set options on a socket based on those in the - * configuration. - * - * @param socket to set the options on - * @throws IOException when the underlying set methods thrown an exception - */ - public void setSocketOptions(Socket socket) throws IOException - { - socket.setSoTimeout( config.getTimeout() ); - socket.setTrafficClass( 0x18 ); - socket.setTcpNoDelay( true ); - } - /** * Broadcasts a packet to all clients that is connected to this instance. * @@ -276,7 +267,7 @@ public class BungeeCord extends ProxyServer { for ( UserConnection con : connections.values() ) { - con.packetQueue.add( packet ); + con.sendPacket( packet ); } } diff --git a/proxy/src/main/java/net/md_5/bungee/BungeeServerInfo.java b/proxy/src/main/java/net/md_5/bungee/BungeeServerInfo.java index a17e08f7..8f5ca6bb 100644 --- a/proxy/src/main/java/net/md_5/bungee/BungeeServerInfo.java +++ b/proxy/src/main/java/net/md_5/bungee/BungeeServerInfo.java @@ -1,8 +1,11 @@ package net.md_5.bungee; -import java.io.DataOutputStream; +import io.netty.bootstrap.Bootstrap; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelFutureListener; +import io.netty.channel.ChannelOption; +import io.netty.channel.socket.nio.NioSocketChannel; import java.net.InetSocketAddress; -import java.net.Socket; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; import lombok.Getter; @@ -11,11 +14,11 @@ import net.md_5.bungee.api.ProxyServer; import net.md_5.bungee.api.ServerPing; import net.md_5.bungee.api.config.ServerInfo; import net.md_5.bungee.api.connection.Server; +import net.md_5.bungee.connection.PingHandler; +import net.md_5.bungee.netty.HandlerBoss; +import net.md_5.bungee.netty.PipelineUtils; import net.md_5.bungee.packet.DefinedPacket; import net.md_5.bungee.packet.PacketFAPluginMessage; -import net.md_5.bungee.packet.PacketFFKick; -import net.md_5.bungee.packet.PacketStream; -import net.md_5.mendax.PacketDefinitions; public class BungeeServerInfo extends ServerInfo { @@ -44,31 +47,24 @@ public class BungeeServerInfo extends ServerInfo @Override public void ping(final Callback callback) { - new Thread() + new Bootstrap() + .channel( NioSocketChannel.class ) + .group( BungeeCord.getInstance().eventLoops ) + .handler( PipelineUtils.BASE ) + .option( ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000 ) // TODO: Configurable + .remoteAddress( getAddress() ) + .connect() + .addListener( new ChannelFutureListener() { @Override - public void run() + public void operationComplete(ChannelFuture future) throws Exception { - try ( Socket socket = new Socket(); ) + if ( !future.isSuccess() ) { - socket.connect( getAddress() ); - - DataOutputStream out = new DataOutputStream( socket.getOutputStream() ); - out.write( 0xFE ); - out.write( 0x01 ); - - PacketStream in = new PacketStream( socket.getInputStream(), PacketDefinitions.VANILLA_PROTOCOL ); - PacketFFKick response = new PacketFFKick( in.readPacket() ); - - String[] split = response.message.split( "\00" ); - - ServerPing ping = new ServerPing( Byte.parseByte( split[1] ), split[2], split[3], Integer.parseInt( split[4] ), Integer.parseInt( split[5] ) ); - callback.done( ping, null ); - } catch ( Throwable t ) - { - callback.done( null, t ); + callback.done( null, future.cause() ); } } - }.start(); + } ) + .channel().pipeline().get( HandlerBoss.class ).setHandler( new PingHandler( this, callback ) ); } } diff --git a/proxy/src/main/java/net/md_5/bungee/EncryptionUtil.java b/proxy/src/main/java/net/md_5/bungee/EncryptionUtil.java index 26bbdb21..28729433 100644 --- a/proxy/src/main/java/net/md_5/bungee/EncryptionUtil.java +++ b/proxy/src/main/java/net/md_5/bungee/EncryptionUtil.java @@ -1,17 +1,10 @@ package net.md_5.bungee; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.math.BigInteger; -import java.net.URL; -import java.net.URLEncoder; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.Key; import java.security.KeyPair; import java.security.KeyPairGenerator; -import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Arrays; import java.util.Random; @@ -32,7 +25,7 @@ public class EncryptionUtil { private static final Random random = new Random(); - private static KeyPair keys; + public static KeyPair keys; public static PacketFDEncryptionRequest encryptRequest() throws NoSuchAlgorithmException { @@ -66,30 +59,6 @@ public class EncryptionUtil return new SecretKeySpec( secret, "AES" ); } - public static boolean isAuthenticated(String username, String connectionHash, SecretKey shared) throws NoSuchAlgorithmException, IOException - { - String encName = URLEncoder.encode( username, "UTF-8" ); - - MessageDigest sha = MessageDigest.getInstance( "SHA-1" ); - for ( byte[] bit : new byte[][] - { - connectionHash.getBytes( "ISO_8859_1" ), shared.getEncoded(), keys.getPublic().getEncoded() - } ) - { - sha.update( bit ); - } - - String encodedHash = URLEncoder.encode( new BigInteger( sha.digest() ).toString( 16 ), "UTF-8" ); - String authURL = "http://session.minecraft.net/game/checkserver.jsp?user=" + encName + "&serverId=" + encodedHash; - String reply; - try ( BufferedReader in = new BufferedReader( new InputStreamReader( new URL( authURL ).openStream() ) ) ) - { - reply = in.readLine(); - } - - return "YES".equals( reply ); - } - public static Cipher getCipher(int opMode, Key shared) throws InvalidAlgorithmParameterException, InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException { Cipher cip = Cipher.getInstance( "AES/CFB8/NoPadding" ); diff --git a/proxy/src/main/java/net/md_5/bungee/EntityMap.java b/proxy/src/main/java/net/md_5/bungee/EntityMap.java index ce9ad4cb..b3637c30 100644 --- a/proxy/src/main/java/net/md_5/bungee/EntityMap.java +++ b/proxy/src/main/java/net/md_5/bungee/EntityMap.java @@ -1,5 +1,7 @@ package net.md_5.bungee; +import io.netty.buffer.ByteBuf; + /** * Class to rewrite integers within packets. */ @@ -113,20 +115,20 @@ public class EntityMap }; } - public static void rewrite(byte[] packet, int oldId, int newId) + public static void rewrite(ByteBuf packet, int oldId, int newId) { - int packetId = Util.getId( packet ); + int packetId = packet.getUnsignedByte( 0 ); if ( packetId == 0x1D ) { // bulk entity - for ( int pos = 2; pos < packet.length; pos += 4 ) + for ( int pos = 2; pos < packet.writerIndex(); pos += 4 ) { - int readId = readInt( packet, pos ); + int readId = packet.getInt( pos ); if ( readId == oldId ) { - setInt( packet, pos, newId ); + packet.setInt( pos, newId ); } else if ( readId == newId ) { - setInt( packet, pos, oldId ); + packet.setInt( pos, oldId ); } } } else @@ -136,29 +138,16 @@ public class EntityMap { for ( int pos : idArray ) { - int readId = readInt( packet, pos ); + int readId = packet.getInt( pos ); if ( readId == oldId ) { - setInt( packet, pos, newId ); + packet.setInt( pos, newId ); } else if ( readId == newId ) { - setInt( packet, pos, oldId ); + packet.setInt( pos, oldId ); } } } } } - - private static void setInt(byte[] buf, int pos, int i) - { - buf[pos] = (byte) ( i >> 24 ); - buf[pos + 1] = (byte) ( i >> 16 ); - buf[pos + 2] = (byte) ( i >> 8 ); - buf[pos + 3] = (byte) i; - } - - private static int readInt(byte[] buf, int pos) - { - return ( ( ( buf[pos] & 0xFF ) << 24 ) | ( ( buf[pos + 1] & 0xFF ) << 16 ) | ( ( buf[pos + 2] & 0xFF ) << 8 ) | buf[pos + 3] & 0xFF ); - } } diff --git a/proxy/src/main/java/net/md_5/bungee/GenericConnection.java b/proxy/src/main/java/net/md_5/bungee/GenericConnection.java deleted file mode 100644 index 876add99..00000000 --- a/proxy/src/main/java/net/md_5/bungee/GenericConnection.java +++ /dev/null @@ -1,61 +0,0 @@ -package net.md_5.bungee; - -import java.io.IOException; -import java.io.OutputStream; -import java.net.Socket; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import static net.md_5.bungee.Logger.$; -import net.md_5.bungee.packet.PacketFFKick; -import net.md_5.bungee.packet.PacketStream; - -/** - * Class to represent a Minecraft connection. - */ -@EqualsAndHashCode -@RequiredArgsConstructor -public class GenericConnection -{ - - protected final Socket socket; - protected final PacketStream stream; - @Getter - public String name; - @Getter - public String displayName; - - /** - * Close the socket with the specified reason. - * - * @param reason to disconnect - */ - public void disconnect(String reason) - { - if ( socket.isClosed() ) - { - return; - } - log( "disconnected with " + reason ); - try - { - stream.write( new PacketFFKick( "[Proxy] " + reason ) ); - } catch ( IOException ex ) - { - } finally - { - try - { - socket.shutdownOutput(); - socket.close(); - } catch ( IOException ioe ) - { - } - } - } - - public void log(String message) - { - $().info( socket.getInetAddress() + ( ( name == null ) ? " " : " [" + name + "] " ) + message ); - } -} diff --git a/proxy/src/main/java/net/md_5/bungee/InitialHandler.java b/proxy/src/main/java/net/md_5/bungee/InitialHandler.java deleted file mode 100644 index ab57c741..00000000 --- a/proxy/src/main/java/net/md_5/bungee/InitialHandler.java +++ /dev/null @@ -1,232 +0,0 @@ -package net.md_5.bungee; - -import com.google.common.base.Preconditions; -import java.io.EOFException; -import java.io.IOException; -import java.net.InetSocketAddress; -import java.net.Socket; -import java.util.ArrayList; -import java.util.List; -import javax.crypto.Cipher; -import javax.crypto.CipherInputStream; -import javax.crypto.CipherOutputStream; -import javax.crypto.SecretKey; -import lombok.Getter; -import net.md_5.bungee.api.ChatColor; -import net.md_5.bungee.api.ProxyServer; -import net.md_5.bungee.api.ServerPing; -import net.md_5.bungee.api.config.ListenerInfo; -import net.md_5.bungee.api.config.ServerInfo; -import net.md_5.bungee.api.connection.PendingConnection; -import net.md_5.bungee.api.connection.ProxiedPlayer; -import net.md_5.bungee.api.event.LoginEvent; -import net.md_5.bungee.api.event.ProxyPingEvent; -import net.md_5.bungee.packet.DefinedPacket; -import net.md_5.bungee.packet.Packet1Login; -import net.md_5.bungee.packet.Packet2Handshake; -import net.md_5.bungee.packet.PacketCDClientStatus; -import net.md_5.bungee.packet.PacketFAPluginMessage; -import net.md_5.bungee.packet.PacketFCEncryptionResponse; -import net.md_5.bungee.packet.PacketFDEncryptionRequest; -import net.md_5.bungee.packet.PacketFEPing; -import net.md_5.bungee.packet.PacketFFKick; -import net.md_5.bungee.packet.PacketHandler; -import net.md_5.bungee.packet.PacketStream; -import net.md_5.mendax.PacketDefinitions; - -public class InitialHandler extends PacketHandler implements Runnable, PendingConnection -{ - - private final Socket socket; - @Getter - private final ListenerInfo listener; - private PacketStream stream; - private Packet1Login forgeLogin; - private Packet2Handshake handshake; - private PacketFDEncryptionRequest request; - private List loginMessages = new ArrayList<>(); - private State thisState = State.HANDSHAKE; - private static final PacketFAPluginMessage forgeMods = new PacketFAPluginMessage( "FML", new byte[] - { - 0, 0, 0, 0, 0, 2 - } ); - - public InitialHandler(Socket socket, ListenerInfo info) throws IOException - { - this.socket = socket; - this.listener = info; - stream = new PacketStream( socket.getInputStream(), socket.getOutputStream(), PacketDefinitions.VANILLA_PROTOCOL ); - } - - private enum State - { - - HANDSHAKE, ENCRYPT, LOGIN, FINISHED; - } - - @Override - public void handle(Packet1Login login) throws Exception - { - Preconditions.checkState( thisState == State.LOGIN, "Not expecting FORGE LOGIN" ); - Preconditions.checkState( forgeLogin == null, "Already received FORGE LOGIN" ); - forgeLogin = login; - stream.setProtocol( PacketDefinitions.FORGE_PROTOCOL ); - } - - @Override - public void handle(PacketFAPluginMessage pluginMessage) throws Exception - { - loginMessages.add( pluginMessage ); - } - - @Override - public void handle(PacketFEPing ping) throws Exception - { - socket.setSoTimeout( 100 ); - boolean newPing = false; - try - { - socket.getInputStream().read(); - newPing = true; - } catch ( IOException ex ) - { - } - - ServerPing pingevent = new ServerPing( BungeeCord.PROTOCOL_VERSION, BungeeCord.GAME_VERSION, - listener.getMotd(), ProxyServer.getInstance().getPlayers().size(), listener.getMaxPlayers() ); - - pingevent = ProxyServer.getInstance().getPluginManager().callEvent( new ProxyPingEvent( this, pingevent ) ).getResponse(); - - String response = ( newPing ) ? ChatColor.COLOR_CHAR + "1" - + "\00" + pingevent.getProtocolVersion() - + "\00" + pingevent.getGameVersion() - + "\00" + pingevent.getMotd() - + "\00" + pingevent.getCurrentPlayers() - + "\00" + pingevent.getMaxPlayers() - : pingevent.getMotd() + ChatColor.COLOR_CHAR + pingevent.getCurrentPlayers() + ChatColor.COLOR_CHAR + pingevent.getMaxPlayers(); - disconnect( response ); - } - - @Override - public void handle(Packet2Handshake handshake) throws Exception - { - Preconditions.checkState( thisState == State.HANDSHAKE, "Not expecting HANDSHAKE" ); - this.handshake = handshake; - stream.write( forgeMods ); - stream.write( request = EncryptionUtil.encryptRequest() ); - thisState = State.ENCRYPT; - } - - @Override - public void handle(PacketFCEncryptionResponse encryptResponse) throws Exception - { - Preconditions.checkState( thisState == State.ENCRYPT, "Not expecting ENCRYPT" ); - - SecretKey shared = EncryptionUtil.getSecret( encryptResponse, request ); - if ( BungeeCord.getInstance().config.isOnlineMode() && !EncryptionUtil.isAuthenticated( handshake.username, request.serverId, shared ) ) - { - throw new KickException( "Not authenticated with minecraft.net" ); - } - - // Check for multiple connections - ProxiedPlayer old = ProxyServer.getInstance().getPlayer( handshake.username ); - if ( old != null ) - { - old.disconnect( "You are already connected to the server" ); - } - - // fire login event - LoginEvent event = new LoginEvent( this ); - ProxyServer.getInstance().getPluginManager().callEvent( event ); - if ( event.isCancelled() ) - { - throw new KickException( event.getCancelReason() ); - } - - stream.write( new PacketFCEncryptionResponse() ); - stream = new PacketStream( new CipherInputStream( socket.getInputStream(), EncryptionUtil.getCipher( Cipher.DECRYPT_MODE, shared ) ), - new CipherOutputStream( socket.getOutputStream(), EncryptionUtil.getCipher( Cipher.ENCRYPT_MODE, shared ) ), stream.getProtocol() ); - - thisState = State.LOGIN; - } - - @Override - public void handle(PacketCDClientStatus clientStatus) throws Exception - { - Preconditions.checkState( thisState == State.LOGIN, "Not expecting LOGIN" ); - - UserConnection userCon = new UserConnection( socket, this, stream, handshake, forgeLogin, loginMessages ); - ServerInfo server = ProxyServer.getInstance().getReconnectHandler().getServer( userCon ); - userCon.connect( server, true ); - - thisState = State.FINISHED; - } - - @Override - public void run() - { - try - { - while ( thisState != State.FINISHED ) - { - byte[] buf = stream.readPacket(); - DefinedPacket packet = DefinedPacket.packet( buf ); - packet.handle( this ); - } - } catch ( KickException ex ) - { - disconnect( "[Proxy - Kicked] " + ex.getMessage() ); - } catch ( EOFException ex ) - { - } catch ( Exception ex ) - { - disconnect( "[Proxy Error] " + Util.exception( ex ) ); - ex.printStackTrace(); - } - } - - @Override - public void disconnect(String reason) - { - thisState = State.FINISHED; - try - { - stream.write( new PacketFFKick( reason ) ); - } catch ( IOException ioe ) - { - } finally - { - try - { - socket.shutdownOutput(); - socket.close(); - } catch ( IOException ioe2 ) - { - } - } - } - - @Override - public String getName() - { - return ( handshake == null ) ? null : handshake.username; - } - - @Override - public byte getVersion() - { - return ( handshake == null ) ? -1 : handshake.procolVersion; - } - - @Override - public InetSocketAddress getVirtualHost() - { - return ( handshake == null ) ? null : new InetSocketAddress( handshake.host, handshake.port ); - } - - @Override - public InetSocketAddress getAddress() - { - return (InetSocketAddress) socket.getRemoteSocketAddress(); - } -} diff --git a/proxy/src/main/java/net/md_5/bungee/KickException.java b/proxy/src/main/java/net/md_5/bungee/KickException.java deleted file mode 100644 index 5b495e00..00000000 --- a/proxy/src/main/java/net/md_5/bungee/KickException.java +++ /dev/null @@ -1,14 +0,0 @@ -package net.md_5.bungee; - -/** - * Exception, which when thrown will disconnect the player from the proxy with - * the specified message. - */ -public class KickException extends RuntimeException -{ - - public KickException(String message) - { - super( message ); - } -} diff --git a/proxy/src/main/java/net/md_5/bungee/ListenThread.java b/proxy/src/main/java/net/md_5/bungee/ListenThread.java deleted file mode 100644 index d0e52973..00000000 --- a/proxy/src/main/java/net/md_5/bungee/ListenThread.java +++ /dev/null @@ -1,48 +0,0 @@ -package net.md_5.bungee; - -import java.io.IOException; -import java.net.ServerSocket; -import java.net.Socket; -import java.net.SocketException; -import static net.md_5.bungee.Logger.$; -import net.md_5.bungee.api.config.ListenerInfo; - -/** - * Thread to listen and dispatch incoming connections to the proxy. - */ -public class ListenThread extends Thread -{ - - public final ServerSocket socket; - private final ListenerInfo info; - - public ListenThread(ListenerInfo info) throws IOException - { - super( "Listen Thread - " + info ); - this.info = info; - socket = new ServerSocket(); - socket.bind( info.getHost() ); - } - - @Override - public void run() - { - while ( !isInterrupted() ) - { - try - { - Socket client = socket.accept(); - BungeeCord.getInstance().setSocketOptions( client ); - $().info( client.getInetAddress() + " has connected" ); - InitialHandler handler = new InitialHandler( client, info ); - BungeeCord.getInstance().threadPool.submit( handler ); - } catch ( SocketException ex ) - { - ex.printStackTrace(); // Now people can see why their operating system is failing them and stop bitching at me! - } catch ( IOException ex ) - { - ex.printStackTrace(); // TODO - } - } - } -} diff --git a/proxy/src/main/java/net/md_5/bungee/Logger.java b/proxy/src/main/java/net/md_5/bungee/Logger.java index 76190f3c..aa38547d 100644 --- a/proxy/src/main/java/net/md_5/bungee/Logger.java +++ b/proxy/src/main/java/net/md_5/bungee/Logger.java @@ -3,7 +3,6 @@ package net.md_5.bungee; import java.io.IOException; import java.io.PrintWriter; import java.io.StringWriter; -import java.text.MessageFormat; import java.text.SimpleDateFormat; import java.util.logging.FileHandler; import java.util.logging.Formatter; @@ -21,7 +20,7 @@ public class Logger extends java.util.logging.Logger public Logger() { - super( "RubberBand", null ); + super( "BungeeCord", null ); try { FileHandler handler = new FileHandler( "proxy.log", 1 << 14, 1, true ); diff --git a/proxy/src/main/java/net/md_5/bungee/ServerConnection.java b/proxy/src/main/java/net/md_5/bungee/ServerConnection.java index 62fc3507..c74b124b 100644 --- a/proxy/src/main/java/net/md_5/bungee/ServerConnection.java +++ b/proxy/src/main/java/net/md_5/bungee/ServerConnection.java @@ -1,47 +1,57 @@ package net.md_5.bungee; +import io.netty.channel.Channel; import java.net.InetSocketAddress; -import java.net.Socket; -import java.util.Queue; -import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.TimeUnit; import lombok.Getter; -import net.md_5.bungee.api.Callback; -import net.md_5.bungee.api.ServerPing; +import lombok.RequiredArgsConstructor; +import lombok.Setter; import net.md_5.bungee.api.config.ServerInfo; import net.md_5.bungee.api.connection.Server; -import net.md_5.bungee.packet.DefinedPacket; import net.md_5.bungee.packet.Packet1Login; import net.md_5.bungee.packet.PacketFAPluginMessage; -import net.md_5.bungee.packet.PacketStream; +import net.md_5.bungee.packet.PacketFFKick; -/** - * Class representing a connection from the proxy to the server; ie upstream. - */ -public class ServerConnection extends GenericConnection implements Server +@RequiredArgsConstructor +public class ServerConnection implements Server { + @Getter + private final Channel ch; @Getter private final ServerInfo info; - public final Packet1Login loginPacket; - public Queue packetQueue = new ConcurrentLinkedQueue<>(); - - public ServerConnection(Socket socket, ServerInfo info, PacketStream stream, Packet1Login loginPacket) - { - super( socket, stream ); - this.info = info; - this.loginPacket = loginPacket; - } + @Getter + private final Packet1Login loginPacket; + @Getter + @Setter + private boolean isObsolete; @Override public void sendData(String channel, byte[] data) { - packetQueue.add( new PacketFAPluginMessage( channel, data ) ); + ch.write( new PacketFAPluginMessage( channel, data ) ); } @Override - public void ping(final Callback callback) + public synchronized void disconnect(String reason) { - getInfo().ping( callback ); + disconnect( ch, reason ); + } + + static void disconnect(final Channel ch, String reason) + { + if ( ch.isActive() ) + { + ch.write( new PacketFFKick( reason ) ); + ch.eventLoop().schedule( new Runnable() + { + @Override + public void run() + { + ch.close(); + } + }, 100, TimeUnit.MILLISECONDS ); + } } @Override diff --git a/proxy/src/main/java/net/md_5/bungee/ServerConnector.java b/proxy/src/main/java/net/md_5/bungee/ServerConnector.java index b080164e..9c138201 100644 --- a/proxy/src/main/java/net/md_5/bungee/ServerConnector.java +++ b/proxy/src/main/java/net/md_5/bungee/ServerConnector.java @@ -1,45 +1,119 @@ package net.md_5.bungee; import com.google.common.base.Preconditions; -import java.io.IOException; -import java.net.Socket; +import io.netty.channel.Channel; import java.util.Queue; +import lombok.RequiredArgsConstructor; import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.ProxyServer; import net.md_5.bungee.api.config.ServerInfo; import net.md_5.bungee.api.event.ServerConnectedEvent; +import net.md_5.bungee.connection.CancelSendSignal; +import net.md_5.bungee.connection.DownstreamBridge; +import net.md_5.bungee.netty.HandlerBoss; import net.md_5.bungee.packet.DefinedPacket; import net.md_5.bungee.packet.Packet1Login; +import net.md_5.bungee.packet.Packet2Handshake; +import net.md_5.bungee.packet.Packet9Respawn; import net.md_5.bungee.packet.PacketCDClientStatus; import net.md_5.bungee.packet.PacketFDEncryptionRequest; import net.md_5.bungee.packet.PacketFFKick; import net.md_5.bungee.packet.PacketHandler; -import net.md_5.bungee.packet.PacketStream; +@RequiredArgsConstructor public class ServerConnector extends PacketHandler { - private final PacketStream stream; - private Packet1Login loginPacket; + private final ProxyServer bungee; + private Channel ch; + private final UserConnection user; + private final ServerInfo target; private State thisState = State.ENCRYPT_REQUEST; - public ServerConnector(PacketStream stream) - { - this.stream = stream; - } - private enum State { ENCRYPT_REQUEST, LOGIN, FINISHED; } + @Override + public void connected(Channel channel) throws Exception + { + this.ch = channel; + // TODO: Fix this crap + channel.write( new Packet2Handshake( user.handshake.procolVersion, user.handshake.username, user.handshake.host, user.handshake.port ) ); + channel.write( PacketCDClientStatus.CLIENT_LOGIN ); + } + @Override public void handle(Packet1Login login) throws Exception { Preconditions.checkState( thisState == State.LOGIN, "Not exepcting LOGIN" ); - loginPacket = login; + + ServerConnection server = new ServerConnection( ch, target, login ); + ServerConnectedEvent event = new ServerConnectedEvent( user, server ); + bungee.getPluginManager().callEvent( event ); + + ch.write( BungeeCord.getInstance().registerChannels() ); + + Queue packetQueue = ( (BungeeServerInfo) target ).getPacketQueue(); + while ( !packetQueue.isEmpty() ) + { + ch.write( packetQueue.poll() ); + } + + synchronized ( user.getSwitchMutex() ) + { + if ( user.getServer() == null ) + { + BungeeCord.getInstance().connections.put( user.getName(), user ); + bungee.getTabListHandler().onConnect( user ); + // Once again, first connection + user.clientEntityId = login.entityId; + user.serverEntityId = login.entityId; + // Set tab list size + Packet1Login modLogin = new Packet1Login( + login.entityId, + login.levelType, + login.gameMode, + (byte) login.dimension, + login.difficulty, + login.unused, + (byte) user.getPendingConnection().getListener().getTabListSize() ); + user.ch.write( modLogin ); + ch.write( BungeeCord.getInstance().registerChannels() ); + } else + { + bungee.getTabListHandler().onServerChange( user ); + user.sendPacket( Packet9Respawn.DIM1_SWITCH ); + user.sendPacket( Packet9Respawn.DIM2_SWITCH ); + + user.serverEntityId = login.entityId; + user.ch.write( new Packet9Respawn( login.dimension, login.difficulty, login.gameMode, (short) 256, login.levelType ) ); + + // Remove from old servers + user.getServer().setObsolete( true ); + user.getServer().disconnect( "Quitting" ); + } + + // TODO: Fix this? + if ( !user.ch.isActive() ) + { + server.disconnect( "Quitting" ); + throw new IllegalStateException( "No client connected for pending server!" ); + } + + // Add to new server + // TODO: Move this to the connected() method of DownstreamBridge + target.addPlayer( user ); + + user.setServer( server ); + ch.pipeline().get( HandlerBoss.class ).setHandler( new DownstreamBridge( bungee, user, server ) ); + } + thisState = State.FINISHED; + + throw new CancelSendSignal(); } @Override @@ -52,66 +126,19 @@ public class ServerConnector extends PacketHandler @Override public void handle(PacketFFKick kick) throws Exception { - throw new KickException( kick.message ); - } - - public static ServerConnection connect(UserConnection user, ServerInfo info, boolean retry) - { - Socket socket = null; - try + String message = ChatColor.RED + "Kicked whilst connecting to " + target.getName() + ": " + kick.message; + if ( user.getServer() == null ) { - socket = new Socket(); - socket.connect( info.getAddress(), BungeeCord.getInstance().config.getTimeout() ); - BungeeCord.getInstance().setSocketOptions( socket ); - PacketStream stream = new PacketStream( socket.getInputStream(), socket.getOutputStream(), user.stream.getProtocol() ); - - ServerConnector connector = new ServerConnector( stream ); - stream.write( user.handshake ); - stream.write( PacketCDClientStatus.CLIENT_LOGIN ); - - while ( connector.thisState != State.FINISHED ) - { - byte[] buf = stream.readPacket(); - DefinedPacket packet = DefinedPacket.packet( buf ); - packet.handle( connector ); - } - - ServerConnection server = new ServerConnection( socket, info, stream, connector.loginPacket ); - ServerConnectedEvent event = new ServerConnectedEvent( user, server ); - ProxyServer.getInstance().getPluginManager().callEvent( event ); - - stream.write( BungeeCord.getInstance().registerChannels() ); - - Queue packetQueue = ( (BungeeServerInfo) info ).getPacketQueue(); - while ( !packetQueue.isEmpty() ) - { - stream.write( packetQueue.poll() ); - } - return server; - } catch ( Exception ex ) + user.disconnect( message ); + } else { - if ( socket != null ) - { - try - { - socket.close(); - } catch ( IOException ioe ) - { - } - } - ServerInfo def = ProxyServer.getInstance().getServers().get( user.getPendingConnection().getListener().getDefaultServer() ); - if ( retry && !info.equals( def ) ) - { - user.sendMessage( ChatColor.RED + "Could not connect to target server, you have been moved to the default server" ); - return connect( user, def, false ); - } else - { - if ( ex instanceof RuntimeException ) - { - throw (RuntimeException) ex; - } - throw new RuntimeException( "Could not connect to target server " + Util.exception( ex ) ); - } + user.sendMessage( message ); } } + + @Override + public String toString() + { + return "[" + user.getName() + "] <-> ServerConnector [" + target.getName() + "]"; + } } 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 2673fcfc..be29988a 100644 --- a/proxy/src/main/java/net/md_5/bungee/UserConnection.java +++ b/proxy/src/main/java/net/md_5/bungee/UserConnection.java @@ -1,214 +1,188 @@ package net.md_5.bungee; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; +import com.google.common.base.Preconditions; +import gnu.trove.set.hash.THashSet; +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.channel.socket.nio.NioSocketChannel; import java.net.InetSocketAddress; -import java.net.Socket; import java.util.Collection; import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; import java.util.List; -import java.util.Map; -import java.util.Queue; -import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.logging.Level; +import lombok.AccessLevel; import lombok.Getter; +import lombok.Setter; import lombok.Synchronized; +import net.md_5.bungee.api.ChatColor; import net.md_5.bungee.api.ProxyServer; import net.md_5.bungee.api.config.ServerInfo; import net.md_5.bungee.api.connection.PendingConnection; import net.md_5.bungee.api.connection.ProxiedPlayer; -import net.md_5.bungee.api.event.ChatEvent; -import net.md_5.bungee.api.event.PlayerDisconnectEvent; -import net.md_5.bungee.api.event.PluginMessageEvent; import net.md_5.bungee.api.event.ServerConnectEvent; +import net.md_5.bungee.netty.HandlerBoss; +import net.md_5.bungee.netty.PipelineUtils; import net.md_5.bungee.packet.*; -public final class UserConnection extends GenericConnection implements ProxiedPlayer +public final class UserConnection implements ProxiedPlayer { public final Packet2Handshake handshake; + private final ProxyServer bungee; + public final Channel ch; final Packet1Login forgeLogin; final List loginMessages; - public Queue packetQueue = new ConcurrentLinkedQueue<>(); @Getter private final PendingConnection pendingConnection; @Getter + @Setter(AccessLevel.PACKAGE) private ServerConnection server; - private UpstreamBridge upBridge; - private DownstreamBridge downBridge; // reconnect stuff - private int clientEntityId; - private int serverEntityId; - private volatile boolean reconnecting; + public int clientEntityId; + public int serverEntityId; // ping stuff - private int trackingPingId; - private long pingTime; + public int trackingPingId; + public long pingTime; @Getter + private String name; + @Getter + private String displayName; + @Getter + @Setter private int ping = 1000; // Permissions - private final Collection groups = new HashSet<>(); - private final Map permissions = new HashMap<>(); + private final Collection playerGroups = new THashSet<>(); + private final Collection permissions = new THashSet<>(); private final Object permMutex = new Object(); - // Hack for connect timings - private ServerInfo nextServer; - private volatile boolean clientConnected = true; + @Getter + private final Object switchMutex = new Object(); - public UserConnection(Socket socket, PendingConnection pendingConnection, PacketStream stream, Packet2Handshake handshake, Packet1Login forgeLogin, List loginMessages) + public UserConnection(BungeeCord bungee, Channel channel, PendingConnection pendingConnection, Packet2Handshake handshake, Packet1Login forgeLogin, List loginMessages) { - super( socket, stream ); + this.bungee = bungee; + this.ch = channel; this.handshake = handshake; this.pendingConnection = pendingConnection; this.forgeLogin = forgeLogin; this.loginMessages = loginMessages; - name = handshake.username.substring( 0, Math.min( handshake.username.length(), 16 ) ); - displayName = name; + this.name = handshake.username; + this.displayName = name; - Collection g = ProxyServer.getInstance().getConfigurationAdapter().getGroups( name ); + Collection g = bungee.getConfigurationAdapter().getGroups( name ); for ( String s : g ) { addGroups( s ); } } + public void sendPacket(DefinedPacket p) + { + ch.write( p ); + } + @Override public void setDisplayName(String name) { - ProxyServer.getInstance().getTabListHandler().onDisconnect( this ); - displayName = name; - ProxyServer.getInstance().getTabListHandler().onConnect( this ); + Preconditions.checkArgument( name.length() <= 16, "Display name cannot be longer than 16 characters" ); + bungee.getTabListHandler().onDisconnect( this ); + bungee.getTabListHandler().onConnect( this ); } @Override public void connect(ServerInfo target) { - nextServer = target; + connect( target, false ); } - public void connect(ServerInfo target, boolean force) + public void connect(ServerInfo info, final boolean retry) { - nextServer = null; - if ( server == null ) + ServerConnectEvent event = new ServerConnectEvent( this, info ); + ProxyServer.getInstance().getPluginManager().callEvent( event ); + final ServerInfo target = event.getTarget(); // Update in case the event changed target + new Bootstrap() + .channel( NioSocketChannel.class ) + .group( BungeeCord.getInstance().eventLoops ) + .handler( new ChannelInitializer() { - // First join - BungeeCord.getInstance().connections.put( name, this ); - ProxyServer.getInstance().getTabListHandler().onConnect( this ); - } - - ServerConnectEvent event = new ServerConnectEvent( this, target ); - BungeeCord.getInstance().getPluginManager().callEvent( event ); - target = event.getTarget(); // Update in case the event changed target - - ProxyServer.getInstance().getTabListHandler().onServerChange( this ); - try - { - reconnecting = true; - - if ( server != null ) + @Override + protected void initChannel(Channel ch) throws Exception { - stream.write( new Packet9Respawn( (byte) 1, (byte) 0, (byte) 0, (short) 256, "DEFAULT" ) ); - stream.write( new Packet9Respawn( (byte) -1, (byte) 0, (byte) 0, (short) 256, "DEFAULT" ) ); + PipelineUtils.BASE.initChannel( ch ); + ch.pipeline().get( HandlerBoss.class ).setHandler( new ServerConnector( bungee, UserConnection.this, target ) ); } - - ServerConnection newServer = ServerConnector.connect( this, target, true ); - if ( server == null ) + } ) + .option( ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000 ) // TODO: Configurable + .remoteAddress( target.getAddress() ) + .connect().addListener( new ChannelFutureListener() + { + @Override + public void operationComplete(ChannelFuture future) throws Exception { - // Once again, first connection - clientEntityId = newServer.loginPacket.entityId; - serverEntityId = newServer.loginPacket.entityId; - // Set tab list size - Packet1Login s = newServer.loginPacket; - Packet1Login login = new Packet1Login( s.entityId, s.levelType, s.gameMode, (byte) s.dimension, s.difficulty, s.unused, (byte) pendingConnection.getListener().getTabListSize() ); - stream.write( login ); - stream.write( BungeeCord.getInstance().registerChannels() ); - - upBridge = new UpstreamBridge(); - upBridge.start(); - } else - { - try - { - downBridge.interrupt(); - downBridge.join(); - } catch ( InterruptedException ie ) + if ( !future.isSuccess() ) { + future.channel().close(); + ServerInfo def = ProxyServer.getInstance().getServers().get( getPendingConnection().getListener().getDefaultServer() ); + if ( retry && !target.equals( def ) ) + { + sendMessage( ChatColor.RED + "Could not connect to target server, you have been moved to the default server" ); + connect( def, false ); + } else + { + if ( server == null ) + { + disconnect( "Server down, could not connect to default! " + future.cause().getClass().getName() ); + } else + { + sendMessage( ChatColor.RED + "Could not connect to target server: " + future.cause().getClass().getName() ); + } + } } - - server.disconnect( "Quitting" ); - server.getInfo().removePlayer( this ); - - Packet1Login login = newServer.loginPacket; - serverEntityId = login.entityId; - stream.write( new Packet9Respawn( login.dimension, login.difficulty, login.gameMode, (short) 256, login.levelType ) ); } - - // Reconnect process has finished, lets get the player moving again - reconnecting = false; - - // Add to new - target.addPlayer( this ); - - // Start the bridges and move on - server = newServer; - downBridge = new DownstreamBridge(); - downBridge.start(); - } catch ( KickException ex ) - { - disconnect( ex.getMessage() ); - } catch ( Exception ex ) - { - disconnect( "Could not connect to server - " + Util.exception( ex ) ); - } + } ); } @Override public synchronized void disconnect(String reason) { - if ( clientConnected ) + if ( ch.isActive() ) { - PlayerDisconnectEvent event = new PlayerDisconnectEvent( this ); - ProxyServer.getInstance().getPluginManager().callEvent( event ); - ProxyServer.getInstance().getTabListHandler().onDisconnect( this ); - ProxyServer.getInstance().getPlayers().remove( this ); - - super.disconnect( reason ); + bungee.getLogger().log( Level.INFO, "[" + getName() + "] disconnected with: " + reason ); + ch.write( new PacketFFKick( reason ) ); + ch.close(); if ( server != null ) { - server.getInfo().removePlayer( this ); server.disconnect( "Quitting" ); - ProxyServer.getInstance().getReconnectHandler().setServer( this ); } - - clientConnected = false; } } @Override public void sendMessage(String message) { - packetQueue.add( new Packet3Chat( message ) ); + ch.write( new Packet3Chat( message ) ); } @Override public void sendData(String channel, byte[] data) { - server.packetQueue.add( new PacketFAPluginMessage( channel, data ) ); + ch.write( new PacketFAPluginMessage( channel, data ) ); } @Override public InetSocketAddress getAddress() { - return (InetSocketAddress) socket.getRemoteSocketAddress(); + return (InetSocketAddress) ch.remoteAddress(); } @Override @Synchronized("permMutex") public Collection getGroups() { - return Collections.unmodifiableCollection( groups ); + return Collections.unmodifiableCollection( playerGroups ); } @Override @@ -217,8 +191,8 @@ public final class UserConnection extends GenericConnection implements ProxiedPl { for ( String group : groups ) { - this.groups.add( group ); - for ( String permission : ProxyServer.getInstance().getConfigurationAdapter().getPermissions( group ) ) + playerGroups.add( group ); + for ( String permission : bungee.getConfigurationAdapter().getPermissions( group ) ) { setPermission( permission, true ); } @@ -231,8 +205,8 @@ public final class UserConnection extends GenericConnection implements ProxiedPl { for ( String group : groups ) { - this.groups.remove( group ); - for ( String permission : ProxyServer.getInstance().getConfigurationAdapter().getPermissions( group ) ) + playerGroups.remove( group ); + for ( String permission : bungee.getConfigurationAdapter().getPermissions( group ) ) { setPermission( permission, false ); } @@ -243,303 +217,19 @@ public final class UserConnection extends GenericConnection implements ProxiedPl @Synchronized("permMutex") public boolean hasPermission(String permission) { - Boolean val = permissions.get( permission ); - return ( val == null ) ? false : val; + return permissions.contains( permission ); } @Override @Synchronized("permMutex") public void setPermission(String permission, boolean value) { - permissions.put( permission, value ); - } - - private class UpstreamBridge extends Thread - { - - public UpstreamBridge() + if ( value ) { - super( "Upstream Bridge - " + name ); - } - - @Override - public void run() + permissions.add( permission ); + } else { - while ( !socket.isClosed() ) - { - try - { - byte[] packet = stream.readPacket(); - boolean sendPacket = true; - int id = Util.getId( packet ); - - switch ( id ) - { - case 0x00: - if ( trackingPingId == new Packet0KeepAlive( packet ).id ) - { - int newPing = (int) ( System.currentTimeMillis() - pingTime ); - ProxyServer.getInstance().getTabListHandler().onPingChange( UserConnection.this, newPing ); - ping = newPing; - } - break; - case 0x03: - Packet3Chat chat = new Packet3Chat( packet ); - if ( chat.message.startsWith( "/" ) ) - { - sendPacket = !ProxyServer.getInstance().getPluginManager().dispatchCommand( UserConnection.this, chat.message.substring( 1 ) ); - } else - { - ChatEvent chatEvent = new ChatEvent( UserConnection.this, server, chat.message ); - ProxyServer.getInstance().getPluginManager().callEvent( chatEvent ); - sendPacket = !chatEvent.isCancelled(); - } - break; - case 0xFA: - // Call the onPluginMessage event - PacketFAPluginMessage message = new PacketFAPluginMessage( packet ); - - // Might matter in the future - if ( message.tag.equals( "BungeeCord" ) ) - { - continue; - } - - PluginMessageEvent event = new PluginMessageEvent( UserConnection.this, server, message.tag, message.data ); - ProxyServer.getInstance().getPluginManager().callEvent( event ); - - if ( event.isCancelled() ) - { - continue; - } - break; - } - - while ( !server.packetQueue.isEmpty() ) - { - DefinedPacket p = server.packetQueue.poll(); - if ( p != null ) - { - server.stream.write( p ); - } - } - - EntityMap.rewrite( packet, clientEntityId, serverEntityId ); - if ( sendPacket && !server.socket.isClosed() ) - { - server.stream.write( packet ); - } - - try - { - Thread.sleep( BungeeCord.getInstance().config.getSleepTime() ); - } catch ( InterruptedException ex ) - { - } - } catch ( IOException ex ) - { - disconnect( "Reached end of stream" ); - } catch ( Exception ex ) - { - disconnect( Util.exception( ex ) ); - } - } - } - } - - private class DownstreamBridge extends Thread - { - - public DownstreamBridge() - { - super( "Downstream Bridge - " + name ); - } - - @Override - public void run() - { - try - { - outer: - while ( !reconnecting ) - { - byte[] packet = server.stream.readPacket(); - int id = Util.getId( packet ); - - switch ( id ) - { - case 0x00: - trackingPingId = new Packet0KeepAlive( packet ).id; - pingTime = System.currentTimeMillis(); - break; - case 0x03: - Packet3Chat chat = new Packet3Chat( packet ); - ChatEvent chatEvent = new ChatEvent( server, UserConnection.this, chat.message ); - ProxyServer.getInstance().getPluginManager().callEvent( chatEvent ); - - if ( chatEvent.isCancelled() ) - { - continue; - } - break; - case 0xC9: - PacketC9PlayerListItem playerList = new PacketC9PlayerListItem( packet ); - if ( !ProxyServer.getInstance().getTabListHandler().onListUpdate( UserConnection.this, playerList.username, playerList.online, playerList.ping ) ) - { - continue; - } - break; - case 0xFA: - // Call the onPluginMessage event - PacketFAPluginMessage message = new PacketFAPluginMessage( packet ); - DataInputStream in = new DataInputStream( new ByteArrayInputStream( message.data ) ); - PluginMessageEvent event = new PluginMessageEvent( server, UserConnection.this, message.tag, message.data ); - ProxyServer.getInstance().getPluginManager().callEvent( event ); - - if ( event.isCancelled() ) - { - continue; - } - - if ( message.tag.equals( "BungeeCord" ) ) - { - String subChannel = in.readUTF(); - if ( subChannel.equals( "Forward" ) ) - { - String target = in.readUTF(); - String channel = in.readUTF(); - short len = in.readShort(); - byte[] data = new byte[ len ]; - in.readFully( data ); - - - ByteArrayOutputStream b = new ByteArrayOutputStream(); - DataOutputStream out = new DataOutputStream( b ); - out.writeUTF( channel ); - out.writeShort( data.length ); - out.write( data ); - - if ( target.equals( "ALL" ) ) - { - for ( ServerInfo server : BungeeCord.getInstance().getServers().values() ) - { - server.sendData( "BungeeCord", b.toByteArray() ); - } - } else - { - ServerInfo server = BungeeCord.getInstance().getServerInfo( target ); - if ( server != null ) - { - server.sendData( "BungeeCord", b.toByteArray() ); - } - } - } - if ( subChannel.equals( "Connect" ) ) - { - ServerInfo server = ProxyServer.getInstance().getServerInfo( in.readUTF() ); - if ( server != null ) - { - connect( server, true ); - break outer; - } - } - if ( subChannel.equals( "IP" ) ) - { - ByteArrayOutputStream b = new ByteArrayOutputStream(); - DataOutputStream out = new DataOutputStream( b ); - out.writeUTF( "IP" ); - out.writeUTF( getAddress().getHostString() ); - out.writeInt( getAddress().getPort() ); - getServer().sendData( "BungeeCord", b.toByteArray() ); - } - if ( subChannel.equals( "PlayerCount" ) ) - { - ServerInfo server = ProxyServer.getInstance().getServerInfo( in.readUTF() ); - if ( server != null ) - { - ByteArrayOutputStream b = new ByteArrayOutputStream(); - DataOutputStream out = new DataOutputStream( b ); - out.writeUTF( "PlayerCount" ); - out.writeUTF( server.getName() ); - out.writeInt( server.getPlayers().size() ); - getServer().sendData( "BungeeCord", b.toByteArray() ); - } - } - if ( subChannel.equals( "PlayerList" ) ) - { - ServerInfo server = ProxyServer.getInstance().getServerInfo( in.readUTF() ); - if ( server != null ) - { - ByteArrayOutputStream b = new ByteArrayOutputStream(); - DataOutputStream out = new DataOutputStream( b ); - out.writeUTF( "PlayerList" ); - out.writeUTF( server.getName() ); - - StringBuilder sb = new StringBuilder(); - for ( ProxiedPlayer p : server.getPlayers() ) - { - sb.append( p.getName() ); - sb.append( "," ); - } - out.writeUTF( sb.substring( 0, sb.length() - 1 ) ); - - getServer().sendData( "BungeeCord", b.toByteArray() ); - } - } - if ( subChannel.equals( "GetServers" ) ) - { - ByteArrayOutputStream b = new ByteArrayOutputStream(); - DataOutputStream out = new DataOutputStream( b ); - out.writeUTF( "GetServers" ); - - StringBuilder sb = new StringBuilder(); - for ( String server : ProxyServer.getInstance().getServers().keySet() ) - { - sb.append( server ); - sb.append( "," ); - } - out.writeUTF( sb.substring( 0, sb.length() - 1 ) ); - - getServer().sendData( "BungeeCord", b.toByteArray() ); - } - if ( subChannel.equals( "Message" ) ) - { - ProxiedPlayer target = ProxyServer.getInstance().getPlayer( in.readUTF() ); - if ( target != null ) - { - target.sendMessage( in.readUTF() ); - } - } - continue; - } - break; - case 0xFF: - disconnect( new PacketFFKick( packet ).message ); - break outer; - } - - while ( !packetQueue.isEmpty() ) - { - DefinedPacket p = packetQueue.poll(); - if ( p != null ) - { - stream.write( p ); - } - } - - EntityMap.rewrite( packet, serverEntityId, clientEntityId ); - stream.write( packet ); - - if ( nextServer != null ) - { - connect( nextServer, true ); - break outer; - } - } - } catch ( Exception ex ) - { - disconnect( Util.exception( ex ) ); - } + permissions.remove( permission ); } } } diff --git a/proxy/src/main/java/net/md_5/bungee/Util.java b/proxy/src/main/java/net/md_5/bungee/Util.java index a96755bb..605cbe22 100644 --- a/proxy/src/main/java/net/md_5/bungee/Util.java +++ b/proxy/src/main/java/net/md_5/bungee/Util.java @@ -27,18 +27,6 @@ public class Util return new InetSocketAddress( split[0], port ); } - /** - * Gets the value of the first unsigned byte of the specified array. Useful - * for getting the id of a packet array . - * - * @param b the array to read from - * @return the unsigned value of the first byte - */ - public static int getId(byte[] b) - { - return b[0] & 0xFF; - } - /** * Normalizes a config path by prefix upper case letters with '_' and * turning them to lowercase. diff --git a/proxy/src/main/java/net/md_5/bungee/connection/CancelSendSignal.java b/proxy/src/main/java/net/md_5/bungee/connection/CancelSendSignal.java new file mode 100644 index 00000000..fce136c6 --- /dev/null +++ b/proxy/src/main/java/net/md_5/bungee/connection/CancelSendSignal.java @@ -0,0 +1,17 @@ +package net.md_5.bungee.connection; + +public class CancelSendSignal extends Error +{ + + @Override + public synchronized Throwable initCause(Throwable cause) + { + return this; + } + + @Override + public synchronized Throwable fillInStackTrace() + { + return this; + } +} diff --git a/proxy/src/main/java/net/md_5/bungee/connection/DownstreamBridge.java b/proxy/src/main/java/net/md_5/bungee/connection/DownstreamBridge.java new file mode 100644 index 00000000..b724b14a --- /dev/null +++ b/proxy/src/main/java/net/md_5/bungee/connection/DownstreamBridge.java @@ -0,0 +1,225 @@ +package net.md_5.bungee.connection; + +import com.google.common.io.ByteArrayDataInput; +import com.google.common.io.ByteArrayDataOutput; +import com.google.common.io.ByteStreams; +import io.netty.buffer.ByteBuf; +import io.netty.channel.Channel; +import lombok.RequiredArgsConstructor; +import net.md_5.bungee.EntityMap; +import net.md_5.bungee.ServerConnection; +import net.md_5.bungee.UserConnection; +import net.md_5.bungee.Util; +import net.md_5.bungee.api.ProxyServer; +import net.md_5.bungee.api.config.ServerInfo; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.api.event.ChatEvent; +import net.md_5.bungee.api.event.PluginMessageEvent; +import net.md_5.bungee.packet.Packet0KeepAlive; +import net.md_5.bungee.packet.Packet3Chat; +import net.md_5.bungee.packet.PacketC9PlayerListItem; +import net.md_5.bungee.packet.PacketFAPluginMessage; +import net.md_5.bungee.packet.PacketFFKick; +import net.md_5.bungee.packet.PacketHandler; + +@RequiredArgsConstructor +public class DownstreamBridge extends PacketHandler +{ + + private final ProxyServer bungee; + private final UserConnection con; + private final ServerConnection server; + + @Override + public void exception(Throwable t) throws Exception + { + con.disconnect( Util.exception( t ) ); + } + + @Override + public void disconnected(Channel channel) throws Exception + { + // We lost connection to the server + server.getInfo().removePlayer( con ); + bungee.getReconnectHandler().setServer( con ); + + if ( !server.isObsolete() ) + { + con.disconnect( "[Proxy] Lost connection to server D:" ); + } + } + + @Override + public void handle(ByteBuf buf) throws Exception + { + EntityMap.rewrite( buf, con.serverEntityId, con.clientEntityId ); + con.ch.write( buf ); + } + + @Override + public void handle(Packet0KeepAlive alive) throws Exception + { + con.trackingPingId = alive.id; + } + + @Override + public void handle(Packet3Chat chat) throws Exception + { + ChatEvent chatEvent = new ChatEvent( con.getServer(), con, chat.message ); + bungee.getPluginManager().callEvent( chatEvent ); + + if ( chatEvent.isCancelled() ) + { + throw new CancelSendSignal(); + } + } + + @Override + public void handle(PacketC9PlayerListItem playerList) throws Exception + { + + if ( !bungee.getTabListHandler().onListUpdate( con, playerList.username, playerList.online, playerList.ping ) ) + { + throw new CancelSendSignal(); + } + } + + @Override + public void handle(PacketFAPluginMessage pluginMessage) throws Exception + { + ByteArrayDataInput in = ByteStreams.newDataInput( pluginMessage.data ); + PluginMessageEvent event = new PluginMessageEvent( con.getServer(), con, pluginMessage.tag, pluginMessage.data.clone() ); + + if ( bungee.getPluginManager().callEvent( event ).isCancelled() ) + { + throw new CancelSendSignal(); + } + + if ( pluginMessage.tag.equals( "BungeeCord" ) ) + { + ByteArrayDataOutput out = ByteStreams.newDataOutput(); + String subChannel = in.readUTF(); + + if ( subChannel.equals( "Forward" ) ) + { + // Read data from server + String target = in.readUTF(); + String channel = in.readUTF(); + short len = in.readShort(); + byte[] data = new byte[ len ]; + in.readFully( data ); + + // Prepare new data to send + out.writeUTF( channel ); + out.writeShort( data.length ); + out.write( data ); + byte[] payload = out.toByteArray(); + + // Null out stream, important as we don't want to send to ourselves + out = null; + + if ( target.equals( "ALL" ) ) + { + for ( ServerInfo server : bungee.getServers().values() ) + { + if ( server != con.getServer().getInfo() ) + { + server.sendData( "BungeeCord", payload ); + } + } + } else + { + ServerInfo server = bungee.getServerInfo( target ); + if ( server != null ) + { + server.sendData( "BungeeCord", payload ); + } + } + } + if ( subChannel.equals( "Connect" ) ) + { + ServerInfo server = bungee.getServerInfo( in.readUTF() ); + if ( server != null ) + { + con.connect( server ); + } + } + if ( subChannel.equals( "IP" ) ) + { + out.writeUTF( "IP" ); + out.writeUTF( con.getAddress().getHostString() ); + out.writeInt( con.getAddress().getPort() ); + } + if ( subChannel.equals( "PlayerCount" ) ) + { + ServerInfo server = bungee.getServerInfo( in.readUTF() ); + if ( server != null ) + { + out.writeUTF( "PlayerCount" ); + out.writeUTF( server.getName() ); + out.writeInt( server.getPlayers().size() ); + } + } + if ( subChannel.equals( "PlayerList" ) ) + { + ServerInfo server = bungee.getServerInfo( in.readUTF() ); + if ( server != null ) + { + out.writeUTF( "PlayerList" ); + out.writeUTF( server.getName() ); + + StringBuilder sb = new StringBuilder(); + for ( ProxiedPlayer p : server.getPlayers() ) + { + sb.append( p.getName() ); + sb.append( "," ); + } + out.writeUTF( sb.substring( 0, sb.length() - 1 ) ); + } + } + if ( subChannel.equals( "GetServers" ) ) + { + out.writeUTF( "GetServers" ); + + StringBuilder sb = new StringBuilder(); + for ( String server : bungee.getServers().keySet() ) + { + sb.append( server ); + sb.append( "," ); + } + out.writeUTF( sb.substring( 0, sb.length() - 1 ) ); + } + if ( subChannel.equals( "Message" ) ) + { + ProxiedPlayer target = bungee.getPlayer( in.readUTF() ); + if ( target != null ) + { + target.sendMessage( in.readUTF() ); + } + } + + // Check we haven't set out to null, and we have written data, if so reply back back along the BungeeCord channel + if ( out != null ) + { + byte[] b = out.toByteArray(); + if ( b.length != 0 ) + { + con.getServer().sendData( "BungeeCord", b ); + } + } + } + } + + @Override + public void handle(PacketFFKick kick) throws Exception + { + con.disconnect( "[Kicked] " + kick.message ); + throw new CancelSendSignal(); + } + + @Override + public String toString() + { + return "[" + con.getName() + "] <-> DownstreamBridge <-> [" + server.getInfo().getName() + "]"; + } +} 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 new file mode 100644 index 00000000..08eae435 --- /dev/null +++ b/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java @@ -0,0 +1,249 @@ +package net.md_5.bungee.connection; + +import com.google.common.base.Preconditions; +import io.netty.channel.Channel; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.math.BigInteger; +import java.net.InetSocketAddress; +import java.net.URL; +import java.net.URLEncoder; +import java.security.MessageDigest; +import java.util.ArrayList; +import java.util.List; +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import net.md_5.bungee.BungeeCord; +import net.md_5.bungee.EncryptionUtil; +import net.md_5.bungee.UserConnection; +import net.md_5.bungee.Util; +import net.md_5.bungee.api.ChatColor; +import net.md_5.bungee.api.ProxyServer; +import net.md_5.bungee.api.ServerPing; +import net.md_5.bungee.api.config.ListenerInfo; +import net.md_5.bungee.api.config.ServerInfo; +import net.md_5.bungee.api.connection.PendingConnection; +import net.md_5.bungee.api.connection.ProxiedPlayer; +import net.md_5.bungee.api.event.LoginEvent; +import net.md_5.bungee.api.event.ProxyPingEvent; +import net.md_5.bungee.netty.CipherCodec; +import net.md_5.bungee.netty.HandlerBoss; +import net.md_5.bungee.netty.PacketDecoder; +import net.md_5.bungee.packet.Packet1Login; +import net.md_5.bungee.packet.Packet2Handshake; +import net.md_5.bungee.packet.PacketCDClientStatus; +import net.md_5.bungee.packet.PacketFAPluginMessage; +import net.md_5.bungee.packet.PacketFCEncryptionResponse; +import net.md_5.bungee.packet.PacketFDEncryptionRequest; +import net.md_5.bungee.packet.PacketFEPing; +import net.md_5.bungee.packet.PacketFFKick; +import net.md_5.bungee.packet.PacketHandler; +import net.md_5.bungee.protocol.PacketDefinitions; + +@RequiredArgsConstructor +public class InitialHandler extends PacketHandler implements PendingConnection +{ + + private final ProxyServer bungee; + private Channel ch; + @Getter + private final ListenerInfo listener; + private Packet1Login forgeLogin; + private Packet2Handshake handshake; + private PacketFDEncryptionRequest request; + private List loginMessages = new ArrayList<>(); + private State thisState = State.HANDSHAKE; + private static final PacketFAPluginMessage forgeMods = new PacketFAPluginMessage( "FML", new byte[] + { + 0, 0, 0, 0, 0, 2 + } ); + + private enum State + { + + HANDSHAKE, ENCRYPT, LOGIN, FINISHED; + } + + @Override + public void connected(Channel channel) throws Exception + { + this.ch = channel; + } + + @Override + public void handle(Packet1Login login) throws Exception + { + Preconditions.checkState( thisState == State.LOGIN, "Not expecting FORGE LOGIN" ); + Preconditions.checkState( forgeLogin == null, "Already received FORGE LOGIN" ); + forgeLogin = login; + + ch.pipeline().get( PacketDecoder.class ).setProtocol( PacketDefinitions.FORGE_PROTOCOL ); + } + + @Override + public void handle(PacketFAPluginMessage pluginMessage) throws Exception + { + loginMessages.add( pluginMessage ); + } + + @Override + public void handle(PacketFEPing ping) throws Exception + { + ServerPing response = new ServerPing( bungee.getProtocolVersion(), bungee.getGameVersion(), + listener.getMotd(), bungee.getPlayers().size(), listener.getMaxPlayers() ); + + response = bungee.getPluginManager().callEvent( new ProxyPingEvent( this, response ) ).getResponse(); + + String kickMessage = ChatColor.DARK_BLUE + + "\00" + response.getProtocolVersion() + + "\00" + response.getGameVersion() + + "\00" + response.getMotd() + + "\00" + response.getCurrentPlayers() + + "\00" + response.getMaxPlayers(); + disconnect( kickMessage ); + } + + @Override + public void handle(Packet2Handshake handshake) throws Exception + { + Preconditions.checkState( thisState == State.HANDSHAKE, "Not expecting HANDSHAKE" ); + Preconditions.checkArgument( handshake.username.length() <= 16, "Cannot have username longer than 16 characters" ); + this.handshake = handshake; + ch.write( forgeMods ); + ch.write( request = EncryptionUtil.encryptRequest() ); + thisState = State.ENCRYPT; + } + + @Override + public void handle(final PacketFCEncryptionResponse encryptResponse) throws Exception + { + Preconditions.checkState( thisState == State.ENCRYPT, "Not expecting ENCRYPT" ); + + // TODO: This is shit + new Thread( "Login Verifier - " + getName() ) + { + @Override + public void run() + { + try + { + SecretKey shared = EncryptionUtil.getSecret( encryptResponse, request ); + if ( BungeeCord.getInstance().config.isOnlineMode() ) + { + String reply = null; + try + { + String encName = URLEncoder.encode( InitialHandler.this.getName(), "UTF-8" ); + + MessageDigest sha = MessageDigest.getInstance( "SHA-1" ); + for ( byte[] bit : new byte[][] + { + request.serverId.getBytes( "ISO_8859_1" ), shared.getEncoded(), EncryptionUtil.keys.getPublic().getEncoded() + } ) + { + sha.update( bit ); + } + + String encodedHash = URLEncoder.encode( new BigInteger( sha.digest() ).toString( 16 ), "UTF-8" ); + String authURL = "http://session.minecraft.net/game/checkserver.jsp?user=" + encName + "&serverId=" + encodedHash; + + try ( BufferedReader in = new BufferedReader( new InputStreamReader( new URL( authURL ).openStream() ) ) ) + { + reply = in.readLine(); + } + } catch ( IOException ex ) + { + } + + if ( !"YES".equals( reply ) ) + { + disconnect( "Not authenticated with Minecraft.net" ); + } + + // Check for multiple connections + ProxiedPlayer old = bungee.getPlayer( handshake.username ); + if ( old != null ) + { + old.disconnect( "You are already connected to the server" ); + } + + // fire login event + LoginEvent event = new LoginEvent( InitialHandler.this ); + if ( bungee.getPluginManager().callEvent( event ).isCancelled() ) + { + disconnect( event.getCancelReason() ); + } + } + + Cipher encrypt = EncryptionUtil.getCipher( Cipher.ENCRYPT_MODE, shared ); + Cipher decrypt = EncryptionUtil.getCipher( Cipher.DECRYPT_MODE, shared ); + ch.write( new PacketFCEncryptionResponse() ); + ch.pipeline().addBefore( "decoder", "cipher", new CipherCodec( encrypt, decrypt ) ); + + thisState = InitialHandler.State.LOGIN; + } catch ( Throwable t ) + { + disconnect( "[Report to md_5 / Server Owner] " + Util.exception( t ) ); + } + } + }.start(); + } + + @Override + public void handle(PacketCDClientStatus clientStatus) throws Exception + { + Preconditions.checkState( thisState == State.LOGIN, "Not expecting LOGIN" ); + + UserConnection userCon = new UserConnection( (BungeeCord) bungee, ch, this, handshake, forgeLogin, loginMessages ); + ch.pipeline().get( HandlerBoss.class ).setHandler( new UpstreamBridge( bungee, userCon ) ); + + ServerInfo server = bungee.getReconnectHandler().getServer( userCon ); + userCon.connect( server, true ); + + thisState = State.FINISHED; + throw new CancelSendSignal(); + } + + @Override + public synchronized void disconnect(String reason) + { + if ( ch.isActive() ) + { + ch.write( new PacketFFKick( reason ) ); + ch.close(); + } + } + + @Override + public String getName() + { + return ( handshake == null ) ? null : handshake.username; + } + + @Override + public byte getVersion() + { + return ( handshake == null ) ? -1 : handshake.procolVersion; + } + + @Override + public InetSocketAddress getVirtualHost() + { + return ( handshake == null ) ? null : new InetSocketAddress( handshake.host, handshake.port ); + } + + @Override + public InetSocketAddress getAddress() + { + return (InetSocketAddress) ch.remoteAddress(); + } + + @Override + public String toString() + { + return "[" + ( ( getName() != null ) ? getName() : getAddress() ) + "] <-> InitialHandler"; + } +} diff --git a/proxy/src/main/java/net/md_5/bungee/connection/PingHandler.java b/proxy/src/main/java/net/md_5/bungee/connection/PingHandler.java new file mode 100644 index 00000000..3ad97380 --- /dev/null +++ b/proxy/src/main/java/net/md_5/bungee/connection/PingHandler.java @@ -0,0 +1,49 @@ +package net.md_5.bungee.connection; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.Channel; +import lombok.RequiredArgsConstructor; +import net.md_5.bungee.api.Callback; +import net.md_5.bungee.api.ServerPing; +import net.md_5.bungee.api.config.ServerInfo; +import net.md_5.bungee.packet.PacketFFKick; +import net.md_5.bungee.packet.PacketHandler; + +@RequiredArgsConstructor +public class PingHandler extends PacketHandler +{ + + private final ServerInfo target; + private final Callback callback; + private static final ByteBuf pingBuf = Unpooled.wrappedBuffer( new byte[] + { + (byte) 0xFE, (byte) 0x01 + } ); + + @Override + public void connected(Channel channel) throws Exception + { + channel.write( pingBuf ); + } + + @Override + public void exception(Throwable t) throws Exception + { + callback.done( null, t ); + } + + @Override + public void handle(PacketFFKick kick) throws Exception + { + String[] split = kick.message.split( "\00" ); + ServerPing ping = new ServerPing( Byte.parseByte( split[1] ), split[2], split[3], Integer.parseInt( split[4] ), Integer.parseInt( split[5] ) ); + callback.done( ping, null ); + } + + @Override + public String toString() + { + return "[Ping Handler] -> " + target.getName(); + } +} diff --git a/proxy/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java b/proxy/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java new file mode 100644 index 00000000..b503116d --- /dev/null +++ b/proxy/src/main/java/net/md_5/bungee/connection/UpstreamBridge.java @@ -0,0 +1,106 @@ +package net.md_5.bungee.connection; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.Channel; +import lombok.RequiredArgsConstructor; +import net.md_5.bungee.EntityMap; +import net.md_5.bungee.UserConnection; +import net.md_5.bungee.Util; +import net.md_5.bungee.api.ProxyServer; +import net.md_5.bungee.api.event.ChatEvent; +import net.md_5.bungee.api.event.PlayerDisconnectEvent; +import net.md_5.bungee.api.event.PluginMessageEvent; +import net.md_5.bungee.packet.Packet0KeepAlive; +import net.md_5.bungee.packet.Packet3Chat; +import net.md_5.bungee.packet.PacketFAPluginMessage; +import net.md_5.bungee.packet.PacketHandler; + +@RequiredArgsConstructor +public class UpstreamBridge extends PacketHandler +{ + + private final ProxyServer bungee; + private final UserConnection con; + + @Override + public void exception(Throwable t) throws Exception + { + con.disconnect( Util.exception( t ) ); + } + + @Override + public void disconnected(Channel channel) throws Exception + { + // We lost connection to the client + PlayerDisconnectEvent event = new PlayerDisconnectEvent( con ); + bungee.getPluginManager().callEvent( event ); + bungee.getTabListHandler().onDisconnect( con ); + bungee.getPlayers().remove( con ); + + if ( con.getServer() != null ) + { + con.getServer().disconnect( "Quitting" ); + } + } + + @Override + public void handle(ByteBuf buf) throws Exception + { + EntityMap.rewrite( buf, con.clientEntityId, con.serverEntityId ); + if ( con.getServer() != null ) + { + con.getServer().getCh().write( buf ); + } + } + + @Override + public void handle(Packet0KeepAlive alive) throws Exception + { + if ( alive.id == con.trackingPingId ) + { + int newPing = (int) ( System.currentTimeMillis() - con.pingTime ); + bungee.getTabListHandler().onPingChange( con, newPing ); + con.setPing( newPing ); + } + } + + @Override + public void handle(Packet3Chat chat) throws Exception + { + if ( chat.message.charAt( 0 ) == '/' ) + { + if ( bungee.getPluginManager().dispatchCommand( con, chat.message.substring( 1 ) ) ) + { + throw new CancelSendSignal(); + } + } else + { + ChatEvent chatEvent = new ChatEvent( con, con.getServer(), chat.message ); + if ( bungee.getPluginManager().callEvent( chatEvent ).isCancelled() ) + { + throw new CancelSendSignal(); + } + } + } + + @Override + public void handle(PacketFAPluginMessage pluginMessage) throws Exception + { + if ( pluginMessage.tag.equals( "BungeeCord" ) ) + { + throw new CancelSendSignal(); + } + + PluginMessageEvent event = new PluginMessageEvent( con, con.getServer(), pluginMessage.tag, pluginMessage.data.clone() ); + if ( bungee.getPluginManager().callEvent( event ).isCancelled() ) + { + throw new CancelSendSignal(); + } + } + + @Override + public String toString() + { + return "[" + con.getName() + "] -> UpstreamBridge"; + } +} diff --git a/proxy/src/main/java/net/md_5/bungee/netty/CipherCodec.java b/proxy/src/main/java/net/md_5/bungee/netty/CipherCodec.java new file mode 100644 index 00000000..a67ab0da --- /dev/null +++ b/proxy/src/main/java/net/md_5/bungee/netty/CipherCodec.java @@ -0,0 +1,50 @@ +package net.md_5.bungee.netty; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.ByteToByteCodec; +import javax.crypto.Cipher; +import javax.crypto.ShortBufferException; + +/** + * This class is a complete solution for encrypting and decoding bytes in a + * Netty stream. It takes two {@link Cipher} instances, used for encryption and + * decryption respectively. + */ +public class CipherCodec extends ByteToByteCodec +{ + + private Cipher encrypt; + private Cipher decrypt; + + public CipherCodec(Cipher encrypt, Cipher decrypt) + { + this.encrypt = encrypt; + this.decrypt = decrypt; + } + + @Override + public void encode(ChannelHandlerContext ctx, ByteBuf in, ByteBuf out) throws Exception + { + cipher( encrypt, in, out ); + } + + @Override + public void decode(ChannelHandlerContext ctx, ByteBuf in, ByteBuf out) throws Exception + { + cipher( decrypt, in, out ); + } + + private void cipher(Cipher cipher, ByteBuf in, ByteBuf out) throws ShortBufferException + { + int available = in.readableBytes(); + int outputSize = cipher.getOutputSize( available ); + if ( out.capacity() < outputSize ) + { + out.capacity( outputSize ); + } + int processed = cipher.update( in.nioBuffer(), out.nioBuffer( out.readerIndex(), outputSize ) ); + in.readerIndex( in.readerIndex() + processed ); + out.writerIndex( out.writerIndex() + processed ); + } +} 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 new file mode 100644 index 00000000..01c031cd --- /dev/null +++ b/proxy/src/main/java/net/md_5/bungee/netty/HandlerBoss.java @@ -0,0 +1,89 @@ +package net.md_5.bungee.netty; + +import com.google.common.base.Preconditions; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundMessageHandlerAdapter; +import io.netty.handler.timeout.ReadTimeoutException; +import java.util.logging.Level; +import net.md_5.bungee.api.ProxyServer; +import net.md_5.bungee.connection.CancelSendSignal; +import net.md_5.bungee.packet.DefinedPacket; +import net.md_5.bungee.packet.PacketHandler; + +/** + * This class is a primitive wrapper for {@link PacketHandler} instances tied to + * channels to maintain simple states, and only call the required, adapted + * methods when the channel is connected. + */ +public class HandlerBoss extends ChannelInboundMessageHandlerAdapter +{ + + private PacketHandler handler; + + public void setHandler(PacketHandler handler) + { + Preconditions.checkArgument( handler != null, "handler" ); + this.handler = handler; + } + + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception + { + if ( handler != null ) + { + handler.connected( ctx.channel() ); + ProxyServer.getInstance().getLogger().log( Level.INFO, "{0} has connected", handler ); + } + } + + @Override + public void channelInactive(ChannelHandlerContext ctx) throws Exception + { + if ( handler != null ) + { + ProxyServer.getInstance().getLogger().log( Level.INFO, "{0} has disconnected", handler ); + handler.disconnected( ctx.channel() ); + } + } + + @Override + public void messageReceived(ChannelHandlerContext ctx, ByteBuf msg) throws Exception + { + if ( handler != null && ctx.channel().isActive() ) + { + DefinedPacket packet = DefinedPacket.packet( msg ); + boolean sendPacket = true; + if ( packet != null ) + { + try + { + packet.handle( handler ); + } catch ( CancelSendSignal ex ) + { + sendPacket = false; + } + } + if ( sendPacket ) + { + handler.handle( msg ); + } + } + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception + { + if ( ctx.channel().isActive() ) + { + if ( cause instanceof ReadTimeoutException ) + { + ProxyServer.getInstance().getLogger().log( Level.WARNING, handler + " - read timed out" ); + } else + { + ProxyServer.getInstance().getLogger().log( Level.SEVERE, handler + " - encountered exception", cause ); + } + ctx.close(); + } + } +} diff --git a/proxy/src/main/java/net/md_5/bungee/netty/PacketDecoder.java b/proxy/src/main/java/net/md_5/bungee/netty/PacketDecoder.java new file mode 100644 index 00000000..696f0148 --- /dev/null +++ b/proxy/src/main/java/net/md_5/bungee/netty/PacketDecoder.java @@ -0,0 +1,34 @@ +package net.md_5.bungee.netty; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.ReplayingDecoder; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; +import net.md_5.bungee.protocol.netty.PacketReader; + +/** + * This class will attempt to read a packet from {@link PacketReader}, with the + * specified {@link #protocol} before returning a new {@link ByteBuf} with the + * copied contents of all bytes read in this frame. + *

+ * It is based on {@link ReplayingDecoder} so that packets will only be returned + * when all needed data is present. + */ +@AllArgsConstructor +public class PacketDecoder extends ReplayingDecoder +{ + + @Getter + @Setter + private int protocol; + + @Override + protected ByteBuf decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception + { + int startIndex = in.readerIndex(); + PacketReader.readPacket( in, protocol ); + return in.copy( startIndex, in.readerIndex() - startIndex ); + } +} 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 new file mode 100644 index 00000000..0e128da1 --- /dev/null +++ b/proxy/src/main/java/net/md_5/bungee/netty/PipelineUtils.java @@ -0,0 +1,65 @@ +package net.md_5.bungee.netty; + +import io.netty.channel.Channel; +import io.netty.channel.ChannelConfig; +import io.netty.channel.ChannelException; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelOption; +import io.netty.handler.timeout.ReadTimeoutHandler; +import io.netty.util.AttributeKey; +import java.util.concurrent.TimeUnit; +import net.md_5.bungee.BungeeCord; +import net.md_5.bungee.ServerConnector; +import net.md_5.bungee.UserConnection; +import net.md_5.bungee.connection.InitialHandler; +import net.md_5.bungee.api.ProxyServer; +import net.md_5.bungee.api.config.ListenerInfo; +import net.md_5.bungee.api.config.ServerInfo; +import net.md_5.bungee.protocol.PacketDefinitions; + +public class PipelineUtils +{ + + public static final AttributeKey LISTENER = new AttributeKey<>( "ListerInfo" ); + public static final AttributeKey USER = new AttributeKey<>( "User" ); + public static final AttributeKey TARGET = new AttributeKey<>( "Target" ); + public static final ChannelInitializer SERVER_CHILD = new ChannelInitializer() + { + @Override + protected void initChannel(Channel ch) throws Exception + { + BASE.initChannel( ch ); + ch.pipeline().get( HandlerBoss.class ).setHandler( new InitialHandler( ProxyServer.getInstance(), ch.attr( LISTENER ).get() ) ); + ch.config().setDefaultHandlerByteBufType( ChannelConfig.ChannelHandlerByteBufType.HEAP ); + } + }; + public static final ChannelInitializer CLIENT = new ChannelInitializer() + { + @Override + protected void initChannel(Channel ch) throws Exception + { + BASE.initChannel( ch ); + 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(); + + public final static class Base extends ChannelInitializer + { + + @Override + public void initChannel(Channel ch) throws Exception + { + try + { + ch.config().setOption( ChannelOption.IP_TOS, 0x18 ); + } catch ( ChannelException ex ) + { + // IP_TOS is not supported (Windows XP / Windows Server 2003) + } + ch.pipeline().addLast( "timer", new ReadTimeoutHandler( BungeeCord.getInstance().config.getTimeout(), TimeUnit.MILLISECONDS ) ); + ch.pipeline().addLast( "decoder", new PacketDecoder( PacketDefinitions.VANILLA_PROTOCOL ) ); + ch.pipeline().addLast( "handler", new HandlerBoss() ); + } + }; +} diff --git a/proxy/src/main/java/net/md_5/bungee/packet/DefinedPacket.java b/proxy/src/main/java/net/md_5/bungee/packet/DefinedPacket.java index d7c20cce..e8d0914c 100644 --- a/proxy/src/main/java/net/md_5/bungee/packet/DefinedPacket.java +++ b/proxy/src/main/java/net/md_5/bungee/packet/DefinedPacket.java @@ -1,12 +1,8 @@ package net.md_5.bungee.packet; -import com.google.common.base.Preconditions; -import com.google.common.io.ByteArrayDataOutput; -import com.google.common.io.ByteStreams; -import java.io.ByteArrayInputStream; -import java.io.DataInputStream; -import java.io.DataOutput; -import java.io.IOException; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ReferenceCounted; +import io.netty.buffer.Unpooled; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import lombok.Delegate; @@ -17,67 +13,40 @@ import net.md_5.bungee.Util; * subclasses can read and write to the backing byte array which can be * retrieved via the {@link #getPacket()} method. */ -public abstract class DefinedPacket implements DataOutput +public abstract class DefinedPacket implements ByteBuf { - private interface Overriden + @Delegate(types = { + ByteBuf.class, ReferenceCounted.class + }) + private ByteBuf buf; - void readUTF(); - - void writeUTF(String s); - } - private ByteArrayInputStream bin; - private DataInputStream input; - @Delegate(excludes = Overriden.class) - private ByteArrayDataOutput out; - /** - * Packet id. - */ - public final int id; - /** - * Already constructed packet. - */ - private byte[] packet; - - public DefinedPacket(int id, byte[] buf) + public DefinedPacket(int id, ByteBuf buf) { - bin = new ByteArrayInputStream( buf ); - input = new DataInputStream( bin ); + this.buf = buf; if ( readUnsignedByte() != id ) { throw new IllegalArgumentException( "Wasn't expecting packet id " + Util.hex( id ) ); } - this.id = id; - packet = buf; } public DefinedPacket(int id) { - out = ByteStreams.newDataOutput(); - this.id = id; + buf = Unpooled.buffer(); writeByte( id ); } - /** - * Gets the bytes that make up this packet. - * - * @return the bytes which make up this packet, either the original byte - * array or the newly written one. - */ - public byte[] getPacket() - { - return packet == null ? packet = out.toByteArray() : packet; - } - - @Override - public void writeUTF(String s) + public void writeString(String s) { writeShort( s.length() ); - writeChars( s ); + for ( char c : s.toCharArray() ) + { + writeChar( c ); + } } - public String readUTF() + public String readString() { short len = readShort(); char[] chars = new char[ len ]; @@ -91,99 +60,17 @@ public abstract class DefinedPacket implements DataOutput public void writeArray(byte[] b) { writeShort( b.length ); - write( b ); + writeBytes( b ); } public byte[] readArray() { short len = readShort(); byte[] ret = new byte[ len ]; - readFully( ret ); + readBytes( ret ); return ret; } - public final int available() - { - return bin.available(); - } - - public final void readFully(byte b[]) - { - try - { - input.readFully( b ); - } catch ( IOException e ) - { - throw new IllegalStateException( e ); - } - } - - public final boolean readBoolean() - { - try - { - return input.readBoolean(); - } catch ( IOException e ) - { - throw new IllegalStateException( e ); - } - } - - public final byte readByte() - { - try - { - return input.readByte(); - } catch ( IOException e ) - { - throw new IllegalStateException( e ); - } - } - - public final int readUnsignedByte() - { - try - { - return input.readUnsignedByte(); - } catch ( IOException e ) - { - throw new IllegalStateException( e ); - } - } - - public final short readShort() - { - try - { - return input.readShort(); - } catch ( IOException e ) - { - throw new IllegalStateException( e ); - } - } - - public final char readChar() - { - try - { - return input.readChar(); - } catch ( IOException e ) - { - throw new IllegalStateException( e ); - } - } - - public final int readInt() - { - try - { - return input.readInt(); - } catch ( IOException e ) - { - throw new IllegalStateException( e ); - } - } - @Override public abstract boolean equals(Object obj); @@ -194,28 +81,38 @@ public abstract class DefinedPacket implements DataOutput public abstract String toString(); public abstract void handle(PacketHandler handler) throws Exception; + @SuppressWarnings("unchecked") private static Class[] classes = new Class[ 256 ]; + @SuppressWarnings("unchecked") + private static Constructor[] consructors = new Constructor[ 256 ]; - public static DefinedPacket packet(byte[] buf) + public static DefinedPacket packet(ByteBuf buf) { - int id = Util.getId( buf ); + short id = buf.getUnsignedByte( 0 ); Class clazz = classes[id]; DefinedPacket ret = null; if ( clazz != null ) { try { - Constructor constructor = clazz.getDeclaredConstructor( byte[].class ); + Constructor constructor = consructors[id]; + if ( constructor == null ) + { + constructor = clazz.getDeclaredConstructor( ByteBuf.class ); + consructors[id] = constructor; + } + if ( constructor != null ) { + buf.markReaderIndex(); ret = constructor.newInstance( buf ); + buf.resetReaderIndex(); } } catch ( IllegalAccessException | InstantiationException | InvocationTargetException | NoSuchMethodException ex ) { } } - Preconditions.checkState( ret != null, "Don't know how to deal with packet ID %s", Util.hex( id ) ); return ret; } diff --git a/proxy/src/main/java/net/md_5/bungee/packet/Packet0KeepAlive.java b/proxy/src/main/java/net/md_5/bungee/packet/Packet0KeepAlive.java index 310d99ed..e8e77623 100644 --- a/proxy/src/main/java/net/md_5/bungee/packet/Packet0KeepAlive.java +++ b/proxy/src/main/java/net/md_5/bungee/packet/Packet0KeepAlive.java @@ -1,5 +1,6 @@ package net.md_5.bungee.packet; +import io.netty.buffer.ByteBuf; import lombok.EqualsAndHashCode; import lombok.ToString; @@ -10,9 +11,9 @@ public class Packet0KeepAlive extends DefinedPacket public int id; - public Packet0KeepAlive(byte[] buffer) + Packet0KeepAlive(ByteBuf buf) { - super( 0x00, buffer ); + super( 0x00, buf ); id = readInt(); } diff --git a/proxy/src/main/java/net/md_5/bungee/packet/Packet1Login.java b/proxy/src/main/java/net/md_5/bungee/packet/Packet1Login.java index 96285c8a..d8bb34bd 100644 --- a/proxy/src/main/java/net/md_5/bungee/packet/Packet1Login.java +++ b/proxy/src/main/java/net/md_5/bungee/packet/Packet1Login.java @@ -1,5 +1,6 @@ package net.md_5.bungee.packet; +import io.netty.buffer.ByteBuf; import lombok.EqualsAndHashCode; import lombok.ToString; @@ -20,24 +21,31 @@ public class Packet1Login extends DefinedPacket { super( 0x01 ); writeInt( entityId ); - writeUTF( levelType ); + writeString( levelType ); writeByte( gameMode ); writeByte( dimension ); writeByte( difficulty ); writeByte( unused ); writeByte( maxPlayers ); + this.entityId = entityId; + this.levelType = levelType; + this.gameMode = gameMode; + this.dimension = dimension; + this.difficulty = difficulty; + this.unused = unused; + this.maxPlayers = maxPlayers; } - public Packet1Login(byte[] buf) + Packet1Login(ByteBuf buf) { super( 0x01, buf ); this.entityId = readInt(); - this.levelType = readUTF(); + this.levelType = readString(); this.gameMode = readByte(); - if ( available() == 4 ) + if ( readableBytes() == 4 ) { this.dimension = readByte(); - } else if ( available() == 7 ) + } else if ( readableBytes() == 7 ) { this.dimension = readInt(); } else diff --git a/proxy/src/main/java/net/md_5/bungee/packet/Packet2Handshake.java b/proxy/src/main/java/net/md_5/bungee/packet/Packet2Handshake.java index e739880e..0908a8db 100644 --- a/proxy/src/main/java/net/md_5/bungee/packet/Packet2Handshake.java +++ b/proxy/src/main/java/net/md_5/bungee/packet/Packet2Handshake.java @@ -1,5 +1,6 @@ package net.md_5.bungee.packet; +import io.netty.buffer.ByteBuf; import lombok.EqualsAndHashCode; import lombok.ToString; @@ -17,17 +18,21 @@ public class Packet2Handshake extends DefinedPacket { super( 0x02 ); writeByte( protocolVersion ); - writeUTF( username ); - writeUTF( host ); + writeString( username ); + writeString( host ); writeInt( port ); + this.procolVersion = protocolVersion; + this.username = username; + this.host = host; + this.port = port; } - public Packet2Handshake(byte[] buf) + Packet2Handshake(ByteBuf buf) { super( 0x02, buf ); this.procolVersion = readByte(); - this.username = readUTF(); - this.host = readUTF(); + this.username = readString(); + this.host = readString(); this.port = readInt(); } diff --git a/proxy/src/main/java/net/md_5/bungee/packet/Packet3Chat.java b/proxy/src/main/java/net/md_5/bungee/packet/Packet3Chat.java index 7b0a1525..f9de2991 100644 --- a/proxy/src/main/java/net/md_5/bungee/packet/Packet3Chat.java +++ b/proxy/src/main/java/net/md_5/bungee/packet/Packet3Chat.java @@ -1,5 +1,6 @@ package net.md_5.bungee.packet; +import io.netty.buffer.ByteBuf; import lombok.EqualsAndHashCode; import lombok.ToString; @@ -13,13 +14,14 @@ public class Packet3Chat extends DefinedPacket public Packet3Chat(String message) { super( 0x03 ); - writeUTF( message ); + writeString( message ); + this.message = message; } - public Packet3Chat(byte[] buf) + Packet3Chat(ByteBuf buf) { super( 0x03, buf ); - this.message = readUTF(); + this.message = readString(); } @Override diff --git a/proxy/src/main/java/net/md_5/bungee/packet/Packet9Respawn.java b/proxy/src/main/java/net/md_5/bungee/packet/Packet9Respawn.java index 2619d3f8..49aa371d 100644 --- a/proxy/src/main/java/net/md_5/bungee/packet/Packet9Respawn.java +++ b/proxy/src/main/java/net/md_5/bungee/packet/Packet9Respawn.java @@ -1,5 +1,6 @@ package net.md_5.bungee.packet; +import io.netty.buffer.ByteBuf; import lombok.EqualsAndHashCode; import lombok.ToString; @@ -8,6 +9,8 @@ import lombok.ToString; public class Packet9Respawn extends DefinedPacket { + public static final Packet9Respawn DIM1_SWITCH = new Packet9Respawn( (byte) 1, (byte) 0, (byte) 0, (short) 256, "DEFAULT" ); + public static final Packet9Respawn DIM2_SWITCH = new Packet9Respawn( (byte) -1, (byte) 0, (byte) 0, (short) 256, "DEFAULT" ); public int dimension; public byte difficulty; public byte gameMode; @@ -21,17 +24,22 @@ public class Packet9Respawn extends DefinedPacket writeByte( difficulty ); writeByte( gameMode ); writeShort( worldHeight ); - writeUTF( levelType ); + writeString( levelType ); + this.dimension = dimension; + this.difficulty = difficulty; + this.gameMode = gameMode; + this.worldHeight = worldHeight; + this.levelType = levelType; } - public Packet9Respawn(byte[] buf) + Packet9Respawn(ByteBuf buf) { super( 0x09, buf ); this.dimension = readInt(); this.difficulty = readByte(); this.gameMode = readByte(); this.worldHeight = readShort(); - this.levelType = readUTF(); + this.levelType = readString(); } @Override diff --git a/proxy/src/main/java/net/md_5/bungee/packet/PacketC9PlayerListItem.java b/proxy/src/main/java/net/md_5/bungee/packet/PacketC9PlayerListItem.java index 2ec61aa0..13ba4694 100644 --- a/proxy/src/main/java/net/md_5/bungee/packet/PacketC9PlayerListItem.java +++ b/proxy/src/main/java/net/md_5/bungee/packet/PacketC9PlayerListItem.java @@ -1,5 +1,6 @@ package net.md_5.bungee.packet; +import io.netty.buffer.ByteBuf; import lombok.EqualsAndHashCode; import lombok.ToString; @@ -12,10 +13,10 @@ public class PacketC9PlayerListItem extends DefinedPacket public boolean online; public int ping; - public PacketC9PlayerListItem(byte[] packet) + PacketC9PlayerListItem(ByteBuf buf) { - super( 0xC9, packet ); - username = readUTF(); + super( 0xC9, buf ); + username = readString(); online = readBoolean(); ping = readShort(); } @@ -23,7 +24,7 @@ public class PacketC9PlayerListItem extends DefinedPacket public PacketC9PlayerListItem(String username, boolean online, int ping) { super( 0xC9 ); - writeUTF( username ); + writeString( username ); writeBoolean( online ); writeShort( ping ); } diff --git a/proxy/src/main/java/net/md_5/bungee/packet/PacketCDClientStatus.java b/proxy/src/main/java/net/md_5/bungee/packet/PacketCDClientStatus.java index 08bdc103..1084c9ad 100644 --- a/proxy/src/main/java/net/md_5/bungee/packet/PacketCDClientStatus.java +++ b/proxy/src/main/java/net/md_5/bungee/packet/PacketCDClientStatus.java @@ -1,5 +1,6 @@ package net.md_5.bungee.packet; +import io.netty.buffer.ByteBuf; import lombok.EqualsAndHashCode; import lombok.ToString; @@ -8,6 +9,10 @@ import lombok.ToString; public class PacketCDClientStatus extends DefinedPacket { + /** + * Represents the packet the client sends to the server when it is ready to + * login. + */ public static PacketCDClientStatus CLIENT_LOGIN = new PacketCDClientStatus( (byte) 0 ); /** @@ -21,7 +26,7 @@ public class PacketCDClientStatus extends DefinedPacket writeByte( payload ); } - public PacketCDClientStatus(byte[] buf) + PacketCDClientStatus(ByteBuf buf) { super( 0xCD, buf ); } diff --git a/proxy/src/main/java/net/md_5/bungee/packet/PacketFAPluginMessage.java b/proxy/src/main/java/net/md_5/bungee/packet/PacketFAPluginMessage.java index e6f7dd97..d7cc5ee0 100644 --- a/proxy/src/main/java/net/md_5/bungee/packet/PacketFAPluginMessage.java +++ b/proxy/src/main/java/net/md_5/bungee/packet/PacketFAPluginMessage.java @@ -1,5 +1,6 @@ package net.md_5.bungee.packet; +import io.netty.buffer.ByteBuf; import lombok.EqualsAndHashCode; import lombok.ToString; @@ -14,16 +15,16 @@ public class PacketFAPluginMessage extends DefinedPacket public PacketFAPluginMessage(String tag, byte[] data) { super( 0xFA ); - writeUTF( tag ); + writeString( tag ); writeArray( data ); this.tag = tag; this.data = data; } - public PacketFAPluginMessage(byte[] buf) + PacketFAPluginMessage(ByteBuf buf) { super( 0xFA, buf ); - this.tag = readUTF(); + this.tag = readString(); this.data = readArray(); } diff --git a/proxy/src/main/java/net/md_5/bungee/packet/PacketFCEncryptionResponse.java b/proxy/src/main/java/net/md_5/bungee/packet/PacketFCEncryptionResponse.java index 0abd9d08..f98d7e7d 100644 --- a/proxy/src/main/java/net/md_5/bungee/packet/PacketFCEncryptionResponse.java +++ b/proxy/src/main/java/net/md_5/bungee/packet/PacketFCEncryptionResponse.java @@ -1,5 +1,6 @@ package net.md_5.bungee.packet; +import io.netty.buffer.ByteBuf; import lombok.EqualsAndHashCode; import lombok.ToString; @@ -23,9 +24,11 @@ public class PacketFCEncryptionResponse extends DefinedPacket super( 0xFC ); writeArray( sharedSecret ); writeArray( verifyToken ); + this.sharedSecret = sharedSecret; + this.verifyToken = verifyToken; } - public PacketFCEncryptionResponse(byte[] buf) + PacketFCEncryptionResponse(ByteBuf buf) { super( 0xFC, buf ); this.sharedSecret = readArray(); diff --git a/proxy/src/main/java/net/md_5/bungee/packet/PacketFDEncryptionRequest.java b/proxy/src/main/java/net/md_5/bungee/packet/PacketFDEncryptionRequest.java index 886e1ad6..a6df1164 100644 --- a/proxy/src/main/java/net/md_5/bungee/packet/PacketFDEncryptionRequest.java +++ b/proxy/src/main/java/net/md_5/bungee/packet/PacketFDEncryptionRequest.java @@ -1,5 +1,6 @@ package net.md_5.bungee.packet; +import io.netty.buffer.ByteBuf; import lombok.EqualsAndHashCode; import lombok.ToString; @@ -15,7 +16,7 @@ public class PacketFDEncryptionRequest extends DefinedPacket public PacketFDEncryptionRequest(String serverId, byte[] publicKey, byte[] verifyToken) { super( 0xFD ); - writeUTF( serverId ); + writeString( serverId ); writeArray( publicKey ); writeArray( verifyToken ); this.serverId = serverId; @@ -23,10 +24,10 @@ public class PacketFDEncryptionRequest extends DefinedPacket this.verifyToken = verifyToken; } - public PacketFDEncryptionRequest(byte[] buf) + PacketFDEncryptionRequest(ByteBuf buf) { super( 0xFD, buf ); - serverId = readUTF(); + serverId = readString(); publicKey = readArray(); verifyToken = readArray(); } diff --git a/proxy/src/main/java/net/md_5/bungee/packet/PacketFEPing.java b/proxy/src/main/java/net/md_5/bungee/packet/PacketFEPing.java index 1838a445..1df353dc 100644 --- a/proxy/src/main/java/net/md_5/bungee/packet/PacketFEPing.java +++ b/proxy/src/main/java/net/md_5/bungee/packet/PacketFEPing.java @@ -1,5 +1,6 @@ package net.md_5.bungee.packet; +import io.netty.buffer.ByteBuf; import lombok.EqualsAndHashCode; import lombok.ToString; @@ -8,9 +9,12 @@ import lombok.ToString; public class PacketFEPing extends DefinedPacket { - public PacketFEPing(byte[] buffer) + public byte version; + + PacketFEPing(ByteBuf buffer) { super( 0xFE, buffer ); + version = readByte(); } @Override diff --git a/proxy/src/main/java/net/md_5/bungee/packet/PacketFFKick.java b/proxy/src/main/java/net/md_5/bungee/packet/PacketFFKick.java index d6d0f584..d7d80b74 100644 --- a/proxy/src/main/java/net/md_5/bungee/packet/PacketFFKick.java +++ b/proxy/src/main/java/net/md_5/bungee/packet/PacketFFKick.java @@ -1,5 +1,6 @@ package net.md_5.bungee.packet; +import io.netty.buffer.ByteBuf; import lombok.EqualsAndHashCode; import lombok.ToString; @@ -13,13 +14,13 @@ public class PacketFFKick extends DefinedPacket public PacketFFKick(String message) { super( 0xFF ); - writeUTF( message ); + writeString( message ); } - public PacketFFKick(byte[] buf) + PacketFFKick(ByteBuf buf) { super( 0xFF, buf ); - this.message = readUTF(); + this.message = readString(); } @Override diff --git a/proxy/src/main/java/net/md_5/bungee/packet/PacketHandler.java b/proxy/src/main/java/net/md_5/bungee/packet/PacketHandler.java index 04184256..f64dfd9a 100644 --- a/proxy/src/main/java/net/md_5/bungee/packet/PacketHandler.java +++ b/proxy/src/main/java/net/md_5/bungee/packet/PacketHandler.java @@ -1,70 +1,75 @@ package net.md_5.bungee.packet; +import io.netty.buffer.ByteBuf; +import io.netty.channel.Channel; + public abstract class PacketHandler { - private void nop(DefinedPacket packet) + @Override + public abstract String toString(); + + public void connected(Channel channel) throws Exception + { + } + + public void disconnected(Channel channel) throws Exception + { + } + + public void exception(Throwable t) throws Exception + { + } + + public void handle(ByteBuf buf) throws Exception { - throw new UnsupportedOperationException( "No handler defined for packet " + packet.getClass() ); } public void handle(Packet0KeepAlive alive) throws Exception { - nop( alive ); } public void handle(Packet1Login login) throws Exception { - nop( login ); } public void handle(Packet2Handshake handshake) throws Exception { - nop( handshake ); } public void handle(Packet3Chat chat) throws Exception { - nop( chat ); } public void handle(Packet9Respawn respawn) throws Exception { - nop( respawn ); } public void handle(PacketC9PlayerListItem playerList) throws Exception { - nop( playerList ); } public void handle(PacketCDClientStatus clientStatus) throws Exception { - nop( clientStatus ); } public void handle(PacketFAPluginMessage pluginMessage) throws Exception { - nop( pluginMessage ); } public void handle(PacketFCEncryptionResponse encryptResponse) throws Exception { - nop( encryptResponse ); } public void handle(PacketFDEncryptionRequest encryptRequest) throws Exception { - nop( encryptRequest ); } public void handle(PacketFEPing ping) throws Exception { - nop( ping ); } public void handle(PacketFFKick kick) throws Exception { - nop( kick ); } } diff --git a/proxy/src/main/java/net/md_5/bungee/packet/PacketStream.java b/proxy/src/main/java/net/md_5/bungee/packet/PacketStream.java deleted file mode 100644 index 01413568..00000000 --- a/proxy/src/main/java/net/md_5/bungee/packet/PacketStream.java +++ /dev/null @@ -1,101 +0,0 @@ -package net.md_5.bungee.packet; - -import java.io.ByteArrayOutputStream; -import java.io.DataInputStream; -import java.io.FilterInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import lombok.Getter; -import lombok.Setter; -import net.md_5.mendax.datainput.DataInputPacketReader; - -/** - * A specialized input stream to parse packets using the Mojang packet - * definitions and then return them as a byte array. - */ -public class PacketStream implements AutoCloseable -{ - - private final DataInputStream dataInput; - @Getter - private OutputStream out; - @Getter - @Setter - private int protocol; - private final TrackingInputStream tracker; - private final byte[] buffer = new byte[ 1 << 18 ]; - - public PacketStream(InputStream in, int protocol) - { - this( in, null, protocol ); - } - - public PacketStream(InputStream in, OutputStream out, int protocol) - { - tracker = new TrackingInputStream( in ); - dataInput = new DataInputStream( tracker ); - this.out = out; - this.protocol = protocol; - } - - public void write(byte[] b) throws IOException - { - out.write( b ); - } - - public void write(DefinedPacket packet) throws IOException - { - out.write( packet.getPacket() ); - } - - /** - * Read an entire packet from the stream and return it as a byte array. - * - * @return the read packet - * @throws IOException when the underlying input stream throws an exception - */ - public byte[] readPacket() throws IOException - { - tracker.out.reset(); - DataInputPacketReader.readPacket( dataInput, buffer, protocol ); - return tracker.out.toByteArray(); - } - - @Override - public void close() throws Exception - { - dataInput.close(); - } - - /** - * Input stream which will wrap another stream and copy all bytes read to a - * {@link ByteArrayOutputStream}. - */ - private class TrackingInputStream extends FilterInputStream - { - - private final ByteArrayOutputStream out = new ByteArrayOutputStream(); - - public TrackingInputStream(InputStream in) - { - super( in ); - } - - @Override - public int read() throws IOException - { - int ret = in.read(); - out.write( ret ); - return ret; - } - - @Override - public int read(byte[] b, int off, int len) throws IOException - { - int ret = in.read( b, off, len ); - out.write( b, off, ret ); - return ret; - } - } -} diff --git a/proxy/src/main/java/net/md_5/bungee/tablist/GlobalTabList.java b/proxy/src/main/java/net/md_5/bungee/tablist/GlobalTabList.java index eb8ae0e9..576f8874 100644 --- a/proxy/src/main/java/net/md_5/bungee/tablist/GlobalTabList.java +++ b/proxy/src/main/java/net/md_5/bungee/tablist/GlobalTabList.java @@ -22,7 +22,7 @@ public class GlobalTabList implements TabListHandler UserConnection con = (UserConnection) player; for ( ProxiedPlayer p : ProxyServer.getInstance().getPlayers() ) { - con.packetQueue.add( new PacketC9PlayerListItem( p.getDisplayName(), true, p.getPing() ) ); + con.sendPacket(new PacketC9PlayerListItem( p.getDisplayName(), true, p.getPing() ) ); } BungeeCord.getInstance().broadcast( new PacketC9PlayerListItem( player.getDisplayName(), true, player.getPing() ) ); } diff --git a/proxy/src/main/java/net/md_5/bungee/tablist/ServerUniqueTabList.java b/proxy/src/main/java/net/md_5/bungee/tablist/ServerUniqueTabList.java index 091d5cad..3c17aab5 100644 --- a/proxy/src/main/java/net/md_5/bungee/tablist/ServerUniqueTabList.java +++ b/proxy/src/main/java/net/md_5/bungee/tablist/ServerUniqueTabList.java @@ -40,7 +40,7 @@ public class ServerUniqueTabList implements TabListHandler { for ( String username : usernames ) { - ( (UserConnection) player ).packetQueue.add( new PacketC9PlayerListItem( username, false, 9999 ) ); + ( (UserConnection) player ).sendPacket(new PacketC9PlayerListItem( username, false, 9999 ) ); } usernames.clear(); }