forked from noisytoot/rubyserv-iirc
469 lines
11 KiB
C
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;
|
|
}
|