Esempio n. 1
0
def createDNSErrorReply(id, question, rcode):
    header = Header(id, Header.OPCODE_QUERY, rcode, qdcount=1, qr=1)

    query = header.pack()
    query += question.pack()

    return query
Esempio n. 2
0
def constructDNSQuery(id, question):
    """
  Construct a query (in binary format), given an id and a QE object
  """
    header = Header(id, Header.OPCODE_QUERY, Header.RCODE_NOERR, qdcount=1)
    query = header.pack()
    query += question.pack()
    return query
Esempio n. 3
0
def print_dns_payload(data):
  """
    Used for debugging
  """
  logger.log(DEBUG1, "payload (length:{0} bytes) received is:\n{1}\n".format(len(data), hexdump(data)))
  logger.log(DEBUG1, "Header received  is:\n{0}\n".format(Header.fromData(data)))
  header_len                = len(Header.fromData(data))
  question                  = QE.fromData(data, header_len)
  logger.log(DEBUG1, "Question received  is:\n{0}\n".format(question))
Esempio n. 4
0
def construct_response(id, question, answers, authority, additional, RCODE=Header.RCODE_NOERR):
    """
    Packs everything down into dns payload format
    """
    ancount = len(answers)
    nscount = len(authority)
    arcount = len(additional)
    header = Header(id, Header.OPCODE_QUERY, RCODE, qdcount=1, ancount=ancount,
                    nscount=nscount, arcount=arcount, qr=True, aa=False, tc=False, rd=True, ra=True)
    packed_secs = ""
    for sec in [answers, authority, additional]:
        packed_secs += reduce(lambda x, y: x + y.pack(), sec, "")
    return "{0}{1}{2}".format(header.pack(),question.pack(), packed_secs)
Esempio n. 5
0
def constructPacket(err, data, question, qid): # creates the packet from data, data is a three tuple of lists ([answer], [authroity], [additional])
    header = Header(qid, Header.QUERY, err, 1, len(data[0]), len(data[1]), len(data[2]), True)
    packet = header.pack()
    packet += question.pack()
    for i in data[0]:
        if i[1].__class__.__name__ == 'DomainName':
            packet += RR_CNAME(i[0], i[2], i[1]).pack()
        else:
            packet += RR_A(i[0], i[2], i[1].toNetwork()).pack()
    for i in data[1]:
        packet += RR_NS(i[0], i[2], i[1]).pack()
    for i in data[2]:
        packet += RR_A(i[0], i[2], i[1].toNetwork()).pack()
    return packet
Esempio n. 6
0
def constructQuestion(id, dn):
    newHeader = Header(id,
                       Header.OPCODE_QUERY,
                       Header.RCODE_NOERR,
                       qdcount=1,
                       ancount=0,
                       nscount=0,
                       arcount=0,
                       qr=0,
                       aa=0,
                       tc=0,
                       rd=0,
                       ra=0)
    neQE = QE(type=QE.TYPE_A, dn=dn)
    return "{0}{1}".format(newHeader.pack(), neQE.pack())
Esempio n. 7
0
def parseDNSPacket(data):
    """
  Extracts useful DNS information from a binary format
  """
    newData = {
        'header': None,
        'question': None,
        'answers': None,
        'authority': None,
        'additional': None
    }
    offset = 0

    newData['header'] = Header.fromData(data, offset)
    offset += newData['header'].__len__()
    newData['question'] = QE.fromData(data, offset)
    offset += newData['question'].__len__()

    (newData['answers'], offset) = parseSectionList(data,
                                                    newData['header']._ancount,
                                                    offset)
    (newData['authority'],
     offset) = parseSectionList(data, newData['header']._nscount, offset)
    (newData['additional'],
     offset) = parseSectionList(data, newData['header']._arcount, offset)

    return newData
Esempio n. 8
0
def construct_A_query(domain_name):
    """
    Constructs a query for an A record for a given domain name
    domain name is the domain name we're querying about
    """
    #generate a 16 bit random number - Header class ensures this is packed correctly
    h_id                    = randint(0, 65535)
    header                  = Header(h_id, Header.OPCODE_QUERY, Header.RCODE_NOERR,
                    qdcount = 1, ancount=0, nscount=0, arcount=0, qr=False,
                    aa      = False, tc=False, rd=False, ra=False)
    if not isinstance(domain_name, DomainName):
        raise Exception("construct_A_query didnt receive a domain name of type DomainName")
    
    question                = QE(type=QE.TYPE_A, dn=domain_name)

    return ("{0}{1}".format(header.pack(),question.pack()), question)
Esempio n. 9
0
def insertRRStoCache(
        reply, offset, header
):  #This function takes a payload and inserts them into the CACHE
    ans = {}
    ns = {}
    addit = {}
    nsarray = []
    ararray = []
    anarray = []
    header = Header.fromData(reply)
    qe = QE.fromData(reply, len(header))
    for x in range(header._ancount):
        rr = RR.fromData(reply, offset)
        cache = CacheEntry(expiration=int(time()) + rr[0]._ttl,
                           authoritative=False)
        ans[x] = rr, cache
        offset = offset + rr[1]
    anarray.append(ans)
    for x in range(header._nscount):
        rr = RR.fromData(reply, offset)
        cache = CacheEntry(expiration=int(time()) + rr[0]._ttl,
                           authoritative=True)
        ns[x] = rr, cache
        offset = offset + rr[1]
    nsarray.append(ns)
    for x in range(header._arcount):
        rr = RR.fromData(reply, offset)
        cache = CacheEntry(expiration=int(time()) + rr[0]._ttl,
                           authoritative=False)
        addit[x] = rr, cache
        offset = offset + rr[1]
    ararray.append(addit)
    return ans, ns, addit
