mirror of
https://codeberg.org/Mercury-IM/Smack
synced 2024-11-30 02: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;
|
||||
|
||||
import org.jivesoftware.smack.util.DNSUtil;
|
||||
import org.jivesoftware.smack.proxy.ProxyInfo;
|
||||
|
||||
import javax.net.SocketFactory;
|
||||
import java.io.File;
|
||||
|
@ -72,6 +73,9 @@ public class ConnectionConfiguration implements Cloneable {
|
|||
private boolean sendPresence;
|
||||
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.
|
||||
* A DNS SRV lookup will be performed to find out the actual host address
|
||||
|
@ -82,7 +86,23 @@ public class ConnectionConfiguration implements Cloneable {
|
|||
public ConnectionConfiguration(String serviceName) {
|
||||
// Perform DNS lookup to get host and port to use
|
||||
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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
public ConnectionConfiguration(String host, int port) {
|
||||
init(host, port, host);
|
||||
init(host, port, host, ProxyInfo.forDefaultProxy());
|
||||
}
|
||||
|
||||
private void init(String host, int port, String serviceName) {
|
||||
/**
|
||||
* 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, ProxyInfo proxy) {
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
this.serviceName = serviceName;
|
||||
this.proxy = proxy;
|
||||
|
||||
// Build the default path to the cacert truststore file. By default we are
|
||||
// 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");
|
||||
keystoreType = "jks";
|
||||
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