#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;
}