aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--LICENSE23
-rw-r--r--Makefile4
-rw-r--r--README.md32
-rw-r--r--client_network.c42
-rw-r--r--commands.c288
-rw-r--r--config.h3
-rw-r--r--general_network.c2
-rw-r--r--main.c19
-rw-r--r--network.h4
-rw-r--r--server_network.c54
-rw-r--r--table.c12
-rw-r--r--tls.c17
-rw-r--r--utils.c10
14 files changed, 422 insertions, 89 deletions
diff --git a/.gitignore b/.gitignore
index b5805e2..8c4fba9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,4 @@ config.c
haxserv
core
.makeopts
+output
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..1465453
--- /dev/null
+++ b/LICENSE
@@ -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.
diff --git a/Makefile b/Makefile
index 77c7349..5ff90c8 100644
--- a/Makefile
+++ b/Makefile
@@ -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;
diff --git a/commands.c b/commands.c
index 0266b0b..e000ac5 100644
--- a/commands.c
+++ b/commands.c
@@ -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;
}
diff --git a/config.h b/config.h
index 9474353..931d726 100644
--- a/config.h
+++ b/config.h
@@ -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,
diff --git a/main.c b/main.c
index 50126a8..4f4108a 100644
--- a/main.c
+++ b/main.c
@@ -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;
}
}
diff --git a/network.h b/network.h
index 88383bd..d9df589 100644
--- a/network.h
+++ b/network.h
@@ -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(" "));
diff --git a/table.c b/table.c
index 3e8c7f1..f234717 100644
--- a/table.c
+++ b/table.c
@@ -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) {
diff --git a/tls.c b/tls.c
index 167f530..d526812 100644
--- a/tls.c
+++ b/tls.c
@@ -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
diff --git a/utils.c b/utils.c
index 10079d2..db0df78 100644
--- a/utils.c
+++ b/utils.c
@@ -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;