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")))
86 log_replay(MAIN_LOG, false, buffer);
87 if (cManager.uplink->state != DISCONNECTED)
88 parse_line(buffer, 0);
93 socket_destroyed(struct io_fd *fd)
96 log_module(MAIN_LOG, LOG_ERROR, "Connection to server lost.");
98 cManager.uplink->state = DISCONNECTED;
100 DelServer(self->uplink, 0, NULL);
103 void replay_event_loop(void)
105 while (!quit_services) {
106 if (!replay_connected) {
107 /* this time fudging is to get some of the logging right */
108 self->link = self->boot = now;
109 cManager.uplink->state = AUTHENTICATING;
110 irc_introduce(cManager.uplink->password);
111 replay_connected = 1;
112 } else if (!replay_read()) {
113 log_module(MAIN_LOG, LOG_ERROR, "Connection to server lost.");
121 create_socket_client(struct uplinkNode *target)
123 int port = target->port;
124 const char *addr = target->host;
127 return feof(replay_file) ? 0 : 1;
130 /* Leave the existing socket open, say we failed. */
131 log_module(MAIN_LOG, LOG_ERROR, "Refusing to create second connection to %s:%d.", addr, port);
135 log_module(MAIN_LOG, LOG_INFO, "Connecting to %s:%i...", addr, port);
137 socket_io_fd = ioset_connect((struct sockaddr*)cManager.uplink->bind_addr, sizeof(struct sockaddr), addr, port, 1, 0, NULL);
139 log_module(MAIN_LOG, LOG_ERROR, "Connection to uplink failed: %s (%d)", strerror(errno), errno);
140 target->state = DISCONNECTED;
144 socket_io_fd->readable_cb = uplink_readable;
145 socket_io_fd->destroy_cb = socket_destroyed;
146 socket_io_fd->line_reads = 1;
147 socket_io_fd->wants_reads = 1;
148 log_module(MAIN_LOG, LOG_INFO, "Connection to server established.");
149 cManager.uplink = target;
150 target->state = AUTHENTICATING;
156 replay_read_line(void)
161 if (replay_line[0]) return;
163 if (!fgets(replay_line, sizeof(replay_line), replay_file)) {
164 if (feof(replay_file)) {
166 memset(replay_line, 0, sizeof(replay_line));
170 if ((replay_line[0] != '[')
171 || (replay_line[3] != ':')
172 || (replay_line[6] != ':')
173 || (replay_line[9] != ' ')
174 || (replay_line[12] != '/')
175 || (replay_line[15] != '/')
176 || (replay_line[20] != ']')
177 || (replay_line[21] != ' ')) {
178 log_module(MAIN_LOG, LOG_ERROR, "Unrecognized timestamp in replay file: %s", replay_line);
181 timestamp.tm_hour = strtoul(replay_line+1, NULL, 10);
182 timestamp.tm_min = strtoul(replay_line+4, NULL, 10);
183 timestamp.tm_sec = strtoul(replay_line+7, NULL, 10);
184 timestamp.tm_mon = strtoul(replay_line+10, NULL, 10) - 1;
185 timestamp.tm_mday = strtoul(replay_line+13, NULL, 10);
186 timestamp.tm_year = strtoul(replay_line+16, NULL, 10) - 1900;
187 timestamp.tm_isdst = 0;
188 new_time = mktime(×tamp);
189 if (new_time == -1) {
190 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);
195 if (strncmp(replay_line+22, "(info) ", 7))
204 char read_line[MAXLEN];
207 /* if it's a sent line, break out to handle it */
208 if (!strncmp(replay_line+29, " ", 3))
210 if (!strncmp(replay_line+29, "W: ", 3)) {
211 log_module(MAIN_LOG, LOG_ERROR, "Expected response from services: %s", replay_line+32);
217 log_replay(MAIN_LOG, false, replay_line+32);
218 safestrncpy(read_line, replay_line+32, sizeof(read_line));
219 len = strlen(read_line);
220 if (read_line[len-1] == '\n')
221 read_line[--len] = 0;
223 parse_line(read_line, 0);
229 replay_write(char *text)
232 if (strncmp(replay_line+29, "W: ", 3)) {
233 log_module(MAIN_LOG, LOG_ERROR, "Unexpected output during replay: %s", text);
236 if (strcmp(replay_line+32, text)) {
237 log_module(MAIN_LOG, LOG_ERROR, "Incorrect output during replay:\nReceived: %sExpected: %s", text, replay_line+32);
239 log_replay(MAIN_LOG, true, text);
245 void putsock(const char *text, ...) PRINTF_LIKE(1, 2);
248 putsock(const char *text, ...)
254 if (!cManager.uplink || cManager.uplink->state == DISCONNECTED) return;
256 va_start(arg_list, text);
257 pos = vsnprintf(buffer, MAXLEN - 2, text, arg_list);
259 if (pos < 0 || pos > (MAXLEN - 2)) pos = MAXLEN - 2;
262 log_replay(MAIN_LOG, true, buffer);
263 buffer[pos++] = '\n';
265 ioset_write(socket_io_fd, buffer, pos);
267 replay_write(buffer);
275 replay_connected = 0;
276 socket_destroyed(socket_io_fd);
278 ioset_close(socket_io_fd->fd, 1);
282 #define CMD_FUNC(NAME) int NAME(UNUSED_ARG(const char *origin), UNUSED_ARG(unsigned int argc), UNUSED_ARG(char **argv))
283 typedef CMD_FUNC(cmd_func_t);
285 static void timed_ping_timeout(void *data);
287 /* Ping state is kept in the timeq (only one of these two can be in
288 * the queue at any given time). */
290 timed_send_ping(UNUSED_ARG(void *data))
292 irc_ping(self->name);
293 timeq_add(now + ping_timeout, timed_ping_timeout, 0);
297 timed_ping_timeout(UNUSED_ARG(void *data))
299 /* Uplink "health" tracking could be accomplished by counting the
300 number of ping timeouts that happen for each uplink. After the
301 timeouts per time period exceeds some amount, the uplink could
302 be marked as unavalable.*/
303 irc_squit(self, "Ping timeout.", NULL);
306 static CMD_FUNC(cmd_pass)
308 const char *true_pass;
312 true_pass = cManager.uplink->their_password;
313 if (true_pass && strcmp(true_pass, argv[1])) {
314 /* It might be good to mark the uplink as unavailable when
315 this happens, though there should be a way of resetting
317 irc_squit(self, "Incorrect password received.", NULL);
321 cManager.uplink->state = BURSTING;
325 static CMD_FUNC(cmd_dummy)
327 /* we don't care about these messages */
331 static CMD_FUNC(cmd_error)
333 if (argv[1]) log_module(MAIN_LOG, LOG_ERROR, "Error: %s", argv[1]);
334 log_module(MAIN_LOG, LOG_ERROR, "Error received from uplink, squitting.");
336 if (cManager.uplink->state != CONNECTED) {
337 /* Error messages while connected should be fine. */
338 cManager.uplink->flags |= UPLINK_UNAVAILABLE;
339 log_module(MAIN_LOG, LOG_ERROR, "Disabling uplink.");
346 static CMD_FUNC(cmd_stats)
352 if (!(un = GetUserH(origin)))
354 switch (argv[1][0]) {
356 unsigned int uptime = now - boot_time;
357 irc_numeric(un, RPL_STATSUPTIME, ":Server Up %d days %d:%02d:%02d",
358 uptime/(24*60*60), (uptime/(60*60))%24, (uptime/60)%60, uptime%60);
359 irc_numeric(un, RPL_MAXCONNECTIONS, ":Highest connection count: %d (%d clients)",
360 self->max_clients+1, self->max_clients);
363 default: /* unrecognized/unhandled stats output */ break;
365 irc_numeric(un, 219, "%s :End of /STATS report", argv[1]);
369 static CMD_FUNC(cmd_whois)
371 struct userNode *from;
372 struct userNode *who;
373 #ifdef WITH_PROTOCOL_P10
374 extern char *his_servername;
375 extern char *his_servercomment;
380 if (!(from = GetUserH(origin))) {
381 log_module(MAIN_LOG, LOG_ERROR, "Could not find WHOIS origin user %s", origin);
384 if(!(who = GetUserH(argv[2]))) {
385 irc_numeric(from, ERR_NOSUCHNICK, "%s@%s :No such nick", argv[2], self->name);
388 if (IsHiddenHost(who) && !IsOper(from)) {
389 /* Just stay quiet. */
392 irc_numeric(from, RPL_WHOISUSER, "%s %s %s * :%s", who->nick, who->ident, who->hostname, who->info);
393 #ifdef WITH_PROTOCOL_P10
394 if (his_servername && his_servercomment)
395 irc_numeric(from, RPL_WHOISSERVER, "%s %s :%s", who->nick, his_servername, his_servercomment);
398 irc_numeric(from, RPL_WHOISSERVER, "%s %s :%s", who->nick, who->uplink->name, who->uplink->description);
401 irc_numeric(from, RPL_WHOISOPERATOR, "%s :is a megalomaniacal power hungry tyrant", who->nick);
403 irc_numeric(from, RPL_ENDOFWHOIS, "%s :End of /WHOIS list", who->nick);
407 static CMD_FUNC(cmd_version)
409 struct userNode *user;
410 if (!(user = GetUserH(origin))) {
411 log_module(MAIN_LOG, LOG_ERROR, "Could not find VERSION origin user %s", origin);
414 irc_numeric(user, 351, "%s.%s %s :%s", PACKAGE_TARNAME, PACKAGE_VERSION, self->name, CODENAME);
418 static CMD_FUNC(cmd_admin)
420 struct userNode *user;
421 struct string_list *slist;
423 if (!(user = GetUserH(origin))) {
424 log_module(MAIN_LOG, LOG_ERROR, "Could not find ADMIN origin user %s", origin);
427 if ((slist = conf_get_data("server/admin", RECDB_STRING_LIST)) && slist->used) {
430 irc_numeric(user, 256, ":Administrative info about %s", self->name);
431 for (i = 0; i < slist->used && i < 3; i++)
432 irc_numeric(user, 257 + i, ":%s", slist->list[i]);
434 irc_numeric(user, 423, ":No administrative info available");
440 recalc_bursts(struct server *eob_server)
443 eob_server->burst = eob_server->self_burst;
444 if (eob_server->uplink != self)
445 eob_server->burst = eob_server->burst || eob_server->uplink->burst;
446 for (nn=0; nn < eob_server->children.used; nn++)
447 recalc_bursts(eob_server->children.list[nn]);
450 static struct chanmsg_func {
452 struct userNode *service;
453 } chanmsg_funcs[256]; /* indexed by trigger character */
455 static struct allchanmsg_func {
457 struct userNode *service;
458 } allchanmsg_funcs[ALLCHANMSG_FUNCS_MAX];
460 struct privmsg_desc {
461 struct userNode *user;
463 unsigned int is_notice : 1;
464 unsigned int is_qualified : 1;
468 privmsg_chan_helper(struct chanNode *cn, void *data)
470 struct privmsg_desc *pd = data;
472 struct chanmsg_func *cf = &chanmsg_funcs[(unsigned char)pd->text[0]];
475 /* Don't complain if it can't find the modeNode because the channel might
477 if ((mn = GetUserMode(cn, pd->user)))
478 mn->idle_since = now;
480 /* Never send a NOTICE to a channel to one of the services */
481 if (!pd->is_notice && cf->func && GetUserMode(cn, cf->service))
482 cf->func(pd->user, cn, pd->text+1, cf->service);
484 /* This catches *all* text sent to the channel that the services server sees */
485 for (x = 0; x < ALLCHANMSG_FUNCS_MAX; x++) {
486 cf = (struct chanmsg_func *)&allchanmsg_funcs[x];
488 break; /* end of list */
490 cf->func(pd->user, cn, pd->text, cf->service);
495 privmsg_invalid(char *name, void *data)
497 struct privmsg_desc *pd = data;
501 irc_numeric(pd->user, ERR_NOSUCHNICK, "%s@%s :No such nick", name, self->name);
505 part_helper(struct chanNode *cn, void *data)
507 DelChannelUser(data, cn, false, 0);
511 reg_chanmsg_func(unsigned char prefix, struct userNode *service, chanmsg_func_t handler)
513 if (chanmsg_funcs[prefix].func)
514 log_module(MAIN_LOG, LOG_WARNING, "Re-registering new chanmsg handler for character `%c'.", prefix);
515 chanmsg_funcs[prefix].func = handler;
516 chanmsg_funcs[prefix].service = service;
520 reg_allchanmsg_func(struct userNode *service, chanmsg_func_t handler)
523 for (x = 0; x < ALLCHANMSG_FUNCS_MAX; x++) {
524 if (allchanmsg_funcs[x].func)
526 allchanmsg_funcs[x].func = handler;
527 allchanmsg_funcs[x].service = service;
533 get_chanmsg_bot(unsigned char prefix)
535 return chanmsg_funcs[prefix].service;
538 static mode_change_func_t *mcf_list;
539 static unsigned int mcf_size = 0, mcf_used = 0;
542 reg_mode_change_func(mode_change_func_t handler)
544 if (mcf_used == mcf_size) {
547 mcf_list = realloc(mcf_list, mcf_size*sizeof(mode_change_func_t));
550 mcf_list = malloc(mcf_size*sizeof(mode_change_func_t));
553 mcf_list[mcf_used++] = handler;
556 struct mod_chanmode *
557 mod_chanmode_alloc(unsigned int argc)
559 struct mod_chanmode *res;
561 res = calloc(1, sizeof(*res) + (argc-1)*sizeof(res->args[0]));
563 res = calloc(1, sizeof(*res));
566 res->alloc_argc = argc;
573 struct mod_chanmode *
574 mod_chanmode_dup(struct mod_chanmode *orig, unsigned int extra)
576 struct mod_chanmode *res;
577 res = mod_chanmode_alloc(orig->argc + extra);
579 res->modes_set = orig->modes_set;
580 res->modes_clear = orig->modes_clear;
581 res->new_limit = orig->new_limit;
582 memcpy(res->new_key, orig->new_key, sizeof(res->new_key));
583 res->argc = orig->argc;
584 memcpy(res->args, orig->args, orig->argc*sizeof(orig->args[0]));
590 mod_chanmode_apply(struct userNode *who, struct chanNode *channel, struct mod_chanmode *change)
595 assert(change->argc <= change->alloc_argc);
596 channel->modes = (channel->modes & ~change->modes_clear) | change->modes_set;
597 if (change->modes_set & MODE_LIMIT)
598 channel->limit = change->new_limit;
599 if (change->modes_set & MODE_KEY)
600 strcpy(channel->key, change->new_key);
601 for (ii = 0; ii < change->argc; ++ii) {
602 switch (change->args[ii].mode) {
604 /* If any existing ban is a subset of the new ban,
605 * silently remove it. The new ban is not allowed
606 * to be more specific than an existing ban.
608 for (jj=0; jj<channel->banlist.used; ++jj) {
609 if (match_ircglobs(change->args[ii].hostmask, channel->banlist.list[jj]->ban)) {
610 banList_remove(&channel->banlist, channel->banlist.list[jj]);
611 free(channel->banlist.list[jj]);
615 bn = calloc(1, sizeof(*bn));
616 safestrncpy(bn->ban, change->args[ii].hostmask, sizeof(bn->ban));
617 safestrncpy(bn->who, who->nick, sizeof(bn->who));
619 banList_append(&channel->banlist, bn);
621 case MODE_REMOVE|MODE_BAN:
622 for (jj=0; jj<channel->banlist.used; ++jj) {
623 if (strcmp(channel->banlist.list[jj]->ban, change->args[ii].hostmask))
625 free(channel->banlist.list[jj]);
626 banList_remove(&channel->banlist, channel->banlist.list[jj]);
632 case MODE_VOICE|MODE_CHANOP:
633 case MODE_REMOVE|MODE_CHANOP:
634 case MODE_REMOVE|MODE_VOICE:
635 case MODE_REMOVE|MODE_VOICE|MODE_CHANOP:
636 if (change->args[ii].mode & MODE_REMOVE)
637 change->args[ii].member->modes &= ~change->args[ii].mode;
639 change->args[ii].member->modes |= change->args[ii].mode;
642 assert(0 && "Invalid mode argument");
649 mod_chanmode_free(struct mod_chanmode *change)
655 mod_chanmode(struct userNode *who, struct chanNode *channel, char **modes, unsigned int argc, unsigned int flags)
657 struct mod_chanmode *change;
660 if (!modes || !modes[0])
662 if (!(change = mod_chanmode_parse(channel, modes, argc, flags)))
664 if (flags & MC_ANNOUNCE)
665 mod_chanmode_announce(who, channel, change);
667 mod_chanmode_apply(who, channel, change);
668 if (flags & MC_NOTIFY)
669 for (ii = 0; ii < mcf_used; ++ii)
670 mcf_list[ii](channel, who, change);
671 mod_chanmode_free(change);
676 irc_make_chanmode(struct chanNode *chan, char *out) {
677 struct mod_chanmode change;
678 mod_chanmode_init(&change);
679 change.modes_set = chan->modes;
680 change.new_limit = chan->limit;
681 safestrncpy(change.new_key, chan->key, sizeof(change.new_key));
682 return strlen(mod_chanmode_format(&change, out));
686 generate_hostmask(struct userNode *user, int options)
688 char *nickname, *ident, *hostname;
692 /* figure out string parts */
693 if (options & GENMASK_OMITNICK)
695 else if (options & GENMASK_USENICK)
696 nickname = user->nick;
699 if (options & GENMASK_STRICT_IDENT)
701 else if (options & GENMASK_ANY_IDENT)
704 ident = alloca(strlen(user->ident)+2);
706 strcpy(ident+1, user->ident + ((*user->ident == '~')?1:0));
708 hostname = user->hostname;
709 if (IsHiddenHost(user) && user->handle_info && hidden_host_suffix && !(options & GENMASK_NO_HIDING)) {
710 hostname = alloca(strlen(user->handle_info->handle) + strlen(hidden_host_suffix) + 2);
711 sprintf(hostname, "%s.%s", user->handle_info->handle, hidden_host_suffix);
712 } else if (options & GENMASK_STRICT_HOST) {
713 if (options & GENMASK_BYIP)
714 hostname = inet_ntoa(user->ip);
715 } else if ((options & GENMASK_BYIP) || !hostname[strspn(hostname, "0123456789.")]) {
716 /* Should generate an IP-based hostmask. By popular acclaim, a /16
717 * hostmask is used by default. */
718 unsigned masked_ip, mask, masklen;
720 mask = ~0 << masklen;
721 masked_ip = ntohl(user->ip.s_addr) & mask;
722 hostname = alloca(32);
723 if (options & GENMASK_SRVXMASK) {
724 sprintf(hostname, "%d.%d.%d.%d/%d", (masked_ip>>24)&0xFF, (masked_ip>>16)&0xFF, (masked_ip>>8)&0xFF, masked_ip&0xFF, masklen);
727 for (ii=0; ii<4; ii++) {
729 ofs += sprintf(hostname+ofs, "%d.", (masked_ip>>24)&0xFF);
733 ofs += sprintf(hostname+ofs, "*.");
736 /* Truncate the last . */
741 /* This heuristic could be made smarter. Is it worth the effort? */
742 for (ii=cnt=0; hostname[ii]; ii++)
743 if (hostname[ii] == '.')
746 /* only a two-level domain name; leave hostname */
747 } else if (cnt == 2) {
748 for (ii=0; user->hostname[ii] != '.'; ii++) ;
749 /* Add 3 to account for the *. and \0. */
750 hostname = alloca(strlen(user->hostname+ii)+3);
751 sprintf(hostname, "*.%s", user->hostname+ii+1);
753 for (cnt=3, ii--; cnt; ii--)
754 if (user->hostname[ii] == '.')
756 /* The loop above will overshoot the dot one character;
757 we skip forward two (the one character and the dot)
758 when printing, so we only add one for the \0. */
759 hostname = alloca(strlen(user->hostname+ii)+1);
760 sprintf(hostname, "*.%s", user->hostname+ii+2);
764 len = strlen(ident) + strlen(hostname) + 2;
766 len += strlen(nickname) + 1;
768 sprintf(mask, "%s!%s@%s", nickname, ident, hostname);
771 sprintf(mask, "%s@%s", ident, hostname);
777 IsChannelName(const char *name) {
782 for (ii=1; name[ii]; ++ii) {
783 if ((name[ii] > 0) && (name[ii] <= 32))
787 if (name[ii] == '\xa0')