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 Jun 21, 2019
1 parent 23815ee commit c3fa8ca
Show file tree
Hide file tree
Showing 9 changed files with 359 additions and 50 deletions.
2 changes: 2 additions & 0 deletions src/Makefile.am
Expand Up @@ -151,6 +151,7 @@ BITCOIN_CORE_H = \
merkleblock.h \
miner.h \
net.h \
net_permissions.h \
net_processing.h \
netaddress.h \
netbase.h \
Expand Down Expand Up @@ -449,6 +450,7 @@ libbitcoin_common_a_SOURCES = \
merkleblock.cpp \
netaddress.cpp \
netbase.cpp \
net_permissions.cpp \
outputtype.cpp \
policy/feerate.cpp \
policy/policy.cpp \
Expand Down
24 changes: 11 additions & 13 deletions src/init.cpp
Expand Up @@ -29,6 +29,7 @@
#include <netbase.h>
#include <net.h>
#include <net_processing.h>
#include <net_permissions.h>
#include <policy/feerate.h>
#include <policy/fees.h>
#include <policy/policy.h>
Expand Down Expand Up @@ -1782,22 +1783,19 @@ 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'"), strBind));
}
connOptions.vWhiteBinds.push_back(addrBind);
CNetWhitebindPermissions whitebind;
std::string error;
if (!CNetWhitebindPermissions::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'"), net));
connOptions.vWhitelistedRange.push_back(subnet);
CNetWhitelistPermissions subnet;
std::string error;
if (!CNetWhitelistPermissions::TryParse(net, &subnet, &error))
return InitError(error);
connOptions.vWhitelistedRange.push_back(subnet);
}

connOptions.vSeedNodes = gArgs.GetArgs("-seednode");
Expand Down
69 changes: 48 additions & 21 deletions src/net.cpp
Expand Up @@ -17,6 +17,7 @@
#include <crypto/sha256.h>
#include <primitives/transaction.h>
#include <netbase.h>
#include <net_permissions.h>
#include <scheduler.h>
#include <ui_interface.h>
#include <util/strencodings.h>
Expand Down Expand Up @@ -62,8 +63,7 @@ static constexpr int DUMP_PEERS_INTERVAL = 15 * 60;
enum BindFlags {
BF_NONE = 0,
BF_EXPLICIT = (1U << 0),
BF_REPORT_ERROR = (1U << 1),
BF_WHITELIST = (1U << 2),
BF_REPORT_ERROR = (1U << 1)
};

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

