Skip to content

Commit

Permalink
Make whitebind/whitelist permissions more flexible
Browse files Browse the repository at this point in the history
  • Loading branch information
NicolasDorier committed Aug 11, 2019
1 parent e5fdda6 commit e5b26de
Show file tree
Hide file tree
Showing 8 changed files with 321 additions and 43 deletions.
2 changes: 2 additions & 0 deletions src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ BITCOIN_CORE_H = \
merkleblock.h \
miner.h \
net.h \
net_permissions.h \
net_processing.h \
netaddress.h \
netbase.h \
Expand Down Expand Up @@ -454,6 +455,7 @@ libbitcoin_common_a_SOURCES = \
merkleblock.cpp \
netaddress.cpp \
netbase.cpp \
net_permissions.cpp \
outputtype.cpp \
policy/feerate.cpp \
policy/policy.cpp \
Expand Down
20 changes: 8 additions & 12 deletions src/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include <key.h>
#include <miner.h>
#include <net.h>
#include <net_permissions.h>
#include <net_processing.h>
#include <netbase.h>
#include <policy/feerate.h>
Expand Down Expand Up @@ -1775,21 +1776,16 @@ bool AppInitMain(InitInterfaces& interfaces)
connOptions.vBinds.push_back(addrBind);
}
for (const std::string& strBind : gArgs.GetArgs("-whitebind")) {
CService addrBind;
if (!Lookup(strBind.c_str(), addrBind, 0, false)) {
return InitError(ResolveErrMsg("whitebind", strBind));
}
if (addrBind.GetPort() == 0) {
return InitError(strprintf(_("Need to specify a port with -whitebind: '%s'").translated, strBind));
}
connOptions.vWhiteBinds.push_back(addrBind);
NetWhitebindPermissions whitebind;
std::string error;
if (!NetWhitebindPermissions::TryParse(strBind, whitebind, error)) return InitError(error);
connOptions.vWhiteBinds.push_back(whitebind);
}

for (const auto& net : gArgs.GetArgs("-whitelist")) {
CSubNet subnet;
LookupSubNet(net.c_str(), subnet);
if (!subnet.IsValid())
return InitError(strprintf(_("Invalid netmask specified in -whitelist: '%s'").translated, net));
NetWhitelistPermissions subnet;
std::string error;
if (!NetWhitelistPermissions::TryParse(net, subnet, error)) return InitError(error);
connOptions.vWhitelistedRange.push_back(subnet);
}

Expand Down
59 changes: 39 additions & 20 deletions src/net.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <crypto/common.h>
#include <crypto/sha256.h>
#include <netbase.h>
#include <net_permissions.h>
#include <primitives/transaction.h>
#include <scheduler.h>
#include <ui_interface.h>
Expand Down Expand Up @@ -67,7 +68,6 @@ enum BindFlags {
BF_NONE = 0,
BF_EXPLICIT = (1U << 0),
BF_REPORT_ERROR = (1U << 1),
BF_WHITELIST = (1U << 2),
};

// The set of sockets cannot be modified while waiting
Expand Down Expand Up @@ -459,12 +459,10 @@ void CNode::CloseSocketDisconnect()
}
}

