435 lines
12 KiB
Plaintext
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.
|