Dns manager daemon and clients can be built, once again.

This commit is contained in:
Philippe Pittoli 2023-02-15 19:21:49 +01:00
parent ee7d20cc21
commit a78eb8c889
8 changed files with 80 additions and 42 deletions

View File

@ -22,9 +22,9 @@ dependencies:
targets: targets:
dnsmanager: dnsmanagerd:
main: src/main.cr main: src/main.cr
dnsmanager-client: dnsmanager-client:
main: src/client/main.cr main: src/client/main.cr
license: MIT license: ISC

View File

@ -1,8 +1,15 @@
require "../../requests/*" require "../../requests/*"
class DNSManager::Client < IPC::Client class DNSManager::Client < IPC
property server_fd : Int32 = -1
def initialize 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 end
# TODO: parse_message should raise exception if response not anticipated # TODO: parse_message should raise exception if response not anticipated
@ -14,33 +21,50 @@ class DNSManager::Client < IPC::Client
em << DNSManager::Response::Error em << DNSManager::Response::Error
em.parse_ipc_json message em.parse_ipc_json message
end end
end
#
# Simple users.
#
# Simple users.
class DNSManager::Client < IPC::Client
def login(token : String) def login(token : String)
request = DNSManager::Request::Login.new token request = DNSManager::Request::Login.new token
send_now @server_fd.not_nil!, request send_now request
parse_message [ DNSManager::Response::Success ], read parse_message [ DNSManager::Response::Success ], read
end end
# Adding a full zone. # Adding a full zone.
def user_zone_add(zone : DNSManager::Storage::Zone) def user_zone_add(zone : DNSManager::Storage::Zone)
request = DNSManager::Request::AddOrUpdateZone.new 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 parse_message [ DNSManager::Response::Success, DNSManager::Response::InvalidZone ], read
end end
end
# Admin stuff. #
class DNSManager::Client < IPC::Client # Admin stuff.
#
def admin_maintainance(key : String, subject : DNSManager::Request::Maintainance::Subject, value : Int32? = nil) def admin_maintainance(key : String, subject : DNSManager::Request::Maintainance::Subject, value : Int32? = nil)
request = DNSManager::Request::Maintainance.new(key,subject) request = DNSManager::Request::Maintainance.new(key,subject)
if value if value
request.value = value request.value = value
end end
send_now @server_fd.not_nil!, request send_now request
parse_message [ DNSManager::Response::Success ], read parse_message [ DNSManager::Response::Success ], read
end 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 end

View File

@ -24,22 +24,29 @@ require "./storage.cr"
require "./network.cr" require "./network.cr"
class DNSManager::Service < IPC::Server class DNSManager::Service < IPC
property configuration : Baguette::Configuration::DNSManager property configuration : Baguette::Configuration::DNSManager
getter storage : DNSManager::Storage getter storage : DNSManager::Storage
getter logged_users : Hash(Int32, AuthD::User::Public) getter logged_users : Hash(Int32, AuthD::User::Public)
property authd : AuthD::Client property authd : AuthD::Client
def initialize(@configuration, @authd : AuthD::Client) def initialize(@configuration, @authd_key : String)
@storage = DNSManager::Storage.new @configuration.storage_directory super()
@storage = DNSManager::Storage.new @configuration.storage_directory, @configuration.recreate_indexes
@logged_users = Hash(Int32, AuthD::User::Public).new @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 end
def get_logged_user(event : IPC::Event::Events) def get_logged_user(event : IPC::Event)
@logged_users[event.fd]? @logged_users[event.fd]?
end end
@ -47,10 +54,13 @@ class DNSManager::Service < IPC::Server
@authd.decode_token token @authd.decode_token token
end end
def handle_request(event : IPC::Event::MessageReceived) def handle_request(event : IPC::Event)
request_start = Time.utc 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? if request.nil?
raise "unknown request type" raise "unknown request type"
@ -81,42 +91,39 @@ class DNSManager::Service < IPC::Server
# in the responses. Allows identifying responses easily. # in the responses. Allows identifying responses easily.
response.id = request.id response.id = request.id
send event.fd, response schedule event.fd, response
duration = Time.utc - request_start 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 if response.is_a? DNSManager::Response::Error
Baguette::Log.warning ">> #{response_str} (#{response.reason})" Baguette::Log.warning ">> #{response_name} (#{response.reason})"
else else
Baguette::Log.debug ">> #{response_str} (Total duration: #{duration})" Baguette::Log.debug ">> #{response_name} (Total duration: #{duration})"
end end
end end
def run def run
Baguette::Log.title "Starting #{@configuration.service_name}" Baguette::Log.title "Starting #{@configuration.service_name}"
@base_timer = configuration.ipc_timer
@timer = configuration.ipc_timer
self.loop do |event| self.loop do |event|
begin begin
case event case event.type
when IPC::Event::Timer when LibIPC::EventType::Timer
Baguette::Log.debug "Timer" if @configuration.print_ipc_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}" Baguette::Log.debug "connection from #{event.fd}"
when IPC::Event::Disconnection when LibIPC::EventType::Disconnection
Baguette::Log.debug "disconnection from #{event.fd}" Baguette::Log.debug "disconnection from #{event.fd}"
@logged_users.delete event.fd @logged_users.delete event.fd
when IPC::Event::MessageSent when LibIPC::EventType::MessageTx
Baguette::Log.debug "message sent to #{event.fd}" Baguette::Log.debug "message sent to #{event.fd}"
when IPC::Event::MessageReceived when LibIPC::EventType::MessageRx
Baguette::Log.debug "message received from #{event.fd}" Baguette::Log.debug "message received from #{event.fd}"
handle_request event handle_request event
@ -125,7 +132,7 @@ class DNSManager::Service < IPC::Server
if event.responds_to?(:fd) if event.responds_to?(:fd)
fd = event.fd fd = event.fd
Baguette::Log.warning "closing #{fd}" Baguette::Log.warning "closing #{fd}"
remove_fd fd close fd
@logged_users.delete fd @logged_users.delete fd
end end
@ -206,10 +213,9 @@ def main
exit 0 exit 0
end 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 service.run
end end

