Esempio n. 1
0
def __load_from_dns(repo_url: str) -> Dict[str, str]:
    repo_url = repo_url if repo_url is not None else "repomd.example.com"
    KEYS = ['alg', 'hash', 'ts', 'val']
    ctx = unbound.ub_ctx()
    ctx.config("/vagrant/unbound/libunbound.conf")
    ctx.add_ta_file("/vagrant/root-server/root.zone.signed")
    status, result = ctx.resolve(repo_url, unbound.RR_TYPE_TXT,
                                 unbound.RR_CLASS_IN)
    if status != 0:
        print("error communicating with DNS server")
    else:
        data = result.data.as_raw_data()
        structured_result = {}
        for d in data:
            key_val = d.decode('ascii')
            # print(key_val)
            key, val = key_val.split('=')
            print(key, val)

            for k in KEYS:
                if key.endswith(k):
                    structured_result[k] = val

        print(structured_result)
        return structured_result
Esempio n. 2
0
def test_ns_connectivity(ip, port, domain):
    # NS connectivity is first tried with TCP (in test_connectivity).
    # If that fails, maybe the NS is not doing TCP. As a last resort
    # (expensive) we initiate an unbound context that will ask the NS a
    # question he can't refuse.
    def ub_callback(data, status, result):
        if status != 0:
            data['result'] = False
        elif result.rcode == 2:  # SERVFAIL
            data['result'] = False
        else:
            data['result'] = True
        data['done'] = True

    ctx = ub_ctx()
    # XXX: Remove for now; inconsistency with applying settings on celery.
    # ctx.set_async(True)
    ctx.set_fwd(ip)
    cb_data = dict(done=False)
    try:
        retval, async_id = ctx.resolve_async(
            domain, cb_data, ub_callback, RR_TYPE_NS, RR_CLASS_IN)
        while retval == 0 and not cb_data['done']:
            time.sleep(0.1)
            retval = ctx.process()
    except SoftTimeLimitExceeded:
        if async_id:
            ctx.cancel(async_id)
        raise
    return cb_data['result']
Esempio n. 3
0
    def _cache_miss(input_key):
        # type: (KeyInfo) -> Validity
        """
        In case the key was not found in the cache, create an Unbound context and contact the DNS
        system
        """
        try:
            import unbound
        except ImportError as e:
            msg = _("Configuration option 'gpgkey_dns_verification' requires "
                    "python3-unbound ({})".format(e))
            raise dnf.exceptions.Error(msg)

        ctx = unbound.ub_ctx()
        if ctx.set_option("verbosity:", "0") != 0:
            logger.debug("Unbound context: Failed to set verbosity")

        if ctx.set_option("qname-minimisation:", "yes") != 0:
            logger.debug("Unbound context: Failed to set qname minimisation")

        if ctx.resolvconf() != 0:
            logger.debug("Unbound context: Failed to read resolv.conf")

        if ctx.add_ta_file("/var/lib/unbound/root.key") != 0:
            logger.debug("Unbound context: Failed to add trust anchor file")

        status, result = ctx.resolve(email2location(input_key.email),
                                     RR_TYPE_OPENPGPKEY, unbound.RR_CLASS_IN)
        if status != 0:
            logger.debug("Communication with DNS servers failed")
            return Validity.ERROR
        if result.bogus:
            logger.debug("DNSSEC signatures are wrong ({})".format(
                result.why_bogus))
            return Validity.BOGUS_RESULT
        if not result.secure:
            logger.debug("Result is not secured with DNSSEC")
            return Validity.RESULT_NOT_SECURE
        if result.nxdomain or (result.rcode == unbound.RCODE_NOERROR
                               and not result.havedata):
            logger.debug("Non-existence of this record was proven by DNSSEC")
            return Validity.PROVEN_NONEXISTENCE
        if not result.havedata:
            # TODO: This is weird result, but there is no way to perform validation, so just return
            # an error
            # Should handle only SERVFAIL, REFUSED and similar rcodes
            logger.debug("Unknown error in DNS communication: {}".format(
                result.rcode_str))
            return Validity.ERROR
        else:
            data = result.data.as_raw_data()[0]
            dns_data_b64 = base64.b64encode(data)
            if dns_data_b64 == input_key.key:
                return Validity.VALID
            else:
                # In case it is different, print the keys for further examination in debug mode
                logger.debug("Key from DNS: {}".format(dns_data_b64))
                logger.debug("Input key   : {}".format(input_key.key))
                return Validity.REVOKED
Esempio n. 4
0
	def __init__(self, task_queue, ta_file): 
		self.task_queue = task_queue
		threading.Thread.__init__(self)
		
		self.resolver = ub_ctx()
		#self.resolver.resolvconf("/etc/resolv.conf")
		#self.resolver.set_fwd("127.0.0.1")
		self.resolver.add_ta_file(ta_file) #read public keys for DNSSEC verification
Esempio n. 5
0
    def resolve(self, name, qtype):

        ctx = ub_ctx()
        ctx.resolvconf(self.resolv_conf)

        if not os.path.isfile(self.dnssec_root_key):
            raise Exception('Trust anchor is missing or inaccessible')
        else:
            ctx.add_ta_file(self.dnssec_root_key)

        status, result = ctx.resolve(name, rdatatype.from_text(qtype),
                                     RR_CLASS_IN)
        if status != 0:
            raise WalletNameLookupError

        if not result.secure or result.bogus:
            raise WalletNameLookupInsecureError
        elif not result.havedata:
            return None
        else:
            # We got data
            txt = result.data.as_domain_list()

            # Reference implementation for serving BIP32 and BIP70 requests
            try:
                # BIP32/BIP70 URL will be base64 encoded. Some wallet addresses fail decode.
                # If it fails, assume wallet address or unknown and return
                b64txt = b64decode(txt[0])
            except:
                return txt[0]

            # Fully qualified bitcoin URI, return as is
            if b64txt.startswith('bitcoin:'):
                return b64txt
            elif re.match(r'^https?:\/\/', b64txt):
                try:
                    # Identify localhost or link_local/multicast/private IPs and return without issuing a GET.
                    lookup_url, return_data = WalletNameResolver.get_endpoint_host(
                        b64txt)

                    if return_data:
                        return return_data

                    # Try the URL. Returning response.text and expect a Bitcoin URI as delivered from Addressimo.
                    response = requests.get(lookup_url,
                                            headers={
                                                'X-Forwarded-For':
                                                '%s' % request.access_route[0]
                                            })
                    return response.text
                except Exception:
                    # Return base64 decoded value if we cannot perform a GET on the URL to allow requester to handle.
                    return b64txt
            else:
                # If you made it this far, assume wallet address and return
                return txt[0]
