def process_dns_message(self, dns_cb, cb_args, dst_id, r_cesid="", naptr_list=[]): """ Enforce rate limit on DNS NAPTRs served by CETP Engine """ try: if not self.dns_threshold_exceeded(): dns_q, addr = cb_args src_ip, src_port = addr key = (host.KEY_HOST_IPV4, src_ip) if not self.host_table.has(key): self._logger.error( "Sender IP '{}' is not a registered host".format( src_ip)) return host_obj = self.host_table.get(key) src_id = host_obj.fqdn #self._logger.info("Connection from '{}'->'{}'".format(src_id, dst_id)) if self.has_connection(src_id, dst_id): conn = self.get_connection(src_id, dst_id) lpip = conn.lpip response = dnsutils.make_response_answer_rr( dns_q, dst_id, dns.rdatatype.A, lpip, rdclass=1, ttl=120, recursion_available=True) dns_cb(dns_q, addr, response) else: self._execute_cetp_policies(dns_cb, cb_args, src_id, dst_id, r_cesid, naptr_list) except Exception as ex: self._logger.info( "Exception in process_dns_message(): '{}' ".format(ex)) return
def dns_process_rgw_wan_soa(self, query, addr, cback): """ Process DNS query from public network of a name in a SOA zone """ fqdn = query.fqdn rdtype = query.question[0].rdtype # Initialize to None to prevent AttributeError query.reputation_resolver = None query.reputation_requestor = None self._logger.debug('WAN SOA: {} ({}) from {}/{}'.format(fqdn, dns.rdatatype.to_text(rdtype), addr[0], query.transport)) if self.hosttable.has((host.KEY_HOST_SERVICE, fqdn)): # The service exists in RGW host_obj = self.hosttable.get((host.KEY_HOST_SERVICE, fqdn)) service_data = host_obj.get_service_sfqdn(fqdn) self._logger.debug('Found service: {} / {}'.format(fqdn, service_data)) elif self.hosttable.has_carriergrade(fqdn): # There is a host with CarrierGrade service in RGW host_obj, service_data = self.hosttable.get_carriergrade(fqdn) self._logger.debug('Found CarrierGrade service: {} / {}'.format(fqdn, service_data)) elif fqdn in self.soa_list: # Querying the RGW domain itself self._logger.debug('Use NS address: {}'.format(fqdn)) host_obj = self.hosttable.get((host.KEY_HOST_FQDN, fqdn)) # Create DNS Response response = dnsutils.make_response_answer_rr(query, fqdn, dns.rdatatype.A, host_obj.ipv4, rdclass=1, ttl=DNSRR_TTL_DEFAULT) self._logger.debug('Send DNS response to {}:{}'.format(addr[0],addr[1])) cback(query, addr, response) return else: # FQDN not found! Answer NXDOMAIN self._logger.debug('Answer {} with NXDOMAIN'.format(fqdn)) response = dnsutils.make_response_rcode(query, dns.rcode.NXDOMAIN) cback(query, addr, response) return # Pre-process request with PBRA. Quick return response if pre-emptive actions are required due to policy response = yield from self.pbra.pbra_dns_preprocess_rgw_wan_soa(query, addr, host_obj, service_data) if response is not None: self._logger.debug('Preprocessing DNS response\n{}'.format(response)) cback(query, addr, response) return self._logger.debug('Continue after pre-processing query / {}'.format(service_data)) # Process only type A/SRV/TXT queries for servicepool domains if rdtype not in (dns.rdatatype.A, dns.rdatatype.SRV, dns.rdatatype.TXT): self._logger.debug('Answer with empty records for public domain {} type {}'.format(fqdn, dns.rdatatype.to_text(rdtype))) response = dnsutils.make_response_rcode(query, rcode=dns.rcode.NOERROR, recursion_available=False) cback(query, addr, response) return # If the service is carriergrade, resolve it first before allocating our own address _ipv4, _service_data = host_obj.ipv4, service_data if service_data['carriergrade'] is True: _carriergrade_fqdn = fqdn if service_data['alias'] is True: # Use original FQDN in carriergrade resolutions, instead of the alias CNAMEd FQDN _carriergrade_fqdn = service_data['_fqdn'] self._logger.debug('CarrierGrade resolution using original fqdn={} instead of alias fqdn={}'.format(_carriergrade_fqdn, fqdn)) _rcode, _ipv4, _service_data = yield from self._dns_resolve_circularpool_carriergrade(host_obj, _carriergrade_fqdn, addr, service_data) if _ipv4 is None: # Propagate rcode value response = dnsutils.make_response_rcode(query, rcode=_rcode, recursion_available=False) cback(query, addr, response) return # Use PBRA to allocate an address according to policy allocated_ipv4 = yield from self.pbra.pbra_dns_process_rgw_wan_soa(query, addr, host_obj, _service_data, _ipv4) if not allocated_ipv4 and query.transport == 'tcp': # Failed to allocate an address - hold and reattempt after T ms to bridge the gap with UDP queries rtx_t = 0.50 self._logger.debug('Failed to allocate an address for {} via TCP, reattempting in {} msec'.format(fqdn, rtx_t*1000)) yield from asyncio.sleep(rtx_t) allocated_ipv4 = yield from self.pbra.pbra_dns_process_rgw_wan_soa(query, addr, host_obj, _service_data, _ipv4) if not allocated_ipv4 and query.transport == 'tcp': # Failed to allocate an address - Answer with empty records to avoid stalling the TCP transaction self._logger.warning('Failed to allocate an address for {} via TCP'.format(fqdn)) response = dnsutils.make_response_rcode(query, rcode=dns.rcode.NOERROR, recursion_available=False) cback(query, addr, response) return if not allocated_ipv4 and query.transport == 'udp': # Failed to allocate an address - Drop DNS Query to trigger reattempt return # Create DNS response based on received query type if rdtype == dns.rdatatype.A: # Create DNS Response type A response = dnsutils.make_response_answer_rr(query, fqdn, dns.rdatatype.A, allocated_ipv4, rdclass=1, ttl=DNSRR_TTL_CIRCULARPOOL) self._logger.debug('Send DNS response to {}:{}'.format(addr[0],addr[1])) cback(query, addr, response) elif rdtype == dns.rdatatype.SRV: # Create DNS Response type SRV # Check service data and build SFQDN for SRV response, then add SFQDN A record to additional records sfqdn = '_{}._{}.{}'.format(_service_data['port'], _service_data['protocol'], fqdn) # Build SRV data response - SRV answer with encoded SFQDN and additional with A record for encoded SFQDN priority, weight, port, target = 10, 100, _service_data['port'], sfqdn srv_rrset = '{} {} {} {}'.format(priority, weight, port, target) response = dnsutils.make_response_answer_rr(query, fqdn, dns.rdatatype.SRV, srv_rrset, rdclass=1, ttl=DNSRR_TTL_CIRCULARPOOL) _foo = dnsutils.make_response_answer_rr(query, sfqdn, dns.rdatatype.A, allocated_ipv4, rdclass=1, ttl=DNSRR_TTL_CIRCULARPOOL) response.additional = _foo.answer self._logger.debug('Send DNS response to {}:{}'.format(addr[0],addr[1])) cback(query, addr, response) elif rdtype == dns.rdatatype.TXT: # Create DNS Response type TXT # Build TXT data response - TXT answer with encoded data service and additional with A record for IP address txt_rrset = 'proxy_{}.port_{}.protocol_{}.{}'.format(_service_data['proxy_required'], _service_data['port'], _service_data['protocol'], fqdn) response = dnsutils.make_response_answer_rr(query, fqdn, dns.rdatatype.TXT, txt_rrset, rdclass=1, ttl=DNSRR_TTL_CIRCULARPOOL) _foo = dnsutils.make_response_answer_rr(query, fqdn, dns.rdatatype.A, allocated_ipv4, rdclass=1, ttl=DNSRR_TTL_CIRCULARPOOL) response.additional = _foo.answer self._logger.debug('Send DNS response to {}:{}'.format(addr[0],addr[1])) cback(query, addr, response)
def dns_process_rgw_lan_soa(self, query, addr, cback): """ Process DNS query from private network of a name in a SOA zone """ # Forward or continue to DNS resolver fqdn = query.fqdn rdtype = query.question[0].rdtype self._logger.debug('LAN SOA: {} ({}) from {}/{}'.format(fqdn, dns.rdatatype.to_text(rdtype), addr[0], query.transport)) if self.hosttable.has((host.KEY_HOST_SERVICE, fqdn)): # The service exists in RGW host_obj = self.hosttable.get((host.KEY_HOST_SERVICE, fqdn)) service_data = host_obj.get_service_sfqdn(fqdn) self._logger.debug('Found service: {} / {}'.format(fqdn, service_data)) elif self.hosttable.has_carriergrade(fqdn): # There is a host with CarrierGrade service in RGW host_obj, service_data = self.hosttable.get_carriergrade(fqdn) self._logger.debug('Found CarrierGrade service: {} / {}'.format(fqdn, service_data)) elif fqdn in self.soa_list: # Querying the RGW domain itself self._logger.debug('Use NS address: {}'.format(fqdn)) host_obj = self.hosttable.get((host.KEY_HOST_FQDN, fqdn)) # Create DNS Response response = dnsutils.make_response_answer_rr(query, fqdn, dns.rdatatype.A, host_obj.ipv4, rdclass=1, ttl=DNSRR_TTL_DEFAULT, recursion_available=True) self._logger.debug('Send DNS response to {}:{}'.format(addr[0],addr[1])) cback(query, addr, response) return else: # FQDN not found! Answer NXDOMAIN self._logger.debug('Answer {} with NXDOMAIN'.format(fqdn)) response = dnsutils.make_response_rcode(query, dns.rcode.NXDOMAIN, recursion_available=True) cback(query, addr, response) return # TODO: Modify this to propagate original queries if carriergrade, and only override certain types, e.g. dns.rdatatype.A, # Evaluate host and service if service_data['carriergrade'] is True: # Resolve via CarrierGrade # Answer with empty records for other types not A if rdtype != dns.rdatatype.A: response = dnsutils.make_response_rcode(query, dns.rcode.NOERROR, recursion_available=True) cback(query, addr, response) return self._logger.debug('Process {} with CarrierGrade resolution'.format(fqdn)) _rcode, _ipv4, _service_data = yield from self._dns_resolve_circularpool_carriergrade(host_obj, fqdn, addr, service_data) if not _ipv4: # Propagate rcode value response = dnsutils.make_response_rcode(query, rcode=_rcode, recursion_available=True) cback(query, addr, response) return self._logger.debug('Completed LAN CarrierGrade resolution: {} @ {}'.format(fqdn, _ipv4)) # Answer query with A type and answer with IPv4 address of the host response = dnsutils.make_response_answer_rr(query, fqdn, dns.rdatatype.A, _ipv4, rdclass=1, ttl=DNSRR_TTL_DEFAULT, recursion_available=True) cback(query, addr, response) elif rdtype == dns.rdatatype.A: # Resolve A type and answer with IPv4 address of the host response = dnsutils.make_response_answer_rr(query, fqdn, rdtype, host_obj.ipv4, rdclass=1, ttl=DNSRR_TTL_DEFAULT, recursion_available=True) cback(query, addr, response) elif rdtype == dns.rdatatype.PTR: # Resolve PTR type and answer with FQDN of the host response = dnsutils.make_response_answer_rr(query, fqdn, rdtype, host_obj.fqdn, rdclass=1, ttl=DNSRR_TTL_DEFAULT, recursion_available=True) cback(query, addr, response) else: # Answer with empty records for other types response = dnsutils.make_response_rcode(query, dns.rcode.NOERROR, recursion_available=True) cback(query, addr, response)