Add a parameter to IPcheck_connect_fail() to support IAuth IP spoofing.
[ircu2.10.12-pk.git] / ircd / m_nick.c
index 06287ddd5b36fa512a43a93c86b60584173c4b1b..b612f8672b9e0cd42c4002b41c81c69589d04663 100644 (file)
 #include "hash.h"
 #include "ircd.h"
 #include "ircd_chattr.h"
+#include "ircd_features.h"
+#include "ircd_log.h"
 #include "ircd_reply.h"
 #include "ircd_string.h"
-#include "ircd_policy.h"
 #include "msg.h"
 #include "numeric.h"
 #include "numnicks.h"
 #include "s_misc.h"
 #include "s_user.h"
 #include "send.h"
+#include "sys.h"
 
-#include <assert.h>
+/* #include <assert.h> -- Now using assert in ircd_log.h */
 #include <stdlib.h>
 #include <string.h>
 
+ /*
+* 'do_nick_name' ensures that the given parameter (nick) is really a proper
+* string for a nickname (note, the 'nick' may be modified in the process...)
+*
+* RETURNS the length of the final NICKNAME (0, if nickname is invalid)
+*
+* Nickname characters are in range 'A'..'}', '_', '-', '0'..'9'
+*  anything outside the above set will terminate nickname.
+* In addition, the first character cannot be '-' or a Digit.
+*
+* Note:
+*  The '~'-character should be allowed, but a change should be global,
+*  some confusion would result if only few servers allowed it...
+*/
+static int do_nick_name(char* nick)
+{
+  char* ch  = nick;
+  char* end = ch + NICKLEN;
+  assert(0 != ch);
+  
+  /* first character in [0..9-] */
+  if (*ch == '-' || IsDigit(*ch))
+    return 0;
+  for ( ; (ch < end) && *ch; ++ch)
+    if (!IsNickChar(*ch))
+      break;
+
+  *ch = '\0';
+
+  return (ch - nick);
+}
+
 /*
  * m_nick - message handler for local clients
  * parv[0] = sender prefix
@@ -112,27 +146,25 @@ int m_nick(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
   char           nick[NICKLEN + 2];
   char*          arg;
   char*          s;
-  const char*    client_name;
 
   assert(0 != cptr);
   assert(cptr == sptr);
 
-  /*
-   * parv[0] will be empty for clients connecting for the first time
-   */
-  client_name = (*(cli_name(sptr))) ? cli_name(sptr) : "*";
+  if (IsServerPort(cptr))
+    return exit_client(cptr, cptr, &me, "Use a different port");
 
   if (parc < 2) {
     send_reply(sptr, ERR_NONICKNAMEGIVEN);
     return 0;
   }
+
   /*
    * Don't let them send make us send back a really long string of
    * garbage
    */
   arg = parv[1];
-  if (strlen(arg) > NICKLEN)
-    arg[NICKLEN] = '\0';
+  if (strlen(arg) > IRCD_MIN(NICKLEN, feature_int(FEAT_NICKLEN)))
+    arg[IRCD_MIN(NICKLEN, feature_int(FEAT_NICKLEN))] = '\0';
 
   if ((s = strchr(arg, '~')))
     *s = '\0';