Esempio n. 6
0
    def _cache_miss(input_key):
        # type: (KeyInfo) -> Validity
        """
        In case the key was not found in the cache, create an Unbound context and contact the DNS
        system
        """
        try:
            import unbound
        except ImportError as e:
            raise RuntimeError("Configuration option 'gpgkey_dns_verification' requires\
            libunbound ({})".format(e))

        ctx = unbound.ub_ctx()
        if ctx.set_option("verbosity:", "0") != 0:
            logger.debug("Unbound context: Failed to set verbosity")

        if ctx.set_option("qname-minimisation:", "yes") != 0:
            logger.debug("Unbound context: Failed to set qname minimisation")

        if ctx.resolvconf() != 0:
            logger.debug("Unbound context: Failed to read resolv.conf")

        if ctx.add_ta_file("/var/lib/unbound/root.key") != 0:
            logger.debug("Unbound context: Failed to add trust anchor file")

        status, result = ctx.resolve(email2location(input_key.email),
                                     RR_TYPE_OPENPGPKEY, unbound.RR_CLASS_IN)
        if status != 0:
            logger.debug("Communication with DNS servers failed")
            return Validity.ERROR
        if result.bogus:
            logger.debug("DNSSEC signatures are wrong")
            return Validity.BOGUS_RESULT
        if not result.secure:
            logger.debug("Result is not secured with DNSSEC")
            return Validity.RESULT_NOT_SECURE
        if result.nxdomain:
            logger.debug("Non-existence of this record was proven by DNSSEC")
            return Validity.PROVEN_NONEXISTENCE
        if not result.havedata:
            # TODO: This is weird result, but there is no way to perform validation, so just return
            # an error
            logger.debug("Unknown error in DNS communication")
            return Validity.ERROR
        else:
            data = result.data.as_raw_data()[0]
            dns_data_b64 = base64.b64encode(data)
            if dns_data_b64 == input_key.key:
                return Validity.VALID
            else:
                # In case it is different, print the keys for further examination in debug mode
                logger.debug("Key from DNS: {}".format(dns_data_b64))
                logger.debug("Input key   : {}".format(input_key.key))
                return Validity.REVOKED
Esempio n. 7
0
def create_context(config_file="ub.lookup.conf", asyncflag=False):
    """
    Create an unbound context to use for testing.

    """
    ctx = unbound.ub_ctx()
    status = ctx.config(config_file)
    if status != 0:
        print("read config failed with status: {}".format(status))
        sys.exit(1)
    ctx.set_async(asyncflag)
    return ctx
Esempio n. 8
0
def create_context(config_file="ub.lookup.conf", asyncflag=False):
    """
    Create an unbound context to use for testing.

    """
    ctx = unbound.ub_ctx()
    status = ctx.config(config_file)
    if status != 0:
        print("read config failed with status: {}".format(status))
        sys.exit(1)
    ctx.set_async(asyncflag)
    return ctx
Esempio n. 9
0
    def ub_ctx(self):
        if self._ub_ctx is None:
            self._ub_ctx = unbound.ub_ctx()
            self._ub_ctx.add_ta_file(
                os.path.join(os.getcwd(), settings.DNS_ROOT_KEY))
            self._ub_ctx.set_option(
                "cache-max-ttl:", str(settings.CACHE_TTL*0.9))
            self._ub_ctx.set_async(True)
            if settings.ENABLE_BATCH and settings.CENTRAL_UNBOUND:
                self._ub_ctx.set_fwd("{}".format(settings.CENTRAL_UNBOUND))

        return self._ub_ctx
Esempio n. 10
0
    def _cache_miss(input_key):
        # type: (KeyInfo) -> Validity
        """
        In case the key was not found in the cache, create an Unbound context and contact the DNS
        system
        """
        try:
            import unbound
        except ImportError as e:
            raise RuntimeError(
                "Configuration option 'gpgkey_dns_verification' requires\
            libunbound ({})".format(e))

        ctx = unbound.ub_ctx()
        if ctx.set_option("verbosity:", "0") != 0:
            logger.debug("Unbound context: Failed to set verbosity")

        if ctx.set_option("qname-minimisation:", "yes") != 0:
            logger.debug("Unbound context: Failed to set qname minimisation")

        if ctx.resolvconf() != 0:
            logger.debug("Unbound context: Failed to read resolv.conf")

        if ctx.add_ta_file("/var/lib/unbound/root.key") != 0:
            logger.debug("Unbound context: Failed to add trust anchor file")

        status, result = ctx.resolve(email2location(input_key.email),
                                     RR_TYPE_OPENPGPKEY, unbound.RR_CLASS_IN)
        if status != 0:
            return Validity.ERROR
        if result.bogus:
            return Validity.BOGUS_RESULT
        if not result.secure:
            return Validity.RESULT_NOT_SECURE
        if result.nxdomain:
            return Validity.PROVEN_NONEXISTENCE
        if not result.havedata:
            # TODO: This is weird result, but there is no way to perform validation, so just return
            # an error
            return Validity.ERROR
        else:
            data = result.data.as_raw_data()[0]
            dns_data_b64 = base64.b64encode(data)
            if dns_data_b64 == input_key.key:
                return Validity.VALID
            else:
                return Validity.REVOKED
Esempio n. 11
0
    def ub_ctx(self):
        if self._ub_ctx is None:
            self._ub_ctx = unbound.ub_ctx()
            if (hasattr(settings, 'ENABLE_INTEGRATION_TEST')
                    and settings.ENABLE_INTEGRATION_TEST):
                self._ub_ctx.debuglevel(2)
                self._ub_ctx.config(settings.IT_UNBOUND_CONFIG_PATH)
                self._ub_ctx.set_fwd(settings.IT_UNBOUND_FORWARD_IP)
            else:
                self._ub_ctx.add_ta_file(
                    os.path.join(os.getcwd(), settings.DNS_ROOT_KEY))
            self._ub_ctx.set_option("cache-max-ttl:",
                                    str(settings.CACHE_TTL * 0.9))
            self._ub_ctx.set_async(True)
            if settings.ENABLE_BATCH and settings.CENTRAL_UNBOUND:
                self._ub_ctx.set_fwd("{}".format(settings.CENTRAL_UNBOUND))

        return self._ub_ctx
Esempio n. 12
0
    def resolve(self, name, qtype):

        ctx = ub_ctx()
        ctx.resolvconf(self.resolv_conf)

        if not os.path.isfile(self.dnssec_root_key):
            raise Exception('Trust anchor is missing or inaccessible')
        else:
            ctx.add_ta_file(self.dnssec_root_key)

        status, result = ctx.resolve(name, rdatatype.from_text(qtype), RR_CLASS_IN)
        if status != 0:
            raise WalletNameLookupError

        if not result.secure or result.bogus:
            raise WalletNameLookupInsecureError
        elif not result.havedata:
            return None
        else:
            # We got data
            txt = result.data.as_domain_list()
            return txt[0]
Esempio n. 13
0
	def __init__(self, taskQueue, taFile, rrScanners, dbQueue, opts, prefix):
		"""Create scanning thread.
		
		@param taskQueue: Queue.Queue containing domains to scan as strings
		@param taFile: trust anchor file for libunbound
		@param rrScanners: list of subclasses of RRTypeParser to use for scan.
		NSParser and DSParser are always on and shouldn't be present in the list.
		@param dbQueue: Queue.Queue for passing things to store to StorageThread
		@param opts: instance of DnsConfigOptions
		@param prefix: prefix of schema
		"""
		self.taskQueue = taskQueue
		self.rrScanners = rrScanners
		self.dbQueue = dbQueue
		self.opts = opts
		self.prefix = prefix
		
		threading.Thread.__init__(self)
		
		self.resolver = ub_ctx()
		if opts.forwarder:
			self.resolver.set_fwd(opts.forwarder)
		self.resolver.add_ta_file(taFile) #read public keys for DNSSEC verification
