Esempio n. 1
0
def fake_query(qname,
               rdtype=rdatatype.A,
               rdclass=rdataclass.IN,
               count=1,
               fake_txt=False):
    """Fake a DNS query, returning count responses to the request

       Three kinds of lookups are faked:
       1. A query for A records for a service will return the count
          as requested in the test. This simulates lookups for the
          ipa-ca A record. To force a difference in responses one can
          vary the count.
       2. AAAA records are not yet supported, return no answer
       3. TXT queries will return the Kerberos realm

       fake_txt will set an invalid Kerberos realm entry to provoke a
       warning.
    """
    m = message.Message()
    if rdtype == rdatatype.A:
        fqdn = DNSName(qname)
        fqdn = fqdn.make_absolute()

        answers = Answer(fqdn,
                         rdataclass.IN,
                         rdatatype.A,
                         m,
                         raise_on_no_answer=False)

        rlist = rrset.from_text_list(fqdn, 86400, rdataclass.IN, rdatatype.A,
                                     gen_addrs(count))

        answers.rrset = rlist
    elif rdtype == rdatatype.AAAA:
        raise NoAnswer(response=Response('no AAAA'))
    elif rdtype == rdatatype.TXT:
        if fake_txt:
            realm = 'FAKE_REALM'
        else:
            realm = m_api.env.realm
        qname = DNSName('_kerberos.' + m_api.env.domain)
        qname = qname.make_absolute()

        answers = Answer(qname,
                         rdataclass.IN,
                         rdatatype.TXT,
                         m,
                         raise_on_no_answer=False)

        rlist = rrset.from_text_list(qname, 86400, rdataclass.IN,
                                     rdatatype.TXT, [realm])

        answers.rrset = rlist

    return answers
Esempio n. 2
0
def make_notify(qname):
    """
    according to RFC1035 and RFC1996
    """
    qname = name.from_text(qname)
    rdtype = rdatatype.SOA
    rdclass = rdataclass.IN

    m = message.Message()
    m.flags |= dns.flags.AA
    m.flags |= dns.opcode.to_flags(dns.opcode.NOTIFY)
    m.find_rrset(m.question, qname, rdclass, rdtype, create=True, force_unique=True)
    return m
Esempio n. 3
0
def query_uri(hosts):
    """
    Return a list containing two answers, one for each uri type
    """
    answers = []
    if version.MAJOR < 2 or (version.MAJOR == 2 and version.MINOR == 0):
        m = message.Message()
    elif version.MAJOR == 2 and version.MINOR > 0:
        m = message.QueryMessage()  # pylint: disable=E1101
        m = message.make_response(m)  # pylint: disable=E1101

    rdtype = rdatatype.URI
    for name in ('_kerberos.', '_kpasswd.'):
        qname = DNSName(name + m_api.env.domain)
        qname = qname.make_absolute()
        if version.MAJOR < 2:
            # pylint: disable=unexpected-keyword-arg
            answer = Answer(qname,
                            rdataclass.IN,
                            rdtype,
                            m,
                            raise_on_no_answer=False)
            # pylint: enable=unexpected-keyword-arg
        else:
            if version.MAJOR == 2 and version.MINOR > 0:
                question = rrset.RRset(qname, rdataclass.IN, rdtype)
                m.question = [question]
            answer = Answer(qname, rdataclass.IN, rdtype, m)

        rl = []
        for host in hosts:
            rlist = rrset.from_text_list(
                qname, 86400, rdataclass.IN, rdatatype.URI, [
                    '0 100 "krb5srv:m:tcp:%s."' % host,
                    '0 100 "krb5srv:m:udp:%s."' % host,
                ])
            rl.extend(rlist)
        answer.rrset = rl
        answers.append(answer)
    return answers
Esempio n. 4
0
def gethostbyname(hostname, timeout):
    """ Translate a host name to IPv4 address.

    Currently this method contains an example. You will have to replace
    this example with the algorithm described in section
    5.3.3 in RFC 1034.

    Args:
        hostname (str): the hostname to resolve

    Returns:
        (str, [str], [str]): (hostname, aliaslist, ipaddrlist)
    """
    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    sock.settimeout(timeout)

    # Create and send query
    question = message.Question(hostname, Type.A, Class.IN)
    header = message.Header(9001, 0, 1, 0, 0, 0)
    header.qr = 0
    header.opcode = 0
    header.rd = 1
    query = message.Message(header, [question])
    sock.sendto(query.to_bytes(), ('198.41.0.4', 53))
    # 198.41.0.4

    # Receive response
    data = sock.recv(512)
    response = message.Message.from_bytes(data)
    # for byte in data:
    #     print [byte]

    # Get data
    for question in response.questions:
        print 'question:', question.qname, Type.by_value[
            question.qtype], Class.by_value[question.qclass]

    addresses = list()
    for answer in response.answers:
        if answer.type_ == Type.A:
            addresses.append(answer.rdata.data)
        print 'answer:', answer.rdata.data, Type.by_value[
            answer.type_], Class.by_value[
                answer.class_], answer.name, answer.ttl

    for authority in response.authorities:
        print 'authority:', authority.rdata.data, Type.by_value[
            authority.type_], Class.by_value[
                authority.class_], authority.name, authority.ttl

    aliases = list()
    for additional in response.additionals:
        if additional.type_ == Type.CNAME:
            aliases.append(additional.rdata.data)
        print 'additional:', additional.rdata.data, Type.by_value[
            additional.type_], Class.by_value[
                additional.class_], additional.name, additional.ttl

    # print('aliases:')
    # for alias in aliases:
    #     print(alias)
    # print('addresses:')
    # for address in addresses:
    #     print(address)

    return hostname, aliases, addresses
