Skip to content

Commit c9bc398

Browse files
committed
Merge pull request #4378
dc942e6 Introduce whitelisted peers. (Pieter Wuille)
2 parents 77ed583 + dc942e6 commit c9bc398

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;
@@ -252,6 +253,8 @@ std::string HelpMessage(HelpMessageMode mode)
252253
strUsage += " -upnp " + _("Use UPnP to map the listening port (default: 0)") + "\n";
253254
#endif
254255
#endif
256+
strUsage += " -whitebind=<addr> " + _("Bind to given address and whitelist peers connecting to it. Use [host]:port notation for IPv6") + "\n";
257+
strUsage += " -whitelist=<netmask> " + _("Whitelist peers connecting from the given netmask or ip. Can be specified multiple times.") + "\n";
255258

256259
#ifdef ENABLE_WALLET
257260
strUsage += "\n" + _("Wallet options:") + "\n";
@@ -506,11 +509,11 @@ bool AppInit2(boost::thread_group& threadGroup)
506509

507510
// ********************************************************* Step 2: parameter interactions
508511

509-
if (mapArgs.count("-bind")) {
512+
if (mapArgs.count("-bind") || mapArgs.count("-whitebind")) {
510513
// when specifying an explicit binding address, you want to listen on it
511514
// even when -connect or -proxy is specified
512515
if (SoftSetBoolArg("-listen", true))
513-
LogPrintf("AppInit2 : parameter interaction: -bind set -> setting -listen=1\n");
516+
LogPrintf("AppInit2 : parameter interaction: -bind or -whitebind set -> setting -listen=1\n");
514517
}
515518

516519
if (mapArgs.count("-connect") && mapMultiArgs["-connect"].size() > 0) {
@@ -554,7 +557,7 @@ bool AppInit2(boost::thread_group& threadGroup)
554557
}
555558

556559
// Make sure enough file descriptors are available
557-
int nBind = std::max((int)mapArgs.count("-bind"), 1);
560+
int nBind = std::max((int)mapArgs.count("-bind") + (int)mapArgs.count("-whitebind"), 1);
558561
nMaxConnections = GetArg("-maxconnections", 125);
559562
nMaxConnections = std::max(std::min(nMaxConnections, (int)(FD_SETSIZE - nBind - MIN_CORE_FILEDESCRIPTORS)), 0);
560563
int nFD = RaiseFileDescriptorLimit(nMaxConnections + MIN_CORE_FILEDESCRIPTORS);
@@ -771,6 +774,15 @@ bool AppInit2(boost::thread_group& threadGroup)
771774
}
772775
}
773776

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

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

src/main.cpp

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ struct CBlockReject {
212212
struct CNodeState {
213213
// Accumulated misbehaviour score for this peer.
214214
int nMisbehavior;
215-
// Whether this peer should be disconnected and banned.
215+
// Whether this peer should be disconnected and banned (unless whitelisted).
216216
bool fShouldBan;
217217
// String name of this peer (debugging/logging purposes).
218218
std::string name;
@@ -1427,7 +1427,8 @@ void Misbehaving(NodeId pnode, int howmuch)
14271427
return;
14281428

14291429
state->nMisbehavior += howmuch;
1430-
if (state->nMisbehavior >= GetArg("-banscore", 100))
1430+
int banscore = GetArg("-banscore", 100);
1431+
if (state->nMisbehavior >= banscore && state->nMisbehavior - howmuch < banscore)
14311432
{
14321433
LogPrintf("Misbehaving: %s (%d -> %d) BAN THRESHOLD EXCEEDED\n", state->name, state->nMisbehavior-howmuch, state->nMisbehavior);
14331434
state->fShouldBan = true;
@@ -3952,6 +3953,11 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
39523953
unsigned int nEvicted = LimitOrphanTxSize(MAX_ORPHAN_TRANSACTIONS);
39533954
if (nEvicted > 0)
39543955
LogPrint("mempool", "mapOrphan overflow, removed %u tx\n", nEvicted);
3956+
} else if (pfrom->fWhitelisted) {
3957+
// Always relay transactions received from whitelisted peers, even
3958+
// if they are already in the mempool (allowing the node to function
3959+
// as a gateway for nodes hidden behind it).
3960+
RelayTransaction(tx);
39553961
}
39563962
int nDoS = 0;
39573963
if (state.IsInvalid(nDoS))
@@ -4445,11 +4451,14 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
44454451

44464452
CNodeState &state = *State(pto->GetId());
44474453
if (state.fShouldBan) {
4448-
if (pto->addr.IsLocal())
4449-
LogPrintf("Warning: not banning local node %s!\n", pto->addr.ToString());
4454+
if (pto->fWhitelisted)
4455+
LogPrintf("Warning: not punishing whitelisted peer %s!\n", pto->addr.ToString());
44504456
else {
44514457
pto->fDisconnect = true;
4452-
CNode::Ban(pto->addr);
4458+
if (pto->addr.IsLocal())
4459+
LogPrintf("Warning: not banning local peer %s!\n", pto->addr.ToString());
4460+
else
4461+
CNode::Ban(pto->addr);
44534462
}
44544463
state.fShouldBan = false;
44554464
}

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)