Kicking & slight bungee plugin compatibility!!

poggers!!
This commit is contained in:
ayunami2000 2022-07-07 19:44:55 -04:00
parent 36bfd5469e
commit d79fe57f16
7 changed files with 251 additions and 95 deletions

View File

@ -0,0 +1,81 @@
package me.ayunami2000.ayungee;
import java.io.IOException;
import java.nio.ByteBuffer;
public class ChatHandler {
// return true to cancel being sent to server/client
public static boolean fromServer(Client client, String message) {
return false;
}
public static boolean fromClient(Client client, String message) {
if (!message.startsWith("/")) return false;
int ind = message.indexOf(' ');
String commandBase = message.substring(1, ind != -1 ? ind : message.length()).toLowerCase();
String args = ind != -1 ? message.substring(ind + 1) : "";
switch (commandBase) {
case "server":
if (args.isEmpty()) {
//usage msg
client.conn.send(new byte[] { 3, 0, 25, 0, (byte) 167, 0, 57, 0, 85, 0, 115, 0, 97, 0, 103, 0, 101, 0, 58, 0, 32, 0, 47, 0, 115, 0, 101, 0, 114, 0, 118, 0, 101, 0, 114, 0, 32, 0, 60, 0, 110, 0, 117, 0, 109, 0, 98, 0, 101, 0, 114, 0, 62 });
} else {
try {
int destServer = Integer.parseInt(args);
client.server = Math.max(0, Math.min(Main.servers.size() - 1, destServer));
try {
client.socket.close();
} catch (IOException ignored) {}
} catch (NumberFormatException e) {
//not a number
client.conn.send(new byte[] { 3, 0, 29, 0, (byte) 167, 0, 57, 0, 84, 0, 104, 0, 97, 0, 116, 0, 32, 0, 105, 0, 115, 0, 32, 0, 110, 0, 111, 0, 116, 0, 32, 0, 97, 0, 32, 0, 118, 0, 97, 0, 108, 0, 105, 0, 100, 0, 32, 0, 110, 0, 117, 0, 109, 0, 98, 0, 101, 0, 114, 0, 33 });
}
}
break;
/*
case "register":
break;
case "login":
break;
*/
default:
return false;
}
return true;
}
public static boolean serverChatMessage(Client client, byte[] packet) {
return fireChatMessage(client, true, packet);
}
public static boolean clientChatMessage(Client client, byte[] packet) {
return fireChatMessage(client, false, packet);
}
private static boolean fireChatMessage(Client client, boolean fromServer, byte[] packet) {
if (packet.length >= 3 && packet[0] == 3) {
ByteBuffer bb = ByteBuffer.wrap(packet);
bb.get();
int msgLen = bb.getShort(); //(short) ((msg[1] << 8) + msg[2] & 0xff);
if (msgLen >= 0) {
if (packet.length >= 3 + msgLen * 2) {
//byte[] chatBytes = new byte[msgLen];
//for (int i = 0; i < chatBytes.length; i++) chatBytes[i] = msg[4 + i * 2];
//String chatMsg = new String(chatBytes);
StringBuilder chatBuilder = new StringBuilder();
for (int i = 0; i < msgLen; i++) chatBuilder.append(bb.getChar());
String chatMsg = chatBuilder.toString();
if (fromServer) {
return fromServer(client, chatMsg);
} else {
return fromClient(client, chatMsg);
}
}
}
}
return false;
}
}

View File

