mirror of
https://codeberg.org/Mercury-IM/Smack
synced 2024-06-22 11:34:50 +02:00
vanitasvitae
99c1c93c2a
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.
361 lines
11 KiB
Java
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();
|
|
}
|
|
|
|
}
|