misc/python/scanner.py
2018-08-05 18:53:07 +02:00

127 lines
3.2 KiB
Python
Executable File

#!/usr/bin/env python3
## Simple TCP port scanner
import argparse
import re
import ast
import socket
import io
import concurrent.futures
def _ports(port):
"""
Validates argparse port argument.
Accepts a single port, a port range or a list (comma separated).
"""
valid_range = range(1, 65535 + 1)
range_re = re.compile("\d+:\d+")
list_re = re.compile("(\d+,\d+)+")
try:
port = int(port)
if port not in valid_range:
raise argparse.ArgumentTypeError("Port must be 1-65535")
return [port]
except ValueError:
if range_re.match(port):
start, stop = (int(i) for i in port.split(":"))
if (start not in valid_range
or stop not in valid_range
or start > stop):
raise argparse.ArgumentTypeError("Invalid range: " + port)
return range(start, stop + 1)
elif list_re.match(port):
try:
return [i for i in ast.literal_eval(id) if i in valid_range]
except:
raise argparse.ArgumentTypeError("Invalid list: " + port)
def _hostname(name):
"""
Validates argparse hostname argument.
Accepts an ip address or a domain name.
"""
try:
socket.gethostbyname(name)
return name
except socket.gaierror:
raise argparse.ArgumentTypeError("Invalid hostname: " + name)
try:
socket.inet_aton(name)
return name
except socket.error:
raise argparse.ArgumentTypeError("Invalid ip address: " + name)
def connect(hostname, port, verbose, banner):
"""
Connects to a given hostname and port.
Set banner to True to get the application banner.
Set verbose level (1,2,3) to get more informations.
"""
out = io.StringIO()
try:
connection = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
connection.connect((hostname, port))
print("[+] %d open" % port, file=out)
if banner:
connection.send("hi\r\n".encode("utf-8"))
banner = connection.recv(100).decode("utf-8").strip("\n")
print(" ┖──[b] " + banner, file=out)
connection.close()
except ConnectionRefusedError as e:
if verbose > 0:
print("[-] %d closed" % port, file=out)
if verbose > 1:
print(" ┖──[v] %s" % e, file=out)
except socket.timeout:
if banner and verbose > 1:
print(" ┖──[b] No response", file=out)
return out
def scan(hostname, ports, verbose=0, banner=False):
"""Perform a scan on the given hostname and ports."""
socket.setdefaulttimeout(2)
try:
name = socket.gethostbyaddr(hostname)
print("[*] Scanning " + name[0])
except socket.error as e:
print("[*] Scanning " + hostname)
with concurrent.futures.ThreadPoolExecutor(100) as executor:
threads = []
for port in ports:
threads.append(executor.submit(connect, hostname, port, verbose, banner))
for i in threads:
print(i.result().getvalue(), end="")
def main():
parser = argparse.ArgumentParser(description="Simple TCP port scanner.")
parser.add_argument(
"hostname", type=_hostname,
help="Hostname, ip address.")
parser.add_argument(
"ports", type=_ports,
help="Single port, range a:b or list (comma separated)")
parser.add_argument(
"-v", "--verbose",
action="count", default=0,
help="Show more information")
parser.add_argument(
"-b", "--banner",
action="store_true", default=False,
help="Get application banner.")
args = parser.parse_args()
scan(**vars(args))
if __name__ == "__main__":
main()