Beispiel #1
0
    def handle(self):
        try:
            data = self.get_data()
            query = dnslib.DNSRecord.parse(data)
            reply = query.reply()
            answers = []

            if reply.get_q().qtype == dnslib.QTYPE.TXT:
                try:
                    challenge_files = os.listdir(BaseDNSRequestHandler.challenges_path)
                    for challenge_file in challenge_files:
                        if challenge_file.startswith(str(reply.get_q().qname).strip('.')):
                            with open(os.path.join(BaseDNSRequestHandler.challenges_path,
                                                   challenge_file), 'r') as challenge_data:
                                answers.append(dnslib.RR(rname=reply.get_q().qname,
                                                         rtype=reply.get_q().qtype,
                                                         ttl=60,
                                                         rdata=dnslib.TXT(challenge_data.read().strip())))
                except OSError:
                    pass
            else:
                answers.append(dnslib.RR(rname=reply.get_q().qname,
                                         ttl=60,
                                         rdata=dnslib.A(LISTEN_ADDRESS)))

            for answer in answers:
                reply.add_answer(answer)
            self.send_data(reply.pack())
        except Exception as e:
            print(e)
Beispiel #2
0
    def try_tox3_resolve(self, reply, name, domain, req_name):
        if not name.startswith("_"):
            return None

        encrypted = name.replace(".", "")[1:]
        try:
            b = notsecure32_decode(encrypted)
            nonce = b[:4] + (b"\0" * 20)
            ck = b[4:36]
            payload = b[36:]
            dec_name = self.cryptocore.dsrep_decode_name(ck, nonce, payload)
        except Exception:
            return None

        try:
            dec_name = dec_name.decode("utf8")
        except UnicodeDecodeError:
            return None

        query = self.db.query(User).filter(
            and_(User.name == name, User.domain == domain))
        user = query.one()

        if not user:
            return None

        msg = binascii.unhexlify(user.toxid)
        nonce_reply = b[:4] + b"\x01" + (b"\0" * 19)
        ct = self.cryptocore.dsrec_encrypt_key(ck, nonce_reply, msg)
        key_part = notsecure32_encode(ct).decode("ascii")

        base = "v=tox3;id={0}".format(key_part).encode("utf8")
        reply.add_answer(dnslib.RR(req_name, 16, ttl=0,
                                   rdata=dnslib.TXT(base)))
        return reply
Beispiel #3
0
    def handle(self): 
        global count       
        count=count+1 #序号
        request_data = self.request[0]
        client_socket = self.request[1]
        #内部搜索
        d=DNSRecord.parse(request_data)
        qname=str(d.q.qname)
        qid=d.header.id
        search=cache.get(qname)
        #print(qname)
        if search:
            ret=d.reply()
            # 不良网站
            if search=="0.0.0.0":
                ret.add_answer(dnslib.RR(qname,QTYPE.TXT,rdata=dnslib.TXT(warning)))
            else:
                ret.add_answer(dnslib.RR(qname,rdata=dnslib.A(search)))
            ret.header.id=qid
            if debug_print:
                print(time.asctime( time.localtime(time.time())),"  ",
                count,"  ",self.client_address,qname)
            elif debug_print2:
                print("\n\n\n")
                print("*******Request Data***********")
                print(d)
                print("********Client Address********")
                print(self.client_address)
                print("********Search Name***********")
                print(qname)
                print("********Search IP*************")
                print(search)

            client_socket.sendto(bytes(ret.pack()), self.client_address)
        else:
            # 将请求转发到 外部 DNS
            redirect_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            redirect_socket.sendto(request_data, (DNS_server, 53))
            response_data, address = redirect_socket.recvfrom(8192)

            if debug_print:
                print(time.asctime( time.localtime(time.time())),"  ",
                count,"  ",self.client_address,qname)
            elif debug_print2:
                print("\n\n\n")
                print("*******Request Data***********")
                print(d)
                print("********Client Address********")
                print(self.client_address)
                print("********Search Name***********")
                print(qname)
                print("********Search IP*************")
                print(DNSRecord.parse(response_data))
            # 将外部响应响应给客户
            client_socket.sendto(response_data, self.client_address)
Beispiel #4
0
    def resolve(self, request, handler):
        reply = request.reply()
        qname = request.q.qname

        # Refuse queries thaat are not for our domain
        if tuple(map(str.lower, map(qname._decode, qname.label[-2:]))) != \
            tuple(map(str.lower, map(self.domain._decode, self.domain.label[-2:]))):
            reply.header.rcode = dnslib.RCODE.REFUSED
            return reply

        # Answer questions about the root domain name
        # TODO(supersat): We don't need to implement this, right?
        if len(qname.label) <= 3:
            if request.q.qtype == dnslib.QTYPE.A:
                reply.add_answer(
                    dnslib.RR(qname,
                              dnslib.QTYPE.A,
                              ttl=300,
                              rdata=self.server_ip))
            return reply

        subdomain = qname._decode(qname.label[1]).lower()
        hostname = qname._decode(qname.label[0]).lower()
        if BASE36_SHA256_HASH.match(subdomain) and len(qname.label) == 4:
            if hostname == '_acme-challenge' and \
                (request.q.qtype == dnslib.QTYPE.TXT or \
                request.q.qtype == dnslib.QTYPE.ANY):
                txt = self.redis.get('acme-dns-01-chal:{}'.format(subdomain))
                if txt:
                    reply.add_answer(
                        dnslib.RR(qname,
                                  dnslib.QTYPE.TXT,
                                  ttl=300,
                                  rdata=dnslib.TXT(txt)))
                else:
                    reply.header.rcode = dnslib.RCODE.NXDOMAIN
            elif IPV4_REGEX.match(hostname) and \
                (request.q.qtype == dnslib.QTYPE.A or \
                request.q.qtype == dnslib.QTYPE.ANY):
                try:
                    ip = tuple(map(int, hostname.split('-')))
                    reply.add_answer(
                        dnslib.RR(qname,
                                  dnslib.QTYPE.A,
                                  ttl=300,
                                  rdata=dnslib.A(ip)))
                except:
                    reply.header.rcode = dnslib.RCODE.NXDOMAIN
            else:
                reply.header.rcode = dnslib.RCODE.NXDOMAIN
            return reply

        reply.header.rcode = dnslib.RCODE.NXDOMAIN
        return reply
