I'm getting an access violation when using the IdHTTP client component in
conjunction with a SOCKS proxy server and using SSL.
- If I use the IdHTTP component with my SOCKS proxy (and a non https URL),
everything works without problems.
- If I use the IdHTTP component with an SSL URL (and no SOCKS proxy),
everything works without problems.
...but using SOCKS and accessing an HTTPS:// link *together* raises the AV.
My problem mirrors the description given in this old newsgroup thread:
It's likely that my understanding of the circumstances is wrong, but from
what I've been able to determine, it looks like there's some problem with
the order of events in IdSSLOpenSSL.pas when you use a SOCKS proxy with SSL?
Here's a run-down:
Scenario 1: Using IdHTTP + https:// URL -> processing reaches line 202 in
IOHandlerSocket.pas
procedure TIdIOHandlerSocket.ConnectClient(....)
begin
...
SocksInfo.MakeSocksConnection(AHost, APort)
1) Processing skips to line 263 in IdSocks.pas:
procedure TIdSocksInfo.MakeSocksConnection(const AHost: string; const APort:
Integer);
begin
case Version of...
2) Because no SOCKS settings are in effect, none of the CASE statements
applies, so processing jumps to the end of the method.
3) Processing returns to IdIOHandlerSocket, and the end of the ConnectClient
method. From there, it's passed back to IdSSLOpenSSL, and the
TIdSSLOpenSSLIOHandlerSocket.ConnectClient
4) ...method. Towards the bottom of this method, there's a section that
reads:
if not PassThrough then begin
OpenEncodedConnection;
end;
5) Processing passes in to the OpenEncodedConnection method on line 999 of
IdSSLOpenSSL.pas
procedure TIdSSLIOHandlerSocket.OpenEncodedConnection;
begin
...
fSSLSocket.Connect(Binding.Handle, fSSLContext)
6) ..and down to the line above. This is the crucial point - Connect (line
1423, IdSSLOpenSSL.pas) look like:
procedure TIdSSLSocket.Connect(const pHandle: TIdStackSocketHandle; ...)
begin
fSSL := IdSslNew(fSSLContent.fContent);
7) The line above seems to be critical. By contrast -
Scenario 2: Using IdHTTP + https:// URL + SOCKS proxy -> steps as per
Scenario 1, up to (1), line 263 in IdSocks.pas
TIdSocksInfo.MakeSocksConnection(const AHost; string; const APort: Integer);
begin
case Version of
svSocks4, svSocks4A: MakeSocks4Connection(AHost, APort);
svSocks5: MakeSocks5Connection(AHost, APort);
end;
end;
1) ...in my case, svSocks5 is selected, and processing jumps to
MakeSocks5Connection, line 137 in IdSocks.pas. Half-way through the
MakeSocks5Connection method, there's a line which reads (line 157):
FIOHandler.Send(tempBuffer, len);
2) From here, processing jumps to the Send method in IdSSLOpenSSL.pas, line
853
function TIdSSLIOHandlerSocket.Send(var ABuf; ALen: integer): integer;
begin
..
result := SendEnc(ABuf, ALen);
3) ...and follows through down to line 859, shown above.
4) SendEnc is on line 883 of IdSSLOpenSSL.pas:
function TIdSSLIOHandlerSocket.SendEnc(var ABuf; ALen: integer): integer;
begin
Result := fSSLSocket.Send(ABuf, ALen);
end;
5) ...Send here passes processing to the Send method on line 1465 of
IdSSLOpenSSL.pas:
function TIdSSLOpenSSLSocket.Send(var ABuf; ALen: integer): integer;
...
Result := IdSslWrite(fSSL, @ABuf, ALen);
6) Here's where things get interesting - the line above marks the critical
difference between scenario's 1 and 2. In Scenario 1, MakeSocks5Connection
was not called, and instead, processing ultimately reached Scenario 1->(6)
and the line:
fSSL := IdSslNew(fSSLContent.fContent);
In Scenario 2, 'New' is not called, so fSSL doesn't point to a valid
instance - and an AV occurs when IdSslWrite is called.
Apologies for the convoluted nature of my explanation (such as it is), but I
hope that might help someone understand the problem I'm encountering, and
possibly offer a solution?
I could probably hack something together (somehow work in a way to ensure
that 'IdSslNew' is called) for my own purposes, but I don't suppose it would
be Indy-architecture-safe, and I'd prefer an 'official' fix - unless I'm
doing something horribly wrong, and there isn't actually a problem at all...
Many thanks for your time, and any advice!
Chris
procedure TIdSSLIOHandlerSocket.ConnectClient(const AHost: string; const
APort: Integer; const ABoundIP: string;
const ABoundPort: Integer; const ABoundPortMin: Integer; const
ABoundPortMax: Integer;
const ATimeout: Integer = IdTimeoutDefault);
begin
inherited ConnectClient(AHost, APort, ABoundIP, ABoundPort, ABoundPortMin,
ABoundPortMax, ATimeout);
DoBeforeConnect(self);
// CreateSSLContext(sslmClient);
// CreateSSLContext(SSLOptions.fMode);
try
Init;
except
on EIdOSSLCouldNotLoadSSLLibrary do begin
if not PassThrough then raise;
end;
end;
if not PassThrough then begin
OpenEncodedConnection;
end;
end;
The problem is, in the inherited ClientConnect call none of the
pre-requisites for SSL communications are established when
MakeSocks<version>Connection is called inside of IdSocks.pas - they're only
put in place via the call to 'Init' and 'OpenEncodedConnection'
*afterwards*. Therefore, the required TIdSSLSocket object is not available
when the IdSocks unit tries to do any SSL communication, which causes an AV.
...For the time being I'm thinking about doing some horrible hack-around:
calling Init and OpenEncodedConnection *before* the inherited ConnectClient
call IF the SockInfo.Version value matches 4, 4A, or 5, anf then exiting the
procedure immediately after the inherited call once it's finished - pretty
ugly.
EIdSocksAuthMethodError
...in IdSocks.pas (line 168) because - for some reason I can't figure out -
the 'ServerAuthMethod' and 'ReqestedAuthMethod' enums don't match.
If I comment that section out (just to see what happens), I always then get
a
EIdSocksServerGeneralError
...in IdSocks.pas (line 231) - so there's obviously some important step or
other stage that I'm carrying out here. Anyway, I'm giving up for now -
still not sure if this is a genuine 'bug' or not, but I've disabled that
combination (SOCKS + SSL + HTTP) in my app for the time being instead...
Chris Harper ,
can u please tell me how u did it?
thanx,
Nuwan