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

如何正确在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:

  1. Connect() for UDP just sets a default target address (it doesn't establish a real connection like TCP).
  2. Indy's TIdSocksInfo doesn't trigger the required UDP ASSOCIATE request when used with TIdUDPClient, which is why the proxy sees 0.0.0.0:0 instead 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 call IdUDPClient1->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

火山引擎 最新活动