232 lines
6.9 KiB
Ruby
232 lines
6.9 KiB
Ruby
# = ildus/server/handler - Ildus server command handler
|
|
#
|
|
# Copyright (C) 2005 Paul van Tilburg <paul@luon.net>
|
|
#
|
|
# Ildus is free software; you can redistribute it and/or modify it under
|
|
# the terms of the GNU General Public License as published by the Free
|
|
# Software Foundation; either version 2 of the License, or (at your option)
|
|
# any later version.
|
|
|
|
require 'ipaddr'
|
|
|
|
require 'ildus/server/backend'
|
|
|
|
module Ildus
|
|
|
|
class Server < GServer
|
|
|
|
class Handler
|
|
|
|
class ProtocolException < StandardError; end
|
|
|
|
class HandlerExit < ProtocolException; end # 221
|
|
|
|
class HostNotFoundError < ProtocolException; end # 425
|
|
class RecordNotFoundError < ProtocolException; end # 426
|
|
|
|
class CmdUnknownError < ProtocolException; end # 500
|
|
class ArgsSyntaxError < ProtocolException; end # 501
|
|
class NotImplementedError < ProtocolException; end # 502
|
|
class TooManyUnknownError < ProtocolException; end # 503
|
|
class AlreadyAuthError < ProtocolException; end # 504
|
|
class BackendError < ProtocolException; end # 506
|
|
class NotAuthError < ProtocolException; end # 530
|
|
class SetUserFirstError < ProtocolException; end # 531
|
|
|
|
MaxCmdErrs = 3
|
|
|
|
ProtocolCodeMapping = {
|
|
200 => "Command OK.",
|
|
# 210, 211, 212, 213
|
|
214 => "Direct comments to %s.",
|
|
215 => "Listing done.",
|
|
220 => "%s %s %s %s, welcome.",
|
|
221 => "%s closed connection, goodbye!",
|
|
230 => "User %s authenticated, proceed.",
|
|
240 => "Updated host %s to IP %s.",
|
|
# 330
|
|
331 => "User name OK, need password.",
|
|
# 420
|
|
421 => "%s forcibly closed connection.",
|
|
422 => "connection timed out, %s forcibly closed connection.",
|
|
# 423, 424
|
|
425 => "Can't find host %s.",
|
|
426 => "Can't find %s-record of host %s.",
|
|
500 => "Syntax error, command `%s' unrecognized.",
|
|
501 => "Syntax error in arguments.",
|
|
502 => "Command not implemented.",
|
|
503 => "Too many unrecognized commands!",
|
|
504 => "You are already authenticated!",
|
|
505 => "Server error: %s!",
|
|
506 => "Server error, update failed: %s",
|
|
530 => "Not authenticated!",
|
|
531 => "Login with USER first."
|
|
}
|
|
|
|
def initialize(server, io)
|
|
@server = server
|
|
@config = server.config
|
|
@io = io
|
|
@commit = false
|
|
|
|
# Create the domain backend.
|
|
type = @config["domain"]["type"]
|
|
klass = Backend.get_domain(type)
|
|
raise "domain backend type `#{type}' not found" if klass.nil?
|
|
@domain = klass.new(@config["domain"])
|
|
|
|
# Create the account backend.
|
|
type = @config["account"]["type"]
|
|
klass = Backend.get_account(type)
|
|
raise "account backend type `#{type}' not found" if klass.nil?
|
|
@account = klass.new(@config["account"])
|
|
rescue => msg
|
|
prot_msg 505, msg
|
|
raise
|
|
end
|
|
|
|
def handle_client
|
|
prot_msg 220, "localhost", Program, Version, Time.now.to_s
|
|
|
|
cmd_errs = 0
|
|
@io.each_line do |line|
|
|
begin
|
|
cmd, *args = line.split
|
|
handle_command(cmd, args)
|
|
rescue HandlerExit
|
|
prot_msg 221, "localhost"
|
|
break
|
|
rescue CmdUnknownError # 500
|
|
raise TooManyUnknownError if cmd_errs > MaxCmdErrs
|
|
cmd_errs = cmd_errs + 1
|
|
prot_msg 500, cmd
|
|
rescue ArgsSyntaxError # 501
|
|
raise TooManyUnknownError if cmd_errs > MaxCmdErrs
|
|
cmd_errs = cmd_errs + 1
|
|
prot_msg 501
|
|
rescue NotImplementedError # 502
|
|
prot_msg 502
|
|
rescue AlreadyAuthError # 504
|
|
prot_msg 504
|
|
rescue BackendError => msg # 506
|
|
prot_msg 506, msg
|
|
rescue NotAuthError # 530
|
|
prot_msg 530
|
|
rescue SetUserFirstError # 531
|
|
prot_msg 531
|
|
end
|
|
end
|
|
rescue TooManyUnknownError # 503
|
|
prot_msg 503
|
|
rescue RuntimeError => msg # 505
|
|
prot_msg 505, msg
|
|
raise
|
|
ensure
|
|
@domain.commit if @commit
|
|
end
|
|
|
|
#########
|
|
private
|
|
#########
|
|
|
|
def handle_command(cmd, args=[])
|
|
method_name = cmd.downcase + "_cmd"
|
|
meth = method(method_name)
|
|
meth[*args]
|
|
rescue ArgumentError
|
|
raise ArgsSyntaxError
|
|
rescue NameError => e
|
|
$stderr.puts "#{e.class}: #{e}"
|
|
$stderr.puts e.backtrace
|
|
raise CmdUnknownError
|
|
end
|
|
|
|
def prot_msg(code, *args)
|
|
msg = code.to_s + " " + (ProtocolCodeMapping[code] % args)
|
|
@io.puts msg
|
|
end
|
|
|
|
def prot_msg_with_body(code, body, *args)
|
|
@io.print code.to_s, "-", body
|
|
prot_msg(code, *args)
|
|
end
|
|
|
|
##################
|
|
# Commands methods
|
|
|
|
def user_cmd(username)
|
|
raise AlreadyAuthError if @account.authenticated?
|
|
@account.user = username
|
|
@domain.user = username
|
|
prot_msg 331
|
|
end
|
|
|
|
def pass_cmd(password)
|
|
raise SetUserFirstError unless @account.user
|
|
raise AlreadyAuthError if @account.authenticated?
|
|
@account.pass = password
|
|
|
|
if @account.authenticated?
|
|
prot_msg 230, @account.user
|
|
else
|
|
raise NotAuthError
|
|
end
|
|
end
|
|
|
|
def updt_cmd(host, addr)
|
|
raise NotAuthError unless @account.authenticated?
|
|
addr = IPAddr.new(addr)
|
|
@commit ||= @domain.update_host(host, addr)
|
|
rescue HostNotFoundError
|
|
prot_msg 425, host
|
|
rescue RecordNotFoundError
|
|
prot_msg 426, type, host
|
|
else
|
|
prot_msg 240, host, addr
|
|
end
|
|
|
|
def adda_cmd(host, new_alias)
|
|
raise NotImplementedError
|
|
end
|
|
|
|
def dela_cmd(host, old_alias)
|
|
raise NotImplementedError
|
|
end
|
|
|
|
def list_cmd
|
|
raise NotAuthError unless @account.authenticated?
|
|
user = @account.user
|
|
list = @domain.hosts.inject(Hash.new) do |memo, (host, info)|
|
|
memo[host] = {"addresses" => info.first, "aliases" => info.last}
|
|
memo
|
|
end
|
|
|
|
prot_msg_with_body 215,
|
|
"Listing of hosts (and aliases) for user #{user}\n" +
|
|
list.to_yaml + "\n"
|
|
end
|
|
|
|
def help_cmd
|
|
prot_msg_with_body 214, <<-EOT, "ildus-admin@localhost"
|
|
The following commands are recognized:
|
|
HELP\t\t\tshow this help
|
|
QUIT\t\t\tclose connection
|
|
USER <user>\t\tsupply username
|
|
PASS <passwd>\t\tgive password
|
|
LIST\t\t\tlist hostnames (and aliases)\t[auth. required]
|
|
UPDT <host> <ip>\tupdate ip for host\t\t[auth. required]
|
|
ADDA <host> <alias>\tadd alias to host\t\t[auth. required]
|
|
DELA <host> <alias>\tdelete alias from host\t\t[auth. required]
|
|
EOT
|
|
end
|
|
|
|
def quit_cmd
|
|
raise HandlerExit
|
|
end
|
|
|
|
end # class Handler
|
|
|
|
end # class Server
|
|
|
|
end # module Ildus
|