You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Android 24+全局代理问题:VPN SocketChannel代理配置失败求助

Fixing VPN Traffic Forwarding to External Proxy on Android 24+

Hey there, let's break down why your current code isn't working and how to fix it to route VPN traffic through your external proxy properly.

Why Your Existing Code Fails

  • First approach: You're connecting directly to the destination, so no proxy is involved at all—this bypasses your external proxy entirely.
  • Second approach: You connect to the proxy, but then try to call connect() again to the destination. That's not how proxies work: once connected to the proxy, you need to send a proxy-specific handshake request (like HTTP CONNECT or SOCKS5) to ask the proxy to establish a tunnel to your target, not call connect() directly.

Correct Implementation Steps

The key is to:

  1. Establish a connection to your external proxy
  2. Complete the proxy handshake to create a tunnel to the destination
  3. Use the established tunnel to send/receive traffic from the destination

Below are examples tailored to fit with the TCPOutput structure from LocalVPN:

Example 1: HTTP/HTTPS Proxy (Using CONNECT Method)

// Step 1: Connect to proxy and protect the socket from VPN loopback
Socket proxySocket = new Socket("PROXY.COM", 8080);
vpnService.protect(proxySocket); // Critical: Prevents this socket's traffic from being routed back through VPN
SocketChannel outputChannel = proxySocket.getChannel();
outputChannel.configureBlocking(false);

// Step 2: Send HTTP CONNECT request to proxy to create tunnel to destination
String connectRequest = String.format(
    "CONNECT %s:%d HTTP/1.1\r\nHost: %s:%d\r\nConnection: keep-alive\r\n\r\n",
    destinationAddress, destinationPort,
    destinationAddress, destinationPort
);
ByteBuffer requestBuffer = ByteBuffer.wrap(connectRequest.getBytes(StandardCharsets.UTF_8));

// Handle non-blocking write (match LocalVPN's async IO pattern)
while (requestBuffer.hasRemaining()) {
    int bytesWritten = outputChannel.write(requestBuffer);
    if (bytesWritten == 0) {
        // Wait for writable event via Selector (as done in TCPOutput)
        break;
    }
}

// Step 3: Verify proxy's response (wait for readable event first in non-blocking mode)
ByteBuffer responseBuffer = ByteBuffer.allocate(1024);
int bytesRead = outputChannel.read(responseBuffer);
if (bytesRead > 0) {
    responseBuffer.flip();
    String response = new String(responseBuffer.array(), 0, bytesRead, StandardCharsets.UTF_8);
    if (!response.startsWith("HTTP/1.1 200")) {
        // Proxy rejected the CONNECT request—handle error and close socket
        proxySocket.close();
        return;
    }
}

// Step 4: Now you can use outputChannel to send/receive traffic to the destination
// This replaces the direct destination connection logic in TCPOutput

Example 2: SOCKS5 Proxy

If you're using a SOCKS5 proxy, the handshake sequence is different:

// Step 1: Connect to SOCKS5 proxy and protect the socket
Socket proxySocket = new Socket("PROXY.COM", 1080);
vpnService.protect(proxySocket);
SocketChannel outputChannel = proxySocket.getChannel();
outputChannel.configureBlocking(false);

// Step 2: SOCKS5 initial handshake (no authentication)
ByteBuffer handshakeBuffer = ByteBuffer.allocate(3);
handshakeBuffer.put((byte) 0x05); // SOCKS version 5
handshakeBuffer.put((byte) 0x01); // Number of authentication methods
handshakeBuffer.put((byte) 0x00); // No authentication required
handshakeBuffer.flip();
outputChannel.write(handshakeBuffer);

// Read handshake response
ByteBuffer authResponse = ByteBuffer.allocate(2);
outputChannel.read(authResponse);
authResponse.flip();
if (authResponse.get() != 0x05 || authResponse.get() != 0x00) {
    proxySocket.close();
    return;
}

// Step 3: Send SOCKS5 connect request to destination
ByteBuffer connectRequest = ByteBuffer.allocate(10 + destinationAddress.length());
connectRequest.put((byte) 0x05); // Version
connectRequest.put((byte) 0x01); // CONNECT command
connectRequest.put((byte) 0x00); // Reserved
connectRequest.put((byte) 0x03); // Domain name type
connectRequest.put((byte) destinationAddress.length()); // Domain length
connectRequest.put(destinationAddress.getBytes(StandardCharsets.UTF_8)); // Domain
connectRequest.putShort((short) destinationPort); // Port
connectRequest.flip();
outputChannel.write(connectRequest);

// Read connect response
ByteBuffer connectResponse = ByteBuffer.allocate(10);
outputChannel.read(connectResponse);
connectResponse.flip();
if (connectResponse.get() != 0x05 || connectResponse.get() != 0x00) {
    proxySocket.close();
    return;
}

// Now ready to transfer traffic through the tunnel

Key Notes for Integration with LocalVPN

  • Non-blocking IO: Since LocalVPN uses Selector for async IO, you'll need to register the proxy SocketChannel with the selector and handle write/read events asynchronously (just like the original TCPOutput does for direct connections).
  • Avoid VPN Loopback: Always call vpnService.protect() on the proxy socket—this tells Android not to route the proxy-bound traffic back through your VPN, which would cause an infinite loop.
  • Error Handling: Add proper error handling for partial writes/reads, proxy timeouts, and failed handshakes (matching the existing error handling in TCPOutput).

内容的提问来源于stack exchange,提问作者DrBrad

火山引擎 最新活动