TIdMapped* Issues - Potential Solutions

Giganews Newsgroups
Subject: TIdMapped* Issues - Potential Solutions
Posted by:  Christian Danner (-…@---.---)
Date: Tue, 13 Jun 2006

Hi!

============================================================
I. TIdMappedPortTCP:
--------------------

I fiddled around with TIdMappedPortTCP descendants (current Indy 10
devel snapshot) and continuously experienced transmission abortions,
which were to be traced to

  TIdMappedPortTCP.DoExecute
-> FReadList.SelectReadList(FDataAvailList, IdTimeoutInfinite)
-> TIdSocketListWindows.FDSelect(@LSet, nil, nil, ATimeout)
-> IdWinsock2.Select(0, AReadSet, AWriteSet, AExceptSet, nil)

where for unknown reasons every now and then the Windows Socket's
select function didn't return for about minute or so.

Therefore I rewrote the DoExecute function removing the concerning
call and now it seems to be o.k (tested with http and pop3
transmissions).

Original code:
------------------------------------------------------------
function TIdMappedPortTCP.DoExecute(AContext: TIdContext): Boolean;
begin
  Result := True;
  with TIdMappedPortContext(AContext) do begin
    try
      if FReadList.SelectReadList(FDataAvailList, IdTimeoutInfinite)
then begin
        //1.LConnectionHandle
        if FDataAvailList.Contains((AContext.Connection.IOHandler as
TIdIOHandlerSocket).Binding.Handle) then begin
          // TODO: WSAECONNRESET (Exception [EIdSocketError] Socket
Error # 10054 Connection reset by peer)
          AContext.Connection.IOHandler.CheckForDataOnSource;
          FNetData :=
AContext.Connection.IOHandler.InputBufferAsString;
          if Length(FNetData) > 0 then begin
            DoLocalClientData(AContext);//bServer
            FOutboundClient.IOHandler.Write(FNetData);
          end;
        end;
        //2.LOutBoundHandle
        if FDataAvailList.Contains((FOutboundClient.IOHandler as
TIdIOHandlerSocket).Binding.Handle) then begin
          FOutboundClient.IOHandler.CheckForDataOnSource;
          FNetData := FOutboundClient.IOHandler.InputBufferAsString;
          if Length(FNetData) > 0 then begin
            DoOutboundClientData(AContext);
            AContext.Connection.IOHandler.Write(FNetData);
          end;
        end;
      end;
    finally
      if not FOutboundClient.Connected then begin
        DoOutboundDisconnect(AContext); //&Connection.Disconnect
      end;
    end;
  end;
end;
------------------------------------------------------------
Replacement:
------------------------------------------------------------
function TIdMappedPortTCP.DoExecute(AContext: TIdContext): Boolean;
begin
  Result := True;
  with TIdMappedPortContext(AContext) do begin
    try
      AContext.Connection.IOHandler.CheckForDataOnSource(1);
      if not AContext.Connection.IOHandler.InputBufferIsEmpty
        then
          begin
            FNetData :=
AContext.Connection.IOHandler.InputBufferAsString;
            if Length(FNetData) > 0
              then
                begin
                  DoLocalClientData(AContext);
                  FOutboundClient.IOHandler.Write(FNetData);
                end
          end;
      FOutboundClient.IOHandler.CheckForDataOnSource(1);
      if not FOutboundClient.IOHandler.InputBufferIsEmpty
        then
          begin
            FNetData := FOutboundClient.IOHandler.InputBufferAsString;
            if Length(FNetData) > 0
              then
                begin
                  DoOutboundClientData(AContext);
                  AContext.Connection.IOHandler.Write(FNetData);
                end
          end;
    finally
      if    not FOutboundClient.Connected
        or not AContext.Connection.Connected
        then
          Result := false;
    end;
  end;
end;
------------------------------------------------------------

Are there any objections?

============================================================
II. TIdMappedPOP3:
------------------

Some time ago there was a discussion ...

------------------------------------------------------------
From: "Remy Lebeau \(TeamB\)" <gambit47.no.spam@no.spam.yahoo.com>
Newsgroups: atozedsoftware.indy.protocol.pop3
References: <2E51E4E329A8E240Mw…@localhost.com>
Subject: Re: TIdMappedPop3 examples
Date: Tue, 10 Aug 2004 10:51:42 -0700

"MwnN" <Mw…@localhost.com> wrote in message
news:2E51E4E329A8E240Mw…@localhost.com...

> Every time I try to use it and try to receive mail in my email
> client I get an "EIdException with message 'Host is empty'",
> even though I've set the MappedHost to the remote pop server.

The only way that error can occur is if you did not set the MappedHost
property at all, or if you are clearing the OutboundClient.Host
property in
the OnConnect event.  Those are the only two things that can set the
outbound Host during connecting.
------------------------------------------------------------

That applies to TIdMappedPortTCP, but TIdMappedPOP3 introduces an
'extended' username argument, and that's the reason for the mentioned
problem. TIdMappedPOP3Thread.OutboundConnect() within IdMappedPOP3.pas
explains the misbehaviour described above:

------------------------------------------------------------
procedure TIdMappedPOP3Thread.OutboundConnect;
...
// first assignment of Host / Port
    with TIdTcpClient(FOutboundClient) do begin
      Port := MappedPort;
      Host := MappedHost;
    end;//with
...
// extraction of username / host / port from the 'extended'
// USER argument (like USER username#host:port)
          LHostPort := Sys.TrimLeft(LHostPort); //Sys.TrimRight above
...
// and then in TIdCustomMappedTelnet.ExtractHostAndPortFromLine
// Host and Port are reassigned, no matter whether the 'USER'
// command has an 'extended' argument or not
          ExtractHostAndPortFromLine(SELF,LHostPort);
...
// thus finally the bang
        if Length(TIdTcpClient(FOutboundClient).Host)<1 then begin
          raise EIdException.Create(RSEmptyHost);
        end;
------------------------------------------------------------
procedure TIdCustomMappedTelnet.ExtractHostAndPortFromLine(...);
...
// that's where the reassignment takes place
  TIdTcpClient(AThread.OutboundClient).Host := LHost;
  TIdTcpClient(AThread.OutboundClient).Port :=
Sys.StrToInt(LPort,TIdTcpClient(AThread.OutboundClient).Port);
------------------------------------------------------------

So in ExtractHostAndPortFromLine a reassignment of Host and Port
values should only be done, if it's certain that an 'extended'
argument was transmitted.

Though parsing for the predefined separator characters (#0, #9, ' '
and ':') seems to be secure enough in order to make such a decision,
maybe a property for a general de/activation of this 'extended USER
argument' feature might be helpful too.

Regards

Christian Danner @ ImhoTec

--
OmniMix .. protect your privacy
http://www.danner-net.de/om.htm

Replies