出现错误时,SSL_shutdown与SSL_free的调用时机及场景疑问
OpenSSL中SSL_shutdown的调用时机与错误场景处理
问题背景
OpenSSL官方的TLS服务器示例中存在一段未做完整错误检查的代码(如下),其中不管SSL操作是否成功都调用了SSL_shutdown和SSL_free,现明确两个核心问题:
SSL_shutdown的正确调用时机是什么?- 若
SSL_set_fd或SSL_accept出现错误,是否仍需调用SSL_shutdown?
示例代码:
while(1) { struct sockaddr_in addr; unsigned int len = sizeof(addr); SSL *ssl; const char reply[] = "test\n"; int client = accept(sock, (struct sockaddr*)&addr, &len); if (client < 0) { perror("Unable to accept"); exit(EXIT_FAILURE); } ssl = SSL_new(ctx); SSL_set_fd(ssl, client); if (SSL_accept(ssl) <= 0) { ERR_print_errors_fp(stderr); } else { SSL_write(ssl, reply, strlen(reply)); } SSL_shutdown(ssl); SSL_free(ssl); close(client); }
问题解答
1. SSL_shutdown的正确调用时机
SSL_shutdown的作用是优雅关闭已成功建立的TLS会话:它会触发TLS层的双向关闭流程(向对方发送close_notify告警,并等待对方的回应),确保双方都能妥善终止加密会话,避免数据截断或残留的加密上下文资源泄漏。
只有当TLS会话已成功建立(即SSL_accept/SSL_connect返回成功),且完成了所有预期的数据交互时,才需要调用SSL_shutdown来完成优雅关闭。
2. 错误场景下是否需要调用SSL_shutdown
分不同错误场景判断:
- SSL_new调用失败(ssl为空):此时未创建任何有效的SSL对象,直接跳过所有SSL相关操作,关闭客户端socket即可。
- SSL_set_fd调用失败:SSL对象已创建,但尚未与客户端socket绑定成功,TLS会话完全未初始化,无需调用
SSL_shutdown,直接调用SSL_free释放SSL对象,再关闭socket。 - SSL_accept调用失败:
- 如果失败发生在TLS握手完全未建立阶段(比如握手初始步骤就失败,没有任何有效TLS消息交互):无需调用
SSL_shutdown,直接释放SSL对象并关闭socket。 - 如果握手过程中已交换部分TLS消息(比如对方已发送
close_notify):可以尝试调用一次SSL_shutdown,但如果调用失败也无需处理——因为SSL_free会自动清理SSL对象的所有资源,SSL_shutdown只是优雅关闭的可选步骤,而非强制要求。
- 如果失败发生在TLS握手完全未建立阶段(比如握手初始步骤就失败,没有任何有效TLS消息交互):无需调用
对示例代码的修正建议
示例代码的核心问题是未检查SSL_new的返回值,且无条件调用SSL_shutdown,正确的处理逻辑应补充错误检查:
while(1) { struct sockaddr_in addr; unsigned int len = sizeof(addr); SSL *ssl = NULL; const char reply[] = "test\n"; int client = accept(sock, (struct sockaddr*)&addr, &len); if (client < 0) { perror("Unable to accept"); exit(EXIT_FAILURE); } ssl = SSL_new(ctx); if (ssl == NULL) { perror("SSL_new failed"); close(client); continue; } if (SSL_set_fd(ssl, client) != 1) { ERR_print_errors_fp(stderr); SSL_free(ssl); close(client); continue; } int accept_ret = SSL_accept(ssl); if (accept_ret <= 0) { ERR_print_errors_fp(stderr); // 仅在握手部分完成时尝试shutdown,否则直接释放 if (accept_ret == SSL_ERROR_WANT_READ || accept_ret == SSL_ERROR_WANT_WRITE) { (void)SSL_shutdown(ssl); } } else { SSL_write(ssl, reply, strlen(reply)); // 会话正常建立,调用shutdown优雅关闭 (void)SSL_shutdown(ssl); } SSL_free(ssl); close(client); }
内容的提问来源于stack exchange,提问作者spraff
相关产品推荐
相关产品推荐

