restemplate/indy/Protocols/IdExplicitTLSClientServerBa...

435 lines
12 KiB
Plaintext

{
$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.14 10/26/2004 9:09:36 PM JPMugaas
Updated references.
Rev 1.13 2004.02.03 5:45:36 PM czhower
Name changes
Rev 1.12 1/25/2004 3:52:28 PM JPMugaas
Fixes for abstract SSL interface to work in NET.
Rev 1.11 1/21/2004 1:23:38 PM JPMugaas
InitComponent.
Rev 1.10 5/25/2003 12:06:16 AM JPMugaas
TLS checking code moved into a protected method for reuse in TIdDirectSMTP.
Note that TLS support is different in that component because of the way it
works.
Rev 1.9 5/21/2003 3:36:42 PM BGooijen
Fixed design time bug regarding the Active property
Rev 1.8 5/8/2003 11:27:38 AM JPMugaas
Moved feature negoation properties down to the ExplicitTLSClient level as
feature negotiation goes hand in hand with explicit TLS support.
Rev 1.7 4/13/2003 05:38:02 PM JPMugaas
Fix for SetTLS exception problem with IdMessage.SaveToFile.
Rev 1.6 4/5/2003 02:06:48 PM JPMugaas
TLS handshake itself can now be handled.
Rev 1.5 3/27/2003 05:46:22 AM JPMugaas
Updated framework with an event if the TLS negotiation command fails.
Cleaned up some duplicate code in the clients.
Rev 1.4 3/26/2003 04:19:18 PM JPMugaas
Cleaned-up some code and illiminated some duplicate things.
Rev 1.3 3/23/2003 11:45:02 PM BGooijen
classes -> Classes
Rev 1.2 3/18/2003 04:36:52 PM JPMugaas
Rev 1.1 3/16/2003 06:08:34 PM JPMugaas
Fixed a bug where the wrong port number was being set. I also expanded a few
things for the server.
Rev 1.0 3/16/2003 02:38:08 PM JPMugaas
Base class for some clients that use both implicit and explicit TLS.
}
unit IdExplicitTLSClientServerBase;
interface
{$i IdCompilerDefines.inc}
uses
Classes,
IdCmdTCPServer,
IdException,
IdGlobal,
IdIOHandler,
IdServerIOHandler,
IdTCPClient;
type
TIdUseTLS = (
utNoTLSSupport,
utUseImplicitTLS, // ssl iohandler req, allways tls
utUseRequireTLS, // ssl iohandler req, user command only accepted when in tls
utUseExplicitTLS // < user can choose to use tls
);
const
ExplicitTLSVals = [utUseRequireTLS,utUseExplicitTLS];
DEF_USETLS = utNoTLSSupport; //we can't assume the user wants to use a SSL IOHandler
type
TIdOnTLSNegotiationFailure = procedure(Asender : TObject; var VContinue : Boolean) of object;
TIdExplicitTLSServer = class(TIdCmdTCPServer)
protected
FRegularProtPort : TIdPort;
FImplicitTLSProtPort : TIdPort;
FUseTLS : TIdUseTLS;
procedure Loaded; override;
procedure SetIOHandler(const AValue: TIdServerIOHandler); override;
procedure SetUseTLS(AValue : TIdUseTLS); virtual;
property UseTLS : TIdUseTLS read FUseTLS write SetUseTLS default DEF_USETLS;
procedure InitComponent; override;
end;
TIdExplicitTLSClient = class(TIdTCPClientCustom)
protected
FRegularProtPort : TIdPort;
FImplicitTLSProtPort : TIdPort;
FUseTLS : TIdUseTLS;
FOnTLSNotAvailable : TIdOnTLSNegotiationFailure;
FOnTLSNegCmdFailed : TIdOnTLSNegotiationFailure;
FOnTLSHandShakeFailed : TIdOnTLSNegotiationFailure;
//feature negotiation stuff
FCapabilities : TStrings;
function GetSupportsTLS : Boolean; virtual;
procedure CheckIfCanUseTLS; virtual;
procedure Loaded; override;
procedure TLSNotAvailable;
procedure DoOnTLSNotAvailable;
procedure ProcessTLSNotAvail;
procedure TLSNegCmdFailed;
procedure DoOnTLSNegCmdFailed;
procedure ProcessTLSNegCmdFailed;
procedure TLSHandShakeFailed;
procedure DoOnTLSHandShakeFailed;
procedure ProcessTLSHandShakeFailed;
procedure SetIOHandler(AValue: TIdIOHandler); override;
procedure SetUseTLS(AValue : TIdUseTLS); virtual;
//Note TLSHandshake should be the ONLY method to do the actual TLS
//or SSL handshake for explicit TLS clients.
procedure TLSHandshake; virtual;
procedure InitComponent; override;
property UseTLS : TIdUseTLS read FUseTLS write SetUseTLS default DEF_USETLS;
public
destructor Destroy; override;
procedure Connect; override;
property SupportsTLS: boolean read GetSupportsTLS;
property Capabilities : TStrings read FCapabilities;
property OnTLSHandShakeFailed : TIdOnTLSNegotiationFailure read FOnTLSHandShakeFailed write FOnTLSHandShakeFailed;
property OnTLSNotAvailable : TIdOnTLSNegotiationFailure read FOnTLSNotAvailable write FOnTLSNotAvailable;
property OnTLSNegCmdFailed : TIdOnTLSNegotiationFailure read FOnTLSNegCmdFailed write FOnTLSNegCmdFailed;
end;
EIdTLSClientException = class(EIdException);
EIdTLSClientSSLIOHandlerRequred = class(EIdTLSClientException);
EIdTLSClientCanNotSetWhileConnected = class(EIdTLSClientException);
EIdTLSClientTLSNotAvailable = class(EIdTLSClientException);
EIdTLSClientTLSNegCmdFailed = class(EIdTLSClientException);
EIdTLSClientTLSHandShakeFailed = class(EIdTLSClientException);
EIdTLSServerException = class(EIdException);
EIdTLSServerSSLIOHandlerRequired = class(EIdTLSServerException);
EIdTLSClientCanNotSetWhileActive = class(EIdTLSClientException);
implementation
uses
IdResourceStringsProtocols, IdSSL, IdBaseComponent, SysUtils;
{ TIdExplicitTLSServer }
procedure TIdExplicitTLSServer.InitComponent;
begin
inherited InitComponent;
FUseTLS := DEF_USETLS;
end;
procedure TIdExplicitTLSServer.Loaded;
begin
inherited Loaded;
if not (IOHandler is TIdServerIOHandler) then begin
SetUseTLS(utNoTLSSupport);
end;
end;
procedure TIdExplicitTLSServer.SetIOHandler(const AValue: TIdServerIOHandler);
begin
inherited SetIOHandler(AValue);
if not (IOHandler is TIdServerIOHandlerSSLBase) then begin
SetUseTLS(utNoTLSSupport);
end;
end;
procedure TIdExplicitTLSServer.SetUseTLS(AValue: TIdUseTLS);
begin
if (not Active) or IsDesignTime then
begin
if IsLoading then begin
FUseTLS := AValue;
Exit;
end;
if (not (IOHandler is TIdServerIOHandlerSSLBase)) and (AValue <> utNoTLSSupport) then begin
raise EIdTLSServerSSLIOHandlerRequired.Create(RSTLSSSLIOHandlerRequired);
end;
if FUseTLS <> AValue then
begin
if AValue = utUseImplicitTLS then
begin
if DefaultPort = FRegularProtPort then begin
DefaultPort := FImplicitTLSProtPort;
end;
end else
begin
if DefaultPort = FImplicitTLSProtPort then begin
DefaultPort := FRegularProtPort;
end;
end;
FUseTLS := AValue;
end;
end else begin
raise EIdTLSClientCanNotSetWhileActive.Create(RSTLSSLCanNotSetWhileConnected);
end;
end;
{ TIdExplicitTLSClient }
procedure TIdExplicitTLSClient.CheckIfCanUseTLS;
begin
if not (IOHandler is TIdSSLIOHandlerSocketBase) then begin
raise EIdTLSClientSSLIOHandlerRequred.Create(RSTLSSSLIOHandlerRequired);
end;
end;
procedure TIdExplicitTLSClient.Connect;
begin
if UseTLS in ExplicitTLSVals then begin
// TLS only enabled later in this case!
(IOHandler as TIdSSLIOHandlerSocketBase).PassThrough := True;
end;
if (IOHandler is TIdSSLIOHandlerSocketBase) then begin
case FUseTLS of
utNoTLSSupport :
begin
(IOHandler as TIdSSLIOHandlerSocketBase).PassThrough := True;
end;
utUseImplicitTLS :
begin
(IOHandler as TIdSSLIOHandlerSocketBase).PassThrough := False;
end;
else
begin
if FUseTLS <> utUseImplicitTLS then begin
(IOHandler as TIdSSLIOHandlerSocketBase).PassThrough := True;
end;
end;
end;
end;
inherited Connect;
end;
procedure TIdExplicitTLSClient.InitComponent;
begin
inherited InitComponent;
FCapabilities := TStringList.Create;
FUseTLS := DEF_USETLS;
end;
destructor TIdExplicitTLSClient.Destroy;
begin
FreeAndNil(FCapabilities);
inherited Destroy;
end;
//OnTLSHandShakeFailed
procedure TIdExplicitTLSClient.DoOnTLSHandShakeFailed;
var
LContinue : Boolean;
begin
LContinue := False;
if Assigned(OnTLSHandShakeFailed) then begin
FOnTLSHandShakeFailed(Self, LContinue);
end;
if not LContinue then begin
TLSHandShakeFailed;
end;
end;
procedure TIdExplicitTLSClient.DoOnTLSNegCmdFailed;
var
LContinue : Boolean;
begin
LContinue := False;
if Assigned(OnTLSNegCmdFailed) then begin
FOnTLSNegCmdFailed(Self, LContinue);
end;
if not LContinue then begin
TLSNegCmdFailed;
end;
end;
procedure TIdExplicitTLSClient.DoOnTLSNotAvailable;
var
LContinue : Boolean;
begin
LContinue := True;
if Assigned(FOnTLSNotAvailable) then begin
FOnTLSNotAvailable(Self, LContinue);
end;
if not LContinue then begin
TLSNotAvailable;
end;
end;
procedure TIdExplicitTLSClient.Loaded;
begin
inherited Loaded;
if not (IOHandler is TIdSSLIOHandlerSocketBase) then begin
SetUseTLS(utNoTLSSupport);
end;
end;
procedure TIdExplicitTLSClient.ProcessTLSHandShakeFailed;
begin
if FUseTLS = utUseRequireTLS then begin
TLSHandShakeFailed;
end else begin
DoOnTLSHandShakeFailed;
end;
end;
procedure TIdExplicitTLSClient.ProcessTLSNegCmdFailed;
begin
if FUseTLS = utUseRequireTLS then begin
TLSNegCmdFailed;
end else begin
DoOnTLSNegCmdFailed;
end;
end;
procedure TIdExplicitTLSClient.ProcessTLSNotAvail;
begin
if FUseTLS = utUseRequireTLS then begin
TLSNotAvailable;
end else begin
DoOnTLSNotAvailable;
end;
end;
procedure TIdExplicitTLSClient.SetIOHandler(AValue: TIdIOHandler);
begin
inherited SetIOHandler(AValue);
if not (IOHandler is TIdSSLIOHandlerSocketBase) then begin
if FUseTLS <> utNoTLSSupport then begin
SetUseTLS(utNoTLSSupport);
end;
end;
end;
procedure TIdExplicitTLSClient.SetUseTLS(AValue: TIdUseTLS);
begin
if Connected then begin
raise EIdTLSClientCanNotSetWhileConnected.Create(RSTLSSLCanNotSetWhileConnected);
end;
if IsLoading then begin
FUseTLS := AValue;
Exit;
end;
if AValue <> utNoTLSSupport then begin
CheckIfCanUseTLS;
end;
if FUseTLS <> AValue then begin
if AValue = utUseImplicitTLS then begin
if Port = FRegularProtPort then begin
Port := FImplicitTLSProtPort;
end;
end else begin
if Port = FImplicitTLSProtPort then begin
Port := FRegularProtPort;
end;
end;
FUseTLS := AValue;
end;
end;
procedure TIdExplicitTLSClient.TLSHandshake;
begin
try
if (IOHandler is TIdSSLIOHandlerSocketBase) then begin
(IOHandler as TIdSSLIOHandlerSocketBase).PassThrough := False;
end;
except
ProcessTLSHandShakeFailed;
end;
end;
procedure TIdExplicitTLSClient.TLSHandShakeFailed;
begin
if Connected then begin
// RLebeau 9/19/2013: do not send a goodbye command to the peer.
// The socket data may be in a bad state at this point!
Disconnect(False);
end;
// This method should always be called in the context of an active 'except'
// block, so use IndyRaiseOuterException() to capture the inner exception
// (if possible) when raising this outer exception...
IndyRaiseOuterException(EIdTLSClientTLSHandShakeFailed.Create(RSTLSSLSSLHandshakeFailed));
end;
procedure TIdExplicitTLSClient.TLSNegCmdFailed;
begin
if Connected then begin
Disconnect;
end;
// This method should never be called in the context of an active 'except'
// block, so do not use IndyRaiseOuterException() to capture an inner exception
// when raising this exception...
raise EIdTLSClientTLSNegCmdFailed.Create(RSTLSSLSSLCmdFailed);
end;
procedure TIdExplicitTLSClient.TLSNotAvailable;
begin
if Connected then begin
Disconnect;
end;
raise EIdTLSClientTLSNotAvailable.Create(RSTLSSLSSLNotAvailable);
end;
function TIdExplicitTLSClient.GetSupportsTLS: boolean;
begin
//this is a dummy for descendants to override. NET doesn't support
//abstract methods.
Result := False;
end;
end.