Coverage Report

Created: 2026-10-07 13:27

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