<commit_before>#!/usr/bin/env python
# Copyright 2010 <NAME> <<EMAIL>>
# See LICENSE for licensing information.

# TorDNSEL-compatible DNS implementation using Twisted Names
# and the TorBEL query interface.
import sys, time
from datetime import datetime

from zope.interface import implements
from twisted.names import server, dns, common, error
from twisted.internet import reactor, interfaces, defer
from twisted.python import failure

from torbel.dnsel import config
from torbel.query import ExitList
from torbel.utils import check_type, ConfigurationError
from torbel import logger

from ipaddr import IPAddress

log = logger.create_logger("tordnsel", logger.DEBUG)

nxdomain = error.DomainError

class TorDNSServerFactory(server.DNSServerFactory):
    def __init__(self, zone, filename, status, *args, **kwargs):
        server.DNSServerFactory.__init__(self, *args, **kwargs)
        self.el = ExitList(filename, status)

        if self.el.stale:
            log.info("Export %s likely stale.", filename)

        # Set up updates.
        nextUpdate = time.mktime(self.el.next_update.timetuple()) - time.time()
        reactor.callLater(nextUpdate, self.update)
        log.debug("Scheduling first update in %.1f seconds.", nextUpdate)
        
        self.root = zone.split(".")
        self.root_name = zone

    def update(self):
        next = self.el.update()
        nextUpdate = time.mktime(next.timetuple()) - time.time()
        log.info("ExitList updated. Next update in %.1f seconds.", nextUpdate)

        reactor.callLater(nextUpdate, self.update)
        
    def handleQuery(self, message, protocol, address):
        query = message.queries[0]
        ip, port = address

        return self.lookup(ip, query, None).addCallback(
            self.gotResolverResponse, protocol, message, address
        ).addErrback(
            self.gotResolverError, protocol, message, address
        )

    def exit_search(self, name, dest_ip, dest_port, tor_ip):
        log.debug("query tor IP %s, dest_ip %s, dest_port %d",
                  tor_ip, dest_ip, dest_port)

        router = self.el.tor_exit_search(int(tor_ip), dest_ip, dest_port)
        if router:
            log.debug("Request for %s:%d matches router %s(%s).",
                      tor_ip, dest_port, router.idhex, router.nickname)
            
            return ([dns.RRHeader(name, dns.A, dns.IN, config.ttl,
                                  payload = dns.Record_A("127.0.0.2"),
                                  auth = True)],
                    # Authority section
                    [dns.RRHeader(self.root_name, dns.NS, dns.IN, config.ttl,
                                  payload = dns.Record_NS(self.root_name))],
                    # Additional section: give the router's idhex and nickname
                    # as CNAME records.
                    [dns.RRHeader(self.root_name, dns.CNAME, dns.IN, config.ttl,
                                  payload = dns.Record_CNAME("id=" +router.idhex)),
                     dns.RRHeader(self.root_name, dns.CNAME, dns.IN, config.ttl,
                                  payload = dns.Record_CNAME("nickname=" + router.nickname))]
                    )

        raise nxdomain(name)

    def lookup(self, address, query, timeout):
        try:
            return defer.succeed(self._lookup(address, query, timeout))
        except error.DomainError, e:
            return defer.fail(failure.Failure(e))
        
    def _lookup(self, address, query, timeout):
        name = str(query.name)
        type = query.type
        cls = dns.IN

        # Refuse non-IN/A questions.
        if cls != dns.IN or type != dns.A:
            log.debug("We don't handle cls %s type %s", cls, type)
            return error.DNSQueryRefusedError()
        # Refuse requests for requests that aren't in our zone of authority.
        q = name.split(".")
        if q[-len(self.root):] != self.root:
            log.debug("We are authoritative and don't handle the TLD of %s",
                      repr(name))
            raise error.DNSQueryRefusedError()

        # Break off the root TLD and the query type.
        q = q[:-len(self.root)]
        qtype = q.pop()

        # DNSEL query type 1 "General IP:Port":
        # Format: {IP1}.{port}.{IP2}.ip-port.torhosts.example.com
        if config.enable_ip_port and qtype == "ip-port":
            # Attempt to parse the DNSEL request.
            try:
                tor_ip = IPAddress("%s.%s.%s.%s" % (q[3], q[2], q[1], q[0]))
                dest_port = int(q[4])
                dest_ip = IPAddress("%s.%s.%s.%s" % (q[8], q[7], q[6], q[5]))
            except:
                raise nxdomain(name)

            return self.exit_search(name, dest_ip, dest_port, tor_ip)
                
        elif config.enable_ip_port_list and qtype == "ip-port-list":
            dest_port = int(q[0])
            dest_ip = IPAddress("%s.%s.%s.%s" % (q[4], q[3], q[2], q[1]))
            log.debug("Query type %s, dest_ip %s, dest_port %d",
                      qtype, dest_ip, dest_port)<commit_msg>dnsel: return NXDOMAIN on any failure.<commit_after>#!/usr/bin/env python
# Copyright 2010 <NAME> <<EMAIL>>
# See LICENSE for licensing information.

