Exemple #1
0
def add_zonemd(zone, zonemd_algorithm='sha384', zonemd_ttl=None):
    """
    Add a ZONEMD record to a zone. This also removes any existing
    ZONEMD records in the zone.

    The ZONEMD record will be at the zone apex, and have an all-zero
    digest.

    If the TTL is not specified, then the TTL of the SOA record is
    used.

    @var zone: The zone object to update.
    @type zone: dns.zone.Zone
    @var zonemd_algorithm: The name of the algorithm to use,
                      "sha384", or the number of the algorithm to use.
    @type zonemd_algorithm: str
    @var zonemd_ttl: The TTL to use for the ZONEMD record, or None to
                     get this from the zone SOA.
    @type zonemd_ttl: int
    @rtype: dns.rdataset.Rdataset
    @raises ZoneDigestUnknownAlgorithm: zonemd_algorithm is unknown

    Returns the placeholder ZONEMD record added, as a ZONEMD object.
    """
    if zonemd_algorithm in ('sha384', 1):
        algorithm = 1
    else:
        msg = 'Unknown digest ' + zonemd_algorithm
        raise ZoneDigestUnknownAlgorithm(msg)

    empty_digest = _EMPTY_DIGEST_BY_ALGORITHM[algorithm]

    # Remove any existing ZONEMD from the zone.
    # Also find the first name, which will be the zone name.
    for name in zone:
        zone.delete_rdataset(name, ZONEMD_RTYPE)
    zone_name = min(zone.keys())

    # Get the zone name.
    zone_name = min(zone.keys())

    # Get the SOA.
    soa_rdataset = zone.get_rdataset(zone_name, dns.rdatatype.SOA)
    soa = soa_rdataset.items[0]

    # Get the TTL to use for our placeholder ZONEMD.
    if zonemd_ttl is None:
        zonemd_ttl = soa_rdataset.ttl

    # Build placeholder ZONEMD and add to the zone.
    placeholder = dns.rdataset.Rdataset(dns.rdataclass.IN, ZONEMD_RTYPE)
    placeholder.update_ttl(zonemd_ttl)
    placeholder_rdata = ZONEMD(dns.rdataclass.IN, soa.serial,
                               algorithm, empty_digest)
    placeholder.add(placeholder_rdata)
    zone.replace_rdataset(zone_name, placeholder)

    return placeholder_rdata
Exemple #2
0
def update_zonemd(zone, zonemd_algorithm='sha384'):
    """
    Calculate the digest of the zone and update the ZONEMD record's
    digest value with that.

    The ZONEMD record must already be present, for example having been
    added by the add_zonemd() function.

    This function does *not* change the serial value of the ZONEMD
    record.

    @var zone: The zone object to update.
    @type zone: dns.zone.Zone
    @var zonemd_algorithm: The name of the algorithm to use, "sha384".
    @type zonemd_algorithm: str
    @rtype: dns.rdataset.Rdataset
    @raises ZoneDigestUnknownAlgorithm: zonemd_algorithm is unknown

    Returns the ZONEMD record added, as a ZONEMD object.
    """
    zone_name = min(zone.keys())
    digest = calculate_zonemd(zone, zonemd_algorithm)
    zonemd = zone.find_rdataset(zone_name, ZONEMD_RTYPE).items[0]
    zonemd.digest = digest
    return zonemd
Exemple #3
0
def get_all_records( dnsServer, zoneName  ):
    zone = dns.zone.from_xfr(dns.query.xfr(dnsServer, zoneName))
    allKeys = zone.keys()
    result = []
    for eachKey in allKeys:
        record_str = zone[eachKey].to_text(eachKey)
        #print '\n\n', record_str, '\n\n'
        for rstr in record_str.split('\n'):
            record_list = rstr.split(' ')
            record_dict = {}
            record_dict['ttl'] = record_list[1]
            record_dict['class'] = record_list[2]
            record_dict['type'] = record_list[3]
            if record_dict['type'] == 'SOA':
                record_dict['name'] = zoneName
                record_dict['server'] = record_list[4]
                record_dict['admin'] = record_list[5]
                record_dict['serial'] = record_list[6]
                record_dict['refresh'] = record_list[7]
                record_dict['retry'] = record_list[8]
                record_dict['expire'] = record_list[9]
                record_dict['minimum'] = record_list[10]
            else:
                record_dict['name'] = record_list[0]
                record_dict['value'] = record_list[4]
            result.append(record_dict)
    #return Response(json.dumps(result, separators=(',',':'), encoding='utf-8'))
    return result
