// this is where we handle the MODE command DLLFUNC int f_handle_mode (aClient *cptr, aClient *sptr, aChannel *chptr, char *mbuf, char *pbuf, time_t sendts, int samode) { int plusminus, securecount; char *p; if (f_config_ulinesecureaction == F_NOTHING) return MOD_SUCCESS; plusminus = 1; for (p = mbuf; *p; p++) { if (*p == '+') plusminus = 1; else if (*p == '-') plusminus = -1; else if (*p == 'z' && plusminus == 1 && (IsServer(sptr) || IsULine(sptr))) { securecount = f_count_secure_users(chptr); if (securecount < chptr->users) { sendto_snomask(SNO_EYES, "*** Uline %s set mode +z on %s: removing insecure users.", sptr->name, chptr->chname ); if (f_config_ulinesecureaction == F_KICK || securecount > 0) kick_insecure_users(chptr); else f_part_insecure_users(chptr); } } } return MOD_SUCCESS; }
/* ** m_svsnick ** parv[0] = sender ** parv[1] = old nickname ** parv[2] = new nickname ** parv[3] = timestamp */ int m_svsnick(aClient *cptr, aClient *sptr, int parc, char *parv[]) { aClient *acptr; aClient *ocptr; /* Other client */ if (!IsULine(sptr) || parc < 4 || (strlen(parv[2]) > NICKLEN)) return -1; /* This looks like an error anyway -Studded */ if (hunt_server_token(cptr, sptr, MSG_SVSNICK, TOK_SVSNICK, "%s %s :%s", 1, parc, parv) != HUNTED_ISME) return 0; /* Forwarded, done */ if (do_nick_name(parv[2]) == 0) return 0; if (!(acptr = find_person(parv[1], NULL))) return 0; /* User not found, bail out */ if ((ocptr = find_client(parv[2], NULL)) && ocptr != acptr) /* Collision */ { exit_client(acptr, acptr, sptr, "Nickname collision due to Services enforced " "nickname change, your nick was overruled"); return 0; } if (acptr != ocptr) acptr->umodes &= ~UMODE_REGNICK; acptr->lastnick = TS2ts(parv[3]); sendto_common_channels(acptr, ":%s NICK :%s", parv[1], parv[2]); add_history(acptr, 1); sendto_serv_butone_token(NULL, parv[1], MSG_NICK, TOK_NICK, "%s :%ld", parv[2], TS2ts(parv[3])); (void)del_from_client_hash_table(acptr->name, acptr); hash_check_watch(acptr, RPL_LOGOFF); sendto_snomask(SNO_NICKCHANGE, "*** Notice -- %s (%s@%s) has been forced to change his/her nickname to %s", acptr->name, acptr->user->username, acptr->user->realhost, parv[2]); RunHook2(HOOKTYPE_LOCAL_NICKCHANGE, acptr, parv[2]); strlcpy(acptr->name, parv[2], sizeof acptr->name); add_to_client_hash_table(parv[2], acptr); hash_check_watch(acptr, RPL_LOGON); return 0; }
int _register_user(aClient *cptr, aClient *sptr, char *nick, char *username, char *umode, char *virthost, char *ip) { ConfigItem_ban *bconf; char *parv[3], *tmpstr; #ifdef HOSTILENAME char stripuser[USERLEN + 1], *u1 = stripuser, *u2, olduser[USERLEN + 1], userbad[USERLEN * 2 + 1], *ubad = userbad, noident = 0; #endif int xx; anUser *user = sptr->user; aClient *nsptr; int i; char mo[256]; char *tkllayer[9] = { me.name, /*0 server.name */ "+", /*1 +|- */ "z", /*2 G */ "*", /*3 user */ NULL, /*4 host */ NULL, NULL, /*6 expire_at */ NULL, /*7 set_at */ NULL /*8 reason */ }; aTKline *savetkl = NULL; ConfigItem_tld *tlds; cptr->last = TStime(); parv[0] = sptr->name; parv[1] = parv[2] = NULL; nick = sptr->name; /* <- The data is always the same, but the pointer is sometimes not, * I need this for one of my modules, so do not remove! ;) -- Syzop */ if (MyConnect(sptr)) { if ((i = check_client(sptr, username))) { /* This had return i; before -McSkaf */ if (i == -5) return FLUSH_BUFFER; sendto_snomask(SNO_CLIENT, "*** Notice -- %s from %s.", i == -3 ? "Too many connections" : "Unauthorized connection", get_client_host(sptr)); ircstp->is_ref++; ircsprintf(mo, "This server is full."); return exit_client(cptr, sptr, &me, i == -3 ? mo : "You are not authorized to connect to this server"); } if (sptr->hostp) { /* reject ascci < 32 and ascii >= 127 (note: upper resolver might be even more strict) */ for (tmpstr = sptr->sockhost; *tmpstr > ' ' && *tmpstr < 127; tmpstr++); /* if host contained invalid ASCII _OR_ the DNS reply is an IP-like reply * (like: 1.2.3.4), then reject it and use IP instead. */ if (*tmpstr || !*user->realhost || (isdigit(*sptr->sockhost) && isdigit(*tmpstr - 1))) strncpyzt(sptr->sockhost, (char *)Inet_ia2p((struct IN_ADDR*)&sptr->ip), sizeof(sptr->sockhost)); } strncpyzt(user->realhost, sptr->sockhost, sizeof(sptr->sockhost)); /* SET HOSTNAME */ /* * I do not consider *, ~ or ! 'hostile' in usernames, * as it is easy to differentiate them (Use \*, \? and \\) * with the possible? * exception of !. With mIRC etc. ident is easy to fake * to contain @ though, so if that is found use non-ident * username. -Donwulff * * I do, We only allow a-z A-Z 0-9 _ - and . now so the * !strchr(sptr->username, '@') check is out of date. -Cabal95 * * Moved the noident stuff here. -OnyxDragon */ if (!(sptr->flags & FLAGS_DOID)) strncpyzt(user->username, username, USERLEN + 1); else if (sptr->flags & FLAGS_GOTID) strncpyzt(user->username, sptr->username, USERLEN + 1); else { /* because username may point to user->username */ char temp[USERLEN + 1]; strncpyzt(temp, username, USERLEN + 1); if (IDENT_CHECK == 0) { strncpyzt(user->username, temp, USERLEN + 1); } else { *user->username = '******'; strncpyzt((user->username + 1), temp, USERLEN); #ifdef HOSTILENAME noident = 1; #endif } } #ifdef HOSTILENAME /* * Limit usernames to just 0-9 a-z A-Z _ - and . * It strips the "bad" chars out, and if nothing is left * changes the username to the first 8 characters of their * nickname. After the MOTD is displayed it sends numeric * 455 to the user telling them what(if anything) happened. * -Cabal95 * * Moved the noident thing to the right place - see above * -OnyxDragon * * No longer use nickname if the entire ident is invalid, * if thats the case, it is likely the user is trying to cause * problems so just ban them. (Using the nick could introduce * hostile chars) -- codemastr */ for (u2 = user->username + noident; *u2; u2++) { if (isallowed(*u2)) *u1++ = *u2; else if (*u2 < 32) { /* * Make sure they can read what control * characters were in their username. */ *ubad++ = '^'; *ubad++ = *u2 + '@'; } else *ubad++ = *u2; } *u1 = '\0'; *ubad = '\0'; if (strlen(stripuser) != strlen(user->username + noident)) { if (stripuser[0] == '\0') { return exit_client(cptr, cptr, cptr, "Hostile username. Please use only 0-9 a-z A-Z _ - and . in your username."); } strcpy(olduser, user->username + noident); strncpy(user->username + 1, stripuser, USERLEN - 1); user->username[0] = '~'; user->username[USERLEN] = '\0'; } else u1 = NULL; #endif /* * following block for the benefit of time-dependent K:-lines */ if ((bconf = Find_ban(sptr, make_user_host(user->username, user->realhost), CONF_BAN_USER))) { ircstp->is_ref++; sendto_one(cptr, ":%s %d %s :*** You are not welcome on this server (%s)" " Email %s for more information.", me.name, ERR_YOUREBANNEDCREEP, cptr->name, bconf->reason ? bconf->reason : "", KLINE_ADDRESS); return exit_client(cptr, cptr, cptr, "You are banned"); } if ((bconf = Find_ban(NULL, sptr->info, CONF_BAN_REALNAME))) { ircstp->is_ref++; sendto_one(cptr, ":%s %d %s :*** Your GECOS (real name) is not allowed on this server (%s)" " Please change it and reconnect", me.name, ERR_YOUREBANNEDCREEP, cptr->name, bconf->reason ? bconf->reason : ""); return exit_client(cptr, sptr, &me, "Your GECOS (real name) is banned from this server"); } tkl_check_expire(NULL); /* Check G/Z lines before shuns -- kill before quite -- codemastr */ if ((xx = find_tkline_match(sptr, 0)) < 0) { ircstp->is_ref++; return xx; } find_shun(sptr); /* Technical note regarding next few lines of code: * If the spamfilter matches, depending on the action: * If it's block/dccblock/whatever the retval is -1 ===> we return, client stays "locked forever". * If it's kill/tklline the retval is -2 ==> we return with -2 (aka: FLUSH_BUFFER) * If it's action is viruschan the retval is -5 ==> we continue, and at the end of this return * take special actions. We cannot do that directly here since the user is not fully registered * yet (at all). * -- Syzop */ spamfilter_build_user_string(spamfilter_user, sptr->name, sptr); xx = dospamfilter(sptr, spamfilter_user, SPAMF_USER, NULL, 0, &savetkl); if ((xx < 0) && (xx != -5)) return xx; RunHookReturnInt(HOOKTYPE_PRE_LOCAL_CONNECT, sptr, !=0); } else {
/* ** m_nick ** parv[0] = sender prefix ** parv[1] = nickname ** if from new client -taz ** parv[2] = nick password ** if from server: ** parv[2] = hopcount ** parv[3] = timestamp ** parv[4] = username ** parv[5] = hostname ** parv[6] = servername ** if NICK version 1: ** parv[7] = servicestamp ** parv[8] = info ** if NICK version 2: ** parv[7] = servicestamp ** parv[8] = umodes ** parv[9] = virthost, * if none ** parv[10] = info ** if NICKIP: ** parv[10] = ip ** parv[11] = info */ DLLFUNC CMD_FUNC(m_nick) { aTKline *tklban; int ishold; aClient *acptr, *serv = NULL; aClient *acptrs; char nick[NICKLEN + 2], *s; Membership *mp; time_t lastnick = (time_t) 0; int differ = 1, update_watch = 1; unsigned char newusr = 0, removemoder = 1; /* * If the user didn't specify a nickname, complain */ if (parc < 2) { sendto_one(sptr, err_str(ERR_NONICKNAMEGIVEN), me.name, parv[0]); return 0; } strncpyzt(nick, parv[1], NICKLEN + 1); if (MyConnect(sptr) && sptr->user && !IsAnOper(sptr)) { if ((sptr->user->flood.nick_c >= NICK_COUNT) && (TStime() - sptr->user->flood.nick_t < NICK_PERIOD)) { /* Throttle... */ sendto_one(sptr, err_str(ERR_NCHANGETOOFAST), me.name, sptr->name, nick, (int)(NICK_PERIOD - (TStime() - sptr->user->flood.nick_t))); return 0; } } /* For a local clients, do proper nickname checking via do_nick_name() * and reject the nick if it returns false. * For remote clients, do a quick check by using do_remote_nick_name(), * if this returned false then reject and kill it. -- Syzop */ if ((IsServer(cptr) && !do_remote_nick_name(nick)) || (!IsServer(cptr) && !do_nick_name(nick))) { sendto_one(sptr, err_str(ERR_ERRONEUSNICKNAME), me.name, parv[0], parv[1], "Illegal characters"); if (IsServer(cptr)) { ircstp->is_kill++; sendto_failops("Bad Nick: %s From: %s %s", parv[1], parv[0], get_client_name(cptr, FALSE)); sendto_one(cptr, ":%s KILL %s :%s (%s <- %s[%s])", me.name, parv[1], me.name, parv[1], nick, cptr->name); if (sptr != cptr) { /* bad nick change */ sendto_serv_butone(cptr, ":%s KILL %s :%s (%s <- %s!%s@%s)", me.name, parv[0], me.name, get_client_name(cptr, FALSE), parv[0], sptr->user ? sptr->username : "", sptr->user ? sptr->user->server : cptr->name); sptr->flags |= FLAGS_KILLED; return exit_client(cptr, sptr, &me, "BadNick"); } } return 0; } /* Kill quarantined opers early... */ if (IsServer(cptr) && (sptr->from->flags & FLAGS_QUARANTINE) && (parc >= 11) && strchr(parv[8], 'o')) { ircstp->is_kill++; /* Send kill to uplink only, hasn't been broadcasted to the rest, anyway */ sendto_one(cptr, ":%s KILL %s :%s (Quarantined: no global oper privileges allowed)", me.name, parv[1], me.name); sendto_realops("QUARANTINE: Oper %s on server %s killed, due to quarantine", parv[1], sptr->name); /* (nothing to exit_client or to free, since user was never added) */ return 0; } /* ** Protocol 4 doesn't send the server as prefix, so it is possible ** the server doesn't exist (a lagged net.burst), in which case ** we simply need to ignore the NICK. Also when we got that server ** name (again) but from another direction. --Run */ /* ** We should really only deal with this for msgs from servers. ** -- Aeto */ if (IsServer(cptr) && (parc > 7 && (!(serv = (aClient *)find_server_b64_or_real(parv[6])) || serv->from != cptr->from))) { sendto_realops("Cannot find server %s (%s)", parv[6], backupbuf); return 0; } /* ** Check against nick name collisions. ** ** Put this 'if' here so that the nesting goes nicely on the screen :) ** We check against server name list before determining if the nickname ** is present in the nicklist (due to the way the below for loop is ** constructed). -avalon */ /* I managed to f**k this up i guess --stskeeps */ if ((acptr = find_server(nick, NULL))) { if (MyConnect(sptr)) { #ifdef GUEST if (IsUnknown(sptr)) { RunHook4(HOOKTYPE_GUEST, cptr, sptr, parc, parv); return 0; } #endif sendto_one(sptr, err_str(ERR_NICKNAMEINUSE), me.name, BadPtr(parv[0]) ? "*" : parv[0], nick); return 0; /* NICK message ignored */ } } /* ** Check for a Q-lined nickname. If we find it, and it's our ** client, just reject it. -Lefler ** Allow opers to use Q-lined nicknames. -Russell */ if (!stricmp("ircd", nick)) { sendto_one(sptr, err_str(ERR_ERRONEUSNICKNAME), me.name, BadPtr(parv[0]) ? "*" : parv[0], nick, "Reserved for internal IRCd purposes"); return 0; } if (!stricmp("irc", nick)) { sendto_one(sptr, err_str(ERR_ERRONEUSNICKNAME), me.name, BadPtr(parv[0]) ? "*" : parv[0], nick, "Reserved for internal IRCd purposes"); return 0; } if (MyClient(sptr)) /* local client changin nick afterwards.. */ { int xx; spamfilter_build_user_string(spamfilter_user, nick, sptr); xx = dospamfilter(sptr, spamfilter_user, SPAMF_USER, NULL, 0, NULL); if (xx < 0) return xx; } if (!IsULine(sptr) && (tklban = find_qline(sptr, nick, &ishold))) { if (IsServer(sptr) && !ishold) /* server introducing new client */ { acptrs = (aClient *)find_server_b64_or_real(sptr->user == NULL ? (char *)parv[6] : (char *)sptr->user-> server); /* (NEW: no unregistered q:line msgs anymore during linking) */ if (!acptrs || (acptrs->serv && acptrs->serv->flags.synced)) sendto_snomask(SNO_QLINE, "Q:lined nick %s from %s on %s", nick, (*sptr->name != 0 && !IsServer(sptr) ? sptr->name : "<unregistered>"), acptrs ? acptrs->name : "unknown server"); } if (IsServer(cptr) && IsPerson(sptr) && !ishold) /* remote user changing nick */ { sendto_snomask(SNO_QLINE, "Q:lined nick %s from %s on %s", nick, sptr->name, sptr->srvptr ? sptr->srvptr->name : "<unknown>"); } if (!IsServer(cptr)) /* local */ { if (ishold) { sendto_one(sptr, err_str(ERR_ERRONEUSNICKNAME), me.name, BadPtr(parv[0]) ? "*" : parv[0], nick, tklban->reason); return 0; } if (!IsOper(cptr)) { sptr->since += 4; /* lag them up */ sendto_one(sptr, err_str(ERR_ERRONEUSNICKNAME), me.name, BadPtr(parv[0]) ? "*" : parv[0], nick, tklban->reason); sendto_snomask(SNO_QLINE, "Forbidding Q-lined nick %s from %s.", nick, get_client_name(cptr, FALSE)); return 0; /* NICK message ignored */ } } } /* ** acptr already has result from previous find_server() */ if (acptr) { /* ** We have a nickname trying to use the same name as ** a server. Send out a nick collision KILL to remove ** the nickname. As long as only a KILL is sent out, ** there is no danger of the server being disconnected. ** Ultimate way to jupiter a nick ? >;-). -avalon */ sendto_failops("Nick collision on %s(%s <- %s)", sptr->name, acptr->from->name, get_client_name(cptr, FALSE)); ircstp->is_kill++; sendto_one(cptr, ":%s KILL %s :%s (%s <- %s)", me.name, sptr->name, me.name, acptr->from->name, /* NOTE: Cannot use get_client_name ** twice here, it returns static ** string pointer--the other info ** would be lost */ get_client_name(cptr, FALSE)); sptr->flags |= FLAGS_KILLED; return exit_client(cptr, sptr, &me, "Nick/Server collision"); } if (MyClient(cptr) && !IsOper(cptr)) cptr->since += 3; /* Nick-flood prot. -Donwulff */ if (!(acptr = find_client(nick, NULL))) goto nickkilldone; /* No collisions, all clear... */ /* ** If the older one is "non-person", the new entry is just ** allowed to overwrite it. Just silently drop non-person, ** and proceed with the nick. This should take care of the ** "dormant nick" way of generating collisions... */ /* Moved before Lost User Field to fix some bugs... -- Barubary */ if (IsUnknown(acptr) && MyConnect(acptr)) { /* This may help - copying code below */ if (acptr == cptr) return 0; acptr->flags |= FLAGS_KILLED; exit_client(NULL, acptr, &me, "Overridden"); goto nickkilldone; } /* A sanity check in the user field... */ if (acptr->user == NULL) { /* This is a Bad Thing */ sendto_failops("Lost user field for %s in change from %s", acptr->name, get_client_name(cptr, FALSE)); ircstp->is_kill++; sendto_one(acptr, ":%s KILL %s :%s (Lost user field!)", me.name, acptr->name, me.name); acptr->flags |= FLAGS_KILLED; /* Here's the previous versions' desynch. If the old one is messed up, trash the old one and accept the new one. Remember - at this point there is a new nick coming in! Handle appropriately. -- Barubary */ exit_client(NULL, acptr, &me, "Lost user field"); goto nickkilldone; } /* ** If acptr == sptr, then we have a client doing a nick ** change between *equivalent* nicknames as far as server ** is concerned (user is changing the case of his/her ** nickname or somesuch) */ if (acptr == sptr) { if (strcmp(acptr->name, nick) != 0) { /* Allows change of case in his/her nick */ removemoder = 0; /* don't set the user -r */ goto nickkilldone; /* -- go and process change */ } else /* ** This is just ':old NICK old' type thing. ** Just forget the whole thing here. There is ** no point forwarding it to anywhere, ** especially since servers prior to this ** version would treat it as nick collision. */ return 0; /* NICK Message ignored */ } /* ** Note: From this point forward it can be assumed that ** acptr != sptr (point to different client structures). */ /* ** Decide, we really have a nick collision and deal with it */ if (!IsServer(cptr)) { /* ** NICK is coming from local client connection. Just ** send error reply and ignore the command. */ #ifdef GUEST if (IsUnknown(sptr)) { RunHook4(HOOKTYPE_GUEST, cptr, sptr, parc, parv); return 0; } #endif sendto_one(sptr, err_str(ERR_NICKNAMEINUSE), /* parv[0] is empty when connecting */ me.name, BadPtr(parv[0]) ? "*" : parv[0], nick); return 0; /* NICK message ignored */ } /* ** NICK was coming from a server connection. ** This means we have a race condition (two users signing on ** at the same time), or two net fragments reconnecting with ** the same nick. ** The latter can happen because two different users connected ** or because one and the same user switched server during a ** net break. ** If we have the old protocol (no TimeStamp and no user@host) ** or if the TimeStamps are equal, we kill both (or only 'new' ** if it was a "NICK new"). Otherwise we kill the youngest ** when user@host differ, or the oldest when they are the same. ** --Run ** */ if (IsServer(sptr)) { /* ** A new NICK being introduced by a neighbouring ** server (e.g. message type "NICK new" received) */ if (parc > 3) { lastnick = TS2ts(parv[3]); if (parc > 5) differ = (mycmp(acptr->user->username, parv[4]) || mycmp(acptr->user->realhost, parv[5])); } sendto_failops("Nick collision on %s (%s %ld <- %s %ld)", acptr->name, acptr->from->name, acptr->lastnick, cptr->name, lastnick); /* ** I'm putting the KILL handling here just to make it easier ** to read, it's hard to follow it the way it used to be. ** Basically, this is what it will do. It will kill both ** users if no timestamp is given, or they are equal. It will ** kill the user on our side if the other server is "correct" ** (user@host differ and their user is older, or user@host are ** the same and their user is younger), otherwise just kill the ** user an reintroduce our correct user. ** The old code just sat there and "hoped" the other server ** would kill their user. Not anymore. ** -- binary */ if (!(parc > 3) || (acptr->lastnick == lastnick)) { ircstp->is_kill++; sendto_serv_butone(NULL, ":%s KILL %s :%s (Nick Collision)", me.name, acptr->name, me.name); acptr->flags |= FLAGS_KILLED; (void)exit_client(NULL, acptr, &me, "Nick collision with no timestamp/equal timestamps"); return 0; /* We killed both users, now stop the process. */ } if ((differ && (acptr->lastnick > lastnick)) || (!differ && (acptr->lastnick < lastnick)) || acptr->from == cptr) /* we missed a QUIT somewhere ? */ { ircstp->is_kill++; sendto_serv_butone(cptr, ":%s KILL %s :%s (Nick Collision)", me.name, acptr->name, me.name); acptr->flags |= FLAGS_KILLED; (void)exit_client(NULL, acptr, &me, "Nick collision"); goto nickkilldone; /* OK, we got rid of the "wrong" user, ** now we're going to add the user the ** other server introduced. */ } if ((differ && (acptr->lastnick < lastnick)) || (!differ && (acptr->lastnick > lastnick))) { /* * Introduce our "correct" user to the other server */ sendto_one(cptr, ":%s KILL %s :%s (Nick Collision)", me.name, parv[1], me.name); send_umode(NULL, acptr, 0, SEND_UMODES, buf); sendto_one_nickcmd(cptr, acptr, buf); if (acptr->user->away) sendto_one(cptr, ":%s AWAY :%s", acptr->name, acptr->user->away); send_user_joins(cptr, acptr); return 0; /* Ignore the NICK */ } return 0; } else { /* ** A NICK change has collided (e.g. message type ":old NICK new"). */ if (parc > 2) lastnick = TS2ts(parv[2]); differ = (mycmp(acptr->user->username, sptr->user->username) || mycmp(acptr->user->realhost, sptr->user->realhost)); sendto_failops ("Nick change collision from %s to %s (%s %ld <- %s %ld)", sptr->name, acptr->name, acptr->from->name, acptr->lastnick, sptr->from->name, lastnick); if (!(parc > 2) || lastnick == acptr->lastnick) { ircstp->is_kill += 2; sendto_serv_butone(NULL, /* First kill the new nick. */ ":%s KILL %s :%s (Self Collision)", me.name, acptr->name, me.name); sendto_serv_butone(cptr, /* Tell my servers to kill the old */ ":%s KILL %s :%s (Self Collision)", me.name, sptr->name, me.name); sptr->flags |= FLAGS_KILLED; acptr->flags |= FLAGS_KILLED; (void)exit_client(NULL, sptr, &me, "Self Collision"); (void)exit_client(NULL, acptr, &me, "Self Collision"); return 0; /* Now that I killed them both, ignore the NICK */ } if ((differ && (acptr->lastnick > lastnick)) || (!differ && (acptr->lastnick < lastnick))) { /* sptr (their user) won, let's kill acptr (our user) */ ircstp->is_kill++; sendto_serv_butone(cptr, ":%s KILL %s :%s (Nick collision: %s <- %s)", me.name, acptr->name, me.name, acptr->from->name, sptr->from->name); acptr->flags |= FLAGS_KILLED; (void)exit_client(NULL, acptr, &me, "Nick collision"); goto nickkilldone; /* their user won, introduce new nick */ } if ((differ && (acptr->lastnick < lastnick)) || (!differ && (acptr->lastnick > lastnick))) { /* acptr (our user) won, let's kill sptr (their user), ** and reintroduce our "correct" user */ ircstp->is_kill++; /* Kill the user trying to change their nick. */ sendto_serv_butone(cptr, ":%s KILL %s :%s (Nick collision: %s <- %s)", me.name, sptr->name, me.name, sptr->from->name, acptr->from->name); sptr->flags |= FLAGS_KILLED; (void)exit_client(NULL, sptr, &me, "Nick collision"); /* * Introduce our "correct" user to the other server */ /* Kill their user. */ sendto_one(cptr, ":%s KILL %s :%s (Nick Collision)", me.name, parv[1], me.name); send_umode(NULL, acptr, 0, SEND_UMODES, buf); sendto_one_nickcmd(cptr, acptr, buf); if (acptr->user->away) sendto_one(cptr, ":%s AWAY :%s", acptr->name, acptr->user->away); send_user_joins(cptr, acptr); return 0; /* their user lost, ignore the NICK */ } } return 0; /* just in case */ nickkilldone: if (IsServer(sptr)) { /* A server introducing a new client, change source */ sptr = make_client(cptr, serv); add_client_to_list(sptr); if (parc > 2) sptr->hopcount = TS2ts(parv[2]); if (parc > 3) sptr->lastnick = TS2ts(parv[3]); else /* Little bit better, as long as not all upgraded */ sptr->lastnick = TStime(); if (sptr->lastnick < 0) { sendto_realops ("Negative timestamp recieved from %s, resetting to TStime (%s)", cptr->name, backupbuf); sptr->lastnick = TStime(); } newusr = 1; } else if (sptr->name[0] && IsPerson(sptr)) { /* ** If the client belongs to me, then check to see ** if client is currently on any channels where it ** is currently banned. If so, do not allow the nick ** change to occur. ** Also set 'lastnick' to current time, if changed. */ if (MyClient(sptr)) { for (mp = sptr->user->channel; mp; mp = mp->next) { if (!is_skochanop(sptr, mp->chptr) && is_banned(sptr, mp->chptr, BANCHK_NICK)) { sendto_one(sptr, err_str(ERR_BANNICKCHANGE), me.name, parv[0], mp->chptr->chname); return 0; } if (CHECK_TARGET_NICK_BANS && !is_skochanop(sptr, mp->chptr) && is_banned_with_nick(sptr, mp->chptr, BANCHK_NICK, nick)) { sendto_one(sptr, ":%s 437 %s %s :Cannot change to a nickname banned on channel", me.name, parv[0], mp->chptr->chname); return 0; } if (!IsOper(sptr) && !IsULine(sptr) && mp->chptr->mode.mode & MODE_NONICKCHANGE && !is_chanownprotop(sptr, mp->chptr)) { sendto_one(sptr, err_str(ERR_NONICKCHANGE), me.name, parv[0], mp->chptr->chname); return 0; } } if (TStime() - sptr->user->flood.nick_t >= NICK_PERIOD) { sptr->user->flood.nick_t = TStime(); sptr->user->flood.nick_c = 1; } else sptr->user->flood.nick_c++; sendto_snomask(SNO_NICKCHANGE, "*** Notice -- %s (%s@%s) has changed his/her nickname to %s", sptr->name, sptr->user->username, sptr->user->realhost, nick); RunHook2(HOOKTYPE_LOCAL_NICKCHANGE, sptr, nick); } else { if (!IsULine(sptr)) sendto_snomask(SNO_FNICKCHANGE, "*** Notice -- %s (%s@%s) has changed his/her nickname to %s", sptr->name, sptr->user->username, sptr->user->realhost, nick); RunHook3(HOOKTYPE_REMOTE_NICKCHANGE, cptr, sptr, nick); } /* * Client just changing his/her nick. If he/she is * on a channel, send note of change to all clients * on that channel. Propagate notice to other servers. */ if (mycmp(parv[0], nick) || /* Next line can be removed when all upgraded --Run */ (!MyClient(sptr) && parc > 2 && TS2ts(parv[2]) < sptr->lastnick)) sptr->lastnick = (MyClient(sptr) || parc < 3) ? TStime() : TS2ts(parv[2]); if (sptr->lastnick < 0) { sendto_realops("Negative timestamp (%s)", backupbuf); sptr->lastnick = TStime(); } add_history(sptr, 1); sendto_common_channels(sptr, ":%s NICK :%s", parv[0], nick); sendto_serv_butone_token(cptr, parv[0], MSG_NICK, TOK_NICK, "%s %ld", nick, sptr->lastnick); if (removemoder) sptr->umodes &= ~UMODE_REGNICK; } else if (!sptr->name[0]) { #ifdef NOSPOOF /* * Client setting NICK the first time. * * Generate a random string for them to pong with. */ sptr->nospoof = getrandom32(); if (PINGPONG_WARNING) sendto_one(sptr, ":%s NOTICE %s :*** If you are having problems" " connecting due to ping timeouts, please" " type /quote pong %X or /raw pong %X now.", me.name, nick, sptr->nospoof, sptr->nospoof); sendto_one(sptr, "PING :%X", sptr->nospoof); #endif /* NOSPOOF */ #ifdef CONTACT_EMAIL sendto_one(sptr, ":%s NOTICE %s :*** If you need assistance with a" " connection problem, please email " CONTACT_EMAIL " with the name and version of the client you are" " using, and the server you tried to connect to: %s", me.name, nick, me.name); #endif /* CONTACT_EMAIL */ #ifdef CONTACT_URL sendto_one(sptr, ":%s NOTICE %s :*** If you need assistance with" " connecting to this server, %s, please refer to: " CONTACT_URL, me.name, nick, me.name); #endif /* CONTACT_URL */ /* Copy password to the passwd field if it's given after NICK * - originally by taz, modified by Wizzu */ if ((parc > 2) && (strlen(parv[2]) <= PASSWDLEN) && !(sptr->listener->umodes & LISTENER_JAVACLIENT)) { if (sptr->passwd) MyFree(sptr->passwd); sptr->passwd = MyMalloc(strlen(parv[2]) + 1); (void)strcpy(sptr->passwd, parv[2]); } /* This had to be copied here to avoid problems.. */ (void)strcpy(sptr->name, nick); if (sptr->user && IsNotSpoof(sptr)) { /* ** USER already received, now we have NICK. ** *NOTE* For servers "NICK" *must* precede the ** user message (giving USER before NICK is possible ** only for local client connection!). register_user ** may reject the client and call exit_client for it ** --must test this and exit m_nick too!!! */ #ifndef NOSPOOF if (USE_BAN_VERSION && MyConnect(sptr)) sendto_one(sptr, ":IRC!IRC@%s PRIVMSG %s :\1VERSION\1", me.name, nick); #endif sptr->lastnick = TStime(); /* Always local client */ if (register_user(cptr, sptr, nick, sptr->user->username, NULL, NULL, NULL) == FLUSH_BUFFER) return FLUSH_BUFFER; strcpy(nick, sptr->name); /* don't ask, but I need this. do not remove! -- Syzop */ update_watch = 0; newusr = 1; } } /* * Finally set new nick name. */ if (update_watch && sptr->name[0]) { (void)del_from_client_hash_table(sptr->name, sptr); if (IsPerson(sptr)) hash_check_watch(sptr, RPL_LOGOFF); } (void)strcpy(sptr->name, nick); (void)add_to_client_hash_table(nick, sptr); if (IsServer(cptr) && parc > 7) { parv[3] = nick; do_cmd(cptr, sptr, "USER", parc - 3, &parv[3]); if (GotNetInfo(cptr) && !IsULine(sptr)) sendto_fconnectnotice(sptr->name, sptr->user, sptr, 0, NULL); } else if (IsPerson(sptr) && update_watch) hash_check_watch(sptr, RPL_LOGON); #ifdef NEWCHFLOODPROT if (sptr->user && !newusr && !IsULine(sptr)) { for (mp = sptr->user->channel; mp; mp = mp->next) { aChannel *chptr = mp->chptr; if (chptr && !(mp->flags & (CHFL_CHANOP|CHFL_VOICE|CHFL_CHANOWNER|CHFL_HALFOP|CHFL_CHANPROT)) && chptr->mode.floodprot && do_chanflood(chptr->mode.floodprot, FLD_NICK) && MyClient(sptr)) { do_chanflood_action(chptr, FLD_NICK, "nick"); } } } #endif if (newusr && !MyClient(sptr) && IsPerson(sptr)) { RunHook(HOOKTYPE_REMOTE_CONNECT, sptr); } return 0; }
DLLFUNC int m_rmtkl(aClient *cptr, aClient *sptr, int parc, char *parv[]) { aTKline *tk, *next = NULL; TKLType *tkltype; char *types, *uhmask, *cmask, *p; char gmt[256], flag; int tklindex; if (!IsULine(sptr) && !(IsPerson(sptr) && IsAnOper(sptr))) { sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]); return -1; } if (IsNotParam(1)) return dumpit(sptr, rmtkl_help); if (IsNotParam(2)) { sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "RMTKL"); sendnotice(sptr, "Type '/RMTKL' for help"); return 0; } types = parv[1]; uhmask = parv[2]; cmask = IsParam(3) ? parv[3] : NULL; /* I don't add 'q' and 'Q' here. They are different. */ if (strchr(types, '*')) types = "KzGZs"; /* check access */ if (!IsULine(sptr)) for (p = types; *p; p++) { tkltype = find_TKLType_by_flag(*p); if (!tkltype->type) continue; if (((tkltype->type & TKL_GLOBAL) && !IsOper(sptr)) || !(sptr->oflag & tkltype->oflag)) { sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]); return -1; } } for (tkltype = tkl_types; tkltype->type; tkltype++) { flag = tkltype->flag; tklindex = tkl_hash(flag); if (!strchr(types, flag)) continue; for (tk = tklines[tklindex]; tk; tk = next) { next = tk->next; if (tk->type != tkltype->type) continue; if (tk->type & TKL_NICK) { /* * If it's a services hold (ie. NickServ is holding * a nick), it's better not to touch it */ if (*tk->usermask == 'H') continue; if (match(uhmask, tk->hostmask)) continue; } else if (match(uhmask, make_user_host(tk->usermask, tk->hostmask))) continue; if (cmask && _match(cmask, tk->reason)) continue; strncpyzt(gmt, asctime(gmtime((TS *)&tk->set_at)), sizeof gmt); iCstrip(gmt); if (tk->type & TKL_NICK) { sendto_snomask(SNO_TKL, "%s removed %s %s (set at %s " "- reason: %s)", sptr->name, tkltype->txt, tk->hostmask, gmt, tk->reason); ircd_log(LOG_TKL, "%s removed %s %s (set at %s " "- reason: %s)", sptr->name, tkltype->txt, tk->hostmask, gmt, tk->reason); } else { sendto_snomask(SNO_TKL, "%s removed %s %s@%s (set at " "%s - reason: %s)", sptr->name, tkltype->txt, tk->usermask, tk->hostmask, gmt, tk->reason); ircd_log(LOG_TKL, "%s removed %s %s@%s (set at " "%s - reason: %s)", sptr->name, tkltype->txt, tk->usermask, tk->hostmask, gmt, tk->reason); } if ((tk->type & TKL_GLOBAL) && flag) sendto_serv_butone_token(&me, me.name, MSG_TKL, TOK_TKL, "- %c %s %s %s", flag, tk->usermask, tk->hostmask, parv[0]); if (tk->type & TKL_SHUN) tkl_check_local_remove_shun(tk); my_tkl_del_line(tk, tklindex); } } return 0; }
DLLFUNC int m_chghost(aClient *cptr, aClient *sptr, int parc, char *parv[]) { aClient *acptr; if (MyClient(sptr) && !IsAnOper(sptr)) { sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]); return 0; } #ifdef DISABLE_USERMOD if (MyClient(sptr)) { sendto_one(sptr, err_str(ERR_DISABLED), me.name, sptr->name, "CHGHOST", "This command is disabled on this server"); return 0; } #endif if ((parc < 3) || !*parv[2]) { sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, sptr->name, "CHGHOST"); return 0; } if (strlen(parv[2]) > (HOSTLEN)) { sendnotice(sptr, "*** ChgName Error: Requested hostname too long -- rejected."); return 0; } if (!valid_host(parv[2])) { sendnotice(sptr, "*** /ChgHost Error: A hostname may contain a-z, A-Z, 0-9, '-' & '.' - Please only use them"); return 0; } if (parv[2][0] == ':') { sendnotice(sptr, "*** A hostname cannot start with ':'"); return 0; } if ((acptr = find_person(parv[1], NULL))) { if (MyClient(sptr) && (IsLocOp(sptr) && !MyClient(acptr))) { sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]); return 0; } if (!strcmp(GetHost(acptr), parv[2])) { sendnotice(sptr, "*** /ChgHost Error: requested host is same as current host."); return 0; } switch (UHOST_ALLOWED) { case UHALLOW_NEVER: if (MyClient(sptr)) { sendto_one(sptr, err_str(ERR_DISABLED), me.name, sptr->name, "CHGHOST", "This command is disabled on this server"); return 0; } break; case UHALLOW_ALWAYS: break; case UHALLOW_NOCHANS: if (IsPerson(acptr) && MyClient(sptr) && acptr->user->joined) { sendnotice(sptr, "*** /ChgHost can not be used while %s is on a channel", acptr->name); return 0; } break; case UHALLOW_REJOIN: rejoin_doquits(acptr); /* join sent later when the host has been changed */ break; } if (!IsULine(sptr)) { sendto_snomask(SNO_EYES, "%s changed the virtual hostname of %s (%s@%s) to be %s", sptr->name, acptr->name, acptr->user->username, acptr->user->realhost, parv[2]); /* Logging added by XeRXeS */ ircd_log(LOG_CHGCMDS, "CHGHOST: %s changed the virtual hostname of %s (%s@%s) to be %s", sptr->name, acptr->name, acptr->user->username, acptr->user->realhost, parv[2]); } acptr->umodes |= UMODE_HIDE; acptr->umodes |= UMODE_SETHOST; sendto_server(cptr, 0, 0, ":%s CHGHOST %s %s", sptr->name, acptr->name, parv[2]); if (acptr->user->virthost) { MyFree(acptr->user->virthost); acptr->user->virthost = 0; } acptr->user->virthost = strdup(parv[2]); if (UHOST_ALLOWED == UHALLOW_REJOIN) rejoin_dojoinandmode(acptr); return 0; } else { sendto_one(sptr, err_str(ERR_NOSUCHNICK), me.name, sptr->name, parv[1]); return 0; } return 0; }
int m_chgident(aClient *cptr, aClient *sptr, int parc, char *parv[]) { aClient *acptr; char *s; int legalident = 1; if (MyClient(sptr) && !IsAnOper(sptr)) { sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]); return 0; } #ifdef DISABLE_USERMOD if (MyClient(sptr)) { sendto_one(sptr, err_str(ERR_DISABLED), me.name, sptr->name, "CHGIDENT", "This command is disabled on this server"); return 0; } #endif if ((parc < 3) || !*parv[2]) { sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, sptr->name, "CHGIDENT"); return 0; } if (strlen(parv[2]) > (USERLEN)) { sendnotice(sptr, "*** ChgIdent Error: Requested ident too long -- rejected."); return 0; } /* illegal?! */ for (s = parv[2]; *s; s++) { if ((*s == '~') && (s == parv[2])) continue; if (!isallowed(*s)) { legalident = 0; } } if (legalident == 0) { sendnotice(sptr, "*** /ChgIdent Error: A ident may contain a-z, A-Z, 0-9, '-' & '.' - Please only use them"); return 0; } if ((acptr = find_person(parv[1], NULL))) { DYN_LOCAL(char, did_parts, acptr->user->joined); switch (UHOST_ALLOWED) { case UHALLOW_NEVER: if (MyClient(sptr)) { sendto_one(sptr, err_str(ERR_DISABLED), me.name, sptr->name, "CHGIDENT", "This command is disabled on this server"); DYN_FREE(did_parts); return 0; } break; case UHALLOW_ALWAYS: break; case UHALLOW_NOCHANS: if (IsPerson(acptr) && MyClient(sptr) && acptr->user->joined) { sendnotice(sptr, "*** /ChgIdent can not be used while %s is on a channel", acptr->name); DYN_FREE(did_parts); return 0; } break; case UHALLOW_REJOIN: rejoin_doparts(acptr, did_parts); /* join sent later when the ident has been changed */ break; } if (!IsULine(sptr)) { sendto_snomask(SNO_EYES, "%s changed the virtual ident of %s (%s@%s) to be %s", sptr->name, acptr->name, acptr->user->username, GetHost(acptr), parv[2]); /* Logging ability added by XeRXeS */ ircd_log(LOG_CHGCMDS, "CHGIDENT: %s changed the virtual ident of %s (%s@%s) to be %s", sptr->name, acptr->name, acptr->user->username, GetHost(acptr), parv[2]); } sendto_serv_butone_token(cptr, sptr->name, MSG_CHGIDENT, TOK_CHGIDENT, "%s %s", acptr->name, parv[2]); ircsprintf(acptr->user->username, "%s", parv[2]); if (UHOST_ALLOWED == UHALLOW_REJOIN) rejoin_dojoinandmode(acptr, did_parts); DYN_FREE(did_parts); return 0; }
/* ** m_mkpasswd ** parv[0] = sender prefix ** parv[1] = password to encrypt */ int m_mkpasswd(aClient *cptr, aClient *sptr, int parc, char *parv[]) { short type; char *result = NULL; if (!MKPASSWD_FOR_EVERYONE && !IsAnOper(sptr)) { sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, sptr->name); return -1; } if (!IsAnOper(sptr)) { /* Non-opers /mkpasswd usage: lag them up, and send a notice to eyes snomask. * This notice is always sent, even in case of bad usage/bad auth methods/etc. */ sptr->since += 7; sendto_snomask(SNO_EYES, "*** /mkpasswd was used by %s (%s@%s) to create a hash.", sptr->name, sptr->user->username, GetHost(sptr)); } if ((parc < 3) || BadPtr(parv[2])) { sendto_one(sptr, ":%s NOTICE %s :*** Syntax: /mkpasswd <authmethod> <password>", me.name, sptr->name); return 0; } /* Don't want to take any risk ;p. -- Syzop */ if (strlen(parv[2]) > 64) { sendto_one(sptr, ":%s NOTICE %s :*** Your parameter (text-to-hash) is too long. Please Shorten it", me.name, sptr->name); return 0; } if ((type = Auth_FindType(parv[1])) == -1) { sendto_one(sptr, ":%s NOTICE %s :*** %s is not an enabled authentication method. SHA1 and above require an SSL compile.", me.name, sptr->name, parv[1]); return 0; } #ifdef AUTHENABLE_UNIXCRYPT if ((type == AUTHTYPE_UNIXCRYPT) && (strlen(parv[2]) > 8)) { sendnotice(sptr, "WARNING: Password truncated to 8 characters due to 'crypt' algorithm. " "You are suggested to use the 'md5' algorithm instead."); parv[2][8] = '\0'; } #endif if (!(result = Auth_Make(type, parv[2]))) { sendto_one(sptr, ":%s NOTICE %s :*** Failed to create hash using that encryption method %s", me.name, sptr->name, parv[1]); return 0; } sendto_one(sptr, ":%s %s %s :*** Your hashed password (method=%s, para=%s) is: %s", me.name, IsWebTV(sptr) ? "PRIVMSG" : "NOTICE", parv[0], parv[1], parv[2], result); return 0; }