如何正确在TIdUDPClient中使用SOCKS 5代理?
Ah, I've run into this exact issue with Indy's TIdUDPClient and SOCKS5 proxies before—UDP works differently than TCP here, and Indy's transparent proxy support doesn't handle the SOCKS5 UDP association automatically. Let's break down what's going wrong and how to fix it.
Why Your Original Code Fails
The core problem is that SOCKS5 requires a separate UDP ASSOCIATE handshake over TCP before you can send any UDP traffic through the proxy. Your original code tries to use TransparentProxy directly with TIdUDPClient::Connect(), but:
Connect()for UDP just sets a default target address (it doesn't establish a real connection like TCP).- Indy's
TIdSocksInfodoesn't trigger the required UDP ASSOCIATE request when used withTIdUDPClient, which is why the proxy sees0.0.0.0:0instead of your target server details.
Step-by-Step Fix
Step 1: Perform the SOCKS5 UDP ASSOCIATE Handshake
First, use a TCP connection to the SOCKS5 proxy to send the UDP ASSOCIATE request. This tells the proxy to reserve a UDP port for forwarding your traffic, and it returns that port (plus the proxy's address) to you.
// Set up TCP client for SOCKS5 handshake TIdTCPClient* tcpSocksHandshake = new TIdTCPClient(); TIdSocksInfo* socksAuth = new TIdSocksInfo(tcpSocksHandshake); tcpSocksHandshake->Host = "your-proxy-ip"; tcpSocksHandshake->Port = your-proxy-port; socksAuth->Version = svSocks5; socksAuth->Authentication = saNoAuthentication; tcpSocksHandshake->TransparentProxy = socksAuth; try { tcpSocksHandshake->Connect(); // Build UDP ASSOCIATE request (per SOCKS5 spec) TIdBytes associateRequest; associateRequest.Add(0x05); // SOCKS version 5 associateRequest.Add(0x03); // Command: UDP ASSOCIATE associateRequest.Add(0x00); // Reserved (must be 0) associateRequest.Add(0x01); // Address type: IPv4 // Destination address: 0.0.0.0 (let proxy choose bind address) associateRequest.Add(0x00); associateRequest.Add(0x00); associateRequest.Add(0x00); associateRequest.Add(0x00); // Destination port: 0 (let proxy choose bind port) associateRequest.Add(0x00); associateRequest.Add(0x00); // Send the request and read the response tcpSocksHandshake->IOHandler->Write(associateRequest); TIdBytes associateResponse; tcpSocksHandshake->IOHandler->ReadBytes(associateResponse, 10); // Minimum response length // Validate the response if (associateResponse[0] != 0x05 || associateResponse[1] != 0x00) { throw Exception("SOCKS5 UDP ASSOCIATE failed: Proxy returned error"); } // Parse the proxy's bound UDP address and port String proxyBindHost; int proxyBindPort; if (associateResponse[3] == 0x01) { // IPv4 address proxyBindHost = String().sprintf("%d.%d.%d.%d", associateResponse[4], associateResponse[5], associateResponse[6], associateResponse[7]); } else if (associateResponse[3] == 0x03) { // Domain name int domainLen = associateResponse[4]; proxyBindHost = BytesToString(associateResponse, 5, domainLen); } else { // IPv6 (simplified handling) proxyBindHost = "[::1]"; } // Extract bound port (big-endian) proxyBindPort = (associateResponse[associateResponse.Length-2] << 8) | associateResponse[associateResponse.Length-1]; // Configure your UDP client to send to the proxy's bound port IdUDPClient1->Host = proxyBindHost; IdUDPClient1->Port = proxyBindPort; } __finally { delete tcpSocksHandshake; delete socksAuth; }
Step 2: Send UDP Traffic with SOCKS5 Header
Now that you have the proxy's bound UDP port, you need to wrap every UDP packet in a SOCKS5 UDP header (which tells the proxy where to forward the traffic). Here's a helper function to do that:
void SendUDPThroughSocks5(TIdUDPClient* udpClient, const String& targetHost, int targetPort, const TIdBytes& data) { TIdBytes socksUdpHeader; // SOCKS5 UDP header structure: // RSV (2 bytes: 0x0000) + FRAG (1 byte: 0x00 = no fragmentation) + ATYP + DST.ADDR + DST.PORT socksUdpHeader.Add(0x00); socksUdpHeader.Add(0x00); socksUdpHeader.Add(0x00); // Handle target address type TIdIPAddress* targetIp = new TIdIPAddress(); try { targetIp->IPAddress = targetHost; if (targetIp->IPVersion == Id_IPv4) { socksUdpHeader.Add(0x01); // IPv4 TIdBytes ipv4Bytes = targetIp->ToBytes(); socksUdpHeader.AddRange(ipv4Bytes); } else if (targetIp->IPVersion == Id_IPv6) { socksUdpHeader.Add(0x04); // IPv6 TIdBytes ipv6Bytes = targetIp->ToBytes(); socksUdpHeader.AddRange(ipv6Bytes); } else { // Domain name socksUdpHeader.Add(0x03); // Domain TIdBytes domainBytes = StringToBytes(targetHost); socksUdpHeader.Add(domainBytes.Length); socksUdpHeader.AddRange(domainBytes); } } __finally { delete targetIp; } // Add target port (big-endian) socksUdpHeader.Add((targetPort >> 8) & 0xFF); socksUdpHeader.Add(targetPort & 0xFF); // Combine header and data, then send to proxy socksUdpHeader.AddRange(data); udpClient->SendBuffer(socksUdpHeader); }
How to Use It
After setting up the UDP client with the proxy's bound port, send your data like this:
TIdBytes yourData = StringToBytes("Hello UDP server!"); SendUDPThroughSocks5(IdUDPClient1, "your-udp-server-ip", your-udp-server-port, yourData);
Key Notes
- Proxy Support: Make sure your SOCKS5 proxy has UDP ASSOCIATE enabled—some proxies disable UDP by default.
- No
Connect()Needed: You don't need to callIdUDPClient1->Connect()anymore, since we're explicitly sending to the proxy's bound port. - Response Handling: If you need to receive responses from the UDP server, you'll need to read the UDP packets from the proxy, strip off the SOCKS5 header, and parse the original source address/port.
内容的提问来源于stack exchange,提问作者Nick




