linux/server.c
2024-12-18 17:24:29 +08:00

504 lines
16 KiB
C
Raw 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.

// 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 extract_path_and_filename(const char *input, char *path, char *filename) {
int last_slash_index = -1; // 记录最后一个 '/' 的位置
int last_dot_index = -1; // 记录最后一个 '.' 的位置
// 遍历路径字符串,找到最后一个 '/' 和 '.'
for (int i = 0; input[i] != '\0'; i++) {
if (input[i] == '/') {
last_slash_index = i; // 更新最后一个 '/' 的位置
}
if (input[i] == '.') {
last_dot_index = i; // 更新最后一个 '.' 的位置
}
}
// 提取路径部分(从开头到最后一个 '/'
if (last_slash_index != -1) {
strncpy(path, input, last_slash_index + 1);
path[last_slash_index + 1] = '\0'; // 确保路径部分以 '\0' 结尾
} else {
path[0] = '\0'; // 如果没有 '/', 为空
}
// 如果 '.' 在 '/' 之前或没有 '.', 忽略后缀处理
if (last_dot_index <= last_slash_index) {
last_dot_index = -1;
}
// 提取文件名部分(从最后一个 '/' 到最后一个 '.' 之间)
int j = 0; // 文件名的索引
for (int i = last_slash_index + 1; i < (last_dot_index == -1 ? strlen(input) : last_dot_index); i++) {
filename[j++] = input[i];
}
filename[j] = '\0'; // 添加字符串结束符
}
// 客户端处理线程
void *client_handler(void *arg)
{
int client_socket = *(int *)arg;
free(arg);
char buffer[BUFFER_SIZE];
while (1)
{
printf("等待客户端发送命令中...\n");
// 初始化缓冲区为 0
memset(buffer, 0, BUFFER_SIZE);
// 接收第一个参数判断参数客户端需要上传还是下载
int bytes = read(client_socket, buffer, BUFFER_SIZE - 1); // 预留一个字节用于NULL终止符
if (bytes <= 0)
{
perror("接收客户端命令失败");
break; // 如果读取失败或连接关闭,退出循环
}
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, "w");
if (!file)
{
perror("文件打开失败");
return NULL;
}
// 接收分块数据
int bytes_received;
while ((bytes_received = recv(client_socket, buffer, BUFFER_SIZE, 0)) > 0)
{
// 检查是否包含结束标志
if (strstr(buffer, "EOF") != NULL)
{
fwrite(buffer, 1, bytes_received - strlen("EOF"), file); // 写入实际数据
break;
}
fwrite(buffer, 1, bytes_received, file);
printf("接收到 %d 字节数据。\n", bytes_received);
}
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);
char path[256]; // 存储提取的路径(无后缀)
char filename[256]; // 存储提取的文件名
extract_path_and_filename(buffer, path, filename);
//压缩包路径
sprintf(compressed_file, "%s", buffer);
// 解压后的加密文件路径
sprintf(file_path, "./files/decrypt/%s.enc",filename);
// 解密文件路径
sprintf(decrypted_file, "./files/decrypt/%s.txt", filename);
printf("%s\n", compressed_file);
printf("%s\n", file_path);
printf("%s\n", decrypted_file);
// 解压缩、解密并发送给客户端
decompress_file(compressed_file, file_path);
printf("文件解压缩完成。\n");
printf("正在解密文件...\n");
sm4_cbc_padding_decrypt_file(file_path, 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");
// 向客户端发送 "READY" 信号
send(client_socket, "stop", strlen("stop"), 0);
// 删除解密后的文件
// remove(decrypted_file);
}
else
{
perror("读取客户端命令失败");
break;
}
memset(buffer, 0, BUFFER_SIZE); // 清空缓冲区,准备接收下一个命令
}
// 关闭连接
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;
}