]> git.ipfire.org Git - location/libloc.git/commitdiff
location: Add a command to export the database as a DNS zone file
authorMichael Tremer <michael.tremer@ipfire.org>
Tue, 16 Dec 2025 16:41:16 +0000 (16:41 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Tue, 16 Dec 2025 16:41:16 +0000 (16:41 +0000)
This is experimental and only supports the "ASN" format for now.

Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
src/python/location/export.py
src/scripts/location.in

index 1d147b1fc998b96b73a3d24431fdb923d94b6fb4..ac41f8211353ca1695a0e2c83ea029a034273086 100644 (file)
@@ -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),
+       }
index b34cc912105f1c2ce032bea76427aa9e62e69c7f..535fe5f0b797ad741181121e3a67a1059402a311 100644 (file)
@@ -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 = []