# TorDNSEL-compatible DNS implementation using Twisted Names
# and the TorBEL query interface.
import sys, time
from datetime import datetime

from zope.interface import implements
from twisted.names import server, dns, common, error
from twisted.internet import reactor, interfaces, defer
from twisted.python import failure

from torbel.dnsel import config
from torbel.query import ExitList
from torbel.utils import check_type, ConfigurationError
from torbel import logger

from ipaddr import IPAddress

log = logger.create_logger("tordnsel", logger.DEBUG)

nxdomain = error.DomainError

class TorDNSServerFactory(server.DNSServerFactory):
    def __init__(self, zone, filename, status, *args, **kwargs):
        server.DNSServerFactory.__init__(self, *args, **kwargs)
        self.el = ExitList(filename, status)

        if self.el.stale:
            log.info("Export %s likely stale.", filename)

        # Set up updates.
        nextUpdate = time.mktime(self.el.next_update.timetuple()) - time.time()
        reactor.callLater(nextUpdate, self.update)
        log.debug("Scheduling first update in %.1f seconds.", nextUpdate)
        
        self.root = zone.split(".")
        self.root_name = zone

    def update(self):
        next = self.el.update()
        nextUpdate = time.mktime(next.timetuple()) - time.time()
        log.info("ExitList updated. Next update in %.1f seconds.", nextUpdate)

        reactor.callLater(nextUpdate, self.update)
        
    def handleQuery(self, message, protocol, address):
        query = message.queries[0]
        ip, port = address

        return self.lookup(ip, query, None).addCallback(
            self.gotResolverResponse, protocol, message, address
        ).addErrback(
            self.gotResolverError, protocol, message, address
        )

    def exit_search(self, name, dest_ip, dest_port, tor_ip):
        log.debug("query tor IP %s, dest_ip %s, dest_port %d",
                  tor_ip, dest_ip, dest_port)

        router = self.el.tor_exit_search(int(tor_ip), dest_ip, dest_port)
        if router:
            log.debug("Request for %s:%d matches router %s(%s).",
                      tor_ip, dest_port, router.idhex, router.nickname)
            
            return ([dns.RRHeader(name, dns.A, dns.IN, config.ttl,
                                  payload = dns.Record_A("127.0.0.2"),
                                  auth = True)],
                    # Authority section
                    [dns.RRHeader(self.root_name, dns.NS, dns.IN, config.ttl,
                                  payload = dns.Record_NS(self.root_name))],
                    # Additional section: give the router's idhex and nickname
                    # as CNAME records.
                    [dns.RRHeader(self.root_name, dns.CNAME, dns.IN, config.ttl,
                                  payload = dns.Record_CNAME("id=" +router.idhex)),
                     dns.RRHeader(self.root_name, dns.CNAME, dns.IN, config.ttl,
                                  payload = dns.Record_CNAME("nickname=" + router.nickname))]
                    )

        raise nxdomain(name)

    def lookup(self, address, query, timeout):
        try:
            return defer.succeed(self._lookup(address, query, timeout))
        except error.DomainError, e:
            return defer.fail(failure.Failure(e))
        # Return NXDOMAIN on any parser failure.
        except:
            return defer.fail(failure.Failure(nxdomain(str(query.name))))
        
    def _lookup(self, address, query, timeout):
        name = str(query.name)
        type = query.type
        cls = dns.IN

        # Refuse non-IN/A questions.
        if cls != dns.IN or type != dns.A:
            log.debug("We don't handle cls %s type %s", cls, type)
            return error.DNSQueryRefusedError()
        # Refuse requests for requests that aren't in our zone of authority.
        q = name.split(".")
        if q[-len(self.root):] != self.root:
            log.debug("We are authoritative and don't handle the TLD of %s",
                      repr(name))
            raise error.DNSQueryRefusedError()

        # Break off the root TLD and the query type.
        q = q[:-len(self.root)]
        qtype = q.pop()

        # DNSEL query type 1 "General IP:Port":
        # Format: {IP1}.{port}.{IP2}.ip-port.torhosts.example.com
        if config.enable_ip_port and qtype == "ip-port":
            # Attempt to parse the DNSEL request.
            tor_ip = IPAddress("%s.%s.%s.%s" % (q[3], q[2], q[1], q[0]))
            dest_port = int(q[4])
            dest_ip = IPAddress("%s.%s.%s.%s" % (q[8], q[7], q[6], q[5]))

            return self.exit_search(name, dest_ip, dest_port, tor_ip)
                
        elif config.enable_ip_port_list and qtype == "ip-port-list":
            dest_port = int(q[0])
            dest_ip = IPAddress("%s.%s.%s.%s" % (q[4], q[3], q[2], q[1]))
            log.debug("Query type %s, dest_ip %s, dest_port %d",
                      qtype, dest_ip, dest_port)
{'fraction_non_alphanumeric': 0.11702544031311155, 'fraction_numerical': 0.005870841487279843, 'mean_word_length': 2.0592636935049384, 'pattern_counts': {'":': 6, '<': 9, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 2, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 10, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2827855', 'n_tokens_mistral': 3164, 'n_tokens_neox': 3013, 'n_words': 783}