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.InetSocketAddress; 20d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.net.Socket; 21d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.net.SocketAddress; 22d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.Arrays; 23d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.concurrent.Callable; 24d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.concurrent.ExecutionException; 25d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.concurrent.FutureTask; 26d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.concurrent.TimeUnit; 27d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport java.util.concurrent.TimeoutException; 28d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 29d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smack.XMPPException; 30d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenimport org.jivesoftware.smackx.bytestreams.socks5.packet.Bytestream.StreamHost; 31d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 32d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen/** 33d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * The SOCKS5 client class handles establishing a connection to a SOCKS5 proxy. Connecting to a 34d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * SOCKS5 proxy requires authentication. This implementation only supports the no-authentication 35d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * authentication method. 36d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * 37d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @author Henning Staib 38d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 39d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chenclass Socks5Client { 40d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 41d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /* stream host containing network settings and name of the SOCKS5 proxy */ 42d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen protected StreamHost streamHost; 43d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 44d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /* SHA-1 digest identifying the SOCKS5 stream */ 45d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen protected String digest; 46d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 47d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /** 48d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Constructor for a SOCKS5 client. 49d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * 50d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @param streamHost containing network settings of the SOCKS5 proxy 51d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @param digest identifying the SOCKS5 Bytestream 52d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 53d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public Socks5Client(StreamHost streamHost, String digest) { 54d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen this.streamHost = streamHost; 55d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen this.digest = digest; 56d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 57d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 58d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /** 59d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Returns the initialized socket that can be used to transfer data between peers via the SOCKS5 60d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * proxy. 61d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * 62d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @param timeout timeout to connect to SOCKS5 proxy in milliseconds 63d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @return socket the initialized socket 64d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @throws IOException if initializing the socket failed due to a network error 65d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @throws XMPPException if establishing connection to SOCKS5 proxy failed 66d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @throws TimeoutException if connecting to SOCKS5 proxy timed out 67d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @throws InterruptedException if the current thread was interrupted while waiting 68d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 69d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public Socket getSocket(int timeout) throws IOException, XMPPException, InterruptedException, 70d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen TimeoutException { 71d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 72d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // wrap connecting in future for timeout 73d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen FutureTask<Socket> futureTask = new FutureTask<Socket>(new Callable<Socket>() { 74d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 75d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen public Socket call() throws Exception { 76d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 77d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // initialize socket 78d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen Socket socket = new Socket(); 79d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen SocketAddress socketAddress = new InetSocketAddress(streamHost.getAddress(), 80d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen streamHost.getPort()); 81d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen socket.connect(socketAddress); 82d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 83d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // initialize connection to SOCKS5 proxy 84d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (!establish(socket)) { 85d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 86d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // initialization failed, close socket 87d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen socket.close(); 88d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen throw new XMPPException("establishing connection to SOCKS5 proxy failed"); 89d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 90d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 91d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 92d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return socket; 93d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 94d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 95d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen }); 96d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen Thread executor = new Thread(futureTask); 97d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen executor.start(); 98d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 99d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // get connection to initiator with timeout 100d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen try { 101d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return futureTask.get(timeout, TimeUnit.MILLISECONDS); 102d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 103d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen catch (ExecutionException e) { 104d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen Throwable cause = e.getCause(); 105d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (cause != null) { 106d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // case exceptions to comply with method signature 107d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (cause instanceof IOException) { 108d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen throw (IOException) cause; 109d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 110d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (cause instanceof XMPPException) { 111d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen throw (XMPPException) cause; 112d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 113d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 114d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 115d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // throw generic IO exception if unexpected exception was thrown 116d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen throw new IOException("Error while connection to SOCKS5 proxy"); 117d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 118d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 119d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 120d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 121d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /** 122d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Initializes the connection to the SOCKS5 proxy by negotiating authentication method and 123d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * requesting a stream for the given digest. Currently only the no-authentication method is 124d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * supported by the Socks5Client. 125d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * <p> 126d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Returns <code>true</code> if a stream could be established, otherwise <code>false</code>. If 127d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * <code>false</code> is returned the given Socket should be closed. 128d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * 129d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @param socket connected to a SOCKS5 proxy 130d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @return <code>true</code> if if a stream could be established, otherwise <code>false</code>. 131d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * If <code>false</code> is returned the given Socket should be closed. 132d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @throws IOException if a network error occurred 133d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 134d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen protected boolean establish(Socket socket) throws IOException { 135d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 136d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /* 137d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * use DataInputStream/DataOutpuStream to assure read and write is completed in a single 138d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * statement 139d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 140d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen DataInputStream in = new DataInputStream(socket.getInputStream()); 141d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen DataOutputStream out = new DataOutputStream(socket.getOutputStream()); 142d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 143d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // authentication negotiation 144d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen byte[] cmd = new byte[3]; 145d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 146d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen cmd[0] = (byte) 0x05; // protocol version 5 147d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen cmd[1] = (byte) 0x01; // number of authentication methods supported 148d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen cmd[2] = (byte) 0x00; // authentication method: no-authentication required 149d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 150d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen out.write(cmd); 151d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen out.flush(); 152d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 153d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen byte[] response = new byte[2]; 154d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen in.readFully(response); 155d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 156d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // check if server responded with correct version and no-authentication method 157d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen if (response[0] != (byte) 0x05 || response[1] != (byte) 0x00) { 158d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return false; 159d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 160d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 161d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // request SOCKS5 connection with given address/digest 162d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen byte[] connectionRequest = createSocks5ConnectRequest(); 163d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen out.write(connectionRequest); 164d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen out.flush(); 165d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 166d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // receive response 167d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen byte[] connectionResponse; 168d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen try { 169d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen connectionResponse = Socks5Utils.receiveSocks5Message(in); 170d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 171d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen catch (XMPPException e) { 172d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return false; // server answered in an unsupported way 173d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 174d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 175d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen // verify response 176d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen connectionRequest[1] = (byte) 0x00; // set expected return status to 0 177d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return Arrays.equals(connectionRequest, connectionResponse); 178d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 179d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 180d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen /** 181d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * Returns a SOCKS5 connection request message. It contains the command "connect", the address 182d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * type "domain" and the digest as address. 183d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * <p> 184d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * (see <a href="http://tools.ietf.org/html/rfc1928">RFC1928</a>) 185d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * 186d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen * @return SOCKS5 connection request message 187d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen */ 188d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen private byte[] createSocks5ConnectRequest() { 189d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen byte addr[] = this.digest.getBytes(); 190d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 191d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen byte[] data = new byte[7 + addr.length]; 192d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen data[0] = (byte) 0x05; // version (SOCKS5) 193d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen data[1] = (byte) 0x01; // command (1 - connect) 194d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen data[2] = (byte) 0x00; // reserved byte (always 0) 195d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen data[3] = (byte) 0x03; // address type (3 - domain name) 196d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen data[4] = (byte) addr.length; // address length 197d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen System.arraycopy(addr, 0, data, 5, addr.length); // address 198d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen data[data.length - 2] = (byte) 0; // address port (2 bytes always 0) 199d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen data[data.length - 1] = (byte) 0; 200d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 201d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen return data; 202d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen } 203d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen 204d7955ce24d294fb2014c59d11fca184471056f44Shuyi Chen} 205