TidTcpserver验证命令列表

时间:2017-04-22 13:23:57

标签: indy indy10 delphi-10-seattle

我试图保护我的Tidtcpserver免受未知命令的侵害

这是我的验证命令功能的样子

 function TConnection.Verfieycmds(const CMSTOV: String): BOOLEAN;
    var
    CMDSTOVERFIYE : Tstringlist;
    I : integer;
    CommandFound : Boolean;
    begin

    Result := False;
    CommandFound := False;

    if Commandlist <> nil then
    begin

    CMDSTOVERFIYE := Commandlist.Lock;
    try

    for I := 0 to CMDSTOVERFIYE.Count - 1 do
    begin
    if CMSTOV = CMDSTOVERFIYE[I] then
    begin
    CommandFound := True;
    end;
    end;

    CommandFound := True;
    Result :=  CommandFound;

    finally
    Commandlist.Unlock;
    end;
    end;



    end;

在执行事件上添加此检查后,在少数客户端连接服务器应用程序后冻结并需要重新启动并且异常日志为空

这是我的服务器代码

type
  TConnection = class(TIdServerContext)
  private
  {Private}

  public
  {Public}
    OutboundCache: TIdThreadSafeStringList;
    Commandlist: TIdThreadSafeStringList;
    LastSendRecv: TIdTicks;
    Name: String;
    constructor Create(AConnection: TIdTCPConnection; AYarn: TIdYarn;
      AList: TIdContextThreadList = nil); override;
    destructor Destroy; override;


  end;

type
  TServobj = class(TForm)
    TcpServer: TIdTCPServer;
    Panel1: TPanel;
    Edit1: TEdit;
    Button1: TButton;
    Button2: TButton;
    procedure TcpServerConnect(AContext: TIdContext);
    procedure TcpServerDisconnect(AContext: TIdContext);
    procedure TcpServerExecute(AContext: TIdContext);
    procedure FormCloseQuery(Sender: TObject; var CanClose: BOOLEAN);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure TcpServerListenException(AThread: TIdListenerThread;
      AException: Exception);
  private
    { Private declarations }
    LastUniqueID: Dword;
    procedure HandleExceptions(Sender: TObject; E: Exception);
    procedure UpdateBindings;

  public
    { Public declarations }

  end;

var
  Servobj: TServobj;

implementation

uses
  dmoudle;

{$R *.dfm}

constructor TConnection.Create(AConnection: TIdTCPConnection; AYarn: TIdYarn;
  AList: TIdContextThreadList = nil);
begin
  inherited;
  OutboundCache := TIdThreadSafeStringList.Create;
  Commandlist := TIdThreadSafeStringList.Create;
  Commandlist.Add('Command1');
  Commandlist.Add('Command2');
  Commandlist.Add('Command3');
  Commandlist.Add('Command4');
  Commandlist.Add('Command5');
  Commandlist.Add('Command6');
  Commandlist.Add('Command7');
  Commandlist.Add('Command8');
  Commandlist.Add('Command9');
  Commandlist.Add('Command10');
  Commandlist.Add('Command11');
  Commandlist.Add('Command12');

  end;




destructor TConnection.Destroy;
var
  Cache: TStringList;
  Commadcaches : TStringList;
  I: integer;
begin

  if OutboundCache <> nil then
  begin
    Cache := OutboundCache.Lock;
    try
      for I := 0 to Cache.Count - 1 do
        Cache.Objects[I].Free;
    finally
      OutboundCache.Unlock;
    end;
    OutboundCache.Free;
  end;


    if Commandlist <> nil then
  begin
    Commadcaches := Commandlist.Lock;
    try
      for I := 0 to Commadcaches.Count - 1 do
        Commadcaches.Objects[I].Free;
    finally
      Commandlist.Unlock;
    end;
    Commandlist.Free;
  end;





  inherited;
end;

procedure TServobj.TcpServerExecute(AContext: TIdContext);
var
  Connection: TConnection;
  Command: String;
  Startercommand : String;
  Params: array [1 .. 200] of String;
  Cache, OutboundCmds: TStringList;
  ParamsCount, P: integer;
  I: integer;
  S: String;
  DECODES : String;
  UConnected : Boolean;
  Len: Integer;
begin



Try
UConnected := AContext.Connection.Connected;
Except
UConnected := False;
End;

If Not UConnected Then
begin
AContext.Connection.Disconnect;
exit;
end;

Len := AContext.Connection.IOHandler.InputBuffer.Size;


If Len >= 200000 then
begin
AContext.Connection.Disconnect;
exit;

end;

Connection := AContext as TConnection;



