From: Michael Tremer Date: Tue, 16 Dec 2025 16:41:16 +0000 (+0000) Subject: location: Add a command to export the database as a DNS zone file X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=fc41191a39dbd67c887197152b9762aeccaefca8;p=location%2Flibloc.git location: Add a command to export the database as a DNS zone file This is experimental and only supports the "ASN" format for now. Signed-off-by: Michael Tremer --- diff --git a/src/python/location/export.py b/src/python/location/export.py index 1d147b1..ac41f82 100644 --- a/src/python/location/export.py +++ b/src/python/location/export.py @@ -16,6 +16,7 @@ # # ############################################################################### +import datetime import io import ipaddress import logging @@ -298,3 +299,75 @@ class Exporter(object): if not directory: for writer in writers.values(): writer.print() + + +class ZoneExporter(object): + def __init__(self, db, format, origin, ttl=None): + self.db = db + self.origin = origin + self.ttl = ttl + + try: + self.type, self._format = self.formats[format] + except KeyError as e: + raise ValueError("Unsupported format for a zone: %s" % format) + + def export(self, f): + assert self.write, "No write method has been selected" + + created_at = datetime.datetime.fromtimestamp(self.db.created_at) + + # Write the header + f.write(";##############################################################################\n") + f.write("; IPFire Location DNS Zone\n") + f.write(";##############################################################################\n") + if self.db.license: + f.write("License: %s\n" % self.db.license) + if self.db.description: + f.write("%s" % self.db.description) + f.write("Updated At: %s" % created_at.isoformat()) + f.write(";##############################################################################\n") + + f.write("$ORIGIN %s" % self.origin) + if self.ttl: + f.write("$TTL %s" % self.ttl) + + # Write all networks + for network in self.db.networks: + self.write(f, network) + + def write(self, f, network): + rp = network.reverse_pointer(suffix="") + + # If we don't have a reverse pointer, we will have to split the network + # into its subnets and call ourselves again. + if rp is None: + for subnet in network.subnets: + self.write(f, subnet) + + return + + # Fetch the payload + content = self._format(self, network) + + # Skip the network if there is no payload + if content is None: + return + + f.write("%s IN %s %s\n" % (rp, self.type, content)) + + def _format_asn(self, network): + # Skip the network if it does not belong to an AS + if network.asn is None: + return + + # Fetch the ASN + asn = self.db.get_as(network.asn) + if asn is None: + return + + return "\"%s\"" % asn + + formats = { + "asn" : ("TXT", _format_asn), + } diff --git a/src/scripts/location.in b/src/scripts/location.in index b34cc91..535fe5f 100644 --- a/src/scripts/location.in +++ b/src/scripts/location.in @@ -188,6 +188,20 @@ class CLI(object): export.add_argument("objects", nargs="*", help=_("List country codes or ASNs to export")) export.set_defaults(func=self.handle_export) + # Export DNS Zones + export_zone = subparsers.add_parser("export-zone", + help=_("Exports the database in DNS zone format"), + ) + export_zone.add_argument("type", help=_("Output Type"), + choices=location.export.ZoneExporter.formats.keys(), + ) + export_zone.add_argument("--output", required=True, type=argparse.FileType("w"), + help=_("The file to write the output to")) + export_zone.add_argument("--origin", required=True, + help=_("The origin of the DNS zone")) + export_zone.add_argument("--ttl", help=_("The TTL of the DNS zone")) + export_zone.set_defaults(func=self.handle_export_zone) + args = parser.parse_args() # Configure logging @@ -604,6 +618,13 @@ class CLI(object): e = location.export.Exporter(db, writer) e.export(ns.directory, countries=countries, asns=asns, families=families) + def handle_export_zone(self, db, ns): + """ + Exports the database in DNS zone format + """ + e = location.export.ZoneExporter(db, format=ns.type, origin=ns.origin, ttl=ns.ttl) + e.export(ns.output) + def format_timedelta(t): s = []