001: /* -*-mode:java; c-basic-offset:2; indent-tabs-mode:nil -*- */
002: /*
003: Copyright (c) 2006-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: - SOCKS: A protocol for TCP proxy across firewalls, Ying-Da Lee
033: http://www.socks.nec.com/protocol/socks4.protocol
034: */
035:
036: package com.jcraft.jsch;
037:
038: import java.io.*;
039: import java.net.*;
040:
041: public class ProxySOCKS4 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 ProxySOCKS4(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 ProxySOCKS4(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: 1) CONNECT
101:
102: The client connects to the SOCKS server and sends a CONNECT request when
103: it wants to establish a connection to an application server. The client
104: includes in the request packet the IP address and the port number of the
105: destination host, and userid, in the following format.
106:
107: +----+----+----+----+----+----+----+----+----+----+....+----+
108: | VN | CD | DSTPORT | DSTIP | USERID |NULL|
109: +----+----+----+----+----+----+----+----+----+----+....+----+
110: # of bytes: 1 1 2 4 variable 1
111:
112: VN is the SOCKS protocol version number and should be 4. CD is the
113: SOCKS command code and should be 1 for CONNECT request. NULL is a byte
114: of all zero bits.
115: */
116:
117: index = 0;
118: buf[index++] = 4;
119: buf[index++] = 1;
120:
121: buf[index++] = (byte) (port >>> 8);
122: buf[index++] = (byte) (port & 0xff);
123:
124: try {
125: InetAddress addr = InetAddress.getByName(host);
126: byte[] byteAddress = addr.getAddress();
127: for (int i = 0; i < byteAddress.length; i++) {
128: buf[index++] = byteAddress[i];
129: }
130: } catch (UnknownHostException uhe) {
131: throw new JSchException("ProxySOCKS4: "
132: + uhe.toString(), uhe);
133: }
134:
135: if (user != null) {
136: System.arraycopy(user.getBytes(), 0, buf, index, user
137: .length());
138: index += user.length();
139: }
140: buf[index++] = 0;
141: out.write(buf, 0, index);
142:
143: /*
144: The SOCKS server checks to see whether such a request should be granted
145: based on any combination of source IP address, destination IP address,
146: destination port number, the userid, and information it may obtain by
147: consulting IDENT, cf. RFC 1413. If the request is granted, the SOCKS
148: server makes a connection to the specified port of the destination host.
149: A reply packet is sent to the client when this connection is established,
150: or when the request is rejected or the operation fails.
151:
152: +----+----+----+----+----+----+----+----+
153: | VN | CD | DSTPORT | DSTIP |
154: +----+----+----+----+----+----+----+----+
155: # of bytes: 1 1 2 4
156:
157: VN is the version of the reply code and should be 0. CD is the result
158: code with one of the following values:
159:
160: 90: request granted
161: 91: request rejected or failed
162: 92: request rejected becasue SOCKS server cannot connect to
163: identd on the client
164: 93: request rejected because the client program and identd
165: report different user-ids
166:
167: The remaining fields are ignored.
168: */
169:
170: int len = 6;
171: int s = 0;
172: while (s < len) {
173: int i = in.read(buf, s, len - s);
174: if (i <= 0) {
175: throw new JSchException(
176: "ProxySOCKS4: stream is closed");
177: }
178: s += i;
179: }
180: if (buf[0] != 0) {
181: throw new JSchException(
182: "ProxySOCKS4: server returns VN " + buf[0]);
183: }
184: if (buf[1] != 90) {
185: try {
186: socket.close();
187: } catch (Exception eee) {
188: }
189: String message = "ProxySOCKS4: server returns CD "
190: + buf[1];
191: throw new JSchException(message);
192: }
193: } catch (RuntimeException e) {
194: throw e;
195: } catch (Exception e) {
196: try {
197: if (socket != null)
198: socket.close();
199: } catch (Exception eee) {
200: }
201: throw new JSchException("ProxySOCKS4: " + e.toString());
202: }
203: }
204:
205: public InputStream getInputStream() {
206: return in;
207: }
208:
209: public OutputStream getOutputStream() {
210: return out;
211: }
212:
213: public Socket getSocket() {
214: return socket;
215: }
216:
217: public void close() {
218: try {
219: if (in != null)
220: in.close();
221: if (out != null)
222: out.close();
223: if (socket != null)
224: socket.close();
225: } catch (Exception e) {
226: }
227: in = null;
228: out = null;
229: socket = null;
230: }
231:
232: public static int getDefaultPort() {
233: return DEFAULTPORT;
234: }
235: }
|