From 5c53842157c9a323e8036ce933d869ad12c2a994 Mon Sep 17 00:00:00 2001 From: ChomeNS <95471003+ChomeNS@users.noreply.github.com> Date: Wed, 14 Jun 2023 19:10:05 +0700 Subject: [PATCH] almost working but still not... :( the issue is in the VoiceChatPlugin at line 146 BTW also ignore the sussy debug lines --- .../chomens_bot/data/voiceChat/Codec.java | 7 + .../data/voiceChat/RawUdpPacket.java | 13 ++ .../chomens_bot/plugins/VoiceChatPlugin.java | 115 +++++++++++----- .../voiceChat/InitializationData.java | 44 ++++++ .../chomens_bot/voiceChat/NetworkMessage.java | 129 ++++++++++++++++++ .../chomens_bot/voiceChat/Packet.java | 9 ++ .../voiceChat/packets/AuthenticatePacket.java | 26 ++++ .../voiceChat/packets/KeepAlivePacket.java | 15 ++ .../voiceChat/packets/PingPacket.java | 26 ++++ .../voiceChat/packets/SecretPacket.java | 50 +++++++ 10 files changed, 400 insertions(+), 34 deletions(-) create mode 100644 src/main/java/land/chipmunk/chayapak/chomens_bot/data/voiceChat/Codec.java create mode 100644 src/main/java/land/chipmunk/chayapak/chomens_bot/data/voiceChat/RawUdpPacket.java create mode 100644 src/main/java/land/chipmunk/chayapak/chomens_bot/voiceChat/InitializationData.java create mode 100644 src/main/java/land/chipmunk/chayapak/chomens_bot/voiceChat/NetworkMessage.java create mode 100644 src/main/java/land/chipmunk/chayapak/chomens_bot/voiceChat/Packet.java create mode 100644 src/main/java/land/chipmunk/chayapak/chomens_bot/voiceChat/packets/AuthenticatePacket.java create mode 100644 src/main/java/land/chipmunk/chayapak/chomens_bot/voiceChat/packets/KeepAlivePacket.java create mode 100644 src/main/java/land/chipmunk/chayapak/chomens_bot/voiceChat/packets/PingPacket.java create mode 100644 src/main/java/land/chipmunk/chayapak/chomens_bot/voiceChat/packets/SecretPacket.java diff --git a/src/main/java/land/chipmunk/chayapak/chomens_bot/data/voiceChat/Codec.java b/src/main/java/land/chipmunk/chayapak/chomens_bot/data/voiceChat/Codec.java new file mode 100644 index 0000000..b00d579 --- /dev/null +++ b/src/main/java/land/chipmunk/chayapak/chomens_bot/data/voiceChat/Codec.java @@ -0,0 +1,7 @@ +package land.chipmunk.chayapak.chomens_bot.data.voiceChat; + +public enum Codec { + VOIP, + AUDIO, + RESTRICTED_LOWDELAY; +} diff --git a/src/main/java/land/chipmunk/chayapak/chomens_bot/data/voiceChat/RawUdpPacket.java b/src/main/java/land/chipmunk/chayapak/chomens_bot/data/voiceChat/RawUdpPacket.java new file mode 100644 index 0000000..2d88d5a --- /dev/null +++ b/src/main/java/land/chipmunk/chayapak/chomens_bot/data/voiceChat/RawUdpPacket.java @@ -0,0 +1,13 @@ +package land.chipmunk.chayapak.chomens_bot.data.voiceChat; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +import java.net.SocketAddress; + +@AllArgsConstructor +public class RawUdpPacket { + @Getter private final byte[] data; + @Getter private final SocketAddress socketAddress; + @Getter private final long timestamp; +} diff --git a/src/main/java/land/chipmunk/chayapak/chomens_bot/plugins/VoiceChatPlugin.java b/src/main/java/land/chipmunk/chayapak/chomens_bot/plugins/VoiceChatPlugin.java index 8b6603e..6c4da29 100644 --- a/src/main/java/land/chipmunk/chayapak/chomens_bot/plugins/VoiceChatPlugin.java +++ b/src/main/java/land/chipmunk/chayapak/chomens_bot/plugins/VoiceChatPlugin.java @@ -7,36 +7,31 @@ import com.github.steveice10.packetlib.Session; import com.github.steveice10.packetlib.packet.Packet; import io.netty.buffer.Unpooled; import land.chipmunk.chayapak.chomens_bot.Bot; -import land.chipmunk.chayapak.chomens_bot.util.AES; +import land.chipmunk.chayapak.chomens_bot.data.voiceChat.RawUdpPacket; import land.chipmunk.chayapak.chomens_bot.util.FriendlyByteBuf; -import lombok.AllArgsConstructor; -import lombok.Getter; +import land.chipmunk.chayapak.chomens_bot.voiceChat.InitializationData; +import land.chipmunk.chayapak.chomens_bot.voiceChat.NetworkMessage; +import land.chipmunk.chayapak.chomens_bot.voiceChat.packets.KeepAlivePacket; +import land.chipmunk.chayapak.chomens_bot.voiceChat.packets.PingPacket; +import land.chipmunk.chayapak.chomens_bot.voiceChat.packets.SecretPacket; +import land.chipmunk.chayapak.chomens_bot.voiceChat.packets.AuthenticatePacket; -import java.net.DatagramPacket; -import java.net.DatagramSocket; -import java.net.SocketAddress; -import java.net.SocketException; -import java.util.Arrays; +import java.net.*; // exists for some reason, still wip and will be finished in the next 69 years // and i prob implemented it in a wrong way lol // at least when you do `/voicechat test (bot username)` it will show `Client not connected` // instead of `(bot username) does not have Simple Voice Chat installed` public class VoiceChatPlugin extends Bot.Listener { - public static final byte MAGIC_BYTE = (byte) 0b11111111; - private final Bot bot; - private final ClientVoiceChatSocket socket; + private InitializationData initializationData; + private ClientVoiceChatSocket socket; + private InetAddress address; + private InetSocketAddress socketAddress; public VoiceChatPlugin(Bot bot) { this.bot = bot; - this.socket = new ClientVoiceChatSocket(); - try { - socket.open(); - } catch (Exception e) { - e.printStackTrace(); - } bot.addListener(this); } @@ -56,43 +51,102 @@ public class VoiceChatPlugin extends Bot.Listener { bot.session().send(new ServerboundCustomPayloadPacket( "voicechat:request_secret", - new byte[] { 0, 0, 0, 17 } // what are these bytes? + new FriendlyByteBuf(Unpooled.buffer()).writeInt(17).array() + )); + + bot.session().send(new ServerboundCustomPayloadPacket( + "voicechat:update_state", + new FriendlyByteBuf(Unpooled.buffer()).writeBoolean(false).array() )); } public void packetReceived(ClientboundCustomPayloadPacket _packet) { // sus /* - System.out.println(_packet.getChannel()); + System.out.println("\"" + _packet.getChannel() + "\""); System.out.println(Arrays.toString(_packet.getData())); System.out.println(new String(_packet.getData())); */ - // for some reason this entire part is not running if (_packet.getChannel().equals("voicechat:secret")) { + System.out.println("got the secret packet"); + + final byte[] bytes = _packet.getData(); + final FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.wrappedBuffer(bytes)); + + final SecretPacket secretPacket = new SecretPacket().fromBytes(buf); + + System.out.println(secretPacket.secret()); + System.out.println(secretPacket.serverPort()); + + initializationData = new InitializationData(bot.options().host(), secretPacket); + + try { + address = InetAddress.getByName(bot.options().host()); + socketAddress = new InetSocketAddress(address, initializationData.serverPort()); + } catch (UnknownHostException e) { + throw new RuntimeException(e); + } + + System.out.println("address " + address.getHostName()); + + socket = new ClientVoiceChatSocket(); + try { + socket.open(); + } catch (Exception e) { + e.printStackTrace(); + } + bot.executorService().submit(() -> { while (true) { - final RawUdpPacket packet = socket.read(); + try { + if (socket.isClosed()) continue; - if (packet == null) return; + System.out.println("reading packet"); + final NetworkMessage message = NetworkMessage.readPacket(socket.read(), initializationData); + System.out.println("DONE reading packet, this message will; prob nto woshow"); - byte[] data = packet.data(); - FriendlyByteBuf buf = new FriendlyByteBuf(Unpooled.wrappedBuffer(data)); + if (message == null) continue; - if (buf.readByte() != MAGIC_BYTE) return; - - // AES.decrypt(secret, payload) ? + if (message.packet() instanceof AuthenticatePacket) { + System.out.println("SERVER AUTHENTICATED FINALLYYYYYYYYYYYYYYYY"); + } else if (message.packet() instanceof PingPacket pingPacket) { + System.out.println("got ping packet"); + sendToServer(new NetworkMessage(pingPacket)); + } else if (message.packet() instanceof KeepAlivePacket) { + System.out.println("got keep alive packet"); + sendToServer(new NetworkMessage(new KeepAlivePacket())); + } else { + System.out.println("got " + message.packet().getClass().getName()); + } + } catch (Exception e) { + e.printStackTrace(); + } } }); } } + public void sendToServer (NetworkMessage message) throws Exception { + socket.send( + message.writeClient(initializationData), + socketAddress + ); + } + private static class VoiceChatSocketBase { private final byte[] BUFFER = new byte[4096]; public RawUdpPacket read (DatagramSocket socket) { try { DatagramPacket packet = new DatagramPacket(BUFFER, BUFFER.length); + + System.out.println("receiving packet"); + + // the problem is this next line, just this 1 line. it makes the entire thing froze socket.receive(packet); + + System.out.println("FINALLY DONE RECEIVING AAAAHHHHHHHHHHHHHHHHHHH"); + // Setting the timestamp after receiving the packet long timestamp = System.currentTimeMillis(); byte[] data = new byte[packet.getLength()]; @@ -138,11 +192,4 @@ public class VoiceChatPlugin extends Bot.Listener { return socket == null; } } - - @AllArgsConstructor - private static class RawUdpPacket { - @Getter private final byte[] data; - @Getter private final SocketAddress socketAddress; - @Getter private final long timestamp; - } } diff --git a/src/main/java/land/chipmunk/chayapak/chomens_bot/voiceChat/InitializationData.java b/src/main/java/land/chipmunk/chayapak/chomens_bot/voiceChat/InitializationData.java new file mode 100644 index 0000000..aea4a2c --- /dev/null +++ b/src/main/java/land/chipmunk/chayapak/chomens_bot/voiceChat/InitializationData.java @@ -0,0 +1,44 @@ +package land.chipmunk.chayapak.chomens_bot.voiceChat; + +import land.chipmunk.chayapak.chomens_bot.data.voiceChat.Codec; +import land.chipmunk.chayapak.chomens_bot.voiceChat.packets.SecretPacket; +import lombok.Getter; + +import java.util.UUID; + +public class InitializationData { + @Getter private final String serverIP; + @Getter private final int serverPort; + @Getter private final UUID playerUUID; + @Getter private final UUID secret; + @Getter private final Codec codec; + @Getter private final int mtuSize; + @Getter private final double voiceChatDistance; + @Getter private final int keepAlive; + @Getter private final boolean groupsEnabled; + @Getter private final boolean allowRecording; + + public InitializationData(String serverIP, SecretPacket secretPacket) { + this.serverIP = serverIP; + this.serverPort = secretPacket.serverPort(); + this.playerUUID = secretPacket.playerUUID(); + this.secret = secretPacket.secret(); + this.codec = secretPacket.codec(); + this.mtuSize = secretPacket.mtuSize(); + this.voiceChatDistance = secretPacket.voiceChatDistance(); + this.keepAlive = secretPacket.keepAlive(); + this.groupsEnabled = secretPacket.groupsEnabled(); + this.allowRecording = secretPacket.allowRecording(); + } + + private static class HostData { + private final String ip; + private final int port; + + public HostData(String ip, int port) { + this.ip = ip; + this.port = port; + } + } + +} diff --git a/src/main/java/land/chipmunk/chayapak/chomens_bot/voiceChat/NetworkMessage.java b/src/main/java/land/chipmunk/chayapak/chomens_bot/voiceChat/NetworkMessage.java new file mode 100644 index 0000000..ca058f0 --- /dev/null +++ b/src/main/java/land/chipmunk/chayapak/chomens_bot/voiceChat/NetworkMessage.java @@ -0,0 +1,129 @@ +package land.chipmunk.chayapak.chomens_bot.voiceChat; + +import io.netty.buffer.Unpooled; +import land.chipmunk.chayapak.chomens_bot.data.voiceChat.RawUdpPacket; +import land.chipmunk.chayapak.chomens_bot.util.AES; +import land.chipmunk.chayapak.chomens_bot.util.FriendlyByteBuf; +import land.chipmunk.chayapak.chomens_bot.voiceChat.packets.AuthenticatePacket; +import lombok.Getter; + +import javax.crypto.BadPaddingException; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import java.lang.reflect.InvocationTargetException; +import java.net.SocketAddress; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +public class NetworkMessage { + public static final byte MAGIC_BYTE = (byte) 0b11111111; + + @Getter private final long timestamp; + @Getter private Packet> packet; + @Getter private SocketAddress address; + + public NetworkMessage(long timestamp, Packet packet) { + this(timestamp); + this.packet = packet; + } + + public NetworkMessage(Packet packet) { + this(System.currentTimeMillis()); + this.packet = packet; + } + + private NetworkMessage(long timestamp) { + this.timestamp = timestamp; + } + + private static final Map>> packetRegistry; + + static { + packetRegistry = new HashMap<>(); +// packetRegistry.put((byte) 0x1, MicPacket.class); +// packetRegistry.put((byte) 0x2, PlayerSoundPacket.class); +// packetRegistry.put((byte) 0x3, GroupSoundPacket.class); +// packetRegistry.put((byte) 0x4, LocationSoundPacket.class); + packetRegistry.put((byte) 0x5, AuthenticatePacket.class); +// packetRegistry.put((byte) 0x6, AuthenticateAckPacket.class); +// packetRegistry.put((byte) 0x7, PingPacket.class); +// packetRegistry.put((byte) 0x8, KeepAlivePacket.class); +// packetRegistry.put((byte) 0x9, ConnectionCheckPacket.class); +// packetRegistry.put((byte) 0xA, ConnectionCheckAckPacket.class); + } + + public static NetworkMessage readPacket (RawUdpPacket packet, InitializationData initializationData) throws IllegalAccessException, InstantiationException, InvocationTargetException, NoSuchMethodException { + final byte[] data = packet.data(); + final FriendlyByteBuf b = new FriendlyByteBuf(Unpooled.wrappedBuffer(data)); + + if (b.readByte() != MAGIC_BYTE) return null; + + return readFromBytes(packet.socketAddress(), initializationData.secret(), b.readByteArray(), System.currentTimeMillis()); + } + + private static NetworkMessage readFromBytes(SocketAddress socketAddress, UUID secret, byte[] encryptedPayload, long timestamp) throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { + byte[] decrypt; + try { + decrypt = AES.decrypt(secret, encryptedPayload); + } catch (Exception e) { + e.printStackTrace(); + return null; + } + + FriendlyByteBuf buffer = new FriendlyByteBuf(Unpooled.wrappedBuffer(decrypt)); + byte packetType = buffer.readByte(); + final Class> packetClass = packetRegistry.get(packetType); + if (packetClass == null) { + System.out.println("packet type is null " + packetType); + return null; + } + Packet p = packetClass.getDeclaredConstructor().newInstance(); + + NetworkMessage message = new NetworkMessage(timestamp); + message.address = socketAddress; + message.packet = p.fromBytes(buffer); + + return message; + } + + private static byte getPacketType(Packet> packet) { + for (Map.Entry>> entry : packetRegistry.entrySet()) { + if (packet.getClass().equals(entry.getValue())) { + return entry.getKey(); + } + } + return -1; + } + + public byte[] writeClient(InitializationData data) throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException { + byte[] payload = write(data.secret()); + FriendlyByteBuf buffer = new FriendlyByteBuf(Unpooled.buffer(1 + 16 + payload.length)); + buffer.writeByte(MAGIC_BYTE); + buffer.writeUUID(data.playerUUID()); + buffer.writeByteArray(payload); + + byte[] bytes = new byte[buffer.readableBytes()]; + buffer.readBytes(bytes); + return bytes; + } + + public byte[] write(UUID secret) throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException { + FriendlyByteBuf buffer = new FriendlyByteBuf(Unpooled.buffer()); + + byte type = getPacketType(packet); + if (type < 0) { + throw new IllegalArgumentException("Packet type not found"); + } + + buffer.writeByte(type); + packet.toBytes(buffer); + + byte[] bytes = new byte[buffer.readableBytes()]; + buffer.readBytes(bytes); + return AES.encrypt(secret, bytes); + } +} diff --git a/src/main/java/land/chipmunk/chayapak/chomens_bot/voiceChat/Packet.java b/src/main/java/land/chipmunk/chayapak/chomens_bot/voiceChat/Packet.java new file mode 100644 index 0000000..6b60453 --- /dev/null +++ b/src/main/java/land/chipmunk/chayapak/chomens_bot/voiceChat/Packet.java @@ -0,0 +1,9 @@ +package land.chipmunk.chayapak.chomens_bot.voiceChat; + +import land.chipmunk.chayapak.chomens_bot.util.FriendlyByteBuf; + +public interface Packet> { + T fromBytes (FriendlyByteBuf buf); + + void toBytes (FriendlyByteBuf buf); +} diff --git a/src/main/java/land/chipmunk/chayapak/chomens_bot/voiceChat/packets/AuthenticatePacket.java b/src/main/java/land/chipmunk/chayapak/chomens_bot/voiceChat/packets/AuthenticatePacket.java new file mode 100644 index 0000000..f737fc9 --- /dev/null +++ b/src/main/java/land/chipmunk/chayapak/chomens_bot/voiceChat/packets/AuthenticatePacket.java @@ -0,0 +1,26 @@ +package land.chipmunk.chayapak.chomens_bot.voiceChat.packets; + +import land.chipmunk.chayapak.chomens_bot.util.FriendlyByteBuf; +import land.chipmunk.chayapak.chomens_bot.voiceChat.Packet; +import lombok.Getter; + +import java.util.UUID; + +public class AuthenticatePacket implements Packet { + @Getter private UUID playerUUID; + @Getter private UUID secret; + + @Override + public AuthenticatePacket fromBytes (FriendlyByteBuf buf) { + AuthenticatePacket packet = new AuthenticatePacket(); + packet.playerUUID = buf.readUUID(); + packet.secret = buf.readUUID(); + return packet; + } + + @Override + public void toBytes (FriendlyByteBuf buf) { + buf.writeUUID(playerUUID); + buf.writeUUID(secret); + } +} diff --git a/src/main/java/land/chipmunk/chayapak/chomens_bot/voiceChat/packets/KeepAlivePacket.java b/src/main/java/land/chipmunk/chayapak/chomens_bot/voiceChat/packets/KeepAlivePacket.java new file mode 100644 index 0000000..59207a3 --- /dev/null +++ b/src/main/java/land/chipmunk/chayapak/chomens_bot/voiceChat/packets/KeepAlivePacket.java @@ -0,0 +1,15 @@ +package land.chipmunk.chayapak.chomens_bot.voiceChat.packets; + +import land.chipmunk.chayapak.chomens_bot.util.FriendlyByteBuf; +import land.chipmunk.chayapak.chomens_bot.voiceChat.Packet; + +public class KeepAlivePacket implements Packet { + @Override + public KeepAlivePacket fromBytes(FriendlyByteBuf buf) { + return new KeepAlivePacket(); + } + + @Override + public void toBytes(FriendlyByteBuf buf) { + } +} diff --git a/src/main/java/land/chipmunk/chayapak/chomens_bot/voiceChat/packets/PingPacket.java b/src/main/java/land/chipmunk/chayapak/chomens_bot/voiceChat/packets/PingPacket.java new file mode 100644 index 0000000..85a8a54 --- /dev/null +++ b/src/main/java/land/chipmunk/chayapak/chomens_bot/voiceChat/packets/PingPacket.java @@ -0,0 +1,26 @@ +package land.chipmunk.chayapak.chomens_bot.voiceChat.packets; + +import land.chipmunk.chayapak.chomens_bot.util.FriendlyByteBuf; +import land.chipmunk.chayapak.chomens_bot.voiceChat.Packet; +import lombok.Getter; + +import java.util.UUID; + +public class PingPacket implements Packet { + @Getter private UUID id; + @Getter private long timestamp; + + @Override + public PingPacket fromBytes(FriendlyByteBuf buf) { + final PingPacket pingPacket = new PingPacket(); + pingPacket.id = buf.readUUID(); + pingPacket.timestamp = buf.readLong(); + return pingPacket; + } + + @Override + public void toBytes(FriendlyByteBuf buf) { + buf.writeUUID(id); + buf.writeLong(timestamp); + } +} diff --git a/src/main/java/land/chipmunk/chayapak/chomens_bot/voiceChat/packets/SecretPacket.java b/src/main/java/land/chipmunk/chayapak/chomens_bot/voiceChat/packets/SecretPacket.java new file mode 100644 index 0000000..38e69f4 --- /dev/null +++ b/src/main/java/land/chipmunk/chayapak/chomens_bot/voiceChat/packets/SecretPacket.java @@ -0,0 +1,50 @@ +package land.chipmunk.chayapak.chomens_bot.voiceChat.packets; + +import land.chipmunk.chayapak.chomens_bot.data.voiceChat.Codec; +import land.chipmunk.chayapak.chomens_bot.util.FriendlyByteBuf; +import land.chipmunk.chayapak.chomens_bot.voiceChat.Packet; +import lombok.Getter; + +import java.util.UUID; + +public class SecretPacket implements Packet { + @Getter private UUID secret; + @Getter private int serverPort; + @Getter private UUID playerUUID; + @Getter private Codec codec; + @Getter private int mtuSize; + @Getter private double voiceChatDistance; + @Getter private int keepAlive; + @Getter private boolean groupsEnabled; + @Getter private String voiceHost; + @Getter private boolean allowRecording; + + @Override + public SecretPacket fromBytes(FriendlyByteBuf buf) { + secret = buf.readUUID(); + serverPort = buf.readInt(); + playerUUID = buf.readUUID(); + codec = Codec.values()[buf.readByte()]; + mtuSize = buf.readInt(); + voiceChatDistance = buf.readDouble(); + keepAlive = buf.readInt(); + groupsEnabled = buf.readBoolean(); + voiceHost = buf.readUtf(); + allowRecording = buf.readBoolean(); + return this; + } + + @Override + public void toBytes (FriendlyByteBuf buf) { + buf.writeUUID(secret); + buf.writeInt(serverPort); + buf.writeUUID(playerUUID); + buf.writeByte(codec.ordinal()); + buf.writeInt(mtuSize); + buf.writeDouble(voiceChatDistance); + buf.writeInt(keepAlive); + buf.writeBoolean(groupsEnabled); + buf.writeUtf(voiceHost); + buf.writeBoolean(allowRecording); + } +}