我创建了一个服务器和一个客户端,它们在安全的TLSv1.3,OpenSSL 3.0.0通道中进行通信。它们必须能够发送和接收,但是在此测试中,服务器仅接收,而客户端仅定期发送应用程序数据。
握手成功后,客户端select()
发出信号,表明有可供读取的数据。当我尝试这样做时,SSL_read()
不会返回。我检查了Wireshark中的网络流量,并调试了OpenSSL库,以发现服务器握手后向客户端发送了2个 SESSION_TICKET ,这就是消息SSL_read()
失败处理。出于好奇,我在握手后立即与服务器发送了一条消息,以查看客户端对此有何反应。令人惊讶的是,客户端离开了阻塞状态,此后通信正常。
由于观察到的行为,我唯一的猜测是我尝试读取供OpenSSL使用的数据,当我调用SSL_read()
时,该数据可能由其状态机处理。当库调用read()
时,实际上没有可用的应用程序数据,因此它会阻塞线程。
我用来读取“计数”字节的成员函数如下所示。
int EventHandler::readByte(CommBuffer &buffer, ssize_t count, struct timeval &tmout){
fd_set rfds;
int retSelect, retRead;
int readByte=0;
int endRead=0;
int readSize=count;
buffer.setBufferSize(count+1);
do{
FD_ZERO(&rfds);
FD_SET(fd,&rfds);
retSelect=select(fd+1,&rfds,NULL,NULL,&tmout);
if(retSelect>0){
if (encryptionOn) {
retRead = SSL_read(ssl, (const_cast<char*>(&buffer.get()[readByte])), readSize); // Thread blocks here.
} else {
retRead = read(fd, (const_cast<char*>(&buffer.get()[readByte])), readSize);
}
if( retRead > 0 ){
readByte+=retRead;
allReadBytes+=retRead;
buffer.setDataSize(readByte);
if(readByte>=count) endRead=1;
readSize=count-readByte;
}
else{
if( retRead == -1 && errno == EINTR ){
continue;
}
if(fd>=0){
close(fd);
}
fd=CLOSED_FD;
return (CLOSED_FD);
}
}
else if(retSelect==0){
// handle timeout error
return(TIMEOUT_FD);
}
else{
return(retSelect);
}
}while(endRead!=1);
return(readByte);
}
请注意,此功能无需加密即可正常工作。 ({read()
而不是SSL_read()
)
答案 0 :(得分:1)
select
在TCP级别工作,而SSL_read
在TLS级别工作。通常,只有在应用程序数据可用时,阻塞套接字上的SSL_read
才会返回,而套接字上的任何类型的数据可用时,select
都会发出信号,即也用于非应用程序记录或不完整的SSL帧
在您的特定情况下,这些数据是会话票证,使用TLS 1.3的会话票证不再作为TLS握手的一部分发送,而是在握手之后发送。但是,对于较低的TLS版本,即使SSL_read
返回了可用数据,select
也会被阻塞:TLS中的应用程序数据在SSL帧内发送,并且可能发生select
发出信号即使只有部分SSL帧可用,也可以使用可用数据。在这种情况下,SSL_read
将阻塞直到可以读取完整的SSL帧。此外,即使SSL_read
发出可用数据信号,select
也会阻止SSL重新协商。
此外,可能发生select
不会发出可用数据的信号,但是SSL_read
会返回一些数据。以前的SSL_read
并没有消耗掉已经从TCP套接字读取的最后一个SSL帧中包含的所有数据时,就是这种情况。
换句话说:select
不应与SSL中的阻塞套接字一起使用。相反,应将套接字设为非阻塞套接字,以便SSL_read
或SSL_ERROR_WANT_READ
可能因记录而处理的SSL_ERROR_WANT_WRITE
或SSL_pending
失败。另外,应调用SSL_read
来检查是否已从TCP套接字读取但import ntc_templates
from ntc_templates.parse import parse_output
vlan_output = (
"VLAN Name Status Ports\n"
"---- -------------------------------- --------- -------------------------------\n"
"1 default active Po1, Eth1/1, Eth1/2, Eth1/3\n"
" Eth1/4, Eth1/5, Eth1/6, Eth1/7\n"
" Eth1/8, Eth1/9, Eth1/10, Eth1/11\n"
" Eth1/12, Eth1/13, Eth1/14\n"
" Eth1/15, Eth1/16, Eth1/17\n"
" Eth1/18, Eth1/19, Eth1/20\n"
" Eth1/21, Eth1/22, Eth1/23\n"
" Eth1/24, Eth1/25, Eth1/26\n"
" Eth1/27, Eth1/28, Eth1/29\n"
" Eth1/30, Eth1/31, Eth1/32\n"
" Eth1/33, Eth1/34, Eth1/35\n"
" Eth1/36, Eth1/38, Eth1/39\n"
" Eth1/40, Eth1/41, Eth1/42\n"
" Eth1/43, Eth1/44, Eth1/45\n"
" Eth1/46, Eth1/47, Eth1/48\n"
"10 test active Po1, Eth1/37, Eth1/41, Eth1/42\n"
" Eth1/43, Eth1/44, Eth1/45\n"
" Eth1/46, Eth1/47, Eth1/48\n")
vlan_parsed = parse_output(platform="cisco_ios", command="show vlan", data=vlan_output)
print(vlan_parsed)
尚未使用的数据。
答案 1 :(得分:0)
如果使用的是阻塞插槽,则可能启用了模式SSL_MODE_AUTO_RETRY
。如果没有要读取的应用程序数据,这可能会导致SSL_read
挂起。
在这种情况下,您可以禁用此模式(但是您需要处理SSL_ERROR_WANT_READ
错误),也可以使用非阻塞套接字。
建议您您可以阅读有关SSL_MODE_AUTO_RETRY的信息。