free bugs [fixed]
[coupserv.git] / client_network.c
blob1ae8fea9529da6cb61b27a58b4eae966d22fc02b
1 // Client network command handlers for HaxServ
2 //
3 // Written by: Test_User <hax@andrewyu.org>
4 //
5 // This is free and unencumbered software released into the public
6 // domain.
7 //
8 // Anyone is free to copy, modify, publish, use, compile, sell, or
9 // distribute this software, either in source code form or as a compiled
10 // binary, for any purpose, commercial or non-commercial, and by any
11 // means.
13 // In jurisdictions that recognize copyright laws, the author or authors
14 // of this software dedicate any and all copyright interest in the
15 // software to the public domain. We make this dedication for the benefit
16 // of the public at large and to the detriment of our heirs and
17 // successors. We intend this dedication to be an overt act of
18 // relinquishment in perpetuity of all present and future rights to this
19 // software under copyright law.
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
24 // IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
25 // OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
26 // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27 // OTHER DEALINGS IN THE SOFTWARE.
29 #include <gnutls/gnutls.h>
30 #include <netdb.h>
31 #include <arpa/inet.h>
32 #include <sys/types.h>
33 #include <netinet/in.h>
34 #include <sys/socket.h>
35 #include <string.h>
36 #include <stdlib.h>
37 #include <stdio.h>
39 #include "commands.h"
40 #include "network.h"
41 #include "config.h"
42 #include "types.h"
43 #include "table.h"
44 #include "tls.h"
46 struct table client_network_commands = {0};
47 struct string client_nick = {0};
48 uint8_t client_connected;
50 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) {
51 if (has_table_index(user_list, uid))
52 return 1;
54 struct user_info *user = malloc(sizeof(*user));
55 if (!user)
56 goto add_local_client_fail_user;
58 struct string server = {
59 .data = malloc(3),
60 .len = 3,
62 if (!server.data)
63 goto add_local_client_fail_server;
65 memcpy(server.data, "1HC", 3);
67 struct string hostname = {
68 .data = malloc(vhost_arg.len),
69 .len = vhost_arg.len,
71 if (!hostname.data)
72 goto add_local_client_fail_hostname;
74 memcpy(hostname.data, vhost_arg.data, hostname.len);
76 struct string vhost = {
77 .data = malloc(vhost_arg.len),
78 .len = vhost_arg.len,
80 if (!vhost.data)
81 goto add_local_client_fail_vhost;
83 memcpy(vhost.data, vhost_arg.data, vhost.len);
85 struct string ident = {
86 .data = malloc(ident_arg.len),
87 .len = ident_arg.len,
89 if (!ident.data)
90 goto add_local_client_fail_ident;
92 memcpy(ident.data, ident_arg.data, ident.len);
94 struct string ip = {
95 .data = malloc(9),
96 .len = 9,
98 if (!ip.data)
99 goto add_local_client_fail_ip;
101 memcpy(ip.data, "/dev/null", 9);
103 struct string realname = {
104 .data = malloc(realname_arg.len),
105 .len = realname_arg.len,
107 if (!realname.data)
108 goto add_local_client_fail_realname;
110 memcpy(realname.data, realname_arg.data, realname.len);
112 struct string nick = {
113 .data = malloc(nick_arg.len),
114 .len = nick_arg.len,
116 if (!nick.data)
117 goto add_local_client_fail_nick;
119 memcpy(nick.data, nick_arg.data, nick.len);
121 *user = (struct user_info){
122 .nick_ts = (uint64_t)timestamp,
123 .user_ts = (uint64_t)timestamp,
124 .server = server,
125 .nick = nick,
126 .hostname = hostname,
127 .vhost = vhost,
128 .ident = ident,
129 .ip = ip,
130 .realname = realname,
131 .opertype = {.data = malloc(0), .len = 0},
132 .metadata = {.array = malloc(0), .len = 0},
135 set_table_index(&user_list, uid, user);
137 SEND(STRING("GLOADMODULE m_servprotect\n")); // required for the +k we're about to use
139 char string_time[21];
140 snprintf(string_time, 21, "%ld", timestamp);
141 SEND(STRING("UID "));
142 SEND(uid);
143 SEND(STRING(" "));
144 SEND(NULSTR(string_time));
145 SEND(STRING(" "));
146 SEND(nick);
147 SEND(STRING(" "));
148 SEND(vhost);
149 SEND(STRING(" "));
150 SEND(vhost);
151 SEND(STRING(" "));
152 SEND(ident);
153 SEND(STRING(" "));
154 SEND(ip);
155 SEND(STRING(" "));
156 SEND(NULSTR(string_time));
157 SEND(STRING(" +k :"));
158 SEND(realname);
159 SEND(STRING("\n"));
160 if (fake_cert) {
161 SEND(STRING(":1HC METADATA "));
162 SEND(uid);
163 SEND(STRING(" ssl_cert :vTrse "));
164 SEND(client_cert);
165 SEND(STRING("\n"));
167 if (!STRING_EQ(uid, STRING("1HC000000"))) { // Don't oper haxserv, because echo is unprivileged
168 SEND(STRING(":"));
169 SEND(uid);
170 SEND(STRING(" OPERTYPE "));
171 SEND(opertype);
172 SEND(STRING("\n"));
175 return 0;
177 add_local_client_fail_nick:
178 free(realname.data);
179 add_local_client_fail_realname:
180 free(ip.data);
181 add_local_client_fail_ip:
182 free(ident.data);
183 add_local_client_fail_ident:
184 free(vhost.data);
185 add_local_client_fail_vhost:
186 free(hostname.data);
187 add_local_client_fail_hostname:
188 free(server.data);
189 add_local_client_fail_server:
190 free(user);
191 add_local_client_fail_user:
193 WRITES(2, STRING("OOM! (add_local_client)\n"));
195 return 1;
198 int client_nick_handler(uint64_t argc, struct string *argv) {
199 if (argc < 1)
200 return 1;
202 void *tmp = malloc(argv[0].len);
203 if (!tmp)
204 return 1;
206 void *name_for_tables;
207 if (client_connected) {
208 name_for_tables = malloc(argv[0].len);
209 if (!name_for_tables) {
210 free(tmp);
211 return 1;
214 memcpy(name_for_tables, argv[0].data, argv[0].len);
217 memcpy(tmp, argv[0].data, argv[0].len);
219 if (client_connected) {
220 SENDCLIENT(STRING(":"));
221 SENDCLIENT(client_nick);
222 SENDCLIENT(STRING("!e@e NICK :"));
223 SENDCLIENT(argv[0]);
224 SENDCLIENT(STRING("\r\n"));
227 free(client_nick.data);
229 client_nick.data = tmp;
230 client_nick.len = argv[0].len;
232 if (client_connected) {
233 struct user_info *client = get_table_index(user_list, STRING("1HC000001"));
234 if (client) {
235 free(client->nick.data);
236 #pragma GCC diagnostic push
237 #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
238 client->nick.data = name_for_tables; // Will not be used uninitialized, ignore the compiler's complaint here
239 #pragma GCC diagnostic pop // Compiler sees global variable and assumes it may be changed in one of the functions called between checks
240 client->nick.len = argv[0].len;
241 } else {
242 free(name_for_tables);
243 WRITES(2, STRING("Client connected but client data missing!\n"));
244 return 1;
247 SEND(STRING(":1HC000001 NICK "));
248 SEND(client_nick);
249 SEND(STRING(" "));
250 char current_time[22];
251 snprintf(current_time, 22, "%ld", time(NULL));
252 SEND(NULSTR(current_time));
253 SEND(STRING("\n"));
256 return 0;
259 int client_user_handler(uint64_t argc, struct string *argv) {
260 if (argc < 4)
261 return 1;
263 if (client_nick.len == 0)
264 return 1;
266 if (add_local_client(STRING("1HC000001"), client_nick, client_hostmask, argv[0], argv[3], time(NULL), 1) != 0)
267 return 1;
269 SENDCLIENT(STRING(":"));
270 SENDCLIENT(server_name);
271 SENDCLIENT(STRING(" 001 "));
272 SENDCLIENT(client_nick);
273 SENDCLIENT(STRING(" :Welcome to the Rexnet IRC Network\r\n:"));
274 SENDCLIENT(server_name);
275 SENDCLIENT(STRING(" 002 "));
276 SENDCLIENT(client_nick);
277 SENDCLIENT(STRING(" :Your host is "));
278 SENDCLIENT(server_name);
279 SENDCLIENT(STRING(", running a totally not sus IRCd\r\n:"));
280 SENDCLIENT(server_name);
281 SENDCLIENT(STRING(" 003 "));
282 SENDCLIENT(client_nick);
283 SENDCLIENT(STRING(" :This server was created 02:51:36 Apr 03 2023\r\n:"));
284 SENDCLIENT(server_name);
285 SENDCLIENT(STRING(" 004 "));
286 SENDCLIENT(client_nick);
287 SENDCLIENT(STRING(" "));
288 SENDCLIENT(server_name);
289 SENDCLIENT(STRING(" InspIRCd-3 BDGHILNORSTWcdghikorswxz ABCDEFGHIJKLMNOPQRSTXYZabcdefghijklmnopqrstuvwz :BEFHIJLXYZabdefghjkloqvw\r\n:"));
290 SENDCLIENT(server_name);
291 SENDCLIENT(STRING(" 005 "));
292 SENDCLIENT(client_nick);
293 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:"));
294 SENDCLIENT(server_name);
295 SENDCLIENT(STRING(" 005 "));
296 SENDCLIENT(client_nick);
297 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:"));
298 SENDCLIENT(server_name);
299 SENDCLIENT(STRING(" 005 "));
300 SENDCLIENT(client_nick);
301 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:"));
302 SENDCLIENT(server_name);
303 SENDCLIENT(STRING(" 005 "));
304 SENDCLIENT(client_nick);
305 SENDCLIENT(STRING(" WATCH=32 WHOX :are supported by this server\r\n"));
307 client_connected = 1;
309 return 0;
312 int client_join_handler(uint64_t argc, struct string *argv) {
313 if (argc < 1)
314 return 1;
316 time_t ctime = time(NULL);
318 struct string channels = argv[0];
319 while (1) {
320 char current_time_nulstr[22];
321 uint64_t current_time;
322 if (ctime < 0) {
323 WRITES(2, STRING("Please check your clock.\n"));
324 return 1;
326 current_time = (uint64_t)ctime;
328 uint64_t offset = 0;
330 while (offset < channels.len && channels.data[offset] != ',')
331 offset++;
333 uint64_t oldlen = channels.len;
334 channels.len = offset;
336 struct channel_info *channel_info = get_table_index(channel_list, channels);
337 if (!channel_info) {
338 channel_info = malloc(sizeof(*channel_info));
339 if (!channel_info) {
340 WRITES(2, STRING("OOM! (client_join)\n"));
341 return 1;
343 *channel_info = (struct channel_info){
344 .ts = current_time,
345 .topic = {.data = malloc(0), .len = 0},
346 .topic_ts = 0,
347 .modes = {.array = malloc(0), .len = 0},
348 .user_list = {.array = malloc(0), .len = 0},
349 .metadata = {.array = malloc(0), .len = 0},
352 set_table_index(&channel_list, channels, channel_info);
354 if (channel_info->ts < current_time)
355 current_time = channel_info->ts;
357 snprintf(current_time_nulstr, 22, "%lu", current_time);
359 SENDCLIENT(STRING(":"));
360 SENDCLIENT(client_nick);
361 SENDCLIENT(STRING("!e@e JOIN :"));
362 SENDCLIENT(channels);
363 for (uint64_t i = 0; i < channel_info->user_list.len; i++) {
364 struct string user;
365 struct user_info *info = channel_info->user_list.array[i].ptr;
366 if (info)
367 user = info->nick;
368 else
369 user = user_list.array[i].name;
371 if (i%5 != 0) {
372 SENDCLIENT(STRING(" "));
373 SENDCLIENT(user);
374 } else {
375 SENDCLIENT(STRING("\r\n:"));
376 SENDCLIENT(server_name);
377 SENDCLIENT(STRING(" 353 "));
378 SENDCLIENT(client_nick);
379 SENDCLIENT(STRING(" = "));
380 SENDCLIENT(channels);
381 SENDCLIENT(STRING(" :"));
382 SENDCLIENT(user);
385 SENDCLIENT(STRING("\r\n:"));
386 SENDCLIENT(server_name);
387 SENDCLIENT(STRING(" 366 "));
388 SENDCLIENT(client_nick);
389 SENDCLIENT(STRING(" "));
390 SENDCLIENT(channels);
391 SENDCLIENT(STRING(" :End of /NAMES list.\r\n"));
393 SEND(STRING(":1HC FJOIN "));
394 SEND(channels);
395 SEND(STRING(" "));
396 SEND(NULSTR(current_time_nulstr));
397 SEND(STRING(" + :,1HC000001\n"));
399 set_table_index(&(channel_info->user_list), STRING("1HC000001"), get_table_index(user_list, STRING("1HC000001"))); // TODO: Actually add local users to user_list
401 channels.len = oldlen;
403 if (channels.len <= offset+1)
404 break;
406 channels.data += offset + 1;
407 channels.len -= offset + 1;
410 return 0;
413 int client_privmsg_handler(uint64_t argc, struct string *argv) {
414 if (argc < 2)
415 return 1;
417 SEND(STRING(":1HC000001 PRIVMSG "));
418 SEND(argv[0]);
419 SEND(STRING(" :"));
420 SEND(argv[1]);
421 SEND(STRING("\n"));
423 uint64_t offset;
424 if (argv[0].data[0] == '#') {
425 if (argv[1].len < command_prefix.len || memcmp(argv[1].data, command_prefix.data, command_prefix.len) != 0)
426 return 0;
428 offset = command_prefix.len;
429 } else {
430 offset = 0;
433 if (offset >= argv[1].len || argv[1].data[offset] == ' ')
434 return 0;
436 uint64_t command_argc = 0;
437 uint64_t old_offset = offset;
438 while (offset < argv[1].len) {
439 while (offset < argv[1].len && argv[1].data[offset] != ' ')
440 offset++;
442 command_argc++;
444 while (offset < argv[1].len && argv[1].data[offset] == ' ')
445 offset++;
447 offset = old_offset;
449 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
450 uint64_t i = 0;
451 while (offset < argv[1].len) {
452 command_argv[i].data = argv[1].data+offset;
453 uint64_t start = offset;
455 while (offset < argv[1].len && argv[1].data[offset] != ' ')
456 offset++;
458 command_argv[i].len = offset - start;
460 while (offset < argv[1].len && argv[1].data[offset] == ' ')
461 offset++;
463 i++;
466 argv[1].data += old_offset;
467 argv[1].len -= old_offset;
468 struct command_def *cmd = get_table_index(user_commands, command_argv[0]);
469 if (cmd) {
470 struct string message[] = {
471 STRING("Local user "),
472 client_nick,
473 STRING(" executes `"),
474 argv[1],
475 STRING("'"),
478 privmsg(STRING("1HC000000"), log_channel, sizeof(message)/sizeof(*message), message);
480 return cmd->func(STRING("1HC000001"), argv[1], argv[0], command_argc, command_argv, 1);
481 } else {
482 if (argv[0].data[0] == '#') {
483 SEND(STRING(":1HC000000 NOTICE "));
484 SEND(argv[0]);
485 SEND(STRING(" :Unknown command: "));
486 SEND(command_prefix);
487 SEND(command_argv[0]);
488 SEND(STRING("\n"));
490 return 0;
493 return 0;
496 int client_raw_handler(uint64_t argc, struct string *argv) {
497 if (argc < 1)
498 return 1;
500 SEND(argv[0]);
501 SEND(STRING("\n"));
503 return 0;
506 int client_ping_handler(uint64_t argc, struct string *argv) {
507 if (argc < 1)
508 return 1;
510 SENDCLIENT(STRING(":"));
511 SENDCLIENT(server_name);
512 SENDCLIENT(STRING(" PONG :"));
513 SENDCLIENT(argv[0]);
514 SENDCLIENT(STRING("\r\n"));
516 return 0;
519 int client_mode_handler(uint64_t argc, struct string *argv) {
520 if (argc < 2)
521 return 0; // Mode querying not supported yet
523 SEND(STRING(":1HC000001 MODE "));
524 for (uint64_t i = 0; i < argc - 1; i++) {
525 SEND(argv[i]);
526 SEND(STRING(" "));
528 SEND(STRING(":"));
529 SEND(argv[argc - 1]);
530 SEND(STRING("\n"));
532 return 0;
535 int client_fd = -1;
537 int client_listen_fd;
538 int initclientnetwork(void) {
539 client_network_commands.array = malloc(0);
541 set_table_index(&client_network_commands, STRING("NICK"), &client_nick_handler);
542 set_table_index(&client_network_commands, STRING("USER"), &client_user_handler);
543 set_table_index(&client_network_commands, STRING("JOIN"), &client_join_handler);
544 set_table_index(&client_network_commands, STRING("PRIVMSG"), &client_privmsg_handler);
545 set_table_index(&client_network_commands, STRING("RAW"), &client_raw_handler);
546 set_table_index(&client_network_commands, STRING("PING"), &client_ping_handler);
547 set_table_index(&client_network_commands, STRING("MODE"), &client_mode_handler);
549 client_nick.data = malloc(0);
551 client_listen_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
552 if (client_listen_fd < 0)
553 return 1;
555 int one = 1;
556 setsockopt(client_listen_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one));
558 struct sockaddr_in localhost = {
559 .sin_family = AF_INET,
560 .sin_port = htons(6667),
562 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
563 bind(client_listen_fd, (struct sockaddr*)&localhost, sizeof(localhost));
565 listen(client_listen_fd, 1);
567 return 0;
570 #if LOGALL
571 ssize_t SENDCLIENT(struct string msg) {
572 if (msg.len == 0 || client_fd == -1)
573 return 0;
575 static char printprefix = 1;
576 if (printprefix) {
577 #if COLORIZE
578 WRITES(1, STRING("\x1b[31m[Us->Client] \x1b[32m"));
579 #else
580 WRITES(1, STRING("[Us->Client] "));
581 #endif
583 printprefix = 0;
586 WRITES(1, msg);
588 if (msg.data[msg.len - 1] == '\n') {
589 printprefix = 1;
590 #if COLORIZE
591 WRITES(1, STRING("\x1b[0m\n"));
592 #else
593 WRITES(1, STRING("\n"));
594 #endif
598 return WRITES(client_fd, msg);
600 #endif