1
0
Fork 0
mirror of https://codeberg.org/Mercury-IM/Smack synced 2024-06-22 11:34:50 +02:00
Smack/smack-extensions/src/test/java/org/jivesoftware/smackx/bytestreams/socks5/Socks5ProxyTest.java
vanitasvitae 99c1c93c2a Fix shouldPreserveAddressOrderOnInsertions test
The test failed because the ArrayList - in contrast
to the underlying Set - did not check for duplicates
on insert. Under certain circumstances this lead to
an index out of bounds exception because the list in
the test contained duplicated entries which were not
present in the set of the Socks5Proxy.
I fixed the issue by only inserting the address when
it was not in the list before.
2017-07-04 21:50:08 +02:00

361 lines
11 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.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import org.jivesoftware.smack.util.StringUtils;
import org.junit.After;
import org.junit.Test;
/**
* Test for Socks5Proxy class.
*
* @author Henning Staib
*/
public class Socks5ProxyTest {
static final String loopbackAddress = InetAddress.getLoopbackAddress().getHostAddress();
/**
* The SOCKS5 proxy should be a singleton used by all XMPP connections.
*/
@Test
public void shouldBeASingleton() {
Socks5Proxy.setLocalSocks5ProxyEnabled(false);
Socks5Proxy proxy1 = Socks5Proxy.getSocks5Proxy();
Socks5Proxy proxy2 = Socks5Proxy.getSocks5Proxy();
assertNotNull(proxy1);
assertNotNull(proxy2);
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.
*
* @throws Exception should not happen
*/
@Test
public void shouldUseFreePortOnNegativeValues() throws Exception {
Socks5Proxy.setLocalSocks5ProxyEnabled(false);
Socks5Proxy proxy = Socks5Proxy.getSocks5Proxy();
assertFalse(proxy.isRunning());
ServerSocket serverSocket = new ServerSocket(0);
Socks5Proxy.setLocalSocks5ProxyPort(-serverSocket.getLocalPort());
proxy.start();
assertTrue(proxy.isRunning());
serverSocket.close();
assertTrue(proxy.getPort() > serverSocket.getLocalPort());
}
/**
* When inserting new network addresses to the proxy the order should remain in the order they
* were inserted.
*/
@Test
public void shouldPreserveAddressOrderOnInsertions() {
Socks5Proxy proxy = Socks5Proxy.getSocks5Proxy();
LinkedHashSet<String> addresses = new LinkedHashSet<>(proxy.getLocalAddresses());
for (int i = 1 ; i <= 3; i++) {
addresses.add(Integer.toString(i));
}
for (String address : addresses) {
proxy.addLocalAddress(address);
}
List<String> localAddresses = proxy.getLocalAddresses();
Iterator<String> iterator = addresses.iterator();
for (int i = 0; i < addresses.size(); i++) {
assertEquals(iterator.next(), localAddresses.get(i));
}
}
/**
* When replacing network addresses of the proxy the order should remain in the order if the
* given list.
*/
@Test
public void shouldPreserveAddressOrderOnReplace() {
Socks5Proxy proxy = Socks5Proxy.getSocks5Proxy();
List<String> addresses = new ArrayList<String>(proxy.getLocalAddresses());
addresses.add("1");
addresses.add("2");
addresses.add("3");
proxy.replaceLocalAddresses(addresses);
List<String> 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.
*
* @throws Exception should not happen
*/
@Test
public void shouldCloseSocketIfNoSocks5Request() throws Exception {
Socks5Proxy.setLocalSocks5ProxyPort(7890);
Socks5Proxy proxy = Socks5Proxy.getSocks5Proxy();
proxy.start();
@SuppressWarnings("resource")
Socket socket = new Socket(loopbackAddress, proxy.getPort());
OutputStream out = socket.getOutputStream();
out.write(new byte[] { 1, 2, 3 });
int res;
try {
res = socket.getInputStream().read();
} catch (SocketException e) {
res = -1;
}
assertEquals(-1, res);
proxy.stop();
}
/**
* The SOCKS5 proxy should reply with an error message if no supported authentication methods
* are given in the SOCKS5 request.
*
* @throws Exception should not happen
*/
@Test
public void shouldRespondWithErrorIfNoSupportedAuthenticationMethod() throws Exception {
Socks5Proxy.setLocalSocks5ProxyPort(7890);
Socks5Proxy proxy = Socks5Proxy.getSocks5Proxy();
proxy.start();
@SuppressWarnings("resource")
Socket socket = new Socket(loopbackAddress, proxy.getPort());
OutputStream out = socket.getOutputStream();
// request username/password-authentication
out.write(new byte[] { (byte) 0x05, (byte) 0x01, (byte) 0x02 });
InputStream in = socket.getInputStream();
assertEquals((byte) 0x05, (byte) in.read());
assertEquals((byte) 0xFF, (byte) in.read());
assertEquals(-1, in.read());
proxy.stop();
}
/**
* The SOCKS5 proxy should respond with an error message if the client is not allowed to connect
* with the proxy.
*
* @throws Exception should not happen
*/
@Test
public void shouldRespondWithErrorIfConnectionIsNotAllowed() throws Exception {
Socks5Proxy.setLocalSocks5ProxyPort(7890);
Socks5Proxy proxy = Socks5Proxy.getSocks5Proxy();
proxy.start();
@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 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();
}
/**
* A Client should successfully establish a connection to the SOCKS5 proxy.
*
* @throws Exception should not happen
*/
@Test
public void shouldSuccessfullyEstablishConnection() throws Exception {
Socks5Proxy.setLocalSocks5ProxyPort(7890);
Socks5Proxy proxy = Socks5Proxy.getSocks5Proxy();
proxy.start();
assertTrue(proxy.isRunning());
String digest = new String(new byte[] { (byte) 0xAA }, StringUtils.UTF8);
// 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<String> addresses = new ArrayList<String>();
addresses.add(address);
socks5Proxy.replaceLocalAddresses(addresses);
}
catch (UnknownHostException e) {
// ignore
}
socks5Proxy.stop();
}
}