mirror of
https://github.com/vanitasvitae/Smack.git
synced 2024-11-26 14:02:06 +01:00
[SMACK-226] Added proxy support via patch from Atul Aggarwal.
git-svn-id: http://svn.igniterealtime.org/svn/repos/smack/trunk@10673 b35dd754-fafc-0310-a699-88a17e54d16e
This commit is contained in:
parent
708284ca46
commit
4c4cc77e42
7 changed files with 1875 additions and 4 deletions
|
@ -21,6 +21,7 @@
|
||||||
package org.jivesoftware.smack;
|
package org.jivesoftware.smack;
|
||||||
|
|
||||||
import org.jivesoftware.smack.util.DNSUtil;
|
import org.jivesoftware.smack.util.DNSUtil;
|
||||||
|
import org.jivesoftware.smack.proxy.ProxyInfo;
|
||||||
|
|
||||||
import javax.net.SocketFactory;
|
import javax.net.SocketFactory;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
@ -71,6 +72,9 @@ public class ConnectionConfiguration implements Cloneable {
|
||||||
private String resource;
|
private String resource;
|
||||||
private boolean sendPresence;
|
private boolean sendPresence;
|
||||||
private SecurityMode securityMode = SecurityMode.enabled;
|
private SecurityMode securityMode = SecurityMode.enabled;
|
||||||
|
|
||||||
|
// Holds the proxy information (such as proxyhost, proxyport, username, password etc)
|
||||||
|
private ProxyInfo proxy;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new ConnectionConfiguration for the specified service name.
|
* Creates a new ConnectionConfiguration for the specified service name.
|
||||||
|
@ -82,7 +86,23 @@ public class ConnectionConfiguration implements Cloneable {
|
||||||
public ConnectionConfiguration(String serviceName) {
|
public ConnectionConfiguration(String serviceName) {
|
||||||
// Perform DNS lookup to get host and port to use
|
// Perform DNS lookup to get host and port to use
|
||||||
DNSUtil.HostAddress address = DNSUtil.resolveXMPPDomain(serviceName);
|
DNSUtil.HostAddress address = DNSUtil.resolveXMPPDomain(serviceName);
|
||||||
init(address.getHost(), address.getPort(), serviceName);
|
init(address.getHost(), address.getPort(), serviceName,
|
||||||
|
ProxyInfo.forDefaultProxy());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new ConnectionConfiguration for the specified service name
|
||||||
|
* with specified proxy.
|
||||||
|
* A DNS SRV lookup will be performed to find out the actual host address
|
||||||
|
* and port to use for the connection.
|
||||||
|
*
|
||||||
|
* @param serviceName the name of the service provided by an XMPP server.
|
||||||
|
* @param proxy the proxy through which XMPP is to be connected
|
||||||
|
*/
|
||||||
|
public ConnectionConfiguration(String serviceName,ProxyInfo proxy) {
|
||||||
|
// Perform DNS lookup to get host and port to use
|
||||||
|
DNSUtil.HostAddress address = DNSUtil.resolveXMPPDomain(serviceName);
|
||||||
|
init(address.getHost(), address.getPort(), serviceName, proxy);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -100,7 +120,26 @@ public class ConnectionConfiguration implements Cloneable {
|
||||||
* @param serviceName the name of the service provided by an XMPP server.
|
* @param serviceName the name of the service provided by an XMPP server.
|
||||||
*/
|
*/
|
||||||
public ConnectionConfiguration(String host, int port, String serviceName) {
|
public ConnectionConfiguration(String host, int port, String serviceName) {
|
||||||
init(host, port, serviceName);
|
init(host, port, serviceName, ProxyInfo.forDefaultProxy());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new ConnectionConfiguration using the specified host, port and
|
||||||
|
* service name. This is useful for manually overriding the DNS SRV lookup
|
||||||
|
* process that's used with the {@link #ConnectionConfiguration(String)}
|
||||||
|
* constructor. For example, say that an XMPP server is running at localhost
|
||||||
|
* in an internal network on port 5222 but is configured to think that it's
|
||||||
|
* "example.com" for testing purposes. This constructor is necessary to connect
|
||||||
|
* to the server in that case since a DNS SRV lookup for example.com would not
|
||||||
|
* point to the local testing server.
|
||||||
|
*
|
||||||
|
* @param host the host where the XMPP server is running.
|
||||||
|
* @param port the port where the XMPP is listening.
|
||||||
|
* @param serviceName the name of the service provided by an XMPP server.
|
||||||
|
* @param proxy the proxy through which XMPP is to be connected
|
||||||
|
*/
|
||||||
|
public ConnectionConfiguration(String host, int port, String serviceName, ProxyInfo proxy) {
|
||||||
|
init(host, port, serviceName, proxy);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -111,13 +150,26 @@ public class ConnectionConfiguration implements Cloneable {
|
||||||
* @param port the port where the XMPP is listening.
|
* @param port the port where the XMPP is listening.
|
||||||
*/
|
*/
|
||||||
public ConnectionConfiguration(String host, int port) {
|
public ConnectionConfiguration(String host, int port) {
|
||||||
init(host, port, host);
|
init(host, port, host, ProxyInfo.forDefaultProxy());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new ConnectionConfiguration for a connection that will connect
|
||||||
|
* to the desired host and port with desired proxy.
|
||||||
|
*
|
||||||
|
* @param host the host where the XMPP server is running.
|
||||||
|
* @param port the port where the XMPP is listening.
|
||||||
|
* @param proxy the proxy through which XMPP is to be connected
|
||||||
|
*/
|
||||||
|
public ConnectionConfiguration(String host, int port, ProxyInfo proxy) {
|
||||||
|
init(host, port, host, proxy);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void init(String host, int port, String serviceName) {
|
private void init(String host, int port, String serviceName, ProxyInfo proxy) {
|
||||||
this.host = host;
|
this.host = host;
|
||||||
this.port = port;
|
this.port = port;
|
||||||
this.serviceName = serviceName;
|
this.serviceName = serviceName;
|
||||||
|
this.proxy = proxy;
|
||||||
|
|
||||||
// Build the default path to the cacert truststore file. By default we are
|
// Build the default path to the cacert truststore file. By default we are
|
||||||
// going to use the file located in $JREHOME/lib/security/cacerts.
|
// going to use the file located in $JREHOME/lib/security/cacerts.
|
||||||
|
@ -134,6 +186,9 @@ public class ConnectionConfiguration implements Cloneable {
|
||||||
keystorePath = System.getProperty("javax.net.ssl.keyStore");
|
keystorePath = System.getProperty("javax.net.ssl.keyStore");
|
||||||
keystoreType = "jks";
|
keystoreType = "jks";
|
||||||
pkcs11Library = "pkcs11.config";
|
pkcs11Library = "pkcs11.config";
|
||||||
|
|
||||||
|
//Setting the SocketFactory according to proxy supplied
|
||||||
|
socketFactory = proxy.getSocketFactory();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
108
source/org/jivesoftware/smack/proxy/DirectSocketFactory.java
Normal file
108
source/org/jivesoftware/smack/proxy/DirectSocketFactory.java
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
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()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public Socket createSocket(String host, int port)
|
||||||
|
throws IOException, UnknownHostException
|
||||||
|
{
|
||||||
|
Socket newSocket = new Socket(Proxy.NO_PROXY);
|
||||||
|
newSocket.connect(new InetSocketAddress(host,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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
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()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public Socket createSocket(String host, int port)
|
||||||
|
throws IOException, UnknownHostException
|
||||||
|
{
|
||||||
|
Socket newSocket = new Socket(Proxy.NO_PROXY);
|
||||||
|
newSocket.connect(new InetSocketAddress(host,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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
312
source/org/jivesoftware/smack/proxy/HTTPProxySocketFactory.java
Normal file
312
source/org/jivesoftware/smack/proxy/HTTPProxySocketFactory.java
Normal file
|
@ -0,0 +1,312 @@
|
||||||
|
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.Base64;
|
||||||
|
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 "
|
||||||
|
+ new String (Base64.encodeBytes((username + ":"
|
||||||
|
+ password).getBytes("UTF-8")));
|
||||||
|
}
|
||||||
|
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*");
|
||||||
|
|
||||||
|
}
|
||||||
|
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.Base64;
|
||||||
|
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 "
|
||||||
|
+ new String (Base64.encodeBytes((username + ":"
|
||||||
|
+ password).getBytes("UTF-8")));
|
||||||
|
}
|
||||||
|
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*");
|
||||||
|
|
||||||
|
}
|
54
source/org/jivesoftware/smack/proxy/ProxyException.java
Normal file
54
source/org/jivesoftware/smack/proxy/ProxyException.java
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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");
|
||||||
|
}
|
||||||
|
}
|
228
source/org/jivesoftware/smack/proxy/ProxyInfo.java
Normal file
228
source/org/jivesoftware/smack/proxy/ProxyInfo.java
Normal file
|
@ -0,0 +1,228 @@
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,398 @@
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,716 @@
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue