Coverage Report

Created: 2026-04-29 19:21

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
180
{
28
180
    NetPermissionFlags flags = NetPermissionFlags::None;
29
180
    ConnectionDirection connection_direction = ConnectionDirection::None;
30
180
    const auto atSeparator = str.find('@');
31
32
    // if '@' is not found (ie, "xxxxx"), the caller should apply implicit permissions
33
180
    if (atSeparator == std::string::npos) {
34
9
        NetPermissions::AddFlag(flags, NetPermissionFlags::Implicit);
35
9
        readen = 0;
36
9
    }
37
    // else (ie, "perm1,perm2@xxxxx"), let's enumerate the permissions by splitting by ',' and calculate the flags
38
171
    else {
39
171
        readen = 0;
40
        // permissions == perm1,perm2
41
171
        const auto permissions = str.substr(0, atSeparator);
42
590
        while (readen < permissions.length()) {
43
422
            const auto commaSeparator = permissions.find(',', readen);
44
422
            const auto len = commaSeparator == std::string::npos ? permissions.length() - readen : commaSeparator - readen;
45
            // permission == perm1
46
422
            const auto permission = permissions.substr(readen, len);
47
422
            readen += len; // We read "perm1"
48
422
            if (commaSeparator != std::string::npos) readen++; // We read ","
49
50
422
            if (permission == "bloomfilter" || permission == "bloom") NetPermissions::AddFlag(flags, NetPermissionFlags::BloomFilter);
51
408
            else if (permission == "noban") NetPermissions::AddFlag(flags, NetPermissionFlags::NoBan);
52
264
            else if (permission == "forcerelay") NetPermissions::AddFlag(flags, NetPermissionFlags::ForceRelay);
53
254
            else if (permission == "mempool") NetPermissions::AddFlag(flags, NetPermissionFlags::Mempool);
54
251
            else if (permission == "download") NetPermissions::AddFlag(flags, NetPermissionFlags::Download);
55
247
            else if (permission == "all") NetPermissions::AddFlag(flags, NetPermissionFlags::All);
56
245
            else if (permission == "relay") NetPermissions::AddFlag(flags, NetPermissionFlags::Relay);
57
237
            else if (permission == "addr") NetPermissions::AddFlag(flags, NetPermissionFlags::Addr);
58
232
            else if (permission == "in") connection_direction |= ConnectionDirection::In;
59
120
            else if (permission == "out") {
60
114
                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
113
                connection_direction |= ConnectionDirection::Out;
66
113
            }
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
422
        }
73
168
        readen++;
74
168
    }
75
76
    // By default, whitelist only applies to incoming connections
77
177
    if (connection_direction == ConnectionDirection::None) {
78
63
        connection_direction = ConnectionDirection::In;
79
114
    } 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
176
    output = flags;
85
176
    if (output_connection_direction) *output_connection_direction = connection_direction;
86
176
    error = Untranslated("");
87
176
    return true;
88
177
}
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
27
{
107
27
    NetPermissionFlags flags;
108
27
    size_t offset;
109
27
    if (!TryParsePermissionFlags(str, flags, /*output_connection_direction=*/nullptr, offset, error)) return false;
110
111
25
    const std::string strBind = str.substr(offset);
112
25
    const std::optional<CService> addrBind{Lookup(strBind, 0, false)};
113
25
    if (!addrBind.has_value()) {
114
3
        error = ResolveErrMsg("whitebind", strBind);
115
3
        return false;
116
3
    }
117
22
    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
21
    output.m_flags = flags;
123
21
    output.m_service = addrBind.value();
124
21
    error = Untranslated("");
125
21
    return true;
126
22
}
127
128
bool NetWhitelistPermissions::TryParse(const std::string& str, NetWhitelistPermissions& output, ConnectionDirection& output_connection_direction, bilingual_str& error)
129
153
{
130
153
    NetPermissionFlags flags;
131
153
    size_t offset;
132
    // Only NetWhitebindPermissions should pass a nullptr for output_connection_direction.
133
153
    if (!TryParsePermissionFlags(str, flags, &output_connection_direction, offset, error)) return false;
134
135
151
    const std::string net = str.substr(offset);
136
151
    const CSubNet subnet{LookupSubNet(net)};
137
151
    if (!subnet.IsValid()) {
138
2
        error = strprintf(_("Invalid netmask specified in -whitelist: '%s'"), net);
139
2
        return false;
140
2
    }
141
142
149
    output.m_flags = flags;
143
149
    output.m_subnet = subnet;
144
149
    error = Untranslated("");
145
149
    return true;
146
151
}