diff --git a/common/network/IPV4Address.cpp b/common/network/IPV4Address.cpp index 3dba9a8fb8..ba619ad0e8 100644 --- a/common/network/IPV4Address.cpp +++ b/common/network/IPV4Address.cpp @@ -14,7 +14,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * IPV4Address.cpp - * A IPV4 address + * An IPV4 address * Copyright (C) 2011 Simon Newton */ diff --git a/common/network/IPV6Address.cpp b/common/network/IPV6Address.cpp new file mode 100644 index 0000000000..5a46d953cc --- /dev/null +++ b/common/network/IPV6Address.cpp @@ -0,0 +1,170 @@ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * IPV6Address.cpp + * An IPV6 address + * Copyright (C) 2023 Peter Newman + */ + +#if HAVE_CONFIG_H +#include +#endif // HAVE_CONFIG_H + +#ifdef HAVE_SYS_SOCKET_H +#include // Required by FreeBSD +#endif // HAVE_SYS_SOCKET_H +#ifdef HAVE_ARPA_INET_H +#include +#endif // HAVE_ARPA_INET_H +#ifdef HAVE_NETINET_IN_H +#include // Required by FreeBSD +#endif // HAVE_NETINET_IN_H + +#include +#include +#include +#include +#include + +#include "common/network/NetworkUtilsInternal.h" +#include "ola/Logging.h" +#include "ola/network/IPV6Address.h" +#include "ola/network/NetworkUtils.h" + +namespace ola { +namespace network { + +using std::string; + +IPV6Address::IPV6Address(const uint8_t *address) { + // TODO(Peter): Deal with any network byte order conversion? + memcpy(&m_address.s6_addr[0], address, sizeof (struct in6_addr)); +} + +bool IPV6Address::operator<(const IPV6Address &other) const { + // TODO(Peter): Deal with any network byte order conversion? + return (memcmp(&m_address.s6_addr[0], + &other.m_address.s6_addr[0], + sizeof (struct in6_addr)) < 0); +} + +bool IPV6Address::operator>(const IPV6Address &other) const { + // TODO(Peter): Deal with any network byte order conversion? + return (memcmp(&m_address.s6_addr[0], + &other.m_address.s6_addr[0], + sizeof (struct in6_addr)) > 0); +} + +bool IPV6StringToAddress(const string &address, struct in6_addr *addr) { + bool ok; +//// TODO(Peter): This currently allows some rather quirky values as per +//// inet_pton, we may want to restrict that in future to match IPV6Validator +//// if that deviates + + if (address.empty()) { + // Don't bother trying to extract an address if we weren't given one + return false; + } + +#ifdef HAVE_INET_PTON + ok = (1 == inet_pton(AF_INET6, address.data(), addr)); +#else + OLA_FATAL << "Failed to convert string to address, inet_pton unavailable"; + return false; +#endif // HAVE_INET_PTON + + if (!ok) { + OLA_WARN << "Could not convert address " << address; + } + return ok; +} + +bool IPV6Address::IsWildcard() const { + return IN6_IS_ADDR_UNSPECIFIED(&m_address); +} + +string IPV6Address::ToString() const { + struct in6_addr addr; + addr = m_address; +#ifdef HAVE_INET_NTOP + char str[INET6_ADDRSTRLEN]; + if (inet_ntop(AF_INET6, &addr, str, INET6_ADDRSTRLEN) == NULL) { + OLA_FATAL << "Failed to convert address to string using inet_ntop"; + return NULL; + } + return str; +#else + OLA_FATAL << "Failed to convert address to string, inet_ntop unavailable"; + return NULL; +#endif // HAVE_INET_NTOP +} + +IPV6Address* IPV6Address::FromString(const string &address) { + struct in6_addr addr; + if (!IPV6StringToAddress(address, &addr)) { + return NULL; + } + + return new IPV6Address(addr); +} + +bool IPV6Address::FromString(const string &address, IPV6Address *target) { + struct in6_addr addr; + if (!IPV6StringToAddress(address, &addr)) { + return false; + } + *target = IPV6Address(addr); + return true; +} + +IPV6Address IPV6Address::FromStringOrDie(const string &address) { + struct in6_addr addr; + assert(IPV6StringToAddress(address, &addr)); + return IPV6Address(addr); +} + +/*bool IPV6Address::ToCIDRMask(IPV6Address address, uint8_t *mask) { + uint32_t netmask = NetworkToHost(address.AsInt()); + uint8_t bits = 0; + bool seen_one = false; + for (uint8_t i = 0; i < std::numeric_limits::digits; i++) { + if (netmask & 1) { + bits++; + seen_one = true; + } else { + if (seen_one) { + return false; + } + } + netmask = netmask >> 1; + } + *mask = bits; + return true; +}*/ + +IPV6Address IPV6Address::WildCard() { + in6_addr wildCard = IN6ADDR_ANY_INIT; + // TODO(Peter): Deal with any host to network conversion... + return IPV6Address(wildCard); +} + +IPV6Address IPV6Address::Loopback() { + in6_addr loopback = IN6ADDR_LOOPBACK_INIT; + // TODO(Peter): Deal with any host to network conversion... + // return IPV6Address(HostToNetwork(IN6ADDR_LOOPBACK_INIT)); + return IPV6Address(loopback); +} +} // namespace network +} // namespace ola diff --git a/common/network/IPV6AddressTest.cpp b/common/network/IPV6AddressTest.cpp new file mode 100644 index 0000000000..05400cb82a --- /dev/null +++ b/common/network/IPV6AddressTest.cpp @@ -0,0 +1,209 @@ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * IPV6AddressTest.cpp + * Test fixture for the IPV6Address class + * Copyright (C) 2023 Peter Newman + */ + +#include + +#include +#include +#include +#include +#include +#include +#include "common/network/NetworkUtilsInternal.h" +#include "ola/network/IPV6Address.h" +#include "ola/network/NetworkUtils.h" +#include "ola/testing/TestUtils.h" + + +using ola::network::IPV6Address; +using ola::network::HostToNetwork; +using std::auto_ptr; +using std::string; +using std::vector; + +class IPV6AddressTest: public CppUnit::TestFixture { + CPPUNIT_TEST_SUITE(IPV6AddressTest); + CPPUNIT_TEST(testIPV6Address); + CPPUNIT_TEST(testWildcard); + CPPUNIT_TEST(testLoopback); + CPPUNIT_TEST_SUITE_END(); + + public: + void testIPV6Address(); + void testWildcard(); + // TODO(Peter): Test the all-nodes link-local multicast group if we add it + void testLoopback(); +}; + +CPPUNIT_TEST_SUITE_REGISTRATION(IPV6AddressTest); + + +/* + * Test the IPV6 Address class works + */ +void IPV6AddressTest::testIPV6Address() { + IPV6Address wildcard_address; + OLA_ASSERT_EQ(string("::"), wildcard_address.ToString()); +// OLA_ASSERT_EQ(static_cast(0), wildcard_address.AsInt()); + OLA_ASSERT_TRUE(wildcard_address.IsWildcard()); + + IPV6Address address1 = IPV6Address::FromStringOrDie("::ffff:c0a8:101"); +// int ip_as_int = address1.AsInt(); + OLA_ASSERT_NE(wildcard_address, address1); +// OLA_ASSERT_NE(HostToNetwork(0xc0a811), ip_as_int); +// OLA_ASSERT_EQ(HostToNetwork(0xc0a80101), static_cast(ip_as_int)); + OLA_ASSERT_EQ(string("::ffff:192.168.1.1"), address1.ToString()); + + IPV6Address address2 = IPV6Address::FromStringOrDie( + "2001:db8:1234:5678:90ab:cdef:feed:face"); +// int ip_as_int = address2.AsInt(); + OLA_ASSERT_NE(wildcard_address, address2); +// OLA_ASSERT_NE(HostToNetwork(0xc0a811), ip_as_int); +// OLA_ASSERT_EQ(HostToNetwork(0xc0a80101), static_cast(ip_as_int)); + + const uint8_t big_endian_address_data[] = + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 10, 0, 0, 1}; + IPV6Address binary_address(big_endian_address_data); + OLA_ASSERT_EQ(string("::ffff:10.0.0.1"), binary_address.ToString()); + + // Test Get() + uint8_t address_data[] = {32, 1, 13, 184, 18, 52, 86, 120, + 144, 171, 205, 239, 254, 237, 250, 206}; + uint8_t addr[IPV6Address::LENGTH]; + address2.Get(addr); + OLA_ASSERT_DATA_EQUALS(addr, + sizeof(addr), + reinterpret_cast(&address_data), + sizeof(address_data)); + + // test copy and assignment + IPV6Address address3(address1); + OLA_ASSERT_EQ(address1, address3); + IPV6Address address4 = address1; + OLA_ASSERT_EQ(address1, address4); + + // test stringification + OLA_ASSERT_EQ(string("::ffff:192.168.1.1"), address1.ToString()); + std::ostringstream str; + str << address1; + OLA_ASSERT_EQ(string("::ffff:192.168.1.1"), str.str()); + + // test from string + auto_ptr string_address( + IPV6Address::FromString("::ffff:10.0.0.1")); + OLA_ASSERT_NOT_NULL(string_address.get()); + OLA_ASSERT_EQ(string("::ffff:10.0.0.1"), string_address->ToString()); + + auto_ptr string_address2(IPV6Address::FromString("foo")); + OLA_ASSERT_NULL(string_address2.get()); + + // and the second form + IPV6Address string_address3; + OLA_ASSERT_TRUE(IPV6Address::FromString( + "::ffff:172.16.4.1", &string_address3)); + OLA_ASSERT_EQ(string("::ffff:172.16.4.1"), string_address3.ToString()); + + IPV6Address string_address4; + // Add the leading zero to the second group + OLA_ASSERT_TRUE(IPV6Address::FromString( + "2001:0db8:1234:5678:90ab:cdef:feed:face", &string_address4)); + // Confirm it's not rendered when we convert to a string + OLA_ASSERT_EQ(string("2001:db8:1234:5678:90ab:cdef:feed:face"), + string_address4.ToString()); + + IPV6Address string_address5; + OLA_ASSERT_TRUE(IPV6Address::FromString( + "2001:db8:dead:beef:dead:beef:dead:beef", &string_address5)); + OLA_ASSERT_EQ(string("2001:db8:dead:beef:dead:beef:dead:beef"), + string_address5.ToString()); + + IPV6Address string_address6; + OLA_ASSERT_FALSE(IPV6Address::FromString("", &string_address6)); + + // make sure sorting works + vector addresses; + addresses.push_back(address1); + addresses.push_back(*string_address); + addresses.push_back(string_address3); + addresses.push_back(string_address4); + addresses.push_back(string_address5); + std::sort(addresses.begin(), addresses.end()); + + // The comparisons take into account network byte order automagically. + OLA_ASSERT_EQ(string("::ffff:10.0.0.1"), addresses[0].ToString()); + OLA_ASSERT_EQ(string("::ffff:172.16.4.1"), addresses[1].ToString()); + OLA_ASSERT_EQ(string("::ffff:192.168.1.1"), addresses[2].ToString()); + OLA_ASSERT_EQ(string("2001:db8:1234:5678:90ab:cdef:feed:face"), + addresses[3].ToString()); + OLA_ASSERT_EQ(string("2001:db8:dead:beef:dead:beef:dead:beef"), + addresses[4].ToString()); + +/* uint8_t mask = 255; // UINT8_MAX; + OLA_ASSERT_TRUE( + IPV6Address::ToCIDRMask(IPV6Address::FromStringOrDie("0.0.0.0"), &mask)); + OLA_ASSERT_EQ(0, static_cast(mask)); + + OLA_ASSERT_TRUE( + IPV6Address::ToCIDRMask(IPV6Address::FromStringOrDie("255.0.0.0"), + &mask)); + OLA_ASSERT_EQ(8, static_cast(mask)); + + OLA_ASSERT_TRUE( + IPV6Address::ToCIDRMask(IPV6Address::FromStringOrDie("255.255.255.0"), + &mask)); + OLA_ASSERT_EQ(24, static_cast(mask)); + + OLA_ASSERT_TRUE( + IPV6Address::ToCIDRMask(IPV6Address::FromStringOrDie("255.255.255.252"), + &mask)); + OLA_ASSERT_EQ(30, static_cast(mask)); + + OLA_ASSERT_TRUE( + IPV6Address::ToCIDRMask(IPV6Address::FromStringOrDie("255.255.255.255"), + &mask)); + OLA_ASSERT_EQ(32, static_cast(mask)); + + OLA_ASSERT_FALSE( + IPV6Address::ToCIDRMask(IPV6Address::FromStringOrDie("255.0.0.255"), + &mask));*/ +} + + +/* + * Test the wildcard address works. + */ +void IPV6AddressTest::testWildcard() { + IPV6Address wildcard_address; + OLA_ASSERT_EQ(string("::"), wildcard_address.ToString()); +// OLA_ASSERT_EQ(static_cast(0), wildcard_address.AsInt()); + OLA_ASSERT_TRUE(wildcard_address.IsWildcard()); + + IPV6Address wildcard_address2 = IPV6Address::WildCard(); + OLA_ASSERT_EQ(wildcard_address, wildcard_address2); +} + + +/* + * Test the loopback address works. + */ +void IPV6AddressTest::testLoopback() { + IPV6Address loopback_address = IPV6Address::Loopback(); + OLA_ASSERT_EQ(string("::1"), loopback_address.ToString()); +} diff --git a/common/network/Makefile.mk b/common/network/Makefile.mk index 44ef81f57d..fc9327e677 100644 --- a/common/network/Makefile.mk +++ b/common/network/Makefile.mk @@ -5,6 +5,7 @@ common_libolacommon_la_SOURCES += \ common/network/FakeInterfacePicker.h \ common/network/HealthCheckedConnection.cpp \ common/network/IPV4Address.cpp \ + common/network/IPV6Address.cpp \ common/network/Interface.cpp \ common/network/InterfacePicker.cpp \ common/network/MACAddress.cpp \ @@ -44,6 +45,7 @@ common_network_HealthCheckedConnectionTester_LDADD = $(COMMON_TESTING_LIBS) common_network_NetworkTester_SOURCES = \ common/network/IPV4AddressTest.cpp \ + common/network/IPV6AddressTest.cpp \ common/network/InterfacePickerTest.cpp \ common/network/InterfaceTest.cpp \ common/network/MACAddressTest.cpp \ diff --git a/common/rpc/RpcChannel.cpp b/common/rpc/RpcChannel.cpp index 7d1c81d6fd..10b5211e93 100644 --- a/common/rpc/RpcChannel.cpp +++ b/common/rpc/RpcChannel.cpp @@ -166,7 +166,7 @@ void RpcChannel::DescriptorReady() { m_buffer_size = AllocateMsgBuffer(m_expected_size); if (m_buffer_size < m_expected_size) { - OLA_WARN << "buffer size to small " << m_buffer_size << " < " << + OLA_WARN << "buffer size too small: " << m_buffer_size << " < " << m_expected_size; return; } diff --git a/common/web/SchemaParser.h b/common/web/SchemaParser.h index dfbd34f44a..ba49dba29e 100644 --- a/common/web/SchemaParser.h +++ b/common/web/SchemaParser.h @@ -91,7 +91,7 @@ class SchemaParser : public JsonParserInterface { /** * @brief Claim the RootValidator that was created by parsing the schema. * @returns A new Validator, or NULL if the schema wasn't valid. Ownership of - * the validtor is transferred to the caller. + * the validator is transferred to the caller. */ ValidatorInterface* ClaimRootValidator(); diff --git a/include/ola/network/HealthCheckedConnection.h b/include/ola/network/HealthCheckedConnection.h index a4311f51eb..cbbe33e351 100644 --- a/include/ola/network/HealthCheckedConnection.h +++ b/include/ola/network/HealthCheckedConnection.h @@ -75,7 +75,7 @@ class HealthCheckedConnection { /** * Call this when a heartbeat is piggybacked on another message. This - * prevents sending heatbeats unless necessary. + * prevents sending heartbeats unless necessary. */ void HeartbeatSent(); diff --git a/include/ola/network/IPV4Address.h b/include/ola/network/IPV4Address.h index 06857af6f8..06139393d5 100644 --- a/include/ola/network/IPV4Address.h +++ b/include/ola/network/IPV4Address.h @@ -14,7 +14,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * * IPV4Address.h - * Represents a IPv4 Address + * Represents an IPv4 Address * Copyright (C) 2011 Simon Newton */ diff --git a/include/ola/network/IPV6Address.h b/include/ola/network/IPV6Address.h new file mode 100644 index 0000000000..23f6000c25 --- /dev/null +++ b/include/ola/network/IPV6Address.h @@ -0,0 +1,240 @@ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * IPV6Address.h + * Represents an IPv6 Address + * Copyright (C) 2023 Peter Newman + */ + +/** + * @addtogroup network + * @{ + * @file IPV6Address.h + * @brief Represents an IPv6 Address. + * @} + */ + +#ifndef INCLUDE_OLA_NETWORK_IPV6ADDRESS_H_ +#define INCLUDE_OLA_NETWORK_IPV6ADDRESS_H_ + +#include // Required by FreeBSD +#include // Required by FreeBSD +#include +#include +#include +#include + +namespace ola { +namespace network { + +/** + * @addtogroup network + * @{ + */ + +/** + * @brief Represents an IPv6 Address. + * + * All methods use network byte order unless otherwise mentioned. + */ +class IPV6Address { + public: + /** + * @brief The length in bytes of an IPv6 address. + */ + enum { LENGTH = 16 }; + + /** + * @brief Create a new IPv6 Address set to IN6ADDR_ANY_INIT (::). + */ + IPV6Address() { + m_address = IN6ADDR_ANY_INIT; + } + +// TODO(Peter): From uint128_t? + + /** + * @brief Construct a new IPv6 address from binary data. + * @param address a pointer to the memory containing the IPv6 address data. The + * data should be most significant byte first. + */ + explicit IPV6Address(const uint8_t *address); + + /** + * @brief Create a new IPv6 Address from a in6_addr struct. + * @param address the ip address, in network byte order. + */ + explicit IPV6Address(in6_addr address) { + m_address = address; + } + + /** + * @brief Copy constructor. + * @param other the IPV6Address to copy. + */ + IPV6Address(const IPV6Address &other) + : m_address(other.m_address) { + } + + /** + * @brief Assignment operator. + * @param other the IPV6Address to assign to this object. + */ + IPV6Address& operator=(const IPV6Address &other) { + if (this != &other) { + m_address = other.m_address; + } + return *this; + } + + /** + * @brief Equals operator. + * @param other the IPV6Address to compare. + * @returns true if both IPV6Addresses are equal. + */ + bool operator==(const IPV6Address &other) const { + return IN6_ARE_ADDR_EQUAL(&m_address, &other.m_address); + } + + /** + * @brief Not equals operator. + * @param other the IPV6Address to compare. + * @returns false if both IPV6Addresses are equal. + */ + bool operator!=(const IPV6Address &other) const { + return !(*this == other); + } + + /** + * @brief Less than operator for partial ordering. + */ + bool operator<(const IPV6Address &other) const; + + /** + * @brief Greater than operator. + */ + bool operator>(const IPV6Address &other) const; + + /** +// * @brief Return the IPV6Address as an int in network-byte order. +// * @returns An uint32 representing the IP address. + */ +// uint32_t AsInt() const { return m_address; } + + /** + * @brief Checks if this address is the wildcard address (::). + * @returns true if this address is the wildcard address. + */ + bool IsWildcard() const; + + /** + * @brief Copy the IPV6Address to a memory location. + * @param ptr the memory location to copy the address to. The location + * should be at least LENGTH bytes. + * @note The address is copied in network byte order. + */ + void Get(uint8_t ptr[LENGTH]) const { + memcpy(ptr, + reinterpret_cast(&m_address.s6_addr[0]), + LENGTH); + } + + /** + * @brief Write the binary representation of the IPv6 address to memory. + * @param buffer a pointer to memory to write the IPv6 address to + * @param length the size of the memory block, should be at least LENGTH. + * @returns true if length was >= LENGTH, false otherwise. + */ + bool Pack(uint8_t *buffer, unsigned int length) const { + if (length < LENGTH) + return false; + Get(buffer); + return true; + } + + /** + * @brief Convert the IPV6Address to a string. + * @returns the string representation of this IPV6Address. + */ + std::string ToString() const; + + /** + * @brief Write the string representation of this IPV6Address to an + * ostream. + * @param out the ostream to write to. + * @param address the address to write. + */ + friend std::ostream& operator<<(std::ostream &out, + const IPV6Address &address) { + return out << address.ToString(); + } + + /** + * @brief Convert a string to an IPV6Address. + * @param address the IP address string to convert. + * @returns a new IPV6Address or NULL if the string was invalid. The caller + * is responsible for deleting the IPV6Address object. + */ + static IPV6Address* FromString(const std::string &address); + + /** + * @brief Convert a string to an IPV6Address. + * @param address the IP address string to convert. + * @param[out] target the converted IPV6Address. + * @returns true if the string was a valid IPv6 address, false otherwise. + */ + static bool FromString(const std::string &address, IPV6Address *target); + + /** + * @brief Convert a string to an IPV6Address or abort. + * @note This should only be used within tests. + * @param address the IP address to convert. + * @return an IPV6Address matching the string. + */ + static IPV6Address FromStringOrDie(const std::string &address); + + /** +// * @brief Convert a subnet mask to its CIDR format value +// * @param address the subnet mask as an IPV6Address object + * @param mask the mask variable to populate + * @return true if we managed to convert the address to a CIDR value, false + otherwise + */ +// static bool ToCIDRMask(IPV6Address address, uint8_t *mask); + + /** + * @brief Returns the wildcard address IN6ADDR_ANY_INIT (::). + * @return an IPV6Address representing the wildcard address. + */ + static IPV6Address WildCard(); + + // TODO(Peter): Add support for the all-nodes link-local multicast group + + /** + * @brief Returns the loopback address (::1/128). + * @return an IPV6Address representing the loopback address. + */ + static IPV6Address Loopback(); + + private: + // TODO(Peter): Decide how to store the address internally... + in6_addr m_address; +}; +/** + * @} + */ +} // namespace network +} // namespace ola +#endif // INCLUDE_OLA_NETWORK_IPV6ADDRESS_H_ diff --git a/include/ola/network/Makefile.mk b/include/ola/network/Makefile.mk index fce6c1435d..f21c3533b7 100644 --- a/include/ola/network/Makefile.mk +++ b/include/ola/network/Makefile.mk @@ -3,6 +3,7 @@ olanetworkinclude_HEADERS = \ include/ola/network/AdvancedTCPConnector.h\ include/ola/network/HealthCheckedConnection.h \ include/ola/network/IPV4Address.h \ + include/ola/network/IPV6Address.h \ include/ola/network/Interface.h \ include/ola/network/InterfacePicker.h \ include/ola/network/MACAddress.h \ diff --git a/include/olad/Preferences.h b/include/olad/Preferences.h index 9c7ff4fa6b..65c921dca2 100644 --- a/include/olad/Preferences.h +++ b/include/olad/Preferences.h @@ -141,6 +141,22 @@ class IPv4Validator: public Validator { }; +/* + * Check an IPv6 address is valid + */ +class IPv6Validator: public Validator { + public: + explicit IPv6Validator(bool empty_ok = true): + m_empty_ok(empty_ok) {} + + bool IsValid(const std::string &value) const; + private: + bool m_empty_ok; + + DISALLOW_COPY_AND_ASSIGN(IPv6Validator); +}; + + /* * The abstract Preferences class */ diff --git a/olad/plugin_api/Preferences.cpp b/olad/plugin_api/Preferences.cpp index a2378f0a3d..32441b40d4 100644 --- a/olad/plugin_api/Preferences.cpp +++ b/olad/plugin_api/Preferences.cpp @@ -53,6 +53,7 @@ #include "ola/StringUtils.h" #include "ola/file/Util.h" #include "ola/network/IPV4Address.h" +#include "ola/network/IPV6Address.h" #include "ola/stl/STLUtils.h" #include "ola/thread/Thread.h" @@ -186,6 +187,34 @@ bool IPv4Validator::IsValid(const string &value) const { } +bool IPv6Validator::IsValid(const string &value) const { + if (value.empty()) { + return m_empty_ok; + } + + /*vector tokens; + // TODO(Peter): Deal with stuff like ::ffff:1.2.3.4 + // Maybe just fall back to the IPv6 string parsing for now...? + StringSplit(value, &tokens, ":"); + if (tokens.size() != ola::network::IPV6Address::LENGTH) { + return false; + } + + for (unsigned int i = 0 ; i < 4; i++) { + unsigned int octet; + if (!StringToInt(tokens[i], &octet)) { + return false; + } + if (octet > UINT8_MAX) { + return false; + } + } + return true;*/ + ola::network::IPV6Address validator_address; + return ola::network::IPV6Address::FromString(value, &validator_address); +} + + // Prefs Factory //----------------------------------------------------------------------------- diff --git a/olad/plugin_api/PreferencesTest.cpp b/olad/plugin_api/PreferencesTest.cpp index 9064444e19..812863c57e 100644 --- a/olad/plugin_api/PreferencesTest.cpp +++ b/olad/plugin_api/PreferencesTest.cpp @@ -40,6 +40,7 @@ using ola::Preferences; using ola::SetValidator; using ola::StringValidator; using ola::IPv4Validator; +using ola::IPv6Validator; using std::string; using std::vector; @@ -131,6 +132,41 @@ void PreferencesTest::testValidators() { IPv4Validator ipv4_validator2(false); // empty not ok OLA_ASSERT_FALSE(ipv4_validator2.IsValid("")); + OLA_ASSERT(ipv4_validator2.IsValid("1.2.3.4")); + OLA_ASSERT(ipv4_validator2.IsValid("10.0.255.1")); + OLA_ASSERT_FALSE(ipv4_validator2.IsValid("foo")); + OLA_ASSERT_FALSE(ipv4_validator2.IsValid("1.2.3")); + OLA_ASSERT_FALSE(ipv4_validator2.IsValid("1.2.3.4.5")); + OLA_ASSERT_FALSE(ipv4_validator2.IsValid("1.f00.3.4")); + + IPv6Validator ipv6_validator; // empty ok + OLA_ASSERT(ipv6_validator.IsValid("")); + OLA_ASSERT(ipv6_validator.IsValid("2001:db8:1234:5678:90ab:cdef:feed:face")); + OLA_ASSERT(ipv6_validator.IsValid("::ffff:1.2.3.4")); + OLA_ASSERT(ipv6_validator.IsValid("::ffff:10.0.255.1")); + OLA_ASSERT_FALSE(ipv6_validator.IsValid("foo")); + OLA_ASSERT_FALSE(ipv6_validator.IsValid( + "2001:db8:1234:5678:90ab:cdef:feed:face:0000")); + OLA_ASSERT_FALSE(ipv6_validator.IsValid( + "2001:db8:1234:5678:90ab:cdef:feed:gggg")); + OLA_ASSERT_FALSE(ipv6_validator.IsValid("::ffff:1.2.3")); + OLA_ASSERT_FALSE(ipv6_validator.IsValid("::ffff:1.2.3.4.5")); + OLA_ASSERT_FALSE(ipv6_validator.IsValid("::ffff:1.f00.3.4")); + + IPv6Validator ipv6_validator2(false); // empty not ok + OLA_ASSERT_FALSE(ipv6_validator2.IsValid("")); + OLA_ASSERT(ipv6_validator2.IsValid( + "2001:db8:1234:5678:90ab:cdef:feed:face")); + OLA_ASSERT(ipv6_validator2.IsValid("::ffff:1.2.3.4")); + OLA_ASSERT(ipv6_validator2.IsValid("::ffff:10.0.255.1")); + OLA_ASSERT_FALSE(ipv6_validator2.IsValid("foo")); + OLA_ASSERT_FALSE(ipv6_validator2.IsValid( + "2001:db8:1234:5678:90ab:cdef:feed:face:0000")); + OLA_ASSERT_FALSE(ipv6_validator2.IsValid( + "2001:db8:1234:5678:90ab:cdef:feed:gggg")); + OLA_ASSERT_FALSE(ipv6_validator2.IsValid("::ffff:1.2.3")); + OLA_ASSERT_FALSE(ipv6_validator2.IsValid("::ffff:1.2.3.4.5")); + OLA_ASSERT_FALSE(ipv6_validator2.IsValid("::ffff:1.f00.3.4")); } diff --git a/python/ola/StringUtils.py b/python/ola/StringUtils.py index a0f51cfafd..4a4d7dd7de 100644 --- a/python/ola/StringUtils.py +++ b/python/ola/StringUtils.py @@ -32,11 +32,11 @@ def StringEscape(s): """Escape unprintable characters in a string.""" # TODO(Peter): How does this interact with the E1.20 Unicode flag? # We don't use sys.version_info.major to support Python 2.6. - if sys.version_info[0] == 2 and type(s) == str: + if sys.version_info[0] == 2 and isinstance(s, str): return s.encode('string-escape') - elif sys.version_info[0] == 2 and type(s) == unicode: + elif sys.version_info[0] == 2 and isinstance(s, unicode): return s.encode('unicode-escape') - elif type(s) == str: + elif isinstance(s, str): # All strings in Python 3 are unicode # This encode/decode pair gets us an escaped string return s.encode('unicode-escape').decode(encoding="ascii", diff --git a/tools/rdm/ResponderTest.py b/tools/rdm/ResponderTest.py index c83388f19a..2ef001d352 100644 --- a/tools/rdm/ResponderTest.py +++ b/tools/rdm/ResponderTest.py @@ -694,15 +694,15 @@ def _CheckForAckOrNack(self, response, unpacked_data, unpack_exception): @staticmethod def _EscapeData(data): - if type(data) == list: + if isinstance(data, list): return [ResponderTestFixture._EscapeData(i) for i in data] - elif type(data) == dict: + elif isinstance(data, dict): d = {} for k, v in data.items(): # We can't escape the key as then it may become a new key d[k] = ResponderTestFixture._EscapeData(v) return d - elif type(data) == str or type(data) == unicode: + elif isinstance(data, str) or isinstance(data, unicode): return StringEscape(data) else: return data diff --git a/tools/rdm/TestHelpers.py b/tools/rdm/TestHelpers.py index 51eb606de7..4a157bcf63 100644 --- a/tools/rdm/TestHelpers.py +++ b/tools/rdm/TestHelpers.py @@ -31,7 +31,7 @@ def ContainsUnprintable(s): """Check if a string s contain unprintable characters.""" # TODO(Peter): How does this interact with the E1.20 Unicode flag? - if type(s) == str or type(s) == unicode: + if isinstance(s, str) or isinstance(s, unicode): # All strings in Python 3 are unicode, Python 2 ones might not be return s != StringEscape(s) else: