diff --git a/CHANGELOG.md b/CHANGELOG.md index d87b56d42..ef81eaa55 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ Version 0.5.3 - ## MISC * #314 Makefile now allows supports the PREFIX environment variable. Thanks @bfontaine +## FEATURES +* #303 Added ability to select network interface. Thanks @audibleblink. Version 0.5.2 - June 9, 2020 diff --git a/lib/extend-http.rb b/lib/extend-http.rb index b85f5a49c..7fd7acbc5 100644 --- a/lib/extend-http.rb +++ b/lib/extend-http.rb @@ -21,10 +21,10 @@ class ExtendedHTTP < Net::HTTP #:nodoc: # without opening the TCP connection or initializing the HTTP session. # The +address+ should be a DNS hostname or IP address. def initialize(address, port = nil) + @address = address @port = (port || HTTP.default_port) - @local_host = nil - @local_port = nil + @curr_http_version = HTTPVersion @keep_alive_timeout = 2 @last_communicated = nil @@ -75,6 +75,8 @@ def connect D "opening connection to #{conn_address}:#{conn_port}..." s = Timeout.timeout(@open_timeout, Net::OpenTimeout) do TCPSocket.open(conn_address, conn_port, @local_host, @local_port) + # TCPSocket.open(conn_address, conn_port, '127.0.0.1', '11111') + end s.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1) D 'opened' diff --git a/lib/target.rb b/lib/target.rb index efce08599..77e9e09b5 100644 --- a/lib/target.rb +++ b/lib/target.rb @@ -267,6 +267,8 @@ def open_url(options) http = ExtendedHTTP::Proxy($PROXY_HOST, $PROXY_PORT, $PROXY_USER, $PROXY_PASS).new(@uri.host, @uri.port) else http = ExtendedHTTP.new(@uri.host, @uri.port) + http.local_host = $local_host + http.local_port = $local_port if $local_port end # set timeouts @@ -317,8 +319,8 @@ def open_url(options) puts @uri.to_s + " [#{status}]" if $verbose > 1 rescue StandardError => err + pp err raise err - end end diff --git a/lib/whatweb.rb b/lib/whatweb.rb index ced48a2e5..9d81f4aca 100644 --- a/lib/whatweb.rb +++ b/lib/whatweb.rb @@ -30,6 +30,7 @@ require 'open-uri' require 'digest/md5' require 'openssl' # required for Ruby version ~> 2.4 +require 'socket' require 'pp' @@ -54,7 +55,7 @@ Dir["#{File.expand_path(File.dirname(__FILE__))}/logging/*.rb"].each {|file| require file } # Output options -$WWDEBUG = false # raise exceptions in plugins, etc +$WWDEBUG = true # raise exceptions in plugins, etc $verbose = 0 # $VERBOSE is reserved in ruby $use_colour = 'auto' $QUIET = false @@ -78,6 +79,9 @@ $BASIC_AUTH_USER = nil $BASIC_AUTH_PASS = nil +$local_host = nil +$local_post = nil + # Ruby Version Compatability if Gem::Version.new(RUBY_VERSION) < Gem::Version.new(2.0) raise('Unsupported version of Ruby. WhatWeb requires Ruby 2.0 or later.') diff --git a/whatweb b/whatweb index 667fa937e..73974d432 100755 --- a/whatweb +++ b/whatweb @@ -155,6 +155,11 @@ PERFORMANCE & STABILITY: --wait=SECONDS\t\tWait SECONDS between connections. \t\t\t\tThis is useful when using a single thread. +NETWORK INTERFACE: + --interface=INTERFACE\t\tUse specified network interface. + --source-port=PORT\t\tSpecify source TCP port to use. + --source-addr=ADDRESS\t\tSpecify source IP address to use. + HELP & MISCELLANEOUS: --short-help\t\t\tShort usage help. --help, -h\t\t\tComplete usage help. @@ -324,6 +329,9 @@ opts = GetoptLong.new( ['-U', '--user-agent', GetoptLong::REQUIRED_ARGUMENT], ['-a', '--aggression', GetoptLong::REQUIRED_ARGUMENT], ['-t', '--max-threads', GetoptLong::REQUIRED_ARGUMENT], + ['--interface', GetoptLong::REQUIRED_ARGUMENT], + ['--source-port', GetoptLong::REQUIRED_ARGUMENT], + ['--source-address', GetoptLong::REQUIRED_ARGUMENT], ['--follow-redirect', GetoptLong::REQUIRED_ARGUMENT], ['--max-redirects', GetoptLong::REQUIRED_ARGUMENT], ['--proxy', GetoptLong::REQUIRED_ARGUMENT], @@ -356,6 +364,51 @@ begin exit when '-p', '--plugins' plugin_selection = arg + + when '--interface' + $interface = arg.downcase + + valid_interfaces = Socket.getifaddrs.map(&:name).uniq + raise "Network interface #{$interface} is not valid.\n" unless valid_interfaces.include? $interface + pp "Found valid interfaces: #{valid_interfaces}" if $verbose > 2 + + # unless the user has already selected this manually + unless $local_host + + # choose the IP address to use + number_of_ips_on_interface = Socket.getifaddrs.select { |x| x.name == $interface and x.addr.ip? }.compact.size + number_of_ipv4s_on_interface = Socket.getifaddrs.select { |x| x.name == $interface and x.addr.ip? and x.addr.ipv4? }.compact.size + number_of_ipv6s_on_interface = Socket.getifaddrs.select { |x| x.name == $interface and x.addr.ip? and x.addr.ipv6? }.compact.size + + $local_host = if number_of_ips_on_interface == 0 + raise "No IP addresses were found for the #{$interface} interface." + + elsif number_of_ipv4s_on_interface > 0 + Socket.getifaddrs.select { |x| x.name == $interface and x.addr.ip? and x.addr.ipv4? }.map(&:addr).first.ip_address + + elsif number_of_ipv6s_on_interface > 0 + Socket.getifaddrs.select { |x| x.name == $interface and x.addr.ip? and x.addr.ipv6? }.map(&:addr).first.ip_address.split("%").first + + elsif number_of_ipv4s_on_interface == 0 and number_of_ipv6s_on_interface == 0 + raise "No IPv4 or IPv6 addresses were found for #{$interface} interface." + end + + pp "IP address auto-selected from interface: #{$local_host}" if $verbose > 2 + else + pp "IP address already selected by user: #{$local_host}" if $verbose > 2 + end + + when '--source-port' + $local_port = arg.to_i + raise "Source port must be betwen 0 and 65535\n" unless (0..65535).include? $local_port + + when '--source-address' + raise "Specify source address or the interface, but not both." if $local_host + + $local_host = arg + valid_ip_addresses = Socket.ip_address_list.map { |x| x.ip_address.split("%").first }.uniq + raise "Valid IP address must be specified\n" unless valid_ip_addresses.include? $local_host + when '-I', '--info-plugins' PluginSupport.load_plugins PluginSupport.plugin_info(arg.split(',')) @@ -547,6 +600,9 @@ rescue StandardError, GetoptLong::Error => err exit end +# check interfaces +pp "interface: $interface, local_host: #{$local_host}, local_port: #{$local_port}" if $verbose > 2 + # sanity check # Disable colours in Windows environments when set to auto if RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ $use_colour = false unless $use_colour == 'always' @@ -636,3 +692,4 @@ logging_list.each(&:close) Plugin.shutdown_all # pp $PLUGIN_TIMES.sort_by {|x,y|y } +