-
Notifications
You must be signed in to change notification settings - Fork 0
/
lib_local_discovery.py
263 lines (240 loc) · 8.98 KB
/
lib_local_discovery.py
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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
import sys
sys.dont_write_bytecode = True # No '*.pyc' precompiled files
from rich import print
import re
from time import ctime
from socket import gaierror, gethostbyname, gethostbyaddr
from netaddr import IPAddress
from scapy.all import ICMP, IP, sr, srp, TCP, Ether, ARP
from lib_lan import validate_subnet, validate_ip
from lib_main import combine_dicts
from lib_parameters import default_hostnames
from lib_main import ListOps
from lib_local_various import showvar
import ipaddress
import lib_local_ifaces
import socket
class Discover:
'''
Having the given local LAN settings, now the hosts will be discovered,
according to given criteria.
(Eg. mac_start = 'b827' means that Raspberry Pi boxes will be discovered.
TODO
ip_se to improve.
Should be clearer with calling for ip and mask.
'''
def __init__(self, host='', mac_start=''):
pass
self.ip_settings = self.ip_setts()
# self.mac_start = mac_start,
def host_passed(self, mac_addr, ip_addr, mac_start=''):
''' Auxiliary function.
Filters out results. Passes further, only under the following conditions:
- mac_start == '' which means no filtering on MAC address
or
- mac_addr starts with mac_start
&
- ip_addr is not same as the one of the current host
Returns:
passed (bool): If True, the host is passed further
'''
passed = False
sanitized_mac = re.sub('[^0-9A-Fa-f]', '', mac_addr)
if (
(mac_start == '' or sanitized_mac.lower().startswith(mac_start))
and
(ip_addr != self.ip_settings['ip'])
):
passed = True
return passed
def get_ip(self, host, verbose=False):
''' Gets IP address from the input hostname.
Args:
host (str): hostname, that IP you look for.
Return:
ip (str/None): IP of resolved host.
'''
ip = None
try:
ip = gethostbyname(host)
except gaierror as err:
pass
if verbose is True:
print('The IP of the host:', host, ' not found.')# Error code:', err)
return ip
def get_hostname(self, host, verbose=True):
''' Gets hostname from the input IP address.
Args:
host (str): IP address, that hostname you look for
Return:
hostname (str/None): hostname, that you look for
TODO test: ?change initial hostname to None?
'''
hostname = ''
try:
get_host = gethostbyaddr(host)
hostname = get_host[0].lower()
if '.' not in hostname:
hostname = ''.join([hostname, '.local'])
except socket.herror as err:
pass
if verbose is True:
print('The hostname of the IP:', host, ' not found')
return hostname
def current_subnet(self):
''' Define current subnet in the IP/CIDR form.
'''
result_subnet = None
if self.ip_settings is not None:
result_subnet = str(
ipaddress.IPv4Network(
self.ip_settings['ip'] + '/' +
self.ip_settings['netmask'],
strict=False))
return result_subnet
def ip_setts(self):
'''
Initial evaluation.
Check if an active and valid network interface exists on your PC.
'''
result_settings = None
input_settings = lib_local_ifaces.IpSettings().final_iface()
if (
len(input_settings) > 0
and
'ip' in input_settings
and
'netmask' in input_settings
):
if 1 == 1:
result_settings = {
'ip': input_settings['ip'],
'netmask': input_settings['netmask'],
}
else:
print('No valid connection found. No scanning will be performed.')
return result_settings
def eval_input(self, host=''):
'''
Check whether IP, subnet or hostname was entered.
ARGS:
host (str): The data, that type is to be verified.
RETURNS:
evaled_host:
'''
evaled_host = None
current_sn = self.current_subnet()
if current_sn is not None:
if host == '':
evaled_host = current_sn
elif validate_subnet(host) is True: # means IP or subnet
evaled_host = host
# hostname to resolve
elif isinstance(host, str) is True:
evaled_host = self.get_ip(host) # Also None may be returned here.
return evaled_host
def arpscan(self, host='', mac_start='', verbose=False):
'''
ARGS:
host (str): host, IP or subnet to be scanned.
default value = '' means,
the entire current subnet will be scanned.
mac_start (str): filter on mac addr.
Returns:
arp_scan_results (list of dicts):
One element contains info about a host gathered during the scan.
The keys are:
'ip_addr'
'mac_addr'
'ssh_open': False - Not relevant yet,
just to build the proper dict structure.
'hostname'
Relevant if the scanned host sends out mDNS info.
If a single host was scanned,
only an empty or a single element list can be returned.
'''
arp_scan_results = []
hostname = ''
host_after_eval = self.eval_input(host)
if host_after_eval is not None: # valid ip settings
if verbose is True:
print('ARP-scanning host:', host_after_eval)
request = Ether(dst='ff:ff:ff:ff:ff:ff') / ARP(pdst=host_after_eval)
responses, _ = srp(request, timeout=1, retry=1, verbose=verbose)
if len(responses) > 0:
print(
'Here is the list of all local hosts discovered on your LAN.\n'
'Please keep in mind!\n'
'Only the ones matching your criteria will be added to the final results.\n'
'All discovered hosts:'
)
for response in responses:
ip_addr, mac_addr = response[1].psrc, response[1].hwsrc
sanitized_mac = re.sub('[^0-9A-Fa-f]', '', mac_addr)
result = {
'ip_addr' : ip_addr,
'mac_addr': mac_addr,
'ssh_open': None,
'hostname': hostname
}
print(
'->IP:', ip_addr,
'\t->MAC address:', mac_addr,
'\t->Discovered hostname:', hostname,
)
if self.host_passed(
mac_addr=mac_addr,
ip_addr=ip_addr,
mac_start=mac_start
) is True:
if result not in arp_scan_results:
arp_scan_results.append(result)
else:
pass
return arp_scan_results
def tcpscan(self, host, dport=22, verbose=False):
'''
Uses scapy to test TCP connection,
for port 22 (ssh)
'''
result = False
if verbose is True:
print('TCP-scanning host:', host, 'on port:', dport)
try:
responses, _ = sr(
IP(dst=host)/TCP(dport=dport, flags="S"),
timeout=1, verbose=verbose
)
if len(responses) > 0:
result = True
except socket.gaierror as err:
print('Error, hostname not found:', err)
return result
def fullscan(self, host='', mac_start='', resolve=True):
'''
Performs ARP scan first on a selected host or subnet.
Then performs SSH scans but only for hosts
returned from the previous scan.
ARGS:
hosts (dict): See arpscan.
mac_start (str): See arpscan.
resolve (bool): decides if program should try to resolve IP to hostname
Returns:
hosts (list of dicts):
One element contains info about an active host,
that responds to ssh.
'''
result_hosts = self.arpscan(host, mac_start=mac_start)
if len(result_hosts) > 0:
# if result_hosts is not None:
for host in result_hosts:
host['ssh_open'] = self.tcpscan(host['ip_addr'])
for host in result_hosts:
if resolve is True:
hostname = self.get_hostname(host['ip_addr'])
host['hostname'] = hostname
return result_hosts
def main():
print('Library for discovery of hosts.')
if __name__ == '__main__':
main()