Logo Search packages:      
Sourcecode: rageircd version File versions  Download package

user_ban.c

/*
 * RageIRCd: an advanced Internet Relay Chat daemon (ircd).
 * (C) 2000-2005 the RageIRCd Development Team, all rights reserved.
 *
 * This software is free, licensed under the General Public License.
 * Please refer to doc/LICENSE and doc/README for further details.
 *
 * $Id: user_ban.c,v 1.19.2.1 2004/12/07 03:05:41 pneumatus Exp $
 */

#include "struct.h"
#include "common.h"
#include "sys.h"
#include "numeric.h"
#include "channel.h"
#include "h.h"
#include "memory.h"
#include "hook.h"
#include "user_ban.h"
#include "queue.h"

ban_hash_list CIDRBIG_bans = LIST_HEAD_INITIALIZER(CIDRBIG_bans);
ban_hash_list **CIDR_bans;

ban_list ipv4_bans;
ban_list host_bans;

ban_list gcos_bans;
ban_list nick_bans;
ban_list chan_bans;
ban_list file_bans;

unsigned int userban_count = 0, simban_count = 0;
unsigned int banentry_count = 0;

static inline void init_ban_list(ban_list *list)
{
      memset(list, 0, sizeof(ban_list));
      list->hash_list = (ban_hash_list *)MyMalloc(sizeof(ban_hash_list) * UBAN_HASH_SIZE);
}

void init_userban()
{
      int i;

      CIDR_bans = (ban_hash_list **)MyMalloc(sizeof(ban_hash_list *) * 256);
      for (i = 0; i < 256; i++) {
            CIDR_bans[i] = (ban_hash_list *)MyMalloc(sizeof(ban_list) * 256);
      }

      init_ban_list(&ipv4_bans);
      init_ban_list(&host_bans);

      init_ban_list(&gcos_bans);
      init_ban_list(&nick_bans);
      init_ban_list(&chan_bans);
      init_ban_list(&file_bans);
}

static unsigned int hash_mask(char *n)
{
      unsigned int v = 0;

      ASSERT(!BadPtr(n));

      while (*n != '\0') {
            if (*n != '.') {
                  v <<= 5;
                  v |= (ToUpper(*n) - 65) & 0xFF;
            }
            n++;
      }

      return (v % UBAN_HASH_SIZE);
}

static unsigned int hash_ipv4(char *n)
{
      unsigned int v = 0;

      ASSERT(!BadPtr(n));

      while (*n != '\0') {
            v = v * 33 + ToLower(*n++);
      }

      return (v % UBAN_HASH_SIZE);
}

static banEntry *banent_alloc()
{
      banEntry *ent;

      ent = (banEntry *)MyMalloc(sizeof(banEntry));
      banentry_count++;

      return ent;
}

static void banent_free(banEntry *ent)
{
      ASSERT(ent != NULL);
      MyFree(ent);
      banentry_count--;
}

static userBan *userban_alloc()
{
      userBan *uban;

      uban = (userBan *)MyMalloc(sizeof(userBan));
      userban_count++;

      return uban;
}

void userban_free(userBan *uban)
{
      ASSERT(uban != NULL);

      if (!BadPtr(uban->user)) {
            MyFree(uban->user);
      }
      if (!BadPtr(uban->host)) {
            MyFree(uban->host);
      }
      if (!BadPtr(uban->reason)) {
            MyFree(uban->reason);
      }

      MyFree(uban);
      userban_count--;
}

static simBan *simban_alloc()
{
      simBan *sban;

      sban = (simBan *)MyMalloc(sizeof(simBan));
      simban_count++;

      return sban;
}

void simban_free(simBan *sban)
{
      ASSERT(sban != NULL);

      if (!BadPtr(sban->mask)) {
            MyFree(sban->mask);
      }
      if (!BadPtr(sban->reason)) {
            MyFree(sban->reason);
      }

      MyFree(sban);
      simban_count--;
}

