• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

libbitcoin / libbitcoin-system / 15655570685

14 Jun 2025 07:53PM UTC coverage: 81.172% (+0.08%) from 81.096%
15655570685

push

github

web-flow
Merge pull request #1701 from evoskuil/master

Move authority and endpoint config classes from network.

177 of 206 new or added lines in 5 files covered. (85.92%)

10610 of 13071 relevant lines covered (81.17%)

3626848.88 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

86.32
/src/config/utilities.cpp
1
/**
2
 * Copyright (c) 2011-2025 libbitcoin developers (see AUTHORS)
3
 *
4
 * This file is part of libbitcoin.
5
 *
6
 * This program is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU Affero General Public License as published by
8
 * the Free Software Foundation, either version 3 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU Affero General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Affero General Public License
17
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18
 */
19
#include <bitcoin/system/config/utilities.hpp>
20

21
#include <regex>
22
#include <bitcoin/system/define.hpp>
23
#include <bitcoin/system/serial/serial.hpp>
24

25
namespace libbitcoin {
26
namespace system {
27
namespace config {
28

29
static_assert(array_count<boost::asio::ip::address_v4::bytes_type> == ipv4_size);
30
static_assert(array_count<boost::asio::ip::address_v6::bytes_type> == ipv6_size);
31

32
// Because deserialize doesn't convert empty to zero.
33
template <typename Integer>
34
inline bool to_integer(Integer& out, const std::string& in) NOEXCEPT
310✔
35
{
36
    if (in.empty())
310✔
37
    {
38
        out = Integer{};
140✔
39
        return true;
140✔
40
    }
41

42
    return deserialize(out, in);
170✔
43
}
44

45
// For calling consistency.
46
inline bool to_string(std::string& to, std::string&& from) NOEXCEPT
68✔
47
{
48
    to = std::move(from);
68✔
49
    return true;
68✔
50
}
51

52
// ASIO make_address allows a port on win32 (which is then lost), so guard in
53
// regex. ASIO addresses do not have ports, that's what endpoints are for.
54
inline bool make_address(asio::address& ip, const std::string& host) NOEXCEPT
147✔
55
{
56
    try
147✔
57
    {
58
        // Regex extracts literal host, non-win32 boost make_address rejects.
59
        ip = boost::asio::ip::make_address(trim_copy(host, { "[", "]" }));
294✔
60
        return true;
145✔
61
    }
62
    catch (const std::exception&)
2✔
63
    {
64
        return false;
2✔
65
    }
2✔
66
}
67

68
// regex parsers.
69
// ----------------------------------------------------------------------------
70

71
// en.wikipedia.org/wiki/List_of_URI_schemes
72
// Schemes of p2p network and our zeromq endpoints.
73
#define SCHEME "(tcp|udp|http|https|inproc):\\/\\/"
74
#define IPV4   "([0-9.]+)"
75
#define IPV6   "\\[([0-9a-f:]+)\\]"
76
#define HOST   "([^:?/\\\\]+)"
77
#define PORT   ":([1-9][0-9]{0,4})"
78
#define CIDR   "\\/([1-9][0-9]{0,2})"
79

80
// sregex_iterator doesn't provide `at()`.
81
BC_PUSH_WARNING(NO_ARRAY_INDEXING)
82
BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
83

84
// Excludes ipv4 mapped/compat, unbracketed, and ports allowed by make_address.
85
bool parse_host(asio::address& ip, const std::string& value) NOEXCEPT
14✔
86
{
87
    static const std::regex regular
14✔
88
    {
89
        "^(" IPV4 "|" IPV6 ")$"
90
    };
14✔
91

92
    std::sregex_iterator token{ value.begin(), value.end(), regular };
14✔
93
    std::sregex_iterator end{};
14✔
94
    return token != end
14✔
95
        && make_address(ip, (*token)[1].str());
20✔
96
}
97

98
// Excludes ipv4 mapped/compat to ipv6.
99
bool parse_authority(asio::address& ip, uint16_t& port, uint8_t& cidr,
158✔
100
    const std::string& value) NOEXCEPT
101
{
102
    constexpr uint8_t maximum_cidr_ip4 = 32;
158✔
103
    constexpr uint8_t maximum_cidr_ip6 = 128;
158✔
104
    static const std::regex regular
158✔
105
    {
106
        "^(" IPV4 "|" IPV6 ")(" PORT ")?(" CIDR ")?$"
107
    };
158✔
108

109
    std::sregex_iterator token{ value.begin(), value.end(), regular };
158✔
110
    std::sregex_iterator end{};
158✔
111
    return token != end
158✔
112
        && make_address(ip, (*token)[1].str())
299✔
113
        && to_integer(port, (*token)[5].str())
297✔
114
        && to_integer(cidr, (*token)[7].str())
432✔
115
        && ((ip.is_v4() && cidr <= maximum_cidr_ip4) ||
295✔
116
            (ip.is_v6() && cidr <= maximum_cidr_ip6));
67✔
117
}
118

119
// Excludes ipv4 mapped/compat to ipv6.
120
bool parse_endpoint(std::string& scheme, std::string& host, uint16_t& port,
46✔
121
    const std::string& value) NOEXCEPT
122
{
123
    static const std::regex regular
46✔
124
    {
125
        "^(" SCHEME ")?(" IPV4 "|" IPV6 "|" HOST ")(" PORT ")?$"
126
    };
46✔
127

128
    std::sregex_iterator token{ value.begin(), value.end(), regular };
46✔
129
    std::sregex_iterator end{};
46✔
130
    return token != end
46✔
131
        && to_string(scheme, (*token)[2].str())
80✔
132
        && to_string(host,   (*token)[3].str())
80✔
133
        && to_integer(port,  (*token)[8].str());
114✔
134
}
135

136
BC_POP_WARNING()
137
BC_POP_WARNING()
138

139
// asio/asio conversions.
140
// ----------------------------------------------------------------------------
141

142
inline bool is_embedded_v4(const boost::asio::ip::address_v6& ip6) NOEXCEPT
39✔
143
{
144
    BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
145
    // is_v4_compatible is deprecated, removed in 1.87, no replacement.
146
    return ip6.is_v4_mapped() || ip6.is_v4_compatible();
39✔
147
    BC_POP_WARNING()
148
}
149

150
// Convert IPv6-mapped to IPV4 (ensures consistent internal matching).
151
// Reduce 4 encodings (IPv6, IPv6-mapped, IPv6-compat, IPv4) to 2 (IPv6, IPv4).
152
asio::address denormalize(const asio::address& ip) NOEXCEPT
37✔
153
{
154
    if (ip.is_v6())
37✔
155
    {
156
        try
26✔
157
        {
158
            const auto ip6 = ip.to_v6();
26✔
159

160
            // to_v4 is deprecated, removed in 1.87, no replacement.
161
            if (is_embedded_v4(ip6)) return { ip6.to_v4() };
26✔
162
        }
NEW
163
        catch (const std::exception&)
×
164
        {
NEW
165
        }
×
166
    }
167

168
    return ip;
36✔
169
}
170

171
// asio/string host conversions.
172
// ----------------------------------------------------------------------------
173

174
inline std::string to_host(const boost::asio::ip::address_v6& ip6) NOEXCEPT
13✔
175
{
176
    try
13✔
177
    {
178
        BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
179
        return is_embedded_v4(ip6) ? to_host(ip6.to_v4()) : ip6.to_string();
13✔
180
        BC_POP_WARNING()
181
    }
NEW
182
    catch (const std::exception&)
×
183
    {
NEW
184
        return { "::" };
×
NEW
185
    }
×
186
}
187

188
inline std::string to_host(const boost::asio::ip::address_v4& ip4) NOEXCEPT
10✔
189
{
190
    try
10✔
191
    {
192
        return ip4.to_string();
10✔
193
    }
NEW
194
    catch (const std::exception&)
×
195
    {
NEW
196
        return { "0.0.0.0" };
×
NEW
197
    }
×
198
}
199

200
// Serialize to host denormal form (unmapped) without ipv6 backets.
201
std::string to_host(const asio::address& ip) NOEXCEPT
23✔
202
{
203
    try
23✔
204
    {
205
        const auto host = denormalize(ip);
23✔
206
        BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
207
        return host.is_v4() ? to_host(host.to_v4()) : to_host(host.to_v6());
23✔
208
        BC_POP_WARNING()
209
    }
NEW
210
    catch (const std::exception&)
×
211
    {
NEW
212
        return { "0.0.0.0" };
×
NEW
213
    }
×
214
}
215

216
// Serialize to host denormal form (unmapped) and with ipv6 backets.
217
std::string to_literal(const asio::address& ip) NOEXCEPT
17✔
218
{
219
    const auto host = to_host(ip);
17✔
220
    return (host.find(":") == std::string::npos) ? host : ("[" + host + "]");
27✔
221
}
222

223
// Rejects ipv6 mapped/compat to ipv4 and unbracketed ipv6.
224
asio::address from_host(const std::string& host) THROWS
14✔
225
{
226
    asio::address out{};
14✔
227
    if (!parse_host(out, host))
14✔
228
        throw istream_exception{ host };
8✔
229

230
    return out;
6✔
231
}
232

233
// Member if subnet addresses contain host.
234
// ----------------------------------------------------------------------------
235

236
inline bool is_member_v4(const boost::asio::ip::address_v4& ip4,
28✔
237
    const boost::asio::ip::address_v4& net4, uint8_t cidr)  THROWS
238
{
239
    const auto hosts = boost::asio::ip::make_network_v4(net4, cidr).hosts();
28✔
240
    return hosts.find(ip4) != hosts.end();
28✔
241
}
242

243
inline bool is_member_v6(const boost::asio::ip::address_v6& ip6,
43✔
244
    const boost::asio::ip::address_v6& net6, uint8_t cidr) THROWS
245
{
246
    const auto hosts = boost::asio::ip::make_network_v6(net6, cidr).hosts();
43✔
247
    return hosts.find(ip6) != hosts.end();
43✔
248
}
249

250
// This assumes host and/or subnet v4 address(s) unmapped.
251
bool is_member(const asio::address& ip, const asio::address& subnet,
71✔
252
    uint8_t cidr) NOEXCEPT
253
{
254
    try
71✔
255
    {
256
        if (ip.is_v4() && subnet.is_v4())
71✔
257
            return is_member_v4(ip.to_v4(), subnet.to_v4(), cidr);
28✔
258

259
        if (ip.is_v6() && subnet.is_v6())
43✔
260
            return is_member_v6(ip.to_v6(), subnet.to_v6(), cidr);
43✔
261
    }
NEW
262
    catch (const std::exception&)
×
263
    {
NEW
264
    }
×
265

266
    return false;
267
}
268

269
} // namespace config
270
} // namespace system
271
} // namespace libbitcoin
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc