# UNDF: UNDF-2026-000000994 --- a/src/bzfs/AccessControlList.h +++ b/src/bzfs/AccessControlList.h @@ -12,6 +12,7 @@ // System headers #include +#include #include #include @@ -173,6 +174,15 @@ /* FIXME the AccessControlList assumes that 255 is a wildcard. it "should" * include a cidr mask with each address. it's still useful as is, though * see wildcard conversion occurs in convert(). + * + * PERF NOTE: ban(), hostBan(), and idBan() each use std::find() on our + * vector to detect duplicates before inserting. When called from merge() + * which processes every entry in a master ban list, this makes ban list + * loading O(B^2) where B = number of bans. For large master ban lists + * (hundreds to thousands of entries from community servers) this is + * significant. Fix: maintain a parallel unordered_set index keyed on + * the ban identity (IP+CIDR for IP bans, hostpat for host bans, idpat + * for ID bans) to achieve O(1) duplicate detection. */ /** This class handles the lists of bans and hostbans. It has functions for @@ -296,6 +306,15 @@ typedef std::vector idBanList_t; idBanList_t idBanList; + // O(1) duplicate detection indexes. + // Key for IP bans: (addr.s_addr, cidr) packed as uint64. + // Key for host bans: hostpat string. + // Key for ID bans: idpat string. + std::unordered_set banIndex; + std::unordered_set hostBanIndex; + std::unordered_set idBanIndex; + + static uint64_t banKey(in_addr addr, unsigned char cidr) { return ((uint64_t)addr.s_addr << 8) | cidr; } std::string banFile; private: --- a/src/bzfs/AccessControlList.cxx +++ b/src/bzfs/AccessControlList.cxx @@ -41,9 +41,12 @@ { BanInfo toban(ipAddr, bannedBy, period, cidr, fromMaster); if (reason) toban.reason = reason; - banList_t::iterator oldit = std::find(banList.begin(), banList.end(), toban); - if (oldit != banList.end()) // IP already in list? -> replace - *oldit = toban; + uint64_t key = banKey(ipAddr, cidr); + if (banIndex.count(key)) { + // IP already in list -> replace + banList_t::iterator oldit = std::find(banList.begin(), banList.end(), toban); + if (oldit != banList.end()) + *oldit = toban; + } else + { + banIndex.insert(key); banList.push_back(toban); + } } @@ -93,9 +96,12 @@ { HostBanInfo toban(hostpat, bannedBy, period,fromMaster); if (reason) toban.reason = reason; - hostBanList_t::iterator oldit = std::find(hostBanList.begin(), hostBanList.end(), toban); - if (oldit != hostBanList.end()) - *oldit = toban; + if (hostBanIndex.count(hostpat)) { + hostBanList_t::iterator oldit = std::find(hostBanList.begin(), hostBanList.end(), toban); + if (oldit != hostBanList.end()) + *oldit = toban; + } else + { + hostBanIndex.insert(hostpat); hostBanList.push_back(toban); + } } @@ -106,9 +112,12 @@ { IdBanInfo toban(idpat, bannedBy, period, fromMaster); if (reason) toban.reason = reason; - idBanList_t::iterator oldit = std::find(idBanList.begin(), idBanList.end(), toban); - if (oldit != idBanList.end()) - *oldit = toban; + if (idBanIndex.count(idpat)) { + idBanList_t::iterator oldit = std::find(idBanList.begin(), idBanList.end(), toban); + if (oldit != idBanList.end()) + *oldit = toban; + } else + { + idBanIndex.insert(idpat); idBanList.push_back(toban); + } }