/************************************************************************
* IRC - Internet Relay Chat, server/s_user.c
*
* Copyright (C) 2000-2003 TR-IRCD Development
*
* Copyright (C) 1990 Jarkko Oikarinen and
* University of Oulu, Co Center
*
* 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 2, 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.
*
* $Id: s_user.c,v 1.9 2004/07/12 09:14:59 tr-ircd Exp $
*/
#include "struct.h"
#include "common.h"
#include "sys.h"
#include "tools.h"
#include "h.h"
#include "channel.h"
#include "class.h"
#include "fd.h"
#include "listener.h"
#include "msg.h"
#include "numeric.h"
#include "s_bsd.h"
#include "s_conf.h"
#include "throttle.h"
#include "supported.h"
#include "packet.h"
#include "hook.h"
#include "language.h"
#define IRCD_BUFSIZE 512
static int hookid_introduce_client = 0;
static int hookid_proxy_add = 0;
static int user_welcome(struct Client *source_p, struct ConfItem *aconf);
static void manage_faked_oper_hostname(struct Client *sptr, struct ConfItem *pwaconf);
static int introduce_client(struct Client *client_p, struct Client *source_p,
struct User *user, char *nick);
void init_user(void)
{
hookid_introduce_client = hook_add_event("introduce client");
hookid_proxy_add = hook_add_event("add to proxy");
return;
}
/*
* ** register_local_user
* ** This function is called when both NICK and USER messages
* ** have been accepted for the client, in whatever order. Only
* ** after this, is the USER message 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:
* **
* ** (actually it has been implemented already for a while) -orabidoo
* **
* ** 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...
*/
int register_local_user(struct Client *client_p, struct Client *source_p,
char *nick, char *username)
{
struct ConfItem *aconf;
struct User *user = source_p->user;
char *p;
char passarr[PASSWDLEN];
char tmpstr2[IRCD_BUFSIZE];
char ipaddr[HOSTIPLEN];
int status;
int r;
dlink_node *ptr;
dlink_node *m;
char newhostarr[HOSTLEN];
char *q;
assert(0 != source_p);
assert(0 != source_p);
assert(source_p->username != username);
user->last = timeofday;
/* pointed out by Mortiis, never be too careful */
if (strlen(username) > USERLEN)
username[USERLEN] = '\0';
if (!valid_hostname(source_p->sockhost)) {
send_me_notice(source_p, ":*** Notice -- You have an illegal character in your hostname");
if (IsClosing(source_p))
return CLIENT_EXITED;
strlcpy_irc(source_p->sockhost, source_p->hostip, HOSTIPLEN);
}
if (strchr(source_p->hostip, ':'))
source_p->protoflags |= PFLAGS_IPV6HOST;
strlcpy_irc(user->host, source_p->sockhost, HOSTLEN);
q = user->host;
strlcpy_irc(user->fakehost, calcmask(q, newhostarr), HOSTLEN);
status = check_client(client_p, source_p, username);
if (status < 0)
return CLIENT_EXITED;
ptr = source_p->confs.head;
aconf = ptr->data;
source_p->allow_read = MAX_FLOOD_PER_SEC_I;
if (aconf == NULL)
return CLIENT_EXITED;
if (!IsGotId(source_p)) {
if (!ServerOpts.identd_use_tilde && ServerOpts.identd_complain) {
ircstp->is_ref++;
send_me_notice(source_p,
":*** Notice -- You need to install identd to use this server");
exit_client(source_p, &me, "Install identd");
return CLIENT_EXITED;
} else if (!ServerOpts.identd_use_tilde && !ServerOpts.identd_complain) {
strlcpy_irc(source_p->username, username, USERLEN);
} else {
*(source_p->username) = '~';
strlcpy_irc(&source_p->username[1], username, USERLEN - 1);
}
source_p->username[USERLEN] = '\0';
}
/* password check */
if (!BadPtr(aconf->passwd) && (p = source_p->passwd) &&
strcmp(calcpass(p, passarr), aconf->passwd)) {
ircstp->is_ref++;
send_me_numeric(source_p, ERR_PASSWDMISMATCH);
exit_client(source_p, &me, "Bad Password");
return CLIENT_EXITED;
}
memset(source_p->passwd, 0, sizeof(source_p->passwd));
/* Limit clients */
/*
* We want to be able to have servers and F-line clients
* connect, so save room for "buffer" connections.
* Smaller servers may want to decrease this, and it should
* probably be just a percentage of the MAXCLIENTS...
* -Taner
*/
/* Except "F:" clients */
if ((((Count.local + 1) >= GeneralOpts.maxclients)
|| ((Count.local + 1) >= (GeneralOpts.maxclients - 5))) && !IsConfExemptLimits(aconf)) {
sendto_lev(REJ_LEV, "Too many clients, rejecting %s[%s].", nick, source_p->sockhost);
ircstp->is_ref++;
exit_client(source_p, &me, "Sorry, server is full - try later");
return CLIENT_EXITED;
}
/* valid user name check */
if (!valid_username(source_p->username)) {
sendto_lev(REJ_LEV, "Invalid username: %s (%s@%s)",
nick, source_p->username, source_p->sockhost);
ircstp->is_ref++;
ircsprintf(tmpstr2, "Invalid username [%s]", source_p->username);
exit_client(source_p, &me, tmpstr2);
return CLIENT_EXITED;
}
if (check_drone_PB(source_p->username, source_p->info)) {
sendto_lev(REJ_LEV, "Rejecting acebot-style drone: %^C [%s]", source_p, source_p->info);
exit_client(source_p, &me, "You match the pattern of a known trojan, please check your system.");
return CLIENT_EXITED;
}
SetClient(source_p);
strlcpy_irc(user->username, source_p->username, USERLEN);
/* end of valid user name check */
m = dlinkFind(&unknown_list, source_p);
if (m != NULL) {
Count.unknown--;
dlinkDelete(m, &unknown_list);
dlinkAdd(source_p, m, &lclient_list);
}
inetntop(source_p->aftype, &IN_ADDR(source_p->ip), ipaddr, HOSTIPLEN);
sendto_lev(CCONN_LEV, "Client connecting: %^C [%s] {%d} [%s]",
source_p, ipaddr, get_client_class(source_p), source_p->info);
if ((++Count.local) > Count.max_loc) {
Count.max_loc = Count.local;
if (!(Count.max_loc % 10))
sendto_lev(DEBUG_LEV, "New Max Local Clients: %d", Count.max_loc);
}
source_p->pingval = get_client_ping(source_p);
source_p->sendqlen = get_sendq(source_p);
source_p->servptr = &me;
add_client_to_llist(&(source_p->servptr->serv->users), source_p);
source_p->servptr->serv->usercnt++;
/* Increment our total user count here */
if (++Count.total > Count.max_tot)
Count.max_tot = Count.total;
assign_localuser_identity(source_p);
SetHasID(source_p);
if (source_p->langstring[0]) {
source_p->lang = lang_parse(source_p->langstring);
if (source_p->lang)
sendto_one_person(source_p, NULL, TOK1_NOTICE, "AUTH :*** Setting language to %s",
source_p->langstring);
}
r = user_welcome(source_p, aconf);
if (r & FLAGS_EXITED)
return CLIENT_EXITED;
return (introduce_client(client_p, source_p, user, nick));
}
/*
* register_remote_user
*
* inputs
* output
* side effects - This function is called when a remote client
* is introduced by a server.
*/
int register_remote_user(struct Client *client_p, struct Client *source_p,
char *nick, char *username)
{
struct User *user = source_p->user;
struct Client *target_p;
assert(0 != source_p);
assert(source_p->username != username);
user->last = timeofday;
/* pointed out by Mortiis, never be too careful */
if (strlen(username) > USERLEN)
username[USERLEN] = '\0';
strlcpy_irc(source_p->username, username, USERLEN);
strlcpy_irc(user->username, username, USERLEN);
SetClient(source_p);
/* Increment our total user count here */
if (++Count.total > Count.max_tot)
Count.max_tot = Count.total;
source_p->servptr = find_server(user->server);
if (source_p->servptr == NULL) {
sendto_ops("Ghost killed: %s on invalid server %s", source_p->name, source_p->user->server);
kill_client(client_p, source_p, "%s (Server doesn't exist)", me.name);
source_p->flags |= FLAGS_KILLED;
return exit_client(source_p, &me, "Ghost");
}
if ((target_p = source_p->servptr) && target_p->from != source_p->from) {
sendto_lev(DEBUG_LEV, "Bad User [%s] :%s USER %s@%s %s, != %s[%s]",
client_p->name, nick, source_p->username,
source_p->sockhost, user->server, target_p->name, target_p->from->name);
kill_client(client_p, source_p,
":%s (%s != %s[%s] USER from wrong direction)", me.name,
user->server, target_p->from->name, target_p->from->sockhost);
source_p->flags |= FLAGS_KILLED;
return exit_client(source_p, &me, "USER server wrong direction");
}
/*
* Super GhostDetect:
* If we can't find the server the user is supposed to be on,
* then simply blow the user away. -Taner
*/
if (!target_p) {
kill_client(client_p, source_p, "%s GHOST (no server found)", me.name, user->server);
sendto_ops("No server %s for user %s[%s@%s] from %s",
user->server, source_p->name, source_p->username,
source_p->sockhost, source_p->from->name);
source_p->flags |= FLAGS_KILLED;
return exit_client(source_p, &me, "Ghosted Client");
}
add_client_to_llist(&(source_p->servptr->serv->users), source_p);
source_p->servptr->serv->usercnt++;
return (introduce_client(client_p, source_p, user, nick));
}
/*
* introduce_client
*
* inputs -
* output -
* side effects - This common function introduces a client to the rest
* of the net, either from a local client connect or
* from a remote connect.
*/
static int introduce_client(struct Client *cptr, struct Client *sptr, struct User *user, char *nick)
{
struct hook_data thisdata;
thisdata.client_p = cptr;
thisdata.source_p = sptr;
thisdata.user = user;
thisdata.name = nick;
if ((NOW - Count.day) > 86400) {
Count.today = 0;
Count.day = NOW;
}
if ((NOW - Count.week) > 604800) {
Count.weekly = 0;
Count.week = NOW;
}
if ((NOW - Count.month) > 2592000) {
Count.monthly = 0;
Count.month = NOW;
}
if ((NOW - Count.year) > 31536000) {
Count.yearly = 0;
Count.year = NOW;
}
Count.today++;
Count.weekly++;
Count.monthly++;
Count.yearly++;
return hook_call_event(hookid_introduce_client, &thisdata);
}
/*
* do_local_user
*
* inputs -
* output -
* side effects -
*/
int do_local_user(char *nick, aClient *cptr, aClient *sptr, char *username, char *host,
char *mask, char *server, unsigned long serviceid, char *realname)
{
struct User *user;
user = make_user(sptr);
if (!IsUnknown(sptr)) {
send_me_numeric(sptr, ERR_ALREADYREGISTRED);
return 0;
}
sptr->umode |= UMODE_i; /* invisible IS default. always */
if(ServerOpts.default_fakehost_mode)
sptr->umode |= UMODE_x;
if (ServerOpts.use_registerfilter)
sptr->umode |= UMODE_R;
strlcpy_irc(user->host, host, sizeof(user->host));
DupString(user->server, me.name);
server[strlen(server) - 1] = '\0';
if ((ServerInfo.aliasname[0] != '\0') && (server[0] != '\0')) {
if (!irc_strcmp(server + 1, ServerInfo.aliasname))
sptr->protoflags |= PFLAGS_ALIASED;
}
Count.invisi++;
if (mask)
strlcpy_irc(user->fakehost, mask, sizeof(user->fakehost));
strlcpy_irc(sptr->info, realname, REALLEN);
sptr->user->servicestamp = serviceid;
sptr->oflag = 0;
if (sptr->name[0]) {
strlcpy_irc(user->username, username, USERLEN);
/* NICK already received, now I have USER... */
return register_local_user(cptr, sptr, sptr->name, username);
} else if (!IsGotId(sptr)) {
strlcpy_irc(user->username, username, USERLEN);
strlcpy_irc(sptr->username, username, USERLEN);
}
return 0;
}
/*
* do_remote_user
*
* inputs -
* output -
* side effects -
*/
int do_remote_user(char *nick, aClient *client_p, aClient *source_p, char *username, char *host,
char *mask, char *server, unsigned long serviceid, char *realname)
{
struct User *user;
assert(0 != source_p);
assert(source_p->username != username);
user = make_user(source_p);
strlcpy_irc(source_p->sockhost, host, HOSTLEN);
strlcpy_irc(user->host, host, HOSTLEN);
strlcpy_irc(user->fakehost, mask, HOSTLEN);
strlcpy_irc(source_p->info, realname, REALLEN);
DupString(user->server, server);
source_p->user->servicestamp = serviceid;
return register_remote_user(client_p, source_p, source_p->name, username);
}
static void manage_faked_oper_hostname(aClient *sptr, aConfItem *pwaconf)
{
if (!ServerOpts.staffhide)
return;
if (!IsConfDoSpoofIp(pwaconf))
return;
if (IsDead(sptr))
return;
/* It is possible to enter this function, even though the
* client has been marked as dead. -TimeMr14C 17.05.2003
*/
if (IsConfSpoofNotice(pwaconf))
sendto_ops("%^C has masked their hostname.", sptr);
send_me_notice(sptr, ":*** Your hostname has been masked.");
if (IsDead(sptr))
return;
/* The above line ensures us that we dont touch the
* user structrue (sptr->user) if a write error has happened
* whilst the send_me_notice -TimeMr14C 17.05.2003
*/
sptr->protoflags |= PFLAGS_OPERFAKEHOST;
sptr->user->real_oper_host = MyMalloc(strlen(sptr->user->host) + 1);
sptr->user->real_oper_ip = MyMalloc(strlen(sptr->hostip) + 1);
strcpy(sptr->user->real_oper_host, sptr->user->host);
strcpy(sptr->user->real_oper_ip, sptr->hostip);
if (IsConfCanChooseFakehost(pwaconf)) {
if (sptr->fakehost_override[0]) {
strlcpy_irc(sptr->sockhost, sptr->fakehost_override, HOSTLEN);
strlcpy_irc(sptr->user->host, sptr->fakehost_override, HOSTLEN);
} else if (pwaconf->localhost[0]) {
strlcpy_irc(sptr->sockhost, pwaconf->localhost, HOSTLEN);
strlcpy_irc(sptr->user->host, pwaconf->localhost, HOSTLEN);
}
} else if (pwaconf != NULL) {
if (pwaconf->localhost[0]) {
strlcpy_irc(sptr->sockhost, pwaconf->localhost, HOSTLEN);
strlcpy_irc(sptr->user->host, pwaconf->localhost, HOSTLEN);
}
}
throttle_remove(sptr->hostip);
sptr->flags |= FLAGS_GOTID;
IN4_ADDR(sptr->ip) = 0;
strcpy(sptr->hostip, "0.0.0.0");
return;
}
/*
* user_welcome
*
* inputs - client pointer to client to welcome
* output - NONE
* side effects -
*/
static int user_welcome(struct Client *sptr, struct ConfItem *aconf)
{
aMotd *smotd;
aMotdItem *smotdline;
send_me_numeric(sptr, RPL_WELCOME, ServerInfo.networkname, sptr->name,
sptr->user->username, sptr->user->host);
/* We require the above line for DCC to work, otherwise,
* we could use %C instead -TimeMr14C */
send_me_numeric(sptr, RPL_YOURHOST, get_client_name(&me, TRUE), version);
send_me_numeric(sptr, RPL_CREATED, creation);
send_me_numeric(sptr, RPL_MYINFO, me.name, version,
GeneralOpts.umodelist, GeneralOpts.allchanmodes, GeneralOpts.paramchanmodes);
send_supported(sptr);
manage_faked_oper_hostname(sptr, aconf);
if (IsDead(sptr))
return sptr->flags;
/* The above call helps us in not being required to deal with additional things
* about the structure itself (reading host, while there isn't one, since freed.
* -TimeMr14C 17.05.2003
*/
/* This call here may call the dummy function with no content, or the one
* after the replacement by the proxymon module.
* -TimeMr14C */
if (GeneralOpts.enable_proxymonitor && !IsConfExemptKline(aconf) && !IsOperFakehost(sptr)) {
struct hook_data thisdata;
thisdata.source_p = sptr;
hook_call_event(hookid_proxy_add, &thisdata);
}
send_lusers(sptr, sptr, 1, NULL);
send_me_notice(sptr, ":*** Notice -- motd was last changed at %s", (&(GeneralOpts.motd))->lastchange);
if (ServerOpts.short_motd) {
send_me_notice(sptr, ":*** Notice -- Please read the motd if you haven't read it");
send_me_numeric(sptr, RPL_MOTDSTART, me.name);
if ((smotd = &(GeneralOpts.shortmotd)) == NULL) {
send_me_numeric(sptr, RPL_MOTD, "*** This is the short motd ***");
} else {
smotdline = smotd->content;
while (smotdline) {
send_me_numeric(sptr, RPL_MOTD, smotdline->line);
smotdline = smotdline->next;
}
}
send_me_numeric(sptr, RPL_ENDOFMOTD);
} else {
send_message_file(&(GeneralOpts.motd), sptr, sptr, 1, NULL);
}
return sptr->flags;
}
syntax highlighted by Code2HTML, v. 0.9.1