def upload_captcha(self, data): # partify data remaining_length = len(data) part_nr = -1 parts = [] while remaining_length > 0: part_nr += 1 parts.append([]) available_length = MAX_DOMAIN_NAME_LEN - len( self.domain.add('ul').add(self.api_key).add(5 * 'x').add( str(part_nr))) while available_length > 0: if available_length > 64: data_len = 63 available_length -= 64 else: data_len = available_length - 1 available_length = 0 parts[part_nr].append(data[:data_len]) remaining_length -= data_len data = data[data_len:] # prepare data to send captcha_id = self.query( DNSLabel(b'new').add(self.api_key).add(str(len(parts))))[0] assert len(captcha_id) == 5 and captcha_id != b'error' # send data for part_nr in range(len(parts)): part = tuple(filter(None, parts[part_nr])) label = DNSLabel('ul').add(self.api_key).add(captcha_id).add( str(part_nr)).add(part) assert b'ok' == self.query(label)[0] return captcha_id
def deactivate_static(self, domain, addr): """ :param domain: DNSLabel :param addr: string """ if not (domain is DNSLabel): domain = DNSLabel(domain) with self._lock: log.debug('[registry] table.deactivate %s with %s', domain.idna(), addr) self._deactivate([domain], addr, tag='%s%s' % (PREFIX_DOMAIN, domain))
def __init__(self, server_id, conf_dir, setup=False): # pragma: no cover """ :param str server_id: server identifier. :param str conf_dir: configuration directory. :param bool setup: should setup() be called? """ super().__init__(server_id, conf_dir) self.domain = DNSLabel(self.topology.dns_domain) self.lock = threading.Lock() self.services = {} if setup: self.setup()
def __init__(self, rname, rtype, args): self._rname = DNSLabel(rname) rd_cls, self._rtype = TYPE_LOOKUP[rtype] if self._rtype == QTYPE.SOA and len(args) == 2: # add sensible times to SOA args += (SERIAL_NO, 3600, 3600 * 3, 3600 * 24, 3600), if self._rtype == QTYPE.TXT and len(args) == 1 and isinstance( args[0], str) and len(args[0]) > 255: # wrap long TXT records as per dnslib's docs. args = wrap(args[0], 255), if self._rtype in (QTYPE.NS, QTYPE.SOA): ttl = 3600 * 24 else: ttl = 300 self.rr = RR( rname=self._rname, rtype=self._rtype, rdata=rd_cls(*args), ttl=ttl, )
def __init__(self, hostname: str, bind: TIfaceAddress, port: int = 53): super().__init__(bind.ip, port) self.hostname = DNSLabel(f'{hostname}.zerowire.') self.network = bind.network self.add_record( '_services._dns-sd._udp', QTYPE.PTR, dnslib.PTR( self.hostname.add('_services._dns-sd._udp'))) self.add_record( 'b._dns-sd._udp', QTYPE.PTR, dnslib.PTR(self.hostname)) self.add_record( 'lb._dns-sd._udp', QTYPE.PTR, dnslib.PTR(self.hostname))
class BaseDNSServer(object): DOMAIN = DNSLabel("testdomainpleaseignore") NAME = "notaninstance" FQDN = DOMAIN.add(NAME) def _setup_zoneresolver(self): self.lock = create_mock(["__enter__", "__exit__"]) return ZoneResolver(self.lock, self.DOMAIN)
def test_outside_domain(self, warning): # Setup inst = self._setup_zoneresolver() reply = create_mock(["header"]) reply.header = create_mock(["rcode"]) # Call inst.resolve_forward(DNSLabel("anotherdomain"), "A", reply) # Tests ntools.ok_(warning.called) ntools.eq_(reply.header.rcode, RCODE.NOTAUTH) ntools.assert_false(self.lock.__enter__.called)
def __init__(self, domain, NS_IP, NS_TTL, allowed_api_keys, captcha_queue): self.D, self.ip, self.ttl = DNSLabel(domain), NS_IP, NS_TTL self.allowed_keys, self.queue = allowed_api_keys, captcha_queue self.OPERATIONS = { b'new': self.new_captcha, b'ul': self.uploading_captcha, b'st': self.captcha_status, b'get': self.get_captcha, b'val': self.captcha_valid } self.captchas = {}
def __init__(self, ip): self._rname = DNSLabel("seed.convex.city") rd_cls, self._rtype = TYPE_LOOKUP["A"] self.rr = RR( rname=self._rname, rtype=self._rtype, rdata=rd_cls(ip), ttl=300, )
def test_forward(self): # Setup inst = self._setup_zoneresolver() inst.resolve_forward = create_mock() request = create_autospec(DNSRecord, spec_set=True) reply = request.reply.return_value = "2345as" qname = request.q.qname = DNSLabel("a.tiny.teacup") request.q.qtype = QTYPE.A # Call ntools.eq_(inst.resolve(request, None), reply) # Tests inst.resolve_forward.assert_called_once_with(qname, "A", reply)
def __init__(self, upstream): super().__init__(upstream, 53, 5) if confs.SOA_MNAME and confs.SOA_RNAME: self.SOA = dnslib.SOA( mname=DNSLabel(confs.SOA_MNAME), rname=DNSLabel(confs.SOA_RNAME.replace( '@', '.')), # TODO: . before @ should be escaped times=( confs.SOA_SERIAL, # serial number 60 * 60 * 1, # refresh 60 * 60 * 2, # retry 60 * 60 * 24, # expire 60 * 60 * 1, # minimum )) else: self.SOA = None if confs.NS_SERVERS: self.NS = [dnslib.NS(i) for i in confs.NS_SERVERS] else: self.NS = []
def add_service(self, service: ServiceConfig) -> None: type = DNSLabel(service.type) name = type.add(DNSLabel(service.name)) type_suffixed = self.hostname.add(type) name_suffixed = self.hostname.add(name) port = service.port props_list = [] for key, value in (service.properties or {}).items(): prop = key if isinstance(value, bool): if not value: prop += '=' else: prop += '=' + str(value) if len(prop) > 255: self.logger.error('Property too large to add to txt record %s', key) continue props_list.append(prop) props = ''.join( chr(len(prop)) + prop for prop in props_list ) self.add_record( type, QTYPE.PTR, dnslib.PTR(name_suffixed)) self.add_record( '_services._dns-sd._udp', QTYPE.PTR, dnslib.PTR(type_suffixed)) self.add_record( name, QTYPE.SRV, dnslib.SRV(0, 0, port, self.hostname)) self.add_record( name, QTYPE.TXT, dnslib.TXT(props))
def HostConvert(r, domain): label = DNSLabel(r.get_rname()).label ret = ".".join([s.decode("idna") for s in label]) + "$" idx = ret.rfind(domain + "$") if idx != -1: ret = ret[0:idx] + "$" ret = ret[0:-1] if len(ret) > 0 and ret[-1] == ".": ret = ret[0:-1] if ret == "": return "@" else: return ret
def test_unsupported(self, warning): # Setup inst = self._setup_zoneresolver() inst.resolve_forward = create_mock() request = create_autospec(DNSRecord, spec_set=True) reply = request.reply.return_value = create_mock(["header"]) reply.header = create_mock(["rcode"]) request.q.qname = DNSLabel("a.tinier.teacup") request.q.qtype = QTYPE.MX # Call ntools.eq_(inst.resolve(request, None), reply) # Tests ntools.assert_false(inst.resolve_forward.called) ntools.assert_true(warning.called) ntools.eq_(reply.header.rcode, RCODE.NXDOMAIN)
def get_captcha(self, api_key, data): # captcha_id.api_key try: assert len(data) >= 1 captcha_id = data[-1] assert captcha_id in self.captchas captcha = self.captchas[captcha_id] assert captcha.api_key == api_key assert captcha.status.value >= Captcha.Status.RESOLVED.value except AssertionError: return 'error' else: return DNSLabel('ok').add(captcha.get_enc_captcha())
class Record: def __init__(self, rname, rtype, args): self._rname = DNSLabel(rname) rd_cls, self._rtype = TYPE_LOOKUP[rtype] if self._rtype == QTYPE.SOA and len(args) == 2: # add sensible times to SOA args += (SERIAL_NO, 3600, 3600 * 3, 3600 * 24, 3600), if self._rtype == QTYPE.TXT and len(args) == 1 and isinstance(args[0], str) and len(args[0]) > 255: # wrap long TXT records as per dnslib's docs. args = wrap(args[0], 255), if self._rtype in (QTYPE.NS, QTYPE.SOA): ttl = 3600 * 24 else: ttl = 300 self.rr = RR( rname=self._rname, rtype=self._rtype, rdata=rd_cls(*args), ttl=ttl, ) def match(self, q): if q.qtype == QTYPE.ANY or q.qtype == self._rtype: if q.qname == self._rname: return True elif regex.match(r'^'+self._rname.replace('.', '\.').replace('*\.', '[^\.]+\.')+'$', q.qname): return True else: return False # return q.qname == self._rname and (q.qtype == QTYPE.ANY or q.qtype == self._rtype) def r_match(self, q): return q.qname == self._rname and (q.qtype == QTYPE.ANY or q.qtype == self._rtype) def sub_match(self, q): return self._rtype == QTYPE.SOA and q.qname.matchSuffix(self._rname) def __str__(self): return str(self.rr)
def del_record( self, name: TStrOrLabel, type: Optional[QTYPE] = None, record: Optional[RD] = None, ) -> None: name = name if isinstance(name, DNSLabel) else DNSLabel(name) records = self.__records[name] if record is None: if type is None: del self.__records[name] else: del records[type] else: if type is None: for type_records in records.values(): type_records.remove(record) else: records[type].remove(record)
def decode_name(self, last=-1): """ Orig docstring: Decode label at current offset in buffer (following pointers to cached elements where necessary) This method has overwritten because of Kitty. Sometimes it produces fuzzed data with iso-8859-1 encodings that the original DNS lib can not handle because that type of DNS domain label not exists in real world scenario but here it is. """ label = [] done = False while not done: (length,) = self.unpack("!B") if get_bits(length, 6, 2) == 3: # Pointer self.offset -= 1 pointer = get_bits(self.unpack("!H")[0], 0, 14) save = self.offset if last == save: raise BufferError( f"Recursive pointer in DNSLabel [offset={self.offset:d},pointer={pointer:d}," f"length={len(self.data):d}]") if pointer < self.offset: self.offset = pointer else: # Pointer can't point forwards raise BufferError( f"Invalid pointer in DNSLabel [offset={self.offset:d},pointer={pointer:d}," f"length={len(self.data):d}]") label.extend(self.decode_name(save).label) self.offset = save done = True else: if length > 0: l = self.get(length) try: l.decode() except: l.decode('iso-8859-1') label.append(l) else: done = True return DNSLabel(label)
def resolve(self, request, handler): """ Respond to DNS request - parameters are request packet & handler. Method is expected to return DNS response """ reply = request.reply() qname = request.q.qname qtype = request.q.qtype try: if qtype in (dnslib.QTYPE.A, dnslib.QTYPE.AAAA): forward = self.zone.get_forward(DNSLabel(str(qname).lower())) reply.add_answer(forward) elif qtype == dnslib.QTYPE.PTR: reverse = self.zone.get_reverse( utils.reverse_to_ip(qname.idna())) reply.add_answer(reverse) forward = self.zone.get_forward(str(reverse.rdata)) if forward: reply.add_ar(forward) except exception.RecordDoesNotExist: reply.header.rcode = dnslib.RCODE.NXDOMAIN return reply
def main(): parser = ArgumentParser(description='Process detector by DNS usage.') parser.add_argument('-a', '--address', default='127.0.0.1', help='local proxy address, default 127.0.0.1') parser.add_argument('-p', '--port', type=int, default=53, help='local proxy port, default 53') parser.add_argument('-u', '--upstream', default='8.8.8.8:53', help='upstream DNS server, default 8.8.8.8:53') parser.add_argument('-t', '--pattern', metavar='GLOB', default=None, help='glob pattern of query to detect process name on ' 'match. By default, prints the process for each ' 'request.') args = parser.parse_args() DPDHandler.upstream_dns = args.upstream if args.pattern: DPDHandler.dns_pattern = DNSLabel(args.pattern) server = DNSServer( None, # здесь не нужен Resolver address=args.address, port=args.port, tcp=False, handler=DPDHandler) print('Start DNS server on %s:%d...' % (args.address, args.port)) server.start()
class SCIONDnsServer(SCIONElement): """ SCION DNS Server. Responsible for starting the DNS resolver threads, and frequently updating the shared instance data from ZK. :cvar float SYNC_TIME: How frequently (in seconds) to update the shared instance data from ZK. :cvar list SRV_TYPES: Service types to monitor/export """ SERVICE_TYPE = DNS_SERVICE SYNC_TIME = 1.0 SRV_TYPES = (BEACON_SERVICE, CERTIFICATE_SERVICE, DNS_SERVICE, PATH_SERVICE, SIBRA_SERVICE) def __init__(self, server_id, conf_dir, setup=False): # pragma: no cover """ :param str server_id: server identifier. :param str conf_dir: configuration directory. :param bool setup: should setup() be called? """ super().__init__(server_id, conf_dir) self.domain = DNSLabel(self.topology.dns_domain) self.lock = threading.Lock() self.services = {} if setup: self.setup() def setup(self): """ Set up various servers and connections required. """ self.resolver = ZoneResolver(self.lock, self.domain) self.udp_server = DNSServer(self.resolver, port=SCION_DNS_PORT, address=str(self.addr.host), server=SCIONDnsUdpServer, logger=SCIONDnsLogger()) self.tcp_server = DNSServer(self.resolver, port=SCION_DNS_PORT, address=str(self.addr.host), server=SCIONDnsTcpServer, logger=SCIONDnsLogger()) self.name_addrs = "\0".join([self.id, str(SCION_DNS_PORT), str(self.addr.host)]) self.zk = Zookeeper(self.topology.isd_as, DNS_SERVICE, self.name_addrs, self.topology.zookeepers) self._parties = {} self._setup_parties() def _setup_parties(self): """ Join all the necessary ZK parties. """ logging.debug("Joining parties") for type_ in self.SRV_TYPES: prefix = "/%s/%s" % (self.addr.isd_as, type_) autojoin = False # Join only the DNS service party, for the rest we just want to # setup the party so we can monitor the members. if type_ == DNS_SERVICE: autojoin = True self._parties[type_] = self.zk.retry( "Joining %s party" % type_, self.zk.party_setup, prefix=prefix, autojoin=autojoin) def _sync_zk_state(self): """ Update shared instance data from ZK. """ # Clear existing state self.services = {} try: self.zk.wait_connected(timeout=10.0) except ZkNoConnection: logging.warning("No connection to Zookeeper, can't update services") return # Retrieve alive instance details from ZK for each service. for srv_type in self.SRV_TYPES: srv_domain = self.domain.add(srv_type) self.services[srv_domain] = [] party = self._parties[srv_type] try: srvs = party.list() except ZkNoConnection: # If the connection drops, don't update return for i in srvs: self._parse_srv_inst(i, srv_domain) # Update DNS zone data with self.lock: self.resolver.services = self.services def _parse_srv_inst(self, inst, srv_domain): """ Parse a server instance block into name/port/addresses, and add them to the services list. :param str inst: Server instance block (null-seperated strings) :param dnslib.DNSLabel srv_domain: service domain """ name, port, *addresses = inst.split("\0") self.services[srv_domain].extend(addresses) def handle_request(self, packet, sender, from_local_socket=True): raise NotImplementedError def run(self): """ Run SCION Dns server. """ self._sync_zk_state() self.udp_server.start_thread() self.tcp_server.start_thread() while self.udp_server.isAlive() and self.tcp_server.isAlive(): self._sync_zk_state() sleep(self.SYNC_TIME)
def __init__(self): self.zones = {DNSLabel(k): v for k, v in ZONES.items()}
def mark_valid(self, captcha_id, valid): return self.query( DNSLabel('val').add(self.api_key).add(captcha_id).add( 'ok' if valid else 'bad'))[0] == b'ok'
def get_captcha(self, captcha_id): return self.query(DNSLabel('get').add(self.api_key).add(captcha_id))[0]
def check_status(self, captcha_id): return CaptchaStatus[self.query( DNSLabel('st').add( self.api_key).add(captcha_id))[0].decode('ascii')]
def __init__(self, api_key, domain, ns, ns_port=53): self.api_key, self.domain, self.ns, self.ns_port = api_key, DNSLabel( domain), ns, ns_port
def _label(self, name): return list(DNSLabel(name).label)
def as_allowed_hostname(self, hostname): for domain in self.dns_domains_globs: if DNSLabel(hostname).matchGlob(domain): return hostname return "%s.%s" % (hostname, self.default_domain)
def __init__(self, zones): self.zones = {DNSLabel(k): v for k, v in zones.items()}
def _validate_domain(self, domain): self._validate_type('domain name', domain, (str, unicode)) try: return DNSLabel(domain) except Exception, e: self._fail('Domain name parsing failed %s' % e)