mirror of
https://github.com/SpigotMC/BungeeCord.git
synced 2024-11-30 22:24:18 +01:00
Use a stateful login system for the initial handler.
This commit is contained in:
parent
36f5f33db0
commit
c65a3ec55e
@ -1,12 +1,11 @@
|
|||||||
package net.md_5.bungee;
|
package net.md_5.bungee;
|
||||||
|
|
||||||
import net.md_5.bungee.config.Configuration;
|
import com.google.common.base.Preconditions;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
|
||||||
import javax.crypto.SecretKey;
|
import javax.crypto.SecretKey;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import net.md_5.bungee.api.ChatColor;
|
import net.md_5.bungee.api.ChatColor;
|
||||||
@ -18,15 +17,19 @@ import net.md_5.bungee.api.connection.PendingConnection;
|
|||||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||||
import net.md_5.bungee.api.event.LoginEvent;
|
import net.md_5.bungee.api.event.LoginEvent;
|
||||||
import net.md_5.bungee.api.event.ProxyPingEvent;
|
import net.md_5.bungee.api.event.ProxyPingEvent;
|
||||||
|
import net.md_5.bungee.packet.DefinedPacket;
|
||||||
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.PacketFCEncryptionResponse;
|
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.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.PacketInputStream;
|
import net.md_5.bungee.packet.PacketInputStream;
|
||||||
import org.bouncycastle.crypto.io.CipherInputStream;
|
import org.bouncycastle.crypto.io.CipherInputStream;
|
||||||
import org.bouncycastle.crypto.io.CipherOutputStream;
|
import org.bouncycastle.crypto.io.CipherOutputStream;
|
||||||
|
|
||||||
public class InitialHandler implements Runnable, PendingConnection
|
public class InitialHandler extends PacketHandler implements Runnable, PendingConnection
|
||||||
{
|
{
|
||||||
|
|
||||||
private final Socket socket;
|
private final Socket socket;
|
||||||
@ -35,6 +38,8 @@ public class InitialHandler implements Runnable, PendingConnection
|
|||||||
private PacketInputStream in;
|
private PacketInputStream in;
|
||||||
private OutputStream out;
|
private OutputStream out;
|
||||||
private Packet2Handshake handshake;
|
private Packet2Handshake handshake;
|
||||||
|
private PacketFDEncryptionRequest request;
|
||||||
|
private State thisState = State.HANDSHAKE;
|
||||||
|
|
||||||
public InitialHandler(Socket socket, ListenerInfo info) throws IOException
|
public InitialHandler(Socket socket, ListenerInfo info) throws IOException
|
||||||
{
|
{
|
||||||
@ -44,92 +49,106 @@ public class InitialHandler implements Runnable, PendingConnection
|
|||||||
out = socket.getOutputStream();
|
out = socket.getOutputStream();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private enum State
|
||||||
|
{
|
||||||
|
|
||||||
|
HANDSHAKE, ENCRYPT, LOGIN, FINISHED;
|
||||||
|
}
|
||||||
|
|
||||||
|
@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);
|
||||||
|
this.handshake = handshake;
|
||||||
|
request = EncryptionUtil.encryptRequest();
|
||||||
|
out.write(request.getPacket());
|
||||||
|
thisState = State.ENCRYPT;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle(PacketFCEncryptionResponse encryptResponse) throws Exception
|
||||||
|
{
|
||||||
|
Preconditions.checkState(thisState == State.ENCRYPT);
|
||||||
|
|
||||||
|
SecretKey shared = EncryptionUtil.getSecret(encryptResponse, request);
|
||||||
|
if (!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);
|
||||||
|
if (event.isCancelled())
|
||||||
|
{
|
||||||
|
disconnect(event.getCancelReason());
|
||||||
|
}
|
||||||
|
|
||||||
|
out.write(new PacketFCEncryptionResponse().getPacket());
|
||||||
|
in = new PacketInputStream(new CipherInputStream(socket.getInputStream(), EncryptionUtil.getCipher(false, shared)));
|
||||||
|
out = new CipherOutputStream(socket.getOutputStream(), EncryptionUtil.getCipher(true, shared));
|
||||||
|
|
||||||
|
thisState = State.LOGIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle(PacketCDClientStatus clientStatus) throws Exception
|
||||||
|
{
|
||||||
|
Preconditions.checkState(thisState == State.LOGIN);
|
||||||
|
|
||||||
|
UserConnection userCon = new UserConnection(socket, this, in, out, handshake, new ArrayList<byte[]>());
|
||||||
|
String server = ProxyServer.getInstance().getReconnectHandler().getServer(userCon);
|
||||||
|
ServerInfo s = BungeeCord.getInstance().config.getServers().get(server);
|
||||||
|
userCon.connect(s);
|
||||||
|
|
||||||
|
thisState = State.FINISHED;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run()
|
public void run()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
byte[] packet = in.readPacket();
|
while (thisState != State.FINISHED)
|
||||||
int id = Util.getId(packet);
|
|
||||||
switch (id)
|
|
||||||
{
|
{
|
||||||
case 0x02:
|
byte[] buf = in.readPacket();
|
||||||
handshake = new Packet2Handshake(packet);
|
DefinedPacket packet = DefinedPacket.packet(buf);
|
||||||
PacketFDEncryptionRequest request = EncryptionUtil.encryptRequest();
|
packet.handle(this);
|
||||||
out.write(request.getPacket());
|
|
||||||
PacketFCEncryptionResponse response = new PacketFCEncryptionResponse(in.readPacket());
|
|
||||||
|
|
||||||
SecretKey shared = EncryptionUtil.getSecret(response, request);
|
|
||||||
if (!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);
|
|
||||||
if (event.isCancelled())
|
|
||||||
{
|
|
||||||
throw new KickException(event.getCancelReason());
|
|
||||||
}
|
|
||||||
|
|
||||||
out.write(new PacketFCEncryptionResponse().getPacket());
|
|
||||||
in = new PacketInputStream(new CipherInputStream(socket.getInputStream(), EncryptionUtil.getCipher(false, shared)));
|
|
||||||
out = new CipherOutputStream(socket.getOutputStream(), EncryptionUtil.getCipher(true, shared));
|
|
||||||
List<byte[]> customPackets = new ArrayList<>();
|
|
||||||
byte[] custom;
|
|
||||||
while (Util.getId((custom = in.readPacket())) != 0xCD)
|
|
||||||
{
|
|
||||||
customPackets.add(custom);
|
|
||||||
}
|
|
||||||
|
|
||||||
UserConnection userCon = new UserConnection(socket, this, in, out, handshake, customPackets);
|
|
||||||
String server = ProxyServer.getInstance().getReconnectHandler().getServer(userCon);
|
|
||||||
ServerInfo s = BungeeCord.getInstance().config.getServers().get(server);
|
|
||||||
userCon.connect(s);
|
|
||||||
break;
|
|
||||||
case 0xFE:
|
|
||||||
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 ping = (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();
|
|
||||||
throw new KickException(ping);
|
|
||||||
default:
|
|
||||||
if (id == 0xFA)
|
|
||||||
{
|
|
||||||
run(); // WTF Spoutcraft
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
// throw new IllegalArgumentException("Wasn't ready for packet id " + Util.hex(id));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (KickException ex)
|
|
||||||
{
|
|
||||||
disconnect(ex.getMessage());
|
|
||||||
} catch (Exception ex)
|
} catch (Exception ex)
|
||||||
{
|
{
|
||||||
disconnect("[Proxy Error] " + Util.exception(ex));
|
disconnect("[Proxy Error] " + Util.exception(ex));
|
||||||
@ -140,6 +159,7 @@ public class InitialHandler implements Runnable, PendingConnection
|
|||||||
@Override
|
@Override
|
||||||
public void disconnect(String reason)
|
public void disconnect(String reason)
|
||||||
{
|
{
|
||||||
|
thisState = State.FINISHED;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
out.write(new PacketFFKick(reason).getPacket());
|
out.write(new PacketFFKick(reason).getPacket());
|
||||||
@ -149,7 +169,7 @@ public class InitialHandler implements Runnable, PendingConnection
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
out.flush();
|
socket.shutdownOutput();
|
||||||
socket.close();
|
socket.close();
|
||||||
} catch (IOException ioe2)
|
} catch (IOException ioe2)
|
||||||
{
|
{
|
||||||
|
@ -5,6 +5,8 @@ import com.google.common.io.ByteArrayDataOutput;
|
|||||||
import com.google.common.io.ByteStreams;
|
import com.google.common.io.ByteStreams;
|
||||||
import java.io.DataInput;
|
import java.io.DataInput;
|
||||||
import java.io.DataOutput;
|
import java.io.DataOutput;
|
||||||
|
import java.lang.reflect.Constructor;
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import lombok.Delegate;
|
import lombok.Delegate;
|
||||||
import net.md_5.bungee.Util;
|
import net.md_5.bungee.Util;
|
||||||
|
|
||||||
@ -106,4 +108,46 @@ public abstract class DefinedPacket implements DataInput, DataOutput
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public abstract String toString();
|
public abstract String toString();
|
||||||
|
|
||||||
|
public void handle(PacketHandler handler) throws Exception
|
||||||
|
{
|
||||||
|
handler.handle(this);
|
||||||
|
}
|
||||||
|
private static Class<? extends DefinedPacket>[] classes = new Class[256];
|
||||||
|
|
||||||
|
public static DefinedPacket packet(byte[] buf)
|
||||||
|
{
|
||||||
|
int id = Util.getId(buf);
|
||||||
|
Class<? extends DefinedPacket> clazz = classes[id];
|
||||||
|
DefinedPacket ret = null;
|
||||||
|
if (clazz != null)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Constructor<? extends DefinedPacket> constructor = clazz.getDeclaredConstructor(byte[].class);
|
||||||
|
if (constructor != null)
|
||||||
|
{
|
||||||
|
ret = constructor.newInstance(buf);
|
||||||
|
}
|
||||||
|
} catch (IllegalAccessException | InstantiationException | InvocationTargetException | NoSuchMethodException ex)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static
|
||||||
|
{
|
||||||
|
classes[0x00] = Packet0KeepAlive.class;
|
||||||
|
classes[0x01] = Packet1Login.class;
|
||||||
|
classes[0x02] = Packet2Handshake.class;
|
||||||
|
classes[0x03] = Packet3Chat.class;
|
||||||
|
classes[0x09] = Packet9Respawn.class;
|
||||||
|
classes[0xC9] = PacketC9PlayerListItem.class;
|
||||||
|
classes[0xCD] = PacketCDClientStatus.class;
|
||||||
|
classes[0xFA] = PacketFAPluginMessage.class;
|
||||||
|
classes[0xFC] = PacketFCEncryptionResponse.class;
|
||||||
|
classes[0xFE] = PacketFEPing.class;
|
||||||
|
classes[0xFF] = PacketFFKick.class;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,4 +30,10 @@ public class Packet2Handshake extends DefinedPacket
|
|||||||
this.host = readUTF();
|
this.host = readUTF();
|
||||||
this.port = readInt();
|
this.port = readInt();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle(PacketHandler handler) throws Exception
|
||||||
|
{
|
||||||
|
handler.handle(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,4 +18,15 @@ public class PacketCDClientStatus extends DefinedPacket
|
|||||||
super(0xCD);
|
super(0xCD);
|
||||||
writeByte(payload);
|
writeByte(payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PacketCDClientStatus(byte[] buf)
|
||||||
|
{
|
||||||
|
super(0xCD, buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle(PacketHandler handler) throws Exception
|
||||||
|
{
|
||||||
|
handler.handle(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,4 +31,10 @@ public class PacketFCEncryptionResponse extends DefinedPacket
|
|||||||
this.sharedSecret = readArray();
|
this.sharedSecret = readArray();
|
||||||
this.verifyToken = readArray();
|
this.verifyToken = readArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle(PacketHandler handler) throws Exception
|
||||||
|
{
|
||||||
|
handler.handle(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,4 +30,10 @@ public class PacketFDEncryptionRequest extends DefinedPacket
|
|||||||
publicKey = readArray();
|
publicKey = readArray();
|
||||||
verifyToken = readArray();
|
verifyToken = readArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle(PacketHandler handler) throws Exception
|
||||||
|
{
|
||||||
|
handler.handle(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
21
proxy/src/main/java/net/md_5/bungee/packet/PacketFEPing.java
Normal file
21
proxy/src/main/java/net/md_5/bungee/packet/PacketFEPing.java
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package net.md_5.bungee.packet;
|
||||||
|
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.ToString;
|
||||||
|
|
||||||
|
@ToString
|
||||||
|
@EqualsAndHashCode(callSuper = false)
|
||||||
|
public class PacketFEPing extends DefinedPacket
|
||||||
|
{
|
||||||
|
|
||||||
|
public PacketFEPing(byte[] buffer)
|
||||||
|
{
|
||||||
|
super(0xFE, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handle(PacketHandler handler) throws Exception
|
||||||
|
{
|
||||||
|
handler.handle(this);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,30 @@
|
|||||||
|
package net.md_5.bungee.packet;
|
||||||
|
|
||||||
|
public abstract class PacketHandler
|
||||||
|
{
|
||||||
|
|
||||||
|
public void handle(DefinedPacket packet) throws Exception
|
||||||
|
{
|
||||||
|
throw new UnsupportedOperationException("No handler defined for packet " + packet.getClass());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handle(Packet2Handshake handshake) throws Exception
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handle(PacketCDClientStatus clientStatus) throws Exception
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handle(PacketFCEncryptionResponse encryptResponse) throws Exception
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handle(PacketFDEncryptionRequest encryptRequest) throws Exception
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handle(PacketFEPing ping) throws Exception
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user