4
4
This script performs a network scan on a given target or subnet.
5
5
It checks if the target hosts are alive, and if ports 80 (HTTP) and 443 (HTTPS) are open, and optionally performs reverse DNS lookups if specified.
6
6
7
- Params
8
- --hostname
7
+ Params:
8
+ --hostname Perform reverse DNS lookup
9
+ --mac Include MAC address in output
9
10
10
11
v1.1 2/2024 silversword411
11
12
v1.4 added open port checker
12
- v1.5 5/2/2024 integrated reverse DNS lookup into the ping function with 1-second timeout
13
- v1.6 5/31/2024 align output to columns and ports low to high
14
- v1.7 2/18/2025 fix columns with long host names and added response time
13
+ v1.5 5/2/2024 silversword411 integrated reverse DNS lookup into the ping function with 1-second timeout
14
+ v1.6 5/31/2024 silversword411 align output to columns and ports low to high
15
+ v1.7 2/18/2025 silversword411 fix columns with long host names and added response time
16
+ v1.8 5/21/2025 silversword411 added MAC address lookup with --mac option
15
17
16
18
TODO: Make subnet get automatically detected
17
19
TODO: run on linux as well
26
28
import argparse
27
29
28
30
29
- # Function to get the IP address of the primary network interface
30
31
def get_host_ip ():
31
32
s = socket .socket (socket .AF_INET , socket .SOCK_DGRAM )
32
33
try :
@@ -39,7 +40,6 @@ def get_host_ip():
39
40
return IP
40
41
41
42
42
- # Function to ping an IP address, check if it is alive, measure response time, and optionally perform a reverse DNS lookup
43
43
def ping_ip (ip , alive_hosts , do_reverse_dns ):
44
44
try :
45
45
output = subprocess .check_output (
@@ -50,9 +50,7 @@ def ping_ip(ip, alive_hosts, do_reverse_dns):
50
50
if "Reply from" in output :
51
51
alive_ip = ipaddress .ip_address (ip )
52
52
response_time = re .search (r"time[=<]\s*(\d+)ms" , output )
53
- response_time = (
54
- int (response_time .group (1 )) if response_time else - 1
55
- ) # If no time found, use -1
53
+ response_time = int (response_time .group (1 )) if response_time else - 1
56
54
57
55
hostname = "NA"
58
56
if do_reverse_dns :
@@ -65,12 +63,22 @@ def ping_ip(ip, alive_hosts, do_reverse_dns):
65
63
finally :
66
64
s .close ()
67
65
68
- alive_hosts .append ((alive_ip , hostname , response_time ))
66
+ alive_hosts .append (
67
+ (alive_ip , hostname , response_time , "" )
68
+ ) # Placeholder for MAC
69
69
except Exception :
70
70
pass
71
71
72
72
73
- # Function to check for open ports
73
+ def get_mac_address (ip ):
74
+ try :
75
+ output = subprocess .check_output (["arp" , "-a" , ip ], universal_newlines = True )
76
+ match = re .search (r"(\w{2}-\w{2}-\w{2}-\w{2}-\w{2}-\w{2})" , output )
77
+ return match .group (1 ) if match else "N/A"
78
+ except Exception :
79
+ return "N/A"
80
+
81
+
74
82
def check_ports (ip , port , open_ports ):
75
83
try :
76
84
with socket .socket (socket .AF_INET , socket .SOCK_STREAM ) as s :
@@ -81,18 +89,19 @@ def check_ports(ip, port, open_ports):
81
89
pass
82
90
83
91
84
- # Parse command-line arguments
85
92
def parse_arguments ():
86
93
parser = argparse .ArgumentParser (
87
- description = "Scan network subnet for alive hosts, open ports, and optionally perform reverse DNS lookup ."
94
+ description = "Scan network subnet for alive hosts, open ports, and optionally perform reverse DNS or get MAC address ."
88
95
)
89
96
parser .add_argument (
90
97
"--hostname" , help = "Perform reverse DNS lookup" , action = "store_true"
91
98
)
99
+ parser .add_argument (
100
+ "--mac" , help = "Include MAC address in output" , action = "store_true"
101
+ )
92
102
return parser .parse_args ()
93
103
94
104
95
- # Main function to detect the subnet and scan it
96
105
def main ():
97
106
args = parse_arguments ()
98
107
host_ip = get_host_ip ()
@@ -111,12 +120,15 @@ def main():
111
120
for t in threads :
112
121
t .join ()
113
122
114
- # Sort the alive hosts numerically
123
+ if args .mac :
124
+ for i , (ip , hostname , response_time , _ ) in enumerate (alive_hosts ):
125
+ mac = get_mac_address (str (ip ))
126
+ alive_hosts [i ] = (ip , hostname , response_time , mac )
127
+
115
128
alive_hosts .sort (key = lambda x : x [0 ])
116
129
117
- # Launch port checks
118
130
port_check_threads = []
119
- for host , _ , _ in alive_hosts :
131
+ for host , _ , _ , _ in alive_hosts :
120
132
for port in [22 , 23 , 25 , 80 , 443 , 2525 , 8443 , 10443 , 10000 , 20000 ]:
121
133
t = threading .Thread (target = check_ports , args = (str (host ), port , open_ports ))
122
134
t .start ()
@@ -125,28 +137,31 @@ def main():
125
137
for t in port_check_threads :
126
138
t .join ()
127
139
128
- # Determine column widths dynamically
129
140
max_hostname_length = max (
130
- (len (hostname ) for _ , hostname , _ in alive_hosts ), default = 8
141
+ (len (hostname ) for _ , hostname , _ , _ in alive_hosts ), default = 8
131
142
)
132
143
ip_column_width = 16
133
- hostname_column_width = max (max_hostname_length , 12 ) + 2 # Minimum width of 12
144
+ hostname_column_width = max (max_hostname_length , 12 ) + 2
134
145
response_time_column_width = 8
135
- ports_column_width = 50 # Static width for ports
146
+ mac_column_width = 20 if args .mac else 0
147
+ ports_column_width = 50
136
148
137
- # Print header
138
- header = f"{ 'IP' :<{ip_column_width }} { '(ms)' :<{response_time_column_width }} { 'Hostname' :<{hostname_column_width }} { 'Open Ports' :<{ports_column_width }} "
149
+ header = f"{ 'IP' :<{ip_column_width }} { '(ms)' :<{response_time_column_width }} { 'Hostname' :<{hostname_column_width }} "
150
+ if args .mac :
151
+ header += f"{ 'MAC Address' :<{mac_column_width }} "
152
+ header += f"{ 'Open Ports' :<{ports_column_width }} "
139
153
print (header )
140
154
print ("-" * len (header ))
141
155
142
- # Print results
143
- for host , hostname , response_time in alive_hosts :
156
+ for host , hostname , response_time , mac in alive_hosts :
144
157
ports = sorted (open_ports [str (host )])
145
158
ports_str = ", " .join (map (str , ports ))
146
159
response_time_str = f"{ response_time } ms" if response_time >= 0 else "N/A"
147
- print (
148
- f"{ str (host ):<{ip_column_width }} { response_time_str :<{response_time_column_width }} { hostname :<{hostname_column_width }} { ports_str :<{ports_column_width }} "
149
- )
160
+ line = f"{ str (host ):<{ip_column_width }} { response_time_str :<{response_time_column_width }} { hostname :<{hostname_column_width }} "
161
+ if args .mac :
162
+ line += f"{ mac :<{mac_column_width }} "
163
+ line += f"{ ports_str :<{ports_column_width }} "
164
+ print (line )
150
165
151
166
print (f"\n Total count of alive hosts: { len (alive_hosts )} " )
152
167
0 commit comments