about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--Makefile7
-rw-r--r--README.md14
-rw-r--r--client.cpp179
-rw-r--r--encryption.hpp38
-rw-r--r--server.cpp206
-rw-r--r--structs.hpp60
-rw-r--r--utils.hpp204
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;
+}