Skip to content

Commit ab5a21b

Browse files
authored
Merge pull request #333 from KimiNewt/support-v3-json
Support v3 json
2 parents d8b0c00 + 22821df commit ab5a21b

File tree

6 files changed

+66
-34
lines changed

6 files changed

+66
-34
lines changed

src/pyshark/capture/capture.py

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,15 @@
33
import threading
44
import subprocess
55
import concurrent.futures
6+
from distutils.version import LooseVersion
67

78
import logbook
89
import sys
910

1011
from logbook import StreamHandler
1112

1213
from pyshark.tshark.tshark import get_process_path, get_tshark_display_filter_flag, \
13-
tshark_supports_json, TSharkVersionException
14+
tshark_supports_json, TSharkVersionException, get_tshark_version
1415
from pyshark.tshark.tshark_json import packet_from_json_packet
1516
from pyshark.tshark.tshark_xml import packet_from_xml_packet, psml_structure_from_xml
1617

@@ -67,6 +68,7 @@ def __init__(self, display_filter=None, only_summaries=False, eventloop=None,
6768
self._log = logbook.Logger(self.__class__.__name__, level=self.DEFAULT_LOG_LEVEL)
6869
self._closed = False
6970
self._custom_parameters = custom_parameters
71+
self._tshark_version = None
7072

7173
if include_raw and not use_json:
7274
raise RawMustUseJsonException("use_json must be True if include_raw")
@@ -158,25 +160,40 @@ def _setup_eventloop(self):
158160
if os.name == 'posix' and isinstance(threading.current_thread(), threading._MainThread):
159161
asyncio.get_child_watcher().attach_loop(self.eventloop)
160162

161-
@classmethod
162-
def _get_json_separator(cls):
163-
return ("}%s%s ," % (os.linesep, os.linesep)).encode()
163+
def _get_json_separators(self):
164+
""""Returns the separators between packets in a JSON output
164165
165-
@classmethod
166-
def _extract_packet_json_from_data(cls, data, got_first_packet=True):
166+
Returns a tuple of (packet_separator, end_of_file_separator, characters_to_disregard).
167+
The latter variable being the number of characters to ignore in order to pass the packet (i.e. extra newlines,
168+
commas, parenthesis).
169+
"""
170+
if LooseVersion(self._tshark_version) >= LooseVersion("3.0.0"):
171+
return ("%s },%s" % (os.linesep, os.linesep)).encode(), ("}%s]" % os.linesep).encode(), (
172+
1 + len(os.linesep))
173+
else:
174+
return ("}%s%s ," % (os.linesep, os.linesep)).encode(), ("}%s%s]" % (os.linesep, os.linesep)).encode(), 1
175+
176+
def _extract_packet_json_from_data(self, data, got_first_packet=True):
167177
tag_start = 0
168178
if not got_first_packet:
169179
tag_start = data.find(b"{")
170180
if tag_start == -1:
171181
return None, data
172-
closing_tag = cls._get_json_separator()
173-
tag_end = data.find(closing_tag)
182+
packet_separator, end_separator, end_tag_strip_length = self._get_json_separators()
183+
found_separator = None
184+
185+
tag_end = data.find(packet_separator)
174186
if tag_end == -1:
175-
closing_tag = ("}%s%s]" % (os.linesep, os.linesep)).encode()
176-
tag_end = data.find(closing_tag)
177-
if tag_end != -1:
178-
# Include closing parenthesis but not comma
179-
tag_end += len(closing_tag) - 1
187+
# Not end of packet, maybe it has end of entire file?
188+
tag_end = data.find(end_separator)
189+
if tag_end != -1:
190+
found_separator = end_separator
191+
else:
192+
# Found a single packet, just add the separator without extras
193+
found_separator = packet_separator
194+
195+
if found_separator:
196+
tag_end += len(found_separator) - end_tag_strip_length
180197
return data[tag_start:tag_end], data[tag_end + 1:]
181198
return None, data
182199

@@ -229,7 +246,8 @@ def _packets_from_tshark_sync(self, packet_count=None, existing_process=None):
229246
if packet_count and packets_captured >= packet_count:
230247
break
231248
finally:
232-
self.eventloop.run_until_complete(self._cleanup_subprocess(tshark_process))
249+
if tshark_process in self._running_processes:
250+
self.eventloop.run_until_complete(self._cleanup_subprocess(tshark_process))
233251

234252
def apply_on_packets(self, callback, timeout=None, packet_count=None):
235253
"""
@@ -357,7 +375,9 @@ async def _get_tshark_process(self, packet_count=None, stdin=None):
357375
"""
358376
if self.use_json:
359377
output_type = 'json'
360-
if not tshark_supports_json(self.tshark_path):
378+
if not self._tshark_version:
379+
self._tshark_version = get_tshark_version(self.tshark_path)
380+
if not tshark_supports_json(self._tshark_version):
361381
raise TSharkVersionException("JSON only supported on Wireshark >= 2.2.0")
362382
else:
363383
output_type = 'psml' if self._only_summaries else 'pdml'

src/pyshark/capture/inmem_capture.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
import struct
55
import time
66
import warnings
7-
7+
from distutils.version import LooseVersion
88

99
from pyshark.capture.capture import Capture, StopCapture
1010

@@ -74,9 +74,17 @@ async def _get_tshark_process(self, packet_count=None):
7474

7575
return proc
7676

77-
@classmethod
78-
def _get_json_separator(cls):
79-
return ("}%s%s" % (os.linesep, os.linesep)).encode()
77+
def _get_json_separators(self):
78+
""""Returns the separators between packets in a JSON output
79+
80+
Returns a tuple of (packet_separator, end_of_file_separator, characters_to_disregard).
81+
The latter variable being the number of characters to ignore in order to pass the packet (i.e. extra newlines,
82+
commas, parenthesis).
83+
"""
84+
if LooseVersion(self._tshark_version) >= LooseVersion("3.0.0"):
85+
return ("%s }" % os.linesep).encode(), ("}%s]" % os.linesep).encode(), 0
86+
else:
87+
return ("}%s%s" % (os.linesep, os.linesep)).encode(), ("}%s%s]" % (os.linesep, os.linesep)).encode(), 1
8088

8189
def _write_packet(self, packet):
8290
# Write packet header

src/pyshark/tshark/tshark.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,7 @@ def get_tshark_version(tshark_path=None):
7777
return version_string
7878

7979

80-
def tshark_supports_json(tshark_path=None):
81-
tshark_version = get_tshark_version(tshark_path)
80+
def tshark_supports_json(tshark_version):
8281
return LooseVersion(tshark_version) >= LooseVersion("2.2.0")
8382

8483

src/setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
setup(
88
name="pyshark",
9-
version="0.4.2.2",
9+
version="0.4.2.3",
1010
packages=find_packages(),
1111
package_data={'': ['*.ini', '*.pcapng']},
1212
install_requires=['lxml', 'py', 'logbook'],

tests/conftest.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,9 @@ def simple_summary_capture(request):
4343
"""
4444
A capture already full of packets
4545
"""
46-
return make_test_capture(request, only_summaries=True)
46+
return make_test_capture(request, only_summaries=True)
47+
48+
49+
@pytest.fixture(params=[True, False])
50+
def simple_xml_and_json_capture(request):
51+
return make_test_capture(request, use_json=request.param)

tests/test_basic_parsing.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,42 @@
1-
def test_count_packets(simple_capture):
1+
def test_count_packets(simple_xml_and_json_capture):
22
"""Test to make sure the right number of packets are read from a known
33
capture"""
4-
packet_count = sum(1 for _ in simple_capture)
4+
packet_count = sum(1 for _ in simple_xml_and_json_capture)
55
assert packet_count == 24
66

77

8-
def test_sum_lengths(simple_capture):
8+
def test_sum_lengths(simple_xml_and_json_capture):
99
"""Test to make sure that the right packet length is being read from
1010
tshark's output by comparing the aggregate length of all packets
1111
to a known value"""
12-
total_length = sum(int(packet.length) for packet in simple_capture)
12+
total_length = sum(int(packet.length) for packet in simple_xml_and_json_capture)
1313
assert total_length == 2178
1414

1515

16-
def test_layers(simple_capture):
16+
def test_layers(simple_xml_and_json_capture):
1717
"""Test to make sure the correct protocols are reported for known
1818
packets"""
1919
packet_indexes = (0, 5, 6, 13, 14, 17, 23)
20-
test_values = [simple_capture[i].highest_layer for i in packet_indexes]
20+
test_values = [simple_xml_and_json_capture[i].highest_layer for i in packet_indexes]
2121
known_values = ['DNS', 'DNS', 'ICMP', 'ICMP', 'TCP', 'HTTP', 'TCP']
2222
assert test_values == known_values
2323

2424

25-
def test_ethernet(simple_capture):
25+
def test_ethernet(simple_xml_and_json_capture):
2626
"""Test to make sure Ethernet fields are being read properly by comparing
2727
packet dissection results to known values"""
28-
packet = simple_capture[0]
28+
packet = simple_xml_and_json_capture[0]
2929
test_values = packet.eth.src, packet.eth.dst
3030
known_values = ('00:00:bb:10:20:10', '00:00:bb:02:04:01')
3131
assert test_values == known_values
3232

3333

34-
def test_icmp(simple_capture):
34+
def test_icmp(simple_xml_and_json_capture):
3535
"""Test to make sure ICMP fields are being read properly by comparing
3636
packet dissection results to known values"""
37-
packet = simple_capture[11]
37+
packet = simple_xml_and_json_capture[11]
3838
# The value returned by tshark is locale-dependent.
3939
# Depending on the locale, a comma can be used instead of a dot
4040
# as decimal separator.
4141
resptime = packet.icmp.resptime.replace(',', '.')
42-
assert resptime == '1.667'
42+
assert resptime == '1.667'

0 commit comments

Comments
 (0)