mirror of
https://github.com/unrealircd/unrealircd.git
synced 2026-06-29 20:06:37 +02:00
1550 lines
47 KiB
C
1550 lines
47 KiB
C
/*
|
|
* IRC - Internet Relay Chat, src/modules/m_nick.c
|
|
* (C) 1999-2005 The UnrealIRCd Team
|
|
*
|
|
* See file AUTHORS in IRC package for additional names of
|
|
* the programmers.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 1, or (at your option)
|
|
* any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*/
|
|
#include "config.h"
|
|
#include "struct.h"
|
|
#include "common.h"
|
|
#include "sys.h"
|
|
#include "numeric.h"
|
|
#include "msg.h"
|
|
#include "proto.h"
|
|
#include "channel.h"
|
|
#include <time.h>
|
|
#include <sys/stat.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#ifdef _WIN32
|
|
#include <io.h>
|
|
#endif
|
|
#include <fcntl.h>
|
|
#include "h.h"
|
|
#ifdef _WIN32
|
|
#include "version.h"
|
|
#endif
|
|
|
|
DLLFUNC CMD_FUNC(m_nick);
|
|
DLLFUNC CMD_FUNC(m_uid);
|
|
DLLFUNC int _register_user(aClient *cptr, aClient *sptr, char *nick, char *username, char *umode, char *virthost, char *ip);
|
|
|
|
#define MSG_NICK "NICK"
|
|
|
|
ModuleHeader MOD_HEADER(m_nick)
|
|
= {
|
|
"m_nick",
|
|
"$Id$",
|
|
"command /nick",
|
|
"3.2-b8-1",
|
|
NULL
|
|
};
|
|
|
|
DLLFUNC int MOD_TEST(m_nick)(ModuleInfo *modinfo)
|
|
{
|
|
MARK_AS_OFFICIAL_MODULE(modinfo);
|
|
EfunctionAdd(modinfo->handle, EFUNC_REGISTER_USER, _register_user);
|
|
return MOD_SUCCESS;
|
|
}
|
|
|
|
DLLFUNC int MOD_INIT(m_nick)(ModuleInfo *modinfo)
|
|
{
|
|
CommandAdd(modinfo->handle, MSG_NICK, m_nick, MAXPARA, M_USER|M_SERVER|M_UNREGISTERED);
|
|
CommandAdd(modinfo->handle, "UID", m_uid, MAXPARA, M_SERVER);
|
|
MARK_AS_OFFICIAL_MODULE(modinfo);
|
|
return MOD_SUCCESS;
|
|
}
|
|
|
|
DLLFUNC int MOD_LOAD(m_nick)(int module_load)
|
|
{
|
|
return MOD_SUCCESS;
|
|
}
|
|
|
|
DLLFUNC int MOD_UNLOAD(m_nick)(int module_unload)
|
|
{
|
|
return MOD_SUCCESS;
|
|
}
|
|
|
|
static char buf[BUFSIZE];
|
|
static char spamfilter_user[NICKLEN + USERLEN + HOSTLEN + REALLEN + 64];
|
|
|
|
/*
|
|
** m_uid
|
|
** parv[0] = sender prefix
|
|
** parv[1] = nickname
|
|
** parv[2] = hopcount
|
|
** parv[3] = timestamp
|
|
** parv[4] = username
|
|
** parv[5] = hostname
|
|
** parv[6] = UID
|
|
** parv[7] = servicestamp
|
|
** parv[8] = umodes
|
|
** parv[9] = virthost, * if none
|
|
** parv[10] = cloaked host, * if none
|
|
** parv[11] = ip
|
|
** parv[12] = info
|
|
*/
|
|
DLLFUNC CMD_FUNC(m_uid)
|
|
{
|
|
aTKline *tklban;
|
|
int ishold;
|
|
aClient *acptr, *serv = NULL;
|
|
aClient *acptrs;
|
|
char nick[NICKLEN + 2], *s, descbuf[BUFSIZE];
|
|
Membership *mp;
|
|
long lastnick = 0l;
|
|
int differ = 1, update_watch = 1;
|
|
unsigned char removemoder = 1;
|
|
|
|
if (!IsServer(cptr))
|
|
strlcpy(nick, parv[1], iConf.nicklen + 1);
|
|
else
|
|
strlcpy(nick, parv[1], NICKLEN + 1);
|
|
|
|
/* 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))
|
|
{
|
|
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_server(cptr, 0, 0,
|
|
":%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(parv[0], NULL))
|
|
|| serv->from != cptr->from)))
|
|
{
|
|
sendto_realops("Cannot find SID %s (%s)", parv[0],
|
|
backupbuf);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
** 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 (!IsULine(sptr) && (tklban = find_qline(sptr, nick, &ishold)))
|
|
{
|
|
if (IsServer(sptr) && !ishold) /* server introducing new client */
|
|
{
|
|
acptrs =
|
|
(aClient *)find_server(sptr->user ==
|
|
NULL ? (char *)parv[6] : (char *)sptr->user->
|
|
server, NULL);
|
|
/* (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>");
|
|
}
|
|
}
|
|
|
|
/*
|
|
** acptr already has result from previous find_server()
|
|
*/
|
|
if ((acptr = find_server(nick, NULL)) != NULL)
|
|
{
|
|
/*
|
|
** 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 ((acptr = find_client(nick, NULL)) != NULL)
|
|
{
|
|
/*
|
|
** A new NICK being introduced by a neighbouring
|
|
** server (e.g. message type "NICK new" received)
|
|
*/
|
|
if (parc > 3)
|
|
{
|
|
lastnick = atol(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_server(NULL, 0, 0,
|
|
":%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_server(cptr, 0, 0,
|
|
":%s KILL %s :%s (Nick Collision)",
|
|
me.name, acptr->name, me.name);
|
|
acptr->flags |= FLAGS_KILLED;
|
|
(void)exit_client(NULL, acptr, &me, "Nick collision");
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
if (IsServer(sptr))
|
|
{
|
|
/* A server introducing a new client, change source */
|
|
|
|
sptr = make_client(cptr, serv);
|
|
strlcpy(sptr->id, parv[6], IDLEN);
|
|
add_client_to_list(sptr);
|
|
add_to_id_hash_table(sptr->id, sptr);
|
|
if (parc > 2)
|
|
sptr->hopcount = atol(parv[2]);
|
|
if (parc > 3)
|
|
sptr->lastnick = atol(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();
|
|
}
|
|
}
|
|
|
|
(void)strlcpy(sptr->name, nick, NICKLEN);
|
|
(void)add_to_client_hash_table(nick, sptr);
|
|
|
|
if (IsServer(cptr) && parc > 7)
|
|
{
|
|
/* XXX: we need to split this out into register_remote_user() or something. */
|
|
parv[3] = nick;
|
|
parv[6] = parv[0];
|
|
do_cmd(cptr, sptr, "USER", parc - 3, &parv[3]);
|
|
if (GotNetInfo(cptr) && !IsULine(sptr))
|
|
sendto_fconnectnotice(sptr->name, sptr->user, sptr, 0, NULL);
|
|
}
|
|
|
|
RunHook(HOOKTYPE_REMOTE_CONNECT, sptr);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
** 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, descbuf[BUFSIZE];
|
|
Membership *mp;
|
|
long lastnick = 0l;
|
|
int differ = 1, update_watch = 1;
|
|
unsigned char newusr = 0, removemoder = 1;
|
|
Hook *h;
|
|
int i;
|
|
/*
|
|
* 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;
|
|
}
|
|
|
|
if (!IsServer(cptr))
|
|
strlcpy(nick, parv[1], iConf.nicklen + 1);
|
|
else
|
|
strlcpy(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_server(cptr, 0, 0,
|
|
":%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(parv[6], NULL))
|
|
|| 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 fuck 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(sptr->user ==
|
|
NULL ? (char *)parv[6] : (char *)sptr->user->
|
|
server, NULL);
|
|
/* (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 their
|
|
** nickname or somesuch)
|
|
*/
|
|
if (acptr == sptr) {
|
|
if (strcmp(acptr->name, nick) != 0)
|
|
{
|
|
/* Allows change of case in their 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 = atol(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_server(NULL, 0, 0,
|
|
":%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_server(cptr, 0, 0,
|
|
":%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 = atol(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_server(NULL, 0, 0, /* First kill the new nick. */
|
|
":%s KILL %s :%s (Self Collision)",
|
|
me.name, acptr->name, me.name);
|
|
sendto_server(cptr, 0, 0, /* 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_server(cptr, 0, 0,
|
|
":%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_server(cptr, 0, 0,
|
|
":%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 = atol(parv[2]);
|
|
if (parc > 3)
|
|
sptr->lastnick = atol(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;
|
|
}
|
|
|
|
for (h = Hooks[HOOKTYPE_CHAN_PERMIT_NICK_CHANGE]; h; h = h->next)
|
|
{
|
|
i = (*(h->func.intfunc))(sptr,mp->chptr);
|
|
if (i != HOOK_CONTINUE)
|
|
break;
|
|
}
|
|
|
|
if (i == HOOK_DENY)
|
|
{
|
|
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 their 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 their nickname to %s",
|
|
sptr->name, sptr->user->username, sptr->user->realhost, nick);
|
|
|
|
RunHook3(HOOKTYPE_REMOTE_NICKCHANGE, cptr, sptr, nick);
|
|
}
|
|
/*
|
|
* Client just changing their 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
|
|
&& atol(parv[2]) < sptr->lastnick))
|
|
sptr->lastnick = (MyClient(sptr)
|
|
|| parc < 3) ? TStime() : atol(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_server(cptr, 0, 0, ":%s NICK %s %ld",
|
|
parv[0], nick, sptr->lastnick);
|
|
if (removemoder)
|
|
sptr->umodes &= ~UMODE_REGNICK;
|
|
}
|
|
else if (!sptr->name[0])
|
|
{
|
|
if (iConf.ping_cookie)
|
|
{
|
|
/*
|
|
* 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);
|
|
}
|
|
|
|
#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))
|
|
{
|
|
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 && *sptr->user->username && IsNotSpoof(sptr) && !CHECKPROTO(sptr, PROTO_CLICAP))
|
|
{
|
|
/*
|
|
** 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!!!
|
|
*/
|
|
if (!iConf.ping_cookie && USE_BAN_VERSION && MyConnect(sptr))
|
|
sendto_one(sptr, ":IRC!IRC@%s PRIVMSG %s :\1VERSION\1",
|
|
me.name, nick);
|
|
|
|
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);
|
|
}
|
|
|
|
/* update fdlist --nenolod */
|
|
if (MyConnect(sptr))
|
|
{
|
|
snprintf(descbuf, sizeof descbuf, "Client: %s", nick);
|
|
fd_desc(sptr->fd, descbuf);
|
|
}
|
|
|
|
(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);
|
|
|
|
if (newusr && !MyClient(sptr) && IsPerson(sptr))
|
|
{
|
|
RunHook(HOOKTYPE_REMOTE_CONNECT, sptr);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
** register_user
|
|
** This function is called when both NICK and USER messages
|
|
** have been accepted for the client, in whatever order. Only
|
|
** after this the USER message is propagated.
|
|
**
|
|
** NICK's must be propagated at once when received, although
|
|
** it would be better to delay them too until full info is
|
|
** available. Doing it is not so simple though, would have
|
|
** to implement the following:
|
|
**
|
|
** 1) user telnets in and gives only "NICK foobar" and waits
|
|
** 2) another user far away logs in normally with the nick
|
|
** "foobar" (quite legal, as this server didn't propagate
|
|
** it).
|
|
** 3) now this server gets nick "foobar" from outside, but
|
|
** has already the same defined locally. Current server
|
|
** would just issue "KILL foobar" to clean out dups. But,
|
|
** this is not fair. It should actually request another
|
|
** nick from local user or kill him/her...
|
|
*/
|
|
extern MODVAR char cmodestring[512];
|
|
extern MODVAR char umodestring[UMODETABLESZ+1];
|
|
|
|
extern int short_motd(aClient *sptr);
|
|
|
|
int _register_user(aClient *cptr, aClient *sptr, char *nick, char *username, char *umode, char *virthost, char *ip)
|
|
{
|
|
ConfigItem_ban *bconf;
|
|
char *parv[3], *tmpstr;
|
|
const char *id;
|
|
#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))
|
|
{
|
|
char temp[USERLEN + 1];
|
|
|
|
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++;
|
|
ircsnprintf(mo, sizeof(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 ASCII < 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 or ::ffff:1.2.3.4), then reject it and use IP instead.
|
|
*/
|
|
if (*tmpstr || !*user->realhost || (isdigit(*sptr->sockhost) && (sptr->sockhost > tmpstr && isdigit(*(tmpstr - 1))) )
|
|
|| (sptr->sockhost[0] == ':'))
|
|
strlcpy(sptr->sockhost, Inet_ia2p(&sptr->ip), sizeof(sptr->sockhost));
|
|
}
|
|
strlcpy(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
|
|
*/
|
|
|
|
/* because username may point to user->username */
|
|
strlcpy(temp, username, USERLEN + 1);
|
|
|
|
if (!(sptr->flags & FLAGS_DOID))
|
|
strlcpy(user->username, temp, USERLEN + 1);
|
|
else if (sptr->flags & FLAGS_GOTID)
|
|
strlcpy(user->username, sptr->username, USERLEN+1);
|
|
else
|
|
{
|
|
if (IDENT_CHECK == 0) {
|
|
strlcpy(user->username, temp, USERLEN+1);
|
|
}
|
|
else {
|
|
*user->username = '~';
|
|
strlcpy((user->username + 1), temp, USERLEN+1);
|
|
#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.");
|
|
}
|
|
|
|
strlcpy(olduser, user->username + noident, USERLEN+1);
|
|
strlcpy(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
|
|
{
|
|
strlcpy(user->username, username, USERLEN+1);
|
|
}
|
|
SetClient(sptr);
|
|
IRCstats.clients++;
|
|
if (sptr->srvptr && sptr->srvptr->serv)
|
|
sptr->srvptr->serv->users++;
|
|
|
|
make_virthost(sptr, user->realhost, user->cloakedhost, 0);
|
|
user->virthost = strdup(user->cloakedhost);
|
|
|
|
if (MyConnect(sptr))
|
|
{
|
|
char descbuf[BUFSIZE];
|
|
int i;
|
|
|
|
snprintf(descbuf, sizeof descbuf, "Client: %s", nick);
|
|
fd_desc(sptr->fd, descbuf);
|
|
|
|
list_move(&sptr->lclient_node, &lclient_list);
|
|
|
|
while (hash_find_id((id = uid_get()), NULL) != NULL)
|
|
;
|
|
strlcpy(sptr->id, id, sizeof sptr->id);
|
|
(void)add_to_id_hash_table(sptr->id, sptr);
|
|
|
|
IRCstats.unknown--;
|
|
IRCstats.me_clients++;
|
|
if (IsHidden(sptr))
|
|
ircd_log(LOG_CLIENT, "Connect - %s!%s@%s [VHOST %s]", nick,
|
|
user->username, user->realhost, user->virthost);
|
|
else
|
|
ircd_log(LOG_CLIENT, "Connect - %s!%s@%s", nick, user->username,
|
|
user->realhost);
|
|
sendto_one(sptr, rpl_str(RPL_WELCOME), me.name, nick,
|
|
ircnetwork, nick, user->username, user->realhost);
|
|
/* This is a duplicate of the NOTICE but see below... */
|
|
sendto_one(sptr, rpl_str(RPL_YOURHOST), me.name, nick,
|
|
me.name, version);
|
|
sendto_one(sptr, rpl_str(RPL_CREATED), me.name, nick, creation);
|
|
sendto_one(sptr, rpl_str(RPL_MYINFO), me.name, parv[0],
|
|
me.name, version, umodestring, cmodestring);
|
|
|
|
for (i = 0; IsupportStrings[i]; i++)
|
|
sendto_one(sptr, rpl_str(RPL_ISUPPORT), me.name, nick, IsupportStrings[i]);
|
|
|
|
sendto_one(sptr, rpl_str(RPL_YOURID), me.name, nick, sptr->id);
|
|
|
|
#ifdef USE_SSL
|
|
if (sptr->flags & FLAGS_SSL)
|
|
if (sptr->ssl)
|
|
sendto_one(sptr,
|
|
":%s NOTICE %s :*** You are connected to %s with %s",
|
|
me.name, sptr->name, me.name,
|
|
ssl_get_cipher(sptr->ssl));
|
|
#endif
|
|
do_cmd(sptr, sptr, "LUSERS", 1, parv);
|
|
short_motd(sptr);
|
|
#ifdef EXPERIMENTAL
|
|
sendto_one(sptr,
|
|
":%s NOTICE %s :*** \2NOTE:\2 This server (%s) is running experimental IRC server software. If you find any bugs or problems, please mail unreal-dev@lists.sourceforge.net about it",
|
|
me.name, sptr->name, me.name);
|
|
#endif
|
|
#ifdef HOSTILENAME
|
|
/*
|
|
* Now send a numeric to the user telling them what, if
|
|
* anything, happened.
|
|
*/
|
|
if (u1)
|
|
sendto_one(sptr, err_str(ERR_HOSTILENAME), me.name,
|
|
sptr->name, olduser, userbad, stripuser);
|
|
#endif
|
|
if (IsSecure(sptr))
|
|
sptr->umodes |= UMODE_SECURE;
|
|
}
|
|
else if (IsServer(cptr))
|
|
{
|
|
aClient *acptr;
|
|
|
|
if (!(acptr = (aClient *)find_server_quick(user->server)))
|
|
{
|
|
sendto_ops
|
|
("Bad USER [%s] :%s USER %s %s : No such server",
|
|
cptr->name, nick, user->username, user->server);
|
|
sendto_one(cptr, ":%s KILL %s :%s (No such server: %s)",
|
|
me.name, sptr->name, me.name, user->server);
|
|
sptr->flags |= FLAGS_KILLED;
|
|
return exit_client(sptr, sptr, &me,
|
|
"USER without prefix(2.8) or wrong prefix");
|
|
}
|
|
else if (acptr->from != sptr->from)
|
|
{
|
|
sendto_ops("Bad User [%s] :%s USER %s %s, != %s[%s]",
|
|
cptr->name, nick, user->username, user->server,
|
|
acptr->name, acptr->from->name);
|
|
sendto_one(cptr, ":%s KILL %s :%s (%s != %s[%s])",
|
|
me.name, sptr->name, me.name, user->server,
|
|
acptr->from->name, acptr->from->sockhost);
|
|
sptr->flags |= FLAGS_KILLED;
|
|
return exit_client(sptr, sptr, &me,
|
|
"USER server wrong direction");
|
|
}
|
|
else
|
|
sptr->flags |= acptr->flags;
|
|
/* *FINALL* this gets in ircd... -- Barubary */
|
|
/* We change this a bit .. */
|
|
if (IsULine(sptr->srvptr))
|
|
sptr->flags |= FLAGS_ULINE;
|
|
}
|
|
if (sptr->umodes & UMODE_INVISIBLE)
|
|
{
|
|
IRCstats.invisible++;
|
|
}
|
|
|
|
if (virthost && umode)
|
|
{
|
|
tkllayer[0] = nick;
|
|
tkllayer[1] = nick;
|
|
tkllayer[2] = umode;
|
|
dontspread = 1;
|
|
do_cmd(cptr, sptr, "MODE", 3, tkllayer);
|
|
dontspread = 0;
|
|
if (virthost && *virthost != '*')
|
|
{
|
|
if (sptr->user->virthost)
|
|
{
|
|
MyFree(sptr->user->virthost);
|
|
sptr->user->virthost = NULL;
|
|
}
|
|
/* Here pig.. yeah you .. -Stskeeps */
|
|
sptr->user->virthost = strdup(virthost);
|
|
}
|
|
if (ip && (*ip != '*'))
|
|
sptr->user->ip_str = strdup(decode_ip(ip));
|
|
}
|
|
|
|
hash_check_watch(sptr, RPL_LOGON); /* Uglier hack */
|
|
send_umode(NULL, sptr, 0, SEND_UMODES|UMODE_SERVNOTICE, buf);
|
|
|
|
/* NICKv2 Servers ! */
|
|
sendto_serv_butone_nickcmd(cptr, sptr, nick,
|
|
sptr->hopcount + 1, sptr->lastnick, user->username, user->realhost,
|
|
user->server, user->svid, sptr->info,
|
|
(*buf == '\0' ? "+" : buf),
|
|
sptr->umodes & UMODE_SETHOST ? sptr->user->virthost : NULL);
|
|
|
|
if (MyConnect(sptr))
|
|
{
|
|
char userhost[USERLEN + HOSTLEN + 6];
|
|
|
|
sendto_connectnotice(nick, user, sptr, 0, NULL); /* moved down, for modules. */
|
|
|
|
/* Send password from sptr->passwd to NickServ for identification,
|
|
* if passwd given and if NickServ is online.
|
|
* - by taz, modified by Wizzu
|
|
*/
|
|
if (sptr->passwd && (nsptr = find_person(NickServ, NULL)))
|
|
{
|
|
int do_identify = 1;
|
|
Hook *h;
|
|
for (h = Hooks[HOOKTYPE_LOCAL_NICKPASS]; h; h = h->next)
|
|
{
|
|
i = (*(h->func.intfunc))(sptr,nsptr);
|
|
if (i == HOOK_DENY)
|
|
{
|
|
do_identify = 0;
|
|
break;
|
|
}
|
|
}
|
|
if (do_identify)
|
|
sendto_one(nsptr, ":%s PRIVMSG %s@%s :IDENTIFY %s",
|
|
sptr->name,
|
|
NickServ, SERVICES_NAME, sptr->passwd);
|
|
}
|
|
if (buf[0] != '\0' && buf[1] != '\0')
|
|
sendto_one(cptr, ":%s MODE %s :%s", cptr->name,
|
|
cptr->name, buf);
|
|
if (user->snomask)
|
|
sendto_one(sptr, rpl_str(RPL_SNOMASK),
|
|
me.name, sptr->name, get_snostr(user->snomask));
|
|
strlcpy(userhost,make_user_host(cptr->user->username, cptr->user->realhost), sizeof(userhost));
|
|
|
|
/* NOTE: Code after this 'if (savetkl)' will not be executed for quarantined-
|
|
* virus-users. So be carefull with the order. -- Syzop
|
|
*/
|
|
if (savetkl)
|
|
return dospamfilter_viruschan(sptr, savetkl, SPAMF_USER); /* [RETURN!] */
|
|
|
|
/* Force the user to join the given chans -- codemastr */
|
|
for (tlds = conf_tld; tlds; tlds = (ConfigItem_tld *) tlds->next) {
|
|
if (!match(tlds->mask, userhost))
|
|
break;
|
|
}
|
|
if (tlds && !BadPtr(tlds->channel)) {
|
|
char *chans[3] = {
|
|
sptr->name,
|
|
tlds->channel,
|
|
NULL
|
|
};
|
|
do_cmd(sptr, sptr, "JOIN", 3, chans);
|
|
}
|
|
else if (!BadPtr(AUTO_JOIN_CHANS) && strcmp(AUTO_JOIN_CHANS, "0"))
|
|
{
|
|
char *chans[3] = {
|
|
sptr->name,
|
|
AUTO_JOIN_CHANS,
|
|
NULL
|
|
};
|
|
do_cmd(sptr, sptr, "JOIN", 3, chans);
|
|
}
|
|
/* NOTE: If you add something here.. be sure to check the 'if (savetkl)' note above */
|
|
}
|
|
|
|
if (MyConnect(sptr) && !BadPtr(sptr->passwd))
|
|
{
|
|
MyFree(sptr->passwd);
|
|
sptr->passwd = NULL;
|
|
}
|
|
return 0;
|
|
}
|
|
|