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:
@@ -2,8 +2,12 @@ package com.github.manevolent.ts3j.protocol;
|
|||||||
|
|
||||||
import com.github.manevolent.ts3j.identity.LocalIdentity;
|
import com.github.manevolent.ts3j.identity.LocalIdentity;
|
||||||
import com.github.manevolent.ts3j.protocol.header.HeaderFlag;
|
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.packet.handler.PacketHandler;
|
||||||
import com.github.manevolent.ts3j.protocol.socket.LocalTeamspeakSocket;
|
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 com.github.manevolent.ts3j.util.Ts3Logging;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -13,10 +17,10 @@ import java.net.SocketException;
|
|||||||
public abstract class LocalEndpoint implements Endpoint {
|
public abstract class LocalEndpoint implements Endpoint {
|
||||||
private final LocalTeamspeakSocket socket;
|
private final LocalTeamspeakSocket socket;
|
||||||
|
|
||||||
|
|
||||||
private PacketHandler handler = null;
|
|
||||||
private LocalIdentity localIdentity = null;
|
private LocalIdentity localIdentity = null;
|
||||||
|
|
||||||
|
private final ReassemblyQueue reassemblyQueue = new ReassemblyQueue();
|
||||||
|
|
||||||
private final Object runLock = new Object();
|
private final Object runLock = new Object();
|
||||||
private Thread receiverThread;
|
private Thread receiverThread;
|
||||||
private boolean running = false;
|
private boolean running = false;
|
||||||
@@ -41,6 +45,9 @@ public abstract class LocalEndpoint implements Endpoint {
|
|||||||
return socket;
|
return socket;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected abstract void acknowledge(NetworkPacket packet, PacketType ackType) throws IOException;
|
||||||
|
protected abstract PacketChannel getChannel(NetworkPacket packet);
|
||||||
|
|
||||||
public void setRunning(boolean running) {
|
public void setRunning(boolean running) {
|
||||||
if (this.running != running) {
|
if (this.running != running) {
|
||||||
this.running = running;
|
this.running = running;
|
||||||
@@ -69,22 +76,6 @@ public abstract class LocalEndpoint implements Endpoint {
|
|||||||
return socket.receive();
|
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() {
|
public LocalIdentity getLocalIdentity() {
|
||||||
return localIdentity;
|
return localIdentity;
|
||||||
}
|
}
|
||||||
@@ -100,7 +91,21 @@ public abstract class LocalEndpoint implements Endpoint {
|
|||||||
|
|
||||||
while (running) {
|
while (running) {
|
||||||
try {
|
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) {
|
} catch (Exception e) {
|
||||||
if (e instanceof InterruptedException) continue;
|
if (e instanceof InterruptedException) continue;
|
||||||
|
|
||||||
|
|||||||
@@ -49,35 +49,43 @@ public final class NetworkPacket {
|
|||||||
* Writes the existing data out to the specified byte buffer
|
* Writes the existing data out to the specified byte buffer
|
||||||
* @param buffer Buffer to write to
|
* @param buffer Buffer to write to
|
||||||
*/
|
*/
|
||||||
public void write(ByteBuffer buffer) {
|
public ByteBuffer write(ByteBuffer buffer) {
|
||||||
writeHeader(buffer);
|
writeHeader(buffer);
|
||||||
writeBody(buffer);
|
writeBody(buffer);
|
||||||
|
|
||||||
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void writeHeader(ByteBuffer buffer) {
|
public ByteBuffer writeHeader(ByteBuffer buffer) {
|
||||||
if (getHeader() == null) throw new NullPointerException("header");
|
if (getHeader() == null) throw new NullPointerException("header");
|
||||||
|
|
||||||
getHeader().setType(getPacket().getType());
|
getHeader().setType(getPacket().getType());
|
||||||
|
|
||||||
getHeader().write(buffer);
|
getHeader().write(buffer);
|
||||||
|
|
||||||
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void writeBody(ByteBuffer buffer) {
|
public ByteBuffer writeBody(ByteBuffer buffer) {
|
||||||
if (getPacket() == null) throw new NullPointerException("packet");
|
if (getPacket() == null) throw new NullPointerException("packet");
|
||||||
|
|
||||||
getPacket().write(buffer);
|
getPacket().write(buffer);
|
||||||
|
|
||||||
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads data from the specified byte buffer, filling in the packet body as needed.
|
* Reads data from the specified byte buffer, filling in the packet body as needed.
|
||||||
* @param buffer Buffer to read from.
|
* @param buffer Buffer to read from.
|
||||||
*/
|
*/
|
||||||
public void read(ByteBuffer buffer) {
|
public ByteBuffer read(ByteBuffer buffer) {
|
||||||
readHeader(buffer);
|
readHeader(buffer);
|
||||||
readBody(buffer);
|
readBody(buffer);
|
||||||
|
|
||||||
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void readHeader(ByteBuffer buffer) {
|
public ByteBuffer readHeader(ByteBuffer buffer) {
|
||||||
if (getHeader() == null) {
|
if (getHeader() == null) {
|
||||||
try {
|
try {
|
||||||
setHeader(getRole().getHeaderClass().newInstance());
|
setHeader(getRole().getHeaderClass().newInstance());
|
||||||
@@ -87,9 +95,11 @@ public final class NetworkPacket {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getHeader().read(buffer);
|
getHeader().read(buffer);
|
||||||
|
|
||||||
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void readBody(ByteBuffer buffer) {
|
public ByteBuffer readBody(ByteBuffer buffer) {
|
||||||
if (getPacket() == null) {
|
if (getPacket() == null) {
|
||||||
try {
|
try {
|
||||||
setPacket(
|
setPacket(
|
||||||
@@ -103,6 +113,8 @@ public final class NetworkPacket {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getPacket().read(buffer);
|
getPacket().read(buffer);
|
||||||
|
|
||||||
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+82
-40
@@ -1,12 +1,15 @@
|
|||||||
package com.github.manevolent.ts3j.protocol.client;
|
package com.github.manevolent.ts3j.protocol.client;
|
||||||
|
|
||||||
import com.github.manevolent.ts3j.command.Command;
|
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.command.response.CommandResponse;
|
||||||
import com.github.manevolent.ts3j.identity.LocalIdentity;
|
|
||||||
import com.github.manevolent.ts3j.protocol.LocalEndpoint;
|
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.SocketRole;
|
||||||
import com.github.manevolent.ts3j.protocol.header.ClientPacketHeader;
|
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.packet.handler.client.LocalClientHandler;
|
||||||
import com.github.manevolent.ts3j.protocol.socket.LocalTeamspeakSocket;
|
import com.github.manevolent.ts3j.protocol.socket.LocalTeamspeakSocket;
|
||||||
import com.github.manevolent.ts3j.protocol.packet.Packet;
|
import com.github.manevolent.ts3j.protocol.packet.Packet;
|
||||||
@@ -18,16 +21,16 @@ import java.net.SocketException;
|
|||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.TimeoutException;
|
import java.util.concurrent.TimeoutException;
|
||||||
import java.util.function.Function;
|
|
||||||
|
|
||||||
public class LocalTeamspeakClient extends LocalEndpoint implements TeamspeakClient {
|
public class LocalTeamspeakClient extends LocalEndpoint implements TeamspeakClient {
|
||||||
private final Object connectionStateLock = new Object();
|
private final Object connectionStateLock = new Object();
|
||||||
|
|
||||||
private final Map<String, Object> clientOptions = new LinkedHashMap<>();
|
private final Map<String, Object> clientOptions = new LinkedHashMap<>();
|
||||||
|
private final PacketChannel channel = new PacketChannel();
|
||||||
|
|
||||||
private LocalClientHandler handler;
|
private ClientConnectionState connectionState = null;
|
||||||
private ClientConnectionState clientConnectionState = null;
|
|
||||||
|
|
||||||
|
private int packetId = 0;
|
||||||
|
private int clientId = 0;
|
||||||
private int returnCodeIndex = 0;
|
private int returnCodeIndex = 0;
|
||||||
|
|
||||||
public LocalTeamspeakClient(LocalTeamspeakSocket socket) {
|
public LocalTeamspeakClient(LocalTeamspeakSocket socket) {
|
||||||
@@ -36,9 +39,45 @@ public class LocalTeamspeakClient extends LocalEndpoint implements TeamspeakClie
|
|||||||
if (socket.getSocketRole() != SocketRole.CLIENT)
|
if (socket.getSocketRole() != SocketRole.CLIENT)
|
||||||
throw new IllegalArgumentException("invalid socket role: " + socket.getSocketRole().name());
|
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 {
|
public CommandResponse sendCommand(Command command) throws IOException {
|
||||||
if (command.willExpectResponse() && !isConnected())
|
if (command.willExpectResponse() && !isConnected())
|
||||||
throw new IOException("not connected");
|
throw new IOException("not connected");
|
||||||
@@ -64,48 +103,49 @@ public class LocalTeamspeakClient extends LocalEndpoint implements TeamspeakClie
|
|||||||
this(new LocalTeamspeakSocket(SocketRole.CLIENT));
|
this(new LocalTeamspeakSocket(SocketRole.CLIENT));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public SocketRole getRole() {
|
* Sends a packet to the server
|
||||||
return SocketRole.CLIENT;
|
* @param packet Packet to send
|
||||||
}
|
* @throws IOException
|
||||||
|
*/
|
||||||
public void send(Packet packet) throws IOException {
|
public void send(Packet packet) throws IOException {
|
||||||
// Construct header
|
// Construct header
|
||||||
ClientPacketHeader header = new ClientPacketHeader();
|
ClientPacketHeader header = new ClientPacketHeader();
|
||||||
|
|
||||||
|
header.setClientId(clientId);
|
||||||
|
header.setPacketId(packetId);
|
||||||
|
|
||||||
packet.setHeaderValues(header);
|
packet.setHeaderValues(header);
|
||||||
|
|
||||||
getSocket().send(header, packet);
|
getSocket().send(header, packet);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void setClientConnectionState(ClientConnectionState state) {
|
private void setConnectionState(ClientConnectionState state) {
|
||||||
synchronized (connectionStateLock) {
|
synchronized (connectionStateLock) {
|
||||||
if (state != this.clientConnectionState) {
|
if (state != this.connectionState) {
|
||||||
Ts3Logging.debug("State changing: " + state.name());
|
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)
|
if (wasDisconnected)
|
||||||
setRunning(true);
|
setRunning(true);
|
||||||
|
|
||||||
if (handler != null)
|
if (channel.getHandler() != null)
|
||||||
handler.handleConnectionStateChanging(state);
|
((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...");
|
Ts3Logging.debug("Assigning " + state.getHandlerClass().getName() + " handler...");
|
||||||
|
|
||||||
handler = state.createHandler(this);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
setHandler(handler);
|
channel.setHandler(state.createHandler(this));
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new IllegalStateException(e);
|
throw new IllegalStateException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.clientConnectionState == ClientConnectionState.DISCONNECTED)
|
if (this.connectionState == ClientConnectionState.DISCONNECTED)
|
||||||
clientOptions.clear();
|
clientOptions.clear();
|
||||||
|
|
||||||
connectionStateLock.notifyAll();
|
connectionStateLock.notifyAll();
|
||||||
@@ -115,17 +155,8 @@ public class LocalTeamspeakClient extends LocalEndpoint implements TeamspeakClie
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isConnected() {
|
public ClientConnectionState getConnectionState() {
|
||||||
return clientConnectionState == ClientConnectionState.CONNECTED;
|
return connectionState;
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Map<String, Object> getOptions() {
|
|
||||||
return clientOptions;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ClientConnectionState getClientConnectionState() {
|
|
||||||
return clientConnectionState;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void joinConnectionState(ClientConnectionState state, long wait)
|
private void joinConnectionState(ClientConnectionState state, long wait)
|
||||||
@@ -133,29 +164,40 @@ public class LocalTeamspeakClient extends LocalEndpoint implements TeamspeakClie
|
|||||||
synchronized (connectionStateLock) {
|
synchronized (connectionStateLock) {
|
||||||
long start = System.currentTimeMillis();
|
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)));
|
connectionStateLock.wait(Math.max(0L, wait - (System.currentTimeMillis() - start)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (clientConnectionState != state)
|
if (connectionState != state)
|
||||||
throw new TimeoutException("timeout waiting for " + state.name() + " 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 {
|
public void connect(InetSocketAddress remote, String password, long timeout) throws IOException {
|
||||||
try {
|
try {
|
||||||
if (clientConnectionState != ClientConnectionState.DISCONNECTED)
|
if (connectionState != ClientConnectionState.DISCONNECTED)
|
||||||
throw new IllegalStateException(clientConnectionState.name());
|
throw new IllegalStateException(connectionState.name());
|
||||||
|
|
||||||
setOption("client.hostname", remote.getHostString());
|
setOption("client.hostname", remote.getHostString());
|
||||||
setOption("client.password", password);
|
setOption("client.password", password);
|
||||||
|
|
||||||
getSocket().connect(remote);
|
getSocket().connect(remote);
|
||||||
|
|
||||||
setClientConnectionState(ClientConnectionState.CONNECTING);
|
setConnectionState(ClientConnectionState.CONNECTING);
|
||||||
joinConnectionState(ClientConnectionState.CONNECTED, timeout);
|
joinConnectionState(ClientConnectionState.CONNECTED, timeout);
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
setClientConnectionState(ClientConnectionState.DISCONNECTED);
|
setConnectionState(ClientConnectionState.DISCONNECTED);
|
||||||
|
|
||||||
throw new IOException(e);
|
throw new IOException(e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +1,38 @@
|
|||||||
package com.github.manevolent.ts3j.protocol.packet;
|
package com.github.manevolent.ts3j.protocol.packet;
|
||||||
|
|
||||||
import com.github.manevolent.ts3j.protocol.ProtocolRole;
|
import com.github.manevolent.ts3j.protocol.ProtocolRole;
|
||||||
|
import com.github.manevolent.ts3j.util.Ts3Logging;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
|
||||||
public class Packet2Command extends Packet {
|
public class Packet2Command extends Packet {
|
||||||
|
private String text;
|
||||||
|
|
||||||
public Packet2Command(ProtocolRole role) {
|
public Packet2Command(ProtocolRole role) {
|
||||||
super(PacketType.COMMAND, role);
|
super(PacketType.COMMAND, role);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getText() {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setText(String text) {
|
||||||
|
this.text = text;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void read(ByteBuffer buffer) {
|
public void read(ByteBuffer buffer) {
|
||||||
|
text = Charset.forName("ASCII").decode(buffer).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void write(ByteBuffer buffer) {
|
public void write(ByteBuffer buffer) {
|
||||||
|
buffer.put(text.getBytes(Charset.forName("ASCII")));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getSize() {
|
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),
|
VOICE_WHISPER(0x1, null, null, false, false, false, false),
|
||||||
PONG(0x5, null, null, false, false, false, false),
|
PONG(0x5, null, null, false, false, false, false),
|
||||||
PING(0x4, null, PONG, 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),
|
ACK_LOW(0x7, null, null, true, true, false, false),
|
||||||
INIT1(0x8, Packet8Init1.class, null, false, true, false, false),
|
INIT1(0x8, Packet8Init1.class, null, false, true, false, false),
|
||||||
COMMAND_LOW(0x3, null, ACK_LOW, true, true, true, true),
|
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 int index;
|
||||||
private final Class<? extends Packet> packetClass;
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
-2
@@ -52,7 +52,7 @@ public class LocalClientHandlerConnecting extends LocalClientHandler {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handlePacket(NetworkPacket packet) throws IOException {
|
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) {
|
if (packet.getPacket() instanceof Packet8Init1) {
|
||||||
Packet8Init1 init1 = (Packet8Init1) packet.getPacket();
|
Packet8Init1 init1 = (Packet8Init1) packet.getPacket();
|
||||||
@@ -136,7 +136,7 @@ public class LocalClientHandlerConnecting extends LocalClientHandler {
|
|||||||
|
|
||||||
}
|
}
|
||||||
} else if (packet.getPacket() instanceof Packet2Command) {
|
} else if (packet.getPacket() instanceof Packet2Command) {
|
||||||
|
Ts3Logging.debug(((Packet2Command) packet.getPacket()).getText());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+70
-23
@@ -1,5 +1,6 @@
|
|||||||
package com.github.manevolent.ts3j.protocol.packet.transformation;
|
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.ProtocolRole;
|
||||||
import com.github.manevolent.ts3j.protocol.header.PacketHeader;
|
import com.github.manevolent.ts3j.protocol.header.PacketHeader;
|
||||||
import com.github.manevolent.ts3j.util.Ts3Logging;
|
import com.github.manevolent.ts3j.util.Ts3Logging;
|
||||||
@@ -17,6 +18,7 @@ import java.security.MessageDigest;
|
|||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
|
||||||
public class PacketTransformation {
|
public class PacketTransformation {
|
||||||
|
private static final int MAC_LEN = 8;
|
||||||
private static MessageDigest SHA256;
|
private static MessageDigest SHA256;
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -70,33 +72,34 @@ public class PacketTransformation {
|
|||||||
return new Pair<>(key, nonce);
|
return new Pair<>(key, nonce);
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] encrypt(PacketHeader header, byte[] body) {
|
public ByteBuffer encrypt(NetworkPacket packet) {
|
||||||
return transform(header, body, true);
|
Pair<byte[], byte[]> parameters = computeParameters(packet.getHeader());
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] decrypt(PacketHeader header, byte[] body) {
|
// Write header (this is temporary)
|
||||||
return transform(header, body, false);
|
ByteBuffer headerBuffer = packet.writeHeader(ByteBuffer.allocate(packet.getHeader().getSize()));
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] transform(PacketHeader header, byte[] body, boolean encrypt) {
|
// Get the header without a MAC for the associated text field
|
||||||
Pair<byte[], byte[]> parameters = computeParameters(header);
|
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()),
|
||||||
CipherParameters cipherParameters = new AEADParameters(new KeyParameter(
|
8 * MAC_LEN,
|
||||||
parameters.getKey()),
|
|
||||||
8 * associatedText.length,
|
|
||||||
parameters.getValue(),
|
parameters.getValue(),
|
||||||
encrypt ? associatedText : header.getMac()
|
headerWithoutMac
|
||||||
);
|
);
|
||||||
|
|
||||||
|
int dataLen = packet.getPacket().getSize();
|
||||||
|
ByteBuffer packetBuffer = packet.writeBody(ByteBuffer.allocate(dataLen));
|
||||||
|
|
||||||
byte[] result;
|
byte[] result;
|
||||||
int len;
|
int len;
|
||||||
synchronized (cipher){
|
|
||||||
cipher.init(encrypt, cipherParameters);
|
|
||||||
|
|
||||||
result = new byte[cipher.getOutputSize(body.length)];
|
synchronized (cipher) {
|
||||||
len = cipher.processBytes(body, 0, body.length, result, 0);
|
cipher.init(true, cipherParameters);
|
||||||
|
|
||||||
|
result = new byte[cipher.getOutputSize(dataLen)];
|
||||||
|
len = cipher.processBytes(packetBuffer.array(), 0, dataLen, result, 0);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
len += cipher.doFinal(result, len);
|
len += cipher.doFinal(result, len);
|
||||||
@@ -105,12 +108,56 @@ public class PacketTransformation {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] output = new byte[len];
|
ByteBuffer outputBuffer = ByteBuffer.allocate(MAC_LEN + headerWithoutMac.length + (len - MAC_LEN));
|
||||||
System.arraycopy(result, 0, output, 0, len);
|
|
||||||
|
|
||||||
if (encrypt)
|
// MAC
|
||||||
header.setMac(associatedText);
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+46
-23
@@ -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.HeaderFlag;
|
||||||
import com.github.manevolent.ts3j.protocol.header.PacketHeader;
|
import com.github.manevolent.ts3j.protocol.header.PacketHeader;
|
||||||
import com.github.manevolent.ts3j.protocol.packet.Packet;
|
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 com.github.manevolent.ts3j.util.Ts3Logging;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@@ -78,28 +80,25 @@ public class LocalTeamspeakSocket extends AbstractTeamspeakSocket implements Tea
|
|||||||
header.getRole().name() + " != " +
|
header.getRole().name() + " != " +
|
||||||
getSocketRole().getOut().name());
|
getSocketRole().getOut().name());
|
||||||
|
|
||||||
|
// Ensure type matches
|
||||||
|
header.setType(packet.getType());
|
||||||
|
|
||||||
|
// Construct a network packet
|
||||||
NetworkPacket networkPacket = new NetworkPacket(header.getRole());
|
NetworkPacket networkPacket = new NetworkPacket(header.getRole());
|
||||||
networkPacket.setHeader(header);
|
networkPacket.setHeader(header);
|
||||||
networkPacket.setPacket(packet);
|
networkPacket.setPacket(packet);
|
||||||
|
|
||||||
if (networkPacket.getHeader().getMac() == null) {
|
// Flush to a buffer
|
||||||
// Generate mac?
|
ByteBuffer outputBuffer;
|
||||||
}
|
|
||||||
|
|
||||||
ByteBuffer outputBuffer = ByteBuffer.allocate(networkPacket.getSize());
|
|
||||||
outputBuffer.order(ByteOrder.BIG_ENDIAN);
|
|
||||||
networkPacket.writeHeader(outputBuffer);
|
|
||||||
|
|
||||||
if (networkPacket.getHeader().getType().isEncrypted() &&
|
if (networkPacket.getHeader().getType().isEncrypted() &&
|
||||||
!networkPacket.getHeader().getPacketFlag(HeaderFlag.UNENCRYPTED)) {
|
!networkPacket.getHeader().getPacketFlag(HeaderFlag.UNENCRYPTED)) {
|
||||||
ByteBuffer encryptionBuffer = ByteBuffer.allocate(500);
|
outputBuffer = getPacketTransformation().encrypt(networkPacket);
|
||||||
networkPacket.writeBody(encryptionBuffer);
|
|
||||||
|
|
||||||
byte[] unencryptedBody = new byte[encryptionBuffer.position()];
|
|
||||||
System.arraycopy(encryptionBuffer.array(), 0, unencryptedBody, 0, unencryptedBody.length);
|
|
||||||
|
|
||||||
outputBuffer.put(getPacketTransformation().encrypt(header, unencryptedBody));
|
|
||||||
} else {
|
} else {
|
||||||
|
outputBuffer = ByteBuffer.allocate(networkPacket.getSize());
|
||||||
|
|
||||||
|
outputBuffer.order(ByteOrder.BIG_ENDIAN);
|
||||||
|
networkPacket.writeHeader(outputBuffer);
|
||||||
networkPacket.writeBody(outputBuffer);
|
networkPacket.writeBody(outputBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -117,7 +116,7 @@ public class LocalTeamspeakSocket extends AbstractTeamspeakSocket implements Tea
|
|||||||
datagramSocket.receive(datagramPacket);
|
datagramSocket.receive(datagramPacket);
|
||||||
|
|
||||||
Ts3Logging.debug(
|
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())
|
"\n" + Ts3Logging.getHex(datagramPacket.getData(), datagramPacket.getLength())
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -125,6 +124,7 @@ public class LocalTeamspeakSocket extends AbstractTeamspeakSocket implements Tea
|
|||||||
}
|
}
|
||||||
|
|
||||||
public NetworkPacket receive() throws IOException {
|
public NetworkPacket receive() throws IOException {
|
||||||
|
while (true) {
|
||||||
DatagramPacket datagramPacket = receiveNetworkPacket();
|
DatagramPacket datagramPacket = receiveNetworkPacket();
|
||||||
|
|
||||||
NetworkPacket networkPacket = new NetworkPacket(getSocketRole().getIn());
|
NetworkPacket networkPacket = new NetworkPacket(getSocketRole().getIn());
|
||||||
@@ -132,20 +132,43 @@ public class LocalTeamspeakSocket extends AbstractTeamspeakSocket implements Tea
|
|||||||
ByteBuffer buffer = ByteBuffer.wrap(datagramPacket.getData());
|
ByteBuffer buffer = ByteBuffer.wrap(datagramPacket.getData());
|
||||||
networkPacket.readHeader(buffer);
|
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
|
||||||
|
|
||||||
|
ByteBuffer packetBuffer;
|
||||||
|
|
||||||
if (networkPacket.getHeader().getType().isEncrypted()) {
|
if (networkPacket.getHeader().getType().isEncrypted()) {
|
||||||
byte[] transformBuffer = new byte[buffer.remaining()];
|
packetBuffer = ByteBuffer.wrap(
|
||||||
buffer.get(transformBuffer);
|
getPacketTransformation().decrypt(
|
||||||
|
networkPacket.getHeader(),
|
||||||
networkPacket.readBody(ByteBuffer.wrap(
|
buffer,
|
||||||
getPacketTransformation().decrypt(networkPacket.getHeader(), transformBuffer)
|
datagramPacket.getLength() - networkPacket.getHeader().getSize()
|
||||||
));
|
)
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
networkPacket.readBody(buffer);
|
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
|
10000L
|
||||||
);
|
);
|
||||||
|
|
||||||
assertEquals(client.getClientConnectionState(), ClientConnectionState.CONNECTED);
|
assertEquals(client.getConnectionState(), ClientConnectionState.CONNECTED);
|
||||||
} catch (Throwable ex) {
|
} catch (Throwable ex) {
|
||||||
ex.printStackTrace();
|
ex.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user