load-net

Untitled

Jul 25th, 2024
76
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 11.24 KB | None | 0 0
  1. #!/usr/bin/env python3
  2.  
  3. import asyncio
  4. import random
  5. import sys
  6. import urllib.parse
  7. import argparse
  8. import subprocess
  9. import os.path
  10. import requests
  11.  
  12. DESCRIPTION = "Downloads all Tor Relay IP addresses from onionoo.torproject.org and checks whether random Relays are available."
  13.  
  14. class TCPSocketConnectChecker:
  15. def __init__(self, host, port, timeout=10.0):
  16. self.host = host
  17. self.port = port
  18. self.timeout = timeout
  19. self.connection_status = None
  20.  
  21. def __repr__(self):
  22. return "{}:{}".format(
  23. self.host if self.host.find(":") == -1 else "[" + self.host + "]",
  24. self.port)
  25.  
  26. async def connect(self):
  27. try:
  28. # Open connection
  29. reader, writer = await asyncio.wait_for(
  30. asyncio.open_connection(self.host, self.port), self.timeout)
  31. # And close it
  32. writer.close()
  33. await writer.wait_closed()
  34. self.connection_status = True
  35. return (True, None)
  36. except (OSError, asyncio.TimeoutError) as e:
  37. self.connection_status = False
  38. return (False, e)
  39.  
  40. class TorRelayGrabber:
  41. def __init__(self, timeout=10.0, proxy=None):
  42. self.timeout = timeout
  43. self.proxy = {'https': proxy} if proxy else None
  44.  
  45. def _grab(self, url):
  46. with requests.get(url, timeout=int(self.timeout), proxies=self.proxy) as r:
  47. return r.json()
  48.  
  49. def grab(self, preferred_urls_list=None):
  50. BASEURL = "https://onionoo.torproject.org/details?type=relay&running=true&fields=fingerprint,or_addresses,country"
  51. # Use public CORS proxy as a regular proxy in case if onionoo.torproject.org is unreachable
  52. URLS = [BASEURL,
  53. "https://icors.vercel.app/?" + urllib.parse.quote(BASEURL),
  54. "https://github.com/ValdikSS/tor-onionoo-mirror/raw/master/details-running-relays-fingerprint-address-only.json",
  55. "https://bitbucket.org/ValdikSS/tor-onionoo-mirror/raw/master/details-running-relays-fingerprint-address-only.json"]
  56. if preferred_urls_list:
  57. for pref_url in preferred_urls_list:
  58. URLS.insert(0, pref_url)
  59.  
  60. for url in URLS:
  61. try:
  62. return self._grab(url)
  63. except Exception as e:
  64. print("Can't download Tor Relay data from/via {}: {}".format(
  65. urllib.parse.urlparse(url).hostname, e
  66. ), file=sys.stderr)
  67.  
  68. def grab_parse(self, preferred_urls_list=None):
  69. grabbed = self.grab(preferred_urls_list)
  70. if grabbed:
  71. grabbed = grabbed["relays"]
  72. return grabbed
  73.  
  74.  
  75. class TorRelay:
  76. def __init__(self, relayinfo):
  77. self.relayinfo = relayinfo
  78. self.fingerprint = relayinfo["fingerprint"]
  79. self.iptuples = self._parse_or_addresses(relayinfo["or_addresses"])
  80. self.reachable = list()
  81.  
  82. def reachables(self):
  83. r = list()
  84. for i in self.reachable:
  85. r.append("{}:{} {}".format(i[0] if i[0].find(":") == -1 else "[" + i[0] + "]",
  86. i[1],
  87. self.fingerprint,))
  88. return r
  89.  
  90. def _reachable_str(self):
  91. return "\n".join(self.reachables())
  92.  
  93. def __repr__(self):
  94. if not self.reachable:
  95. return str(self.relayinfo)
  96. return self._reachable_str()
  97.  
  98. def __len__(self):
  99. return len(self.reachable)
  100.  
  101. def _parse_or_addresses(self, or_addresses):
  102. ret = list()
  103. for address in or_addresses:
  104. parsed = urllib.parse.urlparse("//" + address)
  105. ret.append((parsed.hostname, parsed.port))
  106. return ret
  107.  
  108. async def check(self, timeout=10.0):
  109. for i in self.iptuples:
  110. s = TCPSocketConnectChecker(i[0], i[1], timeout=timeout)
  111. sc = await s.connect()
  112. if sc[0]:
  113. self.reachable.append(i)
  114.  
  115. return bool(self.reachable)
  116.  
  117.  
  118. def start_browser():
  119. browser_cmds=("Browser/start-tor-browser --detach", "Browser/firefox.exe")
  120. for cmd in browser_cmds:
  121. if os.path.exists(cmd.split(" ")[0]):
  122. subprocess.Popen(cmd.split(" "))
  123. break
  124.  
  125.  
  126. def str_list_with_prefix(prefix, list_):
  127. return "\n".join([prefix + r for r in list_])
  128.  
  129.  
  130. async def main_async(args):
  131. NUM_RELAYS = args.num_relays
  132. WORKING_RELAY_NUM_GOAL = args.working_relay_num_goal
  133. TIMEOUT = args.timeout
  134. outstream = args.outfile
  135. torrc_fmt = args.torrc_fmt
  136. BRIDGE_PREFIX = "Bridge " if torrc_fmt else ""
  137.  
  138. print(f"Tor Relay Scanner. Will scan up to {WORKING_RELAY_NUM_GOAL}" +
  139. " working relays (or till the end)", file=sys.stderr)
  140. print("Downloading Tor Relay information from Tor Metrics…", file=sys.stderr)
  141. relays = TorRelayGrabber(timeout=TIMEOUT, proxy=args.proxy).grab_parse(args.url)
  142. if not relays:
  143. print("Tor Relay information can't be downloaded!", file=sys.stderr)
  144. return 1
  145. print("Done!", file=sys.stderr)
  146.  
  147. random.shuffle(relays)
  148.  
  149. if args.preferred_country:
  150. countries = {}
  151. for i, c in enumerate(args.preferred_country.split(",")):
  152. countries[c] = i
  153. # 1000 is just a sufficiently large number for default sorting
  154. relays = sorted(relays, key=lambda x: countries.get(x.get("country"), 1000))
  155.  
  156. if args.port:
  157. relays_new = list()
  158. for relay in relays:
  159. for ipport in TorRelay(relay).iptuples:
  160. if ipport[1] in args.port:
  161. # deep copy needed here, otherwise subsequent loop
  162. # modifies "previous" value
  163. relay_copy = relay.copy()
  164. relay_copy["or_addresses"] = ["{}:{}".format(
  165. ipport[0] if ipport[0].find(":") == -1 else "[" + ipport[0] + "]",
  166. ipport[1])
  167. ]
  168. relays_new.append(relay_copy)
  169. relays = relays_new
  170. if not relays:
  171. print("There are no relays within specified port number constrains!", file=sys.stderr)
  172. print("Try changing port numbers.", file=sys.stderr)
  173. return 2
  174.  
  175. working_relays = list()
  176. ntry = 0
  177. relaypos = 0
  178. numtries = round(len(relays) / NUM_RELAYS)
  179. for ntry in range(numtries):
  180. if len(working_relays) >= WORKING_RELAY_NUM_GOAL:
  181. break
  182.  
  183. relaynum = min(NUM_RELAYS, len(relays) - relaypos - 1)
  184. test_relays = [TorRelay(relays[x])
  185. for x in range(relaypos, relaypos+relaynum)]
  186. relaypos += NUM_RELAYS
  187.  
  188. if not test_relays:
  189. break
  190.  
  191. print(
  192. f"\nTry {ntry}/{numtries}, We'll test the following {NUM_RELAYS} random relays:", file=sys.stderr)
  193. for relay in test_relays:
  194. print(relay, file=sys.stderr)
  195. print("", file=sys.stderr)
  196.  
  197. if ntry:
  198. print(f"Found {len(working_relays)} good relays so far. Test {ntry}/{numtries} started…", file=sys.stderr)
  199. else:
  200. print(f"Test started…", file=sys.stderr)
  201.  
  202. tasks = list()
  203. for relay in test_relays:
  204. tasks.append(asyncio.create_task(relay.check(TIMEOUT)))
  205. fin = await asyncio.gather(*tasks)
  206. print("", file=sys.stderr)
  207.  
  208. print("The following relays are reachable this try:", file=sys.stderr)
  209. for relay in test_relays:
  210. if relay:
  211. print(str_list_with_prefix(BRIDGE_PREFIX, relay.reachables()), file=outstream)
  212. if sys.stdout != outstream:
  213. print(str_list_with_prefix(BRIDGE_PREFIX, relay.reachables()), file=sys.stderr)
  214. working_relays.append(relay)
  215. if not any(test_relays):
  216. print("No relays are reachable this try.", file=sys.stderr)
  217.  
  218. if ntry > 1:
  219. print("", file=sys.stderr)
  220. print("All reachable relays:", file=sys.stderr)
  221. for relay in working_relays:
  222. if relay:
  223. print(str_list_with_prefix(BRIDGE_PREFIX, relay.reachables()), file=sys.stderr)
  224. if not any(working_relays):
  225. print("No relays are reachable, at all.", file=sys.stderr)
  226.  
  227. if any(working_relays):
  228. if torrc_fmt:
  229. print("UseBridges 1", file=outstream)
  230. if args.prefsjs:
  231. try:
  232. with open(args.prefsjs, "r+") as f:
  233. prefsjs = str()
  234. for line in f:
  235. if "torbrowser.settings.bridges." not in line:
  236. prefsjs += line
  237. # Ugly r.reachables() array flattening, as it may have more than one reachable record.
  238. for num, relay in enumerate(sum([r.reachables() for r in working_relays], [])):
  239. prefsjs += f'user_pref("torbrowser.settings.bridges.bridge_strings.{num}", "{relay}");\n'
  240. prefsjs += 'user_pref("torbrowser.settings.bridges.enabled", true);\n'
  241. prefsjs += 'user_pref("torbrowser.settings.bridges.source", 2);\n'
  242. f.seek(0)
  243. f.truncate()
  244. f.write(prefsjs)
  245. except OSError as e:
  246. print("Can't open Tor Browser configuration:", e, file=sys.stderr)
  247.  
  248. if args.start_browser:
  249. start_browser()
  250.  
  251.  
  252. def main():
  253. parser = argparse.ArgumentParser(description=DESCRIPTION)
  254. parser.add_argument('-n', type=int, dest='num_relays', default=30, help='The number of concurrent relays tested.')
  255. parser.add_argument('-g', '--goal', type=int, dest='working_relay_num_goal', default=5, help='Test until at least this number of working relays are found')
  256. parser.add_argument('-c', '--preferred-country', type=str, default="", help='Preferred country list, comma-separated. Example: se,gb,nl,de')
  257. parser.add_argument('--timeout', type=float, default=10.0, help='Socket connection timeout')
  258. parser.add_argument('-o', '--outfile', type=argparse.FileType('w'), default=sys.stdout, help='Output reachable relays to file')
  259. parser.add_argument('--torrc', action='store_true', dest='torrc_fmt', help='Output reachable relays in torrc format (with "Bridge" prefix)')
  260. parser.add_argument('--proxy', type=str, help='Set proxy for onionoo information download. Format: http://user:pass@host:port; socks5h://user:pass@host:port')
  261. parser.add_argument('--url', type=str, action='append', help='Preferred alternative URL for onionoo relay list. Could be used multiple times.')
  262. parser.add_argument('-p', type=int, dest='port', action='append', help='Scan for relays running on specified port number. Could be used multiple times.')
  263. parser.add_argument('--browser', type=str, nargs='?', metavar='/path/to/prefs.js', dest='prefsjs',
  264. const='Browser/TorBrowser/Data/Browser/profile.default/prefs.js',
  265. help='Install found relays into Tor Browser configuration file (prefs.js)')
  266. parser.add_argument('--start-browser', action='store_true', help='Launch browser after scanning')
  267. args = parser.parse_args()
  268. try:
  269. return asyncio.run(main_async(args))
  270. except (KeyboardInterrupt, SystemExit):
  271. pass
Add Comment
Please, Sign In to add comment