userBan *make_userban(char *user, char *host, char *reason, time_t duration)
{
      userBan *uban;
      char *s, hostbuf[HOSTLEN + 1];
      unsigned int flags = 0;
      int numcnt = 0, wildcnt = 0, dotcnt = 0, slashcnt = 0, othercnt = 0;
      int bits = 0, done = 0;
      struct in_addr addr;

      strncpyzt(hostbuf, host, HOSTLEN + 1);

      for (s = hostbuf; *s != '\0'; s++) {
            if (IsDigit(*s)) {
                  numcnt++;
            }
            else if (IsWildChar(*s)) {
                  wildcnt++;
            }
            else if (*s == '.') {
                  dotcnt++;
            }
            else if (*s == '/') {
                  slashcnt++;
            }
            else {
                  othercnt++;
            }
      }

      Debug((DEBUG_DEBUG, "make_userban(%s,%s) num %d wild %d dot %d slash %d other %d",
            user, hostbuf, numcnt, wildcnt, dotcnt, slashcnt, othercnt));

      if (BadPtr(user) || !mycmp(user, "*")) {
            /* Username is wild */
            flags |= UBAN_WILDUSER;
      }

      /* user@* */
      if (wildcnt && !numcnt && !othercnt) {
            if (flags & UBAN_WILDUSER) {
                  return NULL; /* Disgard *@* */
            }

            flags |= (UBAN_HOST|BAN_HASWILDS);

            if (!mycmp(hostbuf, "*.*") || !mycmp(hostbuf, "*")) {
                  flags |= UBAN_WILDHOST;
            }

            done = 1;
      }
      else if (!dotcnt || (slashcnt > 1)) {
            /* Everything must have a dot from here on, and not more than one slash */
            return NULL;
      }

      /* user@a.b.c.d */
      if (numcnt && (dotcnt == 3) && !othercnt) {
            if (wildcnt) {
                  flags |= (UBAN_IPV4|BAN_HASWILDS);
            }
            else if (slashcnt && parse_netmask(hostbuf, &addr, &bits)) {
                  flags |= (bits < 16) ? UBAN_CIDRBIG : UBAN_CIDR;
            }
            else if (inetpton(hostbuf, &addr.s_addr)) {
                  flags |= UBAN_IPV4;
            }
            else {
                  return NULL;
            }

            done = 1;
      }

      /* If there was a slash, but CIDR parsing failed, the host is invalid! */
      if (slashcnt && !done) {
            return NULL;
      }

      /* user@host */
      if (othercnt) {
            /* We default to a host ban... */
            flags |= UBAN_HOST;

            if (wildcnt) {
                  /* At this stage, the host can only *contain* wild cards,
                   * as a wild host (i.e. *.*) is handled at the beginning.
                   */
                  flags |= BAN_HASWILDS;
            }
      }
      else if (!done) {
            return NULL; /* We've done nothing? Drop it. */
      }

      uban = userban_alloc();
      uban->flags = flags;
      uban->timeset = timeofday;

      if ((uban->duration = duration) > 0) {
            uban->flags |= BAN_TEMPORARY;
      }

      if (!BadPtr(user)) {
            DupString(uban->user, user);
      }
      if (!BadPtr(hostbuf)) {
            DupString(uban->host, hostbuf);
      }
      if (!BadPtr(reason)) {
            DupString(uban->reason, reason);
      }

      if ((flags & (UBAN_CIDRBIG|UBAN_CIDR|UBAN_IPV4)) && !(flags & BAN_HASWILDS)) {
            /* Only set these if it's a non-wild */
            uban->ip.s_addr = addr.s_addr;
            uban->bits = bits;
      }

      return uban;
}

void add_userban(userBan *uban)
{
      banEntry *ent;
      unsigned int v;

      ent = banent_alloc();
      ent->ban.u = uban;
      uban->entry = ent;

      if (uban->flags & UBAN_CIDRBIG) {
            LIST_INSERT_HEAD(&CIDRBIG_bans, ent, lp);
      }
      else if (uban->flags & UBAN_CIDR) {
            unsigned char *s = (unsigned char *)&uban->ip.s_addr;
            int a, b;

              a = (int)*s++;
            b = (int)*s;

            LIST_INSERT_HEAD(&CIDR_bans[a][b], ent, lp);
      }
      else if (uban->flags & UBAN_IPV4) {
            if (uban->flags & BAN_HASWILDS) {
                  LIST_INSERT_HEAD(&ipv4_bans.wild_list, ent, lp);
            }
            else {
                  v = hash_ipv4(uban->host);
                  LIST_INSERT_HEAD(&ipv4_bans.hash_list[v], ent, lp);
            }
      }
      else if (uban->flags & UBAN_HOST) {
            if (uban->flags & BAN_HASWILDS) {
                  LIST_INSERT_HEAD(&host_bans.wild_list, ent, lp);
            }
            else {
                  v = hash_mask(uban->host);
                  LIST_INSERT_HEAD(&host_bans.hash_list[v], ent, lp);
            }
      }
      else {
            ircdlog(LOG_ERROR, "add_userban(%s,%s) non-cidr/ipv4/host", uban->user, uban->host);
            ASSERT("unknown hostbased ban type" == NULL);
      }
}

void del_userban(userBan *uban)
{
      banEntry *ent = uban->entry;
      LIST_REMOVE(ent, lp);
      banent_free(ent);
}

