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;
44 static char replay_line[MAXLEN+80];
46 static int ping_timeout;
47 static int replay_connected;
48 static unsigned int nicklen = NICKLEN; /* how long do we think servers allow nicks to be? */
49 static struct userList dead_users;
51 extern struct cManagerNode cManager;
52 extern unsigned long burst_length;
53 extern struct cManagerNode cManager;
54 extern struct policer_params *oper_policer_params, *luser_policer_params;
55 extern server_link_func_t *slf_list;
56 extern unsigned int slf_size, slf_used;
57 extern new_user_func_t *nuf_list;
58 extern unsigned int nuf_size, nuf_used;
59 extern del_user_func_t *duf_list;
60 extern unsigned int duf_size, duf_used;
61 extern time_t boot_time;
63 void received_ping(void);
65 static int replay_read(void);
66 static dict_t irc_func_dict;
68 typedef void (*foreach_chanfunc) (struct chanNode *chan, void *data);
69 typedef void (*foreach_nonchan) (char *name, void *data);
70 typedef void (*foreach_userfunc) (struct userNode *user, void *data);
71 typedef void (*foreach_nonuser) (char *name, void *data);
72 static void parse_foreach(char *target_list, foreach_chanfunc cf, foreach_nonchan nc, foreach_userfunc uf, foreach_nonuser nu, void *data);
75 uplink_readable(struct io_fd *fd) {
76 static char buffer[MAXLEN];
80 pos = ioset_line_read(fd, buffer, sizeof(buffer));
85 if ((eol = strpbrk(buffer, "\r\n")))
87 log_replay(MAIN_LOG, false, buffer);
88 if (cManager.uplink->state != DISCONNECTED)
89 parse_line(buffer, 0);
94 socket_destroyed(struct io_fd *fd)
97 log_module(MAIN_LOG, LOG_ERROR, "Connection to server lost.");
99 cManager.uplink->state = DISCONNECTED;
101 DelServer(self->uplink, 0, NULL);
104 void replay_event_loop(void)
106 while (!quit_services) {
107 if (!replay_connected) {
108 /* this time fudging is to get some of the logging right */
109 self->link = self->boot = now;
110 cManager.uplink->state = AUTHENTICATING;
111 irc_introduce(cManager.uplink->password);
112 replay_connected = 1;
113 } else if (!replay_read()) {
114 log_module(MAIN_LOG, LOG_ERROR, "Connection to server lost.");
122 create_socket_client(struct uplinkNode *target)
124 int port = target->port;
125 const char *addr = target->host;
128 return feof(replay_file) ? 0 : 1;
131 /* Leave the existing socket open, say we failed. */
132 log_module(MAIN_LOG, LOG_ERROR, "Refusing to create second connection to %s:%d.", addr, port);
136 log_module(MAIN_LOG, LOG_INFO, "Connecting to %s:%i...", addr, port);
138 socket_io_fd = ioset_connect((struct sockaddr*)cManager.uplink->bind_addr, sizeof(struct sockaddr), addr, port, 1, 0, NULL);
140 log_module(MAIN_LOG, LOG_ERROR, "Connection to uplink failed: %s (%d)", strerror(errno), errno);
141 target->state = DISCONNECTED;
145 socket_io_fd->readable_cb = uplink_readable;
146 socket_io_fd->destroy_cb = socket_destroyed;
147 socket_io_fd->line_reads = 1;
148 socket_io_fd->wants_reads = 1;
149 log_module(MAIN_LOG, LOG_INFO, "Connection to server established.");
150 cManager.uplink = target;
151 target->state = AUTHENTICATING;
157 replay_read_line(void)
162 if (replay_line[0]) return;
164 if (!fgets(replay_line, sizeof(replay_line), replay_file)) {
165 if (feof(replay_file)) {
167 memset(replay_line, 0, sizeof(replay_line));
171 if ((replay_line[0] != '[')
172 || (replay_line[3] != ':')
173 || (replay_line[6] != ':')
174 || (replay_line[9] != ' ')
175 || (replay_line[12] != '/')
176 || (replay_line[15] != '/')
177 || (replay_line[20] != ']')
178 || (replay_line[21] != ' ')) {
179 log_module(MAIN_LOG, LOG_ERROR, "Unrecognized timestamp in replay file: %s", replay_line);
182 timestamp.tm_hour = strtoul(replay_line+1, NULL, 10);
183 timestamp.tm_min = strtoul(replay_line+4, NULL, 10);
184 timestamp.tm_sec = strtoul(replay_line+7, NULL, 10);
185 timestamp.tm_mon = strtoul(replay_line+10, NULL, 10) - 1;
186 timestamp.tm_mday = strtoul(replay_line+13, NULL, 10);
187 timestamp.tm_year = strtoul(replay_line+16, NULL, 10) - 1900;
188 timestamp.tm_isdst = 0;
189 new_time = mktime(×tamp);
190 if (new_time == -1) {
191 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);
196 if (strncmp(replay_line+22, "(info) ", 7))
205 char read_line[MAXLEN];
208 /* if it's a sent line, break out to handle it */
209 if (!strncmp(replay_line+29, " ", 3))
211 if (!strncmp(replay_line+29, "W: ", 3)) {
212 log_module(MAIN_LOG, LOG_ERROR, "Expected response from services: %s", replay_line+32);
218 log_replay(MAIN_LOG, false, replay_line+32);
219 safestrncpy(read_line, replay_line+32, sizeof(read_line));
220 len = strlen(read_line);
221 if (read_line[len-1] == '\n')
222 read_line[--len] = 0;
224 parse_line(read_line, 0);
230 replay_write(char *text)
233 if (strncmp(replay_line+29, "W: ", 3)) {
234 log_module(MAIN_LOG, LOG_ERROR, "Unexpected output during replay: %s", text);
237 if (strcmp(replay_line+32, text)) {
238 log_module(MAIN_LOG, LOG_ERROR, "Incorrect output during replay:\nReceived: %sExpected: %s", text, replay_line+32);
240 log_replay(MAIN_LOG, true, text);
246 void putsock(const char *text, ...) PRINTF_LIKE(1, 2);
249 putsock(const char *text, ...)
255 if (!cManager.uplink || cManager.uplink->state == DISCONNECTED) return;
257 va_start(arg_list, text);
258 pos = vsnprintf(buffer, MAXLEN - 2, text, arg_list);
260 if (pos < 0 || pos > (MAXLEN - 2)) pos = MAXLEN - 2;
263 log_replay(MAIN_LOG, true, buffer);
264 buffer[pos++] = '\n';
266 ioset_write(socket_io_fd, buffer, pos);
268 replay_write(buffer);
276 replay_connected = 0;
277 socket_destroyed(socket_io_fd);
279 ioset_close(socket_io_fd->fd, 1);
283 #define CMD_FUNC(NAME) int NAME(UNUSED_ARG(const char *origin), UNUSED_ARG(unsigned int argc), UNUSED_ARG(char **argv))
284 typedef CMD_FUNC(cmd_func_t);
286 static void timed_ping_timeout(void *data);
288 /* Ping state is kept in the timeq (only one of these two can be in
289 * the queue at any given time). */
291 timed_send_ping(UNUSED_ARG(void *data))
293 irc_ping(self->name);
294 timeq_add(now + ping_timeout, timed_ping_timeout, 0);
298 timed_ping_timeout(UNUSED_ARG(void *data))
300 /* Uplink "health" tracking could be accomplished by counting the
301 number of ping timeouts that happen for each uplink. After the
302 timeouts per time period exceeds some amount, the uplink could
303 be marked as unavalable.*/
304 irc_squit(self, "Ping timeout.", NULL);
307 static CMD_FUNC(cmd_pass)
309 const char *true_pass;
313 true_pass = cManager.uplink->their_password;
314 if (true_pass && strcmp(true_pass, argv[1])) {
315 /* It might be good to mark the uplink as unavailable when
316 this happens, though there should be a way of resetting
318 irc_squit(self, "Incorrect password received.", NULL);
322 cManager.uplink->state = BURSTING;
326 static CMD_FUNC(cmd_dummy)
328 /* we don't care about these messages */
332 static CMD_FUNC(cmd_error)
334 if (argv[1]) log_module(MAIN_LOG, LOG_ERROR, "Error: %s", argv[1]);
335 log_module(MAIN_LOG, LOG_ERROR, "Error received from uplink, squitting.");
337 if (cManager.uplink->state != CONNECTED) {
338 /* Error messages while connected should be fine. */
339 cManager.uplink->flags |= UPLINK_UNAVAILABLE;
340 log_module(MAIN_LOG, LOG_ERROR, "Disabling uplink.");
347 static CMD_FUNC(cmd_stats)
353 if (!(un = GetUserH(origin)))
355 switch (argv[1][0]) {
357 unsigned int uptime = now - boot_time;
358 irc_numeric(un, RPL_STATSUPTIME, ":Server Up %d days %d:%02d:%02d",
359 uptime/(24*60*60), (uptime/(60*60))%24, (uptime/60)%60, uptime%60);
360 irc_numeric(un, RPL_MAXCONNECTIONS, ":Highest connection count: %d (%d clients)",
361 self->max_clients+1, self->max_clients);
364 default: /* unrecognized/unhandled stats output */ break;
366 irc_numeric(un, 219, "%s :End of /STATS report", argv[1]);
370 static CMD_FUNC(cmd_whois)
372 struct userNode *from;
373 struct userNode *who;
374 #ifdef WITH_PROTOCOL_P10
375 extern char *his_servername;
376 extern char *his_servercomment;
381 if (!(from = GetUserH(origin))) {
382 log_module(MAIN_LOG, LOG_ERROR, "Could not find WHOIS origin user %s", origin);
385 if(!(who = GetUserH(argv[2]))) {
386 irc_numeric(from, ERR_NOSUCHNICK, "%s@%s :No such nick", argv[2], self->name);
389 if (IsHiddenHost(who) && !IsOper(from)) {
390 /* Just stay quiet. */
393 irc_numeric(from, RPL_WHOISUSER, "%s %s %s * :%s", who->nick, who->ident, who->hostname, who->info);
394 #ifdef WITH_PROTOCOL_P10
395 if (his_servername && his_servercomment)
396 irc_numeric(from, RPL_WHOISSERVER, "%s %s :%s", who->nick, his_servername, his_servercomment);
399 irc_numeric(from, RPL_WHOISSERVER, "%s %s :%s", who->nick, who->uplink->name, who->uplink->description);
402 irc_numeric(from, RPL_WHOISOPERATOR, "%s :is a megalomaniacal power hungry tyrant", who->nick);
404 irc_numeric(from, RPL_ENDOFWHOIS, "%s :End of /WHOIS list", who->nick);
408 static CMD_FUNC(cmd_version)
410 struct userNode *user;
411 if (!(user = GetUserH(origin))) {
412 log_module(MAIN_LOG, LOG_ERROR, "Could not find VERSION origin user %s", origin);
415 irc_numeric(user, 351, "%s.%s %s :%s", PACKAGE_TARNAME, PACKAGE_VERSION, self->name, CODENAME);
419 static CMD_FUNC(cmd_admin)
421 struct userNode *user;
422 struct string_list *slist;
424 if (!(user = GetUserH(origin))) {
425 log_module(MAIN_LOG, LOG_ERROR, "Could not find ADMIN origin user %s", origin);
428 if ((slist = conf_get_data("server/admin", RECDB_STRING_LIST)) && slist->used) {
431 irc_numeric(user, 256, ":Administrative info about %s", self->name);
432 for (i = 0; i < slist->used && i < 3; i++)
433 irc_numeric(user, 257 + i, ":%s", slist->list[i]);
435 irc_numeric(user, 423, ":No administrative info available");
441 recalc_bursts(struct server *eob_server)
444 eob_server->burst = eob_server->self_burst;
445 if (eob_server->uplink != self)
446 eob_server->burst = eob_server->burst || eob_server->uplink->burst;
447 for (nn=0; nn < eob_server->children.used; nn++)
448 recalc_bursts(eob_server->children.list[nn]);
451 static struct chanmsg_func {
453 struct userNode *service;
454 } chanmsg_funcs[256]; /* indexed by trigger character */
456 static struct allchanmsg_func {
458 struct userNode *service;
459 } allchanmsg_funcs[ALLCHANMSG_FUNCS_MAX];
461 struct privmsg_desc {
462 struct userNode *user;
464 unsigned int is_notice : 1;
465 unsigned int is_qualified : 1;
469 privmsg_chan_helper(struct chanNode *cn, void *data)
471 struct privmsg_desc *pd = data;
473 struct chanmsg_func *cf = &chanmsg_funcs[(unsigned char)pd->text[0]];
476 /* Don't complain if it can't find the modeNode because the channel might
478 if ((mn = GetUserMode(cn, pd->user)))
479 mn->idle_since = now;
481 /* Never send a NOTICE to a channel to one of the services */
482 if (!pd->is_notice && cf->func && GetUserMode(cn, cf->service))
483 cf->func(pd->user, cn, pd->text+1, cf->service);
485 /* This catches *all* text sent to the channel that the services server sees */
486 for (x = 0; x < ALLCHANMSG_FUNCS_MAX; x++) {
487 cf = (struct chanmsg_func *)&allchanmsg_funcs[x];
489 break; /* end of list */
491 cf->func(pd->user, cn, pd->text, cf->service);
496 privmsg_invalid(char *name, void *data)
498 struct privmsg_desc *pd = data;
502 irc_numeric(pd->user, ERR_NOSUCHNICK, "%s@%s :No such nick", name, self->name);
506 part_helper(struct chanNode *cn, void *data)
508 DelChannelUser(data, cn, false, 0);
512 reg_chanmsg_func(unsigned char prefix, struct userNode *service, chanmsg_func_t handler)
514 if (chanmsg_funcs[prefix].func)
515 log_module(MAIN_LOG, LOG_WARNING, "Re-registering new chanmsg handler for character `%c'.", prefix);
516 chanmsg_funcs[prefix].func = handler;
517 chanmsg_funcs[prefix].service = service;
521 reg_allchanmsg_func(struct userNode *service, chanmsg_func_t handler)
524 for (x = 0; x < ALLCHANMSG_FUNCS_MAX; x++) {
525 if (allchanmsg_funcs[x].func)
527 allchanmsg_funcs[x].func = handler;
528 allchanmsg_funcs[x].service = service;
534 get_chanmsg_bot(unsigned char prefix)
536 return chanmsg_funcs[prefix].service;
539 static mode_change_func_t *mcf_list;
540 static unsigned int mcf_size = 0, mcf_used = 0;
543 reg_mode_change_func(mode_change_func_t handler)
545 if (mcf_used == mcf_size) {
548 mcf_list = realloc(mcf_list, mcf_size*sizeof(mode_change_func_t));
551 mcf_list = malloc(mcf_size*sizeof(mode_change_func_t));
554 mcf_list[mcf_used++] = handler;
557 struct mod_chanmode *
558 mod_chanmode_alloc(unsigned int argc)
560 struct mod_chanmode *res;
562 res = calloc(1, sizeof(*res) + (argc-1)*sizeof(res->args[0]));
564 res = calloc(1, sizeof(*res));
567 res->alloc_argc = argc;
574 struct mod_chanmode *
575 mod_chanmode_dup(struct mod_chanmode *orig, unsigned int extra)
577 struct mod_chanmode *res;
578 res = mod_chanmode_alloc(orig->argc + extra);
580 res->modes_set = orig->modes_set;
581 res->modes_clear = orig->modes_clear;
582 res->new_limit = orig->new_limit;
583 memcpy(res->new_key, orig->new_key, sizeof(res->new_key));
584 res->argc = orig->argc;
585 memcpy(res->args, orig->args, orig->argc*sizeof(orig->args[0]));
591 mod_chanmode_apply(struct userNode *who, struct chanNode *channel, struct mod_chanmode *change)
596 assert(change->argc <= change->alloc_argc);
597 channel->modes = (channel->modes & ~change->modes_clear) | change->modes_set;
598 if (change->modes_set & MODE_LIMIT)
599 channel->limit = change->new_limit;
600 if (change->modes_set & MODE_KEY)
601 strcpy(channel->key, change->new_key);
602 for (ii = 0; ii < change->argc; ++ii) {
603 switch (change->args[ii].mode) {
605 /* If any existing ban is a subset of the new ban,
606 * silently remove it. The new ban is not allowed
607 * to be more specific than an existing ban.
609 for (jj=0; jj<channel->banlist.used; ++jj) {
610 if (match_ircglobs(change->args[ii].hostmask, channel->banlist.list[jj]->ban)) {
611 banList_remove(&channel->banlist, channel->banlist.list[jj]);
612 free(channel->banlist.list[jj]);
616 bn = calloc(1, sizeof(*bn));
617 safestrncpy(bn->ban, change->args[ii].hostmask, sizeof(bn->ban));
618 safestrncpy(bn->who, who->nick, sizeof(bn->who));
620 banList_append(&channel->banlist, bn);
622 case MODE_REMOVE|MODE_BAN:
623 for (jj=0; jj<channel->banlist.used; ++jj) {
624 if (strcmp(channel->banlist.list[jj]->ban, change->args[ii].hostmask))
626 free(channel->banlist.list[jj]);
627 banList_remove(&channel->banlist, channel->banlist.list[jj]);
633 case MODE_VOICE|MODE_CHANOP:
634 case MODE_REMOVE|MODE_CHANOP:
635 case MODE_REMOVE|MODE_VOICE:
636 case MODE_REMOVE|MODE_VOICE|MODE_CHANOP:
637 if (change->args[ii].mode & MODE_REMOVE)
638 change->args[ii].member->modes &= ~change->args[ii].mode;
640 change->args[ii].member->modes |= change->args[ii].mode;
643 assert(0 && "Invalid mode argument");
650 mod_chanmode_free(struct mod_chanmode *change)
656 mod_chanmode(struct userNode *who, struct chanNode *channel, char **modes, unsigned int argc, unsigned int flags)
658 struct mod_chanmode *change;
661 if (!modes || !modes[0])
663 if (!(change = mod_chanmode_parse(channel, modes, argc, flags)))
665 if (flags & MC_ANNOUNCE)
666 mod_chanmode_announce(who, channel, change);
668 mod_chanmode_apply(who, channel, change);
669 if (flags & MC_NOTIFY)
670 for (ii = 0; ii < mcf_used; ++ii)
671 mcf_list[ii](channel, who, change);
672 mod_chanmode_free(change);
677 irc_make_chanmode(struct chanNode *chan, char *out) {
678 struct mod_chanmode change;
679 mod_chanmode_init(&change);
680 change.modes_set = chan->modes;
681 change.new_limit = chan->limit;
682 safestrncpy(change.new_key, chan->key, sizeof(change.new_key));
683 return strlen(mod_chanmode_format(&change, out));
687 generate_hostmask(struct userNode *user, int options)
689 char *nickname, *ident, *hostname;
693 /* figure out string parts */
694 if (options & GENMASK_OMITNICK)
696 else if (options & GENMASK_USENICK)
697 nickname = user->nick;
700 if (options & GENMASK_STRICT_IDENT)
702 else if (options & GENMASK_ANY_IDENT)
705 ident = alloca(strlen(user->ident)+2);
707 strcpy(ident+1, user->ident + ((*user->ident == '~')?1:0));
709 hostname = user->hostname;
710 if (IsHiddenHost(user) && user->handle_info && hidden_host_suffix && !(options & GENMASK_NO_HIDING)) {
711 hostname = alloca(strlen(user->handle_info->handle) + strlen(hidden_host_suffix) + 2);
712 sprintf(hostname, "%s.%s", user->handle_info->handle, hidden_host_suffix);
713 } else if (options & GENMASK_STRICT_HOST) {
714 if (options & GENMASK_BYIP)
715 hostname = inet_ntoa(user->ip);
716 } else if ((options & GENMASK_BYIP) || !hostname[strspn(hostname, "0123456789.")]) {
717 /* Should generate an IP-based hostmask. By popular acclaim, a /16
718 * hostmask is used by default. */
719 unsigned masked_ip, mask, masklen;
721 mask = ~0 << masklen;
722 masked_ip = ntohl(user->ip.s_addr) & mask;
723 hostname = alloca(32);
724 if (options & GENMASK_SRVXMASK) {
725 sprintf(hostname, "%d.%d.%d.%d/%d", (masked_ip>>24)&0xFF, (masked_ip>>16)&0xFF, (masked_ip>>8)&0xFF, masked_ip&0xFF, masklen);
728 for (ii=0; ii<4; ii++) {
730 ofs += sprintf(hostname+ofs, "%d.", (masked_ip>>24)&0xFF);
734 ofs += sprintf(hostname+ofs, "*.");
737 /* Truncate the last . */
742 /* This heuristic could be made smarter. Is it worth the effort? */
743 for (ii=cnt=0; hostname[ii]; ii++)
744 if (hostname[ii] == '.')
747 /* only a two-level domain name; leave hostname */
748 } else if (cnt == 2) {
749 for (ii=0; user->hostname[ii] != '.'; ii++) ;
750 /* Add 3 to account for the *. and \0. */
751 hostname = alloca(strlen(user->hostname+ii)+3);
752 sprintf(hostname, "*.%s", user->hostname+ii+1);
754 for (cnt=3, ii--; cnt; ii--)
755 if (user->hostname[ii] == '.')
757 /* The loop above will overshoot the dot one character;
758 we skip forward two (the one character and the dot)
759 when printing, so we only add one for the \0. */
760 hostname = alloca(strlen(user->hostname+ii)+1);
761 sprintf(hostname, "*.%s", user->hostname+ii+2);
765 len = strlen(ident) + strlen(hostname) + 2;
767 len += strlen(nickname) + 1;
769 sprintf(mask, "%s!%s@%s", nickname, ident, hostname);
772 sprintf(mask, "%s@%s", ident, hostname);
778 IsChannelName(const char *name) {
783 for (ii=1; name[ii]; ++ii) {
784 if ((name[ii] > 0) && (name[ii] <= 32))
788 if (name[ii] == '\xa0')