13
\$\begingroup\$

I made lots of changes to the script presented in my previous question. I was tempted to edit that one with the new code, but it would invalidate @200_success's helpful answer. It was also disallowed on a Meta thread. So I apologize for the new question, but it seemed like the right thing to do.

Again, any and all tips are appreciated! Also, this is my first time writing any docstrings, so if I'm breaking convention, let me know.

#!/usr/bin/env python3
import argparse
import errno
import functools
import multiprocessing
import os
import platform
import socket
import time
from concurrent.futures import ThreadPoolExecutor
from concurrent.futures import ProcessPoolExecutor
DEFAULT_HOST = '127.0.0.1'
DEFAULT_TIMEOUT = 1
DEFAULT_THREADS = 512
PORT_RANGE = range(1, 65536)
def tcp_ping(host, port):
 """
 Attempts to connect to host:port via TCP.
 Arguments:
 host: IP address or URL to hit
 port: Port to hit
 Returns:
 port number, if it's available; otherwise False
 """
 try:
 with socket.socket() as sock:
 sock.connect((host, port))
 print(str(port) + ' Open')
 return port
 except socket.timeout:
 return False
 except socket.error as socket_error:
 if socket_error.errno == errno.ECONNREFUSED:
 return False
 raise
def perform_scan(host, use_threads = False):
 """
 Perform a scan on all valid ports (1 - 65535), either by 
 spawning threads or forking processes.
 Arguments:
 host: IP address or URL to scan
 use_threads: whether or not to use threads; default
 behaviour is to fork processes
 Returns:
 list of available ports
 """
 if use_threads:
 executor = ThreadPoolExecutor(max_workers = DEFAULT_THREADS)
 else:
 executor = ProcessPoolExecutor()
 with executor:
 ping_partial = functools.partial(tcp_ping, host)
 return list(filter(bool, executor.map(ping_partial, PORT_RANGE)))
def is_address_valid(host):
 """
 Validate the host's existence by attempting to establish
 a connection to it on port 80 (HTTP).
 Arguments:
 host: IP address or URL to validate
 Returns:
 bool indicating whether the host is valid
 """
 try:
 with socket.socket() as sock:
 sock.connect((host, 80))
 return True
 except socket.gaierror:
 return False
 except (socket.timeout, socket.error):
 return True
def scan_ports(host = DEFAULT_HOST, timeout = DEFAULT_TIMEOUT):
 """
 Scan all possible ports on the specified host. If the
 operating system is detected as Windows, the ports will be 
 scanned by spawning threads. Otherwise, new processes will
 be forked.
 Arguments:
 host: IP address or URL to scan
 timeout: connection timeout when testing a port
 """
 # Set defaults if CLI arguments were passed in but not specified
 if host is None:
 host = DEFAULT_HOST
 if timeout is None:
 timeout = DEFAULT_TIMEOUT
 # Strip the protocol from the URL, if present
 if '://' in host:
 host = host[host.index('://') + 3:]
 # Set the timeout for all subsequent connections
 socket.setdefaulttimeout(timeout)
 # Validate the IP/host by testing a connection
 if not is_address_valid(host):
 print('DNS lookup for \'' + host + '\' failed.')
 return
 # Perform the scan. On Windows, thread. On all others, fork.
 print('Scanning ' + host + ' ...')
 start_time = time.clock()
 if os.name == 'nt':
 print('Running on Windows OS.')
 available_ports = perform_scan(host, use_threads = True)
 elif os.name == 'posix':
 print('Running on *Nix OS.')
 available_ports = perform_scan(host)
 else:
 print('Unidentified operating system: ' + os.name 
 + ' [' + platform.system() + ' ' + platform.version() + ']')
 available_ports = perform_scan(host)
 end_time = time.clock()
 print('Done.')
 # Display results
 print()
 print('Time elapsed: ' + format(end_time - start_time, '.2f') + ' sec')
 available_ports.sort()
 print()
 print(str(len(available_ports)) + ' ports available.')
 print(available_ports)
def main():
 arg_parser = argparse.ArgumentParser()
 arg_parser.add_argument('-ip', '--host', help = 'IP address/host to scan')
 arg_parser.add_argument('-t', '--timeout', help = 'Connection timeout in seconds', type = int)
 args = arg_parser.parse_args()
 scan_ports(args.host, args.timeout)
if __name__ == '__main__':
 main()
asked Apr 11, 2014 at 1:18
\$\endgroup\$
1
  • 12
    \$\begingroup\$ Asking a new question is the right thing to do! \$\endgroup\$ Commented Apr 11, 2014 at 6:27

2 Answers 2

5
\$\begingroup\$

Trying to connect to TCP port 80 in order to check whether the DNS lookup succeeds is overkill. You should just call socket.gethostbyname().

answered Apr 11, 2014 at 18:58
\$\endgroup\$
4
\$\begingroup\$

I notice a few little things that I would adjust just from a housekeeping perspective.

  1. You import multiprocessing but I don't see where it is used.. I would remove if it is not needed.
  2. some small pep8 stuff - Keyword args should not have spaces around the equal, 2 blank lines between each function, and more than 80 line length on the argpase.add_arg near the bottom.
answered Apr 13, 2014 at 3:50
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.