diff --git a/smack-core/src/main/java/org/jivesoftware/smack/util/InternetAddress.java b/smack-core/src/main/java/org/jivesoftware/smack/util/InternetAddress.java new file mode 100644 index 000000000..0fca8182f --- /dev/null +++ b/smack-core/src/main/java/org/jivesoftware/smack/util/InternetAddress.java @@ -0,0 +1,213 @@ +/** + * + * Copyright 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. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smack.util; + +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.UnknownHostException; + +import org.minidns.dnslabel.DnsLabel; +import org.minidns.dnsname.DnsName; +import org.minidns.dnsname.InvalidDnsNameException; +import org.minidns.util.InetAddressUtil; + +/** + * An internet address, can be given as IP or as DNS name. + *

+ * This type is meant for strings that hold an internet address. The original string used to construct this type is + * stored and returning in the {@link #toString()} method. + *

+ * + * @since 4.4.0 + */ +public abstract class InternetAddress implements CharSequence { + + protected final String originalString; + + protected InternetAddress(String originalString) { + this.originalString = Objects.requireNonNull(originalString, "The 'originalString' argument must not be null"); + } + + public abstract InetAddress asInetAddress() throws UnknownHostException; + + @Override + public String toString() { + return originalString; + } + + @Override + public int length() { + return originalString.length(); + } + + @Override + public char charAt(int index) { + return originalString.charAt(index); + } + + @Override + public CharSequence subSequence(int start, int end) { + return originalString.subSequence(start, end); + } + + public static InternetAddress from(String address) { + final InternetAddress internetAddress; + if (InetAddressUtil.isIpV4Address(address)) { + internetAddress = new InternetAddress.Ipv4(address); + } else if (InetAddressUtil.isIpV6Address(address)) { + internetAddress = new InternetAddress.Ipv6(address); + } else if (address.contains(".")) { + InternetAddress domainNameInternetAddress; + try { + DnsName dnsName = DnsName.from(address); + domainNameInternetAddress = new InternetAddress.DomainName(address, dnsName); + } catch (InvalidDnsNameException e) { + domainNameInternetAddress = new InternetAddress.InvalidDomainName(address, e); + } + internetAddress = domainNameInternetAddress; + } else { + DnsLabel dnsLabel = DnsLabel.from(address); + internetAddress = new InternetAddress.DomainNameLabel(address, dnsLabel); + } + return internetAddress; + } + + public static InternetAddress from(InetAddress inetAddress) { + if (inetAddress instanceof Inet4Address) { + return new InternetAddress.Ipv4(inetAddress.getHostAddress(), (Inet4Address) inetAddress); + } else if (inetAddress instanceof Inet6Address) { + return new InternetAddress.Ipv6(inetAddress.getHostAddress(), (Inet6Address) inetAddress); + } else { + throw new IllegalArgumentException("Unknown type " + inetAddress.getClass() + " of " + inetAddress); + } + } + + private static class InetAddressInternetAddress extends InternetAddress { + private final InetAddress inetAddress; + + protected InetAddressInternetAddress(String originalString, InetAddress inetAddress) { + super(originalString); + this.inetAddress = inetAddress; + } + + @Override + public InetAddress asInetAddress() { + return inetAddress; + } + } + + public static final class Ipv4 extends InetAddressInternetAddress { + + private final Inet4Address inet4Address; + + private Ipv4(String originalString) { + this(originalString, InetAddressUtil.ipv4From(originalString)); + } + + private Ipv4(String originalString, Inet4Address inet4Address) { + super(originalString, inet4Address); + this.inet4Address = inet4Address; + } + + public Inet4Address getInet4Address() { + return inet4Address; + } + } + + public static final class Ipv6 extends InetAddressInternetAddress { + + private Inet6Address inet6Address; + + private Ipv6(String originalString) { + this(originalString, InetAddressUtil.ipv6From(originalString)); + } + + private Ipv6(String originalString, Inet6Address inet6Address) { + super(originalString, inet6Address); + this.inet6Address = inet6Address; + } + + public Inet6Address getInet6Address() { + return inet6Address; + } + } + + private static class NonNumericInternetAddress extends InternetAddress { + private boolean attemptedToResolveInetAddress; + private InetAddress inetAddress; + + protected NonNumericInternetAddress(String originalString) { + super(originalString); + } + + @Override + public InetAddress asInetAddress() throws UnknownHostException { + if (inetAddress != null || attemptedToResolveInetAddress) { + return inetAddress; + } + + attemptedToResolveInetAddress = true; + inetAddress = InetAddress.getByName(originalString); + + return inetAddress; + } + } + + public static final class DomainName extends NonNumericInternetAddress { + + private final DnsName dnsName; + + private DomainName(String originalString, DnsName dnsName) { + super(originalString); + this.dnsName = dnsName; + } + + public DnsName getDnsName() { + return dnsName; + } + + } + + public static final class DomainNameLabel extends NonNumericInternetAddress { + + private final DnsLabel dnsLabel; + + private DomainNameLabel(String originalString, DnsLabel dnsLabel) { + super(originalString); + this.dnsLabel = dnsLabel; + } + + public DnsLabel getDnsLabel() { + return dnsLabel; + } + } + + public static final class InvalidDomainName extends NonNumericInternetAddress { + + private final InvalidDnsNameException invalidDnsNameException; + + private InvalidDomainName(String originalString, InvalidDnsNameException invalidDnsNameException) { + super(originalString); + this.invalidDnsNameException = invalidDnsNameException; + } + + public InvalidDnsNameException getInvalidDnsNameException() { + return invalidDnsNameException; + } + } +} 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 fb0b3fa7b..24e4fad16 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 @@ -17,6 +17,7 @@ package org.jivesoftware.smackx.bytestreams.socks5; import java.io.IOException; +import java.net.InetAddress; import java.net.Socket; import java.util.ArrayList; import java.util.Collections; @@ -663,22 +664,16 @@ public final class Socks5BytestreamManager extends Manager implements Bytestream EntityFullJid myJid = connection.getUser(); for (Socks5Proxy socks5Server : Socks5Proxy.getRunningProxies()) { - List addresses = socks5Server.getLocalAddresses(); + List addresses = socks5Server.getLocalAddresses(); if (addresses.isEmpty()) { - // local address could not be determined - return null; + continue; } - final int port = socks5Server.getPort(); - outerloop: for (String address : addresses) { + final int port = socks5Server.getPort(); + for (InetAddress 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; - } + if (address.isLoopbackAddress()) { + continue; } streamHosts.add(new StreamHost(myJid, address, port)); } 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 c46f4af65..d5350d4a5 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 @@ -93,7 +93,7 @@ public class Socks5Client { // initialize socket Socket socket = new Socket(); - SocketAddress socketAddress = new InetSocketAddress(streamHost.getAddress(), + SocketAddress socketAddress = new InetSocketAddress(streamHost.getAddress().asInetAddress(), streamHost.getPort()); socket.connect(socketAddress); 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 ed5d59597..4ce9f6c9a 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 @@ -20,6 +20,7 @@ import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.InetAddress; +import java.net.InterfaceAddress; import java.net.NetworkInterface; import java.net.ServerSocket; import java.net.Socket; @@ -52,7 +53,7 @@ import org.jivesoftware.smack.util.CloseableUtil; *

* If your application is running on a machine with multiple network interfaces or if you want to * provide your public address in case you are behind a NAT router, invoke - * {@link #addLocalAddress(String)} or {@link #replaceLocalAddresses(Collection)} to modify the list of + * {@link #addLocalAddress(InetAddress)} or {@link #replaceLocalAddresses(Collection)} to modify the list of * local network addresses used for outgoing SOCKS5 Bytestream requests. *

* The local SOCKS5 proxy server refuses all connections except the ones that are explicitly allowed @@ -81,11 +82,17 @@ public final class Socks5Proxy { private static boolean localSocks5ProxyEnabled = true; + /** + * The default port of the local Socks5 Proxy. If this value is negative, the next ports will be tried + * until a unused is found. + */ + private static int DEFAULT_LOCAL_SOCKS5_PROXY_PORT = -7777; + /** * The port of the local Socks5 Proxy. If this value is negative, the next ports will be tried * until a unused is found. */ - private static int localSocks5ProxyPort = -7777; + private int localSocks5ProxyPort = -7777; /* reusable implementation of a SOCKS5 proxy server process */ private Socks5ServerProcess serverProcess; @@ -102,7 +109,7 @@ public final class Socks5Proxy { /* list of digests connections should be stored */ private final List allowedConnections = Collections.synchronizedList(new LinkedList()); - private final Set localAddresses = new LinkedHashSet<>(4); + private final Set localAddresses = new LinkedHashSet<>(4); /** * Private constructor. @@ -116,21 +123,17 @@ public final class Socks5Proxy { } catch (SocketException e) { throw new IllegalStateException(e); } - Set localHostAddresses = new HashSet<>(); + Set localAddresses = new HashSet<>(); for (NetworkInterface networkInterface : Collections.list(networkInterfaces)) { - // We can't use NetworkInterface.getInterfaceAddresses here, which - // would return a List instead the deprecated Enumeration, because - // it's Android API 9 and Smack currently uses 8. Change that when - // we raise Smack's minimum Android API. - Enumeration inetAddresses = networkInterface.getInetAddresses(); - for (InetAddress address : Collections.list(inetAddresses)) { - localHostAddresses.add(address.getHostAddress()); + List interfaceAddresses = networkInterface.getInterfaceAddresses(); + for (InterfaceAddress interfaceAddress : interfaceAddresses) { + localAddresses.add(interfaceAddress.getAddress()); } } - if (localHostAddresses.isEmpty()) { - throw new IllegalStateException("Could not determine any local host address"); + if (localAddresses.isEmpty()) { + throw new IllegalStateException("Could not determine any local internet address"); } - replaceLocalAddresses(localHostAddresses); + replaceLocalAddresses(localAddresses); } /** @@ -151,12 +154,27 @@ public final class Socks5Proxy { Socks5Proxy.localSocks5ProxyEnabled = localSocks5ProxyEnabled; } + private static void checkLocalSocks5ProxyPortArgument(int port) { + if (Math.abs(port) > 65535) { + throw new IllegalArgumentException("Local SOCKS5 proxy port must be within (-65535,65535)"); + } + } + + public static int getDefaultLocalSocks5ProxyPort() { + return DEFAULT_LOCAL_SOCKS5_PROXY_PORT; + } + + public static void setDefaultLocalSocsk5ProxyPort(int defaultLocalSocks5ProxyPort) { + checkLocalSocks5ProxyPortArgument(defaultLocalSocks5ProxyPort); + DEFAULT_LOCAL_SOCKS5_PROXY_PORT = defaultLocalSocks5ProxyPort; + } + /** * Return the port of the local Socks5 proxy. Default is 7777. * * @return the port of the local Socks5 proxy */ - public static int getLocalSocks5ProxyPort() { + public int getLocalSocks5ProxyPort() { return localSocks5ProxyPort; } @@ -166,11 +184,9 @@ public final class Socks5Proxy { * * @param localSocks5ProxyPort the port of the local Socks5 proxy to set */ - public static void setLocalSocks5ProxyPort(int localSocks5ProxyPort) { - if (Math.abs(localSocks5ProxyPort) > 65535) { - throw new IllegalArgumentException("localSocks5ProxyPort must be within (-65535,65535)"); - } - Socks5Proxy.localSocks5ProxyPort = localSocks5ProxyPort; + public void setLocalSocks5ProxyPort(int localSocks5ProxyPort) { + checkLocalSocks5ProxyPortArgument(localSocks5ProxyPort); + this.localSocks5ProxyPort = localSocks5ProxyPort; } /** @@ -269,7 +285,7 @@ public final class Socks5Proxy { * * @param address the local network address to add */ - public void addLocalAddress(String address) { + public void addLocalAddress(InetAddress address) { if (address == null) { return; } @@ -285,7 +301,7 @@ public final class Socks5Proxy { * @param address the local network address to remove * @return true if the address was removed. */ - public boolean removeLocalAddress(String address) { + public boolean removeLocalAddress(InetAddress address) { synchronized (localAddresses) { return localAddresses.remove(address); } @@ -297,7 +313,7 @@ public final class Socks5Proxy { * * @return set of the local network addresses */ - public List getLocalAddresses() { + public List getLocalAddresses() { synchronized (localAddresses) { return new LinkedList<>(localAddresses); } @@ -313,7 +329,7 @@ public final class Socks5Proxy { * * @param addresses the new list of local network addresses */ - public void replaceLocalAddresses(Collection addresses) { + public void replaceLocalAddresses(Collection addresses) { if (addresses == null) { throw new IllegalArgumentException("list must not be null"); } @@ -482,12 +498,12 @@ public final class Socks5Proxy { throw new SmackException.SmackMessageException("Connection is not allowed"); } + // Store the connection before we send the return status. + Socks5Proxy.this.connectionMap.put(responseDigest, socket); + connectionRequest[1] = (byte) 0x00; // set return status to 0 (success) out.write(connectionRequest); out.flush(); - - // store connection - Socks5Proxy.this.connectionMap.put(responseDigest, socket); } } diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/packet/Bytestream.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/packet/Bytestream.java index 8e31b4f18..e51b10061 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/packet/Bytestream.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/bytestreams/socks5/packet/Bytestream.java @@ -16,14 +16,15 @@ */ package org.jivesoftware.smackx.bytestreams.socks5.packet; +import java.net.InetAddress; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.jivesoftware.smack.packet.IQ; import org.jivesoftware.smack.packet.NamedElement; +import org.jivesoftware.smack.util.InternetAddress; import org.jivesoftware.smack.util.Objects; -import org.jivesoftware.smack.util.StringUtils; import org.jivesoftware.smack.util.XmlStringBuilder; import org.jxmpp.jid.Jid; @@ -117,7 +118,7 @@ public class Bytestream extends IQ { * @param address The internet address of the stream host. * @return The added stream host. */ - public StreamHost addStreamHost(final Jid JID, final String address) { + public StreamHost addStreamHost(final Jid JID, String address) { return addStreamHost(JID, address, 0); } @@ -129,7 +130,7 @@ public class Bytestream extends IQ { * @param port The port on which the remote host is seeking connections. * @return The added stream host. */ - public StreamHost addStreamHost(final Jid JID, final String address, final int port) { + public StreamHost addStreamHost(final Jid JID, String address, final int port) { StreamHost host = new StreamHost(JID, address, port); addStreamHost(host); @@ -271,7 +272,7 @@ public class Bytestream extends IQ { private final Jid jid; - private final String address; + private final InternetAddress address; private final int port; @@ -287,8 +288,23 @@ public class Bytestream extends IQ { * @param port port of the stream host. */ public StreamHost(final Jid jid, final String address, int port) { + this(jid, InternetAddress.from(address), port); + } + + public StreamHost(Jid jid, InetAddress address, int port) { + this(jid, InternetAddress.from(address), port); + } + + /** + * Stream Host constructor. + * + * @param jid The JID of the stream host. + * @param address The internet address of the stream host. + * @param port port of the stream host. + */ + public StreamHost(Jid jid, InternetAddress address, int port) { this.jid = Objects.requireNonNull(jid, "StreamHost JID must not be null"); - this.address = StringUtils.requireNotNullNorEmpty(address, "StreamHost address must not be null"); + this.address = Objects.requireNonNull(address); this.port = port; } @@ -306,7 +322,7 @@ public class Bytestream extends IQ { * * @return Returns the internet address of the stream host. */ - public String getAddress() { + public InternetAddress getAddress() { return address; } @@ -328,7 +344,7 @@ public class Bytestream extends IQ { public XmlStringBuilder toXML(org.jivesoftware.smack.packet.XmlEnvironment enclosingNamespace) { XmlStringBuilder xml = new XmlStringBuilder(this); xml.attribute("jid", getJID()); - xml.attribute("host", getAddress()); + xml.attribute("host", address); if (getPort() != 0) { xml.attribute("port", Integer.toString(getPort())); } else { diff --git a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/transports/jingle_s5b/JingleS5BTransportSession.java b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/transports/jingle_s5b/JingleS5BTransportSession.java index 3f907b0cc..df1641df7 100644 --- a/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/transports/jingle_s5b/JingleS5BTransportSession.java +++ b/smack-extensions/src/main/java/org/jivesoftware/smackx/jingle/transports/jingle_s5b/JingleS5BTransportSession.java @@ -17,6 +17,7 @@ package org.jivesoftware.smackx.jingle.transports.jingle_s5b; import java.io.IOException; +import java.net.InetAddress; import java.net.Socket; import java.util.Collections; import java.util.List; @@ -162,7 +163,7 @@ public class JingleS5BTransportSession extends JingleTransportSession byteStreamRequest = ArgumentCaptor.forClass(BytestreamRequest.class); - // assert all requests listener is called - byteStreamRequest = ArgumentCaptor.forClass(BytestreamRequest.class); verify(allRequestsListener, timeout(TIMEOUT)).incomingBytestreamRequest(byteStreamRequest.capture()); // assert user request listener is not called verify(userRequestsListener, never()).incomingBytestreamRequest(byteStreamRequest.capture()); - - } /** 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 83fe32f36..33bb52231 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 @@ -29,7 +29,9 @@ import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ConnectException; +import java.net.InetAddress; import java.net.ServerSocket; +import java.util.List; import java.util.concurrent.TimeoutException; import org.jivesoftware.smack.SmackException; @@ -776,16 +778,24 @@ public class Socks5ByteStreamManagerTest { streamHostUsedPacket.setSessionID(sessionID); streamHostUsedPacket.setUsedHost(initiatorJID); // local proxy used + final String secondStreamHostIp = "192.0.0.1"; // 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); + + List streamHosts = request.getStreamHosts(); + + StreamHost streamHost1 = streamHosts.get(0); assertEquals(response.getUsedHost().getJID(), streamHost1.getJID()); - StreamHost streamHost2 = request.getStreamHosts().get(request.getStreamHosts().size() - 1); + + // Get the last stream host. Note that there may be multiple, but since this unit test added + // secondStreamHostIp as last, it should also be the last entry since the API contract assures that + // the order is preserved. + StreamHost streamHost2 = streamHosts.get(streamHosts.size() - 1); assertEquals(response.getUsedHost().getJID(), streamHost2.getJID()); - assertEquals("localAddress", streamHost2.getAddress()); + assertEquals(secondStreamHostIp, streamHost2.getAddress().toString()); } }, Verification.correspondingSenderReceiver, Verification.requestTypeSET); @@ -798,10 +808,10 @@ public class Socks5ByteStreamManagerTest { socks5Proxy.getLocalAddresses().get(0), socks5Proxy.getPort()); Socks5Client socks5Client = new Socks5Client(streamHost, digest); - InputStream inputStream = socks5Client.getSocket(2000).getInputStream(); + InputStream inputStream = socks5Client.getSocket(10000).getInputStream(); // add another network address before establishing SOCKS5 Bytestream - socks5Proxy.addLocalAddress("localAddress"); + socks5Proxy.addLocalAddress(InetAddress.getByName(secondStreamHostIp)); // finally call the method that should be tested OutputStream outputStream = byteStreamManager.establishSession(targetJID, sessionID).getOutputStream(); @@ -816,9 +826,9 @@ public class Socks5ByteStreamManagerTest { assertArrayEquals(data, result); protocol.verifyAll(); - } finally { - socks5Proxy.stop(); - } + } finally { + socks5Proxy.stop(); + } } /** diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ProxyTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ProxyTest.java index 32c531f88..54ead2f51 100644 --- a/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ProxyTest.java +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ProxyTest.java @@ -35,7 +35,6 @@ import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; -import org.junit.After; import org.junit.Test; /** @@ -48,12 +47,10 @@ public class Socks5ProxyTest { private static final String loopbackAddress = InetAddress.getLoopbackAddress().getHostAddress(); /** - * The SOCKS5 proxy should be a singleton used by all XMPP connections. + * The SOCKS5 proxy should be a quasi singleton used by all XMPP connections. */ @Test - public void shouldBeASingleton() { - Socks5Proxy.setLocalSocks5ProxyEnabled(false); - + public void shouldBeAQuasiSingleton() { Socks5Proxy proxy1 = Socks5Proxy.getSocks5Proxy(); Socks5Proxy proxy2 = Socks5Proxy.getSocks5Proxy(); @@ -62,16 +59,6 @@ public class Socks5ProxyTest { assertSame(proxy1, proxy2); } - /** - * The SOCKS5 proxy should not be started if disabled by configuration. - */ - @Test - public void shouldNotBeRunningIfDisabled() { - Socks5Proxy.setLocalSocks5ProxyEnabled(false); - Socks5Proxy proxy = Socks5Proxy.getSocks5Proxy(); - assertFalse(proxy.isRunning()); - } - /** * The SOCKS5 proxy should use a free port above the one configured. * @@ -79,44 +66,45 @@ public class Socks5ProxyTest { */ @Test public void shouldUseFreePortOnNegativeValues() throws Exception { - Socks5Proxy.setLocalSocks5ProxyEnabled(false); - Socks5Proxy proxy = Socks5Proxy.getSocks5Proxy(); + Socks5Proxy proxy = new Socks5Proxy(); assertFalse(proxy.isRunning()); - ServerSocket serverSocket = new ServerSocket(0); - Socks5Proxy.setLocalSocks5ProxyPort(-serverSocket.getLocalPort()); + try (ServerSocket serverSocket = new ServerSocket(0)) { + proxy.setLocalSocks5ProxyPort(-serverSocket.getLocalPort()); - proxy.start(); + proxy.start(); - assertTrue(proxy.isRunning()); - - serverSocket.close(); - - assertTrue(proxy.getPort() > serverSocket.getLocalPort()); + assertTrue(proxy.isRunning()); + assertTrue(proxy.getPort() > serverSocket.getLocalPort()); + } finally { + proxy.stop(); + } } /** * When inserting new network addresses to the proxy the order should remain in the order they * were inserted. + * + * @throws UnknownHostException */ @Test - public void shouldPreserveAddressOrderOnInsertions() { + public void shouldPreserveAddressOrderOnInsertions() throws UnknownHostException { Socks5Proxy proxy = Socks5Proxy.getSocks5Proxy(); - LinkedHashSet addresses = new LinkedHashSet<>(proxy.getLocalAddresses()); + LinkedHashSet addresses = new LinkedHashSet<>(proxy.getLocalAddresses()); for (int i = 1 ; i <= 3; i++) { - addresses.add(Integer.toString(i)); + addresses.add(InetAddress.getByName(Integer.toString(i))); } - for (String address : addresses) { + for (InetAddress address : addresses) { proxy.addLocalAddress(address); } - List localAddresses = proxy.getLocalAddresses(); + List localAddresses = proxy.getLocalAddresses(); - Iterator iterator = addresses.iterator(); + Iterator iterator = addresses.iterator(); for (int i = 0; i < addresses.size(); i++) { assertEquals(iterator.next(), localAddresses.get(i)); } @@ -125,44 +113,25 @@ public class Socks5ProxyTest { /** * When replacing network addresses of the proxy the order should remain in the order if the * given list. + * + * @throws UnknownHostException */ @Test - public void shouldPreserveAddressOrderOnReplace() { + public void shouldPreserveAddressOrderOnReplace() throws UnknownHostException { Socks5Proxy proxy = Socks5Proxy.getSocks5Proxy(); - List addresses = new ArrayList<>(proxy.getLocalAddresses()); - addresses.add("1"); - addresses.add("2"); - addresses.add("3"); + List addresses = new ArrayList<>(proxy.getLocalAddresses()); + addresses.add(InetAddress.getByName("1")); + addresses.add(InetAddress.getByName("2")); + addresses.add(InetAddress.getByName("3")); proxy.replaceLocalAddresses(addresses); - List localAddresses = proxy.getLocalAddresses(); + List localAddresses = proxy.getLocalAddresses(); for (int i = 0; i < addresses.size(); i++) { assertEquals(addresses.get(i), localAddresses.get(i)); } } - /** - * Inserting the same address multiple times should not cause the proxy to return this address - * multiple times. - */ - @Test - public void shouldNotReturnMultipleSameAddress() { - Socks5Proxy proxy = Socks5Proxy.getSocks5Proxy(); - - proxy.addLocalAddress("same"); - proxy.addLocalAddress("same"); - proxy.addLocalAddress("same"); - - int sameCount = 0; - for (String localAddress : proxy.getLocalAddresses()) { - if ("same".equals(localAddress)) { - sameCount++; - } - } - assertEquals(1, sameCount); - } - /** * If the SOCKS5 proxy accepts a connection that is not a SOCKS5 connection it should close the * corresponding socket. @@ -171,27 +140,24 @@ public class Socks5ProxyTest { */ @Test public void shouldCloseSocketIfNoSocks5Request() throws Exception { - Socks5Proxy.setLocalSocks5ProxyPort(7890); - Socks5Proxy proxy = Socks5Proxy.getSocks5Proxy(); + Socks5Proxy proxy = new Socks5Proxy(); proxy.start(); - @SuppressWarnings("resource") - Socket socket = new Socket(loopbackAddress, proxy.getPort()); + try (Socket socket = new Socket(loopbackAddress, proxy.getPort())) { + OutputStream out = socket.getOutputStream(); + out.write(new byte[] { 1, 2, 3 }); - OutputStream out = socket.getOutputStream(); - out.write(new byte[] { 1, 2, 3 }); + int res; + try { + res = socket.getInputStream().read(); + } catch (SocketException e) { + res = -1; + } - int res; - try { - res = socket.getInputStream().read(); - } catch (SocketException e) { - res = -1; + assertEquals(-1, res); + } finally { + proxy.stop(); } - - assertEquals(-1, res); - - proxy.stop(); - } /** @@ -202,27 +168,24 @@ public class Socks5ProxyTest { */ @Test public void shouldRespondWithErrorIfNoSupportedAuthenticationMethod() throws Exception { - Socks5Proxy.setLocalSocks5ProxyPort(7890); - Socks5Proxy proxy = Socks5Proxy.getSocks5Proxy(); + Socks5Proxy proxy = new Socks5Proxy(); proxy.start(); - @SuppressWarnings("resource") - Socket socket = new Socket(loopbackAddress, proxy.getPort()); + try (Socket socket = new Socket(loopbackAddress, proxy.getPort())) { + OutputStream out = socket.getOutputStream(); - OutputStream out = socket.getOutputStream(); + // request username/password-authentication + out.write(new byte[] { (byte) 0x05, (byte) 0x01, (byte) 0x02 }); - // request username/password-authentication - out.write(new byte[] { (byte) 0x05, (byte) 0x01, (byte) 0x02 }); + InputStream in = socket.getInputStream(); - InputStream in = socket.getInputStream(); - - assertEquals((byte) 0x05, (byte) in.read()); - assertEquals((byte) 0xFF, (byte) in.read()); - - assertEquals(-1, in.read()); - - proxy.stop(); + assertEquals((byte) 0x05, (byte) in.read()); + assertEquals((byte) 0xFF, (byte) in.read()); + assertEquals(-1, in.read()); + } finally { + proxy.stop(); + } } /** @@ -233,39 +196,36 @@ public class Socks5ProxyTest { */ @Test public void shouldRespondWithErrorIfConnectionIsNotAllowed() throws Exception { - Socks5Proxy.setLocalSocks5ProxyPort(7890); - Socks5Proxy proxy = Socks5Proxy.getSocks5Proxy(); + Socks5Proxy proxy = new Socks5Proxy(); proxy.start(); - @SuppressWarnings("resource") - Socket socket = new Socket(loopbackAddress, proxy.getPort()); + try (Socket socket = new Socket(loopbackAddress, proxy.getPort())) { + OutputStream out = socket.getOutputStream(); + out.write(new byte[] { (byte) 0x05, (byte) 0x01, (byte) 0x00 }); - OutputStream out = socket.getOutputStream(); - out.write(new byte[] { (byte) 0x05, (byte) 0x01, (byte) 0x00 }); + InputStream in = socket.getInputStream(); - InputStream in = socket.getInputStream(); + assertEquals((byte) 0x05, (byte) in.read()); + assertEquals((byte) 0x00, (byte) in.read()); - assertEquals((byte) 0x05, (byte) in.read()); - assertEquals((byte) 0x00, (byte) in.read()); + // send valid SOCKS5 message + out.write(new byte[] { (byte) 0x05, (byte) 0x00, (byte) 0x00, (byte) 0x03, (byte) 0x01, (byte) 0xAA, + (byte) 0x00, (byte) 0x00 }); - // send valid SOCKS5 message - out.write(new byte[] { (byte) 0x05, (byte) 0x00, (byte) 0x00, (byte) 0x03, (byte) 0x01, - (byte) 0xAA, (byte) 0x00, (byte) 0x00 }); - - // verify error message - assertEquals((byte) 0x05, (byte) in.read()); - assertFalse((byte) 0x00 == (byte) in.read()); // something other than 0 == success - assertEquals((byte) 0x00, (byte) in.read()); - assertEquals((byte) 0x03, (byte) in.read()); - assertEquals((byte) 0x01, (byte) in.read()); - assertEquals((byte) 0xAA, (byte) in.read()); - assertEquals((byte) 0x00, (byte) in.read()); - assertEquals((byte) 0x00, (byte) in.read()); - - assertEquals(-1, in.read()); - - proxy.stop(); + // verify error message + assertEquals((byte) 0x05, (byte) in.read()); + assertFalse((byte) 0x00 == (byte) in.read()); // something other than 0 == success + assertEquals((byte) 0x00, (byte) in.read()); + assertEquals((byte) 0x03, (byte) in.read()); + assertEquals((byte) 0x01, (byte) in.read()); + assertEquals((byte) 0xAA, (byte) in.read()); + assertEquals((byte) 0x00, (byte) in.read()); + assertEquals((byte) 0x00, (byte) in.read()); + assertEquals(-1, in.read()); + } finally { + proxy.stop(); + } } /** @@ -275,85 +235,63 @@ public class Socks5ProxyTest { */ @Test public void shouldSuccessfullyEstablishConnection() throws Exception { - Socks5Proxy.setLocalSocks5ProxyPort(7890); - Socks5Proxy proxy = Socks5Proxy.getSocks5Proxy(); + Socks5Proxy proxy = new Socks5Proxy(); proxy.start(); - assertTrue(proxy.isRunning()); - String digest = new String(new byte[] { (byte) 0xAA }, StandardCharsets.UTF_8); - - // add digest to allow connection - proxy.addTransfer(digest); - - @SuppressWarnings("resource") - Socket socket = new Socket(loopbackAddress, proxy.getPort()); - - OutputStream out = socket.getOutputStream(); - out.write(new byte[] { (byte) 0x05, (byte) 0x01, (byte) 0x00 }); - - InputStream in = socket.getInputStream(); - - assertEquals((byte) 0x05, (byte) in.read()); - assertEquals((byte) 0x00, (byte) in.read()); - - // send valid SOCKS5 message - out.write(new byte[] { (byte) 0x05, (byte) 0x00, (byte) 0x00, (byte) 0x03, (byte) 0x01, - (byte) 0xAA, (byte) 0x00, (byte) 0x00 }); - - // verify response - assertEquals((byte) 0x05, (byte) in.read()); - assertEquals((byte) 0x00, (byte) in.read()); // success - assertEquals((byte) 0x00, (byte) in.read()); - assertEquals((byte) 0x03, (byte) in.read()); - assertEquals((byte) 0x01, (byte) in.read()); - assertEquals((byte) 0xAA, (byte) in.read()); - assertEquals((byte) 0x00, (byte) in.read()); - assertEquals((byte) 0x00, (byte) in.read()); - - Thread.sleep(200); - - Socket remoteSocket = proxy.getSocket(digest); - - // remove digest - proxy.removeTransfer(digest); - - // test stream - OutputStream remoteOut = remoteSocket.getOutputStream(); - byte[] data = new byte[] { 1, 2, 3, 4, 5 }; - remoteOut.write(data); - remoteOut.flush(); - - for (int i = 0; i < data.length; i++) { - assertEquals(data[i], in.read()); - } - - remoteSocket.close(); - - assertEquals(-1, in.read()); - - proxy.stop(); - - } - - /** - * Reset SOCKS5 proxy settings. - */ - @After - public void cleanup() { - Socks5Proxy.setLocalSocks5ProxyEnabled(true); - Socks5Proxy.setLocalSocks5ProxyPort(7777); - Socks5Proxy socks5Proxy = Socks5Proxy.getSocks5Proxy(); try { - String address = InetAddress.getLocalHost().getHostAddress(); - List addresses = new ArrayList<>(); - addresses.add(address); - socks5Proxy.replaceLocalAddresses(addresses); - } - catch (UnknownHostException e) { - // ignore - } + assertTrue(proxy.isRunning()); + String digest = new String(new byte[] { (byte) 0xAA }, StandardCharsets.UTF_8); - socks5Proxy.stop(); + // add digest to allow connection + proxy.addTransfer(digest); + + @SuppressWarnings("resource") + Socket socket = new Socket(loopbackAddress, proxy.getPort()); + + OutputStream out = socket.getOutputStream(); + out.write(new byte[] { (byte) 0x05, (byte) 0x01, (byte) 0x00 }); + + InputStream in = socket.getInputStream(); + + assertEquals((byte) 0x05, (byte) in.read()); + assertEquals((byte) 0x00, (byte) in.read()); + + // send valid SOCKS5 message + out.write(new byte[] { (byte) 0x05, (byte) 0x00, (byte) 0x00, (byte) 0x03, (byte) 0x01, (byte) 0xAA, + (byte) 0x00, (byte) 0x00 }); + + // verify response + assertEquals((byte) 0x05, (byte) in.read()); + assertEquals((byte) 0x00, (byte) in.read()); // success + assertEquals((byte) 0x00, (byte) in.read()); + assertEquals((byte) 0x03, (byte) in.read()); + assertEquals((byte) 0x01, (byte) in.read()); + assertEquals((byte) 0xAA, (byte) in.read()); + assertEquals((byte) 0x00, (byte) in.read()); + assertEquals((byte) 0x00, (byte) in.read()); + + Socket remoteSocket = proxy.getSocket(digest); + + try { + // remove digest + proxy.removeTransfer(digest); + + // test stream + OutputStream remoteOut = remoteSocket.getOutputStream(); + byte[] data = new byte[] { 1, 2, 3, 4, 5 }; + remoteOut.write(data); + remoteOut.flush(); + + for (int i = 0; i < data.length; i++) { + assertEquals(data[i], in.read()); + } + } finally { + remoteSocket.close(); + } + + assertEquals(-1, in.read()); + } finally { + proxy.stop(); + } } - } diff --git a/smack-extensions/src/test/java/org/jivesoftware/smackx/jingle/transports/jingle_s5b/JingleS5BTransportTest.java b/smack-extensions/src/test/java/org/jivesoftware/smackx/jingle/transports/jingle_s5b/JingleS5BTransportTest.java index c049d27cc..86eb88041 100644 --- a/smack-extensions/src/test/java/org/jivesoftware/smackx/jingle/transports/jingle_s5b/JingleS5BTransportTest.java +++ b/smack-extensions/src/test/java/org/jivesoftware/smackx/jingle/transports/jingle_s5b/JingleS5BTransportTest.java @@ -22,6 +22,8 @@ import static junit.framework.TestCase.assertNotNull; import static junit.framework.TestCase.assertNull; import static junit.framework.TestCase.assertTrue; +import java.net.UnknownHostException; + import org.jivesoftware.smack.test.util.SmackTestSuite; import org.jivesoftware.smack.test.util.TestUtils; @@ -89,7 +91,7 @@ public class JingleS5BTransportTest extends SmackTestSuite { assertNotNull(candidate1.getStreamHost()); assertEquals(JingleS5BTransportCandidate.Type.direct.getWeight(), candidate1.getType().getWeight()); assertEquals("hft54dqy", candidate1.getCandidateId()); - assertEquals("192.168.4.1", candidate1.getHost()); + assertEquals("192.168.4.1", candidate1.getHost().toString()); assertEquals(JidCreate.from("romeo@montague.lit/orchard"), candidate1.getJid()); assertEquals(5086, candidate1.getPort()); assertEquals(8257636, candidate1.getPriority()); @@ -98,7 +100,7 @@ public class JingleS5BTransportTest extends SmackTestSuite { JingleS5BTransportCandidate candidate2 = (JingleS5BTransportCandidate) transport.getCandidates().get(1); assertEquals("hutr46fe", candidate2.getCandidateId()); - assertEquals("24.24.24.1", candidate2.getHost()); + assertEquals("24.24.24.1", candidate2.getHost().toString()); assertEquals(JidCreate.from("romeo@montague.lit/orchard"), candidate2.getJid()); assertEquals(5087, candidate2.getPort()); assertEquals(8258636, candidate2.getPriority()); @@ -107,7 +109,7 @@ public class JingleS5BTransportTest extends SmackTestSuite { JingleS5BTransportCandidate candidate3 = (JingleS5BTransportCandidate) transport.getCandidates().get(2); assertEquals("xmdh4b7i", candidate3.getCandidateId()); - assertEquals("123.456.7.8", candidate3.getHost()); + assertEquals("123.456.7.8", candidate3.getHost().toString()); assertEquals(JidCreate.domainBareFrom("streamer.shakespeare.lit"), candidate3.getJid()); assertEquals(7625, candidate3.getPort()); assertEquals(7878787, candidate3.getPriority()); @@ -187,15 +189,15 @@ public class JingleS5BTransportTest extends SmackTestSuite { } @Test(expected = IllegalArgumentException.class) - public void transportCandidateIllegalPriorityTest() throws XmppStringprepException { + public void transportCandidateIllegalPriorityTest() throws XmppStringprepException, UnknownHostException { FullJid jid = JidCreate.fullFrom("test@test.test/test"); @SuppressWarnings("unused") JingleS5BTransportCandidate candidate = new JingleS5BTransportCandidate( - "cid", "host", jid, 5555, -30, JingleS5BTransportCandidate.Type.proxy); + "cid", "localhost", jid, 5555, -30, JingleS5BTransportCandidate.Type.proxy); } @Test(expected = IllegalArgumentException.class) - public void transportCandidateIllegalPortTest() throws XmppStringprepException { + public void transportCandidateIllegalPortTest() throws XmppStringprepException, UnknownHostException { FullJid jid = JidCreate.fullFrom("test@test.test/test"); @SuppressWarnings("unused") JingleS5BTransportCandidate candidate = new JingleS5BTransportCandidate( @@ -203,9 +205,9 @@ public class JingleS5BTransportTest extends SmackTestSuite { } @Test - public void candidateFromStreamHostTest() throws XmppStringprepException { + public void candidateFromStreamHostTest() throws XmppStringprepException, UnknownHostException { FullJid jid = JidCreate.fullFrom("test@test.test/test"); - String host = "host.address"; + String host = "localhost"; int port = 1234; Bytestream.StreamHost streamHost = new Bytestream.StreamHost(jid, host, port); @@ -213,7 +215,7 @@ public class JingleS5BTransportTest extends SmackTestSuite { assertEquals(2000, candidate.getPriority()); assertEquals(jid, candidate.getJid()); - assertEquals(host, candidate.getHost()); + assertEquals(host, candidate.getHost().toString()); assertEquals(port, candidate.getPort()); assertEquals(streamHost.toXML().toString(), candidate.getStreamHost().toXML().toString());