/** * @file tls_client.c * @brief TLS 1.2/1.3 客户端实现 * @author Assistant * @date 2024 * @version 1.0.0 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define MAX_BUFFER_SIZE 4096 #define TEST_DATA_SIZE 1024 #define MAX_RETRIES 3 #define CONNECT_TIMEOUT 10 // 全局SSL上下文 SSL_CTX *ssl_ctx = NULL; /** * @brief 格式化时间戳为易读格式 */ void print_timestamp(time_t timestamp) { struct tm *tm_info; char time_str[32]; tm_info = gmtime(×tamp); strftime(time_str, sizeof(time_str), "%Y-%m-%d %H:%M:%S UTC", tm_info); printf("[%s]", time_str); } /** * @brief SSL信息回调函数 */ void ssl_info_callback(const SSL *ssl, int type, int val) { const char *str; static time_t last_time = 0; time_t current_time; // 检查SSL对象是否有效 if (!ssl) { printf("SSL回调: SSL对象为空\n"); return; } current_time = time(NULL); switch (type) { case SSL_CB_LOOP: str = SSL_state_string_long(ssl); if (str) { // 计算与上次状态的时间差 if (last_time > 0) { double time_diff = difftime(current_time, last_time); if (time_diff > 0.5) { // 只显示超过0.5秒的延迟 print_timestamp(current_time); printf(" SSL状态: %s (延迟: %.1f秒)\n", str, time_diff); } else { print_timestamp(current_time); printf(" SSL状态: %s\n", str); } } else { print_timestamp(current_time); printf(" SSL状态: %s\n", str); } last_time = current_time; } break; case SSL_CB_ALERT: str = SSL_alert_type_string_long(val); if (str) { print_timestamp(current_time); printf(" SSL警告: %s\n", str); } break; case SSL_CB_EXIT: if (val == 0) { str = SSL_state_string_long(ssl); if (str) { print_timestamp(current_time); printf(" SSL退出: %s\n", str); } } else if (val < 0) { str = SSL_state_string_long(ssl); if (str) { print_timestamp(current_time); printf(" SSL错误: %s\n", str); } } break; case SSL_CB_HANDSHAKE_START: print_timestamp(current_time); printf(" SSL握手开始\n"); last_time = current_time; break; case SSL_CB_HANDSHAKE_DONE: print_timestamp(current_time); printf(" SSL握手完成\n"); break; default: // 过滤掉重复的4097类型回调 if (type != 4097) { print_timestamp(current_time); printf(" SSL回调: 类型=%d, 值=%d\n", type, val); } break; } } /** * @brief SSL消息回调函数 */ void ssl_msg_callback(int write_p, int version, int content_type, const void *buf, size_t len, SSL *ssl, void *arg) { const char *direction = write_p ? "发送" : "接收"; const char *content_name = ""; time_t current_time; // 检查参数有效性 if (!buf || len == 0) { return; } current_time = time(NULL); switch (content_type) { case SSL3_RT_CHANGE_CIPHER_SPEC: content_name = "ChangeCipherSpec"; break; case SSL3_RT_ALERT: content_name = "Alert"; break; case SSL3_RT_HANDSHAKE: content_name = "Handshake"; break; case SSL3_RT_APPLICATION_DATA: content_name = "ApplicationData"; break; default: content_name = "Unknown"; break; } // 只显示重要的握手消息,过滤掉重复的Unknown消息 if (content_type == SSL3_RT_HANDSHAKE || content_type == SSL3_RT_CHANGE_CIPHER_SPEC) { print_timestamp(current_time); printf(" SSL消息: %s %s (版本: %d, 长度: %zu)\n", direction, content_name, version, len); } } /** * @brief 初始化OpenSSL库 * @param debug_level 调试级别 (0=无调试, 1=基本, 2=详细, 3=完整) */ void init_openssl(int debug_level) { SSL_library_init(); SSL_load_error_strings(); OpenSSL_add_all_algorithms(); if (debug_level > 0) { // 启用OpenSSL调试 if (debug_level >= 1) { printf("启用OpenSSL基本调试\n"); } if (debug_level >= 2) { printf("启用OpenSSL详细调试\n"); } if (debug_level >= 3) { printf("启用OpenSSL完整调试\n"); } } } /** * @brief 清理OpenSSL资源 */ void cleanup_openssl() { if (ssl_ctx) { SSL_CTX_free(ssl_ctx); } EVP_cleanup(); ERR_free_strings(); } /** * @brief 创建SSL上下文 * @param cert_file 客户端证书文件路径 * @param key_file 客户端私钥文件路径 * @param ca_file CA证书文件路径 * @param verify_peer 是否验证服务器证书 * @param tls_version TLS版本 (1=TLS1.2, 2=TLS1.3, 0=自动) * @return SSL_CTX* SSL上下文指针,失败返回NULL */ SSL_CTX* create_ssl_context(const char* cert_file, const char* key_file, const char* ca_file, int verify_peer, int tls_version, int debug_level) { SSL_CTX *ctx; // 创建SSL上下文,支持TLS 1.2和1.3 ctx = SSL_CTX_new(TLS_client_method()); if (!ctx) { fprintf(stderr, "无法创建SSL上下文\n"); return NULL; } // 设置调试回调(如果启用调试) if (debug_level >= 2) { SSL_CTX_set_info_callback(ctx, ssl_info_callback); } if (debug_level >= 3) { SSL_CTX_set_msg_callback(ctx, ssl_msg_callback); } // 根据指定版本设置TLS版本 if (tls_version == 1) { // 只使用TLS 1.2 if (SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION) != 1 || SSL_CTX_set_max_proto_version(ctx, TLS1_2_VERSION) != 1) { fprintf(stderr, "设置TLS 1.2版本失败\n"); SSL_CTX_free(ctx); return NULL; } printf("配置为TLS 1.2模式\n"); } else if (tls_version == 2) { // 只使用TLS 1.3 if (SSL_CTX_set_min_proto_version(ctx, TLS1_3_VERSION) != 1 || SSL_CTX_set_max_proto_version(ctx, TLS1_3_VERSION) != 1) { fprintf(stderr, "设置TLS 1.3版本失败\n"); SSL_CTX_free(ctx); return NULL; } printf("配置为TLS 1.3模式\n"); } else { // 自动模式:支持TLS 1.2和1.3 if (SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION) != 1) { fprintf(stderr, "设置最小TLS版本失败\n"); SSL_CTX_free(ctx); return NULL; } if (SSL_CTX_set_max_proto_version(ctx, TLS1_3_VERSION) != 1) { fprintf(stderr, "设置最大TLS版本失败\n"); SSL_CTX_free(ctx); return NULL; } printf("配置为TLS 1.2/1.3自动模式\n"); } // 加载客户端证书 printf("加载客户端证书: %s\n", cert_file); if (SSL_CTX_use_certificate_file(ctx, cert_file, SSL_FILETYPE_PEM) != 1) { fprintf(stderr, "加载客户端证书失败: %s\n", cert_file); ERR_print_errors_fp(stderr); SSL_CTX_free(ctx); return NULL; } printf("客户端证书加载成功\n"); // 加载客户端私钥 printf("加载客户端私钥: %s\n", key_file); if (SSL_CTX_use_PrivateKey_file(ctx, key_file, SSL_FILETYPE_PEM) != 1) { fprintf(stderr, "加载客户端私钥失败: %s\n", key_file); ERR_print_errors_fp(stderr); SSL_CTX_free(ctx); return NULL; } printf("客户端私钥加载成功\n"); // 验证私钥与证书匹配 printf("验证私钥与证书匹配...\n"); if (SSL_CTX_check_private_key(ctx) != 1) { fprintf(stderr, "私钥与证书不匹配\n"); ERR_print_errors_fp(stderr); SSL_CTX_free(ctx); return NULL; } printf("私钥与证书匹配验证成功\n"); // 加载CA证书用于验证服务器证书 if (ca_file && SSL_CTX_load_verify_locations(ctx, ca_file, NULL) != 1) { fprintf(stderr, "加载CA证书失败: %s\n", ca_file); SSL_CTX_free(ctx); return NULL; } // 设置验证模式 if (verify_peer) { SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL); } else { SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL); printf("警告: 已禁用服务器证书验证\n"); } // 设置验证深度 SSL_CTX_set_verify_depth(ctx, 4); return ctx; } /** * @brief 创建TCP连接 * @param hostname 服务器主机名 * @param port 服务器端口 * @return int 套接字文件描述符,失败返回-1 */ int create_tcp_connection(const char* hostname, int port) { int sockfd; struct sockaddr_in server_addr; int opt = 1; // 创建套接字 sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd < 0) { perror("创建套接字失败"); return -1; } // 设置套接字选项,避免RST包问题 if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) { perror("设置SO_REUSEADDR失败"); close(sockfd); return -1; } // 设置TCP_NODELAY选项,减少延迟 if (setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt)) < 0) { perror("设置TCP_NODELAY失败"); close(sockfd); return -1; } // 设置服务器地址 memset(&server_addr, 0, sizeof(server_addr)); server_addr.sin_family = AF_INET; server_addr.sin_port = htons(port); // 解析主机名 if (inet_pton(AF_INET, hostname, &server_addr.sin_addr) <= 0) { fprintf(stderr, "无效的IP地址: %s\n", hostname); close(sockfd); return -1; } // 连接到服务器 if (connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) { perror("连接服务器失败"); close(sockfd); return -1; } printf("成功连接到服务器 %s:%d\n", hostname, port); return sockfd; } /** * @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_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握手(最大重试次数: %d)...\n", max_retries); start_time = time(NULL); // 设置握手超时 if (sockfd > 0) { struct timeval timeout; timeout.tv_sec = 30; // 30秒超时 timeout.tv_usec = 0; setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout)); printf("设置握手超时: 30秒\n"); } 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 (尝试 %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"); 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"); // 对于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; } retry_count++; // 如果不是最后一次尝试,提供重试建议 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"); } } // 所有重试都失败了 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"); 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); } /** * @brief 发送测试数据 * @param ssl SSL连接对象 * @return int 成功返回1,失败返回0 */ int send_test_data(SSL* ssl) { char test_data[TEST_DATA_SIZE]; int bytes_sent; time_t start_time, end_time; double send_time; // 生成1KB测试数据 for (int i = 0; i < TEST_DATA_SIZE; i++) { test_data[i] = 'A' + (i % 26); } printf("发送 %d 字节测试数据...\n", TEST_DATA_SIZE); start_time = time(NULL); // 发送数据 bytes_sent = SSL_write(ssl, test_data, TEST_DATA_SIZE); end_time = time(NULL); send_time = difftime(end_time, start_time); printf("SSL_write() 耗时: %.2f 秒\n", send_time); if (bytes_sent <= 0) { int err = SSL_get_error(ssl, bytes_sent); fprintf(stderr, "发送数据失败,错误代码: %d\n", err); return 0; } printf("成功发送 %d 字节数据\n", bytes_sent); return 1; } /** * @brief 接收服务器响应 * @param ssl SSL连接对象 * @return int 成功返回1,失败返回0 */ int receive_response(SSL* ssl) { char buffer[MAX_BUFFER_SIZE]; int bytes_received; time_t start_time, end_time; double receive_time; int timeout_seconds = 30; // 30秒超时 printf("等待服务器响应(超时: %d秒)...\n", timeout_seconds); start_time = time(NULL); // 设置socket超时 int sockfd = SSL_get_fd(ssl); if (sockfd > 0) { struct timeval timeout; timeout.tv_sec = timeout_seconds; timeout.tv_usec = 0; setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)); } // 接收数据 bytes_received = SSL_read(ssl, buffer, MAX_BUFFER_SIZE - 1); end_time = time(NULL); receive_time = difftime(end_time, start_time); printf("SSL_read() 耗时: %.2f 秒\n", receive_time); // 检查是否超时 if (receive_time >= timeout_seconds) { printf("⚠ 警告: 服务器响应超时(%.2f秒)\n", receive_time); } if (bytes_received <= 0) { int err = SSL_get_error(ssl, bytes_received); fprintf(stderr, "接收数据失败,错误代码: %d\n", err); return 0; } buffer[bytes_received] = '\0'; printf("接收到 %d 字节响应:\n", bytes_received); printf("--- 响应内容 ---\n"); printf("%s\n", buffer); printf("--- 响应结束 ---\n"); // 检查是否收到预期的HTTP响应 if (strstr(buffer, "HTTP/1.1 200 OK") && strstr(buffer, "Hello from TLS server!")) { printf("✓ 收到预期的HTTP响应\n"); } else { printf("⚠ 响应内容与预期不符\n"); } return 1; } /** * @brief 执行单次TLS测试 * @param hostname 服务器主机名 * @param port 服务器端口 * @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, int max_retries, int retry_delay) { int sockfd; SSL* ssl; SSL_CTX* local_ctx = NULL; int success = 0; // 创建TCP连接 sockfd = create_tcp_connection(hostname, port); if (sockfd < 0) { return 0; } // 创建本地SSL上下文(如果提供了tls_version参数) if (tls_version > 0) { printf("为TLS版本 %d 创建SSL上下文...\n", tls_version); local_ctx = create_ssl_context(cert_file, key_file, ca_file, verify_peer, tls_version, debug_level); if (!local_ctx) { fprintf(stderr, "创建SSL上下文失败\n"); close(sockfd); return 0; } printf("SSL上下文创建成功\n"); } // 创建SSL连接 ssl = SSL_new(local_ctx ? local_ctx : ssl_ctx); if (!ssl) { fprintf(stderr, "创建SSL连接失败\n"); if (local_ctx) { SSL_CTX_free(local_ctx); } close(sockfd); return 0; } // 将套接字绑定到SSL连接 SSL_set_fd(ssl, sockfd); // 执行TLS握手(使用指定的重试参数) if (perform_tls_handshake_with_retry(ssl, max_retries, retry_delay)) { // 发送测试数据 if (send_test_data(ssl)) { // 接收响应 if (receive_response(ssl)) { success = 1; } } } // 安全关闭SSL连接和套接字 if (ssl) { // 检查SSL连接状态 int shutdown_state = SSL_get_shutdown(ssl); printf("SSL关闭前状态: %d\n", shutdown_state); if (shutdown_state == 0) { // 连接仍然活跃,需要优雅关闭 printf("开始优雅关闭SSL连接...\n"); // 设置SSL关闭模式为双向关闭 SSL_set_shutdown(ssl, SSL_SENT_SHUTDOWN | SSL_RECEIVED_SHUTDOWN); // 尝试优雅关闭SSL连接 int ret = SSL_shutdown(ssl); printf("第一次SSL_shutdown返回: %d\n", ret); if (ret == 0) { // 需要再次调用SSL_shutdown完成双向关闭 printf("执行第二次SSL关闭...\n"); ret = SSL_shutdown(ssl); printf("第二次SSL_shutdown返回: %d\n", ret); } // 等待一段时间确保关闭完成 usleep(100000); // 等待100ms } // 释放SSL对象 SSL_free(ssl); } // 关闭套接字 if (sockfd > 0) { // 检查套接字状态 int error = 0; socklen_t len = sizeof(error); if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len) == 0) { if (error != 0) { printf("套接字错误: %d\n", error); } } // 设置套接字选项,避免TIME_WAIT状态 int opt = 1; if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) { perror("设置SO_REUSEADDR失败"); } // 设置LINGER选项,确保数据发送完成 struct linger linger_opt; linger_opt.l_onoff = 1; linger_opt.l_linger = 5; // 等待5秒 if (setsockopt(sockfd, SOL_SOCKET, SO_LINGER, &linger_opt, sizeof(linger_opt)) < 0) { perror("设置SO_LINGER失败"); } // 关闭套接字 close(sockfd); } if (local_ctx) { SSL_CTX_free(local_ctx); } return success; } /** * @brief 执行TLS版本循环测试 * @param hostname 服务器主机名 * @param port 服务器端口 * @param cert_file 客户端证书文件 * @param key_file 客户端私钥文件 * @param ca_file CA证书文件 * @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 max_retries, int retry_delay) { int test_num = 0; int success_count = 0; int total_tests = 0; time_t start_time, end_time; printf("=== TLS版本循环测试开始 ===\n"); printf("服务器: %s:%d\n", hostname, port); printf("测试模式: TLS 1.2 -> TLS 1.3 -> 重复\n"); printf("测试次数: %s\n", test_count == -1 ? "无限循环" : "有限次数"); printf("测试间隔: %d 秒\n", interval); printf("========================\n\n"); start_time = time(NULL); while (test_count == -1 || test_num < test_count) { test_num++; total_tests++; // 测试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, max_retries, retry_delay)) { printf("✓ TLS 1.2 测试成功\n"); success_count++; } else { printf("✗ TLS 1.2 测试失败\n"); if (!continue_on_failure) { printf("TLS 1.2 测试失败,退出测试循环\n"); break; // 失败时退出循环 } else { printf("TLS 1.2 测试失败,继续测试\n"); } } // 等待间隔 if (interval > 0) { sleep(interval); } // 测试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, max_retries, retry_delay)) { printf("✓ TLS 1.3 测试成功\n"); success_count++; } else { printf("✗ TLS 1.3 测试失败\n"); if (!continue_on_failure) { printf("TLS 1.3 测试失败,退出测试循环\n"); break; // 失败时退出循环 } else { printf("TLS 1.3 测试失败,继续测试\n"); } } // 如果不是最后一次测试,等待指定间隔 if (test_count == -1 || test_num < test_count) { if (interval > 0) { printf("等待 %d 秒后进行下次测试...\n", interval); sleep(interval); } } } end_time = time(NULL); // 打印测试统计 printf("\n=== 测试统计 ===\n"); printf("总测试次数: %d (TLS 1.2: %d次, TLS 1.3: %d次)\n", total_tests * 2, total_tests, total_tests); printf("成功次数: %d\n", success_count); printf("失败次数: %d\n", total_tests * 2 - success_count); printf("成功率: %.2f%%\n", total_tests > 0 ? (double)success_count / (total_tests * 2) * 100 : 0); printf("总耗时: %ld 秒\n", end_time - start_time); printf("===============\n"); return success_count > 0 ? 1 : 0; } /** * @brief 打印使用说明 */ void print_usage(const char* program_name) { printf("用法: %s [选项]\n", program_name); printf("选项:\n"); printf(" -h 服务器主机名或IP地址 (默认: 127.0.0.1)\n"); printf(" -p 服务器端口 (默认: 443)\n"); printf(" -c 客户端证书文件 (必需)\n"); printf(" -k 客户端私钥文件 (必需)\n"); printf(" -a CA证书文件 (可选)\n"); printf(" -n 测试次数 (默认: 无限循环)\n"); printf(" -i 测试间隔秒数 (默认: 1)\n"); printf(" -v 详细输出\n"); printf(" -x 禁用服务器证书验证\n"); printf(" -t TLS版本 (1=TLS1.2, 2=TLS1.3, 3=循环测试1.2和1.3)\n"); printf(" -f 失败时继续测试(默认:失败时退出)\n"); printf(" -d OpenSSL调试级别 (0=无, 1=基本, 2=详细, 3=完整)\n"); printf(" -r 握手重试次数 (默认: 3)\n"); printf(" -w 重试基础延迟秒数 (默认: 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[]) { char hostname[256] = "127.0.0.1"; int port = 443; char cert_file[256] = ""; char key_file[256] = ""; char ca_file[256] = ""; int test_count = -1; // -1表示无限循环 int interval = 1; int verbose = 0; int no_verify = 0; // 是否禁用服务器证书验证 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; int total_tests = 0; time_t start_time, end_time; // 预处理长选项,将它们转换为短选项 for (int i = 1; i < argc; i++) { if (strcmp(argv[i], "--no-verify") == 0) { no_verify = 1; // 移除这个参数,避免getopt处理 for (int j = i; j < argc - 1; j++) { argv[j] = argv[j + 1]; } argc--; i--; } else if (strcmp(argv[i], "--help") == 0) { print_usage(argv[0]); return 0; } } // 解析命令行参数 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); break; case 'p': port = atoi(optarg); break; case 'c': strncpy(cert_file, optarg, sizeof(cert_file) - 1); break; case 'k': strncpy(key_file, optarg, sizeof(key_file) - 1); break; case 'a': strncpy(ca_file, optarg, sizeof(ca_file) - 1); break; case 'n': test_count = atoi(optarg); break; case 'i': interval = atoi(optarg); break; case 'v': verbose = 1; break; case 'x': no_verify = 1; break; case 't': tls_version = atoi(optarg); if (tls_version < 1 || tls_version > 3) { fprintf(stderr, "错误: TLS版本必须是1、2或3\n"); print_usage(argv[0]); return 1; } break; case 'f': continue_on_failure = 1; break; case 'd': debug_level = atoi(optarg); if (debug_level < 0 || debug_level > 3) { fprintf(stderr, "错误: 调试级别必须是0-3\n"); print_usage(argv[0]); 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; } } // 检查必需参数 if (strlen(cert_file) == 0 || strlen(key_file) == 0) { fprintf(stderr, "错误: 必须指定客户端证书和私钥文件\n"); print_usage(argv[0]); return 1; } // 初始化OpenSSL init_openssl(debug_level); // 根据TLS版本选择测试模式 if (tls_version == 3) { // 循环测试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, max_retries, retry_delay); } else { // 单版本测试 // 创建SSL上下文 ssl_ctx = create_ssl_context(cert_file, key_file, ca_file[0] ? ca_file : NULL, !no_verify, tls_version, debug_level); if (!ssl_ctx) { fprintf(stderr, "创建SSL上下文失败\n"); cleanup_openssl(); return 1; } } printf("=== TLS客户端测试程序 ===\n"); printf("服务器: %s:%d\n", hostname, port); printf("客户端证书: %s\n", cert_file); printf("客户端私钥: %s\n", key_file); if (strlen(ca_file) > 0) { printf("CA证书: %s\n", ca_file); } printf("测试次数: %s\n", test_count == -1 ? "无限循环" : "有限次数"); if (test_count != -1) { printf("计划测试: %d 次\n", test_count); } printf("测试间隔: %d 秒\n", interval); printf("========================\n\n"); start_time = time(NULL); // 开始测试循环 while (test_count == -1 || test_num < test_count) { test_num++; total_tests++; printf("\n--- 第 %d 次测试 ---\n", test_num); if (verbose) { printf("开始时间: %s", ctime(&start_time)); } // 执行TLS测试 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 { printf("✗ 第 %d 次测试失败\n", test_num); if (!continue_on_failure) { printf("测试失败,退出测试循环\n"); break; // 失败时退出循环 } else { printf("测试失败,继续测试\n"); } } // 如果不是最后一次测试,等待指定间隔 if (test_count == -1 || test_num < test_count) { if (interval > 0) { printf("等待 %d 秒后进行下次测试...\n", interval); sleep(interval); } } } end_time = time(NULL); // 打印测试统计 printf("\n=== 测试统计 ===\n"); printf("总测试次数: %d\n", total_tests); printf("成功次数: %d\n", success_count); printf("失败次数: %d\n", total_tests - success_count); printf("成功率: %.2f%%\n", total_tests > 0 ? (double)success_count / total_tests * 100 : 0); printf("总耗时: %ld 秒\n", end_time - start_time); printf("===============\n"); // 清理资源 cleanup_openssl(); return 0; }