123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601 |
- // Client 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 "commands.h"
- #include "network.h"
- #include "config.h"
- #include "types.h"
- #include "table.h"
- #include "tls.h"
- struct table client_network_commands = {0};
- struct string client_nick = {0};
- uint8_t client_connected;
- int add_local_client(struct string uid, struct string nick_arg, struct string vhost_arg, struct string ident_arg, struct string realname_arg, time_t timestamp, char fake_cert) {
- if (has_table_index(user_list, uid))
- return 1;
- struct user_info *user = malloc(sizeof(*user));
- if (!user)
- goto add_local_client_fail_user;
- struct string server = {
- .data = malloc(3),
- .len = 3,
- };
- if (!server.data)
- goto add_local_client_fail_server;
- memcpy(server.data, "1HC", 3);
- struct string hostname = {
- .data = malloc(vhost_arg.len),
- .len = vhost_arg.len,
- };
- if (!hostname.data)
- goto add_local_client_fail_hostname;
- memcpy(hostname.data, vhost_arg.data, hostname.len);
- struct string vhost = {
- .data = malloc(vhost_arg.len),
- .len = vhost_arg.len,
- };
- if (!vhost.data)
- goto add_local_client_fail_vhost;
- memcpy(vhost.data, vhost_arg.data, vhost.len);
- struct string ident = {
- .data = malloc(ident_arg.len),
- .len = ident_arg.len,
- };
- if (!ident.data)
- goto add_local_client_fail_ident;
- memcpy(ident.data, ident_arg.data, ident.len);
- struct string ip = {
- .data = malloc(9),
- .len = 9,
- };
- if (!ip.data)
- goto add_local_client_fail_ip;
- memcpy(ip.data, "/dev/null", 9);
- struct string realname = {
- .data = malloc(realname_arg.len),
- .len = realname_arg.len,
- };
- if (!realname.data)
- goto add_local_client_fail_realname;
- memcpy(realname.data, realname_arg.data, realname.len);
- struct string nick = {
- .data = malloc(nick_arg.len),
- .len = nick_arg.len,
- };
- if (!nick.data)
- goto add_local_client_fail_nick;
- memcpy(nick.data, nick_arg.data, nick.len);
- *user = (struct user_info){
- .nick_ts = (uint64_t)timestamp,
- .user_ts = (uint64_t)timestamp,
- .server = server,
- .nick = nick,
- .hostname = hostname,
- .vhost = vhost,
- .ident = ident,
- .ip = ip,
- .realname = realname,
- .opertype = {.data = malloc(0), .len = 0},
- .metadata = {.array = malloc(0), .len = 0},
- };
- set_table_index(&user_list, uid, user);
- SEND(STRING("GLOADMODULE m_servprotect\n")); // required for the +k we're about to use
- char string_time[21];
- snprintf(string_time, 21, "%ld", timestamp);
- SEND(STRING("UID "));
- SEND(uid);
- SEND(STRING(" "));
- SEND(NULSTR(string_time));
- SEND(STRING(" "));
- SEND(nick);
- SEND(STRING(" "));
- SEND(vhost);
- SEND(STRING(" "));
- SEND(vhost);
- SEND(STRING(" "));
- SEND(ident);
- SEND(STRING(" "));
- SEND(ip);
- SEND(STRING(" "));
- SEND(NULSTR(string_time));
- SEND(STRING(" +k :"));
- SEND(realname);
- SEND(STRING("\n"));
- if (fake_cert) {
- SEND(STRING(":1HC METADATA "));
- SEND(uid);
- SEND(STRING(" ssl_cert :vTrse "));
- 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;
- add_local_client_fail_nick:
- free(realname.data);
- add_local_client_fail_realname:
- free(ip.data);
- add_local_client_fail_ip:
- free(ident.data);
- add_local_client_fail_ident:
- free(vhost.data);
- add_local_client_fail_vhost:
- free(hostname.data);
- add_local_client_fail_hostname:
- free(server.data);
- add_local_client_fail_server:
- free(user);
- add_local_client_fail_user:
- WRITES(2, STRING("OOM! (add_local_client)\n"));
- return 1;
- }
- int client_nick_handler(uint64_t argc, struct string *argv) {
- if (argc < 1)
- return 1;
- void *tmp = malloc(argv[0].len);
- if (!tmp)
- return 1;
- void *name_for_tables;
- if (client_connected) {
- name_for_tables = malloc(argv[0].len);
- if (!name_for_tables) {
- free(tmp);
- return 1;
- }
- memcpy(name_for_tables, argv[0].data, argv[0].len);
- }
- memcpy(tmp, argv[0].data, argv[0].len);
- if (client_connected) {
- SENDCLIENT(STRING(":"));
- SENDCLIENT(client_nick);
- SENDCLIENT(STRING("!e@e NICK :"));
- SENDCLIENT(argv[0]);
- SENDCLIENT(STRING("\r\n"));
- }
- free(client_nick.data);
- client_nick.data = tmp;
- client_nick.len = argv[0].len;
- if (client_connected) {
- struct user_info *client = get_table_index(user_list, STRING("1HC000001"));
- if (client) {
- free(client->nick.data);
- #pragma GCC diagnostic push
- #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
- client->nick.data = name_for_tables; // Will not be used uninitialized, ignore the compiler's complaint here
- #pragma GCC diagnostic pop // Compiler sees global variable and assumes it may be changed in one of the functions called between checks
- client->nick.len = argv[0].len;
- } else {
- free(name_for_tables);
- WRITES(2, STRING("Client connected but client data missing!\n"));
- return 1;
- }
- SEND(STRING(":1HC000001 NICK "));
- SEND(client_nick);
- SEND(STRING(" "));
- char current_time[22];
- snprintf(current_time, 22, "%ld", time(NULL));
- SEND(NULSTR(current_time));
- SEND(STRING("\n"));
- }
- return 0;
- }
- int client_user_handler(uint64_t argc, struct string *argv) {
- if (argc < 4)
- return 1;
- if (client_nick.len == 0)
- return 1;
- if (add_local_client(STRING("1HC000001"), client_nick, client_hostmask, argv[0], argv[3], time(NULL), 1) != 0)
- return 1;
- SENDCLIENT(STRING(":"));
- SENDCLIENT(server_name);
- SENDCLIENT(STRING(" 001 "));
- SENDCLIENT(client_nick);
- SENDCLIENT(STRING(" :Welcome to the Rexnet IRC Network\r\n:"));
- SENDCLIENT(server_name);
- SENDCLIENT(STRING(" 002 "));
- SENDCLIENT(client_nick);
- SENDCLIENT(STRING(" :Your host is "));
- SENDCLIENT(server_name);
- SENDCLIENT(STRING(", running a totally not sus IRCd\r\n:"));
- SENDCLIENT(server_name);
- SENDCLIENT(STRING(" 003 "));
- SENDCLIENT(client_nick);
- SENDCLIENT(STRING(" :This server was created 02:51:36 Apr 03 2023\r\n:"));
- SENDCLIENT(server_name);
- SENDCLIENT(STRING(" 004 "));
- SENDCLIENT(client_nick);
- SENDCLIENT(STRING(" "));
- SENDCLIENT(server_name);
- SENDCLIENT(STRING(" InspIRCd-3 BDGHILNORSTWcdghikorswxz ABCDEFGHIJKLMNOPQRSTXYZabcdefghijklmnopqrstuvwz :BEFHIJLXYZabdefghjkloqvw\r\n:"));
- SENDCLIENT(server_name);
- SENDCLIENT(STRING(" 005 "));
- SENDCLIENT(client_nick);
- SENDCLIENT(STRING(" ACCEPT=100 AWAYLEN=200 BOT=B CALLERID=g CASEMAPPING=ascii CHANLIMIT=#:20 CHANMODES=IXZbegw,k,BEFHJLdfjl,ACDGKMNOPQRSTcimnprstuz CHANNELLEN=60 CHANTYPES=# ELIST=CMNTU ESILENCE=CcdiNnPpTtx EXCEPTS=e :are supported by this server\r\n:"));
- SENDCLIENT(server_name);
- SENDCLIENT(STRING(" 005 "));
- SENDCLIENT(client_nick);
- SENDCLIENT(STRING(" EXTBAN=,ACNOQRSTUacjmnprswz HOSTLEN=64 INVEX=I KEYLEN=32 KICKLEN=300 LINELEN=512 MAXLIST=I:1000,X:1000,b:1000,e:1000,g:1000,w:1000 MAXTARGETS=20 MODES=20 MONITOR=30 NAMELEN=130 NAMESX NETWORK=LibreIRC :are supported by this server\r\n:"));
- SENDCLIENT(server_name);
- SENDCLIENT(STRING(" 005 "));
- SENDCLIENT(client_nick);
- SENDCLIENT(STRING(" NICKLEN=30 OVERRIDE=O PREFIX=(Yqaohv)!~&@%+ REMOVE SAFELIST SECURELIST=60 SILENCE=100 STATUSMSG=!~&@%+ TOPICLEN=330 UHNAMES USERIP USERLEN=10 USERMODES=,,s,BDGHILNORSTWcdghikorwxz :are supported by this server\r\n:"));
- SENDCLIENT(server_name);
- SENDCLIENT(STRING(" 005 "));
- SENDCLIENT(client_nick);
- SENDCLIENT(STRING(" WATCH=32 WHOX :are supported by this server\r\n"));
- client_connected = 1;
- return 0;
- }
- int client_join_handler(uint64_t argc, struct string *argv) {
- if (argc < 1)
- return 1;
- time_t ctime = time(NULL);
- struct string channels = argv[0];
- while (1) {
- char current_time_nulstr[22];
- uint64_t current_time;
- if (ctime < 0) {
- WRITES(2, STRING("Please check your clock.\n"));
- return 1;
- }
- current_time = (uint64_t)ctime;
- uint64_t offset = 0;
- while (offset < channels.len && channels.data[offset] != ',')
- offset++;
- uint64_t oldlen = channels.len;
- channels.len = offset;
- struct channel_info *channel_info = get_table_index(channel_list, channels);
- if (!channel_info) {
- channel_info = malloc(sizeof(*channel_info));
- if (!channel_info) {
- WRITES(2, STRING("OOM! (client_join)\n"));
- return 1;
- }
- *channel_info = (struct channel_info){
- .ts = 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, channels, channel_info);
- }
- if (channel_info->ts < current_time)
- current_time = channel_info->ts;
- snprintf(current_time_nulstr, 22, "%lu", current_time);
- SENDCLIENT(STRING(":"));
- SENDCLIENT(client_nick);
- SENDCLIENT(STRING("!e@e JOIN :"));
- SENDCLIENT(channels);
- for (uint64_t i = 0; i < channel_info->user_list.len; i++) {
- struct string user;
- struct user_info *info = channel_info->user_list.array[i].ptr;
- if (info)
- user = info->nick;
- else
- user = user_list.array[i].name;
- if (i%5 != 0) {
- SENDCLIENT(STRING(" "));
- SENDCLIENT(user);
- } else {
- SENDCLIENT(STRING("\r\n:"));
- SENDCLIENT(server_name);
- SENDCLIENT(STRING(" 353 "));
- SENDCLIENT(client_nick);
- SENDCLIENT(STRING(" = "));
- SENDCLIENT(channels);
- SENDCLIENT(STRING(" :"));
- SENDCLIENT(user);
- }
- }
- SENDCLIENT(STRING("\r\n:"));
- SENDCLIENT(server_name);
- SENDCLIENT(STRING(" 366 "));
- SENDCLIENT(client_nick);
- SENDCLIENT(STRING(" "));
- SENDCLIENT(channels);
- SENDCLIENT(STRING(" :End of /NAMES list.\r\n"));
- SEND(STRING(":1HC FJOIN "));
- SEND(channels);
- SEND(STRING(" "));
- SEND(NULSTR(current_time_nulstr));
- SEND(STRING(" + :,1HC000001\n"));
- set_table_index(&(channel_info->user_list), STRING("1HC000001"), get_table_index(user_list, STRING("1HC000001"))); // TODO: Actually add local users to user_list
- channels.len = oldlen;
- if (channels.len <= offset+1)
- break;
- channels.data += offset + 1;
- channels.len -= offset + 1;
- }
- return 0;
- }
- int client_privmsg_handler(uint64_t argc, struct string *argv) {
- if (argc < 2)
- return 1;
- SEND(STRING(":1HC000001 PRIVMSG "));
- SEND(argv[0]);
- SEND(STRING(" :"));
- SEND(argv[1]);
- SEND(STRING("\n"));
- 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) {
- struct string message[] = {
- STRING("Local user "),
- client_nick,
- STRING(" executes `"),
- argv[1],
- STRING("'"),
- };
- privmsg(STRING("1HC000000"), log_channel, sizeof(message)/sizeof(*message), message);
- return cmd->func(STRING("1HC000001"), argv[1], argv[0], command_argc, command_argv, 1);
- } else {
- if (argv[0].data[0] == '#') {
- SEND(STRING(":1HC000000 NOTICE "));
- SEND(argv[0]);
- SEND(STRING(" :Unknown command: "));
- SEND(command_prefix);
- SEND(command_argv[0]);
- SEND(STRING("\n"));
- }
- return 0;
- }
- return 0;
- }
- int client_raw_handler(uint64_t argc, struct string *argv) {
- if (argc < 1)
- return 1;
- SEND(argv[0]);
- SEND(STRING("\n"));
- return 0;
- }
- int client_ping_handler(uint64_t argc, struct string *argv) {
- if (argc < 1)
- return 1;
- SENDCLIENT(STRING(":"));
- SENDCLIENT(server_name);
- SENDCLIENT(STRING(" PONG :"));
- SENDCLIENT(argv[0]);
- SENDCLIENT(STRING("\r\n"));
- return 0;
- }
- int client_mode_handler(uint64_t argc, struct string *argv) {
- if (argc < 2)
- return 0; // Mode querying not supported yet
- SEND(STRING(":1HC000001 MODE "));
- for (uint64_t i = 0; i < argc - 1; i++) {
- SEND(argv[i]);
- SEND(STRING(" "));
- }
- SEND(STRING(":"));
- SEND(argv[argc - 1]);
- SEND(STRING("\n"));
- return 0;
- }
- int client_fd = -1;
- int client_listen_fd;
- int initclientnetwork(void) {
- client_network_commands.array = malloc(0);
- set_table_index(&client_network_commands, STRING("NICK"), &client_nick_handler);
- set_table_index(&client_network_commands, STRING("USER"), &client_user_handler);
- set_table_index(&client_network_commands, STRING("JOIN"), &client_join_handler);
- set_table_index(&client_network_commands, STRING("PRIVMSG"), &client_privmsg_handler);
- set_table_index(&client_network_commands, STRING("RAW"), &client_raw_handler);
- set_table_index(&client_network_commands, STRING("PING"), &client_ping_handler);
- set_table_index(&client_network_commands, STRING("MODE"), &client_mode_handler);
- client_nick.data = malloc(0);
- client_listen_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
- if (client_listen_fd < 0)
- return 1;
- 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));
- listen(client_listen_fd, 1);
- return 0;
- }
- #if LOGALL
- ssize_t SENDCLIENT(struct string msg) {
- if (msg.len == 0 || client_fd == -1)
- return 0;
- static char printprefix = 1;
- if (printprefix) {
- #if COLORIZE
- WRITES(1, STRING("\x1b[31m[Us->Client] \x1b[32m"));
- #else
- WRITES(1, STRING("[Us->Client] "));
- #endif
- printprefix = 0;
- }
- WRITES(1, msg);
- if (msg.data[msg.len - 1] == '\n') {
- printprefix = 1;
- #if COLORIZE
- WRITES(1, STRING("\x1b[0m\n"));
- #else
- WRITES(1, STRING("\n"));
- #endif
- }
- return WRITES(client_fd, msg);
- }
- #endif
|