class Wmap::NetworkProfiler

Network profiler to optimize the port scanner performance for a specific network / IP. The ultimate goal is to set a reasonable socket time-out parameter for the scanners.

Constants

File_discovery_ports

Attributes

latency[R]
max_parallel[RW]
search_path[RW]
socket_timeout[RW]
verbose[RW]

Public Class Methods

new(params = {}) click to toggle source

Set default instance variables

# File lib/wmap/network_profiler.rb, line 20
def initialize (params = {})           
        @verbose=params.fetch(:verbose, false)
        @socket_timeout=params.fetch(:socket_timeout, 1500)
        #@http_timeout=params.fetch(:http_timeout, 3000)
        @search_path=["/sbin/","/usr/sbin/","/usr/local/bin/","/usr/bin/","/opt/bin/","/opt/sbin/"]
        # Initialize the instance variables
        @discovery_tcp_ports=params.fetch(:discovery_tcp_ports, file_2_list(File_discovery_ports).map!{|x| x.to_i} )
end

Public Instance Methods

open_tcp_port?(target) click to toggle source

Perform TCP Ping as a last resort of the network profiling effort, in case of ICMP tests fail.

# File lib/wmap/network_profiler.rb, line 119
def open_tcp_port? (target)
        puts "Check if any TCP port in the list #{@discovery_tcp_ports} is open on the remote host: #{target}" if @verbose
        begin
                timeo = @socket_timeout/1000.0                                               # change time-out unit from sec to ms
                p=Net::Ping::TCP.new(target,nil,timeo) 
                @discovery_tcp_ports.map do |port|
                        p.port=port
                        if p.ping
                                @which_port=port   
                                # Bug in the current Net::Ping.ping module, where the 'duration' is 100 order off. We make it up here without fixing their code
                                @latency = p.duration * 1000 * 100
                                puts "TCP port detection successful on port: #{@which_port}" if @verbose
                                return true
                        end
                end
                puts "TCP port detection on remote host #{target} fail. " if @verbose
                return false
        rescue Exception => ee
                puts "Error on method #{__method__} on target #{target}: #{ee}" if @verbose
                return false
        end
end
profile(host) click to toggle source

Main worker method that determine the right profiling methods

# File lib/wmap/network_profiler.rb, line 30
def profile(host)      
        puts "Perform web service discovery on host: #{host}" if @verbose
        @latency = @socket_timeout
        begin
                if Process.euid == 0 && socket_icmp_pingable?(host)
                        puts "Network profiling by using raw socket ..." if @verbose
                elsif shell_ping_exist? && shell_pingable?(host)
                        puts "Network profiling by using external shell ping program ..." if @verbose
                elsif open_tcp_port?(host)
                        puts "Network profiling by using TCP ping ..." if @verbose
                else
                        puts "No appropriate profiling method for #{host}" if @verbose
                        # Do nothing
                end
                puts "Found network latency for #{host}: #{@latency} ms" if @verbose
                return @latency
        rescue Exception => ee
                puts "Exception on method #{__method__} for #{host}: #{ee}" if @verbose
                return nil
        end
end
shell_ping_exist?() click to toggle source

Search for local ping executable program. This is helpful for the normal users who has no direct access to generate socket ICMP packets.

# File lib/wmap/network_profiler.rb, line 100
def shell_ping_exist? 
        begin
                puts "Search local shell environment for the ping program ..." if @verbose
                @search_path.map do |path|
                        ping_exe=path+"ping"
                        if File.exist?(ping_exe) && File.executable?(ping_exe)
                                @which_ping=ping_exe
                                puts "Local ping program found: #{ping_exe}" if @verbose
                                return true
                        end
                end
                return false
        rescue Exception => ee
                puts "Exception on method #{__method__}: #{ee}" if @verbose
                return false
        end
end
shell_pingable?(target) click to toggle source

Wrapper for local ping executable. This is needed if the process do not have the root privilege to operate

on the raw ICMP socket
# File lib/wmap/network_profiler.rb, line 76
def shell_pingable? (target)           
        puts "Perform ping test from the shell on: #{target}" if @verbose
        begin
    sum=0
                test_ping= `#{@which_ping} -c 3 #{target}`
                test_ping.scan(/^(.+?)\stime=(.+)\s(.+?)\n/).map do |entry|
                        puts "entry: #{entry}" if @verbose
                        sum=sum+entry[1].to_f
                end
                if sum > 0
                        @latency = sum / 3
                        puts "Ping test from the shell environment successful on #{target}." if @verbose
                        return true
                else
                        puts "Ping test from the shell environment fail on #{target}." if @verbose
                        return false
                end
        rescue Exception => ee
                puts "Exception on method #{__method__} for #{host}: #{ee}" if @verbose
                return false
        end
end
socket_icmp_pingable?(target) click to toggle source

Perform raw socket ICMP echo detection on the host. Note that socket ICMP packet manipulation

need the root privilege access(for example, ICMP 'echo' need to snoop on the interface to detect any replies such as 'ECONNREFUSED'). 
That's why we also use external ping program for the normal users in case they do not has the access.
# File lib/wmap/network_profiler.rb, line 55
def socket_icmp_pingable? (target)
        puts "Perform socket ICMP ping on the target: #{target}" if @verbose
        begin
                timeo = @socket_timeout/1000.0                                               # change time-out unit from sec to ms
                p=Net::Ping::ICMP.new(target,nil,timeo) 
                if p.ping
                        @latency=p.duration * 1000
                        puts "Socket ICMP echo test successful on #{target}." if @verbose
                        return true
                else
                        puts "Socket ICMP echo test fail on #{target}." if @verbose
                        return false
                end
        rescue Exception => ee
                puts "Error on method #{__method__} on target #{target}: #{ee}" if @verbose
                return false
        end
end