Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for json '-e' fields #455

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions src/pyshark/capture/capture.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ class UnknownEncyptionStandardException(Exception):
class RawMustUseJsonException(Exception):
"""If the use_raw argument is True, so should the use_json argument"""

class FieldMustUseJsonException(Exception):
"""If custom parameters include '-e', use_json argument should be True"""

class StopCapture(Exception):
"""Exception that the user can throw anywhere in packet-handling to stop the capture process."""
Expand Down Expand Up @@ -69,10 +71,14 @@ def __init__(self, display_filter=None, only_summaries=False, eventloop=None,
self._custom_parameters = custom_parameters
self._eof_reached = False
self.__tshark_version = None
self._field_param = False

if include_raw and not use_json:
raise RawMustUseJsonException("use_json must be True if include_raw")

if self._custom_parameters and '-e' in self._custom_parameters and not use_json:
raise FieldMustUseJsonException("use_json must be True to properly parse '-e' fields")

if self.debug:
self.set_debug()

Expand Down Expand Up @@ -257,7 +263,7 @@ def _packets_from_tshark_sync(self, packet_count=None, existing_process=None):

def apply_on_packets(self, callback, timeout=None, packet_count=None):
"""Runs through all packets and calls the given callback (a function) with each one as it is read.

If the capture is infinite (i.e. a live capture), it will run forever, otherwise it will complete after all
packets have been read.

Expand Down Expand Up @@ -355,7 +361,7 @@ async def _get_packet_from_stream(self, stream, existing_data, got_first_packet=

if packet:
if self.use_json:
packet = packet_from_json_packet(packet, deduplicate_fields=self._json_has_duplicate_keys)
packet = packet_from_json_packet(packet, self._field_param, deduplicate_fields=self._json_has_duplicate_keys)
else:
packet = packet_from_xml_packet(packet, psml_structure=psml_structure)
return packet, existing_data
Expand Down Expand Up @@ -469,6 +475,10 @@ def get_parameters(self, packet_count=None):
elif isinstance(self._custom_parameters, dict):
for key, val in self._custom_parameters.items():
params += [key, val]
# Add minimum fields if fields are being used.
if '-e' in self._custom_parameters:
params += ['-e', 'null.family', '-e', 'frame', '-e', 'frame.len', '-e', 'frame.protocols', '-e', 'frame.number', '-e', 'frame.interface_name', '-e', 'frame.interface_id', '-e', 'frame.time_epoch']
self._field_param = True
else:
raise TypeError("Custom parameters type not supported.")

Expand Down
47 changes: 37 additions & 10 deletions src/pyshark/tshark/tshark_json.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ def duplicate_object_hook(ordered_pairs):
return json_dict


def packet_from_json_packet(json_pkt, deduplicate_fields=True):
def packet_from_json_packet(json_pkt, field_param=False, deduplicate_fields=True):
"""Creates a Pyshark Packet from a tshark json single packet.

Before tshark 2.6, there could be duplicate keys in a packet json, which creates the need for
Expand All @@ -41,16 +41,43 @@ def packet_from_json_packet(json_pkt, deduplicate_fields=True):
pkt_dict = ujson.loads(json_pkt)
else:
pkt_dict = json.loads(json_pkt.decode('utf-8'))
# We use the frame dict here and not the object access because it's faster.
frame_dict = pkt_dict['_source']['layers'].pop('frame')
frame_dict = {}
layers = []
for layer in frame_dict['frame.protocols'].split(':'):
layer_dict = pkt_dict['_source']['layers'].pop(layer, None)
if layer_dict is not None:
layers.append(JsonLayer(layer, layer_dict))
# Add all leftovers
for name, layer in pkt_dict['_source']['layers'].items():
layers.append(JsonLayer(name, layer))
# Using "-T json -e <field>"
if field_param:
# for layer in frame_dict['frame.protocols'][0].split(':'):
# layer_dict = pkt_dict['_source'].pop('layers')
if pkt_dict:
for key, val in pkt_dict['_source']['layers'].items():
# Separate layer and name within the key and normalize '-' syntax.
layer = key.split('.', 1)[0].replace('-', '_')
name = key.replace('.', '_').replace('_', '.', 1).replace('-', '_')
# Convert list to string (as necessary).
if len(val) == 1:
val = val[0]
# Build frame dict
if layer == 'frame':
frame_dict[name] = val
else:
try:
frame_dict[layer][name] = val
except KeyError:
frame_dict[layer] = {name: val}
# Add all layers
for layer in pkt_dict['_source']['layers']['frame.protocols'][0].replace('-', '_').split(':'):
layer_dict = frame_dict.pop(layer, None)
if layer_dict is not None:
layers.append(JsonLayer(layer, layer_dict))
else:
# We use the frame dict here and not the object access because it's faster.
frame_dict = pkt_dict['_source']['layers'].pop('frame')
for layer in frame_dict['frame.protocols'].split(':'):
layer_dict = pkt_dict['_source']['layers'].pop(layer, None)
if layer_dict is not None:
layers.append(JsonLayer(layer, layer_dict))
# Add all leftovers
for name, layer in pkt_dict['_source']['layers'].items():
layers.append(JsonLayer(name, layer))

return Packet(layers=layers, frame_info=JsonLayer('frame', frame_dict),
number=int(frame_dict.get('frame.number', 0)),
Expand Down