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>
44concept NetworkType =
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>
49concept AddressType =
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 <AddressType 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 <NetworkType 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 static constexpr std::size_t kHeaderSize = 4;
144 // The buffer comes straight from the server, so validate its length
145 // before reading the fixed header and the address bytes below.
146 if (buffer.length < kHeaderSize) {
147 throw storages::postgres::InvalidInputBufferSize(
148 fmt::format("Buffer size {} is too small to parse an IP address", buffer.length)
149 );
150 }
151 IpParserInfo<Bytes> result;
152 const uint8_t* byte_cptr = buffer.buffer;
153 result.family = *byte_cptr;
154 ++byte_cptr;
155 result.prefix_length = *byte_cptr;
156 ++byte_cptr;
157 result.is_cidr = *byte_cptr;
158 ++byte_cptr;
159 result.bytes_number = *byte_cptr;
160 ++byte_cptr;
161 if (buffer.length - kHeaderSize < result.bytes_number) {
162 throw storages::postgres::InvalidInputBufferSize(fmt::format(
163 "Buffer size {} is too small for an IP address of {} bytes",
164 buffer.length,
165 result.bytes_number
166 ));
167 }
168 this->ParseIpBytes(byte_cptr, result.bytes, result.bytes_number, result.family);
169 return result;
170 }
171
172private:
173 template <size_t N>
174 void ParseIpBytes(
175 const uint8_t* byte_cptr,
176 std::array<unsigned char, N>& bytes,
177 unsigned char bytes_number,
178 unsigned char
179 ) {
180 if (bytes_number != bytes.size()) {
181 throw storages::postgres::IpAddressInvalidFormat(
182 fmt::format("Expected address size is {}, actual is {}", bytes_number, bytes.size())
183 );
184 }
185 std::memcpy(bytes.data(), byte_cptr, bytes.size());
186 }
187
188 void ParseIpBytes(
189 const uint8_t* byte_cptr,
190 std::vector<unsigned char>& bytes,
191 unsigned char bytes_number,
192 unsigned char address_family
193 ) {
194 if (!(bytes_number == 16 && address_family == kPgsqlAfInet6) &&
195 !(bytes_number == 4 && address_family == kPgsqlAfInet))
196 {
197 throw storages::postgres::IpAddressInvalidFormat("Invalid INET format");
198 }
199 bytes.resize(bytes_number);
200 std::memcpy(bytes.data(), byte_cptr, bytes_number);
201 }
202};
203
204template <NetworkType T>
205struct NetworkBufferParser : IpBufferParserBase<T> {
206 using BaseType = IpBufferParserBase<T>;
207 using BaseType::BaseType;
208
209 void operator()(FieldBuffer buffer) {
210 using Address = typename T::AddressType;
211 using Bytes = typename Address::BytesType;
212 const auto info = BaseType::template Parse<Bytes>(buffer);
213 constexpr auto expected_family = std::is_same_v<T, NetworkV4> ? kPgsqlAfInet : kPgsqlAfInet6;
214 if (info.family != expected_family) {
215 throw storages::postgres::IpAddressInvalidFormat("Actual address family doesn't supported for type");
216 }
217 if (info.is_cidr != kIsCidr) {
218 throw storages::postgres::IpAddressInvalidFormat("Network isn't in CIDR format");
219 }
220 this->value = T(Address(info.bytes), info.prefix_length);
221 }
222};
223
224template <AddressType T>
225struct AddressBufferParser : detail::IpBufferParserBase<T> {
226 using BaseType = detail::IpBufferParserBase<T>;
227 using BaseType::BaseType;
228
229 void operator()(FieldBuffer buffer) {
230 using Bytes = typename T::BytesType;
231 const auto info = BaseType::template Parse<Bytes>(buffer);
232 constexpr auto expected_family = std::is_same_v<T, AddressV4> ? kPgsqlAfInet : kPgsqlAfInet6;
233 if (info.family != expected_family) {
234 throw storages::postgres::IpAddressInvalidFormat("Actual address family doesn't supported for type");
235 }
236 constexpr unsigned char expected_prefix_length =
237 std::is_same_v<T, AddressV4> ? NetworkV4::kMaximumPrefixLength : NetworkV6::kMaximumPrefixLength;
238 if (info.prefix_length != expected_prefix_length) {
239 throw storages::postgres::IpAddressInvalidFormat(fmt::format(
240 "Expected prefix length is {}, actual prefix is {}",
241 static_cast<int>(expected_prefix_length),
242 static_cast<int>(info.prefix_length)
243 ));
244 }
245 if (info.is_cidr != kIsCidr) {
246 throw storages::postgres::IpAddressInvalidFormat("Network isn't in CIDR format");
247 }
248 this->value = T(info.bytes);
249 }
250};
251
252} // namespace detail
253
254///@brief Binary formatter for utils::ip::NetworkV4
255template <>
256struct BufferFormatter<NetworkV4> : detail::NetworkBufferFormatter<NetworkV4> {
257 using BaseType = detail::NetworkBufferFormatter<NetworkV4>;
258
259 using BaseType::BaseType;
260};
261
262///@brief Binary formatter for utils::ip::NetworkV6
263template <>
264struct BufferFormatter<NetworkV6> : detail::NetworkBufferFormatter<NetworkV6> {
265 using BaseType = detail::NetworkBufferFormatter<NetworkV6>;
266
267 using BaseType::BaseType;
268};
269
270///@brief Binary formatter for utils::ip::AddressV4
271template <>
272struct BufferFormatter<AddressV4> : detail::AddressNetworkBuffer<AddressV4> {
273 using BaseType = detail::AddressNetworkBuffer<AddressV4>;
274
275 using BaseType::BaseType;
276};
277
278///@brief Binary formatter for utils::ip::AddressV6
279template <>
280struct BufferFormatter<AddressV6> : detail::AddressNetworkBuffer<AddressV6> {
281 using BaseType = detail::AddressNetworkBuffer<AddressV6>;
282
283 using BaseType::BaseType;
284};
285
286///@brief Binary formatter for utils::ip::InetNetwork
287template <>
288struct BufferFormatter<InetNetwork> : detail::IpBufferFormatterBase<InetNetwork> {
289 using BaseType = detail::IpBufferFormatterBase<InetNetwork>;
290 using BaseType::BaseType;
291
292 template <typename Buffer>
293 void operator()(const UserTypes& types, Buffer& buffer) {
294 using Address = std::vector<unsigned char>;
295 const typename BaseType::template IpFormatterInfo<Address> info{
296 /* .address = */ this->value.GetBytes(),
297 /* .address_family = */
298 (this->value.GetAddressFamily() == InetNetwork::AddressFamily::kIPv4)
299 ? detail::kPgsqlAfInet
300 : detail::kPgsqlAfInet6,
301 /* .prefix_length = */ static_cast<char>(this->value.GetPrefixLength()),
302 /* .is_cidr = */ detail::kIsInet
303 };
304 BaseType::Format(info, types, buffer);
305 }
306};
307
308/// @brief Binary parser for utils::ip::NetworkV4
309template <>
310struct BufferParser<NetworkV4> : detail::NetworkBufferParser<NetworkV4> {
311 using BaseType = detail::NetworkBufferParser<NetworkV4>;
312
313 using BaseType::BaseType;
314};
315
316/// @brief Binary parser for utils::ip::NetworkV6
317template <>
318struct BufferParser<NetworkV6> : detail::NetworkBufferParser<NetworkV6> {
319 using BaseType = detail::NetworkBufferParser<NetworkV6>;
320
321 using BaseType::BaseType;
322};
323
324/// @brief Binary parser for utils::ip::AddressV4
325template <>
326struct BufferParser<AddressV4> : detail::AddressBufferParser<AddressV4> {
327 using BaseType = detail::AddressBufferParser<AddressV4>;
328
329 using BaseType::BaseType;
330};
331
332/// @brief Binary parser for utils::ip::AddressV6
333template <>
334struct BufferParser<AddressV6> : detail::AddressBufferParser<AddressV6> {
335 using BaseType = detail::AddressBufferParser<AddressV6>;
336
337 using BaseType::BaseType;
338};
339
340/// @brief Binary parser for utils::ip::InetNetwork
341template <>
342struct BufferParser<InetNetwork> : detail::IpBufferParserBase<InetNetwork> {
343 using BaseType = detail::IpBufferParserBase<InetNetwork>;
344 using BaseType::BaseType;
345
346 void operator()(FieldBuffer buffer) {
347 using Bytes = std::vector<unsigned char>;
348 auto info = BaseType::template Parse<Bytes>(buffer);
349 this->value = InetNetwork(
350 std::move(info.bytes),
351 info.prefix_length,
352 (info.family == detail::kPgsqlAfInet ? InetNetwork::AddressFamily::kIPv4 : InetNetwork::AddressFamily::kIPv6
353 )
354 );
355 }
356};
357
358//@{
359/** @name C++ to PostgreSQL mapping for ip types */
360template <>
361struct CppToSystemPg<NetworkV4> : PredefinedOid<PredefinedOids::kCidr> {};
362template <>
363struct CppToSystemPg<NetworkV6> : PredefinedOid<PredefinedOids::kCidr> {};
364template <>
365struct CppToSystemPg<AddressV6> : PredefinedOid<PredefinedOids::kCidr> {};
366template <>
367struct CppToSystemPg<AddressV4> : PredefinedOid<PredefinedOids::kCidr> {};
368template <>
369struct CppToSystemPg<InetNetwork> : PredefinedOid<PredefinedOids::kInet> {};
370//@}
371
372} // namespace io
373
374} // namespace storages::postgres
375
376USERVER_NAMESPACE_END