/* * IRC - Internet Relay Chat, ircd/parse.c * Copyright (C) 1990 Jarkko Oikarinen and * University of Oulu, Computing Center * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 1, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * $Id: parse.c 1674 2006-07-21 07:42:07Z sirvulcan $ */ #include "config.h" #include "parse.h" #include "client.h" #include "channel.h" #include "handlers.h" #include "hash.h" #include "ircd.h" #include "ircd_alloc.h" #include "ircd_chattr.h" #include "ircd_features.h" #include "ircd_log.h" #include "ircd_reply.h" #include "ircd_string.h" #include "msg.h" #include "numeric.h" #include "numnicks.h" #include "opercmds.h" #include "querycmds.h" #include "res.h" #include "s_bsd.h" #include "s_conf.h" #include "s_debug.h" #include "s_misc.h" #include "s_numeric.h" #include "s_user.h" #include "send.h" #include "shun.h" #include "ircd_struct.h" #include "sys.h" #include "whocmds.h" #include "whowas.h" #include #include #include /* * Message Tree stuff mostly written by orabidoo, with changes by Dianora. * Adapted to Undernet, adding token support, etc by comstud 10/06/97 * * completely rewritten June 2, 2003 - Dianora * * This has always just been a trie. Look at volume III of Knuth ACP * * * ok, you start out with an array of pointers, each one corresponds * to a letter at the current position in the command being examined. * * so roughly you have this for matching 'trie' or 'tie' * * 't' points -> [MessageTree *] 'r' -> [MessageTree *] -> 'i' * -> [MessageTree *] -> [MessageTree *] -> 'e' and matches * * 'i' -> [MessageTree *] -> 'e' and matches */ #define MAXPTRLEN 32 /* Must be a power of 2, and * larger than 26 [a-z]|[A-Z] * its used to allocate the set * of pointers at each node of the tree * There are MAXPTRLEN pointers at each node. * Obviously, there have to be more pointers * Than ASCII letters. 32 is a nice number * since there is then no need to shift * 'A'/'a' to base 0 index, at the expense * of a few never used pointers. For a small * parser like this, this is a good compromise * and does make it somewhat faster. * * - Dianora */ struct MessageTree { struct Message *msg; struct MessageTree *pointers[MAXPTRLEN]; }; static struct MessageTree msg_tree; struct Message msgtab[] = { { MSG_PRIVATE, TOK_PRIVATE, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_privmsg, ms_privmsg, mo_privmsg, m_ignore } }, { MSG_NICK, TOK_NICK, 0, MAXPARA, MFLG_SLOW | MFLG_UNREG, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_nick, m_nick, ms_nick, m_nick, m_ignore } }, { MSG_NOTICE, TOK_NOTICE, 0, MAXPARA, MFLG_SLOW | MFLG_IGNORE, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_ignore, m_notice, ms_notice, mo_notice, m_ignore } }, { MSG_WALLCHOPS, TOK_WALLCHOPS, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_wallchops, ms_wallchops, m_wallchops, m_ignore } }, { MSG_WALLHOPS, TOK_WALLHOPS, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_wallhops, ms_wallhops, m_wallhops, m_ignore } }, { MSG_WALLVOICES, TOK_WALLVOICES, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_wallvoices, ms_wallvoices, m_wallvoices, m_ignore } }, { MSG_CPRIVMSG, TOK_CPRIVMSG, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_cprivmsg, m_ignore, m_cprivmsg, m_ignore } }, { MSG_CNOTICE, TOK_CNOTICE, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_cnotice, m_ignore, m_cnotice, m_ignore } }, { MSG_JOIN, TOK_JOIN, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_join, ms_join, m_join, m_ignore } }, { MSG_SVSJOIN, TOK_SVSJOIN, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_ignore, ms_svsjoin, m_ignore, m_ignore } }, { MSG_MODE, TOK_MODE, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_mode, ms_mode, m_mode, m_ignore } }, { MSG_BURST, TOK_BURST, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_ignore, m_ignore, ms_burst, m_ignore, m_ignore } }, { MSG_CREATE, TOK_CREATE, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_ignore, m_ignore, ms_create, m_ignore, m_ignore } }, { MSG_DESTRUCT, TOK_DESTRUCT, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_ignore, m_ignore, ms_destruct, m_ignore, m_ignore } }, { MSG_QUIT, TOK_QUIT, 0, MAXPARA, MFLG_SLOW | MFLG_UNREG, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_quit, m_quit, ms_quit, m_quit, m_ignore } }, { MSG_PART, TOK_PART, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_part, ms_part, m_part, m_ignore } }, { MSG_SVSPART, TOK_SVSPART, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_ignore, ms_svspart, m_ignore, m_ignore } }, { MSG_TOPIC, TOK_TOPIC, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_topic, ms_topic, m_topic, m_ignore } }, { MSG_INVITE, TOK_INVITE, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_invite, ms_invite, m_invite, m_ignore } }, { MSG_IRCOPS, TOK_IRCOPS, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_ignore, m_ircops, m_ignore, m_ircops, m_ignore } }, { MSG_KICK, TOK_KICK, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_kick, ms_kick, m_kick, m_ignore } }, { MSG_WALLOPS, TOK_WALLOPS, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_not_oper, ms_wallops, mo_wallops, m_ignore } }, { MSG_WALLUSERS, TOK_WALLUSERS, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_not_oper, ms_wallusers, mo_wallusers, m_ignore } }, { MSG_DESYNCH, TOK_DESYNCH, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_ignore, m_ignore, ms_desynch, m_ignore, m_ignore } }, { MSG_PING, TOK_PING, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_ping, ms_ping, mo_ping, m_ignore } }, { MSG_PONG, TOK_PONG, 0, MAXPARA, MFLG_SLOW | MFLG_UNREG, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { mr_pong, m_pong, ms_pong, m_pong, m_ignore } }, { MSG_ERROR, TOK_ERROR, 0, MAXPARA, MFLG_SLOW | MFLG_UNREG, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { mr_error, m_ignore, ms_error, m_ignore, m_ignore } }, { MSG_KILL, TOK_KILL, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_not_oper, ms_kill, mo_kill, m_ignore } }, { MSG_USER, TOK_USER, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_user, m_registered, m_ignore, m_registered, m_ignore } }, { MSG_AWAY, TOK_AWAY, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_away, ms_away, m_away, m_ignore } }, { MSG_ISON, TOK_ISON, 0, 1, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_ison, m_ignore, m_ison, m_ignore } }, { MSG_SERVER, TOK_SERVER, 0, MAXPARA, MFLG_SLOW | MFLG_UNREG, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { mr_server, m_registered, ms_server, m_registered, m_ignore } }, { MSG_SQUIT, TOK_SQUIT, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_not_oper, ms_squit, mo_squit, m_ignore } }, { MSG_WHOIS, TOK_WHOIS, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_whois, ms_whois, m_whois, m_ignore } }, { MSG_WHO, TOK_WHO, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_who, m_ignore, m_who, m_ignore } }, { MSG_WHOWAS, TOK_WHOWAS, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_whowas, m_whowas, m_whowas, m_ignore } }, { MSG_LIST, TOK_LIST, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_list, m_ignore, m_list, m_ignore } }, { MSG_NAMES, TOK_NAMES, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_names, ms_names, m_names, m_ignore } }, { MSG_USERHOST, TOK_USERHOST, 0, 1, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_userhost, m_ignore, m_userhost, m_ignore } }, { MSG_USERIP, TOK_USERIP, 0, 1, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_userip, m_ignore, m_userip, m_ignore } }, { MSG_TRACE, TOK_TRACE, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_trace, ms_trace, mo_trace, m_ignore } }, { MSG_PASS, TOK_PASS, 0, MAXPARA, MFLG_SLOW | MFLG_UNREG, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { mr_pass, m_registered, m_ignore, m_registered, m_ignore } }, { MSG_LUSERS, TOK_LUSERS, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_lusers, ms_lusers, m_lusers, m_ignore } }, { MSG_TIME, TOK_TIME, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_time, m_time, m_time, m_ignore } }, { MSG_SETTIME, TOK_SETTIME, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_ignore, ms_settime, mo_settime, m_ignore } }, { MSG_RPING, TOK_RPING, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_not_oper, ms_rping, mo_rping, m_ignore } }, { MSG_RPONG, TOK_RPONG, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_ignore, ms_rpong, m_ignore, m_ignore } }, { MSG_OPER, TOK_OPER, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_oper, ms_oper, mo_oper, m_ignore } }, { MSG_CONNECT, TOK_CONNECT, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_not_oper, ms_connect, mo_connect, m_ignore } }, { MSG_MAP, TOK_MAP, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_map, m_ignore, m_map, m_ignore } }, { MSG_VERSION, TOK_VERSION, 0, MAXPARA, MFLG_SLOW | MFLG_UNREG, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_version, m_version, ms_version, mo_version, m_ignore } }, { MSG_STATS, TOK_STATS, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_stats, m_stats, m_stats, m_ignore } }, { MSG_LINKS, TOK_LINKS, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_links, ms_links, m_links, m_ignore } }, { MSG_ADMIN, TOK_ADMIN, 0, MAXPARA, MFLG_SLOW | MFLG_UNREG, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_admin, m_admin, ms_admin, mo_admin, m_ignore } }, { MSG_HELP, TOK_HELP, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_help, m_ignore, m_help, m_ignore } }, { MSG_INFO, TOK_INFO, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_info, ms_info, mo_info, m_ignore } }, { MSG_CREDITS, TOK_CREDITS, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_info, ms_info, mo_info, m_ignore } }, { MSG_COPYRIGHT, TOK_COPYRIGHT, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_copyright, ms_copyright, m_copyright, m_ignore } }, { MSG_ISNEF, TOK_ISNEF, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_isnef, m_ignore, m_isnef, m_ignore } }, { MSG_MOTD, TOK_MOTD, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_motd, m_motd, m_motd, m_ignore } }, { MSG_CLOSE, TOK_CLOSE, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_not_oper, m_ignore, mo_close, m_ignore } }, { MSG_SILENCE, TOK_SILENCE, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_silence, ms_silence, m_silence, m_ignore } }, { MSG_EXEMPT, TOK_EXEMPT, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_not_oper, ms_exempt, mo_exempt, m_ignore } }, { MSG_GLINE, TOK_GLINE, 0, MAXPARA, 0, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_gline, ms_gline, mo_gline, m_ignore } }, { MSG_SHUN, TOK_SHUN, 0, MAXPARA, 0, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_shun, ms_shun, mo_shun, m_ignore } }, { MSG_JUPE, TOK_JUPE, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_not_oper, ms_jupe, mo_jupe, m_ignore } }, { MSG_OPMODE, TOK_OPMODE, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_not_oper, ms_opmode, mo_opmode, m_ignore } }, { MSG_CLEARMODE, TOK_CLEARMODE, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_not_oper, ms_clearmode, mo_clearmode, m_ignore } }, { MSG_UPING, TOK_UPING, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_not_oper, ms_uping, mo_uping, m_ignore } }, { MSG_END_OF_BURST, TOK_END_OF_BURST, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_ignore, m_ignore, ms_end_of_burst, m_ignore, m_ignore } }, { MSG_END_OF_BURST_ACK, TOK_END_OF_BURST_ACK, 0, MAXPARA, 1, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_ignore, m_ignore, ms_end_of_burst_ack, m_ignore, m_ignore } }, { MSG_HASH, TOK_HASH, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_hash, m_hash, m_hash, m_ignore } }, { MSG_DNS, TOK_DNS, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_ignore, m_ignore, m_dns, m_ignore } }, { MSG_REHASH, TOK_REHASH, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_not_oper, ms_rehash, mo_rehash, m_ignore } }, { MSG_RESTART, TOK_RESTART, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_not_oper, m_ignore, mo_restart, m_ignore } }, { MSG_DIE, TOK_DIE, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_not_oper, m_ignore, mo_die, m_ignore } }, { MSG_PROTO, TOK_PROTO, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_proto, m_proto, m_proto, m_proto, m_ignore } }, { MSG_SET, TOK_SET, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_not_oper, ms_set, mo_set, m_ignore } }, { MSG_RESET, TOK_RESET, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_not_oper, m_ignore, mo_reset, m_ignore } }, { MSG_GET, TOK_GET, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_not_oper, m_ignore, mo_get, m_ignore } }, { MSG_PRIVS, TOK_PRIVS, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_not_oper, ms_privs, mo_privs, m_ignore } }, { MSG_ACCOUNT, TOK_ACCOUNT, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_ignore, m_ignore, ms_account, m_ignore, m_ignore } }, { MSG_MKPASSWD, TOK_MKPASSWD, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_ignore, m_ignore, m_ignore, mo_mkpasswd, m_ignore } }, { MSG_ASLL, TOK_ASLL, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_ignore, m_not_oper, ms_asll, mo_asll, m_ignore } }, { MSG_SETHOST, TOK_SETHOST, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_sethost, m_ignore, m_sethost, m_ignore } }, { MSG_FAKEHOST, TOK_FAKEHOST, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_ignore, m_ignore, ms_fakehost, m_ignore, m_ignore } }, { MSG_CHECK, TOK_CHECK, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_not_oper, mo_check, mo_check, m_ignore } }, { MSG_RULES, TOK_RULES, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_rules, ms_rules, m_rules, m_ignore } }, { MSG_OPERMOTD, TOK_OPERMOTD, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_not_oper, ms_opermotd, m_opermotd, m_ignore } }, { MSG_SVSNICK, TOK_SVSNICK, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_ignore, m_ignore, ms_svsnick, m_ignore, m_ignore } }, { MSG_SVSINFO, TOK_SVSINFO, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_ignore, m_ignore, ms_svsinfo, m_ignore, m_ignore } }, { MSG_SVSNOOP, TOK_SVSNOOP, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_ignore, ms_svsnoop, m_ignore, m_ignore } }, { MSG_SWHOIS, TOK_SWHOIS, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_ignore, ms_swhois, m_ignore, m_ignore } }, { MSG_MARK, TOK_MARK, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_unregistered, m_ignore, ms_mark, m_ignore, m_ignore } }, /* This command is an alias for QUIT during the unregistered part of * of the server. This is because someone jumping via a broken web * proxy will send a 'POST' as their first command - which we will * obviously disconnect them immediately for, stopping people abusing * open gateways */ { MSG_POST, TOK_POST, 0, MAXPARA, MFLG_SLOW, 0, /* UNREG, CLIENT, SERVER, OPER, SERVICE */ { m_quit, m_ignore, m_ignore, m_ignore, m_ignore } }, { 0 } }; static char *para[MAXPARA + 2]; /* leave room for prefix and null */ /* * add_msg_element * * inputs - Pointer to current piece of message tree * - Pointer to struct Message to add at final token position * - Pointer to current portion of cmd or token to add * output - NONE * side effects - recursively build the Message Tree ;-) */ void add_msg_element(struct MessageTree *mtree_p, struct Message *msg_p, char *cmd) { struct MessageTree *ntree_p; if (*cmd == '\0') { mtree_p->msg = msg_p; return; } if ((ntree_p = mtree_p->pointers[*cmd & (MAXPTRLEN-1)]) != NULL) { add_msg_element(ntree_p, msg_p, cmd+1); } else { ntree_p = (struct MessageTree *)MyCalloc(sizeof(struct MessageTree), 1); mtree_p->pointers[*cmd & (MAXPTRLEN-1)] = ntree_p; add_msg_element(ntree_p, msg_p, cmd+1); } } #if ircu_unused /* This is unused in ircu, trivial to do, but left here for later * use if desired. * * - Dianora */ /* * del_msg_element * * inputs - * - * - * output - NONE * side effects - recursively deletes a token from the Message Tree ;-) */ void del_msg_element(struct MessageTree *mtree_p, char *cmd) { struct MessageTree *ntree_p; if (*cmd == '\0') return; if ((ntree_p = mtree_p->pointers[*cmd & (MAXPTRLEN-1)]) != NULL) { del_msg_element(ntree_p, cmd+1); MyFree(ntree_p); mtree_p->pointers[*cmd & (MAXPTRLEN-1)] = NULL; } } #endif /* * initmsgtree() * * inputs - none * output - none * side effect - zero the msg_tree, recursively populate it */ void initmsgtree(void) { int i; memset(&msg_tree, 0, sizeof(msg_tree)); for (i = 0; msgtab[i].cmd != NULL ; i++) { add_msg_element(&msg_tree, &msgtab[i], msgtab[i].cmd); add_msg_element(&msg_tree, &msgtab[i], msgtab[i].tok); } } /* * msg_tree_parse * * inputs - pointer to command/token * - pointer to MessageTree root * output - found Message * for this token/command or NULL if not found * side effects - * Generic tree parser which works for both commands and tokens. * Optimized by Run. * Re-written by Dianora (db) (tail recursive) * */ static struct Message * msg_tree_parse(char *cmd, struct MessageTree *root) { struct MessageTree *mtree; for (mtree = root; mtree; mtree = mtree->pointers[(*cmd++) & (MAXPTRLEN-1)]) { if (*cmd == '\0' && mtree->msg) return mtree->msg; else if (!IsAlpha(*cmd)) return NULL; } return NULL; } /* * parse a buffer. * * NOTE: parse_*() should not be called recusively by any other functions! */ int parse_client(struct Client *cptr, char *buffer, char *bufend) { struct Client* from = cptr; char* ch; char* s; int i; int paramcount; int noprefix = 0; struct Message* mptr; MessageHandler handler = 0; struct svcline* svc = NULL; Debug((DEBUG_DEBUG, "Client Parsing: %s", buffer)); if (IsDead(cptr)) return 0; para[0] = cli_name(from); for (ch = buffer; *ch == ' '; ch++); /* Eat leading spaces */ if (*ch == ':') /* Is any client doing this ? */ { for (++ch; *ch && *ch != ' '; ++ch) ; /* Ignore sender prefix from client */ while (*ch == ' ') ch++; /* Advance to command */ } else noprefix = 1; if (*ch == '\0') { ServerStats->is_empt++; Debug((DEBUG_NOTICE, "Empty message from host %s:%s", cli_name(cptr), cli_name(from))); return (-1); } if ((s = strchr(ch, ' '))) *s++ = '\0'; expire_shuns(); if ((strcasecmp(ch, "NICK")) && /* rejection handled in m_nick.c */ (strcasecmp(ch, "SERVER")) && /* expiring shuns with this or matching * just causes major issues, we dont need * to check shuns with SERVER. */ (strcasecmp(ch, "USER")) && /* avoid issues, doesnt matter if we prase while shunned */ (strcasecmp(ch, "PASS")) && /* LOC */ (strcasecmp(ch, "ADMIN")) && /* get admin info for help*/ (strcasecmp(ch, "PART")) && /* obvious */ (strcasecmp(ch, "QUIT")) && /* obvious */ (strcasecmp(ch, "PONG")) /* survive */ ) { if (shun_lookup(cptr, 0)) { return 0; } } /* * If there is a service, we simply force msg_tree_parse() to * find the command for PRIVMSG. all of the / commands use * PRIVMSG, soo.... --akl */ if ((svc = (struct svcline *)find_svc(ch))) { mptr = msg_tree_parse(MSG_PRIVATE, &msg_tree); } else if ((mptr = msg_tree_parse(ch, &msg_tree)) == NULL) { /* * Note: Give error message *only* to recognized * persons. It's a nightmare situation to have * two programs sending "Unknown command"'s or * equivalent to each other at full blast.... * If it has got to person state, it at least * seems to be well behaving. Perhaps this message * should never be generated, though... --msa * Hm, when is the buffer empty -- if a command * code has been found ?? -Armin */ if (buffer[0] != '\0') { if (IsUser(from)) send_reply(from, ERR_UNKNOWNCOMMAND, ch); Debug((DEBUG_ERROR, "Unknown (%s) from %s", ch, get_client_name(cptr, HIDE_IP))); } ServerStats->is_unco++; return (-1); } paramcount = mptr->parameters; i = bufend - ((s) ? s : ch); mptr->bytes += i; if ((mptr->flags & MFLG_SLOW)) { if (IsOper(cptr)) cli_since(cptr) += 1; else cli_since(cptr) += (2 + i / 120); } /* * Allow only 1 msg per 2 seconds * (on average) to prevent dumping. * to keep the response rate up, * bursts of up to 5 msgs are allowed * -SRB */ /* * Must the following loop really be so devious? On * surface it splits the message to parameters from * blank spaces. But, if paramcount has been reached, * the rest of the message goes into this last parameter * (about same effect as ":" has...) --msa */ /* Note initially true: s==NULL || *(s-1) == '\0' !! */ i = 0; if (s) { if (paramcount > MAXPARA) paramcount = MAXPARA; for (;;) { /* * Never "FRANCE " again!! ;-) Clean * out *all* blanks.. --msa */ while (*s == ' ') *s++ = '\0'; if (*s == '\0') break; if (*s == ':') { /* * The rest is single parameter--can * include blanks also. */ para[++i] = s + 1; break; } para[++i] = s; if (i >= paramcount) break; for (; *s != ' ' && *s; s++); } } para[++i] = NULL; ++mptr->count; handler = mptr->handlers[cli_handler(cptr)]; assert(0 != handler); if (!feature_bool(FEAT_IDLE_FROM_MSG) && IsUser(cptr) && handler != m_ping && handler != m_ignore) cli_user(from)->last = CurrentTime; if (svc != NULL) return lsc(cptr, svc->target, svc->prepend, svc->cmd, i, para); return (*handler) (cptr, from, i, para); } int parse_server(struct Client *cptr, char *buffer, char *bufend) { struct Client* from = cptr; char* ch = buffer; char* s; int len; int i; int numeric = 0; int paramcount; struct Message* mptr; Debug((DEBUG_DEBUG, "Server Parsing: %s", buffer)); if (IsDead(cptr)) return 0; para[0] = cli_name(from); /* * A server ALWAYS sends a prefix. When it starts with a ':' it's the * protocol 9 prefix: a nick or a server name. Otherwise it's a numeric * nick or server */ if (*ch == ':') { /* Let para[0] point to the name of the sender */ para[0] = ch + 1; if (!(ch = strchr(ch, ' '))) return -1; *ch++ = '\0'; /* And let `from' point to its client structure, opps.. a server is _also_ a client --Nem */ from = FindClient(para[0]); /* * If the client corresponding to the * prefix is not found. We must ignore it, * it is simply a lagged message travelling * upstream a SQUIT that removed the client * --Run */ if (from == NULL) { Debug((DEBUG_NOTICE, "Unknown prefix (%s)(%s) from (%s)", para[0], buffer, cli_name(cptr))); ++ServerStats->is_unpf; while (*ch == ' ') ch++; /* * However, the only thing that MUST be * allowed to travel upstream against an * squit, is an SQUIT itself (the timestamp * protects us from being used wrong) */ if (ch[1] == 'Q') { para[0] = cli_name(cptr); from = cptr; } else return 0; } else if (cli_from(from) != cptr) { ++ServerStats->is_wrdi; Debug((DEBUG_NOTICE, "Fake direction: Message (%s) coming from (%s)", buffer, cli_name(cptr))); return 0; } } else { char numeric_prefix[6]; int i; for (i = 0; i < 5; ++i) { if ('\0' == ch[i] || ' ' == (numeric_prefix[i] = ch[i])) { break; } } numeric_prefix[i] = '\0'; /* * We got a numeric nick as prefix * 1 or 2 character prefixes are from servers * 3 or 5 chars are from clients */ if (0 == i) { log_write(LS_SYSTEM, L_WARNING, 0, "Missing Prefix: (%s) %s", cli_name(cptr), buffer); protocol_violation(cptr,"Missing Prefix"); from = cptr; } else if (' ' == ch[1] || ' ' == ch[2]) from = FindNServer(numeric_prefix); else from = findNUser(numeric_prefix); do { ++ch; } while (*ch != ' ' && *ch); /* * If the client corresponding to the * prefix is not found. We must ignore it, * it is simply a lagged message travelling * upstream a SQUIT that removed the client * --Run * There turned out to be other reasons that * a prefix is unknown, needing an upstream * KILL. Also, next to an SQUIT we better * allow a KILL to pass too. * --Run */ if (from == NULL) { ServerStats->is_unpf++; while (*ch == ' ') ch++; if (*ch == 'N' && (ch[1] == ' ' || ch[1] == 'I')) /* Only sent a KILL for a nick change */ { struct Client *server; /* Kill the unknown numeric prefix upstream if * it's server still exists: */ if ((server = FindNServer(numeric_prefix)) && cli_from(server) == cptr) sendcmdto_one(&me, CMD_KILL, cptr, "%s :%s (Unknown numeric nick)", numeric_prefix, cli_name(&me)); } /* * Things that must be allowed to travel * upstream against an squit: */ if (ch[1] == 'Q' || (*ch == 'D' && ch[1] == ' ') || (*ch == 'K' && ch[2] == 'L')) from = cptr; else return 0; } /* Let para[0] point to the name of the sender */ para[0] = cli_name(from); if (cli_from(from) != cptr) { ServerStats->is_wrdi++; Debug((DEBUG_NOTICE, "Fake direction: Message (%s) coming from (%s)", buffer, cli_name(cptr))); return 0; } } while (*ch == ' ') ch++; if (*ch == '\0') { ServerStats->is_empt++; Debug((DEBUG_NOTICE, "Empty message from host %s:%s", cli_name(cptr), cli_name(from))); return (-1); } /* * Extract the command code from the packet. Point s to the end * of the command code and calculate the length using pointer * arithmetic. Note: only need length for numerics and *all* * numerics must have parameters and thus a space after the command * code. -avalon */ s = strchr(ch, ' '); /* s -> End of the command code */ len = (s) ? (s - ch) : 0; if (len == 3 && IsDigit(*ch)) { numeric = (*ch - '0') * 100 + (*(ch + 1) - '0') * 10 + (*(ch + 2) - '0'); paramcount = 2; /* destination, and the rest of it */ ServerStats->is_num++; mptr = NULL; /* Init. to avoid stupid compiler warning :/ */ } else { if (s) *s++ = '\0'; /* Version Receive Send * 2.9 Long Long * 2.10.0 Tkn/Long Long * 2.10.10 Tkn/Long Tkn * 2.10.20 Tkn Tkn * * Clients/unreg servers always receive/ * send long commands -record * * And for the record, this trie parser really does not care. - Dianora */ mptr = msg_tree_parse(ch, &msg_tree); if (mptr == NULL) { /* * Note: Give error message *only* to recognized * persons. It's a nightmare situation to have * two programs sending "Unknown command"'s or * equivalent to each other at full blast.... * If it has got to person state, it at least * seems to be well behaving. Perhaps this message * should never be generated, though... --msa * Hm, when is the buffer empty -- if a command * code has been found ?? -Armin */ #ifdef DEBUGMODE if (buffer[0] != '\0') { Debug((DEBUG_ERROR, "Unknown (%s) from %s", ch, get_client_name(cptr, HIDE_IP))); } #endif ServerStats->is_unco++; return (-1); } paramcount = mptr->parameters; i = bufend - ((s) ? s : ch); mptr->bytes += i; } /* * Must the following loop really be so devious? On * surface it splits the message to parameters from * blank spaces. But, if paramcount has been reached, * the rest of the message goes into this last parameter * (about same effect as ":" has...) --msa */ /* Note initially true: s==NULL || *(s-1) == '\0' !! */ i = 0; if (s) { if (paramcount > MAXPARA) paramcount = MAXPARA; for (;;) { /* * Never "FRANCE " again!! ;-) Clean * out *all* blanks.. --msa */ while (*s == ' ') *s++ = '\0'; if (*s == '\0') break; if (*s == ':') { /* * The rest is single parameter--can * include blanks also. */ if (numeric) para[++i] = s; /* preserve the colon to make do_numeric happy */ else para[++i] = s + 1; break; } para[++i] = s; if (i >= paramcount) break; for (; *s != ' ' && *s; s++); } } para[++i] = NULL; if (numeric) return (do_numeric(numeric, (*buffer != ':'), cptr, from, i, para)); mptr->count++; return (*mptr->handlers[cli_handler(cptr)]) (cptr, from, i, para); }