-#include "crule.h"
-#include "version.h"
-#include "support.h"
-#include "s_serv.h"
-#include "hash.h"
-
-RCSTAG_CC("$Id$");
-
-/*
- * m_squit
- *
- * parv[0] = sender prefix
- * parv[1] = server name
- * parv[2] = timestamp
- * parv[parc-1] = comment
- */
-int m_squit(aClient *cptr, aClient *sptr, int parc, char *parv[])
-{
- Reg1 aConfItem *aconf;
- char *server;
- Reg2 aClient *acptr;
- char *comment = (parc > ((!IsServer(cptr)) ? 2 : 3) &&
- !BadPtr(parv[parc - 1])) ? parv[parc - 1] : cptr->name;
-
- if (!IsPrivileged(sptr))
- {
- sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
- return 0;
- }
-
- if (parc > (IsServer(cptr) ? 2 : 1))
- {
- server = parv[1];
- /*
- * To accomodate host masking, a squit for a masked server
- * name is expanded if the incoming mask is the same as
- * the server name for that link to the name of link.
- */
- if ((*server == '*') && IsServer(cptr) && (aconf = cptr->serv->nline) &&
- !strCasediff(server, my_name_for_link(me.name, aconf)))
- {
- server = cptr->name;
- acptr = cptr;
- }
- else
- {
- /*
- * The following allows wild cards in SQUIT. Only usefull
- * when the command is issued by an oper.
- */
- for (acptr = client; (acptr = next_client(acptr, server));
- acptr = acptr->next)
- if (IsServer(acptr) || IsMe(acptr))
- break;
-
- if (acptr)
- {
- if (IsMe(acptr))
- {
- if (IsServer(cptr))
- {
- acptr = cptr;
- server = cptr->sockhost;
- }
- else
- acptr = NULL;
- }
- else
- {
- /*
- * Look for a matching server that is closer,
- * that way we won't accidently squit two close
- * servers like davis.* and davis-r.* when typing
- * /SQUIT davis*
- */
- aClient *acptr2;
- for (acptr2 = acptr->serv->up; acptr2 != &me;
- acptr2 = acptr2->serv->up)
- if (!match(server, acptr2->name))
- acptr = acptr2;
- }
- }
- }
- /* If atoi(parv[2]) == 0 we must indeed squit !
- * It wil be our neighbour.
- */
- if (acptr && IsServer(cptr) &&
- atoi(parv[2]) && atoi(parv[2]) != acptr->serv->timestamp)
- {
- Debug((DEBUG_NOTICE, "Ignoring SQUIT with wrong timestamp"));
- return 0;
- }
- }
- else
- {
- sendto_one(cptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "SQUIT");
- if (IsServer(cptr))
- {
- /*
- * This is actually protocol error. But, well, closing
- * the link is very proper answer to that...
- */
- server = cptr->sockhost;
- acptr = cptr;
- }
- else
- return 0;
- }
- if (!acptr)
- {
- if (IsUser(sptr))
- sendto_one(sptr, err_str(ERR_NOSUCHSERVER), me.name, parv[0], server);
- return 0;
- }
- if (IsLocOp(sptr) && !MyConnect(acptr))
- {
- sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
- return 0;
- }
-
- return exit_client(cptr, acptr, sptr, comment);
-}
-
-/*
- * 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 unsigned int report_array[17][3] = {
- {CONF_CONNECT_SERVER, RPL_STATSCLINE, 'C'},
- {CONF_NOCONNECT_SERVER, RPL_STATSNLINE, 'N'},
- {CONF_CLIENT, RPL_STATSILINE, 'I'},
- {CONF_KILL, RPL_STATSKLINE, 'K'},
- {CONF_IPKILL, RPL_STATSKLINE, 'k'},
- {CONF_LEAF, RPL_STATSLLINE, 'L'},
- {CONF_OPERATOR, RPL_STATSOLINE, 'O'},
- {CONF_HUB, RPL_STATSHLINE, 'H'},
- {CONF_LOCOP, RPL_STATSOLINE, 'o'},
- {CONF_CRULEALL, RPL_STATSDLINE, 'D'},
- {CONF_CRULEAUTO, RPL_STATSDLINE, 'd'},
- {CONF_UWORLD, RPL_STATSULINE, 'U'},
- {CONF_TLINES, RPL_STATSTLINE, 'T'},
- {CONF_LISTEN_PORT, RPL_STATSPLINE, 'P'},
- {0, 0}
-};
-
-static void report_configured_links(aClient *sptr, int mask)
-{
- static char null[] = "<NULL>";
- aConfItem *tmp;
- unsigned int *p;
- unsigned short int port;
- char c, *host, *pass, *name;
-
- for (tmp = conf; tmp; tmp = tmp->next)
- if ((tmp->status & mask))
- {
- for (p = &report_array[0][0]; *p; p += 3)
- if (*p == tmp->status)
- break;
- if (!*p)
- continue;
- c = (char)*(p + 2);
- host = BadPtr(tmp->host) ? null : tmp->host;
- pass = BadPtr(tmp->passwd) ? null : tmp->passwd;
- name = BadPtr(tmp->name) ? null : tmp->name;
- port = tmp->port;
- /*
- * On K line the passwd contents can be
- * displayed on STATS reply. -Vesa
- */
- /* Special-case 'k' or 'K' lines as appropriate... -Kev */
- if ((tmp->status & CONF_KLINE))
- sendto_one(sptr, rpl_str(p[1]), me.name,
- sptr->name, c, host, pass, name, port, get_conf_class(tmp));
- /* connect rules are classless */
- else if ((tmp->status & CONF_CRULE))
- sendto_one(sptr, rpl_str(p[1]), me.name, sptr->name, c, host, name);
- else if ((tmp->status & CONF_TLINES))
- sendto_one(sptr, rpl_str(p[1]), me.name, sptr->name, c, host, pass);
- else if ((tmp->status & CONF_LISTEN_PORT))
- sendto_one(sptr, rpl_str(p[1]), me.name, sptr->name, c, port,
- tmp->clients, tmp->status);
- else if ((tmp->status & CONF_UWORLD))
- sendto_one(sptr, rpl_str(p[1]),
- me.name, sptr->name, c, host, pass, name, port,
- get_conf_class(tmp));
- else
- sendto_one(sptr, rpl_str(p[1]), me.name, sptr->name, c, host, name,
- port, get_conf_class(tmp));
- }
- return;
-}
-
-/*
- * m_stats
- *
- * parv[0] = sender prefix
- * parv[1] = statistics selector (defaults to Message frequency)
- * parv[2] = target server (current server defaulted, if omitted)
- * And 'stats l' and 'stats' L:
- * parv[3] = server mask ("*" defaulted, if omitted)
- * Or for stats p,P:
- * parv[3] = port mask (returns p-lines when its port is matched by this)
- * Or for stats k,K,i and I:
- * parv[3] = [user@]host.name (returns which K/I-lines match this)
- * or [user@]host.mask (returns which K/I-lines are mmatched by this)
- * (defaults to old reply if ommitted, when local or Oper)
- * A remote mask (something containing wildcards) is only
- * allowed for IRC Operators.
- * Or for stats M:
- * parv[3] = time param
- * parv[4] = time param
- * (see report_memleak_stats() in runmalloc.c for details)
- *
- * This function is getting really ugly. -Ghostwolf
- */
-int m_stats(aClient *cptr, aClient *sptr, int parc, char *parv[])
-{
- static char Sformat[] = ":%s %d %s Connection SendQ SendM SendKBytes "
- "RcveM RcveKBytes :Open since";
- static char Lformat[] = ":%s %d %s %s %u %u %u %u %u :" TIME_T_FMT;
- aMessage *mptr;
- aClient *acptr;
- aGline *agline, *a2gline;
- aConfItem *aconf;
- char stat = parc > 1 ? parv[1][0] : '\0';
- Reg1 int i;
-
-/* m_stats is so obnoxiously full of special cases that the different
- * hunt_server() possiblites were becoming very messy. It now uses a
- * switch() so as to be easier to read and update as params change.
- * -Ghostwolf
- */
- switch (stat)
- {
- /* open to all, standard # of params */
- case 'U':
- case 'u':
- {
- if (hunt_server(0, cptr, sptr, ":%s STATS %s :%s", 2, parc, parv)
- != HUNTED_ISME)
- return 0;
- break;
- }
-
- /* open to all, varying # of params */
- case 'k':
- case 'K':
- case 'i':
- case 'I':
- case 'p':
- case 'P':
- {
- if (parc > 3)
- {
- if (hunt_server(0, cptr, sptr, ":%s STATS %s %s :%s", 2, parc, parv)
- != HUNTED_ISME)
- return 0;
- }
- else
- {
- if (hunt_server(0, cptr, sptr, ":%s STATS %s :%s", 2, parc, parv)
- != HUNTED_ISME)
- return 0;
- }
- break;
- }
-
- /* oper only, varying # of params */
- case 'l':
- case 'L':
- case 'M':
- {
- if (parc == 4)
- {
- if (hunt_server(1, cptr, sptr, ":%s STATS %s %s :%s", 2, parc, parv)
- != HUNTED_ISME)
- return 0;
- }
- else if (parc > 4)
- {
- if (hunt_server(1, cptr, sptr, ":%s STATS %s %s %s :%s", 2, parc,
- parv) != HUNTED_ISME)
- return 0;
- }
- else if (hunt_server(1, cptr, sptr, ":%s STATS %s :%s", 2, parc, parv)
- != HUNTED_ISME)
- return 0;
- break;
- }
-
- /* oper only, standard # of params */
- default:
- {
- if (hunt_server(1, cptr, sptr, ":%s STATS %s :%s", 2, parc, parv)
- != HUNTED_ISME)
- return 0;
- break;
- }
- }
-
- switch (stat)
- {
- case 'L':
- case 'l':
- {
- int doall = 0, wilds = 0;
- char *name = "*";
- if (parc > 3 && *parv[3])
- {
- char *p;
- name = parv[3];
- wilds = (*name == '*' || *name == '?');
- for (p = name + 1; *p; ++p)
- if ((*p == '*' || *p == '?') && p[-1] != '\\')
- {
- wilds = 1;
- break;
- }
- }
- else
- doall = 1;
- /*
- * Send info about connections which match, or all if the
- * mask matches me.name. Only restrictions are on those who
- * are invisible not being visible to 'foreigners' who use
- * a wild card based search to list it.
- */
- sendto_one(sptr, Sformat, me.name, RPL_STATSLINKINFO, parv[0]);
- for (i = 0; i <= highest_fd; i++)
- {
- if (!(acptr = loc_clients[i]))
- continue;
- /* Don't return clients when this is a request for `all' */
- if (doall && IsUser(acptr))
- continue;
- /* Don't show invisible people to unauthorized people when using
- * wildcards -- Is this still needed now /stats is oper only ? */
- if (IsInvisible(acptr) && (doall || wilds) &&
- !(MyConnect(sptr) && IsOper(sptr)) &&
- !IsAnOper(acptr) && (acptr != sptr))
- continue;
- /* Only show the ones that match the given mask - if any */
- if (!doall && wilds && match(name, acptr->name))
- continue;
- /* Skip all that do not match the specific query */
- if (!(doall || wilds) && strCasediff(name, acptr->name))
- continue;
- sendto_one(sptr, Lformat, me.name, RPL_STATSLINKINFO, parv[0],
- (isUpper(stat)) ?
- get_client_name(acptr, TRUE) : get_client_name(acptr, FALSE),
- (int)DBufLength(&acptr->sendQ), (int)acptr->sendM,
- (int)acptr->sendK, (int)acptr->receiveM, (int)acptr->receiveK,
- time(NULL) - acptr->firsttime);
- }
- break;
- }
- case 'C':
- case 'c':
- report_configured_links(sptr,
- CONF_CONNECT_SERVER | CONF_NOCONNECT_SERVER);
- break;
- case 'G':
- case 'g':
- /* send glines */
- for (agline = gline, a2gline = NULL; agline; agline = agline->next)
- {
- if (agline->expire <= TStime())
- { /* handle expired glines */
- free_gline(agline, a2gline);
- agline = a2gline ? a2gline : gline; /* make sure to splice
- list together */
- if (!agline)
- break; /* last gline; break out of loop */
- continue; /* continue! */
- }
- sendto_one(sptr, rpl_str(RPL_STATSGLINE), me.name,
- sptr->name, 'G', agline->name, agline->host,
- agline->expire, agline->reason);
- a2gline = agline;
- }
- break;
- case 'H':
- case 'h':
- report_configured_links(sptr, CONF_HUB | CONF_LEAF);
- break;
- case 'I':
- case 'i':
- case 'K':
- case 'k': /* display CONF_IPKILL as well
- as CONF_KILL -Kev */
- {
- int wilds, count;
- char *user, *host, *p;
- int conf_status = (stat == 'k' || stat == 'K') ? CONF_KLINE : CONF_CLIENT;
- if ((MyUser(sptr) || IsOper(sptr)) && parc < 4)
- {
- report_configured_links(sptr, conf_status);
- break;
- }
- if (parc < 4 || *parv[3] == '\0')
- {
- sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0],
- (conf_status & CONF_KLINE) ? "STATS K" : "STATS I");
- return 0;
- }
- wilds = 0;
- for (p = parv[3]; *p; p++)
- {
- if (*p == '\\')
- {
- if (!*++p)
- break;
- continue;
- }
- if (*p == '?' || *p == '*')
- {
- wilds = 1;
- break;
- }
- }
- if (!(MyConnect(sptr) || IsOper(sptr)))
- {
- wilds = 0;
- count = 3;
- }
- else
- count = 1000;
-
- if (conf_status == CONF_CLIENT)
- {
- user = NULL; /* Not used, but to avoid compiler warning. */
-
- host = parv[3];
- }
- else
- {
- if ((host = strchr(parv[3], '@')))
- {
- user = parv[3];
- *host++ = 0;;
- }
- else
- {
- user = NULL;
- host = parv[3];
- }
- }
- for (aconf = conf; aconf; aconf = aconf->next)
- {
- if ((aconf->status & conf_status))
- {
- if (conf_status == CONF_KLINE)
- {
- if ((!wilds && ((user || aconf->host[1]) &&
- !match(aconf->host, host) &&
- (!user || !match(aconf->name, user)))) ||
- (wilds && !mmatch(host, aconf->host) &&
- (!user || !mmatch(user, aconf->name))))
- {
- sendto_one(sptr, rpl_str(RPL_STATSKLINE), me.name,
- sptr->name, 'K', aconf->host, aconf->passwd, aconf->name,
- aconf->port, get_conf_class(aconf));
- if (--count == 0)
- break;
- }
- }
- else if (conf_status == CONF_CLIENT)
- {
- if ((!wilds && (!match(aconf->host, host) ||
- !match(aconf->name, host))) ||
- (wilds && (!mmatch(host, aconf->host) ||
- !mmatch(host, aconf->name))))
- {
- sendto_one(sptr, rpl_str(RPL_STATSILINE), me.name,
- sptr->name, 'I', aconf->host, aconf->name,
- aconf->port, get_conf_class(aconf));
- if (--count == 0)
- break;
- }
- }
- }
- }
- break;
- }
- case 'M':
-#ifdef MEMSIZESTATS
- sendto_one(sptr, rpl_str(RPL_STATMEMTOT),
- me.name, parv[0], get_mem_size(), get_alloc_cnt());
-#endif
-#ifdef MEMLEAKSTATS
- report_memleak_stats(sptr, parc, parv);
-#endif
-#if !defined(MEMSIZESTATS) && !defined(MEMLEAKSTATS)
- sendto_one(sptr, ":%s NOTICE %s :stats M : Memory allocation monitoring "
- "is not enabled on this server", me.name, parv[0]);
-#endif
- break;
- case 'm':
- for (mptr = msgtab; mptr->cmd; mptr++)
- if (mptr->count)
- sendto_one(sptr, rpl_str(RPL_STATSCOMMANDS),
- me.name, parv[0], mptr->cmd, mptr->count, mptr->bytes);
- break;
- case 'o':
- case 'O':
- report_configured_links(sptr, CONF_OPS);
- break;
- case 'p':
- case 'P':
- {
- int count = 100;
- char port[6];
- if ((MyUser(sptr) || IsOper(sptr)) && parc < 4)
- {
- report_configured_links(sptr, CONF_LISTEN_PORT);
- break;
- }
- if (!(MyConnect(sptr) || IsOper(sptr)))
- count = 3;
- for (aconf = conf; aconf; aconf = aconf->next)
- if (aconf->status == CONF_LISTEN_PORT)
- {
- if (parc >= 4 && *parv[3] != '\0')
- {
- sprintf_irc(port, "%u", aconf->port);
- if (match(parv[3], port))
- continue;
- }
- sendto_one(sptr, rpl_str(RPL_STATSPLINE), me.name, sptr->name, 'P',
- aconf->port, aconf->clients, aconf->status);
- if (--count == 0)
- break;
- }
- break;
- }
- case 'R':
- case 'r':
-#ifdef DEBUGMODE
- send_usage(sptr, parv[0]);
-#endif
- break;
- case 'D':
- report_configured_links(sptr, CONF_CRULEALL);
- break;
- case 'd':
- report_configured_links(sptr, CONF_CRULE);
- break;
- case 't':
- tstats(sptr, parv[0]);
- break;
- case 'T':
- report_configured_links(sptr, CONF_TLINES);
- break;
- case 'U':
- report_configured_links(sptr, CONF_UWORLD);
- break;
- case 'u':
- {
- register time_t nowr;
-
- nowr = now - me.since;
- sendto_one(sptr, rpl_str(RPL_STATSUPTIME), me.name, parv[0],
- nowr / 86400, (nowr / 3600) % 24, (nowr / 60) % 60, nowr % 60);
- sendto_one(sptr, rpl_str(RPL_STATSCONN), me.name, parv[0],
- max_connection_count, max_client_count);
- break;
- }
- case 'W':
- case 'w':
- calc_load(sptr);
- break;
- case 'X':
- case 'x':
-#ifdef DEBUGMODE
- send_listinfo(sptr, parv[0]);
-#endif
- break;
- case 'Y':
- case 'y':
- report_classes(sptr);
- break;
- case 'Z':
- case 'z':
- count_memory(sptr, parv[0]);
- break;
- default:
- stat = '*';
- break;
- }
- sendto_one(sptr, rpl_str(RPL_ENDOFSTATS), me.name, parv[0], stat);
- return 0;
-}
-
-/*
- * m_connect - Added by Jto 11 Feb 1989
- *
- * parv[0] = sender prefix
- * parv[1] = servername
- * parv[2] = [IP-number:]port number
- * parv[3] = remote server
- */
-int m_connect(aClient *cptr, aClient *sptr, int parc, char *parv[])
-{
- int retval;
- unsigned short int port, tmpport;
- aConfItem *aconf, *cconf;
- aClient *acptr;
- char *p;
-
- if (!IsPrivileged(sptr))
- {
- sendto_one(sptr, err_str(ERR_NOPRIVILEGES), me.name, parv[0]);
- return -1;
- }
-
- if (IsLocOp(sptr) && parc > 3) /* Only allow LocOps to make */
- return 0; /* local CONNECTS --SRB */
-
- if (parc > 3 && MyUser(sptr))
- {
- aClient *acptr2, *acptr3;
- if (!(acptr3 = find_match_server(parv[3])))
- {
- sendto_one(sptr, err_str(ERR_NOSUCHSERVER), me.name, parv[0], parv[3]);
- return 0;
- }
-
- /* Look for closest matching server */
- for (acptr2 = acptr3; acptr2 != &me; acptr2 = acptr2->serv->up)
- if (!match(parv[3], acptr2->name))
- acptr3 = acptr2;
-
- parv[3] = acptr3->name;
- }
-
- if (hunt_server(1, cptr, sptr, ":%s CONNECT %s %s :%s", 3, parc, parv) !=
- HUNTED_ISME)
- return 0;
-
- if (parc < 2 || *parv[1] == '\0')
- {
- sendto_one(sptr, err_str(ERR_NEEDMOREPARAMS), me.name, parv[0], "CONNECT");
- return -1;
- }
-
- if ((acptr = FindServer(parv[1])))
- {
- if (MyUser(sptr) || Protocol(cptr) < 10)
- sendto_one(sptr, ":%s NOTICE %s :Connect: Server %s %s %s.",
- me.name, parv[0], parv[1], "already exists from", acptr->from->name);
- else
- sendto_one(sptr, "%s NOTICE %s%s :Connect: Server %s %s %s.",
- NumServ(&me), NumNick(sptr), parv[1], "already exists from",
- acptr->from->name);
- return 0;
- }
-
- if (parc > 2 && !BadPtr(parv[2]))
- p = strchr(parv[2], ':');
- else
- p = 0;
- if (p)
- *p = 0;
- for (aconf = conf; aconf; aconf = aconf->next)
- if (aconf->status == CONF_CONNECT_SERVER &&
- match(parv[1], aconf->name) == 0 &&
- (!p || match(parv[2], aconf->host) == 0 ||
- match(parv[2], strchr(aconf->host, '@') + 1) == 0))
- break;
- /* Checked first servernames, then try hostnames. */
- if (!aconf)
- for (aconf = conf; aconf; aconf = aconf->next)
- if (aconf->status == CONF_CONNECT_SERVER &&
- (match(parv[1], aconf->host) == 0 ||
- match(parv[1], strchr(aconf->host, '@') + 1) == 0))
- break;
- if (p)
- *p = ':';
-
- if (!aconf)
- {
- if (MyUser(sptr) || Protocol(cptr) < 10)
- sendto_one(sptr,
- ":%s NOTICE %s :Connect: Host %s not listed in ircd.conf",
- me.name, parv[0], parv[1]);
- else
- sendto_one(sptr,
- "%s NOTICE %s%s :Connect: Host %s not listed in ircd.conf",
- NumServ(&me), NumNick(sptr), parv[1]);
- return 0;
- }
- /*
- * Get port number from user, if given. If not specified,
- * use the default from configuration structure. If missing
- * from there, then use the precompiled default.
- */
- tmpport = port = aconf->port;
- if (parc > 2 && !BadPtr(parv[2]))
- {
- p = strchr(parv[2], ':');
- if (!p)
- p = parv[2];
- else
- p = p + 1;
- if ((port = atoi(p)) == 0)
- {
- if (MyUser(sptr) || Protocol(cptr) < 10)
- sendto_one(sptr,
- ":%s NOTICE %s :Connect: Invalid port number", me.name, parv[0]);
- else
- sendto_one(sptr, "%s NOTICE %s%s :Connect: Invalid port number",
- NumServ(&me), NumNick(sptr));
- return 0;
- }
- }
- else if (port == 0 && (port = PORTNUM) == 0)
- {
- if (MyUser(sptr) || Protocol(cptr) < 10)
- sendto_one(sptr, ":%s NOTICE %s :Connect: missing port number",
- me.name, parv[0]);
- else
- sendto_one(sptr, "%s NOTICE %s%s :Connect: missing port number",
- NumServ(&me), NumNick(sptr));
- return 0;
- }
-
- /*
- * Evaluate connection rules... If no rules found, allow the
- * connect. Otherwise stop with the first true rule (ie: rules
- * are ored together. Oper connects are effected only by D
- * lines (CRULEALL) not d lines (CRULEAUTO).
- */
- for (cconf = conf; cconf; cconf = cconf->next)
- if ((cconf->status == CONF_CRULEALL) &&
- (match(cconf->host, aconf->name) == 0))
- if (crule_eval(cconf->passwd))
- {
- if (MyUser(sptr) || Protocol(cptr) < 10)
- sendto_one(sptr, ":%s NOTICE %s :Connect: Disallowed by rule: %s",
- me.name, parv[0], cconf->name);
- else
- sendto_one(sptr, "%s NOTICE %s%s :Connect: Disallowed by rule: %s",
- NumServ(&me), NumNick(sptr), cconf->name);
- return 0;
- }