{==============================================================================| | Project : Ararat Synapse | 001.003.001 | |==============================================================================| | Content: TELNET and SSH2 client | |==============================================================================| | Copyright (c)1999-2010, Lukas Gebauer | | All rights reserved. | | | | Redistribution and use in source and binary forms, with or without | | modification, are permitted provided that the following conditions are met: | | | | Redistributions of source code must retain the above copyright notice, this | | list of conditions and the following disclaimer. | | | | Redistributions in binary form must reproduce the above copyright notice, | | this list of conditions and the following disclaimer in the documentation | | and/or other materials provided with the distribution. | | | | Neither the name of Lukas Gebauer nor the names of its contributors may | | be used to endorse or promote products derived from this software without | | specific prior written permission. | | | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR | | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR | | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER | | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH | | DAMAGE. | |==============================================================================| | The Initial Developer of the Original Code is Lukas Gebauer (Czech Republic).| | Portions created by Lukas Gebauer are Copyright (c)2002-2010. | | All Rights Reserved. | |==============================================================================| | Contributor(s): | |==============================================================================| | History: see HISTORY.HTM from distribution package | | (Found at URL: http://www.ararat.cz/synapse/) | |==============================================================================} {:@abstract(Telnet script client) Used RFC: RFC-854 } {$IFDEF FPC} {$MODE DELPHI} {$ENDIF} {$H+} unit tlntsend; interface uses SysUtils, Classes, blcksock, synautil; const cTelnetProtocol = '23'; cSSHProtocol = '22'; TLNT_EOR = #239; TLNT_SE = #240; TLNT_NOP = #241; TLNT_DATA_MARK = #242; TLNT_BREAK = #243; TLNT_IP = #244; TLNT_AO = #245; TLNT_AYT = #246; TLNT_EC = #247; TLNT_EL = #248; TLNT_GA = #249; TLNT_SB = #250; TLNT_WILL = #251; TLNT_WONT = #252; TLNT_DO = #253; TLNT_DONT = #254; TLNT_IAC = #255; type {:@abstract(State of telnet protocol). Used internaly by TTelnetSend.} TTelnetState =(tsDATA, tsIAC, tsIAC_SB, tsIAC_WILL, tsIAC_DO, tsIAC_WONT, tsIAC_DONT, tsIAC_SBIAC, tsIAC_SBDATA, tsSBDATA_IAC); {:@abstract(Class with implementation of Telnet/SSH script client.) Note: Are you missing properties for specify server address and port? Look to parent @link(TSynaClient) too!} TTelnetSend = class(TSynaClient) private FSock: TTCPBlockSocket; FBuffer: Ansistring; FState: TTelnetState; FSessionLog: Ansistring; FSubNeg: Ansistring; FSubType: Ansichar; FTermType: Ansistring; function Connect: Boolean; function Negotiate(const Buf: Ansistring): Ansistring; procedure FilterHook(Sender: TObject; var Value: AnsiString); public constructor Create; destructor Destroy; override; {:Connects to Telnet server.} function Login: Boolean; {:Connects to SSH2 server and login by Username and Password properties. You must use some of SSL plugins with SSH support. For exammple CryptLib.} function SSHLogin: Boolean; {:Logout from telnet server.} procedure Logout; {:Send this data to telnet server.} procedure Send(const Value: string); {:Reading data from telnet server until Value is readed. If it is not readed until timeout, result is @false. Otherwise result is @true.} function WaitFor(const Value: string): Boolean; {:Read data terminated by terminator from telnet server.} function RecvTerminated(const Terminator: string): string; {:Read string from telnet server.} function RecvString: string; published {:Socket object used for TCP/IP operation. Good for seting OnStatus hook, etc.} property Sock: TTCPBlockSocket read FSock; {:all readed datas in this session (from connect) is stored in this large string.} property SessionLog: Ansistring read FSessionLog write FSessionLog; {:Terminal type indentification. By default is 'SYNAPSE'.} property TermType: Ansistring read FTermType write FTermType; end; implementation constructor TTelnetSend.Create; begin inherited Create; FSock := TTCPBlockSocket.Create; FSock.Owner := self; FSock.OnReadFilter := FilterHook; FTimeout := 60000; FTargetPort := cTelnetProtocol; FSubNeg := ''; FSubType := #0; FTermType := 'SYNAPSE'; end; destructor TTelnetSend.Destroy; begin FSock.Free; inherited Destroy; end; function TTelnetSend.Connect: Boolean; begin // Do not call this function! It is calling by LOGIN method! FBuffer := ''; FSessionLog := ''; FState := tsDATA; FSock.CloseSocket; FSock.LineBuffer := ''; FSock.Bind(FIPInterface, cAnyPort); FSock.Connect(FTargetHost, FTargetPort); Result := FSock.LastError = 0; end; function TTelnetSend.RecvTerminated(const Terminator: string): string; begin Result := FSock.RecvTerminated(FTimeout, Terminator); end; function TTelnetSend.RecvString: string; begin Result := FSock.RecvTerminated(FTimeout, CRLF); end; function TTelnetSend.WaitFor(const Value: string): Boolean; begin Result := FSock.RecvTerminated(FTimeout, Value) <> ''; end; procedure TTelnetSend.FilterHook(Sender: TObject; var Value: AnsiString); begin Value := Negotiate(Value); FSessionLog := FSessionLog + Value; end; function TTelnetSend.Negotiate(const Buf: Ansistring): Ansistring; var n: integer; c: Ansichar; Reply: Ansistring; SubReply: Ansistring; begin Result := ''; for n := 1 to Length(Buf) do begin c := Buf[n]; Reply := ''; case FState of tsData: if c = TLNT_IAC then FState := tsIAC else Result := Result + c; tsIAC: case c of TLNT_IAC: begin FState := tsData; Result := Result + TLNT_IAC; end; TLNT_WILL: FState := tsIAC_WILL; TLNT_WONT: FState := tsIAC_WONT; TLNT_DONT: FState := tsIAC_DONT; TLNT_DO: FState := tsIAC_DO; TLNT_EOR: FState := tsDATA; TLNT_SB: begin FState := tsIAC_SB; FSubType := #0; FSubNeg := ''; end; else FState := tsData; end; tsIAC_WILL: begin case c of #3: //suppress GA Reply := TLNT_DO; else Reply := TLNT_DONT; end; FState := tsData; end; tsIAC_WONT: begin Reply := TLNT_DONT; FState := tsData; end; tsIAC_DO: begin case c of #24: //termtype Reply := TLNT_WILL; else Reply := TLNT_WONT; end; FState := tsData; end; tsIAC_DONT: begin Reply := TLNT_WONT; FState := tsData; end; tsIAC_SB: begin FSubType := c; FState := tsIAC_SBDATA; end; tsIAC_SBDATA: begin if c = TLNT_IAC then FState := tsSBDATA_IAC else FSubNeg := FSubNeg + c; end; tsSBDATA_IAC: case c of TLNT_IAC: begin FState := tsIAC_SBDATA; FSubNeg := FSubNeg + c; end; TLNT_SE: begin SubReply := ''; case FSubType of #24: //termtype begin if (FSubNeg <> '') and (FSubNeg[1] = #1) then SubReply := #0 + FTermType; end; end; Sock.SendString(TLNT_IAC + TLNT_SB + FSubType + SubReply + TLNT_IAC + TLNT_SE); FState := tsDATA; end; else FState := tsDATA; end; else FState := tsData; end; if Reply <> '' then Sock.SendString(TLNT_IAC + Reply + c); end; end; procedure TTelnetSend.Send(const Value: string); begin Sock.SendString(ReplaceString(Value, TLNT_IAC, TLNT_IAC + TLNT_IAC)); end; function TTelnetSend.Login: Boolean; begin Result := False; if not Connect then Exit; Result := True; end; function TTelnetSend.SSHLogin: Boolean; begin Result := False; if Connect then begin FSock.SSL.SSLType := LT_SSHv2; FSock.SSL.Username := FUsername; FSock.SSL.Password := FPassword; FSock.SSLDoConnect; Result := FSock.LastError = 0; end; end; procedure TTelnetSend.Logout; begin FSock.CloseSocket; end; end.