Exemple #4
0
def validate_zonemd(zone):
    """
    Validate the digest of the zone.

    @var zone: The zone object to validate.
    @type zone: dns.zone.Zone
    @rtype: (bool, str) tuple

    Returns a tuple of (success code, error message). The success code
    is True if the digest is correct, and False otherwise. The error
    message is "" if there is no error, otherwise a description of the
    problem.
    """
    # Get the SOA and ZONEMD records for the zone.
    zone_name = min(zone.keys())
    soa_rdataset = zone.get_rdataset(zone_name, dns.rdatatype.SOA)
    soa = soa_rdataset.items[0]
    #    zonemd = zone.find_rdataset(zone_name, ZONEMD_RTYPE).items[0]

    original_digests = {}
    for zonemd in zone.find_rdataset(zone_name, ZONEMD_RTYPE).items:
        # Verify that the SOA matches between the SOA and the ZONEMD.
        if soa.serial != zonemd.serial:
            err = ("SOA serial " + str(soa.serial) + " does not " +
                   "match ZONEMD serial " + str(zonemd.serial))
            return False, err

        # Save the original digest.
        if zonemd.algorithm in original_digests:
            err = ("Digest algorithm " + str(zonemd.algorithm) +
                   "used more than once")
            return False, err
        original_digests[zonemd.algorithm] = zonemd.digest

        # Put a placeholder in for the ZONEMD.
        if zonemd.algorithm in _EMPTY_DIGEST_BY_ALGORITHM:
            zonemd.digest = _EMPTY_DIGEST_BY_ALGORITHM[zonemd.algorithm]
        else:
            zonemd.digest = b'\0' * len(zonemd.digest)

    # Calculate the digest.
    digest = calculate_zonemd(zone)

    # Restore ZONEMD.
    for zonemd in zone.find_rdataset(zone_name, ZONEMD_RTYPE).items:
        zonemd.digest = original_digests[zonemd.algorithm]

    # Verify the digest in the zone matches the calculated value.
    if digest != original_digests[ZONEMD_DIGEST_SHA384]:
        zonemd_b2a = binascii.b2a_hex(original_digest[ZONEMD_DIGEST_SHA384])
        zonemd_hex = zonemd_b2a.decode()
        digest_hex = binascii.b2a_hex(digest).decode()
        err = ("ZONEMD digest " + zonemd_hex + " does not " +
               "match calculated digest " + digest_hex)
        return False, err

    # Everything matches, enjoy your zone.
    return True, ""
Exemple #5
0
def calculate_zonemd(zone, zonemd_algorithm='sha384'):
    """
    Calculate the digest of the zone.

    Returns the digest for the zone.

    @var zone: The zone object to digest.
    @type zone: dns.zone.Zone
    @var zonemd_algorithm: The name of the algorithm to use, either "sha384",
                          or the number of the algorithm to use.
    @type zonemd_algorithm: str
    @raises ZoneDigestUnknownAlgorithm: zonemd_algorithm is unknown
    @rtype: bytes
    """
    if zonemd_algorithm in ('sha384', ZONEMD_DIGEST_SHA384):
        hashing = hashlib.sha384()

    # Sort the names in the zone. This is needed for canonization.
    sorted_names = sorted(zone.keys())

    # Iterate across each name in canonical order.
    for name in sorted_names:
        # Save the wire format of the name for later use.
        wire_name = name.canonicalize().to_wire()

        # Iterate across each RRSET in canonical order.
        sorted_rdatasets = sorted(zone.find_node(name).rdatasets,
                                  key=lambda rdataset: rdataset.rdtype)
        for rdataset in sorted_rdatasets:
            # Skip the RRSIG for ZONEMD.
            if rdataset.rdtype == dns.rdatatype.RRSIG:
                if rdataset.covers == ZONEMD_RTYPE:
                    continue

            # Save the wire format of the type, class, and TTL for later use.
            wire_set = struct.pack('!HHI', rdataset.rdtype, rdataset.rdclass,
                                   rdataset.ttl)
            # Extract the wire format of the RDATA and sort them.
            wire_rdatas = []
            for rdata in rdataset:
                wire_rdatas.append(rdata.to_digestable())
            wire_rdatas.sort()

            # Finally update the digest for each RR.
            for wire_rr in wire_rdatas:
                hashing.update(wire_name)
                hashing.update(wire_set)
                hashing.update(struct.pack('!H', len(wire_rr)))
                hashing.update(wire_rr)

    return hashing.digest()
