diff options
author | geniusdex geniusdex@31f1291d-b8d6-0310-a050-a5561fc1590b <geniusdex geniusdex@31f1291d-b8d6-0310-a050-a5561fc1590b@5417fbe8-f217-4b02-8779-1006273d7864> | 2005-06-05 10:46:25 +0000 |
---|---|---|
committer | geniusdex geniusdex@31f1291d-b8d6-0310-a050-a5561fc1590b <geniusdex geniusdex@31f1291d-b8d6-0310-a050-a5561fc1590b@5417fbe8-f217-4b02-8779-1006273d7864> | 2005-06-05 10:46:25 +0000 |
commit | a6e9356357a9dfe0c01871531eba58f6fdce92ea (patch) | |
tree | cd90b41ac8b20a86d00df4818853c21754067ebd /src/userban.c | |
parent | 843f9f38a8e2a567432223bc173710e52d7431c1 (diff) |
BUILD : 1.7.10 (828) BUGS : NOTES : Added a new ban system which should be faster than what we have now and also supports CIDR bans -- not yet in use, but will be used for akills andeventually channel bans -- hashing and cidr bans are not tested yet
git-svn-id: svn://svn.anope.org/anope/trunk@828 31f1291d-b8d6-0310-a050-a5561fc1590b
git-svn-id: http://anope.svn.sourceforge.net/svnroot/anope/trunk@581 5417fbe8-f217-4b02-8779-1006273d7864
Diffstat (limited to 'src/userban.c')
-rw-r--r-- | src/userban.c | 472 |
1 files changed, 472 insertions, 0 deletions
diff --git a/src/userban.c b/src/userban.c new file mode 100644 index 000000000..1ef883516 --- /dev/null +++ b/src/userban.c @@ -0,0 +1,472 @@ +/* Host and IP (incl. CIDR) banning routines + * + * (C) 2003-2005 Anope Team + * Contact us at info@anope.org + * + * Please read COPYING and README for further details. + * + * Based on the original code of Epona by Lara. + * Based on the original code of Services by Andy Church. + * + * $Id$ + * + */ + +#include "services.h" + +/*************************************************************************/ + +#define BANHOSTHASH(x) (*(x) & 0x1F) +#define BANINFOHASH(x) ((((x)->type & BANTYPE_CIDR4) || (*((x)->host) == '*') || (*((x)->host) == '?')) ? 32 : BANHOSTHASH((x)->host)) + +/*************************************************************************/ + +/** + * Turn a cidr value into a netmask + * @param cidr CIDR value + * @return Netmask value + */ +uint32 cidr_to_netmask(uint16 cidr) +{ + if (cidr == 0) + return 0; + + return (0xFFFFFFFF - (1 << (32 - cidr)) + 1); +} + +/** + * Turn a netmask into a cidr value + * @param mask Netmask + * @return CIDR value + */ +uint16 netmask_to_cidr(uint32 mask) +{ + int tmp = 0; + + while (!(mask & (1 << tmp)) && (tmp < 32)) + tmp++; + + return (32 - tmp); +} + +/*************************************************************************/ + +/** + * Check if the given string is some sort of wildcard + * @param str String to check + * @return 1 for wildcard, 0 for anything else + */ +int str_is_wildcard(const char *str) +{ + while (*str) { + if ((*str == '*') || (*str == '?')) + return 1; + str++; + } + + return 0; +} + +/** + * Check if the given string is a pure wildcard + * @param str String to check + * @return 1 for pure wildcard, 0 for anything else + */ +int str_is_pure_wildcard(const char *str) +{ + while (*str) { + if (*str != '*') + return 0; + str++; + } + + return 1; +} + +/*************************************************************************/ + +/** + * Check if the given string is an IP or CIDR mask, and fill the given + * ip/cidr params if so. + * @param str String to check + * @param ip The ipmask to fill when a CIDR is found + * @param mask The CIDR mask to fill when a CIDR is found + * @param host Displayed host + * @return 1 for IP/CIDR, 0 for anything else + */ +int str_is_cidr(char *str, uint32 * ip, uint32 * mask, char **host) +{ + int i; + int octets[4] = { -1, -1, -1, -1 }; + char *s = str; + char buf[512]; + uint16 cidr; + + for (i = 0; i < 4; i++) { + octets[i] = strtol(s, &s, 10); + /* Bail out if the octet is invalid or wrongly terminated */ + if ((octets[i] < 0) || (octets[i] > 255) + || ((i < 3) && (*s != '.'))) + return 0; + if (i < 3) + s++; + } + + /* Fill the IP - the dirty way */ + *ip = octets[3]; + *ip += octets[2] * 256; + *ip += octets[1] * 65536; + *ip += octets[0] * 16777216; + + if (*s == '/') { + s++; + /* There's a CIDR mask here! */ + cidr = strtol(s, &s, 10); + /* Bail out if the CIDR is invalid or the string isn't done yet */ + if ((cidr > 32) || (*s)) + return 0; + } else { + /* No CIDR mask here - use 32 so the whole ip will be matched */ + cidr = 32; + } + + *mask = cidr_to_netmask(cidr); + /* Apply the mask to avoid 255.255.255.255/8 bans */ + *ip &= *mask; + + /* Refill the octets to fill the host */ + octets[0] = (*ip & 0xFF000000) / 16777216; + octets[1] = (*ip & 0x00FF0000) / 65536; + octets[2] = (*ip & 0x0000FF00) / 256; + octets[3] = (*ip & 0x000000FF); + + if (cidr == 32) + snprintf(buf, 512, "%d.%d.%d.%d", octets[0], octets[1], octets[2], + octets[3]); + else + snprintf(buf, 512, "%d.%d.%d.%d/%d", octets[0], octets[1], + octets[2], octets[3], cidr); + + *host = sstrdup(buf); + + return 1; +} + +/*************************************************************************/ + +/** + * Create a BanInfo for the given mask. This function destroys the given + * mask as a side effect. + * @param mask Host/IP/CIDR mask to parse + * @param id Ban ID to use for this ban + * @return BanInfo struct for the given mask, NULL if creation failed + */ +BanInfo *ban_create(char *mask, uint32 id) +{ + char *user, *host, *cidrhost; + char buf[BUFSIZE]; + BanInfo *ban; + uint32 ip; + uint32 cidr; + + ban = scalloc(1, sizeof(BanInfo)); + ban->id = id; + ban->type = BANTYPE_NONE; + + host = strchr(mask, '@'); + if (host) { + *host++ = '\0'; + /* If the user is purely a wildcard, ignore it */ + if (str_is_pure_wildcard(mask)) + user = NULL; + else + user = mask; + } else { + /* If there's no user in the mask, assume a pure wildcard */ + user = NULL; + host = mask; + } + + if (user) { + ban->user = sstrdup(user); + /* Check if we have a wildcard user */ + if (str_is_wildcard(user)) + ban->type |= BANTYPE_USER_WILD; + else + ban->type |= BANTYPE_USER; + } + /* Only check the host if it's not a pure wildcard */ + if (*host && !str_is_pure_wildcard(host)) { + if (ircd->nickip && str_is_cidr(host, &ip, &cidr, &cidrhost)) { + ban->cidr_ip = ip; + ban->cidr_mask = cidr; + ban->type |= BANTYPE_CIDR4; + host = cidrhost; + } else if (strchr(host, '/')) { + /* If we still have a CIDR we're fucked (scientific term) */ + return NULL; + } else { + ban->host = sstrdup(host); + if (str_is_wildcard(host)) + ban->type |= BANTYPE_HOST_WILD; + else + ban->type |= BANTYPE_HOST; + } + } + + if (!user) + user = "*"; + if (!host) + host = "*"; + snprintf(buf, BUFSIZE, "%s@%s", user, host); + ban->mask = sstrdup(buf); + + return ban; +} + +/*************************************************************************/ + +/** + * Match the given BanInfo to the given user/host and optional IP addy + * @param ban BanInfo struct to match against + * @param user User to match against + * @param host Host to match against + * @param ip IP to match against, set to 0 to not match this + * @return 1 for a match, 0 for no match + */ +int ban_match(BanInfo * ban, char *user, char *host, uint32 ip) +{ + if ((ban->type & BANTYPE_CIDR4) && ip + && ((ip & ban->cidr_mask) != ban->cidr_ip)) + return 0; + if ((ban->type & BANTYPE_USER) && (stricmp(ban->user, user) != 0)) + return 0; + if ((ban->type & BANTYPE_HOST) && (stricmp(ban->host, host) != 0)) + return 0; + if ((ban->type & BANTYPE_USER_WILD) + && !match_wild_nocase(ban->user, user)) + return 0; + if ((ban->type & BANTYPE_HOST_WILD) + && !match_wild_nocase(ban->host, host)) + return 0; + + return 1; +} + +/** + * Match the given BanInfo to the given hostmask and optional IP addy + * @param ban BanInfo struct to match against + * @param mask Hostmask to match against + * @param ip IP to match against, set to 0 to not match this + * @return 1 for a match, 0 for no match + */ +int ban_match_mask(BanInfo * ban, char *mask, uint32 ip) +{ + char *user; + char *host; + + if (ban->type & (BANTYPE_USER | BANTYPE_USER_WILD)) { + host = strchr(mask, '@'); + if (host) { + *host++ = '\0'; + user = mask; + } else { + user = NULL; + } + } + if (ban->type & (BANTYPE_HOST | BANTYPE_HOST_WILD)) { + if (ban->type & (BANTYPE_USER | BANTYPE_USER_WILD)) { + if (!user) + host = mask; + } else { + host = strchr(mask, '@'); + if (host) + host++; + else + host = mask; + } + } + + return ban_match(ban, user, host, ip); +} + +/*************************************************************************/ + +/** + * Create and initialize a new ban list + * @param hashed Should the list be hashed or not? + * @return Pointer to the created BanList object + */ +BanList *banlist_create(int hashed) +{ + BanList *bl; + + bl = scalloc(1, sizeof(BanList)); + bl->flags = BANLIST_NONE; + + if (hashed) { + bl->flags |= BANLIST_HASHED; + bl->bans = scalloc(33, sizeof(BanInfo)); + } else { + bl->bans = scalloc(1, sizeof(BanInfo)); + } + + bl->next_id = 1; + + return bl; +} + +/*************************************************************************/ + +/** + * Create a ban and add it to the given banlist + * @param bl BanList object the banmask should be added to + * @param mask The mask to parse and add to the banlist + * @return 1 for success, 0 for failure + */ +int ban_add(BanList * bl, char *mask) +{ + int hash; + char *hostmask; + BanInfo *ban; + + hostmask = sstrdup(mask); + ban = ban_create(hostmask, bl->next_id); + free(hostmask); + if (!ban) + return 0; + + bl->next_id++; + + if (bl->flags & BANLIST_HASHED) { + hash = BANINFOHASH(ban); + ban->next = bl->bans[hash]; + ban->prev = NULL; + if (bl->bans[hash]) + bl->bans[hash]->prev = ban; + bl->bans[hash] = ban; + } else { + ban->next = *(bl->bans); + ban->prev = NULL; + if (*(bl->bans)) + (*(bl->bans))->prev = ban; + *(bl->bans) = ban; + } + + return 1; +} + +/*************************************************************************/ + +/** + * Delete the given ban from the given banlist + * @param bl BanList object the banmask should be deleted from + * @param ban The ban to be deleted, has to be in the given banlist + */ +void ban_del(BanList * bl, BanInfo * ban) +{ + if (ban->next) + ban->next->prev = ban->prev; + if (ban->prev) + ban->prev->next = ban->next; + else if (bl->flags & BANLIST_HASHED) + bl->bans[BANINFOHASH(ban)] = ban->next; + else + *(bl->bans) = ban->next; + + if (ban->user) + free(ban->user); + if (ban->host) + free(ban->host); + free(ban->mask); + + free(ban); +} + +/*************************************************************************/ + +/** + * Match a user, host, and ip to a banlist + * @param bl BanList that should be matched against + * @param user The user to match + * @param host The host to match + * @param ip The ip to match + * @return 1 for match, 0 for no match + */ +int banlist_match(BanList * bl, char *user, char *host, uint32 ip) +{ + BanInfo *ban; + + if (bl->flags & BANLIST_HASHED) { + for (ban = bl->bans[BANHOSTHASH(host)]; ban; ban = ban->next) { + if (ban_match(ban, user, host, ip)) + return 1; + } + /* Now check for all wildcard-qualified bans */ + for (ban = bl->bans[32]; ban; ban = ban->next) { + if (ban_match(ban, user, host, ip)) + return 1; + } + } else { + for (ban = *(bl->bans); ban; ban = ban->next) { + if (ban_match(ban, user, host, ip)) + return 1; + } + } + + /* We matched none, yay */ + return 0; +} + +/** + * Match a mask and ip to a banlist + * @param bl BanList that should be matched against + * @param mask The user@host mask to match + * @return 1 for match, 0 for no match + */ +int banlist_match_mask(BanList * bl, char *mask, uint32 ip) +{ + char *user; + char *host; + + host = strchr(mask, '@'); + if (host) { + *host++ = '\0'; + user = mask; + } else { + user = NULL; + host = mask; + } + + return banlist_match(bl, user, host, ip); +} + +/*************************************************************************/ + +/** + * Lookup a ban id in a banlist + * @param bl BanList to search in + * @param id Ban id to lookup + * @return Pointer to BanInfo for the given id, or NULL if not found + */ +BanInfo *banlist_lookup_id(BanList * bl, uint32 id) +{ + int i; + BanInfo *ban; + + if (bl->flags & BANLIST_HASHED) { + for (i = 0; i < 33; i++) { + for (ban = bl->bans[i]; ban; ban = ban->next) { + if (ban->id == id) + return ban; + } + } + } else { + for (ban = *(bl->bans); ban; ban = ban->next) { + if (ban->id == id) + return ban; + } + } +} + +/* EOF */ |