[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:
Daniel Henninger 2008-07-15 02:59:21 +00:00 committed by dhenninger
parent 708284ca46
commit 4c4cc77e42
7 changed files with 1875 additions and 4 deletions

View File

@ -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;
@ -71,6 +72,9 @@ public class ConnectionConfiguration implements Cloneable {
private String resource;
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.
@ -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());
}
/**
* 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.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();
}
/**

View 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);
}
}

View 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*");
}

View 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");
}
}

View 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;
}
}
}

View File

@ -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());
}
}
}

View File

@ -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;
}
}
}