Esempio n. 10
0
def storeInCache(reply):
    header = Header.fromData(reply)
    qe = QE.fromData(reply, len(header))
    count = header._ancount + header._nscount + header._arcount
    offset = len(header) + len(qe)
    (ans, ns, addit) = insertRRStoCache(reply, offset, header)
    cache[qe._dn] = ans, ns, addit
Esempio n. 11
0
def parseResponse(data, _id):
    responseHeader = Header.fromData(data)
    try: #check for valid header codes
        assert(responseHeader._id == _id)
        assert(responseHeader._opcode == Header.OPCODE_QUERY)
        assert(responseHeader._qr == True)
    except AssertionError:
        raise AttributeError
    try:
        assert(responseHeader._rcode == Header.RCODE_NOERR)
    except AssertionError:
        error = responseHeader._rcode
        if error == Header.RCODE_NAMEERR:
            raise NameError("Name not found")


    noOfQuestions = responseHeader._qdcount
    noOfAnswers = responseHeader._ancount
    noOfAuthorities = responseHeader._nscount
    noOfAdditional = responseHeader._arcount
    
    offset = len(responseHeader)
    
    (offset, questions) = parseQuestions(data, offset, noOfQuestions)
    (offset, answers) = parseRecords(data, offset, noOfAnswers)
    (offset, authorities) = parseRecords(data, offset, noOfAuthorities)
    (offset, additional) = parseRecords(data, offset, noOfAdditional)

    cacheAnswers(answers)
    cacheNameServers(authorities, additional)

    return (questions, answers, authorities, additional)
Esempio n. 12
0
def parse_response_payload(payload):
  """
    for parsing responses the program receives from nameservers
  """
  header                    = Header.fromData(payload)
  byte_ptr = len(header)
  config = OrderedDict(zip(["question" , "answer", "authority", "additional"], ["_qdcount", "_ancount", "_nscount", "_arcount"]))
  parsed = {"header" : header}
  for key, val in config.items():
    #the question section isn't parsed as a RR, needs special treatment
    if key is "question":
      #assumes only ever receive one question entry
      if getattr(header, val) > 1:
        raise Exception("Uh oh!")
      question = QE.fromData(payload, byte_ptr)
      parsed[key] = [question,]
      byte_ptr += len(question)
        
    else:
      num_entries = getattr(header, val)
      rrs, byte_ptr = ([], byte_ptr) if num_entries is 0 else parse_rrs(payload,
                                                                        byte_ptr,
                                                                        num_entries)    
      parsed[key] = rrs
  return parsed
Esempio n. 13
0
def constructErrorHeader(data):  #Constructs a server fail response
    header = Header.fromData(data)
    id = header._id
    qe = QE.fromData(data, len(header))
    newHeader = Header(id,
                       Header.OPCODE_QUERY,
                       Header.RCODE_SRVFAIL,
                       qdcount=1,
                       ancount=0,
                       nscount=0,
                       arcount=0,
                       qr=0,
                       aa=0,
                       tc=0,
                       rd=0,
                       ra=0)
    return "{0}{1}".format(newHeader.pack(), qe.pack())
Esempio n. 14
0
def parseDNS(
    data, reply
):  #This function parses the DNS payload and converts them into a nameserver, ip address dictionary and nameserver array
    header = Header.fromData(data)
    qe = QE.fromData(data, len(header))
    (nsarray, offset) = getNS(reply, len(header) + len(qe))
    (ararray, offset) = getAR(reply, offset)
    nsIPdict = mapValues(nsarray, ararray)
    return nsIPdict, nsarray
Esempio n. 15
0
def getNS(data, offset):
    header = Header.fromData(data)
    nscount = header._nscount
    nsarray = []
    for x in range(0, nscount):
        rr = RR.fromData(data, offset)
        offset = offset + rr[1]
        nsarray.append(rr)
    return nsarray, offset  #This function gets the Nameserver RR in the provided payload and returns an array
Esempio n. 16
0
def queryServer(domain, serverAddress, cnames):
    timeNow = int(time())
    if (timeNow - requestReceived) > 60 - TIMEOUT: #check that you haven't been querying for more than 60 seconds
        raise timeout
    _id = randint(0, 65535) #random 16 bit int
    queryHeader = Header(_id, Header.OPCODE_QUERY, Header.RCODE_NOERR, 1).pack()
    question = QE(dn=domain).pack()
    packet = queryHeader + question
    cs.sendto(queryHeader + question, (serverAddress, 53))
    (data, address) = cs.recvfrom(512)
    return handleResponse(data, _id, cnames)
Esempio n. 17
0
def createDNSReply(id, question, result):
    header = Header(id,
                    Header.OPCODE_QUERY,
                    Header.RCODE_NOERR,
                    qdcount=1,
                    ancount=1,
                    nscount=len(result['authority']),
                    arcount=len(result['additional']),
                    qr=1)

    query = header.pack()
    query += question.pack()
    query += result['answer'].pack()

    for authority in result['authority']:
        query += authority.pack()

    for additional in result['additional']:
        query += additional.pack()

    return query
