增加客户端重试和服务器的详细调试信息
This commit is contained in:
289
tls_client.c
289
tls_client.c
@@ -363,21 +363,93 @@ int create_tcp_connection(const char* hostname, int port) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 执行TLS握手
|
||||
* @brief 分析系统调用错误的详细信息
|
||||
* @param ssl SSL连接对象
|
||||
* @param ret SSL函数返回值
|
||||
* @return void
|
||||
*/
|
||||
void analyze_syscall_error(SSL* ssl, int ret) {
|
||||
int sockfd = SSL_get_fd(ssl);
|
||||
int error = 0;
|
||||
socklen_t len = sizeof(error);
|
||||
|
||||
// 获取套接字错误
|
||||
if (sockfd > 0 && getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len) == 0) {
|
||||
if (error != 0) {
|
||||
fprintf(stderr, "套接字错误代码: %d (%s)\n", error, strerror(error));
|
||||
|
||||
// 详细分析套接字错误
|
||||
switch (error) {
|
||||
case ECONNREFUSED:
|
||||
fprintf(stderr, " -> 连接被拒绝,服务器可能未运行或端口错误\n");
|
||||
break;
|
||||
case ETIMEDOUT:
|
||||
fprintf(stderr, " -> 连接超时,网络可能有问题或服务器响应慢\n");
|
||||
break;
|
||||
case EHOSTUNREACH:
|
||||
fprintf(stderr, " -> 主机不可达,网络路由问题\n");
|
||||
break;
|
||||
case ENETUNREACH:
|
||||
fprintf(stderr, " -> 网络不可达,网络连接问题\n");
|
||||
break;
|
||||
case ECONNRESET:
|
||||
fprintf(stderr, " -> 连接被重置,服务器主动断开连接\n");
|
||||
break;
|
||||
case EPIPE:
|
||||
fprintf(stderr, " -> 管道破裂,连接意外断开\n");
|
||||
break;
|
||||
case EINTR:
|
||||
fprintf(stderr, " -> 系统调用被中断\n");
|
||||
break;
|
||||
case EAGAIN:
|
||||
fprintf(stderr, " -> 资源暂时不可用,可能需要重试\n");
|
||||
break;
|
||||
#ifdef EWOULDBLOCK
|
||||
#if EAGAIN != EWOULDBLOCK
|
||||
case EWOULDBLOCK:
|
||||
fprintf(stderr, " -> 资源暂时不可用,可能需要重试\n");
|
||||
break;
|
||||
#endif
|
||||
#endif
|
||||
default:
|
||||
fprintf(stderr, " -> 其他系统错误\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 检查errno
|
||||
if (errno != 0) {
|
||||
fprintf(stderr, "系统errno: %d (%s)\n", errno, strerror(errno));
|
||||
}
|
||||
|
||||
// 检查SSL内部错误队列
|
||||
unsigned long ssl_err;
|
||||
while ((ssl_err = ERR_get_error()) != 0) {
|
||||
char err_buf[256];
|
||||
ERR_error_string_n(ssl_err, err_buf, sizeof(err_buf));
|
||||
fprintf(stderr, "SSL内部错误: %s\n", err_buf);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 执行TLS握手(带重试机制)
|
||||
* @param ssl SSL连接对象
|
||||
* @param max_retries 最大重试次数
|
||||
* @param base_delay 基础延迟时间(秒)
|
||||
* @return int 成功返回1,失败返回0
|
||||
*/
|
||||
int perform_tls_handshake(SSL* ssl) {
|
||||
int perform_tls_handshake_with_retry(SSL* ssl, int max_retries, int base_delay) {
|
||||
int ret;
|
||||
time_t start_time, end_time, step_time;
|
||||
double handshake_time, step_duration;
|
||||
int retry_count = 0;
|
||||
int sockfd = SSL_get_fd(ssl);
|
||||
|
||||
printf("开始TLS握手...\n");
|
||||
printf("开始TLS握手(最大重试次数: %d)...\n", max_retries);
|
||||
start_time = time(NULL);
|
||||
step_time = start_time;
|
||||
|
||||
// 设置握手超时
|
||||
int sockfd = SSL_get_fd(ssl);
|
||||
if (sockfd > 0) {
|
||||
struct timeval timeout;
|
||||
timeout.tv_sec = 30; // 30秒超时
|
||||
@@ -387,65 +459,146 @@ int perform_tls_handshake(SSL* ssl) {
|
||||
printf("设置握手超时: 30秒\n");
|
||||
}
|
||||
|
||||
// 执行SSL握手
|
||||
printf("正在执行SSL_connect()...\n");
|
||||
ret = SSL_connect(ssl);
|
||||
|
||||
end_time = time(NULL);
|
||||
handshake_time = difftime(end_time, start_time);
|
||||
step_duration = difftime(end_time, step_time);
|
||||
|
||||
printf("SSL_connect() 耗时: %.2f 秒\n", step_duration);
|
||||
|
||||
if (ret != 1) {
|
||||
while (retry_count <= max_retries) {
|
||||
if (retry_count > 0) {
|
||||
int delay = base_delay * (1 << (retry_count - 1)); // 指数退避
|
||||
printf("第 %d 次重试,等待 %d 秒...\n", retry_count, delay);
|
||||
sleep(delay);
|
||||
}
|
||||
|
||||
step_time = time(NULL);
|
||||
printf("正在执行SSL_connect()... (尝试 %d/%d)\n", retry_count + 1, max_retries + 1);
|
||||
|
||||
// 执行SSL握手
|
||||
ret = SSL_connect(ssl);
|
||||
|
||||
end_time = time(NULL);
|
||||
handshake_time = difftime(end_time, start_time);
|
||||
step_duration = difftime(end_time, step_time);
|
||||
|
||||
printf("SSL_connect() 耗时: %.2f 秒\n", step_duration);
|
||||
|
||||
if (ret == 1) {
|
||||
// 握手成功
|
||||
printf("TLS握手成功!\n");
|
||||
printf("总握手耗时: %.2f 秒\n", handshake_time);
|
||||
printf("重试次数: %d\n", retry_count);
|
||||
printf("协议版本: %s\n", SSL_get_version(ssl));
|
||||
printf("密码套件: %s\n", SSL_get_cipher(ssl));
|
||||
|
||||
// 验证服务器证书
|
||||
X509* cert = SSL_get_peer_certificate(ssl);
|
||||
if (cert) {
|
||||
printf("服务器证书验证成功\n");
|
||||
X509_free(cert);
|
||||
} else {
|
||||
printf("警告: 无法获取服务器证书\n");
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// 握手失败,分析错误
|
||||
int err = SSL_get_error(ssl, ret);
|
||||
fprintf(stderr, "TLS握手失败,错误代码: %d\n", err);
|
||||
fprintf(stderr, "TLS握手失败,错误代码: %d (尝试 %d/%d)\n", err, retry_count + 1, max_retries + 1);
|
||||
|
||||
// 详细的错误诊断
|
||||
switch (err) {
|
||||
case SSL_ERROR_SSL:
|
||||
fprintf(stderr, "SSL协议错误,可能是证书验证失败\n");
|
||||
ERR_print_errors_fp(stderr);
|
||||
break;
|
||||
|
||||
case SSL_ERROR_SYSCALL:
|
||||
fprintf(stderr, "系统调用错误\n");
|
||||
fprintf(stderr, "系统调用错误,详细分析:\n");
|
||||
analyze_syscall_error(ssl, ret);
|
||||
|
||||
// 根据系统错误判断是否值得重试
|
||||
if (sockfd > 0) {
|
||||
int error = 0;
|
||||
socklen_t len = sizeof(error);
|
||||
if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len) == 0) {
|
||||
switch (error) {
|
||||
case ECONNREFUSED:
|
||||
case EHOSTUNREACH:
|
||||
case ENETUNREACH:
|
||||
fprintf(stderr, "网络连接问题,建议检查服务器地址和端口\n");
|
||||
break;
|
||||
case ETIMEDOUT:
|
||||
fprintf(stderr, "连接超时,可能是网络延迟或服务器负载高\n");
|
||||
break;
|
||||
case ECONNRESET:
|
||||
case EPIPE:
|
||||
fprintf(stderr, "连接被重置,可能是服务器问题\n");
|
||||
break;
|
||||
case EAGAIN:
|
||||
fprintf(stderr, "资源暂时不可用,适合重试\n");
|
||||
break;
|
||||
#ifdef EWOULDBLOCK
|
||||
#if EAGAIN != EWOULDBLOCK
|
||||
case EWOULDBLOCK:
|
||||
fprintf(stderr, "资源暂时不可用,适合重试\n");
|
||||
break;
|
||||
#endif
|
||||
#endif
|
||||
default:
|
||||
fprintf(stderr, "其他系统错误\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SSL_ERROR_WANT_READ:
|
||||
case SSL_ERROR_WANT_WRITE:
|
||||
fprintf(stderr, "需要更多数据\n");
|
||||
fprintf(stderr, "需要更多数据,这通常是正常的握手过程\n");
|
||||
// 对于WANT_READ/WANT_WRITE,通常不需要重试,这是正常的握手流程
|
||||
if (retry_count == 0) {
|
||||
fprintf(stderr, "这是正常的握手流程,不需要重试\n");
|
||||
return 0;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
fprintf(stderr, "未知SSL错误\n");
|
||||
ERR_print_errors_fp(stderr);
|
||||
break;
|
||||
}
|
||||
|
||||
ERR_print_errors_fp(stderr);
|
||||
retry_count++;
|
||||
|
||||
// 提供解决建议
|
||||
fprintf(stderr, "\n解决建议:\n");
|
||||
fprintf(stderr, "1. 使用 --no-verify 选项禁用证书验证\n");
|
||||
fprintf(stderr, "2. 检查服务器证书是否有效\n");
|
||||
fprintf(stderr, "3. 确保CA证书文件正确\n");
|
||||
fprintf(stderr, "4. 检查系统时间是否正确\n");
|
||||
|
||||
return 0;
|
||||
// 如果不是最后一次尝试,提供重试建议
|
||||
if (retry_count <= max_retries) {
|
||||
fprintf(stderr, "\n重试建议:\n");
|
||||
fprintf(stderr, "1. 检查网络连接\n");
|
||||
fprintf(stderr, "2. 验证服务器地址和端口\n");
|
||||
fprintf(stderr, "3. 检查防火墙设置\n");
|
||||
fprintf(stderr, "4. 尝试使用 --no-verify 选项\n");
|
||||
fprintf(stderr, "5. 检查证书文件是否正确\n");
|
||||
}
|
||||
}
|
||||
|
||||
// 获取连接信息
|
||||
printf("TLS握手成功!\n");
|
||||
printf("握手耗时: %.2f 秒\n", handshake_time);
|
||||
printf("协议版本: %s\n", SSL_get_version(ssl));
|
||||
printf("密码套件: %s\n", SSL_get_cipher(ssl));
|
||||
// 所有重试都失败了
|
||||
fprintf(stderr, "\n所有重试尝试都失败了\n");
|
||||
fprintf(stderr, "最终解决建议:\n");
|
||||
fprintf(stderr, "1. 使用 --no-verify 选项禁用证书验证\n");
|
||||
fprintf(stderr, "2. 检查服务器证书是否有效\n");
|
||||
fprintf(stderr, "3. 确保CA证书文件正确\n");
|
||||
fprintf(stderr, "4. 检查系统时间是否正确\n");
|
||||
fprintf(stderr, "5. 检查网络连接和防火墙设置\n");
|
||||
fprintf(stderr, "6. 尝试使用不同的TLS版本\n");
|
||||
|
||||
// 验证服务器证书
|
||||
X509* cert = SSL_get_peer_certificate(ssl);
|
||||
if (cert) {
|
||||
printf("服务器证书验证成功\n");
|
||||
X509_free(cert);
|
||||
} else {
|
||||
printf("警告: 无法获取服务器证书\n");
|
||||
}
|
||||
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief 执行TLS握手(保持向后兼容)
|
||||
* @param ssl SSL连接对象
|
||||
* @return int 成功返回1,失败返回0
|
||||
*/
|
||||
int perform_tls_handshake(SSL* ssl) {
|
||||
// 使用默认重试参数:最多重试3次,基础延迟2秒
|
||||
return perform_tls_handshake_with_retry(ssl, 3, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -549,10 +702,16 @@ int receive_response(SSL* ssl) {
|
||||
* @param cert_file 客户端证书文件
|
||||
* @param key_file 客户端私钥文件
|
||||
* @param ca_file CA证书文件
|
||||
* @param verify_peer 是否验证服务器证书
|
||||
* @param tls_version TLS版本
|
||||
* @param debug_level 调试级别
|
||||
* @param max_retries 最大重试次数
|
||||
* @param retry_delay 重试延迟
|
||||
* @return int 成功返回1,失败返回0
|
||||
*/
|
||||
int perform_tls_test(const char* hostname, int port, const char* cert_file,
|
||||
const char* key_file, const char* ca_file, int verify_peer, int tls_version, int debug_level) {
|
||||
const char* key_file, const char* ca_file, int verify_peer,
|
||||
int tls_version, int debug_level, int max_retries, int retry_delay) {
|
||||
int sockfd;
|
||||
SSL* ssl;
|
||||
SSL_CTX* local_ctx = NULL;
|
||||
@@ -590,8 +749,8 @@ int perform_tls_test(const char* hostname, int port, const char* cert_file,
|
||||
// 将套接字绑定到SSL连接
|
||||
SSL_set_fd(ssl, sockfd);
|
||||
|
||||
// 执行TLS握手
|
||||
if (perform_tls_handshake(ssl)) {
|
||||
// 执行TLS握手(使用指定的重试参数)
|
||||
if (perform_tls_handshake_with_retry(ssl, max_retries, retry_delay)) {
|
||||
// 发送测试数据
|
||||
if (send_test_data(ssl)) {
|
||||
// 接收响应
|
||||
@@ -679,11 +838,16 @@ int perform_tls_test(const char* hostname, int port, const char* cert_file,
|
||||
* @param verify_peer 是否验证服务器证书
|
||||
* @param test_count 测试次数 (-1表示无限循环)
|
||||
* @param interval 测试间隔
|
||||
* @param continue_on_failure 失败时是否继续
|
||||
* @param debug_level 调试级别
|
||||
* @param max_retries 最大重试次数
|
||||
* @param retry_delay 重试延迟
|
||||
* @return int 成功返回1,失败返回0
|
||||
*/
|
||||
int perform_tls_version_test(const char* hostname, int port, const char* cert_file,
|
||||
const char* key_file, const char* ca_file, int verify_peer,
|
||||
int test_count, int interval, int continue_on_failure, int debug_level) {
|
||||
int test_count, int interval, int continue_on_failure,
|
||||
int debug_level, int max_retries, int retry_delay) {
|
||||
int test_num = 0;
|
||||
int success_count = 0;
|
||||
int total_tests = 0;
|
||||
@@ -704,7 +868,7 @@ int perform_tls_version_test(const char* hostname, int port, const char* cert_fi
|
||||
|
||||
// 测试TLS 1.2
|
||||
printf("\n--- 第 %d 次测试 - TLS 1.2 ---\n", test_num);
|
||||
if (perform_tls_test(hostname, port, cert_file, key_file, ca_file, verify_peer, 1, debug_level)) {
|
||||
if (perform_tls_test(hostname, port, cert_file, key_file, ca_file, verify_peer, 1, debug_level, max_retries, retry_delay)) {
|
||||
printf("✓ TLS 1.2 测试成功\n");
|
||||
success_count++;
|
||||
} else {
|
||||
@@ -724,7 +888,7 @@ int perform_tls_version_test(const char* hostname, int port, const char* cert_fi
|
||||
|
||||
// 测试TLS 1.3
|
||||
printf("\n--- 第 %d 次测试 - TLS 1.3 ---\n", test_num);
|
||||
if (perform_tls_test(hostname, port, cert_file, key_file, ca_file, verify_peer, 2, debug_level)) {
|
||||
if (perform_tls_test(hostname, port, cert_file, key_file, ca_file, verify_peer, 2, debug_level, max_retries, retry_delay)) {
|
||||
printf("✓ TLS 1.3 测试成功\n");
|
||||
success_count++;
|
||||
} else {
|
||||
@@ -778,11 +942,14 @@ void print_usage(const char* program_name) {
|
||||
printf(" -t <version> TLS版本 (1=TLS1.2, 2=TLS1.3, 3=循环测试1.2和1.3)\n");
|
||||
printf(" -f 失败时继续测试(默认:失败时退出)\n");
|
||||
printf(" -d <level> OpenSSL调试级别 (0=无, 1=基本, 2=详细, 3=完整)\n");
|
||||
printf(" -r <retries> 握手重试次数 (默认: 3)\n");
|
||||
printf(" -w <delay> 重试基础延迟秒数 (默认: 2)\n");
|
||||
printf(" --no-verify 禁用服务器证书验证\n");
|
||||
printf(" --help 显示此帮助信息\n");
|
||||
printf("\n示例:\n");
|
||||
printf(" %s -h 192.168.1.100 -p 8443 -c client.crt -k client.key\n", program_name);
|
||||
printf(" %s -h example.com -p 443 -c client.crt -k client.key -a ca.crt -n 10\n", program_name);
|
||||
printf(" %s -h server.com -c client.crt -k client.key -r 5 -w 3\n", program_name);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
@@ -798,6 +965,8 @@ int main(int argc, char *argv[]) {
|
||||
int tls_version = 3; // 默认循环测试TLS 1.2和1.3
|
||||
int continue_on_failure = 0; // 失败时是否继续测试
|
||||
int debug_level = 0; // OpenSSL调试级别
|
||||
int max_retries = 3; // 默认重试次数
|
||||
int retry_delay = 2; // 默认重试延迟
|
||||
int opt;
|
||||
int test_num = 0;
|
||||
int success_count = 0;
|
||||
@@ -821,7 +990,7 @@ int main(int argc, char *argv[]) {
|
||||
}
|
||||
|
||||
// 解析命令行参数
|
||||
while ((opt = getopt(argc, argv, "h:p:c:k:a:n:i:vxt:fd:")) != -1) {
|
||||
while ((opt = getopt(argc, argv, "h:p:c:k:a:n:i:vxt:fd:r:w:")) != -1) {
|
||||
switch (opt) {
|
||||
case 'h':
|
||||
strncpy(hostname, optarg, sizeof(hostname) - 1);
|
||||
@@ -869,6 +1038,22 @@ int main(int argc, char *argv[]) {
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case 'r':
|
||||
max_retries = atoi(optarg);
|
||||
if (max_retries < 0 || max_retries > 10) {
|
||||
fprintf(stderr, "错误: 重试次数必须是0-10\n");
|
||||
print_usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
case 'w':
|
||||
retry_delay = atoi(optarg);
|
||||
if (retry_delay < 1 || retry_delay > 60) {
|
||||
fprintf(stderr, "错误: 重试延迟必须是1-60秒\n");
|
||||
print_usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
print_usage(argv[0]);
|
||||
return 1;
|
||||
@@ -890,7 +1075,7 @@ int main(int argc, char *argv[]) {
|
||||
// 循环测试TLS 1.2和1.3
|
||||
return perform_tls_version_test(hostname, port, cert_file, key_file,
|
||||
ca_file[0] ? ca_file : NULL, !no_verify,
|
||||
test_count, interval, continue_on_failure, debug_level);
|
||||
test_count, interval, continue_on_failure, debug_level, max_retries, retry_delay);
|
||||
} else {
|
||||
// 单版本测试
|
||||
// 创建SSL上下文
|
||||
@@ -930,7 +1115,7 @@ int main(int argc, char *argv[]) {
|
||||
}
|
||||
|
||||
// 执行TLS测试
|
||||
if (perform_tls_test(hostname, port, cert_file, key_file, ca_file, !no_verify, tls_version, debug_level)) {
|
||||
if (perform_tls_test(hostname, port, cert_file, key_file, ca_file, !no_verify, tls_version, debug_level, max_retries, retry_delay)) {
|
||||
printf("✓ 第 %d 次测试成功\n", test_num);
|
||||
success_count++;
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user