From 06a0f182a5365c5d44df189d2f383a230c390dfe Mon Sep 17 00:00:00 2001 From: Gunnar Beutner Date: Wed, 14 Oct 2015 10:11:49 +0200 Subject: [PATCH] Implement the cidr_match function fixes #10354 --- doc/21-library-reference.md | 1 + lib/base/scriptutils.cpp | 1 + lib/base/utility.cpp | 75 +++++++++++++++++++++++++++++++++++++ lib/base/utility.hpp | 1 + 4 files changed, 78 insertions(+) diff --git a/doc/21-library-reference.md b/doc/21-library-reference.md index 92cdbde8e..48f8f5314 100644 --- a/doc/21-library-reference.md +++ b/doc/21-library-reference.md @@ -6,6 +6,7 @@ Function | Description --------------------------------|----------------------- regex(pattern, text) | Returns true if the regex pattern matches the text, false otherwise. match(pattern, text) | Returns true if the wildcard pattern matches the text, false otherwise. +cidr_match(pattern, ip) | Returns true if the CIDR pattern matches the IP address, false otherwise. IPv4 addresses are converted to IPv4-mapped IPv6 addresses before being matched against the pattern. len(value) | Returns the length of the value, i.e. the number of elements for an array or dictionary, or the length of the string in bytes. union(array, array, ...) | Returns an array containing all unique elements from the specified arrays. intersection(array, array, ...) | Returns an array containing all unique elements which are common to all specified arrays. diff --git a/lib/base/scriptutils.cpp b/lib/base/scriptutils.cpp index 31f7ab7c4..647530507 100644 --- a/lib/base/scriptutils.cpp +++ b/lib/base/scriptutils.cpp @@ -39,6 +39,7 @@ using namespace icinga; REGISTER_SAFE_SCRIPTFUNCTION(regex, &ScriptUtils::Regex); REGISTER_SAFE_SCRIPTFUNCTION(match, &Utility::Match); +REGISTER_SAFE_SCRIPTFUNCTION(cidr_match, &Utility::CidrMatch); REGISTER_SAFE_SCRIPTFUNCTION(len, &ScriptUtils::Len); REGISTER_SAFE_SCRIPTFUNCTION(union, &ScriptUtils::Union); REGISTER_SAFE_SCRIPTFUNCTION(intersection, &ScriptUtils::Intersection); diff --git a/lib/base/utility.cpp b/lib/base/utility.cpp index a987c3e4b..60ed577ca 100644 --- a/lib/base/utility.cpp +++ b/lib/base/utility.cpp @@ -143,6 +143,81 @@ bool Utility::Match(const String& pattern, const String& text) return (match(pattern.CStr(), text.CStr()) == 0); } +static void ParseIpMask(const String& ip, char mask[16], int *bits) +{ + String::SizeType slashp = ip.FindFirstOf("/"); + String uip; + + if (slashp == String::NPos) { + uip = ip; + *bits = 0; + } else { + uip = ip.SubStr(0, slashp); + *bits = Convert::ToLong(ip.SubStr(slashp + 1)); + } + + /* IPv4-mapped IPv6 address (::ffff:) */ + memset(mask, 0, 10); + memset(mask + 10, 0xff, 2); + if (inet_pton(AF_INET, uip.CStr(), mask + 12) < 1) { + if (inet_pton(AF_INET6, uip.CStr(), mask) < 1) { + BOOST_THROW_EXCEPTION(std::invalid_argument("Invalid IP address specified.")); + } + } else + *bits += 96; + + if (slashp == String::NPos) + *bits = 128; + + if (*bits > 128) + BOOST_THROW_EXCEPTION(std::invalid_argument("Mask must not be greater than 128.")); + + /* TODO: validate mask */ + for (int i = 0; i < 16; i++) { + int lbits = *bits - i * 8; + + if (lbits >= 8) + continue; + + if (mask[i] & (0xff >> lbits)) + BOOST_THROW_EXCEPTION(std::invalid_argument("Masked-off bits must all be zero.")); + } +} + +static bool IpMaskCheck(char addr[16], char mask[16], int bits) +{ + for (int i = 0; i < 16; i++) { + if (bits < 8) + return !((addr[i] ^ mask[i]) >> (8 - bits)); + + if (mask[i] != addr[i]) + return false; + + bits -= 8; + + if (bits == 0) + return true; + } + + return true; +} + +bool Utility::CidrMatch(const String& pattern, const String& ip) +{ + char mask[16], addr[16]; + int bits; + + try { + ParseIpMask(ip, addr, &bits); + } catch (const std::exception&) { + return false; + } + + ParseIpMask(pattern, mask, &bits); + + return IpMaskCheck(addr, mask, bits); +} + /** * Returns the directory component of a path. See dirname(3) for details. * diff --git a/lib/base/utility.hpp b/lib/base/utility.hpp index 7366d247a..8f386b052 100644 --- a/lib/base/utility.hpp +++ b/lib/base/utility.hpp @@ -65,6 +65,7 @@ public: static String GetSymbolName(const void *addr); static bool Match(const String& pattern, const String& text); + static bool CidrMatch(const String& pattern, const String& ip); static String DirName(const String& path); static String BaseName(const String& path);