mirror of
https://github.com/vanitasvitae/Smack.git
synced 2024-11-23 04:22:05 +01:00
Improve Socks5 Bytestreams
- determine all local IPv4 and IPv6 addresses - prevent loopback addresses from appearing as streamhost Some unit tests where changed because they assumed that a host only has one local address. But nowadays hosts often have more, at least because they are IPv4 and IPv6 multi-homed.
This commit is contained in:
parent
650da55b23
commit
b468a29881
5 changed files with 82 additions and 45 deletions
|
@ -652,24 +652,34 @@ public final class Socks5BytestreamManager implements BytestreamManager {
|
||||||
// get local proxy singleton
|
// get local proxy singleton
|
||||||
Socks5Proxy socks5Server = Socks5Proxy.getSocks5Proxy();
|
Socks5Proxy socks5Server = Socks5Proxy.getSocks5Proxy();
|
||||||
|
|
||||||
if (socks5Server.isRunning()) {
|
if (!socks5Server.isRunning()) {
|
||||||
List<String> addresses = socks5Server.getLocalAddresses();
|
// server is not running
|
||||||
int port = socks5Server.getPort();
|
return null;
|
||||||
|
|
||||||
if (addresses.size() >= 1) {
|
|
||||||
List<StreamHost> streamHosts = new ArrayList<StreamHost>();
|
|
||||||
for (String address : addresses) {
|
|
||||||
StreamHost streamHost = new StreamHost(this.connection.getUser(), address);
|
|
||||||
streamHost.setPort(port);
|
|
||||||
streamHosts.add(streamHost);
|
|
||||||
}
|
|
||||||
return streamHosts;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
List<String> addresses = socks5Server.getLocalAddresses();
|
||||||
|
if (addresses.isEmpty()) {
|
||||||
|
// local address could not be determined
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
final int port = socks5Server.getPort();
|
||||||
|
|
||||||
// server is not running or local address could not be determined
|
List<StreamHost> streamHosts = new ArrayList<StreamHost>();
|
||||||
return null;
|
outerloop: for (String address : addresses) {
|
||||||
|
// Prevent loopback addresses from appearing as streamhost
|
||||||
|
final String[] loopbackAddresses = { "127.0.0.1", "0:0:0:0:0:0:0: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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
StreamHost streamHost = new StreamHost(this.connection.getUser(),
|
||||||
|
address);
|
||||||
|
streamHost.setPort(port);
|
||||||
|
streamHosts.add(streamHost);
|
||||||
|
}
|
||||||
|
return streamHosts;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -20,12 +20,14 @@ import java.io.DataInputStream;
|
||||||
import java.io.DataOutputStream;
|
import java.io.DataOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
|
import java.net.NetworkInterface;
|
||||||
import java.net.ServerSocket;
|
import java.net.ServerSocket;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.net.SocketException;
|
import java.net.SocketException;
|
||||||
import java.net.UnknownHostException;
|
import java.util.Collection;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -47,7 +49,7 @@ import org.jivesoftware.smack.SmackException;
|
||||||
* <p>
|
* <p>
|
||||||
* If your application is running on a machine with multiple network interfaces or if you want to
|
* 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
|
* provide your public address in case you are behind a NAT router, invoke
|
||||||
* {@link #addLocalAddress(String)} or {@link #replaceLocalAddresses(List)} to modify the list of
|
* {@link #addLocalAddress(String)} or {@link #replaceLocalAddresses(Collection)} to modify the list of
|
||||||
* local network addresses used for outgoing SOCKS5 Bytestream requests.
|
* local network addresses used for outgoing SOCKS5 Bytestream requests.
|
||||||
* <p>
|
* <p>
|
||||||
* The local SOCKS5 proxy server refuses all connections except the ones that are explicitly allowed
|
* The local SOCKS5 proxy server refuses all connections except the ones that are explicitly allowed
|
||||||
|
@ -95,7 +97,7 @@ public class Socks5Proxy {
|
||||||
/* list of digests connections should be stored */
|
/* list of digests connections should be stored */
|
||||||
private final List<String> allowedConnections = Collections.synchronizedList(new LinkedList<String>());
|
private final List<String> allowedConnections = Collections.synchronizedList(new LinkedList<String>());
|
||||||
|
|
||||||
private final Set<String> localAddresses = Collections.synchronizedSet(new LinkedHashSet<String>());
|
private final Set<String> localAddresses = new LinkedHashSet<String>(4);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Private constructor.
|
* Private constructor.
|
||||||
|
@ -103,14 +105,27 @@ public class Socks5Proxy {
|
||||||
private Socks5Proxy() {
|
private Socks5Proxy() {
|
||||||
this.serverProcess = new Socks5ServerProcess();
|
this.serverProcess = new Socks5ServerProcess();
|
||||||
|
|
||||||
// add default local address
|
Enumeration<NetworkInterface> networkInterfaces;
|
||||||
try {
|
try {
|
||||||
this.localAddresses.add(InetAddress.getLocalHost().getHostAddress());
|
networkInterfaces = NetworkInterface.getNetworkInterfaces();
|
||||||
|
} catch (SocketException e) {
|
||||||
|
throw new IllegalStateException(e);
|
||||||
}
|
}
|
||||||
catch (UnknownHostException e) {
|
Set<String> localHostAddresses = new HashSet<String>();
|
||||||
// do nothing
|
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<InetAddress> inetAddresses = networkInterface.getInetAddresses();
|
||||||
|
for (InetAddress address : Collections.list(inetAddresses)) {
|
||||||
|
localHostAddresses.add(address.getHostAddress());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
if (localHostAddresses.isEmpty()) {
|
||||||
|
throw new IllegalStateException("Could not determine any local host address");
|
||||||
|
}
|
||||||
|
replaceLocalAddresses(localHostAddresses);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -243,15 +258,17 @@ public class Socks5Proxy {
|
||||||
* <p>
|
* <p>
|
||||||
* Note that the list of addresses initially contains the address returned by
|
* Note that the list of addresses initially contains the address returned by
|
||||||
* <code>InetAddress.getLocalHost().getHostAddress()</code>. You can replace the list of
|
* <code>InetAddress.getLocalHost().getHostAddress()</code>. You can replace the list of
|
||||||
* addresses by invoking {@link #replaceLocalAddresses(List)}.
|
* addresses by invoking {@link #replaceLocalAddresses(Collection)}.
|
||||||
*
|
*
|
||||||
* @param address the local network address to add
|
* @param address the local network address to add
|
||||||
*/
|
*/
|
||||||
public void addLocalAddress(String address) {
|
public void addLocalAddress(String address) {
|
||||||
if (address == null) {
|
if (address == null) {
|
||||||
throw new IllegalArgumentException("address may not be null");
|
return;
|
||||||
|
}
|
||||||
|
synchronized (localAddresses) {
|
||||||
|
this.localAddresses.add(address);
|
||||||
}
|
}
|
||||||
this.localAddresses.add(address);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -259,19 +276,24 @@ public class Socks5Proxy {
|
||||||
* longer be used of outgoing SOCKS5 Bytestream requests.
|
* longer be used of outgoing SOCKS5 Bytestream requests.
|
||||||
*
|
*
|
||||||
* @param address the local network address to remove
|
* @param address the local network address to remove
|
||||||
|
* @return true if the address was removed.
|
||||||
*/
|
*/
|
||||||
public void removeLocalAddress(String address) {
|
public boolean removeLocalAddress(String address) {
|
||||||
this.localAddresses.remove(address);
|
synchronized(localAddresses) {
|
||||||
|
return localAddresses.remove(address);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an unmodifiable list of the local network addresses that will be used for streamhost
|
* Returns an set of the local network addresses that will be used for streamhost
|
||||||
* candidates of outgoing SOCKS5 Bytestream requests.
|
* candidates of outgoing SOCKS5 Bytestream requests.
|
||||||
*
|
*
|
||||||
* @return unmodifiable list of the local network addresses
|
* @return set of the local network addresses
|
||||||
*/
|
*/
|
||||||
public List<String> getLocalAddresses() {
|
public List<String> getLocalAddresses() {
|
||||||
return Collections.unmodifiableList(new ArrayList<String>(this.localAddresses));
|
synchronized (localAddresses) {
|
||||||
|
return new LinkedList<String>(localAddresses);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -284,13 +306,14 @@ public class Socks5Proxy {
|
||||||
*
|
*
|
||||||
* @param addresses the new list of local network addresses
|
* @param addresses the new list of local network addresses
|
||||||
*/
|
*/
|
||||||
public void replaceLocalAddresses(List<String> addresses) {
|
public void replaceLocalAddresses(Collection<String> addresses) {
|
||||||
if (addresses == null) {
|
if (addresses == null) {
|
||||||
throw new IllegalArgumentException("list must not be null");
|
throw new IllegalArgumentException("list must not be null");
|
||||||
}
|
}
|
||||||
this.localAddresses.clear();
|
synchronized(localAddresses) {
|
||||||
this.localAddresses.addAll(addresses);
|
localAddresses.clear();
|
||||||
|
localAddresses.addAll(addresses);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -17,7 +17,6 @@
|
||||||
package org.jivesoftware.smackx.bytestreams.socks5.packet;
|
package org.jivesoftware.smackx.bytestreams.socks5.packet;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -145,8 +144,8 @@ public class Bytestream extends IQ {
|
||||||
*
|
*
|
||||||
* @return Returns the list of stream hosts contained in the packet.
|
* @return Returns the list of stream hosts contained in the packet.
|
||||||
*/
|
*/
|
||||||
public Collection<StreamHost> getStreamHosts() {
|
public List<StreamHost> getStreamHosts() {
|
||||||
return Collections.unmodifiableCollection(streamHosts);
|
return Collections.unmodifiableList(streamHosts);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -799,10 +799,9 @@ public class Socks5ByteStreamManagerTest {
|
||||||
|
|
||||||
public void verify(Bytestream request, Bytestream response) {
|
public void verify(Bytestream request, Bytestream response) {
|
||||||
assertEquals(response.getSessionID(), request.getSessionID());
|
assertEquals(response.getSessionID(), request.getSessionID());
|
||||||
assertEquals(2, request.getStreamHosts().size());
|
StreamHost streamHost1 = request.getStreamHosts().get(0);
|
||||||
StreamHost streamHost1 = (StreamHost) request.getStreamHosts().toArray()[0];
|
|
||||||
assertEquals(response.getUsedHost().getJID(), streamHost1.getJID());
|
assertEquals(response.getUsedHost().getJID(), streamHost1.getJID());
|
||||||
StreamHost streamHost2 = (StreamHost) request.getStreamHosts().toArray()[1];
|
StreamHost streamHost2 = request.getStreamHosts().get(request.getStreamHosts().size() - 1);
|
||||||
assertEquals(response.getUsedHost().getJID(), streamHost2.getJID());
|
assertEquals(response.getUsedHost().getJID(), streamHost2.getJID());
|
||||||
assertEquals("localAddress", streamHost2.getAddress());
|
assertEquals("localAddress", streamHost2.getAddress());
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,6 +30,7 @@ import java.net.Socket;
|
||||||
import java.net.SocketException;
|
import java.net.SocketException;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
|
@ -144,7 +145,13 @@ public class Socks5ProxyTest {
|
||||||
proxy.addLocalAddress("same");
|
proxy.addLocalAddress("same");
|
||||||
proxy.addLocalAddress("same");
|
proxy.addLocalAddress("same");
|
||||||
|
|
||||||
assertEquals(2, proxy.getLocalAddresses().size());
|
int sameCount = 0;
|
||||||
|
for(String localAddress : proxy.getLocalAddresses()) {
|
||||||
|
if ("same".equals(localAddress)) {
|
||||||
|
sameCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assertEquals(1, sameCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -297,7 +304,6 @@ public class Socks5ProxyTest {
|
||||||
proxy.start();
|
proxy.start();
|
||||||
|
|
||||||
assertTrue(proxy.isRunning());
|
assertTrue(proxy.isRunning());
|
||||||
|
|
||||||
String digest = new String(new byte[] { (byte) 0xAA });
|
String digest = new String(new byte[] { (byte) 0xAA });
|
||||||
|
|
||||||
// add digest to allow connection
|
// add digest to allow connection
|
||||||
|
|
Loading…
Reference in a new issue