diff --git a/smack-core/src/test/java/org/jivesoftware/smack/util/NetworkUtil.java b/smack-core/src/test/java/org/jivesoftware/smack/util/NetworkUtil.java index d111963f9..ac58e8790 100644 --- a/smack-core/src/test/java/org/jivesoftware/smack/util/NetworkUtil.java +++ b/smack-core/src/test/java/org/jivesoftware/smack/util/NetworkUtil.java @@ -1,6 +1,6 @@ /** * - * Copyright 2016 Florian Schmaus + * Copyright 2016-2019 Florian Schmaus * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,7 +27,7 @@ public class NetworkUtil { private static final Logger LOGGER = Logger.getLogger(NetworkUtil.class.getName()); - public static ServerSocket getSocketOnLoopback() { + public static ServerSocket getSocketOnLoopback() throws IOException { final InetAddress loopbackAddress = InetAddress.getLoopbackAddress(); final int portMin = 1024; final int portMax = (1 << 16) - 1; @@ -40,13 +40,12 @@ public class NetworkUtil { break; } catch (BindException e) { LOGGER.log(Level.FINEST, "Could not bind port " + port + ", trying next", e); - } catch (IOException e) { - throw new IllegalStateException(e); } } if (serverSocket == null) { - throw new IllegalStateException(); + throw new IOException("Could not bind any port between " + portMin + " and " + portMax + + " on loopback address" + loopbackAddress); } return serverSocket; diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamManager.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamManager.java index 7daf62c85..480c4251f 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamManager.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamManager.java @@ -56,6 +56,7 @@ import org.jivesoftware.smackx.disco.packet.DiscoverItems; import org.jivesoftware.smackx.disco.packet.DiscoverItems.Item; import org.jivesoftware.smackx.filetransfer.FileTransferManager; +import org.jxmpp.jid.EntityFullJid; import org.jxmpp.jid.Jid; /** @@ -149,6 +150,8 @@ public final class Socks5BytestreamManager extends Manager implements Bytestream /* flag to enable/disable prioritization of last working proxy */ private boolean proxyPrioritizationEnabled = true; + private boolean annouceLocalStreamHost = true; + /* * list containing session IDs of SOCKS5 Bytestream initialization packets that should be * ignored by the InitiationListener @@ -375,6 +378,30 @@ public final class Socks5BytestreamManager extends Manager implements Bytestream this.proxyPrioritizationEnabled = proxyPrioritizationEnabled; } + /** + * Returns if the bytestream manager will announce the local stream host(s), i.e. the local SOCKS5 proxy. + *

+ * Local stream hosts will be announced if this option is enabled and at least one is running. + *

+ * + * @return true if + * @since 4.4.0 + */ + public boolean isAnnouncingLocalStreamHostEnabled() { + return annouceLocalStreamHost; + } + + /** + * Set whether or not the bytestream manager will annouce the local stream host(s), i.e. the local SOCKS5 proxy. + * + * @param announceLocalStreamHost + * @see #isAnnouncingLocalStreamHostEnabled() + * @since 4.4.0 + */ + public void setAnnounceLocalStreamHost(boolean announceLocalStreamHost) { + this.annouceLocalStreamHost = announceLocalStreamHost; + } + /** * Establishes a SOCKS5 Bytestream with the given user and returns the Socket to send/receive * data to/from the user. @@ -592,10 +619,12 @@ public final class Socks5BytestreamManager extends Manager implements Bytestream XMPPConnection connection = connection(); List streamHosts = new ArrayList<>(); - // add local proxy on first position if exists - List localProxies = getLocalStreamHost(); - if (localProxies != null) { - streamHosts.addAll(localProxies); + if (annouceLocalStreamHost) { + // add local proxy on first position if exists + List localProxies = getLocalStreamHost(); + if (localProxies != null) { + streamHosts.addAll(localProxies); + } } // query SOCKS5 proxies for network settings @@ -636,34 +665,33 @@ public final class Socks5BytestreamManager extends Manager implements Bytestream * is not running */ public List getLocalStreamHost() { - XMPPConnection connection = connection(); - // get local proxy singleton - Socks5Proxy socks5Server = Socks5Proxy.getSocks5Proxy(); - - if (!socks5Server.isRunning()) { - // server is not running - return null; - } - List addresses = socks5Server.getLocalAddresses(); - if (addresses.isEmpty()) { - // local address could not be determined - return null; - } - final int port = socks5Server.getPort(); - List streamHosts = new ArrayList<>(); - outerloop: for (String address : addresses) { - // Prevent loopback addresses from appearing as streamhost - final String[] loopbackAddresses = { "127.0.0.1", "0:0:0:0:0:0:0:1", "::1" }; - for (String loopbackAddress : loopbackAddresses) { - // Use 'startsWith' here since IPv6 addresses may have scope ID, - // ie. the part after the '%' sign. - if (address.startsWith(loopbackAddress)) { - continue outerloop; - } + + XMPPConnection connection = connection(); + EntityFullJid myJid = connection.getUser(); + + for (Socks5Proxy socks5Server : Socks5Proxy.getRunningProxies()) { + List addresses = socks5Server.getLocalAddresses(); + if (addresses.isEmpty()) { + // local address could not be determined + return null; + } + final int port = socks5Server.getPort(); + + outerloop: for (String address : addresses) { + // Prevent loopback addresses from appearing as streamhost + final String[] loopbackAddresses = { "127.0.0.1", "0:0:0:0:0:0:0:1", "::1" }; + for (String loopbackAddress : loopbackAddresses) { + // Use 'startsWith' here since IPv6 addresses may have scope ID, + // ie. the part after the '%' sign. + if (address.startsWith(loopbackAddress)) { + continue outerloop; + } + } + streamHosts.add(new StreamHost(myJid, address, port)); } - streamHosts.add(new StreamHost(connection.getUser(), address, port)); } + return streamHosts; } diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamRequest.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamRequest.java index 92e17a381..1dda14dd5 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamRequest.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5BytestreamRequest.java @@ -57,12 +57,14 @@ public class Socks5BytestreamRequest implements BytestreamRequest { private static final Cache ADDRESS_BLACKLIST = new ExpirationCache( BLACKLIST_MAX_SIZE, BLACKLIST_LIFETIME); + private static int DEFAULT_CONNECTION_FAILURE_THRESHOLD = 2; + /* * The number of connection failures it takes for a particular SOCKS5 proxy to be blacklisted. * When a proxy is blacklisted no more connection attempts will be made to it for a period of 2 * hours. */ - private static int CONNECTION_FAILURE_THRESHOLD = 2; + private int connectionFailureThreshold = DEFAULT_CONNECTION_FAILURE_THRESHOLD; /* the bytestream initialization request */ private Bytestream bytestreamRequest; @@ -76,6 +78,28 @@ public class Socks5BytestreamRequest implements BytestreamRequest { /* minimum timeout to connect to one SOCKS5 proxy */ private int minimumConnectTimeout = 2000; + /** + * Returns the default connection failure threshold. + * + * @return the default connection failure threshold. + * @see #setConnectFailureThreshold(int) + * @since 4.4.0 + */ + public static int getDefaultConnectFailureThreshold() { + return DEFAULT_CONNECTION_FAILURE_THRESHOLD; + } + + /** + * Sets the default connection failure threshold. + * + * @param defaultConnectFailureThreshold the default connection failure threshold. + * @see #setConnectFailureThreshold(int) + * @since 4.4.0 + */ + public static void setDefaultConnectFailureThreshold(int defaultConnectFailureThreshold) { + DEFAULT_CONNECTION_FAILURE_THRESHOLD = defaultConnectFailureThreshold; + } + /** * Returns the number of connection failures it takes for a particular SOCKS5 proxy to be * blacklisted. When a proxy is blacklisted no more connection attempts will be made to it for a @@ -84,8 +108,8 @@ public class Socks5BytestreamRequest implements BytestreamRequest { * @return the number of connection failures it takes for a particular SOCKS5 proxy to be * blacklisted */ - public static int getConnectFailureThreshold() { - return CONNECTION_FAILURE_THRESHOLD; + public int getConnectFailureThreshold() { + return connectionFailureThreshold; } /** @@ -98,8 +122,8 @@ public class Socks5BytestreamRequest implements BytestreamRequest { * @param connectFailureThreshold the number of connection failures it takes for a particular * SOCKS5 proxy to be blacklisted */ - public static void setConnectFailureThreshold(int connectFailureThreshold) { - CONNECTION_FAILURE_THRESHOLD = connectFailureThreshold; + public void setConnectFailureThreshold(int connectFailureThreshold) { + connectionFailureThreshold = connectFailureThreshold; } /** @@ -234,7 +258,7 @@ public class Socks5BytestreamRequest implements BytestreamRequest { // check to see if this address has been blacklisted int failures = getConnectionFailures(address); - if (CONNECTION_FAILURE_THRESHOLD > 0 && failures >= CONNECTION_FAILURE_THRESHOLD) { + if (connectionFailureThreshold > 0 && failures >= connectionFailureThreshold) { continue; } diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5Client.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5Client.java index 83965e7ca..c46f4af65 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5Client.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5Client.java @@ -36,6 +36,7 @@ import org.jivesoftware.smack.SmackException.NoResponseException; import org.jivesoftware.smack.SmackException.NotConnectedException; import org.jivesoftware.smack.SmackException.SmackMessageException; import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smack.util.Async; import org.jivesoftware.smack.util.CloseableUtil; import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream.StreamHost; @@ -111,27 +112,14 @@ public class Socks5Client { } }); - Thread executor = new Thread(futureTask); - executor.start(); + Async.go(futureTask, "SOCKS5 client connecting to " + streamHost); // get connection to initiator with timeout try { return futureTask.get(timeout, TimeUnit.MILLISECONDS); } catch (ExecutionException e) { - Throwable cause = e.getCause(); - if (cause != null) { - // case exceptions to comply with method signature - if (cause instanceof IOException) { - throw (IOException) cause; - } - if (cause instanceof SmackMessageException) { - throw (SmackMessageException) cause; - } - } - - // throw generic Smack exception if unexpected exception was thrown - throw new IllegalStateException("Error while connecting to SOCKS5 proxy", e); + throw new IOException("ExecutionException while SOCKS5 client attempting to connect to " + streamHost, e); } } diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ClientForInitiator.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ClientForInitiator.java index 714e849af..adca9931f 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ClientForInitiator.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ClientForInitiator.java @@ -79,8 +79,7 @@ public class Socks5ClientForInitiator extends Socks5Client { // check if stream host is the local SOCKS5 proxy if (this.streamHost.getJID().equals(this.connection.get().getUser())) { - Socks5Proxy socks5Server = Socks5Proxy.getSocks5Proxy(); - socket = socks5Server.getSocket(this.digest); + socket = Socks5Proxy.getSocketForDigest(this.digest); if (socket == null) { throw new SmackException.SmackMessageException("target is not connected to SOCKS5 proxy"); } diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5Proxy.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5Proxy.java index effab8762..ed5d59597 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5Proxy.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5Proxy.java @@ -35,6 +35,7 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CopyOnWriteArrayList; import java.util.logging.Level; import java.util.logging.Logger; @@ -73,6 +74,8 @@ import org.jivesoftware.smack.util.CloseableUtil; public final class Socks5Proxy { private static final Logger LOGGER = Logger.getLogger(Socks5Proxy.class.getName()); + private static final List RUNNING_PROXIES = new CopyOnWriteArrayList<>(); + /* SOCKS5 proxy singleton */ private static Socks5Proxy socks5Server; @@ -104,7 +107,7 @@ public final class Socks5Proxy { /** * Private constructor. */ - private Socks5Proxy() { + Socks5Proxy() { this.serverProcess = new Socks5ServerProcess(); Enumeration networkInterfaces; @@ -188,9 +191,9 @@ public final class Socks5Proxy { /** * Starts the local SOCKS5 proxy server. If it is already running, this method does nothing. */ - public synchronized void start() { + public synchronized ServerSocket start() { if (isRunning()) { - return; + return this.serverSocket; } try { if (getLocalSocks5ProxyPort() < 0) { @@ -213,6 +216,8 @@ public final class Socks5Proxy { this.serverThread = new Thread(this.serverProcess); this.serverThread.setName("Smack Local SOCKS5 Proxy [" + this.serverSocket + ']'); this.serverThread.setDaemon(true); + + RUNNING_PROXIES.add(this); this.serverThread.start(); } } @@ -220,6 +225,8 @@ public final class Socks5Proxy { // couldn't setup server LOGGER.log(Level.SEVERE, "couldn't setup local SOCKS5 proxy on port " + getLocalSocks5ProxyPort(), e); } + + return this.serverSocket; } /** @@ -230,6 +237,8 @@ public final class Socks5Proxy { return; } + RUNNING_PROXIES.remove(this); + CloseableUtil.maybeClose(this.serverSocket, LOGGER); if (this.serverThread != null && this.serverThread.isAlive()) { @@ -483,4 +492,17 @@ public final class Socks5Proxy { } + public static Socket getSocketForDigest(String digest) { + for (Socks5Proxy socks5Proxy : RUNNING_PROXIES) { + Socket socket = socks5Proxy.getSocket(digest); + if (socket != null) { + return socket; + } + } + return null; + } + + static List getRunningProxies() { + return RUNNING_PROXIES; + } } diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ByteStreamManagerTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ByteStreamManagerTest.java index aec522ead..7a6784bb8 100644 --- a/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ByteStreamManagerTest.java +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ByteStreamManagerTest.java @@ -28,6 +28,8 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ConnectException; +import java.net.ServerSocket; +import java.util.concurrent.TimeoutException; import org.jivesoftware.smack.SmackException; import org.jivesoftware.smack.SmackException.FeatureNotSupportedException; @@ -37,6 +39,7 @@ import org.jivesoftware.smack.XMPPException.XMPPErrorException; import org.jivesoftware.smack.packet.ErrorIQ; import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.StanzaError; +import org.jivesoftware.smack.util.NetworkUtil; import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream; import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream.StreamHost; @@ -49,8 +52,6 @@ import org.jivesoftware.smackx.disco.packet.DiscoverItems.Item; import org.jivesoftware.util.ConnectionUtils; import org.jivesoftware.util.Protocol; import org.jivesoftware.util.Verification; -import org.junit.After; -import org.junit.Before; import org.junit.Test; import org.jxmpp.jid.DomainBareJid; import org.jxmpp.jid.EntityFullJid; @@ -71,30 +72,6 @@ public class Socks5ByteStreamManagerTest { private static final DomainBareJid xmppServer = initiatorJID.asDomainBareJid(); private static final DomainBareJid proxyJID = JidTestUtil.MUC_EXAMPLE_ORG; private static final String proxyAddress = "127.0.0.1"; - private static final String sessionID = "session_id"; - - // protocol verifier - private Protocol protocol; - - // mocked XMPP connection - private XMPPConnection connection; - - /** - * Initialize fields used in the tests. - * @throws XMPPException - * @throws SmackException - * @throws InterruptedException - */ - @Before - public void setup() throws XMPPException, SmackException, InterruptedException { - - // build protocol verifier - protocol = new Protocol(); - - // create mocked XMPP connection - connection = ConnectionUtils.createMockedConnection(protocol, initiatorJID); - - } /** * Test that {@link Socks5BytestreamManager#getBytestreamManager(XMPPConnection)} returns one @@ -102,7 +79,6 @@ public class Socks5ByteStreamManagerTest { */ @Test public void shouldHaveOneManagerForEveryConnection() { - // mock two connections XMPPConnection connection1 = mock(XMPPConnection.class); XMPPConnection connection2 = mock(XMPPConnection.class); @@ -124,15 +100,21 @@ public class Socks5ByteStreamManagerTest { // assertions assertEquals(conn1ByteStreamManager1, conn1ByteStreamManager2); assertNotSame(conn1ByteStreamManager1, conn2ByteStreamManager1); - } /** * The SOCKS5 Bytestream feature should be removed form the service discovery manager if Socks5 * bytestream feature is disabled. + * + * @throws InterruptedException + * @throws SmackException + * @throws XMPPErrorException */ @Test - public void shouldDisableService() { + public void shouldDisableService() throws XMPPErrorException, SmackException, InterruptedException { + final Protocol protocol = new Protocol(); + final XMPPConnection connection = ConnectionUtils.createMockedConnection(protocol, initiatorJID); + Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection); ServiceDiscoveryManager discoveryManager = ServiceDiscoveryManager.getInstanceFor(connection); @@ -147,9 +129,15 @@ public class Socks5ByteStreamManagerTest { * Invoking {@link Socks5BytestreamManager#establishSession(org.jxmpp.jid.Jid)} should throw an exception * if the given target does not support SOCKS5 Bytestream. * @throws XMPPException + * @throws InterruptedException + * @throws SmackException + * @throws IOException */ @Test - public void shouldFailIfTargetDoesNotSupportSocks5() throws XMPPException { + public void shouldFailIfTargetDoesNotSupportSocks5() + throws XMPPException, SmackException, InterruptedException, IOException { + final Protocol protocol = new Protocol(); + final XMPPConnection connection = ConnectionUtils.createMockedConnection(protocol, initiatorJID); Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection); try { @@ -165,24 +153,27 @@ public class Socks5ByteStreamManagerTest { catch (FeatureNotSupportedException e) { assertTrue(e.getFeature().equals("SOCKS5 Bytestream")); assertTrue(e.getJid().equals(targetJID)); - } catch (Exception e) { - fail(e.getMessage()); } - } /** * Invoking {@link Socks5BytestreamManager#establishSession(org.jxmpp.jid.Jid, String)} should fail if XMPP * server doesn't return any proxies. + * @throws InterruptedException + * @throws SmackException + * @throws XMPPException + * @throws IOException */ @Test - public void shouldFailIfNoSocks5ProxyFound1() { - - // disable clients local SOCKS5 proxy - Socks5Proxy.setLocalSocks5ProxyEnabled(false); + public void shouldFailIfNoSocks5ProxyFound1() + throws SmackException, InterruptedException, IOException, XMPPException { + final Protocol protocol = new Protocol(); + final XMPPConnection connection = ConnectionUtils.createMockedConnection(protocol, initiatorJID); + final String sessionID = "session_id_shouldFailIfNoSocks5ProxyFound1"; // get Socks5ByteStreamManager for connection Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection); + byteStreamManager.setAnnounceLocalStreamHost(false); /** * create responses in the order they should be queried specified by the XEP-0065 @@ -206,7 +197,6 @@ public class Socks5ByteStreamManagerTest { Verification.requestTypeGET); try { - // start SOCKS5 Bytestream byteStreamManager.establishSession(targetJID, sessionID); @@ -216,24 +206,27 @@ public class Socks5ByteStreamManagerTest { protocol.verifyAll(); assertTrue(e.getMessage().contains("no SOCKS5 proxies available")); } - catch (Exception e) { - fail(e.getMessage()); - } - } /** * Invoking {@link Socks5BytestreamManager#establishSession(org.jxmpp.jid.Jid, String)} should fail if no * proxy is a SOCKS5 proxy. + * + * @throws InterruptedException + * @throws SmackException + * @throws XMPPException + * @throws IOException */ @Test - public void shouldFailIfNoSocks5ProxyFound2() { - - // disable clients local SOCKS5 proxy - Socks5Proxy.setLocalSocks5ProxyEnabled(false); + public void shouldFailIfNoSocks5ProxyFound2() + throws SmackException, InterruptedException, IOException, XMPPException { + final Protocol protocol = new Protocol(); + final XMPPConnection connection = ConnectionUtils.createMockedConnection(protocol, initiatorJID); + final String sessionID = "session_id_shouldFailIfNoSocks5ProxyFound2"; // get Socks5ByteStreamManager for connection Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection); + byteStreamManager.setAnnounceLocalStreamHost(false); /** * create responses in the order they should be queried specified by the XEP-0065 @@ -269,7 +262,6 @@ public class Socks5ByteStreamManagerTest { Verification.requestTypeGET); try { - // start SOCKS5 Bytestream byteStreamManager.establishSession(targetJID, sessionID); @@ -279,25 +271,26 @@ public class Socks5ByteStreamManagerTest { protocol.verifyAll(); assertTrue(e.getMessage().contains("no SOCKS5 proxies available")); } - catch (Exception e) { - fail(e.getMessage()); - } - } /** * Invoking {@link Socks5BytestreamManager#establishSession(org.jxmpp.jid.Jid, String)} should fail if no * SOCKS5 proxy can be found. If it turns out that a proxy is not a SOCKS5 proxy it should not * be queried again. + * @throws InterruptedException + * @throws SmackException + * @throws XMPPException + * @throws IOException */ @Test - public void shouldBlacklistNonSocks5Proxies() { - - // disable clients local SOCKS5 proxy - Socks5Proxy.setLocalSocks5ProxyEnabled(false); + public void shouldBlacklistNonSocks5Proxies() throws SmackException, InterruptedException, IOException, XMPPException { + final Protocol protocol = new Protocol(); + final XMPPConnection connection = ConnectionUtils.createMockedConnection(protocol, initiatorJID); + final String sessionID = "session_id_shouldBlacklistNonSocks5Proxies"; // get Socks5ByteStreamManager for connection Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection); + byteStreamManager.setAnnounceLocalStreamHost(false); /** * create responses in the order they should be queried specified by the XEP-0065 @@ -333,7 +326,6 @@ public class Socks5ByteStreamManagerTest { Verification.requestTypeGET); try { - // start SOCKS5 Bytestream byteStreamManager.establishSession(targetJID, sessionID); @@ -343,9 +335,6 @@ public class Socks5ByteStreamManagerTest { protocol.verifyAll(); assertTrue(e.getMessage().contains("no SOCKS5 proxies available")); } - catch (Exception e) { - fail(e.getMessage()); - } /* retry to establish SOCKS5 Bytestream */ @@ -356,7 +345,6 @@ public class Socks5ByteStreamManagerTest { Verification.requestTypeGET); try { - // start SOCKS5 Bytestream byteStreamManager.establishSession(targetJID, sessionID); @@ -370,25 +358,26 @@ public class Socks5ByteStreamManagerTest { protocol.verifyAll(); assertTrue(e.getMessage().contains("no SOCKS5 proxies available")); } - catch (Exception e) { - fail(e.getMessage()); - } - } /** * Invoking {@link Socks5BytestreamManager#establishSession(org.jxmpp.jid.Jid, String)} should fail if the * target does not accept a SOCKS5 Bytestream. See XEP-0065 Section 5.2 A2 + * @throws InterruptedException + * @throws SmackException + * @throws XMPPException + * @throws IOException */ @Test - public void shouldFailIfTargetDoesNotAcceptSocks5Bytestream() { - - // disable clients local SOCKS5 proxy - Socks5Proxy.setLocalSocks5ProxyEnabled(false); + public void shouldFailIfTargetDoesNotAcceptSocks5Bytestream() throws SmackException, InterruptedException, IOException, XMPPException { + final Protocol protocol = new Protocol(); + final XMPPConnection connection = ConnectionUtils.createMockedConnection(protocol, initiatorJID); + final String sessionID = "session_id_shouldFailIfTargetDoesNotAcceptSocks5Bytestream"; // get Socks5ByteStreamManager for connection Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection); + byteStreamManager.setAnnounceLocalStreamHost(false); /** * create responses in the order they should be queried specified by the XEP-0065 @@ -443,7 +432,6 @@ public class Socks5ByteStreamManagerTest { Verification.requestTypeSET); try { - // start SOCKS5 Bytestream byteStreamManager.establishSession(targetJID, sessionID); @@ -453,25 +441,28 @@ public class Socks5ByteStreamManagerTest { protocol.verifyAll(); assertEquals(rejectPacket.getError(), e.getStanzaError()); } - catch (Exception e) { - fail(e.getMessage()); - } - } /** * Invoking {@link Socks5BytestreamManager#establishSession(org.jxmpp.jid.Jid, String)} should fail if the * proxy used by target is invalid. - * @throws XmppStringprepException + * + * @throws InterruptedException + * @throws SmackException + * @throws XMPPException + * @throws IOException */ @Test - public void shouldFailIfTargetUsesInvalidSocks5Proxy() throws XmppStringprepException { - - // disable clients local SOCKS5 proxy - Socks5Proxy.setLocalSocks5ProxyEnabled(false); + public void shouldFailIfTargetUsesInvalidSocks5Proxy() + throws SmackException, InterruptedException, IOException, XMPPException { + final Protocol protocol = new Protocol(); + final XMPPConnection connection = ConnectionUtils.createMockedConnection(protocol, initiatorJID); + final String sessionID = "session_id_shouldFailIfTargetUsesInvalidSocks5Proxy"; // get Socks5ByteStreamManager for connection Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection); + // TODO: It appears that it is not required to disable the local stream host for this unit test. + byteStreamManager.setAnnounceLocalStreamHost(false); /** * create responses in the order they should be queried specified by the XEP-0065 @@ -526,7 +517,6 @@ public class Socks5ByteStreamManagerTest { Verification.requestTypeSET); try { - // start SOCKS5 Bytestream byteStreamManager.establishSession(targetJID, sessionID); @@ -536,24 +526,26 @@ public class Socks5ByteStreamManagerTest { protocol.verifyAll(); assertTrue(e.getMessage().contains("Remote user responded with unknown host")); } - catch (Exception e) { - fail(e.getMessage()); - } - } /** * Invoking {@link Socks5BytestreamManager#establishSession(org.jxmpp.jid.Jid, String)} should fail if * initiator can not connect to the SOCKS5 proxy used by target. + * + * @throws InterruptedException + * @throws SmackException + * @throws XMPPException */ @Test - public void shouldFailIfInitiatorCannotConnectToSocks5Proxy() { - - // disable clients local SOCKS5 proxy - Socks5Proxy.setLocalSocks5ProxyEnabled(false); + public void shouldFailIfInitiatorCannotConnectToSocks5Proxy() + throws SmackException, InterruptedException, XMPPException { + final Protocol protocol = new Protocol(); + final XMPPConnection connection = ConnectionUtils.createMockedConnection(protocol, initiatorJID); + final String sessionID = "session_id_shouldFailIfInitiatorCannotConnectToSocks5Proxy"; // get Socks5ByteStreamManager for connection Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection); + byteStreamManager.setAnnounceLocalStreamHost(false); /** * create responses in the order they should be queried specified by the XEP-0065 @@ -618,7 +610,6 @@ public class Socks5ByteStreamManagerTest { }, Verification.correspondingSenderReceiver, Verification.requestTypeSET); try { - // start SOCKS5 Bytestream byteStreamManager.establishSession(targetJID, sessionID); @@ -627,28 +618,30 @@ public class Socks5ByteStreamManagerTest { catch (IOException e) { // initiator can't connect to proxy because it is not running protocol.verifyAll(); - assertEquals(ConnectException.class, e.getClass()); + Throwable actualCause = e.getCause().getCause(); + assertEquals(ConnectException.class, actualCause.getClass()); } - catch (Exception e) { - fail(e.getMessage()); - } - } /** * Invoking {@link Socks5BytestreamManager#establishSession(org.jxmpp.jid.Jid, String)} should successfully * negotiate and return a SOCKS5 Bytestream connection. * - * @throws Exception should not happen + * @throws InterruptedException + * @throws SmackException + * @throws XMPPException + * @throws IOException */ @Test - public void shouldNegotiateSocks5BytestreamAndTransferData() throws Exception { - - // disable clients local SOCKS5 proxy - Socks5Proxy.setLocalSocks5ProxyEnabled(false); + public void shouldNegotiateSocks5BytestreamAndTransferData() + throws SmackException, InterruptedException, IOException, XMPPException { + final Protocol protocol = new Protocol(); + final XMPPConnection connection = ConnectionUtils.createMockedConnection(protocol, initiatorJID); + final String sessionID = "session_id_shouldNegotiateSocks5BytestreamAndTransferData"; // get Socks5ByteStreamManager for connection Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection); + byteStreamManager.setAnnounceLocalStreamHost(false); /** * create responses in the order they should be queried specified by the XEP-0065 @@ -684,9 +677,10 @@ public class Socks5ByteStreamManagerTest { // build a socks5 stream host info containing the address and the port of the // proxy + ServerSocket proxyServerSocket = NetworkUtil.getSocketOnLoopback(); Bytestream streamHostInfo = Socks5PacketUtils.createBytestreamResponse(proxyJID, initiatorJID); - streamHostInfo.addStreamHost(proxyJID, proxyAddress, 7778); + streamHostInfo.addStreamHost(proxyJID, proxyAddress, proxyServerSocket.getLocalPort()); // return stream host info if it is queried protocol.addResponse(streamHostInfo, Verification.correspondingSenderReceiver, @@ -726,143 +720,146 @@ public class Socks5ByteStreamManagerTest { }, Verification.correspondingSenderReceiver, Verification.requestTypeSET); // start a local SOCKS5 proxy - Socks5TestProxy socks5Proxy = Socks5TestProxy.getProxy(7778); - socks5Proxy.start(); + try (Socks5TestProxy socks5Proxy = new Socks5TestProxy(proxyServerSocket)) { - // create digest to get the socket opened by target - String digest = Socks5Utils.createDigest(sessionID, initiatorJID, targetJID); + // create digest to get the socket opened by target + String digest = Socks5Utils.createDigest(sessionID, initiatorJID, targetJID); - // finally call the method that should be tested - OutputStream outputStream = byteStreamManager.establishSession(targetJID, sessionID).getOutputStream(); + // finally call the method that should be tested + OutputStream outputStream = byteStreamManager.establishSession(targetJID, sessionID).getOutputStream(); - // test the established bytestream - InputStream inputStream = socks5Proxy.getSocket(digest).getInputStream(); + // test the established bytestream + InputStream inputStream = socks5Proxy.getSocket(digest).getInputStream(); - byte[] data = new byte[] { 1, 2, 3 }; - outputStream.write(data); + byte[] data = new byte[] { 1, 2, 3 }; + outputStream.write(data); - byte[] result = new byte[3]; - inputStream.read(result); + byte[] result = new byte[3]; + inputStream.read(result); - assertArrayEquals(data, result); + assertArrayEquals(data, result); + } protocol.verifyAll(); - } /** * If multiple network addresses are added to the local SOCKS5 proxy, all of them should be * contained in the SOCKS5 Bytestream request. * - * @throws Exception should not happen + * @throws InterruptedException + * @throws SmackException + * @throws IOException + * @throws XMPPException + * @throws TimeoutException */ @Test - public void shouldUseMultipleAddressesForLocalSocks5Proxy() throws Exception { - - // enable clients local SOCKS5 proxy on port 7778 - Socks5Proxy.setLocalSocks5ProxyEnabled(true); - Socks5Proxy.setLocalSocks5ProxyPort(7778); + public void shouldUseMultipleAddressesForLocalSocks5Proxy() + throws SmackException, InterruptedException, IOException, TimeoutException, XMPPException { + final Protocol protocol = new Protocol(); + final XMPPConnection connection = ConnectionUtils.createMockedConnection(protocol, initiatorJID); + final String sessionID = "session_id_shouldUseMultipleAddressesForLocalSocks5Proxy"; // start a local SOCKS5 proxy - Socks5Proxy socks5Proxy = Socks5Proxy.getSocks5Proxy(); + Socks5Proxy socks5Proxy = new Socks5Proxy(); socks5Proxy.start(); - assertTrue(socks5Proxy.isRunning()); + try { + assertTrue(socks5Proxy.isRunning()); - // get Socks5ByteStreamManager for connection - Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection); + // get Socks5ByteStreamManager for connection + Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection); - /** - * create responses in the order they should be queried specified by the XEP-0065 - * specification - */ + /** + * create responses in the order they should be queried specified by the XEP-0065 + * specification + */ - // build discover info that supports the SOCKS5 feature - DiscoverInfo discoverInfo = Socks5PacketUtils.createDiscoverInfo(targetJID, initiatorJID); - discoverInfo.addFeature(Bytestream.NAMESPACE); + // build discover info that supports the SOCKS5 feature + DiscoverInfo discoverInfo = Socks5PacketUtils.createDiscoverInfo(targetJID, initiatorJID); + discoverInfo.addFeature(Bytestream.NAMESPACE); - // return that SOCKS5 is supported if target is queried - protocol.addResponse(discoverInfo, Verification.correspondingSenderReceiver, - Verification.requestTypeGET); + // return that SOCKS5 is supported if target is queried + protocol.addResponse(discoverInfo, Verification.correspondingSenderReceiver, + Verification.requestTypeGET); - // build discover items containing no proxy item - DiscoverItems discoverItems = Socks5PacketUtils.createDiscoverItems(xmppServer, - initiatorJID); + // build discover items containing no proxy item + DiscoverItems discoverItems = Socks5PacketUtils.createDiscoverItems(xmppServer, + initiatorJID); - // return the discover item if XMPP server is queried - protocol.addResponse(discoverItems, Verification.correspondingSenderReceiver, - Verification.requestTypeGET); + // return the discover item if XMPP server is queried + protocol.addResponse(discoverItems, Verification.correspondingSenderReceiver, + Verification.requestTypeGET); - // build used stream host response - Bytestream streamHostUsedPacket = Socks5PacketUtils.createBytestreamResponse(targetJID, - initiatorJID); - streamHostUsedPacket.setSessionID(sessionID); - streamHostUsedPacket.setUsedHost(initiatorJID); // local proxy used + // build used stream host response + Bytestream streamHostUsedPacket = Socks5PacketUtils.createBytestreamResponse(targetJID, + initiatorJID); + streamHostUsedPacket.setSessionID(sessionID); + streamHostUsedPacket.setUsedHost(initiatorJID); // local proxy used - // return used stream host info as response to the bytestream initiation - protocol.addResponse(streamHostUsedPacket, new Verification() { + // return used stream host info as response to the bytestream initiation + protocol.addResponse(streamHostUsedPacket, new Verification() { + @Override + public void verify(Bytestream request, Bytestream response) { + assertEquals(response.getSessionID(), request.getSessionID()); + StreamHost streamHost1 = request.getStreamHosts().get(0); + assertEquals(response.getUsedHost().getJID(), streamHost1.getJID()); + StreamHost streamHost2 = request.getStreamHosts().get(request.getStreamHosts().size() - 1); + assertEquals(response.getUsedHost().getJID(), streamHost2.getJID()); + assertEquals("localAddress", streamHost2.getAddress()); + } + }, Verification.correspondingSenderReceiver, Verification.requestTypeSET); - @Override - public void verify(Bytestream request, Bytestream response) { - assertEquals(response.getSessionID(), request.getSessionID()); - StreamHost streamHost1 = request.getStreamHosts().get(0); - assertEquals(response.getUsedHost().getJID(), streamHost1.getJID()); - StreamHost streamHost2 = request.getStreamHosts().get(request.getStreamHosts().size() - 1); - assertEquals(response.getUsedHost().getJID(), streamHost2.getJID()); - assertEquals("localAddress", streamHost2.getAddress()); + // create digest to get the socket opened by target + String digest = Socks5Utils.createDigest(sessionID, initiatorJID, targetJID); + + // connect to proxy as target + socks5Proxy.addTransfer(digest); + StreamHost streamHost = new StreamHost(targetJID, + socks5Proxy.getLocalAddresses().get(0), + socks5Proxy.getPort()); + Socks5Client socks5Client = new Socks5Client(streamHost, digest); + InputStream inputStream = socks5Client.getSocket(2000).getInputStream(); + + // add another network address before establishing SOCKS5 Bytestream + socks5Proxy.addLocalAddress("localAddress"); + + // finally call the method that should be tested + OutputStream outputStream = byteStreamManager.establishSession(targetJID, sessionID).getOutputStream(); + + // test the established bytestream + byte[] data = new byte[] { 1, 2, 3 }; + outputStream.write(data); + + byte[] result = new byte[3]; + inputStream.read(result); + + assertArrayEquals(data, result); + + protocol.verifyAll(); + } finally { + socks5Proxy.stop(); } - - }, Verification.correspondingSenderReceiver, Verification.requestTypeSET); - - // create digest to get the socket opened by target - String digest = Socks5Utils.createDigest(sessionID, initiatorJID, targetJID); - - // connect to proxy as target - socks5Proxy.addTransfer(digest); - StreamHost streamHost = new StreamHost(targetJID, - socks5Proxy.getLocalAddresses().get(0), - socks5Proxy.getPort()); - Socks5Client socks5Client = new Socks5Client(streamHost, digest); - InputStream inputStream = socks5Client.getSocket(2000).getInputStream(); - - // add another network address before establishing SOCKS5 Bytestream - socks5Proxy.addLocalAddress("localAddress"); - - // finally call the method that should be tested - OutputStream outputStream = byteStreamManager.establishSession(targetJID, sessionID).getOutputStream(); - - // test the established bytestream - byte[] data = new byte[] { 1, 2, 3 }; - outputStream.write(data); - - byte[] result = new byte[3]; - inputStream.read(result); - - assertArrayEquals(data, result); - - protocol.verifyAll(); - - // reset proxy settings - socks5Proxy.stop(); - socks5Proxy.removeLocalAddress("localAddress"); - Socks5Proxy.setLocalSocks5ProxyPort(7777); - } /** * Invoking {@link Socks5BytestreamManager#establishSession(org.jxmpp.jid.Jid, String)} the first time * should successfully negotiate a SOCKS5 Bytestream via the second SOCKS5 proxy and should * prioritize this proxy for a second SOCKS5 Bytestream negotiation. + * @throws InterruptedException + * @throws SmackException + * @throws XMPPException + * @throws IOException * - * @throws Exception should not happen */ @Test - public void shouldPrioritizeSecondSocks5ProxyOnSecondAttempt() throws Exception { - - // disable clients local SOCKS5 proxy - Socks5Proxy.setLocalSocks5ProxyEnabled(false); + public void shouldPrioritizeSecondSocks5ProxyOnSecondAttempt() throws SmackException, InterruptedException, IOException, XMPPException { + final Protocol protocol = new Protocol(); + final XMPPConnection connection = ConnectionUtils.createMockedConnection(protocol, initiatorJID); + final String sessionID = "session_id_shouldPrioritizeSecondSocks5ProxyOnSecondAttempt"; // get Socks5ByteStreamManager for connection Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection); + byteStreamManager.setAnnounceLocalStreamHost(false); assertTrue(byteStreamManager.isProxyPrioritizationEnabled()); @@ -878,59 +875,58 @@ public class Socks5ByteStreamManagerTest { } }; - createResponses(streamHostUsedVerification1); // start a local SOCKS5 proxy - Socks5TestProxy socks5Proxy = Socks5TestProxy.getProxy(7778); - socks5Proxy.start(); + try (Socks5TestProxy socks5Proxy = new Socks5TestProxy()) { + createResponses(protocol, sessionID, streamHostUsedVerification1, socks5Proxy); - // create digest to get the socket opened by target - String digest = Socks5Utils.createDigest(sessionID, initiatorJID, targetJID); + // create digest to get the socket opened by target + String digest = Socks5Utils.createDigest(sessionID, initiatorJID, targetJID); - // call the method that should be tested - OutputStream outputStream = byteStreamManager.establishSession(targetJID, sessionID).getOutputStream(); + // call the method that should be tested + OutputStream outputStream = byteStreamManager.establishSession(targetJID, sessionID).getOutputStream(); - // test the established bytestream - InputStream inputStream = socks5Proxy.getSocket(digest).getInputStream(); + // test the established bytestream + InputStream inputStream = socks5Proxy.getSocket(digest).getInputStream(); - byte[] data = new byte[] { 1, 2, 3 }; - outputStream.write(data); + byte[] data = new byte[] { 1, 2, 3 }; + outputStream.write(data); - byte[] result = new byte[3]; - inputStream.read(result); + byte[] result = new byte[3]; + inputStream.read(result); - assertArrayEquals(data, result); + assertArrayEquals(data, result); - protocol.verifyAll(); + protocol.verifyAll(); - Verification streamHostUsedVerification2 = new Verification() { + Verification streamHostUsedVerification2 = new Verification() { - @Override - public void verify(Bytestream request, Bytestream response) { - assertEquals(response.getSessionID(), request.getSessionID()); - assertEquals(2, request.getStreamHosts().size()); - // verify that the used stream host is the first in list - StreamHost streamHost = (StreamHost) request.getStreamHosts().toArray()[0]; - assertEquals(response.getUsedHost().getJID(), streamHost.getJID()); - } + @Override + public void verify(Bytestream request, Bytestream response) { + assertEquals(response.getSessionID(), request.getSessionID()); + assertEquals(2, request.getStreamHosts().size()); + // verify that the used stream host is the first in list + StreamHost streamHost = (StreamHost) request.getStreamHosts().toArray()[0]; + assertEquals(response.getUsedHost().getJID(), streamHost.getJID()); + } - }; - createResponses(streamHostUsedVerification2); + }; + createResponses(protocol, sessionID, streamHostUsedVerification2, socks5Proxy); - // call the method that should be tested again - outputStream = byteStreamManager.establishSession(targetJID, sessionID).getOutputStream(); + // call the method that should be tested again + outputStream = byteStreamManager.establishSession(targetJID, sessionID).getOutputStream(); - // test the established bytestream - inputStream = socks5Proxy.getSocket(digest).getInputStream(); + // test the established bytestream + inputStream = socks5Proxy.getSocket(digest).getInputStream(); - outputStream.write(data); + outputStream.write(data); - inputStream.read(result); + inputStream.read(result); - assertArrayEquals(data, result); - - protocol.verifyAll(); + assertArrayEquals(data, result); + protocol.verifyAll(); + } } /** @@ -938,18 +934,23 @@ public class Socks5ByteStreamManagerTest { * should successfully negotiate a SOCKS5 Bytestream via the second SOCKS5 proxy. The second * negotiation should run in the same manner if prioritization is disabled. * - * @throws Exception should not happen + * @throws IOException + * @throws InterruptedException + * @throws SmackException + * @throws XMPPException + * */ @Test - public void shouldNotPrioritizeSocks5ProxyIfPrioritizationDisabled() throws Exception { - - // disable clients local SOCKS5 proxy - Socks5Proxy.setLocalSocks5ProxyEnabled(false); + public void shouldNotPrioritizeSocks5ProxyIfPrioritizationDisabled() throws IOException, SmackException, InterruptedException, XMPPException { + final Protocol protocol = new Protocol(); + final XMPPConnection connection = ConnectionUtils.createMockedConnection(protocol, initiatorJID); + final String sessionID = "session_id_shouldNotPrioritizeSocks5ProxyIfPrioritizationDisabled"; // get Socks5ByteStreamManager for connection Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection); - byteStreamManager.setProxyPrioritizationEnabled(false); + byteStreamManager.setAnnounceLocalStreamHost(false); + byteStreamManager.setProxyPrioritizationEnabled(false); assertFalse(byteStreamManager.isProxyPrioritizationEnabled()); Verification streamHostUsedVerification = new Verification() { @@ -964,52 +965,51 @@ public class Socks5ByteStreamManagerTest { } }; - createResponses(streamHostUsedVerification); // start a local SOCKS5 proxy - Socks5TestProxy socks5Proxy = Socks5TestProxy.getProxy(7778); - socks5Proxy.start(); + try (Socks5TestProxy socks5Proxy = new Socks5TestProxy()) { + createResponses(protocol, sessionID, streamHostUsedVerification, socks5Proxy); - // create digest to get the socket opened by target - String digest = Socks5Utils.createDigest(sessionID, initiatorJID, targetJID); + // create digest to get the socket opened by target + String digest = Socks5Utils.createDigest(sessionID, initiatorJID, targetJID); - // call the method that should be tested - OutputStream outputStream = byteStreamManager.establishSession(targetJID, sessionID).getOutputStream(); + // call the method that should be tested + OutputStream outputStream = byteStreamManager.establishSession(targetJID, sessionID).getOutputStream(); - // test the established bytestream - InputStream inputStream = socks5Proxy.getSocket(digest).getInputStream(); + // test the established bytestream + InputStream inputStream = socks5Proxy.getSocket(digest).getInputStream(); - byte[] data = new byte[] { 1, 2, 3 }; - outputStream.write(data); + byte[] data = new byte[] { 1, 2, 3 }; + outputStream.write(data); - byte[] result = new byte[3]; - inputStream.read(result); + byte[] result = new byte[3]; + inputStream.read(result); - assertArrayEquals(data, result); + assertArrayEquals(data, result); + + protocol.verifyAll(); + + createResponses(protocol, sessionID, streamHostUsedVerification, socks5Proxy); + + // call the method that should be tested again + outputStream = byteStreamManager.establishSession(targetJID, sessionID).getOutputStream(); + + // test the established bytestream + inputStream = socks5Proxy.getSocket(digest).getInputStream(); + + outputStream.write(data); + + inputStream.read(result); + + assertArrayEquals(data, result); + } protocol.verifyAll(); - - createResponses(streamHostUsedVerification); - - // call the method that should be tested again - outputStream = byteStreamManager.establishSession(targetJID, sessionID).getOutputStream(); - - // test the established bytestream - inputStream = socks5Proxy.getSocket(digest).getInputStream(); - - outputStream.write(data); - - inputStream.read(result); - - assertArrayEquals(data, result); - - protocol.verifyAll(); - - byteStreamManager.setProxyPrioritizationEnabled(true); - } - private void createResponses(Verification streamHostUsedVerification) throws XmppStringprepException { + private static void createResponses(Protocol protocol, String sessionID, + Verification streamHostUsedVerification, Socks5TestProxy socks5TestProxy) + throws XmppStringprepException { // build discover info that supports the SOCKS5 feature DiscoverInfo discoverInfo = Socks5PacketUtils.createDiscoverInfo(targetJID, initiatorJID); discoverInfo.addFeature(Bytestream.NAMESPACE); @@ -1056,7 +1056,7 @@ public class Socks5ByteStreamManagerTest { */ Bytestream streamHostInfo1 = Socks5PacketUtils.createBytestreamResponse( JidCreate.from("proxy2.xmpp-server"), initiatorJID); - streamHostInfo1.addStreamHost(JidCreate.from("proxy2.xmpp-server"), proxyAddress, 7778); + streamHostInfo1.addStreamHost(JidCreate.from("proxy2.xmpp-server"), proxyAddress, socks5TestProxy.getPort()); // return stream host info if it is queried protocol.addResponse(streamHostInfo1, Verification.correspondingSenderReceiver, @@ -1065,7 +1065,7 @@ public class Socks5ByteStreamManagerTest { // build a SOCKS5 stream host info containing the address and the port of the proxy Bytestream streamHostInfo2 = Socks5PacketUtils.createBytestreamResponse(proxyJID, initiatorJID); - streamHostInfo2.addStreamHost(proxyJID, proxyAddress, 7778); + streamHostInfo2.addStreamHost(proxyJID, proxyAddress, socks5TestProxy.getPort()); // return stream host info if it is queried protocol.addResponse(streamHostInfo2, Verification.correspondingSenderReceiver, @@ -1094,16 +1094,6 @@ public class Socks5ByteStreamManagerTest { } }, Verification.correspondingSenderReceiver, Verification.requestTypeSET); - - } - - /** - * Stop eventually started local SOCKS5 test proxy. - */ - @After - public void cleanUp() { - Socks5TestProxy.stopProxy(); - Socks5Proxy.setLocalSocks5ProxyEnabled(true); } } diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ByteStreamRequestTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ByteStreamRequestTest.java index 5f3b373b9..59d5f99c1 100644 --- a/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ByteStreamRequestTest.java +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ByteStreamRequestTest.java @@ -27,19 +27,16 @@ import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; -import org.jivesoftware.smack.SmackException; import org.jivesoftware.smack.XMPPConnection; -import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.Stanza; import org.jivesoftware.smack.packet.StanzaError; +import org.jivesoftware.smack.util.NetworkUtil; import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream; import org.jivesoftware.util.ConnectionUtils; import org.jivesoftware.util.Protocol; -import org.junit.After; -import org.junit.Before; import org.junit.Test; import org.jxmpp.jid.DomainBareJid; import org.jxmpp.jid.EntityFullJid; @@ -60,27 +57,6 @@ public class Socks5ByteStreamRequestTest { private static final String proxyAddress = "127.0.0.1"; private static final String sessionID = "session_id"; - private Protocol protocol; - - private XMPPConnection connection; - - /** - * Initialize fields used in the tests. - * @throws XMPPException - * @throws SmackException - * @throws InterruptedException - */ - @Before - public void setup() throws XMPPException, SmackException, InterruptedException { - - // build protocol verifier - protocol = new Protocol(); - - // create mocked XMPP connection - connection = ConnectionUtils.createMockedConnection(protocol, targetJID); - - } - /** * Accepting a SOCKS5 Bytestream request should fail if the request doesn't contain any Socks5 * proxies. @@ -89,6 +65,9 @@ public class Socks5ByteStreamRequestTest { */ @Test public void shouldFailIfRequestHasNoStreamHosts() throws Exception { + final Protocol protocol = new Protocol(); + final XMPPConnection connection = ConnectionUtils.createMockedConnection(protocol, targetJID); + assertThrows(Socks5Exception.NoSocks5StreamHostsProvided.class, () -> { // build SOCKS5 Bytestream initialization request with no SOCKS5 proxies Bytestream bytestreamInitialization = Socks5PacketUtils.createBytestreamInitiation( @@ -113,7 +92,6 @@ public class Socks5ByteStreamRequestTest { assertEquals(IQ.Type.error, ((IQ) targetResponse).getType()); assertEquals(StanzaError.Condition.item_not_found, targetResponse.getError().getCondition()); - } /** @@ -124,6 +102,9 @@ public class Socks5ByteStreamRequestTest { */ @Test public void shouldFailIfRequestHasInvalidStreamHosts() throws Exception { + final Protocol protocol = new Protocol(); + final XMPPConnection connection = ConnectionUtils.createMockedConnection(protocol, targetJID); + assertThrows(Socks5Exception.CouldNotConnectToAnyProvidedSocks5Host.class, () -> { // build SOCKS5 Bytestream initialization request Bytestream bytestreamInitialization = Socks5PacketUtils.createBytestreamInitiation( @@ -150,7 +131,6 @@ public class Socks5ByteStreamRequestTest { assertEquals(IQ.Type.error, ((IQ) targetResponse).getType()); assertEquals(StanzaError.Condition.item_not_found, targetResponse.getError().getCondition()); - } /** @@ -160,10 +140,13 @@ public class Socks5ByteStreamRequestTest { */ @Test public void shouldBlacklistInvalidProxyAfter2Failures() throws Exception { + final Protocol protocol = new Protocol(); + final XMPPConnection connection = ConnectionUtils.createMockedConnection(protocol, targetJID); // build SOCKS5 Bytestream initialization request Bytestream bytestreamInitialization = Socks5PacketUtils.createBytestreamInitiation( initiatorJID, targetJID, sessionID); + // Add an unreachable stream host. bytestreamInitialization.addStreamHost(JidCreate.from("invalid." + proxyJID), "127.0.0.2", 7778); // get SOCKS5 Bytestream manager for connection @@ -196,44 +179,44 @@ public class Socks5ByteStreamRequestTest { // create test data for stream byte[] data = new byte[] { 1, 2, 3 }; - Socks5TestProxy socks5Proxy = Socks5TestProxy.getProxy(7779); - assertTrue(socks5Proxy.isRunning()); + try (Socks5TestProxy socks5Proxy = new Socks5TestProxy()) { + assertTrue(socks5Proxy.isRunning()); - // add a valid SOCKS5 proxy - bytestreamInitialization.addStreamHost(proxyJID, proxyAddress, 7779); + // add a valid SOCKS5 proxy + bytestreamInitialization.addStreamHost(proxyJID, proxyAddress, socks5Proxy.getPort()); - // build SOCKS5 Bytestream request with the bytestream initialization - Socks5BytestreamRequest byteStreamRequest = new Socks5BytestreamRequest(byteStreamManager, - bytestreamInitialization); + // build SOCKS5 Bytestream request with the bytestream initialization + Socks5BytestreamRequest byteStreamRequest = new Socks5BytestreamRequest(byteStreamManager, + bytestreamInitialization); - // set timeouts - byteStreamRequest.setTotalConnectTimeout(600); - byteStreamRequest.setMinimumConnectTimeout(300); + // set timeouts + byteStreamRequest.setTotalConnectTimeout(600); + byteStreamRequest.setMinimumConnectTimeout(300); - // accept the stream (this is the call that is tested here) - InputStream inputStream = byteStreamRequest.accept().getInputStream(); + // accept the stream (this is the call that is tested here) + InputStream inputStream = byteStreamRequest.accept().getInputStream(); - // create digest to get the socket opened by target - String digest = Socks5Utils.createDigest(sessionID, initiatorJID, targetJID); + // create digest to get the socket opened by target + String digest = Socks5Utils.createDigest(sessionID, initiatorJID, targetJID); - // test stream by sending some data - OutputStream outputStream = socks5Proxy.getSocket(digest).getOutputStream(); - outputStream.write(data); + // test stream by sending some data + OutputStream outputStream = socks5Proxy.getSocket(digest).getOutputStream(); + outputStream.write(data); - // verify that data is transferred correctly - byte[] result = new byte[3]; - inputStream.read(result); - assertArrayEquals(data, result); - - // verify targets response - assertEquals(1, protocol.getRequests().size()); - Stanza targetResponse = protocol.getRequests().remove(0); - assertEquals(Bytestream.class, targetResponse.getClass()); - assertEquals(initiatorJID, targetResponse.getTo()); - assertEquals(IQ.Type.result, ((Bytestream) targetResponse).getType()); - assertEquals(proxyJID, ((Bytestream) targetResponse).getUsedHost().getJID()); + // verify that data is transferred correctly + byte[] result = new byte[3]; + inputStream.read(result); + assertArrayEquals(data, result); + // verify targets response + assertEquals(1, protocol.getRequests().size()); + Stanza targetResponse = protocol.getRequests().remove(0); + assertEquals(Bytestream.class, targetResponse.getClass()); + assertEquals(initiatorJID, targetResponse.getTo()); + assertEquals(IQ.Type.result, ((Bytestream) targetResponse).getType()); + assertEquals(proxyJID, ((Bytestream) targetResponse).getUsedHost().getJID()); + } } /** @@ -243,9 +226,8 @@ public class Socks5ByteStreamRequestTest { */ @Test public void shouldNotBlacklistInvalidProxy() throws Exception { - - // disable blacklisting - Socks5BytestreamRequest.setConnectFailureThreshold(0); + final Protocol protocol = new Protocol(); + final XMPPConnection connection = ConnectionUtils.createMockedConnection(protocol, targetJID); // build SOCKS5 Bytestream initialization request Bytestream bytestreamInitialization = Socks5PacketUtils.createBytestreamInitiation( @@ -265,6 +247,7 @@ public class Socks5ByteStreamRequestTest { // set timeouts byteStreamRequest.setTotalConnectTimeout(600); byteStreamRequest.setMinimumConnectTimeout(300); + byteStreamRequest.setConnectFailureThreshold(0); // accept the stream (this is the call that is tested here) byteStreamRequest.accept(); @@ -279,10 +262,6 @@ public class Socks5ByteStreamRequestTest { assertEquals(StanzaError.Condition.item_not_found, targetResponse.getError().getCondition()); } - - // enable blacklisting - Socks5BytestreamRequest.setConnectFailureThreshold(2); - } /** @@ -294,62 +273,65 @@ public class Socks5ByteStreamRequestTest { */ @Test public void shouldNotTimeoutIfFirstSocks5ProxyDoesNotRespond() throws Exception { + final Protocol protocol = new Protocol(); + final XMPPConnection connection = ConnectionUtils.createMockedConnection(protocol, targetJID); // start a local SOCKS5 proxy - Socks5TestProxy socks5Proxy = Socks5TestProxy.getProxy(7778); + try (Socks5TestProxy socks5Proxy = new Socks5TestProxy()) { + // create a fake SOCKS5 proxy that doesn't respond to a request + ServerSocket unresponsiveSocks5Socket = NetworkUtil.getSocketOnLoopback(); - // create a fake SOCKS5 proxy that doesn't respond to a request - ServerSocket serverSocket = new ServerSocket(7779); + try { + // build SOCKS5 Bytestream initialization request + Bytestream bytestreamInitialization = Socks5PacketUtils.createBytestreamInitiation( + initiatorJID, targetJID, sessionID); + bytestreamInitialization.addStreamHost(proxyJID, proxyAddress, unresponsiveSocks5Socket.getLocalPort()); + bytestreamInitialization.addStreamHost(proxyJID, proxyAddress, socks5Proxy.getPort()); - // build SOCKS5 Bytestream initialization request - Bytestream bytestreamInitialization = Socks5PacketUtils.createBytestreamInitiation( - initiatorJID, targetJID, sessionID); - bytestreamInitialization.addStreamHost(proxyJID, proxyAddress, 7779); - bytestreamInitialization.addStreamHost(proxyJID, proxyAddress, 7778); + // create test data for stream + byte[] data = new byte[] { 1, 2, 3 }; - // create test data for stream - byte[] data = new byte[] { 1, 2, 3 }; + // get SOCKS5 Bytestream manager for connection + Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection); - // get SOCKS5 Bytestream manager for connection - Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection); + // build SOCKS5 Bytestream request with the bytestream initialization + Socks5BytestreamRequest byteStreamRequest = new Socks5BytestreamRequest(byteStreamManager, + bytestreamInitialization); - // build SOCKS5 Bytestream request with the bytestream initialization - Socks5BytestreamRequest byteStreamRequest = new Socks5BytestreamRequest(byteStreamManager, - bytestreamInitialization); + // set timeouts + byteStreamRequest.setTotalConnectTimeout(2000); + byteStreamRequest.setMinimumConnectTimeout(1000); - // set timeouts - byteStreamRequest.setTotalConnectTimeout(2000); - byteStreamRequest.setMinimumConnectTimeout(1000); + // accept the stream (this is the call that is tested here) + InputStream inputStream = byteStreamRequest.accept().getInputStream(); - // accept the stream (this is the call that is tested here) - InputStream inputStream = byteStreamRequest.accept().getInputStream(); + // assert that client tries to connect to dumb SOCKS5 proxy + Socket socket = unresponsiveSocks5Socket.accept(); + assertNotNull(socket); - // assert that client tries to connect to dumb SOCKS5 proxy - Socket socket = serverSocket.accept(); - assertNotNull(socket); + // create digest to get the socket opened by target + String digest = Socks5Utils.createDigest(sessionID, initiatorJID, targetJID); - // create digest to get the socket opened by target - String digest = Socks5Utils.createDigest(sessionID, initiatorJID, targetJID); + // test stream by sending some data + OutputStream outputStream = socks5Proxy.getSocket(digest).getOutputStream(); + outputStream.write(data); - // test stream by sending some data - OutputStream outputStream = socks5Proxy.getSocket(digest).getOutputStream(); - outputStream.write(data); - - // verify that data is transferred correctly - byte[] result = new byte[3]; - inputStream.read(result); - assertArrayEquals(data, result); - - // verify targets response - assertEquals(1, protocol.getRequests().size()); - Stanza targetResponse = protocol.getRequests().remove(0); - assertEquals(Bytestream.class, targetResponse.getClass()); - assertEquals(initiatorJID, targetResponse.getTo()); - assertEquals(IQ.Type.result, ((Bytestream) targetResponse).getType()); - assertEquals(proxyJID, ((Bytestream) targetResponse).getUsedHost().getJID()); - - serverSocket.close(); + // verify that data is transferred correctly + byte[] result = new byte[3]; + inputStream.read(result); + assertArrayEquals(data, result); + // verify targets response + assertEquals(1, protocol.getRequests().size()); + Stanza targetResponse = protocol.getRequests().remove(0); + assertEquals(Bytestream.class, targetResponse.getClass()); + assertEquals(initiatorJID, targetResponse.getTo()); + assertEquals(IQ.Type.result, ((Bytestream) targetResponse).getType()); + assertEquals(proxyJID, ((Bytestream) targetResponse).getUsedHost().getJID()); + } finally { + unresponsiveSocks5Socket.close(); + } + } } /** @@ -359,57 +341,48 @@ public class Socks5ByteStreamRequestTest { */ @Test public void shouldAcceptSocks5BytestreamRequestAndReceiveData() throws Exception { + final Protocol protocol = new Protocol(); + final XMPPConnection connection = ConnectionUtils.createMockedConnection(protocol, targetJID); // start a local SOCKS5 proxy - Socks5TestProxy socks5Proxy = Socks5TestProxy.getProxy(7778); + try (Socks5TestProxy socks5Proxy = new Socks5TestProxy()) { + // build SOCKS5 Bytestream initialization request + Bytestream bytestreamInitialization = Socks5PacketUtils.createBytestreamInitiation( + initiatorJID, targetJID, sessionID); + bytestreamInitialization.addStreamHost(proxyJID, proxyAddress, socks5Proxy.getPort()); - // build SOCKS5 Bytestream initialization request - Bytestream bytestreamInitialization = Socks5PacketUtils.createBytestreamInitiation( - initiatorJID, targetJID, sessionID); - bytestreamInitialization.addStreamHost(proxyJID, proxyAddress, 7778); + // create test data for stream + byte[] data = new byte[] { 1, 2, 3 }; - // create test data for stream - byte[] data = new byte[] { 1, 2, 3 }; + // get SOCKS5 Bytestream manager for connection + Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection); - // get SOCKS5 Bytestream manager for connection - Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection); + // build SOCKS5 Bytestream request with the bytestream initialization + Socks5BytestreamRequest byteStreamRequest = new Socks5BytestreamRequest(byteStreamManager, + bytestreamInitialization); - // build SOCKS5 Bytestream request with the bytestream initialization - Socks5BytestreamRequest byteStreamRequest = new Socks5BytestreamRequest(byteStreamManager, - bytestreamInitialization); + // accept the stream (this is the call that is tested here) + InputStream inputStream = byteStreamRequest.accept().getInputStream(); - // accept the stream (this is the call that is tested here) - InputStream inputStream = byteStreamRequest.accept().getInputStream(); + // create digest to get the socket opened by target + String digest = Socks5Utils.createDigest(sessionID, initiatorJID, targetJID); - // create digest to get the socket opened by target - String digest = Socks5Utils.createDigest(sessionID, initiatorJID, targetJID); + // test stream by sending some data + OutputStream outputStream = socks5Proxy.getSocket(digest).getOutputStream(); + outputStream.write(data); - // test stream by sending some data - OutputStream outputStream = socks5Proxy.getSocket(digest).getOutputStream(); - outputStream.write(data); - - // verify that data is transferred correctly - byte[] result = new byte[3]; - inputStream.read(result); - assertArrayEquals(data, result); - - // verify targets response - assertEquals(1, protocol.getRequests().size()); - Stanza targetResponse = protocol.getRequests().remove(0); - assertEquals(Bytestream.class, targetResponse.getClass()); - assertEquals(initiatorJID, targetResponse.getTo()); - assertEquals(IQ.Type.result, ((Bytestream) targetResponse).getType()); - assertEquals(proxyJID, ((Bytestream) targetResponse).getUsedHost().getJID()); + // verify that data is transferred correctly + byte[] result = new byte[3]; + inputStream.read(result); + assertArrayEquals(data, result); + // verify targets response + assertEquals(1, protocol.getRequests().size()); + Stanza targetResponse = protocol.getRequests().remove(0); + assertEquals(Bytestream.class, targetResponse.getClass()); + assertEquals(initiatorJID, targetResponse.getTo()); + assertEquals(IQ.Type.result, ((Bytestream) targetResponse).getType()); + assertEquals(proxyJID, ((Bytestream) targetResponse).getUsedHost().getJID()); + } } - - /** - * Stop eventually started local SOCKS5 test proxy. - */ - @After - public void cleanUp() { - Socks5TestProxy.stopProxy(); - Socks5Proxy.setLocalSocks5ProxyEnabled(true); - } - } diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ClientForInitiatorTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ClientForInitiatorTest.java index 3b0a5faf8..fe3807eed 100644 --- a/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ClientForInitiatorTest.java +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ClientForInitiatorTest.java @@ -28,12 +28,12 @@ import java.net.Socket; import org.jivesoftware.smack.SmackException; import org.jivesoftware.smack.XMPPConnection; -import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.XMPPException.XMPPErrorException; import org.jivesoftware.smack.packet.EmptyResultIQ; import org.jivesoftware.smack.packet.ErrorIQ; import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.StanzaError; +import org.jivesoftware.smack.util.CloseableUtil; import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream; import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream.StreamHost; @@ -41,8 +41,6 @@ import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream.StreamHost; import org.jivesoftware.util.ConnectionUtils; import org.jivesoftware.util.Protocol; import org.jivesoftware.util.Verification; -import org.junit.After; -import org.junit.Before; import org.junit.Test; import org.jxmpp.jid.DomainBareJid; import org.jxmpp.jid.EntityFullJid; @@ -63,31 +61,8 @@ public class Socks5ClientForInitiatorTest { private static final int GET_SOCKET_TIMEOUT = 90 * 1000; - private static final int proxyPort = 7890; private static final String sessionID = "session_id"; - // protocol verifier - private Protocol protocol; - - // mocked XMPP connection - private XMPPConnection connection; - - /** - * Initialize fields used in the tests. - * @throws XMPPException - * @throws SmackException - * @throws InterruptedException - */ - @Before - public void setup() throws XMPPException, SmackException, InterruptedException { - - // build protocol verifier - protocol = new Protocol(); - - // create mocked XMPP connection - connection = ConnectionUtils.createMockedConnection(protocol, initiatorJID); - } - /** * If the target is not connected to the local SOCKS5 proxy an exception should be thrown. * @@ -95,35 +70,36 @@ public class Socks5ClientForInitiatorTest { */ @Test public void shouldFailIfTargetIsNotConnectedToLocalSocks5Proxy() throws Exception { + Protocol protocol = new Protocol(); + XMPPConnection connection = ConnectionUtils.createMockedConnection(protocol, initiatorJID); // start a local SOCKS5 proxy - Socks5Proxy.setLocalSocks5ProxyPort(proxyPort); - Socks5Proxy socks5Proxy = Socks5Proxy.getSocks5Proxy(); + Socks5Proxy socks5Proxy = new Socks5Proxy(); socks5Proxy.start(); - - // build stream host information for local SOCKS5 proxy - StreamHost streamHost = new StreamHost(connection.getUser(), - loopbackAddress, - socks5Proxy.getPort()); - - // create digest to get the socket opened by target - String digest = Socks5Utils.createDigest(sessionID, initiatorJID, targetJID); - - Socks5ClientForInitiator socks5Client = new Socks5ClientForInitiator(streamHost, digest, - connection, sessionID, targetJID); - try { - socks5Client.getSocket(GET_SOCKET_TIMEOUT); + // build stream host information for local SOCKS5 proxy + StreamHost streamHost = new StreamHost(connection.getUser(), + loopbackAddress, + socks5Proxy.getPort()); - fail("exception should be thrown"); + // create digest to get the socket opened by target + String digest = Socks5Utils.createDigest(sessionID, initiatorJID, targetJID); + + Socks5ClientForInitiator socks5Client = new Socks5ClientForInitiator(streamHost, digest, + connection, sessionID, targetJID); + + try { + socks5Client.getSocket(GET_SOCKET_TIMEOUT); + + fail("exception should be thrown"); + } + catch (SmackException e) { + assertTrue(e.getMessage().contains("target is not connected to SOCKS5 proxy")); + protocol.verifyAll(); // assert no XMPP messages were sent + } + } finally { + socks5Proxy.stop(); } - catch (SmackException e) { - assertTrue(e.getMessage().contains("target is not connected to SOCKS5 proxy")); - protocol.verifyAll(); // assert no XMPP messages were sent - } - - socks5Proxy.stop(); - } /** @@ -133,65 +109,67 @@ public class Socks5ClientForInitiatorTest { */ @Test public void shouldSuccessfullyConnectThroughLocalSocks5Proxy() throws Exception { + Protocol protocol = new Protocol(); + XMPPConnection connection = ConnectionUtils.createMockedConnection(protocol, initiatorJID); // start a local SOCKS5 proxy - Socks5Proxy.setLocalSocks5ProxyPort(proxyPort); - Socks5Proxy socks5Proxy = Socks5Proxy.getSocks5Proxy(); + Socks5Proxy socks5Proxy = new Socks5Proxy(); socks5Proxy.start(); + try { + // test data + final byte[] data = new byte[] { 1, 2, 3 }; - // test data - final byte[] data = new byte[] { 1, 2, 3 }; + // create digest + final String digest = Socks5Utils.createDigest(sessionID, initiatorJID, targetJID); - // create digest - final String digest = Socks5Utils.createDigest(sessionID, initiatorJID, targetJID); + // allow connection of target with this digest + socks5Proxy.addTransfer(digest); - // allow connection of target with this digest - socks5Proxy.addTransfer(digest); + // build stream host information + final StreamHost streamHost = new StreamHost(connection.getUser(), + loopbackAddress, + socks5Proxy.getPort()); - // build stream host information - final StreamHost streamHost = new StreamHost(connection.getUser(), - loopbackAddress, - socks5Proxy.getPort()); - - // target connects to local SOCKS5 proxy - Thread targetThread = new Thread() { - - @Override - public void run() { - try { - Socks5Client targetClient = new Socks5Client(streamHost, digest); - Socket socket = targetClient.getSocket(10000); - socket.getOutputStream().write(data); - } - catch (Exception e) { - fail(e.getMessage()); + // target connects to local SOCKS5 proxy + Thread targetThread = new Thread() { + @Override + public void run() { + try { + Socks5Client targetClient = new Socks5Client(streamHost, digest); + Socket socket = targetClient.getSocket(10000); + socket.getOutputStream().write(data); + } + catch (Exception e) { + // TODO: This does not work. + fail(e.getMessage()); + } } + }; + targetThread.start(); + + // TODO: Replace this Thread.sleep(). + Thread.sleep(200); + + // initiator connects + Socks5ClientForInitiator socks5Client = new Socks5ClientForInitiator(streamHost, digest, + connection, sessionID, targetJID); + + Socket socket = socks5Client.getSocket(GET_SOCKET_TIMEOUT); + + // verify test data + InputStream in = socket.getInputStream(); + for (int i = 0; i < data.length; i++) { + assertEquals(data[i], in.read()); } - }; - targetThread.start(); + targetThread.join(); - Thread.sleep(200); + protocol.verifyAll(); // assert no XMPP messages were sent - // initiator connects - Socks5ClientForInitiator socks5Client = new Socks5ClientForInitiator(streamHost, digest, - connection, sessionID, targetJID); - - Socket socket = socks5Client.getSocket(GET_SOCKET_TIMEOUT); - - // verify test data - InputStream in = socket.getInputStream(); - for (int i = 0; i < data.length; i++) { - assertEquals(data[i], in.read()); + socks5Proxy.removeTransfer(digest); + } finally { + socks5Proxy.stop(); } - - targetThread.join(); - - protocol.verifyAll(); // assert no XMPP messages were sent - - socks5Proxy.removeTransfer(digest); - socks5Proxy.stop(); - } /** @@ -202,6 +180,8 @@ public class Socks5ClientForInitiatorTest { */ @Test public void shouldFailIfActivateSocks5ProxyFails() throws Exception { + Protocol protocol = new Protocol(); + XMPPConnection connection = ConnectionUtils.createMockedConnection(protocol, initiatorJID); // build error response as reply to the stream activation IQ error = new ErrorIQ(StanzaError.getBuilder(StanzaError.Condition.internal_server_error)); @@ -212,30 +192,25 @@ public class Socks5ClientForInitiatorTest { Verification.requestTypeSET); // start a local SOCKS5 proxy - Socks5TestProxy socks5Proxy = Socks5TestProxy.getProxy(proxyPort); - socks5Proxy.start(); + try (Socks5TestProxy socks5Proxy = new Socks5TestProxy()) { + StreamHost streamHost = new StreamHost(proxyJID, loopbackAddress, socks5Proxy.getPort()); - StreamHost streamHost = new StreamHost(proxyJID, - loopbackAddress, socks5Proxy.getPort()); + // create digest to get the socket opened by target + String digest = Socks5Utils.createDigest(sessionID, initiatorJID, targetJID); - // create digest to get the socket opened by target - String digest = Socks5Utils.createDigest(sessionID, initiatorJID, targetJID); + Socks5ClientForInitiator socks5Client = new Socks5ClientForInitiator(streamHost, digest, connection, + sessionID, targetJID); - Socks5ClientForInitiator socks5Client = new Socks5ClientForInitiator(streamHost, digest, - connection, sessionID, targetJID); + try { - try { + socks5Client.getSocket(GET_SOCKET_TIMEOUT); - socks5Client.getSocket(GET_SOCKET_TIMEOUT); - - fail("exception should be thrown"); + fail("exception should be thrown"); + } catch (XMPPErrorException e) { + assertTrue(StanzaError.Condition.internal_server_error.equals(e.getStanzaError().getCondition())); + protocol.verifyAll(); + } } - catch (XMPPErrorException e) { - assertTrue(StanzaError.Condition.internal_server_error.equals(e.getStanzaError().getCondition())); - protocol.verifyAll(); - } - - socks5Proxy.stop(); } /** @@ -246,6 +221,8 @@ public class Socks5ClientForInitiatorTest { */ @Test public void shouldSuccessfullyEstablishConnectionAndActivateSocks5Proxy() throws Exception { + Protocol protocol = new Protocol(); + XMPPConnection connection = ConnectionUtils.createMockedConnection(protocol, initiatorJID); // build activation confirmation response IQ activationResponse = new EmptyResultIQ(); @@ -265,45 +242,34 @@ public class Socks5ClientForInitiatorTest { }); + Socket initiatorSocket = null, targetSocket = null; // start a local SOCKS5 proxy - Socks5TestProxy socks5Proxy = Socks5TestProxy.getProxy(proxyPort); - socks5Proxy.start(); + try (Socks5TestProxy socks5Proxy = new Socks5TestProxy()) { + StreamHost streamHost = new StreamHost(proxyJID, loopbackAddress, socks5Proxy.getPort()); - StreamHost streamHost = new StreamHost(proxyJID, - loopbackAddress, socks5Proxy.getPort()); + // create digest to get the socket opened by target + String digest = Socks5Utils.createDigest(sessionID, initiatorJID, targetJID); - // create digest to get the socket opened by target - String digest = Socks5Utils.createDigest(sessionID, initiatorJID, targetJID); + Socks5ClientForInitiator socks5Client = new Socks5ClientForInitiator(streamHost, digest, connection, + sessionID, targetJID); - Socks5ClientForInitiator socks5Client = new Socks5ClientForInitiator(streamHost, digest, - connection, sessionID, targetJID); + initiatorSocket = socks5Client.getSocket(10000); + InputStream in = initiatorSocket.getInputStream(); - Socket initiatorSocket = socks5Client.getSocket(10000); - InputStream in = initiatorSocket.getInputStream(); + targetSocket = socks5Proxy.getSocket(digest); + OutputStream out = targetSocket.getOutputStream(); - Socket targetSocket = socks5Proxy.getSocket(digest); - OutputStream out = targetSocket.getOutputStream(); + // verify test data + for (int i = 0; i < 10; i++) { + out.write(i); + assertEquals(i, in.read()); + } - // verify test data - for (int i = 0; i < 10; i++) { - out.write(i); - assertEquals(i, in.read()); + protocol.verifyAll(); + } finally { + CloseableUtil.maybeClose(initiatorSocket); + CloseableUtil.maybeClose(targetSocket); } - - protocol.verifyAll(); - - initiatorSocket.close(); - targetSocket.close(); - socks5Proxy.stop(); - - } - - /** - * Reset default port for local SOCKS5 proxy. - */ - @After - public void cleanup() { - Socks5Proxy.setLocalSocks5ProxyPort(7777); } } diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5TestProxy.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5TestProxy.java index c40d037cf..3c4fb236e 100644 --- a/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5TestProxy.java +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5TestProxy.java @@ -31,6 +31,8 @@ import java.util.logging.Level; import java.util.logging.Logger; import org.jivesoftware.smack.SmackException; +import org.jivesoftware.smack.util.CloseableUtil; +import org.jivesoftware.smack.util.NetworkUtil; /** * Simple SOCKS5 proxy for testing purposes. It is almost the same as the Socks5Proxy class but the @@ -38,12 +40,9 @@ import org.jivesoftware.smack.SmackException; * * @author Henning Staib */ -public final class Socks5TestProxy { +public final class Socks5TestProxy implements AutoCloseable { private static final Logger LOGGER = Logger.getLogger(Socks5TestProxy.class.getName()); - /* SOCKS5 proxy singleton */ - private static Socks5TestProxy socks5Server; - /* reusable implementation of a SOCKS5 proxy server process */ private Socks5ServerProcess serverProcess; @@ -51,64 +50,22 @@ public final class Socks5TestProxy { private Thread serverThread; /* server socket to accept SOCKS5 connections */ - private ServerSocket serverSocket; + private final ServerSocket serverSocket; /* assigns a connection to a digest */ private final Map connectionMap = new ConcurrentHashMap(); - /* port of the test proxy */ - private int port = 7777; - private boolean startupComplete; - /** - * Private constructor. - */ - private Socks5TestProxy(int port) { + Socks5TestProxy() throws IOException { + this(NetworkUtil.getSocketOnLoopback()); + } + + Socks5TestProxy(ServerSocket serverSocket) { + this.serverSocket = serverSocket; this.serverProcess = new Socks5ServerProcess(); - this.port = port; - } - - /** - * Returns the local SOCKS5 proxy server. - * - * @param port of the test proxy - * @return the local SOCKS5 proxy server - */ - public static synchronized Socks5TestProxy getProxy(int port) { - if (socks5Server == null) { - socks5Server = new Socks5TestProxy(port); - socks5Server.start(); - } - return socks5Server; - } - - /** - * Stops the test proxy. - */ - public static synchronized void stopProxy() { - if (socks5Server != null) { - socks5Server.stop(); - socks5Server = null; - } - } - - /** - * Starts the local SOCKS5 proxy server. If it is already running, this method does nothing. - */ - public synchronized void start() { - if (isRunning()) { - return; - } - try { - this.serverSocket = new ServerSocket(this.port); - this.serverThread = new Thread(this.serverProcess); - this.serverThread.start(); - } - catch (IOException e) { - LOGGER.log(Level.SEVERE, "exception", e); - // do nothing - } + this.serverThread = new Thread(this.serverProcess); + this.serverThread.start(); } /** @@ -138,8 +95,6 @@ public final class Socks5TestProxy { } } this.serverThread = null; - this.serverSocket = null; - } /** @@ -173,16 +128,15 @@ public final class Socks5TestProxy { * * @param digest identifying the connection * @return socket or null if there is no socket for the given digest + * @throws InterruptedException */ - @SuppressWarnings("WaitNotInLoop") - public Socket getSocket(String digest) { + public Socket getSocket(String digest) throws InterruptedException { synchronized (this) { - if (!startupComplete) { - try { - wait(5000); - } catch (InterruptedException e) { - LOGGER.log(Level.SEVERE, "exception", e); - } + long now = System.currentTimeMillis(); + final long deadline = now + 5000; + while (!startupComplete && now < deadline) { + wait(deadline - now); + now = System.currentTimeMillis(); } } if (!startupComplete) { @@ -197,7 +151,7 @@ public final class Socks5TestProxy { * @return true if the local SOCKS5 proxy server is running, otherwise false */ public boolean isRunning() { - return this.serverSocket != null; + return !this.serverSocket.isClosed(); } /** @@ -213,7 +167,7 @@ public final class Socks5TestProxy { Socket socket = null; try { - + // TODO: Add !serverSocket.isClosed() into the while condition and remove the following lines. if (Socks5TestProxy.this.serverSocket.isClosed() || Thread.currentThread().isInterrupted()) { return; @@ -227,20 +181,16 @@ public final class Socks5TestProxy { synchronized (this) { startupComplete = true; - notify(); + notifyAll(); } } catch (SocketException e) { /* do nothing */ + LOGGER.log(Level.FINE, "Socket exception in Socks5TestProxy " + this, e); } catch (Exception e) { - try { - LOGGER.log(Level.SEVERE, "exception", e); - socket.close(); - } - catch (IOException e1) { - /* Do Nothing */ - } + LOGGER.log(Level.SEVERE, "exception", e); + CloseableUtil.maybeClose(socket, LOGGER); } } @@ -309,4 +259,9 @@ public final class Socks5TestProxy { } + @Override + public void close() { + stop(); + } + }