aboutsummaryrefslogtreecommitdiffstats
path: root/test/suite_sharkd.py
diff options
context:
space:
mode:
authorPeter Wu <peter@lekensteyn.nl>2018-11-20 16:27:56 +0100
committerAnders Broman <a.broman58@gmail.com>2018-11-21 04:36:20 +0000
commit7943dbf7bbffb535f83c333b779f9d384c7fe6df (patch)
tree632ac60aaddd5b79f67f54cd2261a54c04e80eb6 /test/suite_sharkd.py
parent049d7464951eed3aa059b6b875888697bad3b2a5 (diff)
test: extend sharkd tests to cover all requests
All request types have a corresponding test_sharkd_req_* test names which tests the current (documented) behavior. The frame and download tests are not very comprehensive though, but it's better than nothing. (The original test_sharkd_hello_dhcp_pcap test is replaced by test_sharkd_req_status and test_sharkd_req_frames, although the latter does not literally check for the "DHCP" column anymore.) Change-Id: Ic39b954fc50065345ac46e96a7057b7aba2a09e3 Reviewed-on: https://code.wireshark.org/review/30743 Petri-Dish: Peter Wu <peter@lekensteyn.nl> Tested-by: Petri Dish Buildbot Reviewed-by: Anders Broman <a.broman58@gmail.com>
Diffstat (limited to 'test/suite_sharkd.py')
-rw-r--r--test/suite_sharkd.py467
1 files changed, 425 insertions, 42 deletions
diff --git a/test/suite_sharkd.py b/test/suite_sharkd.py
index e69fdf56d8..b39e950e0a 100644
--- a/test/suite_sharkd.py
+++ b/test/suite_sharkd.py
@@ -3,14 +3,18 @@
# Wireshark tests
# By Gerald Combs <gerald@wireshark.org>
#
+# Copyright (c) 2018 Peter Wu <peter@lekensteyn.nl>
+#
# SPDX-License-Identifier: GPL-2.0-or-later
#
'''sharkd tests'''
import json
import subprocess
+import unittest
import subprocesstest
import fixtures
+from matchers import *
@fixtures.fixture(scope='session')
@@ -18,55 +22,434 @@ def cmd_sharkd(program):
return program('sharkd')
-@fixtures.mark_usefixtures('test_env')
+@fixtures.fixture
+def run_sharkd_session(cmd_sharkd, request):
+ self = request.instance
+
+ def run_sharkd_session_real(sharkd_commands):
+ sharkd_proc = self.startProcess(
+ (cmd_sharkd, '-'), stdin=subprocess.PIPE)
+ sharkd_proc.stdin.write('\n'.join(sharkd_commands).encode('utf8'))
+ self.waitProcess(sharkd_proc)
+
+ self.assertIn('Hello in child.', sharkd_proc.stderr_str)
+
+ outputs = []
+ for line in sharkd_proc.stdout_str.splitlines():
+ line = line.strip()
+ if not line:
+ continue
+ try:
+ jdata = json.loads(line)
+ except json.JSONDecodeError:
+ self.fail('Invalid JSON: %r' % line)
+ outputs.append(jdata)
+ return tuple(outputs)
+ return run_sharkd_session_real
+
+
+@fixtures.fixture
+def check_sharkd_session(run_sharkd_session, request):
+ self = request.instance
+
+ def check_sharkd_session_real(sharkd_commands, expected_outputs):
+ sharkd_commands = [json.dumps(x) for x in sharkd_commands]
+ actual_outputs = run_sharkd_session(sharkd_commands)
+ self.assertEqual(expected_outputs, actual_outputs)
+ return check_sharkd_session_real
+
+
+@fixtures.mark_usefixtures('base_env')
@fixtures.uses_fixtures
class case_sharkd(subprocesstest.SubprocessTestCase):
- def test_sharkd_hello_no_pcap(self, cmd_sharkd):
- '''sharkd hello message, no capture file'''
- sharkd_proc = self.startProcess((cmd_sharkd, '-'),
- stdin=subprocess.PIPE
- )
-
- sharkd_commands = b'{"req":"status"}\n'
- sharkd_proc.stdin.write(sharkd_commands)
- self.waitProcess(sharkd_proc)
+ def test_sharkd_req_load_bad_pcap(self, check_sharkd_session, capture_file):
+ check_sharkd_session((
+ {"req": "load", "file": capture_file('non-existant.pcap')},
+ ), (
+ {"err": 2},
+ ))
- self.assertEqual(self.countOutput('Hello in child.', count_stdout=False, count_stderr=True), 1, 'No hello message.')
+ def test_sharkd_req_status_no_pcap(self, check_sharkd_session):
+ check_sharkd_session((
+ {"req": "status"},
+ ), (
+ {"frames": 0, "duration": 0.0},
+ ))
- try:
- jdata = json.loads(sharkd_proc.stdout_str)
- self.assertEqual(jdata['duration'], 0.0, 'Missing duration.')
- except:
- self.fail('Invalid JSON: "{}"'.format(sharkd_proc.stdout_str))
+ def test_sharkd_req_status(self, check_sharkd_session, capture_file):
+ check_sharkd_session((
+ {"req": "load", "file": capture_file('dhcp.pcap')},
+ {"req": "status"},
+ ), (
+ {"err": 0},
+ {"frames": 4, "duration": 0.070345000,
+ "filename": "dhcp.pcap", "filesize": 1400},
+ ))
- def test_sharkd_hello_dhcp_pcap(self, cmd_sharkd, capture_file):
- '''sharkd hello message, simple capture file'''
- sharkd_proc = self.startProcess((cmd_sharkd, '-'),
- stdin=subprocess.PIPE
- )
+ def test_sharkd_req_analyse(self, check_sharkd_session, capture_file):
+ check_sharkd_session((
+ {"req": "load", "file": capture_file('dhcp.pcap')},
+ {"req": "analyse"},
+ ), (
+ {"err": 0},
+ {"frames": 4, "protocols": ["frame", "eth", "ethertype", "ip", "udp",
+ "dhcp"], "first": 1102274184.317452908, "last": 1102274184.387798071},
+ ))
- sharkd_commands = b'{"req":"load","file":'
- sharkd_commands += json.dumps(capture_file('dhcp.pcap')).encode('utf8')
- sharkd_commands += b'}\n'
- sharkd_commands += b'{"req":"status"}\n'
- sharkd_commands += b'{"req":"frames"}\n'
+ def test_sharkd_req_info(self, check_sharkd_session):
+ matchTapNameList = MatchList(
+ {"tap": MatchAny(str), "name": MatchAny(str)})
+ check_sharkd_session((
+ {"req": "info"},
+ ), (
+ {
+ "version": MatchAny(str),
+ "columns": MatchList({"format": MatchAny(str), "name": MatchAny(str)}),
+ "stats": matchTapNameList,
+ "convs": matchTapNameList,
+ "eo": matchTapNameList,
+ "srt": matchTapNameList,
+ "rtd": matchTapNameList,
+ "seqa": matchTapNameList,
+ "taps": matchTapNameList,
+ "follow": matchTapNameList,
+ "ftypes": MatchList(MatchAny(str)),
+ "nstat": matchTapNameList,
+ },
+ ))
- sharkd_proc.stdin.write(sharkd_commands)
- self.waitProcess(sharkd_proc)
+ def test_sharkd_req_check(self, check_sharkd_session, capture_file):
+ check_sharkd_session((
+ {"req": "load", "file": capture_file('dhcp.pcap')},
+ {"req": "check"},
+ {"req": "check", "filter": "garbage filter"},
+ {"req": "check", "field": "garbage field"},
+ {"req": "check", "filter": "ip", "field": "ip"},
+ ), (
+ {"err": 0},
+ {"err": 0},
+ {"err": 0, "filter": '"filter" was unexpected in this context.'},
+ {"err": 0, "field": "notfound"},
+ {"err": 0, "filter": "ok", "field": "ok"},
+ ))
- has_dhcp = False
- for line in sharkd_proc.stdout_str.splitlines():
- line = line.strip()
- if not line: continue
- try:
- jdata = json.loads(line)
- except:
- self.fail('Invalid JSON for "{}"'.format(line))
+ def test_sharkd_req_complete_field(self, check_sharkd_session):
+ check_sharkd_session((
+ {"req": "complete"},
+ {"req": "complete", "field": "frame.le"},
+ {"req": "complete", "field": "garbage.nothing.matches"},
+ ), (
+ {"err": 0},
+ {"err": 0, "field": MatchList(
+ {"f": "frame.len", "t": 7, "n": "Frame length on the wire"}, match_element=any)},
+ {"err": 0, "field": []},
+ ))
- try:
- if 'DHCP' in jdata[0]['c']:
- has_dhcp = True
- except:
- pass
+ def test_sharkd_req_complete_pref(self, check_sharkd_session):
+ check_sharkd_session((
+ {"req": "complete", "pref": "tcp."},
+ {"req": "complete", "pref": "garbage.nothing.matches"},
+ ), (
+ {"err": 0, "pref": MatchList(
+ {"f": "tcp.check_checksum", "d": "Validate the TCP checksum if possible"}, match_element=any)},
+ {"err": 0, "pref": []},
+ ))
+
+ def test_sharkd_req_frames(self, check_sharkd_session, capture_file):
+ # XXX need test for optional input parameters, ignored/marked/commented
+ check_sharkd_session((
+ {"req": "load", "file": capture_file('dhcp.pcap')},
+ {"req": "frames"},
+ ), (
+ {"err": 0},
+ MatchList({
+ "c": MatchList(MatchAny(str)),
+ "num": MatchAny(int),
+ "bg": MatchAny(str),
+ "fg": MatchAny(str),
+ }),
+ ))
+
+ def test_sharkd_req_tap_invalid(self, check_sharkd_session, capture_file):
+ # XXX Unrecognized taps result in an empty line, modify
+ # run_sharkd_session such that checking for it is possible.
+ check_sharkd_session((
+ {"req": "load", "file": capture_file('dhcp.pcap')},
+ {"req": "tap"},
+ {"req": "tap", "tap0": "garbage tap"},
+ ), (
+ {"err": 0},
+ ))
+
+ def test_sharkd_req_tap(self, check_sharkd_session, capture_file):
+ check_sharkd_session((
+ {"req": "load", "file": capture_file('dhcp.pcap')},
+ {"req": "tap"},
+ {"req": "tap", "tap0": "conv:Ethernet", "tap1": "endpt:TCP"},
+ ), (
+ {"err": 0},
+ {
+ "err": 0,
+ "taps": [
+ {
+ "tap": "endpt:TCP",
+ "type": "host",
+ "proto": "TCP",
+ "geoip": MatchAny(bool),
+ "hosts": [],
+ },
+ {
+ "tap": "conv:Ethernet",
+ "type": "conv",
+ "proto": "Ethernet",
+ "geoip": MatchAny(bool),
+ "convs": [
+ {
+ "saddr": MatchAny(str),
+ "daddr": "Broadcast",
+ "txf": 2,
+ "txb": 628,
+ "rxf": 0,
+ "rxb": 0,
+ "start": 0,
+ "stop": 0.070031,
+ "filter": "eth.addr==00:0b:82:01:fc:42 && eth.addr==ff:ff:ff:ff:ff:ff",
+ },
+ {
+ "saddr": MatchAny(str),
+ "daddr": MatchAny(str),
+ "rxf": 0,
+ "rxb": 0,
+ "txf": 2,
+ "txb": 684,
+ "start": 0.000295,
+ "stop": 0.070345,
+ "filter": "eth.addr==00:08:74:ad:f1:9b && eth.addr==00:0b:82:01:fc:42",
+ }
+ ],
+ },
+ # XXX remove the last null element, it is not part of the interface.
+ None
+ ]
+ },
+ ))
+
+ def test_sharkd_req_follow_bad(self, check_sharkd_session, capture_file):
+ # Unrecognized taps currently produce no output (not even err).
+ check_sharkd_session((
+ {"req": "load", "file": capture_file('dhcp.pcap')},
+ {"req": "follow"},
+ {"req": "follow", "follow": "garbage follow", "filter": "ip"},
+ {"req": "follow", "follow": "HTTP", "filter": "garbage filter"},
+ ), (
+ {"err": 0},
+ ))
+
+ def test_sharkd_req_follow_no_match(self, check_sharkd_session, capture_file):
+ check_sharkd_session((
+ {"req": "load", "file": capture_file('dhcp.pcap')},
+ {"req": "follow", "follow": "HTTP", "filter": "ip"},
+ ), (
+ {"err": 0},
+ {"err": 0, "shost": "NONE", "sport": "0", "sbytes": 0,
+ "chost": "NONE", "cport": "0", "cbytes": 0},
+ ))
+
+ def test_sharkd_req_follow_udp(self, check_sharkd_session, capture_file):
+ check_sharkd_session((
+ {"req": "load", "file": capture_file('dhcp.pcap')},
+ {"req": "follow", "follow": "UDP", "filter": "frame.number==1"},
+ ), (
+ {"err": 0},
+ {"err": 0,
+ "shost": "255.255.255.255", "sport": "67", "sbytes": 272,
+ "chost": "0.0.0.0", "cport": "68", "cbytes": 0,
+ "payloads": [
+ {"n": 1, "d": MatchRegExp(r'AQEGAAAAPR0A[a-zA-Z0-9]{330}AANwQBAwYq/wAAAAAAAAA=')}]},
+ ))
+
+ def test_sharkd_req_iograph_bad(self, check_sharkd_session, capture_file):
+ check_sharkd_session((
+ {"req": "load", "file": capture_file('dhcp.pcap')},
+ {"req": "iograph"},
+ {"req": "iograph", "graph0": "garbage graph name"},
+ ), (
+ {"err": 0},
+ {"iograph": []},
+ {"iograph": []},
+ ))
+
+ def test_sharkd_req_iograph_basic(self, check_sharkd_session, capture_file):
+ check_sharkd_session((
+ {"req": "load", "file": capture_file('dhcp.pcap')},
+ {"req": "iograph", "graph0": "max:udp.length", "filter0": "udp.length"},
+ {"req": "iograph", "graph0": "packets", "graph1": "bytes"},
+ {"req": "iograph", "graph0": "packets", "filter0": "garbage filter"},
+ ), (
+ {"err": 0},
+ {"iograph": [{"items": [308.000000]}]},
+ {"iograph": [{"items": [4.000000]}, {"items": [1312.000000]}]},
+ {"iograph": [
+ {"errmsg": 'Filter "garbage filter" is invalid - "filter" was unexpected in this context.'}]},
+ ))
+
+ def test_sharkd_req_intervals_bad(self, check_sharkd_session, capture_file):
+ check_sharkd_session((
+ {"req": "load", "file": capture_file('dhcp.pcap')},
+ {"req": "intervals", "filter": "garbage filter"},
+ ), (
+ {"err": 0},
+ ))
+
+ def test_sharkd_req_intervals_basic(self, check_sharkd_session, capture_file):
+ check_sharkd_session((
+ {"req": "load", "file": capture_file('dhcp.pcap')},
+ {"req": "intervals"},
+ {"req": "intervals", "interval": 1},
+ {"req": "intervals", "filter": "frame.number <= 2"},
+ ), (
+ {"err": 0},
+ {"intervals": [[0, 4, 1312]], "last": 0,
+ "frames": 4, "bytes": 1312},
+ {"intervals": [[0, 2, 656], [70, 2, 656]],
+ "last": 70, "frames": 4, "bytes": 1312},
+ {"intervals": [[0, 2, 656]], "last": 0, "frames": 2, "bytes": 656},
+ ))
+
+ def test_sharkd_req_frame_basic(self, check_sharkd_session, capture_file):
+ # XXX add more tests for other options (ref_frame, prev_frame, columns, color, bytes, hidden)
+ check_sharkd_session((
+ {"req": "load", "file": capture_file('dhcp.pcap')},
+ {"req": "frame", "frame": 2},
+ ), (
+ {"err": 0},
+ # XXX remove the first 0 element, it is not part of the interface.
+ {"err": 0, "fol": [0, ["UDP", "udp.stream eq 1"]]},
+ ))
+
+ def test_sharkd_req_frame_proto(self, check_sharkd_session, capture_file):
+ # Check proto tree output (including an UTF-8 value).
+ check_sharkd_session((
+ {"req": "load", "file": capture_file('dhcp.pcap')},
+ {"req": "frame", "frame": 2, "proto": True},
+ ), (
+ {"err": 0},
+ MatchObject({
+ "tree": MatchList({
+ "l": "Dynamic Host Configuration Protocol (Offer)",
+ "t": "proto",
+ "f": "dhcp",
+ "e": MatchAny(int),
+ "n": MatchList({
+ "l": "Padding: 000000000000000000000000000000000000000000000000…",
+ "h": [316, 26],
+ "f": "dhcp.option.padding == 00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00"
+ }, match_element=any), # match one element from 'n'
+ "h": [42, 300],
+ }, match_element=any), # match one element from 'tree'
+ }),
+ ))
+
+ def test_sharkd_req_setcomment(self, check_sharkd_session, capture_file):
+ check_sharkd_session((
+ {"req": "load", "file": capture_file('dhcp.pcap')},
+ # invalid frame number returns early.
+ {"req": "setcomment", "frame": 99999, "comment": "meh\nbaz"},
+ {"req": "setcomment", "frame": 3, "comment": "foo\nbar"},
+ {"req": "frame", "frame": 3},
+
+ ), (
+ {"err": 0},
+ {"err": 0},
+ {"err": 0, "comment": "foo\nbar", "fol": MatchAny(list)},
+ ))
+
+ def test_sharkd_req_setconf_bad(self, check_sharkd_session):
+ check_sharkd_session((
+ {"req": "setconf", "name": "uat:garbage-pref", "value": "\"\""},
+ ), (
+ {"err": 1, "errmsg": "Unknown preference"},
+ ))
+
+ def test_sharkd_req_dumpconf_bad(self, check_sharkd_session):
+ check_sharkd_session((
+ {"req": "dumpconf", "pref": "invalid-garbage-preference"},
+ {"req": "dumpconf", "pref": "uat:custom_http_header_fields"},
+ ), ())
+
+ def test_sharkd_req_dumpconf_all(self, check_sharkd_session):
+ check_sharkd_session((
+ {"req": "dumpconf"},
+ ), (
+ {"prefs": MatchObject({"tcp.check_checksum": {"b": 0}})},
+ ))
+
+ def test_sharkd_req_download_tls_secrets(self, check_sharkd_session, capture_file):
+ # XXX test download for eo: and rtp: too
+ check_sharkd_session((
+ {"req": "load", "file": capture_file('tls12-dsb.pcapng')},
+ {"req": "download", "token": "ssl-secrets"},
+ ), (
+ {"err": 0},
+ # TODO remove "RSA Session-ID:" support and support "CLIENT_RANDOM "... only
+ {"file": "keylog.txt", "mime": "text/plain",
+ "data": MatchRegExp(r'UlNBIFNlc3Npb24tSUQ6.+')},
+ ))
+
+ def test_sharkd_req_bye(self, check_sharkd_session):
+ check_sharkd_session((
+ {"req": "bye"},
+ ), (
+ ))
+
+ def test_sharkd_bad_request(self, check_sharkd_session):
+ check_sharkd_session((
+ {"req": 1337},
+ ), (
+ ))
+
+ def test_sharkd_config(self, check_sharkd_session):
+ check_sharkd_session((
+ {"req": "setconf", "name": "uat:custom_http_header_fields",
+ "value": "\"X-Header-Name\", \"Description\""},
+ {"req": "setconf", "name": "tcp.check_checksum", "value": "TRUE"},
+ {"req": "dumpconf", "pref": "tcp.check_checksum"},
+ {"req": "setconf", "name": "tcp.check_checksum", "value": "FALSE"},
+ {"req": "dumpconf", "pref": "tcp.check_checksum"},
+ ), (
+ # Check that the UAT preference is set. There is no way to query it
+ # (other than testing for side-effects in dissection).
+ {"err": 0},
+ {"err": 0},
+ {"prefs": {"tcp.check_checksum": {"b": 1}}},
+ {"err": 0},
+ {"prefs": {"tcp.check_checksum": {"b": 0}}},
+ ))
- self.assertTrue(has_dhcp, 'Failed to find DHCP in JSON output')
+ def test_sharkd_config_enum(self, check_sharkd_session):
+ '''Dump default enum preference value, change it and restore it.'''
+ check_sharkd_session((
+ {"req": "dumpconf", "pref": "wlan.ignore_wep"},
+ {"req": "setconf", "name": "wlan.ignore_wep", "value": "Yes - with IV"},
+ {"req": "dumpconf", "pref": "wlan.ignore_wep"},
+ {"req": "setconf", "name": "wlan.ignore_wep", "value": "No"},
+ {"req": "dumpconf", "pref": "wlan.ignore_wep"},
+ ), (
+ {"prefs": {"wlan.ignore_wep": {"e": [
+ {"v": 0, "s": 1, "d": "No"},
+ {"v": 1, "d": "Yes - without IV"},
+ {"v": 2, "d": "Yes - with IV"}
+ ]}}},
+ {"err": 0},
+ {"prefs": {"wlan.ignore_wep": {"e": [
+ {"v": 0, "d": "No"},
+ {"v": 1, "d": "Yes - without IV"},
+ {"v": 2, "s": 1, "d": "Yes - with IV"}
+ ]}}},
+ {"err": 0},
+ {"prefs": {"wlan.ignore_wep": {"e": [
+ {"v": 0, "s": 1, "d": "No"},
+ {"v": 1, "d": "Yes - without IV"},
+ {"v": 2, "d": "Yes - with IV"}
+ ]}}},
+ ))