Do not use third party services for IP detection.
This is a simplified re-do of closed pull #3088. This patch eliminates the privacy and reliability problematic use of centralized web services for discovering the node's addresses for advertisement. The Bitcoin protocol already allows your peers to tell you what IP they think you have, but this data isn't trustworthy since they could lie. So the challenge is using it without creating a DOS vector. To accomplish this we adopt an approach similar to the one used by P2Pool: If we're announcing and don't have a better address discovered (e.g. via UPNP) or configured we just announce to each peer the address that peer told us. Since peers could already replace, forge, or drop our address messages this cannot create a new vulnerability... but if even one of our peers is giving us a good address we'll eventually make a useful advertisement. We also may randomly use the peer-provided address for the daily rebroadcast even if we otherwise have a seemingly routable address, just in case we've been misconfigured (e.g. by UPNP). To avoid privacy problems, we only do these things if discovery is enabled.
This commit is contained in:
parent
06037f3f46
commit
845c86d128
4 changed files with 61 additions and 148 deletions
|
@ -574,6 +574,9 @@ bool AppInit2(boost::thread_group& threadGroup)
|
|||
// to protect privacy, do not listen by default if a default proxy server is specified
|
||||
if (SoftSetBoolArg("-listen", false))
|
||||
LogPrintf("AppInit2 : parameter interaction: -proxy set -> setting -listen=0\n");
|
||||
// to protect privacy, do not discover addresses by default
|
||||
if (SoftSetBoolArg("-discover", false))
|
||||
LogPrintf("AppInit2 : parameter interaction: -proxy set -> setting -discover=0\n");
|
||||
}
|
||||
|
||||
if (!GetBoolArg("-listen", true)) {
|
||||
|
|
41
src/main.cpp
41
src/main.cpp
|
@ -3474,12 +3474,6 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
|
|||
else
|
||||
pfrom->fRelayTxes = true;
|
||||
|
||||
if (pfrom->fInbound && addrMe.IsRoutable())
|
||||
{
|
||||
pfrom->addrLocal = addrMe;
|
||||
SeenLocal(addrMe);
|
||||
}
|
||||
|
||||
// Disconnect if we connected to ourself
|
||||
if (nNonce == nLocalHostNonce && nNonce > 1)
|
||||
{
|
||||
|
@ -3488,6 +3482,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
|
|||
return true;
|
||||
}
|
||||
|
||||
pfrom->addrLocal = addrMe;
|
||||
if (pfrom->fInbound && addrMe.IsRoutable())
|
||||
{
|
||||
SeenLocal(addrMe);
|
||||
}
|
||||
|
||||
// Be shy and don't send version until we hear
|
||||
if (pfrom->fInbound)
|
||||
pfrom->PushVersion();
|
||||
|
@ -3508,7 +3508,12 @@ bool static ProcessMessage(CNode* pfrom, string strCommand, CDataStream& vRecv,
|
|||
{
|
||||
CAddress addr = GetLocalAddress(&pfrom->addr);
|
||||
if (addr.IsRoutable())
|
||||
{
|
||||
pfrom->PushAddress(addr);
|
||||
} else if (IsPeerAddrLocalGood(pfrom)) {
|
||||
addr.SetIP(pfrom->addrLocal);
|
||||
pfrom->PushAddress(addr);
|
||||
}
|
||||
}
|
||||
|
||||
// Get recent addresses
|
||||
|
@ -4371,24 +4376,18 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
|
|||
static int64_t nLastRebroadcast;
|
||||
if (!IsInitialBlockDownload() && (GetTime() - nLastRebroadcast > 24 * 60 * 60))
|
||||
{
|
||||
LOCK(cs_vNodes);
|
||||
BOOST_FOREACH(CNode* pnode, vNodes)
|
||||
{
|
||||
LOCK(cs_vNodes);
|
||||
BOOST_FOREACH(CNode* pnode, vNodes)
|
||||
{
|
||||
// Periodically clear setAddrKnown to allow refresh broadcasts
|
||||
if (nLastRebroadcast)
|
||||
pnode->setAddrKnown.clear();
|
||||
// Periodically clear setAddrKnown to allow refresh broadcasts
|
||||
if (nLastRebroadcast)
|
||||
pnode->setAddrKnown.clear();
|
||||
|
||||
// Rebroadcast our address
|
||||
if (fListen)
|
||||
{
|
||||
CAddress addr = GetLocalAddress(&pnode->addr);
|
||||
if (addr.IsRoutable())
|
||||
pnode->PushAddress(addr);
|
||||
}
|
||||
}
|
||||
// Rebroadcast our address
|
||||
AdvertizeLocal(pnode);
|
||||
}
|
||||
nLastRebroadcast = GetTime();
|
||||
if (!vNodes.empty())
|
||||
nLastRebroadcast = GetTime();
|
||||
}
|
||||
|
||||
//
|
||||
|
|
161
src/net.cpp
161
src/net.cpp
|
@ -142,16 +142,19 @@ bool GetLocal(CService& addr, const CNetAddr *paddrPeer)
|
|||
}
|
||||
|
||||
// get best local address for a particular peer as a CAddress
|
||||
// Otherwise, return the unroutable 0.0.0.0 but filled in with
|
||||
// the normal parameters, since the IP may be changed to a useful
|
||||
// one by discovery.
|
||||
CAddress GetLocalAddress(const CNetAddr *paddrPeer)
|
||||
{
|
||||
CAddress ret(CService("0.0.0.0",0),0);
|
||||
CAddress ret(CService("0.0.0.0",GetListenPort()),0);
|
||||
CService addr;
|
||||
if (GetLocal(addr, paddrPeer))
|
||||
{
|
||||
ret = CAddress(addr);
|
||||
ret.nServices = nLocalServices;
|
||||
ret.nTime = GetAdjustedTime();
|
||||
}
|
||||
ret.nServices = nLocalServices;
|
||||
ret.nTime = GetAdjustedTime();
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -205,21 +208,38 @@ bool RecvLine(SOCKET hSocket, string& strLine)
|
|||
}
|
||||
}
|
||||
|
||||
// used when scores of local addresses may have changed
|
||||
// pushes better local address to peers
|
||||
void static AdvertizeLocal()
|
||||
int GetnScore(const CService& addr)
|
||||
{
|
||||
LOCK(cs_vNodes);
|
||||
BOOST_FOREACH(CNode* pnode, vNodes)
|
||||
LOCK(cs_mapLocalHost);
|
||||
if (mapLocalHost.count(addr) == LOCAL_NONE)
|
||||
return 0;
|
||||
return mapLocalHost[addr].nScore;
|
||||
}
|
||||
|
||||
// Is our peer's addrLocal potentially useful as an external IP source?
|
||||
bool IsPeerAddrLocalGood(CNode *pnode)
|
||||
{
|
||||
return fDiscover && pnode->addr.IsRoutable() && pnode->addrLocal.IsRoutable() &&
|
||||
!IsLimited(pnode->addrLocal.GetNetwork());
|
||||
}
|
||||
|
||||
// pushes our own address to a peer
|
||||
void AdvertizeLocal(CNode *pnode)
|
||||
{
|
||||
if (fListen && pnode->fSuccessfullyConnected)
|
||||
{
|
||||
if (pnode->fSuccessfullyConnected)
|
||||
CAddress addrLocal = GetLocalAddress(&pnode->addr);
|
||||
// If discovery is enabled, sometimes give our peer the address it
|
||||
// tells us that it sees us as in case it has a better idea of our
|
||||
// address than we do.
|
||||
if (IsPeerAddrLocalGood(pnode) && (!addrLocal.IsRoutable() ||
|
||||
GetRand((GetnScore(addrLocal) > LOCAL_MANUAL) ? 8:2) == 0))
|
||||
{
|
||||
CAddress addrLocal = GetLocalAddress(&pnode->addr);
|
||||
if (addrLocal.IsRoutable() && (CService)addrLocal != (CService)pnode->addrLocal)
|
||||
{
|
||||
pnode->PushAddress(addrLocal);
|
||||
pnode->addrLocal = addrLocal;
|
||||
}
|
||||
addrLocal.SetIP(pnode->addrLocal);
|
||||
}
|
||||
if (addrLocal.IsRoutable())
|
||||
{
|
||||
pnode->PushAddress(addrLocal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -257,8 +277,6 @@ bool AddLocal(const CService& addr, int nScore)
|
|||
SetReachable(addr.GetNetwork());
|
||||
}
|
||||
|
||||
AdvertizeLocal();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -296,12 +314,10 @@ bool SeenLocal(const CService& addr)
|
|||
return false;
|
||||
mapLocalHost[addr].nScore++;
|
||||
}
|
||||
|
||||
AdvertizeLocal();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/** check whether a given address is potentially local */
|
||||
bool IsLocal(const CService& addr)
|
||||
{
|
||||
|
@ -323,114 +339,12 @@ bool IsReachable(const CNetAddr& addr)
|
|||
return IsReachable(net);
|
||||
}
|
||||
|
||||
bool GetMyExternalIP2(const CService& addrConnect, const char* pszGet, const char* pszKeyword, CNetAddr& ipRet)
|
||||
{
|
||||
SOCKET hSocket;
|
||||
if (!ConnectSocket(addrConnect, hSocket))
|
||||
return error("GetMyExternalIP() : connection to %s failed", addrConnect.ToString());
|
||||
|
||||
send(hSocket, pszGet, strlen(pszGet), MSG_NOSIGNAL);
|
||||
|
||||
string strLine;
|
||||
while (RecvLine(hSocket, strLine))
|
||||
{
|
||||
if (strLine.empty()) // HTTP response is separated from headers by blank line
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
if (!RecvLine(hSocket, strLine))
|
||||
{
|
||||
CloseSocket(hSocket);
|
||||
return false;
|
||||
}
|
||||
if (pszKeyword == NULL)
|
||||
break;
|
||||
if (strLine.find(pszKeyword) != string::npos)
|
||||
{
|
||||
strLine = strLine.substr(strLine.find(pszKeyword) + strlen(pszKeyword));
|
||||
break;
|
||||
}
|
||||
}
|
||||
CloseSocket(hSocket);
|
||||
if (strLine.find("<") != string::npos)
|
||||
strLine = strLine.substr(0, strLine.find("<"));
|
||||
strLine = strLine.substr(strspn(strLine.c_str(), " \t\n\r"));
|
||||
while (strLine.size() > 0 && isspace(strLine[strLine.size()-1]))
|
||||
strLine.resize(strLine.size()-1);
|
||||
CService addr(strLine,0,true);
|
||||
LogPrintf("GetMyExternalIP() received [%s] %s\n", strLine, addr.ToString());
|
||||
if (!addr.IsValid() || !addr.IsRoutable())
|
||||
return false;
|
||||
ipRet.SetIP(addr);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
CloseSocket(hSocket);
|
||||
return error("GetMyExternalIP() : connection closed");
|
||||
}
|
||||
|
||||
bool GetMyExternalIP(CNetAddr& ipRet)
|
||||
{
|
||||
CService addrConnect;
|
||||
const char* pszGet;
|
||||
const char* pszKeyword;
|
||||
|
||||
for (int nLookup = 0; nLookup <= 1; nLookup++)
|
||||
for (int nHost = 1; nHost <= 1; nHost++)
|
||||
{
|
||||
// We should be phasing out our use of sites like these. If we need
|
||||
// replacements, we should ask for volunteers to put this simple
|
||||
// php file on their web server that prints the client IP:
|
||||
// <?php echo $_SERVER["REMOTE_ADDR"]; ?>
|
||||
if (nHost == 1)
|
||||
{
|
||||
addrConnect = CService("91.198.22.70", 80); // checkip.dyndns.org
|
||||
|
||||
if (nLookup == 1)
|
||||
{
|
||||
CService addrIP("checkip.dyndns.org", 80, true);
|
||||
if (addrIP.IsValid())
|
||||
addrConnect = addrIP;
|
||||
}
|
||||
|
||||
pszGet = "GET / HTTP/1.1\r\n"
|
||||
"Host: checkip.dyndns.org\r\n"
|
||||
"User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)\r\n"
|
||||
"Connection: close\r\n"
|
||||
"\r\n";
|
||||
|
||||
pszKeyword = "Address:";
|
||||
}
|
||||
|
||||
if (GetMyExternalIP2(addrConnect, pszGet, pszKeyword, ipRet))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void ThreadGetMyExternalIP()
|
||||
{
|
||||
CNetAddr addrLocalHost;
|
||||
if (GetMyExternalIP(addrLocalHost))
|
||||
{
|
||||
LogPrintf("GetMyExternalIP() returned %s\n", addrLocalHost.ToStringIP());
|
||||
AddLocal(addrLocalHost, LOCAL_HTTP);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
void AddressCurrentlyConnected(const CService& addr)
|
||||
{
|
||||
addrman.Connected(addr);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
uint64_t CNode::nTotalBytesRecv = 0;
|
||||
uint64_t CNode::nTotalBytesSent = 0;
|
||||
CCriticalSection CNode::cs_totalBytesRecv;
|
||||
|
@ -1687,9 +1601,6 @@ void static Discover(boost::thread_group& threadGroup)
|
|||
}
|
||||
#endif
|
||||
|
||||
// Don't use external IPv4 discovery, when -onlynet="IPv6"
|
||||
if (!IsLimited(NET_IPV4))
|
||||
threadGroup.create_thread(boost::bind(&TraceThread<void (*)()>, "ext-ip", &ThreadGetMyExternalIP));
|
||||
}
|
||||
|
||||
void StartNode(boost::thread_group& threadGroup)
|
||||
|
|
|
@ -60,7 +60,6 @@ unsigned int SendBufferSize();
|
|||
|
||||
void AddOneShot(std::string strDest);
|
||||
bool RecvLine(SOCKET hSocket, std::string& strLine);
|
||||
bool GetMyExternalIP(CNetAddr& ipRet);
|
||||
void AddressCurrentlyConnected(const CService& addr);
|
||||
CNode* FindNode(const CNetAddr& ip);
|
||||
CNode* FindNode(const std::string& addrName);
|
||||
|
@ -96,12 +95,13 @@ enum
|
|||
LOCAL_IF, // address a local interface listens on
|
||||
LOCAL_BIND, // address explicit bound to
|
||||
LOCAL_UPNP, // address reported by UPnP
|
||||
LOCAL_HTTP, // address reported by whatismyip.com and similar
|
||||
LOCAL_MANUAL, // address explicitly specified (-externalip=)
|
||||
|
||||
LOCAL_MAX
|
||||
};
|
||||
|
||||
bool IsPeerAddrLocalGood(CNode *pnode);
|
||||
void AdvertizeLocal(CNode *pnode);
|
||||
void SetLimited(enum Network net, bool fLimited = true);
|
||||
bool IsLimited(enum Network net);
|
||||
bool IsLimited(const CNetAddr& addr);
|
||||
|
|
Loading…
Reference in a new issue