Esempio n. 18
0
def packReply(
    data, reply
):  #This function packs the response payload by first change the header variables, question variables and individulaly packing the RR
    replyheader = Header.fromData(reply)
    replyqe = QE.fromData(reply, len(replyheader))
    count = replyheader._ancount + replyheader._nscount + replyheader._arcount
    offset = len(replyheader) + len(replyqe)
    rr_entries = []
    for x in range(count):
        rr = RR.fromData(reply, offset)
        rr_entries.append(rr)
        offset = offset + rr[1]
    dataHeader = Header.fromData(data)
    replyheader._id = dataHeader._id
    dataqe = QE.fromData(data, len(dataHeader))
    replyqe._type = dataqe._type
    replyqe._dn = dataqe._dn  #check this
    rr_entries[0][0]._dn = dataqe._dn
    packed_rr = ""
    for x in range(0, len(rr_entries)):
        packed_rr = packed_rr + rr_entries[x][0].pack()
    return "{0}{1}{2}".format(replyheader.pack(), replyqe.pack(), packed_rr)
Esempio n. 19
0
def getAR(data, offset):
    #Returns A Records only
    header = Header.fromData(data)
    count = header._arcount
    ararray = []
    for x in range(0, count - 1):
        rr = RR.fromData(data, offset)
        offset = offset + rr[1]
        if rr[0]._type == 1:
            ararray.append(rr)
        if (offset + rr[1] > len(data)):
            break
    return ararray, offset  #This function gets the Additional RR in the provided payload and returns an array
Esempio n. 20
0
def getAnswers(
    reply
):  #This function takes a payload and returns an array that has the answer records
    header = Header.fromData(reply)
    qe = QE.fromData(reply, len(header))
    offset = len(header) + len(qe)
    answerCount = header._ancount
    answers = []
    #print answerCount
    for x in range(answerCount):
        rr = RR.fromData(reply, offset)
        answers.append(rr)
        offset = offset + rr[1]
    return answers
Esempio n. 21
0
def parseRequest(data):
    requestHeader = Header.fromData(data)
    try:
        assert(requestHeader._opcode == Header.OPCODE_QUERY)
        assert(requestHeader._rcode == Header.RCODE_NOERR)
        assert(requestHeader._qr == False)
    except AssertionError:
        raise ValueError("Incorrectly formatted request")
    noOfQueries = requestHeader._qdcount
    offset = requestHeader.__len__()
    queries = []
    for i in range(noOfQueries):
        queries.append(QE.fromData(data, offset))
        offset += queries[i].__len__()
        if str(queries[i]) == "NIMPL":
            raise NotImplementedError 

    return (requestHeader._id, queries)
Esempio n. 22
0
def sendDNSQuery(
        data,
        address):  #This function takes a payload and an address and sends them
    printQuestion(data, address)
    cs.sendto(data, (address, 53))
    (
        reply,
        temp_address,
    ) = cs.recvfrom(512)
    header = Header.fromData(reply)
    if (header._ancount > 0):  #Checks if the response has answers
        qe = QE.fromData(data, len(header))
        rr = RR.fromData(reply, len(header) + len(qe))
        if rr[0]._type == 5:
            return queryCNAME(
                data, reply, rr[0]._cname
            )  #If CNAME is in the answer then it is converted into IP address
        else:
            return reply
    else:
        try:
            (nsIPdict, nsarray) = parseDNS(data, reply)
        except AttributeError:
            qe = QE.fromData(reply, len(header))
            rr = RR.fromData(reply, len(header) + len(qe))
            if rr[0]._type == 6:
                return reply
        if bool(
                nsIPdict
        ) == False:  #If a nameserver didnt respond with any IP we create a new question and send in to the root server
            for x in range(len(nsarray)):
                #print nsarray[x][0]._nsdn
                newData = constructQuestion(header._id, nsarray[x][0]._nsdn)
                newReply = sendDNSQuery(newData, ROOTNS_IN_ADDR)
                anarray = getAnswers(newReply)
                for y in range(len(anarray)):
                    ip = inet_ntoa(anarray[y][0]._inaddr)
                    return sendDNSQuery(data, ip)
        return performRecursiveQuery(
            data, nsIPdict,
            nsarray)  #If no answers in response then a recursive query is made
Esempio n. 23
0
def get_ip_addr(qe, dns_server_to_send=ROOTNS_IN_ADDR):
    if qe._dn in acache:
        print "\nIP address of question entry found in cache: question =", qe._dn
        return_header = Header(randint(0, 65536), Header.OPCODE_QUERY, Header.RCODE_NOERR, qdcount=1, qr=True, aa=True)
        return_rrs = []
        for key in acache[qe._dn]._dict.keys():
            return_rrs.append(RR_A(qe._dn, acache[qe._dn]._dict[key]._expiration, key.toNetwork()))
            return_header._ancount += 1
        return return_header, return_rrs

    if qe._dn in cnamecache:
        print "\nCNAME found in cache - starting search for IP address of canonical name ", cnamecache[qe._dn]._cname
        cname_qe = QE(dn=cnamecache[qe._dn]._cname)
        (return_header, return_rrs) = get_ip_addr(cname_qe)
        return_header._ancount += 1
        return_rrs.insert(0, RR_CNAME(qe._dn, cnamecache[qe._dn]._expiration, cnamecache[qe._dn]._cname))
        return return_header, return_rrs

    # if dns server to send is root, check whether parent domain of query exists in cache
    if dns_server_to_send == ROOTNS_IN_ADDR and qe._dn.parent() is not None:
        dn_runner = qe._dn.parent()
        while dn_runner.parent() is not None:
            if dn_runner in nscache:
                print "\nName server for parent domain found in cache: parent domain =", dn_runner
                for key in nscache[dn_runner].keys():
                    if key in acache:
                        for ip in acache[key]._dict.keys():
                            print "Next authoritative DNS name server domain is:", key
                            print "Next authoritative DNS name server IP is:", ip

                            try:
                                return get_ip_addr(qe, dns_server_to_send=str(ip))
                            except Exception, e:
                                if e.message != "authoritative DNS name server down":
                                    print "Unhandled Exception:", e
                                    print ""
                                    raise e
                                print "\nauthoritative DNS name server down, trying next one"
                                break
            dn_runner = dn_runner.parent()
