upleb.uk

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

summaryrefslogtreecommitdiff
path: root/src/selftest.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/selftest.cpp')
-rw-r--r--src/selftest.cpp254
1 files changed, 253 insertions, 1 deletions
diff --git a/src/selftest.cpp b/src/selftest.cpp
index dcb9192..aa4e528 100644
--- a/src/selftest.cpp
+++ b/src/selftest.cpp
@@ -7,7 +7,9 @@
7 7
8#include "headers_core.h" 8#include "headers_core.h"
9#include "bip32.h" 9#include "bip32.h"
10#include "proxy.h"
10#include "selftest.h" 11#include "selftest.h"
12#include "sockcount.h"
11#include "walletcmd.h" 13#include "walletcmd.h"
12 14
13extern int RunPoolStratumSelfTest(); 15extern int RunPoolStratumSelfTest();
@@ -1253,6 +1255,254 @@ static int RunParseMoneySelfTest()
1253 return nFail == 0 ? 0 : 1; 1255 return nFail == 0 ? 0 : 1;
1254} 1256}
1255 1257
1258static bool SelfTestReadN(SOCKET s, void* buf, int n)
1259{
1260 char* p = (char*)buf;
1261 int off = 0;
1262 while (off < n)
1263 {
1264 int r = recv(s, p + off, n - off, 0);
1265 if (r <= 0)
1266 return false;
1267 off += r;
1268 }
1269 return true;
1270}
1271
1272static bool SelfTestWriteN(SOCKET s, const void* buf, int n)
1273{
1274 const char* p = (const char*)buf;
1275 int off = 0;
1276 while (off < n)
1277 {
1278 int r = send(s, p + off, n - off, 0);
1279 if (r <= 0)
1280 return false;
1281 off += r;
1282 }
1283 return true;
1284}
1285
1286static void SelfTestSetSocketTimeout(SOCKET s, int nTimeoutSecs)
1287{
1288#ifdef _WIN32
1289 DWORD tv = (DWORD)nTimeoutSecs * 1000;
1290 setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(tv));
1291 setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, (const char*)&tv, sizeof(tv));
1292#else
1293 struct timeval tv;
1294 tv.tv_sec = nTimeoutSecs;
1295 tv.tv_usec = 0;
1296 setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
1297 setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
1298#endif
1299}
1300
1301#ifdef _WIN32
1302struct SelfTestWinsock
1303{
1304 bool fStarted;
1305 SelfTestWinsock() : fStarted(false)
1306 {
1307 WSADATA wsadata;
1308 fStarted = (WSAStartup(MAKEWORD(2,2), &wsadata) == 0);
1309 }
1310 ~SelfTestWinsock()
1311 {
1312 if (fStarted)
1313 WSACleanup();
1314 }
1315};
1316#endif
1317
1318static bool RunSocks5HandshakeProbe(std::string& errOut)
1319{
1320 errOut.clear();
1321#ifdef _WIN32
1322 SelfTestWinsock winsock;
1323 if (!winsock.fStarted)
1324 {
1325 errOut = "could not start Winsock";
1326 return false;
1327 }
1328#endif
1329
1330 SOCKET listener = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
1331 if (listener == INVALID_SOCKET)
1332 {
1333 errOut = "could not create listener";
1334 return false;
1335 }
1336 SelfTestSetSocketTimeout(listener, 5);
1337
1338 struct sockaddr_in addr;
1339 memset(&addr, 0, sizeof(addr));
1340 addr.sin_family = AF_INET;
1341 addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
1342 addr.sin_port = 0;
1343 if (bind(listener, (struct sockaddr*)&addr, sizeof(addr)) != 0 ||
1344 listen(listener, 1) != 0)
1345 {
1346 closesocket(listener);
1347 errOut = "could not bind/listen on loopback";
1348 return false;
1349 }
1350
1351#ifdef _WIN32
1352 int addrLen = sizeof(addr);
1353#else
1354 socklen_t addrLen = sizeof(addr);
1355#endif
1356 if (getsockname(listener, (struct sockaddr*)&addr, &addrLen) != 0)
1357 {
1358 closesocket(listener);
1359 errOut = "could not read listener port";
1360 return false;
1361 }
1362 unsigned short proxyPort = ntohs(addr.sin_port);
1363
1364 bool fServerOk = false;
1365 std::string serverErr;
1366 std::string requestedHost;
1367 unsigned short requestedPort = 0;
1368 std::thread server([&]() {
1369 SOCKET s = accept(listener, NULL, NULL);
1370 if (s == INVALID_SOCKET)
1371 {
1372 serverErr = "accept failed";
1373 return;
1374 }
1375 SelfTestSetSocketTimeout(s, 5);
1376
1377 unsigned char hello[3] = {0,0,0};
1378 if (!SelfTestReadN(s, hello, sizeof(hello)) ||
1379 hello[0] != 0x05 || hello[1] != 0x01 || hello[2] != 0x00)
1380 {
1381 serverErr = "bad SOCKS5 greeting";
1382 closesocket(s);
1383 return;
1384 }
1385 unsigned char choice[2] = {0x05, 0x00};
1386 if (!SelfTestWriteN(s, choice, sizeof(choice)))
1387 {
1388 serverErr = "could not write method choice";
1389 closesocket(s);
1390 return;
1391 }
1392
1393 unsigned char hdr[5] = {0,0,0,0,0};
1394 if (!SelfTestReadN(s, hdr, sizeof(hdr)) ||
1395 hdr[0] != 0x05 || hdr[1] != 0x01 || hdr[2] != 0x00 || hdr[3] != 0x03)
1396 {
1397 serverErr = "CONNECT did not use domain-name address type";
1398 closesocket(s);
1399 return;
1400 }
1401
1402 unsigned char len = hdr[4];
1403 std::vector<unsigned char> rest((size_t)len + 2);
1404 if (!SelfTestReadN(s, &rest[0], (int)rest.size()))
1405 {
1406 serverErr = "could not read CONNECT target";
1407 closesocket(s);
1408 return;
1409 }
1410 requestedHost.assign((const char*)&rest[0], (size_t)len);
1411 requestedPort = ((unsigned short)rest[len] << 8) | rest[len + 1];
1412
1413 unsigned char reply[10] = {0x05, 0x00, 0x00, 0x01, 0,0,0,0, 0,0};
1414 if (!SelfTestWriteN(s, reply, sizeof(reply)))
1415 {
1416 serverErr = "could not write CONNECT reply";
1417 closesocket(s);
1418 return;
1419 }
1420 fServerOk = true;
1421 closesocket(s);
1422 });
1423
1424 std::string err;
1425 BtfClearSocks5Proxy();
1426 bool fClientOk = BtfSetSocks5Proxy(strprintf("127.0.0.1:%u", (unsigned)proxyPort), err);
1427 SOCKET client = INVALID_SOCKET;
1428 if (fClientOk)
1429 client = BtfConnectSocket("relay.example", 443, SOCK_NOSTR, 5);
1430 if (client != INVALID_SOCKET)
1431 BtfCloseSocket(client);
1432 BtfClearSocks5Proxy();
1433 closesocket(listener);
1434 server.join();
1435
1436 if (!fClientOk)
1437 errOut = strprintf("could not enable proxy: %s", err.c_str());
1438 else if (client == INVALID_SOCKET)
1439 errOut = "client SOCKS5 connect failed";
1440 else if (!fServerOk)
1441 errOut = serverErr.empty() ? "mock server failed" : serverErr;
1442 else if (requestedHost != "relay.example" || requestedPort != 443)
1443 errOut = strprintf("unexpected CONNECT target %s:%u", requestedHost.c_str(), (unsigned)requestedPort);
1444 else
1445 return true;
1446 return false;
1447}
1448
1449static int RunSocks5ProxySelfTest()
1450{
1451 printf("socks5-proxy self-test\n");
1452 int nFail = 0;
1453 std::string host, err;
1454 unsigned short port = 0;
1455
1456 nFail += Check(BtfParseSocks5Proxy("127.0.0.1:9050", host, port, err) &&
1457 host == "127.0.0.1" && port == 9050,
1458 "parses local Tor proxy endpoint") ? 0 : 1;
1459 nFail += Check(BtfParseSocks5Proxy("localhost:9050", host, port, err) &&
1460 host == "localhost" && port == 9050,
1461 "parses hostname proxy endpoint") ? 0 : 1;
1462 nFail += Check(BtfParseSocks5Proxy("[::1]:9050", host, port, err) &&
1463 host == "::1" && port == 9050,
1464 "parses bracketed IPv6 proxy endpoint") ? 0 : 1;
1465 nFail += Check(!BtfParseSocks5Proxy("::1:9050", host, port, err),
1466 "rejects unbracketed IPv6 proxy endpoint") ? 0 : 1;
1467 nFail += Check(!BtfParseSocks5Proxy("localhost", host, port, err),
1468 "rejects missing port") ? 0 : 1;
1469 nFail += Check(!BtfParseSocks5Proxy("localhost:0", host, port, err),
1470 "rejects zero port") ? 0 : 1;
1471 nFail += Check(!BtfParseSocks5Proxy("localhost:70000", host, port, err),
1472 "rejects out-of-range port") ? 0 : 1;
1473 nFail += Check(!BtfParseSocks5Proxy("localhost:999999999999999999999999999999", host, port, err),
1474 "rejects overflowing port") ? 0 : 1;
1475 nFail += Check(!BtfParseSocks5Proxy("user:pass@localhost:9050", host, port, err),
1476 "rejects unsupported authenticated proxy syntax") ? 0 : 1;
1477
1478 BtfClearSocks5Proxy();
1479 nFail += Check(!BtfSocks5ProxyEnabled(), "starts disabled after clear") ? 0 : 1;
1480 nFail += Check(BtfSetSocks5Proxy("127.0.0.1:9050", err) &&
1481 BtfSocks5ProxyEnabled() &&
1482 BtfSocks5ProxyName() == "127.0.0.1:9050",
1483 "enables parsed SOCKS5 proxy") ? 0 : 1;
1484 BtfClearSocks5Proxy();
1485 nFail += Check(!BtfSocks5ProxyEnabled(), "clear disables proxy") ? 0 : 1;
1486
1487 nFail += Check(BtfSetSocks5Proxy("[::1]:9050", err) &&
1488 BtfSocks5ProxyEnabled() &&
1489 BtfSocks5ProxyName() == "[::1]:9050",
1490 "enables bracketed IPv6 proxy endpoint") ? 0 : 1;
1491 BtfClearSocks5Proxy();
1492
1493 std::string handshakeErr;
1494 bool fHandshakeOk = RunSocks5HandshakeProbe(handshakeErr);
1495 nFail += Check(fHandshakeOk, handshakeErr.empty() ?
1496 "completes SOCKS5 domain-name CONNECT handshake" :
1497 strprintf("completes SOCKS5 domain-name CONNECT handshake (%s)",
1498 handshakeErr.c_str()).c_str()) ? 0 : 1;
1499
1500 printf("%s (%d failure%s)\n", nFail == 0 ? "ALL TESTS PASSED" : "TESTS FAILED",
1501 nFail, nFail == 1 ? "" : "s");
1502 fflush(stdout);
1503 return nFail == 0 ? 0 : 1;
1504}
1505
1256int RunSelfTest(const std::string& name) 1506int RunSelfTest(const std::string& name)
1257{ 1507{
1258 AttachTerminal(); 1508 AttachTerminal();
@@ -1275,8 +1525,10 @@ int RunSelfTest(const std::string& name)
1275 return RunPoolStratumSelfTest(); 1525 return RunPoolStratumSelfTest();
1276 if (name == "parse-money") 1526 if (name == "parse-money")
1277 return RunParseMoneySelfTest(); 1527 return RunParseMoneySelfTest();
1528 if (name == "socks5-proxy")
1529 return RunSocks5ProxySelfTest();
1278 1530
1279 printf("Unknown self-test '%s'\n", name.c_str()); 1531 printf("Unknown self-test '%s'\n", name.c_str());
1280 printf("Known self-tests: wallet-keypool, wallet-hd, wallet-format, wallet-crypto, wallet-encrypt, net-message, consensus-limits, pool-stratum\n"); 1532 printf("Known self-tests: wallet-keypool, wallet-hd, wallet-format, wallet-crypto, wallet-encrypt, net-message, consensus-limits, pool-stratum, parse-money, socks5-proxy\n");
1281 return 1; 1533 return 1;
1282} 1534}