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;
|
|
|
|
|
|
|
|
import java.io.IOException;
|
|
|
|
import java.net.Socket;
|
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.Collections;
|
2015-02-14 17:15:02 +01:00
|
|
|
import java.util.HashSet;
|
2014-02-17 23:58:40 +01:00
|
|
|
import java.util.LinkedList;
|
|
|
|
import java.util.List;
|
|
|
|
import java.util.Map;
|
|
|
|
import java.util.Random;
|
2015-02-14 17:15:02 +01:00
|
|
|
import java.util.Set;
|
2015-06-29 17:07:42 +02:00
|
|
|
import java.util.WeakHashMap;
|
2014-02-17 23:58:40 +01:00
|
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
|
|
import java.util.concurrent.TimeoutException;
|
|
|
|
|
2017-06-14 17:12:43 +02:00
|
|
|
import org.jivesoftware.smack.ConnectionCreationListener;
|
2015-05-24 17:18:48 +02:00
|
|
|
import org.jivesoftware.smack.Manager;
|
2014-03-12 11:50:05 +01:00
|
|
|
import org.jivesoftware.smack.SmackException;
|
2014-03-17 21:06:45 +01:00
|
|
|
import org.jivesoftware.smack.SmackException.FeatureNotSupportedException;
|
2017-06-14 17:12:43 +02:00
|
|
|
import org.jivesoftware.smack.SmackException.NoResponseException;
|
2014-03-19 14:22:20 +01:00
|
|
|
import org.jivesoftware.smack.SmackException.NotConnectedException;
|
2014-03-10 09:45:50 +01:00
|
|
|
import org.jivesoftware.smack.XMPPConnection;
|
2014-05-25 12:28:08 +02:00
|
|
|
import org.jivesoftware.smack.XMPPConnectionRegistry;
|
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-02-17 23:58:40 +01:00
|
|
|
import org.jivesoftware.smack.packet.IQ;
|
2015-02-05 11:17:27 +01:00
|
|
|
import org.jivesoftware.smack.packet.Stanza;
|
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.BytestreamListener;
|
|
|
|
import org.jivesoftware.smackx.bytestreams.BytestreamManager;
|
|
|
|
import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream;
|
|
|
|
import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream.StreamHost;
|
|
|
|
import org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream.StreamHostUsed;
|
|
|
|
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
|
|
|
|
import org.jivesoftware.smackx.disco.packet.DiscoverInfo;
|
|
|
|
import org.jivesoftware.smackx.disco.packet.DiscoverItems;
|
|
|
|
import org.jivesoftware.smackx.disco.packet.DiscoverItems.Item;
|
|
|
|
import org.jivesoftware.smackx.filetransfer.FileTransferManager;
|
2017-06-14 17:12:43 +02:00
|
|
|
|
2015-02-14 17:15:02 +01:00
|
|
|
import org.jxmpp.jid.Jid;
|
2014-02-17 23:58:40 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* The Socks5BytestreamManager class handles establishing SOCKS5 Bytestreams as specified in the <a
|
|
|
|
* href="http://xmpp.org/extensions/xep-0065.html">XEP-0065</a>.
|
|
|
|
* <p>
|
|
|
|
* A SOCKS5 Bytestream is negotiated partly over the XMPP XML stream and partly over a separate
|
|
|
|
* socket. The actual transfer though takes place over a separately created socket.
|
|
|
|
* <p>
|
|
|
|
* A SOCKS5 Bytestream generally has three parties, the initiator, the target, and the stream host.
|
|
|
|
* The stream host is a specialized SOCKS5 proxy setup on a server, or, the initiator can act as the
|
|
|
|
* stream host.
|
|
|
|
* <p>
|
2015-02-14 17:15:02 +01:00
|
|
|
* To establish a SOCKS5 Bytestream invoke the {@link #establishSession(Jid)} method. This will
|
2014-02-17 23:58:40 +01:00
|
|
|
* negotiate a SOCKS5 Bytestream with the given target JID and return a socket.
|
|
|
|
* <p>
|
|
|
|
* If a session ID for the SOCKS5 Bytestream was already negotiated (e.g. while negotiating a file
|
2015-02-14 17:15:02 +01:00
|
|
|
* transfer) invoke {@link #establishSession(Jid, String)}.
|
2014-02-17 23:58:40 +01:00
|
|
|
* <p>
|
|
|
|
* To handle incoming SOCKS5 Bytestream requests add an {@link Socks5BytestreamListener} to the
|
|
|
|
* manager. There are two ways to add this listener. If you want to be informed about incoming
|
|
|
|
* SOCKS5 Bytestreams from a specific user add the listener by invoking
|
2015-02-14 17:15:02 +01:00
|
|
|
* {@link #addIncomingBytestreamListener(BytestreamListener, Jid)}. If the listener should
|
2014-02-17 23:58:40 +01:00
|
|
|
* respond to all SOCKS5 Bytestream requests invoke
|
|
|
|
* {@link #addIncomingBytestreamListener(BytestreamListener)}.
|
|
|
|
* <p>
|
|
|
|
* Note that the registered {@link Socks5BytestreamListener} will NOT be notified on incoming Socks5
|
|
|
|
* bytestream requests sent in the context of <a
|
|
|
|
* href="http://xmpp.org/extensions/xep-0096.html">XEP-0096</a> file transfer. (See
|
|
|
|
* {@link FileTransferManager})
|
|
|
|
* <p>
|
|
|
|
* If no {@link Socks5BytestreamListener}s are registered, all incoming SOCKS5 Bytestream requests
|
|
|
|
* will be rejected by returning a <not-acceptable/> error to the initiator.
|
2018-05-09 23:06:12 +02:00
|
|
|
*
|
2014-02-17 23:58:40 +01:00
|
|
|
* @author Henning Staib
|
|
|
|
*/
|
2015-05-24 17:18:48 +02:00
|
|
|
public final class Socks5BytestreamManager extends Manager implements BytestreamManager {
|
2014-02-17 23:58:40 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* create a new Socks5BytestreamManager and register a shutdown listener on every established
|
|
|
|
* connection
|
|
|
|
*/
|
|
|
|
static {
|
2014-05-25 12:28:08 +02:00
|
|
|
XMPPConnectionRegistry.addConnectionCreationListener(new ConnectionCreationListener() {
|
2014-02-17 23:58:40 +01:00
|
|
|
|
2017-02-11 16:16:41 +01:00
|
|
|
@Override
|
2014-03-10 09:45:50 +01:00
|
|
|
public void connectionCreated(final XMPPConnection connection) {
|
2014-02-17 23:58:40 +01:00
|
|
|
// create the manager for this connection
|
|
|
|
Socks5BytestreamManager.getBytestreamManager(connection);
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/* prefix used to generate session IDs */
|
|
|
|
private static final String SESSION_ID_PREFIX = "js5_";
|
|
|
|
|
|
|
|
/* random generator to create session IDs */
|
2018-03-29 12:35:11 +02:00
|
|
|
private static final Random randomGenerator = new Random();
|
2014-02-17 23:58:40 +01:00
|
|
|
|
|
|
|
/* stores one Socks5BytestreamManager for each XMPP connection */
|
2018-03-29 12:35:11 +02:00
|
|
|
private static final Map<XMPPConnection, Socks5BytestreamManager> managers = new WeakHashMap<>();
|
2014-02-17 23:58:40 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* assigns a user to a listener that is informed if a bytestream request for this user is
|
|
|
|
* received
|
|
|
|
*/
|
2015-02-14 17:15:02 +01:00
|
|
|
private final Map<Jid, BytestreamListener> userListeners = new ConcurrentHashMap<>();
|
2014-02-17 23:58:40 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* list of listeners that respond to all bytestream requests if there are not user specific
|
|
|
|
* listeners for that request
|
|
|
|
*/
|
|
|
|
private final List<BytestreamListener> allRequestListeners = Collections.synchronizedList(new LinkedList<BytestreamListener>());
|
|
|
|
|
|
|
|
/* listener that handles all incoming bytestream requests */
|
|
|
|
private final InitiationListener initiationListener;
|
|
|
|
|
|
|
|
/* timeout to wait for the response to the SOCKS5 Bytestream initialization request */
|
|
|
|
private int targetResponseTimeout = 10000;
|
|
|
|
|
|
|
|
/* timeout for connecting to the SOCKS5 proxy selected by the target */
|
|
|
|
private int proxyConnectionTimeout = 10000;
|
|
|
|
|
|
|
|
/* blacklist of errornous SOCKS5 proxies */
|
2015-02-14 17:15:02 +01:00
|
|
|
private final Set<Jid> proxyBlacklist = Collections.synchronizedSet(new HashSet<Jid>());
|
2014-02-17 23:58:40 +01:00
|
|
|
|
|
|
|
/* remember the last proxy that worked to prioritize it */
|
2015-02-14 17:15:02 +01:00
|
|
|
private Jid lastWorkingProxy;
|
2014-02-17 23:58:40 +01:00
|
|
|
|
|
|
|
/* flag to enable/disable prioritization of last working proxy */
|
|
|
|
private boolean proxyPrioritizationEnabled = true;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* list containing session IDs of SOCKS5 Bytestream initialization packets that should be
|
|
|
|
* ignored by the InitiationListener
|
|
|
|
*/
|
2017-12-13 23:10:11 +01:00
|
|
|
private final List<String> ignoredBytestreamRequests = Collections.synchronizedList(new LinkedList<String>());
|
2014-02-17 23:58:40 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the Socks5BytestreamManager to handle SOCKS5 Bytestreams for a given
|
2014-03-10 09:45:50 +01:00
|
|
|
* {@link XMPPConnection}.
|
2014-02-17 23:58:40 +01:00
|
|
|
* <p>
|
|
|
|
* If no manager exists a new is created and initialized.
|
2018-05-09 23:06:12 +02:00
|
|
|
*
|
2014-02-17 23:58:40 +01:00
|
|
|
* @param connection the XMPP connection or <code>null</code> if given connection is
|
|
|
|
* <code>null</code>
|
|
|
|
* @return the Socks5BytestreamManager for the given XMPP connection
|
|
|
|
*/
|
2014-03-10 09:45:50 +01:00
|
|
|
public static synchronized Socks5BytestreamManager getBytestreamManager(XMPPConnection connection) {
|
2014-02-17 23:58:40 +01:00
|
|
|
if (connection == null) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
Socks5BytestreamManager manager = managers.get(connection);
|
|
|
|
if (manager == null) {
|
|
|
|
manager = new Socks5BytestreamManager(connection);
|
|
|
|
managers.put(connection, manager);
|
|
|
|
}
|
|
|
|
return manager;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Private constructor.
|
2018-05-09 23:06:12 +02:00
|
|
|
*
|
2014-02-17 23:58:40 +01:00
|
|
|
* @param connection the XMPP connection
|
|
|
|
*/
|
2014-03-10 09:45:50 +01:00
|
|
|
private Socks5BytestreamManager(XMPPConnection connection) {
|
2015-05-24 17:18:48 +02:00
|
|
|
super(connection);
|
2014-02-17 23:58:40 +01:00
|
|
|
this.initiationListener = new InitiationListener(this);
|
2015-05-24 17:18:48 +02:00
|
|
|
activate();
|
2014-02-17 23:58:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Adds BytestreamListener that is called for every incoming SOCKS5 Bytestream request unless
|
|
|
|
* there is a user specific BytestreamListener registered.
|
|
|
|
* <p>
|
|
|
|
* If no listeners are registered all SOCKS5 Bytestream request are rejected with a
|
|
|
|
* <not-acceptable/> error.
|
|
|
|
* <p>
|
|
|
|
* Note that the registered {@link BytestreamListener} will NOT be notified on incoming Socks5
|
|
|
|
* bytestream requests sent in the context of <a
|
|
|
|
* href="http://xmpp.org/extensions/xep-0096.html">XEP-0096</a> file transfer. (See
|
|
|
|
* {@link FileTransferManager})
|
2018-05-09 23:06:12 +02:00
|
|
|
*
|
2014-02-17 23:58:40 +01:00
|
|
|
* @param listener the listener to register
|
|
|
|
*/
|
2017-02-11 16:16:41 +01:00
|
|
|
@Override
|
2014-02-17 23:58:40 +01:00
|
|
|
public void addIncomingBytestreamListener(BytestreamListener listener) {
|
|
|
|
this.allRequestListeners.add(listener);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Removes the given listener from the list of listeners for all incoming SOCKS5 Bytestream
|
|
|
|
* requests.
|
2018-05-09 23:06:12 +02:00
|
|
|
*
|
2014-02-17 23:58:40 +01:00
|
|
|
* @param listener the listener to remove
|
|
|
|
*/
|
2017-02-11 16:16:41 +01:00
|
|
|
@Override
|
2014-02-17 23:58:40 +01:00
|
|
|
public void removeIncomingBytestreamListener(BytestreamListener listener) {
|
|
|
|
this.allRequestListeners.remove(listener);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Adds BytestreamListener that is called for every incoming SOCKS5 Bytestream request from the
|
|
|
|
* given user.
|
|
|
|
* <p>
|
|
|
|
* Use this method if you are awaiting an incoming SOCKS5 Bytestream request from a specific
|
|
|
|
* user.
|
|
|
|
* <p>
|
|
|
|
* If no listeners are registered all SOCKS5 Bytestream request are rejected with a
|
|
|
|
* <not-acceptable/> error.
|
|
|
|
* <p>
|
|
|
|
* Note that the registered {@link BytestreamListener} will NOT be notified on incoming Socks5
|
|
|
|
* bytestream requests sent in the context of <a
|
|
|
|
* href="http://xmpp.org/extensions/xep-0096.html">XEP-0096</a> file transfer. (See
|
|
|
|
* {@link FileTransferManager})
|
2018-05-09 23:06:12 +02:00
|
|
|
*
|
2014-02-17 23:58:40 +01:00
|
|
|
* @param listener the listener to register
|
|
|
|
* @param initiatorJID the JID of the user that wants to establish a SOCKS5 Bytestream
|
|
|
|
*/
|
2017-02-11 16:16:41 +01:00
|
|
|
@Override
|
2015-02-14 17:15:02 +01:00
|
|
|
public void addIncomingBytestreamListener(BytestreamListener listener, Jid initiatorJID) {
|
2014-02-17 23:58:40 +01:00
|
|
|
this.userListeners.put(initiatorJID, listener);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Removes the listener for the given user.
|
2018-05-09 23:06:12 +02:00
|
|
|
*
|
2014-02-17 23:58:40 +01:00
|
|
|
* @param initiatorJID the JID of the user the listener should be removed
|
|
|
|
*/
|
2017-02-11 16:16:41 +01:00
|
|
|
@Override
|
2017-08-16 14:31:46 +02:00
|
|
|
public void removeIncomingBytestreamListener(Jid initiatorJID) {
|
2014-02-17 23:58:40 +01:00
|
|
|
this.userListeners.remove(initiatorJID);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Use this method to ignore the next incoming SOCKS5 Bytestream request containing the given
|
|
|
|
* session ID. No listeners will be notified for this request and and no error will be returned
|
|
|
|
* to the initiator.
|
|
|
|
* <p>
|
|
|
|
* This method should be used if you are awaiting a SOCKS5 Bytestream request as a reply to
|
2018-03-31 14:17:30 +02:00
|
|
|
* another stanza (e.g. file transfer).
|
2018-05-09 23:06:12 +02:00
|
|
|
*
|
2014-02-17 23:58:40 +01:00
|
|
|
* @param sessionID to be ignored
|
|
|
|
*/
|
|
|
|
public void ignoreBytestreamRequestOnce(String sessionID) {
|
|
|
|
this.ignoredBytestreamRequests.add(sessionID);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Disables the SOCKS5 Bytestream manager by removing the SOCKS5 Bytestream feature from the
|
|
|
|
* service discovery, disabling the listener for SOCKS5 Bytestream initiation requests and
|
|
|
|
* resetting its internal state, which includes removing this instance from the managers map.
|
|
|
|
* <p>
|
2014-03-10 09:45:50 +01:00
|
|
|
* To re-enable the SOCKS5 Bytestream feature invoke {@link #getBytestreamManager(XMPPConnection)}.
|
2014-02-17 23:58:40 +01:00
|
|
|
* Using the file transfer API will automatically re-enable the SOCKS5 Bytestream feature.
|
|
|
|
*/
|
|
|
|
public synchronized void disableService() {
|
2015-05-24 17:18:48 +02:00
|
|
|
XMPPConnection connection = connection();
|
2014-02-17 23:58:40 +01:00
|
|
|
// remove initiation packet listener
|
2015-01-08 11:01:35 +01:00
|
|
|
connection.unregisterIQRequestHandler(initiationListener);
|
2014-02-17 23:58:40 +01:00
|
|
|
|
|
|
|
// shutdown threads
|
|
|
|
this.initiationListener.shutdown();
|
|
|
|
|
|
|
|
// clear listeners
|
|
|
|
this.allRequestListeners.clear();
|
|
|
|
this.userListeners.clear();
|
|
|
|
|
|
|
|
// reset internal state
|
|
|
|
this.lastWorkingProxy = null;
|
|
|
|
this.proxyBlacklist.clear();
|
|
|
|
this.ignoredBytestreamRequests.clear();
|
|
|
|
|
|
|
|
// remove manager from static managers map
|
2015-05-24 17:18:48 +02:00
|
|
|
managers.remove(connection);
|
2014-02-17 23:58:40 +01:00
|
|
|
|
|
|
|
// shutdown local SOCKS5 proxy if there are no more managers for other connections
|
|
|
|
if (managers.size() == 0) {
|
|
|
|
Socks5Proxy.getSocks5Proxy().stop();
|
|
|
|
}
|
|
|
|
|
|
|
|
// remove feature from service discovery
|
2015-05-24 17:18:48 +02:00
|
|
|
ServiceDiscoveryManager serviceDiscoveryManager = ServiceDiscoveryManager.getInstanceFor(connection);
|
2014-02-17 23:58:40 +01:00
|
|
|
|
|
|
|
// check if service discovery is not already disposed by connection shutdown
|
|
|
|
if (serviceDiscoveryManager != null) {
|
2014-07-05 11:58:13 +02:00
|
|
|
serviceDiscoveryManager.removeFeature(Bytestream.NAMESPACE);
|
2014-02-17 23:58:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the timeout to wait for the response to the SOCKS5 Bytestream initialization request.
|
|
|
|
* Default is 10000ms.
|
2018-05-09 23:06:12 +02:00
|
|
|
*
|
2014-02-17 23:58:40 +01:00
|
|
|
* @return the timeout to wait for the response to the SOCKS5 Bytestream initialization request
|
|
|
|
*/
|
|
|
|
public int getTargetResponseTimeout() {
|
|
|
|
if (this.targetResponseTimeout <= 0) {
|
|
|
|
this.targetResponseTimeout = 10000;
|
|
|
|
}
|
|
|
|
return targetResponseTimeout;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the timeout to wait for the response to the SOCKS5 Bytestream initialization request.
|
|
|
|
* Default is 10000ms.
|
2018-05-09 23:06:12 +02:00
|
|
|
*
|
2014-02-17 23:58:40 +01:00
|
|
|
* @param targetResponseTimeout the timeout to set
|
|
|
|
*/
|
|
|
|
public void setTargetResponseTimeout(int targetResponseTimeout) {
|
|
|
|
this.targetResponseTimeout = targetResponseTimeout;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the timeout for connecting to the SOCKS5 proxy selected by the target. Default is
|
|
|
|
* 10000ms.
|
2018-05-09 23:06:12 +02:00
|
|
|
*
|
2014-02-17 23:58:40 +01:00
|
|
|
* @return the timeout for connecting to the SOCKS5 proxy selected by the target
|
|
|
|
*/
|
|
|
|
public int getProxyConnectionTimeout() {
|
|
|
|
if (this.proxyConnectionTimeout <= 0) {
|
|
|
|
this.proxyConnectionTimeout = 10000;
|
|
|
|
}
|
|
|
|
return proxyConnectionTimeout;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets the timeout for connecting to the SOCKS5 proxy selected by the target. Default is
|
|
|
|
* 10000ms.
|
2018-05-09 23:06:12 +02:00
|
|
|
*
|
2014-02-17 23:58:40 +01:00
|
|
|
* @param proxyConnectionTimeout the timeout to set
|
|
|
|
*/
|
|
|
|
public void setProxyConnectionTimeout(int proxyConnectionTimeout) {
|
|
|
|
this.proxyConnectionTimeout = proxyConnectionTimeout;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns if the prioritization of the last working SOCKS5 proxy on successive SOCKS5
|
|
|
|
* Bytestream connections is enabled. Default is <code>true</code>.
|
2018-05-09 23:06:12 +02:00
|
|
|
*
|
2014-02-17 23:58:40 +01:00
|
|
|
* @return <code>true</code> if prioritization is enabled, <code>false</code> otherwise
|
|
|
|
*/
|
|
|
|
public boolean isProxyPrioritizationEnabled() {
|
|
|
|
return proxyPrioritizationEnabled;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Enable/disable the prioritization of the last working SOCKS5 proxy on successive SOCKS5
|
|
|
|
* Bytestream connections.
|
2018-05-09 23:06:12 +02:00
|
|
|
*
|
2014-02-17 23:58:40 +01:00
|
|
|
* @param proxyPrioritizationEnabled enable/disable the prioritization of the last working
|
|
|
|
* SOCKS5 proxy
|
|
|
|
*/
|
|
|
|
public void setProxyPrioritizationEnabled(boolean proxyPrioritizationEnabled) {
|
|
|
|
this.proxyPrioritizationEnabled = proxyPrioritizationEnabled;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Establishes a SOCKS5 Bytestream with the given user and returns the Socket to send/receive
|
|
|
|
* data to/from the user.
|
|
|
|
* <p>
|
|
|
|
* Use this method to establish SOCKS5 Bytestreams to users accepting all incoming Socks5
|
|
|
|
* bytestream requests since this method doesn't provide a way to tell the user something about
|
|
|
|
* the data to be sent.
|
|
|
|
* <p>
|
|
|
|
* To establish a SOCKS5 Bytestream after negotiation the kind of data to be sent (e.g. file
|
2015-02-14 17:15:02 +01:00
|
|
|
* transfer) use {@link #establishSession(Jid, String)}.
|
2018-05-09 23:06:12 +02:00
|
|
|
*
|
2014-02-17 23:58:40 +01:00
|
|
|
* @param targetJID the JID of the user a SOCKS5 Bytestream should be established
|
|
|
|
* @return the Socket to send/receive data to/from the user
|
|
|
|
* @throws XMPPException if the user doesn't support or accept SOCKS5 Bytestreams, if no Socks5
|
|
|
|
* Proxy could be found, if the user couldn't connect to any of the SOCKS5 Proxies
|
|
|
|
* @throws IOException if the bytestream could not be established
|
|
|
|
* @throws InterruptedException if the current thread was interrupted while waiting
|
2014-03-12 11:50:05 +01:00
|
|
|
* @throws SmackException if there was no response from the server.
|
2014-02-17 23:58:40 +01:00
|
|
|
*/
|
2017-02-11 16:16:41 +01:00
|
|
|
@Override
|
2015-02-14 17:15:02 +01:00
|
|
|
public Socks5BytestreamSession establishSession(Jid targetJID) throws XMPPException,
|
2014-03-12 11:50:05 +01:00
|
|
|
IOException, InterruptedException, SmackException {
|
2014-02-17 23:58:40 +01:00
|
|
|
String sessionID = getNextSessionID();
|
|
|
|
return establishSession(targetJID, sessionID);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Establishes a SOCKS5 Bytestream with the given user using the given session ID and returns
|
|
|
|
* the Socket to send/receive data to/from the user.
|
2018-05-09 23:06:12 +02:00
|
|
|
*
|
2014-02-17 23:58:40 +01:00
|
|
|
* @param targetJID the JID of the user a SOCKS5 Bytestream should be established
|
|
|
|
* @param sessionID the session ID for the SOCKS5 Bytestream request
|
|
|
|
* @return the Socket to send/receive data to/from the user
|
|
|
|
* @throws IOException if the bytestream could not be established
|
|
|
|
* @throws InterruptedException if the current thread was interrupted while waiting
|
2014-03-12 11:50:05 +01:00
|
|
|
* @throws SmackException if the target does not support SOCKS5.
|
2018-05-09 23:06:12 +02:00
|
|
|
* @throws XMPPException
|
2014-02-17 23:58:40 +01:00
|
|
|
*/
|
2017-02-11 16:16:41 +01:00
|
|
|
@Override
|
2015-02-14 17:15:02 +01:00
|
|
|
public Socks5BytestreamSession establishSession(Jid targetJID, String sessionID)
|
2017-12-13 23:10:11 +01:00
|
|
|
throws IOException, InterruptedException, SmackException, XMPPException {
|
2015-05-24 17:18:48 +02:00
|
|
|
XMPPConnection connection = connection();
|
2014-03-12 11:50:05 +01:00
|
|
|
XMPPErrorException discoveryException = null;
|
2014-02-17 23:58:40 +01:00
|
|
|
// check if target supports SOCKS5 Bytestream
|
|
|
|
if (!supportsSocks5(targetJID)) {
|
2014-03-17 21:06:45 +01:00
|
|
|
throw new FeatureNotSupportedException("SOCKS5 Bytestream", targetJID);
|
2014-02-17 23:58:40 +01:00
|
|
|
}
|
|
|
|
|
2015-02-14 17:15:02 +01:00
|
|
|
List<Jid> proxies = new ArrayList<>();
|
2014-02-17 23:58:40 +01:00
|
|
|
// determine SOCKS5 proxies from XMPP-server
|
|
|
|
try {
|
|
|
|
proxies.addAll(determineProxies());
|
2014-03-12 11:50:05 +01:00
|
|
|
} catch (XMPPErrorException e) {
|
2014-02-17 23:58:40 +01:00
|
|
|
// don't abort here, just remember the exception thrown by determineProxies()
|
|
|
|
// determineStreamHostInfos() will at least add the local Socks5 proxy (if enabled)
|
|
|
|
discoveryException = e;
|
|
|
|
}
|
|
|
|
|
|
|
|
// determine address and port of each proxy
|
|
|
|
List<StreamHost> streamHosts = determineStreamHostInfos(proxies);
|
|
|
|
|
|
|
|
if (streamHosts.isEmpty()) {
|
2014-03-12 11:50:05 +01:00
|
|
|
if (discoveryException != null) {
|
|
|
|
throw discoveryException;
|
|
|
|
} else {
|
|
|
|
throw new SmackException("no SOCKS5 proxies available");
|
|
|
|
}
|
2014-02-17 23:58:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// compute digest
|
2015-05-24 17:18:48 +02:00
|
|
|
String digest = Socks5Utils.createDigest(sessionID, connection.getUser(), targetJID);
|
2014-02-17 23:58:40 +01:00
|
|
|
|
|
|
|
// prioritize last working SOCKS5 proxy if exists
|
|
|
|
if (this.proxyPrioritizationEnabled && this.lastWorkingProxy != null) {
|
|
|
|
StreamHost selectedStreamHost = null;
|
|
|
|
for (StreamHost streamHost : streamHosts) {
|
|
|
|
if (streamHost.getJID().equals(this.lastWorkingProxy)) {
|
|
|
|
selectedStreamHost = streamHost;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (selectedStreamHost != null) {
|
|
|
|
streamHosts.remove(selectedStreamHost);
|
|
|
|
streamHosts.add(0, selectedStreamHost);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
Socks5Proxy socks5Proxy = Socks5Proxy.getSocks5Proxy();
|
|
|
|
try {
|
|
|
|
|
|
|
|
// add transfer digest to local proxy to make transfer valid
|
|
|
|
socks5Proxy.addTransfer(digest);
|
|
|
|
|
|
|
|
// create initiation packet
|
|
|
|
Bytestream initiation = createBytestreamInitiation(sessionID, targetJID, streamHosts);
|
|
|
|
|
|
|
|
// send initiation packet
|
2017-01-03 11:12:34 +01:00
|
|
|
Stanza response = connection.createStanzaCollectorAndSend(initiation).nextResultOrThrow(
|
2014-02-17 23:58:40 +01:00
|
|
|
getTargetResponseTimeout());
|
|
|
|
|
|
|
|
// extract used stream host from response
|
|
|
|
StreamHostUsed streamHostUsed = ((Bytestream) response).getUsedHost();
|
|
|
|
StreamHost usedStreamHost = initiation.getStreamHost(streamHostUsed.getJID());
|
|
|
|
|
|
|
|
if (usedStreamHost == null) {
|
2014-03-12 11:50:05 +01:00
|
|
|
throw new SmackException("Remote user responded with unknown host");
|
2014-02-17 23:58:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// build SOCKS5 client
|
|
|
|
Socks5Client socks5Client = new Socks5ClientForInitiator(usedStreamHost, digest,
|
2015-05-24 17:18:48 +02:00
|
|
|
connection, sessionID, targetJID);
|
2014-02-17 23:58:40 +01:00
|
|
|
|
|
|
|
// establish connection to proxy
|
|
|
|
Socket socket = socks5Client.getSocket(getProxyConnectionTimeout());
|
|
|
|
|
|
|
|
// remember last working SOCKS5 proxy to prioritize it for next request
|
|
|
|
this.lastWorkingProxy = usedStreamHost.getJID();
|
|
|
|
|
|
|
|
// negotiation successful, return the output stream
|
|
|
|
return new Socks5BytestreamSession(socket, usedStreamHost.getJID().equals(
|
2015-05-24 17:18:48 +02:00
|
|
|
connection.getUser()));
|
2014-02-17 23:58:40 +01:00
|
|
|
|
|
|
|
}
|
|
|
|
catch (TimeoutException e) {
|
|
|
|
throw new IOException("Timeout while connecting to SOCKS5 proxy");
|
|
|
|
}
|
|
|
|
finally {
|
|
|
|
|
|
|
|
// remove transfer digest if output stream is returned or an exception
|
|
|
|
// occurred
|
|
|
|
socks5Proxy.removeTransfer(digest);
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns <code>true</code> if the given target JID supports feature SOCKS5 Bytestream.
|
2018-05-09 23:06:12 +02:00
|
|
|
*
|
2014-02-17 23:58:40 +01:00
|
|
|
* @param targetJID the target JID
|
|
|
|
* @return <code>true</code> if the given target JID supports feature SOCKS5 Bytestream
|
|
|
|
* otherwise <code>false</code>
|
2018-05-09 23:06:12 +02:00
|
|
|
* @throws XMPPErrorException
|
|
|
|
* @throws NoResponseException
|
|
|
|
* @throws NotConnectedException
|
|
|
|
* @throws InterruptedException
|
2014-02-17 23:58:40 +01:00
|
|
|
*/
|
2015-02-14 17:15:02 +01:00
|
|
|
private boolean supportsSocks5(Jid targetJID) throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
|
2015-05-24 17:18:48 +02:00
|
|
|
return ServiceDiscoveryManager.getInstanceFor(connection()).supportsFeature(targetJID, Bytestream.NAMESPACE);
|
2014-02-17 23:58:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a list of JIDs of SOCKS5 proxies by querying the XMPP server. The SOCKS5 proxies are
|
|
|
|
* in the same order as returned by the XMPP server.
|
2018-05-09 23:06:12 +02:00
|
|
|
*
|
2014-02-17 23:58:40 +01:00
|
|
|
* @return list of JIDs of SOCKS5 proxies
|
2014-03-12 11:50:05 +01:00
|
|
|
* @throws XMPPErrorException if there was an error querying the XMPP server for SOCKS5 proxies
|
|
|
|
* @throws NoResponseException if there was no response from the server.
|
2018-05-09 23:06:12 +02:00
|
|
|
* @throws NotConnectedException
|
|
|
|
* @throws InterruptedException
|
2014-02-17 23:58:40 +01:00
|
|
|
*/
|
2017-07-03 10:35:46 +02:00
|
|
|
public List<Jid> determineProxies() throws NoResponseException, XMPPErrorException, NotConnectedException, InterruptedException {
|
2015-05-24 17:18:48 +02:00
|
|
|
XMPPConnection connection = connection();
|
|
|
|
ServiceDiscoveryManager serviceDiscoveryManager = ServiceDiscoveryManager.getInstanceFor(connection);
|
2014-02-17 23:58:40 +01:00
|
|
|
|
2015-02-14 17:15:02 +01:00
|
|
|
List<Jid> proxies = new ArrayList<>();
|
2014-02-17 23:58:40 +01:00
|
|
|
|
2014-03-23 14:57:18 +01:00
|
|
|
// get all items from XMPP server
|
2015-05-24 22:36:11 +02:00
|
|
|
DiscoverItems discoverItems = serviceDiscoveryManager.discoverItems(connection.getXMPPServiceDomain());
|
2014-02-17 23:58:40 +01:00
|
|
|
|
|
|
|
// query all items if they are SOCKS5 proxies
|
2014-03-24 22:31:42 +01:00
|
|
|
for (Item item : discoverItems.getItems()) {
|
2014-02-17 23:58:40 +01:00
|
|
|
// skip blacklisted servers
|
|
|
|
if (this.proxyBlacklist.contains(item.getEntityID())) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2014-03-23 14:57:18 +01:00
|
|
|
DiscoverInfo proxyInfo;
|
2014-02-17 23:58:40 +01:00
|
|
|
try {
|
|
|
|
proxyInfo = serviceDiscoveryManager.discoverInfo(item.getEntityID());
|
2014-03-23 14:57:18 +01:00
|
|
|
}
|
2017-05-23 16:45:04 +02:00
|
|
|
catch (NoResponseException | XMPPErrorException e) {
|
2014-03-23 14:57:18 +01:00
|
|
|
// blacklist errornous server
|
|
|
|
proxyBlacklist.add(item.getEntityID());
|
|
|
|
continue;
|
|
|
|
}
|
2014-02-17 23:58:40 +01:00
|
|
|
|
2014-10-31 22:22:22 +01:00
|
|
|
if (proxyInfo.hasIdentity("proxy", "bytestreams")) {
|
|
|
|
proxies.add(item.getEntityID());
|
|
|
|
} else {
|
2014-03-23 14:57:18 +01:00
|
|
|
/*
|
|
|
|
* server is not a SOCKS5 proxy, blacklist server to skip next time a Socks5
|
|
|
|
* bytestream should be established
|
|
|
|
*/
|
2014-02-17 23:58:40 +01:00
|
|
|
this.proxyBlacklist.add(item.getEntityID());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return proxies;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a list of stream hosts containing the IP address an the port for the given list of
|
|
|
|
* SOCKS5 proxy JIDs. The order of the returned list is the same as the given list of JIDs
|
|
|
|
* excluding all SOCKS5 proxies who's network settings could not be determined. If a local
|
|
|
|
* SOCKS5 proxy is running it will be the first item in the list returned.
|
2018-05-09 23:06:12 +02:00
|
|
|
*
|
2014-02-17 23:58:40 +01:00
|
|
|
* @param proxies a list of SOCKS5 proxy JIDs
|
|
|
|
* @return a list of stream hosts containing the IP address an the port
|
|
|
|
*/
|
2015-02-14 17:15:02 +01:00
|
|
|
private List<StreamHost> determineStreamHostInfos(List<Jid> proxies) {
|
2015-05-24 17:18:48 +02:00
|
|
|
XMPPConnection connection = connection();
|
2017-12-13 23:10:11 +01:00
|
|
|
List<StreamHost> streamHosts = new ArrayList<>();
|
2014-02-17 23:58:40 +01:00
|
|
|
|
|
|
|
// add local proxy on first position if exists
|
|
|
|
List<StreamHost> localProxies = getLocalStreamHost();
|
|
|
|
if (localProxies != null) {
|
|
|
|
streamHosts.addAll(localProxies);
|
|
|
|
}
|
|
|
|
|
|
|
|
// query SOCKS5 proxies for network settings
|
2015-02-14 17:15:02 +01:00
|
|
|
for (Jid proxy : proxies) {
|
2014-02-17 23:58:40 +01:00
|
|
|
Bytestream streamHostRequest = createStreamHostRequest(proxy);
|
|
|
|
try {
|
2017-12-13 23:10:11 +01:00
|
|
|
Bytestream response = connection.createStanzaCollectorAndSend(
|
2014-02-18 15:05:19 +01:00
|
|
|
streamHostRequest).nextResultOrThrow();
|
2014-02-17 23:58:40 +01:00
|
|
|
streamHosts.addAll(response.getStreamHosts());
|
|
|
|
}
|
2014-03-12 11:50:05 +01:00
|
|
|
catch (Exception e) {
|
2014-02-17 23:58:40 +01:00
|
|
|
// blacklist errornous proxies
|
|
|
|
this.proxyBlacklist.add(proxy);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return streamHosts;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-03-31 14:17:30 +02:00
|
|
|
* Returns a IQ stanza to query a SOCKS5 proxy its network settings.
|
2018-05-09 23:06:12 +02:00
|
|
|
*
|
2014-02-17 23:58:40 +01:00
|
|
|
* @param proxy the proxy to query
|
2018-03-31 14:17:30 +02:00
|
|
|
* @return IQ stanza to query a SOCKS5 proxy its network settings
|
2014-02-17 23:58:40 +01:00
|
|
|
*/
|
2015-03-23 09:27:15 +01:00
|
|
|
private static Bytestream createStreamHostRequest(Jid proxy) {
|
2014-02-17 23:58:40 +01:00
|
|
|
Bytestream request = new Bytestream();
|
2014-06-06 02:20:45 +02:00
|
|
|
request.setType(IQ.Type.get);
|
2014-02-17 23:58:40 +01:00
|
|
|
request.setTo(proxy);
|
|
|
|
return request;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the stream host information of the local SOCKS5 proxy containing the IP address and
|
|
|
|
* the port or null if local SOCKS5 proxy is not running.
|
2018-05-09 23:06:12 +02:00
|
|
|
*
|
2014-02-17 23:58:40 +01:00
|
|
|
* @return the stream host information of the local SOCKS5 proxy or null if local SOCKS5 proxy
|
|
|
|
* is not running
|
|
|
|
*/
|
2017-07-03 10:35:46 +02:00
|
|
|
public List<StreamHost> getLocalStreamHost() {
|
2015-05-24 17:18:48 +02:00
|
|
|
XMPPConnection connection = connection();
|
2014-02-17 23:58:40 +01:00
|
|
|
// get local proxy singleton
|
|
|
|
Socks5Proxy socks5Server = Socks5Proxy.getSocks5Proxy();
|
|
|
|
|
2014-08-19 18:21:07 +02:00
|
|
|
if (!socks5Server.isRunning()) {
|
|
|
|
// server is not running
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
List<String> addresses = socks5Server.getLocalAddresses();
|
|
|
|
if (addresses.isEmpty()) {
|
|
|
|
// local address could not be determined
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
final int port = socks5Server.getPort();
|
|
|
|
|
2017-12-13 23:10:11 +01:00
|
|
|
List<StreamHost> streamHosts = new ArrayList<>();
|
2014-08-19 18:21:07 +02:00
|
|
|
outerloop: for (String address : addresses) {
|
|
|
|
// Prevent loopback addresses from appearing as streamhost
|
2014-08-20 18:12:48 +02:00
|
|
|
final String[] loopbackAddresses = { "127.0.0.1", "0:0:0:0:0:0:0:1", "::1" };
|
2014-08-19 18:21:07 +02:00
|
|
|
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;
|
2014-02-17 23:58:40 +01:00
|
|
|
}
|
|
|
|
}
|
2014-08-19 19:22:08 +02:00
|
|
|
streamHosts.add(new StreamHost(connection.getUser(), address, port));
|
2014-02-17 23:58:40 +01:00
|
|
|
}
|
2014-08-19 18:21:07 +02:00
|
|
|
return streamHosts;
|
2014-02-17 23:58:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-03-31 14:17:30 +02:00
|
|
|
* Returns a SOCKS5 Bytestream initialization request stanza with the given session ID
|
2014-02-17 23:58:40 +01:00
|
|
|
* containing the given stream hosts for the given target JID.
|
2018-05-09 23:06:12 +02:00
|
|
|
*
|
2014-02-17 23:58:40 +01:00
|
|
|
* @param sessionID the session ID for the SOCKS5 Bytestream
|
|
|
|
* @param targetJID the target JID of SOCKS5 Bytestream request
|
|
|
|
* @param streamHosts a list of SOCKS5 proxies the target should connect to
|
|
|
|
* @return a SOCKS5 Bytestream initialization request packet
|
|
|
|
*/
|
2015-03-23 09:27:15 +01:00
|
|
|
private static Bytestream createBytestreamInitiation(String sessionID, Jid targetJID,
|
2014-02-17 23:58:40 +01:00
|
|
|
List<StreamHost> streamHosts) {
|
|
|
|
Bytestream initiation = new Bytestream(sessionID);
|
|
|
|
|
|
|
|
// add all stream hosts
|
|
|
|
for (StreamHost streamHost : streamHosts) {
|
|
|
|
initiation.addStreamHost(streamHost);
|
|
|
|
}
|
|
|
|
|
2014-06-06 02:20:45 +02:00
|
|
|
initiation.setType(IQ.Type.set);
|
2014-02-17 23:58:40 +01:00
|
|
|
initiation.setTo(targetJID);
|
|
|
|
|
|
|
|
return initiation;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2015-02-12 12:13:19 +01:00
|
|
|
* Responses to the given packet's sender with an XMPP error that a SOCKS5 Bytestream is not
|
2014-02-17 23:58:40 +01:00
|
|
|
* accepted.
|
2014-07-12 13:50:50 +02:00
|
|
|
* <p>
|
|
|
|
* Specified in XEP-65 5.3.1 (Example 13)
|
|
|
|
* </p>
|
2018-05-09 23:06:12 +02:00
|
|
|
*
|
2018-04-23 21:06:35 +02:00
|
|
|
* @param packet Stanza that should be answered with a not-acceptable error
|
2018-05-09 23:06:12 +02:00
|
|
|
* @throws NotConnectedException
|
|
|
|
* @throws InterruptedException
|
2014-02-17 23:58:40 +01:00
|
|
|
*/
|
2015-02-14 09:43:44 +01:00
|
|
|
protected void replyRejectPacket(IQ packet) throws NotConnectedException, InterruptedException {
|
2018-04-07 21:25:40 +02:00
|
|
|
StanzaError.Builder xmppError = StanzaError.getBuilder(StanzaError.Condition.not_acceptable);
|
2014-02-17 23:58:40 +01:00
|
|
|
IQ errorIQ = IQ.createErrorResponse(packet, xmppError);
|
2015-05-24 17:18:48 +02:00
|
|
|
connection().sendStanza(errorIQ);
|
2014-02-17 23:58:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Activates the Socks5BytestreamManager by registering the SOCKS5 Bytestream initialization
|
|
|
|
* listener and enabling the SOCKS5 Bytestream feature.
|
|
|
|
*/
|
|
|
|
private void activate() {
|
|
|
|
// register bytestream initiation packet listener
|
2015-05-24 17:18:48 +02:00
|
|
|
connection().registerIQRequestHandler(initiationListener);
|
2014-02-17 23:58:40 +01:00
|
|
|
|
|
|
|
// enable SOCKS5 feature
|
|
|
|
enableService();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Adds the SOCKS5 Bytestream feature to the service discovery.
|
|
|
|
*/
|
|
|
|
private void enableService() {
|
2015-05-24 17:18:48 +02:00
|
|
|
ServiceDiscoveryManager manager = ServiceDiscoveryManager.getInstanceFor(connection());
|
2014-07-05 11:58:13 +02:00
|
|
|
manager.addFeature(Bytestream.NAMESPACE);
|
2014-02-17 23:58:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a new unique session ID.
|
2018-05-09 23:06:12 +02:00
|
|
|
*
|
2014-02-17 23:58:40 +01:00
|
|
|
* @return a new unique session ID
|
|
|
|
*/
|
2015-03-23 09:27:15 +01:00
|
|
|
private static String getNextSessionID() {
|
2014-02-17 23:58:40 +01:00
|
|
|
StringBuilder buffer = new StringBuilder();
|
|
|
|
buffer.append(SESSION_ID_PREFIX);
|
|
|
|
buffer.append(Math.abs(randomGenerator.nextLong()));
|
|
|
|
return buffer.toString();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the XMPP connection.
|
2018-05-09 23:06:12 +02:00
|
|
|
*
|
2014-02-17 23:58:40 +01:00
|
|
|
* @return the XMPP connection
|
|
|
|
*/
|
2014-03-10 09:45:50 +01:00
|
|
|
protected XMPPConnection getConnection() {
|
2015-05-24 17:18:48 +02:00
|
|
|
return connection();
|
2014-02-17 23:58:40 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the {@link BytestreamListener} that should be informed if a SOCKS5 Bytestream request
|
|
|
|
* from the given initiator JID is received.
|
2018-05-09 23:06:12 +02:00
|
|
|
*
|
2014-02-17 23:58:40 +01:00
|
|
|
* @param initiator the initiator's JID
|
|
|
|
* @return the listener
|
|
|
|
*/
|
2015-02-14 17:15:02 +01:00
|
|
|
protected BytestreamListener getUserListener(Jid initiator) {
|
2014-02-17 23:58:40 +01:00
|
|
|
return this.userListeners.get(initiator);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a list of {@link BytestreamListener} that are informed if there are no listeners for
|
|
|
|
* a specific initiator.
|
2018-05-09 23:06:12 +02:00
|
|
|
*
|
2014-02-17 23:58:40 +01:00
|
|
|
* @return list of listeners
|
|
|
|
*/
|
|
|
|
protected List<BytestreamListener> getAllRequestListeners() {
|
|
|
|
return this.allRequestListeners;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the list of session IDs that should be ignored by the InitialtionListener
|
2018-05-09 23:06:12 +02:00
|
|
|
*
|
2014-02-17 23:58:40 +01:00
|
|
|
* @return list of session IDs
|
|
|
|
*/
|
|
|
|
protected List<String> getIgnoredBytestreamRequests() {
|
|
|
|
return ignoredBytestreamRequests;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|