int user_match_ban(aClient *cptr, userBan *uban)
{
      /* Check username first, if we need to */
      if (!(uban->flags & UBAN_WILDUSER) && match(uban->user, cptr->username)) {
            return 0;
      }

      if (uban->flags & (UBAN_CIDR|UBAN_CIDRBIG)) {
            if (match_ipv4(cptr->ip, uban->ip, uban->bits)) {
                  return 1;
            }
      }
      else if (uban->flags & UBAN_IPV4) {
            /* If the ban has wilds, we need a string-based check */
            if (uban->flags & BAN_HASWILDS) {
                  /* Wildcard IP masks can't have UBAN_WILDHOST */
                  if (!match(uban->host, cptr->hostip)) {
                        return 1;
                  }
            }
            else if (cptr->ip.s_addr == uban->ip.s_addr) {
                  return 1;
            }
      }
      else if (uban->flags & UBAN_HOST) {
            if (uban->flags & BAN_HASWILDS) {
                  if ((uban->flags & UBAN_WILDHOST) || !match(uban->host, cptr->host)) {
                        return 1;
                  }
            }
            else if (!mycmp(uban->host, cptr->host)) {
                  return 1;
            }
      }
      else {
            ircdlog(LOG_ERROR, "user_match_ban() encountered unknown userban type");
            ASSERT("unknown userban type" == NULL);
      }

      return 0;
}

userBan *user_find_ban(aClient *cptr, unsigned int include, unsigned int exclude)
{
      banEntry *ent = NULL;
      userBan *uban;

      ASSERT(cptr != NULL);

      /* First we check CIDR */
      if (include & UBAN_CIDR) {
            unsigned char *s = (unsigned char *)&cptr->ip.s_addr;
            int a, b;

            a = (int)*s++;
            b = (int)*s;

            LIST_FOREACH(ent, &CIDR_bans[a][b], lp) {
                  uban = ent->ban.u;
                  ASSERT(uban != NULL);

                  if (BanExpired(uban)) {
                        continue;
                  }
                  if (((include & UBAN_WILDUSER) && !(uban->flags & UBAN_WILDUSER))
                    || ((exclude & UBAN_WILDUSER) && (uban->flags & UBAN_WILDUSER))) {
                        continue;
                  }
                  if (!(uban->flags & UBAN_WILDUSER) && match(uban->user, cptr->username)) {
                        continue;
                  }

                  if (match_ipv4(cptr->ip, uban->ip, uban->bits)) {
                        return uban;
                  }
            }

            LIST_FOREACH(ent, &CIDRBIG_bans, lp) {
                  uban = ent->ban.u;
                  ASSERT(uban != NULL);

                  if (BanExpired(uban)) {
                        continue;
                  }
                  if (((include & UBAN_WILDUSER) && !(uban->flags & UBAN_WILDUSER))
                    || ((exclude & UBAN_WILDUSER) && (uban->flags & UBAN_WILDUSER))) {
                        continue;
                  }
                  if (!(uban->flags & UBAN_WILDUSER) && match(uban->user, cptr->username)) {
                        continue;
                  }

                  if (match_ipv4(cptr->ip, uban->ip, uban->bits)) {
                        return uban;
                  }
            }
      }

      if (include & UBAN_IPV4) {
            unsigned int v = hash_ipv4(cptr->hostip);

            LIST_FOREACH(ent, &ipv4_bans.hash_list[v], lp) {
                  uban = ent->ban.u;

                  if (BanExpired(uban)) {
                        continue;
                  }
                  if (((include & UBAN_WILDUSER) && !(uban->flags & UBAN_WILDUSER))
                    || ((exclude & UBAN_WILDUSER) && (uban->flags & UBAN_WILDUSER))) {
                        continue;
                  }
                  if (!(uban->flags & UBAN_WILDUSER) && match(uban->user, cptr->username)) {
                        continue;
                  }
                  if (uban->ip.s_addr == cptr->ip.s_addr) {
                        return uban;
                  }
            }

            LIST_FOREACH(ent, &ipv4_bans.wild_list, lp) {
                  uban = ent->ban.u;

                  if (BanExpired(uban)) {
                        continue;
                  }
                  if (((include & UBAN_WILDUSER) && !(uban->flags & UBAN_WILDUSER))
                    || ((exclude & UBAN_WILDUSER) && (uban->flags & UBAN_WILDUSER))) {
                        continue;
                  }
                  if (!(uban->flags & UBAN_WILDUSER) && match(uban->user, cptr->username)) {
                        continue;
                  }
                  if (!match(uban->host, cptr->hostip)) {
                        return uban;
                  }
            }
      }

      if (include & UBAN_HOST) {
            unsigned int v = hash_mask(cptr->host);

            LIST_FOREACH(ent, &host_bans.hash_list[v], lp) {
                  uban = ent->ban.u;

                  if (BanExpired(uban)) {
                        continue;
                  }
                  if (((include & UBAN_WILDUSER) && !(uban->flags & UBAN_WILDUSER))
                    || ((exclude & UBAN_WILDUSER) && (uban->flags & UBAN_WILDUSER))) {
                        continue;
                  }
                  if (!(uban->flags & UBAN_WILDUSER) && match(uban->user, cptr->username)) {
                        continue;
                  }
                  if (!mycmp(uban->host, cptr->host)) {
                        return uban;
                  }
            }

            LIST_FOREACH(ent, &host_bans.wild_list, lp) {
                  uban = ent->ban.u;

                  if (BanExpired(uban)) {
                        continue;
                  }
                  if (((include & UBAN_WILDUSER) && !(uban->flags & UBAN_WILDUSER))
                    || ((exclude & UBAN_WILDUSER) && (uban->flags & UBAN_WILDUSER))) {
                        continue;
                  }
                  if (!(uban->flags & UBAN_WILDUSER) && match(uban->user, cptr->username)) {
                        continue;
                  }
                  if (!match(uban->host, cptr->host)) {
                        return uban;
                  }
            }
      }

      return NULL;
}

