216 lines
6.4 KiB
Ruby
216 lines
6.4 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 '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
|
|
@io = io
|
|
type = server.config["backend"]["type"]
|
|
klass = Backend[server.config["backend"]["type"]]
|
|
raise "backend type `#{type}' not found" if klass.nil?
|
|
@backend = klass.new(server.config["backend"])
|
|
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
|
|
end
|
|
|
|
#########
|
|
private
|
|
#########
|
|
|
|
def handle_command(cmd, args=[])
|
|
method_name = cmd.downcase + "_cmd"
|
|
meth = method(method_name)
|
|
meth.call(*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)
|
|
@backend.user = username
|
|
prot_msg 331
|
|
end
|
|
|
|
def pass_cmd(password)
|
|
raise SetUserFirstError unless @backend.user
|
|
|
|
@backend.pass = password
|
|
if @backend.authenticated?
|
|
prot_msg 230, @backend.user
|
|
else
|
|
raise NotAuthError
|
|
end
|
|
end
|
|
|
|
def updt_cmd(hostname, addr)
|
|
raise NotAuthError unless @backend.authenticated?
|
|
@backend.update_hostname(@backend.user, hostname, addr)
|
|
rescue HostNotFoundError
|
|
prot_msg 425, hostname
|
|
rescue RecordNotFoundError
|
|
prot_msg 426, type, hostname
|
|
else
|
|
prot_msg 240, hostname, addr
|
|
end
|
|
|
|
def adda_cmd(hostname, new_alias)
|
|
raise NotImplementedError
|
|
end
|
|
|
|
def dela_cmd(hostname, old_alias)
|
|
raise NotImplementedError
|
|
end
|
|
|
|
def list_cmd
|
|
raise NotAuthError unless @backend.authenticated?
|
|
user = @backend.user
|
|
list = @backend.hostnames
|
|
h = list.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" +
|
|
h.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
|