1
0
Fork 0
coupserv/main.c
2024-06-15 11:17:22 -04:00

469 lines
11 KiB
C

// Main loop part of HaxServ
//
// Written by: Test_User <hax@andrewyu.org>
//
// This is free and unencumbered software released into the public
// domain.
//
// Anyone is free to copy, modify, publish, use, compile, sell, or
// distribute this software, either in source code form or as a compiled
// binary, for any purpose, commercial or non-commercial, and by any
// means.
//
// In jurisdictions that recognize copyright laws, the author or authors
// of this software dedicate any and all copyright interest in the
// software to the public domain. We make this dedication for the benefit
// of the public at large and to the detriment of our heirs and
// successors. We intend this dedication to be an overt act of
// relinquishment in perpetuity of all present and future rights to this
// software under copyright law.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
// IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
// OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
#include <gnutls/gnutls.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include "network.h"
#include "config.h"
#include "types.h"
#include "tls.h"
#include "types.h"
void *client_loop(void *ign) {
pthread_mutex_lock(&send_lock);
while (1) {
struct string full_msg = {.data = malloc(0), .len = 0};
pthread_mutex_unlock(&send_lock);
client_fd = accept(client_listen_fd, NULL, NULL);
pthread_mutex_lock(&send_lock);
listen(client_listen_fd, 0);
client_connected = 0;
while (1) {
char data[512];
pthread_mutex_unlock(&send_lock); // TODO: proper locking, this works for now but is certainly inefficient
uint64_t new_len;
{
ssize_t len = read(client_fd, data, 512);
if (len < 0)
new_len = 0;
else
new_len = (size_t)len;
}
pthread_mutex_lock(&send_lock);
if (new_len == 0) {
goto disconnect_client;
}
uint8_t found = 0;
uint64_t msg_len;
for (uint64_t i = 0; i < new_len; i++) {
if (data[i] == '\n') {
found = 1;
msg_len = i + full_msg.len;
break;
}
}
void *tmp = realloc(full_msg.data, full_msg.len+new_len);
if (tmp == 0 && full_msg.len+new_len != 0) {
WRITES(2, STRING("OOM... disconnect client.\n"));
goto disconnect_client;
}
full_msg.data = tmp;
memcpy(full_msg.data+full_msg.len, data, new_len);
full_msg.len += new_len;
if (!found)
continue;
while (1) {
if (full_msg.data[msg_len - 1] == '\r')
msg_len--;
uint64_t offset = 0;
while (offset < msg_len && full_msg.data[offset] == ' ')
offset++;
if (offset == msg_len) {
puts("Protocol violation: No command.");
goto disconnect_client;
}
struct string command;
command.data = full_msg.data+offset;
found = 0;
for (uint64_t i = offset; i < msg_len; i++) {
if (full_msg.data[i] == ' ') {
found = 1;
command.len = i - offset;
offset = i;
break;
}
}
if (!found) {
command.len = msg_len - offset;
offset = msg_len;
}
while (offset < msg_len && full_msg.data[offset] == ' ')
offset++;
uint64_t argc = 0;
uint64_t old_offset = offset;
if (offset < msg_len) {
while (offset < msg_len) {
if (full_msg.data[offset] == ':') {
argc++;
break;
}
while (offset < msg_len && full_msg.data[offset] != ' ')
offset++;
argc++;
while (offset < msg_len && full_msg.data[offset] == ' ')
offset++;
}
}
offset = old_offset;
struct string argv[argc];
if (offset < msg_len) {
uint64_t i = 0;
while (offset < msg_len) {
if (full_msg.data[offset] == ':') {
argv[i].data = full_msg.data+offset+1;
argv[i].len = msg_len - offset - 1;
break;
}
argv[i].data = full_msg.data+offset;
uint64_t start = offset;
while (offset < msg_len && full_msg.data[offset] != ' ')
offset++;
argv[i].len = offset - start;
while (offset < msg_len && full_msg.data[offset] == ' ')
offset++;
i++;
}
}
int (*func)(uint64_t argc, struct string *argv) = get_table_index(client_network_commands, command);
#if LOGALL
#if COLORIZE
WRITES(1, STRING("\x1b[34m[Client->Us] \x1b[33m"));
#else
WRITES(1, STRING("[Client->Us] "));
#endif
#if COLORIZE
write(1, full_msg.data, msg_len);
WRITES(1, STRING("\x1b[0m\n"));
#else
write(1, full_msg.data, msg_len+(full_msg.data[msg_len] == '\r' ? 2 : 1)); // +2 or 1: \r\n or \n
#endif
#endif
if (func == 0) {
#if !LOGALL
WRITES(2, STRING("[Client] "));
write(2, full_msg.data, msg_len+(full_msg.data[msg_len] == '\r' ? 2 : 1));
#endif
WRITES(2, STRING("WARNING: Command is unknown, ignoring...\n"));
WRITES(2, STRING("\n"));
} else {
#if LOGALL
WRITES(1, STRING("\n"));
#endif
int err = func(argc, argv);
if (err) {
WRITES(1, STRING("Disconnecting client by result of the network command handler...\n"));
goto disconnect_client;
}
}
if (full_msg.data[msg_len] == '\r')
msg_len++;
memmove(full_msg.data, full_msg.data+msg_len+1, full_msg.len - msg_len - 1);
full_msg.len -= msg_len+1;
found = 0;
for (uint64_t i = 0; i < full_msg.len; i++) {
if (full_msg.data[i] == '\n') {
found = 1;
msg_len = i;
break;
}
}
if (found == 0) {
void *tmp = realloc(full_msg.data, full_msg.len);
if (tmp == 0 && full_msg.len != 0) {
puts("AAAAAAAAA (OOM shrinking allocated data?)");
goto disconnect_client;
}
full_msg.data = tmp;
break;
}
}
}
disconnect_client:
if (client_connected) {
SEND(STRING(":1HC000001 QUIT :Ping timeout: -240 seconds\n"));
client_connected = 0;
remove_user(STRING("1HC000001"), STRING("Ping timeout: -240 seconds\n"));
}
close(client_fd);
client_fd = -1;
free(full_msg.data);
listen(client_listen_fd, 1);
}
}
pthread_t client_thread_id;
int main(void) {
if (initservernetwork() != 0)
return 1;
if (initclientnetwork() != 0)
return 1;
pthread_create(&client_thread_id, NULL, client_loop, NULL);
pthread_mutex_lock(&send_lock);
struct string full_msg = {malloc(0), 0};
while (1) {
char data[512];
char timeout;
uint64_t new_len;
char last_timeout = 0;
while (1) {
pthread_mutex_unlock(&send_lock);
new_len = RECV(data, sizeof(data), &timeout);
pthread_mutex_lock(&send_lock);
if (!timeout)
break;
if (last_timeout) {
break;
} else {
SEND(STRING(":1HC PING 1HC 100\n")); // TODO: Fix this as well
last_timeout = 1;
}
}
if (new_len == 0) {
WRITES(1, STRING("Disconnected.\n"));
return 0;
}
uint8_t found = 0;
uint64_t msg_len;
for (uint64_t i = 0; i < new_len; i++) {
if (data[i] == '\n') {
found = 1;
msg_len = i + full_msg.len;
break;
}
}
void *tmp = realloc(full_msg.data, full_msg.len+new_len);
if (tmp == 0 && full_msg.len+new_len != 0) {
WRITES(2, STRING("OOM... currently just exiting bc there's no automatic reconnect in here yet, and the only sane solution to this is resyncing.\n"));
return 1;
}
full_msg.data = tmp;
memcpy(full_msg.data+full_msg.len, data, new_len);
full_msg.len += new_len;
if (!found)
continue;
while (1) {
uint64_t offset = 0;
while (offset < msg_len && full_msg.data[offset] == ' ')
offset++;
if (msg_len == offset) {
WRITES(2, STRING("Protocol violation: Empty message.\n"));
return 2;
}
struct string source;
if (full_msg.data[offset] == ':') {
source.data = full_msg.data + 1 + offset;
found = 0;
for (uint64_t i = offset + 1; i < msg_len; i++) {
if (full_msg.data[i] == ' ') {
found = 1;
source.len = i - offset - 1;
offset = i + 1;
break;
}
}
if (!found || source.len + 1 == msg_len) {
WRITES(2, STRING("Protocol violation: Sender but no command."));
return 2;
}
} else {
source = (struct string){0};
}
while (offset < msg_len && full_msg.data[offset] == ' ')
offset++;
if (offset == msg_len) {
WRITES(2, STRING("Protocol violation: No command."));
return 2;
}
struct string command;
command.data = full_msg.data+offset;
found = 0;
for (uint64_t i = offset; i < msg_len; i++) {
if (full_msg.data[i] == ' ') {
found = 1;
command.len = i - offset;
offset = i;
break;
}
}
if (!found) {
command.len = msg_len - offset;
offset = msg_len;
}
while (offset < msg_len && full_msg.data[offset] == ' ')
offset++;
uint64_t argc = 0;
uint64_t old_offset = offset;
if (offset < msg_len) {
while (offset < msg_len) {
if (full_msg.data[offset] == ':') {
argc++;
break;
}
while (offset < msg_len && full_msg.data[offset] != ' ')
offset++;
argc++;
while (offset < msg_len && full_msg.data[offset] == ' ')
offset++;
}
}
offset = old_offset;
struct string argv[argc];
if (offset < msg_len) {
uint64_t i = 0;
while (offset < msg_len) {
if (full_msg.data[offset] == ':') {
argv[i].data = full_msg.data+offset+1;
argv[i].len = msg_len - offset - 1;
break;
}
argv[i].data = full_msg.data+offset;
uint64_t start = offset;
while (offset < msg_len && full_msg.data[offset] != ' ')
offset++;
argv[i].len = offset - start;
while (offset < msg_len && full_msg.data[offset] == ' ')
offset++;
i++;
}
}
int (*func)(struct string source, uint64_t argc, struct string *argv) = get_table_index(server_network_commands, command);
#if LOGALL
#if COLORIZE
WRITES(1, STRING("\x1b[35m[Server->Us] \x1b[36m"));
#else
WRITES(1, STRING("[Server->Us] "));
#endif
#if COLORIZE
write(1, full_msg.data, msg_len);
WRITES(1, STRING("\x1b[0m\n"));
#else
write(1, full_msg.data, msg_len+1); // +1: \n
#endif
#endif
if (func == 0) {
#if !LOGALL
WRITES(2, STRING("[Server] "));
write(2, full_msg.data, msg_len+1); // +1: \n
#endif
WRITES(2, STRING("WARNING: Command is unknown, ignoring...\n"));
WRITES(2, STRING("\n"));
} else {
#if LOGALL
WRITES(1, STRING("\n"));
#endif
int err = func(source, argc, argv);
if (err) {
#if !LOGALL
WRITES(2, STRING("Message was: [Server] "));
write(2, full_msg.data, msg_len+1);
#endif
WRITES(1, STRING("Disconnecting by result of the network command handler...\n"));
return 0;
}
}
memmove(full_msg.data, full_msg.data+msg_len+1, full_msg.len - msg_len - 1);
full_msg.len -= msg_len+1;
found = 0;
for (uint64_t i = 0; i < full_msg.len; i++) {
if (full_msg.data[i] == '\n') {
found = 1;
msg_len = i;
break;
}
}
if (found == 0) {
void *tmp = realloc(full_msg.data, full_msg.len);
if (tmp == 0 && full_msg.len != 0) {
puts("AAAAAAAAA (OOM shrinking allocated data?)");
return 1;
}
full_msg.data = tmp;
break;
}
}
}
return 0;
}