userBan *ip_find_ban(struct in_addr *ipaddr, char *ipbuf)
{
      banEntry *ent;
      userBan *uban;
      unsigned char *s;
      int a, b;
      struct in_addr kludge;

      s = (unsigned char *)&ipaddr->s_addr;
      a = (int)*s++;
      b = (int)*s;

      LIST_FOREACH(ent, &CIDR_bans[a][b], lp) {
            uban = ent->ban.u;
            ASSERT(uban != NULL);

            if (BanExpired(uban) || !(uban->flags & UBAN_WILDUSER)) {
                  continue;
            }

            kludge.s_addr = ipaddr->s_addr;
            if (match_ipv4(kludge, uban->ip, uban->bits)) {
                  return uban;
            }
      }

      LIST_FOREACH(ent, &CIDRBIG_bans, lp) {
            uban = ent->ban.u;
            ASSERT(uban != NULL);

            if (BanExpired(uban) || !(uban->flags & UBAN_WILDUSER)) {
                  continue;
            }

            kludge.s_addr = ipaddr->s_addr;
            if (match_ipv4(kludge, uban->ip, uban->bits)) {
                  return uban;
            }
      }

      LIST_FOREACH(ent, &ipv4_bans.hash_list[hash_ipv4(ipbuf)], lp) {
            uban = ent->ban.u;
            ASSERT(uban != NULL);

            if (BanExpired(uban) || !(uban->flags & UBAN_WILDUSER)) {
                  continue;
            }

            if (uban->ip.s_addr == ipaddr->s_addr) {
                  return uban;
            }
      }

      LIST_FOREACH(ent, &ipv4_bans.wild_list, lp) {
            uban = ent->ban.u;
            ASSERT(uban != NULL);

            if (BanExpired(uban) || !(uban->flags & UBAN_WILDUSER)) {
                  continue;
            }

            if (!match(uban->host, ipbuf)) {
                  return uban;
            }
      }

      return NULL;
}

userBan *find_userban_exact(userBan *orig, unsigned int careflags)
{
      ban_hash_list *hash;
      banEntry *ent;
      userBan *uban = NULL;

      ASSERT(orig != NULL);

      if (orig->flags & UBAN_CIDRBIG) {
            LIST_FOREACH(ent, &CIDRBIG_bans, lp) {
                  uban = ent->ban.u;
                  ASSERT(uban != NULL);

                  if ((uban->flags ^ orig->flags) & (UBAN_WILDUSER|careflags)) {
                        continue;
                  }
                  if (!(orig->flags & UBAN_WILDUSER) && mycmp(orig->user, uban->user)) {
                        continue;
                  }
                  if ((orig->ip.s_addr == uban->ip.s_addr) && (orig->bits == uban->bits)) {
                        return uban;
                  }
            }
      }
      else if (orig->flags & UBAN_CIDR) {
            unsigned char *s = (unsigned char *)&orig->ip.s_addr;
            int a, b;

            a = (int)*s++;
            b = (int)*s;

            LIST_FOREACH(ent, &CIDR_bans[a][b], lp) {
                  uban = ent->ban.u;
                  ASSERT(uban != NULL);

                  if ((uban->flags ^ orig->flags) & (UBAN_WILDUSER|careflags)) {
                        continue;
                  }
                  if (!(orig->flags & UBAN_WILDUSER) && mycmp(orig->user, uban->user)) {
                        continue;
                  }
                  if ((orig->ip.s_addr == uban->ip.s_addr) && (orig->bits == uban->bits)) {
                        return uban;
                  }
            }
      }
      else if (orig->flags & UBAN_IPV4) {
            if (orig->flags & BAN_HASWILDS) {
                  hash = &ipv4_bans.wild_list;
            }
            else {
                  hash = &ipv4_bans.hash_list[hash_ipv4(orig->host)];
            }

            LIST_FOREACH(ent, hash, lp) {
                  uban = ent->ban.u;
                  ASSERT(uban != NULL);

                  if ((uban->flags ^ orig->flags) & (UBAN_WILDUSER|careflags)) {
                        continue;
                  }
                  if (!(orig->flags & UBAN_WILDUSER) && mycmp(orig->user, uban->user)) {
                        continue;
                  }
                  if (orig->flags & BAN_HASWILDS) {
                        /* Dont take into account wildcards, as we need the exact
                         * host. match() will parse any wilds in the string.
                         */
                        if (!mycmp(orig->host, uban->host)) {
                              return uban;
                        }
                  }
                  else if (orig->ip.s_addr == uban->ip.s_addr) {
                        return uban;
                  }
            }
      }
      else if (orig->flags & UBAN_HOST) {
            if (orig->flags & BAN_HASWILDS) {
                  hash = &host_bans.wild_list;
            }
            else {
                  hash = &host_bans.hash_list[hash_mask(orig->host)];
            }

            LIST_FOREACH(ent, hash, lp) {
                  uban = ent->ban.u;
                  ASSERT(uban != NULL);

                  if ((uban->flags ^ orig->flags) & (UBAN_WILDUSER|careflags)) {
                        continue;
                  }
                  if (!(orig->flags & UBAN_WILDUSER) && mycmp(orig->user, uban->user)) {
                        continue;
                  }
                  if (!mycmp(orig->host, uban->host)) {
                        return uban;
                  }
            }
      }

      return NULL;
}

