diff --git a/LICENSE b/LICENSE index 94a9ed0..8b28719 100644 --- a/LICENSE +++ b/LICENSE @@ -116,7 +116,7 @@ for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official -standard defined by a recognized standards body, or, in the case of +standard defined by a recognized standards buffer, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. diff --git a/pom.xml b/pom.xml index 47d37bc..9b060b4 100644 --- a/pom.xml +++ b/pom.xml @@ -35,5 +35,11 @@ bcprov-jdk15on 1.60 + + + commons-lang + commons-lang + 2.6 + \ No newline at end of file diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/Endpoint.java b/src/main/java/com/github/manevolent/ts3j/protocol/Endpoint.java deleted file mode 100644 index a4e0553..0000000 --- a/src/main/java/com/github/manevolent/ts3j/protocol/Endpoint.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.github.manevolent.ts3j.protocol; - -import com.github.manevolent.ts3j.protocol.packet.Packet; - -import java.io.IOException; -import java.util.function.Function; - -public interface Endpoint { - - SocketRole getRole(); - - void send(Packet packet) throws IOException; - -} diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/LocalEndpoint.java b/src/main/java/com/github/manevolent/ts3j/protocol/LocalEndpoint.java deleted file mode 100644 index b3a020b..0000000 --- a/src/main/java/com/github/manevolent/ts3j/protocol/LocalEndpoint.java +++ /dev/null @@ -1,119 +0,0 @@ -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; -import java.net.InetSocketAddress; -import java.net.SocketException; - -public abstract class LocalEndpoint implements Endpoint { - private final LocalTeamspeakSocket socket; - - private LocalIdentity localIdentity = null; - - private final ReassemblyQueue reassemblyQueue = new ReassemblyQueue(); - - private final Object runLock = new Object(); - private Thread receiverThread; - private boolean running = false; - - protected LocalEndpoint(LocalTeamspeakSocket socket) { - this.socket = socket; - } - - protected LocalEndpoint(SocketRole socketRole) throws SocketException { - this.socket = new LocalTeamspeakSocket(socketRole); - } - - public InetSocketAddress getLocalAddress() { - return getSocket().getLocalAddress(); - } - - public InetSocketAddress getRemoteAddress() { - return getSocket().getLocalAddress(); - } - - protected final LocalTeamspeakSocket getSocket() { - 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; - - if (running) { - Ts3Logging.debug("Starting network receiver thread..."); - - receiverThread = new Thread(new PacketReceiver()); - receiverThread.setDaemon(true); - receiverThread.start(); - } else { - if (receiverThread != null) { - receiverThread.interrupt(); - receiverThread = null; - } - } - } - } - - @Override - public SocketRole getRole() { - return socket.getSocketRole(); - } - - private NetworkPacket receive() throws IOException { - return socket.receive(); - } - - public LocalIdentity getLocalIdentity() { - return localIdentity; - } - - public void setLocalIdentity(LocalIdentity localIdentity) { - this.localIdentity = localIdentity; - } - - private class PacketReceiver implements Runnable { - @Override - public void run() { - Ts3Logging.debug("Entering packet receiver"); - - while (running) { - try { - 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; - - Ts3Logging.debug("Problem handling packet", e); - } - } - - Ts3Logging.debug("Leaving packet receiver"); - } - } -} diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/NetworkPacket.java b/src/main/java/com/github/manevolent/ts3j/protocol/NetworkPacket.java index 113ff48..4ca46bf 100644 --- a/src/main/java/com/github/manevolent/ts3j/protocol/NetworkPacket.java +++ b/src/main/java/com/github/manevolent/ts3j/protocol/NetworkPacket.java @@ -1,127 +1,42 @@ package com.github.manevolent.ts3j.protocol; import com.github.manevolent.ts3j.protocol.header.PacketHeader; -import com.github.manevolent.ts3j.protocol.packet.Packet; -import com.github.manevolent.ts3j.util.Ts3Logging; +import java.net.DatagramPacket; import java.nio.ByteBuffer; -/** - * Represents a fully contained network packet, traveling in or out. - */ public final class NetworkPacket { - private final ProtocolRole role; + private final DatagramPacket datagram; + private final PacketHeader header; + private final ByteBuffer buffer; - private PacketHeader header; - private Packet packet; - - public NetworkPacket(ProtocolRole role) { - if (role == null) throw new NullPointerException("role"); - - this.role = role; + public NetworkPacket(DatagramPacket datagram, PacketHeader header, ByteBuffer buffer) { + this.datagram = datagram; + this.header = header; + this.buffer = buffer; } - public ProtocolRole getRole() { - return role; + /** + * Gets the original packet datagram. + * @return Packet datagram. + */ + public DatagramPacket getDatagram() { + return datagram; } + /** + * Gets the received header. + * @return Packet header. + */ public PacketHeader getHeader() { return header; } - public void setHeader(PacketHeader header) { - if (header == null) throw new NullPointerException("header"); - - this.header = header; - } - - public Packet getPacket() { - return packet; - } - - public void setPacket(Packet packet) { - if (packet == null) throw new NullPointerException("packet"); - - this.packet = packet; - } - /** - * Writes the existing data out to the specified byte buffer - * @param buffer Buffer to write to + * Gets the original packet buffer. + * @return Packet buffer. */ - public ByteBuffer write(ByteBuffer buffer) { - writeHeader(buffer); - writeBody(buffer); - + public ByteBuffer getBuffer() { return buffer; } - - public ByteBuffer writeHeader(ByteBuffer buffer) { - if (getHeader() == null) throw new NullPointerException("header"); - - getHeader().setType(getPacket().getType()); - - getHeader().write(buffer); - - return 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 ByteBuffer read(ByteBuffer buffer) { - readHeader(buffer); - readBody(buffer); - - return buffer; - } - - public ByteBuffer readHeader(ByteBuffer buffer) { - if (getHeader() == null) { - try { - setHeader(getRole().getHeaderClass().newInstance()); - } catch (ReflectiveOperationException e) { - throw new RuntimeException(e); - } - } - - getHeader().read(buffer); - - return buffer; - } - - public ByteBuffer readBody(ByteBuffer buffer) { - if (getPacket() == null) { - try { - setPacket( - getHeader().getType().getPacketClass() - .getConstructor(ProtocolRole.class) - .newInstance(getHeader().getRole()) - ); - } catch (ReflectiveOperationException e) { - throw new RuntimeException(e); - } - } - - getPacket().read(buffer); - - return buffer; - } - - /** - * Gets the size of this packet. This method is generally only used when writing a fully defined packet. - * @return packet size. - */ - public int getSize() { - return getHeader().getSize() + getPacket().getSize(); - } } diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/Packet.java b/src/main/java/com/github/manevolent/ts3j/protocol/Packet.java new file mode 100644 index 0000000..8c1ce48 --- /dev/null +++ b/src/main/java/com/github/manevolent/ts3j/protocol/Packet.java @@ -0,0 +1,127 @@ +package com.github.manevolent.ts3j.protocol; + +import com.github.manevolent.ts3j.protocol.header.PacketHeader; +import com.github.manevolent.ts3j.protocol.packet.PacketBody; + +import java.nio.ByteBuffer; + +/** + * Represents a fully contained network packet, traveling in or out. + */ +public final class Packet { + private final ProtocolRole role; + + private PacketHeader header; + private PacketBody body; + + public Packet(ProtocolRole role) { + if (role == null) throw new NullPointerException("role"); + + this.role = role; + } + + public ProtocolRole getRole() { + return role; + } + + public PacketHeader getHeader() { + return header; + } + + public void setHeader(PacketHeader header) { + if (header == null) throw new NullPointerException("header"); + + this.header = header; + } + + public PacketBody getBody() { + return body; + } + + public void setBody(PacketBody packetBody) { + if (packetBody == null) throw new NullPointerException("packet"); + + this.body = packetBody; + } + + /** + * Writes the existing data out to the specified byte buffer + * @param buffer Buffer to write to + */ + public ByteBuffer write(ByteBuffer buffer) { + writeHeader(buffer); + writeBody(buffer); + + return buffer; + } + + public ByteBuffer writeHeader(ByteBuffer buffer) { + if (getHeader() == null) throw new NullPointerException("header"); + + if (getBody() != null) + getHeader().setType(getBody().getType()); + + getHeader().write(buffer); + + return buffer; + } + + public ByteBuffer writeBody(ByteBuffer buffer) { + if (getBody() == null) throw new NullPointerException("packet"); + + getBody().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 ByteBuffer read(ByteBuffer buffer) { + readHeader(buffer); + readBody(buffer); + + return buffer; + } + + public ByteBuffer readHeader(ByteBuffer buffer) { + if (getHeader() == null) { + try { + setHeader(getRole().getHeaderClass().newInstance()); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + + getHeader().read(buffer); + + return buffer; + } + + public ByteBuffer readBody(ByteBuffer buffer) { + if (getBody() == null) { + try { + setBody( + getHeader().getType().getPacketClass() + .getConstructor(ProtocolRole.class) + .newInstance(getHeader().getRole()) + ); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + + getBody().read(buffer); + + return buffer; + } + + /** + * Gets the size of this packet. This method is generally only used when writing a fully defined packet. + * @return packet size. + */ + public int getSize() { + return (getHeader() == null ? 0 : getHeader().getSize()) + (getBody() == null ? 0 : getBody().getSize()); + } +} diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/RemoteEndpoint.java b/src/main/java/com/github/manevolent/ts3j/protocol/RemoteEndpoint.java deleted file mode 100644 index 3899f4c..0000000 --- a/src/main/java/com/github/manevolent/ts3j/protocol/RemoteEndpoint.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.github.manevolent.ts3j.protocol; - -import java.net.InetSocketAddress; - -public abstract class RemoteEndpoint implements Endpoint { - public abstract InetSocketAddress getRemoteAddress(); -} diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/client/ClientConnectionState.java b/src/main/java/com/github/manevolent/ts3j/protocol/client/ClientConnectionState.java index 6979b0d..6104023 100644 --- a/src/main/java/com/github/manevolent/ts3j/protocol/client/ClientConnectionState.java +++ b/src/main/java/com/github/manevolent/ts3j/protocol/client/ClientConnectionState.java @@ -1,29 +1,34 @@ package com.github.manevolent.ts3j.protocol.client; -import com.github.manevolent.ts3j.protocol.packet.handler.client.LocalClientHandler; -import com.github.manevolent.ts3j.protocol.packet.handler.client.LocalClientHandlerConnected; -import com.github.manevolent.ts3j.protocol.packet.handler.client.LocalClientHandlerConnecting; -import com.github.manevolent.ts3j.protocol.packet.handler.client.LocalClientHandlerDisconnected; +import com.github.manevolent.ts3j.protocol.packet.handler.PacketHandler; +import com.github.manevolent.ts3j.protocol.packet.handler.local.LocalClientHandler; +import com.github.manevolent.ts3j.protocol.packet.handler.local.LocalClientHandlerConnected; +import com.github.manevolent.ts3j.protocol.packet.handler.local.LocalClientHandlerConnecting; +import com.github.manevolent.ts3j.protocol.packet.handler.local.LocalClientHandlerDisconnected; +import com.github.manevolent.ts3j.protocol.socket.client.AbstractTeamspeakClientSocket; +import com.github.manevolent.ts3j.protocol.socket.client.TeamspeakClientSocket; +import org.apache.commons.lang.reflect.ConstructorUtils; public enum ClientConnectionState { CONNECTING(LocalClientHandlerConnecting.class), + NEGOTIATING(LocalClientHandlerConnecting.class), CONNECTED(LocalClientHandlerConnected.class), DISCONNECTED(LocalClientHandlerDisconnected.class); - private final Class clazz; + private final Class clazz; - ClientConnectionState(Class clazz) { + ClientConnectionState(Class clazz) { this.clazz = clazz; } - public Class getHandlerClass() { + public Class getHandlerClass() { return clazz; } - public LocalClientHandler createHandler(LocalTeamspeakClient client) { + public PacketHandler createHandler(TeamspeakClientSocket client) { try { - return clazz.getConstructor(LocalTeamspeakClient.class).newInstance(client); + return (PacketHandler) ConstructorUtils.invokeConstructor(clazz, client); } catch (ReflectiveOperationException ex) { throw new RuntimeException(ex); } diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/client/LocalTeamspeakClient.java b/src/main/java/com/github/manevolent/ts3j/protocol/client/LocalTeamspeakClient.java deleted file mode 100644 index ff9f0db..0000000 --- a/src/main/java/com/github/manevolent/ts3j/protocol/client/LocalTeamspeakClient.java +++ /dev/null @@ -1,218 +0,0 @@ -package com.github.manevolent.ts3j.protocol.client; - -import com.github.manevolent.ts3j.command.Command; -import com.github.manevolent.ts3j.command.response.CommandResponse; -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.header.HeaderFlag; -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.transformation.PacketTransformation; -import com.github.manevolent.ts3j.protocol.socket.LocalTeamspeakSocket; -import com.github.manevolent.ts3j.protocol.packet.Packet; -import com.github.manevolent.ts3j.util.Ts3Crypt; -import com.github.manevolent.ts3j.util.Ts3Logging; - -import java.io.IOException; -import java.net.InetSocketAddress; -import java.net.SocketException; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.concurrent.TimeoutException; - -public class LocalTeamspeakClient extends LocalEndpoint implements TeamspeakClient { - private final Object connectionStateLock = new Object(); - private final Map clientOptions = new LinkedHashMap<>(); - private final PacketChannel channel = new PacketChannel(); - - private ClientConnectionState connectionState = null; - - private int packetId = 0; - private int clientId = 0; - private int returnCodeIndex = 0; - - public LocalTeamspeakClient(LocalTeamspeakSocket socket) { - super(socket); - - if (socket.getSocketRole() != SocketRole.CLIENT) - throw new IllegalArgumentException("invalid socket role: " + socket.getSocketRole().name()); - - 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 getOptions() { - return clientOptions; - } - - public void setSecureParameters(Ts3Crypt.SecureChannelParameters parameters) { - getSocket().setPacketTransformation(new PacketTransformation(parameters.getIvStruct())); - } - - /** - * 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"); - - CommandResponse response; - - if (command.willExpectResponse()) { - int returnCodeIndex = ++ this.returnCodeIndex; - //command.appendParameter(new CommandSingleParameter("return_code", returnCodeIndex)); - response = new CommandResponse(command, returnCodeIndex); - } else { - response = new CommandResponse(command, -1); - } - - String commandString = command.toString(); - - response.setDispatchedTime(System.currentTimeMillis()); - - return response; - } - - public LocalTeamspeakClient() throws SocketException { - this(new LocalTeamspeakSocket(SocketRole.CLIENT)); - } - - /** - * Sends a packet to the server - * @param packet Packet to send - * @throws IOException - */ - int z = 0; - public void send(Packet packet) throws IOException { - // Construct header - ClientPacketHeader header = new ClientPacketHeader(); - - header.setClientId(clientId); - header.setPacketId(packetId); - - packet.setHeaderValues(header); - - if (connectionState == ClientConnectionState.CONNECTING) { - header.setPacketId(z++); - header.setPacketFlag(HeaderFlag.NEW_PROTOCOL, true); - } - - getSocket().send(header, packet); - } - - public void setConnectionState(ClientConnectionState state) { - synchronized (connectionStateLock) { - if (state != this.connectionState) { - Ts3Logging.debug("State changing: " + state.name()); - - boolean wasDisconnected = this.connectionState == ClientConnectionState.DISCONNECTED; - - this.connectionState = state; - - if (wasDisconnected) - setRunning(true); - - if (channel.getHandler() != null) - ((LocalClientHandler)channel.getHandler()).handleConnectionStateChanging(state); - - if (channel.getHandler() == null || channel.getHandler().getClass() != state.getHandlerClass()) { - Ts3Logging.debug("Assigning " + state.getHandlerClass().getName() + " handler..."); - - try { - channel.setHandler(state.createHandler(this)); - } catch (IOException e) { - throw new IllegalStateException(e); - } - } - - if (this.connectionState == ClientConnectionState.DISCONNECTED) - clientOptions.clear(); - - connectionStateLock.notifyAll(); - - Ts3Logging.debug("State changed: " + state.name()); - } - } - } - - public ClientConnectionState getConnectionState() { - return connectionState; - } - - private void joinConnectionState(ClientConnectionState state, long wait) - throws InterruptedException, TimeoutException { - synchronized (connectionStateLock) { - long start = System.currentTimeMillis(); - - while (connectionState != state && System.currentTimeMillis() - start < wait) { - connectionStateLock.wait(Math.max(0L, wait - (System.currentTimeMillis() - start))); - } - - 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 (connectionState != ClientConnectionState.DISCONNECTED) - throw new IllegalStateException(connectionState.name()); - - setOption("client.hostname", remote.getHostString()); - setOption("client.password", password); - - getSocket().connect(remote); - - setConnectionState(ClientConnectionState.CONNECTING); - joinConnectionState(ClientConnectionState.CONNECTED, timeout); - } catch (Throwable e) { - setConnectionState(ClientConnectionState.DISCONNECTED); - - throw new IOException(e); - } - } -} diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/client/TeamspeakClient.java b/src/main/java/com/github/manevolent/ts3j/protocol/client/TeamspeakClient.java deleted file mode 100644 index d97ec7c..0000000 --- a/src/main/java/com/github/manevolent/ts3j/protocol/client/TeamspeakClient.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.github.manevolent.ts3j.protocol.client; - -import com.github.manevolent.ts3j.protocol.Endpoint; - -import java.io.IOException; -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import java.util.Map; -import java.util.function.Function; - -public interface TeamspeakClient extends Endpoint { - void connect(InetSocketAddress remote, String password, long timeout) throws IOException; - - SocketAddress getRemoteAddress(); - - boolean isConnected(); - - Map getOptions(); - - default T getOption(String key, Class clazz) { - Object value = getOptions().get(key); - - if (value == null) return (T) null; - - else if (!value.getClass().isAssignableFrom(clazz)) { - throw new ClassCastException( - clazz.getName() - + " is not assignable to option type " + - value.getClass().getName() - ); - } else - return (T) value; - } - - default Object setOption(String key, Object value) { - return getOptions().put(key, value); - } -} diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/header/ClientPacketHeader.java b/src/main/java/com/github/manevolent/ts3j/protocol/header/ClientPacketHeader.java index 1b4c5ef..789955c 100644 --- a/src/main/java/com/github/manevolent/ts3j/protocol/header/ClientPacketHeader.java +++ b/src/main/java/com/github/manevolent/ts3j/protocol/header/ClientPacketHeader.java @@ -1,6 +1,6 @@ package com.github.manevolent.ts3j.protocol.header; -import com.github.manevolent.ts3j.protocol.packet.PacketType; +import com.github.manevolent.ts3j.protocol.packet.PacketBodyType; import com.github.manevolent.ts3j.protocol.ProtocolRole; import java.nio.ByteBuffer; @@ -44,7 +44,7 @@ public class ClientPacketHeader extends PacketHeader { setClientId((buffer.getShort() & 0xFFFF)); byte packetTypeAndFlags = buffer.get(); - setType(PacketType.fromId((packetTypeAndFlags & 0x0F))); // PT / Packet Type + Flags + setType(PacketBodyType.fromId((packetTypeAndFlags & 0x0F))); // PT / Packet Type + Flags setPacketFlags(packetTypeAndFlags & 0xF0); // (payload) -- see writeBody impl in higher levels diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/header/PacketHeader.java b/src/main/java/com/github/manevolent/ts3j/protocol/header/PacketHeader.java index 491fd9e..77b04bf 100644 --- a/src/main/java/com/github/manevolent/ts3j/protocol/header/PacketHeader.java +++ b/src/main/java/com/github/manevolent/ts3j/protocol/header/PacketHeader.java @@ -1,7 +1,7 @@ package com.github.manevolent.ts3j.protocol.header; -import com.github.manevolent.ts3j.protocol.packet.PacketType; -import com.github.manevolent.ts3j.protocol.packet.Packet; +import com.github.manevolent.ts3j.protocol.packet.PacketBodyType; +import com.github.manevolent.ts3j.protocol.packet.PacketBody; import com.github.manevolent.ts3j.protocol.ProtocolRole; import java.nio.ByteBuffer; @@ -13,7 +13,7 @@ public abstract class PacketHeader { private int packetGeneration; private byte[] mac = new byte[8]; // 8 bytes private int packetId = 0; - private PacketType type; + private PacketBodyType type; private int packetFlags; protected PacketHeader(ProtocolRole role) { @@ -44,11 +44,11 @@ public abstract class PacketHeader { } } - public PacketType getType() { + public PacketBodyType getType() { return type; } - public void setType(PacketType type) { + public void setType(PacketBodyType type) { this.type = type; } @@ -65,7 +65,7 @@ public abstract class PacketHeader { } public void setMac(byte[] mac) { - Packet.assertArray("mac", mac, 8); + PacketBody.assertArray("mac", mac, 8); this.mac = mac; } diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/header/ServerPacketHeader.java b/src/main/java/com/github/manevolent/ts3j/protocol/header/ServerPacketHeader.java index ee90074..3bfb742 100644 --- a/src/main/java/com/github/manevolent/ts3j/protocol/header/ServerPacketHeader.java +++ b/src/main/java/com/github/manevolent/ts3j/protocol/header/ServerPacketHeader.java @@ -1,8 +1,7 @@ package com.github.manevolent.ts3j.protocol.header; import com.github.manevolent.ts3j.protocol.ProtocolRole; -import com.github.manevolent.ts3j.protocol.packet.PacketType; -import com.github.manevolent.ts3j.util.Ts3Logging; +import com.github.manevolent.ts3j.protocol.packet.PacketBodyType; import java.nio.ByteBuffer; @@ -40,7 +39,7 @@ public class ServerPacketHeader extends PacketHeader { // https://github.com/ReSpeak/tsdeclarations/blob/master/ts3protocol.md byte packetTypeAndFlags = buffer.get(); - setType(PacketType.fromId((packetTypeAndFlags & 0x0F))); // PT / Packet Type + Flags + setType(PacketBodyType.fromId((packetTypeAndFlags & 0x0F))); // PT / Packet Type + Flags setPacketFlags(packetTypeAndFlags & 0xF0); // (payload) -- see writeBody impl in higher levels diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/packet/Packet.java b/src/main/java/com/github/manevolent/ts3j/protocol/packet/PacketBody.java similarity index 87% rename from src/main/java/com/github/manevolent/ts3j/protocol/packet/Packet.java rename to src/main/java/com/github/manevolent/ts3j/protocol/packet/PacketBody.java index 82473cd..f238cf6 100644 --- a/src/main/java/com/github/manevolent/ts3j/protocol/packet/Packet.java +++ b/src/main/java/com/github/manevolent/ts3j/protocol/packet/PacketBody.java @@ -5,16 +5,16 @@ import com.github.manevolent.ts3j.protocol.header.PacketHeader; import java.nio.ByteBuffer; -public abstract class Packet { - private final PacketType type; +public abstract class PacketBody { + private final PacketBodyType type; private final ProtocolRole role; - protected Packet(PacketType type, ProtocolRole role) { + protected PacketBody(PacketBodyType type, ProtocolRole role) { this.type = type; this.role = role; } - public PacketType getType() { + public PacketBodyType getType() { return type; } diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/packet/Packet2Command.java b/src/main/java/com/github/manevolent/ts3j/protocol/packet/PacketBody2Command.java similarity index 81% rename from src/main/java/com/github/manevolent/ts3j/protocol/packet/Packet2Command.java rename to src/main/java/com/github/manevolent/ts3j/protocol/packet/PacketBody2Command.java index 2d85ac3..46024c7 100644 --- a/src/main/java/com/github/manevolent/ts3j/protocol/packet/Packet2Command.java +++ b/src/main/java/com/github/manevolent/ts3j/protocol/packet/PacketBody2Command.java @@ -2,16 +2,15 @@ package com.github.manevolent.ts3j.protocol.packet; import com.github.manevolent.ts3j.command.SimpleCommand; 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 { +public class PacketBody2Command extends PacketBody { private String text; - public Packet2Command(ProtocolRole role) { - super(PacketType.COMMAND, role); + public PacketBody2Command(ProtocolRole role) { + super(PacketBodyType.COMMAND, role); } public String getText() { diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/packet/Packet6Ack.java b/src/main/java/com/github/manevolent/ts3j/protocol/packet/PacketBody6Ack.java similarity index 69% rename from src/main/java/com/github/manevolent/ts3j/protocol/packet/Packet6Ack.java rename to src/main/java/com/github/manevolent/ts3j/protocol/packet/PacketBody6Ack.java index 0ac69d7..d0ded14 100644 --- a/src/main/java/com/github/manevolent/ts3j/protocol/packet/Packet6Ack.java +++ b/src/main/java/com/github/manevolent/ts3j/protocol/packet/PacketBody6Ack.java @@ -3,13 +3,18 @@ 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 { +public class PacketBody6Ack extends PacketBody { private int packetId; - public Packet6Ack(ProtocolRole role) { - super(PacketType.ACK, role); + public PacketBody6Ack(ProtocolRole role) { + super(PacketBodyType.ACK, role); + } + + public PacketBody6Ack(ProtocolRole role, int packetId) { + super(PacketBodyType.ACK, role); + + setPacketId(packetId); } public int getPacketId() { diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/packet/Packet8Init1.java b/src/main/java/com/github/manevolent/ts3j/protocol/packet/PacketBody8Init1.java similarity index 91% rename from src/main/java/com/github/manevolent/ts3j/protocol/packet/Packet8Init1.java rename to src/main/java/com/github/manevolent/ts3j/protocol/packet/PacketBody8Init1.java index 480477d..80826f2 100644 --- a/src/main/java/com/github/manevolent/ts3j/protocol/packet/Packet8Init1.java +++ b/src/main/java/com/github/manevolent/ts3j/protocol/packet/PacketBody8Init1.java @@ -7,9 +7,9 @@ import com.github.manevolent.ts3j.protocol.header.PacketHeader; import java.nio.ByteBuffer; import java.nio.ByteOrder; -import static com.github.manevolent.ts3j.protocol.packet.PacketType.INIT1; +import static com.github.manevolent.ts3j.protocol.packet.PacketBodyType.INIT1; -public class Packet8Init1 extends Packet { +public class PacketBody8Init1 extends PacketBody { private static final byte[] LOW_LEVEL_MAC = new byte[] { 0x54, 0x53, 0x33, 0x49, 0x4E, 0x49, 0x54, 0x31 }; @@ -17,7 +17,7 @@ public class Packet8Init1 extends Packet { private byte[] version = new byte[4]; // 4 bytes private Step step; // step instance - public Packet8Init1(ProtocolRole role) { + public PacketBody8Init1(ProtocolRole role) { // sec. 2 of https://github.com/ReSpeak/tsdeclarations/blob/master/ts3protocol.md // (The packet header values are set as following for all packets here:) super(INIT1, role); @@ -152,7 +152,7 @@ public class Packet8Init1 extends Packet { } public void setTimestamp(byte[] timestamp) { - Packet.assertArray("timestamp", timestamp, 4); + PacketBody.assertArray("timestamp", timestamp, 4); this.timestamp = timestamp; } @@ -161,7 +161,7 @@ public class Packet8Init1 extends Packet { } public void setRandom(byte[] random) { - Packet.assertArray("random", random, 4); + PacketBody.assertArray("random", random, 4); this.random = random; } @@ -199,7 +199,7 @@ public class Packet8Init1 extends Packet { } public void setA0reversed(byte[] a0reversed) { - Packet.assertArray("a0reversed", a0reversed, 4); + PacketBody.assertArray("a0reversed", a0reversed, 4); this.a0reversed = a0reversed; } @@ -208,7 +208,7 @@ public class Packet8Init1 extends Packet { } public void setServerStuff(byte[] serverStuff) { - Packet.assertArray("serverStuff", serverStuff, 16); + PacketBody.assertArray("serverStuff", serverStuff, 16); this.serverStuff = serverStuff; } @@ -244,7 +244,7 @@ public class Packet8Init1 extends Packet { } public void setA0reversed(byte[] a0reversed) { - Packet.assertArray("a0reversed", a0reversed, 4); + PacketBody.assertArray("a0reversed", a0reversed, 4); this.a0reversed = a0reversed; } @@ -253,7 +253,7 @@ public class Packet8Init1 extends Packet { } public void setServerStuff(byte[] serverStuff) { - Packet.assertArray("serverStuff", serverStuff, 16); + PacketBody.assertArray("serverStuff", serverStuff, 16); this.serverStuff = serverStuff; } @@ -290,7 +290,7 @@ public class Packet8Init1 extends Packet { } public void setX(byte[] x) { - Packet.assertArray("x", x, 64); + PacketBody.assertArray("x", x, 64); this.x = x; } @@ -299,7 +299,7 @@ public class Packet8Init1 extends Packet { } public void setN(byte[] n) { - Packet.assertArray("n", n, 64); + PacketBody.assertArray("n", n, 64); this.n = n; } @@ -308,7 +308,7 @@ public class Packet8Init1 extends Packet { } public void setServerStuff(byte[] serverStuff) { - Packet.assertArray("serverStuff", serverStuff, 100); + PacketBody.assertArray("serverStuff", serverStuff, 100); this.serverStuff = serverStuff; } @@ -359,7 +359,7 @@ public class Packet8Init1 extends Packet { } public void setX(byte[] x) { - Packet.assertArray("x", x, 64); + PacketBody.assertArray("x", x, 64); this.x = x; } @@ -368,7 +368,7 @@ public class Packet8Init1 extends Packet { } public void setN(byte[] n) { - Packet.assertArray("n", n, 64); + PacketBody.assertArray("n", n, 64); this.n = n; } @@ -377,7 +377,7 @@ public class Packet8Init1 extends Packet { } public void setServerStuff(byte[] serverStuff) { - Packet.assertArray("serverStuff", serverStuff, 100); + PacketBody.assertArray("serverStuff", serverStuff, 100); this.serverStuff = serverStuff; } @@ -394,7 +394,7 @@ public class Packet8Init1 extends Packet { } public void setY(byte[] y) { - Packet.assertArray("y", y, 64); + PacketBody.assertArray("y", y, 64); this.y = y; } diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/packet/PacketBodyFragment.java b/src/main/java/com/github/manevolent/ts3j/protocol/packet/PacketBodyFragment.java new file mode 100644 index 0000000..12cc5fc --- /dev/null +++ b/src/main/java/com/github/manevolent/ts3j/protocol/packet/PacketBodyFragment.java @@ -0,0 +1,37 @@ +package com.github.manevolent.ts3j.protocol.packet; + +import com.github.manevolent.ts3j.protocol.ProtocolRole; + +import java.nio.ByteBuffer; + +public class PacketBodyFragment extends PacketBody { + private byte[] raw; + + public PacketBodyFragment(PacketBodyType type, ProtocolRole role) { + super(type, role); + } + + public byte[] getRaw() { + return raw; + } + + public void setRaw(byte[] raw) { + this.raw = raw; + } + + @Override + public void read(ByteBuffer buffer) { + raw = new byte[buffer.remaining()]; + buffer.get(raw); + } + + @Override + public void write(ByteBuffer buffer) { + buffer.put(raw); + } + + @Override + public int getSize() { + return raw.length; + } +} diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/packet/PacketBodyType.java b/src/main/java/com/github/manevolent/ts3j/protocol/packet/PacketBodyType.java new file mode 100644 index 0000000..2ccadc7 --- /dev/null +++ b/src/main/java/com/github/manevolent/ts3j/protocol/packet/PacketBodyType.java @@ -0,0 +1,74 @@ +package com.github.manevolent.ts3j.protocol.packet; + +public enum PacketBodyType { + VOICE(0x0, null, null, false, false, false, false, false), + VOICE_WHISPER(0x1, null, null, false, false, false, false, false), + PONG(0x5, null, null, false, false, false, false, false), + PING(0x4, null, PONG, false, false, false, false, false), + ACK(0x6, PacketBody6Ack.class, null, true, true, false, false, true), + ACK_LOW(0x7, null, null, true, true, false, false, true), + INIT1(0x8, PacketBody8Init1.class, null, false, true, false, false, false), + COMMAND_LOW(0x3, null, ACK_LOW, true, true, true, true, true), + COMMAND(0x2, PacketBody2Command.class, ACK, true, true, true, true, true); + + private final int index; + private final Class packetClass; + + private final PacketBodyType acknolwedgedBy; + private final boolean encrypted, resend, splittable, compressible, mustEncrypt; + + PacketBodyType(int index, Class packetClass, + PacketBodyType acknolwedgedBy, + boolean encrypted, + boolean resend, + boolean splittable, + boolean compressible, boolean mustEncrypt) { + this.index = index; + this.packetClass = packetClass; + this.acknolwedgedBy = acknolwedgedBy; + this.resend = resend; + this.encrypted = encrypted; + this.splittable = splittable; + this.compressible = compressible; + this.mustEncrypt = mustEncrypt; + } + + public int getIndex() { + return index; + } + + public static PacketBodyType fromId(int index) { + for (PacketBodyType value : values()) + if (value.getIndex() == index) return value; + + throw new IllegalArgumentException("invalid index: " + index); + } + + public Class getPacketClass() { + return packetClass; + } + + public boolean canEncrypt() { + return encrypted; + } + + public PacketBodyType getAcknolwedgedBy() { + return acknolwedgedBy; + } + + public boolean canResend() { + return resend; + } + + public boolean isSplittable() { + return splittable; + } + + public boolean isCompressible() { + return compressible; + } + + public boolean mustEncrypt() { + return mustEncrypt; + } +} diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/packet/PacketFragment.java b/src/main/java/com/github/manevolent/ts3j/protocol/packet/PacketFragment.java deleted file mode 100644 index 77822b5..0000000 --- a/src/main/java/com/github/manevolent/ts3j/protocol/packet/PacketFragment.java +++ /dev/null @@ -1,38 +0,0 @@ -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; - } -} diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/packet/PacketType.java b/src/main/java/com/github/manevolent/ts3j/protocol/packet/PacketType.java deleted file mode 100644 index 1b268af..0000000 --- a/src/main/java/com/github/manevolent/ts3j/protocol/packet/PacketType.java +++ /dev/null @@ -1,69 +0,0 @@ -package com.github.manevolent.ts3j.protocol.packet; - -public enum PacketType { - VOICE(0x0, null, null, false, false, false, false), - 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, 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); - - private final int index; - private final Class packetClass; - - private final PacketType acknolwedgedBy; - private final boolean encrypted, resend, splittable, compressible; - - PacketType(int index, Class packetClass, - PacketType acknolwedgedBy, - boolean encrypted, - boolean resend, - boolean splittable, - boolean compressible) { - this.index = index; - this.packetClass = packetClass; - this.acknolwedgedBy = acknolwedgedBy; - this.resend = resend; - this.encrypted = encrypted; - this.splittable = splittable; - this.compressible = compressible; - } - - public int getIndex() { - return index; - } - - public static PacketType fromId(int index) { - for (PacketType value : values()) - if (value.getIndex() == index) return value; - - throw new IllegalArgumentException("invalid index: " + index); - } - - public Class getPacketClass() { - return packetClass; - } - - public boolean isEncrypted() { - return encrypted; - } - - public PacketType getAcknolwedgedBy() { - return acknolwedgedBy; - } - - public boolean canResend() { - return resend; - } - - public boolean isSplittable() { - return splittable; - } - - public boolean isCompressible() { - return compressible; - } -} diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/packet/channel/PacketChannel.java b/src/main/java/com/github/manevolent/ts3j/protocol/packet/channel/PacketChannel.java deleted file mode 100644 index 849c248..0000000 --- a/src/main/java/com/github/manevolent/ts3j/protocol/packet/channel/PacketChannel.java +++ /dev/null @@ -1,50 +0,0 @@ -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 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(); - } - } -} diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/packet/channel/ReassemblyQueue.java b/src/main/java/com/github/manevolent/ts3j/protocol/packet/channel/ReassemblyQueue.java deleted file mode 100644 index 066927d..0000000 --- a/src/main/java/com/github/manevolent/ts3j/protocol/packet/channel/ReassemblyQueue.java +++ /dev/null @@ -1,88 +0,0 @@ -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 queue = new LinkedHashMap<>(); - private final Map 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 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; - } - } - } -} diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/PacketHandler.java b/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/PacketHandler.java index 871796a..10ff149 100644 --- a/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/PacketHandler.java +++ b/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/PacketHandler.java @@ -1,30 +1,31 @@ package com.github.manevolent.ts3j.protocol.packet.handler; -import com.github.manevolent.ts3j.protocol.NetworkPacket; -import com.github.manevolent.ts3j.protocol.LocalEndpoint; +import com.github.manevolent.ts3j.protocol.Packet; +import com.github.manevolent.ts3j.protocol.socket.client.TeamspeakClientSocket; import java.io.IOException; +import java.util.concurrent.TimeoutException; public abstract class PacketHandler { - private final LocalEndpoint endpoint; + private final TeamspeakClientSocket clientSocket; - protected PacketHandler(LocalEndpoint endpoint) { - this.endpoint = endpoint; + protected PacketHandler(TeamspeakClientSocket clientSocket) { + this.clientSocket = clientSocket; } public void onUnassigning() throws IOException { } - public void onAssigned() throws IOException { + public void onAssigned() throws IOException, TimeoutException { } - public void handlePacket(NetworkPacket packet) throws IOException { + public void handlePacket(Packet packet) throws IOException, TimeoutException { } - public LocalEndpoint getEndpoint() { - return endpoint; + public TeamspeakClientSocket getClient() { + return clientSocket; } } diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/client/LocalClientHandlerConnected.java b/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/client/LocalClientHandlerConnected.java deleted file mode 100644 index d33da6b..0000000 --- a/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/client/LocalClientHandlerConnected.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.github.manevolent.ts3j.protocol.packet.handler.client; - -import com.github.manevolent.ts3j.protocol.client.LocalTeamspeakClient; - -public class LocalClientHandlerConnected extends LocalClientHandler { - public LocalClientHandlerConnected(LocalTeamspeakClient client) { - super(client); - } -} diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/client/LocalClientHandlerDisconnected.java b/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/client/LocalClientHandlerDisconnected.java deleted file mode 100644 index e57aa51..0000000 --- a/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/client/LocalClientHandlerDisconnected.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.github.manevolent.ts3j.protocol.packet.handler.client; - -import com.github.manevolent.ts3j.protocol.client.LocalTeamspeakClient; - -public class LocalClientHandlerDisconnected extends LocalClientHandler { - public LocalClientHandlerDisconnected(LocalTeamspeakClient client) { - super(client); - } -} diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/client/LocalClientHandler.java b/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/local/LocalClientHandler.java similarity index 55% rename from src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/client/LocalClientHandler.java rename to src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/local/LocalClientHandler.java index c98c40f..3dd883a 100644 --- a/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/client/LocalClientHandler.java +++ b/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/local/LocalClientHandler.java @@ -1,16 +1,14 @@ -package com.github.manevolent.ts3j.protocol.packet.handler.client; +package com.github.manevolent.ts3j.protocol.packet.handler.local; import com.github.manevolent.ts3j.protocol.client.ClientConnectionState; -import com.github.manevolent.ts3j.protocol.client.LocalTeamspeakClient; import com.github.manevolent.ts3j.protocol.packet.handler.PacketHandler; - -import java.io.IOException; +import com.github.manevolent.ts3j.protocol.socket.client.LocalTeamspeakClientSocket; public abstract class LocalClientHandler extends PacketHandler { - private final LocalTeamspeakClient client; + private final LocalTeamspeakClientSocket client; - protected LocalClientHandler(LocalTeamspeakClient client) { + protected LocalClientHandler(LocalTeamspeakClientSocket client) { super(client); this.client = client; @@ -20,7 +18,7 @@ public abstract class LocalClientHandler extends PacketHandler { } - protected LocalTeamspeakClient getClient() { + public LocalTeamspeakClientSocket getClient() { return client; } } diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/local/LocalClientHandlerConnected.java b/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/local/LocalClientHandlerConnected.java new file mode 100644 index 0000000..3d2d9a1 --- /dev/null +++ b/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/local/LocalClientHandlerConnected.java @@ -0,0 +1,9 @@ +package com.github.manevolent.ts3j.protocol.packet.handler.local; + +import com.github.manevolent.ts3j.protocol.socket.client.LocalTeamspeakClientSocket; + +public class LocalClientHandlerConnected extends LocalClientHandler { + public LocalClientHandlerConnected(LocalTeamspeakClientSocket client) { + super(client); + } +} diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/client/LocalClientHandlerConnecting.java b/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/local/LocalClientHandlerConnecting.java similarity index 78% rename from src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/client/LocalClientHandlerConnecting.java rename to src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/local/LocalClientHandlerConnecting.java index d83acb3..877478b 100644 --- a/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/client/LocalClientHandlerConnecting.java +++ b/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/local/LocalClientHandlerConnecting.java @@ -1,14 +1,12 @@ -package com.github.manevolent.ts3j.protocol.packet.handler.client; +package com.github.manevolent.ts3j.protocol.packet.handler.local; import com.github.manevolent.ts3j.command.SimpleCommand; import com.github.manevolent.ts3j.command.part.CommandSingleParameter; -import com.github.manevolent.ts3j.license.License; -import com.github.manevolent.ts3j.protocol.NetworkPacket; +import com.github.manevolent.ts3j.protocol.Packet; import com.github.manevolent.ts3j.protocol.ProtocolRole; -import com.github.manevolent.ts3j.protocol.client.ClientConnectionState; -import com.github.manevolent.ts3j.protocol.client.LocalTeamspeakClient; -import com.github.manevolent.ts3j.protocol.packet.Packet2Command; -import com.github.manevolent.ts3j.protocol.packet.Packet8Init1; +import com.github.manevolent.ts3j.protocol.packet.PacketBody2Command; +import com.github.manevolent.ts3j.protocol.packet.PacketBody8Init1; +import com.github.manevolent.ts3j.protocol.socket.client.LocalTeamspeakClientSocket; import com.github.manevolent.ts3j.util.Ts3Crypt; import com.github.manevolent.ts3j.util.Ts3Logging; import javafx.util.Pair; @@ -16,27 +14,26 @@ import org.bouncycastle.math.ec.ECPoint; import java.io.IOException; import java.math.BigInteger; -import java.nio.ByteBuffer; import java.nio.charset.Charset; import java.util.Base64; -import java.util.List; import java.util.Random; +import java.util.concurrent.TimeoutException; public class LocalClientHandlerConnecting extends LocalClientHandler { private byte[] randomBytes; private byte[] alphaBytes; - public LocalClientHandlerConnecting(LocalTeamspeakClient client) { + public LocalClientHandlerConnecting(LocalTeamspeakClientSocket client) { super(client); } @Override - public void onAssigned() throws IOException { - Packet8Init1 packet = new Packet8Init1(ProtocolRole.CLIENT); + public void onAssigned() throws IOException, TimeoutException { + PacketBody8Init1 packet = new PacketBody8Init1(ProtocolRole.CLIENT); // Initialize connection - Packet8Init1.Step0 step = new Packet8Init1.Step0(); + PacketBody8Init1.Step0 step = new PacketBody8Init1.Step0(); Random random = new Random(); randomBytes = new byte[4]; random.nextBytes(randomBytes); @@ -47,7 +44,7 @@ public class LocalClientHandlerConnecting extends LocalClientHandler { sendInit1(packet); } - private void sendInit1(Packet8Init1 packet) throws IOException { + private void sendInit1(PacketBody8Init1 packet) throws IOException, TimeoutException { Ts3Logging.debug("Connecting: sending " + packet.getClass().getSimpleName() + ":" + packet.getStep().getClass().getSimpleName() + "..." @@ -55,19 +52,19 @@ public class LocalClientHandlerConnecting extends LocalClientHandler { packet.setVersion(new byte[] { 0x09, (byte)0x83, (byte)0x8C, (byte)0xCF }); - getClient().send(packet); + getClient().writePacket(packet); } @Override - public void handlePacket(NetworkPacket packet) throws IOException { - Ts3Logging.debug("Connecting: handling " + packet.getPacket().getClass().getSimpleName()); + public void handlePacket(Packet packet) throws IOException, TimeoutException { + Ts3Logging.debug("Connecting: handling " + packet.getBody().getClass().getSimpleName()); - if (packet.getPacket() instanceof Packet8Init1) { - Packet8Init1 init1 = (Packet8Init1) packet.getPacket(); + if (packet.getBody() instanceof PacketBody8Init1) { + PacketBody8Init1 init1 = (PacketBody8Init1) packet.getBody(); switch (init1.getStep().getNumber()) { case 1: - Packet8Init1.Step1 serverReplyStep1 = (Packet8Init1.Step1)init1.getStep(); + PacketBody8Init1.Step1 serverReplyStep1 = (PacketBody8Init1.Step1)init1.getStep(); // Check nonce. It's received backwards, so walk backwards over the array received for (int i = 0; i < 4; i ++) { if (randomBytes[3 - i] != serverReplyStep1.getA0reversed()[i]) @@ -75,9 +72,9 @@ public class LocalClientHandlerConnecting extends LocalClientHandler { } // Build response - Packet8Init1 response2 = new Packet8Init1(ProtocolRole.CLIENT); + PacketBody8Init1 response2 = new PacketBody8Init1(ProtocolRole.CLIENT); - Packet8Init1.Step2 step2 = new Packet8Init1.Step2(); + PacketBody8Init1.Step2 step2 = new PacketBody8Init1.Step2(); step2.setA0reversed(serverReplyStep1.getA0reversed()); step2.setServerStuff(serverReplyStep1.getServerStuff()); response2.setStep(step2); @@ -85,7 +82,7 @@ public class LocalClientHandlerConnecting extends LocalClientHandler { sendInit1(response2); break; case 3: - Packet8Init1.Step3 serverReplyStep3 = (Packet8Init1.Step3)init1.getStep(); + PacketBody8Init1.Step3 serverReplyStep3 = (PacketBody8Init1.Step3)init1.getStep(); // Calculate 'y' // which is the result of x ^ (2 ^ level) % n as an unsigned @@ -113,9 +110,9 @@ public class LocalClientHandlerConnecting extends LocalClientHandler { ); // Build response - Packet8Init1 response4 = new Packet8Init1(ProtocolRole.CLIENT); + PacketBody8Init1 response4 = new PacketBody8Init1(ProtocolRole.CLIENT); - Packet8Init1.Step4 step4 = new Packet8Init1.Step4(); + PacketBody8Init1.Step4 step4 = new PacketBody8Init1.Step4(); step4.setLevel(serverReplyStep3.getLevel()); step4.setX(serverReplyStep3.getX()); @@ -130,7 +127,7 @@ public class LocalClientHandlerConnecting extends LocalClientHandler { initiv.add(new CommandSingleParameter("alpha", Base64.getEncoder().encodeToString(alphaBytes))); - initiv.add(new CommandSingleParameter("omega", getClient().getLocalIdentity().getPublicKeyString())); + initiv.add(new CommandSingleParameter("omega", getClient().getIdentity().getPublicKeyString())); initiv.add(new CommandSingleParameter("ot", "1")); // constant, set to 1 @@ -151,8 +148,8 @@ public class LocalClientHandlerConnecting extends LocalClientHandler { throw new IllegalArgumentException("unexpected Init1 server step: " + init1.getStep().getNumber()); } - } else if (packet.getPacket() instanceof Packet2Command) { - SimpleCommand command = ((Packet2Command) packet.getPacket()).parse(); + } else if (packet.getBody() instanceof PacketBody2Command) { + SimpleCommand command = ((PacketBody2Command) packet.getBody()).parse(); if (command.getName().equalsIgnoreCase("initivexpand2")) { // 3.2.2 initivexpand2 (Client <- Server) @@ -186,12 +183,12 @@ public class LocalClientHandlerConnecting extends LocalClientHandler { byte[] toSign = new byte[86]; System.arraycopy(keyPair.getKey(), 0, toSign, 0, 32); System.arraycopy(beta, 0, toSign, 32, 54); - byte[] sign = getClient().getLocalIdentity().sign(toSign); + byte[] sign = getClient().getIdentity().sign(toSign); Ts3Logging.debug("Created proof (clientek): " + Ts3Logging.getHex(sign)); // Send clientek (telling them the shared secret) - Packet2Command clientek = new Packet2Command(ProtocolRole.CLIENT); + PacketBody2Command clientek = new PacketBody2Command(ProtocolRole.CLIENT); clientek.setText( new SimpleCommand( "clientek", ProtocolRole.CLIENT, @@ -199,12 +196,14 @@ public class LocalClientHandlerConnecting extends LocalClientHandler { new CommandSingleParameter("proof", Base64.getEncoder().encodeToString(sign)) ).build() ); - getClient().send(clientek); + getClient().writePacket(clientek); - Ts3Crypt.SecureChannelParameters secureChannelParameters = - Ts3Crypt.cryptoInit2(license, alphaBytes, omega, proof, beta, keyPair.getValue()); + getClient().setSecureParameters( + Ts3Crypt.cryptoInit2(license, alphaBytes, omega, proof, beta, keyPair.getValue()) + ); + + PacketBody2Command clientinit = new PacketBody2Command(ProtocolRole.CLIENT); - Packet2Command clientinit = new Packet2Command(ProtocolRole.CLIENT); clientinit.setText( new SimpleCommand( "clientinit", ProtocolRole.CLIENT, @@ -220,11 +219,12 @@ public class LocalClientHandlerConnecting extends LocalClientHandler { new CommandSingleParameter("client_nickname_phonetic", "test"), new CommandSingleParameter("client_meta_data", ""), new CommandSingleParameter("client_default_token", ""), - new CommandSingleParameter("client_key_offset", Long.toString(getClient().getLocalIdentity().getKeyOffset())), + new CommandSingleParameter("client_key_offset", Long.toString(getClient().getIdentity().getKeyOffset())), new CommandSingleParameter("hwid", "87056c6e1268aaf5055abf8256415e0e,408978b6d98810cc03f0aa16a4c75600") ).build() ); - getClient().send(clientinit); + + getClient().writePacket(clientinit); } } } diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/local/LocalClientHandlerDisconnected.java b/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/local/LocalClientHandlerDisconnected.java new file mode 100644 index 0000000..30e6faa --- /dev/null +++ b/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/local/LocalClientHandlerDisconnected.java @@ -0,0 +1,9 @@ +package com.github.manevolent.ts3j.protocol.packet.handler.local; + +import com.github.manevolent.ts3j.protocol.socket.client.LocalTeamspeakClientSocket; + +public class LocalClientHandlerDisconnected extends LocalClientHandler { + public LocalClientHandlerDisconnected(LocalTeamspeakClientSocket client) { + super(client); + } +} diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/local/LocalClientHandlerNegotiating.java b/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/local/LocalClientHandlerNegotiating.java new file mode 100644 index 0000000..2d204bc --- /dev/null +++ b/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/local/LocalClientHandlerNegotiating.java @@ -0,0 +1,9 @@ +package com.github.manevolent.ts3j.protocol.packet.handler.local; + +import com.github.manevolent.ts3j.protocol.socket.client.LocalTeamspeakClientSocket; + +public class LocalClientHandlerNegotiating extends LocalClientHandler { + public LocalClientHandlerNegotiating(LocalTeamspeakClientSocket client) { + super(client); + } +} diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/packet/transformation/PacketTransformation.java b/src/main/java/com/github/manevolent/ts3j/protocol/packet/transformation/PacketTransformation.java index 1dccad8..9511b01 100644 --- a/src/main/java/com/github/manevolent/ts3j/protocol/packet/transformation/PacketTransformation.java +++ b/src/main/java/com/github/manevolent/ts3j/protocol/packet/transformation/PacketTransformation.java @@ -1,6 +1,6 @@ package com.github.manevolent.ts3j.protocol.packet.transformation; -import com.github.manevolent.ts3j.protocol.NetworkPacket; +import com.github.manevolent.ts3j.protocol.Packet; import com.github.manevolent.ts3j.protocol.ProtocolRole; import com.github.manevolent.ts3j.protocol.header.PacketHeader; import com.github.manevolent.ts3j.util.Ts3Logging; @@ -72,7 +72,7 @@ public class PacketTransformation { return new Pair<>(key, nonce); } - public ByteBuffer encrypt(NetworkPacket packet) { + public ByteBuffer encrypt(Packet packet) { Pair parameters = computeParameters(packet.getHeader()); // Write header (this is temporary) @@ -93,7 +93,7 @@ public class PacketTransformation { headerWithoutMac ); - int dataLen = packet.getPacket().getSize(); + int dataLen = packet.getBody().getSize(); ByteBuffer packetBuffer = packet.writeBody(ByteBuffer.allocate(dataLen).order(ByteOrder.BIG_ENDIAN)); byte[] result; diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/server/RemoteTeamspeakServer.java b/src/main/java/com/github/manevolent/ts3j/protocol/server/RemoteTeamspeakServer.java deleted file mode 100644 index 4ca98b9..0000000 --- a/src/main/java/com/github/manevolent/ts3j/protocol/server/RemoteTeamspeakServer.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.github.manevolent.ts3j.protocol.server; - -import com.github.manevolent.ts3j.protocol.NetworkPacket; -import com.github.manevolent.ts3j.protocol.RemoteEndpoint; -import com.github.manevolent.ts3j.protocol.SocketRole; -import com.github.manevolent.ts3j.protocol.client.LocalTeamspeakClient; -import com.github.manevolent.ts3j.protocol.packet.Packet; - -import java.io.IOException; -import java.net.InetSocketAddress; - -public class RemoteTeamspeakServer extends RemoteEndpoint implements TeamspeakServer { - private final LocalTeamspeakClient client; - private final InetSocketAddress address; - - public RemoteTeamspeakServer(LocalTeamspeakClient client, - InetSocketAddress address) { - this.client = client; - this.address = address; - } - - @Override - public SocketRole getRole() { - return SocketRole.SERVER; - } - - @Override - public void send(Packet packet) throws IOException { - client.send(packet); - } - - @Override - public InetSocketAddress getRemoteAddress() { - return address; - } -} diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/server/TeamspeakServer.java b/src/main/java/com/github/manevolent/ts3j/protocol/server/TeamspeakServer.java deleted file mode 100644 index 2687855..0000000 --- a/src/main/java/com/github/manevolent/ts3j/protocol/server/TeamspeakServer.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.github.manevolent.ts3j.protocol.server; - -import com.github.manevolent.ts3j.protocol.Endpoint; - -import java.net.InetSocketAddress; - -public interface TeamspeakServer extends Endpoint { - - - -} diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/socket/AbstractTeamspeakSocket.java b/src/main/java/com/github/manevolent/ts3j/protocol/socket/AbstractTeamspeakSocket.java index 1716064..e1a7d1a 100644 --- a/src/main/java/com/github/manevolent/ts3j/protocol/socket/AbstractTeamspeakSocket.java +++ b/src/main/java/com/github/manevolent/ts3j/protocol/socket/AbstractTeamspeakSocket.java @@ -1,18 +1,15 @@ package com.github.manevolent.ts3j.protocol.socket; -import com.github.manevolent.ts3j.protocol.packet.transformation.DefaultPacketTransformation; -import com.github.manevolent.ts3j.protocol.packet.transformation.PacketTransformation; +import com.github.manevolent.ts3j.protocol.SocketRole; public abstract class AbstractTeamspeakSocket implements TeamspeakSocket { - private PacketTransformation transformation = new DefaultPacketTransformation(); + private final SocketRole role; - @Override - public PacketTransformation getPacketTransformation() { - return transformation; + protected AbstractTeamspeakSocket(SocketRole role) { + this.role = role; } - @Override - public void setPacketTransformation(PacketTransformation transformation) { - this.transformation = transformation; + public SocketRole getRole() { + return role; } } diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/socket/LocalTeamspeakSocket.java b/src/main/java/com/github/manevolent/ts3j/protocol/socket/LocalTeamspeakSocket.java deleted file mode 100644 index 1c459c8..0000000 --- a/src/main/java/com/github/manevolent/ts3j/protocol/socket/LocalTeamspeakSocket.java +++ /dev/null @@ -1,176 +0,0 @@ -package com.github.manevolent.ts3j.protocol.socket; - -import com.github.manevolent.ts3j.protocol.NetworkPacket; -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; -import java.net.*; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; - -/** - * Low-level protocol socket (not a client, used for server implementation also) - * - * Used simply to transcribe packets on the network - */ -public class LocalTeamspeakSocket extends AbstractTeamspeakSocket implements TeamspeakSocket { - private final SocketRole socketRole; - - private final DatagramSocket datagramSocket; - private final DatagramPacket datagramPacket = new DatagramPacket(new byte[1500], 1500); - - public LocalTeamspeakSocket(SocketRole socketRole, DatagramSocket datagramSocket) { - this.socketRole = socketRole; - this.datagramSocket = datagramSocket; - } - - public LocalTeamspeakSocket(SocketRole socketRole) throws SocketException { - this(socketRole, new DatagramSocket()); - } - - public SocketRole getSocketRole() { - return socketRole; - } - - public InetSocketAddress getLocalAddress() { - return new InetSocketAddress(datagramSocket.getLocalAddress(), datagramSocket.getLocalPort()); - } - - public InetSocketAddress getRemoteAddress() { - return new InetSocketAddress(datagramSocket.getInetAddress(), datagramSocket.getPort()); - } - - public void connect(SocketAddress remote) throws SocketException { - datagramSocket.connect(remote); - } - - /** - * Writes an actual datagram to the network - * @param packet Packet to write - * @throws IOException - */ - private void sendNetworkPacket(DatagramPacket packet) throws IOException { - if (packet.getAddress() == null || packet.getPort() < 0) - packet.setSocketAddress(datagramSocket.getRemoteSocketAddress()); - - Ts3Logging.debug( - "[NETWORK] WRITE Len=" + packet.getLength() + " to " + packet.getSocketAddress() - ); - - //Ts3Logging.debug(Ts3Logging.getHex(packet.getData(), packet.getLength())); - - datagramSocket.send(packet); - } - - /** - * Sends a NetworkPacket to the network, first converting it to a datagram. - * @param header Header to send - * @throws IOException - */ - public void send(PacketHeader header, Packet packet) throws IOException { - if (header == null) - throw new NullPointerException("header"); - else if (header.getRole() != getSocketRole().getOut()) - throw new IllegalArgumentException("packet role mismatch: " + - 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); - - // Flush to a buffer - ByteBuffer outputBuffer; - - if (networkPacket.getHeader().getType().isEncrypted() && - !networkPacket.getHeader().getPacketFlag(HeaderFlag.UNENCRYPTED)) { - outputBuffer = getPacketTransformation().encrypt(networkPacket); - } else { - outputBuffer = ByteBuffer.allocate(networkPacket.getSize()); - - outputBuffer.order(ByteOrder.BIG_ENDIAN); - networkPacket.writeHeader(outputBuffer); - networkPacket.writeBody(outputBuffer); - } - - Ts3Logging.debug("[PROTOCOL] WRITE " + packet.getType().name()); - - sendNetworkPacket(new DatagramPacket(outputBuffer.array(), outputBuffer.position())); - } - - /** - * Receives a datagram from the network. - * @return Datagram. - * @throws IOException - */ - private DatagramPacket receiveNetworkPacket() throws IOException { - datagramSocket.receive(datagramPacket); - - Ts3Logging.debug( - "[NETWORK] READ Len=" + datagramPacket.getLength() + " from " + datagramPacket.getSocketAddress() - ); - - //Ts3Logging.debug(Ts3Logging.getHex(datagramPacket.getData(), datagramPacket.getLength())); - - return datagramPacket; - } - - public NetworkPacket receive() throws IOException { - while (true) { - DatagramPacket datagramPacket = receiveNetworkPacket(); - - NetworkPacket networkPacket = new NetworkPacket(getSocketRole().getIn()); - - ByteBuffer buffer = ByteBuffer.wrap(datagramPacket.getData()); - networkPacket.readHeader(buffer); - - boolean fragment = networkPacket.getHeader().getPacketFlag(HeaderFlag.FRAGMENTED); - if (fragment && !networkPacket.getHeader().getType().isSplittable()) continue; // drop packet - - ByteBuffer packetBuffer; - - 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; - } - } - -} diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/socket/TeamspeakSocket.java b/src/main/java/com/github/manevolent/ts3j/protocol/socket/TeamspeakSocket.java index afd9f4d..d757af6 100644 --- a/src/main/java/com/github/manevolent/ts3j/protocol/socket/TeamspeakSocket.java +++ b/src/main/java/com/github/manevolent/ts3j/protocol/socket/TeamspeakSocket.java @@ -1,19 +1,14 @@ package com.github.manevolent.ts3j.protocol.socket; import com.github.manevolent.ts3j.protocol.SocketRole; -import com.github.manevolent.ts3j.protocol.header.PacketHeader; -import com.github.manevolent.ts3j.protocol.packet.Packet; -import com.github.manevolent.ts3j.protocol.packet.transformation.PacketTransformation; -import java.io.IOException; +import java.io.Closeable; +import java.net.InetSocketAddress; -public interface TeamspeakSocket { - SocketRole getSocketRole(); +public interface TeamspeakSocket extends Closeable { - void send(PacketHeader header, Packet packet) throws IOException; + SocketRole getRole(); - PacketTransformation getPacketTransformation(); - - void setPacketTransformation(PacketTransformation transformation); + InetSocketAddress getLocalSocketAddress(); } 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 new file mode 100644 index 0000000..fa8b5ee --- /dev/null +++ b/src/main/java/com/github/manevolent/ts3j/protocol/socket/client/AbstractTeamspeakClientSocket.java @@ -0,0 +1,452 @@ +package com.github.manevolent.ts3j.protocol.socket.client; + +import com.github.manevolent.ts3j.command.Command; +import com.github.manevolent.ts3j.command.response.CommandResponse; +import com.github.manevolent.ts3j.identity.Identity; +import com.github.manevolent.ts3j.protocol.NetworkPacket; +import com.github.manevolent.ts3j.protocol.Packet; +import com.github.manevolent.ts3j.protocol.SocketRole; +import com.github.manevolent.ts3j.protocol.client.ClientConnectionState; +import com.github.manevolent.ts3j.protocol.header.HeaderFlag; +import com.github.manevolent.ts3j.protocol.header.PacketHeader; +import com.github.manevolent.ts3j.protocol.packet.PacketBody; +import com.github.manevolent.ts3j.protocol.packet.PacketBody6Ack; +import com.github.manevolent.ts3j.protocol.packet.PacketBodyFragment; +import com.github.manevolent.ts3j.protocol.packet.PacketBodyType; +import com.github.manevolent.ts3j.protocol.packet.handler.PacketHandler; +import com.github.manevolent.ts3j.protocol.packet.handler.local.LocalClientHandler; +import com.github.manevolent.ts3j.protocol.packet.transformation.DefaultPacketTransformation; +import com.github.manevolent.ts3j.protocol.packet.transformation.PacketTransformation; +import com.github.manevolent.ts3j.protocol.socket.AbstractTeamspeakSocket; +import com.github.manevolent.ts3j.util.Ts3Crypt; +import com.github.manevolent.ts3j.util.Ts3Logging; + +import java.io.IOException; +import java.net.DatagramPacket; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.*; +import java.util.concurrent.TimeoutException; + +public abstract class AbstractTeamspeakClientSocket + extends AbstractTeamspeakSocket + implements TeamspeakClientSocket { + private final Object connectionStateLock = new Object(); + private final Map reassemblyQueue = new LinkedHashMap<>(); + private final NetworkReader networkReader = new NetworkReader(); + private final Map clientOptions = new LinkedHashMap<>(); + + private Identity identity = null; + private ClientConnectionState connectionState = null; + private PacketTransformation transformation = new DefaultPacketTransformation(); + private PacketHandler handler; + + private Thread networkThread = null; + private boolean reading = false; + + 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()) + reassemblyQueue.put(bodyType, new ReassemblyQueue()); + } + + protected void start() { + Thread networkThread = new Thread(networkReader); + networkThread.setDaemon(true); + networkThread.start(); + } + + @Override + public Map getOptions() { + return clientOptions; + } + + public final void setSecureParameters(Ts3Crypt.SecureChannelParameters parameters) { + setTransformation(new PacketTransformation(parameters.getIvStruct())); + } + + protected abstract NetworkPacket readNetworkPacket() throws IOException; + + protected abstract void writeNetworkPacket(NetworkPacket packet) throws IOException; + + public void setState(ClientConnectionState state) throws IOException, TimeoutException { + synchronized (connectionStateLock) { + if (state != this.connectionState) { + Ts3Logging.debug("State changing: " + state.name()); + + boolean wasDisconnected = this.connectionState == ClientConnectionState.DISCONNECTED; + + if (wasDisconnected) setReading(true); + + this.connectionState = state; + + if (this.connectionState == ClientConnectionState.DISCONNECTED) setReading(false); + + PacketHandler handler = getHandler(); + + if (handler != null) + ((LocalClientHandler) handler).handleConnectionStateChanging(state); + + if (handler == null || handler.getClass() != state.getHandlerClass()) { + Ts3Logging.debug("Assigning " + state.getHandlerClass().getName() + " handler..."); + + setHandler(state.createHandler(this)); + } + + if (this.connectionState == ClientConnectionState.DISCONNECTED) + clientOptions.clear(); + + connectionStateLock.notifyAll(); + + Ts3Logging.debug("State changed: " + state.name()); + } + } + } + + public ClientConnectionState getState() { + return connectionState; + } + + public void waitForState(ClientConnectionState state, long wait) + throws InterruptedException, TimeoutException { + synchronized (connectionStateLock) { + long start = System.currentTimeMillis(); + + while (connectionState != state && System.currentTimeMillis() - start < wait) { + connectionStateLock.wait(Math.max(0L, wait - (System.currentTimeMillis() - start))); + } + + if (connectionState != state) + throw new TimeoutException("timeout waiting for " + state.name() + " state"); + } + } + + public boolean isConnected() { + return connectionState == ClientConnectionState.CONNECTED; + } + + /** + * 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"); + + CommandResponse response; + + if (command.willExpectResponse()) { + + //command.appendParameter(new CommandSingleParameter("return_code", returnCodeIndex)); + response = new CommandResponse(command, 0); // TODO + } else { + response = new CommandResponse(command, -1); + } + + String commandString = command.toString(); + + response.setDispatchedTime(System.currentTimeMillis()); + + return response; + } + + @Override + public void writePacket(PacketBody body) throws IOException, TimeoutException { + PacketHeader header; + + try { + header = body.getRole().getHeaderClass().newInstance(); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + + // Construct a network packet + Packet packet = new Packet(body.getRole()); + packet.setHeader(header); + packet.setBody(body); + + writePacket(packet); + } + + @Override + public void writePacket(Packet packet) throws IOException, TimeoutException { + if (packet.getHeader() == null) + throw new NullPointerException("header"); + else if (packet.getHeader().getRole() != getRole().getOut()) + throw new IllegalArgumentException("packet role mismatch: " + + packet.getHeader().getRole().name() + " != " + + getRole().getOut().name()); + + // Ensure type matches + packet.getHeader().setType(packet.getHeader().getType()); + + // Make sure new protocol is set if it must be set + if (getState() == ClientConnectionState.CONNECTING) + packet.getHeader().setPacketFlag(HeaderFlag.NEW_PROTOCOL, true); + + // Set header values + packet.getBody().setHeaderValues(packet.getHeader()); + + // Flush to a buffer + ByteBuffer outputBuffer; + + if (packet.getBody().getType().canEncrypt() && + !packet.getHeader().getPacketFlag(HeaderFlag.UNENCRYPTED)) { + outputBuffer = getTransformation().encrypt(packet); + } else { + outputBuffer = ByteBuffer.allocate(packet.getSize()); + + outputBuffer.order(ByteOrder.BIG_ENDIAN); + packet.writeHeader(outputBuffer); + packet.writeBody(outputBuffer); + } + + Ts3Logging.debug("[PROTOCOL] WRITE " + packet.getHeader().getType().name()); + + // Find if the packet must be acknowledged and create a promise for that action + // TODO + + writeNetworkPacket(new NetworkPacket( + new DatagramPacket(outputBuffer.array(), outputBuffer.position()), + packet.getHeader(), + (ByteBuffer) outputBuffer.position(0) + )); + + // If necessary, fulfill promise for the packet acknowledgement or throw TimeoutException + // TODO + } + + private Packet readPacketIntl() throws IOException { + NetworkPacket networkPacket = readNetworkPacket(); + + if (networkPacket.getHeader() == null) throw new NullPointerException("header"); + + Packet packet = new Packet(networkPacket.getHeader().getRole()); + packet.setHeader(networkPacket.getHeader()); + + boolean fragment = networkPacket.getHeader().getPacketFlag(HeaderFlag.FRAGMENTED); + + if (networkPacket.getHeader().getType().isSplittable()) { + if (fragment) + throw new IllegalArgumentException("packet is fragment, but not splittable"); + else { + fragment = reassemblyQueue.get(networkPacket.getHeader().getType()).isReassembling(); + } + } + + boolean encrypted = !networkPacket.getHeader().getPacketFlag(HeaderFlag.UNENCRYPTED) && + networkPacket.getHeader().getType().canEncrypt(); + + if (networkPacket.getHeader().getType().mustEncrypt() && !encrypted) + throw new IllegalArgumentException("packet is unencrypted, but must encrypt"); + + ByteBuffer packetBuffer = + (ByteBuffer) networkPacket.getBuffer().position(networkPacket.getHeader().getSize()); + + // Decrypt + if (encrypted) { + packetBuffer = ByteBuffer.wrap( + getTransformation().decrypt( + networkPacket.getHeader(), + packetBuffer, + networkPacket.getDatagram().getLength() - networkPacket.getHeader().getSize() + ) + ); + } + + // Fragment handling + if (fragment) { + Ts3Logging.debug("[PROTOCOL] READ " + networkPacket.getHeader().getType().name() + " fragment"); + + packet.setBody( + new PacketBodyFragment( + networkPacket.getHeader().getType(), + getRole().getIn() + ) + ); + } else { + Ts3Logging.debug("[PROTOCOL] READ " + networkPacket.getHeader().getType().name()); + } + + // Compression? + // TODO + + // Read packet body + packet.readBody(packetBuffer); + + return packet; + } + + @Override + public Packet readPacket() throws IOException, TimeoutException { + while (true) { + Packet packet = readPacketIntl(); + + // Find if the packet must be acknowledged + PacketBodyType ackType = packet.getHeader().getType().getAcknolwedgedBy(); + if (ackType != null) { + switch (ackType) { + case ACK: + writePacket(new PacketBody6Ack(getRole().getOut(), packet.getHeader().getPacketId())); + break; + } + } + + // Find if the packet is itself an acknowledgement + switch (packet.getHeader().getType()) { + case ACK: + break; + case ACK_LOW: + break; + default: + getHandler().handlePacket(packet); + } + } + } + + @Override + public PacketTransformation getTransformation() { + return transformation; + } + + @Override + public void setTransformation(PacketTransformation transformation) { + this.transformation = transformation; + } + + @Override + public PacketHandler getHandler() { + return handler; + } + + protected void setHandler(PacketHandler handler) throws IOException, TimeoutException { + if (this.handler != handler) { + if (this.handler != null) this.handler.onUnassigning(); + this.handler = handler; + if (handler != null) handler.onAssigned(); + } + } + + public Identity getIdentity() { + return identity; + } + + public void setIdentity(Identity identity) { + this.identity = identity; + } + + protected boolean isReading() { + return reading; + } + + protected void setReading(boolean b) { + if (this.reading != b) { + this.reading = b; + + if (!reading) { + if (networkThread != null) networkThread.interrupt(); + } else { + start(); + } + } + } + + private class NetworkReader implements Runnable { + @Override + public void run() { + while (isReading()) { + try { + readPacket(); + } catch (Exception e) { + if (e instanceof InterruptedException) continue; + + Ts3Logging.debug("Problem handling packet", e); + } + } + } + } + + private class ReassemblyQueue { + private final Map queue = new LinkedHashMap<>(); + private boolean state = false; + + public Packet reassemble(Packet lastFragment) { + // 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--) { + Packet 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); + } + } + + // 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.getBody().getSize()).sum() + lastFragment.getBody().getSize(); + if (totalLength < 0) throw new IllegalArgumentException("reassembly too small: " + totalLength); + + ByteBuffer reassemblyBuffer = ByteBuffer.allocate(totalLength); + + for (Packet old : reassemblyList) + old.writeBody(reassemblyBuffer); + + Packet firstPacket = reassemblyList.get(0); + Packet reassembledPacket = new Packet(firstPacket.getRole()); + reassembledPacket.setHeader(firstPacket.getHeader()); + reassembledPacket.getHeader().setPacketFlag(HeaderFlag.FRAGMENTED, false); + reassembledPacket.readBody((ByteBuffer) reassemblyBuffer.position(0)); + + return reassembledPacket; + } + + public Packet put(Packet packet) { + if (packet.getHeader().getPacketFlag(HeaderFlag.FRAGMENTED)) { + boolean oldState = state; + + state = !state; + + if (!(packet.getBody() instanceof PacketBodyFragment)) + throw new IllegalArgumentException("packet fragment object is not representative of a fragment"); + + boolean fragmentationEnded = oldState; + + queue.put(packet.getHeader().getPacketId(), packet); + + if (fragmentationEnded) + return reassemble(packet); + + return null; + } else { + if (state) { + queue.put(packet.getHeader().getPacketId(), packet); + + return null; + } else { + return packet; + } + } + } + + public boolean isReassembling() { + return state; + } + } +} 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 new file mode 100644 index 0000000..9f1f24b --- /dev/null +++ b/src/main/java/com/github/manevolent/ts3j/protocol/socket/client/LocalTeamspeakClientSocket.java @@ -0,0 +1,124 @@ +package com.github.manevolent.ts3j.protocol.socket.client; + +import com.github.manevolent.ts3j.identity.LocalIdentity; +import com.github.manevolent.ts3j.protocol.NetworkPacket; +import com.github.manevolent.ts3j.protocol.client.ClientConnectionState; +import com.github.manevolent.ts3j.protocol.header.PacketHeader; +import com.github.manevolent.ts3j.util.Ts3Logging; + +import java.io.IOException; +import java.net.DatagramPacket; +import java.net.DatagramSocket; +import java.net.InetSocketAddress; +import java.net.SocketException; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.concurrent.TimeoutException; + +public class LocalTeamspeakClientSocket extends AbstractTeamspeakClientSocket { + private final DatagramSocket socket; + + private final DatagramPacket packet = new DatagramPacket(new byte[500], 500); + + { + try { + socket = new DatagramSocket(); + } catch (SocketException e) { + throw new RuntimeException(e); + } + } + + public LocalTeamspeakClientSocket() { + super(); + } + + @Override + protected NetworkPacket readNetworkPacket() throws IOException { + socket.receive(packet); + + PacketHeader header; + + try { + header = getRole().getIn().getHeaderClass().newInstance(); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + + ByteBuffer buffer = ByteBuffer + .wrap(packet.getData(), packet.getOffset(), packet.getLength()) + .order(ByteOrder.BIG_ENDIAN); + + header.read(buffer); + + return new NetworkPacket( + packet, + header, + buffer + ); + } + + @Override + protected void writeNetworkPacket(NetworkPacket packet) throws IOException { + socket.send(packet.getDatagram()); + } + + + @Override + public LocalIdentity getIdentity() { + return (LocalIdentity) super.getIdentity(); + } + + public void setIdentity(LocalIdentity identity) { + super.setIdentity(identity); + } + + /** + * 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, TimeoutException { + try { + Ts3Logging.debug("Connecting to " + remote + "..."); + + ClientConnectionState connectionState = getState(); + + if (connectionState != ClientConnectionState.DISCONNECTED) + throw new IllegalStateException(connectionState.name()); + + setOption("client.hostname", remote.getHostString()); + setOption("client.password", password); + + socket.connect(remote); + + Ts3Logging.debug("Changing state..."); + setState(ClientConnectionState.CONNECTING); + + waitForState(ClientConnectionState.CONNECTED, timeout); + } catch (TimeoutException e) { + throw e; + } catch (Throwable e) { + setState(ClientConnectionState.DISCONNECTED); + + throw new IOException(e); + } + } + + @Override + protected boolean isReading() { + return socket.isConnected(); + } + + @Override + public InetSocketAddress getLocalSocketAddress() { + return (InetSocketAddress) socket.getLocalSocketAddress(); + } + + @Override + public void close() throws IOException { + socket.close(); + } +} diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/socket/client/TeamspeakClientSocket.java b/src/main/java/com/github/manevolent/ts3j/protocol/socket/client/TeamspeakClientSocket.java new file mode 100644 index 0000000..bc643b5 --- /dev/null +++ b/src/main/java/com/github/manevolent/ts3j/protocol/socket/client/TeamspeakClientSocket.java @@ -0,0 +1,56 @@ +package com.github.manevolent.ts3j.protocol.socket.client; + +import com.github.manevolent.ts3j.identity.Identity; +import com.github.manevolent.ts3j.protocol.Packet; +import com.github.manevolent.ts3j.protocol.packet.PacketBody; +import com.github.manevolent.ts3j.protocol.packet.handler.PacketHandler; +import com.github.manevolent.ts3j.protocol.packet.transformation.PacketTransformation; +import com.github.manevolent.ts3j.protocol.socket.TeamspeakSocket; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.util.Map; +import java.util.concurrent.TimeoutException; + +public interface TeamspeakClientSocket extends TeamspeakSocket { + + Map getOptions(); + + default T getOption(String key, Class clazz) { + Object value = getOptions().get(key); + + if (value == null) return (T) null; + + else if (!value.getClass().isAssignableFrom(clazz)) { + throw new ClassCastException( + clazz.getName() + + " is not assignable to option type " + + value.getClass().getName() + ); + } else + return (T) value; + } + + default Object setOption(String key, Object value) { + return getOptions().put(key, value); + } + + boolean isConnected(); + + Identity getIdentity(); + + void setIdentity(Identity identity); + + PacketTransformation getTransformation(); + + void setTransformation(PacketTransformation transformation); + + PacketHandler getHandler(); + + Packet readPacket() throws IOException, TimeoutException; + + void writePacket(PacketBody body) throws IOException, TimeoutException; + + void writePacket(Packet packet) throws IOException, TimeoutException; + +} diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/socket/server/TeamspeakServerSocket.java b/src/main/java/com/github/manevolent/ts3j/protocol/socket/server/TeamspeakServerSocket.java new file mode 100644 index 0000000..1ba5437 --- /dev/null +++ b/src/main/java/com/github/manevolent/ts3j/protocol/socket/server/TeamspeakServerSocket.java @@ -0,0 +1,9 @@ +package com.github.manevolent.ts3j.protocol.socket.server; + +import com.github.manevolent.ts3j.protocol.socket.client.TeamspeakClientSocket; + +public interface TeamspeakServerSocket { + + TeamspeakClientSocket accept(); + +} diff --git a/src/test/java/com/github/manevolent/ts3j/ServerConnectionTest.java b/src/test/java/com/github/manevolent/ts3j/ServerConnectionTest.java index 7b448d9..9c3bc33 100644 --- a/src/test/java/com/github/manevolent/ts3j/ServerConnectionTest.java +++ b/src/test/java/com/github/manevolent/ts3j/ServerConnectionTest.java @@ -2,7 +2,7 @@ package com.github.manevolent.ts3j; import com.github.manevolent.ts3j.identity.LocalIdentity; import com.github.manevolent.ts3j.protocol.client.ClientConnectionState; -import com.github.manevolent.ts3j.protocol.client.LocalTeamspeakClient; +import com.github.manevolent.ts3j.protocol.socket.client.LocalTeamspeakClientSocket; import junit.framework.TestCase; import java.net.InetSocketAddress; @@ -16,9 +16,9 @@ public class ServerConnectionTest extends TestCase { try { LocalIdentity localIdentity = LocalIdentity.generateNew(8); - LocalTeamspeakClient client = new LocalTeamspeakClient(); + LocalTeamspeakClientSocket client = new LocalTeamspeakClientSocket(); - client.setLocalIdentity(localIdentity); + client.setIdentity(localIdentity); client.connect(new InetSocketAddress( "ts.teamlixo.net", @@ -27,7 +27,7 @@ public class ServerConnectionTest extends TestCase { 10000L ); - assertEquals(client.getConnectionState(), ClientConnectionState.CONNECTED); + assertEquals(client.getState(), ClientConnectionState.CONNECTED); } catch (Throwable ex) { ex.printStackTrace(); }