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