From c739aa0cf8fc885c8cad79d2d639d0ecd49bd5ae Mon Sep 17 00:00:00 2001 From: Ubuntu Date: Fri, 5 Jun 2020 17:12:19 +0000 Subject: Release --- .gitignore | 2 + Makefile | 7 ++ README.md | 14 ++++ client.cpp | 179 +++++++++++++++++++++++++++++++++++++++++++++++++ encryption.hpp | 38 +++++++++++ server.cpp | 206 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ structs.hpp | 60 +++++++++++++++++ utils.hpp | 204 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 710 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 README.md create mode 100644 client.cpp create mode 100644 encryption.hpp create mode 100644 server.cpp create mode 100644 structs.hpp create mode 100644 utils.hpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f2ad853 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +client +server diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..146ecb8 --- /dev/null +++ b/Makefile @@ -0,0 +1,7 @@ +CXX = g++ + +CXXFLAGS = -Wall + +main: + $(CXX) $(CXXFLAGS) server.cpp -o server + $(CXX) $(CXXFLAGS) client.cpp -o client diff --git a/README.md b/README.md new file mode 100644 index 0000000..8421789 --- /dev/null +++ b/README.md @@ -0,0 +1,14 @@ +# encrypted-chat +Old chat client and server I made with easily customizable encryption. + +## Encryption +Change the encrypt and decrypt methods in encryption.hpp +## Run +./server +./client -i IP +(see all arguments in print_help()) +## Compile +Made on void linux. To compile just make it. + +## License +GPL v3 diff --git a/client.cpp b/client.cpp new file mode 100644 index 0000000..f1a8ab7 --- /dev/null +++ b/client.cpp @@ -0,0 +1,179 @@ +#include "utils.hpp" + +#define PORT "4444" + +int main(int argc, char** argv); +int connect_to_server(Options* options); +void* packet_recieveing_thread(void* poptions); +void* ping_thread(void* poptions); +int parse_arguments(int argc, char** argv, Options* options); +int print_help(); + +Options options {}; + +int main(int argc, char** argv) +{ + int status; + + char player_name[20] = "Anonymous"; + char num[5]; + sprintf(num, "%d", (int)(time(0) % 9999)); + strncat(player_name, num, sizeof(player_name)); + + options.server = false; + strncpy(options.port, PORT, sizeof(options.port)); + strncpy(options.name, player_name, sizeof(options.name)); + options.encryption = false; + strncpy(options.password, DEFAULT_PASS, sizeof(options.password)); + + status = parse_arguments(argc, argv, &options); + if(status) + { + print_help(); + goto end; + } + + status = connect_to_server(&options); + if(status) + { + ERROR_LOG("Failed to connect to the server.\n"); + goto end; + } + + INFO_LOG("Succesfully connected to server.\n"); + + pthread_t packet_recieveing_thread_id; + pthread_create(&packet_recieveing_thread_id, NULL, packet_recieveing_thread, (void*)&options); + + start_client(NULL, &options); + + end: + return status; +} + +int connect_to_server(Options* options) +{ + options->sockfd = socket(AF_INET, SOCK_STREAM, 0); + if(options->sockfd < 0) + return 1; + + struct sockaddr_in sockaddr; + sockaddr.sin_family = AF_INET; + sockaddr.sin_addr.s_addr = inet_addr(options->ip); + sockaddr.sin_port = htons(atoi(options->port)); + + if(connect(options->sockfd, (struct sockaddr*)&sockaddr, sizeof(sockaddr))) + return 2; + + Packet packet {}; + packet.type = PacketType::packet_connect; + strncpy(packet.data.packet_connect.name, options->name, sizeof(packet.data.packet_connect.name)); + packet.data.packet_connect.pass = options->pass; + strncpy(packet.data.packet_connect.password, options->password, sizeof(packet.data.packet_connect.password)); + + if(send_packet(&packet, &options->sockfd, options, NULL)) + return 3; + + pthread_t ping_thread_id; + pthread_create(&ping_thread_id, NULL, ping_thread, (void*)options); + + return 0; +} + +void* packet_recieveing_thread(void* poptions) +{ + Options* opt = (Options*)poptions; + Packet incoming_packet {}; + + int read_size = 0; + while((read_size = recv(opt->sockfd, &incoming_packet, sizeof(incoming_packet), 0)) > 0) + { + int result = handle_incoming_packet(&incoming_packet, &opt->sockfd, NULL, &options); + if(result) + { + ERROR_LOG("Invalid packet recieved.\n"); + exit(1); + } + } + + return 0; +} + +void* ping_thread(void* poptions) +{ + Options* opt = (Options*)poptions; + while(1) + { + Packet ping {}; + ping.type = PacketType::packet_ping; + strncpy(ping.data.packet_ping.name, opt->name, sizeof(ping.data.packet_ping.name)); + if(send_packet(&ping, &opt->sockfd, opt, NULL)) + { + ERROR_LOG("Lost connection to server.\n"); + exit(1); + } + + sleep(2); + } +} + +int parse_arguments(int argc, char** argv, Options* options) +{ + extern char* optarg; + int status = 0; + bool mand_flag = false; + int option; + while((option = getopt(argc, argv, "i:p:en:x:h")) != -1) + switch(option) + { + case 'i': + strncpy(options->ip, optarg, sizeof(options->ip)); + mand_flag = true; + break; + case 'p': + strncpy(options->port, optarg, sizeof(options->port)); + break; + case 'e': + options->encryption = true; + break; + case 'n': + strncpy(options->name, optarg, sizeof(options->name)); + break; + case 'x': + options->pass = true; + strncpy(options->password, optarg, sizeof(options->password)); + break; + case 'h': + print_help(); + break; + case ':': + status = 1; + break; + case '?': + status = 1; + break; + } + + if(!mand_flag) + { + status = 1; + ERROR_LOG("Needs ip argument (-i IP)\n"); + } + + return status; +} + +int print_help() +{ + printf( + "Usage:\n" + "\t./client\n" + "\t\t-i (IP) [ip address]\n" + "\t\t-p (PORT) [port number]\n" + "\t\t-e [encryption]\n" + "\t\t-n (NAME) [username]\n" + "\t\t-x (PASS) [password]\n" + "\t\t-h [show help]\n" + ); + return 0; +} diff --git a/encryption.hpp b/encryption.hpp new file mode 100644 index 0000000..b8a1be4 --- /dev/null +++ b/encryption.hpp @@ -0,0 +1,38 @@ +#include +#include +#include +#include +#include +#include +#include +#include "structs.hpp" + +#define DEFAULT_PASS "default" + +int encrypt(Options* options, char* data, int size); +int decrypt(Options* options, char* data, int size); + +/* + * CUSTOMIZE THIS TWO METHODS + * + * Currently using basic XOR example encryption + * + */ + +int encrypt(Options* options, char* data, int size) +{ + if(!options->encryption) + return 0; + + for(int i = 0; i < size; i++) + *(data + i) = *(data + i) ^ (int)options->password[0]; + + return 0; +} + +int decrypt(Options* options, char* data, int size) +{ + encrypt(options, data, size); + + return 0; +} diff --git a/server.cpp b/server.cpp new file mode 100644 index 0000000..597e131 --- /dev/null +++ b/server.cpp @@ -0,0 +1,206 @@ +#include "utils.hpp" + +#define PUBLIC "0.0.0.0" +#define LOCAL "127.0.0.1" +#define IP PUBLIC +#define PORT "4444" + +int main(int argc, char** argv); +int start_server(Options* options); +void* handle_connections(void* poptions); +void* handle_client(void* pclient_fd); +int parse_arguments(int argc, char** argv, Options* options); +int print_help(); + +Options options {}; +std::vector clients; + +int main(int argc, char** argv) +{ + int status; + + options.server = true; + strncpy(options.ip, IP, sizeof(options.ip)); + strncpy(options.port, PORT, sizeof(options.port)); + strncpy(options.name, "Server", sizeof(options.name)); + options.encryption = false; + options.pass = false; + strncpy(options.password, DEFAULT_PASS, sizeof(options.password)); + options.interactive = false; + options.max_clients = -1; + + status = parse_arguments(argc, argv, &options); + if(status) + { + print_help(); + goto end; + } + + status = start_server(&options); + if(status) + { + ERROR_LOG("Failed to start server.\n"); + goto end; + } + + INFO_LOG("Succesfully started server.\n"); + + if(options.interactive) + start_client(&clients, &options); + + pause(); + + end: + return status; +} + +int start_server(Options* options) +{ + options->sockfd = socket(AF_INET, SOCK_STREAM, 0); + if(options->sockfd < 0) + return 1; + + sockaddr_in sockaddr; + memset(&sockaddr, 0, sizeof(sockaddr)); + sockaddr.sin_family = AF_INET; + sockaddr.sin_addr.s_addr = inet_addr(options->ip); + sockaddr.sin_port = htons(atoi(options->port)); + + if(bind(options->sockfd, (struct sockaddr*)&sockaddr, sizeof(sockaddr))) + return 2; + + if(listen(options->sockfd, 1)) + return 3; + + pthread_t connections_thread; + if(pthread_create(&connections_thread, NULL, handle_connections, (void*)options)) + return 4; + + return 0; +} + +void* handle_connections(void* poptions) +{ + Options* options = (Options*)poptions; + sockaddr_in sockaddr; + socklen_t addrlen = sizeof(sockaddr); + + int connection; + while((connection = accept(options->sockfd, (struct sockaddr*)&sockaddr, &addrlen))) + { + if(connection < 0) + return 0; + + if((int)(&clients)->size() == options->max_clients) + { + ERROR_LOG("Client limit reached. New client has been forbidden to connect.\n"); + close(connection); + continue; + } + + pthread_t client_thread; + if(pthread_create(&client_thread, NULL, handle_client, (void*)(intptr_t)connection)) + return 0; + } + + return 0; +} + +void* handle_client(void* pclient_fd) +{ + int client_fd = (intptr_t)pclient_fd; + Packet incoming_packet; + bool first = true; + char name[20]; + + int read_size; + while((read_size = recv(client_fd, &incoming_packet, sizeof(incoming_packet), 0)) > 0) + { + int result = handle_incoming_packet(&incoming_packet, &client_fd, &clients, &options); + if(result == -1) + { + ERROR_LOG("Invalid packet recieved.\n"); + break; + } + else if(result == -3) + return 0; + else if(result > 0) + { + ERROR_LOG("Error handling packet recieved.\n"); + break; + } + + if(first && incoming_packet.type == PacketType::packet_ping) + { + first = false; + strncpy(name, incoming_packet.data.packet_ping.name, sizeof(name)); + } + } + + if(strlen(name) > 0) + { + Packet dc_packet {}; + dc_packet.type = PacketType::packet_disconnect; + strncpy(dc_packet.data.packet_disconnect.name, name, sizeof(dc_packet.data.packet_disconnect.name)); + if(options.encryption) + encrypt(&options, (char*)&dc_packet, sizeof(dc_packet)); + handle_incoming_packet(&dc_packet, &client_fd, &clients, &options); + } + + close(client_fd); + + return 0; +} + +int parse_arguments(int argc, char** argv, Options* options) +{ + extern char* optarg; + int status = 0; + int option; + while((option = getopt(argc, argv, "p:en:ix:h")) != -1) + switch(option) + { + case 'p': + strncpy(options->port, optarg, sizeof(options->port)); + break; + case 'e': + options->encryption = true; + break; + case 'n': + options->max_clients = atoi(optarg); + break; + case 'i': + options->interactive = true; + break; + case 'x': + options->pass = true; + strncpy(options->password, optarg, sizeof(options->password)); + break; + case 'h': + print_help(); + break; + case ':': + status = 1; + break; + case '?': + status = 1; + break; + } + + return status; +} + +int print_help() +{ + printf( + "Usage:\n" + "\t./server\n" + "\t\t-p (PORT) [port number]\n" + "\t\t-e [encryption]\n" + "\t\t-n (NUM_CLIENTS) [max clients]\n" + "\t\t-i [interactive]\n" + "\t\t-x (PASS) [pasword]\n" + "\t\t-h [show help]\n" + ); + return 0; +} diff --git a/structs.hpp b/structs.hpp new file mode 100644 index 0000000..b94ad37 --- /dev/null +++ b/structs.hpp @@ -0,0 +1,60 @@ +struct Options +{ + bool server; + char name[20]; + char ip[20]; + char port[20]; + bool encryption; + bool pass; + char password[20]; + bool interactive; + int max_clients; + int sockfd; +}; + + +enum class PacketType +{ + packet_connect, + packet_disconnect, + packet_message, + packet_ping +}; + +struct PacketConnect +{ + char name[20]; + bool pass; + char password[20]; +}; + +struct PacketDisconnect +{ + char name[20]; +}; + +struct PacketMessage +{ + int client_fd; + bool server; + char name[20]; + char message[200];//make it var length +}; + +struct PacketPing +{ + char name[20]; +}; + +struct Packet +{ + PacketType type; + union + { + PacketConnect packet_connect; + PacketDisconnect packet_disconnect; + PacketMessage packet_message; + PacketPing packet_ping; + } data; + +}; diff --git a/utils.hpp b/utils.hpp new file mode 100644 index 0000000..71762b2 --- /dev/null +++ b/utils.hpp @@ -0,0 +1,204 @@ +#include +#include +#include + +#include "encryption.hpp" + +#define ERROR_LOG(msg, ...) printf("[!] "); printf(msg, ##__VA_ARGS__); fflush(stdout); +#define INFO_LOG(msg, ...) printf("[+] "); printf(msg, ##__VA_ARGS__); fflush(stdout); +#define PRINT_MSG(name, id, msg, sv) if(sv) printf("[%s]: %s", name, msg); else printf("[%s-%d]: %s", name, id, msg); fflush(stdout); + +int start_client(std::vector* clients, Options* options); +int handle_incoming_packet(Packet* packet, int* clientfd, std::vector* clients, Options* options); +int handle_packet_connect(PacketConnect* packet, int* clientfd, std::vector* clients, Options* options); +int handle_packet_disconnect(PacketDisconnect* packet, int* clientfd, std::vector* clients, Options* options); +int handle_packet_message(PacketMessage* packet, int* clientfd, std::vector* clients, Options* options); +int notify_clients(Packet* packet, std::vector* clients, Options* options); +int send_packet(Packet* packet, int* sockfd, Options* options, void* extra_data); +int add_client(int* clientfd, std::vector* clients); +int remove_client(int* clientfd, std::vector* clients); +int enable_echo(); +int disable_echo(); + +int start_client(std::vector* clients, Options* options) +{ + char* stc; + size_t bytes; + while(1) + { + stc = NULL; + getline(&stc, &bytes, stdin); //need to make it so it doesnt print to terminal + //disable_echo(); + Packet packet {}; + packet.type = PacketType::packet_message; + packet.data.packet_message.server = options->server; + strncpy(packet.data.packet_message.name, options->name, sizeof(packet.data.packet_message.name)); + strncpy(packet.data.packet_message.message, stc, sizeof(packet.data.packet_message)); + if(options->server) + { + if(options->encryption) + encrypt(options, (char*)&packet, sizeof(packet)); + handle_incoming_packet(&packet, &options->sockfd, clients, options); + } + else + if(send_packet(&packet, &options->sockfd, options, NULL)) + { + free(stc); + return 0; + } + + free(stc); + } + + return 0; +} + +int handle_incoming_packet(Packet* packet, int* sockfd, std::vector* clients, Options* options) +{ + int status = decrypt(options, (char*)packet, sizeof(*packet)); + if(!status) + switch(packet->type) + { + case PacketType::packet_connect: + status = handle_packet_connect(&packet->data.packet_connect, sockfd, clients, options); + break; + case PacketType::packet_disconnect: + status = handle_packet_disconnect(&packet->data.packet_disconnect, sockfd, clients, options); + break; + case PacketType::packet_message: + status = handle_packet_message(&packet->data.packet_message, sockfd, options->server ? clients : NULL, options); + break; + case PacketType::packet_ping: + status = -2; //dont notify + break; + default: + status = -1; + break; + } + + if(options->server && !status) + status = notify_clients(packet, clients, options); + + return status; +} + +int handle_packet_connect(PacketConnect* packet, int* clientfd, std::vector* clients, Options* options) +{ + int status = 0; + if(options->server) + { + if(options->pass && (!packet->pass || strcmp(options->password, packet->password))) + { + status = -3; + INFO_LOG("Player tried to join with an incorrect password.\n"); + close(*clientfd); + goto end; + } + + status = add_client(clientfd, clients); + } + + INFO_LOG("%s joined the chat!\n", packet->name); + + + end: + return status; +} + +int handle_packet_disconnect(PacketDisconnect* packet, int* clientfd, std::vector* clients, Options* options) +{ + int status = 0; + if(options->server) + status = remove_client(clientfd, clients); + + INFO_LOG("%s left the chat!\n", packet->name); + + return status; +} + +int handle_packet_message(PacketMessage* packet, int* clientfd, std::vector* clients, Options* options) +{ + if(options->server) + { + packet->client_fd = *clientfd; + std::vector::iterator pos = std::find(clients->begin(), clients->end(), *clientfd); + if(*clientfd != options->sockfd && pos == clients->end()) + return 1; + } + + packet->client_fd = *clientfd; + + if(!options->server || options->interactive) + { + PRINT_MSG(packet->name, packet->client_fd, packet->message, packet->server); + } + + return 0; +} + +int notify_clients(Packet* packet, std::vector* clients, Options* options) +{ + for(int clientfd : *clients) + if(send_packet(packet, &clientfd, options, NULL)) + return 1; + + return 0; +} + +int send_packet(Packet* packet, int* sockfd, Options* options, void* extra_data) +{ + int status = encrypt(options, (char*)packet, sizeof(*packet)); + if(status) + return status; + if(send(*sockfd, (const char*)packet, sizeof(*packet), 0) < 0) + status = 1; + status = decrypt(options, (char*)packet, sizeof(*packet)); + + return status; +} + +int add_client(int* clientfd, std::vector* clients) +{ + std::vector::iterator pos = std::find(clients->begin(), clients->end(), *clientfd); + if(pos == clients->end()) + { + clients->push_back(*clientfd); + return 0; + } + + return 1; +} + +int remove_client(int* clientfd, std::vector* clients) +{ + std::vector::iterator pos = std::find(clients->begin(), clients->end(), *clientfd); + if(pos != clients->end()) + { + clients->erase(pos); + return 0; + } + + close(*clientfd); + + return 1; +} + +int enable_echo() +{ + struct termios term; + tcgetattr(0, &term); + term.c_lflag |= ECHO; + tcsetattr(0, TCSAFLUSH, &term); + + return 0; +} + +int disable_echo() +{ + struct termios term; + tcgetattr(0, &term); + term.c_lflag &= ~ECHO; + tcsetattr(0, TCSAFLUSH, &term); + + return 0; +} -- cgit 1.4.1