为啥我的 SOCKS5 UDP 实现不起作用?

Posted

技术标签:

【中文标题】为啥我的 SOCKS5 UDP 实现不起作用?【英文标题】:Why is my SOCKS5 UDP implementation not working?为什么我的 SOCKS5 UDP 实现不起作用? 【发布时间】:2018-06-11 10:05:31 【问题描述】:

我在这个废话上花了 50 多个小时,为什么我的 UDP 实现不起作用?问题是服务器没有收到UDP数据包。我制作了服务器应用程序以在控制台中记录事件,但我发现它没有收到任何东西。这是我遵循的 SOCKS5 文档:https://www.ietf.org/rfc/rfc1928.txt

byte[] response = new byte[256];
int responseLength;
Socket socket = new Socket(proxyHost, proxyPort);
DatagramSocket clientSocket = new DatagramSocket();

DataOutputStream writer = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
DataInputStream reader = new DataInputStream(new BufferedInputStream(socket.getInputStream()));

// connect/authorization request
writer.writeByte(0x05); // VER
writer.writeByte(1); // NMETHODS
writer.writeByte(0); // METHODS
writer.flush();

// connect/authorization response
responseLength = reader.read(response); // RESPONSE IS: 5 0

// udp associate request
writer.writeByte(0x05); // VER
writer.writeByte(0x03); // CMD - UDP ASSOCIATE
writer.writeByte(0x00); // RSV
writer.writeByte(0x01); // ATYP
// is what I'm sending here as DST.ADDR & DST.PORT okay?
writer.write(clientSocket.getLocalAddress().getAddress()); // DST.ADDR
writer.writeShort(clientSocket.getLocalPort()); // DST.PORT
//writer.write(InetAddress.getByName(trackerHost).getAddress());
//writer.writeShort(trackerPort);
writer.flush();

// udp associate response
reader.skipBytes(4); // skip VER, REP, RSV, ATYP bytes
relayAddress = reader.readInt();
InetAddress relayInetAddress = InetAddress.getByAddress(ByteBuffer.allocate(4).putInt(relayAddress).array());
relayPort = reader.readUnsignedShort(); // server returns some random port

// connect to relay server via datagramsocket
clientSocket.connect(relayInetAddress, relayPort);

// send package to destination server
ByteBuffer byteBuffer = ByteBuffer.allocate(16 + 10);
//header - 10 bytes
byteBuffer.putShort((short) 0); // RSV
byteBuffer.put((byte) 0); // FRAG
byteBuffer.put((byte) 1); // ATYP
byteBuffer.put(trackerInetAddress.getAddress()); // DST.ADDR
byteBuffer.putShort((short) targetPort); // DST.PORT
//packet data
byteBuffer.putLong(1);
byteBuffer.putInt(0);
byteBuffer.putInt(1);
// send packet
packet = byteBuffer.array();
DatagramPacket sendPacket = new DatagramPacket(packet, packet.length);//, relayInetAddress, relayPort);
clientSocket.send(sendPacket); // SERVER DOES NOT RECEIVE THIS

验证目标主机在没有代理的情况下可以 100% 正常工作。使用来自https://blazingseollc.com/proxy/ 的支持 SOCKS5 的半专用代理

【问题讨论】:

你为什么用Reader读取字节?你为什么同时滥用Reader.ready()InputStream.available() @EJP 请详细说明,这有什么问题? InputStream.available() 返回可供读取的字节数。我正在使用 ready() 以等待响应数据包到达。它适用于这部分,我收到身份验证和 udp 关联响应很好。读取(或更类似于接收)的问题仅出现在 DatagramSocket 部分。当然,在那部分之前可能已经犯了错误。任何可能的解决方案线索都是金,所以让我知道 1. Reader 读取 char,而不是 byte。 2. while (!inputStreamReader.ready()); 除了抽你的 CPU 之外什么也没做,因为下面的读取无论如何都会阻塞。 3.有一个具体的警告,不要像你一样使用available()。这都在 Javadoc 中。您也完全忽略了响应的内容;您还假设响应而不是断开连接,并且响应表明成功。您需要解析它,并且这样做作为字节。你会发现DataInputStream 的方法很有用。而不是所有这些废话。 @EJP 好的,我用 DataInputStream 和 DataOutputStream 更新了代码;但这不是问题的代码之美,所以它没有解决任何问题 我不是在谈论“美”,我是在谈论正确性。 编写与 Javadoc 不一致的代码是不正确的。我发表了我的评论作为评论,而不是作为答案。我现在要问你为什么不对响应进行错误检查。 【参考方案1】:

原来代理提供商不支持 SOCKS5 的 UDP ASSOCIATE。事实证明大多数代理提供商不支持它。

【讨论】:

代理的响应在REP字段中有一个状态码,指示请求是被接受还是被拒绝,如果被拒绝,那么为什么(例如0x07表示“命令不支持”)。您的代码忽略了该字段(它也忽略了 ATYP 字段并假设响应始终包含 IPv4 地址,但这不是保证)。 我在调试器中检查和错误检查这些字段的响应,这些代理提供者甚至没有费心回复 0x07 或任何指示不支持的命令 他们必须用 something 来回应。 REP 字段中除0x00 之外的任何值都是请求失败。如果他们只是忽略请求而不发送响应,那么他们不符合协议。 那么他们不合规或配置错误 SOCKS 很容易实现,所以我非常怀疑任何代理制造商都不会遵循 complete 规范,即使不是所有命令都实现了。我强烈建议您使用像 Wireshark 这样的数据包嗅探器来验证您实际上以正确的格式传输 SOCKS 请求,并确保代理根本没有真正响应。也许是这样,只是你没有正确阅读。

以上是关于为啥我的 SOCKS5 UDP 实现不起作用?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我的 Printable 实现在 Swift 操场上不起作用?

使用 urllib2、socks5 代理和 socksipy 时超时不起作用

是否已经有适用于 android 的 StopWatch 类,为啥我的实现不起作用?

Dante socks5 在外部服务器上不起作用

为啥我的完成块不起作用?

为啥 SOCKS5 需要通过 UDP 中继 UDP?