Coverage Report

Created: 2026-09-02 14:16

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