diff options
Diffstat (limited to 'src/selftest.cpp')
| -rw-r--r-- | src/selftest.cpp | 212 |
1 files changed, 212 insertions, 0 deletions
diff --git a/src/selftest.cpp b/src/selftest.cpp index 5b36b9d..aa4e528 100644 --- a/src/selftest.cpp +++ b/src/selftest.cpp | |||
| @@ -9,6 +9,7 @@ | |||
| 9 | #include "bip32.h" | 9 | #include "bip32.h" |
| 10 | #include "proxy.h" | 10 | #include "proxy.h" |
| 11 | #include "selftest.h" | 11 | #include "selftest.h" |
| 12 | #include "sockcount.h" | ||
| 12 | #include "walletcmd.h" | 13 | #include "walletcmd.h" |
| 13 | 14 | ||
| 14 | extern int RunPoolStratumSelfTest(); | 15 | extern int RunPoolStratumSelfTest(); |
| @@ -1254,6 +1255,197 @@ static int RunParseMoneySelfTest() | |||
| 1254 | return nFail == 0 ? 0 : 1; | 1255 | return nFail == 0 ? 0 : 1; |
| 1255 | } | 1256 | } |
| 1256 | 1257 | ||
| 1258 | static 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 | |||
| 1272 | static 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 | |||
| 1286 | static 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 | ||
| 1302 | struct 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 | |||
| 1318 | static 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 | |||
| 1257 | static int RunSocks5ProxySelfTest() | 1449 | static int RunSocks5ProxySelfTest() |
| 1258 | { | 1450 | { |
| 1259 | printf("socks5-proxy self-test\n"); | 1451 | printf("socks5-proxy self-test\n"); |
| @@ -1267,12 +1459,19 @@ static int RunSocks5ProxySelfTest() | |||
| 1267 | nFail += Check(BtfParseSocks5Proxy("localhost:9050", host, port, err) && | 1459 | nFail += Check(BtfParseSocks5Proxy("localhost:9050", host, port, err) && |
| 1268 | host == "localhost" && port == 9050, | 1460 | host == "localhost" && port == 9050, |
| 1269 | "parses hostname proxy endpoint") ? 0 : 1; | 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; | ||
| 1270 | nFail += Check(!BtfParseSocks5Proxy("localhost", host, port, err), | 1467 | nFail += Check(!BtfParseSocks5Proxy("localhost", host, port, err), |
| 1271 | "rejects missing port") ? 0 : 1; | 1468 | "rejects missing port") ? 0 : 1; |
| 1272 | nFail += Check(!BtfParseSocks5Proxy("localhost:0", host, port, err), | 1469 | nFail += Check(!BtfParseSocks5Proxy("localhost:0", host, port, err), |
| 1273 | "rejects zero port") ? 0 : 1; | 1470 | "rejects zero port") ? 0 : 1; |
| 1274 | nFail += Check(!BtfParseSocks5Proxy("localhost:70000", host, port, err), | 1471 | nFail += Check(!BtfParseSocks5Proxy("localhost:70000", host, port, err), |
| 1275 | "rejects out-of-range port") ? 0 : 1; | 1472 | "rejects out-of-range port") ? 0 : 1; |
| 1473 | nFail += Check(!BtfParseSocks5Proxy("localhost:999999999999999999999999999999", host, port, err), | ||
| 1474 | "rejects overflowing port") ? 0 : 1; | ||
| 1276 | nFail += Check(!BtfParseSocks5Proxy("user:pass@localhost:9050", host, port, err), | 1475 | nFail += Check(!BtfParseSocks5Proxy("user:pass@localhost:9050", host, port, err), |
| 1277 | "rejects unsupported authenticated proxy syntax") ? 0 : 1; | 1476 | "rejects unsupported authenticated proxy syntax") ? 0 : 1; |
| 1278 | 1477 | ||
| @@ -1285,6 +1484,19 @@ static int RunSocks5ProxySelfTest() | |||
| 1285 | BtfClearSocks5Proxy(); | 1484 | BtfClearSocks5Proxy(); |
| 1286 | nFail += Check(!BtfSocks5ProxyEnabled(), "clear disables proxy") ? 0 : 1; | 1485 | nFail += Check(!BtfSocks5ProxyEnabled(), "clear disables proxy") ? 0 : 1; |
| 1287 | 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 | |||
| 1288 | printf("%s (%d failure%s)\n", nFail == 0 ? "ALL TESTS PASSED" : "TESTS FAILED", | 1500 | printf("%s (%d failure%s)\n", nFail == 0 ? "ALL TESTS PASSED" : "TESTS FAILED", |
| 1289 | nFail, nFail == 1 ? "" : "s"); | 1501 | nFail, nFail == 1 ? "" : "s"); |
| 1290 | fflush(stdout); | 1502 | fflush(stdout); |