bool CConnman::IsWhitelistedRange(const CNetAddr &addr) {
for (const CSubNet& subnet : vWhitelistedRange) {
if (subnet.Match(addr))
return true;
CNetPermissionFlags CConnman::GetWhitelistPermissionFlags(const CNetAddr &addr) {
CNetPermissionFlags flags = PF_NONE;
for (const auto& subnet : vWhitelistedRange) {
if (subnet.subnet.Match(addr))
{
flags = static_cast<CNetPermissionFlags>(flags | subnet.flags);
}
}
return false;
return flags;
}

std::string CNode::GetAddrName() const {
Expand Down Expand Up @@ -525,6 +528,7 @@ void CNode::copyStats(CNodeStats &stats)
X(nRecvBytes);
}
X(fWhitelisted);
X(permissionFlags);
{
LOCK(cs_feeFilter);
X(minFeeFilter);
Expand Down Expand Up @@ -900,7 +904,21 @@ void CConnman::AcceptConnection(const ListenSocket& hListenSocket) {
}
}

bool whitelisted = hListenSocket.whitelisted || IsWhitelistedRange(addr);
CNetPermissionFlags permissionFlags = hListenSocket.permissions;
permissionFlags = static_cast<CNetPermissionFlags>(permissionFlags | GetWhitelistPermissionFlags(addr));
bool noban = (permissionFlags & CNetPermissionFlags::PF_NOBAN) != 0;
bool allowLegacy = (permissionFlags & CNetPermissionFlags::PF_ISDEFAULT) != 0;
if (allowLegacy && gArgs.GetBoolArg("-whitelistforcerelay", false))
{
permissionFlags = static_cast<CNetPermissionFlags>(permissionFlags | PF_FORCERELAY);
}
if (allowLegacy && gArgs.GetBoolArg("-whitelistrelay", false))
{
permissionFlags = static_cast<CNetPermissionFlags>(permissionFlags | PF_RELAY);
}
// PF_ISDEFAULT is not useful for anything else, so we can unset the flag
permissionFlags = static_cast<CNetPermissionFlags>(permissionFlags & ~PF_ISDEFAULT);

{
LOCK(cs_vNodes);
for (const CNode* pnode : vNodes) {
Expand Down Expand Up @@ -937,7 +955,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 @@ -958,9 +976,17 @@ 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 ((permissionFlags & PF_BLOOMFILTER) != 0)
{
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->permissionFlags = permissionFlags;
// If the permission flags are a superset of the default flags, then we have the expected
// behavior of the old fWhitelisted
pnode->fWhitelisted = (PF_DEFAULT & permissionFlags) == PF_DEFAULT;
pnode->m_prefer_evict = bannedlevel > 0;
m_msgproc->InitializeNode(pnode);

Expand Down Expand Up @@ -1996,7 +2022,7 @@ void CConnman::ThreadMessageHandler()



bool CConnman::BindListenPort(const CService &addrBind, std::string& strError, bool fWhitelisted)
bool CConnman::BindListenPort(const CService& addrBind, std::string& strError, CNetPermissionFlags permissions)
{
strError = "";
int nOne = 1;
Expand Down Expand Up @@ -2057,9 +2083,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 @@ -2143,11 +2169,11 @@ NodeId CConnman::GetNewNodeId()
}


bool CConnman::Bind(const CService &addr, unsigned int flags) {
bool CConnman::Bind(const CService &addr, unsigned int flags, CNetPermissionFlags 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 @@ -2156,20 +2182,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<CNetWhitebindPermissions>& 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), CNetPermissionFlags::PF_NONE);
}
for (const auto& addrBind : whiteBinds) {
fBound |= Bind(addrBind, (BF_EXPLICIT | BF_REPORT_ERROR | BF_WHITELIST));
fBound |= Bind(addrBind.service, (BF_EXPLICIT | BF_REPORT_ERROR), addrBind.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, CNetPermissionFlags::PF_NONE);
fBound |= Bind(CService(inaddr_any, GetListenPort()), !fBound ? BF_REPORT_ERROR : BF_NONE, CNetPermissionFlags::PF_NONE);
}
return fBound;
}
Expand Down
26 changes: 16 additions & 10 deletions src/net.h
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<CNetWhitelistPermissions> vWhitelistedRange;
std::vector<CNetWhitebindPermissions> 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 @@ -315,14 +317,14 @@ class CConnman
private:
struct ListenSocket {
SOCKET socket;
bool whitelisted;
CNetPermissionFlags permissions;

ListenSocket(SOCKET socket_, bool whitelisted_) : socket(socket_), whitelisted(whitelisted_) {}
ListenSocket(SOCKET socket_, CNetPermissionFlags permissions_) : socket(socket_), permissions(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, CNetPermissionFlags permissions);
bool Bind(const CService& addr, unsigned int flags, CNetPermissionFlags permissions);
bool InitBinds(const std::vector<CService>& binds, const std::vector<CNetWhitebindPermissions>& whiteBinds);
void ThreadOpenAddedConnections();
void AddOneShot(const std::string& strDest);
void ProcessOneShot();
Expand All @@ -347,7 +349,7 @@ class CConnman

bool AttemptToEvictConnection();
CNode* ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure, bool manual_connection);
bool IsWhitelistedRange(const CNetAddr &addr);
CNetPermissionFlags GetWhitelistPermissionFlags(const CNetAddr &addr);

void DeleteNode(CNode* pnode);

Expand Down Expand Up @@ -380,7 +382,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<CNetWhitelistPermissions> vWhitelistedRange;

unsigned int nSendBufferMaxSize{0};
unsigned int nReceiveFloodSize{0};
Expand Down Expand Up @@ -448,7 +450,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 +556,7 @@ class CNodeStats
mapMsgCmdSize mapSendBytesPerMsgCmd;
uint64_t nRecvBytes;
mapMsgCmdSize mapRecvBytesPerMsgCmd;
CNetPermissionFlags permissionFlags;
bool fWhitelisted;
double dPingTime;
double dPingWait;
Expand Down Expand Up @@ -657,6 +659,10 @@ class CNode
*/
std::string cleanSubVer GUARDED_BY(cs_SubVer){};
bool m_prefer_evict{false}; // This peer is preferred for eviction.
CNetPermissionFlags permissionFlags{PF_NONE};
bool HasPermission(CNetPermissionFlags permission) const {
return (permissionFlags & permission) == 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

0 comments on commit c3fa8ca

Please sign in to comment.