square / okhttp

Square’s meticulous HTTP client for the JVM, Android, and GraalVM.
https://square.github.io/okhttp/
Apache License 2.0
45.64k stars 9.14k forks source link

SOCKS proxy not working on Android platform #2315

Closed mariotaku closed 8 years ago

mariotaku commented 8 years ago

SOCKS proxy is completely broken on Android. When I set up SOCKS proxy in my app, all requests failed, when I wrote an instrumentation test, I found even create a default okhttp instance with a SOCKS proxy, it fails.

Here's the stack trace:

java.net.UnknownHostException: Host is unresolved: www.google.com
at java.net.Socket.connect(Socket.java:867)
at okhttp3.internal.Platform$Android.connectSocket(Platform.java:200)
at okhttp3.internal.io.RealConnection.connectSocket(RealConnection.java:137)
at okhttp3.internal.io.RealConnection.connect(RealConnection.java:108)
at okhttp3.internal.http.StreamAllocation.findConnection(StreamAllocation.java:188)
at okhttp3.internal.http.StreamAllocation.findHealthyConnection(StreamAllocation.java:127)
at okhttp3.internal.http.StreamAllocation.newStream(StreamAllocation.java:97)
at okhttp3.internal.http.HttpEngine.connect(HttpEngine.java:289)
at okhttp3.internal.http.HttpEngine.sendRequest(HttpEngine.java:241)
at okhttp3.RealCall.getResponse(RealCall.java:240)
at okhttp3.RealCall$ApplicationInterceptorChain.proceed(RealCall.java:198)
at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:160)
at okhttp3.RealCall.execute(RealCall.java:57)
at org.mariotaku.twidere.test.okhttp3.OkHttpClientTest.testSocksFunctionality(OkHttpClientTest.java:27)
at java.lang.reflect.Method.invoke(Native Method)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runners.Suite.runChild(Suite.java:128)
at org.junit.runners.Suite.runChild(Suite.java:27)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at org.junit.runner.JUnitCore.run(JUnitCore.java:115)
at android.support.test.internal.runner.TestExecutor.execute(TestExecutor.java:54)
at android.support.test.runner.AndroidJUnitRunner.onStart(AndroidJUnitRunner.java:240)
at android.app.Instrumentation$InstrumentationThread.run(Instrumentation.java:1879)

Sample code:

import org.junit.Test;

import java.net.InetSocketAddress;
import java.net.Proxy;

import okhttp3.OkHttpClient;
import okhttp3.Request;

import static org.junit.Assert.assertTrue;

/**
 * Created by mariotaku on 16/2/5.
 */
public class OkHttpClientTest {

    @Test
    public void testSocksFunctionality() throws Exception {
        final Proxy proxy = new Proxy(Proxy.Type.SOCKS, InetSocketAddress.createUnresolved("127.0.0.1", 1080));
        final OkHttpClient client = new OkHttpClient.Builder()
                .proxy(proxy)
                .build();
        final Request request = new Request.Builder()
                .url("https://www.google.com/")
                .build();
        assertTrue(client.newCall(request).execute().isSuccessful());
    }

}
mariotaku commented 8 years ago

Seems it's a bug inside Android Socket implementation.

SOCKS working fine on Oracle JRE and OpenJDK JRE, and I looked into their code: Socket.connect(SocketAddress endpoint, int timeout)

            if (epoint.isUnresolved())
                impl.connect(addr.getHostName(), port);
            else
                impl.connect(addr, port);

That's what I found in Android framework:

        InetSocketAddress inetAddr = (InetSocketAddress) remoteAddr;
        InetAddress addr;
        if ((addr = inetAddr.getAddress()) == null) {
            throw new UnknownHostException("Host is unresolved: " + inetAddr.getHostName());
        }
        int port = inetAddr.getPort();

        checkDestination(addr, port);
        synchronized (connectLock) {
            try {
                if (!isBound()) {
                    // socket already created at this point by earlier call or
                    // checkOpenAndCreate this caused us to lose socket
                    // options on create
                    // impl.create(true);
                    if (!usingSocks()) {
                        impl.bind(Inet6Address.ANY, 0);
                    }
                    isBound = true;
                }
                impl.connect(remoteAddr, timeout);
                isConnected = true;
                cacheLocalAddress();
            } catch (IOException e) {
                impl.close();
                throw e;
            }
        }

Android only accept resolved InetSocketAddress.

swankjesse commented 8 years ago

Could you report this issue upstream to AOSP? I’m not particularly eager to implement a SOCKS proxy inside of OkHttp. You might need to implement your own SocketFactory that implements SOCKS.

swankjesse commented 8 years ago

No action for us to take on this.