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::Server
|
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)
|
|
|
|
|
2020-12-09 19:01:33 +01:00
|
|
|
property authd : AuthD::Client
|
2020-10-18 02:49:45 +02:00
|
|
|
|
2020-12-03 17:13:40 +01:00
|
|
|
def initialize(@configuration, @authd : AuthD::Client)
|
|
|
|
@storage = DNSManager::Storage.new @configuration.storage_directory
|
2020-10-18 02:49:45 +02:00
|
|
|
|
|
|
|
@logged_users = Hash(Int32, AuthD::User::Public).new
|
|
|
|
|
2020-12-03 17:13:40 +01:00
|
|
|
super @configuration.service_name
|
2020-10-18 02:49:45 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
def get_logged_user(event : IPC::Event::Events)
|
|
|
|
fd = event.connection.fd
|
|
|
|
|
|
|
|
@logged_users[fd]?
|
|
|
|
end
|
|
|
|
|
|
|
|
def decode_token(token : String)
|
2020-12-09 19:01:33 +01:00
|
|
|
@authd.decode_token token
|
2020-10-18 02:49:45 +02:00
|
|
|
end
|
|
|
|
|
|
|
|
def handle_request(event : IPC::Event::MessageReceived)
|
|
|
|
request_start = Time.utc
|
|
|
|
|
|
|
|
request = DNSManager.requests.parse_ipc_json event.message
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
send event.fd, response
|
|
|
|
|
|
|
|
duration = Time.utc - request_start
|
|
|
|
|
|
|
|
response_str = response.class.name.sub /^DNSManager::Response::/, ""
|
|
|
|
|
|
|
|
if response.is_a? DNSManager::Response::Error
|
|
|
|
Baguette::Log.warning ">> #{response_str} (#{response.reason})"
|
|
|
|
else
|
|
|
|
Baguette::Log.debug ">> #{response_str} (Total duration: #{duration})"
|
|
|
|
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
|
|
|
|
when IPC::Event::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 IPC::Event::Connection
|
|
|
|
Baguette::Log.debug "connection from #{event.fd}"
|
|
|
|
|
|
|
|
when IPC::Event::Disconnection
|
|
|
|
Baguette::Log.debug "disconnection from #{event.fd}"
|
|
|
|
fd = event.fd
|
|
|
|
|
|
|
|
@logged_users.delete fd
|
|
|
|
|
|
|
|
when IPC::Event::MessageSent
|
|
|
|
Baguette::Log.debug "message sent to #{event.fd}"
|
|
|
|
|
|
|
|
when IPC::Event::MessageReceived
|
|
|
|
Baguette::Log.debug "message sent to #{event.fd}"
|
|
|
|
|
|
|
|
handle_request event
|
|
|
|
else
|
|
|
|
Baguette::Log.warning "unhandled IPC event: #{event.class}"
|
|
|
|
end
|
|
|
|
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 = AuthD::Client.new
|
|
|
|
authd.key = authd_configuration.shared_key.not_nil!
|
|
|
|
|
|
|
|
service = DNSManager::Service.new configuration, authd
|
|
|
|
service.run
|
|
|
|
end
|
2020-10-18 02:49:45 +02:00
|
|
|
|
2020-12-03 17:13:40 +01:00
|
|
|
main
|