Beispiel #5
0
    def try_tox1_resolve(self, reply, name, domain, req_name):
        query = self.db.query(User).filter(
            and_(User.name == name, User.domain == domain))

        user = query.one()

        if not user:
            reply.header.rcode = dnslib.RCODE.NXDOMAIN
            return reply
        else:
            rec = "v=tox1;id={0}".format(user.toxid).encode("utf8")
            reply.add_answer(
                dnslib.RR(req_name, 16, ttl=self.ttl, rdata=dnslib.TXT(rec)))
            return reply
Beispiel #6
0
  def resolve(self, req, handler):
    reply = req.reply()
    try:
      obj = TheQueryResponsePair(req, reply)
      obj.evaluate()
    except Exception as ex:   
      reply.add_ar(
        dnslib.RR(
          rname = '' + str(ex.__class__.__name__) + ".exception",
          rclass = dnslib.CLASS.IN,
          rtype = dnslib.QTYPE.TXT,
          ttl=10,
          rdata=dnslib.TXT(str(ex))
        )
      )

    return reply
Beispiel #7
0
def make_jianbing(query):
    response = query.reply()
    response.header.ra = 0
    word = query.q.qname.label[0]
    desc = stardict.check(word)
    desc = [desc] if desc else notfound(word)
    # force change rtype to TXT
    response.rr = []

    for txt in desc:
        # if len(txt) > 255
        for part in split_len(txt, 255):
            response.add_answer(
                dnslib.RR(query.q.qname,
                          dnslib.QTYPE.TXT,
                          rdata=dnslib.TXT(part),
                          ttl=5))

    # no Recursion Available
    return response
Beispiel #8
0
def _txt_search(record, rr_list, auth_list, addi_list):
    """
    Searches and adds any TXT records for the domain.
    :param record: Overall record for domain
    :param rr_list: Current record list for the domain
    :param auth_list: Authority list for the domain
    :param addi_list: Additional list for the domain
    """
    try:
        txt_record = record["TXT"]
        ttl = int(txt_record["ttl"])
        for txt in txt_record["value"]:
            rr_list.append(dnslib.RR(rname = record["domain"],
                                     rtype = dnslib.QTYPE.TXT,
                                     rdata = dnslib.TXT(txt),
                                     ttl   = ttl))
            _add_authority(record["domain"], auth_list)
            _add_additional(addi_list)
    except:
        pass
Beispiel #9
0
    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))
Beispiel #10
0
    def resolve(self, request, handler):
        question = request.get_q()
        req_name = str(question.get_qname())
        # TXT = 16
        reply = request.reply()

        pivot = req_name.rfind("_tox.")
        if pivot == -1:
            reply.header.rcode = dnslib.RCODE.NXDOMAIN
            return reply

        name = req_name[:pivot].rstrip(".")
        suffix = req_name[pivot:]
        domain = suffix[5:].rstrip(".")

        if question.qtype == 16:
            if not name and domain in self.workable_domains:
                data = self.cryptocore.public_key.encode("ascii")

                reply.add_answer(
                    dnslib.RR(req_name, 16, ttl=0, rdata=dnslib.TXT(data)))
                return reply

            first_try = self.try_tox3_resolve(reply, name, domain, req_name)
            if not first_try:
                return self.try_tox1_resolve(reply, name, domain, req_name)
            else:
                return first_try
        elif question.qtype == 2:
            reply.add_answer(
                dnslib.RR(req_name, 2, ttl=86400, rdata=dnslib.NS(self.home)))
            return reply
        else:
            reply.header.rcode = dnslib.RCODE.NXDOMAIN
            return reply
        return reply
Beispiel #11
0
    def handle(self):

        request = dnslib.DNSRecord.parse(self.request[0])
        """ see http://www.networksorcery.com/enp/protocol/dns.htm 
        QR flag is 1 (0 = Query, 1 = Response)
        AA flag is 1 (0 = Not authoritative, 1 = Is authoritative)
        
        """
        reply = dnslib.DNSRecord(dnslib.DNSHeader(id=request.header.id, qr=1, aa=1),
                                 q=request.q)

        reply.add_answer(dnslib.RR(
            rname=request.q.qname,
            rtype=1,
            rdata=dnslib.A(self.DNS_LOOKUP),

        ))

        request_name = str(request.q.qname)
        if request_name in self.TXT_LOOKUP:
            for answer in self.TXT_LOOKUP[request_name]:
                reply.add_answer(dnslib.RR(
                    request.q.qname,
                    ttl=300,
                    rtype=dnslib.QTYPE.TXT,
                    rdata=dnslib.TXT(answer.encode("utf-8"))
                ))
                print('dnsserver served txt record for {}: {}'.format(request_name, answer))
        else:
            print('dnsserver didn\'t find txt record for {}'.format(request_name))
            # print("{} not in memory".format(request_name))
            # print("memory", self.TXT_LOOKUP)
            # print(type(request_name))
            pass

        self.send_data(reply.pack())
