summaryrefslogtreecommitdiff
path: root/java/net/SocksSocketImpl.java
diff options
context:
space:
mode:
Diffstat (limited to 'java/net/SocksSocketImpl.java')
-rw-r--r--java/net/SocksSocketImpl.java541
1 files changed, 536 insertions, 5 deletions
diff --git a/java/net/SocksSocketImpl.java b/java/net/SocksSocketImpl.java
index a81e219b..0d9d8f59 100644
--- a/java/net/SocksSocketImpl.java
+++ b/java/net/SocksSocketImpl.java
@@ -347,12 +347,91 @@ class SocksSocketImpl extends PlainSocketImpl implements SocksConsts {
epoint.getPort());
}
if (server == null) {
- // Android-removed: Logic to establish proxy connection based on default ProxySelector
+ // Android-removed: Logic to establish proxy connection based on default ProxySelector.
+ // Removed code that tried to establish proxy connection if ProxySelector#getDefault()
+ // is not null. This was never the case in previous Android releases, was causing
+ // issues and therefore was removed.
/*
- * Removed code that tried to establish proxy connection if
- * ProxySelector#getDefault() is not null.
- * This was never the case in previous android releases, was causing
- * issues and therefore was removed.
+ // 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 = java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction<ProxySelector>() {
+ public ProxySelector run() {
+ return ProxySelector.getDefault();
+ }
+ });
+ if (sel == null) {
+ /*
+ * No default proxySelector --> direct connection
+ *
+ super.connect(epoint, remainingMillis(deadlineMillis));
+ return;
+ }
+ URI uri;
+ // Use getHostString() to avoid reverse lookups
+ String host = epoint.getHostString();
+ // 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;
+ uri = null;
+ }
+ 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, remainingMillis(deadlineMillis));
+ return;
+ }
+ while (iProxy.hasNext()) {
+ p = iProxy.next();
+ if (p == null || p.type() != Proxy.Type.SOCKS) {
+ super.connect(epoint, remainingMillis(deadlineMillis));
+ return;
+ }
+
+ if (!(p.address() instanceof InetSocketAddress))
+ throw new SocketException("Unknown address type for proxy: " + p);
+ // Use getHostString() to avoid reverse lookups
+ server = ((InetSocketAddress) p.address()).getHostString();
+ serverPort = ((InetSocketAddress) p.address()).getPort();
+ if (p instanceof SocksProxy) {
+ if (((SocksProxy)p).protocolVersion() == 4) {
+ useV4 = true;
+ }
+ }
+
+ // Connects to the SOCKS server
+ try {
+ privilegedConnect(server, serverPort, remainingMillis(deadlineMillis));
+ // Worked, let's get outta here
+ break;
+ } catch (IOException e) {
+ // Ooops, let's notify the ProxySelector
+ sel.connectFailed(uri,p.address(),e);
+ server = null;
+ serverPort = -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());
+ }
*/
super.connect(epoint, remainingMillis(deadlineMillis));
return;
@@ -508,6 +587,458 @@ class SocksSocketImpl extends PlainSocketImpl implements SocksConsts {
external_address = epoint;
}
+ // Android-removed: Dead code. bindV4, socksBind, acceptFrom methods.
+ /*
+ 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 = AccessController.doPrivileged(
+ new PrivilegedAction<InetAddress>() {
+ public InetAddress run() {
+ return 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 = getUserName();
+ 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 saddr 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 = java.security.AccessController.doPrivileged(
+ new java.security.PrivilegedAction<ProxySelector>() {
+ public ProxySelector run() {
+ return ProxySelector.getDefault();
+ }
+ });
+ if (sel == null) {
+ /*
+ * No default proxySelector --> direct connection
+ *
+ return;
+ }
+ URI uri;
+ // Use getHostString() to avoid reverse lookups
+ String host = saddr.getHostString();
+ // 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;
+ uri = null;
+ }
+ 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.type() != Proxy.Type.SOCKS) {
+ return;
+ }
+
+ if (!(p.address() instanceof InetSocketAddress))
+ throw new SocketException("Unknown address type for proxy: " + p);
+ // Use getHostString() to avoid reverse lookups
+ server = ((InetSocketAddress) p.address()).getHostString();
+ serverPort = ((InetSocketAddress) p.address()).getPort();
+ if (p instanceof SocksProxy) {
+ if (((SocksProxy)p).protocolVersion() == 4) {
+ useV4 = true;
+ }
+ }
+
+ // Connects to the SOCKS server
+ try {
+ AccessController.doPrivileged(
+ new PrivilegedExceptionAction<Void>() {
+ public Void run() throws Exception {
+ cmdsock = new Socket(new PlainSocketImpl());
+ cmdsock.connect(new InetSocketAddress(server, serverPort));
+ 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;
+ serverPort = -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<Void>() {
+ public Void run() throws Exception {
+ cmdsock = new Socket(new PlainSocketImpl());
+ cmdsock.connect(new InetSocketAddress(server, serverPort));
+ 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!
+ 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 psi = (PlainSocketImpl) s;
+ psi.setInputStream((SocketInputStream) in);
+ psi.setFileDescriptor(cmdsock.getImpl().getFileDescriptor());
+ psi.setAddress(cmdsock.getImpl().getInetAddress());
+ psi.setPort(cmdsock.getImpl().getPort());
+ psi.setLocalPort(cmdsock.getImpl().getLocalPort());
+ } else {
+ 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} field.
*