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>
16USERVER_NAMESPACE_BEGIN
24using AddressV4 = USERVER_NAMESPACE::utils::ip::AddressV4;
28using AddressV6 = USERVER_NAMESPACE::utils::ip::AddressV6;
30using InetNetwork = USERVER_NAMESPACE::utils::ip::InetNetwork;
36inline constexpr char kPgsqlAfInet = AF_INET + 0;
37inline constexpr char kPgsqlAfInet6 = AF_INET + 1;
40inline constexpr char kIsCidr = 1;
41inline constexpr char kIsInet = 0;
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>;
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>;
54struct IpBufferFormatterBase : BufferFormatterBase<T> {
56 using BaseType = BufferFormatterBase<T>;
57 using BaseType::BaseType;
58 template <
typename Address>
59 struct IpFormatterInfo {
61 char address_family =
'\0';
62 char prefix_length =
'\0';
66 template <
typename Buffer,
typename Address>
67 void Format(IpFormatterInfo<Address> info,
const UserTypes& types,
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));
80template <
typename T,
typename = std::enable_if_t<kIsAddressType<T>>>
81struct AddressNetworkBuffer : IpBufferFormatterBase<T> {
82 using BaseType = IpBufferFormatterBase<T>;
83 using BaseType::BaseType;
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 this->value.GetBytes(),
92 is_address_v4 ? kPgsqlAfInet : kPgsqlAfInet6,
94 static_cast<
char>(is_address_v4 ? NetworkV4::kMaximumPrefixLength
95 : NetworkV6::kMaximumPrefixLength),
97 BaseType::Format(info, types, buffer);
101template <
typename T,
typename = std::enable_if_t<kIsNetworkType<T>>>
102struct NetworkBufferFormatter : IpBufferFormatterBase<T> {
103 using BaseType = IpBufferFormatterBase<T>;
104 using BaseType::BaseType;
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.");
116 typename BaseType::
template IpFormatterInfo<Address> info{
117 canonical_network.GetAddress().GetBytes(),
119 std::is_same_v<T, USERVER_NAMESPACE::utils::ip::NetworkV4>
123 static_cast<
char>(canonical_network.GetPrefixLength()),
125 BaseType::Format(info, types, buffer);
130struct IpBufferParserBase : BufferParserBase<T> {
132 using BaseType = BufferParserBase<T>;
133 using BaseType::BaseType;
135 template <
typename Bytes>
136 struct IpParserInfo {
138 unsigned char family =
'\0';
139 unsigned char prefix_length =
'\0';
140 unsigned char is_cidr =
'\0';
141 unsigned char bytes_number =
'\0';
144 template <
typename Bytes>
146 IpParserInfo<Bytes> result;
147 const uint8_t* byte_cptr = buffer.buffer;
148 result.family = *byte_cptr;
150 result.prefix_length = *byte_cptr;
152 result.is_cidr = *byte_cptr;
154 result.bytes_number = *byte_cptr;
156 this->ParseIpBytes(byte_cptr, result.bytes, result.bytes_number,
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,
171 std::memcpy(bytes.data(), byte_cptr, bytes.size());
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");
180 bytes.resize(bytes_number);
181 std::memcpy(bytes.data(), byte_cptr, bytes_number);
185template <
typename T,
typename = std::enable_if_t<kIsNetworkType<T>>>
186struct NetworkBufferParser : IpBufferParserBase<T> {
187 using BaseType = IpBufferParserBase<T>;
188 using BaseType::BaseType;
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");
200 if (info.is_cidr != kIsCidr) {
201 throw storages::postgres::IpAddressInvalidFormat(
202 "Network isn't in CIDR format");
204 this->value = T(Address(info.bytes), info.prefix_length);
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;
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");
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)));
231 if (info.is_cidr != kIsCidr) {
232 throw storages::postgres::IpAddressInvalidFormat(
233 "Network isn't in CIDR format");
235 this->value = T(info.bytes);
280 template <
typename Buffer>
350struct CppToSystemPg<AddressV6> : PredefinedOid<PredefinedOids::kCidr> {};
352struct CppToSystemPg<AddressV4> : PredefinedOid<PredefinedOids::kCidr> {};
354struct CppToSystemPg<InetNetwork> : PredefinedOid<PredefinedOids::kInet> {};