diff --git a/shard.yml b/shard.yml index a3a38e5..f212aff 100644 --- a/shard.yml +++ b/shard.yml @@ -22,9 +22,9 @@ dependencies: targets: - dnsmanager: + dnsmanagerd: main: src/main.cr dnsmanager-client: main: src/client/main.cr -license: MIT +license: ISC diff --git a/src/client/lib/dnsmanager-client.cr b/src/client/lib/dnsmanager-client.cr index f8ba104..542dfee 100644 --- a/src/client/lib/dnsmanager-client.cr +++ b/src/client/lib/dnsmanager-client.cr @@ -1,8 +1,15 @@ require "../../requests/*" -class DNSManager::Client < IPC::Client +class DNSManager::Client < IPC + property server_fd : Int32 = -1 + def initialize - initialize "dnsmanager" + super() + fd = self.connect "dnsmanager" + if fd.nil? + raise "couldn't connect to 'auth' IPC service" + end + @server_fd = fd end # TODO: parse_message should raise exception if response not anticipated @@ -14,33 +21,50 @@ class DNSManager::Client < IPC::Client em << DNSManager::Response::Error em.parse_ipc_json message end -end + # + # Simple users. + # -# Simple users. -class DNSManager::Client < IPC::Client def login(token : String) request = DNSManager::Request::Login.new token - send_now @server_fd.not_nil!, request + send_now request parse_message [ DNSManager::Response::Success ], read end # Adding a full zone. def user_zone_add(zone : DNSManager::Storage::Zone) request = DNSManager::Request::AddOrUpdateZone.new zone - send_now @server_fd.not_nil!, request + send_now request parse_message [ DNSManager::Response::Success, DNSManager::Response::InvalidZone ], read end -end -# Admin stuff. -class DNSManager::Client < IPC::Client + # + # Admin stuff. + # + def admin_maintainance(key : String, subject : DNSManager::Request::Maintainance::Subject, value : Int32? = nil) request = DNSManager::Request::Maintainance.new(key,subject) if value request.value = value end - send_now @server_fd.not_nil!, request + send_now request parse_message [ DNSManager::Response::Success ], read end + + def send_now(msg : IPC::JSON) + m = IPCMessage::TypedMessage.new msg.type.to_u8, msg.to_json + write @server_fd, m + end + + def send_now(type : Request::Type, payload) + m = IPCMessage::TypedMessage.new type.value.to_u8, payload + write @server_fd, m + end + + def read + slice = self.read @server_fd + m = IPCMessage::TypedMessage.deserialize slice + m.not_nil! + end end diff --git a/src/main.cr b/src/main.cr index 6a97c53..5140083 100644 --- a/src/main.cr +++ b/src/main.cr @@ -24,22 +24,29 @@ require "./storage.cr" require "./network.cr" -class DNSManager::Service < IPC::Server +class DNSManager::Service < IPC property configuration : Baguette::Configuration::DNSManager getter storage : DNSManager::Storage getter logged_users : Hash(Int32, AuthD::User::Public) property authd : AuthD::Client - def initialize(@configuration, @authd : AuthD::Client) - @storage = DNSManager::Storage.new @configuration.storage_directory + def initialize(@configuration, @authd_key : String) + super() + @storage = DNSManager::Storage.new @configuration.storage_directory, @configuration.recreate_indexes @logged_users = Hash(Int32, AuthD::User::Public).new - super @configuration.service_name + # TODO: auth service isn't in the FDs pool. + # If the service crashes, dnsmanagerd won't know it. + @authd = AuthD::Client.new + authd.key = @authd_key + + self.timer @configuration.ipc_timer + self.service_init @configuration.service_name end - def get_logged_user(event : IPC::Event::Events) + def get_logged_user(event : IPC::Event) @logged_users[event.fd]? end @@ -47,10 +54,13 @@ class DNSManager::Service < IPC::Server @authd.decode_token token end - def handle_request(event : IPC::Event::MessageReceived) + def handle_request(event : IPC::Event) request_start = Time.utc - request = DNSManager.requests.parse_ipc_json event.message + array = event.message.not_nil! + slice = Slice.new array.to_unsafe, array.size + message = IPCMessage::TypedMessage.deserialize slice + request = DNSManager.requests.parse_ipc_json message.not_nil! if request.nil? raise "unknown request type" @@ -81,42 +91,39 @@ class DNSManager::Service < IPC::Server # in the responses. Allows identifying responses easily. response.id = request.id - send event.fd, response + schedule event.fd, response duration = Time.utc - request_start - response_str = response.class.name.sub /^DNSManager::Response::/, "" + response_name = response.class.name.sub /^DNSManager::Response::/, "" if response.is_a? DNSManager::Response::Error - Baguette::Log.warning ">> #{response_str} (#{response.reason})" + Baguette::Log.warning ">> #{response_name} (#{response.reason})" else - Baguette::Log.debug ">> #{response_str} (Total duration: #{duration})" + Baguette::Log.debug ">> #{response_name} (Total duration: #{duration})" end end def run Baguette::Log.title "Starting #{@configuration.service_name}" - @base_timer = configuration.ipc_timer - @timer = configuration.ipc_timer - self.loop do |event| begin - case event - when IPC::Event::Timer + case event.type + when LibIPC::EventType::Timer Baguette::Log.debug "Timer" if @configuration.print_ipc_timer - when IPC::Event::Connection + when LibIPC::EventType::Connection Baguette::Log.debug "connection from #{event.fd}" - when IPC::Event::Disconnection + when LibIPC::EventType::Disconnection Baguette::Log.debug "disconnection from #{event.fd}" @logged_users.delete event.fd - when IPC::Event::MessageSent + when LibIPC::EventType::MessageTx Baguette::Log.debug "message sent to #{event.fd}" - when IPC::Event::MessageReceived + when LibIPC::EventType::MessageRx Baguette::Log.debug "message received from #{event.fd}" handle_request event @@ -125,7 +132,7 @@ class DNSManager::Service < IPC::Server if event.responds_to?(:fd) fd = event.fd Baguette::Log.warning "closing #{fd}" - remove_fd fd + close fd @logged_users.delete fd end @@ -206,10 +213,9 @@ def main exit 0 end - authd = AuthD::Client.new - authd.key = authd_configuration.shared_key.not_nil! + authd_key = authd_configuration.shared_key.not_nil! - service = DNSManager::Service.new configuration, authd + service = DNSManager::Service.new configuration, authd_key service.run end diff --git a/src/network.cr b/src/network.cr index bef959a..b714a0b 100644 --- a/src/network.cr +++ b/src/network.cr @@ -2,7 +2,7 @@ require "ipc" require "json" class IPC::JSON - def handle(service : IPC::Server, event : IPC::Event::Events) + def handle(service : IPC, event : IPC::Event) raise "unimplemented" end end diff --git a/src/requests/admin.cr b/src/requests/admin.cr index 6a11583..cb44b51 100644 --- a/src/requests/admin.cr +++ b/src/requests/admin.cr @@ -13,7 +13,7 @@ class DNSManager::Request def initialize(@key, @subject) end - def handle(dnsmanagerd : DNSManager::Service, event : IPC::Event::Events) + def handle(dnsmanagerd : DNSManager::Service, event : IPC::Event) # This request means serious business. raise AdminAuthorizationException.new if key != dnsmanagerd.authd.key diff --git a/src/requests/login.cr b/src/requests/login.cr index 719f1f5..87cae57 100644 --- a/src/requests/login.cr +++ b/src/requests/login.cr @@ -6,7 +6,7 @@ class DNSManager::Request def initialize(@token) end - def handle(dnsmanagerd : DNSManager::Service, event : IPC::Event::Events) + def handle(dnsmanagerd : DNSManager::Service, event : IPC::Event) user, _ = dnsmanagerd.decode_token token dnsmanagerd.logged_users[event.fd] = user diff --git a/src/requests/zone.cr b/src/requests/zone.cr index 2aaec9d..2f9743a 100644 --- a/src/requests/zone.cr +++ b/src/requests/zone.cr @@ -8,7 +8,7 @@ class DNSManager::Request def initialize(@zone) end - def handle(dnsmanagerd : DNSManager::Service, event : IPC::Event::Events) + def handle(dnsmanagerd : DNSManager::Service, event : IPC::Event) user = dnsmanagerd.get_logged_user event raise NotLoggedException.new if user.nil? diff --git a/src/storage.cr b/src/storage.cr index 84f1262..19c1621 100644 --- a/src/storage.cr +++ b/src/storage.cr @@ -12,13 +12,21 @@ class DNSManager::Storage getter zones : DODB::CachedDataBase(Zone) getter zones_by_domain : DODB::Index(Zone) - def initialize(@root : String) + def initialize(@root : String, reindex : Bool = false) @user_data = DODB::CachedDataBase(UserData).new "#{@root}/user-data" @user_data_by_uid = @user_data.new_index "uid", &.uid.to_s @zones = DODB::CachedDataBase(Zone).new "#{@root}/zones" @zones_by_domain = @zones.new_index "domain", &.domain Baguette::Log.info "storage initialized" + + if reindex + Baguette::Log.debug "Reindexing user data..." + @user_data.reindex_everything! + Baguette::Log.debug "Reindexing zones..." + @zones.reindex_everything! + Baguette::Log.debug "Reindexed!" + end end def get_user_data(uid : Int32)