diff options
Diffstat (limited to 'java/net/SocksSocketImpl.java')
-rw-r--r-- | java/net/SocksSocketImpl.java | 541 |
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. * |