From c5433bb95a40eb1b5a8685cb50869f1a738de0e5 Mon Sep 17 00:00:00 2001 From: Philippe Pittoli Date: Thu, 17 Jul 2025 23:49:52 +0200 Subject: [PATCH] Delegation WIP. --- src/storage.cr | 52 +++++++++++++++++++++++++++++++++++---------- src/storage/zone.cr | 31 ++++++++++++++++++++++++--- 2 files changed, 69 insertions(+), 14 deletions(-) diff --git a/src/storage.cr b/src/storage.cr index dd7803e..1d98248 100644 --- a/src/storage.cr +++ b/src/storage.cr @@ -15,6 +15,7 @@ class DNSManager::Storage getter zones : DODB::Storage::Common(Zone) getter zones_by_domain : DODB::Trigger::IndexCached(Zone) + getter zones_by_delegation : DODB::Trigger::Tags(Zone) getter tokens : DODB::Storage::Common(Token) getter tokens_by_uuid : DODB::Trigger::IndexCached(Token) @@ -48,12 +49,19 @@ class DNSManager::Storage DODB.no_index end end - @domains_by_owners = @domains.new_tags "owners", &.owners.map &.to_s - @zones = DODB::Storage::Common(Zone).new "#{@root}/zones", 5_000 - @zones_by_domain = @zones.new_index "domain", &.domain - @tokens = DODB::Storage::Common(Token).new "#{@root}/tokens", 5_000 - @tokens_by_uuid = @tokens.new_index "uuid", &.uuid - @tokens_by_domain = @tokens.new_partition "domain", &.domain + @domains_by_owners = @domains.new_tags "owners", &.owners.map &.to_s + @zones = DODB::Storage::Common(Zone).new "#{@root}/zones", 5_000 + @zones_by_domain = @zones.new_index "domain", &.domain + @zones_by_delegation = @zones.new_tags "delegation", do |z| + if delegation = z.delegation + [ delegation.nameserver1, delegation.nameserver2 ] + else + DODB.no_index + end + end + @tokens = DODB::Storage::Common(Token).new "#{@root}/tokens", 5_000 + @tokens_by_uuid = @tokens.new_index "uuid", &.uuid + @tokens_by_domain = @tokens.new_partition "domain", &.domain @zonefiledir = "#{@root}/bind9-zones" Dir.mkdir_p @zonefiledir @@ -77,6 +85,11 @@ class DNSManager::Storage def generate_bind9_zonefile(domain : String) : Nil zone = zone_must_exist! domain + if delegation = zone.delegation + Baguette::Log.info "zone #{domain} is delegated" + return + end + # Safe write. filename_final = "#{@zonefiledir}/#{zone.domain}" filename_wip = "#{filename_final}.wip" @@ -433,12 +446,29 @@ class DNSManager::Storage Response::Zone.new zone end - # TODO, FIXME: verifications + actually delegate the domain. - def delegate_domain(user_id : UserDataID, domain : String, nameserver1 : String, nameserver2 : String) : IPC::JSON - zone = zone_must_exist! domain - user_should_own! user_id, domain + # Deletes the zone and its tokens to wipe the zone data then delegates the domain. + def delegate_domain(user_id : UserDataID, domain_name : String, + nameserver1 : String, nameserver2 : String) : IPC::JSON + _ = zone_must_exist! domain_name + user_should_own! user_id, domain_name - Response::DomainDelegated.new domain, nameserver1, nameserver2 + # Name server domains verification. + return Response::InvalidDomainName.new unless Zone.is_domain_valid? nameserver1 + return Response::InvalidDomainName.new unless Zone.is_domain_valid? nameserver2 + + # Remove the related zone and their registered tokens. + zones_by_domain.delete domain_name + + # The domain may not have tokens. + tokens_by_domain.delete? domain_name + + # TODO, FIXME: actually delegate the domain. + + zone = Zone.new domain_name + zone.delegation = Zone::Delegation.new nameserver1, nameserver2 + zones_by_domain.update_or_create zone.domain, zone + + Response::DomainDelegated.new domain_name, nameserver1, nameserver2 end # Removes user data. diff --git a/src/storage/zone.cr b/src/storage/zone.cr index 07c52bc..479021f 100644 --- a/src/storage/zone.cr +++ b/src/storage/zone.cr @@ -29,6 +29,8 @@ class DNSManager::Storage::Zone property resources = [] of DNSManager::Storage::Zone::ResourceRecord property current_rrid : UInt32 = 0 + property delegation : Delegation? = nil + # We don't want to accept less than 30 seconds TTL. class_property ttl_limit_min = 30 @@ -37,6 +39,22 @@ class DNSManager::Storage::Zone def_clone + class Delegation + include JSON::Serializable + + property nameserver1 : String + property nameserver2 : String + + def initialize(@nameserver1, @nameserver2) + end + + def to_s(io : IO) + io << "#{@nameserver1} #{@nameserver2}\n" + end + + def_clone + end + alias Error = String # Store a Resource Record: A, AAAA, TXT, PTR, CNAME… @@ -918,12 +936,19 @@ class DNSManager::Storage::Zone def to_s(io : IO) io << "ZONE #{@domain} (#{@resources.size} rr)" + if delegation = @delegation + io << " delegated" + end end def pretty_print(io : IO) - io << "DOMAIN #{@domain}.\n" - @resources.each do |rr| - io << rr + if delegation = @delegation + io << "ZONE #{@domain} delegated (to '#{delegation.nameserver1}' and '#{delegation.nameserver2}')\n" + else + io << "ZONE #{@domain}.\n" + @resources.each do |rr| + io << rr + end end end