a simple p2p message with xor cipher


// p2p.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <limits.h>
#include <ifaddrs.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <netdb.h>
#include <net/if.h>

#define BUFFER_SIZE 1024
#define PORT 36963 
#define DEFAULT_KEY "Qk2E@$ilHu#uWUIfrbz#rbID1z"

void open_port(int port);
void check_permissions();
void get_program_directory(char *buffer, size_t buffer_size);
void xor_encrypt_decrypt(const char *input, const char *key, char *output);
void read_key_from_file(const char *filename, char *key, size_t key_size);
void *receive_messages(void *sock_desc);
void *input_thread_func(void *sock_desc_ptr);
void close_sockets(int sock1, int sock2);
void swapFirstLast(char str[]); 
void get_ip_address(); 

// 获取程序目录
void get_program_directory(char *buffer, size_t buffer_size) {
    ssize_t size = readlink("/proc/self/exe", buffer, buffer_size);
    if (size >= 0) {
        buffer[size] = '\0'; // 确保以 null 结尾
        char *lastSlash = strrchr(buffer, '/');
        if (lastSlash) {
            *lastSlash = '\0'; // 保留目录部分
        }
    }
}

// XOR 加密/解密函数
void xor_encrypt_decrypt(const char *input, const char *key, char *output) { 
    size_t input_len = strlen(input);
    size_t key_len = strlen(key);
    for (size_t i = 0; i < input_len; i++) {
        output[i] = input[i] ^ key[i % key_len];
    }
    output[input_len] = '\0'; 
}

// 读取密钥文件的函数
void read_key_from_file(const char *filename, char *key, size_t key_size) {
    char program_dir[PATH_MAX];
    get_program_directory(program_dir, sizeof(program_dir));
    
    char filepath[PATH_MAX];
    snprintf(filepath, sizeof(filepath), "%s/%s", program_dir, filename);
    FILE *file = fopen(filepath, "r");
    if (file) {
        if (fgets(key, key_size, file) != NULL) {
            size_t len = strlen(key);
            if (len > 0 && key[len - 1] == '\n') {
                key[len - 1] = '\0';
            }
        } else {
            strncpy(key, DEFAULT_KEY, key_size);
        }
        fclose(file);
    } else {
        file = fopen(filepath, "w");
        if (file) {
            fprintf(file, "%s\n", DEFAULT_KEY);
            fclose(file);
            strncpy(key, DEFAULT_KEY, key_size);
            key[key_size - 1] = '\0'; // 确保以 null 结尾
            printf("创建新密钥文件 '%s' 并使用默认密钥。\n", filename);
        } else {
            perror("无法创建密钥文件");
            strncpy(key, DEFAULT_KEY, key_size - 1);
            key[key_size - 1] = '\0'; // 确保以 null 结尾
        }
    }
}

// 接收消息的线程函数
void *receive_messages(void *sock_desc) {
    int sock = *(int *)sock_desc;
    char buffer[BUFFER_SIZE];
    char decrypted[BUFFER_SIZE];
    char key[BUFFER_SIZE];
    read_key_from_file("p2p.key", key, sizeof(key));

    while (1) {
        int read_size = recv(sock, buffer, sizeof(buffer) - 1, 0);
        if (read_size > 0) {
            buffer[read_size] = '\0';
            xor_encrypt_decrypt(buffer, key, decrypted);
            swapFirstLast(key); 
            printf("\033[2K\033[1G对方发送的消息: %s\n", decrypted);
            printf("请输入消息: ");
            fflush(stdout);
        } else {
            if (read_size == 0) {
                printf("对方已断开连接\n");
            } else {
                perror("接收消息失败");
            }
            break; // 退出循环
        }
    }

    close(sock); // 关闭现有套接字
    return NULL;
}

// 用户输入处理线程函数
void *input_thread_func(void *sock_desc_ptr) {
    int *sock_desc = (int *)sock_desc_ptr;
    char message[BUFFER_SIZE];
    char key[BUFFER_SIZE];
    read_key_from_file("p2p.key", key, sizeof(key));

    while (1) {
        printf("请输入消息: ");
        fflush(stdout);
        if (fgets(message, sizeof(message), stdin) == NULL) {
            perror("读取输入失败");
            break;
        }
        message[strcspn(message, "\n")] = 0; // 移除换行符
        
	if (strlen(message) == 0) {
            continue; // 继续下一轮循环
        }

        char encrypted[BUFFER_SIZE];
        xor_encrypt_decrypt(message, key, encrypted);
	ssize_t sent_bytes = send(*sock_desc, encrypted, strlen(encrypted), 0);
	if (sent_bytes == strlen(encrypted)) {
            swapFirstLast(key); 
        }
    }
    return NULL;
}

