Hi,

Some weeks ago i implemented TLS capability in my SMTP-Server component.
Every thing works perfekt for the first weeks, but suddenly i noticed that the server process use 100% CPU. I restarted the process and it works again for about 2 days, then it goes to 100% again.

After hours of debuging i found out that it was always the same Server in the Internet which cause the Problem.
In my logs i saw the following SMTP Command sequence:

Receive  > EHLO O026.server.lu
Response < 250-OK
Response < 250-STARTTLS
Response < 250-AUTH PLAIN LOGIN
Response < 250 SIZE 104857600
Receive  > STARTTLS
Response < 220 Ready to start TLS
Receive  > EHLO O026.server.lu
Response < 250-OK
Response < 250-AUTH PLAIN LOGIN
Response < 250 SIZE 104857600
Receive  > MAIL FROM:<[email protected]> SIZE=3318
Response < 250 OK smtp ready for [email protected]
Receive  > RCPT TO:<[email protected]>
Response < 554 Recipient unknown
Receive  > RSET
Response < 250 OK - Reset

After that the CPU utilization goes to 100%. No Disconnect and no Socket Error !?

Because i could not found anything in my source code, i made a tcpdump of this connection. The Server is an active Spammer so it takes only a couple of hours until it cames again ;-)

After a look into the tcpdump i saw that the server sends a QUIT after the RSET, but
it shutdown the SSL before it sends the QUIT !!!

Of course, i know that this behavior is not correct. But it cause the TBlockSocket.RecvTerminated method
to go in an endless loop! So we have a simple process to make a DoS.

I wrote a test client to reproduce this behavior by my self, and made an fix for this condition. *I would like to discuss this fix here. Maybe there is a better way to handle this condition. Maybe it's better to handle the condition in the RecvBuffer method of the SSL Plugin.*
(Btw. i use openssl with freepascal and Linux)

-------------------------------------------------------------------------------
function TBlockSocket.RecvPacket(Timeout: Integer): AnsiString; // Current SVN Version => 009.006.000
var
 x: integer;
{$IFDEF CIL}
 buf: TMemory;
{$ENDIF}
begin
 Result := '';
 ResetLastError;
 if FBuffer <> '' then
 begin
   Result := FBuffer;
   FBuffer := '';
 end
 else
 begin
   {$IFDEF WIN32}
   //not drain CPU on large downloads...
   Sleep(0);
   {$ENDIF}
   x := WaitingData;
   if x > 0 then
   begin
     {$IFDEF CIL}
     SetLength(Buf, x);
     x := RecvBuffer(Buf, x);
     if x >= 0 then
     begin
       SetLength(Buf, x);
       Result := StringOf(Buf);
     end;
     {$ELSE}
     SetLength(Result, x);
     *// Here is the main problem. After the Client drops SSL
**      // **RecvBuffer will return always 0 and not set an Error Code
     // also RecvBuffer did not empty the Buffer, so the WaitingData
     // method will always see the same Data
// In my case WaitingData always announce 16 Bytes and RecvBuffer returns 0 => forever!!!
*      x := RecvBuffer(Pointer(Result), x);
if x >= 0 then SetLength(Result, x);
     {$ENDIF}

*      // Added this to announce an connection error
     if x = 0 then
       FLastError := WSAECONNRESET;
*    end
   else
   begin
     if CanRead(Timeout) then
     begin
       x := WaitingData;
       if x = 0 then
         FLastError := WSAECONNRESET;
       if x > 0 then
       begin
         {$IFDEF CIL}
         SetLength(Buf, x);
         x := RecvBuffer(Buf, x);
         if x >= 0 then
         begin
           SetLength(Buf, x);
           result := StringOf(Buf);
         end;
         {$ELSE}
         SetLength(Result, x);
         x := RecvBuffer(Pointer(Result), x);
         if x >= 0 then
           SetLength(Result, x);
         {$ENDIF}

*          // Added this to announce an connection error
         if x = 0 then
           FLastError := WSAECONNRESET;
*        end;
     end
     else
       FLastError := WSAETIMEDOUT;
   end;
 end;
 if FConvertLineEnd and (Result <> '') then
 begin
   if FLastCR and (Result[1] = LF) then
     Delete(Result, 1, 1);
   if FLastLF and (Result[1] = CR) then
     Delete(Result, 1, 1);
   FLastCR := False;
   FLastLF := False;
 end;
 ExceptCheck;
end;
-------------------------------------------------------------------------------

If you want to reproduce that, the do the following on Client side:

1. Make a normal connection
2. Start TLS with: *Socket.SSLDoConnect;*
3. Send some data. Anything should be ok so far.
4. Shutdown SSL on client side with : *Socket.SSl.Shutdown;
*5. Send some data. Now the Server connection goes in the endless loop.

Alex
------------------------------------------------------------------------------
Enter the BlackBerry Developer Challenge  
This is your chance to win up to $100,000 in prizes! For a limited time, 
vendors submitting new applications to BlackBerry App World(TM) will have
the opportunity to enter the BlackBerry Developer Challenge. See full prize  
details at: http://p.sf.net/sfu/Challenge
_______________________________________________
synalist-public mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/synalist-public

Reply via email to