- /*
- * @(#)SocksSocketImpl.java 1.17 04/04/29
- *
- * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
- * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
- */
- package java.net;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.OutputStream;
- import java.io.BufferedOutputStream;
- import java.security.AccessController;
- import java.security.PrivilegedExceptionAction;
- import java.util.prefs.Preferences;
- import sun.net.www.ParseUtil;
- /* import org.ietf.jgss.*; */
-
- /**
- * SOCKS (V4 & V5) TCP socket implementation (RFC 1928).
- * This is a subclass of PlainSocketImpl.
- * Note this class should <b>NOT</b> be public.
- */
-
- class SocksSocketImpl extends PlainSocketImpl implements SocksConsts {
- private String server = null;
- private int port = DEFAULT_PORT;
- private InetSocketAddress external_address;
- private boolean useV4 = false;
- private Socket cmdsock = null;
- private InputStream cmdIn = null;
- private OutputStream cmdOut = null;
-
- SocksSocketImpl() {
- // Nothing needed
- }
-
- SocksSocketImpl(String server, int port) {
- this.server = server;
- this.port = (port == -1 ? DEFAULT_PORT : port);
- }
-
- SocksSocketImpl(Proxy proxy) {
- SocketAddress a = proxy.address();
- if (a instanceof InetSocketAddress) {
- InetSocketAddress ad = (InetSocketAddress) a;
- server = ad.getHostName();
- port = ad.getPort();
- }
- }
-
- void setV4() {
- useV4 = true;
- }
-
- private synchronized void privilegedConnect(final String host,
- final int port,
- final int timeout)
- throws IOException
- {
- try {
- AccessController.doPrivileged(
- new java.security.PrivilegedExceptionAction() {
- public Object run() throws IOException {
- superConnectServer(host, port, timeout);
- cmdIn = getInputStream();
- cmdOut = getOutputStream();
- return null;
- }
- });
- } catch (java.security.PrivilegedActionException pae) {
- throw (IOException) pae.getException();
- }
- }
-
- private void superConnectServer(String host, int port,
- int timeout) throws IOException {
- super.connect(new InetSocketAddress(host, port), timeout);
- }
-
- private int readSocksReply(InputStream in, byte[] data) throws IOException {
- int len = data.length;
- int received = 0;
- for (int attempts = 0; received < len && attempts < 3; attempts++) {
- int count = in.read(data, received, len - received);
- if (count < 0)
- throw new SocketException("Malformed reply from SOCKS server");
- received += count;
- }
- return received;
- }
-
- /**
- * Provides the authentication machanism required by the proxy.
- */
- private boolean authenticate(byte method, InputStream in,
- BufferedOutputStream out) throws IOException {
- byte[] data = null;
- int i;
- // No Authentication required. We're done then!
- if (method == NO_AUTH)
- return true;
- /**
- * User/Password authentication. Try, in that order :
- * - The application provided Authenticator, if any
- * - The user preferences java.net.socks.username &
- * java.net.socks.password
- * - the user.name & no password (backward compatibility behavior).
- */
- if (method == USER_PASSW) {
- String userName;
- String password = null;
- final InetAddress addr = InetAddress.getByName(server);
- PasswordAuthentication pw = (PasswordAuthentication)
- java.security.AccessController.doPrivileged(
- new java.security.PrivilegedAction() {
- public Object run() {
- return Authenticator.requestPasswordAuthentication(
- server, addr, port, "SOCKS5", "SOCKS authentication", null);
- }
- });
- if (pw != null) {
- userName = pw.getUserName();
- password = new String(pw.getPassword());
- } else {
- final Preferences prefs = Preferences.userRoot().node("/java/net/socks");
- try {
- userName =
- (String) AccessController.doPrivileged(
- new java.security.PrivilegedExceptionAction() {
- public Object run() throws IOException {
- return prefs.get("username", null);
- }
- });
- } catch (java.security.PrivilegedActionException pae) {
- throw (IOException) pae.getException();
- }
-
- if (userName != null) {
- try {
- password =
- (String) AccessController.doPrivileged(
- new java.security.PrivilegedExceptionAction() {
- public Object run() throws IOException {
- return prefs.get("password", null);
- }
- });
- } catch (java.security.PrivilegedActionException pae) {
- throw (IOException) pae.getException();
- }
- } else {
- userName =
- (String) java.security.AccessController.doPrivileged(
- new sun.security.action.GetPropertyAction("user.name"));
- }
- }
- if (userName == null)
- return false;
- out.write(1);
- out.write(userName.length());
- try {
- out.write(userName.getBytes("ISO-8859-1"));
- } catch (java.io.UnsupportedEncodingException uee) {
- assert false;
- }
- if (password != null) {
- out.write(password.length());
- try {
- out.write(password.getBytes("ISO-8859-1"));
- } catch (java.io.UnsupportedEncodingException uee) {
- assert false;
- }
- } else
- out.write(0);
- out.flush();
- data = new byte[2];
- i = readSocksReply(in, data);
- if (i != 2 || data[1] != 0) {
- /* RFC 1929 specifies that the connection MUST be closed if
- authentication fails */
- out.close();
- in.close();
- return false;
- }
- /* Authentication succeeded */
- return true;
- }
- /**
- * GSSAPI authentication mechanism.
- * Unfortunately the RFC seems out of sync with the Reference
- * implementation. I'll leave this in for future completion.
- */
- // if (method == GSSAPI) {
- // try {
- // GSSManager manager = GSSManager.getInstance();
- // GSSName name = manager.createName("SERVICE:socks@"+server,
- // null);
- // GSSContext context = manager.createContext(name, null, null,
- // GSSContext.DEFAULT_LIFETIME);
- // context.requestMutualAuth(true);
- // context.requestReplayDet(true);
- // context.requestSequenceDet(true);
- // context.requestCredDeleg(true);
- // byte []inToken = new byte[0];
- // while (!context.isEstablished()) {
- // byte[] outToken
- // = context.initSecContext(inToken, 0, inToken.length);
- // // send the output token if generated
- // if (outToken != null) {
- // out.write(1);
- // out.write(1);
- // out.writeShort(outToken.length);
- // out.write(outToken);
- // out.flush();
- // data = new byte[2];
- // i = readSocksReply(in, data);
- // if (i != 2 || data[1] == 0xff) {
- // in.close();
- // out.close();
- // return false;
- // }
- // i = readSocksReply(in, data);
- // int len = 0;
- // len = ((int)data[0] & 0xff) << 8;
- // len += data[1];
- // data = new byte[len];
- // i = readSocksReply(in, data);
- // if (i == len)
- // return true;
- // in.close();
- // out.close();
- // }
- // }
- // } catch (GSSException e) {
- // /* RFC 1961 states that if Context initialisation fails the connection
- // MUST be closed */
- // e.printStackTrace();
- // in.close();
- // out.close();
- // }
- // }
- return false;
- }
-
- private void connectV4(InputStream in, OutputStream out,
- InetSocketAddress endpoint) throws IOException {
- if (!(endpoint.getAddress() instanceof Inet4Address)) {
- throw new SocketException("SOCKS V4 requires IPv4 only addresses");
- }
- out.write(PROTO_VERS4);
- out.write(CONNECT);
- out.write((endpoint.getPort() >> 8) & 0xff);
- out.write((endpoint.getPort() >> 0) & 0xff);
- out.write(endpoint.getAddress().getAddress());
- String userName = (String) java.security.AccessController.doPrivileged(
- new sun.security.action.GetPropertyAction("user.name"));
- try {
- out.write(userName.getBytes("ISO-8859-1"));
- } catch (java.io.UnsupportedEncodingException uee) {
- assert false;
- }
- out.write(0);
- out.flush();
- byte[] data = new byte[8];
- int n = readSocksReply(in, data);
- if (n != 8)
- throw new SocketException("Reply from SOCKS server has bad length: " + n);
- if (data[0] != 0 && data[0] != 4)
- throw new SocketException("Reply from SOCKS server has bad version");
- SocketException ex = null;
- switch (data[1]) {
- case 90:
- // Success!
- external_address = endpoint;
- break;
- case 91:
- ex = new SocketException("SOCKS request rejected");
- break;
- case 92:
- ex = new SocketException("SOCKS server couldn't reach destination");
- break;
- case 93:
- ex = new SocketException("SOCKS authentication failed");
- break;
- default:
- ex = new SocketException("Reply from SOCKS server contains bad status");
- break;
- }
- if (ex != null) {
- in.close();
- out.close();
- throw ex;
- }
- }
-
- /**
- * Connects the Socks Socket to the specified endpoint. It will first
- * connect to the SOCKS proxy and negotiate the access. If the proxy
- * grants the connections, then the connect is successful and all
- * further traffic will go to the "real" endpoint.
- *
- * @param endpoint the <code>SocketAddress</code> to connect to.
- * @param timeout the timeout value in milliseconds
- * @throws IOException if the connection can't be established.
- * @throws SecurityException if there is a security manager and it
- * doesn't allow the connection
- * @throws IllegalArgumentException if endpoint is null or a
- * SocketAddress subclass not supported by this socket
- */
- protected void connect(SocketAddress endpoint, int timeout) throws IOException {
- SecurityManager security = System.getSecurityManager();
- if (endpoint == null || !(endpoint instanceof InetSocketAddress))
- throw new IllegalArgumentException("Unsupported address type");
- InetSocketAddress epoint = (InetSocketAddress) endpoint;
- if (security != null) {
- if (epoint.isUnresolved())
- security.checkConnect(epoint.getHostName(),
- epoint.getPort());
- else
- security.checkConnect(epoint.getAddress().getHostAddress(),
- epoint.getPort());
- }
- if (server == null) {
- // This is the general case
- // server is not null only when the socket was created with a
- // specified proxy in which case it does bypass the ProxySelector
- ProxySelector sel = (ProxySelector)
- java.security.AccessController.doPrivileged(
- new java.security.PrivilegedAction() {
- public Object run() {
- return ProxySelector.getDefault();
- }
- });
- if (sel == null) {
- /*
- * No default proxySelector --> direct connection
- */
- super.connect(epoint, timeout);
- return;
- }
- URI uri = null;
- String host = epoint.getHostName();
- // IPv6 litteral?
- if (epoint.getAddress() instanceof Inet6Address &&
- (!host.startsWith("[")) && (host.indexOf(":") >= 0)) {
- host = "[" + host + "]";
- }
- try {
- uri = new URI("socket://" + ParseUtil.encodePath(host) + ":"+ epoint.getPort());
- } catch (URISyntaxException e) {
- // This shouldn't happen
- assert false : e;
- }
- Proxy p = null;
- IOException savedExc = null;
- java.util.Iterator<Proxy> iProxy = null;
- iProxy = sel.select(uri).iterator();
- if (iProxy == null || !(iProxy.hasNext())) {
- super.connect(epoint, timeout);
- return;
- }
- while (iProxy.hasNext()) {
- p = iProxy.next();
- if (p == null || p == Proxy.NO_PROXY) {
- super.connect(epoint, timeout);
- return;
- }
- if (p.type() != Proxy.Type.SOCKS)
- throw new SocketException("Unknown proxy type : " + p.type());
- if (!(p.address() instanceof InetSocketAddress))
- throw new SocketException("Unknow address type for proxy: " + p);
- server = ((InetSocketAddress) p.address()).getHostName();
- port = ((InetSocketAddress) p.address()).getPort();
-
- // Connects to the SOCKS server
- try {
- privilegedConnect(server, port, timeout);
- // Worked, let's get outta here
- break;
- } catch (IOException e) {
- // Ooops, let's notify the ProxySelector
- sel.connectFailed(uri,p.address(),e);
- server = null;
- port = -1;
- savedExc = e;
- // Will continue the while loop and try the next proxy
- }
- }
-
- /*
- * If server is still null at this point, none of the proxy
- * worked
- */
- if (server == null) {
- throw new SocketException("Can't connect to SOCKS proxy:"
- + savedExc.getMessage());
- }
- } else {
- // Connects to the SOCKS server
- try {
- privilegedConnect(server, port, timeout);
- } catch (IOException e) {
- throw new SocketException(e.getMessage());
- }
- }
-
- // cmdIn & cmdOut were intialized during the privilegedConnect() call
- BufferedOutputStream out = new BufferedOutputStream(cmdOut, 512);
- InputStream in = cmdIn;
-
- if (useV4) {
- // SOCKS Protocol version 4 doesn't know how to deal with
- // DOMAIN type of addresses (unresolved addresses here)
- if (epoint.isUnresolved())
- throw new UnknownHostException(epoint.toString());
- connectV4(in, out, epoint);
- return;
- }
-
- // This is SOCKS V5
- out.write(PROTO_VERS);
- out.write(2);
- out.write(NO_AUTH);
- out.write(USER_PASSW);
- out.flush();
- byte[] data = new byte[2];
- int i = readSocksReply(in, data);
- if (i != 2 || ((int)data[0]) != PROTO_VERS) {
- // Maybe it's not a V5 sever after all
- // Let's try V4 before we give up
- // SOCKS Protocol version 4 doesn't know how to deal with
- // DOMAIN type of addresses (unresolved addresses here)
- if (epoint.isUnresolved())
- throw new UnknownHostException(epoint.toString());
- connectV4(in, out, epoint);
- return;
- }
- if (((int)data[1]) == NO_METHODS)
- throw new SocketException("SOCKS : No acceptable methods");
- if (!authenticate(data[1], in, out)) {
- throw new SocketException("SOCKS : authentication failed");
- }
- out.write(PROTO_VERS);
- out.write(CONNECT);
- out.write(0);
- /* Test for IPV4/IPV6/Unresolved */
- if (epoint.isUnresolved()) {
- out.write(DOMAIN_NAME);
- out.write(epoint.getHostName().length());
- try {
- out.write(epoint.getHostName().getBytes("ISO-8859-1"));
- } catch (java.io.UnsupportedEncodingException uee) {
- assert false;
- }
- out.write((epoint.getPort() >> 8) & 0xff);
- out.write((epoint.getPort() >> 0) & 0xff);
- } else if (epoint.getAddress() instanceof Inet6Address) {
- out.write(IPV6);
- out.write(epoint.getAddress().getAddress());
- out.write((epoint.getPort() >> 8) & 0xff);
- out.write((epoint.getPort() >> 0) & 0xff);
- } else {
- out.write(IPV4);
- out.write(epoint.getAddress().getAddress());
- out.write((epoint.getPort() >> 8) & 0xff);
- out.write((epoint.getPort() >> 0) & 0xff);
- }
- out.flush();
- data = new byte[4];
- i = readSocksReply(in, data);
- if (i != 4)
- throw new SocketException("Reply from SOCKS server has bad length");
- SocketException ex = null;
- int nport, len;
- byte[] addr;
- switch (data[1]) {
- case REQUEST_OK:
- // success!
- switch(data[3]) {
- case IPV4:
- addr = new byte[4];
- i = readSocksReply(in, addr);
- if (i != 4)
- throw new SocketException("Reply from SOCKS server badly formatted");
- data = new byte[2];
- i = readSocksReply(in, data);
- if (i != 2)
- throw new SocketException("Reply from SOCKS server badly formatted");
- nport = ((int)data[0] & 0xff) << 8;
- nport += ((int)data[1] & 0xff);
- break;
- case DOMAIN_NAME:
- len = data[1];
- byte[] host = new byte[len];
- i = readSocksReply(in, host);
- if (i != len)
- throw new SocketException("Reply from SOCKS server badly formatted");
- data = new byte[2];
- i = readSocksReply(in, data);
- if (i != 2)
- throw new SocketException("Reply from SOCKS server badly formatted");
- nport = ((int)data[0] & 0xff) << 8;
- nport += ((int)data[1] & 0xff);
- break;
- case IPV6:
- len = data[1];
- addr = new byte[len];
- i = readSocksReply(in, addr);
- if (i != len)
- throw new SocketException("Reply from SOCKS server badly formatted");
- data = new byte[2];
- i = readSocksReply(in, data);
- if (i != 2)
- throw new SocketException("Reply from SOCKS server badly formatted");
- nport = ((int)data[0] & 0xff) << 8;
- nport += ((int)data[1] & 0xff);
- break;
- default:
- ex = new SocketException("Reply from SOCKS server contains wrong code");
- break;
- }
- break;
- case GENERAL_FAILURE:
- ex = new SocketException("SOCKS server general failure");
- break;
- case NOT_ALLOWED:
- ex = new SocketException("SOCKS: Connection not allowed by ruleset");
- break;
- case NET_UNREACHABLE:
- ex = new SocketException("SOCKS: Network unreachable");
- break;
- case HOST_UNREACHABLE:
- ex = new SocketException("SOCKS: Host unreachable");
- break;
- case CONN_REFUSED:
- ex = new SocketException("SOCKS: Connection refused");
- break;
- case TTL_EXPIRED:
- ex = new SocketException("SOCKS: TTL expired");
- break;
- case CMD_NOT_SUPPORTED:
- ex = new SocketException("SOCKS: Command not supported");
- break;
- case ADDR_TYPE_NOT_SUP:
- ex = new SocketException("SOCKS: address type not supported");
- break;
- }
- if (ex != null) {
- in.close();
- out.close();
- throw ex;
- }
- external_address = epoint;
- }
-
- private void bindV4(InputStream in, OutputStream out,
- InetAddress baddr,
- int lport) throws IOException {
- if (!(baddr instanceof Inet4Address)) {
- throw new SocketException("SOCKS V4 requires IPv4 only addresses");
- }
- super.bind(baddr, lport);
- byte[] addr1 = baddr.getAddress();
- /* Test for AnyLocal */
- InetAddress naddr = baddr;
- if (naddr.isAnyLocalAddress()) {
- naddr = cmdsock.getLocalAddress();
- addr1 = naddr.getAddress();
- }
- out.write(PROTO_VERS4);
- out.write(BIND);
- out.write((super.getLocalPort() >> 8) & 0xff);
- out.write((super.getLocalPort() >> 0) & 0xff);
- out.write(addr1);
- String userName = (String) java.security.AccessController.doPrivileged(
- new sun.security.action.GetPropertyAction("user.name"));
- try {
- out.write(userName.getBytes("ISO-8859-1"));
- } catch (java.io.UnsupportedEncodingException uee) {
- assert false;
- }
- out.write(0);
- out.flush();
- byte[] data = new byte[8];
- int n = readSocksReply(in, data);
- if (n != 8)
- throw new SocketException("Reply from SOCKS server has bad length: " + n);
- if (data[0] != 0 && data[0] != 4)
- throw new SocketException("Reply from SOCKS server has bad version");
- SocketException ex = null;
- switch (data[1]) {
- case 90:
- // Success!
- external_address = new InetSocketAddress(baddr, lport);
- break;
- case 91:
- ex = new SocketException("SOCKS request rejected");
- break;
- case 92:
- ex = new SocketException("SOCKS server couldn't reach destination");
- break;
- case 93:
- ex = new SocketException("SOCKS authentication failed");
- break;
- default:
- ex = new SocketException("Reply from SOCKS server contains bad status");
- break;
- }
- if (ex != null) {
- in.close();
- out.close();
- throw ex;
- }
-
- }
-
- /**
- * Sends the Bind request to the SOCKS proxy. In the SOCKS protocol, bind
- * means "accept incoming connection from", so the SocketAddress is the
- * the one of the host we do accept connection from.
- *
- * @param addr the Socket address of the remote host.
- * @exception IOException if an I/O error occurs when binding this socket.
- */
- protected synchronized void socksBind(InetSocketAddress saddr) throws IOException {
- if (socket != null) {
- // this is a client socket, not a server socket, don't
- // call the SOCKS proxy for a bind!
- return;
- }
-
- // Connects to the SOCKS server
-
- if (server == null) {
- // This is the general case
- // server is not null only when the socket was created with a
- // specified proxy in which case it does bypass the ProxySelector
- ProxySelector sel = (ProxySelector)
- java.security.AccessController.doPrivileged(
- new java.security.PrivilegedAction() {
- public Object run() {
- return ProxySelector.getDefault();
- }
- });
- if (sel == null) {
- /*
- * No default proxySelector --> direct connection
- */
- return;
- }
- URI uri = null;
- String host = saddr.getHostName();
- // IPv6 litteral?
- if (saddr.getAddress() instanceof Inet6Address &&
- (!host.startsWith("[")) && (host.indexOf(":") >= 0)) {
- host = "[" + host + "]";
- }
- try {
- uri = new URI("serversocket://" + ParseUtil.encodePath(host) + ":"+ saddr.getPort());
- } catch (URISyntaxException e) {
- // This shouldn't happen
- assert false : e;
- }
- Proxy p = null;
- Exception savedExc = null;
- java.util.Iterator<Proxy> iProxy = null;
- iProxy = sel.select(uri).iterator();
- if (iProxy == null || !(iProxy.hasNext())) {
- return;
- }
- while (iProxy.hasNext()) {
- p = iProxy.next();
- if (p == null || p == Proxy.NO_PROXY) {
- return;
- }
- if (p.type() != Proxy.Type.SOCKS)
- throw new SocketException("Unknown proxy type : " + p.type());
- if (!(p.address() instanceof InetSocketAddress))
- throw new SocketException("Unknow address type for proxy: " + p);
- server = ((InetSocketAddress) p.address()).getHostName();
- port = ((InetSocketAddress) p.address()).getPort();
-
- // Connects to the SOCKS server
- try {
- AccessController.doPrivileged(new PrivilegedExceptionAction() {
- public Object run() throws Exception {
- cmdsock = new Socket(new PlainSocketImpl());
- cmdsock.connect(new InetSocketAddress(server, port));
- cmdIn = cmdsock.getInputStream();
- cmdOut = cmdsock.getOutputStream();
- return null;
- }
- });
- } catch (Exception e) {
- // Ooops, let's notify the ProxySelector
- sel.connectFailed(uri,p.address(),new SocketException(e.getMessage()));
- server = null;
- port = -1;
- cmdsock = null;
- savedExc = e;
- // Will continue the while loop and try the next proxy
- }
- }
-
- /*
- * If server is still null at this point, none of the proxy
- * worked
- */
- if (server == null || cmdsock == null) {
- throw new SocketException("Can't connect to SOCKS proxy:"
- + savedExc.getMessage());
- }
- } else {
- try {
- AccessController.doPrivileged(new PrivilegedExceptionAction() {
- public Object run() throws Exception {
- cmdsock = new Socket(new PlainSocketImpl());
- cmdsock.connect(new InetSocketAddress(server, port));
- cmdIn = cmdsock.getInputStream();
- cmdOut = cmdsock.getOutputStream();
- return null;
- }
- });
- } catch (Exception e) {
- throw new SocketException(e.getMessage());
- }
- }
- BufferedOutputStream out = new BufferedOutputStream(cmdOut, 512);
- InputStream in = cmdIn;
- if (useV4) {
- bindV4(in, out, saddr.getAddress(), saddr.getPort());
- return;
- }
- out.write(PROTO_VERS);
- out.write(2);
- out.write(NO_AUTH);
- out.write(USER_PASSW);
- out.flush();
- byte[] data = new byte[2];
- int i = readSocksReply(in, data);
- if (i != 2 || ((int)data[0]) != PROTO_VERS) {
- // Maybe it's not a V5 sever after all
- // Let's try V4 before we give up
- bindV4(in, out, saddr.getAddress(), saddr.getPort());
- return;
- }
- if (((int)data[1]) == NO_METHODS)
- throw new SocketException("SOCKS : No acceptable methods");
- if (!authenticate(data[1], in, out)) {
- throw new SocketException("SOCKS : authentication failed");
- }
- // We're OK. Let's issue the BIND command.
- out.write(PROTO_VERS);
- out.write(BIND);
- out.write(0);
- int lport = saddr.getPort();
- if (saddr.isUnresolved()) {
- out.write(DOMAIN_NAME);
- out.write(saddr.getHostName().length());
- try {
- out.write(saddr.getHostName().getBytes("ISO-8859-1"));
- } catch (java.io.UnsupportedEncodingException uee) {
- assert false;
- }
- out.write((lport >> 8) & 0xff);
- out.write((lport >> 0) & 0xff);
- } else if (saddr.getAddress() instanceof Inet4Address) {
- byte[] addr1 = saddr.getAddress().getAddress();
- out.write(IPV4);
- out.write(addr1);
- out.write((lport >> 8) & 0xff);
- out.write((lport >> 0) & 0xff);
- out.flush();
- } else if (saddr.getAddress() instanceof Inet6Address) {
- byte[] addr1 = saddr.getAddress().getAddress();
- out.write(IPV6);
- out.write(addr1);
- out.write((lport >> 8) & 0xff);
- out.write((lport >> 0) & 0xff);
- out.flush();
- } else {
- cmdsock.close();
- throw new SocketException("unsupported address type : " + saddr);
- }
- data = new byte[4];
- i = readSocksReply(in, data);
- SocketException ex = null;
- int len, nport;
- byte[] addr;
- switch (data[1]) {
- case REQUEST_OK:
- // success!
- InetSocketAddress real_end = null;
- switch(data[3]) {
- case IPV4:
- addr = new byte[4];
- i = readSocksReply(in, addr);
- if (i != 4)
- throw new SocketException("Reply from SOCKS server badly formatted");
- data = new byte[2];
- i = readSocksReply(in, data);
- if (i != 2)
- throw new SocketException("Reply from SOCKS server badly formatted");
- nport = ((int)data[0] & 0xff) << 8;
- nport += ((int)data[1] & 0xff);
- external_address =
- new InetSocketAddress(new Inet4Address("", addr) , nport);
- break;
- case DOMAIN_NAME:
- len = data[1];
- byte[] host = new byte[len];
- i = readSocksReply(in, host);
- if (i != len)
- throw new SocketException("Reply from SOCKS server badly formatted");
- data = new byte[2];
- i = readSocksReply(in, data);
- if (i != 2)
- throw new SocketException("Reply from SOCKS server badly formatted");
- nport = ((int)data[0] & 0xff) << 8;
- nport += ((int)data[1] & 0xff);
- external_address = new InetSocketAddress(new String(host), nport);
- break;
- case IPV6:
- len = data[1];
- addr = new byte[len];
- i = readSocksReply(in, addr);
- if (i != len)
- throw new SocketException("Reply from SOCKS server badly formatted");
- data = new byte[2];
- i = readSocksReply(in, data);
- if (i != 2)
- throw new SocketException("Reply from SOCKS server badly formatted");
- nport = ((int)data[0] & 0xff) << 8;
- nport += ((int)data[1] & 0xff);
- external_address =
- new InetSocketAddress(new Inet6Address("", addr), nport);
- break;
- }
- break;
- case GENERAL_FAILURE:
- ex = new SocketException("SOCKS server general failure");
- break;
- case NOT_ALLOWED:
- ex = new SocketException("SOCKS: Bind not allowed by ruleset");
- break;
- case NET_UNREACHABLE:
- ex = new SocketException("SOCKS: Network unreachable");
- break;
- case HOST_UNREACHABLE:
- ex = new SocketException("SOCKS: Host unreachable");
- break;
- case CONN_REFUSED:
- ex = new SocketException("SOCKS: Connection refused");
- break;
- case TTL_EXPIRED:
- ex = new SocketException("SOCKS: TTL expired");
- break;
- case CMD_NOT_SUPPORTED:
- ex = new SocketException("SOCKS: Command not supported");
- break;
- case ADDR_TYPE_NOT_SUP:
- ex = new SocketException("SOCKS: address type not supported");
- break;
- }
- if (ex != null) {
- in.close();
- out.close();
- cmdsock.close();
- cmdsock = null;
- throw ex;
- }
- cmdIn = in;
- cmdOut = out;
- }
-
- /**
- * Accepts a connection from a specific host.
- *
- * @param s the accepted connection.
- * @param saddr the socket address of the host we do accept
- * connection from
- * @exception IOException if an I/O error occurs when accepting the
- * connection.
- */
- protected void acceptFrom(SocketImpl s, InetSocketAddress saddr) throws IOException {
- if (cmdsock == null) {
- // Not a Socks ServerSocket.
- return;
- }
- InputStream in = cmdIn;
- // Sends the "SOCKS BIND" request.
- socksBind(saddr);
- in.read();
- int i = in.read();
- in.read();
- SocketException ex = null;
- int nport;
- byte[] addr;
- InetSocketAddress real_end = null;
- switch (i) {
- case REQUEST_OK:
- // success!
- i = in.read();
- switch(i) {
- case IPV4:
- addr = new byte[4];
- readSocksReply(in, addr);
- nport = in.read() << 8;
- nport += in.read();
- real_end =
- new InetSocketAddress(new Inet4Address("", addr) , nport);
- break;
- case DOMAIN_NAME:
- int len = in.read();
- addr = new byte[len];
- readSocksReply(in, addr);
- nport = in.read() << 8;
- nport += in.read();
- real_end = new InetSocketAddress(new String(addr), nport);
- break;
- case IPV6:
- addr = new byte[16];
- readSocksReply(in, addr);
- nport = in.read() << 8;
- nport += in.read();
- real_end =
- new InetSocketAddress(new Inet6Address("", addr), nport);
- break;
- }
- break;
- case GENERAL_FAILURE:
- ex = new SocketException("SOCKS server general failure");
- break;
- case NOT_ALLOWED:
- ex = new SocketException("SOCKS: Accept not allowed by ruleset");
- break;
- case NET_UNREACHABLE:
- ex = new SocketException("SOCKS: Network unreachable");
- break;
- case HOST_UNREACHABLE:
- ex = new SocketException("SOCKS: Host unreachable");
- break;
- case CONN_REFUSED:
- ex = new SocketException("SOCKS: Connection refused");
- break;
- case TTL_EXPIRED:
- ex = new SocketException("SOCKS: TTL expired");
- break;
- case CMD_NOT_SUPPORTED:
- ex = new SocketException("SOCKS: Command not supported");
- break;
- case ADDR_TYPE_NOT_SUP:
- ex = new SocketException("SOCKS: address type not supported");
- break;
- }
- if (ex != null) {
- cmdIn.close();
- cmdOut.close();
- cmdsock.close();
- cmdsock = null;
- throw ex;
- }
-
- /**
- * This is where we have to do some fancy stuff.
- * The datastream from the socket "accepted" by the proxy will
- * come through the cmdSocket. So we have to swap the socketImpls
- */
- if (s instanceof SocksSocketImpl) {
- ((SocksSocketImpl)s).external_address = real_end;
- }
- if (s instanceof PlainSocketImpl) {
- ((PlainSocketImpl)s).setInputStream((SocketInputStream) in);
- }
- s.fd = cmdsock.getImpl().fd;
- s.address = cmdsock.getImpl().address;
- s.port = cmdsock.getImpl().port;
- s.localport = cmdsock.getImpl().localport;
- // Need to do that so that the socket won't be closed
- // when the ServerSocket is closed by the user.
- // It kinds of detaches the Socket because it is now
- // used elsewhere.
- cmdsock = null;
- }
-
-
- /**
- * Returns the value of this socket's <code>address</code> field.
- *
- * @return the value of this socket's <code>address</code> field.
- * @see java.net.SocketImpl#address
- */
- protected InetAddress getInetAddress() {
- if (external_address != null)
- return external_address.getAddress();
- else
- return super.getInetAddress();
- }
-
- /**
- * Returns the value of this socket's <code>port</code> field.
- *
- * @return the value of this socket's <code>port</code> field.
- * @see java.net.SocketImpl#port
- */
- protected int getPort() {
- if (external_address != null)
- return external_address.getPort();
- else
- return super.getPort();
- }
-
- protected int getLocalPort() {
- if (socket != null)
- return super.getLocalPort();
- if (external_address != null)
- return external_address.getPort();
- else
- return super.getLocalPort();
- }
-
- protected void close() throws IOException {
- if (cmdsock != null)
- cmdsock.close();
- cmdsock = null;
- super.close();
- }
-
- }