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 =
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