static inline void remove_list_match_flags(banEntry *ent, unsigned int include, unsigned int exclude)
{
      banEntry *next = NULL;
      userBan *uban;

      while (ent != NULL) {
            next = LIST_NEXT(ent, lp);
            uban = ent->ban.u;
            ASSERT(uban != NULL);

            if ((!include && !exclude) || BanMatchFlags(uban, include, exclude)) {
                  del_userban(uban);
                  userban_free(uban);
            }

            ent = next;
      }
}

void remove_userbans_match_flags(unsigned int include, unsigned int exclude)
{
      int a, b;

      for (a = 0; a < 256; a++) {
            for (b = 0; b < 256; b++) {
                  remove_list_match_flags(LIST_FIRST(&CIDR_bans[a][b]), include, exclude);
            }
      }
      remove_list_match_flags(LIST_FIRST(&CIDRBIG_bans), include, exclude);

      for (a = 0; a < UBAN_HASH_SIZE; a++) {
            remove_list_match_flags(LIST_FIRST(&ipv4_bans.hash_list[a]), include, exclude);
            remove_list_match_flags(LIST_FIRST(&host_bans.hash_list[a]), include, exclude);
      }
      remove_list_match_flags(LIST_FIRST(&ipv4_bans.wild_list), include, exclude);
      remove_list_match_flags(LIST_FIRST(&host_bans.wild_list), include, exclude);
}

static inline char get_userban_flag(unsigned int flags)
{
      unsigned int l = (flags & BAN_LOCAL);
      unsigned int t = (flags & BAN_TEMPORARY);
      unsigned int u = (flags & (UBAN_CIDR|UBAN_IPV4|UBAN_HOST));

      switch (u) {
            case UBAN_CIDR:
            case UBAN_IPV4:
                  return (t) ? 'z' : 'Z';
            case UBAN_HOST:
                  /* Network host ans are AutoKills */
                  return (l) ? ((t) ? 'k' : 'K') : 'A';
            default:
                  break;
      }

      ASSERT("unknown userban flag" == NULL);
      return '?';
}

static inline void report_userban_entry(aClient *cptr, banEntry *ent, unsigned int include, unsigned int exclude)
{
      userBan *uban;
      char flag;

      ASSERT(cptr != NULL);

      while (ent != NULL) {
            uban = ent->ban.u;
            ASSERT(uban != NULL);

            if ((!include && !exclude) || BanMatchFlags(uban, include, exclude)) {
                  flag = get_userban_flag(uban->flags);

                  send_me_numeric(cptr, RPL_STATSKLINE, flag,
                        !BadPtr(uban->host) ? uban->host : "*",
                        (uban->flags & UBAN_WILDUSER) ? "*" : uban->user,
                        (uban->flags & BAN_TEMPORARY) ?
                              (((uban->timeset + uban->duration) - timeofday) / 60) : -1,
                        BanReason(uban));
            }

            ent = LIST_NEXT(ent, lp);
      }
}

