blockchecks 1.3.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (220) hide show
  1. blockchecks/__init__.py +3 -0
  2. blockchecks/bs.py +17 -0
  3. blockchecks/checkers/__init__.py +5 -0
  4. blockchecks/checkers/composite_runner.py +189 -0
  5. blockchecks/checkers/curl_probe.py +997 -0
  6. blockchecks/checkers/dns_secure.py +497 -0
  7. blockchecks/checkers/http3.py +92 -0
  8. blockchecks/checkers/ip_block.py +173 -0
  9. blockchecks/checkers/ip_pin.py +106 -0
  10. blockchecks/checkers/l3_probe.py +167 -0
  11. blockchecks/checkers/port_block.py +76 -0
  12. blockchecks/checkers/quic_raw.py +178 -0
  13. blockchecks/checkers/tcp_tls.py +211 -0
  14. blockchecks/checkers/udp_voice.py +219 -0
  15. blockchecks/checkers/voice_discovery.py +342 -0
  16. blockchecks/checkers/voice_dns.py +562 -0
  17. blockchecks/checkers/youtube_url.py +195 -0
  18. blockchecks/cli/__init__.py +1 -0
  19. blockchecks/cli/cliapp.py +616 -0
  20. blockchecks/cli/commands/__init__.py +1 -0
  21. blockchecks/cli/commands/bench_settle.py +161 -0
  22. blockchecks/cli/commands/mcp.py +30 -0
  23. blockchecks/cli/commands/pair.py +208 -0
  24. blockchecks/cli/commands/pair_phases.py +802 -0
  25. blockchecks/cli/commands/serve.py +63 -0
  26. blockchecks/cli/commands/stop.py +14 -0
  27. blockchecks/cli/commands/tcp.py +117 -0
  28. blockchecks/cli/commands/udp.py +123 -0
  29. blockchecks/cli/parser.py +840 -0
  30. blockchecks/cli/presets.py +67 -0
  31. blockchecks/cli/user_config.py +104 -0
  32. blockchecks/data_block/__init__.py +31 -0
  33. blockchecks/data_block/provider.py +191 -0
  34. blockchecks/data_block/store.py +362 -0
  35. blockchecks/engine/__init__.py +13 -0
  36. blockchecks/engine/_curl_probe_worker.py +30 -0
  37. blockchecks/engine/_probe_worker.py +34 -0
  38. blockchecks/engine/adaptive_queue.py +470 -0
  39. blockchecks/engine/adaptive_runner.py +353 -0
  40. blockchecks/engine/async_runner.py +1007 -0
  41. blockchecks/engine/base_worker.py +84 -0
  42. blockchecks/engine/blob_aliases.py +169 -0
  43. blockchecks/engine/byedpi_matrix_generator.py +144 -0
  44. blockchecks/engine/byedpi_translator.py +323 -0
  45. blockchecks/engine/conf_builder.py +491 -0
  46. blockchecks/engine/config.py +434 -0
  47. blockchecks/engine/db_logger.py +22 -0
  48. blockchecks/engine/domain_loader.py +175 -0
  49. blockchecks/engine/fail_phase.py +128 -0
  50. blockchecks/engine/family_needs.py +222 -0
  51. blockchecks/engine/generators/__init__.py +43 -0
  52. blockchecks/engine/generators/base.py +41 -0
  53. blockchecks/engine/generators/custom.py +155 -0
  54. blockchecks/engine/generators/families/__init__.py +11 -0
  55. blockchecks/engine/generators/families/_helpers.py +112 -0
  56. blockchecks/engine/generators/families/fake.py +213 -0
  57. blockchecks/engine/generators/families/split.py +253 -0
  58. blockchecks/engine/generators/families/tamper.py +244 -0
  59. blockchecks/engine/generators/flowseal.py +335 -0
  60. blockchecks/engine/generators/standard.py +882 -0
  61. blockchecks/engine/in_ns_workers.py +784 -0
  62. blockchecks/engine/matrix_generator.py +287 -0
  63. blockchecks/engine/nfqws_config.py +94 -0
  64. blockchecks/engine/paths.py +322 -0
  65. blockchecks/engine/preflight.py +489 -0
  66. blockchecks/engine/preset_paths.py +109 -0
  67. blockchecks/engine/results.py +82 -0
  68. blockchecks/engine/run_deadline.py +144 -0
  69. blockchecks/engine/run_finalize.py +154 -0
  70. blockchecks/engine/secure_io.py +24 -0
  71. blockchecks/engine/settings.py +107 -0
  72. blockchecks/engine/settle_profile.py +178 -0
  73. blockchecks/engine/static_validator.py +286 -0
  74. blockchecks/engine/store/__init__.py +116 -0
  75. blockchecks/engine/store/models.py +16 -0
  76. blockchecks/engine/store/schema.py +201 -0
  77. blockchecks/engine/store/sqlite_store.py +743 -0
  78. blockchecks/engine/strategy_loader.py +64 -0
  79. blockchecks/engine/system_deps.py +489 -0
  80. blockchecks/engine/tcp_fanout.py +100 -0
  81. blockchecks/engine/test_runner.py +363 -0
  82. blockchecks/engine/triage.py +130 -0
  83. blockchecks/main.py +234 -0
  84. blockchecks/main_phases.py +1102 -0
  85. blockchecks/mcp/__init__.py +29 -0
  86. blockchecks/mcp/server.py +730 -0
  87. blockchecks/nfconf.py +366 -0
  88. blockchecks/provider_import.py +230 -0
  89. blockchecks/service/__init__.py +105 -0
  90. blockchecks/service/batch_bridge_probe.py +186 -0
  91. blockchecks/service/batch_models.py +67 -0
  92. blockchecks/service/batch_scheduler.py +110 -0
  93. blockchecks/service/batch_service.py +385 -0
  94. blockchecks/service/firewall.py +120 -0
  95. blockchecks/service/lua_bridge_ipc.py +133 -0
  96. blockchecks/service/lua_conf.py +112 -0
  97. blockchecks/service/lua_netns.py +82 -0
  98. blockchecks/service/lua_session.py +141 -0
  99. blockchecks/service/metrics.py +238 -0
  100. blockchecks/service/netns_pool.py +228 -0
  101. blockchecks/service/nfqws2.py +316 -0
  102. blockchecks/service/nfqws2_settle.py +71 -0
  103. blockchecks/service/probe.py +107 -0
  104. blockchecks/service/probe_service.py +221 -0
  105. blockchecks/service/run_control.py +178 -0
  106. blockchecks/service/server.py +430 -0
  107. blockchecks/shortlist_export.py +188 -0
  108. blockchecks/shortlist_import.py +206 -0
  109. blockchecks-1.3.2.data/data/blockchecks/blobs/discord_ipdisc.bin +0 -0
  110. blockchecks-1.3.2.data/data/blockchecks/blobs/discord_udp.bin +0 -0
  111. blockchecks-1.3.2.data/data/blockchecks/blobs/game_udp.bin +0 -0
  112. blockchecks-1.3.2.data/data/blockchecks/blobs/http_iana.bin +9 -0
  113. blockchecks-1.3.2.data/data/blockchecks/blobs/quic_4pda.bin +0 -0
  114. blockchecks-1.3.2.data/data/blockchecks/blobs/quic_5ka.bin +0 -0
  115. blockchecks-1.3.2.data/data/blockchecks/blobs/quic_alfabank.bin +0 -0
  116. blockchecks-1.3.2.data/data/blockchecks/blobs/quic_cloudflare.bin +0 -0
  117. blockchecks-1.3.2.data/data/blockchecks/blobs/quic_funpay.bin +0 -0
  118. blockchecks-1.3.2.data/data/blockchecks/blobs/quic_gv_kyber_1.bin +0 -0
  119. blockchecks-1.3.2.data/data/blockchecks/blobs/quic_gv_kyber_2.bin +0 -0
  120. blockchecks-1.3.2.data/data/blockchecks/blobs/quic_gv_rr2.bin +0 -0
  121. blockchecks-1.3.2.data/data/blockchecks/blobs/quic_initial.bin +0 -0
  122. blockchecks-1.3.2.data/data/blockchecks/blobs/quic_initial_dbankcloud_ru.bin +0 -0
  123. blockchecks-1.3.2.data/data/blockchecks/blobs/quic_initial_vk_com.bin +0 -0
  124. blockchecks-1.3.2.data/data/blockchecks/blobs/quic_initial_www_google_com.bin +0 -0
  125. blockchecks-1.3.2.data/data/blockchecks/blobs/quic_rutube.bin +0 -0
  126. blockchecks-1.3.2.data/data/blockchecks/blobs/quic_steam.bin +0 -0
  127. blockchecks-1.3.2.data/data/blockchecks/blobs/quic_tencent.bin +0 -0
  128. blockchecks-1.3.2.data/data/blockchecks/blobs/quic_vk.bin +0 -0
  129. blockchecks-1.3.2.data/data/blockchecks/blobs/stun.bin +0 -0
  130. blockchecks-1.3.2.data/data/blockchecks/blobs/stun2.bin +0 -0
  131. blockchecks-1.3.2.data/data/blockchecks/blobs/tls_5ka.bin +0 -0
  132. blockchecks-1.3.2.data/data/blockchecks/blobs/tls_clienthello_4pda_to.bin +0 -0
  133. blockchecks-1.3.2.data/data/blockchecks/blobs/tls_clienthello_max_ru.bin +0 -0
  134. blockchecks-1.3.2.data/data/blockchecks/blobs/tls_clienthello_vk_com.bin +0 -0
  135. blockchecks-1.3.2.data/data/blockchecks/blobs/tls_clienthello_www_google_com.bin +0 -0
  136. blockchecks-1.3.2.data/data/blockchecks/blobs/tls_funpay.bin +0 -0
  137. blockchecks-1.3.2.data/data/blockchecks/blobs/tls_rzd.bin +0 -0
  138. blockchecks-1.3.2.data/data/blockchecks/blobs/tls_vk.bin +0 -0
  139. blockchecks-1.3.2.data/data/blockchecks/blobs/wireguard_init.bin +0 -0
  140. blockchecks-1.3.2.data/data/blockchecks/configs/alt10__fake_4pda_ts.conf +12 -0
  141. blockchecks-1.3.2.data/data/blockchecks/configs/alt11__fake_multisplit_ts_r8.conf +12 -0
  142. blockchecks-1.3.2.data/data/blockchecks/configs/alt12__fake_hostfakesplit_mixed.conf +13 -0
  143. blockchecks-1.3.2.data/data/blockchecks/configs/alt2__multisplit_pos2.conf +10 -0
  144. blockchecks-1.3.2.data/data/blockchecks/configs/alt3__fake_hostfakesplit_ya.conf +11 -0
  145. blockchecks-1.3.2.data/data/blockchecks/configs/alt4__fake_multisplit_badseq.conf +11 -0
  146. blockchecks-1.3.2.data/data/blockchecks/configs/alt5__syndata_multidisorder.conf +9 -0
  147. blockchecks-1.3.2.data/data/blockchecks/configs/alt6__multisplit_pos1_seqovl681.conf +10 -0
  148. blockchecks-1.3.2.data/data/blockchecks/configs/alt7__multisplit_sniext.conf +10 -0
  149. blockchecks-1.3.2.data/data/blockchecks/configs/alt8__fake_nomod_badseq.conf +12 -0
  150. blockchecks-1.3.2.data/data/blockchecks/configs/alt9__hostfakesplit_ozon.conf +10 -0
  151. blockchecks-1.3.2.data/data/blockchecks/configs/alt__fake_fakedsplit_ts.conf +11 -0
  152. blockchecks-1.3.2.data/data/blockchecks/configs/composite_discord.conf +22 -0
  153. blockchecks-1.3.2.data/data/blockchecks/configs/fake_badsum_r6.conf +10 -0
  154. blockchecks-1.3.2.data/data/blockchecks/configs/fake_null_r6_ts.conf +9 -0
  155. blockchecks-1.3.2.data/data/blockchecks/configs/fake_tls_auto__fake_multidisorder.conf +10 -0
  156. blockchecks-1.3.2.data/data/blockchecks/configs/fake_tls_auto_alt2__multisplit_badseq_r8.conf +11 -0
  157. blockchecks-1.3.2.data/data/blockchecks/configs/fake_tls_auto_alt3__multisplit_ts_r8.conf +11 -0
  158. blockchecks-1.3.2.data/data/blockchecks/configs/fake_tls_auto_alt__fakedsplit_badseq.conf +11 -0
  159. blockchecks-1.3.2.data/data/blockchecks/configs/general__default_multisplit.conf +10 -0
  160. blockchecks-1.3.2.data/data/blockchecks/configs/simple_fake__fake_ts.conf +12 -0
  161. blockchecks-1.3.2.data/data/blockchecks/configs/simple_fake_alt2__fake_max_ru_ts.conf +12 -0
  162. blockchecks-1.3.2.data/data/blockchecks/configs/simple_fake_alt__fake_badseq.conf +12 -0
  163. blockchecks-1.3.2.data/data/blockchecks/configs/udp_voice__fake_r12.conf +10 -0
  164. blockchecks-1.3.2.data/data/blockchecks/configs/udp_voice__fake_r6.conf +10 -0
  165. blockchecks-1.3.2.data/data/blockchecks/configs/udp_voice__fake_r6_autottl.conf +10 -0
  166. blockchecks-1.3.2.data/data/blockchecks/configs/udp_voice__fake_r6_ttl5.conf +10 -0
  167. blockchecks-1.3.2.data/data/blockchecks/configs/udp_voice__wide_ports_r6.conf +10 -0
  168. blockchecks-1.3.2.data/data/blockchecks/lua/blockchecks/geneva.lua +65 -0
  169. blockchecks-1.3.2.data/data/blockchecks/lua/blockchecks/init.lua +3 -0
  170. blockchecks-1.3.2.data/data/blockchecks/lua/blockchecks/scan_bridge.lua +90 -0
  171. blockchecks-1.3.2.data/data/blockchecks/lua/blockchecks/write_ipc.lua +44 -0
  172. blockchecks-1.3.2.data/data/blockchecks/lua/custom/README.md +78 -0
  173. blockchecks-1.3.2.data/data/blockchecks/lua/custom/dupfake.lua +49 -0
  174. blockchecks-1.3.2.data/data/blockchecks/lua/custom/manifest.toml +47 -0
  175. blockchecks-1.3.2.data/data/blockchecks/presets/domains/amazon-aws.txt +12 -0
  176. blockchecks-1.3.2.data/data/blockchecks/presets/domains/benchmark.txt +7 -0
  177. blockchecks-1.3.2.data/data/blockchecks/presets/domains/cloudflare.txt +10 -0
  178. blockchecks-1.3.2.data/data/blockchecks/presets/domains/coverage-tcp.txt +23 -0
  179. blockchecks-1.3.2.data/data/blockchecks/presets/domains/coverage.txt +41 -0
  180. blockchecks-1.3.2.data/data/blockchecks/presets/domains/critical.txt +6 -0
  181. blockchecks-1.3.2.data/data/blockchecks/presets/domains/denylist.txt +43 -0
  182. blockchecks-1.3.2.data/data/blockchecks/presets/domains/diagnostic.txt +2 -0
  183. blockchecks-1.3.2.data/data/blockchecks/presets/domains/discord.txt +23 -0
  184. blockchecks-1.3.2.data/data/blockchecks/presets/domains/google-youtube.txt +24 -0
  185. blockchecks-1.3.2.data/data/blockchecks/presets/domains/pi2.txt +5 -0
  186. blockchecks-1.3.2.data/data/blockchecks/presets/strategies/bc2-parity-ackdrop.tls +5 -0
  187. blockchecks-1.3.2.data/data/blockchecks/presets/strategies/bc2-parity-foolings.tls +8 -0
  188. blockchecks-1.3.2.data/data/blockchecks/presets/strategies/bc2-parity-http.http +9 -0
  189. blockchecks-1.3.2.data/data/blockchecks/presets/strategies/bc2-parity-ipv6.tls +9 -0
  190. blockchecks-1.3.2.data/data/blockchecks/presets/strategies/bc2-parity-quic.quic +8 -0
  191. blockchecks-1.3.2.data/data/blockchecks/presets/strategies/bc2-parity-seqovl.tls +8 -0
  192. blockchecks-1.3.2.data/data/blockchecks/presets/strategies/bc2-parity-voice.udp +6 -0
  193. blockchecks-1.3.2.data/data/blockchecks/presets/strategies/blockcheckS-best.tls +12 -0
  194. blockchecks-1.3.2.data/data/blockchecks/presets/strategies/fiord-http.http +7 -0
  195. blockchecks-1.3.2.data/data/blockchecks/presets/strategies/fiord-quic.quic +8 -0
  196. blockchecks-1.3.2.data/data/blockchecks/presets/strategies/fiord-tls12.tls +15 -0
  197. blockchecks-1.3.2.data/data/blockchecks/presets/strategies/fiord-tls13.tls +8 -0
  198. blockchecks-1.3.2.data/data/blockchecks/presets/strategies/fiord-udp_voice.udp +8 -0
  199. blockchecks-1.3.2.data/data/blockchecks/presets/strategies/flowseal-fast.tls +18 -0
  200. blockchecks-1.3.2.data/data/blockchecks/presets/strategies/gp-custom-dupfake.tls +14 -0
  201. blockchecks-1.3.2.data/data/blockchecks/presets/strategies/gp-custom-http.txt +6 -0
  202. blockchecks-1.3.2.data/data/blockchecks/presets/strategies/gp-custom-quic.txt +6 -0
  203. blockchecks-1.3.2.data/data/blockchecks/presets/strategies/gp-custom-tls12.txt +10 -0
  204. blockchecks-1.3.2.data/data/blockchecks/presets/strategies/gp-custom-tls13.txt +7 -0
  205. blockchecks-1.3.2.data/data/blockchecks/presets/strategies/gp-verified.tls +12 -0
  206. blockchecks-1.3.2.data/data/blockchecks/presets/strategies/gp-voice.txt +9 -0
  207. blockchecks-1.3.2.data/data/blockchecks/presets/strategies/gv-e2e-smoke.tls +6 -0
  208. blockchecks-1.3.2.data/data/blockchecks/presets/strategies/gv5-quic-smoke.quic +7 -0
  209. blockchecks-1.3.2.data/data/blockchecks/presets/strategies/http-tls-dual.http +4 -0
  210. blockchecks-1.3.2.data/data/blockchecks/presets/strategies/http-tls-dual.tls +4 -0
  211. blockchecks-1.3.2.data/data/blockchecks/presets/strategies/shortlist-tls12.tls +4 -0
  212. blockchecks-1.3.2.data/data/blockchecks/presets/strategies/timeout-benchmark.tls +4 -0
  213. blockchecks-1.3.2.data/data/blockchecks/systemd/blockcheck-series.service +18 -0
  214. blockchecks-1.3.2.data/data/blockchecks/systemd/blockcheck-serve.service +18 -0
  215. blockchecks-1.3.2.dist-info/METADATA +118 -0
  216. blockchecks-1.3.2.dist-info/RECORD +220 -0
  217. blockchecks-1.3.2.dist-info/WHEEL +5 -0
  218. blockchecks-1.3.2.dist-info/entry_points.txt +5 -0
  219. blockchecks-1.3.2.dist-info/licenses/LICENSE +21 -0
  220. blockchecks-1.3.2.dist-info/top_level.txt +1 -0
