使用 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 回复 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
代码的问题。
也就是说,您可以模仿 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。
我正在开发一个代理切换系统,它的工作方式有点像浏览器 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 回复 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
代码的问题。
也就是说,您可以模仿 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。