Botan 3.12.0
Crypto and TLS for C&
ipv4_address.cpp
Go to the documentation of this file.
1/*
2* (C) 2026 Jack Lloyd
3*
4* Botan is released under the Simplified BSD License (see license.txt)
5*/
6
7#include <botan/ipv4_address.h>
8
9#include <botan/internal/fmt.h>
10#include <botan/internal/loadstor.h>
11#include <botan/internal/parsing.h>
12#include <bit>
13
14namespace Botan {
15
16//static
17std::optional<IPv4Address> IPv4Address::from_string(std::string_view str) {
18 if(auto ipv4 = string_to_ipv4(str)) {
19 return IPv4Address(*ipv4);
20 } else {
21 return {};
22 }
23}
24
25//static
27 BOTAN_ARG_CHECK(bits <= 32, "IPv4 netmask prefix length must be at most 32");
28 if(bits == 0) {
29 return IPv4Address(0);
30 }
31 return IPv4Address(0xFFFFFFFF << (32 - bits));
32}
33
34std::array<uint8_t, 4> IPv4Address::to_bytes() const {
35 std::array<uint8_t, 4> out{};
36 store_be(m_ip, out);
37 return out;
38}
39
40std::string IPv4Address::to_string() const {
41 return ipv4_to_string(m_ip);
42}
43
44std::optional<size_t> IPv4Address::prefix_length() const {
45 // A 32-bit mask m is a CIDR prefix iff (~m) + 1 is a power of two or zero,
46 // i.e. (~m) & (~m + 1) == 0. If so, the prefix length is the leading-one count.
47 const uint32_t inv = ~m_ip;
48 if((inv & (inv + 1)) != 0) {
49 return std::nullopt;
50 }
51 return std::countl_one(m_ip);
52}
53
55 m_address(address & IPv4Address::netmask(prefix_length)), m_prefix_length(static_cast<uint8_t>(prefix_length)) {
56 // IPv4Address::netmask validates prefix_length <= 32, so by this point
57 // the static_cast is in range.
58}
59
60//static
61std::optional<IPv4Subnet> IPv4Subnet::from_address_and_mask(uint32_t addr, uint32_t mask) {
62 std::array<uint8_t, 8> addr_and_mask{};
63 store_be(&addr_and_mask[0], addr); // NOLINT(*-container-data-pointer)
64 store_be(&addr_and_mask[4], mask);
65 return IPv4Subnet::from_address_and_mask(addr_and_mask);
66}
67
68//static
69std::optional<IPv4Subnet> IPv4Subnet::from_address_and_mask(std::span<const uint8_t, 8> addr_and_mask) {
70 const IPv4Address addr(load_be<uint32_t>(addr_and_mask.data(), 0));
71 const IPv4Address mask(load_be<uint32_t>(addr_and_mask.data(), 1));
72
73 if(const auto plen = mask.prefix_length()) {
74 return IPv4Subnet(addr, *plen);
75 } else {
76 return {};
77 }
78}
79
80//static
81std::optional<IPv4Subnet> IPv4Subnet::from_string(std::string_view str) {
82 const auto slash = str.find('/');
83 if(slash == std::string_view::npos) {
84 return std::nullopt;
85 }
86
87 auto addr = IPv4Address::from_string(str.substr(0, slash));
88 if(!addr.has_value()) {
89 return std::nullopt;
90 }
91
92 const auto plen_str = str.substr(slash + 1);
93 if(plen_str.empty() || plen_str.size() > 2) {
94 return std::nullopt;
95 }
96 size_t plen = 0;
97 for(const char c : plen_str) {
98 if(c < '0' || c > '9') {
99 return std::nullopt;
100 }
101 plen = plen * 10 + static_cast<size_t>(c - '0');
102 }
103 if(plen > 32) {
104 return std::nullopt;
105 }
106
107 return IPv4Subnet(*addr, plen);
108}
109
110bool IPv4Subnet::contains(const IPv4Address& ip) const {
111 return (ip & IPv4Address::netmask(m_prefix_length)) == m_address;
112}
113
114std::string IPv4Subnet::to_string() const {
115 return fmt("{}/{}", m_address.to_string(), static_cast<size_t>(m_prefix_length));
116}
117
118std::vector<uint8_t> IPv4Subnet::serialize() const {
119 std::vector<uint8_t> out;
120 if(is_host()) {
121 out.resize(4);
122 store_be(m_address.value(), out.data());
123 return out;
124 }
125 out.resize(8);
126 store_be(m_address.value(), out.data());
127 store_be(IPv4Address::netmask(m_prefix_length).value(), out.data() + 4);
128 return out;
129}
130
131} // namespace Botan
#define BOTAN_ARG_CHECK(expr, msg)
Definition assert.h:33
static std::optional< IPv4Address > from_string(std::string_view str)
std::optional< size_t > prefix_length() const
IPv4Address(uint32_t ip)
std::string to_string() const
Dotted-decimal form, e.g. "10.0.0.1".
std::array< uint8_t, 4 > to_bytes() const
The address as four bytes, network-byte-order.
static IPv4Address netmask(size_t bits)
static std::optional< IPv4Subnet > from_address_and_mask(std::span< const uint8_t, 8 > addr_and_mask)
std::vector< uint8_t > serialize() const
bool contains(const IPv4Address &ip) const
True iff ip falls within this subnet.
bool is_host() const
True iff prefix_length() == 32.
IPv4Subnet(IPv4Address address, size_t prefix_length)
size_t prefix_length() const
Prefix length in [0, 32].
static std::optional< IPv4Subnet > from_string(std::string_view str)
std::string to_string() const
CIDR-style "10.0.0.0/8".
const IPv4Address & address() const
The network address (host bits already zeroed).
std::string fmt(std::string_view format, const T &... args)
Definition fmt.h:53
std::optional< uint32_t > string_to_ipv4(std::string_view str)
Definition parsing.cpp:155
std::string ipv4_to_string(uint32_t ip)
Definition parsing.cpp:361
constexpr auto store_be(ParamTs &&... params)
Definition loadstor.h:745
constexpr auto load_be(ParamTs &&... params)
Definition loadstor.h:504