Beispiel #12
0
    def resolve(self, request, handler):
        question = request.get_q()
        req_name = str(question.get_qname())
        # TXT = 16
        reply = request.reply()
        suffix = "._tox.{0}".format(self.ireg)

        if question.qtype != 16 and not req_name.endswith(self.ireg):
            reply.header.rcode = dnslib.RCODE.NXDOMAIN
            return reply

        if question.qtype == 16:
            if req_name == suffix[1:]:
                reply.add_answer(dnslib.RR(req_name, 16, ttl=0,
                    rdata=dnslib.TXT(self.cryptocore.public_key.encode("ascii"))))
                return reply
            if not req_name.endswith(suffix):
                reply.header.rcode = dnslib.RCODE.NXDOMAIN
                return reply
            user_name = req_name[:req_name.rfind(suffix)]
            if len(user_name) > NAME_LIMIT_HARD and user_name[0] == "_":
                encrypted = user_name.replace(".", "")[1:]
                try:
                    b = notsecure32_decode(encrypted)
                    nonce = b[:4] + (b"\0" * 20)
                    ck = b[4:36]
                    payload = b[36:]
                    name = self.cryptocore.dsrep_decode_name(ck, nonce, payload)
                except Exception:
                    print("error >_<")
                    reply.header.rcode = dnslib.RCODE.NXDOMAIN
                    return reply

                rec = self.store.get(name.decode("utf8"))
                if not rec:
                    reply.header.rcode = dnslib.RCODE.NXDOMAIN
                    return reply
                base = b"v=tox3;id="
                if rec.pin:
                    r_payload = "{0}{1}{2}".format(rec.public_key, rec.pin,
                                                   rec.checksum)
                else:
                    r_payload = "{0}00000000{1}".format(rec.public_key,
                                                        rec.checksum)
                msg = binascii.unhexlify(r_payload)
                nonce_reply = b[:4] + b"\x01" + (b"\0" * 19)
                ct = self.cryptocore.dsrec_encrypt_key(ck, nonce_reply, msg)

                key_part = notsecure32_encode(ct)
                reply.add_answer(dnslib.RR(req_name, 16, ttl=0,
                                 rdata=dnslib.TXT(b"".join((base, key_part)))))
                return reply
            else:
                rec = self.store.get(user_name)
                if not rec:
                    reply.header.rcode = dnslib.RCODE.NXDOMAIN
                    return reply
                else:
                    reply.add_answer(dnslib.RR(req_name, 16, ttl=0,
                                               rdata=dnslib.TXT(rec.record(0)
                                                            .encode("utf8"))))
                    return reply
        elif question.qtype == 6:
            self.update_soa()
            reply.add_answer(self.soa)
            return reply
        elif question.qtype == 2:
            for name in self.authority_list:
                reply.add_answer(dnslib.RR(req_name, 2, ttl=86400,
                                           rdata=dnslib.NS(name.encode("utf8"))
                                           ))
            return reply
        elif question.qtype == 1 and self.home_addresses:
            for ip in self.home_addresses:
                reply.add_answer(dnslib.RR(req_name, 1, ttl=3600,
                                           rdata=dnslib.A(ip)))
        elif question.qtype == 28 and self.home_addresses_6:
            for ip in self.home_addresses_6:
                reply.add_answer(dnslib.RR(req_name, 28, ttl=3600,
                                           rdata=dnslib.AAAA(ip)))
        else:
            reply.header.rcode = dnslib.RCODE.NXDOMAIN
            return reply
        return reply