Esempio n. 5
0
    def run(self):
        logging.debug('running with %s and %s', self.args, self.kwargs)

        # server socket is bound to an internal (127) address to provide resolution inside the node
        s_server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        s_server.bind((self.ip, self.port))
        logging.debug('server socket bound:%s, %s and %d', s_server, self.ip,
                      self.port)

        #client socket is bound to the standard address so that it can be used to make upstream requests
        s_client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        s_client.bind(('', 0))
        logging.debug('client socket bound:%s,  %s', s_client,
                      str(s_client.getsockname()))

        # c_*_resolver interacts upstream
        # s_*_resolver interacts with local processes serving them dns requests
        threads = [
            procinspect.ProcInspect(name='proc_inspect',
                                    args=(self.proc_inspect_req,
                                          self.proc_inspect_rsp),
                                    kwargs={}),
            TxResolver(name='c_tx_resolver',
                       args=(s_client, self.client_tx_queue),
                       kwargs={}),
            RxResolver(name='c_rx_resolver',
                       args=(s_client, self.client_rx_queue),
                       kwargs={}),
            TxResolver(name='s_tx_resolver',
                       args=(s_server, self.server_tx_queue),
                       kwargs={}),
            RxResolver(name='s_rx_resolver',
                       args=(s_server, self.server_rx_queue),
                       kwargs={}),
        ]

        for t in threads:
            logging.debug('starting thread: %s', t.name)
            t.start()

        #local vars used in select processing and maintaining state of dns queries.
        request_state = {}
        #inputs = [s_server, s_client]
        inputs = []
        outputs = []
        timeout = 0.2

        # select on sockets with timeout, assuming that the recv will fire before the select,
        # may need to change to some other type of inter thread signalling as this select may only work on timeout
        while True:
            readable, writable, exceptional = select.select(
                inputs, outputs, inputs, timeout)
            for s in readable:
                logging.debug('select readable: %s', s)
            for s in exceptional:
                logging.debug('select exceptional: %s', s)
            if not (readable or writable or exceptional):
                #logging.debug('select timeout:')
                pass

            # with viable data on a socket or a timeout attempt to check for the various
            # async conditions

            # #
            # # RESPONSE PROCESSING
            # #
            # [>> 1] check for any proc_inspect_responses
            #
            #  if there is a proc inspect response --
            #  State Transition
            #  Wait_for_first_response -> Wait_for_response_A
            #  OR
            #  Wait_for_response_B -> Idle

            try:
                qitem = self.proc_inspect_rsp.get_nowait()
            except queue.Empty:
                pass
            else:
                lport, mid, pi_rsp = qitem
                ###logging.debug('client_rxqueue: lport %d, m.id %d, proc_ident_rsp ,%s', lport,mid,pi_rsp)

                #find the address of the original requesting process from the stored state
                if mid in request_state:
                    client_addr, t_id, t_question, lp, dns_rsp, _ = request_state[
                        m.id]
                    if lp != lport:
                        logging.debug(
                            'client_rxqueue: lport %d not found in request_state, m.id %d',
                            lport, mid)
                    else:
                        if dns_rsp is not None:
                            #1 send the dns response to client ( TODO this needs to be modified to use localhost address based on procinfo)
                            ###logging.debug('client_rxqueue: sending to server_tx %s, %d,proc_info %s',client_addr, m.id,m)
                            server_qitem = (client_addr, m)
                            self.server_tx_queue.put(server_qitem)
                            del request_state[m.id]
                        else:
                            # store pi_rsp and wait for dns_rsp to arrive
                            request_state[mid] = (client_addr, t_id,
                                                  t_question, lport, dns_rsp,
                                                  pi_rsp)
                else:
                    logging.debug(
                        'client_rxqueue: m.id not found, message dumped - %d',
                        m.id)

            # # [>> 2]  is there a dns response from upstream, find the address of the local process
            # that requested and process it downstream
            # check client_rx_queue and sent to server_tx_queue
            #
            #  if there is a proc inspect response --
            #  State Transition
            #  Wait_for_first_response -> Wait_for_response_B
            #  OR
            #  Wait_for_response_A -> Idle
            try:
                qitem = self.client_rx_queue.get_nowait()
            except queue.Empty:
                pass
            else:
                addr, m = qitem
                ###logging.debug('client_rxqueue: addr %s, m.id ,%d, m.quest, %s, m.ans, %s', addr, m.id, m.question, m.answer)

                #find the address of the original requesting process from the stored state
                if m.id in request_state:
                    client_addr, t_id, t_question, lport, _, pi_rsp = request_state[
                        m.id]
                    dns_rsp = m

                    #
                    # POLICY CHANGE
                    # if the requested name resultion is in the target_map insert this as the answer instead of
                    # using the upstream answer.
                    # TODO: assuming its an A record we are looking for
                    q = m.question[0].name
                    dns_q_name = str(q).rstrip('.')
                    ###logging.debug(' dns_q_name: %s, %s', dns_q_name, q)
                    if dns_q_name in self.target_map:
                        target_result = self.target_map[dns_q_name][
                            'hostlocal']
                        dns_result = dns.rrset.from_text(
                            dns_q_name + '.', 300, 'in', 'a', target_result)
                        logging.debug(' dns_result: %s', dns_result)
                        dns_rsp.answer = [dns_result]

                    #if have both responses act, otherwise wait for second response
                    if pi_rsp is not None:
                        #1 send the dns response to client ( TODO this needs to be modified to use localhost address based on procinfo)
                        ###logging.debug('client_rxqueue: sending to server_tx %s, %d,proc_info %s',client_addr, m.id,m)
                        server_qitem = (client_addr, m)
                        self.server_tx_queue.put(server_qitem)
                        del request_state[m.id]
                    else:
                        # copy the dns response into state and wait for proc_info response
                        request_state[m.id] = (client_addr, t_id, t_question,
                                               lport, dns_rsp, pi_rsp)
                else:
                    logging.debug(
                        'client_rxqueue: m.id not found, message dumped - %d',
                        m.id)

            # #
            # # REQUEST PROCESSING
            # #
            # # [>> 3]  is there a dns query from a local process, process it upstream
            # check server_rx_queue and sent to client_tx_queue
            #
            # State Transition Idle->Wait_for_first_response

            # -- this is the initial state transition, a client process has requested a dns resolution,
            #    initiate a request to get process info details,
            #    initiate an upstead DNS request based on the received info.
            #    transition to waiting for responses for either of these two requests.
            try:
                qitem = self.server_rx_queue.get_nowait()
            except queue.Empty:
                pass
            else:
                addr, m = qitem
                lip, lport = addr
                ###logging.debug('server_rxqueue: addr %s, m.id %d, m.quest %s, m.ans, %s', addr, m.id, m.question, m.answer)

                # issue a proc identification request based on localport
                self.proc_inspect_req.put((lport, m.id))

                # maintain a mapping of dns query id to requesting address so when response comes back
                # the response can be sent downstream to the proper process
                if m.id in request_state:
                    logging.debug(
                        'server_rxqueue: already in request state should not be rx ,%s',
                        m.id)
                request_state[m.id] = (
                    addr, m.id, m.question, lport, None, None
                )  # (first NONE is for DNS resposne, Second for Proc Response)

                # issue a generic dns request (TODO this is hardcoded and needs to change)
                dns_res_addr = ('44.1.13.1', 53)
                #dns_res_addr  = ('10.33.5.10',53)
                client_qitem = (dns_res_addr, m)

                ###logging.debug('server_rxqueue: sending to client_tx %s, %d',dns_res_addr, m.id)
                self.client_tx_queue.put(client_qitem)

            #  [>> 4 ] is there a ????
            #
            try:
                msg = self.resolver_req.get_nowait()
            except queue.Empty:
                pass
            else:
                logging.debug('resolver request received: %s', msg)
                if msg.req_type == "resolver_policy_add":
                    logging.debug('resolver policy add:')
                    args_dict = msg.args_dict
                    resolver_args = args_dict['args']
                    resolver_kwarg = args_dict['kwargs']
                    self.target_map = resolver_kwarg['target_map']
                    logging.debug('target_map: %s', self.target_map)
                    response = message.Message(id=msg.id, data='ok')
                    self.resolver_rsp.put(response)
                else:
                    response = message.Message(
                        id=msg.id, data='nok:   message type not processed')
                    self.resolver_rsp.put(response)

        #cleanup threads
        for t in threads:
            logging.debug('joining thread', t.name)
            t.join()
        time.sleep(2)
        logging.debug('exiting')
        return
    def send_queries(self):
        # Create query
        question = message.Question(self.SNAME, Type.A, Class.IN)
        header = message.Header(42, 0, 1, 0, 0, 0)
        header.qr = 0
        header.opcode = 0
        header.rd = 1
        query = message.Message(header, [question])
        query_bytes = query.to_bytes()

        def send_query(ind, server_data):
            server_name = server_data[0]
            server_ip = server_data[1]
            sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            sock.settimeout(self.timeout)
            if not server_ip:
                if server_name:
                    try:
                        a_rr = self.CACHE.lookup(server_name, Type.A,
                                                 Class.IN)[0]
                        server_ip = a_rr.rdata.data
                    except IndexError:
                        ns_resolver = Resolver(self.caching, self.CACHE)
                        _, addresses, _ = ns_resolver.gethostbyname(
                            server_name)
                        if addresses:
                            server_ip = addresses[0]
                        else:
                            results[ind] = -1
                            return
                else:
                    results[ind] = -1
                    return

            try_count = 0
            max_tries = 3
            while try_count < max_tries:
                try:
                    sock.sendto(query_bytes, (server_ip, 53))
                except:
                    print('Unable to send to: ' + str(server_ip))
                try:
                    results[ind] = {
                        'data': sock.recv(512),
                        'server': server_data
                    }
                    return
                except socket.timeout:
                    try_count += 1
                    print('timeout ' + str(try_count))
            results[ind] = -1

        results = list()
        for ind, server in enumerate(self.SLIST):
            results.append(None)
            Thread(target=send_query, args=(ind, server)).start()

        while True:
            if not all(r in [None, -1] for r in results):  # if result is found
                response = None
                server_data = None
                for i, r in enumerate(results):
                    if r not in [None, -1]:
                        response = message.Message.from_bytes(r['data'])
                        server_data = r['server']
                        results[i] = -1
                        break
                print('--------\nserver: ' + str(server_data[0]) + ', ' +
                      str(server_data[1]))
                self.show_response(response)
                self.analyze_response(response)
                if self.addresses:
                    return
            if all(r == -1 for r in
                   results):  # all results timed out or didn't return anything
                raise ResolverException(RCode.NXDomain)
            sleep(0.01)