Exemple #6
0
def validate_zonemd(zone):
    """
    Validate the digest of the zone.

    @var zone: The zone object to validate.
    @type zone: dns.zone.Zone
    @rtype: (bool, str) tuple

    Returns a tuple of (success code, error message). The success code
    is True if the digest is correct, and False otherwise. The error
    message is "" if there is no error, otherwise a description of the
    problem.
    """
    # Get the SOA and ZONEMD records for the zone.
    zone_name = min(zone.keys())
    soa_rdataset = zone.get_rdataset(zone_name, dns.rdatatype.SOA)
    soa = soa_rdataset.items[0]
    zonemd = zone.find_rdataset(zone_name, ZONEMD_RTYPE).items[0]

    # Verify that the SOA matches between the SOA and the ZONEMD.
    if soa.serial != zonemd.serial:
        err = ("SOA serial " + str(soa.serial) + " does not " +
               "match ZONEMD serial " + str(zonemd.serial))
        return False, err

    # Verify that we understand the digest algorithm.
    if zonemd.algorithm not in _EMPTY_DIGEST_BY_ALGORITHM:
        err = "Unknown digest algorithm " + str(zonemd.algorithm)
        return False, err

    # Put a placeholder in for the ZONEMD.
    original_digest = zonemd.digest
    zonemd.digest = _EMPTY_DIGEST_BY_ALGORITHM[zonemd.algorithm]

    # Calculate the digest and restore ZONEMD.
    digest = calculate_zonemd(zone, zonemd.algorithm)
    zonemd.digest = original_digest

    # Verify the digest in the zone matches the calculated value.
    if digest != zonemd.digest:
        zonemd_hex = binascii.b2a_hex(zonemd.digest).decode()
        digest_hex = binascii.b2a_hex(digest).decode()
        err = ("ZONEMD digest " + zonemd_hex + " does not " +
               "match calculated digest " + digest_hex)
        return False, err

    # Everything matches, enjoy your zone.
    return True, ""
Exemple #7
0
def sync_zone(inwx_conn, origin, zone):
	print(origin)

	dnsorigin = dns.name.from_text(origin, origin=dns.name.empty)

	# create zone
	checkRet = inwx_conn.nameserver.list({'domain': origin})
	if not checkRet['resData']['domains']:
		print(" + Creating a new zone for %s" % origin)
		inwx_conn.nameserver.create({'domain': origin, 'type': 'MASTER', 'ns': NS})

	apizone = inwx_conn.nameserver.info({'domain': origin})['resData']['record']

	# remove old entries from inwx nameserver
	for record in apizone:
		name = '@' if record['name'] == origin else record['name'].rsplit(".%s" % origin, 1)[0]

		if record['type'] == 'NS' and name == '@':  # do not touch NS records on root of zone
			continue
		elif not dns.name.from_text(name, origin=dns.name.empty) in zone:
			print(" + Deleting record from %s (%r)" % (origin, record))
			inwx_conn.nameserver.deleteRecord({'id': record['id']})
			continue
		elif record['type'] not in ['A', 'AAAA', 'MX', 'SRV', 'TXT', 'CNAME', 'NS', 'PTR', 'SSHFP']:
			continue

		found = False

		for key in zone.keys():
			for dataset in zone[key].rdatasets:
				if dataset.ttl != record['ttl']:
					continue

				if dataset.rdtype in [dns.rdatatype.SOA]:
					continue

				for item in dataset.items:
					tmprecord = dns_item_to_record(dataset, item, dnsorigin, key)

					found = True
					for reckey in tmprecord:
						if tmprecord[reckey] != record[reckey]:
							found = False
							break
					if found:
						break
				if found:
					break
			if found:
				break

		if not found:
			print(" + Deleting record from %s (%r)" % (origin, record))
			inwx_conn.nameserver.deleteRecord({'id': record['id']})

	# create new entries from zonefile
	for key in zone.keys():
		for dataset in zone[key].rdatasets:
			for item in dataset.items:
				if dataset.rdtype in [dns.rdatatype.SOA]:
					continue

				# do not touch nameservers on root of zone
				if key.to_text() == '@' and dataset.rdtype == dns.rdatatype.NS:
					continue

				found = False
				tmprecord = dns_item_to_record(dataset, item, dnsorigin, key)

				for record in apizone:
					found = True
					for reckey in tmprecord:
						if tmprecord[reckey] != record[reckey]:
							found = False
							break
					if found:
						break

				if not found:
					print(" + Creating record on %s (%r)" % (origin, tmprecord))
					tmprecord['domain'] = origin
					inwx_conn.nameserver.createRecord(tmprecord)

	# update soa
	apizonesoa = list(record for record in apizone if record['name'] == origin and record['type'] == 'SOA')[0]
	split_apizonesoa = apizonesoa['content'].split()
	zonesoa_rname = dns_name_to_text(list(dataset for dataset in zone['@'].rdatasets if dataset.rdtype == dns.rdatatype.SOA)[0].items[0].rname, dnsorigin)

	if split_apizonesoa[0] != NS[0] or split_apizonesoa[1] != zonesoa_rname:
		apizonesoa['content'] = "%s %s %s" % (NS[0], zonesoa_rname, split_apizonesoa[2])
		print(" + Updating SOA record on %s (%r)" % (origin, apizonesoa))
		inwx_conn.nameserver.updateRecord(apizonesoa)