Esempio n. 14
0
 def __cache_miss(input_key: KeyInfo) -> Validity:
     RR_TYPE_OPENPGPKEY = 61
     ctx = unbound.ub_ctx()
     ctx.config("/vagrant/unbound/libunbound.conf")
     ctx.add_ta_file("/vagrant/root-server/root.zone.signed")
     status, result = ctx.resolve(email2location(input_key.email), RR_TYPE_OPENPGPKEY, unbound.RR_CLASS_IN)
     if status != 0:
         return Validity.ERROR
     if result.bogus:
         return Validity.BOGUS_RESULT
     if not result.secure:
         return Validity.RESULT_NOT_SECURE
     if result.nxdomain:
         return Validity.PROVEN_NONEXISTENCE
     if not result.havedata:
         # TODO: what kind of result is this???
         return Validity.ERROR
     else:
         data = result.data.as_raw_data()[0]
         dns_data_b64 = base64.b64encode(data)
         if dns_data_b64 == input_key.key:
             return Validity.VALID
         else:
             return Validity.REVOKED
Esempio n. 15
0
from timeit import default_timer as timer
import yaml

from celery import shared_task
from django.core.cache import cache
from django.conf import settings
from django.db import connection
from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.utils import timezone
from django.utils.translation import ugettext as _
import unbound

from checks import redis_id

ub_ctx = unbound.ub_ctx()
if (hasattr(settings, 'ENABLE_INTEGRATION_TEST')
        and settings.ENABLE_INTEGRATION_TEST):
    ub_ctx.debuglevel(2)
    ub_ctx.config(settings.IT_UNBOUND_CONFIG_PATH)
    ub_ctx.set_fwd(settings.IT_UNBOUND_FORWARD_IP)
ub_ctx.set_async(True)
if settings.ENABLE_BATCH and settings.CENTRAL_UNBOUND:
    ub_ctx.set_fwd("{}".format(settings.CENTRAL_UNBOUND))

# See: https://stackoverflow.com/a/53875771 for a good summary of the various
# RFCs and other rulings that combine to define what is a valid domain name.
# Of particular note are xn-- which is used for internationalized TLDs, and
# the rejection of digits in the TLD if not xn--. Digits in the last label
# were legal under the original RFC-1035 but not according to the "ICANN
# Application Guidebook for new TLDs (June 2012)" which stated that "The
Esempio n. 16
0
    print("Resolving domain", domain)
    s, r = resolver.resolve(domain)
    print("status: %s, secure: %s, rcode: %s, havedata: %s, answer_len; %s" % (s, r.secure, r.rcode_str, r.havedata, r.answer_len))
    
    s, pkt = ldns.ldns_wire2pkt(r.packet)
    if s != 0:
        raise RuntimeError("Error parsing DNS packet")

    rrsigs = pkt.rr_list_by_type(RR_TYPE_RRSIG, ldns.LDNS_SECTION_ANSWER)
    print("RRSIGs from answer:", sorted(rrsigs))
    
    rrsigs = pkt.rr_list_by_type(RR_TYPE_RRSIG, ldns.LDNS_SECTION_AUTHORITY)
    print("RRSIGs from authority:", sorted(rrsigs))
    
    nsecs = pkt.rr_list_by_type(RR_TYPE_NSEC, ldns.LDNS_SECTION_AUTHORITY)
    print("NSECs:", sorted(nsecs))
    
    nsec3s = pkt.rr_list_by_type(RR_TYPE_NSEC3, ldns.LDNS_SECTION_AUTHORITY)
    print("NSEC3s:", sorted(nsec3s))
    
    print("---")


resolver = ub_ctx()
resolver.add_ta(".   IN DS 20326 8 2 E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC683457104237C7F8EC8D")

dnssecParse("nic.cz")
dnssecParse("nonexistent-domain-blablabla.cz")
dnssecParse("nonexistent-domain-blablabla.root.cz")

Esempio n. 17
0
 
 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 POSSIBILITY OF SUCH DAMAGE.
