【问题标题】:Connecting to i2p sites using Indy TIdHTTPProxyServer trouble使用 Indy TIdHTTPProxyServer 连接到 i2p 站点的问题
【发布时间】:2016-02-22 18:51:39
【问题描述】:

我正在开发一个类似于浏览器 pac 文件的代理切换系统。我已经设法过滤并将大多数请求重定向到正确的 IOhandlers 和 Socks 代理。

我在 Firefox 中使用的连接设置是

“手动代理配置:”
“HTTP 代理 127.0.0.1 端口 8080”
勾选“将此代理服务器用于所有协议”。

“远程 DNS”打勾。

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

问题似乎出在 ChainProxy 函数中。而不是将标头从 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;     

【问题讨论】:

  • OnBeforeCommand 事件中,OutboundClient.IOHandler 属性尚未分配。如果 SwitchProxy() 返回 2 或 3,则您的代码依赖编译器在访问 OutboundClient.Socket 属性之前调用 SocksProxy()ChainProxy()。这是一个危险的假设。更安全的设计是更改SocksProxy()ChainProxy() 以返回一个新的TIdIOHandler,其中附加了所需的TransparentProxy,然后将其分配给OutboundClient.IOHandler,就像案例0和1一样。

标签: delphi proxy indy freepascal indy10


【解决方案1】:

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

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

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

  • 如果Remote DNS 开启,FireFox 会将主机名发送到代理并要求它解析 IP(Indy 在连接到主机名时会这样做)。

由于您的 SOCKS/HTTP 代理与您的 TIdHTTPProxyServer 在同一台机器上运行,如果该机器无法将 .i2p 主机名解析为 IP,则说明您的机器或代理的配置存在 DNS 问题。您的TIdHTTPProxyServer 代码没有问题。

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

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

【讨论】:

  • 谢谢。能够让它工作。我重写了代码如下。
  • procedure ChainProxy(var AContext: TIdHTTPProxyServerContext; Host: String; Port: TIdPort); begin with AContext.OutboundClient do begin IOHandler:=SSL_IO(AContext); Socket.TransparentProxy:=TIdConnectThroughHttpProxy.Create(AContext.OutboundClient); Socket.TransparentProxy.Host:=Host; Socket.TransparentProxy.Port:=Port; TIdConnectThroughHttpProxy(Socket.TransparentProxy).Enabled:=True; end;
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-11-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多