Skip to content

Commit dc942e6

Browse files
committed
Introduce whitelisted peers.
This adds a -whitelist option to specify subnet ranges from which peers that connect are whitelisted. In addition, there is a -whitebind option which works like -bind, except peers connecting to it are also whitelisted (allowing a separate listen port for trusted connections). Being whitelisted has two effects (for now): * They are immune to DoS disconnection/banning. * Transactions they broadcast (which are valid) are always relayed, even if they were already in the mempool. This means that a node can function as a gateway for a local network, and that rebroadcasts from the local network will work as expected. Whitelisting replaces the magic exemption localhost had for DoS disconnection (local addresses are still never banned, though), which implied hidden service connects (from a localhost Tor node) were incorrectly immune to DoS disconnection as well. This old behaviour is removed for that reason, but can be restored using -whitelist=127.0.0.1 or -whitelist=::1 can be specified. -whitebind is safer to use in case non-trusted localhost connections are expected (like hidden services).
1 parent f3330b4 commit dc942e6

File tree

6 files changed

+99
-28
lines changed

6 files changed

+99
-28
lines changed

qa/pull-tester/run-bitcoind-for-test.sh.in

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ touch "$DATADIR/regtest/debug.log"
1010
tail -q -n 1 -F "$DATADIR/regtest/debug.log" | grep -m 1 -q "Done loading" &
1111
WAITER=$!
1212
PORT=`expr $BASHPID + 10000`
13-
"@abs_top_builddir@/src/bitcoind@EXEEXT@" -connect=0.0.0.0 -datadir="$DATADIR" -rpcuser=user -rpcpassword=pass -listen -keypool=3 -debug -debug=net -logtimestamps -port=$PORT -regtest -rpcport=`expr $PORT + 1` &
13+
"@abs_top_builddir@/src/bitcoind@EXEEXT@" -connect=0.0.0.0 -datadir="$DATADIR" -rpcuser=user -rpcpassword=pass -listen -keypool=3 -debug -debug=net -logtimestamps -port=$PORT -whitelist=127.0.0.1 -regtest -rpcport=`expr $PORT + 1` &
1414
BITCOIND=$!
1515

1616
#Install a watchdog.

src/init.cpp

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,8 @@ CWallet* pwalletMain;
5858
enum BindFlags {
5959
BF_NONE = 0,
6060
BF_EXPLICIT = (1U << 0),
61-
BF_REPORT_ERROR = (1U << 1)
61+
BF_REPORT_ERROR = (1U << 1),
62+
BF_WHITELIST = (1U << 2),
6263
};
6364

