#include <chrono>
#include <cstdint>
#include <stdexcept>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
#include "dynamic_string.hpp"
#include "indexed_multivector.hpp"
#include "ip_utils.hpp"
#include "matcher/ip_match.hpp"
#include "radixlib.h"
namespace ddwaf::matcher {
ip_match::ip_match(const std::vector<std::string_view> &ip_list)
: rtree_(radix_new(radix_tree_bits), radix_free)
{
if (!rtree_) {
throw std::runtime_error("failed to instantiate radix tree");
}
init_tree(ip_list);
}
ip_match::ip_match(
const indexed_multivector<std::string, std::pair<std::string, uint64_t>> &ip_list)
: rtree_(radix_new(radix_tree_bits), radix_free)
{
if (!rtree_) {
throw std::runtime_error("failed to instantiate radix tree");
}
for (const auto &[str, expiration] : ip_list) {
ipaddr ip{};
if (ddwaf::parse_cidr(str, ip)) {
prefix_t prefix;
radix_prefix_init(FAMILY_IPv6, ip.data, ip.mask, &prefix);
radix_put_if_absent(rtree_.get(), &prefix, expiration);
}
}
}
ip_match::ip_match(const std::vector<std::pair<std::string, uint64_t>> &ip_list)
: rtree_(radix_new(radix_tree_bits), radix_free)
{
if (!rtree_) {
throw std::runtime_error("failed to instantiate radix tree");
}
for (const auto &[str, expiration] : ip_list) {
ipaddr ip{};
if (ddwaf::parse_cidr(str, ip)) {
prefix_t prefix;
radix_prefix_init(FAMILY_IPv6, ip.data, ip.mask, &prefix);
radix_put_if_absent(rtree_.get(), &prefix, expiration);
}
}
}
[[nodiscard]] bool ip_match::match_ip(const ipaddr &ip) const
{
prefix_t radix_ip;
radix_prefix_init(FAMILY_IPv6, const_cast<uint8_t *>(ip.data), radix_tree_bits, &radix_ip);
auto *node = radix_matching_do(rtree_.get(), &radix_ip);
if (node == nullptr) {
return false;
}
if (node->expiration > 0) {
const uint64_t now = std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::system_clock::now().time_since_epoch())
.count();
if (node->expiration < now) {
return false;
}
}
return true;
}
std::pair<bool, dynamic_string> ip_match::match_impl(std::string_view str) const
{
if (!rtree_ || str.empty() || str.data() == nullptr) {
return {false, {}};
}
ddwaf::ipaddr ip{};
if (!ddwaf::parse_ip(str, ip)) {
return {false, {}};
}
ddwaf::ipv4_to_ipv6(ip);
if (!match_ip(ip)) {
return {false, {}};
}
return {true, std::string{str}};
}
}