001: /* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
002: /*
003: Copyright (c) 2002-2008 ymnk, JCraft,Inc. All rights reserved.
004:
005: Redistribution and use in source and binary forms, with or without
006: modification, are permitted provided that the following conditions are met:
007:
008: 1. Redistributions of source code must retain the above copyright notice,
009: this list of conditions and the following disclaimer.
010:
011: 2. Redistributions in binary form must reproduce the above copyright
012: notice, this list of conditions and the following disclaimer in
013: the documentation and/or other materials provided with the distribution.
014:
015: 3. The names of the authors may not be used to endorse or promote products
016: derived from this software without specific prior written permission.
017:
018: THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
019: INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
020: FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL JCRAFT,
021: INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
022: INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
023: LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
024: OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
025: LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
026: NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
027: EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
028: */
029:
030: /*
031: This file depends on following documents,
032: - RFC 1928 SOCKS Protocol Verseion 5
033: - RFC 1929 Username/Password Authentication for SOCKS V5.
034: */
035:
036: package com.jcraft.jsch;
037:
038: import java.io.*;
039: import java.net.*;
040:
041: public class ProxySOCKS5 implements Proxy {
042: private static int DEFAULTPORT = 1080;
043: private String proxy_host;
044: private int proxy_port;
045: private InputStream in;
046: private OutputStream out;
047: private Socket socket;
048: private String user;
049: private String passwd;
050:
051: public ProxySOCKS5(String proxy_host) {
052: int port = DEFAULTPORT;
053: String host = proxy_host;
054: if (proxy_host.indexOf(':') != -1) {
055: try {
056: host = proxy_host.substring(0, proxy_host.indexOf(':'));
057: port = Integer.parseInt(proxy_host.substring(proxy_host
058: .indexOf(':') + 1));
059: } catch (Exception e) {
060: }
061: }
062: this .proxy_host = host;
063: this .proxy_port = port;
064: }
065:
066: public ProxySOCKS5(String proxy_host, int proxy_port) {
067: this .proxy_host = proxy_host;
068: this .proxy_port = proxy_port;
069: }
070:
071: public void setUserPasswd(String user, String passwd) {
072: this .user = user;
073: this .passwd = passwd;
074: }
075:
076: public void connect(SocketFactory socket_factory, String host,
077: int port, int timeout) throws JSchException {
078: try {
079: if (socket_factory == null) {
080: socket = Util.createSocket(proxy_host, proxy_port,
081: timeout);
082: //socket=new Socket(proxy_host, proxy_port);
083: in = socket.getInputStream();
084: out = socket.getOutputStream();
085: } else {
086: socket = socket_factory.createSocket(proxy_host,
087: proxy_port);
088: in = socket_factory.getInputStream(socket);
089: out = socket_factory.getOutputStream(socket);
090: }
091: if (timeout > 0) {
092: socket.setSoTimeout(timeout);
093: }
094: socket.setTcpNoDelay(true);
095:
096: byte[] buf = new byte[1024];
097: int index = 0;
098:
099: /*
100: +----+----------+----------+
101: |VER | NMETHODS | METHODS |
102: +----+----------+----------+
103: | 1 | 1 | 1 to 255 |
104: +----+----------+----------+
105:
106: The VER field is set to X'05' for this version of the protocol. The
107: NMETHODS field contains the number of method identifier octets that
108: appear in the METHODS field.
109:
110: The values currently defined for METHOD are:
111:
112: o X'00' NO AUTHENTICATION REQUIRED
113: o X'01' GSSAPI
114: o X'02' USERNAME/PASSWORD
115: o X'03' to X'7F' IANA ASSIGNED
116: o X'80' to X'FE' RESERVED FOR PRIVATE METHODS
117: o X'FF' NO ACCEPTABLE METHODS
118: */
119:
120: buf[index++] = 5;
121:
122: buf[index++] = 2;
123: buf[index++] = 0; // NO AUTHENTICATION REQUIRED
124: buf[index++] = 2; // USERNAME/PASSWORD
125:
126: out.write(buf, 0, index);
127:
128: /*
129: The server selects from one of the methods given in METHODS, and
130: sends a METHOD selection message:
131:
132: +----+--------+
133: |VER | METHOD |
134: +----+--------+
135: | 1 | 1 |
136: +----+--------+
137: */
138: //in.read(buf, 0, 2);
139: fill(in, buf, 2);
140:
141: boolean check = false;
142: switch ((buf[1]) & 0xff) {
143: case 0: // NO AUTHENTICATION REQUIRED
144: check = true;
145: break;
146: case 2: // USERNAME/PASSWORD
147: if (user == null || passwd == null)
148: break;
149:
150: /*
151: Once the SOCKS V5 server has started, and the client has selected the
152: Username/Password Authentication protocol, the Username/Password
153: subnegotiation begins. This begins with the client producing a
154: Username/Password request:
155:
156: +----+------+----------+------+----------+
157: |VER | ULEN | UNAME | PLEN | PASSWD |
158: +----+------+----------+------+----------+
159: | 1 | 1 | 1 to 255 | 1 | 1 to 255 |
160: +----+------+----------+------+----------+
161:
162: The VER field contains the current version of the subnegotiation,
163: which is X'01'. The ULEN field contains the length of the UNAME field
164: that follows. The UNAME field contains the username as known to the
165: source operating system. The PLEN field contains the length of the
166: PASSWD field that follows. The PASSWD field contains the password
167: association with the given UNAME.
168: */
169: index = 0;
170: buf[index++] = 1;
171: buf[index++] = (byte) (user.length());
172: System.arraycopy(user.getBytes(), 0, buf, index, user
173: .length());
174: index += user.length();
175: buf[index++] = (byte) (passwd.length());
176: System.arraycopy(passwd.getBytes(), 0, buf, index,
177: passwd.length());
178: index += passwd.length();
179:
180: out.write(buf, 0, index);
181:
182: /*
183: The server verifies the supplied UNAME and PASSWD, and sends the
184: following response:
185:
186: +----+--------+
187: |VER | STATUS |
188: +----+--------+
189: | 1 | 1 |
190: +----+--------+
191:
192: A STATUS field of X'00' indicates success. If the server returns a
193: `failure' (STATUS value other than X'00') status, it MUST close the
194: connection.
195: */
196: //in.read(buf, 0, 2);
197: fill(in, buf, 2);
198: if (buf[1] == 0)
199: check = true;
200: break;
201: default:
202: }
203:
204: if (!check) {
205: try {
206: socket.close();
207: } catch (Exception eee) {
208: }
209: throw new JSchException("fail in SOCKS5 proxy");
210: }
211:
212: /*
213: The SOCKS request is formed as follows:
214:
215: +----+-----+-------+------+----------+----------+
216: |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT |
217: +----+-----+-------+------+----------+----------+
218: | 1 | 1 | X'00' | 1 | Variable | 2 |
219: +----+-----+-------+------+----------+----------+
220:
221: Where:
222:
223: o VER protocol version: X'05'
224: o CMD
225: o CONNECT X'01'
226: o BIND X'02'
227: o UDP ASSOCIATE X'03'
228: o RSV RESERVED
229: o ATYP address type of following address
230: o IP V4 address: X'01'
231: o DOMAINNAME: X'03'
232: o IP V6 address: X'04'
233: o DST.ADDR desired destination address
234: o DST.PORT desired destination port in network octet
235: order
236: */
237:
238: index = 0;
239: buf[index++] = 5;
240: buf[index++] = 1; // CONNECT
241: buf[index++] = 0;
242:
243: byte[] hostb = host.getBytes();
244: int len = hostb.length;
245: buf[index++] = 3; // DOMAINNAME
246: buf[index++] = (byte) (len);
247: System.arraycopy(hostb, 0, buf, index, len);
248: index += len;
249: buf[index++] = (byte) (port >>> 8);
250: buf[index++] = (byte) (port & 0xff);
251:
252: out.write(buf, 0, index);
253:
254: /*
255: The SOCKS request information is sent by the client as soon as it has
256: established a connection to the SOCKS server, and completed the
257: authentication negotiations. The server evaluates the request, and
258: returns a reply formed as follows:
259:
260: +----+-----+-------+------+----------+----------+
261: |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT |
262: +----+-----+-------+------+----------+----------+
263: | 1 | 1 | X'00' | 1 | Variable | 2 |
264: +----+-----+-------+------+----------+----------+
265:
266: Where:
267:
268: o VER protocol version: X'05'
269: o REP Reply field:
270: o X'00' succeeded
271: o X'01' general SOCKS server failure
272: o X'02' connection not allowed by ruleset
273: o X'03' Network unreachable
274: o X'04' Host unreachable
275: o X'05' Connection refused
276: o X'06' TTL expired
277: o X'07' Command not supported
278: o X'08' Address type not supported
279: o X'09' to X'FF' unassigned
280: o RSV RESERVED
281: o ATYP address type of following address
282: o IP V4 address: X'01'
283: o DOMAINNAME: X'03'
284: o IP V6 address: X'04'
285: o BND.ADDR server bound address
286: o BND.PORT server bound port in network octet order
287: */
288:
289: //in.read(buf, 0, 4);
290: fill(in, buf, 4);
291:
292: if (buf[1] != 0) {
293: try {
294: socket.close();
295: } catch (Exception eee) {
296: }
297: throw new JSchException("ProxySOCKS5: server returns "
298: + buf[1]);
299: }
300:
301: switch (buf[3] & 0xff) {
302: case 1:
303: //in.read(buf, 0, 6);
304: fill(in, buf, 6);
305: break;
306: case 3:
307: //in.read(buf, 0, 1);
308: fill(in, buf, 1);
309: //in.read(buf, 0, buf[0]+2);
310: fill(in, buf, (buf[0] & 0xff) + 2);
311: break;
312: case 4:
313: //in.read(buf, 0, 18);
314: fill(in, buf, 18);
315: break;
316: default:
317: }
318: } catch (RuntimeException e) {
319: throw e;
320: } catch (Exception e) {
321: try {
322: if (socket != null)
323: socket.close();
324: } catch (Exception eee) {
325: }
326: String message = "ProxySOCKS5: " + e.toString();
327: if (e instanceof Throwable)
328: throw new JSchException(message, (Throwable) e);
329: throw new JSchException(message);
330: }
331: }
332:
333: public InputStream getInputStream() {
334: return in;
335: }
336:
337: public OutputStream getOutputStream() {
338: return out;
339: }
340:
341: public Socket getSocket() {
342: return socket;
343: }
344:
345: public void close() {
346: try {
347: if (in != null)
348: in.close();
349: if (out != null)
350: out.close();
351: if (socket != null)
352: socket.close();
353: } catch (Exception e) {
354: }
355: in = null;
356: out = null;
357: socket = null;
358: }
359:
360: public static int getDefaultPort() {
361: return DEFAULTPORT;
362: }
363:
364: private void fill(InputStream in, byte[] buf, int len)
365: throws JSchException, IOException {
366: int s = 0;
367: while (s < len) {
368: int i = in.read(buf, s, len - s);
369: if (i <= 0) {
370: throw new JSchException("ProxySOCKS5: stream is closed");
371: }
372: s += i;
373: }
374: }
375: }
|