X-Git-Url: http://git.pk910.de/?p=ircu2.10.12-pk.git;a=blobdiff_plain;f=ircd%2Fgline.c;h=9c3d2970e24df6a078e4c976cb94c64a2b7d8d36;hp=911fa26bbd024bc342cdd82f7fae5b5e62246f5c;hb=72b0c799c590095ad78de705f36e9553bd41fa67;hpb=b1c866a376fd2f65418ce3b3b43bddd4196fa13f diff --git a/ircd/gline.c b/ircd/gline.c index 911fa26..9c3d297 100644 --- a/ircd/gline.c +++ b/ircd/gline.c @@ -5,7 +5,7 @@ * * 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) + * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, @@ -16,32 +16,108 @@ * 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. - * - * $Id$ */ +/** @file + * @brief Implementation of Gline manipulation functions. + * @version $Id$ + */ +#include "config.h" + #include "gline.h" +#include "channel.h" #include "client.h" #include "ircd.h" #include "ircd_alloc.h" +#include "ircd_features.h" +#include "ircd_log.h" +#include "ircd_reply.h" +#include "ircd_snprintf.h" #include "ircd_string.h" #include "match.h" #include "numeric.h" #include "s_bsd.h" +#include "s_debug.h" #include "s_misc.h" +#include "s_stats.h" #include "send.h" #include "struct.h" -#include "sys.h" /* FALSE bleah */ +#include "sys.h" +#include "msg.h" +#include "numnicks.h" +#include "numeric.h" + +/* #include -- Now using assert in ircd_log.h */ +#include +#include +#include + +#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 */ -#include +#define MASK_WILDS 0x10 /**< Mask contains wildcards */ +#define MASK_IP 0x20 /**< Mask is an IP address */ +#define MASK_HALT 0x40 /**< Finished processing mask */ +/** List of user G-lines. */ struct Gline* GlobalGlineList = 0; +/** List of BadChan G-lines. */ struct Gline* BadChanGlineList = 0; +/** Iterate through \a list of G-lines. Use this like a for loop, + * i.e., follow it with braces and use whatever you passed as \a gl + * as a single G-line to be acted upon. + * + * @param[in] list List of G-lines to iterate over. + * @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 <= 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 > TStime()) || \ + (((gl)->gl_flags &= ~GLINE_ACTIVE) && 0) || \ + ((gl)->gl_state = GLOCAL_GLOBAL)) && 0) \ + ; /* empty statement */ \ + else + +/** Find canonical user and host for a string. + * If \a userhost starts with '$', assign \a userhost to *user_p and NULL to *host_p. + * Otherwise, if \a userhost contains '@', assign the earlier part of it to *user_p and the rest to *host_p. + * Otherwise, assign \a def_user to *user_p and \a userhost to *host_p. + * + * @param[in] userhost Input string from user. + * @param[out] user_p Gets pointer to user (or channel/realname) part of hostmask. + * @param[out] host_p Gets point to host part of hostmask (may be assigned NULL). + * @param[in] def_user Default value for user part. + */ static void canon_userhost(char *userhost, char **user_p, char **host_p, char *def_user) { char *tmp; + if (*userhost == '$') { + *user_p = userhost; + *host_p = NULL; + return; + } + if (!(tmp = strchr(userhost, '@'))) { *user_p = def_user; *host_p = userhost; @@ -52,54 +128,81 @@ canon_userhost(char *userhost, char **user_p, char **host_p, char *def_user) } } +/** Create a Gline structure. + * @param[in] user User part of mask. + * @param[in] host Host part of mask (NULL if not applicable). + * @param[in] reason Reason for G-line. + * @param[in] expire Expiration timestamp. + * @param[in] lastmod Last modification timestamp. + * @param[in] flags Bitwise combination of GLINE_* bits. + * @return Newly allocated G-line. + */ static struct Gline * -make_gline(char *userhost, char *reason, time_t expire, time_t lastmod, - unsigned int flags) +make_gline(char *user, char *host, char *reason, time_t expire, time_t lastmod, + time_t lifetime, unsigned int flags) { - struct Gline *agline; - char *user, *host; + struct Gline *gline; + + assert(0 != expire); - agline = (struct Gline *)MyMalloc(sizeof(struct Gline)); /* alloc memory */ - assert(0 != agline); + gline = (struct Gline *)MyMalloc(sizeof(struct Gline)); /* alloc memory */ + assert(0 != gline); - gline->gl_expire = expire; /* initialize gline... */ + DupString(gline->gl_reason, reason); /* initialize gline... */ + gline->gl_expire = expire; + gline->gl_lifetime = lifetime; gline->gl_lastmod = lastmod; gline->gl_flags = flags & GLINE_MASK; + gline->gl_state = GLOCAL_GLOBAL; /* not locally modified */ if (flags & GLINE_BADCHAN) { /* set a BADCHAN gline */ - DupString(gline->gl_user, userhost); /* first, remember channel */ - gline->gl_host = 0; + DupString(gline->gl_user, user); /* first, remember channel */ + gline->gl_host = NULL; gline->gl_next = BadChanGlineList; /* then link it into list */ gline->gl_prev_p = &BadChanGlineList; if (BadChanGlineList) - BadChanGlineList->gl_prev_p = &agline->gl_next; - BadChanGlineList = agline; + BadChanGlineList->gl_prev_p = &gline->gl_next; + BadChanGlineList = gline; } else { - canon_userhost(userhost, &user, &host, "*"); /* find user and host */ - DupString(gline->gl_user, user); /* remember them... */ - DupString(gline->gl_host, host); + if (*user != '$') + DupString(gline->gl_host, host); + else + gline->gl_host = NULL; - if (check_if_ipmask(host)) /* mark if it's an IP mask */ + if (*user != '$' && ipmask_parse(host, &gline->gl_addr, &gline->gl_bits)) gline->gl_flags |= GLINE_IPMASK; gline->gl_next = GlobalGlineList; /* then link it into list */ gline->gl_prev_p = &GlobalGlineList; if (GlobalGlineList) - GlobalGlineList->gl_prev_p = &agline->gl_next; - GlobalGlineList = agline; + GlobalGlineList->gl_prev_p = &gline->gl_next; + GlobalGlineList = gline; } - return agline; + return gline; } +/** Check local clients against a new G-line. + * If the G-line is inactive or a badchan, return immediately. + * Otherwise, if any users match it, disconnect them. + * @param[in] cptr Peer connect that sent the G-line. + * @param[in] sptr Client that originated the G-line. + * @param[in] gline New G-line to check. + * @return Zero, unless \a sptr G-lined himself, in which case CPTR_KILLED. + */ static int do_gline(struct Client *cptr, struct Client *sptr, struct Gline *gline) { struct Client *acptr; int fd, retval = 0, tval; + if (feature_bool(FEAT_DISABLE_GLINES)) + return 0; /* G-lines are disabled */ + + if (GlineIsBadChan(gline)) /* no action taken on badchan glines */ + return 0; if (!GlineIsActive(gline)) /* no action taken on inactive glines */ return 0; @@ -108,254 +211,771 @@ do_gline(struct Client *cptr, struct Client *sptr, struct Gline *gline) * get the users! */ if ((acptr = LocalClientArray[fd])) { - if (!acptr->user) + if (!cli_user(acptr)) continue; - if ((GlineIsIpMask(gline) ? match(gline->host, acptr->sock_ip) : - match(gline->host, acptr->sockhost)) == 0 && - (!acptr->user->username || - match(gline->name, acptr->user->username) == 0)) { - /* ok, here's one that got G-lined */ - sendto_one(acptr, ":%s %d %s :*** %s.", me.name, ERR_YOUREBANNEDCREEP, - acptr->name, gline->reason); - - /* let the ops know about it */ - sendto_op_mask(SNO_GLINE, "G-line active for %s", - get_client_name(acptr, FALSE)); - - /* and get rid of him */ - if ((tval = exit_client_msg(cptr, acptr, &me, "G-lined (%s)", - gline->reason))) - retval = tval; /* retain killed status */ + if (GlineIsRealName(gline)) { /* Realname Gline */ + Debug((DEBUG_DEBUG,"Realname Gline: %s %s",(cli_info(acptr)), + gline->gl_user+2)); + if (match(gline->gl_user+2, cli_info(acptr)) != 0) + continue; + Debug((DEBUG_DEBUG,"Matched!")); + } else { /* Host/IP gline */ + if (cli_user(acptr)->username && + match(gline->gl_user, (cli_user(acptr))->username) != 0) + continue; + + if (GlineIsIpMask(gline)) { + if (!ipmask_check(&cli_ip(acptr), &gline->gl_addr, gline->gl_bits)) + continue; + } + else { + if (match(gline->gl_host, cli_sockhost(acptr)) != 0) + continue; + } } + + /* ok, here's one that got G-lined */ + send_reply(acptr, SND_EXPLICIT | ERR_YOUREBANNEDCREEP, ":%s", + gline->gl_reason); + + /* let the ops know about it */ + sendto_opmask_butone(0, SNO_GLINE, "G-line active for %s", + get_client_name(acptr, SHOW_IP)); + + /* and get rid of him */ + if ((tval = exit_client_msg(cptr, acptr, &me, "G-lined (%s)", + gline->gl_reason))) + retval = tval; /* retain killed status */ } } - return retval; } -static void -propagate_gline(struct Client *cptr, struct Client *sptr, struct Gline *gline) +/** + * 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. + * @param[in] mask G-line mask to check. + * @return One of CHECK_REJECTED, CHECK_OVERRIDABLE, or CHECK_APPROVED. + */ +static int +gline_checkmask(char *mask) { - if (GlineIsLocal(gline)) /* don't propagate local glines */ - return; + 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 */ + ++mask; + ipmask = strtoul(mask, &mask, 10); + + /* sanity-check to date */ + if (*mask || (flags & (MASK_WILDS | MASK_IP)) != MASK_IP) + return CHECK_REJECTED; + if (!dots) { + if (ipmask > 128) + return CHECK_REJECTED; + if (ipmask < 128) + flags |= MASK_WILDS; + } else { + if (dots != 3 || ipmask > 32) + return CHECK_REJECTED; + if (ipmask < 32) + flags |= MASK_WILDS; + } - if (IsUser(sptr)) { /* select appropriate source */ - assert(0 != gline->gl_lastmod); - sendto_serv_butone(cptr, "%s%s " TOK_GLINE " * %c%s%s%s " TIME_T_FMT " " - TIME_T_FMT " :%s", NumNick(sptr), - GlineIsActive(gline) ? '+' : '-', gline->gl_user, - GlineIsBadChan(gline) ? "" : "@", - GlineIsBadChan(gline) ? "" : gline->gl_host, - gline->gl_expire, gline->gl_lastmod, gline->gl_reason); - } else { - if (gline->gl_lastmod) - sendto_serv_butone(cptr, "%s " TOK_GLINE " * %c%s%s%s " TIME_T_FMT " " - TIME_T_FMT " :%s", NumServ(sptr), - GlineIsActive(gline) ? '+' : '-', gline->gl_user, - GlineIsBadChan(gline) ? "" : "@", - GlineIsBadChan(gline) ? "" : gline->gl_host, - gline->gl_expire, gline->gl_lastmod, - gline->gl_reason); - else - sendto_serv_butone(cptr, "%s " TOK_GLINE " * %c%s%s%s " TIME_T_FMT - " :%s", NumServ(sptr), - GlineIsActive(gline) ? '+' : '-', gline->gl_user, - GlineIsBadChan(gline) ? "" : "@", - GlineIsBadChan(gline) ? "" : gline->gl_host, - gline->gl_expire, gline->gl_reason); + flags |= MASK_HALT; /* Halt the ipmask calculation */ + break; /* get out of the loop */ + } else if (!IsIP6Char(*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; +} + +/** Forward a G-line to other servers. + * @param[in] cptr Client that sent us the G-line. + * @param[in] sptr Client that originated the G-line. + * @param[in] gline G-line to forward. + * @return Zero. + */ +static int +gline_propagate(struct Client *cptr, struct Client *sptr, struct Gline *gline) +{ + if (GlineIsLocal(gline)) + return 0; + + assert(gline->gl_lastmod); + + sendcmdto_serv_butone(sptr, CMD_GLINE, cptr, "* %c%s%s%s %Tu %Tu %Tu :%s", + GlineIsRemActive(gline) ? '+' : '-', gline->gl_user, + gline->gl_host ? "@" : "", + gline->gl_host ? gline->gl_host : "", + 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; } -int +/** 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. + * \li A string starting with $R and followed by a mask to match against their realname. + * \li A user\@IP mask (user\@ part optional) to create an IP-based ban. + * \li A user\@host mask (user\@ part optional) to create a hostname ban. + * + * @param[in] cptr Client that sent us the G-line. + * @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 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. + * @return Zero or CPTR_KILLED, depending on whether \a sptr is suicidal. + */ +int gline_add(struct Client *cptr, struct Client *sptr, char *userhost, - char *reason, time_t expire, time_t lastmod, unsigned int flags) + char *reason, time_t expire, time_t lastmod, time_t lifetime, + unsigned int flags) { struct Gline *agline; + char uhmask[USERLEN + HOSTLEN + 2]; + char *user, *host; + int tmp; 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, + expire, lastmod, lifetime, flags)); + + 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 = NULL; + } else if (*userhost == '$') { + switch (userhost[1]) { + 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)); + 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); + } + } 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, 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_error_to_client(sptr, ERR_BADEXPIRE, expire); + 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 += TStime(); /* convert from lifetime to timestamp */ + if (!lifetime) /* no lifetime set, use expiration time */ + lifetime = expire; -#ifdef BADCHAN - if (*userhost == '#' || *userhost == '&' || *userhost == '+') - flags |= GLINE_BADCHAN; -#endif + /* lifetime is already an absolute timestamp */ /* Inform ops... */ - sendto_op_mask(SNO_GLINE, "%s adding %s %s for %s, expiring at " - TIME_T_FMT ": %s", - IsServer(sptr) ? sptr->name : sptr->user->server->name, - flags & GLINE_LOCAL ? "local" : "global", - flags & GLINE_BADCHAN ? "BADCHAN" : "GLINE", userhost, - expire, reason); - -#ifdef GPATH + sendto_opmask_butone(0, ircd_strncmp(reason, "AUTO", 4) ? SNO_GLINE : + 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, reason); + /* and log it */ - if (IsServer(sptr)) - write_log(GPATH, "# " TIME_T_FMT " %s adding %s %s for %s, expiring at " - TIME_T_FMT ": %s\n", TStime(), sptr->name, - flags & GLINE_LOCAL ? "local" : "global", - flags & GLINE_BADCHAN ? "BADCHAN" : "GLINE", userhost, expire, - reason); - else - write_log(GPATH, "# " TIME_T_FMT " %s!%s@%s adding %s %s for %s, " - "expiring at " TIME_T_FMT ": %s\n", TStime(), sptr->name, - sptr->user->username, sptr->user->host, - flags & GLINE_LOCAL ? "local" : "global", - flags & GLINE_BADCHAN ? "BADCHAN" : "GLINE", userhost, expire, - reason); -#endif /* GPATH */ + log_write(LS_GLINE, L_INFO, LOG_NOSNOTICE, + "%#C adding %s %s for %s%s%s, expiring at %Tu: %s", sptr, + flags & GLINE_LOCAL ? "local" : "global", + flags & GLINE_BADCHAN ? "BADCHAN" : "GLINE", user, + flags & (GLINE_BADCHAN|GLINE_REALNAME) ? "" : "@", + flags & (GLINE_BADCHAN|GLINE_REALNAME) ? "" : host, + expire, reason); /* make the gline */ - agline = make_gline(userhost, reason, expire, lastmod, flags); + agline = make_gline(user, host, reason, expire, lastmod, lifetime, flags); - propagate_gline(cptr, sptr, agline); - - if (GlineIsBadChan(agline)) - return 0; + /* since we've disabled overlapped G-line checking, agline should + * never be NULL... + */ + assert(agline); -#ifdef GPATH - /* this can be inserted into the conf */ - write_log(GPATH, "%c:%s:%s:%s\n", GlineIsIpMask(agline) ? 'k' : 'K', - GlineHost(agline), GlineReason(agline), GlineUser(agline)); -#endif /* GPATH */ + gline_propagate(cptr, sptr, agline); return do_gline(cptr, sptr, agline); /* knock off users if necessary */ } +/** Activate a currently inactive G-line. + * @param[in] cptr Peer that told us to activate the G-line. + * @param[in] sptr Client that originally thought it was a good idea. + * @param[in] gline G-line to activate. + * @param[in] lastmod New value for last modification timestamp. + * @param[in] flags 0 if the activation should be propagated, GLINE_LOCAL if not. + * @return Zero, unless \a sptr had a death wish (in which case CPTR_KILLED). + */ int gline_activate(struct Client *cptr, struct Client *sptr, struct Gline *gline, - time_t lastmod) + time_t lastmod, unsigned int flags) { + unsigned int saveflags = 0; + assert(0 != gline); - gline->gl_flags |= GLINE_ACTIVE; - gline->gl_lastmod = lastmod; + saveflags = gline->gl_flags; + + if (flags & GLINE_LOCAL) + gline->gl_flags &= ~GLINE_LDEACT; + else { + gline->gl_flags |= GLINE_ACTIVE; + + if (gline->gl_lastmod) { + if (gline->gl_lastmod >= lastmod) /* force lastmod to increase */ + gline->gl_lastmod++; + else + gline->gl_lastmod = lastmod; + } + } + + if ((saveflags & GLINE_ACTMASK) == GLINE_ACTIVE) + return 0; /* was active to begin with */ /* Inform ops and log it */ - sendto_op_mask(SNO_GLINE, "%s activating %s %s for %s%s%s, expiring at " - TIME_T_FMT ": %s", - IsServer(sptr) ? sptr->name : sptr->user->server->name, - GlineIsLocal(gline) ? "local" : "global", - GlineIsBadChan(gline) ? "BADCHAN" : "GLINE", - gline->gl_user, GlineIsBadChan(gline) ? "" : "@", - GlineIsBadChan(gline) ? "" : gline->gl_host, gline->gl_expire, - gline->gl_reason); - -#ifdef GPATH - if (IsServer(sptr)) - write_log(GPATH, "# " TIME_T_FMT " %s activating %s %s for %s%s%s, " - "expiring at " TIME_T_FMT ": %s\n", TStime(), sptr->name, - GlineIsLocal(gline) ? "local" : "global", - GlineIsBadChan(gline) ? "BADCHAN" : "GLINE", - gline->gl_user, GlineIsBadChan(gline) ? "" : "@", - GlineIsBadChan(gline) ? "" : gline->gl_host, gline->gl_expire, - gline->gl_reason); - else - write_log(GPATH, "# " TIME_T_FMT " %s!%s@%s activating %s %s for " - "%s%s%s, expiring at " TIME_T_FMT ": %s\n", TStime(), sptr->name, - sptr->user->username, sptr->user->host, - GlineIsLocal(gline) ? "local" : "global", - GlineIsBadChan(gline) ? "BADCHAN" : "GLINE", - gline->gl_user, GlineIsBadChan(gline) ? "" : "@", - GlineIsBadChan(gline) ? "" : gline->gl_host, gline->gl_expire, - gline->gl_reason); -#endif /* GPATH */ - - propagate_gline(cptr, sptr, gline); - - return GlineIsBadChan(gline) ? 0 : do_gline(cptr, sptr, gline); + sendto_opmask_butone(0, SNO_GLINE, "%s activating global %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), + GlineIsBadChan(gline) ? "BADCHAN" : "GLINE", + gline->gl_user, gline->gl_host ? "@" : "", + gline->gl_host ? gline->gl_host : "", + 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, gline->gl_reason); + + if (!(flags & GLINE_LOCAL)) /* don't propagate local changes */ + gline_propagate(cptr, sptr, gline); + + return do_gline(cptr, sptr, gline); } +/** Deactivate a G-line. + * @param[in] cptr Peer that gave us the message. + * @param[in] sptr Client that initiated the deactivation. + * @param[in] gline G-line to deactivate. + * @param[in] lastmod New value for G-line last modification timestamp. + * @param[in] flags GLINE_LOCAL to only deactivate locally, 0 to propagate. + * @return Zero. + */ int gline_deactivate(struct Client *cptr, struct Client *sptr, struct Gline *gline, - time_t lastmod) + time_t lastmod, unsigned int flags) { + unsigned int saveflags = 0; + char *msg; + assert(0 != gline); - gline->gl_flags &= ~GLINE_ACTIVE; - gline->gl_lastmod = lastmod; + saveflags = gline->gl_flags; + + if (GlineIsLocal(gline)) + msg = "removing local"; + else if (!gline->gl_lastmod && !(flags & GLINE_LOCAL)) { + msg = "removing global"; + gline->gl_flags &= ~GLINE_ACTIVE; /* propagate a - */ + } else { + msg = "deactivating global"; + + if (flags & GLINE_LOCAL) + gline->gl_flags |= GLINE_LDEACT; + else { + gline->gl_flags &= ~GLINE_ACTIVE; + + if (gline->gl_lastmod) { + if (gline->gl_lastmod >= lastmod) + gline->gl_lastmod++; + else + gline->gl_lastmod = lastmod; + } + } + + if ((saveflags & GLINE_ACTMASK) != GLINE_ACTIVE) + return 0; /* was inactive to begin with */ + } /* Inform ops and log it */ - sendto_op_mask(SNO_GLINE, "%s deactivating %s %s for %s%s%s, expiring at " - TIME_T_FMT ": %s", - IsServer(sptr) ? sptr->name : sptr->user->server->name, - GlineIsLocal(gline) ? "local" : "global", - GlineIsBadChan(gline) ? "BADCHAN" : "GLINE", - gline->gl_user, GlineIsBadChan(gline) ? "" : "@", - GlineIsBadChan(gline) ? "" : gline->gl_host, gline->gl_expire, - gline->gl_reason); - -#ifdef GPATH - if (IsServer(sptr)) - write_log(GPATH, "# " TIME_T_FMT " %s deactivating %s %s for %s%s%s, " - "expiring at " TIME_T_FMT ": %s\n", TStime(), sptr->name, - GlineIsLocal(gline) ? "local" : "global", - GlineIsBadChan(gline) ? "BADCHAN" : "GLINE", - gline->gl_user, GlineIsBadChan(gline) ? "" : "@", - GlineIsBadChan(gline) ? "" : gline->gl_host, gline->gl_expire, - gline->gl_reason); - else - write_log(GPATH, "# " TIME_T_FMT " %s!%s@%s deactivating %s %s for " - "%s%s%s, expiring at " TIME_T_FMT ": %s\n", TStime(), sptr->name, - sptr->user->username, sptr->user->host, - GlineIsLocal(gline) ? "local" : "global", - GlineIsBadChan(gline) ? "BADCHAN" : "GLINE", - gline->gl_user, GlineIsBadChan(gline) ? "" : "@", - GlineIsBadChan(gline) ? "" : gline->gl_host, gline->gl_expire, - gline->gl_reason); -#endif /* GPATH */ - - propagate_gline(cptr, sptr, gline); + sendto_opmask_butone(0, SNO_GLINE, "%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), + msg, GlineIsBadChan(gline) ? "BADCHAN" : "GLINE", + gline->gl_user, gline->gl_host ? "@" : "", + gline->gl_host ? gline->gl_host : "", + 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, gline->gl_reason); + + if (!(flags & GLINE_LOCAL)) /* don't propagate local changes */ + gline_propagate(cptr, sptr, gline); + + /* if it's a local gline or a Uworld gline (and not locally deactivated).. */ + if (GlineIsLocal(gline) || (!gline->gl_lastmod && !(flags & GLINE_LOCAL))) + gline_free(gline); /* get rid of it */ return 0; } +/** Modify a global G-line. + * @param[in] cptr Client that sent us the G-line modification. + * @param[in] sptr Client that originated the G-line modification. + * @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 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. + * @return Zero or CPTR_KILLED, depending on whether \a sptr is suicidal. + */ +int +gline_modify(struct Client *cptr, struct Client *sptr, struct Gline *gline, + enum GlineAction action, char *reason, time_t expire, + time_t lastmod, time_t lifetime, unsigned int flags) +{ + char buf[BUFSIZE], *op = ""; + int pos = 0, non_auto = 0; + + assert(gline); + assert(!GlineIsLocal(gline)); + + Debug((DEBUG_DEBUG, "gline_modify(\"%s\", \"%s\", \"%s%s%s\", %s, \"%s\", " + "%Tu, %Tu, %Tu, 0x%04x)", cli_name(cptr), cli_name(sptr), + gline->gl_user, gline->gl_host ? "@" : "", + gline->gl_host ? gline->gl_host : "", + action == GLINE_ACTIVATE ? "GLINE_ACTIVATE" : + (action == GLINE_DEACTIVATE ? "GLINE_DEACTIVATE" : + (action == GLINE_LOCAL_ACTIVATE ? "GLINE_LOCAL_ACTIVATE" : + (action == GLINE_LOCAL_DEACTIVATE ? "GLINE_LOCAL_DEACTIVATE" : + (action == GLINE_MODIFY ? "GLINE_MODIFY" : "")))), + reason, expire, lastmod, lifetime, flags)); + + /* First, let's check lastmod... */ + if (action != GLINE_LOCAL_ACTIVATE && action != GLINE_LOCAL_DEACTIVATE) { + if (GlineLastMod(gline) > lastmod) { /* we have a more recent version */ + if (IsBurstOrBurstAck(cptr)) + return 0; /* middle of a burst, it'll resync on its own */ + return gline_resend(cptr, gline); /* resync the server */ + } else if (GlineLastMod(gline) == lastmod) + return 0; /* we have that version of the G-line... */ + } + + /* 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 <= TStime() || expire > TStime() + GLINE_MAX_EXPIRE)) { + if (!IsServer(sptr) && MyConnect(sptr)) /* bad expiration time */ + send_reply(sptr, ERR_BADEXPIRE, expire); + return 0; + } + } else + flags &= ~GLINE_EXPIRE; + + /* Now check to see if there's any change... */ + if ((flags & GLINE_EXPIRE) && expire == gline->gl_expire) { + flags &= ~GLINE_EXPIRE; /* no change to expiration time... */ + expire = 0; + } + + /* Next, check out lifetime--this one's a bit trickier... */ + if (!(flags & GLINE_LIFETIME) || !lifetime) + lifetime = gline->gl_lifetime; /* use G-line lifetime */ + + lifetime = IRCD_MAX(lifetime, expire); /* set lifetime to the max */ + + /* OK, let's see which is greater... */ + if (lifetime > gline->gl_lifetime) + flags |= GLINE_LIFETIME; /* have to update lifetime */ + else { + flags &= ~GLINE_LIFETIME; /* no change to lifetime */ + lifetime = 0; + } + + /* Finally, let's see if the reason needs to be updated */ + if ((flags & GLINE_REASON) && reason && + !ircd_strcmp(gline->gl_reason, reason)) + flags &= ~GLINE_REASON; /* no changes to the reason */ + + /* OK, now let's take a look at the action... */ + if ((action == GLINE_ACTIVATE && (gline->gl_flags & GLINE_ACTIVE)) || + (action == GLINE_DEACTIVATE && !(gline->gl_flags & GLINE_ACTIVE)) || + (action == GLINE_LOCAL_ACTIVATE && + (gline->gl_state == GLOCAL_ACTIVATED)) || + (action == GLINE_LOCAL_DEACTIVATE && + (gline->gl_state == GLOCAL_DEACTIVATED)) || + /* can't activate an expired G-line */ + 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", + flags, action == GLINE_ACTIVATE ? "GLINE_ACTIVATE" : + (action == GLINE_DEACTIVATE ? "GLINE_DEACTIVATE" : + (action == GLINE_LOCAL_ACTIVATE ? "GLINE_LOCAL_ACTIVATE" : + (action == GLINE_LOCAL_DEACTIVATE ? "GLINE_LOCAL_DEACTIVATE" : + (action == GLINE_MODIFY ? "GLINE_MODIFY" : "")))))); + + /* If there are no changes to perform, do no changes */ + if (!(flags & GLINE_UPDATE) && action == GLINE_MODIFY) + return 0; + + /* Now we know what needs to be changed, so let's process the changes... */ + + /* Start by updating lastmod, if indicated... */ + if (action != GLINE_LOCAL_ACTIVATE && action != GLINE_LOCAL_DEACTIVATE) + gline->gl_lastmod = lastmod; + + /* Then move on to activity status changes... */ + switch (action) { + case GLINE_ACTIVATE: /* Globally activating G-line */ + gline->gl_flags |= GLINE_ACTIVE; /* make it active... */ + gline->gl_state = GLOCAL_GLOBAL; /* reset local activity state */ + pos += ircd_snprintf(0, buf, sizeof(buf), " globally activating G-line"); + op = "+"; /* operation for G-line propagation */ + break; + + case GLINE_DEACTIVATE: /* Globally deactivating G-line */ + gline->gl_flags &= ~GLINE_ACTIVE; /* make it inactive... */ + gline->gl_state = GLOCAL_GLOBAL; /* reset local activity state */ + pos += ircd_snprintf(0, buf, sizeof(buf), " globally deactivating G-line"); + op = "-"; /* operation for G-line propagation */ + break; + + case GLINE_LOCAL_ACTIVATE: /* Locally activating G-line */ + gline->gl_state = GLOCAL_ACTIVATED; /* make it locally active */ + pos += ircd_snprintf(0, buf, sizeof(buf), " locally activating G-line"); + break; + + case GLINE_LOCAL_DEACTIVATE: /* Locally deactivating G-line */ + gline->gl_state = GLOCAL_DEACTIVATED; /* make it locally inactive */ + pos += ircd_snprintf(0, buf, sizeof(buf), " locally deactivating G-line"); + break; + + case GLINE_MODIFY: /* no change to activity status */ + break; + } + + /* Handle expiration changes... */ + if (flags & GLINE_EXPIRE) { + gline->gl_expire = expire; /* save new expiration time */ + if (pos < BUFSIZE) + pos += ircd_snprintf(0, buf + pos, sizeof(buf) - pos, + "%s%s changing expiration time to %Tu", + pos ? ";" : "", + pos && !(flags & (GLINE_LIFETIME | GLINE_REASON)) ? + " and" : "", expire); + } + + /* Next, handle lifetime changes... */ + if (flags & GLINE_LIFETIME) { + gline->gl_lifetime = lifetime; /* save new lifetime */ + if (pos < BUFSIZE) + pos += ircd_snprintf(0, buf + pos, sizeof(buf) - pos, + "%s%s extending record lifetime to %Tu", + pos ? ";" : "", pos && !(flags & GLINE_REASON) ? + " and" : "", lifetime); + } + + /* 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) + pos += ircd_snprintf(0, buf + pos, sizeof(buf) - pos, + "%s%s changing reason to \"%s\"", + pos ? ";" : "", pos ? " and" : "", reason); + } + + /* All right, inform ops... */ + 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", + gline->gl_user, gline->gl_host ? "@" : "", + gline->gl_host ? gline->gl_host : "", buf); + + /* and log the change */ + log_write(LS_GLINE, L_INFO, LOG_NOSNOTICE, + "%#C modifying global %s for %s%s%s:%s", sptr, + GlineIsBadChan(gline) ? "BADCHAN" : "GLINE", gline->gl_user, + gline->gl_host ? "@" : "", gline->gl_host ? gline->gl_host : "", + buf); + + /* We'll be simple for this release, but we can update this to change + * the propagation syntax on future updates + */ + if (action != GLINE_LOCAL_ACTIVATE && action != GLINE_LOCAL_DEACTIVATE) + sendcmdto_serv_butone(sptr, CMD_GLINE, cptr, + "* %s%s%s%s%s %Tu %Tu %Tu :%s", + flags & GLINE_OPERFORCE ? "!" : "", op, + gline->gl_user, gline->gl_host ? "@" : "", + gline->gl_host ? gline->gl_host : "", + 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); +} + +/** Destroy a local G-line. + * @param[in] cptr Peer that gave us the message. + * @param[in] sptr Client that initiated the destruction. + * @param[in] gline G-line to destroy. + * @return Zero. + */ +int +gline_destroy(struct Client *cptr, struct Client *sptr, struct Gline *gline) +{ + assert(gline); + assert(GlineIsLocal(gline)); + + /* Inform ops and log it */ + sendto_opmask_butone(0, SNO_GLINE, "%s removing local %s for %s%s%s", + (feature_bool(FEAT_HIS_SNOTICES) || IsServer(sptr)) ? + cli_name(sptr) : cli_name((cli_user(sptr))->server), + GlineIsBadChan(gline) ? "BADCHAN" : "GLINE", + gline->gl_user, gline->gl_host ? "@" : "", + gline->gl_host ? gline->gl_host : ""); + log_write(LS_GLINE, L_INFO, LOG_NOSNOTICE, + "%#C removing local %s for %s%s%s", sptr, + GlineIsBadChan(gline) ? "BADCHAN" : "GLINE", gline->gl_user, + gline->gl_host ? "@" : "", gline->gl_host ? gline->gl_host : ""); + + gline_free(gline); /* get rid of the G-line */ + + return 0; /* convenience return */ +} + +/** Find a G-line for a particular mask, guided by certain flags. + * Certain bits in \a flags are interpreted specially: + *
+ *
GLINE_ANY
Search both BadChans and user G-lines.
+ *
GLINE_BADCHAN
Search BadChans.
+ *
GLINE_GLOBAL
Only match global G-lines.
+ *
GLINE_LOCAL
Only match local G-lines.
+ *
GLINE_LASTMOD
Only match G-lines with a last modification time.
+ *
GLINE_EXACT
Require an exact match of G-line mask.
+ *
anything else
Search user G-lines.
+ *
+ * @param[in] userhost Mask to search for. + * @param[in] flags Bitwise combination of GLINE_* flags. + * @return First matching G-line, or NULL if none are found. + */ struct Gline * gline_find(char *userhost, unsigned int flags) { - struct Gline *gline; + struct Gline *gline = 0; struct Gline *sgline; char *user, *host, *t_uh; if (flags & (GLINE_BADCHAN | GLINE_ANY)) { - for (gline = BadChanGlineList; gline; gline = sgline) { - sgline = gline->gl_next; - - if (gline->gl_expire <= TStime()) - gline_free(gline); - else if (match(gline->gl_user, userhost) == 0) + gliter(BadChanGlineList, gline, sgline) { + 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) : + match(gline->gl_user, userhost)) == 0) return gline; } } - if ((flags & (GLINE_BADCHAN | GLINE_ANY)) == GLINE_BADCHAN) + if ((flags & (GLINE_BADCHAN | GLINE_ANY)) == GLINE_BADCHAN || + *userhost == '#' || *userhost == '&') return 0; DupString(t_uh, userhost); - canon_userhost(t_uh, &user, &host, 0); + canon_userhost(t_uh, &user, &host, "*"); - for (gline = GlobalGlineList; gline; gline = sgline) { - sgline = gline->gl_next; - - if (gline->gl_expire <= TStime()) - gline_free(gline); - else if (match(gline->host, host) == 0 && - ((!user && ircd_strcmp(gline->user, "*") == 0) || - match(gline->user, user) == 0)) - break; + gliter(GlobalGlineList, gline, sgline) { + if ((flags & (GlineIsLocal(gline) ? GLINE_GLOBAL : GLINE_LOCAL)) || + (flags & GLINE_LASTMOD && !gline->gl_lastmod)) + continue; + else if (flags & GLINE_EXACT) { + if (((gline->gl_host && host && ircd_strcmp(gline->gl_host, host) == 0) + || (!gline->gl_host && !host)) && + (ircd_strcmp(gline->gl_user, user) == 0)) + break; + } else { + if (((gline->gl_host && host && match(gline->gl_host, host) == 0) + || (!gline->gl_host && !host)) && + (match(gline->gl_user, user) == 0)) + break; + } } MyFree(t_uh); @@ -363,27 +983,53 @@ gline_find(char *userhost, unsigned int flags) return gline; } +/** Find a matching G-line for a user. + * @param[in] cptr Client to compare against. + * @param[in] flags Bitwise combination of GLINE_GLOBAL and/or + * GLINE_LASTMOD to limit matches. + * @return Matching G-line, or NULL if none are found. + */ struct Gline * -gline_lookup(struct Client *cptr) +gline_lookup(struct Client *cptr, unsigned int flags) { struct Gline *gline; struct Gline *sgline; - for (gline = GlobalGlineList; gline; gline = sgline) { - sgline = gline->gl_next; + gliter(GlobalGlineList, gline, sgline) { + if ((flags & GLINE_GLOBAL && gline->gl_flags & GLINE_LOCAL) || + (flags & GLINE_LASTMOD && !gline->gl_lastmod)) + continue; + + if (GlineIsRealName(gline)) { + Debug((DEBUG_DEBUG,"realname gline: '%s' '%s'",gline->gl_user,cli_info(cptr))); + if (match(gline->gl_user+2, cli_info(cptr)) != 0) + continue; + } + else { + if (match(gline->gl_user, (cli_user(cptr))->username) != 0) + continue; - if (gline->gl_expire <= TStime()) - gline_free(gline); - else if ((GlineIsIpMask(gline) ? - match(gline->gl_host, cptr->sock_ip) : - match(gline->gl_host, cptr->sockhost)) == 0 && - match(gline->gl_user, cptr->user->username) == 0) + if (GlineIsIpMask(gline)) { + if (!ipmask_check(&cli_ip(cptr), &gline->gl_addr, gline->gl_bits)) + continue; + } + else { + if (match(gline->gl_host, (cli_user(cptr))->realhost) != 0) + continue; + } + } + if (GlineIsActive(gline)) return gline; } - + /* + * No Glines matched + */ return 0; } +/** Delink and free a G-line. + * @param[in] gline G-line to free. + */ void gline_free(struct Gline *gline) { @@ -400,57 +1046,62 @@ gline_free(struct Gline *gline) MyFree(gline); } +/** Burst all known global G-lines to another server. + * @param[in] cptr Destination of burst. + */ void gline_burst(struct Client *cptr) { struct Gline *gline; struct Gline *sgline; - for (gline = GlobalGlineList; gline; gline = sgline) { /* all glines */ - sgline = gline->gl_next; - - if (gline->gl_expire <= TStime()) /* expire any that need expiring */ - gline_free(gline); - else if (!GlineIsLocal(gline) && gline->gl_lastmod) - sendto_one(cptr, "%s " TOK_GLINE " * %c%s@%s " TIME_T_FMT " " TIME_T_FMT - " :%s", NumServ(&me), GlineIsActive(gline) ? '+' : '-', - gline->gl_user, gline->gl_host, gline->gl_expire, - gline->gl_lastmod, gline->gl_reason); + gliter(GlobalGlineList, gline, sgline) { + if (!GlineIsLocal(gline) && gline->gl_lastmod) + sendcmdto_one(&me, CMD_GLINE, cptr, "* %c%s%s%s %Tu %Tu %Tu :%s", + GlineIsRemActive(gline) ? '+' : '-', gline->gl_user, + gline->gl_host ? "@" : "", + gline->gl_host ? gline->gl_host : "", + gline->gl_expire - TStime(), gline->gl_lastmod, + gline->gl_lifetime, gline->gl_reason); } - for (gline = BadChanGlineList; gline; gline = sgline) { /* all glines */ - sgline = gline->gl_next; - - if (gline->gl_expire <= TStime()) /* expire any that need expiring */ - gline_free(gline); - else if (!GlineIsLocal(gline) && gline->gl_lastmod) - sendto_one(cptr, "%s " TOK_GLINE " * %c%s " TIME_T_FMT " " TIME_T_FMT - " :%s", NumServ(&me), GlineIsActive(gline) ? '+' : '-', - gline->gl_user, gline->gl_expire, gline->gl_lastmod, - 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 - TStime(), gline->gl_lastmod, + gline->gl_lifetime, gline->gl_reason); } } +/** Send a G-line to another server. + * @param[in] cptr Who to inform of the G-line. + * @param[in] gline G-line to send. + * @return Zero. + */ int gline_resend(struct Client *cptr, struct Gline *gline) { if (GlineIsLocal(gline) || !gline->gl_lastmod) return 0; - if (GlineIsBadChan(gline)) - sendto_one(cptr, "%s " TOK_GLINE " * %c%s " TIME_T_FMT " " TIME_T_FMT - " :%s", NumServ(&me), GlineIsActive(gline) ? '+' : '-', - gline->gl_user, gline->gl_expire, gline->gl_lastmod, - gline->gl_reason); - else - sendto_one(cptr, "%s " TOK_GLINE " * %c%s@%s " TIME_T_FMT " " TIME_T_FMT - " :%s", NumServ(&me), GlineIsActive(gline) ? '+' : '-', - gline->gl_user, gline->gl_host, gline->gl_expire, - gline->gl_lastmod, gline->gl_reason); + sendcmdto_one(&me, CMD_GLINE, cptr, "* %c%s%s%s %Tu %Tu %Tu :%s", + GlineIsRemActive(gline) ? '+' : '-', gline->gl_user, + gline->gl_host ? "@" : "", + gline->gl_host ? gline->gl_host : "", + gline->gl_expire - TStime(), gline->gl_lastmod, + gline->gl_lifetime, gline->gl_reason); return 0; } +/** Display one or all G-lines to a user. + * If \a userhost is not NULL, only send the first matching G-line. + * Otherwise send the whole list. + * @param[in] sptr User asking for G-line list. + * @param[in] userhost G-line mask to search for (or NULL). + * @return Zero. + */ int gline_list(struct Client *sptr, char *userhost) { @@ -458,293 +1109,97 @@ gline_list(struct Client *sptr, char *userhost) struct Gline *sgline; if (userhost) { - if (!(gline = gline_find(userhost, GLINE_ANY))) { /* no such gline */ - send_error_to_client(sptr, ERR_NOSUCHGLINE, userhost); - return 0; - } + if (!(gline = gline_find(userhost, GLINE_ANY))) /* no such gline */ + return send_reply(sptr, ERR_NOSUCHGLINE, userhost); /* send gline information along */ - sendto_one(sptr, rpl_str(GLIST), me.name, sptr->name, gline->gl_user, - GlineIsBadChan(gline) ? "" : "@", - GlineIsBadChan(gline) ? "" : gline->gl_host, gline->gl_expire, - GlineIsLocal(gline) ? me.name : "*", - GlineIsActive(gline) ? '+' : '-', gline->gl_reason); + send_reply(sptr, RPL_GLIST, gline->gl_user, + gline->gl_host ? "@" : "", + gline->gl_host ? gline->gl_host : "", + gline->gl_expire, gline->gl_lastmod, + gline->gl_lifetime, + GlineIsLocal(gline) ? cli_name(&me) : "*", + gline->gl_state == GLOCAL_ACTIVATED ? ">" : + (gline->gl_state == GLOCAL_DEACTIVATED ? "<" : ""), + GlineIsRemActive(gline) ? '+' : '-', gline->gl_reason); } else { - for (gline = GlobalGlineList; gline; gline = sgline) { - sgline = gline->gl_next; - - if (gline->gl_expire <= TStime()) - gline_free(gline); - else - sendto_one(sptr, rpl_str(GLIST), me.name, sptr->name, gline->gl_user, - "@", gline->gl_host, gline->gl_expire, - GlineIsLocal(gline) ? me.name : "*", - GlineIsActive(gline) ? '+' : '-', gline->gl_reason); + gliter(GlobalGlineList, gline, sgline) { + send_reply(sptr, RPL_GLIST, gline->gl_user, + gline->gl_host ? "@" : "", + gline->gl_host ? gline->gl_host : "", + gline->gl_expire, gline->gl_lastmod, + gline->gl_lifetime, + GlineIsLocal(gline) ? cli_name(&me) : "*", + gline->gl_state == GLOCAL_ACTIVATED ? ">" : + (gline->gl_state == GLOCAL_DEACTIVATED ? "<" : ""), + GlineIsRemActive(gline) ? '+' : '-', gline->gl_reason); } - for (gline = BadChanGlineList; gline; gline = sgline) { - sgline = gline->gl_next; - - if (gline->gl_expire <= TStime()) - gline_free(gline); - else - sendto_one(sptr, rpl_str(GLIST), me.name, sptr->name, gline->gl_user, - "", "", gline->gl_expire, - GlineIsLocal(gline) ? me.name : "*", - GlineIsActive(gline) ? '+' : '-', gline->gl_reason); + gliter(BadChanGlineList, gline, sgline) { + send_reply(sptr, RPL_GLIST, gline->gl_user, "", "", + gline->gl_expire, gline->gl_lastmod, + gline->gl_lifetime, + GlineIsLocal(gline) ? cli_name(&me) : "*", + gline->gl_state == GLOCAL_ACTIVATED ? ">" : + (gline->gl_state == GLOCAL_DEACTIVATED ? "<" : ""), + GlineIsRemActive(gline) ? '+' : '-', gline->gl_reason); } } /* end of gline information */ - sendto_one(cptr, rpl_str(RPL_ENDOFGLIST), me.name, sptr->name); - return 0; + return send_reply(sptr, RPL_ENDOFGLIST); } +/** Statistics callback to list G-lines. + * @param[in] sptr Client requesting statistics. + * @param[in] sd Stats descriptor for request (ignored). + * @param[in] param Extra parameter from user (ignored). + */ void -gline_stats(struct Client *sptr) +gline_stats(struct Client *sptr, const struct StatDesc *sd, + char *param) { struct Gline *gline; struct Gline *sgline; - for (gline = GlobalGlineList; gline; gline = sgline) { - sgline = gline->gl_next; - - if (gline->gl_expire <= TStime()) - gline_free(gline); - else - sendto_one(sptr, rpl_str(RPL_STATSGLINE), me.name, sptr->name, 'G', - gline->gl_user, gline->gl_host, gline->gl_expire, - gline->gl_reason); - } -} - - -#if 0 /* forget about it! */ -struct Gline *make_gline(int is_ipmask, char *host, char *reason, - char *name, time_t expire) -{ - struct Gline *agline; - -#ifdef BADCHAN - int gtype = 0; - if ('#' == *host || '&' == *host || '+' == *host) - gtype = 1; /* BAD CHANNEL GLINE */ -#endif - - agline = (struct Gline*) MyMalloc(sizeof(struct Gline)); /* alloc memory */ - assert(0 != agline); - DupString(agline->host, host); /* copy vital information */ - DupString(agline->reason, reason); - DupString(agline->name, name); - agline->expire = expire; - agline->gflags = GLINE_ACTIVE; /* gline is active */ - if (is_ipmask) - SetGlineIsIpMask(agline); -#ifdef BADCHAN - if (gtype) - { - agline->next = BadChanGlineList; /* link it into the list */ - return (BadChanGlineList = agline); - } -#endif - agline->next = GlobalGlineList; /* link it into the list */ - return (GlobalGlineList = agline); -} - -struct Gline *find_gline(struct Client *cptr, struct Gline **pgline) -{ - struct Gline* gline = GlobalGlineList; - struct Gline* prev = 0; - - while (gline) { - /* - * look through all glines - */ - if (gline->expire <= TStime()) { - /* - * handle expired glines - */ - free_gline(gline, prev); - gline = prev ? prev->next : GlobalGlineList; - if (!gline) - break; /* gline == NULL means gline == NULL */ - continue; - } - - /* Does gline match? */ - /* Added a check against the user's IP address as well -Kev */ - if ((GlineIsIpMask(gline) ? - match(gline->host, ircd_ntoa((const char*) &cptr->ip)) : - match(gline->host, cptr->sockhost)) == 0 && - match(gline->name, cptr->user->username) == 0) { - if (pgline) - *pgline = prev; /* If they need it, give them the previous gline - entry (probably for free_gline, below) */ - return gline; - } - - prev = gline; - gline = gline->next; - } - - return 0; /* found no glines */ -} - -void free_gline(struct Gline* gline, struct Gline* prev) -{ - assert(0 != gline); - if (prev) - prev->next = gline->next; /* squeeze agline out */ - else { -#ifdef BADCHAN - assert(0 != gline->host); - if ('#' == *gline->host || - '&' == *gline->host || - '+' == *gline->host) { - BadChanGlineList = gline->next; - } - else -#endif - GlobalGlineList = gline->next; + gliter(GlobalGlineList, gline, sgline) { + send_reply(sptr, RPL_STATSGLINE, 'G', gline->gl_user, + gline->gl_host ? "@" : "", + gline->gl_host ? gline->gl_host : "", + gline->gl_expire, gline->gl_lastmod, + gline->gl_lifetime, + gline->gl_state == GLOCAL_ACTIVATED ? ">" : + (gline->gl_state == GLOCAL_DEACTIVATED ? "<" : ""), + GlineIsRemActive(gline) ? '+' : '-', + gline->gl_reason); } - - MyFree(gline->host); /* and free up the memory */ - MyFree(gline->reason); - MyFree(gline->name); - MyFree(gline); } -void gline_remove_expired(time_t now) +/** Calculate memory used by G-lines. + * @param[out] gl_size Number of bytes used by G-lines. + * @return Number of G-lines in use. + */ +int +gline_memory_count(size_t *gl_size) { - struct Gline* gline; - struct Gline* prev = 0; - - for (gline = GlobalGlineList; gline; gline = gline->next) { - if (gline->expire < now) { - free_gline(gline, prev); - gline = (prev) ? prev : GlobalGlineList; - if (!gline) - break; - continue; - } - prev = gline; + struct Gline *gline; + unsigned int gl = 0; + + for (gline = GlobalGlineList; gline; gline = gline->gl_next) { + gl++; + *gl_size += sizeof(struct Gline); + *gl_size += gline->gl_user ? (strlen(gline->gl_user) + 1) : 0; + *gl_size += gline->gl_host ? (strlen(gline->gl_host) + 1) : 0; + *gl_size += gline->gl_reason ? (strlen(gline->gl_reason) + 1) : 0; } -} - -#ifdef BADCHAN -int bad_channel(const char* name) -{ - struct Gline* agline = BadChanGlineList; - while (agline) - { - if ((agline->gflags & GLINE_ACTIVE) && (agline->expire > TStime()) && - !mmatch(agline->host, name)) { - return 1; - } - agline = agline->next; + for (gline = BadChanGlineList; gline; gline = gline->gl_next) { + gl++; + *gl_size += sizeof(struct Gline); + *gl_size += gline->gl_user ? (strlen(gline->gl_user) + 1) : 0; + *gl_size += gline->gl_host ? (strlen(gline->gl_host) + 1) : 0; + *gl_size += gline->gl_reason ? (strlen(gline->gl_reason) + 1) : 0; } - return 0; -} -void bad_channel_remove_expired(time_t now) -{ - struct Gline* gline; - struct Gline* prev = 0; - - for (gline = BadChanGlineList; gline; gline = gline->next) { - if (gline->expire < now) { - free_gline(gline, prev); - gline = (prev) ? prev : BadChanGlineList; - if (!gline) - break; - continue; - } - prev = gline; - } + return gl; } - -#endif - - -void add_gline(struct Client *sptr, int ip_mask, char *host, char *comment, - char *user, time_t expire, int local) -{ - struct Client *acptr; - struct Gline *agline; - int fd; - int gtype = 0; - assert(0 != host); - -#ifdef BADCHAN - if ('#' == *host || '&' == *host || '+' == *host) - gtype = 1; /* BAD CHANNEL */ -#endif - - /* Inform ops */ - sendto_op_mask(SNO_GLINE, - "%s adding %s%s for %s@%s, expiring at " TIME_T_FMT ": %s", sptr->name, - local ? "local " : "", - gtype ? "BADCHAN" : "GLINE", user, host, expire, comment); - -#ifdef GPATH - write_log(GPATH, - "# " TIME_T_FMT " %s adding %s %s for %s@%s, expiring at " TIME_T_FMT - ": %s\n", TStime(), sptr->name, local ? "local" : "global", - gtype ? "BADCHAN" : "GLINE", user, host, expire, comment); - - /* this can be inserted into the conf */ - if (!gtype) - write_log(GPATH, "%c:%s:%s:%s\n", ip_mask ? 'k' : 'K', host, comment, - user); -#endif /* GPATH */ - - agline = make_gline(ip_mask, host, comment, user, expire); - if (local) - SetGlineIsLocal(agline); - -#ifdef BADCHAN - if (gtype) - return; -#endif - - for (fd = HighestFd; fd >= 0; --fd) { - /* - * get the users! - */ - if ((acptr = LocalClientArray[fd])) { - if (!acptr->user) - continue; -#if 0 - /* - * whee!! :) - */ - if (!acptr->user || strlen(acptr->sockhost) > HOSTLEN || - (acptr->user->username ? strlen(acptr->user->username) : 0) > HOSTLEN) - continue; /* these tests right out of - find_kill for safety's sake */ -#endif - - if ((GlineIsIpMask(agline) ? match(agline->host, acptr->sock_ip) : - match(agline->host, acptr->sockhost)) == 0 && - (!acptr->user->username || - match(agline->name, acptr->user->username) == 0)) - { - - /* ok, he was the one that got G-lined */ - sendto_one(acptr, ":%s %d %s :*** %s.", me.name, - ERR_YOUREBANNEDCREEP, acptr->name, agline->reason); - - /* let the ops know about my first kill */ - sendto_op_mask(SNO_GLINE, "G-line active for %s", - get_client_name(acptr, FALSE)); - - /* and get rid of him */ - if (sptr != acptr) - exit_client_msg(sptr->from, acptr, &me, "G-lined (%s)", agline->reason); - } - } - } -} - -#endif /* 0 */