upleb.uk

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

summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBitflash <admin@bitflash.network>2026-08-13 16:07:04 -0300
committerBitflash <admin@bitflash.network>2026-08-13 16:07:04 -0300
commitea08342aba66400f588c6a03685a6f39927cb062 (patch)
tree3d7fa8c459fb3b493b878f68b85cb53b7c16659e /src
parent8e21e8ca0b8d50c29376bc2f0a89575baa477213 (diff)
parent5309170e567f4a8655b3b622e7b26b6d8b382942 (diff)
Merge pull request 'net: add onion-only btf dial mode' (#35) from managed-tor-two-node-smoke into main
Diffstat (limited to 'src')
-rw-r--r--src/main_gui.cpp4
-rw-r--r--src/net.cpp25
-rw-r--r--src/net.h1
-rw-r--r--src/nostr.cpp11
-rw-r--r--src/proxy.cpp51
5 files changed, 81 insertions, 11 deletions
diff --git a/src/main_gui.cpp b/src/main_gui.cpp
index deed471..f0fe903 100644
--- a/src/main_gui.cpp
+++ b/src/main_gui.cpp
@@ -143,6 +143,7 @@ static void PrintUsage()
143 printf(" /socks=HOST:PORT (SOCKS5 proxy for Nostr, .btf relay, and onion dials)\n"); 143 printf(" /socks=HOST:PORT (SOCKS5 proxy for Nostr, .btf relay, and onion dials)\n");
144 printf(" /tor[=HOST:PORT] (Tor mode; default SOCKS5 proxy is 127.0.0.1:9050)\n"); 144 printf(" /tor[=HOST:PORT] (Tor mode; default SOCKS5 proxy is 127.0.0.1:9050)\n");
145 printf(" /managedtor[=PATH] (start Tor, create a hidden service, advertise its onion)\n"); 145 printf(" /managedtor[=PATH] (start Tor, create a hidden service, advertise its onion)\n");
146 printf(" /oniononly (do not fall back to rendezvous when a .btf onion dial fails)\n");
146 printf(" /btfseed=ADDRESS:ENCHEX (extra bootstrap peer, repeatable)\n"); 147 printf(" /btfseed=ADDRESS:ENCHEX (extra bootstrap peer, repeatable)\n");
147 printf("\n"); 148 printf("\n");
148 printf("Wallet:\n"); 149 printf("Wallet:\n");
@@ -278,6 +279,7 @@ static void ParseStartupArguments(int argc, char* argv[])
278 string btfConnect = argval2(argc, argv, "/connectbtf", "-connectbtf"); 279 string btfConnect = argval2(argc, argv, "/connectbtf", "-connectbtf");
279 if (!btfConnect.empty()) 280 if (!btfConnect.empty())
280 strBtfConnect = btfConnect; 281 strBtfConnect = btfConnect;
282 fBtfOnionOnly = arg(argc, argv, "/oniononly") || arg(argc, argv, "-oniononly");
281 283
282 string rvRelay = argval2(argc, argv, "/rvrelay", "-rvrelay"); 284 string rvRelay = argval2(argc, argv, "/rvrelay", "-rvrelay");
283 if (!rvRelay.empty()) 285 if (!rvRelay.empty())
@@ -360,6 +362,8 @@ static void ParseStartupArguments(int argc, char* argv[])
360 BtfSocks5ProxyName().c_str()); 362 BtfSocks5ProxyName().c_str());
361 } 363 }
362 } 364 }
365 if (fBtfOnionOnly && !BtfSocks5ProxyEnabled())
366 fprintf(stderr, "Warning: /oniononly without /tor, /managedtor, or /socks cannot dial .onion peers\n");
363 367
364 // /btfseed=ADDRESS:ENCHEX -- extra bootstrap peers, repeatable. Useful for 368 // /btfseed=ADDRESS:ENCHEX -- extra bootstrap peers, repeatable. Useful for
365 // testing the seed path and for private networks that ship no compiled list. 369 // testing the seed path and for private networks that ship no compiled list.
diff --git a/src/net.cpp b/src/net.cpp
index 6bc3c1c..997a2bd 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -65,6 +65,8 @@ deque<pair<int64, CInv> > vRelayExpiration;
65CCriticalSection cs_mapRelay; 65CCriticalSection cs_mapRelay;
66map<CInv, int64> mapAlreadyAskedFor; 66map<CInv, int64> mapAlreadyAskedFor;
67string strBtfConnect; // .btf peer to keep connected to (from /connectbtf) 67string strBtfConnect; // .btf peer to keep connected to (from /connectbtf)
68bool fBtfOnionOnly = false; // require direct onion transport for .btf peers
69static const int BTF_ONION_CONNECT_TIMEOUT_SECS = 120;
68 70
69int nPeersWatched = 0; 71int nPeersWatched = 0;
70int64 nBlocksReceived = 0; 72int64 nBlocksReceived = 0;
@@ -1022,7 +1024,8 @@ static CNode* ConnectNodeBtfTail(const string& strBtfAddr, const unsigned char p
1022 if (SplitHostPort(strOnionNorm, strOnionHost, nOnionPort)) 1024 if (SplitHostPort(strOnionNorm, strOnionHost, nOnionPort))
1023 { 1025 {
1024 BtfChurnNoteDialAttempt(strBtfAddr, strOnionNorm); 1026 BtfChurnNoteDialAttempt(strBtfAddr, strOnionNorm);
1025 SOCKET hOnionSocket = BtfConnectSocket(strOnionHost, nOnionPort, SOCK_ONION_PEER, 20); 1027 SOCKET hOnionSocket = BtfConnectSocket(strOnionHost, nOnionPort, SOCK_ONION_PEER,
1028 BTF_ONION_CONNECT_TIMEOUT_SECS);
1026 if (hOnionSocket != INVALID_SOCKET) 1029 if (hOnionSocket != INVALID_SOCKET)
1027 { 1030 {
1028 if (fDebug) 1031 if (fDebug)
@@ -1040,12 +1043,23 @@ static CNode* ConnectNodeBtfTail(const string& strBtfAddr, const unsigned char p
1040 return pnode; 1043 return pnode;
1041 } 1044 }
1042 if (fDebug) 1045 if (fDebug)
1043 LogPrint("net", "ConnectNodeBtf: direct onion to %s at %s failed, falling back to rendezvous\n", 1046 LogPrint("net", "ConnectNodeBtf: direct onion to %s at %s failed%s\n",
1044 strBtfAddr.c_str(), strOnionNorm.c_str()); 1047 strBtfAddr.c_str(), strOnionNorm.c_str(),
1048 fBtfOnionOnly ? "" : ", falling back to rendezvous");
1045 BtfChurnNoteDialResult(strBtfAddr, strOnionNorm, false); 1049 BtfChurnNoteDialResult(strBtfAddr, strOnionNorm, false);
1050 if (fBtfOnionOnly)
1051 return NULL;
1046 } 1052 }
1047 } 1053 }
1048 1054
1055 if (fBtfOnionOnly)
1056 {
1057 if (fDebug)
1058 LogPrint("net", "ConnectNodeBtf: onion-only mode refused rendezvous fallback for %s\n",
1059 strBtfAddr.c_str());
1060 return NULL;
1061 }
1062
1049 BtfChurnNoteDialAttempt(strBtfAddr, strMeeting); 1063 BtfChurnNoteDialAttempt(strBtfAddr, strMeeting);
1050 string strHost; 1064 string strHost;
1051 unsigned short nPort = 0; 1065 unsigned short nPort = 0;
@@ -2012,6 +2026,11 @@ bool StartNode(string& strError)
2012 // IP address, and port for the socket that is being bound 2026 // IP address, and port for the socket that is being bound
2013 int nRetryLimit = 15; 2027 int nRetryLimit = 15;
2014 struct sockaddr_in sockaddr = addrLocalHost.GetSockAddr(); 2028 struct sockaddr_in sockaddr = addrLocalHost.GetSockAddr();
2029 // Keep addrLocalHost as this node's advertised address, but listen on all
2030 // local interfaces. Managed Tor forwards hidden-service traffic to
2031 // 127.0.0.1:<port>, and binding only to the LAN address made that onion
2032 // endpoint unreachable even though the P2P listener was running.
2033 sockaddr.sin_addr.s_addr = INADDR_ANY;
2015 if (bind(hListenSocket, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) == SOCKET_ERROR) 2034 if (bind(hListenSocket, (struct sockaddr*)&sockaddr, sizeof(sockaddr)) == SOCKET_ERROR)
2016 { 2035 {
2017 int nErr = WSAGetLastError(); 2036 int nErr = WSAGetLastError();
diff --git a/src/net.h b/src/net.h
index 9300169..984588b 100644
--- a/src/net.h
+++ b/src/net.h
@@ -33,6 +33,7 @@ CNode* ConnectNodeBtfResolved(const string& strBtfAddr, const string& strMeeting
33void ThreadBtfAccept(void* parg); 33void ThreadBtfAccept(void* parg);
34void ThreadBtfConnect(void* parg); 34void ThreadBtfConnect(void* parg);
35extern string strBtfConnect; 35extern string strBtfConnect;
36extern bool fBtfOnionOnly;
36// Bootstrap seeds added at runtime with /btfseed=ADDRESS:ENCHEX (repeatable). 37// Bootstrap seeds added at runtime with /btfseed=ADDRESS:ENCHEX (repeatable).
37// The compiled-in list lives in net.cpp; these are appended to it. 38// The compiled-in list lives in net.cpp; these are appended to it.
38extern std::vector<std::pair<std::string, std::string> > vBtfExtraSeeds; 39extern std::vector<std::pair<std::string, std::string> > vBtfExtraSeeds;
diff --git a/src/nostr.cpp b/src/nostr.cpp
index 0c1b4b6..35fde43 100644
--- a/src/nostr.cpp
+++ b/src/nostr.cpp
@@ -1764,11 +1764,12 @@ void ThreadNostrSeed(void* parg)
1764 LogPrint("nostr", "Nostr: node pubkey = %s\n", key.PubKeyHex().c_str()); 1764 LogPrint("nostr", "Nostr: node pubkey = %s\n", key.PubKeyHex().c_str());
1765 LogPrint("nostr", "Nostr: node .btf address = %s\n", key.BtfAddress().c_str()); 1765 LogPrint("nostr", "Nostr: node .btf address = %s\n", key.BtfAddress().c_str());
1766 1766
1767 // The self-test below publishes a real descriptor, and PublishDescriptor now 1767 // The self-test below publishes a real descriptor. Rendezvous-only nodes
1768 // refuses to publish before ThreadBtfAccept has registered a rendezvous. Give 1768 // need ThreadBtfAccept to register first; Tor nodes can publish as soon as
1769 // registration a bounded head start so the test measures the publish/resolve 1769 // their signed onion endpoint exists, with rendezvous marked pending.
1770 // chain instead of losing the race against it. 1770 for (int i = 0; i < 60 && !fShutdown &&
1771 for (int i = 0; i < 60 && !fShutdown && BtfActiveRelay().empty(); i++) 1771 BtfActiveRelay().empty() &&
1772 BtfLocalOnionEndpoint().empty(); i++)
1772 Sleep(1000); 1773 Sleep(1000);
1773 1774
1774 // One-time self-test of the rendezvous: publish our descriptor to a relay, 1775 // One-time self-test of the rendezvous: publish our descriptor to a relay,
diff --git a/src/proxy.cpp b/src/proxy.cpp
index 7d30911..eed054c 100644
--- a/src/proxy.cpp
+++ b/src/proxy.cpp
@@ -13,6 +13,7 @@ static bool g_fTorProxy = false;
13static std::string g_socks5Host; 13static std::string g_socks5Host;
14static unsigned short g_socks5Port = 0; 14static unsigned short g_socks5Port = 0;
15static const char* DEFAULT_TOR_SOCKS5_PROXY = "127.0.0.1:9050"; 15static const char* DEFAULT_TOR_SOCKS5_PROXY = "127.0.0.1:9050";
16static const int TOR_CONNECT_TIMEOUT_SECS = 120;
16 17
17static void SetSocketTimeout(SOCKET s, int nTimeoutSecs) 18static void SetSocketTimeout(SOCKET s, int nTimeoutSecs)
18{ 19{
@@ -297,10 +298,25 @@ static bool Socks5Connect(SOCKET s, const std::string& destHost,
297 298
298 unsigned char hello[3] = { 0x05, 0x01, 0x00 }; // SOCKS5, one method, no auth 299 unsigned char hello[3] = { 0x05, 0x01, 0x00 }; // SOCKS5, one method, no auth
299 if (!WriteN(s, hello, sizeof(hello))) 300 if (!WriteN(s, hello, sizeof(hello)))
301 {
302 error("SOCKS5 hello write failed for %s:%u\n",
303 destHost.c_str(), (unsigned)destPort);
300 return false; 304 return false;
305 }
301 unsigned char choice[2] = { 0, 0 }; 306 unsigned char choice[2] = { 0, 0 };
302 if (!ReadN(s, choice, sizeof(choice)) || choice[0] != 0x05 || choice[1] != 0x00) 307 if (!ReadN(s, choice, sizeof(choice)))
308 {
309 error("SOCKS5 method selection read failed for %s:%u\n",
310 destHost.c_str(), (unsigned)destPort);
311 return false;
312 }
313 if (choice[0] != 0x05 || choice[1] != 0x00)
314 {
315 error("SOCKS5 proxy rejected no-auth method for %s:%u (version=0x%02x method=0x%02x)\n",
316 destHost.c_str(), (unsigned)destPort,
317 (unsigned)choice[0], (unsigned)choice[1]);
303 return false; 318 return false;
319 }
304 320
305 std::vector<unsigned char> req; 321 std::vector<unsigned char> req;
306 req.reserve(7 + destHost.size()); 322 req.reserve(7 + destHost.size());
@@ -313,11 +329,25 @@ static bool Socks5Connect(SOCKET s, const std::string& destHost,
313 req.push_back((unsigned char)(destPort >> 8)); 329 req.push_back((unsigned char)(destPort >> 8));
314 req.push_back((unsigned char)(destPort & 0xff)); 330 req.push_back((unsigned char)(destPort & 0xff));
315 if (!WriteN(s, &req[0], (int)req.size())) 331 if (!WriteN(s, &req[0], (int)req.size()))
332 {
333 error("SOCKS5 CONNECT request write failed for %s:%u\n",
334 destHost.c_str(), (unsigned)destPort);
316 return false; 335 return false;
336 }
317 337
318 unsigned char rep[4] = {0,0,0,0}; 338 unsigned char rep[4] = {0,0,0,0};
319 if (!ReadN(s, rep, sizeof(rep)) || rep[0] != 0x05) 339 if (!ReadN(s, rep, sizeof(rep)))
340 {
341 error("SOCKS5 CONNECT reply read failed for %s:%u\n",
342 destHost.c_str(), (unsigned)destPort);
320 return false; 343 return false;
344 }
345 if (rep[0] != 0x05)
346 {
347 error("SOCKS5 CONNECT reply has invalid version 0x%02x for %s:%u\n",
348 (unsigned)rep[0], destHost.c_str(), (unsigned)destPort);
349 return false;
350 }
321 if (rep[1] != 0x00) 351 if (rep[1] != 0x00)
322 { 352 {
323 error("SOCKS5 CONNECT to %s:%u failed with reply 0x%02x\n", 353 error("SOCKS5 CONNECT to %s:%u failed with reply 0x%02x\n",
@@ -335,10 +365,20 @@ static bool Socks5Connect(SOCKET s, const std::string& destHost,
335 nAddr = len; 365 nAddr = len;
336 } 366 }
337 else 367 else
368 {
369 error("SOCKS5 CONNECT reply has invalid address type 0x%02x for %s:%u\n",
370 (unsigned)rep[3], destHost.c_str(), (unsigned)destPort);
338 return false; 371 return false;
372 }
339 373
340 std::vector<unsigned char> discard(nAddr + 2); 374 std::vector<unsigned char> discard(nAddr + 2);
341 return ReadN(s, &discard[0], (int)discard.size()); 375 if (!ReadN(s, &discard[0], (int)discard.size()))
376 {
377 error("SOCKS5 CONNECT bind address read failed for %s:%u\n",
378 destHost.c_str(), (unsigned)destPort);
379 return false;
380 }
381 return true;
342} 382}
343 383
344SOCKET BtfConnectSocket(const std::string& destHost, unsigned short destPort, 384SOCKET BtfConnectSocket(const std::string& destHost, unsigned short destPort,
@@ -347,9 +387,11 @@ SOCKET BtfConnectSocket(const std::string& destHost, unsigned short destPort,
347 std::string proxyHost; 387 std::string proxyHost;
348 unsigned short proxyPort = 0; 388 unsigned short proxyPort = 0;
349 bool fProxy = false; 389 bool fProxy = false;
390 bool fTorProxy = false;
350 CRITICAL_BLOCK(cs_socks5) 391 CRITICAL_BLOCK(cs_socks5)
351 { 392 {
352 fProxy = g_fSocks5Proxy; 393 fProxy = g_fSocks5Proxy;
394 fTorProxy = g_fTorProxy;
353 proxyHost = g_socks5Host; 395 proxyHost = g_socks5Host;
354 proxyPort = g_socks5Port; 396 proxyPort = g_socks5Port;
355 } 397 }
@@ -365,6 +407,9 @@ SOCKET BtfConnectSocket(const std::string& destHost, unsigned short destPort,
365 return ConnectDirect(destHost, destPort, nSockSite, nTimeoutSecs); 407 return ConnectDirect(destHost, destPort, nSockSite, nTimeoutSecs);
366 } 408 }
367 409
410 if (fTorProxy && nTimeoutSecs > 0 && nTimeoutSecs < TOR_CONNECT_TIMEOUT_SECS)
411 nTimeoutSecs = TOR_CONNECT_TIMEOUT_SECS;
412
368 SOCKET s = ConnectDirect(proxyHost, proxyPort, nSockSite, nTimeoutSecs); 413 SOCKET s = ConnectDirect(proxyHost, proxyPort, nSockSite, nTimeoutSecs);
369 if (s == INVALID_SOCKET) 414 if (s == INVALID_SOCKET)
370 return INVALID_SOCKET; 415 return INVALID_SOCKET;