From e4b5445b3ca844e568a84abbf931a026a6ca6226 Mon Sep 17 00:00:00 2001 From: Test_User Date: Wed, 3 May 2023 22:57:53 -0400 Subject: C HaxServ --- network.c | 463 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 463 insertions(+) create mode 100644 network.c (limited to 'network.c') diff --git a/network.c b/network.c new file mode 100644 index 0000000..3a92bd5 --- /dev/null +++ b/network.c @@ -0,0 +1,463 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 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; + uint8_t 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'; + + SSL_write(ssl, 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; + } + + // 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; + + 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 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) { + WRITES(1, STRING("Message is not a command.\n")); + return 0; // not for us + } + + offset = command_prefix.len; + } else { + offset = 0; + } + + if (offset >= argv[1].len || argv[1].data[offset] == ' ') { + // TODO: complain about empty command + WRITES(1, STRING("Command is empty?\n")); + 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) { + // TODO: complain about unknown user + WRITES(1, STRING("User is unknown!\n")); + return 0; + } + + if (user->opertype.len != cmd->privs.len || memcmp(user->opertype.data, cmd->privs.data, cmd->privs.len)) { + // TODO: complain about missing privs + WRITES(1, STRING("User lacks privs for this command!\n")); + return 0; + } + } + + WRITES(1, STRING("Executing command ")); + WRITES(1, command_argv[0]); + write(1, "\n", 1); + 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 { + // TODO: complain about unknown command + WRITES(1, STRING("Command is unknown!\n")); + WRITES(1, command_argv[0]); + write(1, "\n", 1); + return 0; + } +} + +int initservernetwork(void) { + network_commands.array = malloc(0); + server_list.array = malloc(0); + user_list.array = malloc(0); + + set_table_index(&network_commands, STRING("PING"), &ping_handler); + set_table_index(&network_commands, STRING("SERVER"), &server_handler); + set_table_index(&network_commands, STRING("UID"), &uid_handler); + set_table_index(&network_commands, STRING("OPERTYPE"), &opertype_handler); + set_table_index(&network_commands, STRING("PRIVMSG"), &privmsg_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 ")); + uint8_t current_time[21]; // C HaxServ will be deprecated long before we reach 20-digit timestamps + snprintf(current_time, 21, "%d", 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(" +o 1HC000000\n")); + } + + SEND(STRING("ENDBURST\n")); + + return 0; +} -- cgit v1.2.3