Esempio n. 24
0
def checkCache(resolverQueryHeader, resolverQueryQE):
    '''
    Check query in acache, cnamecache
    Return its type, header and result
    '''
    # Initialise reponses
    replyHeader = Header(resolverQueryHeader._id, resolverQueryHeader._opcode, Header.RCODE_NOERR, qdcount=1, qr=True, rd=resolverQueryHeader._rd, ra=True)
    replyRR=[]
    
    # A type record: return all matching addresses
    if resolverQueryQE._dn in acache:
        for key in acache[resolverQueryQE._dn]._dict.keys():
            replyRR.append(RR_A(resolverQueryQE._dn, acache[resolverQueryQE._dn]._dict[key]._expiration, key.toNetwork()))
            replyHeader._ancount += 1
        return 'a', replyHeader, replyRR
    
    # CNAME type record: return its CNAME
    elif resolverQueryQE._dn in cnamecache:
        replyRR = RR_CNAME(resolverQueryQE._dn, cnamecache[resolverQueryQE._dn]._expiration, cnamecache[resolverQueryQE._dn]._cname)
        return 'cname', replyHeader, replyRR

    # No record
    else:
        return 'none', replyHeader, []
Esempio n. 25
0
def check_cache(
    data
):  #This function checks if the question from the client is in the cache
    header = Header.fromData(data)
    id = header._id
    qe = QE.fromData(data, len(header))
    domain = qe._dn
    if domain in cache:  #Checks if the question is in the cache dictionary else queries the root server as normal
        ans = cache[qe._dn][0]
        ns = cache[qe._dn][1]
        addit = cache[qe._dn][2]
        keys = ans.keys()
        values = ans.values()
        anarray = []
        nsarray = []
        ararray = []
        packed_rr = ""
        for x in range(
                len(ans)
        ):  #These for loops gets the RR from cache and updates the expiry time
            rr = ans.values()[x][0][0]
            cac = ans.values()[x][1]
            expiry = cac._expiration - int(time())
            rr._ttl = expiry
            if expiry > 0:
                anarray.append(rr)
                packed_rr = packed_rr + rr.pack()
        #print len(anarray)
        if len(anarray) > 0:
            for x in range(len(ns)):
                rr = ns.values()[x][0][0]
                cac = ns.values()[x][1]
                expiry = cac._expiration - int(time())
                rr._ttl = expiry
                if expiry > 0:
                    nsarray.append(rr)
                    packed_rr = packed_rr + rr.pack()
            for x in range(len(addit)):
                rr = addit.values()[x][0][0]
                cac = addit.values()[x][1]
                expiry = cac._expiration - int(time())
                rr._ttl = expiry
                if expiry > 0:
                    ararray.append(rr)
                    packed_rr = packed_rr + rr.pack()
            newHeader = Header(id,
                               Header.OPCODE_QUERY,
                               Header.RCODE_NOERR,
                               qdcount=1,
                               ancount=len(anarray),
                               nscount=len(nsarray),
                               arcount=len(ararray),
                               qr=0,
                               aa=0,
                               tc=0,
                               rd=0,
                               ra=0)
            return "{0}{1}{2}".format(newHeader.pack(), qe.pack(), packed_rr)
        else:
            return sendDNSQuery(data, ROOTNS_IN_ADDR)
    else:
        return sendDNSQuery(data, ROOTNS_IN_ADDR)
Esempio n. 26
0
                            print "Next authoritative DNS name server IP is:", ip

                            try:
                                return get_ip_addr(qe, dns_server_to_send=str(ip))
                            except Exception, e:
                                if e.message != "authoritative DNS name server down":
                                    print "Unhandled Exception:", e
                                    print ""
                                    raise e
                                print "\nauthoritative DNS name server down, trying next one"
                                break
            dn_runner = dn_runner.parent()

    # create DNS query to be sent to authoritative DNS name server
    iq_id = randint(0, 65536)  # random 16 bit int
    iq_header = Header(iq_id, Header.OPCODE_QUERY, Header.RCODE_NOERR, qdcount=1)
    iq = iq_header.pack() + qe.pack()

    for tries in range(3):  # try re-sending to same name server 3x before giving up
        print "Sending iterative query to authoritative DNS name server..."
        cs.sendto(iq, (dns_server_to_send, 53))  # DNS servers use port 53 by convention

        # get reply from server
        try:
            while True:
                cs.settimeout(2)
                (response, address_not_used,) = cs.recvfrom(512)
                cs.settimeout(None)

                response_header = Header.fromData(response)
                if response_header._id == iq_id:
