upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src/net.cpp
diff options
context:
space:
mode:
authorbitflash-maintainer <noreply@bitflash.invalid>2026-08-19 18:17:06 +0000
committerbitflash-maintainer <noreply@bitflash.invalid>2026-08-19 22:48:27 +0000
commit11766bec0ea465da263f78a6f473ca792da59280 (patch)
treef07c7eecabdf6a0b5f2299447dcf957472e7ef58 /src/net.cpp
parent88d30ade28d6836e64d0157da57896bdb320e8a2 (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.cpp')
-rw-r--r--src/net.cpp109
1 files changed, 7 insertions, 102 deletions
diff --git a/src/net.cpp b/src/net.cpp
index 72a7fda..e08f6f7 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -444,28 +444,9 @@ bool GetMyExternalIP(unsigned int& ipRet)
444 444
445 445
446 446
447void AbandonRequests(void (*fn)(void*, CDataStream&), void* param1) 447// AbandonRequests() lived here, sweeping CNode::mapRequests for a callback a
448{ 448// closing dialog had registered. Nothing ever registered one and nothing ever
449 // If the dialog might get closed before the reply comes back, 449// called this, so it went with CRequestTracker.
450 // call this in the destructor so it doesn't get called after it's deleted.
451 CRITICAL_BLOCK(cs_vNodes)
452 {
453 foreach(CNode* pnode, vNodes)
454 {
455 CRITICAL_BLOCK(pnode->cs_mapRequests)
456 {
457 for (map<uint256, CRequestTracker>::iterator mi = pnode->mapRequests.begin(); mi != pnode->mapRequests.end();)
458 {
459 CRequestTracker& tracker = (*mi).second;
460 if (tracker.fn == fn && tracker.param1 == param1)
461 pnode->mapRequests.erase(mi++);
462 else
463 mi++;
464 }
465 }
466 }
467 }
468}
469 450
470 451
471 452
@@ -473,25 +454,10 @@ void AbandonRequests(void (*fn)(void*, CDataStream&), void* param1)
473 454
474 455
475 456
476// 457// The broadcast/subscribe channel system (AnySubscribed, CNode::IsSubscribed,
477// Subscription methods for the broadcast and subscription system. 458// Subscribe, CancelSubscribe) stood here. Its channels were MSG_TABLE and
478// Channel numbers are message numbers, i.e. MSG_TABLE and MSG_PRODUCT. 459// MSG_PRODUCT, there was never an inbound handler for "subscribe",
479// 460// "sub-cancel", "publish" or "pub-cancel", and the objects it carried are gone.
480// The subscription system uses a meet-in-the-middle strategy.
481// With 100,000 nodes, if senders broadcast to 1000 random nodes and receivers
482// subscribe to 1000 random nodes, 99.995% (1 - 0.99^1000) of messages will get through.
483//
484
485bool AnySubscribed(unsigned int nChannel)
486{
487 if (pnodeLocalHost->IsSubscribed(nChannel))
488 return true;
489 CRITICAL_BLOCK(cs_vNodes)
490 foreach(CNode* pnode, vNodes)
491 if (pnode->IsSubscribed(nChannel))
492 return true;
493 return false;
494}
495 461
496void CNode::PushGetBlocks(CBlockIndex* pindexBegin, uint256 hashEnd) 462void CNode::PushGetBlocks(CBlockIndex* pindexBegin, uint256 hashEnd)
497{ 463{
@@ -504,55 +470,6 @@ void CNode::PushGetBlocks(CBlockIndex* pindexBegin, uint256 hashEnd)
504 PushMessage("getblocks", CBlockLocator(pindexBegin), hashEnd); 470 PushMessage("getblocks", CBlockLocator(pindexBegin), hashEnd);
505} 471}
506 472
507bool CNode::IsSubscribed(unsigned int nChannel)
508{
509 if (nChannel >= vfSubscribe.size())
510 return false;
511 return vfSubscribe[nChannel];
512}
513
514void CNode::Subscribe(unsigned int nChannel, unsigned int nHops)
515{
516 if (nChannel >= vfSubscribe.size())
517 return;
518
519 if (!AnySubscribed(nChannel))
520 {
521 // Relay subscribe
522 CRITICAL_BLOCK(cs_vNodes)
523 foreach(CNode* pnode, vNodes)
524 if (pnode != this)
525 pnode->PushMessage("subscribe", nChannel, nHops);
526 }
527
528 vfSubscribe[nChannel] = true;
529}
530
531void CNode::CancelSubscribe(unsigned int nChannel)
532{
533 if (nChannel >= vfSubscribe.size())
534 return;
535
536 // Prevent from relaying cancel if wasn't subscribed
537 if (!vfSubscribe[nChannel])
538 return;
539 vfSubscribe[nChannel] = false;
540
541 if (!AnySubscribed(nChannel))
542 {
543 // Relay subscription cancel
544 CRITICAL_BLOCK(cs_vNodes)
545 foreach(CNode* pnode, vNodes)
546 if (pnode != this)
547 pnode->PushMessage("sub-cancel", nChannel);
548
549 // Clear memory, no longer subscribed
550 if (nChannel == MSG_PRODUCT)
551 CRITICAL_BLOCK(cs_mapProducts)
552 mapProducts.clear();
553 }
554}
555
556 473
557 474
558 475
@@ -1215,17 +1132,6 @@ void CNode::Disconnect()
1215 BtfCloseSocket(hSocket); 1132 BtfCloseSocket(hSocket);
1216 hSocket = INVALID_SOCKET; 1133 hSocket = INVALID_SOCKET;
1217 1134
1218 // All of a nodes broadcasts and subscriptions are automatically torn down
1219 // when it goes down, so a node has to stay up to keep its broadcast going.
1220
1221 CRITICAL_BLOCK(cs_mapProducts)
1222 for (map<uint256, CProduct>::iterator mi = mapProducts.begin(); mi != mapProducts.end();)
1223 AdvertRemoveSource(this, MSG_PRODUCT, 0, (*(mi++)).second);
1224
1225 // Cancel subscriptions
1226 for (unsigned int nChannel = 0; nChannel < vfSubscribe.size(); nChannel++)
1227 if (vfSubscribe[nChannel])
1228 CancelSubscribe(nChannel);
1229} 1135}
1230 1136
1231 1137
@@ -1350,7 +1256,6 @@ void ThreadSocketHandler2(void* parg)
1350 bool fDelete = false; 1256 bool fDelete = false;
1351 TRY_CRITICAL_BLOCK(pnode->cs_vSend) 1257 TRY_CRITICAL_BLOCK(pnode->cs_vSend)
1352 TRY_CRITICAL_BLOCK(pnode->cs_vRecv) 1258 TRY_CRITICAL_BLOCK(pnode->cs_vRecv)
1353 TRY_CRITICAL_BLOCK(pnode->cs_mapRequests)
1354 TRY_CRITICAL_BLOCK(pnode->cs_inventory) 1259 TRY_CRITICAL_BLOCK(pnode->cs_inventory)
1355 fDelete = true; 1260 fDelete = true;
1356 if (fDelete) 1261 if (fDelete)