🚀 go-pugleaf

RetroBBS NetNews Server

Inspired by RockSolid Light RIP Retro Guy

Thread View: borland.public.cppbuilder.internet.socket
5 messages
5 total messages Started by Dennis Cote Wed, 16 Apr 2008 17:10
Error handling in Indy 10
#341
Author: Dennis Cote
Date: Wed, 16 Apr 2008 17:10
123 lines
6367 bytes
Hi All,

I have an application that has recently been converted to use Indy 10. 
This application runs correctly on Win XP. I am now testing the 
application under Vista. It does not work under Vista and I believe the 
issue has to do with Administrator rights, but that is not the immediate 
concern.

My concern is with the nature of the error checking being done in the 
Indy code. A failure to open a socket is generating a RangeError exception.

Just before it fails my application ends up in NewSocketHandle while 
trying to open an TIdICMPClient. This function is copied below.

function TIdStackBSDBase.NewSocketHandle(const ASocketType:TIdSocketType;
   const AProtocol: TIdSocketProtocol;
   const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION;
   const AOverlapped: Boolean = False): TIdStackSocketHandle;
begin
   Result := CheckForSocketError(WSSocket(IdIPFamily[AIPVersion], 
ASocketType, AProtocol, AOverlapped));
end;

I have stepped through the assembly code of this routine to track the 
problem. This routine is essentially the one line.

