1 /* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
3 Copyright (c) 2002-2010 ymnk, JCraft,Inc. All rights reserved.
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
8 1. Redistributions of source code must retain the above copyright notice,
9 this list of conditions and the following disclaimer.
11 2. Redistributions in binary form must reproduce the above copyright
12 notice, this list of conditions and the following disclaimer in
13 the documentation and/or other materials provided with the distribution.
15 3. The names of the authors may not be used to endorse or promote products
16 derived from this software without specific prior written permission.
18 THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
19 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
21 INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
22 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
24 OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
25 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
26 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
27 EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 This file depends on following documents,
32 - RFC 1928 SOCKS Protocol Verseion 5
33 - RFC 1929 Username/Password Authentication for SOCKS V5.
36 package com.jcraft.jsch;
41 public class ProxySOCKS5 implements Proxy{
42 private static int DEFAULTPORT=1080;
43 private String proxy_host;
44 private int proxy_port;
45 private InputStream in;
46 private OutputStream out;
47 private Socket socket;
49 private String passwd;
51 public ProxySOCKS5(String proxy_host){
53 String host=proxy_host;
54 if(proxy_host.indexOf(':')!=-1){
56 host=proxy_host.substring(0, proxy_host.indexOf(':'));
57 port=Integer.parseInt(proxy_host.substring(proxy_host.indexOf(':')+1));
65 public ProxySOCKS5(String proxy_host, int proxy_port){
66 this.proxy_host=proxy_host;
67 this.proxy_port=proxy_port;
69 public void setUserPasswd(String user, String passwd){
73 public void connect(SocketFactory socket_factory, String host, int port, int timeout) throws JSchException{
75 if(socket_factory==null){
76 socket=Util.createSocket(proxy_host, proxy_port, timeout);
77 //socket=new Socket(proxy_host, proxy_port);
78 in=socket.getInputStream();
79 out=socket.getOutputStream();
82 socket=socket_factory.createSocket(proxy_host, proxy_port);
83 in=socket_factory.getInputStream(socket);
84 out=socket_factory.getOutputStream(socket);
87 socket.setSoTimeout(timeout);
89 socket.setTcpNoDelay(true);
91 byte[] buf=new byte[1024];
95 +----+----------+----------+
96 |VER | NMETHODS | METHODS |
97 +----+----------+----------+
99 +----+----------+----------+
101 The VER field is set to X'05' for this version of the protocol. The
102 NMETHODS field contains the number of method identifier octets that
103 appear in the METHODS field.
105 The values currently defined for METHOD are:
107 o X'00' NO AUTHENTICATION REQUIRED
109 o X'02' USERNAME/PASSWORD
110 o X'03' to X'7F' IANA ASSIGNED
111 o X'80' to X'FE' RESERVED FOR PRIVATE METHODS
112 o X'FF' NO ACCEPTABLE METHODS
118 buf[index++]=0; // NO AUTHENTICATION REQUIRED
119 buf[index++]=2; // USERNAME/PASSWORD
121 out.write(buf, 0, index);
124 The server selects from one of the methods given in METHODS, and
125 sends a METHOD selection message:
133 //in.read(buf, 0, 2);
137 switch((buf[1])&0xff){
138 case 0: // NO AUTHENTICATION REQUIRED
141 case 2: // USERNAME/PASSWORD
142 if(user==null || passwd==null)break;
145 Once the SOCKS V5 server has started, and the client has selected the
146 Username/Password Authentication protocol, the Username/Password
147 subnegotiation begins. This begins with the client producing a
148 Username/Password request:
150 +----+------+----------+------+----------+
151 |VER | ULEN | UNAME | PLEN | PASSWD |
152 +----+------+----------+------+----------+
153 | 1 | 1 | 1 to 255 | 1 | 1 to 255 |
154 +----+------+----------+------+----------+
156 The VER field contains the current version of the subnegotiation,
157 which is X'01'. The ULEN field contains the length of the UNAME field
158 that follows. The UNAME field contains the username as known to the
159 source operating system. The PLEN field contains the length of the
160 PASSWD field that follows. The PASSWD field contains the password
161 association with the given UNAME.
165 buf[index++]=(byte)(user.length());
166 System.arraycopy(Util.str2byte(user), 0, buf, index, user.length());
167 index+=user.length();
168 buf[index++]=(byte)(passwd.length());
169 System.arraycopy(Util.str2byte(passwd), 0, buf, index, passwd.length());
170 index+=passwd.length();
172 out.write(buf, 0, index);
175 The server verifies the supplied UNAME and PASSWD, and sends the
184 A STATUS field of X'00' indicates success. If the server returns a
185 `failure' (STATUS value other than X'00') status, it MUST close the
188 //in.read(buf, 0, 2);
197 try{ socket.close(); }
198 catch(Exception eee){
200 throw new JSchException("fail in SOCKS5 proxy");
204 The SOCKS request is formed as follows:
206 +----+-----+-------+------+----------+----------+
207 |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT |
208 +----+-----+-------+------+----------+----------+
209 | 1 | 1 | X'00' | 1 | Variable | 2 |
210 +----+-----+-------+------+----------+----------+
214 o VER protocol version: X'05'
218 o UDP ASSOCIATE X'03'
220 o ATYP address type of following address
221 o IP V4 address: X'01'
223 o IP V6 address: X'04'
224 o DST.ADDR desired destination address
225 o DST.PORT desired destination port in network octet
231 buf[index++]=1; // CONNECT
234 byte[] hostb=Util.str2byte(host);
235 int len=hostb.length;
236 buf[index++]=3; // DOMAINNAME
237 buf[index++]=(byte)(len);
238 System.arraycopy(hostb, 0, buf, index, len);
240 buf[index++]=(byte)(port>>>8);
241 buf[index++]=(byte)(port&0xff);
243 out.write(buf, 0, index);
246 The SOCKS request information is sent by the client as soon as it has
247 established a connection to the SOCKS server, and completed the
248 authentication negotiations. The server evaluates the request, and
249 returns a reply formed as follows:
251 +----+-----+-------+------+----------+----------+
252 |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT |
253 +----+-----+-------+------+----------+----------+
254 | 1 | 1 | X'00' | 1 | Variable | 2 |
255 +----+-----+-------+------+----------+----------+
259 o VER protocol version: X'05'
262 o X'01' general SOCKS server failure
263 o X'02' connection not allowed by ruleset
264 o X'03' Network unreachable
265 o X'04' Host unreachable
266 o X'05' Connection refused
268 o X'07' Command not supported
269 o X'08' Address type not supported
270 o X'09' to X'FF' unassigned
272 o ATYP address type of following address
273 o IP V4 address: X'01'
275 o IP V6 address: X'04'
276 o BND.ADDR server bound address
277 o BND.PORT server bound port in network octet order
280 //in.read(buf, 0, 4);
284 try{ socket.close(); }
285 catch(Exception eee){
287 throw new JSchException("ProxySOCKS5: server returns "+buf[1]);
292 //in.read(buf, 0, 6);
296 //in.read(buf, 0, 1);
298 //in.read(buf, 0, buf[0]+2);
299 fill(in, buf, (buf[0]&0xff)+2);
302 //in.read(buf, 0, 18);
308 catch(RuntimeException e){
312 try{ if(socket!=null)socket.close(); }
313 catch(Exception eee){
315 String message="ProxySOCKS5: "+e.toString();
316 if(e instanceof Throwable)
317 throw new JSchException(message, (Throwable)e);
318 throw new JSchException(message);
321 public InputStream getInputStream(){ return in; }
322 public OutputStream getOutputStream(){ return out; }
323 public Socket getSocket(){ return socket; }
326 if(in!=null)in.close();
327 if(out!=null)out.close();
328 if(socket!=null)socket.close();
336 public static int getDefaultPort(){
339 private void fill(InputStream in, byte[] buf, int len) throws JSchException, IOException{
342 int i=in.read(buf, s, len-s);
344 throw new JSchException("ProxySOCKS5: stream is closed");