Exemplo n.º 1
0
def resolve_query(header_id, initial_domain_name, domain_name, current_ip, initial_ip, is_cname):

    print("iteration")
    '''
    This is the main "powerhouse" function that serves a client request. Upon receiveing a domain name, it first checks
    if it is contained in tha cache. If the cache contains information about the requested domain name, the function
    returns it. The function will recursively search for the domain name asked for as specified in the lecture notes.
    Cache also helps reducing queries by providing sub-domains for a domain name.

    :param header_id: ID of the initial client query
    :param initial_domain_name: the domain name the client was requesting
    :param domain_name: the domain name this function is currently trying to resolve
    :param current_ip: the current IP address we want to send a follow-up query to
    :param initial_ip: the first IP address that our LNS sends a query to (always the Root Server in our case)
    :param is_cname:
    :return: a dictionary of lists containing RRs categorized by the section they belong to in the packet
    '''

    global acache
    global cnamecache
    global nscache
    global global_cname

    rr_return = {'answers': [], 'authorities': [], 'additional': [], 'additional_A': []}

    # Only for the very first iteration (where domain_name == initial_domain_name) check if the requested domain name
    # is inside the cache
    if acache.contains(domain_name) and domain_name == initial_domain_name and not global_cname:
        print("containsx")
        # Get the IP and ttl from the cache and construct an RR_A object for answer section
        ip = acache.getIpAddresses(domain_name)
        ttl = acache.getExpiration(domain_name, ip[0])
        rr_answer = RR_A(domain_name, ttl, ip[0]) #ip[0].toNetwork() if ip[0] is not in byte form

        # Append the RR_A into the dictionary's answer section
        rr_return['answers'].append(rr_answer)

        # Constructing the authority section by looping through all possible sub-domains of domain_name and get the
        # highest-qualified one that exists in the nscache
        lowest_domain = get_subdomain_order(domain_name)
        for i in range(len(lowest_domain)):
            this_domain = DomainName(lowest_domain[i])
            if nscache.contains(this_domain):

                # Get the list of name servers associated with this domain name
                something = nscache.get(this_domain)
                for j in range(len(something)):

                    # For each name server associated with this domain name, retrieve the ttl and construct an
                    # RR_NS object and add it to the authority section of the dictionary
                    ttl = something[j][1]
                    nsdn = nscache.get_nsdn(this_domain)
                    rr_authorities = RR_NS(this_domain, ttl, nsdn[j])
                    rr_return['authorities'].append(rr_authorities)

                    # Check the IP address of all the name servers in the authority section, create an RR_A object and
                    # add it to the additional section of the dictionary
                    if acache.contains(nsdn[j]):
                        this_ip = acache.getIpAddresses(nsdn[j])
                        this_ttl = acache.getExpiration(nsdn[j], this_ip[0])
                        rr_additional = RR_A(nsdn[j], this_ttl, this_ip[0])
                        rr_return['additional'].append((rr_additional))

                # If we found an existing sub-domain, look no further
                break
        return rr_return

    # During the first iteration check what is the highest-qualified sub-domain that exists in the cache. If we find such
    # sub-domain, jump straight to that sub-domain's IP address instead of doing more queries and causing traffic
    if current_ip == initial_ip and not is_cname and not global_cname:
        lowest_domain = get_subdomain_order(domain_name)
        for i in range(len(lowest_domain)):
            this_domain = DomainName(lowest_domain[i])
            if nscache.contains(this_domain):

                # Once we find that sub-domain, retrieve its IP address from the cache and recursively call
                # serve_request() using the new IP address as target
                nsdn = nscache.get_nsdn(this_domain)
                next_ip = acache.getIpAddresses(nsdn[0])
                return resolve_query(header_id, initial_domain_name, domain_name, next_ip, initial_ip, is_cname)

    # Construct the packet
    send_packet_header = Header(header_id, 0, 0, 1)
    send_packet_question = QE(1, domain_name)
    send_packet = send_packet_header.pack() + send_packet_question.pack()

    # Send the packet to the target IP address and wait for an answer
    cs.sendto(send_packet, (current_ip, 53))
    reply, a = cs.recvfrom(512)

    # Parse the reply and construct the RRs
    header_record = parse_header(reply)
    rr = get_records(reply, header_record['ancount'], header_record['nscount'])

    # Cache all the RRs; this stage is only reached when these RRs are not duplicate, otherwise we would have reached
    # them before, without needing to cause traffic
    if header_record['ancount'] != 0: #len(rr['answers']) != 0:
        for i in rr['answers']:
            if i._type == 1:
                acache.put(domain_name, i._addr, i._ttl)
            elif i._type == 5:
                cnamecache.put(domain_name, i._cname, i._ttl)

    if header_record['nscount'] != 0:
        for i in rr['authorities']:
            if i._type == 2:
                nscache.put(i._dn, i._nsdn, i._ttl, True)

    if len(rr['additional']) != 0:
        for i in rr['additional']:
            if i._type == 1:
                acache.put(i._dn, i._addr, i._ttl)

    '''print("acache is:")
    print(acache.__str__())
    print("nscache is:")
    print(nscache.__str__())
    print("cnamecache is:")
    print(cnamecache.__str__())'''

    # Main recursive part
    try:
        # If there is no answer section and there is an additional section, make the same query targetting the first
        # RR_A in additional section
        if len(rr['additional']) != 0:
            next_ip = bin_to_str(rr['additional_A'].pop(0)._addr)
            return resolve_query(header_id, initial_domain_name, domain_name, next_ip, initial_ip, is_cname)

         # If we have an answer section
        elif header_record['ancount'] != 0:

            # IF we found the domain name the client originally requested, return thr records
            if initial_domain_name == domain_name:
                return rr

            # If we found a domain name from an authority section, we target this new domain's IP address with our query
            else:
                next_ip = bin_to_str(rr['answers'][0]._addr)
                return resolve_query(header_id, initial_domain_name, initial_domain_name, next_ip, initial_ip, is_cname)

        # If there is no answer and no RR_A additional section, we need to search for a domain name of the first RR_NS
        # from the authority section recursively
        else:
            next_domain_name = rr['authorities'][0]._nsdn
            return resolve_query(header_id, initial_domain_name, next_domain_name, ROOTNS_IN_ADDR, initial_ip, is_cname)

    # If the server we send a question to does not respond, go ask the next server in the additional section
    except timeout:
        next_ip = bin_to_str(rr['additional_A'].pop(0)._addr)
        return resolve_query(header_id, initial_domain_name, initial_domain_name, next_ip, initial_ip, is_cname)
