diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..aeba617 --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +Flask>=1.1.0 diff --git a/src/ipscanner.py b/src/ipscanner.py index 09775ca..a8e9453 100644 --- a/src/ipscanner.py +++ b/src/ipscanner.py @@ -3,27 +3,33 @@ import json import os from multi.scanner_thread import split_processing +try: + from utils.path_utils import get_absolute_path +except Exception: + def get_absolute_path(relative_path): + dir = os.path.dirname(os.path.abspath(__file__)) + split_path = relative_path.split("/") + return os.path.join(dir, *split_path) # Ask for input -net1 = raw_input('Enter the IP address: ') +try: + net1 = raw_input('Enter the IP address: ') +except NameError: + net1 = input('Enter the IP address: ') net2 = net1.split('.') a = '.' net3 = net2[0] + a + net2[1] + a + net2[2] + a # Print a nice banner with information on which host we are about to scan -print "-" * 60 -print "Please wait, scanning IP address....", net3+"XXX" -print "-" * 60 +print("-" * 60) +print("Please wait, scanning IP address....", net3+"XXX") +print("-" * 60) # Resolves the relative path to absolute path # [BUG]: https://github.com/vinitshahdeo/PortScanner/issues/19 -def get_absolute_path(relative_path): - dir = os.path.dirname(os.path.abspath(__file__)) - split_path = relative_path.split("/") - absolute_path = os.path.join(dir, *split_path) - return absolute_path +"""get_absolute_path now imported from utils.path_utils (fallback defined above).""" # Check what time the scan started @@ -63,11 +69,15 @@ def scan(addr): def run1(ips, range_low, range_high): - for ip in xrange(range_low, range_high): + try: + iterator = xrange + except NameError: + iterator = range + for ip in iterator(range_low, range_high): addr = net3+str(ip) # gets full address if (scan(addr)): - print addr + " is live\n" + print(addr + " is live\n") # calling function from scanner_thread.py for multithreading @@ -77,4 +87,4 @@ def run1(ips, range_low, range_high): # Calculates the difference of time, to see how long it took to run the script total = td2-td1 # Printing the information to screen -print "Scanning completed in ", total +print("Scanning completed in ", total) diff --git a/src/mainScanner.py b/src/mainScanner.py index fdc44eb..ee2a2e5 100644 --- a/src/mainScanner.py +++ b/src/mainScanner.py @@ -6,8 +6,18 @@ import json import os import threading -import __builtin__ +try: + import __builtin__ as builtins +except ImportError: + import builtins from multi.scanner_thread import split_processing +try: + from utils.path_utils import get_absolute_path +except Exception: + def get_absolute_path(relative_path): + dir = os.path.dirname(os.path.abspath(__file__)) + split_path = relative_path.split("/") + return os.path.join(dir, *split_path) import logging from flask import Flask, render_template, request, redirect, url_for @@ -37,17 +47,13 @@ def input(): return EnvironmentError # Print a nice banner with information on which host we are about to scan - print "-" * 60 - print "Please wait, scanning remote host....", remoteServerIP - print "-" * 60 + print("-" * 60) + print("Please wait, scanning remote host....", remoteServerIP) + print("-" * 60) # Resolves the relative path to absolute path # [BUG]: https://github.com/vinitshahdeo/PortScanner/issues/19 - def get_absolute_path(relative_path): - dir = os.path.dirname(os.path.abspath(__file__)) - split_path = relative_path.split("/") - absolute_path = os.path.join(dir, *split_path) - return absolute_path + # get_absolute_path now provided by shared utils (see utils/path_utils.py) # Check what time the scan started t1 = datetime.now() @@ -56,14 +62,16 @@ def get_absolute_path(relative_path): try: with open(get_absolute_path('../config.json')) as config_file: config = json.load(config_file) - print get_absolute_path('../config.json') + print(get_absolute_path('../config.json')) # defining number of threads running concurrently CONST_NUM_THREADS = int(config['thread']['count']) except IOError: - print("config.json file not found") + print("config.json file not found; falling back to thread count = 8") + CONST_NUM_THREADS = 8 except ValueError: - print("Kindly check the json file for appropriateness of range") + print("Kindly check the json file for appropriateness of range; using thread count = 8") + CONST_NUM_THREADS = 8 ports = list(range(range_low, range_high, 1)) # scanning the port only in range of (range_low, range_high) @@ -76,20 +84,20 @@ def scan(ports, range_low, range_high): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) result = sock.connect_ex((remoteServerIP, port)) if result == 0: - print "Port {}: Open".format(port) + print("Port {}:\t Open".format(port)) portnum.append("Port "+str(port)) sock.close() except KeyboardInterrupt: - print "You pressed Ctrl+C" + print("You pressed Ctrl+C") sys.exit() except socket.gaierror: - print 'Hostname could not be resolved. Exiting' + print('Hostname could not be resolved. Exiting') sys.exit() except socket.error: - print "Couldn't connect to server" + print("Couldn't connect to server") sys.exit() # calling function from scanner_thread.py for multithreading @@ -102,7 +110,7 @@ def scan(ports, range_low, range_high): total = t2 - t1 # Printing the information to screen - print 'Scanning Completed in: ', total + print('Scanning Completed in: ', total) return render_template('index.html', portnum=portnum, host=remoteServerIP, range_low=range_low, range_high=range_high, total=total) diff --git a/src/scanner.py b/src/scanner.py index 1cdf98b..903ab93 100644 --- a/src/scanner.py +++ b/src/scanner.py @@ -1,35 +1,69 @@ #!/usr/bin/env python +from __future__ import print_function +"""Multithreaded port scanner. + +Refactored to add optional CLI arguments for non-interactive usage and to +centralize path resolution logic. Backwards compatible with previous +interactive prompt behaviour. + +Author: Basuki (Hacktoberfest 2025 contribution) +""" import socket import subprocess import sys from datetime import datetime import json -import os -import threading -import __builtin__ +import argparse +try: + import __builtin__ as builtins +except ImportError: + import builtins from multi.scanner_thread import split_processing +try: + # Local util for resolving paths (new shared helper) + from utils.path_utils import get_absolute_path +except Exception: # pragma: no cover - fallback for legacy environments + import os + def get_absolute_path(relative_path): + base_dir = os.path.dirname(os.path.abspath(__file__)) + split_path = relative_path.split("/") + return os.path.join(base_dir, *split_path) + +exc = getattr(builtins, "IOError", IOError) + +# Clear the screen (best-effort; works on *nix). Silently ignore on Windows. +try: + subprocess.call('clear', shell=True) +except Exception: + pass + +parser = argparse.ArgumentParser(description="Simple multithreaded port scanner") +parser.add_argument('-H', '--host', help='Remote host to scan (e.g. localhost)') +parser.add_argument('-l', '--low', type=int, help='Override low port (inclusive)') +parser.add_argument('-u', '--high', type=int, help='Override high port (exclusive)') +parser.add_argument('-t', '--threads', type=int, help='Override number of threads') +parser.add_argument('--no-banner', action='store_true', help='Suppress banner output') +args = parser.parse_args() + +# Ask for input only if host not provided via CLI +if args.host: + remoteServer = args.host +else: + try: + remoteServer = raw_input("Enter a remote host to scan: ") + except NameError: + remoteServer = input("Enter a remote host to scan: ") -exc = getattr(__builtin__, "IOError", "FileNotFoundError") - -# Clear the screen -subprocess.call('clear', shell=True) - -# Ask for input -remoteServer = raw_input("Enter a remote host to scan: ") -remoteServerIP = socket.gethostbyname(remoteServer) - -# Print a nice banner with information on which host we are about to scan -print "-" * 60 -print "Please wait, scanning remote host....", remoteServerIP -print "-" * 60 +try: + remoteServerIP = socket.gethostbyname(remoteServer) +except socket.gaierror: + print('Hostname could not be resolved. Exiting') + sys.exit(1) -# Resolves the relative path to absolute path -# [BUG]: https://github.com/vinitshahdeo/PortScanner/issues/19 -def get_absolute_path(relative_path): - dir = os.path.dirname(os.path.abspath(__file__)) - split_path = relative_path.split("/") - absolute_path = os.path.join(dir, *split_path) - return absolute_path +if not args.no_banner: + print("-" * 60) + print("Please wait, scanning remote host....", remoteServerIP) + print("-" * 60) # Check what time the scan started t1 = datetime.now() @@ -38,50 +72,51 @@ def get_absolute_path(relative_path): try: with open(get_absolute_path('../config.json')) as config_file: config = json.load(config_file) - print get_absolute_path('../config.json') range_high = int(config['range']['high']) range_low = int(config['range']['low']) - # defining number of threads running concurrently CONST_NUM_THREADS = int(config['thread']['count']) - except IOError: - print("config.json file not found") + print("config.json file not found; using defaults 1-1024 with 8 threads") + range_low, range_high, CONST_NUM_THREADS = 1, 1024, 8 except ValueError: - print("Kindly check the json file for appropriateness of range") + print("Invalid values in config.json; using safe defaults 1-1024 with 8 threads") + range_low, range_high, CONST_NUM_THREADS = 1, 1024, 8 + +# CLI overrides +if args.low is not None: + range_low = args.low +if args.high is not None: + range_high = args.high +if args.threads is not None and args.threads > 0: + CONST_NUM_THREADS = args.threads + +if range_low >= range_high: + print('Error: low port must be < high port') + sys.exit(2) ports = list(range(range_low, range_high, 1)) -# scanning the port only in range of (range_low, range_high) -def scan(ports, range_low, range_high): +def scan(ports, start_port, end_port): try: - for port in range(range_low, range_high): + for port in range(start_port, end_port): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + sock.settimeout(0.5) result = sock.connect_ex((remoteServerIP, port)) if result == 0: - print "Port {}: Open".format(port) + print("Port {}:\t Open".format(port)) sock.close() - except KeyboardInterrupt: - print "You pressed Ctrl+C" + print("You pressed Ctrl+C") sys.exit() - - except socket.gaierror: - print 'Hostname could not be resolved. Exiting' - sys.exit() - except socket.error: - print "Couldn't connect to server" + print("Couldn't connect to server") sys.exit() - # calling function from scanner_thread.py for multithreading split_processing(ports, CONST_NUM_THREADS, scan, range_low, range_high) # Checking the time again t2 = datetime.now() - -# Calculates the difference of time, to see how long it took to run the script total = t2 - t1 +print('Scanning Completed in: ', total) -# Printing the information to screen -print 'Scanning Completed in: ', total diff --git a/src/utils/path_utils.py b/src/utils/path_utils.py new file mode 100644 index 0000000..f19b9e8 --- /dev/null +++ b/src/utils/path_utils.py @@ -0,0 +1,36 @@ +"""Utility helpers for path resolution. + +Centralizes logic for converting a relative path (using forward slashes) to +an absolute path rooted at the current file's directory. This logic was +previously duplicated across multiple scanner scripts. + +Author: Basuki (Hacktoberfest 2025 contribution) +""" + +from __future__ import absolute_import +import os + + +def get_absolute_path(relative_path): + """Return absolute path for a project-relative path. + + The existing code split relative paths using '/'. This keeps backward + compatibility while supporting Windows paths by joining components with + ``os.path.join``. + + Args: + relative_path (str): A relative path using '/' as separator. + + Returns: + str: The absolute, normalized path. + """ + base_dir = os.path.dirname(os.path.abspath(__file__)) # .../src/utils + # Move up one directory to stay consistent with prior implementations + # which anchored at the script's directory (scanner.py etc.). Those + # scripts live under src/, so we go to their parent with dirname. + src_dir = os.path.dirname(base_dir) + split_path = relative_path.split("/") + return os.path.normpath(os.path.join(src_dir, *split_path)) + + +__all__ = ["get_absolute_path"]