Add server connection / proxying.
This commit is contained in:
parent
1c67b9b9c0
commit
b53f66b6ac
@ -61,7 +61,6 @@ public class BungeeCord {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void main(String[] args) throws IOException {
|
public static void main(String[] args) throws IOException {
|
||||||
System.out.println(Util.hex(15));
|
|
||||||
instance = new BungeeCord();
|
instance = new BungeeCord();
|
||||||
$().info("Enabled BungeeCord version " + instance.version);
|
$().info("Enabled BungeeCord version " + instance.version);
|
||||||
instance.start();
|
instance.start();
|
||||||
|
@ -172,13 +172,12 @@ public class Configuration {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public InetSocketAddress getHostFor(String user, InetSocketAddress requestedHost) {
|
public InetSocketAddress getHostFor(String user, String requestedHost) {
|
||||||
String entry = user + ";" + Util.getAddr(requestedHost);
|
String entry = user + ";" + requestedHost;
|
||||||
String host = requestedHost.getHostString();
|
|
||||||
|
|
||||||
String hostLine;
|
String hostLine;
|
||||||
if (forcedServers.containsKey(host)) {
|
if (forcedServers.containsKey(requestedHost)) {
|
||||||
hostLine = servers.get(forcedServers.get(host));
|
hostLine = servers.get(forcedServers.get(requestedHost));
|
||||||
} else {
|
} else {
|
||||||
hostLine = reconnectLocations.get(entry);
|
hostLine = reconnectLocations.get(entry);
|
||||||
}
|
}
|
||||||
@ -188,8 +187,13 @@ public class Configuration {
|
|||||||
return Util.getAddr(hostLine);
|
return Util.getAddr(hostLine);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setHostFor(UserConnection user, String host) {
|
||||||
|
String entry = user.username + ";" + Util.getAddr(user.handshake.host);
|
||||||
|
reconnectLocations.put(entry, host);
|
||||||
|
}
|
||||||
|
|
||||||
public InetSocketAddress getServer(String name) {
|
public InetSocketAddress getServer(String name) {
|
||||||
String hostline = servers.get(name);
|
String hostline = (name == null) ? defaultServerName : name;
|
||||||
if (hostline != null) {
|
if (hostline != null) {
|
||||||
return Util.getAddr(hostline);
|
return Util.getAddr(hostline);
|
||||||
} else {
|
} else {
|
||||||
|
@ -5,6 +5,7 @@ import java.io.OutputStream;
|
|||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import static net.md_5.bungee.Logger.$;
|
||||||
import net.md_5.bungee.packet.PacketFFKick;
|
import net.md_5.bungee.packet.PacketFFKick;
|
||||||
import net.md_5.bungee.packet.PacketInputStream;
|
import net.md_5.bungee.packet.PacketInputStream;
|
||||||
|
|
||||||
@ -12,13 +13,18 @@ import net.md_5.bungee.packet.PacketInputStream;
|
|||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class GenericConnection {
|
public class GenericConnection {
|
||||||
|
|
||||||
private final Socket socket;
|
protected final Socket socket;
|
||||||
private final PacketInputStream in;
|
protected final PacketInputStream in;
|
||||||
private final OutputStream out;
|
protected final OutputStream out;
|
||||||
|
public String username;
|
||||||
|
|
||||||
public void disconnect(String reason) {
|
public void disconnect(String reason) {
|
||||||
|
if (socket.isClosed()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
log("disconnected with " + reason);
|
||||||
try {
|
try {
|
||||||
out.write(new PacketFFKick(reason).getPacket());
|
out.write(new PacketFFKick("[Proxy] " + reason).getPacket());
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
} finally {
|
} finally {
|
||||||
try {
|
try {
|
||||||
@ -28,4 +34,8 @@ public class GenericConnection {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void log(String message) {
|
||||||
|
$().info(socket.getInetAddress() + ((username == null) ? " " : " [" + username + "] ") + message);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,13 +44,16 @@ public class InitialHandler implements Runnable {
|
|||||||
|
|
||||||
out.write(new PacketFCEncryptionResponse().getPacket());
|
out.write(new PacketFCEncryptionResponse().getPacket());
|
||||||
in = new PacketInputStream(new CipherInputStream(socket.getInputStream(), EncryptionUtil.getCipher(false, shared)));
|
in = new PacketInputStream(new CipherInputStream(socket.getInputStream(), EncryptionUtil.getCipher(false, shared)));
|
||||||
out = new BufferedOutputStream(new CipherOutputStream(socket.getOutputStream(), EncryptionUtil.getCipher(true, shared)), 5120);
|
out = new CipherOutputStream(socket.getOutputStream(), EncryptionUtil.getCipher(true, shared));
|
||||||
|
|
||||||
int ciphId = Util.getId(in.readPacket());
|
int ciphId = Util.getId(in.readPacket());
|
||||||
if (ciphId != 0xCD) {
|
if (ciphId != 0xCD) {
|
||||||
throw new KickException("Unable to receive encrypted client status");
|
throw new KickException("Unable to receive encrypted client status");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UserConnection userCon = new UserConnection(socket, in, out, handshake);
|
||||||
|
userCon.register();
|
||||||
|
userCon.connect(BungeeCord.instance.config.getHostFor(handshake.username, handshake.host));
|
||||||
break;
|
break;
|
||||||
case 0xFE:
|
case 0xFE:
|
||||||
throw new KickException(BungeeCord.instance.config.motd + ChatColor.COLOR_CHAR + BungeeCord.instance.connections.size() + ChatColor.COLOR_CHAR + BungeeCord.instance.config.maxPlayers);
|
throw new KickException(BungeeCord.instance.config.motd + ChatColor.COLOR_CHAR + BungeeCord.instance.connections.size() + ChatColor.COLOR_CHAR + BungeeCord.instance.config.maxPlayers);
|
||||||
@ -70,16 +73,10 @@ public class InitialHandler implements Runnable {
|
|||||||
} catch (IOException ioe) {
|
} catch (IOException ioe) {
|
||||||
} finally {
|
} finally {
|
||||||
try {
|
try {
|
||||||
|
out.flush();
|
||||||
socket.close();
|
socket.close();
|
||||||
} catch (IOException ioe2) {
|
} catch (IOException ioe2) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class KickException extends RuntimeException {
|
|
||||||
|
|
||||||
public KickException(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
8
src/main/java/net/md_5/bungee/KickException.java
Normal file
8
src/main/java/net/md_5/bungee/KickException.java
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
package net.md_5.bungee;
|
||||||
|
|
||||||
|
public class KickException extends RuntimeException {
|
||||||
|
|
||||||
|
public KickException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
@ -10,46 +10,63 @@ 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.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.PacketFFKick;
|
||||||
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 ServerConnection extends GenericConnection {
|
public class ServerConnection extends GenericConnection {
|
||||||
|
|
||||||
private final Packet1Login loginPacket;
|
public final Packet1Login loginPacket;
|
||||||
|
|
||||||
public ServerConnection(Socket socket, PacketInputStream in, OutputStream out, Packet1Login loginPacket) {
|
public ServerConnection(Socket socket, PacketInputStream in, OutputStream out, Packet1Login loginPacket) {
|
||||||
super(socket, in, out);
|
super(socket, in, out);
|
||||||
this.loginPacket = loginPacket;
|
this.loginPacket = loginPacket;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ServerConnection connect(InetSocketAddress address, Packet2Handshake handshake) throws Exception {
|
public static ServerConnection connect(InetSocketAddress address, Packet2Handshake handshake, boolean retry) {
|
||||||
Socket socket = new Socket();
|
try {
|
||||||
socket.connect(address, BungeeCord.instance.config.timeout);
|
Socket socket = new Socket();
|
||||||
BungeeCord.instance.setSocketOptions(socket);
|
socket.connect(address, BungeeCord.instance.config.timeout);
|
||||||
|
BungeeCord.instance.setSocketOptions(socket);
|
||||||
|
|
||||||
PacketInputStream in = new PacketInputStream(socket.getInputStream());
|
PacketInputStream in = new PacketInputStream(socket.getInputStream());
|
||||||
OutputStream out = socket.getOutputStream();
|
OutputStream out = socket.getOutputStream();
|
||||||
|
|
||||||
out.write(handshake.getPacket());
|
out.write(handshake.getPacket());
|
||||||
PacketFDEncryptionRequest encryptRequest = new PacketFDEncryptionRequest(in.readPacket());
|
PacketFDEncryptionRequest encryptRequest = new PacketFDEncryptionRequest(in.readPacket());
|
||||||
|
|
||||||
SecretKey myKey = EncryptionUtil.getSecret();
|
SecretKey myKey = EncryptionUtil.getSecret();
|
||||||
PublicKey pub = EncryptionUtil.getPubkey(encryptRequest);
|
PublicKey pub = EncryptionUtil.getPubkey(encryptRequest);
|
||||||
|
|
||||||
PacketFCEncryptionResponse response = new PacketFCEncryptionResponse(EncryptionUtil.getShared(myKey, pub), EncryptionUtil.encrypt(pub, encryptRequest.verifyToken));
|
PacketFCEncryptionResponse response = new PacketFCEncryptionResponse(EncryptionUtil.getShared(myKey, pub), EncryptionUtil.encrypt(pub, encryptRequest.verifyToken));
|
||||||
|
out.write(response.getPacket());
|
||||||
|
|
||||||
int ciphId = Util.getId(in.readPacket());
|
int ciphId = Util.getId(in.readPacket());
|
||||||
if (ciphId != 0xFC) {
|
if (ciphId != 0xFC) {
|
||||||
throw new RuntimeException("Server did not send encryption enable");
|
throw new RuntimeException("Server did not send encryption enable");
|
||||||
|
}
|
||||||
|
|
||||||
|
in = new PacketInputStream(new CipherInputStream(socket.getInputStream(), EncryptionUtil.getCipher(false, myKey)));
|
||||||
|
out = new CipherOutputStream(out, EncryptionUtil.getCipher(true, myKey));
|
||||||
|
|
||||||
|
out.write(new PacketCDClientStatus((byte) 0).getPacket());
|
||||||
|
byte[] loginResponse = in.readPacket();
|
||||||
|
if (Util.getId(loginResponse) == 0xFF) {
|
||||||
|
throw new KickException("[Kicked] " + new PacketFFKick(loginResponse).message);
|
||||||
|
}
|
||||||
|
Packet1Login login = new Packet1Login(loginResponse);
|
||||||
|
|
||||||
|
return new ServerConnection(socket, in, out, login);
|
||||||
|
} catch (KickException ex) {
|
||||||
|
throw ex;
|
||||||
|
} catch (Exception ex) {
|
||||||
|
InetSocketAddress def = BungeeCord.instance.config.getServer(null);
|
||||||
|
if (retry && address != def) {
|
||||||
|
return connect(def, handshake, false);
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException("Could not connect to target server");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
in = new PacketInputStream(new CipherInputStream(socket.getInputStream(), EncryptionUtil.getCipher(false, myKey)));
|
|
||||||
out = new CipherOutputStream(out, EncryptionUtil.getCipher(true, myKey));
|
|
||||||
|
|
||||||
out.write(new PacketCDClientStatus((byte) 0).getPacket());
|
|
||||||
Packet1Login login = new Packet1Login(in.readPacket());
|
|
||||||
|
|
||||||
return new ServerConnection(socket, in, out, login);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,115 @@
|
|||||||
package net.md_5.bungee;
|
package net.md_5.bungee;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
|
import java.util.Queue;
|
||||||
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
|
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.Packet3Chat;
|
||||||
import net.md_5.bungee.packet.PacketInputStream;
|
import net.md_5.bungee.packet.PacketInputStream;
|
||||||
|
|
||||||
public class UserConnection extends GenericConnection {
|
public class UserConnection extends GenericConnection {
|
||||||
|
|
||||||
private final Packet2Handshake handshake;
|
public final Packet2Handshake handshake;
|
||||||
|
public Queue<DefinedPacket> packetQueue = new ConcurrentLinkedQueue<>();
|
||||||
|
private ServerConnection server;
|
||||||
|
private UpstreamBridge upBridge;
|
||||||
|
private DownstreamBridge downBridge;
|
||||||
|
|
||||||
public UserConnection(Socket socket, PacketInputStream in, OutputStream out, Packet2Handshake handshake) {
|
public UserConnection(Socket socket, PacketInputStream in, OutputStream out, Packet2Handshake handshake) {
|
||||||
super(socket, in, out);
|
super(socket, in, out);
|
||||||
this.handshake = handshake;
|
this.handshake = handshake;
|
||||||
|
username = handshake.username;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void connect(InetSocketAddress serverAddr) {
|
||||||
|
try {
|
||||||
|
ServerConnection newServer = ServerConnection.connect(serverAddr, handshake, true);
|
||||||
|
if (server == null) {
|
||||||
|
out.write(newServer.loginPacket.getPacket());
|
||||||
|
upBridge = new UpstreamBridge();
|
||||||
|
upBridge.start();
|
||||||
|
}
|
||||||
|
if (downBridge != null) {
|
||||||
|
downBridge.interrupt();
|
||||||
|
}
|
||||||
|
downBridge = new DownstreamBridge();
|
||||||
|
server = newServer;
|
||||||
|
downBridge.start();
|
||||||
|
} catch (KickException ex) {
|
||||||
|
destory(ex.getMessage());
|
||||||
|
} catch (Exception ex) {
|
||||||
|
if (server == null) {
|
||||||
|
destory("Could not connect to server");
|
||||||
|
} else {
|
||||||
|
packetQueue.add(new Packet3Chat(ChatColor.YELLOW + "The server you selected is not up at the moment."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void register() {
|
||||||
|
BungeeCord.instance.connections.put(username, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void destory(String reason) {
|
||||||
|
if (BungeeCord.instance.isRunning) {
|
||||||
|
BungeeCord.instance.connections.remove(username);
|
||||||
|
}
|
||||||
|
if (upBridge != null) {
|
||||||
|
upBridge.interrupt();
|
||||||
|
}
|
||||||
|
if (downBridge != null) {
|
||||||
|
downBridge.interrupt();
|
||||||
|
}
|
||||||
|
disconnect(reason);
|
||||||
|
if (server != null) {
|
||||||
|
server.disconnect("Quitting");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class UpstreamBridge extends Thread {
|
||||||
|
|
||||||
|
public UpstreamBridge() {
|
||||||
|
super("Upstream Bridge - " + username);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
while (!interrupted()) {
|
||||||
|
try {
|
||||||
|
byte[] packet = in.readPacket();
|
||||||
|
server.out.write(packet);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
destory("Reached end of stream");
|
||||||
|
} catch (Exception ex) {
|
||||||
|
destory(Util.exception(ex));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class DownstreamBridge extends Thread {
|
||||||
|
|
||||||
|
public DownstreamBridge() {
|
||||||
|
super("Downstream Bridge - " + username);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
while (!interrupted()) {
|
||||||
|
try {
|
||||||
|
byte[] packet = server.in.readPacket();
|
||||||
|
out.write(packet);
|
||||||
|
out.flush();
|
||||||
|
} catch (IOException ex) {
|
||||||
|
destory("Reached end of stream");
|
||||||
|
} catch (Exception ex) {
|
||||||
|
destory(Util.exception(ex));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,9 +15,6 @@ public class Util {
|
|||||||
*/
|
*/
|
||||||
public static InetSocketAddress getAddr(String hostline) {
|
public static InetSocketAddress getAddr(String hostline) {
|
||||||
String[] split = hostline.split(":");
|
String[] split = hostline.split(":");
|
||||||
if (split.length < 2) {
|
|
||||||
throw new IllegalArgumentException("Invalid split format");
|
|
||||||
}
|
|
||||||
int port = DEFAULT_PORT;
|
int port = DEFAULT_PORT;
|
||||||
if (split.length > 1) {
|
if (split.length > 1) {
|
||||||
port = Integer.parseInt(split[1]);
|
port = Integer.parseInt(split[1]);
|
||||||
|
@ -2,6 +2,7 @@ package net.md_5.bungee.packet;
|
|||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.DataInputStream;
|
import java.io.DataInputStream;
|
||||||
|
import java.io.EOFException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import net.md_5.bungee.Util;
|
import net.md_5.bungee.Util;
|
||||||
@ -20,6 +21,9 @@ public class PacketInputStream {
|
|||||||
public byte[] readPacket() throws IOException {
|
public byte[] readPacket() throws IOException {
|
||||||
tracker.out.reset();
|
tracker.out.reset();
|
||||||
int id = tracker.read();
|
int id = tracker.read();
|
||||||
|
if (id == -1) {
|
||||||
|
throw new EOFException();
|
||||||
|
}
|
||||||
Packet codec = VanillaPackets.packets[id];
|
Packet codec = VanillaPackets.packets[id];
|
||||||
if (codec == null) {
|
if (codec == null) {
|
||||||
throw new RuntimeException("No Packet id: " + Util.hex(id));
|
throw new RuntimeException("No Packet id: " + Util.hex(id));
|
||||||
|
Loading…
Reference in New Issue
Block a user