Exemplo n.º 2
0
def recurser(question, ipQuerried):
    queryHeader = Header.fromData(question)
    queryQE = QE.fromData(question, queryHeader.__len__())
    if acache.contains(queryQE._dn):
        ips = acache.getIpAddresses(queryQE._dn)
        foundRRA = 0
        for ip in ips:
            ttl = acache.getExpiration(queryQE._dn, ip) - int(time())
            if ttl < 0:
                # too late for this record
                acache.cache.pop(queryQE._dn)
            else:
                answers.append(RR_A(queryQE._dn, ttl, inet_aton(ip)))
                foundRRA = 1

        if foundRRA is 1:
            newHeader = Header(queryHeader._id, 0, 0, 1, ancount=len(answers))
            newQE = QE(dn=queryQE._dn)
            return newHeader.pack() + newQE.pack()

    elif cnamecache.contains(queryQE._dn):
        cn = cnamecache.getCanonicalName(queryQE._dn)
        ttl = cnamecache.getCanonicalNameExpiration(queryQE._dn) - int(time())
        if ttl < 0:
            cnamecache.cache.pop(queryQE._dn)
        else:
            newHeader = Header(queryHeader._id, 0, 0, 1)
            newQE = QE(dn=cn)
            reply = recurser(newHeader.pack() + newQE.pack(), ROOTNS_IN_ADDR)
            if reply != None:
                answers.append(RR_CNAME(queryQE._dn, ttl, cn))
                return reply

    try:
        cs.sendto(question, (ipQuerried, 53))
        (
            nsreply,
            server_address,
        ) = cs.recvfrom(2048)  # some queries require more space
    except timeout:
        return None
    if len(nsreply) < 43:
        return None  # handle case where there is an empty response

    # Store these for later use when we want to solve CNAMEs or NSs
    queryHeader = Header.fromData(nsreply)
    queryQE = QE.fromData(nsreply, queryHeader.__len__())
    originalQ = str(queryQE).split("IN")[0].strip()

    offset = queryHeader.__len__() + queryQE.__len__()

    # We'll need these for parsing, trust me
    minRRLineLen = len(nsreply) - offset - 1
    rrCounter = 0
    nsAuthorities = []
    rra = []
    cnames = []
    queryRRTuples = []

    # Parsing all returned RRs
    while minRRLineLen < len(nsreply) - offset:
        # Get next glue line
        auxRRline = RR.fromData(nsreply, offset)

        # Append to RR list, update offset
        queryRRTuples.append(auxRRline)
        offset += queryRRTuples[rrCounter][1]

        queryRR = queryRRTuples[rrCounter][0]
        if queryRR.__class__ == RR_NS:
            nsAuthorities.append(queryRR)
        elif queryRR.__class__ == RR_A:
            rra.append(queryRR)
        elif queryRR.__class__ == RR_CNAME:
            cnames.append(queryRR)

        # Update minimum line length for safety stop
        if minRRLineLen > auxRRline[1]: minRRLineLen = auxRRline[1]
        rrCounter += 1

    # Start the handling of RRs

    # Case where we only got NS back
    if len(rra) == 0 and len(cnames) == 0:
        for auth in nsAuthorities:
            reply = recurser(question, str(auth._nsdn))
            return reply

    # Cache NS for later
    if len(nsAuthorities) > 0:
        for ns in nsAuthorities:
            nscache.put(ns._dn,
                        ns._nsdn,
                        ns._ttl + int(time()),
                        authoritative=True)

    # Cache CNAMEs for later and querry them
    if len(cnames) > 0:
        for queryRR in cnames:
            if cnamecache.contains(queryRR._dn) == False:
                cnamecache.put(queryRR._dn, queryRR._cname,
                               queryRR._ttl + int(time()))
            answers.append(queryRR)

            newHeader = Header(randint(1, 65000),
                               Header.OPCODE_QUERY,
                               Header.RCODE_NOERR,
                               qdcount=1)
            newQE = QE(dn=queryRR._cname)
            newQuery = newHeader.pack() + newQE.pack()

            reply = recurser(newQuery, ROOTNS_IN_ADDR)
            return reply

    # Cache all RR_As for later, look if we got the one we are looking for
    if len(rra) > 0:
        for queryRR in rra:
            if acache.contains(queryRR._dn) == False:
                acache.put(queryRR._dn,
                           inet_ntoa(queryRR._addr),
                           queryRR._ttl + int(time()),
                           authoritative=True)
            parts = queryRR.__str__().split("A")
            ip = parts[len(parts) - 1].strip()

            # Found required answer
            if queryRR._dn == originalQ:

                # Add all answers
                counter = 0
                while counter < len(rra):
                    if rra[counter]._dn == originalQ:
                        answers.append(rra[counter])
                        rra.pop(counter)
                    counter += 1
                return nsreply
            else:
                reply = recurser(question, ip)
                return reply