Beispiel #13
0
    def processRequest(self, request):

        reply = dnslib.DNSRecord(dnslib.DNSHeader(id=request.header.id,
                                                  qr=1,
                                                  aa=1,
                                                  ra=1),
                                 q=request.q)

        qname = request.q.qname
        qn = str(qname)
        qtype = request.q.qtype
        qt = dnslib.QTYPE[qtype]
        if qn == self.D or qn.endswith('.' + self.D):

            for name, rrs in self.records.items():
                if name == qn:
                    for rdata in rrs:
                        rqt = rdata.__class__.__name__
                        if qt in ['*', rqt]:
                            reply.add_answer(
                                dnslib.RR(rname=qname,
                                          rtype=getattr(dnslib.QTYPE, rqt),
                                          rclass=1,
                                          ttl=self.TTL,
                                          rdata=rdata))

            for rdata in self.ns_records:
                reply.add_ar(
                    dnslib.RR(rname=self.D,
                              rtype=dnslib.QTYPE.NS,
                              rclass=1,
                              ttl=self.TTL,
                              rdata=rdata))

            reply.add_auth(
                dnslib.RR(rname=self.D,
                          rtype=dnslib.QTYPE.SOA,
                          rclass=1,
                          ttl=self.TTL,
                          rdata=self.soa_record))

        for question in request.questions:

            if (question.qtype == dnslib.QTYPE.TXT):
                #only process TXT record requests
                content = str(question.qname)[:-1]
                if content.endswith(self.D[:-1]):
                    content = content[:-len(self.D[:-1]) - 1]

                key = content[:4]

                if key in self.fIP:
                    content = str(content[4:])
                    self.fIP[key][3] += content
                    self.fIP[key][1] -= len(content)
                    #print(key, content,len(content),self.fIP[key][1] )

                    #print("Left: "+str(self.fIP[sIP][1]))
                    self.progressBar(
                        self.fIP[key][1], self.fIP[key][4], "Receiving '" +
                        self.fIP[key][0] + "' with index " + key)
                    reply.add_answer(
                        dnslib.RR(rname=qname,
                                  rtype=question.qtype,
                                  rclass=1,
                                  ttl=self.TTL,
                                  rdata=dnslib.TXT(content)))
                    #reply.add_answer(dnslib.RR(rname=qname, rtype=question.qtype, rclass=1, ttl=self.TTL, rdata=dnslib.TXT("OK")))
                    if (self.fIP[key][1] == 0):
                        #we have received the entire file. Time to write it.
                        content_decoded = base64.standard_b64decode(
                            self.fIP[key][3])
                        with open(self.fIP[key][0], 'wb') as newfile:
                            newfile.write(content_decoded)

                        hashedWord = md5(content_decoded).hexdigest()
                        if (self.fIP[key][2] == hashedWord):
                            print("\nFile successfully received")
                            reply.add_answer(
                                dnslib.RR(rname=qname,
                                          rtype=question.qtype,
                                          rclass=1,
                                          ttl=self.TTL,
                                          rdata=dnslib.TXT("OK")))

                        else:
                            print("\nFile received but failed hash:")
                            reply.add_answer(
                                dnslib.RR(rname=qname,
                                          rtype=question.qtype,
                                          rclass=1,
                                          ttl=self.TTL,
                                          rdata=dnslib.TXT("FAIL HASH")))

                        del self.fIP[key]

                else:
                    # new connection. we expect a file name
                    print("New file:", content)
                    parts = content.split("|")
                    if (len(parts) == 3):
                        #we have valid request
                        print("new file upload: ", content)
                        self.fIP[parts[2][:4]] = [
                            os.path.basename(parts[0]),
                            int(parts[1]), parts[2], "",
                            int(parts[1])
                        ]
                    reply.add_answer(
                        dnslib.RR(rname=qname,
                                  rtype=question.qtype,
                                  rclass=1,
                                  ttl=self.TTL,
                                  rdata=dnslib.TXT("OK")))

            else:
                reply.add_answer(
                    dnslib.RR(rname=qname,
                              rtype=question.qtype,
                              rclass=1,
                              ttl=self.TTL,
                              rdata=dnslib.A(self.IP)))

            #print("responding:",reply)
            return reply.pack()
 def resolve(self, request, handler):
     reply = request.reply()
     if sys.version_info[0] > 2:
         labelsNormalized = [
             item.decode(errors='replace').lower().strip()
             for item in request.q.qname.label
         ]
     else:
         labelsNormalized = [
             item.decode().lower().strip() for item in request.q.qname.label
         ]
     type, label, position = MatchLabel(self.labels, labelsNormalized)
     replyNXDOMAIN = False
     rcode = None
     if type == None:
         replyNXDOMAIN = True
     elif type == TYPE_PAYLOAD:
         if request.q.qtype != dnslib.QTYPE.TXT and request.q.qtype != dnslib.QTYPE.NULL:
             replyNXDOMAIN = True
         elif not (len(labelsNormalized) >= 3
                   and labelsNormalized[0] in self.payloads.keys()):
             replyNXDOMAIN = True
         else:
             label = labelsNormalized[0]
             try:
                 index = 0
                 encodingIndex = 1
                 index = int(labelsNormalized[1])
                 encodingIndex = 2
             except:
                 pass
             encoding = self.payloads[label][PAYLOAD][ENCODING]
             if encoding == ENCODING_DYNAMIC:
                 if labelsNormalized[encodingIndex] in [
                         ENCODING_BASE64, ENCODING_HEX
                 ]:
                     encoding = labelsNormalized[encodingIndex]
                 else:
                     encoding = ENCODING_NONE
             if not encoding in self.payloads[label][DATA]:
                 if encoding == ENCODING_BASE64:
                     self.payloads[label][DATA][
                         ENCODING_BASE64] = binascii.b2a_base64(
                             self.payloads[label][DATA]
                             [ENCODING_NONE]).strip()
                 elif encoding == ENCODING_HEX:
                     self.payloads[label][DATA][
                         ENCODING_HEX] = binascii.b2a_hex(
                             self.payloads[label][DATA]
                             [ENCODING_NONE]).strip()
             data = self.payloads[label][DATA][encoding]
             if index > len(data) / (self.maxSizeString *
                                     self.maxCountStrings):
                 replyNXDOMAIN = True
             else:
                 if handler.protocol == 'tcp' or not self.args.tcp:
                     dnsStrings = [
                         data[(index * self.maxCountStrings + iter) *
                              self.maxSizeString:
                              (index * self.maxCountStrings + iter + 1) *
                              self.maxSizeString]
                         for iter in range(self.maxCountStrings)
                     ]
                     dnsStrings = [
                         dnsString for dnsString in dnsStrings
                         if len(dnsString) != 0
                     ]
                     if request.q.qtype == dnslib.QTYPE.TXT:
                         reply.add_answer(
                             dnslib.RR(request.q.qname,
                                       dnslib.QTYPE.TXT,
                                       ttl=self.ttl,
                                       rdata=dnslib.TXT(dnsStrings)))
                     elif request.q.qtype == dnslib.QTYPE.NULL:
                         reply.add_answer(
                             dnslib.RR(request.q.qname,
                                       dnslib.QTYPE.NULL,
                                       ttl=self.ttl,
                                       rdata=NULL(dnsStrings)))
                     else:
                         raise Exception('DNS payload: type unknown')
                 else:
                     reply.header.tc = True
     elif type == TYPE_EXFILTRATION:
         if request.q.qtype != dnslib.QTYPE.A:
             replyNXDOMAIN = True
         elif not (len(labelsNormalized) >= 4
                   and label in self.exfiltrations.keys()):
             replyNXDOMAIN = True
         else:
             hexdata = ''.join(labelsNormalized[0:position])
             try:
                 data = ''
                 data = binascii.a2b_hex(hexdata)
             except:
                 pass
             result = Unpack('>HI', data)
             if result != []:
                 filenumber, filesize, data = result
                 filename = '%s-%05d' % (label, filenumber)
                 if not filename in self.exfiltrations[label][FILES]:
                     try:
                         with open(filename, 'wb') as filehandle:
                             filehandle.write(b'\x00' * filesize)
                         filehandle = open(filename, 'r+b')
                         filemmap = mmap.mmap(filehandle.fileno(), filesize)
                         self.exfiltrations[label][FILES][filename] = {
                             FILEHANDLE: filehandle,
                             FILEMMAP: filemmap
                         }
                     except:
                         pass
                 if filesize == 0:
                     if filename in self.exfiltrations[label][FILES]:
                         try:
                             self.exfiltrations[label][FILES][filename][
                                 FILEMMAP].close()
                             self.exfiltrations[label][FILES][filename][
                                 FILEMMAP] = None
                         except:
                             pass
                         try:
                             self.exfiltrations[label][FILES][filename][
                                 FILEHANDLE].close()
                             self.exfiltrations[label][FILES][filename][
                                 FILEHANDLE] = None
                         except:
                             pass
                 else:
                     result = Unpack('>IB', data)
                     if result != []:
                         fileposition, chuncksize, data = result
                         if chuncksize == len(
                                 data
                         ) and filename in self.exfiltrations[label][FILES]:
                             try:
                                 self.exfiltrations[label][FILES][filename][
                                     FILEMMAP][fileposition:fileposition +
                                               chuncksize] = data
                             except:
                                 pass
             if self.exfiltrations[label][ANSWER] == '':
                 replyNXDOMAIN = True
             else:
                 qname = request.q.qname
                 for rr in dnslib.RR.fromZone(
                         self.exfiltrations[label][ANSWER]):
                     a = copy.copy(rr)
                     a.rname = qname
                     reply.add_answer(a)
     elif type == TYPE_TRACK:
         if LOGGING in self.tracks[label]:
             with open(self.tracks[label][LOGGING], 'a') as f:
                 print('%s %s:%d %d %s' %
                       (FormatTimeUTC(), handler.client_address[0],
                        handler.client_address[1], position,
                        '.'.join(labelsNormalized).encode('utf8').decode()),
                       file=f)
         if request.q.qtype != dnslib.QTYPE.A:  #a# handle AAAA too
             replyNXDOMAIN = True
         elif not label in self.tracks.keys():
             replyNXDOMAIN = True
         elif position == 0:
             replyNXDOMAIN = True
         else:
             if self.tracks[label][ANSWER] == '':
                 replyNXDOMAIN = True
             else:
                 qname = request.q.qname
                 for rr in dnslib.RR.fromZone(self.tracks[label][ANSWER]):
                     a = copy.copy(rr)
                     a.rname = qname
                     reply.add_answer(a)
     elif type == TYPE_RCODE:
         if position != 1:
             replyNXDOMAIN = True
         elif not label in self.rcodes.keys():
             replyNXDOMAIN = True
         else:
             try:
                 rcode = abs(ParseInteger(labelsNormalized[0])) % 0x100
             except:
                 replyNXDOMAIN = True
     elif type == TYPE_WILDCARD:
         if LOGGING in self.wildcards[label]:
             with open(self.wildcards[label][LOGGING], 'a') as f:
                 print('%s %s:%d %d %s' %
                       (FormatTimeUTC(), handler.client_address[0],
                        handler.client_address[1], position,
                        '.'.join(labelsNormalized).encode('utf8').decode()),
                       file=f)
         if request.q.qtype != dnslib.QTYPE.A:  #a# handle AAAA too
             replyNXDOMAIN = True
         elif not label in self.wildcards.keys():
             replyNXDOMAIN = True
         elif position == 0:
             replyNXDOMAIN = True
         else:
             qname = request.q.qname
             zoneWildcard = ParseWildcardRequest(
                 labelsNormalized[:position])
             if zoneWildcard == None:
                 replyNXDOMAIN = True
             else:
                 for rr in dnslib.RR.fromZone(zoneWildcard):
                     a = copy.copy(rr)
                     a.rname = qname
                     reply.add_answer(a)
     elif type == TYPE_RESOLVE:
         if position != 0:
             replyNXDOMAIN = True
         else:
             if LOGGING in self.resolves[label]:
                 with open(self.resolves[label][LOGGING], 'a') as f:
                     print('%s %s:%d %d %s' %
                           (FormatTimeUTC(), handler.client_address[0],
                            handler.client_address[1], position, '.'.join(
                                labelsNormalized).encode('utf8').decode()),
                           file=f)
             if request.q.qtype == dnslib.QTYPE.A:
                 qname = request.q.qname
                 answer = self.resolves[label][ANSWER][self.resolves[label]
                                                       [INDEX]]
                 self.resolves[label][INDEX] = (
                     self.resolves[label][INDEX] + 1) % len(
                         self.resolves[label][ANSWER])
                 for rr in dnslib.RR.fromZone(answer):
                     a = copy.copy(rr)
                     a.rname = qname
                     reply.add_answer(a)
             else:
                 replyNXDOMAIN = True
     else:
         replyNXDOMAIN = True
     if replyNXDOMAIN:
         reply.header.rcode = dnslib.RCODE.NXDOMAIN
     if rcode != None:
         reply.header.rcode = rcode
     return reply
