-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.py
More file actions
107 lines (87 loc) · 3.62 KB
/
Copy pathmain.py
File metadata and controls
107 lines (87 loc) · 3.62 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
from textual.app import App, ComposeResult
from textual.containers import Container, Horizontal, Vertical
from textual.widgets import Header, Footer, Input, Button, DataTable, Static, Label
from textual.worker import Worker
from scanner import Scanner
import asyncio
class IPv4ScannerApp(App):
CSS = """
Screen {
layout: vertical;
}
.input-container {
height: auto;
margin: 1;
padding: 1;
border: solid green;
}
.input-label {
padding: 1;
}
#scan-btn {
margin: 1;
width: 100%;
}
DataTable {
height: 1fr;
border: solid blue;
}
"""
def compose(self) -> ComposeResult:
yield Header()
yield Container(
Vertical(
Label("Target IPs (comma separated):", classes="input-label"),
Input(placeholder="e.g. 127.0.0.1, 192.168.1.1", id="ip-input"),
Label("Target Ports (comma separated or range):", classes="input-label"),
Input(placeholder="e.g. 80, 443, 8000-8010", value="80", id="port-input"),
Button("Start Scan", id="scan-btn", variant="primary"),
classes="input-container"
)
)
yield DataTable()
yield Footer()
def on_mount(self) -> None:
table = self.query_one(DataTable)
table.add_columns("IP Address", "Port", "Status")
self.scanner = Scanner()
async def on_button_pressed(self, event: Button.Pressed) -> None:
if event.button.id == "scan-btn":
ip_input = self.query_one("#ip-input", Input).value
port_input = self.query_one("#port-input", Input).value
if not ip_input:
self.notify("Please enter IP addresses.", severity="error")
return
ips = self.scanner.parse_ips(ip_input)
ports = self.scanner.parse_ports(port_input)
if not ips:
self.notify("No valid IPs found.", severity="error")
return
if not ports:
self.notify("No valid ports found.", severity="error")
return
self.query_one(DataTable).clear()
self.run_worker(self.perform_scan(ips, ports), exclusive=True)
async def perform_scan(self, ips, ports):
table = self.query_one(DataTable)
self.notify(f"Scanning {len(ips)} IPs on {len(ports)} ports...")
# We can scan in chunks or all at once. For TUI responsiveness,
# let's process them and update table as we go, or just wait for all.
# To make it update live, we should probably not use the bulk scan_range
# but iterate here.
for ip in ips:
for port in ports:
# Update status to "Scanning..."
# table.add_row(ip, str(port), "Scanning...")
# Actually, let's just await the result
result = await self.scanner.scan_port(ip, port, timeout=0.5)
# result is (ip, port, is_open)
status = "[green]OPEN[/green]" if result[2] else "[red]CLOSED[/red]"
# Only show open ports? Or all? Let's show all for now, or maybe just open.
# Usually scanners show open ports. But user might want to know it checked.
# Let's show all but maybe sort or filter later.
table.add_row(result[0], str(result[1]), status)
self.notify("Scan complete.")
if __name__ == "__main__":
app = IPv4ScannerApp()
app.run()