Exemple #1
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())
Exemple #2
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
Exemple #3
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
Exemple #4
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)
Exemple #5
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
Exemple #6
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
Exemple #7
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
Exemple #8
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))
Exemple #9
0
def queryCNAME(id, question, destination, data):
    addToCNameCache(data._dn, data._cname, data._ttl)
    newQuestion = QE(dn=data._cname)
    result = recursiveQuery(id, newQuestion, ROOTNS_IN_ADDR, True)

    cnameAnswer = result['answer']

    result['answer'] = RR_A(question._dn, cnameAnswer._ttl, cnameAnswer._addr)

    return result
Exemple #10
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)
Exemple #11
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
Exemple #12
0
def addAdditionalRecords(responseRRList):
    '''
    For a given set of query answers, search cache and append corresponding AUTHORITY and ADDITIONAL sections
    Resent query of an authority record if its glue record does not exist
    '''
    # Identify corresponding domain for AUTHORITY and ADDITIONAL sections
    targetDomain = responseRRList[-1]._dn.parent() if responseRRList[-1]._dn.parent() != None else "."

    while True:
    
        # If this domain exists in nscache
        if targetDomain in nscache:
        
            # Search in nscache and append matched results
            for currentNSCache in nscache[targetDomain].keys():
                responseRRAuthorityList.append(RR_NS(targetDomain, nscache[targetDomain][currentNSCache]._expiration, currentNSCache))
                responseHeader._nscount += 1
                
                # If corresponding glue record exists, append it
                if currentNSCache in acache:
                    for currentACache in acache[currentNSCache]._dict.keys():
                        responseRRGlueList.append(RR_A(currentNSCache, acache[currentNSCache]._dict[currentACache]._expiration, currentACache.toNetwork()))
                        responseHeader._arcount += 1
            
                # If corresponding glue record does not exist, send query of it
                else:
                    # Only query valid authoritative records, discard SOA type
                    if currentNSCache not in invalidRRAuthority:
                        print currentNSCache
                        print "\nOne or more additional glue record is missing, searching...."
                        (processHeader, processRR) = resolveQuery(clientQueryHeader, QE(dn=currentNSCache), glueMode = True)
                        for currentOne in processRR:
                            if currentOne._type == RR.TYPE_A:
                                responseRRGlueList.append(currentOne)
                                responseHeader._arcount += 1
                    else:
                        pass
            break
    
        # If this domain does not exist in nscache, try its parent domain
        else:
            targetDomain = targetDomain.parent() if targetDomain.parent() != None else  "."
            
    # Search and save authoritative records with invalid glue records (no A type)
    # Saved invalid authoritative records will not be queried again
    for currentGlue in responseRRGlueList:
        responseRRGlueDN.append(currentGlue._dn)
    for currentAuthority in responseRRAuthorityList:
        if currentAuthority._nsdn not in responseRRGlueDN:
            invalidRRAuthority.append(currentAuthority._nsdn)

    return responseRRAuthorityList, responseRRGlueList
Exemple #13
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)
Exemple #14
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
Exemple #15
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())
Exemple #16
0
def checkAuthorityRecords(id, question, data, seenCNAME):
    """
  If no additional record is found, check the authority records
  """
    filterAuthorityRecords(data)
    for authority in data['authority']:
        if authority._type != RR.TYPE_NS:
            continue
        newQuestion = QE(dn=authority._nsdn)
        result = recursiveQuery(id, newQuestion, ROOTNS_IN_ADDR, seenCNAME)
        if 'rcode' in result and result['rcode'] == Header.RCODE_NOERR:
            result1 = recursiveQuery(id, question,
                                     inet_ntoa(result['answer']._addr),
                                     seenCNAME)
            if result1['rcode'] == Header.RCODE_NOERR:
                return result1

        return {'rcode': Heade.RCODE_SRVFAIL}
Exemple #17
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)
Exemple #18
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()
Exemple #19
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
Exemple #20
0
    # ------------------------------------------------------------------

    # 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)

    # Only respond to A and CNAME queries
    if question._type != QE.TYPE_A:
        logger.info("Ignoring request of type other than A...")
        continue

    answers = []
    authorities = []
    additionals = []

    # Resolve question entry, with a time limit of 60 seconds
    try:
Exemple #21
0
        if rrec._type == RR.TYPE_CNAME:
            cnamecache[rrec._dn] = CnameCacheEntry(rrec._cname, expiration=rrec._ttl)
        if rrec._type == RR.TYPE_NS:
            if rrec._dn not in nscache:
                nscache[rrec._dn] = OrderedDict([(rrec._nsdn, CacheEntry(expiration=rrec._ttl, authoritative=True))])
            else:
                nscache[rrec._dn][rrec._nsdn] = CacheEntry(expiration=rrec._ttl, authoritative=True)

        offset += offset_inc

    # If answer exist, send answer (and all RRs from last response received) to client.
    # Else, check authority & additional section to determine next DNS name server to send query.
    if response_header._ancount > 0:
        if response_rrs[0]._type == RR.TYPE_CNAME:
            print "CNAME found - starting search for IP address of canonical name ", response_rrs[0]._cname
            cname_qe = QE(dn=response_rrs[0]._cname)
            (return_header, return_rrs) = get_ip_addr(cname_qe)

            return_header._ancount += 1
            return_rrs.insert(0, response_rrs[0])
            return return_header, return_rrs

        else:
            return response_header, response_rrs

    authority_rrs = response_rrs[:response_header._nscount]
    additional_rrs = response_rrs[-response_header._arcount:]
    tried = []
    for ns in authority_rrs:
        if ns._type == RR.TYPE_NS:
            for add in additional_rrs:
Exemple #22
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)
Exemple #23
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)
  
Exemple #24
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
Exemple #25
0
def printQuestion(data, address):
    header = Header.fromData(data)
    qe = QE.fromData(data, len(header))
    print "Looking up "
    print qe._dn
    print address
Exemple #26
0
def parseQuestions(data, offset, noOfQuestions):
    questions = []
    for i in range(noOfQuestions):
        questions.append(QE.fromData(data, offset))
        offset += len(questions[i])#increase offset by length of previous question
    return (offset, questions)
Exemple #27
0
def parseQuery(data):
    '''
    Parse a DNS query with fromData() provided in Header and QE classes
    '''
    return Header.fromData(data), QE.fromData(data, 12)