aboutsummaryrefslogtreecommitdiff
path: root/ircd/s_serv.c
diff options
context:
space:
mode:
Diffstat (limited to 'ircd/s_serv.c')
-rw-r--r--ircd/s_serv.c2489
1 files changed, 2489 insertions, 0 deletions
diff --git a/ircd/s_serv.c b/ircd/s_serv.c
new file mode 100644
index 0000000..67c857d
--- /dev/null
+++ b/ircd/s_serv.c
@@ -0,0 +1,2489 @@
+/************************************************************************
+ * 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.65 1999/07/02 16:49:37 kalt 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 (!mycmp(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*/
+ 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));
+ 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++;
+ 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 = mycmp(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
+ /* use first two chars of the password they send in as salt */
+
+ /* passwd may be NULL. Head it off at the pass... */
+ if (*cptr->passwd)
+ {
+ char salt[3];
+ extern char *crypt();
+
+ /* Determine if MD5 or DES */
+ if (strncmp(aconf->passwd, "$1$", 3))
+ {
+ salt[0] = aconf->passwd[0];
+ salt[1] = aconf->passwd[1];
+ }
+ else
+ {
+ salt[0] = aconf->passwd[3];
+ salt[1] = aconf->passwd[4];
+ }
+ 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_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])
+#ifndef ZIP_LINKS
+ sendto_one(cptr, "PASS %s %s IRC|%s %s", bconf->passwd,
+ pass_version, serveropts,
+ (bootopt & BOOT_STRICTPROT) ? "P" : "");
+#else
+ sendto_one(cptr, "PASS %s %s IRC|%s %s%s",
+ bconf->passwd, pass_version, serveropts,
+ (bconf->status == CONF_ZCONNECT_SERVER) ? "Z" : "",
+ (bootopt & BOOT_STRICTPROT) ? "P" : "");
+#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++;
+ 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;
+ 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);
+#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) &&
+ mycmp(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,
+ acptr->user->host, 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)) || mycmp(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!
+*/
+
+static int report_array[17][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'},
+ { 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;
+
+ 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 lines the passwd contents can be
+ * displayed on STATS reply. -Vesa
+ */
+ if (tmp->status == CONF_KILL
+ || tmp->status == CONF_OTHERKILL
+ || tmp->status == CONF_VER)
+ sendto_one(sptr, rpl_str(p[1], to), c, host,
+ (pass) ? pass : null,
+ name, port, get_conf_class(tmp));
+ else
+ sendto_one(sptr, rpl_str(p[1], to), c, host,
+ (pass) ? "*" : null,
+ name, port, get_conf_class(tmp));
+ }
+ 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 (mycmp(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 doall = 0, wilds = 0;
+ char *name = NULL, *cm = NULL;
+
+ if (IsServer(cptr) &&
+ (stat != 'd' && stat != 'p' && stat != 'q' && stat != 's' &&
+ stat != 'u' && stat != 'v') &&
+ !(stat == 'o' && 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 == 4)
+ {
+ if (hunt_server(cptr, sptr, ":%s STATS %s %s %s",
+ 2, parc, parv) != HUNTED_ISME)
+ return 5;
+ }
+
+ if (parc > 2)
+ {
+ name = parv[2];
+ if (!mycmp(name, ME))
+ doall = 2;
+ else if (match(name, ME) == 0)
+ doall = 1;
+ if (index(name, '*') || index(name, '?'))
+ wilds = 1;
+ if (parc > 3)
+ {
+ cm = parv[3];
+ if (!index(cm, '*') && !index(cm, '?'))
+ wilds = 0, doall = 0;
+ }
+ }
+ else
+ name = ME;
+
+ 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.
+ */
+ for (i = 0; i <= highest_fd; i++)
+ {
+ if (!(acptr = local[i]))
+ continue;
+#if 0
+ if (IsPerson(acptr) && IsInvisible(acptr) &&
+ (doall || wilds) && !(MyConnect(sptr) &&
+ IsLocal(sptr) && IsOper(sptr)) &&
+ !IsAnOper(acptr) && acptr != sptr)
+#endif
+ if (IsPerson(acptr) &&
+ (doall || wilds) &&
+ !(MyConnect(sptr) && IsAnOper(sptr)) &&
+ acptr != sptr)
+ continue;
+ if (!doall && wilds && match(name, acptr->name))
+ continue;
+ if (!(doall || wilds) &&
+ ((!cm && mycmp(name, acptr->name)) ||
+ (cm && 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);
+ }
+ 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;
+ 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));
+ 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);
+ 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);
+ 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 irc.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];
+
+ 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 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);
+
+ SPRINTF(buf, "RESTART by %s", get_client_name(sptr, TRUE));
+ restart(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 && mycmp(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,
+ 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) || MyClient(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 (IsOper(sptr) &&
+ (MyClient(sptr) || !(dow && IsInvisible(acptr)))
+ || !dow || IsAnOper(acptr))
+ {
+ if (IsOper(sptr) && !(dow || IsInvisible(acptr)) ||
+ (IsOper(sptr) && IsLocal(sptr)) ||
+ !dow || IsAnOper(acptr))
+*/
+ 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);
+/*
+ {
+ if (IsAnOper(acptr))
+ sendto_one(sptr,
+ rpl_str(RPL_TRACEOPERATOR,
+ parv[0]), class, name);
+ else
+ 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[80];
+ 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, NULL, 0); /* make sure buffer is at empty pos */
+ while (dgets(fd, line, sizeof(line)-1) > 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);
+ }
+ (void)dgets(-1, NULL, 0); /* make sure buffer is at empty pos */
+ 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;
+}