1# 2# Wireshark tests 3# By Gerald Combs <gerald@wireshark.org> 4# 5# Ported from a set of Bash scripts which were copyright 2005 Ulf Lamping 6# 7# SPDX-License-Identifier: GPL-2.0-or-later 8# 9'''Command line option tests''' 10 11import json 12import sys 13import os.path 14import subprocess 15import subprocesstest 16import fixtures 17import shutil 18 19#glossaries = ('fields', 'protocols', 'values', 'decodes', 'defaultprefs', 'currentprefs') 20 21glossaries = ('decodes', 'values') 22testout_pcap = 'testout.pcap' 23 24 25@fixtures.uses_fixtures 26class case_dumpcap_options(subprocesstest.SubprocessTestCase): 27 # XXX Should we generate individual test functions instead of looping? 28 def test_dumpcap_invalid_chars(self, cmd_dumpcap, base_env): 29 '''Invalid dumpcap parameters''' 30 for char_arg in 'CEFGHJKNOQRTUVWXYejloxz': 31 self.assertRun((cmd_dumpcap, '-' + char_arg), env=base_env, 32 expected_return=self.exit_command_line) 33 34 # XXX Should we generate individual test functions instead of looping? 35 def test_dumpcap_valid_chars(self, cmd_dumpcap, base_env): 36 for char_arg in 'hv': 37 self.assertRun((cmd_dumpcap, '-' + char_arg), env=base_env) 38 39 # XXX Should we generate individual test functions instead of looping? 40 def test_dumpcap_interface_chars(self, cmd_dumpcap, base_env): 41 '''Valid dumpcap parameters requiring capture permissions''' 42 valid_returns = [self.exit_ok, self.exit_error] 43 for char_arg in 'DL': 44 process = self.runProcess((cmd_dumpcap, '-' + char_arg), env=base_env) 45 self.assertIn(process.returncode, valid_returns) 46 47 48@fixtures.mark_usefixtures('base_env') 49@fixtures.uses_fixtures 50class case_dumpcap_capture_clopts(subprocesstest.SubprocessTestCase): 51 def test_dumpcap_invalid_capfilter(self, cmd_dumpcap, capture_interface): 52 '''Invalid capture filter''' 53 invalid_filter = '__invalid_protocol' 54 # $DUMPCAP -f 'jkghg' -w './testout.pcap' > ./testout.txt 2>&1 55 testout_file = self.filename_from_id(testout_pcap) 56 self.runProcess((cmd_dumpcap, '-f', invalid_filter, '-w', testout_file)) 57 self.assertTrue(self.grepOutput('Invalid capture filter "' + invalid_filter + '" for interface')) 58 59 def test_dumpcap_invalid_interface_name(self, cmd_dumpcap, capture_interface): 60 '''Invalid capture interface name''' 61 invalid_interface = '__invalid_interface' 62 # $DUMPCAP -i invalid_interface -w './testout.pcap' > ./testout.txt 2>&1 63 testout_file = self.filename_from_id(testout_pcap) 64 self.runProcess((cmd_dumpcap, '-i', invalid_interface, '-w', testout_file)) 65 self.assertTrue(self.grepOutput('The capture session could not be initiated')) 66 67 def test_dumpcap_invalid_interface_index(self, cmd_dumpcap, capture_interface): 68 '''Invalid capture interface index''' 69 invalid_index = '0' 70 # $DUMPCAP -i 0 -w './testout.pcap' > ./testout.txt 2>&1 71 testout_file = self.filename_from_id(testout_pcap) 72 self.runProcess((cmd_dumpcap, '-i', invalid_index, '-w', testout_file)) 73 self.assertTrue(self.grepOutput('There is no interface with that adapter index')) 74 75 76@fixtures.mark_usefixtures('test_env') 77@fixtures.uses_fixtures 78class case_basic_clopts(subprocesstest.SubprocessTestCase): 79 def test_existing_file(self, cmd_tshark, capture_file): 80 # $TSHARK -r "${CAPTURE_DIR}dhcp.pcap" > ./testout.txt 2>&1 81 self.assertRun((cmd_tshark, '-r', capture_file('dhcp.pcap'))) 82 83 def test_nonexistent_file(self, cmd_tshark, capture_file): 84 # $TSHARK - r ThisFileDontExist.pcap > ./testout.txt 2 > &1 85 self.assertRun((cmd_tshark, '-r', capture_file('__ceci_nest_pas_une.pcap')), 86 expected_return=self.exit_error) 87 88 89@fixtures.mark_usefixtures('test_env') 90@fixtures.uses_fixtures 91class case_tshark_options(subprocesstest.SubprocessTestCase): 92 # XXX Should we generate individual test functions instead of looping? 93 def test_tshark_invalid_chars(self, cmd_tshark): 94 '''Invalid tshark parameters''' 95 for char_arg in 'ABCEFHJKMNORTUWXYZabcdefijkmorstuwyz': 96 self.assertRun((cmd_tshark, '-' + char_arg), 97 expected_return=self.exit_command_line) 98 99 # XXX Should we generate individual test functions instead of looping? 100 def test_tshark_valid_chars(self, cmd_tshark): 101 for char_arg in 'Ghv': 102 self.assertRun((cmd_tshark, '-' + char_arg)) 103 104 # XXX Should we generate individual test functions instead of looping? 105 def test_tshark_interface_chars(self, cmd_tshark, cmd_dumpcap): 106 '''Valid tshark parameters requiring capture permissions''' 107 # These options require dumpcap 108 valid_returns = [self.exit_ok, self.exit_error] 109 for char_arg in 'DL': 110 process = self.runProcess((cmd_tshark, '-' + char_arg)) 111 self.assertIn(process.returncode, valid_returns) 112 113 114@fixtures.mark_usefixtures('test_env') 115@fixtures.uses_fixtures 116class case_tshark_capture_clopts(subprocesstest.SubprocessTestCase): 117 def test_tshark_invalid_capfilter(self, cmd_tshark, capture_interface): 118 '''Invalid capture filter''' 119 invalid_filter = '__invalid_protocol' 120 # $TSHARK -f 'jkghg' -w './testout.pcap' > ./testout.txt 2>&1 121 testout_file = self.filename_from_id(testout_pcap) 122 self.runProcess((cmd_tshark, '-f', invalid_filter, '-w', testout_file )) 123 self.assertTrue(self.grepOutput('Invalid capture filter "' + invalid_filter + '" for interface')) 124 125 def test_tshark_invalid_interface_name(self, cmd_tshark, capture_interface): 126 '''Invalid capture interface name''' 127 invalid_interface = '__invalid_interface' 128 # $TSHARK -i invalid_interface -w './testout.pcap' > ./testout.txt 2>&1 129 testout_file = self.filename_from_id(testout_pcap) 130 self.runProcess((cmd_tshark, '-i', invalid_interface, '-w', testout_file)) 131 self.assertTrue(self.grepOutput('The capture session could not be initiated')) 132 133 def test_tshark_invalid_interface_index(self, cmd_tshark, capture_interface): 134 '''Invalid capture interface index''' 135 invalid_index = '0' 136 # $TSHARK -i 0 -w './testout.pcap' > ./testout.txt 2>&1 137 testout_file = self.filename_from_id(testout_pcap) 138 self.runProcess((cmd_tshark, '-i', invalid_index, '-w', testout_file)) 139 self.assertTrue(self.grepOutput('There is no interface with that adapter index')) 140 141 142@fixtures.mark_usefixtures('test_env') 143@fixtures.uses_fixtures 144class case_tshark_name_resolution_clopts(subprocesstest.SubprocessTestCase): 145 def test_tshark_valid_name_resolution(self, cmd_tshark, capture_interface): 146 # $TSHARK -N mnNtdv -a duration:1 > ./testout.txt 2>&1 147 self.assertRun((cmd_tshark, '-N', 'mnNtdv', '-a', 'duration: 1')) 148 149 # XXX Add invalid name resolution. 150 151@fixtures.mark_usefixtures('test_env') 152@fixtures.uses_fixtures 153class case_tshark_unicode_clopts(subprocesstest.SubprocessTestCase): 154 def test_tshark_unicode_display_filter(self, cmd_tshark, capture_file): 155 '''Unicode (UTF-8) display filter''' 156 self.assertRun((cmd_tshark, '-r', capture_file('http.pcap'), '-Y', 'tcp.flags.str == "·······AP···"')) 157 self.assertTrue(self.grepOutput('HEAD.*/v4/iuident.cab')) 158 159 160@fixtures.uses_fixtures 161class case_tshark_dump_glossaries(subprocesstest.SubprocessTestCase): 162 def test_tshark_dump_glossary(self, cmd_tshark, base_env): 163 for glossary in glossaries: 164 try: 165 self.log_fd.truncate() 166 except Exception: 167 pass 168 self.assertRun((cmd_tshark, '-G', glossary), env=base_env, max_lines=20) 169 self.assertEqual(self.countOutput(count_stdout=False, count_stderr=True), 0, 'Found error output while printing glossary ' + glossary) 170 171 def test_tshark_glossary_valid_utf8(self, cmd_tshark, base_env): 172 for glossary in glossaries: 173 env = base_env 174 env['LANG'] = 'en_US.UTF-8' 175 g_contents = subprocess.check_output((cmd_tshark, '-G', glossary), env=env, stderr=subprocess.PIPE) 176 decoded = True 177 try: 178 g_contents.decode('UTF-8') 179 except UnicodeDecodeError: 180 decoded = False 181 self.assertTrue(decoded, '{} is not valid UTF-8'.format(glossary)) 182 183 def test_tshark_glossary_plugin_count(self, cmd_tshark, base_env, features): 184 if not features.have_plugins: 185 self.skipTest('Test requires binary plugin support.') 186 self.assertRun((cmd_tshark, '-G', 'plugins'), env=base_env) 187 self.assertGreaterEqual(self.countOutput('dissector'), 10, 'Fewer than 10 dissector plugins found') 188 189 def test_tshark_elastic_mapping(self, cmd_tshark, dirs, base_env): 190 def get_ip_props(obj): 191 return obj['mappings']['doc']['properties']['layers']['properties']['ip']['properties'] 192 self.maxDiff = None 193 baseline_file = os.path.join(dirs.baseline_dir, 'elastic-mapping-ip-subset.json') 194 with open(baseline_file) as f: 195 expected_obj = json.load(f) 196 keys_to_check = get_ip_props(expected_obj).keys() 197 proc = self.assertRun((cmd_tshark, '-G', 'elastic-mapping', '--elastic-mapping-filter', 'ip')) 198 actual_obj = json.loads(proc.stdout_str) 199 ip_props = get_ip_props(actual_obj) 200 for key in list(ip_props.keys()): 201 if key not in keys_to_check: 202 del ip_props[key] 203 self.assertEqual(actual_obj, expected_obj) 204 205 def test_tshark_unicode_folders(self, cmd_tshark, unicode_env, features): 206 '''Folders output with unicode''' 207 if not features.have_lua: 208 self.skipTest('Test requires Lua scripting support.') 209 proc = self.assertRun((cmd_tshark, '-G', 'folders'), env=unicode_env.env) 210 out = proc.stdout_str 211 pluginsdir = [x.split('\t', 1)[1] for x in out.splitlines() if x.startswith('Personal Lua Plugins:')] 212 self.assertEqual([unicode_env.pluginsdir], pluginsdir) 213 214 215@fixtures.mark_usefixtures('test_env') 216@fixtures.uses_fixtures 217class case_tshark_z_expert(subprocesstest.SubprocessTestCase): 218 def test_tshark_z_expert_all(self, cmd_tshark, capture_file): 219 self.assertRun((cmd_tshark, '-q', '-z', 'expert', 220 '-r', capture_file('http-ooo.pcap'))) 221 self.assertTrue(self.grepOutput('Errors')) 222 self.assertTrue(self.grepOutput('Warns')) 223 self.assertTrue(self.grepOutput('Chats')) 224 225 def test_tshark_z_expert_error(self, cmd_tshark, capture_file): 226 self.assertRun((cmd_tshark, '-q', '-z', 'expert,error', 227 '-r', capture_file('http-ooo.pcap'))) 228 self.assertTrue(self.grepOutput('Errors')) 229 self.assertFalse(self.grepOutput('Warns')) 230 self.assertFalse(self.grepOutput('Chats')) 231 232 def test_tshark_z_expert_warn(self, cmd_tshark, capture_file): 233 self.assertRun((cmd_tshark, '-q', '-z', 'expert,warn', 234 '-r', capture_file('http-ooo.pcap'))) 235 self.assertTrue(self.grepOutput('Errors')) 236 self.assertTrue(self.grepOutput('Warns')) 237 self.assertFalse(self.grepOutput('Chats')) 238 239 def test_tshark_z_expert_note(self, cmd_tshark, capture_file): 240 self.assertRun((cmd_tshark, '-q', '-z', 'expert,note', 241 '-r', capture_file('http2-data-reassembly.pcap'))) 242 self.assertTrue(self.grepOutput('Warns')) 243 self.assertTrue(self.grepOutput('Notes')) 244 self.assertFalse(self.grepOutput('Chats')) 245 246 def test_tshark_z_expert_chat(self, cmd_tshark, capture_file): 247 self.assertRun((cmd_tshark, '-q', '-z', 'expert,chat', 248 '-r', capture_file('http-ooo.pcap'))) 249 self.assertTrue(self.grepOutput('Errors')) 250 self.assertTrue(self.grepOutput('Warns')) 251 self.assertTrue(self.grepOutput('Chats')) 252 253 def test_tshark_z_expert_comment(self, cmd_tshark, capture_file): 254 self.assertRun((cmd_tshark, '-q', '-z', 'expert,comment', 255 '-r', capture_file('sip.pcapng'))) 256 self.assertTrue(self.grepOutput('Notes')) 257 self.assertTrue(self.grepOutput('Comments')) 258 259 def test_tshark_z_expert_invalid_filter(self, cmd_tshark, capture_file): 260 invalid_filter = '__invalid_protocol' 261 self.assertRun((cmd_tshark, '-q', '-z', 'expert,' + invalid_filter, 262 '-r', capture_file('http-ooo.pcap')), 263 expected_return=self.exit_command_line) 264 self.assertTrue(self.grepOutput('Filter "' + invalid_filter + '" is invalid')) 265 266 def test_tshark_z_expert_error_invalid_filter(self, cmd_tshark, capture_file): 267 invalid_filter = '__invalid_protocol' 268 self.assertRun((cmd_tshark, '-q', '-z', 'expert,error,' + invalid_filter, 269 '-r', capture_file('http-ooo.pcap')), 270 expected_return=self.exit_command_line) 271 self.assertTrue(self.grepOutput('Filter "' + invalid_filter + '" is invalid')) 272 273 def test_tshark_z_expert_filter(self, cmd_tshark, capture_file): 274 self.assertRun((cmd_tshark, '-q', '-z', 'expert,udp', # udp is a filter 275 '-r', capture_file('http-ooo.pcap'))) 276 self.assertFalse(self.grepOutput('Errors')) 277 self.assertFalse(self.grepOutput('Warns')) 278 self.assertFalse(self.grepOutput('Chats')) 279 280 def test_tshark_z_expert_error_filter(self, cmd_tshark, capture_file): 281 self.assertRun((cmd_tshark, '-q', '-z', 'expert,error,udp', # udp is a filter 282 '-r', capture_file('http-ooo.pcap'))) 283 self.assertFalse(self.grepOutput('Errors')) 284 self.assertFalse(self.grepOutput('Warns')) 285 self.assertFalse(self.grepOutput('Chats')) 286 287 288@fixtures.mark_usefixtures('test_env') 289@fixtures.uses_fixtures 290class case_tshark_extcap(subprocesstest.SubprocessTestCase): 291 # dumpcap dependency has been added to run this test only with capture support 292 def test_tshark_extcap_interfaces(self, cmd_tshark, cmd_dumpcap, test_env, home_path): 293 # Script extcaps don't work with the current code on windows. 294 # https://www.wireshark.org/docs/wsdg_html_chunked/ChCaptureExtcap.html 295 # TODO: skip this test until it will get fixed. 296 if sys.platform == 'win32': 297 self.skipTest('FIXME extcap .py scripts needs special treatment on Windows') 298 extcap_dir_path = os.path.join(home_path, 'extcap') 299 os.makedirs(extcap_dir_path) 300 test_env['WIRESHARK_EXTCAP_DIR'] = extcap_dir_path 301 source_file = os.path.join(os.path.dirname(__file__), 'sampleif.py') 302 shutil.copy2(source_file, extcap_dir_path) 303 # Ensure the test extcap_tool is properly loaded 304 self.assertRun((cmd_tshark, '-D'), env=test_env) 305 self.assertEqual(1, self.countOutput('sampleif')) 306 # Ensure tshark lists 2 interfaces in the preferences 307 self.assertRun((cmd_tshark, '-G', 'currentprefs'), env=test_env) 308 self.assertEqual(2, self.countOutput('extcap.sampleif.test')) 309