1 /* proto-common.c - common IRC protocol parsing/sending support
2 * Copyright 2000-2006 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)
95 if (fd && fd->state != IO_CONNECTED)
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(cManager.uplink->bind_addr, cManager.uplink->bind_addr_len, 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 log_module(MAIN_LOG, LOG_INFO, "Connection to server established.");
148 cManager.uplink = target;
149 target->state = AUTHENTICATING;
155 replay_read_line(void)
160 if (replay_line[0]) return;
162 if (!fgets(replay_line, sizeof(replay_line), replay_file)) {
163 if (feof(replay_file)) {
165 memset(replay_line, 0, sizeof(replay_line));
169 if ((replay_line[0] != '[')
170 || (replay_line[3] != ':')
171 || (replay_line[6] != ':')
172 || (replay_line[9] != ' ')
173 || (replay_line[12] != '/')
174 || (replay_line[15] != '/')
175 || (replay_line[20] != ']')
176 || (replay_line[21] != ' ')) {
177 log_module(MAIN_LOG, LOG_ERROR, "Unrecognized timestamp in replay file: %s", replay_line);
180 timestamp.tm_hour = strtoul(replay_line+1, NULL, 10);
181 timestamp.tm_min = strtoul(replay_line+4, NULL, 10);
182 timestamp.tm_sec = strtoul(replay_line+7, NULL, 10);
183 timestamp.tm_mon = strtoul(replay_line+10, NULL, 10) - 1;
184 timestamp.tm_mday = strtoul(replay_line+13, NULL, 10);
185 timestamp.tm_year = strtoul(replay_line+16, NULL, 10) - 1900;
186 timestamp.tm_isdst = 0;
187 new_time = mktime(×tamp);
188 if (new_time == -1) {
189 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);
194 if (strncmp(replay_line+22, "(info) ", 7))
203 char read_line[MAXLEN];
206 /* if it's a sent line, break out to handle it */
207 if (!strncmp(replay_line+29, " ", 3))
209 if (!strncmp(replay_line+29, "W: ", 3)) {
210 log_module(MAIN_LOG, LOG_ERROR, "Expected response from services: %s", replay_line+32);
216 log_replay(MAIN_LOG, false, replay_line+32);
217 safestrncpy(read_line, replay_line+32, sizeof(read_line));
218 len = strlen(read_line);
219 if (read_line[len-1] == '\n')
220 read_line[--len] = 0;
222 parse_line(read_line, 0);
228 replay_write(char *text)
231 if (strncmp(replay_line+29, "W: ", 3)) {
232 log_module(MAIN_LOG, LOG_ERROR, "Unexpected output during replay: %s", text);
235 if (strcmp(replay_line+32, text)) {
236 log_module(MAIN_LOG, LOG_ERROR, "Incorrect output during replay:\nReceived: %sExpected: %s", text, replay_line+32);
238 log_replay(MAIN_LOG, true, text);
244 void putsock(const char *text, ...) PRINTF_LIKE(1, 2);
247 putsock(const char *text, ...)
253 if (!cManager.uplink || cManager.uplink->state == DISCONNECTED) return;
255 va_start(arg_list, text);
256 pos = vsnprintf(buffer, MAXLEN - 2, text, arg_list);
258 if (pos < 0 || pos > (MAXLEN - 2)) pos = MAXLEN - 2;
261 log_replay(MAIN_LOG, true, buffer);
262 buffer[pos++] = '\n';
264 ioset_write(socket_io_fd, buffer, pos);
266 replay_write(buffer);
274 replay_connected = 0;
275 socket_destroyed(socket_io_fd);
277 ioset_close(socket_io_fd, 3);
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_version)
371 struct userNode *user;
372 if (!(user = GetUserH(origin))) {
373 log_module(MAIN_LOG, LOG_ERROR, "Could not find VERSION origin user %s", origin);
376 irc_numeric(user, 351, "%s.%s %s :%s", PACKAGE_TARNAME, PACKAGE_VERSION, self->name, CODENAME);
380 static CMD_FUNC(cmd_admin)
382 struct userNode *user;
383 struct string_list *slist;
385 if (!(user = GetUserH(origin))) {
386 log_module(MAIN_LOG, LOG_ERROR, "Could not find ADMIN origin user %s", origin);
389 if ((slist = conf_get_data("server/admin", RECDB_STRING_LIST)) && slist->used) {
392 irc_numeric(user, 256, ":Administrative info about %s", self->name);
393 for (i = 0; i < slist->used && i < 3; i++)
394 irc_numeric(user, 257 + i, ":%s", slist->list[i]);
396 irc_numeric(user, 423, ":No administrative info available");
402 recalc_bursts(struct server *eob_server)
405 eob_server->burst = eob_server->self_burst;
406 if (eob_server->uplink != self)
407 eob_server->burst = eob_server->burst || eob_server->uplink->burst;
408 for (nn=0; nn < eob_server->children.used; nn++)
409 recalc_bursts(eob_server->children.list[nn]);
412 static struct chanmsg_func {
414 struct userNode *service;
415 } chanmsg_funcs[256]; /* indexed by trigger character */
417 static struct allchanmsg_func {
419 struct userNode *service;
420 } allchanmsg_funcs[ALLCHANMSG_FUNCS_MAX];
422 struct privmsg_desc {
423 struct userNode *user;
425 unsigned int is_notice : 1;
426 unsigned int is_qualified : 1;
430 privmsg_chan_helper(struct chanNode *cn, void *data)
432 struct privmsg_desc *pd = data;
434 struct chanmsg_func *cf = &chanmsg_funcs[(unsigned char)pd->text[0]];
437 /* Don't complain if it can't find the modeNode because the channel might
439 if ((mn = GetUserMode(cn, pd->user)))
440 mn->idle_since = now;
442 /* Never send a NOTICE to a channel to one of the services */
443 if (!pd->is_notice && cf->func
444 && ((cn->modes & MODE_REGISTERED) || GetUserMode(cn, cf->service)))
446 /* This catches *all* text sent to the channel that the services server sees */
447 for (x = 0; x < ALLCHANMSG_FUNCS_MAX; x++) {
448 cf = (struct chanmsg_func *)&allchanmsg_funcs[x];
450 break; /* end of list */
452 cf->func(pd->user, cn, pd->text, cf->service);
457 privmsg_invalid(char *name, void *data)
459 struct privmsg_desc *pd = data;
463 irc_numeric(pd->user, ERR_NOSUCHNICK, "%s@%s :No such nick", name, self->name);
467 struct userNode *user;
472 part_helper(struct chanNode *cn, void *data)
474 struct part_desc *desc = data;
475 DelChannelUser(desc->user, cn, desc->text, false);
478 static CMD_FUNC(cmd_part)
480 struct part_desc desc;
484 desc.user = GetUserH(origin);
487 desc.text = (argc > 2) ? argv[argc - 1] : NULL;
488 parse_foreach(argv[1], part_helper, NULL, NULL, NULL, &desc);
493 reg_chanmsg_func(unsigned char prefix, struct userNode *service, chanmsg_func_t handler)
495 if (chanmsg_funcs[prefix].func)
496 log_module(MAIN_LOG, LOG_WARNING, "Re-registering new chanmsg handler for character `%c'.", prefix);
497 chanmsg_funcs[prefix].func = handler;
498 chanmsg_funcs[prefix].service = service;
502 reg_allchanmsg_func(struct userNode *service, chanmsg_func_t handler)
505 for (x = 0; x < ALLCHANMSG_FUNCS_MAX; x++) {
506 if (allchanmsg_funcs[x].func)
508 allchanmsg_funcs[x].func = handler;
509 allchanmsg_funcs[x].service = service;
515 get_chanmsg_bot(unsigned char prefix)
517 return chanmsg_funcs[prefix].service;
520 static mode_change_func_t *mcf_list;
521 static unsigned int mcf_size = 0, mcf_used = 0;
524 reg_mode_change_func(mode_change_func_t handler)
526 if (mcf_used == mcf_size) {
529 mcf_list = realloc(mcf_list, mcf_size*sizeof(mode_change_func_t));
532 mcf_list = malloc(mcf_size*sizeof(mode_change_func_t));
535 mcf_list[mcf_used++] = handler;
538 struct mod_chanmode *
539 mod_chanmode_alloc(unsigned int argc)
541 struct mod_chanmode *res;
543 res = calloc(1, sizeof(*res) + (argc-1)*sizeof(res->args[0]));
545 res = calloc(1, sizeof(*res));
548 res->alloc_argc = argc;
555 struct mod_chanmode *
556 mod_chanmode_dup(struct mod_chanmode *orig, unsigned int extra)
558 struct mod_chanmode *res;
559 res = mod_chanmode_alloc(orig->argc + extra);
561 res->modes_set = orig->modes_set;
562 res->modes_clear = orig->modes_clear;
563 res->new_limit = orig->new_limit;
564 memcpy(res->new_key, orig->new_key, sizeof(res->new_key));
565 memcpy(res->new_upass, orig->new_upass, sizeof(res->new_upass));
566 memcpy(res->new_apass, orig->new_apass, sizeof(res->new_apass));
567 res->argc = orig->argc;
568 memcpy(res->args, orig->args, orig->argc*sizeof(orig->args[0]));
574 mod_chanmode_apply(struct userNode *who, struct chanNode *channel, struct mod_chanmode *change)
579 assert(change->argc <= change->alloc_argc);
580 channel->modes = (channel->modes & ~change->modes_clear) | change->modes_set;
581 if (change->modes_set & MODE_LIMIT)
582 channel->limit = change->new_limit;
583 if (change->modes_set & MODE_KEY)
584 strcpy(channel->key, change->new_key);
585 if (change->modes_set & MODE_UPASS)
586 strcpy(channel->upass, change->new_upass);
587 if (change->modes_set & MODE_APASS)
588 strcpy(channel->apass, change->new_apass);
589 for (ii = 0; ii < change->argc; ++ii) {
590 switch (change->args[ii].mode) {
592 /* If any existing ban is a subset of the new ban,
593 * silently remove it. The new ban is not allowed
594 * to be more specific than an existing ban.
596 for (jj=0; jj<channel->banlist.used; ++jj) {
597 bn = channel->banlist.list[jj];
598 if (match_ircglobs(change->args[ii].u.hostmask, bn->ban)) {
599 banList_remove(&channel->banlist, bn);
604 bn = calloc(1, sizeof(*bn));
605 safestrncpy(bn->ban, change->args[ii].u.hostmask, sizeof(bn->ban));
607 safestrncpy(bn->who, who->nick, sizeof(bn->who));
609 safestrncpy(bn->who, "<unknown>", sizeof(bn->who));
611 banList_append(&channel->banlist, bn);
613 case MODE_REMOVE|MODE_BAN:
614 for (jj=0; jj<channel->banlist.used; ++jj) {
615 bn = channel->banlist.list[jj];
616 if (strcmp(bn->ban, change->args[ii].u.hostmask))
619 banList_remove(&channel->banlist, bn);
625 case MODE_VOICE|MODE_CHANOP:
626 case MODE_REMOVE|MODE_CHANOP:
627 case MODE_REMOVE|MODE_VOICE:
628 case MODE_REMOVE|MODE_VOICE|MODE_CHANOP:
629 if (change->args[ii].mode & MODE_REMOVE)
630 change->args[ii].u.member->modes &= ~change->args[ii].mode;
632 change->args[ii].u.member->modes |= change->args[ii].mode;
635 assert(0 && "Invalid mode argument");
642 mod_chanmode_free(struct mod_chanmode *change)
648 mod_chanmode(struct userNode *who, struct chanNode *channel, char **modes, unsigned int argc, unsigned int flags)
650 struct modeNode *member;
651 struct mod_chanmode *change;
655 if (!modes || !modes[0])
657 if (who && (member = GetUserMode(channel, who)))
658 base_oplevel = member->oplevel;
660 base_oplevel = MAXOPLEVEL;
661 if (!(change = mod_chanmode_parse(channel, modes, argc, flags, base_oplevel)))
663 if (flags & MC_ANNOUNCE)
664 mod_chanmode_announce(who, channel, change);
666 mod_chanmode_apply(who, channel, change);
667 if (flags & MC_NOTIFY)
668 for (ii = 0; ii < mcf_used; ++ii)
669 mcf_list[ii](channel, who, change);
670 mod_chanmode_free(change);
675 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 safestrncpy(change.new_upass, chan->upass, sizeof(change.new_upass));
683 safestrncpy(change.new_apass, chan->apass, sizeof(change.new_apass));
684 return strlen(mod_chanmode_format(&change, out));
688 generate_hostmask(struct userNode *user, int options)
691 char *nickname, *ident, *hostname, *mask;
694 /* figure out string parts */
695 if (options & GENMASK_OMITNICK)
697 else if (options & GENMASK_USENICK)
698 nickname = user->nick;
701 if (options & GENMASK_STRICT_IDENT)
703 else if (options & GENMASK_ANY_IDENT)
706 ident = alloca(strlen(user->ident)+2);
708 strcpy(ident+1, user->ident + ((*user->ident == '~')?1:0));
710 hostname = user->hostname;
711 if (IsFakeHost(user) && IsHiddenHost(user) && !(options & GENMASK_NO_HIDING)) {
712 hostname = user->fakehost;
713 } else if (IsHiddenHost(user) && user->handle_info && hidden_host_suffix && !(options & GENMASK_NO_HIDING)) {
714 hostname = alloca(strlen(user->handle_info->handle) + strlen(hidden_host_suffix) + 2);
715 sprintf(hostname, "%s.%s", user->handle_info->handle, hidden_host_suffix);
716 } else if (options & GENMASK_STRICT_HOST) {
717 if (options & GENMASK_BYIP)
718 hostname = (char*)irc_ntoa(&user->ip);
719 } else if ((options & GENMASK_BYIP) || irc_pton(&ip, NULL, hostname)) {
720 /* Should generate an IP-based hostmask. */
721 hostname = alloca(IRC_NTOP_MAX_SIZE);
722 hostname[IRC_NTOP_MAX_SIZE-1] = '\0';
723 if (irc_in_addr_is_ipv4(user->ip)) {
724 /* By popular acclaim, a /16 hostmask is used. */
725 sprintf(hostname, "%d.%d.*", user->ip.in6_8[12], user->ip.in6_8[13]);
726 } else if (irc_in_addr_is_ipv6(user->ip)) {
727 /* Who knows what the default mask should be? Use a /48 to start with. */
728 sprintf(hostname, "%x:%x:%x:*", user->ip.in6[0], user->ip.in6[1], user->ip.in6[2]);
730 /* Unknown type; just copy IP directly. */
731 irc_ntop(hostname, IRC_NTOP_MAX_SIZE, &user->ip);
735 /* This heuristic could be made smarter. Is it worth the effort? */
736 for (ii=cnt=0; hostname[ii]; ii++)
737 if (hostname[ii] == '.')
739 if (cnt == 0 || cnt == 1) {
740 /* only a one- or two-level domain name; leave hostname */
741 } else if (cnt == 2) {
742 for (ii=0; user->hostname[ii] != '.'; ii++) ;
743 /* Add 3 to account for the *. and \0. */
744 hostname = alloca(strlen(user->hostname+ii)+3);
745 sprintf(hostname, "*.%s", user->hostname+ii+1);
747 for (cnt=3, ii--; cnt; ii--)
748 if (user->hostname[ii] == '.')
750 /* The loop above will overshoot the dot one character;
751 we skip forward two (the one character and the dot)
752 when printing, so we only add one for the \0. */
753 hostname = alloca(strlen(user->hostname+ii)+1);
754 sprintf(hostname, "*.%s", user->hostname+ii+2);
758 len = strlen(ident) + strlen(hostname) + 2;
760 len += strlen(nickname) + 1;
762 sprintf(mask, "%s!%s@%s", nickname, ident, hostname);
765 sprintf(mask, "%s@%s", ident, hostname);
771 IsChannelName(const char *name) {
776 for (ii=1; name[ii]; ++ii) {
777 if ((name[ii] > 0) && (name[ii] <= 32))
781 if (name[ii] == '\xa0')