2 * IRC - Internet Relay Chat, ircd/m_nick.c
3 * Copyright (C) 1990 Jarkko Oikarinen and
4 * University of Oulu, Computing Center
6 * See file AUTHORS in IRC package for additional names of
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 1, or (at your option)
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27 * m_functions execute protocol messages on this server:
29 * cptr is always NON-NULL, pointing to a *LOCAL* client
30 * structure (with an open socket connected!). This
31 * identifies the physical socket where the message
32 * originated (or which caused the m_function to be
33 * executed--some m_functions may call others...).
35 * sptr is the source of the message, defined by the
36 * prefix part of the message if present. If not
37 * or prefix not found, then sptr==cptr.
39 * (!IsServer(cptr)) => (cptr == sptr), because
40 * prefixes are taken *only* from servers...
43 * (sptr == cptr) => the message didn't
46 * (sptr != cptr && IsServer(sptr) means
47 * the prefix specified servername. (?)
49 * (sptr != cptr && !IsServer(sptr) means
50 * that message originated from a remote
55 * (!IsServer(sptr)) means that, sptr can safely
56 * taken as defining the target structure of the
57 * message in this server.
59 * *Always* true (if 'parse' and others are working correct):
61 * 1) sptr->from == cptr (note: cptr->from == cptr)
63 * 2) MyConnect(sptr) <=> sptr == cptr (e.g. sptr
64 * *cannot* be a local connection, unless it's
65 * actually cptr!). [MyConnect(x) should probably
66 * be defined as (x == x->from) --msa ]
68 * parc number of variable parameter strings (if zero,
69 * parv is allowed to be NULL)
71 * parv a NULL terminated list of parameter pointers,
73 * parv[0], sender (prefix string), if not present
74 * this points to an empty string.
75 * parv[1]...parv[parc-1]
76 * pointers to additional parameters
77 * parv[parc] == NULL, *always*
79 * note: it is guaranteed that parv[0]..parv[parc-1] are all
88 #include "ircd_chattr.h"
89 #include "ircd_reply.h"
90 #include "ircd_string.h"
104 * m_nick - message handler for local clients
105 * parv[0] = sender prefix
108 int m_nick(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
110 struct Client* acptr;
111 char nick[NICKLEN + 2];
114 const char* client_name;
117 assert(cptr == sptr);
120 * parv[0] will be empty for clients connecting for the first time
122 client_name = (*(cli_name(sptr))) ? cli_name(sptr) : "*";
125 send_reply(sptr, ERR_NONICKNAMEGIVEN);
129 * Don't let them send make us send back a really long string of
133 if (strlen(arg) > NICKLEN)
136 if ((s = strchr(arg, '~')))
142 * If do_nick_name() returns a null name OR if the server sent a nick
143 * name and do_nick_name() changed it in some way (due to rules of nick
144 * creation) then reject it. If from a server and we reject it,
145 * and KILL it. -avalon 4/4/92
147 if (0 == do_nick_name(nick)) {
148 send_reply(sptr, ERR_ERRONEUSNICKNAME, arg);
153 * Check if this is a LOCAL user trying to use a reserved (Juped)
154 * nick, if so tell him that it's a nick in use...
156 if (isNickJuped(nick)) {
157 send_reply(sptr, ERR_NICKNAMEINUSE, nick);
158 return 0; /* NICK message ignored */
161 if (!(acptr = FindClient(nick))) {
163 * No collisions, all clear...
165 return set_nick_name(cptr, sptr, nick, parc, parv);
167 if (IsServer(acptr)) {
168 send_reply(sptr, ERR_NICKNAMEINUSE, nick);
169 return 0; /* NICK message ignored */
172 * If acptr == sptr, then we have a client doing a nick
173 * change between *equivalent* nicknames as far as server
174 * is concerned (user is changing the case of his/her
175 * nickname or somesuch)
179 * If acptr == sptr, then we have a client doing a nick
180 * change between *equivalent* nicknames as far as server
181 * is concerned (user is changing the case of his/her
182 * nickname or somesuch)
184 if (0 != strcmp(cli_name(acptr), nick)) {
186 * Allows change of case in his/her nick
188 return set_nick_name(cptr, sptr, nick, parc, parv);
191 * This is just ':old NICK old' type thing.
192 * Just forget the whole thing here. There is
193 * no point forwarding it to anywhere,
194 * especially since servers prior to this
195 * version would treat it as nick collision.
200 * Note: From this point forward it can be assumed that
201 * acptr != sptr (point to different client structures).
203 assert(acptr != sptr);
205 * If the older one is "non-person", the new entry is just
206 * allowed to overwrite it. Just silently drop non-person,
207 * and proceed with the nick. This should take care of the
208 * "dormant nick" way of generating collisions...
210 * XXX - hmmm can this happen after one is registered?
212 if (IsUnknown(acptr) && MyConnect(acptr)) {
213 ++ServerStats->is_ref;
214 IPcheck_connect_fail(cli_ip(acptr));
215 exit_client(cptr, acptr, &me, "Overridden by other sign on");
216 return set_nick_name(cptr, sptr, nick, parc, parv);
219 * NICK is coming from local client connection. Just
220 * send error reply and ignore the command.
222 send_reply(sptr, ERR_NICKNAMEINUSE, nick);
223 return 0; /* NICK message ignored */
228 * ms_nick - server message handler for nicks
229 * parv[0] = sender prefix
232 * If from server, source is client:
233 * parv[2] = timestamp
237 * parv[3] = timestamp
240 * parv[6] = umode (optional)
241 * parv[parc-3] = IP# <- Only Protocol >= 10
242 * parv[parc-2] = YXX, numeric nick <- Only Protocol >= 10
243 * parv[parc-1] = info
246 int ms_nick(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
248 struct Client* acptr;
249 char nick[NICKLEN + 2];
255 assert(IsServer(cptr));
257 if ((IsServer(sptr) && parc < 8) || parc < 3) {
258 sendto_opmask_butone(0, SNO_OLDSNO, "bad NICK param count for %s from %C",
260 return need_more_params(sptr, "NICK");
263 ircd_strncpy(nick, parv[1], NICKLEN);
264 nick[NICKLEN] = '\0';
266 if (IsServer(sptr)) {
267 lastnick = atoi(parv[3]);
268 if (lastnick > OLDEST_TS && !IsBurstOrBurstAck(sptr))
269 cli_serv(sptr)->lag = TStime() - lastnick;
272 lastnick = atoi(parv[2]);
273 if (lastnick > OLDEST_TS && !IsBurstOrBurstAck(sptr))
274 cli_serv(cli_user(sptr)->server)->lag = TStime() - lastnick;
277 * If do_nick_name() returns a null name OR if the server sent a nick
278 * name and do_nick_name() changed it in some way (due to rules of nick
279 * creation) then reject it. If from a server and we reject it,
280 * and KILL it. -avalon 4/4/92
282 if (0 == do_nick_name(nick) || 0 != strcmp(nick, parv[1])) {
283 send_reply(sptr, ERR_ERRONEUSNICKNAME, parv[1]);
285 ++ServerStats->is_kill;
286 sendto_opmask_butone(0, SNO_OLDSNO, "Bad Nick: %s From: %s %C", parv[1],
288 sendcmdto_one(&me, CMD_KILL, cptr, "%s :%s (%s <- %s[%s])",
289 IsServer(sptr) ? parv[parc - 2] : parv[0], cli_name(&me), parv[1],
290 nick, cli_name(cptr));
291 if (!IsServer(sptr)) {
295 sendcmdto_serv_butone(&me, CMD_KILL, 0, "%s :%s (%s <- %s!%s@%s)",
296 parv[0], cli_name(&me), cli_name(cptr), parv[0],
297 cli_user(sptr) ? cli_username(sptr) : "",
298 cli_user(sptr) ? cli_name(cli_user(sptr)->server) :
304 * Check against nick name collisions.
306 * Put this 'if' here so that the nesting goes nicely on the screen :)
307 * We check against server name list before determining if the nickname
308 * is present in the nicklist (due to the way the below for loop is
309 * constructed). -avalon
312 acptr = FindClient(nick);
315 * No collisions, all clear...
317 return set_nick_name(cptr, sptr, nick, parc, parv);
321 if (IsServer(acptr)) { /* shouldn't even happen, actually */
323 * We have a nickname trying to use the same name as
324 * a server. Send out a nick collision KILL to remove
325 * the nickname. As long as only a KILL is sent out,
326 * there is no danger of the server being disconnected.
327 * Ultimate way to jupiter a nick ? >;-). -avalon
329 sendto_opmask_butone(0, SNO_OLDSNO, "Nick collision on %C(%C <- %C)", sptr,
330 cli_from(acptr), cptr);
331 ++ServerStats->is_kill;
333 sendcmdto_one(&me, CMD_KILL, cptr, "%C :%s (%s <- %s)", sptr, cli_name(&me),
334 cli_name(cli_from(acptr)), cli_name(cptr));
336 cli_flags(sptr) |= FLAGS_KILLED;
338 * if sptr is a server it is exited here, nothing else to do
340 return exit_client_msg(cptr, sptr, &me,
341 "Killed (*.undernet.org (%s <- %s))",
342 cli_name(cli_from(acptr)), cli_name(cptr));
346 * If acptr == sptr, then we have a client doing a nick
347 * change between *equivalent* nicknames as far as server
348 * is concerned (user is changing the case of his/her
349 * nickname or somesuch)
352 if (strcmp(cli_name(acptr), nick) != 0)
354 * Allows change of case in his/her nick
356 return set_nick_name(cptr, sptr, nick, parc, parv);
359 * This is just ':old NICK old' type thing.
360 * Just forget the whole thing here. There is
361 * no point forwarding it to anywhere,
362 * especially since servers prior to this
363 * version would treat it as nick collision.
365 return 0; /* NICK Message ignored */
369 * Note: From this point forward it can be assumed that
370 * acptr != sptr (point to different client structures).
372 assert(acptr != sptr);
374 * If the older one is "non-person", the new entry is just
375 * allowed to overwrite it. Just silently drop non-person,
376 * and proceed with the nick. This should take care of the
377 * "dormant nick" way of generating collisions...
379 if (IsUnknown(acptr) && MyConnect(acptr)) {
380 ++ServerStats->is_ref;
381 IPcheck_connect_fail(cli_ip(acptr));
382 exit_client(cptr, acptr, &me, "Overridden by other sign on");
383 return set_nick_name(cptr, sptr, nick, parc, parv);
386 * Decide, we really have a nick collision and deal with it
389 * NICK was coming from a server connection.
390 * This means we have a race condition (two users signing on
391 * at the same time), or two net fragments reconnecting with the same nick.
392 * The latter can happen because two different users connected
393 * or because one and the same user switched server during a net break.
394 * If the TimeStamps are equal, we kill both (or only 'new'
395 * if it was a ":server NICK new ...").
396 * Otherwise we kill the youngest when user@host differ,
397 * or the oldest when they are the same.
398 * We treat user and ~user as different, because if it wasn't
399 * a faked ~user the AUTH wouldn't have added the '~'.
403 if (IsServer(sptr)) {
405 * A new NICK being introduced by a neighbouring
406 * server (e.g. message type ":server NICK new ..." received)
408 * compare IP address and username
410 differ = (cli_ip(acptr).s_addr != htonl(base64toint(parv[parc - 3]))) ||
411 (0 != ircd_strcmp(cli_user(acptr)->username, parv[4]));
412 sendto_opmask_butone(0, SNO_OLDSNO, "Nick collision on %C (%C %Tu <- "
413 "%C %Tu (%s user@host))", acptr, cli_from(acptr),
414 cli_lastnick(acptr), cptr, lastnick,
415 differ ? "Different" : "Same");
419 * A NICK change has collided (e.g. message type ":old NICK new").
421 * compare IP address and username
423 differ = (cli_ip(acptr).s_addr != cli_ip(sptr).s_addr) ||
424 (0 != ircd_strcmp(cli_user(acptr)->username, cli_user(sptr)->username));
425 sendto_opmask_butone(0, SNO_OLDSNO, "Nick change collision from %C to "
426 "%C (%C %Tu <- %C %Tu)", sptr, acptr, cli_from(acptr),
427 cli_lastnick(acptr), cptr, lastnick);
430 * Now remove (kill) the nick on our side if it is the youngest.
431 * If no timestamp was received, we ignore the incoming nick
432 * (and expect a KILL for our legit nick soon ):
433 * When the timestamps are equal we kill both nicks. --Run
434 * acptr->from != cptr should *always* be true (?).
436 * This exits the client sending the NICK message
438 if (cli_from(acptr) != cptr) {
439 if ((differ && lastnick >= cli_lastnick(acptr)) ||
440 (!differ && lastnick <= cli_lastnick(acptr))) {
441 if (!IsServer(sptr)) {
442 ++ServerStats->is_kill;
443 sendcmdto_serv_butone(&me, CMD_KILL, sptr, "%C :%s (Nick collision)",
444 sptr, cli_name(&me));
445 assert(!MyConnect(sptr));
447 cli_flags(sptr) |= FLAGS_KILLED;
448 exit_client(cptr, sptr, &me,
449 "Killed (*.undernet.org (Nick collision))");
451 * we have killed sptr off, zero out it's pointer so if it's used
452 * again we'll know about it --Bleep
456 if (lastnick != cli_lastnick(acptr))
457 return 0; /* Ignore the NICK */
459 send_reply(acptr, ERR_NICKCOLLISION, nick);
462 ++ServerStats->is_kill;
463 cli_flags(acptr) |= FLAGS_KILLED;
465 * This exits the client we had before getting the NICK message
468 sendcmdto_serv_butone(&me, CMD_KILL, acptr, "%C :%s (older nick "
469 "overruled)", acptr, cli_name(&me));
470 if (MyConnect(acptr)) {
471 sendcmdto_one(acptr, CMD_QUIT, cptr, ":Killed (*.undernet.org (older "
473 sendcmdto_one(&me, CMD_KILL, acptr, "%C :*.undernet.org (older nick "
474 "overruled)", acptr);
476 exit_client(cptr, acptr, &me, "Killed (*.undernet.org (older nick "
480 sendcmdto_serv_butone(&me, CMD_KILL, acptr, "%C :%s (nick collision from "
481 "same user@host)", acptr, cli_name(&me));
482 if (MyConnect(acptr)) {
483 sendcmdto_one(acptr, CMD_QUIT, cptr, ":Killed (*.undernet.org (nick "
484 "collision from same user@host))");
485 sendcmdto_one(&me, CMD_KILL, acptr, "%C :*.undernet.org (older nick "
486 "overruled)", acptr);
488 exit_client(cptr, acptr, &me, "Killed (*.undernet.org (nick collision "
489 "from same user@host))");
491 if (lastnick == cli_lastnick(acptr))
495 return set_nick_name(cptr, sptr, nick, parc, parv);