1#
2# Wireshark tests
3# By Gerald Combs <gerald@wireshark.org>
4#
5# Copyright (c) 2018 Peter Wu <peter@lekensteyn.nl>
6#
7# SPDX-License-Identifier: GPL-2.0-or-later
8#
9'''sharkd tests'''
10
11import json
12import subprocess
13import unittest
14import subprocesstest
15import fixtures
16from matchers import *
17
18
19@fixtures.fixture(scope='session')
20def cmd_sharkd(program):
21    return program('sharkd')
22
23
24@fixtures.fixture
25def run_sharkd_session(cmd_sharkd, request):
26    self = request.instance
27
28    def run_sharkd_session_real(sharkd_commands):
29        sharkd_proc = self.startProcess(
30            (cmd_sharkd, '-'), stdin=subprocess.PIPE)
31        sharkd_proc.stdin.write('\n'.join(sharkd_commands).encode('utf8'))
32        self.waitProcess(sharkd_proc)
33
34        self.assertIn('Hello in child.', sharkd_proc.stderr_str)
35
36        outputs = []
37        for line in sharkd_proc.stdout_str.splitlines():
38            line = line.strip()
39            if not line:
40                continue
41            try:
42                jdata = json.loads(line)
43            except json.JSONDecodeError:
44                self.fail('Invalid JSON: %r' % line)
45            outputs.append(jdata)
46        return tuple(outputs)
47    return run_sharkd_session_real
48
49
50@fixtures.fixture
51def check_sharkd_session(run_sharkd_session, request):
52    self = request.instance
53
54    def check_sharkd_session_real(sharkd_commands, expected_outputs):
55        sharkd_commands = [json.dumps(x) for x in sharkd_commands]
56        actual_outputs = run_sharkd_session(sharkd_commands)
57        self.assertEqual(expected_outputs, actual_outputs)
58    return check_sharkd_session_real
59
60
61@fixtures.mark_usefixtures('base_env')
62@fixtures.uses_fixtures
63class case_sharkd(subprocesstest.SubprocessTestCase):
64    def test_sharkd_req_load_bad_pcap(self, check_sharkd_session, capture_file):
65        check_sharkd_session((
66            {"jsonrpc":"2.0", "id":1, "method":"load",
67            "params":{"file": capture_file('non-existant.pcap')}
68            },
69        ), (
70            {"jsonrpc":"2.0","id":1,"error":{"code":-2001,"message":"Unable to open the file"}},
71        ))
72
73    def test_sharkd_req_status_no_pcap(self, check_sharkd_session):
74        check_sharkd_session((
75            {"jsonrpc":"2.0", "id":1, "method":"status"},
76        ), (
77            {"jsonrpc":"2.0","id":1,"result":{"frames":0,"duration":0.000000000}},
78        ))
79
80    def test_sharkd_req_status(self, check_sharkd_session, capture_file):
81        check_sharkd_session((
82            {"jsonrpc":"2.0", "id":1, "method":"load",
83            "params":{"file": capture_file('dhcp.pcap')}
84            },
85            {"jsonrpc":"2.0", "id":2, "method":"status"},
86        ), (
87            {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}},
88            {"jsonrpc":"2.0","id":2,"result":{"frames": 4, "duration": 0.070345000,
89                "filename": "dhcp.pcap", "filesize": 1400}},
90        ))
91
92    def test_sharkd_req_analyse(self, check_sharkd_session, capture_file):
93        check_sharkd_session((
94            {"jsonrpc":"2.0", "id":1, "method":"load",
95            "params":{"file": capture_file('dhcp.pcap')}
96            },
97            {"jsonrpc":"2.0", "id":2, "method":"analyse"},
98        ), (
99            {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}},
100            {"jsonrpc":"2.0","id":2,"result":{"frames": 4, "protocols": ["frame", "eth", "ethertype", "ip", "udp",
101                                        "dhcp"], "first": 1102274184.317452908, "last": 1102274184.387798071}},
102        ))
103
104    def test_sharkd_req_info(self, check_sharkd_session):
105        matchTapNameList = MatchList(
106            {"tap": MatchAny(str), "name": MatchAny(str)})
107        check_sharkd_session((
108            {"jsonrpc":"2.0", "id":1, "method":"info"},
109        ), (
110            {"jsonrpc":"2.0","id":1,"result":{
111                "version": MatchAny(str),
112                "columns": MatchList({"format": MatchAny(str), "name": MatchAny(str)}),
113                "stats": matchTapNameList,
114                "convs": matchTapNameList,
115                "eo": matchTapNameList,
116                "srt": matchTapNameList,
117                "rtd": matchTapNameList,
118                "seqa": matchTapNameList,
119                "taps": matchTapNameList,
120                "follow": matchTapNameList,
121                "ftypes": MatchList(MatchAny(str)),
122                "nstat": matchTapNameList,
123            }},
124        ))
125
126    def test_sharkd_req_check(self, check_sharkd_session, capture_file):
127        check_sharkd_session((
128            {"jsonrpc":"2.0", "id":1, "method":"load",
129            "params":{"file": capture_file('dhcp.pcap')}
130            },
131            {"jsonrpc":"2.0", "id":2, "method":"check"},
132            {"jsonrpc":"2.0", "id":3, "method":"check", "params":{"filter": "garbage filter"}},
133            {"jsonrpc":"2.0", "id":4, "method":"check", "params":{"field": "garbage field"}},
134            {"jsonrpc":"2.0", "id":5, "method":"check", "params":{"filter": "ip", "field": "ip"}},
135        ), (
136            {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}},
137            {"jsonrpc":"2.0","id":2,"result":{"status":"OK"}},
138            {"jsonrpc":"2.0","id":3,"error":{"code":-5001,"message":"Filter invalid - \"filter\" was unexpected in this context."}},
139            {"jsonrpc":"2.0","id":4,"error":{"code":-5002,"message":"Field garbage field not found"}},
140            {"jsonrpc":"2.0","id":5,"result":{"status":"OK"}},
141        ))
142
143    def test_sharkd_req_complete_field(self, check_sharkd_session):
144        check_sharkd_session((
145            {"jsonrpc":"2.0", "id":1, "method":"complete"},
146            {"jsonrpc":"2.0", "id":2, "method":"complete", "params":{"field": "frame.le"}},
147            {"jsonrpc":"2.0", "id":3, "method":"complete", "params":{"field": "garbage.nothing.matches"}},
148        ), (
149            {"jsonrpc":"2.0","id":1,"result":{}},
150            {"jsonrpc":"2.0","id":2,"result":{"field": MatchList(
151                {"f": "frame.len", "t": 7, "n": "Frame length on the wire"}, match_element=any)}
152            },
153            {"jsonrpc":"2.0","id":3,"result":{"field": []}},
154        ))
155
156    def test_sharkd_req_complete_pref(self, check_sharkd_session):
157        check_sharkd_session((
158            {"jsonrpc":"2.0", "id":1, "method":"complete", "params":{"pref": "tcp."}},
159            {"jsonrpc":"2.0", "id":2, "method":"complete", "params":{"pref": "garbage.nothing.matches"}},
160        ), (
161            {"jsonrpc":"2.0","id":1,"result":{"pref": MatchList(
162                {"f": "tcp.check_checksum", "d": "Validate the TCP checksum if possible"}, match_element=any)}
163            },
164            {"jsonrpc":"2.0","id":2,"result":{"pref": []}},
165        ))
166
167    def test_sharkd_req_frames(self, check_sharkd_session, capture_file):
168        # XXX need test for optional input parameters, ignored/marked/commented
169        check_sharkd_session((
170            {"jsonrpc":"2.0", "id":1, "method":"load",
171            "params":{"file": capture_file('dhcp.pcap')}
172            },
173            {"jsonrpc":"2.0", "id":2, "method":"frames"},
174        ), (
175            {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}},
176            {"jsonrpc":"2.0","id":2,"result":
177            MatchList({
178                "c": MatchList(MatchAny(str)),
179                "num": MatchAny(int),
180                "bg": MatchAny(str),
181                "fg": MatchAny(str),
182            })
183            },
184        ))
185
186    def test_sharkd_req_tap_invalid(self, check_sharkd_session, capture_file):
187        # XXX Unrecognized taps result in an empty line, modify
188        #     run_sharkd_session such that checking for it is possible.
189        check_sharkd_session((
190            {"jsonrpc":"2.0", "id":1, "method":"load",
191            "params":{"file": capture_file('dhcp.pcap')}
192            },
193            {"jsonrpc":"2.0", "id":2, "method":"tap"},
194            {"jsonrpc":"2.0", "id":3, "method":"tap", "params":{"tap0": "garbage tap"}},
195        ), (
196            {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}},
197            {"jsonrpc":"2.0","id":2,"error":{"code":-32600,"message":"Mandatory parameter tap0 is missing"}},
198            {"jsonrpc":"2.0","id":3,"error":{"code":-11012,"message":"sharkd_session_process_tap() garbage tap not recognized"}},
199        ))
200
201    def test_sharkd_req_tap(self, check_sharkd_session, capture_file):
202        check_sharkd_session((
203            {"jsonrpc":"2.0", "id":1, "method":"load",
204            "params":{"file": capture_file('dhcp.pcap')}
205            },
206            {"jsonrpc":"2.0", "id":2, "method":"tap"},
207            {"jsonrpc":"2.0", "id":3, "method":"tap", "params":{"tap0": "conv:Ethernet", "tap1": "endpt:TCP"}},
208        ), (
209            {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}},
210            {"jsonrpc":"2.0","id":2,"error":{"code":-32600,"message":"Mandatory parameter tap0 is missing"}},
211            {"jsonrpc":"2.0","id":3,"result":{
212                "taps": [
213                    {
214                        "tap": "endpt:TCP",
215                        "type": "host",
216                        "proto": "TCP",
217                        "geoip": MatchAny(bool),
218                        "hosts": [],
219                    },
220                    {
221                        "tap": "conv:Ethernet",
222                        "type": "conv",
223                        "proto": "Ethernet",
224                        "geoip": MatchAny(bool),
225                        "convs": [
226                            {
227                                "saddr": MatchAny(str),
228                                "daddr": "Broadcast",
229                                "txf": 2,
230                                "txb": 628,
231                                "rxf": 0,
232                                "rxb": 0,
233                                "start": 0,
234                                "stop": 0.070031,
235                                "filter": "eth.addr==00:0b:82:01:fc:42 && eth.addr==ff:ff:ff:ff:ff:ff",
236                            },
237                            {
238                                "saddr": MatchAny(str),
239                                "daddr": MatchAny(str),
240                                "rxf": 0,
241                                "rxb": 0,
242                                "txf": 2,
243                                "txb": 684,
244                                "start": 0.000295,
245                                "stop": 0.070345,
246                                "filter": "eth.addr==00:08:74:ad:f1:9b && eth.addr==00:0b:82:01:fc:42",
247                            }
248                        ],
249                    },
250                ]
251            }},
252        ))
253
254    def test_sharkd_req_follow_bad(self, check_sharkd_session, capture_file):
255        # Unrecognized taps currently produce no output (not even err).
256        check_sharkd_session((
257            {"jsonrpc":"2.0", "id":1, "method":"load",
258            "params":{"file": capture_file('dhcp.pcap')}
259            },
260            {"jsonrpc":"2.0", "id":2, "method":"follow"},
261            {"jsonrpc":"2.0", "id":3, "method":"follow",
262            "params":{"follow": "garbage follow", "filter": "ip"}
263            },
264            {"jsonrpc":"2.0", "id":4, "method":"follow",
265            "params":{"follow": "HTTP", "filter": "garbage filter"}
266            },
267        ), (
268            {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}},
269            {"jsonrpc":"2.0","id":2,"error":{"code":-32600,"message":"Mandatory parameter follow is missing"}},
270            {"jsonrpc":"2.0","id":3,"error":{"code":-12001,"message":"sharkd_session_process_follow() follower=garbage follow not found"}},
271            {"jsonrpc":"2.0","id":4,
272            "error":{"code":-12002,"message":"sharkd_session_process_follow() name=HTTP error=Filter \"garbage filter\" is invalid - \"filter\" was unexpected in this context."}
273            },
274        ))
275
276    def test_sharkd_req_follow_no_match(self, check_sharkd_session, capture_file):
277        check_sharkd_session((
278            {"jsonrpc":"2.0", "id":1, "method":"load",
279            "params":{"file": capture_file('dhcp.pcap')}
280            },
281            {"jsonrpc":"2.0", "id":2, "method":"follow",
282            "params":{"follow": "HTTP", "filter": "ip"}
283            },
284        ), (
285            {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}},
286            {"jsonrpc":"2.0","id":2,
287            "result":{"shost": "NONE", "sport": "0", "sbytes": 0,
288             "chost": "NONE", "cport": "0", "cbytes": 0}
289            },
290        ))
291
292    def test_sharkd_req_follow_udp(self, check_sharkd_session, capture_file):
293        check_sharkd_session((
294            {"jsonrpc":"2.0", "id":1, "method":"load",
295            "params":{"file": capture_file('dhcp.pcap')}
296            },
297            {"jsonrpc":"2.0", "id":2, "method":"follow",
298            "params":{"follow": "UDP", "filter": "frame.number==1"}
299            },
300        ), (
301            {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}},
302            {"jsonrpc":"2.0","id":2,
303            "result":{
304             "shost": "255.255.255.255", "sport": "67", "sbytes": 272,
305             "chost": "0.0.0.0", "cport": "68", "cbytes": 0,
306             "payloads": [
307                 {"n": 1, "d": MatchRegExp(r'AQEGAAAAPR0A[a-zA-Z0-9]{330}AANwQBAwYq/wAAAAAAAAA=')}]}
308            },
309        ))
310
311    def test_sharkd_req_iograph_bad(self, check_sharkd_session, capture_file):
312        check_sharkd_session((
313            {"jsonrpc":"2.0", "id":1, "method":"load",
314            "params":{"file": capture_file('dhcp.pcap')}
315            },
316            {"jsonrpc":"2.0", "id":2, "method":"iograph"},
317            {"jsonrpc":"2.0", "id":3, "method":"iograph",
318            "params":{"graph0": "garbage graph name"}
319            },
320        ), (
321            {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}},
322            {"jsonrpc":"2.0","id":2,"error":{"code":-32600,"message":"Mandatory parameter graph0 is missing"}},
323            {"jsonrpc":"2.0","id":3,"result":{"iograph": []}},
324        ))
325
326    def test_sharkd_req_iograph_basic(self, check_sharkd_session, capture_file):
327        check_sharkd_session((
328            {"jsonrpc":"2.0", "id":1, "method":"load",
329            "params":{"file": capture_file('dhcp.pcap')}
330            },
331            {"jsonrpc":"2.0", "id":1, "method":"iograph",
332            "params":{"graph0": "max:udp.length", "filter0": "udp.length"}
333            },
334            {"jsonrpc":"2.0", "id":2, "method":"iograph",
335            "params":{"graph0": "packets", "graph1": "bytes"}
336            },
337            {"jsonrpc":"2.0", "id":3, "method":"iograph",
338            "params":{"graph0": "packets", "filter0": "garbage filter"}
339            },
340        ), (
341            {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}},
342            {"jsonrpc":"2.0","id":1,"result":{"iograph": [{"items": [308.000000]}]}},
343            {"jsonrpc":"2.0","id":2,"result":{"iograph": [{"items": [4.000000]}, {"items": [1312.000000]}]}},
344            {"jsonrpc":"2.0","id":3,"error":{"code":-6001,"message":"Filter \"garbage filter\" is invalid - \"filter\" was unexpected in this context."}},
345        ))
346
347    def test_sharkd_req_intervals_bad(self, check_sharkd_session, capture_file):
348        check_sharkd_session((
349            {"jsonrpc":"2.0", "id":1, "method":"load",
350            "params":{"file": capture_file('dhcp.pcap')}
351            },
352            {"jsonrpc":"2.0", "id":2, "method":"intervals",
353            "params":{"filter": "garbage filter"}
354            },
355        ), (
356            {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}},
357            {"jsonrpc":"2.0","id":2,"error":{"code":-7001,"message":"Invalid filter parameter: garbage filter"}},
358        ))
359
360    def test_sharkd_req_intervals_basic(self, check_sharkd_session, capture_file):
361        check_sharkd_session((
362            {"jsonrpc":"2.0", "id":1, "method":"load",
363            "params":{"file": capture_file('dhcp.pcap')}
364            },
365            {"jsonrpc":"2.0", "id":2, "method":"intervals"},
366            {"jsonrpc":"2.0", "id":3, "method":"intervals",
367            "params":{"interval": 1}
368            },
369            {"jsonrpc":"2.0", "id":4, "method":"intervals",
370            "params":{"filter": "frame.number <= 2"}
371            },
372        ), (
373            {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}},
374            {"jsonrpc":"2.0","id":2,"result":{"intervals":[[0,4,1312]],"last":0,"frames":4,"bytes":1312}},
375            {"jsonrpc":"2.0","id":3,"result":{"intervals":[[0,2,656],[70,2,656]],"last":70,"frames":4,"bytes":1312}},
376            {"jsonrpc":"2.0","id":4,"result":{"intervals":[[0,2,656]],"last":0,"frames":2,"bytes":656}},
377        ))
378
379    def test_sharkd_req_frame_basic(self, check_sharkd_session, capture_file):
380        # XXX add more tests for other options (ref_frame, prev_frame, columns, color, bytes, hidden)
381        check_sharkd_session((
382            {"jsonrpc":"2.0", "id":1, "method":"load",
383            "params":{"file": capture_file('dhcp.pcap')}
384            },
385            {"jsonrpc":"2.0", "id":2, "method":"frame",
386            "params":{"frame": 2}
387            },
388        ), (
389            {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}},
390            {"jsonrpc":"2.0","id":2,"result":{"fol": [["UDP", "udp.stream eq 1"]]}},
391        ))
392
393    def test_sharkd_req_frame_proto(self, check_sharkd_session, capture_file):
394        # Check proto tree output (including an UTF-8 value).
395        check_sharkd_session((
396            {"jsonrpc":"2.0", "id":1, "method":"load",
397            "params":{"file": capture_file('dhcp.pcap')}
398            },
399            {"jsonrpc":"2.0", "id":2, "method":"frame",
400            "params":{"frame": 2, "proto": True}
401            },
402        ), (
403            {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}},
404            {"jsonrpc":"2.0","id":2,"result":
405            MatchObject({
406                "tree": MatchList({
407                    "l": "Dynamic Host Configuration Protocol (Offer)",
408                    "t": "proto",
409                    "f": "dhcp",
410                    "e": MatchAny(int),
411                    "n": MatchList({
412                        "l": "Padding: 0000000000000000000000000000000000000000000000000000",
413                        "h": [316, 26],
414                        "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"
415                    }, match_element=any),  # match one element from 'n'
416                    "h": [42, 300],
417                }, match_element=any),  # match one element from 'tree'
418            })
419            },
420        ))
421
422    def test_sharkd_req_setcomment(self, check_sharkd_session, capture_file):
423        check_sharkd_session((
424            {"jsonrpc":"2.0", "id":1, "method":"load",
425            "params":{"file": capture_file('dhcp.pcap')}
426            },
427            {"jsonrpc":"2.0", "id":2, "method":"setcomment",
428            "params":{"frame": 99999, "comment": "meh\nbaz"}
429            },
430            {"jsonrpc":"2.0", "id":3, "method":"setcomment",
431            "params":{"frame": 3, "comment": "foo\nbar"}
432            },
433            {"jsonrpc":"2.0", "id":4, "method":"frame",
434            "params":{"frame": 3}
435            },
436
437        ), (
438            {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}},
439            {"jsonrpc":"2.0","id":2,"error":{"code":-3002,"message":"Frame number is out of range"}},
440            {"jsonrpc":"2.0","id":3,"result":{"status":"OK"}},
441            {"jsonrpc":"2.0","id":4,"result":{"comment":["foo\nbar"],"fol": MatchAny(list)}},
442        ))
443
444    def test_sharkd_req_setconf_bad(self, check_sharkd_session):
445        check_sharkd_session((
446            {"jsonrpc":"2.0", "id":1, "method":"setconf",
447            "params":{"name": "uat:garbage-pref", "value": "\"\""}
448            },
449        ), (
450            {"jsonrpc":"2.0","id":1,"error":{"code":-4005,"message":"Unable to set the preference"}},
451        ))
452
453    def test_sharkd_req_dumpconf_bad(self, check_sharkd_session):
454        check_sharkd_session((
455            {"jsonrpc":"2.0", "id":1, "method":"dumpconf",
456            "params":{"pref": "bad.preference"}
457            },
458            {"jsonrpc":"2.0", "id":2, "method":"dumpconf",
459            "params":{"pref": "invalid-garbage-preference"}
460            },
461            {"jsonrpc":"2.0", "id":3, "method":"dumpconf",
462            "params":{"pref": "uat:custom_http_header_fields"}
463            },
464        ), (
465            {"jsonrpc":"2.0","id":1,"error":{"code":-9001,"message":"Invalid pref bad.preference."}},
466            {"jsonrpc":"2.0","id":2,"error":{"code":-9002,"message":"Invalid pref invalid-garbage-preference."}},
467            {"jsonrpc":"2.0","id":3,"error":{"code":-9002,"message":"Invalid pref uat:custom_http_header_fields."}},
468        ))
469
470    def test_sharkd_req_dumpconf_all(self, check_sharkd_session):
471        check_sharkd_session((
472            {"jsonrpc":"2.0", "id":1, "method":"dumpconf"},
473        ), (
474            {"jsonrpc":"2.0","id":1,"result":{"prefs": MatchObject({"tcp.check_checksum": {"b": 0}})}
475            },
476        ))
477
478    def test_sharkd_req_download_tls_secrets(self, check_sharkd_session, capture_file):
479        # XXX test download for eo: and rtp: too
480        check_sharkd_session((
481            {"jsonrpc":"2.0", "id":1, "method":"load",
482            "params":{"file": capture_file('tls12-dsb.pcapng')}
483            },
484            {"jsonrpc":"2.0", "id":2, "method":"download",
485            "params":{"token": "ssl-secrets"}
486            },
487        ), (
488            {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}},
489            # TODO remove "RSA Session-ID:" support and support "CLIENT_RANDOM "... only
490            {"jsonrpc":"2.0","id":2,"result":{"file": "keylog.txt", "mime": "text/plain",
491                "data": MatchRegExp(r'UlNBIFNlc3Npb24tSUQ6.+')}
492            },
493        ))
494
495    def test_sharkd_req_bye(self, check_sharkd_session):
496        check_sharkd_session((
497            {"jsonrpc":"2.0", "id":1, "method":"bye"},
498        ), (
499            {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}},
500        ))
501
502    def test_sharkd_bad_request(self, check_sharkd_session):
503        check_sharkd_session((
504            {"jsonrpc":"2.0", "id":1, "method":"dud"},
505        ), (
506            {'jsonrpc': '2.0', 'id': 1, 'error': {'code': -32601, 'message': 'The method dud is not supported'}},
507        ))
508
509    def test_sharkd_config(self, check_sharkd_session):
510        check_sharkd_session((
511            {"jsonrpc":"2.0", "id":1, "method":"setconf",
512            "params":{"name": "uat:custom_http_header_fields", "value": "\"X-Header-Name\", \"Description\""}
513            },
514            {"jsonrpc":"2.0", "id":2, "method":"setconf",
515            "params":{"name": "tcp.check_checksum", "value": "true"}
516            },
517            {"jsonrpc":"2.0", "id":3, "method":"dumpconf",
518            "params":{"pref": "tcp.check_checksum"}
519            },
520            {"jsonrpc":"2.0", "id":4, "method":"setconf",
521            "params":{"name": "tcp.check_checksum", "value": "false"}
522            },
523            {"jsonrpc":"2.0", "id":5, "method":"dumpconf",
524            "params":{"pref": "tcp.check_checksum"}
525            },
526        ), (
527            # Check that the UAT preference is set. There is no way to query it
528            # (other than testing for side-effects in dissection).
529            {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}},
530            {"jsonrpc":"2.0","id":2,"result":{"status":"OK"}},
531            {"jsonrpc":"2.0","id":3,"result":{"prefs":{"tcp.check_checksum":{"b":1}}}},
532            {"jsonrpc":"2.0","id":4,"result":{"status":"OK"}},
533            {"jsonrpc":"2.0","id":5,"result":{"prefs":{"tcp.check_checksum":{"b":0}}}},
534        ))
535
536    def test_sharkd_config_enum(self, check_sharkd_session):
537        '''Dump default enum preference value, change it and restore it.'''
538        check_sharkd_session((
539            {"jsonrpc":"2.0", "id":1, "method":"dumpconf",
540            "params":{"pref": "wlan.ignore_wep"}
541            },
542            {"jsonrpc":"2.0", "id":2, "method":"setconf",
543            "params":{"name": "wlan.ignore_wep", "value": "Yes - with IV"}
544            },
545            {"jsonrpc":"2.0", "id":3, "method":"dumpconf",
546            "params":{"pref": "wlan.ignore_wep"}
547            },
548            {"jsonrpc":"2.0", "id":4, "method":"setconf",
549            "params":{"name": "wlan.ignore_wep", "value": "No"}
550            },
551            {"jsonrpc":"2.0", "id":5, "method":"dumpconf",
552            "params":{"pref": "wlan.ignore_wep"}
553            },
554        ), (
555            {"jsonrpc":"2.0","id":1,"result":{"prefs":{"wlan.ignore_wep":{"e":[{"v":0,"s":1,"d":"No"},{"v":1,"d":"Yes - without IV"},{"v":2,"d":"Yes - with IV"}]}}}},
556            {"jsonrpc":"2.0","id":2,"result":{"status":"OK"}},
557            {"jsonrpc":"2.0","id":3,"result":{"prefs":{"wlan.ignore_wep":{"e":[{"v":0,"d":"No"},{"v":1,"d":"Yes - without IV"},{"v":2,"s":1,"d":"Yes - with IV"}]}}}},
558            {"jsonrpc":"2.0","id":4,"result":{"status":"OK"}},
559            {"jsonrpc":"2.0","id":5,"result":{"prefs":{"wlan.ignore_wep":{"e":[{"v":0,"s":1,"d":"No"},{"v":1,"d":"Yes - without IV"},{"v":2,"d":"Yes - with IV"}]}}}},
560        ))
561
562    def test_sharkd_nested_file(self, check_sharkd_session, capture_file):
563        '''Request a frame from a file with a deep level of nesting.'''
564        check_sharkd_session((
565            {"jsonrpc":"2.0", "id":1, "method":"load",
566            "params":{"file": capture_file("http2-data-reassembly.pcap")}
567            },
568            {"jsonrpc":"2.0", "id":2, "method":"frame",
569            "params":{"frame": "4", "proto": "yes"}
570            },
571        ), (
572            {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}},
573            MatchAny(),
574        ))
575