'''
import unbound

ctx = unbound.ub_ctx()
ctx.resolvconf("/etc/resolv.conf")

status, result = ctx.resolve("nic.cz", unbound.RR_TYPE_MX, unbound.RR_CLASS_IN)
if status == 0 and result.havedata:
    print("Result:")
    print("      raw data:", result.data)
    for k in result.data.mx_list:
        print("      priority:%d address:%s" % k)

status, result = ctx.resolve("nic.cz", unbound.RR_TYPE_A, unbound.RR_CLASS_IN)
if status == 0 and result.havedata:
    print("Result:")
    print("      raw data:", result.data)
    for k in result.data.address_list:
        print("      address:%s" % k)
Esempio n. 18
0
#!/usr/bin/python
from unbound import ub_ctx,ub_strerror,RR_TYPE_A,RR_CLASS_IN

ctx = ub_ctx()
ctx.resolvconf("/etc/resolv.conf")
	
status, result = ctx.resolve("test.record.xxx", RR_TYPE_A, RR_CLASS_IN)
if status == 0 and result.havedata:
    print "Result:", result.data.address_list
else:
    print "No record found"

#define new local zone
status = ctx.zone_add("xxx.","static")
if (status != 0): print "Error zone_add:",status, ub_strerror(status)

#add RR to the zone
status = ctx.data_add("test.record.xxx. IN A 1.2.3.4")
if (status != 0): print "Error data_add:",status, ub_strerror(status)

#lookup for an A record
status, result = ctx.resolve("test.record.xxx", RR_TYPE_A, RR_CLASS_IN)
if status == 0 and result.havedata:
    print "Result:", result.data.as_address_list()
else:
    print "No record found"

Esempio n. 19
0
    def resolve(self, name, qtype):
        '''

        Resolves a Blockchain-based (Namecoin) DNS Name via 2 step process using DNSSEC

        Step 1:
        -------
        Get NS and DS records for the Namecoin name (for example: www.mattdavid.bit) from the Namecoin Client

        Step 2:
        -------
        For each listed nameserver:

            - Create a temporary config file for use with unbound
            - Set Unbound's Trust Anchor to be the given DS records for the Namecoin-based domain name
            - Do DNSSEC-enabled DNS resolution for the given name / qtype


        :param name: DNS Record Name Query (for example: www.mattdavid.bit)
        :param qtype: String representation of query type (for example: A, AAAA, TXT, NS, SOA, etc...)
        :return: Resolved value if successful, None if un-successful
        '''

        name = name.rstrip('.')
        if not name.endswith('.bit'):
            raise ValueError('This is not a valid .bit domain')

        domains = name.split('.')
        domains.reverse()
        if len(domains) < 2:
            raise ValueError('At least SLD Required')

        # Get Namecoin-based Domain Info from Namecoin Blockchain
        nc_domain = self.nc_name_resolver.name_show(domains[1])
        if not nc_domain or not nc_domain.get('value'):
            log.error(
                'No Name Value Data Found for Namecoin-based Domain Name: d/%s'
                % domains[1])
            raise NamecoinValueException('No Name Value Data Found for: d/%s' %
                                         domains[1])

        nc_value = json.loads(nc_domain.get('value', '{}').replace('\'', '"'))
        if not nc_value.get('ds'):
            log.error(
                'No DS Records Present for Namecoin-based Domain Name: %s' %
                name)
            raise NoDSRecordException()

        if not nc_value.get('ns'):
            log.error(
                'No NS Records Present for Namecoin-based Domain Name: %s' %
                name)
            raise NoNameserverException()

        sld = '%s.%s.' % (domains[1], domains[0])
        ds_record = ' '.join([str(x) for x in nc_value['ds'][0][0:3]])

        # Handle both Hex and Base64 encoding (Base64 is the preferred encoding) per:
        # https://wiki.namecoin.info/index.php?title=Domain_Name_Specification
        if re.match('^[0-9a-fA-F]*$', nc_value['ds'][0][3]):
            ds_record += ' %s' % nc_value['ds'][0][3]
        else:
            ds_record += ' %s' % base64.b64decode(
                nc_value['ds'][0][3]).encode('hex').upper()

        ds_ta = '%s IN DS %s' % (sld, ds_record)

        ns_ctx = ub_ctx()
        ns_ctx.resolvconf(self.resolv_conf)

        if not os.path.isfile(self.dnssec_root_key):
            log.error("Trust anchor missing or inaccessible")
            raise Exception("Trust anchor is missing or inaccessible: %s" %
                            self.dnssec_root_key)
        else:
            ns_ctx.add_ta_file(self.dnssec_root_key)

        last_error = None
        for ns in nc_value.get('ns', []):

            lookup_value = None

            status, result = ns_ctx.resolve(ns, rdatatype.from_text('A'),
                                            rdataclass.from_text('IN'))

            # NOTE: We do not require secure DNS resolution here because the Blockchain-stored DS records work as the trust anchor
            # and the signed RRSIG DNS results from the final DNS+DNSSEC lookup will be able to complete the chain of trust
            if status == 0 and result and result.data and not result.bogus:
                tmp_config_file = self._build_temp_unbound_config(
                    sld,
                    result.data.as_address_list()[0])
            else:
                last_error = InvalidNameserverException()
                log.warn('No or Invalid Resolution Result for Nameserver: %s' %
                         ns)
                continue

            ctx = ub_ctx()
            ctx.config(tmp_config_file)
            ctx.add_ta(str(ds_ta))

            _qtype = None
            try:
                _qtype = rdatatype.from_text(qtype)
            except Exception as e:
                log.error(
                    'Unable to get RDATAType for Given Query Type [%s]: %s' %
                    (qtype, str(e)))
                raise ValueError('Unable to get RDATAType for Query Type %s' %
                                 qtype)

            status, result = ctx.resolve(name, _qtype,
                                         rdataclass.from_text('IN'))
            if status != 0:
                log.info("DNS Resolution Failed: %s [%s]" % (name, _qtype))
            elif status == 0:

                if not result.secure:
                    log.info(
                        "DNS Resolution Returned Insecure Result: %s [%s]" %
                        (name, qtype))
                    last_error = InsecureResultException()

                elif result.bogus:
                    log.info("DNS Resolution Returned Bogus Result: %s [%s]" %
                             (name, qtype))
                    last_error = BogusResultException()

                elif not result.havedata:
                    log.info("DNS Resolution Returned Empty Result: %s [%s]" %
                             (name, qtype))
                    last_error = EmptyResultException()

                else:
                    # Get appropriate data by query type
                    if qtype in ['A', 'AAAA']:
                        lookup_value = result.data.as_address_list()
                    elif qtype in ['CNAME', 'TXT']:
                        lookup_value = result.data.as_domain_list()
                    elif qtype in ['MX']:
                        lookup_value = result.data.as_mx_list()
                    else:
                        last_error = NotImplementedError(
                            'Unsupported DNS Query Type: %s' % qtype)

            self._delete_temp_unbound_config(tmp_config_file)

            if lookup_value:
                return lookup_value[0]

            if last_error and isinstance(last_error, NotImplementedError):
                raise last_error

        log.error('DNS Resolution Failed: %s [%s]' % (name, qtype))
        if last_error:
            raise last_error

        return None
Esempio n. 20
0
 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 POSSIBILITY OF SUCH DAMAGE.
'''
from __future__ import print_function
import os
from unbound import ub_ctx, RR_TYPE_A, RR_CLASS_IN

ctx = ub_ctx()
ctx.resolvconf("/etc/resolv.conf")

fw = open("dnssec-valid.txt", "wb")
ctx.debugout(fw)
ctx.debuglevel(2)

if os.path.isfile("keys"):
    ctx.add_ta_file("keys")  #read public keys for DNSSEC verification

status, result = ctx.resolve("www.nic.cz", RR_TYPE_A, RR_CLASS_IN)
if status == 0 and result.havedata:

    print("Result:", sorted(result.data.address_list))

    if result.secure:
