aboutsummaryrefslogtreecommitdiff
path: root/src/org/jivesoftware/smack/proxy
diff options
context:
space:
mode:
Diffstat (limited to 'src/org/jivesoftware/smack/proxy')
-rw-r--r--src/org/jivesoftware/smack/proxy/DirectSocketFactory.java74
-rw-r--r--src/org/jivesoftware/smack/proxy/HTTPProxySocketFactory.java172
-rw-r--r--src/org/jivesoftware/smack/proxy/ProxyException.java44
-rw-r--r--src/org/jivesoftware/smack/proxy/ProxyInfo.java131
-rw-r--r--src/org/jivesoftware/smack/proxy/Socks4ProxySocketFactory.java216
-rw-r--r--src/org/jivesoftware/smack/proxy/Socks5ProxySocketFactory.java375
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;
+ }
+ }
+}