diff options
| author | bitflash-maintainer <noreply@bitflash.invalid> | 2026-08-19 18:17:06 +0000 |
|---|---|---|
| committer | bitflash-maintainer <noreply@bitflash.invalid> | 2026-08-19 22:48:27 +0000 |
| commit | 11766bec0ea465da263f78a6f473ca792da59280 (patch) | |
| tree | f07c7eecabdf6a0b5f2299447dcf957472e7ef58 /src/net.h | |
| parent | 88d30ade28d6836e64d0157da57896bdb320e8a2 (diff) | |
net: stop serving the 2009 marketplace to anonymous peers
Four inherited handlers took attacker-controlled input and wrote to disk
with nothing authorising it. None of them had a caller left anywhere in
the tree, so all four were reachable only from the outside.
checkorder called GenerateNewKey() -- AddKey(), a wallet.dat write -- for
any peer that asked, and handed back the pubkey. Its comment claims the
cache keeps one key per ip, and on a 2009 network of routable addresses
it did. Not here: inbound peers arrive through a rendezvous tunnel and
are tagged by BtfMarkerAddr() over three random bytes, so addr.ip is
fresh every connection and the cache never hits. Reconnect in a loop and
the node mints a key per connection, none of them in any backup taken
beforehand, which for a pre-BIP32 wallet is how coins go missing. The map
holding them was static and never trimmed.
submitorder AddToWallet()'d a CWalletTx from any peer that sent one. It
had to be a valid transaction; it did not have to be ours.
review checked a signature the sender made with its own key, which proves
possession and authorises nothing, then appended to reviews.dat by
reading the whole vector, pushing one entry and writing it all back --
quadratic disk on an unbounded map<string,string> off the socket -- then
walked a link graph the same peer supplied and relayed the message to
every other node.
reply dispatched into CRequestTracker. Nothing ever called PushRequest,
so the table was permanently empty and the handler was a parser and
nothing else.
Removed with the subsystem behind them: market.cpp, CProduct, CReview,
CUser, CReviewDB, the advert publish templates and the subscribe channel
machinery, none of which had an inbound handler either. MSG_REVIEW,
MSG_PRODUCT and MSG_TABLE stay in the inv enum, unserved: CInv puts type
on the wire as an integer and MSG_TX and MSG_BLOCK are shared with every
other node.
Unknown commands are already ignored for extensibility, so peers still
running these lose nothing but an answer they never asked for. Verified
by a clean build of bitflash-node.
(cherry picked from commit ad9c393866c007a115f32d70a9017ce91066a7d1)
Diffstat (limited to 'src/net.h')
| -rw-r--r-- | src/net.h | 138 |
1 files changed, 17 insertions, 121 deletions
| @@ -5,7 +5,6 @@ | |||
| 5 | class CMessageHeader; | 5 | class CMessageHeader; |
| 6 | class CAddress; | 6 | class CAddress; |
| 7 | class CInv; | 7 | class CInv; |
| 8 | class CRequestTracker; | ||
| 9 | class CNode; | 8 | class CNode; |
| 10 | // Defined in main.h, which is included after this file. Only ever used here as | 9 | // Defined in main.h, which is included after this file. Only ever used here as |
| 11 | // a pointer, so the forward declaration is enough. | 10 | // a pointer, so the forward declaration is enough. |
| @@ -14,7 +13,6 @@ class CBlockIndex; | |||
| 14 | 13 | ||
| 15 | 14 | ||
| 16 | static const unsigned short DEFAULT_PORT = htons(8433); | 15 | static const unsigned short DEFAULT_PORT = htons(8433); |
| 17 | static const unsigned int PUBLISH_HOPS = 5; | ||
| 18 | enum | 16 | enum |
| 19 | { | 17 | { |
| 20 | NODE_NETWORK = (1 << 0), | 18 | NODE_NETWORK = (1 << 0), |
| @@ -214,8 +212,6 @@ void BtfChurnNoteHandshakeTimeout(const string& strBtfAddr, const string& strMee | |||
| 214 | void BtfPexCollect(std::vector<std::string>& vDescOut); | 212 | void BtfPexCollect(std::vector<std::string>& vDescOut); |
| 215 | // Verify descriptors a peer sent and remember the good ones. Returns how many. | 213 | // Verify descriptors a peer sent and remember the good ones. Returns how many. |
| 216 | int BtfPexAccept(const std::vector<std::string>& vDesc); | 214 | int BtfPexAccept(const std::vector<std::string>& vDesc); |
| 217 | void AbandonRequests(void (*fn)(void*, CDataStream&), void* param1); | ||
| 218 | bool AnySubscribed(unsigned int nChannel); | ||
| 219 | void ThreadBitcoinMiner(void* parg); | 215 | void ThreadBitcoinMiner(void* parg); |
| 220 | // Starts MinerThreadCount() miners; returns how many came up. | 216 | // Starts MinerThreadCount() miners; returns how many came up. |
| 221 | int StartMinerThreads(); | 217 | int StartMinerThreads(); |
| @@ -489,13 +485,19 @@ public: | |||
| 489 | 485 | ||
| 490 | 486 | ||
| 491 | 487 | ||
| 488 | // MSG_REVIEW, MSG_PRODUCT and MSG_TABLE no longer have a handler, a producer | ||
| 489 | // or a store -- the marketplace they belonged to is gone. They stay in the | ||
| 490 | // enum, and their names stay in ppszTypeName below, because CInv serialises | ||
| 491 | // `type` as a plain integer: dropping them would renumber nothing today but | ||
| 492 | // would make it tempting to renumber later, and MSG_TX and MSG_BLOCK are wire | ||
| 493 | // values shared with every other node. Reserved, never emitted, never served. | ||
| 492 | enum | 494 | enum |
| 493 | { | 495 | { |
| 494 | MSG_TX = 1, | 496 | MSG_TX = 1, |
| 495 | MSG_BLOCK, | 497 | MSG_BLOCK, |
| 496 | MSG_REVIEW, | 498 | MSG_REVIEW, // reserved, dead |
| 497 | MSG_PRODUCT, | 499 | MSG_PRODUCT, // reserved, dead |
| 498 | MSG_TABLE, | 500 | MSG_TABLE, // reserved, dead |
| 499 | }; | 501 | }; |
| 500 | 502 | ||
| 501 | static const char* ppszTypeName[] = | 503 | static const char* ppszTypeName[] = |
| @@ -582,23 +584,9 @@ public: | |||
| 582 | 584 | ||
| 583 | 585 | ||
| 584 | 586 | ||
| 585 | class CRequestTracker | 587 | // CRequestTracker and the PushRequest/"reply" round trip lived here. Nothing |
| 586 | { | 588 | // in this tree ever called PushRequest, so the table was always empty and the |
| 587 | public: | 589 | // "reply" handler was pure inbound parser. Both removed. |
| 588 | void (*fn)(void*, CDataStream&); | ||
| 589 | void* param1; | ||
| 590 | |||
| 591 | explicit CRequestTracker(void (*fnIn)(void*, CDataStream&)=NULL, void* param1In=NULL) | ||
| 592 | { | ||
| 593 | fn = fnIn; | ||
| 594 | param1 = param1In; | ||
| 595 | } | ||
| 596 | |||
| 597 | bool IsNull() | ||
| 598 | { | ||
| 599 | return fn == NULL; | ||
| 600 | } | ||
| 601 | }; | ||
| 602 | 590 | ||
| 603 | 591 | ||
| 604 | 592 | ||
| @@ -644,9 +632,6 @@ protected: | |||
| 644 | public: | 632 | public: |
| 645 | int64 nReleaseTime; | 633 | int64 nReleaseTime; |
| 646 | int64 nDisconnectSince; | 634 | int64 nDisconnectSince; |
| 647 | map<uint256, CRequestTracker> mapRequests; | ||
| 648 | CCriticalSection cs_mapRequests; | ||
| 649 | |||
| 650 | // inventory based relay | 635 | // inventory based relay |
| 651 | set<CInv> setInventoryKnown; | 636 | set<CInv> setInventoryKnown; |
| 652 | set<CInv> setInventoryKnown2; | 637 | set<CInv> setInventoryKnown2; |
| @@ -654,9 +639,6 @@ public: | |||
| 654 | CCriticalSection cs_inventory; | 639 | CCriticalSection cs_inventory; |
| 655 | multimap<int64, CInv> mapAskFor; | 640 | multimap<int64, CInv> mapAskFor; |
| 656 | 641 | ||
| 657 | // publish and subscription | ||
| 658 | vector<char> vfSubscribe; | ||
| 659 | |||
| 660 | // Last "btfpeers" we accepted from this node, to rate-limit the exchange. | 642 | // Last "btfpeers" we accepted from this node, to rate-limit the exchange. |
| 661 | int64 nLastPexRecv; | 643 | int64 nLastPexRecv; |
| 662 | 644 | ||
| @@ -723,7 +705,6 @@ public: | |||
| 723 | strBtfAddr.clear(); | 705 | strBtfAddr.clear(); |
| 724 | strBtfMeeting.clear(); | 706 | strBtfMeeting.clear(); |
| 725 | nStartingHeight = -1; | 707 | nStartingHeight = -1; |
| 726 | vfSubscribe.assign(256, false); | ||
| 727 | 708 | ||
| 728 | // Push a version message | 709 | // Push a version message |
| 729 | /// when NTP implemented, change to just nTime = GetAdjustedTime() | 710 | /// when NTP implemented, change to just nTime = GetAdjustedTime() |
| @@ -1002,49 +983,6 @@ public: | |||
| 1002 | } | 983 | } |
| 1003 | 984 | ||
| 1004 | 985 | ||
| 1005 | void PushRequest(const char* pszCommand, | ||
| 1006 | void (*fn)(void*, CDataStream&), void* param1) | ||
| 1007 | { | ||
| 1008 | uint256 hashReply; | ||
| 1009 | RAND_bytes((unsigned char*)&hashReply, sizeof(hashReply)); | ||
| 1010 | |||
| 1011 | CRITICAL_BLOCK(cs_mapRequests) | ||
| 1012 | mapRequests[hashReply] = CRequestTracker(fn, param1); | ||
| 1013 | |||
| 1014 | PushMessage(pszCommand, hashReply); | ||
| 1015 | } | ||
| 1016 | |||
| 1017 | template<typename T1> | ||
| 1018 | void PushRequest(const char* pszCommand, const T1& a1, | ||
| 1019 | void (*fn)(void*, CDataStream&), void* param1) | ||
| 1020 | { | ||
| 1021 | uint256 hashReply; | ||
| 1022 | RAND_bytes((unsigned char*)&hashReply, sizeof(hashReply)); | ||
| 1023 | |||
| 1024 | CRITICAL_BLOCK(cs_mapRequests) | ||
| 1025 | mapRequests[hashReply] = CRequestTracker(fn, param1); | ||
| 1026 | |||
| 1027 | PushMessage(pszCommand, hashReply, a1); | ||
| 1028 | } | ||
| 1029 | |||
| 1030 | template<typename T1, typename T2> | ||
| 1031 | void PushRequest(const char* pszCommand, const T1& a1, const T2& a2, | ||
| 1032 | void (*fn)(void*, CDataStream&), void* param1) | ||
| 1033 | { | ||
| 1034 | uint256 hashReply; | ||
| 1035 | RAND_bytes((unsigned char*)&hashReply, sizeof(hashReply)); | ||
| 1036 | |||
| 1037 | CRITICAL_BLOCK(cs_mapRequests) | ||
| 1038 | mapRequests[hashReply] = CRequestTracker(fn, param1); | ||
| 1039 | |||
| 1040 | PushMessage(pszCommand, hashReply, a1, a2); | ||
| 1041 | } | ||
| 1042 | |||
| 1043 | |||
| 1044 | |||
| 1045 | bool IsSubscribed(unsigned int nChannel); | ||
| 1046 | void Subscribe(unsigned int nChannel, unsigned int nHops=0); | ||
| 1047 | void CancelSubscribe(unsigned int nChannel); | ||
| 1048 | void Disconnect(); | 986 | void Disconnect(); |
| 1049 | }; | 987 | }; |
| 1050 | 988 | ||
| @@ -1101,50 +1039,8 @@ inline void RelayMessage<>(const CInv& inv, const CDataStream& ss) | |||
| 1101 | 1039 | ||
| 1102 | 1040 | ||
| 1103 | 1041 | ||
| 1104 | // | 1042 | // The publish/subscribe advert templates that used to close this file |
| 1105 | // Templates for the publish and subscription system. | 1043 | // (AdvertStartPublish / AdvertStopPublish / AdvertRemoveSource) are gone along |
| 1106 | // The object being published as T& obj needs to have: | 1044 | // with the marketplace objects they carried. Nothing ever handled an inbound |
| 1107 | // a set<unsigned int> setSources member | 1045 | // "publish" or "pub-cancel", so the machinery only ever pushed; what it pushed |
| 1108 | // specializations of AdvertInsert and AdvertErase | 1046 | // was CProduct, which no longer exists. |
| 1109 | // Currently implemented for CTable and CProduct. | ||
| 1110 | // | ||
| 1111 | |||
| 1112 | template<typename T> | ||
| 1113 | void AdvertStartPublish(CNode* pfrom, unsigned int nChannel, unsigned int nHops, T& obj) | ||
| 1114 | { | ||
| 1115 | // Add to sources | ||
| 1116 | obj.setSources.insert(pfrom->addr.ip); | ||
| 1117 | |||
| 1118 | if (!AdvertInsert(obj)) | ||
| 1119 | return; | ||
| 1120 | |||
| 1121 | // Relay | ||
| 1122 | CRITICAL_BLOCK(cs_vNodes) | ||
| 1123 | foreach(CNode* pnode, vNodes) | ||
| 1124 | if (pnode != pfrom && (nHops < PUBLISH_HOPS || pnode->IsSubscribed(nChannel))) | ||
| 1125 | pnode->PushMessage("publish", nChannel, nHops, obj); | ||
| 1126 | } | ||
| 1127 | |||
| 1128 | template<typename T> | ||
| 1129 | void AdvertStopPublish(CNode* pfrom, unsigned int nChannel, unsigned int nHops, T& obj) | ||
| 1130 | { | ||
| 1131 | uint256 hash = obj.GetHash(); | ||
| 1132 | |||
| 1133 | CRITICAL_BLOCK(cs_vNodes) | ||
| 1134 | foreach(CNode* pnode, vNodes) | ||
| 1135 | if (pnode != pfrom && (nHops < PUBLISH_HOPS || pnode->IsSubscribed(nChannel))) | ||
| 1136 | pnode->PushMessage("pub-cancel", nChannel, nHops, hash); | ||
| 1137 | |||
| 1138 | AdvertErase(obj); | ||
| 1139 | } | ||
| 1140 | |||
| 1141 | template<typename T> | ||
| 1142 | void AdvertRemoveSource(CNode* pfrom, unsigned int nChannel, unsigned int nHops, T& obj) | ||
| 1143 | { | ||
| 1144 | // Remove a source | ||
| 1145 | obj.setSources.erase(pfrom->addr.ip); | ||
| 1146 | |||
| 1147 | // If no longer supported by any sources, cancel it | ||
| 1148 | if (obj.setSources.empty()) | ||
| 1149 | AdvertStopPublish(pfrom, nChannel, nHops, obj); | ||
| 1150 | } | ||