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