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 <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