class Arachni::RPC::Server::Dispatcher::Node
Dispatcher node class, helps maintain a list of all available Dispatchers in the grid and announce itself to neighbouring Dispatchers.
As soon as a new Node
is fired up it checks-in with its neighbour and grabs a list of all available peers.
As soon as it receives the peer list it then announces itself to them.
Upon convergence there will be a grid of Dispatchers each one with its own copy of all available Dispatcher URLs.
@author Tasos “Zapotek” Laskos <tasos.laskos@arachni-scanner.com>
Public Class Methods
Initializes the node by:
* Adding the neighbour (if the user has supplied one) to the peer list. * Getting the neighbour's peer list and appending them to its own. * Announces itself to the neighbour and instructs it to propagate our URL to the others.
@param [Arachni::Options] options @param [String] logfile
Where to send the output.
# File lib/arachni/rpc/server/dispatcher/node.rb, line 40 def initialize( options, logfile = nil ) @options = options @options.dispatcher.external_address ||= @options.rpc.server_address @url = "#{@options.dispatcher.external_address}:#{@options.rpc.server_port}" reroute_to_file( logfile ) if logfile print_status 'Initializing grid node...' @dead_nodes = [] @neighbours = Set.new @nodes_info_cache = [] if (neighbour = @options.dispatcher.neighbour) # Grab the neighbour's neighbours. connect_to_peer( neighbour ).neighbours do |urls| if urls.rpc_exception? add_dead_neighbour( neighbour ) print_info "Neighbour seems dead: #{neighbour}" add_dead_neighbour( neighbour ) next end # Add neighbour and announce it to everyone. add_neighbour( neighbour, true ) urls.each { |url| @neighbours << url if url != @url } end end print_status( 'Node ready.' ) log_updated_neighbours Reactor.global.at_interval( @options.dispatcher.node_ping_interval ) do ping check_for_comebacks end end
Public Instance Methods
Adds a neighbour to the peer list.
@param [String] node_url
URL of a neighbouring node.
@param [Boolean] propagate
Whether or not to announce the new node to the peers.
# File lib/arachni/rpc/server/dispatcher/node.rb, line 93 def add_neighbour( node_url, propagate = false ) # we don't want ourselves in the Set return false if node_url == @url return false if @neighbours.include?( node_url ) print_status "Adding neighbour: #{node_url}" @neighbours << node_url log_updated_neighbours announce( node_url ) if propagate connect_to_peer( node_url ).add_neighbour( @url, propagate ) do |res| next if !res.rpc_exception? add_dead_neighbour( node_url ) print_status "Neighbour seems dead: #{node_url}" end true end
# File lib/arachni/rpc/server/dispatcher/node.rb, line 168 def alive? true end
@return [Boolean]
`true` if grid member, `false` otherwise.
# File lib/arachni/rpc/server/dispatcher/node.rb, line 83 def grid_member? @neighbours.any? end
@return [Hash]
* `url` -- This node's URL. * `pipe_id` -- Bandwidth Pipe ID * `weight` -- Weight * `nickname` -- Nickname * `cost` -- Cost
# File lib/arachni/rpc/server/dispatcher/node.rb, line 158 def info { 'url' => @url, 'pipe_id' => @options.dispatcher.node_pipe_id, 'weight' => @options.dispatcher.node_weight, 'nickname' => @options.dispatcher.node_nickname, 'cost' => @options.dispatcher.node_cost } end
@return [Array]
Neighbour/node/peer URLs.
# File lib/arachni/rpc/server/dispatcher/node.rb, line 114 def neighbours @neighbours.to_a end
# File lib/arachni/rpc/server/dispatcher/node.rb, line 118 def neighbours_with_info( &block ) fail 'This method requires a block!' if !block_given? @neighbours_cmp = '' if @nodes_info_cache.empty? || @neighbours_cmp != neighbours.to_s @neighbours_cmp = neighbours.to_s each = proc do |neighbour, iter| connect_to_peer( neighbour ).info do |info| if info.rpc_exception? print_info "Neighbour seems dead: #{neighbour}" add_dead_neighbour( neighbour ) log_updated_neighbours iter.return( nil ) else iter.return( info ) end end end after = proc do |nodes| @nodes_info_cache = nodes.compact block.call( @nodes_info_cache ) end Reactor.global.create_iterator( neighbours ).map( each, after ) else block.call( @nodes_info_cache ) end end
Private Instance Methods
# File lib/arachni/rpc/server/dispatcher/node.rb, line 178 def add_dead_neighbour( url ) remove_neighbour( url ) @dead_nodes << url end
Announces the node to the ones in the peer list
@param [String] node
URL
# File lib/arachni/rpc/server/dispatcher/node.rb, line 224 def announce( node ) print_status "Advertising: #{node}" neighbours.each do |peer| next if peer == node print_info '---- to: ' + peer connect_to_peer( peer ).add_neighbour( node ) do |res| add_dead_neighbour( peer ) if res.rpc_exception? end end end
# File lib/arachni/rpc/server/dispatcher/node.rb, line 203 def check_for_comebacks @dead_nodes.dup.each do |url| neighbour = connect_to_peer( url ) neighbour.alive? do |res| next if res.rpc_exception? print_status "Dispatcher came back to life: #{url}" ([@url] | neighbours).each do |node| neighbour.add_neighbour( node ){} end add_neighbour( url ) @dead_nodes -= [url] end end end
# File lib/arachni/rpc/server/dispatcher/node.rb, line 237 def connect_to_peer( url ) @rpc_clients ||= {} @rpc_clients[url] ||= Client::Dispatcher.new( @options, url ).node end
# File lib/arachni/rpc/server/dispatcher/node.rb, line 183 def log_updated_neighbours print_info 'Updated neighbours:' if !neighbours.empty? neighbours.each { |node| print_info( '---- ' + node ) } else print_info '<empty>' end end
# File lib/arachni/rpc/server/dispatcher/node.rb, line 193 def ping neighbours.each do |neighbour| connect_to_peer( neighbour ).alive? do |res| next if !res.rpc_exception? add_dead_neighbour( neighbour ) print_status "Found dead neighbour: #{neighbour} " end end end
# File lib/arachni/rpc/server/dispatcher/node.rb, line 174 def remove_neighbour( node_url ) @neighbours -= [node_url] end