1d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen/**
2d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License");
3d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * you may not use this file except in compliance with the License.
4d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * You may obtain a copy of the License at
5d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen *
6d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen *     http://www.apache.org/licenses/LICENSE-2.0
7d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen *
8d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Unless required by applicable law or agreed to in writing, software
9d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * distributed under the License is distributed on an "AS IS" BASIS,
10d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * See the License for the specific language governing permissions and
12d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * limitations under the License.
13d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */
14d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenpackage org.jivesoftware.smackx.bytestreams.socks5;
15d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
16d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.io.IOException;
17d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.lang.ref.WeakReference;
18d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.net.Socket;
19d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.ArrayList;
20d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.Collections;
21d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.Iterator;
22d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.LinkedList;
23d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.List;
24d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.Map;
25d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.Random;
26d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.WeakHashMap;
27d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.concurrent.ConcurrentHashMap;
28d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.concurrent.TimeoutException;
29d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
30d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smack.AbstractConnectionListener;
31d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smack.Connection;
32d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smack.ConnectionCreationListener;
33d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smack.XMPPException;
34d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smack.packet.IQ;
35d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smack.packet.Packet;
36d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smack.packet.XMPPError;
37d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smack.util.SyncPacketSend;
38d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smackx.ServiceDiscoveryManager;
39d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smackx.bytestreams.BytestreamListener;
40d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smackx.bytestreams.BytestreamManager;
41d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream;
42d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream.StreamHost;
43d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream.StreamHostUsed;
44d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smackx.filetransfer.FileTransferManager;
45d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smackx.packet.DiscoverInfo;
46d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smackx.packet.DiscoverItems;
47d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smackx.packet.DiscoverInfo.Identity;
48d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smackx.packet.DiscoverItems.Item;
49d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
50d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen/**
51d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * The Socks5BytestreamManager class handles establishing SOCKS5 Bytestreams as specified in the <a
52d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * href="http://xmpp.org/extensions/xep-0065.html">XEP-0065</a>.
53d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * <p>
54d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * A SOCKS5 Bytestream is negotiated partly over the XMPP XML stream and partly over a separate
55d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * socket. The actual transfer though takes place over a separately created socket.
56d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * <p>
57d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * A SOCKS5 Bytestream generally has three parties, the initiator, the target, and the stream host.
58d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * The stream host is a specialized SOCKS5 proxy setup on a server, or, the initiator can act as the
59d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * stream host.
60d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * <p>
61d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * To establish a SOCKS5 Bytestream invoke the {@link #establishSession(String)} method. This will
62d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * negotiate a SOCKS5 Bytestream with the given target JID and return a socket.
63d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * <p>
64d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * If a session ID for the SOCKS5 Bytestream was already negotiated (e.g. while negotiating a file
65d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * transfer) invoke {@link #establishSession(String, String)}.
66d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * <p>
67d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * To handle incoming SOCKS5 Bytestream requests add an {@link Socks5BytestreamListener} to the
68d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * manager. There are two ways to add this listener. If you want to be informed about incoming
69d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * SOCKS5 Bytestreams from a specific user add the listener by invoking
70d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * {@link #addIncomingBytestreamListener(BytestreamListener, String)}. If the listener should
71d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * respond to all SOCKS5 Bytestream requests invoke
72d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * {@link #addIncomingBytestreamListener(BytestreamListener)}.
73d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * <p>
74d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Note that the registered {@link Socks5BytestreamListener} will NOT be notified on incoming Socks5
75d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * bytestream requests sent in the context of <a
76d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * href="http://xmpp.org/extensions/xep-0096.html">XEP-0096</a> file transfer. (See
77d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * {@link FileTransferManager})
78d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * <p>
79d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * If no {@link Socks5BytestreamListener}s are registered, all incoming SOCKS5 Bytestream requests
80d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * will be rejected by returning a &lt;not-acceptable/&gt; error to the initiator.
81d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen *
82d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @author Henning Staib
83d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */
84d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenpublic final class Socks5BytestreamManager implements BytestreamManager {
85d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
86d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /*
87d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * create a new Socks5BytestreamManager and register a shutdown listener on every established
88d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * connection
89d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
90d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    static {
91d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        Connection.addConnectionCreationListener(new ConnectionCreationListener() {
92d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
93d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            public void connectionCreated(final Connection connection) {
94d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                final Socks5BytestreamManager manager;
95d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                manager = Socks5BytestreamManager.getBytestreamManager(connection);
96d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
97d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                // register shutdown listener
98d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                connection.addConnectionListener(new AbstractConnectionListener() {
99d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
100d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    public void connectionClosed() {
101d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                        manager.disableService();
102d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    }
103d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
104d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    public void connectionClosedOnError(Exception e) {
105d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                        manager.disableService();
106d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    }
107d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
108d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    public void reconnectionSuccessful() {
109d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                        managers.put(connection, manager);
110d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    }
111d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
112d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                });
113d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            }
114d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
115d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        });
116d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
117d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
118d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
119d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * The XMPP namespace of the SOCKS5 Bytestream
120d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
121d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    public static final String NAMESPACE = "http://jabber.org/protocol/bytestreams";
122d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
123d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /* prefix used to generate session IDs */
124d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private static final String SESSION_ID_PREFIX = "js5_";
125d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
126d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /* random generator to create session IDs */
127d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private final static Random randomGenerator = new Random();
128d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
129d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /* stores one Socks5BytestreamManager for each XMPP connection */
130d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private final static Map<Connection, Socks5BytestreamManager> managers = new WeakHashMap<Connection, Socks5BytestreamManager>();
131d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
132d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /* XMPP connection */
133d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private final Connection connection;
134d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
135d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /*
136d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * assigns a user to a listener that is informed if a bytestream request for this user is
137d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * received
138d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
139d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private final Map<String, BytestreamListener> userListeners = new ConcurrentHashMap<String, BytestreamListener>();
140d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
141d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /*
142d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * list of listeners that respond to all bytestream requests if there are not user specific
143d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * listeners for that request
144d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
145d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private final List<BytestreamListener> allRequestListeners = Collections.synchronizedList(new LinkedList<BytestreamListener>());
146d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
147d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /* listener that handles all incoming bytestream requests */
148d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private final InitiationListener initiationListener;
149d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
150d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /* timeout to wait for the response to the SOCKS5 Bytestream initialization request */
151d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private int targetResponseTimeout = 10000;
152d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
153d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /* timeout for connecting to the SOCKS5 proxy selected by the target */
154d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private int proxyConnectionTimeout = 10000;
155d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
156d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /* blacklist of errornous SOCKS5 proxies */
157d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private final List<String> proxyBlacklist = Collections.synchronizedList(new LinkedList<String>());
158d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
159d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /* remember the last proxy that worked to prioritize it */
160d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private String lastWorkingProxy = null;
161d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
162d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /* flag to enable/disable prioritization of last working proxy */
163d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private boolean proxyPrioritizationEnabled = true;
164d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
165d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /*
166d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * list containing session IDs of SOCKS5 Bytestream initialization packets that should be
167d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * ignored by the InitiationListener
168d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
169d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private List<String> ignoredBytestreamRequests = Collections.synchronizedList(new LinkedList<String>());
170d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
171d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
172d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Returns the Socks5BytestreamManager to handle SOCKS5 Bytestreams for a given
173d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * {@link Connection}.
174d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * <p>
175d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * If no manager exists a new is created and initialized.
176d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
177d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param connection the XMPP connection or <code>null</code> if given connection is
178d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *        <code>null</code>
179d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @return the Socks5BytestreamManager for the given XMPP connection
180d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
181d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    public static synchronized Socks5BytestreamManager getBytestreamManager(Connection connection) {
182d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (connection == null) {
183d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            return null;
184d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
185d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        Socks5BytestreamManager manager = managers.get(connection);
186d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (manager == null) {
187d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            manager = new Socks5BytestreamManager(connection);
188d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            managers.put(connection, manager);
189d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            manager.activate();
190d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
191d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        return manager;
192d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
193d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
194d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
195d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Private constructor.
196d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
197d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param connection the XMPP connection
198d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
199d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private Socks5BytestreamManager(Connection connection) {
200d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        this.connection = connection;
201d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        this.initiationListener = new InitiationListener(this);
202d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
203d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
204d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
205d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Adds BytestreamListener that is called for every incoming SOCKS5 Bytestream request unless
206d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * there is a user specific BytestreamListener registered.
207d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * <p>
208d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * If no listeners are registered all SOCKS5 Bytestream request are rejected with a
209d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * &lt;not-acceptable/&gt; error.
210d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * <p>
211d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Note that the registered {@link BytestreamListener} will NOT be notified on incoming Socks5
212d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * bytestream requests sent in the context of <a
213d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * href="http://xmpp.org/extensions/xep-0096.html">XEP-0096</a> file transfer. (See
214d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * {@link FileTransferManager})
215d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
216d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param listener the listener to register
217d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
218d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    public void addIncomingBytestreamListener(BytestreamListener listener) {
219d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        this.allRequestListeners.add(listener);
220d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
221d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
222d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
223d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Removes the given listener from the list of listeners for all incoming SOCKS5 Bytestream
224d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * requests.
225d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
226d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param listener the listener to remove
227d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
228d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    public void removeIncomingBytestreamListener(BytestreamListener listener) {
229d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        this.allRequestListeners.remove(listener);
230d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
231d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
232d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
233d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Adds BytestreamListener that is called for every incoming SOCKS5 Bytestream request from the
234d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * given user.
235d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * <p>
236d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Use this method if you are awaiting an incoming SOCKS5 Bytestream request from a specific
237d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * user.
238d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * <p>
239d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * If no listeners are registered all SOCKS5 Bytestream request are rejected with a
240d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * &lt;not-acceptable/&gt; error.
241d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * <p>
242d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Note that the registered {@link BytestreamListener} will NOT be notified on incoming Socks5
243d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * bytestream requests sent in the context of <a
244d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * href="http://xmpp.org/extensions/xep-0096.html">XEP-0096</a> file transfer. (See
245d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * {@link FileTransferManager})
246d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
247d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param listener the listener to register
248d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param initiatorJID the JID of the user that wants to establish a SOCKS5 Bytestream
249d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
250d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    public void addIncomingBytestreamListener(BytestreamListener listener, String initiatorJID) {
251d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        this.userListeners.put(initiatorJID, listener);
252d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
253d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
254d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
255d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Removes the listener for the given user.
256d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
257d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param initiatorJID the JID of the user the listener should be removed
258d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
259d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    public void removeIncomingBytestreamListener(String initiatorJID) {
260d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        this.userListeners.remove(initiatorJID);
261d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
262d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
263d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
264d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Use this method to ignore the next incoming SOCKS5 Bytestream request containing the given
265d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * session ID. No listeners will be notified for this request and and no error will be returned
266d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * to the initiator.
267d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * <p>
268d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * This method should be used if you are awaiting a SOCKS5 Bytestream request as a reply to
269d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * another packet (e.g. file transfer).
270d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
271d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param sessionID to be ignored
272d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
273d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    public void ignoreBytestreamRequestOnce(String sessionID) {
274d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        this.ignoredBytestreamRequests.add(sessionID);
275d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
276d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
277d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
278d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Disables the SOCKS5 Bytestream manager by removing the SOCKS5 Bytestream feature from the
279d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * service discovery, disabling the listener for SOCKS5 Bytestream initiation requests and
280d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * resetting its internal state.
281d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * <p>
282d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * To re-enable the SOCKS5 Bytestream feature invoke {@link #getBytestreamManager(Connection)}.
283d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Using the file transfer API will automatically re-enable the SOCKS5 Bytestream feature.
284d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
285d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    public synchronized void disableService() {
286d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
287d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        // remove initiation packet listener
288d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        this.connection.removePacketListener(this.initiationListener);
289d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
290d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        // shutdown threads
291d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        this.initiationListener.shutdown();
292d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
293d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        // clear listeners
294d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        this.allRequestListeners.clear();
295d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        this.userListeners.clear();
296d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
297d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        // reset internal state
298d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        this.lastWorkingProxy = null;
299d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        this.proxyBlacklist.clear();
300d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        this.ignoredBytestreamRequests.clear();
301d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
302d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        // remove manager from static managers map
303d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        managers.remove(this.connection);
304d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
305d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        // shutdown local SOCKS5 proxy if there are no more managers for other connections
306d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (managers.size() == 0) {
307d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            Socks5Proxy.getSocks5Proxy().stop();
308d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
309d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
310d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        // remove feature from service discovery
311d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        ServiceDiscoveryManager serviceDiscoveryManager = ServiceDiscoveryManager.getInstanceFor(this.connection);
312d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
313d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        // check if service discovery is not already disposed by connection shutdown
314d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (serviceDiscoveryManager != null) {
315d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            serviceDiscoveryManager.removeFeature(NAMESPACE);
316d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
317d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
318d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
319d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
320d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
321d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Returns the timeout to wait for the response to the SOCKS5 Bytestream initialization request.
322d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Default is 10000ms.
323d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
324d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @return the timeout to wait for the response to the SOCKS5 Bytestream initialization request
325d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
326d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    public int getTargetResponseTimeout() {
327d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (this.targetResponseTimeout <= 0) {
328d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            this.targetResponseTimeout = 10000;
329d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
330d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        return targetResponseTimeout;
331d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
332d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
333d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
334d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Sets the timeout to wait for the response to the SOCKS5 Bytestream initialization request.
335d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Default is 10000ms.
336d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
337d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param targetResponseTimeout the timeout to set
338d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
339d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    public void setTargetResponseTimeout(int targetResponseTimeout) {
340d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        this.targetResponseTimeout = targetResponseTimeout;
341d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
342d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
343d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
344d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Returns the timeout for connecting to the SOCKS5 proxy selected by the target. Default is
345d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * 10000ms.
346d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
347d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @return the timeout for connecting to the SOCKS5 proxy selected by the target
348d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
349d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    public int getProxyConnectionTimeout() {
350d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (this.proxyConnectionTimeout <= 0) {
351d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            this.proxyConnectionTimeout = 10000;
352d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
353d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        return proxyConnectionTimeout;
354d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
355d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
356d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
357d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Sets the timeout for connecting to the SOCKS5 proxy selected by the target. Default is
358d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * 10000ms.
359d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
360d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param proxyConnectionTimeout the timeout to set
361d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
362d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    public void setProxyConnectionTimeout(int proxyConnectionTimeout) {
363d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        this.proxyConnectionTimeout = proxyConnectionTimeout;
364d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
365d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
366d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
367d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Returns if the prioritization of the last working SOCKS5 proxy on successive SOCKS5
368d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Bytestream connections is enabled. Default is <code>true</code>.
369d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
370d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @return <code>true</code> if prioritization is enabled, <code>false</code> otherwise
371d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
372d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    public boolean isProxyPrioritizationEnabled() {
373d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        return proxyPrioritizationEnabled;
374d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
375d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
376d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
377d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Enable/disable the prioritization of the last working SOCKS5 proxy on successive SOCKS5
378d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Bytestream connections.
379d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
380d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param proxyPrioritizationEnabled enable/disable the prioritization of the last working
381d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *        SOCKS5 proxy
382d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
383d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    public void setProxyPrioritizationEnabled(boolean proxyPrioritizationEnabled) {
384d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        this.proxyPrioritizationEnabled = proxyPrioritizationEnabled;
385d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
386d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
387d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
388d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Establishes a SOCKS5 Bytestream with the given user and returns the Socket to send/receive
389d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * data to/from the user.
390d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * <p>
391d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Use this method to establish SOCKS5 Bytestreams to users accepting all incoming Socks5
392d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * bytestream requests since this method doesn't provide a way to tell the user something about
393d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * the data to be sent.
394d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * <p>
395d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * To establish a SOCKS5 Bytestream after negotiation the kind of data to be sent (e.g. file
396d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * transfer) use {@link #establishSession(String, String)}.
397d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
398d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param targetJID the JID of the user a SOCKS5 Bytestream should be established
399d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @return the Socket to send/receive data to/from the user
400d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @throws XMPPException if the user doesn't support or accept SOCKS5 Bytestreams, if no Socks5
401d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *         Proxy could be found, if the user couldn't connect to any of the SOCKS5 Proxies
402d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @throws IOException if the bytestream could not be established
403d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @throws InterruptedException if the current thread was interrupted while waiting
404d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
405d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    public Socks5BytestreamSession establishSession(String targetJID) throws XMPPException,
406d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    IOException, InterruptedException {
407d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        String sessionID = getNextSessionID();
408d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        return establishSession(targetJID, sessionID);
409d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
410d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
411d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
412d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Establishes a SOCKS5 Bytestream with the given user using the given session ID and returns
413d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * the Socket to send/receive data to/from the user.
414d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
415d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param targetJID the JID of the user a SOCKS5 Bytestream should be established
416d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param sessionID the session ID for the SOCKS5 Bytestream request
417d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @return the Socket to send/receive data to/from the user
418d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @throws XMPPException if the user doesn't support or accept SOCKS5 Bytestreams, if no Socks5
419d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *         Proxy could be found, if the user couldn't connect to any of the SOCKS5 Proxies
420d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @throws IOException if the bytestream could not be established
421d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @throws InterruptedException if the current thread was interrupted while waiting
422d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
423d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    public Socks5BytestreamSession establishSession(String targetJID, String sessionID)
424d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    throws XMPPException, IOException, InterruptedException {
425d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
426d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        XMPPException discoveryException = null;
427d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        // check if target supports SOCKS5 Bytestream
428d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (!supportsSocks5(targetJID)) {
429d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            throw new XMPPException(targetJID + " doesn't support SOCKS5 Bytestream");
430d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
431d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
432d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        List<String> proxies = new ArrayList<String>();
433d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        // determine SOCKS5 proxies from XMPP-server
434d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        try {
435d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            proxies.addAll(determineProxies());
436d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        } catch (XMPPException e) {
437d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            // don't abort here, just remember the exception thrown by determineProxies()
438d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            // determineStreamHostInfos() will at least add the local Socks5 proxy (if enabled)
439d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            discoveryException = e;
440d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
441d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
442d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        // determine address and port of each proxy
443d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        List<StreamHost> streamHosts = determineStreamHostInfos(proxies);
444d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
445d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (streamHosts.isEmpty()) {
446d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            throw discoveryException != null ? discoveryException : new XMPPException("no SOCKS5 proxies available");
447d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
448d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
449d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        // compute digest
450d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        String digest = Socks5Utils.createDigest(sessionID, this.connection.getUser(), targetJID);
451d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
452d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        // prioritize last working SOCKS5 proxy if exists
453d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (this.proxyPrioritizationEnabled && this.lastWorkingProxy != null) {
454d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            StreamHost selectedStreamHost = null;
455d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            for (StreamHost streamHost : streamHosts) {
456d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                if (streamHost.getJID().equals(this.lastWorkingProxy)) {
457d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    selectedStreamHost = streamHost;
458d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    break;
459d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                }
460d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            }
461d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            if (selectedStreamHost != null) {
462d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                streamHosts.remove(selectedStreamHost);
463d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                streamHosts.add(0, selectedStreamHost);
464d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            }
465d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
466d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
467d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
468d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        Socks5Proxy socks5Proxy = Socks5Proxy.getSocks5Proxy();
469d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        try {
470d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
471d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            // add transfer digest to local proxy to make transfer valid
472d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            socks5Proxy.addTransfer(digest);
473d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
474d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            // create initiation packet
475d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            Bytestream initiation = createBytestreamInitiation(sessionID, targetJID, streamHosts);
476d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
477d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            // send initiation packet
478d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            Packet response = SyncPacketSend.getReply(this.connection, initiation,
479d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                            getTargetResponseTimeout());
480d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
481d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            // extract used stream host from response
482d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            StreamHostUsed streamHostUsed = ((Bytestream) response).getUsedHost();
483d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            StreamHost usedStreamHost = initiation.getStreamHost(streamHostUsed.getJID());
484d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
485d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            if (usedStreamHost == null) {
486d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                throw new XMPPException("Remote user responded with unknown host");
487d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            }
488d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
489d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            // build SOCKS5 client
490d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            Socks5Client socks5Client = new Socks5ClientForInitiator(usedStreamHost, digest,
491d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                            this.connection, sessionID, targetJID);
492d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
493d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            // establish connection to proxy
494d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            Socket socket = socks5Client.getSocket(getProxyConnectionTimeout());
495d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
496d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            // remember last working SOCKS5 proxy to prioritize it for next request
497d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            this.lastWorkingProxy = usedStreamHost.getJID();
498d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
499d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            // negotiation successful, return the output stream
500d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            return new Socks5BytestreamSession(socket, usedStreamHost.getJID().equals(
501d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                            this.connection.getUser()));
502d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
503d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
504d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        catch (TimeoutException e) {
505d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            throw new IOException("Timeout while connecting to SOCKS5 proxy");
506d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
507d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        finally {
508d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
509d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            // remove transfer digest if output stream is returned or an exception
510d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            // occurred
511d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            socks5Proxy.removeTransfer(digest);
512d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
513d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
514d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
515d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
516d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
517d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Returns <code>true</code> if the given target JID supports feature SOCKS5 Bytestream.
518d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
519d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param targetJID the target JID
520d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @return <code>true</code> if the given target JID supports feature SOCKS5 Bytestream
521d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *         otherwise <code>false</code>
522d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @throws XMPPException if there was an error querying target for supported features
523d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
524d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private boolean supportsSocks5(String targetJID) throws XMPPException {
525d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        ServiceDiscoveryManager serviceDiscoveryManager = ServiceDiscoveryManager.getInstanceFor(this.connection);
526d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        DiscoverInfo discoverInfo = serviceDiscoveryManager.discoverInfo(targetJID);
527d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        return discoverInfo.containsFeature(NAMESPACE);
528d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
529d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
530d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
531d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Returns a list of JIDs of SOCKS5 proxies by querying the XMPP server. The SOCKS5 proxies are
532d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * in the same order as returned by the XMPP server.
533d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
534d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @return list of JIDs of SOCKS5 proxies
535d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @throws XMPPException if there was an error querying the XMPP server for SOCKS5 proxies
536d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
537d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private List<String> determineProxies() throws XMPPException {
538d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        ServiceDiscoveryManager serviceDiscoveryManager = ServiceDiscoveryManager.getInstanceFor(this.connection);
539d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
540d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        List<String> proxies = new ArrayList<String>();
541d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
542d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        // get all items form XMPP server
543d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        DiscoverItems discoverItems = serviceDiscoveryManager.discoverItems(this.connection.getServiceName());
544d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        Iterator<Item> itemIterator = discoverItems.getItems();
545d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
546d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        // query all items if they are SOCKS5 proxies
547d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        while (itemIterator.hasNext()) {
548d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            Item item = itemIterator.next();
549d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
550d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            // skip blacklisted servers
551d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            if (this.proxyBlacklist.contains(item.getEntityID())) {
552d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                continue;
553d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            }
554d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
555d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            try {
556d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                DiscoverInfo proxyInfo;
557d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                proxyInfo = serviceDiscoveryManager.discoverInfo(item.getEntityID());
558d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                Iterator<Identity> identities = proxyInfo.getIdentities();
559d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
560d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                // item must have category "proxy" and type "bytestream"
561d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                while (identities.hasNext()) {
562d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    Identity identity = identities.next();
563d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
564d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    if ("proxy".equalsIgnoreCase(identity.getCategory())
565d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                                    && "bytestreams".equalsIgnoreCase(identity.getType())) {
566d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                        proxies.add(item.getEntityID());
567d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                        break;
568d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    }
569d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
570d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    /*
571d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                     * server is not a SOCKS5 proxy, blacklist server to skip next time a Socks5
572d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                     * bytestream should be established
573d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                     */
574d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    this.proxyBlacklist.add(item.getEntityID());
575d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
576d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                }
577d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            }
578d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            catch (XMPPException e) {
579d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                // blacklist errornous server
580d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                this.proxyBlacklist.add(item.getEntityID());
581d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            }
582d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
583d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
584d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        return proxies;
585d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
586d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
587d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
588d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Returns a list of stream hosts containing the IP address an the port for the given list of
589d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * SOCKS5 proxy JIDs. The order of the returned list is the same as the given list of JIDs
590d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * excluding all SOCKS5 proxies who's network settings could not be determined. If a local
591d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * SOCKS5 proxy is running it will be the first item in the list returned.
592d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
593d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param proxies a list of SOCKS5 proxy JIDs
594d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @return a list of stream hosts containing the IP address an the port
595d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
596d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private List<StreamHost> determineStreamHostInfos(List<String> proxies) {
597d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        List<StreamHost> streamHosts = new ArrayList<StreamHost>();
598d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
599d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        // add local proxy on first position if exists
600d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        List<StreamHost> localProxies = getLocalStreamHost();
601d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (localProxies != null) {
602d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            streamHosts.addAll(localProxies);
603d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
604d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
605d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        // query SOCKS5 proxies for network settings
606d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        for (String proxy : proxies) {
607d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            Bytestream streamHostRequest = createStreamHostRequest(proxy);
608d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            try {
609d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                Bytestream response = (Bytestream) SyncPacketSend.getReply(this.connection,
610d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                                streamHostRequest);
611d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                streamHosts.addAll(response.getStreamHosts());
612d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            }
613d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            catch (XMPPException e) {
614d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                // blacklist errornous proxies
615d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                this.proxyBlacklist.add(proxy);
616d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            }
617d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
618d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
619d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        return streamHosts;
620d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
621d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
622d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
623d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Returns a IQ packet to query a SOCKS5 proxy its network settings.
624d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
625d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param proxy the proxy to query
626d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @return IQ packet to query a SOCKS5 proxy its network settings
627d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
628d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private Bytestream createStreamHostRequest(String proxy) {
629d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        Bytestream request = new Bytestream();
630d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        request.setType(IQ.Type.GET);
631d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        request.setTo(proxy);
632d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        return request;
633d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
634d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
635d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
636d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Returns the stream host information of the local SOCKS5 proxy containing the IP address and
637d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * the port or null if local SOCKS5 proxy is not running.
638d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
639d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @return the stream host information of the local SOCKS5 proxy or null if local SOCKS5 proxy
640d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *         is not running
641d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
642d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private List<StreamHost> getLocalStreamHost() {
643d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
644d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        // get local proxy singleton
645d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        Socks5Proxy socks5Server = Socks5Proxy.getSocks5Proxy();
646d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
647d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (socks5Server.isRunning()) {
648d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            List<String> addresses = socks5Server.getLocalAddresses();
649d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            int port = socks5Server.getPort();
650d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
651d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            if (addresses.size() >= 1) {
652d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                List<StreamHost> streamHosts = new ArrayList<StreamHost>();
653d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                for (String address : addresses) {
654d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    StreamHost streamHost = new StreamHost(this.connection.getUser(), address);
655d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    streamHost.setPort(port);
656d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    streamHosts.add(streamHost);
657d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                }
658d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                return streamHosts;
659d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            }
660d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
661d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
662d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
663d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        // server is not running or local address could not be determined
664d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        return null;
665d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
666d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
667d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
668d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Returns a SOCKS5 Bytestream initialization request packet with the given session ID
669d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * containing the given stream hosts for the given target JID.
670d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
671d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param sessionID the session ID for the SOCKS5 Bytestream
672d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param targetJID the target JID of SOCKS5 Bytestream request
673d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param streamHosts a list of SOCKS5 proxies the target should connect to
674d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @return a SOCKS5 Bytestream initialization request packet
675d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
676d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private Bytestream createBytestreamInitiation(String sessionID, String targetJID,
677d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    List<StreamHost> streamHosts) {
678d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        Bytestream initiation = new Bytestream(sessionID);
679d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
680d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        // add all stream hosts
681d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        for (StreamHost streamHost : streamHosts) {
682d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            initiation.addStreamHost(streamHost);
683d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
684d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
685d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        initiation.setType(IQ.Type.SET);
686d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        initiation.setTo(targetJID);
687d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
688d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        return initiation;
689d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
690d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
691d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
692d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Responses to the given packet's sender with a XMPP error that a SOCKS5 Bytestream is not
693d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * accepted.
694d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
695d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param packet Packet that should be answered with a not-acceptable error
696d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
697d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    protected void replyRejectPacket(IQ packet) {
698d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        XMPPError xmppError = new XMPPError(XMPPError.Condition.no_acceptable);
699d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        IQ errorIQ = IQ.createErrorResponse(packet, xmppError);
700d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        this.connection.sendPacket(errorIQ);
701d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
702d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
703d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
704d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Activates the Socks5BytestreamManager by registering the SOCKS5 Bytestream initialization
705d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * listener and enabling the SOCKS5 Bytestream feature.
706d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
707d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private void activate() {
708d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        // register bytestream initiation packet listener
709d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        this.connection.addPacketListener(this.initiationListener,
710d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                        this.initiationListener.getFilter());
711d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
712d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        // enable SOCKS5 feature
713d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        enableService();
714d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
715d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
716d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
717d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Adds the SOCKS5 Bytestream feature to the service discovery.
718d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
719d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private void enableService() {
720d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        ServiceDiscoveryManager manager = ServiceDiscoveryManager.getInstanceFor(this.connection);
721d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (!manager.includesFeature(NAMESPACE)) {
722d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            manager.addFeature(NAMESPACE);
723d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
724d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
725d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
726d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
727d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Returns a new unique session ID.
728d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
729d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @return a new unique session ID
730d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
731d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private String getNextSessionID() {
732d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        StringBuilder buffer = new StringBuilder();
733d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        buffer.append(SESSION_ID_PREFIX);
734d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        buffer.append(Math.abs(randomGenerator.nextLong()));
735d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        return buffer.toString();
736d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
737d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
738d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
739d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Returns the XMPP connection.
740d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
741d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @return the XMPP connection
742d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
743d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    protected Connection getConnection() {
744d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        return this.connection;
745d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
746d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
747d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
748d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Returns the {@link BytestreamListener} that should be informed if a SOCKS5 Bytestream request
749d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * from the given initiator JID is received.
750d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
751d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param initiator the initiator's JID
752d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @return the listener
753d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
754d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    protected BytestreamListener getUserListener(String initiator) {
755d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        return this.userListeners.get(initiator);
756d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
757d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
758d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
759d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Returns a list of {@link BytestreamListener} that are informed if there are no listeners for
760d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * a specific initiator.
761d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
762d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @return list of listeners
763d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
764d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    protected List<BytestreamListener> getAllRequestListeners() {
765d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        return this.allRequestListeners;
766d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
767d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
768d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
769d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Returns the list of session IDs that should be ignored by the InitialtionListener
770d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
771d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @return list of session IDs
772d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
773d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    protected List<String> getIgnoredBytestreamRequests() {
774d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        return ignoredBytestreamRequests;
775d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
776d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
777d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen}
778