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