0

I'm trying to make Socks v4 work out of the box in java.net, and I seem to have succeeded! Roughtly the code that I'm using is this:

    class SocketImplFactorySocks4 implements SocketImplFactory {
        @Override
        public SocketImpl createSocketImpl() {
            System.out.println("Socket implementation triggered");
            try {
                return socketSocks4Factory();
            } catch (Exception e) {
                e.printStackTrace();
                throw new Error("Can't go further");
            }
        }

        private SocketImpl socketSocks4Factory() throws
                [...] {
            Class<?> aClass = Class.forName("java.net.SocksSocketImpl");
            Constructor<?> cons = aClass.getDeclaredConstructor();
            if (!cons.isAccessible())
                cons.setAccessible(true);
            Object socket = cons.newInstance();
            Method method = socket.getClass().getDeclaredMethod("setV4");
            if (!method.isAccessible())
                method.setAccessible(true);
            method.invoke(socket);
            Field field = socket.getClass().getDeclaredField("useV4");
            field.setAccessible(true);
            Object value = field.get(socket);
            return (SocketImpl) socket;
        }

    }

Long story short, it works when I create a socket and pass -DsocksProxyHost and -DsocksProxyPort.

My problem is when I use the same code in my junit test, I can check with Reflections that Socket.impl.useV4 is set to true, socksProxy* settings are set systemwide, but when I use my socket, it avoids using proxy altogether (I can see it in wireshark).

It's either JUnit or Gradle, but I've reached my limits. Please advice on where should I go next. build.gradle.kts for reference:

tasks{
    test{
        systemProperty("socksProxyHost", "localhost")
        systemProperty("socksProxyPort", "8080")
    }
}

Thanks in advance!

merinoff
  • 311
  • 3
  • 7

1 Answers1

0

Well, it took me way too much time to figure it out, but I did. My initial goal was to test my Socks v4 server code, but there were two problems on my way:

1) Even though Java Socket has support for Socks v4 as client, it is not enabled by default. And there is no way to flip the toggle.

2) Having solved #1, I tried to write E2E test to smoke the whole thing, but for some reason it was avoiding going into the Socks proxy, even though the toggle (useV4) was true. This is what I came with here on SO.

To solve the first problem, I implemented SocketImplFactory (see above in the question). What helped to tackle the topic question was my admin background, even though it didn't kick in until recently. :) I separated the original suspects (JUnit and Gradle) and made the test in a standalone psvm file. The test didn't work, it still avoided going through the proxy. And this is when it hit me: exception for localhost!

Basically, there is a hardcoded exception for localhost(127.0.0.1, ::, etc) deep in Java core library. After some searching I came across DsocksNonProxyHosts option. Which didn't help, as you might have guessed already :) Eventually I ended up at this answer, which mentioned that I might need to implement ProxySelector. Which I did:

    static class myProxySelector extends ProxySelector {

    @Override
    public List<Proxy> select(URI uri) {
        List<Proxy> proxyl = new ArrayList<Proxy>(1);
        InetSocketAddress saddr = InetSocketAddress.createUnresolved("localhost", 1080);
        Proxy proxy = SocksProxy.create(saddr, 4);
        proxyl.add(proxy);
        System.out.println("Selecting Proxy for " + uri.toASCIIString());
        return proxyl;
    }

    @Override
    public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {
        if (uri == null || sa == null || ioe == null) {
            throw new IllegalArgumentException("Arguments can't be null.");
        }
    }
}

The whole socket setup looks like this:

        private void setupSocket() throws IOException {
        Socket.setSocketImplFactory(new SocketImplFactorySocks4());
        ProxySelector proxySelector = new myProxySelector();
        ProxySelector.setDefault(proxySelector);
        proxy = new Proxy(Proxy.Type.SOCKS, new InetSocketAddress("127.0.0.1", 1080));
    }

Now everything I wanted works: I'm both able to E2E-test my socks4 code and can do it localhost.

merinoff
  • 311
  • 3
  • 7