Projects : bitcoin : bitcoin_reorg_bounded_space
| 1 | // Copyright (c) 2009-2010 Satoshi Nakamoto |
| 2 | // Copyright (c) 2009-2012 The Bitcoin developers |
| 3 | // Distributed under the MIT/X11 software license, see the accompanying |
| 4 | // file license.txt or http://www.opensource.org/licenses/mit-license.php. |
| 5 | #ifndef BITCOIN_NET_H |
| 6 | #define BITCOIN_NET_H |
| 7 | |
| 8 | #include <deque> |
| 9 | #include <boost/array.hpp> |
| 10 | #include <boost/foreach.hpp> |
| 11 | #include <openssl/rand.h> |
| 12 | |
| 13 | #include "protocol.h" |
| 14 | |
| 15 | class CAddrDB; |
| 16 | class CNode; |
| 17 | class CBlockIndex; |
| 18 | extern int nBestHeight; |
| 19 | extern int nConnectTimeout; |
| 20 | |
| 21 | |
| 22 | |
| 23 | inline unsigned int ReceiveBufferSize() { return 1000*GetArg("-maxreceivebuffer", 10*1000); } |
| 24 | inline unsigned int SendBufferSize() { return 1000*GetArg("-maxsendbuffer", 10*1000); } |
| 25 | |
| 26 | bool ConnectSocket(const CAddress& addrConnect, SOCKET& hSocketRet, int nTimeout=nConnectTimeout); |
| 27 | bool Lookup(const char *pszName, std::vector<CAddress>& vaddr, int nServices, int nMaxSolutions, int portDefault = 0, bool fAllowPort = false); |
| 28 | bool Lookup(const char *pszName, CAddress& addr, int nServices, int portDefault = 0, bool fAllowPort = false); |
| 29 | bool AddAddress(CAddress addr, int64 nTimePenalty=0, CAddrDB *pAddrDB=NULL); |
| 30 | void AddressCurrentlyConnected(const CAddress& addr); |
| 31 | CNode* FindNode(unsigned int ip); |
| 32 | CNode* ConnectNode(CAddress addrConnect, int64 nTimeout=0); |
| 33 | void MapPort(bool fMapPort); |
| 34 | bool BindListenPort(std::string& strError=REF(std::string())); |
| 35 | void StartNode(void* parg); |
| 36 | bool StopNode(); |
| 37 | |
| 38 | enum |
| 39 | { |
| 40 | MSG_TX = 1, |
| 41 | MSG_BLOCK, |
| 42 | }; |
| 43 | |
| 44 | extern bool fClient; |
| 45 | extern bool fAllowDNS; |
| 46 | extern uint64 nLocalServices; |
| 47 | extern CAddress addrLocalHost; |
| 48 | extern uint64 nLocalHostNonce; |
| 49 | extern boost::array<int, 10> vnThreadsRunning; |
| 50 | |
| 51 | extern std::vector<CNode*> vNodes; |
| 52 | extern CCriticalSection cs_vNodes; |
| 53 | extern std::map<std::vector<unsigned char>, CAddress> mapAddresses; |
| 54 | extern CCriticalSection cs_mapAddresses; |
| 55 | extern std::map<CInv, CDataStream> mapRelay; |
| 56 | extern std::deque<std::pair<int64, CInv> > vRelayExpiration; |
| 57 | extern CCriticalSection cs_mapRelay; |
| 58 | extern std::map<CInv, int64> mapAlreadyAskedFor; |
| 59 | |
| 60 | // Settings |
| 61 | extern int fUseProxy; |
| 62 | extern CAddress addrProxy; |
| 63 | |
| 64 | // Duplicated from main.h, stupid code-in-header-files. |
| 65 | extern int nClientVersionNum; |
| 66 | extern std::string strClientVersionName; |
| 67 | |
| 68 | class peer_disconnected_error : public std::runtime_error |
| 69 | { |
| 70 | public: |
| 71 | explicit peer_disconnected_error() : std::runtime_error("write to disconnected peer") {} |
| 72 | }; |
| 73 | |
| 74 | |
| 75 | |
| 76 | |
| 77 | |
| 78 | class CNode |
| 79 | { |
| 80 | public: |
| 81 | // socket |
| 82 | uint64 nServices; |
| 83 | SOCKET hSocket; |
| 84 | CDataStream vSend; |
| 85 | CDataStream vRecv; |
| 86 | CCriticalSection cs_vSend; |
| 87 | CCriticalSection cs_vRecv; |
| 88 | int64 nLastSend; |
| 89 | int64 nLastRecv; |
| 90 | int64 nLastSendEmpty; |
| 91 | int64 nTimeConnected; |
| 92 | CAddress addr; |
| 93 | int nVersion; |
| 94 | std::string strSubVer; |
| 95 | bool fClient; |
| 96 | bool fInbound; |
| 97 | bool fNetworkNode; |
| 98 | bool fSuccessfullyConnected; |
| 99 | bool fDisconnect; |
| 100 | protected: |
| 101 | int nRefCount; |
| 102 | |
| 103 | // Denial-of-service detection/prevention |
| 104 | // Key is ip address, value is banned-until-time |
| 105 | static std::map<unsigned int, int64> setBanned; |
| 106 | static CCriticalSection cs_setBanned; |
| 107 | int nMisbehavior; |
| 108 | |
| 109 | public: |
| 110 | int64 nReleaseTime; |
| 111 | uint256 hashContinue; |
| 112 | int nStartingHeight; |
| 113 | |
| 114 | // flood relay |
| 115 | std::vector<CAddress> vAddrToSend; |
| 116 | std::set<CAddress> setAddrKnown; |
| 117 | bool fGetAddr; |
| 118 | std::set<uint256> setKnown; |
| 119 | |
| 120 | // inventory based relay |
| 121 | std::set<CInv> setInventoryKnown; |
| 122 | std::vector<CInv> vInventoryToSend; |
| 123 | CCriticalSection cs_inventory; |
| 124 | std::multimap<int64, CInv> mapAskFor; |
| 125 | |
| 126 | CNode(SOCKET hSocketIn, CAddress addrIn, bool fInboundIn=false) |
| 127 | { |
| 128 | nServices = 0; |
| 129 | hSocket = hSocketIn; |
| 130 | // Version 0.2 obsoletes 20 Feb 2012 |
| 131 | vSend.SetVersion(209); |
| 132 | vRecv.SetVersion(209); |
| 133 | nLastSend = 0; |
| 134 | nLastRecv = 0; |
| 135 | nLastSendEmpty = GetTime(); |
| 136 | nTimeConnected = GetTime(); |
| 137 | addr = addrIn; |
| 138 | nVersion = 0; |
| 139 | strSubVer = ""; |
| 140 | fClient = false; // set by version message |
| 141 | fInbound = fInboundIn; |
| 142 | fNetworkNode = false; |
| 143 | fSuccessfullyConnected = false; |
| 144 | fDisconnect = false; |
| 145 | nRefCount = 0; |
| 146 | nReleaseTime = 0; |
| 147 | hashContinue = 0; |
| 148 | nStartingHeight = -1; |
| 149 | fGetAddr = false; |
| 150 | nMisbehavior = 0; |
| 151 | |
| 152 | // Be shy and don't send version until we hear |
| 153 | if (!fInbound) |
| 154 | { |
| 155 | try |
| 156 | { |
| 157 | PushVersion(); |
| 158 | } |
| 159 | catch (peer_disconnected_error& e) |
| 160 | { |
| 161 | // safe to ignore for this single message, and unclear that callers are prepared to handle |
| 162 | } |
| 163 | } |
| 164 | } |
| 165 | |
| 166 | ~CNode() |
| 167 | { |
| 168 | if (hSocket != INVALID_SOCKET) |
| 169 | { |
| 170 | closesocket(hSocket); |
| 171 | hSocket = INVALID_SOCKET; |
| 172 | } |
| 173 | } |
| 174 | |
| 175 | private: |
| 176 | CNode(const CNode&); |
| 177 | void operator=(const CNode&); |
| 178 | public: |
| 179 | |
| 180 | |
| 181 | int GetRefCount() |
| 182 | { |
| 183 | return std::max(nRefCount, 0) + (GetTime() < nReleaseTime ? 1 : 0); |
| 184 | } |
| 185 | |
| 186 | CNode* AddRef(int64 nTimeout=0) |
| 187 | { |
| 188 | if (nTimeout != 0) |
| 189 | nReleaseTime = std::max(nReleaseTime, GetTime() + nTimeout); |
| 190 | else |
| 191 | nRefCount++; |
| 192 | return this; |
| 193 | } |
| 194 | |
| 195 | void Release() |
| 196 | { |
| 197 | nRefCount--; |
| 198 | } |
| 199 | |
| 200 | |
| 201 | |
| 202 | void AddAddressKnown(const CAddress& addr) |
| 203 | { |
| 204 | setAddrKnown.insert(addr); |
| 205 | } |
| 206 | |
| 207 | void PushAddress(const CAddress& addr) |
| 208 | { |
| 209 | // Known checking here is only to save space from duplicates. |
| 210 | // SendMessages will filter it again for knowns that were added |
| 211 | // after addresses were pushed. |
| 212 | if (addr.IsValid() && !setAddrKnown.count(addr)) |
| 213 | vAddrToSend.push_back(addr); |
| 214 | } |
| 215 | |
| 216 | |
| 217 | void AddInventoryKnown(const CInv& inv) |
| 218 | { |
| 219 | CRITICAL_BLOCK(cs_inventory) |
| 220 | setInventoryKnown.insert(inv); |
| 221 | } |
| 222 | |
| 223 | void PushInventory(const CInv& inv) |
| 224 | { |
| 225 | CRITICAL_BLOCK(cs_inventory) |
| 226 | if (!setInventoryKnown.count(inv)) |
| 227 | vInventoryToSend.push_back(inv); |
| 228 | } |
| 229 | |
| 230 | void AskFor(const CInv& inv) |
| 231 | { |
| 232 | // We're using mapAskFor as a priority queue, |
| 233 | // the key is the earliest time the request can be sent |
| 234 | int64& nRequestTime = mapAlreadyAskedFor[inv]; |
| 235 | printf("askfor %s %"PRI64d"\n", inv.ToString().c_str(), nRequestTime); |
| 236 | |
| 237 | // Make sure not to reuse time indexes to keep things in the same order |
| 238 | int64 nNow = (GetTime() - 1) * 1000000; |
| 239 | static int64 nLastTime; |
| 240 | ++nLastTime; |
| 241 | nNow = std::max(nNow, nLastTime); |
| 242 | nLastTime = nNow; |
| 243 | |
| 244 | // Each retry is 2 minutes after the last |
| 245 | nRequestTime = std::max(nRequestTime + 2 * 60 * 1000000, nNow); |
| 246 | mapAskFor.insert(std::make_pair(nRequestTime, inv)); |
| 247 | } |
| 248 | |
| 249 | // Construct a compatible output stream for passing to PushMessage |
| 250 | CDataStream SendingStream() |
| 251 | { |
| 252 | return CDataStream(SER_NETWORK, vSend.GetVersion()); |
| 253 | } |
| 254 | |
| 255 | void PushVersion() |
| 256 | { |
| 257 | /// when NTP implemented, change to just nTime = GetAdjustedTime() |
| 258 | int64 nTime = (fInbound ? GetAdjustedTime() : GetTime()); |
| 259 | CAddress addrYou = (fUseProxy ? CAddress("0.0.0.0") : addr); |
| 260 | CAddress addrMe = (fUseProxy || !addrLocalHost.IsRoutable() ? CAddress("0.0.0.0") : addrLocalHost); |
| 261 | RAND_bytes((unsigned char*)&nLocalHostNonce, sizeof(nLocalHostNonce)); |
| 262 | PushMessage("version", SendingStream() |
| 263 | << nClientVersionNum |
| 264 | << nLocalServices |
| 265 | << nTime |
| 266 | << addrYou |
| 267 | << addrMe |
| 268 | << nLocalHostNonce |
| 269 | << FormatSubVersion(strClientVersionName, nClientVersionNum) |
| 270 | << nBestHeight); |
| 271 | } |
| 272 | |
| 273 | void PushMessage(const char* pszCommand) |
| 274 | { |
| 275 | PushMessage(pszCommand, SendingStream()); |
| 276 | } |
| 277 | |
| 278 | void PushMessage(const char* pszCommand, const CDataStream& payload) |
| 279 | { |
| 280 | if (fDebug) |
| 281 | printf("send %s %s (%d B)\n", addr.ToString().c_str(), pszCommand, payload.size()); |
| 282 | if (mapArgs.count("-dropmessagestest") && GetRand(atoi(mapArgs["-dropmessagestest"])) == 0) |
| 283 | { |
| 284 | printf("dropmessages DROPPING SEND MESSAGE %s %s\n", addr.ToString().c_str(), pszCommand); |
| 285 | return; |
| 286 | } |
| 287 | |
| 288 | SCOPED_LOCK(cs_vSend); |
| 289 | if (fDisconnect) |
| 290 | // Keeping this check inside the lock seems preferable as it assures that past this point, the peer was not disconnected due to flood control (though it may still be for other reasons as fDisconnect isn't strictly guarded by cs_vSend). |
| 291 | throw peer_disconnected_error(); |
| 292 | |
| 293 | unsigned int nHeaderStart = vSend.size(); |
| 294 | try |
| 295 | { |
| 296 | CMessageHeader header(pszCommand, payload.size()); |
| 297 | uint256 hash = Hash(payload.begin(), payload.end()); |
| 298 | memcpy(&(header.nChecksum), &hash, sizeof header.nChecksum); |
| 299 | // If the peer speaks an old protocol version (<209) as indicated by vSend.nVersion, header.nChecksum is excluded from serialization (see CMessageHeader); no need for special handling here as in prior revisions. |
| 300 | // The following may temporarily overflow the sending buffer size limit; that's OK because we rectify it before releasing cs_vSend. |
| 301 | vSend << header; |
| 302 | vSend += payload; |
| 303 | } |
| 304 | catch (...) |
| 305 | { |
| 306 | vSend.resize(nHeaderStart); |
| 307 | printf("aborted send %s %s\n", addr.ToString().c_str(), pszCommand); |
| 308 | throw; |
| 309 | } |
| 310 | |
| 311 | if (vSend.size() > SendBufferSize()) |
| 312 | { |
| 313 | printf("socket send flood control disconnect (%d B)\n", vSend.size()); |
| 314 | vSend.clear(); |
| 315 | CloseSocketDisconnect(); |
| 316 | throw peer_disconnected_error(); |
| 317 | } |
| 318 | } |
| 319 | |
| 320 | void PushGetBlocks(CBlockIndex* pindexBegin, uint256 hashEnd); |
| 321 | void CloseSocketDisconnect(); |
| 322 | |
| 323 | |
| 324 | // Denial-of-service detection/prevention |
| 325 | // The idea is to detect peers that are behaving |
| 326 | // badly and disconnect/ban them, but do it in a |
| 327 | // one-coding-mistake-won't-shatter-the-entire-network |
| 328 | // way. |
| 329 | // IMPORTANT: There should be nothing I can give a |
| 330 | // node that it will forward on that will make that |
| 331 | // node's peers drop it. If there is, an attacker |
| 332 | // can isolate a node and/or try to split the network. |
| 333 | // Dropping a node for sending stuff that is invalid |
| 334 | // now but might be valid in a later version is also |
| 335 | // dangerous, because it can cause a network split |
| 336 | // between nodes running old code and nodes running |
| 337 | // new code. |
| 338 | static void ClearBanned(); // needed for unit testing |
| 339 | static bool IsBanned(unsigned int ip); |
| 340 | bool Misbehaving(int howmuch); // 1 == a little, 100 == a lot |
| 341 | }; |
| 342 | |
| 343 | |
| 344 | |
| 345 | |
| 346 | |
| 347 | |
| 348 | |
| 349 | |
| 350 | |
| 351 | |
| 352 | inline void RelayInventory(const CInv& inv) |
| 353 | { |
| 354 | // Put on lists to offer to the other nodes |
| 355 | SCOPED_LOCK(cs_vNodes); |
| 356 | BOOST_FOREACH(CNode* pnode, vNodes) |
| 357 | pnode->PushInventory(inv); |
| 358 | } |
| 359 | |
| 360 | template<typename T> |
| 361 | void RelayMessage(const CInv& inv, const T& a) |
| 362 | { |
| 363 | CDataStream ss(SER_NETWORK); |
| 364 | ss.reserve(10000); |
| 365 | ss << a; |
| 366 | RelayMessage(inv, ss); |
| 367 | } |
| 368 | |
| 369 | template<> |
| 370 | inline void RelayMessage<>(const CInv& inv, const CDataStream& ss) |
| 371 | { |
| 372 | CRITICAL_BLOCK(cs_mapRelay) |
| 373 | { |
| 374 | // Expire old relay messages |
| 375 | while (!vRelayExpiration.empty() && vRelayExpiration.front().first < GetTime()) |
| 376 | { |
| 377 | mapRelay.erase(vRelayExpiration.front().second); |
| 378 | vRelayExpiration.pop_front(); |
| 379 | } |
| 380 | |
| 381 | // Save original serialized message so newer versions are preserved |
| 382 | mapRelay[inv] = ss; |
| 383 | vRelayExpiration.push_back(std::make_pair(GetTime() + 15 * 60, inv)); |
| 384 | } |
| 385 | |
| 386 | RelayInventory(inv); |
| 387 | } |
| 388 | |
| 389 | #endif |