Exemple #8
0
    def dump_xml(self, zone, exclude=None):

        re_awsalias = re.compile(r'^AWSALIAS')
        # preprocess; this is annoying but necessary to support our little
        # TXT record shim: doing it inside dnspython is just painful
        rr_data = {}
        for rrname in zone.keys():
            rr_name = rrname.derelativize(zone.origin).to_text()
            rr_data[rr_name] = {}
            for rdataset in zone[rrname].rdatasets:
                rr_type = dns.rdatatype.to_text(rdataset.rdtype)
                rr_data[rr_name][rr_type] = {}
                rr_data[rr_name][rr_type]['TTL'] = str(rdataset.ttl)
                rr_data[rr_name][rr_type]['RRS'] = []
                for rdtype in rdataset.items:
                    rr_data[rr_name][rr_type]['RRS'].append(rdtype.to_text(origin=zone.origin,
                        relativize=False))

        # now deal with the ugliness of aws alias records
        for rr_name in rr_data:
            # first, convert any AWSALIAS txt records into A records
            if 'TXT' in rr_data[rr_name]:
                rr_vals_to_delete = []
                for rr_value in rr_data[rr_name]['TXT']['RRS']:
                    if re_awsalias.search(unquote(rr_value)):
                        (_, hosted_zone_id, dns_name) = unquote(rr_value).split(':')
                        # remove the awsalias from the TXT record set
                        rr_vals_to_delete.append(rr_value)
                        # add as an A record with an alias target
                        if 'A' not in rr_data[rr_name]:
                            rr_data[rr_name]['A'] = {}
                        rr_data[rr_name]['A']['AliasTarget'] = {}
                        rr_data[rr_name]['A']['AliasTarget']['HostedZoneId'] = hosted_zone_id
                        rr_data[rr_name]['A']['AliasTarget']['DNSName'] = dns_name
                for rr_value in rr_vals_to_delete:
                    del(rr_data[rr_name]['TXT']['RRS'][
                        rr_data[rr_name]['TXT']['RRS'].index(rr_value)])
                # if we've emptied the TXT set, delete it
                if not rr_data[rr_name]['TXT']['RRS']:
                    del rr_data[rr_name]['TXT']
            # now make sure there's no existing A record for that RR
            if 'A' in rr_data[rr_name]:
                if 'RRS' in rr_data[rr_name]['A'] and 'AliasTarget' in rr_data[rr_name]['A']:
                    raise ValueError(
                        'You cannot have both a static A record and an AWSALIAS'
                        ' at the same RR node: %s' % rr_name)

        # now spit it all back out as XML
        resource_record_sets = et.Element('ResourceRecordSets',
                xmlns=boto.route53.Route53Connection.XMLNameSpace)

        for rr_name in rr_data:
            for rr_type in rr_data[rr_name]:
                resource_record_set = et.SubElement(resource_record_sets, 'ResourceRecordSet')
                text_element(resource_record_set, 'Name', rr_name)
                text_element(resource_record_set, 'Type', rr_type)
                if 'AliasTarget' in rr_data[rr_name][rr_type]:
                    alias_target = et.SubElement(resource_record_set, 'AliasTarget')
                    text_element(alias_target, 'HostedZoneId',
                            rr_data[rr_name][rr_type]['AliasTarget']['HostedZoneId'])
                    text_element(alias_target, 'DNSName',
                            rr_data[rr_name][rr_type]['AliasTarget']['DNSName'])
                else:
                    text_element(resource_record_set, 'TTL', rr_data[rr_name][rr_type]['TTL'])
                    resource_records = et.SubElement(resource_record_set, 'ResourceRecords')
                    for rr_value in rr_data[rr_name][rr_type]['RRS']:
                        resource_record = et.SubElement(resource_records, 'ResourceRecord')
                        text_element(resource_record, 'Value', rr_value)

        out = StringIO()
        et.ElementTree(resource_record_sets).write(out)
        return out.getvalue()
