dnsmanager/src/main.cr

223 lines
6.1 KiB
Crystal
Raw Normal View History

2020-12-03 17:13:40 +01:00
# require "http/server"
require "option_parser"
2020-01-23 20:42:01 +01:00
2020-10-18 02:49:45 +02:00
require "ipc"
require "ipc/json"
2020-01-23 20:42:01 +01:00
require "authd"
2020-10-18 02:49:45 +02:00
require "baguette-crystal-base"
2020-01-23 20:42:01 +01:00
2020-12-10 17:29:26 +01:00
require "./config"
2020-10-18 02:49:45 +02:00
module DNSManager
class Exception < ::Exception
end
class AuthorizationException < ::Exception
end
class NotLoggedException < ::Exception
end
class AdminAuthorizationException < ::Exception
end
end
require "./storage.cr"
require "./network.cr"
class DNSManager::Service < IPC
2020-12-03 17:13:40 +01:00
property configuration : Baguette::Configuration::DNSManager
2020-10-18 02:49:45 +02:00
getter storage : DNSManager::Storage
getter logged_users : Hash(Int32, AuthD::User::Public)
property authd : AuthD::Client
2020-10-18 02:49:45 +02:00
def initialize(@configuration, @authd_key : String)
super()
@storage = DNSManager::Storage.new @configuration.storage_directory, @configuration.recreate_indexes
2020-10-18 02:49:45 +02:00
@logged_users = Hash(Int32, AuthD::User::Public).new
# 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
2020-10-18 02:49:45 +02:00
end
def get_logged_user(event : IPC::Event)
2020-12-13 03:18:30 +01:00
@logged_users[event.fd]?
2020-10-18 02:49:45 +02:00
end
def decode_token(token : String)
@authd.decode_token token
2020-10-18 02:49:45 +02:00
end
def handle_request(event : IPC::Event)
2020-10-18 02:49:45 +02:00
request_start = Time.utc
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!
2020-10-18 02:49:45 +02:00
if request.nil?
raise "unknown request type"
end
reqname = request.class.name.sub /^DNSManager::Request::/, ""
Baguette::Log.debug "<< #{reqname}"
response = DNSManager::Response::Error.new "generic error"
begin
response = request.handle self, event
rescue e : AuthorizationException
Baguette::Log.error "#{reqname} authorization error"
response = DNSManager::Response::Error.new "authorization error"
rescue e : AdminAuthorizationException
Baguette::Log.error "#{reqname} no admin authorization"
response = DNSManager::Response::Error.new "admin authorization error"
rescue e : NotLoggedException
Baguette::Log.error "#{reqname} user not logged"
response = DNSManager::Response::Error.new "user not logged"
# Do not handle generic exception case: do not provide a response.
# rescue e # Generic case
# Baguette::Log.error "#{reqname} generic error #{e}"
end
# If clients sent requests with an “id” field, it is copied
# in the responses. Allows identifying responses easily.
response.id = request.id
schedule event.fd, response
2020-10-18 02:49:45 +02:00
duration = Time.utc - request_start
response_name = response.class.name.sub /^DNSManager::Response::/, ""
2020-10-18 02:49:45 +02:00
if response.is_a? DNSManager::Response::Error
Baguette::Log.warning ">> #{response_name} (#{response.reason})"
2020-10-18 02:49:45 +02:00
else
Baguette::Log.debug ">> #{response_name} (Total duration: #{duration})"
2020-10-18 02:49:45 +02:00
end
end
2020-01-23 20:42:01 +01:00
2020-10-18 02:49:45 +02:00
def run
2020-12-03 17:13:40 +01:00
Baguette::Log.title "Starting #{@configuration.service_name}"
2020-01-23 20:42:01 +01:00
2020-10-18 02:49:45 +02:00
self.loop do |event|
begin
case event.type
when LibIPC::EventType::Timer
2020-12-03 17:13:40 +01:00
Baguette::Log.debug "Timer" if @configuration.print_ipc_timer
2020-10-18 02:49:45 +02:00
when LibIPC::EventType::Connection
2020-10-18 02:49:45 +02:00
Baguette::Log.debug "connection from #{event.fd}"
when LibIPC::EventType::Disconnection
2020-10-18 02:49:45 +02:00
Baguette::Log.debug "disconnection from #{event.fd}"
2020-12-13 03:18:30 +01:00
@logged_users.delete event.fd
2020-10-18 02:49:45 +02:00
when LibIPC::EventType::MessageTx
2020-10-18 02:49:45 +02:00
Baguette::Log.debug "message sent to #{event.fd}"
when LibIPC::EventType::MessageRx
2020-12-13 03:18:30 +01:00
Baguette::Log.debug "message received from #{event.fd}"
2020-10-18 02:49:45 +02:00
handle_request event
2020-12-13 03:18:30 +01:00
2020-10-18 02:49:45 +02:00
else
Baguette::Log.warning "unhandled IPC event: #{event.class}"
2020-12-13 03:18:30 +01:00
if event.responds_to?(:fd)
fd = event.fd
Baguette::Log.warning "closing #{fd}"
close fd
2020-12-13 03:18:30 +01:00
@logged_users.delete fd
end
2020-10-18 02:49:45 +02:00
end
2020-12-13 03:18:30 +01:00
2020-10-18 02:49:45 +02:00
rescue exception
Baguette::Log.error "exception: #{typeof(exception)} - #{exception.message}"
end
end
end
end
2020-12-03 17:13:40 +01:00
def main
# First option parsing, same with all Baguette (service) applications.
simulation, no_configuration, configuration_file = Baguette::Configuration.option_parser
# Authd configuration.
authd_configuration = if no_configuration
Baguette::Log.info "do not load a configuration file."
Baguette::Configuration::Auth.new
else
# Configuration file is for dnsmanagerd.
Baguette::Configuration::Auth.get || Baguette::Configuration::Auth.new
end
if key_file = authd_configuration.shared_key_file
authd_configuration.shared_key = File.read(key_file).chomp
2020-01-23 20:42:01 +01:00
end
2020-12-03 17:13:40 +01:00
# DNSManagerd configuration.
configuration = if no_configuration
Baguette::Log.info "do not load a configuration file."
Baguette::Configuration::DNSManager.new
else
# In case there is a configuration file helping with the parameters.
Baguette::Configuration::DNSManager.get(configuration_file) ||
Baguette::Configuration::DNSManager.new
end
2020-01-23 20:42:01 +01:00
2020-10-18 02:49:45 +02:00
2020-12-03 17:13:40 +01:00
OptionParser.parse do |parser|
parser.on "-v verbosity-level", "--verbosity level", "Verbosity." do |opt|
Baguette::Log.info "Verbosity level: #{opt}"
configuration.verbosity = opt.to_i
end
parser.on "-k key-file", "--key-file file", "Key file." do |opt|
authd_configuration.shared_key = File.read(opt).chomp
Baguette::Log.debug "Authd key: #{authd_configuration.shared_key.not_nil!}"
end
# IPC Service options
parser.on "-s service_name", "--service_name service_name", "Service name (IPC)." do |service_name|
Baguette::Log.info "Service name: #{service_name}"
configuration.service_name = service_name
end
parser.on "-r storage_directory", "--root storage_directory", "Storage directory." do |storage_directory|
Baguette::Log.info "Storage directory: #{storage_directory}"
configuration.storage_directory = storage_directory
end
parser.on "-h", "--help", "Show this help" do
puts parser
exit 0
end
end
2020-10-18 02:49:45 +02:00
2020-12-03 17:13:40 +01:00
if authd_configuration.shared_key.nil?
Baguette::Log.error "No authd key file: cannot continue"
exit 1
end
if simulation
pp! authd_configuration, configuration
exit 0
end
authd_key = authd_configuration.shared_key.not_nil!
2020-12-03 17:13:40 +01:00
service = DNSManager::Service.new configuration, authd_key
2020-12-03 17:13:40 +01:00
service.run
end
2020-10-18 02:49:45 +02:00
2020-12-03 17:13:40 +01:00
main