296 lines
8.0 KiB
Java
296 lines
8.0 KiB
Java
package com.runjva.sourceforge.jsocks.protocol;
|
|
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
import java.io.OutputStream;
|
|
import java.net.InetAddress;
|
|
import java.net.Socket;
|
|
import java.net.SocketException;
|
|
import java.net.UnknownHostException;
|
|
import java.util.Enumeration;
|
|
import java.util.Hashtable;
|
|
|
|
/**
|
|
* SOCKS5 Proxy.
|
|
*/
|
|
|
|
public class Socks5Proxy extends SocksProxyBase implements Cloneable {
|
|
|
|
// Data members
|
|
private Hashtable<Integer, Authentication> authMethods = new Hashtable<Integer, Authentication>();
|
|
private int selectedMethod;
|
|
|
|
boolean resolveAddrLocally = true;
|
|
UDPEncapsulation udp_encapsulation = null;
|
|
|
|
// Public Constructors
|
|
// ====================
|
|
|
|
/**
|
|
* Creates SOCKS5 proxy.
|
|
*
|
|
* @param p
|
|
* Proxy to use to connect to this proxy, allows proxy chaining.
|
|
* @param proxyHost
|
|
* Host on which a Proxy server runs.
|
|
* @param proxyPort
|
|
* Port on which a Proxy server listens for connections.
|
|
* @throws UnknownHostException
|
|
* If proxyHost can't be resolved.
|
|
*/
|
|
public Socks5Proxy(SocksProxyBase p, String proxyHost, int proxyPort)
|
|
throws UnknownHostException {
|
|
|
|
super(p, proxyHost, proxyPort);
|
|
version = 5;
|
|
setAuthenticationMethod(0, new AuthenticationNone());
|
|
}
|
|
|
|
/**
|
|
* Creates SOCKS5 proxy.
|
|
*
|
|
* @param proxyHost
|
|
* Host on which a Proxy server runs.
|
|
* @param proxyPort
|
|
* Port on which a Proxy server listens for connections.
|
|
* @throws UnknownHostException
|
|
* If proxyHost can't be resolved.
|
|
*/
|
|
public Socks5Proxy(String proxyHost, int proxyPort)
|
|
throws UnknownHostException {
|
|
this(null, proxyHost, proxyPort);
|
|
}
|
|
|
|
/**
|
|
* Creates SOCKS5 proxy.
|
|
*
|
|
* @param p
|
|
* Proxy to use to connect to this proxy, allows proxy chaining.
|
|
* @param proxyIP
|
|
* Host on which a Proxy server runs.
|
|
* @param proxyPort
|
|
* Port on which a Proxy server listens for connections.
|
|
*/
|
|
public Socks5Proxy(SocksProxyBase p, InetAddress proxyIP, int proxyPort) {
|
|
super(p, proxyIP, proxyPort);
|
|
version = 5;
|
|
setAuthenticationMethod(0, new AuthenticationNone());
|
|
}
|
|
|
|
/**
|
|
* Creates SOCKS5 proxy.
|
|
*
|
|
* @param proxyIP
|
|
* Host on which a Proxy server runs.
|
|
* @param proxyPort
|
|
* Port on which a Proxy server listens for connections.
|
|
*/
|
|
public Socks5Proxy(InetAddress proxyIP, int proxyPort) {
|
|
this(null, proxyIP, proxyPort);
|
|
}
|
|
|
|
// Public instance methods
|
|
// ========================
|
|
|
|
/**
|
|
* Wether to resolve address locally or to let proxy do so.
|
|
* <p>
|
|
* SOCKS5 protocol allows to send host names rather then IPs in the
|
|
* requests, this option controls wether the hostnames should be send to the
|
|
* proxy server as names, or should they be resolved locally.
|
|
*
|
|
* @param doResolve
|
|
* Wether to perform resolution locally.
|
|
* @return Previous settings.
|
|
*/
|
|
public boolean resolveAddrLocally(boolean doResolve) {
|
|
final boolean old = resolveAddrLocally;
|
|
resolveAddrLocally = doResolve;
|
|
return old;
|
|
}
|
|
|
|
/**
|
|
* Get current setting on how the addresses should be handled.
|
|
*
|
|
* @return Current setting for address resolution.
|
|
* @see Socks5Proxy#resolveAddrLocally(boolean doResolve)
|
|
*/
|
|
public boolean resolveAddrLocally() {
|
|
return resolveAddrLocally;
|
|
}
|
|
|
|
/**
|
|
* Adds another authentication method.
|
|
*
|
|
* @param methodId
|
|
* Authentication method id, see rfc1928
|
|
* @param method
|
|
* Implementation of Authentication
|
|
* @see Authentication
|
|
*/
|
|
public boolean setAuthenticationMethod(int methodId, Authentication method) {
|
|
if ((methodId < 0) || (methodId > 255)) {
|
|
return false;
|
|
}
|
|
if (method == null) {
|
|
// Want to remove a particular method
|
|
return (authMethods.remove(new Integer(methodId)) != null);
|
|
} else {// Add the method, or rewrite old one
|
|
authMethods.put(new Integer(methodId), method);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Get authentication method, which corresponds to given method id
|
|
*
|
|
* @param methodId
|
|
* Authentication method id.
|
|
* @return Implementation for given method or null, if one was not set.
|
|
*/
|
|
public Authentication getAuthenticationMethod(int methodId) {
|
|
final Object method = authMethods.get(new Integer(methodId));
|
|
if (method == null) {
|
|
return null;
|
|
}
|
|
return (Authentication) method;
|
|
}
|
|
|
|
/**
|
|
* Creates a clone of this Proxy. clone() returns an
|
|
*/
|
|
@SuppressWarnings("unchecked")
|
|
public Object clone() {
|
|
final Socks5Proxy newProxy = new Socks5Proxy(proxyIP, proxyPort);
|
|
|
|
final Object o = this.authMethods.clone();
|
|
newProxy.authMethods = (Hashtable<Integer, Authentication>) o;
|
|
|
|
newProxy.directHosts = (InetRange) directHosts.clone();
|
|
newProxy.resolveAddrLocally = resolveAddrLocally;
|
|
newProxy.chainProxy = chainProxy;
|
|
return newProxy;
|
|
}
|
|
|
|
// Public Static(Class) Methods
|
|
// ==============================
|
|
|
|
// Protected Methods
|
|
// =================
|
|
|
|
protected SocksProxyBase copy() {
|
|
final Socks5Proxy copy = new Socks5Proxy(proxyIP, proxyPort);
|
|
|
|
copy.authMethods = this.authMethods; // same Hash, no copy
|
|
copy.directHosts = this.directHosts;
|
|
copy.chainProxy = this.chainProxy;
|
|
copy.resolveAddrLocally = this.resolveAddrLocally;
|
|
return copy;
|
|
}
|
|
|
|
/**
|
|
*
|
|
*
|
|
*/
|
|
protected void startSession() throws SocksException {
|
|
super.startSession();
|
|
Authentication auth;
|
|
final Socket ps = proxySocket; // The name is too long
|
|
|
|
try {
|
|
|
|
final byte nMethods = (byte) authMethods.size(); // Number of
|
|
// methods
|
|
|
|
final byte[] buf = new byte[2 + nMethods]; // 2 is for VER,NMETHODS
|
|
buf[0] = (byte) version;
|
|
buf[1] = nMethods; // Number of methods
|
|
int i = 2;
|
|
|
|
final Enumeration<Integer> ids = authMethods.keys();
|
|
while (ids.hasMoreElements()) {
|
|
buf[i++] = (byte) ids.nextElement().intValue();
|
|
}
|
|
|
|
out.write(buf);
|
|
out.flush();
|
|
|
|
final int versionNumber = in.read();
|
|
selectedMethod = in.read();
|
|
|
|
if ((versionNumber < 0) || (selectedMethod < 0)) {
|
|
// EOF condition was reached
|
|
endSession();
|
|
final String s = "Connection to proxy lost.";
|
|
throw new SocksException(SOCKS_PROXY_IO_ERROR, s);
|
|
}
|
|
|
|
if (versionNumber < version) {
|
|
// What should we do??
|
|
}
|
|
|
|
if (selectedMethod == 0xFF) { // No method selected
|
|
ps.close();
|
|
throw (new SocksException(SOCKS_AUTH_NOT_SUPPORTED));
|
|
}
|
|
|
|
auth = getAuthenticationMethod(selectedMethod);
|
|
if (auth == null) {
|
|
// This shouldn't happen, unless method was removed by other
|
|
// thread, or the server stuffed up
|
|
final String s = "Specified Authentication not found!";
|
|
throw new SocksException(SOCKS_JUST_ERROR, s);
|
|
}
|
|
|
|
final Object[] in_out;
|
|
in_out = auth.doSocksAuthentication(selectedMethod, ps);
|
|
|
|
if (in_out == null) {
|
|
// Authentication failed by some reason
|
|
throw (new SocksException(SOCKS_AUTH_FAILURE));
|
|
}
|
|
|
|
/*
|
|
* Most authentication methods are expected to return simply the
|
|
* input/output streams associated with the socket. However if the
|
|
* auth. method requires some kind of encryption/decryption being
|
|
* done on the connection it should provide classes to handle I/O.
|
|
*/
|
|
|
|
in = (InputStream) in_out[0];
|
|
out = (OutputStream) in_out[1];
|
|
if (in_out.length > 2) {
|
|
udp_encapsulation = (UDPEncapsulation) in_out[2];
|
|
}
|
|
|
|
} catch (final SocksException s_ex) {
|
|
throw s_ex;
|
|
} catch (final UnknownHostException uh_ex) {
|
|
throw new SocksException(SOCKS_PROXY_NO_CONNECT, uh_ex);
|
|
} catch (final SocketException so_ex) {
|
|
throw new SocksException(SOCKS_PROXY_NO_CONNECT, so_ex);
|
|
} catch (final IOException io_ex) {
|
|
throw new SocksException(SOCKS_PROXY_IO_ERROR, io_ex);
|
|
}
|
|
}
|
|
|
|
protected ProxyMessage formMessage(int cmd, InetAddress ip, int port) {
|
|
return new Socks5Message(cmd, ip, port);
|
|
}
|
|
|
|
protected ProxyMessage formMessage(int cmd, String host, int port)
|
|
throws UnknownHostException {
|
|
if (resolveAddrLocally) {
|
|
return formMessage(cmd, InetAddress.getByName(host), port);
|
|
} else {
|
|
return new Socks5Message(cmd, host, port);
|
|
}
|
|
}
|
|
|
|
protected ProxyMessage formMessage(InputStream in) throws SocksException,
|
|
IOException {
|
|
return new Socks5Message(in);
|
|
}
|
|
|
|
}
|