@ -1,5 +1,7 @@
package me.ayunami2000.ayungee;
import org.java_websocket.WebSocket;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@ -14,6 +16,8 @@ public class Client {
public List<byte[]> msgCache =new ArrayList<>();
public WebSocket conn;
public String username;
public int server = 0;
@ -33,7 +37,12 @@ public class Client {
socketIn = sock.getInputStream();
}
public Client(String uname) {
public Client(WebSocket c, String uname) {
conn = c;
username = uname;
}
public String toString() {
return username + " (" + Main.getIp(conn) + ")";
}
}

View File

@ -14,7 +14,6 @@ import java.nio.file.StandardCopyOption;
import java.time.Instant;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
public class Main {
public static List<ServerItem> servers = new ArrayList<>();
@ -92,7 +91,7 @@ public class Main {
Files.copy(cc.getInputStream(), Paths.get("origin_blacklist.txt"), StandardCopyOption.REPLACE_EXISTING);
readUrlBlacklist();
} catch (IOException e) {
System.out.println("An error occurred attempting to update the origin blacklist!");
printMsg("An error occurred attempting to update the origin blacklist!");
}
try {
Thread.sleep(300000);
@ -161,16 +160,49 @@ public class Main {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
boolean running = true;
System.out.println("ayungee started!");
printMsg("ayungee started!");
while (running) {
//System.out.print("> ");
String cmd = reader.readLine();
String[] pieces = cmd.split(" ");
pieces[0] = pieces[0].toLowerCase();
switch (pieces[0]) {
case "help":
case "?":
System.out.println("help ; unban <ip> ; banip <ip> ; ban <username> ; send <username> <serverid> ; stop");
printMsg("help ; unban <ip> ; banip <ip> ; ban <username> ; kickip <ip> ; kick <username> ; send <username> <serverid> ; stop");
break;
case "kick":
if (pieces.length == 1) {
printMsg("Usage: " + pieces[0] + " <username>");
break;
}
//there should NEVER be duplicate usernames...
Client[] targetClientss = clients.values().stream().filter(client -> client.username.equals(pieces[1])).toArray(Client[]::new);
if (targetClientss.length == 0) targetClientss = clients.values().stream().filter(client -> client.username.equalsIgnoreCase(pieces[1])).toArray(Client[]::new);
if (targetClientss.length == 0) {
printMsg("Unable to find any user with that username!");
break;
}
for (Client targetClient : targetClientss) {
targetClient.conn.close();
printMsg("Successfully kicked user " + targetClient);
}
break;
case "kickip":
case "kick-ip":
if (pieces.length == 1) {
printMsg("Usage: " + pieces[0] + " <ip>");
break;
}
//there should NEVER be duplicate usernames...
Client[] targetClientsss = clients.values().stream().filter(client -> getIp(client.conn).equals(pieces[1])).toArray(Client[]::new);
if (targetClientsss.length == 0) {
printMsg("Unable to find any user with that IP!");
break;
}
for (Client targetClient : targetClientsss) {
targetClient.conn.close();
printMsg("Successfully kicked user " + targetClient);
}
break;
case "unban":
case "pardon":
@ -179,103 +211,105 @@ public class Main {
case "pardon-ip":
case "pardonip":
if (pieces.length == 1) {
System.out.println("Usage: " + pieces[0] + " <ip>");
printMsg("Usage: " + pieces[0] + " <ip>");
break;
}
if (bans.remove(pieces[1])) {
System.out.println("Successfully unbanned IP " + pieces[1]);
printMsg("Successfully unbanned IP " + pieces[1]);
saveBans();
} else {
System.out.println("IP " + pieces[1] + " is not banned!");
printMsg("IP " + pieces[1] + " is not banned!");
}
break;
case "ban":
if (pieces.length == 1) {
System.out.println("Usage: " + pieces[0] + " <username>");
printMsg("Usage: " + pieces[0] + " <username>");
break;
}
//there should NEVER be duplicate usernames...
Client[] targetClients = clients.values().stream().filter(client -> client.username.equals(pieces[1])).toArray(Client[]::new);
if (targetClients.length == 0) targetClients = clients.values().stream().filter(client -> client.username.equalsIgnoreCase(pieces[1])).toArray(Client[]::new);
if (targetClients.length == 0) {
System.out.println("Unable to find any user with that username! (note: they must be online)");
printMsg("Unable to find any user with that username! (note: they must be online)");
break;
}
for (Client targetClient : targetClients) {
WebSocket targetWebSocket = getKeysByValue(clients, targetClient).stream().findFirst().orElse(null);
if (targetWebSocket == null) {
System.out.println("An internal error occurred which should never happen! Oops...");
return;
}
WebSocket targetWebSocket = targetClient.conn;
String ipToBan = getIp(targetWebSocket);
if (bans.add(ipToBan)) {
System.out.println("Successfully banned user " + targetClient.username + " with IP " + ipToBan);
printMsg("Successfully banned user " + targetClient.username + " with IP " + ipToBan);
try {
saveBans();
} catch (IOException ignored) {}
} else {
System.out.println("IP " + ipToBan + " is already banned!");
printMsg("IP " + ipToBan + " is already banned!");
}
}
break;
case "ban-ip":
case "banip":
if (pieces.length == 1) {
System.out.println("Usage: " + pieces[0] + " <ip>");
printMsg("Usage: " + pieces[0] + " <ip>");
break;
}
if (bans.add(pieces[1])) {
System.out.println("Successfully banned IP " + pieces[1]);
Client[] targetClientssss = clients.values().stream().filter(client -> getIp(client.conn).equals(pieces[1])).toArray(Client[]::new);
for (Client client : targetClientssss) client.conn.close();
printMsg("Successfully banned IP " + pieces[1]);
saveBans();
} else {
System.out.println("IP " + pieces[1] + " is already banned!");
printMsg("IP " + pieces[1] + " is already banned!");
}
break;
case "send":
case "server":
if (pieces.length == 1 || pieces.length == 2) {
System.out.println("Usage: " + pieces[0] + " <username> <serverindex>");
printMsg("Usage: " + pieces[0] + " <username> <serverindex>");
break;
}
Client targetUser = clients.values().stream().filter(client -> client.username.equals(pieces[1])).findFirst().orElse(clients.values().stream().filter(client -> client.username.equalsIgnoreCase(pieces[1])).findFirst().orElse(null));
if (targetUser == null) {
System.out.println("Unable to find any user with that username!");
printMsg("Unable to find any user with that username!");
break;
}
// 99% sure don't need to worry about this
/*
if (!targetUser.hasLoginHappened) {
printMsg("This user is still logging in to a server; please wait until they have logged in to change their server!");
break;
}
*/
try {
int destServer = Integer.parseInt(pieces[2]);
targetUser.server = Math.max(0, Math.min(servers.size() - 1, destServer));
targetUser.socket.close();
printMsg("Successfully send user " + targetUser + " to server " + destServer + "!");
} catch (NumberFormatException e) {
System.out.println("That is not a valid number!");
printMsg("That is not a valid number!");
}
break;
case "stop":
case "end":
case "exit":
case "quit":
System.out.println("Stopping!");
printMsg("Stopping!");
running = false;
webSocketServer.stop(10);
System.exit(0);
break;
default:
System.out.println("Command not found!");
printMsg("Command not found!");
}
}
}
public static String getIp(WebSocket conn) {
return conn.getAttachment();
public static void printMsg(String msg) {
System.out.println(msg);
//System.out.print("> "); // todo: copy current input to after the >
}
// https://stackoverflow.com/a/2904266/6917520
public static <T, E> Set<T> getKeysByValue(Map<T, E> map, E value) {
return map.entrySet()
.stream()
.filter(entry -> Objects.equals(entry.getValue(), value))
.map(Map.Entry::getKey)
.collect(Collectors.toSet());
public static String getIp(WebSocket conn) {
return conn.getAttachment();
}
private static void saveBans() throws IOException {

View File

@ -0,0 +1,65 @@
package me.ayunami2000.ayungee;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
public class PluginMessages {
// return true to cancel being sent to server/client
public static boolean fromServer(Client client, String name, byte[] data) {
if (name.equals("BungeeCord")) { // eat all bungeecord messages
DataInputStream dataIn = new DataInputStream(new ByteArrayInputStream(data));
try {
String bungeeTag = dataIn.readUTF();
if (bungeeTag.equals("Connect")) { // actually send current player to server :D
String destServer = dataIn.readUTF();
try {
int destServerInt = Integer.parseInt(destServer);
client.server = Math.max(0, Math.min(Main.servers.size() - 1, destServerInt));
} catch (NumberFormatException ignored) {}
}
} catch (IOException e) {
// broken packet
}
return true;
}
return false;
}
public static boolean fromClient(Client client, String name, byte[] data) {
return Skins.setSkin(client.username, client.conn, name, data);
}
public static boolean serverPluginMessage(Client client, byte[] packet) {
return firePluginMessage(client, true, packet);
}
public static boolean clientPluginMessage(Client client, byte[] packet) {
return firePluginMessage(client, false, packet);
}
private static boolean firePluginMessage(Client client, boolean fromServer, byte[] packet) {
if(packet.length >= 3 && packet[0] == (byte) 250) {
ByteBuffer bb = ByteBuffer.wrap(packet);
bb.get();
int tagLen = bb.getShort();
if (tagLen < 0 || tagLen * 2 > packet.length - 3) return false;
StringBuilder tagBuilder = new StringBuilder();
for (int i = 0; i < tagLen; i++) tagBuilder.append(bb.getChar());
//int dataLen = bb.getShort();
int dataLen = bb.remaining() - 2; // - 2 for the 2 bytes specifying data length
String tag = tagBuilder.toString();
int offset = 3 + tagLen * 2 + 2; // - 2 for the 2 bytes specifying data length
byte[] msg = new byte[dataLen];
System.arraycopy(packet, offset, msg, 0, dataLen);
if (fromServer) {
return fromServer(client, tag, msg);
} else {
return fromClient(client, tag, msg);
}
}
return false;
}
}

View File

@ -4,7 +4,6 @@ import org.java_websocket.WebSocket;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
@ -16,21 +15,9 @@ public class Skins {
private static final int[] SKIN_DATA_SIZE = new int[] { 64*32*4, 64*64*4, -9, -9, 1, 64*64*4, -9 }; // 128 pixel skins crash clients
private static final int[] CAPE_DATA_SIZE = new int[] { 32*32*4, -9, 1 };
public static boolean setSkin(String user, WebSocket conn, byte[] initMsg) {
if(initMsg.length >= 3) {
public static boolean setSkin(String user, WebSocket conn, String tag, byte[] msg) {
if(msg.length >= 3) {
try {
ByteBuffer bb = ByteBuffer.wrap(initMsg);
bb.get();
int tagLen = bb.getShort();
if (!(tagLen >= 0 && tagLen < initMsg.length - 1)) return false;
StringBuilder tagBuilder = new StringBuilder();
for (int i = 0; i < tagLen; i++) tagBuilder.append(bb.getChar());
//int dataLen = bb.getShort();
int dataLen = bb.remaining() - 2;
String tag = tagBuilder.toString();
int offset = 3 + tagLen * 2 + 2;
byte[] msg = new byte[dataLen];
System.arraycopy(initMsg, offset, msg, 0, dataLen);
if("EAG|MySkin".equals(tag)) {
if(!skinCollection.containsKey(user)) {
int t = (int)msg[0] & 0xFF;

View File

@ -62,7 +62,7 @@ public class WebSocketProxy extends WebSocketServer {
public void onClose(WebSocket conn, int code, String reason, boolean remote) {
Client selfClient = Main.clients.remove(conn);
if (selfClient != null) {
System.out.println("Player " + selfClient.username + " (" + Main.getIp(conn) + ") left!");
Main.printMsg("Player " + selfClient + " left!");
Skins.removeSkin(selfClient.username);
if (selfClient.socket.isClosed()) {
try {
@ -97,31 +97,33 @@ public class WebSocketProxy extends WebSocketServer {
if (!conn.isOpen()) return;
byte[] msg = message.array();
if (!Main.clients.containsKey(conn)) {
if (msg.length > 3 && msg[1] == (byte) 69) {
// todo: it uses shorts dumbass, get with the system
short unameLen = (short) ((msg[2] << 8) + msg[3] & 0xff);
if (msg.length > 3 && msg[0] == 2 && msg[1] == 69) {
message.get();
message.get();
short unameLen = message.getShort();
if (unameLen < 3 || unameLen > 16) {
conn.close();
return;
}
if (msg.length < 5 + msg[3] * 2) {
if (msg.length < 5 + unameLen * 2) {
conn.close();
return;
}
byte[] uname = new byte[unameLen];
for (int i = 0; i < uname.length; i++) uname[i] = msg[5 + i * 2];
String username = new String(uname);
if (Main.clients.values().stream().anyMatch(client -> client.username.equals(username))) {
StringBuilder unameBuilder = new StringBuilder();
for (int i = 0; i < unameLen; i++) unameBuilder.append(message.getChar());
String username = unameBuilder.toString();
if (Main.clients.values().stream().anyMatch(client -> client.username.equals(username) || client.conn == conn)) {
conn.close();
return;
}
Client selfClient = new Client(username);
Client selfClient = new Client(conn, username);
Main.clients.put(conn, selfClient);
new Thread(() -> {
try {
while (conn.isOpen()) {
int currServer = selfClient.server;
selfClient.hasLoginHappened = false;
if (!selfClient.firstTime) Main.printMsg("Player " + selfClient + " joined server " + selfClient.server + "!");
ServerItem chosenServer = Main.servers.get(currServer);
Socket selfSocket = new Socket(chosenServer.host, chosenServer.port);
selfClient.setSocket(selfSocket);
@ -142,12 +144,16 @@ public class WebSocketProxy extends WebSocketServer {
} else {
continue;
}
if (data[0] == 1 && !selfClient.hasLoginHappened) selfClient.hasLoginHappened = true;
if (selfClient.firstTime && data[0] == 1) selfClient.clientEntityId = selfClient.serverEntityId = EntityMap.readInt(data, 1);
if (!selfClient.firstTime && data[0] == 1) {
if (ChatHandler.serverChatMessage(selfClient, data)) continue;
if (PluginMessages.serverPluginMessage(selfClient, data)) continue;
boolean loginPacket = data[0] == 1;
if (loginPacket && !selfClient.hasLoginHappened) selfClient.hasLoginHappened = true;
if (selfClient.firstTime && loginPacket) selfClient.clientEntityId = selfClient.serverEntityId = EntityMap.readInt(data, 1);
if (!selfClient.firstTime && loginPacket) {
selfClient.serverEntityId = EntityMap.readInt(data, 1);
// assume server is giving valid data; we don't have to validate it because it isn't a potentially malicious client
byte[] worldByte = new byte[data[6] * 2 + 2];
short worldByteLen = (short) ((data[5] << 8) + data[6] & 0xff);
byte[] worldByte = new byte[worldByteLen * 2 + 2];
System.arraycopy(data, 5, worldByte, 0, worldByte.length);
byte gamemode = data[worldByte.length + 5];
byte dimension = data[worldByte.length + 6];
@ -169,6 +175,7 @@ public class WebSocketProxy extends WebSocketServer {
}
EntityMap.rewrite(data, selfClient.serverEntityId, selfClient.clientEntityId);
if (conn.isOpen()) conn.send(data);
if (loginPacket) sendToServer(new byte[] { (byte) 250, 0, 8, 0, 82, 0, 69, 0, 71, 0, 73, 0, 83, 0, 84, 0, 69, 0, 82, 0, 10, 66, 117, 110, 103, 101, 101, 67, 111, 114, 100 }, selfClient);
}
if (conn.isOpen() && selfClient.server == currServer) conn.close();
if (!selfSocket.isClosed()) selfSocket.close();
@ -181,42 +188,15 @@ public class WebSocketProxy extends WebSocketServer {
}).start();
msg[1] = (byte) 61;
selfClient.handshake = msg;
System.out.println("Player " + username + " (" + Main.getIp(conn) + ") joined!");
Main.printMsg("Player " + selfClient + " joined!");
} else {
conn.close();
return;
}
}
Client currClient = Main.clients.get(conn);
if (msg.length >= 3 && msg[0] == 3) {
int msgLen = (short) ((msg[1] << 8) + msg[2] & 0xff);
if (msgLen != 0) {
if (msg.length >= 3 + msgLen * 2) {
byte[] chatBytes = new byte[msgLen];
for (int i = 0; i < chatBytes.length; i++) chatBytes[i] = msg[4 + i * 2];
String chatMsg = new String(chatBytes);
if (chatMsg.toLowerCase().startsWith("/server")) {
String msgArgs = chatMsg.substring(7 + (chatMsg.contains(" ") ? 1 : 0));
if (msgArgs.isEmpty()) {
//usage msg
conn.send(new byte[] { 3, 0, 25, 0, (byte) 167, 0, 57, 0, 85, 0, 115, 0, 97, 0, 103, 0, 101, 0, 58, 0, 32, 0, 47, 0, 115, 0, 101, 0, 114, 0, 118, 0, 101, 0, 114, 0, 32, 0, 60, 0, 110, 0, 117, 0, 109, 0, 98, 0, 101, 0, 114, 0, 62 });
} else {
try {
int destServer = Integer.parseInt(msgArgs);
currClient.server = Math.max(0, Math.min(Main.servers.size() - 1, destServer));
} catch (NumberFormatException e) {
//not a number
conn.send(new byte[] { 3, 0, 29, 0, (byte) 167, 0, 57, 0, 84, 0, 104, 0, 97, 0, 116, 0, 32, 0, 105, 0, 115, 0, 32, 0, 110, 0, 111, 0, 116, 0, 32, 0, 97, 0, 32, 0, 118, 0, 97, 0, 108, 0, 105, 0, 100, 0, 32, 0, 110, 0, 117, 0, 109, 0, 98, 0, 101, 0, 114, 0, 33 });
}
}
return; // don't send to underlying server
}
}
}
}
if (msg.length > 0 && msg[0] == (byte) 250) {
if (Skins.setSkin(currClient.username, conn, msg)) return;
}
if (ChatHandler.clientChatMessage(currClient, msg)) return;
if (PluginMessages.clientPluginMessage(currClient, msg)) return;
if (!currClient.firstTime && !currClient.hasLoginHappened && !(msg[0] == 1 || msg[0] == 2)) return;
if (currClient.socketOut == null || currClient.socket.isOutputShutdown()) {
currClient.msgCache.add(msg);