1
0
Fork 0
mirror of https://github.com/vanitasvitae/Smack.git synced 2024-06-26 05:14:49 +02:00
Smack/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ByteStreamManagerTest.java
Florian Schmaus 9e797c1b17 Enable PacketExtensions for IQs
This is actually only part one, i.e. with this commit if the user adds a
PacketExtension to an IQ it will be included in IQ.toXml(). Which was
previously only the case if the IQ subclass explicitly included packet
extensions.

The second part of the change is to change the IQ provider, so that
packet extensions are automatically parsed.

Cases where PacketExtensions are used for Message and IQ are slightly
changed. The IQ sublcass now only has a field with this
PacketExtension (see for example
bytestreams.ibb.packet.DataPacketExtension).

Also changed hoxt API: Removed unnecessary indirection and made the
API more Smack idiomatic.
2014-11-10 11:43:18 +01:00

1094 lines
44 KiB
Java

/**
*
* Copyright the original author or authors
*
* 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.smackx.bytestreams.socks5;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ConnectException;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.SmackException.FeatureNotSupportedException;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
import org.jivesoftware.smack.packet.ErrorIQ;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.packet.XMPPError;
import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream;
import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream.StreamHost;
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
import org.jivesoftware.smackx.disco.packet.DiscoverInfo;
import org.jivesoftware.smackx.disco.packet.DiscoverItems;
import org.jivesoftware.smackx.disco.packet.DiscoverInfo.Identity;
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;
/**
* Test for Socks5BytestreamManager.
*
* @author Henning Staib
*/
public class Socks5ByteStreamManagerTest {
// settings
String initiatorJID = "initiator@xmpp-server/Smack";
String targetJID = "target@xmpp-server/Smack";
String xmppServer = "xmpp-server";
String proxyJID = "proxy.xmpp-server";
String proxyAddress = "127.0.0.1";
String sessionID = "session_id";
// protocol verifier
Protocol protocol;
// mocked XMPP connection
XMPPConnection connection;
/**
* Initialize fields used in the tests.
* @throws XMPPException
* @throws SmackException
*/
@Before
public void setup() throws XMPPException, SmackException {
// build protocol verifier
protocol = new Protocol();
// create mocked XMPP connection
connection = ConnectionUtils.createMockedConnection(protocol, initiatorJID, xmppServer);
}
/**
* Test that {@link Socks5BytestreamManager#getBytestreamManager(XMPPConnection)} returns one
* bytestream manager for every connection
*/
@Test
public void shouldHaveOneManagerForEveryConnection() {
// mock two connections
XMPPConnection connection1 = mock(XMPPConnection.class);
XMPPConnection connection2 = mock(XMPPConnection.class);
/*
* create service discovery managers for the connections because the
* ConnectionCreationListener is not called when creating mocked connections
*/
ServiceDiscoveryManager.getInstanceFor(connection1);
ServiceDiscoveryManager.getInstanceFor(connection2);
// get bytestream manager for the first connection twice
Socks5BytestreamManager conn1ByteStreamManager1 = Socks5BytestreamManager.getBytestreamManager(connection1);
Socks5BytestreamManager conn1ByteStreamManager2 = Socks5BytestreamManager.getBytestreamManager(connection1);
// get bytestream manager for second connection
Socks5BytestreamManager conn2ByteStreamManager1 = Socks5BytestreamManager.getBytestreamManager(connection2);
// 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.
*/
@Test
public void shouldDisableService() {
Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection);
ServiceDiscoveryManager discoveryManager = ServiceDiscoveryManager.getInstanceFor(connection);
assertTrue(discoveryManager.includesFeature(Bytestream.NAMESPACE));
byteStreamManager.disableService();
assertFalse(discoveryManager.includesFeature(Bytestream.NAMESPACE));
}
/**
* Invoking {@link Socks5BytestreamManager#establishSession(String)} should throw an exception
* if the given target does not support SOCKS5 Bytestream.
* @throws XMPPException
*/
@Test
public void shouldFailIfTargetDoesNotSupportSocks5() throws XMPPException {
Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection);
try {
// build empty discover info as reply if targets features are queried
DiscoverInfo discoverInfo = new DiscoverInfo();
protocol.addResponse(discoverInfo);
// start SOCKS5 Bytestream
byteStreamManager.establishSession(targetJID);
fail("exception should be thrown");
}
catch (FeatureNotSupportedException e) {
assertTrue(e.getFeature().equals("SOCKS5 Bytestream"));
assertTrue(e.getJid().equals(targetJID));
} catch(Exception e) {
fail(e.getMessage());
}
}
/**
* Invoking {@link Socks5BytestreamManager#establishSession(String, String)} should fail if XMPP
* server doesn't return any proxies.
*/
@Test
public void shouldFailIfNoSocks5ProxyFound1() {
// disable clients local SOCKS5 proxy
Socks5Proxy.setLocalSocks5ProxyEnabled(false);
// get Socks5ByteStreamManager for connection
Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection);
/**
* 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);
// return that SOCKS5 is supported if target is queried
protocol.addResponse(discoverInfo, Verification.correspondingSenderReceiver,
Verification.requestTypeGET);
// build discover items with no proxy items
DiscoverItems discoverItems = Socks5PacketUtils.createDiscoverItems(xmppServer,
initiatorJID);
// return the item with no proxy if XMPP server is queried
protocol.addResponse(discoverItems, Verification.correspondingSenderReceiver,
Verification.requestTypeGET);
try {
// start SOCKS5 Bytestream
byteStreamManager.establishSession(targetJID, sessionID);
fail("exception should be thrown");
}
catch (SmackException e) {
protocol.verifyAll();
assertTrue(e.getMessage().contains("no SOCKS5 proxies available"));
}
catch (Exception e) {
fail(e.getMessage());
}
}
/**
* Invoking {@link Socks5BytestreamManager#establishSession(String, String)} should fail if no
* proxy is a SOCKS5 proxy.
*/
@Test
public void shouldFailIfNoSocks5ProxyFound2() {
// disable clients local SOCKS5 proxy
Socks5Proxy.setLocalSocks5ProxyEnabled(false);
// get Socks5ByteStreamManager for connection
Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection);
/**
* 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);
// return that SOCKS5 is supported if target is queried
protocol.addResponse(discoverInfo, Verification.correspondingSenderReceiver,
Verification.requestTypeGET);
// build discover items containing a proxy item
DiscoverItems discoverItems = Socks5PacketUtils.createDiscoverItems(xmppServer,
initiatorJID);
Item item = new Item(proxyJID);
discoverItems.addItem(item);
// return the proxy item if XMPP server is queried
protocol.addResponse(discoverItems, Verification.correspondingSenderReceiver,
Verification.requestTypeGET);
// build discover info for proxy containing information about NOT being a Socks5
// proxy
DiscoverInfo proxyInfo = Socks5PacketUtils.createDiscoverInfo(proxyJID, initiatorJID);
Identity identity = new Identity("noproxy", proxyJID, "bytestreams");
proxyInfo.addIdentity(identity);
// return the proxy identity if proxy is queried
protocol.addResponse(proxyInfo, Verification.correspondingSenderReceiver,
Verification.requestTypeGET);
try {
// start SOCKS5 Bytestream
byteStreamManager.establishSession(targetJID, sessionID);
fail("exception should be thrown");
}
catch (SmackException e) {
protocol.verifyAll();
assertTrue(e.getMessage().contains("no SOCKS5 proxies available"));
}
catch (Exception e) {
fail(e.getMessage());
}
}
/**
* Invoking {@link Socks5BytestreamManager#establishSession(String, 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.
*/
@Test
public void shouldBlacklistNonSocks5Proxies() {
// disable clients local SOCKS5 proxy
Socks5Proxy.setLocalSocks5ProxyEnabled(false);
// get Socks5ByteStreamManager for connection
Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection);
/**
* 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);
// return that SOCKS5 is supported if target is queried
protocol.addResponse(discoverInfo, Verification.correspondingSenderReceiver,
Verification.requestTypeGET);
// build discover items containing a proxy item
DiscoverItems discoverItems = Socks5PacketUtils.createDiscoverItems(xmppServer,
initiatorJID);
Item item = new Item(proxyJID);
discoverItems.addItem(item);
// return the proxy item if XMPP server is queried
protocol.addResponse(discoverItems, Verification.correspondingSenderReceiver,
Verification.requestTypeGET);
// build discover info for proxy containing information about NOT being a Socks5
// proxy
DiscoverInfo proxyInfo = Socks5PacketUtils.createDiscoverInfo(proxyJID, initiatorJID);
Identity identity = new Identity("noproxy", proxyJID, "bytestreams");
proxyInfo.addIdentity(identity);
// return the proxy identity if proxy is queried
protocol.addResponse(proxyInfo, Verification.correspondingSenderReceiver,
Verification.requestTypeGET);
try {
// start SOCKS5 Bytestream
byteStreamManager.establishSession(targetJID, sessionID);
fail("exception should be thrown");
}
catch (SmackException e) {
protocol.verifyAll();
assertTrue(e.getMessage().contains("no SOCKS5 proxies available"));
}
catch (Exception e) {
fail(e.getMessage());
}
/* retry to establish SOCKS5 Bytestream */
// add responses for service discovery again
protocol.addResponse(discoverInfo, Verification.correspondingSenderReceiver,
Verification.requestTypeGET);
protocol.addResponse(discoverItems, Verification.correspondingSenderReceiver,
Verification.requestTypeGET);
try {
// start SOCKS5 Bytestream
byteStreamManager.establishSession(targetJID, sessionID);
fail("exception should be thrown");
}
catch (SmackException e) {
/*
* #verifyAll() tests if the number of requests and responses corresponds and should
* fail if the invalid proxy is queried again
*/
protocol.verifyAll();
assertTrue(e.getMessage().contains("no SOCKS5 proxies available"));
}
catch (Exception e) {
fail(e.getMessage());
}
}
/**
* Invoking {@link Socks5BytestreamManager#establishSession(String, String)} should fail if the
* target does not accept a SOCKS5 Bytestream. See <a
* href="http://xmpp.org/extensions/xep-0065.html#usecase-alternate">XEP-0065 Section 5.2 A2</a>
*/
@Test
public void shouldFailIfTargetDoesNotAcceptSocks5Bytestream() {
// disable clients local SOCKS5 proxy
Socks5Proxy.setLocalSocks5ProxyEnabled(false);
// get Socks5ByteStreamManager for connection
Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection);
/**
* 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);
// return that SOCKS5 is supported if target is queried
protocol.addResponse(discoverInfo, Verification.correspondingSenderReceiver,
Verification.requestTypeGET);
// build discover items containing a proxy item
DiscoverItems discoverItems = Socks5PacketUtils.createDiscoverItems(xmppServer,
initiatorJID);
Item item = new Item(proxyJID);
discoverItems.addItem(item);
// return the proxy item if XMPP server is queried
protocol.addResponse(discoverItems, Verification.correspondingSenderReceiver,
Verification.requestTypeGET);
// build discover info for proxy containing information about being a SOCKS5 proxy
DiscoverInfo proxyInfo = Socks5PacketUtils.createDiscoverInfo(proxyJID, initiatorJID);
Identity identity = new Identity("proxy", proxyJID, "bytestreams");
proxyInfo.addIdentity(identity);
// return the socks5 bytestream proxy identity if proxy is queried
protocol.addResponse(proxyInfo, Verification.correspondingSenderReceiver,
Verification.requestTypeGET);
// build a socks5 stream host info containing the address and the port of the
// proxy
Bytestream streamHostInfo = Socks5PacketUtils.createBytestreamResponse(proxyJID,
initiatorJID);
streamHostInfo.addStreamHost(proxyJID, proxyAddress, 7778);
// return stream host info if it is queried
protocol.addResponse(streamHostInfo, Verification.correspondingSenderReceiver,
Verification.requestTypeGET);
// build error packet to reject SOCKS5 Bytestream
XMPPError xmppError = new XMPPError(XMPPError.Condition.not_acceptable);
IQ rejectPacket = new ErrorIQ(xmppError);
rejectPacket.setFrom(targetJID);
rejectPacket.setTo(initiatorJID);
rejectPacket.setError(xmppError);
// return error packet as response to the bytestream initiation
protocol.addResponse(rejectPacket, Verification.correspondingSenderReceiver,
Verification.requestTypeSET);
try {
// start SOCKS5 Bytestream
byteStreamManager.establishSession(targetJID, sessionID);
fail("exception should be thrown");
}
catch (XMPPErrorException e) {
protocol.verifyAll();
assertEquals(xmppError, e.getXMPPError());
}
catch (Exception e) {
fail(e.getMessage());
}
}
/**
* Invoking {@link Socks5BytestreamManager#establishSession(String, String)} should fail if the
* proxy used by target is invalid.
*/
@Test
public void shouldFailIfTargetUsesInvalidSocks5Proxy() {
// disable clients local SOCKS5 proxy
Socks5Proxy.setLocalSocks5ProxyEnabled(false);
// get Socks5ByteStreamManager for connection
Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection);
/**
* 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);
// return that SOCKS5 is supported if target is queried
protocol.addResponse(discoverInfo, Verification.correspondingSenderReceiver,
Verification.requestTypeGET);
// build discover items containing a proxy item
DiscoverItems discoverItems = Socks5PacketUtils.createDiscoverItems(xmppServer,
initiatorJID);
Item item = new Item(proxyJID);
discoverItems.addItem(item);
// return the proxy item if XMPP server is queried
protocol.addResponse(discoverItems, Verification.correspondingSenderReceiver,
Verification.requestTypeGET);
// build discover info for proxy containing information about being a SOCKS5 proxy
DiscoverInfo proxyInfo = Socks5PacketUtils.createDiscoverInfo(proxyJID, initiatorJID);
Identity identity = new Identity("proxy", proxyJID, "bytestreams");
proxyInfo.addIdentity(identity);
// return the socks5 bytestream proxy identity if proxy is queried
protocol.addResponse(proxyInfo, Verification.correspondingSenderReceiver,
Verification.requestTypeGET);
// build a socks5 stream host info containing the address and the port of the
// proxy
Bytestream streamHostInfo = Socks5PacketUtils.createBytestreamResponse(proxyJID,
initiatorJID);
streamHostInfo.addStreamHost(proxyJID, proxyAddress, 7778);
// return stream host info if it is queried
protocol.addResponse(streamHostInfo, Verification.correspondingSenderReceiver,
Verification.requestTypeGET);
// build used stream host response with unknown proxy
Bytestream streamHostUsedPacket = Socks5PacketUtils.createBytestreamResponse(targetJID,
initiatorJID);
streamHostUsedPacket.setSessionID(sessionID);
streamHostUsedPacket.setUsedHost("invalid.proxy");
// return used stream host info as response to the bytestream initiation
protocol.addResponse(streamHostUsedPacket, Verification.correspondingSenderReceiver,
Verification.requestTypeSET);
try {
// start SOCKS5 Bytestream
byteStreamManager.establishSession(targetJID, sessionID);
fail("exception should be thrown");
}
catch (SmackException e) {
protocol.verifyAll();
assertTrue(e.getMessage().contains("Remote user responded with unknown host"));
}
catch (Exception e) {
fail(e.getMessage());
}
}
/**
* Invoking {@link Socks5BytestreamManager#establishSession(String, String)} should fail if
* initiator can not connect to the SOCKS5 proxy used by target.
*/
@Test
public void shouldFailIfInitiatorCannotConnectToSocks5Proxy() {
// disable clients local SOCKS5 proxy
Socks5Proxy.setLocalSocks5ProxyEnabled(false);
// get Socks5ByteStreamManager for connection
Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection);
/**
* 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);
// return that SOCKS5 is supported if target is queried
protocol.addResponse(discoverInfo, Verification.correspondingSenderReceiver,
Verification.requestTypeGET);
// build discover items containing a proxy item
DiscoverItems discoverItems = Socks5PacketUtils.createDiscoverItems(xmppServer,
initiatorJID);
Item item = new Item(proxyJID);
discoverItems.addItem(item);
// return the proxy item if XMPP server is queried
protocol.addResponse(discoverItems, Verification.correspondingSenderReceiver,
Verification.requestTypeGET);
// build discover info for proxy containing information about being a SOCKS5 proxy
DiscoverInfo proxyInfo = Socks5PacketUtils.createDiscoverInfo(proxyJID, initiatorJID);
Identity identity = new Identity("proxy", proxyJID, "bytestreams");
proxyInfo.addIdentity(identity);
// return the socks5 bytestream proxy identity if proxy is queried
protocol.addResponse(proxyInfo, Verification.correspondingSenderReceiver,
Verification.requestTypeGET);
// build a socks5 stream host info containing the address and the port of the
// proxy
Bytestream streamHostInfo = Socks5PacketUtils.createBytestreamResponse(proxyJID,
initiatorJID);
streamHostInfo.addStreamHost(proxyJID, proxyAddress, 7778);
// return stream host info if it is queried
protocol.addResponse(streamHostInfo, Verification.correspondingSenderReceiver,
Verification.requestTypeGET);
// build used stream host response
Bytestream streamHostUsedPacket = Socks5PacketUtils.createBytestreamResponse(targetJID,
initiatorJID);
streamHostUsedPacket.setSessionID(sessionID);
streamHostUsedPacket.setUsedHost(proxyJID);
// return used stream host info as response to the bytestream initiation
protocol.addResponse(streamHostUsedPacket, new Verification<Bytestream, Bytestream>() {
public void verify(Bytestream request, Bytestream response) {
// verify SOCKS5 Bytestream request
assertEquals(response.getSessionID(), request.getSessionID());
assertEquals(1, request.getStreamHosts().size());
StreamHost streamHost = (StreamHost) request.getStreamHosts().toArray()[0];
assertEquals(response.getUsedHost().getJID(), streamHost.getJID());
}
}, Verification.correspondingSenderReceiver, Verification.requestTypeSET);
try {
// start SOCKS5 Bytestream
byteStreamManager.establishSession(targetJID, sessionID);
fail("exception should be thrown");
}
catch (IOException e) {
// initiator can't connect to proxy because it is not running
protocol.verifyAll();
assertEquals(ConnectException.class, e.getClass());
}
catch (Exception e) {
fail(e.getMessage());
}
}
/**
* Invoking {@link Socks5BytestreamManager#establishSession(String, String)} should successfully
* negotiate and return a SOCKS5 Bytestream connection.
*
* @throws Exception should not happen
*/
@Test
public void shouldNegotiateSocks5BytestreamAndTransferData() throws Exception {
// disable clients local SOCKS5 proxy
Socks5Proxy.setLocalSocks5ProxyEnabled(false);
// get Socks5ByteStreamManager for connection
Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection);
/**
* 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);
// return that SOCKS5 is supported if target is queried
protocol.addResponse(discoverInfo, Verification.correspondingSenderReceiver,
Verification.requestTypeGET);
// build discover items containing a proxy item
DiscoverItems discoverItems = Socks5PacketUtils.createDiscoverItems(xmppServer,
initiatorJID);
Item item = new Item(proxyJID);
discoverItems.addItem(item);
// return the proxy item if XMPP server is queried
protocol.addResponse(discoverItems, Verification.correspondingSenderReceiver,
Verification.requestTypeGET);
// build discover info for proxy containing information about being a SOCKS5 proxy
DiscoverInfo proxyInfo = Socks5PacketUtils.createDiscoverInfo(proxyJID, initiatorJID);
Identity identity = new Identity("proxy", proxyJID, "bytestreams");
proxyInfo.addIdentity(identity);
// return the socks5 bytestream proxy identity if proxy is queried
protocol.addResponse(proxyInfo, Verification.correspondingSenderReceiver,
Verification.requestTypeGET);
// build a socks5 stream host info containing the address and the port of the
// proxy
Bytestream streamHostInfo = Socks5PacketUtils.createBytestreamResponse(proxyJID,
initiatorJID);
streamHostInfo.addStreamHost(proxyJID, proxyAddress, 7778);
// return stream host info if it is queried
protocol.addResponse(streamHostInfo, Verification.correspondingSenderReceiver,
Verification.requestTypeGET);
// build used stream host response
Bytestream streamHostUsedPacket = Socks5PacketUtils.createBytestreamResponse(targetJID,
initiatorJID);
streamHostUsedPacket.setSessionID(sessionID);
streamHostUsedPacket.setUsedHost(proxyJID);
// return used stream host info as response to the bytestream initiation
protocol.addResponse(streamHostUsedPacket, new Verification<Bytestream, Bytestream>() {
public void verify(Bytestream request, Bytestream response) {
assertEquals(response.getSessionID(), request.getSessionID());
assertEquals(1, request.getStreamHosts().size());
StreamHost streamHost = (StreamHost) request.getStreamHosts().toArray()[0];
assertEquals(response.getUsedHost().getJID(), streamHost.getJID());
}
}, Verification.correspondingSenderReceiver, Verification.requestTypeSET);
// build response to proxy activation
IQ activationResponse = Socks5PacketUtils.createActivationConfirmation(proxyJID,
initiatorJID);
// return proxy activation response if proxy should be activated
protocol.addResponse(activationResponse, new Verification<Bytestream, IQ>() {
public void verify(Bytestream request, IQ response) {
assertEquals(targetJID, request.getToActivate().getTarget());
}
}, Verification.correspondingSenderReceiver, Verification.requestTypeSET);
// start a local SOCKS5 proxy
Socks5TestProxy socks5Proxy = Socks5TestProxy.getProxy(7778);
socks5Proxy.start();
// 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();
// test the established bytestream
InputStream inputStream = socks5Proxy.getSocket(digest).getInputStream();
byte[] data = new byte[] { 1, 2, 3 };
outputStream.write(data);
byte[] result = new byte[3];
inputStream.read(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
*/
@Test
public void shouldUseMultipleAddressesForLocalSocks5Proxy() throws Exception {
// enable clients local SOCKS5 proxy on port 7778
Socks5Proxy.setLocalSocks5ProxyEnabled(true);
Socks5Proxy.setLocalSocks5ProxyPort(7778);
// start a local SOCKS5 proxy
Socks5Proxy socks5Proxy = Socks5Proxy.getSocks5Proxy();
socks5Proxy.start();
assertTrue(socks5Proxy.isRunning());
// get Socks5ByteStreamManager for connection
Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection);
/**
* 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);
// 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);
// 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
// return used stream host info as response to the bytestream initiation
protocol.addResponse(streamHostUsedPacket, new Verification<Bytestream, Bytestream>() {
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);
// 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(String, 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 Exception should not happen
*/
@Test
public void shouldPrioritizeSecondSocks5ProxyOnSecondAttempt() throws Exception {
// disable clients local SOCKS5 proxy
Socks5Proxy.setLocalSocks5ProxyEnabled(false);
// get Socks5ByteStreamManager for connection
Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection);
assertTrue(byteStreamManager.isProxyPrioritizationEnabled());
Verification<Bytestream, Bytestream> streamHostUsedVerification1 = new Verification<Bytestream, Bytestream>() {
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 second in list
StreamHost streamHost = (StreamHost) request.getStreamHosts().toArray()[1];
assertEquals(response.getUsedHost().getJID(), streamHost.getJID());
}
};
createResponses(streamHostUsedVerification1);
// start a local SOCKS5 proxy
Socks5TestProxy socks5Proxy = Socks5TestProxy.getProxy(7778);
socks5Proxy.start();
// 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();
// test the established bytestream
InputStream inputStream = socks5Proxy.getSocket(digest).getInputStream();
byte[] data = new byte[] { 1, 2, 3 };
outputStream.write(data);
byte[] result = new byte[3];
inputStream.read(result);
assertArrayEquals(data, result);
protocol.verifyAll();
Verification<Bytestream, Bytestream> streamHostUsedVerification2 = new Verification<Bytestream, Bytestream>() {
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);
// 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();
}
/**
* Invoking {@link Socks5BytestreamManager#establishSession(String, String)} the first time
* 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
*/
@Test
public void shouldNotPrioritizeSocks5ProxyIfPrioritizationDisabled() throws Exception {
// disable clients local SOCKS5 proxy
Socks5Proxy.setLocalSocks5ProxyEnabled(false);
// get Socks5ByteStreamManager for connection
Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection);
byteStreamManager.setProxyPrioritizationEnabled(false);
assertFalse(byteStreamManager.isProxyPrioritizationEnabled());
Verification<Bytestream, Bytestream> streamHostUsedVerification = new Verification<Bytestream, Bytestream>() {
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 second in list
StreamHost streamHost = (StreamHost) request.getStreamHosts().toArray()[1];
assertEquals(response.getUsedHost().getJID(), streamHost.getJID());
}
};
createResponses(streamHostUsedVerification);
// start a local SOCKS5 proxy
Socks5TestProxy socks5Proxy = Socks5TestProxy.getProxy(7778);
socks5Proxy.start();
// 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();
// test the established bytestream
InputStream inputStream = socks5Proxy.getSocket(digest).getInputStream();
byte[] data = new byte[] { 1, 2, 3 };
outputStream.write(data);
byte[] result = new byte[3];
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<Bytestream, Bytestream> streamHostUsedVerification) {
// 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);
// build discover items containing a proxy item
DiscoverItems discoverItems = Socks5PacketUtils.createDiscoverItems(xmppServer,
initiatorJID);
discoverItems.addItem(new Item("proxy2.xmpp-server"));
discoverItems.addItem(new Item(proxyJID));
// return the proxy item if XMPP server is queried
protocol.addResponse(discoverItems, Verification.correspondingSenderReceiver,
Verification.requestTypeGET);
/*
* build discover info for proxy "proxy2.xmpp-server" containing information about being a
* SOCKS5 proxy
*/
DiscoverInfo proxyInfo1 = Socks5PacketUtils.createDiscoverInfo("proxy2.xmpp-server",
initiatorJID);
Identity identity1 = new Identity("proxy", "proxy2.xmpp-server", "bytestreams");
proxyInfo1.addIdentity(identity1);
// return the SOCKS5 bytestream proxy identity if proxy is queried
protocol.addResponse(proxyInfo1, Verification.correspondingSenderReceiver,
Verification.requestTypeGET);
// build discover info for proxy containing information about being a SOCKS5 proxy
DiscoverInfo proxyInfo2 = Socks5PacketUtils.createDiscoverInfo(proxyJID, initiatorJID);
Identity identity2 = new Identity("proxy", proxyJID, "bytestreams");
proxyInfo2.addIdentity(identity2);
// return the SOCKS5 bytestream proxy identity if proxy is queried
protocol.addResponse(proxyInfo2, Verification.correspondingSenderReceiver,
Verification.requestTypeGET);
/*
* build a SOCKS5 stream host info for "proxy2.xmpp-server" containing the address and the
* port of the proxy
*/
Bytestream streamHostInfo1 = Socks5PacketUtils.createBytestreamResponse(
"proxy2.xmpp-server", initiatorJID);
streamHostInfo1.addStreamHost("proxy2.xmpp-server", proxyAddress, 7778);
// return stream host info if it is queried
protocol.addResponse(streamHostInfo1, Verification.correspondingSenderReceiver,
Verification.requestTypeGET);
// 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);
// return stream host info if it is queried
protocol.addResponse(streamHostInfo2, Verification.correspondingSenderReceiver,
Verification.requestTypeGET);
// build used stream host response
Bytestream streamHostUsedPacket = Socks5PacketUtils.createBytestreamResponse(targetJID,
initiatorJID);
streamHostUsedPacket.setSessionID(sessionID);
streamHostUsedPacket.setUsedHost(proxyJID);
// return used stream host info as response to the bytestream initiation
protocol.addResponse(streamHostUsedPacket, streamHostUsedVerification,
Verification.correspondingSenderReceiver, Verification.requestTypeSET);
// build response to proxy activation
IQ activationResponse = Socks5PacketUtils.createActivationConfirmation(proxyJID,
initiatorJID);
// return proxy activation response if proxy should be activated
protocol.addResponse(activationResponse, new Verification<Bytestream, IQ>() {
public void verify(Bytestream request, IQ response) {
assertEquals(targetJID, request.getToActivate().getTarget());
}
}, Verification.correspondingSenderReceiver, Verification.requestTypeSET);
}
/**
* Stop eventually started local SOCKS5 test proxy.
*/
@After
public void cleanUp() {
Socks5TestProxy.stopProxy();
Socks5Proxy.setLocalSocks5ProxyEnabled(true);
}
}