457 lines
14 KiB
C
457 lines
14 KiB
C
// server.c
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
#include <pthread.h>
|
||
#include <semaphore.h>
|
||
#include <signal.h>
|
||
#include <sys/socket.h>
|
||
#include <netinet/in.h>
|
||
#include <unistd.h>
|
||
#include <zlib.h>
|
||
#include <fcntl.h>
|
||
#include <time.h>
|
||
#include <sys/stat.h>
|
||
#include <gmssl/sm4.h>
|
||
#include <gmssl/rand.h>
|
||
#include <dispatch/dispatch.h>
|
||
#include <arpa/inet.h>
|
||
|
||
#define PORT 8001
|
||
#define MAX_CLIENTS 5
|
||
#define BUFFER_SIZE 1024
|
||
// 密钥和偏移量
|
||
uint8_t key[SM4_KEY_SIZE];
|
||
uint8_t iv[SM4_BLOCK_SIZE];
|
||
// 将信号量定义为全局变量
|
||
dispatch_semaphore_t semaphore;
|
||
int log_pipe[2]; // 日志记录的管道
|
||
|
||
// 生成时间戳目录
|
||
void create_timestamp_dir(char *dir_name)
|
||
{
|
||
time_t now = time(NULL);
|
||
struct tm *t = localtime(&now);
|
||
// t->tm_year:从 1900 年开始的年数,因此需要加上 1900 得到完整的年份。
|
||
// t->tm_mon:从 0 开始的月份(0 表示 1 月),因此需要加上 1 得到实际的月份。
|
||
// t->tm_mday:月份中的日期。
|
||
// t->tm_hour:小时。
|
||
// t->tm_min:分钟。
|
||
// t->tm_sec:秒。
|
||
sprintf(dir_name, "./files/%04d%02d%02d%02d%02d%02d",
|
||
t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
|
||
t->tm_hour, t->tm_min, t->tm_sec);
|
||
mkdir("./files", 0777);
|
||
mkdir(dir_name, 0777);
|
||
// 创建解密结果文件目录
|
||
mkdir("./files/decrypt", 0777);
|
||
}
|
||
|
||
// 记录日志
|
||
void log_operation(const char *message)
|
||
{
|
||
write(log_pipe[1], message, strlen(message));
|
||
}
|
||
|
||
// 文件压缩
|
||
int compress_file(const char *src, const char *dest)
|
||
{
|
||
FILE *source = fopen(src, "rb");
|
||
gzFile dest_file = gzopen(dest, "wb");
|
||
if (!source || !dest_file)
|
||
return -1;
|
||
|
||
char buffer[BUFFER_SIZE];
|
||
int bytes_read;
|
||
while ((bytes_read = fread(buffer, 1, BUFFER_SIZE, source)) > 0)
|
||
{
|
||
gzwrite(dest_file, buffer, bytes_read);
|
||
}
|
||
fclose(source);
|
||
gzclose(dest_file);
|
||
return 0;
|
||
}
|
||
|
||
// 文件解压
|
||
int decompress_file(const char *src, const char *dest)
|
||
{
|
||
gzFile source = gzopen(src, "rb");
|
||
FILE *dest_file = fopen(dest, "wb");
|
||
if (!source || !dest_file)
|
||
return -1;
|
||
|
||
char buffer[BUFFER_SIZE];
|
||
int bytes_read;
|
||
while ((bytes_read = gzread(source, buffer, BUFFER_SIZE)) > 0)
|
||
{
|
||
fwrite(buffer, 1, bytes_read, dest_file);
|
||
}
|
||
gzclose(source);
|
||
fclose(dest_file);
|
||
return 0;
|
||
}
|
||
|
||
// 加密文件方法
|
||
int sm4_cbc_padding_encrypt_file(const char *input_file, const char *output_file, const uint8_t *key, const uint8_t *iv)
|
||
{
|
||
SM4_KEY enc_key;
|
||
sm4_set_encrypt_key(&enc_key, key); // 设置加密密钥
|
||
|
||
FILE *in_file = fopen(input_file, "rb");
|
||
FILE *out_file = fopen(output_file, "wb");
|
||
if (!in_file || !out_file)
|
||
{
|
||
perror("文件打开失败");
|
||
return 0;
|
||
}
|
||
|
||
uint8_t input_block[SM4_BLOCK_SIZE];
|
||
uint8_t output_block[SM4_BLOCK_SIZE + SM4_BLOCK_SIZE]; // 加密后可能需要填充
|
||
|
||
size_t bytes_read, outlen;
|
||
while ((bytes_read = fread(input_block, 1, SM4_BLOCK_SIZE, in_file)) > 0)
|
||
{
|
||
if (feof(in_file))
|
||
{
|
||
// 最后一块,带填充加密
|
||
if (sm4_cbc_padding_encrypt(&enc_key, iv, input_block, bytes_read, output_block, &outlen) != 1)
|
||
{
|
||
fprintf(stderr, "加密失败。\n");
|
||
fclose(in_file);
|
||
fclose(out_file);
|
||
return 0;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// 中间块,不需要填充
|
||
if (sm4_cbc_padding_encrypt(&enc_key, iv, input_block, SM4_BLOCK_SIZE, output_block, &outlen) != 1)
|
||
{
|
||
fprintf(stderr, "加密失败。\n");
|
||
fclose(in_file);
|
||
fclose(out_file);
|
||
return 0;
|
||
}
|
||
}
|
||
fwrite(output_block, 1, outlen, out_file);
|
||
}
|
||
|
||
fclose(in_file);
|
||
fclose(out_file);
|
||
return 1; // 成功
|
||
}
|
||
|
||
// 解密文件方法
|
||
int sm4_cbc_padding_decrypt_file(const char *input_file, const char *output_file, const uint8_t *key, const uint8_t *iv)
|
||
{
|
||
SM4_KEY dec_key;
|
||
sm4_set_decrypt_key(&dec_key, key); // 设置解密密钥
|
||
|
||
FILE *in_file = fopen(input_file, "rb");
|
||
FILE *out_file = fopen(output_file, "wb");
|
||
if (!in_file || !out_file)
|
||
{
|
||
perror("文件打开失败");
|
||
return 0;
|
||
}
|
||
|
||
uint8_t input_block[SM4_BLOCK_SIZE + SM4_BLOCK_SIZE]; // 假设有填充
|
||
uint8_t output_block[SM4_BLOCK_SIZE];
|
||
size_t bytes_read, outlen;
|
||
|
||
while ((bytes_read = fread(input_block, 1, sizeof(input_block), in_file)) > 0)
|
||
{
|
||
if (feof(in_file))
|
||
{
|
||
// 最后一块,带填充解密
|
||
if (sm4_cbc_padding_decrypt(&dec_key, iv, input_block, bytes_read, output_block, &outlen) != 1)
|
||
{
|
||
fprintf(stderr, "解密失败。\n");
|
||
fclose(in_file);
|
||
fclose(out_file);
|
||
return 0;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// 中间块,不需要处理填充
|
||
if (sm4_cbc_padding_decrypt(&dec_key, iv, input_block, bytes_read, output_block, &outlen) != 1)
|
||
{
|
||
fprintf(stderr, "解密失败。\n");
|
||
fclose(in_file);
|
||
fclose(out_file);
|
||
return 0;
|
||
}
|
||
}
|
||
fwrite(output_block, 1, outlen, out_file);
|
||
}
|
||
|
||
fclose(in_file);
|
||
fclose(out_file);
|
||
return 1; // 成功
|
||
}
|
||
|
||
// 客户端处理线程
|
||
void *client_handler(void *arg)
|
||
{
|
||
int client_socket = *(int *)arg;
|
||
free(arg);
|
||
|
||
char buffer[BUFFER_SIZE];
|
||
// 初始化缓冲区为 0
|
||
memset(buffer, 0, BUFFER_SIZE);
|
||
|
||
// 接收第一个参数判断参数客户端需要上传还是下载
|
||
int bytes = read(client_socket, buffer, BUFFER_SIZE - 1);// 预留一个字节用于NULL终止符
|
||
if (bytes <= 0)
|
||
{
|
||
perror("接收客户端命令失败");
|
||
return NULL;
|
||
}
|
||
buffer[bytes] = '\0'; // 确保以 NULL 结尾
|
||
char upload_or_download[256] ;
|
||
snprintf(upload_or_download, sizeof(upload_or_download), "%s", buffer);
|
||
printf("客户端发起请求:%s,等待后续指令....\n", upload_or_download);
|
||
|
||
char origin_file_path[256], dir_name[128], file_path[256], compressed_file[256], decrypted_file[256];
|
||
|
||
if (strcmp(upload_or_download, "upload") == 0)
|
||
{
|
||
// 接收文件名(不含后缀)
|
||
int bytes = read(client_socket, buffer, BUFFER_SIZE);
|
||
if (bytes <= 0)
|
||
{
|
||
perror("接收文件名失败");
|
||
return NULL;
|
||
}
|
||
buffer[bytes] = '\0'; // 确保以 NULL 结尾
|
||
char file_name[256];
|
||
snprintf(file_name, sizeof(file_name), "%s", buffer);
|
||
printf("接收文件名:%s\n", file_name);
|
||
// 发送 ACK 确认
|
||
send(client_socket, "ACK", strlen("ACK"), 0);
|
||
|
||
// 先生成./files目录,再生成时间戳目录,调用 create_timestamp_dir 函数,生成一个基于时间戳的目录名称,并将其存储在 dir_name 中
|
||
create_timestamp_dir(dir_name);
|
||
|
||
// 根据传入的文件名以及时间戳拼接生成源文件路径
|
||
sprintf(origin_file_path, "%s/%s.txt", dir_name, buffer);
|
||
// 加密后的文件路径
|
||
sprintf(file_path, "%s/%s.enc", dir_name, buffer);
|
||
// 加密后压缩包的文件路径
|
||
sprintf(compressed_file, "%s/%s.gz", dir_name, buffer);
|
||
// 解密的文件路径
|
||
snprintf(decrypted_file, sizeof(decrypted_file), "./files/decrypt/%s_decrypted.txt", buffer);
|
||
printf("源文件路径:%s\n加密后的文件:%s\n压缩的文件:%s\n解密的文件路径:%s\n", origin_file_path, file_path, compressed_file, decrypted_file);
|
||
|
||
FILE *file = fopen(origin_file_path, "wb");
|
||
if (!file)
|
||
{
|
||
perror("文件打开失败");
|
||
return NULL;
|
||
}
|
||
// 按块读取源文件内容并保存到origin_file_path
|
||
while (1)
|
||
{
|
||
int bytes = read(client_socket, buffer, BUFFER_SIZE -1);
|
||
if (bytes < 0)
|
||
{
|
||
perror("读取数据失败");
|
||
fclose(file);
|
||
return NULL;
|
||
}
|
||
// 检测是否接收到结束标志
|
||
if (strncmp(buffer, "EOF", 3) == 0)
|
||
break;
|
||
// 将数据写入文件
|
||
if (fwrite(buffer, 1, bytes, file) != bytes)
|
||
{
|
||
perror("写入文件失败");
|
||
fclose(file);
|
||
return NULL;
|
||
}
|
||
}
|
||
fclose(file);
|
||
|
||
log_operation("文件已接收。\n");
|
||
|
||
// 加密文件
|
||
printf("正在加密文件...\n");
|
||
sm4_cbc_padding_encrypt_file(origin_file_path, file_path, key, iv);
|
||
printf("文件加密完成。\n");
|
||
char log_message_file_path[BUFFER_SIZE];
|
||
sprintf(log_message_file_path, "文件已加密。加密文件保存路径为:%s\n", file_path);
|
||
log_operation(log_message_file_path);
|
||
// 压缩文件
|
||
printf("正在压缩被加密的文件...\n");
|
||
compress_file(file_path, compressed_file);
|
||
printf("文件压缩完成。\n");
|
||
|
||
// 删除加密文件
|
||
// remove(file_path);
|
||
char log_message_compressed_file[BUFFER_SIZE];
|
||
sprintf(log_message_compressed_file, "文件已加密。加密文件保存路径为:%s\n", compressed_file);
|
||
|
||
// 发送压缩的加密文件路径给客户端
|
||
send(client_socket, compressed_file, strlen(compressed_file), 0);
|
||
log_operation(log_message_compressed_file);
|
||
}
|
||
else if (strcmp(upload_or_download, "download") == 0)
|
||
{
|
||
printf("接收到下载请求...\n");
|
||
|
||
memset(buffer, 0, BUFFER_SIZE);
|
||
// 读取文件路径
|
||
read(client_socket, buffer, BUFFER_SIZE);
|
||
|
||
sprintf(compressed_file, "%s", buffer);
|
||
|
||
// 解压缩、解密并发送给客户端
|
||
decompress_file(compressed_file, compressed_file);
|
||
printf("文件解压缩完成。\n");
|
||
|
||
printf("正在解密文件...\n");
|
||
sm4_cbc_padding_decrypt_file(decrypted_file, decrypted_file, key, iv);
|
||
printf("文件解密完成。\n");
|
||
log_operation("文件已解压并解密,准备发送。\n");
|
||
// 向客户端发送 "READY" 信号
|
||
send(client_socket, "READY", strlen("READY"), 0);
|
||
|
||
// 发送解密后的文件
|
||
FILE *file_to_send = fopen(decrypted_file, "rb");
|
||
while (!feof(file_to_send))
|
||
{
|
||
int bytes_read = fread(buffer, 1, BUFFER_SIZE, file_to_send);
|
||
send(client_socket, buffer, bytes_read, 0);
|
||
}
|
||
fclose(file_to_send);
|
||
|
||
printf("文件发送完成。\n");
|
||
// 删除解密后的文件
|
||
// remove(decrypted_file);
|
||
}
|
||
else
|
||
{
|
||
perror("读取客户端命令失败");
|
||
}
|
||
// 关闭连接
|
||
close(client_socket);
|
||
dispatch_semaphore_signal(semaphore);
|
||
return NULL;
|
||
}
|
||
|
||
int main()
|
||
{
|
||
|
||
int server_socket, client_socket;
|
||
struct sockaddr_in server_addr, client_addr;
|
||
socklen_t client_addr_len = sizeof(client_addr);
|
||
|
||
// 创建服务器 socket
|
||
server_socket = socket(AF_INET, SOCK_STREAM, 0);
|
||
if (server_socket == -1)
|
||
{
|
||
perror("服务器 socket 创建失败");
|
||
exit(EXIT_FAILURE);
|
||
}
|
||
|
||
// 设置服务器地址
|
||
server_addr.sin_family = AF_INET;
|
||
server_addr.sin_addr.s_addr = INADDR_ANY;
|
||
server_addr.sin_port = htons(PORT);
|
||
|
||
// 绑定服务器 socket
|
||
if (bind(server_socket, (struct sockaddr *)&server_addr, sizeof(server_addr)) == -1)
|
||
{
|
||
perror("绑定失败");
|
||
close(server_socket);
|
||
exit(EXIT_FAILURE);
|
||
}
|
||
|
||
// 开始监听
|
||
if (listen(server_socket, MAX_CLIENTS) == -1)
|
||
{
|
||
perror("监听失败");
|
||
close(server_socket);
|
||
exit(EXIT_FAILURE);
|
||
}
|
||
|
||
// 初始化信号量
|
||
semaphore = dispatch_semaphore_create(MAX_CLIENTS);
|
||
if (semaphore == NULL)
|
||
{
|
||
fprintf(stderr, "信号量创建失败\n");
|
||
return -1;
|
||
}
|
||
|
||
// 随机生成加密密钥和 IV
|
||
if (rand_bytes(key, SM4_KEY_SIZE) != 1)
|
||
{
|
||
fprintf(stderr, "随机密钥生成失败。\n");
|
||
return -1;
|
||
}
|
||
if (rand_bytes(iv, SM4_BLOCK_SIZE) != 1)
|
||
{
|
||
fprintf(stderr, "随机IV生成失败。\n");
|
||
return -1;
|
||
}
|
||
// 打印密钥和 IV
|
||
printf("随机生成的密钥: ");
|
||
for (size_t i = 0; i < SM4_KEY_SIZE; i++)
|
||
{
|
||
printf("%02X ", key[i]);
|
||
}
|
||
printf("\n");
|
||
|
||
printf("随机生成的IV: ");
|
||
for (size_t i = 0; i < SM4_BLOCK_SIZE; i++)
|
||
{
|
||
printf("%02X ", iv[i]);
|
||
}
|
||
printf("\n");
|
||
|
||
// 示例:客户端处理逻辑
|
||
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); // 等待信号量
|
||
// printf("客户端进入,当前资源被占用\n");
|
||
|
||
// 创建管道
|
||
pipe(log_pipe);
|
||
if (fork() == 0) // 日志进程
|
||
{
|
||
close(log_pipe[1]); // 关闭写端
|
||
char log_buffer[BUFFER_SIZE];
|
||
while (read(log_pipe[0], log_buffer, BUFFER_SIZE) > 0)
|
||
{
|
||
printf("日志: %s\n", log_buffer);
|
||
}
|
||
exit(0);
|
||
}
|
||
|
||
printf("服务器已启动,等待客户端连接...\n");
|
||
while (1)
|
||
{
|
||
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
|
||
// printf("客户端进入,当前资源被占用\n");
|
||
|
||
int client_socket = accept(server_socket, (struct sockaddr *)&client_addr, &client_addr_len);
|
||
if (client_socket == -1)
|
||
{
|
||
perror("接受客户端连接失败");
|
||
continue;
|
||
}
|
||
|
||
printf("客户端连接成功,地址: %s\n", inet_ntoa(client_addr.sin_addr));
|
||
|
||
int *client_sock = malloc(sizeof(int));
|
||
*client_sock = client_socket;
|
||
|
||
pthread_t thread_id;
|
||
pthread_create(&thread_id, NULL, client_handler, client_sock);
|
||
pthread_detach(thread_id);
|
||
}
|
||
|
||
close(server_socket);
|
||
// 释放信号量
|
||
dispatch_release(semaphore);
|
||
return 0;
|
||
} |