mcollective-client 1.3.3
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of mcollective-client might be problematic. Click here for more details.
- data/bin/mc-call-agent +54 -0
- data/bin/mco +27 -0
- data/lib/mcollective.rb +70 -0
- data/lib/mcollective/agents.rb +160 -0
- data/lib/mcollective/application.rb +354 -0
- data/lib/mcollective/applications.rb +145 -0
- data/lib/mcollective/client.rb +292 -0
- data/lib/mcollective/config.rb +202 -0
- data/lib/mcollective/connector.rb +18 -0
- data/lib/mcollective/connector/base.rb +24 -0
- data/lib/mcollective/facts.rb +39 -0
- data/lib/mcollective/facts/base.rb +86 -0
- data/lib/mcollective/log.rb +103 -0
- data/lib/mcollective/logger.rb +5 -0
- data/lib/mcollective/logger/base.rb +73 -0
- data/lib/mcollective/logger/console_logger.rb +61 -0
- data/lib/mcollective/logger/file_logger.rb +46 -0
- data/lib/mcollective/logger/syslog_logger.rb +53 -0
- data/lib/mcollective/matcher.rb +16 -0
- data/lib/mcollective/matcher/parser.rb +93 -0
- data/lib/mcollective/matcher/scanner.rb +123 -0
- data/lib/mcollective/message.rb +201 -0
- data/lib/mcollective/monkey_patches.rb +104 -0
- data/lib/mcollective/optionparser.rb +164 -0
- data/lib/mcollective/pluginmanager.rb +180 -0
- data/lib/mcollective/pluginpackager.rb +26 -0
- data/lib/mcollective/pluginpackager/agent_definition.rb +79 -0
- data/lib/mcollective/pluginpackager/standard_definition.rb +59 -0
- data/lib/mcollective/registration.rb +16 -0
- data/lib/mcollective/registration/base.rb +75 -0
- data/lib/mcollective/rpc.rb +188 -0
- data/lib/mcollective/rpc/actionrunner.rb +142 -0
- data/lib/mcollective/rpc/agent.rb +441 -0
- data/lib/mcollective/rpc/audit.rb +38 -0
- data/lib/mcollective/rpc/client.rb +793 -0
- data/lib/mcollective/rpc/ddl.rb +258 -0
- data/lib/mcollective/rpc/helpers.rb +339 -0
- data/lib/mcollective/rpc/progress.rb +63 -0
- data/lib/mcollective/rpc/reply.rb +61 -0
- data/lib/mcollective/rpc/request.rb +51 -0
- data/lib/mcollective/rpc/result.rb +41 -0
- data/lib/mcollective/rpc/stats.rb +185 -0
- data/lib/mcollective/runnerstats.rb +90 -0
- data/lib/mcollective/security.rb +26 -0
- data/lib/mcollective/security/base.rb +237 -0
- data/lib/mcollective/shell.rb +87 -0
- data/lib/mcollective/ssl.rb +246 -0
- data/lib/mcollective/unix_daemon.rb +37 -0
- data/lib/mcollective/util.rb +274 -0
- data/lib/mcollective/vendor.rb +41 -0
- data/lib/mcollective/vendor/require_vendored.rb +2 -0
- data/lib/mcollective/windows_daemon.rb +25 -0
- data/spec/Rakefile +16 -0
- data/spec/fixtures/application/test.rb +7 -0
- data/spec/fixtures/test-cert.pem +15 -0
- data/spec/fixtures/test-private.pem +15 -0
- data/spec/fixtures/test-public.pem +6 -0
- data/spec/monkey_patches/instance_variable_defined.rb +7 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +25 -0
- data/spec/unit/agents_spec.rb +280 -0
- data/spec/unit/application_spec.rb +636 -0
- data/spec/unit/applications_spec.rb +155 -0
- data/spec/unit/array.rb +30 -0
- data/spec/unit/config_spec.rb +148 -0
- data/spec/unit/facts/base_spec.rb +118 -0
- data/spec/unit/facts_spec.rb +39 -0
- data/spec/unit/log_spec.rb +71 -0
- data/spec/unit/logger/base_spec.rb +110 -0
- data/spec/unit/logger/syslog_logger_spec.rb +86 -0
- data/spec/unit/matcher/parser_spec.rb +106 -0
- data/spec/unit/matcher/scanner_spec.rb +71 -0
- data/spec/unit/message_spec.rb +401 -0
- data/spec/unit/optionparser_spec.rb +113 -0
- data/spec/unit/pluginmanager_spec.rb +173 -0
- data/spec/unit/pluginpackager/agent_definition_spec.rb +130 -0
- data/spec/unit/pluginpackager/standard_definition_spec.rb +75 -0
- data/spec/unit/plugins/mcollective/connector/activemq_spec.rb +533 -0
- data/spec/unit/plugins/mcollective/connector/stomp/eventlogger_spec.rb +34 -0
- data/spec/unit/plugins/mcollective/connector/stomp_spec.rb +417 -0
- data/spec/unit/plugins/mcollective/packagers/ospackage_spec.rb +229 -0
- data/spec/unit/plugins/mcollective/security/psk_spec.rb +156 -0
- data/spec/unit/registration/base_spec.rb +77 -0
- data/spec/unit/rpc/actionrunner_spec.rb +213 -0
- data/spec/unit/rpc/agent_spec.rb +155 -0
- data/spec/unit/rpc/client_spec.rb +523 -0
- data/spec/unit/rpc/ddl_spec.rb +388 -0
- data/spec/unit/rpc/helpers_spec.rb +55 -0
- data/spec/unit/rpc/reply_spec.rb +143 -0
- data/spec/unit/rpc/request_spec.rb +115 -0
- data/spec/unit/rpc/result_spec.rb +66 -0
- data/spec/unit/rpc/stats_spec.rb +288 -0
- data/spec/unit/runnerstats_spec.rb +40 -0
- data/spec/unit/security/base_spec.rb +279 -0
- data/spec/unit/shell_spec.rb +144 -0
- data/spec/unit/ssl_spec.rb +244 -0
- data/spec/unit/symbol.rb +11 -0
- data/spec/unit/unix_daemon.rb +41 -0
- data/spec/unit/util_spec.rb +342 -0
- data/spec/unit/vendor_spec.rb +34 -0
- data/spec/unit/windows_daemon.rb +43 -0
- data/spec/windows_spec.opts +1 -0
- metadata +242 -0
@@ -0,0 +1,53 @@
|
|
1
|
+
module MCollective
|
2
|
+
module Logger
|
3
|
+
# Implements a syslog based logger using the standard ruby syslog class
|
4
|
+
class Syslog_logger<Base
|
5
|
+
require 'syslog'
|
6
|
+
|
7
|
+
include Syslog::Constants
|
8
|
+
|
9
|
+
def start
|
10
|
+
config = Config.instance
|
11
|
+
|
12
|
+
facility = syslog_facility(config.logfacility)
|
13
|
+
level = config.loglevel.to_sym
|
14
|
+
|
15
|
+
Syslog.close if Syslog.opened?
|
16
|
+
Syslog.open(File.basename($0), 3, facility)
|
17
|
+
|
18
|
+
set_level(level)
|
19
|
+
end
|
20
|
+
|
21
|
+
def syslog_facility(facility)
|
22
|
+
begin
|
23
|
+
Syslog.const_get("LOG_#{facility.upcase}")
|
24
|
+
rescue NameError => e
|
25
|
+
STDERR.puts "Invalid syslog facility #{facility} supplied, reverting to USER"
|
26
|
+
Syslog::LOG_USER
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def set_logging_level(level)
|
31
|
+
# noop
|
32
|
+
end
|
33
|
+
|
34
|
+
def valid_levels
|
35
|
+
{:info => :info,
|
36
|
+
:warn => :warning,
|
37
|
+
:debug => :debug,
|
38
|
+
:fatal => :crit,
|
39
|
+
:error => :err}
|
40
|
+
end
|
41
|
+
|
42
|
+
def log(level, from, msg)
|
43
|
+
if @known_levels.index(level) >= @known_levels.index(@active_level)
|
44
|
+
Syslog.send(map_level(level), "#{from} #{msg}")
|
45
|
+
end
|
46
|
+
rescue
|
47
|
+
# if this fails we probably cant show the user output at all,
|
48
|
+
# STDERR it as last resort
|
49
|
+
STDERR.puts("#{level}: #{msg}")
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module MCollective
|
2
|
+
# A parser and scanner that creates a stack machine for a simple
|
3
|
+
# fact and class matching language used on the CLI to facilitate
|
4
|
+
# a rich discovery language
|
5
|
+
#
|
6
|
+
# Language EBNF
|
7
|
+
#
|
8
|
+
# compound = ["("] expression [")"] {["("] expression [")"]}
|
9
|
+
# expression = [!|not]statement ["and"|"or"] [!|not] statement
|
10
|
+
# char = A-Z | a-z | < | > | => | =< | _ | - |* | / { A-Z | a-z | < | > | => | =< | _ | - | * | / | }
|
11
|
+
# int = 0|1|2|3|4|5|6|7|8|9{|0|1|2|3|4|5|6|7|8|9|0}
|
12
|
+
module Matcher
|
13
|
+
autoload :Parser, "mcollective/matcher/parser"
|
14
|
+
autoload :Scanner, "mcollective/matcher/scanner"
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module MCollective
|
2
|
+
module Matcher
|
3
|
+
class Parser
|
4
|
+
attr_reader :scanner, :execution_stack
|
5
|
+
|
6
|
+
def initialize(args)
|
7
|
+
@scanner = Scanner.new(args)
|
8
|
+
@execution_stack = []
|
9
|
+
parse
|
10
|
+
end
|
11
|
+
|
12
|
+
# Parse the input string, one token at a time a contruct the call stack
|
13
|
+
def parse
|
14
|
+
p_token,p_token_value = nil
|
15
|
+
c_token,c_token_value = @scanner.get_token
|
16
|
+
parenth = 0
|
17
|
+
|
18
|
+
while (c_token != nil)
|
19
|
+
@scanner.token_index += 1
|
20
|
+
n_token, n_token_value = @scanner.get_token
|
21
|
+
|
22
|
+
unless n_token == " "
|
23
|
+
case c_token
|
24
|
+
when "and"
|
25
|
+
unless (n_token =~ /not|statement|\(/) || (scanner.token_index == scanner.arguments.size)
|
26
|
+
raise "Error at column #{scanner.token_index}. \nExpected 'not', 'statement' or '('. Found '#{n_token_value}'"
|
27
|
+
end
|
28
|
+
|
29
|
+
if p_token == nil
|
30
|
+
raise "Error at column #{scanner.token_index}. \n Expression cannot start with 'and'"
|
31
|
+
elsif (p_token == "and" || p_token == "or")
|
32
|
+
raise "Error at column #{scanner.token_index}. \n #{p_token} cannot be followed by 'and'"
|
33
|
+
end
|
34
|
+
|
35
|
+
when "or"
|
36
|
+
unless (n_token =~ /not|statement|\(/) || (scanner.token_index == scanner.arguments.size)
|
37
|
+
raise "Error at column #{scanner.token_index}. \nExpected 'not', 'statement', '('. Found '#{n_token_value}'"
|
38
|
+
end
|
39
|
+
|
40
|
+
if p_token == nil
|
41
|
+
raise "Error at column #{scanner.token_index}. \n Expression cannot start with 'or'"
|
42
|
+
elsif (p_token == "and" || p_token == "or")
|
43
|
+
raise "Error at column #{scanner.token_index}. \n #{p_token} cannot be followed by 'or'"
|
44
|
+
end
|
45
|
+
|
46
|
+
when "not"
|
47
|
+
unless n_token =~ /statement|\(|not/
|
48
|
+
raise "Error at column #{scanner.token_index}. \nExpected 'statement' or '('. Found '#{n_token_value}'"
|
49
|
+
end
|
50
|
+
|
51
|
+
when "statement"
|
52
|
+
unless n_token =~ /and|or|\)/
|
53
|
+
unless scanner.token_index == scanner.arguments.size
|
54
|
+
raise "Error at column #{scanner.token_index}. \nExpected 'and', 'or', ')'. Found '#{n_token_value}'"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
when ")"
|
59
|
+
unless (n_token =~ /|and|or|not|\(/)
|
60
|
+
unless(scanner.token_index == scanner.arguments.size)
|
61
|
+
raise "Error at column #{scanner.token_index}. \nExpected 'and', 'or', 'not' or '('. Found '#{n_token_value}'"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
parenth += 1
|
65
|
+
|
66
|
+
when "("
|
67
|
+
unless n_token =~ /statement|not|\(/
|
68
|
+
raise "Error at column #{scanner.token_index}. \nExpected 'statement', '(', not. Found '#{n_token_value}'"
|
69
|
+
end
|
70
|
+
parenth -= 1
|
71
|
+
|
72
|
+
else
|
73
|
+
raise "Unexpected token found at column #{scanner.token_index}. '#{c_token_value}'"
|
74
|
+
end
|
75
|
+
|
76
|
+
unless n_token == " "
|
77
|
+
@execution_stack << {c_token => c_token_value}
|
78
|
+
end
|
79
|
+
|
80
|
+
p_token, p_token_value = c_token, c_token_value
|
81
|
+
c_token, c_token_value = n_token, n_token_value
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
if parenth < 0
|
86
|
+
raise "Error. Missing parentheses ')'."
|
87
|
+
elsif parenth > 0
|
88
|
+
raise "Error. Missing parentheses '('."
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
module MCollective
|
2
|
+
module Matcher
|
3
|
+
class Scanner
|
4
|
+
attr_accessor :arguments, :token_index
|
5
|
+
|
6
|
+
def initialize(arguments)
|
7
|
+
@token_index = 0
|
8
|
+
@arguments = arguments
|
9
|
+
end
|
10
|
+
|
11
|
+
# Scans the input string and identifies single language tokens
|
12
|
+
def get_token
|
13
|
+
if @token_index >= @arguments.size
|
14
|
+
return nil
|
15
|
+
end
|
16
|
+
|
17
|
+
begin
|
18
|
+
case @arguments.split("")[@token_index]
|
19
|
+
when "("
|
20
|
+
return "(", "("
|
21
|
+
|
22
|
+
when ")"
|
23
|
+
return ")", ")"
|
24
|
+
|
25
|
+
when "n"
|
26
|
+
if (@arguments.split("")[@token_index + 1] == "o") && (@arguments.split("")[@token_index + 2] == "t") && ((@arguments.split("")[@token_index + 3] == " ") || (@arguments.split("")[@token_index + 3] == "("))
|
27
|
+
@token_index += 2
|
28
|
+
return "not", "not"
|
29
|
+
else
|
30
|
+
gen_statement
|
31
|
+
end
|
32
|
+
|
33
|
+
when "!"
|
34
|
+
return "not", "not"
|
35
|
+
|
36
|
+
when "a"
|
37
|
+
if (@arguments.split("")[@token_index + 1] == "n") && (@arguments.split("")[@token_index + 2] == "d") && ((@arguments.split("")[@token_index + 3] == " ") || (@arguments.split("")[@token_index + 3] == "("))
|
38
|
+
@token_index += 2
|
39
|
+
return "and", "and"
|
40
|
+
else
|
41
|
+
gen_statement
|
42
|
+
end
|
43
|
+
|
44
|
+
when "o"
|
45
|
+
if (@arguments.split("")[@token_index + 1] == "r") && ((@arguments.split("")[@token_index + 2] == " ") || (@arguments.split("")[@token_index + 2] == "("))
|
46
|
+
@token_index += 1
|
47
|
+
return "or", "or"
|
48
|
+
else
|
49
|
+
gen_statement
|
50
|
+
end
|
51
|
+
|
52
|
+
when " "
|
53
|
+
return " ", " "
|
54
|
+
|
55
|
+
else
|
56
|
+
gen_statement
|
57
|
+
end
|
58
|
+
end
|
59
|
+
rescue NoMethodError => e
|
60
|
+
pp e
|
61
|
+
raise "Cannot end statement with 'and', 'or', 'not'"
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
# Helper generates a statement token
|
66
|
+
def gen_statement
|
67
|
+
current_token_value = ""
|
68
|
+
j = @token_index
|
69
|
+
|
70
|
+
begin
|
71
|
+
if (@arguments.split("")[j] == "/")
|
72
|
+
begin
|
73
|
+
current_token_value << @arguments.split("")[j]
|
74
|
+
j += 1
|
75
|
+
if @arguments.split("")[j] == "/"
|
76
|
+
current_token_value << "/"
|
77
|
+
break
|
78
|
+
end
|
79
|
+
end until (j >= @arguments.size) || (@arguments.split("")[j] =~ /\//)
|
80
|
+
elsif (@arguments.split("")[j] =~ /=|<|>/)
|
81
|
+
while !(@arguments.split("")[j] =~ /=|<|>/)
|
82
|
+
current_token_value << @arguments.split("")[j]
|
83
|
+
j += 1
|
84
|
+
end
|
85
|
+
|
86
|
+
current_token_value << @arguments.split("")[j]
|
87
|
+
j += 1
|
88
|
+
|
89
|
+
if @arguments.split("")[j] == "/"
|
90
|
+
begin
|
91
|
+
current_token_value << @arguments.split("")[j]
|
92
|
+
j += 1
|
93
|
+
if @arguments.split("")[j] == "/"
|
94
|
+
current_token_value << "/"
|
95
|
+
break
|
96
|
+
end
|
97
|
+
end until (j >= @arguments.size) || (@arguments.split("")[j] =~ /\//)
|
98
|
+
else
|
99
|
+
while (j < @arguments.size) && ((@arguments.split("")[j] != " ") && (@arguments.split("")[j] != ")"))
|
100
|
+
current_token_value << @arguments.split("")[j]
|
101
|
+
j += 1
|
102
|
+
end
|
103
|
+
end
|
104
|
+
else
|
105
|
+
begin
|
106
|
+
current_token_value << @arguments.split("")[j]
|
107
|
+
j += 1
|
108
|
+
end until (j >= @arguments.size) || (@arguments.split("")[j] =~ /\s|\)/)
|
109
|
+
end
|
110
|
+
rescue Exception => e
|
111
|
+
raise "Invalid token found - '#{current_token_value}'"
|
112
|
+
end
|
113
|
+
|
114
|
+
if current_token_value =~ /^(and|or|not|!)$/
|
115
|
+
raise "Class name cannot be 'and', 'or', 'not'. Found '#{current_token_value}'"
|
116
|
+
end
|
117
|
+
|
118
|
+
@token_index += current_token_value.size - 1
|
119
|
+
return "statement", current_token_value
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,201 @@
|
|
1
|
+
module MCollective
|
2
|
+
# container for a message, its headers, agent, collective and other meta data
|
3
|
+
class Message
|
4
|
+
attr_reader :message, :request, :validated, :msgtime, :payload, :type, :expected_msgid, :reply_to
|
5
|
+
attr_accessor :headers, :agent, :collective, :filter
|
6
|
+
attr_accessor :requestid, :discovered_hosts, :options, :ttl
|
7
|
+
|
8
|
+
VALIDTYPES = [:message, :request, :direct_request, :reply]
|
9
|
+
|
10
|
+
# payload - the message body without headers etc, just the text
|
11
|
+
# message - the original message received from the middleware
|
12
|
+
# options[:base64] - if the body base64 encoded?
|
13
|
+
# options[:agent] - the agent the message is for/from
|
14
|
+
# options[:collective] - the collective its for/from
|
15
|
+
# options[:headers] - the message headers
|
16
|
+
# options[:type] - an indicator about the type of message, :message, :request, :direct_request or :reply
|
17
|
+
# options[:request] - if this is a reply this should old the message we are replying to
|
18
|
+
# options[:filter] - for requests, the filter to encode into the message
|
19
|
+
# options[:options] - the normal client options hash
|
20
|
+
# options[:ttl] - the maximum amount of seconds this message can be valid for
|
21
|
+
# options[:expected_msgid] - in the case of replies this is the msgid it is expecting in the replies
|
22
|
+
def initialize(payload, message, options = {})
|
23
|
+
options = {:base64 => false,
|
24
|
+
:agent => nil,
|
25
|
+
:headers => {},
|
26
|
+
:type => :message,
|
27
|
+
:request => nil,
|
28
|
+
:filter => Util.empty_filter,
|
29
|
+
:options => {},
|
30
|
+
:ttl => 60,
|
31
|
+
:expected_msgid => nil,
|
32
|
+
:collective => nil}.merge(options)
|
33
|
+
|
34
|
+
@payload = payload
|
35
|
+
@message = message
|
36
|
+
@requestid = nil
|
37
|
+
@discovered_hosts = nil
|
38
|
+
@reply_to = nil
|
39
|
+
|
40
|
+
@type = options[:type]
|
41
|
+
@headers = options[:headers]
|
42
|
+
@base64 = options[:base64]
|
43
|
+
@filter = options[:filter]
|
44
|
+
@expected_msgid = options[:expected_msgid]
|
45
|
+
@options = options[:options]
|
46
|
+
|
47
|
+
@ttl = @options[:ttl] || Config.instance.ttl
|
48
|
+
@msgtime = 0
|
49
|
+
|
50
|
+
@validated = false
|
51
|
+
|
52
|
+
if options[:request]
|
53
|
+
@request = options[:request]
|
54
|
+
@agent = request.agent
|
55
|
+
@collective = request.collective
|
56
|
+
@type = :reply
|
57
|
+
else
|
58
|
+
@agent = options[:agent]
|
59
|
+
@collective = options[:collective]
|
60
|
+
end
|
61
|
+
|
62
|
+
base64_decode!
|
63
|
+
end
|
64
|
+
|
65
|
+
# Sets the message type to one of the known types. In the case of :direct_request
|
66
|
+
# the list of hosts to communicate with should have been set with #discovered_hosts
|
67
|
+
# else an exception will be raised. This is for extra security, we never accidentally
|
68
|
+
# want to send a direct request without a list of hosts or something weird like that
|
69
|
+
# as it might result in a filterless broadcast being sent.
|
70
|
+
#
|
71
|
+
# Additionally you simply cannot set :direct_request if direct_addressing was not enabled
|
72
|
+
# this is to force a workflow that doesnt not yield in a mistake when someone might assume
|
73
|
+
# direct_addressing is enabled when its not.
|
74
|
+
def type=(type)
|
75
|
+
if type == :direct_request
|
76
|
+
raise "Direct requests is not enabled using the direct_addressing config option" unless Config.instance.direct_addressing
|
77
|
+
|
78
|
+
unless @discovered_hosts && !@discovered_hosts.empty?
|
79
|
+
raise "Can only set type to :direct_request if discovered_hosts have been set"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
raise "Unknown message type #{type}" unless VALIDTYPES.include?(type)
|
84
|
+
|
85
|
+
@type = type
|
86
|
+
end
|
87
|
+
|
88
|
+
# Sets a custom reply-to target for requests. The connector plugin should inspect this
|
89
|
+
# when constructing requests and set this header ensuring replies will go to the custom target
|
90
|
+
# otherwise the connector should just do what it usually does
|
91
|
+
def reply_to=(target)
|
92
|
+
raise "Custom reply targets can only be set on requests" unless [:request, :direct_request].include?(@type)
|
93
|
+
|
94
|
+
@reply_to = target
|
95
|
+
end
|
96
|
+
|
97
|
+
# in the case of reply messages we are expecting replies to a previously
|
98
|
+
# created message. This stores a hint to that previously sent message id
|
99
|
+
# and can be used by other classes like the security plugins as a means
|
100
|
+
# of optimizing their behavior like by ignoring messages not directed
|
101
|
+
# at us.
|
102
|
+
def expected_msgid=(msgid)
|
103
|
+
raise "Can only store the expected msgid for reply messages" unless @type == :reply
|
104
|
+
@expected_msgid = msgid
|
105
|
+
end
|
106
|
+
|
107
|
+
def base64_decode!
|
108
|
+
return unless @base64
|
109
|
+
|
110
|
+
@payload = SSL.base64_decode(@payload)
|
111
|
+
@base64 = false
|
112
|
+
end
|
113
|
+
|
114
|
+
def base64_encode!
|
115
|
+
return if @base64
|
116
|
+
|
117
|
+
@payload = SSL.base64_encode(@payload)
|
118
|
+
@base64 = true
|
119
|
+
end
|
120
|
+
|
121
|
+
def base64?
|
122
|
+
@base64
|
123
|
+
end
|
124
|
+
|
125
|
+
def encode!
|
126
|
+
case type
|
127
|
+
when :reply
|
128
|
+
raise "Cannot encode a reply message if no request has been associated with it" unless request
|
129
|
+
raise 'callerid in original request is not valid, surpressing reply to potentially forged request' unless PluginManager["security_plugin"].valid_callerid?(request.payload[:callerid])
|
130
|
+
|
131
|
+
@requestid = request.payload[:requestid]
|
132
|
+
@payload = PluginManager["security_plugin"].encodereply(agent, payload, requestid, request.payload[:callerid])
|
133
|
+
when :request, :direct_request
|
134
|
+
@requestid = create_reqid
|
135
|
+
@payload = PluginManager["security_plugin"].encoderequest(Config.instance.identity, payload, requestid, filter, agent, collective, ttl)
|
136
|
+
else
|
137
|
+
raise "Cannot encode #{type} messages"
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def decode!
|
142
|
+
raise "Cannot decode message type #{type}" unless [:request, :reply].include?(type)
|
143
|
+
|
144
|
+
@payload = PluginManager["security_plugin"].decodemsg(self)
|
145
|
+
|
146
|
+
if type == :request
|
147
|
+
raise 'callerid in request is not valid, surpressing reply to potentially forged request' unless PluginManager["security_plugin"].valid_callerid?(payload[:callerid])
|
148
|
+
end
|
149
|
+
|
150
|
+
[:collective, :agent, :filter, :requestid, :ttl, :msgtime].each do |prop|
|
151
|
+
instance_variable_set("@#{prop}", payload[prop]) if payload.include?(prop)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
# Perform validation against the message by checking filters and ttl
|
156
|
+
def validate
|
157
|
+
raise "Can only validate request messages" unless type == :request
|
158
|
+
|
159
|
+
msg_age = Time.now.utc.to_i - msgtime
|
160
|
+
|
161
|
+
if msg_age > ttl
|
162
|
+
cid = ""
|
163
|
+
cid += payload[:callerid] + "@" if payload.include?(:callerid)
|
164
|
+
cid += payload[:senderid]
|
165
|
+
|
166
|
+
if msg_age > ttl
|
167
|
+
PluginManager["global_stats"].ttlexpired
|
168
|
+
|
169
|
+
raise(MsgTTLExpired, "Message #{requestid} from #{cid} created at #{msgtime} is #{msg_age} seconds old, TTL is #{ttl}")
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
raise(NotTargettedAtUs, "Received message is not targetted to us") unless PluginManager["security_plugin"].validate_filter?(payload[:filter])
|
174
|
+
|
175
|
+
@validated = true
|
176
|
+
end
|
177
|
+
|
178
|
+
# publish a reply message by creating a target name and sending it
|
179
|
+
def publish
|
180
|
+
Timeout.timeout(2) do
|
181
|
+
# If we've been specificaly told about hosts that were discovered
|
182
|
+
# use that information to do P2P calls if appropriate else just
|
183
|
+
# send it as is.
|
184
|
+
if @discovered_hosts && Config.instance.direct_addressing
|
185
|
+
if @discovered_hosts.size <= Config.instance.direct_addressing_threshold
|
186
|
+
@type = :direct_request
|
187
|
+
Log.debug("Handling #{requestid} as a direct request")
|
188
|
+
end
|
189
|
+
|
190
|
+
PluginManager["connector_plugin"].publish(self)
|
191
|
+
else
|
192
|
+
PluginManager["connector_plugin"].publish(self)
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
def create_reqid
|
198
|
+
Digest::MD5.hexdigest("#{Config.instance.identity}-#{Time.now.to_f}-#{agent}-#{collective}")
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|