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.DataInputStream;
17d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.io.DataOutputStream;
18d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.io.IOException;
19d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.net.InetAddress;
20d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.net.ServerSocket;
21d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.net.Socket;
22d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.net.SocketException;
23d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.net.UnknownHostException;
24d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.ArrayList;
25d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.Collections;
26d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.LinkedHashSet;
27d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.LinkedList;
28d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.List;
29d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.Map;
30d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.Set;
31d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.concurrent.ConcurrentHashMap;
32d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
33d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smack.SmackConfiguration;
34d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smack.XMPPException;
35d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
36d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen/**
37d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * The Socks5Proxy class represents a local SOCKS5 proxy server. It can be enabled/disabled by
38d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * setting the <code>localSocks5ProxyEnabled</code> flag in the <code>smack-config.xml</code> or by
39d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * invoking {@link SmackConfiguration#setLocalSocks5ProxyEnabled(boolean)}. The proxy is enabled by
40d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * default.
41d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * <p>
42d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * The port of the local SOCKS5 proxy can be configured by setting <code>localSocks5ProxyPort</code>
43d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * in the <code>smack-config.xml</code> or by invoking
44d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * {@link SmackConfiguration#setLocalSocks5ProxyPort(int)}. Default port is 7777. If you set the
45d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * port to a negative value Smack tries to the absolute value and all following until it finds an
46d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * open port.
47d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * <p>
48d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * If your application is running on a machine with multiple network interfaces or if you want to
49d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * provide your public address in case you are behind a NAT router, invoke
50d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * {@link #addLocalAddress(String)} or {@link #replaceLocalAddresses(List)} to modify the list of
51d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * local network addresses used for outgoing SOCKS5 Bytestream requests.
52d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * <p>
53d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * The local SOCKS5 proxy server refuses all connections except the ones that are explicitly allowed
54d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * in the process of establishing a SOCKS5 Bytestream (
55d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * {@link Socks5BytestreamManager#establishSession(String)}).
56d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * <p>
57d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * This Implementation has the following limitations:
58d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * <ul>
59d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * <li>only supports the no-authentication authentication method</li>
60d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * <li>only supports the <code>connect</code> command and will not answer correctly to other
61d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * commands</li>
62d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * <li>only supports requests with the domain address type and will not correctly answer to requests
63d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * with other address types</li>
64d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * </ul>
65d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * (see <a href="http://tools.ietf.org/html/rfc1928">RFC 1928</a>)
66d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen *
67d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @author Henning Staib
68d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */
69d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenpublic class Socks5Proxy {
70d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
71d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /* SOCKS5 proxy singleton */
72d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private static Socks5Proxy socks5Server;
73d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
74d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /* reusable implementation of a SOCKS5 proxy server process */
75d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private Socks5ServerProcess serverProcess;
76d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
77d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /* thread running the SOCKS5 server process */
78d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private Thread serverThread;
79d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
80d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /* server socket to accept SOCKS5 connections */
81d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private ServerSocket serverSocket;
82d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
83d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /* assigns a connection to a digest */
84d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private final Map<String, Socket> connectionMap = new ConcurrentHashMap<String, Socket>();
85d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
86d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /* list of digests connections should be stored */
87d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private final List<String> allowedConnections = Collections.synchronizedList(new LinkedList<String>());
88d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
89d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private final Set<String> localAddresses = Collections.synchronizedSet(new LinkedHashSet<String>());
90d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
91d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
92d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Private constructor.
93d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
94d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private Socks5Proxy() {
95d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        this.serverProcess = new Socks5ServerProcess();
96d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
97d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        // add default local address
98d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        try {
99d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            this.localAddresses.add(InetAddress.getLocalHost().getHostAddress());
100d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
101d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        catch (UnknownHostException e) {
102d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            // do nothing
103d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
104d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
105d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
106d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
107d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
108d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Returns the local SOCKS5 proxy server.
109d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
110d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @return the local SOCKS5 proxy server
111d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
112d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    public static synchronized Socks5Proxy getSocks5Proxy() {
113d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (socks5Server == null) {
114d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            socks5Server = new Socks5Proxy();
115d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
116d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (SmackConfiguration.isLocalSocks5ProxyEnabled()) {
117d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            socks5Server.start();
118d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
119d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        return socks5Server;
120d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
121d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
122d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
123d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Starts the local SOCKS5 proxy server. If it is already running, this method does nothing.
124d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
125d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    public synchronized void start() {
126d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (isRunning()) {
127d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            return;
128d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
129d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        try {
130d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            if (SmackConfiguration.getLocalSocks5ProxyPort() < 0) {
131d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                int port = Math.abs(SmackConfiguration.getLocalSocks5ProxyPort());
132d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                for (int i = 0; i < 65535 - port; i++) {
133d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    try {
134d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                        this.serverSocket = new ServerSocket(port + i);
135d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                        break;
136d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    }
137d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    catch (IOException e) {
138d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                        // port is used, try next one
139d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    }
140d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                }
141d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            }
142d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            else {
143d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                this.serverSocket = new ServerSocket(SmackConfiguration.getLocalSocks5ProxyPort());
144d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            }
145d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
146d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            if (this.serverSocket != null) {
147d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                this.serverThread = new Thread(this.serverProcess);
148d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                this.serverThread.start();
149d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            }
150d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
151d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        catch (IOException e) {
152d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            // couldn't setup server
153d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            System.err.println("couldn't setup local SOCKS5 proxy on port "
154d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                            + SmackConfiguration.getLocalSocks5ProxyPort() + ": " + e.getMessage());
155d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
156d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
157d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
158d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
159d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Stops the local SOCKS5 proxy server. If it is not running this method does nothing.
160d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
161d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    public synchronized void stop() {
162d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (!isRunning()) {
163d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            return;
164d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
165d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
166d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        try {
167d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            this.serverSocket.close();
168d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
169d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        catch (IOException e) {
170d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            // do nothing
171d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
172d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
173d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (this.serverThread != null && this.serverThread.isAlive()) {
174d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            try {
175d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                this.serverThread.interrupt();
176d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                this.serverThread.join();
177d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            }
178d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            catch (InterruptedException e) {
179d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                // do nothing
180d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            }
181d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
182d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        this.serverThread = null;
183d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        this.serverSocket = null;
184d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
185d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
186d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
187d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
188d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Adds the given address to the list of local network addresses.
189d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * <p>
190d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Use this method if you want to provide multiple addresses in a SOCKS5 Bytestream request.
191d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * This may be necessary if your application is running on a machine with multiple network
192d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * interfaces or if you want to provide your public address in case you are behind a NAT router.
193d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * <p>
194d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * The order of the addresses used is determined by the order you add addresses.
195d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * <p>
196d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Note that the list of addresses initially contains the address returned by
197d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * <code>InetAddress.getLocalHost().getHostAddress()</code>. You can replace the list of
198d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * addresses by invoking {@link #replaceLocalAddresses(List)}.
199d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
200d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param address the local network address to add
201d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
202d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    public void addLocalAddress(String address) {
203d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (address == null) {
204d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            throw new IllegalArgumentException("address may not be null");
205d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
206d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        this.localAddresses.add(address);
207d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
208d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
209d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
210d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Removes the given address from the list of local network addresses. This address will then no
211d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * longer be used of outgoing SOCKS5 Bytestream requests.
212d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
213d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param address the local network address to remove
214d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
215d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    public void removeLocalAddress(String address) {
216d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        this.localAddresses.remove(address);
217d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
218d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
219d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
220d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Returns an unmodifiable list of the local network addresses that will be used for streamhost
221d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * candidates of outgoing SOCKS5 Bytestream requests.
222d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
223d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @return unmodifiable list of the local network addresses
224d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
225d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    public List<String> getLocalAddresses() {
226d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        return Collections.unmodifiableList(new ArrayList<String>(this.localAddresses));
227d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
228d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
229d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
230d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Replaces the list of local network addresses.
231d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * <p>
232d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Use this method if you want to provide multiple addresses in a SOCKS5 Bytestream request and
233d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * want to define their order. This may be necessary if your application is running on a machine
234d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * with multiple network interfaces or if you want to provide your public address in case you
235d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * are behind a NAT router.
236d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
237d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param addresses the new list of local network addresses
238d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
239d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    public void replaceLocalAddresses(List<String> addresses) {
240d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (addresses == null) {
241d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            throw new IllegalArgumentException("list must not be null");
242d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
243d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        this.localAddresses.clear();
244d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        this.localAddresses.addAll(addresses);
245d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
246d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
247d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
248d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
249d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Returns the port of the local SOCKS5 proxy server. If it is not running -1 will be returned.
250d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
251d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @return the port of the local SOCKS5 proxy server or -1 if proxy is not running
252d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
253d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    public int getPort() {
254d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        if (!isRunning()) {
255d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            return -1;
256d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
257d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        return this.serverSocket.getLocalPort();
258d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
259d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
260d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
261d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Returns the socket for the given digest. A socket will be returned if the given digest has
262d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * been in the list of allowed transfers (see {@link #addTransfer(String)}) while the peer
263d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * connected to the SOCKS5 proxy.
264d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
265d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param digest identifying the connection
266d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @return socket or null if there is no socket for the given digest
267d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
268d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    protected Socket getSocket(String digest) {
269d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        return this.connectionMap.get(digest);
270d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
271d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
272d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
273d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Add the given digest to the list of allowed transfers. Only connections for allowed transfers
274d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * are stored and can be retrieved by invoking {@link #getSocket(String)}. All connections to
275d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * the local SOCKS5 proxy that don't contain an allowed digest are discarded.
276d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
277d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param digest to be added to the list of allowed transfers
278d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
279d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    protected void addTransfer(String digest) {
280d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        this.allowedConnections.add(digest);
281d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
282d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
283d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
284d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Removes the given digest from the list of allowed transfers. After invoking this method
285d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * already stored connections with the given digest will be removed.
286d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * <p>
287d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * The digest should be removed after establishing the SOCKS5 Bytestream is finished, an error
288d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * occurred while establishing the connection or if the connection is not allowed anymore.
289d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
290d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @param digest to be removed from the list of allowed transfers
291d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
292d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    protected void removeTransfer(String digest) {
293d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        this.allowedConnections.remove(digest);
294d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        this.connectionMap.remove(digest);
295d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
296d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
297d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
298d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Returns <code>true</code> if the local SOCKS5 proxy server is running, otherwise
299d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * <code>false</code>.
300d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *
301d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * @return <code>true</code> if the local SOCKS5 proxy server is running, otherwise
302d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     *         <code>false</code>
303d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
304d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    public boolean isRunning() {
305d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        return this.serverSocket != null;
306d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
307d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
308d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    /**
309d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     * Implementation of a simplified SOCKS5 proxy server.
310d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen     */
311d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    private class Socks5ServerProcess implements Runnable {
312d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
313d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        public void run() {
314d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            while (true) {
315d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                Socket socket = null;
316d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
317d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                try {
318d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
319d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    if (Socks5Proxy.this.serverSocket.isClosed()
320d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                                    || Thread.currentThread().isInterrupted()) {
321d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                        return;
322d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    }
323d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
324d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    // accept connection
325d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    socket = Socks5Proxy.this.serverSocket.accept();
326d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
327d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    // initialize connection
328d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    establishConnection(socket);
329d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
330d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                }
331d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                catch (SocketException e) {
332d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    /*
333d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                     * do nothing, if caused by closing the server socket, thread will terminate in
334d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                     * next loop
335d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                     */
336d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                }
337d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                catch (Exception e) {
338d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    try {
339d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                        if (socket != null) {
340d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                            socket.close();
341d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                        }
342d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    }
343d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    catch (IOException e1) {
344d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                        /* do nothing */
345d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    }
346d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                }
347d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            }
348d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
349d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
350d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
351d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        /**
352d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen         * Negotiates a SOCKS5 connection and stores it on success.
353d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen         *
354d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen         * @param socket connection to the client
355d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen         * @throws XMPPException if client requests a connection in an unsupported way
356d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen         * @throws IOException if a network error occurred
357d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen         */
358d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        private void establishConnection(Socket socket) throws XMPPException, IOException {
359d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            DataOutputStream out = new DataOutputStream(socket.getOutputStream());
360d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            DataInputStream in = new DataInputStream(socket.getInputStream());
361d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
362d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            // first byte is version should be 5
363d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            int b = in.read();
364d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            if (b != 5) {
365d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                throw new XMPPException("Only SOCKS5 supported");
366d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            }
367d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
368d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            // second byte number of authentication methods supported
369d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            b = in.read();
370d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
371d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            // read list of supported authentication methods
372d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            byte[] auth = new byte[b];
373d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            in.readFully(auth);
374d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
375d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            byte[] authMethodSelectionResponse = new byte[2];
376d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            authMethodSelectionResponse[0] = (byte) 0x05; // protocol version
377d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
378d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            // only authentication method 0, no authentication, supported
379d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            boolean noAuthMethodFound = false;
380d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            for (int i = 0; i < auth.length; i++) {
381d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                if (auth[i] == (byte) 0x00) {
382d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    noAuthMethodFound = true;
383d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                    break;
384d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                }
385d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            }
386d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
387d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            if (!noAuthMethodFound) {
388d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                authMethodSelectionResponse[1] = (byte) 0xFF; // no acceptable methods
389d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                out.write(authMethodSelectionResponse);
390d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                out.flush();
391d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                throw new XMPPException("Authentication method not supported");
392d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            }
393d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
394d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            authMethodSelectionResponse[1] = (byte) 0x00; // no-authentication method
395d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            out.write(authMethodSelectionResponse);
396d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            out.flush();
397d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
398d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            // receive connection request
399d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            byte[] connectionRequest = Socks5Utils.receiveSocks5Message(in);
400d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
401d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            // extract digest
402d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            String responseDigest = new String(connectionRequest, 5, connectionRequest[4]);
403d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
404d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            // return error if digest is not allowed
405d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            if (!Socks5Proxy.this.allowedConnections.contains(responseDigest)) {
406d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                connectionRequest[1] = (byte) 0x05; // set return status to 5 (connection refused)
407d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                out.write(connectionRequest);
408d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                out.flush();
409d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
410d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen                throw new XMPPException("Connection is not allowed");
411d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            }
412d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
413d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            connectionRequest[1] = (byte) 0x00; // set return status to 0 (success)
414d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            out.write(connectionRequest);
415d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            out.flush();
416d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
417d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            // store connection
418d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen            Socks5Proxy.this.connectionMap.put(responseDigest, socket);
419d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen        }
420d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
421d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen    }
422d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen
423d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen}
424