diff options
Diffstat (limited to 'src/org/jivesoftware/smack/proxy')
6 files changed, 1012 insertions, 0 deletions
diff --git a/src/org/jivesoftware/smack/proxy/DirectSocketFactory.java b/src/org/jivesoftware/smack/proxy/DirectSocketFactory.java new file mode 100644 index 0000000..6197c38 --- /dev/null +++ b/src/org/jivesoftware/smack/proxy/DirectSocketFactory.java @@ -0,0 +1,74 @@ +/** + * $RCSfile$ + * $Revision$ + * $Date$ + * + * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smack.proxy; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.net.Socket; +import java.net.UnknownHostException; +import javax.net.SocketFactory; + +/** + * SocketFactory for direct connection + * + * @author Atul Aggarwal + */ +class DirectSocketFactory + extends SocketFactory +{ + + public DirectSocketFactory() + { + } + + static private int roundrobin = 0; + + public Socket createSocket(String host, int port) + throws IOException, UnknownHostException + { + Socket newSocket = new Socket(Proxy.NO_PROXY); + InetAddress resolved[] = InetAddress.getAllByName(host); + newSocket.connect(new InetSocketAddress(resolved[(roundrobin++) % resolved.length],port)); + return newSocket; + } + + public Socket createSocket(String host ,int port, InetAddress localHost, + int localPort) + throws IOException, UnknownHostException + { + return new Socket(host,port,localHost,localPort); + } + + public Socket createSocket(InetAddress host, int port) + throws IOException + { + Socket newSocket = new Socket(Proxy.NO_PROXY); + newSocket.connect(new InetSocketAddress(host,port)); + return newSocket; + } + + public Socket createSocket( InetAddress address, int port, + InetAddress localAddress, int localPort) + throws IOException + { + return new Socket(address,port,localAddress,localPort); + } + +} diff --git a/src/org/jivesoftware/smack/proxy/HTTPProxySocketFactory.java b/src/org/jivesoftware/smack/proxy/HTTPProxySocketFactory.java new file mode 100644 index 0000000..4ee5dd6 --- /dev/null +++ b/src/org/jivesoftware/smack/proxy/HTTPProxySocketFactory.java @@ -0,0 +1,172 @@ +/** + * $RCSfile$ + * $Revision$ + * $Date$ + * + * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smack.proxy; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.StringReader; +import java.net.HttpURLConnection; +import java.net.InetAddress; +import java.net.Socket; +import java.net.UnknownHostException; +import javax.net.SocketFactory; +import org.jivesoftware.smack.util.StringUtils; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Http Proxy Socket Factory which returns socket connected to Http Proxy + * + * @author Atul Aggarwal + */ +class HTTPProxySocketFactory + extends SocketFactory +{ + + private ProxyInfo proxy; + + public HTTPProxySocketFactory(ProxyInfo proxy) + { + this.proxy = proxy; + } + + public Socket createSocket(String host, int port) + throws IOException, UnknownHostException + { + return httpProxifiedSocket(host, port); + } + + public Socket createSocket(String host ,int port, InetAddress localHost, + int localPort) + throws IOException, UnknownHostException + { + return httpProxifiedSocket(host, port); + } + + public Socket createSocket(InetAddress host, int port) + throws IOException + { + return httpProxifiedSocket(host.getHostAddress(), port); + + } + + public Socket createSocket( InetAddress address, int port, + InetAddress localAddress, int localPort) + throws IOException + { + return httpProxifiedSocket(address.getHostAddress(), port); + } + + private Socket httpProxifiedSocket(String host, int port) + throws IOException + { + String proxyhost = proxy.getProxyAddress(); + int proxyPort = proxy.getProxyPort(); + Socket socket = new Socket(proxyhost,proxyPort); + String hostport = "CONNECT " + host + ":" + port; + String proxyLine; + String username = proxy.getProxyUsername(); + if (username == null) + { + proxyLine = ""; + } + else + { + String password = proxy.getProxyPassword(); + proxyLine = "\r\nProxy-Authorization: Basic " + StringUtils.encodeBase64(username + ":" + password); + } + socket.getOutputStream().write((hostport + " HTTP/1.1\r\nHost: " + + hostport + proxyLine + "\r\n\r\n").getBytes("UTF-8")); + + InputStream in = socket.getInputStream(); + StringBuilder got = new StringBuilder(100); + int nlchars = 0; + + while (true) + { + char c = (char) in.read(); + got.append(c); + if (got.length() > 1024) + { + throw new ProxyException(ProxyInfo.ProxyType.HTTP, "Recieved " + + "header of >1024 characters from " + + proxyhost + ", cancelling connection"); + } + if (c == -1) + { + throw new ProxyException(ProxyInfo.ProxyType.HTTP); + } + if ((nlchars == 0 || nlchars == 2) && c == '\r') + { + nlchars++; + } + else if ((nlchars == 1 || nlchars == 3) && c == '\n') + { + nlchars++; + } + else + { + nlchars = 0; + } + if (nlchars == 4) + { + break; + } + } + + if (nlchars != 4) + { + throw new ProxyException(ProxyInfo.ProxyType.HTTP, "Never " + + "received blank line from " + + proxyhost + ", cancelling connection"); + } + + String gotstr = got.toString(); + + BufferedReader br = new BufferedReader(new StringReader(gotstr)); + String response = br.readLine(); + + if (response == null) + { + throw new ProxyException(ProxyInfo.ProxyType.HTTP, "Empty proxy " + + "response from " + proxyhost + ", cancelling"); + } + + Matcher m = RESPONSE_PATTERN.matcher(response); + if (!m.matches()) + { + throw new ProxyException(ProxyInfo.ProxyType.HTTP , "Unexpected " + + "proxy response from " + proxyhost + ": " + response); + } + + int code = Integer.parseInt(m.group(1)); + + if (code != HttpURLConnection.HTTP_OK) + { + throw new ProxyException(ProxyInfo.ProxyType.HTTP); + } + + return socket; + } + + private static final Pattern RESPONSE_PATTERN + = Pattern.compile("HTTP/\\S+\\s(\\d+)\\s(.*)\\s*"); + +} diff --git a/src/org/jivesoftware/smack/proxy/ProxyException.java b/src/org/jivesoftware/smack/proxy/ProxyException.java new file mode 100644 index 0000000..b37910c --- /dev/null +++ b/src/org/jivesoftware/smack/proxy/ProxyException.java @@ -0,0 +1,44 @@ +/** + * $RCSfile$ + * $Revision$ + * $Date$ + * + * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smack.proxy; + +import java.io.IOException; + +/** + * An exception class to handle exceptions caused by proxy. + * + * @author Atul Aggarwal + */ +public class ProxyException + extends IOException +{ + public ProxyException(ProxyInfo.ProxyType type, String ex, Throwable cause) + { + super("Proxy Exception " + type.toString() + " : "+ex+", "+cause); + } + + public ProxyException(ProxyInfo.ProxyType type, String ex) + { + super("Proxy Exception " + type.toString() + " : "+ex); + } + + public ProxyException(ProxyInfo.ProxyType type) + { + super("Proxy Exception " + type.toString() + " : " + "Unknown Error"); + } +} diff --git a/src/org/jivesoftware/smack/proxy/ProxyInfo.java b/src/org/jivesoftware/smack/proxy/ProxyInfo.java new file mode 100644 index 0000000..5a7d354 --- /dev/null +++ b/src/org/jivesoftware/smack/proxy/ProxyInfo.java @@ -0,0 +1,131 @@ +/** + * $RCSfile$ + * $Revision$ + * $Date$ + * + * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smack.proxy; + +import javax.net.SocketFactory; + +/** + * Class which stores proxy information such as proxy type, host, port, + * authentication etc. + * + * @author Atul Aggarwal + */ + +public class ProxyInfo +{ + public static enum ProxyType + { + NONE, + HTTP, + SOCKS4, + SOCKS5 + } + + private String proxyAddress; + private int proxyPort; + private String proxyUsername; + private String proxyPassword; + private ProxyType proxyType; + + public ProxyInfo( ProxyType pType, String pHost, int pPort, String pUser, + String pPass) + { + this.proxyType = pType; + this.proxyAddress = pHost; + this.proxyPort = pPort; + this.proxyUsername = pUser; + this.proxyPassword = pPass; + } + + public static ProxyInfo forHttpProxy(String pHost, int pPort, String pUser, + String pPass) + { + return new ProxyInfo(ProxyType.HTTP, pHost, pPort, pUser, pPass); + } + + public static ProxyInfo forSocks4Proxy(String pHost, int pPort, String pUser, + String pPass) + { + return new ProxyInfo(ProxyType.SOCKS4, pHost, pPort, pUser, pPass); + } + + public static ProxyInfo forSocks5Proxy(String pHost, int pPort, String pUser, + String pPass) + { + return new ProxyInfo(ProxyType.SOCKS5, pHost, pPort, pUser, pPass); + } + + public static ProxyInfo forNoProxy() + { + return new ProxyInfo(ProxyType.NONE, null, 0, null, null); + } + + public static ProxyInfo forDefaultProxy() + { + return new ProxyInfo(ProxyType.NONE, null, 0, null, null); + } + + public ProxyType getProxyType() + { + return proxyType; + } + + public String getProxyAddress() + { + return proxyAddress; + } + + public int getProxyPort() + { + return proxyPort; + } + + public String getProxyUsername() + { + return proxyUsername; + } + + public String getProxyPassword() + { + return proxyPassword; + } + + public SocketFactory getSocketFactory() + { + if(proxyType == ProxyType.NONE) + { + return new DirectSocketFactory(); + } + else if(proxyType == ProxyType.HTTP) + { + return new HTTPProxySocketFactory(this); + } + else if(proxyType == ProxyType.SOCKS4) + { + return new Socks4ProxySocketFactory(this); + } + else if(proxyType == ProxyType.SOCKS5) + { + return new Socks5ProxySocketFactory(this); + } + else + { + return null; + } + } +} diff --git a/src/org/jivesoftware/smack/proxy/Socks4ProxySocketFactory.java b/src/org/jivesoftware/smack/proxy/Socks4ProxySocketFactory.java new file mode 100644 index 0000000..6a32c11 --- /dev/null +++ b/src/org/jivesoftware/smack/proxy/Socks4ProxySocketFactory.java @@ -0,0 +1,216 @@ +/** + * $RCSfile$ + * $Revision$ + * $Date$ + * + * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smack.proxy; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.Socket; +import java.net.UnknownHostException; +import javax.net.SocketFactory; + +/** + * Socket factory for socks4 proxy + * + * @author Atul Aggarwal + */ +public class Socks4ProxySocketFactory + extends SocketFactory +{ + private ProxyInfo proxy; + + public Socks4ProxySocketFactory(ProxyInfo proxy) + { + this.proxy = proxy; + } + + public Socket createSocket(String host, int port) + throws IOException, UnknownHostException + { + return socks4ProxifiedSocket(host,port); + + } + + public Socket createSocket(String host ,int port, InetAddress localHost, + int localPort) + throws IOException, UnknownHostException + { + return socks4ProxifiedSocket(host,port); + } + + public Socket createSocket(InetAddress host, int port) + throws IOException + { + return socks4ProxifiedSocket(host.getHostAddress(),port); + } + + public Socket createSocket( InetAddress address, int port, + InetAddress localAddress, int localPort) + throws IOException + { + return socks4ProxifiedSocket(address.getHostAddress(),port); + + } + + private Socket socks4ProxifiedSocket(String host, int port) + throws IOException + { + Socket socket = null; + InputStream in = null; + OutputStream out = null; + String proxy_host = proxy.getProxyAddress(); + int proxy_port = proxy.getProxyPort(); + String user = proxy.getProxyUsername(); + String passwd = proxy.getProxyPassword(); + + try + { + socket=new Socket(proxy_host, proxy_port); + in=socket.getInputStream(); + out=socket.getOutputStream(); + socket.setTcpNoDelay(true); + + byte[] buf=new byte[1024]; + int index=0; + + /* + 1) CONNECT + + The client connects to the SOCKS server and sends a CONNECT request when + it wants to establish a connection to an application server. The client + includes in the request packet the IP address and the port number of the + destination host, and userid, in the following format. + + +----+----+----+----+----+----+----+----+----+----+....+----+ + | VN | CD | DSTPORT | DSTIP | USERID |NULL| + +----+----+----+----+----+----+----+----+----+----+....+----+ + # of bytes: 1 1 2 4 variable 1 + + VN is the SOCKS protocol version number and should be 4. CD is the + SOCKS command code and should be 1 for CONNECT request. NULL is a byte + of all zero bits. + */ + + index=0; + buf[index++]=4; + buf[index++]=1; + + buf[index++]=(byte)(port>>>8); + buf[index++]=(byte)(port&0xff); + + try + { + InetAddress addr=InetAddress.getByName(host); + byte[] byteAddress = addr.getAddress(); + for (int i = 0; i < byteAddress.length; i++) + { + buf[index++]=byteAddress[i]; + } + } + catch(UnknownHostException uhe) + { + throw new ProxyException(ProxyInfo.ProxyType.SOCKS4, + uhe.toString(), uhe); + } + + if(user!=null) + { + System.arraycopy(user.getBytes(), 0, buf, index, user.length()); + index+=user.length(); + } + buf[index++]=0; + out.write(buf, 0, index); + + /* + The SOCKS server checks to see whether such a request should be granted + based on any combination of source IP address, destination IP address, + destination port number, the userid, and information it may obtain by + consulting IDENT, cf. RFC 1413. If the request is granted, the SOCKS + server makes a connection to the specified port of the destination host. + A reply packet is sent to the client when this connection is established, + or when the request is rejected or the operation fails. + + +----+----+----+----+----+----+----+----+ + | VN | CD | DSTPORT | DSTIP | + +----+----+----+----+----+----+----+----+ + # of bytes: 1 1 2 4 + + VN is the version of the reply code and should be 0. CD is the result + code with one of the following values: + + 90: request granted + 91: request rejected or failed + 92: request rejected becasue SOCKS server cannot connect to + identd on the client + 93: request rejected because the client program and identd + report different user-ids + + The remaining fields are ignored. + */ + + int len=6; + int s=0; + while(s<len) + { + int i=in.read(buf, s, len-s); + if(i<=0) + { + throw new ProxyException(ProxyInfo.ProxyType.SOCKS4, + "stream is closed"); + } + s+=i; + } + if(buf[0]!=0) + { + throw new ProxyException(ProxyInfo.ProxyType.SOCKS4, + "server returns VN "+buf[0]); + } + if(buf[1]!=90) + { + try + { + socket.close(); + } + catch(Exception eee) + { + } + String message="ProxySOCKS4: server returns CD "+buf[1]; + throw new ProxyException(ProxyInfo.ProxyType.SOCKS4,message); + } + byte[] temp = new byte[2]; + in.read(temp, 0, 2); + return socket; + } + catch(RuntimeException e) + { + throw e; + } + catch(Exception e) + { + try + { + if(socket!=null)socket.close(); + } + catch(Exception eee) + { + } + throw new ProxyException(ProxyInfo.ProxyType.SOCKS4, e.toString()); + } + } +} diff --git a/src/org/jivesoftware/smack/proxy/Socks5ProxySocketFactory.java b/src/org/jivesoftware/smack/proxy/Socks5ProxySocketFactory.java new file mode 100644 index 0000000..23ef623 --- /dev/null +++ b/src/org/jivesoftware/smack/proxy/Socks5ProxySocketFactory.java @@ -0,0 +1,375 @@ +/** + * $RCSfile$ + * $Revision$ + * $Date$ + * + * All rights reserved. Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.jivesoftware.smack.proxy; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.Socket; +import java.net.UnknownHostException; +import javax.net.SocketFactory; + +/** + * Socket factory for Socks5 proxy + * + * @author Atul Aggarwal + */ +public class Socks5ProxySocketFactory + extends SocketFactory +{ + private ProxyInfo proxy; + + public Socks5ProxySocketFactory(ProxyInfo proxy) + { + this.proxy = proxy; + } + + public Socket createSocket(String host, int port) + throws IOException, UnknownHostException + { + return socks5ProxifiedSocket(host,port); + } + + public Socket createSocket(String host ,int port, InetAddress localHost, + int localPort) + throws IOException, UnknownHostException + { + + return socks5ProxifiedSocket(host,port); + + } + + public Socket createSocket(InetAddress host, int port) + throws IOException + { + + return socks5ProxifiedSocket(host.getHostAddress(),port); + + } + + public Socket createSocket( InetAddress address, int port, + InetAddress localAddress, int localPort) + throws IOException + { + + return socks5ProxifiedSocket(address.getHostAddress(),port); + + } + + private Socket socks5ProxifiedSocket(String host, int port) + throws IOException + { + Socket socket = null; + InputStream in = null; + OutputStream out = null; + String proxy_host = proxy.getProxyAddress(); + int proxy_port = proxy.getProxyPort(); + String user = proxy.getProxyUsername(); + String passwd = proxy.getProxyPassword(); + + try + { + socket=new Socket(proxy_host, proxy_port); + in=socket.getInputStream(); + out=socket.getOutputStream(); + + socket.setTcpNoDelay(true); + + byte[] buf=new byte[1024]; + int index=0; + +/* + +----+----------+----------+ + |VER | NMETHODS | METHODS | + +----+----------+----------+ + | 1 | 1 | 1 to 255 | + +----+----------+----------+ + + The VER field is set to X'05' for this version of the protocol. The + NMETHODS field contains the number of method identifier octets that + appear in the METHODS field. + + The values currently defined for METHOD are: + + o X'00' NO AUTHENTICATION REQUIRED + o X'01' GSSAPI + o X'02' USERNAME/PASSWORD + o X'03' to X'7F' IANA ASSIGNED + o X'80' to X'FE' RESERVED FOR PRIVATE METHODS + o X'FF' NO ACCEPTABLE METHODS +*/ + + buf[index++]=5; + + buf[index++]=2; + buf[index++]=0; // NO AUTHENTICATION REQUIRED + buf[index++]=2; // USERNAME/PASSWORD + + out.write(buf, 0, index); + +/* + The server selects from one of the methods given in METHODS, and + sends a METHOD selection message: + + +----+--------+ + |VER | METHOD | + +----+--------+ + | 1 | 1 | + +----+--------+ +*/ + //in.read(buf, 0, 2); + fill(in, buf, 2); + + boolean check=false; + switch((buf[1])&0xff) + { + case 0: // NO AUTHENTICATION REQUIRED + check=true; + break; + case 2: // USERNAME/PASSWORD + if(user==null || passwd==null) + { + break; + } + +/* + Once the SOCKS V5 server has started, and the client has selected the + Username/Password Authentication protocol, the Username/Password + subnegotiation begins. This begins with the client producing a + Username/Password request: + + +----+------+----------+------+----------+ + |VER | ULEN | UNAME | PLEN | PASSWD | + +----+------+----------+------+----------+ + | 1 | 1 | 1 to 255 | 1 | 1 to 255 | + +----+------+----------+------+----------+ + + The VER field contains the current version of the subnegotiation, + which is X'01'. The ULEN field contains the length of the UNAME field + that follows. The UNAME field contains the username as known to the + source operating system. The PLEN field contains the length of the + PASSWD field that follows. The PASSWD field contains the password + association with the given UNAME. +*/ + index=0; + buf[index++]=1; + buf[index++]=(byte)(user.length()); + System.arraycopy(user.getBytes(), 0, buf, index, + user.length()); + index+=user.length(); + buf[index++]=(byte)(passwd.length()); + System.arraycopy(passwd.getBytes(), 0, buf, index, + passwd.length()); + index+=passwd.length(); + + out.write(buf, 0, index); + +/* + The server verifies the supplied UNAME and PASSWD, and sends the + following response: + + +----+--------+ + |VER | STATUS | + +----+--------+ + | 1 | 1 | + +----+--------+ + + A STATUS field of X'00' indicates success. If the server returns a + `failure' (STATUS value other than X'00') status, it MUST close the + connection. +*/ + //in.read(buf, 0, 2); + fill(in, buf, 2); + if(buf[1]==0) + { + check=true; + } + break; + default: + } + + if(!check) + { + try + { + socket.close(); + } + catch(Exception eee) + { + } + throw new ProxyException(ProxyInfo.ProxyType.SOCKS5, + "fail in SOCKS5 proxy"); + } + +/* + The SOCKS request is formed as follows: + + +----+-----+-------+------+----------+----------+ + |VER | CMD | RSV | ATYP | DST.ADDR | DST.PORT | + +----+-----+-------+------+----------+----------+ + | 1 | 1 | X'00' | 1 | Variable | 2 | + +----+-----+-------+------+----------+----------+ + + Where: + + o VER protocol version: X'05' + o CMD + o CONNECT X'01' + o BIND X'02' + o UDP ASSOCIATE X'03' + o RSV RESERVED + o ATYP address type of following address + o IP V4 address: X'01' + o DOMAINNAME: X'03' + o IP V6 address: X'04' + o DST.ADDR desired destination address + o DST.PORT desired destination port in network octet + order +*/ + + index=0; + buf[index++]=5; + buf[index++]=1; // CONNECT + buf[index++]=0; + + byte[] hostb=host.getBytes(); + int len=hostb.length; + buf[index++]=3; // DOMAINNAME + buf[index++]=(byte)(len); + System.arraycopy(hostb, 0, buf, index, len); + index+=len; + buf[index++]=(byte)(port>>>8); + buf[index++]=(byte)(port&0xff); + + out.write(buf, 0, index); + +/* + The SOCKS request information is sent by the client as soon as it has + established a connection to the SOCKS server, and completed the + authentication negotiations. The server evaluates the request, and + returns a reply formed as follows: + + +----+-----+-------+------+----------+----------+ + |VER | REP | RSV | ATYP | BND.ADDR | BND.PORT | + +----+-----+-------+------+----------+----------+ + | 1 | 1 | X'00' | 1 | Variable | 2 | + +----+-----+-------+------+----------+----------+ + + Where: + + o VER protocol version: X'05' + o REP Reply field: + o X'00' succeeded + o X'01' general SOCKS server failure + o X'02' connection not allowed by ruleset + o X'03' Network unreachable + o X'04' Host unreachable + o X'05' Connection refused + o X'06' TTL expired + o X'07' Command not supported + o X'08' Address type not supported + o X'09' to X'FF' unassigned + o RSV RESERVED + o ATYP address type of following address + o IP V4 address: X'01' + o DOMAINNAME: X'03' + o IP V6 address: X'04' + o BND.ADDR server bound address + o BND.PORT server bound port in network octet order +*/ + + //in.read(buf, 0, 4); + fill(in, buf, 4); + + if(buf[1]!=0) + { + try + { + socket.close(); + } + catch(Exception eee) + { + } + throw new ProxyException(ProxyInfo.ProxyType.SOCKS5, + "server returns "+buf[1]); + } + + switch(buf[3]&0xff) + { + case 1: + //in.read(buf, 0, 6); + fill(in, buf, 6); + break; + case 3: + //in.read(buf, 0, 1); + fill(in, buf, 1); + //in.read(buf, 0, buf[0]+2); + fill(in, buf, (buf[0]&0xff)+2); + break; + case 4: + //in.read(buf, 0, 18); + fill(in, buf, 18); + break; + default: + } + return socket; + + } + catch(RuntimeException e) + { + throw e; + } + catch(Exception e) + { + try + { + if(socket!=null) + { + socket.close(); + } + } + catch(Exception eee) + { + } + String message="ProxySOCKS5: "+e.toString(); + if(e instanceof Throwable) + { + throw new ProxyException(ProxyInfo.ProxyType.SOCKS5,message, + (Throwable)e); + } + throw new IOException(message); + } + } + + private void fill(InputStream in, byte[] buf, int len) + throws IOException + { + int s=0; + while(s<len) + { + int i=in.read(buf, s, len-s); + if(i<=0) + { + throw new ProxyException(ProxyInfo.ProxyType.SOCKS5, "stream " + + "is closed"); + } + s+=i; + } + } +} |