Exemplo n.º 3
0
            append_packet = resolve_query(header_id, dn, dn, ROOTNS_IN_ADDR, ROOTNS_IN_ADDR, False)

            # Append the answer sections to the final response packet
            for j in append_packet['answers']:
                new_packet['additional'].append(j)

    # Construct the answer, authority and additional sections from 'packet' dictionary
    response_packet_RR_answer = bytes()
    response_packet_RR_authorities = bytes()
    response_packet_RR_additional = bytes()
    for i in new_packet['answers']:
        response_packet_RR_answer += i.pack()
    for i in new_packet['authorities']:
        response_packet_RR_authorities += i.pack()
    for i in new_packet['additional']:
        response_packet_RR_additional += i.pack()

    # From the received dictionary, construct the header and the question section
    response_packet_header = Header(header_id, 0, 0, 1, len(new_packet['answers']), len(new_packet['authorities']),
                                    len(new_packet['additional']), 1, 1, 0, 1, 1)
    response_packet_question = QE(1, initial_qe_dn)

    # Construct the final packet and send it back to the client
    response_packet_RR = bytes()
    response_packet_RR = response_packet_RR_answer + response_packet_RR_authorities + response_packet_RR_additional
    response_packet = response_packet_header.pack() + response_packet_question.pack() + response_packet_RR
    ss.sendto(response_packet, client_address)


    
Exemplo n.º 4
0
    else:

        # Saving all this data for response reconstrucion later
        question = data
        ip = ROOTNS_IN_ADDR
        initialHeader = Header.fromData(data)
        initialQE = QE.fromData(data, initialHeader.__len__())
        initialId = initialHeader._id

        # The call to solve the main query
        nsreply = recurser(question, ip)

        if nsreply == None:
            # Respond with name error
            newHeader = Header(initialId, 0, Header.RCODE_NAMEERR, 1)
            response = newHeader.pack() + initialQE.pack()
        else:
            # Save these because we were using globals (never a good practice - too late now, sue me)
            finalAns = []
            finalAns.extend(answers)

            responseRRA = ""
            for ans in finalAns:
                responseRRA += ans.pack()
                foundParent = 0
                parent = ans._dn

                # Finding the NS most highly-qualified domain parent for returned DomainNames
                # These are definitely found in cache as we already parsed them in the main query
                while foundParent == 0:
                    parent = parent.parent()