@@ -0,0 +1,3 @@
1
+ """blockchecks — lightspeed DPI strategy tester."""
2
+
3
+ __version__ = "1.3.2"
blockchecks/bs.py ADDED
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env python3
2
+ """blockcheckS — lightspeed DPI strategy tester.
3
+
4
+ Thin entry point. Implementation lives in blockchecks.cli.
5
+ """
6
+
7
+ import sys
8
+
9
+ from blockchecks.cli.commands.pair import cmd_pair
10
+ from blockchecks.cli.commands.tcp import cmd_tcp
11
+ from blockchecks.cli.commands.udp import cmd_udp
12
+ from blockchecks.cli.parser import main
13
+
14
+ __all__ = ["main", "cmd_tcp", "cmd_udp", "cmd_pair"]
15
+
16
+ if __name__ == "__main__":
17
+ sys.exit(main())
@@ -0,0 +1,5 @@
1
+ """Connectivity checkers: TLS, UDP voice, discovery, composite."""
2
+
3
+ from blockchecks.checkers.tcp_tls import TlsResult, check_tls
4
+
5
+ __all__ = ["TlsResult", "check_tls"]
@@ -0,0 +1,189 @@
1
+ """Test a composite nfqws2 config against all its target domains.
2
+
3
+ Uses ONE netns + ONE nfqws2 for all domains — the config already
4
+ profiles traffic by hostlist, so one instance handles everything.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import asyncio
10
+ import os
11
+ import re
12
+ import subprocess as sp
13
+ import time
14
+
15
+ from colorama import Fore, Style
16
+ from colorama import init as colorama_init
17
+
18
+ from blockchecks.checkers.curl_probe import CurlProbeRequest, worker_wall_timeout
19
+ from blockchecks.engine.async_runner import (
20
+ AsyncTestRunner,
21
+ StrategyItem,
22
+ TcpTestResult,
23
+ )
24
+ from blockchecks.engine.config import NFQUEUE_TCP, NFQUEUE_UDP
25
+ from blockchecks.engine.config import PYTHON_BIN as PYTHON
26
+ from blockchecks.service.nfqws2 import start_daemon
27
+ from blockchecks.service.probe import invoke_curl_probe_worker, probe_request_dict
28
+
29
+ colorama_init(autoreset=True)
30
+
31
+ GREEN = Fore.GREEN + Style.BRIGHT
32
+ RED = Fore.RED + Style.BRIGHT
33
+ CYAN = Fore.CYAN
34
+ RESET = Style.RESET_ALL
35
+
36
+ DOMAINS = [
37
+ "discord.com",
38
+ "discord.gg",
39
+ "discord.media",
40
+ "discordapp.com",
41
+ "discordcdn.com",
42
+ "gateway.discord.gg",
43
+ ]
44
+
45
+ _FQDN_RE = re.compile(r"^[A-Za-z0-9]([A-Za-z0-9.-]*[A-Za-z0-9])?$")
46
+
47
+
48
+ def _valid_domain(domain: str) -> bool:
49
+ return bool(domain) and len(domain) < 253 and _FQDN_RE.match(domain) is not None
50
+
51
+
52
+ def normalize_domains(domains: list[str] | None) -> list[str]:
53
+ """Split comma-separated argv tokens and strip; drop empties; dedupe order-preserving."""
54
+ if not domains:
55
+ return list(DOMAINS)
56
+ out: list[str] = []
57
+ seen: set[str] = set()
58
+ for raw in domains:
59
+ for part in str(raw).split(","):
60
+ d = part.strip()
61
+ if not d or d in seen:
62
+ continue
63
+ seen.add(d)
64
+ out.append(d)
65
+ return out
66
+
67
+
68
+ async def run(
69
+ config_path: str, domains: list[str] = None, _parallel: int = 2, timeout: float = 5.0
70
+ ):
71
+ domains = normalize_domains(domains)
72
+
73
+ config_abs = os.path.abspath(config_path)
74
+ if not os.path.exists(config_abs):
75
+ print(f"{RED}Config not found: {config_abs}{RESET}")
76
+ return 1
77
+
78
+ print(f"\n{CYAN}composite{RESET} {os.path.basename(config_abs)} {len(domains)} domains")
79
+ print()
80
+
81
+ # One netns + one nfqws2 for ALL domains
82
+ runner = AsyncTestRunner(pool_size=1)
83
+ await runner.start()
84
+ ns_name = await runner.pool.acquire()
85
+
86
+ item = StrategyItem(label="composite", strategy=config_abs, is_config=True)
87
+ t0 = time.perf_counter()
88
+ results = []
89
+
90
+ try:
91
+ # Start the single nfqws2 instance
92
+ await asyncio.to_thread(start_daemon, ns_name, config_abs)
93
+ await asyncio.sleep(0.5)
94
+
95
+ # Add iptables rules inside the netns
96
+ sp.run(
97
+ [
98
+ "sudo",
99
+ "ip",
100
+ "netns",
101
+ "exec",
102
+ ns_name,
103
+ "iptables",
104
+ "-A",
105
+ "OUTPUT",
106
+ "-p",
107
+ "tcp",
108
+ "--dport",
109
+ "443",
110
+ "-j",
111
+ "NFQUEUE",
112
+ "--queue-num",
113
+ str(NFQUEUE_TCP),
114
+ "--queue-bypass",
115
+ ],
116
+ capture_output=True,
117
+ timeout=5,
118
+ )
119
+ sp.run(
120
+ [
121
+ "sudo",
122
+ "ip",
123
+ "netns",
124
+ "exec",
125
+ ns_name,
126
+ "iptables",
127
+ "-A",
128
+ "OUTPUT",
129
+ "-p",
130
+ "udp",
131
+ "-m",
132
+ "multiport",
133
+ "--dports",
134
+ "50000:50100",
135
+ "-j",
136
+ "NFQUEUE",
137
+ "--queue-num",
138
+ str(NFQUEUE_UDP),
139
+ "--queue-bypass",
140
+ ],
141
+ capture_output=True,
142
+ timeout=5,
143
+ )
144
+
145
+ # Test all domains sequentially (sharing one nfqws2) via JSON worker
146
+ for domain in domains:
147
+ if not _valid_domain(domain):
148
+ results.append(
149
+ TcpTestResult(
150
+ item=item,
151
+ domain=domain,
152
+ success=False,
153
+ error="invalid domain",
154
+ )
155
+ )
156
+ print(f" {RED}FAIL{RESET} {domain} — invalid domain")
157
+ continue
158
+ payload = {
159
+ "mode": "single",
160
+ "request": probe_request_dict(CurlProbeRequest(domain=domain, timeout=timeout)),
161
+ "repeats": 1,
162
+ "parallel_repeats": False,
163
+ "repeats_mode": "fast",
164
+ "quick_break": False,
165
+ }
166
+ wall = worker_wall_timeout(timeout, 1, settle_slack=3.0)
167
+ data = await asyncio.to_thread(invoke_curl_probe_worker, ns_name, PYTHON, payload, wall)
168
+
169
+ result = TcpTestResult(item=item, domain=domain)
170
+ result.success = data.get("success", False)
171
+ result.http_code = data.get("http_code", 0)
172
+ result.latency_ms = data.get("latency_ms", 0)
173
+ result.error = data.get("error", "") or ""
174
+ results.append(result)
175
+
176
+ tag = f"{GREEN}OK{RESET}" if result.success else f"{RED}FAIL{RESET}"
177
+ lat = f"{result.latency_ms:.0f}ms" if result.latency_ms else ""
178
+ code_str = f"HTTP {result.http_code}" if result.http_code else ""
179
+ err = f" — {result.error[:50]}" if result.error else ""
180
+ print(f" {tag} {domain:30s} {lat:>8s} {code_str}{err}")
181
+
182
+ finally:
183
+ await runner.pool.release(ns_name)
184
+ await runner.stop()
185
+
186
+ elapsed = time.perf_counter() - t0
187
+ passed = sum(1 for r in results if r.success)
188
+ print(f"\n {passed}/{len(results)} passed in {elapsed:.1f}s")
189
+ return 0 if passed > 0 else 1