Coverage Report

Created: 2025-04-14 16:24

/Users/mcomp/contrib/bitcoin/src/net_permissions.cpp
Line
Count
Source (jump to first uncovered line)
1
// Copyright (c) 2009-2021 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
0
{
28
0
    NetPermissionFlags flags = NetPermissionFlags::None;
29
0
    ConnectionDirection connection_direction = ConnectionDirection::None;
30
0
    const auto atSeparator = str.find('@');
31
32
    // if '@' is not found (ie, "xxxxx"), the caller should apply implicit permissions
33
0
    if (atSeparator == std::string::npos) {
34
0
        NetPermissions::AddFlag(flags, NetPermissionFlags::Implicit);
35
0
        readen = 0;
36
0
    }
37
    // else (ie, "perm1,perm2@xxxxx"), let's enumerate the permissions by splitting by ',' and calculate the flags
38
0
    else {
39
0
        readen = 0;
40
        // permissions == perm1,perm2
41
0
        const auto permissions = str.substr(0, atSeparator);
42
0
        while (readen < permissions.length()) {
43
0
            const auto commaSeparator = permissions.find(',', readen);
44
0
            const auto len = commaSeparator == std::string::npos ? permissions.length() - readen : commaSeparator - readen;
45
            // permission == perm1
46
0
            const auto permission = permissions.substr(readen, len);
47
0
            readen += len; // We read "perm1"
48
0
            if (commaSeparator != std::string::npos) readen++; // We read ","
49
50
0
            if (permission == "bloomfilter" || permission == "bloom") NetPermissions::AddFlag(flags, NetPermissionFlags::BloomFilter);
51
0
            else if (permission == "noban") NetPermissions::AddFlag(flags, NetPermissionFlags::NoBan);
52
0
            else if (permission == "forcerelay") NetPermissions::AddFlag(flags, NetPermissionFlags::ForceRelay);
53
0
            else if (permission == "mempool") NetPermissions::AddFlag(flags, NetPermissionFlags::Mempool);
54
0
            else if (permission == "download") NetPermissions::AddFlag(flags, NetPermissionFlags::Download);
55
0
            else if (permission == "all") NetPermissions::AddFlag(flags, NetPermissionFlags::All);
56
0
            else if (permission == "relay") NetPermissions::AddFlag(flags, NetPermissionFlags::Relay);
57
0
            else if (permission == "addr") NetPermissions::AddFlag(flags, NetPermissionFlags::Addr);
58
0
            else if (permission == "in") connection_direction |= ConnectionDirection::In;
59
0
            else if (permission == "out") {
60
0
                if (output_connection_direction == nullptr) {
61
                    // Only NetWhitebindPermissions() should pass a nullptr.
62
0
                    error = _("whitebind may only be used for incoming connections (\"out\" was passed)");
63
0
                    return false;
64
0
                }
65
0
                connection_direction |= ConnectionDirection::Out;
66
0
            }
67
0
            else if (permission.length() == 0); // Allow empty entries
68
0
            else {
69
0
                error = strprintf(_("Invalid P2P permission: '%s'"), permission);
70
0
                return false;
71
0
            }
72
0
        }
73
0
        readen++;
74
0
    }
75
76
    // By default, whitelist only applies to incoming connections
77
0
    if (connection_direction == ConnectionDirection::None) {
78
0
        connection_direction = ConnectionDirection::In;
79
0
    } else if (flags == NetPermissionFlags::None) {
80
0
        error = strprintf(_("Only direction was set, no permissions: '%s'"), str);
81
0
        return false;
82
0
    }
83
84
0
    output = flags;
85
0
    if (output_connection_direction) *output_connection_direction = connection_direction;
86
0
    error = Untranslated("");
87
0
    return true;
88
0
}
89
90
}
91
92
std::vector<std::string> NetPermissions::ToStrings(NetPermissionFlags flags)
93
0
{
94
0
    std::vector<std::string> strings;
95
0
    if (NetPermissions::HasFlag(flags, NetPermissionFlags::BloomFilter)) strings.emplace_back("bloomfilter");
96
0
    if (NetPermissions::HasFlag(flags, NetPermissionFlags::NoBan)) strings.emplace_back("noban");
97
0
    if (NetPermissions::HasFlag(flags, NetPermissionFlags::ForceRelay)) strings.emplace_back("forcerelay");
98
0
    if (NetPermissions::HasFlag(flags, NetPermissionFlags::Relay)) strings.emplace_back("relay");
99
0
    if (NetPermissions::HasFlag(flags, NetPermissionFlags::Mempool)) strings.emplace_back("mempool");
100
0
    if (NetPermissions::HasFlag(flags, NetPermissionFlags::Download)) strings.emplace_back("download");
101
0
    if (NetPermissions::HasFlag(flags, NetPermissionFlags::Addr)) strings.emplace_back("addr");
102
0
    return strings;
103
0
}
104
105
bool NetWhitebindPermissions::TryParse(const std::string& str, NetWhitebindPermissions& output, bilingual_str& error)
106
0
{
107
0
    NetPermissionFlags flags;
108
0
    size_t offset;
109
0
    if (!TryParsePermissionFlags(str, flags, /*output_connection_direction=*/nullptr, offset, error)) return false;
110
111
0
    const std::string strBind = str.substr(offset);
112
0
    const std::optional<CService> addrBind{Lookup(strBind, 0, false)};
113
0
    if (!addrBind.has_value()) {
114
0
        error = ResolveErrMsg("whitebind", strBind);
115
0
        return false;
116
0
    }
117
0
    if (addrBind.value().GetPort() == 0) {
118
0
        error = strprintf(_("Need to specify a port with -whitebind: '%s'"), strBind);
119
0
        return false;
120
0
    }
121
122
0
    output.m_flags = flags;
123
0
    output.m_service = addrBind.value();
124
0
    error = Untranslated("");
125
0
    return true;
126
0
}
127
128
bool NetWhitelistPermissions::TryParse(const std::string& str, NetWhitelistPermissions& output, ConnectionDirection& output_connection_direction, bilingual_str& error)
129
0
{
130
0
    NetPermissionFlags flags;
131
0
    size_t offset;
132
    // Only NetWhitebindPermissions should pass a nullptr for output_connection_direction.
133
0
    if (!TryParsePermissionFlags(str, flags, &output_connection_direction, offset, error)) return false;
134
135
0
    const std::string net = str.substr(offset);
136
0
    const CSubNet subnet{LookupSubNet(net)};
137
0
    if (!subnet.IsValid()) {
138
0
        error = strprintf(_("Invalid netmask specified in -whitelist: '%s'"), net);
139
0
        return false;
140
0
    }
141
142
0
    output.m_flags = flags;
143
0
    output.m_subnet = subnet;
144
0
    error = Untranslated("");
145
0
    return true;
146
0
}