TIdUDPClient::Connect() 将调用TIdSocksInfo::OpenUDP() 向 SOCKS v5 代理发送 UDP ASSOCIATION 请求。 TIdUDPClient 将其Host 和Port 属性值作为参数传递给OpenUDP()。
但是,TIdSocksInfo::OpenUDP() 会忽略传递给它的这些主机和端口值。将 IP 0.0.0.0(或用于 IPv6 的 ::0)和端口 0 发送到 SOCKS 代理是硬编码的。这没关系,根据 SOCKS v5 规范,RFC 1928:
UDP 关联
UDP ASSOCIATE 请求用于在 UDP 中继进程内建立关联以处理 UDP 数据报。 DST.ADDR 和 DST.PORT 字段包含客户端期望用于发送关联的 UDP 数据报的地址和端口。服务器可以使用此信息来限制对关联的访问。 如果客户端在 UDP ASSOCIATE 时不拥有信息,则客户端必须使用全零的端口号和地址。
当 UDP ASSOCIATE 请求到达的 TCP 连接终止时,UDP 关联终止。
在对 UDP ASSOCIATE 请求的回复中,BND.PORT 和 BND.ADDR 字段指示客户端必须发送要中继的 UDP 请求消息的端口号/地址。
因此,在 UDP ASSOCIATION 请求中,指定的主机/端口是客户端将从其发送传出数据报的本地主机/端口,如果还不知道该信息,则为零。这让代理的 UDP 中继知道当数据报从客户端发送到目标对等方时数据报来自哪里,以及当数据报从目标对等方发送到客户端时将数据报发送到何处。如果请求的主机/端口全为零,中继应该简单地使用发送请求的主机/端口。
UDP ASSOCIATION 请求中指定的主机/端口不是数据报要转发到的目标对等主机/端口,就像您想的那样。该目标信息在客户端将传递给代理以在创建关联之后转发的各个数据报中指定。与 TCP 不同,UDP 是无连接的,因此客户端可以创建单个关联,然后根据需要将数据报发送到多个目标。
UDP 中没有连接,因此代理在处理 UDP ASSOCIATION 请求时没有任何内容可以发送到目标服务器。它只是设置代理的侦听端口,然后将在客户端和目标服务器之间中继后续数据报。
之后发送数据报进行转发时,TIdUDPClient::SendBuffer() 将调用TIdSocksInfo::SendToUDP(),它也传递了TIdUDPClient 的Host 和Port 属性值。然后SendUDP() 将带有该目标信息的数据报发送到代理的中继端口:
基于 UDP 的客户端必须在对 UDP ASSOCIATE 请求的回复中通过 BND.PORT 指示的 UDP 端口将其数据报发送到 UDP 中继服务器。 如果选择的身份验证方法提供封装出于真实性、完整性和/或机密性的目的,必须使用适当的封装来封装数据报。每个 UDP 数据报都带有一个 UDP 请求头:
... [请求头,其中包含 DST.ADDR 和 DST.PORT] ...
当 UDP 中继服务器决定中继 UDP 数据报时,它会默默地这样做,而不会通知请求客户端。 同样,它会丢弃不能或不会中继的数据报。 当 UDP 中继服务器收到来自远程主机的回复数据报时,它必须使用上述 UDP 请求头封装该数据报,以及任何身份验证-方法相关的封装。
UDP 中继服务器必须从 SOCKS 服务器获取将向 UDP ASSOCIATE 回复中给出的 BND.PORT 发送数据报的客户端的预期 IP 地址。它必须丢弃来自任何源 IP 地址的任何数据报,而不是为特定关联记录的数据报。
...
因此,无论您在与目标服务器通信时遇到什么问题,它都可能与TIdUDPClient1::Connect() 根本无关,而更可能与TIdUDPClient::SendBuffer() 和TIdUDPClient::ReceiveBuffer() 有关。要么:
如果您再次嗅探TIdUDPClient 的流量并正确查看针对您的服务器的实际通信数据报,那么您将不得不嗅探代理另一端的流量以确保这些数据报实际被转发向前。如果是,那么问题必须出在网络或服务器本身上。如果不是,那么问题就出在代理本身上。