Add experimental Forge support. This may cause issues when using Vanilla clients etc, so caution is advised. Please visit GitHub to report any issues you encounter. Thanks @LexManos for providing the basis for this implementation.
This commit is contained in:
parent
d3c1339cc9
commit
7b631092f5
@ -7,7 +7,6 @@ import lombok.RequiredArgsConstructor;
|
|||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
import net.md_5.bungee.api.connection.Server;
|
import net.md_5.bungee.api.connection.Server;
|
||||||
import net.md_5.bungee.netty.ChannelWrapper;
|
import net.md_5.bungee.netty.ChannelWrapper;
|
||||||
import net.md_5.bungee.packet.Packet1Login;
|
|
||||||
import net.md_5.bungee.packet.PacketFAPluginMessage;
|
import net.md_5.bungee.packet.PacketFAPluginMessage;
|
||||||
import net.md_5.bungee.packet.PacketFFKick;
|
import net.md_5.bungee.packet.PacketFFKick;
|
||||||
|
|
||||||
@ -20,7 +19,7 @@ public class ServerConnection implements Server
|
|||||||
@Getter
|
@Getter
|
||||||
private final BungeeServerInfo info;
|
private final BungeeServerInfo info;
|
||||||
@Getter
|
@Getter
|
||||||
private final Packet1Login loginPacket;
|
private final boolean isForgeWrapper;
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
private boolean isObsolete;
|
private boolean isObsolete;
|
||||||
|
@ -1,10 +1,14 @@
|
|||||||
package net.md_5.bungee;
|
package net.md_5.bungee;
|
||||||
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
|
import com.google.common.io.ByteArrayDataInput;
|
||||||
import com.google.common.io.ByteArrayDataOutput;
|
import com.google.common.io.ByteArrayDataOutput;
|
||||||
import com.google.common.io.ByteStreams;
|
import com.google.common.io.ByteStreams;
|
||||||
|
import java.security.PublicKey;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Queue;
|
import java.util.Queue;
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
import javax.crypto.SecretKey;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import net.md_5.bungee.api.ChatColor;
|
import net.md_5.bungee.api.ChatColor;
|
||||||
import net.md_5.bungee.api.ProxyServer;
|
import net.md_5.bungee.api.ProxyServer;
|
||||||
@ -18,6 +22,9 @@ import net.md_5.bungee.connection.CancelSendSignal;
|
|||||||
import net.md_5.bungee.connection.DownstreamBridge;
|
import net.md_5.bungee.connection.DownstreamBridge;
|
||||||
import net.md_5.bungee.netty.HandlerBoss;
|
import net.md_5.bungee.netty.HandlerBoss;
|
||||||
import net.md_5.bungee.netty.ChannelWrapper;
|
import net.md_5.bungee.netty.ChannelWrapper;
|
||||||
|
import net.md_5.bungee.netty.CipherDecoder;
|
||||||
|
import net.md_5.bungee.netty.CipherEncoder;
|
||||||
|
import net.md_5.bungee.netty.PacketDecoder;
|
||||||
import net.md_5.bungee.packet.DefinedPacket;
|
import net.md_5.bungee.packet.DefinedPacket;
|
||||||
import net.md_5.bungee.packet.Packet1Login;
|
import net.md_5.bungee.packet.Packet1Login;
|
||||||
import net.md_5.bungee.packet.Packet9Respawn;
|
import net.md_5.bungee.packet.Packet9Respawn;
|
||||||
@ -25,9 +32,11 @@ import net.md_5.bungee.packet.PacketCDClientStatus;
|
|||||||
import net.md_5.bungee.packet.PacketCEScoreboardObjective;
|
import net.md_5.bungee.packet.PacketCEScoreboardObjective;
|
||||||
import net.md_5.bungee.packet.PacketD1Team;
|
import net.md_5.bungee.packet.PacketD1Team;
|
||||||
import net.md_5.bungee.packet.PacketFAPluginMessage;
|
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.PacketFDEncryptionRequest;
|
||||||
import net.md_5.bungee.packet.PacketFFKick;
|
import net.md_5.bungee.packet.PacketFFKick;
|
||||||
import net.md_5.bungee.packet.PacketHandler;
|
import net.md_5.bungee.packet.PacketHandler;
|
||||||
|
import net.md_5.bungee.protocol.PacketDefinitions;
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class ServerConnector extends PacketHandler
|
public class ServerConnector extends PacketHandler
|
||||||
@ -38,11 +47,12 @@ public class ServerConnector extends PacketHandler
|
|||||||
private final UserConnection user;
|
private final UserConnection user;
|
||||||
private final BungeeServerInfo target;
|
private final BungeeServerInfo target;
|
||||||
private State thisState = State.ENCRYPT_REQUEST;
|
private State thisState = State.ENCRYPT_REQUEST;
|
||||||
|
private SecretKey secretkey;
|
||||||
|
|
||||||
private enum State
|
private enum State
|
||||||
{
|
{
|
||||||
|
|
||||||
ENCRYPT_REQUEST, LOGIN, FINISHED;
|
ENCRYPT_REQUEST, ENCRYPT_RESPONSE, LOGIN, FINISHED;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -57,8 +67,13 @@ public class ServerConnector extends PacketHandler
|
|||||||
channel.write( new PacketFAPluginMessage( "BungeeCord", out.toByteArray() ) );
|
channel.write( new PacketFAPluginMessage( "BungeeCord", out.toByteArray() ) );
|
||||||
|
|
||||||
channel.write( user.getPendingConnection().getHandshake() );
|
channel.write( user.getPendingConnection().getHandshake() );
|
||||||
|
|
||||||
|
// Skip encryption if we are not using Forge
|
||||||
|
if ( user.getPendingConnection().getForgeLogin() == null )
|
||||||
|
{
|
||||||
channel.write( PacketCDClientStatus.CLIENT_LOGIN );
|
channel.write( PacketCDClientStatus.CLIENT_LOGIN );
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void disconnected(ChannelWrapper channel) throws Exception
|
public void disconnected(ChannelWrapper channel) throws Exception
|
||||||
@ -71,7 +86,7 @@ public class ServerConnector extends PacketHandler
|
|||||||
{
|
{
|
||||||
Preconditions.checkState( thisState == State.LOGIN, "Not exepcting LOGIN" );
|
Preconditions.checkState( thisState == State.LOGIN, "Not exepcting LOGIN" );
|
||||||
|
|
||||||
ServerConnection server = new ServerConnection( ch, target, login );
|
ServerConnection server = new ServerConnection( ch, target, false );
|
||||||
ServerConnectedEvent event = new ServerConnectedEvent( user, server );
|
ServerConnectedEvent event = new ServerConnectedEvent( user, server );
|
||||||
bungee.getPluginManager().callEvent( event );
|
bungee.getPluginManager().callEvent( event );
|
||||||
|
|
||||||
@ -96,7 +111,8 @@ public class ServerConnector extends PacketHandler
|
|||||||
|
|
||||||
synchronized ( user.getSwitchMutex() )
|
synchronized ( user.getSwitchMutex() )
|
||||||
{
|
{
|
||||||
if ( user.getServer() == null )
|
// TODO: This whole wrapper business is a hack
|
||||||
|
if ( user.getServer() == null || user.getServer().isForgeWrapper() )
|
||||||
{
|
{
|
||||||
// Once again, first connection
|
// Once again, first connection
|
||||||
user.setClientEntityId( login.entityId );
|
user.setClientEntityId( login.entityId );
|
||||||
@ -110,7 +126,7 @@ public class ServerConnector extends PacketHandler
|
|||||||
login.difficulty,
|
login.difficulty,
|
||||||
login.unused,
|
login.unused,
|
||||||
(byte) user.getPendingConnection().getListener().getTabListSize(),
|
(byte) user.getPendingConnection().getListener().getTabListSize(),
|
||||||
false );
|
secretkey != null ); // If we are encrypting, we must be Forge
|
||||||
user.sendPacket( modLogin );
|
user.sendPacket( modLogin );
|
||||||
} else
|
} else
|
||||||
{
|
{
|
||||||
@ -165,6 +181,40 @@ public class ServerConnector extends PacketHandler
|
|||||||
public void handle(PacketFDEncryptionRequest encryptRequest) throws Exception
|
public void handle(PacketFDEncryptionRequest encryptRequest) throws Exception
|
||||||
{
|
{
|
||||||
Preconditions.checkState( thisState == State.ENCRYPT_REQUEST, "Not expecting ENCRYPT_REQUEST" );
|
Preconditions.checkState( thisState == State.ENCRYPT_REQUEST, "Not expecting ENCRYPT_REQUEST" );
|
||||||
|
|
||||||
|
// Only need to handle this if we want to use encryption
|
||||||
|
if ( user.getPendingConnection().getForgeLogin() != null )
|
||||||
|
{
|
||||||
|
PublicKey publickey = EncryptionUtil.getPubkey( encryptRequest );
|
||||||
|
this.secretkey = EncryptionUtil.getSecret();
|
||||||
|
|
||||||
|
byte[] shared = EncryptionUtil.encrypt( publickey, secretkey.getEncoded() );
|
||||||
|
byte[] token = EncryptionUtil.encrypt( publickey, encryptRequest.verifyToken );
|
||||||
|
|
||||||
|
ch.write( new PacketFCEncryptionResponse( shared, token ) );
|
||||||
|
|
||||||
|
Cipher encrypt = EncryptionUtil.getCipher( Cipher.ENCRYPT_MODE, secretkey );
|
||||||
|
ch.getHandle().pipeline().addBefore( "decoder", "encrypt", new CipherEncoder( encrypt ) );
|
||||||
|
|
||||||
|
thisState = State.ENCRYPT_RESPONSE;
|
||||||
|
} else
|
||||||
|
{
|
||||||
|
thisState = State.LOGIN;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle(PacketFCEncryptionResponse encryptResponse) throws Exception
|
||||||
|
{
|
||||||
|
Preconditions.checkState( thisState == State.ENCRYPT_RESPONSE, "Not expecting ENCRYPT_RESPONSE" );
|
||||||
|
|
||||||
|
Cipher decrypt = EncryptionUtil.getCipher( Cipher.DECRYPT_MODE, secretkey );
|
||||||
|
ch.getHandle().pipeline().addBefore( "decoder", "decrypt", new CipherDecoder( decrypt ) );
|
||||||
|
|
||||||
|
ch.write( user.getPendingConnection().getForgeLogin() );
|
||||||
|
user.setServer( new ServerConnection( ch, target, true ) );
|
||||||
|
|
||||||
|
ch.write( PacketCDClientStatus.CLIENT_LOGIN );
|
||||||
thisState = State.LOGIN;
|
thisState = State.LOGIN;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -193,6 +243,26 @@ public class ServerConnector extends PacketHandler
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle(PacketFAPluginMessage pluginMessage) throws Exception
|
||||||
|
{
|
||||||
|
if ( ( pluginMessage.data[0] & 0xFF ) == 0 && pluginMessage.tag.equals( "FML" ) )
|
||||||
|
{
|
||||||
|
ByteArrayDataInput in = ByteStreams.newDataInput( pluginMessage.data );
|
||||||
|
in.readUnsignedByte();
|
||||||
|
for ( int i = 0; i < in.readInt(); i++ )
|
||||||
|
{
|
||||||
|
in.readUTF();
|
||||||
|
}
|
||||||
|
if ( in.readByte() != 0 )
|
||||||
|
{
|
||||||
|
ch.getHandle().pipeline().get( PacketDecoder.class ).setProtocol( PacketDefinitions.FORGE_PROTOCOL );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
user.sendPacket( pluginMessage ); // We have to forward these to the user, especially with Forge as stuff might break
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString()
|
public String toString()
|
||||||
{
|
{
|
||||||
|
@ -53,8 +53,9 @@ public final class UserConnection implements ProxiedPlayer
|
|||||||
private final InitialHandler pendingConnection;
|
private final InitialHandler pendingConnection;
|
||||||
/*========================================================================*/
|
/*========================================================================*/
|
||||||
@Getter
|
@Getter
|
||||||
|
@NonNull
|
||||||
@Setter
|
@Setter
|
||||||
private ServerConnection server;
|
private ServerConnection server = null;
|
||||||
@Getter
|
@Getter
|
||||||
private final Object switchMutex = new Object();
|
private final Object switchMutex = new Object();
|
||||||
@Getter
|
@Getter
|
||||||
|
@ -33,6 +33,8 @@ import net.md_5.bungee.netty.HandlerBoss;
|
|||||||
import net.md_5.bungee.netty.ChannelWrapper;
|
import net.md_5.bungee.netty.ChannelWrapper;
|
||||||
import net.md_5.bungee.netty.CipherDecoder;
|
import net.md_5.bungee.netty.CipherDecoder;
|
||||||
import net.md_5.bungee.netty.CipherEncoder;
|
import net.md_5.bungee.netty.CipherEncoder;
|
||||||
|
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.Packet2Handshake;
|
||||||
import net.md_5.bungee.packet.PacketCDClientStatus;
|
import net.md_5.bungee.packet.PacketCDClientStatus;
|
||||||
import net.md_5.bungee.packet.PacketFAPluginMessage;
|
import net.md_5.bungee.packet.PacketFAPluginMessage;
|
||||||
@ -41,6 +43,7 @@ import net.md_5.bungee.packet.PacketFDEncryptionRequest;
|
|||||||
import net.md_5.bungee.packet.PacketFEPing;
|
import net.md_5.bungee.packet.PacketFEPing;
|
||||||
import net.md_5.bungee.packet.PacketFFKick;
|
import net.md_5.bungee.packet.PacketFFKick;
|
||||||
import net.md_5.bungee.packet.PacketHandler;
|
import net.md_5.bungee.packet.PacketHandler;
|
||||||
|
import net.md_5.bungee.protocol.PacketDefinitions;
|
||||||
|
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class InitialHandler extends PacketHandler implements PendingConnection
|
public class InitialHandler extends PacketHandler implements PendingConnection
|
||||||
@ -51,6 +54,8 @@ public class InitialHandler extends PacketHandler implements PendingConnection
|
|||||||
@Getter
|
@Getter
|
||||||
private final ListenerInfo listener;
|
private final ListenerInfo listener;
|
||||||
@Getter
|
@Getter
|
||||||
|
private Packet1Login forgeLogin;
|
||||||
|
@Getter
|
||||||
private Packet2Handshake handshake;
|
private Packet2Handshake handshake;
|
||||||
private PacketFDEncryptionRequest request;
|
private PacketFDEncryptionRequest request;
|
||||||
@Getter
|
@Getter
|
||||||
@ -100,6 +105,16 @@ public class InitialHandler extends PacketHandler implements PendingConnection
|
|||||||
disconnect( kickMessage );
|
disconnect( kickMessage );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@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.getHandle().pipeline().get( PacketDecoder.class ).setProtocol( PacketDefinitions.FORGE_PROTOCOL );
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handle(Packet2Handshake handshake) throws Exception
|
public void handle(Packet2Handshake handshake) throws Exception
|
||||||
{
|
{
|
||||||
@ -196,12 +211,12 @@ public class InitialHandler extends PacketHandler implements PendingConnection
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
thisState = InitialHandler.State.LOGIN;
|
||||||
ch.write( new PacketFCEncryptionResponse() );
|
ch.write( new PacketFCEncryptionResponse() );
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Cipher encrypt = EncryptionUtil.getCipher( Cipher.ENCRYPT_MODE, sharedKey );
|
Cipher encrypt = EncryptionUtil.getCipher( Cipher.ENCRYPT_MODE, sharedKey );
|
||||||
ch.getHandle().pipeline().addBefore( "decoder", "encrypt", new CipherEncoder( encrypt ) );
|
ch.getHandle().pipeline().addBefore( "decoder", "encrypt", new CipherEncoder( encrypt ) );
|
||||||
thisState = InitialHandler.State.LOGIN;
|
|
||||||
} catch ( GeneralSecurityException ex )
|
} catch ( GeneralSecurityException ex )
|
||||||
{
|
{
|
||||||
disconnect( "Cipher error: " + Util.exception( ex ) );
|
disconnect( "Cipher error: " + Util.exception( ex ) );
|
||||||
|
Loading…
Reference in New Issue
Block a user