View File

@ -2,7 +2,7 @@ require "ipc"
require "json" require "json"
class IPC::JSON class IPC::JSON
def handle(service : IPC::Server, event : IPC::Event::Events) def handle(service : IPC, event : IPC::Event)
raise "unimplemented" raise "unimplemented"
end end
end end

View File

@ -13,7 +13,7 @@ class DNSManager::Request
def initialize(@key, @subject) def initialize(@key, @subject)
end end
def handle(dnsmanagerd : DNSManager::Service, event : IPC::Event::Events) def handle(dnsmanagerd : DNSManager::Service, event : IPC::Event)
# This request means serious business. # This request means serious business.
raise AdminAuthorizationException.new if key != dnsmanagerd.authd.key raise AdminAuthorizationException.new if key != dnsmanagerd.authd.key

View File

@ -6,7 +6,7 @@ class DNSManager::Request
def initialize(@token) def initialize(@token)
end end
def handle(dnsmanagerd : DNSManager::Service, event : IPC::Event::Events) def handle(dnsmanagerd : DNSManager::Service, event : IPC::Event)
user, _ = dnsmanagerd.decode_token token user, _ = dnsmanagerd.decode_token token
dnsmanagerd.logged_users[event.fd] = user dnsmanagerd.logged_users[event.fd] = user

View File

@ -8,7 +8,7 @@ class DNSManager::Request
def initialize(@zone) def initialize(@zone)
end end
def handle(dnsmanagerd : DNSManager::Service, event : IPC::Event::Events) def handle(dnsmanagerd : DNSManager::Service, event : IPC::Event)
user = dnsmanagerd.get_logged_user event user = dnsmanagerd.get_logged_user event
raise NotLoggedException.new if user.nil? raise NotLoggedException.new if user.nil?

View File

@ -12,13 +12,21 @@ class DNSManager::Storage
getter zones : DODB::CachedDataBase(Zone) getter zones : DODB::CachedDataBase(Zone)
getter zones_by_domain : DODB::Index(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 = DODB::CachedDataBase(UserData).new "#{@root}/user-data"
@user_data_by_uid = @user_data.new_index "uid", &.uid.to_s @user_data_by_uid = @user_data.new_index "uid", &.uid.to_s
@zones = DODB::CachedDataBase(Zone).new "#{@root}/zones" @zones = DODB::CachedDataBase(Zone).new "#{@root}/zones"
@zones_by_domain = @zones.new_index "domain", &.domain @zones_by_domain = @zones.new_index "domain", &.domain
Baguette::Log.info "storage initialized" 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 end
def get_user_data(uid : Int32) def get_user_data(uid : Int32)