#include "config.h"
#include "gline.h"
+#include "channel.h"
#include "client.h"
#include "ircd.h"
#include "ircd_alloc.h"
#include "msg.h"
#include "numnicks.h"
#include "numeric.h"
-#include "whocmds.h"
/* #include <assert.h> -- Now using assert in ircd_log.h */
#include <string.h>
* @param[in] gl Name of a struct Gline pointer variable that will be made to point to the G-lines in sequence.
* @param[in] next Name of a scratch struct Gline pointer variable.
*/
+/* There is some subtlety here with the boolean operators:
+ * (x || 1) is used to continue in a logical-and series even when !x.
+ * (x && 0) is used to continue in a logical-or series even when x.
+ */
#define gliter(list, gl, next) \
/* Iterate through the G-lines in the list */ \
for ((gl) = (list); (gl); (gl) = (next)) \
/* Figure out the next pointer in list... */ \
if ((((next) = (gl)->gl_next) || 1) && \
/* Then see if it's expired */ \
- (gl)->gl_lifetime <= CurrentTime) \
+ (gl)->gl_lifetime <= TStime()) \
/* Record has expired, so free the G-line */ \
gline_free((gl)); \
/* See if we need to expire the G-line */ \
- else if (((gl)->gl_expire > CurrentTime || \
- ((gl)->gl_flags &= ~GLINE_ACTIVE) || \
+ else if ((((gl)->gl_expire > TStime()) || \
+ (((gl)->gl_flags &= ~GLINE_ACTIVE) && 0) || \
((gl)->gl_state = GLOCAL_GLOBAL)) && 0) \
; /* empty statement */ \
else
make_gline(char *user, char *host, char *reason, time_t expire, time_t lastmod,
time_t lifetime, unsigned int flags)
{
- struct Gline *gline, *after = 0;
-
- /* Disable checking for overlapping G-lines. The problem is that we
- * may have a wide-mask, long lifetime G-line that we've
- * deactivated--maybe it was a mistake?--and someone comes along and
- * wants to set a narrower overlapping G-line with a shorter
- * lifetime. If we were to leave this logic enabled, there would be
- * no way to set that narrower G-line.
- */
-/* if (!(flags & GLINE_BADCHAN)) { /\* search for overlapping glines first *\/ */
-
-/* for (gline = GlobalGlineList; gline; gline = sgline) { */
-/* sgline = gline->gl_next; */
-
-/* if (gline->gl_expire <= CurrentTime) */
-/* gline_free(gline); */
-/* else if (((gline->gl_flags & GLINE_LOCAL) != (flags & GLINE_LOCAL)) || */
-/* (gline->gl_host && !host) || (!gline->gl_host && host)) */
-/* continue; */
-/* else if (!mmatch(gline->gl_user, user) /\* gline contains new mask *\/ */
-/* && (gline->gl_host == NULL || !mmatch(gline->gl_host, host))) { */
-/* if (expire <= gline->gl_expire) /\* will expire before wider gline *\/ */
-/* return 0; */
-/* else */
-/* after = gline; /\* stick new gline after this one *\/ */
-/* } else if (!mmatch(user, gline->gl_user) /\* new mask contains gline *\/ */
-/* && (gline->gl_host==NULL || !mmatch(host, gline->gl_host)) */
-/* && gline->gl_expire <= expire) /\* old expires before new *\/ */
-/* gline_free(gline); /\* save some memory *\/ */
-/* } */
-/* } */
+ struct Gline *gline;
+
+ assert(0 != expire);
gline = (struct Gline *)MyMalloc(sizeof(struct Gline)); /* alloc memory */
assert(0 != gline);
if (flags & GLINE_BADCHAN) { /* set a BADCHAN gline */
DupString(gline->gl_user, user); /* first, remember channel */
- gline->gl_host = 0;
+ gline->gl_host = NULL;
gline->gl_next = BadChanGlineList; /* then link it into list */
gline->gl_prev_p = &BadChanGlineList;
if (*user != '$' && ipmask_parse(host, &gline->gl_addr, &gline->gl_bits))
gline->gl_flags |= GLINE_IPMASK;
- if (after) {
- gline->gl_next = after->gl_next;
- gline->gl_prev_p = &after->gl_next;
- if (after->gl_next)
- after->gl_next->gl_prev_p = &gline->gl_next;
- after->gl_next = gline;
- } else {
- gline->gl_next = GlobalGlineList; /* then link it into list */
- gline->gl_prev_p = &GlobalGlineList;
- if (GlobalGlineList)
- GlobalGlineList->gl_prev_p = &gline->gl_next;
- GlobalGlineList = gline;
- }
+ gline->gl_next = GlobalGlineList; /* then link it into list */
+ gline->gl_prev_p = &GlobalGlineList;
+ if (GlobalGlineList)
+ GlobalGlineList->gl_prev_p = &gline->gl_next;
+ GlobalGlineList = gline;
}
return gline;
* @param[in] gline G-line to forward.
* @return Zero.
*/
-int
+static int
gline_propagate(struct Client *cptr, struct Client *sptr, struct Gline *gline)
{
if (GlineIsLocal(gline))
GlineIsRemActive(gline) ? '+' : '-', gline->gl_user,
gline->gl_host ? "@" : "",
gline->gl_host ? gline->gl_host : "",
- gline->gl_expire - CurrentTime, gline->gl_lastmod,
- gline->gl_lifetime - CurrentTime, gline->gl_reason);
+ gline->gl_expire - TStime(), gline->gl_lastmod,
+ gline->gl_lifetime, gline->gl_reason);
return 0;
}
+/** Count number of users who match \a mask.
+ * @param[in] mask user\@host or user\@ip mask to check.
+ * @param[in] flags Bitmask possibly containing the value GLINE_LOCAL, to limit searches to this server.
+ * @return Count of matching users.
+ */
+static int
+count_users(char *mask, int flags)
+{
+ struct irc_in_addr ipmask;
+ struct Client *acptr;
+ int count = 0;
+ int ipmask_valid;
+ char namebuf[USERLEN + HOSTLEN + 2];
+ char ipbuf[USERLEN + SOCKIPLEN + 2];
+ unsigned char ipmask_len;
+
+ ipmask_valid = ipmask_parse(mask, &ipmask, &ipmask_len);
+ for (acptr = GlobalClientList; acptr; acptr = cli_next(acptr)) {
+ if (!IsUser(acptr))
+ continue;
+ if ((flags & GLINE_LOCAL) && !MyConnect(acptr))
+ continue;
+
+ ircd_snprintf(0, namebuf, sizeof(namebuf), "%s@%s",
+ cli_user(acptr)->username, cli_user(acptr)->realhost);
+ ircd_snprintf(0, ipbuf, sizeof(ipbuf), "%s@%s", cli_user(acptr)->username,
+ ircd_ntoa(&cli_ip(acptr)));
+
+ if (!match(mask, namebuf)
+ || !match(mask, ipbuf)
+ || (ipmask_valid && ipmask_check(&cli_ip(acptr), &ipmask, ipmask_len)))
+ count++;
+ }
+
+ return count;
+}
+
+/** Count number of users with a realname matching \a mask.
+ * @param[in] mask Wildcard mask to match against realnames.
+ * @return Count of matching users.
+ */
+static int
+count_realnames(const char *mask)
+{
+ struct Client *acptr;
+ int minlen;
+ int count;
+ char cmask[BUFSIZE];
+
+ count = 0;
+ matchcomp(cmask, &minlen, NULL, mask);
+ for (acptr = GlobalClientList; acptr; acptr = cli_next(acptr)) {
+ if (!IsUser(acptr))
+ continue;
+ if (strlen(cli_info(acptr)) < minlen)
+ continue;
+ if (!matchexec(cli_info(acptr), cmask, minlen))
+ count++;
+ }
+ return count;
+}
+
/** Create a new G-line and add it to global lists.
* \a userhost may be in one of four forms:
* \li A channel name, to add a BadChan.
* @param[in] sptr Client that originated the G-line.
* @param[in] userhost Text mask for the G-line.
* @param[in] reason Reason for G-line.
- * @param[in] expire Duration of G-line in seconds.
+ * @param[in] expire Expiration time of G-line.
* @param[in] lastmod Last modification time of G-line.
* @param[in] lifetime Lifetime of G-line.
* @param[in] flags Bitwise combination of GLINE_* flags.
assert(0 != userhost);
assert(0 != reason);
+ assert(((flags & (GLINE_GLOBAL | GLINE_LOCAL)) == GLINE_GLOBAL) ||
+ ((flags & (GLINE_GLOBAL | GLINE_LOCAL)) == GLINE_LOCAL));
Debug((DEBUG_DEBUG, "gline_add(\"%s\", \"%s\", \"%s\", \"%s\", %Tu, %Tu "
"%Tu, 0x%04x)", cli_name(cptr), cli_name(sptr), userhost, reason,
if (*userhost == '#' || *userhost == '&') {
if ((flags & GLINE_LOCAL) && !HasPriv(sptr, PRIV_LOCAL_BADCHAN))
return send_reply(sptr, ERR_NOPRIVILEGES);
+ /* Allow maximum channel name length, plus margin for wildcards. */
+ if (strlen(userhost+1) >= CHANNELLEN + 6)
+ return send_reply(sptr, ERR_LONGMASK);
flags |= GLINE_BADCHAN;
user = userhost;
- host = 0;
+ host = NULL;
} else if (*userhost == '$') {
switch (userhost[1]) {
- case 'R': flags |= GLINE_REALNAME; break;
+ case 'R':
+ /* Allow REALLEN for the real name, plus margin for wildcards. */
+ if (strlen(userhost+2) >= REALLEN + 6)
+ return send_reply(sptr, ERR_LONGMASK);
+ flags |= GLINE_REALNAME;
+ break;
default:
/* uh, what to do here? */
/* The answer, my dear Watson, is we throw a protocol_violation()
-- hikari */
if (IsServer(cptr))
return protocol_violation(sptr,"%s has been smoking the sweet leaf and sent me a whacky gline",cli_name(sptr));
- else {
- sendto_opmask_butone(NULL, SNO_GLINE, "%s has been smoking the sweet leaf and sent me a whacky gline", cli_name(sptr));
- return 0;
- }
- break;
+ sendto_opmask_butone(NULL, SNO_GLINE, "%s has been smoking the sweet leaf and sent me a whacky gline", cli_name(sptr));
+ return 0;
+ }
+ user = userhost;
+ host = NULL;
+ if (MyUser(sptr) || (IsUser(sptr) && flags & GLINE_LOCAL)) {
+ tmp = count_realnames(userhost + 2);
+ if ((tmp >= feature_int(FEAT_GLINEMAXUSERCOUNT))
+ && !(flags & GLINE_OPERFORCE))
+ return send_reply(sptr, ERR_TOOMANYUSERS, tmp);
}
- user = userhost;
- host = 0;
} else {
canon_userhost(userhost, &user, &host, "*");
if (sizeof(uhmask) <
break;
}
- if ((tmp = count_users(uhmask)) >=
+ if ((tmp = count_users(uhmask, flags)) >=
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 (!(flags & GLINE_FORCE) &&
+ (expire <= TStime() || expire > TStime() + GLINE_MAX_EXPIRE)) {
if (!IsServer(sptr) && MyConnect(sptr))
send_reply(sptr, ERR_BADEXPIRE, expire);
return 0;
+ } else if (expire <= TStime()) {
+ /* This expired G-line was forced to be added, so mark it inactive. */
+ flags &= ~GLINE_ACTIVE;
}
- expire += CurrentTime; /* convert from lifetime to timestamp */
-
if (!lifetime) /* no lifetime set, use expiration time */
lifetime = expire;
- else /* convert lifetime into timestamp */
- lifetime += CurrentTime;
+
+ /* lifetime is already an absolute timestamp */
/* Inform ops... */
sendto_opmask_butone(0, ircd_strncmp(reason, "AUTO", 4) ? SNO_GLINE :
- SNO_AUTO, "%s adding %s %s for %s%s%s, expiring at "
+ SNO_AUTO, "%s adding %s%s %s for %s%s%s, expiring at "
"%Tu: %s",
(feature_bool(FEAT_HIS_SNOTICES) || IsServer(sptr)) ?
cli_name(sptr) :
cli_name((cli_user(sptr))->server),
+ (flags & GLINE_ACTIVE) ? "" : "deactivated ",
(flags & GLINE_LOCAL) ? "local" : "global",
(flags & GLINE_BADCHAN) ? "BADCHAN" : "GLINE", user,
(flags & (GLINE_BADCHAN|GLINE_REALNAME)) ? "" : "@",
(flags & (GLINE_BADCHAN|GLINE_REALNAME)) ? "" : host,
- expire + TSoffset, reason);
+ expire, reason);
/* and log it */
log_write(LS_GLINE, L_INFO, LOG_NOSNOTICE,
flags & GLINE_BADCHAN ? "BADCHAN" : "GLINE", user,
flags & (GLINE_BADCHAN|GLINE_REALNAME) ? "" : "@",
flags & (GLINE_BADCHAN|GLINE_REALNAME) ? "" : host,
- expire + TSoffset, reason);
+ expire, reason);
/* make the gline */
agline = make_gline(user, host, reason, expire, lastmod, lifetime, flags);
* never be NULL...
*/
assert(agline);
-/* if (!agline) /\* if it overlapped, silently return *\/ */
-/* return 0; */
gline_propagate(cptr, sptr, agline);
GlineIsBadChan(gline) ? "BADCHAN" : "GLINE",
gline->gl_user, gline->gl_host ? "@" : "",
gline->gl_host ? gline->gl_host : "",
- gline->gl_expire + TSoffset, gline->gl_reason);
-
+ gline->gl_expire, gline->gl_reason);
+
log_write(LS_GLINE, L_INFO, LOG_NOSNOTICE,
"%#C activating global %s for %s%s%s, expiring at %Tu: %s", sptr,
GlineIsBadChan(gline) ? "BADCHAN" : "GLINE", gline->gl_user,
gline->gl_host ? "@" : "",
gline->gl_host ? gline->gl_host : "",
- gline->gl_expire + TSoffset, gline->gl_reason);
+ gline->gl_expire, gline->gl_reason);
if (!(flags & GLINE_LOCAL)) /* don't propagate local changes */
gline_propagate(cptr, sptr, gline);
msg, GlineIsBadChan(gline) ? "BADCHAN" : "GLINE",
gline->gl_user, gline->gl_host ? "@" : "",
gline->gl_host ? gline->gl_host : "",
- gline->gl_expire + TSoffset, gline->gl_reason);
+ gline->gl_expire, gline->gl_reason);
log_write(LS_GLINE, L_INFO, LOG_NOSNOTICE,
"%#C %s %s for %s%s%s, expiring at %Tu: %s", sptr, msg,
GlineIsBadChan(gline) ? "BADCHAN" : "GLINE", gline->gl_user,
gline->gl_host ? "@" : "",
gline->gl_host ? gline->gl_host : "",
- gline->gl_expire + TSoffset, gline->gl_reason);
+ gline->gl_expire, gline->gl_reason);
if (!(flags & GLINE_LOCAL)) /* don't propagate local changes */
gline_propagate(cptr, sptr, gline);
* @param[in] gline G-line being modified.
* @param[in] action Resultant status of the G-line.
* @param[in] reason Reason for G-line.
- * @param[in] expire Duration of G-line in seconds.
+ * @param[in] expire Expiration time of G-line.
* @param[in] lastmod Last modification time of G-line.
* @param[in] lifetime Lifetime of G-line.
* @param[in] flags Bitwise combination of GLINE_* flags.
time_t lastmod, time_t lifetime, unsigned int flags)
{
char buf[BUFSIZE], *op = "";
- int pos = 0;
+ int pos = 0, non_auto = 0;
assert(gline);
assert(!GlineIsLocal(gline));
/* All right, we know that there's a change of some sort. What is it? */
/* first, check out the expiration time... */
if ((flags & GLINE_EXPIRE) && expire) {
- if (!(flags & GLINE_FORCE) && (expire <= 0 || expire > GLINE_MAX_EXPIRE)) {
+ if (!(flags & GLINE_FORCE) &&
+ (expire <= TStime() || expire > TStime() + GLINE_MAX_EXPIRE)) {
if (!IsServer(sptr) && MyConnect(sptr)) /* bad expiration time */
send_reply(sptr, ERR_BADEXPIRE, expire);
return 0;
}
-
- /* convert to a timestamp... */
- expire += CurrentTime;
} else
flags &= ~GLINE_EXPIRE;
}
/* Next, check out lifetime--this one's a bit trickier... */
- if ((flags & GLINE_LIFETIME) && lifetime)
- lifetime += CurrentTime; /* convert to a timestamp */
- else
+ if (!(flags & GLINE_LIFETIME) || !lifetime)
lifetime = gline->gl_lifetime; /* use G-line lifetime */
lifetime = IRCD_MAX(lifetime, expire); /* set lifetime to the max */
(action == GLINE_LOCAL_DEACTIVATE &&
(gline->gl_state == GLOCAL_DEACTIVATED)) ||
/* can't activate an expired G-line */
- IRCD_MAX(gline->gl_expire, expire) <= CurrentTime)
+ IRCD_MAX(gline->gl_expire, expire) <= TStime())
action = GLINE_MODIFY; /* no activity state modifications */
Debug((DEBUG_DEBUG, "About to perform changes; flags 0x%04x, action %s",
/* Now, handle reason changes... */
if (flags & GLINE_REASON) {
+ non_auto = non_auto || ircd_strncmp(gline->gl_reason, "AUTO", 4);
MyFree(gline->gl_reason); /* release old reason */
DupString(gline->gl_reason, reason); /* store new reason */
if (pos < BUFSIZE)
}
/* All right, inform ops... */
- sendto_opmask_butone(0, SNO_GLINE, "%s modifying global %s for %s%s%s:%s",
+ non_auto = non_auto || ircd_strncmp(gline->gl_reason, "AUTO", 4);
+ sendto_opmask_butone(0, non_auto ? SNO_GLINE : SNO_AUTO,
+ "%s modifying global %s for %s%s%s:%s",
(feature_bool(FEAT_HIS_SNOTICES) || IsServer(sptr)) ?
cli_name(sptr) : cli_name((cli_user(sptr))->server),
GlineIsBadChan(gline) ? "BADCHAN" : "GLINE",
flags & GLINE_OPERFORCE ? "!" : "", op,
gline->gl_user, gline->gl_host ? "@" : "",
gline->gl_host ? gline->gl_host : "",
- gline->gl_expire - CurrentTime, gline->gl_lastmod,
- gline->gl_lifetime - CurrentTime, gline->gl_reason);
+ gline->gl_expire - TStime(), gline->gl_lastmod,
+ gline->gl_lifetime, gline->gl_reason);
/* OK, let's do the G-line... */
return do_gline(cptr, sptr, gline);
if (flags & (GLINE_BADCHAN | GLINE_ANY)) {
gliter(BadChanGlineList, gline, sgline) {
- if ((flags & GLINE_GLOBAL && gline->gl_flags & GLINE_LOCAL) ||
- (flags & GLINE_LOCAL && gline->gl_flags & GLINE_GLOBAL) ||
+ if ((flags & (GlineIsLocal(gline) ? GLINE_GLOBAL : GLINE_LOCAL)) ||
(flags & GLINE_LASTMOD && !gline->gl_lastmod))
continue;
else if ((flags & GLINE_EXACT ? ircd_strcmp(gline->gl_user, userhost) :
canon_userhost(t_uh, &user, &host, "*");
gliter(GlobalGlineList, gline, sgline) {
- if ((flags & GLINE_GLOBAL && gline->gl_flags & GLINE_LOCAL) ||
- (flags & GLINE_LOCAL && gline->gl_flags & GLINE_GLOBAL) ||
+ if ((flags & (GlineIsLocal(gline) ? GLINE_GLOBAL : GLINE_LOCAL)) ||
(flags & GLINE_LASTMOD && !gline->gl_lastmod))
continue;
else if (flags & GLINE_EXACT) {
GlineIsRemActive(gline) ? '+' : '-', gline->gl_user,
gline->gl_host ? "@" : "",
gline->gl_host ? gline->gl_host : "",
- gline->gl_expire - CurrentTime, gline->gl_lastmod,
- gline->gl_lifetime - CurrentTime, gline->gl_reason);
+ gline->gl_expire - TStime(), gline->gl_lastmod,
+ gline->gl_lifetime, gline->gl_reason);
}
gliter(BadChanGlineList, gline, sgline) {
if (!GlineIsLocal(gline) && gline->gl_lastmod)
sendcmdto_one(&me, CMD_GLINE, cptr, "* %c%s %Tu %Tu %Tu :%s",
GlineIsRemActive(gline) ? '+' : '-', gline->gl_user,
- gline->gl_expire - CurrentTime, gline->gl_lastmod,
- gline->gl_lifetime - CurrentTime, gline->gl_reason);
+ gline->gl_expire - TStime(), gline->gl_lastmod,
+ gline->gl_lifetime, gline->gl_reason);
}
}
GlineIsRemActive(gline) ? '+' : '-', gline->gl_user,
gline->gl_host ? "@" : "",
gline->gl_host ? gline->gl_host : "",
- gline->gl_expire - CurrentTime, gline->gl_lastmod,
- gline->gl_lifetime - CurrentTime, gline->gl_reason);
+ gline->gl_expire - TStime(), gline->gl_lastmod,
+ gline->gl_lifetime, gline->gl_reason);
return 0;
}
send_reply(sptr, RPL_GLIST, gline->gl_user,
gline->gl_host ? "@" : "",
gline->gl_host ? gline->gl_host : "",
- gline->gl_expire + TSoffset, gline->gl_lastmod,
- gline->gl_lifetime + TSoffset,
+ gline->gl_expire, gline->gl_lastmod,
+ gline->gl_lifetime,
GlineIsLocal(gline) ? cli_name(&me) : "*",
gline->gl_state == GLOCAL_ACTIVATED ? ">" :
(gline->gl_state == GLOCAL_DEACTIVATED ? "<" : ""),
send_reply(sptr, RPL_GLIST, gline->gl_user,
gline->gl_host ? "@" : "",
gline->gl_host ? gline->gl_host : "",
- gline->gl_expire + TSoffset, gline->gl_lastmod,
- gline->gl_lifetime + TSoffset,
+ gline->gl_expire, gline->gl_lastmod,
+ gline->gl_lifetime,
GlineIsLocal(gline) ? cli_name(&me) : "*",
gline->gl_state == GLOCAL_ACTIVATED ? ">" :
(gline->gl_state == GLOCAL_DEACTIVATED ? "<" : ""),
gliter(BadChanGlineList, gline, sgline) {
send_reply(sptr, RPL_GLIST, gline->gl_user, "", "",
- gline->gl_expire + TSoffset, gline->gl_lastmod,
- gline->gl_lifetime + TSoffset,
+ gline->gl_expire, gline->gl_lastmod,
+ gline->gl_lifetime,
GlineIsLocal(gline) ? cli_name(&me) : "*",
gline->gl_state == GLOCAL_ACTIVATED ? ">" :
(gline->gl_state == GLOCAL_DEACTIVATED ? "<" : ""),
send_reply(sptr, RPL_STATSGLINE, 'G', gline->gl_user,
gline->gl_host ? "@" : "",
gline->gl_host ? gline->gl_host : "",
- gline->gl_expire + TSoffset, gline->gl_lastmod,
- gline->gl_lifetime + TSoffset,
+ gline->gl_expire, gline->gl_lastmod,
+ gline->gl_lifetime,
gline->gl_state == GLOCAL_ACTIVATED ? ">" :
(gline->gl_state == GLOCAL_DEACTIVATED ? "<" : ""),
GlineIsRemActive(gline) ? '+' : '-',