Merge pull request #537 from MF1-MS/mf1-ms/use_xmpp_connection_as_local_socks5_address

Use XMPP connection as local socks5 address
This commit is contained in:
Florian Schmaus 2023-04-26 11:35:04 +00:00 committed by GitHub
commit 19b20fefec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 124 additions and 1 deletions

View File

@ -21,6 +21,7 @@ import java.io.IOException;
import java.io.PipedReader;
import java.io.PipedWriter;
import java.io.Writer;
import java.net.InetAddress;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -314,6 +315,11 @@ public class XMPPBOSHConnection extends AbstractXMPPConnection {
}
}
@Override
public InetAddress getLocalAddress() {
return null;
}
@Override
protected void shutdown() {
instantShutdown();

View File

@ -16,6 +16,7 @@
*/
package org.jivesoftware.smack;
import java.net.InetAddress;
import java.util.concurrent.TimeUnit;
import javax.xml.namespace.QName;
@ -160,6 +161,14 @@ public interface XMPPConnection {
*/
EntityFullJid getUser();
/**
* Returns the local address currently in use for this connection, or <code>null</code> if
* this is invalid for the type of underlying connection.
*
* @return the local address currently in use for this connection
*/
InetAddress getLocalAddress();
/**
* Returns the stream ID for this connection, which is the value set by the server
* when opening an XMPP stream. This value will be <code>null</code> if not connected to the server.

View File

@ -17,6 +17,7 @@
package org.jivesoftware.smack.c2s;
import java.io.IOException;
import java.net.InetAddress;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.Collection;
@ -1128,6 +1129,11 @@ public final class ModularXmppClientToServerConnection extends AbstractXMPPConne
walkStateGraph(walkStateGraphContext);
}
@Override
public InetAddress getLocalAddress() {
return null;
}
private Map<String, Object> getFilterStats() {
Collection<XmppInputOutputFilter> filters;
synchronized (this) {

View File

@ -17,6 +17,7 @@
package org.jivesoftware.smack;
import java.io.IOException;
import java.net.InetAddress;
import java.util.Date;
import java.util.Random;
import java.util.concurrent.BlockingQueue;
@ -139,6 +140,11 @@ public class DummyConnection extends AbstractXMPPConnection {
}
}
@Override
public InetAddress getLocalAddress() {
return null;
}
/**
* Returns the number of packets that's sent through {@link #sendStanza(Stanza)} and
* that has not been returned by {@link #getSentPacket()}.

View File

@ -656,13 +656,23 @@ public final class Socks5BytestreamManager extends Manager implements Bytestream
*/
public List<StreamHost> getLocalStreamHost() {
// Ensure that the local SOCKS5 proxy is running (if enabled).
Socks5Proxy.getSocks5Proxy();
Socks5Proxy socks5Proxy = Socks5Proxy.getSocks5Proxy();
List<StreamHost> streamHosts = new ArrayList<>();
XMPPConnection connection = connection();
EntityFullJid myJid = connection.getUser();
// The default local address is often just 'the first address found in the
// list of addresses read from the OS' and this might mean an internal
// IP address that cannot reach external servers. So wherever possible
// use the same IP address being used to connect to the XMPP server
// because this local address has a better chance of being suitable.
InetAddress xmppLocalAddress = connection.getLocalAddress();
if (xmppLocalAddress != null) {
socks5Proxy.replaceLocalAddresses(Collections.singletonList(xmppLocalAddress));
}
for (Socks5Proxy socks5Server : Socks5Proxy.getRunningProxies()) {
List<InetAddress> addresses = socks5Server.getLocalAddresses();
if (addresses.isEmpty()) {

View File

@ -24,12 +24,16 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeoutException;
@ -1002,6 +1006,72 @@ public class Socks5ByteStreamManagerTest {
protocol.verifyAll();
}
/**
* Invoking {@link Socks5BytestreamManager#getLocalStreamHost()} should return only a local address
* from XMPP connection when it is connected and has a socket with a bound non-localhost IP address.
*
* @throws InterruptedException if the calling thread was interrupted.
* @throws SmackException if Smack detected an exceptional situation.
* @throws XMPPErrorException if an XMPP protocol error was received.
*/
@Test
public void shouldUseXMPPConnectionLocalAddressWhenConnected() throws InterruptedException, XMPPErrorException, SmackException {
final Protocol protocol = new Protocol();
final XMPPConnection connection = ConnectionUtils.createMockedConnection(protocol, initiatorJID);
// prepare XMPP local address
Inet4Address xmppLocalAddress = mock(Inet4Address.class);
when(xmppLocalAddress.getHostAddress()).thenReturn("81.72.63.54");
when(connection.getLocalAddress()).thenReturn(xmppLocalAddress);
// get Socks5ByteStreamManager for connection
Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection);
List<StreamHost> localStreamHost = byteStreamManager.getLocalStreamHost();
// must be only 1 stream host with XMPP local address IP
assertEquals(1, localStreamHost.size());
assertEquals("81.72.63.54", localStreamHost.get(0).getAddress().toString());
assertEquals(initiatorJID, localStreamHost.get(0).getJID());
}
/**
* Invoking {@link Socks5BytestreamManager#getLocalStreamHost()} should return all non-localhost
* local addresses when its XMPP connection's socket is null.
*
* @throws InterruptedException if the calling thread was interrupted.
* @throws SmackException if Smack detected an exceptional situation.
* @throws XMPPErrorException if an XMPP protocol error was received.
* @throws UnknownHostException if address cannot be resolved.
*/
@Test
public void shouldUseSocks5LocalAddressesWhenNotConnected() throws InterruptedException, XMPPErrorException, SmackException, UnknownHostException {
final Protocol protocol = new Protocol();
final XMPPConnection connection = ConnectionUtils.createMockedConnection(protocol, initiatorJID);
// No XMPP local address
when(connection.getLocalAddress()).thenReturn(null);
// get Socks5ByteStreamManager for connection
Socks5BytestreamManager byteStreamManager = Socks5BytestreamManager.getBytestreamManager(connection);
List<InetAddress> localAddresses = new ArrayList<>();
for (InetAddress inetAddress : Socks5Proxy.getSocks5Proxy().getLocalAddresses()) {
if (!inetAddress.isLoopbackAddress()) {
localAddresses.add(inetAddress);
}
}
List<StreamHost> localStreamHost = byteStreamManager.getLocalStreamHost();
// Must be the same addresses as in SOCKS5 proxy local address list (excluding loopback)
assertEquals(localAddresses.size(), localStreamHost.size());
for (StreamHost streamHost : localStreamHost) {
assertTrue(localAddresses.contains(streamHost.getAddress().asInetAddress()));
assertEquals(initiatorJID, streamHost.getJID());
}
}
private static void createResponses(Protocol protocol, String sessionID,
Verification<Bytestream, Bytestream> streamHostUsedVerification, Socks5TestProxy socks5TestProxy)
throws XmppStringprepException {

View File

@ -1938,4 +1938,20 @@ public class XMPPTCPConnection extends AbstractXMPPConnection {
this.bundleAndDeferCallback = bundleAndDeferCallback;
}
/**
* Returns the local address currently in use for this connection.
*
* @return the local address
*/
@Override
public InetAddress getLocalAddress() {
final Socket socket = this.socket;
if (socket == null) return null;
InetAddress localAddress = socket.getLocalAddress();
if (localAddress.isAnyLocalAddress()) return null;
return localAddress;
}
}