2014-02-17 23:58:40 +01:00
|
|
|
/**
|
|
|
|
*
|
|
|
|
* 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;
|
|
|
|
|
2014-08-15 23:16:18 +02:00
|
|
|
import static org.junit.Assert.assertEquals;
|
|
|
|
import static org.junit.Assert.assertNotNull;
|
|
|
|
import static org.junit.Assert.assertTrue;
|
|
|
|
import static org.junit.Assert.fail;
|
2014-02-17 23:58:40 +01:00
|
|
|
|
|
|
|
import java.io.InputStream;
|
|
|
|
import java.io.OutputStream;
|
2016-07-08 16:34:21 +02:00
|
|
|
import java.net.InetAddress;
|
2014-02-17 23:58:40 +01:00
|
|
|
import java.net.Socket;
|
|
|
|
|
2014-03-12 11:50:05 +01:00
|
|
|
import org.jivesoftware.smack.SmackException;
|
2014-03-10 09:45:50 +01:00
|
|
|
import org.jivesoftware.smack.XMPPConnection;
|
2014-02-17 23:58:40 +01:00
|
|
|
import org.jivesoftware.smack.XMPPException;
|
2014-03-12 11:50:05 +01:00
|
|
|
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
|
2014-11-07 21:12:01 +01:00
|
|
|
import org.jivesoftware.smack.packet.EmptyResultIQ;
|
|
|
|
import org.jivesoftware.smack.packet.ErrorIQ;
|
2014-02-17 23:58:40 +01:00
|
|
|
import org.jivesoftware.smack.packet.IQ;
|
2018-04-07 21:25:40 +02:00
|
|
|
import org.jivesoftware.smack.packet.StanzaError;
|
2017-06-14 17:12:43 +02:00
|
|
|
|
2014-02-17 23:58:40 +01:00
|
|
|
import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream;
|
|
|
|
import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream.StreamHost;
|
2017-06-14 17:12:43 +02:00
|
|
|
|
2014-02-17 23:58:40 +01:00
|
|
|
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;
|
2015-02-14 17:15:02 +01:00
|
|
|
import org.jxmpp.jid.DomainBareJid;
|
2015-05-27 19:29:51 +02:00
|
|
|
import org.jxmpp.jid.EntityFullJid;
|
2015-02-14 17:15:02 +01:00
|
|
|
import org.jxmpp.jid.JidTestUtil;
|
2014-02-17 23:58:40 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Test for Socks5ClientForInitiator class.
|
2018-05-09 23:06:12 +02:00
|
|
|
*
|
2014-02-17 23:58:40 +01:00
|
|
|
* @author Henning Staib
|
|
|
|
*/
|
|
|
|
public class Socks5ClientForInitiatorTest {
|
|
|
|
|
|
|
|
// settings
|
2017-12-13 23:10:11 +01:00
|
|
|
private static final EntityFullJid initiatorJID = JidTestUtil.DUMMY_AT_EXAMPLE_ORG_SLASH_DUMMYRESOURCE;
|
|
|
|
private static final EntityFullJid targetJID = JidTestUtil.FULL_JID_1_RESOURCE_1;
|
|
|
|
private static final DomainBareJid proxyJID = JidTestUtil.MUC_EXAMPLE_ORG;
|
|
|
|
private static final String loopbackAddress = InetAddress.getLoopbackAddress().getHostAddress();
|
2016-07-08 16:34:21 +02:00
|
|
|
|
2017-05-25 11:59:45 +02:00
|
|
|
private static final int GET_SOCKET_TIMEOUT = 90 * 1000;
|
|
|
|
|
2017-12-13 23:10:11 +01:00
|
|
|
private static final int proxyPort = 7890;
|
|
|
|
private static final String sessionID = "session_id";
|
2014-02-17 23:58:40 +01:00
|
|
|
|
|
|
|
// protocol verifier
|
2017-12-13 23:10:11 +01:00
|
|
|
private Protocol protocol;
|
2014-02-17 23:58:40 +01:00
|
|
|
|
|
|
|
// mocked XMPP connection
|
2017-12-13 23:10:11 +01:00
|
|
|
private XMPPConnection connection;
|
2014-02-17 23:58:40 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Initialize fields used in the tests.
|
2018-05-09 23:06:12 +02:00
|
|
|
* @throws XMPPException
|
|
|
|
* @throws SmackException
|
|
|
|
* @throws InterruptedException
|
2014-02-17 23:58:40 +01:00
|
|
|
*/
|
|
|
|
@Before
|
2015-02-14 09:43:44 +01:00
|
|
|
public void setup() throws XMPPException, SmackException, InterruptedException {
|
2014-02-17 23:58:40 +01:00
|
|
|
|
|
|
|
// build protocol verifier
|
|
|
|
protocol = new Protocol();
|
|
|
|
|
|
|
|
// create mocked XMPP connection
|
2018-04-18 14:10:14 +02:00
|
|
|
connection = ConnectionUtils.createMockedConnection(protocol, initiatorJID);
|
2014-02-17 23:58:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* If the target is not connected to the local SOCKS5 proxy an exception should be thrown.
|
2018-05-09 23:06:12 +02:00
|
|
|
*
|
2014-02-17 23:58:40 +01:00
|
|
|
* @throws Exception should not happen
|
|
|
|
*/
|
|
|
|
@Test
|
|
|
|
public void shouldFailIfTargetIsNotConnectedToLocalSocks5Proxy() throws Exception {
|
|
|
|
|
|
|
|
// start a local SOCKS5 proxy
|
2014-02-23 17:48:07 +01:00
|
|
|
Socks5Proxy.setLocalSocks5ProxyPort(proxyPort);
|
2014-02-17 23:58:40 +01:00
|
|
|
Socks5Proxy socks5Proxy = Socks5Proxy.getSocks5Proxy();
|
|
|
|
socks5Proxy.start();
|
|
|
|
|
|
|
|
// build stream host information for local SOCKS5 proxy
|
|
|
|
StreamHost streamHost = new StreamHost(connection.getUser(),
|
2016-07-08 16:34:21 +02:00
|
|
|
loopbackAddress,
|
2014-08-19 19:22:08 +02:00
|
|
|
socks5Proxy.getPort());
|
2014-02-17 23:58:40 +01:00
|
|
|
|
|
|
|
// 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 {
|
2017-05-25 11:59:45 +02:00
|
|
|
socks5Client.getSocket(GET_SOCKET_TIMEOUT);
|
2014-02-17 23:58:40 +01:00
|
|
|
|
|
|
|
fail("exception should be thrown");
|
|
|
|
}
|
2014-03-12 11:50:05 +01:00
|
|
|
catch (SmackException e) {
|
2014-02-17 23:58:40 +01:00
|
|
|
assertTrue(e.getMessage().contains("target is not connected to SOCKS5 proxy"));
|
|
|
|
protocol.verifyAll(); // assert no XMPP messages were sent
|
|
|
|
}
|
|
|
|
|
|
|
|
socks5Proxy.stop();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Initiator and target should successfully connect to the local SOCKS5 proxy.
|
2018-05-09 23:06:12 +02:00
|
|
|
*
|
2014-02-17 23:58:40 +01:00
|
|
|
* @throws Exception should not happen
|
|
|
|
*/
|
|
|
|
@Test
|
|
|
|
public void shouldSuccessfullyConnectThroughLocalSocks5Proxy() throws Exception {
|
|
|
|
|
|
|
|
// start a local SOCKS5 proxy
|
2014-02-23 17:48:07 +01:00
|
|
|
Socks5Proxy.setLocalSocks5ProxyPort(proxyPort);
|
2014-02-17 23:58:40 +01:00
|
|
|
Socks5Proxy socks5Proxy = Socks5Proxy.getSocks5Proxy();
|
|
|
|
socks5Proxy.start();
|
|
|
|
|
|
|
|
// test data
|
|
|
|
final byte[] data = new byte[] { 1, 2, 3 };
|
|
|
|
|
|
|
|
// create digest
|
|
|
|
final String digest = Socks5Utils.createDigest(sessionID, initiatorJID, targetJID);
|
|
|
|
|
|
|
|
// allow connection of target with this digest
|
|
|
|
socks5Proxy.addTransfer(digest);
|
|
|
|
|
|
|
|
// build stream host information
|
|
|
|
final StreamHost streamHost = new StreamHost(connection.getUser(),
|
2016-07-08 16:34:21 +02:00
|
|
|
loopbackAddress,
|
2014-08-19 19:22:08 +02:00
|
|
|
socks5Proxy.getPort());
|
2014-02-17 23:58:40 +01:00
|
|
|
|
|
|
|
// 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());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
targetThread.start();
|
|
|
|
|
|
|
|
Thread.sleep(200);
|
|
|
|
|
|
|
|
// initiator connects
|
|
|
|
Socks5ClientForInitiator socks5Client = new Socks5ClientForInitiator(streamHost, digest,
|
|
|
|
connection, sessionID, targetJID);
|
|
|
|
|
2017-05-25 11:59:45 +02:00
|
|
|
Socket socket = socks5Client.getSocket(GET_SOCKET_TIMEOUT);
|
2014-02-17 23:58:40 +01:00
|
|
|
|
|
|
|
// verify test data
|
|
|
|
InputStream in = socket.getInputStream();
|
|
|
|
for (int i = 0; i < data.length; i++) {
|
|
|
|
assertEquals(data[i], in.read());
|
|
|
|
}
|
|
|
|
|
|
|
|
targetThread.join();
|
|
|
|
|
|
|
|
protocol.verifyAll(); // assert no XMPP messages were sent
|
|
|
|
|
|
|
|
socks5Proxy.removeTransfer(digest);
|
|
|
|
socks5Proxy.stop();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* If the initiator can connect to a SOCKS5 proxy but activating the stream fails an exception
|
|
|
|
* should be thrown.
|
2018-05-09 23:06:12 +02:00
|
|
|
*
|
2014-02-17 23:58:40 +01:00
|
|
|
* @throws Exception should not happen
|
|
|
|
*/
|
|
|
|
@Test
|
|
|
|
public void shouldFailIfActivateSocks5ProxyFails() throws Exception {
|
|
|
|
|
|
|
|
// build error response as reply to the stream activation
|
2018-04-07 21:25:40 +02:00
|
|
|
IQ error = new ErrorIQ(StanzaError.getBuilder(StanzaError.Condition.internal_server_error));
|
2014-02-17 23:58:40 +01:00
|
|
|
error.setFrom(proxyJID);
|
|
|
|
error.setTo(initiatorJID);
|
|
|
|
|
|
|
|
protocol.addResponse(error, Verification.correspondingSenderReceiver,
|
|
|
|
Verification.requestTypeSET);
|
|
|
|
|
|
|
|
// start a local SOCKS5 proxy
|
|
|
|
Socks5TestProxy socks5Proxy = Socks5TestProxy.getProxy(proxyPort);
|
|
|
|
socks5Proxy.start();
|
|
|
|
|
2014-08-19 19:22:08 +02:00
|
|
|
StreamHost streamHost = new StreamHost(proxyJID,
|
2016-07-08 16:34:21 +02:00
|
|
|
loopbackAddress, socks5Proxy.getPort());
|
2014-02-17 23:58:40 +01:00
|
|
|
|
|
|
|
// 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 {
|
|
|
|
|
2017-05-25 11:59:45 +02:00
|
|
|
socks5Client.getSocket(GET_SOCKET_TIMEOUT);
|
2014-02-17 23:58:40 +01:00
|
|
|
|
|
|
|
fail("exception should be thrown");
|
|
|
|
}
|
2014-03-12 11:50:05 +01:00
|
|
|
catch (XMPPErrorException e) {
|
2018-06-20 15:55:48 +02:00
|
|
|
assertTrue(StanzaError.Condition.internal_server_error.equals(e.getStanzaError().getCondition()));
|
2014-02-17 23:58:40 +01:00
|
|
|
protocol.verifyAll();
|
|
|
|
}
|
|
|
|
|
|
|
|
socks5Proxy.stop();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Target and initiator should successfully connect to a "remote" SOCKS5 proxy and the initiator
|
|
|
|
* activates the bytestream.
|
2018-05-09 23:06:12 +02:00
|
|
|
*
|
2014-02-17 23:58:40 +01:00
|
|
|
* @throws Exception should not happen
|
|
|
|
*/
|
|
|
|
@Test
|
|
|
|
public void shouldSuccessfullyEstablishConnectionAndActivateSocks5Proxy() throws Exception {
|
|
|
|
|
|
|
|
// build activation confirmation response
|
2014-11-07 21:12:01 +01:00
|
|
|
IQ activationResponse = new EmptyResultIQ();
|
2014-02-17 23:58:40 +01:00
|
|
|
|
|
|
|
activationResponse.setFrom(proxyJID);
|
|
|
|
activationResponse.setTo(initiatorJID);
|
|
|
|
|
|
|
|
protocol.addResponse(activationResponse, Verification.correspondingSenderReceiver,
|
|
|
|
Verification.requestTypeSET, new Verification<Bytestream, IQ>() {
|
|
|
|
|
2017-02-11 16:16:41 +01:00
|
|
|
@Override
|
2014-02-17 23:58:40 +01:00
|
|
|
public void verify(Bytestream request, IQ response) {
|
|
|
|
// verify that the correct stream should be activated
|
|
|
|
assertNotNull(request.getToActivate());
|
|
|
|
assertEquals(targetJID, request.getToActivate().getTarget());
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
// start a local SOCKS5 proxy
|
|
|
|
Socks5TestProxy socks5Proxy = Socks5TestProxy.getProxy(proxyPort);
|
|
|
|
socks5Proxy.start();
|
|
|
|
|
2014-08-19 19:22:08 +02:00
|
|
|
StreamHost streamHost = new StreamHost(proxyJID,
|
2016-07-08 16:34:21 +02:00
|
|
|
loopbackAddress, socks5Proxy.getPort());
|
2014-02-17 23:58:40 +01:00
|
|
|
|
|
|
|
// 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);
|
|
|
|
|
|
|
|
Socket initiatorSocket = socks5Client.getSocket(10000);
|
|
|
|
InputStream in = initiatorSocket.getInputStream();
|
|
|
|
|
|
|
|
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());
|
|
|
|
}
|
|
|
|
|
|
|
|
protocol.verifyAll();
|
|
|
|
|
|
|
|
initiatorSocket.close();
|
|
|
|
targetSocket.close();
|
|
|
|
socks5Proxy.stop();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Reset default port for local SOCKS5 proxy.
|
|
|
|
*/
|
|
|
|
@After
|
|
|
|
public void cleanup() {
|
2014-02-23 17:48:07 +01:00
|
|
|
Socks5Proxy.setLocalSocks5ProxyPort(7777);
|
2014-02-17 23:58:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|