diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/main_gui.cpp | 30 | ||||
| -rw-r--r-- | src/proxy.cpp | 50 | ||||
| -rw-r--r-- | src/proxy.h | 3 | ||||
| -rw-r--r-- | src/readme.txt | 3 | ||||
| -rw-r--r-- | src/selftest.cpp | 25 |
5 files changed, 105 insertions, 6 deletions
diff --git a/src/main_gui.cpp b/src/main_gui.cpp index 90a6050..bef8503 100644 --- a/src/main_gui.cpp +++ b/src/main_gui.cpp | |||
| @@ -138,6 +138,7 @@ static void PrintUsage() | |||
| 138 | printf("Network:\n"); | 138 | printf("Network:\n"); |
| 139 | printf(" /port=N (P2P listen port, default 8433)\n"); | 139 | printf(" /port=N (P2P listen port, default 8433)\n"); |
| 140 | printf(" /socks=HOST:PORT (SOCKS5 proxy for outbound Nostr and .btf relay dials)\n"); | 140 | printf(" /socks=HOST:PORT (SOCKS5 proxy for outbound Nostr and .btf relay dials)\n"); |
| 141 | printf(" /tor[=HOST:PORT] (Tor mode; default SOCKS5 proxy is 127.0.0.1:9050)\n"); | ||
| 141 | printf(" /btfseed=ADDRESS:ENCHEX (extra bootstrap peer, repeatable)\n"); | 142 | printf(" /btfseed=ADDRESS:ENCHEX (extra bootstrap peer, repeatable)\n"); |
| 142 | printf("\n"); | 143 | printf("\n"); |
| 143 | printf("Wallet:\n"); | 144 | printf("Wallet:\n"); |
| @@ -285,16 +286,35 @@ static void ParseStartupArguments(int argc, char* argv[]) | |||
| 285 | if (!announceRelay.empty()) | 286 | if (!announceRelay.empty()) |
| 286 | strBtfAnnounceRelay = announceRelay; | 287 | strBtfAnnounceRelay = announceRelay; |
| 287 | 288 | ||
| 288 | string socksProxy = argval2(argc, argv, "/socks", "-socks"); | 289 | bool fTorMode = arg(argc, argv, "/tor") || arg(argc, argv, "-tor"); |
| 289 | if (!socksProxy.empty()) | 290 | if (fTorMode) |
| 290 | { | 291 | { |
| 292 | string socksProxy = argval2(argc, argv, "/socks", "-socks"); | ||
| 293 | if (!socksProxy.empty()) | ||
| 294 | fprintf(stderr, "Warning: /tor takes precedence over /socks=%s\n", | ||
| 295 | socksProxy.c_str()); | ||
| 296 | |||
| 297 | string torProxy = argval2(argc, argv, "/tor", "-tor"); | ||
| 291 | string err; | 298 | string err; |
| 292 | if (!BtfSetSocks5Proxy(socksProxy, err)) | 299 | if (!BtfEnableTorProxy(torProxy, err)) |
| 293 | fprintf(stderr, "Ignoring /socks=%s: %s\n", socksProxy.c_str(), err.c_str()); | 300 | fprintf(stderr, "Ignoring /tor=%s: %s\n", torProxy.c_str(), err.c_str()); |
| 294 | else | 301 | else |
| 295 | fprintf(stderr, "SOCKS5 proxy enabled for outbound discovery/relay dials: %s\n", | 302 | fprintf(stderr, "Tor mode enabled through SOCKS5 proxy %s\n", |
| 296 | BtfSocks5ProxyName().c_str()); | 303 | BtfSocks5ProxyName().c_str()); |
| 297 | } | 304 | } |
| 305 | else | ||
| 306 | { | ||
| 307 | string socksProxy = argval2(argc, argv, "/socks", "-socks"); | ||
| 308 | if (!socksProxy.empty()) | ||
| 309 | { | ||
| 310 | string err; | ||
| 311 | if (!BtfSetSocks5Proxy(socksProxy, err)) | ||
| 312 | fprintf(stderr, "Ignoring /socks=%s: %s\n", socksProxy.c_str(), err.c_str()); | ||
| 313 | else | ||
| 314 | fprintf(stderr, "SOCKS5 proxy enabled for outbound discovery/relay dials: %s\n", | ||
| 315 | BtfSocks5ProxyName().c_str()); | ||
| 316 | } | ||
| 317 | } | ||
| 298 | 318 | ||
| 299 | // /btfseed=ADDRESS:ENCHEX -- extra bootstrap peers, repeatable. Useful for | 319 | // /btfseed=ADDRESS:ENCHEX -- extra bootstrap peers, repeatable. Useful for |
| 300 | // testing the seed path and for private networks that ship no compiled list. | 320 | // testing the seed path and for private networks that ship no compiled list. |
diff --git a/src/proxy.cpp b/src/proxy.cpp index d7dc324..7d30911 100644 --- a/src/proxy.cpp +++ b/src/proxy.cpp | |||
| @@ -9,8 +9,10 @@ | |||
| 9 | 9 | ||
| 10 | static CCriticalSection cs_socks5; | 10 | static CCriticalSection cs_socks5; |
| 11 | static bool g_fSocks5Proxy = false; | 11 | static bool g_fSocks5Proxy = false; |
| 12 | static bool g_fTorProxy = false; | ||
| 12 | static std::string g_socks5Host; | 13 | static std::string g_socks5Host; |
| 13 | static unsigned short g_socks5Port = 0; | 14 | static unsigned short g_socks5Port = 0; |
| 15 | static const char* DEFAULT_TOR_SOCKS5_PROXY = "127.0.0.1:9050"; | ||
| 14 | 16 | ||
| 15 | static void SetSocketTimeout(SOCKET s, int nTimeoutSecs) | 17 | static void SetSocketTimeout(SOCKET s, int nTimeoutSecs) |
| 16 | { | 18 | { |
| @@ -67,6 +69,24 @@ static bool ValidProxyIpv6HostChar(char c) | |||
| 67 | return isxdigit((unsigned char)c) || c == ':' || c == '.'; | 69 | return isxdigit((unsigned char)c) || c == ':' || c == '.'; |
| 68 | } | 70 | } |
| 69 | 71 | ||
| 72 | static std::string LowerProxyHost(std::string host) | ||
| 73 | { | ||
| 74 | if (!host.empty() && host[host.size() - 1] == '.') | ||
| 75 | host.erase(host.size() - 1); | ||
| 76 | for (size_t i = 0; i < host.size(); i++) | ||
| 77 | host[i] = (char)tolower((unsigned char)host[i]); | ||
| 78 | return host; | ||
| 79 | } | ||
| 80 | |||
| 81 | bool BtfIsTorOnionHost(const std::string& host) | ||
| 82 | { | ||
| 83 | std::string lower = LowerProxyHost(host); | ||
| 84 | static const char* suffix = ".onion"; | ||
| 85 | size_t suffixLen = strlen(suffix); | ||
| 86 | return lower.size() > suffixLen && | ||
| 87 | lower.compare(lower.size() - suffixLen, suffixLen, suffix) == 0; | ||
| 88 | } | ||
| 89 | |||
| 70 | static bool ParsePort(const std::string& port, unsigned short& portOut, | 90 | static bool ParsePort(const std::string& port, unsigned short& portOut, |
| 71 | std::string& errOut) | 91 | std::string& errOut) |
| 72 | { | 92 | { |
| @@ -176,7 +196,8 @@ bool BtfParseSocks5Proxy(const std::string& spec, std::string& hostOut, | |||
| 176 | return true; | 196 | return true; |
| 177 | } | 197 | } |
| 178 | 198 | ||
| 179 | bool BtfSetSocks5Proxy(const std::string& spec, std::string& errOut) | 199 | static bool SetSocks5Proxy(const std::string& spec, bool fTorMode, |
| 200 | std::string& errOut) | ||
| 180 | { | 201 | { |
| 181 | std::string host; | 202 | std::string host; |
| 182 | unsigned short port = 0; | 203 | unsigned short port = 0; |
| @@ -187,15 +208,27 @@ bool BtfSetSocks5Proxy(const std::string& spec, std::string& errOut) | |||
| 187 | g_socks5Host = host; | 208 | g_socks5Host = host; |
| 188 | g_socks5Port = port; | 209 | g_socks5Port = port; |
| 189 | g_fSocks5Proxy = true; | 210 | g_fSocks5Proxy = true; |
| 211 | g_fTorProxy = fTorMode; | ||
| 190 | } | 212 | } |
| 191 | return true; | 213 | return true; |
| 192 | } | 214 | } |
| 193 | 215 | ||
| 216 | bool BtfSetSocks5Proxy(const std::string& spec, std::string& errOut) | ||
| 217 | { | ||
| 218 | return SetSocks5Proxy(spec, false, errOut); | ||
| 219 | } | ||
| 220 | |||
| 221 | bool BtfEnableTorProxy(const std::string& spec, std::string& errOut) | ||
| 222 | { | ||
| 223 | return SetSocks5Proxy(spec.empty() ? DEFAULT_TOR_SOCKS5_PROXY : spec, true, errOut); | ||
| 224 | } | ||
| 225 | |||
| 194 | void BtfClearSocks5Proxy() | 226 | void BtfClearSocks5Proxy() |
| 195 | { | 227 | { |
| 196 | CRITICAL_BLOCK(cs_socks5) | 228 | CRITICAL_BLOCK(cs_socks5) |
| 197 | { | 229 | { |
| 198 | g_fSocks5Proxy = false; | 230 | g_fSocks5Proxy = false; |
| 231 | g_fTorProxy = false; | ||
| 199 | g_socks5Host.clear(); | 232 | g_socks5Host.clear(); |
| 200 | g_socks5Port = 0; | 233 | g_socks5Port = 0; |
| 201 | } | 234 | } |
| @@ -208,6 +241,13 @@ bool BtfSocks5ProxyEnabled() | |||
| 208 | return false; | 241 | return false; |
| 209 | } | 242 | } |
| 210 | 243 | ||
| 244 | bool BtfTorProxyEnabled() | ||
| 245 | { | ||
| 246 | CRITICAL_BLOCK(cs_socks5) | ||
| 247 | return g_fSocks5Proxy && g_fTorProxy; | ||
| 248 | return false; | ||
| 249 | } | ||
| 250 | |||
| 211 | std::string BtfSocks5ProxyName() | 251 | std::string BtfSocks5ProxyName() |
| 212 | { | 252 | { |
| 213 | CRITICAL_BLOCK(cs_socks5) | 253 | CRITICAL_BLOCK(cs_socks5) |
| @@ -315,7 +355,15 @@ SOCKET BtfConnectSocket(const std::string& destHost, unsigned short destPort, | |||
| 315 | } | 355 | } |
| 316 | 356 | ||
| 317 | if (!fProxy) | 357 | if (!fProxy) |
| 358 | { | ||
| 359 | if (BtfIsTorOnionHost(destHost)) | ||
| 360 | { | ||
| 361 | error("Refusing direct .onion connection to %s:%u; use /tor or /socks\n", | ||
| 362 | destHost.c_str(), (unsigned)destPort); | ||
| 363 | return INVALID_SOCKET; | ||
| 364 | } | ||
| 318 | return ConnectDirect(destHost, destPort, nSockSite, nTimeoutSecs); | 365 | return ConnectDirect(destHost, destPort, nSockSite, nTimeoutSecs); |
| 366 | } | ||
| 319 | 367 | ||
| 320 | SOCKET s = ConnectDirect(proxyHost, proxyPort, nSockSite, nTimeoutSecs); | 368 | SOCKET s = ConnectDirect(proxyHost, proxyPort, nSockSite, nTimeoutSecs); |
| 321 | if (s == INVALID_SOCKET) | 369 | if (s == INVALID_SOCKET) |
diff --git a/src/proxy.h b/src/proxy.h index b5e69c8..6429cf2 100644 --- a/src/proxy.h +++ b/src/proxy.h | |||
| @@ -21,8 +21,11 @@ | |||
| 21 | bool BtfParseSocks5Proxy(const std::string& spec, std::string& hostOut, | 21 | bool BtfParseSocks5Proxy(const std::string& spec, std::string& hostOut, |
| 22 | unsigned short& portOut, std::string& errOut); | 22 | unsigned short& portOut, std::string& errOut); |
| 23 | bool BtfSetSocks5Proxy(const std::string& spec, std::string& errOut); | 23 | bool BtfSetSocks5Proxy(const std::string& spec, std::string& errOut); |
| 24 | bool BtfEnableTorProxy(const std::string& spec, std::string& errOut); | ||
| 24 | void BtfClearSocks5Proxy(); | 25 | void BtfClearSocks5Proxy(); |
| 25 | bool BtfSocks5ProxyEnabled(); | 26 | bool BtfSocks5ProxyEnabled(); |
| 27 | bool BtfTorProxyEnabled(); | ||
| 28 | bool BtfIsTorOnionHost(const std::string& host); | ||
| 26 | std::string BtfSocks5ProxyName(); | 29 | std::string BtfSocks5ProxyName(); |
| 27 | 30 | ||
| 28 | SOCKET BtfConnectSocket(const std::string& destHost, unsigned short destPort, | 31 | SOCKET BtfConnectSocket(const std::string& destHost, unsigned short destPort, |
diff --git a/src/readme.txt b/src/readme.txt index 1fa77df..794fe20 100644 --- a/src/readme.txt +++ b/src/readme.txt | |||
| @@ -46,6 +46,9 @@ Command-line options (in addition to the original ones) | |||
| 46 | route outbound Nostr and .btf rendezvous dials through a | 46 | route outbound Nostr and .btf rendezvous dials through a |
| 47 | SOCKS5 proxy, e.g. /socks=127.0.0.1:9050 for local Tor | 47 | SOCKS5 proxy, e.g. /socks=127.0.0.1:9050 for local Tor |
| 48 | or /socks=[::1]:9050 for an IPv6 loopback proxy | 48 | or /socks=[::1]:9050 for an IPv6 loopback proxy |
| 49 | /tor[=HOST:PORT] | ||
| 50 | Tor mode for outbound Nostr and .btf rendezvous dials; | ||
| 51 | defaults to the local Tor SOCKS5 proxy at 127.0.0.1:9050 | ||
| 49 | /solomine mine without requiring a connected peer (local testing) | 52 | /solomine mine without requiring a connected peer (local testing) |
| 50 | /operator operator mode (run pool server) | 53 | /operator operator mode (run pool server) |
| 51 | /participant=ADDR | 54 | /participant=ADDR |
diff --git a/src/selftest.cpp b/src/selftest.cpp index aa4e528..cca5335 100644 --- a/src/selftest.cpp +++ b/src/selftest.cpp | |||
| @@ -1474,6 +1474,11 @@ static int RunSocks5ProxySelfTest() | |||
| 1474 | "rejects overflowing port") ? 0 : 1; | 1474 | "rejects overflowing port") ? 0 : 1; |
| 1475 | nFail += Check(!BtfParseSocks5Proxy("user:pass@localhost:9050", host, port, err), | 1475 | nFail += Check(!BtfParseSocks5Proxy("user:pass@localhost:9050", host, port, err), |
| 1476 | "rejects unsupported authenticated proxy syntax") ? 0 : 1; | 1476 | "rejects unsupported authenticated proxy syntax") ? 0 : 1; |
| 1477 | nFail += Check(BtfIsTorOnionHost("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcd.onion") && | ||
| 1478 | BtfIsTorOnionHost("Relay.Example.ONION.") && | ||
| 1479 | !BtfIsTorOnionHost("relay.example") && | ||
| 1480 | !BtfIsTorOnionHost(".onion"), | ||
| 1481 | "detects .onion hosts without accepting lookalikes") ? 0 : 1; | ||
| 1477 | 1482 | ||
| 1478 | BtfClearSocks5Proxy(); | 1483 | BtfClearSocks5Proxy(); |
| 1479 | nFail += Check(!BtfSocks5ProxyEnabled(), "starts disabled after clear") ? 0 : 1; | 1484 | nFail += Check(!BtfSocks5ProxyEnabled(), "starts disabled after clear") ? 0 : 1; |
| @@ -1490,6 +1495,26 @@ static int RunSocks5ProxySelfTest() | |||
| 1490 | "enables bracketed IPv6 proxy endpoint") ? 0 : 1; | 1495 | "enables bracketed IPv6 proxy endpoint") ? 0 : 1; |
| 1491 | BtfClearSocks5Proxy(); | 1496 | BtfClearSocks5Proxy(); |
| 1492 | 1497 | ||
| 1498 | nFail += Check(BtfEnableTorProxy("", err) && | ||
| 1499 | BtfSocks5ProxyEnabled() && | ||
| 1500 | BtfTorProxyEnabled() && | ||
| 1501 | BtfSocks5ProxyName() == "127.0.0.1:9050", | ||
| 1502 | "enables Tor mode on the default local SOCKS5 endpoint") ? 0 : 1; | ||
| 1503 | BtfClearSocks5Proxy(); | ||
| 1504 | |||
| 1505 | nFail += Check(BtfEnableTorProxy("[::1]:9050", err) && | ||
| 1506 | BtfSocks5ProxyEnabled() && | ||
| 1507 | BtfTorProxyEnabled() && | ||
| 1508 | BtfSocks5ProxyName() == "[::1]:9050", | ||
| 1509 | "enables Tor mode on a custom IPv6 endpoint") ? 0 : 1; | ||
| 1510 | BtfClearSocks5Proxy(); | ||
| 1511 | |||
| 1512 | nFail += Check(BtfSetSocks5Proxy("127.0.0.1:9051", err) && | ||
| 1513 | BtfSocks5ProxyEnabled() && | ||
| 1514 | !BtfTorProxyEnabled(), | ||
| 1515 | "plain SOCKS5 mode is distinct from Tor mode") ? 0 : 1; | ||
| 1516 | BtfClearSocks5Proxy(); | ||
| 1517 | |||
| 1493 | std::string handshakeErr; | 1518 | std::string handshakeErr; |
| 1494 | bool fHandshakeOk = RunSocks5HandshakeProbe(handshakeErr); | 1519 | bool fHandshakeOk = RunSocks5HandshakeProbe(handshakeErr); |
| 1495 | nFail += Check(fHandshakeOk, handshakeErr.empty() ? | 1520 | nFail += Check(fHandshakeOk, handshakeErr.empty() ? |