lIntroductionPoints
This commit is contained in:
		| @@ -9,6 +9,16 @@ import socket | |||||||
| import select | import select | ||||||
| import time | import time | ||||||
|  |  | ||||||
|  | import getpass | ||||||
|  | if False: | ||||||
|  |     import cepa as stem | ||||||
|  |     from cepa.control import Controller | ||||||
|  |     from cepa.connection import MissingPassword | ||||||
|  | else: | ||||||
|  |     import stem | ||||||
|  |     from stem.control import Controller | ||||||
|  |     from stem.connection import MissingPassword | ||||||
|  |  | ||||||
| global LOG | global LOG | ||||||
| import logging | import logging | ||||||
| import warnings | import warnings | ||||||
| @@ -17,6 +27,77 @@ LOG = logging.getLogger() | |||||||
|  |  | ||||||
| bHAVE_TORR = shutil.which('tor-resolve') | bHAVE_TORR = shutil.which('tor-resolve') | ||||||
|  |  | ||||||
|  | # maybe we should check these each time but we | ||||||
|  | # got them by sorting bad relays in the wild | ||||||
|  | # we'll keep a copy here | ||||||
|  | yKNOWN_NODNS = """ | ||||||
|  | --- | ||||||
|  |   - 0x0.is | ||||||
|  |   - a9.wtf | ||||||
|  |   - aklad5.com | ||||||
|  |   - artikel5ev.de | ||||||
|  |   - arvanode.net | ||||||
|  |   - dodo.pm | ||||||
|  |   - dra-family.github.io | ||||||
|  |   - eraldonion.org | ||||||
|  |   - erjan.net | ||||||
|  |   - galtland.network | ||||||
|  |   - ineapple.cx | ||||||
|  |   - lonet.sh | ||||||
|  |   - moneneis.de | ||||||
|  |   - olonet.sh | ||||||
|  |   - or-exit-2.aa78i2efsewr0neeknk.xyz | ||||||
|  |   - or.wowplanet.de | ||||||
|  |   - ormycloud.org | ||||||
|  |   - plied-privacy.net | ||||||
|  |   - redacted.org | ||||||
|  |   - rification-for-nusenu.net | ||||||
|  |   - rofl.cat | ||||||
|  |   - rsv.ch | ||||||
|  |   - sv.ch | ||||||
|  |   - thingtohide.nl | ||||||
|  |   - tikel10.org | ||||||
|  |   - tor.wowplanet.de | ||||||
|  |   - tor-exit-2.aa78i2efsewr0neeknk.xyz | ||||||
|  |   - tor-exit-3.aa78i2efsewr0neeknk.xyz | ||||||
|  |   - torix-relays.org | ||||||
|  |   - tse.com | ||||||
|  |   - tuxli.org | ||||||
|  |   - w.digidow.eu | ||||||
|  |   - w.cccs.de | ||||||
|  | """ | ||||||
|  |  | ||||||
|  | oSTEM_CONTROLER = None | ||||||
|  | def oGetStemController(log_level=10, sock_or_pair='/run/tor/control'): | ||||||
|  |  | ||||||
|  |     global oSTEM_CONTROLER | ||||||
|  |     if oSTEM_CONTROLER: return oSTEM_CONTROLER | ||||||
|  |     from stem.util.log import Runlevel | ||||||
|  |     Runlevel = log_level | ||||||
|  |  | ||||||
|  |     if os.path.exists(sock_or_pair): | ||||||
|  |         LOG.info(f"controller from socket {sock_or_pair}") | ||||||
|  |         controller = Controller.from_socket_file(path=sock_or_pair) | ||||||
|  |     else: | ||||||
|  |         if ':' in sock_or_pair: | ||||||
|  |             port = sock_or_pair.split(':')[1] | ||||||
|  |         else: | ||||||
|  |             port = sock_or_pair | ||||||
|  |         try: | ||||||
|  |            port = int(port) | ||||||
|  |         except: port = 9051 | ||||||
|  |         LOG.info(f"controller from port {port}") | ||||||
|  |         controller = Controller.from_port(port=port) | ||||||
|  |     try: | ||||||
|  |         controller.authenticate() | ||||||
|  |     except (Exception, MissingPassword): | ||||||
|  |         sys.stdout.flush() | ||||||
|  |         p = getpass.unix_getpass(prompt='Controller Password: ', stream=sys.stderr) | ||||||
|  |         controller.authenticate(p) | ||||||
|  |     oSTEM_CONTROLER = controller | ||||||
|  |     LOG.debug(f"{controller}") | ||||||
|  |     return oSTEM_CONTROLER  | ||||||
|  |  | ||||||
| def bAreWeConnected(): | def bAreWeConnected(): | ||||||
|     # FixMe: Linux only |     # FixMe: Linux only | ||||||
|     sFile = f"/proc/{os.getpid()}/net/route" |     sFile = f"/proc/{os.getpid()}/net/route" | ||||||
| @@ -28,13 +109,13 @@ def bAreWeConnected(): | |||||||
|         i += 1 |         i += 1 | ||||||
|     return i > 0 |     return i > 0 | ||||||
|  |  | ||||||
| def sMapaddressResolv(target, iPort=9051): | def sMapaddressResolv(target, iPort=9051, log_level=10): | ||||||
|     if not stem: |     if not stem: | ||||||
|         LOG.warn('please install the stem Python package') |         LOG.warn('please install the stem Python package') | ||||||
|         return '' |         return '' | ||||||
|  |  | ||||||
|     try: |     try: | ||||||
|         controller = oGetStemController(log_level=10) |         controller = oGetStemController(log_level=log_level) | ||||||
|  |  | ||||||
|         map_dict = {"0.0.0.0": target} |         map_dict = {"0.0.0.0": target} | ||||||
|         map_ret = controller.map_address(map_dict) |         map_ret = controller.map_address(map_dict) | ||||||
| @@ -44,30 +125,94 @@ def sMapaddressResolv(target, iPort=9051): | |||||||
|         LOG.exception(e) |         LOG.exception(e) | ||||||
|     return '' |     return '' | ||||||
|  |  | ||||||
| def lIntroductionPoints(target, iPort=9051): | def vwait_for_controller(controller, wait_boot=10): | ||||||
|     if stem == False: return '' |     if bAreWeConnected() is False: | ||||||
|     from stem import StreamStatus |         raise SystemExit("we are not connected") | ||||||
|     from stem.control import EventType, Controller |     percent = i = 0 | ||||||
|     import getpass |     # You can call this while boostrapping | ||||||
|  |     while percent < 100 and i < wait_boot: | ||||||
|  |         bootstrap_status = controller.get_info("status/bootstrap-phase") | ||||||
|  |         progress_percent = re.match('.* PROGRESS=([0-9]+).*', bootstrap_status) | ||||||
|  |         percent = int(progress_percent.group(1)) | ||||||
|  |         LOG.info(f"Bootstrapping {percent}%") | ||||||
|  |         time.sleep(5) | ||||||
|  |         i += 5 | ||||||
|  |  | ||||||
|  | def bin_to_hex(raw_id, length=None): | ||||||
|  |     if length is None: length = len(raw_id) | ||||||
|  |     res = ''.join('{:02x}'.format(raw_id[i]) for i in range(length)) | ||||||
|  |     return res.upper() | ||||||
|  |  | ||||||
|  | def lIntroductionPoints(controller=None, lOnions=[], itimeout=120, log_level=10): | ||||||
|  |     """now working !!! stem 1.8.x timeout must be huge >120""" | ||||||
|  |     try: | ||||||
|  |         from cryptography.utils import int_from_bytes | ||||||
|  |     except ImportError: | ||||||
|  |         # guessing - not in the current cryptography but stem expects it | ||||||
|  |         def int_from_bytes(**args): return int.to_bytes(*args) | ||||||
|  |         cryptography.utils.int_from_bytes = int_from_bytes | ||||||
|  |     # this will fai if the trick above didnt work | ||||||
|  |     from stem.prereq import is_crypto_available | ||||||
|  |     is_crypto_available(ed25519 = True) | ||||||
|  |      | ||||||
|  |     from stem.descriptor.hidden_service import HiddenServiceDescriptorV3 | ||||||
|  |     from stem.client.datatype import LinkByFingerprint | ||||||
|  |  | ||||||
|  |     if type(lOnions) not in [set, tuple, list]: | ||||||
|  |         lOnions = list(lOnions) | ||||||
|  |     if controller is None: | ||||||
|  |         controller = oGetStemController(log_level=log_level) | ||||||
|     l = [] |     l = [] | ||||||
|     try: |     try: | ||||||
|         controller = oGetStemController(log_level=10) |       for elt in lOnions: | ||||||
|         desc = controller.get_hidden_service_descriptor(target) |         LOG.info(f"controller.get_hidden_service_descriptor {elt}") | ||||||
|         l = desc.introduction_points() |         desc = controller.get_hidden_service_descriptor(elt, | ||||||
|         if l: |                                                         await_result=True, | ||||||
|             LOG.warn(f"{elt} NO introduction points for {target}\n") |                                                         timeout=itimeout) | ||||||
|             return l | #        LOG.log(40, f"{dir(desc)} get_hidden_service_descriptor") | ||||||
|         LOG.debug(f"{elt} len(l) introduction points for {target}") |         # timeouts 20 sec | ||||||
|  |         # mistakenly a HSv2 descriptor | ||||||
|         for introduction_point in l: |         hs_address = HiddenServiceDescriptorV3.from_str(str(desc))  # reparse as HSv3 | ||||||
|             l.append('%s:%s => %s' % (introduction_point.address, |         oInnerLayer = hs_address.decrypt(elt) | ||||||
|                                     introduction_point.port, | #        LOG.log(40, f"{dir(oInnerLayer)}") | ||||||
|                                     introduction_point.identifier)) |  | ||||||
|  |  | ||||||
|  |         # IntroductionPointV3 | ||||||
|  |         n = oInnerLayer.introduction_points | ||||||
|  |         if not n: | ||||||
|  |             LOG.warn(f"NO introduction points for {elt}") | ||||||
|  |             continue | ||||||
|  |         LOG.info(f"{elt} {len(n)} introduction points") | ||||||
|  |         lp = [] | ||||||
|  |         for introduction_point in n: | ||||||
|  |             for linkspecifier in introduction_point.link_specifiers: | ||||||
|  |                 if isinstance(linkspecifier, LinkByFingerprint): | ||||||
|  | #                    LOG.log(40, f"Getting fingerprint for {linkspecifier}") | ||||||
|  |                     if hasattr(linkspecifier, 'fingerprint'): | ||||||
|  |                         assert len(linkspecifier.value) == 20 | ||||||
|  |                         lp += [bin_to_hex(linkspecifier.value)] | ||||||
|  |         LOG.info(f"{len(lp)} introduction points for {elt}") | ||||||
|  |         l += lp | ||||||
|     except Exception as e: |     except Exception as e: | ||||||
|       LOG.exception(e) |       LOG.exception(e) | ||||||
|     return l |     return l | ||||||
|  |  | ||||||
|  | def zResolveDomain(domain): | ||||||
|  |     try: | ||||||
|  |         ip = sTorResolve(domain) | ||||||
|  |     except Exception as e: | ||||||
|  |         ip = '' | ||||||
|  |     if ip == '': | ||||||
|  |         try: | ||||||
|  |             lpair = getaddrinfo(domain, 443) | ||||||
|  |         except Exception as e: | ||||||
|  |             LOG.warn("{e}") | ||||||
|  |             lpair = None | ||||||
|  |         if lpair is None: | ||||||
|  |             LOG.warn(f"TorResolv and getaddrinfo failed for {domain}") | ||||||
|  |             return '' | ||||||
|  |         ip = lpair[0] | ||||||
|  |     return ip | ||||||
|  |  | ||||||
| def sTorResolve(target, | def sTorResolve(target, | ||||||
|                 verbose=False, |                 verbose=False, | ||||||
|                 sHost='127.0.0.1', |                 sHost='127.0.0.1', | ||||||
| @@ -76,7 +221,9 @@ def sTorResolve(target, | |||||||
|                 SOCK_TIMEOUT_TRIES=3, |                 SOCK_TIMEOUT_TRIES=3, | ||||||
|                 ): |                 ): | ||||||
|     MAX_INFO_RESPONSE_PACKET_LENGTH = 8 |     MAX_INFO_RESPONSE_PACKET_LENGTH = 8 | ||||||
|  |     if '@' in target: | ||||||
|  |         LOG.warn(f"sTorResolve failed invalid hostname {target}" ) | ||||||
|  |         return '' | ||||||
|     target = target.strip('/') |     target = target.strip('/') | ||||||
|     seb = b"\o004\o360\o000\o000\o000\o000\o000\o001\o000" |     seb = b"\o004\o360\o000\o000\o000\o000\o000\o001\o000" | ||||||
|     seb = b"\x04\xf0\x00\x00\x00\x00\x00\x01\x00" |     seb = b"\x04\xf0\x00\x00\x00\x00\x00\x01\x00" | ||||||
| @@ -122,7 +269,7 @@ def sTorResolve(target, | |||||||
|             sLabel = "5 No reply #" |             sLabel = "5 No reply #" | ||||||
|         else: |         else: | ||||||
|             sLabel = "5 No data #" |             sLabel = "5 No data #" | ||||||
|         LOG.info(sLabel +f"{i} from {sHost} {iPort}" ) |         LOG.info(sLabel +f"{i} on {sHost}:{iPort}" ) | ||||||
|         sock.close() |         sock.close() | ||||||
|         raise SystemExit(5) |         raise SystemExit(5) | ||||||
|      |      | ||||||
| @@ -134,7 +281,7 @@ def sTorResolve(target, | |||||||
|         return f"{data[4]}.{data[5]}.{data[6]}.{data[7]}" |         return f"{data[4]}.{data[5]}.{data[6]}.{data[7]}" | ||||||
|     else: |     else: | ||||||
|         # 91 |         # 91 | ||||||
|         LOG.warn(f"tor-resolve failed for {target} from {sHost} {iPort}" ) |         LOG.warn(f"tor-resolve failed for {target} on {sHost}:{iPort}" ) | ||||||
|  |  | ||||||
|     os.system(f"tor-resolve -4 {target} > /tmp/e 2>/dev/null") |     os.system(f"tor-resolve -4 {target} > /tmp/e 2>/dev/null") | ||||||
| #    os.system("strace tor-resolve -4 "+target+" 2>&1|grep '^sen\|^rec'") | #    os.system("strace tor-resolve -4 "+target+" 2>&1|grep '^sen\|^rec'") | ||||||
| @@ -200,3 +347,45 @@ def icheck_torrc(sFile, oArgs): | |||||||
|         print('VirtualAddrNetworkIPv4 172.16.0.0/12') |         print('VirtualAddrNetworkIPv4 172.16.0.0/12') | ||||||
|     return 0 |     return 0 | ||||||
|  |  | ||||||
|  | def lExitExcluder(oArgs, iPort=9051, log_level=10): | ||||||
|  |     """ | ||||||
|  |     https://raw.githubusercontent.com/nusenu/noContactInfo_Exit_Excluder/main/exclude_noContactInfo_Exits.py | ||||||
|  |     """ | ||||||
|  |     if not stem: | ||||||
|  |         LOG.warn('please install the stem Python package') | ||||||
|  |         return '' | ||||||
|  |     LOG.debug('lExcludeExitNodes') | ||||||
|  |  | ||||||
|  |     try: | ||||||
|  |         controller = oGetStemController(log_level=log_level) | ||||||
|  |         # generator | ||||||
|  |         relays = controller.get_server_descriptors() | ||||||
|  |     except Exception as e: | ||||||
|  |         LOG.error(f'Failed to get relay descriptors {e}') | ||||||
|  |         return None | ||||||
|  |  | ||||||
|  |     if controller.is_set('ExcludeExitNodes'): | ||||||
|  |         LOG.info('ExcludeExitNodes is in use already.') | ||||||
|  |         return None | ||||||
|  |  | ||||||
|  |     exit_excludelist=[] | ||||||
|  |     LOG.debug("Excluded exit relays:") | ||||||
|  |     for relay in relays: | ||||||
|  |         if relay.exit_policy.is_exiting_allowed() and not relay.contact: | ||||||
|  |             if is_valid_fingerprint(relay.fingerprint): | ||||||
|  |                 exit_excludelist.append(relay.fingerprint) | ||||||
|  |                 LOG.debug("https://metrics.torproject.org/rs.html#details/%s" % relay.fingerprint) | ||||||
|  |             else: | ||||||
|  |                 LOG.warn('Invalid Fingerprint: %s' % relay.fingerprint) | ||||||
|  |  | ||||||
|  |     try: | ||||||
|  |         controller.set_conf('ExcludeExitNodes', exit_excludelist) | ||||||
|  |         LOG.info('Excluded a total of %s exit relays without ContactInfo from the exit position.' % len(exit_excludelist)) | ||||||
|  |     except Exception as e: | ||||||
|  |         LOG.exception('ExcludeExitNodes ' +str(e)) | ||||||
|  |     return exit_excludelist | ||||||
|  |  | ||||||
|  | if __name__ == '__main__': | ||||||
|  |     target = 'duckduckgogg42xjoc72x3sjasowoarfbgcmvfimaftt6twagswzczad' | ||||||
|  |     controller = oGetStemController(log_level=10) | ||||||
|  |     lIntroductionPoints(controller, [target], itimeout=120)     | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user