blob: 3273ec7251d281c98fd9964c733d28639f4664c2 [file] [log] [blame]
William A. Kennington IIId5957f52023-06-16 16:55:01 -07001#include <fmt/core.h>
2
William A. Kennington III14dd4eb2023-01-12 10:51:12 -08003#include <stdplus/net/addr/ip.hpp>
4#include <stdplus/numeric/endian.hpp>
William A. Kennington IIId5957f52023-06-16 16:55:01 -07005#include <stdplus/numeric/str.hpp>
6#include <stdplus/str/conv.hpp>
William A. Kennington III14dd4eb2023-01-12 10:51:12 -08007
William A. Kennington IIIc870ac32023-06-17 17:13:45 -07008#include <bit>
William A. Kennington III14dd4eb2023-01-12 10:51:12 -08009#include <limits>
10#include <type_traits>
11
12namespace stdplus
13{
14namespace detail
15{
16
17// AddressSan doesn't understand our masking of shift UB
Patrick Williams5e026b82025-05-28 17:15:03 -040018__attribute__((no_sanitize("undefined"))) constexpr uint32_t addr32Mask(
19 std::ptrdiff_t pfx) noexcept
William A. Kennington III14dd4eb2023-01-12 10:51:12 -080020{
William A. Kennington III49d92692023-06-06 14:02:01 -070021 // NOLINTNEXTLINE(clang-analyzer-core.UndefinedBinaryOperatorResult)
Patrick Williamsd8e0af52024-08-16 15:21:12 -040022 // NOLINTNEXTLINE(clang-analyzer-core.BitwiseShift)
William A. Kennington III49d92692023-06-06 14:02:01 -070023 auto v = ~uint32_t{0} << (32 - pfx);
William A. Kennington III14dd4eb2023-01-12 10:51:12 -080024 // Positive prefix check + mask to handle UB when the left shift becomes
25 // more than 31 bits
William A. Kennington III49d92692023-06-06 14:02:01 -070026 return hton(static_cast<uint32_t>(-int32_t{pfx > 0}) & v);
William A. Kennington III14dd4eb2023-01-12 10:51:12 -080027}
28
29constexpr In4Addr addrToSubnet(In4Addr a, std::size_t pfx) noexcept
30{
William A. Kennington III3bd5add2023-06-28 11:56:15 -070031 return In4Addr{in_addr{a.word() & addr32Mask(pfx)}};
William A. Kennington III14dd4eb2023-01-12 10:51:12 -080032}
33
34constexpr In6Addr addrToSubnet(In6Addr a, std::size_t pfx, std::size_t i = 0,
35 std::size_t s = 0, In6Addr ret = {}) noexcept
36{
37 if (s + 32 < pfx)
38 {
William A. Kennington III5f280542023-06-28 11:57:38 -070039 ret.word(i, a.word(i));
William A. Kennington III14dd4eb2023-01-12 10:51:12 -080040 return addrToSubnet(a, pfx, i + 1, s + 32, ret);
41 }
William A. Kennington III5f280542023-06-28 11:57:38 -070042 ret.word(i, a.word(i) & addr32Mask(pfx - s));
William A. Kennington III14dd4eb2023-01-12 10:51:12 -080043 return ret;
44}
45
46constexpr InAnyAddr addrToSubnet(InAnyAddr a, std::size_t pfx) noexcept
47{
48 return std::visit([&](auto av) { return InAnyAddr{addrToSubnet(av, pfx)}; },
49 a);
50}
51
52constexpr bool subnetContains(auto, auto, std::size_t) noexcept
53{
54 return false;
55}
56
57template <typename T>
58constexpr bool subnetContains(T l, T r, std::size_t pfx) noexcept
59{
60 return addrToSubnet(l, pfx) == addrToSubnet(r, pfx);
61}
62
63constexpr bool subnetContains(InAnyAddr l, auto r, std::size_t pfx) noexcept
64{
65 return std::visit([&](auto v) { return detail::subnetContains(v, r, pfx); },
66 l);
67}
68
69constexpr std::size_t addrBits(auto a) noexcept
70{
71 return sizeof(a) << 3;
72}
73
74void invalidSubnetPfx(std::size_t pfx);
75
William A. Kennington IIId5957f52023-06-16 16:55:01 -070076template <typename Addr_, typename Pfx_>
William A. Kennington III14dd4eb2023-01-12 10:51:12 -080077class Subnet46
78{
William A. Kennington IIId5957f52023-06-16 16:55:01 -070079 public:
80 using Addr = Addr_;
81 using Pfx = Pfx_;
82
William A. Kennington III14dd4eb2023-01-12 10:51:12 -080083 private:
William A. Kennington III124a63a2023-12-17 21:36:48 -080084 static inline constexpr std::size_t maxPfx = sizeof(Addr) * 8;
William A. Kennington III14dd4eb2023-01-12 10:51:12 -080085 static_assert(std::is_unsigned_v<Pfx> && std::is_integral_v<Pfx>);
86 static_assert(std::numeric_limits<Pfx>::max() >= maxPfx);
87
88 Addr addr;
89 Pfx pfx;
90
91 public:
92 constexpr Subnet46(Addr addr, Pfx pfx) : addr(addr), pfx(pfx)
93 {
94 if (addrBits(addr) < pfx)
95 {
96 invalidSubnetPfx(pfx);
97 }
98 }
99
100 constexpr auto getAddr() const noexcept
101 {
102 return addr;
103 }
104
105 constexpr auto getPfx() const noexcept
106 {
107 return pfx;
108 }
109
110 constexpr bool operator==(Subnet46 rhs) const noexcept
111 {
112 return addr == rhs.addr && pfx == rhs.pfx;
113 }
114
115 constexpr Addr network() const noexcept
116 {
117 return addrToSubnet(addr, pfx);
118 }
119
120 constexpr bool contains(Addr addr) const noexcept
121 {
122 return addrToSubnet(this->addr, pfx) == addrToSubnet(addr, pfx);
123 }
124};
125
126} // namespace detail
127
William A. Kennington IIIc870ac32023-06-17 17:13:45 -0700128template <typename T>
129constexpr T pfxToMask(std::uint8_t pfx);
130
131template <>
132constexpr In4Addr pfxToMask<In4Addr>(std::uint8_t pfx)
133{
134 return in_addr{detail::addr32Mask(pfx)};
135}
136
137constexpr std::uint8_t maskToPfx(In4Addr mask)
138{
William A. Kennington III3bd5add2023-06-28 11:56:15 -0700139 uint32_t x = ntoh(mask.word());
William A. Kennington IIIc870ac32023-06-17 17:13:45 -0700140 if ((~x & (~x + 1)) != 0)
141 {
142 throw std::invalid_argument("Invalid netmask");
143 }
144 return 32 - std::countr_zero(x);
145}
146
William A. Kennington IIId5957f52023-06-16 16:55:01 -0700147using Subnet4 = detail::Subnet46<In4Addr, std::uint8_t>;
148using Subnet6 = detail::Subnet46<In6Addr, std::uint8_t>;
William A. Kennington III14dd4eb2023-01-12 10:51:12 -0800149
William A. Kennington IIIa42dfb42023-06-28 17:48:32 -0700150class SubnetAny;
151
152template <typename T>
153struct IsSubnet : std::false_type
154{};
155
156template <typename Addr, typename Pfx>
157struct IsSubnet<detail::Subnet46<Addr, Pfx>> : std::true_type
158{};
159
160template <>
161struct IsSubnet<SubnetAny> : std::true_type
162{};
163
164template <typename T>
165concept Subnet = IsSubnet<T>::value;
166
William A. Kennington III14dd4eb2023-01-12 10:51:12 -0800167class SubnetAny
168{
William A. Kennington IIId5957f52023-06-16 16:55:01 -0700169 public:
170 using Addr = InAnyAddr;
171 using Pfx = std::uint8_t;
172
William A. Kennington III14dd4eb2023-01-12 10:51:12 -0800173 private:
William A. Kennington IIId5957f52023-06-16 16:55:01 -0700174 Addr addr;
175 Pfx pfx;
William A. Kennington III14dd4eb2023-01-12 10:51:12 -0800176
177 public:
William A. Kennington IIId5957f52023-06-16 16:55:01 -0700178 constexpr SubnetAny(auto addr, Pfx pfx) : addr(addr), pfx(pfx)
William A. Kennington III14dd4eb2023-01-12 10:51:12 -0800179 {
180 if (detail::addrBits(addr) < pfx)
181 {
182 detail::invalidSubnetPfx(pfx);
183 }
184 }
William A. Kennington IIId5957f52023-06-16 16:55:01 -0700185 constexpr SubnetAny(InAnyAddr addr, Pfx pfx) : addr(addr), pfx(pfx)
William A. Kennington III14dd4eb2023-01-12 10:51:12 -0800186 {
187 if (std::visit([](auto v) { return detail::addrBits(v); }, addr) < pfx)
188 {
189 detail::invalidSubnetPfx(pfx);
190 }
191 }
192
William A. Kennington IIIa42dfb42023-06-28 17:48:32 -0700193 template <Subnet T>
194 constexpr SubnetAny(T o) noexcept : addr(o.getAddr()), pfx(o.getPfx())
William A. Kennington III14dd4eb2023-01-12 10:51:12 -0800195 {}
196
197 constexpr auto getAddr() const noexcept
198 {
199 return addr;
200 }
201
202 constexpr auto getPfx() const noexcept
203 {
204 return pfx;
205 }
206
William A. Kennington IIIa42dfb42023-06-28 17:48:32 -0700207 template <Subnet T>
208 constexpr bool operator==(T rhs) const noexcept
William A. Kennington III14dd4eb2023-01-12 10:51:12 -0800209 {
210 return addr == rhs.getAddr() && pfx == rhs.getPfx();
211 }
William A. Kennington III14dd4eb2023-01-12 10:51:12 -0800212
213 constexpr InAnyAddr network() const noexcept
214 {
215 return detail::addrToSubnet(addr, pfx);
216 }
217
218 constexpr bool contains(In4Addr addr) const noexcept
219 {
220 return detail::subnetContains(this->addr, addr, pfx);
221 }
222 constexpr bool contains(in_addr addr) const noexcept
223 {
224 return contains(In4Addr{addr});
225 }
226 constexpr bool contains(In6Addr addr) const noexcept
227 {
228 return detail::subnetContains(this->addr, addr, pfx);
229 }
230 constexpr bool contains(in6_addr addr) const noexcept
231 {
232 return contains(In6Addr{addr});
233 }
234 constexpr bool contains(InAnyAddr addr) const noexcept
235 {
236 return std::visit([&](auto v) { return contains(v); }, addr);
237 }
238};
239
William A. Kennington IIIa42dfb42023-06-28 17:48:32 -0700240template <Subnet Sub>
241struct FromStr<Sub>
William A. Kennington III160e3822023-06-16 17:09:43 -0700242{
243 template <typename CharT>
William A. Kennington IIIa42dfb42023-06-28 17:48:32 -0700244 constexpr Sub operator()(std::basic_string_view<CharT> sv) const
William A. Kennington III160e3822023-06-16 17:09:43 -0700245 {
246 const auto pos = sv.rfind('/');
247 if (pos == sv.npos)
248 {
249 throw std::invalid_argument("Invalid subnet");
250 }
William A. Kennington IIIa42dfb42023-06-28 17:48:32 -0700251 return {FromStr<typename Sub::Addr>{}(sv.substr(0, pos)),
252 StrToInt<10, typename Sub::Pfx>{}(sv.substr(pos + 1))};
William A. Kennington III160e3822023-06-16 17:09:43 -0700253 }
254};
255
William A. Kennington IIIa42dfb42023-06-28 17:48:32 -0700256template <Subnet Sub>
257struct ToStr<Sub>
William A. Kennington IIId5957f52023-06-16 16:55:01 -0700258{
William A. Kennington IIIa42dfb42023-06-28 17:48:32 -0700259 using type = Sub;
260 using ToDec = IntToStr<10, typename Sub::Pfx>;
William A. Kennington IIId5957f52023-06-16 16:55:01 -0700261 // Addr + sep + 3 prefix chars
William A. Kennington III124a63a2023-12-17 21:36:48 -0800262 static inline constexpr std::size_t buf_size =
William A. Kennington IIIa42dfb42023-06-28 17:48:32 -0700263 ToStr<typename Sub::Addr>::buf_size + 1 + ToDec::buf_size;
William A. Kennington IIId5957f52023-06-16 16:55:01 -0700264
265 template <typename CharT>
William A. Kennington IIIa42dfb42023-06-28 17:48:32 -0700266 constexpr CharT* operator()(CharT* buf, Sub v) const noexcept
William A. Kennington IIId5957f52023-06-16 16:55:01 -0700267 {
William A. Kennington IIIa42dfb42023-06-28 17:48:32 -0700268 buf = ToStr<typename Sub::Addr>{}(buf, v.getAddr());
William A. Kennington IIId5957f52023-06-16 16:55:01 -0700269 (buf++)[0] = '/';
270 return ToDec{}(buf, v.getPfx());
271 }
272};
273
William A. Kennington III39011c82023-06-28 17:49:30 -0700274namespace detail
275{
276
277template <Subnet Sub>
278struct CompileInAddrInt<Sub> : CompileInAddrInt<typename Sub::Addr>
279{
280 Sub::Pfx pfx = 0;
281
282 template <typename CharT>
William A. Kennington III4c8d2422023-12-08 15:47:31 -0800283 consteval void compile(std::basic_string_view<CharT> sv) noexcept
William A. Kennington III39011c82023-06-28 17:49:30 -0700284 {
285 const auto pos = sv.rfind('/');
286 if (pos == sv.npos)
287 {
288 this->valid = false;
289 }
290 static_cast<CompileInAddrInt<typename Sub::Addr>&>(*this).compile(
291 sv.substr(0, pos));
292 try
293 {
294 pfx = StrToInt<10, typename Sub::Pfx>{}(sv.substr(pos + 1));
295 }
296 catch (...)
297 {
298 this->valid = false;
299 }
300 }
301};
302
303template <typename CharT, std::size_t N>
304struct CompileSubnet4 : CompileInAddr<Subnet4, CharT, N>
305{
William A. Kennington III4c8d2422023-12-08 15:47:31 -0800306 consteval CompileSubnet4(const CharT (&str)[N]) noexcept :
William A. Kennington III39011c82023-06-28 17:49:30 -0700307 CompileInAddr<Subnet4, CharT, N>(str)
308 {}
309};
310
311template <typename CharT, std::size_t N>
312struct CompileSubnet6 : CompileInAddr<Subnet6, CharT, N>
313{
William A. Kennington III4c8d2422023-12-08 15:47:31 -0800314 consteval CompileSubnet6(const CharT (&str)[N]) noexcept :
William A. Kennington III39011c82023-06-28 17:49:30 -0700315 CompileInAddr<Subnet6, CharT, N>(str)
316 {}
317};
318
319template <typename CharT, std::size_t N>
320struct CompileSubnetAny : CompileInAddr<SubnetAny, CharT, N>
321{
William A. Kennington III4c8d2422023-12-08 15:47:31 -0800322 consteval CompileSubnetAny(const CharT (&str)[N]) noexcept :
William A. Kennington III39011c82023-06-28 17:49:30 -0700323 CompileInAddr<SubnetAny, CharT, N>(str)
324 {}
325};
326
327} // namespace detail
328
329inline namespace subnet_literals
330{
331
332template <detail::CompileSubnet4 Str>
Alexander Hansenbe32aee2025-05-12 12:16:51 +0200333constexpr auto operator""_sub4() noexcept
William A. Kennington III39011c82023-06-28 17:49:30 -0700334{
335 static_assert(Str.valid, "stdplus::Subnet4");
336 return Subnet4(Str.addr, Str.pfx);
337}
338
339template <detail::CompileSubnet6 Str>
Alexander Hansenbe32aee2025-05-12 12:16:51 +0200340constexpr auto operator""_sub6() noexcept
William A. Kennington III39011c82023-06-28 17:49:30 -0700341{
342 static_assert(Str.valid, "stdplus::Subnet6");
343 return Subnet6(Str.addr, Str.pfx);
344}
345
346template <detail::CompileSubnetAny Str>
Alexander Hansenbe32aee2025-05-12 12:16:51 +0200347constexpr auto operator""_sub() noexcept
William A. Kennington III39011c82023-06-28 17:49:30 -0700348{
349 static_assert(Str.valid, "stdplus::SubnetAny");
350 return Str.v4 ? SubnetAny(Str.u.addr4, Str.pfx)
351 : SubnetAny(Str.u.addr6, Str.pfx);
352}
353
354} // namespace subnet_literals
William A. Kennington IIIa42dfb42023-06-28 17:48:32 -0700355} // namespace stdplus
356
357template <stdplus::Subnet Sub, typename CharT>
358struct fmt::formatter<Sub, CharT> : stdplus::Format<stdplus::ToStr<Sub>, CharT>
359{};
360
361template <stdplus::Subnet Sub>
362struct std::hash<Sub>
William A. Kennington IIIb03c9cb2023-06-17 16:36:21 -0700363{
William A. Kennington IIIa42dfb42023-06-28 17:48:32 -0700364 constexpr std::size_t operator()(Sub addr) const noexcept
William A. Kennington IIIb03c9cb2023-06-17 16:36:21 -0700365 {
366 return stdplus::hashMulti(addr.getAddr(), addr.getPfx());
367 }
368};
William A. Kennington III5b20b0a2023-07-18 13:38:37 -0700369
370namespace std
371{
372template <typename T, typename CharT>
373struct formatter;
374
375template <stdplus::Subnet Sub, typename CharT>
376struct formatter<Sub, CharT> : stdplus::Format<stdplus::ToStr<Sub>, CharT>
377{};
378} // namespace std