From 037ea5b9ceaec42f9000cd6d3c47e548fd472d31 Mon Sep 17 00:00:00 2001 From: "Kevin L. Mitchell" Date: Thu, 28 Jun 2001 21:40:57 +0000 Subject: [PATCH] Author: Kev Log message: This should be the last of the code required for u2.10.11 before release. Not the last of the documentation, unfortunately, but you can't have everything...Anyway, this implements restrictions on the G-line mask for ordinary operators to keep accidental *@* G-lines from being set. In particular, if the host mask contains any wildcards or if the G-line would impact 20 or more users (this number is configurable), then the G-line would have to be forced by doing !+*@host.mask.goes.here or whatever. This check is also smart enough to not allow *@*foo.com or *@127.0.0.1/15 bans, either. git-svn-id: file:///home/klmitch/undernet-ircu/undernet-ircu-svn/ircu2/trunk@521 c9e4aea6-c8fd-4c43-8297-357d70d61c8c --- ChangeLog | 56 +++++++++++++++ doc/example.conf | 3 + doc/readme.features | 24 +++++++ doc/readme.gline | 14 +++- include/client.h | 4 +- include/gline.h | 1 + include/ircd_features.h | 3 + include/numeric.h | 5 +- include/whocmds.h | 2 +- ircd/client.c | 4 +- ircd/gline.c | 147 ++++++++++++++++++++++++++++++++++------ ircd/ircd_features.c | 3 + ircd/m_gline.c | 22 +++++- ircd/s_err.c | 6 +- ircd/whocmds.c | 24 +++++++ 15 files changed, 286 insertions(+), 32 deletions(-) diff --git a/ChangeLog b/ChangeLog index 404db93..c4534ae 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,59 @@ +2001-06-28 Kevin L. Mitchell + + * ircd/whocmds.c (count_users): routine to count the number of + users matching a given user@host mask + + * ircd/s_err.c: add error messages for ERR_LONGMASK, + ERR_TOOMANYUSERS, and ERR_MASKTOOWIDE + + * ircd/m_gline.c: look for and advance past '!' flag on G-lines + from operators; only set GLINE_OPERFORCE flag if oper has the + PRIV_WIDE_GLINE privilege + + * ircd/ircd_features.c: add GLINEMAXUSERCOUNT, which is the + maximum number of users a G-line can impact before it has to be + forced; OPER_WIDE_GLINE, to allow operators to use ! to force a + wide G-line to be set; and LOCOP_WIDE_GLINE, to allow local + operators to use ! to force a wide G-line to be set + + * ircd/gline.c: make make_gline() be called with separate user and + host arguments, and not call canon_userhost() directly; implement + gline_checkmask() to verify that a host mask is acceptable; move + BADCHAN check up in gline_add(), and check passed-in mask under + certain circumstances for acceptability; fix call to + sendto_opmask_butone() to handle separation of userhost into user + and host in gline_add(); update call to make_gline() + + * ircd/client.c: use FEAT_OPER_WIDE_GLINE and + FEAT_LOCOP_WIDE_GLINE to set PRIV_WIDE_GLINE for an operator; add + PRIV_WIDE_GLINE to privtab[] for client_report_privs() + + * include/whocmds.h (count_users): declare routine to count users + matching a given user@host mask + + * include/numeric.h: added three new error returns: ERR_LONGMASK + -- mask can't be formatted into a buffer; ERR_TOOMANYUSERS -- too + many users would be impacted by the mask; ERR_MASKTOOWIDE -- mask + contains wildcards in the wrong places + + * include/ircd_features.h: add FEAT_GLINEMAXUSERCOUNT, + FEAT_OPER_WIDE_GLINE, and FEAT_LOCOP_WIDE_GLINE + + * include/gline.h (GLINE_OPERFORCE): provides a way for m_gline() + to signal to gline_add() that the operator attempted to force the + G-line to be set + + * include/client.h (PRIV_WIDE_GLINE): new privilege for operators + + * doc/readme.gline: update to document new "!" prefix to a G-line + user@host mask + + * doc/readme.features: document GLINEMAXUSERCOUNT, + OPER_WIDE_GLINE, and LOCOP_WIDE_GLINE + + * doc/example.conf: update to mention new features along with + their defaults + 2001-06-27 Kevin L. Mitchell * doc/example.conf: updated example.conf from Braden diff --git a/doc/example.conf b/doc/example.conf index 049b7c8..e83adc4 100644 --- a/doc/example.conf +++ b/doc/example.conf @@ -465,6 +465,7 @@ P:192.168.*:::6666 # F:PINGFREQUENCY:120 # F:CONNECTFREQUENCY:600 # F:DEFAULTMAXSENDQLENGTH:40000 +# F:GLINEMAXUSERCOUNT:20 # F:MPATH:ircd.motd # F:RPATH:remote.motd # F:PPATH:ircd.pid @@ -495,6 +496,7 @@ P:192.168.*:::6666 # F:OPER_LBADCHAN:FALSE # F:OPER_SET:TRUE # F:OPERS_SEE_IN_SECRET_CHANNELS:TRUE +# F:OPER_WIDE_GLINE:TRUE # F:LOCOP_KILL:TRUE # F:LOCOP_REHASH:TRUE # F:LOCOP_RESTART:FALSE @@ -505,6 +507,7 @@ P:192.168.*:::6666 # F:LOCOP_LBADCHAN:FALSE # F:LOCOP_SET:FALSE # F:LOCOP_SEE_IN_SECRET_CHANNELS:FALSE +# F:LOCOP_WIDE_GLINE:FALSE # Well, you have now reached the end of this sample configuration diff --git a/doc/readme.features b/doc/readme.features index eca7aa7..74520d8 100644 --- a/doc/readme.features +++ b/doc/readme.features @@ -340,6 +340,14 @@ probably always override this value in your "ircd.conf" with the Y-lines. The given value used to be an often used value for client sendQs. +GLINEMAXUSERCOUNT + * Type: integer + * Default: 20 + +G-lines that affect too many users have to be set with a special +command, to prevent accidental G-lines of large blocks of users. This +feature sets that particular threshold. + MPATH * Type: string * Default: "ircd.motd" @@ -594,6 +602,14 @@ themselves. This can be used to make a reasonable judgment in the case of a "channel takeover" being reported, while the channel is set invite-only. See doc/readme.who for more details. +OPER_WIDE_GLINE + * Type: boolean + * Default: TRUE + +This selects whether global IRC operators on this server are permitted +to use the /GLINE command with the ! flag to force slightly wide +G-lines to be set. + LOCOP_KILL * Type: boolean * Default: TRUE @@ -667,3 +683,11 @@ able to see who is on a specified secret channel, without joining themselves. This can be used to make a reasonable judgment in the case of a "channel takeover" being reported, while the channel is set invite-only. See doc/readme.who for more details. + +LOCOP_WIDE_GLINE + * Type: boolean + * Default: FALSE + +This selects whether local IRC operators are permitted to use the +/GLINE command with the ! flag to force slightly wide G-lines to be +set. diff --git a/doc/readme.gline b/doc/readme.gline index deb48b2..0526898 100644 --- a/doc/readme.gline +++ b/doc/readme.gline @@ -10,7 +10,7 @@ an error is returned. For an operator, the syntax is: - GLINE [[+|-] [[] :]] + GLINE [[!][+|-] [[] :]] If is not given, or if it is not prefixed by "+" or "-", the operation is exactly the same as if it were issued by an ordinary @@ -23,7 +23,10 @@ given, as described below. Otherwise, if the G-line currently exists, a prefix of "+" will cause an inactive G-line to be activated, whereas a prefix of "-" will cause an active G-line to be deactivated. If an attempt is made to modify a G-line set by a U-lined service such as -Uworld, the change will be forced to be local. +Uworld, the change will be forced to be local. If the mask would not +be permitted due to it being too wide or affecting too many users +(governed by the GLINEMAXUSERCOUNT feature), the "!" prefix may be +used to force the G-line to be set anyway. If the G-line does not already exist, it is created. The parameter is used to select whether the G-line is only to apply to a @@ -33,7 +36,12 @@ server. This could be useful if a single particular link is having problems, for instance. The parameter is a number of seconds, not to exceed 7 days, for the G-line to exist. The argument is mandatory and should describe why this particular G-line -was placed. +was placed. The parameter must be a user@host mask; the host +component must contain at least 2 non-wildcarded subdomains or, if it +is an IP address, at least 16 bits. Normally, the host component may +not contain *any* wildcards, but that can be overridden with the "!" +prefix, as indicated above, if the operator has the WIDE_GLINE +privilege. For a server, the syntax is: diff --git a/include/client.h b/include/client.h index 3af00dd..91ceba1 100644 --- a/include/client.h +++ b/include/client.h @@ -95,7 +95,9 @@ struct AuthRequest; #define PRIV_DISPLAY 25 /* "Is an oper" displayed */ #define PRIV_SEE_OPERS 26 /* display hidden opers */ -#define PRIV_LAST_PRIV 26 /* must be the same as the last priv */ +#define PRIV_WIDE_GLINE 27 /* oper can set wider G-lines */ + +#define PRIV_LAST_PRIV 27 /* must be the same as the last priv */ #define _PRIV_NBITS (8 * sizeof(unsigned long)) diff --git a/include/gline.h b/include/gline.h index cc4e815..ec10009 100644 --- a/include/gline.h +++ b/include/gline.h @@ -56,6 +56,7 @@ struct Gline { #define GLINE_LDEACT 0x0080 /* locally deactivated */ #define GLINE_GLOBAL 0x0100 /* find only global glines */ #define GLINE_LASTMOD 0x0200 /* find only glines with non-zero lastmod */ +#define GLINE_OPERFORCE 0x0400 /* oper forcing gline to be set */ #define GLINE_MASK (GLINE_ACTIVE | GLINE_BADCHAN | GLINE_LOCAL) #define GLINE_ACTMASK (GLINE_ACTIVE | GLINE_LDEACT) diff --git a/include/ircd_features.h b/include/ircd_features.h index 73037bc..6d4dea3 100644 --- a/include/ircd_features.h +++ b/include/ircd_features.h @@ -56,6 +56,7 @@ enum Feature { FEAT_PINGFREQUENCY, FEAT_CONNECTFREQUENCY, FEAT_DEFAULTMAXSENDQLENGTH, + FEAT_GLINEMAXUSERCOUNT, /* Some misc. default paths */ FEAT_MPATH, @@ -94,6 +95,7 @@ enum Feature { FEAT_OPER_LBADCHAN, FEAT_OPER_SET, FEAT_OPERS_SEE_IN_SECRET_CHANNELS, + FEAT_OPER_WIDE_GLINE, /* features that affect local opers on this server */ FEAT_LOCOP_KILL, @@ -106,6 +108,7 @@ enum Feature { FEAT_LOCOP_LBADCHAN, FEAT_LOCOP_SET, FEAT_LOCOP_SEE_IN_SECRET_CHANNELS, + FEAT_LOCOP_WIDE_GLINE, FEAT_LAST_F }; diff --git a/include/numeric.h b/include/numeric.h index 4f90cd4..4500fb0 100644 --- a/include/numeric.h +++ b/include/numeric.h @@ -378,6 +378,9 @@ extern const struct Numeric* get_error_numeric(int err); #define ERR_BADEXPIRE 515 /* Undernet extension - jupe -Kev */ #define ERR_DONTCHEAT 516 /* Undernet extension */ #define ERR_DISABLED 517 /* Undernet extension -Kev */ -#define ERR_LASTERROR 518 +#define ERR_LONGMASK 518 /* Undernet extension -Kev */ +#define ERR_TOOMANYUSERS 519 /* Undernet extension -Kev */ +#define ERR_MASKTOOWIDE 520 /* Undernet extension -Kev */ +#define ERR_LASTERROR 521 #endif /* INCLUDED_numeric_h */ diff --git a/include/whocmds.h b/include/whocmds.h index 44110f7..298dae0 100644 --- a/include/whocmds.h +++ b/include/whocmds.h @@ -56,6 +56,6 @@ struct Channel; */ extern void do_who(struct Client* sptr, struct Client* acptr, struct Channel* repchan, int fields, char* qrt); - +extern int count_users(char* mask); #endif /* INCLUDED_whocmds_h */ diff --git a/ircd/client.c b/ircd/client.c index a7aaa23..dac2e39 100644 --- a/ircd/client.c +++ b/ircd/client.c @@ -138,6 +138,7 @@ static struct { { PRIV_LOCAL_BADCHAN, FEAT_OPER_LBADCHAN, FLAGS_OPER }, { PRIV_SET, FEAT_OPER_SET, FLAGS_OPER }, { PRIV_SEE_CHAN, FEAT_OPERS_SEE_IN_SECRET_CHANNELS, FLAGS_OPER }, + { PRIV_WIDE_GLINE, FEAT_OPER_WIDE_GLINE, FLAGS_OPER }, { PRIV_LOCAL_KILL, FEAT_LOCOP_KILL, FLAGS_LOCOP }, { PRIV_REHASH, FEAT_LOCOP_REHASH, FLAGS_LOCOP }, @@ -149,6 +150,7 @@ static struct { { PRIV_LOCAL_BADCHAN, FEAT_LOCOP_LBADCHAN, FLAGS_LOCOP }, { PRIV_SET, FEAT_LOCOP_SET, FLAGS_LOCOP }, { PRIV_SEE_CHAN, FEAT_LOCOP_SEE_IN_SECRET_CHANNELS, FLAGS_LOCOP }, + { PRIV_WIDE_GLINE, FEAT_LOCOP_WIDE_GLINE, FLAGS_LOCOP }, { 0, FEAT_LAST_F, 0 } }; @@ -220,7 +222,7 @@ static struct { P(GLINE), P(LOCAL_GLINE), P(JUPE), P(LOCAL_JUPE), P(OPMODE), P(LOCAL_OPMODE), P(SET), P(WHOX), P(BADCHAN), P(LOCAL_BADCHAN), P(SEE_CHAN), P(PROPAGATE), - P(DISPLAY), P(SEE_OPERS), + P(DISPLAY), P(SEE_OPERS), P(WIDE_GLINE), #undef P { 0, 0 } }; diff --git a/ircd/gline.c b/ircd/gline.c index 6362156..7ed3f1c 100644 --- a/ircd/gline.c +++ b/ircd/gline.c @@ -25,9 +25,11 @@ #include "client.h" #include "ircd.h" #include "ircd_alloc.h" +#include "ircd_features.h" #include "ircd_log.h" #include "ircd_policy.h" #include "ircd_reply.h" +#include "ircd_snprintf.h" #include "ircd_string.h" #include "match.h" #include "numeric.h" @@ -41,12 +43,27 @@ #include "numnicks.h" #include "numeric.h" #include "sys.h" /* FALSE bleah */ +#include "whocmds.h" #include #include #include +#include #include /* for inet_ntoa */ +#define CHECK_APPROVED 0 /* Mask is acceptable */ +#define CHECK_OVERRIDABLE 1 /* Mask is acceptable, but not by default */ +#define CHECK_REJECTED 2 /* Mask is totally unacceptable */ + +#define MASK_WILD_0 0x01 /* Wildcards in the last position */ +#define MASK_WILD_1 0x02 /* Wildcards in the next-to-last position */ + +#define MASK_WILD_MASK 0x03 /* Mask out the positional wildcards */ + +#define MASK_WILDS 0x10 /* Mask contains wildcards */ +#define MASK_IP 0x20 /* Mask is an IP address */ +#define MASK_HALT 0x40 /* Finished processing mask */ + struct Gline* GlobalGlineList = 0; struct Gline* BadChanGlineList = 0; @@ -66,14 +83,12 @@ canon_userhost(char *userhost, char **user_p, char **host_p, char *def_user) } static struct Gline * -make_gline(char *userhost, char *reason, time_t expire, time_t lastmod, +make_gline(char *user, char *host, char *reason, time_t expire, time_t lastmod, unsigned int flags) { struct Gline *gline, *sgline, *after = 0; - char *user, *host; if (!(flags & GLINE_BADCHAN)) { /* search for overlapping glines first */ - canon_userhost(userhost, &user, &host, "*"); /* find user and host */ for (gline = GlobalGlineList; gline; gline = sgline) { sgline = gline->gl_next; @@ -104,7 +119,7 @@ make_gline(char *userhost, char *reason, time_t expire, time_t lastmod, gline->gl_flags = flags & GLINE_MASK; if (flags & GLINE_BADCHAN) { /* set a BADCHAN gline */ - DupString(gline->gl_user, userhost); /* first, remember channel */ + DupString(gline->gl_user, user); /* first, remember channel */ gline->gl_host = 0; gline->gl_next = BadChanGlineList; /* then link it into list */ @@ -202,6 +217,68 @@ do_gline(struct Client *cptr, struct Client *sptr, struct Gline *gline) return retval; } +/* + * This routine implements the mask checking applied to local + * G-lines. Basically, host masks must have a minimum of two non-wild + * domain fields, and IP masks must have a minimum of 16 bits. If the + * mask has even one wild-card, OVERRIDABLE is returned, assuming the + * other check doesn't fail. + */ +static int +gline_checkmask(char *mask) +{ + unsigned int flags = MASK_IP; + unsigned int dots = 0; + unsigned int ipmask = 0; + + for (; *mask; mask++) { /* go through given mask */ + if (*mask == '.') { /* it's a separator; advance positional wilds */ + flags = (flags & ~MASK_WILD_MASK) | ((flags << 1) & MASK_WILD_MASK); + dots++; + + if ((flags & (MASK_IP | MASK_WILDS)) == MASK_IP) + ipmask += 8; /* It's an IP with no wilds, count bits */ + } else if (*mask == '*' || *mask == '?') + flags |= MASK_WILD_0 | MASK_WILDS; /* found a wildcard */ + else if (*mask == '/') { /* n.n.n.n/n notation; parse bit specifier */ + ipmask = strtoul(++mask, &mask, 10); + + if (*mask || dots != 3 || ipmask > 32 || /* sanity-check to date */ + (flags & (MASK_WILDS | MASK_IP)) != MASK_IP) + return CHECK_REJECTED; /* how strange... */ + + if (ipmask < 32) /* it's a masked address; mark wilds */ + flags |= MASK_WILDS; + + flags |= MASK_HALT; /* Halt the ipmask calculation */ + + break; /* get out of the loop */ + } else if (!IsDigit(*mask)) { + flags &= ~MASK_IP; /* not an IP anymore! */ + ipmask = 0; + } + } + + /* Sanity-check quads */ + if (dots > 3 || (!(flags & MASK_WILDS) && dots < 3)) { + flags &= ~MASK_IP; + ipmask = 0; + } + + /* update bit count if necessary */ + if ((flags & (MASK_IP | MASK_WILDS | MASK_HALT)) == MASK_IP) + ipmask += 8; + + /* Check to see that it's not too wide of a mask */ + if (flags & MASK_WILDS && + ((!(flags & MASK_IP) && (dots < 2 || flags & MASK_WILD_MASK)) || + (flags & MASK_IP && ipmask < 16))) + return CHECK_REJECTED; /* to wide, reject */ + + /* Ok, it's approved; require override if it has wildcards, though */ + return flags & MASK_WILDS ? CHECK_OVERRIDABLE : CHECK_APPROVED; +} + int gline_propagate(struct Client *cptr, struct Client *sptr, struct Gline *gline) { @@ -231,22 +308,13 @@ gline_add(struct Client *cptr, struct Client *sptr, char *userhost, char *reason, time_t expire, time_t lastmod, unsigned int flags) { struct Gline *agline; + char uhmask[USERLEN + HOSTLEN + 2]; + char *user, *host; + int tmp; assert(0 != userhost); assert(0 != reason); - /* - * You cannot set a negative (or zero) expire time, nor can you set an - * expiration time for greater than GLINE_MAX_EXPIRE. - */ - if (!(flags & GLINE_FORCE) && (expire <= 0 || expire > GLINE_MAX_EXPIRE)) { - if (!IsServer(sptr) && MyConnect(sptr)) - send_reply(sptr, ERR_BADEXPIRE, expire); - return 0; - } - - expire += CurrentTime; /* convert from lifetime to timestamp */ - /* NO_OLD_GLINE allows *@#channel to work correctly */ if (*userhost == '#' || *userhost == '&' || *userhost == '+' # ifndef NO_OLD_GLINE @@ -257,10 +325,49 @@ gline_add(struct Client *cptr, struct Client *sptr, char *userhost, return send_reply(sptr, ERR_NOPRIVILEGES); flags |= GLINE_BADCHAN; +# ifndef NO_OLD_GLINE + if (userhost[2] == '#' || userhost[2] == '&' || userhost[2] == '+') + user = userhost + 2; + else +# endif /* OLD_GLINE */ + user = userhost; + host = 0; + } else { + canon_userhost(userhost, &user, &host, "*"); + if (sizeof(uhmask) < + ircd_snprintf(0, uhmask, sizeof(uhmask), "%s@%s", user, host)) + return send_reply(sptr, ERR_LONGMASK); + else if (MyUser(sptr) || (IsUser(sptr) && flags & GLINE_LOCAL)) { + switch (gline_checkmask(host)) { + case CHECK_OVERRIDABLE: /* oper overrided restriction */ + if (flags & GLINE_OPERFORCE) + break; + /*FALLTHROUGH*/ + case CHECK_REJECTED: + return send_reply(sptr, ERR_MASKTOOWIDE, uhmask); + break; + } + + if ((tmp = count_users(uhmask)) >= + feature_int(FEAT_GLINEMAXUSERCOUNT) && !(flags & GLINE_OPERFORCE)) + return send_reply(sptr, ERR_TOOMANYUSERS, tmp); + } } + /* + * You cannot set a negative (or zero) expire time, nor can you set an + * expiration time for greater than GLINE_MAX_EXPIRE. + */ + if (!(flags & GLINE_FORCE) && (expire <= 0 || expire > GLINE_MAX_EXPIRE)) { + if (!IsServer(sptr) && MyConnect(sptr)) + send_reply(sptr, ERR_BADEXPIRE, expire); + return 0; + } + + expire += CurrentTime; /* convert from lifetime to timestamp */ + /* Inform ops... */ - sendto_opmask_butone(0, SNO_GLINE, "%s adding %s %s for %s, expiring at " + sendto_opmask_butone(0, SNO_GLINE, "%s adding %s %s for %s%s%s, expiring at " "%Tu: %s", #ifdef HEAD_IN_SAND_SNOTICES cli_name(sptr), @@ -269,7 +376,9 @@ gline_add(struct Client *cptr, struct Client *sptr, char *userhost, cli_name((cli_user(sptr))->server), #endif flags & GLINE_LOCAL ? "local" : "global", - flags & GLINE_BADCHAN ? "BADCHAN" : "GLINE", userhost, + flags & GLINE_BADCHAN ? "BADCHAN" : "GLINE", user, + flags & GLINE_BADCHAN ? "" : "@", + flags & GLINE_BADCHAN ? "" : host, expire + TSoffset, reason); /* and log it */ @@ -280,7 +389,7 @@ gline_add(struct Client *cptr, struct Client *sptr, char *userhost, expire + TSoffset, reason); /* make the gline */ - agline = make_gline(userhost, reason, expire, lastmod, flags); + agline = make_gline(user, host, reason, expire, lastmod, flags); if (!agline) /* if it overlapped, silently return */ return 0; diff --git a/ircd/ircd_features.c b/ircd/ircd_features.c index 05ed003..83971d7 100644 --- a/ircd/ircd_features.c +++ b/ircd/ircd_features.c @@ -263,6 +263,7 @@ static struct FeatureDesc { F_I(PINGFREQUENCY, 0, 120, init_class), F_I(CONNECTFREQUENCY, 0, 600, init_class), F_I(DEFAULTMAXSENDQLENGTH, 0, 40000, init_class), + F_I(GLINEMAXUSERCOUNT, 0, 20, 0), /* Some misc. default paths */ F_S(MPATH, FEAT_CASE | FEAT_MYOPER, "ircd.motd", motd_init), @@ -301,6 +302,7 @@ static struct FeatureDesc { F_B(OPER_LBADCHAN, 0, 0, 0), F_B(OPER_SET, 0, 1, 0), F_B(OPERS_SEE_IN_SECRET_CHANNELS, 0, 1, 0), + F_B(OPER_WIDE_GLINE, 0, 1, 0), /* features that affect local opers on this server */ F_B(LOCOP_KILL, 0, 1, 0), @@ -313,6 +315,7 @@ static struct FeatureDesc { F_B(LOCOP_LBADCHAN, 0, 0, 0), F_B(LOCOP_SET, 0, 0, 0), F_B(LOCOP_SEE_IN_SECRET_CHANNELS, 0, 0, 0), + F_B(LOCOP_WIDE_GLINE, 0, 0, 0), #undef F_S #undef F_B diff --git a/ircd/m_gline.c b/ircd/m_gline.c index e1aa27d..a0f8b22 100644 --- a/ircd/m_gline.c +++ b/ircd/m_gline.c @@ -136,6 +136,13 @@ ms_gline(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) time_t expire_off, lastmod = 0; char *mask = parv[2], *target = parv[1], *reason; + if (*mask == '!') { + mask++; + + if (HasPriv(sptr, PRIV_WIDE_GLINE)) + flags |= GLINE_OPERFORCE; + } + if ((parc == 3 && *mask == '-') || parc == 5) { if (!find_conf_byhost(cli_confs(cptr), cli_name(sptr), CONF_UWORLD)) return need_more_params(sptr, "GLINE"); @@ -158,8 +165,9 @@ ms_gline(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) (parc == 3) ? "%C %s" : "%C %s %s :%s", acptr, mask, parv[3], reason); else - sendcmdto_one(sptr, CMD_GLINE, acptr, "%C %s %s %s :%s", acptr, mask, - parv[3], parv[4], reason); + sendcmdto_one(sptr, CMD_GLINE, acptr, "%C %s%s %s %s :%s", acptr, + flags & GLINE_OPERFORCE ? "!" : "", mask, parv[3], + parv[4], reason); return 0; } @@ -236,6 +244,13 @@ mo_gline(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) if (parc < 2) return gline_list(sptr, 0); + if (*mask == '!') { + mask++; + + if (HasPriv(sptr, PRIV_WIDE_GLINE)) + flags |= GLINE_OPERFORCE; + } + if (*mask == '+') { flags |= GLINE_ACTIVE; mask++; @@ -268,7 +283,8 @@ mo_gline(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) if (!HasPriv(sptr, PRIV_GLINE)) return send_reply(sptr, ERR_NOPRIVILEGES); - sendcmdto_one(sptr, CMD_GLINE, acptr, "%C %c%s %s %Tu :%s", acptr, + sendcmdto_one(sptr, CMD_GLINE, acptr, "%C %s%c%s %s %Tu :%s", acptr, + flags & GLINE_OPERFORCE ? "!" : "", flags & GLINE_ACTIVE ? '+' : '-', mask, parv[3], TStime(), reason); return 0; diff --git a/ircd/s_err.c b/ircd/s_err.c index 7a4fda3..f5a382d 100644 --- a/ircd/s_err.c +++ b/ircd/s_err.c @@ -1069,11 +1069,11 @@ static Numeric replyTable[] = { /* 517 */ { ERR_DISABLED, "%s :Command disabled.", "517" }, /* 518 */ - { 0 }, + { ERR_LONGMASK, " :Mask is too long", "518" }, /* 519 */ - { 0 }, + { ERR_TOOMANYUSERS, "%d :Too many users affected by mask", "519" }, /* 520 */ - { 0 }, + { ERR_MASKTOOWIDE, "%s :Mask is too wide", "520" }, /* 521 */ { 0 }, /* 522 */ diff --git a/ircd/whocmds.c b/ircd/whocmds.c index 26c12d4..39a9bc7 100644 --- a/ircd/whocmds.c +++ b/ircd/whocmds.c @@ -32,6 +32,7 @@ #include "ircd_chattr.h" #include "ircd_policy.h" #include "ircd_reply.h" +#include "ircd_snprintf.h" #include "ircd_string.h" #include "list.h" #include "match.h" @@ -231,3 +232,26 @@ void do_who(struct Client* sptr, struct Client* acptr, struct Channel* repchan, send_reply(sptr, fields ? RPL_WHOSPCRPL : RPL_WHOREPLY, ++p1); } +int +count_users(char *mask) +{ + struct Client *acptr; + int count = 0; + char namebuf[USERLEN + HOSTLEN + 2]; + char ipbuf[USERLEN + 16 + 2]; + + for (acptr = GlobalClientList; acptr; acptr = cli_next(acptr)) { + if (!IsUser(acptr)) + continue; + + ircd_snprintf(0, namebuf, sizeof(namebuf), "%s@%s", + cli_user(acptr)->username, cli_user(acptr)->host); + ircd_snprintf(0, ipbuf, sizeof(ipbuf), "%s@%s", cli_user(acptr)->username, + ircd_ntoa((const char *) &(cli_ip(acptr)))); + + if (!match(mask, namebuf) || !match(mask, ipbuf)) + count++; + } + + return count; +} -- 2.20.1