]>
Commit | Line | Data |
---|---|---|
1 | /* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */ | |
2 | /* | |
3 | Copyright (c) 2002-2010 ymnk, JCraft,Inc. All rights reserved. | |
4 | ||
5 | Redistribution and use in source and binary forms, with or without | |
6 | modification, are permitted provided that the following conditions are met: | |
7 | ||
8 | 1. Redistributions of source code must retain the above copyright notice, | |
9 | this list of conditions and the following disclaimer. | |
10 | ||
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. | |
14 | ||
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. | |
17 | ||
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. | |
28 | */ | |
29 | ||
30 | /* | |
31 | This file depends on following documents, | |
32 | - RFC 1928 SOCKS Protocol Verseion 5 | |
33 | - RFC 1929 Username/Password Authentication for SOCKS V5. | |
34 | */ | |
35 | ||
36 | package com.jcraft.jsch; | |
37 | ||
38 | import java.io.*; | |
39 | import java.net.*; | |
40 | ||
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; | |
48 | private String user; | |
49 | private String passwd; | |
50 | ||
51 | public ProxySOCKS5(String proxy_host){ | |
52 | int port=DEFAULTPORT; | |
53 | String host=proxy_host; | |
54 | if(proxy_host.indexOf(':')!=-1){ | |
55 | try{ | |
56 | host=proxy_host.substring(0, proxy_host.indexOf(':')); | |
57 | port=Integer.parseInt(proxy_host.substring(proxy_host.indexOf(':')+1)); | |
58 | } | |
59 | catch(Exception e){ | |
60 | } | |
61 | } | |
62 | this.proxy_host=host; | |
63 | this.proxy_port=port; | |
64 | } | |
65 | public ProxySOCKS5(String proxy_host, int proxy_port){ | |
66 | this.proxy_host=proxy_host; | |
67 | this.proxy_port=proxy_port; | |
68 | } | |
69 | public void setUserPasswd(String user, String passwd){ | |
70 | this.user=user; | |
71 | this.passwd=passwd; | |
72 | } | |
73 | public void connect(SocketFactory socket_factory, String host, int port, int timeout) throws JSchException{ | |
74 | try{ | |
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(); | |
80 | } | |
81 | else{ | |
82 | socket=socket_factory.createSocket(proxy_host, proxy_port); | |
83 | in=socket_factory.getInputStream(socket); | |
84 | out=socket_factory.getOutputStream(socket); | |
85 | } | |
86 | if(timeout>0){ | |
87 | socket.setSoTimeout(timeout); | |
88 | } | |
89 | socket.setTcpNoDelay(true); | |
90 | ||
91 | byte[] buf=new byte[1024]; | |
92 | int index=0; | |
93 | ||
94 | /* | |
95 | +----+----------+----------+ | |
96 | |VER | NMETHODS | METHODS | | |
97 | +----+----------+----------+ | |
98 | | 1 | 1 | 1 to 255 | | |
99 | +----+----------+----------+ | |
100 | ||
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. | |
104 | ||
105 | The values currently defined for METHOD are: | |
106 | ||
107 | o X'00' NO AUTHENTICATION REQUIRED | |
108 | o X'01' GSSAPI | |
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 | |
113 | */ | |
114 | ||
115 | buf[index++]=5; | |
116 | ||
117 | buf[index++]=2; | |
118 | buf[index++]=0; // NO AUTHENTICATION REQUIRED | |
119 | buf[index++]=2; // USERNAME/PASSWORD | |
120 | ||
121 | out.write(buf, 0, index); | |
122 | ||
123 | /* | |
124 | The server selects from one of the methods given in METHODS, and | |
125 | sends a METHOD selection message: | |
126 | ||
127 | +----+--------+ | |
128 | |VER | METHOD | | |
129 | +----+--------+ | |
130 | | 1 | 1 | | |
131 | +----+--------+ | |
132 | */ | |
133 | //in.read(buf, 0, 2); | |
134 | fill(in, buf, 2); | |
135 | ||
136 | boolean check=false; | |
137 | switch((buf[1])&0xff){ | |
138 | case 0: // NO AUTHENTICATION REQUIRED | |
139 | check=true; | |
140 | break; | |
141 | case 2: // USERNAME/PASSWORD | |
142 | if(user==null || passwd==null)break; | |
143 | ||
144 | /* | |
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: | |
149 | ||
150 | +----+------+----------+------+----------+ | |
151 | |VER | ULEN | UNAME | PLEN | PASSWD | | |
152 | +----+------+----------+------+----------+ | |
153 | | 1 | 1 | 1 to 255 | 1 | 1 to 255 | | |
154 | +----+------+----------+------+----------+ | |
155 | ||
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. | |
162 | */ | |
163 | index=0; | |
164 | buf[index++]=1; | |
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(); | |
171 | ||
172 | out.write(buf, 0, index); | |
173 | ||
174 | /* | |
175 | The server verifies the supplied UNAME and PASSWD, and sends the | |
176 | following response: | |
177 | ||
178 | +----+--------+ | |
179 | |VER | STATUS | | |
180 | +----+--------+ | |
181 | | 1 | 1 | | |
182 | +----+--------+ | |
183 | ||
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 | |
186 | connection. | |
187 | */ | |
188 | //in.read(buf, 0, 2); | |
189 | fill(in, buf, 2); | |
190 | if(buf[1]==0) | |
191 | check=true; | |
192 | break; | |
193 | default: | |
194 | } | |
195 | ||
196 | if(!check){ | |
197 | try{ socket.close(); } | |
198 | catch(Exception eee){ | |
199 | } | |
200 | throw new JSchException("fail in SOCKS5 proxy"); | |
201 | } | |
202 | ||
203 | /* | |
204 | The SOCKS request is formed as follows: | |
205 | ||
206 | +----+-----+-------+------+----------+----------+ | |
207 | |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT | | |
208 | +----+-----+-------+------+----------+----------+ | |
209 | | 1 | 1 | X'00' | 1 | Variable | 2 | | |
210 | +----+-----+-------+------+----------+----------+ | |
211 | ||
212 | Where: | |
213 | ||
214 | o VER protocol version: X'05' | |
215 | o CMD | |
216 | o CONNECT X'01' | |
217 | o BIND X'02' | |
218 | o UDP ASSOCIATE X'03' | |
219 | o RSV RESERVED | |
220 | o ATYP address type of following address | |
221 | o IP V4 address: X'01' | |
222 | o DOMAINNAME: X'03' | |
223 | o IP V6 address: X'04' | |
224 | o DST.ADDR desired destination address | |
225 | o DST.PORT desired destination port in network octet | |
226 | order | |
227 | */ | |
228 | ||
229 | index=0; | |
230 | buf[index++]=5; | |
231 | buf[index++]=1; // CONNECT | |
232 | buf[index++]=0; | |
233 | ||
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); | |
239 | index+=len; | |
240 | buf[index++]=(byte)(port>>>8); | |
241 | buf[index++]=(byte)(port&0xff); | |
242 | ||
243 | out.write(buf, 0, index); | |
244 | ||
245 | /* | |
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: | |
250 | ||
251 | +----+-----+-------+------+----------+----------+ | |
252 | |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT | | |
253 | +----+-----+-------+------+----------+----------+ | |
254 | | 1 | 1 | X'00' | 1 | Variable | 2 | | |
255 | +----+-----+-------+------+----------+----------+ | |
256 | ||
257 | Where: | |
258 | ||
259 | o VER protocol version: X'05' | |
260 | o REP Reply field: | |
261 | o X'00' succeeded | |
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 | |
267 | o X'06' TTL expired | |
268 | o X'07' Command not supported | |
269 | o X'08' Address type not supported | |
270 | o X'09' to X'FF' unassigned | |
271 | o RSV RESERVED | |
272 | o ATYP address type of following address | |
273 | o IP V4 address: X'01' | |
274 | o DOMAINNAME: X'03' | |
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 | |
278 | */ | |
279 | ||
280 | //in.read(buf, 0, 4); | |
281 | fill(in, buf, 4); | |
282 | ||
283 | if(buf[1]!=0){ | |
284 | try{ socket.close(); } | |
285 | catch(Exception eee){ | |
286 | } | |
287 | throw new JSchException("ProxySOCKS5: server returns "+buf[1]); | |
288 | } | |
289 | ||
290 | switch(buf[3]&0xff){ | |
291 | case 1: | |
292 | //in.read(buf, 0, 6); | |
293 | fill(in, buf, 6); | |
294 | break; | |
295 | case 3: | |
296 | //in.read(buf, 0, 1); | |
297 | fill(in, buf, 1); | |
298 | //in.read(buf, 0, buf[0]+2); | |
299 | fill(in, buf, (buf[0]&0xff)+2); | |
300 | break; | |
301 | case 4: | |
302 | //in.read(buf, 0, 18); | |
303 | fill(in, buf, 18); | |
304 | break; | |
305 | default: | |
306 | } | |
307 | } | |
308 | catch(RuntimeException e){ | |
309 | throw e; | |
310 | } | |
311 | catch(Exception e){ | |
312 | try{ if(socket!=null)socket.close(); } | |
313 | catch(Exception eee){ | |
314 | } | |
315 | String message="ProxySOCKS5: "+e.toString(); | |
316 | if(e instanceof Throwable) | |
317 | throw new JSchException(message, (Throwable)e); | |
318 | throw new JSchException(message); | |
319 | } | |
320 | } | |
321 | public InputStream getInputStream(){ return in; } | |
322 | public OutputStream getOutputStream(){ return out; } | |
323 | public Socket getSocket(){ return socket; } | |
324 | public void close(){ | |
325 | try{ | |
326 | if(in!=null)in.close(); | |
327 | if(out!=null)out.close(); | |
328 | if(socket!=null)socket.close(); | |
329 | } | |
330 | catch(Exception e){ | |
331 | } | |
332 | in=null; | |
333 | out=null; | |
334 | socket=null; | |
335 | } | |
336 | public static int getDefaultPort(){ | |
337 | return DEFAULTPORT; | |
338 | } | |
339 | private void fill(InputStream in, byte[] buf, int len) throws JSchException, IOException{ | |
340 | int s=0; | |
341 | while(s<len){ | |
342 | int i=in.read(buf, s, len-s); | |
343 | if(i<=0){ | |
344 | throw new JSchException("ProxySOCKS5: stream is closed"); | |
345 | } | |
346 | s+=i; | |
347 | } | |
348 | } | |
349 | } |