// Main loop part of HaxServ // // Written by: Test_User // // 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 #include #include #include #include #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); free(full_msg.data); listen(client_listen_fd, 1); } } pthread_t client_thread_id; int main(void) { initservernetwork(); initclientnetwork(); 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 100 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; offset = i + 1; source.len = 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; }