我正在编写一个非阻塞的Websocket客户端,并使用OpenSSL作为TLS层。我可以连接到远程服务器,完成TLS握手,发送升级请求,获得升级确认响应,然后在TLS层与SSL_ERROR_ZERO_RETURN
断开连接之前获得实际的websocket响应。
SSL_get_error(...)
返回:6 // SSL_ERROR_ZERO_RETURN
ERR_error_string(ERR_get_error(), nullptr)
返回:error:00000000:lib(0):func(0):reason(0)
根据我的理解,ERR_get_error()
应弹出并返回错误队列中的第一个错误,SSL_get_error()
返回SSL_*
函数的最后一个错误。我不明白为什么SSL_get_error()
会返回错误值,但ERR_get_error()
却没有。根据之前的Stack Overflow Question,SSL_get_error()
不会调用ERR_get_error()
。
重复调用以下代码(因为它是非阻塞套接字):
ERR_clear_error();
int ret = SSL_read(...);
if (ret > 0) {
// read bytes from socket
} else {
int err_code = SSL_get_error(ssl_session_, ret);
if (err_code == SSL_ERROR_ZERO_RETURN || err_code == SSL_ERROR_SYSCALL || err_code == SSL_ERROR_SSL) {
sprintf("Disconnected: %d %s", err_code, ERR_error_string(ERR_get_error(), nullptr));
// Disconnect Code
}
}
我有两个问题:
为什么我没有收到ERR_get_error()的错误值?
为什么我在建立TLS和Websocket会话后如此迅速地断开连接?
编辑1
我使用wireshark捕获客户端和服务器之间的数据包。我确认TLS握手,websocket升级和初始服务器响应都是成功的。我注意到在初始服务器响应之后,我的客户端从服务器获得Encrypted Alert 21
,我认为这是一个致命的错误,并解释了为什么TLS会话立即终止并且我的SSL错误队列为空(虽然它可能是客户端)问题,我不认为这是最近行动的结果),并且解释了我在SSL_ERROR_ZERO_RETURN
之后获得的SSL_Read
价值。
我不确定Encrypted Alert 21
需要什么。它可能是我使用的证书(自签名)。需要进一步调查。
答案 0 :(得分:-1)
好吧,这个问题的根本原因已经确定,但很多箍都被跳了出来,浪费了时间。我能够通过使用OpenSSL方法SSL_SESSION_get_master_key()获取主密钥以及使用wireshark的客户端Hello随机值来解密SSL流量。
SSL_Connect后输出主密钥的相关代码:
ERR_clear_error();
int ret = SSL_connect(ssl_ptr);
if (ret > 0) {
SSL_SESSION * ssl_session = SSL_get_session(ssl_ptr);
if(ssl_session != NULL) {
unsigned char master_key_buf[256];
size_t outlen = sizeof(master_key_buf);
size_t buf_size = SSL_SESSION_get_master_key(ssl_session, master_key_buf, outlen);
if(outlen > 0) {
char hex_encoded_master_buf[513];
// hex encode the master key
for(size_t i = 0; i < buf_size; ++i) {
sprintf(&hex_encoded_master_buf[2*i], "%02x", master_key_buf[i]);
}
hex_encoded_master_buf[(2*buf_size)] = '\0';
// log out the hex-encoded master key in master buf here
}
}
}
使用wireshark中的NSS Key Log CLIENT_RANDOM格式来解密捕获的SSL流量,我能够检查前面提到的Encrypted Alert 21
,它最终只是一个WebSocket FIN和close_notify。
事实证明,根本原因是在握手期间,我的WSS升级请求消息确实包含了正确的标头,但我实际上是在发送垃圾有效负载。这是在发送消息时使用消息缓冲区的sizeof
而不是strlen
来设置大小的情况。服务器可以很好地解析升级消息并成功完成握手,但下次检查其套接字时,它会在预期WSS消息时读取垃圾。这导致了websocket连接的突然关闭。
在摘要中,回答我原来的两个问题:
连接正在服务器端终止,并且错误队列将包含客户端上的错误,至少在SSL / TLS层上没有错误。
我的初始升级请求包含一个有效的Websocket升级请求,其中包含垃圾数据。服务器解析了Websocket升级请求并确认了升级,并开始发回数据。下次服务器检查其套接字时,它仍然具有与原始Websocket升级请求一起发送的垃圾值。由于服务器无法将其识别为有效的Websocket消息或其他任何内容,因此它决定使用close_notify
终止连接。