diff --git a/zoneforge/api/zones.py b/zoneforge/api/zones.py index 18113fb..453bce2 100644 --- a/zoneforge/api/zones.py +++ b/zoneforge/api/zones.py @@ -5,6 +5,7 @@ from zoneforge.core import ( get_zones, create_zone, + create_zone_from_text, delete_zone, create_record, update_record, @@ -126,6 +127,25 @@ }, ) +zone_import_parser = reqparse.RequestParser() +zone_import_parser.add_argument( + "content", type=str, help="Content of the DNS Zone file", required=True +) + +zone_export_model = api.model( + "DnsZoneExport", + { + "name": fields.String(example="example.com."), + "content": fields.String( + example=""" + $ORIGIN example.com. + @ 36000 IN SOA ns1 hostmaster 20250116 28800 1800 2592000 86400 ; minimum (1 day) + @ 86400 IN NS ns1 + """ + ), + }, +) + zone_transfer_parser = reqparse.RequestParser() zone_transfer_parser.add_argument( "zone_name", @@ -327,6 +347,39 @@ def delete(self, zone_name: str): raise NotFound("A zone with that name does not exist.") +@api.route("//import") +class DnsZoneImport(Resource): + @api.marshal_with(zone_model) + def post(self, zone_name: str): + args = zone_import_parser.parse_args() + dns_name = dns.name.from_text(zone_name) + new_zone = create_zone_from_text( + zone_name=dns_name, + zone_text=args["content"], + zonefile_folder=current_app.config["ZONE_FILE_FOLDER"], + ) + return new_zone.to_response() + + +@api.route("//export") +class DnsZoneExport(Resource): + @api.marshal_with(zone_export_model) + def get(self, zone_name: str): + dns_name = dns.name.from_text(zone_name) + export_zone = get_zones( + zonefile_folder=current_app.config["ZONE_FILE_FOLDER"], + zone_name=dns_name, + )[0] + if not export_zone: + raise NotFound("A zone with that name does not exist.") + + export_data = { + "zone_name": zone_name, + "content": export_zone.to_text(), + } + return export_data + + @api.route("/transfer") class DnsZoneInboundTransfer(Resource): @api.expect(zone_transfer_parser) diff --git a/zoneforge/core/__init__.py b/zoneforge/core/__init__.py index 96c2357..b0423d1 100644 --- a/zoneforge/core/__init__.py +++ b/zoneforge/core/__init__.py @@ -53,7 +53,7 @@ def __setattr__(self, name, value): else: setattr(self._zone, name, value) - def to_response(self): + def to_response(self) -> dict: res = {} res["name"] = self.origin.to_text() res["record_count"] = self.record_count @@ -150,6 +150,22 @@ def create_zone( return new_zfzone +def create_zone_from_text( + zone_name: dns.name.Name, zone_text: str, zonefile_folder: str +) -> ZFZone: + try: + new_zone = dns.zone.from_text( + text=zone_text, + origin=zone_name, + relativize=True, + ) + except dns.exception.SyntaxError as e: + raise BadRequest("Invalid zone file content was provided") from e + new_zfzone = ZFZone(zone=new_zone, zonefile_folder=zonefile_folder) + new_zfzone.write_to_file() + return new_zfzone + + def delete_zone(zone_name: dns.name.Name, zonefile_folder: str) -> bool: zone_file_name = join(zonefile_folder, f"{zone_name}zone") if exists(zone_file_name):