diff options
author | Ubuntu <baitinq@Peces.qluofd4gyjdubpy0ojfn3gxkic.bx.internal.cloudapp.net> | 2020-06-05 17:12:19 +0000 |
---|---|---|
committer | Ubuntu <baitinq@Peces.qluofd4gyjdubpy0ojfn3gxkic.bx.internal.cloudapp.net> | 2020-06-05 17:12:19 +0000 |
commit | c739aa0cf8fc885c8cad79d2d639d0ecd49bd5ae (patch) | |
tree | 66fb3ec1af228082fab674e9aa98ba17d6b9cb44 | |
download | encrypted-chat-c739aa0cf8fc885c8cad79d2d639d0ecd49bd5ae.tar.gz encrypted-chat-c739aa0cf8fc885c8cad79d2d639d0ecd49bd5ae.tar.bz2 encrypted-chat-c739aa0cf8fc885c8cad79d2d639d0ecd49bd5ae.zip |
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | Makefile | 7 | ||||
-rw-r--r-- | README.md | 14 | ||||
-rw-r--r-- | client.cpp | 179 | ||||
-rw-r--r-- | encryption.hpp | 38 | ||||
-rw-r--r-- | server.cpp | 206 | ||||
-rw-r--r-- | structs.hpp | 60 | ||||
-rw-r--r-- | utils.hpp | 204 |
8 files changed, 710 insertions, 0 deletions
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 <cstdio> +#include <unistd.h> +#include <cstring> +#include <pthread.h> +#include <termios.h> +#include <vector> +#include <algorithm> +#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<int> 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 <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#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<int>* clients, Options* options); +int handle_incoming_packet(Packet* packet, int* clientfd, std::vector<int>* clients, Options* options); +int handle_packet_connect(PacketConnect* packet, int* clientfd, std::vector<int>* clients, Options* options); +int handle_packet_disconnect(PacketDisconnect* packet, int* clientfd, std::vector<int>* clients, Options* options); +int handle_packet_message(PacketMessage* packet, int* clientfd, std::vector<int>* clients, Options* options); +int notify_clients(Packet* packet, std::vector<int>* clients, Options* options); +int send_packet(Packet* packet, int* sockfd, Options* options, void* extra_data); +int add_client(int* clientfd, std::vector<int>* clients); +int remove_client(int* clientfd, std::vector<int>* clients); +int enable_echo(); +int disable_echo(); + +int start_client(std::vector<int>* 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<int>* 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<int>* 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<int>* 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<int>* clients, Options* options) +{ + if(options->server) + { + packet->client_fd = *clientfd; + std::vector<int>::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<int>* 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<int>* clients) +{ + std::vector<int>::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<int>* clients) +{ + std::vector<int>::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; +} |