diff --git a/src/main/java/com/github/manevolent/ts3j/command/response/AbstractCommandResponse.java b/src/main/java/com/github/manevolent/ts3j/command/response/AbstractCommandResponse.java index c2f1f67..141b2a6 100644 --- a/src/main/java/com/github/manevolent/ts3j/command/response/AbstractCommandResponse.java +++ b/src/main/java/com/github/manevolent/ts3j/command/response/AbstractCommandResponse.java @@ -39,11 +39,11 @@ public abstract class AbstractCommandResponse implements CommandResponse { return map(future.get()); } - protected void completeSuccess(Iterable commands) { + public void completeSuccess(Iterable commands) { future.complete(commands); } - protected void completeFailure(Throwable thrown) { + public void completeFailure(Throwable thrown) { future.completeExceptionally(thrown); } diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/socket/client/AbstractTeamspeakClientSocket.java b/src/main/java/com/github/manevolent/ts3j/protocol/socket/client/AbstractTeamspeakClientSocket.java index 1b67b35..8d93174 100644 --- a/src/main/java/com/github/manevolent/ts3j/protocol/socket/client/AbstractTeamspeakClientSocket.java +++ b/src/main/java/com/github/manevolent/ts3j/protocol/socket/client/AbstractTeamspeakClientSocket.java @@ -30,6 +30,7 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.*; import java.util.concurrent.*; +import java.util.function.Consumer; /** * Server/client common abstract client socket, used to interact with the UDP connection. Provides most of the protocol @@ -62,14 +63,16 @@ public abstract class AbstractTeamspeakClientSocket private CommandProcessor commandProcessor; + private Consumer exceptionHandler = throwable -> { + throwable.printStackTrace(); + }; + private Identity identity = null; private ClientConnectionState connectionState = null; private PacketTransformation transformation = new InitPacketTransformation(); private PacketHandler handler; - private int generationId = 0; private int clientId = 0; - private int packetId = 0; private long lastResponse; private long lastPing; @@ -98,12 +101,6 @@ public abstract class AbstractTeamspeakClientSocket protected AbstractTeamspeakClientSocket() { super(SocketRole.CLIENT); - try { - setState(ClientConnectionState.DISCONNECTED); - } catch (Exception e) { - throw new RuntimeException(e); - } - // Initialize reassembly queue for (PacketBodyType bodyType : PacketBodyType.values()) if (bodyType.isSplittable()) @@ -124,6 +121,12 @@ public abstract class AbstractTeamspeakClientSocket for (PacketKind kind : PacketKind.values()) { packetStatistics.put(kind, new PacketStatistics()); } + + try { + setState(ClientConnectionState.DISCONNECTED); + } catch (Exception e) { + throw new RuntimeException(e); + } } public PacketStatistics getStatistics(PacketKind kind) { @@ -158,6 +161,7 @@ public abstract class AbstractTeamspeakClientSocket if (state != this.connectionState) { Ts3Debugging.debug("State changing: " + state.name()); + boolean initialState = this.connectionState == null; boolean wasDisconnected = this.connectionState == ClientConnectionState.DISCONNECTED; if (wasDisconnected) setReading(true); @@ -168,8 +172,8 @@ public abstract class AbstractTeamspeakClientSocket connectionStateLock.notifyAll(); - if (state == ClientConnectionState.DISCONNECTED) - clientOptions.clear(); + if (state == ClientConnectionState.DISCONNECTED && !initialState) + onDisconnect(); Ts3Debugging.debug("State changed: " + state.name()); } @@ -189,6 +193,31 @@ public abstract class AbstractTeamspeakClientSocket } } + protected void onDisconnect() { + clientOptions.clear(); + + clientId = -1; + + readQueue.clear(); + + for (PacketBodyType bodyType : PacketBodyType.values()) + if (bodyType.isSplittable()) + reassemblyQueue.get(bodyType).reset(); + + pingQueue.clear(); + sendQueue.clear(); + sendQueueLow.clear(); + + for (PacketBodyType bodyType : PacketBodyType.values()) { + localSendCounter.get(bodyType).reset(); + remoteSendCounter.get(bodyType).reset(); + } + + for (PacketKind kind : PacketKind.values()) { + packetStatistics.put(kind, new PacketStatistics()); + } + } + public ClientConnectionState getState() { return connectionState; } @@ -517,7 +546,7 @@ public abstract class AbstractTeamspeakClientSocket packet.setBody(new PacketBodyFragment(networkPacket.getHeader().getType(), getRole().getIn())); } else { - if (packet.getHeader().getPacketFlag(HeaderFlag.COMPRESSED)) + if (packet.getHeader().getPacketFlag(HeaderFlag.COMPRESSED) && packet.getHeader().getType().isCompressible()) packet.setBody(new PacketBodyCompressed(networkPacket.getHeader().getType(), getRole().getIn())); Ts3Debugging.debug("[PROTOCOL] READ " + networkPacket.getHeader().getType().name()); @@ -528,7 +557,6 @@ public abstract class AbstractTeamspeakClientSocket // Finally, handle compressed bodies if (packet.getBody() instanceof PacketBodyCompressed) { - Ts3Debugging.debug("[PROTOCOL] DECOMPRESS " + networkPacket.getHeader().getType().name()); byte[] decompressed = QuickLZ.decompress(((PacketBodyCompressed) packet.getBody()).getCompressed()); @@ -591,16 +619,17 @@ public abstract class AbstractTeamspeakClientSocket } networkPacket.getHeader().setGeneration(generation); - - // Ignore voice and whisper, there is a decompression issue with them - if (networkPacket.getHeader().getPacketFlag(HeaderFlag.COMPRESSED) && ( - networkPacket.getHeader().getType() == PacketBodyType.VOICE || - networkPacket.getHeader().getType() == PacketBodyType.VOICE_WHISPER - )) - continue; - // Read packet (decrypt, decompress, etc) - Packet packet = readPacketIntl(networkPacket); + Packet packet; + try { + packet = readPacketIntl(networkPacket); + } catch (Exception ex) { + exceptionHandler.accept( + new Exception("Problem reading " + networkPacket.getHeader().getType().name(), ex) + ); + + continue; + } // Find if the packet must be acknowledged PacketBodyType ackType = packet @@ -705,10 +734,13 @@ public abstract class AbstractTeamspeakClientSocket currentRto = smoothedRtt + Math.max(0D, 4 * smoothedRttVar); } + // This is pulled out of the following IF block so that packet generation can increase correctly + boolean placed = counter != null && counter.put(packet.getHeader().getPacketId()); + if (packet.getHeader().getType().canResend() && packet.getHeader().getType() != PacketBodyType.INIT1) { // Find if we already acknowledged this packet if (counter != null) - handle = handle & counter.put(packet.getHeader().getPacketId()); + handle = handle & placed; } if (response != null) { @@ -805,22 +837,6 @@ public abstract class AbstractTeamspeakClientSocket this.clientId = clientId; } - public int getPacketId() { - return packetId; - } - - public void setPacketId(int packetId) { - this.packetId = packetId; - } - - public int getGenerationId() { - return generationId; - } - - public void setGenerationId(int generationId) { - this.generationId = generationId; - } - public String getNickname( ){ return getOption("client.nickname", String.class); } @@ -837,6 +853,14 @@ public abstract class AbstractTeamspeakClientSocket this.commandProcessor = commandProcessor; } + public Consumer getExceptionHandler() { + return exceptionHandler; + } + + public void setExceptionHandler(Consumer exceptionHandler) { + this.exceptionHandler = exceptionHandler; + } + private class NetworkHandler implements Runnable { @Override public void run() { @@ -864,10 +888,12 @@ public abstract class AbstractTeamspeakClientSocket try { readQueue.put(readPacket()); } catch (Throwable e) { - if (e instanceof InterruptedException) continue; - - e.printStackTrace(); + if (e instanceof InterruptedException) { + Thread.yield(); + continue; + } + exceptionHandler.accept(e); break; } } @@ -875,6 +901,7 @@ public abstract class AbstractTeamspeakClientSocket try { setState(ClientConnectionState.DISCONNECTED); } catch (Throwable e1) { + exceptionHandler.accept(e1); } } } @@ -991,6 +1018,7 @@ public abstract class AbstractTeamspeakClientSocket */ boolean put(int packetId); + void reset(); } public static class RemoteCounterZero implements RemoteCounter { @@ -1010,6 +1038,11 @@ public abstract class AbstractTeamspeakClientSocket return true; } + @Override + public void reset() { + // Do nothing + } + } public static class RemoteCounterFull implements RemoteCounter { @@ -1141,6 +1174,12 @@ public abstract class AbstractTeamspeakClientSocket return put(packetId); } + @Override + public void reset() { + for (int i = 0; i < bufferSize; i ++) + buffer[i] = null; + } + private boolean putRelative(int index, int generation) { Integer existing = buffer[index]; if (existing == null || existing != generation) { @@ -1203,6 +1242,8 @@ public abstract class AbstractTeamspeakClientSocket default Pair current() { return new Pair<>(getPacketId(), getGeneration()); } + + void reset(); } public static class LocalCounterZero implements LocalCounter { @@ -1230,6 +1271,11 @@ public abstract class AbstractTeamspeakClientSocket public void setGeneration(int i) { throw new UnsupportedOperationException(); } + + @Override + public void reset() { + // Do nothing + } } public static class LocalCounterFull implements LocalCounter { @@ -1284,6 +1330,13 @@ public abstract class AbstractTeamspeakClientSocket } } + @Override + public void reset() { + synchronized (sendLock) { + packetId = generationId = 0; + } + } + @Override public Pair next() { synchronized (sendLock) { @@ -1301,7 +1354,7 @@ public abstract class AbstractTeamspeakClientSocket private final Map queue = new LinkedHashMap<>(); private boolean state = false; - public Packet reassemble(Packet lastFragment) { + public Packet reassemble(Packet lastFragment) throws IOException { // Pull out all other packets in the queue before this one which would also be fragmented List reassemblyList = new ArrayList<>(); for (int packetId = lastFragment.getHeader().getPacketId();; packetId--) { @@ -1342,7 +1395,7 @@ public abstract class AbstractTeamspeakClientSocket return reassembledPacket; } - public Packet put(Packet packet) { + public Packet put(Packet packet) throws IOException { Packet reassembled; if (packet.getHeader().getPacketFlag(HeaderFlag.FRAGMENTED)) { @@ -1376,5 +1429,10 @@ public abstract class AbstractTeamspeakClientSocket public boolean isReassembling() { return state; } + + public void reset() { + state = false; + queue.clear(); + } } } diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/socket/client/LocalTeamspeakClientSocket.java b/src/main/java/com/github/manevolent/ts3j/protocol/socket/client/LocalTeamspeakClientSocket.java index 29b8b3c..14f2dec 100644 --- a/src/main/java/com/github/manevolent/ts3j/protocol/socket/client/LocalTeamspeakClientSocket.java +++ b/src/main/java/com/github/manevolent/ts3j/protocol/socket/client/LocalTeamspeakClientSocket.java @@ -214,6 +214,24 @@ public class LocalTeamspeakClientSocket super.setIdentity(identity); } + @Override + protected void onDisconnect() { + super.onDisconnect(); + + synchronized (commandSendLock) { + for (Integer id : new ArrayList<>(awaitingCommands.keySet())) { + ClientCommandResponse response = awaitingCommands.get(id); + response.completeFailure(new IOException("client disconnected")); + } + + awaitingCommands.clear(); + + calculateAcceptingReturnCode(); + } + + serverId = null; + } + /** * Initiates a connection to a server * @param remote remote sever to contact @@ -232,8 +250,7 @@ public class LocalTeamspeakClientSocket throw new IllegalStateException(connectionState.name()); setClientId(0); - setPacketId(0); - setGenerationId(0); + serverId = null; awaitingCommands.clear(); acceptingReturnCode = lastReturnCode = 0; @@ -320,11 +337,15 @@ public class LocalTeamspeakClientSocket Command command, Function processor ) throws IOException, TimeoutException { - int returnCode; final ClientCommandResponse> response; - // sync may not be necessary here synchronized (commandSendLock) { + if (getState() != ClientConnectionState.CONNECTED) + throw new IOException("not connected"); + + int returnCode; + + // sync may not be necessary here returnCode = lastReturnCode++; command.add(new CommandSingleParameter("return_code", Integer.toString(returnCode))); @@ -400,11 +421,10 @@ public class LocalTeamspeakClientSocket try { writePacket(voice); - } catch (IOException e) { + } catch (Exception e) { // All we are really concerned about here is a disconnection event, in which case the loop // will exit. - } catch (TimeoutException e) { - // Not really possible; as we might expect, voice doesn't have ACKs. + getExceptionHandler().accept(e); } } } diff --git a/src/main/java/com/github/manevolent/ts3j/util/QuickLZ.java b/src/main/java/com/github/manevolent/ts3j/util/QuickLZ.java index a47ede0..0050dbe 100644 --- a/src/main/java/com/github/manevolent/ts3j/util/QuickLZ.java +++ b/src/main/java/com/github/manevolent/ts3j/util/QuickLZ.java @@ -12,6 +12,7 @@ package com.github.manevolent.ts3j.util;// QuickLZ data compression library // // Version: 1.5.0 final +import java.io.IOException; import java.util.Base64; public final class QuickLZ @@ -309,182 +310,150 @@ public final class QuickLZ a[i + j] = (byte)(value >>> (j * 8)); } - static public byte[] decompress(byte[] source) - { - int size = (int)sizeDecompressed(source); - if (size > 65536) throw new IllegalArgumentException("decompression too large: " + size + " (" + Base64.getEncoder().encodeToString(source) + ")"); + static public byte[] decompress(byte[] source) throws IOException { + try { + int level = (source[0] >>> 2) & 0x3; - int src = headerLen(source); - int dst = 0; - long cword_val = 1; - byte[] destination = new byte[size]; - int[] hashtable = new int[4096]; - byte[] hash_counter = new byte[4096]; - int last_matchstart = size - UNCONDITIONAL_MATCHLEN - UNCOMPRESSED_END - 1; - int last_hashed = -1; - int hash; - int fetch = 0; + if (level != 1 && level != 3) + throw new IllegalArgumentException("unsupported QuickLZ level: " + level); - int level = (source[0] >>> 2) & 0x3; + int size = (int) sizeDecompressed(source); + if (size > 65536) + throw new IllegalArgumentException("decompression too large: " + size); - if (level != 1 && level != 3) - throw new IllegalArgumentException("unsupported QuickLZ level: " + level); + int src = headerLen(source); + int dst = 0; + long cword_val = 1; + byte[] destination = new byte[size]; + int[] hashtable = new int[4096]; + byte[] hash_counter = new byte[4096]; + int last_matchstart = size - UNCONDITIONAL_MATCHLEN - UNCOMPRESSED_END - 1; + int last_hashed = -1; + int hash; + int fetch = 0; - if ((source[0] & 1) != 1) - { - byte[] d2 = new byte[size]; - System.arraycopy(source, headerLen(source), d2, 0, size); - return d2; - } + if ((source[0] & 1) != 1) { + byte[] d2 = new byte[size]; + System.arraycopy(source, headerLen(source), d2, 0, size); + return d2; + } - for (;;) - { - if (cword_val == 1) - { - cword_val = fast_read(source, src, 4); - src += 4; - if (dst <= last_matchstart) - { - if(level == 1) - fetch = (int)fast_read(source, src, 3); - else - fetch = (int)fast_read(source, src, 4); - } - } + for (; ; ) { + if (cword_val == 1) { + cword_val = fast_read(source, src, 4); + src += 4; + if (dst <= last_matchstart) { + if (level == 1) + fetch = (int) fast_read(source, src, 3); + else + fetch = (int) fast_read(source, src, 4); + } + } - if ((cword_val & 1) == 1) - { - int matchlen; - int offset2; + if ((cword_val & 1) == 1) { + int matchlen; + int offset2; - cword_val = cword_val >>> 1; + cword_val = cword_val >>> 1; - if (level == 1) - { - hash = (fetch >>> 4) & 0xfff; - offset2 = hashtable[hash]; + if (level == 1) { + hash = (fetch >>> 4) & 0xfff; + offset2 = hashtable[hash]; - if ((fetch & 0xf) != 0) - { - matchlen = (fetch & 0xf) + 2; - src += 2; - } - else - { - matchlen = ((int)source[src + 2]) & 0xff; - src += 3; - } - } - else - { - int offset; + if ((fetch & 0xf) != 0) { + matchlen = (fetch & 0xf) + 2; + src += 2; + } else { + matchlen = ((int) source[src + 2]) & 0xff; + src += 3; + } + } else { + int offset; - if ((fetch & 3) == 0) - { - offset = (fetch & 0xff) >>> 2; - matchlen = 3; - src++; - } - else if ((fetch & 2) == 0) - { - offset = (fetch & 0xffff) >>> 2; - matchlen = 3; - src += 2; - } - else if ((fetch & 1) == 0) - { - offset = (fetch & 0xffff) >>> 6; - matchlen = ((fetch >>> 2) & 15) + 3; - src += 2; - } - else if ((fetch & 127) != 3) - { - offset = (fetch >>> 7) & 0x1ffff; - matchlen = ((fetch >>> 2) & 0x1f) + 2; - src += 3; - } - else - { - offset = (fetch >>> 15); - matchlen = ((fetch >>> 7) & 255) + 3; - src += 4; - } - offset2 = (int)(dst - offset); - } + if ((fetch & 3) == 0) { + offset = (fetch & 0xff) >>> 2; + matchlen = 3; + src++; + } else if ((fetch & 2) == 0) { + offset = (fetch & 0xffff) >>> 2; + matchlen = 3; + src += 2; + } else if ((fetch & 1) == 0) { + offset = (fetch & 0xffff) >>> 6; + matchlen = ((fetch >>> 2) & 15) + 3; + src += 2; + } else if ((fetch & 127) != 3) { + offset = (fetch >>> 7) & 0x1ffff; + matchlen = ((fetch >>> 2) & 0x1f) + 2; + src += 3; + } else { + offset = (fetch >>> 15); + matchlen = ((fetch >>> 7) & 255) + 3; + src += 4; + } + offset2 = (int) (dst - offset); + } - destination[dst + 0] = destination[offset2 + 0]; - destination[dst + 1] = destination[offset2 + 1]; - destination[dst + 2] = destination[offset2 + 2]; + destination[dst + 0] = destination[offset2 + 0]; + destination[dst + 1] = destination[offset2 + 1]; + destination[dst + 2] = destination[offset2 + 2]; - for (int i = 3; i < matchlen; i += 1) - { - destination[dst + i] = destination[offset2 + i]; - } - dst += matchlen; + for (int i = 3; i < matchlen; i += 1) { + destination[dst + i] = destination[offset2 + i]; + } + dst += matchlen; - if (level == 1) - { - fetch = (int)fast_read(destination, last_hashed + 1, 3); // destination[last_hashed + 1] | (destination[last_hashed + 2] << 8) | (destination[last_hashed + 3] << 16); - while (last_hashed < dst - matchlen) - { - last_hashed++; - hash = ((fetch >>> 12) ^ fetch) & (HASH_VALUES - 1); - hashtable[hash] = last_hashed; - hash_counter[hash] = 1; - fetch = fetch >>> 8 & 0xffff | (((int)destination[last_hashed + 3]) & 0xff) << 16; - } - fetch = (int)fast_read(source, src, 3); - } - else - { - fetch = (int)fast_read(source, src, 4); - } - last_hashed = dst - 1; - } - else - { - if (dst <= last_matchstart) - { - destination[dst] = source[src]; - dst += 1; - src += 1; - cword_val = cword_val >>> 1; + if (level == 1) { + fetch = (int) fast_read(destination, last_hashed + 1, 3); // destination[last_hashed + 1] | (destination[last_hashed + 2] << 8) | (destination[last_hashed + 3] << 16); + while (last_hashed < dst - matchlen) { + last_hashed++; + hash = ((fetch >>> 12) ^ fetch) & (HASH_VALUES - 1); + hashtable[hash] = last_hashed; + hash_counter[hash] = 1; + fetch = fetch >>> 8 & 0xffff | (((int) destination[last_hashed + 3]) & 0xff) << 16; + } + fetch = (int) fast_read(source, src, 3); + } else { + fetch = (int) fast_read(source, src, 4); + } + last_hashed = dst - 1; + } else { + if (dst <= last_matchstart) { + destination[dst] = source[src]; + dst += 1; + src += 1; + cword_val = cword_val >>> 1; - if (level == 1) - { - while (last_hashed < dst - 3) - { - last_hashed++; - int fetch2 = (int)fast_read(destination, last_hashed, 3); - hash = ((fetch2 >>> 12) ^ fetch2) & (HASH_VALUES - 1); - hashtable[hash] = last_hashed; - hash_counter[hash] = 1; - } - fetch = fetch >> 8 & 0xffff | (((int)source[src + 2]) & 0xff) << 16; - } - else - { - fetch = fetch >> 8 & 0xffff | (((int)source[src + 2]) & 0xff) << 16 | (((int)source[src + 3]) & 0xff) << 24; - } - } - else - { - while (dst <= size - 1) - { - if (cword_val == 1) - { - src += CWORD_LEN; - cword_val = 0x80000000L; - } + if (level == 1) { + while (last_hashed < dst - 3) { + last_hashed++; + int fetch2 = (int) fast_read(destination, last_hashed, 3); + hash = ((fetch2 >>> 12) ^ fetch2) & (HASH_VALUES - 1); + hashtable[hash] = last_hashed; + hash_counter[hash] = 1; + } + fetch = fetch >> 8 & 0xffff | (((int) source[src + 2]) & 0xff) << 16; + } else { + fetch = fetch >> 8 & 0xffff | (((int) source[src + 2]) & 0xff) << 16 | (((int) source[src + 3]) & 0xff) << 24; + } + } else { + while (dst <= size - 1) { + if (cword_val == 1) { + src += CWORD_LEN; + cword_val = 0x80000000L; + } - destination[dst] = source[src]; - dst++; - src++; - cword_val = cword_val >>> 1; - } - return destination; - } - } - } + destination[dst] = source[src]; + dst++; + src++; + cword_val = cword_val >>> 1; + } + return destination; + } + } + } + } catch (Throwable ex) { + throw new IOException(ex.getMessage() + " (" + Base64.getEncoder().encodeToString(source) + ")", ex); + } } } diff --git a/src/test/java/com/github/manevolent/ts3j/CompressionTest.java b/src/test/java/com/github/manevolent/ts3j/CompressionTest.java new file mode 100644 index 0000000..310f61b --- /dev/null +++ b/src/test/java/com/github/manevolent/ts3j/CompressionTest.java @@ -0,0 +1,33 @@ +package com.github.manevolent.ts3j; + +import com.github.manevolent.ts3j.util.QuickLZ; +import junit.framework.TestCase; + +import java.util.Random; + +/** + * The assertion check values SHOULD be retrieved from another known working codebase (i.e. Splamy's C# codebase) + * for the given input license string that is in no way associated with this codebase. + */ +public class CompressionTest extends TestCase { + + public static void main(String[] args) throws Exception { + new CompressionTest().testParser(); + } + + public void testParser() throws Exception { + byte[] b = new byte[600]; + Random r = new Random(0x100100); + + for (int i = 0; i < 1024; i ++) { + r.nextBytes(b); + QuickLZ.decompress(QuickLZ.compress(b, 1)); + } + + for (int i = 0; i < 1024; i ++) { + r.nextBytes(b); + QuickLZ.decompress(QuickLZ.compress(b, 3)); + } + } + +} diff --git a/src/test/java/com/github/manevolent/ts3j/GenerationTest.java b/src/test/java/com/github/manevolent/ts3j/GenerationTest.java new file mode 100644 index 0000000..ef45cc7 --- /dev/null +++ b/src/test/java/com/github/manevolent/ts3j/GenerationTest.java @@ -0,0 +1,56 @@ +package com.github.manevolent.ts3j; + +import com.github.manevolent.ts3j.protocol.Packet; +import com.github.manevolent.ts3j.protocol.ProtocolRole; +import com.github.manevolent.ts3j.protocol.header.ClientPacketHeader; +import com.github.manevolent.ts3j.protocol.packet.PacketBody2Command; +import com.github.manevolent.ts3j.protocol.packet.PacketBodyType; +import com.github.manevolent.ts3j.protocol.packet.transformation.PacketTransformation; +import com.github.manevolent.ts3j.protocol.socket.client.AbstractTeamspeakClientSocket; +import com.github.manevolent.ts3j.util.Pair; +import com.github.manevolent.ts3j.util.Ts3Debugging; +import junit.framework.TestCase; + +import java.nio.ByteBuffer; + +public class GenerationTest extends TestCase { + public static void main(String[] args) throws Exception { + new GenerationTest().testParser(); + } + + public void testParser() throws Exception { + Ts3Debugging.setEnabled(true); + + byte[] fakeMac = new byte[8], iv = new byte[16]; + PacketTransformation transformation = new PacketTransformation(iv, fakeMac); + + AbstractTeamspeakClientSocket.LocalCounterFull local = + new AbstractTeamspeakClientSocket.LocalCounterFull(65536, true); + + AbstractTeamspeakClientSocket.RemoteCounterFull remote = + new AbstractTeamspeakClientSocket.RemoteCounterFull(65536, 100); + + ClientPacketHeader header = new ClientPacketHeader(); + + for (int i = 0; i < 100_000; i ++) { + // CLIENT side + Pair pair = local.next(); + + header.setType(PacketBodyType.COMMAND); + header.setPacketId(pair.getKey()); + header.setGeneration(pair.getValue()); + + Packet packet = new Packet(ProtocolRole.CLIENT, header); + packet.setBody(new PacketBody2Command(ProtocolRole.CLIENT, "Hello world " + i + "!")); + + ByteBuffer sendBuffer = transformation.encrypt(packet); + + // SERVER side + header.setGeneration(remote.getGeneration(header.getPacketId())); + transformation.decrypt(header, sendBuffer, packet.getBody().getSize()); + + remote.put(header.getPacketId()); + } + } + +} diff --git a/src/test/java/com/github/manevolent/ts3j/ServerConnectionTest.java b/src/test/java/com/github/manevolent/ts3j/ServerConnectionTest.java index 5067f63..7414823 100644 --- a/src/test/java/com/github/manevolent/ts3j/ServerConnectionTest.java +++ b/src/test/java/com/github/manevolent/ts3j/ServerConnectionTest.java @@ -1,23 +1,14 @@ package com.github.manevolent.ts3j; - -import com.github.manevolent.ts3j.api.Client; -import com.github.manevolent.ts3j.audio.Microphone; -import com.github.manevolent.ts3j.enums.CodecType; +; import com.github.manevolent.ts3j.event.*; import com.github.manevolent.ts3j.identity.LocalIdentity; import com.github.manevolent.ts3j.protocol.client.ClientConnectionState; import com.github.manevolent.ts3j.protocol.socket.client.LocalTeamspeakClientSocket; import com.github.manevolent.ts3j.util.Ts3Debugging; -import java.io.IOException; import java.math.BigInteger; import java.net.InetSocketAddress; import java.util.Base64; -import java.util.Random; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeoutException; import static org.junit.Assert.assertEquals; @@ -40,9 +31,11 @@ public class ServerConnectionTest { client.setOption("client.hwid", "JAVAJAVAJAVA"); - client.connect(new InetSocketAddress( + client.connect( + new InetSocketAddress( "ts.teamlixo.net", - 9987), + 9987 + ), null, 10000L ); @@ -153,7 +146,7 @@ public class ServerConnectionTest { client.subscribeAll(); - Thread.sleep(1000000L); + Thread.sleep(100000000L); } catch (Throwable ex) { ex.printStackTrace(); }