Beispiel #15
0
    def dns_resolve(self, query, tcp):
        """ 
            Resolves DNS request from one client at the time.
            Encodes its actual data and forms DNS response from it.
        """
        request = dns.DNSRecord.parse(query)
        reply = request.reply()
        
        domain = request.q.get_qname()
        qtype =  request.q.qtype
        data = exf.domain_decode(str(domain), base64.urlsafe_b64decode)
        
        # If encryption key is present - decode it for futher data decryption
        if (len(request.questions) > 1):
            enc_domain = str(request.questions[1].get_qname())
            enc_key = exf.domain_decode(enc_domain, base64.urlsafe_b64decode)
            # Descramble key
            enc_key = exf.scramble(enc_key, (4, 12), True)

            # Check if the key is scramble offset
            if len(enc_key) < 3:
                enc_key = tuple(enc_key) 
                data = exf.scramble(data, enc_key, True)
            # Or AES decryption key
            else:
                enc_key = enc_key.decode()
                data = exf.aes_decrypt(data, enc_key)
            # reply.add_question(request.questions[1])

        if DEBUG:
            print_with_time("***", f"DNS QTYPE is {qtype}")
            print_with_time("***", f"Original data length {len(data)} bytes")
            print_with_time("***", f"{data[:24]}...")

        data = base64.b64encode(data)

        core_domain = deepcopy(domain)
        # Get TLD domain from original object
        core_domain.label = domain.label[-2:]

        if (qtype == dns.QTYPE.A):
            data = exf.ip_encode(data, False)
        
        elif (qtype == dns.QTYPE.AAAA):
            data = exf.ip_encode(data, True)

        elif (qtype ==  dns.QTYPE.TXT):
            data = [dns.TXT(data)]
        
        elif (qtype == 10):     # NULL type
            data = [dns.RD(data)]
        
        else:
            data = exf.domain_encode(data, str(core_domain), base64.urlsafe_b64encode)
            if (qtype == dns.QTYPE.CNAME):
                data = [dns.CNAME(data)]
            elif (qtype ==  dns.QTYPE.MX):
                data = [dns.MX(data)]
            elif (qtype == dns.QTYPE.NS):
                data = [dns.NS(data)]

        for rd in data:
            reply.add_answer(dns.RR(str(domain), rtype=qtype, rdata=rd))
        
        raw_reply = reply.pack()
        # Truncate large (> 512 bytes) data for UDP payload
        if (len(raw_reply) > exf.MAX_DNS_LEN and not tcp):
            print_with_time("DNS", f"Response message is big! Truncate it...")
            reply.header.set_tc(1)
            raw_reply = reply.pack()[:exf.MAX_DNS_LEN]
        
        print_with_time("DNS", f"Sending back the request in size {len(raw_reply)} bytes\n")
        return raw_reply