int main(int argc, char *argv[]) {
    check_permissions();
    open_port(PORT);
    printf("端口已成功打开。\n");

    char key[BUFFER_SIZE];
    read_key_from_file("p2p.key", key, sizeof(key)); // 读取密钥

    struct sockaddr_in server_addr, client_addr;

    if (argc > 1) {
        const char *server_ip = argv[1];
        int sock = socket(AF_INET, SOCK_STREAM, 0);
        if (sock < 0) {
            perror("创建 socket 失败");
            return 1;
        }

        server_addr.sin_family = AF_INET;
        server_addr.sin_port = htons(PORT);
        inet_pton(AF_INET, server_ip, &server_addr.sin_addr);

        if (connect(sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
            perror("连接失败");
            close(sock);
            return 1;
        } else {
            printf("连接成功\n");

            pthread_t recv_thread, input_thread;
            pthread_create(&recv_thread, NULL, receive_messages, (void *)&sock);
            pthread_create(&input_thread, NULL, input_thread_func, (void *)&sock);
            pthread_join(recv_thread, NULL);
            pthread_cancel(input_thread); // 取消输入线程,确保退出
            pthread_join(input_thread, NULL);
            close(sock);
            return 0;
        }
    }

    // 进入服务器模式
    while (1) {
        int listen_sock = socket(AF_INET, SOCK_STREAM, 0);
        server_addr.sin_family = AF_INET;
        server_addr.sin_addr.s_addr = INADDR_ANY;
        server_addr.sin_port = htons(PORT);

        int opt = 1;
        if (setsockopt(listen_sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) {
            perror("setsockopt failed");
            close(listen_sock);
            exit(EXIT_FAILURE);
        }

        if (bind(listen_sock, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
            perror("绑定失败");
            close(listen_sock);
            continue; // 返回循环以重新绑定
        }

        listen(listen_sock, 3);
	printf("本机为服务端\n");
	get_ip_address();
        printf("等待连接...\n");

        int addr_len = sizeof(client_addr);
        int new_socket = accept(listen_sock, (struct sockaddr *)&client_addr, (socklen_t*)&addr_len);
        if (new_socket < 0) {
            perror("接收连接失败");
            close(listen_sock);
            continue; // 返回循环以等待下一个连接
        }
        printf("连接成功\n");


        pthread_t recv_thread, input_thread;
        pthread_create(&recv_thread, NULL, receive_messages, (void *)&new_socket);
        pthread_create(&input_thread, NULL, input_thread_func, (void *)&new_socket);

        pthread_join(recv_thread, NULL);
        pthread_cancel(input_thread); // 取消输入线程,确保退出
        pthread_join(input_thread, NULL);

        close_sockets(new_socket, listen_sock); // 关闭套接字
    }

    return 0;
}

// 打开端口
void open_port(int port) {
    char command[100];
    snprintf(command, sizeof(command), "iptables -A INPUT -p tcp --dport %d -j ACCEPT", port);
    int result = system(command);
    if (result == -1 || WEXITSTATUS(result) != 0) {
        fprintf(stderr, "执行打开 TCP 端口命令失败. 退出.\n");
        exit(EXIT_FAILURE);
    }
}

// 权限检查函数
void check_permissions() {
    if (getuid() != 0) {
        fprintf(stderr, "您必须以管理员身份运行此程序.\n");
        exit(EXIT_FAILURE);
    }
}

// 关闭套接字的函数
void close_sockets(int sock1, int sock2) {
    close(sock1);
    close(sock2);
}

void swapFirstLast(char str[]) {
    int length = strlen(str);
    if (length > 1) {
        char temp = str[0];
        str[0] = str[length - 1];
        str[length - 1] = temp;
    }
}

void get_ip_address() {
    struct ifaddrs *addrs;
    char ip[INET_ADDRSTRLEN];

    // 获取所有网络接口的地址信息
    if (getifaddrs(&addrs) == -1) {
        perror("getifaddrs");
        exit(EXIT_FAILURE);
    }

    // 遍历每个接口
    for (struct ifaddrs *tmp = addrs; tmp != NULL; tmp = tmp->ifa_next) {
        // 只处理IPv4地址,并且接口是活动的
        if (tmp->ifa_addr && tmp->ifa_addr->sa_family == AF_INET) {
            if (getnameinfo(tmp->ifa_addr, sizeof(struct sockaddr_in), ip, sizeof(ip), NULL, 0, NI_NUMERICHOST) == 0) {
                // 检查接口是否是活动的
                if (tmp->ifa_flags & IFF_UP) {
                    printf("Interface: %s, IP Address: %s\n", tmp->ifa_name, ip);
                }
            }
        }
    }

    // 释放内存
    freeifaddrs(addrs);
}