3022 lines
80 KiB
C
3022 lines
80 KiB
C
/************************************************************************
|
|
* IRC - Internet Relay Chat, ircd/s_serv.c (formerly ircd/s_msg.c)
|
|
* Copyright (C) 1990 Jarkko Oikarinen and
|
|
* University of Oulu, Computing 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 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.
|
|
*/
|
|
|
|
#ifndef lint
|
|
static char sccsid[] = "@(#)s_serv.c 2.55 2/7/94 (C) 1988 University of Oulu, \
|
|
Computing Center and Jarkko Oikarinen";
|
|
#endif
|
|
|
|
#include "struct.h"
|
|
#include "common.h"
|
|
#include "sys.h"
|
|
#include "numeric.h"
|
|
#include "msg.h"
|
|
#include "channel.h"
|
|
#include "userload.h"
|
|
#if defined(PCS) || defined(AIX) || defined(DYNIXPTX) || defined(SVR3)
|
|
#include <time.h>
|
|
#endif
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <utmp.h>
|
|
#include "h.h"
|
|
|
|
static char buf[BUFSIZE];
|
|
|
|
int max_connection_count = 1, max_client_count = 1;
|
|
|
|
/*
|
|
** m_functions execute protocol messages on this server:
|
|
**
|
|
** cptr is always NON-NULL, pointing to a *LOCAL* client
|
|
** structure (with an open socket connected!). This
|
|
** identifies the physical socket where the message
|
|
** originated (or which caused the m_function to be
|
|
** executed--some m_functions may call others...).
|
|
**
|
|
** sptr is the source of the message, defined by the
|
|
** prefix part of the message if present. If not
|
|
** or prefix not found, then sptr==cptr.
|
|
**
|
|
** (!IsServer(cptr)) => (cptr == sptr), because
|
|
** prefixes are taken *only* from servers...
|
|
**
|
|
** (IsServer(cptr))
|
|
** (sptr == cptr) => the message didn't
|
|
** have the prefix.
|
|
**
|
|
** (sptr != cptr && IsServer(sptr) means
|
|
** the prefix specified servername. (?)
|
|
**
|
|
** (sptr != cptr && !IsServer(sptr) means
|
|
** that message originated from a remote
|
|
** user (not local).
|
|
**
|
|
** combining
|
|
**
|
|
** (!IsServer(sptr)) means that, sptr can safely
|
|
** taken as defining the target structure of the
|
|
** message in this server.
|
|
**
|
|
** *Always* true (if 'parse' and others are working correct):
|
|
**
|
|
** 1) sptr->from == cptr (note: cptr->from == cptr)
|
|
**
|
|
** 2) MyConnect(sptr) <=> sptr == cptr (e.g. sptr
|
|
** *cannot* be a local connection, unless it's
|
|
** actually cptr!). [MyConnect(x) should probably
|
|
** be defined as (x == x->from) --msa ]
|
|
**
|
|
** parc number of variable parameter strings (if zero,
|
|
** parv is allowed to be NULL)
|
|
**
|
|
** parv a NULL terminated list of parameter pointers,
|
|
**
|
|
** parv[0], sender (prefix string), if not present
|
|
** this points to an empty string.
|
|
** parv[1]...parv[parc-1]
|
|
** pointers to additional parameters
|
|
** parv[parc] == NULL, *always*
|
|
**
|
|
** note: it is guaranteed that parv[0]..parv[parc-1] are all
|
|
** non-NULL pointers.
|
|
*/
|
|
|
|
/*
|
|
** m_version
|
|
** parv[0] = sender prefix
|
|
** parv[1] = remote server
|
|
*/
|
|
int m_version(cptr, sptr, parc, parv)
|
|
aClient *sptr, *cptr;
|
|
int parc;
|
|
char *parv[];
|
|
{
|
|
extern char serveropts[];
|
|
Reg1 aClient *acptr;
|
|
/* version should also be always available imo --dl
|
|
if (check_registered(sptr))
|
|
return 0;
|
|
*/
|
|
if (MyConnect(sptr) && parc > 1)
|
|
{
|
|
if (!(acptr = find_match_server(parv[1])))
|
|
{
|
|
sendto_one(sptr, err_str(ERR_NOSUCHSERVER),
|
|
me.name, parv[0], parv[1]);
|
|
return 0;
|
|
}
|
|
parv[1] = acptr->name;
|
|
}
|
|
|
|
if (hunt_server(cptr,sptr,":%s VERSION :%s",1,parc,parv)==HUNTED_ISME)
|
|
sendto_one(sptr, rpl_str(RPL_VERSION), me.name,
|
|
parv[0], version, debugmode, me.name, serveropts);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
** m_squit
|
|
** parv[0] = sender prefix
|
|
** parv[1] = server name
|
|
** parv[2] = timestamp
|
|
** parv[parc-1] = comment
|
|
*/
|
|
int m_squit(cptr, sptr, parc, parv)
|
|
aClient *cptr, *sptr;
|
|
int parc;
|
|
char *parv[];
|
|
{
|
|
Reg1 aConfItem *aconf;
|
|
char *server;
|
|
Reg2 aClient *acptr;
|
|
char *comment = (parc > ((!IsServer(cptr) || Protocol(cptr) < 9) ?
|
|
2 : 3) && !BadPtr(parv[parc-1])) ?
|
|
parv[parc-1] : cptr->name;
|
|
|
|
if (!IsPrivileged(sptr))
|
|
{
|
|
sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
|
|
return 0;
|
|
}
|
|
|
|
if (parc > (IsServer(cptr) ? ((Protocol(cptr) < 9) ? 1 : 2) : 1))
|
|
{
|
|
server = parv[1];
|
|
/*
|
|
** To accomodate host masking, a squit for a masked server
|
|
** name is expanded if the incoming mask is the same as
|
|
** the server name for that link to the name of link.
|
|
*/
|
|
if ((*server == '*') && IsServer(cptr) &&
|
|
(aconf = cptr->serv->nline) &&
|
|
!strcasecmp(server, my_name_for_link(me.name, aconf)))
|
|
{
|
|
server = cptr->name;
|
|
acptr = cptr;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
** The following allows wild cards in SQUIT. Only usefull
|
|
** when the command is issued by an oper.
|
|
*/
|
|
for (acptr = client; (acptr = next_client(acptr, server));
|
|
acptr = acptr->next)
|
|
if (IsServer(acptr) || IsMe(acptr))
|
|
break;
|
|
|
|
if (acptr)
|
|
{
|
|
if (IsMe(acptr))
|
|
{
|
|
if (IsServer(cptr))
|
|
{
|
|
acptr = cptr;
|
|
server = cptr->sockhost;
|
|
}
|
|
else
|
|
acptr = NULL;
|
|
}
|
|
else
|
|
{
|
|
/*
|
|
** Look for a matching server that is closer,
|
|
** that way we won't accidently squit two close
|
|
** servers like davis.* and davis-r.* when typing
|
|
** /SQUIT davis*
|
|
*/
|
|
aClient *acptr2;
|
|
for (acptr2 = acptr->serv->up; acptr2 != &me;
|
|
acptr2 = acptr2->serv->up)
|
|
if (!match(server, acptr2->name))
|
|
acptr = acptr2;
|
|
}
|
|
}
|
|
}
|
|
/* If atoi(parv[2]) == 0 we must indeed squit !
|
|
** It wil be our neighbour.
|
|
*/
|
|
if (acptr && IsServer(sptr) && Protocol(sptr) > 4 &&
|
|
atoi(parv[2]) && atoi(parv[2]) != acptr->serv->timestamp)
|
|
{
|
|
Debug((DEBUG_NOTICE, "Ignoring SQUIT with wrong timestamp"));
|
|
return 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sendto_one(cptr, err_str(ERR_NEEDMOREPARAMS),
|
|
me.name, parv[0], "SQUIT");
|
|
if (IsServer(cptr))
|
|
{
|
|
/*
|
|
** This is actually protocol error. But, well, closing
|
|
** the link is very proper answer to that...
|
|
*/
|
|
server = cptr->sockhost;
|
|
acptr = cptr;
|
|
}
|
|
else
|
|
return 0;
|
|
}
|
|
if (!acptr)
|
|
{
|
|
if (IsPerson(sptr))
|
|
sendto_one(sptr, err_str(ERR_NOSUCHSERVER),
|
|
me.name, parv[0], server);
|
|
return 0;
|
|
}
|
|
if (IsLocOp(sptr) && !MyConnect(acptr))
|
|
{
|
|
sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
|
|
return 0;
|
|
}
|
|
|
|
return exit_client(cptr, acptr, sptr, comment);
|
|
}
|
|
|
|
int exit_new_server(cptr, sptr, host, prot, timestamp, fmt, p1, p2, p3)
|
|
aClient *cptr, *sptr;
|
|
u_short prot;
|
|
time_t timestamp;
|
|
char *fmt, *p1, *p2, *p3, *host;
|
|
{
|
|
char buf[128];
|
|
if (!IsServer(sptr))
|
|
{
|
|
if (prot < 9)
|
|
{
|
|
strcpy(buf, ":%s SQUIT %s :");
|
|
strcat(buf, fmt);
|
|
sendto_one(cptr, buf, me.name, host, p1, p2, p3);
|
|
}
|
|
return exit_client_msg(cptr, cptr, &me, fmt, p1, p2, p3);
|
|
}
|
|
if (Protocol(cptr) < 9)
|
|
{
|
|
strcpy(buf, ":%s SQUIT %s :");
|
|
strcat(buf, fmt);
|
|
sendto_one(cptr, buf, me.name, host, p1, p2, p3);
|
|
}
|
|
else
|
|
{
|
|
strcpy(buf, ":%s SQUIT %s %lu :");
|
|
strcat(buf, fmt);
|
|
sendto_one(cptr, buf, me.name, host, timestamp, p1, p2, p3);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int a_kills_b_too(a, b)
|
|
register aClient *a, *b;
|
|
{
|
|
for (; b != a && b != &me; b = b->serv->up);
|
|
return (a == b ? 1 : 0);
|
|
}
|
|
|
|
/*
|
|
** m_server
|
|
** parv[0] = sender prefix
|
|
** parv[1] = servername
|
|
** parv[2] = hopcount
|
|
** P09: parv[3] = start timestamp
|
|
** parv[4] = link timestamp
|
|
** parv[5] = major protocol version: P09
|
|
** parv[parc-1] = serverinfo
|
|
*/
|
|
int m_server(cptr, sptr, parc, parv)
|
|
aClient *cptr, *sptr;
|
|
int parc;
|
|
char *parv[];
|
|
{
|
|
Reg1 char *ch;
|
|
Reg2 int i;
|
|
Reg3 Link *lp;
|
|
char info[REALLEN+1], *inpath, *host, *s;
|
|
aClient *acptr, *bcptr, *LHcptr = NULL;
|
|
aConfItem *aconf, *bconf = NULL, *cconf, *lhconf = NULL;
|
|
int hop, ret, active_lh_line = 0;
|
|
u_short prot;
|
|
time_t start_timestamp=0, timestamp=0, recv_time, ghost=0;
|
|
|
|
recv_time = TStime();
|
|
info[0] = '\0';
|
|
inpath = get_client_name(cptr, TRUE);
|
|
if (parc < 2 || *parv[1] == '\0')
|
|
{
|
|
sendto_one(cptr,"ERROR :No servername");
|
|
return exit_client(cptr, cptr, &me, "No servername");
|
|
}
|
|
/* Since .U5 a hopcount is required --Run */
|
|
if (parc < 3 || (hop=atoi(parv[2])) == 0 ||
|
|
(IsServer(cptr) && parc < ((Protocol(cptr) > 4) ? 7 : 4)))
|
|
{
|
|
sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS),
|
|
me.name, parv[0], "SERVER");
|
|
return exit_client(cptr, cptr, &me, "Need more parameters");
|
|
}
|
|
host = parv[1];
|
|
/* Detect Protocol(cptr) */
|
|
if (parc > 6)
|
|
{
|
|
if (strlen(parv[5])!=3 || (parv[5][0] != 'P' && parv[5][0] != 'J'))
|
|
return exit_client_msg(cptr, sptr, &me,
|
|
"Bogus protocol (%s)", parv[5]);
|
|
prot=atoi(parv[5]+1);
|
|
start_timestamp = atoi(parv[3]);
|
|
timestamp = atoi(parv[4]);
|
|
Debug((DEBUG_INFO, "Got SERVER %s with timestamp [%s] age %lu (%lu)",
|
|
host, parv[4], start_timestamp, me.serv->timestamp));
|
|
if (prot>4 &&
|
|
(timestamp<780000000 || (hop==1 && start_timestamp<780000000)))
|
|
{
|
|
return exit_client_msg(cptr, sptr, &me,
|
|
"Bogus timestamps (%s %s)", parv[3], parv[4]);
|
|
}
|
|
}
|
|
else if (!IsServer(cptr)) /* New local server ? */
|
|
/* Protocol 4 didn't support protocol versions yet */
|
|
prot=4;
|
|
else /* Remote server */
|
|
prot=0; /* Set to 'unknown' */
|
|
if (parc > 3) (void)strncpy(info, parv[parc-1], REALLEN);
|
|
if (prot && (prot < atoi(MINOR_PROTOCOL)))
|
|
{
|
|
sendto_ops("Got incompatible protocol version (%s) from %s",
|
|
parv[5], get_client_name(cptr, TRUE));
|
|
return exit_new_server(cptr, sptr, host, prot, timestamp,
|
|
"Incompatible protocol: %s", parv[5]);
|
|
}
|
|
/*
|
|
** Check for "FRENCH " infection ;-) (actually this should
|
|
** be replaced with routine to check the hostname syntax in
|
|
** general). [ This check is still needed, even after the parse
|
|
** is fixed, because someone can send "SERVER :foo bar " ].
|
|
** Also, changed to check other "difficult" characters, now
|
|
** that parse lets all through... --msa
|
|
*/
|
|
if (strlen(host) > HOSTLEN)
|
|
host[HOSTLEN] = '\0';
|
|
for (ch = host; *ch; ch++)
|
|
if (*ch <= ' ' || *ch > '~')
|
|
break;
|
|
if (*ch || !index(host, '.'))
|
|
{
|
|
sendto_ops("Bogus server name (%s) from %s", host,
|
|
get_client_name(cptr, TRUE));
|
|
return exit_client_msg(cptr, cptr, &me,
|
|
"Bogus server name (%s)", host);
|
|
}
|
|
|
|
if (IsPerson(cptr))
|
|
{
|
|
/*
|
|
** A local link that has been identified as a USER
|
|
** tries something fishy... ;-)
|
|
*/
|
|
sendto_one(cptr, err_str(ERR_ALREADYREGISTRED),
|
|
me.name, parv[0]);
|
|
sendto_ops("User %s trying to become a server %s",
|
|
get_client_name(cptr, TRUE),host);
|
|
return 0;
|
|
}
|
|
|
|
if (IsServer(cptr))
|
|
{
|
|
/*
|
|
** A local server introduces a new server behind this link.
|
|
** Check if this is allowed according L:, H: and Q: lines.
|
|
*/
|
|
if (info[0] == '\0')
|
|
return exit_client_msg(cptr, cptr, &me,
|
|
"No server info specified for %s", host);
|
|
|
|
/*
|
|
** See if the newly found server is behind a guaranteed
|
|
** leaf (L-line). If so, close the link.
|
|
*/
|
|
if ((lhconf = find_conf_host(cptr->confs, host, CONF_LEAF)) &&
|
|
(!lhconf->port || (hop > lhconf->port)))
|
|
{
|
|
/*
|
|
** L: lines normally come in pairs, here we try to
|
|
** make sure that the oldest link is squitted, not
|
|
** both.
|
|
*/
|
|
active_lh_line = 1;
|
|
if (timestamp <= cptr->serv->timestamp)
|
|
LHcptr = NULL; /* Kill incoming server */
|
|
else
|
|
LHcptr = cptr; /* Squit ourselfs */
|
|
}
|
|
else if (!(lhconf = find_conf_host(cptr->confs, host, CONF_HUB)) ||
|
|
(lhconf->port && (hop > lhconf->port)) )
|
|
{
|
|
aClient *ac3ptr;
|
|
active_lh_line = 2;
|
|
/* Look for net junction causing this: */
|
|
LHcptr = (Protocol(sptr)>4) ? NULL : /* incoming server */
|
|
cptr; /* Or local if prot==4 */
|
|
if (Protocol(sptr)>4 && *parv[5]!='J')
|
|
for (ac3ptr = sptr; ac3ptr != &me;
|
|
ac3ptr = ac3ptr->serv->up)
|
|
if (IsJunction(ac3ptr))
|
|
{
|
|
LHcptr=ac3ptr;
|
|
break;
|
|
}
|
|
}
|
|
/*
|
|
** See if the newly found server has a Q line for it in
|
|
** our conf. If it does, lose the link that brought it
|
|
** into our network. Format:
|
|
**
|
|
** Q:<unused>:<reason>:<servername>
|
|
**
|
|
** Example: Q:*:for the hell of it:eris.Berkeley.EDU
|
|
*/
|
|
if ((aconf = find_conf_name(host, CONF_QUARANTINED_SERVER)))
|
|
{
|
|
sendto_ops_butone(NULL, &me,
|
|
":%s WALLOPS * :%s brought in %s, %s %s",
|
|
me.name, me.name, get_client_name(cptr,FALSE),
|
|
host, "closing link because",
|
|
BadPtr(aconf->passwd) ? "reason unspecified" :
|
|
aconf->passwd);
|
|
|
|
return exit_client_msg(cptr, cptr, &me,
|
|
"%s is not welcome: %s. %s", host,
|
|
BadPtr(aconf->passwd) ? "reason unspecified" :
|
|
aconf->passwd, "Q-lined.");
|
|
}
|
|
}
|
|
|
|
if (IsUnknown(cptr) || IsHandshake(cptr))
|
|
{
|
|
char *encr;
|
|
|
|
/*
|
|
** A local link that is still in undefined state wants
|
|
** to be a SERVER. Check if this is allowed and change
|
|
** status accordingly...
|
|
*/
|
|
/*
|
|
** If there is more then one server on the same machine
|
|
** that we try to connect to, it could be that the /CONNECT
|
|
** <mask> caused this connect to be put at the wrong place
|
|
** in the hashtable.
|
|
** --Run
|
|
*/
|
|
if (IsHandshake(cptr) &&
|
|
hash_nick_name(cptr->name) != hash_nick_name(host))
|
|
{
|
|
del_from_client_hash_table(cptr->name, cptr);
|
|
add_to_client_hash_table(host, cptr);
|
|
}
|
|
strncpyzt(cptr->name, host, sizeof(cptr->name));
|
|
strncpyzt(cptr->info, info[0] ? info:me.name, sizeof(cptr->info));
|
|
cptr->hopcount = hop;
|
|
|
|
/* check connection rules */
|
|
for (cconf = conf; cconf; cconf = cconf->next)
|
|
if ((cconf->status == CONF_CRULEALL) &&
|
|
(matches(cconf->host, host) == 0))
|
|
if (crule_eval (cconf->passwd))
|
|
{
|
|
ircstp->is_ref++;
|
|
sendto_ops("Refused connection from %s.",
|
|
get_client_host(cptr));
|
|
return exit_client(cptr, cptr, &me,
|
|
"Disallowed by connection rule");
|
|
}
|
|
|
|
if (check_server_init(cptr))
|
|
{
|
|
ircstp->is_ref++;
|
|
sendto_ops("Received unauthorized connection from %s.",
|
|
get_client_host(cptr));
|
|
return exit_client(cptr, cptr, &me, "No C/N conf lines");
|
|
}
|
|
|
|
host = cptr->name;
|
|
|
|
current_load_data.conn_count++;
|
|
update_load();
|
|
|
|
if (!(aconf = find_conf(cptr->confs, host, CONF_NOCONNECT_SERVER)))
|
|
{
|
|
ircstp->is_ref++;
|
|
sendto_ops("Access denied. No N line for server %s", inpath);
|
|
return exit_client_msg(cptr, cptr, &me,
|
|
"Access denied. No N line for server %s", inpath);
|
|
}
|
|
if (!(bconf = find_conf(cptr->confs, host, CONF_CONNECT_SERVER)))
|
|
{
|
|
ircstp->is_ref++;
|
|
sendto_ops("Only N (no C) field for server %s",inpath);
|
|
return exit_client_msg(cptr, cptr, &me,
|
|
"Only N (no C) field for server %s", inpath);
|
|
}
|
|
|
|
#ifdef CRYPT_LINK_PASSWORD
|
|
/* passwd may be NULL. Head it off at the pass... */
|
|
if(*cptr->passwd)
|
|
{
|
|
char salt[3];
|
|
extern char *crypt();
|
|
|
|
salt[0]=aconf->passwd[0];
|
|
salt[1]=aconf->passwd[1];
|
|
salt[2]='\0';
|
|
encr = crypt(cptr->passwd, salt);
|
|
}
|
|
else
|
|
encr = "";
|
|
#else
|
|
encr = cptr->passwd;
|
|
#endif /* CRYPT_LINK_PASSWORD */
|
|
if (*aconf->passwd && !StrEq(aconf->passwd, encr))
|
|
{
|
|
ircstp->is_ref++;
|
|
sendto_ops("Access denied (passwd mismatch) %s", inpath);
|
|
return exit_client_msg(cptr, cptr, &me,
|
|
"No Access (passwd mismatch) %s", inpath);
|
|
}
|
|
bzero(cptr->passwd, sizeof(cptr->passwd));
|
|
|
|
#ifndef HUB
|
|
for (i = 0; i <= highest_fd; i++)
|
|
if (local[i] && IsServer(local[i]))
|
|
{
|
|
active_lh_line = 3;
|
|
LHcptr = cptr;
|
|
break;
|
|
}
|
|
#endif
|
|
if (!IsUnknown(cptr))
|
|
{
|
|
s = (char *)index(aconf->host, '@');
|
|
*s = '\0'; /* should never be NULL */
|
|
Debug((DEBUG_INFO, "Check Usernames [%s]vs[%s]",
|
|
aconf->host, cptr->username));
|
|
if (matches(aconf->host, cptr->username))
|
|
{
|
|
*s = '@';
|
|
ircstp->is_ref++;
|
|
sendto_ops("Username mismatch [%s]v[%s] : %s",
|
|
aconf->host, cptr->username,
|
|
get_client_name(cptr, TRUE));
|
|
return exit_client(cptr, cptr, &me, "Bad Username");
|
|
}
|
|
*s = '@';
|
|
}
|
|
}
|
|
|
|
/*
|
|
** We want to find IsConnecting() and IsHandshake() too,
|
|
** use find_client(). The second try is for hostmasking, although
|
|
** I doubt if that is still fully supported at all --Run.
|
|
*/
|
|
while ((acptr = find_client(host, NULL)) ||
|
|
(acptr = find_name(host, NULL)))
|
|
{
|
|
/*
|
|
** This link is trying feed me a server that I already have
|
|
** access through another path
|
|
**
|
|
** Do not allow Uworld to do this.
|
|
** Do not allow servers that are juped.
|
|
** Do not allow servers that have older link timestamps
|
|
** then this try.
|
|
**
|
|
** If my ircd.conf sucks, I can try to connect to myself:
|
|
*/
|
|
if (acptr == &me)
|
|
return exit_client_msg(cptr, cptr, &me,
|
|
"nick collision with me (%s)", host);
|
|
/*
|
|
** Kill our try, if we had one.
|
|
*/
|
|
if (IsConnecting(acptr))
|
|
{
|
|
if (!active_lh_line && exit_client(cptr, acptr, &me,
|
|
"Just connected via another link") == CPTR_KILLED)
|
|
return CPTR_KILLED;
|
|
/*
|
|
** We can have only ONE 'IsConnecting', 'IsHandshake' or
|
|
** 'IsServer', because new 'IsConnecting's are refused to
|
|
** the same server if we already had it.
|
|
*/
|
|
break;
|
|
}
|
|
/*
|
|
** Avoid other nick collisions...
|
|
** This is a doubtfull test though, what else would it be
|
|
** when it has a server.name ?
|
|
*/
|
|
else if (!IsServer(acptr) && !IsHandshake(acptr))
|
|
return exit_client_msg(cptr, cptr, &me,
|
|
"Nickname %s already exists!", host);
|
|
/*
|
|
** Our new server might be a juped server,
|
|
** or someone trying abuse a second Uworld:
|
|
*/
|
|
else if (IsServer(acptr) &&
|
|
(strncasecmp(acptr->info,"JUPE",4)==0 ||
|
|
find_conf_host(cptr->confs, sptr->name, CONF_UWORLD)))
|
|
{
|
|
if (!IsServer(sptr))
|
|
return exit_client(cptr, sptr, &me, acptr->info);
|
|
sendto_one(cptr,
|
|
":%s WALLOPS :Received :%s SERVER %s from %s !?!",
|
|
me.name, parv[0], parv[1], cptr->name);
|
|
return exit_new_server(cptr, sptr, host, prot, timestamp,
|
|
"%s", acptr->info);
|
|
}
|
|
/*
|
|
** Of course we find the handshake this link was before :)
|
|
*/
|
|
else if (IsHandshake(acptr) && acptr == cptr)
|
|
break;
|
|
/*
|
|
** This is the old protocol, can be removed when all upgraded
|
|
** to u2.9.
|
|
*/
|
|
if (prot<9 || (IsServer(acptr) && Protocol(acptr)<9))
|
|
return exit_client_msg(cptr, cptr, &me,
|
|
"server %s already exists (protocol 4 failure)", host);
|
|
/*
|
|
** Here we have a server nick collision...
|
|
** We don't want to kill the link that was last /connected,
|
|
** but we neither want to kill a good (old) link.
|
|
** Therefor we kill the second youngest link.
|
|
*/
|
|
else
|
|
{
|
|
aClient *c2ptr = NULL, *c3ptr = acptr;
|
|
aClient *ac2ptr, *ac3ptr;
|
|
|
|
/* Search youngest link: */
|
|
for (ac3ptr = acptr; ac3ptr != &me; ac3ptr = ac3ptr->serv->up)
|
|
if (ac3ptr->serv->timestamp > c3ptr->serv->timestamp)
|
|
c3ptr = ac3ptr;
|
|
if (IsServer(sptr))
|
|
{
|
|
for (ac3ptr = sptr; ac3ptr != &me;
|
|
ac3ptr = ac3ptr->serv->up)
|
|
if (ac3ptr->serv->timestamp > c3ptr->serv->timestamp)
|
|
c3ptr = ac3ptr;
|
|
}
|
|
if (timestamp > c3ptr->serv->timestamp)
|
|
{
|
|
c3ptr = NULL;
|
|
c2ptr = acptr; /* Make sure they differ */
|
|
}
|
|
/* Search second youngest link: */
|
|
for (ac2ptr = acptr; ac2ptr != &me; ac2ptr = ac2ptr->serv->up)
|
|
if (ac2ptr != c3ptr &&
|
|
ac2ptr->serv->timestamp >
|
|
(c2ptr ? c2ptr->serv->timestamp : timestamp))
|
|
c2ptr = ac2ptr;
|
|
if (IsServer(sptr))
|
|
{
|
|
for (ac2ptr = sptr; ac2ptr != &me;
|
|
ac2ptr = ac2ptr->serv->up)
|
|
if (ac2ptr != c3ptr &&
|
|
ac2ptr->serv->timestamp >
|
|
(c2ptr ? c2ptr->serv->timestamp : timestamp))
|
|
c2ptr = ac2ptr;
|
|
}
|
|
if (c3ptr && timestamp >
|
|
(c2ptr ? c2ptr->serv->timestamp : timestamp))
|
|
c2ptr = NULL;
|
|
/* If timestamps are equal, decide which link to break
|
|
** by name.
|
|
*/
|
|
if ((c2ptr ? c2ptr->serv->timestamp : timestamp) ==
|
|
(c3ptr ? c3ptr->serv->timestamp : timestamp))
|
|
{
|
|
char *n2, *n2up;
|
|
char *n3, *n3up;
|
|
if (c2ptr)
|
|
{ n2=c2ptr->name;
|
|
n2up=MyConnect(c2ptr)?me.name:c2ptr->serv->up->name; }
|
|
else
|
|
{ n2=host;
|
|
n2up=IsServer(sptr)?sptr->name:me.name; }
|
|
if (c3ptr)
|
|
{ n3=c3ptr->name;
|
|
n3up=MyConnect(c3ptr)?me.name:c3ptr->serv->up->name; }
|
|
else
|
|
{ n3=host;
|
|
n3up=IsServer(sptr)?sptr->name:me.name; }
|
|
if (strcmp(n2, n2up) > 0)
|
|
n2=n2up;
|
|
if (strcmp(n3, n3up) > 0)
|
|
n3=n3up;
|
|
if (strcmp(n3, n2) > 0)
|
|
{
|
|
ac2ptr = c2ptr; c2ptr = c3ptr; c3ptr = ac2ptr;
|
|
}
|
|
}
|
|
/* Now squit the second youngest link: */
|
|
if (!c2ptr)
|
|
return exit_new_server(cptr, sptr, host, prot, timestamp,
|
|
"server %s already exists and is %ld seconds younger.",
|
|
host, (long)acptr->serv->timestamp - (long)timestamp);
|
|
else if (c2ptr->from == cptr || IsServer(sptr))
|
|
{
|
|
if (active_lh_line)
|
|
{
|
|
/*
|
|
** If the L: or H: line also gets rid of this link,
|
|
** we sent just one squit.
|
|
*/
|
|
if (LHcptr && a_kills_b_too(LHcptr, c2ptr))
|
|
break;
|
|
/*
|
|
** If breaking the loop here solves the L: or H:
|
|
** line problem, we don't squit that.
|
|
*/
|
|
if (c2ptr->from == cptr ||
|
|
(LHcptr && a_kills_b_too(c2ptr, LHcptr)))
|
|
active_lh_line = 0;
|
|
else
|
|
{
|
|
/*
|
|
** If we still have a L: or H: line problem,
|
|
** we prefer to squit the new server, solving
|
|
** loop and L:/H: line problem with only one squit.
|
|
*/
|
|
LHcptr = NULL;
|
|
break;
|
|
}
|
|
}
|
|
/*
|
|
** If the new server was introduced by a server that caused a
|
|
** Ghost less then 20 seconds ago, this is probably also
|
|
** a Ghost... (20 seconds is more then enough because all
|
|
** SERVER messages are at the beginning of a net.burst). --Run
|
|
*/
|
|
if (now - cptr->serv->ghost < 20)
|
|
{
|
|
if (exit_client(cptr, acptr, &me, "Ghost loop") == CPTR_KILLED)
|
|
return CPTR_KILLED;
|
|
}
|
|
else if (exit_client_msg(cptr, c2ptr, &me,
|
|
"Loop <-- %s (new link is %d seconds younger)", host,
|
|
(c3ptr ? (long)c3ptr->serv->timestamp : timestamp) -
|
|
(long)c2ptr->serv->timestamp) == CPTR_KILLED)
|
|
return CPTR_KILLED;
|
|
}
|
|
else
|
|
{
|
|
if (active_lh_line)
|
|
{
|
|
if (LHcptr && a_kills_b_too(LHcptr, acptr))
|
|
break;
|
|
if (acptr->from == cptr ||
|
|
(LHcptr && a_kills_b_too(acptr, LHcptr)))
|
|
active_lh_line = 0;
|
|
else
|
|
{
|
|
LHcptr = NULL;
|
|
break;
|
|
}
|
|
}
|
|
/*
|
|
** We can't believe it is a lagged server message
|
|
** when it directly connects to us...
|
|
** kill the older link at the ghost, rather then
|
|
** at the second youngest link, assuming it isn't
|
|
** a REAL loop.
|
|
*/
|
|
ghost = now; /* Mark that it caused a ghost */
|
|
if (exit_client(cptr, acptr, &me, "Ghost") == CPTR_KILLED)
|
|
return CPTR_KILLED;
|
|
break;
|
|
}
|
|
/*
|
|
** Did we kill the incoming server off already ?
|
|
*/
|
|
if (c2ptr->from == cptr)
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (active_lh_line)
|
|
{
|
|
if (LHcptr == NULL)
|
|
return exit_new_server(cptr, sptr, host, prot, timestamp,
|
|
(active_lh_line == 2) ?
|
|
"Non-Hub link %s <- %s(%s)" :
|
|
"Leaf-only link %s <- %s(%s)",
|
|
get_client_name(cptr, TRUE), host,
|
|
lhconf ? (lhconf->host ? lhconf->host : "*") : "!");
|
|
else
|
|
{
|
|
register int killed = a_kills_b_too(LHcptr, sptr);
|
|
if (active_lh_line < 3)
|
|
{
|
|
if (exit_client_msg(cptr, LHcptr, &me,
|
|
(active_lh_line == 2) ?
|
|
"Non-Hub link %s <- %s(%s)" :
|
|
"Leaf-only link %s <- %s(%s)",
|
|
get_client_name(cptr, TRUE), host,
|
|
lhconf ? (lhconf->host ? lhconf->host : "*") : "!")
|
|
== CPTR_KILLED)
|
|
return CPTR_KILLED;
|
|
}
|
|
else
|
|
{
|
|
ircstp->is_ref++;
|
|
if (exit_client(cptr, LHcptr, &me, "I'm a leaf") ==
|
|
CPTR_KILLED)
|
|
return CPTR_KILLED;
|
|
}
|
|
/*
|
|
** Did we kill the incoming server off already ?
|
|
*/
|
|
if (killed)
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (IsServer(cptr))
|
|
{
|
|
/*
|
|
** Server is informing about a new server behind
|
|
** this link. Create REMOTE server structure,
|
|
** add it to list and propagate word to my other
|
|
** server links...
|
|
*/
|
|
|
|
acptr = make_client(cptr);
|
|
(void)make_server(acptr);
|
|
acptr->serv->prot = prot;
|
|
acptr->serv->timestamp = timestamp;
|
|
acptr->hopcount = hop;
|
|
strncpyzt(acptr->name, host, sizeof(acptr->name));
|
|
strncpyzt(acptr->info, info, sizeof(acptr->info));
|
|
acptr->serv->up = sptr;
|
|
acptr->serv->updown = add_dlink(&sptr->serv->down, acptr);
|
|
SetServer(acptr);
|
|
acptr->flags|=FLAGS_TS8;
|
|
add_client_to_list(acptr);
|
|
(void)add_to_client_hash_table(acptr->name, acptr);
|
|
if (Protocol(cptr)>4 && *parv[5] == 'J')
|
|
{
|
|
sendto_ops("Net junction: %s %s", sptr->name, acptr->name);
|
|
SetJunction(acptr);
|
|
}
|
|
/*
|
|
** Old sendto_serv_but_one() call removed because we now
|
|
** need to send different names to different servers
|
|
** (domain name matching)
|
|
*/
|
|
for (i = 0; i <= highest_fd; i++)
|
|
{
|
|
if (!(bcptr = local[i]) || !IsServer(bcptr) ||
|
|
bcptr == cptr || IsMe(bcptr))
|
|
continue;
|
|
if (!(cconf = bcptr->serv->nline))
|
|
{
|
|
sendto_ops("Lost N-line for %s on %s. Closing",
|
|
get_client_name(cptr, TRUE), host);
|
|
return exit_client(cptr, cptr, &me,
|
|
"Lost N line");
|
|
}
|
|
if (matches(my_name_for_link(me.name, cconf),
|
|
acptr->name) == 0)
|
|
continue;
|
|
if (Protocol(bcptr) > 4)
|
|
{
|
|
if (Protocol(cptr) > 4)
|
|
sendto_one(bcptr, ":%s SERVER %s %d 0 %s %s :%s",
|
|
parv[0], acptr->name, hop+1,
|
|
parv[4], parv[5], acptr->info);
|
|
else
|
|
sendto_one(bcptr, ":%s SERVER %s %d 0 0 P0%u :%s",
|
|
parv[0], acptr->name, hop+1,
|
|
Protocol(acptr), acptr->info);
|
|
}
|
|
else
|
|
sendto_one(bcptr, ":%s SERVER %s %d :%s",
|
|
parv[0], acptr->name, hop+1, acptr->info);
|
|
}
|
|
#ifdef USE_SERVICES
|
|
check_services_butone(SERVICE_WANT_SERVER, sptr,
|
|
":%s SERVER %s %d %s %s :%s", parv[0],
|
|
acptr->name, hop+1, parv[3], parv[4], acptr->info);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
if (IsUnknown(cptr) || IsHandshake(cptr))
|
|
{
|
|
(void)make_server(cptr);
|
|
cptr->serv->timestamp = timestamp;
|
|
cptr->serv->prot = prot;
|
|
cptr->serv->ghost = ghost;
|
|
|
|
ret = m_server_estab(cptr, aconf, bconf);
|
|
}
|
|
else ret = 0;
|
|
|
|
if (IsServer(cptr) && prot > 4 && start_timestamp > 780000000 &&
|
|
start_timestamp < me.serv->timestamp)
|
|
{
|
|
#ifndef RELIABLE_CLOCK
|
|
#ifdef TESTNET
|
|
sendto_ops("Debug: my start time: %lu ; others start time: %lu",
|
|
me.serv->timestamp, start_timestamp);
|
|
sendto_ops("Debug: receive time: %lu ; received timestamp: %lu"
|
|
" ; difference %ld", recv_time, timestamp, timestamp - recv_time);
|
|
#endif
|
|
me.serv->timestamp += timestamp - recv_time;
|
|
TSoffset += timestamp - recv_time;
|
|
#else
|
|
if (abs(timestamp - recv_time)>30)
|
|
{ sendto_ops("Connected to a net with a timestamp-clock"
|
|
" difference of %ld seconds! Used SETTIME to correct"
|
|
" this.", timestamp - recv_time);
|
|
sendto_one(cptr, ":%s SETTIME %lu :%s", me.name, TStime(),
|
|
me.name); }
|
|
#endif
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* m_server_estab, may only be called after a SERVER was received from cptr,
|
|
* and thus make_server was called, and serv->prot set. --Run
|
|
*/
|
|
int m_server_estab(cptr, aconf, bconf)
|
|
Reg1 aClient *cptr;
|
|
Reg2 aConfItem *aconf, *bconf;
|
|
{
|
|
Reg3 aClient *acptr;
|
|
Reg4 Link *lp;
|
|
char *inpath, *s;
|
|
int split, i;
|
|
|
|
split = (strcasecmp(cptr->name, cptr->sockhost) &&
|
|
strncasecmp(cptr->info, "JUPE", 4));
|
|
inpath = get_client_name(cptr,TRUE);
|
|
|
|
if (IsUnknown(cptr))
|
|
{
|
|
if (bconf->passwd[0])
|
|
sendto_one(cptr,"PASS :%s",bconf->passwd);
|
|
/*
|
|
** Pass my info to the new server
|
|
*/
|
|
if (Protocol(cptr)==4)
|
|
sendto_one(cptr, "SERVER %s 1 :%s",
|
|
my_name_for_link(me.name, aconf),
|
|
(me.info[0]) ? (me.info) : "IRCers United");
|
|
else
|
|
sendto_one(cptr, "SERVER %s 1 %lu %lu J%s :%s",
|
|
my_name_for_link(me.name, aconf),
|
|
me.serv->timestamp, cptr->serv->timestamp,
|
|
MAJOR_PROTOCOL,
|
|
(me.info[0]) ? (me.info) : "IRCers United");
|
|
}
|
|
|
|
det_confs_butmask(cptr,
|
|
CONF_LEAF|CONF_HUB|CONF_NOCONNECT_SERVER|CONF_UWORLD);
|
|
|
|
if (!IsHandshake(cptr))
|
|
(void)add_to_client_hash_table(cptr->name, cptr);
|
|
SetServer(cptr);
|
|
cptr->flags|=FLAGS_TS8;
|
|
nextping = now;
|
|
if (cptr->serv->user && (acptr=find_client(cptr->serv->by, NULL)) &&
|
|
acptr->user == cptr->serv->user)
|
|
sendto_one(acptr, ":%s NOTICE %s :Link with %s established.",
|
|
me.name, acptr->name, inpath);
|
|
else
|
|
acptr = NULL;
|
|
sendto_lops_butone(acptr, "Link with %s established.", inpath);
|
|
cptr->serv->up = &me;
|
|
cptr->serv->updown = add_dlink(&me.serv->down, cptr);
|
|
cptr->serv->nline = aconf;
|
|
sendto_ops("Net junction: %s %s", me.name, cptr->name);
|
|
SetJunction(cptr);
|
|
#ifdef USE_SERVICES
|
|
check_services_butone(SERVICE_WANT_SERVER, sptr,
|
|
":%s SERVER %s %d :%s", parv[0],
|
|
cptr->name, hop+1, cptr->info);
|
|
#endif
|
|
/*
|
|
** Old sendto_serv_but_one() call removed because we now
|
|
** need to send different names to different servers
|
|
** (domain name matching) Send new server to other servers.
|
|
*/
|
|
for (i = 0; i <= highest_fd; i++)
|
|
{
|
|
if (!(acptr = local[i]) || !IsServer(acptr) ||
|
|
acptr == cptr || IsMe(acptr))
|
|
continue;
|
|
if ((aconf = acptr->serv->nline) &&
|
|
!matches(my_name_for_link(me.name, aconf), cptr->name))
|
|
continue;
|
|
if (split && Protocol(acptr)<9)
|
|
{
|
|
sendto_one(acptr,":%s SERVER %s 2 :[%s] %s", me.name,
|
|
cptr->name, cptr->sockhost, cptr->info);
|
|
}
|
|
else if (split)
|
|
{
|
|
sendto_one(acptr, ":%s SERVER %s 2 0 %lu %s%u :[%s] %s",
|
|
me.name, cptr->name, cptr->serv->timestamp,
|
|
(Protocol(cptr)>9)?"J":"J0", Protocol(cptr),
|
|
cptr->sockhost, cptr->info);
|
|
}
|
|
else if (!split && Protocol(acptr)<9)
|
|
{
|
|
sendto_one(acptr,":%s SERVER %s 2 :%s",
|
|
me.name, cptr->name, cptr->info);
|
|
}
|
|
else
|
|
{
|
|
sendto_one(acptr, ":%s SERVER %s 2 0 %lu %s%u :%s",
|
|
me.name, cptr->name, cptr->serv->timestamp,
|
|
(Protocol(cptr)>9)?"J":"J0", Protocol(cptr),
|
|
cptr->info);
|
|
}
|
|
}
|
|
|
|
/*
|
|
** Pass on my client information to the new server
|
|
**
|
|
** First, pass only servers (idea is that if the link gets
|
|
** cancelled beacause the server was already there,
|
|
** there are no NICK's to be cancelled...). Of course,
|
|
** if cancellation occurs, all this info is sent anyway,
|
|
** and I guess the link dies when a read is attempted...? --msa
|
|
**
|
|
** Note: Link cancellation to occur at this point means
|
|
** that at least two servers from my fragment are building
|
|
** up connection this other fragment at the same time, it's
|
|
** a race condition, not the normal way of operation...
|
|
**
|
|
*/
|
|
|
|
aconf = cptr->serv->nline;
|
|
for (acptr = &me; acptr; acptr = acptr->prev)
|
|
{
|
|
/* acptr->from == acptr for acptr == cptr */
|
|
if (acptr->from == cptr)
|
|
continue;
|
|
if (IsServer(acptr))
|
|
{
|
|
if (matches(my_name_for_link(me.name, aconf),
|
|
acptr->name) == 0)
|
|
continue;
|
|
split = (MyConnect(acptr) &&
|
|
strcasecmp(acptr->name, acptr->sockhost) &&
|
|
strncasecmp(acptr->info, "JUPE", 4));
|
|
if (split && Protocol(cptr)<9)
|
|
{
|
|
sendto_one(cptr, ":%s SERVER %s %d :[%s] %s",
|
|
acptr->serv->up->name, acptr->name,
|
|
acptr->hopcount+1,
|
|
acptr->sockhost, acptr->info);
|
|
}
|
|
else if (split)
|
|
{
|
|
sendto_one(cptr,
|
|
":%s SERVER %s %d 0 %lu %s%u :[%s] %s",
|
|
acptr->serv->up->name, acptr->name,
|
|
acptr->hopcount+1, acptr->serv->timestamp,
|
|
(Protocol(acptr)>9)?"P":"P0", Protocol(acptr),
|
|
acptr->sockhost, acptr->info);
|
|
}
|
|
else if (!split && Protocol(cptr)<9)
|
|
{
|
|
sendto_one(cptr, ":%s SERVER %s %d :%s",
|
|
acptr->serv->up->name, acptr->name,
|
|
acptr->hopcount+1, acptr->info);
|
|
}
|
|
else
|
|
{
|
|
sendto_one(cptr,
|
|
":%s SERVER %s %d 0 %lu %s%u :%s",
|
|
acptr->serv->up->name, acptr->name,
|
|
acptr->hopcount+1, acptr->serv->timestamp,
|
|
(Protocol(acptr)>9)?"P":"P0", Protocol(acptr),
|
|
acptr->info);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (acptr = &me; acptr; acptr = acptr->prev)
|
|
{
|
|
/* acptr->from == acptr for acptr == cptr */
|
|
if (acptr->from == cptr)
|
|
continue;
|
|
if (IsPerson(acptr))
|
|
{
|
|
/*
|
|
** IsPerson(x) is true only when IsClient(x) is true.
|
|
** These are only true when *BOTH* NICK and USER have
|
|
** been received. -avalon
|
|
** Or only NICK in new format. --Run
|
|
*/
|
|
sendto_one(cptr,":%s NICK %s %d %d %s %s %s :%s",
|
|
acptr->user->server->name,
|
|
acptr->name, acptr->hopcount + 1, acptr->lastnick,
|
|
acptr->user->username, acptr->user->host,
|
|
acptr->user->server->name, acptr->info);
|
|
send_umode(cptr, acptr, 0, SEND_UMODES, buf);
|
|
send_user_joins(cptr, acptr);
|
|
}
|
|
else if (IsService(acptr))
|
|
{
|
|
sendto_one(cptr,"NICK %s :%d",
|
|
acptr->name, acptr->hopcount + 1);
|
|
sendto_one(cptr,":%s SERVICE * * :%s",
|
|
acptr->name, acptr->info);
|
|
}
|
|
}
|
|
/*
|
|
** Last, pass all channels plus statuses
|
|
*/
|
|
{
|
|
Reg1 aChannel *chptr;
|
|
for (chptr = channel; chptr; chptr = chptr->nextch)
|
|
send_channel_modes(cptr, chptr);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
** m_info
|
|
** parv[0] = sender prefix
|
|
** parv[1] = servername
|
|
*/
|
|
int m_info(cptr, sptr, parc, parv)
|
|
aClient *cptr, *sptr;
|
|
int parc;
|
|
char *parv[];
|
|
{
|
|
char **text = infotext;
|
|
|
|
if (check_registered(sptr))
|
|
return 0;
|
|
|
|
if (hunt_server(cptr,sptr,":%s INFO :%s",1,parc,parv) == HUNTED_ISME)
|
|
{
|
|
while (*text)
|
|
sendto_one(sptr, rpl_str(RPL_INFO),
|
|
me.name, parv[0], *text++);
|
|
|
|
sendto_one(sptr, rpl_str(RPL_INFO), me.name, parv[0], "");
|
|
sendto_one(sptr,
|
|
":%s %d %s :Birth Date: %s, compile # %s",
|
|
me.name, RPL_INFO, parv[0], creation, generation);
|
|
sendto_one(sptr, ":%s %d %s :On-line since %s",
|
|
me.name, RPL_INFO, parv[0],
|
|
myctime(me.firsttime));
|
|
sendto_one(sptr, rpl_str(RPL_ENDOFINFO), me.name, parv[0]);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
** m_links
|
|
** parv[0] = sender prefix
|
|
** parv[1] = servername mask
|
|
** or
|
|
** parv[0] = sender prefix
|
|
** parv[1] = server to query
|
|
** parv[2] = servername mask
|
|
*/
|
|
int m_links(cptr, sptr, parc, parv)
|
|
aClient *cptr, *sptr;
|
|
int parc;
|
|
char *parv[];
|
|
{
|
|
char *mask;
|
|
aClient *acptr;
|
|
|
|
if (check_registered_user(sptr))
|
|
return 0;
|
|
|
|
if (parc > 2)
|
|
{
|
|
if (hunt_server(cptr, sptr, ":%s LINKS %s :%s", 1, parc, parv)
|
|
!= HUNTED_ISME)
|
|
return 0;
|
|
mask = parv[2];
|
|
}
|
|
else
|
|
mask = parc < 2 ? NULL : parv[1];
|
|
|
|
for (acptr = client, (void)collapse(mask); acptr; acptr = acptr->next)
|
|
{
|
|
if (!IsServer(acptr) && !IsMe(acptr))
|
|
continue;
|
|
if (!BadPtr(mask) && match(mask, acptr->name))
|
|
continue;
|
|
sendto_one(sptr, rpl_str(RPL_LINKS),
|
|
me.name, parv[0], acptr->name, acptr->serv->up->name,
|
|
acptr->hopcount, acptr->serv->prot,
|
|
(acptr->info[0] ? acptr->info :
|
|
"(Unknown Location)"));
|
|
}
|
|
|
|
sendto_one(sptr, rpl_str(RPL_ENDOFLINKS), me.name, parv[0],
|
|
BadPtr(mask) ? "*" : mask);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
** m_summon should be redefined to ":prefix SUMMON host user" so
|
|
** that "hunt_server"-function could be used for this too!!! --msa
|
|
** As of 2.7.1e, this was the case. -avalon
|
|
**
|
|
** parv[0] = sender prefix
|
|
** parv[1] = user
|
|
** parv[2] = server
|
|
** parv[3] = channel (optional)
|
|
*/
|
|
int m_summon(cptr, sptr, parc, parv)
|
|
aClient *sptr, *cptr;
|
|
int parc;
|
|
char *parv[];
|
|
{
|
|
char *host, *user, *chname;
|
|
#ifdef ENABLE_SUMMON
|
|
char hostbuf[17], namebuf[10], linebuf[10];
|
|
# ifdef LEAST_IDLE
|
|
char linetmp[10], ttyname[15]; /* Ack */
|
|
struct stat stb;
|
|
time_t ltime = (time_t)0;
|
|
# endif
|
|
int fd, flag = 0;
|
|
#endif
|
|
|
|
if (check_registered_user(sptr))
|
|
return 0;
|
|
if (parc < 2 || *parv[1] == '\0')
|
|
{
|
|
sendto_one(sptr, err_str(ERR_NORECIPIENT),
|
|
me.name, parv[0], "SUMMON");
|
|
return 0;
|
|
}
|
|
user = parv[1];
|
|
host = (parc < 3 || BadPtr(parv[2])) ? me.name : parv[2];
|
|
chname = (parc > 3) ? parv[3] : "*";
|
|
/*
|
|
** Summoning someone on remote server, find out which link to
|
|
** use and pass the message there...
|
|
*/
|
|
parv[1] = user;
|
|
parv[2] = host;
|
|
parv[3] = chname;
|
|
parv[4] = NULL;
|
|
if (hunt_server(cptr, sptr, ":%s SUMMON %s %s %s", 2, parc, parv) ==
|
|
HUNTED_ISME)
|
|
{
|
|
#ifdef ENABLE_SUMMON
|
|
if ((fd = utmp_open()) == -1)
|
|
{
|
|
sendto_one(sptr, err_str(ERR_FILEERROR),
|
|
me.name, parv[0], "open", UTMP);
|
|
return 0;
|
|
}
|
|
# ifndef LEAST_IDLE
|
|
while ((flag = utmp_read(fd, namebuf, linebuf, hostbuf,
|
|
sizeof(hostbuf))) == 0)
|
|
if (StrEq(namebuf,user))
|
|
break;
|
|
# else
|
|
/* use least-idle tty, not the first
|
|
* one we find in utmp. 10/9/90 Spike@world.std.com
|
|
* (loosely based on Jim Frost jimf@saber.com code)
|
|
*/
|
|
|
|
while ((flag = utmp_read(fd, namebuf, linetmp, hostbuf,
|
|
sizeof(hostbuf))) == 0)
|
|
{
|
|
if (StrEq(namebuf,user))
|
|
{
|
|
(void)sprintf(ttyname,"/dev/%s",linetmp);
|
|
if (stat(ttyname,&stb) == -1)
|
|
{
|
|
sendto_one(sptr,
|
|
err_str(ERR_FILEERROR),
|
|
me.name, sptr->name,
|
|
"stat", ttyname);
|
|
return 0;
|
|
}
|
|
if (!ltime)
|
|
{
|
|
ltime= stb.st_mtime;
|
|
(void)strcpy(linebuf,linetmp);
|
|
}
|
|
else if (stb.st_mtime > ltime) /* less idle */
|
|
{
|
|
ltime= stb.st_mtime;
|
|
(void)strcpy(linebuf,linetmp);
|
|
}
|
|
}
|
|
}
|
|
# endif
|
|
(void)utmp_close(fd);
|
|
# ifdef LEAST_IDLE
|
|
if (ltime == 0)
|
|
# else
|
|
if (flag == -1)
|
|
# endif
|
|
sendto_one(sptr, err_str(ERR_NOLOGIN),
|
|
me.name, parv[0], user);
|
|
else
|
|
summon(sptr, user, linebuf, chname);
|
|
#else
|
|
sendto_one(sptr, err_str(ERR_SUMMONDISABLED),
|
|
me.name, parv[0]);
|
|
#endif /* ENABLE_SUMMON */
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
** m_stats
|
|
** parv[0] = sender prefix
|
|
** parv[1] = statistics selector (defaults to Message frequency)
|
|
** parv[2] = server name (current server defaulted, if omitted)
|
|
**
|
|
** Currently supported are:
|
|
** M = Message frequency (the old stat behaviour)
|
|
** L = Local Link statistics
|
|
** C = Report C and N configuration lines
|
|
*/
|
|
/*
|
|
** m_stats/stats_conf
|
|
** Report N/C-configuration lines from this server. This could
|
|
** report other configuration lines too, but converting the
|
|
** status back to "char" is a bit akward--not worth the code
|
|
** it needs...
|
|
**
|
|
** Note: The info is reported in the order the server uses
|
|
** it--not reversed as in ircd.conf!
|
|
*/
|
|
|
|
static int report_array[15][3] = {
|
|
{ CONF_CONNECT_SERVER, RPL_STATSCLINE, 'C'},
|
|
{ CONF_NOCONNECT_SERVER, RPL_STATSNLINE, 'N'},
|
|
{ CONF_CLIENT, RPL_STATSILINE, 'I'},
|
|
{ CONF_KILL, RPL_STATSKLINE, 'K'},
|
|
{ CONF_QUARANTINED_SERVER,RPL_STATSQLINE, 'Q'},
|
|
{ CONF_LEAF, RPL_STATSLLINE, 'L'},
|
|
{ CONF_OPERATOR, RPL_STATSOLINE, 'O'},
|
|
{ CONF_HUB, RPL_STATSHLINE, 'H'},
|
|
{ CONF_LOCOP, RPL_STATSOLINE, 'o'},
|
|
{ CONF_CRULEALL, RPL_STATSDLINE, 'D'},
|
|
{ CONF_CRULEAUTO, RPL_STATSDLINE, 'd'},
|
|
{ CONF_SERVICE, RPL_STATSSLINE, 'S'},
|
|
{ CONF_UWORLD, RPL_STATSULINE, 'U'},
|
|
{ CONF_TLINES, RPL_STATSTLINE, 'T'},
|
|
{ 0, 0}
|
|
};
|
|
|
|
static void report_configured_links(sptr, mask)
|
|
aClient *sptr;
|
|
int mask;
|
|
{
|
|
static char null[] = "<NULL>";
|
|
aConfItem *tmp;
|
|
int *p, port;
|
|
char c, *host, *pass, *name;
|
|
|
|
for (tmp = conf; tmp; tmp = tmp->next)
|
|
if (tmp->status & mask)
|
|
{
|
|
for (p = &report_array[0][0]; *p; p += 3)
|
|
if (*p == tmp->status)
|
|
break;
|
|
if (!*p)
|
|
continue;
|
|
c = (char)*(p+2);
|
|
host = BadPtr(tmp->host) ? null : tmp->host;
|
|
pass = BadPtr(tmp->passwd) ? null : tmp->passwd;
|
|
name = BadPtr(tmp->name) ? null : tmp->name;
|
|
port = (int)tmp->port;
|
|
/*
|
|
* On K line the passwd contents can be
|
|
* displayed on STATS reply. -Vesa
|
|
*/
|
|
if (tmp->status == CONF_KILL)
|
|
sendto_one(sptr, rpl_str(p[1]), me.name,
|
|
sptr->name, c, host, pass,
|
|
name, port, get_conf_class(tmp));
|
|
/* connect rules are classless */
|
|
else if (tmp->status & CONF_CRULE)
|
|
sendto_one(sptr, rpl_str(p[1]), me.name,
|
|
sptr->name, c, host, name);
|
|
else if (tmp->status & CONF_TLINES)
|
|
sendto_one(sptr, rpl_str(p[1]), me.name,
|
|
sptr->name, c, host, pass);
|
|
else
|
|
sendto_one(sptr, rpl_str(p[1]), me.name,
|
|
sptr->name, c, host, name, port,
|
|
get_conf_class(tmp));
|
|
}
|
|
return;
|
|
}
|
|
|
|
int m_stats(cptr, sptr, parc, parv)
|
|
aClient *cptr, *sptr;
|
|
int parc;
|
|
char *parv[];
|
|
{
|
|
static char Sformat[] = ":%s %d %s Connection SendQ SendM SendKBytes RcveM RcveKBytes :Open since";
|
|
static char Lformat[] = ":%s %d %s %s %u %u %u %u %u :%u";
|
|
struct Message *mptr;
|
|
aClient *acptr;
|
|
aGline *agline, *a2gline;
|
|
char stat = parc > 1 ? parv[1][0] : '\0';
|
|
Reg1 int i;
|
|
int doall = 0, wilds = 0;
|
|
char *name;
|
|
|
|
if (check_registered(sptr))
|
|
return 0;
|
|
|
|
if (hunt_server(cptr,sptr,":%s STATS %s :%s",2,parc,parv)!=HUNTED_ISME)
|
|
return 0;
|
|
|
|
if (parc > 2)
|
|
{
|
|
name = parv[2];
|
|
if (!strcasecmp(name, me.name))
|
|
doall = 2;
|
|
else if (matches(name, me.name) == 0)
|
|
doall = 1;
|
|
if (index(name, '*') || index(name, '?'))
|
|
wilds = 1;
|
|
}
|
|
else
|
|
name = me.name;
|
|
|
|
switch (stat)
|
|
{
|
|
case 'L' : case 'l' :
|
|
/*
|
|
* send info about connections which match, or all if the
|
|
* mask matches me.name. Only restrictions are on those who
|
|
* are invisible not being visible to 'foreigners' who use
|
|
* a wild card based search to list it.
|
|
*/
|
|
sendto_one(sptr, Sformat, me.name, RPL_STATSLINKINFO, parv[0]);
|
|
for (i = 0; i <= highest_fd; i++)
|
|
{
|
|
if (!(acptr = local[i]))
|
|
continue;
|
|
if (IsInvisible(acptr) && (doall || wilds) &&
|
|
!(MyConnect(sptr) && IsOper(sptr)) &&
|
|
!IsAnOper(acptr) && (acptr != sptr))
|
|
continue;
|
|
if (!doall && wilds && matches(name, acptr->name))
|
|
continue;
|
|
if (!(doall || wilds) && strcasecmp(name, acptr->name))
|
|
continue;
|
|
sendto_one(sptr, Lformat, me.name,
|
|
RPL_STATSLINKINFO, parv[0],
|
|
(isupper(stat)) ?
|
|
get_client_name(acptr, TRUE) :
|
|
get_client_name(acptr, FALSE),
|
|
(int)DBufLength(&acptr->sendQ),
|
|
(int)acptr->sendM, (int)acptr->sendK,
|
|
(int)acptr->receiveM, (int)acptr->receiveK,
|
|
time(NULL) - acptr->firsttime);
|
|
}
|
|
break;
|
|
case 'C' : case 'c' :
|
|
report_configured_links(sptr, CONF_CONNECT_SERVER|
|
|
CONF_NOCONNECT_SERVER);
|
|
break;
|
|
case 'G' : case 'g' :
|
|
/* send glines */
|
|
for (agline = gline, a2gline = NULL; agline;
|
|
agline = agline->next) {
|
|
if (agline->expire <= TStime()) { /* handle expired glines */
|
|
free_gline(agline, a2gline);
|
|
agline = a2gline ? a2gline : gline; /* make sure to splice
|
|
list together */
|
|
if (!agline) break; /* last gline; break out of loop */
|
|
continue; /* continue! */
|
|
}
|
|
sendto_one(sptr, rpl_str(RPL_STATSGLINE), me.name,
|
|
sptr->name, 'G', agline->name, agline->host,
|
|
agline->expire, agline->reason);
|
|
a2gline = agline;
|
|
}
|
|
break;
|
|
case 'H' : case 'h' :
|
|
report_configured_links(sptr, CONF_HUB|CONF_LEAF);
|
|
break;
|
|
case 'I' : case 'i' :
|
|
report_configured_links(sptr, CONF_CLIENT);
|
|
break;
|
|
case 'K' : case 'k' :
|
|
report_configured_links(sptr, CONF_KILL);
|
|
break;
|
|
case 'M' : case 'm' :
|
|
for (mptr = msgtab; mptr->cmd; mptr++)
|
|
if (mptr->count)
|
|
sendto_one(sptr, rpl_str(RPL_STATSCOMMANDS),
|
|
me.name, parv[0], mptr->cmd,
|
|
mptr->count, mptr->bytes);
|
|
break;
|
|
case 'o' : case 'O' :
|
|
report_configured_links(sptr, CONF_OPS);
|
|
break;
|
|
case 'Q' : case 'q' :
|
|
report_configured_links(sptr, CONF_QUARANTINED_SERVER);
|
|
break;
|
|
case 'R' : case 'r' :
|
|
#ifdef DEBUGMODE
|
|
send_usage(sptr,parv[0]);
|
|
#endif
|
|
break;
|
|
case 'D' :
|
|
report_configured_links(sptr, CONF_CRULEALL);
|
|
break;
|
|
case 'd' :
|
|
report_configured_links(sptr, CONF_CRULE);
|
|
break;
|
|
case 'S' : case 's' :
|
|
report_configured_links(sptr, CONF_SERVICE);
|
|
break;
|
|
case 't' :
|
|
tstats(sptr, parv[0]);
|
|
break;
|
|
case 'T' :
|
|
report_configured_links(sptr, CONF_TLINES);
|
|
break;
|
|
case 'U' :
|
|
report_configured_links(sptr, CONF_UWORLD);
|
|
break;
|
|
case 'u' :
|
|
{
|
|
register time_t nowr;
|
|
|
|
nowr = now - me.since;
|
|
sendto_one(sptr, rpl_str(RPL_STATSUPTIME), me.name, parv[0],
|
|
nowr/86400, (nowr/3600)%24, (nowr/60)%60, nowr%60);
|
|
sendto_one(sptr, rpl_str(RPL_STATSCONN), me.name, parv[0],
|
|
max_connection_count, max_client_count);
|
|
break;
|
|
}
|
|
case 'W' : case 'w' :
|
|
calc_load(sptr, parv[0]);
|
|
break;
|
|
case 'X' : case 'x' :
|
|
#ifdef DEBUGMODE
|
|
send_listinfo(sptr, parv[0]);
|
|
#endif
|
|
break;
|
|
case 'Y' : case 'y' :
|
|
report_classes(sptr);
|
|
break;
|
|
case 'Z' : case 'z' :
|
|
count_memory(sptr, parv[0]);
|
|
break;
|
|
default :
|
|
stat = '*';
|
|
break;
|
|
}
|
|
sendto_one(sptr, rpl_str(RPL_ENDOFSTATS), me.name, parv[0], stat);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
** m_users
|
|
** parv[0] = sender prefix
|
|
** parv[1] = servername
|
|
*/
|
|
int m_users(cptr, sptr, parc, parv)
|
|
aClient *cptr, *sptr;
|
|
int parc;
|
|
char *parv[];
|
|
{
|
|
#ifdef ENABLE_USERS
|
|
char namebuf[10],linebuf[10],hostbuf[17];
|
|
int fd, flag = 0;
|
|
#endif
|
|
|
|
if (check_registered_user(sptr))
|
|
return 0;
|
|
|
|
if (hunt_server(cptr,sptr,":%s USERS :%s",1,parc,parv) == HUNTED_ISME)
|
|
{
|
|
#ifdef ENABLE_USERS
|
|
if ((fd = utmp_open()) == -1)
|
|
{
|
|
sendto_one(sptr, err_str(ERR_FILEERROR),
|
|
me.name, parv[0], "open", UTMP);
|
|
return 0;
|
|
}
|
|
|
|
sendto_one(sptr, rpl_str(RPL_USERSSTART), me.name, parv[0]);
|
|
while (utmp_read(fd, namebuf, linebuf,
|
|
hostbuf, sizeof(hostbuf)) == 0)
|
|
{
|
|
flag = 1;
|
|
sendto_one(sptr, rpl_str(RPL_USERS), me.name, parv[0],
|
|
namebuf, linebuf, hostbuf);
|
|
}
|
|
if (flag == 0)
|
|
sendto_one(sptr, rpl_str(RPL_NOUSERS),
|
|
me.name, parv[0]);
|
|
|
|
sendto_one(sptr, rpl_str(RPL_ENDOFUSERS), me.name, parv[0]);
|
|
(void)utmp_close(fd);
|
|
#else
|
|
sendto_one(sptr, err_str(ERR_USERSDISABLED), me.name, parv[0]);
|
|
#endif
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
** Note: At least at protocol level ERROR has only one parameter,
|
|
** although this is called internally from other functions
|
|
** --msa
|
|
**
|
|
** parv[0] = sender prefix
|
|
** parv[*] = parameters
|
|
*/
|
|
int m_error(cptr, sptr, parc, parv)
|
|
aClient *cptr, *sptr;
|
|
int parc;
|
|
char *parv[];
|
|
{
|
|
Reg1 char *para;
|
|
|
|
para = (parc > 1 && *parv[1] != '\0') ? parv[1] : "<>";
|
|
|
|
Debug((DEBUG_ERROR,"Received ERROR message from %s: %s",
|
|
sptr->name, para));
|
|
/*
|
|
** Ignore error messages generated by normal user clients
|
|
** (because ill-behaving user clients would flood opers
|
|
** screen otherwise). Pass ERROR's from other sources to
|
|
** the local operator...
|
|
*/
|
|
if (IsPerson(cptr) || IsService(cptr))
|
|
return 0;
|
|
if (cptr == sptr)
|
|
sendto_ops("ERROR :from %s -- %s",
|
|
get_client_name(cptr, FALSE), para);
|
|
else
|
|
sendto_ops("ERROR :from %s via %s -- %s", sptr->name,
|
|
get_client_name(cptr,FALSE), para);
|
|
if (IsUnknown(cptr))
|
|
exit_client_msg(cptr, cptr, &me,
|
|
":%s %d %s ERROR :Register first.", me.name,
|
|
ERR_NOTREGISTERED, sptr->name);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
** m_help
|
|
** parv[0] = sender prefix
|
|
*/
|
|
int m_help(cptr, sptr, parc, parv)
|
|
aClient *cptr, *sptr;
|
|
int parc;
|
|
char *parv[];
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; msgtab[i].cmd; i++)
|
|
sendto_one(sptr,":%s NOTICE %s :%s",
|
|
me.name, parv[0], msgtab[i].cmd);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* parv[0] = sender
|
|
* parv[1] = host/server mask.
|
|
* parv[2] = server to query
|
|
*/
|
|
int m_lusers(cptr, sptr, parc, parv)
|
|
aClient *cptr, *sptr;
|
|
int parc;
|
|
char *parv[];
|
|
{
|
|
int s_count = 0, c_count = 0, u_count = 0, i_count = 0;
|
|
int o_count = 0, m_client = 0, m_client_local = 0, m_server = 0;
|
|
char mydom_mask[HOSTLEN + 1];
|
|
aClient *acptr;
|
|
|
|
if (check_registered_user(sptr))
|
|
return 0;
|
|
|
|
if (parc > 2)
|
|
if(hunt_server(cptr, sptr, ":%s LUSERS %s :%s", 2, parc, parv)
|
|
!= HUNTED_ISME)
|
|
return 0;
|
|
|
|
mydom_mask[0] = '*';
|
|
strncpy(&mydom_mask[1], DOMAINNAME, HOSTLEN - 1);
|
|
|
|
(void)collapse(parv[1]);
|
|
for (acptr = client; acptr; acptr = acptr->next)
|
|
{
|
|
if (parc>1) {
|
|
if (!IsServer(acptr) && acptr->user)
|
|
{
|
|
if (match(parv[1], acptr->user->server->name))
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
if (match(parv[1], acptr->name))
|
|
continue;
|
|
}
|
|
}
|
|
|
|
switch (acptr->status)
|
|
{
|
|
case STAT_SERVER:
|
|
if (MyConnect(acptr))
|
|
m_server++;
|
|
case STAT_ME:
|
|
s_count++;
|
|
break;
|
|
case STAT_CLIENT:
|
|
if (IsOper(acptr))
|
|
o_count++;
|
|
#ifdef SHOW_INVISIBLE_LUSERS
|
|
if (MyConnect(acptr)) {
|
|
m_client++;
|
|
if (matches(mydom_mask, acptr->sockhost) == 0)
|
|
m_client_local++;
|
|
}
|
|
if (!IsInvisible(acptr))
|
|
c_count++;
|
|
else
|
|
i_count++;
|
|
#else
|
|
if (MyConnect(acptr))
|
|
{
|
|
if (IsInvisible(acptr))
|
|
{
|
|
if (IsAnOper(sptr))
|
|
m_client++;
|
|
}
|
|
else
|
|
m_client++;
|
|
}
|
|
if (!IsInvisible(acptr))
|
|
c_count++;
|
|
else
|
|
i_count++;
|
|
#endif
|
|
break;
|
|
case STAT_PING:
|
|
break;
|
|
default:
|
|
u_count++;
|
|
break;
|
|
}
|
|
}
|
|
#ifndef SHOW_INVISIBLE_LUSERS
|
|
if (IsAnOper(sptr) && i_count)
|
|
#endif
|
|
sendto_one(sptr, rpl_str(RPL_LUSERCLIENT), me.name, parv[0],
|
|
c_count, i_count, s_count);
|
|
#ifndef SHOW_INVISIBLE_LUSERS
|
|
else
|
|
sendto_one(sptr,
|
|
":%s %d %s :There are %d users on %d servers", me.name,
|
|
RPL_LUSERCLIENT, parv[0], c_count, s_count);
|
|
#endif
|
|
if (o_count)
|
|
sendto_one(sptr, rpl_str(RPL_LUSEROP),
|
|
me.name, parv[0], o_count);
|
|
if (u_count > 0)
|
|
sendto_one(sptr, rpl_str(RPL_LUSERUNKNOWN),
|
|
me.name, parv[0], u_count);
|
|
if ((c_count = count_channels(sptr))>0)
|
|
sendto_one(sptr, rpl_str(RPL_LUSERCHANNELS),
|
|
me.name, parv[0], count_channels(sptr));
|
|
sendto_one(sptr, rpl_str(RPL_LUSERME),
|
|
me.name, parv[0], m_client, m_server);
|
|
sendto_one(sptr,
|
|
":%s NOTICE %s :Highest connection count: %d (%d clients)",
|
|
me.name, parv[0], max_connection_count, max_client_count);
|
|
if (m_client > max_client_count)
|
|
max_client_count = m_client;
|
|
if ((m_client + m_server) > max_connection_count) {
|
|
max_connection_count = m_client + m_server;
|
|
if (max_connection_count % 10 == 0) /* only send on even tens */
|
|
sendto_ops("Maximum connections: %d (%d clients)",
|
|
max_connection_count, max_client_count);
|
|
}
|
|
current_load_data.local_count = m_client_local;
|
|
current_load_data.client_count = m_client;
|
|
current_load_data.conn_count = m_client + m_server;
|
|
return 0;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
* m_connect() - Added by Jto 11 Feb 1989
|
|
***********************************************************************/
|
|
|
|
/*
|
|
** m_connect
|
|
** parv[0] = sender prefix
|
|
** parv[1] = servername
|
|
** parv[2] = port number
|
|
** parv[3] = remote server
|
|
*/
|
|
int m_connect(cptr, sptr, parc, parv)
|
|
aClient *cptr, *sptr;
|
|
int parc;
|
|
char *parv[];
|
|
{
|
|
int port, tmpport, retval;
|
|
aConfItem *aconf, *cconf;
|
|
aClient *acptr;
|
|
|
|
if (!IsPrivileged(sptr))
|
|
{
|
|
sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
|
|
return -1;
|
|
}
|
|
|
|
if (IsLocOp(sptr) && parc > 3) /* Only allow LocOps to make */
|
|
return 0; /* local CONNECTS --SRB */
|
|
|
|
if (parc > 3 && MyClient(sptr))
|
|
{
|
|
aClient *acptr2, *acptr3;
|
|
if (!(acptr3 = find_match_server(parv[3])))
|
|
{
|
|
sendto_one(sptr, err_str(ERR_NOSUCHSERVER),
|
|
me.name, parv[0], parv[3]);
|
|
return 0;
|
|
}
|
|
|
|
/* Look for closest matching server */
|
|
for (acptr2 = acptr3; acptr2 != &me; acptr2 = acptr2->serv->up)
|
|
if (!match(parv[3], acptr2->name))
|
|
acptr3 = acptr2;
|
|
|
|
parv[3] = acptr3->name;
|
|
}
|
|
|
|
if (hunt_server(cptr,sptr,":%s CONNECT %s %s :%s",
|
|
3,parc,parv) != HUNTED_ISME)
|
|
return 0;
|
|
|
|
if (parc < 2 || *parv[1] == '\0')
|
|
{
|
|
sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS),
|
|
me.name, parv[0], "CONNECT");
|
|
return -1;
|
|
}
|
|
|
|
if ((acptr = find_server(parv[1], NULL)))
|
|
{
|
|
sendto_one(sptr, ":%s NOTICE %s :Connect: Server %s %s %s.",
|
|
me.name, parv[0], parv[1], "already exists from",
|
|
acptr->from->name);
|
|
return 0;
|
|
}
|
|
|
|
for (aconf = conf; aconf; aconf = aconf->next)
|
|
if (aconf->status == CONF_CONNECT_SERVER &&
|
|
matches(parv[1], aconf->name) == 0)
|
|
break;
|
|
/* Checked first servernames, then try hostnames. */
|
|
if (!aconf)
|
|
for (aconf = conf; aconf; aconf = aconf->next)
|
|
if (aconf->status == CONF_CONNECT_SERVER &&
|
|
(matches(parv[1], aconf->host) == 0 ||
|
|
matches(parv[1], index(aconf->host, '@')+1) == 0))
|
|
break;
|
|
|
|
if (!aconf)
|
|
{
|
|
sendto_one(sptr,
|
|
"NOTICE %s :Connect: Host %s not listed in ircd.conf",
|
|
parv[0], parv[1]);
|
|
return 0;
|
|
}
|
|
/*
|
|
** Get port number from user, if given. If not specified,
|
|
** use the default form configuration structure. If missing
|
|
** from there, then use the precompiled default.
|
|
*/
|
|
tmpport = port = aconf->port;
|
|
if (parc > 2 && !BadPtr(parv[2]))
|
|
{
|
|
if ((port = atoi(parv[2])) <= 0)
|
|
{
|
|
sendto_one(sptr,
|
|
"NOTICE %s :Connect: Illegal port number",
|
|
parv[0]);
|
|
return 0;
|
|
}
|
|
}
|
|
else if (port <= 0 && (port = PORTNUM) <= 0)
|
|
{
|
|
sendto_one(sptr, ":%s NOTICE %s :Connect: missing port number",
|
|
me.name, parv[0]);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
** Evaluate connection rules... If no rules found, allow the
|
|
** connect. Otherwise stop with the first true rule (ie: rules
|
|
** are ored together. Oper connects are effected only by D
|
|
** lines (CRULEALL) not d lines (CRULEAUTO).
|
|
*/
|
|
for (cconf = conf; cconf; cconf = cconf->next)
|
|
if ((cconf->status == CONF_CRULEALL) &&
|
|
(matches(cconf->host, aconf->name) == 0))
|
|
if (crule_eval (cconf->passwd))
|
|
{
|
|
sendto_one(sptr,
|
|
"NOTICE %s :Connect: Disallowed by rule: %s",
|
|
parv[0], cconf->name);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
** Notify all operators about remote connect requests
|
|
*/
|
|
if (!IsAnOper(cptr))
|
|
{
|
|
sendto_ops_butone(NULL, &me,
|
|
":%s WALLOPS :Remote CONNECT %s %s from %s",
|
|
me.name, parv[1], parv[2] ? parv[2] : "",
|
|
get_client_name(sptr,FALSE));
|
|
#if defined(USE_SYSLOG) && defined(SYSLOG_CONNECT)
|
|
syslog(LOG_DEBUG, "CONNECT From %s : %s %d", parv[0], parv[1], parv[2] ? parv[2] : "");
|
|
#endif
|
|
}
|
|
aconf->port = port;
|
|
switch (retval = connect_server(aconf, sptr, NULL))
|
|
{
|
|
case 0:
|
|
sendto_one(sptr,
|
|
":%s NOTICE %s :*** Connecting to %s[%s].",
|
|
me.name, parv[0], aconf->host, aconf->name);
|
|
break;
|
|
case -1:
|
|
/* Comments already sent */
|
|
break;
|
|
case -2:
|
|
sendto_one(sptr, ":%s NOTICE %s :*** Host %s is unknown.",
|
|
me.name, parv[0], aconf->host);
|
|
break;
|
|
default:
|
|
sendto_one(sptr,
|
|
":%s NOTICE %s :*** Connection to %s failed: %s",
|
|
me.name, parv[0], aconf->host, strerror(retval));
|
|
}
|
|
aconf->port = tmpport;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
** m_wallops (write to *all* opers currently online)
|
|
** parv[0] = sender prefix
|
|
** parv[1] = message text
|
|
*/
|
|
int m_wallops(cptr, sptr, parc, parv)
|
|
aClient *cptr, *sptr;
|
|
int parc;
|
|
char *parv[];
|
|
{
|
|
char *message;
|
|
|
|
message = parc > 1 ? parv[1] : NULL;
|
|
|
|
if (BadPtr(message))
|
|
{
|
|
sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS),
|
|
me.name, parv[0], "WALLOPS");
|
|
return 0;
|
|
}
|
|
|
|
if (!IsServer(sptr) && MyConnect(sptr) && !IsAnOper(sptr))
|
|
{
|
|
sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
|
|
return 0;
|
|
}
|
|
sendto_ops_butone(IsServer(cptr) ? cptr : NULL, sptr,
|
|
":%s WALLOPS :%s", parv[0], message);
|
|
#ifdef USE_SERVICES
|
|
check_services_butone(SERVICE_WANT_WALLOP, sptr, ":%s WALLOP :%s",
|
|
parv[0], message);
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
time_t TSoffset=0; /* Global variable; Offset of timestamps to system clock */
|
|
|
|
/*
|
|
** m_time
|
|
** parv[0] = sender prefix
|
|
** parv[1] = servername
|
|
*/
|
|
|
|
int m_time(cptr, sptr, parc, parv)
|
|
aClient *cptr, *sptr;
|
|
int parc;
|
|
char *parv[];
|
|
{
|
|
if (check_registered_user(sptr))
|
|
return 0;
|
|
if (hunt_server(cptr,sptr,":%s TIME :%s",1,parc,parv) == HUNTED_ISME)
|
|
sendto_one(sptr, rpl_str(RPL_TIME), me.name,
|
|
parv[0], me.name, TStime(), TSoffset, date((long)0));
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
** m_settime
|
|
** parv[0] = sender prefix
|
|
** parv[1] = new time
|
|
** parv[2] = servername
|
|
*/
|
|
int m_settime(cptr, sptr, parc, parv)
|
|
aClient *cptr, *sptr;
|
|
int parc;
|
|
char *parv[];
|
|
{
|
|
time_t t;
|
|
long int dt;
|
|
static char tbuf[11];
|
|
|
|
if (!IsPrivileged(sptr)) return 0;
|
|
|
|
if (parc == 2 && MyClient(sptr)) parv[parc++]=me.name;
|
|
|
|
if (parc < 3)
|
|
{
|
|
sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS),
|
|
me.name, parv[0], "SETTIME");
|
|
return 0;
|
|
}
|
|
|
|
t=atoi(parv[1]);
|
|
dt=TStime()-t;
|
|
|
|
if (t < 779557906 || dt < -9000000)
|
|
{
|
|
sendto_one(sptr, ":%s NOTICE %s :SETTIME: Bad value", me.name, parv[0]);
|
|
return 0;
|
|
}
|
|
|
|
if (IsServer(sptr))
|
|
sendto_serv_butone(cptr, ":%s SETTIME %s :%s",parv[0],parv[1],parv[2]);
|
|
else
|
|
{
|
|
sprintf(tbuf,"%lu",TStime());
|
|
parv[1]=tbuf;
|
|
if (hunt_server(cptr,sptr,":%s SETTIME %s :%s",2,parc,parv) != HUNTED_ISME)
|
|
return 0;
|
|
}
|
|
|
|
sendto_ops("SETTIME from %s, clock is set %ld seconds %s",
|
|
get_client_name(sptr,FALSE), (dt < 0) ? -dt : dt,
|
|
(dt < 0) ? "forwards" : "backwards");
|
|
TSoffset-=dt;
|
|
if (IsPerson(sptr))
|
|
sendto_one(sptr, ":%s NOTICE %s :clock is set %ld seconds %s", me.name,
|
|
parv[0], (dt < 0) ? -dt : dt, (dt < 0) ? "forwards" : "backwards");
|
|
return 0;
|
|
}
|
|
|
|
char *militime(sec, usec)
|
|
char *sec, *usec;
|
|
{
|
|
struct timeval tv;
|
|
static char timebuf[18];
|
|
|
|
(void)gettimeofday(&tv,NULL);
|
|
if (sec && usec)
|
|
sprintf(timebuf,"%ld",
|
|
(tv.tv_sec-atoi(sec))*1000+(tv.tv_usec-atoi(usec))/1000);
|
|
else
|
|
sprintf(timebuf,"%ld %ld",tv.tv_sec,tv.tv_usec);
|
|
return timebuf;
|
|
}
|
|
|
|
/*
|
|
** m_rping -- by Run
|
|
**
|
|
** parv[0] = sender prefix
|
|
** parv[1] = pinged server
|
|
** parv[2] = from person: start server ; from server: sender
|
|
** parv[3] = start time in s ;from person: Optional remark
|
|
** parv[4] = start time in us
|
|
*/
|
|
int m_rping(cptr, sptr, parc, parv)
|
|
aClient *cptr, *sptr;
|
|
int parc;
|
|
char *parv[];
|
|
{
|
|
aClient *acptr;
|
|
|
|
if (!IsPrivileged(sptr)) return 0;
|
|
|
|
if (parc < (IsAnOper(sptr) ? (MyConnect(sptr) ? 2 : 3) : 6))
|
|
{
|
|
sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS),
|
|
me.name, parv[0], "RPING");
|
|
return 0;
|
|
}
|
|
if (MyClient(sptr))
|
|
{
|
|
if (parc == 2)
|
|
parv[parc++]=me.name;
|
|
else if (!(acptr=find_match_server(parv[2])))
|
|
{
|
|
parv[3]=parv[2];
|
|
parv[2]=me.name;
|
|
parc++;
|
|
}
|
|
else
|
|
parv[2]=acptr->name;
|
|
if (parc == 3) parv[parc++]="<No client start time>";
|
|
}
|
|
|
|
if (IsAnOper(sptr))
|
|
{
|
|
if (hunt_server(cptr,sptr,":%s RPING %s %s :%s",2,parc,parv) != HUNTED_ISME)
|
|
return 0;
|
|
if (!(acptr=find_match_server(parv[1])) || !IsServer(acptr))
|
|
{
|
|
sendto_one(sptr, err_str(ERR_NOSUCHSERVER), me.name, parv[0], parv[1]);
|
|
return 0;
|
|
}
|
|
sendto_one(acptr,":%s RPING %s %s %s :%s",me.name,acptr->name,sptr->name,
|
|
militime(NULL, NULL), parv[3]);
|
|
}
|
|
else
|
|
{
|
|
if (hunt_server(cptr,sptr,":%s RPING %s %s %s %s :%s",1,parc,parv)
|
|
!= HUNTED_ISME)
|
|
return 0;
|
|
sendto_one(cptr,":%s RPONG %s %s %s %s :%s",me.name,parv[0],
|
|
parv[2],parv[3],parv[4],parv[5]);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
** m_rpong -- by Run too :)
|
|
**
|
|
** parv[0] = sender prefix
|
|
** parv[1] = from pinged server: start server; from start server: sender
|
|
** parv[2] = from pinged server: sender; from start server: pinged server
|
|
** parv[3] = pingtime in ms
|
|
** parv[4] = client info (for instance start time)
|
|
*/
|
|
int m_rpong(cptr, sptr, parc, parv)
|
|
aClient *cptr, *sptr;
|
|
int parc;
|
|
char *parv[];
|
|
{
|
|
aClient *acptr;
|
|
|
|
if (!IsServer(sptr)) return 0;
|
|
|
|
if (parc < 5)
|
|
{
|
|
sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS),
|
|
me.name, parv[0], "RPING");
|
|
return 0;
|
|
}
|
|
|
|
if (!(acptr=find_client(parv[1],(aClient *)NULL)))
|
|
return 0;
|
|
|
|
if (!IsMe(acptr))
|
|
{
|
|
if (IsServer(acptr) && parc > 5)
|
|
{
|
|
sendto_one(acptr,":%s RPONG %s %s %s %s :%s",
|
|
parv[0],parv[1],parv[2],parv[3],parv[4],parv[5]);
|
|
return 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
parv[1]=parv[2];
|
|
parv[2]=sptr->name;
|
|
parv[0]=me.name;
|
|
parv[3]=militime(parv[3],parv[4]);
|
|
parv[4]=parv[5];
|
|
if (!(acptr=find_person(parv[1],(aClient *)NULL)))
|
|
return 0; /* No bouncing between servers ! */
|
|
}
|
|
|
|
sendto_one(acptr,":%s RPONG %s %s %s :%s",
|
|
parv[0],parv[1],parv[2],parv[3],parv[4]);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
** m_admin
|
|
** parv[0] = sender prefix
|
|
** parv[1] = servername
|
|
*/
|
|
int m_admin(cptr, sptr, parc, parv)
|
|
aClient *cptr, *sptr;
|
|
int parc;
|
|
char *parv[];
|
|
{
|
|
aConfItem *aconf;
|
|
|
|
/* admin should be available to all (to get contact point for I lines,...) --dl
|
|
if (check_registered(sptr))
|
|
return 0;
|
|
*/
|
|
if (MyConnect(sptr) && parc > 1)
|
|
{
|
|
aClient *acptr;
|
|
if (!(acptr = find_match_server(parv[1])))
|
|
{
|
|
sendto_one(sptr, err_str(ERR_NOSUCHSERVER),
|
|
me.name, parv[0], parv[1]);
|
|
return 0;
|
|
}
|
|
parv[1] = acptr->name;
|
|
}
|
|
if (hunt_server(cptr,sptr,":%s ADMIN :%s",1,parc,parv) != HUNTED_ISME)
|
|
return 0;
|
|
if ((aconf = find_admin()))
|
|
{
|
|
sendto_one(sptr, rpl_str(RPL_ADMINME),
|
|
me.name, parv[0], me.name);
|
|
sendto_one(sptr, rpl_str(RPL_ADMINLOC1),
|
|
me.name, parv[0], aconf->host);
|
|
sendto_one(sptr, rpl_str(RPL_ADMINLOC2),
|
|
me.name, parv[0], aconf->passwd);
|
|
sendto_one(sptr, rpl_str(RPL_ADMINEMAIL),
|
|
me.name, parv[0], aconf->name);
|
|
}
|
|
else
|
|
sendto_one(sptr, err_str(ERR_NOADMININFO),
|
|
me.name, parv[0], me.name);
|
|
return 0;
|
|
}
|
|
|
|
#if defined(OPER_REHASH) || defined(LOCOP_REHASH)
|
|
/*
|
|
** m_rehash
|
|
**
|
|
*/
|
|
int m_rehash(cptr, sptr, parc, parv)
|
|
aClient *cptr, *sptr;
|
|
int parc;
|
|
char *parv[];
|
|
{
|
|
#ifndef LOCOP_REHASH
|
|
if (!MyClient(sptr) || !IsOper(sptr))
|
|
#else
|
|
# ifdef OPER_REHASH
|
|
if (!MyClient(sptr) || !IsAnOper(sptr))
|
|
# else
|
|
if (!MyClient(sptr) || !IsLocOp(sptr))
|
|
# endif
|
|
#endif
|
|
{
|
|
sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
|
|
return 0;
|
|
}
|
|
sendto_one(sptr, rpl_str(RPL_REHASHING), me.name, parv[0], configfile);
|
|
sendto_ops("%s is rehashing Server config file", parv[0]);
|
|
#ifdef USE_SYSLOG
|
|
syslog(LOG_INFO, "REHASH From %s\n", get_client_name(sptr, FALSE));
|
|
#endif
|
|
return rehash(cptr, sptr, (parc > 1) ? ((*parv[1] == 'q')?2:0) : 0);
|
|
}
|
|
#endif
|
|
|
|
#if defined(OPER_RESTART) || defined(LOCOP_RESTART)
|
|
/*
|
|
** m_restart
|
|
**
|
|
*/
|
|
int m_restart(cptr, sptr, parc, parv)
|
|
aClient *cptr, *sptr;
|
|
int parc;
|
|
char *parv[];
|
|
{
|
|
#ifndef LOCOP_RESTART
|
|
if (!MyClient(sptr) || !IsOper(sptr))
|
|
#else
|
|
# ifdef OPER_RESTART
|
|
if (!MyClient(sptr) || !IsAnOper(sptr))
|
|
# else
|
|
if (!MyClient(sptr) || !IsLocOp(sptr))
|
|
# endif
|
|
#endif
|
|
{
|
|
sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
|
|
return 0;
|
|
}
|
|
#ifdef USE_SYSLOG
|
|
syslog(LOG_WARNING, "Server RESTART by %s\n",
|
|
get_client_name(sptr,FALSE));
|
|
#endif
|
|
server_reboot();
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
** m_trace
|
|
** parv[0] = sender prefix
|
|
** parv[1] = servername
|
|
*/
|
|
int m_trace(cptr, sptr, parc, parv)
|
|
aClient *cptr, *sptr;
|
|
int parc;
|
|
char *parv[];
|
|
{
|
|
Reg1 int i;
|
|
Reg2 aClient *acptr;
|
|
aClass *cltmp;
|
|
char *tname;
|
|
int doall, link_s[MAXCONNECTIONS], link_u[MAXCONNECTIONS];
|
|
int cnt = 0, wilds, dow;
|
|
|
|
if (check_registered(sptr))
|
|
return 0;
|
|
|
|
if (parc > 2)
|
|
if (hunt_server(cptr, sptr, ":%s TRACE %s :%s",
|
|
2, parc, parv))
|
|
return 0;
|
|
|
|
if (parc > 1)
|
|
tname = parv[1];
|
|
else
|
|
tname = me.name;
|
|
|
|
switch (hunt_server(cptr, sptr, ":%s TRACE :%s", 1, parc, parv))
|
|
{
|
|
case HUNTED_PASS: /* note: gets here only if parv[1] exists */
|
|
{
|
|
aClient *ac2ptr;
|
|
|
|
ac2ptr = next_client(client, tname);
|
|
sendto_one(sptr, rpl_str(RPL_TRACELINK), me.name, parv[0],
|
|
version, debugmode, tname, ac2ptr->from->name);
|
|
return 0;
|
|
}
|
|
case HUNTED_ISME:
|
|
break;
|
|
default:
|
|
return 0;
|
|
}
|
|
|
|
doall = (parv[1] && (parc > 1)) ? !matches(tname, me.name): TRUE;
|
|
wilds = !parv[1] || index(tname, '*') || index(tname, '?');
|
|
dow = wilds || doall;
|
|
|
|
for (i = 0; i < MAXCONNECTIONS; i++)
|
|
link_s[i] = 0, link_u[i] = 0;
|
|
|
|
if (doall) {
|
|
for (acptr = client; acptr; acptr = acptr->next) {
|
|
#ifdef SHOW_INVISIBLE_LUSERS
|
|
if (IsPerson(acptr))
|
|
link_u[acptr->from->fd]++;
|
|
#else
|
|
if (IsPerson(acptr) &&
|
|
(!IsInvisible(acptr) || IsOper(sptr)))
|
|
link_u[acptr->from->fd]++;
|
|
#endif
|
|
else if (IsServer(acptr))
|
|
link_s[acptr->from->fd]++;
|
|
}
|
|
}
|
|
|
|
/* report all direct connections */
|
|
|
|
for (i = 0; i <= highest_fd; i++)
|
|
{
|
|
char *name;
|
|
int class;
|
|
|
|
if (!(acptr = local[i])) /* Local Connection? */
|
|
continue;
|
|
if (IsInvisible(acptr) && dow &&
|
|
!(MyConnect(sptr) && IsOper(sptr)) &&
|
|
!IsAnOper(acptr) && (acptr != sptr))
|
|
continue;
|
|
if (!doall && wilds && matches(tname, acptr->name))
|
|
continue;
|
|
if (!dow && strcasecmp(tname, acptr->name))
|
|
continue;
|
|
name = get_client_name(acptr,FALSE);
|
|
class = get_client_class(acptr);
|
|
|
|
switch(acptr->status)
|
|
{
|
|
case STAT_CONNECTING:
|
|
sendto_one(sptr, rpl_str(RPL_TRACECONNECTING), me.name,
|
|
parv[0], class, name);
|
|
cnt++;
|
|
break;
|
|
case STAT_HANDSHAKE:
|
|
sendto_one(sptr, rpl_str(RPL_TRACEHANDSHAKE), me.name,
|
|
parv[0], class, name);
|
|
cnt++;
|
|
break;
|
|
case STAT_ME:
|
|
break;
|
|
case STAT_UNKNOWN:
|
|
sendto_one(sptr, rpl_str(RPL_TRACEUNKNOWN),
|
|
me.name, parv[0], class, name);
|
|
cnt++;
|
|
break;
|
|
case STAT_CLIENT:
|
|
/* Only opers see users if there is a wildcard
|
|
* but anyone can see all the opers.
|
|
*/
|
|
if ((IsOper(sptr) &&
|
|
(MyClient(sptr) || !(dow && IsInvisible(acptr))))
|
|
|| !dow || IsAnOper(acptr))
|
|
{
|
|
if (IsAnOper(acptr))
|
|
sendto_one(sptr,
|
|
rpl_str(RPL_TRACEOPERATOR),
|
|
me.name,
|
|
parv[0], class, name,
|
|
now - acptr->lasttime);
|
|
else
|
|
sendto_one(sptr,rpl_str(RPL_TRACEUSER),
|
|
me.name, parv[0],
|
|
class, name,
|
|
now - acptr->lasttime);
|
|
cnt++;
|
|
}
|
|
break;
|
|
case STAT_SERVER:
|
|
if (acptr->serv->user)
|
|
sendto_one(sptr, rpl_str(RPL_TRACESERVER),
|
|
me.name, parv[0], class, link_s[i],
|
|
link_u[i], name, acptr->serv->by,
|
|
acptr->serv->user->username,
|
|
acptr->serv->user->host,
|
|
now - acptr->lasttime);
|
|
else
|
|
sendto_one(sptr, rpl_str(RPL_TRACESERVER),
|
|
me.name, parv[0], class, link_s[i],
|
|
link_u[i], name, *(acptr->serv->by) ?
|
|
acptr->serv->by : "*", "*", me.name,
|
|
now - acptr->lasttime);
|
|
cnt++;
|
|
break;
|
|
case STAT_SERVICE:
|
|
sendto_one(sptr, rpl_str(RPL_TRACESERVICE),
|
|
me.name, parv[0], class, name);
|
|
cnt++;
|
|
break;
|
|
case STAT_LOG:
|
|
sendto_one(sptr, rpl_str(RPL_TRACELOG), me.name,
|
|
parv[0], LOGFILE, acptr->port);
|
|
cnt++;
|
|
break;
|
|
case STAT_PING:
|
|
sendto_one(sptr, rpl_str(RPL_TRACEPING), me.name,
|
|
parv[0], name, (acptr->acpt) ?
|
|
acptr->acpt->name : "<null>");
|
|
break;
|
|
default: /* ...we actually shouldn't come here... --msa */
|
|
sendto_one(sptr, rpl_str(RPL_TRACENEWTYPE), me.name,
|
|
parv[0], name);
|
|
cnt++;
|
|
break;
|
|
}
|
|
}
|
|
/*
|
|
* Add these lines to summarize the above which can get rather long
|
|
* and messy when done remotely - Avalon
|
|
*/
|
|
if (!IsAnOper(sptr) || !cnt)
|
|
{
|
|
if (cnt)
|
|
return 0;
|
|
/* let the user have some idea that its at the end of the
|
|
* trace
|
|
*/
|
|
sendto_one(sptr, rpl_str(RPL_TRACESERVER),
|
|
me.name, parv[0], 0, link_s[me.fd],
|
|
link_u[me.fd], me.name, "*", "*", me.name);
|
|
return 0;
|
|
}
|
|
for (cltmp = FirstClass(); doall && cltmp; cltmp = NextClass(cltmp))
|
|
if (Links(cltmp) > 0)
|
|
sendto_one(sptr, rpl_str(RPL_TRACECLASS), me.name,
|
|
parv[0], Class(cltmp), Links(cltmp));
|
|
return 0;
|
|
}
|
|
|
|
extern aConfItem *find_tline PROTO((aClient *));
|
|
|
|
/*
|
|
* m_motd
|
|
* parv[0] - sender prefix
|
|
* parv[1] - servername
|
|
*
|
|
* modified 30 mar 1995 by flux (cmlambertus@ucdavis.edu)
|
|
* T line patch - display motd based on hostmask
|
|
*/
|
|
int m_motd(cptr, sptr, parc, parv)
|
|
aClient *cptr, *sptr;
|
|
int parc;
|
|
char *parv[];
|
|
{
|
|
int fd, nr, tflag;
|
|
char line[80];
|
|
Reg1 char *tmp;
|
|
struct stat sb;
|
|
struct tm *tm;
|
|
aConfItem *ftmp;
|
|
|
|
if (check_registered(sptr))
|
|
return 0;
|
|
|
|
if (hunt_server(cptr, sptr, ":%s MOTD :%s", 1,parc,parv)!=HUNTED_ISME)
|
|
return 0;
|
|
|
|
/*
|
|
* find out if we have a T line for our hostname
|
|
*/
|
|
ftmp = find_tline(cptr);
|
|
|
|
/*
|
|
* stop NFS hangs...most systems should be able to open a file in
|
|
* 3 seconds. -avalon (curtesy of wumpus)
|
|
*/
|
|
(void)alarm(3);
|
|
/* if we returned 0 in find_tline, there is no T line for this host
|
|
* so display the stock ircd.motd
|
|
*/
|
|
if (!ftmp || (fd = open(ftmp->passwd, O_RDONLY)) == -1)
|
|
fd = open(MOTD, O_RDONLY);
|
|
(void)alarm(0);
|
|
if (fd == -1)
|
|
{
|
|
sendto_one(sptr, err_str(ERR_NOMOTD), me.name, parv[0]);
|
|
return 0;
|
|
}
|
|
(void)fstat(fd, &sb);
|
|
sendto_one(sptr, rpl_str(RPL_MOTDSTART), me.name, parv[0], me.name);
|
|
tm = localtime((time_t *)&sb.st_mtime); /* NetBSD needs cast */
|
|
sendto_one(sptr, ":%s %d %s :- %d/%d/%d %d:%02d", me.name, RPL_MOTD,
|
|
parv[0], tm->tm_mday, tm->tm_mon + 1, 1900 + tm->tm_year,
|
|
tm->tm_hour, tm->tm_min);
|
|
(void)dgets(-1, NULL, 0); /* make sure buffer is at empty pos */
|
|
while ((nr=dgets(fd, line, sizeof(line)-1)) > 0)
|
|
{
|
|
line[nr]='\0';
|
|
if ((tmp = (char *)index(line,'\n')))
|
|
*tmp = '\0';
|
|
if ((tmp = (char *)index(line,'\r')))
|
|
*tmp = '\0';
|
|
sendto_one(sptr, rpl_str(RPL_MOTD), me.name, parv[0], line);
|
|
}
|
|
(void)dgets(-1, NULL, 0); /* make sure buffer is at empty pos */
|
|
sendto_one(sptr, rpl_str(RPL_ENDOFMOTD), me.name, parv[0]);
|
|
(void)close(fd);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
** m_close - added by Darren Reed Jul 13 1992.
|
|
*/
|
|
int m_close(cptr, sptr, parc, parv)
|
|
aClient *cptr, *sptr;
|
|
int parc;
|
|
char *parv[];
|
|
{
|
|
Reg1 aClient *acptr;
|
|
Reg2 int i;
|
|
int closed = 0;
|
|
|
|
if (!MyOper(sptr))
|
|
{
|
|
sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
|
|
return 0;
|
|
}
|
|
|
|
for (i = highest_fd; i; i--)
|
|
{
|
|
if (!(acptr = local[i]))
|
|
continue;
|
|
if (!IsUnknown(acptr) && !IsConnecting(acptr) &&
|
|
!IsHandshake(acptr))
|
|
continue;
|
|
sendto_one(sptr, rpl_str(RPL_CLOSING), me.name, parv[0],
|
|
get_client_name(acptr, TRUE), acptr->status);
|
|
(void)exit_client(cptr, acptr, &me, "Oper Closing");
|
|
closed++;
|
|
}
|
|
sendto_one(sptr, rpl_str(RPL_CLOSEEND), me.name, parv[0], closed);
|
|
return 0;
|
|
}
|
|
|
|
#if defined(OPER_DIE) || defined(LOCOP_DIE)
|
|
int m_die(cptr, sptr, parc, parv)
|
|
aClient *cptr, *sptr;
|
|
int parc;
|
|
char *parv[];
|
|
{
|
|
Reg1 aClient *acptr;
|
|
Reg2 int i;
|
|
|
|
#ifndef LOCOP_DIE
|
|
if (!MyClient(sptr) || !IsOper(sptr))
|
|
#else
|
|
# ifdef OPER_DIE
|
|
if (!MyClient(sptr) || !IsAnOper(sptr))
|
|
# else
|
|
if (!MyClient(sptr) || !IsLocOp(sptr))
|
|
# endif
|
|
#endif
|
|
{
|
|
sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
|
|
return 0;
|
|
}
|
|
|
|
for (i = 0; i <= highest_fd; i++)
|
|
{
|
|
if (!(acptr = local[i]))
|
|
continue;
|
|
if (IsClient(acptr))
|
|
sendto_one(acptr,
|
|
":%s NOTICE %s :Server Terminating. %s",
|
|
me.name, acptr->name,
|
|
get_client_name(sptr, TRUE));
|
|
else if (IsServer(acptr))
|
|
sendto_one(acptr, ":%s ERROR :Terminated by %s",
|
|
me.name, get_client_name(sptr, TRUE));
|
|
}
|
|
(void)s_die();
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
int m_gline(cptr, sptr, parc, parv)
|
|
aClient *cptr, *sptr;
|
|
int parc;
|
|
char *parv[];
|
|
{
|
|
aGline *agline, *a2gline = NULL;
|
|
aConfItem *tmp;
|
|
aClient *acptr;
|
|
char *user, *host;
|
|
int active = -1;
|
|
#ifdef GPATH
|
|
int logfile;
|
|
#endif /* GPATH */
|
|
time_t expire = 0;
|
|
|
|
if (IsServer(cptr)) {
|
|
if (find_conf_host(cptr->confs, sptr->name, CONF_UWORLD)) {
|
|
if (parc < 3 || (*parv[2] != '-' && (parc < 5 || *parv[4] == '\0'))) {
|
|
sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0],
|
|
"GLINE");
|
|
return 0;
|
|
}
|
|
|
|
if (*parv[2] == '-') /* add mode or delete mode? */
|
|
active = GLINE_INACTIVE;
|
|
else active = GLINE_ACTIVE;
|
|
|
|
if (*parv[2] == '+' || *parv[2] == '-')
|
|
parv[2]++; /* step past mode indicator */
|
|
|
|
/* forward the message appropriately */
|
|
if (!(acptr = find_person(parv[1], NULL)))
|
|
sendto_serv_butone(cptr, active == GLINE_INACTIVE ? ":%s GLINE %s -%s"
|
|
: ":%s GLINE %s +%s %s :%s", parv[0], parv[1],
|
|
parv[2], parv[3], parv[4]); /* global! */
|
|
else if (IsServer(acptr) || !MyConnect(acptr))
|
|
sendto_one(acptr, active == GLINE_INACTIVE ? ":%s GLINE %s -%s" :
|
|
":%s GLINE %s +%s %s :%s", parv[0], parv[1], parv[2],
|
|
parv[3], parv[4]); /* single destination */
|
|
|
|
if (!(host = strchr(parv[2], '@'))) { /* convert user@host */
|
|
user = "*"; /* no @'s; assume username is '*' */
|
|
host = parv[2];
|
|
} else {
|
|
user = parv[2];
|
|
*(host++) = '\0'; /* break up string at the '@' */
|
|
}
|
|
|
|
for (agline = gline, a2gline = NULL; agline; agline = agline->next) {
|
|
if (agline->expire <= TStime()) { /* handle expired glines */
|
|
free_gline(agline, a2gline);
|
|
agline = a2gline ? a2gline : gline;
|
|
if (!agline) break; /* end of list due to expire */
|
|
continue;
|
|
}
|
|
|
|
if (!strcasecmp(agline->name, user) && !strcasecmp(agline->host, host))
|
|
break;
|
|
|
|
a2gline = agline;
|
|
}
|
|
|
|
if (active == GLINE_INACTIVE && agline) { /* removing the gline */
|
|
sendto_ops("%s removing GLINE for %s@%s", parv[0], agline->name,
|
|
agline->host); /* notify opers */
|
|
|
|
#ifdef GPATH
|
|
(void)alarm(3); /* make a log entry */
|
|
logfile = open(GPATH, O_WRONLY|O_APPEND|O_CREAT, S_IRUSR|S_IWUSR);
|
|
(void)alarm(0);
|
|
|
|
(void)sprintf(buf, "# %lu %s removing GLINE for %s@%s\n", TStime(),
|
|
parv[0], agline->name, agline->host);
|
|
|
|
(void)alarm(3);
|
|
(void)write(logfile, buf, strlen(buf));
|
|
(void)alarm(0);
|
|
|
|
(void)close(logfile);
|
|
#endif /* GPATH */
|
|
|
|
free_gline(agline, a2gline); /* remove the gline */
|
|
|
|
} else if (active != GLINE_INACTIVE) { /* must be adding a gline */
|
|
expire = atoi(parv[3]) + TStime(); /* expire time? */
|
|
if (agline && agline->expire < expire) { /* new expire time? */
|
|
/* yes, notify the opers */
|
|
sendto_ops("%s resetting expiration time on GLINE for %s@%s to "
|
|
"%lu", parv[0], agline->name, agline->host, expire);
|
|
|
|
#ifdef GPATH
|
|
(void)alarm(3); /* make a log entry */
|
|
logfile = open(GPATH, O_WRONLY|O_APPEND|O_CREAT, S_IRUSR|S_IWUSR);
|
|
(void)alarm(0);
|
|
|
|
(void)sprintf(buf, "# %lu %s resetting expiration time on GLINE "
|
|
"for %s@%s to %lu\n", TStime(), parv[0], agline->name,
|
|
agline->host, expire);
|
|
|
|
(void)alarm(3);
|
|
(void)write(logfile, buf, strlen(buf));
|
|
(void)alarm(0);
|
|
|
|
(void)close(logfile);
|
|
#endif /* GPATH */
|
|
|
|
agline->expire = expire; /* reset the expire time */
|
|
} else if (!agline) { /* create gline */
|
|
/* find any pre-existing K-lines */
|
|
for (tmp = conf; tmp; tmp = tmp->next)
|
|
if ((tmp->status == CONF_KILL) && tmp->host && tmp->name) {
|
|
if ((!match(tmp->name,user)) && (!match(tmp->host,host)))
|
|
return 0; /* found an existing K-line that matches */
|
|
}
|
|
|
|
for (agline = gline, a2gline = NULL; agline; agline = agline->next) {
|
|
if (agline->expire <= TStime()) { /* handle expired glines */
|
|
free_gline(agline, a2gline);
|
|
agline = agline ? a2gline : gline;
|
|
if (!agline) break; /* end of list due to expire */
|
|
continue;
|
|
}
|
|
if ((!match(agline->name,user)) && (!match(agline->host,host)))
|
|
return 0; /* found an existing G-line that matches */
|
|
|
|
a2gline = agline; /* keep track of back pointer */
|
|
}
|
|
|
|
sendto_ops("%s adding GLINE for %s@%s, expiring at %lu: %s", parv[0],
|
|
user, host, expire, parv[4]); /* inform ops */
|
|
#ifdef GPATH
|
|
(void)alarm(3); /* make a log entry */
|
|
logfile = open(GPATH, O_WRONLY|O_APPEND|O_CREAT, S_IRUSR|S_IWUSR);
|
|
(void)alarm(0);
|
|
|
|
(void)sprintf(buf, "# %lu %s adding GLINE for %s@%s, expiring at "
|
|
"%lu: %s\n", TStime(), parv[0], user, host, expire,
|
|
parv[4]);
|
|
|
|
(void)alarm(3);
|
|
(void)write(logfile, buf, strlen(buf));
|
|
(void)alarm(0);
|
|
|
|
/* this can be inserted into the conf */
|
|
(void)sprintf(buf, "K:%s:%s:%s\n", host, parv[4], user);
|
|
|
|
(void)alarm(3);
|
|
(void)write(logfile, buf, strlen(buf));
|
|
(void)alarm(0);
|
|
|
|
(void)close(logfile);
|
|
#endif /* GPATH */
|
|
|
|
agline = make_gline(host, parv[4], user, expire); /* add the line */
|
|
|
|
for (active = 0; active <= highest_fd; active++) /* get the users! */
|
|
if ((acptr = local[active]) && !IsMe(acptr)) {
|
|
|
|
if (!acptr->user || strlen(acptr->sockhost) > (size_t) HOSTLEN ||
|
|
(acptr->user->username ? strlen(acptr->user->username) : 0) >
|
|
(size_t) HOSTLEN) continue; /* these tests right out of
|
|
find_kill for safety's sake */
|
|
|
|
if (match(agline->host, acptr->sockhost) == 0 &&
|
|
(!acptr->user->username ||
|
|
match(agline->name, acptr->user->username) == 0)) {
|
|
|
|
/* ok, he was the one that got G-lined */
|
|
sendto_one(acptr, ":%s %d %s :*** %s.", me.name,
|
|
ERR_YOUREBANNEDCREEP, acptr->name, agline->reason);
|
|
|
|
/* let the ops know about my first kill */
|
|
sendto_ops("G-line active for %s",
|
|
get_client_name(acptr, FALSE));
|
|
|
|
/* and get rid of him */
|
|
exit_client(cptr, acptr, &me, "G-lined");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else return 0; /* server not permitted to add a GLINE anyway */
|
|
}
|
|
else if (parc < 2) { /* not enough args and a user...list glines */
|
|
|
|
for (agline = gline, a2gline = NULL; agline; agline = agline->next) {
|
|
|
|
if (agline->expire <= TStime()) { /* handle expired glines */
|
|
free_gline(agline, a2gline);
|
|
agline = a2gline ? a2gline : gline;
|
|
if (!agline) break; /* agline->next is illegal if agline == NULL! */
|
|
continue; /* don't need to display this gline; next iteration will */
|
|
}
|
|
|
|
/* display the GLINE */
|
|
sendto_one(cptr, rpl_str(RPL_GLIST), me.name, parv[0],
|
|
agline->name, agline->host, agline->expire, agline->reason,
|
|
(agline->active == GLINE_INACTIVE) ? " (Inactive)" : "");
|
|
|
|
a2gline = agline; /* keep the back pointer */
|
|
}
|
|
sendto_one(cptr, rpl_str(RPL_ENDOFGLIST), me.name, parv[0]);
|
|
|
|
} else {
|
|
|
|
if (IsOper(cptr)) { /* non-oper not permitted to change things */
|
|
if (*parv[1] == '-') { /* oper wants to deactivate the gline */
|
|
active = GLINE_INACTIVE;
|
|
parv[1]++;
|
|
} else if (*parv[1] == '+') { /* oper wants to activate inactive gline */
|
|
active = GLINE_ACTIVE;
|
|
parv[1]++;
|
|
}
|
|
|
|
if (parc > 2)
|
|
expire = atoi(parv[2]) + TStime(); /* oper wants to reset expire TS */
|
|
}
|
|
|
|
if (!(host = strchr(parv[1], '@'))) {
|
|
user = "*"; /* no @'s; assume username is '*' */
|
|
host = parv[1];
|
|
} else {
|
|
user = parv[1];
|
|
*(host++) = '\0'; /* break up string at the '@' */
|
|
}
|
|
|
|
for (agline = gline, a2gline = NULL; agline; agline = agline->next) {
|
|
|
|
if (agline->expire <= TStime()) { /* handle expired glines */
|
|
free_gline(agline, a2gline);
|
|
agline = a2gline ? a2gline : gline;
|
|
if (!agline) break; /* break out of loop */
|
|
continue;
|
|
}
|
|
|
|
if ((!match(agline->name, user) || !strcasecmp(agline->name, user)) &&
|
|
(!match(agline->host, host) || !strcasecmp(agline->host, host)))
|
|
break;
|
|
|
|
a2gline = agline;
|
|
}
|
|
|
|
if (!agline) {
|
|
sendto_one(cptr, err_str(ERR_NOSUCHGLINE), me.name, parv[0], user, host);
|
|
return 0;
|
|
}
|
|
|
|
if (expire <= agline->expire)
|
|
expire = 0;
|
|
|
|
if ((active == -1 || active == agline->active) && expire == 0) {
|
|
/* oper wants a list of one gline only */
|
|
sendto_one(cptr, rpl_str(RPL_GLIST), me.name, parv[0], agline->name,
|
|
agline->host, agline->expire, agline->reason,
|
|
(agline->active == GLINE_INACTIVE) ? " (Inactive)" : "");
|
|
sendto_one(cptr, rpl_str(RPL_ENDOFGLIST), me.name, parv[0]);
|
|
return 0;
|
|
}
|
|
|
|
if (active != -1 && active != agline->active)
|
|
agline->active = active; /* reset activation on gline */
|
|
else
|
|
active = -1; /* for later sendto_ops and logging functions */
|
|
|
|
if (expire)
|
|
agline->expire = expire; /* reset expiration time */
|
|
|
|
/* inform the operators what's up */
|
|
if (active != -1) { /* changing the activation */
|
|
sendto_ops(!expire ? "%s %sactivating GLINE for %s@%s":"%s %sactivating "
|
|
"GLINE for %s@%s and resetting expiration time to %lu",
|
|
parv[0], active == GLINE_INACTIVE ? "de" : "re", agline->name,
|
|
agline->host, agline->expire);
|
|
#ifdef GPATH
|
|
(void)alarm(3); /* make a log entry */
|
|
logfile = open(GPATH, O_WRONLY|O_APPEND|O_CREAT, S_IRUSR|S_IWUSR);
|
|
(void)alarm(0);
|
|
|
|
(void)sprintf(buf, !expire ? "# %lu %s!%s@%s %sactivating GLINE for "
|
|
"%s@%s\n" : "# %lu %s!%s@%s %sactivating GLINE for %s@%s "
|
|
"and resetting expiration time to %lu\n", TStime(),
|
|
parv[0], cptr->user->username, cptr->user->host,
|
|
active == GLINE_INACTIVE ? "de" : "re", agline->name,
|
|
agline->host, agline->expire);
|
|
|
|
(void)alarm(3);
|
|
(void)write(logfile, buf, strlen(buf));
|
|
(void)alarm(0);
|
|
|
|
(void)close(logfile);
|
|
#endif /* GPATH */
|
|
|
|
} else { /* changing only the expiration */
|
|
sendto_ops("%s resetting expiration time on GLINE for %s@%s to %lu",
|
|
parv[0], agline->name, agline->host, agline->expire);
|
|
#ifdef GPATH
|
|
(void)alarm(3); /* make a log entry */
|
|
logfile = open(GPATH, O_WRONLY|O_APPEND|O_CREAT, S_IRUSR|S_IWUSR);
|
|
(void)alarm(0);
|
|
|
|
(void)sprintf(buf, "# %lu %s!%s@%s resetting expiration time on GLINE "
|
|
"for %s@%s to %lu\n", TStime(), parv[0],
|
|
cptr->user->username, cptr->user->host, agline->name,
|
|
agline->host, agline->expire);
|
|
|
|
(void)alarm(3);
|
|
(void)write(logfile, buf, strlen(buf));
|
|
(void)alarm(0);
|
|
|
|
(void)close(logfile);
|
|
#endif /* GPATH */
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|