summaryrefslogtreecommitdiff
path: root/server_network.c
diff options
context:
space:
mode:
authorTest_User <hax@andrewyu.org>2023-05-06 03:03:58 -0400
committerTest_User <hax@andrewyu.org>2023-05-06 03:03:58 -0400
commit32d75fadbf193218d0be42ca91b7688f1854f6e4 (patch)
treebacc1c35036a0b38fe63f6cffeb711be43cc7ae3 /server_network.c
parent329ca8e8f40efdd7838d40435b5f113d2877c13c (diff)
downloadcoupserv-32d75fadbf193218d0be42ca91b7688f1854f6e4.tar.gz
coupserv-32d75fadbf193218d0be42ca91b7688f1854f6e4.zip
Start adding client support
Diffstat (limited to 'server_network.c')
-rw-r--r--server_network.c631
1 files changed, 631 insertions, 0 deletions
diff --git a/server_network.c b/server_network.c
new file mode 100644
index 0000000..0d7beb4
--- /dev/null
+++ b/server_network.c
@@ -0,0 +1,631 @@
+// Server network command handlers for 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 <netdb.h>
+#include <arpa/inet.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "network.h"
+#include "types.h"
+#include "table.h"
+#include "tls.h"
+#include "config.h"
+#include "utils.h"
+#include "commands.h"
+
+int resolve(char *address, char *port, struct sockaddr *sockaddr) {
+ int success;
+ struct addrinfo hints = {0}, *info;
+
+ hints.ai_family = AF_INET;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = IPPROTO_TCP;
+ hints.ai_flags = AI_NUMERICSERV | AI_ADDRCONFIG;
+
+ success = getaddrinfo(address, port, &hints, &info);
+
+ if (success == 0) {
+ *sockaddr = *(info->ai_addr);
+ freeaddrinfo(info);
+ }
+
+ return success;
+}
+
+struct table server_network_commands = {0};
+struct table server_list = {0};
+struct table user_list = {0};
+
+int ping_handler(struct string sender, uint64_t argc, struct string *argv) {
+ if (argc < 2) {
+ puts("Invalid PING recieved! (Missing parameters)");
+ return 1;
+ }
+
+ uint64_t len = 1 + argv[1].len + 6 + argv[1].len + 1 + sender.len + 1;
+ char msg[len];
+ uint64_t offset = 0;
+ msg[0] = ':';
+ offset++;
+ memcpy(msg+offset, argv[1].data, argv[1].len);
+ offset += argv[1].len;
+ memcpy(msg+offset, " PONG ", 6);
+ offset += 6;
+ memcpy(msg+offset, argv[1].data, argv[1].len);
+ offset += argv[1].len;
+ msg[offset] = ' ';
+ offset++;
+ memcpy(msg+offset, sender.data, sender.len);
+ offset += sender.len;
+ msg[offset] = '\n';
+
+ SEND(((struct string){msg, len}));
+
+ return 0;
+}
+
+int server_handler(struct string sender, uint64_t argc, struct string *argv) {
+ if (argc < 5) {
+ puts("Invalid SERVER recieved! (Missing parameters)");
+ return 1;
+ }
+
+ if (argv[2].len > 20) {
+ puts("Invalid SERVER recieved! (Distance too large)");
+ return 1;
+ }
+
+ uint8_t err;
+ uint64_t distance = str_to_unsigned(argv[2], &err);
+ if (err) {
+ puts("Invalid SERVER recieved! (Invalid distance given)");
+ return 1;
+ }
+
+ if (sender.len != 0) {
+ struct remote_server *from = get_table_index(server_list, sender);
+ if (!from) {
+ puts("Invalid SERVER recieved! (Unknown source)");
+ return 1;
+ }
+
+ distance += from->distance + 1;
+ }
+
+ struct string address;
+ address.data = malloc(argv[0].len);
+ if (address.data == 0)
+ goto server_handler_oom;
+
+ address.len = argv[0].len;
+ memcpy(address.data, argv[0].data, argv[0].len);
+
+ struct string name;
+ name.data = malloc(argv[4].len);
+ if (name.data == 0)
+ goto server_handler_free_address;
+
+ name.len = argv[4].len;
+ memcpy(name.data, argv[4].data, argv[4].len);
+
+ struct remote_server *server = malloc(sizeof(*server));
+ if (server == 0)
+ goto server_handler_free_name;
+
+ struct string via;
+ if (sender.len != 0) { // connected to the sender
+ via.data = malloc(sender.len);
+ if (!via.data)
+ goto server_handler_free_server;
+
+ via.len = sender.len;
+ memcpy(via.data, sender.data, sender.len);
+ } else { // connected directly to us
+ via = (struct string){0};
+ }
+
+ *server = (struct remote_server){
+ .name = name,
+ .address = address,
+ .distance = distance,
+ .via = via,
+ };
+
+ if (set_table_index(&server_list, argv[3], server) != 0)
+ goto server_handler_free_via;
+
+ return 0;
+
+ server_handler_free_via:
+ if (sender.len != 0)
+ free(via.data);
+ server_handler_free_server:
+ free(server);
+ server_handler_free_name:
+ free(name.data);
+ server_handler_free_address:
+ free(address.data);
+ server_handler_oom:
+ puts("OOM! (server_handler)");
+
+ return 1;
+}
+
+int uid_handler(struct string sender, uint64_t argc, struct string *argv) {
+ if (argc < 10) {
+ puts("Invalid UID recieved! (Missing parameters)");
+ return 1;
+ }
+
+ if (argv[1].len > 20 || argv[7].len > 20) {
+ puts("Invalid UID recieved! (Timestamp too long)");
+ return 1;
+ }
+
+ if (sender.len == 0) {
+ puts("Invalid UID recieved! (No source)");
+ return 1;
+ }
+
+ if (get_table_index(user_list, argv[0])) {
+ WRITES(2, STRING("Invalid UID revieved! (Attempted to create already-existing user)\n"));
+ return 1;
+ }
+
+ // TODO: modes
+
+ uint8_t err;
+ uint64_t nick_ts = str_to_unsigned(argv[1], &err);
+ if (err) {
+ puts("Invalid UID recieved! (Invalid nick timestamp)");
+ return 1;
+ }
+
+ uint64_t user_ts = str_to_unsigned(argv[7], &err);
+ if (err) {
+ puts("Invalid UID recieved! (Invalid user timestamp)");
+ return 1;
+ }
+
+ struct string server;
+ server.data = malloc(sender.len);
+ if (!server.data)
+ goto uid_handler_oom;
+ server.len = sender.len;
+ memcpy(server.data, sender.data, sender.len);
+
+ struct string nick;
+ nick.data = malloc(argv[2].len);
+ if (!nick.data)
+ goto uid_handler_free_server;
+ nick.len = argv[2].len;
+ memcpy(nick.data, argv[2].data, argv[2].len);
+
+ struct string hostname;
+ hostname.data = malloc(argv[3].len);
+ if (!hostname.data)
+ goto uid_handler_free_nick;
+ hostname.len = argv[3].len;
+ memcpy(hostname.data, argv[3].data, argv[3].len);
+
+ struct string vhost;
+ vhost.data = malloc(argv[4].len);
+ if (!vhost.data)
+ goto uid_handler_free_hostname;
+ vhost.len = argv[4].len;
+ memcpy(vhost.data, argv[4].data, argv[4].len);
+
+ struct string ident;
+ ident.data = malloc(argv[5].len);
+ if (!ident.data)
+ goto uid_handler_free_vhost;
+ ident.len = argv[5].len;
+ memcpy(ident.data, argv[5].data, argv[5].len);
+
+ struct string ip;
+ ip.data = malloc(argv[6].len);
+ if (!ip.data)
+ goto uid_handler_free_ident;
+ ip.len = argv[6].len;
+ memcpy(ip.data, argv[6].data, argv[6].len);
+
+ struct string realname;
+ realname.data = malloc(argv[argc - 1].len);
+ if (!realname.data)
+ goto uid_handler_free_ip;
+ realname.len = argv[argc - 1].len;
+ memcpy(realname.data, argv[argc - 1].data, argv[argc - 1].len);
+
+ struct user_info *user = malloc(sizeof(*user));
+ if (!user)
+ goto uid_handler_free_realname;
+
+ *user = (struct user_info){
+ .nick_ts = nick_ts,
+ .user_ts = user_ts,
+ .server = server,
+ .nick = nick,
+ .hostname = hostname,
+ .vhost = vhost,
+ .ident = ident,
+ .ip = ip,
+ .realname = realname,
+ .metadata = (struct table){.array = malloc(0), .len = 0},
+ };
+
+ if (set_table_index(&user_list, argv[0], user) != 0)
+ goto uid_handler_free_user;
+
+ return 0;
+
+ uid_handler_free_user:
+ free(user);
+ uid_handler_free_realname:
+ free(realname.data);
+ uid_handler_free_ip:
+ free(ip.data);
+ uid_handler_free_ident:
+ free(ident.data);
+ uid_handler_free_vhost:
+ free(vhost.data);
+ uid_handler_free_hostname:
+ free(hostname.data);
+ uid_handler_free_nick:
+ free(nick.data);
+ uid_handler_free_server:
+ free(server.data);
+ uid_handler_oom:
+ puts("OOM! (uid_handler)");
+
+ return 1;
+}
+
+int opertype_handler(struct string sender, uint64_t argc, struct string *argv) {
+ if (argc < 1) {
+ WRITES(2, STRING("Invalid OPERTYPE recieved! (Missing parameters)\n"));
+ return 1;
+ }
+
+ if (sender.len == 0) {
+ WRITES(2, STRING("Invalid OPERTYPE recieved! (No source)\n"));
+ return 1;
+ }
+
+ struct user_info *user = get_table_index(user_list, sender);
+ if (!user) {
+ WRITES(2, STRING("Server "));
+ WRITES(2, sender);
+ WRITES(2, STRING(" attempted to set OPERTYPE on a nonexistent user!\n"));
+ return 1;
+ }
+
+ struct string opertype = {.data = malloc(argv[0].len), .len = argv[0].len};
+ if (!opertype.data) {
+ WRITES(2, STRING("OOM! (opertype_handler)\n"));
+ return 1;
+ }
+ memcpy(opertype.data, argv[0].data, argv[0].len);
+
+ if (user->opertype.len)
+ free(user->opertype.data);
+
+ user->opertype = opertype;
+
+ return 0;
+}
+
+int quit_handler(struct string sender, uint64_t argc, struct string *argv) {
+ struct user_info *info = remove_table_index(&user_list, sender);
+ if (!info) {
+ WRITES(2, STRING("QUIT: Unknown user!\n"));
+ return 1;
+ }
+
+ free(info->server.data);
+ free(info->nick.data);
+ if (info->opertype.len)
+ free(info->opertype.data);
+
+ // TODO: metadata
+ free(info->metadata.array);
+ free(info->realname.data);
+ free(info->hostname.data);
+ free(info->ip.data);
+ free(info->vhost.data);
+ free(info);
+
+ return 0;
+}
+
+int kill_handler(struct string sender, uint64_t argc, struct string *argv) {
+ if (argc < 2) {
+ WRITES(2, STRING("Invalid KILL recieved! (Missing parameters)\n"));
+ return 1;
+ }
+
+// TODO: Get accurate list of what got killed, what to rejoin, etc
+
+ struct string id = STRING("1HC000000");
+ if (argv[0].len != id.len || memcmp(argv[0].data, id.data, id.len) != 0) {
+ WRITES(2, STRING("Invalid KILL recieved! (Unknown user)\n"));
+ return 1;
+ }
+
+ char current_time[21]; // C HaxServ will be deprecated long before we reach 20-digit timestamps
+ snprintf(current_time, 21, "%ld", time(NULL));
+ SEND(STRING("UID 1HC000000 "));
+ SEND(((struct string){current_time, strlen(current_time)}));
+ SEND(STRING(" "));
+ SEND(nick);
+ SEND(STRING(" "));
+ SEND(hostmask);
+ SEND(STRING(" "));
+ SEND(hostmask);
+ SEND(STRING(" HaxServ 192.168.1.1 "));
+ SEND(((struct string){current_time, strlen(current_time)}));
+ SEND(STRING(" +k :HaxServ\n:1HC000000 OPERTYPE Admin\n"));
+
+ for (uint64_t i = 0; i < num_prejoin_channels; i++) {
+ SEND(STRING("FJOIN "));
+ SEND(prejoin_channels[i]);
+ SEND(STRING(" "));
+ SEND(((struct string){current_time, strlen(current_time)}));
+ SEND(STRING(" + :,1HC000000\nMODE "));
+ SEND(prejoin_channels[i]);
+ SEND(STRING(" +o 1HC000000\n"));
+ }
+
+ return 0;
+}
+
+int nick_handler(struct string sender, uint64_t argc, struct string *argv) {
+ if (argc < 2) {
+ WRITES(2, STRING("Invalid NICK recieved! (Missing parameters)\n"));
+ return 1;
+ }
+
+ struct user_info *info = get_table_index(user_list, sender);
+ if (!info) {
+ WRITES(2, STRING("NICK: Unknown user!\n"));
+ return 1;
+ }
+
+ void *tmp = malloc(argv[0].len);
+ if (!tmp) {
+ WRITES(2, STRING("OOM! (nick_handler)\n"));
+ return 1;
+ }
+ memcpy(tmp, argv[0].data, argv[0].len);
+
+ free(info->nick.data);
+ info->nick.data = tmp;
+ info->nick.len = argv[0].len;
+
+ if (argv[1].len > 20) {
+ WRITES(2, STRING("Invalid NICK recieved! (Timestamp too long)\n"));
+ return 1;
+ }
+
+ uint8_t err;
+ uint64_t ts = str_to_unsigned(argv[1], &err);
+ if (err) {
+ WRITES(2, STRING("Invalid NICK recieved! (Invalid timestamp)\n"));
+ return 1;
+ }
+ info->nick_ts = ts;
+
+ return 0;
+}
+
+int privmsg_handler(struct string sender, uint64_t argc, struct string *argv) {
+ if (argc < 2) {
+ WRITES(2, STRING("Invalid PRIVMSG recieved (Missing parameters)\n"));
+ return 1;
+ }
+
+ if (sender.len == 0) {
+ WRITES(2, STRING("Invalid PRIVMSG recieved (No source)\n"));
+ return 1;
+ }
+
+ uint64_t offset;
+ if (argv[0].data[0] == '#') {
+ if (argv[1].len < command_prefix.len || memcmp(argv[1].data, command_prefix.data, command_prefix.len) != 0)
+ return 0;
+
+ offset = command_prefix.len;
+ } else {
+ offset = 0;
+ }
+
+ if (offset >= argv[1].len || argv[1].data[offset] == ' ')
+ return 0;
+
+ uint64_t command_argc = 0;
+ uint64_t old_offset = offset;
+ while (offset < argv[1].len) {
+ while (offset < argv[1].len && argv[1].data[offset] != ' ')
+ offset++;
+
+ command_argc++;
+
+ while (offset < argv[1].len && argv[1].data[offset] == ' ')
+ offset++;
+ }
+ offset = old_offset;
+
+ struct string command_argv[command_argc]; // argv[0] in this case is the command itself, unlike network command handlers... might change one of these two later to match
+ uint64_t i = 0;
+ while (offset < argv[1].len) {
+ command_argv[i].data = argv[1].data+offset;
+ uint64_t start = offset;
+
+ while (offset < argv[1].len && argv[1].data[offset] != ' ')
+ offset++;
+
+ command_argv[i].len = offset - start;
+
+ while (offset < argv[1].len && argv[1].data[offset] == ' ')
+ offset++;
+
+ i++;
+ }
+
+ argv[1].data += old_offset;
+ argv[1].len -= old_offset;
+ struct command_def *cmd = get_table_index(user_commands, command_argv[0]);
+ if (cmd) {
+ if (!cmd->local_only) {
+ if (cmd->privs.len != 0 && sender.len != 3) { // servers always count as oper :P
+ struct user_info *user = get_table_index(user_list, sender);
+ if (!user) {
+ WRITES(2, STRING("User is unknown!\n"));
+
+ SEND(STRING(":1HC000000 NOTICE "));
+ if (argv[0].data[0] == '#')
+ SEND(argv[0]);
+ else
+ SEND(sender);
+ SEND(STRING(" :You don't seem to exist... and neither do I!\n"));
+
+ return 1; // have already desynced
+ }
+
+ if (user->opertype.len != cmd->privs.len || memcmp(user->opertype.data, cmd->privs.data, cmd->privs.len)) {
+ // TODO: complain about missing privs
+ SEND(STRING(":1HC000000 NOTICE "));
+ if (argv[0].data[0] == '#')
+ SEND(argv[0]);
+ else
+ SEND(sender);
+ SEND(STRING(" :You are not authorized to execute this command.\n"));
+
+ return 0;
+ }
+ }
+
+ SEND(STRING(":1HC000000 PRIVMSG "));
+ SEND(log_channel);
+ if (sender.len == 3) {
+ SEND(STRING(" :Server "));
+ SEND(sender);
+ } else {
+ struct user_info *user = get_table_index(user_list, sender);
+ if (user) {
+ SEND(STRING(" :User "));
+ SEND(user->nick);
+ } else {
+ SEND(STRING(" :An unknown user (something desycned... this shouldn't happen)"));
+ }
+ }
+
+ SEND(STRING(" executes `"));
+ SEND(argv[1]);
+ SEND(STRING("'\n"));
+
+ return cmd->func(sender, argv[1], argv[0], command_argc, command_argv);
+ } else {
+ // TODO: complain about remote access
+ WRITES(1, STRING("Not executing local-only command from a remote source!\n"));
+ return 0;
+ }
+ } else {
+ SEND(STRING(":1HC000000 NOTICE "));
+ if (argv[0].data[0] == '#')
+ SEND(argv[0]);
+ else
+ SEND(sender);
+ SEND(STRING(" :Unknown command: " "\x03" "04"));
+ SEND(argv[1]);
+ SEND(STRING("\n"));
+ return 0;
+ }
+}
+
+int initservernetwork(void) {
+ server_network_commands.array = malloc(0);
+ server_list.array = malloc(0);
+ user_list.array = malloc(0);
+
+ set_table_index(&server_network_commands, STRING("PING"), &ping_handler);
+ set_table_index(&server_network_commands, STRING("SERVER"), &server_handler);
+ set_table_index(&server_network_commands, STRING("UID"), &uid_handler);
+ set_table_index(&server_network_commands, STRING("OPERTYPE"), &opertype_handler);
+ set_table_index(&server_network_commands, STRING("PRIVMSG"), &privmsg_handler);
+ set_table_index(&server_network_commands, STRING("QUIT"), &quit_handler);
+ set_table_index(&server_network_commands, STRING("KILL"), &kill_handler);
+ set_table_index(&server_network_commands, STRING("NICK"), &nick_handler);
+
+ init_user_commands();
+
+ int retval = connect_tls();
+ if (retval != 0) {
+ printf("connect_tls(): %d\n", retval);
+ return 1;
+ }
+
+ // probably inefficient to be calling SSL_write this frequently, but also less effort
+ SEND(STRING("SERVER hax.irc.andrewyu.org "));
+ SEND(send_password);
+ SEND(STRING(" 0 1HC :HaxServ\n"));
+ SEND(STRING("BURST "));
+ char current_time[21]; // C HaxServ will be deprecated long before we reach 20-digit timestamps
+ snprintf(current_time, 21, "%ld", time(NULL));
+ SEND(((struct string){current_time, strlen(current_time)}));
+ SEND(STRING("\nUID 1HC000000 "));
+ SEND(((struct string){current_time, strlen(current_time)}));
+ SEND(STRING(" "));
+ SEND(nick);
+ SEND(STRING(" "));
+ SEND(hostmask);
+ SEND(STRING(" "));
+ SEND(hostmask);
+ SEND(STRING(" HaxServ 192.168.1.1 "));
+ SEND(((struct string){current_time, strlen(current_time)}));
+ SEND(STRING(" +k :HaxServ\n:1HC000000 OPERTYPE Admin\n"));
+
+ for (uint64_t i = 0; i < num_prejoin_channels; i++) {
+ SEND(STRING("FJOIN "));
+ SEND(prejoin_channels[i]);
+ SEND(STRING(" "));
+ SEND(((struct string){current_time, strlen(current_time)}));
+ SEND(STRING(" + :,1HC000000\nMODE "));
+ SEND(prejoin_channels[i]);
+ SEND(STRING(" +k 1HC000000\n:1HC000000 OPERTYPE Admin\n"));
+ }
+
+ SEND(STRING("ENDBURST\n"));
+
+ return 0;
+}