void report_userbans_match_flags(aClient *cptr, unsigned int include, unsigned int exclude)
{
      int a, b;

      for (a = 0; a < 256; a++) {
            for (b = 0; b < 256; b++) {
                  report_userban_entry(cptr, LIST_FIRST(&CIDR_bans[a][b]), include, exclude);
            }
      }
      report_userban_entry(cptr, LIST_FIRST(&CIDRBIG_bans), include, exclude);

      for (a = 0; a < UBAN_HASH_SIZE; a++) {
            report_userban_entry(cptr, LIST_FIRST(&ipv4_bans.hash_list[a]), include, exclude);
            report_userban_entry(cptr, LIST_FIRST(&host_bans.hash_list[a]), include, exclude);
      }
      report_userban_entry(cptr, LIST_FIRST(&ipv4_bans.wild_list), include, exclude);
      report_userban_entry(cptr, LIST_FIRST(&host_bans.wild_list), include, exclude);
}

static inline void expire_userban_list(banEntry *ent)
{
      banEntry *next = NULL;
      userBan *uban;

      while (ent != NULL) {
            next = LIST_NEXT(ent, lp);
            uban = ent->ban.u;
            ASSERT(uban != NULL);

            if (BanExpired(uban)) {
                  del_userban(uban);
                  userban_free(uban);
            }

            ent = next;
      }
}

void expire_userbans()
{
      int a, b;

      for (a = 0; a < 256; a++) {
            for (b = 0; b < 256; b++) {
                  expire_userban_list(LIST_FIRST(&CIDR_bans[a][b]));
            }
      }
      expire_userban_list(LIST_FIRST(&CIDRBIG_bans));

      for (a = 0; a < UBAN_HASH_SIZE; a++) {
            expire_userban_list(LIST_FIRST(&ipv4_bans.hash_list[a]));
            expire_userban_list(LIST_FIRST(&host_bans.hash_list[a]));
      }
      expire_userban_list(LIST_FIRST(&ipv4_bans.wild_list));
      expire_userban_list(LIST_FIRST(&host_bans.wild_list));
}

simBan *make_simban(char *mask, char *reason, time_t duration, unsigned int flags)
{
      simBan *sban;
      char *s, maskbuf[512];
      int wildcnt = 0, othercnt = 0;

      if (!(flags & (BAN_LOCAL|BAN_NETWORK))) {
            return NULL;
      }
      if (!(flags & (SBAN_NICK|SBAN_GCOS|SBAN_CHAN|SBAN_FILE))) {
            return NULL;
      }

      strncpyzt(maskbuf, mask, 512);

      for (s = maskbuf; *s != '\0'; s++) {
            if (IsWildChar(*s)) {
                  wildcnt++;
            }
            else {
                  othercnt++;
            }
      }

      if (!othercnt) {
            return NULL; /* Disregard wild masks */
      }

      if (wildcnt) {
            flags |= BAN_HASWILDS;
      }

      sban = simban_alloc();
      sban->flags = flags;
      sban->timeset = timeofday;
      sban->duration = duration;

      if (!BadPtr(maskbuf)) {
            DupString(sban->mask, maskbuf);
      }
      if (!BadPtr(reason)) {
            DupString(sban->reason, reason);
      }

      return sban;
}

static ban_list *get_simban_list(unsigned int flags)
{
      unsigned int listflag = 0;

      listflag = (flags & (SBAN_NICK|SBAN_GCOS|SBAN_CHAN|SBAN_FILE));
      ASSERT(listflag != 0);

      switch (listflag) {
            case SBAN_NICK:
                  return &nick_bans;
            case SBAN_GCOS:
                  return &gcos_bans;
            case SBAN_CHAN:
                  return &chan_bans;
            case SBAN_FILE:
                  return &file_bans;
            default:
                  break;
      }

      ASSERT("unknown simban list" == NULL);
      return NULL;
}

void add_simban(simBan *sban)
{
      banEntry *ent;
      ban_list *list;
      ban_hash_list *hash;

      if ((list = get_simban_list(sban->flags)) == NULL) {
            return;
      }

      ent = banent_alloc();
      ent->ban.s = sban;
      sban->entry = ent;

      if (sban->flags & BAN_HASWILDS) {
            hash = &list->wild_list;
      }
      else {
            hash = &list->hash_list[hash_mask(sban->mask)];
      }

      LIST_INSERT_HEAD(hash, ent, lp);
}

void del_simban(simBan *sban)
{
      banEntry *ent = sban->entry;
      LIST_REMOVE(ent, lp);
      banent_free(ent);
}

