mirror of
https://gitlab.com/freepascal.org/fpc/source.git
synced 2025-08-18 07:49:12 +02:00
* ltelnet subcommand first character improvements. Mantis #27273
git-svn-id: trunk@29466 -
This commit is contained in:
parent
b21d30b3aa
commit
d47aae933c
@ -37,15 +37,18 @@ type
|
|||||||
private
|
private
|
||||||
FItems: array of Char;
|
FItems: array of Char;
|
||||||
FIndex: Byte;
|
FIndex: Byte;
|
||||||
|
FAllowInflation: Boolean;
|
||||||
FOnFull: TLOnFull;
|
FOnFull: TLOnFull;
|
||||||
function GetFull: Boolean;
|
function GetFull: Boolean;
|
||||||
function GetItem(const i: Byte): Char;
|
function GetItem(const i: Byte): Char;
|
||||||
procedure SetItem(const i: Byte; const Value: Char);
|
procedure SetItem(const i: Byte; const Value: Char);
|
||||||
|
procedure SetAllowInflation(const b: boolean);
|
||||||
public
|
public
|
||||||
constructor Create;
|
constructor Create;
|
||||||
procedure Clear;
|
procedure Clear;
|
||||||
procedure Push(const Value: Char);
|
procedure Push(const Value: Char);
|
||||||
property ItemIndex: Byte read FIndex;
|
property ItemIndex: Byte read FIndex;
|
||||||
|
property AllowInflation: Boolean read FAllowInflation write SetAllowInflation;
|
||||||
property Items[i: Byte]: Char read GetItem write SetItem; default;
|
property Items[i: Byte]: Char read GetItem write SetItem; default;
|
||||||
property Full: Boolean read GetFull;
|
property Full: Boolean read GetFull;
|
||||||
property OnFull: TLOnFull read FOnFull write FOnFull;
|
property OnFull: TLOnFull read FOnFull write FOnFull;
|
||||||
@ -56,46 +59,79 @@ implementation
|
|||||||
uses
|
uses
|
||||||
lTelnet;
|
lTelnet;
|
||||||
|
|
||||||
|
(* The normal situation is that there are up to TL_CSLENGTH items on the stack. *)
|
||||||
|
(* However this may be relaxed in cases (assumed to be rare) where subcommand *)
|
||||||
|
(* parameters are being accumulated. *)
|
||||||
|
|
||||||
constructor TLControlStack.Create;
|
constructor TLControlStack.Create;
|
||||||
begin
|
begin
|
||||||
FOnFull:=nil;
|
FOnFull:=nil;
|
||||||
FIndex:=0;
|
FIndex:=0; (* Next insertion point, [0] when empty *)
|
||||||
|
FAllowInflation := false;
|
||||||
SetLength(FItems, TL_CSLENGTH);
|
SetLength(FItems, TL_CSLENGTH);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TLControlStack.GetFull: Boolean;
|
function TLControlStack.GetFull: Boolean;
|
||||||
begin
|
begin
|
||||||
Result:=False;
|
Result:=False; (* It's full when it has a complete *)
|
||||||
if FIndex >= TL_CSLENGTH then
|
if FIndex >= TL_CSLENGTH then (* command, irrespective of whether the *)
|
||||||
Result:=True;
|
Result:=True; (* stack's inflated by a subcommand. *)
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function TLControlStack.GetItem(const i: Byte): Char;
|
function TLControlStack.GetItem(const i: Byte): Char;
|
||||||
begin
|
begin
|
||||||
Result:=TS_NOP;
|
Result:=TS_NOP;
|
||||||
if i < TL_CSLENGTH then
|
if not FAllowInflation then begin
|
||||||
Result:=FItems[i];
|
if i < TL_CSLENGTH then
|
||||||
|
Result:=FItems[i]
|
||||||
|
end else
|
||||||
|
if i < Length(FItems) then
|
||||||
|
Result:=FItems[i]
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TLControlStack.SetItem(const i: Byte; const Value: Char);
|
procedure TLControlStack.SetItem(const i: Byte; const Value: Char);
|
||||||
begin
|
begin
|
||||||
if i < TL_CSLENGTH then
|
if not FAllowInflation then begin
|
||||||
FItems[i]:=Value;
|
if i < TL_CSLENGTH then
|
||||||
|
FItems[i]:=Value
|
||||||
|
end else begin
|
||||||
|
while i >= Length(FItems) do begin
|
||||||
|
SetLength(FItems, Length(FItems) + 1);
|
||||||
|
FItems[Length(FItems) - 1] := TS_NOP
|
||||||
|
end;
|
||||||
|
FItems[i] := Value
|
||||||
|
end
|
||||||
|
end;
|
||||||
|
|
||||||
|
procedure TLControlStack.SetAllowInflation(const b: boolean);
|
||||||
|
|
||||||
|
begin
|
||||||
|
FAllowInflation := b;
|
||||||
|
if not b then (* No more funny stuff please *)
|
||||||
|
Clear
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TLControlStack.Clear;
|
procedure TLControlStack.Clear;
|
||||||
begin
|
begin
|
||||||
FIndex:=0;
|
FIndex:=0;
|
||||||
|
FAllowInflation := false;
|
||||||
|
SetLength(FItems, TL_CSLENGTH) (* In case inflation was allowed *)
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TLControlStack.Push(const Value: Char);
|
procedure TLControlStack.Push(const Value: Char);
|
||||||
begin
|
begin
|
||||||
if FIndex < TL_CSLENGTH then begin
|
if not FAllowInflation then
|
||||||
FItems[FIndex]:=Value;
|
if FIndex < TL_CSLENGTH then begin
|
||||||
Inc(FIndex);
|
FItems[FIndex]:=Value;
|
||||||
if Full and Assigned(FOnFull) then
|
Inc(FIndex)
|
||||||
FOnFull;
|
end else begin end
|
||||||
|
else begin
|
||||||
|
SetLength(FItems, Length(FItems) + 1);
|
||||||
|
FItems[Length(FItems) - 1] := Value;
|
||||||
|
FIndex := Length(FItems)
|
||||||
end;
|
end;
|
||||||
|
if Full and Assigned(FOnFull) then
|
||||||
|
FOnFull;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
@ -27,7 +27,7 @@ unit lTelnet;
|
|||||||
interface
|
interface
|
||||||
|
|
||||||
uses
|
uses
|
||||||
Classes, lNet, lControlStack;
|
Classes, SysUtils, lNet, lControlStack;
|
||||||
|
|
||||||
const
|
const
|
||||||
// Telnet printer signals
|
// Telnet printer signals
|
||||||
@ -72,9 +72,11 @@ type
|
|||||||
TLSubcommandCallback= function(command: char; const parameters, defaultResponse: string): string;
|
TLSubcommandCallback= function(command: char; const parameters, defaultResponse: string): string;
|
||||||
TLSubcommandEntry= record
|
TLSubcommandEntry= record
|
||||||
callback: TLSubcommandCallback;
|
callback: TLSubcommandCallback;
|
||||||
defaultResponse: string
|
defaultResponse: string;
|
||||||
|
requiredParams: integer
|
||||||
end;
|
end;
|
||||||
TLSubcommandArray= array[#$00..#$ff] of TLSubcommandEntry;
|
TLSubcommandArray= array[#$00..#$ff] of TLSubcommandEntry;
|
||||||
|
EInsufficientSubcommandParameters= class(Exception);
|
||||||
|
|
||||||
{ TLTelnet }
|
{ TLTelnet }
|
||||||
|
|
||||||
@ -117,7 +119,7 @@ type
|
|||||||
procedure StackFull;
|
procedure StackFull;
|
||||||
procedure DoubleIAC(var s: string);
|
procedure DoubleIAC(var s: string);
|
||||||
function TelnetParse(const msg: string): Integer;
|
function TelnetParse(const msg: string): Integer;
|
||||||
procedure React(const Operation, Command: Char); virtual; abstract;
|
function React(const Operation, Command: Char): boolean; virtual; abstract;
|
||||||
procedure SendCommand(const Command: Char; const Value: Boolean); virtual; abstract;
|
procedure SendCommand(const Command: Char; const Value: Boolean); virtual; abstract;
|
||||||
|
|
||||||
procedure OnCs(aSocket: TLSocket);
|
procedure OnCs(aSocket: TLSocket);
|
||||||
@ -136,7 +138,8 @@ type
|
|||||||
procedure SetOption(const Option: Char);
|
procedure SetOption(const Option: Char);
|
||||||
procedure UnSetOption(const Option: Char);
|
procedure UnSetOption(const Option: Char);
|
||||||
|
|
||||||
function RegisterSubcommand(aOption: char; callback: TLSubcommandCallback; const defaultResponse: string= ''): boolean;
|
function RegisterSubcommand(aOption: char; callback: TLSubcommandCallback;
|
||||||
|
const defaultResponse: string= ''; requiredParams: integer= 0): boolean;
|
||||||
|
|
||||||
procedure Disconnect(const Forced: Boolean = True); override;
|
procedure Disconnect(const Forced: Boolean = True); override;
|
||||||
|
|
||||||
@ -164,7 +167,7 @@ type
|
|||||||
procedure OnRe(aSocket: TLSocket);
|
procedure OnRe(aSocket: TLSocket);
|
||||||
procedure OnCo(aSocket: TLSocket);
|
procedure OnCo(aSocket: TLSocket);
|
||||||
|
|
||||||
procedure React(const Operation, Command: Char); override;
|
function React(const Operation, Command: Char): boolean; override;
|
||||||
|
|
||||||
procedure SendCommand(const Command: Char; const Value: Boolean); override;
|
procedure SendCommand(const Command: Char; const Value: Boolean); override;
|
||||||
public
|
public
|
||||||
@ -190,7 +193,9 @@ function LTelnetSubcommandCallback(command: char; const parameters, defaultRespo
|
|||||||
implementation
|
implementation
|
||||||
|
|
||||||
uses
|
uses
|
||||||
SysUtils, Math;
|
Math;
|
||||||
|
|
||||||
|
const subcommandEndLength= 2;
|
||||||
|
|
||||||
var
|
var
|
||||||
zz: Char;
|
zz: Char;
|
||||||
@ -306,8 +311,10 @@ begin
|
|||||||
begin
|
begin
|
||||||
FOutput.WriteByte(Byte(FStack[1]));
|
FOutput.WriteByte(Byte(FStack[1]));
|
||||||
FOutput.WriteByte(Byte(FStack[2]));
|
FOutput.WriteByte(Byte(FStack[2]));
|
||||||
end else React(FStack[1], FStack[2]);
|
FStack.Clear
|
||||||
FStack.Clear;
|
end else
|
||||||
|
if React(FStack[1], FStack[2]) then
|
||||||
|
FStack.Clear
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TLTelnet.DoubleIAC(var s: string);
|
procedure TLTelnet.DoubleIAC(var s: string);
|
||||||
@ -394,15 +401,22 @@ end;
|
|||||||
|
|
||||||
(* If already set, the callback can be reverted to nil but it can't be changed *)
|
(* If already set, the callback can be reverted to nil but it can't be changed *)
|
||||||
(* in a single step. The default response, if specified, is used by the *)
|
(* in a single step. The default response, if specified, is used by the *)
|
||||||
(* LTelnetSubcommandCallback() function and is available to others. *)
|
(* LTelnetSubcommandCallback() function and is available to others; the *)
|
||||||
|
(* callback will not be invoked until there is at least the indicated number of *)
|
||||||
|
(* parameter bytes available. *)
|
||||||
//
|
//
|
||||||
function TLTelnet.RegisterSubcommand(aOption: char; callback: TLSubcommandCallback; const defaultResponse: string= ''): boolean;
|
function TLTelnet.RegisterSubcommand(aOption: char; callback: TLSubcommandCallback;
|
||||||
|
const defaultResponse: string= ''; requiredParams: integer= 0): boolean;
|
||||||
|
|
||||||
begin
|
begin
|
||||||
result := (not Assigned(FSubcommandCallbacks[aOption].callback)) or (@callback = nil);
|
result := (not Assigned(FSubcommandCallbacks[aOption].callback)) or (@callback = nil);
|
||||||
if result then begin
|
if result then begin
|
||||||
FSubcommandCallbacks[aOption].callback := callback;
|
FSubcommandCallbacks[aOption].callback := callback;
|
||||||
FSubcommandCallbacks[aOption].defaultResponse := defaultResponse
|
FSubcommandCallbacks[aOption].defaultResponse := defaultResponse;
|
||||||
|
Inc(requiredParams, subcommandEndLength);
|
||||||
|
if requiredParams < 0 then (* Assume -subcommandEndLength is a *)
|
||||||
|
requiredParams := 0; (* valid parameter. *)
|
||||||
|
FSubcommandCallbacks[aOption].requiredParams := requiredParams;
|
||||||
end
|
end
|
||||||
end { TLTelnet.RegisterSubcommand } ;
|
end { TLTelnet.RegisterSubcommand } ;
|
||||||
|
|
||||||
@ -464,7 +478,7 @@ begin
|
|||||||
FOnConnect(aSocket);
|
FOnConnect(aSocket);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TLTelnetClient.React(const Operation, Command: Char);
|
function TLTelnetClient.React(const Operation, Command: Char): boolean;
|
||||||
|
|
||||||
procedure Accept(const Operation, Command: Char);
|
procedure Accept(const Operation, Command: Char);
|
||||||
begin
|
begin
|
||||||
@ -487,17 +501,28 @@ procedure TLTelnetClient.React(const Operation, Command: Char);
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
(* Retrieve the parameters from the current instance, and pass them explicitly *)
|
(* Retrieve the parameters from the current instance, and pass them explicitly *)
|
||||||
(* to the callback. *)
|
(* to the callback. Return false if there are insufficient parameters on the *)
|
||||||
|
(* stack. *)
|
||||||
//
|
//
|
||||||
procedure subcommand(command: char);
|
function subcommand(command: char): boolean;
|
||||||
|
|
||||||
var parameters, response: string;
|
var parameters, response: string;
|
||||||
i: integer;
|
i: integer;
|
||||||
|
|
||||||
begin
|
begin
|
||||||
if FStack.ItemIndex > 5 then begin
|
FStack.AllowInflation := true; (* We might need more than the standard *)
|
||||||
SetLength(parameters, FStack.ItemIndex - 5);
|
if FStack.ItemIndex > 65536 then (* command, but protect against parse *)
|
||||||
Move(FStack[3], parameters[1], FStack.ItemIndex - 5);
|
{%H- 6018 } exit(true); (* failure which could be a DoS attack. *)
|
||||||
|
i := FStack.ItemIndex - TL_CSLENGTH; (* Number of parameter bytes available.*)
|
||||||
|
if i < FSubcommandCallbacks[command].requiredParams then
|
||||||
|
exit(false); (* Early insufficient-parameters decision *)
|
||||||
|
result := true;
|
||||||
|
if FStack.ItemIndex > TL_CSLENGTH then begin
|
||||||
|
SetLength(parameters, FStack.ItemIndex - TL_CSLENGTH );
|
||||||
|
Move(FStack[3], parameters[1], FStack.ItemIndex - TL_CSLENGTH );
|
||||||
|
if (Length(parameters) >= 2) and (parameters[Length(parameters)] = TS_IAC) and
|
||||||
|
(parameters[Length(parameters) - 1] <> TS_IAC) then
|
||||||
|
exit(false); (* Special case: need at least one more *)
|
||||||
i := 1;
|
i := 1;
|
||||||
while i <= Length(parameters) - 1 do (* Undouble IACs *)
|
while i <= Length(parameters) - 1 do (* Undouble IACs *)
|
||||||
if (parameters[i] = TS_IAC) and (parameters[i + 1] = TS_IAC) then
|
if (parameters[i] = TS_IAC) and (parameters[i + 1] = TS_IAC) then
|
||||||
@ -506,13 +531,27 @@ procedure TLTelnetClient.React(const Operation, Command: Char);
|
|||||||
Inc(i)
|
Inc(i)
|
||||||
end else
|
end else
|
||||||
parameters := '';
|
parameters := '';
|
||||||
response := FSubcommandCallbacks[command].callback(command, parameters, FSubcommandCallbacks[command].defaultResponse);
|
if Length(parameters) < FSubcommandCallbacks[command].requiredParams then
|
||||||
|
exit(false); (* Insufficient params after IAC undouble *)
|
||||||
|
if (FSubcommandCallbacks[command].requiredParams >= subcommandEndLength) and
|
||||||
|
(Length(parameters) >= subcommandEndLength) then
|
||||||
|
SetLength(parameters, Length(parameters) - subcommandEndLength);
|
||||||
|
try
|
||||||
|
response := FSubcommandCallbacks[command].callback(command, parameters,
|
||||||
|
FSubcommandCallbacks[command].defaultResponse)
|
||||||
|
except
|
||||||
|
on e: EInsufficientSubcommandParameters do
|
||||||
|
Exit(false) (* Late insufficient-parameters decision *)
|
||||||
|
else
|
||||||
|
Raise (* Application-specific error *)
|
||||||
|
end;
|
||||||
DoubleIAC(response);
|
DoubleIAC(response);
|
||||||
AddToBuffer(TS_IAC + TS_SB + command + response + TS_IAC + TS_SE);
|
AddToBuffer(TS_IAC + TS_SB + command + response + TS_IAC + TS_SE);
|
||||||
OnCs(nil)
|
OnCs(nil)
|
||||||
end { subcommand } ;
|
end { subcommand } ;
|
||||||
|
|
||||||
begin
|
begin
|
||||||
|
result := true; (* Stack will normally be cleared *)
|
||||||
{$ifdef debug}
|
{$ifdef debug}
|
||||||
Writeln('**GOT** ', TNames[Operation], ' ', TNames[Command]);
|
Writeln('**GOT** ', TNames[Operation], ' ', TNames[Command]);
|
||||||
{$endif}
|
{$endif}
|
||||||
@ -529,7 +568,12 @@ begin
|
|||||||
TS_SB : if not Assigned(FSubcommandCallbacks[command].callback) then
|
TS_SB : if not Assigned(FSubcommandCallbacks[command].callback) then
|
||||||
refuse(TS_WONT, command)
|
refuse(TS_WONT, command)
|
||||||
else
|
else
|
||||||
subcommand(command)
|
result := subcommand(command)
|
||||||
|
|
||||||
|
(* In the final case above, the stack will not be cleared if sufficient *)
|
||||||
|
(* parameters to keep the subcommand happy have not yet been parsed out of the *)
|
||||||
|
(* message. *)
|
||||||
|
|
||||||
end;
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
@ -559,7 +603,7 @@ end;
|
|||||||
|
|
||||||
function TLTelnetClient.Get(out aData; const aSize: Integer; aSocket: TLSocket): Integer;
|
function TLTelnetClient.Get(out aData; const aSize: Integer; aSocket: TLSocket): Integer;
|
||||||
begin
|
begin
|
||||||
Result := FOutput.Read(aData, aSize);
|
Result := FOutput.Read(aData {%H- 5058 } , aSize);
|
||||||
if FOutput.Position = FOutput.Size then
|
if FOutput.Position = FOutput.Size then
|
||||||
FOutput.Clear;
|
FOutput.Clear;
|
||||||
end;
|
end;
|
||||||
|
Loading…
Reference in New Issue
Block a user