Logo Search packages:      
Sourcecode: rageircd version File versions  Download package

m_snick.c

/*
 * RageIRCd: an advanced Internet Relay Chat daemon (ircd).
 * (C) 2000-2005 the RageIRCd Development Team, all rights reserved.
 *
 * This software is free, licensed under the General Public License.
 * Please refer to doc/LICENSE and doc/README for further details.
 *
 * $Id: m_snick.c,v 1.37.2.8 2005/07/08 23:29:49 amcwilliam Exp $
 */

#include "config.h"
#include "struct.h"
#include "common.h"
#include "sys.h"
#include "numeric.h"
#include "msg.h"
#include "channel.h"
#include "h.h"
#include "memory.h"
#include "modules.h"
#include "xmode.h"
#include <time.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>

Module MOD_HEADER(m_snick) = {
      "m_snick",
      "SNICK protocol",
      6, "$Revision: 1.37.2.8 $"
};

int MOD_LOAD(m_snick)()
{
      if (register_command(&MOD_HEADER(m_snick), &CMD_SNICK, m_snick) == NULL) {
            return MOD_FAILURE;
      }
      return MOD_SUCCESS;
}

int MOD_UNLOAD(m_snick)()
{
      return MOD_SUCCESS;
}

/*
 * m_snick
 *    parv[0] = sender prefix
 *    parv[1] = nickname
 *    parv[2] = TS or !TS
 *    parv[3] = hop
 *    parv[4] = username
 *    parv[5] = hostname
 *    parv[6] = ip or !ip
 *    parv[7] = maskedhost
 *    parv[8] = servername or !suid
 *    parv[9] = servicestamp or !servicestamp
 *    parv[10] = user modes
 *    parv[11] = gcos
 */
