first commit
This commit is contained in:
497
tls_server.c
Executable file
497
tls_server.c
Executable file
@@ -0,0 +1,497 @@
|
||||
#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读取错误: %d,IP: %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错误: %d,IP: %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: %s,TLS版本: %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;
|
||||
}
|
Reference in New Issue
Block a user