/************************************************************************ * IRC - Internet Relay Chat, common/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. */ #ifndef lint static char rcsid[] = "@(#)$Id: parse.c,v 1.25 1999/04/14 17:29:36 kalt Exp $"; #endif #include "os.h" #ifndef CLIENT_COMPILE # include "s_defines.h" #else # include "c_defines.h" #endif #define PARSE_C #ifndef CLIENT_COMPILE # include "s_externs.h" #else # include "c_externs.h" #endif #undef PARSE_C struct Message msgtab[] = { { MSG_PRIVATE, m_private, MAXPARA, MSG_LAG|MSG_REGU, 0, 0, 0L}, #ifndef CLIENT_COMPILE { MSG_NJOIN, m_njoin, MAXPARA, MSG_LAG|MSG_NOU, 0, 0, 0L}, #endif { MSG_JOIN, m_join, MAXPARA, MSG_LAG|MSG_REGU, 0, 0, 0L}, { MSG_MODE, m_mode, MAXPARA, MSG_LAG|MSG_REG, 0, 0, 0L}, { MSG_NICK, m_nick, MAXPARA, MSG_LAG, 0, 0, 0L}, { MSG_PART, m_part, MAXPARA, MSG_LAG|MSG_REGU, 0, 0, 0L}, { MSG_QUIT, m_quit, MAXPARA, MSG_LAG, 0, 0, 0L}, { MSG_NOTICE, m_notice, MAXPARA, MSG_LAG|MSG_REG, 0, 0, 0L}, { MSG_KICK, m_kick, MAXPARA, MSG_LAG|MSG_REGU, 0, 0, 0L}, { MSG_SERVER, m_server, MAXPARA, MSG_LAG|MSG_NOU, 0, 0, 0L}, #ifndef CLIENT_COMPILE { MSG_TRACE, m_trace, MAXPARA, MSG_LAG|MSG_REG, 0, 0, 0L}, #endif { MSG_TOPIC, m_topic, MAXPARA, MSG_LAG|MSG_REGU, 0, 0, 0L}, { MSG_INVITE, m_invite, MAXPARA, MSG_LAG|MSG_REGU, 0, 0, 0L}, { MSG_WALLOPS, m_wallops, MAXPARA, MSG_LAG|MSG_REG|MSG_NOU, 0, 0, 0L}, { MSG_PING, m_ping, MAXPARA, MSG_LAG|MSG_REG, 0, 0, 0L}, { MSG_PONG, m_pong, MAXPARA, MSG_LAG|MSG_REG, 0, 0, 0L}, { MSG_ERROR, m_error, MAXPARA, MSG_LAG|MSG_REG|MSG_NOU, 0, 0, 0L}, #ifdef OPER_KILL { MSG_KILL, m_kill, MAXPARA, MSG_LAG|MSG_REG|MSG_OP|MSG_LOP, 0,0, 0L}, #else { MSG_KILL, m_kill, MAXPARA, MSG_LAG|MSG_REG|MSG_NOU, 0, 0, 0L}, #endif #ifndef CLIENT_COMPILE { MSG_USER, m_user, MAXPARA, MSG_LAG|MSG_NOU, 0, 0, 0L}, { MSG_AWAY, m_away, MAXPARA, MSG_LAG|MSG_REGU, 0, 0, 0L}, { MSG_UMODE, m_umode, MAXPARA, MSG_LAG|MSG_REGU, 0, 0, 0L}, { MSG_ISON, m_ison, 1, MSG_LAG|MSG_REG, 0, 0, 0L}, { MSG_SQUIT, m_squit, MAXPARA, MSG_LAG|MSG_REG|MSG_OP|MSG_LOP, 0,0, 0L}, { MSG_WHOIS, m_whois, MAXPARA, MSG_LAG|MSG_REG, 0, 0, 0L}, { MSG_WHO, m_who, MAXPARA, MSG_LAG|MSG_REG, 0, 0, 0L}, { MSG_WHOWAS, m_whowas, MAXPARA, MSG_LAG|MSG_REG, 0, 0, 0L}, { MSG_LIST, m_list, MAXPARA, MSG_LAG|MSG_REG, 0, 0, 0L}, { MSG_NAMES, m_names, MAXPARA, MSG_LAG|MSG_REGU, 0, 0, 0L}, { MSG_USERHOST,m_userhost, MAXPARA, MSG_LAG|MSG_REG, 0, 0, 0L}, { MSG_PASS, m_pass, MAXPARA, MSG_LAG|MSG_NOU, 0, 0, 0L}, { MSG_LUSERS, m_lusers, MAXPARA, MSG_LAG|MSG_REG, 0, 0, 0L}, { MSG_TIME, m_time, MAXPARA, MSG_LAG|MSG_REGU, 0, 0, 0L}, { MSG_OPER, m_oper, MAXPARA, MSG_LAG|MSG_REGU, 0, 0, 0L}, { MSG_CONNECT, m_connect, MAXPARA, MSG_LAG|MSG_REGU|MSG_OP|MSG_LOP, 0, 0, 0L}, { MSG_VERSION, m_version, MAXPARA, MSG_LAG|MSG_REGU, 0, 0, 0L}, { MSG_STATS, m_stats, MAXPARA, MSG_LAG|MSG_REGU, 0, 0, 0L}, { MSG_LINKS, m_links, MAXPARA, MSG_LAG|MSG_REG, 0, 0, 0L}, { MSG_ADMIN, m_admin, MAXPARA, MSG_LAG, 0, 0, 0L}, { MSG_USERS, m_users, MAXPARA, MSG_LAG|MSG_REGU, 0, 0, 0L}, { MSG_SUMMON, m_summon, MAXPARA, MSG_LAG|MSG_REGU, 0, 0, 0L}, { MSG_HELP, m_help, MAXPARA, MSG_LAG|MSG_REGU, 0, 0, 0L}, { MSG_INFO, m_info, MAXPARA, MSG_LAG|MSG_REGU, 0, 0, 0L}, { MSG_MOTD, m_motd, MAXPARA, MSG_LAG|MSG_REGU, 0, 0, 0L}, { MSG_CLOSE, m_close, MAXPARA, MSG_LAG|MSG_REGU|MSG_OP, 0, 0, 0L}, { MSG_RECONECT,m_reconnect,MAXPARA, MSG_LAG|MSG_NOU, 0, 0, 0L}, { MSG_SERVICE, m_service, MAXPARA, MSG_LAG|MSG_NOU, 0, 0, 0L}, #ifdef USE_SERVICES { MSG_SERVSET, m_servset, MAXPARA, MSG_LAG|MSG_SVC, 0, 0, 0L}, #endif { MSG_SQUERY, m_squery, MAXPARA, MSG_LAG|MSG_REGU, 0, 0, 0L}, { MSG_SERVLIST,m_servlist, MAXPARA, MSG_LAG|MSG_REG, 0, 0, 0L}, { MSG_HASH, m_hash, MAXPARA, MSG_LAG|MSG_REG, 0, 0, 0L}, { MSG_DNS, m_dns, MAXPARA, MSG_LAG|MSG_REG, 0, 0, 0L}, #ifdef OPER_REHASH { MSG_REHASH, m_rehash, MAXPARA, MSG_REGU|MSG_OP # ifdef LOCOP_REHASH |MSG_LOP # endif , 0, 0, 0L}, #endif #ifdef OPER_RESTART { MSG_RESTART, m_restart, MAXPARA, MSG_REGU|MSG_OP # ifdef LOCOP_RESTART |MSG_LOP # endif , 0, 0, 0L}, #endif #ifdef OPER_DIE { MSG_DIE, m_die, MAXPARA, MSG_REGU|MSG_OP # ifdef LOCOP_DIE |MSG_LOP # endif , 0, 0, 0L}, #endif #endif /* !CLIENT_COMPILE */ { (char *) 0, (int (*)()) 0, 0, 0, 0, 0, 0L} }; /* * NOTE: parse() should not be called recursively by other functions! */ static char *para[MAXPARA+1]; #ifdef CLIENT_COMPILE static char sender[NICKLEN+USERLEN+HOSTLEN+3]; char userhost[USERLEN+HOSTLEN+2]; #define timeofday time(NULL) #else static char sender[HOSTLEN+1]; static int cancel_clients __P((aClient *, aClient *, char *)); static void remove_unknown __P((aClient *, char *)); #endif /* ** 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. */ #ifndef CLIENT_COMPILE aClient *find_client(name, cptr) char *name; Reg aClient *cptr; { aClient *acptr = cptr; if (name && *name) acptr = hash_find_client(name, cptr); return acptr; } aClient *find_service(name, cptr) char *name; Reg aClient *cptr; { aClient *acptr = cptr; if (index(name, '@')) acptr = hash_find_client(name, cptr); return acptr; } #else /* CLIENT_COMPILE */ aClient *find_client(name, cptr) char *name; aClient *cptr; { Reg aClient *c2ptr = cptr; if (!name || !*name) return c2ptr; for (c2ptr = client; c2ptr; c2ptr = c2ptr->next) if (mycmp(name, c2ptr->name) == 0) return c2ptr; return cptr; } #endif /* CLIENT_COMPILE */ /* ** Find a user@host (server or user). ** ** *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_userhost(user, host, cptr, count) char *user, *host; aClient *cptr; int *count; { Reg aClient *c2ptr; Reg aClient *res = cptr; *count = 0; if (user) for (c2ptr = client; c2ptr; c2ptr = c2ptr->next) { if (!MyClient(c2ptr)) /* implies mine and a user */ continue; if ((!host || !match(host, c2ptr->user->host)) && mycmp(user, c2ptr->user->username) == 0) { (*count)++; res = c2ptr; } } return res; } /* ** 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. */ #ifndef CLIENT_COMPILE /* ** Find a server from hash table, given its name */ aClient *find_server(name, cptr) char *name; Reg aClient *cptr; { aClient *acptr = cptr; if (name && *name) acptr = hash_find_server(name, cptr); return acptr; } /* ** Given a server name, find the server mask behind which the server ** is hidden. */ aClient *find_mask(name, cptr) char *name; aClient *cptr; { static char servermask[HOSTLEN+1]; Reg aClient *c2ptr = cptr; Reg char *mask = servermask; if (!name || !*name) return c2ptr; if ((c2ptr = hash_find_server(name, cptr))) return (c2ptr); if (index(name, '*')) return c2ptr; strcpy (servermask, name); while (*mask) { if (*(mask+1) == '.') { *mask = '*'; if ((c2ptr = hash_find_server(mask, cptr))) return (c2ptr); } mask++; } return (c2ptr ? c2ptr : cptr); } /* ** Find a server from hash table, given its token */ aServer *find_tokserver(token, cptr, c2ptr) int token; aClient *cptr, *c2ptr; { return hash_find_stoken(token, cptr, c2ptr); } /* ** Find a server, given its name (which might contain *'s, in which case ** the first match will be return [not the best one]) */ aClient *find_name(name, cptr) char *name; aClient *cptr; { Reg aClient *c2ptr = cptr; Reg aServer *sp = NULL; if (!name || !*name) return c2ptr; if ((c2ptr = hash_find_server(name, cptr))) return (c2ptr); if (!index(name, '*')) return c2ptr; for (sp = svrtop; sp; sp = sp->nexts) { /* ** A server present in the list necessarily has a non NULL ** bcptr pointer. */ if (match(name, sp->bcptr->name) == 0) break; if (index(sp->bcptr->name, '*')) if (match(sp->bcptr->name, name) == 0) break; } return (sp ? sp->bcptr : cptr); } #else aClient *find_server(name, cptr) char *name; aClient *cptr; { Reg aClient *c2ptr = cptr; if (!name || !*name) return c2ptr; for (c2ptr = client; c2ptr; c2ptr = c2ptr->next) { if (!IsServer(c2ptr) && !IsMe(c2ptr)) continue; if (match(c2ptr->name, name) == 0 || match(name, c2ptr->name) == 0) break; } return (c2ptr ? c2ptr : cptr); } #endif /* CLIENT_COMPILE */ /* ** Find person by (nick)name. */ aClient *find_person(name, cptr) char *name; aClient *cptr; { Reg aClient *c2ptr = cptr; c2ptr = find_client(name, c2ptr); if (c2ptr && IsClient(c2ptr) && c2ptr->user) return c2ptr; else return cptr; } /* * parse a buffer. * Return values: * errors: -3 for unknown origin/sender, -2 for FLUSH_BUFFER, -1 for bad cptr * * NOTE: parse() should not be called recusively by any other fucntions! */ int parse(cptr, buffer, bufend) aClient *cptr; char *buffer, *bufend; { Reg aClient *from = cptr; Reg char *ch, *s; Reg int len, i, numeric = 0, paramcount; Reg struct Message *mptr = NULL; int ret; #ifndef CLIENT_COMPILE Debug((DEBUG_DEBUG, "Parsing %s: %s", get_client_name(cptr, FALSE), buffer)); if (IsDead(cptr)) return -1; #endif s = sender; *s = '\0'; for (ch = buffer; *ch == ' '; ch++) ; para[0] = from->name; if (*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'; #ifdef CLIENT_COMPILE if ((s = index(sender, '!'))) { *s++ = '\0'; strncpyzt(userhost, s, sizeof(userhost)); } else if ((s = index(sender, '@'))) { *s++ = '\0'; strncpyzt(userhost, s, sizeof(userhost)); } #endif /* ** 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 || /* ** I really believe that the followin line is ** useless. What a waste, especially with 2.9 ** hostmasks.. at least the test on from->name ** will make it a bit better. -krys */ (*from->name == '*' && match(from->name, sender))) from = find_server(sender, (aClient *)NULL); #ifndef CLIENT_COMPILE /* Is there svc@server prefix ever? -Vesa */ /* every time a service talks -krys */ if (!from && index(sender, '@')) from = find_service(sender, (aClient *)NULL); if (!from) from = find_mask(sender, (aClient *) NULL); #endif para[0] = sender; /* 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 * Since 2.9 we pick them up and .. --Vesa */ if (!from) { Debug((DEBUG_ERROR, "Unknown prefix (%s)(%s) from (%s)", sender, buffer, cptr->name)); ircstp->is_unpf++; #ifndef CLIENT_COMPILE remove_unknown(cptr, sender); #endif return -3; /* Grab it in read_message() */ } if (from->from != cptr) { ircstp->is_wrdi++; Debug((DEBUG_ERROR, "Message (%s) coming from (%s)", buffer, cptr->name)); #ifndef CLIENT_COMPILE return cancel_clients(cptr, from, buffer); #else return -1; #endif } } while (*ch == ' ') ch++; } if (*ch == '\0') { ircstp->is_empt++; Debug((DEBUG_NOTICE, "Empty message from host %s:%s", cptr->name, from->name)); 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))) { numeric = (*ch - '0') * 100 + (*(ch + 1) - '0') * 10 + (*(ch + 2) - '0'); paramcount = MAXPARA; ircstp->is_num++; } else { if (s) *s++ = '\0'; for (mptr = msgtab; mptr->cmd; mptr++) if (mycmp(mptr->cmd, ch) == 0) break; if (!mptr->cmd) { /* ** 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') { cptr->flags |= FLAGS_UNKCMD; if (IsPerson(from)) sendto_one(from, ":%s %d %s %s :Unknown command", me.name, ERR_UNKNOWNCOMMAND, from->name, ch); #ifdef CLIENT_COMPILE Debug((DEBUG_ERROR,"Unknown (%s) from %s[%s]", ch, cptr->name, cptr->sockhost)); #else else if (IsServer(cptr)) sendto_flag(SCH_ERROR, "Unknown command from %s:%s", get_client_name(cptr, TRUE), ch); Debug((DEBUG_ERROR,"Unknown (%s) from %s", ch, get_client_name(cptr, TRUE))); #endif } ircstp->is_unco++; return -1; } paramcount = mptr->parameters; i = bufend - ((s) ? s : ch); mptr->bytes += i; #ifndef CLIENT_COMPILE if ((mptr->flags & MSG_LAG) && !(IsServer(cptr) || IsService(cptr))) { /* Flood control partly migrated into penalty */ if (bootopt & BOOT_PROT) cptr->since += (1 + i / 100); else cptr->since = timeofday; /* 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 */ } #endif } /* ** 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' !! */ #ifdef CLIENT_COMPILE if (me.user) para[0] = sender; #endif 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-1) break; for (; *s != ' ' && *s; s++) ; } } para[++i] = NULL; /* at worst, ++i is paramcount (MAXPARA) */ if (mptr == NULL) return (do_numeric(numeric, cptr, from, i, para)); mptr->count++; if (!MyConnect(from)) mptr->rcount++; if (IsRegisteredUser(cptr) && #ifdef IDLE_FROM_MSG mptr->func == m_private) #else mptr->func != m_ping && mptr->func != m_pong) #endif from->user->last = timeofday; Debug((DEBUG_DEBUG, "Function: %#x = %s parc %d parv %#x", mptr->func, mptr->cmd, i, para)); #ifndef CLIENT_COMPILE if ((mptr->flags & MSG_REGU) && check_registered_user(from)) return -1; if ((mptr->flags & MSG_SVC) && check_registered_service(from)) return -1; if ((mptr->flags & MSG_REG) && check_registered(from)) return -1; if ((mptr->flags & MSG_NOU) && (MyPerson(from) || MyService(from))) { sendto_one(from, err_str(ERR_ALREADYREGISTRED, para[0])); return-1; } if (MyConnect(from) && !IsServer(from) && (mptr->flags & (MSG_LOP|MSG_OP)) && !((mptr->flags & MSG_OP) && (IsOper(from))) && !((mptr->flags & MSG_LOP) && (IsLocOp(from)))) { sendto_one(from, err_str(ERR_NOPRIVILEGES, para[0])); return -1; } #endif /* ** ALL m_functions return now UNIFORMLY: ** -2 old FLUSH_BUFFER return value (unchanged). ** -1 if parsing of a protocol message leads in a syntactic/semantic ** error and NO penalty scoring should be applied. ** >=0 if protocol message processing was successful. The return ** value indicates the penalty score. */ ret = (*mptr->func)(cptr, from, i, para); #ifndef CLIENT_COMPILE /* ** Add penalty score for sucessfully parsed command if issued by ** a LOCAL user client. */ if ((ret > 0) && IsRegisteredUser(cptr) && (bootopt & BOOT_PROT)) { cptr->since += ret; /* only to lurk sendto_one(cptr, ":%s NOTICE %s :*** Penalty INCR [%s] +%d", me.name, cptr->name, ch, ret); */ } #endif return (ret != FLUSH_BUFFER) ? 2 : FLUSH_BUFFER; } /* * field breakup for ircd.conf file. */ char *getfield(irc_newline) char *irc_newline; { static char *line = NULL; char *end, *field; if (irc_newline) line = irc_newline; if (line == NULL) return(NULL); field = line; if ((end = (char *)index(line, IRCDCONF_DELIMITER)) == NULL) { line = NULL; if ((end = (char *)index(field,'\n')) == NULL) end = field + strlen(field); } else line = end + 1; *end = '\0'; return(field); } #ifndef CLIENT_COMPILE static int cancel_clients(cptr, sptr, cmd) aClient *cptr, *sptr; char *cmd; { /* * kill all possible points that are causing confusion here, * I'm not sure I've got this all right... * - avalon */ sendto_flag(SCH_NOTICE, "Message (%s) for %s[%s!%s@%s] from %s", cmd, sptr->name, sptr->from->name, sptr->from->username, sptr->from->sockhost, get_client_name(cptr, TRUE)); /* * Incorrect prefix for a server from some connection. If it is a * client trying to be annoying, just QUIT them, if it is a server * then the same deal. */ if (IsServer(sptr) || IsMe(sptr)) { sendto_flag(SCH_NOTICE, "Dropping server %s",cptr->name); return exit_client(cptr, cptr, &me, "Fake Direction"); } /* * Ok, someone is trying to impose as a client and things are * confused. If we got the wrong prefix from a server, send out a * kill, else just exit the lame client. */ if (IsServer(cptr)) { sendto_serv_butone(NULL, ":%s KILL %s :%s (%s[%s] != %s)", me.name, sptr->name, me.name, sptr->name, sptr->from->name, get_client_name(cptr, TRUE)); sptr->flags |= FLAGS_KILLED; return exit_client(cptr, sptr, &me, "Fake Prefix"); } return exit_client(cptr, cptr, &me, "Fake prefix"); } static void remove_unknown(cptr, sender) 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; /* * squit if it is a server because it means something is really * wrong. */ if (index(sender, '.') /* <- buggy, it could be a service! */ && !index(sender, '@')) /* better.. */ { sendto_flag(SCH_LOCAL, "Squitting unknown %s brought by %s.", sender, get_client_name(cptr, FALSE)); sendto_one(cptr, ":%s SQUIT %s :(Unknown from %s)", me.name, sender, get_client_name(cptr, FALSE)); } else /* * 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 * it can simply be caused by lag (among other things), so just * drop it if it is not a server. -krys * services aren't prone to collisions, so lag shouldn't be responsible * if we get here and sender is a service, we should probably issue * a kill in this case! -krys */ sendto_flag(SCH_LOCAL, "Dropping unknown %s brought by %s.", sender, get_client_name(cptr, FALSE)); } #endif