// check for pending outbound commands...
  OutboundCmds := nil;
  try
    Cache := Connection.OutboundCache.Lock;
    try
      if Cache.Count > 0 then
      begin
        OutboundCmds := TStringList.Create;
        OutboundCmds.Assign(Cache);
        Cache.Clear;
      end;
    finally
      Connection.OutboundCache.Unlock;
    end;

    if OutboundCmds <> nil then
    begin
      for I := 0 to OutboundCmds.Count - 1 do
      begin
        AContext.Connection.IOHandler.Writeln(OutboundCmds.Strings[I],
          IndyTextEncoding_UTF8);
        MS := TMemoryStream(OutboundCmds.Objects[I]);
        if MS <> nil then
        begin
          AContext.Connection.IOHandler.DefStringEncoding := IndyTextEncoding_UTF8;
          AContext.Connection.IOHandler.LargeStream := true;
          AContext.Connection.IOHandler.Write(MS, 0, true);
        end;
      end;
      Connection.LastSendRecv := Ticks64;
    end;




  finally
    if OutboundCmds <> nil then
    begin
      for I := 0 to OutboundCmds.Count - 1 do
      begin
        OutboundCmds.Objects[I].Free;
      end;
    end;
    OutboundCmds.Free;
  end;

  // check for a pending inbound command...
  if AContext.Connection.IOHandler.InputBufferIsEmpty then
  begin
    AContext.Connection.IOHandler.CheckForDataOnSource(100);
    AContext.Connection.IOHandler.CheckForDisconnect;
    if AContext.Connection.IOHandler.InputBufferIsEmpty then
    begin
    if GetElapsedTicks(Connection.LastSendRecv) >= 60000 then
     AContext.Connection.Disconnect;
     Exit;
    end;
  end;



Startercommand := Decode64(AContext.Connection.Socket.ReadLn(IndyTextEncoding_UTF8), IndyTextEncoding_UTF8);
Command := Startercommand;

{HERE I START TO CHECK COMMAND LIST}
if (command <> 'ISACTIVE') then
begin

if Connection.Verfieycmds(Command) <> true then
begin
AContext.Connection.Disconnect;
Exit;
end;

end;
{HERE I START TO CHECK COMMAND LIST}

Connection.LastSendRecv := Ticks64;


if Command = '' then
begin
AContext.Connection.Disconnect;
Exit;
end;




  ReceiveParams := False;
  ReceiveStream := False;

  if Command[1] = '1' then // command with params
  begin
    Command := Copy(Command, 2, MaxInt);
    ReceiveParams := true;
  end
  else if Command[1] = '2' then // command + memorystream
  begin
    Command := Copy(Command, 2, MaxInt);
    ReceiveStream := true;
  end
  else if Command[1] = '3' then // command with params + memorystream
  begin
    Command := Copy(Command, 2, MaxInt);
    ReceiveParams := true;
    ReceiveStream := true;
  end;

  if ReceiveParams then // params is incomming
  begin
    S := AContext.Connection.Socket.ReadLn(IndyTextEncoding_UTF8);
    DECODES := Decode64(S, IndyTextEncoding_UTF8);

    ParamsCount := 0;
    while (DECODES <> '') and (ParamsCount < 200) do
    begin
      Inc(ParamsCount);
      P := Pos(Sep, DECODES);
      if P = 0 then
        Params[ParamsCount] := DECODES
      else
      begin
        Params[ParamsCount] := Copy(DECODES, 1, P - 1);
        Delete(DECODES, 1, P + 5);
      end;
    end;
  end;



if Command = 'Broadcastanymessage' then
begin
if ParamsCount <> 3 then
begin
AContext.Connection.Disconnect;
Exit;
end;
//do something

end;

end;

如果我从执行中删除Verfieycmds,请检查服务器是否正常运行。我做错了什么?

1 个答案:

答案 0 :(得分:1)

没有理由使用TIdThreadSafeStringList作为命令列表。只有创建列表的线程才能访问它,因此使用锁定是不必要的开销。

就此而言,没有理由为每个客户分配新的列表。那只是浪费记忆。

您的命令以需要解码的方式进行编码,然后才能验证它们。

尝试更像这样的东西:

type
  TConnection = class(TIdServerContext)
  private
    function HasInboundData: Boolean;
    procedure SendOutboundCache;
  public
    OutboundCache: TIdThreadSafeStringList;
    LastSendRecv: TIdTicks;
    // ...
    constructor Create(AConnection: TIdTCPConnection; AYarn: TIdYarn; AList: TIdContextThreadList = nil); override;
    destructor Destroy; override;
  end;

type
  TServobj = class(TForm)
    TcpServer: TIdTCPServer;
    //...
    procedure TcpServerConnect(AContext: TIdContext);
    //...
    procedure TcpServerExecute(AContext: TIdContext);
    procedure FormCreate(Sender: TObject);
    //...
  private
    //...
  end;

var
  Servobj: TServobj;

implementation

uses
  dmoudle;

{$R *.dfm}

constructor TConnection.Create(AConnection: TIdTCPConnection; AYarn: TIdYarn; AList: TIdContextThreadList = nil);
begin
  inherited;
  OutboundCache := TIdThreadSafeStringList.Create; 
  LastSendRecv := Ticks64;