Esempio n. 21
0
class Resolver(object):
    """
    Responsible for querying dns records and returning results of the query in a formatted response.
    The general format is: {'domain', 'rr_types', 'answer'}.
    This class does not attempt to reach any hosts. See 'Reacher" class for host connecting & reachability.
    This class does not attempt to obtain DMARC, SPF, or DKIM records from DNS. For this, see DmarcianClient class.
    This class borrows functionality from the 'ip_helper.py' module to assist in constructing proper IPv6 addresses.
    This class has sibling FormattedResponse classes that wrap dictionaries that contain the response data.
    Inherits from: object.
    Parent to: None.
    Sibling to: Reacher, DmarcianClient
    """

    ctx = ub.ub_ctx()  # non dnssec context
    ctx.resolvconf(Config.RESOLV_CONF_LOCATION)

    ctx_dnssec = ub.ub_ctx()
    ctx_dnssec.resolvconf(Config.RESOLV_CONF_LOCATION)
    ctx_dnssec.add_ta_file(Config.ROOT_TRUST_ANCHOR)

    def __init__(self):
        pass

    def get_a_records(self, domain: str, as_json: bool = False):
        """
        Accepts domain: str. Returns a formatted answer including dictionary of A records in the format:
        {'domain': 'example.com', 'rr_types':['a'], 'answer': ['1.1.1.1', '2.2.2.2', '3.3.3.3' ... etc]}.
        If no record is found, returns None in the answer section.
        Set 'as_json' to True to return a pure json response instead of the wrapped formatted response.
        """

        formatted_answer = {
            'domain': domain,
            'rr_types': ["a"],
            'answer': None
        }

        status, results = Resolver.ctx.resolve(domain,
                                               rrtype=ub.RR_TYPE_A,
                                               rrclass=ub.RR_CLASS_IN)

        if status != 0:
            raise DNSResolveError(
                f"Error occurred while resolving IPv4 for {domain}")
        elif results.havedata == 1 and len(results.data.address_list) > 0:
            ipv4_addr_list = results.data.address_list
            formatted_answer['answer'] = {}
            i = 0
            for ip in ipv4_addr_list:
                formatted_answer['answer'][i] = ip
                i += 1
        if as_json:
            return json.dumps(formatted_answer)
        # return DNSFormattedResponse(formatted_answer)
        return formatted_answer

    def get_aaaa_records(self, domain: str, as_json: bool = False):
        """
        Accepts domain: str. Returns a formatted answer including dictionary of A records in the format:
        {'domain': 'example.com', 'rr_types':['aaaa'], 'answer': ['f::0', 'f::1', 'f::2' ... etc]}.
        If no record is found, returns None in the answer section.
        Set 'as_json' to True to return a pure json response instead of the wrapped formatted response.
        """
        formatted_answer = {
            'domain': domain,
            'rr_types': ["aaaa"],
            'answer': None
        }

        status, results = Resolver.ctx.resolve(domain,
                                               rrtype=ub.RR_TYPE_AAAA,
                                               rrclass=ub.RR_CLASS_IN)

        if status != 0:
            raise DNSResolveError(
                f"Error occurred while resolving IPv4 for {domain}")
        elif results.havedata == 1 and len(results.data.address_list) > 0:
            ipv6_addr_list = results.rawdata
            formatted_answer['answer'] = {}
            i = 0
            for ip in ipv6_addr_list:
                if ip_helper.V6.is_valid(ip):
                    formatted_answer['answer'][
                        i] = ip_helper.V6.bytes_to_hexadectet(ip)
                i += 1
        if as_json:
            return json.dumps(formatted_answer)
        # return DNSFormattedResponse(formatted_answer)
        return formatted_answer

    # no exposed json for this private method below

    def _get_aaaa_bytes(self, domain: str):
        """Private internal class method. Takes in a single server name.
        If ipv6 data is found, returns bytes. If not found, returns None. No formatted response has been set."""
        ipv6_bytes = None

        status, results = Resolver.ctx.resolve(domain, rrtype=ub.RR_TYPE_AAAA)

        if status != 0:
            raise DNSResolveError(
                f"Error resolving AAAA record for {domain}: {ub.ub_strerror(status)}"
            )
        elif results.havedata == 1 and ip_helper.V6.is_valid(
                results.rawdata[0]):
            ipv6_bytes = results.rawdata[0]
            # return results

        return ipv6_bytes

    def get_soa(self, domain: str, as_json: bool = False):
        """
        Accepts domain: str. Returns a formatted answer containing 'Start Of Authority' records in the format:
        {'domain': 'example.com', 'rr_types':['soa'], 'answer': ['administrator info, other info, time info, etc]}.
        If no record is found, returns None in the answer section.
        Set 'as_json=True' to return a pure json response instead of the wrapped formatted response.
        """
        formatted_answer = {
            'domain': domain,
            'rr_types': ["soa"],
            'answer': None
        }

        soa_list = None
        status, result = Resolver.ctx_dnssec.resolve(domain, ub.RR_TYPE_SOA,
                                                     ub.RR_CLASS_IN)
        if status == 0 and result.havedata and result.secure == 1:
            formatted_answer['answer'] = {}
            soa_list = result.data.data
            i = 0
            for record in soa_list:
                formatted_answer['answer'][i] = str(record)
                i += 1
        elif status != 0:  # throw/raise error
            print("Resolve error: ", ub.ub_strerror(status))
        elif result.havedata == 0:  # if no data in result
            print("No data.")

        if as_json:
            return json.dumps(formatted_answer)
        return DNSFormattedResponse(formatted_answer)

    def get_ns(self, domain: str, as_json: bool = False):
        """
        Accepts domain: str. Returns a formatted answer containing 'NS' records in the format:
        {'domain': 'example.com', 'rr_types':['ns'], 'answer': ['ns1.com', 'ns2.com', ... etc]}.
        If no record is found, returns None in the answer section.
        Set 'as_json=True' to return a pure json response instead of the wrapped formatted response.
        """
        formatted_answer = {
            'domain': domain,
            'rr_types': ["ns"],
            'answer': None
        }

        status, results = Resolver.ctx.resolve(domain, rrtype=ub.RR_TYPE_NS)

        if status != 0:
            raise DNSResolveError(
                f"Error occured while resolving IPv4 for {domain}")
        elif results.havedata == 1 and len(results.data.address_list) > 0:
            ns_list = list(results.data.as_domain_list())
            formatted_answer['answer'] = {}
            i = 0
            for each in ns_list:
                formatted_answer['answer'][i] = each
                i += 1
        if as_json:
            return json.dumps(formatted_answer)
        # return DNSFormattedResponse(formatted_answer)
        return formatted_answer

    def get_mx(self, domain: str, as_json: bool = False):
        """
        Accepts domain: str. Returns a formatted answer containing 'MX' records in the format:
        {'domain': 'example.com', 'rr_types':['mx'], 'answer': ['smtp1.com', 'smtp2.com', ... etc]}.
        If no record is found, returns None in the answer section.
        Set 'as_json=True' to return a pure json response instead of the wrapped formatted response.
        """
        formatted_answer = {
            'domain': domain,
            'rr_types': ["mx"],
            'answer': None
        }

        status, results = Resolver.ctx.resolve(domain, rrtype=ub.RR_TYPE_MX)

        if status != 0:
            raise DNSResolveError(
                f"Error while fetching Mail Exchange list for {domain}")
        elif results.havedata == 1 and len(results.data.as_mx_list()) > 0:
            mx_list = list(results.data.as_mx_list())
            formatted_answer['answer'] = {}
            i = 0
            for priority, name in mx_list:
                formatted_answer['answer'][i] = name
                i += 1
        if as_json:
            return json.dumps(formatted_answer)
        # return DNSFormattedResponse(formatted_answer)
        return formatted_answer

    # methods below this line use unbound indirectly. They use methods in this class as their dependencies.

    # dns host mapping for use with reachability
    def get_ipv6_mapping(self,
                         domain: str,
                         associated_with: str,
                         as_json: bool = False):
        """
        Accepts a domain, a type of host to associate an ip address with, and optionally 'as_json=True' to output json.
        This builds the DNSHostMappingFormattedResponse which can then be 'unpacked' by the Reacher class method
        reach_dns_hosts() or can be passed into a IPV6ExistsState directly.
        Output is something like,
        {'domain': 'x.com', 'rr_types':['ns', 'aaaa'], 'answer': {'ns1.com':'f::1', 'ns2.com':'f::2' ... etc}
        """
        formatted_answer = {'domain': domain, 'rr_types': [], 'answer': None}

        name_list = None
        if associated_with == "ns":
            ns_list = None
            if self.get_ns(domain).get_response()['answer'] is not None:
                ns_list = list(
                    self.get_ns(domain).get_response()['answer'].values())
            name_list = ns_list
            formatted_answer['rr_types'].append("ns")
        elif associated_with == "mx":
            mx_list = None
            if self.get_mx(domain).get_response()['answer'] is not None:
                mx_list = list(
                    self.get_mx(domain).get_response()['answer'].values())
            name_list = mx_list
            formatted_answer['rr_types'].append("mx")

        formatted_answer['rr_types'].append(
            "aaaa"
        )  # [rr_type, 4a] in that order indicates 'answer' content format.

        if associated_with == "ns" and name_list is None:  # all domains have ns records, but not necessarily mx records
            raise ValueError(
                "Value of 'name_list' is None while querying for NS records. Should be non-empty list."
            )

        if name_list:
            i = 0
            formatted_answer['answer'] = {}
            for name in name_list:
                ip6_dict = self.get_aaaa_records(
                    name).get_response()  # None or valid ip6 address returned
                if ip6_dict['answer'] is not None:
                    formatted_answer['answer'][name] = ip6_dict['answer'].get(
                        i)
                else:
                    formatted_answer['answer'][name] = ip6_dict['answer']
                i += 1
                if ip6_dict['answer'] and i + 1 > len(
                        ip6_dict['answer']
                ):  # i beyond the bounds of 'data' dict
                    i = 0
                    continue
        if as_json:
            return json.dumps(formatted_answer)
        return DNSHostMappingFormattedResponse(formatted_answer)

    def get_ipv4_mapping(self,
                         domain: str,
                         associated_with: str,
                         as_json: bool = False):  # newly added for decoupling
        """
        Accepts a domain, a type of host to associate an ip address with, and optionally 'as_json=True' to output json.
        This builds the DNSHostMappingFormattedResponse which can then be 'unpacked' by the Reacher class method
        reach_dns_hosts() or can be passed into a IPV4ExistsState directly.
        Output is something like,
        {'domain': 'x.com', 'rr_types':['ns', 'a'], 'answer': {'ns1.com':'1.1.1.1', 'ns2.com':'2.2.2.2' ... etc}
        """
        formatted_answer = {'domain': domain, 'rr_types': [], 'answer': None}

        name_list = None
        if associated_with == "ns":
            ns_list = None
            if self.get_ns(domain)['answer'] is not None:
                ns_list = list(self.get_ns(domain)['answer'].values())
            name_list = ns_list
            formatted_answer['rr_types'].append("ns")
        elif associated_with == "mx":
            mx_list = None
            if self.get_mx(domain)['answer'] is not None:
                mx_list = list(self.get_mx(domain)['answer'].values())
            name_list = mx_list
            formatted_answer['rr_types'].append("mx")

        formatted_answer['rr_types'].append(
            "a"
        )  # [name_record, ipv6] in that order indicates 'data' response index contents.

        if associated_with == "ns" and name_list is None:  # all domains have ns records, but not necessarily mx records
            raise ValueError(
                "Value of 'name_list' is None while querying for NS records. Should be non-empty list."
            )

        if name_list:
            i = 0
            formatted_answer['answer'] = {}
            for name in name_list:
                ip4_dict = self.get_a_records(
                    name)  # None or valid ip4 address returned
                if ip4_dict['answer'] is not None:
                    formatted_answer['answer'][name] = ip4_dict['answer'].get(
                        i)
                else:
                    formatted_answer['answer'][name] = ip4_dict['answer']
                i += 1
                if ip4_dict['answer'] and i + 1 > len(
                        ip4_dict['answer']
                ):  # i beyond the bounds of 'data' dict
                    i = 0
                    continue
        if as_json:
            return json.dumps(formatted_answer)
        # return DNSHostMappingFormattedResponse(formatted_answer)
        return formatted_answer

    # dnssec
    def dnssec_comprehensive(self, domain: str, as_json: bool = False):

        formatted_response = {
            'domain': domain,
            'rr_types':
            ['a', 'dnssec', 'dnskey', 'rrsig', 'nsec', 'ds', 'soa'],
            'answer': {
                'validation': {
                    'a': None,
                    'dnssec': None
                },
                'signatures': {
                    'rrsig': None,
                    'nsec': None,
                    'ds': None,
                    'soa': None
                }
            }
        }

        validation_response = self.dnssec_validate(
            domain=domain, as_json=False).get_response()
        formatted_response['answer']['validation'] = validation_response[
            'answer']

        dnssec_sigs_response = self.get_dnssec_sigs(
            domain=domain, as_json=False).get_response()
        formatted_response['answer']['signatures'] = dnssec_sigs_response[
            'answer']

        if as_json:
            return json.dumps(formatted_response)
        return DNSSECFormattedResponse(formatted_response)

    def dnssec_validate(self, domain: str, as_json: bool = False):
        """Accepts a domain: str.
        Returns a formatted answer dictionary with dnssec validation results in the 'answer.'"""
        formatted_answer = {
            'domain': domain,
            'rr_types': ["a", "dnssec"],
            'answer': None
        }

        status, result = Resolver.ctx_dnssec.resolve(domain, ub.RR_TYPE_A)
        if status == 0 and result.havedata:
            formatted_answer['answer'] = {}
            ip_address_list = result.data.address_list
            for ip in ip_address_list:
                if result.secure:
                    formatted_answer['answer'][ip] = "secure"
                elif result.bogus:
                    formatted_answer['answer'][ip] = "bogus"
                else:
                    formatted_answer['answer'][ip] = "insecure"
        if as_json:
            return json.dumps(formatted_answer)
        return DNSSECValidatedFormattedResponse(formatted_answer)

    def get_dnssec_sigs(self, domain: str, as_json: bool = False):
        """
        Accepts a domain name and acquires all DNSSEC records according to RFC4034 and RFC4035: DNSKEY, RRSIG, NSEC, DS.
        Additionally, it also gets the SOA record. This method does not validate DNSSEC. It only checks for proper
        signatures.
        """

        # fetch
        formatted_answer = {
            'domain': domain,
            'rr_types': ['dnskey', 'rrsig', 'nsec', 'ds', 'soa'],
            'answer': None
        }
        dnskey_fa = self.get_dnskeys(domain, as_json=False).get_response()
        rrsig_fa = self.get_rrsigs(domain, as_json=False).get_response()
        nsec_fa = self.get_nsec(domain, as_json=False).get_response()
        ds_fa = self.get_ds(domain, as_json=False).get_response()
        soa_fa = self.get_soa(domain, as_json=False).get_response()

        # construct multi-resource record answer
        formatted_answer['answer'] = {}

        if soa_fa['answer'] is not None and len(soa_fa['answer']) > 0:
            keys = list(soa_fa['answer'].keys())
            for key in keys:
                formatted_answer['answer']['soa'] = []
                formatted_answer['answer']['soa'].append(soa_fa['answer'][key])
        else:
            formatted_answer['answer']['soa'] = None

        if dnskey_fa['answer'] is not None and len(dnskey_fa['answer']) > 0:
            keys = list(dnskey_fa['answer'].keys())
            for key in keys:
                formatted_answer['answer']['dnskey'] = []
                formatted_answer['answer']['dnskey'].append(
                    dnskey_fa['answer'][key])
        else:
            formatted_answer['answer']['dnskey'] = None

        if rrsig_fa['answer'] is not None and len(rrsig_fa['answer']) > 0:
            keys = list(rrsig_fa['answer'].keys())
            for key in keys:
                formatted_answer['answer']['rrsig'] = []
                formatted_answer['answer']['rrsig'].append(
                    rrsig_fa['answer'][key])
        else:
            formatted_answer['answer']['rrsig'] = None

        if nsec_fa['answer'] is not None and len(nsec_fa['answer']) > 0:
            keys = list(nsec_fa['answer'].keys())
            for key in keys:
                formatted_answer['answer']['nsec'] = []
                formatted_answer['answer']['nsec'].append(
                    nsec_fa['answer'][key])
        else:
            formatted_answer['answer']['nsec'] = None

        if ds_fa['answer'] is not None and len(ds_fa['answer']) > 0:
            keys = list(ds_fa['answer'].keys())
            for key in keys:
                formatted_answer['answer']['ds'] = []
                formatted_answer['answer']['ds'].append(ds_fa['answer'][key])
        else:
            formatted_answer['answer']['ds'] = None

        if as_json:
            return json.dumps(formatted_answer)
        return DNSSECSignaturesFormattedResponse(formatted_answer)

    def get_dnskeys(self, domain: str, as_json: bool = False):
        """Accepts a domain: str. Returns a formatted answer dictionary, including dnskey records in the 'answer'."""
        formatted_answer = {
            'domain': domain,
            'rr_types': ["dnskey"],
            'answer': None
        }

        status, result = Resolver.ctx_dnssec.resolve(domain,
                                                     rrtype=ub.RR_TYPE_DNSKEY)

        if status == 0 and result.havedata == 1:
            print("returned dnskeys.")
            formatted_answer['answer'] = {}
            dns_keys_list = result.data.data  # list of dnskeys. *should* return None or non-empty list
            i = 0
            for key in dns_keys_list:  # place into dict
                if as_json:
                    formatted_answer['answer'][i] = str(key)
                else:
                    formatted_answer['answer'][i] = key
                i += 1
        elif status != 0:  # throw/raise error
            print("Resolve error: ", ub.ub_strerror(status))
        elif result.havedata == 0:  # if no data in result
            print("No data.")

        if as_json:
            return json.dumps(formatted_answer)
        return DNSFormattedResponse(formatted_answer)

    def get_rrsigs(self, domain: str, as_json: bool = False):
        """Accepts a domain: str. Returns a formatted answer dictionary, with rrsig records in the 'answer'."""
        formatted_answer = {
            'domain': domain,
            'rr_types': ["rrsig"],
            'answer': None
        }

        status, result = Resolver.ctx_dnssec.resolve(domain,
                                                     rrtype=ub.RR_TYPE_RRSIG)
        if status == 0 and result.havedata:
            print("rrsigs returned.")
            rrsig_list = result.data.data
            formatted_answer['answer'] = {}
            i = 0
            for sig in rrsig_list:
                if as_json:
                    formatted_answer['answer'][i] = str(sig)
                else:
                    formatted_answer['answer'][i] = sig
                i += 1
        elif status != 0:  # throw/raise error
            print("Resolve error: ", ub.ub_strerror(status))
        elif result.havedata == 0:  # if no data in result
            print("No data.")
            print(result.rcode_str)
        elif result.rcode != 0:
            print(result.rcode_str)

        if as_json:
            return json.dumps(formatted_answer)
        return DNSFormattedResponse(formatted_answer)

    def get_ds(self, domain: str, as_json: bool = False):
        """Accepts a domain. Returns a formatted answer dictionary with ds records in the 'answer'."""
        formatted_answer = {
            'domain': domain,
            'rr_types': ["ds"],
            'answer': None
        }

        status, result = Resolver.ctx_dnssec.resolve(domain,
                                                     rrtype=ub.RR_TYPE_DS)

        if status == 0 and result.havedata:
            print("ds record returned.")
            formatted_answer['answer'] = {}
            ds_records_list = result.data.data
            i = 0
            for ds in ds_records_list:
                if as_json:
                    formatted_answer['answer'][i] = str(ds)
                else:
                    formatted_answer['answer'][i] = ds
                i += 0
        elif status != 0:  # throw/raise error
            print("Resolve error: ", ub.ub_strerror(status))
        elif result.havedata == 0:  # if no data in result
            print("No data.")
        if as_json:
            return json.dumps(formatted_answer)
        return DNSFormattedResponse(formatted_answer)

    def get_nsec(self, domain: str, as_json: bool = False):
        """Accepts a domain: str. Returns a formatted answer dictionary with the nsec records inside the 'answer'."""
        formatted_answer = {
            'domain': domain,
            'rr_types': ["nsec"],
            'answer': None
        }

        status, result = Resolver.ctx_dnssec.resolve(domain,
                                                     rrtype=ub.RR_TYPE_NSEC)
        if status == 0 and result.havedata:
            nsec_list = result.data.data
            formatted_answer['answer'] = {}
            i = 0
            for nsec in nsec_list:
                if as_json:
                    formatted_answer['answer'][i] = str(nsec)
                else:
                    formatted_answer['answer'][i] = nsec
                i += 1
        elif status != 0:  # throw/raise error
            print("Resolve error: ", ub.ub_strerror(status), result.rcode_str)
        elif result.havedata == 0:  # if no data in result
            print("No data", result.rcode_str)

        if as_json:
            return json.dumps(formatted_answer)
        return DNSFormattedResponse(formatted_answer)
