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

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, #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