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