From 8528f74fae660b2bc229956ef8517dd1134a4fb8 Mon Sep 17 00:00:00 2001 From: Philippe Pittoli Date: Mon, 8 May 2023 17:34:50 +0200 Subject: [PATCH] Add and replace RR. --- src/client/lib/dnsmanager-client.cr | 27 +++++++----- src/client/main.cr | 17 +++++++- src/client/parser.cr | 13 ++++++ src/requests/zone.cr | 30 +++++++++++++ src/storage.cr | 66 ++++++++++++++++++++++++++++- src/storage/zone.cr | 6 +-- 6 files changed, 143 insertions(+), 16 deletions(-) diff --git a/src/client/lib/dnsmanager-client.cr b/src/client/lib/dnsmanager-client.cr index f7c61ac..7a2694a 100644 --- a/src/client/lib/dnsmanager-client.cr +++ b/src/client/lib/dnsmanager-client.cr @@ -18,7 +18,7 @@ class DNSManager::Client < IPC expected_messages.each do |e| em << e end - em << DNSManager::Response::Error + em << Response::Error em.parse_ipc_json message end @@ -27,35 +27,42 @@ class DNSManager::Client < IPC # def login(token : String) - request = DNSManager::Request::Login.new token + request = Request::Login.new token send_now request parse_message [ Response::Success ], read end # Adding a full zone. - def user_zone_add(zone : DNSManager::Storage::Zone) - request = DNSManager::Request::AddOrUpdateZone.new zone + def user_zone_add(zone : Storage::Zone) + request = Request::AddOrUpdateZone.new zone send_now request parse_message [ Response::Success ], read end # Removing a zone. def user_zone_del(domain : String) - request = DNSManager::Request::DeleteZone.new domain + request = Request::DeleteZone.new domain send_now request parse_message [ Response::Success ], read end # Get a zone. def user_zone_get(domain : String) - request = DNSManager::Request::GetZone.new domain + request = Request::GetZone.new domain send_now request parse_message [ Response::Zone ], read end + # Add a RR. + def user_rr_add(domain : String, rr : Storage::Zone::ResourceRecord) + request = Request::AddRR.new domain, rr + send_now request + parse_message [ Response::Success ], read + end + # Get user domain list. def user_domain_list() - request = DNSManager::Request::UserDomains.new + request = Request::UserDomains.new send_now request parse_message [ Response::DomainList ], read end @@ -64,13 +71,13 @@ class DNSManager::Client < IPC # Admin stuff. # - def admin_maintenance(key : String, subject : DNSManager::Request::Maintenance::Subject, value : Int32? = nil) - request = DNSManager::Request::Maintenance.new(key,subject) + def admin_maintenance(key : String, subject : Request::Maintenance::Subject, value : Int32? = nil) + request = Request::Maintenance.new(key,subject) if value request.value = value end send_now request - parse_message [ DNSManager::Response::Success ], read + parse_message [ Response::Success ], read end def send_now(msg : IPC::JSON) diff --git a/src/client/main.cr b/src/client/main.cr index 00180d8..8634173 100644 --- a/src/client/main.cr +++ b/src/client/main.cr @@ -35,12 +35,17 @@ class Actions # Admin section. # - # Maintenance + # Maintenance. @the_call["admin-maintenance"] = ->admin_maintenance + # Zone operations. @the_call["user-zone-add"] = ->user_zone_add @the_call["user-zone-del"] = ->user_zone_del @the_call["user-zone-get"] = ->user_zone_get + + # Zone RR operations. + @the_call["user-rr-add-a"] = ->user_rr_add_a + @the_call["user-domain-list"] = ->user_domain_list end @@ -129,6 +134,16 @@ class Actions puts "error for user_zone_del: #{e.message}" end + def user_rr_add_a + domain, name, ttl, target = Context.args.not_nil! + begin + rr = DNSManager::Storage::Zone::A.new name, ttl.to_u32, target + pp! @dnsmanagerd.user_rr_add domain, rr + rescue e + puts "error for user_rr_add_a: #{e.message}" + end + end + end def main diff --git a/src/client/parser.cr b/src/client/parser.cr index f2602e5..4fd4e78 100644 --- a/src/client/parser.cr +++ b/src/client/parser.cr @@ -143,6 +143,19 @@ def parsing_cli(authd_config : Baguette::Configuration::Auth) end end + parser.on("rr", "Zone Resource Record operations.") do + parser.on("add", "Add new RR.") do + Baguette::Log.info "Add RR." + parser.banner = "COMMAND: user rr add [A|AAAA|CNAME|MX|SRV|TXT|NS]" + parser.on("A", "Add new A RR.") do + Baguette::Log.info "add new A RR." + Context.command = "user-rr-add-a" + parser.banner = "COMMAND: user rr add A " + unrecognized_args_to_context_args.call parser, 4, nil + end + end + end + end opt_help.call parser diff --git a/src/requests/zone.cr b/src/requests/zone.cr index 315d73e..f7d6f75 100644 --- a/src/requests/zone.cr +++ b/src/requests/zone.cr @@ -56,4 +56,34 @@ class DNSManager::Request end end DNSManager.requests << UserDomains + + IPC::JSON.message AddRR, 14 do + property domain : String + property rr : DNSManager::Storage::Zone::ResourceRecord + + def initialize(@domain, @rr) + end + + def handle(dnsmanagerd : DNSManager::Service, event : IPC::Event) : IPC::JSON + user = dnsmanagerd.get_logged_user event + raise NotLoggedException.new if user.nil? + dnsmanagerd.storage.add_rr user.uid, @domain, @rr + end + end + DNSManager.requests << AddRR + + IPC::JSON.message UpdateRR, 15 do + property domain : String + property rr : DNSManager::Storage::Zone::ResourceRecord + + def initialize(@domain, @rr) + end + + def handle(dnsmanagerd : DNSManager::Service, event : IPC::Event) : IPC::JSON + user = dnsmanagerd.get_logged_user event + raise NotLoggedException.new if user.nil? + dnsmanagerd.storage.update_rr user.uid, @domain, @rr + end + end + DNSManager.requests << UpdateRR end diff --git a/src/storage.cr b/src/storage.cr index d78e93a..fe6a8f0 100644 --- a/src/storage.cr +++ b/src/storage.cr @@ -48,7 +48,7 @@ class DNSManager::Storage user_data = user_data_by_uid.get? user_id.to_s unless user_data Baguette::Log.info "New user #{user_id}" - @user_data << Storage::UserData.new user_id + @user_data << UserData.new user_id end Response::Success.new @@ -58,7 +58,7 @@ class DNSManager::Storage # Test zone validity. if errors = zone.get_errors? Baguette::Log.warning "zone #{zone.domain} update with errors: #{errors}" - return DNSManager::Response::InvalidZone.new errors + return Response::InvalidZone.new errors end # User must exist. @@ -92,6 +92,68 @@ class DNSManager::Storage Response::Error.new "error while updating the domain #{zone.domain}" end + def add_rr(user_id : Int32, domain : String, rr : Zone::ResourceRecord) : IPC::JSON + # User must exist. + user_data = user_data_by_uid.get? user_id.to_s + unless user_data + Baguette::Log.warning "unknown user #{user_id} tries to add a RR in domain #{domain}" + return Response::UnknownUser.new + end + + # Zone must exist. + zone = zones_by_domain.get? domain + unless zone + return Response::InvalidZone.new ["Domain not found."] + end + + # User must own the zone. + unless user_data.domains.includes? domain + Baguette::Log.warning "user #{user_id} doesn't own domain #{domain}" + return Response::NoOwnership.new + end + + zone << rr + + # Update the zone. + zones_by_domain.update_or_create zone.domain, zone + + Response::Success.new + rescue e + Baguette::Log.error "trying to add a resource record in domain #{domain}: #{e}" + Response::Error.new "error while adding a resource record in domain #{domain}" + end + + def update_rr(user_id : Int32, domain : String, rr : Zone::ResourceRecord) : IPC::JSON + # User must exist. + user_data = user_data_by_uid.get? user_id.to_s + unless user_data + Baguette::Log.warning "unknown user #{user_id} tries to add -or update- zone #{domain}" + return Response::UnknownUser.new + end + + # Zone must exist. + zone = zones_by_domain.get? domain + unless zone + return Response::InvalidZone.new ["Domain not found."] + end + + # User must own the zone. + unless user_data.domains.includes? domain + Baguette::Log.warning "user #{user_id} doesn't own domain #{domain}" + return Response::NoOwnership.new + end + + zone.resources.map { |x| x.rrid == rr.rrid ? rr : x } + + # Update the zone. + zones_by_domain.update_or_create zone.domain, zone + + Response::Success.new + rescue e + Baguette::Log.error "trying to replace a resource record in domain #{domain}: #{e}" + Response::Error.new "error while replacing a resource record in domain #{domain}" + end + def delete_domain(user_id : Int32, domain : String) : IPC::JSON # User must exist. user_data = user_data_by_uid.get? user_id.to_s diff --git a/src/storage/zone.cr b/src/storage/zone.cr index 2556655..03ea4bb 100644 --- a/src/storage/zone.cr +++ b/src/storage/zone.cr @@ -50,7 +50,7 @@ class DNSManager::Storage::Zone end def to_s(io : IO) - io << "#{ "%30s" % @name} #{ "%6d" % @ttl} IN #{ "%10s" % @rrtype.upcase} #{ "%30s" % @target}\n" + io << "(#{ "%4d" % @rrid }) #{ "%30s" % @name} #{ "%6d" % @ttl} IN #{ "%10s" % @rrtype.upcase} #{ "%30s" % @target}\n" end end @@ -70,7 +70,7 @@ class DNSManager::Storage::Zone end def to_s(io : IO) - io << "#{name} #{ttl} #{target} #{@rrtype.upcase} (#{mname} #{rname}\n" + io << "(#{ "%4d" % @rrid }) #{name} #{ttl} #{target} #{@rrtype.upcase} (#{mname} #{rname}\n" io << "\t\t#{ "%10d" % serial} ; serial\n" io << "\t\t#{ "%10d" % refresh} ; refresh\n" io << "\t\t#{ "%10d" % retry} ; retry\n" @@ -225,7 +225,7 @@ class DNSManager::Storage::Zone end def to_s(io : IO) - io << "#{ "%30s" % @name} #{ "%6d" % @ttl} IN MX #{ "%3d" % @priority} #{ "%30s" % @target}\n" + io << "(#{ "%4d" % @rrid }) #{ "%30s" % @name} #{ "%6d" % @ttl} IN MX #{ "%3d" % @priority} #{ "%30s" % @target}\n" end def get_errors : Array(Error)