]> granicus.if.org Git - icinga2/commitdiff
Implement the cidr_match function
authorGunnar Beutner <gunnar@beutner.name>
Wed, 14 Oct 2015 08:11:49 +0000 (10:11 +0200)
committerGunnar Beutner <gunnar@beutner.name>
Wed, 14 Oct 2015 08:14:01 +0000 (10:14 +0200)
fixes #10354

doc/21-library-reference.md
lib/base/scriptutils.cpp
lib/base/utility.cpp
lib/base/utility.hpp

index 92cdbde8ef197bd7a9d15f70d0bd9500ab1d6b16..48f8f531493e736a96012a3a2375d8742ac5d0ca 100644 (file)
@@ -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.
index 31f7ab7c4fad03c33aa4fca5b38bf77df55b3d69..6475305077675b18785335e66003e532c0bba06f 100644 (file)
@@ -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);
index a987c3e4bccad46a95e0692795f763c910a63268..60ed577cacf002057284449b45e2c702ab3e5c2a 100644 (file)
@@ -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:<ipv4-bits>) */
+       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.
  *
index 7366d247ac6f2244a0a9f02ad92cb56978842c64..8f386b05230413d50771046485fef7f27e2c83a5 100644 (file)
@@ -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);