userver: userver/utils/ip.hpp Source File
⚠️ This is the documentation for an old userver version. Click here to switch to the latest version.
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
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 <exception>
8#include <string>
9#include <system_error>
10#include <vector>
11
12#include <sys/socket.h>
13
14#include <fmt/format.h>
15
16USERVER_NAMESPACE_BEGIN
17
18// IP address and utilities
19namespace utils::ip {
20
21/// @ingroup userver_containers
22///
23/// @brief Base class for IPv4/IPv6 addresses
24template <std::size_t N>
25class AddressBase final {
26 static_assert(N == 4 || N == 16, "Address can only be 4 or 16 bytes size");
27
28 public:
29 static constexpr std::size_t AddressSize = N;
30 using BytesType = std::array<unsigned char, N>;
31
32 AddressBase() noexcept : address_({0}) {}
33 explicit AddressBase(const BytesType& address) : address_(address) {}
34
35 /// @brief Get the address in bytes, in network byte order.
36 const BytesType& GetBytes() const noexcept { return address_; }
37
38 friend bool operator==(const AddressBase<N>& a1,
39 const AddressBase<N>& a2) noexcept {
40 return a1.address_ == a2.address_;
41 }
42
43 friend bool operator!=(const AddressBase<N>& a1,
44 const AddressBase<N>& a2) noexcept {
45 return a1.address_ != a2.address_;
46 }
47
48 private:
49 BytesType address_;
50};
51
52/// @ingroup userver_containers
53///
54/// @brief IPv4 address in network bytes order
55using AddressV4 = AddressBase<4>;
56
57/// @ingroup userver_containers
58///
59/// @brief IPv6 address in network bytes order
60using AddressV6 = AddressBase<16>;
61
62template <typename T>
63inline constexpr bool kIsAddressType =
64 std::is_same_v<T, AddressV4> || std::is_same_v<T, AddressV6>;
65
66/// @brief Create an IPv4 address from an IP address string in dotted decimal
67/// form.
68/// @throw AddressSystemError
69AddressV4 AddressV4FromString(const std::string& str);
70
71/// @brief Create an IPv6 address from an IP address string in dotted decimal
72/// form.
73AddressV6 AddressV6FromString(const std::string& 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,
85 typename = std::enable_if_t<kIsAddressType<Address>>>
86class NetworkBase final {
87 public:
88 using AddressType = Address;
89 static constexpr unsigned char kMaximumPrefixLength =
90 std::is_same_v<Address, AddressV4> ? 32 : 128;
91
92 NetworkBase() noexcept = default;
93
94 NetworkBase(const AddressType& address, unsigned short prefix_length)
95 : address_(address), prefix_length_(prefix_length) {
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 /// @brief Get the address address of network
104 AddressType GetAddress() const noexcept { return address_; }
105
106 /// @brief Get prefix length of address network
107 unsigned char GetPrefixLength() const noexcept { return prefix_length_; }
108
109 friend bool operator==(const NetworkBase<Address>& a,
110 const NetworkBase<Address>& b) noexcept {
111 return a.address_ == b.address_ && a.prefix_length_ == b.prefix_length_;
112 }
113
114 friend bool operator!=(const NetworkBase<Address>& a,
115 const NetworkBase<Address>& b) noexcept {
116 return !(a == b);
117 }
118
119 private:
120 AddressType address_;
121 unsigned char prefix_length_ = 0;
122};
123
124/// @ingroup userver_containers
125///
126/// @brief IPv4 network.
127using NetworkV4 = NetworkBase<AddressV4>;
128
129/// @ingroup userver_containers
130///
131/// @brief IPv6 network.
132using NetworkV6 = NetworkBase<AddressV6>;
133
134///@brief Create an IPv4 network from a string containing IP address and prefix
135/// length.
136/// @throw std::invalid_argument, AddressSystemError
138
139/// @brief Create an IPv6 network from a string containing IP address and prefix
140/// length.
142
143///@brief Get the network as an address in dotted decimal format.
144std::string NetworkV4ToString(const NetworkV4& network);
145
146/// @brief Get the network as an address in dotted decimal format.
147std::string NetworkV6ToString(const NetworkV6& network);
148
149/// @brief Convert NetworkV4 to CIDR format
151
152/// @brief Convert NetworkV4 to CIDR format
154
155/// @ingroup userver_containers
156///
157/// @brief INET IPv4/IPv4 network
158/// @warning InetNetwork class is deprecated. You should use InetNetwork class
159/// via transformation function to/from NetworkV4/NetworkV6.
160/// Use this class only if you need to work with INET PostgreSQL format.
161class InetNetwork final {
162 public:
163 enum class AddressFamily : unsigned char { IPv4 = AF_INET, IPv6 = AF_INET6 };
164
165 // Default constructor: IPv4 address
166 InetNetwork();
167 InetNetwork(std::vector<unsigned char>&& bytes, unsigned char prefix_length,
168 AddressFamily address_family);
169
170 /// @brief Get the address in bytes
171 const std::vector<unsigned char>& GetBytes() const noexcept { return bytes_; }
172
173 /// @brief Get the prefix length of network
174 unsigned char GetPrefixLength() const noexcept { return prefix_length_; }
175
176 /// @brief Get the address family
177 AddressFamily GetAddressFamily() const noexcept { return address_family_; }
178
179 friend bool operator==(const InetNetwork& lhs, const InetNetwork& rhs) {
180 return lhs.address_family_ == rhs.address_family_ &&
181 lhs.prefix_length_ == rhs.prefix_length_ && lhs.bytes_ == rhs.bytes_;
182 }
183
184 friend bool operator!=(const InetNetwork& lhs, const InetNetwork& rhs) {
185 return !operator==(lhs, rhs);
186 }
187
188 private:
189 std::vector<unsigned char> bytes_;
190 unsigned char prefix_length_;
191 AddressFamily address_family_;
192};
193
194/// @brief Convert InetNetwork to NetworkV4
196
197/// @brief Convert InetNetwork to NetworkV6
199
200/// @brief Convert NetworkV4 to InetNetwork
201InetNetwork NetworkV4ToInetNetwork(const NetworkV4& network);
202
203/// @brief Convert NetworkV6 to InetNetwork
204InetNetwork NetworkV6ToInetNetwork(const NetworkV6& network);
205
206/// @brief Invalid network or address
207class AddressSystemError final : public std::exception {
208 public:
209 AddressSystemError(std::error_code code, std::string_view msg)
210 : msg_(msg), code_(code) {}
211
212 /// Operating system error code.
213 const std::error_code& Code() const { return code_; }
214
215 const char* what() const noexcept final { return msg_.c_str(); }
216
217 private:
218 std::string msg_;
219 std::error_code code_;
220};
221
222} // namespace utils::ip
223
224USERVER_NAMESPACE_END