Esempio n. 27
0
        signal.signal(signal.SIGALRM, handler)
        signal.alarm(60)
        ##clean out expired records before we handle this query
        update_caches() 
        response  = construct_response(*resolve(0, qid, question, qname, qname, get_best_ns(nscache, qname), [], [], []))
        signal.alarm(0)   
    except (OutOfTimeException) as e:
        # Server Failure
        logger.error(e)
        signal.alarm(0)
        response = construct_response(qid, question, [], [], [], RCODE=Header.RCODE_SRVFAIL)
    return response 
# This is a simple, single-threaded server that takes successive
# connections with each iteration of the following loop:
while 1:
  logger.log(DEBUG1, "\n"*40)
  logger.log(DEBUG1, "="*400)
  
  (data, address,)         = ss.recvfrom(512) # DNS limits UDP msgs to 512 bytes
  if not data:
    log.error("client provided no data")
    continue
  header = Header.fromData(data)
  qid = header._id
  header_len                = len(header)
  question = QE.fromData(data, header_len)
  qname                     = DomainName.fromData(data, header_len)
  response = exc(qid, qname, question)
  ss.sendto(response, address)
  
Esempio n. 28
0
def printQuestion(data, address):
    header = Header.fromData(data)
    qe = QE.fromData(data, len(header))
    print "Looking up "
    print qe._dn
    print address
Esempio n. 29
0
while 1:
    # Wait for query
    (data, address,) = ss.recvfrom(512)  # DNS limits UDP msgs to 512 bytes
    if not data:
        logger.error("client provided no data")
        continue
    
    else:
        # Parse client query
        (clientQueryHeader, clientQueryQE) = parseQuery(data)

        # Discard unqualified queries
        if clientQueryHeader._rd == False:
            logger.error("client requested iterative query")
            reply = Header(clientQueryHeader._id, clientQueryHeader._opcode, Header.RCODE_NIMPL, qdcount= clientQueryHeader._qdcount, qr=True, rd=clientQueryHeader._rd, ra=True).pack()
             
        if clientQueryHeader._qdcount != 1:
            logger.error("client requested more than one query")
            reply = Header(clientQueryHeader._id, clientQueryHeader._opcode, Header.RCODE_NIMPL, qdcount= clientQueryHeader._qdcount, qr=True, rd=clientQueryHeader._rd, ra=True).pack()

        if clientQueryQE._type != QE.TYPE_A or clientQueryHeader._opcode != Header.QUERY:
            logger.error("client's query is not A type or not standard")
            reply = Header(clientQueryHeader._id, clientQueryHeader._opcode, Header.RCODE_NIMPL, qdcount= clientQueryHeader._qdcount, qr=True, rd=clientQueryHeader._rd, ra=True).pack()

        # Resolve client query
        else:
            # Initialise response/reply to client
        
            responseRRList = []
            responseRRAuthorityList = []
Esempio n. 30
0
def queryCNAME(data, reply,
               cname):  #This function takes a CNAME to query its IP address
    header = Header.fromData(data)
    data_2 = constructQuestion(header._id, cname)
    reply = sendDNSQuery(data_2, ROOTNS_IN_ADDR)
    return packReply(data, reply)
Esempio n. 31
0
# This is a simple, single-threaded server that takes successive
# connections with each iteration of the following loop:
while 1:
    (
        data,
        address,
    ) = ss.recvfrom(512)  # DNS limits UDP msgs to 512 bytes

    if not data:
        logger.error("client provided no data")
        continue

    # ------------------------------------------------------------------

    # Parse the request
    header = Header.fromData(data)

    # Ignore anything other than requests
    if header._qr != 0:
        logger.info("Ignoring non-request...")
        continue

    logger.info("Received request ({} bytes). Header:".format(len(data)))
    logger.info(header)

    # Parse current question entry
    question = QE.fromData(data, 12)

    logger.info("Received question:")
    logger.info(question)
Esempio n. 32
0
def parseHeader(reply):
    header = Header.fromData(reply)
    return header._ancount, header._nscount, header._arcount
Esempio n. 33
0
def parseQuery(data):
    '''
    Parse a DNS query with fromData() provided in Header and QE classes
    '''
    return Header.fromData(data), QE.fromData(data, 12)
