This repository has been archived on 2020-04-11. You can view files and clone it, but cannot push or open issues or pull requests.
ildus/lib/ildus/server/handler.rb

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