pair<u8 *, size_t> setupFDRFloodControl(const vector<hwlmLiteral> &lits, const EngineDescription &eng) { vector<FDRFlood> tmpFlood(N_CHARS); u32 default_suffix = eng.getDefaultFloodSuffixLength(); // zero everything to avoid spurious distinctions in the compares memset(&tmpFlood[0], 0, N_CHARS * sizeof(FDRFlood)); for (u32 c = 0; c < N_CHARS; c++) { tmpFlood[c].suffix = default_suffix; } for (const auto &lit : lits) { DEBUG_PRINTF("lit: '%s'%s\n", escapeString(lit.s).c_str(), lit.nocase ? " (nocase)" : ""); u32 litSize = verify_u32(lit.s.size()); u32 maskSize = (u32)lit.msk.size(); u8 c = lit.s[litSize - 1]; bool nocase = ourisalpha(c) ? lit.nocase : false; if (nocase && maskSize && (lit.msk[maskSize - 1] & CASE_BIT)) { c = (lit.cmp[maskSize - 1] & CASE_BIT) ? mytolower(c) : mytoupper(c); nocase = false; } u32 iEnd = MAX(litSize, maskSize); u32 upSuffix = iEnd; // upSuffix is used as an upper case suffix length // for case-less, or as a suffix length for case-sensitive; u32 loSuffix = iEnd; // loSuffix used only for case-less as a lower case suffix // length; for (u32 i = 0; i < iEnd; i++) { if (i < litSize) { if (isDifferent(c, lit.s[litSize - i - 1], lit.nocase)) { DEBUG_PRINTF("non-flood char in literal[%u] %c != %c\n", i, c, lit.s[litSize - i - 1]); upSuffix = MIN(upSuffix, i); loSuffix = MIN(loSuffix, i); // makes sense only for case-less break; } } if (i < maskSize) { u8 m = lit.msk[maskSize - i - 1]; u8 cm = lit.cmp[maskSize - i - 1] & m; if(nocase) { if ((mytoupper(c) & m) != cm) { DEBUG_PRINTF("non-flood char in mask[%u] %c != %c\n", i, mytoupper(c), cm); upSuffix = MIN(upSuffix, i); } if ((mytolower(c) & m) != cm) { DEBUG_PRINTF("non-flood char in mask[%u] %c != %c\n", i, mytolower(c), cm); loSuffix = MIN(loSuffix, i); } if (loSuffix != iEnd && upSuffix != iEnd) { break; } } else if ((c & m) != cm) { DEBUG_PRINTF("non-flood char in mask[%u] %c != %c\n", i, c, cm); upSuffix = MIN(upSuffix, i); break; } } } if(upSuffix != iEnd) { updateFloodSuffix(tmpFlood, nocase ? mytoupper(c) : c, upSuffix); } else { addFlood(tmpFlood, nocase ? mytoupper(c) : c, lit, upSuffix); } if (nocase) { if(loSuffix != iEnd) { updateFloodSuffix(tmpFlood, mytolower(c), loSuffix); } else { addFlood(tmpFlood, mytolower(c), lit, loSuffix); } } } #ifdef DEBUG for (u32 i = 0; i < N_CHARS; i++) { FDRFlood &fl = tmpFlood[i]; if (!fl.idCount) { continue; } printf("i is %02x fl->idCount is %hd fl->suffix is %d fl->allGroups is " "%016llx\n", i, fl.idCount, fl.suffix, fl.allGroups); for (u32 j = 0; j < fl.idCount; j++) { printf("j is %d fl.groups[j] %016llx fl.len[j] %d \n", j, fl.groups[j], fl.len[j]); } } #endif map<FDRFlood, CharReach, FloodComparator> flood2chars; for (u32 i = 0; i < N_CHARS; i++) { FDRFlood fl = tmpFlood[i]; flood2chars[fl].set(i); } u32 nDistinctFloods = flood2chars.size(); size_t floodHeaderSize = sizeof(u32) * N_CHARS; size_t floodStructSize = sizeof(FDRFlood) * nDistinctFloods; size_t totalSize = ROUNDUP_16(floodHeaderSize + floodStructSize); u8 *buf = (u8 *)aligned_zmalloc(totalSize); assert(buf); // otherwise would have thrown std::bad_alloc u32 *floodHeader = (u32 *)buf; FDRFlood *layoutFlood = (FDRFlood * )(buf + floodHeaderSize); u32 currentFloodIndex = 0; for (const auto &m : flood2chars) { const FDRFlood &fl = m.first; const CharReach &cr = m.second; layoutFlood[currentFloodIndex] = fl; for (size_t c = cr.find_first(); c != cr.npos; c = cr.find_next(c)) { floodHeader[c] = currentFloodIndex; } currentFloodIndex++; } DEBUG_PRINTF("made a flood structure with %zu + %zu = %zu\n", floodHeaderSize, floodStructSize, totalSize); return make_pair((u8 *)buf, totalSize); }
/* * WARNING WARNING WARNING * * This function is very ugly in terms of args handling, and it is * very easy to inadvertently break something if you make small tweaks * to it. * * args and numargs do NOT always map together in this function, * for example numargs = 3, means that args really has 3+3=6 elements * in some points: numargs should be numargs2. * * Basically, the bottom two elements of args[] are dropped * in this function when * it goes to args2 to call the particular handlers -Mysid */ void parseLine(char *line) { int i = 0, a = 0, x = 0, prefixed = 0; char *args2[MAX_IRC_LINE_LEN + 5]; char *args[MAX_IRC_LINE_LEN + 5]; char realargs[151][151]; u_int16_t numargs = 0; /* * Seems Ok to me ^^^ * sizes may be off(?) */ /* Yes, your sizes were off.. -Mysid */ strncpyzt(coreBuffer, line, MAX_IRC_LINE_LEN); #ifdef DEBUG printf("Read: %s\n", coreBuffer); #endif CTime = time(NULL); while (*line && x < 150) { while (*line != ' ' && *line && a < 150) { realargs[x][a] = *line; a++; line++; } realargs[x][a] = 0; args[x] = realargs[x]; x++; numargs++; while (*line == ' ') line++; a = 0; } /* ensure the next item is null so we can check it later */ realargs[x][0] = 0; args[x] = realargs[x]; if (args[0][0] == ':') { prefixed = 1; /** \bug old lame bugfix, what would be better is to use a 'from' value and args++'ing it, if there's no prefix then have from set to the uplink -Mysid */ args[0]++; } else prefixed = 0; if (!strcmp(args[0], "PING") && !prefixed) { sSend("PONG :%s", myname); return; } else if (!strcmp(args[0], "ERROR") && !strcmp(args[1], ":Closing")) sshutdown(0); else if (!strcmp(args[0], "NICK") && !prefixed) { if (strchr(args[4], '*') || strchr(args[4], '?') || strchr(args[4], '!') || strchr(args[4], '@')) { char nick[NICKLEN]; strncpyzt(nick, args[1], NICKLEN); sSend (":%s KILL %s :%s!%s (Your ident reply contains either a *, !, @, or ?. Please remove this before returning.)", services[1].name, nick, services[1].host, services[1].name); addGhost(nick); timer(15, delTimedGhost, strdup(nick)); return; } addNewUser(args, numargs); /* nickserv.c, add new user. */ return; } if (!strcmp(args[1], "PRIVMSG")) { UserList *tmp = getNickData(args[0]); if (strchr(args[2], '#') || strchr(args[2], '$')) return; if (!strcasecmp(args[0], NickServ) || !strcasecmp(args[0], GameServ) || !strcasecmp(args[0], OperServ) || !strcasecmp(args[0], ChanServ) || !strcasecmp(args[0], MemoServ) || !strcasecmp(args[0], InfoServ)) return; if (tmp && tmp->reg && tmp->reg->flags & NBANISH) { sSend(":%s NOTICE %s :This nickname is banished." " You cannot use services until you change" " nicknames.", NickServ, args[0]); return; } if (!tmp) { nDesynch(args[0], "PRIVMSG"); return; } if (addFlood(tmp, 1)) return; if (getBanInfo(tmp->nick, tmp->user, tmp->host, A_IGNORE) != NULL) { if (tmp->floodlevel.GetLev() < 2) sSend (":%s NOTICE %s :You are on services ignore, you may not use any Service", NickServ, tmp->nick); if (!isOper(tmp) || tmp->caccess < ACC_RECOGNIZED || !tmp->reg || !(tmp->reg->opflags & OROOT)) return; } args[3]++; while (*args[3] == ' ') args[3]++; for (i = 3; i < numargs; i++) args2[i - 3] = args[i]; numargs -= 3; /* Handle pings before even going to the services */ if (!strcasecmp(args2[0], "\001PING")) { if (addFlood(tmp, 3)) return; if (numargs < 3) sSend(":%s NOTICE %s :\001PING %s", args[2], args[0], args2[1]); else sSend(":%s NOTICE %s :\001PING %s %s", args[2], args[0], args2[1], args2[2]); return; } /* NOTE: numargs maps to args2 not args at this point */ if (!strncasecmp(args[2], OperServ, strlen(OperServ))) sendToOperServ(tmp, args2, numargs); else if (!strncasecmp(args[2], NickServ, strlen(NickServ))) sendToNickServ(tmp, args2, numargs); else if (!strncasecmp(args[2], ChanServ, strlen(ChanServ))) sendToChanServ(tmp, args2, numargs); else if (!strncasecmp(args[2], MemoServ, strlen(MemoServ))) sendToMemoServ(tmp, args2, numargs); else if (!strncasecmp(args[2], InfoServ, strlen(InfoServ))) sendToInfoServ(tmp, args2, numargs); else if (!strncasecmp(args[2], GameServ, strlen(GameServ))) sendToGameServ(tmp, args2, numargs); else if (isGhost(args[2])) { sSend (":%s NOTICE %s :This is a NickServ registered nick enforcer, and not a real user.", args[2], args[0]); } /* Note, the below should be correct. */ else if ((numargs >= 1) && adCheck(tmp, args[2], args2, numargs)) return; return; } else if (!strcmp(args[1], "QUIT")) { remUser(args[0], 0); return; } else if (!strcmp(args[1], "NICK")) { UserList *tmp = getNickData(args[0]); if (addFlood(tmp, 5)) return; changeNick(args[0], args[2], args[3]); return; } else if (!strcmp(args[1], "MODE") && !strcmp(args[0], args[2])) { setMode(args[0], args[3]); return; } else if (!strcmp(args[1], "MODE")) { setChanMode(args, numargs); return; } else if (!strcmp(args[1], "TOPIC")) { setChanTopic(args, numargs); return; } else if (!strcmp(args[1], "AWAY")) { if (numargs < 3) { setFlags(args[0], NISAWAY, '-'); checkMemos(getNickData(args[0])); } else setFlags(args[0], NISAWAY, '+'); return; } else if (!strcmp(args[1], "JOIN")) { addUserToChan(getNickData(args[0]), args[2]); return; } else if (!strcmp(args[1], "PART")) { remUserFromChan(getNickData(args[0]), args[2]); return; } else if (!strcmp(args[1], "KICK")) { remUserFromChan(getNickData(args[3]), args[2]); return; } else if (!strcmp(args[1], "KILL")) { int i; for (i = 0; i < NUMSERVS; i++) { if (!strcasecmp(args[2], services[i].name)) { addUser(services[i].name, services[i].uname, services[i].host, services[i].rname, services[i].mode); sSend(":%s KILL %s :%s!%s (services kill protection)", services[i].name, args[0], services[i].host, services[i].name); sSend(":%s GLOBOPS :%s just killed me!", services[i].name, args[0]); remUser(args[0], 0); return; } } if (isGhost(args[2])) { delGhost(args[2]); return; } else remUser(args[2], 1); return; } else if (!strcmp(args[1], "MOTD")) { UserList *tmp = getNickData(args[0]); if (addFlood(tmp, 1)) return; motd(args[0]); return; } else if (!strcmp(args[1], "INFO")) { UserList *tmp = getNickData(args[0]); if (!tmp || addFlood(tmp, 3)) return; sendInfoReply(tmp); return; } else if (!strcmp(args[1], "VERSION")) { UserList *tmp = getNickData(args[0]); if (addFlood(tmp, 1)) return; sSend(":%s 351 %s %s %s :%s", myname, args[0], VERSION_STRING, myname, VERSION_QUOTE); return; } else if ((!strcmp(args[1], "GNOTICE") || !strcmp(args[1], "GLOBOPS")) && !strcmp(args[2], ":Link") && !strcmp(args[3], "with") && !strncmp(args[4], myname, strlen(myname))) { sSend(":%s GNOTICE :Link with %s[services@%s] established.", myname, args[0], hostname); strncpyzt(hostname, args[0], sizeof(hostname)); expireNicks(NULL); expireChans(NULL); sync_cfg("1"); checkTusers(NULL); flushLogs(NULL); nextNsync = (SYNCTIME + CTime); nextCsync = ((SYNCTIME * 2) + CTime); nextMsync = ((SYNCTIME * 3) + CTime); loadakills(); return; } #ifdef IRCD_HURTSET else if (!strcmp(args[1], "HURTSET") && (numargs >= 4)) { UserList *hurtwho; if ((hurtwho = getNickData(args[2]))) { if (args[3] && *args[3] == '-') hurtwho->oflags &= ~(NISAHURT); else if (args[3] && atoi(args[3]) == 4) { hurtwho->oflags |= (NISAHURT); } else if (args[3] && isdigit(*args[3]) && (getBanInfo(hurtwho->nick, hurtwho->user, hurtwho->host, A_AHURT) != NULL)) hurtwho->oflags |= (NISAHURT); } } #endif else if (!strcmp(args[1], "SQUIT")) { time_t jupe; jupe = time(NULL); if (strchr(args[2], '.')) return; sSend(":%s WALLOPS :%s Un-jupitered by %s at %s", myname, args[2], args[0], ctime(&(jupe))); return; } else if (!strcmp(args[1], "STATS") && numargs > 3) { const char* from = args[0]; if (args[2] && !strcasecmp(args[2], "OPTS")) { sSend(":%s NOTICE %s :Network name: %s", InfoServ, from, NETWORK); #ifdef AKILLMAILTO sSend(":%s NOTICE %s :Akill log address: %s", InfoServ, from, AKILLMAILTO); #endif #ifdef ENABLE_GRPOPS sSend(":%s NOTICE %s :GRPops enabled.", InfoServ, from); #endif #ifdef MD5_AUTH sSend(":%s NOTICE %s :MD5 authentication available.", InfoServ, from); #endif } else if (args[2] && !strcasecmp(args[2], "V$")) { sSend(":%s NOTICE %s :Based on sn services1.4.", InfoServ, from); } } /* "No N-line" error */ else if (!strcmp(args[0], "ERROR") && !strcmp(args[1], ":No")) { fprintf(stderr, "Error connecting to server: No N-line\n"); sshutdown(2); } }