Beispiel #16
0
def StartDNSListener(self):

    # Setup a UDP server listening on port UDP 53
    udps = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    udps.bind(('0.0.0.0', 53))

    try:
        numberOfChunks = 0
        chunkIndex = 0
        fileData = ''
        domainName = tasks.define_dns_domain.dnsDomainName
        tasks.send_command.send_cmd = "OK"

        while True:
            data, addr = udps.recvfrom(1024)
            request = dnslib.DNSRecord.parse(data)

            if request.q.qtype == 16:

                # Get the query qname
                qname = str(request.q.qname)

                if qname.startswith("checker."):
                    checkerDetails = qname.split(".")
                    # DNS Agent Isolation
                    if checkerDetails[
                            1] == tasks.agent_iso_class.unique_agent_name:
                        if len(tasks.send_command.send_cmd) > 255:
                            print("Task lengh is more than 255 characters.")
                            print(
                                "Functionality added in TODO list for future release."
                            )
                            tasks.send_command.send_cmd = "OK"
                        else:
                            xorkey = 'j' * len(tasks.send_command.send_cmd)
                            encrypt_cmd = encrypt.Encryption.xor_crypt_string(
                                self, tasks.send_command.send_cmd, xorkey,
                                True, False)
                            reply = dnslib.DNSRecord(dnslib.DNSHeader(
                                id=request.header.id, qr=1, aa=1, ra=1),
                                                     q=request.q)
                            reply.add_answer(
                                dnslib.RR(request.q.qname,
                                          dnslib.QTYPE.TXT,
                                          rdata=dnslib.TXT(encrypt_cmd)))
                            udps.sendto(reply.pack(), addr)
                            tasks.send_command.send_cmd = "OK"
                    else:
                        reply = dnslib.DNSRecord(dnslib.DNSHeader(
                            id=request.header.id, qr=1, aa=1, ra=1),
                                                 q=request.q)
                        reply.add_answer(
                            dnslib.RR(request.q.qname,
                                      dnslib.QTYPE.TXT,
                                      rdata=dnslib.TXT("OK")))
                        udps.sendto(reply.pack(), addr)

                elif qname.startswith("agent."):
                    agentDetails = qname.split(".")
                    agentChunk = encrypt.Encryption.fromBase64URL(
                        self, agentDetails[1])
                    numberOfChunks = int(agentChunk)

                    # Reset all variables
                    fileData = ''
                    chunkIndex = 0

                    # send OK in order to receive the data
                    reply = dnslib.DNSRecord(dnslib.DNSHeader(
                        id=request.header.id, qr=1, aa=1, ra=1),
                                             q=request.q)
                    reply.add_answer(
                        dnslib.RR(request.q.qname,
                                  dnslib.QTYPE.TXT,
                                  rdata=dnslib.TXT("OK")))
                    udps.sendto(reply.pack(), addr)

                elif qname.startswith("results."):
                    resultsDetails = qname.split(".")
                    resultsChunk = encrypt.Encryption.fromBase64URL(
                        self, resultsDetails[1])
                    numberOfChunks = int(resultsChunk)

                    # Reset all variables
                    fileData = ''
                    chunkIndex = 0

                    # send OK in order to receive the data
                    reply = dnslib.DNSRecord(dnslib.DNSHeader(
                        id=request.header.id, qr=1, aa=1, ra=1),
                                             q=request.q)
                    reply.add_answer(
                        dnslib.RR(request.q.qname,
                                  dnslib.QTYPE.TXT,
                                  rdata=dnslib.TXT("OK")))
                    udps.sendto(reply.pack(), addr)

                else:
                    try:
                        msg = qname[0:-(len(domainName) + 2)]
                        chunkNumber, rawData = msg.split('.', 1)
                    except:
                        print("Failed to split the message.")

                    if (int(chunkNumber) == chunkIndex):
                        fileData += rawData.replace('.', '')
                        chunkIndex += 1

                    # Always acknowledge the received chunk (whether or not it was already received)
                    reply = dnslib.DNSRecord(dnslib.DNSHeader(
                        id=request.header.id, qr=1, aa=1, ra=1),
                                             q=request.q)
                    reply.add_answer(
                        dnslib.RR(request.q.qname,
                                  dnslib.QTYPE.TXT,
                                  rdata=dnslib.TXT(chunkNumber)))
                    udps.sendto(reply.pack(), addr)

                    # Have we received all chunks of data ?
                    if chunkIndex == numberOfChunks:
                        try:
                            if agentDetails[0] == "agent":
                                results = encrypt.Encryption.fromBase64URL(
                                    self, fileData)
                                decodeData = results.decode()
                                realData = str(decodeData)
                                xorkey = 'j' * len(realData)
                                data = encrypt.Encryption.xor_crypt_string(
                                    self, realData, xorkey, False, True)
                                agentName = data.split(" ")
                                print("DNS Agent " + agentName[0] +
                                      " Arrived!\n")
                                # clean agentdetails variable
                                agentDetails[0] = "clean"
                                handlers.listAllAgents.append(data)
                            else:
                                results = encrypt.Encryption.fromBase64URL(
                                    self, fileData)
                                decodeData = results.decode()
                                realData = str(decodeData)
                                xorkey = 'j' * len(realData)
                                data = encrypt.Encryption.xor_crypt_string(
                                    self, realData, xorkey, False, True)
                                if "DownloadingFileRand1me3" in data:
                                    AgentFolder = './Agents/' + tasks.agent_iso_class.unique_agent_name
                                    os.chdir(os.path.dirname(__file__))
                                    if not os.path.exists(AgentFolder +
                                                          '/Downloads'):
                                        os.makedirs(AgentFolder + '/Downloads')
                                    split_data = data.split(" ")
                                    tempFile = "ignoremeFile.txt"
                                    # https://stackoverflow.com/questions/22216076/unicodedecodeerror-utf8-codec-cant-decode-byte-0xa5-in-position-0-invalid-s
                                    fullPath = AgentFolder + '/Downloads/' + tempFile
                                    orgFile = AgentFolder + '/Downloads/' + split_data[
                                        1]
                                    try:
                                        with open(fullPath, 'w') as out_file:
                                            out_file.write(split_data[2])
                                            out_file.close()
                                            Convertb64_to_orgFile = subprocess.Popen(
                                                "cat " + fullPath +
                                                " | base64 -d > " + orgFile,
                                                stdin=subprocess.PIPE,
                                                shell=True)
                                            Convertb64_to_orgFile.communicate()
                                            print(
                                                "File " + split_data[1] +
                                                " Downloaded Successfully under "
                                                + orgFile)
                                    except:
                                        print("Failed to download the file!")
                                elif "This1sScreenSh0t" in data:
                                    AgentFolder = './Agents/' + tasks.agent_iso_class.unique_agent_name
                                    os.chdir(os.path.dirname(__file__))
                                    if not os.path.exists(AgentFolder +
                                                          '/Screenshots'):
                                        os.makedirs(AgentFolder +
                                                    '/Screenshots')
                                    split_data = data.split(" ")
                                    imgPath = AgentFolder + '/Screenshots/'
                                    try:
                                        with open(
                                                imgPath +
                                                tasks.agent_iso_class.
                                                unique_agent_name +
                                                str(datetime.now().strftime(
                                                    '-%d%m%Y-%H.%M.%S')) +
                                                '.png', 'wb') as out_image:
                                            out_image.write(
                                                base64.b64decode(
                                                    split_data[1]))
                                            out_image.close()
                                            print("Screenshot save under " +
                                                  imgPath)
                                    except:
                                        print("Screenshot failed")
                                else:
                                    print(data)
                        except IOError:
                            print("[!] Could not read")

            # Query type is not TXT
            else:
                reply = dnslib.DNSRecord(dnslib.DNSHeader(id=request.header.id,
                                                          qr=1,
                                                          aa=1,
                                                          ra=1),
                                         q=request.q)
                udps.sendto(reply.pack(), addr)
    except KeyboardInterrupt:
        print("[!] Stopping DNS Server")
        udps.close()
