From fd0f1d73f12e25e964397343c26e264a32c3543e Mon Sep 17 00:00:00 2001 From: cgranleese-r7 Date: Mon, 30 Oct 2023 13:42:27 +0000 Subject: [PATCH 1/4] Adds resolve host mixin --- lib/msf/core/post/dns.rb | 4 ++ lib/msf/core/post/dns/resolve_host.rb | 39 +++++++++++++++++++ modules/post/windows/gather/enum_computers.rb | 31 ++++----------- 3 files changed, 50 insertions(+), 24 deletions(-) create mode 100644 lib/msf/core/post/dns.rb create mode 100644 lib/msf/core/post/dns/resolve_host.rb diff --git a/lib/msf/core/post/dns.rb b/lib/msf/core/post/dns.rb new file mode 100644 index 000000000000..3034a9f84761 --- /dev/null +++ b/lib/msf/core/post/dns.rb @@ -0,0 +1,4 @@ +# -*- coding: binary -*- + +module Msf::Post::DNS +end diff --git a/lib/msf/core/post/dns/resolve_host.rb b/lib/msf/core/post/dns/resolve_host.rb new file mode 100644 index 000000000000..5d71d6a7b886 --- /dev/null +++ b/lib/msf/core/post/dns/resolve_host.rb @@ -0,0 +1,39 @@ +# -*- coding: binary -*- + +module Msf + class Post + module DNS + ### + # + # This module resolves session DNS + # + ### + module ResolveHost + # Takes the host name and makes use of nslookup to resolve the IP + # + # @param [String] host Hostname + # @return [Array, nil] result[:ips], ips The resolved IPs + def resolve_host(host) + if client.respond_to?(:net) && client.commands.include?(Rex::Post::Meterpreter::Extensions::Stdapi::COMMAND_ID_STDAPI_NET_RESOLVE_HOST) + result = client.net.resolve.resolve_host(host) + result[:ips] + else + ips = [] + data = cmd_exec("nslookup #{host}") + if data =~ /Name/ + # Remove unnecessary data and get the section with the addresses + returned_data = data.split(/Name:/)[1] + # check each element of the array to see if they are IP + returned_data.gsub(/\r\n\t |\r\n|Aliases:|Addresses:|Address:/, ' ').split(' ').each do |e| + if Rex::Socket.dotted_ip?(e) + ips << e + end + end + end + ips + end + end + end + end + end +end diff --git a/modules/post/windows/gather/enum_computers.rb b/modules/post/windows/gather/enum_computers.rb index 936cbdb840b4..7eb135df861d 100644 --- a/modules/post/windows/gather/enum_computers.rb +++ b/modules/post/windows/gather/enum_computers.rb @@ -7,6 +7,7 @@ class MetasploitModule < Msf::Post include Msf::Post::File include Msf::Post::Windows::Accounts include Msf::Post::Windows::Registry + include Msf::Post::DNS::ResolveHost def initialize(info = {}) super( @@ -60,34 +61,16 @@ def run # # @param [String] host Hostname # @return [String] ip The resolved IP - def resolve_host(host) - vprint_status("Looking up IP for #{host}") - return host if Rex::Socket.dotted_ip?(host) - - ip = [] - data = cmd_exec("nslookup #{host}") - if data =~ /Name/ - # Remove unnecessary data and get the section with the addresses - returned_data = data.split(/Name:/)[1] - # check each element of the array to see if they are IP - returned_data.gsub(/\r\n\t |\r\n|Aliases:|Addresses:|Address:/, ' ').split(' ').each do |e| - if Rex::Socket.dotted_ip?(e) - ip << e - end - end - end - - if ip.blank? - 'Not resolvable' - else - ip.join(', ') - end + def gethost(hostname) + ## get IP for host + vprint_status("Looking up IP for #{hostname}") + resolve_host(hostname).join(', ') end def get_domain_computers computer_list = [] divisor = "-------------------------------------------------------------------------------\r\n" - net_view_response = cmd_exec('net view') + net_view_response = cmd_exec("cmd.exe", "/c net view") unless net_view_response.include?(divisor) print_error("The net view command failed with: #{net_view_response}") return [] @@ -115,7 +98,7 @@ def list_computers(domain, hosts) ] ) hosts.each do |hostname| - hostip = resolve_host(hostname) + hostip = gethost(hostname) tbl << [domain, hostname, hostip] end From 0cbc302cfe835b6d06bae605fdb54f6c6b5c9baa Mon Sep 17 00:00:00 2001 From: cgranleese-r7 Date: Tue, 7 Nov 2023 14:11:23 +0000 Subject: [PATCH 2/4] Addresses PR feedback --- lib/msf/core/post/dns/resolve_host.rb | 4 +-- modules/post/windows/gather/enum_computers.rb | 26 ++++++++++++++++--- 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/lib/msf/core/post/dns/resolve_host.rb b/lib/msf/core/post/dns/resolve_host.rb index 5d71d6a7b886..75ee2a3c7460 100644 --- a/lib/msf/core/post/dns/resolve_host.rb +++ b/lib/msf/core/post/dns/resolve_host.rb @@ -13,9 +13,9 @@ module ResolveHost # # @param [String] host Hostname # @return [Array, nil] result[:ips], ips The resolved IPs - def resolve_host(host) + def resolve_host(host, family) if client.respond_to?(:net) && client.commands.include?(Rex::Post::Meterpreter::Extensions::Stdapi::COMMAND_ID_STDAPI_NET_RESOLVE_HOST) - result = client.net.resolve.resolve_host(host) + result = client.net.resolve.resolve_host(host, family) result[:ips] else ips = [] diff --git a/modules/post/windows/gather/enum_computers.rb b/modules/post/windows/gather/enum_computers.rb index 7eb135df861d..14c780e10c2d 100644 --- a/modules/post/windows/gather/enum_computers.rb +++ b/modules/post/windows/gather/enum_computers.rb @@ -61,10 +61,10 @@ def run # # @param [String] host Hostname # @return [String] ip The resolved IP - def gethost(hostname) + def gethost(hostname, family) ## get IP for host vprint_status("Looking up IP for #{hostname}") - resolve_host(hostname).join(', ') + resolve_host(hostname, family) end def get_domain_computers @@ -87,6 +87,7 @@ def get_domain_computers end def list_computers(domain, hosts) + meterpreter_dns_resolving_errors = [] tbl = Rex::Text::Table.new( 'Header' => 'List of identified Hosts.', 'Indent' => 1, @@ -98,12 +99,29 @@ def list_computers(domain, hosts) ] ) hosts.each do |hostname| - hostip = gethost(hostname) - tbl << [domain, hostname, hostip] + begin + hostipv4 = gethost(hostname, AF_INET) + rescue Rex::Post::Meterpreter::RequestError => e + meterpreter_dns_resolving_errors << "IPV4: #{hostname} could not be resolved - #{e}" + end + + begin + hostname = "google.com" + hostipv6 = gethost(hostname, AF_INET6) + rescue Rex::Post::Meterpreter::RequestError => e + meterpreter_dns_resolving_errors << "IPV6: #{hostname} could not be resolved - #{e}" + end + + hostipv4.each { |ip| tbl << [domain, hostname, ip] } unless hostipv4.nil? + hostipv6.each { |ip| tbl << [domain, hostname, ip] } unless hostipv6.nil? end print_line("\n#{tbl}\n") + meterpreter_dns_resolving_errors.each do | error | + print_warning(error) + end + report_note( host: session, type: 'domain.hosts', From 8ee5649f1562da210f966dd4766a3a92f68012bc Mon Sep 17 00:00:00 2001 From: cgranleese-r7 Date: Mon, 3 Feb 2025 14:47:56 +0000 Subject: [PATCH 3/4] Adds fallbacks and some tidy up --- lib/msf/core/post/dns/resolve_host.rb | 49 +++++++++++++++---- modules/post/windows/gather/enum_computers.rb | 4 +- 2 files changed, 41 insertions(+), 12 deletions(-) diff --git a/lib/msf/core/post/dns/resolve_host.rb b/lib/msf/core/post/dns/resolve_host.rb index 75ee2a3c7460..268ce79716fa 100644 --- a/lib/msf/core/post/dns/resolve_host.rb +++ b/lib/msf/core/post/dns/resolve_host.rb @@ -9,28 +9,57 @@ module DNS # ### module ResolveHost - # Takes the host name and makes use of nslookup to resolve the IP + # Takes the host name and resolves the IP # - # @param [String] host Hostname - # @return [Array, nil] result[:ips], ips The resolved IPs + # @param [String] host + # @param [Integer] family + # @return [Hash] The resolved IPs def resolve_host(host, family) if client.respond_to?(:net) && client.commands.include?(Rex::Post::Meterpreter::Extensions::Stdapi::COMMAND_ID_STDAPI_NET_RESOLVE_HOST) - result = client.net.resolve.resolve_host(host, family) - result[:ips] + resolved_host = client.net.resolve.resolve_host(host, family) + # We want to only return the array off IPs + resolved_host.reject { |k, _v| k == :ip } else ips = [] data = cmd_exec("nslookup #{host}") if data =~ /Name/ # Remove unnecessary data and get the section with the addresses - returned_data = data.split(/Name:/)[1] + returned_data = data.split(/Name:/)[1..] # check each element of the array to see if they are IP - returned_data.gsub(/\r\n\t |\r\n|Aliases:|Addresses:|Address:/, ' ').split(' ').each do |e| - if Rex::Socket.dotted_ip?(e) - ips << e + returned_data.each do |entry| + _host, ip = entry.gsub(/\r\n\t |\r\n|Aliases:|Addresses:|Address:/, ' ').split(' ') + filtered_ip = filter_ips(ip, family) + ips << filtered_ip unless filtered_ip.nil? + end + # If nslookup responds with "no answer", fall back to resolving via host command + elsif data =~ /No answer/ + data = cmd_exec("host #{host}") + if data =~ /has address/ + # Remove unnecessary data and get the section with the addresses + returned_data = data.split("\n")[...-1] + # check each element of the array to see if they are IP + returned_data.each do |entry| + ip = entry.split(' ').last + filtered_ip = filter_ips(ip, family) + ips << filtered_ip unless filtered_ip.nil? end end end - ips + {:hostname=>host, :ips=>ips} + end + end + + # Takes the host and family and returns the IP address if it matches the appropriate family + # Needed to handle request that fallback to nslookup or host, as they return both IPV4 and IPV6. + # + # @param [String] ip + # @param [Integer] family + # @return [String] ip + def filter_ips(ip, family) + if family == AF_INET + ip if !!(ip =~ Resolv::IPv4::Regex) + elsif family == AF_INET6 + ip if !!(ip =~ Resolv::IPv6::Regex) end end end diff --git a/modules/post/windows/gather/enum_computers.rb b/modules/post/windows/gather/enum_computers.rb index 14c780e10c2d..99fdeda2bf2e 100644 --- a/modules/post/windows/gather/enum_computers.rb +++ b/modules/post/windows/gather/enum_computers.rb @@ -59,7 +59,8 @@ def run # Takes the host name and makes use of nslookup to resolve the IP # - # @param [String] host Hostname + # @param [Object] hostname + # @param [Object] family # @return [String] ip The resolved IP def gethost(hostname, family) ## get IP for host @@ -106,7 +107,6 @@ def list_computers(domain, hosts) end begin - hostname = "google.com" hostipv6 = gethost(hostname, AF_INET6) rescue Rex::Post::Meterpreter::RequestError => e meterpreter_dns_resolving_errors << "IPV6: #{hostname} could not be resolved - #{e}" From dea28d1988a25ad0ff429e57c8a1a42525d91b04 Mon Sep 17 00:00:00 2001 From: cgranleese-r7 Date: Mon, 3 Feb 2025 16:04:34 +0000 Subject: [PATCH 4/4] Adds a test module for host resolution --- lib/msf/core/post/dns/resolve_host.rb | 39 ++++---- modules/post/windows/gather/enum_computers.rb | 24 ++--- spec/support/acceptance/command_shell/cmd.rb | 32 +++++++ .../support/acceptance/command_shell/linux.rb | 26 ++++++ .../acceptance/command_shell/powershell.rb | 32 +++++++ spec/support/acceptance/session/java.rb | 16 ++++ spec/support/acceptance/session/mettle.rb | 26 ++++++ spec/support/acceptance/session/php.rb | 17 ++++ spec/support/acceptance/session/python.rb | 16 ++++ .../acceptance/session/windows_meterpreter.rb | 32 +++++++ test/modules/post/test/resolve_host.rb | 89 +++++++++++++++++++ 11 files changed, 321 insertions(+), 28 deletions(-) create mode 100644 test/modules/post/test/resolve_host.rb diff --git a/lib/msf/core/post/dns/resolve_host.rb b/lib/msf/core/post/dns/resolve_host.rb index 268ce79716fa..07aa05aa6897 100644 --- a/lib/msf/core/post/dns/resolve_host.rb +++ b/lib/msf/core/post/dns/resolve_host.rb @@ -1,5 +1,7 @@ # -*- coding: binary -*- +require 'rex/post/meterpreter/extensions/stdapi/constants' + module Msf class Post module DNS @@ -12,7 +14,7 @@ module ResolveHost # Takes the host name and resolves the IP # # @param [String] host - # @param [Integer] family + # @param [Integer] family AF_INET for IPV4 and AF_INET6 for IPV6 # @return [Hash] The resolved IPs def resolve_host(host, family) if client.respond_to?(:net) && client.commands.include?(Rex::Post::Meterpreter::Extensions::Stdapi::COMMAND_ID_STDAPI_NET_RESOLVE_HOST) @@ -27,9 +29,9 @@ def resolve_host(host, family) returned_data = data.split(/Name:/)[1..] # check each element of the array to see if they are IP returned_data.each do |entry| - _host, ip = entry.gsub(/\r\n\t |\r\n|Aliases:|Addresses:|Address:/, ' ').split(' ') - filtered_ip = filter_ips(ip, family) - ips << filtered_ip unless filtered_ip.nil? + ip_list = entry.gsub(/\r\n\t |\r\n|Aliases:|Addresses:|Address:/, ' ').split(' ') - [host] + filtered_ips = filter_ips(ip_list, family) + ips = filtered_ips unless filtered_ips.empty? end # If nslookup responds with "no answer", fall back to resolving via host command elsif data =~ /No answer/ @@ -38,29 +40,32 @@ def resolve_host(host, family) # Remove unnecessary data and get the section with the addresses returned_data = data.split("\n")[...-1] # check each element of the array to see if they are IP - returned_data.each do |entry| - ip = entry.split(' ').last - filtered_ip = filter_ips(ip, family) - ips << filtered_ip unless filtered_ip.nil? - end + ip_list = returned_data.map { |entry| entry.split(' ').last } + filtered_ips = filter_ips(ip_list, family) + ips = filtered_ips unless filtered_ips.empty? end end - {:hostname=>host, :ips=>ips} + { hostname: host, ips: ips } end end # Takes the host and family and returns the IP address if it matches the appropriate family # Needed to handle request that fallback to nslookup or host, as they return both IPV4 and IPV6. # - # @param [String] ip + # @param [Array] ips # @param [Integer] family - # @return [String] ip - def filter_ips(ip, family) - if family == AF_INET - ip if !!(ip =~ Resolv::IPv4::Regex) - elsif family == AF_INET6 - ip if !!(ip =~ Resolv::IPv6::Regex) + # @return [Array] ips + def filter_ips(ips, family) + filtered_ips = [] + ips.each do |ip| + if family == AF_INET + filtered_ips << ip if !!(ip =~ Resolv::IPv4::Regex) + elsif family == AF_INET6 + filtered_ips << ip if !!(ip =~ Resolv::IPv6::Regex) + end end + + filtered_ips end end end diff --git a/modules/post/windows/gather/enum_computers.rb b/modules/post/windows/gather/enum_computers.rb index 99fdeda2bf2e..96dde66b3d07 100644 --- a/modules/post/windows/gather/enum_computers.rb +++ b/modules/post/windows/gather/enum_computers.rb @@ -3,6 +3,8 @@ # Current source: https://github.com/rapid7/metasploit-framework ## +require 'rex/post/meterpreter/extensions/stdapi/constants' + class MetasploitModule < Msf::Post include Msf::Post::File include Msf::Post::Windows::Accounts @@ -100,20 +102,20 @@ def list_computers(domain, hosts) ] ) hosts.each do |hostname| - begin - hostipv4 = gethost(hostname, AF_INET) - rescue Rex::Post::Meterpreter::RequestError => e - meterpreter_dns_resolving_errors << "IPV4: #{hostname} could not be resolved - #{e}" - end + hostipv4 = gethost(hostname, AF_INET) + hostipv6 = gethost(hostname, AF_INET6) - begin - hostipv6 = gethost(hostname, AF_INET6) - rescue Rex::Post::Meterpreter::RequestError => e - meterpreter_dns_resolving_errors << "IPV6: #{hostname} could not be resolved - #{e}" + if hostipv4[:ips].empty? + meterpreter_dns_resolving_errors << "IPV4: #{hostname} could not be resolved" + else + tbl << [domain, hostname, hostipv4[:ips].join(',')] end - hostipv4.each { |ip| tbl << [domain, hostname, ip] } unless hostipv4.nil? - hostipv6.each { |ip| tbl << [domain, hostname, ip] } unless hostipv6.nil? + if hostipv6[:ips].empty? + meterpreter_dns_resolving_errors << "IPV6: #{hostname} could not be resolved" if hostipv6[:ips].empty? + else + tbl << [domain, hostname, hostipv6[:ips].join(',')] unless hostipv6[:ips].nil? + end end print_line("\n#{tbl}\n") diff --git a/spec/support/acceptance/command_shell/cmd.rb b/spec/support/acceptance/command_shell/cmd.rb index 8f9fca7f68cf..84bda40b88c7 100644 --- a/spec/support/acceptance/command_shell/cmd.rb +++ b/spec/support/acceptance/command_shell/cmd.rb @@ -17,6 +17,38 @@ module Acceptance::Session } ], module_tests: [ + { + name: 'post/test/resolve_host', + platforms: [ + [ + :linux, + { + skip: true, + reason: 'Payload not compiled for platform' + } + ], + [ + :osx, + { + skip: true, + reason: 'Payload not compiled for platform' + } + ], + :windows + ], + skipped: false, + lines: { + linux: { + known_failures: [] + }, + osx: { + known_failures: [] + }, + windows: { + known_failures: [] + } + } + }, { name: 'post/test/cmd_exec', platforms: [ diff --git a/spec/support/acceptance/command_shell/linux.rb b/spec/support/acceptance/command_shell/linux.rb index 3eb40744ec55..8a5cc2dafc14 100644 --- a/spec/support/acceptance/command_shell/linux.rb +++ b/spec/support/acceptance/command_shell/linux.rb @@ -49,6 +49,32 @@ module Acceptance::Session } } }, + { + name: "post/test/resolve_host", + platforms: [ + :linux, + :osx, + [ + :windows, + { + skip: true, + reason: "Payload not compiled for platform" + } + ] + ], + skipped: false, + lines: { + linux: { + known_failures: [] + }, + osx: { + known_failures: [] + }, + windows: { + known_failures: [] + } + } + }, { name: "post/test/cmd_exec", platforms: [ diff --git a/spec/support/acceptance/command_shell/powershell.rb b/spec/support/acceptance/command_shell/powershell.rb index f941057eccd4..493edb7aa75a 100644 --- a/spec/support/acceptance/command_shell/powershell.rb +++ b/spec/support/acceptance/command_shell/powershell.rb @@ -17,6 +17,38 @@ module Acceptance::Session } ], module_tests: [ + { + name: 'post/test/resolve_host', + platforms: [ + [ + :linux, + { + skip: true, + reason: 'Payload not compiled for platform' + } + ], + [ + :osx, + { + skip: true, + reason: 'Payload not compiled for platform' + } + ], + :windows + ], + skipped: false, + lines: { + linux: { + known_failures: [] + }, + osx: { + known_failures: [] + }, + windows: { + known_failures: [] + } + } + }, { name: 'post/test/cmd_exec', platforms: [ diff --git a/spec/support/acceptance/session/java.rb b/spec/support/acceptance/session/java.rb index 029ce6e672f0..c92c1f1dd7b7 100644 --- a/spec/support/acceptance/session/java.rb +++ b/spec/support/acceptance/session/java.rb @@ -73,6 +73,22 @@ module Acceptance::Session } } }, + { + name: "post/test/resolve_host", + platforms: [:linux, :osx, :windows], + skipped: false, + lines: { + linux: { + known_failures: [] + }, + osx: { + known_failures: [] + }, + windows: { + known_failures: [] + } + } + }, { name: "post/test/cmd_exec", platforms: [:linux, :osx, :windows], diff --git a/spec/support/acceptance/session/mettle.rb b/spec/support/acceptance/session/mettle.rb index 182a0d370610..df792326f08f 100644 --- a/spec/support/acceptance/session/mettle.rb +++ b/spec/support/acceptance/session/mettle.rb @@ -69,6 +69,32 @@ module Acceptance::Session } } }, + { + name: "post/test/resolve_host", + platforms: [ + :linux, + :osx, + [ + :windows, + { + skip: true, + reason: "Payload not compiled for platform" + } + ] + ], + skipped: false, + lines: { + linux: { + known_failures: [] + }, + osx: { + known_failures: [] + }, + windows: { + known_failures: [] + } + } + }, { name: "post/test/cmd_exec", platforms: [ diff --git a/spec/support/acceptance/session/php.rb b/spec/support/acceptance/session/php.rb index 0cc3fb666858..a608da234b80 100644 --- a/spec/support/acceptance/session/php.rb +++ b/spec/support/acceptance/session/php.rb @@ -60,6 +60,23 @@ module Acceptance::Session } } }, + { + name: "post/test/resolve_host", + platforms: [:linux, :osx, :windows], + skipped: false, + lines: { + linux: { + known_failures: [] + }, + osx: { + known_failures: [] + }, + windows: { + known_failures: [ + ] + } + } + }, { name: "post/test/cmd_exec", platforms: [:linux, :osx, :windows], diff --git a/spec/support/acceptance/session/python.rb b/spec/support/acceptance/session/python.rb index eb2870c0eb7c..c93fc4bb37d7 100644 --- a/spec/support/acceptance/session/python.rb +++ b/spec/support/acceptance/session/python.rb @@ -72,6 +72,22 @@ module Acceptance::Session } } }, + { + name: "post/test/resolve_host", + platforms: [:linux, :osx, :windows], + skipped: false, + lines: { + linux: { + known_failures: [] + }, + osx: { + known_failures: [] + }, + windows: { + known_failures: [] + } + } + }, { name: "post/test/cmd_exec", platforms: [:linux, :osx, :windows], diff --git a/spec/support/acceptance/session/windows_meterpreter.rb b/spec/support/acceptance/session/windows_meterpreter.rb index 7784ab66d2fa..a28d2c8bd74a 100644 --- a/spec/support/acceptance/session/windows_meterpreter.rb +++ b/spec/support/acceptance/session/windows_meterpreter.rb @@ -53,6 +53,38 @@ module Acceptance::Session } } }, + { + name: "post/test/resolve_host", + platforms: [ + [ + :linux, + { + skip: true, + reason: "Payload not compiled for platform" + } + ], + [ + :osx, + { + skip: true, + reason: "Payload not compiled for platform" + } + ], + :windows + ], + skipped: false, + lines: { + linux: { + known_failures: [] + }, + osx: { + known_failures: [] + }, + windows: { + known_failures: [] + } + } + }, { name: "post/test/cmd_exec", platforms: [ diff --git a/test/modules/post/test/resolve_host.rb b/test/modules/post/test/resolve_host.rb new file mode 100644 index 000000000000..b33de11eb14f --- /dev/null +++ b/test/modules/post/test/resolve_host.rb @@ -0,0 +1,89 @@ +require 'rex' +require 'rex/post/meterpreter/extensions/stdapi/constants' + +lib = File.join(Msf::Config.install_root, 'test', 'lib') +$LOAD_PATH.push(lib) unless $LOAD_PATH.include?(lib) +require 'module_test' + +class MetasploitModule < Msf::Post + include Msf::ModuleTest::PostTest + include Msf::Post::DNS::ResolveHost + + def initialize(info = {}) + super( + update_info( + info, + 'Name' => 'Meterpreter resolve_host test', + 'Description' => %q{ This module will test the meterpreter resolve_host API }, + 'License' => MSF_LICENSE, + 'Platform' => [ 'windows', 'linux', 'unix', 'java', 'osx' ], + 'SessionTypes' => ['meterpreter', 'shell', 'powershell'] + ) + ) + end + + def test_resolve_host + vprint_status('Starting resolve_host tests') + + it 'should return a Hash' do + # TODO: UPDATE TO NOT POINT AT GOOGLE + hostname = 'google.com' + family = AF_INET6 + + resolved_host = resolve_host(hostname, family) + resolved_host.is_a?(Hash) + end + + it 'should return a valid IPV4 host' do + hostname = 'google.com' + family = AF_INET + + resolved_host = resolve_host(hostname, family) + puts resolved_host[:ips] + if resolved_host[:ips].empty? + false + else + resolved_host[:ips].each do |ip| + !!(ip =~ Resolv::IPv4::Regex) + end + end + end + + it 'should return a valid IPV6 host' do + hostname = 'google.com' + family = AF_INET6 + + resolved_host = resolve_host(hostname, family) + puts resolved_host[:ips] + if resolved_host[:ips].empty? + false + else + resolved_host[:ips].each do |ip| + !!(ip =~ Resolv::IPv6::Regex) + end + end + end + + it 'should handle an invalid IPV4 host' do + hostname = 'foo.bar' + family = AF_INET + + begin + resolve_host(hostname, family) + rescue Rex::Post::Meterpreter::RequestError => e + e.instance_of?(Rex::Post::Meterpreter::RequestError) + end + end + + it 'should handle an invalid IPV6 host' do + hostname = 'foo.bar' + family = AF_INET6 + + begin + resolve_host(hostname, family) + rescue Rex::Post::Meterpreter::RequestError => e + e.instance_of?(Rex::Post::Meterpreter::RequestError) + end + end + end +end