end;

destructor TConnection.Destroy;
var
  Cache: TStringList;
  I: integer;
begin
  if OutboundCache <> nil then
  begin
    Cache := OutboundCache.Lock;
    try
      for I := 0 to Cache.Count - 1 do
        Cache.Objects[I].Free;
    finally
      OutboundCache.Unlock;
    end;
    OutboundCache.Free;
  end;
  inherited;
end;

function TConnection.HasInboundData: Boolean;
begin
  if Connection.IOHandler.InputBufferIsEmpty then
  begin
    Connection.IOHandler.CheckForDataOnSource(100);
    Connection.IOHandler.CheckForDisconnect;
    if Connection.IOHandler.InputBufferIsEmpty then
    begin
      if GetElapsedTicks(LastSendRecv) >= 60000 then
        Connection.Disconnect;

      Result := False;
      Exit;
    end;
  end;

  Result := True;
end;

procedure TConnection.SendOutboundCache;
var
  Cache, OutboundCmds: TStringList;
  MS: TMemoryStream;
  I: integer;
begin
  OutboundCmds := nil;
  try
    Cache := OutboundCache.Lock;
    try
      if Cache.Count = 0 then
        Exit;
      OutboundCmds := TStringList.Create;
      OutboundCmds.Assign(Cache);
      Cache.Clear;
    finally
      OutboundCache.Unlock;
    end;

    for I := 0 to OutboundCmds.Count - 1 do
    begin
      Connection.IOHandler.WriteLn(OutboundCmds.Strings[I]);
      MS := TMemoryStream(OutboundCmds.Objects[I]);
      if MS <> nil then
      begin
        Connection.IOHandler.LargeStream := true;
        Connection.IOHandler.Write(MS, 0, true);
      end;
    end;
    LastSendRecv := Ticks64;
  finally
    if OutboundCmds <> nil then
    begin
      for I := 0 to OutboundCmds.Count - 1 do
      begin
        OutboundCmds.Objects[I].Free;
      end;
    end;
    OutboundCmds.Free;
  end;
end;

procedure TServobj.FormCreate(Sender: TObject);
begin
  TcpServer.ContextClass := TConnection;
end;

procedure TServobj.TcpServerConnect(AContext: TIdContext);
begin
  AContext.Connection.IOHandler.DefStringEncoding := IndyTextEncoding_UTF8
end;

const
  ValidCmds: array[0..13] of String = (
    'ISACTIVE',
    'Broadcastanymessage',
    'Command1',
    'Command2',
    'Command3',
    'Command4',
    'Command5',
    'Command6',
    'Command7',
    'Command8',
    'Command9',
    'Command10',
    'Command11',
    'Command12'
  ); 

procedure TServobj.TcpServerExecute(AContext: TIdContext);
var
  Connection: TConnection;
  Command, Decoded: String;
  Params: array[1..200] of String;
  ParamsCount, P, I, WhichCmd: integer;
begin
  Connection := AContext as TConnection;

  // check for pending outbound commands...

  Connection.SendOutboundCache;

  // check for a pending inbound command...

  if not Connection.HasInboundData then
    Exit;

  Command := Decode64(AContext.Connection.IOHandler.ReadLn, IndyTextEncoding_UTF8);

  ReceiveParams := False;
  ReceiveStream := False;

  if Command <> '' then
  begin
    if Command[1] = '1' then // command with params
    begin
      Delete(Command, 1, 1);
      ReceiveParams := true;
    end
    else if Command[1] = '2' then // command + memorystream
    begin
      Delete(Command, 1, 1);
      ReceiveStream := true;
    end
    else if Command[1] = '3' then // command with params + memorystream
    begin
      Delete(Command, 1, 1);
      ReceiveParams := true;
      ReceiveStream := true;
    end;
  end;

  WhichCmd := PosInStrArray(Command, ValidCmds);
  if WhichCmd = -1 then
  begin
    AContext.Connection.Disconnect;
    Exit;
  end;

  if ReceiveParams then // params is incomming
  begin
    Decoded := Decode64(AContext.Connection.IOHandler.ReadLn, IndyTextEncoding_UTF8);
    ParamsCount := 0;
    while (Decoded <> '') and (ParamsCount < 200) do
    begin
      Inc(ParamsCount);
      P := Pos(Sep, Decoded);
      if P = 0 then
        Params[ParamsCount] := Decoded
      else
      begin
        Params[ParamsCount] := Copy(Decoded, 1, P - 1);
        Delete(Decoded, 1, P + Length(Sep));
      end;
    end;
  end;

  Connection.LastSendRecv := Ticks64;

  case WhichCmd of
    // process commands as needed...

    1: begin // Broadcastanymessage
      if ParamsCount <> 3 then
      begin
        AContext.Connection.Disconnect;
        Exit;
      end;

      //do something
     end;

    // ...
  end;
end;