Esempio n. 22
0
    print("Resolving domain", domain)
    s, r = resolver.resolve(domain)
    print("status: %s, secure: %s, rcode: %s, havedata: %s, answer_len; %s" % (s, r.secure, r.rcode_str, r.havedata, r.answer_len))
    
    s, pkt = ldns.ldns_wire2pkt(r.packet)
    if s != 0:
        raise RuntimeError("Error parsing DNS packet")

    rrsigs = pkt.rr_list_by_type(RR_TYPE_RRSIG, ldns.LDNS_SECTION_ANSWER)
    print("RRSIGs from answer:", sorted(rrsigs))
    
    rrsigs = pkt.rr_list_by_type(RR_TYPE_RRSIG, ldns.LDNS_SECTION_AUTHORITY)
    print("RRSIGs from authority:", sorted(rrsigs))
    
    nsecs = pkt.rr_list_by_type(RR_TYPE_NSEC, ldns.LDNS_SECTION_AUTHORITY)
    print("NSECs:", sorted(nsecs))
    
    nsec3s = pkt.rr_list_by_type(RR_TYPE_NSEC3, ldns.LDNS_SECTION_AUTHORITY)
    print("NSEC3s:", sorted(nsec3s))
    
    print("---")


resolver = ub_ctx()
resolver.add_ta(".   IN DS   19036 8 2 49AAC11D7B6F6446702E54A1607371607A1A41855200FD2CE1CDDE32F24E8FB5")

