userver: userver/storages/postgres/io/ip.hpp Source File
Loading...
Searching...
No Matches
ip.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/storages/postgres/io/ip.hpp
4/// @brief utils::ip::NetworkV4 I/O support
5/// @ingroup userver_postgres_parse_and_format
6
7#include <limits>
8
9#include <userver/storages/postgres/io/buffer_io.hpp>
10#include <userver/storages/postgres/io/buffer_io_base.hpp>
11#include <userver/storages/postgres/io/integral_types.hpp>
12#include <userver/storages/postgres/io/string_types.hpp>
13#include <userver/storages/postgres/io/type_mapping.hpp>
14#include <userver/utils/ip.hpp>
15
16USERVER_NAMESPACE_BEGIN
17
18namespace storages::postgres {
19
20// Corresponds to NetworkV4
21using NetworkV4 = USERVER_NAMESPACE::utils::ip::NetworkV4;
22
23// Corresponds to AddressV4
24using AddressV4 = USERVER_NAMESPACE::utils::ip::AddressV4;
25
26using NetworkV6 = USERVER_NAMESPACE::utils::ip::NetworkV6;
27
28using AddressV6 = USERVER_NAMESPACE::utils::ip::AddressV6;
29
30using InetNetwork = USERVER_NAMESPACE::utils::ip::InetNetwork;
31
32namespace io {
33
34namespace detail {
35// Corresponds to postgresql address family
36inline constexpr char kPgsqlAfInet = AF_INET + 0;
37inline constexpr char kPgsqlAfInet6 = AF_INET + 1;
38
39// Corresponds to postgresql is_cidr flag
40inline constexpr char kIsCidr = 1;
41inline constexpr char kIsInet = 0;
42
43template <typename T>
44inline constexpr bool kIsNetworkType =
45 std::is_same_v<T, USERVER_NAMESPACE::utils::ip::NetworkV4> ||
46 std::is_same_v<T, USERVER_NAMESPACE::utils::ip::NetworkV6>;
47
48template <typename T>
49inline constexpr bool kIsAddressType =
50 std::is_same_v<T, USERVER_NAMESPACE::utils::ip::AddressV4> ||
51 std::is_same_v<T, USERVER_NAMESPACE::utils::ip::AddressV6>;
52
53template <typename T>
54struct IpBufferFormatterBase : BufferFormatterBase<T> {
55protected:
56 using BaseType = BufferFormatterBase<T>;
57 using BaseType::BaseType;
58 template <typename Address>
59 struct IpFormatterInfo {
60 Address address;
61 char address_family = '\0';
62 char prefix_length = '\0';
63 char is_cidr = '\0';
64 };
65
66 template <typename Buffer, typename Address>
67 void Format(IpFormatterInfo<Address> info, const UserTypes& types, Buffer& buffer) {
68 buffer.reserve(buffer.size() + info.address.size() + 4);
69 io::WriteBuffer(types, buffer, info.address_family);
70 io::WriteBuffer(types, buffer, info.prefix_length);
71 io::WriteBuffer(types, buffer, info.is_cidr);
72 io::WriteBuffer(types, buffer, static_cast<char>(info.address.size()));
73 for (const auto val : info.address) {
74 io::WriteBuffer(types, buffer, static_cast<char>(val));
75 }
76 }
77};
78
79template <typename T, typename = std::enable_if_t<kIsAddressType<T>>>
80struct AddressNetworkBuffer : IpBufferFormatterBase<T> {
81 using BaseType = IpBufferFormatterBase<T>;
82 using BaseType::BaseType;
83
84 template <typename Buffer>
85 void operator()(const UserTypes& types, Buffer& buffer) {
86 using Address = typename T::BytesType;
87 constexpr bool kIsAddressV4 = std::is_same_v<T, USERVER_NAMESPACE::utils::ip::AddressV4>;
88 const typename BaseType::template IpFormatterInfo<Address> info{
89 /* .address = */ this->value.GetBytes(),
90 /* .address_family = */ kIsAddressV4 ? kPgsqlAfInet : kPgsqlAfInet6,
91 /* .prefix_length = */
92 static_cast<char>(kIsAddressV4 ? NetworkV4::kMaximumPrefixLength : NetworkV6::kMaximumPrefixLength),
93 /* .is_cidr = */ kIsCidr
94 };
95 BaseType::Format(info, types, buffer);
96 }
97};
98
99template <typename T, typename = std::enable_if_t<kIsNetworkType<T>>>
100struct NetworkBufferFormatter : IpBufferFormatterBase<T> {
101 using BaseType = IpBufferFormatterBase<T>;
102 using BaseType::BaseType;
103
104 template <typename Buffer>
105 void operator()(const UserTypes& types, Buffer& buffer) {
106 using Address = typename T::AddressType::BytesType;
107 const auto canonical_network = USERVER_NAMESPACE::utils::ip::TransformToCidrFormat(this->value);
108 if (canonical_network != this->value) {
110 "Network expected CIDR format. Use utils::ip::TransformToCidrFormat "
111 "method to conversation."
112 );
113 }
114 const typename BaseType::template IpFormatterInfo<Address> info{
115 /* .address = */ canonical_network.GetAddress().GetBytes(),
116 /* .address_family = */
117 std::is_same_v<T, USERVER_NAMESPACE::utils::ip::NetworkV4> ? kPgsqlAfInet : kPgsqlAfInet6,
118 /* .prefix_length = */
119 static_cast<char>(canonical_network.GetPrefixLength()),
120 /* .is_cidr = */ kIsCidr
121 };
122 BaseType::Format(info, types, buffer);
123 }
124};
125
126template <typename T>
127struct IpBufferParserBase : BufferParserBase<T> {
128protected:
129 using BaseType = BufferParserBase<T>;
130 using BaseType::BaseType;
131
132 template <typename Bytes>
133 struct IpParserInfo {
134 Bytes bytes{};
135 unsigned char family = '\0';
136 unsigned char prefix_length = '\0';
137 unsigned char is_cidr = '\0';
138 unsigned char bytes_number = '\0';
139 };
140
141 template <typename Bytes>
142 IpParserInfo<Bytes> Parse(FieldBuffer buffer) {
143 IpParserInfo<Bytes> result;
144 const uint8_t* byte_cptr = buffer.buffer;
145 result.family = *byte_cptr;
146 ++byte_cptr;
147 result.prefix_length = *byte_cptr;
148 ++byte_cptr;
149 result.is_cidr = *byte_cptr;
150 ++byte_cptr;
151 result.bytes_number = *byte_cptr;
152 ++byte_cptr;
153 this->ParseIpBytes(byte_cptr, result.bytes, result.bytes_number, result.family);
154 return result;
155 }
156
157private:
158 template <size_t N>
159 void ParseIpBytes(
160 const uint8_t* byte_cptr,
161 std::array<unsigned char, N>& bytes,
162 unsigned char bytes_number,
163 unsigned char
164 ) {
165 if (bytes_number != bytes.size()) {
166 throw storages::postgres::IpAddressInvalidFormat(
167 fmt::format("Expected address size is {}, actual is {}", bytes_number, bytes.size())
168 );
169 }
170 std::memcpy(bytes.data(), byte_cptr, bytes.size());
171 }
172
173 void ParseIpBytes(
174 const uint8_t* byte_cptr,
175 std::vector<unsigned char>& bytes,
176 unsigned char bytes_number,
177 unsigned char address_family
178 ) {
179 if (!(bytes_number == 16 && address_family == kPgsqlAfInet6) &&
180 !(bytes_number == 4 && address_family == kPgsqlAfInet))
181 {
182 throw storages::postgres::IpAddressInvalidFormat("Invalid INET format");
183 }
184 bytes.resize(bytes_number);
185 std::memcpy(bytes.data(), byte_cptr, bytes_number);
186 }
187};
188
189template <typename T, typename = std::enable_if_t<kIsNetworkType<T>>>
190struct NetworkBufferParser : IpBufferParserBase<T> {
191 using BaseType = IpBufferParserBase<T>;
192 using BaseType::BaseType;
193
194 void operator()(FieldBuffer buffer) {
195 using Address = typename T::AddressType;
196 using Bytes = typename Address::BytesType;
197 const auto info = BaseType::template Parse<Bytes>(buffer);
198 constexpr auto expected_family = std::is_same_v<T, NetworkV4> ? kPgsqlAfInet : kPgsqlAfInet6;
199 if (info.family != expected_family) {
200 throw storages::postgres::IpAddressInvalidFormat("Actual address family doesn't supported for type");
201 }
202 if (info.is_cidr != kIsCidr) {
203 throw storages::postgres::IpAddressInvalidFormat("Network isn't in CIDR format");
204 }
205 this->value = T(Address(info.bytes), info.prefix_length);
206 }
207};
208
209template <typename T, typename = std::enable_if_t<kIsAddressType<T>>>
210struct AddressBufferParser : detail::IpBufferParserBase<T> {
211 using BaseType = detail::IpBufferParserBase<T>;
212 using BaseType::BaseType;
213
214 void operator()(FieldBuffer buffer) {
215 using Bytes = typename T::BytesType;
216 const auto info = BaseType::template Parse<Bytes>(buffer);
217 constexpr auto expected_family = std::is_same_v<T, AddressV4> ? kPgsqlAfInet : kPgsqlAfInet6;
218 if (info.family != expected_family) {
219 throw storages::postgres::IpAddressInvalidFormat("Actual address family doesn't supported for type");
220 }
221 constexpr unsigned char expected_prefix_length =
222 std::is_same_v<T, AddressV4> ? NetworkV4::kMaximumPrefixLength : NetworkV6::kMaximumPrefixLength;
223 if (info.prefix_length != expected_prefix_length) {
224 throw storages::postgres::IpAddressInvalidFormat(fmt::format(
225 "Expected prefix length is {}, actual prefix is {}",
226 static_cast<int>(expected_prefix_length),
227 static_cast<int>(info.prefix_length)
228 ));
229 }
230 if (info.is_cidr != kIsCidr) {
231 throw storages::postgres::IpAddressInvalidFormat("Network isn't in CIDR format");
232 }
233 this->value = T(info.bytes);
234 }
235};
236
237} // namespace detail
238
239///@brief Binary formatter for utils::ip::NetworkV4
240template <>
241struct BufferFormatter<NetworkV4> : detail::NetworkBufferFormatter<NetworkV4> {
242 using BaseType = detail::NetworkBufferFormatter<NetworkV4>;
243
244 using BaseType::BaseType;
245};
246
247///@brief Binary formatter for utils::ip::NetworkV6
248template <>
249struct BufferFormatter<NetworkV6> : detail::NetworkBufferFormatter<NetworkV6> {
250 using BaseType = detail::NetworkBufferFormatter<NetworkV6>;
251
252 using BaseType::BaseType;
253};
254
255///@brief Binary formatter for utils::ip::AddressV4
256template <>
257struct BufferFormatter<AddressV4> : detail::AddressNetworkBuffer<AddressV4> {
258 using BaseType = detail::AddressNetworkBuffer<AddressV4>;
259
260 using BaseType::BaseType;
261};
262
263///@brief Binary formatter for utils::ip::AddressV6
264template <>
265struct BufferFormatter<AddressV6> : detail::AddressNetworkBuffer<AddressV6> {
266 using BaseType = detail::AddressNetworkBuffer<AddressV6>;
267
268 using BaseType::BaseType;
269};
270
271///@brief Binary formatter for utils::ip::InetNetwork
272template <>
273struct BufferFormatter<InetNetwork> : detail::IpBufferFormatterBase<InetNetwork> {
274 using BaseType = detail::IpBufferFormatterBase<InetNetwork>;
275 using BaseType::BaseType;
276
277 template <typename Buffer>
278 void operator()(const UserTypes& types, Buffer& buffer) {
279 using Address = std::vector<unsigned char>;
280 const typename BaseType::template IpFormatterInfo<Address> info{
281 /* .address = */ this->value.GetBytes(),
282 /* .address_family = */
283 (this->value.GetAddressFamily() == InetNetwork::AddressFamily::kIPv4)
284 ? detail::kPgsqlAfInet
285 : detail::kPgsqlAfInet6,
286 /* .prefix_length = */ static_cast<char>(this->value.GetPrefixLength()),
287 /* .is_cidr = */ detail::kIsInet
288 };
289 BaseType::Format(info, types, buffer);
290 }
291};
292
293/// @brief Binary parser for utils::ip::NetworkV4
294template <>
295struct BufferParser<NetworkV4> : detail::NetworkBufferParser<NetworkV4> {
296 using BaseType = detail::NetworkBufferParser<NetworkV4>;
297
298 using BaseType::BaseType;
299};
300
301/// @brief Binary parser for utils::ip::NetworkV6
302template <>
303struct BufferParser<NetworkV6> : detail::NetworkBufferParser<NetworkV6> {
304 using BaseType = detail::NetworkBufferParser<NetworkV6>;
305
306 using BaseType::BaseType;
307};
308
309/// @brief Binary parser for utils::ip::AddressV4
310template <>
311struct BufferParser<AddressV4> : detail::AddressBufferParser<AddressV4> {
312 using BaseType = detail::AddressBufferParser<AddressV4>;
313
314 using BaseType::BaseType;
315};
316
317/// @brief Binary parser for utils::ip::AddressV6
318template <>
319struct BufferParser<AddressV6> : detail::AddressBufferParser<AddressV6> {
320 using BaseType = detail::AddressBufferParser<AddressV6>;
321
322 using BaseType::BaseType;
323};
324
325/// @brief Binary parser for utils::ip::InetNetwork
326template <>
327struct BufferParser<InetNetwork> : detail::IpBufferParserBase<InetNetwork> {
328 using BaseType = detail::IpBufferParserBase<InetNetwork>;
329 using BaseType::BaseType;
330
331 void operator()(FieldBuffer buffer) {
332 using Bytes = std::vector<unsigned char>;
333 auto info = BaseType::template Parse<Bytes>(buffer);
334 this->value = InetNetwork(
335 std::move(info.bytes),
336 info.prefix_length,
337 (info.family == detail::kPgsqlAfInet ? InetNetwork::AddressFamily::kIPv4 : InetNetwork::AddressFamily::kIPv6
338 )
339 );
340 }
341};
342
343//@{
344/** @name C++ to PostgreSQL mapping for ip types */
345template <>
346struct CppToSystemPg<NetworkV4> : PredefinedOid<PredefinedOids::kCidr> {};
347template <>
348struct CppToSystemPg<NetworkV6> : PredefinedOid<PredefinedOids::kCidr> {};
349template <>
350struct CppToSystemPg<AddressV6> : PredefinedOid<PredefinedOids::kCidr> {};
351template <>
352struct CppToSystemPg<AddressV4> : PredefinedOid<PredefinedOids::kCidr> {};
353template <>
354struct CppToSystemPg<InetNetwork> : PredefinedOid<PredefinedOids::kInet> {};
355//@}
356
357} // namespace io
358
359} // namespace storages::postgres
360
361USERVER_NAMESPACE_END