diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | LICENSE | 23 | ||||
-rw-r--r-- | Makefile | 4 | ||||
-rw-r--r-- | README.md | 32 | ||||
-rw-r--r-- | client_network.c | 42 | ||||
-rw-r--r-- | commands.c | 288 | ||||
-rw-r--r-- | config.h | 3 | ||||
-rw-r--r-- | general_network.c | 2 | ||||
-rw-r--r-- | main.c | 19 | ||||
-rw-r--r-- | network.h | 4 | ||||
-rw-r--r-- | server_network.c | 54 | ||||
-rw-r--r-- | table.c | 12 | ||||
-rw-r--r-- | tls.c | 17 | ||||
-rw-r--r-- | utils.c | 10 |
14 files changed, 422 insertions, 89 deletions
@@ -4,3 +4,4 @@ config.c haxserv core .makeopts +output @@ -0,0 +1,23 @@ +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. @@ -28,7 +28,7 @@ #INCLUDEFLAGS = -CFLAGS += $(INCLUDEFLAGS) -D_REENTRANT -ggdb3 -Wall -Wextra -Wsign-conversion -Wno-unused-parameter $(shell pkg-config gnutls --cflags) +CFLAGS += $(INCLUDEFLAGS) -D_REENTRANT -ggdb3 -Wall -Wextra -Wsign-conversion -Wno-unused-parameter $(shell pkg-config gnutls --cflags) -std=gnu99 LDFLAGS = -lpthread $(shell pkg-config gnutls --libs) @@ -74,7 +74,7 @@ $(shell printf '%s%s\n' 'LAST_SAFE_STACK = ' $(SAFE_STACK) >> .makeopts) $(shell $(RM) haxserv *.o) endif -DEPS = $(shell $(CC) $(INCLUDEFLAGS) -MM -MT $(1).o $(1).c | sed -z 's/\\\n //g') +DEPS = $(shell $(CC) $(INCLUDEFLAGS) -MM -MT $(1).o $(1).c | sed 's_\\$$__g') ifeq ($(VERBOSE), 1) CFLAGS += -DLOGALL=1 diff --git a/README.md b/README.md new file mode 100644 index 0000000..9aa8353 --- /dev/null +++ b/README.md @@ -0,0 +1,32 @@ +# HaxServ, a public domain chaotic pseudoserver for InspIRCd + +## Features +* Links with a InspIRCd v2/v3 uplink server via the 1202 protocol with GnuTLS +* Accepts one local client connection (not a bouncer yet) +* Lets network operators wreck havoc (e.g. inject raw S2S commands) + +## Goal +* Allows the operator to study the behavior of the S2S protocol +* Annoy users + +## Commands +"Standard" bot commands like `echo`, `tell`, and `help` not shown. + +|Command|Description| +|-------|-----------| +|`:`|Sends a raw S2S message to the uplink server| +|`sus`|Randomly kills the user issuing the command, Among Us-style| +|`cr`|Randomly kills the user issuing the command, Mindustry-style| +|`spam`|Repeats #2, #1 times| +|`sh`|Executes a command locally and sends the output (message must be from the local connection)| +|`kill_old`|Kills connections older than #1, and OperServ because OperServ is wrong| +|`clear`|Clears a channel| + +## Other information +* Reference + [https://irc.runxiyu.org/haxserv.html](https://irc.runxiyu.org/haxserv.html) + for a list of known instances on public networks +* Check out [HaxIRCd](https://git.andrewyu.org/hax/haxircd.git/) for a + work-in-progress rewrite that aims to support redundent server links and + multiple protocols, slightly inspired by + [Sable](https://github.com/Libera-Chat/sable). diff --git a/client_network.c b/client_network.c index 90a406a..0050812 100644 --- a/client_network.c +++ b/client_network.c @@ -29,6 +29,7 @@ #include <gnutls/gnutls.h> #include <netdb.h> #include <arpa/inet.h> +#include <errno.h> #include <sys/types.h> #include <netinet/in.h> #include <sys/socket.h> @@ -156,10 +157,6 @@ int add_local_client(struct string uid, struct string nick_arg, struct string vh SEND(NULSTR(string_time)); SEND(STRING(" +k :")); SEND(realname); - SEND(STRING("\n:")); - SEND(uid); - SEND(STRING(" OPERTYPE ")); - SEND(opertype); SEND(STRING("\n")); if (fake_cert) { SEND(STRING(":1HC METADATA ")); @@ -168,6 +165,13 @@ int add_local_client(struct string uid, struct string nick_arg, struct string vh SEND(client_cert); SEND(STRING("\n")); } + if (!STRING_EQ(uid, STRING("1HC000000"))) { // Don't oper haxserv, because echo is unprivileged + SEND(STRING(":")); + SEND(uid); + SEND(STRING(" OPERTYPE ")); + SEND(opertype); + SEND(STRING("\n")); + } return 0; @@ -469,7 +473,7 @@ int client_privmsg_handler(uint64_t argc, struct string *argv) { client_nick, STRING(" executes `"), argv[1], - STRING("'\n"), + STRING("'"), }; privmsg(STRING("1HC000000"), log_channel, sizeof(message)/sizeof(*message), message); @@ -479,7 +483,8 @@ int client_privmsg_handler(uint64_t argc, struct string *argv) { if (argv[0].data[0] == '#') { SEND(STRING(":1HC000000 NOTICE ")); SEND(argv[0]); - SEND(STRING(" :Unknown command: " "\x03" "04")); + SEND(STRING(" :Unknown command: ")); + SEND(command_prefix); SEND(command_argv[0]); SEND(STRING("\n")); } @@ -531,6 +536,10 @@ int client_mode_handler(uint64_t argc, struct string *argv) { int client_fd = -1; int client_listen_fd; +int client_listen_fd_ready = 0; +struct sockaddr_in client_listen_fd_bind_addr = { + .sin_family = AF_INET, +}; int initclientnetwork(void) { client_network_commands.array = malloc(0); @@ -551,21 +560,26 @@ int initclientnetwork(void) { int one = 1; setsockopt(client_listen_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)); - struct sockaddr_in localhost = { - .sin_family = AF_INET, - .sin_port = htons(6667), - }; - inet_pton(AF_INET, "127.0.0.1", &localhost.sin_addr); // this is indeed localhost for mine, and I have no intent to change this - bind(client_listen_fd, (struct sockaddr*)&localhost, sizeof(localhost)); + client_listen_fd_bind_addr.sin_port = htons(6667); - listen(client_listen_fd, 1); + inet_pton(AF_INET, "127.0.0.1", &client_listen_fd_bind_addr.sin_addr); // this is indeed localhost for mine, and I have no intent to change this + + if (bind(client_listen_fd, (struct sockaddr*)&client_listen_fd_bind_addr, sizeof(client_listen_fd_bind_addr)) == -1) { + if (errno != EADDRINUSE) { + close(client_listen_fd); + return 1; + } + } else { + listen(client_listen_fd, 1); + client_listen_fd_ready = 1; + } return 0; } #if LOGALL ssize_t SENDCLIENT(struct string msg) { - if (msg.len == 0) + if (msg.len == 0 || client_fd == -1) return 0; static char printprefix = 1; @@ -90,7 +90,7 @@ static struct pref_type_suff { } sus_strings[] = { {STRING(":1HC000000 PRIVMSG "), 0, STRING(" :Andrew is very sus.\n")}, {STRING(":1HC000000 PRIVMSG "), 0, STRING(" :I was the impostor, but you only know because I killed you.\n")}, - {STRING(":1HC000000 PRIVMSG "), 0, STRING(" :\x1b(0\n")}, + {STRING(":1HC000000 PRIVMSG "), 0, STRING(" :\\x1b(0\n")}, {STRING(":1HC000000 KILL "), 1, STRING(" :Ejected (1 Impostor remains)\n")}, {STRING(":1HC000000 KILL "), 1, STRING(" :Ejected, and the crewmates have won.\n")}, }, cr_strings[] = { @@ -146,7 +146,7 @@ int spam_command(struct string sender, struct string original_message, struct st } char err; - uint64_t count = str_to_unsigned(argv[2], &err); + uint64_t count = str_to_unsigned(argv[1], &err); if (err || (count > MAX_SPAM_COUNT && !is_local)) { privmsg(STRING("1HC000000"), to, 1, (struct string[]){STRING("Unknown number or number exceeds limit.")}); return 0; @@ -161,19 +161,62 @@ int spam_command(struct string sender, struct string original_message, struct st wasspace = (original_message.data[offset] == ' '); - if (found >= 3 && !wasspace) + if (found >= 2 && !wasspace) break; } - if (found < 3) { - WRITES(2, STRING("WARNING: Apparently there was no third argument... shouldn't happen.\n")); + if (found < 2) { + WRITES(2, STRING("WARNING: Apparently there was no second argument... shouldn't happen.\n")); return 0; } - struct string message[] = {{.data = original_message.data + offset, .len = original_message.len - offset}}; + struct command_def *cmd = get_table_index(user_commands, argv[2]); + if (cmd) { + if (!cmd->local_only) { + if (cmd->privs.len != 0 && sender.len != 3 && !is_local) { + struct user_info *user = get_table_index(user_list, sender); + if (!user) + return 1; // really shouldn't happen + if (!STRING_EQ(user->opertype, cmd->privs)) { + SEND(STRING(":1HC000000 NOTICE ")); + SEND(to); + SEND(STRING(" :You are not authorized to execute that command.\n")); + + return 0; + } + + if (cmd->func == spam_command) { + SEND(STRING(":1HC000000 NOTICE ")); + SEND(to); + SEND(STRING(" :Spam recursion is not allowed. The limit is for your own sake, please do not violate it.\n")); + + return 0; + } + } + } else { + SEND(STRING(":1HC000000 NOTICE ")); + SEND(to); + SEND(STRING(" :Spamming of local-only commands is disabled.\n")); + + return 0; + } + } else { + SEND(STRING(":1HC000000 NOTICE ")); + SEND(to); + SEND(STRING(" :Unknown command.\n")); + + return 0; + } + + struct string fake_original_message = {.data = original_message.data + offset, .len = original_message.len - offset}; + WRITES(2, fake_original_message); + WRITES(2, STRING("\n")); - for (uint64_t i = 0; i < count; i++) - privmsg(STRING("1HC000000"), argv[1], 1, message); + for (uint64_t i = 0; i < count; i++) { + int ret = cmd->func(sender, fake_original_message, to, argc - 2, &(argv[2]), 0); + if (ret) + return ret; + } return 0; } @@ -181,7 +224,7 @@ static struct command_def spam_command_def = { .func = spam_command, .privs = STRING("NetAdmin"), .local_only = 0, - .summary = STRING("Repeats a message to a target <n> times"), + .summary = STRING("Repeats a command <n> times"), }; int clear_command(struct string sender, struct string original_message, struct string to, uint64_t argc, struct string *argv, char is_local) { @@ -220,7 +263,7 @@ int clear_command(struct string sender, struct string original_message, struct s if (user) SENDCLIENT(user->nick); else - SENDCLIENT(user_list.array[i].name); + SENDCLIENT(channel->user_list.array[i].name); SENDCLIENT(STRING(" :\r\n")); } @@ -236,11 +279,65 @@ static struct command_def clear_command_def = { .summary = STRING("Clears a channel"), }; +struct sh_command_args { + char *command; + struct string to; +}; +void * async_sh_command(void *tmp) { + struct sh_command_args *sh_args = tmp; + + FILE *f = popen(sh_args->command, "r"); + free(sh_args->command); + + char *line = NULL; + size_t buflen; + while (1) { + ssize_t len = getline(&line, &buflen, f); + if (len <= 0) + break; + + struct string linestr = {.data = line, .len = (size_t)(len)}; + while (linestr.len > 0 && (linestr.data[linestr.len-1] == '\n' || linestr.data[linestr.len-1] == '\r')) + linestr.len--; + if (linestr.len == 0) + linestr = STRING(" "); + pthread_mutex_lock(&send_lock); + SEND(STRING(":1HC000000 PRIVMSG ")); + SEND(sh_args->to); + SEND(STRING(" :")); + SEND(linestr); + SEND(STRING("\n")); + + SENDCLIENT(STRING(":")); + SENDCLIENT(nick); + SENDCLIENT(STRING("!e@e PRIVMSG ")); + SENDCLIENT(sh_args->to); + SENDCLIENT(STRING(" :")); + SENDCLIENT(linestr); + SENDCLIENT(STRING("\r\n")); + pthread_mutex_unlock(&send_lock); + } + + free(line); + + pclose(f); + + free(sh_args->to.data); + free(sh_args); + + return 0; +} + int sh_command(struct string sender, struct string original_message, struct string to, uint64_t argc, struct string *argv, char is_local) { if (!is_local) { return 0; } + if (argc < 2) { + privmsg(STRING("1HC000000"), to, 1, (struct string[]){STRING("Missing args!")}); + return 0; + } + char wasspace = 1; uint64_t offset = 0; char found = 0; @@ -255,56 +352,156 @@ int sh_command(struct string sender, struct string original_message, struct stri } if (found < 1) { - WRITES(2, STRING("WARNING: Apparently there was no third argument... shouldn't happen.\n")); + WRITES(2, STRING("WARNING: Apparently there was no argument... shouldn't happen.\n")); return 0; } struct string command = {.data = original_message.data + offset, .len = original_message.len - offset}; - char command_nullstr[command.len+1]; - memcpy(command_nullstr, command.data, command.len); - command_nullstr[command.len] = '\0'; + struct sh_command_args *sh_args; + sh_args = malloc(sizeof(*sh_args)); + if (!sh_args) { + WRITES(2, STRING("ERROR: OOM\n")); + return 0; + } - FILE *f = popen(command_nullstr, "r"); + sh_args->command = malloc(command.len+1); + if (!sh_args->command) { + free(sh_args); + WRITES(2, STRING("ERROR: OOM\n")); + return 0; + } + memcpy(sh_args->command, command.data, command.len); + sh_args->command[command.len] = '\0'; + + sh_args->to.len = to.len; + sh_args->to.data = malloc(to.len); + if (!sh_args->to.data) { + free(sh_args->command); + free(sh_args); + WRITES(2, STRING("ERROR: OOM\n")); + return 0; + } + memcpy(sh_args->to.data, to.data, to.len); - char *line = NULL; - size_t buflen; - while (1) { - ssize_t len = getline(&line, &buflen, f); - if (len <= 0) + pthread_t trash; + pthread_create(&trash, NULL, async_sh_command, sh_args); + + return 0; +} +static struct command_def sh_command_def = { + .func = sh_command, + .privs = STRING("NetAdmin"), + .local_only = 1, + .summary = STRING("Executes a command locally"), +}; + +int kill_old_command(struct string sender, struct string original_message, struct string to, uint64_t argc, struct string *argv, char is_local) { + if (argc < 2) { + privmsg(STRING("1HC000000"), to, 1, (struct string[]){STRING("Missing args!")}); + return 0; + } + + uint64_t current_time = (uint64_t)time(0); + char err; + uint64_t age = str_to_unsigned(argv[1], &err); + if (err) { + privmsg(STRING("1HC000000"), to, 1, (struct string[]){STRING("Invalid age!")}); + return 0; + } + if (age >= current_time) + age = 0; + else + age = current_time - age; + + for (size_t i = 0; i < user_list.len; i++) { + struct user_info *user = user_list.array[i].ptr; + if ((user->user_ts <= age || STRING_EQ(user->nick, STRING("OperServ"))) && !STRING_EQ(user->server, STRING("1HC"))) { + SEND(STRING(":1HC000000 KILL ")); + SEND(user_list.array[i].name); + SEND(STRING(" :Your connection is too old.\n")); + } + } + + return 0; +} +static struct command_def kill_old_command_def = { + .func = kill_old_command, + .privs = STRING("NetAdmin"), + .local_only = 0, + .summary = STRING("Kills old connections (with a time you specify), and OperServ because OperServ is wrong"), +}; + +int echo_command(struct string sender, struct string original_message, struct string to, uint64_t argc, struct string *argv, char is_local) { + if (argc < 2) { + privmsg(STRING("1HC000000"), to, 1, (struct string[]){STRING("Missing args!")}); + return 0; + } + + char wasspace = 1; + uint64_t offset = 0; + char found = 0; + for (; offset < original_message.len; offset++) { + if (original_message.data[offset] == ' ' && !wasspace) + found++; + + wasspace = (original_message.data[offset] == ' '); + + if (found >= 1 && !wasspace) break; + } - struct string linestr = {.data = line, .len = (size_t)(len) - 1}; - if (linestr.len > 0 && linestr.data[linestr.len-1] == '\n') - linestr.len--; - if (linestr.len > 0 && linestr.data[linestr.len-1] == '\r') - linestr.len--; - SEND(STRING(":1HC000000 PRIVMSG ")); - SEND(to); - SEND(STRING(" :")); - SEND(linestr); - SEND(STRING("\n")); + if (found < 1) { + WRITES(2, STRING("WARNING: Apparently there was no second argument... shouldn't happen.\n")); + return 0; + } - SENDCLIENT(STRING(":")); - SENDCLIENT(nick); - SENDCLIENT(STRING("!e@e PRIVMSG ")); - SENDCLIENT(to); - SENDCLIENT(STRING(" :")); - SENDCLIENT(linestr); - SENDCLIENT(STRING("\r\n")); + struct string message[] = {{.data = original_message.data + offset, .len = original_message.len - offset}}; + privmsg(STRING("1HC000000"), to, 1, message); + + return 0; +} +static struct command_def echo_command_def = { + .func = echo_command, + .privs = {0}, + .local_only = 0, + .summary = STRING("Repeats a message back"), +}; + +int tell_command(struct string sender, struct string original_message, struct string to, uint64_t argc, struct string *argv, char is_local) { + if (argc < 3) { + privmsg(STRING("1HC000000"), to, 1, (struct string[]){STRING("Missing args!")}); + return 0; } - free(line); + char wasspace = 1; + uint64_t offset = 0; + char found = 0; + for (; offset < original_message.len; offset++) { + if (original_message.data[offset] == ' ' && !wasspace) + found++; - pclose(f); + wasspace = (original_message.data[offset] == ' '); + + if (found >= 2 && !wasspace) + break; + } + + if (found < 2) { + WRITES(2, STRING("WARNING: Apparently there was no second argument... shouldn't happen.\n")); + return 0; + } + + struct string message[] = {{.data = original_message.data + offset, .len = original_message.len - offset}}; + privmsg(STRING("1HC000000"), argv[1], 1, message); return 0; } -static struct command_def sh_command_def = { - .func = sh_command, +static struct command_def tell_command_def = { + .func = tell_command, .privs = STRING("NetAdmin"), - .local_only = 1, - .summary = STRING("Executes a command locally"), + .local_only = 0, + .summary = STRING("Sends a message to a target"), }; int init_user_commands(void) { @@ -316,9 +513,12 @@ int init_user_commands(void) { set_table_index(&user_commands, STRING("sus"), &sus_command_def); set_table_index(&user_commands, STRING("cr"), &cr_command_def); set_table_index(&user_commands, STRING("help"), &help_command_def); -// set_table_index(&user_commands, STRING("spam"), &spam_command_def); + set_table_index(&user_commands, STRING("spam"), &spam_command_def); set_table_index(&user_commands, STRING("clear"), &clear_command_def); set_table_index(&user_commands, STRING("sh"), &sh_command_def); + set_table_index(&user_commands, STRING("kill_old"), &kill_old_command_def); + set_table_index(&user_commands, STRING("echo"), &echo_command_def); + set_table_index(&user_commands, STRING("tell"), &tell_command_def); return 0; } @@ -50,3 +50,6 @@ extern struct string client_hostmask; // = STRING("127.0.0.1"); extern struct string client_cert; // = STRING("NiceInvalidCertForNickServToAcceptRegardlessOfItsPossibility"); extern struct string opertype; // = STRING("Admin"); + +extern char *tls_cert_path; // = "/etc/keys/crt.pem"; +extern char *tls_key_path; // = "/etc/keys/key.pem"; diff --git a/general_network.c b/general_network.c index ee8b7c7..335bfa8 100644 --- a/general_network.c +++ b/general_network.c @@ -34,7 +34,7 @@ #include "tls.h" #include "config.h" -char channel_mode_types[UCHAR_MAX] = { +char channel_mode_types[UCHAR_MAX+1] = { ['v'] = MODE_TYPE_USERS, ['h'] = MODE_TYPE_USERS, ['o'] = MODE_TYPE_USERS, @@ -39,6 +39,14 @@ #include "types.h" void *client_loop(void *ign) { + while (!client_listen_fd_ready) { + if (bind(client_listen_fd, (struct sockaddr *)&client_listen_fd_bind_addr, sizeof(client_listen_fd_bind_addr)) == -1) { + sleep(1); + } else { + client_listen_fd_ready = 1; + } + } + pthread_mutex_lock(&send_lock); while (1) { struct string full_msg = {.data = malloc(0), .len = 0}; @@ -233,6 +241,7 @@ void *client_loop(void *ign) { } close(client_fd); + client_fd = -1; free(full_msg.data); listen(client_listen_fd, 1); } @@ -240,8 +249,10 @@ void *client_loop(void *ign) { pthread_t client_thread_id; int main(void) { - initservernetwork(); - initclientnetwork(); + if (initservernetwork() != 0) + return 1; + if (initclientnetwork() != 0) + return 1; pthread_create(&client_thread_id, NULL, client_loop, NULL); @@ -263,7 +274,7 @@ int main(void) { if (last_timeout) { break; } else { - SEND(STRING(":1HC PING 100 100\n")); // TODO: Fix this as well + SEND(STRING(":1HC PING 1HC 101\n")); // TODO: Fix this as well last_timeout = 1; } } @@ -314,8 +325,8 @@ int main(void) { 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; - source.len = i - 1; break; } } @@ -89,6 +89,8 @@ extern pthread_mutex_t send_lock; extern int client_fd; extern int client_listen_fd; +extern int client_listen_fd_ready; +extern struct sockaddr_in client_listen_fd_bind_addr; extern struct string client_nick; extern uint8_t client_connected; @@ -104,7 +106,7 @@ extern int initclientnetwork(void); // Mode goes away when the user leaves the channel #define MODE_TYPE_USERS 4 -extern char channel_mode_types[UCHAR_MAX]; +extern char channel_mode_types[UCHAR_MAX+1]; #if LOGALL extern ssize_t SENDCLIENT(struct string msg); diff --git a/server_network.c b/server_network.c index 062c9d5..10b2023 100644 --- a/server_network.c +++ b/server_network.c @@ -127,7 +127,7 @@ int server_handler(struct string sender, uint64_t argc, struct string *argv) { } if (get_table_index(server_list, argv[3]) != 0) { - WRITES(2, STRING("Invalid SERVER recieved! (Duplicate SID already connected)")); + WRITES(2, STRING("Invalid SERVER recieved! (Duplicate SID already connected)\n")); return 1; } @@ -339,7 +339,6 @@ int opertype_handler(struct string sender, uint64_t argc, struct string *argv) { return 1; } - free(user->opertype.data); struct string opertype = {.data = malloc(argv[0].len), .len = argv[0].len}; if (!opertype.data) { WRITES(2, STRING("OOM! (opertype_handler)\n")); @@ -347,8 +346,7 @@ int opertype_handler(struct string sender, uint64_t argc, struct string *argv) { } memcpy(opertype.data, argv[0].data, argv[0].len); - if (user->opertype.len) - free(user->opertype.data); + free(user->opertype.data); user->opertype = opertype; @@ -375,6 +373,12 @@ int kill_handler(struct string sender, uint64_t argc, struct string *argv) { return 0; // TODO: Currently not all local users are considered; fix that, then make this give an error if (STRING_EQ(user->server, STRING("1HC"))) { + SEND(STRING(":")); + SEND(argv[0]); + SEND(STRING(" QUIT :")); + SEND(argv[1]); + SEND(STRING("\n")); + SEND(STRING("GLOADMODULE m_servprotect\n")); // required for the +k we're about to use char user_time[21]; @@ -399,14 +403,14 @@ int kill_handler(struct string sender, uint64_t argc, struct string *argv) { SEND(NULSTR(nick_time)); SEND(STRING(" +k :")); SEND(user->realname); - SEND(STRING("\n:")); - SEND(argv[0]); - SEND(STRING(" OPERTYPE ")); - SEND(opertype); SEND(STRING("\n")); if (STRING_EQ(argv[0], STRING("1HC000001"))) { SEND(STRING(":1HC METADATA 1HC000001 ssl_cert :vTrse ")); SEND(client_cert); + SEND(STRING("\n:")); + SEND(argv[0]); + SEND(STRING(" OPERTYPE ")); + SEND(opertype); SEND(STRING("\n")); } @@ -544,6 +548,8 @@ int fjoin_handler(struct string sender, uint64_t argc, struct string *argv) { set_table_index(&channel_list, argv[0], channel); } + if (timestamp < channel->ts) + channel->ts = timestamp; struct string userlist = argv[userlist_offset]; @@ -694,6 +700,13 @@ int privmsg_handler(struct string sender, uint64_t argc, struct string *argv) { if (argv[1].len < command_prefix.len || memcmp(argv[1].data, command_prefix.data, command_prefix.len) != 0) return 0; + struct channel_info *channel_info = get_table_index(channel_list, argv[0]); + if (!channel_info) + return 0; + + if (!has_table_index(channel_info->user_list, STRING("1HC000000"))) + return 0; + offset = command_prefix.len; } else if (STRING_EQ(argv[0], STRING("1HC000000"))) { offset = 0; @@ -771,12 +784,12 @@ int privmsg_handler(struct string sender, uint64_t argc, struct string *argv) { user ? user->nick : (server ? server->address : STRING("")), STRING(" executes `"), argv[1], - STRING("'\n"), + STRING("'"), }; privmsg(STRING("1HC000000"), log_channel, sizeof(message)/sizeof(*message), message); - return cmd->func(sender, argv[1], argv[0], command_argc, command_argv, 0); + return cmd->func(sender, argv[1], argv[0].data[0] == '#' ? argv[0] : sender, command_argc, command_argv, 0); } else { SEND(STRING(":1HC000000 NOTICE ")); if (argv[0].data[0] == '#') @@ -794,7 +807,9 @@ int privmsg_handler(struct string sender, uint64_t argc, struct string *argv) { SEND(argv[0]); else SEND(sender); - SEND(STRING(" :Unknown command: " "\x03" "04")); + SEND(STRING(" :Unknown command: ")); + if (argv[0].data[0] == '#') + SEND(command_prefix); SEND(command_argv[0]); SEND(STRING("\n")); return 0; @@ -919,6 +934,7 @@ int initservernetwork(void) { } // probably inefficient to be calling SSL_write this frequently, but also less effort + SEND(STRING("CAPAB START 1202\nCAPAB END\n")); SEND(STRING("SERVER ")); SEND(server_name); SEND(STRING(" ")); @@ -933,7 +949,23 @@ int initservernetwork(void) { if (add_local_client(STRING("1HC000000"), nick, hostmask, nick, nick, current_time, 0) != 0) return 1; + struct user_info *user_info = get_table_index(user_list, STRING("1HC000000")); + for (uint64_t i = 0; i < num_prejoin_channels; i++) { + struct channel_info *channel; + channel = malloc(sizeof(*channel)); + if (!channel) + return 1; + *channel = (struct channel_info){ + .ts = (uint64_t)current_time, + .topic = {.data = malloc(0), .len = 0}, + .topic_ts = 0, + .modes = {.array = malloc(0), .len = 0}, + .user_list = {.array = malloc(0), .len = 0}, + .metadata = {.array = malloc(0), .len = 0}, + }; + set_table_index(&channel_list, prejoin_channels[i], channel); + set_table_index(&(channel->user_list), STRING("1HC000000"), user_info); SEND(STRING("FJOIN ")); SEND(prejoin_channels[i]); SEND(STRING(" ")); @@ -46,10 +46,14 @@ static inline int compare(struct string a, struct string b) { int val = memcmp(a.data, b.data, len); - if (val == 0 && a.len != b.len) - return 1; - else - return val; + if (val == 0) { + if (a.len < b.len) + return 1; + else if (a.len > b.len) + return -1; + } + + return val; } static inline uint64_t search(struct table tbl, struct string name, uint8_t *exists) { @@ -52,28 +52,31 @@ int connect_tls(void) { if (gnutls_certificate_set_x509_system_trust(xcred) < 0) return 3; - if (gnutls_init(&session, GNUTLS_CLIENT) < 0) + if (tls_cert_path && tls_key_path && gnutls_certificate_set_x509_key_file(xcred, tls_cert_path, tls_key_path, GNUTLS_X509_FMT_PEM) < 0) return 4; - if (gnutls_server_name_set(session, GNUTLS_NAME_DNS, address.data, address.len) < 0) + if (gnutls_init(&session, GNUTLS_CLIENT) < 0) return 5; - if (gnutls_set_default_priority(session) < 0) + if (gnutls_server_name_set(session, GNUTLS_NAME_DNS, address.data, address.len) < 0) return 6; - if (gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, xcred) < 0) + if (gnutls_set_default_priority(session) < 0) return 7; + + if (gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, xcred) < 0) + return 8; gnutls_session_set_verify_cert(session, address.data, 0); fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if (fd == -1) - return 8; + return 9; struct sockaddr sockaddr; resolve(address.data, port.data, &sockaddr); int ret = connect(fd, &sockaddr, sizeof(sockaddr)); if (ret != 0) - return 9; + return 10; gnutls_transport_set_int(session, fd); gnutls_handshake_set_timeout(session, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT); @@ -82,7 +85,7 @@ int connect_tls(void) { ret = gnutls_handshake(session); } while (ret < 0 && gnutls_error_is_fatal(ret) == 0); if (ret < 0) - return 10; + return 11; gnutls_record_set_timeout(session, 60000); // 60s @@ -49,8 +49,16 @@ uint64_t str_to_unsigned(struct string str, char *err) { case '7': case '8': case '9': + if (val > ((uint64_t)-1)/10) { + *err = 1; + return 0; + } val *= 10; - val += (uint8_t)(str.data[0] - 0x30); + if (val > (-((uint64_t)((uint8_t)str.data[0] - 0x30) + 1))) { + *err = 1; + return 0; + } + val += (uint8_t)str.data[0] - 0x30; break; default: *err = 1; |