Files
multitls/tls_server.c
huanglinhuan dbb413b49d first commit
2025-10-14 20:06:34 +08:00

498 lines
16 KiB
C
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/md5.h>
#include <openssl/conf.h>
#include <signal.h>
#include <getopt.h>
#include <stdarg.h>
#define MAX_CLIENTS 100
#define BUFFER_SIZE 4096
#define MAX_MESSAGE_SIZE 4096
#define CLIENT_TIMEOUT 30 // 客户端超时时间(秒)
// 全局变量
SSL_CTX *ssl_ctx;
int server_socket;
int client_count = 0;
pthread_mutex_t client_mutex = PTHREAD_MUTEX_INITIALIZER;
// IP统计结构体
typedef struct {
char ip[INET_ADDRSTRLEN];
int total_connections;
int successful_connections;
time_t first_connection_time;
time_t last_connection_time;
} ip_stats_t;
ip_stats_t ip_statistics[MAX_CLIENTS];
pthread_mutex_t ip_stats_mutex = PTHREAD_MUTEX_INITIALIZER;
// 时间戳日志函数
void log_with_timestamp(const char *format, ...) {
time_t now;
struct tm *tm_info;
char timestamp[64];
va_list args;
time(&now);
tm_info = localtime(&now);
strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", tm_info);
printf("[%s] ", timestamp);
va_start(args, format);
vprintf(format, args);
va_end(args);
}
// 更新IP统计信息
void update_ip_stats(const char *ip, int success) {
pthread_mutex_lock(&ip_stats_mutex);
// 查找或创建IP统计记录
int index = -1;
for (int i = 0; i < MAX_CLIENTS; i++) {
if (strlen(ip_statistics[i].ip) == 0) {
index = i;
strcpy(ip_statistics[i].ip, ip);
ip_statistics[i].total_connections = 0;
ip_statistics[i].successful_connections = 0;
ip_statistics[i].first_connection_time = time(NULL);
break;
} else if (strcmp(ip_statistics[i].ip, ip) == 0) {
index = i;
break;
}
}
if (index >= 0) {
ip_statistics[index].total_connections++;
if (success) {
ip_statistics[index].successful_connections++;
}
ip_statistics[index].last_connection_time = time(NULL);
// 每10次连接统计一次
if (ip_statistics[index].total_connections % 10 == 0) {
double success_rate = (double)ip_statistics[index].successful_connections /
ip_statistics[index].total_connections * 100.0;
time_t duration = ip_statistics[index].last_connection_time -
ip_statistics[index].first_connection_time;
log_with_timestamp("IP统计 [%s]: 总次数=%d, 成功次数=%d, 成功率=%.2f%%, 耗时=%ld秒\n",
ip, ip_statistics[index].total_connections,
ip_statistics[index].successful_connections,
success_rate, duration);
}
}
pthread_mutex_unlock(&ip_stats_mutex);
}
// 客户端结构体
typedef struct {
int socket;
SSL *ssl;
pthread_t thread;
int active;
} client_t;
client_t clients[MAX_CLIENTS];
// 初始化OpenSSL
void init_openssl() {
SSL_load_error_strings();
OpenSSL_add_ssl_algorithms();
SSL_library_init();
}
// 清理OpenSSL
void cleanup_openssl() {
EVP_cleanup();
ERR_free_strings();
}
// 创建SSL上下文
SSL_CTX *create_context() {
const SSL_METHOD *method;
SSL_CTX *ctx;
// 使用TLS方法支持1.2和1.3
method = TLS_server_method();
ctx = SSL_CTX_new(method);
if (!ctx) {
perror("无法创建SSL上下文");
ERR_print_errors_fp(stderr);
exit(EXIT_FAILURE);
}
// 设置最小TLS版本为1.2
SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION);
// 加载证书和私钥从cert目录加载
if (SSL_CTX_use_certificate_file(ctx, "cert/server.crt", SSL_FILETYPE_PEM) <= 0) {
ERR_print_errors_fp(stderr);
exit(EXIT_FAILURE);
}
if (SSL_CTX_use_PrivateKey_file(ctx, "cert/server.key", SSL_FILETYPE_PEM) <= 0) {
ERR_print_errors_fp(stderr);
exit(EXIT_FAILURE);
}
return ctx;
}
// 计算MD5哈希
void calculate_md5(const char *input, char *output) {
unsigned char digest[MD5_DIGEST_LENGTH];
MD5_CTX ctx;
MD5_Init(&ctx);
MD5_Update(&ctx, input, strlen(input));
MD5_Final(digest, &ctx);
for (int i = 0; i < MD5_DIGEST_LENGTH; i++) {
sprintf(output + (i * 2), "%02x", digest[i]);
}
output[32] = '\0';
}
// 处理客户端连接
void *handle_client(void *arg) {
client_t *client = (client_t *)arg;
char buffer[BUFFER_SIZE];
char md5_result[33];
int bytes_received;
char client_ip[INET_ADDRSTRLEN];
int connection_success = 0;
// 获取客户端IP地址
struct sockaddr_in client_addr;
socklen_t addr_len = sizeof(client_addr);
if (getpeername(client->socket, (struct sockaddr*)&client_addr, &addr_len) == 0) {
inet_ntop(AF_INET, &client_addr.sin_addr, client_ip, INET_ADDRSTRLEN);
} else {
strcpy(client_ip, "unknown");
}
log_with_timestamp("新客户端连接IP: %s线程ID: %lu套接字: %d\n",
client_ip, client->thread, client->socket);
while (client->active) {
// 检查SSL连接状态
if (SSL_get_shutdown(client->ssl) & SSL_RECEIVED_SHUTDOWN) {
printf("检测到SSL关闭信号\n");
break;
}
// 设置套接字超时
struct timeval timeout;
timeout.tv_sec = 30; // 30秒超时
timeout.tv_usec = 0;
setsockopt(client->socket, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
// 检查连接是否仍然有效
if (!client->active || client->socket < 0) {
log_with_timestamp("客户端连接已失效退出处理IP: %s\n", client_ip);
goto cleanup;
}
// 接收客户端消息
bytes_received = SSL_read(client->ssl, buffer, MAX_MESSAGE_SIZE);
if (bytes_received <= 0) {
int ssl_error = SSL_get_error(client->ssl, bytes_received);
switch (ssl_error) {
case SSL_ERROR_ZERO_RETURN:
log_with_timestamp("客户端正常断开连接IP: %s\n", client_ip);
goto cleanup;
case SSL_ERROR_WANT_READ:
printf("SSL需要更多数据继续等待...\n");
continue;
case SSL_ERROR_WANT_WRITE:
printf("SSL需要写入数据继续等待...\n");
continue;
case SSL_ERROR_SSL:
// 检查是否是客户端关闭连接导致的错误
if (bytes_received == 0) {
log_with_timestamp("客户端关闭连接IP: %s\n", client_ip);
} else {
log_with_timestamp("SSL协议错误可能是数据格式问题或连接异常IP: %s\n", client_ip);
ERR_print_errors_fp(stderr);
}
// 尝试清理SSL错误队列
ERR_clear_error();
goto cleanup;
case SSL_ERROR_SYSCALL:
if (bytes_received == 0) {
log_with_timestamp("客户端关闭连接系统调用IP: %s\n", client_ip);
} else {
log_with_timestamp("SSL系统调用错误可能是网络问题IP: %s\n", client_ip);
}
// 不要直接break让连接正常清理
goto cleanup;
default:
log_with_timestamp("SSL读取错误: %dIP: %s\n", ssl_error, client_ip);
goto cleanup;
}
break;
}
buffer[bytes_received] = '\0';
// 检查消息长度
if (bytes_received >= MAX_MESSAGE_SIZE - 1) {
printf("警告: 消息可能被截断,长度: %d\n", bytes_received);
}
log_with_timestamp("收到客户端消息IP: %s长度: %d内容: %s\n",
client_ip, bytes_received, buffer);
// 计算MD5
calculate_md5(buffer, md5_result);
log_with_timestamp("MD5计算完成IP: %s结果: %s\n", client_ip, md5_result);
// 发送MD5结果给客户端
if (SSL_write(client->ssl, md5_result, strlen(md5_result)) <= 0) {
int write_error = SSL_get_error(client->ssl, -1);
log_with_timestamp("发送MD5结果失败SSL错误: %dIP: %s\n", write_error, client_ip);
goto cleanup;
}
log_with_timestamp("已发送MD5结果给客户端IP: %s\n", client_ip);
connection_success = 1; // 标记连接成功
}
cleanup:
// 更新IP统计
update_ip_stats(client_ip, connection_success);
// 清理客户端连接
log_with_timestamp("开始清理客户端连接IP: %s套接字: %d\n", client_ip, client->socket);
if (client->ssl) {
SSL_shutdown(client->ssl);
SSL_free(client->ssl);
client->ssl = NULL;
}
if (client->socket >= 0) {
close(client->socket);
client->socket = -1;
}
pthread_mutex_lock(&client_mutex);
client->active = 0;
client_count--;
pthread_mutex_unlock(&client_mutex);
log_with_timestamp("客户端连接已关闭IP: %s当前连接数: %d\n", client_ip, client_count);
return NULL;
}
// 信号处理函数
void signal_handler(int sig) {
printf("\n收到信号 %d正在关闭服务器...\n", sig);
// 关闭所有客户端连接
for (int i = 0; i < MAX_CLIENTS; i++) {
if (clients[i].active) {
clients[i].active = 0;
if (clients[i].ssl) {
SSL_shutdown(clients[i].ssl);
SSL_free(clients[i].ssl);
}
if (clients[i].socket > 0) {
close(clients[i].socket);
}
}
}
if (server_socket > 0) {
close(server_socket);
}
if (ssl_ctx) {
SSL_CTX_free(ssl_ctx);
}
cleanup_openssl();
exit(0);
}
int main(int argc, char *argv[]) {
int port = 8443; // 默认端口
struct sockaddr_in server_addr, client_addr;
socklen_t client_len = sizeof(client_addr);
int opt = 1;
int c;
// 解析命令行参数
while ((c = getopt(argc, argv, "p:h")) != -1) {
switch (c) {
case 'p':
port = atoi(optarg);
if (port <= 0 || port > 65535) {
printf("错误: 端口号必须在1-65535之间\n");
exit(EXIT_FAILURE);
}
break;
case 'h':
printf("用法: %s [-p 端口] [-h]\n", argv[0]);
printf("选项:\n");
printf(" -p 端口 指定服务器端口 (默认: 8443)\n");
printf(" -h 显示此帮助信息\n");
printf("\n示例:\n");
printf(" %s -p 9000 # 在端口9000运行服务器\n", argv[0]);
printf(" %s # 在默认端口8443运行服务器\n", argv[0]);
exit(EXIT_SUCCESS);
case '?':
printf("使用 -h 查看帮助信息\n");
exit(EXIT_FAILURE);
default:
abort();
}
}
log_with_timestamp("启动TLS服务器端口: %d\n", port);
// 设置信号处理
signal(SIGINT, signal_handler);
signal(SIGTERM, signal_handler);
// 初始化OpenSSL
init_openssl();
// 创建SSL上下文
ssl_ctx = create_context();
// 创建服务器套接字
server_socket = socket(AF_INET, SOCK_STREAM, 0);
if (server_socket < 0) {
perror("创建套接字失败");
exit(EXIT_FAILURE);
}
// 设置套接字选项
if (setsockopt(server_socket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
perror("设置套接字选项失败");
exit(EXIT_FAILURE);
}
// 绑定地址
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(port);
if (bind(server_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
perror("绑定地址失败");
exit(EXIT_FAILURE);
}
// 开始监听
if (listen(server_socket, 10) < 0) {
perror("监听失败");
exit(EXIT_FAILURE);
}
log_with_timestamp("服务器已启动,等待客户端连接...\n");
log_with_timestamp("支持TLS 1.2和1.3\n");
log_with_timestamp("按Ctrl+C停止服务器\n");
// 初始化客户端数组
for (int i = 0; i < MAX_CLIENTS; i++) {
clients[i].socket = -1;
clients[i].ssl = NULL;
clients[i].active = 0;
}
// 主循环:接受客户端连接
while (1) {
int client_socket = accept(server_socket, (struct sockaddr *)&client_addr, &client_len);
if (client_socket < 0) {
perror("接受连接失败");
continue;
}
log_with_timestamp("新连接来自: %s:%d\n", inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port));
log_with_timestamp("当前活跃连接数: %d/%d\n", client_count, MAX_CLIENTS);
// 检查是否达到最大客户端数
if (client_count >= MAX_CLIENTS) {
log_with_timestamp("已达到最大客户端连接数,拒绝新连接\n");
close(client_socket);
continue;
}
// 创建SSL连接
SSL *ssl = SSL_new(ssl_ctx);
SSL_set_fd(ssl, client_socket);
if (SSL_accept(ssl) <= 0) {
log_with_timestamp("SSL握手失败IP: %s\n", inet_ntoa(client_addr.sin_addr));
ERR_print_errors_fp(stderr);
SSL_free(ssl);
close(client_socket);
continue;
}
log_with_timestamp("SSL握手成功IP: %sTLS版本: %s\n",
inet_ntoa(client_addr.sin_addr), SSL_get_version(ssl));
// 查找空闲的客户端槽位
int client_index = -1;
pthread_mutex_lock(&client_mutex);
for (int i = 0; i < MAX_CLIENTS; i++) {
if (!clients[i].active) {
client_index = i;
break;
}
}
if (client_index == -1) {
log_with_timestamp("没有可用的客户端槽位\n");
SSL_free(ssl);
close(client_socket);
pthread_mutex_unlock(&client_mutex);
continue;
}
// 设置客户端信息
clients[client_index].socket = client_socket;
clients[client_index].ssl = ssl;
clients[client_index].active = 1;
client_count++;
// 创建处理线程
if (pthread_create(&clients[client_index].thread, NULL, handle_client, &clients[client_index]) != 0) {
log_with_timestamp("创建线程失败\n");
clients[client_index].active = 0;
client_count--;
SSL_free(ssl);
close(client_socket);
pthread_mutex_unlock(&client_mutex);
continue;
} else {
// 分离线程,让系统自动回收线程资源
pthread_detach(clients[client_index].thread);
log_with_timestamp("客户端处理线程已创建,当前连接数: %d\n", client_count);
}
pthread_mutex_unlock(&client_mutex);
}
return 0;
}