dnssecParse("nic.cz")
dnssecParse("nonexistent-domain-blablabla.cz")
dnssecParse("nonexistent-domain-blablabla.root.cz")

Esempio n. 23
0
#!/usr/bin/env python
from unbound import ub_ctx, ub_version
from binascii import hexlify

RR_TYPE_TLSA = 52

print "Unbound version:", ub_version()

u = ub_ctx()
u.add_ta_file("keys.tlsa-test")
u.set_fwd("127.0.0.1")
u.set_option("dlv-anchor:", "dlv.isc.org. IN DNSKEY 257 3 5 BEAAAAPHMu/5onzrEE7z1egmhg/WPO0+juoZrW3euWEn4MxDCE1+lLy2 brhQv5rN32RKtMzX6Mj70jdzeND4XknW58dnJNPCxn8+jAGl2FZLK8t+ 1uq4W+nnA3qO2+DL+k6BD4mewMLbIYFwe0PG73Te9fZ2kJb56dhgMde5 ymX4BI/oQ+ cAK50/xvJv00Frf8kw6ucMTwFlgPe+jnGxPPEmHAte/URk Y62ZfkLoBAADLHQ9IrS2tryAe7mbBZVcOwIeU/Rw/mRx/vwwMCTgNboM QKtUdvNXDrYJDSHZws3xiRXF1Rf+al9UmZfSav/4NWLKjHzpT59k/VSt TDN0YUuWrBNh")