Exemple #9
0
    def dump_xml(self, zone, exclude=None):

        re_awsalias = re.compile(r'^AWSALIAS')
        # preprocess; this is annoying but necessary to support our little
        # TXT record shim: doing it inside dnspython is just painful
        rr_data = {}
        for rrname in zone.keys():
            rr_name = rrname.derelativize(zone.origin).to_text()
            rr_data[rr_name] = {}
            for rdataset in zone[rrname].rdatasets:
                rr_type = dns.rdatatype.to_text(rdataset.rdtype)
                rr_data[rr_name][rr_type] = {}
                rr_data[rr_name][rr_type]['TTL'] = str(rdataset.ttl)
                rr_data[rr_name][rr_type]['RRS'] = []
                for rdtype in rdataset.items:
                    rr_data[rr_name][rr_type]['RRS'].append(rdtype.to_text(origin=zone.origin,
                        relativize=False))

        # now deal with the ugliness of aws alias records
        for rr_name in rr_data:
            # first, convert any AWSALIAS txt records into A records
            if 'TXT' in rr_data[rr_name]:
                rr_vals_to_delete = []
                for rr_value in rr_data[rr_name]['TXT']['RRS']:
                    if re_awsalias.search(unquote(rr_value)):
                        (_, hosted_zone_id, dns_name) = unquote(rr_value).split(':')
                        # remove the awsalias from the TXT record set
                        rr_vals_to_delete.append(rr_value)
                        # add as an A record with an alias target
                        if 'A' not in rr_data[rr_name]:
                            rr_data[rr_name]['A'] = {}
                        rr_data[rr_name]['A']['AliasTarget'] = {}
                        rr_data[rr_name]['A']['AliasTarget']['HostedZoneId'] = hosted_zone_id
                        rr_data[rr_name]['A']['AliasTarget']['DNSName'] = dns_name
                for rr_value in rr_vals_to_delete:
                    del(rr_data[rr_name]['TXT']['RRS'][
                        rr_data[rr_name]['TXT']['RRS'].index(rr_value)])
                # if we've emptied the TXT set, delete it
                if not rr_data[rr_name]['TXT']['RRS']:
                    del rr_data[rr_name]['TXT']
            # now make sure there's no existing A record for that RR
            if 'A' in rr_data[rr_name]:
                if 'RRS' in rr_data[rr_name]['A'] and 'AliasTarget' in rr_data[rr_name]['A']:
                    raise ValueError(
                        'You cannot have both a static A record and an AWSALIAS'
                        ' at the same RR node: %s' % rr_name)

        # now spit it all back out as XML
        resource_record_sets = et.Element('ResourceRecordSets',
                xmlns=boto.route53.Route53Connection.XMLNameSpace)

        for rr_name in rr_data:
            for rr_type in rr_data[rr_name]:
                resource_record_set = et.SubElement(resource_record_sets, 'ResourceRecordSet')
                text_element(resource_record_set, 'Name', rr_name)
                text_element(resource_record_set, 'Type', rr_type)
                if 'AliasTarget' in rr_data[rr_name][rr_type]:
                    alias_target = et.SubElement(resource_record_set, 'AliasTarget')
                    text_element(alias_target, 'HostedZoneId',
                            rr_data[rr_name][rr_type]['AliasTarget']['HostedZoneId'])
                    text_element(alias_target, 'DNSName',
                            rr_data[rr_name][rr_type]['AliasTarget']['DNSName'])
                else:
                    text_element(resource_record_set, 'TTL', rr_data[rr_name][rr_type]['TTL'])
                    resource_records = et.SubElement(resource_record_set, 'ResourceRecords')
                    for rr_value in rr_data[rr_name][rr_type]['RRS']:
                        resource_record = et.SubElement(resource_records, 'ResourceRecord')
                        text_element(resource_record, 'Value', rr_value)

        out = StringIO()
        et.ElementTree(resource_record_sets).write(out)
        return out.getvalue()