1 /* proto-common.c - common IRC protocol parsing/sending support
2 * Copyright 2000-2004 srvx Development Team
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version. Important limitations are
8 * listed in the COPYING file that accompanies this software.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, email srvx-maintainers@srvx.net.
25 #ifdef HAVE_SYS_SOCKET_H
26 #include <sys/socket.h>
28 #ifdef HAVE_NETINET_IN_H
29 #include <netinet/in.h>
31 #ifdef HAVE_ARPA_INET_H
32 #include <arpa/inet.h>
35 unsigned int lines_processed;
37 struct io_fd *socket_io_fd;
39 const char *hidden_host_suffix;
41 static char replay_line[MAXLEN+80];
43 static int ping_timeout;
44 static int replay_connected;
45 static unsigned int nicklen = NICKLEN; /* how long do we think servers allow nicks to be? */
46 static struct userList dead_users;
48 extern struct cManagerNode cManager;
49 extern unsigned long burst_length;
50 extern struct cManagerNode cManager;
51 extern struct policer_params *oper_policer_params, *luser_policer_params;
52 extern server_link_func_t *slf_list;
53 extern unsigned int slf_size, slf_used;
54 extern new_user_func_t *nuf_list;
55 extern unsigned int nuf_size, nuf_used;
56 extern del_user_func_t *duf_list;
57 extern unsigned int duf_size, duf_used;
58 extern time_t boot_time;
60 void received_ping(void);
62 static int replay_read(void);
63 static dict_t irc_func_dict;
65 typedef void (*foreach_chanfunc) (struct chanNode *chan, void *data);
66 typedef void (*foreach_nonchan) (char *name, void *data);
67 typedef void (*foreach_userfunc) (struct userNode *user, void *data);
68 typedef void (*foreach_nonuser) (char *name, void *data);
69 static void parse_foreach(char *target_list, foreach_chanfunc cf, foreach_nonchan nc, foreach_userfunc uf, foreach_nonuser nu, void *data);
72 uplink_readable(struct io_fd *fd) {
73 static char buffer[MAXLEN];
77 pos = ioset_line_read(fd, buffer, sizeof(buffer));
82 if ((eol = strpbrk(buffer, "\r\n"))) *eol = 0;
83 log_replay(MAIN_LOG, false, buffer);
84 if (cManager.uplink->state != DISCONNECTED)
85 parse_line(buffer, 0);
90 socket_destroyed(struct io_fd *fd)
93 log_module(MAIN_LOG, LOG_ERROR, "Connection to server lost.");
95 cManager.uplink->state = DISCONNECTED;
96 if (self->uplink) DelServer(self->uplink, 0, NULL);
99 void replay_event_loop(void)
101 while (!quit_services) {
102 if (!replay_connected) {
103 /* this time fudging is to get some of the logging right */
104 self->link = self->boot = now;
105 cManager.uplink->state = AUTHENTICATING;
106 irc_introduce(cManager.uplink->password);
107 replay_connected = 1;
108 } else if (!replay_read()) {
109 log_module(MAIN_LOG, LOG_ERROR, "Connection to server lost.");
117 create_socket_client(struct uplinkNode *target)
119 int port = target->port;
120 const char *addr = target->host;
123 return feof(replay_file) ? 0 : 1;
126 /* Leave the existing socket open, say we failed. */
127 log_module(MAIN_LOG, LOG_ERROR, "Refusing to create second connection to %s:%d.", addr, port);
131 log_module(MAIN_LOG, LOG_INFO, "Connecting to %s:%i...", addr, port);
133 socket_io_fd = ioset_connect((struct sockaddr*)cManager.uplink->bind_addr, sizeof(struct sockaddr), addr, port, 1, 0, NULL);
135 log_module(MAIN_LOG, LOG_ERROR, "Connection to uplink failed: %s (%d)", strerror(errno), errno);
136 target->state = DISCONNECTED;
140 socket_io_fd->readable_cb = uplink_readable;
141 socket_io_fd->destroy_cb = socket_destroyed;
142 socket_io_fd->line_reads = 1;
143 socket_io_fd->wants_reads = 1;
144 log_module(MAIN_LOG, LOG_INFO, "Connection to server established.");
145 cManager.uplink = target;
146 target->state = AUTHENTICATING;
152 replay_read_line(void)
157 if (replay_line[0]) return;
159 if (!fgets(replay_line, sizeof(replay_line), replay_file)) {
160 if (feof(replay_file)) {
162 memset(replay_line, 0, sizeof(replay_line));
166 if ((replay_line[0] != '[')
167 || (replay_line[3] != ':')
168 || (replay_line[6] != ':')
169 || (replay_line[9] != ' ')
170 || (replay_line[12] != '/')
171 || (replay_line[15] != '/')
172 || (replay_line[20] != ']')
173 || (replay_line[21] != ' ')) {
174 log_module(MAIN_LOG, LOG_ERROR, "Unrecognized timestamp in replay file: %s", replay_line);
177 timestamp.tm_hour = strtoul(replay_line+1, NULL, 10);
178 timestamp.tm_min = strtoul(replay_line+4, NULL, 10);
179 timestamp.tm_sec = strtoul(replay_line+7, NULL, 10);
180 timestamp.tm_mon = strtoul(replay_line+10, NULL, 10) - 1;
181 timestamp.tm_mday = strtoul(replay_line+13, NULL, 10);
182 timestamp.tm_year = strtoul(replay_line+16, NULL, 10) - 1900;
183 timestamp.tm_isdst = 0;
184 new_time = mktime(×tamp);
185 if (new_time == -1) {
186 log_module(MAIN_LOG, LOG_ERROR, "Unable to parse time struct tm_sec=%d tm_min=%d tm_hour=%d tm_mday=%d tm_mon=%d tm_year=%d", timestamp.tm_sec, timestamp.tm_min, timestamp.tm_hour, timestamp.tm_mday, timestamp.tm_mon, timestamp.tm_year);
191 if (strncmp(replay_line+22, "(info) ", 7))
200 char read_line[MAXLEN];
203 /* if it's a sent line, break out to handle it */
204 if (!strncmp(replay_line+29, " ", 3))
206 if (!strncmp(replay_line+29, "W: ", 3)) {
207 log_module(MAIN_LOG, LOG_ERROR, "Expected response from services: %s", replay_line+32);
213 log_replay(MAIN_LOG, false, replay_line+32);
214 safestrncpy(read_line, replay_line+32, sizeof(read_line));
215 len = strlen(read_line);
216 if (read_line[len-1] == '\n')
217 read_line[--len] = 0;
219 parse_line(read_line, 0);
225 replay_write(char *text)
228 if (strncmp(replay_line+29, "W: ", 3)) {
229 log_module(MAIN_LOG, LOG_ERROR, "Unexpected output during replay: %s", text);
232 if (strcmp(replay_line+32, text)) {
233 log_module(MAIN_LOG, LOG_ERROR, "Incorrect output during replay:\nReceived: %sExpected: %s", text, replay_line+32);
235 log_replay(MAIN_LOG, true, text);
241 void putsock(const char *text, ...) PRINTF_LIKE(1, 2);
244 putsock(const char *text, ...)
250 if (!cManager.uplink || cManager.uplink->state == DISCONNECTED) return;
252 va_start(arg_list, text);
253 pos = vsnprintf(buffer, MAXLEN - 2, text, arg_list);
255 if (pos < 0 || pos > (MAXLEN - 2)) pos = MAXLEN - 2;
258 log_replay(MAIN_LOG, true, buffer);
259 buffer[pos++] = '\n';
261 ioset_write(socket_io_fd, buffer, pos);
263 replay_write(buffer);
271 replay_connected = 0;
272 socket_destroyed(socket_io_fd);
274 ioset_close(socket_io_fd->fd, 1);
278 #define CMD_FUNC(NAME) int NAME(UNUSED_ARG(const char *origin), UNUSED_ARG(unsigned int argc), UNUSED_ARG(char **argv))
279 typedef CMD_FUNC(cmd_func_t);
281 static void timed_ping_timeout(void *data);
283 /* Ping state is kept in the timeq (only one of these two can be in
284 * the queue at any given time). */
286 timed_send_ping(UNUSED_ARG(void *data))
288 irc_ping(self->name);
289 timeq_add(now + ping_timeout, timed_ping_timeout, 0);
293 timed_ping_timeout(UNUSED_ARG(void *data))
295 /* Uplink "health" tracking could be accomplished by counting the
296 number of ping timeouts that happen for each uplink. After the
297 timeouts per time period exceeds some amount, the uplink could
298 be marked as unavalable.*/
299 irc_squit(self, "Ping timeout.", NULL);
302 static CMD_FUNC(cmd_pass)
304 const char *true_pass;
308 true_pass = cManager.uplink->their_password;
309 if (true_pass && strcmp(true_pass, argv[1])) {
310 /* It might be good to mark the uplink as unavailable when
311 this happens, though there should be a way of resetting
313 irc_squit(self, "Incorrect password received.", NULL);
317 cManager.uplink->state = BURSTING;
321 static CMD_FUNC(cmd_dummy)
323 /* we don't care about these messages */
327 static CMD_FUNC(cmd_error)
329 if (argv[1]) log_module(MAIN_LOG, LOG_ERROR, "Error: %s", argv[1]);
330 log_module(MAIN_LOG, LOG_ERROR, "Error received from uplink, squitting.");
332 if (cManager.uplink->state != CONNECTED) {
333 /* Error messages while connected should be fine. */
334 cManager.uplink->flags |= UPLINK_UNAVAILABLE;
335 log_module(MAIN_LOG, LOG_ERROR, "Disabling uplink.");
342 static CMD_FUNC(cmd_stats)
348 if (!(un = GetUserH(origin)))
350 switch (argv[1][0]) {
352 unsigned int uptime = now - boot_time;
353 irc_numeric(un, RPL_STATSUPTIME, ":Server Up %d days %d:%02d:%02d",
354 uptime/(24*60*60), (uptime/(60*60))%24, (uptime/60)%60, uptime%60);
355 irc_numeric(un, RPL_MAXCONNECTIONS, ":Highest connection count: %d (%d clients)",
356 self->max_clients+1, self->max_clients);
359 default: /* unrecognized/unhandled stats output */ break;
361 irc_numeric(un, 219, "%s :End of /STATS report", argv[1]);
365 static CMD_FUNC(cmd_whois)
367 struct userNode *from;
368 struct userNode *who;
372 if (!(from = GetUserH(origin))) {
373 log_module(MAIN_LOG, LOG_ERROR, "Could not find WHOIS origin user %s", origin);
376 if(!(who = GetUserH(argv[2]))) {
377 irc_numeric(from, ERR_NOSUCHNICK, "%s@%s :No such nick", argv[2], self->name);
380 if (IsHiddenHost(who) && !IsOper(from)) {
381 /* Just stay quiet. */
384 irc_numeric(from, RPL_WHOISUSER, "%s %s %s * :%s", who->nick, who->ident, who->hostname, who->info);
385 irc_numeric(from, RPL_WHOISSERVER, "%s %s :%s", who->nick, who->uplink->name, who->uplink->description);
387 irc_numeric(from, RPL_WHOISOPERATOR, "%s :is a megalomaniacal power hungry tyrant", who->nick);
389 irc_numeric(from, RPL_ENDOFWHOIS, "%s :End of /WHOIS list", who->nick);
393 static CMD_FUNC(cmd_version)
395 struct userNode *user;
396 if (!(user = GetUserH(origin))) {
397 log_module(MAIN_LOG, LOG_ERROR, "Could not find VERSION origin user %s", origin);
400 irc_numeric(user, 351, "%s.%s %s :%s", PACKAGE_TARNAME, PACKAGE_VERSION, self->name, CODENAME);
404 static CMD_FUNC(cmd_admin)
406 struct userNode *user;
407 struct string_list *slist;
409 if (!(user = GetUserH(origin))) {
410 log_module(MAIN_LOG, LOG_ERROR, "Could not find ADMIN origin user %s", origin);
413 if ((slist = conf_get_data("server/admin", RECDB_STRING_LIST)) && slist->used) {
416 irc_numeric(user, 256, ":Administrative info about %s", self->name);
417 for (i = 0; i < slist->used && i < 3; i++)
418 irc_numeric(user, 257 + i, ":%s", slist->list[i]);
420 irc_numeric(user, 423, ":No administrative info available");
426 recalc_bursts(struct server *eob_server)
429 eob_server->burst = eob_server->self_burst;
430 if (eob_server->uplink != self)
431 eob_server->burst = eob_server->burst || eob_server->uplink->burst;
432 for (nn=0; nn < eob_server->children.used; nn++)
433 recalc_bursts(eob_server->children.list[nn]);
436 static struct chanmsg_func {
438 struct userNode *service;
439 } chanmsg_funcs[256]; /* indexed by trigger character */
441 struct privmsg_desc {
442 struct userNode *user;
444 unsigned int is_notice : 1;
445 unsigned int is_qualified : 1;
449 privmsg_chan_helper(struct chanNode *cn, void *data)
451 struct privmsg_desc *pd = data;
453 struct chanmsg_func *cf = &chanmsg_funcs[(unsigned char)pd->text[0]];
455 /* Don't complain if it can't find the modeNode because the channel might
457 if ((mn = GetUserMode(cn, pd->user)))
458 mn->idle_since = now;
460 /* Never send a NOTICE to a channel to one of the services */
461 if (!pd->is_notice && cf->func && GetUserMode(cn, cf->service))
462 cf->func(pd->user, cn, pd->text+1, cf->service);
466 privmsg_invalid(char *name, void *data)
468 struct privmsg_desc *pd = data;
472 irc_numeric(pd->user, ERR_NOSUCHNICK, "%s@%s :No such nick", name, self->name);
476 part_helper(struct chanNode *cn, void *data)
478 DelChannelUser(data, cn, false, 0);
482 reg_chanmsg_func(unsigned char prefix, struct userNode *service, chanmsg_func_t handler)
484 if (chanmsg_funcs[prefix].func)
485 log_module(MAIN_LOG, LOG_WARNING, "Re-registering new chanmsg handler for character `%c'.", prefix);
486 chanmsg_funcs[prefix].func = handler;
487 chanmsg_funcs[prefix].service = service;
491 get_chanmsg_bot(unsigned char prefix)
493 return chanmsg_funcs[prefix].service;
496 static mode_change_func_t *mcf_list;
497 static unsigned int mcf_size = 0, mcf_used = 0;
500 reg_mode_change_func(mode_change_func_t handler)
502 if (mcf_used == mcf_size) {
505 mcf_list = realloc(mcf_list, mcf_size*sizeof(mode_change_func_t));
508 mcf_list = malloc(mcf_size*sizeof(mode_change_func_t));
511 mcf_list[mcf_used++] = handler;
514 struct mod_chanmode *
515 mod_chanmode_alloc(unsigned int argc)
517 struct mod_chanmode *res;
519 res = calloc(1, sizeof(*res) + (argc-1)*sizeof(res->args[0]));
521 res = calloc(1, sizeof(*res));
523 res->alloc_argc = argc;
529 struct mod_chanmode *
530 mod_chanmode_dup(struct mod_chanmode *orig, unsigned int extra)
532 struct mod_chanmode *res;
533 res = mod_chanmode_alloc(orig->argc + extra);
535 res->modes_set = orig->modes_set;
536 res->modes_clear = orig->modes_clear;
537 res->argc = orig->argc;
538 memcpy(res->args, orig->args, orig->argc*sizeof(orig->args[0]));
544 mod_chanmode_apply(struct userNode *who, struct chanNode *channel, struct mod_chanmode *change)
549 assert(change->argc <= change->alloc_argc);
550 channel->modes = (channel->modes & ~change->modes_clear) | change->modes_set;
551 if (change->modes_set & MODE_LIMIT)
552 channel->limit = change->new_limit;
553 if (change->modes_set & MODE_KEY)
554 strcpy(channel->key, change->new_key);
555 for (ii = 0; ii < change->argc; ++ii) {
556 switch (change->args[ii].mode) {
558 /* If any existing ban is a subset of the new ban,
559 * silently remove it. The new ban is not allowed
560 * to be more specific than an existing ban.
562 for (jj=0; jj<channel->banlist.used; ++jj) {
563 if (match_ircglobs(change->args[ii].hostmask, channel->banlist.list[jj]->ban)) {
564 banList_remove(&channel->banlist, channel->banlist.list[jj]);
565 free(channel->banlist.list[jj]);
569 bn = calloc(1, sizeof(*bn));
570 safestrncpy(bn->ban, change->args[ii].hostmask, sizeof(bn->ban));
571 safestrncpy(bn->who, who->nick, sizeof(bn->who));
573 banList_append(&channel->banlist, bn);
575 case MODE_REMOVE|MODE_BAN:
576 for (jj=0; jj<channel->banlist.used; ++jj) {
577 if (strcmp(channel->banlist.list[jj]->ban, change->args[ii].hostmask))
579 free(channel->banlist.list[jj]);
580 banList_remove(&channel->banlist, channel->banlist.list[jj]);
586 case MODE_VOICE|MODE_CHANOP:
587 case MODE_REMOVE|MODE_CHANOP:
588 case MODE_REMOVE|MODE_VOICE:
589 case MODE_REMOVE|MODE_VOICE|MODE_CHANOP:
590 if (change->args[ii].mode & MODE_REMOVE)
591 change->args[ii].member->modes &= ~change->args[ii].mode;
593 change->args[ii].member->modes |= change->args[ii].mode;
596 assert(0 && "Invalid mode argument");
603 mod_chanmode_free(struct mod_chanmode *change)
609 mod_chanmode(struct userNode *who, struct chanNode *channel, char **modes, unsigned int argc, unsigned int flags)
611 struct mod_chanmode *change;
614 if (!modes || !modes[0])
616 if (!(change = mod_chanmode_parse(channel, modes, argc, flags)))
618 if (flags & MC_ANNOUNCE)
619 mod_chanmode_announce(who, channel, change);
621 mod_chanmode_apply(who, channel, change);
622 if (flags & MC_NOTIFY)
623 for (ii = 0; ii < mcf_used; ++ii)
624 mcf_list[ii](channel, who, change);
625 mod_chanmode_free(change);
630 irc_make_chanmode(struct chanNode *chan, char *out) {
631 struct mod_chanmode change;
632 change.modes_set = chan->modes;
633 change.modes_clear = change.argc = 0;
634 change.new_limit = chan->limit;
635 safestrncpy(change.new_key, chan->key, sizeof(change.new_key));
636 return strlen(mod_chanmode_format(&change, out));
640 generate_hostmask(struct userNode *user, int options)
642 char *nickname, *ident, *hostname;
646 /* figure out string parts */
647 if (options & GENMASK_OMITNICK)
649 else if (options & GENMASK_USENICK)
650 nickname = user->nick;
653 if (options & GENMASK_STRICT_IDENT)
655 else if (options & GENMASK_ANY_IDENT)
658 ident = alloca(strlen(user->ident)+2);
660 strcpy(ident+1, user->ident + ((*user->ident == '~')?1:0));
662 hostname = user->hostname;
663 if (IsHiddenHost(user) && user->handle_info && hidden_host_suffix && !(options & GENMASK_NO_HIDING)) {
664 hostname = alloca(strlen(user->handle_info->handle) + strlen(hidden_host_suffix) + 2);
665 sprintf(hostname, "%s.%s", user->handle_info->handle, hidden_host_suffix);
666 } else if (options & GENMASK_STRICT_HOST) {
667 if (options & GENMASK_BYIP)
668 hostname = inet_ntoa(user->ip);
669 } else if ((options & GENMASK_BYIP) || !hostname[strspn(hostname, "0123456789.")]) {
670 /* Should generate an IP-based hostmask. By popular acclaim, a /16
671 * hostmask is used by default. */
672 unsigned masked_ip, mask, masklen;
674 mask = ~0 << masklen;
675 masked_ip = ntohl(user->ip.s_addr) & mask;
676 hostname = alloca(32);
677 if (options & GENMASK_SRVXMASK) {
678 sprintf(hostname, "%d.%d.%d.%d/%d", (masked_ip>>24)&0xFF, (masked_ip>>16)&0xFF, (masked_ip>>8)&0xFF, masked_ip&0xFF, masklen);
681 for (ii=0; ii<4; ii++) {
683 ofs += sprintf(hostname+ofs, "%d.", (masked_ip>>24)&0xFF);
687 ofs += sprintf(hostname+ofs, "*.");
690 /* Truncate the last . */
695 /* This heuristic could be made smarter. Is it worth the effort? */
696 for (ii=cnt=0; hostname[ii]; ii++)
697 if (hostname[ii] == '.')
700 /* only a two-level domain name; leave hostname */
701 } else if (cnt == 2) {
702 for (ii=0; user->hostname[ii] != '.'; ii++) ;
703 /* Add 3 to account for the *. and \0. */
704 hostname = alloca(strlen(user->hostname+ii)+3);
705 sprintf(hostname, "*.%s", user->hostname+ii+1);
707 for (cnt=3, ii--; cnt; ii--)
708 if (user->hostname[ii] == '.')
710 /* The loop above will overshoot the dot one character;
711 we skip forward two (the one character and the dot)
712 when printing, so we only add one for the \0. */
713 hostname = alloca(strlen(user->hostname+ii)+1);
714 sprintf(hostname, "*.%s", user->hostname+ii+2);
718 len = strlen(ident) + strlen(hostname) + 2;
720 len += strlen(nickname) + 1;
722 sprintf(mask, "%s!%s@%s", nickname, ident, hostname);
725 sprintf(mask, "%s@%s", ident, hostname);
731 IsChannelName(const char *name) {
736 for (ii=1; name[ii]; ++ii) {
737 if ((name[ii] > 0) && (name[ii] <= 32))
741 if (name[ii] == '\xa0')