From 584c419b0e28ea2368494fd304e4e0e30c3afae0 Mon Sep 17 00:00:00 2001 From: Dabbler10 Date: Mon, 16 Dec 2024 22:41:30 +0500 Subject: [PATCH 1/3] add extra --- main.py | 26 +++++++++++++------------- tcpping.py | 11 ++++++++--- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/main.py b/main.py index accd026..cbfb612 100644 --- a/main.py +++ b/main.py @@ -1,18 +1,18 @@ import argparse +import click from tcpping import TcpPing -def parse_args() -> argparse.Namespace: - parser = argparse.ArgumentParser(description="TCP Ping") - parser.add_argument("host", help="Host") - parser.add_argument("port", type=int, default=80, help="Port") - parser.add_argument("-c", "--count", type=int, default=0, help="Number of pings to send (default infinity)") - parser.add_argument("-t", "--timeout", type=float, default=2.0, help="Timeout for each ping in seconds (default 2)") - parser.add_argument("-i", "--interval", type=float, default=1.0, help="Interval between pings in seconds (default 1)") - return parser.parse_args() +@click.command() +@click.argument('host', type=str) +@click.argument('port', type=int) +@click.option('-c', '--count', default=0, type=int, help='Number of pings to send (default: 0, infinite).') +@click.option('-t', '--timeout', default=2.0, type=float, help='Timeout for each ping in seconds (default: 2).') +@click.option('-i', '--interval', default=1.0, type=float, help='Interval between pings in seconds (default: 1).') +@click.option('--http', is_flag=True, help='Use HTTP.') +def tcping(host: str, port: int, count: int, timeout: float, interval: float, http: bool): + tcp_ping = TcpPing(host, port, count, timeout, interval, http) + tcp_ping.run() - -if __name__ == "__main__": - args = parse_args() - tcp_ping = TcpPing(args.host, args.port, args.count, args.timeout, args.interval) - tcp_ping.run() \ No newline at end of file +if __name__ == '__main__': + tcping() diff --git a/tcpping.py b/tcpping.py index 22eaa23..23d43c5 100644 --- a/tcpping.py +++ b/tcpping.py @@ -3,18 +3,21 @@ from scapy.all import IP, TCP, sr1 class TcpPing: - def __init__(self, host: str, port: int, count: int, timeout: float, interval: float): + def __init__(self, host: str, port: int, count: int, timeout: float, interval: float, http: bool): self.host = host self.port = port self.count = count self.timeout = timeout self.interval = interval + self.http = http self.sent_packets = 0 self.received_packets = 0 self.response_times = [] def send_ping(self) -> float | None: - packet = IP(dst=self.host) / TCP(dport=self.port, flags="S") + packet = (IPv6(dst=self.host) if ":" in self.host else IP(dst=self.host)) / TCP(dport=self.port, flags="S") + if self.http: + packet = packet / ("GET / HTTP/1.1\r\nHost: " + self.host + "\r\n\r\n") start_time = time.time() response = sr1(packet, timeout=self.timeout, verbose=0) elapsed_time = (time.time() - start_time) * 1000 @@ -23,7 +26,9 @@ def send_ping(self) -> float | None: if response and response.haslayer(TCP) and response.getlayer(TCP).flags == 0x12: self.received_packets += 1 self.response_times.append(elapsed_time) - rst_packet = IP(dst=self.host) / TCP(dport=self.port, flags="R") + rst_packet = (IPv6(dst=self.host) if ":" in self.host else IP(dst=self.host)) / TCP(dport=self.port, flags="R") + if self.http: + rst_packet = rst_packet / ("GET / HTTP/1.1\r\nHost: " + self.host + "\r\n\r\n") sr1(rst_packet, timeout=self.timeout, verbose=0) return elapsed_time else: From dcff4b0876a02fc9c34761f0ec1acfd107a4a65d Mon Sep 17 00:00:00 2001 From: Dabbler10 Date: Wed, 18 Dec 2024 01:29:12 +0500 Subject: [PATCH 2/3] add threadpool --- .../inspectionProfiles/profiles_settings.xml | 6 + .idea/misc.xml | 7 ++ .idea/vcs.xml | 6 + .idea/workspace.xml | 71 ++++++++++++ main.py | 6 +- tcpping.py | 107 +++++++++++------- 6 files changed, 160 insertions(+), 43 deletions(-) create mode 100644 .idea/inspectionProfiles/profiles_settings.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/vcs.xml create mode 100644 .idea/workspace.xml diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..44d4ca8 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 0000000..5f23a0a --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,71 @@ + + + + + + + + + + + + + { + "customColor": "", + "associatedIndex": 7 +} + + + + + + + + + + + + + + + + + + + + 1734364356738 + + + + + + \ No newline at end of file diff --git a/main.py b/main.py index cbfb612..c9f4cfc 100644 --- a/main.py +++ b/main.py @@ -4,14 +4,14 @@ @click.command() -@click.argument('host', type=str) +@click.argument('hosts', type=str, nargs=-1) @click.argument('port', type=int) @click.option('-c', '--count', default=0, type=int, help='Number of pings to send (default: 0, infinite).') @click.option('-t', '--timeout', default=2.0, type=float, help='Timeout for each ping in seconds (default: 2).') @click.option('-i', '--interval', default=1.0, type=float, help='Interval between pings in seconds (default: 1).') @click.option('--http', is_flag=True, help='Use HTTP.') -def tcping(host: str, port: int, count: int, timeout: float, interval: float, http: bool): - tcp_ping = TcpPing(host, port, count, timeout, interval, http) +def tcping(hosts: str, port: int, count: int, timeout: float, interval: float, http: bool): + tcp_ping = TcpPing(hosts, port, count, timeout, interval, http) tcp_ping.run() if __name__ == '__main__': diff --git a/tcpping.py b/tcpping.py index 23d43c5..87a6bb6 100644 --- a/tcpping.py +++ b/tcpping.py @@ -1,63 +1,90 @@ import sys +import threading import time +from concurrent.futures import ThreadPoolExecutor, as_completed + from scapy.all import IP, TCP, sr1 class TcpPing: - def __init__(self, host: str, port: int, count: int, timeout: float, interval: float, http: bool): - self.host = host - self.port = port - self.count = count - self.timeout = timeout - self.interval = interval - self.http = http - self.sent_packets = 0 - self.received_packets = 0 - self.response_times = [] - - def send_ping(self) -> float | None: - packet = (IPv6(dst=self.host) if ":" in self.host else IP(dst=self.host)) / TCP(dport=self.port, flags="S") - if self.http: - packet = packet / ("GET / HTTP/1.1\r\nHost: " + self.host + "\r\n\r\n") + def __init__(self, hosts: str, port: int, count: int, timeout: float, interval: float, http: bool): + self._hosts = hosts + self._port = port + self._count = count + self._timeout = timeout + self._interval = interval + self._http = http + self._sent_packets = 0 + self._received_packets = 0 + self._response_times = [] + self._stop_event = threading.Event() + + def _send_ping(self, host: str) -> float | None: + packet = (IPv6(dst=host) if ":" in host else IP(dst=host)) / TCP(dport=self._port, flags="S") + if self._http: + packet = packet / ("GET / HTTP/1.1\r\nHost: " + host + "\r\n\r\n") start_time = time.time() - response = sr1(packet, timeout=self.timeout, verbose=0) + response = sr1(packet, timeout=self._timeout, verbose=0) elapsed_time = (time.time() - start_time) * 1000 - self.sent_packets += 1 + self._sent_packets += 1 if response and response.haslayer(TCP) and response.getlayer(TCP).flags == 0x12: - self.received_packets += 1 - self.response_times.append(elapsed_time) - rst_packet = (IPv6(dst=self.host) if ":" in self.host else IP(dst=self.host)) / TCP(dport=self.port, flags="R") - if self.http: - rst_packet = rst_packet / ("GET / HTTP/1.1\r\nHost: " + self.host + "\r\n\r\n") - sr1(rst_packet, timeout=self.timeout, verbose=0) + self._received_packets += 1 + self._response_times.append(elapsed_time) + rst_packet = (IPv6(dst=host) if ":" in host else IP(dst=host)) / TCP(dport=self._port, flags="R") + if self._http: + rst_packet = rst_packet / ("GET / HTTP/1.1\r\nHost: " + host + "\r\n\r\n") + sr1(rst_packet, timeout=self._timeout, verbose=0) return elapsed_time else: return None - def print_statistics(self) -> None: + def _print_statistics(self) -> None: print("\n--- TCPing statistics ---") - print(f"Sent packets: {self.sent_packets}") - print(f"Received packets: {self.received_packets}") - loss = ((self.sent_packets - self.received_packets) / self.sent_packets) * 100 if self.sent_packets > 0 else 0 + print(f"Sent packets: {self._sent_packets}") + print(f"Received packets: {self._received_packets}") + loss = ((self._sent_packets - self._received_packets) / self._sent_packets) * 100 if self._sent_packets > 0 else 0 print(f"Packet loss: {loss:.2f}%") - if self.received_packets > 0: - print(f"Min response time: {min(self.response_times):.2f} ms") - print(f"Max response time: {max(self.response_times):.2f} ms") - print(f"Average response time: {sum(self.response_times) / len(self.response_times):.2f} ms") + if self._received_packets > 0: + print(f"Min response time: {min(self._response_times):.2f} ms") + print(f"Max response time: {max(self._response_times):.2f} ms") + print(f"Average response time: {sum(self._response_times) / len(self._response_times):.2f} ms") else: print("No responses received.") - def run(self) -> None: + def _ping(self, host) -> None: try: count = 0 - while self.count == 0 or count < self.count: - response_time = self.send_ping() + while (self._count == 0 or count < self._count) and not self._stop_event.is_set(): + response_time = self._send_ping(host) if response_time is not None: - print(f"Reply from {self.host}:{self.port}, time={response_time:.2f} ms") + print(f"Reply from {host}:{self._port}, time={response_time:.2f} ms") else: - print(f"Request to {self.host}:{self.port} timed out.") + print(f"Request to {host}:{self._port} timed out.") count += 1 - time.sleep(self.interval) - finally: - self.print_statistics() - sys.exit(0) \ No newline at end of file + time.sleep(self._interval) + + except KeyboardInterrupt: + pass + + + def run(self) -> None: + with ThreadPoolExecutor(max_workers=8) as executor: + try: + futures = { + executor.submit(self._ping, host): host + for host in self._hosts + } + + while not self._stop_event.is_set(): + for future in futures: + try: + future.result(timeout=0.5) + except TimeoutError: + pass + + finally: + self._stop_event.set() + executor.shutdown(wait=True) + self._print_statistics() + sys.exit(0) + From 1e0bc35346c1b7d7d1648fd99f63e2cd13804e6e Mon Sep 17 00:00:00 2001 From: Dabbler10 Date: Thu, 19 Dec 2024 18:20:38 +0500 Subject: [PATCH 3/3] fix --- tcpping.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tcpping.py b/tcpping.py index 87a6bb6..0a0f49d 100644 --- a/tcpping.py +++ b/tcpping.py @@ -75,7 +75,7 @@ def run(self) -> None: for host in self._hosts } - while not self._stop_event.is_set(): + while not self._stop_event.is_set() and self._sent_packets <= self._count * 2: for future in futures: try: future.result(timeout=0.5)