diff --git a/openvpn/addr/ip.hpp b/openvpn/addr/ip.hpp index 61f4eeca..07c91f92 100644 --- a/openvpn/addr/ip.hpp +++ b/openvpn/addr/ip.hpp @@ -22,12 +22,13 @@ #ifndef OPENVPN_ADDR_IP_H #define OPENVPN_ADDR_IP_H +#include +#include #include #include // for std::memset #include -#include #include #include #include @@ -141,7 +142,7 @@ class Addr const TITLE &title, const Version required_version) { - Addr a = from_string(ipstr, title, required_version); + const Addr a = from_string(ipstr, title, required_version); return a.to_string(); } @@ -241,15 +242,9 @@ class Addr static bool is_valid(const std::string &ipstr) { // fast path -- rule out validity if invalid chars - for (size_t i = 0; i < ipstr.length(); ++i) - { - const char c = ipstr[i]; - if (!((c >= '0' && c <= '9') - || (c >= 'a' && c <= 'f') - || (c >= 'A' && c <= 'F') - || (c == '.' || c == ':' || c == '%'))) - return false; - } + if (std::any_of(ipstr.begin(), ipstr.end(), [](auto c) + { return !(std::isxdigit(c) || c == '.' || c == ':' || c == '%'); })) + return false; // slow path { @@ -261,12 +256,15 @@ class Addr static Addr from_hex(Version v, const std::string &s) { - if (v == V4) + switch (v) + { + case V4: return from_ipv4(IPv4::Addr::from_hex(s)); - else if (v == V6) + case V6: return from_ipv6(IPv6::Addr::from_hex(s)); - else + default: OPENVPN_IP_THROW("from_hex: address unspecified"); + } } static Addr from_ipv4(IPv4::Addr addr) @@ -289,36 +287,40 @@ class Addr { if (ver == V4) return u.v4; - else - OPENVPN_IP_THROW("to_ipv4: address is not IPv4"); + OPENVPN_IP_THROW("to_ipv4: address is not IPv4"); } IPv4::Addr to_ipv4_zero() const { - if (ver == V4) + switch (ver) + { + case V4: return u.v4; - else if (ver == UNSPEC) + case UNSPEC: return IPv4::Addr::from_zero(); - else + default: OPENVPN_IP_THROW("to_ipv4_zero: address is not IPv4"); + } } const IPv6::Addr &to_ipv6() const { if (ver == V6) return u.v6; - else - OPENVPN_IP_THROW("to_ipv6: address is not IPv6"); + OPENVPN_IP_THROW("to_ipv6: address is not IPv6"); } IPv6::Addr to_ipv6_zero() const { - if (ver == V6) + switch (ver) + { + case V6: return u.v6; - else if (ver == UNSPEC) + case UNSPEC: return IPv6::Addr::from_zero(); - else + default: OPENVPN_IP_THROW("to_ipv6_zero: address is not IPv6"); + } } const IPv4::Addr &to_ipv4_nocheck() const @@ -335,10 +337,9 @@ class Addr { if (sa->sa_family == AF_INET) return from_ipv4(IPv4::Addr::from_sockaddr(reinterpret_cast(sa))); - else if (sa->sa_family == AF_INET6) + if (sa->sa_family == AF_INET6) return from_ipv6(IPv6::Addr::from_sockaddr(reinterpret_cast(sa))); - else - return Addr(); + return Addr(); } static bool sockaddr_defined(const struct sockaddr *sa) @@ -348,44 +349,56 @@ class Addr static Addr from_ulong(Version v, unsigned long ul) { - if (v == V4) + switch (v) + { + case V4: return from_ipv4(IPv4::Addr::from_ulong(ul)); - else if (v == V6) + case V6: return from_ipv6(IPv6::Addr::from_ulong(ul)); - else + default: OPENVPN_IP_THROW("from_ulong: address unspecified"); + } } // return *this as a ulong, will raise exception on overflow unsigned long to_ulong() const { - if (ver == V4) + switch (ver) + { + case V4: return u.v4.to_ulong(); - else if (ver == V6) + case V6: return u.v6.to_ulong(); - else + default: OPENVPN_IP_THROW("to_ulong: address unspecified"); + } } - static Addr from_long(Version v, long ul) + static Addr from_long(Version v, const long ul) { - if (v == V4) + switch (v) + { + case V4: return from_ipv4(IPv4::Addr::from_long(ul)); - else if (v == V6) + case V6: return from_ipv6(IPv6::Addr::from_long(ul)); - else + default: OPENVPN_IP_THROW("from_long: address unspecified"); + } } // return *this as a long, will raise exception on overflow long to_long() const { - if (ver == V4) + switch (ver) + { + case V4: return u.v4.to_long(); - else if (ver == V6) + case V6: return u.v6.to_long(); - else + default: OPENVPN_IP_THROW("to_long: address unspecified"); + } } // return Addr from 16 byte binary string @@ -408,102 +421,119 @@ class Addr // convert Addr to 16 byte binary string void to_byte_string(unsigned char *bytestr) const { - if (ver == V4) + switch (ver) + { + case V4: IPv6::Addr::v4_to_byte_string(bytestr, u.v4.to_uint32_net()); - else if (ver == V6) + break; + case V6: u.v6.to_byte_string(bytestr); - else + break; + default: std::memset(bytestr, 0, 16); + break; + } } // convert Addr to variable length byte string void to_byte_string_variable(unsigned char *bytestr) const { - if (ver == V4) + switch (ver) + { + case V4: u.v4.to_byte_string(bytestr); - else if (ver == V6) + break; + case V6: u.v6.to_byte_string(bytestr); - else + break; + default: OPENVPN_IP_THROW("to_byte_string_variable: address unspecified"); + } } std::uint32_t to_uint32_net() const // return value in net byte order { - if (ver == V4) - return u.v4.to_uint32_net(); - else - return 0; + return (ver == V4) ? u.v4.to_uint32_net() : 0; } // construct an address where all bits are zero static Addr from_zero(const Version v) { - if (v == V4) + switch (v) + { + case V4: return from_ipv4(IPv4::Addr::from_zero()); - else if (v == V6) + case V6: return from_ipv6(IPv6::Addr::from_zero()); - else + default: OPENVPN_IP_THROW("from_zero: IP version unspecified"); + } } // construct the "one" address static Addr from_one(const Version v) { - if (v == V4) + switch (v) + { + case V4: return from_ipv4(IPv4::Addr::from_one()); - else if (v == V6) + case V6: return from_ipv6(IPv6::Addr::from_one()); - else + default: OPENVPN_IP_THROW("from_one: IP version unspecified"); + } } // construct an address where all bits are one static Addr from_zero_complement(const Version v) { - if (v == V4) + switch (v) + { + case V4: return from_ipv4(IPv4::Addr::from_zero_complement()); - else if (v == V6) + case V6: return from_ipv6(IPv6::Addr::from_zero_complement()); - else + default: OPENVPN_IP_THROW("from_zero_complement: IP version unspecified"); + } } // validate the prefix length for the IP version static bool validate_prefix_len(Version v, const unsigned int prefix_len) { - if (v == V4) - { - if (prefix_len <= V4_SIZE) - return true; - } - else if (v == V6) - { - if (prefix_len <= V6_SIZE) - return true; - } + if (v == V4 && prefix_len <= V4_SIZE) + return true; + if (v == V6 && prefix_len <= V6_SIZE) + return true; return false; } // build a netmask using given prefix_len static Addr netmask_from_prefix_len(Version v, const unsigned int prefix_len) { - if (v == V4) + switch (v) + { + case V4: return from_ipv4(IPv4::Addr::netmask_from_prefix_len(prefix_len)); - else if (v == V6) + case V6: return from_ipv6(IPv6::Addr::netmask_from_prefix_len(prefix_len)); - else + default: OPENVPN_IP_THROW("netmask_from_prefix_len: address unspecified"); + } } // build a netmask using *this as extent Addr netmask_from_extent() const { - if (ver == V4) + switch (ver) + { + case V4: return from_ipv4(u.v4.netmask_from_extent()); - else if (ver == V6) + case V6: return from_ipv6(u.v6.netmask_from_extent()); - else + default: OPENVPN_IP_THROW("netmask_from_extent: address unspecified"); + } } std::string to_string() const @@ -514,8 +544,7 @@ class Addr std::string ret = a.to_string(); return ret; } - else - return "UNSPEC"; + return "UNSPEC"; } std::string to_string_bracket_ipv6() const @@ -531,42 +560,46 @@ class Addr std::string to_hex() const { - if (ver == V4) + switch (ver) + { + case V4: return u.v4.to_hex(); - else if (ver == V6) + case V6: return u.v6.to_hex(); - else + default: OPENVPN_IP_THROW("to_hex: address unspecified"); + } } std::string arpa() const { - if (ver == V4) + switch (ver) + { + case V4: return u.v4.arpa(); - else if (ver == V6) + case V6: return u.v6.arpa(); - else + default: OPENVPN_IP_THROW("arpa: address unspecified"); + } } static Addr from_asio(const openvpn_io::ip::address &addr) { + Addr ret; if (addr.is_v4()) { - Addr a; - a.ver = V4; - a.u.v4 = IPv4::Addr::from_asio(addr.to_v4()); - return a; + ret.ver = V4; + ret.u.v4 = IPv4::Addr::from_asio(addr.to_v4()); } else if (addr.is_v6()) { - Addr a; - a.ver = V6; - a.u.v6 = IPv6::Addr::from_asio(addr.to_v6()); - return a; + ret.ver = V6; + ret.u.v6 = IPv6::Addr::from_asio(addr.to_v6()); } - else + else if (ret.ver == UNSPEC) OPENVPN_IP_THROW("from_asio: address unspecified"); + return ret; } openvpn_io::ip::address to_asio() const @@ -610,42 +643,6 @@ class Addr return operator+(-delta); } -#define OPENVPN_IP_OPERATOR_BINOP(OP) \ - Addr operator OP(const Addr &other) const \ - { \ - if (ver != other.ver) \ - OPENVPN_IP_THROW("binop: version inconsistency"); \ - switch (ver) \ - { \ - case V4: \ - { \ - Addr ret; \ - ret.ver = V4; \ - ret.u.v4 = u.v4 OP other.u.v4; \ - return ret; \ - } \ - case V6: \ - { \ - Addr ret; \ - ret.ver = V6; \ - ret.u.v6 = u.v6 OP other.u.v6; \ - return ret; \ - } \ - default: \ - OPENVPN_IP_THROW("binop: address unspecified"); \ - } \ - } - - OPENVPN_IP_OPERATOR_BINOP(+) - OPENVPN_IP_OPERATOR_BINOP(-) - OPENVPN_IP_OPERATOR_BINOP(*) - OPENVPN_IP_OPERATOR_BINOP(/) - OPENVPN_IP_OPERATOR_BINOP(%) - OPENVPN_IP_OPERATOR_BINOP(&) - OPENVPN_IP_OPERATOR_BINOP(|) - -#undef OPENVPN_IP_OPERATOR_BINOP - Addr operator<<(const unsigned int shift) const { switch (ver) @@ -761,33 +758,61 @@ class Addr return !operator==(other); } -#define OPENVPN_IP_OPERATOR_REL(OP) \ - bool operator OP(const Addr &other) const \ - { \ - if (ver == other.ver) \ - { \ - switch (ver) \ - { \ - case V4: \ - return u.v4 OP other.u.v4; \ - case V6: \ - return u.v6 OP other.u.v6; \ - default: \ - return false; \ - } \ - } \ - else if (ver OP other.ver) \ - return true; \ - else \ - return false; \ + bool operator<(const Addr &other) const + { + return compare(other, std::less<>{}); } - OPENVPN_IP_OPERATOR_REL(<) - OPENVPN_IP_OPERATOR_REL(>) - OPENVPN_IP_OPERATOR_REL(<=) - OPENVPN_IP_OPERATOR_REL(>=) + bool operator>(const Addr &other) const + { + return compare(other, std::greater<>{}); + } -#undef OPENVPN_IP_OPERATOR_REL + bool operator<=(const Addr &other) const + { + return compare(other, std::less_equal<>{}); + } + + bool operator>=(const Addr &other) const + { + return compare(other, std::greater_equal<>{}); + } + + // Operator overloads for binary operations + Addr operator+(const Addr &other) const + { + return binary_op(other, std::plus<>{}); + } + + Addr operator-(const Addr &other) const + { + return binary_op(other, std::minus<>{}); + } + + Addr operator*(const Addr &other) const + { + return binary_op(other, std::multiplies<>{}); + } + + Addr operator/(const Addr &other) const + { + return binary_op(other, std::divides<>{}); + } + + Addr operator%(const Addr &other) const + { + return binary_op(other, std::modulus<>{}); + } + + Addr operator&(const Addr &other) const + { + return binary_op(other, std::bit_and<>{}); + } + + Addr operator|(const Addr &other) const + { + return binary_op(other, std::bit_or<>{}); + } bool unspecified() const { @@ -944,7 +969,7 @@ class Addr } // throw exception if address is not a valid netmask - void validate_netmask() + void validate_netmask() const { prefix_len(); } @@ -1013,12 +1038,15 @@ class Addr // address size in bits of particular IP version static unsigned int version_size(Version v) { - if (v == V4) + switch (v) + { + case V4: return IPv4::Addr::SIZE; - else if (v == V6) + case V6: return IPv6::Addr::SIZE; - else + default: return 0; + } } template @@ -1128,6 +1156,50 @@ class Addr } u{}; Version ver; + + template + bool compare(const Addr &other, Comparator comp) const + { + if (ver == other.ver) + { + switch (ver) + { + case V4: + return comp(u.v4, other.u.v4); + case V6: + return comp(u.v6, other.u.v6); + default: + return false; + } + } + return comp(ver, other.ver); + } + + template + Addr binary_op(const Addr &other, BinaryOp op) const + { + if (ver != other.ver) + { + OPENVPN_IP_THROW("binop: version inconsistency"); + } + + Addr ret; + ret.ver = ver; + + switch (ver) + { + case V4: + ret.u.v4 = op(u.v4, other.u.v4); + break; + case V6: + ret.u.v6 = op(u.v6, other.u.v6); + break; + default: + OPENVPN_IP_THROW("binop: address unspecified"); + } + + return ret; + } }; OPENVPN_OSTREAM(Addr, to_string)