我想让SMTP TLS“隐式/显式”配置透明,为此我想到了使用事件触发器。
我的想法是捕捉TLS协商成功或失败,然后用另一个选项再试一次。
我在TIdSMTP
和TIdSSLIOHandlerSocketOpenSSL
上看到了一些事件,但似乎都没有正确触发。
Indy的文档也无法下载here(DNS中断错误)。
我使用以下带有“Sleep(1);
“的事件只是为了在其中放置一个断点。TIdSMTP
:OnTLSNotAvailable
个OnTLSHandShakeFailed
个OnTLSNegCmdFailed
个OnFailedEHLO
个TIdSSLIOHandlerSocketOpenSSL
:OnStatus
OnStatusInfo
个OnStatusInfoEx
个
我测试过的:
例如,我使用Outlooksmtp.office365.com,端口为587(在Indy中为STARTTLS
-utUseExplicitTLS
),但我故意将TIdSMTP.UseTLS
更改为utUseImlicitTLS
以模拟TLS协商失败。
在第一个TIdSMTP.Connect()
测试中,我得到了一个EIdSocketError
异常('*Socket Error # 10060 Connection timed out'),而不是TLS协商失败的消息/异常。在这个测试中,唯一触发的事件是TIdSSLIOHandlerSocketOpenSSL.OnStatus
和TIdSMTP.OnStatus
。
在第二次TIdSMTP.Connect()
测试中,我没有收到EIdSocketError
的超时消息,除了第一次尝试的事件外,它还触发了TIdSSLIOHandlerSocketOpenSSL.OnStatusInfo
和TIdSSLIOHandlerSocketOpenSSL.OnStatusInfoEx
事件,处理TLS连接错误。
一些代码片段:
procedure Connect;
var
LSMTP: TIdSMTP;
LSSL: TIdSSLIOHandlerSocketOpenSSL;
begin
LSMTP:= TIdSMTP.Create(nil);
LSSL := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
{functions to be declared}
LSMTP.OnTLSNotAvailable := MyTLSNotAvailable;
LSMTP.OnTLSHandShakeFailed:= MyTLSHandShakeFailed;
LSMTP.OnTLSNegCmdFailed := MyTLSNegCmdFailed;
LSMTP.OnFailedEHLO := MyFailedEHLO;
LSSL.OnStatus := MyOnStatus;
LSSL.OnStatusInfo := MySSLInfo;
LSSL.OnStatusInfoEx:= MySSlInfoEx;
LSSL.ConnectTimeout:= 1000000;
LSSL.ReadTimeout := 1000000;
LSMTP.IOHandler := FSSL;
LSMTP.AuthType := satDefault;
LSMTP.UseTLS := utUseExplicitTLS;
LSSL.SSLOptions.Method:= sslvSSLv23;
LSSL.SSLOptions.Mode := sslmBoth;
LSMTP.Host:= 'smtp.office365.com';
LSMTP.Port:= '587';
LSMTP.Username:= [email protected];
LSMTP.Password:= password;
LSMTP.From:= LSMTP.Username;
try
LSMTP.Connect;
except
raise Exception.Create(Format('Error during connect attempt: %s',[Exception(ExceptObject).Message]));
end;
end;
字符串
我有几个问题
- 为什么在这些情况下不触发
TIdSMTP.OnTLSNotAvailable
、TIdSMTP.OnTLSHandShakeFailed
和TIdSMTP.OnTLSNegCmdFailed
事件? - 为什么
TIdSSLIOHandlerSocketOpenSSL.OnStatusInfo
和TIdSSLIOHandlerSocketOpenSSL.OnStatusInfoEx
事件只在第二次连接尝试中触发?
我错过了什么吗?
1条答案
按热度按时间xe55xuns1#
您不应该使用事件来完成您正在尝试的操作,因为Indy不是事件驱动的。事件仅用于提供信息,而不是用于流控制。如果TLS握手失败,将相应地引发异常。捕获异常,关闭TCP连接,然后根据需要重试。
至于你试图模拟TLS失败:
utUseImplicitTLS
,则客户端将在建立TCP连接后立即发送TLS握手,然后当它将服务器的未加密SMTP问候误解为TLS握手响应时,TLS握手失败。utUseExplicitTLS
,则客户端将在建立TCP连接后超时(或死锁,如果不使用超时),因为它将等待服务器从未发送的未加密SMTP问候,因为服务器正在等待客户端从未发送的TLS握手。关于现有的各种活动:
OnTLSNotAvailable
在以下情况下触发:UseTLS
是utUseRequireTLS
1,并且UseEhlo
是True
,并且EHLO
命令以了解服务器的功能时,SMTP服务器不会通告对STARTTLS
的支持。如果事件处理程序将
VContinue
参数设置为False
,则会引发EIdTLSClientTLSNotAvailable
异常,否则SMTP会话将继续不加密。默认值为True
。一曰:在客户端,
utUseRequireTLS
类似于utUseExplicitTLS
,但服务器拒绝使用TLS是不可接受的。而utUseExplicitTLS
的TLS是可选的。OnTLSHandShakeFailed
在以下情况下被触发:UseTLS
是utUseImplicitTLS
或utUseExplicitTLS
(不是utUseRequireTLS
),并且SSLIOHandler
在TLS握手期间引发异常。如果事件处理程序将
VContinue
参数设置为False
,则会引发EIdTLSClientTLSHandShakeFailed
异常,否则SMTP会话将继续不加密。默认值为False
。注意事项:在这种情况下,如果绕过异常,那么SMTP会话在技术上将处于 undefined behavior 领域,因为套接字数据的状态将是未知的(异常是在数据包中间引发的吗?在数据包之间?还有更多的数据包仍在飞行中?等等)。与其他事件不同,套接字数据处于良好定义的状态。
OnTLSNegCmdFailed
在以下情况下被触发:UseTLS
是utUseExplicitTLS
(不是utUseRequireTLS
),并且STARTTLS
命令发送失败响应。如果事件处理程序将
VContinue
参数设置为False
,则会引发EIdTLSClientTLSNegCmdFailed
异常,否则SMTP会话将继续不加密。默认值为False
。OnFailedEHLO
在以下情况下被触发:UseEhlo
是True
,并且EHLO
命令发送失败响应。如果事件处理程序将
VContinue
参数设置为False
,则TCP连接将关闭并引发EIdSMTPReplyError
异常,否则SMTP会话将继续,而不知道服务器的功能。默认值为True
。OnStatus
用于一些通用组件级操作,如解析主机名的IP地址、连接到TCP端口、编码电子邮件等。OnStatusInfo/Ex
由SSLIOHandler
触发,以在TLS会话期间从OpenSSL库提供状态更新。有关提供哪种状态值的信息,请参阅OpenSSL的文档:https://www.openssl.org/docs/man1.0.2/man3/SSL_CTX_set_info_callback.html