1
0
mirror of https://github.com/kellyjonbrazil/jc.git synced 2025-06-17 00:07:37 +02:00
Files
jc/jc/parsers/netstat_freebsd_osx.py
Kelly Brazil 1d0ad2f045 doc fixes
2021-01-04 18:01:16 -08:00

321 lines
10 KiB
Python

"""jc - JSON CLI output utility FreeBSD and OSX netstat Parser"""
def normalize_headers(header):
header = header.lower()
header = header.replace('local address', 'local_address')
header = header.replace('foreign address', 'foreign_address')
header = header.replace('(state)', 'state')
header = header.replace('inode', 'unix_inode')
header = header.replace('flags', 'unix_flags')
header = header.replace('-', '_')
return header
def normalize_route_headers(header):
header = header.lower()
header = header.replace('flags', 'route_flags')
header = header.replace('refs', 'route_refs')
header = header.replace('netif', 'iface')
header = header.replace('-', '_')
return header
def normalize_interface_headers(header):
header = header.lower()
header = header.replace('name', 'iface')
header = header.replace('-', '_')
return header
def parse_item(headers, entry, kind):
entry = entry.split(maxsplit=len(headers) - 1)
# fixup udp records with no state field entry
if kind == 'network' and entry[0].startswith('udp'):
entry.insert(5, None)
if kind == 'network' and 'socket' in headers and 'udp' in str(entry):
entry.insert(7, None)
# fixup -T output on FreeBSD
if kind == 'network' and '0_win' in headers and entry[0].startswith('udp'):
entry.insert(1, '')
entry.insert(1, '')
entry.insert(1, '')
# fixup interface records with no address field entry
if kind == 'interface' and len(entry) == 8:
entry.insert(3, None)
output_line = dict(zip(headers, entry))
output_line['kind'] = kind
return output_line
def parse_post(raw_data):
for entry in raw_data:
# fixup name field in Registered kernel control module
if 'name' in entry:
if entry['name']:
entry['name'] = entry['name'].strip()
# create network and transport protocol fields
if 'local_address' in entry:
if entry['local_address']:
ladd = entry['local_address'].rsplit('.', maxsplit=1)[0]
lport = entry['local_address'].rsplit('.', maxsplit=1)[1]
entry['local_address'] = ladd
entry['local_port'] = lport
if 'foreign_address' in entry:
if entry['foreign_address']:
fadd = entry['foreign_address'].rsplit('.', maxsplit=1)[0]
fport = entry['foreign_address'].rsplit('.', maxsplit=1)[1]
entry['foreign_address'] = fadd
entry['foreign_port'] = fport
if 'proto' in entry and 'kind' in entry:
if entry['kind'] == 'network':
if entry['proto'] == 'udp46':
entry['transport_protocol'] = entry['proto'][:-2]
elif entry['proto'].startswith('icm'):
entry['transport_protocol'] = 'icmp'
else:
entry['transport_protocol'] = entry['proto'][:-1]
if '6' in entry['proto']:
entry['network_protocol'] = 'ipv6'
else:
entry['network_protocol'] = 'ipv4'
# add route_flags_pretty field
if 'route_flags' in entry:
flag_map = {
'1': 'PROTO1',
'2': 'PROTO2',
'3': 'PROTO3',
'B': 'BLACKHOLE',
'b': 'BROADCAST',
'C': 'CLONING',
'c': 'PRCLONING',
'D': 'DYNAMIC',
'G': 'GATEWAY',
'H': 'HOST',
'I': 'IFSCOPE',
'i': 'IFREF',
'L': 'LLINFO',
'M': 'MODIFIED',
'm': 'MULTICAST',
'R': 'REJECT',
'r': 'ROUTER',
'S': 'STATIC',
'U': 'UP',
'W': 'WASCLONED',
'X': 'XRESOLVE',
'Y': 'PROXY',
}
pretty_flags = []
for flag in entry['route_flags']:
if flag in flag_map:
pretty_flags.append(flag_map[flag])
entry['route_flags_pretty'] = pretty_flags
# strip whitespace from beginning and end of all string values
for item in entry:
if isinstance(entry[item], str):
entry[item] = entry[item].strip()
return raw_data
def parse(cleandata):
"""
Main text parsing function for OSX netstat
Parameters:
cleandata: (string) text data to parse
Returns:
List of Dictionaries. Raw structured data.
"""
raw_output = []
network = False
multipath = False
socket = False
reg_kernel_control = False
active_kernel_event = False
active_kernel_control = False
routing_table = False
interface_table = False
for line in cleandata:
if line.startswith('Active Internet'):
network = True
multipath = False
socket = False
reg_kernel_control = False
active_kernel_event = False
active_kernel_control = False
routing_table = False
interface_table = False
continue
if line.startswith('Active Multipath Internet connections'):
network = False
multipath = True
socket = False
reg_kernel_control = False
active_kernel_event = False
active_kernel_control = False
routing_table = False
interface_table = False
continue
if line.startswith('Active LOCAL (UNIX) domain sockets') or line.startswith('Active UNIX domain sockets'):
network = False
multipath = False
socket = True
reg_kernel_control = False
active_kernel_event = False
active_kernel_control = False
routing_table = False
interface_table = False
continue
if line.startswith('Registered kernel control modules'):
network = False
multipath = False
socket = False
reg_kernel_control = True
active_kernel_event = False
active_kernel_control = False
routing_table = False
interface_table = False
continue
if line.startswith('Active kernel event sockets'):
network = False
multipath = False
socket = False
reg_kernel_control = False
active_kernel_event = True
active_kernel_control = False
routing_table = False
interface_table = False
continue
if line.startswith('Active kernel control sockets'):
network = False
multipath = False
socket = False
reg_kernel_control = False
active_kernel_event = False
active_kernel_control = True
routing_table = False
interface_table = False
continue
if line.startswith('Routing tables'):
network = False
multipath = False
socket = False
reg_kernel_control = False
active_kernel_event = False
active_kernel_control = False
routing_table = True
interface_table = False
continue
if line.startswith('Name '):
network = False
multipath = False
socket = False
reg_kernel_control = False
active_kernel_event = False
active_kernel_control = False
routing_table = False
interface_table = True
# don't continue since there is no real header row for this table
# get headers
if network and (line.startswith('Socket ') or line.startswith('Proto ') or line.startswith('Tcpcb ')):
header_text = normalize_headers(line)
headers = header_text.split()
continue
if socket and line.startswith('Address '):
header_text = normalize_headers(line)
headers = header_text.split()
continue
if reg_kernel_control and (line.startswith('id ') or line.startswith('kctlref ')):
header_text = normalize_headers(line)
headers = header_text.split()
continue
if active_kernel_event and (line.startswith('Proto ') or line.startswith(' pcb ')):
header_text = normalize_headers(line)
headers = header_text.split()
continue
if active_kernel_control and (line.startswith('Proto ') or line.startswith(' pcb ')):
header_text = normalize_headers(line)
headers = header_text.split()
continue
if routing_table and line.startswith('Destination '):
header_text = normalize_route_headers(line)
headers = header_text.split()
continue
if interface_table and line.startswith('Name '):
header_text = normalize_interface_headers(line)
headers = header_text.split()
continue
# get items
if network:
raw_output.append(parse_item(headers, line, 'network'))
continue
if multipath:
# not implemented
continue
if socket:
raw_output.append(parse_item(headers, line, 'socket'))
continue
if reg_kernel_control:
raw_output.append(parse_item(headers, line, 'Registered kernel control module'))
continue
if active_kernel_event:
raw_output.append(parse_item(headers, line, 'Active kernel event socket'))
continue
if active_kernel_control:
raw_output.append(parse_item(headers, line, 'Active kernel control socket'))
continue
if routing_table and not (line.startswith('Internet:') or line.startswith('Internet6:')):
raw_output.append(parse_item(headers, line, 'route'))
continue
if interface_table:
raw_output.append(parse_item(headers, line, 'interface'))
continue
return parse_post(raw_output)