IdStackBSDBase.pas.389: Result := 
CheckForSocketError(WSSocket(IdIPFamily[AIPVersion], ASocketType,
00837321 8B45FC           mov eax,[ebp-$04]
00837324 50               push eax
00837325 0FB64508         movzx eax,[ebp+$08]
00837329 50               push eax
0083732A 8BCE             mov ecx,esi
0083732C 85C9             test ecx,ecx
0083732E 7905             jns $00837335
00837330 E8F35FE9FF       call System::__linkproc__ BoundErr()
00837335 0FB6450C         movzx eax,[ebp+$0c]
00837339 8B1485246E9900   mov edx,[eax*4+$996e24]
00837340 8BC3             mov eax,ebx
00837342 8B30             mov esi,[eax]
00837344 FF96C4000000     call dword ptr [esi+$000000c4]
0083734A 8BD0             mov edx,eax
0083734C 85D2             test edx,edx
0083734E 7905             jns $00837355
00837350 E8D35FE9FF       call System::__linkproc__ BoundErr()
00837355 8BC3             mov eax,ebx
00837357 E81C0D0000       call Idstack::TIdStack::CheckForSocketError(int)
0083735C 85C0             test eax,eax
0083735E 7905             jns $00837365
00837360 E8C35FE9FF       call System::__linkproc__ BoundErr()

This code pushes the arguments to WSSocket() and then makes the virtual 
function call at 00837344. This ends up at WSSocket in the Windows stack.

function TIdStackWindows.WSSocket(AFamily, AStruct, AProtocol: Integer;
  const AOverlapped: Boolean = False): TIdStackSocketHandle;
begin
   if AOverlapped then begin
     Result := WSASocket(AFamily, AStruct, 
AProtocol,nil,0,WSA_FLAG_OVERLAPPED);
   end else begin
     Result := IdWinsock2.Socket(AFamily, AStruct, AProtocol);
   end;
end;

The argument AOverlapped is false, so it makes the call to 
IdWinsock2.Socket().

IdStackWindows.pas.600: Result := IdWinsock2.Socket(AFamily, AStruct, 
AProtocol);
00835494 50               push eax
00835495 51               push ecx
00835496 52               push edx
00835497 A12CF19900       mov eax,[$0099f12c]
0083549C 8B00             mov eax,[eax]
0083549E FFD0             call eax
IdStackWindows.pas.602: end;
008354A0 5D               pop ebp
008354A1 C20800           ret $0008

This function is failing (probably because of administrator rights) and 
returning a value of 0xFFFFFFFF (or -1) in the ax register to 
NewSocketHandle.

NewSocketHandle now does a range check on the result at 0083734A through 
0083734E. I'm not sure if that is correct but it seems like it might be 
(see below). In any case, the range check is failing because the result 
is negative (i.e the sign flag is set by the test instruction). This 
causes the code to call System::BoundErr() which generates the 
RangeError exception. The result of WSSocket() never makes it to the 
function CheckForSocketError() which is obviously intended to check this 
result because of this range check.

Now I'm no Delphi expert (i.e. it took me quite a while to confirm that 
LongWord and Longword are the same type because Delphi is case 
insensitive) but I tracked the types to see why this range check is 
being done. It seems reasonable to me, but I may be mistaken.

WSSocket() is defined to return a TIdStackSocketHandle result. This type 
is defined as a TSocket at line 115 of IdStatckConsts.pas. This type is 
in turn defined as PtrUint at line 219 of IdWinsock2.pas. Finally this 
type is defined as LongWord at line 674 of IdGlobal.pas.

The type of the argumnet to function CheckForSocketError() is defined as 
Integer at line 571 of IdStack.pas.

So NewSocketHandle() is trying to pass the LongWord result from 
WSSocket() to the Integer argument of CheckForSocketError(). Since 
LongWord is an unsiged type, its range is larger than the Integer 
arguments type. Hence the range check is required.

It seems to me that this range check will always fail if WSSocket 
returns 0xFFFFFFFF (or -1) for a failure, and hence we will never get to 
see the real reason for the failure that CheckForSocketError() is 
supposed to produce. I think some type casting must be needed to tell 
the compiler not to check the range of the result before passing it to 
CheckForSocketError(), but I'm not sure how that is done in Delphi. I 
also suspect this same error may exist in other places in the code.

Can anyone say if this analysis is correct, and if so how the result 
should be cast from unsigned to signed without the range check?

Dennis Cote
Re: Error handling in Indy 10
#344
Author: Remy Lebeau (TeamB)
Date: Wed, 16 Apr 2008 20:23
96 lines
4496 bytes
"Dennis Cote" <denniscote@gmail.com> wrote in message 
news:480632fb$1@newsgroups.borland.com...

> I am now testing the application under Vista. It does not
> work under Vista and I believe the issue has to do with
> Administrator rights

You are using the TIdICMPClient component, which uses a RAW socket 
internally (as opposed to using a TCP or UDP socket).  Admin rights are 
required in order to use RAW sockets on NT4 onwards (which means your code 
should not have worked on XP, either, unless you are running as an admin). 
Of course, Microsoft circumvents its own rules by allowing icmp.dll to use 
RAW sockets without admin rights.

> I think some type casting must be needed to tell the
> compiler not to check the range of the result before
> passing it to CheckForSocketError() but I'm not
> sure how that is done in Delphi.

Rather than casting, you could disable range checking itself for 
NewSocketHandle().  Indy does this in a couple of other places, for example:

    function TIdStackBSDBase.NewSocketHandle(const ASocketType: 
TIdSocketType;
        const AProtocol: TIdSocketProtocol;
        const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION;
        const AOverlapped: Boolean = False): TIdStackSocketHandle;
    begin
        {$IFOPT R+}
        {$DEFINE RestoreRangeCheckingON}
        {$R-}
        {$ENDIF}
        Result := CheckForSocketError(WSSocket(IdIPFamily[AIPVersion], 
ASocketType, AProtocol, AOverlapped));
        {$IFDEF RestoreRangeCheckingON}
        {$R+}
        {$UNDEF RestoreRangeCheckingON}
        {$ENDIF}
    end;

Otherwise, you would have to hard-code the casting, which can be pretty 
ugly:

    function TIdStackBSDBase.NewSocketHandle(const ASocketType: 
TIdSocketType;
        const AProtocol: TIdSocketProtocol;
        const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION;
        const AOverlapped: Boolean = False): TIdStackSocketHandle;
    begin
        Result := 
TIdStackSocketHandle(CheckForSocketError(Integer(WSSocket(IdIPFamily[AIPVersion], 
ASocketType, AProtocol, AOverlapped))));
    end;

Here is a cleaner version:

    function TIdStackBSDBase.NewSocketHandle(const ASocketType: 
TIdSocketType;
        const AProtocol: TIdSocketProtocol;
        const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION;
        const AOverlapped: Boolean = False): TIdStackSocketHandle;
    begin
        Result := WSSocket(IdIPFamily[AIPVersion], ASocketType, AProtocol, 
AOverlapped);
        CheckForSocketError(Integer(Result));
    end;

> I also suspect this same error may exist in other places in the code.

Actually, it doesn't.  Everywhere else that calls CheckForSocketError() 
passes it an Integer.

> Can anyone say if this analysis is correct, and if so how the
> result should be cast from unsigned to signed without the
> range check?

Since CheckForSocketError() is expecting an Integer error code, not a 
TSocket handle, I would suggest changing TIdStackBSDBase.NewSocketHandle() 
to the following instead:

    function TIdStackBSDBase.NewSocketHandle(const ASocketType: 
TIdSocketType;
        const AProtocol: TIdSocketProtocol;
        const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION;
        const AOverlapped: Boolean = False): TIdStackSocketHandle;
    begin
        Result := WSSocket(IdIPFamily[AIPVersion], ASocketType, AProtocol, 
AOverlapped);
        if Result = Id_INVALID_SOCKET then begin
            RaiseLastSocketError;
        end;
    end;


Gambit 
Re: Error handling in Indy 10
#345
Author: Dennis Cote
Date: Wed, 16 Apr 2008 21:00
72 lines
3660 bytes
Remy Lebeau (TeamB) wrote:
> 
> You are using the TIdICMPClient component, which uses a RAW socket 
> internally (as opposed to using a TCP or UDP socket).  Admin rights are 
> required in order to use RAW sockets on NT4 onwards (which means your code 
> should not have worked on XP, either, unless you are running as an admin). 
> Of course, Microsoft circumvents its own rules by allowing icmp.dll to use 
> RAW sockets without admin rights.
> 

Yes, I am running as an administrator under XP and Vista.

> 
> Rather than casting, you could disable range checking itself for 
> NewSocketHandle().  Indy does this in a couple of other places, for example:
> 
>     function TIdStackBSDBase.NewSocketHandle(const ASocketType: 
> TIdSocketType;
>         const AProtocol: TIdSocketProtocol;
>         const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION;
>         const AOverlapped: Boolean = False): TIdStackSocketHandle;
>     begin
>         {$IFOPT R+}
>         {$DEFINE RestoreRangeCheckingON}
>         {$R-}
>         {$ENDIF}
>         Result := CheckForSocketError(WSSocket(IdIPFamily[AIPVersion], 
> ASocketType, AProtocol, AOverlapped));
>         {$IFDEF RestoreRangeCheckingON}
>         {$R+}
>         {$UNDEF RestoreRangeCheckingON}
>         {$ENDIF}
>     end;
> 

This would also disable the other range check done during the call of 
WSSocket(). I'm don't think that is needed, and it should probably be 
left until it is known to cause a problem.

> 
>> I also suspect this same error may exist in other places in the code.
> 
> Actually, it doesn't.  Everywhere else that calls CheckForSocketError() 
> passes it an Integer.
> 

Good to hear.

> 
> Since CheckForSocketError() is expecting an Integer error code, not a 
> TSocket handle, I would suggest changing TIdStackBSDBase.NewSocketHandle() 
> to the following instead:
> 
>     function TIdStackBSDBase.NewSocketHandle(const ASocketType: 
> TIdSocketType;
>         const AProtocol: TIdSocketProtocol;
>         const AIPVersion: TIdIPVersion = ID_DEFAULT_IP_VERSION;
>         const AOverlapped: Boolean = False): TIdStackSocketHandle;
>     begin
>         Result := WSSocket(IdIPFamily[AIPVersion], ASocketType, AProtocol, 
> AOverlapped);
>         if Result = Id_INVALID_SOCKET then begin
>             RaiseLastSocketError;
>         end;
>     end;
> 

Thanks Remy. I will make this change in my local source file. Will this 
also be changed in the Indy SVN source, or will I have to apply a patch 
to future downloads?

Dennis Cote
Re: Error handling in Indy 10
#346
Author: Remy Lebeau (TeamB)
Date: Wed, 16 Apr 2008 23:08
12 lines
1373 bytes
"Dennis Cote" <denniscote@gmail.com> wrote in message 
news:480668fa$1@newsgroups.borland.com...

> Thanks Remy. I will make this change in my local source file.
> Will this also be changed in the Indy SVN source, or will I
> have to apply a patch to future downloads?

If it works for you, then I will merge it into SVN.


Gambit 
Re: Error handling in Indy 10
#349
Author: Dennis Cote
Date: Thu, 17 Apr 2008 15:16
13 lines
1323 bytes
Remy Lebeau (TeamB) wrote:
> 
> If it works for you, then I will merge it into SVN.
> 

It works for me.

I get correct operation of the ICMP client under XP.

I also now get an Access Denied exception under Vista (as expected) 
rather than the Range Error exception when WSSocket() fails.

Dennis Cote
Thread Navigation

This is a paginated view of messages in the thread with full content displayed inline.

Messages are displayed in chronological order, with the original post highlighted in green.

Use pagination controls to navigate through all messages in large threads.

Back to All Threads