Merge pull request #1190 from rnicoll/1.8-1d21ba2f5ecbf03086d0b65c4c4c80a39a94c2ee
Backport Bitcoin Core 0.10.x P2P changes to Dogecoin 1.8.3
This commit is contained in:
commit
7f2e24a00b
|
@ -10,7 +10,7 @@ touch "$DATADIR/regtest/debug.log"
|
|||
tail -q -n 1 -F "$DATADIR/regtest/debug.log" | grep -m 1 -q "Done loading" &
|
||||
WAITER=$!
|
||||
PORT=`expr $BASHPID + 10000`
|
||||
"@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` &
|
||||
"@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` &
|
||||
BITCOIND=$!
|
||||
|
||||
#Install a watchdog.
|
||||
|
|
324
src/addrman.cpp
324
src/addrman.cpp
|
@ -9,34 +9,27 @@
|
|||
|
||||
using namespace std;
|
||||
|
||||
int CAddrInfo::GetTriedBucket(const std::vector<unsigned char> &nKey) const
|
||||
int CAddrInfo::GetTriedBucket(const uint256& nKey) const
|
||||
{
|
||||
CDataStream ss1(SER_GETHASH, 0);
|
||||
std::vector<unsigned char> vchKey = GetKey();
|
||||
ss1 << nKey << vchKey;
|
||||
uint64_t hash1 = Hash(ss1.begin(), ss1.end()).GetLow64();
|
||||
|
||||
CDataStream ss2(SER_GETHASH, 0);
|
||||
std::vector<unsigned char> vchGroupKey = GetGroup();
|
||||
ss2 << nKey << vchGroupKey << (hash1 % ADDRMAN_TRIED_BUCKETS_PER_GROUP);
|
||||
uint64_t hash2 = Hash(ss2.begin(), ss2.end()).GetLow64();
|
||||
uint64_t hash1 = (CHashWriter(SER_GETHASH, 0) << nKey << GetKey()).GetHash().GetLow64();
|
||||
uint64_t hash2 = (CHashWriter(SER_GETHASH, 0) << nKey << GetGroup() << (hash1 % ADDRMAN_TRIED_BUCKETS_PER_GROUP)).GetHash().GetLow64();
|
||||
return hash2 % ADDRMAN_TRIED_BUCKET_COUNT;
|
||||
}
|
||||
|
||||
int CAddrInfo::GetNewBucket(const std::vector<unsigned char> &nKey, const CNetAddr& src) const
|
||||
int CAddrInfo::GetNewBucket(const uint256& nKey, const CNetAddr& src) const
|
||||
{
|
||||
CDataStream ss1(SER_GETHASH, 0);
|
||||
std::vector<unsigned char> vchGroupKey = GetGroup();
|
||||
std::vector<unsigned char> vchSourceGroupKey = src.GetGroup();
|
||||
ss1 << nKey << vchGroupKey << vchSourceGroupKey;
|
||||
uint64_t hash1 = Hash(ss1.begin(), ss1.end()).GetLow64();
|
||||
|
||||
CDataStream ss2(SER_GETHASH, 0);
|
||||
ss2 << nKey << vchSourceGroupKey << (hash1 % ADDRMAN_NEW_BUCKETS_PER_SOURCE_GROUP);
|
||||
uint64_t hash2 = Hash(ss2.begin(), ss2.end()).GetLow64();
|
||||
uint64_t hash1 = (CHashWriter(SER_GETHASH, 0) << nKey << GetGroup() << vchSourceGroupKey).GetHash().GetLow64();
|
||||
uint64_t hash2 = (CHashWriter(SER_GETHASH, 0) << nKey << vchSourceGroupKey << (hash1 % ADDRMAN_NEW_BUCKETS_PER_SOURCE_GROUP)).GetHash().GetLow64();
|
||||
return hash2 % ADDRMAN_NEW_BUCKET_COUNT;
|
||||
}
|
||||
|
||||
int CAddrInfo::GetBucketPosition(const uint256 &nKey, bool fNew, int nBucket) const
|
||||
{
|
||||
uint64_t hash1 = (CHashWriter(SER_GETHASH, 0) << nKey << (fNew ? 'N' : 'K') << nBucket << GetKey()).GetHash().GetLow64();
|
||||
return hash1 % ADDRMAN_BUCKET_SIZE;
|
||||
}
|
||||
|
||||
bool CAddrInfo::IsTerrible(int64_t nNow) const
|
||||
{
|
||||
if (nLastTry && nLastTry >= nNow-60) // never remove things tried the last minute
|
||||
|
@ -67,8 +60,6 @@ double CAddrInfo::GetChance(int64_t nNow) const
|
|||
if (nSinceLastSeen < 0) nSinceLastSeen = 0;
|
||||
if (nSinceLastTry < 0) nSinceLastTry = 0;
|
||||
|
||||
fChance *= 600.0 / (600.0 + nSinceLastSeen);
|
||||
|
||||
// deprioritize very recent attempts away
|
||||
if (nSinceLastTry < 60*10)
|
||||
fChance *= 0.01;
|
||||
|
@ -125,93 +116,44 @@ void CAddrMan::SwapRandom(unsigned int nRndPos1, unsigned int nRndPos2)
|
|||
vRandom[nRndPos2] = nId1;
|
||||
}
|
||||
|
||||
int CAddrMan::SelectTried(int nKBucket)
|
||||
void CAddrMan::Delete(int nId)
|
||||
{
|
||||
std::vector<int> &vTried = vvTried[nKBucket];
|
||||
assert(mapInfo.count(nId) != 0);
|
||||
CAddrInfo& info = mapInfo[nId];
|
||||
assert(!info.fInTried);
|
||||
assert(info.nRefCount == 0);
|
||||
|
||||
// random shuffle the first few elements (using the entire list)
|
||||
// find the least recently tried among them
|
||||
int64_t nOldest = -1;
|
||||
int nOldestPos = -1;
|
||||
for (unsigned int i = 0; i < ADDRMAN_TRIED_ENTRIES_INSPECT_ON_EVICT && i < vTried.size(); i++)
|
||||
{
|
||||
int nPos = GetRandInt(vTried.size() - i) + i;
|
||||
int nTemp = vTried[nPos];
|
||||
vTried[nPos] = vTried[i];
|
||||
vTried[i] = nTemp;
|
||||
assert(nOldest == -1 || mapInfo.count(nTemp) == 1);
|
||||
if (nOldest == -1 || mapInfo[nTemp].nLastSuccess < mapInfo[nOldest].nLastSuccess) {
|
||||
nOldest = nTemp;
|
||||
nOldestPos = nPos;
|
||||
}
|
||||
}
|
||||
|
||||
return nOldestPos;
|
||||
SwapRandom(info.nRandomPos, vRandom.size() - 1);
|
||||
vRandom.pop_back();
|
||||
mapAddr.erase(info);
|
||||
mapInfo.erase(nId);
|
||||
nNew--;
|
||||
}
|
||||
|
||||
int CAddrMan::ShrinkNew(int nUBucket)
|
||||
void CAddrMan::ClearNew(int nUBucket, int nUBucketPos)
|
||||
{
|
||||
assert(nUBucket >= 0 && (unsigned int)nUBucket < vvNew.size());
|
||||
std::set<int> &vNew = vvNew[nUBucket];
|
||||
|
||||
// first look for deletable items
|
||||
for (std::set<int>::iterator it = vNew.begin(); it != vNew.end(); it++)
|
||||
{
|
||||
assert(mapInfo.count(*it));
|
||||
CAddrInfo &info = mapInfo[*it];
|
||||
if (info.IsTerrible())
|
||||
{
|
||||
if (--info.nRefCount == 0)
|
||||
{
|
||||
SwapRandom(info.nRandomPos, vRandom.size()-1);
|
||||
vRandom.pop_back();
|
||||
mapAddr.erase(info);
|
||||
mapInfo.erase(*it);
|
||||
nNew--;
|
||||
}
|
||||
vNew.erase(it);
|
||||
return 0;
|
||||
// if there is an entry in the specified bucket, delete it.
|
||||
if (vvNew[nUBucket][nUBucketPos] != -1) {
|
||||
int nIdDelete = vvNew[nUBucket][nUBucketPos];
|
||||
CAddrInfo& infoDelete = mapInfo[nIdDelete];
|
||||
assert(infoDelete.nRefCount > 0);
|
||||
infoDelete.nRefCount--;
|
||||
vvNew[nUBucket][nUBucketPos] = -1;
|
||||
if (infoDelete.nRefCount == 0) {
|
||||
Delete(nIdDelete);
|
||||
}
|
||||
}
|
||||
|
||||
// otherwise, select four randomly, and pick the oldest of those to replace
|
||||
int n[4] = {GetRandInt(vNew.size()), GetRandInt(vNew.size()), GetRandInt(vNew.size()), GetRandInt(vNew.size())};
|
||||
int nI = 0;
|
||||
int nOldest = -1;
|
||||
for (std::set<int>::iterator it = vNew.begin(); it != vNew.end(); it++)
|
||||
{
|
||||
if (nI == n[0] || nI == n[1] || nI == n[2] || nI == n[3])
|
||||
{
|
||||
assert(nOldest == -1 || mapInfo.count(*it) == 1);
|
||||
if (nOldest == -1 || mapInfo[*it].nTime < mapInfo[nOldest].nTime)
|
||||
nOldest = *it;
|
||||
}
|
||||
nI++;
|
||||
}
|
||||
assert(mapInfo.count(nOldest) == 1);
|
||||
CAddrInfo &info = mapInfo[nOldest];
|
||||
if (--info.nRefCount == 0)
|
||||
{
|
||||
SwapRandom(info.nRandomPos, vRandom.size()-1);
|
||||
vRandom.pop_back();
|
||||
mapAddr.erase(info);
|
||||
mapInfo.erase(nOldest);
|
||||
nNew--;
|
||||
}
|
||||
vNew.erase(nOldest);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void CAddrMan::MakeTried(CAddrInfo& info, int nId, int nOrigin)
|
||||
void CAddrMan::MakeTried(CAddrInfo& info, int nId)
|
||||
{
|
||||
assert(vvNew[nOrigin].count(nId) == 1);
|
||||
|
||||
// remove the entry from all new buckets
|
||||
for (std::vector<std::set<int> >::iterator it = vvNew.begin(); it != vvNew.end(); it++)
|
||||
{
|
||||
if ((*it).erase(nId))
|
||||
for (int bucket = 0; bucket < ADDRMAN_NEW_BUCKET_COUNT; bucket++) {
|
||||
int pos = info.GetBucketPosition(nKey, true, bucket);
|
||||
if (vvNew[bucket][pos] == nId) {
|
||||
vvNew[bucket][pos] = -1;
|
||||
info.nRefCount--;
|
||||
}
|
||||
}
|
||||
nNew--;
|
||||
|
||||
|
@ -219,46 +161,36 @@ void CAddrMan::MakeTried(CAddrInfo& info, int nId, int nOrigin)
|
|||
|
||||
// what tried bucket to move the entry to
|
||||
int nKBucket = info.GetTriedBucket(nKey);
|
||||
std::vector<int> &vTried = vvTried[nKBucket];
|
||||
int nKBucketPos = info.GetBucketPosition(nKey, false, nKBucket);
|
||||
|
||||
// first check whether there is place to just add it
|
||||
if (vTried.size() < ADDRMAN_TRIED_BUCKET_SIZE)
|
||||
{
|
||||
vTried.push_back(nId);
|
||||
nTried++;
|
||||
info.fInTried = true;
|
||||
return;
|
||||
// first make space to add it (the existing tried entry there is moved to new, deleting whatever is there).
|
||||
if (vvTried[nKBucket][nKBucketPos] != -1) {
|
||||
// find an item to evict
|
||||
int nIdEvict = vvTried[nKBucket][nKBucketPos];
|
||||
assert(mapInfo.count(nIdEvict) == 1);
|
||||
CAddrInfo& infoOld = mapInfo[nIdEvict];
|
||||
|
||||
// Remove the to-be-evicted item from the tried set.
|
||||
infoOld.fInTried = false;
|
||||
vvTried[nKBucket][nKBucketPos] = -1;
|
||||
nTried--;
|
||||
|
||||
// find which new bucket it belongs to
|
||||
int nUBucket = infoOld.GetNewBucket(nKey);
|
||||
int nUBucketPos = infoOld.GetBucketPosition(nKey, true, nUBucket);
|
||||
ClearNew(nUBucket, nUBucketPos);
|
||||
assert(vvNew[nUBucket][nUBucketPos] == -1);
|
||||
|
||||
// Enter it into the new set again.
|
||||
infoOld.nRefCount = 1;
|
||||
vvNew[nUBucket][nUBucketPos] = nIdEvict;
|
||||
nNew++;
|
||||
}
|
||||
assert(vvTried[nKBucket][nKBucketPos] == -1);
|
||||
|
||||
// otherwise, find an item to evict
|
||||
int nPos = SelectTried(nKBucket);
|
||||
|
||||
// find which new bucket it belongs to
|
||||
assert(mapInfo.count(vTried[nPos]) == 1);
|
||||
int nUBucket = mapInfo[vTried[nPos]].GetNewBucket(nKey);
|
||||
std::set<int> &vNew = vvNew[nUBucket];
|
||||
|
||||
// remove the to-be-replaced tried entry from the tried set
|
||||
CAddrInfo& infoOld = mapInfo[vTried[nPos]];
|
||||
infoOld.fInTried = false;
|
||||
infoOld.nRefCount = 1;
|
||||
// do not update nTried, as we are going to move something else there immediately
|
||||
|
||||
// check whether there is place in that one,
|
||||
if (vNew.size() < ADDRMAN_NEW_BUCKET_SIZE)
|
||||
{
|
||||
// if so, move it back there
|
||||
vNew.insert(vTried[nPos]);
|
||||
} else {
|
||||
// otherwise, move it to the new bucket nId came from (there is certainly place there)
|
||||
vvNew[nOrigin].insert(vTried[nPos]);
|
||||
}
|
||||
nNew++;
|
||||
|
||||
vTried[nPos] = nId;
|
||||
// we just overwrote an entry in vTried; no need to update nTried
|
||||
vvTried[nKBucket][nKBucketPos] = nId;
|
||||
nTried++;
|
||||
info.fInTried = true;
|
||||
return;
|
||||
}
|
||||
|
||||
void CAddrMan::Good_(const CService &addr, int64_t nTime)
|
||||
|
@ -279,22 +211,21 @@ void CAddrMan::Good_(const CService &addr, int64_t nTime)
|
|||
// update info
|
||||
info.nLastSuccess = nTime;
|
||||
info.nLastTry = nTime;
|
||||
info.nTime = nTime;
|
||||
info.nAttempts = 0;
|
||||
// nTime is not updated here, to avoid leaking information about
|
||||
// currently-connected peers.
|
||||
|
||||
// if it is already in the tried set, don't do anything else
|
||||
if (info.fInTried)
|
||||
return;
|
||||
|
||||
// find a bucket it is in now
|
||||
int nRnd = GetRandInt(vvNew.size());
|
||||
int nRnd = GetRandInt(ADDRMAN_NEW_BUCKET_COUNT);
|
||||
int nUBucket = -1;
|
||||
for (unsigned int n = 0; n < vvNew.size(); n++)
|
||||
{
|
||||
int nB = (n+nRnd) % vvNew.size();
|
||||
std::set<int> &vNew = vvNew[nB];
|
||||
if (vNew.count(nId))
|
||||
{
|
||||
for (unsigned int n = 0; n < ADDRMAN_NEW_BUCKET_COUNT; n++) {
|
||||
int nB = (n + nRnd) % ADDRMAN_NEW_BUCKET_COUNT;
|
||||
int nBpos = info.GetBucketPosition(nKey, true, nB);
|
||||
if (vvNew[nB][nBpos] == nId) {
|
||||
nUBucket = nB;
|
||||
break;
|
||||
}
|
||||
|
@ -307,7 +238,7 @@ void CAddrMan::Good_(const CService &addr, int64_t nTime)
|
|||
LogPrint("addrman", "Moving %s to tried\n", addr.ToString());
|
||||
|
||||
// move nId to the tried tables
|
||||
MakeTried(info, nId, nUBucket);
|
||||
MakeTried(info, nId);
|
||||
}
|
||||
|
||||
bool CAddrMan::Add_(const CAddress &addr, const CNetAddr& source, int64_t nTimePenalty)
|
||||
|
@ -356,13 +287,25 @@ bool CAddrMan::Add_(const CAddress &addr, const CNetAddr& source, int64_t nTimeP
|
|||
}
|
||||
|
||||
int nUBucket = pinfo->GetNewBucket(nKey, source);
|
||||
std::set<int> &vNew = vvNew[nUBucket];
|
||||
if (!vNew.count(nId))
|
||||
{
|
||||
pinfo->nRefCount++;
|
||||
if (vNew.size() == ADDRMAN_NEW_BUCKET_SIZE)
|
||||
ShrinkNew(nUBucket);
|
||||
vvNew[nUBucket].insert(nId);
|
||||
int nUBucketPos = pinfo->GetBucketPosition(nKey, true, nUBucket);
|
||||
if (vvNew[nUBucket][nUBucketPos] != nId) {
|
||||
bool fInsert = vvNew[nUBucket][nUBucketPos] == -1;
|
||||
if (!fInsert) {
|
||||
CAddrInfo& infoExisting = mapInfo[vvNew[nUBucket][nUBucketPos]];
|
||||
if (infoExisting.IsTerrible() || (infoExisting.nRefCount > 1 && pinfo->nRefCount == 0)) {
|
||||
// Overwrite the existing new table entry.
|
||||
fInsert = true;
|
||||
}
|
||||
}
|
||||
if (fInsert) {
|
||||
ClearNew(nUBucket, nUBucketPos);
|
||||
pinfo->nRefCount++;
|
||||
vvNew[nUBucket][nUBucketPos] = nId;
|
||||
} else {
|
||||
if (pinfo->nRefCount == 0) {
|
||||
Delete(nId);
|
||||
}
|
||||
}
|
||||
}
|
||||
return fNew;
|
||||
}
|
||||
|
@ -386,44 +329,39 @@ void CAddrMan::Attempt_(const CService &addr, int64_t nTime)
|
|||
info.nAttempts++;
|
||||
}
|
||||
|
||||
CAddress CAddrMan::Select_(int nUnkBias)
|
||||
CAddress CAddrMan::Select_()
|
||||
{
|
||||
if (size() == 0)
|
||||
return CAddress();
|
||||
|
||||
double nCorTried = sqrt(nTried) * (100.0 - nUnkBias);
|
||||
double nCorNew = sqrt(nNew) * nUnkBias;
|
||||
if ((nCorTried + nCorNew)*GetRandInt(1<<30)/(1<<30) < nCorTried)
|
||||
{
|
||||
// Use a 50% chance for choosing between tried and new table entries.
|
||||
if (nTried > 0 && (nNew == 0 || GetRandInt(2) == 0)) {
|
||||
// use a tried node
|
||||
double fChanceFactor = 1.0;
|
||||
while(1)
|
||||
{
|
||||
int nKBucket = GetRandInt(vvTried.size());
|
||||
std::vector<int> &vTried = vvTried[nKBucket];
|
||||
if (vTried.size() == 0) continue;
|
||||
int nPos = GetRandInt(vTried.size());
|
||||
assert(mapInfo.count(vTried[nPos]) == 1);
|
||||
CAddrInfo &info = mapInfo[vTried[nPos]];
|
||||
if (GetRandInt(1<<30) < fChanceFactor*info.GetChance()*(1<<30))
|
||||
while (1) {
|
||||
int nKBucket = GetRandInt(ADDRMAN_TRIED_BUCKET_COUNT);
|
||||
int nKBucketPos = GetRandInt(ADDRMAN_BUCKET_SIZE);
|
||||
if (vvTried[nKBucket][nKBucketPos] == -1)
|
||||
continue;
|
||||
int nId = vvTried[nKBucket][nKBucketPos];
|
||||
assert(mapInfo.count(nId) == 1);
|
||||
CAddrInfo& info = mapInfo[nId];
|
||||
if (GetRandInt(1 << 30) < fChanceFactor * info.GetChance() * (1 << 30))
|
||||
return info;
|
||||
fChanceFactor *= 1.2;
|
||||
}
|
||||
} else {
|
||||
// use a new node
|
||||
double fChanceFactor = 1.0;
|
||||
while(1)
|
||||
{
|
||||
int nUBucket = GetRandInt(vvNew.size());
|
||||
std::set<int> &vNew = vvNew[nUBucket];
|
||||
if (vNew.size() == 0) continue;
|
||||
int nPos = GetRandInt(vNew.size());
|
||||
std::set<int>::iterator it = vNew.begin();
|
||||
while (nPos--)
|
||||
it++;
|
||||
assert(mapInfo.count(*it) == 1);
|
||||
CAddrInfo &info = mapInfo[*it];
|
||||
if (GetRandInt(1<<30) < fChanceFactor*info.GetChance()*(1<<30))
|
||||
while (1) {
|
||||
int nUBucket = GetRandInt(ADDRMAN_NEW_BUCKET_COUNT);
|
||||
int nUBucketPos = GetRandInt(ADDRMAN_BUCKET_SIZE);
|
||||
if (vvNew[nUBucket][nUBucketPos] == -1)
|
||||
continue;
|
||||
int nId = vvNew[nUBucket][nUBucketPos];
|
||||
assert(mapInfo.count(nId) == 1);
|
||||
CAddrInfo& info = mapInfo[nId];
|
||||
if (GetRandInt(1 << 30) < fChanceFactor * info.GetChance() * (1 << 30))
|
||||
return info;
|
||||
fChanceFactor *= 1.2;
|
||||
}
|
||||
|
@ -462,29 +400,37 @@ int CAddrMan::Check_()
|
|||
if (setTried.size() != nTried) return -9;
|
||||
if (mapNew.size() != nNew) return -10;
|
||||
|
||||
for (int n=0; n<vvTried.size(); n++)
|
||||
{
|
||||
std::vector<int> &vTried = vvTried[n];
|
||||
for (std::vector<int>::iterator it = vTried.begin(); it != vTried.end(); it++)
|
||||
{
|
||||
if (!setTried.count(*it)) return -11;
|
||||
setTried.erase(*it);
|
||||
for (int n = 0; n < ADDRMAN_TRIED_BUCKET_COUNT; n++) {
|
||||
for (int i = 0; i < ADDRMAN_BUCKET_SIZE; i++) {
|
||||
if (vvTried[n][i] != -1) {
|
||||
if (!setTried.count(vvTried[n][i]))
|
||||
return -11;
|
||||
if (mapInfo[vvTried[n][i]].GetTriedBucket(nKey) != n)
|
||||
return -17;
|
||||
if (mapInfo[vvTried[n][i]].GetBucketPosition(nKey, false, n) != i)
|
||||
return -18;
|
||||
setTried.erase(vvTried[n][i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int n=0; n<vvNew.size(); n++)
|
||||
{
|
||||
std::set<int> &vNew = vvNew[n];
|
||||
for (std::set<int>::iterator it = vNew.begin(); it != vNew.end(); it++)
|
||||
{
|
||||
if (!mapNew.count(*it)) return -12;
|
||||
if (--mapNew[*it] == 0)
|
||||
mapNew.erase(*it);
|
||||
for (int n = 0; n < ADDRMAN_NEW_BUCKET_COUNT; n++) {
|
||||
for (int i = 0; i < ADDRMAN_BUCKET_SIZE; i++) {
|
||||
if (vvNew[n][i] != -1) {
|
||||
if (!mapNew.count(vvNew[n][i]))
|
||||
return -12;
|
||||
if (mapInfo[vvNew[n][i]].GetBucketPosition(nKey, true, n) != i)
|
||||
return -19;
|
||||
if (--mapNew[vvNew[n][i]] == 0)
|
||||
mapNew.erase(vvNew[n][i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (setTried.size()) return -13;
|
||||
if (mapNew.size()) return -15;
|
||||
if (nKey.IsNull())
|
||||
return -16;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
444
src/addrman.h
444
src/addrman.h
|
@ -8,6 +8,7 @@
|
|||
#include "netbase.h"
|
||||
#include "protocol.h"
|
||||
#include "sync.h"
|
||||
#include "uint256.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <map>
|
||||
|
@ -75,17 +76,20 @@ public:
|
|||
}
|
||||
|
||||
// Calculate in which "tried" bucket this entry belongs
|
||||
int GetTriedBucket(const std::vector<unsigned char> &nKey) const;
|
||||
int GetTriedBucket(const uint256 &nKey) const;
|
||||
|
||||
// Calculate in which "new" bucket this entry belongs, given a certain source
|
||||
int GetNewBucket(const std::vector<unsigned char> &nKey, const CNetAddr& src) const;
|
||||
int GetNewBucket(const uint256 &nKey, const CNetAddr& src) const;
|
||||
|
||||
// Calculate in which "new" bucket this entry belongs, using its default source
|
||||
int GetNewBucket(const std::vector<unsigned char> &nKey) const
|
||||
int GetNewBucket(const uint256 &nKey) const
|
||||
{
|
||||
return GetNewBucket(nKey, source);
|
||||
}
|
||||
|
||||
//! Calculate in which position of a bucket to store this entry.
|
||||
int GetBucketPosition(const uint256 &nKey, bool fNew, int nBucket) const;
|
||||
|
||||
// Determine whether the statistics about this entry are bad enough so that it can just be deleted
|
||||
bool IsTerrible(int64_t nNow = GetAdjustedTime()) const;
|
||||
|
||||
|
@ -94,55 +98,49 @@ public:
|
|||
|
||||
};
|
||||
|
||||
// Stochastic address manager
|
||||
//
|
||||
// Design goals:
|
||||
// * Only keep a limited number of addresses around, so that addr.dat and memory requirements do not grow without bound.
|
||||
// * Keep the address tables in-memory, and asynchronously dump the entire to able in addr.dat.
|
||||
// * Make sure no (localized) attacker can fill the entire table with his nodes/addresses.
|
||||
//
|
||||
// To that end:
|
||||
// * Addresses are organized into buckets.
|
||||
// * Address that have not yet been tried go into 256 "new" buckets.
|
||||
// * Based on the address range (/16 for IPv4) of source of the information, 32 buckets are selected at random
|
||||
// * The actual bucket is chosen from one of these, based on the range the address itself is located.
|
||||
// * One single address can occur in up to 4 different buckets, to increase selection chances for addresses that
|
||||
// are seen frequently. The chance for increasing this multiplicity decreases exponentially.
|
||||
// * When adding a new address to a full bucket, a randomly chosen entry (with a bias favoring less recently seen
|
||||
// ones) is removed from it first.
|
||||
// * Addresses of nodes that are known to be accessible go into 64 "tried" buckets.
|
||||
// * Each address range selects at random 4 of these buckets.
|
||||
// * The actual bucket is chosen from one of these, based on the full address.
|
||||
// * When adding a new good address to a full bucket, a randomly chosen entry (with a bias favoring less recently
|
||||
// tried ones) is evicted from it, back to the "new" buckets.
|
||||
// * Bucket selection is based on cryptographic hashing, using a randomly-generated 256-bit key, which should not
|
||||
// be observable by adversaries.
|
||||
// * Several indexes are kept for high performance. Defining DEBUG_ADDRMAN will introduce frequent (and expensive)
|
||||
// consistency checks for the entire data structure.
|
||||
/** Stochastic address manager
|
||||
*
|
||||
* Design goals:
|
||||
* * Keep the address tables in-memory, and asynchronously dump the entire to able in peers.dat.
|
||||
* * Make sure no (localized) attacker can fill the entire table with his nodes/addresses.
|
||||
*
|
||||
* To that end:
|
||||
* * Addresses are organized into buckets.
|
||||
* * Address that have not yet been tried go into 1024 "new" buckets.
|
||||
* * Based on the address range (/16 for IPv4) of source of the information, 64 buckets are selected at random
|
||||
* * The actual bucket is chosen from one of these, based on the range the address itself is located.
|
||||
* * One single address can occur in up to 8 different buckets, to increase selection chances for addresses that
|
||||
* are seen frequently. The chance for increasing this multiplicity decreases exponentially.
|
||||
* * When adding a new address to a full bucket, a randomly chosen entry (with a bias favoring less recently seen
|
||||
* ones) is removed from it first.
|
||||
* * Addresses of nodes that are known to be accessible go into 256 "tried" buckets.
|
||||
* * Each address range selects at random 8 of these buckets.
|
||||
* * The actual bucket is chosen from one of these, based on the full address.
|
||||
* * When adding a new good address to a full bucket, a randomly chosen entry (with a bias favoring less recently
|
||||
* tried ones) is evicted from it, back to the "new" buckets.
|
||||
* * Bucket selection is based on cryptographic hashing, using a randomly-generated 256-bit key, which should not
|
||||
* be observable by adversaries.
|
||||
* * Several indexes are kept for high performance. Defining DEBUG_ADDRMAN will introduce frequent (and expensive)
|
||||
* consistency checks for the entire data structure.
|
||||
*/
|
||||
|
||||
// total number of buckets for tried addresses
|
||||
#define ADDRMAN_TRIED_BUCKET_COUNT 64
|
||||
|
||||
// maximum allowed number of entries in buckets for tried addresses
|
||||
#define ADDRMAN_TRIED_BUCKET_SIZE 64
|
||||
#define ADDRMAN_TRIED_BUCKET_COUNT 256
|
||||
|
||||
// total number of buckets for new addresses
|
||||
#define ADDRMAN_NEW_BUCKET_COUNT 256
|
||||
#define ADDRMAN_NEW_BUCKET_COUNT 1024
|
||||
|
||||
// maximum allowed number of entries in buckets for new addresses
|
||||
#define ADDRMAN_NEW_BUCKET_SIZE 64
|
||||
// maximum allowed number of entries in buckets for new and tried addresses
|
||||
#define ADDRMAN_BUCKET_SIZE 64
|
||||
|
||||
// over how many buckets entries with tried addresses from a single group (/16 for IPv4) are spread
|
||||
#define ADDRMAN_TRIED_BUCKETS_PER_GROUP 4
|
||||
#define ADDRMAN_TRIED_BUCKETS_PER_GROUP 8
|
||||
|
||||
// over how many buckets entries with new addresses originating from a single group are spread
|
||||
#define ADDRMAN_NEW_BUCKETS_PER_SOURCE_GROUP 32
|
||||
#define ADDRMAN_NEW_BUCKETS_PER_SOURCE_GROUP 64
|
||||
|
||||
// in how many buckets for entries with new addresses a single address may occur
|
||||
#define ADDRMAN_NEW_BUCKETS_PER_ADDRESS 4
|
||||
|
||||
// how many entries in a bucket with tried addresses are inspected, when selecting one to replace
|
||||
#define ADDRMAN_TRIED_ENTRIES_INSPECT_ON_EVICT 4
|
||||
#define ADDRMAN_NEW_BUCKETS_PER_ADDRESS 8
|
||||
|
||||
// how old addresses can maximally be
|
||||
#define ADDRMAN_HORIZON_DAYS 30
|
||||
|
@ -170,7 +168,7 @@ private:
|
|||
mutable CCriticalSection cs;
|
||||
|
||||
// secret key to randomize bucket select with
|
||||
std::vector<unsigned char> nKey;
|
||||
uint256 nKey;
|
||||
|
||||
// last used nId
|
||||
int nIdCount;
|
||||
|
@ -188,13 +186,13 @@ private:
|
|||
int nTried;
|
||||
|
||||
// list of "tried" buckets
|
||||
std::vector<std::vector<int> > vvTried;
|
||||
int vvTried[ADDRMAN_TRIED_BUCKET_COUNT][ADDRMAN_BUCKET_SIZE];
|
||||
|
||||
// number of (unique) "new" entries
|
||||
int nNew;
|
||||
|
||||
// list of "new" buckets
|
||||
std::vector<std::set<int> > vvNew;
|
||||
int vvNew[ADDRMAN_NEW_BUCKET_COUNT][ADDRMAN_BUCKET_SIZE];
|
||||
|
||||
protected:
|
||||
|
||||
|
@ -208,17 +206,14 @@ protected:
|
|||
// Swap two elements in vRandom.
|
||||
void SwapRandom(unsigned int nRandomPos1, unsigned int nRandomPos2);
|
||||
|
||||
// Return position in given bucket to replace.
|
||||
int SelectTried(int nKBucket);
|
||||
|
||||
// Remove an element from a "new" bucket.
|
||||
// This is the only place where actual deletes occur.
|
||||
// They are never deleted while in the "tried" table, only possibly evicted back to the "new" table.
|
||||
int ShrinkNew(int nUBucket);
|
||||
|
||||
// Move an entry from the "new" table(s) to the "tried" table
|
||||
// @pre vvUnkown[nOrigin].count(nId) != 0
|
||||
void MakeTried(CAddrInfo& info, int nId, int nOrigin);
|
||||
void MakeTried(CAddrInfo& info, int nId);
|
||||
|
||||
// Delete an entry. It must not be in tried, and have refcount 0.
|
||||
void Delete(int nId);
|
||||
|
||||
// Clear a position in a "new" table. This is the only place where entries are actually deleted.
|
||||
void ClearNew(int nUBucket, int nUBucketPos);
|
||||
|
||||
// Mark an entry "good", possibly moving it from "new" to "tried".
|
||||
void Good_(const CService &addr, int64_t nTime);
|
||||
|
@ -230,8 +225,7 @@ protected:
|
|||
void Attempt_(const CService &addr, int64_t nTime);
|
||||
|
||||
// Select an address to connect to.
|
||||
// nUnkBias determines how much to favor new addresses over tried ones (min=0, max=100)
|
||||
CAddress Select_(int nUnkBias);
|
||||
CAddress Select_();
|
||||
|
||||
#ifdef DEBUG_ADDRMAN
|
||||
// Perform consistency check. Returns an error code or zero.
|
||||
|
@ -245,153 +239,220 @@ protected:
|
|||
void Connected_(const CService &addr, int64_t nTime);
|
||||
|
||||
public:
|
||||
/**
|
||||
* serialized format:
|
||||
* * version byte (currently 1)
|
||||
* * 0x20 + nKey (serialized as if it were a vector, for backward compatibility)
|
||||
* * nNew
|
||||
* * nTried
|
||||
* * number of "new" buckets XOR 2**30
|
||||
* * all nNew addrinfos in vvNew
|
||||
* * all nTried addrinfos in vvTried
|
||||
* * for each bucket:
|
||||
* * number of elements
|
||||
* * for each element: index
|
||||
*
|
||||
* 2**30 is xorred with the number of buckets to make addrman deserializer v0 detect it
|
||||
* as incompatible. This is necessary because it did not check the version number on
|
||||
* deserialization.
|
||||
*
|
||||
* Notice that vvTried, mapAddr and vVector are never encoded explicitly;
|
||||
* they are instead reconstructed from the other information.
|
||||
*
|
||||
* vvNew is serialized, but only used if ADDRMAN_UNKOWN_BUCKET_COUNT didn't change,
|
||||
* otherwise it is reconstructed as well.
|
||||
*
|
||||
* This format is more complex, but significantly smaller (at most 1.5 MiB), and supports
|
||||
* changes to the ADDRMAN_ parameters without breaking the on-disk structure.
|
||||
*
|
||||
* We don't use ADD_SERIALIZE_METHODS since the serialization and deserialization code has
|
||||
* very little in common.
|
||||
*/
|
||||
template<typename Stream>
|
||||
void Serialize(Stream &s, int nType, int nVersionDummy) const
|
||||
{
|
||||
LOCK(cs);
|
||||
|
||||
IMPLEMENT_SERIALIZE
|
||||
(({
|
||||
// serialized format:
|
||||
// * version byte (currently 0)
|
||||
// * nKey
|
||||
// * nNew
|
||||
// * nTried
|
||||
// * number of "new" buckets
|
||||
// * all nNew addrinfos in vvNew
|
||||
// * all nTried addrinfos in vvTried
|
||||
// * for each bucket:
|
||||
// * number of elements
|
||||
// * for each element: index
|
||||
//
|
||||
// Notice that vvTried, mapAddr and vVector are never encoded explicitly;
|
||||
// they are instead reconstructed from the other information.
|
||||
//
|
||||
// vvNew is serialized, but only used if ADDRMAN_UNKOWN_BUCKET_COUNT didn't change,
|
||||
// otherwise it is reconstructed as well.
|
||||
//
|
||||
// This format is more complex, but significantly smaller (at most 1.5 MiB), and supports
|
||||
// changes to the ADDRMAN_ parameters without breaking the on-disk structure.
|
||||
{
|
||||
LOCK(cs);
|
||||
unsigned char nVersion = 0;
|
||||
READWRITE(nVersion);
|
||||
READWRITE(nKey);
|
||||
READWRITE(nNew);
|
||||
READWRITE(nTried);
|
||||
unsigned char nVersion = 1;
|
||||
s << nVersion;
|
||||
s << ((unsigned char)32);
|
||||
s << nKey;
|
||||
s << nNew;
|
||||
s << nTried;
|
||||
|
||||
CAddrMan *am = const_cast<CAddrMan*>(this);
|
||||
if (fWrite)
|
||||
{
|
||||
int nUBuckets = ADDRMAN_NEW_BUCKET_COUNT;
|
||||
READWRITE(nUBuckets);
|
||||
std::map<int, int> mapUnkIds;
|
||||
int nIds = 0;
|
||||
for (std::map<int, CAddrInfo>::iterator it = am->mapInfo.begin(); it != am->mapInfo.end(); it++)
|
||||
{
|
||||
if (nIds == nNew) break; // this means nNew was wrong, oh ow
|
||||
mapUnkIds[(*it).first] = nIds;
|
||||
CAddrInfo &info = (*it).second;
|
||||
if (info.nRefCount)
|
||||
{
|
||||
READWRITE(info);
|
||||
nIds++;
|
||||
}
|
||||
int nUBuckets = ADDRMAN_NEW_BUCKET_COUNT ^ (1 << 30);
|
||||
s << nUBuckets;
|
||||
std::map<int, int> mapUnkIds;
|
||||
int nIds = 0;
|
||||
for (std::map<int, CAddrInfo>::const_iterator it = mapInfo.begin(); it != mapInfo.end(); it++) {
|
||||
mapUnkIds[(*it).first] = nIds;
|
||||
const CAddrInfo &info = (*it).second;
|
||||
if (info.nRefCount) {
|
||||
assert(nIds != nNew); // this means nNew was wrong, oh ow
|
||||
s << info;
|
||||
nIds++;
|
||||
}
|
||||
}
|
||||
nIds = 0;
|
||||
for (std::map<int, CAddrInfo>::const_iterator it = mapInfo.begin(); it != mapInfo.end(); it++) {
|
||||
const CAddrInfo &info = (*it).second;
|
||||
if (info.fInTried) {
|
||||
assert(nIds != nTried); // this means nTried was wrong, oh ow
|
||||
s << info;
|
||||
nIds++;
|
||||
}
|
||||
}
|
||||
for (int bucket = 0; bucket < ADDRMAN_NEW_BUCKET_COUNT; bucket++) {
|
||||
int nSize = 0;
|
||||
for (int i = 0; i < ADDRMAN_BUCKET_SIZE; i++) {
|
||||
if (vvNew[bucket][i] != -1)
|
||||
nSize++;
|
||||
}
|
||||
s << nSize;
|
||||
for (int i = 0; i < ADDRMAN_BUCKET_SIZE; i++) {
|
||||
if (vvNew[bucket][i] != -1) {
|
||||
int nIndex = mapUnkIds[vvNew[bucket][i]];
|
||||
s << nIndex;
|
||||
}
|
||||
nIds = 0;
|
||||
for (std::map<int, CAddrInfo>::iterator it = am->mapInfo.begin(); it != am->mapInfo.end(); it++)
|
||||
{
|
||||
if (nIds == nTried) break; // this means nTried was wrong, oh ow
|
||||
CAddrInfo &info = (*it).second;
|
||||
if (info.fInTried)
|
||||
{
|
||||
READWRITE(info);
|
||||
nIds++;
|
||||
}
|
||||
}
|
||||
for (std::vector<std::set<int> >::iterator it = am->vvNew.begin(); it != am->vvNew.end(); it++)
|
||||
{
|
||||
const std::set<int> &vNew = (*it);
|
||||
int nSize = vNew.size();
|
||||
READWRITE(nSize);
|
||||
for (std::set<int>::iterator it2 = vNew.begin(); it2 != vNew.end(); it2++)
|
||||
{
|
||||
int nIndex = mapUnkIds[*it2];
|
||||
READWRITE(nIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Stream>
|
||||
void Unserialize(Stream& s, int nType, int nVersionDummy)
|
||||
{
|
||||
LOCK(cs);
|
||||
|
||||
Clear();
|
||||
|
||||
unsigned char nVersion;
|
||||
s >> nVersion;
|
||||
unsigned char nKeySize;
|
||||
s >> nKeySize;
|
||||
if (nKeySize != 32) throw std::ios_base::failure("Incorrect keysize in addrman deserialization");
|
||||
s >> nKey;
|
||||
s >> nNew;
|
||||
s >> nTried;
|
||||
int nUBuckets = 0;
|
||||
s >> nUBuckets;
|
||||
if (nVersion != 0) {
|
||||
nUBuckets ^= (1 << 30);
|
||||
}
|
||||
|
||||
// Deserialize entries from the new table.
|
||||
for (int n = 0; n < nNew; n++) {
|
||||
CAddrInfo &info = mapInfo[n];
|
||||
s >> info;
|
||||
mapAddr[info] = n;
|
||||
info.nRandomPos = vRandom.size();
|
||||
vRandom.push_back(n);
|
||||
if (nVersion != 1 || nUBuckets != ADDRMAN_NEW_BUCKET_COUNT) {
|
||||
// In case the new table data cannot be used (nVersion unknown, or bucket count wrong),
|
||||
// immediately try to give them a reference based on their primary source address.
|
||||
int nUBucket = info.GetNewBucket(nKey);
|
||||
int nUBucketPos = info.GetBucketPosition(nKey, true, nUBucket);
|
||||
if (vvNew[nUBucket][nUBucketPos] == -1) {
|
||||
vvNew[nUBucket][nUBucketPos] = n;
|
||||
info.nRefCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
nIdCount = nNew;
|
||||
|
||||
// Deserialize entries from the tried table.
|
||||
int nLost = 0;
|
||||
for (int n = 0; n < nTried; n++) {
|
||||
CAddrInfo info;
|
||||
s >> info;
|
||||
int nKBucket = info.GetTriedBucket(nKey);
|
||||
int nKBucketPos = info.GetBucketPosition(nKey, false, nKBucket);
|
||||
if (vvTried[nKBucket][nKBucketPos] == -1) {
|
||||
info.nRandomPos = vRandom.size();
|
||||
info.fInTried = true;
|
||||
vRandom.push_back(nIdCount);
|
||||
mapInfo[nIdCount] = info;
|
||||
mapAddr[info] = nIdCount;
|
||||
vvTried[nKBucket][nKBucketPos] = nIdCount;
|
||||
nIdCount++;
|
||||
} else {
|
||||
int nUBuckets = 0;
|
||||
READWRITE(nUBuckets);
|
||||
am->nIdCount = 0;
|
||||
am->mapInfo.clear();
|
||||
am->mapAddr.clear();
|
||||
am->vRandom.clear();
|
||||
am->vvTried = std::vector<std::vector<int> >(ADDRMAN_TRIED_BUCKET_COUNT, std::vector<int>(0));
|
||||
am->vvNew = std::vector<std::set<int> >(ADDRMAN_NEW_BUCKET_COUNT, std::set<int>());
|
||||
for (int n = 0; n < am->nNew; n++)
|
||||
{
|
||||
CAddrInfo &info = am->mapInfo[n];
|
||||
READWRITE(info);
|
||||
am->mapAddr[info] = n;
|
||||
info.nRandomPos = vRandom.size();
|
||||
am->vRandom.push_back(n);
|
||||
if (nUBuckets != ADDRMAN_NEW_BUCKET_COUNT)
|
||||
{
|
||||
am->vvNew[info.GetNewBucket(am->nKey)].insert(n);
|
||||
nLost++;
|
||||
}
|
||||
}
|
||||
nTried -= nLost;
|
||||
|
||||
// Deserialize positions in the new table (if possible).
|
||||
for (int bucket = 0; bucket < nUBuckets; bucket++) {
|
||||
int nSize = 0;
|
||||
s >> nSize;
|
||||
for (int n = 0; n < nSize; n++) {
|
||||
int nIndex = 0;
|
||||
s >> nIndex;
|
||||
if (nIndex >= 0 && nIndex < nNew) {
|
||||
CAddrInfo &info = mapInfo[nIndex];
|
||||
int nUBucketPos = info.GetBucketPosition(nKey, true, bucket);
|
||||
if (nVersion == 1 && nUBuckets == ADDRMAN_NEW_BUCKET_COUNT && vvNew[bucket][nUBucketPos] == -1 && info.nRefCount < ADDRMAN_NEW_BUCKETS_PER_ADDRESS) {
|
||||
info.nRefCount++;
|
||||
}
|
||||
}
|
||||
am->nIdCount = am->nNew;
|
||||
int nLost = 0;
|
||||
for (int n = 0; n < am->nTried; n++)
|
||||
{
|
||||
CAddrInfo info;
|
||||
READWRITE(info);
|
||||
std::vector<int> &vTried = am->vvTried[info.GetTriedBucket(am->nKey)];
|
||||
if (vTried.size() < ADDRMAN_TRIED_BUCKET_SIZE)
|
||||
{
|
||||
info.nRandomPos = vRandom.size();
|
||||
info.fInTried = true;
|
||||
am->vRandom.push_back(am->nIdCount);
|
||||
am->mapInfo[am->nIdCount] = info;
|
||||
am->mapAddr[info] = am->nIdCount;
|
||||
vTried.push_back(am->nIdCount);
|
||||
am->nIdCount++;
|
||||
} else {
|
||||
nLost++;
|
||||
}
|
||||
}
|
||||
am->nTried -= nLost;
|
||||
for (int b = 0; b < nUBuckets; b++)
|
||||
{
|
||||
std::set<int> &vNew = am->vvNew[b];
|
||||
int nSize = 0;
|
||||
READWRITE(nSize);
|
||||
for (int n = 0; n < nSize; n++)
|
||||
{
|
||||
int nIndex = 0;
|
||||
READWRITE(nIndex);
|
||||
CAddrInfo &info = am->mapInfo[nIndex];
|
||||
if (nUBuckets == ADDRMAN_NEW_BUCKET_COUNT && info.nRefCount < ADDRMAN_NEW_BUCKETS_PER_ADDRESS)
|
||||
{
|
||||
info.nRefCount++;
|
||||
vNew.insert(nIndex);
|
||||
}
|
||||
vvNew[bucket][nUBucketPos] = nIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});)
|
||||
|
||||
CAddrMan() : vRandom(0), vvTried(ADDRMAN_TRIED_BUCKET_COUNT, std::vector<int>(0)), vvNew(ADDRMAN_NEW_BUCKET_COUNT, std::set<int>())
|
||||
// Prune new entries with refcount 0 (as a result of collisions).
|
||||
int nLostUnk = 0;
|
||||
for (std::map<int, CAddrInfo>::const_iterator it = mapInfo.begin(); it != mapInfo.end(); ) {
|
||||
if (it->second.fInTried == false && it->second.nRefCount == 0) {
|
||||
std::map<int, CAddrInfo>::const_iterator itCopy = it++;
|
||||
Delete(itCopy->first);
|
||||
nLostUnk++;
|
||||
} else {
|
||||
it++;
|
||||
}
|
||||
}
|
||||
if (nLost + nLostUnk > 0) {
|
||||
LogPrint("addrman", "addrman lost %i new and %i tried addresses due to collisions\n", nLostUnk, nLost);
|
||||
}
|
||||
|
||||
Check();
|
||||
}
|
||||
|
||||
unsigned int GetSerializeSize(int nType, int nVersion) const
|
||||
{
|
||||
nKey.resize(32);
|
||||
RAND_bytes(&nKey[0], 32);
|
||||
return (CSizeComputer(nType, nVersion) << *this).size();
|
||||
}
|
||||
|
||||
nIdCount = 0;
|
||||
nTried = 0;
|
||||
nNew = 0;
|
||||
void Clear()
|
||||
{
|
||||
std::vector<int>().swap(vRandom);
|
||||
RAND_bytes((unsigned char *)&nKey, 32);
|
||||
for (size_t bucket = 0; bucket < ADDRMAN_NEW_BUCKET_COUNT; bucket++) {
|
||||
for (size_t entry = 0; entry < ADDRMAN_BUCKET_SIZE; entry++) {
|
||||
vvNew[bucket][entry] = -1;
|
||||
}
|
||||
}
|
||||
for (size_t bucket = 0; bucket < ADDRMAN_TRIED_BUCKET_COUNT; bucket++) {
|
||||
for (size_t entry = 0; entry < ADDRMAN_BUCKET_SIZE; entry++) {
|
||||
vvTried[bucket][entry] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
nIdCount = 0;
|
||||
nTried = 0;
|
||||
nNew = 0;
|
||||
}
|
||||
|
||||
CAddrMan()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
~CAddrMan()
|
||||
{
|
||||
nKey = uint256(0);
|
||||
}
|
||||
|
||||
// Return the number of (unique) addresses in all tables.
|
||||
int size()
|
||||
int size()
|
||||
{
|
||||
return vRandom.size();
|
||||
}
|
||||
|
@ -462,15 +523,16 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
// Choose an address to connect to.
|
||||
// nUnkBias determines how much "new" entries are favored over "tried" ones (0-100).
|
||||
CAddress Select(int nUnkBias = 50)
|
||||
/**
|
||||
* Choose an address to connect to.
|
||||
*/
|
||||
CAddress Select()
|
||||
{
|
||||
CAddress addrRet;
|
||||
{
|
||||
LOCK(cs);
|
||||
Check();
|
||||
addrRet = Select_(nUnkBias);
|
||||
addrRet = Select_();
|
||||
Check();
|
||||
}
|
||||
return addrRet;
|
||||
|
|
15
src/compat.h
15
src/compat.h
|
@ -59,19 +59,4 @@ typedef u_int SOCKET;
|
|||
#define SOCKET_ERROR -1
|
||||
#endif
|
||||
|
||||
inline int myclosesocket(SOCKET& hSocket)
|
||||
{
|
||||
if (hSocket == INVALID_SOCKET)
|
||||
return WSAENOTSOCK;
|
||||
#ifdef WIN32
|
||||
int ret = closesocket(hSocket);
|
||||
#else
|
||||
int ret = close(hSocket);
|
||||
#endif
|
||||
hSocket = INVALID_SOCKET;
|
||||
return ret;
|
||||
}
|
||||
#define closesocket(s) myclosesocket(s)
|
||||
|
||||
|
||||
#endif
|
||||
|
|
61
src/init.cpp
61
src/init.cpp
|
@ -62,7 +62,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),
|
||||
};
|
||||
|
||||
|
||||
|
@ -183,7 +184,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;
|
||||
|
@ -236,9 +237,8 @@ std::string HelpMessage(HelpMessageMode hmm)
|
|||
strUsage += " -onion=<ip:port> " + _("Use separate SOCKS5 proxy to reach peers via Tor hidden services (default: -proxy)") + "\n";
|
||||
strUsage += " -onlynet=<net> " + _("Only connect to nodes in network <net> (IPv4, IPv6 or Tor)") + "\n";
|
||||
strUsage += " -port=<port> " + _("Listen for connections on <port> (default: 22556 or testnet: 44556)") + "\n";
|
||||
strUsage += " -proxy=<ip:port> " + _("Connect through SOCKS proxy") + "\n";
|
||||
strUsage += " -proxy=<ip:port> " + _("Connect through SOCKS5 proxy") + "\n";
|
||||
strUsage += " -seednode=<ip> " + _("Connect to a node to retrieve peer addresses, and disconnect") + "\n";
|
||||
strUsage += " -socks=<n> " + _("Select SOCKS version for -proxy (4 or 5, default: 5)") + "\n";
|
||||
strUsage += " -timeout=<n> " + _("Specify connection timeout in milliseconds (default: 5000)") + "\n";
|
||||
#ifdef USE_UPNP
|
||||
#if USE_UPNP
|
||||
|
@ -247,6 +247,8 @@ std::string HelpMessage(HelpMessageMode hmm)
|
|||
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";
|
||||
|
@ -479,11 +481,11 @@ bool AppInit2(boost::thread_group& threadGroup)
|
|||
nLocalServices |= NODE_BLOOM;
|
||||
}
|
||||
|
||||
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) {
|
||||
|
@ -527,7 +529,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);
|
||||
|
@ -549,6 +551,12 @@ bool AppInit2(boost::thread_group& threadGroup)
|
|||
// Check for -debugnet (deprecated)
|
||||
if (GetBoolArg("-debugnet", false))
|
||||
InitWarning(_("Warning: Deprecated argument -debugnet ignored, use -debug=net"));
|
||||
// Check for -socks - as this is a privacy risk to continue, exit here
|
||||
if (mapArgs.count("-socks"))
|
||||
return InitError(_("Error: Unsupported argument -socks found. Setting SOCKS version isn't possible anymore, only SOCKS5 proxies are supported."));
|
||||
// Check for -tor - as this is a privacy risk to continue, exit here
|
||||
if (GetBoolArg("-tor", false))
|
||||
return InitError(_("Error: Unsupported argument -tor found, use -onion."));
|
||||
|
||||
fBenchmark = GetBoolArg("-benchmark", false);
|
||||
mempool.setSanityCheck(GetBoolArg("-checkmempool", RegTest()));
|
||||
|
@ -732,10 +740,6 @@ bool AppInit2(boost::thread_group& threadGroup)
|
|||
|
||||
RegisterNodeSignals(GetNodeSignals());
|
||||
|
||||
int nSocksVersion = GetArg("-socks", 5);
|
||||
if (nSocksVersion != 4 && nSocksVersion != 5)
|
||||
return InitError(strprintf(_("Unknown -socks proxy version requested: %i"), nSocksVersion));
|
||||
|
||||
if (mapArgs.count("-onlynet")) {
|
||||
std::set<enum Network> nets;
|
||||
BOOST_FOREACH(std::string snet, mapMultiArgs["-onlynet"]) {
|
||||
|
@ -751,6 +755,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")) {
|
||||
|
@ -759,12 +772,10 @@ bool AppInit2(boost::thread_group& threadGroup)
|
|||
return InitError(strprintf(_("Invalid -proxy address: '%s'"), mapArgs["-proxy"]));
|
||||
|
||||
if (!IsLimited(NET_IPV4))
|
||||
SetProxy(NET_IPV4, addrProxy, nSocksVersion);
|
||||
if (nSocksVersion > 4) {
|
||||
if (!IsLimited(NET_IPV6))
|
||||
SetProxy(NET_IPV6, addrProxy, nSocksVersion);
|
||||
SetNameProxy(addrProxy, nSocksVersion);
|
||||
}
|
||||
SetProxy(NET_IPV4, addrProxy);
|
||||
if (!IsLimited(NET_IPV6))
|
||||
SetProxy(NET_IPV6, addrProxy);
|
||||
SetNameProxy(addrProxy);
|
||||
fProxy = true;
|
||||
}
|
||||
|
||||
|
@ -782,24 +793,32 @@ bool AppInit2(boost::thread_group& threadGroup)
|
|||
addrOnion = mapArgs.count("-onion")?CService(mapArgs["-onion"], 9050):CService(mapArgs["-tor"], 9050);
|
||||
if (!addrOnion.IsValid())
|
||||
return InitError(strprintf(_("Invalid -onion address: '%s'"), mapArgs.count("-onion")?mapArgs["-onion"]:mapArgs["-tor"]));
|
||||
SetProxy(NET_TOR, addrOnion, 5);
|
||||
SetProxy(NET_TOR, addrOnion);
|
||||
SetReachable(NET_TOR);
|
||||
}
|
||||
|
||||
// see Step 2: parameter interactions for more information about these
|
||||
fNoListen = !GetBoolArg("-listen", true);
|
||||
fListen = GetBoolArg("-listen", true);
|
||||
fDiscover = GetBoolArg("-discover", true);
|
||||
fNameLookup = GetBoolArg("-dns", true);
|
||||
|
||||
bool fBound = false;
|
||||
if (!fNoListen) {
|
||||
if (mapArgs.count("-bind")) {
|
||||
if (fListen) {
|
||||
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;
|
||||
|
|
62
src/main.cpp
62
src/main.cpp
|
@ -207,9 +207,13 @@ struct CBlockReject {
|
|||
// processing of incoming data is done after the ProcessMessage call returns,
|
||||
// and we're no longer holding the node's locks.
|
||||
struct CNodeState {
|
||||
//! The peer's address
|
||||
CService address;
|
||||
//! Whether we have a fully established connection.
|
||||
bool fCurrentlyConnected;
|
||||
// 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;
|
||||
|
@ -223,6 +227,7 @@ struct CNodeState {
|
|||
int64_t nLastBlockProcess;
|
||||
|
||||
CNodeState() {
|
||||
fCurrentlyConnected = false;
|
||||
nMisbehavior = 0;
|
||||
fShouldBan = false;
|
||||
nBlocksToDownload = 0;
|
||||
|
@ -253,12 +258,16 @@ void InitializeNode(NodeId nodeid, const CNode *pnode) {
|
|||
LOCK(cs_main);
|
||||
CNodeState &state = mapNodeState.insert(std::make_pair(nodeid, CNodeState())).first->second;
|
||||
state.name = pnode->addrName;
|
||||
state.address = pnode->addr;
|
||||
}
|
||||
|
||||
void FinalizeNode(NodeId nodeid) {
|
||||
LOCK(cs_main);
|
||||
CNodeState *state = State(nodeid);
|
||||
|
||||
if (state->nMisbehavior == 0 && state->fCurrentlyConnected) {
|
||||
AddressCurrentlyConnected(state->address);
|
||||
}
|
||||
BOOST_FOREACH(const QueuedBlock& entry, state->vBlocksInFlight)
|
||||
mapBlocksInFlight.erase(entry.hash);
|
||||
BOOST_FOREACH(const uint256& hash, state->vBlocksToDownload)
|
||||
|
@ -1571,7 +1580,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;
|
||||
|
@ -3737,7 +3747,7 @@ void static ProcessGetData(CNode* pfrom)
|
|||
}
|
||||
}
|
||||
|
||||
bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
|
||||
bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv, int64_t nTimeReceived)
|
||||
{
|
||||
RandAddSeedPerfmon();
|
||||
LogPrint("net", "received: %s (%" PRIszu" bytes)\n", strCommand, vRecv.size());
|
||||
|
@ -3822,7 +3832,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
|
|||
if (!pfrom->fInbound)
|
||||
{
|
||||
// Advertise our address
|
||||
if (!fNoListen && !IsInitialBlockDownload())
|
||||
if (fListen && !IsInitialBlockDownload())
|
||||
{
|
||||
CAddress addr = GetLocalAddress(&pfrom->addr);
|
||||
if (addr.IsRoutable())
|
||||
|
@ -3870,6 +3880,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
|
|||
else if (strCommand == "verack")
|
||||
{
|
||||
pfrom->SetRecvVersion(min(pfrom->nVersion, PROTOCOL_VERSION));
|
||||
|
||||
// Mark this node as currently connected, so we update its timestamp later.
|
||||
if (pfrom->fNetworkNode) {
|
||||
LOCK(cs_main);
|
||||
State(pfrom->GetId())->fCurrentlyConnected = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -4163,6 +4179,11 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
|
|||
unsigned int nEvicted = LimitOrphanTxSize(nMaxOrphanTx);
|
||||
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, tx.GetHash());
|
||||
}
|
||||
int nDoS = 0;
|
||||
if (state.IsInvalid(nDoS))
|
||||
|
@ -4257,7 +4278,7 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
|
|||
|
||||
else if (strCommand == "pong")
|
||||
{
|
||||
int64_t pingUsecEnd = GetTimeMicros();
|
||||
int64_t pingUsecEnd = nTimeReceived;
|
||||
uint64_t nonce = 0;
|
||||
size_t nAvail = vRecv.in_avail();
|
||||
bool bPingFinished = false;
|
||||
|
@ -4426,11 +4447,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv)
|
|||
}
|
||||
|
||||
|
||||
// Update the last seen time for this node's address
|
||||
if (pfrom->fNetworkNode)
|
||||
if (strCommand == "version" || strCommand == "addr" || strCommand == "inv" || strCommand == "getdata" || strCommand == "ping")
|
||||
AddressCurrentlyConnected(pfrom->addr);
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -4513,7 +4529,7 @@ bool ProcessMessages(CNode* pfrom)
|
|||
bool fRet = false;
|
||||
try
|
||||
{
|
||||
fRet = ProcessMessage(pfrom, strCommand, vRecv);
|
||||
fRet = ProcessMessage(pfrom, strCommand, vRecv, msg.nTime);
|
||||
boost::this_thread::interruption_point();
|
||||
}
|
||||
catch (std::ios_base::failure& e)
|
||||
|
@ -4572,8 +4588,8 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
|
|||
// RPC ping request by user
|
||||
pingSend = true;
|
||||
}
|
||||
if (pto->nLastSend && GetTime() - pto->nLastSend > 30 * 60 && pto->vSendMsg.empty()) {
|
||||
// Ping automatically sent as a keepalive
|
||||
if (pto->nPingNonceSent == 0 && pto->nPingUsecStart + PING_INTERVAL * 1000000 < GetTimeMicros()) {
|
||||
// Ping automatically sent as a latency probe & keepalive.
|
||||
pingSend = true;
|
||||
}
|
||||
if (pingSend) {
|
||||
|
@ -4581,15 +4597,14 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
|
|||
while (nonce == 0) {
|
||||
RAND_bytes((unsigned char*)&nonce, sizeof(nonce));
|
||||
}
|
||||
pto->nPingNonceSent = nonce;
|
||||
pto->fPingQueued = false;
|
||||
pto->nPingUsecStart = GetTimeMicros();
|
||||
if (pto->nVersion > BIP0031_VERSION) {
|
||||
// Take timestamp as close as possible before transmitting ping
|
||||
pto->nPingUsecStart = GetTimeMicros();
|
||||
pto->nPingNonceSent = nonce;
|
||||
pto->PushMessage("ping", nonce);
|
||||
} else {
|
||||
// Peer is too old to support ping command with nonce, pong will never arrive, disable timing
|
||||
pto->nPingUsecStart = 0;
|
||||
// Peer is too old to support ping command with nonce, pong will never arrive.
|
||||
pto->nPingNonceSent = 0;
|
||||
pto->PushMessage("ping");
|
||||
}
|
||||
}
|
||||
|
@ -4611,7 +4626,7 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
|
|||
pnode->setAddrKnown.clear();
|
||||
|
||||
// Rebroadcast our address
|
||||
if (!fNoListen)
|
||||
if (fListen)
|
||||
{
|
||||
CAddress addr = GetLocalAddress(&pnode->addr);
|
||||
if (addr.IsRoutable())
|
||||
|
@ -4650,11 +4665,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;
|
||||
}
|
||||
|
|
212
src/net.cpp
212
src/net.cpp
|
@ -36,9 +36,31 @@
|
|||
#define MSG_NOSIGNAL 0
|
||||
#endif
|
||||
|
||||
// Fix for ancient MinGW versions, that don't have defined these in ws2tcpip.h.
|
||||
// Todo: Can be removed when our pull-tester is upgraded to a modern MinGW version.
|
||||
#ifdef WIN32
|
||||
#ifndef PROTECTION_LEVEL_UNRESTRICTED
|
||||
#define PROTECTION_LEVEL_UNRESTRICTED 10
|
||||
#endif
|
||||
#ifndef IPV6_PROTECTION_LEVEL
|
||||
#define IPV6_PROTECTION_LEVEL 23
|
||||
#endif
|
||||
#endif
|
||||
|
||||
using namespace std;
|
||||
using namespace boost;
|
||||
|
||||
namespace {
|
||||
const int MAX_OUTBOUND_CONNECTIONS = 8;
|
||||
|
||||
struct ListenSocket {
|
||||
SOCKET socket;
|
||||
bool whitelisted;
|
||||
|
||||
ListenSocket(SOCKET socket, bool whitelisted) : socket(socket), whitelisted(whitelisted) {}
|
||||
};
|
||||
}
|
||||
|
||||
bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOutbound = NULL, const char *strDest = NULL, bool fOneShot = false);
|
||||
|
||||
|
||||
|
@ -46,6 +68,7 @@ bool OpenNetworkConnection(const CAddress& addrConnect, CSemaphoreGrant *grantOu
|
|||
// Global state variables
|
||||
//
|
||||
bool fDiscover = true;
|
||||
bool fListen = true;
|
||||
uint64_t nLocalServices = NODE_NETWORK;
|
||||
CCriticalSection cs_mapLocalHost;
|
||||
map<CNetAddr, LocalServiceInfo> mapLocalHost;
|
||||
|
@ -54,7 +77,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;
|
||||
int nMaxOutboundConnections = 8;
|
||||
|
@ -98,7 +121,7 @@ unsigned short GetListenPort()
|
|||
// find 'best' local address for a particular peer
|
||||
bool GetLocal(CService& addr, const CNetAddr *paddrPeer)
|
||||
{
|
||||
if (fNoListen)
|
||||
if (!fListen)
|
||||
return false;
|
||||
|
||||
int nBestScore = -1;
|
||||
|
@ -313,7 +336,7 @@ bool GetMyExternalIP2(const CService& addrConnect, const char* pszGet, const cha
|
|||
{
|
||||
if (!RecvLine(hSocket, strLine))
|
||||
{
|
||||
closesocket(hSocket);
|
||||
CloseSocket(hSocket);
|
||||
return false;
|
||||
}
|
||||
if (pszKeyword == NULL)
|
||||
|
@ -324,7 +347,7 @@ bool GetMyExternalIP2(const CService& addrConnect, const char* pszGet, const cha
|
|||
break;
|
||||
}
|
||||
}
|
||||
closesocket(hSocket);
|
||||
CloseSocket(hSocket);
|
||||
if (strLine.find("<") != string::npos)
|
||||
strLine = strLine.substr(0, strLine.find("<"));
|
||||
strLine = strLine.substr(strspn(strLine.c_str(), " \t\n\r"));
|
||||
|
@ -338,7 +361,7 @@ bool GetMyExternalIP2(const CService& addrConnect, const char* pszGet, const cha
|
|||
return true;
|
||||
}
|
||||
}
|
||||
closesocket(hSocket);
|
||||
CloseSocket(hSocket);
|
||||
return error("GetMyExternalIP() : connection closed");
|
||||
}
|
||||
|
||||
|
@ -499,8 +522,7 @@ void CNode::CloseSocketDisconnect()
|
|||
if (hSocket != INVALID_SOCKET)
|
||||
{
|
||||
LogPrint("net", "disconnecting node %s\n", addrName);
|
||||
closesocket(hSocket);
|
||||
hSocket = INVALID_SOCKET;
|
||||
CloseSocket(hSocket);
|
||||
}
|
||||
|
||||
// in case this fails, we'll empty the recv buffer when the CNode is deleted
|
||||
|
@ -513,11 +535,6 @@ void CNode::CloseSocketDisconnect()
|
|||
pnodeSync = NULL;
|
||||
}
|
||||
|
||||
void CNode::Cleanup()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void CNode::PushVersion()
|
||||
{
|
||||
int nBestHeight = g_signals.GetHeight().get_value_or(0);
|
||||
|
@ -570,6 +587,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)
|
||||
|
@ -586,6 +621,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,
|
||||
|
@ -630,8 +666,16 @@ bool CNode::ReceiveMsgBytes(const char *pch, unsigned int nBytes)
|
|||
if (handled < 0)
|
||||
return false;
|
||||
|
||||
if (msg.in_data && msg.hdr.nMessageSize > MAX_PROTOCOL_MESSAGE_LENGTH) {
|
||||
LogPrint("net", "Oversized message from peer=%i, disconnecting", GetId());
|
||||
return false;
|
||||
}
|
||||
|
||||
pch += handled;
|
||||
nBytes -= handled;
|
||||
|
||||
if (msg.complete())
|
||||
msg.nTime = GetTimeMicros();
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -759,7 +803,6 @@ void ThreadSocketHandler()
|
|||
|
||||
// close socket and cleanup
|
||||
pnode->CloseSocketDisconnect();
|
||||
pnode->Cleanup();
|
||||
|
||||
// hold in disconnected pool until all refs are released
|
||||
if (pnode->fNetworkNode || pnode->fInbound)
|
||||
|
@ -803,7 +846,6 @@ void ThreadSocketHandler()
|
|||
uiInterface.NotifyNumConnectionsChanged(nPrevNodeCount);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Find which sockets have data to receive
|
||||
//
|
||||
|
@ -820,11 +862,12 @@ 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;
|
||||
}
|
||||
|
||||
{
|
||||
LOCK(cs_vNodes);
|
||||
BOOST_FOREACH(CNode* pnode, vNodes)
|
||||
|
@ -885,58 +928,61 @@ void ThreadSocketHandler()
|
|||
MilliSleep(timeout.tv_usec/1000);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Accept new connections
|
||||
//
|
||||
BOOST_FOREACH(SOCKET hListenSocket, vhListenSocket)
|
||||
if (hListenSocket != INVALID_SOCKET && FD_ISSET(hListenSocket, &fdsetRecv))
|
||||
BOOST_FOREACH(const ListenSocket& hListenSocket, vhListenSocket)
|
||||
{
|
||||
struct sockaddr_storage sockaddr;
|
||||
socklen_t len = sizeof(sockaddr);
|
||||
SOCKET hSocket = accept(hListenSocket, (struct sockaddr*)&sockaddr, &len);
|
||||
CAddress addr;
|
||||
int nInbound = 0;
|
||||
if (hListenSocket.socket != INVALID_SOCKET && FD_ISSET(hListenSocket.socket, &fdsetRecv))
|
||||
{
|
||||
struct sockaddr_storage sockaddr;
|
||||
socklen_t len = sizeof(sockaddr);
|
||||
SOCKET hSocket = accept(hListenSocket.socket, (struct sockaddr*)&sockaddr, &len);
|
||||
CAddress addr;
|
||||
int nInbound = 0;
|
||||
|
||||
if (hSocket != INVALID_SOCKET)
|
||||
if (!addr.SetSockAddr((const struct sockaddr*)&sockaddr))
|
||||
LogPrintf("Warning: Unknown socket family\n");
|
||||
if (hSocket != INVALID_SOCKET)
|
||||
if (!addr.SetSockAddr((const struct sockaddr*)&sockaddr))
|
||||
LogPrintf("Warning: Unknown socket family\n");
|
||||
|
||||
{
|
||||
LOCK(cs_vNodes);
|
||||
BOOST_FOREACH(CNode* pnode, vNodes)
|
||||
if (pnode->fInbound)
|
||||
nInbound++;
|
||||
}
|
||||
|
||||
if (hSocket == INVALID_SOCKET)
|
||||
{
|
||||
int nErr = WSAGetLastError();
|
||||
if (nErr != WSAEWOULDBLOCK)
|
||||
LogPrintf("socket error accept failed: %s\n", NetworkErrorString(nErr));
|
||||
}
|
||||
else if (nInbound >= nMaxConnections - nMaxOutboundConnections)
|
||||
{
|
||||
closesocket(hSocket);
|
||||
}
|
||||
else if (CNode::IsBanned(addr))
|
||||
{
|
||||
LogPrintf("connection from %s dropped (banned)\n", addr.ToString());
|
||||
closesocket(hSocket);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint("net", "accepted connection %s\n", addr.ToString());
|
||||
CNode* pnode = new CNode(hSocket, addr, "", true);
|
||||
pnode->AddRef();
|
||||
bool whitelisted = hListenSocket.whitelisted || CNode::IsWhitelistedRange(addr);
|
||||
{
|
||||
LOCK(cs_vNodes);
|
||||
vNodes.push_back(pnode);
|
||||
BOOST_FOREACH(CNode* pnode, vNodes)
|
||||
if (pnode->fInbound)
|
||||
nInbound++;
|
||||
}
|
||||
|
||||
if (hSocket == INVALID_SOCKET)
|
||||
{
|
||||
int nErr = WSAGetLastError();
|
||||
if (nErr != WSAEWOULDBLOCK)
|
||||
LogPrintf("socket error accept failed: %s\n", NetworkErrorString(nErr));
|
||||
}
|
||||
else if (nInbound >= nMaxConnections - MAX_OUTBOUND_CONNECTIONS)
|
||||
{
|
||||
CloseSocket(hSocket);
|
||||
}
|
||||
else if (CNode::IsBanned(addr) && !whitelisted)
|
||||
{
|
||||
LogPrintf("connection from %s dropped (banned)\n", addr.ToString());
|
||||
CloseSocket(hSocket);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogPrint("net", "accepted connection %s\n", addr.ToString());
|
||||
CNode* pnode = new CNode(hSocket, addr, "", true);
|
||||
pnode->AddRef();
|
||||
pnode->fWhitelisted = whitelisted;
|
||||
|
||||
{
|
||||
LOCK(cs_vNodes);
|
||||
vNodes.push_back(pnode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Service each socket
|
||||
//
|
||||
|
@ -1010,23 +1056,27 @@ void ThreadSocketHandler()
|
|||
//
|
||||
// Inactivity checking
|
||||
//
|
||||
if (pnode->vSendMsg.empty())
|
||||
pnode->nLastSendEmpty = GetTime();
|
||||
if (GetTime() - pnode->nTimeConnected > 60)
|
||||
int64_t nTime = GetTime();
|
||||
if (nTime - pnode->nTimeConnected > 60)
|
||||
{
|
||||
if (pnode->nLastRecv == 0 || pnode->nLastSend == 0)
|
||||
{
|
||||
LogPrint("net", "socket no message in first 60 seconds, %d %d\n", pnode->nLastRecv != 0, pnode->nLastSend != 0);
|
||||
pnode->fDisconnect = true;
|
||||
}
|
||||
else if (GetTime() - pnode->nLastSend > 90*60 && GetTime() - pnode->nLastSendEmpty > 90*60)
|
||||
else if (nTime - pnode->nLastSend > TIMEOUT_INTERVAL)
|
||||
{
|
||||
LogPrintf("socket not sending\n");
|
||||
LogPrintf("socket sending timeout: %is\n", nTime - pnode->nLastSend);
|
||||
pnode->fDisconnect = true;
|
||||
}
|
||||
else if (GetTime() - pnode->nLastRecv > 90*60)
|
||||
else if (nTime - pnode->nLastRecv > (pnode->nVersion > BIP0031_VERSION ? TIMEOUT_INTERVAL : 90*60))
|
||||
{
|
||||
LogPrintf("socket inactivity timeout\n");
|
||||
LogPrintf("socket receive timeout: %is\n", nTime - pnode->nLastRecv);
|
||||
pnode->fDisconnect = true;
|
||||
}
|
||||
else if (pnode->nPingNonceSent && pnode->nPingUsecStart + TIMEOUT_INTERVAL * 1000000 < GetTimeMicros())
|
||||
{
|
||||
LogPrintf("ping timeout: %fs\n", 0.000001 * (GetTimeMicros() - pnode->nPingUsecStart));
|
||||
pnode->fDisconnect = true;
|
||||
}
|
||||
}
|
||||
|
@ -1310,8 +1360,7 @@ void ThreadOpenConnections()
|
|||
int nTries = 0;
|
||||
while (true)
|
||||
{
|
||||
// use an nUnkBias between 10 (no outgoing connections) and 90 (8 outgoing connections)
|
||||
CAddress addr = addrman.Select(10 + min(nOutbound,8)*10);
|
||||
CAddress addr = addrman.Select();
|
||||
|
||||
// if we selected an invalid address, restart
|
||||
if (!addr.IsValid() || setConnected.count(addr.GetGroup()) || IsLocal(addr))
|
||||
|
@ -1557,7 +1606,7 @@ void ThreadMessageHandler()
|
|||
|
||||
|
||||
|
||||
bool BindListenPort(const CService &addrBind, string& strError)
|
||||
bool BindListenPort(const CService &addrBind, string& strError, bool fWhitelisted)
|
||||
{
|
||||
strError = "";
|
||||
int nOne = 1;
|
||||
|
@ -1580,18 +1629,16 @@ bool BindListenPort(const CService &addrBind, string& strError)
|
|||
return false;
|
||||
}
|
||||
|
||||
#ifndef WIN32
|
||||
#ifdef SO_NOSIGPIPE
|
||||
// Different way of disabling SIGPIPE on BSD
|
||||
setsockopt(hListenSocket, SOL_SOCKET, SO_NOSIGPIPE, (void*)&nOne, sizeof(int));
|
||||
#endif
|
||||
|
||||
#ifndef WIN32
|
||||
// Allow binding if the port is still in TIME_WAIT state after
|
||||
// the program was closed and restarted. Not an issue on windows.
|
||||
// the program was closed and restarted. Not an issue on windows!
|
||||
setsockopt(hListenSocket, SOL_SOCKET, SO_REUSEADDR, (void*)&nOne, sizeof(int));
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef WIN32
|
||||
// Set to non-blocking, incoming connections will also inherit this
|
||||
if (ioctlsocket(hListenSocket, FIONBIO, (u_long*)&nOne) == SOCKET_ERROR)
|
||||
|
@ -1615,10 +1662,8 @@ bool BindListenPort(const CService &addrBind, string& strError)
|
|||
#endif
|
||||
#endif
|
||||
#ifdef WIN32
|
||||
int nProtLevel = 10 /* PROTECTION_LEVEL_UNRESTRICTED */;
|
||||
int nParameterId = 23 /* IPV6_PROTECTION_LEVEl */;
|
||||
// this call is allowed to fail
|
||||
setsockopt(hListenSocket, IPPROTO_IPV6, nParameterId, (const char*)&nProtLevel, sizeof(int));
|
||||
int nProtLevel = PROTECTION_LEVEL_UNRESTRICTED;
|
||||
setsockopt(hListenSocket, IPPROTO_IPV6, IPV6_PROTECTION_LEVEL, (const char*)&nProtLevel, sizeof(int));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -1630,6 +1675,7 @@ bool BindListenPort(const CService &addrBind, string& strError)
|
|||
else
|
||||
strError = strprintf(_("Unable to bind to %s on this computer (bind returned error %s)"), addrBind.ToString(), NetworkErrorString(nErr));
|
||||
LogPrintf("%s\n", strError);
|
||||
CloseSocket(hListenSocket);
|
||||
return false;
|
||||
}
|
||||
LogPrintf("Bound to %s\n", addrBind.ToString());
|
||||
|
@ -1639,12 +1685,13 @@ bool BindListenPort(const CService &addrBind, string& strError)
|
|||
{
|
||||
strError = strprintf(_("Error: Listening for incoming connections failed (listen returned error %s)"), NetworkErrorString(WSAGetLastError()));
|
||||
LogPrintf("%s\n", strError);
|
||||
CloseSocket(hListenSocket);
|
||||
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;
|
||||
|
@ -1771,11 +1818,11 @@ public:
|
|||
// Close sockets
|
||||
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)
|
||||
LogPrintf("closesocket(hListenSocket) failed with error %s\n", NetworkErrorString(WSAGetLastError()));
|
||||
CloseSocket(pnode->hSocket);
|
||||
BOOST_FOREACH(ListenSocket& hListenSocket, vhListenSocket)
|
||||
if (hListenSocket.socket != INVALID_SOCKET)
|
||||
if (!CloseSocket(hListenSocket.socket))
|
||||
LogPrintf("CloseSocket(hListenSocket) failed with error %s\n", NetworkErrorString(WSAGetLastError()));
|
||||
|
||||
// clean up some globals (to help leak detection)
|
||||
BOOST_FOREACH(CNode *pnode, vNodes)
|
||||
|
@ -1784,6 +1831,7 @@ public:
|
|||
delete pnode;
|
||||
vNodes.clear();
|
||||
vNodesDisconnected.clear();
|
||||
vhListenSocket.clear();
|
||||
delete semOutbound;
|
||||
semOutbound = NULL;
|
||||
delete pnodeLocalHost;
|
||||
|
|
37
src/net.h
37
src/net.h
|
@ -28,6 +28,7 @@
|
|||
#include <boost/signals2/signal.hpp>
|
||||
#include <openssl/rand.h>
|
||||
|
||||
|
||||
class CAddrMan;
|
||||
class CBlockIndex;
|
||||
class CNode;
|
||||
|
@ -36,8 +37,14 @@ namespace boost {
|
|||
class thread_group;
|
||||
}
|
||||
|
||||
/** Time between pings automatically sent out for latency probing and keepalive (in seconds). */
|
||||
static const int PING_INTERVAL = 2 * 60;
|
||||
/** Time after which to disconnect, after waiting for a ping response (or inactivity). */
|
||||
static const int TIMEOUT_INTERVAL = 20 * 60;
|
||||
/** The maximum number of entries in an 'inv' protocol message */
|
||||
static const unsigned int MAX_INV_SZ = 50000;
|
||||
/** Maximum length of incoming protocol messages (no message over 2 MiB is currently acceptable). */
|
||||
static const unsigned int MAX_PROTOCOL_MESSAGE_LENGTH = 2 * 1024 * 1024;
|
||||
/** The maximum number of entries in mapAskFor */
|
||||
static const size_t MAPASKFOR_MAX_SZ = MAX_INV_SZ;
|
||||
|
||||
|
@ -53,7 +60,7 @@ CNode* FindNode(const CService& ip);
|
|||
CNode* ConnectNode(CAddress addrConnect, const char *strDest = NULL);
|
||||
void MapPort(bool fUseUPnP);
|
||||
unsigned short GetListenPort();
|
||||
bool BindListenPort(const CService &bindAddr, std::string& strError=REF(std::string()));
|
||||
bool BindListenPort(const CService &bindAddr, std::string& strError, bool fWhitelisted = false);
|
||||
void StartNode(boost::thread_group& threadGroup);
|
||||
bool StopNode();
|
||||
void SocketSendData(CNode *pnode);
|
||||
|
@ -100,6 +107,7 @@ CAddress GetLocalAddress(const CNetAddr *paddrPeer = NULL);
|
|||
|
||||
|
||||
extern bool fDiscover;
|
||||
extern bool fListen;
|
||||
extern uint64_t nLocalServices;
|
||||
extern uint64_t nLocalHostNonce;
|
||||
extern CAddrMan addrman;
|
||||
|
@ -143,6 +151,7 @@ public:
|
|||
uint64_t nSendBytes;
|
||||
uint64_t nRecvBytes;
|
||||
bool fSyncNode;
|
||||
bool fWhitelisted;
|
||||
double dPingTime;
|
||||
double dPingWait;
|
||||
std::string addrLocal;
|
||||
|
@ -162,11 +171,14 @@ public:
|
|||
CDataStream vRecv; // received message data
|
||||
unsigned int nDataPos;
|
||||
|
||||
int64_t nTime; // time (in microseconds) of message receipt.
|
||||
|
||||
CNetMessage(int nTypeIn, int nVersionIn) : hdrbuf(nTypeIn, nVersionIn), vRecv(nTypeIn, nVersionIn) {
|
||||
hdrbuf.resize(24);
|
||||
in_data = false;
|
||||
nHdrPos = 0;
|
||||
nDataPos = 0;
|
||||
nTime = 0;
|
||||
}
|
||||
|
||||
bool complete() const
|
||||
|
@ -212,7 +224,6 @@ public:
|
|||
|
||||
int64_t nLastSend;
|
||||
int64_t nLastRecv;
|
||||
int64_t nLastSendEmpty;
|
||||
int64_t nTimeConnected;
|
||||
CAddress addr;
|
||||
std::string addrName;
|
||||
|
@ -223,6 +234,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;
|
||||
|
@ -246,6 +258,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
|
||||
|
||||
|
@ -268,10 +285,14 @@ public:
|
|||
CCriticalSection cs_inventory;
|
||||
std::multimap<int64_t, CInv> mapAskFor;
|
||||
|
||||
// Ping time measurement
|
||||
// Ping time measurement:
|
||||
// The pong reply we're expecting, or 0 if no pong expected.
|
||||
uint64_t nPingNonceSent;
|
||||
// Time (in usec) the last ping was sent, or 0 if no ping was ever sent.
|
||||
int64_t nPingUsecStart;
|
||||
// Last measured round-trip time.
|
||||
int64_t nPingUsecTime;
|
||||
// Whether a ping is requested.
|
||||
bool fPingQueued;
|
||||
|
||||
CNode(SOCKET hSocketIn, CAddress addrIn, std::string addrNameIn = "", bool fInboundIn=false) : ssSend(SER_NETWORK, INIT_PROTO_VERSION), setAddrKnown(5000)
|
||||
|
@ -283,12 +304,12 @@ public:
|
|||
nLastRecv = 0;
|
||||
nSendBytes = 0;
|
||||
nRecvBytes = 0;
|
||||
nLastSendEmpty = GetTime();
|
||||
nTimeConnected = GetTime();
|
||||
addr = addrIn;
|
||||
addrName = addrNameIn == "" ? addr.ToStringIPPort() : addrNameIn;
|
||||
nVersion = 0;
|
||||
strSubVer = "";
|
||||
fWhitelisted = false;
|
||||
fOneShot = false;
|
||||
fClient = false; // set by version message
|
||||
fInbound = fInboundIn;
|
||||
|
@ -328,8 +349,7 @@ public:
|
|||
{
|
||||
if (hSocket != INVALID_SOCKET)
|
||||
{
|
||||
closesocket(hSocket);
|
||||
hSocket = INVALID_SOCKET;
|
||||
CloseSocket(hSocket);
|
||||
}
|
||||
if (pfilter)
|
||||
delete pfilter;
|
||||
|
@ -682,8 +702,6 @@ public:
|
|||
void Subscribe(unsigned int nChannel, unsigned int nHops=0);
|
||||
void CancelSubscribe(unsigned int nChannel);
|
||||
void CloseSocketDisconnect();
|
||||
void Cleanup();
|
||||
|
||||
|
||||
// Denial-of-service detection/prevention
|
||||
// The idea is to detect peers that are behaving
|
||||
|
@ -704,6 +722,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);
|
||||
|
|
177
src/netbase.cpp
177
src/netbase.cpp
|
@ -25,7 +25,7 @@ using namespace std;
|
|||
|
||||
// Settings
|
||||
static proxyType proxyInfo[NET_MAX];
|
||||
static proxyType nameproxyInfo;
|
||||
static CService nameProxy;
|
||||
static CCriticalSection cs_proxyInfos;
|
||||
int nConnectTimeout = 5000;
|
||||
bool fNameLookup = false;
|
||||
|
@ -161,56 +161,12 @@ bool LookupNumeric(const char *pszName, CService& addr, int portDefault)
|
|||
return Lookup(pszName, addr, portDefault, false);
|
||||
}
|
||||
|
||||
bool static Socks4(const CService &addrDest, SOCKET& hSocket)
|
||||
{
|
||||
LogPrintf("SOCKS4 connecting %s\n", addrDest.ToString());
|
||||
if (!addrDest.IsIPv4())
|
||||
{
|
||||
closesocket(hSocket);
|
||||
return error("Proxy destination is not IPv4");
|
||||
}
|
||||
char pszSocks4IP[] = "\4\1\0\0\0\0\0\0user";
|
||||
struct sockaddr_in addr;
|
||||
socklen_t len = sizeof(addr);
|
||||
if (!addrDest.GetSockAddr((struct sockaddr*)&addr, &len) || addr.sin_family != AF_INET)
|
||||
{
|
||||
closesocket(hSocket);
|
||||
return error("Cannot get proxy destination address");
|
||||
}
|
||||
memcpy(pszSocks4IP + 2, &addr.sin_port, 2);
|
||||
memcpy(pszSocks4IP + 4, &addr.sin_addr, 4);
|
||||
char* pszSocks4 = pszSocks4IP;
|
||||
int nSize = sizeof(pszSocks4IP);
|
||||
|
||||
int ret = send(hSocket, pszSocks4, nSize, MSG_NOSIGNAL);
|
||||
if (ret != nSize)
|
||||
{
|
||||
closesocket(hSocket);
|
||||
return error("Error sending to proxy");
|
||||
}
|
||||
char pchRet[8];
|
||||
if (recv(hSocket, pchRet, 8, 0) != 8)
|
||||
{
|
||||
closesocket(hSocket);
|
||||
return error("Error reading proxy response");
|
||||
}
|
||||
if (pchRet[1] != 0x5a)
|
||||
{
|
||||
closesocket(hSocket);
|
||||
if (pchRet[1] != 0x5b)
|
||||
LogPrintf("ERROR: Proxy returned error %d\n", pchRet[1]);
|
||||
return false;
|
||||
}
|
||||
LogPrintf("SOCKS4 connected %s\n", addrDest.ToString());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool static Socks5(string strDest, int port, SOCKET& hSocket)
|
||||
{
|
||||
LogPrintf("SOCKS5 connecting %s\n", strDest);
|
||||
if (strDest.size() > 255)
|
||||
{
|
||||
closesocket(hSocket);
|
||||
CloseSocket(hSocket);
|
||||
return error("Hostname too long");
|
||||
}
|
||||
char pszSocks5Init[] = "\5\1\0";
|
||||
|
@ -219,18 +175,18 @@ bool static Socks5(string strDest, int port, SOCKET& hSocket)
|
|||
ssize_t ret = send(hSocket, pszSocks5Init, nSize, MSG_NOSIGNAL);
|
||||
if (ret != nSize)
|
||||
{
|
||||
closesocket(hSocket);
|
||||
CloseSocket(hSocket);
|
||||
return error("Error sending to proxy");
|
||||
}
|
||||
char pchRet1[2];
|
||||
if (recv(hSocket, pchRet1, 2, 0) != 2)
|
||||
{
|
||||
closesocket(hSocket);
|
||||
CloseSocket(hSocket);
|
||||
return error("Error reading proxy response");
|
||||
}
|
||||
if (pchRet1[0] != 0x05 || pchRet1[1] != 0x00)
|
||||
{
|
||||
closesocket(hSocket);
|
||||
CloseSocket(hSocket);
|
||||
return error("Proxy failed to initialize");
|
||||
}
|
||||
string strSocks5("\5\1");
|
||||
|
@ -242,23 +198,23 @@ bool static Socks5(string strDest, int port, SOCKET& hSocket)
|
|||
ret = send(hSocket, strSocks5.c_str(), strSocks5.size(), MSG_NOSIGNAL);
|
||||
if (ret != (ssize_t)strSocks5.size())
|
||||
{
|
||||
closesocket(hSocket);
|
||||
CloseSocket(hSocket);
|
||||
return error("Error sending to proxy");
|
||||
}
|
||||
char pchRet2[4];
|
||||
if (recv(hSocket, pchRet2, 4, 0) != 4)
|
||||
{
|
||||
closesocket(hSocket);
|
||||
CloseSocket(hSocket);
|
||||
return error("Error reading proxy response");
|
||||
}
|
||||
if (pchRet2[0] != 0x05)
|
||||
{
|
||||
closesocket(hSocket);
|
||||
CloseSocket(hSocket);
|
||||
return error("Proxy failed to accept request");
|
||||
}
|
||||
if (pchRet2[1] != 0x00)
|
||||
{
|
||||
closesocket(hSocket);
|
||||
CloseSocket(hSocket);
|
||||
switch (pchRet2[1])
|
||||
{
|
||||
case 0x01: return error("Proxy error: general failure");
|
||||
|
@ -274,7 +230,7 @@ bool static Socks5(string strDest, int port, SOCKET& hSocket)
|
|||
}
|
||||
if (pchRet2[2] != 0x00)
|
||||
{
|
||||
closesocket(hSocket);
|
||||
CloseSocket(hSocket);
|
||||
return error("Error: malformed proxy response");
|
||||
}
|
||||
char pchRet3[256];
|
||||
|
@ -286,23 +242,23 @@ bool static Socks5(string strDest, int port, SOCKET& hSocket)
|
|||
{
|
||||
ret = recv(hSocket, pchRet3, 1, 0) != 1;
|
||||
if (ret) {
|
||||
closesocket(hSocket);
|
||||
CloseSocket(hSocket);
|
||||
return error("Error reading from proxy");
|
||||
}
|
||||
int nRecv = pchRet3[0];
|
||||
ret = recv(hSocket, pchRet3, nRecv, 0) != nRecv;
|
||||
break;
|
||||
}
|
||||
default: closesocket(hSocket); return error("Error: malformed proxy response");
|
||||
default: CloseSocket(hSocket); return error("Error: malformed proxy response");
|
||||
}
|
||||
if (ret)
|
||||
{
|
||||
closesocket(hSocket);
|
||||
CloseSocket(hSocket);
|
||||
return error("Error reading from proxy");
|
||||
}
|
||||
if (recv(hSocket, pchRet3, 2, 0) != 2)
|
||||
{
|
||||
closesocket(hSocket);
|
||||
CloseSocket(hSocket);
|
||||
return error("Error reading from proxy");
|
||||
}
|
||||
LogPrintf("SOCKS5 connected %s\n", strDest);
|
||||
|
@ -336,7 +292,7 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe
|
|||
if (fcntl(hSocket, F_SETFL, fFlags | O_NONBLOCK) == -1)
|
||||
#endif
|
||||
{
|
||||
closesocket(hSocket);
|
||||
CloseSocket(hSocket);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -356,13 +312,13 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe
|
|||
if (nRet == 0)
|
||||
{
|
||||
LogPrint("net", "connection to %s timeout\n", addrConnect.ToString());
|
||||
closesocket(hSocket);
|
||||
CloseSocket(hSocket);
|
||||
return false;
|
||||
}
|
||||
if (nRet == SOCKET_ERROR)
|
||||
{
|
||||
LogPrintf("select() for %s failed: %s\n", addrConnect.ToString(), NetworkErrorString(WSAGetLastError()));
|
||||
closesocket(hSocket);
|
||||
CloseSocket(hSocket);
|
||||
return false;
|
||||
}
|
||||
socklen_t nRetSize = sizeof(nRet);
|
||||
|
@ -373,13 +329,13 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe
|
|||
#endif
|
||||
{
|
||||
LogPrintf("getsockopt() for %s failed: %s\n", addrConnect.ToString(), NetworkErrorString(WSAGetLastError()));
|
||||
closesocket(hSocket);
|
||||
CloseSocket(hSocket);
|
||||
return false;
|
||||
}
|
||||
if (nRet != 0)
|
||||
{
|
||||
LogPrintf("connect() to %s failed after select(): %s\n", addrConnect.ToString(), NetworkErrorString(nRet));
|
||||
closesocket(hSocket);
|
||||
CloseSocket(hSocket);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -390,7 +346,7 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe
|
|||
#endif
|
||||
{
|
||||
LogPrintf("connect() to %s failed: %s\n", addrConnect.ToString(), NetworkErrorString(WSAGetLastError()));
|
||||
closesocket(hSocket);
|
||||
CloseSocket(hSocket);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -406,7 +362,7 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe
|
|||
if (fcntl(hSocket, F_SETFL, fFlags & ~O_NONBLOCK) == SOCKET_ERROR)
|
||||
#endif
|
||||
{
|
||||
closesocket(hSocket);
|
||||
CloseSocket(hSocket);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -414,53 +370,49 @@ bool static ConnectSocketDirectly(const CService &addrConnect, SOCKET& hSocketRe
|
|||
return true;
|
||||
}
|
||||
|
||||
bool SetProxy(enum Network net, CService addrProxy, int nSocksVersion) {
|
||||
bool SetProxy(enum Network net, CService addrProxy) {
|
||||
assert(net >= 0 && net < NET_MAX);
|
||||
if (nSocksVersion != 0 && nSocksVersion != 4 && nSocksVersion != 5)
|
||||
return false;
|
||||
if (nSocksVersion != 0 && !addrProxy.IsValid())
|
||||
if (!addrProxy.IsValid())
|
||||
return false;
|
||||
LOCK(cs_proxyInfos);
|
||||
proxyInfo[net] = std::make_pair(addrProxy, nSocksVersion);
|
||||
proxyInfo[net] = addrProxy;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GetProxy(enum Network net, proxyType &proxyInfoOut) {
|
||||
assert(net >= 0 && net < NET_MAX);
|
||||
LOCK(cs_proxyInfos);
|
||||
if (!proxyInfo[net].second)
|
||||
if (!proxyInfo[net].IsValid())
|
||||
return false;
|
||||
proxyInfoOut = proxyInfo[net];
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SetNameProxy(CService addrProxy, int nSocksVersion) {
|
||||
if (nSocksVersion != 0 && nSocksVersion != 5)
|
||||
return false;
|
||||
if (nSocksVersion != 0 && !addrProxy.IsValid())
|
||||
bool SetNameProxy(CService addrProxy) {
|
||||
if (!addrProxy.IsValid())
|
||||
return false;
|
||||
LOCK(cs_proxyInfos);
|
||||
nameproxyInfo = std::make_pair(addrProxy, nSocksVersion);
|
||||
nameProxy = addrProxy;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GetNameProxy(proxyType &nameproxyInfoOut) {
|
||||
bool GetNameProxy(CService &nameProxyOut) {
|
||||
LOCK(cs_proxyInfos);
|
||||
if (!nameproxyInfo.second)
|
||||
if(!nameProxy.IsValid())
|
||||
return false;
|
||||
nameproxyInfoOut = nameproxyInfo;
|
||||
nameProxyOut = nameProxy;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool HaveNameProxy() {
|
||||
LOCK(cs_proxyInfos);
|
||||
return nameproxyInfo.second != 0;
|
||||
return nameProxy.IsValid();
|
||||
}
|
||||
|
||||
bool IsProxy(const CNetAddr &addr) {
|
||||
LOCK(cs_proxyInfos);
|
||||
for (int i = 0; i < NET_MAX; i++) {
|
||||
if (proxyInfo[i].second && (addr == (CNetAddr)proxyInfo[i].first))
|
||||
if (addr == (CNetAddr)proxyInfo[i])
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -469,31 +421,18 @@ bool IsProxy(const CNetAddr &addr) {
|
|||
bool ConnectSocket(const CService &addrDest, SOCKET& hSocketRet, int nTimeout)
|
||||
{
|
||||
proxyType proxy;
|
||||
|
||||
// no proxy needed
|
||||
// no proxy needed (none set for target network)
|
||||
if (!GetProxy(addrDest.GetNetwork(), proxy))
|
||||
return ConnectSocketDirectly(addrDest, hSocketRet, nTimeout);
|
||||
|
||||
SOCKET hSocket = INVALID_SOCKET;
|
||||
|
||||
// first connect to proxy server
|
||||
if (!ConnectSocketDirectly(proxy.first, hSocket, nTimeout))
|
||||
if (!ConnectSocketDirectly(proxy, hSocket, nTimeout))
|
||||
return false;
|
||||
|
||||
// do socks negotiation
|
||||
switch (proxy.second) {
|
||||
case 4:
|
||||
if (!Socks4(addrDest, hSocket))
|
||||
return false;
|
||||
break;
|
||||
case 5:
|
||||
if (!Socks5(addrDest.ToStringIP(), addrDest.GetPort(), hSocket))
|
||||
return false;
|
||||
break;
|
||||
default:
|
||||
closesocket(hSocket);
|
||||
if (!Socks5(addrDest.ToStringIP(), addrDest.GetPort(), hSocket))
|
||||
return false;
|
||||
}
|
||||
|
||||
hSocketRet = hSocket;
|
||||
return true;
|
||||
|
@ -507,30 +446,25 @@ bool ConnectSocketByName(CService &addr, SOCKET& hSocketRet, const char *pszDest
|
|||
|
||||
SOCKET hSocket = INVALID_SOCKET;
|
||||
|
||||
proxyType nameproxy;
|
||||
GetNameProxy(nameproxy);
|
||||
CService nameProxy;
|
||||
GetNameProxy(nameProxy);
|
||||
|
||||
CService addrResolved(CNetAddr(strDest, fNameLookup && !nameproxy.second), port);
|
||||
CService addrResolved(CNetAddr(strDest, fNameLookup && !HaveNameProxy()), port);
|
||||
if (addrResolved.IsValid()) {
|
||||
addr = addrResolved;
|
||||
return ConnectSocket(addr, hSocketRet, nTimeout);
|
||||
}
|
||||
addr = CService("0.0.0.0:0");
|
||||
if (!nameproxy.second)
|
||||
return false;
|
||||
if (!ConnectSocketDirectly(nameproxy.first, hSocket, nTimeout))
|
||||
return false;
|
||||
|
||||
switch(nameproxy.second) {
|
||||
default:
|
||||
case 4:
|
||||
closesocket(hSocket);
|
||||
return false;
|
||||
case 5:
|
||||
if (!Socks5(strDest, port, hSocket))
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
addr = CService("0.0.0.0:0");
|
||||
|
||||
if (!HaveNameProxy())
|
||||
return false;
|
||||
// first connect to name proxy server
|
||||
if (!ConnectSocketDirectly(nameProxy, hSocket, nTimeout))
|
||||
return false;
|
||||
// do socks negotiation
|
||||
if (!Socks5(strDest, (unsigned short)port, hSocket))
|
||||
return false;
|
||||
|
||||
hSocketRet = hSocket;
|
||||
return true;
|
||||
|
@ -1270,3 +1204,16 @@ std::string NetworkErrorString(int err)
|
|||
return strprintf("%s (%d)", s, err);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool CloseSocket(SOCKET& hSocket)
|
||||
{
|
||||
if (hSocket == INVALID_SOCKET)
|
||||
return false;
|
||||
#ifdef WIN32
|
||||
int ret = closesocket(hSocket);
|
||||
#else
|
||||
int ret = close(hSocket);
|
||||
#endif
|
||||
hSocket = INVALID_SOCKET;
|
||||
return ret != SOCKET_ERROR;
|
||||
}
|
||||
|
|
|
@ -163,14 +163,14 @@ class CService : public CNetAddr
|
|||
)
|
||||
};
|
||||
|
||||
typedef std::pair<CService, int> proxyType;
|
||||
typedef CService proxyType;
|
||||
|
||||
enum Network ParseNetwork(std::string net);
|
||||
void SplitHostPort(std::string in, int &portOut, std::string &hostOut);
|
||||
bool SetProxy(enum Network net, CService addrProxy, int nSocksVersion = 5);
|
||||
bool SetProxy(enum Network net, CService addrProxy);
|
||||
bool GetProxy(enum Network net, proxyType &proxyInfoOut);
|
||||
bool IsProxy(const CNetAddr &addr);
|
||||
bool SetNameProxy(CService addrProxy, int nSocksVersion = 5);
|
||||
bool SetNameProxy(CService addrProxy);
|
||||
bool HaveNameProxy();
|
||||
bool LookupHost(const char *pszName, std::vector<CNetAddr>& vIP, unsigned int nMaxSolutions = 0, bool fAllowLookup = true);
|
||||
bool LookupHostNumeric(const char *pszName, std::vector<CNetAddr>& vIP, unsigned int nMaxSolutions = 0);
|
||||
|
@ -181,5 +181,7 @@ bool ConnectSocket(const CService &addr, SOCKET& hSocketRet, int nTimeout = nCon
|
|||
bool ConnectSocketByName(CService &addr, SOCKET& hSocketRet, const char *pszDest, int portDefault = 0, int nTimeout = nConnectTimeout);
|
||||
/** Return readable error string for a network error code */
|
||||
std::string NetworkErrorString(int err);
|
||||
/** Close socket and set hSocket to INVALID_SOCKET */
|
||||
bool CloseSocket(SOCKET& hSocket);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -318,26 +318,6 @@
|
|||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="socksVersionLabel">
|
||||
<property name="text">
|
||||
<string>SOCKS &Version:</string>
|
||||
</property>
|
||||
<property name="textFormat">
|
||||
<enum>Qt::PlainText</enum>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>socksVersion</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QValueComboBox" name="socksVersion">
|
||||
<property name="toolTip">
|
||||
<string>SOCKS version of the proxy (e.g. 5)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_1_Network">
|
||||
<property name="orientation">
|
||||
|
|
|
@ -49,15 +49,8 @@ OptionsDialog::OptionsDialog(QWidget *parent) :
|
|||
ui->proxyPort->setEnabled(false);
|
||||
ui->proxyPort->setValidator(new QIntValidator(1, 65535, this));
|
||||
|
||||
/** SOCKS version is only selectable for default proxy and is always 5 for IPv6 and Tor */
|
||||
ui->socksVersion->setEnabled(false);
|
||||
ui->socksVersion->addItem("5", 5);
|
||||
ui->socksVersion->addItem("4", 4);
|
||||
ui->socksVersion->setCurrentIndex(0);
|
||||
|
||||
connect(ui->connectSocks, SIGNAL(toggled(bool)), ui->proxyIp, SLOT(setEnabled(bool)));
|
||||
connect(ui->connectSocks, SIGNAL(toggled(bool)), ui->proxyPort, SLOT(setEnabled(bool)));
|
||||
connect(ui->connectSocks, SIGNAL(toggled(bool)), ui->socksVersion, SLOT(setEnabled(bool)));
|
||||
|
||||
ui->proxyIp->installEventFilter(this);
|
||||
|
||||
|
@ -175,7 +168,6 @@ void OptionsDialog::setMapper()
|
|||
mapper->addMapping(ui->connectSocks, OptionsModel::ProxyUse);
|
||||
mapper->addMapping(ui->proxyIp, OptionsModel::ProxyIP);
|
||||
mapper->addMapping(ui->proxyPort, OptionsModel::ProxyPort);
|
||||
mapper->addMapping(ui->socksVersion, OptionsModel::ProxySocksVersion);
|
||||
|
||||
/* Window */
|
||||
#ifndef Q_OS_MAC
|
||||
|
|
|
@ -123,11 +123,6 @@ void OptionsModel::Init()
|
|||
// Only try to set -proxy, if user has enabled fUseProxy
|
||||
if (settings.value("fUseProxy").toBool() && !SoftSetArg("-proxy", settings.value("addrProxy").toString().toStdString()))
|
||||
addOverriddenOption("-proxy");
|
||||
if (!settings.contains("nSocksVersion"))
|
||||
settings.setValue("nSocksVersion", 5);
|
||||
// Only try to set -socks, if user has enabled fUseProxy
|
||||
if (settings.value("fUseProxy").toBool() && !SoftSetArg("-socks", settings.value("nSocksVersion").toString().toStdString()))
|
||||
addOverriddenOption("-socks");
|
||||
|
||||
// Display
|
||||
if (!settings.contains("language"))
|
||||
|
@ -189,8 +184,6 @@ QVariant OptionsModel::data(const QModelIndex & index, int role) const
|
|||
QStringList strlIpPort = settings.value("addrProxy").toString().split(":", QString::SkipEmptyParts);
|
||||
return strlIpPort.at(1);
|
||||
}
|
||||
case ProxySocksVersion:
|
||||
return settings.value("nSocksVersion", 5);
|
||||
|
||||
#ifdef ENABLE_WALLET
|
||||
case Fee:
|
||||
|
@ -282,13 +275,6 @@ bool OptionsModel::setData(const QModelIndex & index, const QVariant & value, in
|
|||
}
|
||||
}
|
||||
break;
|
||||
case ProxySocksVersion: {
|
||||
if (settings.value("nSocksVersion") != value) {
|
||||
settings.setValue("nSocksVersion", value.toInt());
|
||||
setRestartRequired(true);
|
||||
}
|
||||
}
|
||||
break;
|
||||
#ifdef ENABLE_WALLET
|
||||
case Fee: // core option - can be changed on-the-fly
|
||||
// Todo: Add is valid check and warn via message, if not
|
||||
|
@ -357,20 +343,16 @@ bool OptionsModel::getProxySettings(QNetworkProxy& proxy) const
|
|||
// GUI settings can be overridden with -proxy.
|
||||
proxyType curProxy;
|
||||
if (GetProxy(NET_IPV4, curProxy)) {
|
||||
if (curProxy.second == 5) {
|
||||
proxy.setType(QNetworkProxy::Socks5Proxy);
|
||||
proxy.setHostName(QString::fromStdString(curProxy.first.ToStringIP()));
|
||||
proxy.setPort(curProxy.first.GetPort());
|
||||
proxy.setType(QNetworkProxy::Socks5Proxy);
|
||||
proxy.setHostName(QString::fromStdString(curProxy.ToStringIP()));
|
||||
proxy.setPort(curProxy.GetPort());
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
proxy.setType(QNetworkProxy::NoProxy);
|
||||
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
void OptionsModel::setRestartRequired(bool fRequired)
|
||||
|
|
|
@ -345,20 +345,14 @@ void PaymentServer::initNetManager()
|
|||
|
||||
QNetworkProxy proxy;
|
||||
|
||||
// Query active proxy (fails if no SOCKS5 proxy)
|
||||
// Query active SOCKS5 proxy
|
||||
if (optionsModel->getProxySettings(proxy)) {
|
||||
if (proxy.type() == QNetworkProxy::Socks5Proxy) {
|
||||
netManager->setProxy(proxy);
|
||||
netManager->setProxy(proxy);
|
||||
|
||||
qDebug() << "PaymentServer::initNetManager : Using SOCKS5 proxy" << proxy.hostName() << ":" << proxy.port();
|
||||
}
|
||||
else
|
||||
qDebug() << "PaymentServer::initNetManager : No active proxy server found.";
|
||||
qDebug() << "PaymentServer::initNetManager : Using SOCKS5 proxy" << proxy.hostName() << ":" << proxy.port();
|
||||
}
|
||||
else
|
||||
emit message(tr("Net manager warning"),
|
||||
tr("Your active proxy doesn't support SOCKS5, which is required for payment requests via proxy."),
|
||||
CClientUIInterface::MSG_WARNING);
|
||||
qDebug() << "PaymentServer::initNetManager : No active proxy server found.";
|
||||
|
||||
connect(netManager, SIGNAL(finished(QNetworkReply*)),
|
||||
this, SLOT(netRequestFinished(QNetworkReply*)));
|
||||
|
|
|
@ -71,7 +71,7 @@ Value getinfo(const Array& params, bool fHelp)
|
|||
obj.push_back(Pair("blocks", (int)chainActive.Height()));
|
||||
obj.push_back(Pair("timeoffset", GetTimeOffset()));
|
||||
obj.push_back(Pair("connections", (int)vNodes.size()));
|
||||
obj.push_back(Pair("proxy", (proxy.first.IsValid() ? proxy.first.ToStringIPPort() : string())));
|
||||
obj.push_back(Pair("proxy", (proxy.IsValid() ? proxy.ToStringIPPort() : string())));
|
||||
obj.push_back(Pair("difficulty", (double)GetDifficulty()));
|
||||
obj.push_back(Pair("testnet", TestNet()));
|
||||
#ifdef ENABLE_WALLET
|
||||
|
|
|
@ -134,8 +134,8 @@ Value getpeerinfo(const Array& params, bool fHelp)
|
|||
if (fStateStats) {
|
||||
obj.push_back(Pair("banscore", statestats.nMisbehavior));
|
||||
}
|
||||
if (stats.fSyncNode)
|
||||
obj.push_back(Pair("syncnode", true));
|
||||
obj.push_back(Pair("syncnode", stats.fSyncNode));
|
||||
obj.push_back(Pair("whitelisted", stats.fWhitelisted));
|
||||
|
||||
ret.push_back(obj);
|
||||
}
|
||||
|
@ -368,7 +368,7 @@ Value getnetworkinfo(const Array& params, bool fHelp)
|
|||
obj.push_back(Pair("protocolversion",(int)PROTOCOL_VERSION));
|
||||
obj.push_back(Pair("timeoffset", GetTimeOffset()));
|
||||
obj.push_back(Pair("connections", (int)vNodes.size()));
|
||||
obj.push_back(Pair("proxy", (proxy.first.IsValid() ? proxy.first.ToStringIPPort() : string())));
|
||||
obj.push_back(Pair("proxy", (proxy.IsValid() ? proxy.ToStringIPPort() : string())));
|
||||
obj.push_back(Pair("relayfee", ValueFromAmount(CTransaction::nMinRelayTxFee)));
|
||||
Array localAddresses;
|
||||
{
|
||||
|
|
|
@ -831,6 +831,35 @@ struct ser_streamplaceholder
|
|||
|
||||
typedef std::vector<char, zero_after_free_allocator<char> > CSerializeData;
|
||||
|
||||
class CSizeComputer
|
||||
{
|
||||
protected:
|
||||
size_t nSize;
|
||||
|
||||
public:
|
||||
int nType;
|
||||
int nVersion;
|
||||
|
||||
CSizeComputer(int nTypeIn, int nVersionIn) : nSize(0), nType(nTypeIn), nVersion(nVersionIn) {}
|
||||
|
||||
CSizeComputer& write(const char *psz, int nSize)
|
||||
{
|
||||
this->nSize += nSize;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
CSizeComputer& operator<<(const T& obj)
|
||||
{
|
||||
::Serialize(*this, obj, nType, nVersion);
|
||||
return (*this);
|
||||
}
|
||||
|
||||
size_t size() const {
|
||||
return nSize;
|
||||
}
|
||||
};
|
||||
|
||||
/** Double ended buffer combining vector and stream-like interfaces.
|
||||
*
|
||||
* >> and << read and write unformatted data using the above serialization templates.
|
||||
|
|
|
@ -97,7 +97,6 @@ bool fDaemon = false;
|
|||
bool fServer = false;
|
||||
string strMiscWarning;
|
||||
bool fBloomFilters = true;
|
||||
bool fNoListen = false;
|
||||
bool fLogTimestamps = false;
|
||||
volatile bool fReopenDebugLog = false;
|
||||
CClientUIInterface uiInterface;
|
||||
|
|
|
@ -115,7 +115,6 @@ extern bool fPrintToDebugLog;
|
|||
extern bool fServer;
|
||||
extern std::string strMiscWarning;
|
||||
extern bool fBloomFilters;
|
||||
extern bool fNoListen;
|
||||
extern bool fLogTimestamps;
|
||||
extern volatile bool fReopenDebugLog;
|
||||
|
||||
|
|
Loading…
Reference in a new issue