{ $Project$ $Workfile$ $Revision$ $DateUTC$ $Id$ This file is part of the Indy (Internet Direct) project, and is offered under the dual-licensing agreement described on the Indy website. (http://www.indyproject.org/) Copyright: (c) 1993-2005, Chad Z. Hower and the Indy Pit Crew. All rights reserved. } { $Log$ } { Rev 1.0 11/13/2002 08:03:48 AM JPMugaas } unit IdTunnelCommon; {* Indy Tunnel components module Copyright (C) 1999, 2000, 2001 Gregor Ibic (gregor.ibic@intelicom.si) Intelicom d.o.o., www.intelicom.si This component is published under same license like Indy package. This package is a TCP Tunnel implementation written by Gregor Ibic (gregor.ibic@intelicom.si). This notice may not be removed or altered from any source distribution. // MAJOR CHANGES 05-January-20001 GI: Major code reorganization and polishing 31-May-2000 GI TunnelHeaders eliminated. Some other code jugling. 29-May-2000 GI Components split in several files to be more compliant with Indy coding standards. It consists of: - IdTunnelHeaders - IdTunnelCommon - IdTunnelMaster - IdTunnelSlave 24-May-2000 GI: Turbo translation mode finished (01:24). It works! Will draw icons in the morning. 23-May-2000 GI: Turbo translation mode to Indy standard started by Gregor Ibic (hehe) (now is 23:15) *} interface {$i IdCompilerDefines.inc} uses SysUtils, Classes, SyncObjs, IdException, IdHashCRC, IdStack, IdCoder, IdResourceStrings, IdTCPServer; const BUFFERLEN = $4000; // Statistics constants NumberOfConnectionsType = 1; NumberOfPacketsType = 2; CompressionRatioType = 3; CompressedBytesType = 4; BytesReadType = 5; BytesWriteType = 6; NumberOfClientsType = 7; NumberOfSlavesType = 8; NumberOfServicesType = 9; // Message types tmError = 0; tmData = 1; tmDisconnect = 2; tmConnect = 3; tmCustom = 99; type TIdStatisticsOperation = (soIncrease, soDecrease ); TIdHeader = record CRC16: Word; MsgType: Word; MsgLen: Word; UserId: Word; Port: Word; IpAddr: TIdInAddr; end; TReceiver = class(TObject) private fiPrenosLen: LongInt; fiMsgLen: LongInt; fsData: String; fbNewMessage: Boolean; fCRCFailed: Boolean; Locker: TCriticalSection; CRC16Calculator: TIdHashCRC16; function FNewMessage: Boolean; procedure SetData(const Value: string); public pBuffer: PChar; HeaderLen: Integer; Header: TIdHeader; MsgLen: Word; TypeDetected: Boolean; Msg: PChar; property Data: String read fsData write SetData; property NewMessage: Boolean read FNewMessage; property CRCFailed: Boolean read fCRCFailed; procedure ShiftData; constructor Create; destructor Destroy; override; end; TSender = class(TObject) public Header: TIdHeader; DataLen: Word; HeaderLen: Integer; pMsg: PChar; Locker: TCriticalSection; CRC16Calculator: TIdHashCRC16; public Msg: String; procedure PrepareMsg(var Header: TIdHeader; buffer: PChar; buflen: Integer); constructor Create; destructor Destroy; override; end; // // END Communication classes /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // Logging class // TLogger = class(TObject) private OnlyOneThread: TCriticalSection; // Some locking code fLogFile: TextFile; // Debug Log File fbActive: Boolean; public property Active: Boolean read fbActive Default False; procedure LogEvent(Msg: String); constructor Create(LogFileName: String); destructor Destroy; override; end; // // Logging class /////////////////////////////////////////////////////////////////////////////// TSendMsgEvent = procedure(Thread: TIdPeerThread; var CustomMsg: String) of object; TSendTrnEvent = procedure(Thread: TIdPeerThread; var Header: TIdHeader; var CustomMsg: String) of object; TSendTrnEventC = procedure(var Header: TIdHeader; var CustomMsg: String) of object; TTunnelEventC = procedure(Receiver: TReceiver) of object; TSendMsgEventC = procedure(var CustomMsg: String) of object; // TTunnelEvent = procedure(Thread: TSlaveThread) of object; EIdTunnelException = class(EIdException); EIdTunnelTransformErrorBeforeSend = class(EIdTunnelException); EIdTunnelTransformError = class(EIdTunnelException); EIdTunnelConnectToMasterFailed = class(EIdTunnelException); EIdTunnelDontAllowConnections = class(EIdTunnelException); EIdTunnelCRCFailed = class(EIdTunnelException); EIdTunnelMessageTypeRecognitionError = class(EIdTunnelException); EIdTunnelMessageHandlingFailed = class(EIdTunnelException); EIdTunnelInterpretationOfMessageFailed = class(EIdTunnelException); EIdTunnelCustomMessageInterpretationFailure = class(EIdTunnelException); implementation /////////////////////////////////////////////////////////////////////////////// // Communication classes // constructor TSender.Create; begin inherited; Locker := TCriticalSection.Create; CRC16Calculator := TIdHashCRC16.Create; HeaderLen := SizeOf(TIdHeader); GetMem(pMsg, BUFFERLEN); end; destructor TSender.Destroy; begin FreeMem(pMsg, BUFFERLEN); Locker.Free; CRC16Calculator.Free; inherited; end; procedure TSender.PrepareMsg(var Header: TIdHeader; buffer: PChar; buflen: Integer); begin Locker.Enter; try //Header.MsgType := mType; Header.CRC16 := CRC16Calculator.HashValue(buffer^); Header.MsgLen := Headerlen + bufLen; //Header.UserId := mUser; //Header.Port := Port; //Header.IpAddr := IPAddr; Move(Header, pMsg^, Headerlen); Move(buffer^, (pMsg + Headerlen)^, bufLen); SetLength(Msg, Header.MsgLen); SetString(Msg, pMsg, Header.MsgLen); finally Locker.Leave; end; end; constructor TReceiver.Create; begin inherited; Locker := TCriticalSection.Create; CRC16Calculator := TIdHashCRC16.Create; fiPrenosLen := 0; fsData := ''; {Do not Localize} fiMsgLen := 0; HeaderLen := SizeOf(TIdHeader); GetMem(pBuffer, BUFFERLEN); GetMem(Msg, BUFFERLEN); end; destructor TReceiver.Destroy; begin FreeMem(pBuffer, BUFFERLEN); FreeMem(Msg, BUFFERLEN); Locker.Free; CRC16Calculator.Free; inherited; end; function TReceiver.FNewMessage: Boolean; begin Result := fbNewMessage; end; procedure TReceiver.SetData(const Value: string); var CRC16: Word; begin Locker.Enter; try try fsData := Value; fiMsgLen := Length(fsData); if fiMsgLen > 0 then begin Move(fsData[1], (pBuffer + fiPrenosLen)^, fiMsgLen); fiPrenosLen := fiPrenosLen + fiMsgLen; if (fiPrenosLen >= HeaderLen) then begin // copy the header Move(pBuffer^, Header, HeaderLen); TypeDetected := True; // do we have enough data for the entire message if Header.MsgLen <= fiPrenosLen then begin MsgLen := Header.MsgLen - HeaderLen; Move((pBuffer+HeaderLen)^, Msg^, MsgLen); // Calculate the crc code CRC16 := CRC16Calculator.HashValue(Msg^); if CRC16 <> Header.CRC16 then begin fCRCFailed := True; end else begin fCRCFailed := False; end; fbNewMessage := True; end else begin fbNewMessage := False; end; end else begin TypeDetected := False; end; end else begin fbNewMessage := False; TypeDetected := False; end; except raise; end; finally Locker.Leave; end; end; procedure TReceiver.ShiftData; var CRC16: Word; begin Locker.Enter; try fiPrenosLen := fiPrenosLen - Header.MsgLen; // check if we have another entire message if fiPrenosLen > 0 then begin Move((pBuffer + Header.MsgLen)^, pBuffer^, fiPrenosLen); end; // check if we have another entire message if (fiPrenosLen >= HeaderLen) then begin // copy the header Move(pBuffer^, Header, HeaderLen); TypeDetected := True; // do we have enough data for the entire message if Header.MsgLen <= fiPrenosLen then begin MsgLen := Header.MsgLen - HeaderLen; Move((pBuffer+HeaderLen)^, Msg^, MsgLen); // Calculate the crc code CRC16 := CRC16Calculator.HashValue(Msg^); if CRC16 <> Header.CRC16 then begin fCRCFailed := True; end else begin fCRCFailed := False; end; fbNewMessage := True; end else begin fbNewMessage := False; end; end else begin TypeDetected := False; end; finally Locker.Leave; end; end; // // END Communication classes /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // Logging class // constructor TLogger.Create(LogFileName: String); begin fbActive := False; OnlyOneThread := TCriticalSection.Create; try AssignFile(fLogFile, LogFileName); Rewrite(fLogFile); fbActive := True; except fbActive := False; //self.Destroy; // catch file i/o errors, double create file end; end; destructor TLogger.Destroy; begin if fbActive then CloseFile(fLogFile); OnlyOneThread.Free; inherited; end; procedure TLogger.LogEvent(Msg: String); begin OnlyOneThread.Enter; try WriteLn(fLogFile, Msg); Flush(fLogFile); finally OnlyOneThread.Leave; end; end; // // Logging class /////////////////////////////////////////////////////////////////////////////// end.