for fqdn in ("torproject.org", "labs.nic.cz", "fedoraproject.org", "nohats.ca", "rogue.nohats.ca"):
	s, r = u.resolve("_443._tcp." + fqdn, RR_TYPE_TLSA)
	print "fqdn: %s, status: %s, rcode: %s, secure: %s, bogus: %s, why_bogus: %s" % \
	    (fqdn, s, r.rcode, r.secure, r.bogus, r.why_bogus)
        if r.havedata:
            print [hexlify(rr) for rr in r.data.data]
	
Esempio n. 24
0
    def resolve(self, name, qtype):
        '''

        Resolves a Blockchain-based (Namecoin) DNS Name via 2 step process using DNSSEC

        Step 1:
        -------
        Get NS and DS records for the Namecoin name (for example: www.mattdavid.bit) from the Namecoin Client

        Step 2:
        -------
        For each listed nameserver:

            - Create a temporary config file for use with unbound
            - Set Unbound's Trust Anchor to be the given DS records for the Namecoin-based domain name
            - Do DNSSEC-enabled DNS resolution for the given name / qtype


        :param name: DNS Record Name Query (for example: www.mattdavid.bit)
        :param qtype: String representation of query type (for example: A, AAAA, TXT, NS, SOA, etc...)
        :return: Resolved value if successful, None if un-successful
        '''

        name = name.rstrip('.')
        if not name.endswith('.bit'):
            raise ValueError('This is not a valid .bit domain')

        domains = name.split('.')
        domains.reverse()
        if len(domains) < 2:
            raise ValueError('At least SLD Required')

        # Get Namecoin-based Domain Info from Namecoin Blockchain
        nc_domain = self.nc_name_resolver.name_show(domains[1])
        if not nc_domain or not nc_domain.get('value'):
            log.error('No Name Value Data Found for Namecoin-based Domain Name: d/%s' % domains[1])
            raise NamecoinValueException('No Name Value Data Found for: d/%s' % domains[1])

        nc_value = json.loads(nc_domain.get('value', '{}').replace('\'','"'))
        if not nc_value.get('ds'):
            log.error('No DS Records Present for Namecoin-based Domain Name: %s' % name)
            raise NoDSRecordException()

        if not nc_value.get('ns'):
            log.error('No NS Records Present for Namecoin-based Domain Name: %s' % name)
            raise NoNameserverException()

        sld = '%s.%s.' % (domains[1], domains[0])
        ds_record = ' '.join([str(x) for x in nc_value['ds'][0][0:3]])

        # Handle both Hex and Base64 encoding (Base64 is the preferred encoding) per:
        # https://wiki.namecoin.info/index.php?title=Domain_Name_Specification
        if re.match('^[0-9a-fA-F]*$', nc_value['ds'][0][3]):
            ds_record += ' %s' % nc_value['ds'][0][3]
        else:
            ds_record += ' %s' % base64.b64decode(nc_value['ds'][0][3]).encode('hex').upper()

        if qtype in ['DS']:
            return ds_record
        ds_ta = '%s IN DS %s' % (sld, ds_record)

        ns_ctx = ub_ctx()
        ns_ctx.resolvconf(self.resolv_conf)

        if not os.path.isfile(self.dnssec_root_key):
            log.error("Trust anchor missing or inaccessible")
            raise Exception("Trust anchor is missing or inaccessible: %s" % self.dnssec_root_key)
        else:
            ns_ctx.add_ta_file(self.dnssec_root_key)

        last_error = None
        for ns in nc_value.get('ns', []):

            lookup_value = None

            status, result = ns_ctx.resolve(ns, rdatatype.from_text('A'), rdataclass.from_text('IN'))

            # NOTE: We do not require secure DNS resolution here because the Blockchain-stored DS records work as the trust anchor
            # and the signed RRSIG DNS results from the final DNS+DNSSEC lookup will be able to complete the chain of trust
            if status == 0 and result and result.data and not result.bogus:
                tmp_config_file = self._build_temp_unbound_config(sld, result.data.as_address_list()[0])
            else:
                last_error = InvalidNameserverException()
                log.warn('No or Invalid Resolution Result for Nameserver: %s' % ns)
                continue

            ctx = ub_ctx()
            ctx.config(tmp_config_file)
            ctx.add_ta(str(ds_ta))

            _qtype = None
            try:
                _qtype = rdatatype.from_text(qtype)
            except Exception as e:
                log.error('Unable to get RDATAType for Given Query Type [%s]: %s' % (qtype, str(e)))
                raise ValueError('Unable to get RDATAType for Query Type %s' % qtype)

            status, result = ctx.resolve(name, _qtype, rdataclass.from_text('IN'))
            if status != 0:
                log.info("DNS Resolution Failed: %s [%s]" % (name, _qtype))
            elif status == 0:

                if not result.secure:
                    log.info("DNS Resolution Returned Insecure Result: %s [%s]" % (name, qtype))
                    last_error = InsecureResultException()

                elif result.bogus:
                    log.info("DNS Resolution Returned Bogus Result: %s [%s]" % (name, qtype))
                    last_error = BogusResultException()

                elif not result.havedata:
                    log.info("DNS Resolution Returned Empty Result: %s [%s]" % (name, qtype))
                    last_error = EmptyResultException()

                else:
                    # Get appropriate data by query type
                    # TODO: Add SOA Record Processing
                    if qtype in ['A','AAAA']:
                        lookup_value = result.data.as_address_list()
                    elif qtype in ['CNAME','TXT','PTR','SPF','CAA','NAPTR']:
                        lookup_value = result.data.as_domain_list()
                    elif qtype in ['MX']:
                        lookup_value = result.data.as_mx_list()
                    elif qtype in ['NS']:
                        lookup_value = nc_value.get('ns', [])
                    elif qtype in ['TLSA']:
                        lookup_value = [NamecoinResolver.tlsa_convert(result.rawdata[0])]
                    else:
                        last_error = NotImplementedError('Unsupported DNS Query Type: %s' % qtype)

            self._delete_temp_unbound_config(tmp_config_file)

            if lookup_value:
                return lookup_value[0]

            if last_error and isinstance(last_error, NotImplementedError):
                raise last_error

        log.error('DNS Resolution Failed: %s [%s]' % (name, qtype))
        if last_error:
            raise last_error

        return None