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));
527 struct mod_chanmode *
528 mod_chanmode_dup(struct mod_chanmode *orig, unsigned int extra)
530 struct mod_chanmode *res;
531 res = mod_chanmode_alloc(orig->argc + extra);
533 res->modes_set = orig->modes_set;
534 res->modes_clear = orig->modes_clear;
535 res->argc = orig->argc;
536 memcpy(res->args, orig->args, orig->argc*sizeof(orig->args[0]));
542 mod_chanmode_apply(struct userNode *who, struct chanNode *channel, struct mod_chanmode *change)
547 channel->modes = (channel->modes & ~change->modes_clear) | change->modes_set;
548 if (change->modes_set & MODE_LIMIT)
549 channel->limit = change->new_limit;
550 if (change->modes_set & MODE_KEY)
551 strcpy(channel->key, change->new_key);
552 for (ii = 0; ii < change->argc; ++ii) {
553 switch (change->args[ii].mode) {
555 /* If any existing ban is a subset of the new ban,
556 * silently remove it. The new ban is not allowed
557 * to be more specific than an existing ban.
559 for (jj=0; jj<channel->banlist.used; ++jj) {
560 if (match_ircglobs(change->args[ii].hostmask, channel->banlist.list[jj]->ban)) {
561 banList_remove(&channel->banlist, channel->banlist.list[jj]);
562 free(channel->banlist.list[jj]);
566 bn = calloc(1, sizeof(*bn));
567 safestrncpy(bn->ban, change->args[ii].hostmask, sizeof(bn->ban));
568 safestrncpy(bn->who, who->nick, sizeof(bn->who));
570 banList_append(&channel->banlist, bn);
572 case MODE_REMOVE|MODE_BAN:
573 for (jj=0; jj<channel->banlist.used; ++jj) {
574 if (strcmp(channel->banlist.list[jj]->ban, change->args[ii].hostmask))
576 free(channel->banlist.list[jj]);
577 banList_remove(&channel->banlist, channel->banlist.list[jj]);
582 assert((change->args[ii].mode & (MODE_REMOVE|MODE_CHANOP|MODE_VOICE)) != 0);
583 if (change->args[ii].mode & MODE_REMOVE)
584 change->args[ii].member->modes &= ~change->args[ii].mode;
586 change->args[ii].member->modes |= change->args[ii].mode;
593 mod_chanmode_free(struct mod_chanmode *change)
599 mod_chanmode(struct userNode *who, struct chanNode *channel, char **modes, unsigned int argc, unsigned int flags)
601 struct mod_chanmode *change;
604 if (!modes || !modes[0])
606 if (!(change = mod_chanmode_parse(channel, modes, argc, flags)))
608 if (flags & MC_ANNOUNCE)
609 mod_chanmode_announce(who, channel, change);
611 mod_chanmode_apply(who, channel, change);
612 if (flags & MC_NOTIFY)
613 for (ii = 0; ii < mcf_used; ++ii)
614 mcf_list[ii](channel, who, change);
615 mod_chanmode_free(change);
620 irc_make_chanmode(struct chanNode *chan, char *out) {
621 struct mod_chanmode change;
622 change.modes_set = chan->modes;
623 change.modes_clear = change.argc = 0;
624 change.new_limit = chan->limit;
625 safestrncpy(change.new_key, chan->key, sizeof(change.new_key));
626 return strlen(mod_chanmode_format(&change, out));
630 generate_hostmask(struct userNode *user, int options)
632 char *nickname, *ident, *hostname;
636 /* figure out string parts */
637 if (options & GENMASK_OMITNICK)
639 else if (options & GENMASK_USENICK)
640 nickname = user->nick;
643 if (options & GENMASK_STRICT_IDENT)
645 else if (options & GENMASK_ANY_IDENT)
648 ident = alloca(strlen(user->ident)+2);
650 strcpy(ident+1, user->ident + ((*user->ident == '~')?1:0));
652 hostname = user->hostname;
653 if (IsHiddenHost(user) && user->handle_info && hidden_host_suffix && !(options & GENMASK_NO_HIDING)) {
654 hostname = alloca(strlen(user->handle_info->handle) + strlen(hidden_host_suffix) + 2);
655 sprintf(hostname, "%s.%s", user->handle_info->handle, hidden_host_suffix);
656 } else if (options & GENMASK_STRICT_HOST) {
657 if (options & GENMASK_BYIP)
658 hostname = inet_ntoa(user->ip);
659 } else if ((options & GENMASK_BYIP) || !hostname[strspn(hostname, "0123456789.")]) {
660 /* Should generate an IP-based hostmask. By popular acclaim, a /16
661 * hostmask is used by default. */
662 unsigned masked_ip, mask, masklen;
664 mask = ~0 << masklen;
665 masked_ip = ntohl(user->ip.s_addr) & mask;
666 hostname = alloca(32);
667 if (options & GENMASK_SRVXMASK) {
668 sprintf(hostname, "%d.%d.%d.%d/%d", (masked_ip>>24)&0xFF, (masked_ip>>16)&0xFF, (masked_ip>>8)&0xFF, masked_ip&0xFF, masklen);
671 for (ii=0; ii<4; ii++) {
673 ofs += sprintf(hostname+ofs, "%d.", (masked_ip>>24)&0xFF);
677 ofs += sprintf(hostname+ofs, "*.");
680 /* Truncate the last . */
685 /* This heuristic could be made smarter. Is it worth the effort? */
686 for (ii=cnt=0; hostname[ii]; ii++)
687 if (hostname[ii] == '.')
690 /* only a two-level domain name; leave hostname */
691 } else if (cnt == 2) {
692 for (ii=0; user->hostname[ii] != '.'; ii++) ;
693 /* Add 3 to account for the *. and \0. */
694 hostname = alloca(strlen(user->hostname+ii)+3);
695 sprintf(hostname, "*.%s", user->hostname+ii+1);
697 for (cnt=3, ii--; cnt; ii--)
698 if (user->hostname[ii] == '.')
700 /* The loop above will overshoot the dot one character;
701 we skip forward two (the one character and the dot)
702 when printing, so we only add one for the \0. */
703 hostname = alloca(strlen(user->hostname+ii)+1);
704 sprintf(hostname, "*.%s", user->hostname+ii+2);
708 len = strlen(ident) + strlen(hostname) + 2;
710 len += strlen(nickname) + 1;
712 sprintf(mask, "%s!%s@%s", nickname, ident, hostname);
715 sprintf(mask, "%s@%s", ident, hostname);
721 IsChannelName(const char *name) {
726 for (ii=1; name[ii]; ++ii) {
727 if ((name[ii] > 0) && (name[ii] <= 32))
731 if (name[ii] == '\xa0')