userver: userver/storages/postgres/io/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
1#pragma once
2
3/// @file userver/storages/postgres/io/network.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> {
55 protected:
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,
68 Buffer& buffer) {
69 buffer.reserve(buffer.size() + info.address.size() + 4);
70 io::WriteBuffer(types, buffer, info.address_family);
71 io::WriteBuffer(types, buffer, info.prefix_length);
72 io::WriteBuffer(types, buffer, info.is_cidr);
73 io::WriteBuffer(types, buffer, static_cast<char>(info.address.size()));
74 for (const auto val : info.address) {
75 io::WriteBuffer(types, buffer, static_cast<char>(val));
76 }
77 }
78};
79
80template <typename T, typename = std::enable_if_t<kIsAddressType<T>>>
81struct AddressNetworkBuffer : IpBufferFormatterBase<T> {
82 using BaseType = IpBufferFormatterBase<T>;
83 using BaseType::BaseType;
84
85 template <typename Buffer>
86 void operator()(const UserTypes& types, Buffer& buffer) {
87 using Address = typename T::BytesType;
88 constexpr bool is_address_v4 =
89 std::is_same_v<T, USERVER_NAMESPACE::utils::ip::AddressV4>;
90 typename BaseType::template IpFormatterInfo<Address> info{
91 /* .address = */ this->value.GetBytes(),
92 /* .address_family = */ is_address_v4 ? kPgsqlAfInet : kPgsqlAfInet6,
93 /* .prefix_length = */
94 static_cast<char>(is_address_v4 ? NetworkV4::kMaximumPrefixLength
95 : NetworkV6::kMaximumPrefixLength),
96 /* .is_cidr = */ kIsCidr};
97 BaseType::Format(info, types, buffer);
98 }
99};
100
101template <typename T, typename = std::enable_if_t<kIsNetworkType<T>>>
102struct NetworkBufferFormatter : IpBufferFormatterBase<T> {
103 using BaseType = IpBufferFormatterBase<T>;
104 using BaseType::BaseType;
105
106 template <typename Buffer>
107 void operator()(const UserTypes& types, Buffer& buffer) {
108 using Address = typename T::AddressType::BytesType;
109 const auto canonical_network =
110 USERVER_NAMESPACE::utils::ip::TransformToCidrFormat(this->value);
111 if (canonical_network != this->value) {
112 throw IpAddressInvalidFormat(
113 "Network expected CIDR format. Use utils::ip::TransformToCidrFormat "
114 "method to conversation.");
115 }
116 typename BaseType::template IpFormatterInfo<Address> info{
117 /* .address = */ canonical_network.GetAddress().GetBytes(),
118 /* .address_family = */
119 std::is_same_v<T, USERVER_NAMESPACE::utils::ip::NetworkV4>
120 ? kPgsqlAfInet
121 : kPgsqlAfInet6,
122 /* .prefix_length = */
123 static_cast<char>(canonical_network.GetPrefixLength()),
124 /* .is_cidr = */ kIsCidr};
125 BaseType::Format(info, types, buffer);
126 }
127};
128
129template <typename T>
130struct IpBufferParserBase : BufferParserBase<T> {
131 protected:
132 using BaseType = BufferParserBase<T>;
133 using BaseType::BaseType;
134
135 template <typename Bytes>
136 struct IpParserInfo {
137 Bytes bytes{};
138 unsigned char family = '\0';
139 unsigned char prefix_length = '\0';
140 unsigned char is_cidr = '\0';
141 unsigned char bytes_number = '\0';
142 };
143
144 template <typename Bytes>
145 IpParserInfo<Bytes> Parse(FieldBuffer buffer) {
146 IpParserInfo<Bytes> result;
147 const uint8_t* byte_cptr = buffer.buffer;
148 result.family = *byte_cptr;
149 ++byte_cptr;
150 result.prefix_length = *byte_cptr;
151 ++byte_cptr;
152 result.is_cidr = *byte_cptr;
153 ++byte_cptr;
154 result.bytes_number = *byte_cptr;
155 ++byte_cptr;
156 this->ParseIpBytes(byte_cptr, result.bytes, result.bytes_number,
157 result.family);
158 return result;
159 }
160
161 private:
162 template <size_t N>
163 void ParseIpBytes(const uint8_t* byte_cptr,
164 std::array<unsigned char, N>& bytes,
165 unsigned char bytes_number, unsigned char) {
166 if (bytes_number != bytes.size()) {
167 throw storages::postgres::IpAddressInvalidFormat(
168 fmt::format("Expected address size is {}, actual is {}", bytes_number,
169 bytes.size()));
170 }
171 std::memcpy(bytes.data(), byte_cptr, bytes.size());
172 }
173
174 void ParseIpBytes(const uint8_t* byte_cptr, std::vector<unsigned char>& bytes,
175 unsigned char bytes_number, unsigned char address_family) {
176 if (!(bytes_number == 16 && address_family == kPgsqlAfInet6) &&
177 !(bytes_number == 4 && address_family == kPgsqlAfInet)) {
178 throw storages::postgres::IpAddressInvalidFormat("Invalid INET format");
179 }
180 bytes.resize(bytes_number);
181 std::memcpy(bytes.data(), byte_cptr, bytes_number);
182 }
183};
184
185template <typename T, typename = std::enable_if_t<kIsNetworkType<T>>>
186struct NetworkBufferParser : IpBufferParserBase<T> {
187 using BaseType = IpBufferParserBase<T>;
188 using BaseType::BaseType;
189
190 void operator()(FieldBuffer buffer) {
191 using Address = typename T::AddressType;
192 using Bytes = typename Address::BytesType;
193 const auto info = BaseType::template Parse<Bytes>(buffer);
194 constexpr auto expected_family =
195 std::is_same_v<T, NetworkV4> ? kPgsqlAfInet : kPgsqlAfInet6;
196 if (info.family != expected_family) {
197 throw storages::postgres::IpAddressInvalidFormat(
198 "Actual address family doesn't supported for type");
199 }
200 if (info.is_cidr != kIsCidr) {
201 throw storages::postgres::IpAddressInvalidFormat(
202 "Network isn't in CIDR format");
203 }
204 this->value = T(Address(info.bytes), info.prefix_length);
205 }
206};
207
208template <typename T, typename = std::enable_if_t<kIsAddressType<T>>>
209struct AddressBufferParser : detail::IpBufferParserBase<T> {
210 using BaseType = detail::IpBufferParserBase<T>;
211 using BaseType::BaseType;
212
213 void operator()(FieldBuffer buffer) {
214 using Bytes = typename T::BytesType;
215 const auto info = BaseType::template Parse<Bytes>(buffer);
216 constexpr auto expected_family =
217 std::is_same_v<T, AddressV4> ? kPgsqlAfInet : kPgsqlAfInet6;
218 if (info.family != expected_family) {
219 throw storages::postgres::IpAddressInvalidFormat(
220 "Actual address family doesn't supported for type");
221 }
222 constexpr unsigned char expected_prefix_length =
223 std::is_same_v<T, AddressV4> ? NetworkV4::kMaximumPrefixLength
224 : NetworkV6::kMaximumPrefixLength;
225 if (info.prefix_length != expected_prefix_length) {
226 throw storages::postgres::IpAddressInvalidFormat(
227 fmt::format("Expected prefix length is {}, actual prefix is {}",
228 static_cast<int>(expected_prefix_length),
229 static_cast<int>(info.prefix_length)));
230 }
231 if (info.is_cidr != kIsCidr) {
232 throw storages::postgres::IpAddressInvalidFormat(
233 "Network isn't in CIDR format");
234 }
235 this->value = T(info.bytes);
236 }
237};
238
239} // namespace detail
240
241///@brief Binary formatter for utils::ip::NetworkV4
242template <>
245
246 using BaseType::BaseType;
247};
248
249///@brief Binary formatter for utils::ip::NetworkV6
250template <>
253
254 using BaseType::BaseType;
255};
256
257///@brief Binary formatter for utils::ip::AddressV4
258template <>
261
262 using BaseType::BaseType;
263};
264
265///@brief Binary formatter for utils::ip::AddressV6
266template <>
269
270 using BaseType::BaseType;
271};
272
273///@brief Binary formatter for utils::ip::InetNetwork
274template <>
278 using BaseType::BaseType;
279
280 template <typename Buffer>
281 void operator()(const UserTypes& types, Buffer& buffer) {
282 using Address = std::vector<unsigned char>;
283 typename BaseType::template IpFormatterInfo<Address> info{
284 /* .address = */ this->value.GetBytes(),
285 /* .address_family = */
289 /* .prefix_length = */ static_cast<char>(this->value.GetPrefixLength()),
290 /* .is_cidr = */ detail::kIsInet};
292 }
293};
294
295/// @brief Binary parser for utils::ip::NetworkV4
296template <>
299
300 using BaseType::BaseType;
301};
302
303/// @brief Binary parser for utils::ip::NetworkV6
304template <>
307
308 using BaseType::BaseType;
309};
310
311/// @brief Binary parser for utils::ip::AddressV4
312template <>
315
316 using BaseType::BaseType;
317};
318
319/// @brief Binary parser for utils::ip::AddressV6
320template <>
323
324 using BaseType::BaseType;
325};
326
327/// @brief Binary parser for utils::ip::InetNetwork
328template <>
331 using BaseType::BaseType;
332
333 void operator()(FieldBuffer buffer) {
334 using Bytes = std::vector<unsigned char>;
335 auto info = BaseType::template Parse<Bytes>(buffer);
340 }
341};
342
343//@{
344/** @name C++ to PostgreSQL mapping for ip types */
345template <>
347template <>
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