Coverage Report

Created: 2026-10-19 15:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/tmp/bitcoin/src/net_permissions.cpp
Line
Count
Source
1
// Copyright (c) 2009-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/messages.h>
6
#include <common/system.h>
7
#include <net_permissions.h>
8
#include <netbase.h>
9
#include <util/translation.h>
10
11
using common::ResolveErrMsg;
12
13
const std::vector<std::string> NET_PERMISSIONS_DOC{
14
    "bloomfilter (allow requesting BIP37 filtered blocks and transactions)",
15
    "noban (do not ban for misbehavior; implies download)",
16
    "forcerelay (relay transactions that are already in the mempool; implies relay)",
17
    "relay (relay even in -blocksonly mode, and unlimited transaction announcements)",
18
    "mempool (allow requesting BIP35 mempool contents)",
19
    "download (allow getheaders during IBD, no disconnect after maxuploadtarget limit)",
20
    "addr (responses to GETADDR avoid hitting the cache and contain random records with the most up-to-date info)"
21
};
22
23
namespace {
24
25
// Parse the following format: "perm1,perm2@xxxxxx"
26
static bool TryParsePermissionFlags(const std::string& str, NetPermissionFlags& output, ConnectionDirection* output_connection_direction, size_t& readen, bilingual_str& error)
27
221
{
28
221
    NetPermissionFlags flags = NetPermissionFlags::None;
29
221
    ConnectionDirection connection_direction = ConnectionDirection::None;
30
221
    const auto atSeparator = str.find('@');
31
32
    // if '@' is not found (ie, "xxxxx"), the caller should apply implicit permissions
33
221
    if (atSeparator == std::string::npos) {
34
16
        NetPermissions::AddFlag(flags, NetPermissionFlags::Implicit);
35
16
        readen = 0;
36
16
    }
37
    // else (ie, "perm1,perm2@xxxxx"), let's enumerate the permissions by splitting by ',' and calculate the flags
38
205
    else {
39
205
        readen = 0;
40
        // permissions == perm1,perm2
41
205
        const auto permissions = str.substr(0, atSeparator);
42
726
        while (readen < permissions.length()) {
43
524
            const auto commaSeparator = permissions.find(',', readen);
44
524
            const auto len = commaSeparator == std::string::npos ? permissions.length() - readen : commaSeparator - readen;
45
            // permission == perm1
46
524
            const auto permission = permissions.substr(readen, len);
47
524
            readen += len; // We read "perm1"
48
524
            if (commaSeparator != std::string::npos) readen++; // We read ","
49
50
524
            if (permission == "bloomfilter" || permission == "bloom") NetPermissions::AddFlag(flags, NetPermissionFlags::BloomFilter);
51
510
            else if (permission == "noban") NetPermissions::AddFlag(flags, NetPermissionFlags::NoBan);
52
332
            else if (permission == "forcerelay") NetPermissions::AddFlag(flags, NetPermissionFlags::ForceRelay);
53
322
            else if (permission == "mempool") NetPermissions::AddFlag(flags, NetPermissionFlags::Mempool);
54
319
            else if (permission == "download") NetPermissions::AddFlag(flags, NetPermissionFlags::Download);
55
315
            else if (permission == "all") NetPermissions::AddFlag(flags, NetPermissionFlags::All);
56
313
            else if (permission == "relay") NetPermissions::AddFlag(flags, NetPermissionFlags::Relay);
57
305
            else if (permission == "addr") NetPermissions::AddFlag(flags, NetPermissionFlags::Addr);
58
300
            else if (permission == "in") connection_direction |= ConnectionDirection::In;
59
154
            else if (permission == "out") {
60
148
                if (output_connection_direction == nullptr) {
61
                    // Only NetWhitebindPermissions() should pass a nullptr.
62
1
                    error = _("whitebind may only be used for incoming connections (\"out\" was passed)");
63
1
                    return false;
64
1
                }
65
147
                connection_direction |= ConnectionDirection::Out;
66
147
            }
67
6
            else if (permission.length() == 0); // Allow empty entries
68
2
            else {
69
2
                error = strprintf(_("Invalid P2P permission: '%s'"), permission);
70
2
                return false;
71
2
            }
72
524
        }
73
202
        readen++;
74
202
    }
75
76
    // By default, whitelist only applies to incoming connections
77
218
    if (connection_direction == ConnectionDirection::None) {
78
70
        connection_direction = ConnectionDirection::In;
79
148
    } else if (flags == NetPermissionFlags::None) {
80
1
        error = strprintf(_("Only direction was set, no permissions: '%s'"), str);
81
1
        return false;
82
1
    }
83
84
217
    output = flags;
85
217
    if (output_connection_direction) *output_connection_direction = connection_direction;
86
217
    error = Untranslated("");
87
217
    return true;
88
218
}
89
90
}
91
92
std::vector<std::string> NetPermissions::ToStrings(NetPermissionFlags flags)
93
22.6k
{
94
22.6k
    std::vector<std::string> strings;
95
22.6k
    if (NetPermissions::HasFlag(flags, NetPermissionFlags::BloomFilter)) strings.emplace_back("bloomfilter");
96
22.6k
    if (NetPermissions::HasFlag(flags, NetPermissionFlags::NoBan)) strings.emplace_back("noban");
97
22.6k
    if (NetPermissions::HasFlag(flags, NetPermissionFlags::ForceRelay)) strings.emplace_back("forcerelay");
98
22.6k
    if (NetPermissions::HasFlag(flags, NetPermissionFlags::Relay)) strings.emplace_back("relay");
99
22.6k
    if (NetPermissions::HasFlag(flags, NetPermissionFlags::Mempool)) strings.emplace_back("mempool");
100
22.6k
    if (NetPermissions::HasFlag(flags, NetPermissionFlags::Download)) strings.emplace_back("download");
101
22.6k
    if (NetPermissions::HasFlag(flags, NetPermissionFlags::Addr)) strings.emplace_back("addr");
102
22.6k
    return strings;
103
22.6k
}
104
105
bool NetWhitebindPermissions::TryParse(const std::string& str, NetWhitebindPermissions& output, bilingual_str& error)
106
34
{
107
34
    NetPermissionFlags flags;
108
34
    size_t offset;
109
34
    if (!TryParsePermissionFlags(str, flags, /*output_connection_direction=*/nullptr, offset, error)) return false;
110
111
32
    const std::string strBind = str.substr(offset);
112
32
    const std::optional<CService> addrBind{Lookup(strBind, 0, false)};
113
32
    if (!addrBind.has_value()) {
114
3
        error = ResolveErrMsg("whitebind", strBind);
115
3
        return false;
116
3
    }
117
29
    if (addrBind.value().GetPort() == 0) {
118
1
        error = strprintf(_("Need to specify a port with -whitebind: '%s'"), strBind);
119
1
        return false;
120
1
    }
121
122
28
    output.m_flags = flags;
123
28
    output.m_service = addrBind.value();
124
28
    error = Untranslated("");
125
28
    return true;
126
29
}
127
128
bool NetWhitelistPermissions::TryParse(const std::string& str, NetWhitelistPermissions& output, ConnectionDirection& output_connection_direction, bilingual_str& error)
129
187
{
130
187
    NetPermissionFlags flags;
131
187
    size_t offset;
132
    // Only NetWhitebindPermissions should pass a nullptr for output_connection_direction.
133
187
    if (!TryParsePermissionFlags(str, flags, &output_connection_direction, offset, error)) return false;
134
135
185
    const std::string net = str.substr(offset);
136
185
    const CSubNet subnet{LookupSubNet(net)};
137
185
    if (!subnet.IsValid()) {
138
2
        error = strprintf(_("Invalid netmask specified in -whitelist: '%s'"), net);
139
2
        return false;
140
2
    }
141
142
183
    output.m_flags = flags;
143
183
    output.m_subnet = subnet;
144
183
    error = Untranslated("");
145
183
    return true;
146
185
}