userver: userver/utils/ip.hpp Source File
Loading...
Searching...
No Matches
ip.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/utils/ip.hpp
4/// @brief IPv4 and IPv6 addresses and networks
5
6#include <array>
7#include <cstddef>
8#include <exception>
9#include <string>
10#include <system_error>
11#include <vector>
12
13#include <sys/socket.h>
14
15#include <fmt/format.h>
16
17#include <userver/utils/zstring_view.hpp>
18
19USERVER_NAMESPACE_BEGIN
20
21/// IP address and utilities
22namespace utils::ip {
23
24/// @ingroup userver_containers
25///
26/// @brief Base class for IPv4/IPv6 addresses
27template <std::size_t N>
28class AddressBase final {
29 static_assert(N == 4 || N == 16, "Address can only be 4 or 16 bytes size");
30
31public:
32 static constexpr std::size_t kAddressSize = N;
33 using BytesType = std::array<unsigned char, N>;
34
35 AddressBase() noexcept : address_({0}) {}
36 explicit AddressBase(const BytesType& address)
37 : address_(address)
38 {}
39
40 /// @brief Get the address in bytes, in network byte order.
41 const BytesType& GetBytes() const noexcept { return address_; }
42
43 friend bool operator==(const AddressBase<N>& a1, const AddressBase<N>& a2) noexcept {
44 return a1.address_ == a2.address_;
45 }
46
47 friend bool operator!=(const AddressBase<N>& a1, const AddressBase<N>& a2) noexcept {
48 return a1.address_ != a2.address_;
49 }
50
51private:
52 BytesType address_;
53};
54
55/// @ingroup userver_containers
56///
57/// @brief IPv4 address in network bytes order
58using AddressV4 = AddressBase<4>;
59
60/// @ingroup userver_containers
61///
62/// @brief IPv6 address in network bytes order
63using AddressV6 = AddressBase<16>;
64
65template <typename T>
66inline constexpr bool kIsAddressType = std::is_same_v<T, AddressV4> || std::is_same_v<T, AddressV6>;
67
68/// @brief Create an IPv4 address from an IP address string in dotted decimal form.
69/// @throw AddressSystemError
70AddressV4 AddressV4FromString(utils::zstring_view str);
71
72/// @brief Create an IPv6 address from an IP address string in dotted decimal form.
73AddressV6 AddressV6FromString(utils::zstring_view str);
74
75/// @brief Get the address as a string in dotted decimal format.
76std::string AddressV4ToString(const AddressV4& address);
77
78/// @brief Get the address as a string in dotted decimal format.
79std::string AddressV6ToString(const AddressV6& address);
80
81/// @ingroup userver_containers
82///
83/// @brief Base class for IPv4/IPv6 network
84template <typename Address, typename = std::enable_if_t<kIsAddressType<Address>>>
85class NetworkBase final {
86public:
87 using AddressType = Address;
88 static constexpr unsigned char kMaximumPrefixLength = std::is_same_v<Address, AddressV4> ? 32 : 128;
89
90 NetworkBase() noexcept = default;
91
92 NetworkBase(const AddressType& address, unsigned short prefix_length)
93 : address_(address),
94 prefix_length_(prefix_length)
95 {
96 if (prefix_length > kMaximumPrefixLength) {
97 throw std::out_of_range(fmt::format(
98 "{} prefix length is too large",
99 std::is_same_v<Address, AddressV4> ? "NetworkV4" : "NetworkV6"
100 ));
101 }
102 }
103
104 /// @brief Get the address address of network
105 AddressType GetAddress() const noexcept { return address_; }
106
107 /// @brief Get prefix length of address network
108 unsigned char GetPrefixLength() const noexcept { return prefix_length_; }
109
110 /// @brief Returns true if the address is in network
111 bool ContainsAddress(const AddressType& address) const {
112 const auto network_bytes = address_.GetBytes();
113 const auto address_bytes = address.GetBytes();
114
115 std::uint8_t diff = 0;
116 for (std::size_t byte_index = 0; byte_index < kMaximumPrefixLength / 8; ++byte_index) {
117 std::uint8_t mask_byte = 0;
118 if (byte_index == prefix_length_ / 8) {
119 mask_byte = ~((1 << (8 - prefix_length_ % 8)) - 1);
120 }
121 if (byte_index < prefix_length_ / 8) {
122 mask_byte = 255;
123 }
124
125 diff |= (network_bytes[byte_index] ^ address_bytes[byte_index]) & mask_byte;
126 }
127 return !diff;
128 }
129
130 friend bool operator==(const NetworkBase<Address>& a, const NetworkBase<Address>& b) noexcept {
131 return a.address_ == b.address_ && a.prefix_length_ == b.prefix_length_;
132 }
133
134 friend bool operator!=(const NetworkBase<Address>& a, const NetworkBase<Address>& b) noexcept { return !(a == b); }
135
136private:
137 AddressType address_;
138 unsigned char prefix_length_ = 0;
139};
140
141/// @ingroup userver_containers
142///
143/// @brief IPv4 network.
144using NetworkV4 = NetworkBase<AddressV4>;
145
146/// @ingroup userver_containers
147///
148/// @brief IPv6 network.
149using NetworkV6 = NetworkBase<AddressV6>;
150
151///@brief Create an IPv4 network from a string containing IP address and prefix
152/// length.
153/// @throw std::invalid_argument, AddressSystemError
154NetworkV4 NetworkV4FromString(const std::string& str);
155
156/// @brief Create an IPv6 network from a string containing IP address and prefix
157/// length.
158NetworkV6 NetworkV6FromString(const std::string& str);
159
160///@brief Get the network as an address in dotted decimal format.
161std::string NetworkV4ToString(const NetworkV4& network);
162
163/// @brief Get the network as an address in dotted decimal format.
164std::string NetworkV6ToString(const NetworkV6& network);
165
166/// @brief Convert NetworkV4 to CIDR format
167NetworkV4 TransformToCidrFormat(NetworkV4 network);
168
169/// @brief Convert NetworkV4 to CIDR format
170NetworkV6 TransformToCidrFormat(NetworkV6 network);
171
172/// @ingroup userver_containers
173///
174/// @brief INET IPv4/IPv4 network
175/// @warning InetNetwork class is deprecated. You should use InetNetwork class
176/// via transformation function to/from NetworkV4/NetworkV6.
177/// Use this class only if you need to work with INET PostgreSQL format.
178class InetNetwork final {
179public:
180 enum class AddressFamily : unsigned char { kIPv4 = AF_INET, kIPv6 = AF_INET6 };
181
182 // Default constructor: IPv4 address
183 InetNetwork();
184 InetNetwork(std::vector<unsigned char>&& bytes, unsigned char prefix_length, AddressFamily address_family);
185
186 /// @brief Get the address in bytes
187 const std::vector<unsigned char>& GetBytes() const noexcept { return bytes_; }
188
189 /// @brief Get the prefix length of network
190 unsigned char GetPrefixLength() const noexcept { return prefix_length_; }
191
192 /// @brief Get the address family
193 AddressFamily GetAddressFamily() const noexcept { return address_family_; }
194
195 friend bool operator==(const InetNetwork& lhs, const InetNetwork& rhs) {
196 return lhs.address_family_ == rhs.address_family_ && lhs.prefix_length_ == rhs.prefix_length_ &&
197 lhs.bytes_ == rhs.bytes_;
198 }
199
200 friend bool operator!=(const InetNetwork& lhs, const InetNetwork& rhs) { return !operator==(lhs, rhs); }
201
202private:
203 std::vector<unsigned char> bytes_;
204 unsigned char prefix_length_;
205 AddressFamily address_family_;
206};
207
208/// @brief Convert InetNetwork to NetworkV4
209NetworkV4 NetworkV4FromInetNetwork(const InetNetwork& inet_network);
210
211/// @brief Convert InetNetwork to NetworkV6
212NetworkV6 NetworkV6FromInetNetwork(const InetNetwork& inet_network);
213
214/// @brief Convert NetworkV4 to InetNetwork
215InetNetwork NetworkV4ToInetNetwork(const NetworkV4& network);
216
217/// @brief Convert NetworkV6 to InetNetwork
218InetNetwork NetworkV6ToInetNetwork(const NetworkV6& network);
219
220/// @brief Invalid network or address
221class AddressSystemError final : public std::exception {
222public:
223 AddressSystemError(std::error_code code, std::string_view msg)
224 : msg_(msg),
225 code_(code)
226 {}
227
228 /// Operating system error code.
229 const std::error_code& Code() const { return code_; }
230
231 const char* what() const noexcept final { return msg_.c_str(); }
232
233private:
234 std::string msg_;
235 std::error_code code_;
236};
237
238} // namespace utils::ip
239
240USERVER_NAMESPACE_END