/************************************************************************
* 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 rcsid[] = "@(#)$Id: s_serv.c,v 1.22 2004/03/31 23:46:13 skold Exp $";
#endif
#include "os.h"
#include "s_defines.h"
#define S_SERV_C
#include "s_externs.h"
#undef S_SERV_C
static char buf[BUFSIZE];
static int check_link __P((aClient *));
/*
** 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[];
{
if (hunt_server(cptr,sptr,":%s VERSION :%s",1,parc,parv)==HUNTED_ISME)
sendto_one(sptr, rpl_str(RPL_VERSION, parv[0]),
version, debugmode, ME, serveropts);
return 2;
}
/*
** m_squit
** parv[0] = sender prefix
** parv[1] = server name
** parv[2] = comment
*/
int m_squit(cptr, sptr, parc, parv)
aClient *cptr, *sptr;
int parc;
char *parv[];
{
Reg aConfItem *aconf;
char *server;
Reg aClient *acptr;
char *comment = (parc > 2 && parv[2]) ? parv[2] : cptr->name;
if (parc > 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.
*/
while ((*server == '*') && IsServer(cptr))
{
aconf = cptr->serv->nline;
if (!aconf)
break;
if (!strcasecmp(server,
my_name_for_link(ME, aconf->port)))
server = cptr->name;
break; /* WARNING is normal here */
}
/*
** 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 && IsMe(acptr))
{
acptr = cptr;
server = cptr->sockhost;
}
}
else
{
/*
** This is actually protocol error. But, well, closing
** the link is very proper answer to that...
*/
server = cptr->name;
acptr = cptr;
}
/*
** SQUIT semantics is tricky, be careful...
**
** The old (irc2.2PL1 and earlier) code just cleans away the
** server client from the links (because it is never true
** "cptr == acptr".
**
** This logic here works the same way until "SQUIT host" hits
** the server having the target "host" as local link. Then it
** will do a real cleanup spewing SQUIT's and QUIT's to all
** directions, also to the link from which the orinal SQUIT
** came, generating one unnecessary "SQUIT host" back to that
** link.
**
** One may think that this could be implemented like
** "hunt_server" (e.g. just pass on "SQUIT" without doing
** nothing until the server having the link as local is
** reached). Unfortunately this wouldn't work in the real life,
** because either target may be unreachable or may not comply
** with the request. In either case it would leave target in
** links--no command to clear it away. So, it's better just
** clean out while going forward, just to be sure.
**
** ...of course, even better cleanout would be to QUIT/SQUIT
** dependant users/servers already on the way out, but
** currently there is not enough information about remote
** clients to do this... --msa
*/
if (!acptr)
{
sendto_one(sptr, err_str(ERR_NOSUCHSERVER, parv[0]), server);
return 1;
}
if (MyConnect(sptr) && !MyConnect(acptr) && parc < 3)
{
sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS,parv[0]), "SQUIT");
return 0;
}
if (IsLocOp(sptr) && !MyConnect(acptr))
{
sendto_one(sptr, err_str(ERR_NOPRIVILEGES, parv[0]));
return 1;
}
if (!MyConnect(acptr) && (cptr != acptr->from))
{
/*
** The following is an awful kludge, but I don't see any other
** way to change the pre 2.10.3 behaviour. I'm probably going
** to regret it.. -kalt
*/
if ((acptr->from->serv->version & SV_OLDSQUIT) == 0)
{
/* better server: just propagate upstream */
sendto_one(acptr->from, ":%s SQUIT %s :%s", parv[0],
acptr->name, comment);
sendto_flag(SCH_SERVER,
"Forwarding SQUIT %s from %s (%s)",
acptr->name, parv[0], comment);
sendto_flag(SCH_DEBUG,
"Forwarding SQUIT %s to %s from %s (%s)",
acptr->name, acptr->from->name,
parv[0], comment);
return 1;
}
/*
** ack, bad server encountered!
** must send back to other good servers which were trying to
** do the right thing, and fake the yet to come SQUIT which
** will never be received from the bad servers.
*/
if (IsServer(cptr) &&
(cptr->serv->version & SV_OLDSQUIT) == 0)
{
sendto_one(cptr, ":%s SQUIT %s :%s (Bounced for %s)",
ME, acptr->name, comment, parv[0]);
sendto_flag(SCH_DEBUG, "Bouncing SQUIT %s back to %s",
acptr->name, acptr->from->name);
}
}
/*
** Notify all opers, if my local link is remotely squitted
*/
if (MyConnect(acptr) && !IsAnOper(cptr))
{
sendto_ops_butone(NULL, &me,
":%s WALLOPS :Received SQUIT %s from %s (%s)",
ME, server, parv[0], comment);
#if defined(USE_SYSLOG) && defined(SYSLOG_SQUIT)
syslog(LOG_DEBUG,"SQUIT From %s : %s (%s)",
parv[0], server, comment);
#endif
}
if (MyConnect(acptr))
{
int timeconnected = timeofday - acptr->firsttime;
sendto_flag(SCH_NOTICE,
"Closing link to %s (%d, %2d:%02d:%02d)",
get_client_name(acptr, FALSE),
timeconnected / 86400,
(timeconnected % 86400) / 3600,
(timeconnected % 3600)/60,
timeconnected % 60);
}
sendto_flag(SCH_SERVER, "Received SQUIT %s from %s (%s)",
acptr->name, parv[0], comment);
if (MyConnect(acptr) &&
IsServer(cptr) && (cptr->serv->version & SV_OLDSQUIT) == 0)
{
sendto_one(cptr, ":%s SQUIT %s :%s", ME, acptr->name, comment);
sendto_flag(SCH_DEBUG, "Issuing additionnal SQUIT %s for %s",
acptr->name, acptr->from->name);
}
return exit_client(cptr, acptr, sptr, comment);
}
/*
** check_version
** The PASS command delivers additional information about incoming
** connection. The data is temporarily stored to info/name/username
** in m_pass() and processed here before the fields are natively used.
** Return: < 1: exit/error, > 0: no error
*/
int check_version(cptr)
aClient *cptr;
{
char *id, *misc = NULL, *link = NULL;
Debug((DEBUG_INFO,"check_version: %s", cptr->info));
if (cptr->info == DefInfo)
{
cptr->hopcount = SV_OLD;
return 1; /* no version checked (e.g. older than 2.9) */
}
if (id = index(cptr->info, ' '))
{
*id++ = '\0';
if (link = index(id, ' '))
*link++ = '\0';
if (misc = index(id, '|'))
*misc++ = '\0';
else
{
misc = id;
id = "";
}
}
else
id = "";
if (!strncmp(cptr->info, "021", 3))
{
cptr->hopcount = SV_29|SV_NJOIN|SV_NMODE|SV_NCHAN; /* SV_2_10*/
#ifdef RUSNET_IRCD
cptr->hopcount |= SV_FORCE; /* RusNet 1.4 */
if (link && strchr(link, PROTO_CAPS_K))
cptr->hopcount |= SV_KLINE;
if (link && strchr(link, PROTO_CAPS_R))
cptr->hopcount |= SV_RMODE;
#endif
}
else if (!strncmp(cptr->info, "0209", 4))
cptr->hopcount = SV_29|SV_OLDSQUIT; /* 2.9+ protocol */
else
cptr->hopcount = SV_OLD; /* uhuh */
if (!strcmp("IRC", id) && !strncmp(cptr->info, "02100", 5) &&
atoi(cptr->info+5) < 20600)
/* before 2.10.3a6 ( 2.10.3a5 is just broken ) */
cptr->hopcount |= SV_OLDSQUIT;
/* Check version number/mask from conf */
sprintf(buf, "%s/%s", id, cptr->info);
if (find_two_masks(cptr->name, buf, CONF_VER))
{
sendto_flag(SCH_ERROR, "Bad version %s %s from %s", id,
cptr->info, get_client_name(cptr, TRUE));
return exit_client(cptr, cptr, &me, "Bad version");
}
if (misc)
{
sprintf(buf, "%s/%s", id, misc);
/* Check version flags from conf */
if (find_conf_flags(cptr->name, buf, CONF_VER))
{
sendto_flag(SCH_ERROR, "Bad flags %s (%s) from %s",
misc, id, get_client_name(cptr, TRUE));
return exit_client(cptr, cptr, &me, "Bad flags");
}
}
/* right now, I can't code anything good for this */
/* Stop whining, and do it! ;) */
if (link && strchr(link, 'Z')) /* Compression requested */
cptr->flags |= FLAGS_ZIPRQ;
/*
* If server was started with -p strict, be careful about the
* other server mode.
*/
if (link && strncmp(cptr->info, "020", 3) &&
(bootopt & BOOT_STRICTPROT) && !strchr(link, 'P'))
return exit_client(cptr, cptr, &me, "Unsafe mode");
return 2;
}
/*
** m_server
** parv[0] = sender prefix
** parv[1] = servername
** parv[2] = serverinfo/hopcount
** parv[3] = token/serverinfo (2.9)
** parv[4] = serverinfo
*/
int m_server(cptr, sptr, parc, parv)
aClient *cptr, *sptr;
int parc;
char *parv[];
{
Reg char *ch;
Reg int i;
char info[REALLEN+1], *inpath, *host, *stok;
aClient *acptr, *bcptr;
aConfItem *aconf;
int hop = 0, token = 0;
if (sptr->user) /* in case NICK hasn't been received yet */
{
sendto_one(sptr, err_str(ERR_ALREADYREGISTRED, parv[0]));
return 1;
}
info[0] = info[REALLEN] = '\0'; /* strncpy() doesn't guarantee NULL */
inpath = get_client_name(cptr, FALSE);
if (parc < 2 || *parv[1] == '\0')
{
sendto_one(cptr,"ERROR :No servername");
return 1;
}
host = parv[1];
if (parc > 3 && (hop = atoi(parv[2])))
{
if (parc > 4 && (token = atoi(parv[3])))
(void)strncpy(info, parv[4], REALLEN);
else
(void)strncpy(info, parv[3], REALLEN);
}
else if (parc > 2)
{
(void)strncpy(info, parv[2], REALLEN);
i = strlen(info);
if (parc > 3 && ((i+2) < REALLEN))
{
(void)strncat(info, " ", REALLEN - i - 1);
(void)strncat(info, parv[3], REALLEN - i - 2);
}
}
/*
** 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) > (size_t) HOSTLEN)
host[HOSTLEN] = '\0';
for (ch = host; *ch; ch++)
if (*ch <= ' ' || *ch > '~')
break;
if (*ch || !index(host, '.'))
{
sendto_one(sptr,"ERROR :Bogus server name (%s)", host);
sendto_flag(SCH_ERROR, "Bogus server name (%s) from %s", host,
get_client_name(cptr, TRUE));
return 2;
}
/* *WHEN* can it be that "cptr != sptr" ????? --msa */
/* When SERVER command (like now) has prefix. -avalon */
if (IsRegistered(cptr) && ((acptr = find_name(host, NULL))
|| (acptr = find_mask(host, NULL))))
{
/*
** This link is trying feed me a server that I already have
** access through another path -- multiple paths not accepted
** currently, kill this link immeatedly!!
**
** Rather than KILL the link which introduced it, KILL the
** youngest of the two links. -avalon
*/
bcptr = (cptr->firsttime > acptr->from->firsttime) ? cptr :
acptr->from;
sendto_one(bcptr, "ERROR :Server %s already exists", host);
/* in both cases the bcptr (the youngest is killed) */
if (bcptr == cptr)
{
sendto_flag(SCH_ERROR,
"Link %s cancelled, server %s already exists",
get_client_name(bcptr, TRUE), host);
return exit_client(bcptr, bcptr, &me, "Server Exists");
}
else
{
/*
** in this case, we are not dropping the link from
** which we got the SERVER message. Thus we canNOT
** `return' yet! -krys
*/
strcpy(buf, get_client_name(bcptr, TRUE));
sendto_flag(SCH_ERROR,
"Link %s cancelled, server %s reintroduced by %s",
buf, host, get_client_name(cptr, TRUE));
(void) exit_client(bcptr, bcptr, &me, "Server Exists");
}
}
if ((acptr = find_person(host, NULL)) && (acptr != cptr))
{
/*
** Server trying to use the same name as a person. Would
** cause a fair bit of confusion. Enough to make it hellish
** for a while and servers to send stuff to the wrong place.
*/
sendto_one(cptr,"ERROR :Nickname %s already exists!", host);
sendto_flag(SCH_ERROR,
"Link %s cancelled: Server/nick collision on %s",
inpath, host);
sendto_serv_butone(NULL, /* all servers */
":%s KILL %s :%s (%s <- %s)",
ME, acptr->name, ME,
acptr->from->name, host);
acptr->flags |= FLAGS_KILLED;
(void)exit_client(NULL, acptr, &me, "Nick collision");
return exit_client(cptr, cptr, &me, "Nick as Server");
}
if (IsServer(cptr))
{
/* A server can only be introduced by another server. */
if (!IsServer(sptr))
{
sendto_flag(SCH_LOCAL,
"Squitting %s brought by %s (introduced by %s)",
host, get_client_name(cptr, FALSE),
sptr->name);
sendto_one(cptr,
":%s SQUIT %s :(Introduced by %s from %s)",
me.name, host, sptr->name,
get_client_name(cptr, FALSE));
return 1;
}
/*
** 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...
*/
if (parc == 1 || info[0] == '\0')
{
sendto_one(cptr,
"ERROR :No server info specified for %s",
host);
sendto_flag(SCH_ERROR, "No server info for %s from %s",
host, get_client_name(cptr, TRUE));
return 1;
}
/*
** See if the newly found server is behind a guaranteed
** leaf (L-line). If so, close the link.
*/
if ((aconf = find_conf_host(cptr->confs, host, CONF_LEAF)) &&
(!aconf->port || (hop > aconf->port)))
{
sendto_flag(SCH_ERROR,
"Leaf-only link %s->%s - Closing",
get_client_name(cptr, TRUE),
aconf->host ? aconf->host : "*");
sendto_one(cptr, "ERROR :Leaf-only link, sorry.");
return exit_client(cptr, cptr, &me, "Leaf Only");
}
/*
**
*/
if (!(aconf = find_conf_host(cptr->confs, host, CONF_HUB)) ||
(aconf->port && (hop > aconf->port)) )
{
sendto_flag(SCH_ERROR,
"Non-Hub link %s introduced %s(%s).",
get_client_name(cptr, TRUE), host,
aconf ? (aconf->host ? aconf->host : "*") :
"!");
return exit_client(cptr, cptr, &me,
"Too many servers");
}
/*
** 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, get_client_name(cptr, TRUE),
host, "closing link because",
BadPtr(aconf->passwd) ? "reason unspecified" :
aconf->passwd);
sendto_one(cptr,
"ERROR :%s is not welcome: %s. %s",
host, BadPtr(aconf->passwd) ?
"reason unspecified" : aconf->passwd,
"Go away and get a life");
return exit_client(cptr, cptr, &me, "Q-Lined Server");
}
acptr = make_client(cptr);
(void)make_server(acptr);
acptr->hopcount = hop;
strncpyzt(acptr->name, host, sizeof(acptr->name));
#ifdef RUSNET_IRCD
acptr->serv->crc = gen_crc(host);
#endif
if (acptr->info != DefInfo)
MyFree(acptr->info);
acptr->info = mystrdup(info);
acptr->serv->up = sptr->name;
acptr->serv->stok = token;
acptr->serv->snum = find_server_num(acptr->name);
SetServer(acptr);
istat.is_serv++;
#ifdef EXTRA_STATISTICS
/*
** Keep track of the highest number of connected
** servers (hostmasks) to this network.
**/
if (istat.is_serv > istat.is_m_serv)
istat.is_m_serv = istat.is_serv;
#endif
add_client_to_list(acptr);
(void)add_to_client_hash_table(acptr->name, acptr);
(void)add_to_server_hash_table(acptr->serv, cptr);
/*
** Old sendto_serv_but_one() call removed because we now
** need to send different names to different servers
** (domain name matching)
*/
for (i = fdas.highest; i >= 0; i--)
{
if (!(bcptr = local[fdas.fd[i]]) || !IsServer(bcptr) ||
bcptr == cptr || IsMe(bcptr))
continue;
if (!(aconf = bcptr->serv->nline))
{
sendto_flag(SCH_NOTICE,
"Lost N-line for %s on %s:Closing",
get_client_name(cptr, TRUE), host);
return exit_client(cptr, cptr, &me,
"Lost N line");
}
if (match(my_name_for_link(ME, aconf->port),
acptr->name) == 0)
continue;
stok = acptr->serv->tok;
sendto_one(bcptr, ":%s SERVER %s %d %s :%s", parv[0],
acptr->name, hop+1, stok, acptr->info);
}
#ifdef USE_SERVICES
check_services_butone(SERVICE_WANT_SERVER, acptr->name, acptr,
":%s SERVER %s %d %s :%s", parv[0],
acptr->name, hop+1, acptr->serv->tok,
acptr->info);
#endif
sendto_flag(SCH_SERVER, "Received SERVER %s from %s (%d %s)",
acptr->name, parv[0], hop+1, acptr->info);
return 0;
}
if ((!IsUnknown(cptr) && !IsHandshake(cptr)) ||
(cptr->flags & FLAGS_UNKCMD))
return 1;
/*
** A local link that is still in undefined state wants
** to be a SERVER. Check if this is allowed and change
** status accordingly...
*/
strncpyzt(cptr->name, host, sizeof(cptr->name));
/* cptr->name has to exist before check_version(), and cptr->info
* may not be filled before check_version(). */
if ((hop = check_version(cptr)) < 1)
return hop; /* from exit_client() */
if (cptr->info != DefInfo)
MyFree(cptr->info);
cptr->info = mystrdup(info[0] ? info : ME);
switch (check_server_init(cptr))
{
case 0 :
return m_server_estab(cptr);
case 1 :
sendto_flag(SCH_NOTICE, "Checking access for %s",
get_client_name(cptr,TRUE));
return 1;
default :
ircstp->is_ref++;
sendto_flag(SCH_NOTICE, "Unauthorized server from %s.",
get_client_host(cptr));
return exit_client(cptr, cptr, &me, "No C/N conf lines");
}
}
int m_server_estab(cptr)
Reg aClient *cptr;
{
Reg aClient *acptr;
Reg aConfItem *aconf, *bconf;
char mlname[HOSTLEN+1], *inpath, *host, *s, *encr, *stok;
int split, i;
host = cptr->name;
inpath = get_client_name(cptr,TRUE); /* "refresh" inpath with host */
split = strcasecmp(cptr->name, cptr->sockhost);
if (!(aconf = find_conf(cptr->confs, host, CONF_NOCONNECT_SERVER)))
{
ircstp->is_ref++;
sendto_one(cptr,
"ERROR :Access denied. No N line for server %s",
inpath);
sendto_flag(SCH_ERROR,
"Access denied. No N line for server %s", inpath);
return exit_client(cptr, cptr, &me, "No N line for server");
}
if (!(bconf = find_conf(cptr->confs, host, CONF_CONNECT_SERVER|
CONF_ZCONNECT_SERVER)))
{
ircstp->is_ref++;
sendto_one(cptr, "ERROR :Only N (no C) field for server %s",
inpath);
sendto_flag(SCH_ERROR,
"Only N (no C) field for server %s",inpath);
return exit_client(cptr, cptr, &me, "No C line for server");
}
if (cptr->hopcount == SV_OLD) /* lame test, should be == 0 */
{
sendto_one(cptr, "ERROR :Server version is too old.");
sendto_flag(SCH_ERROR, "Old version for %s", inpath);
return exit_client(cptr, cptr, &me, "Old version");
}
#ifdef CRYPT_LINK_PASSWORD
/* pass whole aconf->passwd as salt, let crypt() deal with it */
if (*cptr->passwd)
{
extern char *crypt();
encr = crypt(cptr->passwd, aconf->passwd);
if (encr == NULL)
{
ircstp->is_ref++;
sendto_one(cptr, "ERROR :No Access (crypt failed) %s",
inpath);
sendto_flag(SCH_ERROR,
"Access denied (crypt failed) %s", inpath);
return exit_client(cptr, cptr, &me, "Bad Password");
}
}
else
encr = "";
#else
encr = cptr->passwd;
#endif /* CRYPT_LINK_PASSWORD */
if (*aconf->passwd && !StrEq(aconf->passwd, encr))
{
ircstp->is_ref++;
sendto_one(cptr, "ERROR :No Access (passwd mismatch) %s",
inpath);
sendto_flag(SCH_ERROR,
"Access denied (passwd mismatch) %s", inpath);
return exit_client(cptr, cptr, &me, "Bad Password");
}
bzero(cptr->passwd, sizeof(cptr->passwd));
#ifndef HUB
for (i = 0; i <= highest_fd; i++)
if (local[i] && IsServer(local[i]))
{
ircstp->is_ref++;
sendto_flag(SCH_ERROR, "I'm a leaf, cannot link %s",
get_client_name(cptr, TRUE));
return exit_client(cptr, cptr, &me, "I'm a leaf");
}
#endif
(void) strcpy(mlname, my_name_for_link(ME, aconf->port));
if (IsUnknown(cptr))
{
if (bconf->passwd[0])
sendto_one(cptr, "PASS %s %s IRC|%s %s"
#ifdef ZIP_LINKS
"%s"
#endif
#ifdef RUSNET_IRCD
"%c%c"
#endif
, bconf->passwd, pass_version, serveropts,
#ifdef ZIP_LINKS
(bconf->status == CONF_ZCONNECT_SERVER) ? "Z" : "",
#endif
(bootopt & BOOT_STRICTPROT) ? "P" : ""
#ifdef RUSNET_IRCD
, PROTO_CAPS_K, PROTO_CAPS_R
#endif
);
/*
** Pass my info to the new server
*/
sendto_one(cptr, "SERVER %s 1 :%s", mlname, me.info);
/*
** If we get a connection which has been authorized to be
** an already existing connection, remove the already
** existing connection if it has a sendq else remove the
** new and duplicate server. -avalon
** Remove existing link only if it has been linked for longer
** and has sendq higher than a threshold. -Vesa
*/
if ((acptr = find_name(host, NULL))
|| (acptr = find_mask(host, NULL)))
{
if (MyConnect(acptr) &&
DBufLength(&acptr->sendQ) > CHREPLLEN &&
timeofday - acptr->firsttime > TIMESEC)
(void) exit_client(acptr, acptr, &me,
"New Server");
else
return exit_client(cptr, cptr, &me,
"Server Exists");
}
}
else
{
s = (char *)index(aconf->host, '@');
*s = '\0'; /* should never be NULL */
Debug((DEBUG_INFO, "Check Usernames [%s]vs[%s]",
aconf->host, cptr->username));
if (match(aconf->host, cptr->username))
{
*s = '@';
ircstp->is_ref++;
sendto_flag(SCH_ERROR,
"Username mismatch [%s]v[%s] : %s",
aconf->host, cptr->username,
get_client_name(cptr, TRUE));
sendto_one(cptr, "ERROR :No Username Match");
return exit_client(cptr, cptr, &me, "Bad User");
}
*s = '@';
}
#ifdef ZIP_LINKS
if ((cptr->flags & FLAGS_ZIPRQ) &&
(bconf->status == CONF_ZCONNECT_SERVER))
{
if (zip_init(cptr) == -1)
{
zip_free(cptr);
sendto_flag(SCH_ERROR,
"Unable to setup compressed link for %s",
get_client_name(cptr, TRUE));
return exit_client(cptr, cptr, &me,
"zip_init() failed");
}
cptr->flags |= FLAGS_ZIP|FLAGS_ZIPSTART;
}
#endif
det_confs_butmask(cptr, CONF_LEAF|CONF_HUB|CONF_NOCONNECT_SERVER);
/*
** *WARNING*
** In the following code in place of plain server's
** name we send what is returned by get_client_name
** which may add the "sockhost" after the name. It's
** *very* *important* that there is a SPACE between
** the name and sockhost (if present). The receiving
** server will start the information field from this
** first blank and thus puts the sockhost into info.
** ...a bit tricky, but you have been warned, besides
** code is more neat this way... --msa
*/
SetServer(cptr);
istat.is_unknown--;
istat.is_serv++;
istat.is_myserv++;
#ifdef EXTRA_STATISTICS
/*
** Keep track of the highest number of connected
** servers to this network as well as the servers
** connected to me
**/
if (istat.is_serv > istat.is_m_serv)
istat.is_m_serv = istat.is_serv;
if (istat.is_myserv > istat.is_m_myserv)
istat.is_m_myserv = istat.is_myserv;
#endif
nextping = timeofday;
sendto_flag(SCH_NOTICE, "Link with %s established. (%X%s)", inpath,
cptr->hopcount, (cptr->flags & FLAGS_ZIP) ? "z" : "");
(void)add_to_client_hash_table(cptr->name, cptr);
/* doesnt duplicate cptr->serv if allocted this struct already */
(void)make_server(cptr);
cptr->serv->up = me.name;
cptr->serv->nline = aconf;
cptr->serv->version = cptr->hopcount; /* temporary location */
cptr->hopcount = 1; /* local server connection */
cptr->serv->snum = find_server_num(cptr->name);
cptr->serv->stok = 1;
#ifdef RUSNET_IRCD
cptr->serv->crc = gen_crc(cptr->name);
/*
#ifdef USE_SERVICES
if (strcasecmp(cptr->name, SERVICES_SERV) == 0)
invincible = cptr->serv->crc;
#endif
*/
#endif
cptr->flags |= FLAGS_CBURST;
(void) add_to_server_hash_table(cptr->serv, cptr);
Debug((DEBUG_NOTICE, "Server link established with %s V%X %d",
cptr->name, cptr->serv->version, cptr->serv->stok));
add_fd(cptr->fd, &fdas);
/*
** Remove all NDELAY locks depends on this server. -kmale
*/
release_locks_byserver(cptr->name,(long)(DELAYCHASETIMELIMIT));
#ifdef USE_SERVICES
check_services_butone(SERVICE_WANT_SERVER, cptr->name, cptr,
":%s SERVER %s %d %s :%s", ME, cptr->name,
cptr->hopcount+1, cptr->serv->tok, cptr->info);
#endif
sendto_flag(SCH_SERVER, "Sending SERVER %s (%d %s)", cptr->name,
1, cptr->info);
/*
** 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 = fdas.highest; i >= 0; i--)
{
if (!(acptr = local[fdas.fd[i]]) || !IsServer(acptr) ||
acptr == cptr || IsMe(acptr))
continue;
if ((aconf = acptr->serv->nline) &&
!match(my_name_for_link(ME, aconf->port), cptr->name))
continue;
stok = cptr->serv->tok;
if (split)
sendto_one(acptr,":%s SERVER %s 2 %s :[%s] %s",
ME, cptr->name, stok,
cptr->sockhost, cptr->info);
else
sendto_one(acptr,":%s SERVER %s 2 %s :%s",
ME, cptr->name, stok, 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...
**
** ALSO NOTE: using the get_client_name for server names--
** see previous *WARNING*!!! (Also, original inpath
** is destroyed...)
*/
aconf = cptr->serv->nline;
for (acptr = &me; acptr; acptr = acptr->prev)
{
/* acptr->from == acptr for acptr == cptr */
if ((acptr->from == cptr) || !IsServer(acptr))
continue;
if (*mlname == '*' && match(mlname, acptr->name) == 0)
continue;
split = (MyConnect(acptr) &&
strcasecmp(acptr->name, acptr->sockhost));
stok = acptr->serv->tok;
if (split)
sendto_one(cptr, ":%s SERVER %s %d %s :[%s] %s",
acptr->serv->up,
acptr->name, acptr->hopcount+1, stok,
acptr->sockhost, acptr->info);
else
sendto_one(cptr, ":%s SERVER %s %d %s :%s",
acptr->serv->up, acptr->name,
acptr->hopcount+1, stok, 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
*/
if (*mlname == '*' &&
match(mlname, acptr->user->server) == 0)
stok = me.serv->tok;
else
stok = acptr->user->servp->tok;
send_umode(NULL, acptr, 0, SEND_UMODES, buf);
sendto_one(cptr,"NICK %s %d %s %s %s %s :%s",
acptr->name, acptr->hopcount + 1,
acptr->user->username,
#ifdef RUSNET_IRCD
acptr->sockhost,
#else
acptr->user->host,
#endif
stok,
(*buf) ? buf : "+", acptr->info);
if ((cptr->serv->version & SV_NJOIN) == 0)
send_user_joins(cptr, acptr);
}
else if (IsService(acptr) &&
match(acptr->service->dist, cptr->name) == 0)
{
if (*mlname == '*' &&
match(mlname, acptr->service->server) == 0)
stok = me.serv->tok;
else
stok = acptr->service->servp->tok;
sendto_one(cptr, "SERVICE %s %s %s %d %d :%s",
acptr->name, stok, acptr->service->dist,
acptr->service->type, acptr->hopcount + 1,
acptr->info);
}
/* the previous if does NOT catch all services.. ! */
}
flush_connections(cptr->fd);
/*
** Last, pass all channels modes
** only sending modes for LIVE channels.
*/
{
Reg aChannel *chptr;
for (chptr = channel; chptr; chptr = chptr->nextch)
if (chptr->users)
{
if (cptr->serv->version & SV_NJOIN)
send_channel_members(cptr, chptr);
send_channel_modes(cptr, chptr);
}
}
cptr->flags &= ~FLAGS_CBURST;
#ifdef ZIP_LINKS
/*
** some stats about the connect burst,
** they are slightly incorrect because of cptr->zip->outbuf.
*/
if ((cptr->flags & FLAGS_ZIP) && cptr->zip->out->total_in)
sendto_flag(SCH_NOTICE,
"Connect burst to %s: %lu, compressed: %lu (%3.1f%%)",
get_client_name(cptr, TRUE),
cptr->zip->out->total_in,cptr->zip->out->total_out,
(float) 100*cptr->zip->out->total_out/cptr->zip->out->total_in);
#endif
return 0;
}
int m_reconnect(cptr, sptr, parc, parv)
aClient *cptr, *sptr;
int parc;
char *parv[];
{
aConfItem *aconf;
aClient *acptr = NULL;
char *name;
int i;
if (IsRegistered(sptr))
return exit_client(cptr, sptr, &me, "Already registered");
if (parc < 3)
return 1;
name = parv[1];
for (i = highest_fd; i >= 0; i--)
{
if (!(acptr = local[i]) || !IsHeld(acptr) ||
bcmp((char *)&acptr->ip, (char *)&cptr->ip,
sizeof(acptr->ip)) || strcasecmp(acptr->name, name))
continue;
if (!(aconf = find_conf_name(name, CONF_CONNECT_SERVER|
CONF_ZCONNECT_SERVER)) ||
atoi(parv[2]) != acptr->receiveM)
break;
attach_confs(acptr, name, CONF_SERVER_MASK);
acptr->flags &= ~FLAGS_HELD;
acptr->fd = cptr->fd;
cptr->fd = -2;
SetUnknown(acptr);
if (check_server(acptr, NULL, NULL, NULL, TRUE) < 0)
break;
sendto_flag(SCH_NOTICE, "%s has reconnected",
get_client_name(acptr, TRUE));
return exit_client(cptr, sptr, &me, "Reconnected");
}
sendto_flag(SCH_NOTICE, "Reconnect from %s failed",
get_client_name(cptr, TRUE));
if (acptr)
(void) exit_client(cptr, acptr, &me, "Reconnect failed");
return exit_client(cptr, sptr, &me, "Reconnect failed");
}
/*
** 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 (IsServer(cptr) && check_link(cptr))
{
sendto_one(sptr, rpl_str(RPL_TRYAGAIN, parv[0]),
"INFO");
return 5;
}
if (hunt_server(cptr,sptr,":%s INFO :%s",1,parc,parv) == HUNTED_ISME)
{
while (*text)
sendto_one(sptr, rpl_str(RPL_INFO, parv[0]), *text++);
sendto_one(sptr, rpl_str(RPL_INFO, parv[0]), "");
sendto_one(sptr,
":%s %d %s :Birth Date: %s, compile # %s",
ME, RPL_INFO, parv[0], creation, generation);
sendto_one(sptr, ":%s %d %s :On-line since %s",
ME, RPL_INFO, parv[0],
myctime(me.firsttime));
sendto_one(sptr, rpl_str(RPL_ENDOFINFO, parv[0]));
return 5;
}
else
return 10;
}
/*
** 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[];
{
Reg aServer *asptr;
char *mask;
aClient *acptr;
if (parc > 2)
{
if (IsServer(cptr) && check_link(cptr) && !IsOper(sptr))
{
sendto_one(sptr, rpl_str(RPL_TRYAGAIN, parv[0]),
"LINKS");
return 5;
}
if (hunt_server(cptr, sptr, ":%s LINKS %s :%s", 1, parc, parv)
!= HUNTED_ISME)
return 5;
mask = parv[2];
}
else
mask = parc < 2 ? NULL : parv[1];
for (asptr = svrtop, (void)collapse(mask); asptr; asptr = asptr->nexts)
{
acptr = asptr->bcptr;
if (!BadPtr(mask) && match(mask, acptr->name))
continue;
sendto_one(sptr, rpl_str(RPL_LINKS, parv[0]),
acptr->name, acptr->serv->up,
acptr->hopcount, (acptr->info[0] ? acptr->info :
"(Unknown Location)"));
}
sendto_one(sptr, rpl_str(RPL_ENDOFLINKS, parv[0]),
BadPtr(mask) ? "*" : mask);
return 2;
}
/*
** 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 (parc < 2 || *parv[1] == '\0')
{
sendto_one(sptr, err_str(ERR_NORECIPIENT, parv[0]), "SUMMON");
return 1;
}
user = parv[1];
host = (parc < 3 || BadPtr(parv[2])) ? ME : 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, parv[0]),
"open", UTMP);
return 1;
}
# 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))
{
SPRINTF(ttyname,"/dev/%s",linetmp);
if (stat(ttyname,&stb) == -1)
{
sendto_one(sptr,
err_str(ERR_FILEERROR,
sptr->name),
"stat", ttyname);
return 1;
}
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, parv[0]), user);
else
summon(sptr, user, linebuf, chname);
#else
sendto_one(sptr, err_str(ERR_SUMMONDISABLED, parv[0]));
#endif /* ENABLE_SUMMON */
}
else
return 3;
return 2;
}
/*
** 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!
*/
#ifdef RUSNET_IRCD
#define REP_ARRAY_SIZE 19
#else
#define REP_ARRAY_SIZE 17
#endif
static int report_array[REP_ARRAY_SIZE][3] = {
{ CONF_ZCONNECT_SERVER, RPL_STATSCLINE, 'c'},
{ CONF_CONNECT_SERVER, RPL_STATSCLINE, 'C'},
{ CONF_NOCONNECT_SERVER, RPL_STATSNLINE, 'N'},
{ CONF_CLIENT, RPL_STATSILINE, 'I'},
{ CONF_RCLIENT, RPL_STATSILINE, 'i'},
{ CONF_OTHERKILL, RPL_STATSKLINE, 'k'},
{ 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_SERVICE, RPL_STATSSLINE, 'S'},
{ CONF_VER, RPL_STATSVLINE, 'V'},
{ CONF_BOUNCE, RPL_STATSBLINE, 'B'},
{ CONF_DENY, RPL_STATSDLINE, 'D'},
#ifdef RUSNET_IRCD
{ CONF_EXEMPT, RPL_STATSKLINE, 'E'},
{ CONF_INTERFACE, RPL_STATSFLINE, 'F'},
#endif
{ 0, 0, 0}
};
static void report_configured_links(sptr, to, mask)
aClient *sptr;
char *to;
int mask;
{
static char null[] = "<NULL>";
aConfItem *tmp;
int *p, port;
char c, *host, *pass, *name;
#ifdef RUSNET_IRCD
aClient *acptr;
acptr = find_client(to, NULL);
#endif
for (tmp = (mask & (CONF_KILL|CONF_OTHERKILL)) ? kconf : 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/V/B/F lines the passwd contents can be
* displayed on STATS reply. -Vesa
*/
if (tmp->status == CONF_KILL
|| tmp->status == CONF_OTHERKILL
|| tmp->status == CONF_VER
#ifdef RUSNET_IRCD
|| tmp->status == CONF_INTERFACE
#endif
|| tmp->status == CONF_BOUNCE)
sendto_one(sptr, rpl_str(p[1], to), c, host,
(pass) ? pass : null,
name, port, get_conf_class(tmp));
#ifdef RUSNET_IRCD
else if (tmp->status == CONF_CONNECT_SERVER ||
tmp->status == CONF_ZCONNECT_SERVER ||
tmp->status == CONF_CLIENT ||
tmp->status == CONF_RCLIENT)
sendto_one(sptr, rpl_str(p[1], to), c, host,
(pass || !(IsOper(sptr) ||
IsServer(sptr))) ? "x" : null,
name, port, get_conf_class(tmp),
tmp->localpref);
else if (tmp->status == CONF_OPERATOR ||
tmp->status == CONF_LOCOP)
sendto_one(sptr, rpl_str(p[1], to), c,
(acptr && IsAnOper(acptr)) ? host : "-",
(pass) ? "x" : null,
name, port, get_conf_class(tmp));
#endif
else
sendto_one(sptr, rpl_str(p[1], to), c, host,
(pass) ? "x" : null,
name, port, get_conf_class(tmp));
}
#ifdef RUSNET_IRCD
if (mask & CONF_EXEMPT)
for (tmp = econf; 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;
sendto_one(sptr, rpl_str(p[1], to), c, host,
(pass) ? pass : null,
name, port, get_conf_class(tmp));
}
#endif
return;
}
static void report_ping(sptr, to)
aClient *sptr;
char *to;
{
aConfItem *tmp;
aCPing *cp;
for (tmp = conf; tmp; tmp = tmp->next)
if ((cp = tmp->ping) && cp->lseq)
{
if (strcasecmp(tmp->name, tmp->host))
SPRINTF(buf,"%s[%s]",tmp->name, tmp->host);
else
(void)strcpy(buf, tmp->name);
sendto_one(sptr, rpl_str(RPL_STATSPING, to),
buf, cp->lseq, cp->lrecvd,
cp->ping / (cp->recvd ? cp->recvd : 1),
tmp->pref);
sendto_flag(SCH_DEBUG, "%s: %d", buf, cp->seq);
}
return;
}
int m_stats(cptr, sptr, parc, parv)
aClient *cptr, *sptr;
int parc;
char *parv[];
{
static char Lformat[] = ":%s %d %s %s %u %u %u %u %u :%u";
struct Message *mptr;
aClient *acptr;
char stat = parc > 1 ? parv[1][0] : '\0';
Reg int i;
int wilds, doall;
char *name, *cm;
#ifdef STATS_RESTRICTED
if (!IsAnOper(sptr) && !(stat == 'c' || stat == 'C' ||
stat == 'H' || stat == 'h' ||
stat == 'o' || stat == 'O' ||
stat == 'U' || stat == 'u'))
{
sendto_one(sptr, err_str(ERR_NOPRIVILEGES, parv[0]));
return 2;
}
#endif
if (IsServer(cptr) &&
(stat != 'd' && stat != 'p' && stat != 'q' && stat != 's' &&
stat != 'u' && stat != 'v') &&
!((stat == 'o' || stat == 'c') && IsOper(sptr)))
{
if (check_link(cptr))
{
sendto_one(sptr, rpl_str(RPL_TRYAGAIN, parv[0]),
"STATS");
return 5;
}
}
if (parc == 3)
{
if (hunt_server(cptr, sptr, ":%s STATS %s %s",
2, parc, parv) != HUNTED_ISME)
return 5;
}
else if (parc >= 3)
{
if (hunt_server(cptr, sptr, ":%s STATS %s %s %s",
2, parc, parv) != HUNTED_ISME)
return 5;
}
name = (parc > 2) ? parv[2] : ME;
cm = (parc > 3) ? parv[3]: name;
doall = !match(name, ME) && !match(cm, ME);
wilds = index(cm, '*') || index(cm, '?');
switch (stat)
{
case 'L' : case 'l' :
/*
* send info about connections which match, or all if the
* mask matches ME. Only restrictions are on those who
* are invisible not being visible to 'foreigners' who use
* a wild card based search to list it.
*/
if (doall || wilds)
{
for (i = 0; i <= highest_fd; i++)
{
if (!(acptr = local[i]))
continue;
if (IsPerson(acptr) && !(MyConnect(sptr)
&& IsAnOper(sptr)) && acptr != sptr)
continue;
if (wilds && match(cm, acptr->name))
continue;
sendto_one(cptr, Lformat, ME,
RPL_STATSLINKINFO, parv[0],
get_client_name(acptr, isupper(stat)),
(int)DBufLength(&acptr->sendQ),
(int)acptr->sendM, (int)acptr->sendK,
(int)acptr->receiveM,
(int)acptr->receiveK,
timeofday - acptr->firsttime);
}
}
else
{
if ((acptr = find_client(cm, NULL)))
sendto_one(cptr, Lformat, ME,
RPL_STATSLINKINFO, parv[0],
get_client_name(acptr, isupper(stat)),
(int)DBufLength(&acptr->sendQ),
(int)acptr->sendM, (int)acptr->sendK,
(int)acptr->receiveM,
(int)acptr->receiveK,
timeofday - acptr->firsttime);
}
break;
#if defined(USE_IAUTH)
case 'a' : case 'A' : /* iauth configuration */
report_iauth_conf(sptr, parv[0]);
break;
#endif
case 'B' : case 'b' : /* B conf lines */
report_configured_links(cptr, parv[0], CONF_BOUNCE);
break;
case 'c' : case 'C' : /* C and N conf lines */
report_configured_links(cptr, parv[0], CONF_CONNECT_SERVER|
CONF_ZCONNECT_SERVER|
CONF_NOCONNECT_SERVER);
break;
case 'd' : case 'D' : /* defines */
send_defines(cptr, parv[0]);
break;
#ifdef RUSNET_IRCD
case 'F' : case 'f' : /* F conf lines */
report_configured_links(cptr, parv[0], CONF_INTERFACE);
break;
#endif
case 'H' : case 'h' : /* H, L and D conf lines */
report_configured_links(cptr, parv[0],
CONF_HUB|CONF_LEAF|CONF_DENY);
break;
case 'I' : case 'i' : /* I (and i) conf lines */
report_configured_links(cptr, parv[0],
CONF_CLIENT|CONF_RCLIENT);
break;
case 'K' : case 'k' : /* K lines */
report_configured_links(cptr, parv[0],
(CONF_KILL|CONF_OTHERKILL
#ifdef RUSNET_IRCD
|CONF_EXEMPT
#endif
));
break;
case 'M' : case 'm' : /* commands use/stats */
for (mptr = msgtab; mptr->cmd; mptr++)
if (mptr->count)
sendto_one(cptr, rpl_str(RPL_STATSCOMMANDS,
parv[0]), mptr->cmd,
mptr->count, mptr->bytes,
mptr->rcount);
break;
case 'o' : case 'O' : /* O (and o) lines */
report_configured_links(cptr, parv[0], CONF_OPS);
break;
case 'p' : case 'P' : /* ircd ping stats */
report_ping(sptr, parv[0]);
break;
case 'Q' : case 'q' : /* Q lines */
report_configured_links(cptr,parv[0],CONF_QUARANTINED_SERVER);
break;
case 'R' : case 'r' : /* usage */
send_usage(cptr, parv[0]);
break;
case 'S' : case 's' : /* S lines */
report_configured_links(cptr, parv[0], CONF_SERVICE);
break;
case 'T' : case 't' : /* various statistics */
tstats(cptr, parv[0]);
break;
case 'U' : case 'u' : /* uptime */
{
register time_t now;
now = timeofday - me.since;
sendto_one(sptr, rpl_str(RPL_STATSUPTIME, parv[0]),
now/86400, (now/3600)%24, (now/60)%60, now%60);
break;
}
case 'V' : case 'v' : /* V conf lines */
report_configured_links(cptr, parv[0], CONF_VER);
break;
case 'X' : case 'x' : /* lists */
#ifdef DEBUGMODE
send_listinfo(cptr, parv[0]);
#endif
break;
case 'Y' : case 'y' : /* Y lines */
report_classes(cptr, parv[0]);
break;
case 'Z' : /* memory use (OPER only) */
if (MyOper(sptr))
count_memory(cptr, parv[0], 1);
else
sendto_one(sptr, err_str(ERR_NOPRIVILEGES, parv[0]));
break;
case 'z' : /* memory use */
count_memory(cptr, parv[0], 0);
break;
default :
stat = '*';
break;
}
sendto_one(cptr, rpl_str(RPL_ENDOFSTATS, parv[0]), stat);
return 2;
}
/*
** 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 (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, parv[0]),
"open", UTMP);
return 1;
}
sendto_one(sptr, rpl_str(RPL_USERSSTART, parv[0]));
while (utmp_read(fd, namebuf, linebuf,
hostbuf, sizeof(hostbuf)) == 0)
{
flag = 1;
sendto_one(sptr, rpl_str(RPL_USERS, parv[0]),
namebuf, linebuf, hostbuf);
}
if (flag == 0)
sendto_one(sptr, rpl_str(RPL_NOUSERS, parv[0]));
sendto_one(sptr, rpl_str(RPL_ENDOFUSERS, parv[0]));
(void)utmp_close(fd);
#else
sendto_one(sptr, err_str(ERR_USERSDISABLED, parv[0]));
#endif
}
else
return 3;
return 2;
}
/*
** 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[];
{
Reg 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) || IsUnknown(cptr) || IsService(cptr))
return 2;
if (cptr == sptr)
sendto_flag(SCH_ERROR, "from %s -- %s",
get_client_name(cptr, FALSE), para);
else
sendto_flag(SCH_ERROR, "from %s via %s -- %s",
sptr->name, get_client_name(cptr,FALSE), para);
return 2;
}
/*
** 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, parv[0], msgtab[i].cmd);
return 2;
}
/*
* 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, /* server */
c_count = 0, /* client (visible) */
u_count = 0, /* unknown */
i_count = 0, /* invisible client */
o_count = 0, /* oparator */
v_count = 0; /* service */
int m_client = 0, /* my clients */
m_server = 0, /* my server links */
m_service = 0; /* my services */
aClient *acptr;
if (parc > 2)
if (hunt_server(cptr, sptr, ":%s LUSERS %s :%s", 2, parc, parv)
!= HUNTED_ISME)
return 3;
if (parc == 1 || !MyConnect(sptr))
{
sendto_one(sptr, rpl_str(RPL_LUSERCLIENT, parv[0]),
istat.is_user[0] + istat.is_user[1],
istat.is_service, istat.is_serv);
if (istat.is_oper)
sendto_one(sptr, rpl_str(RPL_LUSEROP, parv[0]),
istat.is_oper);
if (istat.is_unknown > 0)
sendto_one(sptr, rpl_str(RPL_LUSERUNKNOWN, parv[0]),
istat.is_unknown);
if (istat.is_chan)
sendto_one(sptr, rpl_str(RPL_LUSERCHANNELS, parv[0]),
istat.is_chan);
sendto_one(sptr, rpl_str(RPL_LUSERME, parv[0]),
istat.is_myclnt, istat.is_myservice,
istat.is_myserv);
#ifdef EXTRA_STATISTICS
sendto_one(sptr, rpl_str(RPL_LOCALUSERS, parv[0]),
istat.is_myclnt, istat.is_m_myclnt);
sendto_one(sptr, rpl_str(RPL_GLOBALUSERS, parv[0]),
istat.is_user[1] + istat.is_user[0],
istat.is_m_users);
#endif
return 2;
}
(void)collapse(parv[1]);
for (acptr = client; acptr; acptr = acptr->next)
{
if (!IsServer(acptr) && acptr->user)
{
if (match(parv[1], acptr->user->server))
continue;
}
else
if (match(parv[1], acptr->name))
continue;
switch (acptr->status)
{
case STAT_SERVER:
if (MyConnect(acptr))
m_server++;
/* flow thru */
case STAT_ME:
s_count++;
break;
case STAT_SERVICE:
if (MyConnect(acptr))
m_service++;
v_count++;
break;
case STAT_CLIENT:
if (IsOper(acptr))
o_count++;
#ifdef SHOW_INVISIBLE_LUSERS
if (MyConnect(acptr))
m_client++;
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;
default:
u_count++;
break;
}
}
#ifndef SHOW_INVISIBLE_LUSERS
if (IsAnOper(sptr) && i_count)
#endif
sendto_one(sptr, rpl_str(RPL_LUSERCLIENT, parv[0]),
c_count + i_count, v_count, s_count);
#ifndef SHOW_INVISIBLE_LUSERS
else
sendto_one(sptr, rpl_str(RPL_LUSERCLIENT, parv[0]),
c_count, v_count, s_count);
#endif
if (o_count)
sendto_one(sptr, rpl_str(RPL_LUSEROP, parv[0]), o_count);
if (u_count > 0)
sendto_one(sptr, rpl_str(RPL_LUSERUNKNOWN, parv[0]), u_count);
if ((c_count = count_channels(sptr))>0)
sendto_one(sptr, rpl_str(RPL_LUSERCHANNELS, parv[0]),
count_channels(sptr));
sendto_one(sptr, rpl_str(RPL_LUSERME, parv[0]), m_client, m_service,
m_server);
#if 0 /* it sends local values which is probably not that you wanted -erra */
/* #ifdef EXTRA_STATISTICS */
sendto_one(sptr, rpl_str(RPL_LOCALUSERS, parv[0]),
istat.is_myclnt, istat.is_m_myclnt);
sendto_one(sptr, rpl_str(RPL_GLOBALUSERS, parv[0]),
c_count + i_count, istat.is_m_users);
#endif
return 2;
}
/***********************************************************************
* 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;
aClient *acptr;
if (parc > 3 && IsLocOp(sptr))
{
sendto_one(sptr, err_str(ERR_NOPRIVILEGES, parv[0]));
return 1;
}
if (hunt_server(cptr,sptr,":%s CONNECT %s %s :%s",
3,parc,parv) != HUNTED_ISME)
return 1;
if (parc < 3 || *parv[1] == '\0')
{
sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS, parv[0]),
"CONNECT");
return 0;
}
if ((acptr = find_name(parv[1], NULL))
|| (acptr = find_mask(parv[1], NULL)))
{
sendto_one(sptr, ":%s NOTICE %s :Connect: Server %s %s %s.",
ME, 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 ||
aconf->status == CONF_ZCONNECT_SERVER) &&
match(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 ||
aconf->status == CONF_ZCONNECT_SERVER) &&
(match(parv[1], aconf->host) == 0 ||
match(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 ((port = atoi(parv[2])) <= 0)
{
sendto_one(sptr, "NOTICE %s :Connect: Illegal port number",
parv[0]);
return 0;
}
else if (port <= 0)
{
sendto_one(sptr, ":%s NOTICE %s :Connect: missing port number",
ME, parv[0]);
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, 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, parv[0], aconf->host, aconf->name);
sendto_flag(SCH_NOTICE, "Connecting to %s[%s] by %s",
aconf->host, aconf->name,
get_client_name(sptr, FALSE));
break;
case -1:
sendto_one(sptr, ":%s NOTICE %s :*** Couldn't connect to %s.",
ME, parv[0], aconf->host);
sendto_flag(SCH_NOTICE, "Couldn't connect to %s by %s",
aconf->host, get_client_name(sptr, FALSE));
break;
case -2:
sendto_one(sptr, ":%s NOTICE %s :*** Host %s is unknown.",
ME, parv[0], aconf->host);
sendto_flag(SCH_NOTICE, "Connect by %s to unknown host %s",
get_client_name(sptr, FALSE), aconf->host);
break;
default:
sendto_one(sptr,
":%s NOTICE %s :*** Connection to %s failed: %s",
ME, parv[0], aconf->host, strerror(retval));
sendto_flag(SCH_NOTICE, "Connection to %s by %s failed: %s",
aconf->host, get_client_name(sptr, FALSE),
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, *pv[4];
message = parc > 1 ? parv[1] : NULL;
if (BadPtr(message))
{
sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS, parv[0]),
"WALLOPS");
return 1;
}
if (!IsServer(sptr))
{
pv[0] = parv[0];
pv[1] = "+wallops";
pv[2] = message;
pv[3] = NULL;
return m_private(cptr, sptr, 3, pv);
}
sendto_ops_butone(IsServer(cptr) ? cptr : NULL, sptr,
":%s WALLOPS :%s", parv[0], message);
#ifdef USE_SERVICES
check_services_butone(SERVICE_WANT_WALLOP, NULL, sptr,
":%s WALLOP :%s", parv[0], message);
#endif
return 2;
}
/*
** m_time
** parv[0] = sender prefix
** parv[1] = servername
*/
int m_time(cptr, sptr, parc, parv)
aClient *cptr, *sptr;
int parc;
char *parv[];
{
if (hunt_server(cptr,sptr,":%s TIME :%s",1,parc,parv) == HUNTED_ISME)
sendto_one(sptr, rpl_str(RPL_TIME, parv[0]), ME, date((long)0));
return 2;
}
/*
** 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;
if (IsRegistered(cptr) && /* only local query for unregistered */
hunt_server(cptr,sptr,":%s ADMIN :%s",1,parc,parv) != HUNTED_ISME)
return 3;
if ((aconf = find_admin()) && aconf->host && aconf->passwd
&& aconf->name)
{
sendto_one(sptr, rpl_str(RPL_ADMINME, parv[0]), ME);
sendto_one(sptr, rpl_str(RPL_ADMINLOC1, parv[0]), aconf->host);
sendto_one(sptr, rpl_str(RPL_ADMINLOC2, parv[0]),
aconf->passwd);
sendto_one(sptr, rpl_str(RPL_ADMINEMAIL, parv[0]),
aconf->name);
}
else
sendto_one(sptr, err_str(ERR_NOADMININFO, parv[0]), ME);
return 2;
}
#if defined(OPER_REHASH) || defined(LOCOP_REHASH)
/*
** m_rehash
**
*/
int m_rehash(cptr, sptr, parc, parv)
aClient *cptr, *sptr;
int parc;
char *parv[];
{
sendto_one(sptr, rpl_str(RPL_REHASHING, parv[0]),
mybasename(configfile));
sendto_flag(SCH_NOTICE,
"%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[];
{
Reg aClient *acptr;
Reg int i;
char killer[HOSTLEN * 2 + USERLEN + 5];
#ifdef RUSNET_IRCD
strcpy(killer, get_client_xname(sptr, FALSE));
SPRINTF(buf, "RESTART by %s", get_client_xname(sptr, FALSE));
#else
strcpy(killer, get_client_name(sptr, FALSE));
SPRINTF(buf, "RESTART by %s", get_client_name(sptr, FALSE));
#endif
for (i = 0; i <= highest_fd; i++)
{
if (!(acptr = local[i]))
continue;
if (IsClient(acptr) || IsService(acptr))
{
sendto_one(acptr,
":%s NOTICE %s :Server Restarting. %s",
ME, acptr->name, killer);
acptr->exitc = EXITC_DIE;
if (IsClient(acptr))
exit_client(acptr, acptr, &me,
"Server Restarting");
/* services are kept for logging purposes */
}
else if (IsServer(acptr))
sendto_one(acptr, ":%s ERROR :Restarted by %s",
ME, killer);
}
flush_connections(me.fd);
server_reboot(buf);
/*NOT REACHED*/
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[];
{
Reg int i;
Reg aClient *acptr;
aClass *cltmp;
char *tname;
int doall, link_s[MAXCONNECTIONS], link_u[MAXCONNECTIONS];
int wilds, dow;
if (parc > 1)
tname = parv[1];
else
tname = ME;
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, parv[1]);
sendto_one(sptr, rpl_str(RPL_TRACELINK, parv[0]),
version, debugmode, tname, ac2ptr->from->name,
ac2ptr->from->serv->version,
(ac2ptr->from->flags & FLAGS_ZIP) ? "z" : "",
timeofday - ac2ptr->from->firsttime,
(int)DBufLength(&ac2ptr->from->sendQ),
(int)DBufLength(&sptr->from->sendQ));
return 5;
}
case HUNTED_ISME:
break;
default:
return 1;
}
doall = (parv[1] && (parc > 1)) ? !match(tname, ME): TRUE;
wilds = !parv[1] || index(tname, '*') || index(tname, '?');
dow = wilds || doall;
if (doall) {
for (i = 0; i < MAXCONNECTIONS; i++)
link_s[i] = 0, link_u[i] = 0;
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 (IsPerson(acptr) && IsInvisible(acptr) && dow &&
!(MyConnect(sptr) && IsAnOper(sptr)) &&
!IsAnOper(acptr) && (acptr != sptr))
continue;
if (!doall && wilds && match(tname, acptr->name))
continue;
if (!dow && strcasecmp(tname, acptr->name))
continue;
#ifdef RUSNET_IRCD
name = get_client_xname(acptr, IsAnOper(sptr));
#else
name = get_client_name(acptr,FALSE);
#endif
class = get_client_class(acptr);
switch(acptr->status)
{
case STAT_CONNECTING:
sendto_one(sptr, rpl_str(RPL_TRACECONNECTING,
parv[0]), class, name);
break;
case STAT_HANDSHAKE:
sendto_one(sptr, rpl_str(RPL_TRACEHANDSHAKE, parv[0]),
class, name);
break;
case STAT_ME:
break;
case STAT_UNKNOWN:
if (IsAnOper(sptr) || (MyPerson(sptr) && SendWallops(sptr)))
sendto_one(sptr,
rpl_str(RPL_TRACEUNKNOWN, parv[0]),
class, name);
break;
case STAT_CLIENT:
/* Only opers see users if there is a wildcard
* but anyone can see all the opers.
*/
if (IsAnOper(acptr))
sendto_one(sptr,
rpl_str(RPL_TRACEOPERATOR, parv[0]),
class, name);
else if (!dow || (MyConnect(sptr) && IsAnOper(sptr)))
sendto_one(sptr,
rpl_str(RPL_TRACEUSER, parv[0]),
class, name);
break;
case STAT_SERVER:
if (acptr->serv->user)
sendto_one(sptr, rpl_str(RPL_TRACESERVER,
parv[0]), class, link_s[i],
link_u[i], name, acptr->serv->by,
acptr->serv->user->username,
acptr->serv->user->host,
acptr->serv->version,
(acptr->flags & FLAGS_ZIP) ?"z":"");
else
sendto_one(sptr, rpl_str(RPL_TRACESERVER,
parv[0]), class, link_s[i],
link_u[i], name,
*(acptr->serv->by) ?
acptr->serv->by : "*", "*", ME,
acptr->serv->version,
(acptr->flags & FLAGS_ZIP) ?"z":"");
break;
case STAT_RECONNECT:
sendto_one(sptr, rpl_str(RPL_TRACERECONNECT, parv[0]),
class, name);
break;
case STAT_SERVICE:
sendto_one(sptr, rpl_str(RPL_TRACESERVICE, parv[0]),
class, name, acptr->service->type,
acptr->service->wants);
break;
case STAT_LOG:
sendto_one(sptr, rpl_str(RPL_TRACELOG, parv[0]),
ME, acptr->port);
break;
default: /* ...we actually shouldn't come here... --msa */
sendto_one(sptr, rpl_str(RPL_TRACENEWTYPE, parv[0]),
name);
break;
}
}
/*
* Add these lines to summarize the above which can get rather long
* and messy when done remotely - Avalon
*/
if (IsPerson(sptr) && SendWallops(sptr))
for (cltmp = FirstClass(); doall && cltmp; cltmp = NextClass(cltmp))
if (Links(cltmp) > 0)
sendto_one(sptr, rpl_str(RPL_TRACECLASS, parv[0]),
Class(cltmp), Links(cltmp));
sendto_one(sptr, rpl_str(RPL_TRACEEND, parv[0]), tname, version,
debugmode);
return 2;
}
/*
** m_motd
** parv[0] = sender prefix
** parv[1] = servername
*/
int m_motd(cptr, sptr, parc, parv)
aClient *cptr, *sptr;
int parc;
char *parv[];
{
#ifdef CACHED_MOTD
register aMotd *temp;
struct tm *tm;
#else
int fd;
char line[512], *head, *tail;
Reg char *tmp;
struct stat sb;
struct tm *tm;
#endif
if (check_link(cptr))
{
sendto_one(sptr, rpl_str(RPL_TRYAGAIN, parv[0]), "MOTD");
return 5;
}
if (hunt_server(cptr, sptr, ":%s MOTD :%s", 1,parc,parv)!=HUNTED_ISME)
return 5;
#ifdef CACHED_MOTD
tm = &motd_tm;
if (motd == NULL)
{
sendto_one(sptr, err_str(ERR_NOMOTD, parv[0]));
return 1;
}
sendto_one(sptr, rpl_str(RPL_MOTDSTART, parv[0]), ME);
sendto_one(sptr, ":%s %d %s :- %d/%d/%d %d:%02d", ME, RPL_MOTD,
parv[0], tm->tm_mday, tm->tm_mon + 1, 1900 + tm->tm_year,
tm->tm_hour, tm->tm_min);
temp = motd;
for(temp=motd;temp != NULL;temp = temp->next)
sendto_one(sptr, rpl_str(RPL_MOTD, parv[0]), temp->line);
sendto_one(sptr, rpl_str(RPL_ENDOFMOTD, parv[0]));
return 2;
#else
/*
* stop NFS hangs...most systems should be able to open a file in
* 3 seconds. -avalon (curtesy of wumpus)
*/
(void)alarm(3);
fd = open(IRCDMOTD_PATH, O_RDONLY);
(void)alarm(0);
if (fd == -1)
{
sendto_one(sptr, err_str(ERR_NOMOTD, parv[0]));
return 1;
}
(void)fstat(fd, &sb);
sendto_one(sptr, rpl_str(RPL_MOTDSTART, parv[0]), ME);
tm = localtime(&sb.st_mtime);
sendto_one(sptr, ":%s %d %s :- %d/%d/%d %d:%02d", ME, RPL_MOTD,
parv[0], tm->tm_mday, tm->tm_mon + 1, 1900 + tm->tm_year,
tm->tm_hour, tm->tm_min);
(void)dgets(-1, line, 0, &head, &tail); /* initialize line */
while (dgets(fd, line, sizeof(line)-1, &head, &tail) > 0)
{
if ((tmp = (char *)index(line,'\n')))
*tmp = '\0';
if ((tmp = (char *)index(line,'\r')))
*tmp = '\0';
sendto_one(sptr, rpl_str(RPL_MOTD, parv[0]), line);
}
sendto_one(sptr, rpl_str(RPL_ENDOFMOTD, parv[0]));
(void)close(fd);
return 2;
#endif /* CACHED_MOTD */
}
/*
** m_close - added by Darren Reed Jul 13 1992.
*/
int m_close(cptr, sptr, parc, parv)
aClient *cptr, *sptr;
int parc;
char *parv[];
{
Reg aClient *acptr;
Reg int i;
int closed = 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, parv[0]),
get_client_name(acptr, TRUE), acptr->status);
(void)exit_client(acptr, acptr, &me, "Oper Closing");
closed++;
}
sendto_one(sptr, rpl_str(RPL_CLOSEEND, parv[0]), closed);
return 1;
}
#if defined(OPER_DIE) || defined(LOCOP_DIE)
int m_die(cptr, sptr, parc, parv)
aClient *cptr, *sptr;
int parc;
char *parv[];
{
Reg aClient *acptr;
Reg int i;
char killer[HOSTLEN * 2 + USERLEN + 5];
strcpy(killer, get_client_name(sptr, TRUE));
for (i = 0; i <= highest_fd; i++)
{
if (!(acptr = local[i]))
continue;
if (IsClient(acptr) || IsService(acptr))
{
sendto_one(acptr,
":%s NOTICE %s :Server Terminating. %s",
ME, acptr->name, killer);
acptr->exitc = EXITC_DIE;
if (IsClient(acptr))
exit_client(acptr, acptr, &me, "Server died");
/* services are kept for logging purposes */
}
else if (IsServer(acptr))
sendto_one(acptr, ":%s ERROR :Terminated by %s",
ME, killer);
}
flush_connections(me.fd);
(void)s_die(0);
return 0;
}
#endif
/*
** storing server names in User structures is a real waste,
** the following functions change it to only store a pointer.
** A better way might be to store in Server structure and use servp. -krys
*/
static char **server_name = NULL;
static int server_max = 0, server_num = 0;
/*
** find_server_string
**
** Given an index, this will return a pointer to the corresponding
** (already allocated) string
*/
char *
find_server_string(snum)
int snum;
{
if (snum < server_num && snum >= 0)
return server_name[snum];
/* request for a bogus snum value, something is wrong */
sendto_flag(SCH_ERROR, "invalid index for server_name[] : %d (%d,%d)",
snum, server_num, server_max);
return NULL;
}
/*
** find_server_num
**
** Given a server name, this will return the index of the corresponding
** string. This index can be used with find_server_name_from_num().
** If the string doesn't exist already, it will be allocated.
*/
int
find_server_num(sname)
char *sname;
{
Reg int i = 0;
while (i < server_num)
{
if (!strcasecmp(server_name[i], sname))
break;
i++;
}
if (i < server_num)
return i;
if (i == server_max)
{
/* server_name[] array is full, let's make it bigger! */
if (server_name)
server_name = (char **) MyRealloc((char *)server_name,
sizeof(char *)*(server_max+=50));
else
server_name = (char **) MyMalloc(sizeof(char *)*(server_max=50));
}
server_name[server_num] = mystrdup(sname);
return server_num++;
}
/*
** check_link (added 97/12 to prevent abuse)
** routine which tries to find out how healthy a link is.
** useful to know if more strain may be imposed on the link or not.
**
** returns 0 if link load is light, -1 otherwise.
*/
static int
check_link(cptr)
aClient *cptr;
{
if (!IsServer(cptr))
return 0;
if (!(bootopt & BOOT_PROT))
return 0;
ircstp->is_ckl++;
if ((int)DBufLength(&cptr->sendQ) > 65536) /* SendQ is already (too) high*/
{
cptr->serv->lastload = timeofday;
ircstp->is_cklQ++;
return -1;
}
if (timeofday - cptr->firsttime < 60) /* link is too young */
{
ircstp->is_ckly++;
return -1;
}
if (timeofday - cptr->serv->lastload > 30)
/* last request more than 30 seconds ago => OK */
{
cptr->serv->lastload = timeofday;
ircstp->is_cklok++;
return 0;
}
if (timeofday - cptr->serv->lastload > 15
&& (int)DBufLength(&cptr->sendQ) < CHREPLLEN)
/* last request between 15 and 30 seconds ago, but little SendQ */
{
cptr->serv->lastload = timeofday;
ircstp->is_cklq++;
return 0;
}
ircstp->is_cklno++;
return -1;
}
syntax highlighted by Code2HTML, v. 0.9.1