/************************************************************************ * Unreal Internet Relay Chat Daemon, src/packet.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. */ #include "struct.h" #include "common.h" #include "sys.h" #include "msg.h" #include "h.h" #include ID_Copyright ("(C) 1988 University of Oulu, Computing Center and Jarkko Oikarinen"); ID_Notes("2.12 1/30/94"); aCommand *CommandHash[256]; /* one per letter */ aCommand *TokenHash[256]; /* ** dopacket ** cptr - pointer to client structure for which the buffer data ** applies. ** buffer - pointr to the buffer containing the newly read data ** length - number of valid bytes of data in the buffer ** ** Note: ** It is implicitly assumed that dopacket is called only ** with cptr of "local" variation, which contains all the ** necessary fields (buffer etc..) */ void add_CommandX(char *cmd, char *token, int (*func)(), unsigned char parameters, int flags) ; int dopacket(aClient *cptr, char *buffer, int length) { char *ch1; char *ch2; aClient *acpt = cptr->listener; #ifdef ZIP_LINKS int zipped = 0; int done_unzip = 0; #endif me.receiveB += length; /* Update bytes received */ cptr->receiveB += length; if (cptr->receiveB > 1023) { cptr->receiveK += (cptr->receiveB >> 10); cptr->receiveB &= 0x03ff; /* 2^10 = 1024, 3ff = 1023 */ } if (acpt != &me) { acpt->receiveB += length; if (acpt->receiveB > 1023) { acpt->receiveK += (acpt->receiveB >> 10); acpt->receiveB &= 0x03ff; } } if (me.receiveB > 1023) { me.receiveK += (me.receiveB >> 10); me.receiveB &= 0x03ff; } ch1 = cptr->buffer + cptr->count; ch2 = buffer; #ifdef ZIP_LINKS if (IsZipStart(cptr)) { if (*ch2 == '\n' || *ch2 == '\r') { ch2++; length--; } cptr->zip->first = 0; } else { done_unzip = 1; } if (IsZipped(cptr)) { /* uncompressed buffer first */ zipped = length; cptr->zip->inbuf[0] = '\0'; /* unnecessary but nicer for debugging */ cptr->zip->incount = 0; ch2 = unzip_packet(cptr, ch2, &zipped); length = zipped; zipped = 1; if (length == -1) return exit_client(cptr, cptr, &me, "fatal error in unzip_packet(1)"); } /* While there is "stuff" in the compressed input to deal with, * keep loop parsing it. I have to go through this loop at least once. * -Dianora */ do { #endif while (--length >= 0) { char g = (*ch1 = *ch2++); /* * Yuck. Stuck. To make sure we stay backward compatible, * we must assume that either CR or LF terminates the message * and not CR-LF. By allowing CR or LF (alone) into the body * of messages, backward compatibility is lost and major * problems will arise. - Avalon */ if (g < '\16' && (g == '\n' || g == '\r')) { if (ch1 == cptr->buffer) continue; /* Skip extra LF/CR's */ *ch1 = '\0'; me.receiveM += 1; /* Update messages received */ cptr->receiveM += 1; if (cptr->listener != &me) cptr->listener->receiveM += 1; cptr->count = 0; /* ...just in case parse returns with ** FLUSH_BUFFER without removing the ** structure pointed by cptr... --msa */ if (parse(cptr, cptr->buffer, ch1) == FLUSH_BUFFER) /* ** FLUSH_BUFFER means actually that cptr ** structure *does* not exist anymore!!! --msa */ return FLUSH_BUFFER; /* ** Socket is dead so exit (which always returns with ** FLUSH_BUFFER here). - avalon */ if (cptr->flags & FLAGS_DEADSOCKET) return exit_client(cptr, cptr, &me, cptr->error_str ? cptr->error_str : "Dead socket"); #ifdef ZIP_LINKS if ((IsZipped(cptr)) && (zipped == 0) && (length > 0)) { /* ** beginning of server connection, the buffer ** contained PASS/CAPAB/SERVER and is now ** zipped! ** Ignore the '\n' that should be here. */ /* Checked RFC1950: \r or \n can't start a ** zlib stream -orabidoo */ zipped = length; if (zipped > 0 && (*ch2 == '\n' || *ch2 == '\r')) { ch2++; zipped--; } cptr->zip->first = 0; ch2 = unzip_packet(cptr, ch2, &zipped); length = zipped; zipped = 1; if (length == -1) return exit_client(cptr, cptr, &me, "fatal error in unzip_packet(2)"); } #endif ch1 = cptr->buffer; } else if (ch1 < cptr->buffer + (sizeof(cptr->buffer) - 1)) ch1++; /* There is always room for the null */ } #ifdef ZIP_LINKS /* Now see if anything is left uncompressed in the input * If so, uncompress it and continue to parse * -Dianora */ if ((IsZipped(cptr)) && cptr->zip->incount) { /* This call simply finishes unzipping whats left * second parameter is not used. -Dianora */ ch2 = unzip_packet(cptr, (char *)NULL, &zipped); length = zipped; zipped = 1; if (length == -1) return exit_client(cptr, cptr, &me, "fatal error in unzip_packet(3)"); ch1 = ch2 + length; done_unzip = 0; } else { done_unzip = 1; } } while(!done_unzip); #endif cptr->count = ch1 - cptr->buffer; return 0; } void init_CommandHash(void) { #ifdef DEVELOP_DEBUG aCommand *p; int i; long chainlength; #endif bzero(CommandHash, sizeof(CommandHash)); bzero(TokenHash, sizeof(TokenHash)); add_Command(MSG_MODE, TOK_MODE, m_mode, MAXPARA); add_Command(MSG_OPERMOTD, TOK_OPERMOTD, m_opermotd, MAXPARA); add_CommandX(MSG_NICK, TOK_NICK, m_nick, MAXPARA, M_UNREGISTERED|M_USER|M_SERVER); add_CommandX(MSG_JOIN, TOK_JOIN, m_join, MAXPARA, M_USER); add_CommandX(MSG_USER, TOK_USER, m_user, 4, M_UNREGISTERED|M_USER); add_CommandX(MSG_PART, TOK_PART, m_part, 2, M_USER); add_Command(MSG_WATCH, TOK_WATCH, m_watch, 1); add_Command(MSG_LUSERS, TOK_LUSERS, m_lusers, MAXPARA); add_CommandX(MSG_ERROR, TOK_ERROR, m_error, MAXPARA, M_UNREGISTERED|M_SERVER); add_Command(MSG_NAMES, TOK_NAMES, m_names, MAXPARA); add_CommandX(MSG_VERSION, TOK_VERSION, m_version, MAXPARA, M_UNREGISTERED|M_USER|M_SERVER); add_Command(MSG_SUMMON, NULL, m_summon, 1); add_Command(MSG_USERS, NULL, m_users, MAXPARA); add_Command(MSG_INFO, TOK_INFO, m_info, MAXPARA); add_Command(MSG_MOTD, TOK_MOTD, m_motd, MAXPARA); add_Command(MSG_DNS, TOK_DNS, m_dns, MAXPARA); add_Command(MSG_REHASH, TOK_REHASH, m_rehash, MAXPARA); add_Command(MSG_RESTART, TOK_RESTART, m_restart, MAXPARA); add_Command(MSG_DIE, TOK_DIE, m_die, MAXPARA); add_Command(MSG_DALINFO, TOK_DALINFO, m_dalinfo, MAXPARA); add_Command(MSG_CREDITS, TOK_CREDITS, m_credits, MAXPARA); add_Command(MSG_LICENSE, TOK_LICENSE, m_license, MAXPARA); add_Command(MSG_BOTMOTD, TOK_BOTMOTD, m_botmotd, MAXPARA); add_Command(MSG_NEWJOIN, TOK_JOIN, m_join, MAXPARA); add_Command(MSG_MODULE, TOK_MODULE, m_module, MAXPARA); add_Command(MSG_TKL, TOK_TKL, m_tkl, MAXPARA); #ifdef DEVELOP_DEBUG for (i = 0; i <= 255; i++) { chainlength = 0; for (p = CommandHash[i]; p; p = p->next) chainlength++; if (chainlength) fprintf(stderr, "%c chainlength = %i\r\n", i, chainlength); } fprintf(stderr, "Tokens:\n"); for (i = 0; i <= 255; i++) { chainlength = 0; for (p = TokenHash[i]; p; p = p->next) chainlength++; if (chainlength) fprintf(stderr, "%c chainlength = %i\r\n", i, chainlength); } #endif } aCommand *add_Command_backend(char *cmd, int (*func)(), unsigned char parameters, unsigned char token, int flags) { aCommand *newcmd = (aCommand *) MyMalloc(sizeof(aCommand)); bzero(newcmd, sizeof(aCommand)); newcmd->cmd = (char *) strdup(cmd); newcmd->parameters = (parameters > MAXPARA) ? MAXPARA : parameters; newcmd->func = func; newcmd->flags = flags; /* Add in hash with hash value = first byte */ if (!token) AddListItem(newcmd, CommandHash[toupper(*cmd)]); else AddListItem(newcmd, TokenHash[*cmd]); return newcmd; } int CommandExists(char *name) { aCommand *p; for (p = CommandHash[toupper(*name)]; p; p = p->next) { if (!stricmp(p->cmd, name)) return 1; } for (p = TokenHash[*name]; p; p = p->next) { if (!strcmp(p->cmd, name)) return 1; } return 0; } Command *CommandAdd(Module *module, char *cmd, char *tok, int (*func)(), unsigned char params, int flags) { Command *command; if (find_Command_simple(cmd) || (tok && find_Command_simple(tok))) { if (module) module->errorcode = MODERR_EXISTS; return NULL; } command = MyMallocEx(sizeof(Command)); command->cmd = add_Command_backend(cmd,func,params, 0, flags); command->tok = NULL; command->cmd->owner = module; if (tok) { command->tok = add_Command_backend(tok,func,params,1,flags); command->cmd->friend = command->tok; command->tok->friend = command->cmd; command->tok->owner = module; } else command->cmd->friend = NULL; if (module) { ModuleObject *cmdobj = (ModuleObject *)MyMallocEx(sizeof(ModuleObject)); cmdobj->object.command = command; cmdobj->type = MOBJ_COMMAND; AddListItem(cmdobj, module->objects); module->errorcode = MODERR_NOERROR; } return command; } void CommandDel(Command *command) { Cmdoverride *ovr, *ovrnext; DelListItem(command->cmd, CommandHash[toupper(*command->cmd->cmd)]); if (command->tok) DelListItem(command->tok, TokenHash[*command->tok->cmd]); if (command->cmd->owner) { ModuleObject *cmdobj; for (cmdobj = command->cmd->owner->objects; cmdobj; cmdobj = (ModuleObject *)cmdobj->next) { if (cmdobj->type == MOBJ_COMMAND && cmdobj->object.command == command) { DelListItem(cmdobj,command->cmd->owner->objects); MyFree(cmdobj); break; } } } for (ovr = command->cmd->overriders; ovr; ovr = ovrnext) { ovrnext = ovr->next; CmdoverrideDel(ovr); } MyFree(command->cmd->cmd); MyFree(command->cmd); if (command->tok) { MyFree(command->tok->cmd); MyFree(command->tok); } MyFree(command); } void add_Command(char *name, char *token, int (*func)(), unsigned char parameters) { aCommand *cmd, *tok; cmd = add_Command_backend(name, func, parameters, 0, 0); if (token) { tok = add_Command_backend(token, func, parameters, 1, 0); tok->friend = cmd; cmd->friend = tok; } else cmd->friend = NULL; } void add_CommandX(char *name, char *token, int (*func)(), unsigned char parameters, int flags) { aCommand *cmd, *tok; cmd = add_Command_backend(name, func, parameters, 0, flags); if (token != NULL) { tok = add_Command_backend(token, func, parameters, 1, flags); tok->friend = cmd; cmd->friend = tok; } else cmd->friend = NULL; } inline aCommand *find_CommandEx(char *cmd, int (*func)(), int token) { aCommand *p; if (!token) { for (p = CommandHash[toupper(*cmd)]; p; p = p->next) if (!stricmp(p->cmd, cmd) && p->func == func) return p; return NULL; } for (p = TokenHash[*cmd]; p; p = p->next) if (!strcmp(p->cmd, cmd) && p->func == func) return p; return NULL; } int del_Command(char *cmd, char *token, int (*func)()) { aCommand *p; int i = 0; p = find_CommandEx(cmd, func, 0); if (!p) i--; else { Cmdoverride *ovr, *ovrnext; DelListItem(p, CommandHash[toupper(*cmd)]); for (ovr = p->overriders; ovr; ovr = ovrnext) { ovrnext = ovr->next; CmdoverrideDel(ovr); } if (p->cmd) MyFree(p->cmd); MyFree(p); } if (token != NULL) { p = find_CommandEx(token, func, 1); if (!p) i--; else { DelListItem(p, TokenHash[*token]); if (p->cmd) MyFree(p->cmd); MyFree(p); } } return i; } static inline aCommand *find_Token(char *cmd, int flags) { aCommand *p; for (p = TokenHash[*cmd]; p; p = p->next) { if ((flags & M_UNREGISTERED) && !(p->flags & M_UNREGISTERED)) continue; if ((flags & M_SHUN) && !(p->flags & M_SHUN)) continue; if ((flags & M_VIRUS) && !(p->flags & M_VIRUS)) continue; if ((flags & M_ALIAS) && !(p->flags & M_ALIAS)) continue; if (!strcmp(p->cmd, cmd)) return p; } return NULL; } static inline aCommand *find_Cmd(char *cmd, int flags) { aCommand *p; for (p = CommandHash[toupper(*cmd)]; p; p = p->next) { if ((flags & M_UNREGISTERED) && !(p->flags & M_UNREGISTERED)) continue; if ((flags & M_SHUN) && !(p->flags & M_SHUN)) continue; if ((flags & M_VIRUS) && !(p->flags & M_VIRUS)) continue; if ((flags & M_ALIAS) && !(p->flags & M_ALIAS)) continue; if (!stricmp(p->cmd, cmd)) return p; } return NULL; } inline aCommand *find_Command(char *cmd, short token, int flags) { aCommand *p; Debug((DEBUG_NOTICE, "FindCommand %s", cmd)); if (token) { if (strlen(cmd) < 3) { if ((p = find_Token(cmd, flags))) return p; return find_Cmd(cmd, flags); } if ((p = find_Cmd(cmd, flags))) return p; return find_Token(cmd, flags); } return find_Cmd(cmd, flags); } aCommand *find_Command_simple(char *cmd) { aCommand *p; for (p = CommandHash[toupper(*cmd)]; p; p = p->next) { if (!stricmp(p->cmd, cmd)) return (p); } for (p = TokenHash[*cmd]; p; p = p->next) { if (!strcmp(p->cmd, cmd)) return p; } return NULL; }