diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/results.py | 52 | ||||
-rw-r--r-- | lib/scanner.py | 85 | ||||
-rw-r--r-- | lib/util.py | 4 | ||||
-rw-r--r-- | lib/worker.py | 38 |
4 files changed, 179 insertions, 0 deletions
diff --git a/lib/results.py b/lib/results.py new file mode 100644 index 0000000..1565c4b --- /dev/null +++ b/lib/results.py @@ -0,0 +1,52 @@ +from threading import Lock +from lib.util import Colored +from lib.scanner import PortState +from ch1p import telegram_notify + + +class Results: + def __init__(self): + self.warnings = [] + self.mutex = Lock() + + def add(self, worker): + host = worker.get_host() + with self.mutex: + if not worker.done: + print(f'{Colored.RED}{worker.name}: scanning failed{Colored.END}') + return + + if worker.name != host: + print(f'{worker.name} ({host}):') + else: + print(f'{host}:') + + opened = [] + results = worker.get_results() + for port, state in results: + if state != PortState.OPEN: + continue + + opened.append(port) + if not worker.is_expected(port): + self.warnings.append(f'<b>{worker.name}</b> ({host}): port {port} is open') + print(f' {Colored.RED}{port} opened{Colored.END}') + else: + print(f' {Colored.GREEN}{port} opened{Colored.END}') + + if worker.opened: + for port in worker.opened: + if port not in opened: + self.warnings.append( + f'<b>{worker.name}</b> ({host}): port {port} is NOT open') + print(f' {Colored.RED}{port} not opened{Colored.END}') + print() + + def has_warnings(self): + return len(self.warnings) > 0 + + def notify(self, chat_id=None, token=None): + text = '<b>❗️Attention!</b>\n\n' + text += '\n'.join(self.warnings) + + telegram_notify(text, parse_mode='html', chat_id=chat_id, token=token) diff --git a/lib/scanner.py b/lib/scanner.py new file mode 100644 index 0000000..f78e4f6 --- /dev/null +++ b/lib/scanner.py @@ -0,0 +1,85 @@ +import struct +import socket +import threading +import queue +import logging + +from enum import Enum, auto + +logger = logging.getLogger(__name__) + + +class PortState(Enum): + OPEN = auto() + CLOSED = auto() + FILTERED = auto() + + +class TCPScanner: + def __init__(self, host, ports, timeout=5): + self.host = host + self.ports = ports + self.timeout = timeout + self.results = [] + self.q = queue.SimpleQueue() + self.failed = False + + def scan(self, num_threads=5): + for port in self.ports: + self.q.put(port) + + threads = [] + for i in range(num_threads): + t = threading.Thread(target=self.run) + t.start() + threads.append(t) + + for t in threads: + t.join() + + def run(self): + while True: + if self.failed: + break + + try: + port = self.q.get(block=False) + except queue.Empty: + break + + try: + self._scan(port) + except Exception as e: + logger.exception(e) + self.failed = True + break + + def _scan(self, port): + try: + conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + conn.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, struct.pack("ii", 1, 0)) + conn.settimeout(self.timeout) + + ret = conn.connect_ex((self.host, port)) + + # DATA RECEIVED - SYN ACK + if ret == 0: + logger.debug('%s:%d - tcp open (SYN-ACK packet)' % (self.host, port)) + self.results.append((port, PortState.OPEN)) + + # RST RECEIVED - PORT CLOSED + elif ret == 111: + logger.debug('%s:%d - tcp closed (RST packet)' % (self.host, port)) + self.results.append((port, PortState.CLOSED)) + + # ERR CODE 11 - TIMEOUT + elif ret == 11: + self.results.append((port, PortState.FILTERED)) + + else: + logger.debug('%s:%d - code %d' % (self.host, port, ret)) + + conn.close() + + except socket.timeout: + self.results.append((port, PortState.FILTERED))
\ No newline at end of file diff --git a/lib/util.py b/lib/util.py new file mode 100644 index 0000000..c6087a7 --- /dev/null +++ b/lib/util.py @@ -0,0 +1,4 @@ +class Colored: + GREEN = '\033[92m' + RED = '\033[91m' + END = '\033[0m'
\ No newline at end of file diff --git a/lib/worker.py b/lib/worker.py new file mode 100644 index 0000000..f8d6c55 --- /dev/null +++ b/lib/worker.py @@ -0,0 +1,38 @@ +import logging + +from threading import Thread +from lib.scanner import TCPScanner + +logger = logging.getLogger(__name__) + + +class Worker(Thread): + def __init__(self, name, host, opened=None, concurrency=None, timeout=None): + Thread.__init__(self) + + assert concurrency is not None + + self.done = False + self.name = name + self.concurrency = concurrency + self.opened = opened + + scanner_kw = {} + if timeout is not None: + scanner_kw['timeout'] = timeout + self.scanner = TCPScanner(host, range(0, 65535), **scanner_kw) + + def run(self): + logger.info(f'starting {self.name} ({self.concurrency} threads)') + self.scanner.scan(num_threads=self.concurrency) + self.done = not self.scanner.failed + logger.info(f'finished {self.name}') + + def get_results(self): + return self.scanner.results + + def is_expected(self, port): + return (self.opened is not None) and (port in self.opened) + + def get_host(self): + return self.scanner.host |