diff --git a/pom.xml b/pom.xml index c00d129..47d37bc 100644 --- a/pom.xml +++ b/pom.xml @@ -29,5 +29,11 @@ RELEASE test + + + org.bouncycastle + bcprov-jdk15on + 1.60 + \ No newline at end of file diff --git a/src/main/java/com/github/manevolent/ts3j/ClientConnectionState.java b/src/main/java/com/github/manevolent/ts3j/ClientConnectionState.java deleted file mode 100644 index a355cb9..0000000 --- a/src/main/java/com/github/manevolent/ts3j/ClientConnectionState.java +++ /dev/null @@ -1,28 +0,0 @@ - -package com.github.manevolent.ts3j; - -import com.github.manevolent.ts3j.handler.*; - -public enum ClientConnectionState { - CONNECTING(TeamspeakClientHandlerConnecting.class), - CONNECTED(TeamspeakClientHandlerConnected.class), - DISCONNECTED(TeamspeakClientHandlerDisconnected.class); - - private final Class clazz; - - ClientConnectionState(Class clazz) { - this.clazz = clazz; - } - - public Class getHandlerClass() { - return clazz; - } - - public TeamspeakClientHandler createHandler(Teamspeak3Client client) { - try { - return clazz.getConstructor(Teamspeak3Client.class).newInstance(client); - } catch (ReflectiveOperationException ex) { - throw new RuntimeException(ex); - } - } -} \ No newline at end of file diff --git a/src/main/java/com/github/manevolent/ts3j/command/Command.java b/src/main/java/com/github/manevolent/ts3j/command/Command.java index 9ef8974..ae9436e 100644 --- a/src/main/java/com/github/manevolent/ts3j/command/Command.java +++ b/src/main/java/com/github/manevolent/ts3j/command/Command.java @@ -1,8 +1,48 @@ package com.github.manevolent.ts3j.command; import com.github.manevolent.ts3j.command.part.CommandParameter; +import com.github.manevolent.ts3j.protocol.ProtocolRole; public interface Command { + /** + * Gets the name, or label, of this command. + * @return Command label. + */ + String getName(); + + /** + * Gets the intended use role of this command (source). + * @return Command role. + */ + ProtocolRole getRole(); + + /** + * Gets the parameter list of this command. + * @return Parameter list. + */ Iterable getParameters(); - void appendParameter(CommandParameter parameter); + + /** + * Finds if the command is expected to be responded to with a specific return_code. + * This is handled by supporting protocol handlers, not by the command code itself. + * @return true if the command is expected to support a return_code. + */ + default boolean willExpectResponse() { return false; } + + /** + * Builds the command to a netowrk-issueable string. + * The implementation currently defaults to the basic command format for TS3: + * label -flag parameter=value + * @return Baked command string. + */ + default String build() { + StringBuilder builder = new StringBuilder(); + builder.append(getName()); + + for (CommandParameter parameter : getParameters()) { + builder.append(" " + (parameter.toString()).trim()); + } + + return builder.toString(); + } } diff --git a/src/main/java/com/github/manevolent/ts3j/command/SimpleCommand.java b/src/main/java/com/github/manevolent/ts3j/command/SimpleCommand.java index ebe3751..54156d3 100644 --- a/src/main/java/com/github/manevolent/ts3j/command/SimpleCommand.java +++ b/src/main/java/com/github/manevolent/ts3j/command/SimpleCommand.java @@ -1,39 +1,34 @@ package com.github.manevolent.ts3j.command; import com.github.manevolent.ts3j.command.part.CommandParameter; +import com.github.manevolent.ts3j.protocol.ProtocolRole; +import java.util.Collection; import java.util.Collections; -import java.util.LinkedList; -import java.util.List; public class SimpleCommand implements Command { private final String name; - private final List parameterList = new LinkedList<>(); + private final ProtocolRole role; + private final Collection params; - public SimpleCommand(String name) { + public SimpleCommand(String name, ProtocolRole role, Collection params) { this.name = name; + this.role = role; + this.params = params; + } + + @Override + public String getName() { + return name; + } + + @Override + public ProtocolRole getRole() { + return role; } @Override public Iterable getParameters() { - return Collections.unmodifiableList(parameterList); - } - - @Override - public void appendParameter(CommandParameter parameter) { - parameterList.add(parameter); - } - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append(name); - - for (CommandParameter parameter : getParameters()) { - builder.append(" "); - builder.append(parameter.toString()); - } - - return builder.toString(); + return Collections.unmodifiableCollection(params); } } diff --git a/src/main/java/com/github/manevolent/ts3j/command/TypedCommand.java b/src/main/java/com/github/manevolent/ts3j/command/TypedCommand.java new file mode 100644 index 0000000..4dc2b5f --- /dev/null +++ b/src/main/java/com/github/manevolent/ts3j/command/TypedCommand.java @@ -0,0 +1,50 @@ +package com.github.manevolent.ts3j.command; + +import com.github.manevolent.ts3j.command.part.CommandParameter; +import com.github.manevolent.ts3j.protocol.ProtocolRole; + +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; + +public abstract class TypedCommand implements Command { + private final ProtocolRole role; + private final String name; + private final Map parameters = new LinkedHashMap<>(); + + protected TypedCommand(ProtocolRole role, String name) { + this.role = role; + + this.name = name; + } + + protected CommandParameter set(CommandParameter commandParameter) { + this.parameters.put(commandParameter.getName(), commandParameter); + + return commandParameter; + } + + public CommandParameter get(String key) { + return this.parameters.get(key); + } + + @Override + public Iterable getParameters() { + return Collections.unmodifiableCollection(parameters.values()); + } + + @Override + public String getName() { + return name; + } + + @Override + public ProtocolRole getRole() { + return role; + } + + @Override + public String toString() { + return build(); + } +} diff --git a/src/main/java/com/github/manevolent/ts3j/command/part/CommandOption.java b/src/main/java/com/github/manevolent/ts3j/command/part/CommandOption.java index 7dd10b5..00f9ccc 100644 --- a/src/main/java/com/github/manevolent/ts3j/command/part/CommandOption.java +++ b/src/main/java/com/github/manevolent/ts3j/command/part/CommandOption.java @@ -1,17 +1,68 @@ package com.github.manevolent.ts3j.command.part; -import com.github.manevolent.ts3j.enums.CommandPartType; - public class CommandOption implements CommandParameter { - private final String value; + private final String key; + private boolean enabled = false; - public CommandOption(String value) { - this.value = " -" + value; + public CommandOption(String key) { + this.key = key; + } + + + @Override + public void set(boolean value) { + this.enabled = value; } @Override - public CommandPartType getType() { - return CommandPartType.OPTION; + public String getName() { + return key; } + @Override + public void set(char value) { + throw new UnsupportedOperationException(); + } + + @Override + public void set(String value) { + throw new UnsupportedOperationException(); + } + @Override + public void set(byte value) { + throw new UnsupportedOperationException(); + } + + @Override + public void set(short value) { + throw new UnsupportedOperationException(); + } + + @Override + public void set(int value) { + throw new UnsupportedOperationException(); + } + + @Override + public void set(long value) { + throw new UnsupportedOperationException(); + } + + @Override + public void set(float value) { + throw new UnsupportedOperationException(); + } + + @Override + public void set(double value) { + throw new UnsupportedOperationException(); + } + + @Override + public String toString() { + if (enabled) + return "-" + key; + else + return ""; + } } diff --git a/src/main/java/com/github/manevolent/ts3j/command/part/CommandParameter.java b/src/main/java/com/github/manevolent/ts3j/command/part/CommandParameter.java index 1af70fc..3c22f16 100644 --- a/src/main/java/com/github/manevolent/ts3j/command/part/CommandParameter.java +++ b/src/main/java/com/github/manevolent/ts3j/command/part/CommandParameter.java @@ -1,9 +1,23 @@ package com.github.manevolent.ts3j.command.part; -import com.github.manevolent.ts3j.enums.CommandPartType; - public interface CommandParameter { - CommandPartType getType(); + String getName(); + + void set(char value); + void set(String value); + void set(boolean value); + void set(byte value); + void set(short value); + void set(int value); + void set(long value); + void set(float value); + void set(double value); + + /** + * Converts the command part to a usable command string element. + * @return Command string element. + */ + String toString(); } diff --git a/src/main/java/com/github/manevolent/ts3j/command/part/CommandSingleParameter.java b/src/main/java/com/github/manevolent/ts3j/command/part/CommandSingleParameter.java index ef57d87..907c047 100644 --- a/src/main/java/com/github/manevolent/ts3j/command/part/CommandSingleParameter.java +++ b/src/main/java/com/github/manevolent/ts3j/command/part/CommandSingleParameter.java @@ -1,81 +1,70 @@ package com.github.manevolent.ts3j.command.part; -import com.github.manevolent.ts3j.enums.CommandPartType; +import com.github.manevolent.ts3j.util.Ts3String; public class CommandSingleParameter implements CommandParameter { - private final String key, value; + private final String key; + private String value; - public CommandSingleParameter(String key, String value) { + public CommandSingleParameter(String key) { this.key = key; - this.value = value; - } - - public CommandSingleParameter(String key, boolean value) { - this.key = key; - this.value = serialize(value); - } - - public CommandSingleParameter(String key, byte value) { - this.key = key; - this.value = serialize(value); - } - - public CommandSingleParameter(String key, short value) { - this.key = key; - this.value = serialize(value); - } - - public CommandSingleParameter(String key, int value) { - this.key = key; - this.value = serialize(value); - } - - public CommandSingleParameter(String key, long value) { - this.key = key; - this.value = serialize(value); - } - - public CommandSingleParameter(String key, float value) { - this.key = key; - this.value = serialize(value); - } - - public CommandSingleParameter(String key, double value) { - this.key = key; - this.value = serialize(value); } @Override - public CommandPartType getType() { - return CommandPartType.SINGLE_PARAMETER; + public String toString() { + if (value == null) + return ""; + else + return (key + "=" + Ts3String.escape(value.trim())); } - public static String serialize(boolean value) { - return value ? "1": "0"; + @Override + public String getName() { + return key; } - public static String serialize(byte value) { - return Byte.toString(value); + @Override + public void set(char value) { + this.value = Character.toString(value); } - public static String serialize(short value) { - return Short.toString(value); + @Override + public void set(String value) { + this.value = value; } - public static String serialize(int value) { - return Integer.toString(value); + @Override + public void set(boolean value) { + this.value = (value ? "1" : "0"); } - public static String serialize(long value) { - return Long.toString(value); + @Override + public void set(byte value) { + this.value = Byte.toString(value); } - public static String serialize(float value) { - return Float.toString(value); + @Override + public void set(short value) { + this.value = Short.toString(value); } - public static String serialize(double value) { - return Double.toString(value); + @Override + public void set(int value) { + this.value = Integer.toString(value); } + @Override + public void set(long value) { + this.value = Long.toString(value); + } + + @Override + public void set(float value) { + this.value = Float.toString(value); + } + + @Override + public void set(double value) { + this.value = Double.toString(value); + } } diff --git a/src/main/java/com/github/manevolent/ts3j/command/type/client/ClientInitIV.java b/src/main/java/com/github/manevolent/ts3j/command/type/client/ClientInitIV.java new file mode 100644 index 0000000..73f29ae --- /dev/null +++ b/src/main/java/com/github/manevolent/ts3j/command/type/client/ClientInitIV.java @@ -0,0 +1,16 @@ +package com.github.manevolent.ts3j.command.type.client; + +import com.github.manevolent.ts3j.command.TypedCommand; +import com.github.manevolent.ts3j.command.part.CommandSingleParameter; +import com.github.manevolent.ts3j.protocol.ProtocolRole; + +public class ClientInitIV extends TypedCommand { + public ClientInitIV() { + super(ProtocolRole.CLIENT, "clientinitiv"); + + set(new CommandSingleParameter("ot")); + set(new CommandSingleParameter("alpha")); + set(new CommandSingleParameter("omega")); + set(new CommandSingleParameter("ip")); + } +} diff --git a/src/main/java/com/github/manevolent/ts3j/handler/TeamspeakClientHandler.java b/src/main/java/com/github/manevolent/ts3j/handler/TeamspeakClientHandler.java deleted file mode 100644 index e6829c0..0000000 --- a/src/main/java/com/github/manevolent/ts3j/handler/TeamspeakClientHandler.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.github.manevolent.ts3j.handler; - -import com.github.manevolent.ts3j.Teamspeak3Client; -import com.github.manevolent.ts3j.ClientConnectionState; -import com.github.manevolent.ts3j.protocol.NetworkPacket; - -import java.io.IOException; - -public abstract class TeamspeakClientHandler { - private final Teamspeak3Client client; - - protected TeamspeakClientHandler(Teamspeak3Client client) { - this.client = client; - } - - protected final Teamspeak3Client getClient() { - return client; - } - - public void handleConnectionStateChanging(ClientConnectionState clientConnectionState) { - - } - - public void onAssigned() throws IOException { - - } - - public void handlePacket(NetworkPacket packet) throws IOException { - - } -} diff --git a/src/main/java/com/github/manevolent/ts3j/handler/TeamspeakClientHandlerConnected.java b/src/main/java/com/github/manevolent/ts3j/handler/TeamspeakClientHandlerConnected.java deleted file mode 100644 index 7b4fa1c..0000000 --- a/src/main/java/com/github/manevolent/ts3j/handler/TeamspeakClientHandlerConnected.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.github.manevolent.ts3j.handler; - -import com.github.manevolent.ts3j.Teamspeak3Client; - -public class TeamspeakClientHandlerConnected extends TeamspeakClientHandler { - public TeamspeakClientHandlerConnected(Teamspeak3Client client) { - super(client); - } -} diff --git a/src/main/java/com/github/manevolent/ts3j/handler/TeamspeakClientHandlerDisconnected.java b/src/main/java/com/github/manevolent/ts3j/handler/TeamspeakClientHandlerDisconnected.java deleted file mode 100644 index 021f7e7..0000000 --- a/src/main/java/com/github/manevolent/ts3j/handler/TeamspeakClientHandlerDisconnected.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.github.manevolent.ts3j.handler; - -import com.github.manevolent.ts3j.Teamspeak3Client; - -public class TeamspeakClientHandlerDisconnected extends TeamspeakClientHandler { - public TeamspeakClientHandlerDisconnected(Teamspeak3Client client) { - super(client); - } -} diff --git a/src/main/java/com/github/manevolent/ts3j/identity/Identity.java b/src/main/java/com/github/manevolent/ts3j/identity/Identity.java new file mode 100644 index 0000000..7ee5825 --- /dev/null +++ b/src/main/java/com/github/manevolent/ts3j/identity/Identity.java @@ -0,0 +1,159 @@ +package com.github.manevolent.ts3j.identity; + +import com.github.manevolent.ts3j.util.Ts3Logging; +import org.bouncycastle.asn1.ASN1Encodable; +import org.bouncycastle.asn1.DERBitString; +import org.bouncycastle.asn1.DERInteger; +import org.bouncycastle.asn1.DERSequence; +import org.bouncycastle.math.ec.ECPoint; + +import java.io.IOException; +import java.nio.charset.Charset; +import java.security.DigestException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Base64; + +public class Identity { + private static MessageDigest sha1; + + static { + try { + sha1 = MessageDigest.getInstance("SHA-1"); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + } + + private final ECPoint publicKey; + private final String fingerprint; + private final String publicKeyString; + + private long keyOffset = 0L, lastCheckedKeyOffset = 0L; + + public Identity(ECPoint publicKey) { + this.publicKey = publicKey; + + this.publicKeyString = Base64.getEncoder().encodeToString(encodePublicKey()); + this.fingerprint = generateFingerprint(); + } + + public ECPoint getPublicKey() { + return publicKey; + } + + public String getPublicKeyString() { + return publicKeyString; + } + + public String getFingerprint() { + return fingerprint; + } + + public long getLastCheckedKeyOffset() { + return lastCheckedKeyOffset; + } + + public void setLastCheckedKeyOffset(long lastCheckedKeyOffset) { + this.lastCheckedKeyOffset = lastCheckedKeyOffset; + } + + public long getKeyOffset() { + return keyOffset; + } + + public void setKeyOffset(long keyOffset) { + this.keyOffset = keyOffset; + } + + public void improveSecurity(int target) { + byte[] hashBuffer = new byte[getPublicKeyString().length() + 20]; + byte[] pubKeyBytes = getPublicKeyString().getBytes(Charset.forName("ASCII")); + System.arraycopy(pubKeyBytes, 0, hashBuffer, 0, pubKeyBytes.length); + + + setLastCheckedKeyOffset(Math.max(getKeyOffset(), getLastCheckedKeyOffset())); + int best = getSecurityLevel(hashBuffer, pubKeyBytes.length, getKeyOffset()); + while (true) + { + if (best >= target) return; + + int curr = getSecurityLevel(hashBuffer, pubKeyBytes.length, lastCheckedKeyOffset); + + if (curr > best) + { + Ts3Logging.debug("Improved identity security level: from " + best + " to " + curr); + keyOffset = lastCheckedKeyOffset; + best = curr; + } + lastCheckedKeyOffset ++; + } + } + + public int getSecurityLevel() { + byte[] hashBuffer = new byte[getPublicKeyString().length() + 20]; + byte[] pubKeyBytes = getPublicKeyString().getBytes(Charset.forName("ASCII")); + System.arraycopy(pubKeyBytes, 0, hashBuffer, 0, pubKeyBytes.length); + + return getSecurityLevel(hashBuffer, pubKeyBytes.length, getKeyOffset()); + } + + private static int getSecurityLevel(byte[] hashBuffer, int pubKeyLen, long offset) { + byte[] numBuffer = new byte[20]; + int numLen = 0; + do + { + numBuffer[numLen] = (byte)((offset % 10) & 0xFF); + offset /= 10; + numLen ++; + } while (offset > 0); + + for (int i = 0; i < numLen; i ++) + hashBuffer[pubKeyLen + i] = numBuffer[numLen - (i + 1)]; + + byte[] outHash; + synchronized (sha1) { + sha1.update(hashBuffer, 0, pubKeyLen + numLen); + outHash = sha1.digest(); + } + + return getLeadingZeroBits(outHash); + } + + // GOOD + private static int getLeadingZeroBits(byte[] data) { + int curr = 0; + int i ; + for (i = 0; i < data.length; i ++) { + if (data[i] == 0) curr += 8; + else break; + } + if (i < data.length) + for (int bit = 0; bit < 8; bit ++) { + if ((data[i] & (1 << bit)) == 0) curr++; + else break; + } + return curr; + } + + private byte[] encodePublicKey() { + try { + byte[] dataArray = new DERSequence(new ASN1Encodable[]{ + new DERBitString(new byte[]{0b0000_0000}, 7), + new DERInteger(32), + new DERInteger(publicKey.getAffineXCoord().toBigInteger()), + new DERInteger(publicKey.getAffineYCoord().toBigInteger()) + }).getEncoded(); + + return dataArray; + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + private String generateFingerprint() { + synchronized (sha1) { + return Base64.getEncoder().encodeToString(sha1.digest(encodePublicKey())); + } + } +} diff --git a/src/main/java/com/github/manevolent/ts3j/identity/LocalIdentity.java b/src/main/java/com/github/manevolent/ts3j/identity/LocalIdentity.java new file mode 100644 index 0000000..fd9fabf --- /dev/null +++ b/src/main/java/com/github/manevolent/ts3j/identity/LocalIdentity.java @@ -0,0 +1,59 @@ +package com.github.manevolent.ts3j.identity; + +import org.bouncycastle.crypto.AsymmetricCipherKeyPair; +import org.bouncycastle.crypto.generators.ECKeyPairGenerator; +import org.bouncycastle.crypto.params.ECDomainParameters; +import org.bouncycastle.crypto.params.ECKeyGenerationParameters; +import org.bouncycastle.crypto.params.ECPrivateKeyParameters; +import org.bouncycastle.crypto.params.ECPublicKeyParameters; +import org.bouncycastle.jce.ECNamedCurveTable; +import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec; +import org.bouncycastle.math.ec.ECPoint; + +import java.math.BigInteger; +import java.security.*; + +public class LocalIdentity extends Identity { + static { + Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider()); + } + + private final BigInteger privateKey; + + public LocalIdentity(ECPoint publicKey, BigInteger privateKey) { + super(publicKey); + + this.privateKey = privateKey; + } + + public static LocalIdentity load(ECPoint publicKey, + BigInteger privateKey, + long keyOffset, + long lastCheckedKeyOffset) { + LocalIdentity localIdentity = new LocalIdentity(publicKey, privateKey); + + localIdentity.setKeyOffset(keyOffset); + localIdentity.setLastCheckedKeyOffset(lastCheckedKeyOffset); + + return localIdentity; + } + + public static LocalIdentity generateNew(int securityLevel) throws GeneralSecurityException { + ECNamedCurveParameterSpec ecp = ECNamedCurveTable.getParameterSpec("prime256v1"); + ECDomainParameters domainParams = + new ECDomainParameters(ecp.getCurve(), ecp.getG(), ecp.getN(), ecp.getH(), ecp.getSeed()); + ECKeyGenerationParameters keyGenParams = new ECKeyGenerationParameters(domainParams, new SecureRandom()); + + ECKeyPairGenerator generator = new ECKeyPairGenerator(); + generator.init(keyGenParams); + + AsymmetricCipherKeyPair keyPair = generator.generateKeyPair(); + ECPrivateKeyParameters privateKey = (ECPrivateKeyParameters) keyPair.getPrivate(); + ECPublicKeyParameters publicKey = (ECPublicKeyParameters) keyPair.getPublic(); + + LocalIdentity localIdentity = load(publicKey.getQ().normalize(), privateKey.getD(), 0, 0); + localIdentity.improveSecurity(securityLevel); + + return localIdentity; + } +} diff --git a/src/main/java/com/github/manevolent/ts3j/protocol/Endpoint.java b/src/main/java/com/github/manevolent/ts3j/protocol/Endpoint.java new file mode 100644 index 0000000..ed71802 --- /dev/null +++ b/src/main/java/com/github/manevolent/ts3j/protocol/Endpoint.java @@ -0,0 +1,13 @@ +package com.github.manevolent.ts3j.protocol; + +import com.github.manevolent.ts3j.protocol.packet.Packet; + +import java.io.IOException; + +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 new file mode 100644 index 0000000..4d9a299 --- /dev/null +++ b/src/main/java/com/github/manevolent/ts3j/protocol/LocalEndpoint.java @@ -0,0 +1,109 @@ +package com.github.manevolent.ts3j.protocol; + +import com.github.manevolent.ts3j.identity.LocalIdentity; +import com.github.manevolent.ts3j.protocol.packet.handler.PacketHandler; +import com.github.manevolent.ts3j.protocol.socket.LocalTeamspeakSocket; +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 PacketHandler handler = null; + private LocalIdentity localIdentity = null; + + 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; + } + + 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 void setHandler(PacketHandler handler) throws IOException { + if (this.handler != handler) { + if (this.handler != null) this.handler.onUnassigning(); + this.handler = handler; + if (handler != null) handler.onAssigned(); + } + } + + protected void handlePacket(NetworkPacket packet) throws IOException { + if (this.handler != null) this.handler.handlePacket(packet); + } + + 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 { + handlePacket(receive()); + } 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/RemoteEndpoint.java b/src/main/java/com/github/manevolent/ts3j/protocol/RemoteEndpoint.java new file mode 100644 index 0000000..3899f4c --- /dev/null +++ b/src/main/java/com/github/manevolent/ts3j/protocol/RemoteEndpoint.java @@ -0,0 +1,7 @@ +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/TeamspeakSocket.java b/src/main/java/com/github/manevolent/ts3j/protocol/TeamspeakSocket.java new file mode 100644 index 0000000..2796847 --- /dev/null +++ b/src/main/java/com/github/manevolent/ts3j/protocol/TeamspeakSocket.java @@ -0,0 +1,12 @@ +package com.github.manevolent.ts3j.protocol; + +import com.github.manevolent.ts3j.protocol.header.PacketHeader; +import com.github.manevolent.ts3j.protocol.packet.Packet; + +import java.io.IOException; + +public interface TeamspeakSocket { + SocketRole getSocketRole(); + + void send(PacketHeader header, Packet packet) throws IOException; +} 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 new file mode 100644 index 0000000..6979b0d --- /dev/null +++ b/src/main/java/com/github/manevolent/ts3j/protocol/client/ClientConnectionState.java @@ -0,0 +1,31 @@ + +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; + +public enum ClientConnectionState { + CONNECTING(LocalClientHandlerConnecting.class), + CONNECTED(LocalClientHandlerConnected.class), + DISCONNECTED(LocalClientHandlerDisconnected.class); + + private final Class clazz; + + ClientConnectionState(Class clazz) { + this.clazz = clazz; + } + + public Class getHandlerClass() { + return clazz; + } + + public LocalClientHandler createHandler(LocalTeamspeakClient client) { + try { + return clazz.getConstructor(LocalTeamspeakClient.class).newInstance(client); + } catch (ReflectiveOperationException ex) { + throw new RuntimeException(ex); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/github/manevolent/ts3j/Teamspeak3Client.java b/src/main/java/com/github/manevolent/ts3j/protocol/client/LocalTeamspeakClient.java similarity index 63% rename from src/main/java/com/github/manevolent/ts3j/Teamspeak3Client.java rename to src/main/java/com/github/manevolent/ts3j/protocol/client/LocalTeamspeakClient.java index 4126d13..3b0a502 100644 --- a/src/main/java/com/github/manevolent/ts3j/Teamspeak3Client.java +++ b/src/main/java/com/github/manevolent/ts3j/protocol/client/LocalTeamspeakClient.java @@ -1,50 +1,71 @@ -package com.github.manevolent.ts3j; +package com.github.manevolent.ts3j.protocol.client; import com.github.manevolent.ts3j.command.Command; import com.github.manevolent.ts3j.command.part.CommandSingleParameter; import com.github.manevolent.ts3j.command.response.CommandResponse; -import com.github.manevolent.ts3j.handler.TeamspeakClientHandler; -import com.github.manevolent.ts3j.protocol.NetworkPacket; +import com.github.manevolent.ts3j.identity.LocalIdentity; +import com.github.manevolent.ts3j.protocol.LocalEndpoint; import com.github.manevolent.ts3j.protocol.SocketRole; -import com.github.manevolent.ts3j.protocol.Teamspeak3Socket; import com.github.manevolent.ts3j.protocol.header.ClientPacketHeader; +import com.github.manevolent.ts3j.protocol.packet.handler.client.LocalClientHandler; +import com.github.manevolent.ts3j.protocol.socket.LocalTeamspeakSocket; import com.github.manevolent.ts3j.protocol.packet.Packet; import com.github.manevolent.ts3j.util.Ts3Logging; import java.io.IOException; -import java.net.DatagramSocket; import java.net.InetSocketAddress; import java.net.SocketException; import java.util.LinkedHashMap; import java.util.Map; import java.util.concurrent.TimeoutException; -public class Teamspeak3Client { - private final PacketReceiver packetReceiver = new PacketReceiver(); - - private final Teamspeak3Socket socket; +public class LocalTeamspeakClient extends LocalEndpoint implements TeamspeakClient { private final Object connectionStateLock = new Object(); private final Map clientOptions = new LinkedHashMap<>(); - private TeamspeakClientHandler handler; - - private InetSocketAddress remote = null; + private LocalClientHandler handler; private ClientConnectionState clientConnectionState = null; + private int returnCodeIndex = 0; - public Teamspeak3Client(DatagramSocket datagramSocket) throws SocketException { - this.socket = new Teamspeak3Socket(SocketRole.CLIENT, datagramSocket); + public LocalTeamspeakClient(LocalTeamspeakSocket socket) { + super(socket); + + if (socket.getSocketRole() != SocketRole.CLIENT) + throw new IllegalArgumentException("invalid socket role: " + socket.getSocketRole().name()); setClientConnectionState(ClientConnectionState.DISCONNECTED); } - public Teamspeak3Client() throws SocketException { - this(new DatagramSocket()); + 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 InetSocketAddress getRemote() { - return remote; + public LocalTeamspeakClient() throws SocketException { + this(new LocalTeamspeakSocket(SocketRole.CLIENT)); + } + + @Override + public SocketRole getRole() { + return SocketRole.CLIENT; } public void send(Packet packet) throws IOException { @@ -53,7 +74,7 @@ public class Teamspeak3Client { packet.setHeaderValues(header); - socket.send(header, packet); + getSocket().send(header, packet); } protected void setClientConnectionState(ClientConnectionState state) { @@ -65,13 +86,8 @@ public class Teamspeak3Client { this.clientConnectionState = state; - if (wasDisconnected) { - Ts3Logging.debug("Starting network receiver thread..."); - - Thread receiverThread = new Thread(new PacketReceiver()); - receiverThread.setDaemon(true); - receiverThread.start(); - } + if (wasDisconnected) + setRunning(true); if (handler != null) handler.handleConnectionStateChanging(state); @@ -82,7 +98,7 @@ public class Teamspeak3Client { handler = state.createHandler(this); try { - handler.onAssigned(); + setHandler(handler); } catch (IOException e) { throw new IllegalStateException(e); } @@ -102,6 +118,11 @@ public class Teamspeak3Client { return clientConnectionState == ClientConnectionState.CONNECTED; } + @Override + public Map getOptions() { + return clientOptions; + } + public ClientConnectionState getClientConnectionState() { return clientConnectionState; } @@ -120,46 +141,6 @@ public class Teamspeak3Client { } } - public T getOption(String key, Class clazz) { - Object value = clientOptions.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; - } - - public Object setOption(String key, Object value) { - return clientOptions.put(key, value); - } - - 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 void connect(InetSocketAddress remote, String password, long timeout) throws IOException { try { if (clientConnectionState != ClientConnectionState.DISCONNECTED) @@ -168,7 +149,7 @@ public class Teamspeak3Client { setOption("client.hostname", remote.getHostString()); setOption("client.password", password); - socket.connect(remote); + getSocket().connect(remote); setClientConnectionState(ClientConnectionState.CONNECTING); joinConnectionState(ClientConnectionState.CONNECTED, timeout); @@ -178,25 +159,4 @@ public class Teamspeak3Client { throw new IOException(e); } } - - private void handlePacket(NetworkPacket packet) throws Exception { - if (handler != null) handler.handlePacket(packet); - } - - private class PacketReceiver implements Runnable { - @Override - public void run() { - Ts3Logging.debug("Entering packet receiver"); - - while (clientConnectionState != ClientConnectionState.DISCONNECTED) { - try { - handlePacket(socket.receive()); - } catch (Exception e) { - Ts3Logging.debug("Problem handling packet", e); - } - } - - Ts3Logging.debug("Leaving packet receiver"); - } - } } 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 new file mode 100644 index 0000000..b90ef8d --- /dev/null +++ b/src/main/java/com/github/manevolent/ts3j/protocol/client/TeamspeakClient.java @@ -0,0 +1,37 @@ +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; + +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/packet/Packet8Init1.java b/src/main/java/com/github/manevolent/ts3j/protocol/packet/Packet8Init1.java index 2a18fe3..480477d 100644 --- a/src/main/java/com/github/manevolent/ts3j/protocol/packet/Packet8Init1.java +++ b/src/main/java/com/github/manevolent/ts3j/protocol/packet/Packet8Init1.java @@ -90,6 +90,9 @@ public class Packet8Init1 extends Packet { case 3: step = new Step3(); break; + case 4: + step = new Step4(); + break; default: throw new IllegalArgumentException("invalid Init1 step: " + stepNumber); } 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 new file mode 100644 index 0000000..871796a --- /dev/null +++ b/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/PacketHandler.java @@ -0,0 +1,30 @@ +package com.github.manevolent.ts3j.protocol.packet.handler; + +import com.github.manevolent.ts3j.protocol.NetworkPacket; +import com.github.manevolent.ts3j.protocol.LocalEndpoint; + +import java.io.IOException; + +public abstract class PacketHandler { + private final LocalEndpoint endpoint; + + protected PacketHandler(LocalEndpoint endpoint) { + this.endpoint = endpoint; + } + + public void onUnassigning() throws IOException { + + } + + public void onAssigned() throws IOException { + + } + + public void handlePacket(NetworkPacket packet) throws IOException { + + } + + public LocalEndpoint getEndpoint() { + return endpoint; + } +} 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/client/LocalClientHandler.java new file mode 100644 index 0000000..c98c40f --- /dev/null +++ b/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/client/LocalClientHandler.java @@ -0,0 +1,26 @@ +package com.github.manevolent.ts3j.protocol.packet.handler.client; + +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; + +public abstract class LocalClientHandler extends PacketHandler { + private final LocalTeamspeakClient client; + + protected LocalClientHandler(LocalTeamspeakClient client) { + super(client); + + this.client = client; + } + + public void handleConnectionStateChanging(ClientConnectionState clientConnectionState) { + + } + + protected LocalTeamspeakClient getClient() { + return client; + } +} 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 new file mode 100644 index 0000000..d33da6b --- /dev/null +++ b/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/client/LocalClientHandlerConnected.java @@ -0,0 +1,9 @@ +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/handler/TeamspeakClientHandlerConnecting.java b/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/client/LocalClientHandlerConnecting.java similarity index 77% rename from src/main/java/com/github/manevolent/ts3j/handler/TeamspeakClientHandlerConnecting.java rename to src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/client/LocalClientHandlerConnecting.java index 6c3e3c4..153c825 100644 --- a/src/main/java/com/github/manevolent/ts3j/handler/TeamspeakClientHandlerConnecting.java +++ b/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/client/LocalClientHandlerConnecting.java @@ -1,20 +1,23 @@ -package com.github.manevolent.ts3j.handler; +package com.github.manevolent.ts3j.protocol.packet.handler.client; -import com.github.manevolent.ts3j.Teamspeak3Client; -import com.github.manevolent.ts3j.command.Command; +import com.github.manevolent.ts3j.command.type.client.ClientInitIV; import com.github.manevolent.ts3j.protocol.NetworkPacket; import com.github.manevolent.ts3j.protocol.ProtocolRole; +import com.github.manevolent.ts3j.protocol.client.LocalTeamspeakClient; import com.github.manevolent.ts3j.protocol.packet.Packet8Init1; import com.github.manevolent.ts3j.util.Ts3Logging; import java.io.IOException; import java.math.BigInteger; +import java.nio.charset.Charset; +import java.util.Base64; import java.util.Random; -public class TeamspeakClientHandlerConnecting extends TeamspeakClientHandler { +public class LocalClientHandlerConnecting extends LocalClientHandler { private byte[] randomBytes; + private byte[] alphaBytes; - public TeamspeakClientHandlerConnecting(Teamspeak3Client client) { + public LocalClientHandlerConnecting(LocalTeamspeakClient client) { super(client); } @@ -90,10 +93,12 @@ public class TeamspeakClientHandlerConnecting extends TeamspeakClientHandler { byte[] solution = x.modPow(BigInteger.valueOf(2L).pow(serverReplyStep3.getLevel()), n).toByteArray(); + Ts3Logging.debug(Ts3Logging.getHex(solution)); + System.arraycopy( - solution, Math.max(0, solution.length - 64), - y, 64 - solution.length, - solution.length + solution, Math.abs(solution.length - 64), + y, solution.length < 64 ? 64 - solution.length : 0, + solution.length < 64 ? solution.length : 64 ); // Build response @@ -107,8 +112,17 @@ public class TeamspeakClientHandlerConnecting extends TeamspeakClientHandler { step4.setY(y); step4.setServerStuff(serverReplyStep3.getServerStuff()); + alphaBytes = new byte[10]; + new Random().nextBytes(alphaBytes); - step4.setClientIVcommand(new byte[4]); // TODO + ClientInitIV command = new ClientInitIV(); + + command.get("alpha").set(Base64.getEncoder().encodeToString(alphaBytes)); + command.get("omega").set(getClient().getLocalIdentity().getPublicKeyString()); + command.get("ot").set(1); // ? + command.get("ip").set(null); + + step4.setClientIVcommand(command.build().getBytes(Charset.forName("UTF8"))); response4.setStep(step4); 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 new file mode 100644 index 0000000..e57aa51 --- /dev/null +++ b/src/main/java/com/github/manevolent/ts3j/protocol/packet/handler/client/LocalClientHandlerDisconnected.java @@ -0,0 +1,9 @@ +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/server/RemoteTeamspeakServer.java b/src/main/java/com/github/manevolent/ts3j/protocol/server/RemoteTeamspeakServer.java new file mode 100644 index 0000000..4ca98b9 --- /dev/null +++ b/src/main/java/com/github/manevolent/ts3j/protocol/server/RemoteTeamspeakServer.java @@ -0,0 +1,36 @@ +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 new file mode 100644 index 0000000..2687855 --- /dev/null +++ b/src/main/java/com/github/manevolent/ts3j/protocol/server/TeamspeakServer.java @@ -0,0 +1,11 @@ +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/Teamspeak3Socket.java b/src/main/java/com/github/manevolent/ts3j/protocol/socket/LocalTeamspeakSocket.java similarity index 82% rename from src/main/java/com/github/manevolent/ts3j/protocol/Teamspeak3Socket.java rename to src/main/java/com/github/manevolent/ts3j/protocol/socket/LocalTeamspeakSocket.java index 194f458..b0ff10d 100644 --- a/src/main/java/com/github/manevolent/ts3j/protocol/Teamspeak3Socket.java +++ b/src/main/java/com/github/manevolent/ts3j/protocol/socket/LocalTeamspeakSocket.java @@ -1,14 +1,14 @@ -package com.github.manevolent.ts3j.protocol; +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.TeamspeakSocket; 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.io.IOException; -import java.net.DatagramPacket; -import java.net.DatagramSocket; -import java.net.SocketAddress; -import java.net.SocketException; +import java.net.*; import java.nio.ByteBuffer; import java.nio.ByteOrder; @@ -17,18 +17,18 @@ import java.nio.ByteOrder; * * Used simply to transcribe packets on the network */ -public final class Teamspeak3Socket { +public class LocalTeamspeakSocket implements TeamspeakSocket { private final SocketRole socketRole; private final DatagramSocket datagramSocket; private final DatagramPacket datagramPacket = new DatagramPacket(new byte[1500], 1500); - public Teamspeak3Socket(SocketRole socketRole, DatagramSocket datagramSocket) { + public LocalTeamspeakSocket(SocketRole socketRole, DatagramSocket datagramSocket) { this.socketRole = socketRole; this.datagramSocket = datagramSocket; } - public Teamspeak3Socket(SocketRole socketRole) throws SocketException { + public LocalTeamspeakSocket(SocketRole socketRole) throws SocketException { this(socketRole, new DatagramSocket()); } @@ -36,6 +36,14 @@ public final class Teamspeak3Socket { 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); } @@ -116,4 +124,5 @@ public final class Teamspeak3Socket { return networkPacket; } + } diff --git a/src/main/java/com/github/manevolent/ts3j/util/Ts3Logging.java b/src/main/java/com/github/manevolent/ts3j/util/Ts3Logging.java index 7e7fe38..76dbba6 100644 --- a/src/main/java/com/github/manevolent/ts3j/util/Ts3Logging.java +++ b/src/main/java/com/github/manevolent/ts3j/util/Ts3Logging.java @@ -18,8 +18,8 @@ public final class Ts3Logging { System.err.println("[INFO] " + message); } - public static void debug(String message) { - System.err.println("[DEBUG] " + message); + public static void debug(Object message) { + System.err.println("[DEBUG] " + message.toString()); } public static void debug(String message, Throwable ex) { diff --git a/src/test/java/com/github/manevolent/ts3j/ServerConnectionTest.java b/src/test/java/com/github/manevolent/ts3j/ServerConnectionTest.java index 9f1cbf4..459c535 100644 --- a/src/test/java/com/github/manevolent/ts3j/ServerConnectionTest.java +++ b/src/test/java/com/github/manevolent/ts3j/ServerConnectionTest.java @@ -1,5 +1,8 @@ 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 junit.framework.TestCase; import java.net.InetSocketAddress; @@ -10,15 +13,23 @@ public class ServerConnectionTest extends TestCase { } public void testParser() throws Exception { - Teamspeak3Client client = new Teamspeak3Client(); + try { + LocalIdentity localIdentity = LocalIdentity.generateNew(8); - client.connect(new InetSocketAddress( - "voice.teamspeak.com", - 9987), - null, - 10000L - ); + LocalTeamspeakClient client = new LocalTeamspeakClient(); - assertEquals(client.getClientConnectionState(), ClientConnectionState.CONNECTED); + client.setLocalIdentity(localIdentity); + + client.connect(new InetSocketAddress( + "voice.teamspeak.com", + 9987), + null, + 10000L + ); + + assertEquals(client.getClientConnectionState(), ClientConnectionState.CONNECTED); + } catch (Throwable ex) { + ex.printStackTrace(); + } } }