Search code examples
botstor

Is there a way to find out if a particular IP is coming from Tor or acting as a Tor exit node?


I want to add a method for my bot protection project that allows certain rules, one of these rules will be that the current IP belongs to Tor, not because connections from Tor are bad but rather to provide customization options (like disabling JS).

I can't find a free solution without an API key that is fast enough to fulfill the requirements. The Tor Exit List also does not seem to contain all ips, such as certain Ipv6 ips.

I found Tor Exonera: https://metrics.torproject.org/exonerator.html but there doesn't seem to be an API for it.


Solution

  • I found the solution with the help of Possible to add proxy after TOR exit node?, which links to this blog article: https://lists.torproject.org/pipermail/tor-project/2020-March/002759.html.

    The following is the implementation for Python:

    import dns.resolver
    import ipaddress
    
    def reverse_ip(ip):
        try:
            ip_obj = ipaddress.ip_address(ip)
            if isinstance(ip_obj, ipaddress.IPv4Address):
                return '.'.join(reversed(ip.split('.'))) + '.dnsel.torproject.org'
            elif isinstance(ip_obj, ipaddress.IPv6Address):
                return '.'.join(reversed(ip_obj.exploded.replace(':', ''))) + '.dnsel.torproject.org'
        except ValueError:
            raise ValueError("Invalid IP address format")
    
    def is_tor_ip(ip):
        query = reverse_ip(ip)
        
        try:
            answers = dns.resolver.resolve(query, 'A')
            for rdata in answers:
                if rdata.to_text() == '127.0.0.2':
                    return True
        except dns.resolver.NXDOMAIN:
            return False
        except dns.resolver.NoAnswer:
            return False
        except dns.exception.DNSException as e:
            print(f"DNS query failed: {e}")
            return False
    
        return False
    
    print(is_tor_ip("185.220.101.25"))
    print(is_tor_ip("2a0b:f4c2:1::1"))