6465
static const char* FEE_ESTIMATES_FILENAME="fee_estimates.dat";
@@ -192,7 +193,7 @@ bool static Bind(const CService &addr, unsigned int flags) {
192193
if (!(flags & BF_EXPLICIT) && IsLimited(addr))
193194
return false;
194195
std::string strError;
195-
if (!BindListenPort(addr, strError)) {
196+
if (!BindListenPort(addr, strError, flags & BF_WHITELIST)) {
196197
if (flags & BF_REPORT_ERROR)
197198
return InitError(strError);
198199
return false;
@@ -253,6 +254,8 @@ std::string HelpMessage(HelpMessageMode mode)
253254
strUsage += " -upnp " + _("Use UPnP to map the listening port (default: 0)") + "\n";
254255
#endif
255256
#endif
257+
strUsage += " -whitebind=<addr> " + _("Bind to given address and whitelist peers connecting to it. Use [host]:port notation for IPv6") + "\n";
258+
strUsage += " -whitelist=<netmask> " + _("Whitelist peers connecting from the given netmask or ip. Can be specified multiple times.") + "\n";
256259

257260
#ifdef ENABLE_WALLET
258261
strUsage += "\n" + _("Wallet options:") + "\n";
@@ -504,11 +507,11 @@ bool AppInit2(boost::thread_group& threadGroup)
504507

505508
// ********************************************************* Step 2: parameter interactions
506509

507-
if (mapArgs.count("-bind")) {
510+
if (mapArgs.count("-bind") || mapArgs.count("-whitebind")) {
508511
// when specifying an explicit binding address, you want to listen on it
509512
// even when -connect or -proxy is specified
510513
if (SoftSetBoolArg("-listen", true))
511-
LogPrintf("AppInit2 : parameter interaction: -bind set -> setting -listen=1\n");
514+
LogPrintf("AppInit2 : parameter interaction: -bind or -whitebind set -> setting -listen=1\n");
512515
}
513516

514517
if (mapArgs.count("-connect") && mapMultiArgs["-connect"].size() > 0) {
@@ -552,7 +555,7 @@ bool AppInit2(boost::thread_group& threadGroup)
552555
}
553556

554557
// Make sure enough file descriptors are available
555-
int nBind = std::max((int)mapArgs.count("-bind"), 1);
558+
int nBind = std::max((int)mapArgs.count("-bind") + (int)mapArgs.count("-whitebind"), 1);
556559
nMaxConnections = GetArg("-maxconnections", 125);
557560
nMaxConnections = std::max(std::min(nMaxConnections, (int)(FD_SETSIZE - nBind - MIN_CORE_FILEDESCRIPTORS)), 0);
558561
int nFD = RaiseFileDescriptorLimit(nMaxConnections + MIN_CORE_FILEDESCRIPTORS);
@@ -769,6 +772,15 @@ bool AppInit2(boost::thread_group& threadGroup)
769772
}
770773
}
771774

775+
if (mapArgs.count("-whitelist")) {
776+
BOOST_FOREACH(const std::string& net, mapMultiArgs["-whitelist"]) {
777+
CSubNet subnet(net);
778+
if (!subnet.IsValid())
779+
return InitError(strprintf(_("Invalid netmask specified in -whitelist: '%s'"), net));
780+
CNode::AddWhitelistedRange(subnet);
781+
}
782+
}
783+
772784
CService addrProxy;
773785
bool fProxy = false;
774786
if (mapArgs.count("-proxy")) {
@@ -805,13 +817,21 @@ bool AppInit2(boost::thread_group& threadGroup)
805817

806818
bool fBound = false;
807819
if (fListen) {
808-
if (mapArgs.count("-bind")) {
820+
if (mapArgs.count("-bind") || mapArgs.count("-whitebind")) {
809821
BOOST_FOREACH(std::string strBind, mapMultiArgs["-bind"]) {
810822
CService addrBind;
811823
if (!Lookup(strBind.c_str(), addrBind, GetListenPort(), false))
812824
return InitError(strprintf(_("Cannot resolve -bind address: '%s'"), strBind));
813825
fBound |= Bind(addrBind, (BF_EXPLICIT | BF_REPORT_ERROR));
814826
}
827+
BOOST_FOREACH(std::string strBind, mapMultiArgs["-whitebind"]) {
828+
CService addrBind;
829+
if (!Lookup(strBind.c_str(), addrBind, 0, false))
830+
return InitError(strprintf(_("Cannot resolve -whitebind address: '%s'"), strBind));
831+
if (addrBind.GetPort() == 0)
832+
return InitError(strprintf(_("Need to specify a port with -whitebind: '%s'"), strBind));
833+
fBound |= Bind(addrBind, (BF_EXPLICIT | BF_REPORT_ERROR | BF_WHITELIST));
834+
}
815835
}
816836
else {
817837
struct in_addr inaddr_any;

src/main.cpp

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ struct CBlockReject {
210210
struct CNodeState {
211211
// Accumulated misbehaviour score for this peer.
212212
int nMisbehavior;
213-
// Whether this peer should be disconnected and banned.
213+
// Whether this peer should be disconnected and banned (unless whitelisted).
214214
bool fShouldBan;
215215
// String name of this peer (debugging/logging purposes).
216216
std::string name;
@@ -1425,7 +1425,8 @@ void Misbehaving(NodeId pnode, int howmuch)
14251425
return;
14261426

14271427
state->nMisbehavior += howmuch;
1428-
if (state->nMisbehavior >= GetArg("-banscore", 100))
1428+
int banscore = GetArg("-banscore", 100);
1429+
if (state->nMisbehavior >= banscore && state->nMisbehavior - howmuch < banscore)
14291430
{
14301431
LogPrintf("Misbehaving: %s (%d -> %d) BAN THRESHOLD EXCEEDED\n", state->name, state->nMisbehavior-howmuch, state->nMisbehavior);
14311432
state->fShouldBan = true;
@@ -3947,6 +3948,11 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
39473948
unsigned int nEvicted = LimitOrphanTxSize(MAX_ORPHAN_TRANSACTIONS);
39483949
if (nEvicted > 0)
39493950
LogPrint("mempool", "mapOrphan overflow, removed %u tx\n", nEvicted);
3951+
} else if (pfrom->fWhitelisted) {
3952+
// Always relay transactions received from whitelisted peers, even
3953+
// if they are already in the mempool (allowing the node to function
3954+
// as a gateway for nodes hidden behind it).
3955+
RelayTransaction(tx);
39503956
}
39513957
int nDoS = 0;
39523958
if (state.IsInvalid(nDoS))
@@ -4440,11 +4446,14 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
44404446

44414447
CNodeState &state = *State(pto->GetId());
44424448
if (state.fShouldBan) {
4443-
if (pto->addr.IsLocal())
4444-
LogPrintf("Warning: not banning local node %s!\n", pto->addr.ToString());
4449+
if (pto->fWhitelisted)
4450+
LogPrintf("Warning: not punishing whitelisted peer %s!\n", pto->addr.ToString());
44454451
else {
44464452
pto->fDisconnect = true;
4447-
CNode::Ban(pto->addr);
4453+
if (pto->addr.IsLocal())
4454+
LogPrintf("Warning: not banning local peer %s!\n", pto->addr.ToString());
4455+
else
4456+
CNode::Ban(pto->addr);
44484457
}
44494458
state.fShouldBan = false;
44504459
}

src/net.cpp

Lines changed: 45 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,16 @@
5050
using namespace std;
5151
using namespace boost;
5252

53-
static const int MAX_OUTBOUND_CONNECTIONS = 8;
53+
namespace {
54+
const int MAX_OUTBOUND_CONNECTIONS = 8;
55+
56+
struct ListenSocket {
57+
SOCKET socket;
58+
bool whitelisted;
59+
60+
ListenSocket(SOCKET socket, bool whitelisted) : socket(socket), whitelisted(whitelisted) {}
61+
};
62+
}
5463

5564
//
5665
// Global state variables
@@ -65,7 +74,7 @@ static bool vfLimited[NET_MAX] = {};
6574
static CNode* pnodeLocalHost = NULL;
6675
static CNode* pnodeSync = NULL;
6776
uint64_t nLocalHostNonce = 0;
68-
static std::vector<SOCKET> vhListenSocket;
77+
static std::vector<ListenSocket> vhListenSocket;
6978
CAddrMan addrman;
7079
int nMaxConnections = 125;
7180

@@ -593,6 +602,24 @@ bool CNode::Ban(const CNetAddr &addr) {
593602
return true;
594603
}
595604

605+
606+
std::vector<CSubNet> CNode::vWhitelistedRange;
607+
CCriticalSection CNode::cs_vWhitelistedRange;
608+
609+
bool CNode::IsWhitelistedRange(const CNetAddr &addr) {
610+
LOCK(cs_vWhitelistedRange);
611+
BOOST_FOREACH(const CSubNet& subnet, vWhitelistedRange) {
612+
if (subnet.Match(addr))
613+
return true;
614+
}
615+
return false;
616+
}
617+
618+
void CNode::AddWhitelistedRange(const CSubNet &subnet) {
619+
LOCK(cs_vWhitelistedRange);
620+
vWhitelistedRange.push_back(subnet);
621+
}
622+
596623
#undef X
597624
#define X(name) stats.name = name
598625
void CNode::copyStats(CNodeStats &stats)
@@ -609,6 +636,7 @@ void CNode::copyStats(CNodeStats &stats)
609636
X(nStartingHeight);
610637
X(nSendBytes);
611638
X(nRecvBytes);
639+
X(fWhitelisted);
612640
stats.fSyncNode = (this == pnodeSync);
613641

614642
// It is common for nodes with good ping times to suddenly become lagged,
@@ -848,9 +876,9 @@ void ThreadSocketHandler()
848876
SOCKET hSocketMax = 0;
849877
bool have_fds = false;
850878

851-
BOOST_FOREACH(SOCKET hListenSocket, vhListenSocket) {
852-
FD_SET(hListenSocket, &fdsetRecv);
853-
hSocketMax = max(hSocketMax, hListenSocket);
879+
BOOST_FOREACH(const ListenSocket& hListenSocket, vhListenSocket) {
880+
FD_SET(hListenSocket.socket, &fdsetRecv);
881+
hSocketMax = max(hSocketMax, hListenSocket.socket);
854882
have_fds = true;
855883
}
856884

@@ -917,20 +945,21 @@ void ThreadSocketHandler()
917945
//
918946
// Accept new connections
919947
//
920-
BOOST_FOREACH(SOCKET hListenSocket, vhListenSocket)
948+
BOOST_FOREACH(const ListenSocket& hListenSocket, vhListenSocket)
921949
{
922-
if (hListenSocket != INVALID_SOCKET && FD_ISSET(hListenSocket, &fdsetRecv))
950+
if (hListenSocket.socket != INVALID_SOCKET && FD_ISSET(hListenSocket.socket, &fdsetRecv))
923951
{
924952
struct sockaddr_storage sockaddr;
925953
socklen_t len = sizeof(sockaddr);
926-
SOCKET hSocket = accept(hListenSocket, (struct sockaddr*)&sockaddr, &len);
954+
SOCKET hSocket = accept(hListenSocket.socket, (struct sockaddr*)&sockaddr, &len);
927955
CAddress addr;
928956
int nInbound = 0;
929957

930958
if (hSocket != INVALID_SOCKET)
931959
if (!addr.SetSockAddr((const struct sockaddr*)&sockaddr))
932960
LogPrintf("Warning: Unknown socket family\n");
933961

962+
bool whitelisted = hListenSocket.whitelisted || CNode::IsWhitelistedRange(addr);
934963
{
935964
LOCK(cs_vNodes);
936965
BOOST_FOREACH(CNode* pnode, vNodes)
@@ -948,7 +977,7 @@ void ThreadSocketHandler()
948977
{
949978
closesocket(hSocket);
950979
}
951-
else if (CNode::IsBanned(addr))
980+
else if (CNode::IsBanned(addr) && !whitelisted)
952981
{
953982
LogPrintf("connection from %s dropped (banned)\n", addr.ToString());
954983
closesocket(hSocket);
@@ -957,6 +986,7 @@ void ThreadSocketHandler()
957986
{
958987
CNode* pnode = new CNode(hSocket, addr, "", true);
959988
pnode->AddRef();
989+
pnode->fWhitelisted = whitelisted;
960990

961991
{
962992
LOCK(cs_vNodes);
@@ -1580,7 +1610,7 @@ void ThreadMessageHandler()
15801610

15811611

15821612

1583-
bool BindListenPort(const CService &addrBind, string& strError)
1613+
bool BindListenPort(const CService &addrBind, string& strError, bool fWhitelisted)
15841614
{
15851615
strError = "";
15861616
int nOne = 1;
@@ -1661,9 +1691,9 @@ bool BindListenPort(const CService &addrBind, string& strError)
16611691
return false;
16621692
}
16631693

1664-
vhListenSocket.push_back(hListenSocket);
1694+
vhListenSocket.push_back(ListenSocket(hListenSocket, fWhitelisted));
16651695

1666-
if (addrBind.IsRoutable() && fDiscover)
1696+
if (addrBind.IsRoutable() && fDiscover && !fWhitelisted)
16671697
AddLocal(addrBind, LOCAL_BIND);
16681698

16691699
return true;
@@ -1788,9 +1818,9 @@ class CNetCleanup
17881818
BOOST_FOREACH(CNode* pnode, vNodes)
17891819
if (pnode->hSocket != INVALID_SOCKET)
17901820
closesocket(pnode->hSocket);
1791-
BOOST_FOREACH(SOCKET hListenSocket, vhListenSocket)
1792-
if (hListenSocket != INVALID_SOCKET)
1793-
if (closesocket(hListenSocket) == SOCKET_ERROR)
1821+
BOOST_FOREACH(ListenSocket& hListenSocket, vhListenSocket)
1822+
if (hListenSocket.socket != INVALID_SOCKET)
1823+
if (closesocket(hListenSocket.socket) == SOCKET_ERROR)
17941824
LogPrintf("closesocket(hListenSocket) failed with error %s\n", NetworkErrorString(WSAGetLastError()));
17951825

17961826
// clean up some globals (to help leak detection)

src/net.h

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ CNode* ConnectNode(CAddress addrConnect, const char *pszDest = NULL);
6464
bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOutbound = NULL, const char *strDest = NULL, bool fOneShot = false);
6565
void MapPort(bool fUseUPnP);
6666
unsigned short GetListenPort();
67-
bool BindListenPort(const CService &bindAddr, std::string& strError);
67+
bool BindListenPort(const CService &bindAddr, std::string& strError, bool fWhitelisted = false);
6868
void StartNode(boost::thread_group& threadGroup);
6969
bool StopNode();
7070
void SocketSendData(CNode *pnode);
@@ -154,6 +154,7 @@ class CNodeStats
154154
uint64_t nSendBytes;
155155
uint64_t nRecvBytes;
156156
bool fSyncNode;
157+
bool fWhitelisted;
157158
double dPingTime;
158159
double dPingWait;
159160
std::string addrLocal;
@@ -236,6 +237,7 @@ class CNode
236237
// store the sanitized version in cleanSubVer. The original should be used when dealing with
237238
// the network or wire types and the cleaned string used when displayed or logged.
238239
std::string strSubVer, cleanSubVer;
240+
bool fWhitelisted; // This peer can bypass DoS banning.
239241
bool fOneShot;
240242
bool fClient;
241243
bool fInbound;
@@ -259,6 +261,11 @@ class CNode
259261
static std::map<CNetAddr, int64_t> setBanned;
260262
static CCriticalSection cs_setBanned;
261263

264+
// Whitelisted ranges. Any node connecting from these is automatically
265+
// whitelisted (as well as those connecting to whitelisted binds).
266+
static std::vector<CSubNet> vWhitelistedRange;
267+
static CCriticalSection cs_vWhitelistedRange;
268+
262269
// Basic fuzz-testing
263270
void Fuzz(int nChance); // modifies ssSend
264271

@@ -305,6 +312,7 @@ class CNode
305312
addrName = addrNameIn == "" ? addr.ToStringIPPort() : addrNameIn;
306313
nVersion = 0;
307314
strSubVer = "";
315+
fWhitelisted = false;
308316
fOneShot = false;
309317
fClient = false; // set by version message
310318
fInbound = fInboundIn;
@@ -720,6 +728,9 @@ class CNode
720728
static bool Ban(const CNetAddr &ip);
721729
void copyStats(CNodeStats &stats);
722730

731+
static bool IsWhitelistedRange(const CNetAddr &ip);
732+
static void AddWhitelistedRange(const CSubNet &subnet);
733+
723734
// Network stats
724735
static void RecordBytesRecv(uint64_t bytes);
725736
static void RecordBytesSent(uint64_t bytes);

src/rpcnet.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ Value getpeerinfo(const Array& params, bool fHelp)
137137
obj.push_back(Pair("syncheight", statestats.nSyncHeight));
138138
}
139139
obj.push_back(Pair("syncnode", stats.fSyncNode));
140+
obj.push_back(Pair("whitelisted", stats.fWhitelisted));
140141

141142
ret.push_back(obj);
142143
}

0 commit comments

Comments
 (0)