I'm working on a proxy switching system that works a bit like browser pac files. I've managed to filter and redirect most requests to the correct IOhandlers and Socks proxys.
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.
I'm pretty sure that the Remote DNS isn't the problem because if I set Firefox's HTTP port to 4444. I2P works fine.
The problem seems to be in the ChainProxy function. Instead of passing the headers from HTTPProxyServer: TIdHTTPProxyServer proxy host '127.0.0.1' proxy port '8080' to Chain: TIdConnectThroughHttpProxy; proxy host '127.0.0.1' proxy port '4444'. It does a DNS request for the i2p web site name which of course fails. What am I doing wrong? Thanks.
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;
If the HTTP client that is connected to your TIdHTTPProxyServer
requests a .i2p
hostname (and presumably that is what SwitchProxy()
looks for when it returns 3), then the OutboundClient.IOHandler
will ask TIdConnectThroughHttpProxy
to make a socket connection to the HTTP proxy running at 127.0.0.1:4444 and send it a command to connect to the hostname and port from the HTTP client's original .i2p
request. The HTTP proxy will then have to resolve the .i2p
hostname to an IP using DNS before it can make a socket connection to that host and return a reply to TIdConnectThroughHttpProxy
. TIdSocksInfo
operates in the same manner.
The Remote DNS
option in FireFox controls whether FireFox itself resolves a hostname to an IP before connecting to the proxy, or whether it asks the proxy to do the resolving:
If Remote DNS
is off, FireFox will resolve the IP locally and then ask the proxy to connect to that IP (Indy does not do this when connecting to a hostname).
If Remote DNS
is on, FireFox will send the hostname to the proxy and ask it to resolve the IP (Indy does this when connecting to a hostname).
Since your SOCKS/HTTP proxies are running on the same machine as your TIdHTTPProxyServer
, if the machine cannot resolve the .i2p
hostname to an IP, that is a DNS issue with your machine, or the proxy's configurations. It is not a problem with your TIdHTTPProxyServer
code.
That being said, you can mimic what FireFox does when Remote DNS
is off. The simplest way to locally resolve a hostname to an IP is to use Indy's GIdStack.ResolveHost()
function in the IdStack
unit. This relies on the local OS to perform the actual DNS lookup. If you want to perform the DNS lookup using an external DNS server of your choosing (such as any number of free open DNS servers on the public Internet, like Google's DNS servers at 8.8.8.8 or 8.8.4.4), you can use Indy's TIdDNSResolver
component for that.
Either way, you can have your OnBeforeCommand
handler retrieve the value of the TIdTCPClient(OutboundClient).Host
property (which is initialized from the URL in the HTTP client's original request), and if it is not already an IP address then manually resolve it to an IP and assign it back to the TIdTCPClient(OutboundClient).Host
property before exiting your handler. All subsequent chained proxy requests will skip server-side DNS lookups and connect to the IP as-is.