root/lib/cyborghood/cyborg/botnet/protocol.rb @ 2eaae89a
7e9520c0 | Marc Dequènes (Duck) | #--
|
|
# CyborgHood, a distributed system management software.
|
|||
364e4a96 | Marc Dequènes (Duck) | # Copyright (c) 2009-2011 Marc Dequènes (Duck) <Duck@DuckCorp.org>
|
|
7e9520c0 | Marc Dequènes (Duck) | #
|
|
# This program 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 3 of the License, or
|
|||
# (at your option) any later version.
|
|||
#
|
|||
# This program is distributed in the hope that it will be useful,
|
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|||
# GNU General Public License for more details.
|
|||
#
|
|||
# You should have received a copy of the GNU General Public License
|
|||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|||
#++
|
|||
# Note: Event machine core is mono-thread, so this code is not threadsafe.
|
|||
# Only long tasks will be run in threads, which do not care about all this.
|
|||
module CyborgHood
|
|||
45333094 | Marc Dequènes (Duck) | class BotProtocol
|
|
7e9520c0 | Marc Dequènes (Duck) | VERSION = "0.1"
|
|
CAPABILITIES = []
|
|||
21a7eaf9 | Marc Dequènes (Duck) | @@request_callback = proc do |result|
|
|
protocol = result[:reply_message].conv_thread.conversation.protocol
|
|||
protocol.process_request_result(result)
|
|||
end
|
|||
7e9520c0 | Marc Dequènes (Duck) | ||
def initialize(conversation)
|
|||
@conversation = conversation
|
|||
@negociation_received = false
|
|||
@negociation_sent = false
|
|||
@negociation_ok = false
|
|||
end
|
|||
def negociation_ok?
|
|||
@negociation_ok
|
|||
end
|
|||
def process_received_message(message)
|
|||
method = "receive_" + message.action_code.downcase.tr(" ", "_")
|
|||
if respond_to? method
|
|||
send(method, message)
|
|||
else
|
|||
send_error_protocol "unknown action"
|
|||
end
|
|||
end
|
|||
21a7eaf9 | Marc Dequènes (Duck) | def process_request_result(result)
|
|
feb16bd0 | Marc Dequènes (Duck) | # TODO: better processing of result (info/warnings/…)
|
|
2eaae89a | Marc Dequènes (Duck) | if result[:errors]
|
|
feb16bd0 | Marc Dequènes (Duck) | send_error_action(result[:reply_message], result[:errors])
|
|
21a7eaf9 | Marc Dequènes (Duck) | else
|
|
feb16bd0 | Marc Dequènes (Duck) | send_reply_result(result[:reply_message], result[:results])
|
|
21a7eaf9 | Marc Dequènes (Duck) | end
|
|
end
|
|||
7e9520c0 | Marc Dequènes (Duck) | def receive_announce_helo(message)
|
|
unless message.conv_thread.id == 0
|
|||
return send_quit_decline "bad negociation"
|
|||
end
|
|||
45333094 | Marc Dequènes (Duck) | if message.action_parameters.nil?
|
|
return send_quit_decline "missing parameters"
|
|||
end
|
|||
unless message.action_parameters[:bot_name] =~ Regexp.new(Conversation::BOT_NAME_PATTERN)
|
|||
7e9520c0 | Marc Dequènes (Duck) | return send_quit_decline "bad bot name"
|
|
end
|
|||
45333094 | Marc Dequènes (Duck) | unless message.action_parameters[:protocol_version] == VERSION
|
|
7e9520c0 | Marc Dequènes (Duck) | return send_quit_decline "protocol version does not match"
|
|
end
|
|||
@negociation_received = true
|
|||
45333094 | Marc Dequènes (Duck) | @conversation.set_peer_info(message.action_parameters[:bot_name], message.action_parameters[:capabilities])
|
|
7e9520c0 | Marc Dequènes (Duck) | ||
if @negociation_sent
|
|||
send_announce_ok(message)
|
|||
@negociation_ok = true
|
|||
d57fd602 | Marc Dequènes (Duck) | @conversation.set_comm_ready
|
|
7e9520c0 | Marc Dequènes (Duck) | else
|
|
send_announce_helo(message)
|
|||
end
|
|||
end
|
|||
def receive_announce_ok(message)
|
|||
unless @negociation_sent and @negociation_received
|
|||
send_quit_decline "bad negociation"
|
|||
end
|
|||
@negociation_ok = true
|
|||
end
|
|||
def receive_request_capabilities(message)
|
|||
bc642412 | Marc Dequènes (Duck) | send_reply_result(message, @conversation.bot.capabilities + CAPABILITIES)
|
|
7e9520c0 | Marc Dequènes (Duck) | end
|
|
def receive_request_call(message)
|
|||
45333094 | Marc Dequènes (Duck) | if message.action_parameters.nil?
|
|
9dfb2047 | Marc Dequènes (Duck) | return send_error_action(message, {
|
|
:category => 'API',
|
|||
:severity => :unrecoverable,
|
|||
:message => "missing parameters"
|
|||
})
|
|||
7e9520c0 | Marc Dequènes (Duck) | end
|
|
feb16bd0 | Marc Dequènes (Duck) | ||
7e9520c0 | Marc Dequènes (Duck) | send_reply_ack(message)
|
|
feb16bd0 | Marc Dequènes (Duck) | ||
process_result_cb = Proc.new do |reply|
|
|||
reply[:reply_message] = message
|
|||
@@request_callback.call reply
|
|||
end
|
|||
@conversation.bot.schedule_task do
|
|||
a8442036 | Marc Dequènes (Duck) | begin
|
|
feb16bd0 | Marc Dequènes (Duck) | @conversation.bot.interface._call(message.conv_thread.session,
|
|
a8442036 | Marc Dequènes (Duck) | message.action_parameters[:node],
|
|
feb16bd0 | Marc Dequènes (Duck) | message.action_parameters[:parameters],
|
|
&process_result_cb)
|
|||
a8442036 | Marc Dequènes (Duck) | rescue CyberError => e
|
|
feb16bd0 | Marc Dequènes (Duck) | process_result_cb.call(:errors => [{
|
|
a8442036 | Marc Dequènes (Duck) | :category => e.category,
|
|
:severity => e.severity,
|
|||
:message => e.message
|
|||
feb16bd0 | Marc Dequènes (Duck) | }])
|
|
a8442036 | Marc Dequènes (Duck) | rescue
|
|
feb16bd0 | Marc Dequènes (Duck) | process_result_cb.call(:errors => [{
|
|
a8442036 | Marc Dequènes (Duck) | :category => 'unknown',
|
|
be27f17e | Marc Dequènes (Duck) | :severity => :unrecoverable,
|
|
a8442036 | Marc Dequènes (Duck) | :message => $!.to_s
|
|
feb16bd0 | Marc Dequènes (Duck) | }])
|
|
7e9520c0 | Marc Dequènes (Duck) | end
|
|
end
|
|||
end
|
|||
def receive_request_exists(message)
|
|||
45333094 | Marc Dequènes (Duck) | if message.action_parameters.nil?
|
|
9dfb2047 | Marc Dequènes (Duck) | return send_error_action(message, {
|
|
:category => 'API',
|
|||
:severity => :unrecoverable,
|
|||
:message => "missing parameters"
|
|||
})
|
|||
45333094 | Marc Dequènes (Duck) | end
|
|
7e9520c0 | Marc Dequènes (Duck) | send_reply_ack(message)
|
|
@conversation.bot.schedule_task(@@request_callback) do
|
|||
{
|
|||
:reply_message => message,
|
|||
feb16bd0 | Marc Dequènes (Duck) | :results => @conversation.bot.interface._is_node?(message.conv_thread.session, message.action_parameters[:node])
|
|
7e9520c0 | Marc Dequènes (Duck) | }
|
|
end
|
|||
end
|
|||
def receive_request_describe(message)
|
|||
# TODO: implement when ready in the interface
|
|||
21a7eaf9 | Marc Dequènes (Duck) | send_reply_decline(message, "not implemented")
|
|
end
|
|||
def receive_error_protocol(message)
|
|||
logger.error "received protocol error notification from '#{@conversation.peer_name}': #{message.action_parameters[:error]}"
|
|||
end
|
|||
def receive_error_action(message)
|
|||
fd4ccbdd | Marc Dequènes (Duck) | message.pop_callback do |cb|
|
|
if cb
|
|||
error = message.action_parameters[:error]
|
|||
exception = CyberError.new(error[:severity], error[:category], error[:message])
|
|||
cb.call({:status => :error, :exception => exception})
|
|||
else
|
|||
send_error_protocol("received reply for unknown action")
|
|||
end
|
|||
21a7eaf9 | Marc Dequènes (Duck) | end
|
|
end
|
|||
2cfb7e86 | Marc Dequènes (Duck) | # TODO: what if the peer close a thread i have opened ?
|
|
4afb2001 | Marc Dequènes (Duck) | # send error to all actions ?
|
|
def receive_notify_thread_closed(message)
|
|||
message.conv_thread.close(false)
|
|||
end
|
|||
def receive_notify_event(message)
|
|||
06f77931 | Marc Dequènes (Duck) | @conversation.bot.get_channel("peer/#{@conversation.peer_name}/incoming") << {
|
|
2cfb7e86 | Marc Dequènes (Duck) | :from => message.conversation.peer_id,
|
|
:topic => message.action_parameters[:name],
|
|||
:info => message.action_parameters[:info]
|
|||
}
|
|||
4afb2001 | Marc Dequènes (Duck) | end
|
|
21a7eaf9 | Marc Dequènes (Duck) | def receive_reply_ack(message)
|
|
2cfb7e86 | Marc Dequènes (Duck) | # TODO: cancel timeout (which does not exist yet)
|
|
21a7eaf9 | Marc Dequènes (Duck) | end
|
|
def receive_reply_decline(message)
|
|||
fd4ccbdd | Marc Dequènes (Duck) | message.pop_callback do |cb|
|
|
if cb
|
|||
cb.call({:status => :decline, :reason => message.action_parameters[:reason]})
|
|||
else
|
|||
send_error_protocol("received reply for unknown action")
|
|||
end
|
|||
21a7eaf9 | Marc Dequènes (Duck) | end
|
|
end
|
|||
def receive_reply_result(message)
|
|||
fd4ccbdd | Marc Dequènes (Duck) | message.pop_callback do |cb|
|
|
if cb
|
|||
cb.call({:status => :ok, :result => message.action_parameters[:result]})
|
|||
else
|
|||
send_error_protocol("received reply for unknown action")
|
|||
end
|
|||
21a7eaf9 | Marc Dequènes (Duck) | end
|
|
7e9520c0 | Marc Dequènes (Duck) | end
|
|
3152d6bd | Marc Dequènes (Duck) | def receive_quit_decline(message)
|
|
23c30bd5 | Marc Dequènes (Duck) | logger.warn "peer '#{@conversation.peer_name}' refused more conversation: #{message.action_parameters[:reason]}"
|
|
3152d6bd | Marc Dequènes (Duck) | @conversation.set_comm_stop(true)
|
|
# TODO: notify client
|
|||
end
|
|||
def receive_quit_leaving(message)
|
|||
21a7eaf9 | Marc Dequènes (Duck) | logger.info "peer '#{@conversation.peer_name}' is leaving"
|
|
3152d6bd | Marc Dequènes (Duck) | @conversation.set_comm_stop(true)
|
|
# TODO: notify client
|
|||
end
|
|||
7e9520c0 | Marc Dequènes (Duck) | def send_announce_helo(recv_message = nil)
|
|
action_code = "ANNOUNCE HELO"
|
|||
45333094 | Marc Dequènes (Duck) | action_parameters = {
|
|
:bot_name => @conversation.bot.name,
|
|||
:protocol_version => VERSION
|
|||
}
|
|||
message = (recv_message.nil? ? @conversation.thread('system').new_message(action_code, action_parameters) :
|
|||
recv_message.create_reply(action_code, action_parameters))
|
|||
7e9520c0 | Marc Dequènes (Duck) | message.send
|
|
45333094 | Marc Dequènes (Duck) | @negociation_sent = true
|
|
7e9520c0 | Marc Dequènes (Duck) | end
|
|
def send_announce_ok(recv_message)
|
|||
recv_message.create_reply("ANNOUNCE OK").send
|
|||
end
|
|||
def send_request_capabilities
|
|||
@conversation.thread('system').new_message("REQUEST EXISTS", { :node => node }).send
|
|||
end
|
|||
706c6cbd | Marc Dequènes (Duck) | def send_request_call(conv_thread, node, *parameters, &callback)
|
|
message = conv_thread.new_message("REQUEST CALL", { :node => node, :parameters => parameters }).send
|
|||
fd4ccbdd | Marc Dequènes (Duck) | message.register_callback(callback)
|
|
7e9520c0 | Marc Dequènes (Duck) | end
|
|
14dd8318 | Marc Dequènes (Duck) | def send_request_exists(conv_thread, node, &callback)
|
|
21a7eaf9 | Marc Dequènes (Duck) | message = conv_thread.new_message("REQUEST EXISTS", { :node => node }).send
|
|
fd4ccbdd | Marc Dequènes (Duck) | message.register_callback(callback)
|
|
7e9520c0 | Marc Dequènes (Duck) | end
|
|
def send_request_describe(conv_thread, node)
|
|||
21a7eaf9 | Marc Dequènes (Duck) | message = conv_thread.new_message("REQUEST DESCRIBE", { :node => node }).send
|
|
fd4ccbdd | Marc Dequènes (Duck) | message.register_callback(callback)
|
|
7e9520c0 | Marc Dequènes (Duck) | end
|
|
def send_error_protocol(error, fatal = false)
|
|||
@conversation.thread('system').new_message("ERROR PROTOCOL", { :error => error }).send
|
|||
@conversation.set_error_status(fatal)
|
|||
end
|
|||
def send_error_action(recv_message, error)
|
|||
recv_message.create_reply("ERROR ACTION", { :error => error }).send
|
|||
end
|
|||
4afb2001 | Marc Dequènes (Duck) | def send_notify_thread_closed(conv_thread)
|
|
conv_thread.new_message("NOTIFY THREAD CLOSED").send
|
|||
end
|
|||
def send_notify_event(conv_thread, event_name, event_info)
|
|||
conv_thread.new_message.create_reply("NOTIFY EVENT", { :name => event_name, :info => event_info }).send
|
|||
end
|
|||
7e9520c0 | Marc Dequènes (Duck) | def send_reply_ack(recv_message)
|
|
recv_message.create_reply("REPLY ACK").send
|
|||
end
|
|||
def send_reply_decline(recv_message, reason)
|
|||
recv_message.create_reply("REPLY DECLINE", { :reason => reason }).send
|
|||
end
|
|||
def send_reply_result(recv_message, result)
|
|||
21a7eaf9 | Marc Dequènes (Duck) | recv_message.create_reply("REPLY RESULT", { :result => result }).send
|
|
7e9520c0 | Marc Dequènes (Duck) | end
|
|
def send_quit_decline(reason)
|
|||
45333094 | Marc Dequènes (Duck) | @conversation.set_comm_stop do
|
|
@conversation.thread('system').new_message("QUIT DECLINE", { :reason => reason }).send
|
|||
end
|
|||
7e9520c0 | Marc Dequènes (Duck) | end
|
|
def send_quit_leaving
|
|||
45333094 | Marc Dequènes (Duck) | @conversation.set_comm_stop do
|
|
@conversation.thread('system').new_message("QUIT LEAVING").send
|
|||
end
|
|||
7e9520c0 | Marc Dequènes (Duck) | end
|
|
end
|
|||
end
|