Esempio n. 34
0
def resolve_iterative(question_domain, nameserver=DomainName(ROOTNS_DN)):
    logger.info("SEP:  ITERATIVE START: '{}'  =>  '{}'".format(
        question_domain, nameserver))

    # Nameserver itself should be resolved
    if nameserver not in acache:
        resolve_recursive(nameserver)

    # If it still isn't resolved, ignore it
    if nameserver not in acache:
        logger.info("SEP3: ITERATIVE ERROR: '{}'  =>  '{}'".format(
            question_domain, nameserver))
        return [], [], []

    nameserver_ip = acache[nameserver]._dict.keys()[0]

    # Check A cache
    if question_domain in acache:
        logger.info("Domain is in A-Cache")
        return [acache[question_domain]], [], []

    # Check CNAME cache
    if question_domain in cnamecache:
        logger.info("Domain is in CNAME-Cache")
        return [cnamecache[question_domain]], [], []

    # At this point the domain is definitely not in the cache, so we'll ask the nameserver

    # Build the request
    request_hdr = Header(id=randint(0, 65536),
                         opcode=Header.OPCODE_QUERY,
                         rcode=Header.RCODE_NOERR,
                         qdcount=1)

    request_question = QE(dn=question_domain)

    request_data = request_hdr.pack() + request_question.pack()

    response = None
    response_hdr = None

    # ------------------------------------------------------------------------------------------
    # We'll send the UDP data to ask the nameserver about the question domain

    for _ in range(2):
        logger.info("Requesting '{}' to nameserver: '{}' ({})".format(
            question_domain, nameserver, nameserver_ip))

        # Send data to server
        cs.sendto(request_data, (str(nameserver_ip), 53))

        # Wait for reply
        try:
            cs.settimeout(2)
            response, _ = cs.recvfrom(512)

            # Build response header
            response_hdr = Header.fromData(response)

            # If a proper response is received, break
            if response_hdr._id == request_hdr._id:
                break

        except timeout:
            logger.info("Nameserver '{}' timed out".format(nameserver))

        if response is not None:
            break

    if response is None:
        logger.info("SEP3: ITERATIVE ERROR: '{}'  =>  '{}'".format(
            question_domain, nameserver))
        return [], [], []

    # ------------------------------------------------------------------------------------------
    # Process the returned resource records

    # Skip header and question entry
    resource_record_head = len(request_data)
    resource_record_quantity = response_hdr._ancount + response_hdr._nscount + response_hdr._arcount

    rr_answers = []
    rr_authoritative = []
    rr_additional = []

    # Go over each resource record
    for curr_resource_record_index in range(resource_record_quantity):

        # Fetch the current resource record
        curr_resource_record, rr_size = RR.fromData(response,
                                                    resource_record_head)

        # Move the reading head
        resource_record_head += rr_size

        # Record will be authoritative if it's in the AUTHORITY SECTION (hence after the ANSWERS section)
        is_answer = curr_resource_record_index < response_hdr._ancount
        is_authoritative = not is_answer and curr_resource_record_index < response_hdr._ancount + response_hdr._nscount
        is_additional = not (is_answer or is_authoritative)

        # Store the current resource record
        if curr_resource_record._type in [
                RR.TYPE_A, RR.TYPE_NS, RR.TYPE_CNAME
        ]:
            if is_answer:
                rr_answers.append(curr_resource_record)
            elif is_authoritative:
                rr_authoritative.append(curr_resource_record)
            else:
                rr_additional.append(curr_resource_record)

        # Record type A
        if curr_resource_record._type == RR.TYPE_A:
            record_address = InetAddr.fromNetwork(curr_resource_record._inaddr)
            # logger.info("Adding 'A' record for {}: {}".format(curr_resource_record._dn, record_address))
            logger.info(curr_resource_record)

            # Add to A-Cache
            if curr_resource_record._dn not in acache:
                acache[curr_resource_record._dn] = ACacheEntry({
                    record_address:
                    CacheEntry(expiration=curr_resource_record._ttl,
                               authoritative=is_authoritative)
                })

            # Update A-Cache
            else:
                acache[curr_resource_record.
                       _dn]._dict[record_address] = CacheEntry(
                           expiration=curr_resource_record._ttl,
                           authoritative=is_authoritative)

        # Record type NS
        elif curr_resource_record._type == RR.TYPE_NS:
            # logger.info("Adding 'NS' record for {}: {}".format(curr_resource_record._dn, curr_resource_record._nsdn))
            logger.info(curr_resource_record)

            # Add to NS-Cache
            if curr_resource_record._dn not in nscache:
                nscache[curr_resource_record._dn] = OrderedDict({
                    curr_resource_record._nsdn:
                    CacheEntry(expiration=curr_resource_record._ttl,
                               authoritative=True)
                })

            # Update A-Cache
            else:
                nscache[curr_resource_record._dn][
                    curr_resource_record._nsdn] = CacheEntry(
                        expiration=curr_resource_record._ttl,
                        authoritative=True)

        # Record type CNAME
        elif curr_resource_record._type == RR.TYPE_CNAME:
            # logger.info("Adding 'CNAME' record for {}".format(curr_resource_record._dn))
            logger.info(curr_resource_record)

            cnamecache[curr_resource_record._dn] = CnameCacheEntry(
                curr_resource_record._cname,
                expiration=curr_resource_record._ttl)

        else:
            logger.info(curr_resource_record)

    logger.info(
        "Received {} resource records ({} answers, {} authoritative, {} additional)"
        .format(resource_record_quantity, len(rr_answers),
                len(rr_authoritative), len(rr_additional)))

    # if len(rr_answers):
    #     logger.info(rr_answers[0])
    # elif len(rr_authoritative):
    #     logger.info(rr_authoritative[0])

    logger.info("SEP3: ITERATIVE END  : '{}'  =>  '{}'".format(
        question_domain, nameserver))
    return rr_answers, rr_authoritative, rr_additional