bool CConnman::IsWhitelistedRange(const CNetAddr &addr) {
for (const CSubNet& subnet : vWhitelistedRange) {
if (subnet.Match(addr))
return true;
void CConnman::AddWhitelistPermissionFlags(NetPermissionFlags& flags, const CNetAddr &addr) const {
for (const auto& subnet : vWhitelistedRange) {
if (subnet.m_subnet.Match(addr)) NetPermissions::AddFlag(flags, subnet.m_flags);
}
return false;
}

std::string CNode::GetAddrName() const {
Expand Down Expand Up @@ -529,6 +527,7 @@ void CNode::copyStats(CNodeStats &stats)
X(nRecvBytes);
}
X(fWhitelisted);
X(m_permissionFlags);
{
LOCK(cs_feeFilter);
X(minFeeFilter);
Expand Down Expand Up @@ -904,7 +903,20 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) {
}
}

bool whitelisted = hListenSocket.whitelisted || IsWhitelistedRange(addr);
NetPermissionFlags permissionFlags = NetPermissionFlags::PF_NONE;
hListenSocket.AddSocketPermissionFlags(permissionFlags);
AddWhitelistPermissionFlags(permissionFlags, addr);
const bool noban = NetPermissions::HasFlag(permissionFlags, NetPermissionFlags::PF_NOBAN);
bool legacyWhitelisted = false;
if (NetPermissions::HasFlag(permissionFlags, NetPermissionFlags::PF_ISIMPLICIT)) {
NetPermissions::ClearFlag(permissionFlags, PF_ISIMPLICIT);
if (gArgs.GetBoolArg("-whitelistforcerelay", false)) NetPermissions::AddFlag(permissionFlags, PF_FORCERELAY);
if (gArgs.GetBoolArg("-whitelistrelay", false)) NetPermissions::AddFlag(permissionFlags, PF_RELAY);
NetPermissions::AddFlag(permissionFlags, PF_MEMPOOL);
NetPermissions::AddFlag(permissionFlags, PF_NOBAN);
legacyWhitelisted = true;
}

{
LOCK(cs_vNodes);
for (const CNode* pnode : vNodes) {
Expand Down Expand Up @@ -941,7 +953,7 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) {

// Don't accept connections from banned peers, but if our inbound slots aren't almost full, accept
// if the only banning reason was an automatic misbehavior ban.
if (!whitelisted && bannedlevel > ((nInbound + 1 < nMaxInbound) ? 1 : 0))
if (!noban && bannedlevel > ((nInbound + 1 < nMaxInbound) ? 1 : 0))
{
LogPrint(BCLog::NET, "connection from %s dropped (banned)\n", addr.ToString());
CloseSocket(hSocket);
Expand All @@ -962,9 +974,15 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) {
uint64_t nonce = GetDeterministicRandomizer(RANDOMIZER_ID_LOCALHOSTNONCE).Write(id).Finalize();
CAddress addr_bind = GetBindAddress(hSocket);

CNode* pnode = new CNode(id, nLocalServices, GetBestHeight(), hSocket, addr, CalculateKeyedNetGroup(addr), nonce, addr_bind, "", true);
ServiceFlags nodeServices = nLocalServices;
if (NetPermissions::HasFlag(permissionFlags, PF_BLOOMFILTER)) {
nodeServices = static_cast<ServiceFlags>(nodeServices | NODE_BLOOM);
}
CNode* pnode = new CNode(id, nodeServices, GetBestHeight(), hSocket, addr, CalculateKeyedNetGroup(addr), nonce, addr_bind, "", true);
pnode->AddRef();
pnode->fWhitelisted = whitelisted;
pnode->m_permissionFlags = permissionFlags;
// If this flag is present, the user probably expect that RPC and QT report it as whitelisted (backward compatibility)
pnode->fWhitelisted = legacyWhitelisted;
pnode->m_prefer_evict = bannedlevel > 0;
m_msgproc->InitializeNode(pnode);

Expand Down Expand Up @@ -1983,7 +2001,7 @@ void CConnman::ThreadMessageHandler()



bool CConnman::BindListenPort(const CService &addrBind, std::string& strError, bool fWhitelisted)
bool CConnman::BindListenPort(const CService& addrBind, std::string& strError, NetPermissionFlags permissions)
{
strError = "";
int nOne = 1;
Expand Down Expand Up @@ -2044,9 +2062,9 @@ bool CConnman::BindListenPort(const CService &addrBind, std::string& strError, b
return false;
}

vhListenSocket.push_back(ListenSocket(hListenSocket, fWhitelisted));
vhListenSocket.push_back(ListenSocket(hListenSocket, permissions));

if (addrBind.IsRoutable() && fDiscover && !fWhitelisted)
if (addrBind.IsRoutable() && fDiscover && (permissions & PF_NOBAN) == 0)
AddLocal(addrBind, LOCAL_BIND);

return true;
Expand Down Expand Up @@ -2130,11 +2148,11 @@ NodeId CConnman::GetNewNodeId()
}


bool CConnman::Bind(const CService &addr, unsigned int flags) {
bool CConnman::Bind(const CService &addr, unsigned int flags, NetPermissionFlags permissions) {
if (!(flags & BF_EXPLICIT) && !IsReachable(addr))
return false;
std::string strError;
if (!BindListenPort(addr, strError, (flags & BF_WHITELIST) != 0)) {
if (!BindListenPort(addr, strError, permissions)) {
if ((flags & BF_REPORT_ERROR) && clientInterface) {
clientInterface->ThreadSafeMessageBox(strError, "", CClientUIInterface::MSG_ERROR);
}
Expand All @@ -2143,20 +2161,21 @@ bool CConnman::Bind(const CService &addr, unsigned int flags) {
return true;
}

bool CConnman::InitBinds(const std::vector<CService>& binds, const std::vector<CService>& whiteBinds) {
bool CConnman::InitBinds(const std::vector<CService>& binds, const std::vector<NetWhitebindPermissions>& whiteBinds)
{
bool fBound = false;
for (const auto& addrBind : binds) {
fBound |= Bind(addrBind, (BF_EXPLICIT | BF_REPORT_ERROR));
fBound |= Bind(addrBind, (BF_EXPLICIT | BF_REPORT_ERROR), NetPermissionFlags::PF_NONE);
}
for (const auto& addrBind : whiteBinds) {
fBound |= Bind(addrBind, (BF_EXPLICIT | BF_REPORT_ERROR | BF_WHITELIST));
fBound |= Bind(addrBind.m_service, (BF_EXPLICIT | BF_REPORT_ERROR), addrBind.m_flags);
}
if (binds.empty() && whiteBinds.empty()) {
struct in_addr inaddr_any;
inaddr_any.s_addr = INADDR_ANY;
struct in6_addr inaddr6_any = IN6ADDR_ANY_INIT;
fBound |= Bind(CService(inaddr6_any, GetListenPort()), BF_NONE);
fBound |= Bind(CService(inaddr_any, GetListenPort()), !fBound ? BF_REPORT_ERROR : BF_NONE);
fBound |= Bind(CService(inaddr6_any, GetListenPort()), BF_NONE, NetPermissionFlags::PF_NONE);
fBound |= Bind(CService(inaddr_any, GetListenPort()), !fBound ? BF_REPORT_ERROR : BF_NONE, NetPermissionFlags::PF_NONE);
}
return fBound;
}
Expand Down
30 changes: 19 additions & 11 deletions src/net.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include <hash.h>
#include <limitedmap.h>
#include <netaddress.h>
#include <net_permissions.h>
#include <policy/feerate.h>
#include <protocol.h>
#include <random.h>
Expand Down Expand Up @@ -138,8 +139,9 @@ class CConnman
uint64_t nMaxOutboundLimit = 0;
int64_t m_peer_connect_timeout = DEFAULT_PEER_CONNECT_TIMEOUT;
std::vector<std::string> vSeedNodes;
std::vector<CSubNet> vWhitelistedRange;
std::vector<CService> vBinds, vWhiteBinds;
std::vector<NetWhitelistPermissions> vWhitelistedRange;
std::vector<NetWhitebindPermissions> vWhiteBinds;
std::vector<CService> vBinds;
bool m_use_addrman_outgoing = true;
std::vector<std::string> m_specified_outgoing;
std::vector<std::string> m_added_nodes;
Expand Down Expand Up @@ -314,15 +316,17 @@ class CConnman

private:
struct ListenSocket {
public:
SOCKET socket;
bool whitelisted;

ListenSocket(SOCKET socket_, bool whitelisted_) : socket(socket_), whitelisted(whitelisted_) {}
inline void AddSocketPermissionFlags(NetPermissionFlags& flags) const { NetPermissions::AddFlag(flags, m_permissions); }
ListenSocket(SOCKET socket_, NetPermissionFlags permissions_) : socket(socket_), m_permissions(permissions_) {}
private:
NetPermissionFlags m_permissions;
};

bool BindListenPort(const CService &bindAddr, std::string& strError, bool fWhitelisted = false);
bool Bind(const CService &addr, unsigned int flags);
bool InitBinds(const std::vector<CService>& binds, const std::vector<CService>& whiteBinds);
bool BindListenPort(const CService& bindAddr, std::string& strError, NetPermissionFlags permissions);
bool Bind(const CService& addr, unsigned int flags, NetPermissionFlags permissions);
bool InitBinds(const std::vector<CService>& binds, const std::vector<NetWhitebindPermissions>& whiteBinds);
void ThreadOpenAddedConnections();
void AddOneShot(const std::string& strDest);
void ProcessOneShot();
Expand All @@ -347,7 +351,7 @@ class CConnman

bool AttemptToEvictConnection();
CNode* ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure, bool manual_connection);
bool IsWhitelistedRange(const CNetAddr &addr);
void AddWhitelistPermissionFlags(NetPermissionFlags& flags, const CNetAddr &addr) const;

void DeleteNode(CNode* pnode);

Expand Down Expand Up @@ -380,7 +384,7 @@ class CConnman

// Whitelisted ranges. Any node connecting from these is automatically
// whitelisted (as well as those connecting to whitelisted binds).
std::vector<CSubNet> vWhitelistedRange;
std::vector<NetWhitelistPermissions> vWhitelistedRange;

unsigned int nSendBufferMaxSize{0};
unsigned int nReceiveFloodSize{0};
Expand Down Expand Up @@ -448,7 +452,6 @@ void StartMapPort();
void InterruptMapPort();
void StopMapPort();
unsigned short GetListenPort();
bool BindListenPort(const CService &bindAddr, std::string& strError, bool fWhitelisted = false);

struct CombinerAll
{
Expand Down Expand Up @@ -555,6 +558,7 @@ class CNodeStats
mapMsgCmdSize mapSendBytesPerMsgCmd;
uint64_t nRecvBytes;
mapMsgCmdSize mapRecvBytesPerMsgCmd;
NetPermissionFlags m_permissionFlags;
bool fWhitelisted;
double dPingTime;
double dPingWait;
Expand Down Expand Up @@ -657,6 +661,9 @@ class CNode
*/
std::string cleanSubVer GUARDED_BY(cs_SubVer){};
bool m_prefer_evict{false}; // This peer is preferred for eviction.
bool HasPermission(NetPermissionFlags permission) const {
return NetPermissions::HasFlag(m_permissionFlags, permission);
}
bool fWhitelisted{false}; // This peer can bypass DoS banning.
bool fFeeler{false}; // If true this node is being used as a short lived feeler.
bool fOneShot{false};
Expand Down Expand Up @@ -753,6 +760,7 @@ class CNode
const ServiceFlags nLocalServices;
const int nMyStartingHeight;
int nSendVersion{0};
NetPermissionFlags m_permissionFlags{ PF_NONE };
std::list<CNetMessage> vRecvMsg; // Used only by SocketHandler thread

mutable CCriticalSection cs_addrName;
Expand Down
Loading

0 comments on commit e5b26de

Please sign in to comment.