From 15f2896811cd2b9706422d83bc1ba126ba4088c8 Mon Sep 17 00:00:00 2001 From: Matias Fontanini Date: Wed, 4 Sep 2013 12:57:36 -0300 Subject: [PATCH] Added is_private and is_loopback methods to IPv4 and IPv6 addresses. --- include/ip_address.h | 21 ++++++ include/ipv6_address.h | 8 +++ src/ip_address.cpp | 22 ++++++ src/ipv6_address.cpp | 138 ++++++++++++++++++++------------------ tests/src/ipaddress.cpp | 31 +++++++++ tests/src/ipv6address.cpp | 6 ++ tests/src/utils.cpp | 2 +- 7 files changed, 161 insertions(+), 67 deletions(-) diff --git a/include/ip_address.h b/include/ip_address.h index 0c71189..decd51e 100644 --- a/include/ip_address.h +++ b/include/ip_address.h @@ -124,6 +124,27 @@ public: return ip_addr < rhs.ip_addr; } + /** + * \brief Returns true if this is a private IPv4 address. + * + * This takes into account the private network ranges defined in + * RFC 1918. Therefore, this method returns true if this address + * is in any of the following network ranges, false otherwise: + * + * - 192.168.0.0/16 + * - 10.0.0.0/8 + * - 172.16.0.0/12 + */ + bool is_private() const; + + /** + * \brief Returns true if this is a loopback IPv4 address. + * + * This method returns true if this address is in the address range + * 127.0.0.0/8, false otherwise. + */ + bool is_loopback() const; + /** * \brief Writes this address to a std::ostream. * diff --git a/include/ipv6_address.h b/include/ipv6_address.h index fbb4de8..904e436 100644 --- a/include/ipv6_address.h +++ b/include/ipv6_address.h @@ -184,6 +184,14 @@ public: return std::copy(begin(), end(), iter); } + /** + * \brief Returns true if this is a loopback IPv6 address. + * + * This method returns true if this address is the ::1/128 address, + * false otherwise. + */ + bool is_loopback() const; + /** * \brief Writes this address in hex-notation to a std::ostream. * diff --git a/src/ip_address.cpp b/src/ip_address.cpp index b41ea7a..fd8b99a 100644 --- a/src/ip_address.cpp +++ b/src/ip_address.cpp @@ -36,6 +36,14 @@ using std::string; namespace Tins{ +const AddressRange private_ranges[] = { + IPv4Address("192.168.0.0") / 16, + IPv4Address("10.0.0.0") / 8, + IPv4Address("172.16.0.0") / 12 +}; + +const AddressRange loopback_range = IPv4Address("127.0.0.0") / 8; + IPv4Address::IPv4Address(uint32_t ip) : ip_addr(Endian::be_to_host(ip)) { @@ -96,6 +104,20 @@ std::ostream &operator<<(std::ostream &output, const IPv4Address &addr) { return output;; } +bool IPv4Address::is_private() const { + const AddressRange *iter = private_ranges; + while(iter != private_ranges + 3) { + if(iter->contains(*this)) + return true; + ++iter; + } + return false; +} + +bool IPv4Address::is_loopback() const { + return loopback_range.contains(*this); +} + AddressRange operator/(const IPv4Address &addr, int mask) { if(mask > 32) throw std::logic_error("Prefix length cannot exceed 32"); diff --git a/src/ipv6_address.cpp b/src/ipv6_address.cpp index 2bcd3ec..c099011 100644 --- a/src/ipv6_address.cpp +++ b/src/ipv6_address.cpp @@ -44,72 +44,78 @@ #include "address_range.h" namespace Tins { - IPv6Address::IPv6Address() { - std::fill(address, address + address_size, 0); - } - - IPv6Address::IPv6Address(const char *addr) { - init(addr); - } - - IPv6Address::IPv6Address(const_iterator ptr) { - std::copy(ptr, ptr + address_size, address); - } - - IPv6Address::IPv6Address(const std::string &addr) { - init(addr.c_str()); - } - - void IPv6Address::init(const char *addr) { - #ifdef WIN32 - // mingw on linux somehow doesn't have InetPton - #ifdef _MSC_VER - if(InetPtonA(AF_INET6, addr, address) != 1) - throw malformed_address(); - #else - ULONG dummy1; - USHORT dummy2; - // Maybe change this, mingw doesn't have any other conversion function - if(RtlIpv6StringToAddressExA(addr, (IN6_ADDR*)address, &dummy1, &dummy2) != NO_ERROR) - throw malformed_address(); - #endif - #else - if(inet_pton(AF_INET6, addr, address) == 0) - throw malformed_address(); - #endif - } +const IPv6Address loopback_address = "::1"; - std::string IPv6Address::to_string() const { - char buffer[INET6_ADDRSTRLEN]; - #ifdef WIN32 - // mingw on linux somehow doesn't have InetNtop - #ifdef _MSC_VER - if(InetNtopA(AF_INET6, (PVOID)address, buffer, sizeof(buffer)) != 0) - throw malformed_address(); - #else - ULONG sz = sizeof(buffer); - if(RtlIpv6AddressToStringExA((const IN6_ADDR*)address, 0, 0, buffer, &sz) != NO_ERROR) - throw malformed_address(); - #endif - #else - if(inet_ntop(AF_INET6, address, buffer, sizeof(buffer)) == 0) - throw malformed_address(); - #endif - return buffer; - } - - AddressRange operator/(const IPv6Address &addr, int mask) { - if(mask > 128) - throw std::logic_error("Prefix length cannot exceed 128"); - IPv6Address last_addr; - IPv6Address::iterator it = last_addr.begin(); - while(mask > 8) { - *it = 0xff; - ++it; - mask -= 8; - } - *it = 0xff << (8 - mask); - return AddressRange::from_mask(addr, last_addr); - } +IPv6Address::IPv6Address() { + std::fill(address, address + address_size, 0); +} + +IPv6Address::IPv6Address(const char *addr) { + init(addr); +} + +IPv6Address::IPv6Address(const_iterator ptr) { + std::copy(ptr, ptr + address_size, address); +} + +IPv6Address::IPv6Address(const std::string &addr) { + init(addr.c_str()); +} + +void IPv6Address::init(const char *addr) { + #ifdef WIN32 + // mingw on linux somehow doesn't have InetPton + #ifdef _MSC_VER + if(InetPtonA(AF_INET6, addr, address) != 1) + throw malformed_address(); + #else + ULONG dummy1; + USHORT dummy2; + // Maybe change this, mingw doesn't have any other conversion function + if(RtlIpv6StringToAddressExA(addr, (IN6_ADDR*)address, &dummy1, &dummy2) != NO_ERROR) + throw malformed_address(); + #endif + #else + if(inet_pton(AF_INET6, addr, address) == 0) + throw malformed_address(); + #endif +} + +std::string IPv6Address::to_string() const { + char buffer[INET6_ADDRSTRLEN]; + #ifdef WIN32 + // mingw on linux somehow doesn't have InetNtop + #ifdef _MSC_VER + if(InetNtopA(AF_INET6, (PVOID)address, buffer, sizeof(buffer)) != 0) + throw malformed_address(); + #else + ULONG sz = sizeof(buffer); + if(RtlIpv6AddressToStringExA((const IN6_ADDR*)address, 0, 0, buffer, &sz) != NO_ERROR) + throw malformed_address(); + #endif + #else + if(inet_ntop(AF_INET6, address, buffer, sizeof(buffer)) == 0) + throw malformed_address(); + #endif + return buffer; +} + +bool IPv6Address::is_loopback() const { + return loopback_address == *this; +} + +AddressRange operator/(const IPv6Address &addr, int mask) { + if(mask > 128) + throw std::logic_error("Prefix length cannot exceed 128"); + IPv6Address last_addr; + IPv6Address::iterator it = last_addr.begin(); + while(mask > 8) { + *it = 0xff; + ++it; + mask -= 8; + } + *it = 0xff << (8 - mask); + return AddressRange::from_mask(addr, last_addr); +} } diff --git a/tests/src/ipaddress.cpp b/tests/src/ipaddress.cpp index d262264..7befdbd 100644 --- a/tests/src/ipaddress.cpp +++ b/tests/src/ipaddress.cpp @@ -49,3 +49,34 @@ TEST(IPAddressTest, LessThanOperator) { EXPECT_LT(addr1, "192.168.0.226"); EXPECT_LT(addr1, "193.0.0.0"); } + +TEST(IPAddressTest, IsPrivate) { + EXPECT_TRUE(IPv4Address("192.168.0.1").is_private()); + EXPECT_TRUE(IPv4Address("192.168.133.7").is_private()); + EXPECT_TRUE(IPv4Address("192.168.255.254").is_private()); + EXPECT_FALSE(IPv4Address("192.169.0.1").is_private()); + EXPECT_FALSE(IPv4Address("192.167.255.254").is_private()); + + EXPECT_TRUE(IPv4Address("10.0.0.1").is_private()); + EXPECT_TRUE(IPv4Address("10.5.1.2").is_private()); + EXPECT_TRUE(IPv4Address("10.255.255.254").is_private()); + EXPECT_FALSE(IPv4Address("11.0.0.1").is_private()); + EXPECT_FALSE(IPv4Address("9.255.255.254").is_private()); + + EXPECT_TRUE(IPv4Address("172.16.0.1").is_private()); + EXPECT_TRUE(IPv4Address("172.31.255.254").is_private()); + EXPECT_TRUE(IPv4Address("172.20.13.75").is_private()); + EXPECT_FALSE(IPv4Address("172.15.0.1").is_private()); + EXPECT_FALSE(IPv4Address("172.32.0.1").is_private()); + + EXPECT_FALSE(IPv4Address("100.100.100.100").is_private()); + EXPECT_FALSE(IPv4Address("199.199.29.10").is_private()); +} + +TEST(IPAddressTest, IsLoopback) { + EXPECT_TRUE(IPv4Address("127.0.0.1").is_loopback()); + EXPECT_TRUE(IPv4Address("127.0.0.0").is_loopback()); + EXPECT_TRUE(IPv4Address("127.255.255.254").is_loopback()); + EXPECT_FALSE(IPv4Address("126.255.255.254").is_loopback()); + EXPECT_FALSE(IPv4Address("128.0.0.0").is_loopback()); +} diff --git a/tests/src/ipv6address.cpp b/tests/src/ipv6address.cpp index 675634c..9957365 100644 --- a/tests/src/ipv6address.cpp +++ b/tests/src/ipv6address.cpp @@ -85,3 +85,9 @@ TEST(IPv6AddressTest, Copy) { addr1.copy(addr2.begin()); EXPECT_EQ(addr1, addr2); } + +TEST(IPv6AddressTest, IsLoopback) { + EXPECT_TRUE(IPv6Address("::1").is_loopback()); + EXPECT_FALSE(IPv6Address("::2").is_loopback()); + EXPECT_FALSE(IPv6Address("ffff::2").is_loopback()); +} diff --git a/tests/src/utils.cpp b/tests/src/utils.cpp index fd3f6bd..3bea77a 100644 --- a/tests/src/utils.cpp +++ b/tests/src/utils.cpp @@ -81,7 +81,7 @@ TEST_F(UtilsTest, ResolveDomain) { } TEST_F(UtilsTest, ResolveDomain6) { - IPv6Address localhost_ip("2001:500:88:200::10"); + IPv6Address localhost_ip("2606:2800:220:6d:26bf:1447:1097:aa7"); EXPECT_EQ(Utils::resolve_domain6("example.com"), localhost_ip); }