Got past encryption. Further refactoring as the scope of the project comes into view. Packet fragment reassembly in progress, can ACK commands right now and reassemble clientinitiv2

This commit is contained in:
Manevolent
2018-07-24 12:02:51 -04:00
parent 66cd3d6116
commit 24e6e366e5
13 changed files with 480 additions and 127 deletions
@@ -2,8 +2,12 @@ package com.github.manevolent.ts3j.protocol;
import com.github.manevolent.ts3j.identity.LocalIdentity;
import com.github.manevolent.ts3j.protocol.header.HeaderFlag;
import com.github.manevolent.ts3j.protocol.header.PacketHeader;
import com.github.manevolent.ts3j.protocol.packet.PacketType;
import com.github.manevolent.ts3j.protocol.packet.channel.PacketChannel;
import com.github.manevolent.ts3j.protocol.packet.handler.PacketHandler;
import com.github.manevolent.ts3j.protocol.socket.LocalTeamspeakSocket;
import com.github.manevolent.ts3j.protocol.packet.channel.ReassemblyQueue;
import com.github.manevolent.ts3j.util.Ts3Logging;
import java.io.IOException;
@@ -13,10 +17,10 @@ import java.net.SocketException;
public abstract class LocalEndpoint implements Endpoint {
private final LocalTeamspeakSocket socket;
private PacketHandler handler = null;
private LocalIdentity localIdentity = null;
private final ReassemblyQueue reassemblyQueue = new ReassemblyQueue();
private final Object runLock = new Object();
private Thread receiverThread;
private boolean running = false;
@@ -41,6 +45,9 @@ public abstract class LocalEndpoint implements Endpoint {
return socket;
}
protected abstract void acknowledge(NetworkPacket packet, PacketType ackType) throws IOException;
protected abstract PacketChannel getChannel(NetworkPacket packet);
public void setRunning(boolean running) {
if (this.running != running) {
this.running = running;
@@ -69,22 +76,6 @@ public abstract class LocalEndpoint implements Endpoint {
return socket.receive();
}
public void setHandler(PacketHandler handler) throws IOException {
if (this.handler != handler) {
if (this.handler != null) this.handler.onUnassigning();
this.handler = handler;
if (handler != null) handler.onAssigned();
}
}
protected void handlePacket(NetworkPacket packet) throws IOException {
if (packet.getHeader().getPacketFlag(HeaderFlag.FRAGMENTED)) {
} else {
if (this.handler != null) this.handler.handlePacket(packet);
}
}
public LocalIdentity getLocalIdentity() {
return localIdentity;
}
@@ -100,7 +91,21 @@ public abstract class LocalEndpoint implements Endpoint {
while (running) {
try {
handlePacket(receive());
NetworkPacket packet = receive();
PacketChannel channel = getChannel(packet);
if (channel == null) continue;
channel.insert(packet);
if (packet.getHeader().getType().getAcknolwedgedBy() != null)
acknowledge(packet, packet.getHeader().getType().getAcknolwedgedBy());
PacketHandler handler = channel.getHandler();
if (handler != null) {
while ((packet = channel.next()) != null) {
handler.handlePacket(packet);
}
}
} catch (Exception e) {
if (e instanceof InterruptedException) continue;
@@ -49,35 +49,43 @@ public final class NetworkPacket {
* Writes the existing data out to the specified byte buffer
* @param buffer Buffer to write to
*/
public void write(ByteBuffer buffer) {
public ByteBuffer write(ByteBuffer buffer) {
writeHeader(buffer);
writeBody(buffer);
return buffer;
}
public void writeHeader(ByteBuffer buffer) {
public ByteBuffer writeHeader(ByteBuffer buffer) {
if (getHeader() == null) throw new NullPointerException("header");
getHeader().setType(getPacket().getType());
getHeader().write(buffer);
return buffer;
}
public void writeBody(ByteBuffer buffer) {
public ByteBuffer writeBody(ByteBuffer buffer) {
if (getPacket() == null) throw new NullPointerException("packet");
getPacket().write(buffer);
return buffer;
}
/**
* Reads data from the specified byte buffer, filling in the packet body as needed.
* @param buffer Buffer to read from.
*/
public void read(ByteBuffer buffer) {
public ByteBuffer read(ByteBuffer buffer) {
readHeader(buffer);
readBody(buffer);
return buffer;
}
public void readHeader(ByteBuffer buffer) {
public ByteBuffer readHeader(ByteBuffer buffer) {
if (getHeader() == null) {
try {
setHeader(getRole().getHeaderClass().newInstance());
@@ -87,9 +95,11 @@ public final class NetworkPacket {
}
getHeader().read(buffer);
return buffer;
}
public void readBody(ByteBuffer buffer) {
public ByteBuffer readBody(ByteBuffer buffer) {
if (getPacket() == null) {
try {
setPacket(
@@ -103,6 +113,8 @@ public final class NetworkPacket {
}
getPacket().read(buffer);
return buffer;
}
/**
@@ -1,12 +1,15 @@
package com.github.manevolent.ts3j.protocol.client;
import com.github.manevolent.ts3j.command.Command;
import com.github.manevolent.ts3j.command.part.CommandSingleParameter;
import com.github.manevolent.ts3j.command.response.CommandResponse;
import com.github.manevolent.ts3j.identity.LocalIdentity;
import com.github.manevolent.ts3j.protocol.LocalEndpoint;
import com.github.manevolent.ts3j.protocol.NetworkPacket;
import com.github.manevolent.ts3j.protocol.ProtocolRole;
import com.github.manevolent.ts3j.protocol.SocketRole;
import com.github.manevolent.ts3j.protocol.header.ClientPacketHeader;
import com.github.manevolent.ts3j.protocol.packet.Packet6Ack;
import com.github.manevolent.ts3j.protocol.packet.PacketType;
import com.github.manevolent.ts3j.protocol.packet.channel.PacketChannel;
import com.github.manevolent.ts3j.protocol.packet.handler.client.LocalClientHandler;
import com.github.manevolent.ts3j.protocol.socket.LocalTeamspeakSocket;
import com.github.manevolent.ts3j.protocol.packet.Packet;
@@ -18,16 +21,16 @@ import java.net.SocketException;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.TimeoutException;
import java.util.function.Function;
public class LocalTeamspeakClient extends LocalEndpoint implements TeamspeakClient {
private final Object connectionStateLock = new Object();
private final Map<String, Object> clientOptions = new LinkedHashMap<>();
private final PacketChannel channel = new PacketChannel();
private LocalClientHandler handler;
private ClientConnectionState clientConnectionState = null;
private ClientConnectionState connectionState = null;
private int packetId = 0;
private int clientId = 0;
private int returnCodeIndex = 0;
public LocalTeamspeakClient(LocalTeamspeakSocket socket) {
@@ -36,9 +39,45 @@ public class LocalTeamspeakClient extends LocalEndpoint implements TeamspeakClie
if (socket.getSocketRole() != SocketRole.CLIENT)
throw new IllegalArgumentException("invalid socket role: " + socket.getSocketRole().name());
setClientConnectionState(ClientConnectionState.DISCONNECTED);
setConnectionState(ClientConnectionState.DISCONNECTED);
}
@Override
public SocketRole getRole() {
return SocketRole.CLIENT;
}
@Override
protected void acknowledge(NetworkPacket packet, PacketType ackType) throws IOException {
switch (ackType) {
case ACK:
Ts3Logging.debug("ACK " + packet.getHeader().getType().name());
Packet6Ack ack = new Packet6Ack(ProtocolRole.CLIENT);
ack.setPacketId(packet.getHeader().getPacketId());
send(ack);
break;
}
}
// Just return the client's single channel (to the server) here
@Override
protected PacketChannel getChannel(NetworkPacket packet) {
return channel;
}
@Override
public Map<String, Object> getOptions() {
return clientOptions;
}
/**
* Sends a command to the remote server.
* @param command Command to send.
* @return Command response object to track command response.
* @throws IOException
*/
public CommandResponse sendCommand(Command command) throws IOException {
if (command.willExpectResponse() && !isConnected())
throw new IOException("not connected");
@@ -64,48 +103,49 @@ public class LocalTeamspeakClient extends LocalEndpoint implements TeamspeakClie
this(new LocalTeamspeakSocket(SocketRole.CLIENT));
}
@Override
public SocketRole getRole() {
return SocketRole.CLIENT;
}
/**
* Sends a packet to the server
* @param packet Packet to send
* @throws IOException
*/
public void send(Packet packet) throws IOException {
// Construct header
ClientPacketHeader header = new ClientPacketHeader();
header.setClientId(clientId);
header.setPacketId(packetId);
packet.setHeaderValues(header);
getSocket().send(header, packet);
}
protected void setClientConnectionState(ClientConnectionState state) {
private void setConnectionState(ClientConnectionState state) {
synchronized (connectionStateLock) {
if (state != this.clientConnectionState) {
if (state != this.connectionState) {
Ts3Logging.debug("State changing: " + state.name());
boolean wasDisconnected = this.clientConnectionState == ClientConnectionState.DISCONNECTED;
boolean wasDisconnected = this.connectionState == ClientConnectionState.DISCONNECTED;
this.clientConnectionState = state;
this.connectionState = state;
if (wasDisconnected)
setRunning(true);
if (handler != null)
handler.handleConnectionStateChanging(state);
if (channel.getHandler() != null)
((LocalClientHandler)channel.getHandler()).handleConnectionStateChanging(state);
if (handler == null || handler.getClass() != state.getHandlerClass()) {
if (channel.getHandler() == null || channel.getHandler().getClass() != state.getHandlerClass()) {
Ts3Logging.debug("Assigning " + state.getHandlerClass().getName() + " handler...");
handler = state.createHandler(this);
try {
setHandler(handler);
channel.setHandler(state.createHandler(this));
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
if (this.clientConnectionState == ClientConnectionState.DISCONNECTED)
if (this.connectionState == ClientConnectionState.DISCONNECTED)
clientOptions.clear();
connectionStateLock.notifyAll();
@@ -115,17 +155,8 @@ public class LocalTeamspeakClient extends LocalEndpoint implements TeamspeakClie
}
}
public boolean isConnected() {
return clientConnectionState == ClientConnectionState.CONNECTED;
}
@Override
public Map<String, Object> getOptions() {
return clientOptions;
}
public ClientConnectionState getClientConnectionState() {
return clientConnectionState;
public ClientConnectionState getConnectionState() {
return connectionState;
}
private void joinConnectionState(ClientConnectionState state, long wait)
@@ -133,29 +164,40 @@ public class LocalTeamspeakClient extends LocalEndpoint implements TeamspeakClie
synchronized (connectionStateLock) {
long start = System.currentTimeMillis();
while (clientConnectionState != state && System.currentTimeMillis() - start < wait) {
while (connectionState != state && System.currentTimeMillis() - start < wait) {
connectionStateLock.wait(Math.max(0L, wait - (System.currentTimeMillis() - start)));
}
if (clientConnectionState != state)
if (connectionState != state)
throw new TimeoutException("timeout waiting for " + state.name() + " state");
}
}
public boolean isConnected() {
return connectionState == ClientConnectionState.CONNECTED;
}
/**
* Initiates a connection to a server
* @param remote remote sever to contact
* @param password server password
* @param timeout timeout, in milliseconds, to complete a connection.
* @throws IOException
*/
public void connect(InetSocketAddress remote, String password, long timeout) throws IOException {
try {
if (clientConnectionState != ClientConnectionState.DISCONNECTED)
throw new IllegalStateException(clientConnectionState.name());
if (connectionState != ClientConnectionState.DISCONNECTED)
throw new IllegalStateException(connectionState.name());
setOption("client.hostname", remote.getHostString());
setOption("client.password", password);
getSocket().connect(remote);
setClientConnectionState(ClientConnectionState.CONNECTING);
setConnectionState(ClientConnectionState.CONNECTING);
joinConnectionState(ClientConnectionState.CONNECTED, timeout);
} catch (Throwable e) {
setClientConnectionState(ClientConnectionState.DISCONNECTED);
setConnectionState(ClientConnectionState.DISCONNECTED);
throw new IOException(e);
}
@@ -1,26 +1,38 @@
package com.github.manevolent.ts3j.protocol.packet;
import com.github.manevolent.ts3j.protocol.ProtocolRole;
import com.github.manevolent.ts3j.util.Ts3Logging;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
public class Packet2Command extends Packet {
private String text;
public Packet2Command(ProtocolRole role) {
super(PacketType.COMMAND, role);
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
@Override
public void read(ByteBuffer buffer) {
text = Charset.forName("ASCII").decode(buffer).toString();
}
@Override
public void write(ByteBuffer buffer) {
buffer.put(text.getBytes(Charset.forName("ASCII")));
}
@Override
public int getSize() {
return 0;
return text.length();
}
}
@@ -0,0 +1,37 @@
package com.github.manevolent.ts3j.protocol.packet;
import com.github.manevolent.ts3j.protocol.ProtocolRole;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
public class Packet6Ack extends Packet {
private int packetId;
public Packet6Ack(ProtocolRole role) {
super(PacketType.ACK, role);
}
public int getPacketId() {
return packetId;
}
public void setPacketId(int packetId) {
this.packetId = packetId;
}
@Override
public void read(ByteBuffer buffer) {
packetId = buffer.getShort() & 0x0000FFFF;
}
@Override
public void write(ByteBuffer buffer) {
buffer.putShort((short) (getPacketId() & 0xFFFFFF));
}
@Override
public int getSize() {
return 2;
}
}
@@ -0,0 +1,38 @@
package com.github.manevolent.ts3j.protocol.packet;
import com.github.manevolent.ts3j.protocol.ProtocolRole;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
public class PacketFragment extends Packet {
private byte[] fragment;
public PacketFragment( PacketType type, ProtocolRole role) {
super(type, role);
}
public byte[] getFragment() {
return fragment;
}
public void setFragment(byte[] fragment) {
this.fragment = fragment;
}
@Override
public void read(ByteBuffer buffer) {
fragment = new byte[buffer.remaining()];
buffer.get(fragment);
}
@Override
public void write(ByteBuffer buffer) {
buffer.put(fragment);
}
@Override
public int getSize() {
return fragment.length;
}
}
@@ -5,12 +5,11 @@ public enum PacketType {
VOICE_WHISPER(0x1, null, null, false, false, false, false),
PONG(0x5, null, null, false, false, false, false),
PING(0x4, null, PONG, false, false, false, false),
ACK(0x6, null, null, true, true, false, false),
ACK(0x6, Packet6Ack.class, null, true, true, false, false),
ACK_LOW(0x7, null, null, true, true, false, false),
INIT1(0x8, Packet8Init1.class, null, false, true, false, false),
COMMAND_LOW(0x3, null, ACK_LOW, true, true, true, true),
COMMAND(0x2, Packet2Command.class, ACK, true, true, true, true)
;
COMMAND(0x2, Packet2Command.class, ACK, true, true, true, true);
private final int index;
private final Class<? extends Packet> packetClass;
@@ -0,0 +1,50 @@
package com.github.manevolent.ts3j.protocol.packet.channel;
import com.github.manevolent.ts3j.protocol.NetworkPacket;
import com.github.manevolent.ts3j.protocol.header.HeaderFlag;
import com.github.manevolent.ts3j.protocol.packet.handler.PacketHandler;
import java.io.IOException;
import java.util.NoSuchElementException;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
public final class PacketChannel {
private final Queue<NetworkPacket> receive = new ConcurrentLinkedQueue<>();
private final ReassemblyQueue reassemblyQueue = new ReassemblyQueue();
private PacketHandler handler;
/**
* Inserts a packet into the receive queue.
* @param networkPacket Packet to insert.
*/
public void insert(NetworkPacket networkPacket) {
if ((networkPacket = reassemblyQueue.put(networkPacket)) != null)
receive.add(networkPacket);
}
/**
* Finds the next packet in the receive queue available for dequeue.
* @return Network packet, null otherwise
*/
public NetworkPacket next() {
try {
return receive.remove();
} catch (NoSuchElementException ex) {
return null;
}
}
public PacketHandler getHandler() {
return handler;
}
public void setHandler(PacketHandler handler) throws IOException {
if (this.handler != handler) {
if (this.handler != null) this.handler.onUnassigning();
this.handler = handler;
if (handler != null) handler.onAssigned();
}
}
}
@@ -0,0 +1,88 @@
package com.github.manevolent.ts3j.protocol.packet.channel;
import com.github.manevolent.ts3j.protocol.NetworkPacket;
import com.github.manevolent.ts3j.protocol.header.HeaderFlag;
import com.github.manevolent.ts3j.protocol.packet.PacketFragment;
import com.github.manevolent.ts3j.protocol.packet.PacketType;
import java.nio.ByteBuffer;
import java.util.*;
public class ReassemblyQueue {
private final Map<Integer, NetworkPacket> queue = new LinkedHashMap<>();
private final Map<PacketType, Boolean> fragmentation = new LinkedHashMap<>();
public NetworkPacket reassemble(NetworkPacket lastFragment) {
// Pull out all other packets in the queue before this one which would also be fragmented
List<NetworkPacket> reassemblyList = new ArrayList<>();
for (int packetId = lastFragment.getHeader().getPacketId();; packetId--) {
NetworkPacket olderPacket = queue.get(packetId);
if (olderPacket == null)
break; // Chain breaks here
else if (olderPacket.getHeader().getType() != lastFragment.getHeader().getType())
continue; // skip
else {
reassemblyList.add(olderPacket);
fragmentation.remove(packetId); // Don't reuse this packet
}
}
// Reverse the collection
Collections.reverse(reassemblyList);
// Rebuild a master packet from the contents of all previous packet fragments
int totalLength = reassemblyList.stream().mapToInt(x -> x.getPacket().getSize()).sum() +
lastFragment.getPacket().getSize();
if (totalLength <= 0) throw new IllegalArgumentException("reassembly too small: " + totalLength);
ByteBuffer reassemblyBuffer = ByteBuffer.allocate(totalLength);
for (NetworkPacket old : reassemblyList)
old.writeBody(reassemblyBuffer);
NetworkPacket firstPacket = reassemblyList.get(0);
NetworkPacket reassembledPacket = new NetworkPacket(firstPacket.getRole());
reassembledPacket.setHeader(firstPacket.getHeader());
reassembledPacket.getHeader().setPacketFlag(HeaderFlag.FRAGMENTED, false);
reassembledPacket.readBody((ByteBuffer) reassemblyBuffer.position(0));
return reassembledPacket;
}
public NetworkPacket put(NetworkPacket networkPacket) {
Boolean fragmentState = fragmentation.get(networkPacket.getHeader().getType());
if (networkPacket.getHeader().getPacketFlag(HeaderFlag.FRAGMENTED)) {
if (!(networkPacket.getPacket() instanceof PacketFragment))
throw new IllegalArgumentException("packet fragment object is not instance of PacketFragment class");
boolean fragmentationEnded = false;
if (fragmentState == null)
fragmentation.put(networkPacket.getHeader().getType(), fragmentState = true);
else if (fragmentState) {
// Fragmentation end
fragmentationEnded = true;
fragmentation.put(networkPacket.getHeader().getType(), fragmentState = false);
}
queue.put(networkPacket.getHeader().getPacketId(), networkPacket);
if (fragmentationEnded)
return reassemble(networkPacket);
return null;
} else {
if (fragmentState != null && fragmentState) {
queue.put(networkPacket.getHeader().getPacketId(), networkPacket);
return null;
} else {
return networkPacket;
}
}
}
}
@@ -52,7 +52,7 @@ public class LocalClientHandlerConnecting extends LocalClientHandler {
@Override
public void handlePacket(NetworkPacket packet) throws IOException {
Ts3Logging.debug("Connecting: handling " + packet.getClass().getSimpleName());
Ts3Logging.debug("Connecting: handling " + packet.getPacket().getClass().getSimpleName());
if (packet.getPacket() instanceof Packet8Init1) {
Packet8Init1 init1 = (Packet8Init1) packet.getPacket();
@@ -136,7 +136,7 @@ public class LocalClientHandlerConnecting extends LocalClientHandler {
}
} else if (packet.getPacket() instanceof Packet2Command) {
Ts3Logging.debug(((Packet2Command) packet.getPacket()).getText());
}
}
}
@@ -1,5 +1,6 @@
package com.github.manevolent.ts3j.protocol.packet.transformation;
import com.github.manevolent.ts3j.protocol.NetworkPacket;
import com.github.manevolent.ts3j.protocol.ProtocolRole;
import com.github.manevolent.ts3j.protocol.header.PacketHeader;
import com.github.manevolent.ts3j.util.Ts3Logging;
@@ -17,6 +18,7 @@ import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class PacketTransformation {
private static final int MAC_LEN = 8;
private static MessageDigest SHA256;
{
@@ -70,33 +72,34 @@ public class PacketTransformation {
return new Pair<>(key, nonce);
}
public byte[] encrypt(PacketHeader header, byte[] body) {
return transform(header, body, true);
}
public ByteBuffer encrypt(NetworkPacket packet) {
Pair<byte[], byte[]> parameters = computeParameters(packet.getHeader());
public byte[] decrypt(PacketHeader header, byte[] body) {
return transform(header, body, false);
}
// Write header (this is temporary)
ByteBuffer headerBuffer = packet.writeHeader(ByteBuffer.allocate(packet.getHeader().getSize()));
private byte[] transform(PacketHeader header, byte[] body, boolean encrypt) {
Pair<byte[], byte[]> parameters = computeParameters(header);
// Get the header without a MAC for the associated text field
byte[] headerWithoutMac = new byte[packet.getHeader().getSize() - MAC_LEN];
System.arraycopy(headerBuffer.array(), MAC_LEN, headerWithoutMac, 0, headerWithoutMac.length);
byte[] associatedText = new byte[8];
CipherParameters cipherParameters = new AEADParameters(new KeyParameter(
parameters.getKey()),
8 * associatedText.length,
CipherParameters cipherParameters = new AEADParameters(
new KeyParameter(parameters.getKey()),
8 * MAC_LEN,
parameters.getValue(),
encrypt ? associatedText : header.getMac()
headerWithoutMac
);
int dataLen = packet.getPacket().getSize();
ByteBuffer packetBuffer = packet.writeBody(ByteBuffer.allocate(dataLen));
byte[] result;
int len;
synchronized (cipher){
cipher.init(encrypt, cipherParameters);
result = new byte[cipher.getOutputSize(body.length)];
len = cipher.processBytes(body, 0, body.length, result, 0);
synchronized (cipher) {
cipher.init(true, cipherParameters);
result = new byte[cipher.getOutputSize(dataLen)];
len = cipher.processBytes(packetBuffer.array(), 0, dataLen, result, 0);
try {
len += cipher.doFinal(result, len);
@@ -105,12 +108,56 @@ public class PacketTransformation {
}
}
byte[] output = new byte[len];
System.arraycopy(result, 0, output, 0, len);
ByteBuffer outputBuffer = ByteBuffer.allocate(MAC_LEN + headerWithoutMac.length + (len - MAC_LEN));
if (encrypt)
header.setMac(associatedText);
// MAC
outputBuffer.put(result, len - MAC_LEN, MAC_LEN);
return output;
// Rest of header
outputBuffer.put(headerWithoutMac);
// Encrypted body
outputBuffer.put(result, 0, len - MAC_LEN);
return outputBuffer;
}
public byte[] decrypt(PacketHeader header, ByteBuffer buffer, int dataLen) {
Pair<byte[], byte[]> parameters = computeParameters(header);
byte[] headerWithoutMac = new byte[header.getSize() - MAC_LEN];
System.arraycopy(buffer.array(), MAC_LEN, headerWithoutMac, 0, headerWithoutMac.length);
// Get the header without a MAC for the associated text field
CipherParameters cipherParameters = new AEADParameters(
new KeyParameter(parameters.getKey()),
8 * MAC_LEN,
parameters.getValue(),
headerWithoutMac
);
byte[] result;
int len;
synchronized (cipher) {
cipher.init(false, cipherParameters);
result = new byte[cipher.getOutputSize(dataLen + MAC_LEN)];
len = cipher.processBytes(buffer.array(), header.getSize(), dataLen, result, 0);
len += cipher.processBytes(buffer.array(), 0, MAC_LEN, result, len);
try {
len += cipher.doFinal(result, len);
} catch (InvalidCipherTextException e) {
throw new RuntimeException(e);
}
if (len != dataLen)
throw new IllegalArgumentException(len + " != " + dataLen);
}
return result;
}
}
@@ -5,6 +5,8 @@ import com.github.manevolent.ts3j.protocol.SocketRole;
import com.github.manevolent.ts3j.protocol.header.HeaderFlag;
import com.github.manevolent.ts3j.protocol.header.PacketHeader;
import com.github.manevolent.ts3j.protocol.packet.Packet;
import com.github.manevolent.ts3j.protocol.packet.PacketFragment;
import com.github.manevolent.ts3j.protocol.packet.PacketType;
import com.github.manevolent.ts3j.util.Ts3Logging;
import java.io.IOException;
@@ -78,28 +80,25 @@ public class LocalTeamspeakSocket extends AbstractTeamspeakSocket implements Tea
header.getRole().name() + " != " +
getSocketRole().getOut().name());
// Ensure type matches
header.setType(packet.getType());
// Construct a network packet
NetworkPacket networkPacket = new NetworkPacket(header.getRole());
networkPacket.setHeader(header);
networkPacket.setPacket(packet);
if (networkPacket.getHeader().getMac() == null) {
// Generate mac?
}
ByteBuffer outputBuffer = ByteBuffer.allocate(networkPacket.getSize());
outputBuffer.order(ByteOrder.BIG_ENDIAN);
networkPacket.writeHeader(outputBuffer);
// Flush to a buffer
ByteBuffer outputBuffer;
if (networkPacket.getHeader().getType().isEncrypted() &&
!networkPacket.getHeader().getPacketFlag(HeaderFlag.UNENCRYPTED)) {
ByteBuffer encryptionBuffer = ByteBuffer.allocate(500);
networkPacket.writeBody(encryptionBuffer);
byte[] unencryptedBody = new byte[encryptionBuffer.position()];
System.arraycopy(encryptionBuffer.array(), 0, unencryptedBody, 0, unencryptedBody.length);
outputBuffer.put(getPacketTransformation().encrypt(header, unencryptedBody));
outputBuffer = getPacketTransformation().encrypt(networkPacket);
} else {
outputBuffer = ByteBuffer.allocate(networkPacket.getSize());
outputBuffer.order(ByteOrder.BIG_ENDIAN);
networkPacket.writeHeader(outputBuffer);
networkPacket.writeBody(outputBuffer);
}
@@ -117,7 +116,7 @@ public class LocalTeamspeakSocket extends AbstractTeamspeakSocket implements Tea
datagramSocket.receive(datagramPacket);
Ts3Logging.debug(
"[NETWORK] READ Len=" + datagramPacket.getLength() + " to " + datagramPacket.getSocketAddress() +
"[NETWORK] READ Len=" + datagramPacket.getLength() + " from " + datagramPacket.getSocketAddress() +
"\n" + Ts3Logging.getHex(datagramPacket.getData(), datagramPacket.getLength())
);
@@ -125,27 +124,51 @@ public class LocalTeamspeakSocket extends AbstractTeamspeakSocket implements Tea
}
public NetworkPacket receive() throws IOException {
DatagramPacket datagramPacket = receiveNetworkPacket();
while (true) {
DatagramPacket datagramPacket = receiveNetworkPacket();
NetworkPacket networkPacket = new NetworkPacket(getSocketRole().getIn());
NetworkPacket networkPacket = new NetworkPacket(getSocketRole().getIn());
ByteBuffer buffer = ByteBuffer.wrap(datagramPacket.getData());
networkPacket.readHeader(buffer);
ByteBuffer buffer = ByteBuffer.wrap(datagramPacket.getData());
networkPacket.readHeader(buffer);
Ts3Logging.debug("[PROTOCOL] READ " + networkPacket.getHeader().getType().name());
boolean fragment = networkPacket.getHeader().getPacketFlag(HeaderFlag.FRAGMENTED);
if (fragment && !networkPacket.getHeader().getType().isSplittable()) continue; // drop packet
if (networkPacket.getHeader().getType().isEncrypted()) {
byte[] transformBuffer = new byte[buffer.remaining()];
buffer.get(transformBuffer);
ByteBuffer packetBuffer;
networkPacket.readBody(ByteBuffer.wrap(
getPacketTransformation().decrypt(networkPacket.getHeader(), transformBuffer)
));
} else {
networkPacket.readBody(buffer);
if (networkPacket.getHeader().getType().isEncrypted()) {
packetBuffer = ByteBuffer.wrap(
getPacketTransformation().decrypt(
networkPacket.getHeader(),
buffer,
datagramPacket.getLength() - networkPacket.getHeader().getSize()
)
);
} else {
packetBuffer = buffer;
}
if (fragment) {
Ts3Logging.debug("[PROTOCOL] READ " +
networkPacket.getHeader().getType().name() + " fragment");
networkPacket.setPacket(
new PacketFragment(
networkPacket.getHeader().getType(),
getSocketRole().getIn()
)
);
} else {
Ts3Logging.debug("[PROTOCOL] READ " +
networkPacket.getHeader().getType().name());
}
networkPacket.readBody(packetBuffer);
return networkPacket;
}
return networkPacket;
}
}
@@ -27,7 +27,7 @@ public class ServerConnectionTest extends TestCase {
10000L
);
assertEquals(client.getClientConnectionState(), ClientConnectionState.CONNECTED);
assertEquals(client.getConnectionState(), ClientConnectionState.CONNECTED);
} catch (Throwable ex) {
ex.printStackTrace();
}