Add server connection / proxying.

This commit is contained in:
md_5 2012-10-05 14:12:01 +10:00
parent 1c67b9b9c0
commit b53f66b6ac
9 changed files with 181 additions and 46 deletions

View File

@ -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();

View File

@ -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 {

View File

@ -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);
}
} }

View File

@ -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);
}
}
} }

View File

@ -0,0 +1,8 @@
package net.md_5.bungee;
public class KickException extends RuntimeException {
public KickException(String message) {
super(message);
}
}

View File

@ -10,20 +10,22 @@ 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) {
try {
Socket socket = new Socket(); Socket socket = new Socket();
socket.connect(address, BungeeCord.instance.config.timeout); socket.connect(address, BungeeCord.instance.config.timeout);
BungeeCord.instance.setSocketOptions(socket); BungeeCord.instance.setSocketOptions(socket);
@ -38,6 +40,7 @@ public class ServerConnection extends GenericConnection {
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) {
@ -48,8 +51,22 @@ public class ServerConnection extends GenericConnection {
out = new CipherOutputStream(out, EncryptionUtil.getCipher(true, myKey)); out = new CipherOutputStream(out, EncryptionUtil.getCipher(true, myKey));
out.write(new PacketCDClientStatus((byte) 0).getPacket()); out.write(new PacketCDClientStatus((byte) 0).getPacket());
Packet1Login login = new Packet1Login(in.readPacket()); 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); 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");
}
}
} }
} }

View File

@ -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));
}
}
}
} }
} }

View File

@ -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]);

View File

@ -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));