我正在编写一个非阻塞 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 session 后很快就断开连接?
编辑 1
我使用wireshark来捕获客户端和服务器之间的数据包。我确认 TLS 握手、Websocket 升级和初始服务器响应均成功。我注意到在初始服务器响应后,我的客户端从服务器收到一个Encrypted Alert 21
,我认为这是一个 fatal error ,并解释了为什么 TLS session 立即终止并且我的 SSL 错误队列为空(虽然它可能是客户端问题,我不认为这是最近操作的结果),并且有点解释了我在 SSL_Read
之后得到的 SSL_ERROR_ZERO_RETURN
值。
我不确定加密警报 21
意味着什么。这可能是我正在使用的证书(自签名)。需要进一步调查。
最佳答案
好吧,问题的根本原因已经确定,但还是跳过了很多麻烦,并且浪费了时间。我能够通过使用 OpenSSL 方法获取主 key 来解密 SSL 流量,SSL_SESSION_get_master_key() ,以及带有wireshark的Client Hello的随机值。
SSL_Connect后输出主 key 的相关代码:
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
}
}
}
使用NSS Key Log CLIENT_RANDOM通过使用wireshark中的格式来解密捕获的SSL流量,我能够检查前面提到的Encrypted Alert 21
,它最终只是一个WebSocket FIN和close_notify。
事实证明,根本原因是在握手期间,我的 WSS 升级请求消息确实包含正确的 header ,但我实际上用它发送了垃圾有效负载。这是发送消息时使用消息缓冲区的 sizeof
而不是 strlen
设置大小的情况。服务器本来可以很好地解析升级消息并成功完成握手,但下次检查其套接字时,当它期待 WSS 消息时,它会读入垃圾。这导致 websocket 连接突然关闭。
总而言之,回答我原来的两个问题:
- 为什么我没有获得 ERR_get_error() 的错误值?
服务器端正在终止连接,错误队列将包含客户端的错误,但至少在 SSL/TLS 层上没有任何错误。
- 为什么我在建立 TLS 和 Websocket session 后很快就断开连接?
我的初始升级请求包含有效的 Websocket 升级请求,其后带有垃圾数据。 Server解析Websocket升级请求并确认升级,并开始发回数据。下次服务器检查其套接字时,它仍然具有与原始 Websocket 升级请求一起发送的垃圾值。由于服务器无法将其识别为有效的 Websocket 消息或任何其他相关消息,因此它决定使用 close_notify
终止连接。
关于c++ - SSL_Read() 返回 SSL_ERROR_ZERO_RETURN 但 ERR_get_error() 为 0,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50223224/