simBan *find_simban_flags(char *mask, unsigned int flags)
{
      ban_list *list;
      ban_hash_list *hash;
      banEntry *ent;
      simBan *sban;

      if ((list = get_simban_list(flags)) == NULL) {
            return NULL;
      }

      hash = &list->hash_list[hash_mask(mask)];
      LIST_FOREACH(ent, hash, lp) {
            sban = ent->ban.s;
            ASSERT(sban != NULL);

            if (!BanExpired(sban) && !mycmp(sban->mask, mask)) {
                  return sban;
            }
      }

      hash = &list->wild_list;
      LIST_FOREACH(ent, hash, lp) {
            sban = ent->ban.s;
            ASSERT(sban != NULL);

            if (!BanExpired(sban) && !match(sban->mask, mask)) {
                  return sban;
            }
      }

      return NULL;
}

simBan *find_simban_exact(simBan *orig)
{
      ban_list *list;
      ban_hash_list *hash;
      banEntry *ent;
      simBan *sban;

      if ((list = get_simban_list(orig->flags)) == NULL) {
            return NULL;
      }

      if (orig->flags & BAN_HASWILDS) {
            hash = &list->wild_list;
      }
      else {
            hash = &list->hash_list[hash_mask(orig->mask)];
      }

      LIST_FOREACH(ent, hash, lp) {
            sban = ent->ban.s;
            ASSERT(sban != NULL);

            if ((sban->flags == orig->flags) && !mycmp(sban->mask, orig->mask)) {
                  return sban;
            }
      }

      return NULL;
}

void remove_simbans_match_flags(unsigned int include, unsigned int exclude)
{
      ban_list *list;
      banEntry *ent;
      simBan *sban;
      int a;

      if ((list = get_simban_list(include)) == NULL) {
            ircdlog(LOG_ERROR, "Unknown simban list in remove_simbans_match_flags()");
            return;
      }

      for (a = 0; a < UBAN_HASH_SIZE; a++) {
            LIST_FOREACH(ent, &list->hash_list[a], lp) {
                  sban = ent->ban.s;
                  ASSERT(sban != NULL);

                  if (BanExpired(sban)) {
                        continue;
                  }
                  if (BanMatchFlags(sban, include, exclude)) {
                        sban->flags |= BAN_TEMPORARY;
                        sban->timeset = timeofday - 5;
                        sban->duration = 1;
                  }
            }
      }

      LIST_FOREACH(ent, &list->wild_list, lp) {
            sban = ent->ban.s;
            ASSERT(sban != NULL);

            if (BanExpired(sban)) {
                  continue;
            }
            if (BanMatchFlags(sban, include, exclude)) {
                  sban->flags |= BAN_TEMPORARY;
                  sban->timeset = timeofday - 5;
                  sban->duration = 1;
            }
      }
}

void remove_simbans_match_mask(unsigned int flags, char *mask, int wild)
{
      ban_list *list;
      banEntry *ent;
      simBan *sban;
      int a;

      if ((list = get_simban_list(flags)) == NULL) {
            ircdlog(LOG_ERROR, "Unknown simban list in remove_simbans_match_mask()");
            return;
      }

      for (a = 0; a < UBAN_HASH_SIZE; a++) {
            LIST_FOREACH(ent, &list->hash_list[a], lp) {
                  sban = ent->ban.s;
                  ASSERT(sban != NULL);

                  if (BanExpired(sban) || ((sban->flags & flags) != flags)) {
                        continue;
                  }
                  if (BanMatchMask(mask, sban->mask, wild)) {
                        sban->flags |= BAN_TEMPORARY;
                        sban->timeset = timeofday - 5;
                        sban->duration = 1;
                  }
            }
      }

      LIST_FOREACH(ent, &list->wild_list, lp) {
            sban = ent->ban.s;
            ASSERT(sban != NULL);

            if (BanExpired(sban) || ((sban->flags & flags) != flags)) {
                  continue;
            }
            if (BanMatchMask(mask, sban->mask, wild)) {
                  sban->flags |= BAN_TEMPORARY;
                  sban->timeset = timeofday - 5;
                  sban->duration = 1;
            }
      }
}

static inline char get_simban_flag(unsigned int flags)
{
      unsigned int l = (flags & BAN_LOCAL);
      unsigned int s = (flags & (SBAN_NICK|SBAN_GCOS|SBAN_CHAN|SBAN_FILE));

      switch (s) {
            case SBAN_NICK:
            case SBAN_CHAN:
                  /* Nick and chan restrictions are the same in bahamut, so we
                   * will use the same flag here
                   */
                  return (l) ? 'Q' : 'q';
            case SBAN_GCOS:
                  return (l) ? 'G' : 'g';
            case SBAN_FILE:
                  return 'D';
            default:
                  break;
      }

      ASSERT("unknown simban flag" == NULL);
      return '?';
}