int m_snick(aClient *cptr, aClient *sptr, int parc, char *parv[])
{
      char *b64id = NULL, *p, ubuf[16];
      unsigned long newid, newip;
      aClient *acptr, *uplink = NULL;
      long newts, oldts;
      int sameuser = 0, mindex;

      if (!IsServer(sptr) || parc < 12) {
            return 0;
      }

      newts = get_ts(parv[2]);
      newip = get_ts(parv[6]); /* Uses the same encoding as TS */

      Debug((DEBUG_DEBUG, "SNICK[%s] got TS(%s) [%ld] and IP(%s) [%ld]", parv[1], parv[2],
            newts, parv[6], newip));

      /* Check for a nick collision. */
      do {
            if ((acptr = find_client(parv[1], NULL)) == NULL) {
                  break;
            }

            if (IsUnknown(acptr)) {
                  Debug((DEBUG_DEBUG, "SNICK collision (%s) with unknown", parv[1]));

                  if (MyConnect(acptr)) {
                        exit_client(NULL, acptr, &me, "Overridden");
                        break;
                  }

                  if (acptr->user == NULL) {
                        sendto_realops_lev(SKILL_LEV, "Nick collision on %s", parv[1]);
                        sendto_serv_kill_msg_butone(NULL, &me, acptr, ":Nick collision");
                        SetKilled(acptr);
                        return exit_client(cptr, acptr, &me, "Overridden");
                  }
            }

            Debug((DEBUG_DEBUG, "ARGH! Client collision: %s", get_client_name(acptr, FALSE)));

            ASSERT(acptr->user != NULL);

            oldts = acptr->tsinfo;

            /* Collision logic: we are either dealing with a race condition
             * (two users signing onto different servers at the same time),
             * or a server<->server synch with two identical nicknames.
             *
             * TS are equal or zero: kill both
             * Same user: kill oldest
             * Different user: kill youngest
             */

            /* TS are equal or either TS is zero -- kill both */
            if (!newts || !oldts || (newts == oldts)) {
                  ircstp->is_kill++;

                  sendto_realops_lev(SKILL_LEV, "Nick collision on %s (all nicks)", acptr->name);
                  sendto_serv_kill_msg_butone(NULL, &me, acptr, ":%s (Nick collision)", me.name);

                  SetKilled(acptr);
                  return exit_client(sptr, acptr, &me, "Nick collision");
            }

            sameuser = ((acptr->ip.s_addr == newip) && !mycmp(acptr->username, parv[4])) ? 1 : 0;

            if ((sameuser && (newts < oldts)) || (!sameuser && (newts > oldts))) {
                  /* Either:
                   *    - same users, but they're older
                   *    - diff users, but they're younger
                   * We can safely ignore this SNICK now, no kills required!
                   * The other side will exit their client and send the
                   * appropriate kills backwards. Then, it will accept the
                   * SNICK for our client.
                   */
                  Debug((DEBUG_DEBUG, "SNICK[%s] ignoring, our client won!", parv[1]));
                  return 0;
            }

            /* OK -- we have to kill our client. So be it! Send the message
             * backwards to the rest of the network.
             */
            Debug((DEBUG_DEBUG, "Nick collision on %s", parv[1]));
            ircstp->is_kill++;

            sendto_realops_lev(SKILL_LEV, "Nick collision on %s", acptr->name);
            sendto_serv_kill_msg_butone(sptr, &me, acptr, ":%s (Nick collision)", me.name);

            /* Tell our client what's going on. Exit them, and then gracefully
             * accept the remote servers SNICK.
             */
            send_me_numeric(acptr, ERR_NICKCOLLISION, acptr->name);

            SetKilled(acptr);
            exit_client(sptr, acptr, &me, "Nick collision");

            break;
      } while (0);

      /* Accept the SNICK and validate the clients server. */
      if (is_id(parv[8])) {
            b64id = parv[8];

            Debug((DEBUG_DEBUG, "SNICK[%s] got server suid [%s]", parv[1], b64id));

            if ((acptr = find_by_base64_id(b64id)) != NULL) {
                  sendto_realops_lev(DEBUG_LEV, "IDENTITIY COLLISION! (%s[%s][%s] <> "
                        "%s[%s][%s])", sptr->name, sptr->uplink->name, b64id,
                        acptr->name, acptr->uplink->name, acptr->id.string);
                  exit_client(cptr, acptr, &me, "Identity Collision");
            }
            uplink = find_serv_by_base64_id(b64id, &newid);
      }
      else {
            uplink = find_server(parv[8], NULL);
      }
      if (uplink == NULL) {
            sendto_realops_lev(DEBUG_LEV, "Unknown server in SNICK for %s", parv[1]);
            return 0;
      }

      Debug((DEBUG_DEBUG, "SNICK[%s] building client", parv[1]));

      acptr = sptr;
      sptr = make_client(cptr, uplink);

      if (IsULine(uplink)) {
            SetULine(sptr); /* If the uplink is U:lined, the client is also U:lined */
      }

      add_client_to_list(sptr);
      strcpy(sptr->name, parv[1]);
      sptr->hopcount = atoi(parv[3]);
      sptr->tsinfo = newts;
      sptr->ip.s_addr = newip;
      inetntop(&sptr->ip, sptr->hostip, HOSTIPLEN + 1);

      if (b64id != NULL) {
            sptr->id.id = newid;
            strncpyzt(sptr->id.string, b64id, IDLEN + 1);
            add_userid_to_serv(uplink, sptr);
            SetSUID(sptr);
      }

      add_to_client_hash_table(parv[1], sptr);

      strncpyzt(sptr->username, parv[4], USERLEN + 1);
      strncpyzt(sptr->host, parv[5], HOSTLEN + 1);
      strncpyzt(sptr->info, parv[11], REALLEN + 1);

      make_user(sptr);
      sptr->user->server = find_or_add(uplink->name);
      sptr->user->servicestamp = get_ts(parv[9]);

      SetClient(sptr);

      /* Parse the specified user modes */
      for (p = parv[10]; *p != '\0'; p++) {
            if (*p == '-' || (*p == '+')) {
                  continue; /* Skip + and - flags */
            }

            /* Loop through the table to find the flag */
            /* We now use the index map, no loop required! */
            if ((mindex = usermodes->map[(unsigned char)*p]) > -1) {
                  /* Add it, if it's global */
                  sptr->umode |= (usermodes->table[mindex].mode & SEND_UMODES);
            }
      }
      
      /* SNICK is used to distribute new local clients across the network */
      if (GotNBurst(cptr) && GotNBurst(uplink) && !IsULine(sptr)) {
            /* gcliconn notices for clients on pre-2.0.1 servers will not be sent */
            sendto_realops_lev(GCCONN_LEV, "Client connecting at %s: %s (%s@%s)",
                  sptr->uplink->name, sptr->name, sptr->username, sptr->host);
      }

      /* Update internal counters */
      if (++Count.total > Count.max_tot) {
            Count.max_tot = Count.total;
      }
      if (HasMode(sptr, UMODE_OPER)) {
            Count.oper++;
      }
      if (HasMode(sptr, UMODE_INVISIBLE)) {
            Count.invisi++;
      }
      
      if (HasMode(sptr, UMODE_MASKED)) {
            if ((*(parv[7]) == '*') && (*(parv[7] + 1) == '\0')) {
                  DelMode(sptr, UMODE_MASKED);
            }
            else {
                  strncpyzt(sptr->user->maskedhost, parv[7], HOSTLEN + 1);
            }
      }

      /* Get what user modes the user now has, in relation to SEND_UMODES */
      send_umode(NULL, sptr, 0, SEND_UMODES, ubuf);
      if (*ubuf == '\0') {
            *ubuf = '+';
            *(ubuf + 1) = '\0';
      }

      hash_check_watch(sptr, RPL_LOGON);
      introduce_user(cptr, sptr, ubuf);

      return 0;
}

Generated by  Doxygen 1.6.0   Back to index