Class: Wmap::NetworkProfiler

Inherits:
Object
  • Object
show all
Includes:
Utils
Defined in:
lib/wmap/network_profiler.rb

Overview

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.

Constant Summary collapse

File_discovery_ports =
File.dirname(__FILE__)+'/../../settings/discovery_ports'

Constants included from Utils::UrlMagic

Utils::UrlMagic::Max_http_timeout, Utils::UrlMagic::User_agent

Constants included from Utils::DomainRoot

Utils::DomainRoot::File_ccsld, Utils::DomainRoot::File_cctld, Utils::DomainRoot::File_gtld, Utils::DomainRoot::File_tld

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Utils

#cidr_2_ips, #file_2_hash, #file_2_list, #get_nameserver, #get_nameservers, #host_2_ip, #host_2_ips, #is_cidr?, #is_fqdn?, #is_ip?, #list_2_file, #reverse_dns_lookup, #sort_ips, #valid_dns_record?, #zone_transferable?

Methods included from Utils::Logger

#wlog

Methods included from Utils::UrlMagic

#create_absolute_url_from_base, #create_absolute_url_from_context, #host_2_url, #is_site?, #is_ssl?, #is_url?, #landing_location, #make_absolute, #normalize_url, #open_page, #redirect_location, #response_code, #response_headers, #url_2_host, #url_2_path, #url_2_port, #url_2_site, #urls_on_same_domain?

Methods included from Utils::DomainRoot

#get_domain_root, #get_domain_root_by_ccsld, #get_domain_root_by_cctld, #get_domain_root_by_tlds, #get_sub_domain, #is_domain_root?, #print_ccsld, #print_cctld, #print_gtld

Constructor Details

#initialize(params = {}) ⇒ NetworkProfiler

Set default instance variables


20
21
22
23
24
25
26
27
# 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

Instance Attribute Details

#latencyObject (readonly)

Discovered network latency


16
17
18
# File 'lib/wmap/network_profiler.rb', line 16

def latency
  @latency
end

#max_parallelObject

Returns the value of attribute max_parallel.


15
16
17
# File 'lib/wmap/network_profiler.rb', line 15

def max_parallel
  @max_parallel
end

#search_pathObject

Returns the value of attribute search_path.


15
16
17
# File 'lib/wmap/network_profiler.rb', line 15

def search_path
  @search_path
end

#socket_timeoutObject

Returns the value of attribute socket_timeout.


15
16
17
# File 'lib/wmap/network_profiler.rb', line 15

def socket_timeout
  @socket_timeout
end

#verboseObject

Returns the value of attribute verbose.


15
16
17
# File 'lib/wmap/network_profiler.rb', line 15

def verbose
  @verbose
end

Instance Method Details

#open_tcp_port?(target) ⇒ Boolean

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

Returns:

  • (Boolean)

119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# 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) ⇒ Object

Main worker method that determine the right profiling methods


30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# 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?Boolean

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

Returns:

  • (Boolean)

100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# 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) ⇒ Boolean

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

on the raw ICMP socket

Returns:

  • (Boolean)

76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# 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) ⇒ Boolean

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.

Returns:

  • (Boolean)

55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# 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