void report_simbans_match_flags(aClient *cptr, unsigned int include, unsigned int exclude)
{
      ban_list *list;
      banEntry *ent;
      simBan *sban;
      int a;
      char flag;

      if ((list = get_simban_list(include)) == NULL) {
            ircdlog(LOG_ERROR, "Unknown simban type in report_simbans_match_flags()");
            return;
      }

      for (a = 0; a < UBAN_HASH_SIZE; a++) {
            LIST_FOREACH(ent, &list->hash_list[a], lp) {
                  sban = ent->ban.s;
                  ASSERT(sban != NULL);

                  if (BanExpired(sban)) {
                        continue;
                  }

                  if (((sban->flags & include) == include) && !(sban->flags & exclude)) {
                        flag = get_simban_flag(sban->flags);

                        send_me_numeric(cptr, (sban->flags & SBAN_GCOS) ? RPL_STATSGLINE : RPL_STATSQLINE,
                              flag, sban->mask, (sban->flags & BAN_TEMPORARY) ?
                                    (((sban->timeset + sban->duration) - timeofday) / 60) : -1,
                              BanReason(sban));
                  }
            }
      }

      LIST_FOREACH(ent, &list->wild_list, lp) {
            sban = ent->ban.s;
            ASSERT(sban != NULL);

            if (BanExpired(sban)) {
                  continue;
            }

            if (((sban->flags & include) == include) && !(sban->flags & exclude)) {
                  flag = get_simban_flag(sban->flags);

                  send_me_numeric(cptr, (sban->flags & SBAN_GCOS) ? RPL_STATSGLINE : RPL_STATSQLINE,
                        flag, sban->mask, (sban->flags & BAN_TEMPORARY) ?
                              (((sban->timeset + sban->duration) - timeofday) / 60) : -1,
                        BanReason(sban));
            }
      }
}

static inline void expire_simban_list(banEntry *ent)
{
      banEntry *next = NULL;
      simBan *sban;

      while (ent != NULL) {
            next = LIST_NEXT(ent, lp);
            sban = ent->ban.s;
            ASSERT(sban != NULL);

            if (BanExpired(sban)) {
                  del_simban(sban);
                  simban_free(sban);
            }

            ent = next;
      }
}

void expire_simbans()
{
      int a;

      for (a = 0; a < UBAN_HASH_SIZE; a++) {
            expire_simban_list(LIST_FIRST(&nick_bans.hash_list[a]));
            expire_simban_list(LIST_FIRST(&gcos_bans.hash_list[a]));
            expire_simban_list(LIST_FIRST(&chan_bans.hash_list[a]));
            expire_simban_list(LIST_FIRST(&file_bans.hash_list[a]));
      }
      expire_simban_list(LIST_FIRST(&nick_bans.wild_list));
      expire_simban_list(LIST_FIRST(&gcos_bans.wild_list));
      expire_simban_list(LIST_FIRST(&chan_bans.wild_list));
      expire_simban_list(LIST_FIRST(&file_bans.wild_list));
}

void send_simbans(aClient *cptr, unsigned int flags)
{
      ban_list *list;
      banEntry *ent;
      simBan *sban;
      int a;

      if (!(flags & (SBAN_NICK|SBAN_CHAN|SBAN_GCOS))) {
            ircdlog(LOG_ERROR, "Invalid simban type in send_simbans()");
            return;
      }

      if ((list = get_simban_list(flags)) == NULL) {
            ircdlog(LOG_ERROR, "Unknown simban type in send_simbans()");
            return;
      }

      for (a = 0; a < UBAN_HASH_SIZE; a++) {
            LIST_FOREACH(ent, &list->hash_list[a], lp) {
                  sban = ent->ban.s;
                  ASSERT(sban != NULL);

                  if (sban->flags & BAN_TEMPORARY || (sban->flags & flags) != flags) {
                        continue;
                  }

                  if (sban->flags & SBAN_GCOS) {
                        sendto_one_client_nopostfix(cptr, &me, &CMD_SGLINE, "%d :%s:%s",
                              strlen(sban->mask), sban->mask, BanReason(sban));
                  }
                  else {
                        sendto_one_client_nopostfix(cptr, &me, &CMD_SQLINE, "%s :%s",
                              sban->mask, BanReason(sban));
                  }
            }
      }

      LIST_FOREACH(ent, &list->wild_list, lp) {
            sban = ent->ban.s;
            ASSERT(sban != NULL);

            if (sban->flags & BAN_TEMPORARY || (sban->flags & flags) != flags) {
                  continue;
            }

            if (sban->flags & SBAN_GCOS) {
                  sendto_one_client_nopostfix(cptr, &me, &CMD_SGLINE, "%d :%s:%s",
                        strlen(sban->mask), sban->mask, BanReason(sban));
            }
            else {
                  sendto_one_client_nopostfix(cptr, &me, &CMD_SQLINE, "%s :%s",
                        sban->mask, BanReason(sban));
            }
      }
}

Generated by  Doxygen 1.6.0   Back to index