Esempio n. 35
0
def resolveQuery(clientQueryHeader, clientQueryQE, RaiseException = False, glueMode = False):
    '''
    Resolve a query for a given QE
    The function can also be called resursivly with RaiseException = True, see later
    The function can additionally be called when querying the address of a glue record with glueMode = True, see later
    '''
    # Initialise reponses
    resolverQueryID = randint(1, 65535)
    resolverQueryHeader = Header(resolverQueryID, Header.OPCODE_QUERY, Header.RCODE_NOERR, qdcount=1)
    resolverReplyRRAuthority = []
    resolverReplyRRGlue = []
    resolverReplyRRCNAME = []
    usedNameServer = []
    
    # Copy client's query QE
    resolverQueryQE = QE(clientQueryQE._type, clientQueryQE._dn)
    
    # Set root nameserver as default
    queryNameServer = ROOTNS_IN_ADDR
    
    
    # Keep querying until return
    while True:
        print "\n------------------------------------\n"
        print "sending query of:",resolverQueryQE
        print "nameserver:", queryNameServer
        
        # Check if current query is cached
        (cacheType, cacheHeader, cacheRR) = checkCache(resolverQueryHeader, resolverQueryQE)
        
        # If A type cache exists, append its CNAME and return results
        if cacheType == 'a':
            print "Cache hit (A) for query: ", resolverQueryQE._dn
            cacheRR = resolverReplyRRCNAME + cacheRR
            cacheHeader._ancount = len(cacheRR)
            return cacheHeader, cacheRR
        
        # If CNAME cache exists, keep searching all its CNAME in cache
        elif cacheType == 'cname':
            print "Cache hit (CNAME) for query: ", resolverQueryQE._dn
            resolverReplyRRCNAME.append(cacheRR)
            cacheQueryQE = QE(clientQueryQE._type, cacheRR._cname)
            
            while cacheType != 'none':
                (cacheType, cacheHeader, cacheRR) = checkCache(Header(resolverQueryID, Header.OPCODE_QUERY, Header.RCODE_NOERR, qdcount=1), cacheQueryQE)
                
                # If A record is found in cache, return
                if cacheType == 'a':
                    print "Cache hit (A) for query: ", cacheQueryQE._dn
                    cacheRR = resolverReplyRRCNAME + cacheRR
                    cacheHeader._ancount = len(cacheRR)
                    return cacheHeader, cacheRR
                
                # If another CNAME is found, append it and keep searching next one in cache
                elif cacheType == 'cname':
                    print "Cache hit (A) for query: ", cacheQueryQE._dn
                    resolverReplyRRCNAME.append(cacheRR)
                    cacheQueryQE._dn = cacheRR._cname
                    
                # If no A record found in cache, break loop and start a query
                else:
                    print "Cache miss for query: ", cacheQueryQE._dn
                    break

            # Initialise query header, QE and name server
            resolverQueryID = randint(1, 65535)
            resolverQueryHeader = Header(resolverQueryID, Header.OPCODE_QUERY, Header.RCODE_NOERR, qdcount=1)
            resolverQueryQE._dn = cacheRR._cname
            queryNameServer = ROOTNS_IN_ADDR

        # No cached answer
        else:
            print "Cache miss for query: ", resolverQueryQE._dn
            pass


        # Keep querying a name server for twice before giving up and catch timeout for each query
        cs.sendto(resolverQueryHeader.pack()+resolverQueryQE.pack(), (queryNameServer, 53))
        try:
            resolverReplyID = 0
            while resolverQueryID != resolverReplyID:
                exceptionFlag = False
                cs.settimeout(1)
                (resolverReply, resolverReplyAddress, ) = cs.recvfrom(512)
                cs.settimeout(None)
                resolverReplyID = Header.fromData(resolverReply)._id

        except timeout:
            cs.settimeout(None)
            print "Target Name Server is not responding, attempt: 1/2"
            print "\nDouble timeout period and retrying..."
            
            # If last attempt failed, double the timeout period and try again
            cs.sendto(resolverQueryHeader.pack()+resolverQueryQE.pack(), (queryNameServer, 53))
            try:
                resolverReplyID = 0
                while resolverQueryID != resolverReplyID:
                    exceptionFlag = False
                    cs.settimeout(2)
                    (resolverReply, resolverReplyAddress, ) = cs.recvfrom(512)
                    cs.settimeout(None)
                    resolverReplyID = Header.fromData(resolverReply)._id
                break
            except timeout:
                cs.settimeout(None)
                print "Target Name Server is not responding, attempt: 2/2"
                exceptionFlag = True
    

        # If query finally failed, change queryNameServer accordingly
        if exceptionFlag == True:

            # If root name server is not reponsive, give up query
            if queryNameServer == ROOTNS_IN_ADDR:
                print "\nRoot name server is not responding, abandoning query"
                return Header(clientQueryHeader._id, clientQueryHeader._opcode, Header.RCODE_SRVFAIL, qdcount= clientQueryHeader._qdcount, qr=True, rd=clientQueryHeader._rd, ra=True), []
            
            
            # If other NS records exist, select another name server for the same zone
            elif len(resolverReplyRRAuthority) > 1:
               print "\nTLD or authoritative name server is not responding, finding alternatives"
               
               # Save the failed name server to prevent further use
               usedNameServer.append(queryNameServer)
               
               # Find an alternative name server
               for currentRRAuthority in resolverReplyRRAuthority:
                    if currentRRAuthority._type == RR.TYPE_NS:
                        for currentRRGlue in resolverReplyRRGlue:
                                if currentRRAuthority._nsdn == currentRRGlue._dn and inet_ntoa(currentRRGlue._inaddr) not in usedNameServer:
                                    queryNameServer = inet_ntoa(currentRRGlue._inaddr)
                                    resolverQueryID = randint(1, 65535)
                                    resolverQueryHeader = Header(resolverQueryID, Header.OPCODE_QUERY, Header.RCODE_NOERR, qdcount=1)
                                    break
                        break
        
            # Otherwise, give up query
            else:
                print "\nTLD or authoritative name server is not responding, no alternatives found"
                return Header(clientQueryHeader._id, clientQueryHeader._opcode, Header.RCODE_SRVFAIL, qdcount= clientQueryHeader._qdcount, qr=True, rd=clientQueryHeader._rd, ra=True), []


        # If query is successful, process resource records
        else:
            resolverReplyHeader = Header.fromData(resolverReply)
            print "\nResponse received:"
            print resolverReplyHeader
            resolverReplyRR = []
            offset = len(resolverQueryHeader.pack()+resolverQueryQE.pack())

            for currentRecordIndex in range(resolverReplyHeader._ancount + resolverReplyHeader._nscount + resolverReplyHeader._arcount):
                (currentRecord, currentRecordOffset) = RR.fromData(resolverReply, offset)
                resolverReplyRR.append(currentRecord)
                print currentRecord
                
                authoritativeFlag = True if resolverReplyHeader._aa == 1 else False
                
                # Save current record in cache
                saveToCache(currentRecord, authoritativeFlag)
                offset += currentRecordOffset


            # If answer exists, classify the answer section
            if resolverReplyHeader._ancount > 0:
            
                # If type A answer is found, return it with all its CNAME records
                if resolverReplyRR[0]._type == RR.TYPE_A:
                    print "\nAddress answer found"
                    resolverReplyRR = resolverReplyRR[0:resolverReplyHeader._ancount]
                    resolverReplyRR = resolverReplyRRCNAME + resolverReplyRR
                    resolverReplyHeader._ancount = len(resolverReplyRR)
                    return resolverReplyHeader, resolverReplyRR
                
                # If type CNAME answer is found, save to cache and send query for the CNAME
                elif resolverReplyRR[0]._type == RR.TYPE_CNAME:
                    print "\nCNAME found"
                    resolverReplyRRCNAME.append(resolverReplyRR[0])
                    resolverQueryID = randint(1, 65535)
                    resolverQueryHeader = Header(resolverQueryID, Header.OPCODE_QUERY, Header.RCODE_NOERR, qdcount=1)
                    resolverQueryQE._dn = resolverReplyRR[0]._cname
                    queryNameServer = ROOTNS_IN_ADDR

                # Discard other types of answers
                else:
                    print "Unknown answer type, please verify your query\n"
                    return Header(clientQueryHeader._id, clientQueryHeader._opcode, Header.RCODE_SRVFAIL, qdcount= clientQueryHeader._qdcount, qr=True, rd=clientQueryHeader._rd, ra=True), []


            # If no answer is found, keep querying with NS type records
            elif resolverReplyHeader._ancount == 0:
                    print "\nNo answer received, processing AUTHORITY and ADDITIONAL sections"
                    resolverReplyRRAuthority = resolverReplyRR[:resolverReplyHeader._nscount]
                    
                    # Filter glue records
                    resolverReplyRRGlue = []
                    for currentRRGlue in resolverReplyRR[-resolverReplyHeader._arcount:]:
                        if currentRRGlue._type == RR.TYPE_A:
                            resolverReplyRRGlue.append(currentRRGlue)

                    # Determine whether correct glue records are available
                    glueRecordFlag = False
                    for currentRRAuthority in resolverReplyRRAuthority:
                        if currentRRAuthority._type == RR.TYPE_NS:
                            for currentRRGlue in resolverReplyRRGlue:
                                # Correct glue record found, use the glue record for next query
                                if currentRRAuthority._nsdn == currentRRGlue._dn:
                                    glueRecordFlag = True
                                    queryNameServer = inet_ntoa(currentRRGlue._inaddr)
                                    resolverQueryID = randint(1, 65535)
                                    resolverQueryHeader = Header(resolverQueryID, Header.OPCODE_QUERY, Header.RCODE_NOERR, qdcount=1)
                    
                    
                    # If no glue record is found, determine whether correct authoritative records available
                    if glueRecordFlag == False:
                        
                        matchFlag = False
                        for currentRRAuthority in resolverReplyRRAuthority:
                            if currentRRAuthority._type == RR.TYPE_NS:
                                matchFlag = True
                    
                        # If correct authoritative records available, send query of its address
                        if matchFlag:
                            for currentRRAuthority in resolverReplyRRAuthority:
                                if currentRRAuthority._type == RR.TYPE_NS:
                                    try:
                                        (missingHeader, missingRR) = resolveQuery(clientQueryHeader, QE(dn = currentRRAuthority._nsdn), True)
                                        queryNameServer = inet_ntoa(missingRR[0]._inaddr)
                                        resolverQueryID = randint(1, 65535)
                                        resolverQueryHeader = Header(resolverQueryID, Header.OPCODE_QUERY, Header.RCODE_NOERR, qdcount=1)
                                        break
                                    except Exception:
                                        pass

                        # If no correct authoritative records available, query/answer is invalid (Most likely SOA type).
                        else:
                            # This flag is set only when this function is called recursively (i.e.querying for address of authoritative records)
                            if RaiseException:
                                print "\nInvalid query/answer type: Re-trying"
                                raise Exception("Invalid query/answer type: Re-trying")

                            # This flag is set only when this function is called for appending the ADDITIONAL section of given ANSWER and AUTHORITY sections
                            if glueMode:
                                print "\nInvalid answer type to the glue record query, cannot be cached or returned"
                                return resolverReplyHeader, []

                            # Client query is invalid, re-try until timeout
                            else:
                                print "\nInvalid query/answer type:  please verify your query"
                                sleep (1)
                                continue
            
            # DNS packet received from name server is corrupted, re-try
            else:
                print "\nDNS packet corrupted: Answer number is negative\nRe-trying...\n"
                continue