1 /* proto-common.c - common IRC protocol parsing/sending support
2 * Copyright 2000-2004 srvx Development Team
4 * This file is part of srvx.
6 * srvx is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with srvx; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
27 #ifdef HAVE_SYS_SOCKET_H
28 #include <sys/socket.h>
30 #ifdef HAVE_NETINET_IN_H
31 #include <netinet/in.h>
33 #ifdef HAVE_ARPA_INET_H
34 #include <arpa/inet.h>
37 unsigned int lines_processed;
39 struct io_fd *socket_io_fd;
41 const char *hidden_host_suffix;
43 static char replay_line[MAXLEN+80];
45 static int ping_timeout;
46 static int replay_connected;
47 static unsigned int nicklen = NICKLEN; /* how long do we think servers allow nicks to be? */
48 static struct userList dead_users;
50 extern struct cManagerNode cManager;
51 extern unsigned long burst_length;
52 extern struct cManagerNode cManager;
53 extern struct policer_params *oper_policer_params, *luser_policer_params;
54 extern server_link_func_t *slf_list;
55 extern unsigned int slf_size, slf_used;
56 extern new_user_func_t *nuf_list;
57 extern unsigned int nuf_size, nuf_used;
58 extern del_user_func_t *duf_list;
59 extern unsigned int duf_size, duf_used;
60 extern time_t boot_time;
62 void received_ping(void);
64 static int replay_read(void);
65 static dict_t irc_func_dict;
67 typedef void (*foreach_chanfunc) (struct chanNode *chan, void *data);
68 typedef void (*foreach_nonchan) (char *name, void *data);
69 typedef void (*foreach_userfunc) (struct userNode *user, void *data);
70 typedef void (*foreach_nonuser) (char *name, void *data);
71 static void parse_foreach(char *target_list, foreach_chanfunc cf, foreach_nonchan nc, foreach_userfunc uf, foreach_nonuser nu, void *data);
74 uplink_readable(struct io_fd *fd) {
75 static char buffer[MAXLEN];
79 pos = ioset_line_read(fd, buffer, sizeof(buffer));
84 if ((eol = strpbrk(buffer, "\r\n"))) *eol = 0;
85 log_replay(MAIN_LOG, false, buffer);
86 if (cManager.uplink->state != DISCONNECTED)
87 parse_line(buffer, 0);
92 socket_destroyed(struct io_fd *fd)
95 log_module(MAIN_LOG, LOG_ERROR, "Connection to server lost.");
97 cManager.uplink->state = DISCONNECTED;
98 if (self->uplink) DelServer(self->uplink, 0, NULL);
101 void replay_event_loop(void)
103 while (!quit_services) {
104 if (!replay_connected) {
105 /* this time fudging is to get some of the logging right */
106 self->link = self->boot = now;
107 cManager.uplink->state = AUTHENTICATING;
108 irc_introduce(cManager.uplink->password);
109 replay_connected = 1;
110 } else if (!replay_read()) {
111 log_module(MAIN_LOG, LOG_ERROR, "Connection to server lost.");
119 create_socket_client(struct uplinkNode *target)
121 int port = target->port;
122 const char *addr = target->host;
125 return feof(replay_file) ? 0 : 1;
128 /* Leave the existing socket open, say we failed. */
129 log_module(MAIN_LOG, LOG_ERROR, "Refusing to create second connection to %s:%d.", addr, port);
133 log_module(MAIN_LOG, LOG_INFO, "Connecting to %s:%i...", addr, port);
135 socket_io_fd = ioset_connect((struct sockaddr*)cManager.uplink->bind_addr, sizeof(struct sockaddr), addr, port, 1, 0, NULL);
137 log_module(MAIN_LOG, LOG_ERROR, "Connection to uplink failed: %s (%d)", strerror(errno), errno);
138 target->state = DISCONNECTED;
142 socket_io_fd->readable_cb = uplink_readable;
143 socket_io_fd->destroy_cb = socket_destroyed;
144 socket_io_fd->line_reads = 1;
145 socket_io_fd->wants_reads = 1;
146 log_module(MAIN_LOG, LOG_INFO, "Connection to server established.");
147 cManager.uplink = target;
148 target->state = AUTHENTICATING;
154 replay_read_line(void)
159 if (replay_line[0]) return;
161 if (!fgets(replay_line, sizeof(replay_line), replay_file)) {
162 if (feof(replay_file)) {
164 memset(replay_line, 0, sizeof(replay_line));
168 if ((replay_line[0] != '[')
169 || (replay_line[3] != ':')
170 || (replay_line[6] != ':')
171 || (replay_line[9] != ' ')
172 || (replay_line[12] != '/')
173 || (replay_line[15] != '/')
174 || (replay_line[20] != ']')
175 || (replay_line[21] != ' ')) {
176 log_module(MAIN_LOG, LOG_ERROR, "Unrecognized timestamp in replay file: %s", replay_line);
179 timestamp.tm_hour = strtoul(replay_line+1, NULL, 10);
180 timestamp.tm_min = strtoul(replay_line+4, NULL, 10);
181 timestamp.tm_sec = strtoul(replay_line+7, NULL, 10);
182 timestamp.tm_mon = strtoul(replay_line+10, NULL, 10) - 1;
183 timestamp.tm_mday = strtoul(replay_line+13, NULL, 10);
184 timestamp.tm_year = strtoul(replay_line+16, NULL, 10) - 1900;
185 timestamp.tm_isdst = 0;
186 new_time = mktime(×tamp);
187 if (new_time == -1) {
188 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);
193 if (strncmp(replay_line+22, "(info) ", 7))
202 char read_line[MAXLEN];
205 /* if it's a sent line, break out to handle it */
206 if (!strncmp(replay_line+29, " ", 3))
208 if (!strncmp(replay_line+29, "W: ", 3)) {
209 log_module(MAIN_LOG, LOG_ERROR, "Expected response from services: %s", replay_line+32);
215 log_replay(MAIN_LOG, false, replay_line+32);
216 safestrncpy(read_line, replay_line+32, sizeof(read_line));
217 len = strlen(read_line);
218 if (read_line[len-1] == '\n')
219 read_line[--len] = 0;
221 parse_line(read_line, 0);
227 replay_write(char *text)
230 if (strncmp(replay_line+29, "W: ", 3)) {
231 log_module(MAIN_LOG, LOG_ERROR, "Unexpected output during replay: %s", text);
234 if (strcmp(replay_line+32, text)) {
235 log_module(MAIN_LOG, LOG_ERROR, "Incorrect output during replay:\nReceived: %sExpected: %s", text, replay_line+32);
237 log_replay(MAIN_LOG, true, text);
243 void putsock(const char *text, ...) PRINTF_LIKE(1, 2);
246 putsock(const char *text, ...)
252 if (!cManager.uplink || cManager.uplink->state == DISCONNECTED) return;
254 va_start(arg_list, text);
255 pos = vsnprintf(buffer, MAXLEN - 2, text, arg_list);
257 if (pos < 0 || pos > (MAXLEN - 2)) pos = MAXLEN - 2;
260 log_replay(MAIN_LOG, true, buffer);
261 buffer[pos++] = '\n';
263 ioset_write(socket_io_fd, buffer, pos);
265 replay_write(buffer);
273 replay_connected = 0;
274 socket_destroyed(socket_io_fd);
276 ioset_close(socket_io_fd->fd, 1);
280 #define CMD_FUNC(NAME) int NAME(UNUSED_ARG(const char *origin), UNUSED_ARG(unsigned int argc), UNUSED_ARG(char **argv))
281 typedef CMD_FUNC(cmd_func_t);
283 static void timed_ping_timeout(void *data);
285 /* Ping state is kept in the timeq (only one of these two can be in
286 * the queue at any given time). */
288 timed_send_ping(UNUSED_ARG(void *data))
290 irc_ping(self->name);
291 timeq_add(now + ping_timeout, timed_ping_timeout, 0);
295 timed_ping_timeout(UNUSED_ARG(void *data))
297 /* Uplink "health" tracking could be accomplished by counting the
298 number of ping timeouts that happen for each uplink. After the
299 timeouts per time period exceeds some amount, the uplink could
300 be marked as unavalable.*/
301 irc_squit(self, "Ping timeout.", NULL);
304 static CMD_FUNC(cmd_pass)
306 const char *true_pass;
310 true_pass = cManager.uplink->their_password;
311 if (true_pass && strcmp(true_pass, argv[1])) {
312 /* It might be good to mark the uplink as unavailable when
313 this happens, though there should be a way of resetting
315 irc_squit(self, "Incorrect password received.", NULL);
319 cManager.uplink->state = BURSTING;
323 static CMD_FUNC(cmd_dummy)
325 /* we don't care about these messages */
329 static CMD_FUNC(cmd_error)
331 if (argv[1]) log_module(MAIN_LOG, LOG_ERROR, "Error: %s", argv[1]);
332 log_module(MAIN_LOG, LOG_ERROR, "Error received from uplink, squitting.");
334 if (cManager.uplink->state != CONNECTED) {
335 /* Error messages while connected should be fine. */
336 cManager.uplink->flags |= UPLINK_UNAVAILABLE;
337 log_module(MAIN_LOG, LOG_ERROR, "Disabling uplink.");
344 static CMD_FUNC(cmd_stats)
350 if (!(un = GetUserH(origin)))
352 switch (argv[1][0]) {
354 unsigned int uptime = now - boot_time;
355 irc_numeric(un, RPL_STATSUPTIME, ":Server Up %d days %d:%02d:%02d",
356 uptime/(24*60*60), (uptime/(60*60))%24, (uptime/60)%60, uptime%60);
357 irc_numeric(un, RPL_MAXCONNECTIONS, ":Highest connection count: %d (%d clients)",
358 self->max_clients+1, self->max_clients);
361 default: /* unrecognized/unhandled stats output */ break;
363 irc_numeric(un, 219, "%s :End of /STATS report", argv[1]);
367 static CMD_FUNC(cmd_whois)
369 struct userNode *from;
370 struct userNode *who;
374 if (!(from = GetUserH(origin))) {
375 log_module(MAIN_LOG, LOG_ERROR, "Could not find WHOIS origin user %s", origin);
378 if(!(who = GetUserH(argv[2]))) {
379 irc_numeric(from, ERR_NOSUCHNICK, "%s@%s :No such nick", argv[2], self->name);
382 if (IsHiddenHost(who) && !IsOper(from)) {
383 /* Just stay quiet. */
386 irc_numeric(from, RPL_WHOISUSER, "%s %s %s * :%s", who->nick, who->ident, who->hostname, who->info);
387 irc_numeric(from, RPL_WHOISSERVER, "%s %s :%s", who->nick, who->uplink->name, who->uplink->description);
389 irc_numeric(from, RPL_WHOISOPERATOR, "%s :is a megalomaniacal power hungry tyrant", who->nick);
391 irc_numeric(from, RPL_ENDOFWHOIS, "%s :End of /WHOIS list", who->nick);
395 static CMD_FUNC(cmd_version)
397 struct userNode *user;
398 if (!(user = GetUserH(origin))) {
399 log_module(MAIN_LOG, LOG_ERROR, "Could not find VERSION origin user %s", origin);
402 irc_numeric(user, 351, "%s.%s %s :%s", PACKAGE_TARNAME, PACKAGE_VERSION, self->name, CODENAME);
406 static CMD_FUNC(cmd_admin)
408 struct userNode *user;
409 struct string_list *slist;
411 if (!(user = GetUserH(origin))) {
412 log_module(MAIN_LOG, LOG_ERROR, "Could not find ADMIN origin user %s", origin);
415 if ((slist = conf_get_data("server/admin", RECDB_STRING_LIST)) && slist->used) {
418 irc_numeric(user, 256, ":Administrative info about %s", self->name);
419 for (i = 0; i < slist->used && i < 3; i++)
420 irc_numeric(user, 257 + i, ":%s", slist->list[i]);
422 irc_numeric(user, 423, ":No administrative info available");
428 recalc_bursts(struct server *eob_server)
431 eob_server->burst = eob_server->self_burst;
432 if (eob_server->uplink != self)
433 eob_server->burst = eob_server->burst || eob_server->uplink->burst;
434 for (nn=0; nn < eob_server->children.used; nn++)
435 recalc_bursts(eob_server->children.list[nn]);
438 static struct chanmsg_func {
440 struct userNode *service;
441 } chanmsg_funcs[256]; /* indexed by trigger character */
443 struct privmsg_desc {
444 struct userNode *user;
446 unsigned int is_notice : 1;
447 unsigned int is_qualified : 1;
451 privmsg_chan_helper(struct chanNode *cn, void *data)
453 struct privmsg_desc *pd = data;
455 struct chanmsg_func *cf = &chanmsg_funcs[(unsigned char)pd->text[0]];
457 /* Don't complain if it can't find the modeNode because the channel might
459 if ((mn = GetUserMode(cn, pd->user)))
460 mn->idle_since = now;
462 /* Never send a NOTICE to a channel to one of the services */
463 if (!pd->is_notice && cf->func && GetUserMode(cn, cf->service))
464 cf->func(pd->user, cn, pd->text+1, cf->service);
468 privmsg_invalid(char *name, void *data)
470 struct privmsg_desc *pd = data;
474 irc_numeric(pd->user, ERR_NOSUCHNICK, "%s@%s :No such nick", name, self->name);
478 part_helper(struct chanNode *cn, void *data)
480 DelChannelUser(data, cn, false, 0);
484 reg_chanmsg_func(unsigned char prefix, struct userNode *service, chanmsg_func_t handler)
486 if (chanmsg_funcs[prefix].func)
487 log_module(MAIN_LOG, LOG_WARNING, "Re-registering new chanmsg handler for character `%c'.", prefix);
488 chanmsg_funcs[prefix].func = handler;
489 chanmsg_funcs[prefix].service = service;
493 get_chanmsg_bot(unsigned char prefix)
495 return chanmsg_funcs[prefix].service;
498 static mode_change_func_t *mcf_list;
499 static unsigned int mcf_size = 0, mcf_used = 0;
502 reg_mode_change_func(mode_change_func_t handler)
504 if (mcf_used == mcf_size) {
507 mcf_list = realloc(mcf_list, mcf_size*sizeof(mode_change_func_t));
510 mcf_list = malloc(mcf_size*sizeof(mode_change_func_t));
513 mcf_list[mcf_used++] = handler;
516 struct mod_chanmode *
517 mod_chanmode_alloc(unsigned int argc)
519 struct mod_chanmode *res;
521 res = calloc(1, sizeof(*res) + (argc-1)*sizeof(res->args[0]));
523 res = calloc(1, sizeof(*res));
525 res->alloc_argc = argc;
531 struct mod_chanmode *
532 mod_chanmode_dup(struct mod_chanmode *orig, unsigned int extra)
534 struct mod_chanmode *res;
535 res = mod_chanmode_alloc(orig->argc + extra);
537 res->modes_set = orig->modes_set;
538 res->modes_clear = orig->modes_clear;
539 res->argc = orig->argc;
540 memcpy(res->args, orig->args, orig->argc*sizeof(orig->args[0]));
546 mod_chanmode_apply(struct userNode *who, struct chanNode *channel, struct mod_chanmode *change)
551 assert(change->argc <= change->alloc_argc);
552 channel->modes = (channel->modes & ~change->modes_clear) | change->modes_set;
553 if (change->modes_set & MODE_LIMIT)
554 channel->limit = change->new_limit;
555 if (change->modes_set & MODE_KEY)
556 strcpy(channel->key, change->new_key);
557 for (ii = 0; ii < change->argc; ++ii) {
558 switch (change->args[ii].mode) {
560 /* If any existing ban is a subset of the new ban,
561 * silently remove it. The new ban is not allowed
562 * to be more specific than an existing ban.
564 for (jj=0; jj<channel->banlist.used; ++jj) {
565 if (match_ircglobs(change->args[ii].hostmask, channel->banlist.list[jj]->ban)) {
566 banList_remove(&channel->banlist, channel->banlist.list[jj]);
567 free(channel->banlist.list[jj]);
571 bn = calloc(1, sizeof(*bn));
572 safestrncpy(bn->ban, change->args[ii].hostmask, sizeof(bn->ban));
573 safestrncpy(bn->who, who->nick, sizeof(bn->who));
575 banList_append(&channel->banlist, bn);
577 case MODE_REMOVE|MODE_BAN:
578 for (jj=0; jj<channel->banlist.used; ++jj) {
579 if (strcmp(channel->banlist.list[jj]->ban, change->args[ii].hostmask))
581 free(channel->banlist.list[jj]);
582 banList_remove(&channel->banlist, channel->banlist.list[jj]);
588 case MODE_VOICE|MODE_CHANOP:
589 case MODE_REMOVE|MODE_CHANOP:
590 case MODE_REMOVE|MODE_VOICE:
591 case MODE_REMOVE|MODE_VOICE|MODE_CHANOP:
592 if (change->args[ii].mode & MODE_REMOVE)
593 change->args[ii].member->modes &= ~change->args[ii].mode;
595 change->args[ii].member->modes |= change->args[ii].mode;
598 assert(0 && "Invalid mode argument");
605 mod_chanmode_free(struct mod_chanmode *change)
611 mod_chanmode(struct userNode *who, struct chanNode *channel, char **modes, unsigned int argc, unsigned int flags)
613 struct mod_chanmode *change;
616 if (!modes || !modes[0])
618 if (!(change = mod_chanmode_parse(channel, modes, argc, flags)))
620 if (flags & MC_ANNOUNCE)
621 mod_chanmode_announce(who, channel, change);
623 mod_chanmode_apply(who, channel, change);
624 if (flags & MC_NOTIFY)
625 for (ii = 0; ii < mcf_used; ++ii)
626 mcf_list[ii](channel, who, change);
627 mod_chanmode_free(change);
632 irc_make_chanmode(struct chanNode *chan, char *out) {
633 struct mod_chanmode change;
634 change.modes_set = chan->modes;
635 change.modes_clear = change.argc = 0;
636 change.new_limit = chan->limit;
637 safestrncpy(change.new_key, chan->key, sizeof(change.new_key));
638 return strlen(mod_chanmode_format(&change, out));
642 generate_hostmask(struct userNode *user, int options)
644 char *nickname, *ident, *hostname;
648 /* figure out string parts */
649 if (options & GENMASK_OMITNICK)
651 else if (options & GENMASK_USENICK)
652 nickname = user->nick;
655 if (options & GENMASK_STRICT_IDENT)
657 else if (options & GENMASK_ANY_IDENT)
660 ident = alloca(strlen(user->ident)+2);
662 strcpy(ident+1, user->ident + ((*user->ident == '~')?1:0));
664 hostname = user->hostname;
665 if (IsHiddenHost(user) && user->handle_info && hidden_host_suffix && !(options & GENMASK_NO_HIDING)) {
666 hostname = alloca(strlen(user->handle_info->handle) + strlen(hidden_host_suffix) + 2);
667 sprintf(hostname, "%s.%s", user->handle_info->handle, hidden_host_suffix);
668 } else if (options & GENMASK_STRICT_HOST) {
669 if (options & GENMASK_BYIP)
670 hostname = inet_ntoa(user->ip);
671 } else if ((options & GENMASK_BYIP) || !hostname[strspn(hostname, "0123456789.")]) {
672 /* Should generate an IP-based hostmask. By popular acclaim, a /16
673 * hostmask is used by default. */
674 unsigned masked_ip, mask, masklen;
676 mask = ~0 << masklen;
677 masked_ip = ntohl(user->ip.s_addr) & mask;
678 hostname = alloca(32);
679 if (options & GENMASK_SRVXMASK) {
680 sprintf(hostname, "%d.%d.%d.%d/%d", (masked_ip>>24)&0xFF, (masked_ip>>16)&0xFF, (masked_ip>>8)&0xFF, masked_ip&0xFF, masklen);
683 for (ii=0; ii<4; ii++) {
685 ofs += sprintf(hostname+ofs, "%d.", (masked_ip>>24)&0xFF);
689 ofs += sprintf(hostname+ofs, "*.");
692 /* Truncate the last . */
697 /* This heuristic could be made smarter. Is it worth the effort? */
698 for (ii=cnt=0; hostname[ii]; ii++)
699 if (hostname[ii] == '.')
702 /* only a two-level domain name; leave hostname */
703 } else if (cnt == 2) {
704 for (ii=0; user->hostname[ii] != '.'; ii++) ;
705 /* Add 3 to account for the *. and \0. */
706 hostname = alloca(strlen(user->hostname+ii)+3);
707 sprintf(hostname, "*.%s", user->hostname+ii+1);
709 for (cnt=3, ii--; cnt; ii--)
710 if (user->hostname[ii] == '.')
712 /* The loop above will overshoot the dot one character;
713 we skip forward two (the one character and the dot)
714 when printing, so we only add one for the \0. */
715 hostname = alloca(strlen(user->hostname+ii)+1);
716 sprintf(hostname, "*.%s", user->hostname+ii+2);
720 len = strlen(ident) + strlen(hostname) + 2;
722 len += strlen(nickname) + 1;
724 sprintf(mask, "%s!%s@%s", nickname, ident, hostname);
727 sprintf(mask, "%s@%s", ident, hostname);
733 IsChannelName(const char *name) {
738 for (ii=1; name[ii]; ++ii) {
739 if ((name[ii] > 0) && (name[ii] <= 32))
743 if (name[ii] == '\xa0')