Beispiel #17
0
def py3dns(serverip='', serverport=0):
    class DNSify(str):
        def __getattr__(self, item):
            return DNSify(item + '.' + self)

    def get_lan_ip4(forcelocalhost=False):
        if forcelocalhost: return '127.0.0.1'
        s = dnslib.socket.socket(dnslib.socket.AF_INET,
                                 dnslib.socket.SOCK_DGRAM)
        try:
            s.connect(('10.255.255.255', 0))
            IP = s.getsockname()[0]
        except:
            IP = '127.0.0.1'
        finally:
            s.close()
        return IP

    def is_domain_blacklisted(sqname, domain_blacklist):
        for test_domain in domain_blacklist:
            if test_domain in sqname:
                return True
        return False

    # Init server settings
    killcommand = 'stop.py3dns.now'
    serve_forever = True
    udp_buffer_size = 1024
    server_name = dnslib.socket.getfqdn()
    server_ip = get_lan_ip4()
    if serverip: server_ip = serverip
    reverse_server_ip = '.'.join(reversed(
        server_ip.split('.'))) + '.in-addr.arpa'
    server_port = 53
    if serverport: server_port = int(serverport)
    server_protocol = 'UDP'
    public_dns_resolvers = [
        '91.239.100.100', '89.233.43.71', '8.8.8.8', '8.8.4.4'
    ]

    # Init simple host cache
    host_cache = {}
    host_cache[server_ip] = server_name

    # Init blacklists
    use_blacklists = True
    domain_blacklist = make_domain_blacklist()
    ipaddr_blacklist = make_ipaddr_blacklist()
    rpz_domain = DNSify('getthefuckaway.net')
    rpz_ip4 = '10.20.30.40'
    rpz_ip6 = '10:20:30:40:50:60:70:80'

    # Init UDP socket server
    udpsrv = dnslib.socket.socket(dnslib.socket.AF_INET,
                                  dnslib.socket.SOCK_DGRAM)
    udpsrv.bind((server_ip, server_port))
    udpsrv.setblocking(False)

    # Init external resolvers
    external_resolver = dns.resolver.Resolver()
    external_resolver_cache = dns.resolver.Cache(cleaning_interval=600.0)
    external_resolver.cache = external_resolver_cache
    external_resolver.nameservers = public_dns_resolvers
    external_resolver.retry_servfail = False
    external_resolver.port = 53
    ##external_resolver.timeout = 1.0
    ##external_resolver.lifetime = 2.0

    # Timestamp UTC
    now = datetime.datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S.%f')

    # App Header
    print('+' + ('-' * 71))
    print('| PY3DNS v/1.0 by BikerDroid')
    print('+' + ('-' * 71))
    print('| Server    :', server_name)
    print('| Address   :', server_ip)
    print('| Port Used :', server_port)
    print('| Protocol  :', server_protocol)
    print('| Recv Size :', udp_buffer_size, 'bytes')
    print('| Solvers   :',
          str(public_dns_resolvers).strip("[]").replace("'", ""))
    print('+' + ('-' * 71))
    print('| Stop PY3DNS by sending "nslookup ' + killcommand + ' ' +
          server_ip + '"')
    print('+' + ('-' * 71))
    print(now, ': Ready to serve...')

    # Main Loop
    while serve_forever:

        # Clear vars
        sres = sip4 = sip6 = smx = scname = sns = stxt = sptr = ssoa = sany = hostip = ''

        # Main: Get client request, add hostip and host name to host_cache
        try:
            data, addr = udpsrv.recvfrom(udp_buffer_size)
            hostip = str(addr[0])
            if not hostip in host_cache:
                host_cache[hostip] = dnslib.socket.getfqdn(
                    str(dnslib.socket.gethostbyaddr(hostip)[0]))
            now = datetime.datetime.utcnow().strftime(
                '%Y-%m-%d %H:%M:%S.%f')  # UTC
        except:
            continue

        # Client request -> id, qname (domain), qtype (A,AAAA,MX etc)
        request = dnslib.DNSRecord.parse(data)
        qid = request.header.id
        qname = request.q.qname
        qtype = request.q.qtype
        slabel = str(qname.label)
        sqname = str(qname)
        sqtype = str(dnslib.QTYPE[qtype])

        # Rem this line if rpz_domain is to overwrite
        # blacklisted domains with their real names.
        sdomain = sqname

        # Dirty trick to shut down server from commandline:
        # Syntax : nslookup stop.dns.srv <server_ip>
        # Example: nslookup stop.dns.srv 127.0.0.1
        # Must be before external_resolver.query()
        if sqname.rstrip('.') == killcommand:
            serve_forever = False
            continue

        # Dirty Reverse Lookup of local server
        # Allows requesting client to get server_name
        if qtype == dnslib.QTYPE.PTR:
            if sqname.rstrip('.') == reverse_server_ip:
                reply = dnslib.DNSRecord(header=dnslib.DNSHeader(id=qid,
                                                                 qr=1,
                                                                 aa=1,
                                                                 ra=1,
                                                                 rcode=0),
                                         q=dnslib.DNSQuestion(sqname,
                                                              qtype)).reply()
                reply.add_answer(
                    dnslib.RR(sqname,
                              dnslib.QTYPE.PTR,
                              rdata=dnslib.PTR(server_name),
                              ttl=3600))
                udpsrv.sendto(reply.pack(), addr)
                print(now, ':', sqname, '|', sqtype, '=', qtype, '|',
                      server_name, '|', hostip, '=', host_cache[hostip])
                continue

        # Get DNS record from public_dns_resolvers.
        # This section can be changed to serve from
        # own database, stationary files or similar.
        try:
            if not sqname.rstrip('.') in domain_blacklist:
                external_resolver_result = external_resolver.query(
                    sqname.rstrip('.'), sqtype)
            found = True
        except:
            print(now, '> qtype', sqtype,
                  '(' + str(qtype) + ') was not found for', sqname)
            external_resolver_result = []
            sres = sip4 = sip6 = ''
            found = False

        if not found:
            # Create DNSRecord Header reply: rcode = 5 (Query Refused). See RFC2136 for rcode's.
            reply = dnslib.DNSRecord(header=dnslib.DNSHeader(id=qid,
                                                             qr=1,
                                                             aa=1,
                                                             ra=1,
                                                             rcode=5),
                                     q=dnslib.DNSQuestion(sdomain,
                                                          qtype)).reply()
        else:

            # Create DNSRecord Header reply: rcode = 0 (No Error)
            reply = dnslib.DNSRecord(header=dnslib.DNSHeader(id=qid,
                                                             qr=1,
                                                             aa=1,
                                                             ra=1,
                                                             rcode=0),
                                     q=dnslib.DNSQuestion(sdomain,
                                                          qtype)).reply()

            # Add A record answer for domain and IP
            # Filter blacklisted IP4/6 addresses.
            if qtype == dnslib.QTYPE.A:
                if is_domain_blacklisted(
                        sqname.rstrip('.'),
                        domain_blacklist):  # Simple domain blacklist check
                    sres = sip4 = rpz_ip4
                    reply.add_answer(
                        dnslib.RR(sdomain,
                                  dnslib.QTYPE.A,
                                  rdata=dnslib.A(sip4),
                                  ttl=60))
                else:
                    for data in external_resolver_result:
                        sres = sip4 = str(data).strip()
                        if sip4:
                            if sip4 in ipaddr_blacklist:  # Simple IP (4+6) blacklist check
                                sres = sip4 = rpz_ip4
                            reply.add_answer(
                                dnslib.RR(sdomain,
                                          dnslib.QTYPE.A,
                                          rdata=dnslib.A(sip4),
                                          ttl=60))

            # Add AAAA record answer for domain and IP
            # Filter blacklisted IP4/6 addresses.
            elif qtype == dnslib.QTYPE.AAAA:
                if is_domain_blacklisted(
                        sqname.rstrip('.'),
                        domain_blacklist):  # Simple domain blacklist check
                    sres = sip6 = rpz_ip6
                    reply.add_answer(
                        dnslib.RR(sdomain,
                                  dnslib.QTYPE.AAAA,
                                  rdata=dnslib.AAAA(sip6),
                                  ttl=60))
                else:
                    for data in external_resolver_result:
                        sres = sip6 = str(data).strip()
                        if sip6:
                            if sip6 in ipaddr_blacklist:  # Simple IP (4+6) blacklist check
                                sres = sip6 = rpz_ip6
                            reply.add_answer(
                                dnslib.RR(sdomain,
                                          dnslib.QTYPE.AAAA,
                                          rdata=dnslib.AAAA(sip6),
                                          ttl=60))

            # Add NS record answer for domain
            elif qtype == dnslib.QTYPE.NS:
                for data in external_resolver_result:
                    sres = sns = str(data).strip()
                    if sns:
                        reply.add_answer(
                            dnslib.RR(sdomain,
                                      dnslib.QTYPE.NS,
                                      rdata=dnslib.NS(sns),
                                      ttl=60))

            # Add MX record answer for domain and IP
            elif qtype == dnslib.QTYPE.MX:
                for data in external_resolver_result:
                    sres = smx = str(data).strip()
                    if smx:
                        reply.add_answer(
                            dnslib.RR(sdomain,
                                      dnslib.QTYPE.MX,
                                      rdata=dnslib.MX(smx),
                                      ttl=60))

            # Add CNAME record answer for domain
            elif qtype == dnslib.QTYPE.CNAME:
                for data in external_resolver_result:
                    sres = scname = str(data).strip()
                    if scname:
                        reply.add_answer(
                            dnslib.RR(sdomain,
                                      dnslib.QTYPE.CNAME,
                                      rdata=dnslib.CNAME(scname),
                                      ttl=60))

            # Add TXT record answer for domain
            elif qtype == dnslib.QTYPE.TXT:
                for data in external_resolver_result:
                    sres = stxt = str(data).strip()
                    if stxt:
                        reply.add_answer(
                            dnslib.RR(sdomain,
                                      dnslib.QTYPE.TXT,
                                      rdata=dnslib.TXT(stxt),
                                      ttl=60))

            # Add PTR record answer for domain
            elif qtype == dnslib.QTYPE.PTR:
                for data in external_resolver_result:
                    sres = sptr = str(data).strip()
                    if sptr:
                        reply.add_answer(
                            dnslib.RR(sdomain,
                                      dnslib.QTYPE.PTR,
                                      rdata=dnslib.PTR(sptr),
                                      ttl=60))

            # Add ANY record answer for domain
            elif qtype == dnslib.QTYPE.ANY:
                for data in external_resolver_result:
                    sres = sany = str(data).strip()
                    if sany:
                        reply.add_answer(
                            dnslib.RR(sdomain,
                                      dnslib.QTYPE.ANY,
                                      rdata=dnslib.ANY(sany),
                                      ttl=60))

            # Add SOA record answer for domain
            elif qtype == dnslib.QTYPE.SOA:
                for data in external_resolver_result:
                    if str(data).strip():
                        soa_data = str(data).strip().split(' ')
                        soa_mname = soa_data[0]
                        soa_rname = soa_data[1]
                        soa_serial = soa_data[2]
                        soa_refresh = soa_data[3]
                        soa_retry = soa_data[4]
                        soa_expire = soa_data[5]
                        soa_minimum = soa_data[6]
                        soa_time = (int(soa_serial), int(soa_refresh),
                                    int(soa_retry), int(soa_expire),
                                    int(soa_minimum))
                        sres = soa_mname + ',' + soa_rname + ',' + soa_serial + ',' + soa_refresh + ',' + soa_retry + ',' + soa_expire + ',' + soa_minimum
                        reply.add_answer(
                            dnslib.RR(sdomain,
                                      dnslib.QTYPE.SOA,
                                      rdata=dnslib.SOA(soa_mname, soa_rname,
                                                       soa_time),
                                      ttl=60))
            else:
                # Unknown qtype - add CNAME as answer :)
                reply.add_answer(
                    dnslib.RR(qname,
                              dnslib.QTYPE.CNAME,
                              rdata=dnslib.CNAME(server_name),
                              ttl=60))

        # Send DNS reply to client address using UDP
        udpsrv.sendto(reply.pack(), addr)

        # Show status in console
        print(now, ':', sqname, '|', sqtype, '=', qtype, '|', sres, '|',
              hostip, '=', host_cache[hostip])  #,reply.pack()

    # Shutting down
    print(now, ': Shutting down...')
    udpsrv.shutdown(0)
    print(now, ': Done.')