#!/bin/python3

"""
https://null-byte.wonderhowto.com/how-to/build-stealth-port-scanner-with-scapy-and-python-0164779/
"""

from logging import getLogger, ERROR
getLogger("scapy.runtime").setLevel(ERROR)
from scapy.all import *
import sys
from datetime import datetime
from time import strftime

SYNACK = 0x12 # code for SYN ACK
RSTACK = 0x14 # code for RST ACK
MAX_PORT = 65535
# IP_ADDRESS = None
# START_PORT = None
# STOP_PORT = None
IP_ADDRESS = "google.com"
START_PORT = "1"
STOP_PORT = "100"


def checkhost(ip):
	"""
	Check that the target is up
	param1: ip = target ip address
	"""
	# verb     : level of verbosity, from 0 (almost mute) to 3 (verbose)
	conf.verb = 0 
	try:
		pring = sr1(IP(dst=ip)/ICMP())
		print("[*] Target up")
	except Exception:
		print("\n[!] Couldn't resolve Target: {}".format(ip))
		print("\n[!] Exiting ...")
		sys.exit(1)

def syn_scan_port(target, port):
	"""
	Three way handshake -> Stealth scan
	SYN+PORT -> start connection
	SYN+ACK <- confirm start
	RST -> end of connection
	"""
	srcport = RandShort() # scapy func for generating a small num to use as a source port
	conf.verb = 0
	# send using sr1 because we expect the SYN ACK response
	# S = SYN, / = over => over TCP
	packet = IP(dst=target)/TCP(sport=srcport, dport=port, flags="S")
	# sr1 = send receive packets 
	SYNACK_pkt = sr1(packet, timeout=1)
	if SYNACK_pkt is None:
		return False
	pkt_flags = SYNACK_pkt.getlayer(TCP).flags
	RST_pkt = IP(dst=target)/TCP(sport=srcport, dport=port, flags="R")
	send(RST_pkt) # send RST, not sr1 because we don't expect a response
	# if the response was SYNACK then True
	if pkt_flags == SYNACK:
		return True
	else:
		return False

def scan_port(target=None, start_port=None, stop_port=None):

	try:
		if not target:
			target = input("[*] Enter Target IP Address: ").strip()
		if not start_port:
			start_port = input("[*] Enter Start Port Number: ").strip()
		if not stop_port:
			stop_port = input("[*] Enter Stop Port Number: ").strip()
		if int(start_port) < 1 or int(stop_port) < 0 or int(stop_port) > MAX_PORT or int(start_port) > int(stop_port):
			print("\n[!] Invalid Range of Ports\n[!] Exiting...")
			sys.exit(1)
	except KeyboardInterrupt:
		print("\n[!] User KeyboardInterrupt\n[!] Exiting...")
		sys.exit(1)

	checkhost(target) # Check target ip us up
	ports = range(int(start_port), int(stop_port) + 1)
	start_clock = datetime.now()
	print("[*] Scanning started at: {}".format(strftime("%H:%M:%S")))
	for port in ports:
		status = syn_scan_port(target, ports)
		if status:
			print("\tPort: {} Open".format(port))
	stop_clock = datetime.now()
	total_time = stop_clock - start_clock
	print("\n\n[*] SCAN PORTS FINISHED")
	print("[*] Total Scan Duration: {}".format(total_time))

if __name__ == "__main__":
	print("Let's scan some ports!")
	scan_port(IP_ADDRESS, START_PORT, STOP_PORT)
	print("\n\n[*] DONE")