Branch data Line data Source code
1 : : // Copyright (c) 2026-present The Bitcoin Core developers
2 : : // Distributed under the MIT software license, see the accompanying
3 : : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4 : :
5 : : #include <common/url.h>
6 : : #include <interfaces/ipc.h>
7 : : #include <util/strencodings.h>
8 : : #include <util/string.h>
9 : : #include <util/translation.h>
10 : :
11 : : #include <cstdint>
12 : : #include <string>
13 : : #include <string_view>
14 : : #include <utility>
15 : :
16 : : namespace {
17 : 17 : util::Result<int64_t> ParseIntOption(std::string_view name, std::string_view value, int64_t min_val, int64_t max_val)
18 : : {
19 [ + + + - ]: 21 : if (value.empty()) return util::Error{Untranslated(strprintf("Missing value for %s option", name))};
20 : 15 : const auto parsed{ToIntegral<int64_t>(value)};
21 [ + + + - ]: 17 : if (!parsed) return util::Error{Untranslated(strprintf("Invalid %s value '%s'", name, value))};
22 [ + + + - ]: 20 : if (*parsed < min_val) return util::Error{Untranslated(strprintf("%s must be at least %d", name, min_val))};
23 [ + + + - ]: 13 : if (*parsed > max_val) return util::Error{Untranslated(strprintf("%s must be at most %d", name, max_val))};
24 : 10 : return *parsed;
25 : : }
26 : : } // namespace
27 : :
28 : : namespace interfaces {
29 : 21 : util::Result<ipc::ListenAddress> Ipc::parseListenAddress(std::string address)
30 : : {
31 : 21 : constexpr std::string_view UNIX_PREFIX{"unix:"};
32 : :
33 : : // Only Unix socket -ipcbind values support inline socket options. Leave
34 : : // other address families untouched so ipc::Process can report address
35 : : // scheme errors consistently with -ipcconnect.
36 [ - + + + ]: 21 : if (!address.starts_with(UNIX_PREFIX)) {
37 : 1 : return ipc::ListenAddress{.address = std::move(address)};
38 : : }
39 : :
40 : : // Socket options follow the first comma:
41 : : // unix:,max-connections=8
42 : : // unix:/custom/path,max-connections=8
43 : : // Commas in socket paths must be URL-encoded as %2C.
44 : : //
45 : : // Subsequent commas separate individual options.
46 : 20 : const size_t option_pos{address.find(',')};
47 [ + + ]: 20 : if (option_pos == std::string::npos) {
48 [ - + ]: 2 : return ipc::ListenAddress{.address = UrlDecode(address)};
49 : : }
50 : :
51 [ - + ]: 18 : const std::string_view address_view{address};
52 : :
53 : 18 : ipc::ListenAddress listen_address{
54 : : .address = UrlDecode(address_view.substr(0, option_pos)),
55 : 18 : };
56 : :
57 [ + - + + ]: 46 : for (const std::string& option_str : util::SplitString(address_view.substr(option_pos + 1), ',')) {
58 [ + + ]: 22 : if (option_str.empty()) {
59 [ + - + - ]: 9 : return util::Error{Untranslated("Empty socket option")};
60 : : }
61 : 19 : const size_t eq{option_str.find('=')};
62 [ - + + - ]: 19 : const std::string_view name{std::string_view{option_str}.substr(0, eq)};
63 [ + + + - ]: 37 : const std::string_view value{eq == std::string::npos ? std::string_view{} : std::string_view{option_str}.substr(eq + 1)};
64 [ + + ]: 19 : if (name == "max-connections") {
65 [ + - ]: 17 : auto parsed{ParseIntOption(name, value, 1, static_cast<int64_t>(ipc::MAX_CONNECTIONS))};
66 [ + + + - ]: 31 : if (!parsed) return util::Error{util::ErrorString(parsed)};
67 : 10 : listen_address.max_connections = static_cast<size_t>(*parsed);
68 : 17 : } else {
69 [ + - + - ]: 6 : return util::Error{Untranslated(strprintf("Unknown socket option '%s'", name))};
70 : : }
71 : 18 : }
72 : :
73 : 6 : return listen_address;
74 : 18 : }
75 : : } // namespace interfaces
|