@@ -140,10 +172,7 @@ int m_nick(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
   strcpy(nick, arg);
 
   /*
-   * If do_nick_name() returns a null name OR if the server sent a nick
-   * name and do_nick_name() changed it in some way (due to rules of nick
-   * creation) then reject it. If from a server and we reject it,
-   * and KILL it. -avalon 4/4/92
+   * If do_nick_name() returns a null name then reject it.
    */
   if (0 == do_nick_name(nick)) {
     send_reply(sptr, ERR_ERRONEUSNICKNAME, arg);
@@ -209,10 +238,15 @@ int m_nick(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
    * "dormant nick" way of generating collisions...
    *
    * XXX - hmmm can this happen after one is registered?
+   *
+   * Yes, client 1 connects to IRC and registers, client 2 connects and
+   * sends "NICK foo" but doesn't send anything more.  client 1 now does
+   * /nick foo, they should succeed and client 2 gets disconnected with
+   * the message below.
    */
   if (IsUnknown(acptr) && MyConnect(acptr)) {
-    ++ServerStats->is_ref;
-    IPcheck_connect_fail(cli_ip(acptr));
+    ServerStats->is_ref++;
+    IPcheck_connect_fail(acptr, 0);
     exit_client(cptr, acptr, &me, "Overridden by other sign on");
     return set_nick_name(cptr, sptr, nick, parc, parv);
   }
@@ -250,7 +284,7 @@ int ms_nick(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
   char nick[NICKLEN + 2];
   time_t lastnick = 0;
   int differ = 1;
-  char *type;
+  const char *type;
 
   assert(0 != cptr);
   assert(0 != sptr);
@@ -284,7 +318,7 @@ int ms_nick(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
    * creation) then reject it. If from a server and we reject it,
    * and KILL it. -avalon 4/4/92
    */
-  if (strlen(nick) != do_nick_name(nick))
+  if (!do_nick_name(nick) || strcmp(nick, parv[1]))
   {
     send_reply(sptr, ERR_ERRONEUSNICKNAME, parv[1]);
     
@@ -328,10 +362,21 @@ int ms_nick(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
       return 0;
   }
   /* now we know we have a real collision. */
+  /*
+   * Note: From this point forward it can be assumed that
+   * acptr != sptr (point to different client structures).
+   */
+  assert(acptr != sptr);
+  /*
+   * 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...
+   */
   if (IsUnknown(acptr) && MyConnect(acptr))
   {
     ServerStats->is_ref++;
-    IPcheck_connect_fail(cli_ip(acptr));
+    IPcheck_connect_fail(acptr, 0);
     exit_client(cptr, acptr, &me, "Overridden by other sign on");
     return set_nick_name(cptr, sptr, nick, parc, parv);
   }
@@ -355,13 +400,15 @@ int ms_nick(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
    */
   if (IsServer(sptr))
   {
+    struct irc_in_addr ip;
     /*
      * A new NICK being introduced by a neighbouring
      * server (e.g. message type ":server NICK new ..." received)
      *
      * compare IP address and username
      */
-    differ =  (cli_ip(acptr).s_addr != htonl(base64toint(parv[parc - 3]))) ||
+    base64toip(parv[parc - 3], &ip);
+    differ =  (0 != memcmp(&cli_ip(acptr), &ip, sizeof(cli_ip(acptr)))) ||
               (0 != ircd_strcmp(cli_user(acptr)->username, parv[4]));
     sendto_opmask_butone(0, SNO_OLDSNO, "Nick collision on %C (%C %Tu <- "
                         "%C %Tu (%s user@host))", acptr, cli_from(acptr),
@@ -375,13 +422,13 @@ int ms_nick(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
      *
      * compare IP address and username
      */
-    differ =  (cli_ip(acptr).s_addr != cli_ip(sptr).s_addr) ||
-              (0 != ircd_strcmp(cli_user(acptr)->username, cli_user(sptr)->username));              
+    differ =  (0 != memcmp(&cli_ip(acptr), &cli_ip(sptr), sizeof(cli_ip(acptr)))) ||
+              (0 != ircd_strcmp(cli_user(acptr)->username, cli_user(sptr)->username));
     sendto_opmask_butone(0, SNO_OLDSNO, "Nick change collision from %C to "
                         "%C (%C %Tu <- %C %Tu)", sptr, acptr, cli_from(acptr),
                         cli_lastnick(acptr), cptr, lastnick);
   }
-  type = differ ? "older nick overruled" : "nick collision from same user@host";
+  type = differ ? "overruled by older nick" : "nick collision from same user@host";
   /*
    * Now remove (kill) the nick on our side if it is the youngest.
    * If no timestamp was received, we ignore the incoming nick
@@ -394,40 +441,32 @@ int ms_nick(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
   if ((differ && lastnick >= cli_lastnick(acptr)) ||
       (!differ && lastnick <= cli_lastnick(acptr)))
   {
-    /* We need to bounce this kill straight back... Although the nick message
-     * for acptr is probably waiting in their recvq from me, its also possible
-     * that sptr will change their nick on cptr before cptr receives the
-     * nick message for acptr, which would leave acptr and sptr both alive
-     * on cptr, but only acptr alive on me, i.e. desync. This extra kill
-     * message has been absent for a while in ircu although it was a major
-     * problem when it was tried on efnet, so I don't know how big an issue it
-     * is. Probably best that this be left here, anyway...
-     */
     ServerStats->is_kill++;
-    sendcmdto_one(&me, CMD_KILL, cptr, "%s :%s (%s)",
-                  nick, cli_name(&me), type);
-    /* But if this was a nick change and not a nick introduction,
-     * we also need to ensure that we remove our local state
-     * record of the original client... Also, the rest of the
-     * net should be informed...
-     */
     if (!IsServer(sptr))
     {
+      /* If this was a nick change and not a nick introduction, we
+       * need to ensure that we remove our record of the client, and
+       * send a KILL to the whole network.
+       */
       assert(!MyConnect(sptr));
       /* Inform the rest of the net... */
-      sendcmdto_serv_butone(&me, CMD_KILL, cptr, "%s :%s (%s)",
-                            nick, cli_name(&me), type);
+      sendcmdto_serv_butone(&me, CMD_KILL, 0, "%C :%s (%s)",
+                            sptr, cli_name(&me), type);
       /* Don't go sending off a QUIT message... */
-      cli_flags(sptr) |= FLAGS_KILLED;
+      SetFlag(sptr, FLAG_KILLED);
       /* Remove them locally. */
       exit_client_msg(cptr, sptr, &me,
-                      "Killed (" HEAD_IN_SAND_SERVERNAME " (Nick collision))",
-                      type);
-      /*
-       * We have killed sptr off, zero out it's pointer so if it's used
-       * again we'll know about it --Bleep
+                      "Killed (%s (%s))",
+                      feature_str(FEAT_HIS_SERVERNAME), type);
+    }
+    else
+    {
+      /* If the origin is a server, this was a new client, so we only
+       * send the KILL in the direction it came from.  We have no
+       * client record that we would have to clean up.
        */
-      sptr = NULL;
+      sendcmdto_one(&me, CMD_KILL, cptr, "%s :%s (%s)",
+                    parv[parc - 2], cli_name(&me), type);
     }
     /* If the timestamps differ and we just killed sptr, we don't need to kill
      * acptr as well.
@@ -439,14 +478,15 @@ int ms_nick(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
   send_reply(acptr, ERR_NICKCOLLISION, nick);
 
   ServerStats->is_kill++;
-  cli_flags(acptr) |= FLAGS_KILLED;
+  SetFlag(acptr, FLAG_KILLED);
   /*
    * This exits the client we had before getting the NICK message
    */
-  sendcmdto_serv_butone(&me, CMD_KILL, NULL, "%C :" HEAD_IN_SAND_SERVERNAME
-                        " (%s)", acptr, type);
-  exit_client_msg(cptr, acptr, &me, "Killed (" HEAD_IN_SAND_SERVERNAME " (%s))",
-                  type);
+  sendcmdto_serv_butone(&me, CMD_KILL, NULL, "%C :%s (%s)",
+                        acptr, feature_str(FEAT_HIS_SERVERNAME),
+                        type);
+  exit_client_msg(cptr, acptr, &me, "Killed (%s (%s))",
+                  feature_str(FEAT_HIS_SERVERNAME), type);
   if (lastnick == cli_lastnick(acptr))
     return 0;
   if (sptr == NULL)