ping scanner

写了一个程序,通过 ping 命令来查询地址段内的主机是否活动。
支持输入地址段格式(如 10.1.1.1-10.1.1.100)或 CIDR格式(如 10.1.1.0/28)。

CLI 版本

import subprocess
import ipaddress
import time
import platform
from typing import Optional, Callable, List, Tuple

def ping_host(ip: str, callback: Optional[Callable[[str], None]] = None) -> bool:
    """Ping a single host and return True if responsive."""
    ping_cmd = ["ping", "-n", "1", ip] if platform.system() == "Windows" else ["ping", "-c", "1", ip]

    try:
        process = subprocess.Popen(
            ping_cmd,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
        )
        stdout, stderr = process.communicate(timeout=1)
        return process.returncode == 0

    except subprocess.TimeoutExpired:
        output = f" Timeout pinging {ip}"
        if callback:
            callback(output)
        else:
            print(output)
        return False

    except Exception as e:
        output = f" Error pinging {ip}: {e}"
        if callback:
            callback(output)
        else:
            print(output)
        return False

def scan_ip_range(
    start_ip: str,
    end_ip: str,
    callback: Optional[Callable[[str], None]] = None
) -> Tuple[List[str], bool]:
    """Scan a range of IP addresses and return live hosts and success status."""
    live_hosts = []
    try:
        start = ipaddress.ip_address(start_ip)
        end = ipaddress.ip_address(end_ip)
        current_ip = start

        while current_ip <= end:
            ip_str = str(current_ip)
            output = f"\nPinging {ip_str}..."
            if callback:
                callback(output)
            else:
                print(output, end="")

            if ping_host(ip_str, callback):
                live_hosts.append(ip_str)
                output = " Live!"
                if callback:
                    callback(output)
                else:
                    print(output, end="")

            current_ip += 1
            time.sleep(0.1)

        return live_hosts, True

    except ValueError as e:
        output = f"Invalid IP address: {e}\n"
        if callback:
            callback(output)
        else:
            print(output)
        return [], False

def scan_cidr_network(
    network: str,
    callback: Optional[Callable[[str], None]] = None
) -> Tuple[List[str], bool]:
    """Scan a network in CIDR notation and return live hosts and success status."""
    live_hosts = []
    try:
        net = ipaddress.ip_network(network, strict=False)
        for ip in net.hosts():
            ip_str = str(ip)
            output = f"\nPinging {ip_str}..."
            if callback:
                callback(output)
            else:
                print(output, end="")

            if ping_host(ip_str, callback):
                live_hosts.append(ip_str)
                output = " Live!"
                if callback:
                    callback(output)
                else:
                    print(output, end="")

            time.sleep(0.1)

        return live_hosts, True

    except ValueError as e:
        output = f"Invalid network address: {e}"
        if callback:
            callback(output)
        else:
            print(output)
        return [], False

def parse_input(input_str: str) -> Tuple[str, Optional[str], Optional[str]]:
    """Parse input string and determine input type (range, cidr, or invalid)."""
    input_str = input_str.strip()

    if "-" in input_str:
        try:
            start_ip, end_ip = input_str.split("-")
            ipaddress.ip_address(start_ip.strip())
            ipaddress.ip_address(end_ip.strip())
            return "range", start_ip.strip(), end_ip.strip()
        except ValueError:
            return "invalid", None, None

    elif "/" in input_str:
        try:
            ipaddress.ip_network(input_str)
            return "cidr", input_str, None
        except ValueError:
            return "invalid", None, None

    return "invalid", None, None

def generate_report(live_hosts: List[str]) -> str:
    """Generate and save a report of live hosts."""
    report = "\nLive Hosts:\n" + "\n".join(live_hosts)

    with open("scan_report.txt", "w") as f:
        f.write(report)

    return "\nReport saved to scan_report.txt"

def main():
    """Main function to run the scanner."""
    input_str = input(
        "Enter IP range (e.g., 10.1.1.1-10.1.1.100) or CIDR (e.g., 10.1.1.0/28): "
    )
    input_type, value1, value2 = parse_input(input_str)

    if input_type == "range":
        live_hosts, success = scan_ip_range(value1, value2)
    elif input_type == "cidr":
        live_hosts, success = scan_cidr_network(value1)
    else:
        print("Invalid input format.")
        live_hosts = []
        success = False

    if success:
        if live_hosts:
            print("\n\nLive Hosts Found:")
            for host in live_hosts:
                print(host)
        else:
            print("\nNo live hosts found.")

        report = generate_report(live_hosts)
        print(report)

if __name__ == "__main__":
    main()

GUI 版本

import tkinter as tk
import tkinter.scrolledtext as st
from ping_scanner import parse_input, scan_ip_range, scan_cidr_network, generate_report

class PingScannerGUI:
    def __init__(self, master):
        self.master = master
        master.title("Ping Scanner")

        self.label = tk.Label(
            master,
            text="Enter the IP address range (e.g., 10.1.1.1-10.1.1.100)\n   or CIDR notation (e.g., 10.1.1.0/28)",
        )
        self.label.pack()

        self.entry = tk.Entry(master, width=50)
        self.entry.pack()

        self.scan_button = tk.Button(
            master,
            text="Scan",
            command=self.scan,
            activebackground="lightgray",
            activeforeground="gray",
        )
        self.scan_button.pack()

        self.result_area = st.ScrolledText(master, width=50, height=20)
        self.result_area.pack()
        self.result_area.config(state=tk.DISABLED)

    def update_results(self, text):
        self.result_area.config(state=tk.NORMAL)
        self.result_area.insert(tk.END, text)
        self.result_area.config(state=tk.DISABLED)
        self.result_area.see(tk.END)  # Scroll to the end

    def scan(self):
        self.scan_button.config(
            state=tk.DISABLED
        )  # Disable the button and change color
        self.result_area.config(state=tk.NORMAL)
        self.result_area.delete(1.0, tk.END)  # Clear previous results
        self.result_area.config(state=tk.DISABLED)

        input_str = self.entry.get()
        input_type, value1, value2 = parse_input(input_str)

        if input_type == "range":
            live_hosts, success = scan_ip_range(value1, value2, callback=self.update_results)
        elif input_type == "cidr":
            live_hosts, success = scan_cidr_network(value1, callback=self.update_results)
        else:
            self.update_results("Invalid input format.\n")
            self.scan_button.config(state=tk.NORMAL)
            return

        if success and live_hosts:
            self.update_results("\n\nLive Hosts:\n")
            for host in live_hosts:
                self.update_results(host + "\n")
        elif success:
            self.update_results("No live hosts found in the specified range.\n")
        else:
            self.update_results("Error occurred during scanning.\n")

        report = generate_report(live_hosts)  # Still generate report
        self.update_results(report)
        self.scan_button.config(state=tk.NORMAL)  # Re-enable the button

root = tk.Tk()
gui = PingScannerGUI(root)
root.mainloop()

留下评论

您的邮箱地址不会被公开。 必填项已用 * 标注