Esempio n. 7
0
def fake_query(qname, rdtype=rdatatype.A, rdclass=rdataclass.IN, count=1,
               fake_txt=False):
    """Fake a DNS query, returning count responses to the request

       Three kinds of lookups are faked:
       1. A query for A/AAAA records for a service will return the count
          as requested in the test. This simulates lookups for the
          ipa-ca A/AAAA record. To force a difference in responses one can
          vary the count.
       2. TXT queries will return the Kerberos realm

       fake_txt will set an invalid Kerberos realm entry to provoke a
       warning.
    """
    if version.MAJOR < 2 or (version.MAJOR == 2 and version.MINOR == 0):
        m = message.Message()
    elif version.MAJOR == 2 and version.MINOR > 0:
        m = message.QueryMessage()
        m = message.make_response(m)
    if rdtype in (rdatatype.A, rdatatype.AAAA):
        fqdn = DNSName(qname)
        fqdn = fqdn.make_absolute()

        if version.MAJOR < 2:
            # pylint: disable=unexpected-keyword-arg
            answers = Answer(fqdn, rdataclass.IN, rdtype, m,
                             raise_on_no_answer=False)
            # pylint: enable=unexpected-keyword-arg
        else:
            if version.MAJOR == 2 and version.MINOR > 0:
                question = rrset.RRset(fqdn, rdataclass.IN, rdtype)
                m.question = [question]
            answers = Answer(fqdn, rdataclass.IN, rdtype, m)

        rlist = rrset.from_text_list(fqdn, 86400, rdataclass.IN,
                                     rdtype, gen_addrs(rdtype, count))

        answers.rrset = rlist
    elif rdtype == rdatatype.TXT:
        if fake_txt:
            realm = 'FAKE_REALM'
        else:
            realm = m_api.env.realm
        qname = DNSName('_kerberos.' + m_api.env.domain)
        qname = qname.make_absolute()

        if version.MAJOR < 2:
            # pylint: disable=unexpected-keyword-arg
            answers = Answer(qname, rdataclass.IN, rdatatype.TXT, m,
                             raise_on_no_answer=False)
            # pylint: enable=unexpected-keyword-arg
        else:
            if version.MAJOR == 2 and version.MINOR > 0:
                question = rrset.RRset(qname, rdataclass.IN, rdtype)
                m.question = [question]
            answers = Answer(qname, rdataclass.IN, rdatatype.TXT, m)

        rlist = rrset.from_text_list(qname, 86400, rdataclass.IN,
                                     rdatatype.TXT, [realm])

        answers.rrset = rlist

    return answers