Permalink
Switch branches/tags
Nothing to show
Find file
Fetching contributors…
Cannot retrieve contributors at this time
executable file 111 lines (94 sloc) 3.48 KB
#!/usr/bin/env ruby
if ARGV.include? '-h' or ARGV.include? '--help' then puts <<-HELP
Port-scan one machine or everything on your subnet(s).
http://benalman.com/
Usage: #{File.basename $0} [--full] [host_or_ip_or_wildcard ...]
Scans your subnet(s) using nmap, displaying results in a pretty format. If *
appears at the end of the line, that IP is currently bound to this machine.
Specify --full for a full subnet port scan (this can take a while), or specify
one or more hosts, IPs or wildcards on which to perform a full port scan. The
--full option is implicit when any hosts, IPs or wildcards are specified.
Maybe it'll help you find that server you lost. It didn't help me--but that's
only because the machine I was looking for wasn't connected. Whoops.
Copyright (c) 2012 "Cowboy" Ben Alman
Licensed under the MIT license.
http://benalman.com/about/license/
HELP
exit; end
# my "test suite"
%s{
scans=("" "x" "192.168.0.69" "192.168.0.232" "192.168.0.1" "192.168.0.1 192.168.0.107" "--full"); \
for s in "${scans[@]}"; do echo -e "===\nargs: $s\n---"; scan $s; echo -e "---\nexit code: $?"; done
}
ips = []
subnets = []
# Get IPs and subnets we care about from ifconfig. Basically, from any network
# device starting with the letter "e" (en0, en1, eth0, eth1, etc).
ether = nil
%x{ifconfig}.each_line do |line|
if line =~ /^(\S)/
# If the line starts with a letter, set a flag if that letter is "e".
ether = $1 == 'e'
elsif ether && line =~ /inet (?:addr:)?((\d+\.\d+\.\d+)\.\d+)/
# Save IP address (1.2.3.4) and subnet (1.2.3.*) for later.
ips << $1
subnets << "#{$2}.*"
end
end
# Parse things to scan from the passed arguments.
args = ARGV.reject{|a| a =~ /^-/}.join ' '
# Full scan?
full = ARGV.include?('--full') || !args.empty?
# Get a space-delimited list of unique subnets to be scanned, but only if
# things to scan weren't already passed as arguments.
things_to_scan = !args.empty? ? args : subnets.uniq.join(' ')
# Since nmap only supports saving greppable output to a file and not STDOUT,
# create a tempfile.
require 'tempfile'
tmp = Tempfile.new 'scan'
if full
puts "Scanning #{things_to_scan} (full scan, be patient)"
args = "-sT #{things_to_scan}" # The works (full port scan).
# Skip ping if there no wildcards were passed.
args = "-PN #{args}" unless things_to_scan.include? '*'
else
puts "Scanning #{things_to_scan}"
args = "-sP #{things_to_scan}" # Relatively quick scan.
end
# Actually perform scan.
%x{nmap -oG "#{tmp.path}" -T4 #{args} >/dev/null 2>&1}
puts
# Parse scan results.
skip = ''
exit_code = 0
tmp.read.each_line do |line|
if line =~ /^Host: (\S+) \((\S*)\).*Status: Up$/
# Found an IP.
ip = $1
hostname = $2.empty? ? '???' : $2
# If the found IP is bound to a local adapter we care about, show a *.
isbound = ips.include?(ip) ? ' *' : ''
# Output the IP / hostname.
puts "#{skip}#{ip}#{' ' * (16 - ip.length)}#{hostname}#{isbound}"
elsif line =~ /Ports: (.*?)\t/
# This only appears in the tempfile if a full port scan was done.
# Output the port details.
$1.split(', ').each do |part|
parts = part.split '/'
printf ' :%-5d %s %s', parts[0], parts[2], parts[4]
print " (#{parts[6]})" if parts[6]
puts
end
# The next time an IP is displayed, skip a line.
skip = "\n"
elsif line =~ /# Nmap done.* (\d+ IP address.*)/
exit_code = 1 if $1.start_with? '0 IP addresses'
puts unless $1.include? '(0 hosts up)'
puts $1
end
end
# Cleanup.
tmp.close
tmp.unlink
# Quit.
exit exit_code