aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorPieter Wuille <pieter.wuille@gmail.com>2014-06-21 13:34:36 +0200
committerPieter Wuille <pieter.wuille@gmail.com>2014-07-09 20:40:14 +0200
commitdc942e6f276b9fabc21f06d11cd16871d4054f82 (patch)
tree5ccbf11f0aec355fe115a606bb370125bb631d98 /src
parentf3330b40a599615f15f585166ef08f00d06e9616 (diff)
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).
Diffstat (limited to 'src')
-rw-r--r--src/init.cpp32
-rw-r--r--src/main.cpp19
-rw-r--r--src/net.cpp60
-rw-r--r--src/net.h13
-rw-r--r--src/rpcnet.cpp1
5 files changed, 98 insertions, 27 deletions
diff --git a/src/init.cpp b/src/init.cpp
index a1d75c9674..492070cbd9 100644
--- a/src/init.cpp
+++ b/src/init.cpp
@@ -58,7 +58,8 @@ CWallet* pwalletMain;
enum BindFlags {
BF_NONE = 0,
BF_EXPLICIT = (1U << 0),
- BF_REPORT_ERROR = (1U << 1)
+ BF_REPORT_ERROR = (1U << 1),
+ BF_WHITELIST = (1U << 2),
};
static const char* FEE_ESTIMATES_FILENAME="fee_estimates.dat";
@@ -192,7 +193,7 @@ bool static Bind(const CService &addr, unsigned int flags) {
if (!(flags & BF_EXPLICIT) && IsLimited(addr))
return false;
std::string strError;
- if (!BindListenPort(addr, strError)) {
+ if (!BindListenPort(addr, strError, flags & BF_WHITELIST)) {
if (flags & BF_REPORT_ERROR)
return InitError(strError);
return false;
@@ -253,6 +254,8 @@ std::string HelpMessage(HelpMessageMode mode)
strUsage += " -upnp " + _("Use UPnP to map the listening port (default: 0)") + "\n";
#endif
#endif
+ strUsage += " -whitebind=<addr> " + _("Bind to given address and whitelist peers connecting to it. Use [host]:port notation for IPv6") + "\n";
+ strUsage += " -whitelist=<netmask> " + _("Whitelist peers connecting from the given netmask or ip. Can be specified multiple times.") + "\n";
#ifdef ENABLE_WALLET
strUsage += "\n" + _("Wallet options:") + "\n";
@@ -504,11 +507,11 @@ bool AppInit2(boost::thread_group& threadGroup)
// ********************************************************* Step 2: parameter interactions
- if (mapArgs.count("-bind")) {
+ if (mapArgs.count("-bind") || mapArgs.count("-whitebind")) {
// when specifying an explicit binding address, you want to listen on it
// even when -connect or -proxy is specified
if (SoftSetBoolArg("-listen", true))
- LogPrintf("AppInit2 : parameter interaction: -bind set -> setting -listen=1\n");
+ LogPrintf("AppInit2 : parameter interaction: -bind or -whitebind set -> setting -listen=1\n");
}
if (mapArgs.count("-connect") && mapMultiArgs["-connect"].size() > 0) {
@@ -552,7 +555,7 @@ bool AppInit2(boost::thread_group& threadGroup)
}
// Make sure enough file descriptors are available
- int nBind = std::max((int)mapArgs.count("-bind"), 1);
+ int nBind = std::max((int)mapArgs.count("-bind") + (int)mapArgs.count("-whitebind"), 1);
nMaxConnections = GetArg("-maxconnections", 125);
nMaxConnections = std::max(std::min(nMaxConnections, (int)(FD_SETSIZE - nBind - MIN_CORE_FILEDESCRIPTORS)), 0);
int nFD = RaiseFileDescriptorLimit(nMaxConnections + MIN_CORE_FILEDESCRIPTORS);
@@ -769,6 +772,15 @@ bool AppInit2(boost::thread_group& threadGroup)
}
}
+ if (mapArgs.count("-whitelist")) {
+ BOOST_FOREACH(const std::string& net, mapMultiArgs["-whitelist"]) {
+ CSubNet subnet(net);
+ if (!subnet.IsValid())
+ return InitError(strprintf(_("Invalid netmask specified in -whitelist: '%s'"), net));
+ CNode::AddWhitelistedRange(subnet);
+ }
+ }
+
CService addrProxy;
bool fProxy = false;
if (mapArgs.count("-proxy")) {
@@ -805,13 +817,21 @@ bool AppInit2(boost::thread_group& threadGroup)
bool fBound = false;
if (fListen) {
- if (mapArgs.count("-bind")) {
+ if (mapArgs.count("-bind") || mapArgs.count("-whitebind")) {
BOOST_FOREACH(std::string strBind, mapMultiArgs["-bind"]) {
CService addrBind;
if (!Lookup(strBind.c_str(), addrBind, GetListenPort(), false))
return InitError(strprintf(_("Cannot resolve -bind address: '%s'"), strBind));
fBound |= Bind(addrBind, (BF_EXPLICIT | BF_REPORT_ERROR));
}
+ BOOST_FOREACH(std::string strBind, mapMultiArgs["-whitebind"]) {
+ CService addrBind;
+ if (!Lookup(strBind.c_str(), addrBind, 0, false))
+ return InitError(strprintf(_("Cannot resolve -whitebind address: '%s'"), strBind));
+ if (addrBind.GetPort() == 0)
+ return InitError(strprintf(_("Need to specify a port with -whitebind: '%s'"), strBind));
+ fBound |= Bind(addrBind, (BF_EXPLICIT | BF_REPORT_ERROR | BF_WHITELIST));
+ }
}
else {
struct in_addr inaddr_any;
diff --git a/src/main.cpp b/src/main.cpp
index a9c080ffae..8c119507e6 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -210,7 +210,7 @@ struct CBlockReject {
struct CNodeState {
// Accumulated misbehaviour score for this peer.
int nMisbehavior;
- // Whether this peer should be disconnected and banned.
+ // Whether this peer should be disconnected and banned (unless whitelisted).
bool fShouldBan;
// String name of this peer (debugging/logging purposes).
std::string name;
@@ -1425,7 +1425,8 @@ void Misbehaving(NodeId pnode, int howmuch)
return;
state->nMisbehavior += howmuch;
- if (state->nMisbehavior >= GetArg("-banscore", 100))
+ int banscore = GetArg("-banscore", 100);
+ if (state->nMisbehavior >= banscore && state->nMisbehavior - howmuch < banscore)
{
LogPrintf("Misbehaving: %s (%d -> %d) BAN THRESHOLD EXCEEDED\n", state->name, state->nMisbehavior-howmuch, state->nMisbehavior);
state->fShouldBan = true;
@@ -3947,6 +3948,11 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
unsigned int nEvicted = LimitOrphanTxSize(MAX_ORPHAN_TRANSACTIONS);
if (nEvicted > 0)
LogPrint("mempool", "mapOrphan overflow, removed %u tx\n", nEvicted);
+ } else if (pfrom->fWhitelisted) {
+ // Always relay transactions received from whitelisted peers, even
+ // if they are already in the mempool (allowing the node to function
+ // as a gateway for nodes hidden behind it).
+ RelayTransaction(tx);
}
int nDoS = 0;
if (state.IsInvalid(nDoS))
@@ -4440,11 +4446,14 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
CNodeState &state = *State(pto->GetId());
if (state.fShouldBan) {
- if (pto->addr.IsLocal())
- LogPrintf("Warning: not banning local node %s!\n", pto->addr.ToString());
+ if (pto->fWhitelisted)
+ LogPrintf("Warning: not punishing whitelisted peer %s!\n", pto->addr.ToString());
else {
pto->fDisconnect = true;
- CNode::Ban(pto->addr);
+ if (pto->addr.IsLocal())
+ LogPrintf("Warning: not banning local peer %s!\n", pto->addr.ToString());
+ else
+ CNode::Ban(pto->addr);
}
state.fShouldBan = false;
}
diff --git a/src/net.cpp b/src/net.cpp
index 6a660dc9bd..6a6d9df6af 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -50,7 +50,16 @@
using namespace std;
using namespace boost;
-static const int MAX_OUTBOUND_CONNECTIONS = 8;
+namespace {
+ const int MAX_OUTBOUND_CONNECTIONS = 8;
+
+ struct ListenSocket {
+ SOCKET socket;
+ bool whitelisted;
+
+ ListenSocket(SOCKET socket, bool whitelisted) : socket(socket), whitelisted(whitelisted) {}
+ };
+}
//
// Global state variables
@@ -65,7 +74,7 @@ static bool vfLimited[NET_MAX] = {};
static CNode* pnodeLocalHost = NULL;
static CNode* pnodeSync = NULL;
uint64_t nLocalHostNonce = 0;
-static std::vector<SOCKET> vhListenSocket;
+static std::vector<ListenSocket> vhListenSocket;
CAddrMan addrman;
int nMaxConnections = 125;
@@ -593,6 +602,24 @@ bool CNode::Ban(const CNetAddr &addr) {
return true;
}
+
+std::vector<CSubNet> CNode::vWhitelistedRange;
+CCriticalSection CNode::cs_vWhitelistedRange;
+
+bool CNode::IsWhitelistedRange(const CNetAddr &addr) {
+ LOCK(cs_vWhitelistedRange);
+ BOOST_FOREACH(const CSubNet& subnet, vWhitelistedRange) {
+ if (subnet.Match(addr))
+ return true;
+ }
+ return false;
+}
+
+void CNode::AddWhitelistedRange(const CSubNet &subnet) {
+ LOCK(cs_vWhitelistedRange);
+ vWhitelistedRange.push_back(subnet);
+}
+
#undef X
#define X(name) stats.name = name
void CNode::copyStats(CNodeStats &stats)
@@ -609,6 +636,7 @@ void CNode::copyStats(CNodeStats &stats)
X(nStartingHeight);
X(nSendBytes);
X(nRecvBytes);
+ X(fWhitelisted);
stats.fSyncNode = (this == pnodeSync);
// It is common for nodes with good ping times to suddenly become lagged,
@@ -848,9 +876,9 @@ void ThreadSocketHandler()
SOCKET hSocketMax = 0;
bool have_fds = false;
- BOOST_FOREACH(SOCKET hListenSocket, vhListenSocket) {
- FD_SET(hListenSocket, &fdsetRecv);
- hSocketMax = max(hSocketMax, hListenSocket);
+ BOOST_FOREACH(const ListenSocket& hListenSocket, vhListenSocket) {
+ FD_SET(hListenSocket.socket, &fdsetRecv);
+ hSocketMax = max(hSocketMax, hListenSocket.socket);
have_fds = true;
}
@@ -917,13 +945,13 @@ void ThreadSocketHandler()
//
// Accept new connections
//
- BOOST_FOREACH(SOCKET hListenSocket, vhListenSocket)
+ BOOST_FOREACH(const ListenSocket& hListenSocket, vhListenSocket)
{
- if (hListenSocket != INVALID_SOCKET && FD_ISSET(hListenSocket, &fdsetRecv))
+ if (hListenSocket.socket != INVALID_SOCKET && FD_ISSET(hListenSocket.socket, &fdsetRecv))
{
struct sockaddr_storage sockaddr;
socklen_t len = sizeof(sockaddr);
- SOCKET hSocket = accept(hListenSocket, (struct sockaddr*)&sockaddr, &len);
+ SOCKET hSocket = accept(hListenSocket.socket, (struct sockaddr*)&sockaddr, &len);
CAddress addr;
int nInbound = 0;
@@ -931,6 +959,7 @@ void ThreadSocketHandler()
if (!addr.SetSockAddr((const struct sockaddr*)&sockaddr))
LogPrintf("Warning: Unknown socket family\n");
+ bool whitelisted = hListenSocket.whitelisted || CNode::IsWhitelistedRange(addr);
{
LOCK(cs_vNodes);
BOOST_FOREACH(CNode* pnode, vNodes)
@@ -948,7 +977,7 @@ void ThreadSocketHandler()
{
closesocket(hSocket);
}
- else if (CNode::IsBanned(addr))
+ else if (CNode::IsBanned(addr) && !whitelisted)
{
LogPrintf("connection from %s dropped (banned)\n", addr.ToString());
closesocket(hSocket);
@@ -957,6 +986,7 @@ void ThreadSocketHandler()
{
CNode* pnode = new CNode(hSocket, addr, "", true);
pnode->AddRef();
+ pnode->fWhitelisted = whitelisted;
{
LOCK(cs_vNodes);
@@ -1580,7 +1610,7 @@ void ThreadMessageHandler()
-bool BindListenPort(const CService &addrBind, string& strError)
+bool BindListenPort(const CService &addrBind, string& strError, bool fWhitelisted)
{
strError = "";
int nOne = 1;
@@ -1661,9 +1691,9 @@ bool BindListenPort(const CService &addrBind, string& strError)
return false;
}
- vhListenSocket.push_back(hListenSocket);
+ vhListenSocket.push_back(ListenSocket(hListenSocket, fWhitelisted));
- if (addrBind.IsRoutable() && fDiscover)
+ if (addrBind.IsRoutable() && fDiscover && !fWhitelisted)
AddLocal(addrBind, LOCAL_BIND);
return true;
@@ -1788,9 +1818,9 @@ public:
BOOST_FOREACH(CNode* pnode, vNodes)
if (pnode->hSocket != INVALID_SOCKET)
closesocket(pnode->hSocket);
- BOOST_FOREACH(SOCKET hListenSocket, vhListenSocket)
- if (hListenSocket != INVALID_SOCKET)
- if (closesocket(hListenSocket) == SOCKET_ERROR)
+ BOOST_FOREACH(ListenSocket& hListenSocket, vhListenSocket)
+ if (hListenSocket.socket != INVALID_SOCKET)
+ if (closesocket(hListenSocket.socket) == SOCKET_ERROR)
LogPrintf("closesocket(hListenSocket) failed with error %s\n", NetworkErrorString(WSAGetLastError()));
// clean up some globals (to help leak detection)
diff --git a/src/net.h b/src/net.h
index c2a0416455..4e42a1eeb2 100644
--- a/src/net.h
+++ b/src/net.h
@@ -64,7 +64,7 @@ CNode* ConnectNode(CAddress addrConnect, const char *pszDest = NULL);
bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOutbound = NULL, const char *strDest = NULL, bool fOneShot = false);
void MapPort(bool fUseUPnP);
unsigned short GetListenPort();
-bool BindListenPort(const CService &bindAddr, std::string& strError);
+bool BindListenPort(const CService &bindAddr, std::string& strError, bool fWhitelisted = false);
void StartNode(boost::thread_group& threadGroup);
bool StopNode();
void SocketSendData(CNode *pnode);
@@ -154,6 +154,7 @@ public:
uint64_t nSendBytes;
uint64_t nRecvBytes;
bool fSyncNode;
+ bool fWhitelisted;
double dPingTime;
double dPingWait;
std::string addrLocal;
@@ -236,6 +237,7 @@ public:
// store the sanitized version in cleanSubVer. The original should be used when dealing with
// the network or wire types and the cleaned string used when displayed or logged.
std::string strSubVer, cleanSubVer;
+ bool fWhitelisted; // This peer can bypass DoS banning.
bool fOneShot;
bool fClient;
bool fInbound;
@@ -259,6 +261,11 @@ protected:
static std::map<CNetAddr, int64_t> setBanned;
static CCriticalSection cs_setBanned;
+ // Whitelisted ranges. Any node connecting from these is automatically
+ // whitelisted (as well as those connecting to whitelisted binds).
+ static std::vector<CSubNet> vWhitelistedRange;
+ static CCriticalSection cs_vWhitelistedRange;
+
// Basic fuzz-testing
void Fuzz(int nChance); // modifies ssSend
@@ -305,6 +312,7 @@ public:
addrName = addrNameIn == "" ? addr.ToStringIPPort() : addrNameIn;
nVersion = 0;
strSubVer = "";
+ fWhitelisted = false;
fOneShot = false;
fClient = false; // set by version message
fInbound = fInboundIn;
@@ -720,6 +728,9 @@ public:
static bool Ban(const CNetAddr &ip);
void copyStats(CNodeStats &stats);
+ static bool IsWhitelistedRange(const CNetAddr &ip);
+ static void AddWhitelistedRange(const CSubNet &subnet);
+
// Network stats
static void RecordBytesRecv(uint64_t bytes);
static void RecordBytesSent(uint64_t bytes);
diff --git a/src/rpcnet.cpp b/src/rpcnet.cpp
index cf2c293caf..680717930b 100644
--- a/src/rpcnet.cpp
+++ b/src/rpcnet.cpp
@@ -137,6 +137,7 @@ Value getpeerinfo(const Array& params, bool fHelp)
obj.push_back(Pair("syncheight", statestats.nSyncHeight));
}
obj.push_back(Pair("syncnode", stats.fSyncNode));
+ obj.push_back(Pair("whitelisted", stats.fWhitelisted));
ret.push_back(obj);
}