Coverage Report

Created: 2026-09-14 20:36

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
197
{
28
197
    NetPermissionFlags flags = NetPermissionFlags::None;
29
197
    ConnectionDirection connection_direction = ConnectionDirection::None;
30
197
    const auto atSeparator = str.find('@');
31
32
    // if '@' is not found (ie, "xxxxx"), the caller should apply implicit permissions
33
197
    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
181
    else {
39
181
        readen = 0;
40
        // permissions == perm1,perm2
41
181
        const auto permissions = str.substr(0, atSeparator);
42
630
        while (readen < permissions.length()) {
43
452
            const auto commaSeparator = permissions.find(',', readen);
44
452
            const auto len = commaSeparator == std::string::npos ? permissions.length() - readen : commaSeparator - readen;
45
            // permission == perm1
46
452
            const auto permission = permissions.substr(readen, len);
47
452
            readen += len; // We read "perm1"
48
452
            if (commaSeparator != std::string::npos) readen++; // We read ","
49
50
452
            if (permission == "bloomfilter" || permission == "bloom") NetPermissions::AddFlag(flags, NetPermissionFlags::BloomFilter);
51
438
            else if (permission == "noban") NetPermissions::AddFlag(flags, NetPermissionFlags::NoBan);
52
284
            else if (permission == "forcerelay") NetPermissions::AddFlag(flags, NetPermissionFlags::ForceRelay);
53
274
            else if (permission == "mempool") NetPermissions::AddFlag(flags, NetPermissionFlags::Mempool);
54
271
            else if (permission == "download") NetPermissions::AddFlag(flags, NetPermissionFlags::Download);
55
267
            else if (permission == "all") NetPermissions::AddFlag(flags, NetPermissionFlags::All);
56
265
            else if (permission == "relay") NetPermissions::AddFlag(flags, NetPermissionFlags::Relay);
57
257
            else if (permission == "addr") NetPermissions::AddFlag(flags, NetPermissionFlags::Addr);
58
252
            else if (permission == "in") connection_direction |= ConnectionDirection::In;
59
130
            else if (permission == "out") {
60
124
                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
123
                connection_direction |= ConnectionDirection::Out;
66
123
            }
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
452
        }
73
178
        readen++;
74
178
    }
75
76
    // By default, whitelist only applies to incoming connections
77
194
    if (connection_direction == ConnectionDirection::None) {
78
70
        connection_direction = ConnectionDirection::In;
79
124
    } 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
193
    output = flags;
85
193
    if (output_connection_direction) *output_connection_direction = connection_direction;
86
193
    error = Untranslated("");
87
193
    return true;
88
194
}
89
90
}
91
92
std::vector<std::string> NetPermissions::ToStrings(NetPermissionFlags flags)
93
13.9k
{
94
13.9k
    std::vector<std::string> strings;
95
13.9k
    if (NetPermissions::HasFlag(flags, NetPermissionFlags::BloomFilter)) strings.emplace_back("bloomfilter");
96
13.9k
    if (NetPermissions::HasFlag(flags, NetPermissionFlags::NoBan)) strings.emplace_back("noban");
97
13.9k
    if (NetPermissions::HasFlag(flags, NetPermissionFlags::ForceRelay)) strings.emplace_back("forcerelay");
98
13.9k
    if (NetPermissions::HasFlag(flags, NetPermissionFlags::Relay)) strings.emplace_back("relay");
99
13.9k
    if (NetPermissions::HasFlag(flags, NetPermissionFlags::Mempool)) strings.emplace_back("mempool");
100
13.9k
    if (NetPermissions::HasFlag(flags, NetPermissionFlags::Download)) strings.emplace_back("download");
101
13.9k
    if (NetPermissions::HasFlag(flags, NetPermissionFlags::Addr)) strings.emplace_back("addr");
102
13.9k
    return strings;
103
13.9k
}
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
163
{
130
163
    NetPermissionFlags flags;
131
163
    size_t offset;
132
    // Only NetWhitebindPermissions should pass a nullptr for output_connection_direction.
133
163
    if (!TryParsePermissionFlags(str, flags, &output_connection_direction, offset, error)) return false;
134
135
161
    const std::string net = str.substr(offset);
136
161
    const CSubNet subnet{LookupSubNet(net)};
137
161
    if (!subnet.IsValid()) {
138
2
        error = strprintf(_("Invalid netmask specified in -whitelist: '%s'"), net);
139
2
        return false;
140
2
    }
141
142
159
    output.m_flags = flags;
143
159
    output.m_subnet = subnet;
144
159
    error = Untranslated("");
145
159
    return true;
146
161
}