使用 Indy TIdHTTPProxyServer 问题连接到 i2p 站点

Connecting to i2p sites using Indy TIdHTTPProxyServer trouble

我正在开发一个代理切换系统,它的工作方式有点像浏览器 pac 文件。我已经设法过滤并将大多数请求重定向到正确的 IOhandlers 和 Socks 代理。

The Connection Settings I use in Firefox is

"Manual Proxy Configuration:"
"HTTP Proxy 127.0.0.1 Port 8080"
"Use this proxy server for all protocols" is ticked.

"Remote DNS" is ticked.

我很确定远程 DNS 不是问题,因为如果我将 Firefox 的 HTTP 端口设置为 4444。I2P 工作正常。

问题似乎出在 ChainProxy 函数中。而不是将 headers 从 HTTPProxyServer: TIdHTTPProxyServer 代理主机 '127.0.0.1' 代理端口 '8080' 传递到 Chain: TIdConnectThroughHttpProxy;代理主机“127.0.0.1”代理端口“4444”。它对 i2p 网站名称执行 DNS 请求,当然会失败。 我究竟做错了什么? 谢谢。


function Standard_IO(AContext: TIdHTTPProxyServerContext): TIdIOHandler;
var
  StackIO: TIdIOHandlerStack;
begin
  StackIO:=TIdIOHandlerStack.Create(AContext.OutboundClient);
  Result:=StackIO;
end;

function SSL_IO(AContext: TIdHTTPProxyServerContext): TIdIOHandler;
var
  SSLStackIO: TIdSSLIOHandlerSocketOpenSSL;
begin
  SSLStackIO:=TIdSSLIOHandlerSocketOpenSSL.Create(AContext.OutboundClient);
  SSLStackIO.SSLOptions.Mode:=sslmUnassigned;
  SSLStackIO.SSLOptions.Method:=sslvTLSv1_2;
  SSLStackIO.SSLOptions.SSLVersions:=[sslvSSLv2,sslvSSLv3,sslvTLSv1,sslvTLSv1_1,sslvTLSv1_2];
  SSLStackIO.SSLOptions.VerifyMode:=[];
  SSLStackIO.PassThrough:=True;
  Result:=SSLStackIO;
end;

function SocksProxy(AContext: TIdHTTPProxyServerContext; Host: String; Port: TIdPort; Version: TSocksVersion):  TIdCustomTransparentProxy;
var
  Socks: TIdSocksInfo;
begin
  AContext.OutboundClient.IOHandler:=Standard_IO(AContext);
  Socks:=TIdSocksInfo.Create(AContext.OutboundClient);
  Socks.Host:=Host;
  Socks.Port:=Port;
  Socks.Authentication:=saNoAuthentication;
  Socks.Version:=Version;
  Result:=Socks;
end;

function ChainProxy(AContext: TIdHTTPProxyServerContext; Host: String; Port: TIdPort): TIdCustomTransparentProxy;
var
  Chain: TIdConnectThroughHttpProxy;
begin
   AContext.OutboundClient.IOHandler:=Standard_IO(AContext);
   Chain:=TIdConnectThroughHttpProxy.Create(AContext.OutboundClient);
   Chain.Host:=Host;
   Chain.Port:=Port;
   Chain.Enabled:=True;
   Result:=Chain;
end;  

procedure TForm1.HTTPProxyServerHTTPBeforeCommand(AContext: TIdHTTPProxyServerContext);
begin
  case SwitchProxy(AContext) of
    0: AContext.OutboundClient.IOHandler:=Standard_IO(AContext);  // http://*
    1: AContext.OutboundClient.IOHandler:=SSL_IO(AContext);       // https://*:443
    2: AContext.OutboundClient.Socket.TransparentProxy:=SocksProxy(AContext, '127.0.0.1', 9150, svSocks5);  // *.onion
    3: AContext.OutboundClient.Socket.TransparentProxy:=ChainProxy(AContext, '127.0.0.1', 4444);            // *.i2p
   end;
end;     

如果连接到您的 TIdHTTPProxyServer 的 HTTP 客户端请求 .i2p 主机名(并且大概是 SwitchProxy() 在 returns 3 时查找的主机名) ,然后 OutboundClient.IOHandler 将要求 TIdConnectThroughHttpProxy 与位于 127.0.0.1:4444 的 HTTP 代理 运行 建立套接字连接,并向其发送命令以从 HTTP 连接到主机名和端口客户的原始 .i2p 请求。然后,HTTP 代理必须使用 DNS 将 .i2p 主机名解析为 IP,然后才能与该主机建立套接字连接并 return 回复 TIdConnectThroughHttpProxyTIdSocksInfo 以相同的方式操作。

FireFox 中的 Remote DNS 选项控制 FireFox 是在连接到代理之前自行将主机名解析为 IP,还是要求代理进行解析:

  • 如果 Remote DNS 关闭,FireFox 将在本地解析 IP,然后要求代理连接到该 IP(Indy 在连接到主机名时不会这样做)。

  • 如果 Remote DNS 开启,FireFox 会将主机名发送给代理并要求它解析 IP(Indy 在连接到主机名时执行此操作)。

由于您的 SOCKS/HTTP 代理 运行 与您的 TIdHTTPProxyServer 在同一台机器上,如果机器无法将 .i2p 主机名解析为 IP,那就是您的机器或代理配置的 DNS 问题。这不是您的 TIdHTTPProxyServer 代码的问题。

也就是说,您可以模仿 FireFox 在 Remote DNS 关闭时的操作。在本地将主机名解析为 IP 的最简单方法是在 IdStack 单元中使用 Indy 的 GIdStack.ResolveHost() 函数。这依赖于本地 OS 来执行实际的 DNS 查找。如果您想使用您选择的外部 DNS 服务器执行 DNS 查找(例如 public Internet 上任意数量的免费开放 DNS 服务器,例如 Google 的 DNS 服务器 8.8.8.8 或8.8.4.4),你可以为此使用 Indy 的 TIdDNSResolver 组件。

无论哪种方式,您都可以让 OnBeforeCommand 处理程序检索 TIdTCPClient(OutboundClient).Host 属性 的值(这是从 HTTP 客户端原始请求中的 URL 初始化的),如果它还不是 IP 地址,则在退出处理程序之前手动将其解析为 IP 并将其分配回 TIdTCPClient(OutboundClient).Host 属性。所有后续的链接代理请求将跳过服务器端 DNS 查找并按原样连接到 IP。