From 3280195510ef8b357afe832259993a830a655348 Mon Sep 17 00:00:00 2001 From: Eduardo Arias Date: Fri, 23 Aug 2024 08:16:33 -0700 Subject: [PATCH] Use std::string_view as key for collection::Collection - Introduced transparent comparator & hash to be able to lookup for std::string_view in std::unordered_map where the key is a std::string, which avoids creating unnecessary std::string instances (with a heap-allocation and copy) - This is a C++20 feature, which introduced support for transparent comparisons in std::unordered_map. - Refactored code to remove duplication of MyEqual & MyHash - Replaced simple hash function with a variant of the Fowler-Noll-Vo hash function (FNV-1a) --- headers/modsecurity/anchored_set_variable.h | 39 +++------- .../anchored_set_variable_translation_proxy.h | 10 ++- headers/modsecurity/collection/collection.h | 54 ++++++++------ headers/modsecurity/collection/util.h | 72 ++++++++++++++++++ src/anchored_set_variable.cc | 13 ++-- .../backend/in_memory-per_process.cc | 44 ++++++----- .../backend/in_memory-per_process.h | 73 +++++-------------- src/collection/backend/lmdb.cc | 32 ++++---- src/collection/backend/lmdb.h | 37 ++++------ src/request_body_processor/multipart.cc | 36 +++++---- src/request_body_processor/multipart.h | 29 +------- src/variables/variable.h | 8 ++ 12 files changed, 228 insertions(+), 219 deletions(-) create mode 100644 headers/modsecurity/collection/util.h diff --git a/headers/modsecurity/anchored_set_variable.h b/headers/modsecurity/anchored_set_variable.h index a92884b8f1..486100f9f0 100644 --- a/headers/modsecurity/anchored_set_variable.h +++ b/headers/modsecurity/anchored_set_variable.h @@ -30,6 +30,7 @@ #endif #include "modsecurity/variable_value.h" +#include "modsecurity/collection/util.h" #ifndef HEADERS_MODSECURITY_ANCHORED_SET_VARIABLE_H_ #define HEADERS_MODSECURITY_ANCHORED_SET_VARIABLE_H_ @@ -46,49 +47,31 @@ class KeyExclusions; } -struct MyEqual { - bool operator()(const std::string& Left, const std::string& Right) const { - return Left.size() == Right.size() - && std::equal(Left.begin(), Left.end(), Right.begin(), - [](char a, char b) { - return tolower(a) == tolower(b); - }); - } -}; - -struct MyHash{ - size_t operator()(const std::string& Keyval) const { - // You might need a better hash function than this - size_t h = 0; - std::for_each(Keyval.begin(), Keyval.end(), [&](char c) { - h += tolower(c); - }); - return h; - } -}; - - class AnchoredSetVariable : public std::unordered_multimap { public: AnchoredSetVariable(Transaction *t, const std::string &name); ~AnchoredSetVariable(); +#if __cplusplus >= 202002L + using KeyType = std::string_view; +#else + using KeyType = const std::string&; +#endif + void unset(); - void set(const std::string &key, const std::string &value, + void set(KeyType key, std::string_view value, size_t offset); - void set(const std::string &key, const std::string &value, + void set(KeyType key, std::string_view value, size_t offset, size_t len); - void setCopy(std::string key, std::string value, size_t offset); - void resolve(std::vector &l); void resolve(std::vector &l, variables::KeyExclusions &ke); - void resolve(const std::string &key, + void resolve(KeyType key, std::vector &l); void resolveRegularExpression(Utils::Regex *r, @@ -98,7 +81,7 @@ class AnchoredSetVariable : public std::unordered_multimap &l, variables::KeyExclusions &ke); - std::unique_ptr resolveFirst(const std::string &key); + std::unique_ptr resolveFirst(KeyType key); Transaction *m_transaction; std::string m_name; diff --git a/headers/modsecurity/anchored_set_variable_translation_proxy.h b/headers/modsecurity/anchored_set_variable_translation_proxy.h index b1cda117e1..82180c1a40 100644 --- a/headers/modsecurity/anchored_set_variable_translation_proxy.h +++ b/headers/modsecurity/anchored_set_variable_translation_proxy.h @@ -45,6 +45,12 @@ class AnchoredSetVariableTranslationProxy { virtual ~AnchoredSetVariableTranslationProxy() = default; +#if __cplusplus >= 202002L + using KeyType = std::string_view; +#else + using KeyType = const std::string&; +#endif + void resolve(std::vector &l) { m_fount->resolve(l); translate(l); @@ -56,7 +62,7 @@ class AnchoredSetVariableTranslationProxy { translate(l); } - void resolve(const std::string &key, + void resolve(KeyType key, std::vector &l) { m_fount->resolve(key, l); translate(l); @@ -75,7 +81,7 @@ class AnchoredSetVariableTranslationProxy { translate(l); }; - std::unique_ptr resolveFirst(const std::string &key) { + std::unique_ptr resolveFirst(KeyType key) { std::vector l; resolve(l); diff --git a/headers/modsecurity/collection/collection.h b/headers/modsecurity/collection/collection.h index d8e0cc0c97..3585a26fa4 100644 --- a/headers/modsecurity/collection/collection.h +++ b/headers/modsecurity/collection/collection.h @@ -45,37 +45,43 @@ class Collection { explicit Collection(std::string_view a) : m_name(a) { } virtual ~Collection() { } - virtual bool storeOrUpdateFirst(const std::string& key, +#if __cplusplus >= 202002L + using KeyType = std::string_view; +#else + using KeyType = const std::string&; +#endif + + virtual bool storeOrUpdateFirst(KeyType key, std::string_view value) = 0; - virtual bool updateFirst(const std::string& key, + virtual bool updateFirst(KeyType key, std::string_view value) = 0; - virtual void del(const std::string& key) = 0; + virtual void del(KeyType key) = 0; - virtual void setExpiry(const std::string& key, int32_t expiry_seconds) = 0; + virtual void setExpiry(KeyType key, int32_t expiry_seconds) = 0; virtual std::unique_ptr resolveFirst( - const std::string& var) = 0; + KeyType key) = 0; - virtual void resolveSingleMatch(const std::string& var, + virtual void resolveSingleMatch(KeyType key, std::vector &l) = 0; - virtual void resolveMultiMatches(const std::string& var, + virtual void resolveMultiMatches(KeyType key, std::vector &l, variables::KeyExclusions &ke) = 0; - virtual void resolveRegularExpression(const std::string& var, + virtual void resolveRegularExpression(KeyType key, std::vector &l, variables::KeyExclusions &ke) = 0; /* storeOrUpdateFirst */ - bool storeOrUpdateFirst(std::string_view key, + bool storeOrUpdateFirst(KeyType key, std::string_view compartment, std::string_view value) { return storeOrUpdateFirst(nkey(compartment, key), value); } - bool storeOrUpdateFirst(std::string_view key, + bool storeOrUpdateFirst(KeyType key, std::string_view compartment, std::string_view compartment2, std::string_view value) { return storeOrUpdateFirst(nkey(compartment, compartment2, key), value); @@ -83,64 +89,64 @@ class Collection { /* updateFirst */ - bool updateFirst(std::string_view key, std::string_view compartment, + bool updateFirst(KeyType key, std::string_view compartment, std::string_view value) { return updateFirst(nkey(compartment, key), value); } - bool updateFirst(std::string_view key, std::string_view compartment, + bool updateFirst(KeyType key, std::string_view compartment, std::string_view compartment2, std::string_view value) { return updateFirst(nkey(compartment, compartment2, key), value); } /* del */ - void del(std::string_view key, std::string_view compartment) { + void del(KeyType key, std::string_view compartment) { del(nkey(compartment, key)); } - void del(std::string_view key, std::string_view compartment, + void del(KeyType key, std::string_view compartment, std::string_view compartment2) { del(nkey(compartment, compartment2, key)); } /* setExpiry */ - void setExpiry(std::string_view key, std::string_view compartment, + void setExpiry(KeyType key, std::string_view compartment, int32_t expiry_seconds) { setExpiry(nkey(compartment, key), expiry_seconds); } - void setExpiry(std::string_view key, std::string_view compartment, + void setExpiry(KeyType key, std::string_view compartment, std::string_view compartment2, int32_t expiry_seconds) { setExpiry(nkey(compartment, compartment2, key), expiry_seconds); } /* resolveFirst */ - std::unique_ptr resolveFirst(std::string_view var, + std::unique_ptr resolveFirst(KeyType var, std::string_view compartment) { return resolveFirst(nkey(compartment, var)); } - std::unique_ptr resolveFirst(std::string_view var, + std::unique_ptr resolveFirst(KeyType var, std::string_view compartment, std::string_view compartment2) { return resolveFirst(nkey(compartment, compartment2, var)); } /* resolveSingleMatch */ - void resolveSingleMatch(std::string_view var, + void resolveSingleMatch(KeyType var, std::string_view compartment, std::vector &l) { resolveSingleMatch(nkey(compartment, var), l); } - void resolveSingleMatch(std::string_view var, + void resolveSingleMatch(KeyType var, std::string_view compartment, std::string_view compartment2, std::vector &l) { resolveSingleMatch(nkey(compartment, compartment2, var), l); @@ -148,14 +154,14 @@ class Collection { /* resolveMultiMatches */ - void resolveMultiMatches(std::string_view var, + void resolveMultiMatches(KeyType var, std::string_view compartment, std::vector &l, variables::KeyExclusions &ke) { resolveMultiMatches(nkey(compartment, var), l, ke); } - void resolveMultiMatches(std::string_view var, + void resolveMultiMatches(KeyType var, std::string_view compartment, std::string_view compartment2, std::vector &l, variables::KeyExclusions &ke) { @@ -164,14 +170,14 @@ class Collection { /* resolveRegularExpression */ - void resolveRegularExpression(std::string_view var, + void resolveRegularExpression(KeyType var, std::string_view compartment, std::vector &l, variables::KeyExclusions &ke) { resolveRegularExpression(nkey(compartment, var), l, ke); } - void resolveRegularExpression(std::string_view var, + void resolveRegularExpression(KeyType var, std::string_view compartment, std::string_view compartment2, std::vector &l, variables::KeyExclusions &ke) { resolveRegularExpression(nkey(compartment, compartment2, var), l, ke); diff --git a/headers/modsecurity/collection/util.h b/headers/modsecurity/collection/util.h new file mode 100644 index 0000000000..73834baaa4 --- /dev/null +++ b/headers/modsecurity/collection/util.h @@ -0,0 +1,72 @@ +/* + * ModSecurity, http://www.modsecurity.org/ + * Copyright (c) 2015 - 2021 Trustwave Holdings, Inc. (http://www.trustwave.com/) + * + * You may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * If any of the files related to licensing are missing or if you have any + * other questions related to licensing please contact Trustwave Holdings, Inc. + * directly using the email address security@modsecurity.org. + * + */ + +#ifndef HEADERS_MODSECURITY_COLLECTION_UTIL_H_H +#define HEADERS_MODSECURITY_COLLECTION_UTIL_H_H + +#ifdef __cplusplus +#include +#include + + +namespace modsecurity { + + +struct MyEqual { +#if __cplusplus >= 202002L + using is_transparent = void; + + template + bool operator()(const T& Left, const U& Right) const { +#else + bool operator()(const std::string& Left, const std::string& Right) const { +#endif + return Left.size() == Right.size() + && std::equal(Left.begin(), Left.end(), Right.begin(), + [](char a, char b) { + return tolower(a) == tolower(b); + }); + } +}; + + +struct MyHash{ +#if __cplusplus >= 202002L + using is_transparent = void; + + template + std::size_t operator()(const T& Keyval) const { +#else + std::size_t operator()(const std::string& Keyval) const { +#endif + // computes the hash using a variant of the + // Fowler-Noll-Vo hash function (FNV-1a) + constexpr std::uint64_t prime{0x01000193}; // FNV prime + std::size_t hash{0x811c9dc5}; // FNV offset basis + for (char c : Keyval) { + hash ^= tolower(c); + hash *= prime; + } + return hash; + } +}; + + +} // namespace modsecurity + + +#endif // __cplusplus + +#endif // HEADERS_MODSECURITY_COLLECTION_UTIL_H_H diff --git a/src/anchored_set_variable.cc b/src/anchored_set_variable.cc index 307e75468c..bc9c299e67 100644 --- a/src/anchored_set_variable.cc +++ b/src/anchored_set_variable.cc @@ -50,16 +50,16 @@ void AnchoredSetVariable::unset() { } -void AnchoredSetVariable::set(const std::string &key, - const std::string &value, size_t offset, size_t len) { +void AnchoredSetVariable::set(KeyType key, + std::string_view value, size_t offset, size_t len) { auto var = new VariableValue(m_name, key, value); var->addOrigin(len, offset); emplace(key, var); } -void AnchoredSetVariable::set(const std::string &key, - const std::string &value, size_t offset) { +void AnchoredSetVariable::set(KeyType key, + std::string_view value, size_t offset) { auto var = new VariableValue(m_name, key, value); var->addOrigin(value.size(), offset); emplace(key, var); @@ -88,8 +88,9 @@ void AnchoredSetVariable::resolve( } -void AnchoredSetVariable::resolve(const std::string &key, +void AnchoredSetVariable::resolve(KeyType key, std::vector &l) { + auto range = this->equal_range(key); for (auto it = range.first; it != range.second; ++it) { l.push_back(new VariableValue(*it->second)); @@ -98,7 +99,7 @@ void AnchoredSetVariable::resolve(const std::string &key, std::unique_ptr AnchoredSetVariable::resolveFirst( - const std::string &key) { + KeyType key) { if (auto search = this->find(key); search != this->end()) { return std::make_unique(search->second->getValue()); diff --git a/src/collection/backend/in_memory-per_process.cc b/src/collection/backend/in_memory-per_process.cc index 97272f1c6d..8026227635 100644 --- a/src/collection/backend/in_memory-per_process.cc +++ b/src/collection/backend/in_memory-per_process.cc @@ -30,9 +30,7 @@ #include "src/utils/string.h" -namespace modsecurity { -namespace collection { -namespace backend { +namespace modsecurity::collection::backend { InMemoryPerProcess::InMemoryPerProcess(std::string_view name) : @@ -46,7 +44,7 @@ InMemoryPerProcess::~InMemoryPerProcess() { template -inline void __store(Map &map, const std::string& key, std::string_view value) { +inline void __store(Map &map, InMemoryPerProcess::KeyType key, std::string_view value) { // NOTE: should be called with write-lock previously acquired map.emplace(key, value); @@ -55,7 +53,7 @@ inline void __store(Map &map, const std::string& key, std::string_view value) { template inline bool __updateFirst(Map &map, - const std::string& key, + InMemoryPerProcess::KeyType key, std::string_view value) { // NOTE: should be called with write-lock previously acquired @@ -68,13 +66,13 @@ inline bool __updateFirst(Map &map, } -void InMemoryPerProcess::store(const std::string& key, std::string_view value) { +void InMemoryPerProcess::store(KeyType key, std::string_view value) { const std::lock_guard lock(m_mutex); // write lock (exclusive access) __store(m_map, key, value); } -bool InMemoryPerProcess::storeOrUpdateFirst(const std::string& key, +bool InMemoryPerProcess::storeOrUpdateFirst(KeyType key, std::string_view value) { const std::lock_guard lock(m_mutex); // write lock (exclusive access) if (__updateFirst(m_map, key, value) == false) { @@ -84,29 +82,31 @@ bool InMemoryPerProcess::storeOrUpdateFirst(const std::string& key, } -bool InMemoryPerProcess::updateFirst(const std::string& key, +bool InMemoryPerProcess::updateFirst(KeyType key, std::string_view value) { const std::lock_guard lock(m_mutex); // write lock (exclusive access) return __updateFirst(m_map, key, value); } -void InMemoryPerProcess::del(const std::string& key) { +void InMemoryPerProcess::del(KeyType key) { const std::lock_guard lock(m_mutex); // write lock (exclusive access) - m_map.erase(key); + const auto range = m_map.equal_range(key); + m_map.erase(range.first, range.second); } -void InMemoryPerProcess::delIfExpired(const std::string& key) { +void InMemoryPerProcess::delIfExpired(KeyType key) { const std::lock_guard lock(m_mutex); // write lock (exclusive access) + const auto range = m_map.equal_range(key); // Double check the status while within the mutex - const auto iter = std::find_if(m_map.begin(), m_map.end(), - [&key](const auto &x) { return x.first == key && x.second.isExpired(); }); - if (iter != m_map.end()) { - m_map.erase(key); + const auto iter = std::find_if(range.first, range.second, + [](const auto &x) { return x.second.isExpired(); }); + if (iter != range.second) { + m_map.erase(range.first, range.second); } } -void InMemoryPerProcess::setExpiry(const std::string& key, int32_t expiry_seconds) { +void InMemoryPerProcess::setExpiry(KeyType key, int32_t expiry_seconds) { const std::lock_guard lock(m_mutex); // write lock (exclusive access) if (const auto search = m_map.find(key); search != m_map.end()) { @@ -120,7 +120,7 @@ void InMemoryPerProcess::setExpiry(const std::string& key, int32_t expiry_second } -void InMemoryPerProcess::resolveSingleMatch(const std::string& var, +void InMemoryPerProcess::resolveSingleMatch(KeyType var, std::vector &l) { std::list expiredVars; @@ -145,7 +145,7 @@ void InMemoryPerProcess::resolveSingleMatch(const std::string& var, } -void InMemoryPerProcess::resolveMultiMatches(const std::string& var, +void InMemoryPerProcess::resolveMultiMatches(KeyType var, std::vector &l, variables::KeyExclusions &ke) { const auto keySize = var.size(); l.reserve(15); @@ -192,7 +192,7 @@ void InMemoryPerProcess::resolveMultiMatches(const std::string& var, } -void InMemoryPerProcess::resolveRegularExpression(const std::string& var, +void InMemoryPerProcess::resolveRegularExpression(KeyType var, std::vector &l, variables::KeyExclusions &ke) { Utils::Regex r(var, true); std::list expiredVars; @@ -225,7 +225,7 @@ void InMemoryPerProcess::resolveRegularExpression(const std::string& var, std::unique_ptr InMemoryPerProcess::resolveFirst( - const std::string& var) { + KeyType var) { std::unique_ptr ret; std::list expiredVars; @@ -252,6 +252,4 @@ std::unique_ptr InMemoryPerProcess::resolveFirst( } -} // namespace backend -} // namespace collection -} // namespace modsecurity +} // namespace modsecurity::collection::backend diff --git a/src/collection/backend/in_memory-per_process.h b/src/collection/backend/in_memory-per_process.h index b6f8c14396..24c95934f4 100644 --- a/src/collection/backend/in_memory-per_process.h +++ b/src/collection/backend/in_memory-per_process.h @@ -14,7 +14,9 @@ */ -#ifdef __cplusplus +#ifndef SRC_COLLECTION_BACKEND_IN_MEMORY_PER_PROCESS_H_ +#define SRC_COLLECTION_BACKEND_IN_MEMORY_PER_PROCESS_H_ + #include #include #include @@ -24,89 +26,53 @@ #include #include #include -#endif - #include "modsecurity/variable_value.h" #include "modsecurity/collection/collection.h" +#include "modsecurity/collection/util.h" #include "src/collection/backend/collection_data.h" #include "src/variables/variable.h" -#ifndef SRC_COLLECTION_BACKEND_IN_MEMORY_PER_PROCESS_H_ -#define SRC_COLLECTION_BACKEND_IN_MEMORY_PER_PROCESS_H_ - -#ifdef __cplusplus -namespace modsecurity { -namespace collection { -namespace backend { +namespace modsecurity::collection::backend { -/* - * FIXME: - * - * This was an example grabbed from: - * http://stackoverflow.com/questions/8627698/case-insensitive-stl-containers-e-g-stdunordered-set - * - * We have to have a better hash function, maybe based on the std::hash. - * - */ -struct MyEqual { - bool operator()(const std::string& Left, const std::string& Right) const { - return Left.size() == Right.size() - && std::equal(Left.begin(), Left.end(), Right.begin(), - [](char a, char b) { - return tolower(a) == tolower(b); - }); - } -}; - -struct MyHash{ - size_t operator()(const std::string& Keyval) const { - // You might need a better hash function than this - size_t h = 0; - std::for_each(Keyval.begin(), Keyval.end(), [&](char c) { - h += tolower(c); - }); - return h; - } -}; class InMemoryPerProcess : public Collection { public: explicit InMemoryPerProcess(std::string_view name); ~InMemoryPerProcess() override; - void store(const std::string& key, std::string_view value); + void store(KeyType key, std::string_view value); - bool storeOrUpdateFirst(const std::string& key, + bool storeOrUpdateFirst(KeyType key, std::string_view value) override; - bool updateFirst(const std::string& key, + bool updateFirst(KeyType key, std::string_view value) override; - void del(const std::string& key) override; + void del(KeyType key) override; - void delIfExpired(const std::string& key); + void delIfExpired(KeyType key); - void setExpiry(const std::string& key, int32_t expiry_seconds) override; + void setExpiry(KeyType key, int32_t expiry_seconds) override; - std::unique_ptr resolveFirst(const std::string& var) override; + std::unique_ptr resolveFirst(KeyType var) override; - void resolveSingleMatch(const std::string& var, + void resolveSingleMatch(KeyType var, std::vector &l) override; - void resolveMultiMatches(const std::string& var, + void resolveMultiMatches(KeyType var, std::vector &l, variables::KeyExclusions &ke) override; - void resolveRegularExpression(const std::string& var, + void resolveRegularExpression(KeyType var, std::vector &l, variables::KeyExclusions &ke) override; /* store */ - void store(std::string_view key, std::string_view compartment, + void store(KeyType key, std::string_view compartment, std::string_view value) { store(nkey(compartment, key), value); } - void store(std::string_view key, std::string_view compartment, + void store(KeyType key, std::string_view compartment, std::string_view compartment2, std::string_view value) { store(nkey(compartment, compartment2, key), value); } @@ -117,10 +83,7 @@ class InMemoryPerProcess : std::shared_mutex m_mutex; }; -} // namespace backend -} // namespace collection -} // namespace modsecurity -#endif +} // namespace modsecurity::collection::backend #endif // SRC_COLLECTION_BACKEND_IN_MEMORY_PER_PROCESS_H_ diff --git a/src/collection/backend/lmdb.cc b/src/collection/backend/lmdb.cc index 64a6534c73..5b265db768 100644 --- a/src/collection/backend/lmdb.cc +++ b/src/collection/backend/lmdb.cc @@ -33,9 +33,7 @@ #undef LMDB_STDOUT_COUT -namespace modsecurity { -namespace collection { -namespace backend { +namespace modsecurity::collection::backend { #ifdef WITH_LMDB @@ -57,9 +55,9 @@ int LMDB::txn_begin(unsigned int flags, MDB_txn **ret) { } } -void LMDB::string2val(const std::string& str, MDB_val *val) { +inline void string2val(std::string_view str, MDB_val *val) { val->mv_size = sizeof(char)*(str.size()); - val->mv_data = const_cast(str.c_str()); + val->mv_data = const_cast(str.data()); } @@ -154,7 +152,7 @@ void LMDB::lmdb_debug(int rc, const std::string &op, const std::string &scope) { } -std::unique_ptr LMDB::resolveFirst(const std::string& var) { +std::unique_ptr LMDB::resolveFirst(KeyType var) { int rc; MDB_val mdb_key; MDB_val mdb_value; @@ -192,7 +190,7 @@ std::unique_ptr LMDB::resolveFirst(const std::string& var) { } -void LMDB::setExpiry(const std::string &key, int32_t expiry_seconds) { +void LMDB::setExpiry(KeyType key, int32_t expiry_seconds) { int rc; MDB_txn *txn; MDB_val mdb_key; @@ -250,7 +248,7 @@ void LMDB::setExpiry(const std::string &key, int32_t expiry_seconds) { return; } -void LMDB::delIfExpired(const std::string& key) { +void LMDB::delIfExpired(KeyType key) { MDB_txn *txn; MDB_val mdb_key; MDB_val mdb_value_ret; @@ -295,7 +293,7 @@ void LMDB::delIfExpired(const std::string& key) { return; } -bool LMDB::storeOrUpdateFirst(const std::string &key, +bool LMDB::storeOrUpdateFirst(KeyType key, std::string_view value) { int rc; MDB_txn *txn; @@ -355,7 +353,7 @@ bool LMDB::storeOrUpdateFirst(const std::string &key, } -void LMDB::resolveSingleMatch(const std::string& var, +void LMDB::resolveSingleMatch(KeyType var, std::vector &l) { int rc; MDB_txn *txn; @@ -399,7 +397,7 @@ void LMDB::resolveSingleMatch(const std::string& var, } -bool LMDB::updateFirst(const std::string &key, +bool LMDB::updateFirst(KeyType key, std::string_view value) { int rc; MDB_txn *txn; @@ -463,7 +461,7 @@ bool LMDB::updateFirst(const std::string &key, } -void LMDB::del(const std::string& key) { +void LMDB::del(KeyType key) { int rc; MDB_txn *txn; MDB_val mdb_key; @@ -507,7 +505,7 @@ void LMDB::del(const std::string& key) { return; } -void LMDB::resolveMultiMatches(const std::string& var, +void LMDB::resolveMultiMatches(KeyType var, std::vector &l, variables::KeyExclusions &ke) { MDB_val key, data; @@ -560,7 +558,7 @@ void LMDB::resolveMultiMatches(const std::string& var, } const char *a = reinterpret_cast(key.mv_data); - if (strncmp(var.c_str(), a, keySize) == 0) { + if (strncmp(var.data(), a, keySize) == 0) { std::string key_to_insert(reinterpret_cast(key.mv_data), key.mv_size); l.insert(l.begin(), new VariableValue(m_name, key_to_insert, collectionData.getValue())); } @@ -578,7 +576,7 @@ void LMDB::resolveMultiMatches(const std::string& var, } -void LMDB::resolveRegularExpression(const std::string& var, +void LMDB::resolveRegularExpression(KeyType var, std::vector &l, variables::KeyExclusions &ke) { MDB_val key, data; @@ -672,6 +670,4 @@ MDBEnvProvider::~MDBEnvProvider() { #endif -} // namespace backend -} // namespace collection -} // namespace modsecurity +} // namespace modsecurity::collection::backend diff --git a/src/collection/backend/lmdb.h b/src/collection/backend/lmdb.h index 54630e9545..fdc90c9c5d 100644 --- a/src/collection/backend/lmdb.h +++ b/src/collection/backend/lmdb.h @@ -14,7 +14,9 @@ */ -#ifdef __cplusplus +#ifndef SRC_COLLECTION_BACKEND_LMDB_H_ +#define SRC_COLLECTION_BACKEND_LMDB_H_ + #include #include #include @@ -23,7 +25,6 @@ #include #include #include -#endif #ifdef WITH_LMDB #include @@ -36,15 +37,9 @@ #include "modsecurity/collection/collection.h" #include "src/variables/variable.h" -#ifndef SRC_COLLECTION_BACKEND_LMDB_H_ -#define SRC_COLLECTION_BACKEND_LMDB_H_ - #ifdef WITH_LMDB -#ifdef __cplusplus -namespace modsecurity { -namespace collection { -namespace backend { +namespace modsecurity::collection::backend { /** @@ -97,43 +92,39 @@ class LMDB : public: explicit LMDB(const std::string &name); - bool storeOrUpdateFirst(const std::string &key, + bool storeOrUpdateFirst(KeyType key, std::string_view value) override; - bool updateFirst(const std::string &key, + bool updateFirst(KeyType key, std::string_view value) override; - void del(const std::string& key) override; + void del(KeyType key) override; - void setExpiry(const std::string& key, int32_t expiry_seconds) override; + void setExpiry(KeyType key, int32_t expiry_seconds) override; - std::unique_ptr resolveFirst(const std::string& var) override; + std::unique_ptr resolveFirst(KeyType var) override; - void resolveSingleMatch(const std::string& var, + void resolveSingleMatch(KeyType var, std::vector &l) override; - void resolveMultiMatches(const std::string& var, + void resolveMultiMatches(KeyType var, std::vector &l, variables::KeyExclusions &ke) override; - void resolveRegularExpression(const std::string& var, + void resolveRegularExpression(KeyType var, std::vector &l, variables::KeyExclusions &ke) override; private: int txn_begin(unsigned int flags, MDB_txn **ret); - void string2val(const std::string& str, MDB_val *val); void inline lmdb_debug(int rc, const std::string &op, const std::string &scope); - void delIfExpired(const std::string& key); + void delIfExpired(KeyType key); MDB_env *m_env; MDB_dbi m_dbi; bool isOpen; }; -} // namespace backend -} // namespace collection -} // namespace modsecurity -#endif +} // namespace modsecurity::collection::backend #endif // WITH_LMDB diff --git a/src/request_body_processor/multipart.cc b/src/request_body_processor/multipart.cc index fd140329b8..5f606d1088 100644 --- a/src/request_body_processor/multipart.cc +++ b/src/request_body_processor/multipart.cc @@ -31,11 +31,13 @@ #include #include #include +#include #include "modsecurity/rules_set.h" #include "modsecurity/collection/collections.h" #include "src/utils/string.h" +using namespace std::literals; namespace modsecurity { namespace RequestBodyProcessor { @@ -752,19 +754,23 @@ int Multipart::process_part_header(std::string *error, int offset) { m_mpp->m_last_header_line.assign(""); } - if (m_mpp->m_headers.count("Content-Disposition") == 0) { - ms_dbg_a(m_transaction, 1, - "Multipart: Part missing Content-Disposition header."); - - error->assign("Multipart: Part missing " \ - "Content-Disposition header."); +#if __cplusplus >= 202002L + const auto content_disposition_header_name = "Content-Disposition"sv; +#else + const char *content_disposition_header_name = "Content-Disposition"; +#endif + const auto it = m_mpp->m_headers.find(content_disposition_header_name); + if (it == m_mpp->m_headers.end()) { + const auto err = "Multipart: Part missing Content-Disposition header."; + ms_dbg_a(m_transaction, 1, err); + error->assign(err); return false; } - header_value = m_mpp->m_headers.at("Content-Disposition").second; + header_value = it->second.second; try { rc = parse_content_disposition(header_value.c_str(), - m_mpp->m_headers.at("Content-Disposition").first); + it->second.first); } catch (...) { ms_dbg_a(m_transaction, 1, "Multipart: Unexpected error parsing Content-Disposition header."); @@ -858,10 +864,11 @@ int Multipart::process_part_header(std::string *error, int offset) { utils::string::chomp(new_value); /* update the header value in the table */ - header_value = m_mpp->m_headers.at( - m_mpp->m_last_header_name).second; - new_value = header_value + " " + new_value; - m_mpp->m_headers.at(m_mpp->m_last_header_name).second = new_value; + const auto it = m_mpp->m_headers.find(m_mpp->m_last_header_name); + assert(it != m_mpp->m_headers.end()); + header_value = it->second.second; + new_value = fmt::format("{} {}", header_value, new_value); + it->second.second = new_value; ms_dbg_a(m_transaction, 9, "Multipart: Continued folder header \"" \ @@ -949,10 +956,9 @@ int Multipart::process_part_header(std::string *error, int offset) { m_mpp->m_last_header_name.assign(header_name); - m_mpp->m_headers.emplace( - std::string(header_name), std::make_pair(offset - len + i, - std::string(header_value))); + header_name, std::make_pair(offset - len + i, + header_value)); ms_dbg_a(m_transaction, 9, "Multipart: Added part header \"" + header_name \ diff --git a/src/request_body_processor/multipart.h b/src/request_body_processor/multipart.h index bfada4b979..0d2abae924 100644 --- a/src/request_body_processor/multipart.h +++ b/src/request_body_processor/multipart.h @@ -13,14 +13,15 @@ * */ +#ifndef SRC_REQUEST_BODY_PROCESSOR_MULTIPART_H_ +#define SRC_REQUEST_BODY_PROCESSOR_MULTIPART_H_ + #include #include #include #include #include - -#ifndef SRC_REQUEST_BODY_PROCESSOR_MULTIPART_H_ -#define SRC_REQUEST_BODY_PROCESSOR_MULTIPART_H_ +#include #include "modsecurity/transaction.h" @@ -32,28 +33,6 @@ namespace RequestBodyProcessor { #define MULTIPART_FILE 2 -struct MyHash { - size_t operator()(const std::string& Keyval) const { - size_t h = 0; - std::for_each(Keyval.begin(), Keyval.end(), [&](char c) { - h += tolower(c); - }); - return h; - } -}; - - -struct MyEqual { - bool operator()(const std::string& Left, const std::string& Right) const { - return Left.size() == Right.size() - && std::equal(Left.begin(), Left.end(), Right.begin(), - [](char a, char b) { - return tolower(a) == tolower(b); - }); - } -}; - - class MultipartPartTmpFile { public: explicit MultipartPartTmpFile(Transaction *transaction) diff --git a/src/variables/variable.h b/src/variables/variable.h index bd5b65cc6e..5e6359200a 100644 --- a/src/variables/variable.h +++ b/src/variables/variable.h @@ -183,9 +183,13 @@ class VariableMonkeyResolution { collection_delimiter_offset = variable.find(":"); } std::string_view col; // collection name excluding individual variable specification +#if __cplusplus >= 202002L + std::string_view var; // variable within the collection +#else // NOTE: cannot use std::string_view here because resolve // needs to receive a std::string to call find std::string var; // variable within the collection +#endif if (collection_delimiter_offset == std::string::npos) { col = variable; } else { @@ -509,9 +513,13 @@ class VariableMonkeyResolution { } } else { const auto col = variable.substr(0, collection); +#if __cplusplus >= 202002L + const auto var = variable.substr(collection + 1); +#else // NOTE: cannot use std::string_view here because resolveFirst // needs to receive a std::string to call find const std::string var(variable.substr(collection + 1)); +#endif if (comp(col, "ARGS")) { vv = t->m_variableArgs.resolveFirst(var); } else if (comp(variable, "ARGS_NAMES")) {