diff --git a/src/pyshark/capture/capture.py b/src/pyshark/capture/capture.py index 47c20947..1e923a14 100644 --- a/src/pyshark/capture/capture.py +++ b/src/pyshark/capture/capture.py @@ -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.""" @@ -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() @@ -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. @@ -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 @@ -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.") diff --git a/src/pyshark/tshark/tshark_json.py b/src/pyshark/tshark/tshark_json.py index 2148307a..1ea2bf32 100644 --- a/src/pyshark/tshark/tshark_json.py +++ b/src/pyshark/tshark/tshark_json.py @@ -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 @@ -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 " + 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)),