linux/server.c

457 lines
14 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 *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;
}