/************************************************************************ * Unreal Internet Relay Chat Daemon, src/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. */ /* -- Jto -- 03 Jun 1990 * Changed the order of defines... */ /* parse.c 2.33 1/30/94 (C) 1988 University of Oulu, Computing Center and Jarkko Oikarinen */ #include #include "struct.h" #include "common.h" #undef RAWDEBUG char backupbuf[8192]; #define MSGTAB #include "msg.h" #undef MSGTAB #include "sys.h" #include "numeric.h" #include "h.h" #include "proto.h" /* * NOTE: parse() should not be called recursively by other functions! */ static char *para[MAXPARA + 2]; static char sender[HOSTLEN + 1]; static int cancel_clients(aClient *, aClient *, char *); static void remove_unknown(aClient *, char *); /* ** Find a client (server or user) by name. ** ** *Note* ** Semantics of this function has been changed from ** the old. 'name' is now assumed to be a null terminated ** string and the search is the for server and user. */ aClient *find_client(char *name, aClient *cptr) { if (cptr == NULL || IsServer(cptr)) { aClient *acptr; if ((acptr = hash_find_id(name, NULL)) != NULL) return acptr; } return hash_find_client(name, NULL); } aClient inline *find_nickserv(char *name, aClient *cptr) { if (name) cptr = hash_find_nickserver(name, cptr); return cptr; } /* ** Find server by name. ** ** This implementation assumes that server and user names ** are unique, no user can have a server name and vice versa. ** One should maintain separate lists for users and servers, ** if this restriction is removed. ** ** *Note* ** Semantics of this function has been changed from ** the old. 'name' is now assumed to be a null terminated ** string. */ aClient inline *find_server(char *name, aClient *cptr) { if (name) { aClient *acptr; if ((acptr = find_client(name, NULL)) != NULL && (IsServer(acptr) || IsMe(acptr))) return acptr; } return NULL; } aClient inline *find_name(char *name, aClient *cptr) { aClient *c2ptr = cptr; if (!collapse(name)) return c2ptr; if ((c2ptr = hash_find_server(name, cptr))) return (c2ptr); if (!index(name, '*')) return c2ptr; list_for_each_entry(c2ptr, &client_list, client_node) { if (!IsServer(c2ptr) && !IsMe(c2ptr)) continue; if (match(name, c2ptr->name) == 0) break; if (index(c2ptr->name, '*')) if (match(c2ptr->name, name) == 0) break; } return (c2ptr ? c2ptr : cptr); } /* ** Find person by (nick)name. */ aClient *find_person(char *name, aClient *cptr) { aClient *c2ptr; c2ptr = find_client(name, cptr); if (c2ptr && IsClient(c2ptr) && c2ptr->user) return c2ptr; return NULL; } void ban_flooder(aClient *cptr) { /* place_host_ban also takes care of removing any other clients with same host/ip */ place_host_ban(cptr, BAN_ACT_ZLINE, "Flood from unknown connection", UNKNOWN_FLOOD_BANTIME); return; } /* * This routine adds fake lag if needed. */ void parse_addlag(aClient *cptr, int cmdbytes) { if (!IsServer(cptr) && !IsNoFakeLag(cptr) && #ifdef FAKELAG_CONFIGURABLE !(cptr->local->class && (cptr->local->class->options & CLASS_OPT_NOFAKELAG)) && #endif !ValidatePermissionsForPath("privacy:fakelag",cptr,NULL,NULL,NULL)) { cptr->local->since += (1 + cmdbytes/90); } } /* * parse a buffer. * * NOTE: parse() should not be called recusively by any other fucntions! */ int parse(aClient *cptr, char *buffer, char *bufend) { Hook *h; int buf_len = 0; aClient *from = cptr; char *ch, *s; int len, i, numeric = 0, paramcount; #ifdef DEBUGMODE time_t then, ticks; int retval; #endif aCommand *cmptr = NULL; for(h = Hooks[HOOKTYPE_PACKET]; h; h = h->next) { buf_len = (int)(bufend - buffer); (*(h->func.intfunc))(from, &me, &buffer, &buf_len); if(!buffer) return 0; bufend = buffer + buf_len; } Debug((DEBUG_ERROR, "Parsing: %s (from %s)", buffer, (*cptr->name ? cptr->name : "*"))); if (IsDead(cptr)) return 0; if ((cptr->local->receiveK >= UNKNOWN_FLOOD_AMOUNT) && IsUnknown(cptr)) { sendto_snomask(SNO_FLOOD, "Flood from unknown connection %s detected", cptr->local->sockhost); ban_flooder(cptr); return FLUSH_BUFFER; } /* this call is a bit obsolete? - takes up CPU */ backupbuf[0] = '\0'; strlcpy(backupbuf, buffer, sizeof(backupbuf)); #if defined(DEBUGMODE) && defined(RAWCMDLOGGING) ircd_log(LOG_ERROR, "<- %s: %s", cptr->name, backupbuf); #endif s = sender; *s = '\0'; for (ch = buffer; *ch == ' '; ch++) ; //para[0] = from->name; para[0] = (char *)0xDEADBEEF; /* helps us catch bugs :) -- 1/2 */ if (*ch == ':' || *ch == '@') { /* ** Copy the prefix to 'sender' assuming it terminates ** with SPACE (or NULL, which is an error, though). */ for (++ch, i = 0; *ch && *ch != ' '; ++ch) if (s < (sender + sizeof(sender) - 1)) *s++ = *ch; /* leave room for NULL */ *s = '\0'; /* ** Actually, only messages coming from servers can have ** the prefix--prefix silently ignored, if coming from ** a user client... ** ** ...sigh, the current release "v2.2PL1" generates also ** null prefixes, at least to NOTIFY messages (e.g. it ** puts "sptr->nickname" as prefix from server structures ** where it's null--the following will handle this case ** as "no prefix" at all --msa (": NOTICE nick ...") */ if (*sender && IsServer(cptr)) { from = find_client(sender, (aClient *)NULL); if (!from && index(sender, '@')) from = find_nickserv(sender, (aClient *)NULL); //para[0] = sender; para[0] = (char *)0xDEADBEEF; /* helps us catch bugs :) -- 2/2 */ /* Hmm! If the client corresponding to the * prefix is not found--what is the correct * action??? Now, I will ignore the message * (old IRC just let it through as if the * prefix just wasn't there...) --msa */ if (!from) { Debug((DEBUG_ERROR, "Unknown prefix (%s)(%s) from (%s)", sender, buffer, cptr->name)); ircstp->is_unpf++; remove_unknown(cptr, sender); return -1; } if (from->from != cptr) { ircstp->is_wrdi++; Debug((DEBUG_ERROR, "Message (%s) coming from (%s)", buffer, cptr->name)); return cancel_clients(cptr, from, ch); } } while (*ch == ' ') ch++; } if (*ch == '\0') { ircstp->is_empt++; Debug((DEBUG_NOTICE, "Empty message from host %s:%s", cptr->name, from->name)); if (!IsServer(cptr)) cptr->local->since++; /* 1s fake lag */ 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 paramters and thus a space after the command ** code. -avalon */ s = (char *)index(ch, ' '); /* s -> End of the command code */ len = (s) ? (s - ch) : 0; if (len == 3 && isdigit(*ch) && isdigit(*(ch + 1)) && isdigit(*(ch + 2))) { cmptr = NULL; numeric = (*ch - '0') * 100 + (*(ch + 1) - '0') * 10 + (*(ch + 2) - '0'); paramcount = MAXPARA; ircstp->is_num++; } else { int flags = 0; int bytes = bufend - ch; if (s) *s++ = '\0'; if (!IsRegistered(from)) flags |= M_UNREGISTERED; if (IsPerson(from)) flags |= M_USER; if (IsServer(from)) flags |= M_SERVER; if (IsShunned(from)) flags |= M_SHUN; if (IsVirus(from)) flags |= M_VIRUS; if (IsOper(from)) flags |= M_OPER; cmptr = find_Command(ch, IsServer(cptr) ? 1 : 0, flags); if (!cmptr) { /* ** 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 ** This error should indeed not be sent in case ** of notices -- Syzop. */ if (!IsRegistered(cptr) && stricmp(ch, "NOTICE")) { sendto_one(from, ":%s %d %s :You have not registered", me.name, ERR_NOTREGISTERED, ch); parse_addlag(cptr, bytes); return -1; } if (IsShunned(cptr)) return -1; if (buffer[0] != '\0') { if (IsPerson(from)) sendto_one(from, ":%s %d %s %s :Unknown command", me.name, ERR_UNKNOWNCOMMAND, from->name, ch); Debug((DEBUG_ERROR, "Unknown (%s) from %s", ch, get_client_name(cptr, TRUE))); parse_addlag(cptr, bytes); } ircstp->is_unco++; return (-1); } if (cmptr->flags != 0) { /* temporary until all commands are updated */ /* Logic in comparisions below is a bit complicated, see notes */ /* If you're a user, and this command does not permit users or opers, deny */ if ((flags & M_USER) && !(cmptr->flags & M_USER) && !(cmptr->flags & M_OPER)) { sendto_one(cptr, rpl_str(ERR_NOTFORUSERS), me.name, from->name, cmptr->cmd); parse_addlag(cptr, bytes); return -1; } /* If you're a server, but command doesn't want servers, deny */ if ((flags & M_SERVER) && !(cmptr->flags & M_SERVER)) return -1; } /* If you're a user, but not an operator, and this requires operators, deny */ if ((cmptr->flags & M_OPER) && (flags & M_USER) && !(flags & M_OPER)) { sendto_one(cptr, rpl_str(ERR_NOPRIVILEGES), me.name, from->name); parse_addlag(cptr, bytes); return -1; } paramcount = cmptr->parameters; cmptr->bytes += bytes; if (!(cmptr->flags & M_NOLAG)) parse_addlag(cptr, bytes); } /* ** 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; We now use functions to create commands, so we can just check this once when the command is created rather than each time the command is used -- codemastr */ 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; if (cmptr == NULL) return (do_numeric(numeric, cptr, from, i, para)); cmptr->count++; if (IsRegisteredUser(cptr) && (cmptr->flags & M_RESETIDLE)) cptr->local->last = TStime(); #ifndef DEBUGMODE if (cmptr->flags & M_ALIAS) return (*cmptr->func) (cptr, from, i, para, cmptr->cmd); else { if (!cmptr->overriders) return (*cmptr->func) (cptr, from, i, para); return (*cmptr->overridetail->func) (cmptr->overridetail, cptr, from, i, para); } #else then = clock(); if (cmptr->flags & M_ALIAS) retval = (*cmptr->func) (cptr, from, i, para, cmptr->cmd); else { if (!cmptr->overriders) retval = (*cmptr->func) (cptr, from, i, para); else retval = (*cmptr->overridetail->func) (cmptr->overridetail, cptr, from, i, para); } if (retval != FLUSH_BUFFER) { ticks = (clock() - then); if (IsServer(cptr)) cmptr->rticks += ticks; else cmptr->lticks += ticks; cptr->local->cputime += ticks; } return retval; #endif } static int cancel_clients(aClient *cptr, aClient *sptr, char *cmd) { if (IsServer(cptr) || IsServer(sptr) || IsMe(sptr)) return 0; return exit_client(cptr, cptr, &me, "Fake prefix"); } static void remove_unknown(aClient *cptr, char *sender) { if (!IsRegistered(cptr) || IsClient(cptr)) return; /* * Not from a server so don't need to worry about it. */ if (!IsServer(cptr)) return; #ifdef DEVELOP sendto_ops("Killing %s (%s)", sender, backupbuf); return; #endif /* * Do kill if it came from a server because it means there is a ghost * user on the other server which needs to be removed. -avalon */ if ((isdigit(*sender) && strlen(sender) <= SIDLEN) || index(sender, '.')) sendto_one(cptr, ":%s SQUIT %s :(Unknown prefix (%s) from %s)", me.name, sender, sender, cptr->name); else sendto_one(cptr, ":%s KILL %s :%s (%s(?) <- %s)", me.name, sender, me.name, sender, cptr->name); }