diff --git a/api/src/main/java/net/md_5/bungee/api/ServerPing.java b/api/src/main/java/net/md_5/bungee/api/ServerPing.java index e37f9aee..a74da42d 100644 --- a/api/src/main/java/net/md_5/bungee/api/ServerPing.java +++ b/api/src/main/java/net/md_5/bungee/api/ServerPing.java @@ -13,7 +13,7 @@ public class ServerPing /** * Numeric protocol version supported by the server. */ - private final byte protocolVersion; + private final int protocolVersion; /** * Human readable game version. */ diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/AbstractPacketHandler.java b/protocol/src/main/java/net/md_5/bungee/protocol/AbstractPacketHandler.java index 6101c314..c282f345 100644 --- a/protocol/src/main/java/net/md_5/bungee/protocol/AbstractPacketHandler.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/AbstractPacketHandler.java @@ -19,10 +19,25 @@ import net.md_5.bungee.protocol.packet.Handshake; import net.md_5.bungee.protocol.packet.EncryptionResponse; import net.md_5.bungee.protocol.packet.LoginRequest; import net.md_5.bungee.protocol.packet.LoginSuccess; +import net.md_5.bungee.protocol.packet.PingPacket; +import net.md_5.bungee.protocol.packet.StatusRequest; +import net.md_5.bungee.protocol.packet.StatusResponse; public abstract class AbstractPacketHandler { + public void handle(PingPacket ping) throws Exception + { + } + + public void handle(StatusRequest statusRequest) throws Exception + { + } + + public void handle(StatusResponse statusResponse) throws Exception + { + } + public void handle(Handshake handshake) throws Exception { } diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/MinecraftCodec.java b/protocol/src/main/java/net/md_5/bungee/protocol/MinecraftCodec.java index fca9ffc9..65961ab9 100644 --- a/protocol/src/main/java/net/md_5/bungee/protocol/MinecraftCodec.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/MinecraftCodec.java @@ -23,6 +23,8 @@ public class MinecraftCodec extends MessageToMessageCodec packet) { + Preconditions.checkArgument( packetMap.containsKey( packet ), "Cannot get ID for packet " + packet ); + return packetMap.get( packet ); } } diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/Varint21LengthFieldPrepender.java b/protocol/src/main/java/net/md_5/bungee/protocol/Varint21LengthFieldPrepender.java index 417adf38..dfae35ae 100644 --- a/protocol/src/main/java/net/md_5/bungee/protocol/Varint21LengthFieldPrepender.java +++ b/protocol/src/main/java/net/md_5/bungee/protocol/Varint21LengthFieldPrepender.java @@ -17,7 +17,7 @@ public class Varint21LengthFieldPrepender extends MessageToByteEncoder out.ensureWritable( headerLen + bodyLen ); DefinedPacket.writeVarInt( bodyLen, out ); - out.writeBytes( msg, msg.readerIndex(), bodyLen ); + out.writeBytes( msg ); } private static int varintSize(int paramInt) diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/PingPacket.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/PingPacket.java new file mode 100644 index 00000000..5f24d425 --- /dev/null +++ b/protocol/src/main/java/net/md_5/bungee/protocol/packet/PingPacket.java @@ -0,0 +1,37 @@ +package net.md_5.bungee.protocol.packet; + +import io.netty.buffer.ByteBuf; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import net.md_5.bungee.protocol.AbstractPacketHandler; +import net.md_5.bungee.protocol.DefinedPacket; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = false) +public class PingPacket extends DefinedPacket +{ + + private long time; + + @Override + public void read(ByteBuf buf) + { + time = buf.readLong(); + } + + @Override + public void write(ByteBuf buf) + { + buf.writeLong( time ); + } + + @Override + public void handle(AbstractPacketHandler handler) throws Exception + { + handler.handle( this ); + } +} diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/StatusRequest.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/StatusRequest.java new file mode 100644 index 00000000..738f0c92 --- /dev/null +++ b/protocol/src/main/java/net/md_5/bungee/protocol/packet/StatusRequest.java @@ -0,0 +1,31 @@ +package net.md_5.bungee.protocol.packet; + +import io.netty.buffer.ByteBuf; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import net.md_5.bungee.protocol.AbstractPacketHandler; +import net.md_5.bungee.protocol.DefinedPacket; + +@Data +@NoArgsConstructor +@EqualsAndHashCode(callSuper = false) +public class StatusRequest extends DefinedPacket +{ + + @Override + public void read(ByteBuf buf) + { + } + + @Override + public void write(ByteBuf buf) + { + } + + @Override + public void handle(AbstractPacketHandler handler) throws Exception + { + handler.handle( this ); + } +} diff --git a/protocol/src/main/java/net/md_5/bungee/protocol/packet/StatusResponse.java b/protocol/src/main/java/net/md_5/bungee/protocol/packet/StatusResponse.java new file mode 100644 index 00000000..69a9d46c --- /dev/null +++ b/protocol/src/main/java/net/md_5/bungee/protocol/packet/StatusResponse.java @@ -0,0 +1,37 @@ +package net.md_5.bungee.protocol.packet; + +import io.netty.buffer.ByteBuf; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import net.md_5.bungee.protocol.AbstractPacketHandler; +import net.md_5.bungee.protocol.DefinedPacket; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = false) +public class StatusResponse extends DefinedPacket +{ + + private String response; + + @Override + public void read(ByteBuf buf) + { + response = readString( buf ); + } + + @Override + public void write(ByteBuf buf) + { + writeString( response, buf ); + } + + @Override + public void handle(AbstractPacketHandler handler) throws Exception + { + handler.handle( this ); + } +} diff --git a/proxy/src/main/java/Test.java b/proxy/src/main/java/Test.java new file mode 100644 index 00000000..cc08bad4 --- /dev/null +++ b/proxy/src/main/java/Test.java @@ -0,0 +1,40 @@ + +import net.md_5.bungee.BungeeCord; +import net.md_5.bungee.api.ChatColor; +import net.md_5.bungee.api.ProxyServer; +import net.md_5.bungee.command.ConsoleCommandSender; + +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +/** + * + * @author michael + */ +public class Test +{ + + public static void main(String[] args) throws Exception + { + + System.setProperty( "java.net.preferIPv4Stack", "true" ); + + BungeeCord bungee = new BungeeCord(); + ProxyServer.setInstance( bungee ); + bungee.getLogger().info( "Enabled BungeeCord version " + bungee.getVersion() ); + bungee.start(); + + while ( bungee.isRunning ) + { + String line = bungee.getConsoleReader().readLine( ">" ); + if ( line != null ) + { + if ( !bungee.getPluginManager().dispatchCommand( ConsoleCommandSender.getInstance(), line ) ) + { + bungee.getConsole().sendMessage( ChatColor.RED + "Command not found" ); + } + } + } + } +} diff --git a/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java b/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java index 5fd22518..96d6b1b7 100644 --- a/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java +++ b/proxy/src/main/java/net/md_5/bungee/connection/InitialHandler.java @@ -3,10 +3,11 @@ package net.md_5.bungee.connection; import com.google.common.base.Preconditions; import com.google.gson.JsonElement; import com.google.gson.JsonObject; -import io.netty.util.concurrent.ScheduledFuture; +import java.math.BigInteger; import java.net.InetSocketAddress; import java.net.URLEncoder; import java.security.GeneralSecurityException; +import java.security.MessageDigest; import java.util.ArrayList; import java.util.List; import java.util.logging.Level; @@ -16,7 +17,6 @@ import lombok.Getter; import lombok.RequiredArgsConstructor; import net.md_5.bungee.BungeeCord; import net.md_5.bungee.EncryptionUtil; -import net.md_5.bungee.PacketConstants; import net.md_5.bungee.UserConnection; import net.md_5.bungee.Util; import net.md_5.bungee.api.Callback; @@ -37,7 +37,6 @@ import net.md_5.bungee.netty.CipherDecoder; import net.md_5.bungee.netty.CipherEncoder; import net.md_5.bungee.netty.PacketHandler; import net.md_5.bungee.netty.PipelineUtils; -import net.md_5.bungee.protocol.MinecraftInput; import net.md_5.bungee.protocol.DefinedPacket; import net.md_5.bungee.protocol.packet.Login; import net.md_5.bungee.protocol.packet.Handshake; @@ -48,8 +47,12 @@ import net.md_5.bungee.protocol.packet.EncryptionRequest; import net.md_5.bungee.protocol.packet.Kick; import net.md_5.bungee.api.AbstractReconnectHandler; import net.md_5.bungee.api.event.PlayerHandshakeEvent; +import net.md_5.bungee.protocol.PacketWrapper; import net.md_5.bungee.protocol.Protocol; import net.md_5.bungee.protocol.packet.LoginRequest; +import net.md_5.bungee.protocol.packet.LoginSuccess; +import net.md_5.bungee.protocol.packet.PingPacket; +import net.md_5.bungee.protocol.packet.StatusRequest; @RequiredArgsConstructor public class InitialHandler extends PacketHandler implements PendingConnection @@ -82,7 +85,6 @@ public class InitialHandler extends PacketHandler implements PendingConnection }; @Getter private boolean onlineMode = BungeeCord.getInstance().config.isOnlineMode(); - private ScheduledFuture pingFuture; private InetSocketAddress vHost; private byte version = -1; @Getter @@ -91,7 +93,7 @@ public class InitialHandler extends PacketHandler implements PendingConnection private enum State { - HANDSHAKE, ENCRYPT, LOGIN, FINISHED; + HANDSHAKE, STATUS, PING, USERNAME, ENCRYPT, LOGIN, FINISHED; } @Override @@ -100,6 +102,14 @@ public class InitialHandler extends PacketHandler implements PendingConnection this.ch = channel; } + @Override + public void handle(PacketWrapper packet) throws Exception + { + int len = DefinedPacket.readVarInt( packet.buf ); + int id = DefinedPacket.readVarInt( packet.buf ); + throw new UnsupportedOperationException( "Cannot handle unknown packet at login!" ); + } + @Override public void exception(Throwable t) throws Exception { @@ -109,22 +119,6 @@ public class InitialHandler extends PacketHandler implements PendingConnection @Override public void handle(PluginMessage pluginMessage) throws Exception { - if ( pluginMessage.getTag().equals( "MC|PingHost" ) ) - { - if ( pingFuture.cancel( false ) ) - { - MinecraftInput in = pluginMessage.getMCStream(); - version = in.readByte(); - String connectHost = in.readString(); - int connectPort = in.readInt(); - this.vHost = new InetSocketAddress( connectHost, connectPort ); - - respondToPing(); - } - - return; - } - // TODO: Unregister? if ( pluginMessage.getTag().equals( "REGISTER" ) ) { @@ -135,7 +129,8 @@ public class InitialHandler extends PacketHandler implements PendingConnection } } - private void respondToPing() + @Override + public void handle(StatusRequest statusRequest) throws Exception { ServerInfo forced = AbstractReconnectHandler.getForcedHost( this ); final String motd = ( forced != null ) ? forced.getMotd() : listener.getMotd(); @@ -167,22 +162,17 @@ public class InitialHandler extends PacketHandler implements PendingConnection forced.ping( pingBack ); } else { - // pingBack.done( new ServerPing( bungee.getProtocolVersion(), bungee.getGameVersion(), motd, bungee.getOnlineCount(), listener.getMaxPlayers() ), null ); + pingBack.done( new ServerPing( bungee.getProtocolVersion(), bungee.getGameVersion(), motd, bungee.getOnlineCount(), listener.getMaxPlayers() ), null ); } } - /* - @Override - public void handle(PacketFEPing ping) throws Exception - { - pingFuture = ch.getHandle().eventLoop().schedule( new Runnable() - { - @Override - public void run() - { - respondToPing(); - } - }, 200, TimeUnit.MILLISECONDS ); - }*/ + + @Override + public void handle(PingPacket ping) throws Exception + { + Preconditions.checkState( thisState == State.PING, "Not expecting PING" ); + unsafe.sendPacket( ping ); + disconnect( "" ); + } @Override public void handle(Handshake handshake) throws Exception @@ -206,9 +196,11 @@ public class InitialHandler extends PacketHandler implements PendingConnection { case 1: // Ping + thisState = State.STATUS; ch.setProtocol( Protocol.STATUS ); break; case 2: + thisState = State.USERNAME; ch.setProtocol( Protocol.LOGIN ); // Login break; @@ -220,6 +212,7 @@ public class InitialHandler extends PacketHandler implements PendingConnection @Override public void handle(LoginRequest loginRequest) throws Exception { + Preconditions.checkState( thisState == State.USERNAME, "Not expecting USERNAME" ); this.loginRequest = loginRequest; if ( getName().length() > 16 ) @@ -242,8 +235,9 @@ public class InitialHandler extends PacketHandler implements PendingConnection return; } - unsafe().sendPacket( PacketConstants.I_AM_BUNGEE ); - unsafe().sendPacket( PacketConstants.FORGE_MOD_REQUEST ); + // TODO: Nuuuu Mojang why u do this + // unsafe().sendPacket( PacketConstants.I_AM_BUNGEE ); + // unsafe().sendPacket( PacketConstants.FORGE_MOD_REQUEST ); unsafe().sendPacket( request = EncryptionUtil.encryptRequest( this.onlineMode ) ); thisState = State.ENCRYPT; @@ -261,9 +255,18 @@ public class InitialHandler extends PacketHandler implements PendingConnection if ( this.onlineMode ) { String encName = URLEncoder.encode( InitialHandler.this.getName(), "UTF-8" ); - String encID = URLEncoder.encode( InitialHandler.this.request.getServerId(), "UTF-8" ); - String authURL = "https://sessionserver.mojang.com/session/minecraft/hasJoined?username=" + encName + "&serverId=" + encID; + MessageDigest sha = MessageDigest.getInstance( "SHA-1" ); + for ( byte[] bit : new byte[][] + { + request.getServerId().getBytes( "ISO_8859_1" ), sharedKey.getEncoded(), EncryptionUtil.keys.getPublic().getEncoded() + } ) + { + sha.update( bit ); + } + String encodedHash = URLEncoder.encode( new BigInteger( sha.digest() ).toString( 16 ), "UTF-8" ); + + String authURL = "https://sessionserver.mojang.com/session/minecraft/hasJoined?username=" + encName + "&serverId=" + encodedHash; Callback handler = new Callback() { @@ -272,16 +275,12 @@ public class InitialHandler extends PacketHandler implements PendingConnection { if ( error == null ) { - JsonObject obj = BungeeCord.getInstance().gson.fromJson( result, JsonObject.class ); + LoginResult obj = BungeeCord.getInstance().gson.fromJson( result, LoginResult.class ); if ( obj != null ) { - JsonElement id = obj.get( "id" ); - if ( id != null ) - { - UUID = id.getAsString(); - finish(); - return; - } + UUID = obj.getId(); + finish(); + return; } disconnect( "Not authenticated with Minecraft.net" ); } else @@ -330,15 +329,26 @@ public class InitialHandler extends PacketHandler implements PendingConnection { if ( ch.getHandle().isActive() ) { - unsafe().sendPacket( new EncryptionResponse( new byte[ 0 ], new byte[ 0 ] ) ); - try + + if ( onlineMode ) { - Cipher encrypt = EncryptionUtil.getCipher( Cipher.ENCRYPT_MODE, sharedKey ); - ch.addBefore( PipelineUtils.DECRYPT_HANDLER, PipelineUtils.ENCRYPT_HANDLER, new CipherEncoder( encrypt ) ); - } catch ( GeneralSecurityException ex ) - { - disconnect( "Cipher error: " + Util.exception( ex ) ); + unsafe().sendPacket( new EncryptionResponse( new byte[ 0 ], new byte[ 0 ] ) ); + try + { + Cipher encrypt = EncryptionUtil.getCipher( Cipher.ENCRYPT_MODE, sharedKey ); + ch.addBefore( PipelineUtils.DECRYPT_HANDLER, PipelineUtils.ENCRYPT_HANDLER, new CipherEncoder( encrypt ) ); + } catch ( GeneralSecurityException ex ) + { + disconnect( "Cipher error: " + Util.exception( ex ) ); + } } + + if ( UUID == null ) + { + UUID = java.util.UUID.randomUUID().toString(); + } + unsafe.sendPacket( new LoginSuccess( BungeeCord.getInstance().gson.toJson( new LoginResult( UUID ) ) ) ); + ch.setProtocol( Protocol.GAME ); } } } ); @@ -380,7 +390,7 @@ public class InitialHandler extends PacketHandler implements PendingConnection { if ( !ch.isClosed() ) { - unsafe().sendPacket( new Kick( reason ) ); + unsafe().sendPacket( new Kick( BungeeCord.getInstance().gson.toJson( reason ) ) ); ch.close(); } } diff --git a/proxy/src/main/java/net/md_5/bungee/connection/LoginResult.java b/proxy/src/main/java/net/md_5/bungee/connection/LoginResult.java new file mode 100644 index 00000000..e0b4060c --- /dev/null +++ b/proxy/src/main/java/net/md_5/bungee/connection/LoginResult.java @@ -0,0 +1,12 @@ +package net.md_5.bungee.connection; + +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +@AllArgsConstructor +public class LoginResult +{ + + private String id; +} diff --git a/proxy/src/main/java/net/md_5/bungee/netty/ChannelWrapper.java b/proxy/src/main/java/net/md_5/bungee/netty/ChannelWrapper.java index 492c1d00..10bb86b1 100644 --- a/proxy/src/main/java/net/md_5/bungee/netty/ChannelWrapper.java +++ b/proxy/src/main/java/net/md_5/bungee/netty/ChannelWrapper.java @@ -33,10 +33,10 @@ public class ChannelWrapper if ( packet instanceof PacketWrapper ) { ( (PacketWrapper) packet ).setReleased( true ); - ch.write( ( (PacketWrapper) packet ).buf ); + ch.write( ( (PacketWrapper) packet ).buf, ch.voidPromise() ); } else { - ch.write( packet ); + ch.write( packet, ch.voidPromise() ); } ch.flush(); } diff --git a/proxy/src/main/java/net/md_5/bungee/netty/HandlerBoss.java b/proxy/src/main/java/net/md_5/bungee/netty/HandlerBoss.java index 56f0ade4..fc80d169 100644 --- a/proxy/src/main/java/net/md_5/bungee/netty/HandlerBoss.java +++ b/proxy/src/main/java/net/md_5/bungee/netty/HandlerBoss.java @@ -12,8 +12,6 @@ import net.md_5.bungee.connection.CancelSendSignal; import net.md_5.bungee.connection.InitialHandler; import net.md_5.bungee.connection.PingHandler; import net.md_5.bungee.protocol.BadPacketException; -import net.md_5.bungee.protocol.MinecraftCodec; -import net.md_5.bungee.protocol.Protocol; /** * This class is a primitive wrapper for {@link PacketHandler} instances tied to diff --git a/proxy/src/main/java/net/md_5/bungee/netty/PipelineUtils.java b/proxy/src/main/java/net/md_5/bungee/netty/PipelineUtils.java index f1c2d1e7..52ae773a 100644 --- a/proxy/src/main/java/net/md_5/bungee/netty/PipelineUtils.java +++ b/proxy/src/main/java/net/md_5/bungee/netty/PipelineUtils.java @@ -76,10 +76,10 @@ public class PipelineUtils { // IP_TOS is not supported (Windows XP / Windows Server 2003) } - + ch.pipeline().addLast( FRAME_PREPENDER, framePrepender ); ch.pipeline().addLast( TIMEOUT_HANDLER, new ReadTimeoutHandler( BungeeCord.getInstance().config.getTimeout(), TimeUnit.MILLISECONDS ) ); ch.pipeline().addLast( FRAME_DECODER, new Varint21FrameDecoder() ); - ch.pipeline().addLast( FRAME_PREPENDER, framePrepender ); + ch.pipeline().addLast( BOSS_HANDLER, new HandlerBoss() ); } };