summaryrefslogtreecommitdiffstats
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/README.test20
-rw-r--r--test/baseline/communityid-filtered.txt2
-rw-r--r--test/baseline/communityid.txt105
-rw-r--r--test/baseline/dhcp-filter.ek8
-rw-r--r--test/baseline/dhcp-raw.ek8
-rw-r--r--test/baseline/dhcp.ek8
-rw-r--r--test/baseline/dhcp.json644
-rw-r--r--test/baseline/dhcp.jsonraw3592
-rw-r--r--test/baseline/elastic-mapping-ip-subset.json306
-rw-r--r--test/baseline/ff-ts-usec-pcap-direct.txt4
-rw-r--r--test/baseline/io-rawshark-dhcp-pcap.txt5
-rw-r--r--test/captures/arp.pcapbin0 -> 70 bytes
-rw-r--r--test/captures/c1222_std_example8.pcapbin0 -> 319 bytes
-rw-r--r--test/captures/challenge01_ooo_stream.pcapng.gzbin0 -> 1522510 bytes
-rw-r--r--test/captures/communityid.pcap.gzbin0 -> 3117 bytes
-rw-r--r--test/captures/cose_encrypt0_tagged.cbordiag12
-rw-r--r--test/captures/cose_encrypt0_tagged.pcapbin0 -> 255 bytes
-rw-r--r--test/captures/cose_encrypt_tagged.cbordiag24
-rw-r--r--test/captures/cose_encrypt_tagged.pcapbin0 -> 294 bytes
-rw-r--r--test/captures/cose_keyset.cbordiag19
-rw-r--r--test/captures/cose_keyset.pcapbin0 -> 439 bytes
-rw-r--r--test/captures/cose_mac0_tagged.cbordiag10
-rw-r--r--test/captures/cose_mac0_tagged.pcapbin0 -> 240 bytes
-rw-r--r--test/captures/cose_mac_tagged.cbordiag40
-rw-r--r--test/captures/cose_mac_tagged.pcapbin0 -> 513 bytes
-rw-r--r--test/captures/cose_sign1_tagged.cbordiag14
-rw-r--r--test/captures/cose_sign1_tagged.pcapbin0 -> 301 bytes
-rw-r--r--test/captures/cose_sign_tagged.cbordiag39
-rw-r--r--test/captures/cose_sign_tagged.pcapbin0 -> 503 bytes
-rw-r--r--test/captures/data-utf8.pcapbin0 -> 125 bytes
-rw-r--r--test/captures/dhcp-nanosecond.pcapbin0 -> 1400 bytes
-rw-r--r--test/captures/dhcp-nanosecond.pcapngbin0 -> 1640 bytes
-rw-r--r--test/captures/dhcp.pcapbin0 -> 1400 bytes
-rw-r--r--test/captures/dhcp.pcapngbin0 -> 1508 bytes
-rw-r--r--test/captures/dhe1.pcapng.gzbin0 -> 1180 bytes
-rw-r--r--test/captures/dmgr.pcapngbin0 -> 20452 bytes
-rw-r--r--test/captures/dns+icmp.pcapng.gzbin0 -> 1588 bytes
-rw-r--r--test/captures/dns-mdns.pcapbin0 -> 72858 bytes
-rw-r--r--test/captures/dns-ooo.pcapbin0 -> 418 bytes
-rw-r--r--test/captures/dns_port.pcapbin0 -> 1318 bytes
-rw-r--r--test/captures/dtls12-aes128ccm8-dsb.pcapngbin0 -> 2044 bytes
-rw-r--r--test/captures/dtls12-aes128ccm8.pcapbin0 -> 1535 bytes
-rw-r--r--test/captures/dtn_tcpclv3_bpv6_transfer.pcapngbin0 -> 4768 bytes
-rw-r--r--test/captures/dtn_tcpclv4_bpv7_transfer.pcapngbin0 -> 3960 bytes
-rw-r--r--test/captures/dtn_udpcl_bpv7_bpsec_bcb_admin.cbordiag7
-rw-r--r--test/captures/dtn_udpcl_bpv7_bpsec_bcb_admin.pcapngbin0 -> 656 bytes
-rw-r--r--test/captures/dtn_udpcl_bpv7_bpsec_bib_admin.cbordiag7
-rw-r--r--test/captures/dtn_udpcl_bpv7_bpsec_bib_admin.pcapngbin0 -> 652 bytes
-rw-r--r--test/captures/dvb-ci_UV1_0000.pcapbin0 -> 101 bytes
-rw-r--r--test/captures/empty.pcapbin0 -> 24 bytes
-rw-r--r--test/captures/esp-bug-12671.pcapng.gzbin0 -> 1136 bytes
-rw-r--r--test/captures/gitOverTCP.pcapbin0 -> 582 bytes
-rw-r--r--test/captures/grpc_person_search_json_with_image.pcapng.gzbin0 -> 1573 bytes
-rw-r--r--test/captures/grpc_person_search_protobuf_with_image-missing_headers.pcapng.gzbin0 -> 746 bytes
-rw-r--r--test/captures/grpc_person_search_protobuf_with_image.pcapng.gzbin0 -> 1433 bytes
-rw-r--r--test/captures/grpc_stream_reassembly_sample.pcapng.gzbin0 -> 6642 bytes
-rw-r--r--test/captures/grpc_web.pcapng.gzbin0 -> 19366 bytes
-rw-r--r--test/captures/http-brotli.pcapngbin0 -> 1840 bytes
-rw-r--r--test/captures/http-ooo-fuzzed.pcapngbin0 -> 1584 bytes
-rw-r--r--test/captures/http-ooo.pcapbin0 -> 1209 bytes
-rw-r--r--test/captures/http-ooo2.pcapbin0 -> 536 bytes
-rw-r--r--test/captures/http.pcapbin0 -> 247 bytes
-rw-r--r--test/captures/http2-brotli.pcapngbin0 -> 6484 bytes
-rw-r--r--test/captures/http2-data-reassembly.pcapbin0 -> 82151 bytes
-rw-r--r--test/captures/http2_follow_multistream.pcapngbin0 -> 247716 bytes
-rw-r--r--test/captures/http3-qpack-reassembly-anon.pcapngbin0 -> 5988 bytes
-rw-r--r--test/captures/icmp.pcapng.gzbin0 -> 313 bytes
-rw-r--r--test/captures/ieee802.3cb-ping.pcapng.gzbin0 -> 850 bytes
-rw-r--r--test/captures/ikev1-bug-12610.pcapng.gzbin0 -> 2921 bytes
-rw-r--r--test/captures/ikev1-bug-12620.pcapng.gzbin0 -> 2011 bytes
-rw-r--r--test/captures/ikev1-certs.pcapbin0 -> 6015 bytes
-rw-r--r--test/captures/ikev2-decrypt-3des-sha1_160.pcapbin0 -> 1860 bytes
-rw-r--r--test/captures/ikev2-decrypt-aes128ccm12-2.pcapbin0 -> 1416 bytes
-rw-r--r--test/captures/ikev2-decrypt-aes128ccm12.pcapbin0 -> 1432 bytes
-rw-r--r--test/captures/ikev2-decrypt-aes192ctr.pcapbin0 -> 1512 bytes
-rw-r--r--test/captures/ikev2-decrypt-aes256cbc.pcapngbin0 -> 1792 bytes
-rw-r--r--test/captures/ikev2-decrypt-aes256ccm16.pcapngbin0 -> 1728 bytes
-rw-r--r--test/captures/ikev2-decrypt-aes256gcm16.pcapbin0 -> 1448 bytes
-rw-r--r--test/captures/ikev2-decrypt-aes256gcm8.pcapbin0 -> 1400 bytes
-rw-r--r--test/captures/ipoipoip.pcapbin0 -> 204 bytes
-rw-r--r--test/captures/ipv6.pcapbin0 -> 126 bytes
-rw-r--r--test/captures/ipx_rip.pcapbin0 -> 100 bytes
-rw-r--r--test/captures/knxip_DataSec.pcapbin0 -> 125 bytes
-rw-r--r--test/captures/knxip_SecureWrapper.pcapbin0 -> 137 bytes
-rw-r--r--test/captures/knxip_TimerNotify.pcapbin0 -> 118 bytes
-rw-r--r--test/captures/krb-816.pcap.gzbin0 -> 21013 bytes
-rw-r--r--test/captures/logistics_multicast.pcapngbin0 -> 139980 bytes
-rw-r--r--test/captures/many_interfaces.pcapng.1bin0 -> 20888 bytes
-rw-r--r--test/captures/many_interfaces.pcapng.2bin0 -> 3392 bytes
-rw-r--r--test/captures/many_interfaces.pcapng.3bin0 -> 3704 bytes
-rw-r--r--test/captures/mongo-zstd.pcapngbin0 -> 568 bytes
-rw-r--r--test/captures/netperfmeter.pcapng.gzbin0 -> 120209 bytes
-rw-r--r--test/captures/nfs.pcapbin0 -> 418 bytes
-rw-r--r--test/captures/ntp.pcapbin0 -> 130 bytes
-rw-r--r--test/captures/owe.pcapng.gzbin0 -> 5634 bytes
-rw-r--r--test/captures/packet-h2-14_headers.pcapngbin0 -> 25280 bytes
-rw-r--r--test/captures/protobuf_tcp_addressbook.pcapng.gzbin0 -> 927 bytes
-rw-r--r--test/captures/protobuf_test_default_value.pcapngbin0 -> 476 bytes
-rw-r--r--test/captures/protobuf_test_leading_dot.pcapngbin0 -> 584 bytes
-rw-r--r--test/captures/protobuf_test_map_and_oneof_types.pcapngbin0 -> 616 bytes
-rw-r--r--test/captures/protobuf_udp_addressbook_with_image_ts.pcapngbin0 -> 736 bytes
-rw-r--r--test/captures/protohier-with-comments.pcapngbin0 -> 26188 bytes
-rw-r--r--test/captures/protohier-without-comments.pcapngbin0 -> 26136 bytes
-rw-r--r--test/captures/quic-fragmented-handshakes.pcapng.gzbin0 -> 32804 bytes
-rw-r--r--test/captures/quic_follow_multistream.pcapngbin0 -> 982848 bytes
-rw-r--r--test/captures/retrans-tls.pcapbin0 -> 158 bytes
-rw-r--r--test/captures/rsa-p-lt-q.pcapbin0 -> 2111 bytes
-rw-r--r--test/captures/rsasnakeoil2.pcapbin0 -> 25057 bytes
-rw-r--r--test/captures/s7comm-fuzz.pcapng.gzbin0 -> 235 bytes
-rw-r--r--test/captures/sample_control4_2012-03-24.pcapbin0 -> 10949 bytes
-rw-r--r--test/captures/segmented_fpm.pcapbin0 -> 33144 bytes
-rw-r--r--test/captures/sip-rtp.pcapngbin0 -> 144300 bytes
-rw-r--r--test/captures/sip.pcapngbin0 -> 3636 bytes
-rw-r--r--test/captures/sipmsg.log136
-rw-r--r--test/captures/smb300-aes-128-ccm.pcap.gzbin0 -> 1235 bytes
-rw-r--r--test/captures/smb311-aes-128-ccm.pcap.gzbin0 -> 1327 bytes
-rw-r--r--test/captures/smb311-aes-128-gcm.pcap.gzbin0 -> 1380 bytes
-rw-r--r--test/captures/smb311-aes-256-ccm.pcap.gzbin0 -> 1526 bytes
-rw-r--r--test/captures/smb311-aes-256-gcm.pcap.gzbin0 -> 1527 bytes
-rw-r--r--test/captures/smb311-chained-patternv1-lznt1.pcapng.gzbin0 -> 717 bytes
-rw-r--r--test/captures/smb311-lz77-lz77huff-lznt1.pcap.gzbin0 -> 446 bytes
-rw-r--r--test/captures/snakeoil-dtls.pcapbin0 -> 2327 bytes
-rw-r--r--test/captures/tcp-badsegments.pcapbin0 -> 1732 bytes
-rw-r--r--test/captures/tcp-exp-option-tarr.pcap.gzbin0 -> 682 bytes
-rw-r--r--test/captures/text2pcap_hash_eol.txt10
-rw-r--r--test/captures/tftp.pcapbin0 -> 117 bytes
-rw-r--r--test/captures/tls-fragmented-handshakes.pcap.gzbin0 -> 16911 bytes
-rw-r--r--test/captures/tls-fragmented-over-tcp-segmented.pcapng.gzbin0 -> 22304 bytes
-rw-r--r--test/captures/tls-over-tls.pcapng.gzbin0 -> 31628 bytes
-rw-r--r--test/captures/tls-renegotiation.pcapbin0 -> 12935 bytes
-rw-r--r--test/captures/tls12-aes128ccm.pcapbin0 -> 2057 bytes
-rw-r--r--test/captures/tls12-aes256gcm.pcapbin0 -> 2064 bytes
-rw-r--r--test/captures/tls12-chacha20poly1305.pcapbin0 -> 43693 bytes
-rw-r--r--test/captures/tls12-dsb.pcapngbin0 -> 10260 bytes
-rw-r--r--test/captures/tls13-20-chacha20poly1305.pcapbin0 -> 10336 bytes
-rw-r--r--test/captures/tls13-rfc8446.pcapbin0 -> 4158 bytes
-rw-r--r--test/captures/trunc.pcapbin0 -> 995 bytes
-rw-r--r--test/captures/udt-dtls.pcapng.gzbin0 -> 16379 bytes
-rw-r--r--test/captures/websocket-compressed-fragmented.pcapng.gzbin0 -> 2023 bytes
-rw-r--r--test/captures/websocket-compressed.pcapng.gzbin0 -> 1583 bytes
-rw-r--r--test/captures/websocket-fragmented.pcapng.gzbin0 -> 1810 bytes
-rw-r--r--test/captures/websocket.pcapng.gzbin0 -> 1388 bytes
-rw-r--r--test/captures/wep.pcapng.gzbin0 -> 3147 bytes
-rw-r--r--test/captures/wireguard-ping-tcp-dsb.pcapngbin0 -> 5912 bytes
-rw-r--r--test/captures/wireguard-ping-tcp.pcapbin0 -> 5120 bytes
-rw-r--r--test/captures/wireguard-psk.pcapbin0 -> 736 bytes
-rw-r--r--test/captures/wpa-Induction.pcap.gzbin0 -> 77052 bytes
-rw-r--r--test/captures/wpa-ccmp-256.pcapng.gzbin0 -> 5261 bytes
-rw-r--r--test/captures/wpa-eap-tls.pcap.gzbin0 -> 25629 bytes
-rw-r--r--test/captures/wpa-gcmp-256.pcapng.gzbin0 -> 5079 bytes
-rw-r--r--test/captures/wpa-gcmp.pcapng.gzbin0 -> 5584 bytes
-rw-r--r--test/captures/wpa-test-decode-mgmt.pcap.gzbin0 -> 903 bytes
-rw-r--r--test/captures/wpa-test-decode-tdls.pcap.gzbin0 -> 3423 bytes
-rw-r--r--test/captures/wpa-test-decode.pcap.gzbin0 -> 167294 bytes
-rw-r--r--test/captures/wpa1-gtk-rekey.pcapng.gzbin0 -> 7114 bytes
-rw-r--r--test/captures/wpa2-ft-eap.pcapng.gzbin0 -> 4927 bytes
-rw-r--r--test/captures/wpa2-ft-psk.pcapng.gzbin0 -> 5074 bytes
-rw-r--r--test/captures/wpa2-psk-mfp.pcapng.gzbin0 -> 3128 bytes
-rw-r--r--test/captures/wpa3-sae.pcapng.gzbin0 -> 5981 bytes
-rw-r--r--test/captures/wpa3-suiteb-192.pcapng.gzbin0 -> 5109 bytes
-rw-r--r--test/captures/wpa_ptk_extended_key_id.pcap.gzbin0 -> 20462 bytes
-rw-r--r--test/config/80211_keys.tmpl12
-rw-r--r--test/config/80211_keys.user_tk_tmpl12
-rw-r--r--test/config/c1222_decryption_table.tmpl2
-rw-r--r--test/config/dtlsdecrypttablefile.tmpl1
-rw-r--r--test/config/esp_sa.tmpl2
-rw-r--r--test/config/ikev1_decryption_table.tmpl4
-rw-r--r--test/config/ikev2_decryption_table.tmpl10
-rw-r--r--test/config/ssl_keys.tmpl2
-rw-r--r--test/conftest.py33
-rw-r--r--test/fixtures_ws.py388
-rw-r--r--test/hosts.custom4
-rw-r--r--test/hosts.global4
-rw-r--r--test/hosts.personal4
-rw-r--r--test/keys/dhe1_keylog.dat2
-rw-r--r--test/keys/http2-data-reassembly.keys1
-rw-r--r--test/keys/key.p12bin0 -> 3842 bytes
-rw-r--r--test/keys/knx_keyring.xml58
-rw-r--r--test/keys/krb-816.keytabbin0 -> 97 bytes
-rw-r--r--test/keys/rsa-p-lt-q.key15
-rw-r--r--test/keys/rsa-p-lt-q.p816
-rw-r--r--test/keys/rsasnakeoil2.key19
-rw-r--r--test/keys/snakeoil-rsa.key15
-rw-r--r--test/keys/tls-over-tls.key27
-rw-r--r--test/keys/tls12-chacha20poly1305.keys9
-rw-r--r--test/keys/tls12-dsb-1.keys2
-rw-r--r--test/keys/tls12-dsb-2.keys1
-rw-r--r--test/keys/tls13-20-chacha20poly1305.keys9
-rw-r--r--test/keys/tls13-rfc8446-noearly.keys8
-rw-r--r--test/keys/tls13-rfc8446.keys9
-rw-r--r--test/keys/udt-dtls.key28
-rw-r--r--test/lua/acme_file.lua1453
-rw-r--r--test/lua/add_packet_field.lua899
-rw-r--r--test/lua/byte_array.lua215
-rw-r--r--test/lua/dir.lua195
-rw-r--r--test/lua/dissectFPM.lua452
-rw-r--r--test/lua/dissector.lua659
-rw-r--r--test/lua/field.lua165
-rw-r--r--test/lua/field_setup.lua108
-rw-r--r--test/lua/globals_2.2.txt1221
-rw-r--r--test/lua/inspect.lua715
-rw-r--r--test/lua/int64.lua360
-rw-r--r--test/lua/listener.lua246
-rw-r--r--test/lua/nstime.lua140
-rw-r--r--test/lua/pcap_file.lua752
-rw-r--r--test/lua/pinfo.lua220
-rw-r--r--test/lua/proto.lua211
-rw-r--r--test/lua/protobuf_test_called_by_custom_dissector.lua68
-rw-r--r--test/lua/protobuf_test_field_subdissector_table.lua6
-rw-r--r--test/lua/protofield.lua236
-rw-r--r--test/lua/script_args.lua24
-rw-r--r--test/lua/struct.lua367
-rw-r--r--test/lua/testlib.lua174
-rw-r--r--test/lua/try_heuristics.lua61
-rw-r--r--test/lua/tvb.lua922
-rw-r--r--test/lua/unicode.lua55
-rw-r--r--test/lua/util.lua118
-rw-r--r--test/lua/verify_dissector.lua380
-rw-r--r--test/lua/verify_globals.lua135
-rw-r--r--test/matchers.py66
-rw-r--r--test/protobuf_lang_files/complex_proto_files/complex_syntax.proto82
-rw-r--r--test/protobuf_lang_files/complex_proto_files/unittest_custom_options.proto395
-rw-r--r--test/protobuf_lang_files/user_defined_types/addressbook.proto30
-rw-r--r--test/protobuf_lang_files/user_defined_types/greet.proto22
-rw-r--r--test/protobuf_lang_files/user_defined_types/person_search_service.proto15
-rw-r--r--test/protobuf_lang_files/user_defined_types/test_default_value.proto59
-rw-r--r--test/protobuf_lang_files/user_defined_types/test_leading_dot.proto24
-rw-r--r--test/protobuf_lang_files/user_defined_types/test_map.proto17
-rw-r--r--test/protobuf_lang_files/well_know_types/google/protobuf/any.proto22
-rw-r--r--test/protobuf_lang_files/well_know_types/google/protobuf/descriptor.proto282
-rw-r--r--test/protobuf_lang_files/well_know_types/google/protobuf/timestamp.proto20
-rwxr-xr-xtest/sampleif.py59
-rw-r--r--test/subprocesstest.py125
-rw-r--r--test/suite_capture.py599
-rw-r--r--test/suite_clopts.py329
-rw-r--r--test/suite_decryption.py1437
-rw-r--r--test/suite_dfilter/__init__.py8
-rw-r--r--test/suite_dfilter/dfiltertest.py115
-rw-r--r--test/suite_dfilter/group_bytes.py25
-rw-r--r--test/suite_dfilter/group_columns.py54
-rw-r--r--test/suite_dfilter/group_double.py91
-rw-r--r--test/suite_dfilter/group_ether.py108
-rw-r--r--test/suite_dfilter/group_function.py87
-rw-r--r--test/suite_dfilter/group_integer.py195
-rw-r--r--test/suite_dfilter/group_ipv4.py130
-rw-r--r--test/suite_dfilter/group_ipv6.py173
-rw-r--r--test/suite_dfilter/group_membership.py112
-rw-r--r--test/suite_dfilter/group_scanner.py34
-rw-r--r--test/suite_dfilter/group_slice.py94
-rw-r--r--test/suite_dfilter/group_string.py240
-rw-r--r--test/suite_dfilter/group_syntax.py487
-rw-r--r--test/suite_dfilter/group_time.py148
-rw-r--r--test/suite_dfilter/group_tvb.py57
-rw-r--r--test/suite_dissection.py929
-rw-r--r--test/suite_dissectors/dissectorstest.py116
-rw-r--r--test/suite_dissectors/group_asterix.py3875
-rw-r--r--test/suite_dissectors/group_netperfmeter.py430
-rw-r--r--test/suite_extcaps.py90
-rw-r--r--test/suite_fileformats.py242
-rw-r--r--test/suite_follow.py342
-rw-r--r--test/suite_follow_dccp.py87
-rw-r--r--test/suite_follow_multistream.py62
-rw-r--r--test/suite_io.py83
-rw-r--r--test/suite_mergecap.py270
-rw-r--r--test/suite_nameres.py106
-rw-r--r--test/suite_outputformats.py92
-rw-r--r--test/suite_release.py47
-rw-r--r--test/suite_sharkd.py1492
-rw-r--r--test/suite_text2pcap.py549
-rw-r--r--test/suite_unittests.py105
-rw-r--r--test/suite_wslua.py313
-rwxr-xr-xtest/travis-upload-artifacts.sh34
-rwxr-xr-xtest/util_dump_dhcp_pcap.py45
273 files changed, 31324 insertions, 0 deletions
diff --git a/test/README.test b/test/README.test
new file mode 100644
index 0000000..a8ec1cf
--- /dev/null
+++ b/test/README.test
@@ -0,0 +1,20 @@
+Wireshark Tests
+
+The recommended steps to prepare for and to run tests:
+
+* Install two Python packages, pytest: `pip install pytest pytest-xdist`
+* Build programs (“wireshark”, “tshark”, etc.): `ninja`
+* Build additional programs for the “unittests” suite: `ninja test-programs`
+* Run tests in the build directory: `pytest`
+
+Replace `ninja test-programs` by `make test-programs` as needed.
+
+See the “Wireshark Tests” chapter of the Developer's Guide for details:
+https://www.wireshark.org/docs/wsdg_html_chunked/ChapterTests.html
+
+If you need to update the baseline files use the following commands (on a Linux system)
+mkdir ~/.config/wireshark/profiles/ctest
+TZ=UTC WIRESHARK_RUN_FROM_BUILD_DIRECTORY=1 build/run/tshark -C ctest -T ek -r test/captures/dhcp.pcap > test/baseline/dhcp.ek
+TZ=UTC WIRESHARK_RUN_FROM_BUILD_DIRECTORY=1 build/run/tshark -C ctest -T json -r test/captures/dhcp.pcap > test/baseline/dhcp.json
+TZ=UTC WIRESHARK_RUN_FROM_BUILD_DIRECTORY=1 build/run/tshark -C ctest -T jsonraw -r test/captures/dhcp.pcap > test/baseline/dhcp.jsonraw
+TZ=UTC WIRESHARK_RUN_FROM_BUILD_DIRECTORY=1 build/run/tshark -C ctest -T ek -r test/captures/dhcp.pcap -x > test/baseline/dhcp-raw.ek
diff --git a/test/baseline/communityid-filtered.txt b/test/baseline/communityid-filtered.txt
new file mode 100644
index 0000000..0b79470
--- /dev/null
+++ b/test/baseline/communityid-filtered.txt
@@ -0,0 +1,2 @@
+1:d/FP5EW3wiY1vCndhwleRRKHowQ=
+1:d/FP5EW3wiY1vCndhwleRRKHowQ=
diff --git a/test/baseline/communityid.txt b/test/baseline/communityid.txt
new file mode 100644
index 0000000..a5caa80
--- /dev/null
+++ b/test/baseline/communityid.txt
@@ -0,0 +1,105 @@
+
+
+
+
+
+
+1:dGHyGvjMfljg6Bppwm3bg0LO8TY=
+1:dGHyGvjMfljg6Bppwm3bg0LO8TY=
+1:BtEUCMYecYjJ7spEkVZDiCFaMTY=
+1:BtEUCMYecYjJ7spEkVZDiCFaMTY=
+1:ORxAZfN3ld7Sv73/HQTNnvgxbpY=
+1:ORxAZfN3ld7Sv73/HQTNnvgxbpY=
+1:zavyT/cezQr1fmImYCwYnMXbgck=
+1:zavyT/cezQr1fmImYCwYnMXbgck=
+1:dGHyGvjMfljg6Bppwm3bg0LO8TY=
+1:dGHyGvjMfljg6Bppwm3bg0LO8TY=
+1:NdobDX8PQNJbAyfkWxhtL2Pqp5w=
+1:NdobDX8PQNJbAyfkWxhtL2Pqp5w=
+1:NdobDX8PQNJbAyfkWxhtL2Pqp5w=
+1:bnQKq8A2r//dWnkRW2EYcMhShjc=
+1:bnQKq8A2r//dWnkRW2EYcMhShjc=
+1:bnQKq8A2r//dWnkRW2EYcMhShjc=
+1:2ObVBgIn28oZvibYZhZMBgh7WdQ=
+1:2ObVBgIn28oZvibYZhZMBgh7WdQ=
+1:2ObVBgIn28oZvibYZhZMBgh7WdQ=
+1:hLZd0XGWojozrvxqE0dWB1iM6R0=
+1:hLZd0XGWojozrvxqE0dWB1iM6R0=
+1:hLZd0XGWojozrvxqE0dWB1iM6R0=
+1:+TW+HtLHvV1xnGhV1lv7XoJrqQg=
+1:+TW+HtLHvV1xnGhV1lv7XoJrqQg=
+1:+TW+HtLHvV1xnGhV1lv7XoJrqQg=
+1:+TW+HtLHvV1xnGhV1lv7XoJrqQg=
+1:+TW+HtLHvV1xnGhV1lv7XoJrqQg=
+1:+TW+HtLHvV1xnGhV1lv7XoJrqQg=
+1:BtEUCMYecYjJ7spEkVZDiCFaMTY=
+1:BtEUCMYecYjJ7spEkVZDiCFaMTY=
+1:hO+sN4H+MG5MY/8hIrXPqc4ZQz0=
+1:pkvHqCL88/tg1k4cPigmZXUtL00=
+1:dGHyGvjMfljg6Bppwm3bg0LO8TY=
+1:dGHyGvjMfljg6Bppwm3bg0LO8TY=
+1:jwuBy9UWZK1KUFqJV5cHdVpfrlY=
+1:MEixa66kuz0OMvlQqnAIzP3n2xg=
+1:ORxAZfN3ld7Sv73/HQTNnvgxbpY=
+1:GpbEQrKqfWtsfsFiqg8fufoZe5Y=
+1:GpbEQrKqfWtsfsFiqg8fufoZe5Y=
+1:GpbEQrKqfWtsfsFiqg8fufoZe5Y=
+1:GpbEQrKqfWtsfsFiqg8fufoZe5Y=
+1:GpbEQrKqfWtsfsFiqg8fufoZe5Y=
+1:GpbEQrKqfWtsfsFiqg8fufoZe5Y=
+1:GpbEQrKqfWtsfsFiqg8fufoZe5Y=
+1:GpbEQrKqfWtsfsFiqg8fufoZe5Y=
+1:GpbEQrKqfWtsfsFiqg8fufoZe5Y=
+1:GpbEQrKqfWtsfsFiqg8fufoZe5Y=
+1:zavyT/cezQr1fmImYCwYnMXbgck=
+1:zavyT/cezQr1fmImYCwYnMXbgck=
+1:X0snYXpgwiv9TZtqg64sgzUn6Dk=
+1:X0snYXpgwiv9TZtqg64sgzUn6Dk=
+1:X0snYXpgwiv9TZtqg64sgzUn6Dk=
+1:X0snYXpgwiv9TZtqg64sgzUn6Dk=
+1:X0snYXpgwiv9TZtqg64sgzUn6Dk=
+1:X0snYXpgwiv9TZtqg64sgzUn6Dk=
+1:X0snYXpgwiv9TZtqg64sgzUn6Dk=
+1:X0snYXpgwiv9TZtqg64sgzUn6Dk=
+1:X0snYXpgwiv9TZtqg64sgzUn6Dk=
+1:X0snYXpgwiv9TZtqg64sgzUn6Dk=
+1:X0snYXpgwiv9TZtqg64sgzUn6Dk=
+1:X0snYXpgwiv9TZtqg64sgzUn6Dk=
+1:/qFaeAR+gFe1KYjMzVDsMv+wgU4=
+1:/qFaeAR+gFe1KYjMzVDsMv+wgU4=
+1:/qFaeAR+gFe1KYjMzVDsMv+wgU4=
+1:/qFaeAR+gFe1KYjMzVDsMv+wgU4=
+1:/qFaeAR+gFe1KYjMzVDsMv+wgU4=
+1:/qFaeAR+gFe1KYjMzVDsMv+wgU4=
+1:/qFaeAR+gFe1KYjMzVDsMv+wgU4=
+1:/qFaeAR+gFe1KYjMzVDsMv+wgU4=
+1:/qFaeAR+gFe1KYjMzVDsMv+wgU4=
+1:/qFaeAR+gFe1KYjMzVDsMv+wgU4=
+1:/qFaeAR+gFe1KYjMzVDsMv+wgU4=
+1:/qFaeAR+gFe1KYjMzVDsMv+wgU4=
+1:/qFaeAR+gFe1KYjMzVDsMv+wgU4=
+1:/qFaeAR+gFe1KYjMzVDsMv+wgU4=
+1:/qFaeAR+gFe1KYjMzVDsMv+wgU4=
+1:/qFaeAR+gFe1KYjMzVDsMv+wgU4=
+1:/qFaeAR+gFe1KYjMzVDsMv+wgU4=
+1:MP2EtRCAUIZvTw6MxJHLV7N7JDs=
+1:MP2EtRCAUIZvTw6MxJHLV7N7JDs=
+1:MP2EtRCAUIZvTw6MxJHLV7N7JDs=
+1:MP2EtRCAUIZvTw6MxJHLV7N7JDs=
+1:MP2EtRCAUIZvTw6MxJHLV7N7JDs=
+1:LQU9qZlK+B5F3KDmev6m5PMibrg=
+1:LQU9qZlK+B5F3KDmev6m5PMibrg=
+1:LQU9qZlK+B5F3KDmev6m5PMibrg=
+1:LQU9qZlK+B5F3KDmev6m5PMibrg=
+1:LQU9qZlK+B5F3KDmev6m5PMibrg=
+1:LQU9qZlK+B5F3KDmev6m5PMibrg=
+1:LQU9qZlK+B5F3KDmev6m5PMibrg=
+1:LQU9qZlK+B5F3KDmev6m5PMibrg=
+1:LQU9qZlK+B5F3KDmev6m5PMibrg=
+1:LQU9qZlK+B5F3KDmev6m5PMibrg=
+1:LQU9qZlK+B5F3KDmev6m5PMibrg=
+1:LQU9qZlK+B5F3KDmev6m5PMibrg=
+1:d/FP5EW3wiY1vCndhwleRRKHowQ=
+1:d/FP5EW3wiY1vCndhwleRRKHowQ=
+1:KHlLkgoJW7ifUTyTSgyVfkFHzKw=
+1:KHlLkgoJW7ifUTyTSgyVfkFHzKw=
diff --git a/test/baseline/dhcp-filter.ek b/test/baseline/dhcp-filter.ek
new file mode 100644
index 0000000..49d0a19
--- /dev/null
+++ b/test/baseline/dhcp-filter.ek
@@ -0,0 +1,8 @@
+{"index":{"_index":"packets-2004-12-05","_type":"doc"}}
+{"timestamp":"1102274184317","layers":{"frame":{"filtered":"frame"},"eth":{"filtered":"eth"},"ip":{"filtered":"ip"},"udp":{"filtered":"udp"},"dhcp":{"dhcp_dhcp_type":{"filtered":"dhcp.type"},"dhcp_dhcp_hw_type":{"filtered":"dhcp.hw.type"},"dhcp_dhcp_hw_len":{"filtered":"dhcp.hw.len"},"dhcp_dhcp_hops":{"filtered":"dhcp.hops"},"dhcp_dhcp_id":{"filtered":"dhcp.id"},"dhcp_dhcp_secs":{"filtered":"dhcp.secs"},"dhcp_dhcp_flags":{"filtered":"dhcp.flags"},"dhcp_dhcp_ip_client":{"filtered":"dhcp.ip.client"},"dhcp_dhcp_ip_your":{"filtered":"dhcp.ip.your"},"dhcp_dhcp_ip_server":{"filtered":"dhcp.ip.server"},"dhcp_dhcp_ip_relay":{"filtered":"dhcp.ip.relay"},"dhcp_dhcp_hw_mac_addr":{"filtered":"dhcp.hw.mac_addr"},"dhcp_dhcp_hw_addr_padding":{"filtered":"dhcp.hw.addr_padding"},"dhcp_dhcp_server":{"filtered":"dhcp.server"},"dhcp_dhcp_file":{"filtered":"dhcp.file"},"dhcp_dhcp_cookie":{"filtered":"dhcp.cookie"},"dhcp_dhcp_option_type":[{"filtered":"dhcp.option.type"},{"filtered":"dhcp.option.type"},{"filtered":"dhcp.option.type"},{"filtered":"dhcp.option.type"},{"filtered":"dhcp.option.type"}],"dhcp_dhcp_option_padding":{"filtered":"dhcp.option.padding"}}}}
+{"index":{"_index":"packets-2004-12-05","_type":"doc"}}
+{"timestamp":"1102274184317","layers":{"frame":{"filtered":"frame"},"eth":{"filtered":"eth"},"ip":{"filtered":"ip"},"udp":{"filtered":"udp"},"dhcp":{"dhcp_dhcp_type":{"filtered":"dhcp.type"},"dhcp_dhcp_hw_type":{"filtered":"dhcp.hw.type"},"dhcp_dhcp_hw_len":{"filtered":"dhcp.hw.len"},"dhcp_dhcp_hops":{"filtered":"dhcp.hops"},"dhcp_dhcp_id":{"filtered":"dhcp.id"},"dhcp_dhcp_secs":{"filtered":"dhcp.secs"},"dhcp_dhcp_flags":{"filtered":"dhcp.flags"},"dhcp_dhcp_ip_client":{"filtered":"dhcp.ip.client"},"dhcp_dhcp_ip_your":{"filtered":"dhcp.ip.your"},"dhcp_dhcp_ip_server":{"filtered":"dhcp.ip.server"},"dhcp_dhcp_ip_relay":{"filtered":"dhcp.ip.relay"},"dhcp_dhcp_hw_mac_addr":{"filtered":"dhcp.hw.mac_addr"},"dhcp_dhcp_hw_addr_padding":{"filtered":"dhcp.hw.addr_padding"},"dhcp_dhcp_server":{"filtered":"dhcp.server"},"dhcp_dhcp_file":{"filtered":"dhcp.file"},"dhcp_dhcp_cookie":{"filtered":"dhcp.cookie"},"dhcp_dhcp_option_type":[{"filtered":"dhcp.option.type"},{"filtered":"dhcp.option.type"},{"filtered":"dhcp.option.type"},{"filtered":"dhcp.option.type"},{"filtered":"dhcp.option.type"},{"filtered":"dhcp.option.type"},{"filtered":"dhcp.option.type"}],"dhcp_dhcp_option_padding":{"filtered":"dhcp.option.padding"}}}}
+{"index":{"_index":"packets-2004-12-05","_type":"doc"}}
+{"timestamp":"1102274184387","layers":{"frame":{"filtered":"frame"},"eth":{"filtered":"eth"},"ip":{"filtered":"ip"},"udp":{"filtered":"udp"},"dhcp":{"dhcp_dhcp_type":{"filtered":"dhcp.type"},"dhcp_dhcp_hw_type":{"filtered":"dhcp.hw.type"},"dhcp_dhcp_hw_len":{"filtered":"dhcp.hw.len"},"dhcp_dhcp_hops":{"filtered":"dhcp.hops"},"dhcp_dhcp_id":{"filtered":"dhcp.id"},"dhcp_dhcp_secs":{"filtered":"dhcp.secs"},"dhcp_dhcp_flags":{"filtered":"dhcp.flags"},"dhcp_dhcp_ip_client":{"filtered":"dhcp.ip.client"},"dhcp_dhcp_ip_your":{"filtered":"dhcp.ip.your"},"dhcp_dhcp_ip_server":{"filtered":"dhcp.ip.server"},"dhcp_dhcp_ip_relay":{"filtered":"dhcp.ip.relay"},"dhcp_dhcp_hw_mac_addr":{"filtered":"dhcp.hw.mac_addr"},"dhcp_dhcp_hw_addr_padding":{"filtered":"dhcp.hw.addr_padding"},"dhcp_dhcp_server":{"filtered":"dhcp.server"},"dhcp_dhcp_file":{"filtered":"dhcp.file"},"dhcp_dhcp_cookie":{"filtered":"dhcp.cookie"},"dhcp_dhcp_option_type":[{"filtered":"dhcp.option.type"},{"filtered":"dhcp.option.type"},{"filtered":"dhcp.option.type"},{"filtered":"dhcp.option.type"},{"filtered":"dhcp.option.type"},{"filtered":"dhcp.option.type"}],"dhcp_dhcp_option_padding":{"filtered":"dhcp.option.padding"}}}}
+{"index":{"_index":"packets-2004-12-05","_type":"doc"}}
+{"timestamp":"1102274184387","layers":{"frame":{"filtered":"frame"},"eth":{"filtered":"eth"},"ip":{"filtered":"ip"},"udp":{"filtered":"udp"},"dhcp":{"dhcp_dhcp_type":{"filtered":"dhcp.type"},"dhcp_dhcp_hw_type":{"filtered":"dhcp.hw.type"},"dhcp_dhcp_hw_len":{"filtered":"dhcp.hw.len"},"dhcp_dhcp_hops":{"filtered":"dhcp.hops"},"dhcp_dhcp_id":{"filtered":"dhcp.id"},"dhcp_dhcp_secs":{"filtered":"dhcp.secs"},"dhcp_dhcp_flags":{"filtered":"dhcp.flags"},"dhcp_dhcp_ip_client":{"filtered":"dhcp.ip.client"},"dhcp_dhcp_ip_your":{"filtered":"dhcp.ip.your"},"dhcp_dhcp_ip_server":{"filtered":"dhcp.ip.server"},"dhcp_dhcp_ip_relay":{"filtered":"dhcp.ip.relay"},"dhcp_dhcp_hw_mac_addr":{"filtered":"dhcp.hw.mac_addr"},"dhcp_dhcp_hw_addr_padding":{"filtered":"dhcp.hw.addr_padding"},"dhcp_dhcp_server":{"filtered":"dhcp.server"},"dhcp_dhcp_file":{"filtered":"dhcp.file"},"dhcp_dhcp_cookie":{"filtered":"dhcp.cookie"},"dhcp_dhcp_option_type":[{"filtered":"dhcp.option.type"},{"filtered":"dhcp.option.type"},{"filtered":"dhcp.option.type"},{"filtered":"dhcp.option.type"},{"filtered":"dhcp.option.type"},{"filtered":"dhcp.option.type"},{"filtered":"dhcp.option.type"}],"dhcp_dhcp_option_padding":{"filtered":"dhcp.option.padding"}}}}
diff --git a/test/baseline/dhcp-raw.ek b/test/baseline/dhcp-raw.ek
new file mode 100644
index 0000000..df063df
--- /dev/null
+++ b/test/baseline/dhcp-raw.ek
@@ -0,0 +1,8 @@
+{"index":{"_index":"packets-2004-12-05","_type":"doc"}}
+{"timestamp":"1102274184317","layers":{"dhcp_raw":"0101060000003d1d0000000000000000000000000000000000000000000b8201fc4200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000638253633501013d0701000b8201fc4232040000000037040103062aff00000000000000","dhcp":{"dhcp_dhcp_option_requested_ip_address_raw":"00000000","dhcp_dhcp_option_requested_ip_address":"0.0.0.0","dhcp_dhcp_hw_type_raw":["01","01"],"dhcp_dhcp_hw_type":["0x01","0x01"],"dhcp_dhcp_ip_your_raw":"00000000","dhcp_dhcp_ip_your":"0.0.0.0","dhcp_dhcp_flags_raw":"0000","dhcp_dhcp_flags":"0x0000","dhcp_dhcp_option_value_raw":["01","01000b8201fc42","00000000","0103062a"],"dhcp_dhcp_option_value":["01","01:00:0b:82:01:fc:42","00:00:00:00","01:03:06:2a"],"dhcp_dhcp_hw_len_raw":"06","dhcp_dhcp_hw_len":"6","dhcp_dhcp_option_length_raw":["01","07","04","04"],"dhcp_dhcp_option_length":["1","7","4","4"],"dhcp_dhcp_flags_bc_raw":"0","dhcp_dhcp_flags_bc":false,"dhcp_dhcp_id_raw":"00003d1d","dhcp_dhcp_id":"0x00003d1d","dhcp_dhcp_hw_mac_addr_raw":["000b8201fc42","000b8201fc42"],"dhcp_dhcp_hw_mac_addr":["00:0b:82:01:fc:42","00:0b:82:01:fc:42"],"dhcp_dhcp_ip_client_raw":"00000000","dhcp_dhcp_ip_client":"0.0.0.0","dhcp_dhcp_secs_raw":"0000","dhcp_dhcp_secs":"0","dhcp_dhcp_server_raw":"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","dhcp_dhcp_server":"","dhcp_dhcp_hw_addr_padding_raw":"00000000000000000000","dhcp_dhcp_hw_addr_padding":"00:00:00:00:00:00:00:00:00:00","dhcp_dhcp_option_type_raw":["350101","3d0701000b8201fc42","320400000000","37040103062a","ff"],"dhcp_dhcp_option_type":["53","61","50","55","0"],"dhcp_dhcp_hops_raw":"00","dhcp_dhcp_hops":"0","dhcp_dhcp_file_raw":"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","dhcp_dhcp_file":"","dhcp_dhcp_ip_server_raw":"00000000","dhcp_dhcp_ip_server":"0.0.0.0","dhcp_dhcp_option_dhcp_raw":"01","dhcp_dhcp_option_dhcp":"1","dhcp_dhcp_option_request_list_item_raw":["01","03","06","2a"],"dhcp_dhcp_option_request_list_item":["1","3","6","42"],"dhcp_dhcp_cookie_raw":"63825363","dhcp_dhcp_cookie":"99.130.83.99","dhcp_dhcp_option_padding_raw":"00000000000000","dhcp_dhcp_option_padding":"00:00:00:00:00:00:00","dhcp_dhcp_ip_relay_raw":"00000000","dhcp_dhcp_ip_relay":"0.0.0.0","dhcp_dhcp_type_raw":"01","dhcp_dhcp_type":"1","dhcp_dhcp_flags_reserved_raw":"0","dhcp_dhcp_flags_reserved":"0x0000","dhcp_dhcp_option_end_raw":"ff","dhcp_dhcp_option_end":"255"},"udp_raw":"004400430118591f","udp":{"udp_udp_time_delta":"0.000000000","udp_udp_dstport_raw":"0043","udp_udp_dstport":"67","udp_udp_checksum_raw":"591f","udp_udp_checksum":"0x591f","udp_udp_port_raw":["0044","0043"],"udp_udp_port":["68","67"],"udp_udp_checksum_status":"2","udp_udp_stream":"0","udp_udp_length_raw":"0118","udp_udp_length":"280","text":"Timestamps","udp_udp_srcport_raw":"0044","udp_udp_srcport":"68","udp_udp_payload_raw":"0101060000003d1d0000000000000000000000000000000000000000000b8201fc4200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000638253633501013d0701000b8201fc4232040000000037040103062aff00000000000000","udp_udp_payload":"01:01:06:00:00:00:3d:1d:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:0b:82:01:fc:42: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: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: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: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: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: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: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:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:63:82:53:63:35:01:01:3d:07:01:00:0b:82:01:fc:42:32:04:00:00:00:00:37:04:01:03:06:2a:ff:00:00:00:00:00:00:00","udp_udp_time_relative":"0.000000000"},"ip_raw":"4500012ca8360000fa11178b00000000ffffffff","ip":{"ip_ip_flags_rb_raw":"0","ip_ip_flags_rb":false,"ip_ip_addr_raw":["00000000","ffffffff"],"ip_ip_addr":["0.0.0.0","255.255.255.255"],"ip_ip_dsfield_ecn_raw":"0","ip_ip_dsfield_ecn":"0","ip_ip_frag_offset_raw":"0","ip_ip_frag_offset":"0","ip_ip_hdr_len_raw":"45","ip_ip_hdr_len":"20","ip_ip_id_raw":"a836","ip_ip_id":"0xa836","ip_ip_version_raw":"45","ip_ip_version":"4","ip_ip_dst_raw":"ffffffff","ip_ip_dst":"255.255.255.255","ip_ip_host_raw":["00000000","ffffffff"],"ip_ip_host":["0.0.0.0","255.255.255.255"],"ip_ip_flags_raw":"0","ip_ip_flags":"0x00","ip_ip_src_host_raw":"00000000","ip_ip_src_host":"0.0.0.0","ip_ip_flags_df_raw":"0","ip_ip_flags_df":false,"ip_ip_len_raw":"012c","ip_ip_len":"300","ip_ip_checksum_status":"2","ip_ip_dst_host_raw":"ffffffff","ip_ip_dst_host":"255.255.255.255","ip_ip_src_raw":"00000000","ip_ip_src":"0.0.0.0","ip_ip_ttl_raw":"fa","ip_ip_ttl":"250","ip_ip_flags_mf_raw":"0","ip_ip_flags_mf":false,"ip_ip_checksum_raw":"178b","ip_ip_checksum":"0x178b","ip_ip_proto_raw":"11","ip_ip_proto":"17","ip_ip_dsfield_dscp_raw":"0","ip_ip_dsfield_dscp":"0","ip_ip_dsfield_raw":"00","ip_ip_dsfield":"0x00"},"frame_raw":"ffffffffffff000b8201fc4208004500012ca8360000fa11178b00000000ffffffff004400430118591f0101060000003d1d0000000000000000000000000000000000000000000b8201fc4200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000638253633501013d0701000b8201fc4232040000000037040103062aff00000000000000","frame":{"frame_frame_len":"314","frame_frame_marked":false,"frame_frame_number":"1","frame_frame_time_epoch":"2004-12-05T19:16:24.317453000Z","frame_frame_time":"2004-12-05T19:16:24.317453000Z","frame_frame_time_relative":"0.000000000","frame_frame_encap_type":"1","frame_frame_offset_shift":"0.000000000","frame_frame_time_delta_displayed":"0.000000000","frame_frame_time_utc":"2004-12-05T19:16:24.317453000Z","frame_frame_ignored":false,"frame_frame_cap_len":"314","frame_frame_time_delta":"0.000000000","frame_frame_protocols":"eth:ethertype:ip:udp:dhcp"},"eth_raw":"ffffffffffff000b8201fc420800","eth":{"eth_eth_src_lg_raw":"0","eth_eth_src_lg":false,"eth_eth_dst_raw":"ffffffffffff","eth_eth_dst":"ff:ff:ff:ff:ff:ff","eth_eth_lg_raw":["1","0"],"eth_eth_lg":[true,false],"eth_eth_dst_oui_raw":"ffffff","eth_eth_dst_oui":"16777215","eth_eth_addr_oui_raw":["ffffff","000b82"],"eth_eth_addr_oui":["16777215","2946"],"eth_eth_src_resolved_raw":"000b8201fc42","eth_eth_src_resolved":"GrandstreamN_01:fc:42","eth_eth_addr_oui_resolved_raw":"000b8201fc42","eth_eth_addr_oui_resolved":"Grandstream Networks, Inc.","eth_eth_src_oui_resolved_raw":"000b8201fc42","eth_eth_src_oui_resolved":"Grandstream Networks, Inc.","eth_eth_src_oui_raw":"000b82","eth_eth_src_oui":"2946","eth_eth_addr_resolved_raw":["ffffffffffff","000b8201fc42"],"eth_eth_addr_resolved":["Broadcast","GrandstreamN_01:fc:42"],"eth_eth_type_raw":"0800","eth_eth_type":"0x0800","eth_eth_src_raw":"000b8201fc42","eth_eth_src":"00:0b:82:01:fc:42","eth_eth_addr_raw":["ffffffffffff","000b8201fc42"],"eth_eth_addr":["ff:ff:ff:ff:ff:ff","00:0b:82:01:fc:42"],"eth_eth_dst_ig_raw":"1","eth_eth_dst_ig":true,"eth_eth_dst_lg_raw":"1","eth_eth_dst_lg":true,"eth_eth_src_ig_raw":"0","eth_eth_src_ig":false,"eth_eth_ig_raw":["1","0"],"eth_eth_ig":[true,false],"eth_eth_dst_resolved_raw":"ffffffffffff","eth_eth_dst_resolved":"Broadcast"}}}
+{"index":{"_index":"packets-2004-12-05","_type":"doc"}}
+{"timestamp":"1102274184317","layers":{"dhcp_raw":"0201060000003d1d0000000000000000c0a8000ac0a8000100000000000b8201fc4200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000638253633501020104ffffff003a04000007083b0400000c4e330400000e103604c0a80001ff0000000000000000000000000000000000000000000000000000","dhcp":{"dhcp_dhcp_option_dhcp_server_id_raw":"c0a80001","dhcp_dhcp_option_dhcp_server_id":"192.168.0.1","dhcp_dhcp_hw_type_raw":"01","dhcp_dhcp_hw_type":"0x01","dhcp_dhcp_ip_your_raw":"c0a8000a","dhcp_dhcp_ip_your":"192.168.0.10","dhcp_dhcp_flags_raw":"0000","dhcp_dhcp_flags":"0x0000","dhcp_dhcp_option_ip_address_lease_time_raw":"00000e10","dhcp_dhcp_option_ip_address_lease_time":"3600","dhcp_dhcp_option_value_raw":["02","ffffff00","00000708","00000c4e","00000e10","c0a80001"],"dhcp_dhcp_option_value":["02","ff:ff:ff:00","00:00:07:08","00:00:0c:4e","00:00:0e:10","c0:a8:00:01"],"dhcp_dhcp_hw_len_raw":"06","dhcp_dhcp_hw_len":"6","dhcp_dhcp_option_length_raw":["01","04","04","04","04","04"],"dhcp_dhcp_option_length":["1","4","4","4","4","4"],"dhcp_dhcp_flags_bc_raw":"0","dhcp_dhcp_flags_bc":false,"dhcp_dhcp_id_raw":"00003d1d","dhcp_dhcp_id":"0x00003d1d","dhcp_dhcp_hw_mac_addr_raw":"000b8201fc42","dhcp_dhcp_hw_mac_addr":"00:0b:82:01:fc:42","dhcp_dhcp_ip_client_raw":"00000000","dhcp_dhcp_ip_client":"0.0.0.0","dhcp_dhcp_secs_raw":"0000","dhcp_dhcp_secs":"0","dhcp_dhcp_server_raw":"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","dhcp_dhcp_server":"","dhcp_dhcp_option_end_raw":"ff","dhcp_dhcp_option_end":"255","dhcp_dhcp_hw_addr_padding_raw":"00000000000000000000","dhcp_dhcp_hw_addr_padding":"00:00:00:00:00:00:00:00:00:00","dhcp_dhcp_option_type_raw":["350102","0104ffffff00","3a0400000708","3b0400000c4e","330400000e10","3604c0a80001","ff"],"dhcp_dhcp_option_type":["53","1","58","59","51","54","0"],"dhcp_dhcp_hops_raw":"00","dhcp_dhcp_hops":"0","dhcp_dhcp_file_raw":"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","dhcp_dhcp_file":"","dhcp_dhcp_ip_server_raw":"c0a80001","dhcp_dhcp_ip_server":"192.168.0.1","dhcp_dhcp_option_dhcp_raw":"02","dhcp_dhcp_option_dhcp":"2","dhcp_dhcp_option_subnet_mask_raw":"ffffff00","dhcp_dhcp_option_subnet_mask":"255.255.255.0","dhcp_dhcp_cookie_raw":"63825363","dhcp_dhcp_cookie":"99.130.83.99","dhcp_dhcp_option_renewal_time_value_raw":"00000708","dhcp_dhcp_option_renewal_time_value":"1800","dhcp_dhcp_ip_relay_raw":"00000000","dhcp_dhcp_ip_relay":"0.0.0.0","dhcp_dhcp_type_raw":"02","dhcp_dhcp_type":"2","dhcp_dhcp_flags_reserved_raw":"0","dhcp_dhcp_flags_reserved":"0x0000","dhcp_dhcp_option_padding_raw":"0000000000000000000000000000000000000000000000000000","dhcp_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","dhcp_dhcp_option_rebinding_time_value_raw":"00000c4e","dhcp_dhcp_option_rebinding_time_value":"3150"},"udp_raw":"0043004401342233","udp":{"udp_udp_time_delta":"0.000000000","udp_udp_dstport_raw":"0044","udp_udp_dstport":"68","udp_udp_checksum_raw":"2233","udp_udp_checksum":"0x2233","udp_udp_port_raw":["0043","0044"],"udp_udp_port":["67","68"],"udp_udp_checksum_status":"2","udp_udp_stream":"1","udp_udp_length_raw":"0134","udp_udp_length":"308","text":"Timestamps","udp_udp_srcport_raw":"0043","udp_udp_srcport":"67","udp_udp_payload_raw":"0201060000003d1d0000000000000000c0a8000ac0a8000100000000000b8201fc4200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000638253633501020104ffffff003a04000007083b0400000c4e330400000e103604c0a80001ff0000000000000000000000000000000000000000000000000000","udp_udp_payload":"02:01:06:00:00:00:3d:1d:00:00:00:00:00:00:00:00:c0:a8:00:0a:c0:a8:00:01:00:00:00:00:00:0b:82:01:fc:42: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: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: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: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: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: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: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:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:63:82:53:63:35:01:02:01:04:ff:ff:ff:00:3a:04:00:00:07:08:3b:04:00:00:0c:4e:33:04:00:00:0e:10:36:04:c0:a8:00:01:ff: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","udp_udp_time_relative":"0.000000000"},"ip_raw":"450001480445000080110000c0a80001c0a8000a","ip":{"ip_ip_flags_rb_raw":"0","ip_ip_flags_rb":false,"ip_ip_addr_raw":["c0a80001","c0a8000a"],"ip_ip_addr":["192.168.0.1","192.168.0.10"],"ip_ip_dsfield_ecn_raw":"0","ip_ip_dsfield_ecn":"0","ip_ip_frag_offset_raw":"0","ip_ip_frag_offset":"0","ip_ip_hdr_len_raw":"45","ip_ip_hdr_len":"20","ip_ip_id_raw":"0445","ip_ip_id":"0x0445","ip_ip_version_raw":"45","ip_ip_version":"4","ip_ip_dst_raw":"c0a8000a","ip_ip_dst":"192.168.0.10","ip_ip_host_raw":["c0a80001","c0a8000a"],"ip_ip_host":["192.168.0.1","192.168.0.10"],"ip_ip_flags_raw":"0","ip_ip_flags":"0x00","ip_ip_src_host_raw":"c0a80001","ip_ip_src_host":"192.168.0.1","ip_ip_flags_df_raw":"0","ip_ip_flags_df":false,"ip_ip_len_raw":"0148","ip_ip_len":"328","ip_ip_checksum_status":"2","ip_ip_dst_host_raw":"c0a8000a","ip_ip_dst_host":"192.168.0.10","ip_ip_src_raw":"c0a80001","ip_ip_src":"192.168.0.1","ip_ip_ttl_raw":"80","ip_ip_ttl":"128","ip_ip_flags_mf_raw":"0","ip_ip_flags_mf":false,"ip_ip_checksum_raw":"0000","ip_ip_checksum":"0x0000","ip_ip_proto_raw":"11","ip_ip_proto":"17","ip_ip_dsfield_dscp_raw":"0","ip_ip_dsfield_dscp":"0","ip_ip_dsfield_raw":"00","ip_ip_dsfield":"0x00"},"frame_raw":"000b8201fc42000874adf19b0800450001480445000080110000c0a80001c0a8000a00430044013422330201060000003d1d0000000000000000c0a8000ac0a8000100000000000b8201fc4200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000638253633501020104ffffff003a04000007083b0400000c4e330400000e103604c0a80001ff0000000000000000000000000000000000000000000000000000","frame":{"frame_frame_len":"342","frame_frame_marked":false,"frame_frame_number":"2","frame_frame_time_epoch":"2004-12-05T19:16:24.317748000Z","frame_frame_time":"2004-12-05T19:16:24.317748000Z","frame_frame_time_relative":"0.000295000","frame_frame_encap_type":"1","frame_frame_offset_shift":"0.000000000","frame_frame_time_delta_displayed":"0.000295000","frame_frame_time_utc":"2004-12-05T19:16:24.317748000Z","frame_frame_ignored":false,"frame_frame_cap_len":"342","frame_frame_time_delta":"0.000295000","frame_frame_protocols":"eth:ethertype:ip:udp:dhcp"},"eth_raw":"000b8201fc42000874adf19b0800","eth":{"eth_eth_src_lg_raw":"0","eth_eth_src_lg":false,"eth_eth_dst_raw":"000b8201fc42","eth_eth_dst":"00:0b:82:01:fc:42","eth_eth_lg_raw":["0","0"],"eth_eth_lg":[false,false],"eth_eth_dst_resolved_raw":"000b8201fc42","eth_eth_dst_resolved":"GrandstreamN_01:fc:42","eth_eth_addr_oui_raw":["000b82","000874"],"eth_eth_addr_oui":["2946","2164"],"eth_eth_src_ig_raw":"0","eth_eth_src_ig":false,"eth_eth_src_resolved_raw":"000874adf19b","eth_eth_src_resolved":"Dell_ad:f1:9b","eth_eth_addr_oui_resolved_raw":["000b8201fc42","000874adf19b"],"eth_eth_addr_oui_resolved":["Grandstream Networks, Inc.","Dell Inc."],"eth_eth_src_oui_raw":"000874","eth_eth_src_oui":"2164","eth_eth_src_oui_resolved_raw":"000874adf19b","eth_eth_src_oui_resolved":"Dell Inc.","eth_eth_addr_resolved_raw":["000b8201fc42","000874adf19b"],"eth_eth_addr_resolved":["GrandstreamN_01:fc:42","Dell_ad:f1:9b"],"eth_eth_type_raw":"0800","eth_eth_type":"0x0800","eth_eth_src_raw":"000874adf19b","eth_eth_src":"00:08:74:ad:f1:9b","eth_eth_addr_raw":["000b8201fc42","000874adf19b"],"eth_eth_addr":["00:0b:82:01:fc:42","00:08:74:ad:f1:9b"],"eth_eth_dst_ig_raw":"0","eth_eth_dst_ig":false,"eth_eth_dst_oui_resolved_raw":"000b8201fc42","eth_eth_dst_oui_resolved":"Grandstream Networks, Inc.","eth_eth_dst_lg_raw":"0","eth_eth_dst_lg":false,"eth_eth_ig_raw":["0","0"],"eth_eth_ig":[false,false],"eth_eth_dst_oui_raw":"000b82","eth_eth_dst_oui":"2946"}}}
+{"index":{"_index":"packets-2004-12-05","_type":"doc"}}
+{"timestamp":"1102274184387","layers":{"dhcp_raw":"0101060000003d1e0000000000000000000000000000000000000000000b8201fc4200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000638253633501033d0701000b8201fc423204c0a8000a3604c0a8000137040103062aff00","dhcp":{"dhcp_dhcp_option_requested_ip_address_raw":"c0a8000a","dhcp_dhcp_option_requested_ip_address":"192.168.0.10","dhcp_dhcp_hw_type_raw":["01","01"],"dhcp_dhcp_hw_type":["0x01","0x01"],"dhcp_dhcp_ip_your_raw":"00000000","dhcp_dhcp_ip_your":"0.0.0.0","dhcp_dhcp_flags_raw":"0000","dhcp_dhcp_flags":"0x0000","dhcp_dhcp_option_value_raw":["03","01000b8201fc42","c0a8000a","c0a80001","0103062a"],"dhcp_dhcp_option_value":["03","01:00:0b:82:01:fc:42","c0:a8:00:0a","c0:a8:00:01","01:03:06:2a"],"dhcp_dhcp_hw_len_raw":"06","dhcp_dhcp_hw_len":"6","dhcp_dhcp_option_length_raw":["01","07","04","04","04"],"dhcp_dhcp_option_length":["1","7","4","4","4"],"dhcp_dhcp_flags_bc_raw":"0","dhcp_dhcp_flags_bc":false,"dhcp_dhcp_id_raw":"00003d1e","dhcp_dhcp_id":"0x00003d1e","dhcp_dhcp_option_dhcp_server_id_raw":"c0a80001","dhcp_dhcp_option_dhcp_server_id":"192.168.0.1","dhcp_dhcp_hw_mac_addr_raw":["000b8201fc42","000b8201fc42"],"dhcp_dhcp_hw_mac_addr":["00:0b:82:01:fc:42","00:0b:82:01:fc:42"],"dhcp_dhcp_ip_client_raw":"00000000","dhcp_dhcp_ip_client":"0.0.0.0","dhcp_dhcp_secs_raw":"0000","dhcp_dhcp_secs":"0","dhcp_dhcp_server_raw":"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","dhcp_dhcp_server":"","dhcp_dhcp_hw_addr_padding_raw":"00000000000000000000","dhcp_dhcp_hw_addr_padding":"00:00:00:00:00:00:00:00:00:00","dhcp_dhcp_option_type_raw":["350103","3d0701000b8201fc42","3204c0a8000a","3604c0a80001","37040103062a","ff"],"dhcp_dhcp_option_type":["53","61","50","54","55","0"],"dhcp_dhcp_hops_raw":"00","dhcp_dhcp_hops":"0","dhcp_dhcp_file_raw":"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","dhcp_dhcp_file":"","dhcp_dhcp_ip_server_raw":"00000000","dhcp_dhcp_ip_server":"0.0.0.0","dhcp_dhcp_option_dhcp_raw":"03","dhcp_dhcp_option_dhcp":"3","dhcp_dhcp_option_request_list_item_raw":["01","03","06","2a"],"dhcp_dhcp_option_request_list_item":["1","3","6","42"],"dhcp_dhcp_cookie_raw":"63825363","dhcp_dhcp_cookie":"99.130.83.99","dhcp_dhcp_option_padding_raw":"00","dhcp_dhcp_option_padding":"00","dhcp_dhcp_ip_relay_raw":"00000000","dhcp_dhcp_ip_relay":"0.0.0.0","dhcp_dhcp_type_raw":"01","dhcp_dhcp_type":"1","dhcp_dhcp_flags_reserved_raw":"0","dhcp_dhcp_flags_reserved":"0x0000","dhcp_dhcp_option_end_raw":"ff","dhcp_dhcp_option_end":"255"},"udp_raw":"0044004301189fbd","udp":{"udp_udp_time_delta":"0.070031000","udp_udp_dstport_raw":"0043","udp_udp_dstport":"67","udp_udp_checksum_raw":"9fbd","udp_udp_checksum":"0x9fbd","udp_udp_port_raw":["0044","0043"],"udp_udp_port":["68","67"],"udp_udp_checksum_status":"2","udp_udp_stream":"0","udp_udp_length_raw":"0118","udp_udp_length":"280","text":"Timestamps","udp_udp_srcport_raw":"0044","udp_udp_srcport":"68","udp_udp_payload_raw":"0101060000003d1e0000000000000000000000000000000000000000000b8201fc4200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000638253633501033d0701000b8201fc423204c0a8000a3604c0a8000137040103062aff00","udp_udp_payload":"01:01:06:00:00:00:3d:1e:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:0b:82:01:fc:42: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: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: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: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: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: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: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:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:63:82:53:63:35:01:03:3d:07:01:00:0b:82:01:fc:42:32:04:c0:a8:00:0a:36:04:c0:a8:00:01:37:04:01:03:06:2a:ff:00","udp_udp_time_relative":"0.070031000"},"ip_raw":"4500012ca8370000fa11178a00000000ffffffff","ip":{"ip_ip_flags_rb_raw":"0","ip_ip_flags_rb":false,"ip_ip_addr_raw":["00000000","ffffffff"],"ip_ip_addr":["0.0.0.0","255.255.255.255"],"ip_ip_dsfield_ecn_raw":"0","ip_ip_dsfield_ecn":"0","ip_ip_frag_offset_raw":"0","ip_ip_frag_offset":"0","ip_ip_hdr_len_raw":"45","ip_ip_hdr_len":"20","ip_ip_id_raw":"a837","ip_ip_id":"0xa837","ip_ip_version_raw":"45","ip_ip_version":"4","ip_ip_dst_raw":"ffffffff","ip_ip_dst":"255.255.255.255","ip_ip_host_raw":["00000000","ffffffff"],"ip_ip_host":["0.0.0.0","255.255.255.255"],"ip_ip_flags_raw":"0","ip_ip_flags":"0x00","ip_ip_src_host_raw":"00000000","ip_ip_src_host":"0.0.0.0","ip_ip_flags_df_raw":"0","ip_ip_flags_df":false,"ip_ip_len_raw":"012c","ip_ip_len":"300","ip_ip_checksum_status":"2","ip_ip_dst_host_raw":"ffffffff","ip_ip_dst_host":"255.255.255.255","ip_ip_src_raw":"00000000","ip_ip_src":"0.0.0.0","ip_ip_ttl_raw":"fa","ip_ip_ttl":"250","ip_ip_flags_mf_raw":"0","ip_ip_flags_mf":false,"ip_ip_checksum_raw":"178a","ip_ip_checksum":"0x178a","ip_ip_proto_raw":"11","ip_ip_proto":"17","ip_ip_dsfield_dscp_raw":"0","ip_ip_dsfield_dscp":"0","ip_ip_dsfield_raw":"00","ip_ip_dsfield":"0x00"},"frame_raw":"ffffffffffff000b8201fc4208004500012ca8370000fa11178a00000000ffffffff0044004301189fbd0101060000003d1e0000000000000000000000000000000000000000000b8201fc4200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000638253633501033d0701000b8201fc423204c0a8000a3604c0a8000137040103062aff00","frame":{"frame_frame_len":"314","frame_frame_marked":false,"frame_frame_number":"3","frame_frame_time_epoch":"2004-12-05T19:16:24.387484000Z","frame_frame_time":"2004-12-05T19:16:24.387484000Z","frame_frame_time_relative":"0.070031000","frame_frame_encap_type":"1","frame_frame_offset_shift":"0.000000000","frame_frame_time_delta_displayed":"0.069736000","frame_frame_time_utc":"2004-12-05T19:16:24.387484000Z","frame_frame_ignored":false,"frame_frame_cap_len":"314","frame_frame_time_delta":"0.069736000","frame_frame_protocols":"eth:ethertype:ip:udp:dhcp"},"eth_raw":"ffffffffffff000b8201fc420800","eth":{"eth_eth_src_lg_raw":"0","eth_eth_src_lg":false,"eth_eth_dst_raw":"ffffffffffff","eth_eth_dst":"ff:ff:ff:ff:ff:ff","eth_eth_lg_raw":["1","0"],"eth_eth_lg":[true,false],"eth_eth_dst_oui_raw":"ffffff","eth_eth_dst_oui":"16777215","eth_eth_addr_oui_raw":["ffffff","000b82"],"eth_eth_addr_oui":["16777215","2946"],"eth_eth_src_resolved_raw":"000b8201fc42","eth_eth_src_resolved":"GrandstreamN_01:fc:42","eth_eth_addr_oui_resolved_raw":"000b8201fc42","eth_eth_addr_oui_resolved":"Grandstream Networks, Inc.","eth_eth_src_oui_resolved_raw":"000b8201fc42","eth_eth_src_oui_resolved":"Grandstream Networks, Inc.","eth_eth_src_oui_raw":"000b82","eth_eth_src_oui":"2946","eth_eth_addr_resolved_raw":["ffffffffffff","000b8201fc42"],"eth_eth_addr_resolved":["Broadcast","GrandstreamN_01:fc:42"],"eth_eth_type_raw":"0800","eth_eth_type":"0x0800","eth_eth_src_raw":"000b8201fc42","eth_eth_src":"00:0b:82:01:fc:42","eth_eth_addr_raw":["ffffffffffff","000b8201fc42"],"eth_eth_addr":["ff:ff:ff:ff:ff:ff","00:0b:82:01:fc:42"],"eth_eth_dst_ig_raw":"1","eth_eth_dst_ig":true,"eth_eth_dst_lg_raw":"1","eth_eth_dst_lg":true,"eth_eth_src_ig_raw":"0","eth_eth_src_ig":false,"eth_eth_ig_raw":["1","0"],"eth_eth_ig":[true,false],"eth_eth_dst_resolved_raw":"ffffffffffff","eth_eth_dst_resolved":"Broadcast"}}}
+{"index":{"_index":"packets-2004-12-05","_type":"doc"}}
+{"timestamp":"1102274184387","layers":{"dhcp_raw":"0201060000003d1e0000000000000000c0a8000a0000000000000000000b8201fc4200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000638253633501053a04000007083b0400000c4e330400000e103604c0a800010104ffffff00ff0000000000000000000000000000000000000000000000000000","dhcp":{"dhcp_dhcp_option_dhcp_server_id_raw":"c0a80001","dhcp_dhcp_option_dhcp_server_id":"192.168.0.1","dhcp_dhcp_hw_type_raw":"01","dhcp_dhcp_hw_type":"0x01","dhcp_dhcp_ip_your_raw":"c0a8000a","dhcp_dhcp_ip_your":"192.168.0.10","dhcp_dhcp_flags_raw":"0000","dhcp_dhcp_flags":"0x0000","dhcp_dhcp_option_ip_address_lease_time_raw":"00000e10","dhcp_dhcp_option_ip_address_lease_time":"3600","dhcp_dhcp_option_value_raw":["05","00000708","00000c4e","00000e10","c0a80001","ffffff00"],"dhcp_dhcp_option_value":["05","00:00:07:08","00:00:0c:4e","00:00:0e:10","c0:a8:00:01","ff:ff:ff:00"],"dhcp_dhcp_hw_len_raw":"06","dhcp_dhcp_hw_len":"6","dhcp_dhcp_option_length_raw":["01","04","04","04","04","04"],"dhcp_dhcp_option_length":["1","4","4","4","4","4"],"dhcp_dhcp_flags_bc_raw":"0","dhcp_dhcp_flags_bc":false,"dhcp_dhcp_id_raw":"00003d1e","dhcp_dhcp_id":"0x00003d1e","dhcp_dhcp_hw_mac_addr_raw":"000b8201fc42","dhcp_dhcp_hw_mac_addr":"00:0b:82:01:fc:42","dhcp_dhcp_ip_client_raw":"00000000","dhcp_dhcp_ip_client":"0.0.0.0","dhcp_dhcp_secs_raw":"0000","dhcp_dhcp_secs":"0","dhcp_dhcp_server_raw":"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","dhcp_dhcp_server":"","dhcp_dhcp_option_end_raw":"ff","dhcp_dhcp_option_end":"255","dhcp_dhcp_hw_addr_padding_raw":"00000000000000000000","dhcp_dhcp_hw_addr_padding":"00:00:00:00:00:00:00:00:00:00","dhcp_dhcp_option_type_raw":["350105","3a0400000708","3b0400000c4e","330400000e10","3604c0a80001","0104ffffff00","ff"],"dhcp_dhcp_option_type":["53","58","59","51","54","1","0"],"dhcp_dhcp_hops_raw":"00","dhcp_dhcp_hops":"0","dhcp_dhcp_file_raw":"0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","dhcp_dhcp_file":"","dhcp_dhcp_ip_server_raw":"00000000","dhcp_dhcp_ip_server":"0.0.0.0","dhcp_dhcp_option_dhcp_raw":"05","dhcp_dhcp_option_dhcp":"5","dhcp_dhcp_option_subnet_mask_raw":"ffffff00","dhcp_dhcp_option_subnet_mask":"255.255.255.0","dhcp_dhcp_cookie_raw":"63825363","dhcp_dhcp_cookie":"99.130.83.99","dhcp_dhcp_option_renewal_time_value_raw":"00000708","dhcp_dhcp_option_renewal_time_value":"1800","dhcp_dhcp_ip_relay_raw":"00000000","dhcp_dhcp_ip_relay":"0.0.0.0","dhcp_dhcp_type_raw":"02","dhcp_dhcp_type":"2","dhcp_dhcp_flags_reserved_raw":"0","dhcp_dhcp_flags_reserved":"0x0000","dhcp_dhcp_option_padding_raw":"0000000000000000000000000000000000000000000000000000","dhcp_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","dhcp_dhcp_option_rebinding_time_value_raw":"00000c4e","dhcp_dhcp_option_rebinding_time_value":"3150"},"udp_raw":"004300440134dfdb","udp":{"udp_udp_time_delta":"0.070050000","udp_udp_dstport_raw":"0044","udp_udp_dstport":"68","udp_udp_checksum_raw":"dfdb","udp_udp_checksum":"0xdfdb","udp_udp_port_raw":["0043","0044"],"udp_udp_port":["67","68"],"udp_udp_checksum_status":"2","udp_udp_stream":"1","udp_udp_length_raw":"0134","udp_udp_length":"308","text":"Timestamps","udp_udp_srcport_raw":"0043","udp_udp_srcport":"67","udp_udp_payload_raw":"0201060000003d1e0000000000000000c0a8000a0000000000000000000b8201fc4200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000638253633501053a04000007083b0400000c4e330400000e103604c0a800010104ffffff00ff0000000000000000000000000000000000000000000000000000","udp_udp_payload":"02:01:06:00:00:00:3d:1e:00:00:00:00:00:00:00:00:c0:a8:00:0a:00:00:00:00:00:00:00:00:00:0b:82:01:fc:42: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: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: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: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: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: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: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:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:63:82:53:63:35:01:05:3a:04:00:00:07:08:3b:04:00:00:0c:4e:33:04:00:00:0e:10:36:04:c0:a8:00:01:01:04:ff:ff:ff:00:ff: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","udp_udp_time_relative":"0.070050000"},"ip_raw":"450001480446000080110000c0a80001c0a8000a","ip":{"ip_ip_flags_rb_raw":"0","ip_ip_flags_rb":false,"ip_ip_addr_raw":["c0a80001","c0a8000a"],"ip_ip_addr":["192.168.0.1","192.168.0.10"],"ip_ip_dsfield_ecn_raw":"0","ip_ip_dsfield_ecn":"0","ip_ip_frag_offset_raw":"0","ip_ip_frag_offset":"0","ip_ip_hdr_len_raw":"45","ip_ip_hdr_len":"20","ip_ip_id_raw":"0446","ip_ip_id":"0x0446","ip_ip_version_raw":"45","ip_ip_version":"4","ip_ip_dst_raw":"c0a8000a","ip_ip_dst":"192.168.0.10","ip_ip_host_raw":["c0a80001","c0a8000a"],"ip_ip_host":["192.168.0.1","192.168.0.10"],"ip_ip_flags_raw":"0","ip_ip_flags":"0x00","ip_ip_src_host_raw":"c0a80001","ip_ip_src_host":"192.168.0.1","ip_ip_flags_df_raw":"0","ip_ip_flags_df":false,"ip_ip_len_raw":"0148","ip_ip_len":"328","ip_ip_checksum_status":"2","ip_ip_dst_host_raw":"c0a8000a","ip_ip_dst_host":"192.168.0.10","ip_ip_src_raw":"c0a80001","ip_ip_src":"192.168.0.1","ip_ip_ttl_raw":"80","ip_ip_ttl":"128","ip_ip_flags_mf_raw":"0","ip_ip_flags_mf":false,"ip_ip_checksum_raw":"0000","ip_ip_checksum":"0x0000","ip_ip_proto_raw":"11","ip_ip_proto":"17","ip_ip_dsfield_dscp_raw":"0","ip_ip_dsfield_dscp":"0","ip_ip_dsfield_raw":"00","ip_ip_dsfield":"0x00"},"frame_raw":"000b8201fc42000874adf19b0800450001480446000080110000c0a80001c0a8000a004300440134dfdb0201060000003d1e0000000000000000c0a8000a0000000000000000000b8201fc4200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000638253633501053a04000007083b0400000c4e330400000e103604c0a800010104ffffff00ff0000000000000000000000000000000000000000000000000000","frame":{"frame_frame_len":"342","frame_frame_marked":false,"frame_frame_number":"4","frame_frame_time_epoch":"2004-12-05T19:16:24.387798000Z","frame_frame_time":"2004-12-05T19:16:24.387798000Z","frame_frame_time_relative":"0.070345000","frame_frame_encap_type":"1","frame_frame_offset_shift":"0.000000000","frame_frame_time_delta_displayed":"0.000314000","frame_frame_time_utc":"2004-12-05T19:16:24.387798000Z","frame_frame_ignored":false,"frame_frame_cap_len":"342","frame_frame_time_delta":"0.000314000","frame_frame_protocols":"eth:ethertype:ip:udp:dhcp"},"eth_raw":"000b8201fc42000874adf19b0800","eth":{"eth_eth_src_lg_raw":"0","eth_eth_src_lg":false,"eth_eth_dst_raw":"000b8201fc42","eth_eth_dst":"00:0b:82:01:fc:42","eth_eth_lg_raw":["0","0"],"eth_eth_lg":[false,false],"eth_eth_dst_resolved_raw":"000b8201fc42","eth_eth_dst_resolved":"GrandstreamN_01:fc:42","eth_eth_addr_oui_raw":["000b82","000874"],"eth_eth_addr_oui":["2946","2164"],"eth_eth_src_ig_raw":"0","eth_eth_src_ig":false,"eth_eth_src_resolved_raw":"000874adf19b","eth_eth_src_resolved":"Dell_ad:f1:9b","eth_eth_addr_oui_resolved_raw":["000b8201fc42","000874adf19b"],"eth_eth_addr_oui_resolved":["Grandstream Networks, Inc.","Dell Inc."],"eth_eth_src_oui_raw":"000874","eth_eth_src_oui":"2164","eth_eth_src_oui_resolved_raw":"000874adf19b","eth_eth_src_oui_resolved":"Dell Inc.","eth_eth_addr_resolved_raw":["000b8201fc42","000874adf19b"],"eth_eth_addr_resolved":["GrandstreamN_01:fc:42","Dell_ad:f1:9b"],"eth_eth_type_raw":"0800","eth_eth_type":"0x0800","eth_eth_src_raw":"000874adf19b","eth_eth_src":"00:08:74:ad:f1:9b","eth_eth_addr_raw":["000b8201fc42","000874adf19b"],"eth_eth_addr":["00:0b:82:01:fc:42","00:08:74:ad:f1:9b"],"eth_eth_dst_ig_raw":"0","eth_eth_dst_ig":false,"eth_eth_dst_oui_resolved_raw":"000b8201fc42","eth_eth_dst_oui_resolved":"Grandstream Networks, Inc.","eth_eth_dst_lg_raw":"0","eth_eth_dst_lg":false,"eth_eth_ig_raw":["0","0"],"eth_eth_ig":[false,false],"eth_eth_dst_oui_raw":"000b82","eth_eth_dst_oui":"2946"}}}
diff --git a/test/baseline/dhcp.ek b/test/baseline/dhcp.ek
new file mode 100644
index 0000000..5606618
--- /dev/null
+++ b/test/baseline/dhcp.ek
@@ -0,0 +1,8 @@
+{"index":{"_index":"packets-2004-12-05","_type":"doc"}}
+{"timestamp":"1102274184317","layers":{"dhcp":{"dhcp_dhcp_option_requested_ip_address":"0.0.0.0","dhcp_dhcp_hw_type":["0x01","0x01"],"dhcp_dhcp_ip_your":"0.0.0.0","dhcp_dhcp_flags":"0x0000","dhcp_dhcp_option_value":["01","01:00:0b:82:01:fc:42","00:00:00:00","01:03:06:2a"],"dhcp_dhcp_hw_len":"6","dhcp_dhcp_option_length":["1","7","4","4"],"dhcp_dhcp_flags_bc":false,"dhcp_dhcp_id":"0x00003d1d","dhcp_dhcp_hw_mac_addr":["00:0b:82:01:fc:42","00:0b:82:01:fc:42"],"dhcp_dhcp_ip_client":"0.0.0.0","dhcp_dhcp_secs":"0","dhcp_dhcp_server":"","dhcp_dhcp_hw_addr_padding":"00:00:00:00:00:00:00:00:00:00","dhcp_dhcp_option_type":["53","61","50","55","0"],"dhcp_dhcp_hops":"0","dhcp_dhcp_file":"","dhcp_dhcp_ip_server":"0.0.0.0","dhcp_dhcp_option_dhcp":"1","dhcp_dhcp_option_request_list_item":["1","3","6","42"],"dhcp_dhcp_cookie":"99.130.83.99","dhcp_dhcp_option_padding":"00:00:00:00:00:00:00","dhcp_dhcp_ip_relay":"0.0.0.0","dhcp_dhcp_type":"1","dhcp_dhcp_flags_reserved":"0x0000","dhcp_dhcp_option_end":"255"},"udp":{"udp_udp_time_delta":"0.000000000","udp_udp_dstport":"67","udp_udp_checksum":"0x591f","udp_udp_port":["68","67"],"udp_udp_checksum_status":"2","udp_udp_stream":"0","udp_udp_length":"280","text":"Timestamps","udp_udp_srcport":"68","udp_udp_payload":"01:01:06:00:00:00:3d:1d:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:0b:82:01:fc:42: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: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: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: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: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: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: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:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:63:82:53:63:35:01:01:3d:07:01:00:0b:82:01:fc:42:32:04:00:00:00:00:37:04:01:03:06:2a:ff:00:00:00:00:00:00:00","udp_udp_time_relative":"0.000000000"},"ip":{"ip_ip_flags_rb":false,"ip_ip_addr":["0.0.0.0","255.255.255.255"],"ip_ip_dsfield_ecn":"0","ip_ip_frag_offset":"0","ip_ip_hdr_len":"20","ip_ip_id":"0xa836","ip_ip_version":"4","ip_ip_dst":"255.255.255.255","ip_ip_host":["0.0.0.0","255.255.255.255"],"ip_ip_flags":"0x00","ip_ip_src_host":"0.0.0.0","ip_ip_flags_df":false,"ip_ip_len":"300","ip_ip_checksum_status":"2","ip_ip_dst_host":"255.255.255.255","ip_ip_src":"0.0.0.0","ip_ip_ttl":"250","ip_ip_flags_mf":false,"ip_ip_checksum":"0x178b","ip_ip_proto":"17","ip_ip_dsfield_dscp":"0","ip_ip_dsfield":"0x00"},"frame":{"frame_frame_len":"314","frame_frame_marked":false,"frame_frame_number":"1","frame_frame_time_epoch":"2004-12-05T19:16:24.317453000Z","frame_frame_time":"2004-12-05T19:16:24.317453000Z","frame_frame_time_relative":"0.000000000","frame_frame_encap_type":"1","frame_frame_offset_shift":"0.000000000","frame_frame_time_delta_displayed":"0.000000000","frame_frame_time_utc":"2004-12-05T19:16:24.317453000Z","frame_frame_ignored":false,"frame_frame_cap_len":"314","frame_frame_time_delta":"0.000000000","frame_frame_protocols":"eth:ethertype:ip:udp:dhcp"},"eth":{"eth_eth_src_lg":false,"eth_eth_dst":"ff:ff:ff:ff:ff:ff","eth_eth_lg":[true,false],"eth_eth_dst_oui":"16777215","eth_eth_addr_oui":["16777215","2946"],"eth_eth_src_resolved":"GrandstreamN_01:fc:42","eth_eth_addr_oui_resolved":"Grandstream Networks, Inc.","eth_eth_src_oui_resolved":"Grandstream Networks, Inc.","eth_eth_src_oui":"2946","eth_eth_addr_resolved":["Broadcast","GrandstreamN_01:fc:42"],"eth_eth_type":"0x0800","eth_eth_src":"00:0b:82:01:fc:42","eth_eth_addr":["ff:ff:ff:ff:ff:ff","00:0b:82:01:fc:42"],"eth_eth_dst_ig":true,"eth_eth_dst_lg":true,"eth_eth_src_ig":false,"eth_eth_ig":[true,false],"eth_eth_dst_resolved":"Broadcast"}}}
+{"index":{"_index":"packets-2004-12-05","_type":"doc"}}
+{"timestamp":"1102274184317","layers":{"dhcp":{"dhcp_dhcp_option_dhcp_server_id":"192.168.0.1","dhcp_dhcp_hw_type":"0x01","dhcp_dhcp_ip_your":"192.168.0.10","dhcp_dhcp_flags":"0x0000","dhcp_dhcp_option_ip_address_lease_time":"3600","dhcp_dhcp_option_value":["02","ff:ff:ff:00","00:00:07:08","00:00:0c:4e","00:00:0e:10","c0:a8:00:01"],"dhcp_dhcp_hw_len":"6","dhcp_dhcp_option_length":["1","4","4","4","4","4"],"dhcp_dhcp_flags_bc":false,"dhcp_dhcp_id":"0x00003d1d","dhcp_dhcp_hw_mac_addr":"00:0b:82:01:fc:42","dhcp_dhcp_ip_client":"0.0.0.0","dhcp_dhcp_secs":"0","dhcp_dhcp_server":"","dhcp_dhcp_option_end":"255","dhcp_dhcp_hw_addr_padding":"00:00:00:00:00:00:00:00:00:00","dhcp_dhcp_option_type":["53","1","58","59","51","54","0"],"dhcp_dhcp_hops":"0","dhcp_dhcp_file":"","dhcp_dhcp_ip_server":"192.168.0.1","dhcp_dhcp_option_dhcp":"2","dhcp_dhcp_option_subnet_mask":"255.255.255.0","dhcp_dhcp_cookie":"99.130.83.99","dhcp_dhcp_option_renewal_time_value":"1800","dhcp_dhcp_ip_relay":"0.0.0.0","dhcp_dhcp_type":"2","dhcp_dhcp_flags_reserved":"0x0000","dhcp_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","dhcp_dhcp_option_rebinding_time_value":"3150"},"udp":{"udp_udp_time_delta":"0.000000000","udp_udp_dstport":"68","udp_udp_checksum":"0x2233","udp_udp_port":["67","68"],"udp_udp_checksum_status":"2","udp_udp_stream":"1","udp_udp_length":"308","text":"Timestamps","udp_udp_srcport":"67","udp_udp_payload":"02:01:06:00:00:00:3d:1d:00:00:00:00:00:00:00:00:c0:a8:00:0a:c0:a8:00:01:00:00:00:00:00:0b:82:01:fc:42: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: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: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: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: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: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: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:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:63:82:53:63:35:01:02:01:04:ff:ff:ff:00:3a:04:00:00:07:08:3b:04:00:00:0c:4e:33:04:00:00:0e:10:36:04:c0:a8:00:01:ff: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","udp_udp_time_relative":"0.000000000"},"ip":{"ip_ip_flags_rb":false,"ip_ip_addr":["192.168.0.1","192.168.0.10"],"ip_ip_dsfield_ecn":"0","ip_ip_frag_offset":"0","ip_ip_hdr_len":"20","ip_ip_id":"0x0445","ip_ip_version":"4","ip_ip_dst":"192.168.0.10","ip_ip_host":["192.168.0.1","192.168.0.10"],"ip_ip_flags":"0x00","ip_ip_src_host":"192.168.0.1","ip_ip_flags_df":false,"ip_ip_len":"328","ip_ip_checksum_status":"2","ip_ip_dst_host":"192.168.0.10","ip_ip_src":"192.168.0.1","ip_ip_ttl":"128","ip_ip_flags_mf":false,"ip_ip_checksum":"0x0000","ip_ip_proto":"17","ip_ip_dsfield_dscp":"0","ip_ip_dsfield":"0x00"},"frame":{"frame_frame_len":"342","frame_frame_marked":false,"frame_frame_number":"2","frame_frame_time_epoch":"2004-12-05T19:16:24.317748000Z","frame_frame_time":"2004-12-05T19:16:24.317748000Z","frame_frame_time_relative":"0.000295000","frame_frame_encap_type":"1","frame_frame_offset_shift":"0.000000000","frame_frame_time_delta_displayed":"0.000295000","frame_frame_time_utc":"2004-12-05T19:16:24.317748000Z","frame_frame_ignored":false,"frame_frame_cap_len":"342","frame_frame_time_delta":"0.000295000","frame_frame_protocols":"eth:ethertype:ip:udp:dhcp"},"eth":{"eth_eth_src_lg":false,"eth_eth_dst":"00:0b:82:01:fc:42","eth_eth_lg":[false,false],"eth_eth_dst_resolved":"GrandstreamN_01:fc:42","eth_eth_addr_oui":["2946","2164"],"eth_eth_src_ig":false,"eth_eth_src_resolved":"Dell_ad:f1:9b","eth_eth_addr_oui_resolved":["Grandstream Networks, Inc.","Dell Inc."],"eth_eth_src_oui":"2164","eth_eth_src_oui_resolved":"Dell Inc.","eth_eth_addr_resolved":["GrandstreamN_01:fc:42","Dell_ad:f1:9b"],"eth_eth_type":"0x0800","eth_eth_src":"00:08:74:ad:f1:9b","eth_eth_addr":["00:0b:82:01:fc:42","00:08:74:ad:f1:9b"],"eth_eth_dst_ig":false,"eth_eth_dst_oui_resolved":"Grandstream Networks, Inc.","eth_eth_dst_lg":false,"eth_eth_ig":[false,false],"eth_eth_dst_oui":"2946"}}}
+{"index":{"_index":"packets-2004-12-05","_type":"doc"}}
+{"timestamp":"1102274184387","layers":{"dhcp":{"dhcp_dhcp_option_requested_ip_address":"192.168.0.10","dhcp_dhcp_hw_type":["0x01","0x01"],"dhcp_dhcp_ip_your":"0.0.0.0","dhcp_dhcp_flags":"0x0000","dhcp_dhcp_option_value":["03","01:00:0b:82:01:fc:42","c0:a8:00:0a","c0:a8:00:01","01:03:06:2a"],"dhcp_dhcp_hw_len":"6","dhcp_dhcp_option_length":["1","7","4","4","4"],"dhcp_dhcp_flags_bc":false,"dhcp_dhcp_id":"0x00003d1e","dhcp_dhcp_option_dhcp_server_id":"192.168.0.1","dhcp_dhcp_hw_mac_addr":["00:0b:82:01:fc:42","00:0b:82:01:fc:42"],"dhcp_dhcp_ip_client":"0.0.0.0","dhcp_dhcp_secs":"0","dhcp_dhcp_server":"","dhcp_dhcp_hw_addr_padding":"00:00:00:00:00:00:00:00:00:00","dhcp_dhcp_option_type":["53","61","50","54","55","0"],"dhcp_dhcp_hops":"0","dhcp_dhcp_file":"","dhcp_dhcp_ip_server":"0.0.0.0","dhcp_dhcp_option_dhcp":"3","dhcp_dhcp_option_request_list_item":["1","3","6","42"],"dhcp_dhcp_cookie":"99.130.83.99","dhcp_dhcp_option_padding":"00","dhcp_dhcp_ip_relay":"0.0.0.0","dhcp_dhcp_type":"1","dhcp_dhcp_flags_reserved":"0x0000","dhcp_dhcp_option_end":"255"},"udp":{"udp_udp_time_delta":"0.070031000","udp_udp_dstport":"67","udp_udp_checksum":"0x9fbd","udp_udp_port":["68","67"],"udp_udp_checksum_status":"2","udp_udp_stream":"0","udp_udp_length":"280","text":"Timestamps","udp_udp_srcport":"68","udp_udp_payload":"01:01:06:00:00:00:3d:1e:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:0b:82:01:fc:42: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: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: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: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: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: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: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:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:63:82:53:63:35:01:03:3d:07:01:00:0b:82:01:fc:42:32:04:c0:a8:00:0a:36:04:c0:a8:00:01:37:04:01:03:06:2a:ff:00","udp_udp_time_relative":"0.070031000"},"ip":{"ip_ip_flags_rb":false,"ip_ip_addr":["0.0.0.0","255.255.255.255"],"ip_ip_dsfield_ecn":"0","ip_ip_frag_offset":"0","ip_ip_hdr_len":"20","ip_ip_id":"0xa837","ip_ip_version":"4","ip_ip_dst":"255.255.255.255","ip_ip_host":["0.0.0.0","255.255.255.255"],"ip_ip_flags":"0x00","ip_ip_src_host":"0.0.0.0","ip_ip_flags_df":false,"ip_ip_len":"300","ip_ip_checksum_status":"2","ip_ip_dst_host":"255.255.255.255","ip_ip_src":"0.0.0.0","ip_ip_ttl":"250","ip_ip_flags_mf":false,"ip_ip_checksum":"0x178a","ip_ip_proto":"17","ip_ip_dsfield_dscp":"0","ip_ip_dsfield":"0x00"},"frame":{"frame_frame_len":"314","frame_frame_marked":false,"frame_frame_number":"3","frame_frame_time_epoch":"2004-12-05T19:16:24.387484000Z","frame_frame_time":"2004-12-05T19:16:24.387484000Z","frame_frame_time_relative":"0.070031000","frame_frame_encap_type":"1","frame_frame_offset_shift":"0.000000000","frame_frame_time_delta_displayed":"0.069736000","frame_frame_time_utc":"2004-12-05T19:16:24.387484000Z","frame_frame_ignored":false,"frame_frame_cap_len":"314","frame_frame_time_delta":"0.069736000","frame_frame_protocols":"eth:ethertype:ip:udp:dhcp"},"eth":{"eth_eth_src_lg":false,"eth_eth_dst":"ff:ff:ff:ff:ff:ff","eth_eth_lg":[true,false],"eth_eth_dst_oui":"16777215","eth_eth_addr_oui":["16777215","2946"],"eth_eth_src_resolved":"GrandstreamN_01:fc:42","eth_eth_addr_oui_resolved":"Grandstream Networks, Inc.","eth_eth_src_oui_resolved":"Grandstream Networks, Inc.","eth_eth_src_oui":"2946","eth_eth_addr_resolved":["Broadcast","GrandstreamN_01:fc:42"],"eth_eth_type":"0x0800","eth_eth_src":"00:0b:82:01:fc:42","eth_eth_addr":["ff:ff:ff:ff:ff:ff","00:0b:82:01:fc:42"],"eth_eth_dst_ig":true,"eth_eth_dst_lg":true,"eth_eth_src_ig":false,"eth_eth_ig":[true,false],"eth_eth_dst_resolved":"Broadcast"}}}
+{"index":{"_index":"packets-2004-12-05","_type":"doc"}}
+{"timestamp":"1102274184387","layers":{"dhcp":{"dhcp_dhcp_option_dhcp_server_id":"192.168.0.1","dhcp_dhcp_hw_type":"0x01","dhcp_dhcp_ip_your":"192.168.0.10","dhcp_dhcp_flags":"0x0000","dhcp_dhcp_option_ip_address_lease_time":"3600","dhcp_dhcp_option_value":["05","00:00:07:08","00:00:0c:4e","00:00:0e:10","c0:a8:00:01","ff:ff:ff:00"],"dhcp_dhcp_hw_len":"6","dhcp_dhcp_option_length":["1","4","4","4","4","4"],"dhcp_dhcp_flags_bc":false,"dhcp_dhcp_id":"0x00003d1e","dhcp_dhcp_hw_mac_addr":"00:0b:82:01:fc:42","dhcp_dhcp_ip_client":"0.0.0.0","dhcp_dhcp_secs":"0","dhcp_dhcp_server":"","dhcp_dhcp_option_end":"255","dhcp_dhcp_hw_addr_padding":"00:00:00:00:00:00:00:00:00:00","dhcp_dhcp_option_type":["53","58","59","51","54","1","0"],"dhcp_dhcp_hops":"0","dhcp_dhcp_file":"","dhcp_dhcp_ip_server":"0.0.0.0","dhcp_dhcp_option_dhcp":"5","dhcp_dhcp_option_subnet_mask":"255.255.255.0","dhcp_dhcp_cookie":"99.130.83.99","dhcp_dhcp_option_renewal_time_value":"1800","dhcp_dhcp_ip_relay":"0.0.0.0","dhcp_dhcp_type":"2","dhcp_dhcp_flags_reserved":"0x0000","dhcp_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","dhcp_dhcp_option_rebinding_time_value":"3150"},"udp":{"udp_udp_time_delta":"0.070050000","udp_udp_dstport":"68","udp_udp_checksum":"0xdfdb","udp_udp_port":["67","68"],"udp_udp_checksum_status":"2","udp_udp_stream":"1","udp_udp_length":"308","text":"Timestamps","udp_udp_srcport":"67","udp_udp_payload":"02:01:06:00:00:00:3d:1e:00:00:00:00:00:00:00:00:c0:a8:00:0a:00:00:00:00:00:00:00:00:00:0b:82:01:fc:42: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: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: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: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: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: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: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:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:63:82:53:63:35:01:05:3a:04:00:00:07:08:3b:04:00:00:0c:4e:33:04:00:00:0e:10:36:04:c0:a8:00:01:01:04:ff:ff:ff:00:ff: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","udp_udp_time_relative":"0.070050000"},"ip":{"ip_ip_flags_rb":false,"ip_ip_addr":["192.168.0.1","192.168.0.10"],"ip_ip_dsfield_ecn":"0","ip_ip_frag_offset":"0","ip_ip_hdr_len":"20","ip_ip_id":"0x0446","ip_ip_version":"4","ip_ip_dst":"192.168.0.10","ip_ip_host":["192.168.0.1","192.168.0.10"],"ip_ip_flags":"0x00","ip_ip_src_host":"192.168.0.1","ip_ip_flags_df":false,"ip_ip_len":"328","ip_ip_checksum_status":"2","ip_ip_dst_host":"192.168.0.10","ip_ip_src":"192.168.0.1","ip_ip_ttl":"128","ip_ip_flags_mf":false,"ip_ip_checksum":"0x0000","ip_ip_proto":"17","ip_ip_dsfield_dscp":"0","ip_ip_dsfield":"0x00"},"frame":{"frame_frame_len":"342","frame_frame_marked":false,"frame_frame_number":"4","frame_frame_time_epoch":"2004-12-05T19:16:24.387798000Z","frame_frame_time":"2004-12-05T19:16:24.387798000Z","frame_frame_time_relative":"0.070345000","frame_frame_encap_type":"1","frame_frame_offset_shift":"0.000000000","frame_frame_time_delta_displayed":"0.000314000","frame_frame_time_utc":"2004-12-05T19:16:24.387798000Z","frame_frame_ignored":false,"frame_frame_cap_len":"342","frame_frame_time_delta":"0.000314000","frame_frame_protocols":"eth:ethertype:ip:udp:dhcp"},"eth":{"eth_eth_src_lg":false,"eth_eth_dst":"00:0b:82:01:fc:42","eth_eth_lg":[false,false],"eth_eth_dst_resolved":"GrandstreamN_01:fc:42","eth_eth_addr_oui":["2946","2164"],"eth_eth_src_ig":false,"eth_eth_src_resolved":"Dell_ad:f1:9b","eth_eth_addr_oui_resolved":["Grandstream Networks, Inc.","Dell Inc."],"eth_eth_src_oui":"2164","eth_eth_src_oui_resolved":"Dell Inc.","eth_eth_addr_resolved":["GrandstreamN_01:fc:42","Dell_ad:f1:9b"],"eth_eth_type":"0x0800","eth_eth_src":"00:08:74:ad:f1:9b","eth_eth_addr":["00:0b:82:01:fc:42","00:08:74:ad:f1:9b"],"eth_eth_dst_ig":false,"eth_eth_dst_oui_resolved":"Grandstream Networks, Inc.","eth_eth_dst_lg":false,"eth_eth_ig":[false,false],"eth_eth_dst_oui":"2946"}}}
diff --git a/test/baseline/dhcp.json b/test/baseline/dhcp.json
new file mode 100644
index 0000000..4e61bf8
--- /dev/null
+++ b/test/baseline/dhcp.json
@@ -0,0 +1,644 @@
+[
+ {
+ "_index": "packets-2004-12-05",
+ "_type": "doc",
+ "_score": null,
+ "_source": {
+ "layers": {
+ "frame": {
+ "frame.encap_type": "1",
+ "frame.time": "Dec 5, 2004 19:16:24.317453000 UTC",
+ "frame.time_utc": "Dec 5, 2004 19:16:24.317453000 UTC",
+ "frame.time_epoch": "1102274184.317453000",
+ "frame.offset_shift": "0.000000000",
+ "frame.time_delta": "0.000000000",
+ "frame.time_delta_displayed": "0.000000000",
+ "frame.time_relative": "0.000000000",
+ "frame.number": "1",
+ "frame.len": "314",
+ "frame.cap_len": "314",
+ "frame.marked": "0",
+ "frame.ignored": "0",
+ "frame.protocols": "eth:ethertype:ip:udp:dhcp"
+ },
+ "eth": {
+ "eth.dst": "ff:ff:ff:ff:ff:ff",
+ "eth.dst_tree": {
+ "eth.dst_resolved": "Broadcast",
+ "eth.dst.oui": "16777215",
+ "eth.addr": "ff:ff:ff:ff:ff:ff",
+ "eth.addr_resolved": "Broadcast",
+ "eth.addr.oui": "16777215",
+ "eth.dst.lg": "1",
+ "eth.lg": "1",
+ "eth.dst.ig": "1",
+ "eth.ig": "1"
+ },
+ "eth.src": "00:0b:82:01:fc:42",
+ "eth.src_tree": {
+ "eth.src_resolved": "GrandstreamN_01:fc:42",
+ "eth.src.oui": "2946",
+ "eth.src.oui_resolved": "Grandstream Networks, Inc.",
+ "eth.addr": "00:0b:82:01:fc:42",
+ "eth.addr_resolved": "GrandstreamN_01:fc:42",
+ "eth.addr.oui": "2946",
+ "eth.addr.oui_resolved": "Grandstream Networks, Inc.",
+ "eth.src.lg": "0",
+ "eth.lg": "0",
+ "eth.src.ig": "0",
+ "eth.ig": "0"
+ },
+ "eth.type": "0x0800"
+ },
+ "ip": {
+ "ip.version": "4",
+ "ip.hdr_len": "20",
+ "ip.dsfield": "0x00",
+ "ip.dsfield_tree": {
+ "ip.dsfield.dscp": "0",
+ "ip.dsfield.ecn": "0"
+ },
+ "ip.len": "300",
+ "ip.id": "0xa836",
+ "ip.flags": "0x00",
+ "ip.flags_tree": {
+ "ip.flags.rb": "0",
+ "ip.flags.df": "0",
+ "ip.flags.mf": "0"
+ },
+ "ip.frag_offset": "0",
+ "ip.ttl": "250",
+ "ip.proto": "17",
+ "ip.checksum": "0x178b",
+ "ip.checksum.status": "2",
+ "ip.src": "0.0.0.0",
+ "ip.addr": "0.0.0.0",
+ "ip.src_host": "0.0.0.0",
+ "ip.host": "0.0.0.0",
+ "ip.dst": "255.255.255.255",
+ "ip.addr": "255.255.255.255",
+ "ip.dst_host": "255.255.255.255",
+ "ip.host": "255.255.255.255"
+ },
+ "udp": {
+ "udp.srcport": "68",
+ "udp.dstport": "67",
+ "udp.port": "68",
+ "udp.port": "67",
+ "udp.length": "280",
+ "udp.checksum": "0x591f",
+ "udp.checksum.status": "2",
+ "udp.stream": "0",
+ "Timestamps": {
+ "udp.time_relative": "0.000000000",
+ "udp.time_delta": "0.000000000"
+ },
+ "udp.payload": "01:01:06:00:00:00:3d:1d:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:0b:82:01:fc:42: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: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: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: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: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: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: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:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:63:82:53:63:35:01:01:3d:07:01:00:0b:82:01:fc:42:32:04:00:00:00:00:37:04:01:03:06:2a:ff:00:00:00:00:00:00:00"
+ },
+ "dhcp": {
+ "dhcp.type": "1",
+ "dhcp.hw.type": "0x01",
+ "dhcp.hw.len": "6",
+ "dhcp.hops": "0",
+ "dhcp.id": "0x00003d1d",
+ "dhcp.secs": "0",
+ "dhcp.flags": "0x0000",
+ "dhcp.flags_tree": {
+ "dhcp.flags.bc": "0",
+ "dhcp.flags.reserved": "0x0000"
+ },
+ "dhcp.ip.client": "0.0.0.0",
+ "dhcp.ip.your": "0.0.0.0",
+ "dhcp.ip.server": "0.0.0.0",
+ "dhcp.ip.relay": "0.0.0.0",
+ "dhcp.hw.mac_addr": "00:0b:82:01:fc:42",
+ "dhcp.hw.addr_padding": "00:00:00:00:00:00:00:00:00:00",
+ "dhcp.server": "",
+ "dhcp.file": "",
+ "dhcp.cookie": "99.130.83.99",
+ "dhcp.option.type": "53",
+ "dhcp.option.type_tree": {
+ "dhcp.option.length": "1",
+ "dhcp.option.value": "01",
+ "dhcp.option.dhcp": "1"
+ },
+ "dhcp.option.type": "61",
+ "dhcp.option.type_tree": {
+ "dhcp.option.length": "7",
+ "dhcp.option.value": "01:00:0b:82:01:fc:42",
+ "dhcp.hw.type": "0x01",
+ "dhcp.hw.mac_addr": "00:0b:82:01:fc:42"
+ },
+ "dhcp.option.type": "50",
+ "dhcp.option.type_tree": {
+ "dhcp.option.length": "4",
+ "dhcp.option.value": "00:00:00:00",
+ "dhcp.option.requested_ip_address": "0.0.0.0"
+ },
+ "dhcp.option.type": "55",
+ "dhcp.option.type_tree": {
+ "dhcp.option.length": "4",
+ "dhcp.option.value": "01:03:06:2a",
+ "dhcp.option.request_list_item": "1",
+ "dhcp.option.request_list_item": "3",
+ "dhcp.option.request_list_item": "6",
+ "dhcp.option.request_list_item": "42"
+ },
+ "dhcp.option.type": "0",
+ "dhcp.option.type_tree": {
+ "dhcp.option.end": "255"
+ },
+ "dhcp.option.padding": "00:00:00:00:00:00:00"
+ }
+ }
+ }
+ },
+ {
+ "_index": "packets-2004-12-05",
+ "_type": "doc",
+ "_score": null,
+ "_source": {
+ "layers": {
+ "frame": {
+ "frame.encap_type": "1",
+ "frame.time": "Dec 5, 2004 19:16:24.317748000 UTC",
+ "frame.time_utc": "Dec 5, 2004 19:16:24.317748000 UTC",
+ "frame.time_epoch": "1102274184.317748000",
+ "frame.offset_shift": "0.000000000",
+ "frame.time_delta": "0.000295000",
+ "frame.time_delta_displayed": "0.000295000",
+ "frame.time_relative": "0.000295000",
+ "frame.number": "2",
+ "frame.len": "342",
+ "frame.cap_len": "342",
+ "frame.marked": "0",
+ "frame.ignored": "0",
+ "frame.protocols": "eth:ethertype:ip:udp:dhcp"
+ },
+ "eth": {
+ "eth.dst": "00:0b:82:01:fc:42",
+ "eth.dst_tree": {
+ "eth.dst_resolved": "GrandstreamN_01:fc:42",
+ "eth.dst.oui": "2946",
+ "eth.dst.oui_resolved": "Grandstream Networks, Inc.",
+ "eth.addr": "00:0b:82:01:fc:42",
+ "eth.addr_resolved": "GrandstreamN_01:fc:42",
+ "eth.addr.oui": "2946",
+ "eth.addr.oui_resolved": "Grandstream Networks, Inc.",
+ "eth.dst.lg": "0",
+ "eth.lg": "0",
+ "eth.dst.ig": "0",
+ "eth.ig": "0"
+ },
+ "eth.src": "00:08:74:ad:f1:9b",
+ "eth.src_tree": {
+ "eth.src_resolved": "Dell_ad:f1:9b",
+ "eth.src.oui": "2164",
+ "eth.src.oui_resolved": "Dell Inc.",
+ "eth.addr": "00:08:74:ad:f1:9b",
+ "eth.addr_resolved": "Dell_ad:f1:9b",
+ "eth.addr.oui": "2164",
+ "eth.addr.oui_resolved": "Dell Inc.",
+ "eth.src.lg": "0",
+ "eth.lg": "0",
+ "eth.src.ig": "0",
+ "eth.ig": "0"
+ },
+ "eth.type": "0x0800"
+ },
+ "ip": {
+ "ip.version": "4",
+ "ip.hdr_len": "20",
+ "ip.dsfield": "0x00",
+ "ip.dsfield_tree": {
+ "ip.dsfield.dscp": "0",
+ "ip.dsfield.ecn": "0"
+ },
+ "ip.len": "328",
+ "ip.id": "0x0445",
+ "ip.flags": "0x00",
+ "ip.flags_tree": {
+ "ip.flags.rb": "0",
+ "ip.flags.df": "0",
+ "ip.flags.mf": "0"
+ },
+ "ip.frag_offset": "0",
+ "ip.ttl": "128",
+ "ip.proto": "17",
+ "ip.checksum": "0x0000",
+ "ip.checksum.status": "2",
+ "ip.src": "192.168.0.1",
+ "ip.addr": "192.168.0.1",
+ "ip.src_host": "192.168.0.1",
+ "ip.host": "192.168.0.1",
+ "ip.dst": "192.168.0.10",
+ "ip.addr": "192.168.0.10",
+ "ip.dst_host": "192.168.0.10",
+ "ip.host": "192.168.0.10"
+ },
+ "udp": {
+ "udp.srcport": "67",
+ "udp.dstport": "68",
+ "udp.port": "67",
+ "udp.port": "68",
+ "udp.length": "308",
+ "udp.checksum": "0x2233",
+ "udp.checksum.status": "2",
+ "udp.stream": "1",
+ "Timestamps": {
+ "udp.time_relative": "0.000000000",
+ "udp.time_delta": "0.000000000"
+ },
+ "udp.payload": "02:01:06:00:00:00:3d:1d:00:00:00:00:00:00:00:00:c0:a8:00:0a:c0:a8:00:01:00:00:00:00:00:0b:82:01:fc:42: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: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: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: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: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: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: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:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:63:82:53:63:35:01:02:01:04:ff:ff:ff:00:3a:04:00:00:07:08:3b:04:00:00:0c:4e:33:04:00:00:0e:10:36:04:c0:a8:00:01:ff: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"
+ },
+ "dhcp": {
+ "dhcp.type": "2",
+ "dhcp.hw.type": "0x01",
+ "dhcp.hw.len": "6",
+ "dhcp.hops": "0",
+ "dhcp.id": "0x00003d1d",
+ "dhcp.secs": "0",
+ "dhcp.flags": "0x0000",
+ "dhcp.flags_tree": {
+ "dhcp.flags.bc": "0",
+ "dhcp.flags.reserved": "0x0000"
+ },
+ "dhcp.ip.client": "0.0.0.0",
+ "dhcp.ip.your": "192.168.0.10",
+ "dhcp.ip.server": "192.168.0.1",
+ "dhcp.ip.relay": "0.0.0.0",
+ "dhcp.hw.mac_addr": "00:0b:82:01:fc:42",
+ "dhcp.hw.addr_padding": "00:00:00:00:00:00:00:00:00:00",
+ "dhcp.server": "",
+ "dhcp.file": "",
+ "dhcp.cookie": "99.130.83.99",
+ "dhcp.option.type": "53",
+ "dhcp.option.type_tree": {
+ "dhcp.option.length": "1",
+ "dhcp.option.value": "02",
+ "dhcp.option.dhcp": "2"
+ },
+ "dhcp.option.type": "1",
+ "dhcp.option.type_tree": {
+ "dhcp.option.length": "4",
+ "dhcp.option.value": "ff:ff:ff:00",
+ "dhcp.option.subnet_mask": "255.255.255.0"
+ },
+ "dhcp.option.type": "58",
+ "dhcp.option.type_tree": {
+ "dhcp.option.length": "4",
+ "dhcp.option.value": "00:00:07:08",
+ "dhcp.option.renewal_time_value": "1800"
+ },
+ "dhcp.option.type": "59",
+ "dhcp.option.type_tree": {
+ "dhcp.option.length": "4",
+ "dhcp.option.value": "00:00:0c:4e",
+ "dhcp.option.rebinding_time_value": "3150"
+ },
+ "dhcp.option.type": "51",
+ "dhcp.option.type_tree": {
+ "dhcp.option.length": "4",
+ "dhcp.option.value": "00:00:0e:10",
+ "dhcp.option.ip_address_lease_time": "3600"
+ },
+ "dhcp.option.type": "54",
+ "dhcp.option.type_tree": {
+ "dhcp.option.length": "4",
+ "dhcp.option.value": "c0:a8:00:01",
+ "dhcp.option.dhcp_server_id": "192.168.0.1"
+ },
+ "dhcp.option.type": "0",
+ "dhcp.option.type_tree": {
+ "dhcp.option.end": "255"
+ },
+ "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"
+ }
+ }
+ }
+ },
+ {
+ "_index": "packets-2004-12-05",
+ "_type": "doc",
+ "_score": null,
+ "_source": {
+ "layers": {
+ "frame": {
+ "frame.encap_type": "1",
+ "frame.time": "Dec 5, 2004 19:16:24.387484000 UTC",
+ "frame.time_utc": "Dec 5, 2004 19:16:24.387484000 UTC",
+ "frame.time_epoch": "1102274184.387484000",
+ "frame.offset_shift": "0.000000000",
+ "frame.time_delta": "0.069736000",
+ "frame.time_delta_displayed": "0.069736000",
+ "frame.time_relative": "0.070031000",
+ "frame.number": "3",
+ "frame.len": "314",
+ "frame.cap_len": "314",
+ "frame.marked": "0",
+ "frame.ignored": "0",
+ "frame.protocols": "eth:ethertype:ip:udp:dhcp"
+ },
+ "eth": {
+ "eth.dst": "ff:ff:ff:ff:ff:ff",
+ "eth.dst_tree": {
+ "eth.dst_resolved": "Broadcast",
+ "eth.dst.oui": "16777215",
+ "eth.addr": "ff:ff:ff:ff:ff:ff",
+ "eth.addr_resolved": "Broadcast",
+ "eth.addr.oui": "16777215",
+ "eth.dst.lg": "1",
+ "eth.lg": "1",
+ "eth.dst.ig": "1",
+ "eth.ig": "1"
+ },
+ "eth.src": "00:0b:82:01:fc:42",
+ "eth.src_tree": {
+ "eth.src_resolved": "GrandstreamN_01:fc:42",
+ "eth.src.oui": "2946",
+ "eth.src.oui_resolved": "Grandstream Networks, Inc.",
+ "eth.addr": "00:0b:82:01:fc:42",
+ "eth.addr_resolved": "GrandstreamN_01:fc:42",
+ "eth.addr.oui": "2946",
+ "eth.addr.oui_resolved": "Grandstream Networks, Inc.",
+ "eth.src.lg": "0",
+ "eth.lg": "0",
+ "eth.src.ig": "0",
+ "eth.ig": "0"
+ },
+ "eth.type": "0x0800"
+ },
+ "ip": {
+ "ip.version": "4",
+ "ip.hdr_len": "20",
+ "ip.dsfield": "0x00",
+ "ip.dsfield_tree": {
+ "ip.dsfield.dscp": "0",
+ "ip.dsfield.ecn": "0"
+ },
+ "ip.len": "300",
+ "ip.id": "0xa837",
+ "ip.flags": "0x00",
+ "ip.flags_tree": {
+ "ip.flags.rb": "0",
+ "ip.flags.df": "0",
+ "ip.flags.mf": "0"
+ },
+ "ip.frag_offset": "0",
+ "ip.ttl": "250",
+ "ip.proto": "17",
+ "ip.checksum": "0x178a",
+ "ip.checksum.status": "2",
+ "ip.src": "0.0.0.0",
+ "ip.addr": "0.0.0.0",
+ "ip.src_host": "0.0.0.0",
+ "ip.host": "0.0.0.0",
+ "ip.dst": "255.255.255.255",
+ "ip.addr": "255.255.255.255",
+ "ip.dst_host": "255.255.255.255",
+ "ip.host": "255.255.255.255"
+ },
+ "udp": {
+ "udp.srcport": "68",
+ "udp.dstport": "67",
+ "udp.port": "68",
+ "udp.port": "67",
+ "udp.length": "280",
+ "udp.checksum": "0x9fbd",
+ "udp.checksum.status": "2",
+ "udp.stream": "0",
+ "Timestamps": {
+ "udp.time_relative": "0.070031000",
+ "udp.time_delta": "0.070031000"
+ },
+ "udp.payload": "01:01:06:00:00:00:3d:1e:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:0b:82:01:fc:42: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: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: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: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: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: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: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:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:63:82:53:63:35:01:03:3d:07:01:00:0b:82:01:fc:42:32:04:c0:a8:00:0a:36:04:c0:a8:00:01:37:04:01:03:06:2a:ff:00"
+ },
+ "dhcp": {
+ "dhcp.type": "1",
+ "dhcp.hw.type": "0x01",
+ "dhcp.hw.len": "6",
+ "dhcp.hops": "0",
+ "dhcp.id": "0x00003d1e",
+ "dhcp.secs": "0",
+ "dhcp.flags": "0x0000",
+ "dhcp.flags_tree": {
+ "dhcp.flags.bc": "0",
+ "dhcp.flags.reserved": "0x0000"
+ },
+ "dhcp.ip.client": "0.0.0.0",
+ "dhcp.ip.your": "0.0.0.0",
+ "dhcp.ip.server": "0.0.0.0",
+ "dhcp.ip.relay": "0.0.0.0",
+ "dhcp.hw.mac_addr": "00:0b:82:01:fc:42",
+ "dhcp.hw.addr_padding": "00:00:00:00:00:00:00:00:00:00",
+ "dhcp.server": "",
+ "dhcp.file": "",
+ "dhcp.cookie": "99.130.83.99",
+ "dhcp.option.type": "53",
+ "dhcp.option.type_tree": {
+ "dhcp.option.length": "1",
+ "dhcp.option.value": "03",
+ "dhcp.option.dhcp": "3"
+ },
+ "dhcp.option.type": "61",
+ "dhcp.option.type_tree": {
+ "dhcp.option.length": "7",
+ "dhcp.option.value": "01:00:0b:82:01:fc:42",
+ "dhcp.hw.type": "0x01",
+ "dhcp.hw.mac_addr": "00:0b:82:01:fc:42"
+ },
+ "dhcp.option.type": "50",
+ "dhcp.option.type_tree": {
+ "dhcp.option.length": "4",
+ "dhcp.option.value": "c0:a8:00:0a",
+ "dhcp.option.requested_ip_address": "192.168.0.10"
+ },
+ "dhcp.option.type": "54",
+ "dhcp.option.type_tree": {
+ "dhcp.option.length": "4",
+ "dhcp.option.value": "c0:a8:00:01",
+ "dhcp.option.dhcp_server_id": "192.168.0.1"
+ },
+ "dhcp.option.type": "55",
+ "dhcp.option.type_tree": {
+ "dhcp.option.length": "4",
+ "dhcp.option.value": "01:03:06:2a",
+ "dhcp.option.request_list_item": "1",
+ "dhcp.option.request_list_item": "3",
+ "dhcp.option.request_list_item": "6",
+ "dhcp.option.request_list_item": "42"
+ },
+ "dhcp.option.type": "0",
+ "dhcp.option.type_tree": {
+ "dhcp.option.end": "255"
+ },
+ "dhcp.option.padding": "00"
+ }
+ }
+ }
+ },
+ {
+ "_index": "packets-2004-12-05",
+ "_type": "doc",
+ "_score": null,
+ "_source": {
+ "layers": {
+ "frame": {
+ "frame.encap_type": "1",
+ "frame.time": "Dec 5, 2004 19:16:24.387798000 UTC",
+ "frame.time_utc": "Dec 5, 2004 19:16:24.387798000 UTC",
+ "frame.time_epoch": "1102274184.387798000",
+ "frame.offset_shift": "0.000000000",
+ "frame.time_delta": "0.000314000",
+ "frame.time_delta_displayed": "0.000314000",
+ "frame.time_relative": "0.070345000",
+ "frame.number": "4",
+ "frame.len": "342",
+ "frame.cap_len": "342",
+ "frame.marked": "0",
+ "frame.ignored": "0",
+ "frame.protocols": "eth:ethertype:ip:udp:dhcp"
+ },
+ "eth": {
+ "eth.dst": "00:0b:82:01:fc:42",
+ "eth.dst_tree": {
+ "eth.dst_resolved": "GrandstreamN_01:fc:42",
+ "eth.dst.oui": "2946",
+ "eth.dst.oui_resolved": "Grandstream Networks, Inc.",
+ "eth.addr": "00:0b:82:01:fc:42",
+ "eth.addr_resolved": "GrandstreamN_01:fc:42",
+ "eth.addr.oui": "2946",
+ "eth.addr.oui_resolved": "Grandstream Networks, Inc.",
+ "eth.dst.lg": "0",
+ "eth.lg": "0",
+ "eth.dst.ig": "0",
+ "eth.ig": "0"
+ },
+ "eth.src": "00:08:74:ad:f1:9b",
+ "eth.src_tree": {
+ "eth.src_resolved": "Dell_ad:f1:9b",
+ "eth.src.oui": "2164",
+ "eth.src.oui_resolved": "Dell Inc.",
+ "eth.addr": "00:08:74:ad:f1:9b",
+ "eth.addr_resolved": "Dell_ad:f1:9b",
+ "eth.addr.oui": "2164",
+ "eth.addr.oui_resolved": "Dell Inc.",
+ "eth.src.lg": "0",
+ "eth.lg": "0",
+ "eth.src.ig": "0",
+ "eth.ig": "0"
+ },
+ "eth.type": "0x0800"
+ },
+ "ip": {
+ "ip.version": "4",
+ "ip.hdr_len": "20",
+ "ip.dsfield": "0x00",
+ "ip.dsfield_tree": {
+ "ip.dsfield.dscp": "0",
+ "ip.dsfield.ecn": "0"
+ },
+ "ip.len": "328",
+ "ip.id": "0x0446",
+ "ip.flags": "0x00",
+ "ip.flags_tree": {
+ "ip.flags.rb": "0",
+ "ip.flags.df": "0",
+ "ip.flags.mf": "0"
+ },
+ "ip.frag_offset": "0",
+ "ip.ttl": "128",
+ "ip.proto": "17",
+ "ip.checksum": "0x0000",
+ "ip.checksum.status": "2",
+ "ip.src": "192.168.0.1",
+ "ip.addr": "192.168.0.1",
+ "ip.src_host": "192.168.0.1",
+ "ip.host": "192.168.0.1",
+ "ip.dst": "192.168.0.10",
+ "ip.addr": "192.168.0.10",
+ "ip.dst_host": "192.168.0.10",
+ "ip.host": "192.168.0.10"
+ },
+ "udp": {
+ "udp.srcport": "67",
+ "udp.dstport": "68",
+ "udp.port": "67",
+ "udp.port": "68",
+ "udp.length": "308",
+ "udp.checksum": "0xdfdb",
+ "udp.checksum.status": "2",
+ "udp.stream": "1",
+ "Timestamps": {
+ "udp.time_relative": "0.070050000",
+ "udp.time_delta": "0.070050000"
+ },
+ "udp.payload": "02:01:06:00:00:00:3d:1e:00:00:00:00:00:00:00:00:c0:a8:00:0a:00:00:00:00:00:00:00:00:00:0b:82:01:fc:42: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: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: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: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: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: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: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:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:00:63:82:53:63:35:01:05:3a:04:00:00:07:08:3b:04:00:00:0c:4e:33:04:00:00:0e:10:36:04:c0:a8:00:01:01:04:ff:ff:ff:00:ff: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"
+ },
+ "dhcp": {
+ "dhcp.type": "2",
+ "dhcp.hw.type": "0x01",
+ "dhcp.hw.len": "6",
+ "dhcp.hops": "0",
+ "dhcp.id": "0x00003d1e",
+ "dhcp.secs": "0",
+ "dhcp.flags": "0x0000",
+ "dhcp.flags_tree": {
+ "dhcp.flags.bc": "0",
+ "dhcp.flags.reserved": "0x0000"
+ },
+ "dhcp.ip.client": "0.0.0.0",
+ "dhcp.ip.your": "192.168.0.10",
+ "dhcp.ip.server": "0.0.0.0",
+ "dhcp.ip.relay": "0.0.0.0",
+ "dhcp.hw.mac_addr": "00:0b:82:01:fc:42",
+ "dhcp.hw.addr_padding": "00:00:00:00:00:00:00:00:00:00",
+ "dhcp.server": "",
+ "dhcp.file": "",
+ "dhcp.cookie": "99.130.83.99",
+ "dhcp.option.type": "53",
+ "dhcp.option.type_tree": {
+ "dhcp.option.length": "1",
+ "dhcp.option.value": "05",
+ "dhcp.option.dhcp": "5"
+ },
+ "dhcp.option.type": "58",
+ "dhcp.option.type_tree": {
+ "dhcp.option.length": "4",
+ "dhcp.option.value": "00:00:07:08",
+ "dhcp.option.renewal_time_value": "1800"
+ },
+ "dhcp.option.type": "59",
+ "dhcp.option.type_tree": {
+ "dhcp.option.length": "4",
+ "dhcp.option.value": "00:00:0c:4e",
+ "dhcp.option.rebinding_time_value": "3150"
+ },
+ "dhcp.option.type": "51",
+ "dhcp.option.type_tree": {
+ "dhcp.option.length": "4",
+ "dhcp.option.value": "00:00:0e:10",
+ "dhcp.option.ip_address_lease_time": "3600"
+ },
+ "dhcp.option.type": "54",
+ "dhcp.option.type_tree": {
+ "dhcp.option.length": "4",
+ "dhcp.option.value": "c0:a8:00:01",
+ "dhcp.option.dhcp_server_id": "192.168.0.1"
+ },
+ "dhcp.option.type": "1",
+ "dhcp.option.type_tree": {
+ "dhcp.option.length": "4",
+ "dhcp.option.value": "ff:ff:ff:00",
+ "dhcp.option.subnet_mask": "255.255.255.0"
+ },
+ "dhcp.option.type": "0",
+ "dhcp.option.type_tree": {
+ "dhcp.option.end": "255"
+ },
+ "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"
+ }
+ }
+ }
+ }
+]
diff --git a/test/baseline/dhcp.jsonraw b/test/baseline/dhcp.jsonraw
new file mode 100644
index 0000000..69a7526
--- /dev/null
+++ b/test/baseline/dhcp.jsonraw
@@ -0,0 +1,3592 @@
+[
+ {
+ "_index": "packets-2004-12-05",
+ "_type": "doc",
+ "_score": null,
+ "_source": {
+ "layers": {
+ "frame_raw": [
+ "ffffffffffff000b8201fc4208004500012ca8360000fa11178b00000000ffffffff004400430118591f0101060000003d1d0000000000000000000000000000000000000000000b8201fc4200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000638253633501013d0701000b8201fc4232040000000037040103062aff00000000000000",
+ 0,
+ 314,
+ 0,
+ 1
+ ],
+ "frame": {
+ "frame.encap_type_raw": [
+ "",
+ 0,
+ 0,
+ 0,
+ 13
+ ],
+ "frame.time_raw": [
+ "",
+ 0,
+ 0,
+ 0,
+ 24
+ ],
+ "frame.time_utc_raw": [
+ "",
+ 0,
+ 0,
+ 0,
+ 24
+ ],
+ "frame.time_epoch_raw": [
+ "",
+ 0,
+ 0,
+ 0,
+ 24
+ ],
+ "frame.offset_shift_raw": [
+ "",
+ 0,
+ 0,
+ 0,
+ 25
+ ],
+ "frame.time_delta_raw": [
+ "",
+ 0,
+ 0,
+ 0,
+ 25
+ ],
+ "frame.time_delta_displayed_raw": [
+ "",
+ 0,
+ 0,
+ 0,
+ 25
+ ],
+ "frame.time_relative_raw": [
+ "",
+ 0,
+ 0,
+ 0,
+ 25
+ ],
+ "frame.number_raw": [
+ "",
+ 0,
+ 0,
+ 0,
+ 7
+ ],
+ "frame.len_raw": [
+ "",
+ 0,
+ 0,
+ 0,
+ 7
+ ],
+ "frame.cap_len_raw": [
+ "",
+ 0,
+ 0,
+ 0,
+ 7
+ ],
+ "frame.marked_raw": [
+ "",
+ 0,
+ 0,
+ 0,
+ 2
+ ],
+ "frame.ignored_raw": [
+ "",
+ 0,
+ 0,
+ 0,
+ 2
+ ],
+ "frame.protocols_raw": [
+ "",
+ 0,
+ 0,
+ 0,
+ 26
+ ]
+ },
+ "eth_raw": [
+ "ffffffffffff000b8201fc420800",
+ 0,
+ 14,
+ 0,
+ 1
+ ],
+ "eth": {
+ "eth.dst_raw": [
+ "ffffffffffff",
+ 0,
+ 6,
+ 0,
+ 29
+ ],
+ "eth.dst_tree": {
+ "eth.dst_resolved_raw": [
+ "ffffffffffff",
+ 0,
+ 6,
+ 0,
+ 26
+ ],
+ "eth.dst.oui_raw": [
+ "ffffff",
+ 0,
+ 3,
+ 0,
+ 6
+ ],
+ "eth.addr_raw": [
+ "ffffffffffff",
+ 0,
+ 6,
+ 0,
+ 29
+ ],
+ "eth.addr_resolved_raw": [
+ "ffffffffffff",
+ 0,
+ 6,
+ 0,
+ 26
+ ],
+ "eth.addr.oui_raw": [
+ "ffffff",
+ 0,
+ 3,
+ 0,
+ 6
+ ],
+ "eth.dst.lg_raw": [
+ "1",
+ 0,
+ 3,
+ 131072,
+ 2
+ ],
+ "eth.lg_raw": [
+ "1",
+ 0,
+ 3,
+ 131072,
+ 2
+ ],
+ "eth.dst.ig_raw": [
+ "1",
+ 0,
+ 3,
+ 65536,
+ 2
+ ],
+ "eth.ig_raw": [
+ "1",
+ 0,
+ 3,
+ 65536,
+ 2
+ ]
+ },
+ "eth.src_raw": [
+ "000b8201fc42",
+ 6,
+ 6,
+ 0,
+ 29
+ ],
+ "eth.src_tree": {
+ "eth.src_resolved_raw": [
+ "000b8201fc42",
+ 6,
+ 6,
+ 0,
+ 26
+ ],
+ "eth.src.oui_raw": [
+ "000b82",
+ 6,
+ 3,
+ 0,
+ 6
+ ],
+ "eth.src.oui_resolved_raw": [
+ "000b8201fc42",
+ 6,
+ 6,
+ 0,
+ 26
+ ],
+ "eth.addr_raw": [
+ "000b8201fc42",
+ 6,
+ 6,
+ 0,
+ 29
+ ],
+ "eth.addr_resolved_raw": [
+ "000b8201fc42",
+ 6,
+ 6,
+ 0,
+ 26
+ ],
+ "eth.addr.oui_raw": [
+ "000b82",
+ 6,
+ 3,
+ 0,
+ 6
+ ],
+ "eth.addr.oui_resolved_raw": [
+ "000b8201fc42",
+ 6,
+ 6,
+ 0,
+ 26
+ ],
+ "eth.src.lg_raw": [
+ "0",
+ 6,
+ 3,
+ 131072,
+ 2
+ ],
+ "eth.lg_raw": [
+ "0",
+ 6,
+ 3,
+ 131072,
+ 2
+ ],
+ "eth.src.ig_raw": [
+ "0",
+ 6,
+ 3,
+ 65536,
+ 2
+ ],
+ "eth.ig_raw": [
+ "0",
+ 6,
+ 3,
+ 65536,
+ 2
+ ]
+ },
+ "eth.type_raw": [
+ "0800",
+ 12,
+ 2,
+ 0,
+ 5
+ ]
+ },
+ "ip_raw": [
+ "4500012ca8360000fa11178b00000000ffffffff",
+ 14,
+ 20,
+ 0,
+ 1
+ ],
+ "ip": {
+ "ip.version_raw": [
+ "45",
+ 14,
+ 1,
+ 0,
+ 4
+ ],
+ "ip.hdr_len_raw": [
+ "45",
+ 14,
+ 1,
+ 0,
+ 4
+ ],
+ "ip.dsfield_raw": [
+ "00",
+ 15,
+ 1,
+ 0,
+ 4
+ ],
+ "ip.dsfield_tree": {
+ "ip.dsfield.dscp_raw": [
+ "0",
+ 15,
+ 1,
+ 252,
+ 4
+ ],
+ "ip.dsfield.ecn_raw": [
+ "0",
+ 15,
+ 1,
+ 3,
+ 4
+ ]
+ },
+ "ip.len_raw": [
+ "012c",
+ 16,
+ 2,
+ 0,
+ 5
+ ],
+ "ip.id_raw": [
+ "a836",
+ 18,
+ 2,
+ 0,
+ 5
+ ],
+ "ip.flags_raw": [
+ "0",
+ 20,
+ 1,
+ 224,
+ 4
+ ],
+ "ip.flags_tree": {
+ "ip.flags.rb_raw": [
+ "0",
+ 20,
+ 1,
+ 128,
+ 2
+ ],
+ "ip.flags.df_raw": [
+ "0",
+ 20,
+ 1,
+ 64,
+ 2
+ ],
+ "ip.flags.mf_raw": [
+ "0",
+ 20,
+ 1,
+ 32,
+ 2
+ ]
+ },
+ "ip.frag_offset_raw": [
+ "0",
+ 20,
+ 2,
+ 8191,
+ 5
+ ],
+ "ip.ttl_raw": [
+ "fa",
+ 22,
+ 1,
+ 0,
+ 4
+ ],
+ "ip.proto_raw": [
+ "11",
+ 23,
+ 1,
+ 0,
+ 4
+ ],
+ "ip.checksum_raw": [
+ "178b",
+ 24,
+ 2,
+ 0,
+ 5
+ ],
+ "ip.checksum.status_raw": [
+ "",
+ 24,
+ 0,
+ 0,
+ 4
+ ],
+ "ip.src_raw": [
+ "00000000",
+ 26,
+ 4,
+ 0,
+ 32
+ ],
+ "ip.addr_raw": [
+ "00000000",
+ 26,
+ 4,
+ 0,
+ 32
+ ],
+ "ip.src_host_raw": [
+ "00000000",
+ 26,
+ 4,
+ 0,
+ 26
+ ],
+ "ip.host_raw": [
+ "00000000",
+ 26,
+ 4,
+ 0,
+ 26
+ ],
+ "ip.dst_raw": [
+ "ffffffff",
+ 30,
+ 4,
+ 0,
+ 32
+ ],
+ "ip.addr_raw": [
+ "ffffffff",
+ 30,
+ 4,
+ 0,
+ 32
+ ],
+ "ip.dst_host_raw": [
+ "ffffffff",
+ 30,
+ 4,
+ 0,
+ 26
+ ],
+ "ip.host_raw": [
+ "ffffffff",
+ 30,
+ 4,
+ 0,
+ 26
+ ]
+ },
+ "udp_raw": [
+ "004400430118591f",
+ 34,
+ 8,
+ 0,
+ 1
+ ],
+ "udp": {
+ "udp.srcport_raw": [
+ "0044",
+ 34,
+ 2,
+ 0,
+ 5
+ ],
+ "udp.dstport_raw": [
+ "0043",
+ 36,
+ 2,
+ 0,
+ 5
+ ],
+ "udp.port_raw": [
+ "0044",
+ 34,
+ 2,
+ 0,
+ 5
+ ],
+ "udp.port_raw": [
+ "0043",
+ 36,
+ 2,
+ 0,
+ 5
+ ],
+ "udp.length_raw": [
+ "0118",
+ 38,
+ 2,
+ 0,
+ 5
+ ],
+ "udp.checksum_raw": [
+ "591f",
+ 40,
+ 2,
+ 0,
+ 5
+ ],
+ "udp.checksum.status_raw": [
+ "",
+ 40,
+ 0,
+ 0,
+ 4
+ ],
+ "udp.stream_raw": [
+ "",
+ 42,
+ 0,
+ 0,
+ 7
+ ],
+ "Timestamps": {
+ "udp.time_relative_raw": [
+ "",
+ 34,
+ 0,
+ 0,
+ 25
+ ],
+ "udp.time_delta_raw": [
+ "",
+ 34,
+ 0,
+ 0,
+ 25
+ ]
+ },
+ "udp.payload_raw": [
+ "0101060000003d1d0000000000000000000000000000000000000000000b8201fc4200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000638253633501013d0701000b8201fc4232040000000037040103062aff00000000000000",
+ 42,
+ 272,
+ 0,
+ 30
+ ]
+ },
+ "dhcp_raw": [
+ "0101060000003d1d0000000000000000000000000000000000000000000b8201fc4200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000638253633501013d0701000b8201fc4232040000000037040103062aff00000000000000",
+ 42,
+ 272,
+ 0,
+ 1
+ ],
+ "dhcp": {
+ "dhcp.type_raw": [
+ "01",
+ 42,
+ 1,
+ 0,
+ 4
+ ],
+ "dhcp.hw.type_raw": [
+ "01",
+ 43,
+ 1,
+ 0,
+ 4
+ ],
+ "dhcp.hw.len_raw": [
+ "06",
+ 44,
+ 1,
+ 0,
+ 4
+ ],
+ "dhcp.hops_raw": [
+ "00",
+ 45,
+ 1,
+ 0,
+ 4
+ ],
+ "dhcp.id_raw": [
+ "00003d1d",
+ 46,
+ 4,
+ 0,
+ 7
+ ],
+ "dhcp.secs_raw": [
+ "0000",
+ 50,
+ 2,
+ 0,
+ 5
+ ],
+ "dhcp.flags_raw": [
+ "0000",
+ 52,
+ 2,
+ 0,
+ 5
+ ],
+ "dhcp.flags_tree": {
+ "dhcp.flags.bc_raw": [
+ "0",
+ 52,
+ 2,
+ 32768,
+ 2
+ ],
+ "dhcp.flags.reserved_raw": [
+ "0",
+ 52,
+ 2,
+ 32767,
+ 5
+ ]
+ },
+ "dhcp.ip.client_raw": [
+ "00000000",
+ 54,
+ 4,
+ 0,
+ 32
+ ],
+ "dhcp.ip.your_raw": [
+ "00000000",
+ 58,
+ 4,
+ 0,
+ 32
+ ],
+ "dhcp.ip.server_raw": [
+ "00000000",
+ 62,
+ 4,
+ 0,
+ 32
+ ],
+ "dhcp.ip.relay_raw": [
+ "00000000",
+ 66,
+ 4,
+ 0,
+ 32
+ ],
+ "dhcp.hw.mac_addr_raw": [
+ "000b8201fc42",
+ 70,
+ 6,
+ 0,
+ 29
+ ],
+ "dhcp.hw.addr_padding_raw": [
+ "00000000000000000000",
+ 76,
+ 10,
+ 0,
+ 30
+ ],
+ "dhcp.server_raw": [
+ "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ 86,
+ 64,
+ 0,
+ 26
+ ],
+ "dhcp.file_raw": [
+ "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ 150,
+ 128,
+ 0,
+ 26
+ ],
+ "dhcp.cookie_raw": [
+ "63825363",
+ 278,
+ 4,
+ 0,
+ 32
+ ],
+ "dhcp.option.type_raw": [
+ "350101",
+ 282,
+ 3,
+ 0,
+ 4
+ ],
+ "dhcp.option.type_tree": {
+ "dhcp.option.length_raw": [
+ "01",
+ 283,
+ 1,
+ 0,
+ 4
+ ],
+ "dhcp.option.value_raw": [
+ "01",
+ 284,
+ 1,
+ 0,
+ 30
+ ],
+ "dhcp.option.dhcp_raw": [
+ "01",
+ 284,
+ 1,
+ 0,
+ 4
+ ]
+ },
+ "dhcp.option.type_raw": [
+ "3d0701000b8201fc42",
+ 285,
+ 9,
+ 0,
+ 4
+ ],
+ "dhcp.option.type_tree": {
+ "dhcp.option.length_raw": [
+ "07",
+ 286,
+ 1,
+ 0,
+ 4
+ ],
+ "dhcp.option.value_raw": [
+ "01000b8201fc42",
+ 287,
+ 7,
+ 0,
+ 30
+ ],
+ "dhcp.hw.type_raw": [
+ "01",
+ 287,
+ 1,
+ 0,
+ 4
+ ],
+ "dhcp.hw.mac_addr_raw": [
+ "000b8201fc42",
+ 288,
+ 6,
+ 0,
+ 29
+ ]
+ },
+ "dhcp.option.type_raw": [
+ "320400000000",
+ 294,
+ 6,
+ 0,
+ 4
+ ],
+ "dhcp.option.type_tree": {
+ "dhcp.option.length_raw": [
+ "04",
+ 295,
+ 1,
+ 0,
+ 4
+ ],
+ "dhcp.option.value_raw": [
+ "00000000",
+ 296,
+ 4,
+ 0,
+ 30
+ ],
+ "dhcp.option.requested_ip_address_raw": [
+ "00000000",
+ 296,
+ 4,
+ 0,
+ 32
+ ]
+ },
+ "dhcp.option.type_raw": [
+ "37040103062a",
+ 300,
+ 6,
+ 0,
+ 4
+ ],
+ "dhcp.option.type_tree": {
+ "dhcp.option.length_raw": [
+ "04",
+ 301,
+ 1,
+ 0,
+ 4
+ ],
+ "dhcp.option.value_raw": [
+ "0103062a",
+ 302,
+ 4,
+ 0,
+ 30
+ ],
+ "dhcp.option.request_list_item_raw": [
+ "01",
+ 302,
+ 1,
+ 0,
+ 4
+ ],
+ "dhcp.option.request_list_item_raw": [
+ "03",
+ 303,
+ 1,
+ 0,
+ 4
+ ],
+ "dhcp.option.request_list_item_raw": [
+ "06",
+ 304,
+ 1,
+ 0,
+ 4
+ ],
+ "dhcp.option.request_list_item_raw": [
+ "2a",
+ 305,
+ 1,
+ 0,
+ 4
+ ]
+ },
+ "dhcp.option.type_raw": [
+ "ff",
+ 306,
+ 1,
+ 0,
+ 4
+ ],
+ "dhcp.option.type_tree": {
+ "dhcp.option.end_raw": [
+ "ff",
+ 306,
+ 1,
+ 0,
+ 4
+ ]
+ },
+ "dhcp.option.padding_raw": [
+ "00000000000000",
+ 307,
+ 7,
+ 0,
+ 30
+ ]
+ }
+ }
+ }
+ },
+ {
+ "_index": "packets-2004-12-05",
+ "_type": "doc",
+ "_score": null,
+ "_source": {
+ "layers": {
+ "frame_raw": [
+ "000b8201fc42000874adf19b0800450001480445000080110000c0a80001c0a8000a00430044013422330201060000003d1d0000000000000000c0a8000ac0a8000100000000000b8201fc4200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000638253633501020104ffffff003a04000007083b0400000c4e330400000e103604c0a80001ff0000000000000000000000000000000000000000000000000000",
+ 0,
+ 342,
+ 0,
+ 1
+ ],
+ "frame": {
+ "frame.encap_type_raw": [
+ "",
+ 0,
+ 0,
+ 0,
+ 13
+ ],
+ "frame.time_raw": [
+ "",
+ 0,
+ 0,
+ 0,
+ 24
+ ],
+ "frame.time_utc_raw": [
+ "",
+ 0,
+ 0,
+ 0,
+ 24
+ ],
+ "frame.time_epoch_raw": [
+ "",
+ 0,
+ 0,
+ 0,
+ 24
+ ],
+ "frame.offset_shift_raw": [
+ "",
+ 0,
+ 0,
+ 0,
+ 25
+ ],
+ "frame.time_delta_raw": [
+ "",
+ 0,
+ 0,
+ 0,
+ 25
+ ],
+ "frame.time_delta_displayed_raw": [
+ "",
+ 0,
+ 0,
+ 0,
+ 25
+ ],
+ "frame.time_relative_raw": [
+ "",
+ 0,
+ 0,
+ 0,
+ 25
+ ],
+ "frame.number_raw": [
+ "",
+ 0,
+ 0,
+ 0,
+ 7
+ ],
+ "frame.len_raw": [
+ "",
+ 0,
+ 0,
+ 0,
+ 7
+ ],
+ "frame.cap_len_raw": [
+ "",
+ 0,
+ 0,
+ 0,
+ 7
+ ],
+ "frame.marked_raw": [
+ "",
+ 0,
+ 0,
+ 0,
+ 2
+ ],
+ "frame.ignored_raw": [
+ "",
+ 0,
+ 0,
+ 0,
+ 2
+ ],
+ "frame.protocols_raw": [
+ "",
+ 0,
+ 0,
+ 0,
+ 26
+ ]
+ },
+ "eth_raw": [
+ "000b8201fc42000874adf19b0800",
+ 0,
+ 14,
+ 0,
+ 1
+ ],
+ "eth": {
+ "eth.dst_raw": [
+ "000b8201fc42",
+ 0,
+ 6,
+ 0,
+ 29
+ ],
+ "eth.dst_tree": {
+ "eth.dst_resolved_raw": [
+ "000b8201fc42",
+ 0,
+ 6,
+ 0,
+ 26
+ ],
+ "eth.dst.oui_raw": [
+ "000b82",
+ 0,
+ 3,
+ 0,
+ 6
+ ],
+ "eth.dst.oui_resolved_raw": [
+ "000b8201fc42",
+ 0,
+ 6,
+ 0,
+ 26
+ ],
+ "eth.addr_raw": [
+ "000b8201fc42",
+ 0,
+ 6,
+ 0,
+ 29
+ ],
+ "eth.addr_resolved_raw": [
+ "000b8201fc42",
+ 0,
+ 6,
+ 0,
+ 26
+ ],
+ "eth.addr.oui_raw": [
+ "000b82",
+ 0,
+ 3,
+ 0,
+ 6
+ ],
+ "eth.addr.oui_resolved_raw": [
+ "000b8201fc42",
+ 0,
+ 6,
+ 0,
+ 26
+ ],
+ "eth.dst.lg_raw": [
+ "0",
+ 0,
+ 3,
+ 131072,
+ 2
+ ],
+ "eth.lg_raw": [
+ "0",
+ 0,
+ 3,
+ 131072,
+ 2
+ ],
+ "eth.dst.ig_raw": [
+ "0",
+ 0,
+ 3,
+ 65536,
+ 2
+ ],
+ "eth.ig_raw": [
+ "0",
+ 0,
+ 3,
+ 65536,
+ 2
+ ]
+ },
+ "eth.src_raw": [
+ "000874adf19b",
+ 6,
+ 6,
+ 0,
+ 29
+ ],
+ "eth.src_tree": {
+ "eth.src_resolved_raw": [
+ "000874adf19b",
+ 6,
+ 6,
+ 0,
+ 26
+ ],
+ "eth.src.oui_raw": [
+ "000874",
+ 6,
+ 3,
+ 0,
+ 6
+ ],
+ "eth.src.oui_resolved_raw": [
+ "000874adf19b",
+ 6,
+ 6,
+ 0,
+ 26
+ ],
+ "eth.addr_raw": [
+ "000874adf19b",
+ 6,
+ 6,
+ 0,
+ 29
+ ],
+ "eth.addr_resolved_raw": [
+ "000874adf19b",
+ 6,
+ 6,
+ 0,
+ 26
+ ],
+ "eth.addr.oui_raw": [
+ "000874",
+ 6,
+ 3,
+ 0,
+ 6
+ ],
+ "eth.addr.oui_resolved_raw": [
+ "000874adf19b",
+ 6,
+ 6,
+ 0,
+ 26
+ ],
+ "eth.src.lg_raw": [
+ "0",
+ 6,
+ 3,
+ 131072,
+ 2
+ ],
+ "eth.lg_raw": [
+ "0",
+ 6,
+ 3,
+ 131072,
+ 2
+ ],
+ "eth.src.ig_raw": [
+ "0",
+ 6,
+ 3,
+ 65536,
+ 2
+ ],
+ "eth.ig_raw": [
+ "0",
+ 6,
+ 3,
+ 65536,
+ 2
+ ]
+ },
+ "eth.type_raw": [
+ "0800",
+ 12,
+ 2,
+ 0,
+ 5
+ ]
+ },
+ "ip_raw": [
+ "450001480445000080110000c0a80001c0a8000a",
+ 14,
+ 20,
+ 0,
+ 1
+ ],
+ "ip": {
+ "ip.version_raw": [
+ "45",
+ 14,
+ 1,
+ 0,
+ 4
+ ],
+ "ip.hdr_len_raw": [
+ "45",
+ 14,
+ 1,
+ 0,
+ 4
+ ],
+ "ip.dsfield_raw": [
+ "00",
+ 15,
+ 1,
+ 0,
+ 4
+ ],
+ "ip.dsfield_tree": {
+ "ip.dsfield.dscp_raw": [
+ "0",
+ 15,
+ 1,
+ 252,
+ 4
+ ],
+ "ip.dsfield.ecn_raw": [
+ "0",
+ 15,
+ 1,
+ 3,
+ 4
+ ]
+ },
+ "ip.len_raw": [
+ "0148",
+ 16,
+ 2,
+ 0,
+ 5
+ ],
+ "ip.id_raw": [
+ "0445",
+ 18,
+ 2,
+ 0,
+ 5
+ ],
+ "ip.flags_raw": [
+ "0",
+ 20,
+ 1,
+ 224,
+ 4
+ ],
+ "ip.flags_tree": {
+ "ip.flags.rb_raw": [
+ "0",
+ 20,
+ 1,
+ 128,
+ 2
+ ],
+ "ip.flags.df_raw": [
+ "0",
+ 20,
+ 1,
+ 64,
+ 2
+ ],
+ "ip.flags.mf_raw": [
+ "0",
+ 20,
+ 1,
+ 32,
+ 2
+ ]
+ },
+ "ip.frag_offset_raw": [
+ "0",
+ 20,
+ 2,
+ 8191,
+ 5
+ ],
+ "ip.ttl_raw": [
+ "80",
+ 22,
+ 1,
+ 0,
+ 4
+ ],
+ "ip.proto_raw": [
+ "11",
+ 23,
+ 1,
+ 0,
+ 4
+ ],
+ "ip.checksum_raw": [
+ "0000",
+ 24,
+ 2,
+ 0,
+ 5
+ ],
+ "ip.checksum.status_raw": [
+ "",
+ 24,
+ 0,
+ 0,
+ 4
+ ],
+ "ip.src_raw": [
+ "c0a80001",
+ 26,
+ 4,
+ 0,
+ 32
+ ],
+ "ip.addr_raw": [
+ "c0a80001",
+ 26,
+ 4,
+ 0,
+ 32
+ ],
+ "ip.src_host_raw": [
+ "c0a80001",
+ 26,
+ 4,
+ 0,
+ 26
+ ],
+ "ip.host_raw": [
+ "c0a80001",
+ 26,
+ 4,
+ 0,
+ 26
+ ],
+ "ip.dst_raw": [
+ "c0a8000a",
+ 30,
+ 4,
+ 0,
+ 32
+ ],
+ "ip.addr_raw": [
+ "c0a8000a",
+ 30,
+ 4,
+ 0,
+ 32
+ ],
+ "ip.dst_host_raw": [
+ "c0a8000a",
+ 30,
+ 4,
+ 0,
+ 26
+ ],
+ "ip.host_raw": [
+ "c0a8000a",
+ 30,
+ 4,
+ 0,
+ 26
+ ]
+ },
+ "udp_raw": [
+ "0043004401342233",
+ 34,
+ 8,
+ 0,
+ 1
+ ],
+ "udp": {
+ "udp.srcport_raw": [
+ "0043",
+ 34,
+ 2,
+ 0,
+ 5
+ ],
+ "udp.dstport_raw": [
+ "0044",
+ 36,
+ 2,
+ 0,
+ 5
+ ],
+ "udp.port_raw": [
+ "0043",
+ 34,
+ 2,
+ 0,
+ 5
+ ],
+ "udp.port_raw": [
+ "0044",
+ 36,
+ 2,
+ 0,
+ 5
+ ],
+ "udp.length_raw": [
+ "0134",
+ 38,
+ 2,
+ 0,
+ 5
+ ],
+ "udp.checksum_raw": [
+ "2233",
+ 40,
+ 2,
+ 0,
+ 5
+ ],
+ "udp.checksum.status_raw": [
+ "",
+ 40,
+ 0,
+ 0,
+ 4
+ ],
+ "udp.stream_raw": [
+ "",
+ 42,
+ 0,
+ 0,
+ 7
+ ],
+ "Timestamps": {
+ "udp.time_relative_raw": [
+ "",
+ 34,
+ 0,
+ 0,
+ 25
+ ],
+ "udp.time_delta_raw": [
+ "",
+ 34,
+ 0,
+ 0,
+ 25
+ ]
+ },
+ "udp.payload_raw": [
+ "0201060000003d1d0000000000000000c0a8000ac0a8000100000000000b8201fc4200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000638253633501020104ffffff003a04000007083b0400000c4e330400000e103604c0a80001ff0000000000000000000000000000000000000000000000000000",
+ 42,
+ 300,
+ 0,
+ 30
+ ]
+ },
+ "dhcp_raw": [
+ "0201060000003d1d0000000000000000c0a8000ac0a8000100000000000b8201fc4200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000638253633501020104ffffff003a04000007083b0400000c4e330400000e103604c0a80001ff0000000000000000000000000000000000000000000000000000",
+ 42,
+ 300,
+ 0,
+ 1
+ ],
+ "dhcp": {
+ "dhcp.type_raw": [
+ "02",
+ 42,
+ 1,
+ 0,
+ 4
+ ],
+ "dhcp.hw.type_raw": [
+ "01",
+ 43,
+ 1,
+ 0,
+ 4
+ ],
+ "dhcp.hw.len_raw": [
+ "06",
+ 44,
+ 1,
+ 0,
+ 4
+ ],
+ "dhcp.hops_raw": [
+ "00",
+ 45,
+ 1,
+ 0,
+ 4
+ ],
+ "dhcp.id_raw": [
+ "00003d1d",
+ 46,
+ 4,
+ 0,
+ 7
+ ],
+ "dhcp.secs_raw": [
+ "0000",
+ 50,
+ 2,
+ 0,
+ 5
+ ],
+ "dhcp.flags_raw": [
+ "0000",
+ 52,
+ 2,
+ 0,
+ 5
+ ],
+ "dhcp.flags_tree": {
+ "dhcp.flags.bc_raw": [
+ "0",
+ 52,
+ 2,
+ 32768,
+ 2
+ ],
+ "dhcp.flags.reserved_raw": [
+ "0",
+ 52,
+ 2,
+ 32767,
+ 5
+ ]
+ },
+ "dhcp.ip.client_raw": [
+ "00000000",
+ 54,
+ 4,
+ 0,
+ 32
+ ],
+ "dhcp.ip.your_raw": [
+ "c0a8000a",
+ 58,
+ 4,
+ 0,
+ 32
+ ],
+ "dhcp.ip.server_raw": [
+ "c0a80001",
+ 62,
+ 4,
+ 0,
+ 32
+ ],
+ "dhcp.ip.relay_raw": [
+ "00000000",
+ 66,
+ 4,
+ 0,
+ 32
+ ],
+ "dhcp.hw.mac_addr_raw": [
+ "000b8201fc42",
+ 70,
+ 6,
+ 0,
+ 29
+ ],
+ "dhcp.hw.addr_padding_raw": [
+ "00000000000000000000",
+ 76,
+ 10,
+ 0,
+ 30
+ ],
+ "dhcp.server_raw": [
+ "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ 86,
+ 64,
+ 0,
+ 26
+ ],
+ "dhcp.file_raw": [
+ "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ 150,
+ 128,
+ 0,
+ 26
+ ],
+ "dhcp.cookie_raw": [
+ "63825363",
+ 278,
+ 4,
+ 0,
+ 32
+ ],
+ "dhcp.option.type_raw": [
+ "350102",
+ 282,
+ 3,
+ 0,
+ 4
+ ],
+ "dhcp.option.type_tree": {
+ "dhcp.option.length_raw": [
+ "01",
+ 283,
+ 1,
+ 0,
+ 4
+ ],
+ "dhcp.option.value_raw": [
+ "02",
+ 284,
+ 1,
+ 0,
+ 30
+ ],
+ "dhcp.option.dhcp_raw": [
+ "02",
+ 284,
+ 1,
+ 0,
+ 4
+ ]
+ },
+ "dhcp.option.type_raw": [
+ "0104ffffff00",
+ 285,
+ 6,
+ 0,
+ 4
+ ],
+ "dhcp.option.type_tree": {
+ "dhcp.option.length_raw": [
+ "04",
+ 286,
+ 1,
+ 0,
+ 4
+ ],
+ "dhcp.option.value_raw": [
+ "ffffff00",
+ 287,
+ 4,
+ 0,
+ 30
+ ],
+ "dhcp.option.subnet_mask_raw": [
+ "ffffff00",
+ 287,
+ 4,
+ 0,
+ 32
+ ]
+ },
+ "dhcp.option.type_raw": [
+ "3a0400000708",
+ 291,
+ 6,
+ 0,
+ 4
+ ],
+ "dhcp.option.type_tree": {
+ "dhcp.option.length_raw": [
+ "04",
+ 292,
+ 1,
+ 0,
+ 4
+ ],
+ "dhcp.option.value_raw": [
+ "00000708",
+ 293,
+ 4,
+ 0,
+ 30
+ ],
+ "dhcp.option.renewal_time_value_raw": [
+ "00000708",
+ 293,
+ 4,
+ 0,
+ 7
+ ]
+ },
+ "dhcp.option.type_raw": [
+ "3b0400000c4e",
+ 297,
+ 6,
+ 0,
+ 4
+ ],
+ "dhcp.option.type_tree": {
+ "dhcp.option.length_raw": [
+ "04",
+ 298,
+ 1,
+ 0,
+ 4
+ ],
+ "dhcp.option.value_raw": [
+ "00000c4e",
+ 299,
+ 4,
+ 0,
+ 30
+ ],
+ "dhcp.option.rebinding_time_value_raw": [
+ "00000c4e",
+ 299,
+ 4,
+ 0,
+ 7
+ ]
+ },
+ "dhcp.option.type_raw": [
+ "330400000e10",
+ 303,
+ 6,
+ 0,
+ 4
+ ],
+ "dhcp.option.type_tree": {
+ "dhcp.option.length_raw": [
+ "04",
+ 304,
+ 1,
+ 0,
+ 4
+ ],
+ "dhcp.option.value_raw": [
+ "00000e10",
+ 305,
+ 4,
+ 0,
+ 30
+ ],
+ "dhcp.option.ip_address_lease_time_raw": [
+ "00000e10",
+ 305,
+ 4,
+ 0,
+ 7
+ ]
+ },
+ "dhcp.option.type_raw": [
+ "3604c0a80001",
+ 309,
+ 6,
+ 0,
+ 4
+ ],
+ "dhcp.option.type_tree": {
+ "dhcp.option.length_raw": [
+ "04",
+ 310,
+ 1,
+ 0,
+ 4
+ ],
+ "dhcp.option.value_raw": [
+ "c0a80001",
+ 311,
+ 4,
+ 0,
+ 30
+ ],
+ "dhcp.option.dhcp_server_id_raw": [
+ "c0a80001",
+ 311,
+ 4,
+ 0,
+ 32
+ ]
+ },
+ "dhcp.option.type_raw": [
+ "ff",
+ 315,
+ 1,
+ 0,
+ 4
+ ],
+ "dhcp.option.type_tree": {
+ "dhcp.option.end_raw": [
+ "ff",
+ 315,
+ 1,
+ 0,
+ 4
+ ]
+ },
+ "dhcp.option.padding_raw": [
+ "0000000000000000000000000000000000000000000000000000",
+ 316,
+ 26,
+ 0,
+ 30
+ ]
+ }
+ }
+ }
+ },
+ {
+ "_index": "packets-2004-12-05",
+ "_type": "doc",
+ "_score": null,
+ "_source": {
+ "layers": {
+ "frame_raw": [
+ "ffffffffffff000b8201fc4208004500012ca8370000fa11178a00000000ffffffff0044004301189fbd0101060000003d1e0000000000000000000000000000000000000000000b8201fc4200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000638253633501033d0701000b8201fc423204c0a8000a3604c0a8000137040103062aff00",
+ 0,
+ 314,
+ 0,
+ 1
+ ],
+ "frame": {
+ "frame.encap_type_raw": [
+ "",
+ 0,
+ 0,
+ 0,
+ 13
+ ],
+ "frame.time_raw": [
+ "",
+ 0,
+ 0,
+ 0,
+ 24
+ ],
+ "frame.time_utc_raw": [
+ "",
+ 0,
+ 0,
+ 0,
+ 24
+ ],
+ "frame.time_epoch_raw": [
+ "",
+ 0,
+ 0,
+ 0,
+ 24
+ ],
+ "frame.offset_shift_raw": [
+ "",
+ 0,
+ 0,
+ 0,
+ 25
+ ],
+ "frame.time_delta_raw": [
+ "",
+ 0,
+ 0,
+ 0,
+ 25
+ ],
+ "frame.time_delta_displayed_raw": [
+ "",
+ 0,
+ 0,
+ 0,
+ 25
+ ],
+ "frame.time_relative_raw": [
+ "",
+ 0,
+ 0,
+ 0,
+ 25
+ ],
+ "frame.number_raw": [
+ "",
+ 0,
+ 0,
+ 0,
+ 7
+ ],
+ "frame.len_raw": [
+ "",
+ 0,
+ 0,
+ 0,
+ 7
+ ],
+ "frame.cap_len_raw": [
+ "",
+ 0,
+ 0,
+ 0,
+ 7
+ ],
+ "frame.marked_raw": [
+ "",
+ 0,
+ 0,
+ 0,
+ 2
+ ],
+ "frame.ignored_raw": [
+ "",
+ 0,
+ 0,
+ 0,
+ 2
+ ],
+ "frame.protocols_raw": [
+ "",
+ 0,
+ 0,
+ 0,
+ 26
+ ]
+ },
+ "eth_raw": [
+ "ffffffffffff000b8201fc420800",
+ 0,
+ 14,
+ 0,
+ 1
+ ],
+ "eth": {
+ "eth.dst_raw": [
+ "ffffffffffff",
+ 0,
+ 6,
+ 0,
+ 29
+ ],
+ "eth.dst_tree": {
+ "eth.dst_resolved_raw": [
+ "ffffffffffff",
+ 0,
+ 6,
+ 0,
+ 26
+ ],
+ "eth.dst.oui_raw": [
+ "ffffff",
+ 0,
+ 3,
+ 0,
+ 6
+ ],
+ "eth.addr_raw": [
+ "ffffffffffff",
+ 0,
+ 6,
+ 0,
+ 29
+ ],
+ "eth.addr_resolved_raw": [
+ "ffffffffffff",
+ 0,
+ 6,
+ 0,
+ 26
+ ],
+ "eth.addr.oui_raw": [
+ "ffffff",
+ 0,
+ 3,
+ 0,
+ 6
+ ],
+ "eth.dst.lg_raw": [
+ "1",
+ 0,
+ 3,
+ 131072,
+ 2
+ ],
+ "eth.lg_raw": [
+ "1",
+ 0,
+ 3,
+ 131072,
+ 2
+ ],
+ "eth.dst.ig_raw": [
+ "1",
+ 0,
+ 3,
+ 65536,
+ 2
+ ],
+ "eth.ig_raw": [
+ "1",
+ 0,
+ 3,
+ 65536,
+ 2
+ ]
+ },
+ "eth.src_raw": [
+ "000b8201fc42",
+ 6,
+ 6,
+ 0,
+ 29
+ ],
+ "eth.src_tree": {
+ "eth.src_resolved_raw": [
+ "000b8201fc42",
+ 6,
+ 6,
+ 0,
+ 26
+ ],
+ "eth.src.oui_raw": [
+ "000b82",
+ 6,
+ 3,
+ 0,
+ 6
+ ],
+ "eth.src.oui_resolved_raw": [
+ "000b8201fc42",
+ 6,
+ 6,
+ 0,
+ 26
+ ],
+ "eth.addr_raw": [
+ "000b8201fc42",
+ 6,
+ 6,
+ 0,
+ 29
+ ],
+ "eth.addr_resolved_raw": [
+ "000b8201fc42",
+ 6,
+ 6,
+ 0,
+ 26
+ ],
+ "eth.addr.oui_raw": [
+ "000b82",
+ 6,
+ 3,
+ 0,
+ 6
+ ],
+ "eth.addr.oui_resolved_raw": [
+ "000b8201fc42",
+ 6,
+ 6,
+ 0,
+ 26
+ ],
+ "eth.src.lg_raw": [
+ "0",
+ 6,
+ 3,
+ 131072,
+ 2
+ ],
+ "eth.lg_raw": [
+ "0",
+ 6,
+ 3,
+ 131072,
+ 2
+ ],
+ "eth.src.ig_raw": [
+ "0",
+ 6,
+ 3,
+ 65536,
+ 2
+ ],
+ "eth.ig_raw": [
+ "0",
+ 6,
+ 3,
+ 65536,
+ 2
+ ]
+ },
+ "eth.type_raw": [
+ "0800",
+ 12,
+ 2,
+ 0,
+ 5
+ ]
+ },
+ "ip_raw": [
+ "4500012ca8370000fa11178a00000000ffffffff",
+ 14,
+ 20,
+ 0,
+ 1
+ ],
+ "ip": {
+ "ip.version_raw": [
+ "45",
+ 14,
+ 1,
+ 0,
+ 4
+ ],
+ "ip.hdr_len_raw": [
+ "45",
+ 14,
+ 1,
+ 0,
+ 4
+ ],
+ "ip.dsfield_raw": [
+ "00",
+ 15,
+ 1,
+ 0,
+ 4
+ ],
+ "ip.dsfield_tree": {
+ "ip.dsfield.dscp_raw": [
+ "0",
+ 15,
+ 1,
+ 252,
+ 4
+ ],
+ "ip.dsfield.ecn_raw": [
+ "0",
+ 15,
+ 1,
+ 3,
+ 4
+ ]
+ },
+ "ip.len_raw": [
+ "012c",
+ 16,
+ 2,
+ 0,
+ 5
+ ],
+ "ip.id_raw": [
+ "a837",
+ 18,
+ 2,
+ 0,
+ 5
+ ],
+ "ip.flags_raw": [
+ "0",
+ 20,
+ 1,
+ 224,
+ 4
+ ],
+ "ip.flags_tree": {
+ "ip.flags.rb_raw": [
+ "0",
+ 20,
+ 1,
+ 128,
+ 2
+ ],
+ "ip.flags.df_raw": [
+ "0",
+ 20,
+ 1,
+ 64,
+ 2
+ ],
+ "ip.flags.mf_raw": [
+ "0",
+ 20,
+ 1,
+ 32,
+ 2
+ ]
+ },
+ "ip.frag_offset_raw": [
+ "0",
+ 20,
+ 2,
+ 8191,
+ 5
+ ],
+ "ip.ttl_raw": [
+ "fa",
+ 22,
+ 1,
+ 0,
+ 4
+ ],
+ "ip.proto_raw": [
+ "11",
+ 23,
+ 1,
+ 0,
+ 4
+ ],
+ "ip.checksum_raw": [
+ "178a",
+ 24,
+ 2,
+ 0,
+ 5
+ ],
+ "ip.checksum.status_raw": [
+ "",
+ 24,
+ 0,
+ 0,
+ 4
+ ],
+ "ip.src_raw": [
+ "00000000",
+ 26,
+ 4,
+ 0,
+ 32
+ ],
+ "ip.addr_raw": [
+ "00000000",
+ 26,
+ 4,
+ 0,
+ 32
+ ],
+ "ip.src_host_raw": [
+ "00000000",
+ 26,
+ 4,
+ 0,
+ 26
+ ],
+ "ip.host_raw": [
+ "00000000",
+ 26,
+ 4,
+ 0,
+ 26
+ ],
+ "ip.dst_raw": [
+ "ffffffff",
+ 30,
+ 4,
+ 0,
+ 32
+ ],
+ "ip.addr_raw": [
+ "ffffffff",
+ 30,
+ 4,
+ 0,
+ 32
+ ],
+ "ip.dst_host_raw": [
+ "ffffffff",
+ 30,
+ 4,
+ 0,
+ 26
+ ],
+ "ip.host_raw": [
+ "ffffffff",
+ 30,
+ 4,
+ 0,
+ 26
+ ]
+ },
+ "udp_raw": [
+ "0044004301189fbd",
+ 34,
+ 8,
+ 0,
+ 1
+ ],
+ "udp": {
+ "udp.srcport_raw": [
+ "0044",
+ 34,
+ 2,
+ 0,
+ 5
+ ],
+ "udp.dstport_raw": [
+ "0043",
+ 36,
+ 2,
+ 0,
+ 5
+ ],
+ "udp.port_raw": [
+ "0044",
+ 34,
+ 2,
+ 0,
+ 5
+ ],
+ "udp.port_raw": [
+ "0043",
+ 36,
+ 2,
+ 0,
+ 5
+ ],
+ "udp.length_raw": [
+ "0118",
+ 38,
+ 2,
+ 0,
+ 5
+ ],
+ "udp.checksum_raw": [
+ "9fbd",
+ 40,
+ 2,
+ 0,
+ 5
+ ],
+ "udp.checksum.status_raw": [
+ "",
+ 40,
+ 0,
+ 0,
+ 4
+ ],
+ "udp.stream_raw": [
+ "",
+ 42,
+ 0,
+ 0,
+ 7
+ ],
+ "Timestamps": {
+ "udp.time_relative_raw": [
+ "",
+ 34,
+ 0,
+ 0,
+ 25
+ ],
+ "udp.time_delta_raw": [
+ "",
+ 34,
+ 0,
+ 0,
+ 25
+ ]
+ },
+ "udp.payload_raw": [
+ "0101060000003d1e0000000000000000000000000000000000000000000b8201fc4200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000638253633501033d0701000b8201fc423204c0a8000a3604c0a8000137040103062aff00",
+ 42,
+ 272,
+ 0,
+ 30
+ ]
+ },
+ "dhcp_raw": [
+ "0101060000003d1e0000000000000000000000000000000000000000000b8201fc4200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000638253633501033d0701000b8201fc423204c0a8000a3604c0a8000137040103062aff00",
+ 42,
+ 272,
+ 0,
+ 1
+ ],
+ "dhcp": {
+ "dhcp.type_raw": [
+ "01",
+ 42,
+ 1,
+ 0,
+ 4
+ ],
+ "dhcp.hw.type_raw": [
+ "01",
+ 43,
+ 1,
+ 0,
+ 4
+ ],
+ "dhcp.hw.len_raw": [
+ "06",
+ 44,
+ 1,
+ 0,
+ 4
+ ],
+ "dhcp.hops_raw": [
+ "00",
+ 45,
+ 1,
+ 0,
+ 4
+ ],
+ "dhcp.id_raw": [
+ "00003d1e",
+ 46,
+ 4,
+ 0,
+ 7
+ ],
+ "dhcp.secs_raw": [
+ "0000",
+ 50,
+ 2,
+ 0,
+ 5
+ ],
+ "dhcp.flags_raw": [
+ "0000",
+ 52,
+ 2,
+ 0,
+ 5
+ ],
+ "dhcp.flags_tree": {
+ "dhcp.flags.bc_raw": [
+ "0",
+ 52,
+ 2,
+ 32768,
+ 2
+ ],
+ "dhcp.flags.reserved_raw": [
+ "0",
+ 52,
+ 2,
+ 32767,
+ 5
+ ]
+ },
+ "dhcp.ip.client_raw": [
+ "00000000",
+ 54,
+ 4,
+ 0,
+ 32
+ ],
+ "dhcp.ip.your_raw": [
+ "00000000",
+ 58,
+ 4,
+ 0,
+ 32
+ ],
+ "dhcp.ip.server_raw": [
+ "00000000",
+ 62,
+ 4,
+ 0,
+ 32
+ ],
+ "dhcp.ip.relay_raw": [
+ "00000000",
+ 66,
+ 4,
+ 0,
+ 32
+ ],
+ "dhcp.hw.mac_addr_raw": [
+ "000b8201fc42",
+ 70,
+ 6,
+ 0,
+ 29
+ ],
+ "dhcp.hw.addr_padding_raw": [
+ "00000000000000000000",
+ 76,
+ 10,
+ 0,
+ 30
+ ],
+ "dhcp.server_raw": [
+ "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ 86,
+ 64,
+ 0,
+ 26
+ ],
+ "dhcp.file_raw": [
+ "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ 150,
+ 128,
+ 0,
+ 26
+ ],
+ "dhcp.cookie_raw": [
+ "63825363",
+ 278,
+ 4,
+ 0,
+ 32
+ ],
+ "dhcp.option.type_raw": [
+ "350103",
+ 282,
+ 3,
+ 0,
+ 4
+ ],
+ "dhcp.option.type_tree": {
+ "dhcp.option.length_raw": [
+ "01",
+ 283,
+ 1,
+ 0,
+ 4
+ ],
+ "dhcp.option.value_raw": [
+ "03",
+ 284,
+ 1,
+ 0,
+ 30
+ ],
+ "dhcp.option.dhcp_raw": [
+ "03",
+ 284,
+ 1,
+ 0,
+ 4
+ ]
+ },
+ "dhcp.option.type_raw": [
+ "3d0701000b8201fc42",
+ 285,
+ 9,
+ 0,
+ 4
+ ],
+ "dhcp.option.type_tree": {
+ "dhcp.option.length_raw": [
+ "07",
+ 286,
+ 1,
+ 0,
+ 4
+ ],
+ "dhcp.option.value_raw": [
+ "01000b8201fc42",
+ 287,
+ 7,
+ 0,
+ 30
+ ],
+ "dhcp.hw.type_raw": [
+ "01",
+ 287,
+ 1,
+ 0,
+ 4
+ ],
+ "dhcp.hw.mac_addr_raw": [
+ "000b8201fc42",
+ 288,
+ 6,
+ 0,
+ 29
+ ]
+ },
+ "dhcp.option.type_raw": [
+ "3204c0a8000a",
+ 294,
+ 6,
+ 0,
+ 4
+ ],
+ "dhcp.option.type_tree": {
+ "dhcp.option.length_raw": [
+ "04",
+ 295,
+ 1,
+ 0,
+ 4
+ ],
+ "dhcp.option.value_raw": [
+ "c0a8000a",
+ 296,
+ 4,
+ 0,
+ 30
+ ],
+ "dhcp.option.requested_ip_address_raw": [
+ "c0a8000a",
+ 296,
+ 4,
+ 0,
+ 32
+ ]
+ },
+ "dhcp.option.type_raw": [
+ "3604c0a80001",
+ 300,
+ 6,
+ 0,
+ 4
+ ],
+ "dhcp.option.type_tree": {
+ "dhcp.option.length_raw": [
+ "04",
+ 301,
+ 1,
+ 0,
+ 4
+ ],
+ "dhcp.option.value_raw": [
+ "c0a80001",
+ 302,
+ 4,
+ 0,
+ 30
+ ],
+ "dhcp.option.dhcp_server_id_raw": [
+ "c0a80001",
+ 302,
+ 4,
+ 0,
+ 32
+ ]
+ },
+ "dhcp.option.type_raw": [
+ "37040103062a",
+ 306,
+ 6,
+ 0,
+ 4
+ ],
+ "dhcp.option.type_tree": {
+ "dhcp.option.length_raw": [
+ "04",
+ 307,
+ 1,
+ 0,
+ 4
+ ],
+ "dhcp.option.value_raw": [
+ "0103062a",
+ 308,
+ 4,
+ 0,
+ 30
+ ],
+ "dhcp.option.request_list_item_raw": [
+ "01",
+ 308,
+ 1,
+ 0,
+ 4
+ ],
+ "dhcp.option.request_list_item_raw": [
+ "03",
+ 309,
+ 1,
+ 0,
+ 4
+ ],
+ "dhcp.option.request_list_item_raw": [
+ "06",
+ 310,
+ 1,
+ 0,
+ 4
+ ],
+ "dhcp.option.request_list_item_raw": [
+ "2a",
+ 311,
+ 1,
+ 0,
+ 4
+ ]
+ },
+ "dhcp.option.type_raw": [
+ "ff",
+ 312,
+ 1,
+ 0,
+ 4
+ ],
+ "dhcp.option.type_tree": {
+ "dhcp.option.end_raw": [
+ "ff",
+ 312,
+ 1,
+ 0,
+ 4
+ ]
+ },
+ "dhcp.option.padding_raw": [
+ "00",
+ 313,
+ 1,
+ 0,
+ 30
+ ]
+ }
+ }
+ }
+ },
+ {
+ "_index": "packets-2004-12-05",
+ "_type": "doc",
+ "_score": null,
+ "_source": {
+ "layers": {
+ "frame_raw": [
+ "000b8201fc42000874adf19b0800450001480446000080110000c0a80001c0a8000a004300440134dfdb0201060000003d1e0000000000000000c0a8000a0000000000000000000b8201fc4200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000638253633501053a04000007083b0400000c4e330400000e103604c0a800010104ffffff00ff0000000000000000000000000000000000000000000000000000",
+ 0,
+ 342,
+ 0,
+ 1
+ ],
+ "frame": {
+ "frame.encap_type_raw": [
+ "",
+ 0,
+ 0,
+ 0,
+ 13
+ ],
+ "frame.time_raw": [
+ "",
+ 0,
+ 0,
+ 0,
+ 24
+ ],
+ "frame.time_utc_raw": [
+ "",
+ 0,
+ 0,
+ 0,
+ 24
+ ],
+ "frame.time_epoch_raw": [
+ "",
+ 0,
+ 0,
+ 0,
+ 24
+ ],
+ "frame.offset_shift_raw": [
+ "",
+ 0,
+ 0,
+ 0,
+ 25
+ ],
+ "frame.time_delta_raw": [
+ "",
+ 0,
+ 0,
+ 0,
+ 25
+ ],
+ "frame.time_delta_displayed_raw": [
+ "",
+ 0,
+ 0,
+ 0,
+ 25
+ ],
+ "frame.time_relative_raw": [
+ "",
+ 0,
+ 0,
+ 0,
+ 25
+ ],
+ "frame.number_raw": [
+ "",
+ 0,
+ 0,
+ 0,
+ 7
+ ],
+ "frame.len_raw": [
+ "",
+ 0,
+ 0,
+ 0,
+ 7
+ ],
+ "frame.cap_len_raw": [
+ "",
+ 0,
+ 0,
+ 0,
+ 7
+ ],
+ "frame.marked_raw": [
+ "",
+ 0,
+ 0,
+ 0,
+ 2
+ ],
+ "frame.ignored_raw": [
+ "",
+ 0,
+ 0,
+ 0,
+ 2
+ ],
+ "frame.protocols_raw": [
+ "",
+ 0,
+ 0,
+ 0,
+ 26
+ ]
+ },
+ "eth_raw": [
+ "000b8201fc42000874adf19b0800",
+ 0,
+ 14,
+ 0,
+ 1
+ ],
+ "eth": {
+ "eth.dst_raw": [
+ "000b8201fc42",
+ 0,
+ 6,
+ 0,
+ 29
+ ],
+ "eth.dst_tree": {
+ "eth.dst_resolved_raw": [
+ "000b8201fc42",
+ 0,
+ 6,
+ 0,
+ 26
+ ],
+ "eth.dst.oui_raw": [
+ "000b82",
+ 0,
+ 3,
+ 0,
+ 6
+ ],
+ "eth.dst.oui_resolved_raw": [
+ "000b8201fc42",
+ 0,
+ 6,
+ 0,
+ 26
+ ],
+ "eth.addr_raw": [
+ "000b8201fc42",
+ 0,
+ 6,
+ 0,
+ 29
+ ],
+ "eth.addr_resolved_raw": [
+ "000b8201fc42",
+ 0,
+ 6,
+ 0,
+ 26
+ ],
+ "eth.addr.oui_raw": [
+ "000b82",
+ 0,
+ 3,
+ 0,
+ 6
+ ],
+ "eth.addr.oui_resolved_raw": [
+ "000b8201fc42",
+ 0,
+ 6,
+ 0,
+ 26
+ ],
+ "eth.dst.lg_raw": [
+ "0",
+ 0,
+ 3,
+ 131072,
+ 2
+ ],
+ "eth.lg_raw": [
+ "0",
+ 0,
+ 3,
+ 131072,
+ 2
+ ],
+ "eth.dst.ig_raw": [
+ "0",
+ 0,
+ 3,
+ 65536,
+ 2
+ ],
+ "eth.ig_raw": [
+ "0",
+ 0,
+ 3,
+ 65536,
+ 2
+ ]
+ },
+ "eth.src_raw": [
+ "000874adf19b",
+ 6,
+ 6,
+ 0,
+ 29
+ ],
+ "eth.src_tree": {
+ "eth.src_resolved_raw": [
+ "000874adf19b",
+ 6,
+ 6,
+ 0,
+ 26
+ ],
+ "eth.src.oui_raw": [
+ "000874",
+ 6,
+ 3,
+ 0,
+ 6
+ ],
+ "eth.src.oui_resolved_raw": [
+ "000874adf19b",
+ 6,
+ 6,
+ 0,
+ 26
+ ],
+ "eth.addr_raw": [
+ "000874adf19b",
+ 6,
+ 6,
+ 0,
+ 29
+ ],
+ "eth.addr_resolved_raw": [
+ "000874adf19b",
+ 6,
+ 6,
+ 0,
+ 26
+ ],
+ "eth.addr.oui_raw": [
+ "000874",
+ 6,
+ 3,
+ 0,
+ 6
+ ],
+ "eth.addr.oui_resolved_raw": [
+ "000874adf19b",
+ 6,
+ 6,
+ 0,
+ 26
+ ],
+ "eth.src.lg_raw": [
+ "0",
+ 6,
+ 3,
+ 131072,
+ 2
+ ],
+ "eth.lg_raw": [
+ "0",
+ 6,
+ 3,
+ 131072,
+ 2
+ ],
+ "eth.src.ig_raw": [
+ "0",
+ 6,
+ 3,
+ 65536,
+ 2
+ ],
+ "eth.ig_raw": [
+ "0",
+ 6,
+ 3,
+ 65536,
+ 2
+ ]
+ },
+ "eth.type_raw": [
+ "0800",
+ 12,
+ 2,
+ 0,
+ 5
+ ]
+ },
+ "ip_raw": [
+ "450001480446000080110000c0a80001c0a8000a",
+ 14,
+ 20,
+ 0,
+ 1
+ ],
+ "ip": {
+ "ip.version_raw": [
+ "45",
+ 14,
+ 1,
+ 0,
+ 4
+ ],
+ "ip.hdr_len_raw": [
+ "45",
+ 14,
+ 1,
+ 0,
+ 4
+ ],
+ "ip.dsfield_raw": [
+ "00",
+ 15,
+ 1,
+ 0,
+ 4
+ ],
+ "ip.dsfield_tree": {
+ "ip.dsfield.dscp_raw": [
+ "0",
+ 15,
+ 1,
+ 252,
+ 4
+ ],
+ "ip.dsfield.ecn_raw": [
+ "0",
+ 15,
+ 1,
+ 3,
+ 4
+ ]
+ },
+ "ip.len_raw": [
+ "0148",
+ 16,
+ 2,
+ 0,
+ 5
+ ],
+ "ip.id_raw": [
+ "0446",
+ 18,
+ 2,
+ 0,
+ 5
+ ],
+ "ip.flags_raw": [
+ "0",
+ 20,
+ 1,
+ 224,
+ 4
+ ],
+ "ip.flags_tree": {
+ "ip.flags.rb_raw": [
+ "0",
+ 20,
+ 1,
+ 128,
+ 2
+ ],
+ "ip.flags.df_raw": [
+ "0",
+ 20,
+ 1,
+ 64,
+ 2
+ ],
+ "ip.flags.mf_raw": [
+ "0",
+ 20,
+ 1,
+ 32,
+ 2
+ ]
+ },
+ "ip.frag_offset_raw": [
+ "0",
+ 20,
+ 2,
+ 8191,
+ 5
+ ],
+ "ip.ttl_raw": [
+ "80",
+ 22,
+ 1,
+ 0,
+ 4
+ ],
+ "ip.proto_raw": [
+ "11",
+ 23,
+ 1,
+ 0,
+ 4
+ ],
+ "ip.checksum_raw": [
+ "0000",
+ 24,
+ 2,
+ 0,
+ 5
+ ],
+ "ip.checksum.status_raw": [
+ "",
+ 24,
+ 0,
+ 0,
+ 4
+ ],
+ "ip.src_raw": [
+ "c0a80001",
+ 26,
+ 4,
+ 0,
+ 32
+ ],
+ "ip.addr_raw": [
+ "c0a80001",
+ 26,
+ 4,
+ 0,
+ 32
+ ],
+ "ip.src_host_raw": [
+ "c0a80001",
+ 26,
+ 4,
+ 0,
+ 26
+ ],
+ "ip.host_raw": [
+ "c0a80001",
+ 26,
+ 4,
+ 0,
+ 26
+ ],
+ "ip.dst_raw": [
+ "c0a8000a",
+ 30,
+ 4,
+ 0,
+ 32
+ ],
+ "ip.addr_raw": [
+ "c0a8000a",
+ 30,
+ 4,
+ 0,
+ 32
+ ],
+ "ip.dst_host_raw": [
+ "c0a8000a",
+ 30,
+ 4,
+ 0,
+ 26
+ ],
+ "ip.host_raw": [
+ "c0a8000a",
+ 30,
+ 4,
+ 0,
+ 26
+ ]
+ },
+ "udp_raw": [
+ "004300440134dfdb",
+ 34,
+ 8,
+ 0,
+ 1
+ ],
+ "udp": {
+ "udp.srcport_raw": [
+ "0043",
+ 34,
+ 2,
+ 0,
+ 5
+ ],
+ "udp.dstport_raw": [
+ "0044",
+ 36,
+ 2,
+ 0,
+ 5
+ ],
+ "udp.port_raw": [
+ "0043",
+ 34,
+ 2,
+ 0,
+ 5
+ ],
+ "udp.port_raw": [
+ "0044",
+ 36,
+ 2,
+ 0,
+ 5
+ ],
+ "udp.length_raw": [
+ "0134",
+ 38,
+ 2,
+ 0,
+ 5
+ ],
+ "udp.checksum_raw": [
+ "dfdb",
+ 40,
+ 2,
+ 0,
+ 5
+ ],
+ "udp.checksum.status_raw": [
+ "",
+ 40,
+ 0,
+ 0,
+ 4
+ ],
+ "udp.stream_raw": [
+ "",
+ 42,
+ 0,
+ 0,
+ 7
+ ],
+ "Timestamps": {
+ "udp.time_relative_raw": [
+ "",
+ 34,
+ 0,
+ 0,
+ 25
+ ],
+ "udp.time_delta_raw": [
+ "",
+ 34,
+ 0,
+ 0,
+ 25
+ ]
+ },
+ "udp.payload_raw": [
+ "0201060000003d1e0000000000000000c0a8000a0000000000000000000b8201fc4200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000638253633501053a04000007083b0400000c4e330400000e103604c0a800010104ffffff00ff0000000000000000000000000000000000000000000000000000",
+ 42,
+ 300,
+ 0,
+ 30
+ ]
+ },
+ "dhcp_raw": [
+ "0201060000003d1e0000000000000000c0a8000a0000000000000000000b8201fc4200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000638253633501053a04000007083b0400000c4e330400000e103604c0a800010104ffffff00ff0000000000000000000000000000000000000000000000000000",
+ 42,
+ 300,
+ 0,
+ 1
+ ],
+ "dhcp": {
+ "dhcp.type_raw": [
+ "02",
+ 42,
+ 1,
+ 0,
+ 4
+ ],
+ "dhcp.hw.type_raw": [
+ "01",
+ 43,
+ 1,
+ 0,
+ 4
+ ],
+ "dhcp.hw.len_raw": [
+ "06",
+ 44,
+ 1,
+ 0,
+ 4
+ ],
+ "dhcp.hops_raw": [
+ "00",
+ 45,
+ 1,
+ 0,
+ 4
+ ],
+ "dhcp.id_raw": [
+ "00003d1e",
+ 46,
+ 4,
+ 0,
+ 7
+ ],
+ "dhcp.secs_raw": [
+ "0000",
+ 50,
+ 2,
+ 0,
+ 5
+ ],
+ "dhcp.flags_raw": [
+ "0000",
+ 52,
+ 2,
+ 0,
+ 5
+ ],
+ "dhcp.flags_tree": {
+ "dhcp.flags.bc_raw": [
+ "0",
+ 52,
+ 2,
+ 32768,
+ 2
+ ],
+ "dhcp.flags.reserved_raw": [
+ "0",
+ 52,
+ 2,
+ 32767,
+ 5
+ ]
+ },
+ "dhcp.ip.client_raw": [
+ "00000000",
+ 54,
+ 4,
+ 0,
+ 32
+ ],
+ "dhcp.ip.your_raw": [
+ "c0a8000a",
+ 58,
+ 4,
+ 0,
+ 32
+ ],
+ "dhcp.ip.server_raw": [
+ "00000000",
+ 62,
+ 4,
+ 0,
+ 32
+ ],
+ "dhcp.ip.relay_raw": [
+ "00000000",
+ 66,
+ 4,
+ 0,
+ 32
+ ],
+ "dhcp.hw.mac_addr_raw": [
+ "000b8201fc42",
+ 70,
+ 6,
+ 0,
+ 29
+ ],
+ "dhcp.hw.addr_padding_raw": [
+ "00000000000000000000",
+ 76,
+ 10,
+ 0,
+ 30
+ ],
+ "dhcp.server_raw": [
+ "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ 86,
+ 64,
+ 0,
+ 26
+ ],
+ "dhcp.file_raw": [
+ "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ 150,
+ 128,
+ 0,
+ 26
+ ],
+ "dhcp.cookie_raw": [
+ "63825363",
+ 278,
+ 4,
+ 0,
+ 32
+ ],
+ "dhcp.option.type_raw": [
+ "350105",
+ 282,
+ 3,
+ 0,
+ 4
+ ],
+ "dhcp.option.type_tree": {
+ "dhcp.option.length_raw": [
+ "01",
+ 283,
+ 1,
+ 0,
+ 4
+ ],
+ "dhcp.option.value_raw": [
+ "05",
+ 284,
+ 1,
+ 0,
+ 30
+ ],
+ "dhcp.option.dhcp_raw": [
+ "05",
+ 284,
+ 1,
+ 0,
+ 4
+ ]
+ },
+ "dhcp.option.type_raw": [
+ "3a0400000708",
+ 285,
+ 6,
+ 0,
+ 4
+ ],
+ "dhcp.option.type_tree": {
+ "dhcp.option.length_raw": [
+ "04",
+ 286,
+ 1,
+ 0,
+ 4
+ ],
+ "dhcp.option.value_raw": [
+ "00000708",
+ 287,
+ 4,
+ 0,
+ 30
+ ],
+ "dhcp.option.renewal_time_value_raw": [
+ "00000708",
+ 287,
+ 4,
+ 0,
+ 7
+ ]
+ },
+ "dhcp.option.type_raw": [
+ "3b0400000c4e",
+ 291,
+ 6,
+ 0,
+ 4
+ ],
+ "dhcp.option.type_tree": {
+ "dhcp.option.length_raw": [
+ "04",
+ 292,
+ 1,
+ 0,
+ 4
+ ],
+ "dhcp.option.value_raw": [
+ "00000c4e",
+ 293,
+ 4,
+ 0,
+ 30
+ ],
+ "dhcp.option.rebinding_time_value_raw": [
+ "00000c4e",
+ 293,
+ 4,
+ 0,
+ 7
+ ]
+ },
+ "dhcp.option.type_raw": [
+ "330400000e10",
+ 297,
+ 6,
+ 0,
+ 4
+ ],
+ "dhcp.option.type_tree": {
+ "dhcp.option.length_raw": [
+ "04",
+ 298,
+ 1,
+ 0,
+ 4
+ ],
+ "dhcp.option.value_raw": [
+ "00000e10",
+ 299,
+ 4,
+ 0,
+ 30
+ ],
+ "dhcp.option.ip_address_lease_time_raw": [
+ "00000e10",
+ 299,
+ 4,
+ 0,
+ 7
+ ]
+ },
+ "dhcp.option.type_raw": [
+ "3604c0a80001",
+ 303,
+ 6,
+ 0,
+ 4
+ ],
+ "dhcp.option.type_tree": {
+ "dhcp.option.length_raw": [
+ "04",
+ 304,
+ 1,
+ 0,
+ 4
+ ],
+ "dhcp.option.value_raw": [
+ "c0a80001",
+ 305,
+ 4,
+ 0,
+ 30
+ ],
+ "dhcp.option.dhcp_server_id_raw": [
+ "c0a80001",
+ 305,
+ 4,
+ 0,
+ 32
+ ]
+ },
+ "dhcp.option.type_raw": [
+ "0104ffffff00",
+ 309,
+ 6,
+ 0,
+ 4
+ ],
+ "dhcp.option.type_tree": {
+ "dhcp.option.length_raw": [
+ "04",
+ 310,
+ 1,
+ 0,
+ 4
+ ],
+ "dhcp.option.value_raw": [
+ "ffffff00",
+ 311,
+ 4,
+ 0,
+ 30
+ ],
+ "dhcp.option.subnet_mask_raw": [
+ "ffffff00",
+ 311,
+ 4,
+ 0,
+ 32
+ ]
+ },
+ "dhcp.option.type_raw": [
+ "ff",
+ 315,
+ 1,
+ 0,
+ 4
+ ],
+ "dhcp.option.type_tree": {
+ "dhcp.option.end_raw": [
+ "ff",
+ 315,
+ 1,
+ 0,
+ 4
+ ]
+ },
+ "dhcp.option.padding_raw": [
+ "0000000000000000000000000000000000000000000000000000",
+ 316,
+ 26,
+ 0,
+ 30
+ ]
+ }
+ }
+ }
+ }
+]
diff --git a/test/baseline/elastic-mapping-ip-subset.json b/test/baseline/elastic-mapping-ip-subset.json
new file mode 100644
index 0000000..d1a85d2
--- /dev/null
+++ b/test/baseline/elastic-mapping-ip-subset.json
@@ -0,0 +1,306 @@
+{
+ "settings": {
+ "index.mapping.total_fields.limit": 1000000
+ },
+ "mappings": {
+ "dynamic": false,
+ "properties": {
+ "timestamp": {
+ "type": "date"
+ },
+ "layers": {
+ "properties": {
+ "ip": {
+ "properties": {
+ "ip_ip_version": {
+ "type": "short"
+ },
+ "ip_ip_hdr_len": {
+ "type": "short"
+ },
+ "ip_ip_dsfield": {
+ "type": "short"
+ },
+ "ip_ip_dsfield_dscp": {
+ "type": "short"
+ },
+ "ip_ip_dsfield_ecn": {
+ "type": "short"
+ },
+ "ip_ip_tos": {
+ "type": "short"
+ },
+ "ip_ip_tos_precedence": {
+ "type": "short"
+ },
+ "ip_ip_tos_delay": {
+ "type": "boolean"
+ },
+ "ip_ip_tos_throughput": {
+ "type": "boolean"
+ },
+ "ip_ip_tos_reliability": {
+ "type": "boolean"
+ },
+ "ip_ip_tos_cost": {
+ "type": "boolean"
+ },
+ "ip_ip_len": {
+ "type": "integer"
+ },
+ "ip_ip_id": {
+ "type": "integer"
+ },
+ "ip_ip_dst": {
+ "type": "ip"
+ },
+ "ip_ip_src": {
+ "type": "ip"
+ },
+ "ip_ip_addr": {
+ "type": "ip"
+ },
+ "ip_ip_geoip_asnum": {
+ "type": "long"
+ },
+ "ip_ip_geoip_lat": {
+ "type": "float"
+ },
+ "ip_ip_geoip_lon": {
+ "type": "float"
+ },
+ "ip_ip_geoip_src_asnum": {
+ "type": "long"
+ },
+ "ip_ip_geoip_src_lat": {
+ "type": "float"
+ },
+ "ip_ip_geoip_src_lon": {
+ "type": "float"
+ },
+ "ip_ip_geoip_dst_asnum": {
+ "type": "long"
+ },
+ "ip_ip_geoip_dst_lat": {
+ "type": "float"
+ },
+ "ip_ip_geoip_dst_lon": {
+ "type": "float"
+ },
+ "ip_ip_flags": {
+ "type": "short"
+ },
+ "ip_ip_flags_sf": {
+ "type": "boolean"
+ },
+ "ip_ip_flags_rb": {
+ "type": "boolean"
+ },
+ "ip_ip_flags_df": {
+ "type": "boolean"
+ },
+ "ip_ip_flags_mf": {
+ "type": "boolean"
+ },
+ "ip_ip_frag_offset": {
+ "type": "integer"
+ },
+ "ip_ip_ttl": {
+ "type": "short"
+ },
+ "ip_ip_proto": {
+ "type": "short"
+ },
+ "ip_ip_checksum": {
+ "type": "integer"
+ },
+ "ip_ip_checksum_calculated": {
+ "type": "integer"
+ },
+ "ip_ip_checksum_status": {
+ "type": "short"
+ },
+ "ip_ip_opt_type": {
+ "type": "short"
+ },
+ "ip_ip_opt_type_copy": {
+ "type": "boolean"
+ },
+ "ip_ip_opt_type_class": {
+ "type": "short"
+ },
+ "ip_ip_opt_type_number": {
+ "type": "short"
+ },
+ "ip_ip_opt_len": {
+ "type": "short"
+ },
+ "ip_ip_opt_ptr": {
+ "type": "short"
+ },
+ "ip_ip_opt_sid": {
+ "type": "integer"
+ },
+ "ip_ip_opt_mtu": {
+ "type": "integer"
+ },
+ "ip_ip_opt_id_number": {
+ "type": "integer"
+ },
+ "ip_ip_opt_ohc": {
+ "type": "integer"
+ },
+ "ip_ip_opt_rhc": {
+ "type": "integer"
+ },
+ "ip_ip_opt_originator": {
+ "type": "ip"
+ },
+ "ip_ip_opt_ra": {
+ "type": "integer"
+ },
+ "ip_ip_opt_addr": {
+ "type": "ip"
+ },
+ "ip_ip_opt_padding": {
+ "type": "byte"
+ },
+ "ip_ip_opt_qs_func": {
+ "type": "short"
+ },
+ "ip_ip_opt_qs_rate": {
+ "type": "short"
+ },
+ "ip_ip_opt_qs_ttl": {
+ "type": "short"
+ },
+ "ip_ip_opt_qs_ttl_diff": {
+ "type": "short"
+ },
+ "ip_ip_opt_qs_unused": {
+ "type": "short"
+ },
+ "ip_ip_opt_qs_nonce": {
+ "type": "long"
+ },
+ "ip_ip_opt_qs_reserved": {
+ "type": "long"
+ },
+ "ip_ip_opt_sec_rfc791_sec": {
+ "type": "integer"
+ },
+ "ip_ip_opt_sec_rfc791_comp": {
+ "type": "integer"
+ },
+ "ip_ip_opt_sec_cl": {
+ "type": "short"
+ },
+ "ip_ip_opt_sec_prot_auth_flags": {
+ "type": "short"
+ },
+ "ip_ip_opt_sec_prot_auth_genser": {
+ "type": "boolean"
+ },
+ "ip_ip_opt_sec_prot_auth_siop_esi": {
+ "type": "boolean"
+ },
+ "ip_ip_opt_sec_prot_auth_sci": {
+ "type": "boolean"
+ },
+ "ip_ip_opt_sec_prot_auth_nsa": {
+ "type": "boolean"
+ },
+ "ip_ip_opt_sec_prot_auth_doe": {
+ "type": "boolean"
+ },
+ "ip_ip_opt_sec_prot_auth_unassigned": {
+ "type": "short"
+ },
+ "ip_ip_opt_sec_prot_auth_unassigned": {
+ "type": "short"
+ },
+ "ip_ip_opt_sec_prot_auth_fti": {
+ "type": "boolean"
+ },
+ "ip_ip_opt_ext_sec_add_sec_info_format_code": {
+ "type": "short"
+ },
+ "ip_ip_opt_ext_sec_add_sec_info": {
+ "type": "byte"
+ },
+ "ip_ip_rec_rt": {
+ "type": "ip"
+ },
+ "ip_ip_cur_rt": {
+ "type": "ip"
+ },
+ "ip_ip_src_rt": {
+ "type": "ip"
+ },
+ "ip_ip_empty_rt": {
+ "type": "ip"
+ },
+ "ip_ip_cipso_tag_type": {
+ "type": "short"
+ },
+ "ip_ip_fragment_overlap": {
+ "type": "boolean"
+ },
+ "ip_ip_fragment_overlap_conflict": {
+ "type": "boolean"
+ },
+ "ip_ip_fragment_multipletails": {
+ "type": "boolean"
+ },
+ "ip_ip_fragment_toolongfragment": {
+ "type": "boolean"
+ },
+ "ip_ip_fragment_error": {
+ "type": "long"
+ },
+ "ip_ip_fragment_count": {
+ "type": "long"
+ },
+ "ip_ip_fragment": {
+ "type": "long"
+ },
+ "ip_ip_fragments": {
+ "type": "byte"
+ },
+ "ip_ip_reassembled_in": {
+ "type": "long"
+ },
+ "ip_ip_reassembled_length": {
+ "type": "long"
+ },
+ "ip_ip_reassembled_data": {
+ "type": "byte"
+ },
+ "ip_ip_cipso_doi": {
+ "type": "long"
+ },
+ "ip_ip_cipso_sensitivity_level": {
+ "type": "short"
+ },
+ "ip_ip_cipso_tag_data": {
+ "type": "byte"
+ },
+ "ip_ip_opt_overflow": {
+ "type": "short"
+ },
+ "ip_ip_opt_flag": {
+ "type": "short"
+ },
+ "ip_ip_opt_time_stamp": {
+ "type": "long"
+ },
+ "ip_ip_opt_time_stamp_addr": {
+ "type": "ip"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/test/baseline/ff-ts-usec-pcap-direct.txt b/test/baseline/ff-ts-usec-pcap-direct.txt
new file mode 100644
index 0000000..f4ac417
--- /dev/null
+++ b/test/baseline/ff-ts-usec-pcap-direct.txt
@@ -0,0 +1,4 @@
+1 1102274184.317453000 0.000000000
+2 1102274184.317748000 0.000295000
+3 1102274184.387484000 0.069736000
+4 1102274184.387798000 0.000314000
diff --git a/test/baseline/io-rawshark-dhcp-pcap.txt b/test/baseline/io-rawshark-dhcp-pcap.txt
new file mode 100644
index 0000000..c6fd020
--- /dev/null
+++ b/test/baseline/io-rawshark-dhcp-pcap.txt
@@ -0,0 +1,5 @@
+
+1 1 -
+2 1 -
+3 1 -
+4 1 -
diff --git a/test/captures/arp.pcap b/test/captures/arp.pcap
new file mode 100644
index 0000000..f9fca0c
--- /dev/null
+++ b/test/captures/arp.pcap
Binary files differ
diff --git a/test/captures/c1222_std_example8.pcap b/test/captures/c1222_std_example8.pcap
new file mode 100644
index 0000000..ad517f0
--- /dev/null
+++ b/test/captures/c1222_std_example8.pcap
Binary files differ
diff --git a/test/captures/challenge01_ooo_stream.pcapng.gz b/test/captures/challenge01_ooo_stream.pcapng.gz
new file mode 100644
index 0000000..aa96da0
--- /dev/null
+++ b/test/captures/challenge01_ooo_stream.pcapng.gz
Binary files differ
diff --git a/test/captures/communityid.pcap.gz b/test/captures/communityid.pcap.gz
new file mode 100644
index 0000000..a7ecc4e
--- /dev/null
+++ b/test/captures/communityid.pcap.gz
Binary files differ
diff --git a/test/captures/cose_encrypt0_tagged.cbordiag b/test/captures/cose_encrypt0_tagged.cbordiag
new file mode 100644
index 0000000..36b9d62
--- /dev/null
+++ b/test/captures/cose_encrypt0_tagged.cbordiag
@@ -0,0 +1,12 @@
+16(
+ [
+ / protected h'a1010a' / << {
+ / alg / 1:10 / AES-CCM-16-64-128 /
+ } >> ,
+ / unprotected / {
+ / iv / 5:h'89f52f65a1c580933b5261a78c'
+ },
+ / ciphertext / h'5974e1b99a3a4cc09a659aa2e9e7fff161d38ce71cb45ce
+460ffb569'
+ ]
+)
diff --git a/test/captures/cose_encrypt0_tagged.pcap b/test/captures/cose_encrypt0_tagged.pcap
new file mode 100644
index 0000000..51d1cbf
--- /dev/null
+++ b/test/captures/cose_encrypt0_tagged.pcap
Binary files differ
diff --git a/test/captures/cose_encrypt_tagged.cbordiag b/test/captures/cose_encrypt_tagged.cbordiag
new file mode 100644
index 0000000..30767f9
--- /dev/null
+++ b/test/captures/cose_encrypt_tagged.cbordiag
@@ -0,0 +1,24 @@
+96(
+ [
+ / protected h'a1010a' / << {
+ / alg / 1:10 / AES-CCM-16-64-128 /
+ } >>,
+ / unprotected / {
+ / iv / 5:h'89f52f65a1c580933b5261a76c'
+ },
+ / ciphertext / h'753548a19b1307084ca7b2056924ed95f2e3b17006dfe93
+1b687b847',
+ / recipients / [
+ [
+ / protected h'a10129' / << {
+ / alg / 1:-10
+ } >>,
+ / unprotected / {
+ / salt / -20:'aabbccddeeffgghh',
+ / kid / 4:'our-secret'
+ },
+ / ciphertext / h''
+ ]
+ ]
+ ]
+)
diff --git a/test/captures/cose_encrypt_tagged.pcap b/test/captures/cose_encrypt_tagged.pcap
new file mode 100644
index 0000000..71f5ad8
--- /dev/null
+++ b/test/captures/cose_encrypt_tagged.pcap
Binary files differ
diff --git a/test/captures/cose_keyset.cbordiag b/test/captures/cose_keyset.cbordiag
new file mode 100644
index 0000000..11b1df3
--- /dev/null
+++ b/test/captures/cose_keyset.cbordiag
@@ -0,0 +1,19 @@
+[
+ {
+ 1:2,
+ 2:'meriadoc.brandybuck@buckland.example',
+ -1:1,
+ -2:h'65eda5a12577c2bae829437fe338701a10aaa375e1bb5b5de108de439c0
+8551d',
+ -3:h'1e52ed75701163f7f9e40ddf9f341b3dc9ba860af7e0ca7ca7e9eecd008
+4d19c',
+ -4:h'aff907c99f9ad3aae6c4cdf21122bce2bd68b5283e6907154ad911840fa
+208cf'
+ },
+ {
+ 1:4,
+ 2:'018c0ae5-4d9b-471b-bfd6-eef314bc7037',
+ -1:h'849b57219dae48de646d07dbb533566e976686457c1491be3a76dcea6c4
+27188'
+ }
+]
diff --git a/test/captures/cose_keyset.pcap b/test/captures/cose_keyset.pcap
new file mode 100644
index 0000000..5b98d31
--- /dev/null
+++ b/test/captures/cose_keyset.pcap
Binary files differ
diff --git a/test/captures/cose_mac0_tagged.cbordiag b/test/captures/cose_mac0_tagged.cbordiag
new file mode 100644
index 0000000..ead2910
--- /dev/null
+++ b/test/captures/cose_mac0_tagged.cbordiag
@@ -0,0 +1,10 @@
+17(
+ [
+ / protected h'a1010f' / << {
+ / alg / 1:15 / AES-CBC-MAC-256//64 /
+ } >> ,
+ / unprotected / {},
+ / payload / 'This is the content.',
+ / tag / h'726043745027214f'
+ ]
+)
diff --git a/test/captures/cose_mac0_tagged.pcap b/test/captures/cose_mac0_tagged.pcap
new file mode 100644
index 0000000..0454b19
--- /dev/null
+++ b/test/captures/cose_mac0_tagged.pcap
Binary files differ
diff --git a/test/captures/cose_mac_tagged.cbordiag b/test/captures/cose_mac_tagged.cbordiag
new file mode 100644
index 0000000..91d9b89
--- /dev/null
+++ b/test/captures/cose_mac_tagged.cbordiag
@@ -0,0 +1,40 @@
+97(
+ [
+ / protected h'a10105' / << {
+ / alg / 1:5 / HMAC 256//256 /
+ } >> ,
+ / unprotected / {},
+ / payload / 'This is the content.',
+ / tag / h'bf48235e809b5c42e995f2b7d5fa13620e7ed834e337f6aa43df16
+1e49e9323e',
+ / recipients / [
+ [
+ / protected h'a101381c' / << {
+ / alg / 1:-29 / ECHD-ES+A128KW /
+ } >> ,
+ / unprotected / {
+ / ephemeral / -1:{
+ / kty / 1:2,
+ / crv / -1:3,
+ / x / -2:h'0043b12669acac3fd27898ffba0bcd2e6c366d53bc4db
+71f909a759304acfb5e18cdc7ba0b13ff8c7636271a6924b1ac63c02688075b55ef2
+d613574e7dc242f79c3',
+ / y / -3:true
+ },
+ / kid / 4:'bilbo.baggins@hobbiton.example'
+ },
+ / ciphertext / h'339bc4f79984cdc6b3e6ce5f315a4c7d2b0ac466fce
+a69e8c07dfbca5bb1f661bc5f8e0df9e3eff5'
+ ],
+ [
+ / protected / h'',
+ / unprotected / {
+ / alg / 1:-5 / A256KW /,
+ / kid / 4:'018c0ae5-4d9b-471b-bfd6-eef314bc7037'
+ },
+ / ciphertext / h'0b2c7cfce04e98276342d6476a7723c090dfdd15f9a
+518e7736549e998370695e6d6a83b4ae507bb'
+ ]
+ ]
+ ]
+)
diff --git a/test/captures/cose_mac_tagged.pcap b/test/captures/cose_mac_tagged.pcap
new file mode 100644
index 0000000..3c685b2
--- /dev/null
+++ b/test/captures/cose_mac_tagged.pcap
Binary files differ
diff --git a/test/captures/cose_sign1_tagged.cbordiag b/test/captures/cose_sign1_tagged.cbordiag
new file mode 100644
index 0000000..6406332
--- /dev/null
+++ b/test/captures/cose_sign1_tagged.cbordiag
@@ -0,0 +1,14 @@
+18(
+ [
+ / protected h'a10126' / << {
+ / alg / 1:-7 / ECDSA 256 /
+ } >>,
+ / unprotected / {
+ / kid / 4:'11'
+ },
+ / payload / 'This is the content.',
+ / signature / h'8eb33e4ca31d1c465ab05aac34cc6b23d58fef5c083106c4
+d25a91aef0b0117e2af9a291aa32e14ab834dc56ed2a223444547e01f11d3b0916e5
+a4c345cacb36'
+ ]
+)
diff --git a/test/captures/cose_sign1_tagged.pcap b/test/captures/cose_sign1_tagged.pcap
new file mode 100644
index 0000000..f3c8440
--- /dev/null
+++ b/test/captures/cose_sign1_tagged.pcap
Binary files differ
diff --git a/test/captures/cose_sign_tagged.cbordiag b/test/captures/cose_sign_tagged.cbordiag
new file mode 100644
index 0000000..92003a4
--- /dev/null
+++ b/test/captures/cose_sign_tagged.cbordiag
@@ -0,0 +1,39 @@
+98(
+ [
+ / protected h'a2687265736572766564f40281687265736572766564' /
+ << {
+ "reserved":false,
+ / crit / 2:[
+ "reserved"
+ ]
+ } >>,
+ / unprotected / {},
+ / payload / 'This is the content.',
+ / signatures / [
+ [
+ / protected h'a10126' / << {
+ / alg / 1:-7 / ECDSA 256 /
+ } >>,
+ / unprotected / {
+ / kid / 4:'11'
+ },
+ / signature / h'e2aeafd40d69d19dfe6e52077c5d7ff4e408282cbefb
+5d06cbf414af2e19d982ac45ac98b8544c908b4507de1e90b717c3d34816fe926a2b
+98f53afd2fa0f30a'
+ ],
+ [
+ / protected h'a1013823' / << {
+ / alg / 1:-36 / ECDSA 521 /
+ } >> ,
+ / unprotected / {
+ / kid / 4:'bilbo.baggins@hobbiton.example'
+ },
+ / signature / h'00a2d28a7c2bdb1587877420f65adf7d0b9a06635dd1
+de64bb62974c863f0b160dd2163734034e6ac003b01e8705524c5c4ca479a952f024
+7ee8cb0b4fb7397ba08d009e0c8bf482270cc5771aa143966e5a469a09f613488030
+c5b07ec6d722e3835adb5b2d8c44e95ffb13877dd2582866883535de3bb03d01753f
+83ab87bb4f7a0297'
+ ]
+ ]
+ ]
+)
diff --git a/test/captures/cose_sign_tagged.pcap b/test/captures/cose_sign_tagged.pcap
new file mode 100644
index 0000000..428acb9
--- /dev/null
+++ b/test/captures/cose_sign_tagged.pcap
Binary files differ
diff --git a/test/captures/data-utf8.pcap b/test/captures/data-utf8.pcap
new file mode 100644
index 0000000..286acab
--- /dev/null
+++ b/test/captures/data-utf8.pcap
Binary files differ
diff --git a/test/captures/dhcp-nanosecond.pcap b/test/captures/dhcp-nanosecond.pcap
new file mode 100644
index 0000000..7c93206
--- /dev/null
+++ b/test/captures/dhcp-nanosecond.pcap
Binary files differ
diff --git a/test/captures/dhcp-nanosecond.pcapng b/test/captures/dhcp-nanosecond.pcapng
new file mode 100644
index 0000000..3a65e4e
--- /dev/null
+++ b/test/captures/dhcp-nanosecond.pcapng
Binary files differ
diff --git a/test/captures/dhcp.pcap b/test/captures/dhcp.pcap
new file mode 100644
index 0000000..a42d610
--- /dev/null
+++ b/test/captures/dhcp.pcap
Binary files differ
diff --git a/test/captures/dhcp.pcapng b/test/captures/dhcp.pcapng
new file mode 100644
index 0000000..530c64c
--- /dev/null
+++ b/test/captures/dhcp.pcapng
Binary files differ
diff --git a/test/captures/dhe1.pcapng.gz b/test/captures/dhe1.pcapng.gz
new file mode 100644
index 0000000..3811257
--- /dev/null
+++ b/test/captures/dhe1.pcapng.gz
Binary files differ
diff --git a/test/captures/dmgr.pcapng b/test/captures/dmgr.pcapng
new file mode 100644
index 0000000..10eb29d
--- /dev/null
+++ b/test/captures/dmgr.pcapng
Binary files differ
diff --git a/test/captures/dns+icmp.pcapng.gz b/test/captures/dns+icmp.pcapng.gz
new file mode 100644
index 0000000..ddd09fa
--- /dev/null
+++ b/test/captures/dns+icmp.pcapng.gz
Binary files differ
diff --git a/test/captures/dns-mdns.pcap b/test/captures/dns-mdns.pcap
new file mode 100644
index 0000000..7410b1e
--- /dev/null
+++ b/test/captures/dns-mdns.pcap
Binary files differ
diff --git a/test/captures/dns-ooo.pcap b/test/captures/dns-ooo.pcap
new file mode 100644
index 0000000..594b925
--- /dev/null
+++ b/test/captures/dns-ooo.pcap
Binary files differ
diff --git a/test/captures/dns_port.pcap b/test/captures/dns_port.pcap
new file mode 100644
index 0000000..6974505
--- /dev/null
+++ b/test/captures/dns_port.pcap
Binary files differ
diff --git a/test/captures/dtls12-aes128ccm8-dsb.pcapng b/test/captures/dtls12-aes128ccm8-dsb.pcapng
new file mode 100644
index 0000000..9a9bf4e
--- /dev/null
+++ b/test/captures/dtls12-aes128ccm8-dsb.pcapng
Binary files differ
diff --git a/test/captures/dtls12-aes128ccm8.pcap b/test/captures/dtls12-aes128ccm8.pcap
new file mode 100644
index 0000000..3e293c1
--- /dev/null
+++ b/test/captures/dtls12-aes128ccm8.pcap
Binary files differ
diff --git a/test/captures/dtn_tcpclv3_bpv6_transfer.pcapng b/test/captures/dtn_tcpclv3_bpv6_transfer.pcapng
new file mode 100644
index 0000000..d8e0764
--- /dev/null
+++ b/test/captures/dtn_tcpclv3_bpv6_transfer.pcapng
Binary files differ
diff --git a/test/captures/dtn_tcpclv4_bpv7_transfer.pcapng b/test/captures/dtn_tcpclv4_bpv7_transfer.pcapng
new file mode 100644
index 0000000..4bdf152
--- /dev/null
+++ b/test/captures/dtn_tcpclv4_bpv7_transfer.pcapng
Binary files differ
diff --git a/test/captures/dtn_udpcl_bpv7_bpsec_bcb_admin.cbordiag b/test/captures/dtn_udpcl_bpv7_bpsec_bcb_admin.cbordiag
new file mode 100644
index 0000000..4b55f65
--- /dev/null
+++ b/test/captures/dtn_udpcl_bpv7_bpsec_bcb_admin.cbordiag
@@ -0,0 +1,7 @@
+[_
+ [7, 11094, 1, [2, [26622, 12070]], [2, [5279, 7390]], [2, [4785, 1111]], [81089243, 993], 52532350140, 1646, 2047, h'55E4'],
+ [7, 7, 175, 0, 24(h'1B000000013075CD37')],
+ [10, 5, 89, 0, 24(h'820007')],
+ [12, 25, 162, 0, 63(h'8101 02 01 82028219149f191cde 84 8201426869 820205 8203426869 820407 8181 8201457468657265')],
+ [1, 1, 3, 0, 24(h'8201848482F41B000000018BA3F02382F41A3027AC8782F41B000000018DFAF97381F503820282185D18B9821A533D733D190119')]
+]
diff --git a/test/captures/dtn_udpcl_bpv7_bpsec_bcb_admin.pcapng b/test/captures/dtn_udpcl_bpv7_bpsec_bcb_admin.pcapng
new file mode 100644
index 0000000..1625c19
--- /dev/null
+++ b/test/captures/dtn_udpcl_bpv7_bpsec_bcb_admin.pcapng
Binary files differ
diff --git a/test/captures/dtn_udpcl_bpv7_bpsec_bib_admin.cbordiag b/test/captures/dtn_udpcl_bpv7_bpsec_bib_admin.cbordiag
new file mode 100644
index 0000000..fdb4ade
--- /dev/null
+++ b/test/captures/dtn_udpcl_bpv7_bpsec_bib_admin.cbordiag
@@ -0,0 +1,7 @@
+[_
+ [7, 11094, 1, [2, [26622, 12070]], [2, [5279, 7390]], [2, [4785, 1111]], [81089243, 993], 52532350140, 1646, 2047, h'55E4'],
+ [7, 7, 175, 0, 24(h'1B000000013075CD37')],
+ [10, 5, 89, 0, 24(h'820007')],
+ [11, 25, 162, 0, 63(h'8101 01 01 82028219149f191cde 83 820105 8202426869 820307 8181 8201457468657265')],
+ [1, 1, 3, 0, 24(h'8201848482F41B000000018BA3F02382F41A3027AC8782F41B000000018DFAF97381F503820282185D18B9821A533D733D190119')]
+]
diff --git a/test/captures/dtn_udpcl_bpv7_bpsec_bib_admin.pcapng b/test/captures/dtn_udpcl_bpv7_bpsec_bib_admin.pcapng
new file mode 100644
index 0000000..245feca
--- /dev/null
+++ b/test/captures/dtn_udpcl_bpv7_bpsec_bib_admin.pcapng
Binary files differ
diff --git a/test/captures/dvb-ci_UV1_0000.pcap b/test/captures/dvb-ci_UV1_0000.pcap
new file mode 100644
index 0000000..aa192ca
--- /dev/null
+++ b/test/captures/dvb-ci_UV1_0000.pcap
Binary files differ
diff --git a/test/captures/empty.pcap b/test/captures/empty.pcap
new file mode 100644
index 0000000..a324304
--- /dev/null
+++ b/test/captures/empty.pcap
Binary files differ
diff --git a/test/captures/esp-bug-12671.pcapng.gz b/test/captures/esp-bug-12671.pcapng.gz
new file mode 100644
index 0000000..a31e8c5
--- /dev/null
+++ b/test/captures/esp-bug-12671.pcapng.gz
Binary files differ
diff --git a/test/captures/gitOverTCP.pcap b/test/captures/gitOverTCP.pcap
new file mode 100644
index 0000000..e2c33f4
--- /dev/null
+++ b/test/captures/gitOverTCP.pcap
Binary files differ
diff --git a/test/captures/grpc_person_search_json_with_image.pcapng.gz b/test/captures/grpc_person_search_json_with_image.pcapng.gz
new file mode 100644
index 0000000..4de8621
--- /dev/null
+++ b/test/captures/grpc_person_search_json_with_image.pcapng.gz
Binary files differ
diff --git a/test/captures/grpc_person_search_protobuf_with_image-missing_headers.pcapng.gz b/test/captures/grpc_person_search_protobuf_with_image-missing_headers.pcapng.gz
new file mode 100644
index 0000000..c128bb3
--- /dev/null
+++ b/test/captures/grpc_person_search_protobuf_with_image-missing_headers.pcapng.gz
Binary files differ
diff --git a/test/captures/grpc_person_search_protobuf_with_image.pcapng.gz b/test/captures/grpc_person_search_protobuf_with_image.pcapng.gz
new file mode 100644
index 0000000..076c7bc
--- /dev/null
+++ b/test/captures/grpc_person_search_protobuf_with_image.pcapng.gz
Binary files differ
diff --git a/test/captures/grpc_stream_reassembly_sample.pcapng.gz b/test/captures/grpc_stream_reassembly_sample.pcapng.gz
new file mode 100644
index 0000000..f5e22e0
--- /dev/null
+++ b/test/captures/grpc_stream_reassembly_sample.pcapng.gz
Binary files differ
diff --git a/test/captures/grpc_web.pcapng.gz b/test/captures/grpc_web.pcapng.gz
new file mode 100644
index 0000000..5d52714
--- /dev/null
+++ b/test/captures/grpc_web.pcapng.gz
Binary files differ
diff --git a/test/captures/http-brotli.pcapng b/test/captures/http-brotli.pcapng
new file mode 100644
index 0000000..5d7a499
--- /dev/null
+++ b/test/captures/http-brotli.pcapng
Binary files differ
diff --git a/test/captures/http-ooo-fuzzed.pcapng b/test/captures/http-ooo-fuzzed.pcapng
new file mode 100644
index 0000000..7cb871c
--- /dev/null
+++ b/test/captures/http-ooo-fuzzed.pcapng
Binary files differ
diff --git a/test/captures/http-ooo.pcap b/test/captures/http-ooo.pcap
new file mode 100644
index 0000000..be0b5d2
--- /dev/null
+++ b/test/captures/http-ooo.pcap
Binary files differ
diff --git a/test/captures/http-ooo2.pcap b/test/captures/http-ooo2.pcap
new file mode 100644
index 0000000..8cab45c
--- /dev/null
+++ b/test/captures/http-ooo2.pcap
Binary files differ
diff --git a/test/captures/http.pcap b/test/captures/http.pcap
new file mode 100644
index 0000000..145c2b0
--- /dev/null
+++ b/test/captures/http.pcap
Binary files differ
diff --git a/test/captures/http2-brotli.pcapng b/test/captures/http2-brotli.pcapng
new file mode 100644
index 0000000..7e07e85
--- /dev/null
+++ b/test/captures/http2-brotli.pcapng
Binary files differ
diff --git a/test/captures/http2-data-reassembly.pcap b/test/captures/http2-data-reassembly.pcap
new file mode 100644
index 0000000..b0f15ee
--- /dev/null
+++ b/test/captures/http2-data-reassembly.pcap
Binary files differ
diff --git a/test/captures/http2_follow_multistream.pcapng b/test/captures/http2_follow_multistream.pcapng
new file mode 100644
index 0000000..afba90e
--- /dev/null
+++ b/test/captures/http2_follow_multistream.pcapng
Binary files differ
diff --git a/test/captures/http3-qpack-reassembly-anon.pcapng b/test/captures/http3-qpack-reassembly-anon.pcapng
new file mode 100644
index 0000000..4a7e02d
--- /dev/null
+++ b/test/captures/http3-qpack-reassembly-anon.pcapng
Binary files differ
diff --git a/test/captures/icmp.pcapng.gz b/test/captures/icmp.pcapng.gz
new file mode 100644
index 0000000..653b9e5
--- /dev/null
+++ b/test/captures/icmp.pcapng.gz
Binary files differ
diff --git a/test/captures/ieee802.3cb-ping.pcapng.gz b/test/captures/ieee802.3cb-ping.pcapng.gz
new file mode 100644
index 0000000..71b4beb
--- /dev/null
+++ b/test/captures/ieee802.3cb-ping.pcapng.gz
Binary files differ
diff --git a/test/captures/ikev1-bug-12610.pcapng.gz b/test/captures/ikev1-bug-12610.pcapng.gz
new file mode 100644
index 0000000..b4d6be6
--- /dev/null
+++ b/test/captures/ikev1-bug-12610.pcapng.gz
Binary files differ
diff --git a/test/captures/ikev1-bug-12620.pcapng.gz b/test/captures/ikev1-bug-12620.pcapng.gz
new file mode 100644
index 0000000..14724fc
--- /dev/null
+++ b/test/captures/ikev1-bug-12620.pcapng.gz
Binary files differ
diff --git a/test/captures/ikev1-certs.pcap b/test/captures/ikev1-certs.pcap
new file mode 100644
index 0000000..6922aa3
--- /dev/null
+++ b/test/captures/ikev1-certs.pcap
Binary files differ
diff --git a/test/captures/ikev2-decrypt-3des-sha1_160.pcap b/test/captures/ikev2-decrypt-3des-sha1_160.pcap
new file mode 100644
index 0000000..ffdc7b5
--- /dev/null
+++ b/test/captures/ikev2-decrypt-3des-sha1_160.pcap
Binary files differ
diff --git a/test/captures/ikev2-decrypt-aes128ccm12-2.pcap b/test/captures/ikev2-decrypt-aes128ccm12-2.pcap
new file mode 100644
index 0000000..5ffecbe
--- /dev/null
+++ b/test/captures/ikev2-decrypt-aes128ccm12-2.pcap
Binary files differ
diff --git a/test/captures/ikev2-decrypt-aes128ccm12.pcap b/test/captures/ikev2-decrypt-aes128ccm12.pcap
new file mode 100644
index 0000000..66dabfe
--- /dev/null
+++ b/test/captures/ikev2-decrypt-aes128ccm12.pcap
Binary files differ
diff --git a/test/captures/ikev2-decrypt-aes192ctr.pcap b/test/captures/ikev2-decrypt-aes192ctr.pcap
new file mode 100644
index 0000000..31f16cb
--- /dev/null
+++ b/test/captures/ikev2-decrypt-aes192ctr.pcap
Binary files differ
diff --git a/test/captures/ikev2-decrypt-aes256cbc.pcapng b/test/captures/ikev2-decrypt-aes256cbc.pcapng
new file mode 100644
index 0000000..ce3d247
--- /dev/null
+++ b/test/captures/ikev2-decrypt-aes256cbc.pcapng
Binary files differ
diff --git a/test/captures/ikev2-decrypt-aes256ccm16.pcapng b/test/captures/ikev2-decrypt-aes256ccm16.pcapng
new file mode 100644
index 0000000..7887461
--- /dev/null
+++ b/test/captures/ikev2-decrypt-aes256ccm16.pcapng
Binary files differ
diff --git a/test/captures/ikev2-decrypt-aes256gcm16.pcap b/test/captures/ikev2-decrypt-aes256gcm16.pcap
new file mode 100644
index 0000000..1e77424
--- /dev/null
+++ b/test/captures/ikev2-decrypt-aes256gcm16.pcap
Binary files differ
diff --git a/test/captures/ikev2-decrypt-aes256gcm8.pcap b/test/captures/ikev2-decrypt-aes256gcm8.pcap
new file mode 100644
index 0000000..a0d74de
--- /dev/null
+++ b/test/captures/ikev2-decrypt-aes256gcm8.pcap
Binary files differ
diff --git a/test/captures/ipoipoip.pcap b/test/captures/ipoipoip.pcap
new file mode 100644
index 0000000..4c15e1a
--- /dev/null
+++ b/test/captures/ipoipoip.pcap
Binary files differ
diff --git a/test/captures/ipv6.pcap b/test/captures/ipv6.pcap
new file mode 100644
index 0000000..bdfb72c
--- /dev/null
+++ b/test/captures/ipv6.pcap
Binary files differ
diff --git a/test/captures/ipx_rip.pcap b/test/captures/ipx_rip.pcap
new file mode 100644
index 0000000..89533cb
--- /dev/null
+++ b/test/captures/ipx_rip.pcap
Binary files differ
diff --git a/test/captures/knxip_DataSec.pcap b/test/captures/knxip_DataSec.pcap
new file mode 100644
index 0000000..7e28cd4
--- /dev/null
+++ b/test/captures/knxip_DataSec.pcap
Binary files differ
diff --git a/test/captures/knxip_SecureWrapper.pcap b/test/captures/knxip_SecureWrapper.pcap
new file mode 100644
index 0000000..0be64d8
--- /dev/null
+++ b/test/captures/knxip_SecureWrapper.pcap
Binary files differ
diff --git a/test/captures/knxip_TimerNotify.pcap b/test/captures/knxip_TimerNotify.pcap
new file mode 100644
index 0000000..4e7ae37
--- /dev/null
+++ b/test/captures/knxip_TimerNotify.pcap
Binary files differ
diff --git a/test/captures/krb-816.pcap.gz b/test/captures/krb-816.pcap.gz
new file mode 100644
index 0000000..59002a6
--- /dev/null
+++ b/test/captures/krb-816.pcap.gz
Binary files differ
diff --git a/test/captures/logistics_multicast.pcapng b/test/captures/logistics_multicast.pcapng
new file mode 100644
index 0000000..1068480
--- /dev/null
+++ b/test/captures/logistics_multicast.pcapng
Binary files differ
diff --git a/test/captures/many_interfaces.pcapng.1 b/test/captures/many_interfaces.pcapng.1
new file mode 100644
index 0000000..960e35d
--- /dev/null
+++ b/test/captures/many_interfaces.pcapng.1
Binary files differ
diff --git a/test/captures/many_interfaces.pcapng.2 b/test/captures/many_interfaces.pcapng.2
new file mode 100644
index 0000000..7056f46
--- /dev/null
+++ b/test/captures/many_interfaces.pcapng.2
Binary files differ
diff --git a/test/captures/many_interfaces.pcapng.3 b/test/captures/many_interfaces.pcapng.3
new file mode 100644
index 0000000..48367c0
--- /dev/null
+++ b/test/captures/many_interfaces.pcapng.3
Binary files differ
diff --git a/test/captures/mongo-zstd.pcapng b/test/captures/mongo-zstd.pcapng
new file mode 100644
index 0000000..565815f
--- /dev/null
+++ b/test/captures/mongo-zstd.pcapng
Binary files differ
diff --git a/test/captures/netperfmeter.pcapng.gz b/test/captures/netperfmeter.pcapng.gz
new file mode 100644
index 0000000..b62386a
--- /dev/null
+++ b/test/captures/netperfmeter.pcapng.gz
Binary files differ
diff --git a/test/captures/nfs.pcap b/test/captures/nfs.pcap
new file mode 100644
index 0000000..e71d80b
--- /dev/null
+++ b/test/captures/nfs.pcap
Binary files differ
diff --git a/test/captures/ntp.pcap b/test/captures/ntp.pcap
new file mode 100644
index 0000000..815ed71
--- /dev/null
+++ b/test/captures/ntp.pcap
Binary files differ
diff --git a/test/captures/owe.pcapng.gz b/test/captures/owe.pcapng.gz
new file mode 100644
index 0000000..930d6bc
--- /dev/null
+++ b/test/captures/owe.pcapng.gz
Binary files differ
diff --git a/test/captures/packet-h2-14_headers.pcapng b/test/captures/packet-h2-14_headers.pcapng
new file mode 100644
index 0000000..5f4f0e5
--- /dev/null
+++ b/test/captures/packet-h2-14_headers.pcapng
Binary files differ
diff --git a/test/captures/protobuf_tcp_addressbook.pcapng.gz b/test/captures/protobuf_tcp_addressbook.pcapng.gz
new file mode 100644
index 0000000..7c985c3
--- /dev/null
+++ b/test/captures/protobuf_tcp_addressbook.pcapng.gz
Binary files differ
diff --git a/test/captures/protobuf_test_default_value.pcapng b/test/captures/protobuf_test_default_value.pcapng
new file mode 100644
index 0000000..37144ee
--- /dev/null
+++ b/test/captures/protobuf_test_default_value.pcapng
Binary files differ
diff --git a/test/captures/protobuf_test_leading_dot.pcapng b/test/captures/protobuf_test_leading_dot.pcapng
new file mode 100644
index 0000000..1ec0dcc
--- /dev/null
+++ b/test/captures/protobuf_test_leading_dot.pcapng
Binary files differ
diff --git a/test/captures/protobuf_test_map_and_oneof_types.pcapng b/test/captures/protobuf_test_map_and_oneof_types.pcapng
new file mode 100644
index 0000000..fa1b298
--- /dev/null
+++ b/test/captures/protobuf_test_map_and_oneof_types.pcapng
Binary files differ
diff --git a/test/captures/protobuf_udp_addressbook_with_image_ts.pcapng b/test/captures/protobuf_udp_addressbook_with_image_ts.pcapng
new file mode 100644
index 0000000..d6cc864
--- /dev/null
+++ b/test/captures/protobuf_udp_addressbook_with_image_ts.pcapng
Binary files differ
diff --git a/test/captures/protohier-with-comments.pcapng b/test/captures/protohier-with-comments.pcapng
new file mode 100644
index 0000000..4a4d78e
--- /dev/null
+++ b/test/captures/protohier-with-comments.pcapng
Binary files differ
diff --git a/test/captures/protohier-without-comments.pcapng b/test/captures/protohier-without-comments.pcapng
new file mode 100644
index 0000000..942e920
--- /dev/null
+++ b/test/captures/protohier-without-comments.pcapng
Binary files differ
diff --git a/test/captures/quic-fragmented-handshakes.pcapng.gz b/test/captures/quic-fragmented-handshakes.pcapng.gz
new file mode 100644
index 0000000..f029a46
--- /dev/null
+++ b/test/captures/quic-fragmented-handshakes.pcapng.gz
Binary files differ
diff --git a/test/captures/quic_follow_multistream.pcapng b/test/captures/quic_follow_multistream.pcapng
new file mode 100644
index 0000000..10d98b5
--- /dev/null
+++ b/test/captures/quic_follow_multistream.pcapng
Binary files differ
diff --git a/test/captures/retrans-tls.pcap b/test/captures/retrans-tls.pcap
new file mode 100644
index 0000000..90be0de
--- /dev/null
+++ b/test/captures/retrans-tls.pcap
Binary files differ
diff --git a/test/captures/rsa-p-lt-q.pcap b/test/captures/rsa-p-lt-q.pcap
new file mode 100644
index 0000000..2d2a33f
--- /dev/null
+++ b/test/captures/rsa-p-lt-q.pcap
Binary files differ
diff --git a/test/captures/rsasnakeoil2.pcap b/test/captures/rsasnakeoil2.pcap
new file mode 100644
index 0000000..a1c6bd4
--- /dev/null
+++ b/test/captures/rsasnakeoil2.pcap
Binary files differ
diff --git a/test/captures/s7comm-fuzz.pcapng.gz b/test/captures/s7comm-fuzz.pcapng.gz
new file mode 100644
index 0000000..5eaa425
--- /dev/null
+++ b/test/captures/s7comm-fuzz.pcapng.gz
Binary files differ
diff --git a/test/captures/sample_control4_2012-03-24.pcap b/test/captures/sample_control4_2012-03-24.pcap
new file mode 100644
index 0000000..cd33b1c
--- /dev/null
+++ b/test/captures/sample_control4_2012-03-24.pcap
Binary files differ
diff --git a/test/captures/segmented_fpm.pcap b/test/captures/segmented_fpm.pcap
new file mode 100644
index 0000000..f86e103
--- /dev/null
+++ b/test/captures/segmented_fpm.pcap
Binary files differ
diff --git a/test/captures/sip-rtp.pcapng b/test/captures/sip-rtp.pcapng
new file mode 100644
index 0000000..5ea96ee
--- /dev/null
+++ b/test/captures/sip-rtp.pcapng
Binary files differ
diff --git a/test/captures/sip.pcapng b/test/captures/sip.pcapng
new file mode 100644
index 0000000..7d1f6f2
--- /dev/null
+++ b/test/captures/sip.pcapng
Binary files differ
diff --git a/test/captures/sipmsg.log b/test/captures/sipmsg.log
new file mode 100644
index 0000000..d69c99c
--- /dev/null
+++ b/test/captures/sipmsg.log
@@ -0,0 +1,136 @@
+File opened.
+Mar 6 13:34:22.599 UDP[3:0]10.102.131.194:5060 OPENED
+Mar 6 13:34:22.616 UDP[6:0]10.102.130.185:5060 OPENED
+Mar 6 13:34:49.416 On [6:0]10.102.130.185:5060 received from 10.102.130.150:5060
+REGISTER sip:csp.noklab.net SIP/2.0
+Via: SIP/2.0/UDP 192.168.1.100:5060;branch=z9hG4bK26b7a48d
+From: sip:34903@csp.noklab.net
+To: sip:34903@csp.noklab.net
+Call-ID: 003094c3-a0160002-23aa7e86-29e5808d@192.168.1.100
+CSeq: 144 REGISTER
+User-Agent: CSCO/7
+Contact: <sip:34903@192.168.1.100:5060>
+Content-Length: 0
+Expires: 3600
+
+
+----------------------------------------
+Mar 6 13:34:49.516 On [6:0]10.102.130.185:5060 sent to 10.102.130.150:5060
+SIP/2.0 200 OK
+Via: SIP/2.0/UDP 192.168.1.100:5060;received=10.102.130.150;branch=z9hG4bK26b7a48d;rport=5060
+From: sip:34903@csp.noklab.net
+To: sip:34903@csp.noklab.net
+Call-ID: 003094c3-a0160002-23aa7e86-29e5808d@192.168.1.100
+CSeq: 144 REGISTER
+Contact: <sip:34903@192.168.1.100:5060>;expires=34
+
+
+----------------------------------------
+Mar 6 13:39:06.100 On 127.0.0.1:5060 received from 127.0.0.1:5070
+INVITE sip:17324201111@135.25.31.10:5060;acme_realm=cpea8500 SIP/2.0
+Via: SIP/2.0/UDP 127.0.0.1:5070;branch=z9hG4bKIWFuqpq6n00c0o1eckfm741;acme_irealm=public;acme_sa=192.168.109.112
+Contact: "B5-2C23-052 Blu"<sip:7323685154@127.0.0.1:5070>
+GenericID: 117318834600008@0008250123d0
+Supported: 100rel
+From: "B5-2C23-052 Blu"<sip:7323685154@127.25.29.135:5060>;tag=0000047b000ce0e0
+To: <sip:17324201111@135.25.31.10:5060>
+Call-ID: 7f00000113ce0000047b000cd140@127.0.0.1
+CSeq: 2 INVITE
+P-Asserted-Identity: "B5-2C23-052 Blu"<sip:7323685154@127.25.29.135:5060>
+Content-Length: 187
+Content-Type: application/sdp
+
+v=0
+o=IWF 10 10 IN IP4 192.168.109.113
+s=H323 Call
+c=IN IP4 192.168.109.113
+t=0 0
+m=audio 29156 RTP/AVP 18 0
+a=rtpmap:18 G729/8000/1
+a=fmtp:18 annexb=yes
+a=rtpmap:0 PCMU/8000/1
+
+----------------------------------------
+Mar 6 13:39:06.104 On 127.0.0.1:5060 sent to 127.0.0.1:5070
+SIP/2.0 100 Trying
+Via: SIP/2.0/UDP 127.0.0.1:5070;branch=z9hG4bKIWFuqpq6n00c0o1eckfm741
+From: "B5-2C23-052 Blu"<sip:7323685154@127.0.0.1:5060>;tag=0000047b000ce0e0
+To: <sip:17324201111@127.0.0.1:5060>
+Call-ID: 7f00000113ce0000047b000cd140@127.0.0.1
+CSeq: 2 INVITE
+
+
+----------------------------------------
+Mar 6 13:39:06.122 On 127.0.0.1:2945 sent to 127.0.0.1:2944
+ 0000: ac 3e fd 01 00 07 89 d9 00 fc 10 00 00 00 02 00 .>..............
+ 0010: 00 00 00 00 f1 21 00 00 00 02 00 6d 30 00 6a 8c .....!.....m0.j.
+ 0020: 00 02 20 01 80 00 06 70 75 62 6c 69 63 83 00 05 .. ....public...
+ 0030: 24 57 45 53 54 84 00 08 63 70 65 61 38 35 30 30 $WEST...cpea8500
+ 0040: 86 00 05 24 45 41 53 54 88 00 01 01 8b 00 01 00 ...$EAST........
+ 0050: 89 00 02 00 02 8a 00 04 00 00 00 00 98 00 04 00 ................
+ 0060: 00 00 00 99 00 04 00 00 00 00 9a 00 04 00 00 00 ................
+ 0070: 00 94 00 04 87 19 1f 0a 96 00 01 01 a7 00 01 00 ................
+ 0080: a8 00 01 00 a9 00 02 00 00 21 00 00 00 02 00 76 .........!.....v
+ 0090: 30 00 73 8c 00 02 10 01 80 00 08 63 70 65 61 38 0.s........cpea8
+ 00a0: 35 30 30 83 00 05 24 45 41 53 54 84 00 06 70 75 500...$EAST...pu
+ 00b0: 62 6c 69 63 86 00 05 24 57 45 53 54 87 00 06 c0 blic...$WEST....
+ 00c0: a8 6d 71 71 e4 88 00 01 01 8b 00 01 01 89 00 02 .mqq............
+ 00d0: 00 02 8a 00 04 00 00 00 00 98 00 04 00 00 00 00 ................
+ 00e0: 99 00 04 00 00 00 00 9a 00 04 00 00 00 00 94 00 ................
+ 00f0: 04 7f 00 00 01 96 00 01 01 a7 00 01 00 a8 00 01 ................
+ 0100: 00 a9 00 02 00 00 ......
+Transaction = 494041 {
+ Context = $ {
+ Add = $ {
+ Flow {
+ index=1E
+ irealm=public
+ idest=$WEST
+ erealm=cpea8500
+ esource=$EAST
+ media=audio
+ trans=UDP
+ mode=off
+ num=2
+ bw=0
+ peakr=0
+ avgr=0
+ mbs=0
+ subscr=135.25.31.10
+ }
+ },
+ Add = $ {
+ Flow {
+ index=1W
+ irealm=cpea8500
+ idest=$EAST
+ erealm=public
+ esource=$WEST
+ edest=192.168.109.113:29156
+ media=audio
+ trans=UDP
+ mode=1way
+ num=2
+ bw=0
+ peakr=0
+ avgr=0
+ mbs=0
+ subscr=127.0.0.1
+ }
+ }
+ }
+}
+----------------------------------------
+Mar 6 13:39:06.127 On 127.0.0.1:5060 sent to 127.0.0.1:5070
+SIP/2.0 181 Call Is Being Forwarded
+Via: SIP/2.0/UDP 127.0.0.1:5070;branch=z9hG4bKIWFuqpq6n00c0o1eckfm741;acme_iwf_2833_preferred=101
+From: "B5-2C23-052 Blu"<sip:7323685154@127.25.29.135:5060>;tag=0000047b000ce0e0
+To: <sip:17324201111@135.25.31.10:5060>
+Call-ID: 7f00000113ce0000047b000cd140@127.0.0.1
+CSeq: 2 INVITE
+
+
+----------------------------------------
+Jun 8 14:35:50.233 UDP[3:0]10.102.131.194:5060 CLOSED
+Jun 8 14:35:50.233 UDP[6:0]10.102.130.185:5060 CLOSED
+File closed.
diff --git a/test/captures/smb300-aes-128-ccm.pcap.gz b/test/captures/smb300-aes-128-ccm.pcap.gz
new file mode 100644
index 0000000..6f0c8d7
--- /dev/null
+++ b/test/captures/smb300-aes-128-ccm.pcap.gz
Binary files differ
diff --git a/test/captures/smb311-aes-128-ccm.pcap.gz b/test/captures/smb311-aes-128-ccm.pcap.gz
new file mode 100644
index 0000000..fa4e196
--- /dev/null
+++ b/test/captures/smb311-aes-128-ccm.pcap.gz
Binary files differ
diff --git a/test/captures/smb311-aes-128-gcm.pcap.gz b/test/captures/smb311-aes-128-gcm.pcap.gz
new file mode 100644
index 0000000..02e34a9
--- /dev/null
+++ b/test/captures/smb311-aes-128-gcm.pcap.gz
Binary files differ
diff --git a/test/captures/smb311-aes-256-ccm.pcap.gz b/test/captures/smb311-aes-256-ccm.pcap.gz
new file mode 100644
index 0000000..cfc71eb
--- /dev/null
+++ b/test/captures/smb311-aes-256-ccm.pcap.gz
Binary files differ
diff --git a/test/captures/smb311-aes-256-gcm.pcap.gz b/test/captures/smb311-aes-256-gcm.pcap.gz
new file mode 100644
index 0000000..c7e1b1a
--- /dev/null
+++ b/test/captures/smb311-aes-256-gcm.pcap.gz
Binary files differ
diff --git a/test/captures/smb311-chained-patternv1-lznt1.pcapng.gz b/test/captures/smb311-chained-patternv1-lznt1.pcapng.gz
new file mode 100644
index 0000000..92e5a04
--- /dev/null
+++ b/test/captures/smb311-chained-patternv1-lznt1.pcapng.gz
Binary files differ
diff --git a/test/captures/smb311-lz77-lz77huff-lznt1.pcap.gz b/test/captures/smb311-lz77-lz77huff-lznt1.pcap.gz
new file mode 100644
index 0000000..f14bfba
--- /dev/null
+++ b/test/captures/smb311-lz77-lz77huff-lznt1.pcap.gz
Binary files differ
diff --git a/test/captures/snakeoil-dtls.pcap b/test/captures/snakeoil-dtls.pcap
new file mode 100644
index 0000000..ef5fd21
--- /dev/null
+++ b/test/captures/snakeoil-dtls.pcap
Binary files differ
diff --git a/test/captures/tcp-badsegments.pcap b/test/captures/tcp-badsegments.pcap
new file mode 100644
index 0000000..a48b916
--- /dev/null
+++ b/test/captures/tcp-badsegments.pcap
Binary files differ
diff --git a/test/captures/tcp-exp-option-tarr.pcap.gz b/test/captures/tcp-exp-option-tarr.pcap.gz
new file mode 100644
index 0000000..12f5ec3
--- /dev/null
+++ b/test/captures/tcp-exp-option-tarr.pcap.gz
Binary files differ
diff --git a/test/captures/text2pcap_hash_eol.txt b/test/captures/text2pcap_hash_eol.txt
new file mode 100644
index 0000000..b7c6d2f
--- /dev/null
+++ b/test/captures/text2pcap_hash_eol.txt
@@ -0,0 +1,10 @@
+2015-10-01 21:16:24.317453 127.0.0.1 -> 127.0.0.1 UDP 96 Source port: 36887 Destination port: 36888
+
+0000 00 00 00 00 00 00 00 00 00 00 00 00 08 00 45 00 ..............E.
+0010 00 3e 3b f2 40 00 40 11 00 bb 7f 00 00 01 7f 00 .>;.@.@.........
+0020 00 01 90 17 90 18 00 2a 00 00 00 00 01 00 00 01 .......*........
+0030 00 00 00 00 00 00 01 01 01 01 01 01 01 01 20 23 .............. #
+0040 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 01 ................
+0050 01 01 01 01 01 01 01 01 01 01 01 01 2f cc 9c e4 ............/...
+
+#TEXT2PCAP test_directive
diff --git a/test/captures/tftp.pcap b/test/captures/tftp.pcap
new file mode 100644
index 0000000..f88c22a
--- /dev/null
+++ b/test/captures/tftp.pcap
Binary files differ
diff --git a/test/captures/tls-fragmented-handshakes.pcap.gz b/test/captures/tls-fragmented-handshakes.pcap.gz
new file mode 100644
index 0000000..9f97664
--- /dev/null
+++ b/test/captures/tls-fragmented-handshakes.pcap.gz
Binary files differ
diff --git a/test/captures/tls-fragmented-over-tcp-segmented.pcapng.gz b/test/captures/tls-fragmented-over-tcp-segmented.pcapng.gz
new file mode 100644
index 0000000..ed9e96f
--- /dev/null
+++ b/test/captures/tls-fragmented-over-tcp-segmented.pcapng.gz
Binary files differ
diff --git a/test/captures/tls-over-tls.pcapng.gz b/test/captures/tls-over-tls.pcapng.gz
new file mode 100644
index 0000000..1192bc6
--- /dev/null
+++ b/test/captures/tls-over-tls.pcapng.gz
Binary files differ
diff --git a/test/captures/tls-renegotiation.pcap b/test/captures/tls-renegotiation.pcap
new file mode 100644
index 0000000..7d772a5
--- /dev/null
+++ b/test/captures/tls-renegotiation.pcap
Binary files differ
diff --git a/test/captures/tls12-aes128ccm.pcap b/test/captures/tls12-aes128ccm.pcap
new file mode 100644
index 0000000..6f032a7
--- /dev/null
+++ b/test/captures/tls12-aes128ccm.pcap
Binary files differ
diff --git a/test/captures/tls12-aes256gcm.pcap b/test/captures/tls12-aes256gcm.pcap
new file mode 100644
index 0000000..576739c
--- /dev/null
+++ b/test/captures/tls12-aes256gcm.pcap
Binary files differ
diff --git a/test/captures/tls12-chacha20poly1305.pcap b/test/captures/tls12-chacha20poly1305.pcap
new file mode 100644
index 0000000..1eaa854
--- /dev/null
+++ b/test/captures/tls12-chacha20poly1305.pcap
Binary files differ
diff --git a/test/captures/tls12-dsb.pcapng b/test/captures/tls12-dsb.pcapng
new file mode 100644
index 0000000..d9bf1ab
--- /dev/null
+++ b/test/captures/tls12-dsb.pcapng
Binary files differ
diff --git a/test/captures/tls13-20-chacha20poly1305.pcap b/test/captures/tls13-20-chacha20poly1305.pcap
new file mode 100644
index 0000000..da2246d
--- /dev/null
+++ b/test/captures/tls13-20-chacha20poly1305.pcap
Binary files differ
diff --git a/test/captures/tls13-rfc8446.pcap b/test/captures/tls13-rfc8446.pcap
new file mode 100644
index 0000000..4500f59
--- /dev/null
+++ b/test/captures/tls13-rfc8446.pcap
Binary files differ
diff --git a/test/captures/trunc.pcap b/test/captures/trunc.pcap
new file mode 100644
index 0000000..8dd75ed
--- /dev/null
+++ b/test/captures/trunc.pcap
Binary files differ
diff --git a/test/captures/udt-dtls.pcapng.gz b/test/captures/udt-dtls.pcapng.gz
new file mode 100644
index 0000000..67750f3
--- /dev/null
+++ b/test/captures/udt-dtls.pcapng.gz
Binary files differ
diff --git a/test/captures/websocket-compressed-fragmented.pcapng.gz b/test/captures/websocket-compressed-fragmented.pcapng.gz
new file mode 100644
index 0000000..001a891
--- /dev/null
+++ b/test/captures/websocket-compressed-fragmented.pcapng.gz
Binary files differ
diff --git a/test/captures/websocket-compressed.pcapng.gz b/test/captures/websocket-compressed.pcapng.gz
new file mode 100644
index 0000000..8276c06
--- /dev/null
+++ b/test/captures/websocket-compressed.pcapng.gz
Binary files differ
diff --git a/test/captures/websocket-fragmented.pcapng.gz b/test/captures/websocket-fragmented.pcapng.gz
new file mode 100644
index 0000000..99078b5
--- /dev/null
+++ b/test/captures/websocket-fragmented.pcapng.gz
Binary files differ
diff --git a/test/captures/websocket.pcapng.gz b/test/captures/websocket.pcapng.gz
new file mode 100644
index 0000000..b83981f
--- /dev/null
+++ b/test/captures/websocket.pcapng.gz
Binary files differ
diff --git a/test/captures/wep.pcapng.gz b/test/captures/wep.pcapng.gz
new file mode 100644
index 0000000..e41f688
--- /dev/null
+++ b/test/captures/wep.pcapng.gz
Binary files differ
diff --git a/test/captures/wireguard-ping-tcp-dsb.pcapng b/test/captures/wireguard-ping-tcp-dsb.pcapng
new file mode 100644
index 0000000..d15790f
--- /dev/null
+++ b/test/captures/wireguard-ping-tcp-dsb.pcapng
Binary files differ
diff --git a/test/captures/wireguard-ping-tcp.pcap b/test/captures/wireguard-ping-tcp.pcap
new file mode 100644
index 0000000..79255ed
--- /dev/null
+++ b/test/captures/wireguard-ping-tcp.pcap
Binary files differ
diff --git a/test/captures/wireguard-psk.pcap b/test/captures/wireguard-psk.pcap
new file mode 100644
index 0000000..a38088b
--- /dev/null
+++ b/test/captures/wireguard-psk.pcap
Binary files differ
diff --git a/test/captures/wpa-Induction.pcap.gz b/test/captures/wpa-Induction.pcap.gz
new file mode 100644
index 0000000..27e3670
--- /dev/null
+++ b/test/captures/wpa-Induction.pcap.gz
Binary files differ
diff --git a/test/captures/wpa-ccmp-256.pcapng.gz b/test/captures/wpa-ccmp-256.pcapng.gz
new file mode 100644
index 0000000..d65bb1e
--- /dev/null
+++ b/test/captures/wpa-ccmp-256.pcapng.gz
Binary files differ
diff --git a/test/captures/wpa-eap-tls.pcap.gz b/test/captures/wpa-eap-tls.pcap.gz
new file mode 100644
index 0000000..307e5fa
--- /dev/null
+++ b/test/captures/wpa-eap-tls.pcap.gz
Binary files differ
diff --git a/test/captures/wpa-gcmp-256.pcapng.gz b/test/captures/wpa-gcmp-256.pcapng.gz
new file mode 100644
index 0000000..83ece8e
--- /dev/null
+++ b/test/captures/wpa-gcmp-256.pcapng.gz
Binary files differ
diff --git a/test/captures/wpa-gcmp.pcapng.gz b/test/captures/wpa-gcmp.pcapng.gz
new file mode 100644
index 0000000..e3b799b
--- /dev/null
+++ b/test/captures/wpa-gcmp.pcapng.gz
Binary files differ
diff --git a/test/captures/wpa-test-decode-mgmt.pcap.gz b/test/captures/wpa-test-decode-mgmt.pcap.gz
new file mode 100644
index 0000000..b4e04d2
--- /dev/null
+++ b/test/captures/wpa-test-decode-mgmt.pcap.gz
Binary files differ
diff --git a/test/captures/wpa-test-decode-tdls.pcap.gz b/test/captures/wpa-test-decode-tdls.pcap.gz
new file mode 100644
index 0000000..f166dda
--- /dev/null
+++ b/test/captures/wpa-test-decode-tdls.pcap.gz
Binary files differ
diff --git a/test/captures/wpa-test-decode.pcap.gz b/test/captures/wpa-test-decode.pcap.gz
new file mode 100644
index 0000000..050f94e
--- /dev/null
+++ b/test/captures/wpa-test-decode.pcap.gz
Binary files differ
diff --git a/test/captures/wpa1-gtk-rekey.pcapng.gz b/test/captures/wpa1-gtk-rekey.pcapng.gz
new file mode 100644
index 0000000..88e4c06
--- /dev/null
+++ b/test/captures/wpa1-gtk-rekey.pcapng.gz
Binary files differ
diff --git a/test/captures/wpa2-ft-eap.pcapng.gz b/test/captures/wpa2-ft-eap.pcapng.gz
new file mode 100644
index 0000000..5ec811a
--- /dev/null
+++ b/test/captures/wpa2-ft-eap.pcapng.gz
Binary files differ
diff --git a/test/captures/wpa2-ft-psk.pcapng.gz b/test/captures/wpa2-ft-psk.pcapng.gz
new file mode 100644
index 0000000..1579314
--- /dev/null
+++ b/test/captures/wpa2-ft-psk.pcapng.gz
Binary files differ
diff --git a/test/captures/wpa2-psk-mfp.pcapng.gz b/test/captures/wpa2-psk-mfp.pcapng.gz
new file mode 100644
index 0000000..da445ae
--- /dev/null
+++ b/test/captures/wpa2-psk-mfp.pcapng.gz
Binary files differ
diff --git a/test/captures/wpa3-sae.pcapng.gz b/test/captures/wpa3-sae.pcapng.gz
new file mode 100644
index 0000000..7558270
--- /dev/null
+++ b/test/captures/wpa3-sae.pcapng.gz
Binary files differ
diff --git a/test/captures/wpa3-suiteb-192.pcapng.gz b/test/captures/wpa3-suiteb-192.pcapng.gz
new file mode 100644
index 0000000..663dee9
--- /dev/null
+++ b/test/captures/wpa3-suiteb-192.pcapng.gz
Binary files differ
diff --git a/test/captures/wpa_ptk_extended_key_id.pcap.gz b/test/captures/wpa_ptk_extended_key_id.pcap.gz
new file mode 100644
index 0000000..c093018
--- /dev/null
+++ b/test/captures/wpa_ptk_extended_key_id.pcap.gz
Binary files differ
diff --git a/test/config/80211_keys.tmpl b/test/config/80211_keys.tmpl
new file mode 100644
index 0000000..da825ac
--- /dev/null
+++ b/test/config/80211_keys.tmpl
@@ -0,0 +1,12 @@
+# Keys needed for the decryption test suite
+"wep","1234567890"
+"wpa-pwd","Induction"
+"wpa-pwd","test0815"
+"wpa-pwd","12345678"
+"wpa-psk","a5001e18e0b3f792278825bc3abff72d7021d7c157b600470ef730e2490835d4"
+"wpa-psk","79258f6ceeecedd3482b92deaabdb675f09bcb4003ef5074f5ddb10a94ebe00a"
+"wpa-psk","23a9ee58c7810546ae3e7509fda9f97435778d689e53a54891c56d02f18ca162"
+"wpa-psk","ecbfe709d6151eaba6a4fd9cba94fbb570c1fc4c15506fad3185b4a0a0cfda9a"
+"wpa-psk","a4b0b2efa7f77d1006eccf1a814b62125c15fac5c137d9cdff8c75c43194268f"
+"wpa-psk","fc738f5b63ba93ebf0a45d42c5a0b1b5064649fa98f59bc062c2944de3780fe276088c95daaf672deb6780051aa13563"
+"msk","fc3fe399f0ab9eeb5b6e87b6e2b276d828e874de1773d4a925f5410d96565b22b1471711baffb8611b28d2a09cc1a6aaffbbfdf3cccf12db57f175c53bfe2b7b"
diff --git a/test/config/80211_keys.user_tk_tmpl b/test/config/80211_keys.user_tk_tmpl
new file mode 100644
index 0000000..f8f3361
--- /dev/null
+++ b/test/config/80211_keys.user_tk_tmpl
@@ -0,0 +1,12 @@
+# Keys needed for the 80211 user TK test cases in decryption test suite
+"tk","d0e57d224c1bb8806089d8c23154074c"
+"tk","6eaf63f4ad7997ced353723de3029f4d"
+"tk","fb42811bcb59b7845376246454fbdab7"
+"tk","4e30e8c019bea43ea5262b10853b818d"
+"tk","70cdbf2e5bc0ca22e53930818a5d80e4"
+"tk","4e6abbcf9dc0943936700b6825952218f58a47dfdf51dbb8ce9b02fd7d2d9e40"
+"tk","502085ca205e668f7e7c61cdf4f731336bb31e4f5b28ec91860174192e9b2190"
+"tk","755a9c1c9e605d5ff62849e4a17a935c"
+"tk","7ff30f7a8dd67950eaaf2f20a869a62d"
+"tk","b3dc2ff2d88d0d34c1ddc421cea17f304af3c46acbbe7b6d808b6ebf1b98ec38"
+"tk","a745ee2313f86515a155c4cb044bc148ae234b9c72707f772b69c2fede3e4016"
diff --git a/test/config/c1222_decryption_table.tmpl b/test/config/c1222_decryption_table.tmpl
new file mode 100644
index 0000000..4c070e1
--- /dev/null
+++ b/test/config/c1222_decryption_table.tmpl
@@ -0,0 +1,2 @@
+# This file is automatically generated, DO NOT MODIFY.
+"2",01020304050607080102030405060708
diff --git a/test/config/dtlsdecrypttablefile.tmpl b/test/config/dtlsdecrypttablefile.tmpl
new file mode 100644
index 0000000..869822e
--- /dev/null
+++ b/test/config/dtlsdecrypttablefile.tmpl
@@ -0,0 +1 @@
+"127.0.0.1","4433","data","TEST_KEYS_DIRsnakeoil-rsa.key",""
diff --git a/test/config/esp_sa.tmpl b/test/config/esp_sa.tmpl
new file mode 100644
index 0000000..24fbb21
--- /dev/null
+++ b/test/config/esp_sa.tmpl
@@ -0,0 +1,2 @@
+"IPv4","192.168.0.1","192.168.0.100","0x070883c2","AES-CBC [RFC3602]","0x5de1a4c2c72662c9fda7a7c78cd25623","HMAC-SHA-1-96 [RFC2404]","0x51c9213c18232f8f26c70c4dee6e0e6d56e31e8a"
+"IPv4","192.168.0.100","192.168.0.1","0xc254fe64","AES-CBC [RFC3602]","0x88e1dad7140af03b8d4f3d734d21be4b","HMAC-SHA-1-96 [RFC2404]","0x3e00d517c1220d4b7d2950fcc02edd4b6023d278"
diff --git a/test/config/ikev1_decryption_table.tmpl b/test/config/ikev1_decryption_table.tmpl
new file mode 100644
index 0000000..e457d2c
--- /dev/null
+++ b/test/config/ikev1_decryption_table.tmpl
@@ -0,0 +1,4 @@
+# This file is automatically generated, DO NOT MODIFY.
+fafaeb49382a763c,735be0cb62f82675c4f7bf8fbab9b56834ba76d6ab4fa240
+ab3e0f1f8bc3be8e,928392441e00ece5e8af3bc545416e86afa1291e7c013271514738b70ef25d22
+29f6006212625def,c26a81baec6070d289f0b4f13c2fb182e3d5406d11c29ebc2fc179502541325b
diff --git a/test/config/ikev2_decryption_table.tmpl b/test/config/ikev2_decryption_table.tmpl
new file mode 100644
index 0000000..674564e
--- /dev/null
+++ b/test/config/ikev2_decryption_table.tmpl
@@ -0,0 +1,10 @@
+# This file is automatically generated, DO NOT MODIFY.
+1234567890123456,0987654321098765,,,"NULL [RFC2410]",,,"NONE [RFC4306]"
+19ab98963486359f,78f13157ccd3b3d8,2e0e194070fc658c2bfbfdbf8b956be4b2eaa33d02a43cca,219f3080e631774b8d5836d3a675b099b1e271c9bdcf6e15,"3DES [RFC2451]",c96f5bad08aebbff60509c7495f11c183818b916,e742ac415cdfdd709c9de92769a169e0a5224f79,"HMAC_SHA1_160 [RFC4595]"
+ea684d21597afd36,d9fe2ab22dac23ac,be83fe15f6a9976941870830fe26c014b863b3,79e0f4476861a76e64329e787b1c4ff38d732f,"AES-CCM-128 with 12 octet ICV [RFC5282]",,,"NONE [RFC4306]"
+a2926ae833c6f138,5464c57d0dc5e272,5daf82e6fd7e57d5fbf76cd5af73fd46035db0bc,68848f4a7602b20c7d033cc998b0c097032ac38a,"AES-CTR-128 [RFC5930]",,,"ANY 96-bits of Authentication [No Checking]"
+81f24c0acd8fa55c,192383172724c706,aaa06839eaf0959d486eeecda7a48b23080963b5fd7217928e8fbf58,92dc96ec87076caa84e26b3621c7c469427e2e4bcc1b962362a3dde3,"AES-CTR-192 [RFC5930]",65777be31ee2137f31cf23fa0ee834dfede11cc0adc9a84541026642c09df2bf96056a2036e97a67ce7d3c5b6f37e17e8fe64f4ef23e14f5997fb7671df3adaf,42abd4709f1b94fd8c2270c74aae3fbe61c0b9c109c55f3e3b9ed7e480bc75c3985c15234caa623c8ca0606c303921d7cfd44861df1e798370b2ee95fe712e52,"HMAC_SHA2_512_256 [RFC4868]"
+191ccd371a7a1f7b,bc123d15e4af593f,9096ddd2933620e8f48122c53a3f562cb0222c1cf97ce41fcc874ea2582a89ac,6718c6b2bbef2f234eac4c13832f885d87b574afd2af0111161e99b5dc61b4d4,"AES-CBC-256 [RFC3602]",12d532c3e83c757906af548dfe1ccf223ca5507af77898454e2d55c8ace57a17,30c4ead18c93024b58a86c1e3db60f550221801026853170b4cb0248d3a95329,"HMAC_SHA2_256_128 [RFC4868]"
+cd7ae76304b277e2,74f6080ed799d463,daa0a85a81e6adda7b8c568f1c4cfaa6e9f9edb242e9895f012caaa642eacf4d004903,e02281ba4bb8ed20321faff956b95ce7f841b3039984dad4ed4625e77743fce4a04f32,"AES-CCM-256 with 16 octet ICV [RFC5282]",,,"NONE [RFC4306]"
+5d48bfeeb7d574da,bbb73016c0503640,91b817d036d97db3ace64475cd8d1cbeab186295020211a9cf0c16cec10b92b453ecd24e,d04516586721974d970627d85f7d031433b6558c0ec6faecf9217e5445e17e7eeee6bc68,"AES-GCM-256 with 8 octet ICV [RFC5282]",,,"NONE [RFC4306]"
+0158b8fb90b7623d,13514610cea16160,647075bf167447a1c8683e8dbe4794b4cfe73799cc6bec34905441159ce13705c8dfb3a9,15c9eae6f94631d63068bf44bb69999abc07b3d15e915fd8f0ed99ad481efd75deb02a5e,"AES-GCM-256 with 16 octet ICV [RFC5282]",,,"NONE [RFC4306]"
diff --git a/test/config/ssl_keys.tmpl b/test/config/ssl_keys.tmpl
new file mode 100644
index 0000000..8daa5cb
--- /dev/null
+++ b/test/config/ssl_keys.tmpl
@@ -0,0 +1,2 @@
+"127.0.0.1","443","http","TEST_KEYS_DIRrsasnakeoil2.key",""
+"127.0.0.1","9131","http","TEST_KEYS_DIRkey.p12","WebAS"
diff --git a/test/conftest.py b/test/conftest.py
new file mode 100644
index 0000000..8f6eda5
--- /dev/null
+++ b/test/conftest.py
@@ -0,0 +1,33 @@
+#
+# Wireshark tests
+#
+# Copyright (c) 2018 Peter Wu <peter@lekensteyn.nl>
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+'''pytest configuration'''
+
+
+def pytest_addoption(parser):
+ parser.addoption('--disable-capture', action='store_true',
+ help='Disable capture tests'
+ )
+ parser.addoption('--disable-gui', action='store_true',
+ help='Disable GUI tests'
+ )
+ parser.addoption('--build-type', default='RelWithDebInfo',
+ help='CMake build type for multi-config generators.'
+ )
+ parser.addoption('--program-path',
+ help='Path to Wireshark executables.'
+ )
+ parser.addoption('--skip-missing-programs',
+ help='Skip tests that lack programs from this list instead of failing'
+ ' them. Use "all" to ignore all missing programs.'
+ )
+ parser.addoption('--enable-release', action='store_true',
+ help='Enable release tests'
+ )
+
+from fixtures_ws import *
+
diff --git a/test/fixtures_ws.py b/test/fixtures_ws.py
new file mode 100644
index 0000000..ceee402
--- /dev/null
+++ b/test/fixtures_ws.py
@@ -0,0 +1,388 @@
+#
+# Wireshark tests
+#
+# Copyright (c) 2018 Peter Wu <peter@lekensteyn.nl>
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+'''Fixtures that are specific to Wireshark.'''
+
+from contextlib import contextmanager
+import os
+import re
+import subprocess
+import sys
+import tempfile
+import types
+import pytest
+
+@pytest.fixture(scope='session')
+def capture_interface(request, cmd_dumpcap):
+ '''
+ Name of capture interface. Tests will be skipped if dumpcap is not
+ available or no Loopback interface is available.
+ '''
+ disabled = request.config.getoption('--disable-capture', default=False)
+ if disabled:
+ pytest.skip('Capture tests are disabled via --disable-capture')
+ proc = subprocess.Popen((cmd_dumpcap, '-D'), stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE, universal_newlines=True)
+ outs, errs = proc.communicate()
+ if proc.returncode != 0:
+ print('"dumpcap -D" exited with %d. stderr:\n%s' %
+ (proc.returncode, errs))
+ pytest.skip('Test requires capture privileges and an interface.')
+ # Matches: "lo (Loopback)" (Linux), "lo0 (Loopback)" (macOS) or
+ # "\Device\NPF_{...} (Npcap Loopback Adapter)" (Windows)
+ print('"dumpcap -D" output:\n%s' % (outs,))
+ m = re.search(r'^(\d+)\. .*\(.*Loopback.*\)', outs, re.MULTILINE|re.IGNORECASE)
+ if not m:
+ pytest.skip('Test requires a capture interface.')
+ iface = m.group(1)
+ # Interface found, check for capture privileges (needed for Linux).
+ try:
+ subprocess.check_output((cmd_dumpcap, '-L', '-i', iface),
+ stderr=subprocess.STDOUT,
+ universal_newlines=True)
+ return iface
+ except subprocess.CalledProcessError as e:
+ print('"dumpcap -L -i %s" exited with %d. Output:\n%s' % (iface,
+ e.returncode,
+ e.output))
+ pytest.skip('Test requires capture privileges.')
+
+
+@pytest.fixture(scope='session')
+def program_path(request):
+ '''
+ Path to the Wireshark binaries as set by the --program-path option, the
+ WS_BIN_PATH environment variable or (curdir)/run.
+ '''
+ curdir_run = os.path.join(os.curdir, 'run')
+ if sys.platform == 'win32':
+ build_type = request.config.getoption('--build-type')
+ curdir_run_config = os.path.join(curdir_run, build_type)
+ if os.path.exists(curdir_run_config):
+ curdir_run = curdir_run_config
+ paths = (
+ request.config.getoption('--program-path', default=None),
+ os.environ.get('WS_BIN_PATH'),
+ curdir_run,
+ )
+ for path in paths:
+ if type(path) == str and os.path.isdir(path):
+ return path
+ raise AssertionError('Missing directory with Wireshark binaries')
+
+
+@pytest.fixture(scope='session')
+def program(program_path, request):
+ skip_if_missing = request.config.getoption('--skip-missing-programs',
+ default='')
+ skip_if_missing = skip_if_missing.split(',') if skip_if_missing else []
+ dotexe = ''
+ if sys.platform.startswith('win32'):
+ dotexe = '.exe'
+
+ def resolver(name):
+ path = os.path.abspath(os.path.join(program_path, name + dotexe))
+ if not os.access(path, os.X_OK):
+ if skip_if_missing == ['all'] or name in skip_if_missing:
+ pytest.skip('Program %s is not available' % (name,))
+ raise AssertionError('Program %s is not available' % (name,))
+ return path
+ return resolver
+
+
+@pytest.fixture(scope='session')
+def cmd_capinfos(program):
+ return program('capinfos')
+
+
+@pytest.fixture(scope='session')
+def cmd_dumpcap(program):
+ return program('dumpcap')
+
+
+@pytest.fixture(scope='session')
+def cmd_mergecap(program):
+ return program('mergecap')
+
+
+@pytest.fixture(scope='session')
+def cmd_rawshark(program):
+ return program('rawshark')
+
+
+@pytest.fixture(scope='session')
+def cmd_tshark(program):
+ return program('tshark')
+
+
+@pytest.fixture(scope='session')
+def cmd_text2pcap(program):
+ return program('text2pcap')
+
+
+@pytest.fixture(scope='session')
+def cmd_editcap(program):
+ return program('editcap')
+
+
+@pytest.fixture(scope='session')
+def cmd_wireshark(program):
+ return program('wireshark')
+
+
+@pytest.fixture(scope='session')
+def wireshark_command(cmd_wireshark):
+ # Windows can always display the GUI and macOS can if we're in a login session.
+ # On Linux, headless mode is used, see QT_QPA_PLATFORM in the 'test_env' fixture.
+ if sys.platform == 'darwin' and 'SECURITYSESSIONID' not in os.environ:
+ pytest.skip('Wireshark GUI tests require loginwindow session')
+ if sys.platform not in ('win32', 'darwin', 'linux'):
+ if 'DISPLAY' not in os.environ:
+ pytest.skip('Wireshark GUI tests require DISPLAY')
+ return (cmd_wireshark, '-ogui.update.enabled:FALSE')
+
+
+@pytest.fixture(scope='session')
+def cmd_extcap(program):
+ def extcap_name(name):
+ if sys.platform == 'darwin':
+ return program(os.path.join('Wireshark.app/Contents/MacOS/extcap', name))
+ else:
+ return program(os.path.join('extcap', name))
+ return extcap_name
+
+
+@pytest.fixture(scope='session')
+def features(cmd_tshark, make_env):
+ '''Returns an object describing available features in tshark.'''
+ try:
+ tshark_v = subprocess.check_output(
+ (cmd_tshark, '--version'),
+ stderr=subprocess.PIPE,
+ universal_newlines=True,
+ env=make_env()
+ )
+ tshark_v = re.sub(r'\s+', ' ', tshark_v)
+ except subprocess.CalledProcessError as ex:
+ print('Failed to detect tshark features: %s' % (ex,))
+ tshark_v = ''
+ gcry_m = re.search(r'with +Gcrypt +([0-9]+)\.([0-9]+)', tshark_v)
+ gcry_ver = (int(gcry_m.group(1)),int(gcry_m.group(2)))
+ return types.SimpleNamespace(
+ have_x64='Compiled (64-bit)' in tshark_v,
+ have_lua='with Lua' in tshark_v,
+ have_lua_unicode='(with UfW patches)' in tshark_v,
+ have_nghttp2='with nghttp2' in tshark_v,
+ have_nghttp3='with nghttp3' in tshark_v,
+ have_kerberos='with Kerberos' in tshark_v,
+ have_gnutls='with GnuTLS' in tshark_v,
+ have_pkcs11='and PKCS #11 support' in tshark_v,
+ have_brotli='with brotli' in tshark_v,
+ have_plugins='binary plugins supported' in tshark_v,
+ )
+
+
+@pytest.fixture(scope='session')
+def dirs():
+ '''Returns fixed directories containing test input.'''
+ this_dir = os.path.dirname(__file__)
+ return types.SimpleNamespace(
+ baseline_dir=os.path.join(this_dir, 'baseline'),
+ capture_dir=os.path.join(this_dir, 'captures'),
+ config_dir=os.path.join(this_dir, 'config'),
+ key_dir=os.path.join(this_dir, 'keys'),
+ lua_dir=os.path.join(this_dir, 'lua'),
+ protobuf_lang_files_dir=os.path.join(this_dir, 'protobuf_lang_files'),
+ tools_dir=os.path.join(this_dir, '..', 'tools'),
+ )
+
+
+@pytest.fixture(scope='session')
+def capture_file(dirs):
+ '''Returns the path to a capture file.'''
+ def resolver(filename):
+ return os.path.join(dirs.capture_dir, filename)
+ return resolver
+
+@pytest.fixture
+def result_file(tmp_path):
+ '''Returns the path to a temporary file.'''
+ def result_file_real(filename):
+ return str(tmp_path / filename)
+ return result_file_real
+
+@pytest.fixture
+def home_path():
+ '''Per-test home directory, removed when finished.'''
+ with tempfile.TemporaryDirectory(prefix='wireshark-tests-home-') as dirname:
+ yield dirname
+
+
+@pytest.fixture
+def conf_path(home_path):
+ '''Path to the Wireshark configuration directory.'''
+ if sys.platform.startswith('win32'):
+ conf_path = os.path.join(home_path, 'Wireshark')
+ else:
+ conf_path = os.path.join(home_path, '.config', 'wireshark')
+ os.makedirs(conf_path)
+ return conf_path
+
+
+@pytest.fixture(scope='session')
+def make_env():
+ """A factory for a modified environment to ensure reproducible tests."""
+ def make_env_real(home=None):
+ env = os.environ.copy()
+ env['TZ'] = 'UTC'
+ home_env = 'APPDATA' if sys.platform.startswith('win32') else 'HOME'
+ if home:
+ env[home_env] = home
+ else:
+ # This directory is supposed not to be written and is used by
+ # "readonly" tests that do not read any other preferences.
+ env[home_env] = "/wireshark-tests-unused"
+ # XDG_CONFIG_HOME takes precedence over HOME, which we don't want.
+ try:
+ del env['XDG_CONFIG_HOME']
+ except KeyError:
+ pass
+ return env
+ return make_env_real
+
+
+@pytest.fixture
+def base_env(home_path, make_env, request):
+ """A modified environment to ensure reproducible tests. Tests can modify
+ this environment as they see fit."""
+ env = make_env(home=home_path)
+
+ return env
+
+
+@pytest.fixture
+def test_env(base_env, conf_path, request, dirs):
+ '''A process environment with a populated configuration directory.'''
+ # Populate our UAT files
+ uat_files = [
+ '80211_keys',
+ 'dtlsdecrypttablefile',
+ 'esp_sa',
+ 'ssl_keys',
+ 'c1222_decryption_table',
+ 'ikev1_decryption_table',
+ 'ikev2_decryption_table',
+ ]
+ # uat.c replaces backslashes...
+ key_dir_path = os.path.join(dirs.key_dir, '').replace('\\', '\\x5c')
+ for uat in uat_files:
+ template_file = os.path.join(dirs.config_dir, uat + '.tmpl')
+ out_file = os.path.join(conf_path, uat)
+ with open(template_file, 'r') as f:
+ template_contents = f.read()
+ cf_contents = template_contents.replace('TEST_KEYS_DIR', key_dir_path)
+ with open(out_file, 'w') as f:
+ f.write(cf_contents)
+
+ env = base_env
+ env['WIRESHARK_RUN_FROM_BUILD_DIRECTORY'] = '1'
+ env['WIRESHARK_QUIT_AFTER_CAPTURE'] = '1'
+
+ # Allow GUI tests to be run without opening windows nor requiring a Xserver.
+ # Set envvar QT_DEBUG_BACKINGSTORE=1 to save the window contents to a file
+ # in the current directory, output0000.png, output0001.png, etc. Note that
+ # this will overwrite existing files.
+ if sys.platform == 'linux':
+ # This option was verified working on Arch Linux with Qt 5.12.0-2 and
+ # Ubuntu 16.04 with libqt5gui5 5.5.1+dfsg-16ubuntu7.5. On macOS and
+ # Windows it unfortunately crashes (Qt 5.12.0).
+ env['QT_QPA_PLATFORM'] = 'minimal'
+
+ return env
+
+
+@pytest.fixture
+def test_env_80211_user_tk(base_env, conf_path, request, dirs):
+ '''A process environment with a populated configuration directory.'''
+ # Populate our UAT files
+ uat_files = [
+ '80211_keys',
+ ]
+ # uat.c replaces backslashes...
+ key_dir_path = os.path.join(dirs.key_dir, '').replace('\\', '\\x5c')
+ for uat in uat_files:
+ template_file = os.path.join(dirs.config_dir, uat + '.user_tk_tmpl')
+ out_file = os.path.join(conf_path, uat)
+ with open(template_file, 'r') as f:
+ template_contents = f.read()
+ cf_contents = template_contents.replace('TEST_KEYS_DIR', key_dir_path)
+ with open(out_file, 'w') as f:
+ f.write(cf_contents)
+
+ env = base_env
+ env['WIRESHARK_RUN_FROM_BUILD_DIRECTORY'] = '1'
+ env['WIRESHARK_QUIT_AFTER_CAPTURE'] = '1'
+
+ # Allow GUI tests to be run without opening windows nor requiring a Xserver.
+ # Set envvar QT_DEBUG_BACKINGSTORE=1 to save the window contents to a file
+ # in the current directory, output0000.png, output0001.png, etc. Note that
+ # this will overwrite existing files.
+ if sys.platform == 'linux':
+ # This option was verified working on Arch Linux with Qt 5.12.0-2 and
+ # Ubuntu 16.04 with libqt5gui5 5.5.1+dfsg-16ubuntu7.5. On macOS and
+ # Windows it unfortunately crashes (Qt 5.12.0).
+ env['QT_QPA_PLATFORM'] = 'minimal'
+
+ return env
+
+@pytest.fixture
+def unicode_env(home_path, make_env):
+ '''A Wireshark configuration directory with Unicode in its path.'''
+ home_env = 'APPDATA' if sys.platform.startswith('win32') else 'HOME'
+ uni_home = os.path.join(home_path, 'unicode-Ф-€-中-testcases')
+ env = make_env(home=uni_home)
+ if sys.platform == 'win32':
+ pluginsdir = os.path.join(uni_home, 'Wireshark', 'plugins')
+ else:
+ pluginsdir = os.path.join(uni_home, '.local/lib/wireshark/plugins')
+ os.makedirs(pluginsdir)
+ return types.SimpleNamespace(
+ path=lambda *args: os.path.join(uni_home, *args),
+ env=env,
+ pluginsdir=pluginsdir
+ )
+
+
+@pytest.fixture(scope='session')
+def make_screenshot():
+ '''Creates a screenshot and save it to a file. Intended for CI purposes.'''
+ def make_screenshot_real(filename):
+ try:
+ if sys.platform == 'darwin':
+ subprocess.check_call(['screencapture', filename])
+ else:
+ print("Creating a screenshot on this platform is not supported")
+ return
+ size = os.path.getsize(filename)
+ print("Created screenshot %s (%d bytes)" % (filename, size))
+ except (subprocess.CalledProcessError, OSError) as e:
+ print("Failed to take screenshot:", e)
+ return make_screenshot_real
+
+
+@pytest.fixture
+def make_screenshot_on_error(request, make_screenshot, result_file):
+ '''Writes a screenshot when a process times out.'''
+ @contextmanager
+ def make_screenshot_on_error_real():
+ try:
+ yield
+ except subprocess.TimeoutExpired:
+ filename = result_file('screenshot.png')
+ make_screenshot(filename)
+ raise
+ return make_screenshot_on_error_real
diff --git a/test/hosts.custom b/test/hosts.custom
new file mode 100644
index 0000000..d799713
--- /dev/null
+++ b/test/hosts.custom
@@ -0,0 +1,4 @@
+# Custom profile hosts
+# Matches addresses in dns+icmp.pcapng.gz
+4.2.2.2 custom-4-2-2-2
+174.137.42.65 custom-174-137-42-65
diff --git a/test/hosts.global b/test/hosts.global
new file mode 100644
index 0000000..9f1b75c
--- /dev/null
+++ b/test/hosts.global
@@ -0,0 +1,4 @@
+# Global hosts
+# Matches addresses in dns+icmp.pcapng.gz
+8.8.8.8 global-8-8-8-8
+8.8.4.4 global-8-8-4-4
diff --git a/test/hosts.personal b/test/hosts.personal
new file mode 100644
index 0000000..4a7901b
--- /dev/null
+++ b/test/hosts.personal
@@ -0,0 +1,4 @@
+# Default profile / personal hosts
+# Matches addresses in dns+icmp.pcapng.gz
+8.8.4.4 personal-8-8-4-4
+4.2.2.2 personal-4-2-2-2
diff --git a/test/keys/dhe1_keylog.dat b/test/keys/dhe1_keylog.dat
new file mode 100644
index 0000000..98819f3
--- /dev/null
+++ b/test/keys/dhe1_keylog.dat
@@ -0,0 +1,2 @@
+# the client's random number and the master secret for test/captures/dhe1.pcapng.gz
+CLIENT_RANDOM 531f88d114fcf9ce9729b5458f73e1807324459029ee4bea43f8ee4ce06c77c0 3CC9E5068E674393C10E540430F60AB794C028B277CAD9C708758400B803AD4FC81D6796AFD14D8952F7CD9E4268B4DB
diff --git a/test/keys/http2-data-reassembly.keys b/test/keys/http2-data-reassembly.keys
new file mode 100644
index 0000000..8a3e2fb
--- /dev/null
+++ b/test/keys/http2-data-reassembly.keys
@@ -0,0 +1 @@
+CLIENT_RANDOM 59b4b71f50e71bff50f88388679c0156714d158bf10edd29f1d45fb4fffb3010 750fc27332a9cc17802defc48bd5693c9278d68680ae64d9dffa1e638ebd17a7902ad69501c413571b7c63dc23a0918b \ No newline at end of file
diff --git a/test/keys/key.p12 b/test/keys/key.p12
new file mode 100644
index 0000000..43a66dd
--- /dev/null
+++ b/test/keys/key.p12
Binary files differ
diff --git a/test/keys/knx_keyring.xml b/test/keys/knx_keyring.xml
new file mode 100644
index 0000000..c9b3cdd
--- /dev/null
+++ b/test/keys/knx_keyring.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- All attributes of the Keyring element are mandatory. For details on how to calculate/verify the Signature attribute, see below -->
+<!-- "oKGio6SlpqeoqaqrrK2urw==" = $ A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF -->
+<!-- "sLGys7S1tre4ubq7vL2+vw==" = $ B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF -->
+<Keyring xmlns="http://knx.org/xml/keyring/1" Project="Family Home" Created="2015-03-04T20:55:58.0160546Z" CreatedBy="ETS 5.5 Build 456" Signature="MTIzNDU2Nzg5MDEyMzQ1Ng==">
+ <!-- A backbone element is included if the project has a secure IP backbone or the keyring contains at least one IP Routing interface -->
+ <!-- The Latency and BackboneKey attributes are only included if the project has a secure IP backbone -->
+ <!-- BackboneKey := Base64( AES128-CBC( PBKDF2( HMAC-SHA256, KeyringPassword, "1.keyring.ets.knx.org", 65536, 128), MSB128(SHA256(this.Keyring.Created), Project.BackboneKey as byte[])) --> (1)
+ <Backbone MulticastAddress="224.0.23.12" Latency="1000" Key="oKGio6SlpqeoqaqrrK2urw=="/>
+ <!-- There might be 0..n interface elements. Possible types are "USB", "Tunneling" or "Routing". There is no support for "Eiblib/IP". -->
+ <!-- Depending on the type of interface different interface attributes are possible. -->
+ <!-- USB interfaces only appear in the keying if used for Data Security communication. They only have the mandatory attribute "IndividualAddress". -->
+ <Interface Type="USB" IndividualAddress="2.1.56">
+ <!-- Interfaces used for Data Security communication have a list of group addresses which will be communicated over this interface. -->
+ <!-- Each interface group has a mandatory list of trusted senders that are allowed to send telegrams to this group address. -->
+ <Group Address="3971" Senders="1.1.1 1.1.3 1.1.4"/>
+ <Group Address="3972" Senders="1.1.2 1.1.4"/>
+ <Group Address="14271" Senders="1.1.1"/>
+ </Interface>
+ <!-- Tunneling interfaces might appear in the keying because either they are used for Data Security communication or access to the interface itself is protected (or both). -->
+ <!-- KNXnet/IP Tunneling interfaces have as a mandatary attribute the KNX individual address of the host device. -->
+ <!-- If the Tunneling interface is used for Data Security communication, the attribute "IndividualAddress" is mandatory, otherwise it's optional. -->
+ <!-- Secured Tunneling interfaces need to have the "UserID" and "Password" attributes. An "Authentication" attribute containing the interface's device authenication code is optional. -->
+ <!-- Password := Base64( AES128-CBC( PBKDF2( HMAC-SHA256, KeyringPassword, "1.keyring.ets.knx.org", 65536, 128), MSB128(SHA256(this.Keyring.Created)), RandomBytes(8)+PKCS#7Padding( Device.BusAccess(IA).Password, 24) as byte[])) --> (1)
+ <!-- Authentication := Base64( AES128-CBC( PBKDF2( HMAC-SHA256, KeyringPassword, "1.keyring.ets.knx.org", 65536, 128), MSB128(SHA256(this.Keyring.Created)), RandomBytes(8)+PKCS#7Padding( Device.AthenticationCode, 24) as byte[])) --> (1)
+ <Interface Type="Tunneling" Host="2.1.0" IndividualAddress="2.1.56" UserID="3" Password="A3BzNDU2Nzg5MDEyMzQnUA3BzNDU2Nzg5MDEyMzQnUw=" Authentication="A3BzNDU2Nzg5MDEyMzQnUA3BzNDU2Nzg5MDEyMzQnUw="/>
+ <!-- If the Backbone (KNXnet/IP Routing) interface is used for Data Security communication, the attribute "IndividualAddress" is mandatory, otherwise it's optional. -->
+ <!-- If no "IndividualAddress" attribute is present, backbone interface elements shall be omitted altogether as all necessary information is part of the Backbone element already. -->
+ <Interface Type="Backbone" IndividualAddress="0.0.123">
+ <Group Address="256" Senders="1.1.1 1.1.3 1.1.4"/>
+ <Group Address="3972" Senders="1.1.2 1.1.4"/>
+ </Interface>
+ <!-- The "GroupAddresses" collection shall only be present if the keyring contains interface groups for Data Security communication. -->
+ <!-- The "Address" and "Key" attributes are mandatory for every "GroupAddresses/Group" element. -->
+ <!-- Key := Base64( AES128-CBC( PBKDF2( HMAC-SHA256, KeyringPassword, "1.keyring.ets.knx.org", 65536, 128), MSB128(SHA256(this.Keyring.Created)), GroupAddress.Key as byte[])) --> (1)
+ <GroupAddresses>
+ <Group Address="256" Key="oKGio6SlpqeoqaqrrK2urw=="/>
+ <Group Address="3971" Key="oKGio6SlpqeoqaqrrK2urw=="/>
+ <Group Address="3972" Key="oKGio6SlpqeoqaqrrK2urw=="/>
+ <Group Address="14271" Key="oKGio6SlpqeoqaqrrK2urw=="/>
+ </GroupAddresses>
+ <!-- Keyings exported for the whole project (for backup or diagnostic purposes) shall contain one "Device" entry for every secure device. -->
+ <!-- For interface keyings the "Devices" list is optional and shall only be present if sequence numbers are known for devices referenced as interface group senders. -->
+ <!-- The "ToolKey" attribute shall only be present if the keying was exported for the whole project. -->
+ <!-- The "SeqNr" attribute is optional and shall only be present if a received sequence number for a given device is known. -->
+ <!-- ToolKey := Base64( AES128-CBC( PBKDF2( HMAC-SHA256, KeyringPassword, "1.keyring.ets.knx.org", 65536, 128), MSB128(SHA256(this.Keyring.Created)), Device.ToolKey as byte[])) --> (1)
+ <!-- Secured IP-enabled devices need to have the "ManagementPassword" attribute. An "Authentication" attribute containing the device's authenication code is optional. -->
+ <!-- ManagementPassword := Base64( AES128-CBC( PBKDF2( HMAC-SHA256, KeyringPassword, "1.keyring.ets.knx.org", 65536, 128), MSB128(SHA256(this.Keyring.Created)), RandomBytes(8)+PKCS#7Padding( Device.ManagementPassword, 24) as byte[])) --> (1)
+ <!-- Authentication := Base64( AES128-CBC( PBKDF2( HMAC-SHA256, KeyringPassword, "1.keyring.ets.knx.org", 65536, 128), MSB128(SHA256(this.Keyring.Created)), RandomBytes(8)+PKCS#7Padding( Device.AthenticationCode, 24) as byte[])) --> (1)
+ <!-- The "ManagementPassword" and "Authentication" attributes shall only be present if the keying was exported for the whole project. -->
+ <Devices>
+ <Device IndividualAddress="1.1.1" ToolKey="sLGys7S1tre4ubq7vL2+vw==" SequenceNumber="45678"/>
+ <Device IndividualAddress="1.1.2" ToolKey="sLGys7S1tre4ubq7vL2+vw==" SequenceNumber="34567"/>
+ <Device IndividualAddress="1.1.3" ToolKey="sLGys7S1tre4ubq7vL2+vw==" SequenceNumber="23456"/>
+ <Device IndividualAddress="1.1.4" ToolKey="sLGys7S1tre4ubq7vL2+vw==" SequenceNumber="12345"/>
+ <Device IndividualAddress="2.1.0" ToolKey="sLGys7S1tre4ubq7vL2+vw==" SequenceNumber="1234" ManagementPassword="A3BzNDU2Nzg5MDEyMzQnUA3BzNDU2Nzg5MDEyMzQnUw=" Authentication="A3BzNDU2Nzg5MDEyMzQnUA3BzNDU2Nzg5MDEyMzQnUw="/>
+ </Devices>
+</Keyring> \ No newline at end of file
diff --git a/test/keys/krb-816.keytab b/test/keys/krb-816.keytab
new file mode 100644
index 0000000..aa0b8f1
--- /dev/null
+++ b/test/keys/krb-816.keytab
Binary files differ
diff --git a/test/keys/rsa-p-lt-q.key b/test/keys/rsa-p-lt-q.key
new file mode 100644
index 0000000..8fdfa85
--- /dev/null
+++ b/test/keys/rsa-p-lt-q.key
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXAIBAAKBgQDhP+fIyQK5qfcXejONrbSZPrW8xuVOO6R34/cBVTKDz1D+f/TH
+5fTxgZuxnZxsVv//cEK6YRI66q8MmrLejgTucm9Q6LSwJfBXnDenicUkzDz0tdPA
+Ng+dpF3WaIRYJ/Dow4Yt8EdPS+NrwtcO4TyThyCLsz28sC2roagex/zmswIDAQAB
+AoGBAIBJ82tKAQFZql92vBH/UxLMwpln9oXeNkgKUE40BfdIhmrM2c9YS2+ZT+GT
+UNnrmxYTDA425zkjHNvi5+kVnfxW9TsvJmJF2MDx/mtjx8GtMkOiFrqkYGdPRIoz
+vg8UZZPKZ+YQY+2AvIFPYSHHvzwJMc+dBUushD091O8aLifRAkEA5tgQKCWrCQTi
+qWEx2zzeH4WfZuQ1eufp77vWYG8XkRX6wW2Eb2vSl/avxNN7Ej5KBBB1dtSmAjGM
+9oNBYqaqlwJBAPnLxjc9rqc8F/E8uVikd9tzRxFUjHqxG2wEIpGpnAmJ0eb3Xids
+NzjGDVkzkKzRPfkZN4H2jAscKhEdZXlv9EUCQCMzy7Lzm5NyhUYjJkEylQTlkZtV
+LbqiZxBB6r0l88gSO/0HQGzlWmYGHmO7hEcR7KOWBvOqFe67s61b8rqig90CQB+H
+aO1wC6twGlWIpJxbpgU896toUJLr59oqa3KXRequSqAseOXg8tdnqCeqKoiloHzg
+gfEVfXephmXCoBxD1UECQCxYuPVRq6sl+UgnH0unPl4F/1biRkunhcYaVx6JQBtz
+tRZm0DCietfGzhrfI2IxmEdGzOSoHbA18HLtOqzgwSA=
+-----END RSA PRIVATE KEY-----
diff --git a/test/keys/rsa-p-lt-q.p8 b/test/keys/rsa-p-lt-q.p8
new file mode 100644
index 0000000..d9fca7e
--- /dev/null
+++ b/test/keys/rsa-p-lt-q.p8
@@ -0,0 +1,16 @@
+-----BEGIN PRIVATE KEY-----
+MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAOE/58jJArmp9xd6
+M42ttJk+tbzG5U47pHfj9wFVMoPPUP5/9Mfl9PGBm7GdnGxW//9wQrphEjrqrwya
+st6OBO5yb1DotLAl8FecN6eJxSTMPPS108A2D52kXdZohFgn8OjDhi3wR09L42vC
+1w7hPJOHIIuzPbywLauhqB7H/OazAgMBAAECgYEAgEnza0oBAVmqX3a8Ef9TEszC
+mWf2hd42SApQTjQF90iGaszZz1hLb5lP4ZNQ2eubFhMMDjbnOSMc2+Ln6RWd/Fb1
+Oy8mYkXYwPH+a2PHwa0yQ6IWuqRgZ09EijO+DxRlk8pn5hBj7YC8gU9hIce/PAkx
+z50FS6yEPT3U7xouJ9ECQQDm2BAoJasJBOKpYTHbPN4fhZ9m5DV65+nvu9ZgbxeR
+FfrBbYRva9KX9q/E03sSPkoEEHV21KYCMYz2g0FipqqXAkEA+cvGNz2upzwX8Ty5
+WKR323NHEVSMerEbbAQikamcCYnR5vdeJ2w3OMYNWTOQrNE9+Rk3gfaMCxwqER1l
+eW/0RQJAIzPLsvObk3KFRiMmQTKVBOWRm1UtuqJnEEHqvSXzyBI7/QdAbOVaZgYe
+Y7uERxHso5YG86oV7ruzrVvyuqKD3QJAH4do7XALq3AaVYiknFumBTz3q2hQkuvn
+2iprcpdF6q5KoCx45eDy12eoJ6oqiKWgfOCB8RV9d6mGZcKgHEPVQQJALFi49VGr
+qyX5SCcfS6c+XgX/VuJGS6eFxhpXHolAG3O1FmbQMKJ618bOGt8jYjGYR0bM5Kgd
+sDXwcu06rODBIA==
+-----END PRIVATE KEY-----
diff --git a/test/keys/rsasnakeoil2.key b/test/keys/rsasnakeoil2.key
new file mode 100644
index 0000000..2602273
--- /dev/null
+++ b/test/keys/rsasnakeoil2.key
@@ -0,0 +1,19 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICWwIBAAKBgQCkblMUCt4s42BVmvJCpq9HEi8Xzvq63E5jVjS5unNLeEQ9xmxp
+pCWzYQKdCQQ/cj3YJ9OwWkV3tzbkJiPMEriu3qe2OoI8fCRZCviWQ4ujKTY/kX9d
+xyOUKX8Kzgq9jZsvGReq1Y7sZqI36z9XUzzyqrt5GUuQfqejmf6ETInwPQIDAQAB
+AoGAedqEWKsBIPTTtDziYYBTDnEsUxGA/685rCX7ZtQEkx4qPDlqqBMMGVW/8Q34
+hugrap+BIgSTzHcLB6I4DwiksUpR08x0hf0oxqqjMo0KykhZDfUUfxR85JHUrFZM
+GznurVhfSBXX4Il9Tgc/RPzD32FZ6gaz9sFumJh0LKKadeECQQDWOfP6+nIAvmyH
+aRINErBSlK+xv2mZ4jEKvROIQmrpyNyoOStYLG/DRPlEzAIA6oQnowGgS6gwaibg
+g7yVTgBpAkEAxH6dcwhIDRTILvtUdKSWB6vdhtXFGdebaU4cuUOW2kWwPpyIj4XN
+D+rezwfptmeOr34DCA/QKCI/BWkbFDG2tQJAVAH971nvAuOp46AMeBvwETJFg8qw
+Oqw81x02X6TMEEm4Xi+tE7K5UTXnGld2Ia3VjUWbCaUhm3rFLB39Af/IoQJAUn/G
+o5GKjtN26SLk5sRjqXzjWcVPJ/Z6bdA6Bx71q1cvFFqsi3XmDxTRz6LG4arBIbWK
+mEvrXa5jP2ZN1EC7MQJAYTfwPZ8/4x/USmA4vx9FKdADdDoZnA9ZSwezWaqa44My
+bJ0SY/WmNU+Z4ldVIkcevwwwcxqLF399hjrXWhzlBQ==
+-----END RSA PRIVATE KEY-----
+
+
+
+
diff --git a/test/keys/snakeoil-rsa.key b/test/keys/snakeoil-rsa.key
new file mode 100644
index 0000000..49ec507
--- /dev/null
+++ b/test/keys/snakeoil-rsa.key
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICWwIBAAKBgQCkblMUCt4s42BVmvJCpq9HEi8Xzvq63E5jVjS5unNLeEQ9xmxp
+pCWzYQKdCQQ/cj3YJ9OwWkV3tzbkJiPMEriu3qe2OoI8fCRZCviWQ4ujKTY/kX9d
+xyOUKX8Kzgq9jZsvGReq1Y7sZqI36z9XUzzyqrt5GUuQfqejmf6ETInwPQIDAQAB
+AoGAedqEWKsBIPTTtDziYYBTDnEsUxGA/685rCX7ZtQEkx4qPDlqqBMMGVW/8Q34
+hugrap+BIgSTzHcLB6I4DwiksUpR08x0hf0oxqqjMo0KykhZDfUUfxR85JHUrFZM
+GznurVhfSBXX4Il9Tgc/RPzD32FZ6gaz9sFumJh0LKKadeECQQDWOfP6+nIAvmyH
+aRINErBSlK+xv2mZ4jEKvROIQmrpyNyoOStYLG/DRPlEzAIA6oQnowGgS6gwaibg
+g7yVTgBpAkEAxH6dcwhIDRTILvtUdKSWB6vdhtXFGdebaU4cuUOW2kWwPpyIj4XN
+D+rezwfptmeOr34DCA/QKCI/BWkbFDG2tQJAVAH971nvAuOp46AMeBvwETJFg8qw
+Oqw81x02X6TMEEm4Xi+tE7K5UTXnGld2Ia3VjUWbCaUhm3rFLB39Af/IoQJAUn/G
+o5GKjtN26SLk5sRjqXzjWcVPJ/Z6bdA6Bx71q1cvFFqsi3XmDxTRz6LG4arBIbWK
+mEvrXa5jP2ZN1EC7MQJAYTfwPZ8/4x/USmA4vx9FKdADdDoZnA9ZSwezWaqa44My
+bJ0SY/WmNU+Z4ldVIkcevwwwcxqLF399hjrXWhzlBQ==
+-----END RSA PRIVATE KEY-----
diff --git a/test/keys/tls-over-tls.key b/test/keys/tls-over-tls.key
new file mode 100644
index 0000000..adae9e8
--- /dev/null
+++ b/test/keys/tls-over-tls.key
@@ -0,0 +1,27 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIEogIBAAKCAQEAuIpwxT/BdUhlQNwpm11Wrz6oEV5glXDeFBSp3CUd7SX+2O1z
+iXxe6cW0p2ljPQjnvVFe/NTkDcGhgaXVH+8/sk1N1s101L98ZxjLaMaMKhj+yf6Q
+IOvN1weaMt9qnl8JRrk5f6aNGWTr+dqLU2veE6zX1oT/lFtDTafopyAFGpEJnDRz
+NudARQpt1CnakRu2zkKsQ/ApMjRa9Zm4w2Kf+M23IeU+j/gi5wAU9/D39ke67tKO
+GDNXYfMfFcgsmDrH+a2xrU3Zf9KtXiPI5lw2dXs3UG19Sr+HRoydCrIPPJBMzUKw
+9M3Hc7i0aMO2vn94GJKC6mqpOeeQIWo5Y4GMnwIDAQABAoIBAE6eLAzcbH6aqQhI
+wzD9QsDF4LQFkQAZZYMIipTO+0DcvwWLo30fDxBoud3Yd/64nIF6+QydZcq2gyfI
+jlNcibZcWJz6SpuYOFdzqLSqYWxN2b4URTLBQqApDPg/VhzCQCFxJ53KRrJa3G1F
+PbX8bk/TguBRKND7UGD095i7e3ElPmmTEe4RSpzqYqCsUQ9e1ndhAVRKYSMXHdbY
+N4wtM+P2bIJghWQjyo2P4Jb7gXF47ghBett4krduvxv96m3dIuGAM0Y7V9okclQT
+7cv/8iDUXBhbWzIsB3bXG0lm9QttQ3T0doSlqg0q2DgLiYBHEhK9SiUAVt+dQftP
+kRYa+gECgYEA9c1tNXM2u4SOgMUyiFkU8Slr7MbywMq2jnalbea63Q8JxNWNW9VX
+wQOBZvKK/BEGEP9kOeS1aMQ5dUZOxzI6SN9eCfEDFzIA5VknaZcJdBW+X3kyy/wF
+7awEWcZ6B9HrtRZ+vA2rQ4LyfJd2M1eGuK6+0WJa0hGjglbFBrHJNLECgYEAwDJg
+ls3IWhPEQXWFwXXv1SM63hnbUpGFICLqLz26+qZ64hR52ZcvuZsS3l7slO+5YSim
+fQNXXYZiwQm+SdtL5V5anUKvtaMWw9VlZVNOFPYyh1RyaGr5CO0j5aoC76DnD2ee
+cRQPv8Uodf50FR5Y9uyxijGOfA3SMj4WfLppak8CgYAnDg3VGUpP/x7ZTPvbeDQA
+oXE5fN7jTRI2jpl4XdnA9/u4X6oHNl2sGE9+OPlmVZoeJ0YYgMNmMw9iF9q6gbuL
+CpqZf8ba76H+zuyZNVtWK4JFDy/IA3I5skQ6s3N+PJdz/XADlzRoFK1MqJAqVjTc
+sT82a6c8i3rsYbcKekMa8QKBgBPjorc4aGlZ7k9P2B2jFMSbtrXROy8aPAqNUmq9
+GqJhpAnNUKbBzICKmbNFY3ouLKLvT/tT4zCcfY+4cGa4OOxtjTcE9aX4UJzHcoy/
+yC7HI4d5p7VCjK7ty28y3sbpgb/IW08cYlzYDE3ZnS2qTE6RQ/YnFrWjwILOhgk2
+ST5lAoGAIYpxE2Qcz9nHHfx2Fn70Ysg8XUoyk61QBSeZEmVBPp1XhVEz7idJc4PZ
+tozarw3mo9aqY/sX2f+mwLhL0E5wxy6kcVICkYhE9y45bESAeWk9YIlOMUzTW84C
+bVJUJiciDPuTJks6XVrRzc0GayaxpblhKHeR7w+6V22+lS7wgTg=
+-----END RSA PRIVATE KEY-----
diff --git a/test/keys/tls12-chacha20poly1305.keys b/test/keys/tls12-chacha20poly1305.keys
new file mode 100644
index 0000000..cbdbcad
--- /dev/null
+++ b/test/keys/tls12-chacha20poly1305.keys
@@ -0,0 +1,9 @@
+# SSL/TLS secrets log file, generated by OpenSSL
+CLIENT_RANDOM ab85fc5f6db67c9cf825cd8a1f34cf6c5e89ec09656a4944ec5536a36aed5728 aee53fb112659bc2b3801db7d230c25100dfb79ffb00f086256885dfc1f8fc545e526fc92fe884348a84964bb7b4bbc0
+CLIENT_RANDOM 59ee68728af906f3fb825bf306f7f40ae1fb68fc13d769a00843d3f2ba5dac45 7b126413d8876316b67a35fc3d530bca6574070a5a4b829868b3f94c36b0b1f673ab0a18f213ee7a5fb47ba50676acb3
+CLIENT_RANDOM 1c73db9d11c0c4fb8acf3f62ec0a50a1559eeaded9a9a54e829f9f291d592069 f38dbcf8c3eb3c30514fbcfe2208efab88b3593468b8695e8c1f55662ffe5ab4151ed4ead5fb207c8707ea073e8aaaba
+RSA 3e2ffbd86d10f694 0303f4d5b30f42e5df09d7d3fe363b4ff104b469bcade9e4506bf6cc621441d577cc5fbe448979fdd9be53556881058a
+CLIENT_RANDOM 121cdd2ae0f9f089b48897272e37c89cf41f4eb8262ac1272cc02a73fd23fa0b 2a169a4a71b6b988d525546ca3f56d185f16490eecc19659963f796bf8c6b4cf10f8739aeb0f2162451c1e46718c137d
+CLIENT_RANDOM db9350951d7ace9c91422b44b526a549fa3ca97b9e0d5c421c08ee3223b3d74a bd0c489a9d428510d937ae09038a0cc84bc3ed72dfe136712d94fcc001d46f54b33992dd3f2f897625cad07f7f9caeb2
+CLIENT_RANDOM 55a097b1bf4bf3c0f7161a81f15be86a78f2ca05f9784b07e0af761c1f9f1d65 bdf97a381c55c50421757a63227717b6a5c840ef8fc61975e0c495cff55f4dde41080b63c896f94639351697810bfcd9
+CLIENT_RANDOM f6fb54f9367adab3128073734d14e7230e6fe3c5c494caa15e130b5a955dbcb6 8409faa69c4c6ebf56cbad7b9ace4f41815de444410c2c595f409b54a80014e0acf944b8ee6f62eb0ef873714514a19f
diff --git a/test/keys/tls12-dsb-1.keys b/test/keys/tls12-dsb-1.keys
new file mode 100644
index 0000000..e6d535e
--- /dev/null
+++ b/test/keys/tls12-dsb-1.keys
@@ -0,0 +1,2 @@
+# first
+CLIENT_RANDOM f67a28b386b31c620d76c0026fdd9888edbe6bf0f5b715b2caca158f84ae9d66 cc38e78182b9dfd74ef3103d79bbc99cfc9b4dad209ed209062b5481e63353128da7571b13cfd4d3a5ae7d0520fb346d
diff --git a/test/keys/tls12-dsb-2.keys b/test/keys/tls12-dsb-2.keys
new file mode 100644
index 0000000..d32fd4a
--- /dev/null
+++ b/test/keys/tls12-dsb-2.keys
@@ -0,0 +1 @@
+CLIENT_RANDOM 1e0d63b41d7c7bb639559cfc9f06ffd5c65fe4a9df31abc5af833b0d834436f4 c7f5dda54fb417181cb26e52112afaf9e1756addd77d3c479d96a609c0d3c9bb9929c8475cafb4dbad8f72e868a43e02
diff --git a/test/keys/tls13-20-chacha20poly1305.keys b/test/keys/tls13-20-chacha20poly1305.keys
new file mode 100644
index 0000000..e858b9a
--- /dev/null
+++ b/test/keys/tls13-20-chacha20poly1305.keys
@@ -0,0 +1,9 @@
+# SSL/TLS secrets log file, generated by OpenSSL
+SERVER_HANDSHAKE_TRAFFIC_SECRET 3d89529eeebe176375ef29bd146a49e02c375771628244948f6e9408457fdbc1 4e1fbe0594634161e1af3f8b6e940561ba3fc2174b6cefb5b0da3e040cfb23e4
+SERVER_TRAFFIC_SECRET_0 3d89529eeebe176375ef29bd146a49e02c375771628244948f6e9408457fdbc1 1e519f7e8d3e8d45d7cf13038f50a50e1a2f6f0a9f918b9ae856f3269068ef8f
+CLIENT_HANDSHAKE_TRAFFIC_SECRET 3d89529eeebe176375ef29bd146a49e02c375771628244948f6e9408457fdbc1 69c6f71caf5031fc55671bf88c70a77acac0d81de20d3cf5e6b4dd7fac4ffe4d
+CLIENT_TRAFFIC_SECRET_0 3d89529eeebe176375ef29bd146a49e02c375771628244948f6e9408457fdbc1 292497c163345eaa5b16b0b7907faa31dbb7ce76ac9a1c35ca99ba9dafeae72c
+SERVER_HANDSHAKE_TRAFFIC_SECRET 4cfd639e633b0ccdedab543dd333639d2bacd023b2f12a94724eb1c20f5b8e11 976e0b13800a6c2335ed9bb124f38fb2f654c6585dfc14db72eb09cd30148a1c
+SERVER_TRAFFIC_SECRET_0 4cfd639e633b0ccdedab543dd333639d2bacd023b2f12a94724eb1c20f5b8e11 f7e0e34d554b2431353dadb226d9fbf248dd15e76944234bfb13b1102af43aaf
+CLIENT_HANDSHAKE_TRAFFIC_SECRET 4cfd639e633b0ccdedab543dd333639d2bacd023b2f12a94724eb1c20f5b8e11 d46820a69b572a15d2ef8778d32f1e00300725ab42bc9f3e8f6ede598a1ab532
+CLIENT_TRAFFIC_SECRET_0 4cfd639e633b0ccdedab543dd333639d2bacd023b2f12a94724eb1c20f5b8e11 69bf451e01db2af6924af9b78759a9f3e84affab23300238dbf94c54709cdda1
diff --git a/test/keys/tls13-rfc8446-noearly.keys b/test/keys/tls13-rfc8446-noearly.keys
new file mode 100644
index 0000000..15e7e4c
--- /dev/null
+++ b/test/keys/tls13-rfc8446-noearly.keys
@@ -0,0 +1,8 @@
+CLIENT_HANDSHAKE_TRAFFIC_SECRET 2635fafc16c49a3e997ef714c303806dc8dbf634a2005b0e0186521c4ad6f9df 3a497c91f6e130fbc18fc9f773b92bb0d538dfedc30e964cde0676396f24d0df
+SERVER_HANDSHAKE_TRAFFIC_SECRET 2635fafc16c49a3e997ef714c303806dc8dbf634a2005b0e0186521c4ad6f9df 1a63b313c605f90e0b3c5717ebbbc62e1da3fe8e2aa66e499409a06b89040783
+CLIENT_TRAFFIC_SECRET_0 2635fafc16c49a3e997ef714c303806dc8dbf634a2005b0e0186521c4ad6f9df 1ce3e54d6b980d838f79564fd33d43a7664df24ead913c316c379ca3dd349b74
+SERVER_TRAFFIC_SECRET_0 2635fafc16c49a3e997ef714c303806dc8dbf634a2005b0e0186521c4ad6f9df befa80156bd5cb23899c23afadd8deb87c4117323b3e184085b57c8f4dc56760
+CLIENT_HANDSHAKE_TRAFFIC_SECRET b67947da9d3e4b2ce8acffa975e30aa7ef90f7ec0d39de78db392f38b9a9a41d f4b31725da386891edbf521b96547be8b166487ca56ac197ac8df728c303ee80
+SERVER_HANDSHAKE_TRAFFIC_SECRET b67947da9d3e4b2ce8acffa975e30aa7ef90f7ec0d39de78db392f38b9a9a41d ee3a6c64336e7f22214ab8f4b1aba29b0e7c72c84890a240d5c0c451ffceee9a
+CLIENT_TRAFFIC_SECRET_0 b67947da9d3e4b2ce8acffa975e30aa7ef90f7ec0d39de78db392f38b9a9a41d ceca66e29c1452990be5d1a439805adb9e582931051e847d8ad676147fd63b13
+SERVER_TRAFFIC_SECRET_0 b67947da9d3e4b2ce8acffa975e30aa7ef90f7ec0d39de78db392f38b9a9a41d bf428b9e2e4853bab9c442f23d0dc45a9d552ab31ec96c7b9633ed16694924d0
diff --git a/test/keys/tls13-rfc8446.keys b/test/keys/tls13-rfc8446.keys
new file mode 100644
index 0000000..9195e25
--- /dev/null
+++ b/test/keys/tls13-rfc8446.keys
@@ -0,0 +1,9 @@
+CLIENT_HANDSHAKE_TRAFFIC_SECRET 2635fafc16c49a3e997ef714c303806dc8dbf634a2005b0e0186521c4ad6f9df 3a497c91f6e130fbc18fc9f773b92bb0d538dfedc30e964cde0676396f24d0df
+SERVER_HANDSHAKE_TRAFFIC_SECRET 2635fafc16c49a3e997ef714c303806dc8dbf634a2005b0e0186521c4ad6f9df 1a63b313c605f90e0b3c5717ebbbc62e1da3fe8e2aa66e499409a06b89040783
+CLIENT_TRAFFIC_SECRET_0 2635fafc16c49a3e997ef714c303806dc8dbf634a2005b0e0186521c4ad6f9df 1ce3e54d6b980d838f79564fd33d43a7664df24ead913c316c379ca3dd349b74
+SERVER_TRAFFIC_SECRET_0 2635fafc16c49a3e997ef714c303806dc8dbf634a2005b0e0186521c4ad6f9df befa80156bd5cb23899c23afadd8deb87c4117323b3e184085b57c8f4dc56760
+CLIENT_EARLY_TRAFFIC_SECRET b67947da9d3e4b2ce8acffa975e30aa7ef90f7ec0d39de78db392f38b9a9a41d 42c0101fad261571cb8799c86a1eb4afe6dcef4a5f88664ac63e4c77452a77ef
+CLIENT_HANDSHAKE_TRAFFIC_SECRET b67947da9d3e4b2ce8acffa975e30aa7ef90f7ec0d39de78db392f38b9a9a41d f4b31725da386891edbf521b96547be8b166487ca56ac197ac8df728c303ee80
+SERVER_HANDSHAKE_TRAFFIC_SECRET b67947da9d3e4b2ce8acffa975e30aa7ef90f7ec0d39de78db392f38b9a9a41d ee3a6c64336e7f22214ab8f4b1aba29b0e7c72c84890a240d5c0c451ffceee9a
+CLIENT_TRAFFIC_SECRET_0 b67947da9d3e4b2ce8acffa975e30aa7ef90f7ec0d39de78db392f38b9a9a41d ceca66e29c1452990be5d1a439805adb9e582931051e847d8ad676147fd63b13
+SERVER_TRAFFIC_SECRET_0 b67947da9d3e4b2ce8acffa975e30aa7ef90f7ec0d39de78db392f38b9a9a41d bf428b9e2e4853bab9c442f23d0dc45a9d552ab31ec96c7b9633ed16694924d0
diff --git a/test/keys/udt-dtls.key b/test/keys/udt-dtls.key
new file mode 100644
index 0000000..b4db542
--- /dev/null
+++ b/test/keys/udt-dtls.key
@@ -0,0 +1,28 @@
+-----BEGIN PRIVATE KEY-----
+MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDg/jR4vVFNpBta
+s1uv4IXhqpePHIIuOoOnb2sK6G8gXdCxKm406JLecNuIqq0eUydH6oawar96vYGM
+lLKTfvMnN3RgL3wNG6G+x+zYPjMaS6G+X8SPAG/XtlIxzDFdzliJT4WpKw1nNa7V
+sryYTYLZ4aX0hbbETuvXhdZTqCUYX8t2TYyTjeTBybWLyXcWZReGShHPlThn55b0
+rtUXkUQoTl3iv330jvJX8MN8haRLwr4+s2jYIGhsINdvMziGo89Eh1FYJXeC4en0
+FTEzIx9XA8FiVA8Rf1EEJv6syIvE11GGBCrX0Hu1brg7n6Y0TvkQGcL8tbrFDKlE
+W7HlQk9TAgMBAAECggEBALfNk8orRxc5gItJSRbWQilH9saYEJV5ggIv2G+x0M7N
+NWb2dc/NS+ZipkXwwLqsTcPHiT7oBgNce1AATh6GsFeSSwUk5Z/DuhAkPY2uyoqp
+zLm8fNQiFDxSGrXJzW6H2vZZu6Smoi11wp2bhcyaTdJ3L98huVyH6M1J7fyruZo9
+Z8GssyCSujfze7cIc7Py1wEEC0+LzGijkc70HDgsxwj6TXKx/ifXi+5tCCesA3z1
+6AIe0zMTcwpXRbdEZGO9fvBxGoK9hwiVJ6/sPmxFXWpBfu6TMLJjLcwwvnoNuWdH
+83+PfD+sdE7zo6CUEGDHPvJ/Fyq5lIGjEP0ErruGKPkCgYEA9PyUvC3nHkGN+5t9
+JNDaNZWEaiBW7w5yQN5MnQCwK3I8ajE+EDoPl0YDhJW4HFS/iwu8WZeb4pN0WLkT
+C2K7Tfb+9G7ebLoQ997l0zEcnHqiKA1CqGpu/g0mcVlGFExthvgv9rmt+1QLTXHJ
+F/YirWCTyyIgw11iY2ap8u7kSK0CgYEA6xuHHFTtH2pjRXxuOBhwsvA+8oNd/4A8
+phNfJgS7t9LYDMqzyer0ixdkae1vH4peZ1USn6326Vx53hYulH0A69iNM7zrNWM7
+JI+y0ftyb/Ji7CwpQAd3JVjUjvoQljrpPqrArm8fGhTucqS8E1fY1u98b3N85RrY
+u3uAcwGb9/8CgYAys08ovqs1FMYIiz5T7zEpo77ao8S6BphYmmjqmSjcZPDh3T0F
+6K4vVVsHBmEq49McOJqLRBgLxQ5wCiVJ1u4CjZpoBcXcZIl8ctHHakOMksiaV1wz
+NIux4hDRpnMdYk/MffKXMggymksYhPLkFZlJnlIX2QFEzT++aJHFZ/EwpQKBgCs8
+QLiBFao1UlQw8cP3GqKNc8X9Sof1+TFBVroTHMJNT9XqYO28+4OopZqlQ041j+7I
+wkgDIekATJj+00oTQtwcUrs0/rwup22tz2C2MPFNTcvIwz03Ij4H++7fJbW617Hi
+jNSHMt0FBGSozr1v5jyAhg2o20r2iOzRZWnA3gHZAoGAXkvhtQxD5sdypzUhs6uS
+d3PgKTbQK8PLU5KGl7DZ2oaemQ2QUQw0J9tlEQRItTxB+MDf21FddD8n6c1a3zJa
+gay7xiarE8JN0pHoQ1qCqZBwWXQRSPiNu8Bxu2oPpUi2iQdBVQG1bACQOToVUDpv
+wCW2aisjPbg71ZkZEvhk2cg=
+-----END PRIVATE KEY-----
diff --git a/test/lua/acme_file.lua b/test/lua/acme_file.lua
new file mode 100644
index 0000000..f159ba2
--- /dev/null
+++ b/test/lua/acme_file.lua
@@ -0,0 +1,1453 @@
+------------------------------------------
+-- acme_file_reader.lua
+-- Author: Hadriel Kaplan (hadrielk at yahoo dot com)
+-- version = 1.0
+-- date = 3/3/2014
+------------------------------------------
+--[[
+ This is a Wireshark Lua-based capture file reader.
+ This "capture file" reader reads message logs from Acme Packet (now Oracle) Session Border Controllers,
+ such as sipmsg.log files. There are several variants of the log file format, as well as some changes that
+ can happen based on how the log file is generated and retrieved; for example if it's generated through a
+ 'tail' command, or FTP'ed by a FTP client which adds carriage-returns. This Lua file reader tries to handle
+ such conditions.
+
+ Note: this script wasn't written to be super-efficient, nor clever. When you've been writing Lua for a while
+ you get used to writing in a different, more elegant fashion than this script is; but other people find it
+ hard to read such Lua code, so I've tried to keep this simpler.
+
+ Features:
+ -handles sipmsg type logs, sipdns type logs, algd type logs
+ -handles both IPv4 and IPv6, for both UDP and TCP
+ -reads sipmsg logs from 3800, 4250, 4500, 9200, 6300 SBCs
+ -handles logs with extra carriage-returns and linefeeds, such as from certain FTP'ed cases
+ -handles logs generated/copied from a 'tail' command on the SBC ACLI
+ -handles MBCD messages in logs, and puts their decoded ascii description in comments in Wireshark
+
+ Issues:
+ -for very large logs (many megabytes), it takes a long time (many minutes)
+ -creates fake IP and UDP/TCP headers, which might be misleading
+ -has to guess sometimes, though it hasn't guessed wrong yet as far as I know
+
+ To-do:
+ - make it use Struct.tohex/fromhex now that we have the Struct library in Wireshark
+ - make it use a linux cooked-mode pseudo-header (see https://gitlab.com/wireshark/wireshark/-/wikis/SLL)
+ - make it use preferences, once I write C-code for Wireshark to do that :)
+ - rewrite some of the pattern searches to use real regex/PCRE instead?
+
+Example SIP over UDP message:
+Aug 26 19:25:10.685 On [5:0]2.1.1.1:5060 received from 2.1.2.115:5060
+REGISTER sip:2.1.1.1:5060 SIP/2.0
+Via: SIP/2.0/UDP 2.1.2.115:5060;branch=z9hG4bK6501441021660x81000412
+From: <sip:public_115@2.1.1.1:5060>;tag=520052-7015560x81000412
+To: <sip:public_115@2.1.1.1:5060>
+Call-ID: 680192-4234150x81000412@2.1.2.115
+CSeq: 247 REGISTER
+Contact: <sip:public_115@2.1.2.115:5060;transport=udp>
+Expires: 300
+Max-Forwards: 70
+Authorization: Digest username="public_115",realm="empirix.com",uri="sip:2.1.1.1",response="5d61837cc54dc27018a40f2532e622de",nonce="430f6ff09ecd8c3fdfc5430b6e7e437a4cf77057",algorithm=md5
+Content-Length: 0
+
+
+----------------------------------------
+Another one:
+2007-03-06 13:38:48.037 OPENED
+2007-03-06 13:38:48.037 OPENED
+2007-03-06 13:38:48.037 OPENED
+Mar 6 13:38:54.959 On [1:0]135.25.29.135:5060 received from 192.168.109.138:65471
+OPTIONS sip:135.25.29.135 SIP/2.0
+Accept: application/sdp
+User-Agent: ABS GW v5.1.0
+To: sip:135.25.29.135
+From: sip:192.168.109.138;tag=a2a090ade36bb108da70b0c8f7ba02e9
+Contact: sip:192.168.109.138
+Call-ID: 8c0296da4a0d9f4d97e1389cd28d2352@172.16.6.114
+CSeq: 347517161 OPTIONS
+Via: SIP/2.0/UDP 192.168.109.138;branch=z9hG4bK21feac80fe9a63c1cf2988baa2af0849
+Max-Forwards: 70
+Content-Length: 0
+
+
+----------------------------------------
+Another SIP over UDP (from 9200):
+File opened.
+Jun 8 14:34:22.599 UDP[3:0]10.102.131.194:5060 OPENED
+Jun 8 14:34:22.616 UDP[6:0]10.102.130.185:5060 OPENED
+Jun 8 14:34:49.416 On [6:0]10.102.130.185:5060 received from 10.102.130.150:5060
+REGISTER sip:csp.noklab.net SIP/2.0
+Via: SIP/2.0/UDP 192.168.1.100:5060;branch=z9hG4bK26b7a48d
+From: sip:34903@csp.noklab.net
+To: sip:34903@csp.noklab.net
+Call-ID: 003094c3-a0160002-23aa7e86-29e5808d@192.168.1.100
+CSeq: 144 REGISTER
+User-Agent: CSCO/7
+Contact: <sip:34903@192.168.1.100:5060>
+Content-Length: 0
+Expires: 3600
+
+
+----------------------------------------
+
+Example SIP over TCP message (note it ends in the middle of a header name):
+Jan 12 00:03:54.700 On 172.25.96.200:8194 received from 172.25.32.28:5060
+SIP/2.0 200 OK
+From: Unavailable <sip:Unavailable@172.25.96.200:5060;user=phone>;tag=1200822480
+To: 24001900011 <sip:0011@172.25.32.28:5060;user=phone>;tag=03c86c0b27df1b1254aeccbc000
+Call-ID: 7f6b0887-1d313896-1511da31-b045@144.229.136.111
+CSe
+----------------------------------------
+
+Example SIP Pre and Post-NAT messages:
+Post-NAT from private<realm=e911core> encoded:
+SIP/2.0 302 Moved Temporarily
+Call-ID: SD27o9f04-fcc63aa885c83e22a1be64cfc210b55e-vjvtv00
+CSeq: 2 INVITE
+From: <sip:7866932005@127.1.0.100:5060;user=phone;e911core=TSD5051AEPCORE-dnamt76v6nm04;CKE=BSLD-5cuduig6t52l2;e911vpn=TSD5051AEPVPN-7gdq13vt8fi59>;tag=SD27o9f04-10000000-0-1424021314
+To: <sip:911@127.0.0.100;user=phone;CKE=BSLD-8blt7m3dhnj17>;tag=10280004-0-1239441202
+Via: SIP/2.0/UDP 127.254.254.1:5060;branch=z9hG4bK5i4ue300dgrdras7q281.1
+Server: DC-SIP/1.2
+Content-Length: 0
+Contact: <sip:1111119999@127.0.0.100:5060;e911core=TSD5051AEPCORE-5n86t36uuma01>
+
+
+----------------------------------------
+Pre-NAT to private<realm=e911core> decode:
+ACK sip:911@127.0.0.100;user=phone;CKE=BSLD-8blt7m3dhnj17 SIP/2.0
+Via: SIP/2.0/UDP 127.254.254.1:5060;branch=z9hG4bK5i4ue300dgrdras7q281.1
+Call-ID: SD27o9f04-fcc63aa885c83e22a1be64cfc210b55e-vjvtv00
+CSeq: 2 ACK
+From: <sip:7866932005@127.1.0.100:5060;user=phone;e911core=TSD5051AEPCORE-dnamt76v6nm04;CKE=BSLD-5cuduig6t52l2;e911vpn=TSD5051AEPVPN-7gdq13vt8fi59>;tag=SD27o9f04-10000000-0-1424021314
+To: <sip:911@127.0.0.100;user=phone;CKE=BSLD-8blt7m3dhnj17>;tag=10280004-0-1239441202
+Max-Forwards: 70
+
+
+----------------------------------------
+
+Example DNS message:
+Nov 1 23:03:12.811 On 10.21.232.194:1122 received from 10.21.199.204:53
+DNS Response 3916 flags=8503 q=1 ans=0 auth=1 add=0 net-ttl=0
+ Q:NAPTR 7.6.5.4.3.2.1.0.1.2.e164
+ NS:SOA e164 ttl=0 netnumber01
+ rname=user.netnumber01
+ ser=223 ref=0 retry=0 exp=0 minttl=0
+
+ 0000: 0f 4c 85 03 00 01 00 00 00 01 00 00 01 37 01 36 .L...........7.6
+ 0010: 01 35 01 34 01 33 01 32 01 31 01 30 01 31 01 32 .5.4.3.2.1.0.1.2
+ 0020: 04 65 31 36 34 00 00 23 00 01 04 65 31 36 34 00 .e164..#...e164.
+ 0030: 00 06 00 01 00 00 00 00 00 33 0b 6e 65 74 6e 75 .........3.netnu
+ 0040: 6d 62 65 72 30 31 00 04 75 73 65 72 0b 6e 65 74 mber01..user.net
+ 0050: 6e 75 6d 62 65 72 30 31 00 00 00 00 df 00 00 00 number01........
+ 0060: 00 00 00 00 00 00 00 00 00 00 00 00 00 .............
+
+----------------------------------------
+Example MGCP message (note the IP/UDP headers are in the hex):
+Mar 1 14:37:26.683 On [0:803]172.16.84.141:2427 sent to 172.16.74.100:2427
+Packet:
+ 0000: 00 04 00 00 00 01 00 02 00 00 03 23 0a ad 00 c9 ...........#....
+ 0010: 45 00 00 a8 23 36 00 00 3c 11 63 fd ac 10 54 8d E...#6..<.c...T.
+ 0020: ac 10 4a 64 09 7b 09 7b 00 94 16 c2 32 35 30 20 ..Jd.{.{....250
+
+250 55363 Connection Deleted
+P: PS=6551, OS=1048160, PR=6517, OR=1042720, PL=0, JI=1, LA=5, PC/RPS=6466, PC/ROS=1034560, PC/RPL=0, PC/RJI=0
+
+----------------------------------------
+Example MBCD message:
+Mar 1 14:37:26.672 On 127.0.0.1:2946 sent to 127.0.0.1:2944
+ 0000: ac 3e fd a8 01 01 77 36 9e 00 37 10 0c 34 4c bc .>....w6..7..4L.
+ 0010: 00 30 23 0c 34 4c bc 00 11 33 00 0e 35 00 04 00 .0#.4L...3..5...
+ 0020: 00 00 00 30 00 04 00 00 00 00 23 0c 34 4c bd 00 ...0......#.4L..
+ 0030: 11 33 00 0e 35 00 04 00 00 00 00 30 00 04 00 00 .3..5......0....
+ 0040: 00 00 ..
+Transaction = 24589982 {
+ Context = 204754108 {
+ Subtract = 204754108 {
+ Audit {
+ Stats,
+ Flow
+ }
+ },
+ Subtract = 204754109 {
+ Audit {
+ Stats,
+ Flow
+ }
+ }
+ }
+}
+----------------------------------------
+
+]]----------------------------------------
+
+-- debug printer, set DEBUG to true to enable printing debug info
+-- set DEBUG2 to true to enable really verbose printing
+local DEBUG, DEBUG2 = true, false
+
+local dprint = function() end
+local dprint2 = function() end
+if DEBUG or DEBUG2 then
+ dprint = function(...)
+ print(table.concat({"Lua:", ...}," "))
+ end
+
+ if DEBUG2 then
+ dprint2 = dprint
+ end
+end
+
+-- this should be done as a preference setting
+local ALWAYS_UDP = true
+
+
+local fh = FileHandler.new("Oracle Acme Packet logs", "acme",
+ "A file reader for Oracle Acme Packet message logs such as sipmsg.log","rs")
+
+
+-- There are certain things we have to create fake state/data for, because they
+-- don't exist in the log file for example to create IP headers we have to create
+-- fake identification field values, and to create timestamps we have to guess the
+-- year (and in some cases month/day as well), and for TCP we have to create fake
+-- connection info, such as sequence numbers. We can't simply have a global static
+-- variable holding such things, because Wireshark reads the file sequentially at
+-- first, but then calls seek_read for random packets again and we don't want to
+-- re-create the fake info again because it will be wrong. So we need to create it
+-- for each packet and remember what we created for each packet, so that seek_read
+-- gets the same values. We could store the variables in a big table, keyed by the
+-- specific header info line for each one; but instead we'll key it off of the file
+-- position number, since read() sets it for Wireshark and seek_read() gets it from
+-- Wireshark. So we'll have a set of global statics used during read(), but the
+-- actual per-packet values will be stored in a table indexed/keyed by the file
+-- position number. A separate table holds TCP peer connection info as described
+-- later.
+
+-- I said above that this state is "global", but really it can't be global to this
+-- whole script file, because more than one file can be opened for reading at the
+-- same time. For example if the user presses the reload button, the capture file
+-- will be opened for reading before the previous (same) one is closed. So we have
+-- to store state per-file. The good news is Wireshark gives us a convenient way to
+-- do that, using the CaptureInfo.private_table attribute/member. We can save a Lua
+-- table with whatever contents we want, to this private_table member, and get it
+-- later during the other read/seek_read/cose function calls.
+
+-- So to store this per-file state, we're going to use Lua class objects. They're
+-- just Lua tables that have functions and meta-functions and can be treated like
+-- objects in terms of syntax/behavior.
+
+local State = {}
+local State_mt = { __index = State }
+
+function State.new()
+ local new_class = { -- the new instance
+ -- stuff we need to keep track of to cerate fake info
+ ip_ident = 0,
+ tyear = 0,
+ tmonth = 0,
+ tmin = 0,
+ tsec = 0,
+ tmilli = 0,
+ nstime = NSTime(),
+ -- the following table holds per-packet info
+ -- the key index will be a number - the file position - but it won't be an array type table (too sparse).
+ -- Each packet's entry is a table holding the "static" variables for that packet; this sub-table will be
+ -- an array style instead of hashmap, to reduce size/performance
+ -- This table needs to be cleared whenever the file is closed/opened.
+ packets = {},
+
+ -- the following local table holds TCP peer "connection" info, which is basically
+ -- TCP control block (TCB) type information; this is needed to create and keep track
+ -- of fake TCP sockets/headers for messages that went over TCP, for example for fake
+ -- sequence number info.
+ -- The key index for this is the local+remote ip:port strings concatenated.
+ -- The value is a sub-table, array style, holding the most recent sequence numbers.
+ -- This whole table needs to be cleared whenever the file is closed/opened.
+ tcb = {},
+
+ }
+ setmetatable( new_class, State_mt ) -- all instances share the same metatable
+ return new_class
+end
+
+-- the indices for the State.packets{} variable sub-tables
+local IP_IDENT = 1
+local TTIME = 2
+local LOCAL_SEQ = 3
+local REMOTE_SEQ = 4
+
+-- the indices for the State.tcb{} sub-tables
+local TLOCAL_SEQ = 1
+local TREMOTE_SEQ = 2
+
+-- helper functions
+local char = string.char
+local floor = math.floor
+
+-- takes a Lua number and converts it into a 2-byte string binary (network order)
+
+local function dec2bin16(num)
+ return Struct.pack(">I2",num)
+end
+
+-- takes a Lua number and converts it into a 4-byte string binary (network order)
+local function dec2bin32(num)
+ return Struct.pack(">I4",num)
+end
+
+
+-- function to skip log info before/between/after messages
+local delim = "^%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-%-$"
+-- words that must be found to be skipped. "File ..." is found in 9200 logs)
+local skiplist = { " OPENED", " CLOSED", " STARTED", " STOPPED", "^File ", delim }
+-- pre/post NAT entries
+local pre_nat_header_pattern = "^Pre%-NAT to private<realm=([^>]+)> decode:\r?$"
+local post_nat_header_pattern = "^Post%-NAT from private<realm=([^>]+)> encoded:\r?$"
+
+local function skip_ahead(file, line, position)
+ repeat
+ local found = #line == 0 -- will be false unless the line is empty
+ for i, word in ipairs(skiplist) do
+ if line:find(word) then
+ found = true
+ break
+ end
+ end
+ if found then
+ position = file:seek()
+ line = file:read()
+ if not line then return nil end
+ elseif line:find(pre_nat_header_pattern) or line:find(post_nat_header_pattern) then
+ -- skip the whole message
+ found = true
+ repeat
+ line = file:read()
+ until line:find(delim)
+ end
+ until not found
+ return line, position
+end
+
+-- following pattern grabs month, day, hour, min, sec, millisecs
+local header_time_pattern = "^(%u%l%l) ?(%d%d?) (%d%d?):(%d%d):(%d%d)%.(%d%d%d) On "
+-- tail'ed file has no month/day
+local header_tail_time_pattern = "^(%d%d):(%d%d)%.(%d%d%d) On "
+
+-- grabs local and remote IPv4:ports (not phy/vlan), and words in between (i.e., "sent to" or "received from")
+local header_address_pattern = "(%d%d?%d?%.%d%d?%d?%.%d%d?%d?%.%d%d?%d?):(%d+) (%l+ %l+) (%d%d?%d?%.%d%d?%d?%.%d%d?%d?%.%d%d?%d?):(%d+) ?\r?$"
+-- grabs local and remote IPv6:ports (not phy/vlan), and words in between (i.e., "sent to" or "received from")
+local header_v6address_pattern = "%[([:%x]+)%]:(%d+) (%l+ %l+) %[([:%x]+)%]:(%d+) ?\r?$"
+
+-- grabs phy/vlan info
+local header_phy_pattern = "%[(%d+):(%d+)%]"
+
+local SENT = 1
+local RECV = 2
+local function get_direction(phrase)
+ if #phrase == 7 and phrase:find("sent to") then
+ return SENT
+ elseif #phrase == 13 and phrase:find("received from") then
+ return RECV
+ end
+ dprint("direction phrase not found")
+ return nil
+end
+
+-- monthlist table for getting month number value from 3-char name (number is table index)
+local monthlist = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}
+
+-- Compute the difference in seconds between local time and UTC
+-- from http://lua-users.org/wiki/TimeZone
+local function get_timezone()
+ local now = os.time()
+ return os.difftime(now, os.time(os.date("!*t", now)))
+end
+local timezone = get_timezone()
+
+function State:get_timestamp(line, file_position, seeking)
+ local i, line_pos, month, day, hour, min, sec, milli = line:find(header_time_pattern)
+ if not month then
+ return
+ end
+
+ if seeking then
+ -- we've seen this packet before, just go get the saved timestamp
+ sec = self.packets[file_position][TTIME]
+ if not sec then
+ dprint("failed to get saved timestamp for packet at position:", file_position)
+ return
+ end
+ return sec, line_pos
+ end
+
+ -- find the month's number
+ for index, name in ipairs(monthlist) do
+ if month == name then
+ month = index
+ break
+ end
+ end
+ if type(month) ~= "number" then return end
+
+ day = tonumber(day)
+ hour = tonumber(hour)
+ min = tonumber(min)
+ sec = tonumber(sec)
+ milli = tonumber(milli)
+
+ if not day or not hour or not min or not sec or not milli then
+ dprint("timestamp could not be determined")
+ return nil
+ end
+
+ -- we don't know what year the log file was created, so we have to guess
+ -- if we guess the current system year, then a log of December loaded in January will appear wrong,
+ -- as will a log file which lasts over new year
+ -- so we're going to check the current system month, and if it's less than the log file's then we'll
+ -- assume the log file started last year; if the system month is larger or equal, then we'll assume the log
+ -- file is of this year. We only do this checking once per file.
+ if self.tyear == 0 then
+ local curr_year, curr_month = tonumber(os.date("%Y")), tonumber(os.date("%m"))
+ if curr_month < month then
+ -- use last year
+ if curr_year > 0 then
+ curr_year = curr_year - 1
+ end
+ end
+ self.tyear = curr_year
+
+ -- XXX - but for purposes of testing, we just force the year to
+ -- 2014, so that we can compare the result of this code reading
+ -- an Acme log with the result of the pcapng reader reading a
+ -- pcapng file with the same packets - the time stamps in
+ -- pcapng files are times since the Epoch, so the year is known
+ self.tyear = 2014
+ end
+
+ -- if this message's month is less than previous message's, then year wrapped
+ if month < self.tmonth then
+ self.tyear = self.tyear + 1
+ end
+ self.tmonth = month
+
+ local timet = os.time({ ["year"] = self.tyear, ["month"] = month, ["day"] = day, ["hour"] = hour, ["min"] = min, ["sec"] = sec })
+ if not timet then
+ dprint("timestamp conversion failed")
+ end
+
+ timet = timet + timezone
+
+ -- make an NSTime
+ self.nstime = NSTime(timet, milli * 1000000)
+ self.packets[file_position][TTIME] = self.nstime
+
+ timet = timet + (milli/1000)
+ dprint2("found time of ", os.date("%c",timet), " with value=",timet)
+
+ return self.nstime, line_pos
+end
+
+-- get_tail_time() gets a fictitious timestamp starting from 19:00:00 on Dec 31, 1969, and incrementing based
+-- on the minutes/secs/millisecs seen (i.e., if the minute wrapped then hour increases by 1, etc.).
+-- this is needed for tail'ed log files, since they don't show month/day/hour
+function State:get_tail_time(line, file_position, seeking)
+ local i, line_pos, min, sec, milli = line:find(header_tail_time_pattern)
+ if not min then return end
+
+ if seeking then
+ -- we've seen this packet before, just go get the saved timestamp
+ sec = self.packets[file_position][TTIME]
+ if not sec then
+ dprint("failed to get saved timestamp for packet at position:", file_position)
+ return
+ end
+ return sec, line_pos
+ end
+
+ min = tonumber(min)
+ sec = tonumber(sec)
+ milli = tonumber(milli)
+
+ if not min or not sec or not milli then
+ dprint("timestamp could not be determined")
+ return nil
+ end
+
+ -- get difference in time
+ local tmin, tsec, tmilli, nstime = self.tmin, self.tsec, self.tmilli, self.nstime
+ local ttime = nstime.secs
+
+ -- min, sec, milli are what the log says this tail'ed packet is
+ -- tmin, tsec, tmilli are what we got from last packet
+ -- nstime is the unix time of that, and ttime is the seconds of that unix time
+
+ -- if minutes wrapped, or they're equal but seconds wrapped, then handle it as if in the next hour
+ if (min < tmin) or (min == tmin and sec < tsec) or (min == tmin and sec == tsec and milli < tmilli) then
+ -- something wrapped, calculate difference as if in next hour
+ ttime = ttime + (((min * 60) + sec + 3600) - ((tmin * 60) + tsec))
+ else
+ ttime = ttime + (((min * 60) + sec) - ((tmin * 60) + tsec))
+ end
+ self.tmin, self.tsec, self.tmilli = min, sec, milli
+ self.nstime = NSTime(ttime, milli * 1000000)
+ self.packets[file_position][TTIME] = self.nstime
+
+ return self.nstime, line_pos
+end
+
+local hexbin = {
+ ["0"]=0, ["1"]=1, ["2"]=2, ["3"]=3, ["4"]=4, ["5"]=5, ["6"]=6, ["7"]=7, ["8"]=8, ["9"]=9, ["a"]=10, ["b"]=11, ["c"]=12, ["d"]=13, ["e"]=14, ["f"]=15,
+ ["00"]=0, ["01"]=1, ["02"]=2, ["03"]=3, ["04"]=4, ["05"]=5, ["06"]=6, ["07"]=7, ["08"]=8, ["09"]=9, ["0a"]=10, ["0b"]=11, ["0c"]=12, ["0d"]=13, ["0e"]=14, ["0f"]=15,
+ ["10"]=16, ["11"]=17, ["12"]=18, ["13"]=19, ["14"]=20, ["15"]=21, ["16"]=22, ["17"]=23, ["18"]=24, ["19"]=25, ["1a"]=26, ["1b"]=27, ["1c"]=28, ["1d"]=29, ["1e"]=30, ["1f"]=31,
+ ["20"]=32, ["21"]=33, ["22"]=34, ["23"]=35, ["24"]=36, ["25"]=37, ["26"]=38, ["27"]=39, ["28"]=40, ["29"]=41, ["2a"]=42, ["2b"]=43, ["2c"]=44, ["2d"]=45, ["2e"]=46, ["2f"]=47,
+ ["30"]=48, ["31"]=49, ["32"]=50, ["33"]=51, ["34"]=52, ["35"]=53, ["36"]=54, ["37"]=55, ["38"]=56, ["39"]=57, ["3a"]=58, ["3b"]=59, ["3c"]=60, ["3d"]=61, ["3e"]=62, ["3f"]=63,
+ ["40"]=64, ["41"]=65, ["42"]=66, ["43"]=67, ["44"]=68, ["45"]=69, ["46"]=70, ["47"]=71, ["48"]=72, ["49"]=73, ["4a"]=74, ["4b"]=75, ["4c"]=76, ["4d"]=77, ["4e"]=78, ["4f"]=79,
+ ["50"]=80, ["51"]=81, ["52"]=82, ["53"]=83, ["54"]=84, ["55"]=85, ["56"]=86, ["57"]=87, ["58"]=88, ["59"]=89, ["5a"]=90, ["5b"]=91, ["5c"]=92, ["5d"]=93, ["5e"]=94, ["5f"]=95,
+ ["60"]=96, ["61"]=97, ["62"]=98, ["63"]=99, ["64"]=100, ["65"]=101, ["66"]=102, ["67"]=103, ["68"]=104, ["69"]=105, ["6a"]=106, ["6b"]=107, ["6c"]=108, ["6d"]=109, ["6e"]=110, ["6f"]=111,
+ ["70"]=112, ["71"]=113, ["72"]=114, ["73"]=115, ["74"]=116, ["75"]=117, ["76"]=118, ["77"]=119, ["78"]=120, ["79"]=121, ["7a"]=122, ["7b"]=123, ["7c"]=124, ["7d"]=125, ["7e"]=126, ["7f"]=127,
+ ["80"]=128, ["81"]=129, ["82"]=130, ["83"]=131, ["84"]=132, ["85"]=133, ["86"]=134, ["87"]=135, ["88"]=136, ["89"]=137, ["8a"]=138, ["8b"]=139, ["8c"]=140, ["8d"]=141, ["8e"]=142, ["8f"]=143,
+ ["90"]=144, ["91"]=145, ["92"]=146, ["93"]=147, ["94"]=148, ["95"]=149, ["96"]=150, ["97"]=151, ["98"]=152, ["99"]=153, ["9a"]=154, ["9b"]=155, ["9c"]=156, ["9d"]=157, ["9e"]=158, ["9f"]=159,
+ ["a0"]=160, ["a1"]=161, ["a2"]=162, ["a3"]=163, ["a4"]=164, ["a5"]=165, ["a6"]=166, ["a7"]=167, ["a8"]=168, ["a9"]=169, ["aa"]=170, ["ab"]=171, ["ac"]=172, ["ad"]=173, ["ae"]=174, ["af"]=175,
+ ["b0"]=176, ["b1"]=177, ["b2"]=178, ["b3"]=179, ["b4"]=180, ["b5"]=181, ["b6"]=182, ["b7"]=183, ["b8"]=184, ["b9"]=185, ["ba"]=186, ["bb"]=187, ["bc"]=188, ["bd"]=189, ["be"]=190, ["bf"]=191,
+ ["c0"]=192, ["c1"]=193, ["c2"]=194, ["c3"]=195, ["c4"]=196, ["c5"]=197, ["c6"]=198, ["c7"]=199, ["c8"]=200, ["c9"]=201, ["ca"]=202, ["cb"]=203, ["cc"]=204, ["cd"]=205, ["ce"]=206, ["cf"]=207,
+ ["d0"]=208, ["d1"]=209, ["d2"]=210, ["d3"]=211, ["d4"]=212, ["d5"]=213, ["d6"]=214, ["d7"]=215, ["d8"]=216, ["d9"]=217, ["da"]=218, ["db"]=219, ["dc"]=220, ["dd"]=221, ["de"]=222, ["df"]=223,
+ ["e0"]=224, ["e1"]=225, ["e2"]=226, ["e3"]=227, ["e4"]=228, ["e5"]=229, ["e6"]=230, ["e7"]=231, ["e8"]=232, ["e9"]=233, ["ea"]=234, ["eb"]=235, ["ec"]=236, ["ed"]=237, ["ee"]=238, ["ef"]=239,
+ ["f0"]=240, ["f1"]=241, ["f2"]=242, ["f3"]=243, ["f4"]=244, ["f5"]=245, ["f6"]=246, ["f7"]=247, ["f8"]=248, ["f9"]=249, ["fa"]=250, ["fb"]=251, ["fc"]=252, ["fd"]=253, ["fe"]=254, ["ff"]=255
+}
+
+local function iptobytes(ipaddr)
+ local bytes = { ipaddr:match("(%d+)%.(%d+)%.(%d+)%.(%d+)") }
+ if not #bytes == 4 then
+ dprint("failed to get ip address bytes for '", ipaddr, "'")
+ return
+ end
+ local ip = ""
+ for i, byte in ipairs(bytes) do
+ ip = ip .. char(tonumber(byte))
+ end
+ return ip
+end
+
+local function hexword2bin(word)
+ if #word == 4 then
+ return char(hexbin[word:sub(1,2)], hexbin[word:sub(3,4)])
+ elseif #word == 3 then
+ return char(hexbin[word:sub(1,1)], hexbin[word:sub(2,3)])
+ elseif #word < 3 then
+ return char(0, hexbin[word])
+ end
+ return nil -- error
+end
+
+-- convert this 2620:0:60:8ac::102 to its 16-byte binary (=8 of 2-byte words)
+local NUMWORDS = 8
+local function ipv6tobytes(ipaddr)
+ -- start with all 16 bytes being zeroes
+ local words = { "\00\00", "\00\00", "\00\00", "\00\00", "\00\00", "\00\00", "\00\00", "\00\00" }
+ -- now walk from front of ipv6 address string replacing byte numbers above;
+ -- if we hit a "::", then jump to end and do it in reverse
+ local colon_s, colon_e = ipaddr:find("::%x")
+ if colon_s then
+ -- there's a double-colon, so split the string and do the end first, backwards
+ -- get each chunk first
+ local t = {}
+ local index, wordix = 1, NUMWORDS
+ for w in string.gmatch(ipaddr:sub(colon_e - 1), ":(%x+)") do
+ t[index] = hexword2bin(w)
+ index = index + 1
+ end
+ for ix=index-1, 1, -1 do
+ words[wordix] = t[ix]
+ wordix = wordix - 1
+ end
+ ipaddr = ipaddr:sub(1, colon_s)
+ end
+
+ local i = 1
+ for w in string.gmatch(ipaddr, "(%x+):?") do
+ words[i] = hexword2bin(w)
+ i = i + 1
+ end
+
+ if not #words == NUMWORDS then
+ dprint("failed to get IPv6 address bytes for '", ipaddr, "'")
+ return
+ end
+
+ return table.concat(words)
+end
+
+-- calculates checksum as done for IP, TCP, UDP
+local function checksum(chunk)
+ local sum = 0
+ -- take every 2-byte value and add them up
+ for one, two in chunk:gmatch("(.)(.)") do
+ sum = sum + (string.byte(one) * 256) + (string.byte(two))
+ while floor(sum / 65536) > 0 do
+ -- add carry/overflow value
+ sum = (sum % 65536) + (floor(sum / 65536))
+ end
+ end
+
+ -- now get one's complement of that
+ sum = 65535 - sum
+
+ -- and return it as a 2-byte string
+ return dec2bin16(sum)
+end
+
+----------------------------------------
+-- protocol type number
+local PROTO_UDP = "\17"
+local PROTO_TCP = "\06"
+-- enum
+local IPv4 = 1
+local IPv6 = 2
+-- both type enums and header lengths
+local UDP = 8
+local TCP = 20
+
+----------------------------------------
+-- Packet creation/serialization occurs using a Lua class object model
+-- There's a single base class 'Packet' which has data/methods every packet type has
+-- 'RawPacket' and 'DataPacket' both derive from 'Packet'.
+-- 'RawPacket' is for packets which the log file has the raw IP/UDP headers for,
+-- such as ALG log messages (MGCP/NCS). Since the IP headers are in them, we use those.
+-- 'DataPacket' is for packets which the log file only has payload data for, and
+-- we need to create fake IP/UDP or IP/TCP headers for.
+-- 'BinPacket' and'AsciiPacket' both derive from 'DataPacket'.
+-- 'BinPacket' is for binary-style logged packets, such as MBCD or DNS, while
+-- 'AsciiPacket' is for ascii-style ones such as SIP.
+-- 'DnsPacket' derives from 'BinPacket', for DNS-style logs.
+
+-- Each class has a read_data() method, which reads in the packet data, builds the packet,
+-- and sets the Wireshark buffer. Some classes have a get_data() method which read_data()
+-- calls, to get the payload data before building a fake packet.
+
+-- The base Packet class has a get_hex_data() and get_ascii_data() methods, to get the payload
+-- in either form, and those base methods are called by get_data() or read_data() of derived
+-- classes.
+
+-- For performance reasons, packet data is read line-by-line into a table (called bufftbl),
+-- which is concatenated at the end. This avoids Lua building interim strings and garbage
+-- collecting them. But it makes the code uglier. The get_data()/get_hex_data()/get_ascii_data()
+-- methods read into this table they get passed, while the read_data() functions handle managing
+-- the table.
+
+----------------------------------------
+----------------------------------------
+-- The base Packet class, from which others derive
+-- all Packets have a ptype, timestamp, source and dest address:port, and data
+--
+local Packet = {}
+local Packet_mt = { __index = Packet }
+
+function Packet.new(state, timestamp, direction, source_ip, source_port, dest_ip, dest_port, ptype, ttype, file_position)
+ local new_class = { -- the new instance
+ ["state"] = state,
+ ["timestamp"] = timestamp,
+ ["direction"] = direction,
+ ["source_ip"] = source_ip,
+ ["source_port"] = source_port,
+ ["dest_ip"] = dest_ip,
+ ["dest_port"] = dest_port,
+ ["ptype"] = ptype,
+ ["ttype"] = ttype,
+ ["file_position"] = file_position
+ }
+ setmetatable( new_class, Packet_mt ) -- all instances share the same metatable
+ return new_class
+end
+
+function Packet:set_comment(comment)
+ self["comment"] = comment
+end
+
+function Packet:set_wslua_fields(frame)
+ frame.time = self.timestamp
+ frame.rec_type = wtap_rec_types.PACKET
+ frame.flags = wtap_presence_flags.TS -- for timestamp
+ if self.comment then
+ frame.comment = self.comment
+ end
+ return true
+end
+
+local packet_hexline_pattern = "^ %x%x%x0: %x%x"
+function Packet:get_hex_data(file, line, bufftbl, index)
+ local start = index
+
+ dprint2("Packet:get_hex_data() called")
+ repeat
+ for word in line:gmatch("(%x%x) ") do
+ bufftbl[index] = char(hexbin[word])
+ index = index + 1
+ if ((index - start) % 16) == 0 then break end
+ end
+ line = file:read()
+ until not line or not line:find(packet_hexline_pattern)
+
+ return index - start, line
+end
+
+function Packet:get_ascii_data(file, line, bufftbl, index, only_newline)
+ local bufflen = 0 -- keep tally of total length of payload
+ local found_delim = true
+
+ dprint2("Packet:get_ascii_data() called")
+ repeat
+ bufftbl[index] = line
+ bufflen = bufflen + #line
+
+ -- sanity check if line has "\r" at end, and if so only add \n
+ if line:find("\r",-1,true) then
+ bufftbl[index+1] = "\n"
+ bufflen = bufflen + 1
+ dprint2("Found carriage-return at end of line")
+ elseif only_newline then
+ -- only add a newline
+ bufftbl[index+1] = "\n"
+ bufflen = bufflen + 1
+ else
+ bufftbl[index+1] = "\r\n"
+ bufflen = bufflen + 2
+ end
+ index = index + 2
+
+ -- read next line now
+ line = file:read()
+ if not line then
+ -- hit eof?
+ found_delim = false
+ break
+ end
+
+ until line:find(delim)
+
+ -- get rid of last \r\n, if we found a dashed delimiter, as it's not part of packet
+ if found_delim then
+ bufflen = bufflen - bufftbl[index-1]:len()
+ bufftbl[index-1] = nil
+ end
+
+ dprint2("Packet:get_ascii_data() returning", bufflen)
+ return bufflen
+end
+
+----------------------------------------
+-- RawPacket class, for packets that the log file contains the whole IP header for, such as algd logs
+--
+local RawPacket = {}
+local RawPacket_mt = { __index = RawPacket }
+setmetatable( RawPacket, Packet_mt ) -- make RawPacket inherit from Packet
+
+function RawPacket.new(...)
+ local new_class = Packet.new(...) -- the new instance
+ setmetatable( new_class, RawPacket_mt ) -- all instances share the same metatable
+ return new_class
+end
+
+function RawPacket:read_data(file, frame, line, seeking)
+ local bufftbl = {} -- table to hold data bytes
+ local index = 1 -- start at first slot in array
+
+ -- need to skip "Packet:" line and first 0000: line, it's internal junk
+ line = file:read()
+ line = file:read()
+
+ dprint2("RawPacket:read_data() getting hex from line='", line, "'")
+ local bufflen, line = self:get_hex_data(file, line, bufftbl, index)
+ if not bufflen or bufflen < 21 then
+ dprint("error getting binary data")
+ return false
+ end
+
+ -- add remainder as more packet data, but first delete overlap
+ -- see if frag bits are set in IP header, to see if UDP/TCP header exists
+ if self.ptype == IPv4 then
+ -- grab byte with frag flags and first byte of offset
+ local flag = string.byte(bufftbl[7]) -- converts binary character to number
+ local frag_offset = flag % 32 -- masks off upper 3 bits
+ frag_offset = (frag_offset * 256) + string.byte(bufftbl[8])
+ flag = floor(flag / 224) -- shift right
+ flag = flag % 2 -- mask upper bits
+ if flag == 1 or frag_offset > 0 then
+ -- we have a fragmented IPv4 packet, so no proto header
+ -- only save first 20 bytes (the IP header)
+ for i=bufflen, 21, -1 do
+ bufftbl[i] = nil
+ end
+ bufflen = 20
+ else
+ -- only save first 20 + proto size bytes
+ local save
+ if bufftbl[10] == PROTO_UDP then
+ save = 28
+ elseif bufftbl[10] == PROTO_TCP then
+ save = 40
+ else
+ dprint("failed to fix raw packet overlap")
+ return
+ end
+ for i=bufflen, save+1, -1 do
+ bufftbl[i] = nil
+ end
+ bufflen = save
+ end
+ end
+ -- TODO: IPv6
+
+ -- now read in rest of message, if any
+ -- first skip extra empty newline
+ if #line == 0 then
+ line = file:read()
+ end
+
+ bufflen = bufflen + self:get_ascii_data(file, line, bufftbl, bufflen+1, true)
+
+ frame.data = table.concat(bufftbl)
+
+ return true
+end
+
+----------------------------------------
+-- DataPacket class, for packets that the log file contains just the payload data for
+--
+local DataPacket = {}
+local DataPacket_mt = { __index = DataPacket }
+setmetatable( DataPacket, Packet_mt ) -- make DataPacket inherit from Packet
+
+function DataPacket.new(...)
+ local new_class = Packet.new(...) -- the new instance
+ setmetatable( new_class, DataPacket_mt ) -- all instances share the same metatable
+ return new_class
+end
+
+function DataPacket:set_tcbkey(key)
+ self["tcbkey"] = key
+ return
+end
+
+function DataPacket:build_ipv4_hdr(bufflen, proto, seeking)
+ local len = bufflen + 20 -- 20 byte IPv4 header size
+
+ -- figure out the ip identification value
+ local ip_ident
+ if seeking then
+ ip_ident = self.state.packets[self.file_position][IP_IDENT]
+ else
+ -- increment ident value
+ self.state.ip_ident = self.state.ip_ident + 1
+ if self.state.ip_ident == 65536 then
+ self.state.ip_ident = 1
+ end
+ ip_ident = self.state.ip_ident
+ -- save it for future seeking
+ self.state.packets[self.file_position][IP_IDENT] = ip_ident
+ end
+
+ -- use a table to concatenate as it's slightly faster that way
+ local hdrtbl = {
+ "\69\00", -- 1=ipv4 and 20 byte header length
+ dec2bin16(len), -- 2=packet length bytes
+ dec2bin16(ip_ident), -- 3=ident field bytes
+ "\00\00\64", -- 4=flags/fragment offset, ttl
+ proto, -- 5=proto
+ "\00\00", -- 6=checksum (using zero for now)
+ iptobytes(self.source_ip), -- 7=source ip
+ iptobytes(self.dest_ip) -- 8=dest ip
+ }
+
+ -- calc IPv4 header checksum, and set its value
+ hdrtbl[6] = checksum(table.concat(hdrtbl))
+
+ return table.concat(hdrtbl)
+end
+
+function DataPacket:build_ipv6_hdr(bufflen, proto)
+ -- use a table to concatenate as it's slightly faster that way
+ local hdrtbl = {
+ "\96\00\00\00", -- 1=ipv6 version, class, label
+ dec2bin16(bufflen), -- 2=packet length bytes
+ proto .. "\64", -- 4=proto, ttl
+ ipv6tobytes(self.source_ip), -- 5=source ip
+ ipv6tobytes(self.dest_ip) -- 6=dest ip
+ }
+ return table.concat(hdrtbl)
+end
+
+-- calculates TCP/UDP header checksums with pseudo-header info
+function DataPacket:calc_header_checksum(bufftbl, bufflen, hdrtbl, proto)
+ -- first create pseudo IP header
+ if self.ptype == IPv4 then
+ local iphdrtbl = {
+ iptobytes(self.source_ip), -- 1=source ip
+ iptobytes(self.dest_ip), -- 2=dest ip
+ "\00", -- zeros
+ proto, -- proto
+ dec2bin16(bufflen) -- payload length bytes
+ }
+ bufftbl[1] = table.concat(iphdrtbl)
+ elseif self.ptype == IPv6 then
+ local iphdrtbl = {
+ ipv6tobytes(self.source_ip), -- 1=source ip
+ ipv6tobytes(self.dest_ip), -- 2=dest ip
+ "\00\00", -- zeroes
+ dec2bin16(bufflen), -- payload length bytes
+ "\00\00\00", -- zeros
+ proto -- proto
+ }
+ bufftbl[1] = table.concat(iphdrtbl)
+ end
+
+ -- and pseudo TCP or UDP header
+ bufftbl[2] = table.concat(hdrtbl)
+
+ -- see if payload is odd length
+ local odd = false
+ if bufflen % 2 == 1 then
+ -- odd number of payload bytes, add zero byte at end
+ odd = true -- remember to undo this
+ bufftbl[#bufftbl+1] = "\00"
+ end
+
+ local result = checksum(table.concat(bufftbl))
+
+ -- remove pseudo-headers
+ bufftbl[1] = nil
+ bufftbl[2] = nil
+ if odd then
+ bufftbl[#bufftbl] = nil
+ end
+
+ return result
+end
+
+
+function DataPacket:build_udp_hdr(bufflen, bufftbl)
+ local len = bufflen + 8 -- 8 for size of UDP header
+ local hdrtbl = {
+ dec2bin16(self.source_port), -- 1=source port bytes
+ dec2bin16(self.dest_port), -- 2=dest port bytes
+ dec2bin16(len), -- 3=payload length bytes
+ "\00\00" -- 4=checksum
+ }
+ if bufftbl then
+ -- calc udp checksum (only done for IPv6)
+ hdrtbl[4] = self:calc_header_checksum(bufftbl, len, hdrtbl, PROTO_UDP)
+ end
+ return table.concat(hdrtbl)
+end
+
+
+function DataPacket:build_tcp_hdr(bufflen, bufftbl, seeking)
+ local len = bufflen + 20 -- 20 for size of TCP header
+
+ local local_seq, remote_seq
+ if seeking then
+ local_seq = self.state.packets[self.file_position][LOCAL_SEQ]
+ remote_seq = self.state.packets[self.file_position][REMOTE_SEQ]
+ else
+ -- find socket/tcb info for this "stream", create if not found
+ if not self.state.tcb[self.tcbkey] then
+ -- create them
+ self.state.tcb[self.tcbkey] = {}
+ local_seq = 1
+ remote_seq = 1
+ self.state.packets[self.file_position][LOCAL_SEQ] = 1
+ self.state.packets[self.file_position][REMOTE_SEQ] = 1
+ -- set tcb to next sequence numbers, so that the correct "side"
+ -- acknowledges receiving these bytes
+ if self.direction == SENT then
+ -- this packet is being sent, so local sequence increases next time
+ self.state.tcb[self.tcbkey][TLOCAL_SEQ] = bufflen+1
+ self.state.tcb[self.tcbkey][TREMOTE_SEQ] = 1
+ else
+ -- this packet is being received, so remote sequence increases next time
+ -- and local side will acknowldge it next time
+ self.state.tcb[self.tcbkey][TLOCAL_SEQ] = 1
+ self.state.tcb[self.tcbkey][TREMOTE_SEQ] = bufflen+1
+ end
+ else
+ -- stream already exists, so send the current tcb seqs and update for next time
+ if self.direction == SENT then
+ -- this packet is being sent, so local sequence increases next time
+ local_seq = self.state.tcb[self.tcbkey][TLOCAL_SEQ]
+ remote_seq = self.state.tcb[self.tcbkey][TREMOTE_SEQ]
+ self.state.tcb[self.tcbkey][TLOCAL_SEQ] = local_seq + bufflen
+ else
+ -- this packet is being received, so the "local" seq number of the packet is the remote's seq really
+ local_seq = self.state.tcb[self.tcbkey][TREMOTE_SEQ]
+ remote_seq = self.state.tcb[self.tcbkey][TLOCAL_SEQ]
+ -- and remote seq needs to increase next time (remember local_seq is TREMOTE_SEQ)
+ self.state.tcb[self.tcbkey][TREMOTE_SEQ] = local_seq + bufflen
+ end
+ self.state.packets[self.file_position][LOCAL_SEQ] = local_seq
+ self.state.packets[self.file_position][REMOTE_SEQ] = remote_seq
+ end
+ end
+
+ local hdrtbl = {
+ dec2bin16(self.source_port), -- 1=source port bytes
+ dec2bin16(self.dest_port), -- 2=dest port bytes
+ dec2bin32(local_seq), -- 3=sequence
+ dec2bin32(remote_seq), -- 4=ack number
+ "\80\16\255\255", -- 5=offset, flags, window size
+ "\00\00", -- 6=checksum
+ "\00\00" -- 7=urgent pointer
+ }
+
+ -- calc tcp checksum
+ hdrtbl[6] = self:calc_header_checksum(bufftbl, len, hdrtbl, PROTO_TCP)
+
+ return table.concat(hdrtbl)
+end
+
+function DataPacket:build_packet(bufftbl, bufflen, seeking)
+ dprint2("DataPacket:build_packet() called with ptype=",self.ptype)
+ if self.ptype == IPv4 then
+ if self.ttype == UDP then
+ bufftbl[2] = self:build_udp_hdr(bufflen)
+ bufftbl[1] = self:build_ipv4_hdr(bufflen + 8, PROTO_UDP, seeking)
+ elseif self.ttype == TCP then
+ bufftbl[2] = self:build_tcp_hdr(bufflen, bufftbl, seeking)
+ bufftbl[1] = self:build_ipv4_hdr(bufflen + 20, PROTO_TCP, seeking)
+ end
+ elseif self.ptype == IPv6 then
+ -- UDP for IPv6 requires checksum calculation, so we can't avoid more work
+ if self.ttype == UDP then
+ bufftbl[2] = self:build_udp_hdr(bufflen, bufftbl)
+ bufftbl[1] = self:build_ipv6_hdr(bufflen + 8, PROTO_UDP)
+ elseif self.ttype == TCP then
+ bufftbl[2] = self:build_tcp_hdr(bufflen, bufftbl, seeking)
+ bufftbl[1] = self:build_ipv6_hdr(bufflen + 20, PROTO_TCP)
+ end
+ else
+ dprint("DataPacket:build_packet: invalid packet type (neither IPv4 nor IPv6)")
+ return nil
+ end
+
+ return table.concat(bufftbl)
+end
+
+-- for performance, we read each line into a table and concatenate it at end
+-- but it makes this code super ugly
+function DataPacket:read_data(file, frame, line, seeking)
+ local bufftbl = { "", "" } -- 2 slots for ip and udp/tcp headers
+ local index = 3 -- start at third slot in array
+ local comment -- for any packet comments
+
+ dprint2("DataPacket: read_data(): calling get_data")
+ local bufflen = self:get_data(file, line, bufftbl, index)
+ if not bufflen then
+ dprint("DataPacket: error getting ascii or binary data")
+ return false
+ end
+
+ local buff = self:build_packet(bufftbl, bufflen, seeking)
+
+ frame.data = buff
+
+ return true
+end
+
+
+----------------------------------------
+-- BinPacket class, for packets that the log file contains binary payload data for, such as MBCD
+--
+local BinPacket = {}
+local BinPacket_mt = { __index = BinPacket }
+setmetatable( BinPacket, DataPacket_mt ) -- make BinPacket inherit from DataPacket
+
+function BinPacket.new(...)
+ local new_class = DataPacket.new(...) -- the new instance
+ setmetatable( new_class, BinPacket_mt ) -- all instances share the same metatable
+ return new_class
+end
+
+function BinPacket:get_comment_data(file, line, stop_pattern)
+ local comments = {}
+
+ while line and not line:find(stop_pattern) do
+ if #line > 0 then
+ comments[#comments+1] = line
+ comments[#comments+1] = "\r\n"
+ end
+ line = file:read()
+ end
+
+ if #comments > 0 then
+ -- get rid of extra "\r\n"
+ comments[#comments] = nil
+ self:set_comment(table.concat(comments))
+ end
+
+ return line
+end
+
+function BinPacket:get_data(file, line, bufftbl, index)
+ local is_alg = false
+
+ local bufflen, line = self:get_hex_data(file, line, bufftbl, index)
+
+ -- now eat rest of message until delimiter or end of file
+ -- we'll put them in comments
+ line = self:get_comment_data(file, line, delim)
+
+ -- return the bufflen, which is the same as number of table entries we made
+ return bufflen
+end
+
+----------------------------------------
+-- DnsPacket class, for DNS packets (which are binary but with comments at top)
+--
+local DnsPacket = {}
+local DnsPacket_mt = { __index = DnsPacket }
+setmetatable( DnsPacket, BinPacket_mt ) -- make DnsPacket inherit from BinPacket
+
+function DnsPacket.new(...)
+ local new_class = BinPacket.new(...) -- the new instance
+ setmetatable( new_class, DnsPacket_mt ) -- all instances share the same metatable
+ return new_class
+end
+
+local binpacket_start_pattern = "^ 0000: %x%x %x%x %x%x %x%x %x%x %x%x %x%x %x%x "
+function DnsPacket:get_data(file, line, bufftbl, index)
+ -- it's UDP regardless of what parse_header() thinks
+ self.ttype = UDP
+
+ -- comments are at top instead of bottom of message
+ line = self:get_comment_data(file, line, binpacket_start_pattern)
+
+ local bufflen, line = self:get_hex_data(file, line, bufftbl, index)
+
+ -- now eat rest of message until delimiter or end of file
+ while line and not line:find(delim) do
+ line = file:read()
+ end
+
+ -- return the bufflen, which is the same as number of table entries we made
+ return bufflen
+end
+
+----------------------------------------
+-- AsciiPacket class, for packets that the log file contains ascii payload data for
+--
+local AsciiPacket = {}
+local AsciiPacket_mt = { __index = AsciiPacket }
+setmetatable( AsciiPacket, DataPacket_mt ) -- make AsciiPacket inherit from DataPacket
+
+function AsciiPacket.new(...)
+ local new_class = DataPacket.new(...) -- the new instance
+ setmetatable( new_class, AsciiPacket_mt ) -- all instances share the same metatable
+ return new_class
+end
+
+function AsciiPacket:get_data(file, line, bufftbl, index)
+ return self:get_ascii_data(file, line, bufftbl, index)
+end
+
+
+----------------------------------------
+-- To determine packet type, we peek at the first line of 'data' following the log
+-- message header. Its pattern determines the Packet object type.
+-- The following are the patterns we look for; if it doesn't match one of these,
+-- then it's an AsciiPacket:
+local packet_patterns = {
+ { "^ 0000: %x%x %x%x %x%x %x%x %x%x %x%x %x%x %x%x ", BinPacket },
+ { "^Packet:$", RawPacket },
+ { "^DNS Query %d+ flags=%d+ q=%d+ ans=%d+", DnsPacket },
+ { "^DNS Response %d+ flags=%d+ q=%d+ ans=%d+", DnsPacket }
+}
+-- indeces for above
+local PP_PATTERN = 1
+local PP_CLASS = 2
+
+local function get_packet_class(line)
+ for i, t in ipairs(packet_patterns) do
+ if line:find(t[PP_PATTERN]) then
+ dprint2("got class type=",i)
+ return t[PP_CLASS]
+ end
+ end
+ dprint2("got class type AsciiPacket")
+ return AsciiPacket
+end
+
+----------------------------------------
+-- parses header line
+-- returns nil on failure
+-- the header lines look like this:
+-- Aug 10 14:30:11.134 On [1:544]10.201.145.237:5060 received from 10.210.1.193:5060
+-- this one has no phy/vlan info in brackets:
+-- Mar 6 13:39:06.122 On 127.0.0.1:2945 sent to 127.0.0.1:2944
+-- this one is IPv6:
+-- Aug 10 14:30:11.140 On [3:0][2620:0:60:8ac::102]:5060 sent to [2620:0:60:8ab::12]:5060
+-- this is from a tail'ed log output:
+-- 52:22.434 On [0:0]205.152.56.211:5060 received from 205.152.56.75:5060
+local loopback_pattern = "^127%.0%.0%.%d+$"
+local function parse_header(state, file, line, file_position, seeking)
+
+ if seeking then
+ -- verify we've seen this packet before
+ if not state.packets[file_position] then
+ dprint("parse_header: packet at file position ", file_position, " not saved previously")
+ return
+ end
+ else
+ -- first time through, create sub-table for the packet
+ state.packets[file_position] = {}
+ end
+
+ -- get time info, and line match ending position
+ local timestamp, line_pos = state:get_timestamp(line, file_position, seeking)
+ if not timestamp then
+ -- see if it's a tail'ed log instead
+ timestamp, line_pos = state:get_tail_time(line, file_position, seeking)
+ end
+
+ if not timestamp then
+ dprint("parse_header: could not parse time portion")
+ return
+ end
+
+ local ptype, ttype = IPv4, UDP
+
+ -- get phy/vlan if present
+ -- first skip past time portion
+ local phy, vlan, i, j, k
+ line_pos = line_pos + 1
+ i, j, phy, vlan = line:find(header_phy_pattern, line_pos)
+ if i then
+ phy = tonumber(phy)
+ vlan = tonumber(vlan)
+ line_pos = j -- skip past this portion for next match
+ else
+ -- if there's no phy/vlan info, then assume it's TCP (unless it's loopback address we'll check later)
+ ttype = TCP
+ end
+
+ -- get addresses and direction
+ local local_ip, local_port, direction, remote_ip, remote_port = line:match(header_address_pattern, line_pos)
+ if not local_ip then
+ -- try IPv6
+ local_ip, local_port, direction, remote_ip, remote_port = line:match(header_v6address_pattern, line_pos)
+ if not local_ip then
+ dprint("parse_header: could not parse address portion")
+ return nil
+ end
+ ptype = IPv6
+ end
+
+ if local_ip:find(loopback_pattern) and remote_ip:find(loopback_pattern) then
+ -- internal loopback packets never have phy/vlan but are always UDP messages (for all intents)
+ ttype = UDP
+ end
+
+ -- override above decisions based on configuration
+ if ALWAYS_UDP then
+ ttype = UDP
+ end
+
+ direction = get_direction(direction)
+ if direction == nil then
+ dprint("parse_header: failed to convert direction")
+ return nil
+ end
+
+ local source_ip, source_port, dest_ip, dest_port = local_ip, local_port, remote_ip, remote_port
+ if direction == RECV then
+ -- swap them
+ source_ip, source_port, dest_ip, dest_port = remote_ip, remote_port, local_ip, local_port
+ end
+ -- convert
+ source_port = tonumber(source_port)
+ dest_port = tonumber(dest_port)
+
+ -- peek at next line to determine packet type
+ local position = file:seek()
+ line = file:read()
+ dprint2("parse_header: peeking at line='", line, "'")
+ packet_class = get_packet_class(line)
+ file:seek("set", position) -- go back
+
+ dprint2("parse_header calling packet_class.new with:",
+ tostring(timestamp), direction, source_ip, source_port,
+ dest_ip, dest_port, ptype, ttype, file_position)
+
+ local packet = packet_class.new(state, timestamp, direction, source_ip, source_port, dest_ip, dest_port, ptype, ttype, file_position)
+ if not packet then
+ dprint("parse_header: parser failed to create Packet object")
+ end
+
+ if ttype == TCP then
+ -- if the packet is tcp type, then set the key for TCB table lookup
+ packet:set_tcbkey(table.concat({ "[", local_ip, "]:", local_port, "->[", remote_ip, "]:", remote_port }))
+ end
+
+ return packet
+end
+
+
+----------------------------------------
+-- file handling functions for Wireshark to use
+
+-- The read_open is called by Wireshark once per file, to see if the file is this reader's type.
+-- It passes in (1) a File and (2) CaptureInfo object to this function
+-- Since there is no exact magic sequence to search for, we have to use heuristics to guess if the file
+-- is our type or not, which we do by parsing a message header.
+-- Since Wireshark uses the file cursor position for future reading of this file, we also have to seek back to the beginning
+-- so that our normal read() function works correctly.
+local function read_open(file, capture)
+ dprint2("read_open called")
+ -- save current position to return later
+ local position = file:seek()
+
+ local line = file:read()
+ if not line then return false end
+
+ dprint2("read_open: got this line begin:\n'", line, "'")
+
+ line, position = skip_ahead(file, line, position)
+ if not line then return false end
+
+ dprint2("read_open: got this line after skip:\n'", line, "', with position=", position)
+
+ local state = State.new()
+
+ if parse_header(state, file, line, position) then
+ dprint2("read_open success")
+
+ file:seek("set",position)
+
+ capture.time_precision = wtap_filetypes.TSPREC_MSEC -- for millisecond precision
+ capture.encap = wtap.RAW_IP -- whole file is raw IP format
+ capture.snapshot_length = 0 -- unknown snaplen
+ capture.comment = "Oracle Acme Packet SBC message log"
+ capture.os = "VxWorks or Linux"
+ capture.hardware = "Oracle Acme Packet SBC"
+
+ -- reset state variables
+ capture.private_table = State.new()
+
+ dprint2("read_open returning true")
+ return true
+ end
+
+ dprint2("read_open returning false")
+ return false
+end
+
+----------------------------------------
+-- this is used by both read() and seek_read()
+local function read_common(funcname, file, capture, frame, position, seeking)
+ dprint2(funcname, "read_common called")
+ local state = capture.private_table
+
+ if not state then
+ dprint(funcname, "error getting capture state")
+ return false
+ end
+
+ local line = file:read()
+ if not line then
+ dprint(funcname, "hit end of file")
+ return false
+ end
+ line, position = skip_ahead(file, line, position)
+ if not line then
+ if file:read(0) ~= nil then
+ dprint(funcname, "did not hit end of file after skipping but ending anyway")
+ else
+ dprint2(funcname, "hit end of file after skipping")
+ end
+ return false
+ end
+
+ dprint2(funcname, ": parsing line='", line, "'")
+ local phdr = parse_header(state, file, line, position, seeking)
+ if not phdr then
+ dprint(funcname, "failed to parse header")
+ return false
+ end
+
+ line = file:read()
+
+ dprint2(funcname,": calling class object's read_data()")
+ phdr:read_data(file, frame, line, seeking)
+
+ if not phdr:set_wslua_fields(frame) then
+ dprint(funcname, "failed to set Wireshark packet header info")
+ return
+ end
+
+ dprint2(funcname, "read_common returning position")
+ return position
+end
+
+----------------------------------------
+-- Wireshark/tshark calls read() for each frame/record in the file
+-- It passes in (1) a File, (2) CaptureInfo, and (3) a FrameInfo object to this function
+-- It expects in return the file offset position the record starts at,
+-- or nil/false if there's an error or end-of-file is reached.
+-- The offset position is used later: wireshark remembers it and gives
+-- it to seek_read() at various random times
+local function read(file, capture, frame)
+ dprint2("read called")
+ local position = file:seek()
+ position = read_common("read", file, capture, frame, position)
+ if not position then
+ if file:read(0) ~= nil then
+ dprint("read failed to call read_common")
+ else
+ dprint2("read: reached end of file")
+ end
+ return false
+ end
+ return position
+end
+
+----------------------------------------
+-- Wireshark/tshark calls seek_read() for each frame/record in the file, at random times
+-- It passes in (1) File, (2) CaptureInfo, (3) FrameInfo, and (4) the offset position number
+-- It expects in return true for successful parsing, or nil/false if there's an error.
+local function seek_read(file, capture, frame, offset)
+ dprint2("seek_read called")
+ file:seek("set",offset)
+ if not read_common("seek_read", file, capture, frame, offset, true) then
+ dprint("seek_read failed to call read_common")
+ return false
+ end
+ return true
+end
+
+----------------------------------------
+-- Wireshark/tshark calls read_close() when it's closing the file completely
+-- It passes in (1) a File and (2) CaptureInfo object to this function
+-- this is a good opportunity to clean up any state you may have created during
+-- file reading.
+-- In our case there *is* state to reset, but we only saved it in
+-- the capture.private_table, so Wireshark will clean it up for us.
+local function read_close(file, capture)
+ dprint2("read_close called")
+ return true
+end
+
+----------------------------------------
+-- An often unused function, Wireshark calls this when the sequential walk-through is over
+-- It passes in (1) a File and (2) CaptureInfo object to this function
+-- (i.e., no more calls to read(), only to seek_read()).
+-- In our case there *is* some state to reset, but we only saved it in
+-- the capture.private_table, so Wireshark will clean it up for us.
+local function seq_read_close(file, capture)
+ dprint2("seq_read_close called")
+ return true
+end
+
+-- set above functions to the FileHandler
+fh.read_open = read_open
+fh.read = read
+fh.seek_read = seek_read
+fh.read_close = read_close
+fh.seq_read_close = seq_read_close
+fh.extensions = "log" -- this is just a hint
+
+-- and finally, register the FileHandler!
+register_filehandler(fh)
diff --git a/test/lua/add_packet_field.lua b/test/lua/add_packet_field.lua
new file mode 100644
index 0000000..d1a2a6c
--- /dev/null
+++ b/test/lua/add_packet_field.lua
@@ -0,0 +1,899 @@
+--[[
+ The tree:add_packet_field() method returns a value and offset in addition to a tree item.
+ This file tests whether the value and offset are correct. As for the value,
+ its correctness is tested in several ways for a given input.
+
+ 1. The returned value should match a precomputed value
+
+ 2. The returned value should match the value obtained from a Field object
+ right after tree:add_packet_field() is called
+
+ 3. The returned value should match the value obtained from a Field object
+ right after tree:add() is called with the same input as tree:add_packet_field()
+
+ 4. The returned value should match the value obtained from the corresponding value function
+ called on the input tvbrange
+
+ There are some incompatibilties and limitations due to handling of encodings.
+ Incompatibilities are noted with the text INCOMPATIBILITY in a nearby comment.
+]]
+
+local field_setup = require "field_setup"
+
+--[[
+ This dissector expects a capture with at least one packet on UDP 65333.
+ All the actual test data is synthetic.
+]]--
+local myproto = Proto("test", "Test")
+
+field_data = field_setup(myproto, "test")
+
+function hexlify_string(s)
+ local sep = ""
+ local hx = ""
+ for i=1,#s do
+ hx = hx .. sep .. string.format("%02x", s:byte(i))
+ sep = " "
+ end
+ return hx
+end
+
+--[[
+ Ensure the value is represented in a way that shows up when printed.
+ It is assumed the string representation is relatively short.
+
+ The test suite will report an error if we print invalid utf8 for any reason.
+ We work around this by passing a substitution string used when the real
+ string has invalid utf8. We also print the output bytes in hex after the string,
+ and those bytes are always faithful to the real output.
+]]--
+function format_value_for_print(v, substitution)
+ local t = type(v)
+ local s
+ if t == "string" then
+ local hx = hexlify_string(v)
+ if substitution ~= nil then
+ s = string.format("(invalid utf8) \"%s\" [%s]", substitution, hx)
+ else
+ s = string.format("\"%s\" [%s]", v, hx)
+ end
+
+ else
+ s = tostring(v)
+ end
+ return string.format("(%s) %s", type(v), s)
+end
+
+function format_encoding_for_print(enc)
+
+ local char_enc = "ASCII"
+ if bit.band(enc, ENC_UTF_16) ~= 0 then
+ char_enc = "UTF-16"
+ end
+
+ local enc_enc = "BE"
+ if bit.band(enc, ENC_LITTLE_ENDIAN) ~= 0 then
+ end_enc = "LE"
+ end
+
+ if enc == ENC_ISO_8601_DATE_TIME then
+ char_enc = "ISO_8601"
+ end_enc = "-"
+ end
+
+ return string.format("%s %s", char_enc, end_enc)
+end
+
+function print_test_data(test_data)
+ print(string.format("TEST: using field type: %s", test_data.field_type))
+ if test_data.hexlify then
+ print(string.format("TEST: input was hexlified from: \"%s\"", test_data.original_input))
+ end
+ print(string.format("TEST: using data: [%s]", test_data.input))
+ print(string.format("TEST: using offset: %d", test_data.input_offset))
+ print(string.format("TEST: using encoding: %s", format_encoding_for_print(test_data.encoding)))
+ print()
+end
+
+function general_equality_test(a, b)
+ return a == b
+end
+
+--equal or both nan
+function float_equality_test(a, b)
+ return a == b or (a ~= a and b ~= b)
+end
+
+function recent_field_value(t)
+ local values = {field_data[t].value_field()}
+ return values[#values].value
+end
+
+function add_packet_field_returns_precomputed_value(test_data)
+
+ print(string.format(" EXPECT: precomputed return value: %s", format_value_for_print(test_data.expect_precomputed)))
+ print(string.format(" OUTPUT: add_packet_field returned value: %s", format_value_for_print(test_data.returned_value)))
+
+ if test_data.equality_function(test_data.returned_value, test_data.expect_precomputed) then
+ print(" PASS: the return value is correct")
+ print()
+ return true
+ end
+
+ print(" FAIL: the returned value is incorrect")
+ print()
+ return false
+end
+
+function add_packet_field_then_value_field_returns_expected_value(test_data)
+
+ print(string.format(" EXPECT: value field value %s", format_value_for_print(test_data.expect_add_pf_field_value)))
+ print(string.format(" OUTPUT: value field after tree:add_packet_field() returned: %s",
+ format_value_for_print(test_data.returned_add_pf_field_value)))
+
+ local incompatible = test_data.expect_add_pf_field_value ~= test_data.expect_precomputed
+ if incompatible then
+ print(" WARNING: the value field does not return the same value as the other implementations")
+ end
+ if test_data.equality_function(test_data.returned_add_pf_field_value, test_data.expect_add_pf_field_value) then
+ print(" PASS: the value field is correct")
+ print()
+ return true
+ end
+
+ print(" FAIL: the value field is incorrect")
+ print()
+ return false
+end
+
+function tree_add_then_value_field_returns_expected_value(test_data)
+
+ if test_data.skip_tree_add_test then
+ print(" SKIP: " .. test_data.skip_tree_add_test_message)
+ print()
+ return true
+ end
+
+ print(string.format(" EXPECT: value field value %s", format_value_for_print(test_data.expect_add_field_value)))
+ print(string.format(" OUTPUT: value field after tree:add() returned: %s",
+ format_value_for_print(test_data.returned_add_field_value)))
+
+ local incompatible = test_data.expect_add_field_value ~= test_data.expect_precomputed
+ if incompatible then
+ print(" WARNING: the value field does not return the same value as the other implementations")
+ end
+ if test_data.equality_function(test_data.returned_add_field_value, test_data.expect_add_field_value) then
+ print(" PASS: the value field is correct")
+ print()
+ return true
+ end
+
+ print(" FAIL: the value field is incorrect")
+ print()
+ return false
+
+end
+
+--[[
+ The tvbrange:string() function can return invalid utf8 even when the input is valid.
+]]
+function tvbrange_returns_expected_value(test_data)
+
+ if test_data.tvbr_fn == nil then
+ print(" SKIP: no tvbrange function for this field type")
+ print()
+ return true
+ end
+
+ local tvbr_value, tvbr_fn_printable = test_data.tvbr_fn(test_data.input_tvbrange, test_data.encoding)
+ local pass = test_data.equality_function(tvbr_value, test_data.expect_tvbrange_value)
+ local incompatible = test_data.expect_tvbrange_value ~= test_data.expect_precomputed
+ local tvbr_value_printable = format_value_for_print(tvbr_value)
+ local expect_value_printable = format_value_for_print(test_data.expect_tvbrange_value, test_data.expect_tvbrange_value_printable)
+ if pass then
+ --if the outputs are equal, then the substitute is useable for both
+ tvbr_value_printable = format_value_for_print(tvbr_value, test_data.expect_tvbrange_value_printable)
+ end
+
+ print(string.format(" TEST: using tvbrange function %s", tvbr_fn_printable))
+ print(string.format(" EXPECT: tvbrange value %s", expect_value_printable))
+ print(string.format(" OUTPUT: tvbrange returned %s", tvbr_value_printable))
+ if incompatible then
+ print(" WARNING: the tvbr function is not compatible with the other implementations")
+ end
+
+ if pass then
+ print(" PASS: the the tvbr function works as expected")
+ print()
+ return true
+ end
+
+ print(" FAIL: the the tvbr function works as expected")
+ print()
+ return false
+end
+
+function add_packet_field_returns_correct_offset(test_data)
+
+ print(string.format(" EXPECT: offset %d", test_data.expect_offset))
+ print(string.format(" OUTPUT: add_packet_field returned offset %d", test_data.returned_offset))
+
+ if test_data.returned_offset == test_data.expect_offset then
+ print(" PASS: the returned offset is correct")
+ print()
+ return true
+ end
+
+ print(" FAIL: the returned offset is incorrect")
+ print()
+ return false
+end
+
+function add_packet_field_all_tests(tree, test_data)
+ print_test_data(test_data)
+ local ret = true
+ and add_packet_field_returns_precomputed_value(test_data)
+ and add_packet_field_then_value_field_returns_expected_value(test_data)
+ and tree_add_then_value_field_returns_expected_value(test_data)
+ and tvbrange_returns_expected_value(test_data)
+ and add_packet_field_returns_correct_offset(test_data)
+ return ret
+end
+
+function generate_test_data_for_case(tree, field_type, case, tvbr_fn, equality_function, use_offset)
+
+ local input = case.input
+ if case.hexlify then
+ input = hexlify_string(case.input)
+ end
+
+ local input_byte_length = string.len(input:gsub(" ", "")) / 2
+ local input_offset = 0
+ if use_offset then
+ input = "77 " .. input
+ input_offset = 1
+ end
+
+ local input_tvb = ByteArray.new(input):tvb()
+ local input_tvbrange
+
+ if case.fake_input_length == nil then
+ input_tvbrange = input_tvb(input_offset, input_byte_length)
+ else
+ input_tvbrange = input_tvb(input_offset, case.fake_input_length)
+ end
+
+ local t = field_data[field_type]
+ local add_pf_leaf, returned_value, returned_offset = tree:add_packet_field(t.packet_field, input_tvbrange, case.encoding)
+ local add_pf_field_value = recent_field_value(field_type)
+
+ local add_leaf = nil
+ local add_field_value = nil
+ local skip_tree_add_test_message = nil
+ local skip_tree_add_test = false
+
+ if case.encoding == ENC_ASCII + ENC_BIG_ENDIAN then
+ add_leaf = tree:add(t.packet_field, input_tvbrange)
+ add_field_value = recent_field_value(field_type)
+ elseif case.encoding == ENC_ASCII + ENC_LITTLE_ENDIAN then
+ add_leaf = tree:add_le(t.packet_field, input_tvbrange)
+ add_field_value = recent_field_value(field_type)
+ else
+ skip_tree_add_test = true
+ skip_tree_add_test_message = "tree:add() only uses ASCII encoding"
+ end
+
+ local expect_add_pf_field_value = case.output
+ if case.incompatible_add_pf_field then
+ expect_add_pf_field_value = case.expect_add_pf_field_value
+ end
+
+ local expect_add_field_value = case.output
+ if case.incompatible_add_field then
+ expect_add_field_value = case.expect_add_field_value
+ end
+
+ local expect_tvbrange_value = case.output
+ if case.incompatible_tvbrange then
+ expect_tvbrange_value = case.expect_tvbrange_value
+ end
+
+ local expect_offset = input_byte_length + input_offset
+ if case.variable_input_length then
+ expect_offset = case.input_length + input_offset
+ end
+
+ return {
+ field_type = field_type,
+ hexlify = case.hexlify,
+ original_input = case.input,
+ input = input,
+ input_offset = input_offset,
+ input_tvbrange = input_tvbrange,
+ encoding = case.encoding,
+
+ returned_value = returned_value,
+ returned_offset = returned_offset,
+ returned_add_pf_field_value = add_pf_field_value,
+ returned_add_field_value = add_field_value,
+
+ tvbr_fn = tvbr_fn,
+ equality_function = equality_function,
+ expect_precomputed = case.output,
+ expect_add_pf_field_value = expect_add_pf_field_value,
+
+ expect_add_field_value = expect_add_field_value,
+ skip_tree_add_test = skip_tree_add_test,
+ skip_tree_add_test_message = skip_tree_add_test_message,
+
+ expect_tvbrange_value = expect_tvbrange_value,
+ expect_tvbrange_value_printable = case.expect_tvbrange_value_printable,
+ expect_offset = expect_offset
+ }
+end
+
+function run_test_cases_all_tests(tree, field_type, test_cases, tvbr_fn, equality_function)
+ local test_data
+ for _ , case in ipairs(test_cases) do
+ test_data = generate_test_data_for_case(tree, field_type, case, tvbr_fn, equality_function, true)
+ if not add_packet_field_all_tests(tree, test_data) then
+ return false
+ end
+
+ test_data = generate_test_data_for_case(tree, field_type, case, tvbr_fn, equality_function, false)
+ if not add_packet_field_all_tests(tree, test_data) then
+ return false
+ end
+ end
+
+ return true
+end
+
+function simple_integer_tests(tree)
+ local uint8_test_cases = {
+ {input = "ff", encoding = ENC_LITTLE_ENDIAN, output = 0xff},
+ {input = "00", encoding = ENC_LITTLE_ENDIAN, output = 0x00},
+ {input = "ff", encoding = ENC_BIG_ENDIAN, output = 0xff},
+ {input = "00", encoding = ENC_BIG_ENDIAN, output = 0x00},
+ }
+
+ local uint16_test_cases = {
+ {input = "ff 00", encoding = ENC_LITTLE_ENDIAN, output = 0x00ff},
+ {input = "00 ff", encoding = ENC_LITTLE_ENDIAN, output = 0xff00},
+ {input = "ff 00", encoding = ENC_BIG_ENDIAN, output = 0xff00},
+ {input = "00 ff", encoding = ENC_BIG_ENDIAN, output = 0x00ff},
+ }
+
+ local uint24_test_cases = {
+ {input = "ff 00 00", encoding = ENC_LITTLE_ENDIAN, output = 0x0000ff},
+ {input = "00 ff 00", encoding = ENC_LITTLE_ENDIAN, output = 0x00ff00},
+ {input = "00 00 ff", encoding = ENC_LITTLE_ENDIAN, output = 0xff0000},
+ {input = "ff 00 00", encoding = ENC_BIG_ENDIAN, output = 0xff0000},
+ {input = "00 ff 00", encoding = ENC_BIG_ENDIAN, output = 0x00ff00},
+ {input = "00 00 ff", encoding = ENC_BIG_ENDIAN, output = 0x0000ff},
+ }
+
+ local uint32_test_cases = {
+ {input = "ff 00 00 00", encoding = ENC_LITTLE_ENDIAN, output = 0x000000ff},
+ {input = "00 ff 00 00", encoding = ENC_LITTLE_ENDIAN, output = 0x0000ff00},
+ {input = "00 00 ff 00", encoding = ENC_LITTLE_ENDIAN, output = 0x00ff0000},
+ {input = "00 00 00 ff", encoding = ENC_LITTLE_ENDIAN, output = 0xff000000},
+ {input = "ff 00 00 00", encoding = ENC_BIG_ENDIAN, output = 0xff000000},
+ {input = "00 ff 00 00", encoding = ENC_BIG_ENDIAN, output = 0x00ff0000},
+ {input = "00 00 ff 00", encoding = ENC_BIG_ENDIAN, output = 0x0000ff00},
+ {input = "00 00 00 ff", encoding = ENC_BIG_ENDIAN, output = 0x000000ff},
+ }
+
+ function tvbr_uint (tvbr, encoding)
+ if encoding == ENC_LITTLE_ENDIAN then
+ return tvbr:le_uint(), "le_uint()"
+ else
+ return tvbr:uint(), "uint()"
+ end
+ end
+
+ local int8_test_cases = {
+ {input = "ff", encoding = ENC_LITTLE_ENDIAN, output = -0x01},
+ {input = "00", encoding = ENC_LITTLE_ENDIAN, output = 0x00},
+ {input = "ff", encoding = ENC_BIG_ENDIAN, output = -0x01},
+ {input = "00", encoding = ENC_BIG_ENDIAN, output = 0x00},
+ }
+
+ local int16_test_cases = {
+ {input = "ff 00", encoding = ENC_LITTLE_ENDIAN, output = 0x00ff},
+ {input = "00 ff", encoding = ENC_LITTLE_ENDIAN, output = -0x0100},
+ {input = "ff 00", encoding = ENC_BIG_ENDIAN, output = -0x0100},
+ {input = "00 ff", encoding = ENC_BIG_ENDIAN, output = 0x00ff},
+ }
+
+ local int24_test_cases = {
+ {input = "ff 00 00", encoding = ENC_LITTLE_ENDIAN, output = 0x0000ff},
+ {input = "00 ff 00", encoding = ENC_LITTLE_ENDIAN, output = 0x00ff00},
+ {input = "00 00 ff", encoding = ENC_LITTLE_ENDIAN, output = -0x010000},
+ {input = "ff 00 00", encoding = ENC_BIG_ENDIAN, output = -0x010000},
+ {input = "00 ff 00", encoding = ENC_BIG_ENDIAN, output = 0x00ff00},
+ {input = "00 00 ff", encoding = ENC_BIG_ENDIAN, output = 0x0000ff},
+ }
+
+ local int32_test_cases = {
+ {input = "ff 00 00 00", encoding = ENC_LITTLE_ENDIAN, output = 0x000000ff},
+ {input = "00 ff 00 00", encoding = ENC_LITTLE_ENDIAN, output = 0x0000ff00},
+ {input = "00 00 ff 00", encoding = ENC_LITTLE_ENDIAN, output = 0x00ff0000},
+ {input = "00 00 00 ff", encoding = ENC_LITTLE_ENDIAN, output = -0x01000000},
+ {input = "ff 00 00 00", encoding = ENC_BIG_ENDIAN, output = -0x01000000},
+ {input = "00 ff 00 00", encoding = ENC_BIG_ENDIAN, output = 0x00ff0000},
+ {input = "00 00 ff 00", encoding = ENC_BIG_ENDIAN, output = 0x0000ff00},
+ {input = "00 00 00 ff", encoding = ENC_BIG_ENDIAN, output = 0x000000ff},
+ }
+
+ function tvbr_int(tvbr, encoding)
+ if encoding == ENC_LITTLE_ENDIAN then
+ return tvbr:le_int(), "le_int()"
+ else
+ return tvbr:int(), "int()"
+ end
+ end
+
+ return true
+ and run_test_cases_all_tests(tree, "uint8", uint8_test_cases, tvbr_uint, general_equality_test)
+ and run_test_cases_all_tests(tree, "uint16", uint16_test_cases, tvbr_uint, general_equality_test)
+ and run_test_cases_all_tests(tree, "uint24", uint24_test_cases, tvbr_uint, general_equality_test)
+ and run_test_cases_all_tests(tree, "uint32", uint32_test_cases, tvbr_uint, general_equality_test)
+
+ and run_test_cases_all_tests(tree, "int8", int8_test_cases, tvbr_int, general_equality_test)
+ and run_test_cases_all_tests(tree, "int16", int16_test_cases, tvbr_int, general_equality_test)
+ and run_test_cases_all_tests(tree, "int24", int24_test_cases, tvbr_int, general_equality_test)
+ and run_test_cases_all_tests(tree, "int32", int32_test_cases, tvbr_int, general_equality_test)
+end
+
+function integer64_tests(tree)
+
+ local uint64_test_cases = {
+ {input = "ff 00 00 00 00 00 00 00", encoding = ENC_LITTLE_ENDIAN, output = UInt64(0x000000ff, 0x00000000)},
+ {input = "00 ff 00 00 00 00 00 00", encoding = ENC_LITTLE_ENDIAN, output = UInt64(0x0000ff00, 0x00000000)},
+ {input = "00 00 ff 00 00 00 00 00", encoding = ENC_LITTLE_ENDIAN, output = UInt64(0x00ff0000, 0x00000000)},
+ {input = "00 00 00 ff 00 00 00 00", encoding = ENC_LITTLE_ENDIAN, output = UInt64(0xff000000, 0x00000000)},
+ {input = "00 00 00 00 ff 00 00 00", encoding = ENC_LITTLE_ENDIAN, output = UInt64(0x00000000, 0x000000ff)},
+ {input = "00 00 00 00 00 ff 00 00", encoding = ENC_LITTLE_ENDIAN, output = UInt64(0x00000000, 0x0000ff00)},
+ {input = "00 00 00 00 00 00 ff 00", encoding = ENC_LITTLE_ENDIAN, output = UInt64(0x00000000, 0x00ff0000)},
+ {input = "00 00 00 00 00 00 00 ff", encoding = ENC_LITTLE_ENDIAN, output = UInt64(0x00000000, 0xff000000)},
+ {input = "ff 00 00 00 00 00 00 00", encoding = ENC_BIG_ENDIAN, output = UInt64(0x00000000, 0xff000000)},
+ {input = "00 ff 00 00 00 00 00 00", encoding = ENC_BIG_ENDIAN, output = UInt64(0x00000000, 0x00ff0000)},
+ {input = "00 00 ff 00 00 00 00 00", encoding = ENC_BIG_ENDIAN, output = UInt64(0x00000000, 0x0000ff00)},
+ {input = "00 00 00 ff 00 00 00 00", encoding = ENC_BIG_ENDIAN, output = UInt64(0x00000000, 0x000000ff)},
+ {input = "00 00 00 00 ff 00 00 00", encoding = ENC_BIG_ENDIAN, output = UInt64(0xff000000, 0x00000000)},
+ {input = "00 00 00 00 00 ff 00 00", encoding = ENC_BIG_ENDIAN, output = UInt64(0x00ff0000, 0x00000000)},
+ {input = "00 00 00 00 00 00 ff 00", encoding = ENC_BIG_ENDIAN, output = UInt64(0x0000ff00, 0x00000000)},
+ {input = "00 00 00 00 00 00 00 ff", encoding = ENC_BIG_ENDIAN, output = UInt64(0x000000ff, 0x00000000)},
+ }
+
+ function tvbr_uint(tvbr, encoding)
+ if encoding == ENC_LITTLE_ENDIAN then
+ return tvbr:le_uint64(), "le_uint64()"
+ else
+ return tvbr:uint64(), "uint64()"
+ end
+ end
+
+ local int64_test_cases = {
+ {input = "ff 00 00 00 00 00 00 00", encoding = ENC_LITTLE_ENDIAN, output = Int64(0x000000ff, 0x00000000)},
+ {input = "00 ff 00 00 00 00 00 00", encoding = ENC_LITTLE_ENDIAN, output = Int64(0x0000ff00, 0x00000000)},
+ {input = "00 00 ff 00 00 00 00 00", encoding = ENC_LITTLE_ENDIAN, output = Int64(0x00ff0000, 0x00000000)},
+ {input = "00 00 00 ff 00 00 00 00", encoding = ENC_LITTLE_ENDIAN, output = Int64(0xff000000, 0x00000000)},
+ {input = "00 00 00 00 ff 00 00 00", encoding = ENC_LITTLE_ENDIAN, output = Int64(0x00000000, 0x000000ff)},
+ {input = "00 00 00 00 00 ff 00 00", encoding = ENC_LITTLE_ENDIAN, output = Int64(0x00000000, 0x0000ff00)},
+ {input = "00 00 00 00 00 00 ff 00", encoding = ENC_LITTLE_ENDIAN, output = Int64(0x00000000, 0x00ff0000)},
+ {input = "00 00 00 00 00 00 00 ff", encoding = ENC_LITTLE_ENDIAN, output = Int64(0x00000000, 0xff000000)},
+ {input = "ff 00 00 00 00 00 00 00", encoding = ENC_BIG_ENDIAN, output = Int64(0x00000000, 0xff000000)},
+ {input = "00 ff 00 00 00 00 00 00", encoding = ENC_BIG_ENDIAN, output = Int64(0x00000000, 0x00ff0000)},
+ {input = "00 00 ff 00 00 00 00 00", encoding = ENC_BIG_ENDIAN, output = Int64(0x00000000, 0x0000ff00)},
+ {input = "00 00 00 ff 00 00 00 00", encoding = ENC_BIG_ENDIAN, output = Int64(0x00000000, 0x000000ff)},
+ {input = "00 00 00 00 ff 00 00 00", encoding = ENC_BIG_ENDIAN, output = Int64(0xff000000, 0x00000000)},
+ {input = "00 00 00 00 00 ff 00 00", encoding = ENC_BIG_ENDIAN, output = Int64(0x00ff0000, 0x00000000)},
+ {input = "00 00 00 00 00 00 ff 00", encoding = ENC_BIG_ENDIAN, output = Int64(0x0000ff00, 0x00000000)},
+ {input = "00 00 00 00 00 00 00 ff", encoding = ENC_BIG_ENDIAN, output = Int64(0x000000ff, 0x00000000)},
+ }
+
+ function tvbr_int(tvbr, encoding)
+ if encoding == ENC_LITTLE_ENDIAN then
+ return tvbr:le_int64(), "le_int64()"
+ else
+ return tvbr:int64(), "int64()"
+ end
+ end
+
+ return true
+ and run_test_cases_all_tests(tree, "uint64", uint64_test_cases, tvbr_uint, general_equality_test)
+ and run_test_cases_all_tests(tree, "int64", int64_test_cases, tvbr_int, general_equality_test)
+end
+
+function string_tests(tree)
+
+ local ABC_ascii = "41 42 43"
+ local ABCzD_ascii = "41 42 43 00 44"
+
+ local SHARK_16_little = "b5 30 e1 30"
+ local SHARKzSA_16_little = "b5 30 e1 30 00 00 b5 30"
+
+ local SHARK_16_big = "30 b5 30 e1"
+ local SHARKzSA_16_big = "30 b5 30 e1 00 00 30 b5"
+
+ local string_test_cases = {
+ {input = ABC_ascii, encoding = ENC_ASCII, output = "ABC"},
+
+ {input = ABCzD_ascii, encoding = ENC_ASCII, output = "ABC"},
+
+ {input = SHARK_16_little, encoding = ENC_ASCII, output = "�0�0"},
+
+ {input = SHARK_16_little, encoding = ENC_UTF_16 + ENC_LITTLE_ENDIAN, output = "サメ"},
+
+ {input = SHARKzSA_16_little, encoding = ENC_UTF_16 + ENC_LITTLE_ENDIAN, output = "サメ"},
+
+ {input = SHARK_16_big, encoding = ENC_UTF_16 + ENC_BIG_ENDIAN, output = "サメ"},
+
+ {input = SHARKzSA_16_big, encoding = ENC_UTF_16 + ENC_BIG_ENDIAN, output = "サメ"},
+ }
+
+ function tvbr_string(tvbr, encoding)
+ return tvbr:string(encoding), string.format("string(%s)", format_encoding_for_print(encoding))
+ end
+
+ --[[
+ stringz computes its own input length by looking for null
+ the input length includes the null, which is 2 bytes for utf16
+ ]]--
+ local stringz_tests = {
+
+ {input = ABCzD_ascii, encoding = ENC_ASCII, output = "ABC",
+ variable_input_length = true, input_length = 4
+ },
+
+ {input = SHARKzSA_16_little, encoding = ENC_UTF_16 + ENC_LITTLE_ENDIAN, output = "サメ",
+ variable_input_length = true, input_length = 6,
+ },
+
+ {input = SHARKzSA_16_big, encoding = ENC_UTF_16 + ENC_BIG_ENDIAN, output = "サメ",
+ variable_input_length = true, input_length = 6,
+ },
+ }
+
+ function tvbr_stringz(tvbr, encoding)
+ return tvbr:stringz(encoding), string.format("stringz(%s)", format_encoding_for_print(encoding))
+ end
+
+ local ustring_tests = {
+ {input = SHARK_16_big, encoding = ENC_UTF_16 + ENC_BIG_ENDIAN, output = "サメ"},
+ {input = SHARKzSA_16_big, encoding = ENC_UTF_16 + ENC_BIG_ENDIAN, output = "サメ"},
+ }
+
+ function tvbr_ustring(tvbr, encoding)
+ return tvbr:ustring(), "ustring()"
+ end
+
+ local le_ustring_tests = {
+ {input = SHARK_16_little, encoding = ENC_UTF_16 + ENC_LITTLE_ENDIAN, output = "サメ"},
+ {input = SHARKzSA_16_little, encoding = ENC_UTF_16 + ENC_LITTLE_ENDIAN, output = "サメ"},
+ }
+
+ function tvbr_le_ustring(tvbr, encoding)
+ return tvbr:le_ustring(), "le_ustring()"
+ end
+
+ local ustringz_tests = {
+ {input = SHARKzSA_16_big, encoding = ENC_UTF_16 + ENC_BIG_ENDIAN, output = "サメ",
+ variable_input_length = true, input_length = 6
+ },
+ }
+
+ function tvbr_ustringz(tvbr, encoding)
+ return tvbr:ustringz(), "ustringz()"
+ end
+
+ local le_ustringz_tests = {
+ {input = SHARKzSA_16_little, encoding = ENC_UTF_16 + ENC_LITTLE_ENDIAN, output = "サメ",
+ variable_input_length = true, input_length = 6
+ },
+ }
+
+ function tvbr_le_ustringz(tvbr, encoding)
+ return tvbr:le_ustringz(), "le_ustringz()"
+ end
+
+ return true
+ and run_test_cases_all_tests(tree, "string", string_test_cases, tvbr_string, general_equality_test)
+ and run_test_cases_all_tests(tree, "stringz", stringz_tests, tvbr_stringz, general_equality_test)
+ and run_test_cases_all_tests(tree, "string", ustring_tests, tvbr_ustring, general_equality_test)
+ and run_test_cases_all_tests(tree, "string", le_ustring_tests, tvbr_le_ustring, general_equality_test)
+ and run_test_cases_all_tests(tree, "stringz", ustringz_tests, tvbr_ustringz, general_equality_test)
+ and run_test_cases_all_tests(tree, "stringz", le_ustringz_tests, tvbr_le_ustringz, general_equality_test)
+end
+
+function bool_char_tests(tree)
+
+ local bool_tests = {
+ {input = "ff", encoding = ENC_BIG_ENDIAN, output = true},
+ {input = "00", encoding = ENC_BIG_ENDIAN, output = false},
+ {input = "01", encoding = ENC_BIG_ENDIAN, output = true},
+ {input = "ff", encoding = ENC_LITTLE_ENDIAN, output = true},
+ {input = "00", encoding = ENC_LITTLE_ENDIAN, output = false},
+ {input = "01", encoding = ENC_LITTLE_ENDIAN, output = true},
+ }
+
+ local char_tests = {
+ {input = "ff", encoding = ENC_BIG_ENDIAN, output = 0xff},
+ {input = "00", encoding = ENC_BIG_ENDIAN, output = 0x00},
+ {input = "30", encoding = ENC_BIG_ENDIAN, output = 0x30},
+ {input = "ff", encoding = ENC_LITTLE_ENDIAN, output = 0xff},
+ {input = "00", encoding = ENC_LITTLE_ENDIAN, output = 0x00},
+ {input = "30", encoding = ENC_LITTLE_ENDIAN, output = 0x30},
+ }
+
+ return true
+ and run_test_cases_all_tests(tree, "boolean", bool_tests, nil, general_equality_test)
+ and run_test_cases_all_tests(tree, "char", char_tests, nil, general_equality_test)
+end
+
+function float_tests(tree)
+
+ local be_float = {
+ {input = "3c 00 00 00", encoding = ENC_BIG_ENDIAN, output = 0.0078125},
+ {input = "bd a0 00 00", encoding = ENC_BIG_ENDIAN, output = -0.078125},
+ {input = "3f 48 00 00", encoding = ENC_BIG_ENDIAN, output = 0.78125},
+ {input = "c0 fa 00 00", encoding = ENC_BIG_ENDIAN, output = -7.8125},
+ {input = "42 9c 40 00", encoding = ENC_BIG_ENDIAN, output = 78.125},
+ {input = "c4 43 50 00", encoding = ENC_BIG_ENDIAN, output = -781.25},
+ {input = "45 f4 24 00", encoding = ENC_BIG_ENDIAN, output = 7812.5},
+ {input = "c7 98 96 80", encoding = ENC_BIG_ENDIAN, output = -78125.0},
+ {input = "49 3e bc 20", encoding = ENC_BIG_ENDIAN, output = 781250.0},
+ {input = "ca ee 6b 28", encoding = ENC_BIG_ENDIAN, output = -7812500.0},
+ {input = "00 00 00 00", encoding = ENC_BIG_ENDIAN, output = 0.0},
+ {input = "80 00 00 00", encoding = ENC_BIG_ENDIAN, output = -0.0},
+ {input = "7f c0 00 00", encoding = ENC_BIG_ENDIAN, output = 0/0},
+ {input = "7f 80 00 00", encoding = ENC_BIG_ENDIAN, output = 1/0},
+ {input = "ff 80 00 00", encoding = ENC_BIG_ENDIAN, output = -1/0},
+ }
+
+ local le_float = {
+ {input = "00 00 00 3c", encoding = ENC_LITTLE_ENDIAN, output = 0.0078125},
+ {input = "00 00 a0 bd", encoding = ENC_LITTLE_ENDIAN, output = -0.078125},
+ {input = "00 00 48 3f", encoding = ENC_LITTLE_ENDIAN, output = 0.78125},
+ {input = "00 00 fa c0", encoding = ENC_LITTLE_ENDIAN, output = -7.8125},
+ {input = "00 40 9c 42", encoding = ENC_LITTLE_ENDIAN, output = 78.125},
+ {input = "00 50 43 c4", encoding = ENC_LITTLE_ENDIAN, output = -781.25},
+ {input = "00 24 f4 45", encoding = ENC_LITTLE_ENDIAN, output = 7812.5},
+ {input = "80 96 98 c7", encoding = ENC_LITTLE_ENDIAN, output = -78125.0},
+ {input = "20 bc 3e 49", encoding = ENC_LITTLE_ENDIAN, output = 781250.0},
+ {input = "28 6b ee ca", encoding = ENC_LITTLE_ENDIAN, output = -7812500.0},
+ {input = "00 00 00 00", encoding = ENC_LITTLE_ENDIAN, output = 0.0},
+ {input = "00 00 00 80", encoding = ENC_LITTLE_ENDIAN, output = -0.0},
+ {input = "00 00 c0 7f", encoding = ENC_LITTLE_ENDIAN, output = 0/0},
+ {input = "00 00 80 7f", encoding = ENC_LITTLE_ENDIAN, output = 1/0},
+ {input = "00 00 80 ff", encoding = ENC_LITTLE_ENDIAN, output = -1/0},
+ }
+
+ local be_double = {
+ {input = "3f 80 00 00 00 00 00 00", encoding = ENC_BIG_ENDIAN, output = 0.0078125},
+ {input = "bf e9 00 00 00 00 00 00", encoding = ENC_BIG_ENDIAN, output = -0.78125},
+ {input = "40 88 6a 00 00 00 00 00", encoding = ENC_BIG_ENDIAN, output = 781.25},
+ {input = "c0 f3 12 d0 00 00 00 00", encoding = ENC_BIG_ENDIAN, output = -78125.0},
+ {input = "41 92 a0 5f 20 00 00 00", encoding = ENC_BIG_ENDIAN, output = 78125000.0},
+ {input = "c1 fd 1a 94 a2 00 00 00", encoding = ENC_BIG_ENDIAN, output = -7812500000.0},
+ {input = "42 9c 6b f5 26 34 00 00", encoding = ENC_BIG_ENDIAN, output = 7812500000000.0},
+ {input = "c3 06 34 57 85 d8 a0 00", encoding = ENC_BIG_ENDIAN, output = -781250000000000.0},
+ {input = "43 a5 af 1d 78 b5 8c 40", encoding = ENC_BIG_ENDIAN, output = 7.8125e+17},
+ {input = "c4 10 f0 cf 06 4d d5 92", encoding = ENC_BIG_ENDIAN, output = -7.8125e+19},
+ {input = "00 00 00 00 00 00 00 00", encoding = ENC_BIG_ENDIAN, output = 0.0},
+ {input = "80 00 00 00 00 00 00 00", encoding = ENC_BIG_ENDIAN, output = -0.0},
+ {input = "7f f8 00 00 00 00 00 00", encoding = ENC_BIG_ENDIAN, output = 0/0},
+ {input = "7f f0 00 00 00 00 00 00", encoding = ENC_BIG_ENDIAN, output = 1/0},
+ {input = "ff f0 00 00 00 00 00 00", encoding = ENC_BIG_ENDIAN, output = -1/0},
+ }
+
+ local le_double = {
+ {input = "00 00 00 00 00 00 80 3f", encoding = ENC_LITTLE_ENDIAN, output = 0.0078125},
+ {input = "00 00 00 00 00 00 e9 bf", encoding = ENC_LITTLE_ENDIAN, output = -0.78125},
+ {input = "00 00 00 00 00 6a 88 40", encoding = ENC_LITTLE_ENDIAN, output = 781.25},
+ {input = "00 00 00 00 d0 12 f3 c0", encoding = ENC_LITTLE_ENDIAN, output = -78125.0},
+ {input = "00 00 00 20 5f a0 92 41", encoding = ENC_LITTLE_ENDIAN, output = 78125000.0},
+ {input = "00 00 00 a2 94 1a fd c1", encoding = ENC_LITTLE_ENDIAN, output = -7812500000.0},
+ {input = "00 00 34 26 f5 6b 9c 42", encoding = ENC_LITTLE_ENDIAN, output = 7812500000000.0},
+ {input = "00 a0 d8 85 57 34 06 c3", encoding = ENC_LITTLE_ENDIAN, output = -781250000000000.0},
+ {input = "40 8c b5 78 1d af a5 43", encoding = ENC_LITTLE_ENDIAN, output = 7.8125e+17},
+ {input = "92 d5 4d 06 cf f0 10 c4", encoding = ENC_LITTLE_ENDIAN, output = -7.8125e+19},
+ {input = "00 00 00 00 00 00 00 00", encoding = ENC_LITTLE_ENDIAN, output = 0.0},
+ {input = "00 00 00 00 00 00 00 80", encoding = ENC_LITTLE_ENDIAN, output = -0.0},
+ {input = "00 00 00 00 00 00 f8 7f", encoding = ENC_LITTLE_ENDIAN, output = 0/0},
+ {input = "00 00 00 00 00 00 f0 7f", encoding = ENC_LITTLE_ENDIAN, output = 1/0},
+ {input = "00 00 00 00 00 00 f0 ff", encoding = ENC_LITTLE_ENDIAN, output = -1/0},
+ }
+
+ function tvbr_float(tvbr, encoding)
+ return tvbr:float(), "float()"
+ end
+
+ function tvbr_le_float(tvbr, encoding)
+ return tvbr:le_float(), "le_float()"
+ end
+
+ return true
+ and run_test_cases_all_tests(tree, "float", be_float, tvbr_float, float_equality_test)
+ and run_test_cases_all_tests(tree, "double", be_double, tvbr_float, float_equality_test)
+ and run_test_cases_all_tests(tree, "float", le_float, tvbr_le_float, float_equality_test)
+ and run_test_cases_all_tests(tree, "double", le_double, tvbr_le_float, float_equality_test)
+end
+
+function address_tests(tree)
+
+ --INCOMPATIBILITY: value fields always assume big-endian encoding for IPv4 addresses
+ local ipv4_test_cases = {
+ {input = "01 00 00 00", encoding = ENC_LITTLE_ENDIAN, output = Address.ip("0.0.0.1"),
+ incompatible_add_pf_field = true, expect_add_pf_field_value = Address.ip("1.0.0.0"),
+ incompatible_add_field = true, expect_add_field_value = Address.ip("1.0.0.0")
+ },
+ {input = "00 02 00 00", encoding = ENC_LITTLE_ENDIAN, output = Address.ip("0.0.2.0"),
+ incompatible_add_pf_field = true, expect_add_pf_field_value = Address.ip("0.2.0.0"),
+ incompatible_add_field = true, expect_add_field_value = Address.ip("0.2.0.0")
+ },
+ {input = "00 00 03 00", encoding = ENC_LITTLE_ENDIAN, output = Address.ip("0.3.0.0"),
+ incompatible_add_pf_field = true, expect_add_pf_field_value = Address.ip("0.0.3.0"),
+ incompatible_add_field = true, expect_add_field_value = Address.ip("0.0.3.0")
+ },
+ {input = "00 00 00 04", encoding = ENC_LITTLE_ENDIAN, output = Address.ip("4.0.0.0"),
+ incompatible_add_pf_field = true, expect_add_pf_field_value = Address.ip("0.0.0.4"),
+ incompatible_add_field = true, expect_add_field_value = Address.ip("0.0.0.4")
+ },
+ {input = "01 00 00 00", encoding = ENC_BIG_ENDIAN, output = Address.ip("1.0.0.0")},
+ {input = "00 02 00 00", encoding = ENC_BIG_ENDIAN, output = Address.ip("0.2.0.0")},
+ {input = "00 00 03 00", encoding = ENC_BIG_ENDIAN, output = Address.ip("0.0.3.0")},
+ {input = "00 00 00 04", encoding = ENC_BIG_ENDIAN, output = Address.ip("0.0.0.4")},
+ }
+
+ function tvbr_ipv4 (tvbr, encoding)
+ if encoding == ENC_LITTLE_ENDIAN then
+ return tvbr:le_ipv4(), "le_ipv4()"
+ else
+ return tvbr:ipv4(), "ipv4()"
+ end
+ end
+
+ local ipv6_test_cases = {
+ {encoding = ENC_BIG_ENDIAN, input = "0000 0000 0000 0000 0000 0000 0000 00ff",
+ output = Address.ipv6("0000:0000:0000:0000:0000:0000:0000:00ff")},
+ {encoding = ENC_BIG_ENDIAN, input = "0000 0000 0000 0000 0000 0000 0000 ff00",
+ output = Address.ipv6("0000:0000:0000:0000:0000:0000:0000:ff00")},
+ {encoding = ENC_BIG_ENDIAN, input = "0000 0000 0000 0000 0000 0000 00ff 0000",
+ output = Address.ipv6("0000:0000:0000:0000:0000:0000:00ff:0000")},
+ {encoding = ENC_BIG_ENDIAN, input = "0000 0000 0000 0000 0000 0000 ff00 0000",
+ output = Address.ipv6("0000:0000:0000:0000:0000:0000:ff00:0000")},
+ {encoding = ENC_BIG_ENDIAN, input = "0000 0000 0000 0000 0000 00ff 0000 0000",
+ output = Address.ipv6("0000:0000:0000:0000:0000:00ff:0000:0000")},
+ {encoding = ENC_BIG_ENDIAN, input = "0000 0000 0000 0000 0000 ff00 0000 0000",
+ output = Address.ipv6("0000:0000:0000:0000:0000:ff00:0000:0000")},
+ {encoding = ENC_BIG_ENDIAN, input = "0000 0000 0000 0000 00ff 0000 0000 0000",
+ output = Address.ipv6("0000:0000:0000:0000:00ff:0000:0000:0000")},
+ {encoding = ENC_BIG_ENDIAN, input = "0000 0000 0000 0000 ff00 0000 0000 0000",
+ output = Address.ipv6("0000:0000:0000:0000:ff00:0000:0000:0000")},
+ {encoding = ENC_BIG_ENDIAN, input = "0000 0000 0000 00ff 0000 0000 0000 0000",
+ output = Address.ipv6("0000:0000:0000:00ff:0000:0000:0000:0000")},
+ {encoding = ENC_BIG_ENDIAN, input = "0000 0000 0000 ff00 0000 0000 0000 0000",
+ output = Address.ipv6("0000:0000:0000:ff00:0000:0000:0000:0000")},
+ {encoding = ENC_BIG_ENDIAN, input = "0000 0000 00ff 0000 0000 0000 0000 0000",
+ output = Address.ipv6("0000:0000:00ff:0000:0000:0000:0000:0000")},
+ {encoding = ENC_BIG_ENDIAN, input = "0000 0000 ff00 0000 0000 0000 0000 0000",
+ output = Address.ipv6("0000:0000:ff00:0000:0000:0000:0000:0000")},
+ {encoding = ENC_BIG_ENDIAN, input = "0000 00ff 0000 0000 0000 0000 0000 0000",
+ output = Address.ipv6("0000:00ff:0000:0000:0000:0000:0000:0000")},
+ {encoding = ENC_BIG_ENDIAN, input = "0000 ff00 0000 0000 0000 0000 0000 0000",
+ output = Address.ipv6("0000:ff00:0000:0000:0000:0000:0000:0000")},
+ {encoding = ENC_BIG_ENDIAN, input = "00ff 0000 0000 0000 0000 0000 0000 0000",
+ output = Address.ipv6("00ff:0000:0000:0000:0000:0000:0000:0000")},
+ {encoding = ENC_BIG_ENDIAN, input = "ff00 0000 0000 0000 0000 0000 0000 0000",
+ output = Address.ipv6("ff00:0000:0000:0000:0000:0000:0000:0000")},
+ }
+
+ function tvbr_ipv6 (tvbr, encoding)
+ return tvbr:ipv6(), "ipv6()"
+ end
+
+ local ether_test_cases = {
+ {input = "ff 00 00 00 00 00", encoding = 0, output = Address.ether("ff:00:00:00:00:00")},
+ {input = "00 ff 00 00 00 00", encoding = 0, output = Address.ether("00:ff:00:00:00:00")},
+ {input = "00 00 ff 00 00 00", encoding = 0, output = Address.ether("00:00:ff:00:00:00")},
+ {input = "00 00 00 ff 00 00", encoding = 0, output = Address.ether("00:00:00:ff:00:00")},
+ {input = "00 00 00 00 ff 00", encoding = 0, output = Address.ether("00:00:00:00:ff:00")},
+ {input = "00 00 00 00 00 ff", encoding = 0, output = Address.ether("00:00:00:00:00:ff")},
+ }
+
+ function tvbr_ether (tvbr, encoding)
+ return tvbr:ether(), "ether()"
+ end
+
+ return true
+ and run_test_cases_all_tests(tree, "ipv4", ipv4_test_cases, tvbr_ipv4, general_equality_test)
+ and run_test_cases_all_tests(tree, "ipv6", ipv6_test_cases, tvbr_ipv6, general_equality_test)
+ and run_test_cases_all_tests(tree, "ether", ether_test_cases, tvbr_ether, general_equality_test)
+end
+
+function time_tests(tree)
+
+ local time_cases = {
+ {input ="00 01 02 03", encoding = ENC_BIG_ENDIAN, output = NSTime(0x00010203,0)},
+ {input ="03 02 01 00", encoding = ENC_LITTLE_ENDIAN, output = NSTime(0x00010203,0)},
+ {input ="00 01 02 03 04 05 06 07", encoding = ENC_BIG_ENDIAN, output = NSTime(0x00010203, 0x04050607)},
+ {input ="03 02 01 00 07 06 05 04", encoding = ENC_LITTLE_ENDIAN, output = NSTime(0x00010203, 0x04050607)},
+ }
+
+ local string_cases = {
+ {input = "1994-11-05T13:15:30Z", encoding = ENC_ISO_8601_DATE_TIME, output = NSTime(784041330, 0),
+ hexlify=true},
+ {input = "1994-11-05T13:15:30Z12345", encoding = ENC_ISO_8601_DATE_TIME, output = NSTime(784041330, 0),
+ hexlify=true, variable_input_length = true, input_length = 20},
+ }
+
+ function tvbr_nstime(tvbr, encoding)
+ if encoding == ENC_LITTLE_ENDIAN then
+ return tvbr:le_nstime(), "le_nstime()"
+ else
+ return tvbr:nstime(encoding), string.format("nstime(%s)", format_encoding_for_print(encoding))
+ end
+ end
+
+ return true
+ and run_test_cases_all_tests(tree, "relative_time", time_cases, tvbr_nstime, general_equality_test)
+ and run_test_cases_all_tests(tree, "absolute_time", time_cases, tvbr_nstime, general_equality_test)
+ and run_test_cases_all_tests(tree, "absolute_time", string_cases, tvbr_nstime, general_equality_test)
+end
+
+function bytearray_tests(tree)
+
+ local bytes_tests = {
+ {input = "00 01 02 03 ff", encoding = 0, output = ByteArray.new("00 01 02 03 ff")}
+ }
+
+ function tvbr_bytes(tvbr, encoding)
+ return tvbr:bytes(), "bytes()"
+ end
+
+ local varbytes_tests = {
+ {input = "04 00 01 02 ff", encoding = ENC_BIG_ENDIAN,
+ output = ByteArray.new("00 01 02 ff"), fake_input_length = 1},
+ {input = "00 04 00 01 02 ff", encoding = ENC_BIG_ENDIAN,
+ output = ByteArray.new("00 01 02 ff"), fake_input_length = 2},
+ {input = "00 00 00 04 00 01 02 ff", encoding = ENC_BIG_ENDIAN,
+ output = ByteArray.new("00 01 02 ff"), fake_input_length = 4},
+ }
+
+ return true
+ and run_test_cases_all_tests(tree, "bytes", bytes_tests, tvbr_bytes, general_equality_test)
+ and run_test_cases_all_tests(tree, "oid", bytes_tests, tvbr_bytes, general_equality_test)
+ and run_test_cases_all_tests(tree, "rel_oid", bytes_tests, tvbr_bytes, general_equality_test)
+ and run_test_cases_all_tests(tree, "system_id", bytes_tests, tvbr_bytes, general_equality_test)
+ and run_test_cases_all_tests(tree, "uint_bytes", varbytes_tests, nil, general_equality_test)
+end
+
+function run_all_tests(tree)
+ return true
+ and simple_integer_tests(tree)
+ and integer64_tests(tree)
+ and string_tests(tree)
+ and bool_char_tests(tree)
+ and float_tests(tree)
+ and address_tests(tree)
+ and time_tests(tree)
+ and bytearray_tests(tree)
+end
+
+local has_run = false
+function myproto.dissector(tvb, pkt, root)
+ if has_run then
+ return
+ end
+ has_run = true
+ local tree = root:add(myproto, tvb(0))
+ if run_all_tests(tree) then
+ print("All tests passed!")
+ print()
+ end
+end
+
+DissectorTable.get("udp.port"):add(65333, myproto)
diff --git a/test/lua/byte_array.lua b/test/lua/byte_array.lua
new file mode 100644
index 0000000..8e4631b
--- /dev/null
+++ b/test/lua/byte_array.lua
@@ -0,0 +1,215 @@
+-- test script for ByteArray integer functions
+
+local testlib = require("testlib")
+
+local INT = "int"
+local UINT = "uint"
+local INT64 = "int64"
+local UINT64 = "uint64"
+local LE_INT = "le_int"
+local LE_UINT = "le_uint"
+local LE_INT64 = "le_int64"
+local LE_UINT64 = "le_uint64"
+
+-- expected number of runs per type
+local taptests = {
+ [INT]=14,
+ [UINT]=14,
+ [INT64]=15,
+ [UINT64]=15,
+ [LE_INT]=14,
+ [LE_UINT]=14,
+ [LE_INT64]=15,
+ [LE_UINT64]=15
+}
+testlib.init(taptests)
+
+local empty = ByteArray.new("")
+local be_data = ByteArray.new("FF 00 00 00 00 00 00 00")
+local le_data = ByteArray.new("00 00 00 00 00 00 00 FF")
+
+-- the following are so we can use pcall (which needs a function to call)
+
+local function ByteArray_int(array,offset,length)
+ local value = array:int(offset,length)
+end
+
+local function ByteArray_uint(array,offset,length)
+ local value = array:uint(offset,length)
+end
+
+local function ByteArray_int64(array,offset,length)
+ local value = array:int64(offset,length)
+end
+
+local function ByteArray_uint64(array,offset,length)
+ local value = array:uint64(offset,length)
+end
+
+local function ByteArray_le_int(array,offset,length)
+ local value = array:le_int(offset,length)
+end
+
+local function ByteArray_le_uint(array,offset,length)
+ local value = array:le_uint(offset,length)
+end
+
+local function ByteArray_le_int64(array,offset,length)
+ local value = array:le_int64(offset,length)
+end
+
+local function ByteArray_le_uint64(array,offset,length)
+ local value = array:le_uint64(offset,length)
+end
+
+------------- test script ------------
+
+testlib.testing(INT,"negative tests")
+testlib.test(INT,"ByteArray:int-0",not pcall(ByteArray_int, empty))
+testlib.test(INT,"ByteArray:int-1",not pcall(ByteArray_int, be_data))
+testlib.test(INT,"ByteArray:int-2",not pcall(ByteArray_int, be_data, -1))
+testlib.test(INT,"ByteArray:int-3",not pcall(ByteArray_int, be_data, 0))
+testlib.test(INT,"ByteArray:int-4",not pcall(ByteArray_int, be_data, 0, -1))
+testlib.test(INT,"ByteArray:int-5",not pcall(ByteArray_int, be_data, 0, 0))
+testlib.test(INT,"ByteArray:int-6",not pcall(ByteArray_int, be_data, 0, 5))
+testlib.test(INT,"ByteArray:int-7",not pcall(ByteArray_int, be_data, 7, 2))
+testlib.test(INT,"ByteArray:int-8",not pcall(ByteArray_int, be_data, 8, 1))
+
+testlib.testing(INT,"positive tests")
+testlib.test(INT,"ByteArray:int-9", be_data:int(0, 1) == -1)
+testlib.test(INT,"ByteArray:int-10", be_data:int(0, 2) == -256)
+testlib.test(INT,"ByteArray:int-11", be_data:int(0, 3) == -65536)
+testlib.test(INT,"ByteArray:int-12", be_data:int(0, 4) == -16777216)
+testlib.test(INT,"ByteArray:int-13", be_data:subset(2, 2):int() == 0)
+
+testlib.testing(UINT,"negative tests")
+testlib.test(UINT,"ByteArray:uint-0",not pcall(ByteArray_uint, empty))
+testlib.test(UINT,"ByteArray:uint-1",not pcall(ByteArray_uint, be_data))
+testlib.test(UINT,"ByteArray:uint-2",not pcall(ByteArray_uint, be_data, -1))
+testlib.test(UINT,"ByteArray:uint-3",not pcall(ByteArray_uint, be_data, 0))
+testlib.test(UINT,"ByteArray:uint-4",not pcall(ByteArray_uint, be_data, 0, -1))
+testlib.test(UINT,"ByteArray:uint-5",not pcall(ByteArray_uint, be_data, 0, 0))
+testlib.test(UINT,"ByteArray:uint-6",not pcall(ByteArray_uint, be_data, 0, 5))
+testlib.test(UINT,"ByteArray:uint-7",not pcall(ByteArray_uint, be_data, 7, 2))
+testlib.test(UINT,"ByteArray:uint-8",not pcall(ByteArray_uint, be_data, 8, 1))
+
+testlib.testing(UINT,"positive tests")
+testlib.test(UINT,"ByteArray:uint-9", be_data:uint(0, 1) == 255)
+testlib.test(UINT,"ByteArray:uint-10", be_data:uint(0, 2) == 65280)
+testlib.test(UINT,"ByteArray:uint-11", be_data:uint(0, 3) == 16711680)
+testlib.test(UINT,"ByteArray:uint-12", be_data:uint(0, 4) == 4278190080)
+testlib.test(UINT,"ByteArray:uint-13", be_data:subset(2, 2):uint() == 0)
+
+testlib.testing(INT64,"negative tests")
+testlib.test(INT64,"ByteArray:int64-0",not pcall(ByteArray_int64, empty))
+testlib.test(INT64,"ByteArray:int64-1",not pcall(ByteArray_int64, be_data, -1))
+testlib.test(INT64,"ByteArray:int64-2",not pcall(ByteArray_int64, be_data, 0, 0))
+testlib.test(INT64,"ByteArray:int64-3",not pcall(ByteArray_int64, be_data, 0, 9))
+testlib.test(INT64,"ByteArray:int64-4",not pcall(ByteArray_int64, be_data, 7, 2))
+testlib.test(INT64,"ByteArray:int64-5",not pcall(ByteArray_int64, be_data, 8, 1))
+
+testlib.testing(INT64,"positive tests")
+testlib.test(INT64,"ByteArray:int64-6", be_data:int64(0, 1):tonumber() == -1)
+testlib.test(INT64,"ByteArray:int64-7", be_data:int64(0, 2):tonumber() == -256)
+testlib.test(INT64,"ByteArray:int64-8", be_data:int64(0, 3):tonumber() == -65536)
+testlib.test(INT64,"ByteArray:int64-9", be_data:int64(0, 4):tonumber() == -16777216)
+testlib.test(INT64,"ByteArray:int64-10", be_data:int64(0, 5):tonumber() == -4294967296)
+testlib.test(INT64,"ByteArray:int64-11", be_data:int64(0, 6):tonumber() == -1099511627776)
+testlib.test(INT64,"ByteArray:int64-12", be_data:int64(0, 7):tonumber() == -281474976710656)
+testlib.test(INT64,"ByteArray:int64-13", be_data:int64():tonumber() == -72057594037927936)
+testlib.test(INT64,"ByteArray:int64-14", be_data:subset(2, 2):int64():tonumber() == 0)
+
+testlib.testing(UINT64,"negative tests")
+testlib.test(UINT64,"ByteArray:uint64-0",not pcall(ByteArray_uint64, empty))
+testlib.test(UINT64,"ByteArray:uint64-1",not pcall(ByteArray_uint64, be_data, -1))
+testlib.test(UINT64,"ByteArray:uint64-2",not pcall(ByteArray_uint64, be_data, 0, 0))
+testlib.test(UINT64,"ByteArray:uint64-3",not pcall(ByteArray_uint64, be_data, 0, 9))
+testlib.test(UINT64,"ByteArray:uint64-4",not pcall(ByteArray_uint64, be_data, 7, 2))
+testlib.test(UINT64,"ByteArray:uint64-5",not pcall(ByteArray_uint64, be_data, 8, 1))
+
+testlib.testing(UINT64,"positive tests")
+testlib.test(UINT64,"ByteArray:uint64-6", be_data:uint64(0, 1):tonumber() == 255)
+testlib.test(UINT64,"ByteArray:uint64-7", be_data:uint64(0, 2):tonumber() == 65280)
+testlib.test(UINT64,"ByteArray:uint64-8", be_data:uint64(0, 3):tonumber() == 16711680)
+testlib.test(UINT64,"ByteArray:uint64-9", be_data:uint64(0, 4):tonumber() == 4278190080)
+testlib.test(UINT64,"ByteArray:uint64-10", be_data:uint64(0, 5):tonumber() == 1095216660480)
+testlib.test(UINT64,"ByteArray:uint64-11", be_data:uint64(0, 6):tonumber() == 280375465082880)
+testlib.test(UINT64,"ByteArray:uint64-12", be_data:uint64(0, 7):tonumber() == 71776119061217280)
+testlib.test(UINT64,"ByteArray:uint64-13", be_data:uint64():tonumber() == 18374686479671623680)
+testlib.test(UINT64,"ByteArray:uint64-14", be_data:subset(2, 2):uint64():tonumber() == 0)
+
+testlib.testing(LE_INT,"negative tests")
+testlib.test(LE_INT,"ByteArray:le_int-0",not pcall(ByteArray_le_int, empty))
+testlib.test(LE_INT,"ByteArray:le_int-1",not pcall(ByteArray_le_int, le_data))
+testlib.test(LE_INT,"ByteArray:le_int-2",not pcall(ByteArray_le_int, le_data, -1))
+testlib.test(LE_INT,"ByteArray:le_int-3",not pcall(ByteArray_le_int, le_data, 0))
+testlib.test(LE_INT,"ByteArray:le_int-4",not pcall(ByteArray_le_int, le_data, 0, -1))
+testlib.test(LE_INT,"ByteArray:le_int-5",not pcall(ByteArray_le_int, le_data, 0, 0))
+testlib.test(LE_INT,"ByteArray:le_int-6",not pcall(ByteArray_le_int, le_data, 0, 5))
+testlib.test(LE_INT,"ByteArray:le_int-7",not pcall(ByteArray_le_int, le_data, 7, 2))
+testlib.test(LE_INT,"ByteArray:le_int-8",not pcall(ByteArray_le_int, le_data, 8, 1))
+
+testlib.testing(LE_INT,"positive tests")
+testlib.test(LE_INT,"ByteArray:le_int-9", le_data:le_int(7) == -1)
+testlib.test(LE_INT,"ByteArray:le_int-10", le_data:le_int(6, 2) == -256)
+testlib.test(LE_INT,"ByteArray:le_int-11", le_data:le_int(5, 3) == -65536)
+testlib.test(LE_INT,"ByteArray:le_int-12", le_data:le_int(4, 4) == -16777216)
+testlib.test(LE_INT,"ByteArray:le_int-13", be_data:subset(2, 2):le_int() == 0)
+
+testlib.testing(LE_UINT,"negative tests")
+testlib.test(LE_UINT,"ByteArray:le_uint-0",not pcall(ByteArray_le_uint, empty))
+testlib.test(LE_UINT,"ByteArray:le_uint-1",not pcall(ByteArray_le_uint, le_data))
+testlib.test(LE_UINT,"ByteArray:le_uint-2",not pcall(ByteArray_le_uint, le_data, -1))
+testlib.test(LE_UINT,"ByteArray:le_uint-3",not pcall(ByteArray_le_uint, le_data, 0))
+testlib.test(LE_UINT,"ByteArray:le_uint-4",not pcall(ByteArray_le_uint, le_data, 0, -1))
+testlib.test(LE_UINT,"ByteArray:le_uint-5",not pcall(ByteArray_le_uint, le_data, 0, 0))
+testlib.test(LE_UINT,"ByteArray:le_uint-6",not pcall(ByteArray_le_uint, le_data, 0, 5))
+testlib.test(LE_UINT,"ByteArray:le_uint-7",not pcall(ByteArray_le_uint, le_data, 7, 2))
+testlib.test(LE_UINT,"ByteArray:le_uint-8",not pcall(ByteArray_le_uint, le_data, 8, 1))
+
+testlib.testing(LE_UINT,"positive tests")
+testlib.test(LE_UINT,"ByteArray:le_uint-9", le_data:le_uint(7) == 255)
+testlib.test(LE_UINT,"ByteArray:le_uint-10", le_data:le_uint(6, 2) == 65280)
+testlib.test(LE_UINT,"ByteArray:le_uint-11", le_data:le_uint(5, 3) == 16711680)
+testlib.test(LE_UINT,"ByteArray:le_uint-12", le_data:le_uint(4, 4) == 4278190080)
+testlib.test(LE_UINT,"ByteArray:le_uint-13", be_data:subset(2, 2):le_uint() == 0)
+
+testlib.testing(LE_INT64,"negative tests")
+testlib.test(LE_INT64,"ByteArray:le_int64-0",not pcall(ByteArray_le_int64, empty))
+testlib.test(LE_INT64,"ByteArray:le_int64-1",not pcall(ByteArray_le_int64, le_data, -1))
+testlib.test(LE_INT64,"ByteArray:le_int64-2",not pcall(ByteArray_le_int64, le_data, 0, 0))
+testlib.test(LE_INT64,"ByteArray:le_int64-3",not pcall(ByteArray_le_int64, le_data, 0, 9))
+testlib.test(LE_INT64,"ByteArray:le_int64-4",not pcall(ByteArray_le_int64, le_data, 7, 2))
+testlib.test(LE_INT64,"ByteArray:le_int64-5",not pcall(ByteArray_le_int64, le_data, 8, 1))
+
+testlib.testing(LE_INT64,"positive tests")
+testlib.test(LE_INT64,"ByteArray:le_int64-6", le_data:le_int64(7):tonumber() == -1)
+testlib.test(LE_INT64,"ByteArray:le_int64-7", le_data:le_int64(6, 2):tonumber() == -256)
+testlib.test(LE_INT64,"ByteArray:le_int64-8", le_data:le_int64(5, 3):tonumber() == -65536)
+testlib.test(LE_INT64,"ByteArray:le_int64-9", le_data:le_int64(4, 4):tonumber() == -16777216)
+testlib.test(LE_INT64,"ByteArray:le_int64-10", le_data:le_int64(3, 5):tonumber() == -4294967296)
+testlib.test(LE_INT64,"ByteArray:le_int64-11", le_data:le_int64(2, 6):tonumber() == -1099511627776)
+testlib.test(LE_INT64,"ByteArray:le_int64-12", le_data:le_int64(1, 7):tonumber() == -281474976710656)
+testlib.test(LE_INT64,"ByteArray:le_int64-13", le_data:le_int64():tonumber() == -72057594037927936)
+testlib.test(LE_INT64,"ByteArray:le_int64-14", le_data:subset(0, 2):le_int64():tonumber() == 0)
+
+testlib.testing(LE_UINT64,"negative tests")
+testlib.test(LE_UINT64,"ByteArray:le_uint64-0",not pcall(ByteArray_le_uint64, empty))
+testlib.test(LE_UINT64,"ByteArray:le_uint64-1",not pcall(ByteArray_le_uint64, le_data, -1))
+testlib.test(LE_UINT64,"ByteArray:le_uint64-2",not pcall(ByteArray_le_uint64, le_data, 0, 0))
+testlib.test(LE_UINT64,"ByteArray:le_uint64-3",not pcall(ByteArray_le_uint64, le_data, 0, 9))
+testlib.test(LE_UINT64,"ByteArray:le_uint64-4",not pcall(ByteArray_le_uint64, le_data, 7, 2))
+testlib.test(LE_UINT64,"ByteArray:le_uint64-5",not pcall(ByteArray_le_uint64, le_data, 8, 1))
+
+testlib.testing(LE_UINT64,"positive tests")
+testlib.test(LE_UINT64,"ByteArray:le_uint64-6", le_data:le_uint64(7):tonumber() == 255)
+testlib.test(LE_UINT64,"ByteArray:le_uint64-7", le_data:le_uint64(6, 2):tonumber() == 65280)
+testlib.test(LE_UINT64,"ByteArray:le_uint64-8", le_data:le_uint64(5, 3):tonumber() == 16711680)
+testlib.test(LE_UINT64,"ByteArray:le_uint64-9", le_data:le_uint64(4, 4):tonumber() == 4278190080)
+testlib.test(LE_UINT64,"ByteArray:le_uint64-10", le_data:le_uint64(3, 5):tonumber() == 1095216660480)
+testlib.test(LE_UINT64,"ByteArray:le_uint64-11", le_data:le_uint64(2, 6):tonumber() == 280375465082880)
+testlib.test(LE_UINT64,"ByteArray:le_uint64-12", le_data:le_uint64(1, 7):tonumber() == 71776119061217280)
+testlib.test(LE_UINT64,"ByteArray:le_uint64-13", le_data:le_uint64():tonumber() == 18374686479671623680)
+testlib.test(LE_UINT64,"ByteArray:le_uint64-14", le_data:subset(0, 2):le_uint64():tonumber() == 0)
+
+testlib.getResults() \ No newline at end of file
diff --git a/test/lua/dir.lua b/test/lua/dir.lua
new file mode 100644
index 0000000..c9ff8ea
--- /dev/null
+++ b/test/lua/dir.lua
@@ -0,0 +1,195 @@
+-- test script for wslua Dir functions
+
+local testlib = require("testlib")
+local OTHER = "other"
+testlib.init( { [OTHER] = 0 } )
+
+------------- helper funcs ------------
+
+-- the following are so we can use pcall (which needs a function to call)
+local function callDirFuncBase(name, t)
+ t.result = Dir[name]()
+ return true
+end
+
+local function callDirFunc(name, val, t)
+ t.result = Dir[name](val)
+ return true
+end
+
+local function makeFile(filename)
+ local f = io.open(filename, "w")
+ if not f then
+ error ("failed to make file"..filename.." in directory\n"..
+ "make sure to delete 'temp' directory before running again")
+ end
+ f:write("fooobarrloo")
+ f:close()
+ return true
+end
+
+--------------------------
+
+-- for our called function results
+local t = {}
+
+testlib.testing("Dir basics")
+
+testlib.test(OTHER,"global", _G.Dir ~= nil)
+testlib.test(OTHER,"global", type(Dir.make) == 'function')
+testlib.test(OTHER,"global", type(Dir.remove) == 'function')
+testlib.test(OTHER,"global", type(Dir.remove_all) == 'function')
+testlib.test(OTHER,"global", type(Dir.open) == 'function')
+testlib.test(OTHER,"global", type(Dir.close) == 'function')
+testlib.test(OTHER,"global", type(Dir.exists) == 'function')
+testlib.test(OTHER,"global", type(Dir.personal_config_path) == 'function')
+testlib.test(OTHER,"global", type(Dir.global_config_path) == 'function')
+testlib.test(OTHER,"global", type(Dir.personal_plugins_path) == 'function')
+testlib.test(OTHER,"global", type(Dir.global_plugins_path) == 'function')
+
+testlib.testing("Dir paths/filenames")
+
+testlib.test(OTHER,"Dir.__FILE__", __FILE__ ~= nil)
+testlib.test(OTHER,"Dir.__DIR__", __DIR__ ~= nil)
+testlib.test(OTHER,"Dir.exists", pcall(callDirFunc, "exists", "temp", t))
+testlib.test(OTHER,"Dir.personal_config_path", pcall(callDirFuncBase, "personal_config_path", t))
+testlib.test(OTHER,"Dir.global_config_path", pcall(callDirFuncBase, "global_config_path", t))
+testlib.test(OTHER,"Dir.personal_plugins_path", pcall(callDirFuncBase, "personal_plugins_path", t))
+testlib.test(OTHER,"Dir.global_plugins_path", pcall(callDirFuncBase, "global_plugins_path", t))
+
+-- Users expect trailing slashes for DATA_DIR and USER_DIR (bug 14619).
+local dirsep = package.config:sub(1,1)
+testlib.test(OTHER,"DATA_DIR", string.sub(DATA_DIR, -1) == dirsep)
+testlib.test(OTHER,"USER_DIR", string.sub(USER_DIR, -1) == dirsep)
+
+print("\nFor your information, I got the following info:\n")
+print("__FILE__ = '" .. __FILE__ .. "'")
+print("__DIR__ = '" .. __DIR__ .. "'")
+print("personal_config_path = '" .. Dir.personal_config_path() .. "'")
+print("global_config_path = '" .. Dir.global_config_path() .. "'")
+print("personal_plugins_path = '" .. Dir.personal_plugins_path() .. "'")
+print("global_plugins_path = '" .. Dir.global_plugins_path() .. "'")
+print("\n")
+
+testlib.testing("Directory manipulation")
+
+testlib.test(OTHER,"Dir.exists", pcall(callDirFunc, "exists", "temp", t))
+
+if t.result == true or t.result == false then
+ error("this testsuite requires there be no 'temp' directory or file; please remove it")
+end
+
+testlib.testing("Dir.make")
+
+testlib.test(OTHER,"Dir.make", pcall(callDirFunc, "make", "temp", t) and t.result == true)
+testlib.test(OTHER,"Dir.exists", pcall(callDirFunc, "exists", "temp", t) and t.result == true)
+-- make the same dir, should give false
+testlib.test(OTHER,"Dir.make", pcall(callDirFunc, "make", "temp", t) and t.result == false)
+
+testlib.testing("Dir.remove")
+
+testlib.test(OTHER,"Dir.remove", pcall(callDirFunc, "remove", "temp", t) and t.result == true)
+testlib.test(OTHER,"Dir.exists", pcall(callDirFunc, "exists", "temp", t) and t.result == nil)
+testlib.test(OTHER,"Dir.remove", pcall(callDirFunc, "remove", "temp", t) and t.result == false)
+
+Dir.make("temp")
+makeFile("temp/file.txt")
+
+-- will return nil because temp has a file
+testlib.test(OTHER,"Dir.remove", pcall(callDirFunc, "remove", "temp", t) and t.result == nil)
+
+testlib.testing("Dir.remove_all")
+
+testlib.test(OTHER,"Dir.remove_all", pcall(callDirFunc, "remove_all", "temp", t) and t.result == true)
+testlib.test(OTHER,"Dir.remove_all", pcall(callDirFunc, "remove_all", "temp", t) and t.result == false)
+
+Dir.make("temp")
+makeFile("temp/file1.txt")
+makeFile("temp/file2.txt")
+makeFile("temp/file3.txt")
+testlib.test(OTHER,"Dir.remove_all", pcall(callDirFunc, "remove_all", "temp", t) and t.result == true)
+testlib.test(OTHER,"Dir.remove_all", pcall(callDirFunc, "remove_all", "temp", t) and t.result == false)
+
+testlib.testing("Dir.open")
+
+Dir.make("temp")
+makeFile("temp/file1.txt")
+makeFile("temp/file2.txt")
+makeFile("temp/file3.txt")
+testlib.test(OTHER,"Dir.open", pcall(callDirFunc, "open", "temp", t))
+testlib.test(OTHER,"Dir.open", type(t.result) == 'userdata')
+testlib.test(OTHER,"Dir.open", typeof(t.result) == 'Dir')
+
+io.stdout:write("calling Dir object...")
+local dir = t.result
+local files = {}
+files[dir()] = true
+io.stdout:write("passed\n")
+files[dir()] = true
+files[dir()] = true
+
+testlib.test(OTHER,"Dir.call", files["file1.txt"])
+testlib.test(OTHER,"Dir.call", files["file2.txt"])
+testlib.test(OTHER,"Dir.call", files["file3.txt"])
+testlib.test(OTHER,"Dir.call", dir() == nil)
+testlib.test(OTHER,"Dir.call", dir() == nil)
+
+testlib.testing("Dir.close")
+
+testlib.test(OTHER,"Dir.close", pcall(callDirFunc, "close", dir, t))
+testlib.test(OTHER,"Dir.close", pcall(callDirFunc, "close", dir, t))
+
+testlib.testing("Negative testing 1")
+-- now try breaking it
+testlib.test(OTHER,"Dir.open", pcall(callDirFunc, "open", "temp", t))
+dir = t.result
+-- call dir() now
+files = {}
+files[dir()] = true
+
+Dir.remove_all("temp")
+
+-- call it again
+files[dir()] = true
+files[dir()] = true
+testlib.test(OTHER,"Dir.call", files["file1.txt"])
+testlib.test(OTHER,"Dir.call", files["file2.txt"])
+testlib.test(OTHER,"Dir.call", files["file3.txt"])
+testlib.test(OTHER,"Dir.close", pcall(callDirFunc, "close", dir, t))
+
+testlib.testing("Negative testing 2")
+-- do it again, but this time don't do dir() until after removing the files
+Dir.make("temp")
+makeFile("temp/file1.txt")
+makeFile("temp/file2.txt")
+makeFile("temp/file3.txt")
+
+testlib.test(OTHER,"Dir.open", pcall(callDirFunc, "open", "temp", t))
+dir = t.result
+
+Dir.remove_all("temp")
+-- now do it
+file = dir()
+testlib.test(OTHER,"Dir.call", file == nil)
+testlib.test(OTHER,"Dir.close", pcall(callDirFunc, "close", dir, t))
+
+
+-- negative tests
+testlib.testing("Negative testing 3")
+
+-- invalid args
+testlib.test(OTHER,"Dir.make", not pcall(callDirFunc, "make", {}, t))
+testlib.test(OTHER,"Dir.make", not pcall(callDirFunc, "make", nil, t))
+testlib.test(OTHER,"Dir.remove", not pcall(callDirFunc, "remove", {}, t))
+testlib.test(OTHER,"Dir.remove", not pcall(callDirFunc, "remove", nil, t))
+testlib.test(OTHER,"Dir.remove_all", not pcall(callDirFunc, "remove_all", {}, t))
+testlib.test(OTHER,"Dir.remove_all", not pcall(callDirFunc, "remove_all", nil, t))
+testlib.test(OTHER,"Dir.open", not pcall(callDirFunc, "open", {}, t))
+testlib.test(OTHER,"Dir.open", not pcall(callDirFunc, "open", nil, t))
+testlib.test(OTHER,"Dir.close", not pcall(callDirFunc, "close", "dir", t))
+testlib.test(OTHER,"Dir.close", not pcall(callDirFunc, "close", nil, t))
+
+
+print("\n-----------------------------\n")
+
+testlib.getResults()
diff --git a/test/lua/dissectFPM.lua b/test/lua/dissectFPM.lua
new file mode 100644
index 0000000..da52d74
--- /dev/null
+++ b/test/lua/dissectFPM.lua
@@ -0,0 +1,452 @@
+----------------------------------------
+--
+-- author: Hadriel Kaplan <hadriel@128technology.com>
+-- Copyright (c) 2015, Hadriel Kaplan
+-- This code is in the Public Domain, or the BSD (3 clause) license
+-- if Public Domain does not apply in your country.
+--
+-- Version: 1.0
+--
+------------------------------------------
+--[[
+ This code is a plugin for Wireshark, to dissect Quagga FPM Netlink
+ protocol messages over TCP.
+
+ This script is used for testing, so it does some odd things:
+ * it dissects the FPM in two ways, controlled by a pref setting:
+ 1) using the desegment_offset/desegment_len method
+ 2) using the dissect_tcp_pdus() method
+ * it removes any existing FPM dissector; there isn't one right now
+ but there likely will be in the future.
+
+ Wireshark has a "Netlink" protocol dissector, but it currently expects
+ to be running on a Linux cooked-mode SLL header and link type. That's
+ because Netlink has traditionally been used between the Linux kernel
+ and user-space apps. But the open-source Quagga, zebra, and the
+ commercial ZebOS routing products also send Netlink messages over TCP
+ to other processes or even outside the box, to a "Forwarding Plane Manager"
+ (FPM) that controls forwarding-plane devices (typically hardware).
+
+ The Netlink message is encapsulated within an FPM header, which identifies
+ an FPM message version (currently 1), the type of message it contains
+ (namely a Netlink message), and its length.
+
+ So we have:
+ struct fpm_msg_hdr_t
+ {
+ uint8_t version;
+ uint8_t msg_type;
+ uint16_t msg_len;
+ }
+ followed by a Netlink message.
+]]----------------------------------------
+
+
+----------------------------------------
+-- do not modify this table
+local debug_level = {
+ DISABLED = 0,
+ LEVEL_1 = 1,
+ LEVEL_2 = 2
+}
+
+-- set this DEBUG to debug_level.LEVEL_1 to enable printing debug_level info
+-- set it to debug_level.LEVEL_2 to enable really verbose printing
+-- note: this will be overridden by user's preference settings
+local DEBUG = debug_level.LEVEL_1
+
+local default_settings =
+{
+ debug_level = DEBUG,
+ enabled = true, -- whether this dissector is enabled or not
+ port = 2620,
+ max_msg_len = 4096,
+ desegment = true, -- whether to TCP desegement or not
+ dissect_tcp = false, -- whether to use the dissect_tcp_pdus method or not
+ subdissect = true, -- whether to call sub-dissector or not
+ subdiss_type = wtap.NETLINK, -- the encap we get the subdissector for
+}
+
+local dprint = function() end
+local dprint2 = function() end
+local function reset_debug_level()
+ if default_settings.debug_level > debug_level.DISABLED then
+ dprint = function(...)
+ print(table.concat({"Lua:", ...}," "))
+ end
+
+ if default_settings.debug_level > debug_level.LEVEL_1 then
+ dprint2 = dprint
+ end
+ end
+end
+-- call it now
+reset_debug_level()
+
+
+----------------------------------------
+-- creates a Proto object, but doesn't register it yet
+local fpmProto = Proto("fpm", "FPM Header")
+
+
+----------------------------------------
+-- a function to convert tables of enumerated types to valstring tables
+-- i.e., from { "name" = number } to { number = "name" }
+local function makeValString(enumTable)
+ local t = {}
+ for name,num in pairs(enumTable) do
+ t[num] = name
+ end
+ return t
+end
+
+local MsgType = {
+ NONE = 0,
+ NETLINK = 1,
+}
+local msgtype_valstr = makeValString(MsgType)
+
+
+----------------------------------------
+-- a table of all of our Protocol's fields
+local hdr_fields =
+{
+ version = ProtoField.uint8 ("fpm.version", "Version", base.DEC),
+ msg_type = ProtoField.uint8 ("fpm.type", "Type", base.DEC, msgtype_valstr),
+ msg_len = ProtoField.uint16("fpm.length", "Length", base.DEC),
+}
+
+-- create a flat array table of the above that can be registered
+local pfields = {}
+
+-- recursive function to flatten the table into pfields
+local function flattenTable(tbl)
+ for k,v in pairs(tbl) do
+ if type(v) == 'table' then
+ flattenTable(v)
+ else
+ pfields[#pfields+1] = v
+ end
+ end
+end
+-- call it
+flattenTable(hdr_fields)
+
+-- register them
+fpmProto.fields = pfields
+
+dprint2("fpmProto ProtoFields registered")
+
+
+----------------------------------------
+-- some forward "declarations" of helper functions we use in the dissector
+local createSLL
+
+-- due to a bug in wireshark, we need to keep newly created tvb's for longer
+-- than the duration of the dissect function
+local tvbs = {}
+
+function fpmProto.init()
+ tvbs = {}
+end
+
+
+local FPM_MSG_HDR_LEN = 4
+
+----------------------------------------
+-- the following function is used for the new dissect_tcp_pdus method
+-- this one returns the length of the full message
+local function get_fpm_length(tvbuf, pktinfo, offset)
+ dprint2("FPM get_fpm_length function called")
+ local lengthVal = tvbuf:range(offset + 2, 2):uint()
+
+ if lengthVal > default_settings.max_msg_len then
+ -- too many bytes, invalid message
+ dprint("FPM message length is too long: ", lengthVal)
+ lengthVal = tvbuf:len()
+ end
+
+ return lengthVal
+end
+
+-- the following is the dissection function called for
+-- the new dissect_tcp_pdus method
+local function dissect_fpm_pdu(tvbuf, pktinfo, root)
+ dprint2("FPM dissect_fpm_pdu function called")
+
+ local lengthTvbr = tvbuf:range(2, 2)
+ local lengthVal = lengthTvbr:uint()
+
+ -- set the protocol column to show our protocol name
+ pktinfo.cols.protocol:set("FPM")
+
+ -- We start by adding our protocol to the dissection display tree.
+ local tree = root:add(fpmProto, tvbuf:range(offset, lengthVal))
+
+ local versionTvbr = tvbuf:range(0, 1)
+ local versionVal = versionTvbr:uint()
+ tree:add(hdr_fields.version, versionTvbr)
+
+ local msgTypeTvbr = tvbuf:range(1, 1)
+ local msgTypeVal = msgTypeTvbr:uint()
+ tree:add(hdr_fields.msg_type, msgTypeTvbr)
+
+ tree:add(hdr_fields.msg_len, lengthTvbr)
+
+ local result
+ if (versionVal == 1) and (msgTypeVal == MsgType.NETLINK) then
+ -- it carries a Netlink message, so we're going to create
+ -- a fake Linux SLL header for the built-in Netlink dissector
+ local payload = tvbuf:raw(FPM_MSG_HDR_LEN, lengthVal - FPM_MSG_HDR_LEN)
+ result = createSLL(payload)
+ end
+
+ -- looks good, go dissect it
+ if result then
+ -- ok now the hard part - try calling a sub-dissector?
+ -- only if settings/prefs told us to of course...
+ if default_settings.subdissect then
+ dprint2("FPM trying sub-dissector for wtap encap type:", default_settings.subdiss_type)
+
+ -- due to a bug in wireshark, we need to keep newly created tvb's for longer
+ -- than the duration of the dissect function
+ tvbs[#tvbs+1] = ByteArray.new(result, true):tvb("Netlink Message")
+ DissectorTable.get("wtap_encap"):try(default_settings.subdiss_type, tvbs[#tvbs], pktinfo, root)
+
+ -- local tvb = ByteArray.new(result, true):tvb("Netlink Message")
+ -- DissectorTable.get("wtap_encap"):try(default_settings.subdiss_type, tvb, pktinfo, root)
+ dprint2("FPM returning from sub-dissector")
+ end
+ else
+ dprint("FPM header not correctly dissected")
+ end
+
+ return lengthVal, 0
+end
+
+
+----------------------------------------
+-- the following function is used for dissecting using the
+-- old desegment_offset/desegment_len method
+-- it's a separate function because we run over TCP and thus might
+-- need to parse multiple messages in a single segment
+local function dissect(tvbuf, pktinfo, root, offset, origlen)
+ dprint2("FPM dissect function called")
+
+ local pktlen = origlen - offset
+
+ if pktlen < FPM_MSG_HDR_LEN then
+ -- we need more bytes
+ pktinfo.desegment_offset = offset
+ pktinfo.desegment_len = DESEGMENT_ONE_MORE_SEGMENT
+ return 0, DESEGMENT_ONE_MORE_SEGMENT
+ end
+
+ local lengthTvbr = tvbuf:range(offset + 2, 2)
+ local lengthVal = lengthTvbr:uint()
+
+ if lengthVal > default_settings.max_msg_len then
+ -- too many bytes, invalid message
+ dprint("FPM message length is too long: ", lengthVal)
+ return pktlen, 0
+ end
+
+ if pktlen < lengthVal then
+ dprint2("Need more bytes to desegment FPM")
+ pktinfo.desegment_offset = offset
+ pktinfo.desegment_len = (lengthVal - pktlen)
+ return 0, -(lengthVal - pktlen)
+ end
+
+ -- set the protocol column to show our protocol name
+ pktinfo.cols.protocol:set("FPM")
+
+ -- We start by adding our protocol to the dissection display tree.
+ local tree = root:add(fpmProto, tvbuf:range(offset, lengthVal))
+
+ local versionTvbr = tvbuf:range(offset, 1)
+ local versionVal = versionTvbr:uint()
+ tree:add(hdr_fields.version, versionTvbr)
+
+ local msgTypeTvbr = tvbuf:range(offset + 1, 1)
+ local msgTypeVal = msgTypeTvbr:uint()
+ tree:add(hdr_fields.msg_type, msgTypeTvbr)
+
+ tree:add(hdr_fields.msg_len, lengthTvbr)
+
+ local result
+ if (versionVal == 1) and (msgTypeVal == MsgType.NETLINK) then
+ -- it carries a Netlink message, so we're going to create
+ -- a fake Linux SLL header for the built-in Netlink dissector
+ local payload = tvbuf:raw(offset + FPM_MSG_HDR_LEN, lengthVal - FPM_MSG_HDR_LEN)
+ result = createSLL(payload)
+ end
+
+ -- looks good, go dissect it
+ if result then
+ -- ok now the hard part - try calling a sub-dissector?
+ -- only if settings/prefs told us to of course...
+ if default_settings.subdissect then
+ dprint2("FPM trying sub-dissector for wtap encap type:", default_settings.subdiss_type)
+
+ -- due to a bug in wireshark, we need to keep newly created tvb's for longer
+ -- than the duration of the dissect function
+ tvbs[#tvbs+1] = ByteArray.new(result, true):tvb("Netlink Message")
+ DissectorTable.get("wtap_encap"):try(default_settings.subdiss_type, tvbs[#tvbs], pktinfo, root)
+
+ -- local tvb = ByteArray.new(result, true):tvb("Netlink Message")
+ -- DissectorTable.get("wtap_encap"):try(default_settings.subdiss_type, tvb, pktinfo, root)
+ dprint2("FPM returning from sub-dissector")
+ end
+ else
+ dprint("FPM header not correctly dissected")
+ end
+
+ return lengthVal, 0
+end
+
+
+----------------------------------------
+-- The following creates the callback function for the dissector.
+-- It's the same as doing "appProto.dissector = function (tvbuf,pkt,root)"
+-- The 'tvbuf' is a Tvb object, 'pktinfo' is a Pinfo object, and 'root' is a TreeItem object.
+-- Whenever Wireshark dissects a packet that our Proto is hooked into, it will call
+-- this function and pass it these arguments for the packet it's dissecting.
+function fpmProto.dissector(tvbuf, pktinfo, root)
+ dprint2("fpmProto.dissector called")
+
+ local bytes_consumed = 0
+
+ if default_settings.dissect_tcp then
+ dprint2("using new dissect_tcp_pdus method")
+ dissect_tcp_pdus(tvbuf, root, FPM_MSG_HDR_LEN, get_fpm_length, dissect_fpm_pdu, default_settings.desegment)
+ bytes_consumed = tvbuf:len()
+ else
+ dprint2("using old desegment_offset/desegment_len method")
+ -- get the length of the packet buffer (Tvb).
+ local pktlen = tvbuf:len()
+ local offset, bytes_needed = 0, 0
+
+ tvbs = {}
+ while bytes_consumed < pktlen do
+ offset, bytes_needed = dissect(tvbuf, pktinfo, root, bytes_consumed, pktlen)
+ if offset == 0 then
+ if bytes_consumed > 0 then
+ return bytes_consumed
+ else
+ return bytes_needed
+ end
+ end
+ bytes_consumed = bytes_consumed + offset
+ end
+ end
+
+ return bytes_consumed
+end
+
+
+----------------------------------------
+-- we want to have our protocol dissection invoked for a specific TCP port,
+-- so get the TCP dissector table and add our protocol to it
+-- first remove any existing dissector for that port, if there is one
+local old_dissector = DissectorTable.get("tcp.port"):get_dissector(default_settings.port)
+if old_dissector then
+ dprint("Retrieved existing dissector")
+end
+
+local function enableDissector()
+ DissectorTable.get("tcp.port"):set(default_settings.port, fpmProto)
+end
+-- call it now
+enableDissector()
+
+local function disableDissector()
+ if old_dissector then
+ DissectorTable.get("tcp.port"):set(default_settings.port, old_dissector)
+ end
+end
+
+
+--------------------------------------------------------------------------------
+-- preferences handling stuff
+--------------------------------------------------------------------------------
+
+local debug_pref_enum = {
+ { 1, "Disabled", debug_level.DISABLED },
+ { 2, "Level 1", debug_level.LEVEL_1 },
+ { 3, "Level 2", debug_level.LEVEL_2 },
+}
+
+----------------------------------------
+-- register our preferences
+fpmProto.prefs.enabled = Pref.bool("Dissector enabled", default_settings.enabled,
+ "Whether the FPM dissector is enabled or not")
+
+
+fpmProto.prefs.desegment = Pref.bool("Reassemble FPM messages spanning multiple TCP segments",
+ default_settings.desegment,
+ "Whether the FPM dissector should reassemble"..
+ " messages spanning multiple TCP segments."..
+ " To use this option, you must also enable"..
+ " \"Allow subdissectors to reassemble TCP"..
+ " streams\" in the TCP protocol settings.")
+
+fpmProto.prefs.dissect_tcp = Pref.bool("Use dissect_tcp_pdus", default_settings.dissect_tcp,
+ "Whether the FPM dissector should use the new" ..
+ " dissect_tcp_pdus model or not")
+
+fpmProto.prefs.subdissect = Pref.bool("Enable sub-dissectors", default_settings.subdissect,
+ "Whether the FPM packet's content" ..
+ " should be dissected or not")
+
+fpmProto.prefs.debug = Pref.enum("Debug", default_settings.debug_level,
+ "The debug printing level", debug_pref_enum)
+
+----------------------------------------
+-- a function for handling prefs being changed
+function fpmProto.prefs_changed()
+ dprint2("prefs_changed called")
+
+ default_settings.dissect_tcp = fpmProto.prefs.dissect_tcp
+
+ default_settings.subdissect = fpmProto.prefs.subdissect
+
+ default_settings.debug_level = fpmProto.prefs.debug
+ reset_debug_level()
+
+ if default_settings.enabled ~= fpmProto.prefs.enabled then
+ default_settings.enabled = fpmProto.prefs.enabled
+ if default_settings.enabled then
+ enableDissector()
+ else
+ disableDissector()
+ end
+ -- have to reload the capture file for this type of change
+ reload()
+ end
+
+end
+
+dprint2("pcapfile Prefs registered")
+
+
+----------------------------------------
+-- the hatype field of the SLL must be 824 decimal, in big-endian encoding (0x0338)
+local ARPHRD_NETLINK = "\003\056"
+local WS_NETLINK_ROUTE = "\000\000"
+local function emptyBytes(num)
+ return string.rep("\000", num)
+end
+
+createSLL = function (payload)
+ dprint2("FPM createSLL function called")
+ local sllmsg =
+ {
+ emptyBytes(2), -- Unused 2B
+ ARPHRD_NETLINK, -- netlink type
+ emptyBytes(10), -- Unused 10B
+ WS_NETLINK_ROUTE, -- Route type
+ payload -- the Netlink message
+ }
+ return table.concat(sllmsg)
+end
diff --git a/test/lua/dissector.lua b/test/lua/dissector.lua
new file mode 100644
index 0000000..836aa7b
--- /dev/null
+++ b/test/lua/dissector.lua
@@ -0,0 +1,659 @@
+----------------------------------------
+-- script-name: dns_dissector.lua
+--
+-- author: Hadriel Kaplan <hadrielk at yahoo dot com>
+-- Copyright (c) 2014, Hadriel Kaplan
+-- This code is in the Public Domain, or the BSD (3 clause) license if Public Domain does not apply
+-- in your country.
+--
+-- Version: 2.1
+--
+-- Changes since 2.0:
+-- * fixed a bug with default settings
+-- * added ability for command-line to overide defaults
+--
+-- Changes since 1.0:
+-- * made it use the new ProtoExpert class model for expert info
+-- * add a protocol column with the proto name
+-- * added heuristic dissector support
+-- * added preferences settings
+-- * removed byteArray2String(), and uses the new ByteArray:raw() method instead
+--
+-- BACKGROUND:
+-- This is an example Lua script for a protocol dissector. The purpose of this script is two-fold:
+-- * To provide a reference tutorial for others writing Wireshark dissectors in Lua
+-- * To test various functions being called in various ways, so this script can be used in the test-suites
+-- I've tried to meet both of those goals, but it wasn't easy. No doubt some folks will wonder why some
+-- functions are called some way, or differently than previous invocations of the same function. I'm trying to
+-- to show both that it can be done numerous ways, but also I'm trying to test those numerous ways, and my more
+-- immediate need is for test coverage rather than tutorial guide. (the Lua API is sorely lacking in test scripts)
+--
+-- OVERVIEW:
+-- This script creates an elementary dissector for DNS. It's neither comprehensive nor error-free with regards
+-- to the DNS protocol. That's OK. The goal isn't to fully dissect DNS properly - Wireshark already has a good
+-- DNS dissector built-in. We don't need another one. We also have other example Lua scripts, but I don't think
+-- they do a good job of explaining things, and the nice thing about this one is getting capture files to
+-- run it against is trivial. (plus I uploaded one)
+--
+-- HOW TO RUN THIS SCRIPT:
+-- Wireshark and Tshark support multiple ways of loading Lua scripts: through a dofile() call in init.lua,
+-- through the file being in either the global or personal plugins directories, or via the command line.
+-- See the Wireshark User's Guide chapter on Lua (https://www.wireshark.org/docs/wsdg_html_chunked/wsluarm_modules.html).
+-- Once the script is loaded, it creates a new protocol named "MyDNS" (or "MYDNS" in some places). If you have
+-- a capture file with DNS packets in it, simply select one in the Packet List pane, right-click on it, and
+-- select "Decode As ...", and then in the dialog box that shows up scroll down the list of protocols to one
+-- called "MYDNS", select that and click the "ok" or "apply" button. Voila`, you're now decoding DNS packets
+-- using the simplistic dissector in this script. Another way is to download the capture file made for
+-- this script, and open that - since the DNS packets in it use UDP port 65333 (instead of the default 53),
+-- and since the MyDNS protocol in this script has been set to automatically decode UDP port 65333, it will
+-- automagically do it without doing "Decode As ...".
+--
+----------------------------------------
+-- do not modify this table
+local debug_level = {
+ DISABLED = 0,
+ LEVEL_1 = 1,
+ LEVEL_2 = 2
+}
+
+-- set this DEBUG to debug_level.LEVEL_1 to enable printing debug_level info
+-- set it to debug_level.LEVEL_2 to enable really verbose printing
+-- note: this will be overridden by user's preference settings
+local DEBUG = debug_level.LEVEL_1
+
+local default_settings =
+{
+ debug_level = DEBUG,
+ port = 65333,
+ heur_enabled = true,
+ heur_regmode = 1,
+}
+
+-- for testing purposes, we want to be able to pass in changes to the defaults
+-- from the command line; because you can't set lua preferences from the command
+-- line using the '-o' switch (the preferences don't exist until this script is
+-- loaded, so the command line thinks they're invalid preferences being set)
+-- so we pass them in as command arguments insetad, and handle it here:
+local args={...} -- get passed-in args
+if args and #args > 0 then
+ for _, arg in ipairs(args) do
+ local name, value = arg:match("(.+)=(.+)")
+ if name and value then
+ if tonumber(value) then
+ value = tonumber(value)
+ elseif value == "true" or value == "TRUE" then
+ value = true
+ elseif value == "false" or value == "FALSE" then
+ value = false
+ elseif value == "DISABLED" then
+ value = debug_level.DISABLED
+ elseif value == "LEVEL_1" then
+ value = debug_level.LEVEL_1
+ elseif value == "LEVEL_2" then
+ value = debug_level.LEVEL_2
+ else
+ error("invalid commandline argument value")
+ end
+ else
+ error("invalid commandline argument syntax")
+ end
+
+ default_settings[name] = value
+ end
+end
+
+local dprint = function() end
+local dprint2 = function() end
+local function reset_debug_level()
+ if default_settings.debug_level > debug_level.DISABLED then
+ dprint = function(...)
+ print(table.concat({"Lua:", ...}," "))
+ end
+
+ if default_settings.debug_level > debug_level.LEVEL_1 then
+ dprint2 = dprint
+ end
+ end
+end
+-- call it now
+reset_debug_level()
+
+dprint2("Wireshark version = ", get_version())
+dprint2("Lua version = ", _VERSION)
+
+----------------------------------------
+-- Unfortunately, the older Wireshark/Tshark versions have bugs, and part of the point
+-- of this script is to test those bugs are now fixed. So we need to check the version
+-- end error out if it's too old.
+local major, minor, micro = get_version():match("(%d+)%.(%d+)%.(%d+)")
+if major and tonumber(major) <= 1 and ((tonumber(minor) <= 10) or (tonumber(minor) == 11 and tonumber(micro) < 3)) then
+ error( "Sorry, but your Wireshark/Tshark version ("..get_version()..") is too old for this script!\n"..
+ "This script needs Wireshark/Tshark version 1.11.3 or higher.\n" )
+end
+
+-- more sanity checking
+-- verify we have the ProtoExpert class in wireshark, as that's the newest thing this file uses
+assert(ProtoExpert.new, "Wireshark does not have the ProtoExpert class, so it's too old - get the latest 1.11.3 or higher")
+
+----------------------------------------
+
+
+----------------------------------------
+-- creates a Proto object, but doesn't register it yet
+local dns = Proto("mydns","MyDNS Protocol")
+
+----------------------------------------
+-- multiple ways to do the same thing: create a protocol field (but not register it yet)
+-- the abbreviation should always have "<myproto>." before the specific abbreviation, to avoid collisions
+local pf_trasaction_id = ProtoField.new ("Transaction ID", "mydns.trans_id", ftypes.UINT16)
+local pf_flags = ProtoField.new ("Flags", "mydns.flags", ftypes.UINT16, nil, base.HEX)
+local pf_num_questions = ProtoField.uint16("mydns.num_questions", "Number of Questions")
+local pf_num_answers = ProtoField.uint16("mydns.num_answers", "Number of Answer RRs")
+local pf_num_authority_rr = ProtoField.uint16("mydns.num_authority_rr", "Number of Authority RRs")
+local pf_num_additional_rr = ProtoField.uint16("mydns.num_additional_rr", "Number of Additional RRs")
+
+-- within the flags field, we want to parse/show the bits separately
+-- note the "base" argument becomes the size of the bitmask'ed field when ftypes.BOOLEAN is used
+-- the "mask" argument is which bits we want to use for this field (e.g., base=16 and mask=0x8000 means we want the top bit of a 16-bit field)
+-- again the following shows different ways of doing the same thing basically
+local pf_flag_response = ProtoField.new ("Response", "mydns.flags.response", ftypes.BOOLEAN, {"this is a response","this is a query"}, 16, 0x8000, "is the message a response?")
+local pf_flag_opcode = ProtoField.new ("Opcode", "mydns.flags.opcode", ftypes.UINT16, nil, base.DEC, 0x7800, "operation code")
+local pf_flag_authoritative = ProtoField.new ("Authoritative", "mydns.flags.authoritative", ftypes.BOOLEAN, nil, 16, 0x0400, "is the response authoritative?")
+local pf_flag_truncated = ProtoField.bool ("mydns.flags.truncated", "Truncated", 16, nil, 0x0200, "is the message truncated?")
+local pf_flag_recursion_desired = ProtoField.bool ("mydns.flags.recursion_desired", "Recursion desired", 16, {"yes","no"}, 0x0100, "do the query recursivley?")
+local pf_flag_recursion_available = ProtoField.bool ("mydns.flags.recursion_available", "Recursion available", 16, nil, 0x0080, "does the server support recursion?")
+local pf_flag_z = ProtoField.uint16("mydns.flags.z", "World War Z - Reserved for future use", base.HEX, nil, 0x0040, "when is it the future?")
+local pf_flag_authenticated = ProtoField.bool ("mydns.flags.authenticated", "Authenticated", 16, {"yes","no"}, 0x0020, "did the server DNSSEC authenticate?")
+local pf_flag_checking_disabled = ProtoField.bool ("mydns.flags.checking_disabled", "Checking disabled", 16, nil, 0x0010)
+
+-- no, these aren't all the DNS response codes - this is just an example
+local rcodes = {
+ [0] = "No Error",
+ [1] = "Format Error",
+ [2] = "Server Failure",
+ [3] = "Non-Existent Domain",
+ [9] = "Server Not Authoritative for zone"
+}
+-- the above rcodes table is used in this next ProtoField
+local pf_flag_rcode = ProtoField.uint16("mydns.flags.rcode", "Response code", base.DEC, rcodes, 0x000F)
+local pf_query = ProtoField.new("Query", "mydns.query", ftypes.BYTES)
+local pf_query_name = ProtoField.new("Name", "mydns.query.name", ftypes.STRING)
+local pf_query_name_len = ProtoField.new("Name Length", "mydns.query.name.len", ftypes.UINT8)
+local pf_query_label_count = ProtoField.new("Label Count", "mydns.query.label.count", ftypes.UINT8)
+local rrtypes = { [1] = "A (IPv4 host address)", [2] = "NS (authoritative name server)", [28] = "AAAA (for geeks only)" }
+local pf_query_type = ProtoField.uint16("mydns.query.type", "Type", base.DEC, rrtypes)
+-- again, not all class types are listed here
+local classes = {
+ [0] = "Reserved",
+ [1] = "IN (Internet)",
+ [2] = "The 1%",
+ [5] = "First class",
+ [6] = "Business class",
+ [65535] = "Cattle class"
+}
+local pf_query_class = ProtoField.uint16("mydns.query.class", "Class", base.DEC, classes, nil, "keep it classy folks")
+
+----------------------------------------
+-- this actually registers the ProtoFields above, into our new Protocol
+-- in a real script I wouldn't do it this way; I'd build a table of fields programmatically
+-- and then set dns.fields to it, so as to avoid forgetting a field
+dns.fields = { pf_trasaction_id, pf_flags,
+ pf_num_questions, pf_num_answers, pf_num_authority_rr, pf_num_additional_rr,
+ pf_flag_response, pf_flag_opcode, pf_flag_authoritative,
+ pf_flag_truncated, pf_flag_recursion_desired, pf_flag_recursion_available,
+ pf_flag_z, pf_flag_authenticated, pf_flag_checking_disabled, pf_flag_rcode,
+ pf_query, pf_query_name, pf_query_name_len, pf_query_label_count, pf_query_type, pf_query_class }
+
+----------------------------------------
+-- create some expert info fields (this is new functionality in 1.11.3)
+-- Expert info fields are very similar to proto fields: they're tied to our protocol,
+-- they're created in a similar way, and registered by setting a 'experts' field to
+-- a table of them just as proto fields were put into the 'dns.fields' above
+-- The old way of creating expert info was to just add it to the tree, but that
+-- didn't let the expert info be filterable in wireshark, whereas this way does
+local ef_query = ProtoExpert.new("mydns.query.expert", "DNS query message",
+ expert.group.REQUEST_CODE, expert.severity.CHAT)
+local ef_response = ProtoExpert.new("mydns.response.expert", "DNS response message",
+ expert.group.RESPONSE_CODE, expert.severity.CHAT)
+local ef_ultimate = ProtoExpert.new("mydns.response.ultimate.expert", "DNS answer to life, the universe, and everything",
+ expert.group.COMMENTS_GROUP, expert.severity.NOTE)
+-- some error expert info's
+local ef_too_short = ProtoExpert.new("mydns.too_short.expert", "DNS message too short",
+ expert.group.MALFORMED, expert.severity.ERROR)
+local ef_bad_query = ProtoExpert.new("mydns.query.missing.expert", "DNS query missing or malformed",
+ expert.group.MALFORMED, expert.severity.WARN)
+
+-- register them
+dns.experts = { ef_query, ef_too_short, ef_bad_query, ef_response, ef_ultimate }
+
+----------------------------------------
+-- we don't just want to display our protocol's fields, we want to access the value of some of them too!
+-- There are several ways to do that. One is to just parse the buffer contents in Lua code to find
+-- the values. But since ProtoFields actually do the parsing for us, and can be retrieved using Field
+-- objects, it's kinda cool to do it that way. So let's create some Fields to extract the values.
+-- The following creates the Field objects, but they're not 'registered' until after this script is loaded.
+-- Also, these lines can't be before the 'dns.fields = ...' line above, because the Field.new() here is
+-- referencing fields we're creating, and they're not "created" until that line above.
+-- Furthermore, you cannot put these 'Field.new()' lines inside the dissector function.
+-- Before Wireshark version 1.11, you couldn't even do this concept (of using fields you just created).
+local questions_field = Field.new("mydns.num_questions")
+local query_type_field = Field.new("mydns.query.type")
+local query_class_field = Field.new("mydns.query.class")
+local response_field = Field.new("mydns.flags.response")
+
+-- here's a little helper function to access the response_field value later.
+-- Like any Field retrieval, you can't retrieve a field's value until its value has been
+-- set, which won't happen until we actually use our ProtoFields in TreeItem:add() calls.
+-- So this isResponse() function can't be used until after the pf_flag_response ProtoField
+-- has been used inside the dissector.
+-- Note that calling the Field object returns a FieldInfo object, and calling that
+-- returns the value of the field - in this case a boolean true/false, since we set the
+-- "mydns.flags.response" ProtoField to ftype.BOOLEAN way earlier when we created the
+-- pf_flag_response ProtoField. Clear as mud?
+--
+-- A shorter version of this function would be:
+-- local function isResponse() return response_field()() end
+-- but I though the below is easier to understand.
+local function isResponse()
+ local response_fieldinfo = response_field()
+ return response_fieldinfo()
+end
+
+--------------------------------------------------------------------------------
+-- preferences handling stuff
+--------------------------------------------------------------------------------
+
+-- a "enum" table for our enum pref, as required by Pref.enum()
+-- having the "index" number makes ZERO sense, and is completely illogical
+-- but it's what the code has expected it to be for a long time. Ugh.
+local debug_pref_enum = {
+ { 1, "Disabled", debug_level.DISABLED },
+ { 2, "Level 1", debug_level.LEVEL_1 },
+ { 3, "Level 2", debug_level.LEVEL_2 },
+}
+
+dns.prefs.debug = Pref.enum("Debug", default_settings.debug_level,
+ "The debug printing level", debug_pref_enum)
+
+dns.prefs.port = Pref.uint("Port number", default_settings.port,
+ "The UDP port number for MyDNS")
+
+dns.prefs.heur = Pref.bool("Heuristic enabled", default_settings.heur_enabled,
+ "Whether heuristic dissection is enabled or not")
+
+----------------------------------------
+-- a function for handling prefs being changed
+function dns.prefs_changed()
+ dprint2("prefs_changed called")
+
+ default_settings.debug_level = dns.prefs.debug
+ reset_debug_level()
+
+ default_settings.heur_enabled = dns.prefs.heur
+
+ if default_settings.port ~= dns.prefs.port then
+ -- remove old one, if not 0
+ if default_settings.port ~= 0 then
+ dprint2("removing MyDNS from port",default_settings.port)
+ DissectorTable.get("udp.port"):remove(default_settings.port, dns)
+ end
+ -- set our new default
+ default_settings.port = dns.prefs.port
+ -- add new one, if not 0
+ if default_settings.port ~= 0 then
+ dprint2("adding MyDNS to port",default_settings.port)
+ DissectorTable.get("udp.port"):add(default_settings.port, dns)
+ end
+ end
+
+end
+
+dprint2("MyDNS Prefs registered")
+
+
+----------------------------------------
+---- some constants for later use ----
+-- the DNS header size
+local DNS_HDR_LEN = 12
+
+-- the smallest possible DNS query field size
+-- has to be at least a label null terminator, 2-bytes type and 2-bytes class
+local MIN_QUERY_LEN = 5
+
+----------------------------------------
+-- some forward "declarations" of helper functions we use in the dissector
+-- I don't usually use this trick, but it'll help reading/grok'ing this script I think
+-- if we don't focus on them.
+local getQueryName
+
+
+----------------------------------------
+-- The following creates the callback function for the dissector.
+-- It's the same as doing "dns.dissector = function (tvbuf,pkt,root)"
+-- The 'tvbuf' is a Tvb object, 'pktinfo' is a Pinfo object, and 'root' is a TreeItem object.
+-- Whenever Wireshark dissects a packet that our Proto is hooked into, it will call
+-- this function and pass it these arguments for the packet it's dissecting.
+function dns.dissector(tvbuf,pktinfo,root)
+ dprint2("dns.dissector called")
+
+ -- set the protocol column to show our protocol name
+ pktinfo.cols.protocol:set("MYDNS")
+
+ -- We want to check that the packet size is rational during dissection, so let's get the length of the
+ -- packet buffer (Tvb).
+ -- Because DNS has no additional payload data other than itself, and it rides on UDP without padding,
+ -- we can use tvb:len() or tvb:reported_len() here; but I prefer tvb:reported_length_remaining() as it's safer.
+ local pktlen = tvbuf:reported_length_remaining()
+
+ -- We start by adding our protocol to the dissection display tree.
+ -- A call to tree:add() returns the child created, so we can add more "under" it using that return value.
+ -- The second argument is how much of the buffer/packet this added tree item covers/represents - in this
+ -- case (DNS protocol) that's the remainder of the packet.
+ local tree = root:add(dns, tvbuf:range(0,pktlen))
+
+ -- now let's check it's not too short
+ if pktlen < DNS_HDR_LEN then
+ -- since we're going to add this protocol to a specific UDP port, we're going to
+ -- assume packets in this port are our protocol, so the packet being too short is an error
+ -- the old way: tree:add_expert_info(PI_MALFORMED, PI_ERROR, "packet too short")
+ -- the correct way now:
+ tree:add_proto_expert_info(ef_too_short)
+ dprint("packet length",pktlen,"too short")
+ return
+ end
+
+ -- Now let's add our transaction id under our dns protocol tree we just created.
+ -- The transaction id starts at offset 0, for 2 bytes length.
+ tree:add(pf_trasaction_id, tvbuf:range(0,2))
+
+ -- We'd like to put the transaction id number in the GUI row for this packet, in its
+ -- INFO column/cell. First we need the transaction id value, though. Since we just
+ -- dissected it with the previous code line, we could now get it using a Field's
+ -- FieldInfo extractor, but instead we'll get it directly from the TvbRange just
+ -- to show how to do that. We'll use Field/FieldInfo extractors later on...
+ local transid = tvbuf:range(0,2):uint()
+ pktinfo.cols.info:set("(".. transid ..")")
+
+ -- now let's add the flags, which are all in the packet bytes at offset 2 of length 2
+ -- instead of calling this again and again, let's just use a variable
+ local flagrange = tvbuf:range(2,2)
+
+ -- for our flags field, we want a sub-tree
+ local flag_tree = tree:add(pf_flags, flagrange)
+ -- I'm indenting this for clarity, because it's adding to the flag's child-tree
+
+ -- let's add the type of message (query vs. response)
+ local query_flag_tree = flag_tree:add(pf_flag_response, flagrange)
+
+ -- let's also add an expert info about it
+ if isResponse() then
+ query_flag_tree:add_proto_expert_info(ef_response, "It's a response!")
+ if transid == 42 then
+ tree:add_tvb_expert_info(ef_ultimate, tvbuf:range(0,2))
+ end
+ else
+ query_flag_tree:add_proto_expert_info(ef_query)
+ end
+
+ -- we now know if it's a response or query, so let's put that in the
+ -- GUI packet row, in the INFO column cell
+ -- this line of code uses a Lua trick for doing something similar to
+ -- the C/C++ 'test ? true : false' shorthand
+ pktinfo.cols.info:prepend(isResponse() and "Response " or "Query ")
+
+ flag_tree:add(pf_flag_opcode, flagrange)
+
+ if isResponse() then
+ flag_tree:add(pf_flag_authoritative, flagrange)
+ end
+
+ flag_tree:add(pf_flag_truncated, flagrange)
+
+ if isResponse() then
+ flag_tree:add(pf_flag_recursion_available, flagrange)
+ else
+ flag_tree:add(pf_flag_recursion_desired, flagrange)
+ end
+
+ flag_tree:add(pf_flag_z, flagrange)
+
+ if isResponse() then
+ flag_tree:add(pf_flag_authenticated, flagrange)
+ flag_tree:add(pf_flag_rcode, flagrange)
+ end
+
+ flag_tree:add(pf_flag_checking_disabled, flagrange)
+
+ -- now add more to the main mydns tree
+ tree:add(pf_num_questions, tvbuf:range(4,2))
+ tree:add(pf_num_answers, tvbuf:range(6,2))
+ -- another way to get a TvbRange is just to call the Tvb like this
+ tree:add(pf_num_authority_rr, tvbuf(8,2))
+ -- or if we're crazy, we can create a sub-TvbRange, from a sub-TvbRange of the TvbRange
+ tree:add(pf_num_additional_rr, tvbuf:range(10,2):range()())
+
+ local num_queries = questions_field()()
+ local pos = DNS_HDR_LEN
+
+ if num_queries > 0 then
+ -- let's create a sub-tree, using a plain text description (not a field from the packet)
+ local queries_tree = tree:add("Queries")
+
+ local pktlen_remaining = pktlen - pos
+
+ -- multiple questions in one query hasn't been used for a long time, but just in case, let's loop
+ while num_queries > 0 and pktlen_remaining > 0 do
+ if pktlen_remaining < MIN_QUERY_LEN then
+ -- old way: queries_tree:add_expert_info(PI_MALFORMED, PI_ERROR, "query field missing or too short")
+ queries_tree:add_proto_expert_info(ef_bad_query)
+ return
+ end
+
+ -- we don't know how long this query field in total is, so we have to parse it first before
+ -- adding it to the tree, because we want to identify the correct bytes it covers
+ local label_count, name, name_len = getQueryName(tvbuf:range(pos,pktlen_remaining))
+ if not label_count then
+ queries_tree:add_expert_info(PI_MALFORMED, PI_ERROR, name)
+ return
+ end
+
+ -- now add the first query to the 'Queries' child tree we just created
+ -- we're going to change the string generated by this later, after we figure out the subsequent fields.
+ -- the whole query field is the query name field length we just got, plus 2-byte type and 2-byte class.
+ local q_tree = queries_tree:add(pf_query, tvbuf:range(pos, name_len + 4))
+
+ q_tree:add(pf_query_name, tvbuf:range(pos, name_len), name)
+ pos = pos + name_len
+
+ pktinfo.cols.info:append(" "..name)
+
+ -- the following tree items are generated by us, not encoded in the packet per se, so mark them as such
+ q_tree:add(pf_query_name_len, name_len):set_generated()
+ q_tree:add(pf_query_label_count, label_count):set_generated()
+
+ q_tree:add(pf_query_type, tvbuf:range(pos, 2))
+ q_tree:add(pf_query_class, tvbuf:range(pos + 2, 2))
+ pos = pos + 4
+
+ -- now change the query text
+ -- calling a Field returns a multival of one FieldInfo object for
+ -- each value, so we select() only the most recent one
+ q_tree:set_text(name..": type "..select(-1, query_type_field()).display
+ ..", class "..select(-1, query_class_field()).display)
+
+ pktlen_remaining = pktlen_remaining - (name_len + 4)
+ num_queries = num_queries - 1
+ end -- end of while loop
+
+ if num_queries > 0 then
+ -- we didn't process them all
+ queries_tree:add_expert_info(PI_MALFORMED, PI_ERROR, num_queries .. " query field(s) missing")
+ return
+ end
+ end
+
+ -- parsing answers, authority RRs, and additional RRs is up to you!
+
+ dprint2("dns.dissector returning",pos)
+
+ -- tell wireshark how much of tvbuff we dissected
+ return pos
+end
+
+----------------------------------------
+-- we want to have our protocol dissection invoked for a specific UDP port,
+-- so get the udp dissector table and add our protocol to it
+DissectorTable.get("udp.port"):add(default_settings.port, dns)
+
+----------------------------------------
+-- we also want to add the heuristic dissector, for any UDP protocol
+-- first we need a heuristic dissection function
+-- this is that function - when wireshark invokes this, it will pass in the same
+-- things it passes in to the "dissector" function, but we only want to actually
+-- dissect it if it's for us, and we need to return true if it's for us, or else false
+-- figuring out if it's for us or not is not easy
+-- we need to try as hard as possible, or else we'll think it's for us when it's
+-- not and block other heuristic dissectors from getting their chance
+--
+-- in practice, you'd never set a dissector like this to be heuristic, because there
+-- just isn't enough information to safely detect if it's DNS or not
+-- but I'm doing it to show how it would be done
+--
+-- Note: this heuristic stuff is new in 1.11.3
+local function heur_dissect_dns(tvbuf,pktinfo,root)
+ dprint2("heur_dissect_dns called")
+
+ -- if our preferences tell us not to do this, return false
+ if not default_settings.heur_enabled then
+ return false
+ end
+
+ if tvbuf:len() < DNS_HDR_LEN then
+ dprint("heur_dissect_dns: tvb shorter than DNS_HDR_LEN of:",DNS_HDR_LEN)
+ return false
+ end
+
+ local tvbr = tvbuf:range(0,DNS_HDR_LEN)
+
+ -- the first 2 bytes are transaction id, which can be anything so no point in checking those
+ -- the next 2 bytes contain flags, a couple of which have some values we can check against
+
+ -- the opcode has to be 0, 1, 2, 4 or 5
+ -- the opcode field starts at bit offset 17 (in C-indexing), for 4 bits in length
+ local check = tvbr:bitfield(17,4)
+ if check == 3 or check > 5 then
+ dprint("heur_dissect_dns: invalid opcode:",check)
+ return false
+ end
+
+ -- the rcode has to be 0-10, 16-22 (we're ignoring private use rcodes here)
+ -- the rcode field starts at bit offset 28 (in C-indexing), for 4 bits in length
+ check = tvbr:bitfield(28,4)
+ if check > 22 or (check > 10 and check < 16) then
+ dprint("heur_dissect_dns: invalid rcode:",check)
+ return false
+ end
+
+ dprint2("heur_dissect_dns checking questions/answers")
+
+ -- now let's verify the number of questions/answers are reasonable
+ check = tvbr:range(4,2):uint() -- num questions
+ if check > 100 then return false end
+ check = tvbr:range(6,2):uint() -- num answers
+ if check > 100 then return false end
+ check = tvbr:range(8,2):uint() -- num authority
+ if check > 100 then return false end
+ check = tvbr:range(10,2):uint() -- num additional
+ if check > 100 then return false end
+
+ dprint2("heur_dissect_dns: everything looks good calling the real dissector")
+
+ -- don't do this line in your script - I'm just doing it so our test-suite can
+ -- verify this script
+ root:add("Heuristic dissector used"):set_generated()
+
+ -- ok, looks like it's ours, so go dissect it
+ -- note: calling the dissector directly like this is new in 1.11.3
+ -- also note that calling a Dissector object, as this does, means we don't
+ -- get back the return value of the dissector function we created previously
+ -- so it might be better to just call the function directly instead of doing
+ -- this, but this script is used for testing and this tests the call() function
+ dns.dissector(tvbuf,pktinfo,root)
+
+ -- since this is over a transport protocol, such as UDP, we can set the
+ -- conversation to make it sticky for our dissector, so that all future
+ -- packets to/from the same address:port pair will just call our dissector
+ -- function directly instead of this heuristic function
+ -- this is a new attribute of pinfo in 1.11.3
+ pktinfo.conversation = dns
+
+ return true
+end
+
+-- now register that heuristic dissector into the udp heuristic list
+if default_settings.heur_regmode == 1 then
+ -- this is the "normal" way to register a heuristic: using a lua function
+ dns:register_heuristic("udp",heur_dissect_dns)
+elseif default_settings.heur_regmode == 2 then
+ -- this is to test the fix for bug 10695:
+ dns:register_heuristic("udp",dns.dissector)
+elseif default_settings.heur_regmode == 3 then
+ -- and this too is to test the fix for bug 10695:
+ dns:register_heuristic("udp", function (...) return dns.dissector(...); end )
+end
+
+-- We're done!
+-- our protocol (Proto) gets automatically registered after this script finishes loading
+----------------------------------------
+
+----------------------------------------
+-- DNS query names are not just null-terminated strings; they're actually a sequence of
+-- 'labels', with a length octet before each one. So "foobar.com" is actually the
+-- string "\06foobar\03com\00". We could create a ProtoField for label_length and label_name
+-- or whatever, but since this is an example script I'll show how to do it in raw code.
+-- This function is given the TvbRange object from the dissector() function, and needs to
+-- parse it.
+-- On success, it returns three things: the number of labels, the name string, and how
+-- many bytes it covered of the buffer.
+-- On failure, it returns nil and the error message.
+getQueryName = function (tvbr)
+ local label_count = 0
+ local name = ""
+ local name_len = 0
+
+ local len_remaining = tvbr:len()
+ if len_remaining < 2 then
+ -- it's too short
+ return nil, "invalid name"
+ end
+
+ local barray = tvbr:bytes() -- gets a ByteArray of the TvbRange
+ local pos = 0 -- unlike Lua, ByteArray uses 0-based indexing
+
+ repeat
+ local label_len = barray:get_index(pos)
+ if label_len >= len_remaining then
+ return nil, "invalid label length of "..label_len
+ end
+ pos = pos + 1 -- move past label length octet
+ if label_len > 0 then
+ -- append the label and a dot to name string
+ -- note: this uses the new method of ByteArray:raw(), added in 1.11.3
+ name = name .. barray:raw(pos, label_len) .. "."
+ label_count = label_count + 1
+ pos = pos + label_len -- move past label
+ end
+ name_len = name_len + label_len + 1
+ len_remaining = len_remaining - (label_len + 1) -- subtract label and its length octet
+ until label_len == 0
+
+ -- we appended an extra dot, so get rid of it
+ name = name:sub(1, -2)
+
+ if name == "" then
+ -- this is the root zone (.)
+ name = "<Root>"
+ end
+
+ return label_count, name, name_len
+end
diff --git a/test/lua/field.lua b/test/lua/field.lua
new file mode 100644
index 0000000..f049b81
--- /dev/null
+++ b/test/lua/field.lua
@@ -0,0 +1,165 @@
+-- test script for wslua Field/FieldInfo functions
+-- use with dhcp.pcap in test/captures directory
+local testlib = require("testlib")
+
+local FRAME = "frame"
+local PER_FRAME = "per-frame"
+local OTHER = "other"
+
+local n_frames = 1
+testlib.init({
+ [FRAME] = n_frames,
+ [PER_FRAME] = n_frames*43,
+ [OTHER] = 16,
+})
+
+------------- helper funcs ------------
+
+local function toMacAddr(addrhex)
+ return addrhex:gsub("..","%0:"):sub(1,-2)
+end
+
+-- the following are so we can use pcall (which needs a function to call)
+local function makeField(name)
+ local foo = Field.new(name)
+ return true
+end
+
+local function makeFieldInfo(field)
+ local foo = field()
+ return true
+end
+
+local function setFieldInfo(finfo,name,value)
+ finfo[name] = value
+ return true
+end
+
+local function getFieldInfo(finfo,name)
+ local foo = finfo[name]
+ return true
+end
+
+--------------------------
+
+testlib.testing(OTHER, "Field")
+
+testlib.test(OTHER,"Field.new-0",pcall(makeField,"ip.src"))
+testlib.test(OTHER,"Field.new-1",not pcall(makeField,"FooBARhowdy"))
+testlib.test(OTHER,"Field.new-2",not pcall(makeField))
+testlib.test(OTHER,"Field.new-3",not pcall(makeField,""))
+testlib.test(OTHER,"Field.new-4",not pcall(makeField,"IP.SRC"))
+
+-- declare some field extractors
+local f_frame_encap_type = Field.new("frame.encap_type")
+local f_frame_proto = Field.new("frame.protocols")
+local f_eth_src = Field.new("eth.src")
+local f_eth_dst = Field.new("eth.dst")
+local f_eth_mac = Field.new("eth.addr")
+local f_ip_src = Field.new("ip.src")
+local f_ip_dst = Field.new("ip.dst")
+local f_udp_srcport = Field.new("udp.srcport")
+local f_udp_dstport = Field.new("udp.dstport")
+local f_dhcp_hw = Field.new("dhcp.hw.mac_addr")
+local f_dhcp_opt = Field.new("dhcp.option.type")
+
+testlib.test(OTHER,"Field__tostring-1", tostring(f_frame_proto) == "frame.protocols")
+
+testlib.test(OTHER,"Field.name-1", f_frame_proto.name == "frame.protocols")
+testlib.test(OTHER,"Field.name-2", f_eth_src.name == "eth.src")
+
+testlib.test(OTHER,"Field.display-1", f_frame_proto.display == "Protocols in frame")
+testlib.test(OTHER,"Field.display-2", f_eth_src.display == "Source")
+
+testlib.test(OTHER,"Field.type-1", f_frame_proto.type == ftypes.STRING)
+testlib.test(OTHER,"Field.type-2", f_eth_src.type == ftypes.ETHER)
+testlib.test(OTHER,"Field.type-3", f_ip_src.type == ftypes.IPv4)
+testlib.test(OTHER,"Field.type-4", f_udp_srcport.type == ftypes.UINT16)
+testlib.test(OTHER,"Field.type-5", f_dhcp_opt.type == ftypes.UINT8)
+
+-- make sure can't create a FieldInfo outside tap
+testlib.test(OTHER,"Field__call-1",not pcall(makeFieldInfo,f_eth_src))
+
+local tap = Listener.new()
+
+--------------------------
+
+function tap.packet(pinfo,tvb)
+ testlib.countPacket(FRAME)
+
+ testlib.testing(FRAME,"Field")
+ testlib.test(PER_FRAME,"Field__tostring-2", tostring(f_frame_proto) == "frame.protocols")
+
+ -- make sure can't create a Field inside tap
+ testlib.test(PER_FRAME,"Field.new-5",not pcall(makeField,"ip.src"))
+
+ testlib.test(PER_FRAME,"Field__call-2",pcall(makeFieldInfo,f_eth_src))
+
+ testlib.test(PER_FRAME,"Field.name-3", f_frame_proto.name == "frame.protocols")
+ testlib.test(PER_FRAME,"Field.name-4", f_eth_src.name == "eth.src")
+
+ testlib.test(PER_FRAME,"Field.display-3", f_frame_proto.display == "Protocols in frame")
+ testlib.test(PER_FRAME,"Field.display-4", f_eth_src.display == "Source")
+
+ testlib.test(PER_FRAME,"Field.type-6", f_frame_proto.type == ftypes.STRING)
+ testlib.test(PER_FRAME,"Field.type-7", f_eth_src.type == ftypes.ETHER)
+ testlib.test(PER_FRAME,"Field.type-8", f_ip_src.type == ftypes.IPv4)
+ testlib.test(PER_FRAME,"Field.type-9", f_udp_srcport.type == ftypes.UINT16)
+ testlib.test(PER_FRAME,"Field.type-10", f_dhcp_opt.type == ftypes.UINT8)
+
+ testlib.testing(FRAME,"FieldInfo")
+
+ local finfo_udp_srcport = f_udp_srcport()
+ testlib.test(PER_FRAME,"FieldInfo.name-1", finfo_udp_srcport.name == "udp.srcport")
+ testlib.test(PER_FRAME,"FieldInfo.type-1", finfo_udp_srcport.type == ftypes.UINT16)
+ testlib.test(PER_FRAME,"FieldInfo.little_endian-1", finfo_udp_srcport.little_endian == false)
+ testlib.test(PER_FRAME,"FieldInfo.big_endian-1", finfo_udp_srcport.big_endian == true)
+ testlib.test(PER_FRAME,"FieldInfo.is_url-1", finfo_udp_srcport.is_url == false)
+ testlib.test(PER_FRAME,"FieldInfo.offset-1", finfo_udp_srcport.offset == 34)
+ testlib.test(PER_FRAME,"FieldInfo.source-1", finfo_udp_srcport.source == tvb)
+
+ -- check ether addr
+ local fi_eth_src = f_eth_src()
+ testlib.test(PER_FRAME,"FieldInfo.type-2", fi_eth_src.type == ftypes.ETHER)
+ testlib.test(PER_FRAME,"FieldInfo.range-0",pcall(getFieldInfo,fi_eth_src,"range"))
+ local eth_macs = { f_eth_mac() }
+ local eth_src1 = tostring(f_eth_src().range)
+ local eth_src2 = tostring(tvb:range(6,6))
+ local eth_src3 = tostring(eth_macs[2].tvb)
+
+ testlib.test(PER_FRAME,"FieldInfo.range-1", eth_src1 == eth_src2)
+ testlib.test(PER_FRAME,"FieldInfo.range-2", eth_src1 == eth_src3)
+ testlib.test(PER_FRAME,"FieldInfo.range-3",not pcall(setFieldInfo,fi_eth_src,"range",3))
+ testlib.test(PER_FRAME,"FieldInfo.range-4", tostring(f_frame_encap_type().range) == "<EMPTY>")
+
+ testlib.test(PER_FRAME,"FieldInfo.generated-1", f_frame_proto().generated == true)
+ testlib.test(PER_FRAME,"FieldInfo.generated-2", eth_macs[2].generated == false)
+ testlib.test(PER_FRAME,"FieldInfo.generated-3",not pcall(setFieldInfo,fi_eth_src,"generated",3))
+
+ testlib.test(PER_FRAME,"FieldInfo.name-1", fi_eth_src.name == "eth.src")
+ testlib.test(PER_FRAME,"FieldInfo.name-2",not pcall(setFieldInfo,fi_eth_src,"name","3"))
+
+ testlib.test(PER_FRAME,"FieldInfo.label-1", fi_eth_src.label == tostring(fi_eth_src))
+ testlib.test(PER_FRAME,"FieldInfo.label-2", fi_eth_src.label == toMacAddr(eth_src1))
+ testlib.test(PER_FRAME,"FieldInfo.label-3",not pcall(setFieldInfo,fi_eth_src,"label","3"))
+
+ testlib.test(PER_FRAME,"FieldInfo.display-1", select(1, string.find(fi_eth_src.display, toMacAddr(eth_src1))) ~= nil)
+ testlib.test(PER_FRAME,"FieldInfo.display-2",not pcall(setFieldInfo,fi_eth_src,"display","3"))
+
+ testlib.test(PER_FRAME,"FieldInfo.eq-1", eth_macs[2] == select(2, f_eth_mac()))
+ testlib.test(PER_FRAME,"FieldInfo.eq-2", eth_macs[1] ~= fi_eth_src)
+ testlib.test(PER_FRAME,"FieldInfo.eq-3", eth_macs[1] == f_eth_dst())
+
+ testlib.test(PER_FRAME,"FieldInfo.offset-1", eth_macs[1].offset == 0)
+ testlib.test(PER_FRAME,"FieldInfo.offset-2", -fi_eth_src == 6)
+ testlib.test(PER_FRAME,"FieldInfo.offset-3",not pcall(setFieldInfo,fi_eth_src,"offset","3"))
+
+ testlib.test(PER_FRAME,"FieldInfo.len-1", fi_eth_src.len == 6)
+ testlib.test(PER_FRAME,"FieldInfo.len-2",not pcall(setFieldInfo,fi_eth_src,"len",6))
+
+ testlib.pass(FRAME)
+end
+
+function tap.draw()
+ testlib.getResults()
+end
diff --git a/test/lua/field_setup.lua b/test/lua/field_setup.lua
new file mode 100644
index 0000000..d73b13a
--- /dev/null
+++ b/test/lua/field_setup.lua
@@ -0,0 +1,108 @@
+function field_setup(proto, prefix)
+
+ local pf_boolean = ProtoField.new("Boolean", prefix..".boolean", ftypes.BOOLEAN)
+ local pf_char = ProtoField.new("Char", prefix..".char", ftypes.CHAR)
+ local pf_uint8 = ProtoField.new("Uint8", prefix..".uint8", ftypes.UINT8)
+ local pf_uint16 = ProtoField.new("Uint16", prefix..".uint16", ftypes.UINT16)
+ local pf_uint24 = ProtoField.new("Uint24", prefix..".uint24", ftypes.UINT24)
+ local pf_uint32 = ProtoField.new("Uint32", prefix..".uint32", ftypes.UINT32)
+ local pf_uint64 = ProtoField.new("Uint64", prefix..".uint64", ftypes.UINT64)
+ local pf_int8 = ProtoField.new("Int8", prefix..".int8", ftypes.INT8)
+ local pf_int16 = ProtoField.new("Int16", prefix..".int16", ftypes.INT16)
+ local pf_int24 = ProtoField.new("Int24", prefix..".int24", ftypes.INT24)
+ local pf_int32 = ProtoField.new("Int32", prefix..".int32", ftypes.INT32)
+ local pf_int64 = ProtoField.new("Int64", prefix..".int64", ftypes.INT64)
+ local pf_float = ProtoField.new("Float", prefix..".float", ftypes.FLOAT)
+ local pf_double = ProtoField.new("Double", prefix..".double", ftypes.DOUBLE)
+ local pf_absolute_time = ProtoField.new("Absolute_Time", prefix..".absolute_time", ftypes.ABSOLUTE_TIME)
+ local pf_relative_time = ProtoField.new("Relative_Time", prefix..".relative_time", ftypes.RELATIVE_TIME)
+ local pf_string = ProtoField.new("String", prefix..".string", ftypes.STRING)
+ local pf_stringz = ProtoField.new("Stringz", prefix..".stringz", ftypes.STRINGZ)
+ local pf_ether = ProtoField.new("Ether", prefix..".ether", ftypes.ETHER)
+ local pf_bytes = ProtoField.new("Bytes", prefix..".bytes", ftypes.BYTES)
+ local pf_uint_bytes = ProtoField.new("Uint_Bytes", prefix..".uint_bytes", ftypes.UINT_BYTES)
+ local pf_ipv4 = ProtoField.new("Ipv4", prefix..".ipv4", ftypes.IPv4)
+ local pf_ipv6 = ProtoField.new("Ipv6", prefix..".ipv6", ftypes.IPv6)
+ local pf_ipxnet = ProtoField.new("Ipxnet", prefix..".ipxnet", ftypes.IPXNET)
+ local pf_framenum = ProtoField.new("Framenum", prefix..".framenum", ftypes.FRAMENUM)
+ local pf_guid = ProtoField.new("Guid", prefix..".guid", ftypes.GUID)
+ local pf_oid = ProtoField.new("Oid", prefix..".oid", ftypes.OID)
+ local pf_rel_oid = ProtoField.new("Rel_Oid", prefix..".rel_oid", ftypes.REL_OID)
+ local pf_system_id = ProtoField.new("System_Id", prefix..".system_id", ftypes.SYSTEM_ID)
+ local pf_eui64 = ProtoField.new("Eui64", prefix..".eui64", ftypes.EUI64)
+
+ proto.fields = {
+ pf_boolean, pf_char, pf_uint8, pf_uint16, pf_uint24, pf_uint32, pf_uint64, pf_int8,
+ pf_int16, pf_int24, pf_int32, pf_int64, pf_float, pf_double, pf_absolute_time, pf_relative_time,
+ pf_string, pf_stringz, pf_ether, pf_bytes, pf_uint_bytes, pf_ipv4, pf_ipv6, pf_ipxnet,
+ pf_framenum, pf_guid, pf_oid, pf_rel_oid, pf_system_id, pf_eui64,
+ }
+
+ local vf_boolean = Field.new(prefix..".boolean")
+ local vf_char = Field.new(prefix..".char")
+ local vf_uint8 = Field.new(prefix..".uint8")
+ local vf_uint16 = Field.new(prefix..".uint16")
+ local vf_uint24 = Field.new(prefix..".uint24")
+ local vf_uint32 = Field.new(prefix..".uint32")
+ local vf_uint64 = Field.new(prefix..".uint64")
+ local vf_int8 = Field.new(prefix..".int8")
+ local vf_int16 = Field.new(prefix..".int16")
+ local vf_int24 = Field.new(prefix..".int24")
+ local vf_int32 = Field.new(prefix..".int32")
+ local vf_int64 = Field.new(prefix..".int64")
+ local vf_float = Field.new(prefix..".float")
+ local vf_double = Field.new(prefix..".double")
+ local vf_absolute_time = Field.new(prefix..".absolute_time")
+ local vf_relative_time = Field.new(prefix..".relative_time")
+ local vf_string = Field.new(prefix..".string")
+ local vf_stringz = Field.new(prefix..".stringz")
+ local vf_ether = Field.new(prefix..".ether")
+ local vf_bytes = Field.new(prefix..".bytes")
+ local vf_uint_bytes = Field.new(prefix..".uint_bytes")
+ local vf_ipv4 = Field.new(prefix..".ipv4")
+ local vf_ipv6 = Field.new(prefix..".ipv6")
+ local vf_ipxnet = Field.new(prefix..".ipxnet")
+ local vf_framenum = Field.new(prefix..".framenum")
+ local vf_guid = Field.new(prefix..".guid")
+ local vf_oid = Field.new(prefix..".oid")
+ local vf_rel_oid = Field.new(prefix..".rel_oid")
+ local vf_system_id = Field.new(prefix..".system_id")
+ local vf_eui64 = Field.new(prefix..".eui64")
+
+ local fieldmap = {
+ ["boolean"] = {packet_field = pf_boolean, value_field = vf_boolean},
+ ["char"] = {packet_field = pf_char, value_field = vf_char},
+ ["uint8"] = {packet_field = pf_uint8, value_field = vf_uint8},
+ ["uint16"] = {packet_field = pf_uint16, value_field = vf_uint16},
+ ["uint24"] = {packet_field = pf_uint24, value_field = vf_uint24},
+ ["uint32"] = {packet_field = pf_uint32, value_field = vf_uint32},
+ ["uint64"] = {packet_field = pf_uint64, value_field = vf_uint64},
+ ["int8"] = {packet_field = pf_int8, value_field = vf_int8},
+ ["int16"] = {packet_field = pf_int16, value_field = vf_int16},
+ ["int24"] = {packet_field = pf_int24, value_field = vf_int24},
+ ["int32"] = {packet_field = pf_int32, value_field = vf_int32},
+ ["int64"] = {packet_field = pf_int64, value_field = vf_int64},
+ ["float"] = {packet_field = pf_float, value_field = vf_float},
+ ["double"] = {packet_field = pf_double, value_field = vf_double},
+ ["absolute_time"] = {packet_field = pf_absolute_time, value_field = vf_absolute_time},
+ ["relative_time"] = {packet_field = pf_relative_time, value_field = vf_relative_time},
+ ["string"] = {packet_field = pf_string, value_field = vf_string},
+ ["stringz"] = {packet_field = pf_stringz, value_field = vf_stringz},
+ ["ether"] = {packet_field = pf_ether, value_field = vf_ether},
+ ["bytes"] = {packet_field = pf_bytes, value_field = vf_bytes},
+ ["uint_bytes"] = {packet_field = pf_uint_bytes, value_field = vf_uint_bytes},
+ ["ipv4"] = {packet_field = pf_ipv4, value_field = vf_ipv4},
+ ["ipv6"] = {packet_field = pf_ipv6, value_field = vf_ipv6},
+ ["ipxnet"] = {packet_field = pf_ipxnet, value_field = vf_ipxnet},
+ ["framenum"] = {packet_field = pf_framenum, value_field = vf_framenum},
+ ["guid"] = {packet_field = pf_guid, value_field = vf_guid},
+ ["oid"] = {packet_field = pf_oid, value_field = vf_oid},
+ ["rel_oid"] = {packet_field = pf_rel_oid, value_field = vf_rel_oid},
+ ["system_id"] = {packet_field = pf_system_id, value_field = vf_system_id},
+ ["eui64"] = {packet_field = pf_eui64, value_field = vf_eui64},
+ }
+
+ return fieldmap
+end
+
+return field_setup
diff --git a/test/lua/globals_2.2.txt b/test/lua/globals_2.2.txt
new file mode 100644
index 0000000..4e32457
--- /dev/null
+++ b/test/lua/globals_2.2.txt
@@ -0,0 +1,1221 @@
+-- Wireshark version: 1.12.6
+{
+ ["Address"] = {
+ ["__typeof"] = "Address",
+ ["ip"] = '<function 1>',
+ ["ipv4"] = '<function 1>',
+ ['<metatable>'] = {
+ ["__eq"] = '<function 2>',
+ ["__index"] = '<filtered>',
+ ["__le"] = '<function 4>',
+ ["__lt"] = '<function 5>',
+ ["__methods"] = '<table 2>',
+ ["__tostring"] = '<function 6>',
+ ["__typeof"] = "Address"
+ }
+ },
+ ["ByteArray"] = {
+ ["__typeof"] = "ByteArray",
+ ["append"] = '<function 7>',
+ ["base64_decode"] = '<function 8>',
+ ["get_index"] = '<function 9>',
+ ["len"] = '<function 10>',
+ ["new"] = '<function 11>',
+ ["prepend"] = '<function 12>',
+ ["raw"] = '<function 13>',
+ ["set_index"] = '<function 14>',
+ ["set_size"] = '<function 15>',
+ ["subset"] = '<function 16>',
+ ["tohex"] = '<function 17>',
+ ["tvb"] = '<function 18>',
+ ['<metatable>'] = {
+ ["__call"] = '<function 16>',
+ ["__concat"] = '<function 19>',
+ ["__eq"] = '<function 20>',
+ ["__index"] = '<filtered>',
+ ["__methods"] = '<table 3>',
+ ["__tostring"] = '<function 22>',
+ ["__typeof"] = "ByteArray"
+ }
+ },
+ ["Column"] = {
+ ["__typeof"] = "Column",
+ ["append"] = '<function 23>',
+ ["clear"] = '<function 24>',
+ ["clear_fence"] = '<function 25>',
+ ["fence"] = '<function 26>',
+ ["prepend"] = '<function 27>',
+ ["preppend"] = '<function 27>',
+ ["set"] = '<function 28>',
+ ['<metatable>'] = {
+ ["__index"] = '<filtered>',
+ ["__methods"] = '<table 4>',
+ ["__tostring"] = '<function 30>',
+ ["__typeof"] = "Column"
+ }
+ },
+ ["DATA_DIR"] = '<filtered>',
+ ["DESEGMENT_ONE_MORE_SEGMENT"] = 268435455,
+ ["Dir"] = {
+ ["__typeof"] = "Dir",
+ ["close"] = '<function 31>',
+ ["exists"] = '<function 32>',
+ ["global_config_path"] = '<function 33>',
+ ["global_plugins_path"] = '<function 34>',
+ ["make"] = '<function 35>',
+ ["open"] = '<function 36>',
+ ["personal_config_path"] = '<function 37>',
+ ["personal_plugins_path"] = '<function 38>',
+ ["remove"] = '<function 39>',
+ ["remove_all"] = '<function 40>',
+ ['<metatable>'] = {
+ ["__call"] = '<function 41>',
+ ["__index"] = '<filtered>',
+ ["__methods"] = '<table 5>',
+ ["__typeof"] = "Dir"
+ }
+ },
+ ["Dissector"] = {
+ ["__typeof"] = "Dissector",
+ ["call"] = '<function 43>',
+ ["get"] = '<function 44>',
+ ["list"] = '<function 45>',
+ ['<metatable>'] = {
+ ["__call"] = '<function 46>',
+ ["__index"] = '<filtered>',
+ ["__methods"] = '<table 6>',
+ ["__tostring"] = '<function 48>',
+ ["__typeof"] = "Dissector"
+ }
+ },
+ ["DissectorTable"] = {
+ ["__typeof"] = "DissectorTable",
+ ["add"] = '<function 49>',
+ ["get"] = '<function 50>',
+ ["get_dissector"] = '<function 51>',
+ ["heuristic_list"] = '<function 52>',
+ ["list"] = '<function 53>',
+ ["new"] = '<function 54>',
+ ["remove"] = '<function 55>',
+ ["remove_all"] = '<function 56>',
+ ["set"] = '<function 57>',
+ ["try"] = '<function 58>',
+ ['<metatable>'] = {
+ ["__index"] = '<filtered>',
+ ["__methods"] = '<table 7>',
+ ["__tostring"] = '<function 60>',
+ ["__typeof"] = "DissectorTable"
+ }
+ },
+ ["Dumper"] = {
+ ["__typeof"] = "Dumper",
+ ["close"] = '<function 61>',
+ ["dump"] = '<function 62>',
+ ["dump_current"] = '<function 63>',
+ ["flush"] = '<function 64>',
+ ["new"] = '<function 65>',
+ ["new_for_current"] = '<function 66>',
+ ['<metatable>'] = {
+ ["__index"] = '<filtered>',
+ ["__methods"] = '<table 8>',
+ ["__typeof"] = "Dumper"
+ }
+ },
+ ["ENC_3GPP_TS_23_038_7BITS"] = 44,
+ ["ENC_ASCII"] = 0,
+ ["ENC_ASCII_7BITS"] = 52,
+ ["ENC_BIG_ENDIAN"] = 0,
+ ["ENC_CHARENCODING_MASK"] = 2147483646,
+ ["ENC_CP437"] = 50,
+ ["ENC_EBCDIC"] = 46,
+ ["ENC_ISO_8601_DATE"] = 65536,
+ ["ENC_ISO_8601_DATE_TIME"] = 196608,
+ ["ENC_ISO_8601_DATE_TIME_BASIC"] = 1048576,
+ ["ENC_ISO_8601_TIME"] = 131072,
+ ["ENC_ISO_8859_1"] = 10,
+ ["ENC_ISO_8859_10"] = 28,
+ ["ENC_ISO_8859_11"] = 30,
+ ["ENC_ISO_8859_13"] = 34,
+ ["ENC_ISO_8859_14"] = 36,
+ ["ENC_ISO_8859_15"] = 38,
+ ["ENC_ISO_8859_16"] = 40,
+ ["ENC_ISO_8859_2"] = 12,
+ ["ENC_ISO_8859_3"] = 14,
+ ["ENC_ISO_8859_4"] = 16,
+ ["ENC_ISO_8859_5"] = 18,
+ ["ENC_ISO_8859_6"] = 20,
+ ["ENC_ISO_8859_7"] = 22,
+ ["ENC_ISO_8859_8"] = 24,
+ ["ENC_ISO_8859_9"] = 26,
+ ["ENC_LITTLE_ENDIAN"] = 2147483648,
+ ["ENC_MAC_ROMAN"] = 48,
+ ["ENC_NA"] = 0,
+ ["ENC_NUM_PREF"] = 2097152,
+ ["ENC_RFC_1123"] = 524288,
+ ["ENC_RFC_822"] = 262144,
+ ["ENC_SEP_COLON"] = 131072,
+ ["ENC_SEP_DASH"] = 262144,
+ ["ENC_SEP_DOT"] = 524288,
+ ["ENC_SEP_MASK"] = 2031616,
+ ["ENC_SEP_NONE"] = 65536,
+ ["ENC_SEP_SPACE"] = 1048576,
+ ["ENC_STRING"] = 50331648,
+ ["ENC_STR_HEX"] = 33554432,
+ ["ENC_STR_MASK"] = 65534,
+ ["ENC_STR_NUM"] = 16777216,
+ ["ENC_STR_TIME_MASK"] = 983040,
+ ["ENC_TIME_NTP"] = 2,
+ ["ENC_TIME_TIMESPEC"] = 0,
+ ["ENC_TIME_TOD"] = 4,
+ ["ENC_UCS_2"] = 6,
+ ["ENC_UCS_4"] = 8,
+ ["ENC_UTF_16"] = 4,
+ ["ENC_UTF_8"] = 2,
+ ["ENC_WINDOWS_1250"] = 42,
+ ["Field"] = {
+ ["__typeof"] = "Field",
+ ["list"] = '<function 68>',
+ ["new"] = '<function 69>',
+ ['<metatable>'] = {
+ ["__call"] = '<function 70>',
+ ["__index"] = '<filtered>',
+ ["__methods"] = '<table 9>',
+ ["__tostring"] = '<function 72>',
+ ["__typeof"] = "Field"
+ }
+ },
+ ["File"] = {
+ ["__typeof"] = "File",
+ ["lines"] = '<function 73>',
+ ["read"] = '<function 74>',
+ ["seek"] = '<function 75>',
+ ["write"] = '<function 76>',
+ ['<metatable>'] = {
+ ["__getters"] = {
+ ["__typeof"] = "getter",
+ ["compressed"] = '<function 78>'
+ },
+ ["__index"] = '<filtered>',
+ ["__methods"] = '<table 10>',
+ ["__newindex"] = '<function 79>',
+ ["__setters"] = {
+ ["__typeof"] = "setter"
+ },
+ ["__tostring"] = '<function 80>',
+ ["__typeof"] = "File"
+ }
+ },
+ ["FileHandler"] = {
+ ["__typeof"] = "FileHandler",
+ ["new"] = '<function 81>',
+ ['<metatable>'] = {
+ ["__getters"] = {
+ ["__typeof"] = "getter",
+ ["extensions"] = '<function 83>',
+ ["supported_comment_types"] = '<function 84>',
+ ["type"] = '<function 85>',
+ ["writes_name_resolution"] = '<function 86>',
+ ["writing_must_seek"] = '<function 87>'
+ },
+ ["__index"] = '<filtered>',
+ ["__methods"] = '<table 11>',
+ ["__newindex"] = '<function 88>',
+ ["__setters"] = {
+ ["__typeof"] = "setter",
+ ["can_write_encap"] = '<function 89>',
+ ["extensions"] = '<function 90>',
+ ["read"] = '<function 91>',
+ ["read_close"] = '<function 92>',
+ ["read_open"] = '<function 93>',
+ ["seek_read"] = '<function 94>',
+ ["seq_read_close"] = '<function 95>',
+ ["supported_comment_types"] = '<function 96>',
+ ["write"] = '<function 97>',
+ ["write_close"] = '<function 98>',
+ ["write_open"] = '<function 99>',
+ ["writes_name_resolution"] = '<function 100>',
+ ["writing_must_seek"] = '<function 101>'
+ },
+ ["__tostring"] = '<function 102>',
+ ["__typeof"] = "FileHandler"
+ }
+ },
+ ["FrameInfo"] = {
+ ["__typeof"] = "FrameInfo",
+ ["read_data"] = '<function 103>',
+ ['<metatable>'] = {
+ ["__getters"] = {
+ ["__typeof"] = "getter",
+ ["captured_length"] = '<function 105>',
+ ["comment"] = '<function 106>',
+ ["data"] = '<function 107>',
+ ["encap"] = '<function 108>',
+ ["flags"] = '<function 109>',
+ ["original_length"] = '<function 110>',
+ ["rec_type"] = '<function 111>',
+ ["time"] = '<function 112>'
+ },
+ ["__index"] = '<filtered>',
+ ["__methods"] = '<table 12>',
+ ["__newindex"] = '<function 113>',
+ ["__setters"] = {
+ ["__typeof"] = "setter",
+ ["captured_length"] = '<function 114>',
+ ["comment"] = '<function 115>',
+ ["data"] = '<function 116>',
+ ["encap"] = '<function 117>',
+ ["flags"] = '<function 118>',
+ ["original_length"] = '<function 119>',
+ ["rec_type"] = '<function 120>',
+ ["time"] = '<function 121>'
+ },
+ ["__tostring"] = '<function 122>',
+ ["__typeof"] = "FrameInfo"
+ }
+ },
+ ["FrameInfoConst"] = {
+ ["__typeof"] = "FrameInfoConst",
+ ["write_data"] = '<function 123>',
+ ['<metatable>'] = {
+ ["__getters"] = {
+ ["__typeof"] = "getter",
+ ["captured_length"] = '<function 125>',
+ ["comment"] = '<function 126>',
+ ["data"] = '<function 127>',
+ ["encap"] = '<function 128>',
+ ["flags"] = '<function 129>',
+ ["original_length"] = '<function 130>',
+ ["rec_type"] = '<function 131>',
+ ["time"] = '<function 132>'
+ },
+ ["__index"] = '<filtered>',
+ ["__methods"] = '<table 13>',
+ ["__newindex"] = '<function 133>',
+ ["__setters"] = {
+ ["__typeof"] = "setter"
+ },
+ ["__tostring"] = '<function 134>',
+ ["__typeof"] = "FrameInfoConst"
+ }
+ },
+ ["GUI_ENABLED"] = false,
+ ["H225_ALERTING"] = 3,
+ ["H225_CALL_PROCEDING"] = 1,
+ ["H225_CONNECT"] = 2,
+ ["H225_CS"] = 1,
+ ["H225_EMPTY"] = 8,
+ ["H225_FACILITY"] = 6,
+ ["H225_INFORMATION"] = 4,
+ ["H225_NOTIFY"] = 12,
+ ["H225_OTHER"] = 13,
+ ["H225_OTHERS"] = 2,
+ ["H225_PROGRESS"] = 7,
+ ["H225_RAS"] = 0,
+ ["H225_RELEASE_COMPLET"] = 5,
+ ["H225_SETUP"] = 0,
+ ["H225_SETUP_ACK"] = 11,
+ ["H225_STATUS"] = 9,
+ ["H225_STATUS_INQUIRY"] = 10,
+ ["Int64"] = {
+ ["__typeof"] = "Int64",
+ ["arshift"] = '<function 145>',
+ ["band"] = '<function 146>',
+ ["bnot"] = '<function 147>',
+ ["bor"] = '<function 148>',
+ ["bswap"] = '<function 149>',
+ ["bxor"] = '<function 150>',
+ ["decode"] = '<function 151>',
+ ["encode"] = '<function 152>',
+ ["fromhex"] = '<function 153>',
+ ["higher"] = '<function 154>',
+ ["lower"] = '<function 155>',
+ ["lshift"] = '<function 156>',
+ ["max"] = '<function 157>',
+ ["min"] = '<function 158>',
+ ["new"] = '<function 159>',
+ ["rol"] = '<function 160>',
+ ["ror"] = '<function 161>',
+ ["rshift"] = '<function 162>',
+ ["tohex"] = '<function 163>',
+ ["tonumber"] = '<function 164>',
+ ['<metatable>'] = {
+ ["__add"] = '<function 165>',
+ ["__call"] = '<function 166>',
+ ["__concat"] = '<function 167>',
+ ["__div"] = '<function 168>',
+ ["__eq"] = '<function 169>',
+ ["__index"] = '<filtered>',
+ ["__le"] = '<function 171>',
+ ["__lt"] = '<function 172>',
+ ["__methods"] = '<table 14>',
+ ["__mod"] = '<function 173>',
+ ["__mul"] = '<function 174>',
+ ["__pow"] = '<function 175>',
+ ["__sub"] = '<function 176>',
+ ["__tostring"] = '<function 177>',
+ ["__typeof"] = "Int64",
+ ["__unm"] = '<function 178>'
+ }
+ },
+ ["Listener"] = {
+ ["__typeof"] = "Listener",
+ ["list"] = '<function 179>',
+ ["new"] = '<function 180>',
+ ["remove"] = '<function 181>',
+ ['<metatable>'] = {
+ ["__getters"] = {
+ ["__typeof"] = "getter"
+ },
+ ["__index"] = '<filtered>',
+ ["__methods"] = '<table 15>',
+ ["__newindex"] = '<function 183>',
+ ["__setters"] = {
+ ["__typeof"] = "setter",
+ ["draw"] = '<function 184>',
+ ["packet"] = '<function 185>',
+ ["reset"] = '<function 186>'
+ },
+ ["__tostring"] = '<function 187>',
+ ["__typeof"] = "Listener"
+ }
+ },
+ ["MENU_PACKET_ANALYZE_UNSORTED"] = 0,
+ ["MENU_ANALYZE_CONVERSATION_FILTER"] = 1,
+ ["MENU_STAT_UNSORTED"] = 2,
+ ["MENU_STAT_GENERIC"] = 3,
+ ["MENU_STAT_CONVERSATION_LIST"] = 4,
+ ["MENU_STAT_ENDPOINT_LIST"] = 5,
+ ["MENU_STAT_RESPONSE_TIME"] = 6,
+ ["MENU_STAT_RSERPOOL"] = 7,
+ ["MENU_STAT_TELEPHONY"] = 8,
+ ["MENU_STAT_TELEPHONY_ANSI"] = 9,
+ ["MENU_STAT_TELEPHONY_GSM"] = 10,
+ ["MENU_STAT_TELEPHONY_LTE"] = 11,
+ ["MENU_STAT_TELEPHONY_MTP3"] = 12,
+ ["MENU_STAT_TELEPHONY_SCTP"] = 13,
+ ["MENU_TOOLS_UNSORTED"] = 14,
+ ["MENU_LOG_ANALYZE_UNSORTED"] = 15,
+ ["MENU_LOG_STAT_UNSORTED"] = 16,
+ ["NSTime"] = '<filtered>',
+ ["PI_ASSUMPTION"] = 218103808,
+ ["PI_CHAT"] = 2097152,
+ ["PI_CHECKSUM"] = 16777216,
+ ["PI_COMMENT"] = 1048576,
+ ["PI_COMMENTS_GROUP"] = 184549376,
+ ["PI_DEBUG"] = 134217728,
+ ["PI_DECRYPTION"] = 201326592,
+ ["PI_DEPRECATED"] = 234881024,
+ ["PI_ERROR"] = 8388608,
+ ["PI_GROUP_MASK"] = 4278190080,
+ ["PI_MALFORMED"] = 117440512,
+ ["PI_NOTE"] = 4194304,
+ ["PI_PROTOCOL"] = 150994944,
+ ["PI_REASSEMBLE"] = 100663296,
+ ["PI_REQUEST_CODE"] = 67108864,
+ ["PI_RESPONSE_CODE"] = 50331648,
+ ["PI_SECURITY"] = 167772160,
+ ["PI_SEQUENCE"] = 33554432,
+ ["PI_SEVERITY_MASK"] = 15728640,
+ ["PI_UNDECODED"] = 83886080,
+ ["PI_WARN"] = 6291456,
+ ["Pref"] = {
+ ["__typeof"] = "Pref",
+ ["bool"] = '<function 188>',
+ ["enum"] = '<function 189>',
+ ["range"] = '<function 190>',
+ ["statictext"] = '<function 191>',
+ ["string"] = '<function 192>',
+ ["uint"] = '<function 193>',
+ ['<metatable>'] = {
+ ["__index"] = '<filtered>',
+ ["__methods"] = '<table 16>',
+ ["__typeof"] = "Pref"
+ }
+ },
+ ["ProgDlg"] = {
+ ["__typeof"] = "ProgDlg",
+ ["close"] = '<function 195>',
+ ["new"] = '<function 196>',
+ ["stopped"] = '<function 197>',
+ ["update"] = '<function 198>',
+ ['<metatable>'] = {
+ ["__index"] = '<filtered>',
+ ["__methods"] = '<table 17>',
+ ["__tostring"] = '<function 200>',
+ ["__typeof"] = "ProgDlg"
+ }
+ },
+ ["Proto"] = '<filtered>',
+ ["ProtoExpert"] = {
+ ["__typeof"] = "ProtoExpert",
+ ["new"] = '<function 201>',
+ ['<metatable>'] = {
+ ["__index"] = '<filtered>',
+ ["__methods"] = '<table 18>',
+ ["__tostring"] = '<function 203>',
+ ["__typeof"] = "ProtoExpert"
+ }
+ },
+ ["ProtoField"] = {
+ ["__typeof"] = "ProtoField",
+ ["absolute_time"] = '<function 204>',
+ ["bool"] = '<function 205>',
+ ["bytes"] = '<function 206>',
+ ["double"] = '<function 207>',
+ ["ether"] = '<function 208>',
+ ["float"] = '<function 209>',
+ ["framenum"] = '<function 210>',
+ ["guid"] = '<function 211>',
+ ["int16"] = '<function 212>',
+ ["int24"] = '<function 213>',
+ ["int32"] = '<function 214>',
+ ["int64"] = '<function 215>',
+ ["int8"] = '<function 216>',
+ ["ipv4"] = '<function 217>',
+ ["ipv6"] = '<function 218>',
+ ["ipx"] = '<function 219>',
+ ["new"] = '<function 220>',
+ ["oid"] = '<function 221>',
+ ["rel_oid"] = '<function 222>',
+ ["relative_time"] = '<function 223>',
+ ["string"] = '<function 224>',
+ ["stringz"] = '<function 225>',
+ ["systemid"] = '<function 226>',
+ ["ubytes"] = '<function 227>',
+ ["uint16"] = '<function 228>',
+ ["uint24"] = '<function 229>',
+ ["uint32"] = '<function 230>',
+ ["uint64"] = '<function 231>',
+ ["uint8"] = '<function 232>',
+ ['<metatable>'] = {
+ ["__index"] = '<filtered>',
+ ["__methods"] = '<table 19>',
+ ["__tostring"] = '<function 234>',
+ ["__typeof"] = "ProtoField"
+ }
+ },
+ ["PseudoHeader"] = {
+ ["__typeof"] = "PseudoHeader",
+ ["atm"] = '<function 235>',
+ ["eth"] = '<function 236>',
+ ["mtp2"] = '<function 237>',
+ ["none"] = '<function 238>',
+ ['<metatable>'] = {
+ ["__index"] = '<filtered>',
+ ["__methods"] = '<table 20>',
+ ["__typeof"] = "PseudoHeader"
+ }
+ },
+ ["Struct"] = {
+ ["__typeof"] = "Struct",
+ ["fromhex"] = '<function 240>',
+ ["pack"] = '<function 241>',
+ ["size"] = '<function 242>',
+ ["tohex"] = '<function 243>',
+ ["unpack"] = '<function 244>',
+ ["values"] = '<function 245>',
+ ['<metatable>'] = {
+ ["__index"] = '<filtered>',
+ ["__methods"] = '<table 21>',
+ ["__typeof"] = "Struct"
+ }
+ },
+ ["TextWindow"] = {
+ ["__typeof"] = "TextWindow",
+ ["add_button"] = '<function 247>',
+ ["append"] = '<function 248>',
+ ["clear"] = '<function 249>',
+ ["get_text"] = '<function 250>',
+ ["new"] = '<function 251>',
+ ["prepend"] = '<function 252>',
+ ["set"] = '<function 253>',
+ ["set_atclose"] = '<function 254>',
+ ["set_editable"] = '<function 255>',
+ ['<metatable>'] = {
+ ["__index"] = '<filtered>',
+ ["__methods"] = '<table 22>',
+ ["__tostring"] = '<function 250>',
+ ["__typeof"] = "TextWindow"
+ }
+ },
+ ["TreeItem"] = {
+ ["__typeof"] = "TreeItem",
+ ["add"] = '<function 257>',
+ ["add_expert_info"] = '<function 258>',
+ ["add_le"] = '<function 259>',
+ ["add_packet_field"] = '<function 260>',
+ ["add_proto_expert_info"] = '<function 261>',
+ ["add_tvb_expert_info"] = '<function 262>',
+ ["append_text"] = '<function 263>',
+ ["prepend_text"] = '<function 264>',
+ ["set_generated"] = '<function 265>',
+ ["set_hidden"] = '<function 266>',
+ ["set_len"] = '<function 267>',
+ ["set_text"] = '<function 268>',
+ ['<metatable>'] = {
+ ["__index"] = '<filtered>',
+ ["__methods"] = '<table 23>',
+ ["__typeof"] = "TreeItem"
+ }
+ },
+ ["Tvb"] = {
+ ["__typeof"] = "Tvb",
+ ["len"] = '<function 270>',
+ ["offset"] = '<function 271>',
+ ["range"] = '<function 272>',
+ ["raw"] = '<function 273>',
+ ["reported_len"] = '<function 274>',
+ ["reported_length_remaining"] = '<function 275>',
+ ['<metatable>'] = {
+ ["__call"] = '<function 272>',
+ ["__index"] = '<filtered>',
+ ["__methods"] = '<table 24>',
+ ["__tostring"] = '<function 277>',
+ ["__typeof"] = "Tvb"
+ }
+ },
+ ["TvbRange"] = {
+ ["__typeof"] = "TvbRange",
+ ["bitfield"] = '<function 278>',
+ ["bytes"] = '<function 279>',
+ ["ether"] = '<function 280>',
+ ["float"] = '<function 281>',
+ ["int"] = '<function 282>',
+ ["int64"] = '<function 283>',
+ ["ipv4"] = '<function 284>',
+ ["le_float"] = '<function 285>',
+ ["le_int"] = '<function 286>',
+ ["le_int64"] = '<function 287>',
+ ["le_ipv4"] = '<function 288>',
+ ["le_nstime"] = '<function 289>',
+ ["le_uint"] = '<function 290>',
+ ["le_uint64"] = '<function 291>',
+ ["le_ustring"] = '<function 292>',
+ ["le_ustringz"] = '<function 293>',
+ ["len"] = '<function 294>',
+ ["nstime"] = '<function 295>',
+ ["offset"] = '<function 296>',
+ ["range"] = '<function 297>',
+ ["raw"] = '<function 298>',
+ ["string"] = '<function 299>',
+ ["stringz"] = '<function 300>',
+ ["strsize"] = '<function 301>',
+ ["tvb"] = '<function 302>',
+ ["uint"] = '<function 303>',
+ ["uint64"] = '<function 304>',
+ ["uncompress"] = '<function 305>',
+ ["ustring"] = '<function 306>',
+ ["ustringz"] = '<function 307>',
+ ['<metatable>'] = {
+ ["__call"] = '<function 297>',
+ ["__concat"] = '<function 167>',
+ ["__index"] = '<filtered>',
+ ["__methods"] = '<table 25>',
+ ["__tostring"] = '<function 309>',
+ ["__typeof"] = "TvbRange"
+ }
+ },
+ ["UInt64"] = {
+ ["__typeof"] = "UInt64",
+ ["arshift"] = '<function 310>',
+ ["band"] = '<function 311>',
+ ["bnot"] = '<function 312>',
+ ["bor"] = '<function 313>',
+ ["bswap"] = '<function 314>',
+ ["bxor"] = '<function 315>',
+ ["decode"] = '<function 316>',
+ ["encode"] = '<function 317>',
+ ["fromhex"] = '<function 318>',
+ ["higher"] = '<function 319>',
+ ["lower"] = '<function 320>',
+ ["lshift"] = '<function 321>',
+ ["max"] = '<function 322>',
+ ["min"] = '<function 323>',
+ ["new"] = '<function 324>',
+ ["rol"] = '<function 325>',
+ ["ror"] = '<function 326>',
+ ["rshift"] = '<function 327>',
+ ["tohex"] = '<function 328>',
+ ["tonumber"] = '<function 329>',
+ ['<metatable>'] = {
+ ["__add"] = '<function 330>',
+ ["__call"] = '<function 331>',
+ ["__concat"] = '<function 167>',
+ ["__div"] = '<function 332>',
+ ["__eq"] = '<function 333>',
+ ["__index"] = '<filtered>',
+ ["__le"] = '<function 335>',
+ ["__lt"] = '<function 336>',
+ ["__methods"] = '<table 26>',
+ ["__mod"] = '<function 337>',
+ ["__mul"] = '<function 338>',
+ ["__pow"] = '<function 339>',
+ ["__sub"] = '<function 340>',
+ ["__tostring"] = '<function 341>',
+ ["__typeof"] = "UInt64",
+ ["__unm"] = '<function 342>'
+ }
+ },
+ ["USER_DIR"] = '<filtered>',
+ ["_G"] = '<table 1>',
+ ["_VERSION"] = '<filtered>',
+ ["all_field_infos"] = '<function 343>',
+ ["apply_filter"] = '<function 344>',
+ ["assert"] = '<function 345>',
+ ["base"] = {
+ ["CUSTOM"] = 6,
+ ["DEC"] = 1,
+ ["DEC_HEX"] = 4,
+ ["HEX"] = 2,
+ ["HEX_DEC"] = 5,
+ ["NONE"] = 0,
+ ["OCT"] = 3
+ },
+ ["bit"] = {
+ ["arshift"] = '<function 346>',
+ ["band"] = '<function 347>',
+ ["bnot"] = '<function 348>',
+ ["bor"] = '<function 349>',
+ ["bswap"] = '<function 350>',
+ ["bxor"] = '<function 351>',
+ ["lshift"] = '<function 352>',
+ ["rol"] = '<function 353>',
+ ["ror"] = '<function 354>',
+ ["rshift"] = '<function 355>',
+ ["tobit"] = '<function 356>',
+ ["tohex"] = '<function 357>'
+ },
+ ["bit32"] = {
+ ["arshift"] = '<function 358>',
+ ["band"] = '<function 359>',
+ ["bnot"] = '<function 360>',
+ ["bor"] = '<function 361>',
+ ["btest"] = '<function 362>',
+ ["bxor"] = '<function 363>',
+ ["extract"] = '<function 364>',
+ ["lrotate"] = '<function 365>',
+ ["lshift"] = '<function 366>',
+ ["replace"] = '<function 367>',
+ ["rrotate"] = '<function 368>',
+ ["rshift"] = '<function 369>'
+ },
+ ["browser_open_data_file"] = '<function 370>',
+ ["browser_open_url"] = '<function 371>',
+ ["collectgarbage"] = '<function 372>',
+ ["copy_to_clipboard"] = '<function 373>',
+ ["coroutine"] = {
+ ["create"] = '<function 374>',
+ ["resume"] = '<function 375>',
+ ["running"] = '<function 376>',
+ ["status"] = '<function 377>',
+ ["wrap"] = '<function 378>',
+ ["yield"] = '<function 379>'
+ },
+ ["datafile_path"] = '<function 33>',
+ ["deregister_filehandler"] = '<function 382>',
+ ["dofile"] = '<function 383>',
+ ["error"] = '<function 384>',
+ ["expert"] = {
+ ["group"] = {
+ ["CHECKSUM"] = 16777216,
+ ["COMMENTS_GROUP"] = 184549376,
+ ["DEBUG"] = 134217728,
+ ["MALFORMED"] = 117440512,
+ ["PROTOCOL"] = 150994944,
+ ["REASSEMBLE"] = 100663296,
+ ["REQUEST_CODE"] = 67108864,
+ ["RESPONSE_CODE"] = 50331648,
+ ["SECURITY"] = 167772160,
+ ["SEQUENCE"] = 33554432,
+ ["UNDECODED"] = 83886080
+ },
+ ["severity"] = {
+ ["CHAT"] = 2097152,
+ ["COMMENT"] = 1048576,
+ ["ERROR"] = 8388608,
+ ["NOTE"] = 4194304,
+ ["WARN"] = 6291456
+ }
+ },
+ ["file_exists"] = '<function 385>',
+ ["format_date"] = '<function 386>',
+ ["format_time"] = '<function 387>',
+ ["ftypes"] = {
+ ["ABSOLUTE_TIME"] = 15,
+ ["AX25"] = 31,
+ ["BOOLEAN"] = 2,
+ ["BYTES"] = 21,
+ ["DOUBLE"] = 14,
+ ["ETHER"] = 20,
+ ["EUI64"] = 30,
+ ["FLOAT"] = 13,
+ ["FRAMENUM"] = 26,
+ ["GUID"] = 28,
+ ["INT16"] = 9,
+ ["INT24"] = 10,
+ ["INT32"] = 11,
+ ["INT64"] = 12,
+ ["INT8"] = 8,
+ ["IPXNET"] = 25,
+ ["IPv4"] = 23,
+ ["IPv6"] = 24,
+ ["NONE"] = 0,
+ ["OID"] = 29,
+ ["PROTOCOL"] = 1,
+ ["RELATIVE_TIME"] = 16,
+ ["REL_OID"] = 33,
+ ["STRING"] = 17,
+ ["STRINGZ"] = 18,
+ ["STRINGZPAD"] = 35,
+ ["SYSTEM_ID"] = 34,
+ ["UINT16"] = 4,
+ ["UINT24"] = 5,
+ ["UINT32"] = 6,
+ ["UINT64"] = 7,
+ ["UINT8"] = 3,
+ ["UINT_BYTES"] = 22,
+ ["UINT_STRING"] = 19,
+ ["VINES"] = 32
+ },
+ ["get_filter"] = '<function 388>',
+ ["get_version"] = '<function 389>',
+ ["getmetatable"] = '<function 390>',
+ ["gui_enabled"] = '<function 391>',
+ ["h225_cs_type"] = {
+ [1] = "H225_CALL_PROCEDING",
+ [2] = "H225_CONNECT",
+ [3] = "H225_ALERTING",
+ [4] = "H225_INFORMATION",
+ [5] = "H225_RELEASE_COMPLET",
+ [6] = "H225_FACILITY",
+ [7] = "H225_PROGRESS",
+ [8] = "H225_EMPTY",
+ [9] = "H225_STATUS",
+ [10] = "H225_STATUS_INQUIRY",
+ [11] = "H225_SETUP_ACK",
+ [12] = "H225_NOTIFY",
+ [13] = "H225_OTHER",
+ [0] = "H225_SETUP"
+ },
+ ["h225_msg_type"] = {
+ [1] = "H225_CS",
+ [2] = "H225_OTHERS",
+ [0] = "H225_RAS"
+ },
+ ["init_routines"] = {},
+ ["io"] = {
+ ["close"] = '<function 393>',
+ ["flush"] = '<function 394>',
+ ["input"] = '<function 395>',
+ ["lines"] = '<function 396>',
+ ["open"] = '<function 397>',
+ ["output"] = '<function 398>',
+ ["popen"] = '<function 399>',
+ ["read"] = '<function 400>',
+ ["stderr"] = '<userdata 1>',
+ ["stdin"] = '<userdata 2>',
+ ["stdout"] = '<userdata 3>',
+ ["tmpfile"] = '<function 401>',
+ ["type"] = '<function 402>',
+ ["write"] = '<function 403>'
+ },
+ ["ipairs"] = '<function 404>',
+ ["load"] = '<function 405>',
+ ["loadfile"] = '<function 406>',
+ ["loadstring"] = '<function 405>',
+ ["math"] = {
+ ["abs"] = '<function 407>',
+ ["acos"] = '<function 408>',
+ ["asin"] = '<function 409>',
+ ["atan"] = '<function 410>',
+ ["atan2"] = '<function 411>',
+ ["ceil"] = '<function 412>',
+ ["cos"] = '<function 413>',
+ ["cosh"] = '<function 414>',
+ ["deg"] = '<function 415>',
+ ["exp"] = '<function 416>',
+ ["floor"] = '<function 417>',
+ ["fmod"] = '<function 418>',
+ ["frexp"] = '<function 419>',
+ ["huge"] = '<number inf>',
+ ["ldexp"] = '<function 420>',
+ ["log"] = '<function 421>',
+ ["log10"] = '<function 422>',
+ ["max"] = '<function 423>',
+ ["min"] = '<function 424>',
+ ["modf"] = '<function 425>',
+ ["pi"] = 3.1415926535898,
+ ["pow"] = '<function 426>',
+ ["rad"] = '<function 427>',
+ ["random"] = '<function 428>',
+ ["randomseed"] = '<function 429>',
+ ["sin"] = '<function 430>',
+ ["sinh"] = '<function 431>',
+ ["sqrt"] = '<function 432>',
+ ["tan"] = '<function 433>',
+ ["tanh"] = '<function 434>'
+ },
+ ["module"] = '<function 436>',
+ ["new_dialog"] = '<function 437>',
+ ["next"] = '<function 438>',
+ ["open_capture_file"] = '<function 439>',
+ ["os"] = {
+ ["clock"] = '<function 440>',
+ ["date"] = '<function 441>',
+ ["difftime"] = '<function 442>',
+ ["execute"] = '<function 443>',
+ ["exit"] = '<function 444>',
+ ["getenv"] = '<function 445>',
+ ["remove"] = '<function 446>',
+ ["rename"] = '<function 447>',
+ ["setlocale"] = '<function 448>',
+ ["time"] = '<function 449>',
+ ["tmpname"] = '<function 450>'
+ },
+ ["package"] = {
+ ["config"] = '<filtered>',
+ ["cpath"] = '<filtered>',
+ ["loaded"] = '<filtered>',
+ ["loaders"] = {
+ [1] = '<function 451>',
+ [2] = '<function 452>',
+ [3] = '<function 453>',
+ [4] = '<function 454>' },
+ ["loadlib"] = '<function 455>',
+ ["path"] = '<filtered>',
+ ["preload"] = {},
+ ["searchers"] = '<table 33>',
+ ["searchpath"] = '<function 457>',
+ ["seeall"] = '<function 458>'
+ },
+ ["pairs"] = '<function 459>',
+ ["pcall"] = '<function 460>',
+ ["persconffile_path"] = '<function 37>',
+ ["prefs_changed"] = {},
+ ["print"] = '<function 461>',
+ ["rawequal"] = '<function 462>',
+ ["rawget"] = '<function 463>',
+ ["rawlen"] = '<function 464>',
+ ["rawset"] = '<function 465>',
+ ["register_filehandler"] = '<function 466>',
+ ["register_menu"] = '<function 467>',
+ ["register_postdissector"] = '<function 468>',
+ ["register_stat_cmd_arg"] = '<function 469>',
+ ["reload"] = '<function 470>',
+ ["report_failure"] = '<function 471>',
+ ["require"] = '<function 472>',
+ ["retap_packets"] = '<function 473>',
+ ["rex_pcre2"] = {
+ ["_VERSION"] = "Lrexlib 2.9.1 (for PCRE2)"
+ },
+ ["running_superuser"] = '<filtered>',
+ ["select"] = '<function 474>',
+ ["set_color_filter_slot"] = '<function 475>',
+ ["set_filter"] = '<function 476>',
+ ["setmetatable"] = '<function 477>',
+ ["string"] = {
+ ["byte"] = '<function 478>',
+ ["char"] = '<function 479>',
+ ["dump"] = '<function 480>',
+ ["find"] = '<function 481>',
+ ["format"] = '<function 482>',
+ ["gmatch"] = '<function 483>',
+ ["gsub"] = '<function 484>',
+ ["len"] = '<function 485>',
+ ["lower"] = '<function 486>',
+ ["match"] = '<function 487>',
+ ["rep"] = '<function 488>',
+ ["reverse"] = '<function 489>',
+ ["sub"] = '<function 490>',
+ ["upper"] = '<function 491>'
+ },
+ ["table"] = {
+ ["concat"] = '<function 492>',
+ ["insert"] = '<function 493>',
+ ["maxn"] = '<function 494>',
+ ["pack"] = '<function 495>',
+ ["remove"] = '<function 496>',
+ ["sort"] = '<function 497>',
+ ["unpack"] = '<function 498>'
+ },
+ ["tonumber"] = '<function 499>',
+ ["tostring"] = '<function 500>',
+ ["type"] = '<function 501>',
+ ["typeof"] = '<function 502>',
+ ["unpack"] = '<function 498>',
+ ["wtap"] = {
+ ["APPLE_IP_OVER_IEEE1394"] = 62,
+ ["ARCNET"] = 8,
+ ["ARCNET_LINUX"] = 9,
+ ["ASCEND"] = 16,
+ ["ATM_PDUS"] = 13,
+ ["ATM_PDUS_UNTRUNCATED"] = 14,
+ ["ATM_RFC1483"] = 10,
+ ["AX25"] = 148,
+ ["AX25_KISS"] = 147,
+ ["BACNET_MS_TP"] = 63,
+ ["BACNET_MS_TP_WITH_PHDR"] = 143,
+ ["BER"] = 90,
+ ["BLUETOOTH_BREDR_BB"] = 160,
+ ["BLUETOOTH_H4"] = 41,
+ ["BLUETOOTH_H4_WITH_PHDR"] = 99,
+ ["BLUETOOTH_HCI"] = 102,
+ ["BLUETOOTH_LE_LL"] = 154,
+ ["BLUETOOTH_LE_LL_WITH_PHDR"] = 161,
+ ["BLUETOOTH_LINUX_MONITOR"] = 159,
+ ["CAN20B"] = 109,
+ ["CATAPULT_DCT2000"] = 89,
+ ["CHDLC"] = 28,
+ ["CHDLC_WITH_PHDR"] = 40,
+ ["CISCO_IOS"] = 29,
+ ["COSINE"] = 34,
+ ["DBUS"] = 146,
+ ["DOCSIS"] = 33,
+ ["DPNSS"] = 117,
+ ["DVBCI"] = 132,
+ ["ENC"] = 38,
+ ["EPON"] = 172,
+ ["ERF"] = 98,
+ ["ETHERNET"] = 1,
+ ["FDDI"] = 5,
+ ["FDDI_BITSWAPPED"] = 6,
+ ["FIBRE_CHANNEL_FC2"] = 121,
+ ["FIBRE_CHANNEL_FC2_WITH_FRAME_DELIMS"] = 122,
+ ["FLEXRAY"] = 106,
+ ["FRELAY"] = 26,
+ ["FRELAY_WITH_PHDR"] = 27,
+ ["GCOM_SERIAL"] = 78,
+ ["GCOM_TIE1"] = 77,
+ ["GPRS_LLC"] = 66,
+ ["GSM_UM"] = 116,
+ ["HHDLC"] = 32,
+ ["I2C_LINUX"] = 112,
+ ["IEEE802_15_4"] = 104,
+ ["IEEE802_15_4_NOFCS"] = 127,
+ ["IEEE802_15_4_NONASK_PHY"] = 113,
+ ["IEEE802_16_MAC_CPS"] = 93,
+ ["IEEE_802_11"] = 20,
+ ["IEEE_802_11_AVS"] = 24,
+ ["IEEE_802_11_NETMON"] = 126,
+ ["IEEE_802_11_PRISM"] = 21,
+ ["IEEE_802_11_RADIOTAP"] = 23,
+ ["IEEE_802_11_WITH_RADIO"] = 22,
+ ["INFINIBAND"] = 150,
+ ["IPMB_KONTRON"] = 103,
+ ["IPMI_TRACE"] = 173,
+ ["IPNET"] = 124,
+ ["IP_OVER_FC"] = 18,
+ ["IP_OVER_IB_PCAP"] = 180,
+ ["IP_OVER_IB_SNOOP"] = 137,
+ ["IRDA"] = 44,
+ ["ISDN"] = 17,
+ ["IXVERIWAVE"] = 144,
+ ["JPEG_JFIF"] = 123,
+ ["JUNIPER_ATM1"] = 67,
+ ["JUNIPER_ATM2"] = 68,
+ ["JUNIPER_CHDLC"] = 86,
+ ["JUNIPER_ETHER"] = 83,
+ ["JUNIPER_FRELAY"] = 85,
+ ["JUNIPER_GGSN"] = 87,
+ ["JUNIPER_MLFR"] = 82,
+ ["JUNIPER_MLPPP"] = 81,
+ ["JUNIPER_PPP"] = 84,
+ ["JUNIPER_PPPOE"] = 76,
+ ["JUNIPER_SVCS"] = 151,
+ ["JUNIPER_VP"] = 91,
+ ["K12"] = 80,
+ ["LAPB"] = 12,
+ ["LAPD"] = 131,
+ ["LAYER1_EVENT"] = 110,
+ ["LIN"] = 107,
+ ["LINUX_ATM_CLIP"] = 11,
+ ["LINUX_LAPD"] = 88,
+ ["LOCALTALK"] = 30,
+ ["LOGCAT"] = 163,
+ ["LOGCAT_BRIEF"] = 164,
+ ["LOGCAT_LONG"] = 170,
+ ["LOGCAT_PROCESS"] = 165,
+ ["LOGCAT_TAG"] = 166,
+ ["LOGCAT_THREAD"] = 167,
+ ["LOGCAT_THREADTIME"] = 169,
+ ["LOGCAT_TIME"] = 168,
+ ["MIME"] = 134,
+ ["MOST"] = 108,
+ ["MPEG"] = 96,
+ ["MPEG_2_TS"] = 138,
+ ["MTP2"] = 42,
+ ["MTP2_WITH_PHDR"] = 75,
+ ["MTP3"] = 43,
+ ["MUX27010"] = 133,
+ ["NETANALYZER"] = 135,
+ ["NETANALYZER_TRANSPARENT"] = 136,
+ ["NETLINK"] = 158,
+ ["NETTL_ETHERNET"] = 71,
+ ["NETTL_FDDI"] = 73,
+ ["NETTL_RAW_ICMP"] = 64,
+ ["NETTL_RAW_ICMPV6"] = 65,
+ ["NETTL_RAW_IP"] = 70,
+ ["NETTL_RAW_TELNET"] = 94,
+ ["NETTL_TOKEN_RING"] = 72,
+ ["NETTL_UNKNOWN"] = 74,
+ ["NETTL_X25"] = 79,
+ ["NFC_LLCP"] = 140,
+ ["NFLOG"] = 141,
+ ["NSTRACE_1_0"] = 119,
+ ["NSTRACE_2_0"] = 120,
+ ["NSTRACE_3_0"] = 162,
+ ["NULL"] = 15,
+ ["OLD_PFLOG"] = 31,
+ ["PACKETLOGGER"] = 118,
+ ["PER_PACKET"] = -1,
+ ["PFLOG"] = 39,
+ ["PKTAP"] = 171,
+ ["PPI"] = 97,
+ ["PPP"] = 4,
+ ["PPP_ETHER"] = 139,
+ ["PPP_WITH_PHDR"] = 19,
+ ["RAW_IP"] = 7,
+ ["RAW_IP4"] = 129,
+ ["RAW_IP6"] = 130,
+ ["RAW_IPFIX"] = 128,
+ ["REDBACK"] = 69,
+ ["RTAC_SERIAL"] = 153,
+ ["SCCP"] = 101,
+ ["SCTP"] = 149,
+ ["SDH"] = 145,
+ ["SDLC"] = 36,
+ ["SITA"] = 100,
+ ["SLIP"] = 3,
+ ["SLL"] = 25,
+ ["SOCKETCAN"] = 125,
+ ["STANAG_4607"] = 156,
+ ["STANAG_5066_D_PDU"] = 157,
+ ["SYMANTEC"] = 61,
+ ["TNEF"] = 114,
+ ["TOKEN_RING"] = 2,
+ ["TZSP"] = 37,
+ ["UNKNOWN"] = 0,
+ ["USB_FREEBSD"] = 92,
+ ["USBPCAP"] = 152,
+ ["USB_LINUX"] = 95,
+ ["USB_LINUX_MMAPPED"] = 115,
+ ["USER0"] = 45,
+ ["USER1"] = 46,
+ ["USER10"] = 55,
+ ["USER11"] = 56,
+ ["USER12"] = 57,
+ ["USER13"] = 58,
+ ["USER14"] = 59,
+ ["USER15"] = 60,
+ ["USER2"] = 47,
+ ["USER3"] = 48,
+ ["USER4"] = 49,
+ ["USER5"] = 50,
+ ["USER6"] = 51,
+ ["USER7"] = 52,
+ ["USER8"] = 53,
+ ["USER9"] = 54,
+ ["V5_EF"] = 142,
+ ["WFLEET_HDLC"] = 35,
+ ["WIRESHARK_UPPER_PDU"] = 156,
+ ["X2E_SERIAL"] = 111,
+ ["X2E_XORAYA"] = 105
+ },
+ ["wtap_comments"] = {
+ ["PER_INTERFACE"] = 2,
+ ["PER_PACKET"] = 4,
+ ["PER_SECTION"] = 1
+ },
+ ["wtap_encaps"] = '<table 36>',
+ ["wtap_filetypes"] = {
+ ["5VIEWS"] = 9,
+ ["AETHRA"] = 60,
+ ["ASCEND"] = 26,
+ ["BER"] = 12,
+ ["BTSNOOP"] = 50,
+ ["CAMINS"] = 64,
+ ["CATAPULT_DCT2000"] = 14,
+ ["COMMVIEW"] = 49,
+ ["COSINE"] = 17,
+ ["CSIDS"] = 18,
+ ["DAINTREE_SNA"] = 54,
+ ["DBS_ETHERWATCH"] = 19,
+ ["DCT3TRACE"] = 52,
+ ["ERF"] = 20,
+ ["EYESDN"] = 21,
+ ["HCIDUMP"] = 13,
+ ["I4BTRACE"] = 25,
+ ["IPFIX"] = 58,
+ ["IPTRACE_1_0"] = 10,
+ ["IPTRACE_2_0"] = 11,
+ ["ISERIES"] = 23,
+ ["ISERIES_UNICODE"] = 24,
+ ["JPEG_JFIF"] = 57,
+ ["K12"] = 40,
+ ["K12TEXT"] = 47,
+ ["LANALYZER"] = 34,
+ ["LOGCAT"] = 67,
+ ["LOGCAT_BRIEF"] = 68,
+ ["LOGCAT_LONG"] = 74,
+ ["LOGCAT_PROCESS"] = 69,
+ ["LOGCAT_TAG"] = 70,
+ ["LOGCAT_THREAD"] = 71,
+ ["LOGCAT_THREADTIME"] = 73,
+ ["LOGCAT_TIME"] = 72,
+ ["MIME"] = 59,
+ ["MPEG"] = 46,
+ ["MPEG_2_TS"] = 61,
+ ["NETSCALER_1_0"] = 55,
+ ["NETSCALER_2_0"] = 56,
+ ["NETSCALER_3_0"] = 66,
+ ["NETSCREEN"] = 48,
+ ["NETTL"] = 22,
+ ["NETWORK_INSTRUMENTS"] = 33,
+ ["NETXRAY_1_0"] = 16,
+ ["NETXRAY_1_1"] = 31,
+ ["NETXRAY_OLD"] = 15,
+ ["NGSNIFFER_COMPRESSED"] = 30,
+ ["NGSNIFFER_UNCOMPRESSED"] = 29,
+ ["PACKETLOGGER"] = 53,
+ ["PCAP"] = 1,
+ ["PCAPNG"] = 2,
+ ["PCAP_AIX"] = 4,
+ ["PCAP_NOKIA"] = 6,
+ ["PCAP_NSEC"] = 3,
+ ["PCAP_SS990417"] = 7,
+ ["PCAP_SS990915"] = 8,
+ ["PCAP_SS991029"] = 5,
+ ["PEEKCLASSIC_V56"] = 43,
+ ["PEEKCLASSIC_V7"] = 44,
+ ["PEEKTAGGED"] = 45,
+ ["PPPDUMP"] = 35,
+ ["RADCOM"] = 36,
+ ["SHOMITI"] = 38,
+ ["SNOOP"] = 37,
+ ["STANAG_4607"] = 65,
+ ["TNEF"] = 51,
+ ["TOSHIBA"] = 41,
+ ["TSPREC_CSEC"] = 2,
+ ["TSPREC_DSEC"] = 1,
+ ["TSPREC_MSEC"] = 3,
+ ["TSPREC_NSEC"] = 9,
+ ["TSPREC_SEC"] = 0,
+ ["TSPREC_USEC"] = 6,
+ ["UNKNOWN"] = 0,
+ ["VISUAL_NETWORKS"] = 42,
+ ["VMS"] = 39,
+ ["VWR_80211"] = 62,
+ ["VWR_ETH"] = 63
+ },
+ ["wtap_presence_flags"] = {
+ ["CAP_LEN"] = 2,
+ ["INTERFACE_ID"] = 4,
+ ["TS"] = 1
+ },
+ ["wtap_rec_types"] = {
+ ["FT_SPECIFIC_EVENT"] = 1,
+ ["FT_SPECIFIC_REPORT"] = 2,
+ ["PACKET"] = 0
+ },
+ ["xpcall"] = '<function 504>'
+}
diff --git a/test/lua/inspect.lua b/test/lua/inspect.lua
new file mode 100644
index 0000000..6b4aff9
--- /dev/null
+++ b/test/lua/inspect.lua
@@ -0,0 +1,715 @@
+-------------------------------------------------------------------
+-- This was changed for Wireshark's use by Hadriel Kaplan.
+--
+-- Changes made:
+-- * provided 'serialize' option to output serialized info (ie, can be marshaled),
+-- though note that serializing functions/metatables/userdata/threads will not
+-- magically make them be their original type when marshaled.
+-- * provided 'notostring' option, which if true will disabled calling __tostring
+-- metamethod of tables.
+-- * made it always print the index number of numbered-array entries, and on separate
+-- lines like the normal key'd entries (much easier to read this way I think)
+-- New public functions:
+-- inspect.compare(first,second[,options])
+-- inspect.marshal(inString[,options])
+-- inspect.makeFilter(arrayTable)
+--
+-- For the *changes*:
+-- Copyright (c) 2014, Hadriel Kaplan
+-- My change to the code is in the Public Domain, or the BSD (3 clause) license if
+-- Public Domain does not apply in your country, or you would prefer a BSD license.
+-- But the original code is still under Enrique García Cota's MIT license (below).
+-------------------------------------------------------------------
+
+local inspect ={
+ _VERSION = 'inspect.lua 2.0.0 - with changes',
+ _URL = 'http://github.com/kikito/inspect.lua',
+ _DESCRIPTION = 'human-readable representations of tables',
+ _LICENSE = [[
+ MIT LICENSE
+
+ Copyright (c) 2013 Enrique García Cota
+
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ]],
+ _TINDEX_KEY = '<index>', -- the key name to use for index number entries for tables
+ _DEPTH_MARKER = " ['<depth>'] = true " -- instead of printing '...' we print this
+}
+
+-- Apostrophizes the string if it has quotes, but not apostrophes
+-- Otherwise, it returns a regular quoted string
+local function smartQuote(str)
+ if str:match('"') and not str:match("'") then
+ return "'" .. str .. "'"
+ end
+ return '"' .. str:gsub('"', '\\"') .. '"'
+end
+
+local controlCharsTranslation = {
+ ["\a"] = "\\a", ["\b"] = "\\b", ["\f"] = "\\f", ["\n"] = "\\n",
+ ["\r"] = "\\r", ["\t"] = "\\t", ["\v"] = "\\v"
+}
+
+local function escapeChar(c) return controlCharsTranslation[c] end
+
+local function escape(str)
+ local result = str:gsub("\\", "\\\\"):gsub("(%c)", escapeChar)
+ return result
+end
+
+local function isIdentifier(str)
+ return type(str) == 'string' and str:match( "^[_%a][_%a%d]*$" )
+end
+
+local function isArrayKey(k, length)
+ return type(k) == 'number' and 1 <= k and k <= length
+end
+
+local function isDictionaryKey(k, length)
+ return not isArrayKey(k, length)
+end
+
+local defaultTypeOrders = {
+ ['number'] = 1, ['boolean'] = 2, ['string'] = 3, ['table'] = 4,
+ ['function'] = 5, ['userdata'] = 6, ['thread'] = 7
+}
+
+local function sortKeys(a, b)
+ local ta, tb = type(a), type(b)
+
+ -- strings and numbers are sorted numerically/alphabetically
+ if ta == tb and (ta == 'string' or ta == 'number') then return a < b end
+
+ local dta, dtb = defaultTypeOrders[ta], defaultTypeOrders[tb]
+ -- Two default types are compared according to the defaultTypeOrders table
+ if dta and dtb then return defaultTypeOrders[ta] < defaultTypeOrders[tb]
+ elseif dta then return true -- default types before custom ones
+ elseif dtb then return false -- custom types after default ones
+ end
+
+ -- custom types are sorted out alphabetically
+ return ta < tb
+end
+
+local function getDictionaryKeys(t)
+ local keys, length = {}, #t
+ for k,_ in pairs(t) do
+ if isDictionaryKey(k, length) then table.insert(keys, k) end
+ end
+ table.sort(keys, sortKeys)
+ return keys
+end
+
+local function getToStringResultSafely(t, mt)
+ local __tostring = type(mt) == 'table' and rawget(mt, '__tostring')
+ local str, ok
+ if type(__tostring) == 'function' then
+ ok, str = pcall(__tostring, t)
+ str = ok and str or 'error: ' .. tostring(str)
+ end
+ if type(str) == 'string' and #str > 0 then return str end
+end
+
+local maxIdsMetaTable = {
+ __index = function(self, typeName)
+ rawset(self, typeName, 0)
+ return 0
+ end
+}
+
+local idsMetaTable = {
+ __index = function (self, typeName)
+ local col = setmetatable({}, {__mode = "kv"})
+ rawset(self, typeName, col)
+ return col
+ end
+}
+
+local function countTableAppearances(t, tableAppearances)
+ tableAppearances = tableAppearances or setmetatable({}, {__mode = "k"})
+
+ if type(t) == 'table' then
+ if not tableAppearances[t] then
+ tableAppearances[t] = 1
+ for k,v in pairs(t) do
+ countTableAppearances(k, tableAppearances)
+ countTableAppearances(v, tableAppearances)
+ end
+ countTableAppearances(getmetatable(t), tableAppearances)
+ else
+ tableAppearances[t] = tableAppearances[t] + 1
+ end
+ end
+
+ return tableAppearances
+end
+
+local function parse_filter(filter)
+ if type(filter) == 'function' then return filter end
+ -- not a function, so it must be a table or table-like
+ filter = type(filter) == 'table' and filter or {filter}
+ local dictionary = {}
+ for _,v in pairs(filter) do dictionary[v] = true end
+ return function(x) return dictionary[x] end
+end
+
+local function makePath(path, key)
+ local newPath, len = {}, #path
+ for i=1, len do newPath[i] = path[i] end
+ newPath[len+1] = key
+ return newPath
+end
+
+-------------------------------------------------------------------
+function inspect.inspect(rootObject, options)
+ options = options or {}
+ local depth = options.depth or math.huge
+ local filter = parse_filter(options.filter or {})
+ local serialize = options.serialize
+
+ local depth_marker = inspect._DEPTH_MARKER
+
+ local tableAppearances = countTableAppearances(rootObject)
+
+ local buffer = {}
+ local maxIds = setmetatable({}, maxIdsMetaTable)
+ local ids = setmetatable({}, idsMetaTable)
+ local level = 0
+ local blen = 0 -- buffer length
+
+ local function puts(...)
+ local args = {...}
+ for i=1, #args do
+ blen = blen + 1
+ buffer[blen] = tostring(args[i])
+ end
+ end
+
+ -- like puts above, but for things we want as quoted strings
+ -- so they become values, as we do if serializing
+ local function putv(...)
+ blen = blen + 1
+ buffer[blen] = "'"
+ puts(...)
+ blen = blen + 1
+ buffer[blen] = "'"
+ end
+
+ -- if serializing, using raw strings is unsafe, so we use the full "['key']" style
+ local function putk(...)
+ blen = blen + 1
+ buffer[blen] = "['"
+ puts(...)
+ blen = blen + 1
+ buffer[blen] = "']"
+ end
+
+ -- if not serializing, it's all puts
+ if not serialize then
+ putv = puts
+ putk = puts
+ depth_marker = '...'
+ end
+
+ -- disable using __tostring metamethod
+ local getToStringResultSafely = getToStringResultSafely
+ if options.notostring or serialize then
+ getToStringResultSafely = function() return end
+ end
+
+ local function down(f)
+ level = level + 1
+ f()
+ level = level - 1
+ end
+
+ local function tabify()
+ puts("\n", string.rep(" ", level))
+ end
+
+ local function commaControl(needsComma)
+ if needsComma then puts(',') end
+ return true
+ end
+
+ local function alreadyVisited(v)
+ return ids[type(v)][v] ~= nil
+ end
+
+ local function getId(v)
+ local tv = type(v)
+ local id = ids[tv][v]
+ if not id then
+ id = maxIds[tv] + 1
+ maxIds[tv] = id
+ ids[tv][v] = id
+ end
+ return id
+ end
+
+ local putValue -- forward declaration that needs to go before putTable & putKey
+
+ local function putKey(k)
+ if not serialize and isIdentifier(k) then return puts(k) end
+ puts("[")
+ putValue(k, {})
+ puts("]")
+ end
+
+ local function putTable(t, path)
+ if alreadyVisited(t) then
+ putv('<table ', getId(t), '>')
+ elseif level >= depth then
+ puts('{', depth_marker, '}')
+ else
+ if not serialize and tableAppearances[t] > 1 then puts('<', getId(t), '>') end
+
+ local dictKeys = getDictionaryKeys(t)
+ local length = #t
+ local mt = getmetatable(t)
+ local to_string_result = getToStringResultSafely(t, mt)
+
+ puts('{')
+ down(function()
+ if to_string_result then
+ puts(' -- ', escape(to_string_result))
+ if length >= 1 then tabify() end -- tabify the array values
+ end
+
+ local needsComma = false
+
+ if serialize and tableAppearances[t] > 1 then
+ getId(t)
+ end
+
+ for i=1, length do
+ needsComma = commaControl(needsComma)
+ -- just doing puts(' ') made for ugly arrays
+ tabify()
+ putKey(i)
+ puts(' = ')
+ putValue(t[i], makePath(path, i))
+ end
+
+ for _,k in ipairs(dictKeys) do
+ needsComma = commaControl(needsComma)
+ tabify()
+ putKey(k)
+ puts(' = ')
+ putValue(t[k], makePath(path, k))
+ end
+
+ if mt then
+ needsComma = commaControl(needsComma)
+ tabify()
+ putk('<metatable>')
+ puts(' = ')
+ putValue(mt, makePath(path, '<metatable>'))
+ end
+ end)
+
+ if #dictKeys > 0 or mt then -- dictionary table. Justify closing }
+ tabify()
+ elseif length > 0 then -- array tables have one extra space before closing }
+ puts(' ')
+ end
+
+ puts('}')
+ end
+
+ end
+
+ -- putvalue is forward-declared before putTable & putKey
+ putValue = function(v, path)
+ if filter(v, path) then
+ putv('<filtered>')
+ else
+ local tv = type(v)
+
+ if tv == 'string' then
+ puts(smartQuote(escape(v)))
+ elseif tv == 'number' and v == math.huge then
+ putv('<number inf>')
+ elseif tv == 'number' or tv == 'boolean' or tv == 'nil' then
+ puts(tostring(v))
+ elseif tv == 'table' then
+ putTable(v, path)
+ else
+ putv('<',tv,' ',getId(v),'>')
+ end
+ end
+ end
+
+ putValue(rootObject, {})
+
+ return table.concat(buffer)
+end
+
+setmetatable(inspect, { __call = function(_, ...) return inspect.inspect(...) end })
+
+-------------------------------------------------------------------
+
+-- The above is very close to Enrique's original inspect library.
+-- Below are my main changes.
+
+-------------------------------------------------------------------
+-- Given a string generated by inspect() with the serialize option,
+-- this function marshals it back into a Lua table/whatever.
+-- If the string's table(s) had metatable(s), i.e. "<metatable>" tables,
+-- then this keeps them as "<metatable>" subtables unless the option
+-- 'nometa' is set to true.
+--
+-- This function also removes all "<index>" entries.
+--
+function inspect.marshal(inString, options)
+ options = options or {}
+ local index = inspect._TINDEX_KEY
+
+ local function removeIndex(t)
+ if type(t) == 'table' then
+ t[index] = nil
+ for _, v in pairs(t) do
+ removeIndex(v)
+ end
+ end
+ end
+
+ local function removeMeta(t)
+ if type(t) == 'table' then
+ t['<metatable>'] = nil
+ for _, v in pairs(t) do
+ removeMeta(v)
+ end
+ end
+ end
+
+ -- first skip past comments/empty-lines
+ -- warning: super-hack-ish weak
+ local pos, ok, dk = 1, true, true
+ local fin
+ local stop = string.len(inString)
+ while ok or dk do
+ ok, fin = inString:find("^[%s\r\n]+",pos)
+ if ok then pos = fin + 1 end
+ dk, fin = inString:find("^%-%-.-\n",pos)
+ if dk then pos = fin + 1 end
+ end
+
+ if not inString:find("^%s*return[%s%{]",pos) then
+ inString = "return " .. inString
+ end
+
+ local t = assert(loadstring(inString))()
+
+ removeIndex(t)
+
+ if options.nometa then removeMeta(t) end
+
+ return t
+end
+
+-------------------------------------------------------------------
+
+-------------------------------------------------------------------
+-- more private functions
+
+-- things like '<function>' are equal to '<function 32>'
+local mungetypes = {
+ {"^<function ?%d*>", '<function>'},
+ {"^<table ?%d*>", '<table>'},
+ {"^<userdata ?%d*>", '<userdata>'},
+ {"^<thread ?%d*>", '<thread>'}
+}
+local function normalizeString(s)
+ for _,t in ipairs(mungetypes) do
+ if s:find(t[1]) then
+ return t[2]
+ end
+ end
+ return s
+end
+
+local typetable = {
+ ['<function>'] = 'function',
+ ['<table>'] = 'table',
+ ['<userdata>'] = 'userdata',
+ ['<thread>'] = 'thread'
+}
+local function getType(v)
+ local tv = type(v)
+ if tv == 'string' then
+ tv = typetable[normalizeString(v)] or 'string'
+ end
+ return tv
+end
+
+local function tablelength(t)
+ local count = 0
+ for _ in pairs(t) do count = count + 1 end
+ return count
+end
+
+-- for pretty-printing paths, for debug output
+-- this is non-optimal, but only gets used in verbose mode anyway
+local function serializePath(path)
+ local t = {}
+ for i,k in ipairs(path) do
+ local tk = type(k)
+ if isIdentifier(k) then
+ t[i] = ((i == 1) and k) or ('.'..k)
+ elseif tk == 'string' then
+ t[i] = '[' .. smartQuote(escape(k)) .. ']'
+ elseif tk == 'number' or tk == 'boolean' then
+ t[i] = '[' .. tostring(k) .. ']'
+ else
+ t[i] = "['<" .. tk .. ">']"
+ end
+ end
+ if #t == 0 then t[1] = '{}' end
+ return table.concat(t)
+end
+
+-------------------------------------------------------------------
+
+-------------------------------------------------------------------
+-- Given one table and another, this function detects if the first is
+-- completely contained in the second object. The second can have more
+-- entries, but cannot be missing an entry in the first one. Entry values
+-- must match as well - i.e., string values are the same, numbers the
+-- same, booleans the same.
+--
+-- The function returns true if the first is in the second, false otherwise.
+-- It also returns a table of the diff, which will be empty if they matched.
+-- This returned table is structured like the first one passed in,
+-- so calling print(inspect(returnedTabled)) will make it pretty print.
+--
+-- The returned table's members have their values replaced with mismatch
+-- information, explaining what the mismatch was. Setting the option "keep"
+-- makes it not replace the values, but keep them as they were in the first
+-- table.
+--
+-- By default, the key's values must match in both tables. If the option
+-- 'nonumber' is set, then number values are not compared. This is useful
+-- if they're things that can change (like exported C-code numbers).
+--
+-- By default, the metatables/"<metatables>" are also compared. If the option
+-- 'nometa' is set, then metatables are not compared, nor does it matter if
+-- they exist in either table.
+--
+-- Like inspect(), there's a 'filter' option, which works the same way:
+-- it ignores its value completely in terms of matching, so their string values
+-- can be different, but the keys still have to exist. Sub-tables of
+-- such keys (i.e., if the key's value is a table) are not checked/compared.
+-- In other words, it's identical to the filter option for inspect().
+--
+-- The option 'ignore' is similar to 'filter', except matching ones
+-- are not checked for existence in the tables at all.
+--
+-- Setting the 'depth' option applies as in inspect(), to both tables.
+--
+-- Setting the option 'verbose' makes it print out as it compares, for
+-- debugging or test purposes.
+--
+function inspect.compare(firstTable, secondTable, options)
+ options = options or {}
+ local depth = options.depth or math.huge
+ local filter = parse_filter(options.filter or {})
+ local ignore = parse_filter(options.ignore or {})
+
+ local function puts(...)
+ local args = {...}
+ for i=1, #args do
+ blen = blen + 1
+ buffer[blen] = tostring(args[i])
+ end
+ end
+
+ -- for debug printing
+ local function dprint(...)
+ local args = {...}
+ print(table.concat(args))
+ end
+
+ local serializePath = serializePath
+
+ if not options.verbose then
+ dprint = function() return end
+ serializePath = function() return end
+ end
+
+ -- for error message replacing key value
+ local function emsg(...)
+ local args = {...}
+ return(table.concat(args))
+ end
+
+ if options.keep then
+ emsg = function() return end
+ end
+
+ -- declare checkValue here
+ local checkValue
+
+ local function checkTable(f, s, path)
+ dprint("checking ",serializePath(path)," table contents")
+
+ for k, v in pairs(f) do
+ local child = makePath(path, k)
+
+ if not ignore(v,child) then
+ local ret, msg = checkValue(v, s[k], child)
+ if ret then
+ f[k] = nil
+ elseif msg then
+ f[k] = msg
+ dprint(serializePath(child)," ",msg)
+ end
+ else
+ dprint("ignoring ",serializePath(child))
+ f[k] = nil
+ end
+ end
+ return tablelength(f) == 0
+ end
+
+ -- a wrapper for failure cases in checkValue() that can be handled the same way
+ local function compCheck(f,s,func)
+ if not func() then
+ return false, emsg("mismatched ",getType(f)," values: ",tostring(f)," --> ",tostring(s))
+ end
+ return true
+ end
+
+ -- kinda ugly, but I wanted pretty information output
+ checkValue = function(f, s, path)
+ local tf = getType(f)
+
+ dprint("checking ",serializePath(path)," (",tf,")")
+
+ if s == nil then
+ return false, emsg("missing ",tf,"!")
+ elseif tf ~= getType(s) then
+ return false, emsg("type mismatch (",tf,") --> (",getType(s),")")
+ elseif type(f) == 'table' then
+ return checkTable(f, s, path)
+ end
+
+ return compCheck(f,s,function()
+ if tf == 'string' or tf == 'boolean' then
+ return f == s
+ elseif tf == 'number' then
+ return f == s or options.nonumber
+ else
+ -- assume they're the same functions/userdata/looped-table
+ -- type matching before would already cover it otherwise
+ return true
+ end
+ end)
+ end
+
+ -- inspect+serialize both tables, to normalize them, separate their
+ -- metatables, limit depth, etc. Also, since we pass the filter option on,
+ -- the filtered items become "<filtered>" and will by definition match
+ local function normalizeTable(t)
+ return assert( inspect.marshal( inspect.inspect(t,{serialize=true,depth=depth,filter=filter}), {nometa=options.nometa} ))
+ end
+
+ local first = normalizeTable(firstTable)
+ local second = normalizeTable(secondTable)
+
+ return checkTable(first, second, {}), first
+
+end
+
+-------------------------------------------------------------------
+
+
+
+-------------------------------------------------------------------
+-- Given a table of key strings, return a function that can be used for
+-- the 'filter' option of inspect() and inspect.compare() functions.
+function inspect.makeFilter(arrayTable)
+ local filter = {} -- our filter lookup tree (tables of tables)
+ local matchNode = {} -- a table instance we use as a key for nodes which match
+ local wildcard = {} -- a key table of wildcard match names
+
+ local function buildFilter(pathname)
+ local t = filter
+ local key
+ -- if the filtered name starts with a '.', it's a wildcard
+ if pathname:find("^%.") then
+ wildcard[pathname:sub(2)] = true
+ return
+ end
+ for sep, name in pathname:gmatch("([%.%[\"\']*)([^%.%[\"\'%]]+)[\"\'%]]?") do
+ if sep == '[' then
+ if name == 'true' then
+ key = true
+ elseif name == 'false' then
+ key = false
+ else
+ key = tonumber(name)
+ end
+ else
+ -- to be safe, we'll check the key name doesn't mean a table/function/userdata
+ local tn = getType(name)
+ if tn == 'string' then
+ key = name
+ else
+ error("filter key '"..pathname.."' has key '"..name.."' which is an unsupported type ("..tn..")")
+ end
+ end
+
+ if not t[key] then
+ t[key] = {}
+ end
+ t = t[key]
+ end
+
+ t[matchNode] = true
+ end
+
+ -- we could call serializePath() and do a simple lookup, but it's expensive and
+ -- we'd be calling it a LOT. So instead we break up the filter
+ -- table into true "path" elements, into a filter tree, and compare
+ -- against it... thereby avoiding string concat/manip during compare.
+
+ for _, pathname in ipairs(arrayTable) do
+ buildFilter(pathname)
+ end
+
+ return function(value,path)
+ local t = filter
+ if wildcard[ path[#path] ] then
+ return true
+ end
+ for _,v in ipairs(path) do
+ if not t[v] then
+ return false
+ end
+ t = t[v]
+ end
+ return t[matchNode] == true
+ end
+
+end
+
+return inspect
+
diff --git a/test/lua/int64.lua b/test/lua/int64.lua
new file mode 100644
index 0000000..6a703d3
--- /dev/null
+++ b/test/lua/int64.lua
@@ -0,0 +1,360 @@
+
+-- This is a test script for tshark/wireshark.
+-- This script runs inside tshark/wireshark, so to run it do:
+-- wireshark -X lua_script:<path_to_testdir>/lua/int64.lua
+-- tshark -r bogus.cap -X lua_script:<path_to_testdir>/lua/int64.lua
+
+-- Tests Int64/UInt64 functions
+
+local testlib = require("testlib")
+local OTHER = "other"
+testlib.init( { [OTHER] = 23 } )
+
+-- you can't compare (use the '==') userdata objects with numbers, so this function does it instead.
+function checkeq(arg1,arg2)
+ if arg1 == arg2 then
+ return true
+ elseif type(arg1) == 'userdata' and arg1.tonumber then
+ if type(arg2) == 'userdata' and arg2.tonumber then
+ return arg1:tonumber() == arg2:tonumber()
+ else
+ return arg1:tonumber() == arg2
+ end
+ elseif type(arg2) == 'userdata' and arg2.tonumber then
+ return arg1 == arg2:tonumber()
+ else
+ return false
+ end
+end
+
+-----------------------------
+
+testlib.testing("Int64/UInt64 library")
+
+local testtbl = {
+ { ["type"]=Int64, ["name"]="Int64" } ,
+ { ["type"]=UInt64, ["name"]="UInt64" },
+}
+
+for i,t in ipairs(testtbl) do
+ testlib.init( { [t.name] = 125+(t.name == "Int64" and 3 or 0) } )
+
+ testlib.testing(t.name, "class")
+ local obj = t.type
+
+ for name, val in pairs(obj) do
+ print("\t"..name.." = "..type(val))
+ end
+
+ testlib.test(t.name,"class1",type(obj) == 'table')
+ testlib.test(t.name,"class2",type(obj.new) == 'function')
+ testlib.test(t.name,"class3",type(obj.max) == 'function')
+ testlib.test(t.name,"class4",type(obj.min) == 'function')
+ testlib.test(t.name,"class5",type(obj.tonumber) == 'function')
+ testlib.test(t.name,"class6",type(obj.fromhex) == 'function')
+ testlib.test(t.name,"class7",type(obj.tohex) == 'function')
+ testlib.test(t.name,"class8",type(obj.higher) == 'function')
+ testlib.test(t.name,"class9",type(obj.lower) == 'function')
+
+
+ testlib.testing(t.name, "new, tonumber, tostring")
+ local val = 12345
+ local my64a = obj.new(val)
+ local my64b = obj.new(tostring(val))
+ local zero = obj.new(0)
+ -- remember in Lua it's a double, so only precise up to 9,007,199,254,740,992
+ local my64c = obj.new(val,100)
+ local valc = (100 * 4294967296) + val
+ print(tostring(my64c))
+ local my64z = obj.new(0,0)
+ local my64d = obj.new(0,100)
+ local vald = (100 * 4294967296)
+
+ testlib.test(t.name,"new1",checkeq(my64a,val))
+ testlib.test(t.name,"new2",checkeq(my64b,val))
+ testlib.test(t.name,"new3",checkeq(my64a,obj.new(my64b)))
+ testlib.test(t.name,"new3b",checkeq(my64a,obj(my64b)))
+ testlib.test(t.name,"new4",checkeq(valc,my64c))
+ testlib.test(t.name,"new5",checkeq(0,my64z))
+ testlib.test(t.name,"new6",obj.new(0,1):tonumber() == (2^32))
+ if t.name == "Int64" then
+ testlib.test(t.name,"new7",obj(-1):tonumber() == -1)
+ testlib.test(t.name,"new8",obj.new(0,-1):tonumber() == -4294967296)
+ testlib.test(t.name,"new9",obj(obj.new(-1)):tonumber() == -1)
+ end
+
+ testlib.test(t.name,"tonumber1",val == my64a:tonumber())
+ testlib.test(t.name,"tonumber2",valc == my64c:tonumber())
+ testlib.test(t.name,"tonumber3",vald == my64d:tonumber())
+ testlib.test(t.name,"tonumber4",0 == my64z:tonumber())
+
+ testlib.test(t.name,"tostring1", tostring(my64a)==tostring(val))
+ testlib.test(t.name,"tostring2",tostring(my64b)==tostring(val))
+ testlib.test(t.name,"tostring3",tostring(my64c)==tostring(valc))
+ testlib.test(t.name,"tostring4",tostring(my64d)==tostring(vald))
+
+
+ testlib.testing(t.name, "compare ops")
+
+ testlib.test(t.name,"eq", my64a == my64b)
+
+ testlib.test(t.name,"le1", my64a <= my64b)
+ testlib.test(t.name,"le2", my64a <= my64c)
+ testlib.test(t.name,"le3", my64z <= my64c)
+
+ testlib.test(t.name,"ge1", my64a >= my64b)
+ testlib.test(t.name,"ge2", my64c >= my64b)
+ testlib.test(t.name,"ge2", my64c >= my64z)
+
+ testlib.test(t.name,"neq1",not(my64a ~= my64b))
+ testlib.test(t.name,"neq2",my64a ~= obj(0))
+ testlib.test(t.name,"neq2",my64a ~= my64c)
+
+ testlib.test(t.name,"gt1",my64a > my64z)
+ testlib.test(t.name,"gt2",my64c > my64a)
+
+ testlib.test(t.name,"lt1",not(my64a < my64b))
+ testlib.test(t.name,"lt2",my64a < my64c)
+
+
+ testlib.testing(t.name, "math ops")
+
+ testlib.test(t.name,"add1",checkeq(my64a + my64b, val + val))
+ testlib.test(t.name,"add2",my64a + my64z == my64b)
+ testlib.test(t.name,"add3",my64a + my64b == my64b + my64a)
+ testlib.test(t.name,"add4",my64d + my64a == my64c)
+ testlib.test(t.name,"add5",checkeq(my64a + vald, valc))
+ testlib.test(t.name,"add6",checkeq(vald + my64a, valc))
+
+ testlib.test(t.name,"sub1",checkeq(my64a - my64b, 0))
+ testlib.test(t.name,"sub2",my64a - my64b == my64z)
+ testlib.test(t.name,"sub3",my64a - my64b == my64b - my64a)
+ testlib.test(t.name,"sub4",my64c - my64a == my64d)
+ testlib.test(t.name,"sub5",checkeq(my64a - val, 0))
+
+ testlib.test(t.name,"mod1",checkeq(my64a % my64b, 0))
+ testlib.test(t.name,"mod2",checkeq(my64c % my64b, valc % val))
+ testlib.test(t.name,"mod3",checkeq(my64c % val, valc % val))
+ testlib.test(t.name,"mod4",checkeq(val % my64c, val % valc))
+
+ testlib.test(t.name,"div1",checkeq(my64a / my64b, 1))
+ testlib.test(t.name,"div2",checkeq(my64a / val, 1))
+ testlib.test(t.name,"div3",checkeq(val / my64a, 1))
+ testlib.test(t.name,"div4",my64c / my64d == obj.new(1))
+
+ testlib.test(t.name,"pow1",checkeq(my64a ^ 1, val))
+ testlib.test(t.name,"pow2",checkeq(my64a ^ obj.new(2), val ^ 2))
+ testlib.test(t.name,"pow3",checkeq(my64a ^ obj.new(3), val ^ 3))
+ testlib.test(t.name,"pow4",checkeq(my64c ^ 1, valc ^ 1))
+
+ testlib.test(t.name,"mul1",checkeq(my64a * obj(1), my64b))
+ testlib.test(t.name,"mul2",checkeq(my64a * my64b, my64b * my64a))
+ testlib.test(t.name,"mul3",checkeq(my64a * 1, my64b))
+ testlib.test(t.name,"mul4",checkeq(2 * my64c, 2 * valc))
+
+ if t.name == "Int64" then
+ -- unary minus on UInt64 is illogical, but oh well
+ testlib.test(t.name,"unm1",checkeq(-my64a,-val))
+ testlib.test(t.name,"unm2",checkeq(string.sub(tostring(-my64a),1,1), "-"))
+ testlib.test(t.name,"unm3",checkeq(-my64c,-valc))
+ else
+ testlib.test(t.name,"unm1",checkeq(-my64a,val))
+ testlib.test(t.name,"unm2",checkeq(string.sub(tostring(-my64a),1,1), "1"))
+ testlib.test(t.name,"unm3",checkeq(-my64c,valc))
+ end
+ testlib.test(t.name,"unm4",checkeq(-my64z,0))
+
+ testlib.testing(t.name, "methods")
+
+ testlib.test(t.name,"higher1",my64a:higher() == 0)
+ testlib.test(t.name,"higher2",my64c:higher() == 100)
+
+ testlib.test(t.name,"lower1",my64a:lower() == val)
+ testlib.test(t.name,"lower2",my64c:lower() == val)
+ testlib.test(t.name,"lower3",my64d:lower() == 0)
+
+ local vale1 = 3735928559 -- yields hex of deadbeef
+ local vale2 = 5045997 -- yields 4cfeed
+ local my64e = obj.new(vale1, vale2)
+ testlib.test(t.name,"fromhex1",obj.fromhex("0000000000003039") == my64a);
+ testlib.test(t.name,"fromhex2",obj.fromhex("3039") == my64a);
+ testlib.test(t.name,"fromhex3",obj.fromhex("0000006400003039") == my64c);
+ testlib.test(t.name,"fromhex4",obj.fromhex("0000000000000000") == my64z);
+ testlib.test(t.name,"fromhex5",obj.fromhex("004cfeeddeadbeef") == my64e);
+ testlib.test(t.name,"fromhex6",obj.fromhex("4cFEEDDEADBEEF") == my64e);
+
+ testlib.test(t.name,"tohex1",my64a:tohex() == "0000000000003039")
+ testlib.test(t.name,"tohex2",my64c:tohex(16) == "0000006400003039")
+ testlib.test(t.name,"tohex3",my64z:tohex() == "0000000000000000")
+ testlib.test(t.name,"tohex4",my64e:tohex() == "004cfeeddeadbeef")
+ testlib.test(t.name,"tohex5",my64e:tohex(8) == "deadbeef")
+ testlib.test(t.name,"tohex6",my64e:tohex(-8) == "DEADBEEF")
+
+ testlib.test(t.name,"encode1",my64a:encode(true) == "\57\48\00\00\00\00\00\00")
+ testlib.test(t.name,"encode2",my64a:encode(false) == "\00\00\00\00\00\00\48\57")
+ testlib.test(t.name,"encode3",my64c:encode(false) == "\00\00\00\100\00\00\48\57")
+
+ testlib.test(t.name,"decode1",obj.decode("\57\48\00\00\00\00\00\00", true) == my64a)
+ testlib.test(t.name,"decode2",obj.decode("\00\00\00\00\00\00\48\57", false) == my64a)
+ testlib.test(t.name,"decode3",obj.decode("\00\00\00\100\00\00\48\57", false) == my64c)
+
+
+ local function testpower(b)
+ testlib.testing(t.name, "powers of "..b)
+ b=obj.new(b)
+ local z=obj.new(1)
+ for i=0,100 do
+ print(i,z,b^i)
+ assert(z==b^i)
+ z=b*z
+ end
+ end
+
+ testpower(2)
+ testpower(3)
+
+ testlib.testing(t.name, "factorials")
+
+ F={
+ [1]="1",
+ [2]="2",
+ [3]="6",
+ [4]="24",
+ [5]="120",
+ [6]="720",
+ [7]="5040",
+ [8]="40320",
+ [9]="362880",
+ [10]="3628800",
+ [11]="39916800",
+ [12]="479001600",
+ [13]="6227020800",
+ [14]="87178291200",
+ [15]="1307674368000",
+ [16]="20922789888000",
+ [17]="355687428096000",
+ [18]="6402373705728000",
+ [19]="121645100408832000",
+ [20]="2432902008176640000",
+ }
+ z=obj.new(1)
+ f=1
+ for i=1,20 do
+ z=z*i
+ f=f*i
+ s=obj.tonumber(z)
+ print(i,z,f,f==obj.tonumber(z),tostring(z)==F[i])
+ --print(i,int64.new(F[i]))
+ end
+
+ testlib.testing(t.name, "bit operations")
+
+ testlib.test(t.name,"band1",checkeq(obj(1):band(1), 1))
+ testlib.test(t.name,"band2",checkeq(obj(1):band(0), 0))
+ testlib.test(t.name,"band3",checkeq(obj(4294967295,100):band(4294967295), 4294967295))
+ testlib.test(t.name,"band4",obj.new(4294967295,100):band(obj(0,100),obj(0,100),obj(0,100)) == obj(0,100))
+ testlib.test(t.name,"band5",checkeq(obj.new(4294967295,100):band(obj.new(0,100),obj(0)), 0))
+
+ testlib.test(t.name,"bor1",checkeq(obj(1):bor(1), 1))
+ testlib.test(t.name,"bor2",checkeq(obj(1):bor(0), 1))
+ testlib.test(t.name,"bor3",checkeq(obj(0):bor(0), 0))
+ testlib.test(t.name,"bor4",obj.new(0,100):bor(4294967295) == obj.new(4294967295,100))
+ testlib.test(t.name,"bor5",obj.new(1):bor(obj(2),obj.new(4),obj(8),16,32,64,128) == obj(255))
+
+ testlib.test(t.name,"bxor1",checkeq(obj.new(1):bxor(1), 0))
+ testlib.test(t.name,"bxor2",checkeq(obj.new(1):bxor(0), 1))
+ testlib.test(t.name,"bxor3",checkeq(obj.new(0):bxor(0), 0))
+ testlib.test(t.name,"bxor4",obj.new(4294967295,100):bxor(obj(0,100)) == obj.new(4294967295))
+ testlib.test(t.name,"bxor5",obj.new(1):bxor(obj(2),obj(4),obj(8),16,32,64,128) == obj(255))
+
+ testlib.test(t.name,"bnot1",checkeq(obj.new(4294967295,4294967295):bnot(), 0))
+ testlib.test(t.name,"bnot2",obj.new(0):bnot() == obj.new(4294967295,4294967295))
+ testlib.test(t.name,"bnot3",obj.new(0xaaaaaaaa,0xaaaaaaaa):bnot() == obj.new( 0x55555555, 0x55555555))
+
+ testlib.test(t.name,"bsawp1",obj.new( 0x01020304, 0x05060708 ):bswap() == obj.new( 0x08070605, 0x04030201 ))
+ testlib.test(t.name,"bsawp2",obj.new( 0xFF020304, 0xFF060708 ):bswap() == obj.new( 0x080706FF, 0x040302FF ))
+
+ testlib.test(t.name,"lshift1",obj.new( 0x01020304, 0x0506070F ):lshift(4) == obj.new( 0x10203040, 0x506070f0 ))
+ testlib.test(t.name,"lshift2",obj.new( 0x0102030F, 0x05060708 ):lshift(63) == obj.new( 0, 0x80000000 ))
+ if t.name == "Int64" then
+ testlib.test(t.name,"lshift3",checkeq(obj.new( 0x0102030F, 0x05060708 ):lshift(63), -9223372036854775808))
+ else
+ testlib.test(t.name,"lshift3",obj.new( 0x0102030F, 0x05060708 ):lshift(63) == obj.new( 0, 0x80000000 ))
+ end
+
+ testlib.test(t.name,"rshift1",obj.new( 0x01020304, 0xF5060708 ):rshift(4) == obj.new( 0x80102030, 0x0F506070 ))
+ testlib.test(t.name,"rshift2",checkeq(obj.new( 0x01020304, 0xF5060708 ):rshift(63), 1))
+
+ if t.name == "Int64" then
+ testlib.test(t.name,"arshift1",obj.new( 0x01020304, 0xF5060708 ):arshift(4) == obj.new( 0x80102030, 0xFF506070 ))
+ testlib.test(t.name,"arshift2",obj.new( 0x01020304, 0xF5060708 ):arshift(63) == obj.new( 0xFFFFFFFF, 0xFFFFFFFF ))
+ else
+ testlib.test(t.name,"arshift1",obj.new( 0x01020304, 0xF5060708 ):arshift(4) == obj.new( 0x80102030, 0x0F506070 ))
+ testlib.test(t.name,"arshift2",checkeq(obj.new( 0x01020304, 0xF5060708 ):arshift(63),1))
+ end
+ testlib.test(t.name,"arshift3",obj.new( 0x01020304, 0x05060708 ):arshift(4) == obj.new( 0x80102030, 0x00506070 ))
+ testlib.test(t.name,"arshift4",checkeq(obj.new( 0x01020304, 0x05060708 ):arshift(63), 0))
+
+ testlib.test(t.name,"rol1",obj.new( 0x01020304, 0xF5060708 ):rol(4) == obj.new( 0x1020304F, 0x50607080 ))
+ testlib.test(t.name,"rol2",obj.new( 0x01020304, 0xF5060708 ):rol(32):rol(32) == obj.new( 0x01020304, 0xF5060708 ))
+
+ testlib.test(t.name,"ror1",obj.new( 0x01020304, 0xF5060708 ):ror(4) == obj.new( 0x80102030, 0x4F506070 ))
+ testlib.test(t.name,"ror2",obj.new( 0x01020304, 0xF5060708 ):ror(32):ror(32) == obj.new( 0x01020304, 0xF5060708 ))
+
+end
+
+testlib.testing("min and max values")
+z=Int64.new(2)
+z=z^63-1
+testlib.test(OTHER,"max1",tostring(Int64.max()) == "9223372036854775807")
+testlib.test(OTHER,"max2",Int64.max() == Int64.new(4294967295, 2147483647))
+testlib.test(OTHER,"max3",z==Int64.max())
+testlib.test(OTHER,"min1",tostring(Int64.min()) == "-9223372036854775808")
+testlib.test(OTHER,"min2",Int64.min() == Int64.new(0,2147483648))
+z=-z
+z=z-1
+testlib.test(OTHER,"min3",z==Int64.min())
+
+testlib.test(OTHER,"minmax",Int64.min()== - Int64.max() - 1)
+
+--Because of g_ascii_strtoll() usage without errno check, "invalid" strings are converted to 0
+testlib.testing("invalid string values")
+testlib.test(OTHER,"invalid",Int64.new("invalid")== Int64.new(0,0))
+testlib.test(OTHER,"invalid2",UInt64.new("invalid")== UInt64.new(0,0))
+
+testlib.testing("error conditions")
+
+local function divtest(f,s)
+ local r = (f / s)
+ if r == 5 then
+ io.stdout:write("ok...")
+ else
+ error("test failed!")
+ end
+end
+
+local function modtest(f,s)
+ local r = (f % s)
+ if r == 5 then
+ io.stdout:write("ok...")
+ else
+ error("test failed!")
+ end
+end
+
+testlib.test(OTHER,"error1", pcall(divtest, 10, 2)) -- not an error, but checking the div function works above
+testlib.test(OTHER,"error2", not pcall(divtest, Int64(10), 0))
+testlib.test(OTHER,"error3", not pcall(divtest, Int64(10), Int64(0)))
+testlib.test(OTHER,"error4", not pcall(divtest, Int64(10), UInt64(0)))
+testlib.test(OTHER,"error5", not pcall(divtest, UInt64(10), 0))
+testlib.test(OTHER,"error6", not pcall(divtest, UInt64(10), Int64(0)))
+testlib.test(OTHER,"error7", not pcall(divtest, UInt64(10), UInt64(0)))
+testlib.test(OTHER,"error8", pcall(modtest, 17, 6)) -- not an error, but checking the mod function works above
+testlib.test(OTHER,"error9", not pcall(modtest, Int64(10), 0))
+testlib.test(OTHER,"error10", not pcall(modtest, Int64(10), Int64(0)))
+testlib.test(OTHER,"error11", not pcall(modtest, Int64(10), UInt64(0)))
+testlib.test(OTHER,"error12", not pcall(modtest, UInt64(10), 0))
+testlib.test(OTHER,"error13", not pcall(modtest, UInt64(10), Int64(0)))
+testlib.test(OTHER,"error14", not pcall(modtest, UInt64(10), UInt64(0)))
+
+testlib.getResults()
diff --git a/test/lua/listener.lua b/test/lua/listener.lua
new file mode 100644
index 0000000..8716f2a
--- /dev/null
+++ b/test/lua/listener.lua
@@ -0,0 +1,246 @@
+-- test script for various Lua functions
+-- use with dhcp.pcap in test/captures directory
+
+local testlib = require("testlib")
+
+------------- general test helper funcs ------------
+local FRAME = "frame"
+local ETH = "eth"
+local IP = "ip"
+local DHCP = "dhcp"
+local OTHER = "other"
+local PDISS = "postdissector"
+
+-- expected number of runs per type
+-- note ip (5 tests) only runs 3 times because it gets removed
+-- and dhcp (5 tests) only runs twice because the filter makes it run
+-- once and then it gets replaced with a different one for the second time
+local n_frames = 4
+local taptests = {
+ [FRAME]=5*n_frames,
+ [ETH]=5*n_frames,
+ [IP]=5*3,
+ [DHCP]=5*2,
+ [OTHER]=16,
+ [PDISS]=n_frames,
+}
+testlib.init(taptests)
+
+local pkt_fields = { [FRAME] = {}, [PDISS] = {} }
+local function getAllFieldInfos(group)
+ local fields = { all_field_infos() }
+ local fieldnames = {}
+ for i,v in ipairs(fields) do
+ fieldnames[i] = v.name
+ end
+ local pktnum = testlib.getPktCount(group)
+ pkt_fields[group][pktnum] = { ["num"] = #fields, ["fields"] = fieldnames }
+end
+
+local function dumpAllFieldInfos()
+ for i,v in ipairs(pkt_fields[FRAME]) do
+ print("In frame tap for packet ".. i ..":")
+ print(" number of fields = ".. v.num)
+ for _,name in ipairs(v.fields) do
+ print(" field = ".. name)
+ end
+ local w = pkt_fields[PDISS][i]
+ print("In postdissector for packet ".. i ..":")
+ print(" number of fields = ".. w.num)
+ for _,name in ipairs(w.fields) do
+ print(" field = ".. name)
+ end
+ end
+end
+
+local function checkAllFieldInfos()
+ for i,v in ipairs(pkt_fields[FRAME]) do
+ local numfields = v.num
+ if numfields ~= pkt_fields[PDISS][i].num then
+ print("Tap and postdissector do not have same number of fields!")
+ return false
+ end
+ if numfields < 100 then
+ print("Too few fields!")
+ return false
+ end
+ end
+ return true
+end
+
+
+---------
+-- the following are so we can use pcall (which needs a function to call)
+local function makeListener(...)
+ local foo = Listener.new(...)
+end
+
+local function setListener(tap,name,value)
+ tap[name] = value
+end
+
+local function getListener(tap,name)
+ local foo = tap[name]
+end
+
+------------- test script ------------
+testlib.testing(OTHER,"negative tests")
+testlib.test(OTHER,"Listener.new-1",not pcall(makeListener,"FooBARhowdy"))
+testlib.test(OTHER,"Listener.new-2",not pcall(makeListener,"ip","FooBARhowdy"))
+local tmptap = Listener.new()
+local func = function(...)
+ passed[OTHER] = 0
+ error("This shouldn't be called!")
+end
+testlib.test(OTHER,"Listener.set-3",pcall(setListener,tmptap,"packet",func))
+testlib.test(OTHER,"Listener.set-4",pcall(setListener,tmptap,"reset",func))
+testlib.test(OTHER,"Listener.set-5",pcall(setListener,tmptap,"draw",func))
+testlib.test(OTHER,"Listener.set-6",not pcall(setListener,Listener,"packet",func))
+testlib.test(OTHER,"Listener.set-7",not pcall(setListener,Listener,"reset",func))
+testlib.test(OTHER,"Listener.set-8",not pcall(setListener,Listener,"draw",func))
+testlib.test(OTHER,"Listener.set-9",not pcall(setListener,Listener,"foobar",func))
+
+testlib.test(OTHER,"Listener.get-10",not pcall(getListener,tmptap,"packet",func))
+testlib.test(OTHER,"Listener.get-11",not pcall(getListener,tmptap,"reset",func))
+testlib.test(OTHER,"Listener.get-12",not pcall(getListener,tmptap,"draw",func))
+
+print("removing tmptap twice")
+testlib.test(OTHER,"Listener.remove-13",pcall(tmptap.remove,tmptap))
+testlib.test(OTHER,"Listener.remove-14",pcall(tmptap.remove,tmptap))
+
+testlib.test(OTHER,"typeof-15", typeof(tmptap) == "Listener")
+
+
+-- declare some field extractors
+local f_eth_src = Field.new("eth.src")
+local f_eth_dst = Field.new("eth.dst")
+local f_eth_mac = Field.new("eth.addr")
+local f_ip_src = Field.new("ip.src")
+local f_ip_dst = Field.new("ip.dst")
+local f_dhcp_hw = Field.new("dhcp.hw.mac_addr")
+local f_dhcp_opt = Field.new("dhcp.option.type")
+
+local tap_frame = Listener.new(nil,nil,true)
+local tap_eth = Listener.new("eth")
+local tap_ip = Listener.new("ip","dhcp")
+local tap_dhcp = Listener.new("dhcp","dhcp.option.dhcp == 1")
+
+local second_time = false
+
+function tap_frame.packet(pinfo,tvb,frame)
+ testlib.countPacket(FRAME)
+ testlib.testing(FRAME,"Frame")
+
+ testlib.test(FRAME,"arg-1", typeof(pinfo) == "Pinfo")
+ testlib.test(FRAME,"arg-2", typeof(tvb) == "Tvb")
+ testlib.test(FRAME,"arg-3", frame == nil)
+
+ testlib.test(FRAME,"pinfo.number-1",pinfo.number == testlib.getPktCount(FRAME))
+
+ -- check ether addr
+ local eth_src1 = tostring(f_eth_src().range)
+ local eth_src2 = tostring(tvb:range(6,6))
+ testlib.test(FRAME,"FieldInfo.range-1", eth_src1 == eth_src2)
+
+ getAllFieldInfos(FRAME)
+end
+
+function tap_eth.packet(pinfo,tvb,eth)
+ testlib.countPacket(ETH)
+
+ -- on the 4th run of eth, remove the ip one and add a new dhcp one
+ if testlib.getPktCount(ETH) == 4 then
+ testlib.testing(ETH,"removing ip tap, replacing dhcp tap")
+ tap_ip:remove()
+ tap_dhcp:remove()
+ tap_dhcp = Listener.new("dhcp")
+ tap_dhcp.packet = dhcp_packet
+ second_time = true
+ end
+
+ testlib.testing(ETH,"Eth")
+
+ testlib.test(ETH,"arg-1", typeof(pinfo) == "Pinfo")
+ testlib.test(ETH,"arg-2", typeof(tvb) == "Tvb")
+ testlib.test(ETH,"arg-3", type(eth) == "table")
+
+ testlib.test(ETH,"pinfo.number-1",pinfo.number == testlib.getPktCount(ETH))
+
+ -- check ether addr
+ local eth_src1 = tostring(f_eth_src().range)
+ local eth_src2 = tostring(tvb:range(6,6))
+ testlib.test(ETH,"FieldInfo.range-1", eth_src1 == eth_src2)
+end
+
+function tap_ip.packet(pinfo,tvb,ip)
+ testlib.countPacket(IP)
+ testlib.testing(IP,"IP")
+
+ testlib.test(IP,"arg-1", typeof(pinfo) == "Pinfo")
+ testlib.test(IP,"arg-2", typeof(tvb) == "Tvb")
+ testlib.test(IP,"arg-3", type(ip) == "table")
+
+ testlib.test(IP,"pinfo.number-1",pinfo.number == testlib.getPktCount(IP))
+
+ -- check ether addr
+ local eth_src1 = tostring(f_eth_src().range)
+ local eth_src2 = tostring(tvb:range(6,6))
+ testlib.test(IP,"FieldInfo.range-1", eth_src1 == eth_src2)
+end
+
+dhcp_packet = function (pinfo,tvb,dhcp)
+ testlib.countPacket(DHCP)
+ testlib.testing(DHCP,"DHCP")
+
+ testlib.test(DHCP,"arg-1", typeof(pinfo) == "Pinfo")
+ testlib.test(DHCP,"arg-2", typeof(tvb) == "Tvb")
+ testlib.test(DHCP,"arg-3", dhcp == nil)
+
+ if not second_time then
+ testlib.test(DHCP,"pinfo.number-1",pinfo.number == testlib.getPktCount(DHCP))
+ else
+ testlib.test(DHCP,"pinfo.number-1",pinfo.number == 4)
+ end
+
+ -- check ether addr
+ local eth_src1 = tostring(f_eth_src().range)
+ local eth_src2 = tostring(tvb:range(6,6))
+ testlib.test(DHCP,"FieldInfo.range-1", eth_src1 == eth_src2)
+end
+tap_dhcp.packet = dhcp_packet
+
+function tap_frame.reset()
+ -- reset never gets called in tshark (sadly)
+ --[[ XXX: this is no longer the case?!
+ if not GUI_ENABLED then
+ error("reset called!!")
+ end
+ --]]
+end
+
+function tap_frame.draw()
+ testlib.test(OTHER,"all_field_infos", checkAllFieldInfos())
+ testlib.getResults()
+end
+
+-- max_gap.lua
+-- create a gap.max field containing the maximum gap between two packets between two ip nodes
+
+-- we create a "protocol" for our tree
+local max_gap_p = Proto("gap","Gap in IP conversations")
+
+-- we create our fields
+local max_gap_field = ProtoField.float("gap.max")
+
+-- we add our fields to the protocol
+max_gap_p.fields = { max_gap_field }
+
+-- then we register max_gap_p as a postdissector
+register_postdissector(max_gap_p,true)
+function max_gap_p.dissector(tvb,pinfo,tree)
+ testlib.countPacket(PDISS)
+ getAllFieldInfos(PDISS)
+ testlib.pass(PDISS)
+end
+
+
diff --git a/test/lua/nstime.lua b/test/lua/nstime.lua
new file mode 100644
index 0000000..f7e2f66
--- /dev/null
+++ b/test/lua/nstime.lua
@@ -0,0 +1,140 @@
+-- test script for various Lua functions
+-- use with dhcp.pcap in test/captures directory
+
+local testlib = require("testlib")
+
+local FRAME = "frame"
+local PER_FRAME = "per-frame"
+local OTHER = "other"
+
+-- expected number of runs per type
+local n_frames = 4
+local taptests = {
+ [FRAME]=n_frames,
+ [PER_FRAME]=n_frames*5,
+ [OTHER]=44
+}
+testlib.init(taptests)
+
+---------
+-- the following are so we can use pcall (which needs a function to call)
+local function setNSTime(nst,name,value)
+ nst[name] = value
+end
+
+local function getNSTime(nst,name)
+ local foo = nst[name]
+end
+
+------------- test script ------------
+testlib.testing(OTHER,"negative tests")
+testlib.test(OTHER,"NSTime.new-1",not pcall(NSTime,"FooBARhowdy"))
+testlib.test(OTHER,"NSTime.new-2",not pcall(NSTime,"ip","FooBARhowdy"))
+local tmptime = NSTime()
+testlib.test(OTHER,"NSTime.set-3",pcall(setNSTime,tmptime,"secs",10))
+testlib.test(OTHER,"NSTime.set-4",not pcall(setNSTime,tmptime,"foobar",1000))
+testlib.test(OTHER,"NSTime.set-5",pcall(setNSTime,tmptime,"nsecs",123))
+testlib.test(OTHER,"NSTime.set-6",not pcall(setNSTime,NSTime,"secs",0))
+testlib.test(OTHER,"NSTime.set-7",not pcall(setNSTime,tmptime,"secs","foobar"))
+testlib.test(OTHER,"NSTime.set-8",not pcall(setNSTime,NSTime,"nsecs",0))
+testlib.test(OTHER,"NSTime.set-9",not pcall(setNSTime,tmptime,"nsecs","foobar"))
+
+testlib.test(OTHER,"NSTime.get-10",pcall(getNSTime,tmptime,"secs"))
+testlib.test(OTHER,"NSTime.get-11",pcall(getNSTime,tmptime,"nsecs"))
+testlib.test(OTHER,"NSTime.get-12",not pcall(getNSTime,NSTime,"secs"))
+testlib.test(OTHER,"NSTime.get-13",not pcall(getNSTime,NSTime,"nsecs"))
+
+
+testlib.testing(OTHER,"basic tests")
+local first = NSTime()
+local second = NSTime(100,100)
+local third = NSTime(0,100)
+testlib.test(OTHER,"NSTime.secs-14", first.secs == 0)
+testlib.test(OTHER,"NSTime.secs-15", second.secs == 100)
+testlib.test(OTHER,"NSTime.secs-16", third.secs == 0)
+
+testlib.test(OTHER,"NSTime.nsecs-17", first.nsecs == 0)
+testlib.test(OTHER,"NSTime.nsecs-18", second.nsecs == 100)
+testlib.test(OTHER,"NSTime.nsecs-19", third.nsecs == 100)
+
+testlib.test(OTHER,"NSTime.eq-20", first == NSTime())
+testlib.test(OTHER,"NSTime.neq-21", second ~= third)
+
+testlib.test(OTHER,"NSTime.add-22", first + second == second)
+testlib.test(OTHER,"NSTime.add-23", third + NSTime(100,0) == second)
+testlib.test(OTHER,"NSTime.add-24", NSTime(100) + NSTime(nil,100) == second)
+
+testlib.test(OTHER,"NSTime.lt-25", third < second)
+testlib.test(OTHER,"NSTime.gt-26", third > first)
+testlib.test(OTHER,"NSTime.le-27", second <= NSTime(100,100))
+
+testlib.test(OTHER,"NSTime.unm-28", -first == first)
+testlib.test(OTHER,"NSTime.unm-29", -(-second) == second)
+testlib.test(OTHER,"NSTime.unm-30", -second == NSTime(-100,-100))
+testlib.test(OTHER,"NSTime.unm-31", -third == NSTime(0,-100))
+
+testlib.test(OTHER,"NSTime.tostring-32", tostring(first) == "0.000000000")
+testlib.test(OTHER,"NSTime.tostring-33", tostring(second) == "100.000000100")
+testlib.test(OTHER,"NSTime.tostring-34", tostring(third) == "0.000000100")
+
+testlib.test(OTHER,"NSTime.tonumber-35", first:tonumber() == 0.0)
+testlib.test(OTHER,"NSTime.tonumber-36", second:tonumber() == 100.0000001)
+testlib.test(OTHER,"NSTime.tonumber-37", third:tonumber() == 0.0000001)
+
+testlib.testing(OTHER,"setters/getters")
+first.secs = 123
+first.nsecs = 100
+testlib.test(OTHER,"NSTime.set-38", first == NSTime(123,100))
+testlib.test(OTHER,"NSTime.get-39", first.secs == 123)
+testlib.test(OTHER,"NSTime.get-40", first.nsecs == 100)
+
+local minus0_4 = NSTime() - NSTime(0,400000000)
+testlib.test(OTHER,"NSTime.negative_tonumber-41", minus0_4:tonumber() == -0.4)
+testlib.test(OTHER,"NSTime.negative_tostring-42", tostring(minus0_4) == "-0.400000000")
+local minus0_4 = NSTime() - NSTime(1,400000000)
+testlib.test(OTHER,"NSTime.negative_tonumber-43", minus0_4:tonumber() == -1.4)
+testlib.test(OTHER,"NSTime.negative_tostring-44", tostring(minus0_4) == "-1.400000000")
+
+
+----------------------------------
+
+-- declare some field extractors
+local f_frame_time = Field.new("frame.time")
+local f_frame_time_rel = Field.new("frame.time_relative")
+local f_frame_time_delta = Field.new("frame.time_delta")
+
+local tap = Listener.new()
+
+local begin = NSTime()
+local now, previous
+
+function tap.packet(pinfo,tvb,frame)
+ testlib.countPacket(FRAME)
+ testlib.testing(FRAME,"NSTime in Frame")
+
+ local fi_now = f_frame_time()
+ local fi_rel = f_frame_time_rel()
+ local fi_delta = f_frame_time_delta()
+
+ testlib.test(PER_FRAME,"typeof-1", typeof(begin) == "NSTime")
+ testlib.test(PER_FRAME,"typeof-2", typeof(fi_now()) == "NSTime")
+
+ now = fi_now()
+ if testlib.getPktCount(FRAME) == 1 then
+ testlib.test(PER_FRAME,"__eq-1", begin == fi_delta())
+ testlib.test(PER_FRAME,"NSTime.secs-1", fi_delta().secs == 0)
+ testlib.test(PER_FRAME,"NSTime.nsecs-1", fi_delta().nsecs == 0)
+ begin = fi_now()
+ else
+ testlib.test(PER_FRAME,"__sub__eq-1", now - previous == fi_delta())
+ testlib.test(PER_FRAME,"__sub__eq-2", now - begin == fi_rel())
+ testlib.test(PER_FRAME,"__add-1", (previous - begin) + (now - previous) == fi_rel())
+ end
+ previous = now
+
+ testlib.pass(FRAME)
+end
+
+function tap.draw()
+ testlib.getResults()
+end
diff --git a/test/lua/pcap_file.lua b/test/lua/pcap_file.lua
new file mode 100644
index 0000000..b949a38
--- /dev/null
+++ b/test/lua/pcap_file.lua
@@ -0,0 +1,752 @@
+-- pcap_file_reader.lua
+--------------------------------------------------------------------------------
+--[[
+ This is a Wireshark Lua-based pcap capture file reader.
+ Author: Hadriel Kaplan
+
+ This "capture file" reader reads pcap files - the old style ones. Don't expect this to
+ be as good as the real thing; this is a simplistic implementation to show how to
+ create such file readers, and for testing purposes.
+
+ This script requires Wireshark v1.12 or newer.
+--]]
+--------------------------------------------------------------------------------
+
+-- do not modify this table
+local debug = {
+ DISABLED = 0,
+ LEVEL_1 = 1,
+ LEVEL_2 = 2
+}
+
+-- set this DEBUG to debug.LEVEL_1 to enable printing debug info
+-- set it to debug.LEVEL_2 to enable really verbose printing
+local DEBUG = debug.LEVEL_1
+
+
+local wireshark_name = "Wireshark"
+if not GUI_ENABLED then
+ wireshark_name = "Tshark"
+end
+
+-- verify Wireshark is new enough
+local major, minor, micro = get_version():match("(%d+)%.(%d+)%.(%d+)")
+if major and tonumber(major) <= 1 and ((tonumber(minor) <= 10) or (tonumber(minor) == 11 and tonumber(micro) < 3)) then
+ error( "Sorry, but your " .. wireshark_name .. " version (" .. get_version() .. ") is too old for this script!\n" ..
+ "This script needs " .. wireshark_name .. "version 1.12 or higher.\n" )
+end
+
+-- verify we have the Struct library in wireshark
+-- technically we should be able to do this with 'require', but Struct is a built-in
+assert(Struct.unpack, wireshark_name .. " does not have the Struct library!")
+
+--------------------------------------------------------------------------------
+-- early definitions
+-- throughout most of this file I try to pre-declare things to help ease
+-- reading it and following the logic flow, but some things just have to be done
+-- before others, so this sections has such things that cannot be avoided
+--------------------------------------------------------------------------------
+
+-- first some variable declarations for functions we'll define later
+local parse_file_header, parse_rec_header, read_common
+
+-- these will be set inside of parse_file_header(), but we're declaring them up here
+local default_settings =
+{
+ debug = DEBUG,
+ corrected_magic = 0xa1b2c3d4,
+ version_major = 2,
+ version_minor = 4,
+ timezone = 0,
+ sigfigs = 0,
+ read_snaplen = 0, -- the snaplen we read from file
+ snaplen = 0, -- the snaplen we use (limited by WTAP_MAX_PACKET_SIZE)
+ linktype = -1, -- the raw linktype number in the file header
+ wtap_type = wtap_encaps.UNKNOWN, -- the mapped internal wtap number based on linktype
+ endianess = ENC_BIG_ENDIAN,
+ time_precision = wtap_tsprecs.USEC,
+ rec_hdr_len = 16, -- default size of record header
+ rec_hdr_patt = "I4 I4 I4 I4", -- pattern for Struct to use
+ num_rec_fields = 4, -- number of vars in pattern
+}
+
+local dprint = function() end
+local dprint2 = function() end
+local function reset_debug()
+ if default_settings.debug > debug.DISABLED then
+ dprint = function(...)
+ print(table.concat({"Lua:", ...}," "))
+ end
+
+ if default_settings.debug > debug.LEVEL_1 then
+ dprint2 = dprint
+ end
+ end
+end
+-- call it now
+reset_debug()
+
+--------------------------------------------------------------------------------
+-- file reader handling functions for Wireshark to use
+--------------------------------------------------------------------------------
+
+----------------------------------------
+-- The read_open() is called by Wireshark once per file, to see if the file is this reader's type.
+-- Wireshark passes in (1) a File object and (2) CaptureInfo object to this function
+-- It expects in return either nil or false to mean it's not our file type, or true if it is
+-- In our case what this means is we figure out if the file has the magic header, and get the
+-- endianess of the file, and the encapsulation type of its frames/records
+local function read_open(file, capture)
+ dprint2("read_open() called")
+
+ local file_settings = parse_file_header(file)
+
+ if file_settings then
+
+ dprint2("read_open: success, file is for us")
+
+ -- save our state
+ capture.private_table = file_settings
+
+ -- if the file is for us, we MUST set the file position cursor to
+ -- where we want the first call to read() function to get it the next time
+ -- for example if we checked a few records to be sure it's or type
+ -- but in this simple example we only verify the file header (24 bytes)
+ -- and we want the file position to remain after that header for our read()
+ -- call, so we don't change it back
+ --file:seek("set",position)
+
+ -- these we can also set per record later during read operations
+ capture.time_precision = file_settings.time_precision
+ capture.encap = file_settings.wtap_type
+ capture.snapshot_length = file_settings.snaplen
+
+ return true
+ end
+
+ dprint2("read_open: file not for us")
+
+ -- if it's not for us, wireshark will reset the file position itself
+
+ return false
+end
+
+----------------------------------------
+-- Wireshark/tshark calls read() for each frame/record in the file
+-- It passes in (1) a File, (2) CaptureInfo, and (3) FrameInfo object to this function
+-- It expects in return the file offset position the record starts at,
+-- or nil/false if there's an error or end-of-file is reached.
+-- The offset position is used later: wireshark remembers it and gives
+-- it to seek_read() at various random times
+local function read(file, capture, frame)
+ dprint2("read() called")
+
+ -- call our common reader function
+ local position = file:seek()
+
+ if not read_common("read", file, capture, frame) then
+ -- this isnt' actually an error, because it might just mean we reached end-of-file
+ -- so let's test for that (read(0) is a special case in Lua, see Lua docs)
+ if file:read(0) ~= nil then
+ dprint("read: failed to call read_common")
+ else
+ dprint2("read: reached end of file")
+ end
+ return false
+ end
+
+ dprint2("read: succeess")
+
+ -- return the position we got to (or nil if we hit EOF/error)
+ return position
+end
+
+----------------------------------------
+-- Wireshark/tshark calls seek_read() for each frame/record in the file, at random times
+-- It passes in (1) a File, (2) CaptureInfo, (3) FrameInfo object, and the offset position number
+-- It expects in return true for successful parsing, or nil/false if there's an error.
+local function seek_read(file, capture, frame, offset)
+ dprint2("seek_read() called")
+
+ -- first move to the right position in the file
+ file:seek("set",offset)
+
+ if not read_common("seek_read", file, capture, frame) then
+ dprint("seek_read: failed to call read_common")
+ return false
+ end
+
+ return true
+end
+
+----------------------------------------
+-- Wireshark/tshark calls read_close() when it's closing the file completely
+-- It passes in (1) a File and (2) CaptureInfo object to this function
+-- this is a good opportunity to clean up any state you may have created during
+-- file reading. (in our case there's no real state)
+local function read_close(file, capture)
+ dprint2("read_close() called")
+ -- we don't really have to reset anything, because we used the
+ -- capture.private_table and wireshark clears it for us after this function
+ return true
+end
+
+----------------------------------------
+-- An often unused function, Wireshark calls this when the sequential walk-through is over
+-- (i.e., no more calls to read(), only to seek_read()).
+-- It passes in (1) a File and (2) CaptureInfo object to this function
+-- This gives you a chance to clean up any state you used during read() calls, but remember
+-- that there will be calls to seek_read() after this (in Wireshark, though not Tshark)
+local function seq_read_close(file, capture)
+ dprint2("First pass of read() calls are over, but there may be seek_read() calls after this")
+ return true
+end
+
+----------------------------------------
+-- ok, so let's create a FileHandler object
+local fh = FileHandler.new("Lua-based PCAP reader", "lua_pcap", "A Lua-based file reader for PCAP-type files","rms")
+
+-- set above functions to the FileHandler
+fh.read_open = read_open
+fh.read = read
+fh.seek_read = seek_read
+fh.read_close = read_close
+fh.seq_read_close = seq_read_close
+fh.extensions = "pcap;cap" -- this is just a hint
+
+-- and finally, register the FileHandler!
+register_filehandler(fh)
+
+dprint2("FileHandler registered")
+
+--------------------------------------------------------------------------------
+-- ok now for the boring stuff that actually does the work
+--------------------------------------------------------------------------------
+
+----------------------------------------
+-- in Lua, we have access to encapsulation types in the 'wtap_encaps' table, but
+-- those numbers don't actually necessarily match the numbers in pcap files
+-- for the encapsulation type, because the namespace got screwed up at some
+-- point in the past (blame LBL NRG, not wireshark for that)
+-- but I'm not going to create the full mapping of these two namespaces
+-- instead we'll just use this smaller table to map them
+-- these are taken from wiretap/pcap-common.c
+local pcap2wtap = {
+ [0] = wtap_encaps.NULL,
+ [1] = wtap_encaps.ETHERNET,
+ [6] = wtap_encaps.TOKEN_RING,
+ [8] = wtap_encaps.SLIP,
+ [9] = wtap_encaps.PPP,
+ [101] = wtap_encaps.RAW_IP,
+ [105] = wtap_encaps.IEEE_802_11,
+ [127] = wtap_encaps.IEEE_802_11_RADIOTAP,
+ [140] = wtap_encaps.MTP2,
+ [141] = wtap_encaps.MTP3,
+ [143] = wtap_encaps.DOCSIS,
+ [147] = wtap_encaps.USER0,
+ [148] = wtap_encaps.USER1,
+ [149] = wtap_encaps.USER2,
+ [150] = wtap_encaps.USER3,
+ [151] = wtap_encaps.USER4,
+ [152] = wtap_encaps.USER5,
+ [153] = wtap_encaps.USER6,
+ [154] = wtap_encaps.USER7,
+ [155] = wtap_encaps.USER8,
+ [156] = wtap_encaps.USER9,
+ [157] = wtap_encaps.USER10,
+ [158] = wtap_encaps.USER11,
+ [159] = wtap_encaps.USER12,
+ [160] = wtap_encaps.USER13,
+ [161] = wtap_encaps.USER14,
+ [162] = wtap_encaps.USER15,
+ [186] = wtap_encaps.USB,
+ [187] = wtap_encaps.BLUETOOTH_H4,
+ [189] = wtap_encaps.USB_LINUX,
+ [195] = wtap_encaps.IEEE802_15_4,
+}
+
+-- we can use the above to directly map very quickly
+-- but to map it backwards we'll use this, because I'm lazy:
+local function wtap2pcap(encap)
+ for k,v in pairs(pcap2wtap) do
+ if v == encap then
+ return k
+ end
+ end
+ return 0
+end
+
+----------------------------------------
+-- here are the "structs" we're going to parse, of the various records in a pcap file
+-- these pattern string gets used in calls to Struct.unpack()
+--
+-- we will prepend a '<' or '>' later, once we figure out what endian-ess the files are in
+--
+-- this is a constant for minimum we need to read before we figure out the filetype
+local FILE_HDR_LEN = 24
+-- a pcap file header struct
+-- this is: magic, version_major, version_minor, timezone, sigfigs, snaplen, encap type
+local FILE_HEADER_PATT = "I4 I2 I2 i4 I4 I4 I4"
+-- it's too bad Struct doesn't have a way to get the number of vars the pattern holds
+-- another thing to add to my to-do list?
+local NUM_HDR_FIELDS = 7
+
+-- these will hold the '<'/'>' prepended version of above
+--local file_header, rec_header
+
+-- snaplen/caplen can't be bigger than this
+local WTAP_MAX_PACKET_SIZE = 65535
+
+----------------------------------------
+-- different pcap file types have different magic values
+-- we need to know various things about them for various functions
+-- in this script, so this table holds all the info
+--
+-- See default_settings table above for the defaults used if this table
+-- doesn't override them.
+--
+-- Arguably, these magic types represent different "Protocols" to dissect later,
+-- but this script treats them all as "pcapfile" protocol.
+--
+-- From this table, we'll auto-create a value-string table for file header magic field
+local magic_spells =
+{
+ normal =
+ {
+ magic = 0xa1b2c3d4,
+ name = "Normal (Big-endian)",
+ },
+ swapped =
+ {
+ magic = 0xd4c3b2a1,
+ name = "Swapped Normal (Little-endian)",
+ endianess = ENC_LITTLE_ENDIAN,
+ },
+ modified =
+ {
+ -- this is for a ss991029 patched format only
+ magic = 0xa1b2cd34,
+ name = "Modified",
+ rec_hdr_len = 24,
+ rec_hdr_patt = "I4I4I4I4 I4 I2 I1 I1",
+ num_rec_fields = 8,
+ },
+ swapped_modified =
+ {
+ -- this is for a ss991029 patched format only
+ magic = 0x34cdb2a1,
+ name = "Swapped Modified",
+ rec_hdr_len = 24,
+ rec_hdr_patt = "I4I4I4I4 I4 I2 I1 I1",
+ num_rec_fields = 8,
+ endianess = ENC_LITTLE_ENDIAN,
+ },
+ nsecs =
+ {
+ magic = 0xa1b23c4d,
+ name = "Nanosecond",
+ time_precision = wtap_filetypes.TSPREC_NSEC,
+ },
+ swapped_nsecs =
+ {
+ magic = 0x4d3cb2a1,
+ name = "Swapped Nanosecond",
+ endianess = ENC_LITTLE_ENDIAN,
+ time_precision = wtap_filetypes.TSPREC_NSEC,
+ },
+}
+
+-- create a magic-to-spell entry table from above magic_spells table
+-- so we can find them faster during file read operations
+-- we could just add them right back into spells table, but this is cleaner
+local magic_values = {}
+for k,t in pairs(magic_spells) do
+ magic_values[t.magic] = t
+end
+
+-- the function which makes a copy of the default settings per file
+local function new_settings()
+ dprint2("creating new file_settings")
+ local file_settings = {}
+ for k,v in pairs(default_settings) do
+ file_settings[k] = v
+ end
+ return file_settings
+end
+
+-- set the file_settings that the magic value defines in magic_values
+local function set_magic_file_settings(magic)
+ local t = magic_values[magic]
+ if not t then
+ dprint("set_magic_file_settings: did not find magic settings for:",magic)
+ return false
+ end
+
+ local file_settings = new_settings()
+
+ -- the magic_values/spells table uses the same key names, so this is easy
+ for k,v in pairs(t) do
+ file_settings[k] = v
+ end
+
+ -- based on endianess, set the file_header and rec_header
+ -- and determine corrected_magic
+ if file_settings.endianess == ENC_BIG_ENDIAN then
+ file_settings.file_hdr_patt = '>' .. FILE_HEADER_PATT
+ file_settings.rec_hdr_patt = '>' .. file_settings.rec_hdr_patt
+ file_settings.corrected_magic = magic
+ else
+ file_settings.file_hdr_patt = '<' .. FILE_HEADER_PATT
+ file_settings.rec_hdr_patt = '<' .. file_settings.rec_hdr_patt
+ local m = Struct.pack(">I4", magic)
+ file_settings.corrected_magic = Struct.unpack("<I4", m)
+ end
+
+ file_settings.rec_hdr_len = Struct.size(file_settings.rec_hdr_patt)
+
+ return file_settings
+end
+
+----------------------------------------
+-- internal functions declared previously
+----------------------------------------
+
+----------------------------------------
+-- used by read_open(), this parses the file header
+parse_file_header = function(file)
+ dprint2("parse_file_header() called")
+
+ -- by default, file:read() gets the next "string", meaning ending with a newline \n
+ -- but we want raw byte reads, so tell it how many bytes to read
+ local line = file:read(FILE_HDR_LEN)
+
+ -- it's ok for us to not be able to read it, but we need to tell wireshark the
+ -- file's not for us, so return false
+ if not line then return false end
+
+ dprint2("parse_file_header: got this line:\n'", Struct.tohex(line,false,":"), "'")
+
+ -- let's peek at the magic int32, assuming it's big-endian
+ local magic = Struct.unpack(">I4", line)
+
+ local file_settings = set_magic_file_settings(magic)
+
+ if not file_settings then
+ dprint("magic was: '", magic, "', so not a known pcap file?")
+ return false
+ end
+
+ -- this is: magic, version_major, version_minor, timezone, sigfigs, snaplen, encap type
+ local fields = { Struct.unpack(file_settings.file_hdr_patt, line) }
+
+ -- sanity check; also note that Struct.unpack() returns the fields plus
+ -- a number of where in the line it stopped reading (i.e., the end in this case)
+ -- so we got back number of fields + 1
+ if #fields ~= NUM_HDR_FIELDS + 1 then
+ -- this should never happen, since we already told file:read() to grab enough bytes
+ dprint("parse_file_header: failed to read the file header")
+ return nil
+ end
+
+ -- fields[1] is the magic, which we already parsed and saved before, but just to be sure
+ -- our endianess is set right, we validate what we got is what we expect now that
+ -- endianess has been corrected
+ if fields[1] ~= file_settings.corrected_magic then
+ dprint ("parse_file_header: endianess screwed up? Got:'", fields[1],
+ "', but wanted:", file_settings.corrected_magic)
+ return nil
+ end
+
+ file_settings.version_major = fields[2]
+ file_settings.version_minor = fields[3]
+ file_settings.timezone = fields[4]
+ file_settings.sigfigs = fields[5]
+ file_settings.read_snaplen = fields[6]
+ file_settings.linktype = fields[7]
+
+ -- wireshark only supports version 2.0 and later
+ if fields[2] < 2 then
+ dprint("got version =",VERSION_MAJOR,"but only version 2 or greater supported")
+ return false
+ end
+
+ -- convert pcap file interface type to wtap number type
+ file_settings.wtap_type = pcap2wtap[file_settings.linktype]
+ if not file_settings.wtap_type then
+ dprint("file nettype", file_settings.linktype,
+ "couldn't be mapped to wireshark wtap type")
+ return false
+ end
+
+ file_settings.snaplen = file_settings.read_snaplen
+ if file_settings.snaplen > WTAP_MAX_PACKET_SIZE then
+ file_settings.snaplen = WTAP_MAX_PACKET_SIZE
+ end
+
+ dprint2("read_file_header: got magic='", magic,
+ "', major version='", file_settings.version_major,
+ "', minor='", file_settings.version_minor,
+ "', timezone='", file_settings.timezone,
+ "', sigfigs='", file_settings.sigfigs,
+ "', read_snaplen='", file_settings.read_snaplen,
+ "', snaplen='", file_settings.snaplen,
+ "', nettype ='", file_settings.linktype,
+ "', wtap ='", file_settings.wtap_type)
+
+ --ok, it's a pcap file
+ dprint2("parse_file_header: success")
+ return file_settings
+end
+
+----------------------------------------
+-- this is used by both read() and seek_read()
+-- the calling function to this should have already set the file position correctly
+read_common = function(funcname, file, capture, frame)
+ dprint2(funcname,": read_common() called")
+
+ -- get the state info
+ local file_settings = capture.private_table
+
+ -- first parse the record header, which will set the FrameInfo fields
+ if not parse_rec_header(funcname, file, file_settings, frame) then
+ dprint2(funcname, ": read_common: hit end of file or error")
+ return false
+ end
+
+ frame.encap = file_settings.wtap_type
+
+ -- now we need to get the packet bytes from the file record into the frame...
+ -- we *could* read them into a string using file:read(numbytes), and then
+ -- set them to frame.data so that wireshark gets it...
+ -- but that would mean the packet's string would be copied into Lua
+ -- and then sent right back into wireshark, which is gonna slow things
+ -- down; instead FrameInfo has a read_data() method, which makes
+ -- wireshark read directly from the file into the frame buffer, so we use that
+ if not frame:read_data(file, frame.captured_length) then
+ dprint(funcname, ": read_common: failed to read data from file into buffer")
+ return false
+ end
+
+ return true
+end
+
+----------------------------------------
+-- the function to parse individual records
+parse_rec_header = function(funcname, file, file_settings, frame)
+ dprint2(funcname,": parse_rec_header() called")
+
+ local line = file:read(file_settings.rec_hdr_len)
+
+ -- it's ok for us to not be able to read it, if it's end of file
+ if not line then return false end
+
+ -- this is: time_sec, time_usec, capture_len, original_len
+ local fields = { Struct.unpack(file_settings.rec_hdr_patt, line) }
+
+ -- sanity check; also note that Struct.unpack() returns the fields plus
+ -- a number of where in the line it stopped reading (i.e., the end in this case)
+ -- so we got back number of fields + 1
+ if #fields ~= file_settings.num_rec_fields + 1 then
+ dprint(funcname, ": parse_rec_header: failed to read the record header, got:",
+ #fields, ", expected:", file_settings.num_rec_fields)
+ return nil
+ end
+
+ local nsecs = fields[2]
+
+ if file_settings.time_precision == wtap_filetypes.TSPREC_USEC then
+ nsecs = nsecs * 1000
+ elseif file_settings.time_precision == wtap_filetypes.TSPREC_MSEC then
+ nsecs = nsecs * 1000000
+ end
+
+ frame.time = NSTime(fields[1], nsecs)
+
+ local caplen, origlen = fields[3], fields[4]
+
+ -- sanity check, verify captured length isn't more than original length
+ if caplen > origlen then
+ dprint("captured length of", caplen, "is bigger than original length of", origlen)
+ -- swap them, a cool Lua ability
+ caplen, origlen = origlen, caplen
+ end
+
+ if caplen > WTAP_MAX_PACKET_SIZE then
+ dprint("Got a captured_length of", caplen, "which is too big")
+ caplen = WTAP_MAX_PACKET_SIZE
+ end
+
+ frame.rec_type = wtap_rec_types.PACKET
+
+ frame.captured_length = caplen
+ frame.original_length = origlen
+
+ frame.flags = wtap_presence_flags.TS + wtap_presence_flags.CAP_LEN -- for timestamp|cap_len
+
+ dprint2(funcname,": parse_rec_header() returning")
+ return true
+end
+
+
+
+--------------------------------------------------------------------------------
+-- file writer handling functions for Wireshark to use
+--------------------------------------------------------------------------------
+
+-- file encaps we can handle writing
+local canwrite = {
+ [ wtap_encaps.NULL ] = true,
+ [ wtap_encaps.ETHERNET ] = true,
+ [ wtap_encaps.PPP ] = true,
+ [ wtap_encaps.RAW_IP ] = true,
+ [ wtap_encaps.IEEE_802_11 ] = true,
+ [ wtap_encaps.MTP2 ] = true,
+ [ wtap_encaps.MTP3 ] = true,
+ -- etc., etc.
+}
+
+-- we can't reuse the variables we used in the reader, because this script might be used to both
+-- open a file for reading and write it out, at the same time, so we cerate another file_settings
+-- instance.
+-- set the file_settings for the little-endian version in magic_spells
+local function create_writer_file_settings()
+ dprint2("create_writer_file_settings called")
+ local t = magic_spells.swapped
+
+ local file_settings = new_settings()
+
+ -- the magic_values/spells table uses the same key names, so this is easy
+ for k,v in pairs(t) do
+ file_settings[k] = v
+ end
+
+ -- based on endianess, set the file_header and rec_header
+ -- and determine corrected_magic
+ if file_settings.endianess == ENC_BIG_ENDIAN then
+ file_settings.file_hdr_patt = '>' .. FILE_HEADER_PATT
+ file_settings.rec_hdr_patt = '>' .. file_settings.rec_hdr_patt
+ file_settings.corrected_magic = file_settings.magic
+ else
+ file_settings.file_hdr_patt = '<' .. FILE_HEADER_PATT
+ file_settings.rec_hdr_patt = '<' .. file_settings.rec_hdr_patt
+ local m = Struct.pack(">I4", file_settings.magic)
+ file_settings.corrected_magic = Struct.unpack("<I4", m)
+ end
+
+ file_settings.rec_hdr_len = Struct.size(file_settings.rec_hdr_patt)
+
+ return file_settings
+end
+
+----------------------------------------
+-- The can_write_encap() function is called by Wireshark when it wants to write out a file,
+-- and needs to see if this file writer can handle the packet types in the window.
+-- We need to return true if we can handle it, else false
+local function can_write_encap(encap)
+ dprint2("can_write_encap() called with encap=",encap)
+ return canwrite[encap] or false
+end
+
+local function write_open(file, capture)
+ dprint2("write_open() called")
+
+ local file_settings = create_writer_file_settings()
+
+ -- write out file header
+ local hdr = Struct.pack(file_settings.file_hdr_patt,
+ file_settings.corrected_magic,
+ file_settings.version_major,
+ file_settings.version_minor,
+ file_settings.timezone,
+ file_settings.sigfigs,
+ capture.snapshot_length,
+ wtap2pcap(capture.encap))
+
+ if not hdr then
+ dprint("write_open: error generating file header")
+ return false
+ end
+
+ dprint2("write_open generating:", Struct.tohex(hdr))
+
+ if not file:write(hdr) then
+ dprint("write_open: error writing file header to file")
+ return false
+ end
+
+ -- save settings
+ capture.private_table = file_settings
+
+ return true
+end
+
+local function write(file, capture, frame)
+ dprint2("write() called")
+
+ -- get file settings
+ local file_settings = capture.private_table
+ if not file_settings then
+ dprint("write() failed to get private table file settings")
+ return false
+ end
+
+ -- write out record header: time_sec, time_usec, capture_len, original_len
+
+ -- first get times
+ local nstime = frame.time
+
+ -- pcap format is in usecs, but wireshark's internal is nsecs
+ local nsecs = nstime.nsecs
+
+ if file_settings.time_precision == wtap_filetypes.TSPREC_USEC then
+ nsecs = nsecs / 1000
+ elseif file_settings.time_precision == wtap_filetypes.TSPREC_MSEC then
+ nsecs = nsecs / 1000000
+ end
+
+ local hdr = Struct.pack(file_settings.rec_hdr_patt,
+ nstime.secs,
+ nsecs,
+ frame.captured_length,
+ frame.original_length)
+
+ if not hdr then
+ dprint("write: error generating record header")
+ return false
+ end
+
+ if not file:write(hdr) then
+ dprint("write: error writing record header to file")
+ return false
+ end
+
+ -- we could write the packet data the same way, by getting frame.data and writing it out
+ -- but we can avoid copying those bytes into Lua by using the write_data() function
+ if not frame:write_data(file) then
+ dprint("write: error writing record data to file")
+ return false
+ end
+
+ return true
+end
+
+local function write_close(file, capture)
+ dprint2("write_close() called")
+ dprint2("Good night, and good luck")
+ return true
+end
+
+-- ok, so let's create another FileHandler object
+local fh2 = FileHandler.new("Lua-based PCAP writer", "lua_pcap2", "A Lua-based file writer for PCAP-type files","wms")
+
+-- set above functions to the FileHandler
+fh2.can_write_encap = can_write_encap
+fh2.write_open = write_open
+fh2.write = write
+fh2.write_close = write_close
+fh2.extensions = "pcap;cap" -- this is just a hint
+
+-- and finally, register the FileHandler!
+register_filehandler(fh2)
+
+dprint2("Second FileHandler registered")
diff --git a/test/lua/pinfo.lua b/test/lua/pinfo.lua
new file mode 100644
index 0000000..52bd3d0
--- /dev/null
+++ b/test/lua/pinfo.lua
@@ -0,0 +1,220 @@
+-- test script for Pinfo and Address functions
+-- use with dhcp.pcap in test/captures directory
+
+------------- general test helper funcs ------------
+local testlib = require("testlib")
+local FRAME = "frame"
+local DENIED = "denied"
+local GETTER = "getter"
+local SETTER = "setter"
+local ADDR = "address"
+local OTHER = "other"
+
+-- expected number of runs per type
+local n_frames = 4
+local taptests = {
+ [FRAME]=n_frames,
+ [DENIED]=n_frames*32,
+ [GETTER]=n_frames*39,
+ [SETTER]=n_frames*15,
+ [ADDR]=n_frames*6,
+ [OTHER]=n_frames*2,
+}
+testlib.init(taptests)
+
+---------
+-- the following are so we can use pcall (which needs a function to call)
+local function setPinfo(pinfo,name,value)
+ pinfo[name] = value
+end
+
+local function getPinfo(pinfo,name)
+ local foo = pinfo[name]
+end
+
+------------- test script ------------
+
+
+local tap = Listener.new()
+
+
+function tap.packet(pinfo,tvb)
+ testlib.countPacket(FRAME)
+ testlib.testing(FRAME,"Pinfo in Frame")
+
+ testlib.test(OTHER,"typeof-1", typeof(pinfo) == "Pinfo")
+
+ testlib.test(OTHER,"tostring-1", tostring(pinfo) == "a Pinfo")
+
+ testlib.testing(FRAME,"negative tests")
+
+ -- try to set read-only attributes
+ testlib.test(DENIED,"Pinfo.number-set-1",not pcall(setPinfo,pinfo,"number",0))
+ testlib.test(DENIED,"Pinfo.len-set-1",not pcall(setPinfo,pinfo,"len",0))
+ testlib.test(DENIED,"Pinfo.caplen-set-1",not pcall(setPinfo,pinfo,"caplen",0))
+ testlib.test(DENIED,"Pinfo.rel_ts-set-1",not pcall(setPinfo,pinfo,"rel_ts",0))
+ testlib.test(DENIED,"Pinfo.delta_ts-set-1",not pcall(setPinfo,pinfo,"delta_ts",0))
+ testlib.test(DENIED,"Pinfo.delta_dis_ts-set-1",not pcall(setPinfo,pinfo,"delta_dis_ts",0))
+ testlib.test(DENIED,"Pinfo.visited-set-1",not pcall(setPinfo,pinfo,"visited",0))
+ testlib.test(DENIED,"Pinfo.lo-set-1",not pcall(setPinfo,pinfo,"lo",0))
+ testlib.test(DENIED,"Pinfo.hi-set-1",not pcall(setPinfo,pinfo,"hi",0))
+ testlib.test(DENIED,"Pinfo.port_type-set-1",not pcall(setPinfo,pinfo,"port_type",0))
+ testlib.test(DENIED,"Pinfo.match-set-1",not pcall(setPinfo,pinfo,"match",0))
+ testlib.test(DENIED,"Pinfo.curr_proto-set-1",not pcall(setPinfo,pinfo,"curr_proto",0))
+ testlib.test(DENIED,"Pinfo.columns-set-1",not pcall(setPinfo,pinfo,"columns",0))
+ testlib.test(DENIED,"Pinfo.cols-set-1",not pcall(setPinfo,pinfo,"cols",0))
+ testlib.test(DENIED,"Pinfo.private-set-1",not pcall(setPinfo,pinfo,"private",0))
+ testlib.test(DENIED,"Pinfo.fragmented-set-1",not pcall(setPinfo,pinfo,"fragmented",0))
+ testlib.test(DENIED,"Pinfo.in_error_pkt-set-1",not pcall(setPinfo,pinfo,"in_error_pkt",0))
+ testlib.test(DENIED,"Pinfo.match_uint-set-1",not pcall(setPinfo,pinfo,"match_uint",0))
+ testlib.test(DENIED,"Pinfo.match_string-set-1",not pcall(setPinfo,pinfo,"match_string",0))
+
+ -- wrong type being set
+ testlib.test(DENIED,"Pinfo.src-set-1",not pcall(setPinfo,pinfo,"src","foobar"))
+ testlib.test(DENIED,"Pinfo.dst-set-1",not pcall(setPinfo,pinfo,"dst","foobar"))
+ testlib.test(DENIED,"Pinfo.dl_src-set-1",not pcall(setPinfo,pinfo,"dl_src","foobar"))
+ testlib.test(DENIED,"Pinfo.dl_dst-set-1",not pcall(setPinfo,pinfo,"dl_dst","foobar"))
+ testlib.test(DENIED,"Pinfo.net_src-set-1",not pcall(setPinfo,pinfo,"net_src","foobar"))
+ testlib.test(DENIED,"Pinfo.net_dst-set-1",not pcall(setPinfo,pinfo,"net_dst","foobar"))
+ testlib.test(DENIED,"Pinfo.src_port-set-1",not pcall(setPinfo,pinfo,"src_port","foobar"))
+ testlib.test(DENIED,"Pinfo.dst_port-set-1",not pcall(setPinfo,pinfo,"dst_port","foobar"))
+ testlib.test(DENIED,"Pinfo.can_desegment-set-1",not pcall(setPinfo,pinfo,"can_desegment","foobar"))
+ testlib.test(DENIED,"Pinfo.desegment_len-set-1",not pcall(setPinfo,pinfo,"desegment_len","foobar"))
+ testlib.test(DENIED,"Pinfo.desegment_offset-set-1",not pcall(setPinfo,pinfo,"desegment_offset","foobar"))
+
+ -- invalid attribute names
+ testlib.test(DENIED,"Pinfo.set-1",not pcall(setPinfo,pinfo,"foobar","foobar"))
+ testlib.test(DENIED,"Pinfo.get-12",not pcall(getPinfo,pinfo,"foobar"))
+
+ testlib.testing(FRAME,"basic getter tests")
+
+ local pktlen, srcip, dstip, srcport, dstport
+
+ if pinfo.number == 1 or pinfo.number == 3 then
+ pktlen = 314
+ srcip = "0.0.0.0"
+ dstip = "255.255.255.255"
+ srcport = 68
+ dstport = 67
+ else
+ pktlen = 342
+ srcip = "192.168.0.1"
+ dstip = "192.168.0.10"
+ srcport = 67
+ dstport = 68
+ end
+
+ testlib.test(GETTER,"Pinfo.number-get-1",pinfo.number == testlib.getPktCount(FRAME))
+ testlib.test(GETTER,"Pinfo.len-get-1",pinfo.len == pktlen)
+ testlib.test(GETTER,"Pinfo.caplen-get-1",pinfo.caplen == pktlen)
+ testlib.test(GETTER,"Pinfo.visited-get-1",pinfo.visited == true)
+ testlib.test(GETTER,"Pinfo.lo-get-1",tostring(pinfo.lo) == srcip)
+ testlib.test(GETTER,"Pinfo.lo-get-2",typeof(pinfo.lo) == "Address")
+ testlib.test(GETTER,"Pinfo.hi-get-1",tostring(pinfo.hi) == dstip)
+ testlib.test(GETTER,"Pinfo.hi-get-2",typeof(pinfo.hi) == "Address")
+ testlib.test(GETTER,"Pinfo.port_type-get-1",pinfo.port_type == 3)
+ testlib.test(GETTER,"Pinfo.match-get-1",pinfo.match == 0)
+ testlib.test(GETTER,"Pinfo.curr_proto-get-1",tostring(pinfo.curr_proto) == "<Missing Protocol Name>")
+ testlib.test(GETTER,"Pinfo.columns-get-1",tostring(pinfo.columns) == "Columns")
+ testlib.test(GETTER,"Pinfo.columns-get-2",typeof(pinfo.columns) == "Columns")
+ testlib.test(GETTER,"Pinfo.cols-get-1",tostring(pinfo.cols) == "Columns")
+ testlib.test(GETTER,"Pinfo.cols-get-2",typeof(pinfo.cols) == "Columns")
+ testlib.test(GETTER,"Pinfo.private-get-1",type(pinfo.private) == "userdata")
+ testlib.test(GETTER,"Pinfo.fragmented-get-1",pinfo.fragmented == false)
+
+ testlib.test(GETTER,"Pinfo.in_error_pkt-get-1",pinfo.in_error_pkt == false)
+ testlib.test(GETTER,"Pinfo.match_uint-get-1",pinfo.match_uint == 0)
+ testlib.test(GETTER,"Pinfo.match_string-get-1",pinfo.match_string == nil)
+
+ testlib.test(GETTER,"Pinfo.src-get-1",tostring(pinfo.src) == srcip)
+ testlib.test(GETTER,"Pinfo.src-get-2",typeof(pinfo.src) == "Address")
+ testlib.test(GETTER,"Pinfo.dst-get-1",tostring(pinfo.dst) == dstip)
+ testlib.test(GETTER,"Pinfo.dst-get-2",typeof(pinfo.dst) == "Address")
+
+ testlib.test(GETTER,"Pinfo.dl_src-get-1",typeof(pinfo.dl_src) == "Address")
+ testlib.test(GETTER,"Pinfo.dl_dst-get-1",typeof(pinfo.dl_dst) == "Address")
+ testlib.test(GETTER,"Pinfo.net_src-get-1",tostring(pinfo.net_src) == srcip)
+ testlib.test(GETTER,"Pinfo.net_src-get-2",typeof(pinfo.net_src) == "Address")
+ testlib.test(GETTER,"Pinfo.net_dst-get-1",tostring(pinfo.net_dst) == dstip)
+ testlib.test(GETTER,"Pinfo.net_dst-get-2",typeof(pinfo.net_dst) == "Address")
+ testlib.test(GETTER,"Pinfo.src_port-get-1",pinfo.src_port == srcport)
+ testlib.test(GETTER,"Pinfo.dst_port-get-1",pinfo.dst_port == dstport)
+ testlib.test(GETTER,"Pinfo.can_desegment-get-1",pinfo.can_desegment == 0)
+ testlib.test(GETTER,"Pinfo.desegment_len-get-1",pinfo.desegment_len == 0)
+ testlib.test(GETTER,"Pinfo.desegment_offset-get-1",pinfo.desegment_offset == 0)
+
+ testlib.test(GETTER,"pinfo.p2p_dir", pinfo.p2p_dir == P2P_DIR_UNKNOWN)
+
+ if pinfo.number == 1 then
+ testlib.test(GETTER,"Pinfo.rel_ts-get-1",pinfo.rel_ts == 0)
+ testlib.test(GETTER,"Pinfo.delta_ts-get-1",pinfo.delta_ts == 0)
+ testlib.test(GETTER,"Pinfo.delta_dis_ts-get-1",pinfo.delta_dis_ts == 0)
+ elseif pinfo.number == 2 then
+ testlib.test(GETTER,"Pinfo.rel_ts-get-1",pinfo.rel_ts == 0.000295)
+ testlib.test(GETTER,"Pinfo.delta_ts-get-1",pinfo.delta_ts == 0.000295)
+ testlib.test(GETTER,"Pinfo.delta_dis_ts-get-1",pinfo.delta_dis_ts == 0.000295)
+ elseif pinfo.number == 3 then
+ testlib.test(GETTER,"Pinfo.rel_ts-get-1",pinfo.rel_ts == 0.070031)
+ testlib.test(GETTER,"Pinfo.delta_ts-get-1",pinfo.delta_ts == 0.069736)
+ testlib.test(GETTER,"Pinfo.delta_dis_ts-get-1",pinfo.delta_dis_ts == 0.069736)
+ elseif pinfo.number == 4 then
+ testlib.test(GETTER,"Pinfo.rel_ts-get-1",pinfo.rel_ts == 0.070345)
+ testlib.test(GETTER,"Pinfo.delta_ts-get-1",pinfo.delta_ts == 0.000314)
+ testlib.test(GETTER,"Pinfo.delta_dis_ts-get-1",pinfo.delta_dis_ts == 0.000314)
+ end
+
+
+ testlib.testing(FRAME,"basic setter tests")
+
+ local tmp = pinfo.src
+ pinfo.src = pinfo.dst
+ pinfo.dst = tmp
+ testlib.test(SETTER,"Pinfo.src-set-1",tostring(pinfo.src) == dstip)
+ testlib.test(SETTER,"Pinfo.src-set-1",typeof(pinfo.src) == "Address")
+ testlib.test(SETTER,"Pinfo.dst-set-1",tostring(pinfo.dst) == srcip)
+ testlib.test(SETTER,"Pinfo.dst-set-1",typeof(pinfo.dst) == "Address")
+
+ local dl_dst_val = tostring(pinfo.dl_dst)
+ local dl_src_val = tostring(pinfo.dl_src)
+ tmp = pinfo.dl_src
+ pinfo.dl_src = pinfo.dl_dst
+ pinfo.dl_dst = tmp
+ testlib.test(SETTER,"Pinfo.dl_src-set-1",tostring(pinfo.dl_src) == dl_dst_val)
+ testlib.test(SETTER,"Pinfo.dl_dst-set-1",tostring(pinfo.dl_dst) == dl_src_val)
+
+ tmp = pinfo.net_src
+ pinfo.net_src = pinfo.net_dst
+ pinfo.net_dst = tmp
+ testlib.test(SETTER,"Pinfo.net_src-set-1",tostring(pinfo.net_src) == dstip)
+ testlib.test(SETTER,"Pinfo.net_src-set-1",typeof(pinfo.net_src) == "Address")
+ testlib.test(SETTER,"Pinfo.net_dst-set-1",tostring(pinfo.net_dst) == srcip)
+ testlib.test(SETTER,"Pinfo.net_dst-set-1",typeof(pinfo.net_dst) == "Address")
+
+ tmp = pinfo.src_port
+ pinfo.src_port = pinfo.dst_port
+ pinfo.dst_port = tmp
+ testlib.test(SETTER,"Pinfo.src_port-set-1",pinfo.src_port == dstport)
+ testlib.test(SETTER,"Pinfo.dst_port-set-1",pinfo.dst_port == srcport)
+
+ pinfo.can_desegment = 12
+ testlib.test(SETTER,"Pinfo.can_desegment-set-1",pinfo.can_desegment == 12)
+ pinfo.desegment_len = 34
+ testlib.test(SETTER,"Pinfo.desegment_len-set-1",pinfo.desegment_len == 34)
+ pinfo.desegment_offset = 45
+ testlib.test(SETTER,"Pinfo.desegment_offset-set-1",pinfo.desegment_offset == 45)
+
+ testlib.testing(FRAME,"Address functions")
+ testlib.test(ADDR,"Address-eq-1", pinfo.lo == pinfo.dst)
+ testlib.test(ADDR,"Address-eq-2", pinfo.lo ~= pinfo.hi)
+ testlib.test(ADDR,"Address-lt-1", pinfo.lo < pinfo.hi)
+ testlib.test(ADDR,"Address-lt-2", pinfo.hi > pinfo.lo)
+ testlib.test(ADDR,"Address-le-1", pinfo.lo <= pinfo.hi)
+ testlib.test(ADDR,"Address-le-2", pinfo.lo <= pinfo.dst)
+
+ testlib.pass(FRAME)
+
+end
+
+function tap.draw()
+ testlib.getResults()
+end
diff --git a/test/lua/proto.lua b/test/lua/proto.lua
new file mode 100644
index 0000000..cc03898
--- /dev/null
+++ b/test/lua/proto.lua
@@ -0,0 +1,211 @@
+----------------------------------------
+-- script-name: proto.lua
+-- Test the Proto/ProtoField API
+----------------------------------------
+
+------------- general test helper funcs ------------
+local testlib = require("testlib")
+
+local OTHER = "other"
+
+-- expected number of runs per type
+local taptests = {
+ [OTHER]=48
+}
+testlib.init(taptests)
+
+---------
+-- the following are so we can use pcall (which needs a function to call)
+local function callFunc(func,...)
+ func(...)
+end
+
+local function callObjFuncGetter(vart,varn,tobj,name,...)
+ vart[varn] = tobj[name](...)
+end
+
+local function setValue(tobj,name,value)
+ tobj[name] = value
+end
+
+local function getValue(tobj,name)
+ local foo = tobj[name]
+end
+
+------------- test script ------------
+
+----------------------------------------
+-- creates a Proto object, but doesn't register it yet
+testlib.testing(OTHER,"Proto creation")
+
+testlib.test(OTHER,"Proto.__call", pcall(callFunc,Proto,"foo","Foo Protocol"))
+testlib.test(OTHER,"Proto.__call", pcall(callFunc,Proto,"foo1","Foo1 Protocol"))
+testlib.test(OTHER,"Proto.__call", not pcall(callFunc,Proto,"","Bar Protocol"))
+testlib.test(OTHER,"Proto.__call", not pcall(callFunc,Proto,nil,"Bar Protocol"))
+testlib.test(OTHER,"Proto.__call", not pcall(callFunc,Proto,"bar",""))
+testlib.test(OTHER,"Proto.__call", not pcall(callFunc,Proto,"bar",nil))
+
+
+local dns = Proto("mydns","MyDNS Protocol")
+
+testlib.test(OTHER,"Proto.__tostring", tostring(dns) == "Proto: MYDNS")
+
+----------------------------------------
+-- multiple ways to do the same thing: create a protocol field (but not register it yet)
+-- the abbreviation should always have "<myproto>." before the specific abbreviation, to avoid collisions
+testlib.testing(OTHER,"ProtoField creation")
+
+local pfields = {} -- a table to hold fields, so we can pass them back/forth through pcall()
+--- variable -- what dissector.lua did, so we almost match it
+local pf_trasaction_id = 1 -- ProtoField.new("Transaction ID", "mydns.trans_id", ftypes.UINT16)
+local pf_flags = 2 -- ProtoField.new("Flags", "mydns.flags", ftypes.UINT16, nil, base.HEX)
+local pf_num_questions = 3 -- ProtoField.uint16("mydns.num_questions", "Number of Questions")
+local pf_num_answers = 4 -- ProtoField.uint16("mydns.num_answers", "Number of Answer RRs")
+local pf_num_authority_rr = 5 -- ProtoField.uint16("mydns.num_authority_rr", "Number of Authority RRs")
+local pf_num_additional_rr = 6 -- ProtoField.uint16("mydns.num_additional_rr", "Number of Additional RRs")
+
+testlib.test(OTHER,"ProtoField.new",pcall(callObjFuncGetter, pfields,pf_trasaction_id, ProtoField,"new", "Transaction ID", "mydns.trans_id", ftypes.INT16,nil,"base.DEC"))
+testlib.test(OTHER,"ProtoField.new",pcall(callObjFuncGetter, pfields,pf_flags, ProtoField,"new", "Flags", "mydns.flags", ftypes.UINT16, nil, "base.HEX"))
+
+-- tries to register a field that already exists (from the real dns proto dissector) but with incompatible type
+testlib.test(OTHER,"ProtoField.new_duplicate_bad",not pcall(callObjFuncGetter, pfields,10, ProtoField,"new", "Flags", "dns.flags", ftypes.INT16, nil, "base.HEX"))
+testlib.test(OTHER,"ProtoField.int16_duplicate_bad",not pcall(callObjFuncGetter, pfields,10, ProtoField,"int16", "dns.id","Transaction ID"))
+-- now compatible (but different type)
+testlib.test(OTHER,"ProtoField.new_duplicate_ok",pcall(callObjFuncGetter, pfields,10, ProtoField,"new", "Flags", "dns.flags", ftypes.UINT32, nil, "base.HEX"))
+testlib.test(OTHER,"ProtoField.uint16_duplicate_ok",pcall(callObjFuncGetter, pfields,10, ProtoField,"uint16", "dns.id","Transaction ID"))
+
+-- invalid valuestring arg
+testlib.test(OTHER,"ProtoField.new_invalid_valuestring",not pcall(callObjFuncGetter, pfields,10, ProtoField,"new", "Transaction ID", "mydns.trans_id", ftypes.INT16,"howdy","base.DEC"))
+-- invalid ftype
+testlib.test(OTHER,"ProtoField.new_invalid_ftype",not pcall(callObjFuncGetter, pfields,10, ProtoField,"new", "Transaction ID", "mydns.trans_id", 9999))
+-- invalid description
+--testlib.test(OTHER,"ProtoField.new_invalid_description",not pcall(callObjFuncGetter, pfields,10, ProtoField,"new", "", "mydns.trans_id", ftypes.INT16))
+testlib.test(OTHER,"ProtoField.new_invalid_description",not pcall(callObjFuncGetter, pfields,10, ProtoField,"new", nil, "mydns.trans_id", ftypes.INT16))
+
+testlib.test(OTHER,"ProtoField.new_invalid_abbr",not pcall(callObjFuncGetter, pfields,10, ProtoField,"new", "trans id", "", ftypes.INT16))
+testlib.test(OTHER,"ProtoField.new_invalid_abbr",not pcall(callObjFuncGetter, pfields,10, ProtoField,"new", "trans id", nil, ftypes.INT16))
+
+testlib.test(OTHER,"ProtoField.int16",pcall(callObjFuncGetter, pfields,pf_num_questions, ProtoField,"int16", "mydns.num_questions", "Number of Questions"))
+testlib.test(OTHER,"ProtoField.int16",pcall(callObjFuncGetter, pfields,pf_num_answers, ProtoField,"int16", "mydns.num_answers", "Number of Answer RRs",base.DEC))
+testlib.test(OTHER,"ProtoField.int16",pcall(callObjFuncGetter, pfields,pf_num_authority_rr, ProtoField,"int16", "mydns.num_authority_rr", "Number of Authority RRs",base.DEC))
+testlib.test(OTHER,"ProtoField.int16",pcall(callObjFuncGetter, pfields,pf_num_additional_rr, ProtoField,"int16", "mydns.num_additional_rr", "Number of Additional RRs"))
+
+-- now undo the table thingy
+pf_trasaction_id = pfields[pf_trasaction_id]
+pf_flags = pfields[pf_flags]
+pf_num_questions = pfields[pf_num_questions]
+pf_num_answers = pfields[pf_num_answers]
+pf_num_authority_rr = pfields[pf_num_authority_rr]
+pf_num_additional_rr = pfields[pf_num_additional_rr]
+
+-- within the flags field, we want to parse/show the bits separately
+-- note the "base" argument becomes the size of the bitmask'ed field when ftypes.BOOLEAN is used
+-- the "mask" argument is which bits we want to use for this field (e.g., base=16 and mask=0x8000 means we want the top bit of a 16-bit field)
+-- again the following shows different ways of doing the same thing basically
+local pf_flag_response = ProtoField.new("Response", "mydns.flags.response", ftypes.BOOLEAN, {"this is a response","this is a query"}, 16, 0x8000, "is the message a response?")
+local pf_flag_opcode = ProtoField.new("Opcode", "mydns.flags.opcode", ftypes.UINT16, nil, base.DEC, 0x7800, "operation code")
+local pf_flag_authoritative = ProtoField.new("Authoritative", "mydns.flags.authoritative", ftypes.BOOLEAN, nil, 16, 0x0400, "is the response authoritative?")
+local pf_flag_truncated = ProtoField.bool("mydns.flags.truncated", "Truncated", 16, nil, 0x0200, "is the message truncated?")
+local pf_flag_recursion_desired = ProtoField.bool("mydns.flags.recursion_desired", "Recursion desired", 16, {"yes","no"}, 0x0100, "do the query recursivley?")
+local pf_flag_recursion_available = ProtoField.bool("mydns.flags.recursion_available", "Recursion available", 16, nil, 0x0080, "does the server support recursion?")
+local pf_flag_z = ProtoField.uint16("mydns.flags.z", "World War Z - Reserved for future use", base.HEX, nil, 0x0040, "when is it the future?")
+local pf_flag_authenticated = ProtoField.bool("mydns.flags.authenticated", "Authenticated", 16, {"yes","no"}, 0x0020, "did the server DNSSEC authenticate?")
+local pf_flag_checking_disabled = ProtoField.bool("mydns.flags.checking_disabled", "Checking disabled", 16, nil, 0x0010)
+
+-- no, these aren't all the DNS response codes - this is just an example
+local rcodes = {
+ [0] = "No Error",
+ [1] = "Format Error",
+ [2] = "Server Failure",
+ [3] = "Non-Existent Domain",
+ [9] = "Server Not Authoritative for zone"
+}
+-- the above rcodes table is used in this next ProtoField
+local pf_flag_rcode = ProtoField.uint16("mydns.flags.rcode", "Response code", base.DEC, rcodes, 0x000F)
+local pf_query = ProtoField.new("Query", "mydns.query", ftypes.BYTES)
+local pf_query_name = ProtoField.new("Name", "mydns.query.name", ftypes.STRING)
+local pf_query_name_len = ProtoField.new("Name Length", "mydns.query.name.len", ftypes.UINT8)
+local pf_query_label_count = ProtoField.new("Label Count", "mydns.query.label.count", ftypes.UINT8)
+local rrtypes = { [1] = "A (IPv4 host address)", [2] = "NS (authoritative name server)", [28] = "AAAA (for geeks only)" }
+local pf_query_type = ProtoField.uint16("mydns.query.type", "Type", base.DEC, rrtypes)
+-- again, not all class types are listed here
+local classes = {
+ [0] = "Reserved",
+ [1] = "IN (Internet)",
+ [2] = "The 1%",
+ [5] = "First class",
+ [6] = "Business class",
+ [65535] = "Cattle class"
+}
+local pf_query_class = ProtoField.uint16("mydns.query.class", "Class", base.DEC, classes, nil, "keep it classy folks")
+
+
+testlib.testing(OTHER,"Proto functions")
+
+----------------------------------------
+-- this actually registers the ProtoFields above, into our new Protocol
+-- in a real script I wouldn't do it this way; I'd build a table of fields programaticaly
+-- and then set dns.fields to it, so as to avoid forgetting a field
+local myfields = { pf_trasaction_id, pf_flags,
+ pf_num_questions, pf_num_answers, pf_num_authority_rr, pf_num_additional_rr,
+ pf_flag_response, pf_flag_opcode, pf_flag_authoritative,
+ pf_flag_truncated, pf_flag_recursion_desired, pf_flag_recursion_available,
+ pf_flag_z, pf_flag_authenticated, pf_flag_checking_disabled, pf_flag_rcode,
+ pf_query, pf_query_name, pf_query_name_len, pf_query_label_count, pf_query_type, pf_query_class }
+
+--dns.fields = myfields
+testlib.test(OTHER,"Proto.fields-set", pcall(setValue,dns,"fields",myfields))
+testlib.test(OTHER,"Proto.fields-get", pcall(getValue,dns,"fields"))
+testlib.test(OTHER,"Proto.fields-get", #dns.fields == #myfields)
+
+local pf_foo = ProtoField.uint16("myfoo.com", "Fooishly", base.DEC, rcodes, 0x000F)
+
+local foo = Proto("myfoo","MyFOO Protocol")
+local bar = Proto("mybar","MyBAR Protocol")
+
+testlib.test(OTHER,"Proto.fields-set", pcall(setValue,foo,"fields",pf_foo))
+testlib.test(OTHER,"Proto.fields-get", #foo.fields == 1)
+testlib.test(OTHER,"Proto.fields-get", foo.fields[1] == pf_foo)
+
+testlib.test(OTHER,"Proto.fields-set", not pcall(setValue,bar,"fields","howdy"))
+testlib.test(OTHER,"Proto.fields-set", not pcall(setValue,bar,"fields",nil))
+testlib.test(OTHER,"Proto.fields-get", #bar.fields == 0)
+
+testlib.test(OTHER,"Proto.name-get", foo.name == "MYFOO")
+testlib.test(OTHER,"Proto.name-set", not pcall(setValue,foo,"name","howdy"))
+
+testlib.test(OTHER,"Proto.description-get", foo.description == "MyFOO Protocol")
+testlib.test(OTHER,"Proto.description-set", not pcall(setValue,foo,"description","howdy"))
+
+testlib.test(OTHER,"Proto.prefs-get", typeof(foo.prefs) == "Prefs")
+testlib.test(OTHER,"Proto.prefs-set", not pcall(setValue,foo,"prefs","howdy"))
+
+local function dummy()
+ setFailed(OTHER)
+ error("dummy function called!")
+ return
+end
+
+-- can't get this because we haven't set it yet
+testlib.test(OTHER,"Proto.dissector-get", not pcall(getValue,foo,"dissector"))
+-- now set it
+testlib.test(OTHER,"Proto.dissector-set", pcall(setValue,foo,"dissector",dummy))
+testlib.test(OTHER,"Proto.dissector-set", not pcall(setValue,foo,"dissector","howdy"))
+testlib.test(OTHER,"Proto.dissector-get", pcall(getValue,foo,"dissector"))
+
+testlib.test(OTHER,"Proto.prefs_changed-set", pcall(setValue,foo,"prefs_changed",dummy))
+testlib.test(OTHER,"Proto.prefs_changed-get", not pcall(getValue,foo,"prefs_changed"))
+testlib.test(OTHER,"Proto.prefs_changed-set", not pcall(setValue,foo,"prefs_changed","howdy"))
+
+local function dummy_init()
+ testlib.test(OTHER,"Proto.init-called",true)
+end
+
+testlib.test(OTHER,"Proto.init-set", pcall(setValue,foo,"init",dummy_init))
+testlib.test(OTHER,"Proto.init-set", pcall(setValue,bar,"init",dummy_init))
+
+testlib.test(OTHER,"Proto.init-get", not pcall(getValue,foo,"init"))
+testlib.test(OTHER,"Proto.init-set", not pcall(setValue,foo,"init","howdy"))
+
+testlib.getResults()
+
diff --git a/test/lua/protobuf_test_called_by_custom_dissector.lua b/test/lua/protobuf_test_called_by_custom_dissector.lua
new file mode 100644
index 0000000..7a16365
--- /dev/null
+++ b/test/lua/protobuf_test_called_by_custom_dissector.lua
@@ -0,0 +1,68 @@
+do
+ local protobuf_dissector = Dissector.get("protobuf")
+
+ -- Create protobuf dissector based on UDP or TCP.
+ -- The UDP dissector will take the whole tvb as a message.
+ -- The TCP dissector will parse tvb as format:
+ -- [4bytes length][a message][4bytes length][a message]...
+ -- @param name The name of the new dissector.
+ -- @param desc The description of the new dissector.
+ -- @param for_udp Register the new dissector to UDP table.(Enable 'Decode as')
+ -- @param for_tcp Register the new dissector to TCP table.(Enable 'Decode as')
+ -- @param msgtype Message type. This must be the root message defined in your .proto file.
+ local function create_protobuf_dissector(name, desc, for_udp, for_tcp, msgtype)
+ local proto = Proto(name, desc)
+ local f_length = ProtoField.uint32(name .. ".length", "Length", base.DEC)
+ proto.fields = { f_length }
+
+ proto.dissector = function(tvb, pinfo, tree)
+ local subtree = tree:add(proto, tvb())
+ if for_udp and pinfo.port_type == 3 then -- UDP
+ if msgtype ~= nil then
+ pinfo.private["pb_msg_type"] = "message," .. msgtype
+ end
+ pcall(Dissector.call, protobuf_dissector, tvb, pinfo, subtree)
+ elseif for_tcp and pinfo.port_type == 2 then -- TCP
+ local offset = 0
+ local remaining_len = tvb:len()
+ while remaining_len > 0 do
+ if remaining_len < 4 then -- head not enough
+ pinfo.desegment_offset = offset
+ pinfo.desegment_len = DESEGMENT_ONE_MORE_SEGMENT
+ return -1
+ end
+
+ local data_len = tvb(offset, 4):uint()
+
+ if remaining_len - 4 < data_len then -- data not enough
+ pinfo.desegment_offset = offset
+ pinfo.desegment_len = data_len - (remaining_len - 4)
+ return -1
+ end
+ subtree:add(f_length, tvb(offset, 4))
+
+ if msgtype ~= nil then
+ pinfo.private["pb_msg_type"] = "message," .. msgtype
+ end
+ pcall(Dissector.call, protobuf_dissector,
+ tvb(offset + 4, data_len):tvb(), pinfo, subtree)
+
+ offset = offset + 4 + data_len
+ remaining_len = remaining_len - 4 - data_len
+ end
+ end
+ pinfo.columns.protocol:set(name)
+ end
+
+ if for_udp then DissectorTable.get("udp.port"):add(0, proto) end
+ if for_tcp then DissectorTable.get("tcp.port"):add(0, proto) end
+ return proto
+ end
+
+ -- default pure protobuf udp and tcp dissector without message type
+ create_protobuf_dissector("protobuf_udp", "Protobuf UDP")
+ create_protobuf_dissector("protobuf_tcp", "Protobuf TCP")
+ -- add more protobuf dissectors with message types
+ create_protobuf_dissector("AddrBook", "Tutorial AddressBook",
+ true, true, "tutorial.AddressBook")
+end
diff --git a/test/lua/protobuf_test_field_subdissector_table.lua b/test/lua/protobuf_test_field_subdissector_table.lua
new file mode 100644
index 0000000..379b2f5
--- /dev/null
+++ b/test/lua/protobuf_test_field_subdissector_table.lua
@@ -0,0 +1,6 @@
+-- Test protobuf_field dissector table
+do
+ local protobuf_field_table = DissectorTable.get("protobuf_field")
+ local png_dissector = Dissector.get("png")
+ protobuf_field_table:add("tutorial.Person.portrait_image", png_dissector)
+end
diff --git a/test/lua/protofield.lua b/test/lua/protofield.lua
new file mode 100644
index 0000000..9d2223f
--- /dev/null
+++ b/test/lua/protofield.lua
@@ -0,0 +1,236 @@
+----------------------------------------
+-- script-name: protofield.lua
+-- test the ProtoField API
+----------------------------------------
+
+local testlib = require("testlib")
+
+local FRAME = "frame"
+local PER_FRAME = "per-frame"
+local OTHER = "other"
+
+-- expected number of runs
+local n_frames = 4
+local taptests = {
+ [FRAME]=n_frames,
+ [PER_FRAME]=n_frames*8,
+ [OTHER]=50,
+}
+testlib.init(taptests)
+
+------------- test script ------------
+
+----------------------------------------
+local test_proto = Proto.new("test", "Test Proto")
+test_proto.fields.time_field = ProtoField.uint16("test.time", "Time", base.UNIT_STRING, {" sec", " secs"})
+test_proto.fields.dist_field = ProtoField.uint16("test.dist", "Distance", base.UNIT_STRING, {" km"})
+test_proto.fields.filtered_field = ProtoField.uint16("test.filtered", "Filtered Field", base.DEC)
+
+-- Field type: CHAR
+success = pcall(ProtoField.new, "char", "test.char0", ftypes.CHAR)
+testlib.test(OTHER,"ProtoField-char", success)
+
+success = pcall(ProtoField.new, "char base NONE without valuestring", "test.char1", ftypes.CHAR, nil, base.NONE)
+testlib.test(OTHER,"ProtoField-char-without-valuestring", not success)
+
+success = pcall(ProtoField.new, "char base NONE with valuestring", "test.char2", ftypes.CHAR, {1, "Value"}, base.NONE)
+testlib.test(OTHER,"ProtoField-char-with-valuestring", success)
+
+success = pcall(ProtoField.new, "char base DEC", "test.char3", ftypes.CHAR, nil, base.DEC)
+testlib.test(OTHER,"ProtoField-char-base-dec", not success)
+
+success = pcall(ProtoField.new, "char base UNIT_STRING", "test.char4", ftypes.CHAR, {" m"}, base.UNIT_STRING)
+testlib.test(OTHER,"ProtoField-char-unit-string", not success)
+
+success = pcall(ProtoField.new, "char base RANGE_STRING", "test.char5", ftypes.CHAR, {{1, 2, "Value"}}, base.RANGE_STRING)
+testlib.test(OTHER,"ProtoField-char-range-string", success)
+
+-- Field type: BOOLEAN/UINT64 with (64 bit) mask
+success = pcall(ProtoField.new, "boolean", "test.boolean0", ftypes.BOOLEAN, nil, base.HEX, 0x1)
+testlib.test(OTHER,"ProtoField-new-bool-mask-trivial", success)
+
+success = pcall(ProtoField.new, "boolean", "test.boolean1", ftypes.BOOLEAN, nil, base.HEX, "1")
+testlib.test(OTHER,"ProtoField-new-bool-mask-string", success)
+
+success = pcall(ProtoField.new, "boolean", "test.boolean2", ftypes.BOOLEAN, nil, base.HEX, UInt64(0x00000001, 0x0))
+testlib.test(OTHER,"ProtoField-new-bool-mask-uint64", success)
+
+success = pcall(ProtoField.new, "boolean", "test.boolean3", ftypes.BOOLEAN, nil, base.NONE, "invalid") -- 0
+testlib.test(OTHER,"ProtoField-new-bool-mask-string-invalid", success)
+
+success = pcall(ProtoField.new, "boolean", "test.boolean4", ftypes.BOOLEAN, nil, base.HEX, "-1") -- 0xFFFFFFFFFFFFFFFF
+testlib.test(OTHER,"ProtoField-new-bool-mask-negative", success)
+
+success = pcall(ProtoField.new, "boolean", "test.boolean5", ftypes.BOOLEAN, nil, base.NONE)
+testlib.test(OTHER,"ProtoField-new-bool-mask-none", success)
+
+success = pcall(ProtoField.new, "boolean", "test.boolean6", ftypes.BOOLEAN, nil, base.NONE, nil)
+testlib.test(OTHER,"ProtoField-new-bool-mask-nil", success)
+
+success = pcall(ProtoField.bool, "test.boolean10", nil, 64, nil, 0x1)
+testlib.test(OTHER,"ProtoField-bool-mask-trivial", success)
+
+success = pcall(ProtoField.bool, "test.boolean11", nil, 64, nil, "1")
+testlib.test(OTHER,"ProtoField-bool-mask-string", success)
+
+success = pcall(ProtoField.bool, "test.boolean12", nil, 64, nil, UInt64(0x00000001, 0x0))
+testlib.test(OTHER,"ProtoField-bool-mask-uint64", success)
+
+success = pcall(ProtoField.bool, "test.boolean13", nil, base.NONE, nil, "invalid") -- 0
+testlib.test(OTHER,"ProtoField-bool-mask-string-invalid", success)
+
+success = pcall(ProtoField.bool, "test.boolean14", nil, 64, nil, "-1") -- 0xFFFFFFFFFFFFFFFF
+testlib.test(OTHER,"ProtoField-bool-mask-negative", success)
+
+success = pcall(ProtoField.bool, "test.boolean15", nil, base.NONE, nil)
+testlib.test(OTHER,"ProtoField-bool-mask-none", success)
+
+success = pcall(ProtoField.bool, "test.boolean16", nil, base.NONE, nil, nil)
+testlib.test(OTHER,"ProtoField-bool-mask-nil", success)
+
+success = pcall(ProtoField.new, "uint64", "test.uint64_0", ftypes.UINT64, nil, base.HEX, 0x1)
+testlib.test(OTHER,"ProtoField-new-uint64-mask-trivial", success)
+
+success = pcall(ProtoField.new, "uint64", "test.uint64_1", ftypes.UINT64, nil, base.HEX, "1")
+testlib.test(OTHER,"ProtoField-new-uint64-mask-string", success)
+
+success = pcall(ProtoField.new, "uint64", "test.uint64_2", ftypes.UINT64, nil, base.HEX, UInt64(0x00000001, 0x0))
+testlib.test(OTHER,"ProtoField-new-uint64-mask-uint64", success)
+
+success = pcall(ProtoField.new, "uint64", "test.uint64_3", ftypes.UINT64, nil, base.NONE, "invalid") -- 0
+testlib.test(OTHER,"ProtoField-new-uint64-mask-string-invalid", success)
+
+success = pcall(ProtoField.new, "uint64", "test.uint64_4", ftypes.UINT64, nil, base.HEX, "-1") -- 0xFFFFFFFFFFFFFFFF
+testlib.test(OTHER,"ProtoField-new-uint64-mask-negative", success)
+
+success = pcall(ProtoField.new, "uint64", "test.uint64_5", ftypes.UINT64, nil, base.NONE)
+testlib.test(OTHER,"ProtoField-new-uint64-mask-none", success)
+
+success = pcall(ProtoField.new, "uint64", "test.uint64_6", ftypes.UINT64, nil, base.NONE, nil)
+testlib.test(OTHER,"ProtoField-new-uint64-mask-nil", success)
+
+success = pcall(ProtoField.uint64, "test.uint64_10", nil, base.HEX, nil, 0x1)
+testlib.test(OTHER,"ProtoField-uint64-mask-trivial", success)
+
+success = pcall(ProtoField.uint64, "test.uint64_11", nil, base.HEX, nil, "1")
+testlib.test(OTHER,"ProtoField-uint64-mask-string", success)
+
+success = pcall(ProtoField.uint64, "test.uint64_12", nil, base.HEX, nil, UInt64(0x00000001, 0x0))
+testlib.test(OTHER,"ProtoField-uint64-mask-uint64", success)
+
+success = pcall(ProtoField.uint64, "test.uint64_13", nil, base.DEC, nil, "invalid") -- 0
+testlib.test(OTHER,"ProtoField-uint64-mask-string-invalid", success)
+
+success = pcall(ProtoField.uint64, "test.uint64_14", nil, base.DEC, nil, "-1") -- 0xFFFFFFFFFFFFFFFF
+testlib.test(OTHER,"ProtoField-uint64-mask-negative", success)
+
+success = pcall(ProtoField.uint64, "test.uint64_15", nil, base.DEC, nil)
+testlib.test(OTHER,"ProtoField-uint64-mask-none", success)
+
+success = pcall(ProtoField.uint64, "test.uint64_16", nil, base.DEC, nil, nil)
+testlib.test(OTHER,"ProtoField-uint64-mask-nil", success)
+
+
+-- Field name: empty, illegal, incompatible
+success = pcall(ProtoField.int8, nil, "empty field name 1")
+testlib.test(OTHER,"ProtoField-empty-field-name-1", not success)
+
+success = pcall(ProtoField.int8, "", "empty field name 2")
+testlib.test(OTHER,"ProtoField-empty-field-name-2", not success)
+
+success = pcall(ProtoField.int8, "test.$", "illegal field name")
+testlib.test(OTHER,"ProtoField-illegal-field-name", not success)
+
+success = pcall(ProtoField.int8, "frame.time", "incompatible field name")
+testlib.test(OTHER,"ProtoField-incompatible-field-name", not success)
+
+-- Actual name: empty
+success = pcall(ProtoField.int8, "test.empty_name_1")
+testlib.test(OTHER,"ProtoField-empty-name-1", success) -- will use abbrev
+
+success = pcall(ProtoField.int8, "test.empty_name_2", "")
+testlib.test(OTHER,"ProtoField-empty-name-2", not success)
+
+-- Signed integer base values, only base.DEC should work
+success = pcall(ProtoField.int8, "test.int.base_none", "int base NONE", base.NONE)
+testlib.test(OTHER,"ProtoField-int-base-none", not success)
+
+success = pcall(ProtoField.int8, "test.int.base_dec", "int base DEC", base.DEC)
+testlib.test(OTHER,"ProtoField-int-base-dec", success)
+
+success = pcall(ProtoField.int8, "test.int.base_hex", "int base HEX", base.HEX)
+testlib.test(OTHER,"ProtoField-int-base-hex", not success)
+
+success = pcall(ProtoField.int8, "test.int.base_oct", "int base OCT", base.OCT)
+testlib.test(OTHER,"ProtoField-int-base-oct", not success)
+
+success = pcall(ProtoField.int8, "test.int.base_dec_hex", "int base DEC_HEX", base.DEC_HEX)
+testlib.test(OTHER,"ProtoField-int-base-dec-hex", not success)
+
+success = pcall(ProtoField.int8, "test.int.base_hex_dec", "int base HEX_DEC", base.HEX_DEC)
+testlib.test(OTHER,"ProtoField-int-base-hex-dec", not success)
+
+-- Passing no table should not work
+success = pcall(ProtoField.uint16, "test.bad0", "Bad0", base.UNIT_STRING)
+testlib.test(OTHER,"ProtoField-unitstring-no-table", not success)
+
+-- Passing an empty table should not work
+success = pcall(ProtoField.uint16, "test.bad1", "Bad1", base.UNIT_STRING, {})
+testlib.test(OTHER,"ProtoField-unitstring-empty-table", not success)
+
+-- Passing userdata should not work
+success = pcall(ProtoField.uint16, "test.bad2", "Bad2", base.UNIT_STRING, {test_proto})
+testlib.test(OTHER,"ProtoField-unitstring-userdata", not success)
+
+-- Too many items are not supported
+success = pcall(ProtoField.uint16, "test.bad3", "Bad3", base.UNIT_STRING, {"too", "many", "items"})
+testlib.test(OTHER,"ProtoField-unitstring-too-many-items", not success)
+
+local numinits = 0
+function test_proto.init()
+ numinits = numinits + 1
+ if numinits == 2 then
+ testlib.getResults()
+ end
+end
+
+-- Test expected text with singular and plural forms
+function test_proto.dissector(tvb, pinfo, tree)
+ local ti
+ testlib.countPacket(FRAME)
+
+ local tvb1 = ByteArray.new("00 00"):tvb("Tvb1")
+ ti = tree:add(test_proto.fields.time_field, tvb1())
+ testlib.test(PER_FRAME,"Time: 0 secs", ti.text == "Time: 0 secs")
+ ti = tree:add(test_proto.fields.dist_field, tvb1())
+ testlib.test(PER_FRAME,"Distance: 0 km", ti.text == "Distance: 0 km")
+
+ local tvb2 = ByteArray.new("00 01"):tvb("Tvb2")
+ ti = tree:add(test_proto.fields.time_field, tvb2())
+ testlib.test(PER_FRAME,"Time: 1 sec", ti.text == "Time: 1 sec")
+ ti = tree:add(test_proto.fields.dist_field, tvb2())
+ testlib.test(PER_FRAME,"Distance: 1 km", ti.text == "Distance: 1 km")
+
+ local tvb3 = ByteArray.new("ff ff"):tvb("Tvb3")
+ ti = tree:add(test_proto.fields.time_field, tvb3())
+ testlib.test(PER_FRAME,"Time: 65535 secs", ti.text == "Time: 65535 secs")
+ ti = tree:add(test_proto.fields.dist_field, tvb3())
+ testlib.test(PER_FRAME,"Distance: 65535 km", ti.text == "Distance: 65535 km")
+
+ ti = tree:add(test_proto.fields.filtered_field, tvb2())
+ -- Note that this file should be loaded in tshark twice. Once with a visible
+ -- tree (-V) and once without a visible tree.
+ if tree.visible then
+ -- Tree is visible so both fields should be referenced
+ testlib.test(PER_FRAME,"Visible tree: Time is referenced", tree:referenced(test_proto.fields.time_field) == true)
+ testlib.test(PER_FRAME,"Visible tree: Filtered field is referenced", tree:referenced(test_proto.fields.filtered_field) == true)
+ else
+ -- Tree is not visible so only the field that appears in a filter should be referenced
+ testlib.test(PER_FRAME,"Invisible tree: Time is NOT referenced", tree:referenced(test_proto.fields.time_field) == false)
+ testlib.test(PER_FRAME,"Invisible tree: Filtered field is referenced", tree:referenced(test_proto.fields.filtered_field) == true)
+ end
+ testlib.pass(FRAME)
+end
+
+DissectorTable.get("udp.port"):add(65333, test_proto)
+DissectorTable.get("udp.port"):add(65346, test_proto)
diff --git a/test/lua/script_args.lua b/test/lua/script_args.lua
new file mode 100644
index 0000000..56ca3fb
--- /dev/null
+++ b/test/lua/script_args.lua
@@ -0,0 +1,24 @@
+----------------------------------------
+-- This just verifies the number of args it got is what it expected.
+-- The first arg should be a number, for how many total args to expect,
+-- including itself.
+
+local testlib = require("testlib")
+
+local ARGS = "args"
+testlib.init({ [ARGS]=3 })
+
+-----------------------------
+
+testlib.testing("Command-line args")
+
+local arg={...} -- get passed-in args
+
+testlib.test(ARGS, "arg1", arg ~= nil and #arg > 0)
+
+local numargs = tonumber(arg[1])
+testlib.test(ARGS, "arg2", numargs ~= nil)
+
+testlib.test(ARGS, "arg3", #arg == numargs)
+
+testlib.getResults()
diff --git a/test/lua/struct.lua b/test/lua/struct.lua
new file mode 100644
index 0000000..bc6a2fb
--- /dev/null
+++ b/test/lua/struct.lua
@@ -0,0 +1,367 @@
+
+-- This is a test script for tshark/wireshark.
+-- This script runs inside tshark/wireshark, so to run it do:
+-- wireshark -X lua_script:<path_to_testdir>/lua/struct.lua
+-- tshark -r bogus.cap -X lua_script:<path_to_testdir>/lua/struct.lua
+
+-- Tests Struct functions
+
+local testlib = require("testlib")
+local OTHER = "other"
+
+--
+-- auxiliary function to print an hexadecimal `dump' of a given string
+-- (not used by the test)
+--
+local function tohex(s, sep)
+ local patt = "%02x" .. (sep or "")
+ s = string.gsub(s, "(.)", function(c)
+ return string.format(patt, string.byte(c))
+ end)
+ if sep then s = s:sub(1,-(sep:len()+1)) end
+ return s
+end
+
+local function bp (s)
+ s = tohex(s)
+ print(s)
+end
+
+
+-----------------------------
+
+print("Lua version: ".._VERSION)
+
+testlib.init({ [OTHER] = 0 })
+
+testlib.testing(OTHER, "Struct library")
+
+local lib = Struct
+testlib.test(OTHER,"global",_G.Struct == lib)
+
+for name, val in pairs(lib) do
+ print("\t"..name.." = "..type(val))
+end
+
+testlib.test(OTHER,"class1",type(lib) == 'table')
+testlib.test(OTHER,"class2",type(lib.pack) == 'function')
+testlib.test(OTHER,"class3",type(lib.unpack) == 'function')
+testlib.test(OTHER,"class4",type(lib.size) == 'function')
+
+
+local val1 = "\42\00\00\00\00\00\00\01\00\00\00\02\00\00\00\03\00\00\00\04"
+local fmt1_le = "<!4biii4i4"
+local fmt1_be = ">!4biii4i4"
+local fmt1_64le = "<!4ieE"
+local fmt1_64be = ">!4ieE"
+local fmt2_be = ">!4bi(ii4)i"
+
+testlib.testing(OTHER, "basic size")
+
+testlib.test(OTHER,"basic_size1", lib.size(fmt1_le) == string.len(val1))
+testlib.test(OTHER,"basic_size2", lib.size(fmt1_le) == Struct.size(fmt1_be))
+testlib.test(OTHER,"basic_size3", lib.size(fmt1_le) == Struct.size(fmt1_64le))
+testlib.test(OTHER,"basic_size4", lib.size(fmt2_be) == Struct.size(fmt1_64le))
+
+testlib.testing(OTHER, "basic values")
+
+testlib.test(OTHER,"basic_values1", lib.values(fmt1_le) == 5)
+testlib.test(OTHER,"basic_values2", lib.values(fmt1_be) == lib.values(fmt1_le))
+testlib.test(OTHER,"basic_values3", lib.values(fmt1_64le) == 3)
+testlib.test(OTHER,"basic_values4", lib.values(fmt2_be) == lib.values(fmt1_64le))
+testlib.test(OTHER,"basic_values4", lib.values(" (I) s x i XxX c0") == 3)
+
+testlib.testing(OTHER, "tohex")
+local val1hex = "2A:00:00:00:00:00:00:01:00:00:00:02:00:00:00:03:00:00:00:04"
+testlib.test(OTHER,"tohex1", Struct.tohex(val1) == tohex(val1):upper())
+testlib.test(OTHER,"tohex2", Struct.tohex(val1,true) == tohex(val1))
+testlib.test(OTHER,"tohex3", Struct.tohex(val1,false,":") == val1hex)
+testlib.test(OTHER,"tohex4", Struct.tohex(val1,true,":") == val1hex:lower())
+
+testlib.testing(OTHER, "fromhex")
+testlib.test(OTHER,"fromhex1", Struct.fromhex(val1hex,":") == val1)
+local val1hex2 = val1hex:gsub(":","")
+testlib.test(OTHER,"fromhex2", Struct.fromhex(val1hex2) == val1)
+testlib.test(OTHER,"fromhex3", Struct.fromhex(val1hex2:lower()) == val1)
+
+testlib.testing(OTHER, "basic unpack")
+local ret1, ret2, ret3, ret4, ret5, pos = lib.unpack(fmt1_le, val1)
+testlib.test(OTHER,"basic_unpack1", ret1 == 42 and ret2 == 0x01000000 and ret3 == 0x02000000 and ret4 == 0x03000000 and ret5 == 0x04000000)
+testlib.test(OTHER,"basic_unpack_position1", pos == string.len(val1) + 1)
+
+ret1, ret2, ret3, ret4, ret5, pos = lib.unpack(fmt1_be, val1)
+testlib.test(OTHER,"basic_unpack2", ret1 == 42 and ret2 == 1 and ret3 == 2 and ret4 == 3 and ret5 == 4)
+testlib.test(OTHER,"basic_unpack_position2", pos == string.len(val1) + 1)
+
+ret1, ret2, ret3, pos = lib.unpack(fmt1_64le, val1)
+testlib.test(OTHER,"basic_unpack3", ret1 == 42 and ret2 == Int64.new( 0x01000000, 0x02000000) and ret3 == UInt64.new( 0x03000000, 0x04000000))
+print(typeof(ret2),typeof(ret3))
+testlib.test(OTHER,"basic_unpack3b", typeof(ret2) == "Int64" and typeof(ret3) == "UInt64")
+testlib.test(OTHER,"basic_unpack_position3", pos == string.len(val1) + 1)
+
+ret1, ret2, ret3, pos = lib.unpack(fmt1_64be, val1)
+testlib.test(OTHER,"basic_unpack4", ret1 == 0x2A000000 and ret2 == Int64.new( 2, 1) and ret3 == UInt64.new( 4, 3))
+testlib.test(OTHER,"basic_unpack4b", typeof(ret2) == "Int64" and typeof(ret3) == "UInt64")
+testlib.test(OTHER,"basic_unpack_position4", pos == string.len(val1) + 1)
+
+ret1, ret2, ret3, pos = lib.unpack(fmt2_be, val1)
+testlib.test(OTHER,"basic_unpack5", ret1 == 42 and ret2 == 1 and ret3 == 4)
+testlib.test(OTHER,"basic_unpack_position5", pos == string.len(val1) + 1)
+
+testlib.testing(OTHER, "basic pack")
+local pval1 = lib.pack(fmt1_le, lib.unpack(fmt1_le, val1))
+testlib.test(OTHER,"basic_pack1", pval1 == val1)
+testlib.test(OTHER,"basic_pack2", val1 == lib.pack(fmt1_be, lib.unpack(fmt1_be, val1)))
+testlib.test(OTHER,"basic_pack3", val1 == lib.pack(fmt1_64le, lib.unpack(fmt1_64le, val1)))
+testlib.test(OTHER,"basic_pack4", val1 == lib.pack(fmt1_64be, lib.unpack(fmt1_64be, val1)))
+testlib.test(OTHER,"basic_pack5", lib.pack(fmt2_be, lib.unpack(fmt1_be, val1)) == lib.pack(">!4biiii", 42, 1, 0, 0, 2))
+
+----------------------------------
+-- following comes from:
+-- https://github.com/LuaDist/struct/blob/master/teststruct.lua
+-- unfortunately many of his tests assumed a local machine word
+-- size of 4 bytes for long and such, so I had to muck with this
+-- to make it handle 64-bit compiles.
+-- $Id: teststruct.lua,v 1.2 2008/04/18 20:06:01 roberto Exp $
+
+
+-- some pack/unpack commands are host-size dependent, so we need to pad
+local l_pad, ln_pad = "",""
+if lib.size("l") == 8 then
+ -- the machine running this script uses a long of 8 bytes
+ l_pad = "\00\00\00\00"
+ ln_pad = "\255\255\255\255"
+end
+
+local a,b,c,d,e,f,x
+
+testlib.testing(OTHER, "pack")
+testlib.test(OTHER,"pack_I",#Struct.pack("I", 67324752) == 4)
+
+testlib.test(OTHER,"pack_b1",lib.pack('b', 10) == string.char(10))
+testlib.test(OTHER,"pack_b2",lib.pack('bbb', 10, 20, 30) == string.char(10, 20, 30))
+
+testlib.test(OTHER,"pack_h1",lib.pack('<h', 10) == string.char(10, 0))
+testlib.test(OTHER,"pack_h2",lib.pack('>h', 10) == string.char(0, 10))
+testlib.test(OTHER,"pack_h3",lib.pack('<h', -10) == string.char(256-10, 256-1))
+
+testlib.test(OTHER,"pack_l1",lib.pack('<l', 10) == string.char(10, 0, 0, 0)..l_pad)
+testlib.test(OTHER,"pack_l2",lib.pack('>l', 10) == l_pad..string.char(0, 0, 0, 10))
+testlib.test(OTHER,"pack_l3",lib.pack('<l', -10) == string.char(256-10, 256-1, 256-1, 256-1)..ln_pad)
+
+testlib.testing(OTHER, "unpack")
+testlib.test(OTHER,"unpack_h1",lib.unpack('<h', string.char(10, 0)) == 10)
+testlib.test(OTHER,"unpack_h2",lib.unpack('>h', string.char(0, 10)) == 10)
+testlib.test(OTHER,"unpack_h3",lib.unpack('<h', string.char(256-10, 256-1)) == -10)
+
+testlib.test(OTHER,"unpack_l1",lib.unpack('<l', string.char(10, 0, 0, 1)..l_pad) == 10 + 2^(3*8))
+testlib.test(OTHER,"unpack_l2",lib.unpack('>l', l_pad..string.char(0, 1, 0, 10)) == 10 + 2^(2*8))
+testlib.test(OTHER,"unpack_l3",lib.unpack('<l', string.char(256-10, 256-1, 256-1, 256-1)..ln_pad) == -10)
+
+-- limits
+lims = {{'B', 255}, {'b', 127}, {'b', -128},
+ {'I1', 255}, {'i1', 127}, {'i1', -128},
+ {'H', 2^16 - 1}, {'h', 2^15 - 1}, {'h', -2^15},
+ {'I2', 2^16 - 1}, {'i2', 2^15 - 1}, {'i2', -2^15},
+ {'L', 2^32 - 1}, {'l', 2^31 - 1}, {'l', -2^31},
+ {'I4', 2^32 - 1}, {'i4', 2^31 - 1}, {'i4', -2^31},
+ }
+
+for _, a in pairs{'', '>', '<'} do
+ local i = 1
+ for _, l in pairs(lims) do
+ local fmt = a .. l[1]
+ testlib.test(OTHER,"limit"..i.."("..l[1]..")", lib.unpack(fmt, lib.pack(fmt, l[2])) == l[2])
+ i = i + 1
+ end
+end
+
+
+testlib.testing(OTHER, "fixed-sized ints")
+-- tests for fixed-sized ints
+local num = 1
+for _, i in pairs{1,2,4} do
+ x = lib.pack('<i'..i, -3)
+ testlib.test(OTHER,"pack_fixedlen"..num, string.len(x) == i)
+ testlib.test(OTHER,"pack_fixed"..num, x == string.char(256-3) .. string.rep(string.char(256-1), i-1))
+ testlib.test(OTHER,"unpack_fixed"..num, lib.unpack('<i'..i, x) == -3)
+ num = num + 1
+end
+
+
+testlib.testing(OTHER, "alignment")
+-- alignment
+d = lib.pack("d", 5.1)
+ali = {[1] = string.char(1)..d,
+ [2] = string.char(1, 0)..d,
+ [4] = string.char(1, 0, 0, 0)..d,
+ [8] = string.char(1, 0, 0, 0, 0, 0, 0, 0)..d,
+ }
+
+num = 1
+for a,r in pairs(ali) do
+ testlib.test(OTHER,"pack_align"..num, lib.pack("!"..a.."bd", 1, 5.1) == r)
+ local x,y = lib.unpack("!"..a.."bd", r)
+ testlib.test(OTHER,"unpack_align"..num, x == 1 and y == 5.1)
+ num = num + 1
+end
+
+
+testlib.testing(OTHER, "string")
+-- strings
+testlib.test(OTHER,"string_pack1",lib.pack("c", "alo alo") == "a")
+testlib.test(OTHER,"string_pack2",lib.pack("c4", "alo alo") == "alo ")
+testlib.test(OTHER,"string_pack3",lib.pack("c5", "alo alo") == "alo a")
+testlib.test(OTHER,"string_pack4",lib.pack("!4b>c7", 1, "alo alo") == "\1alo alo")
+testlib.test(OTHER,"string_pack5",lib.pack("!2<s", "alo alo") == "alo alo\0")
+testlib.test(OTHER,"string_pack6",lib.pack(" c0 ", "alo alo") == "alo alo")
+num = 1
+for _, f in pairs{"B", "l", "i2", "f", "d"} do
+ for _, s in pairs{"", "a", "alo", string.rep("x", 200)} do
+ local x = lib.pack(f.."c0", #s, s)
+ testlib.test(OTHER,"string_unpack"..num, lib.unpack(f.."c0", x) == s)
+ num = num + 1
+ end
+end
+
+
+testlib.testing(OTHER, "indeces")
+-- indices
+x = lib.pack("!>iiiii", 1, 2, 3, 4, 5)
+local i = 1
+local k = 1
+num = 1
+while i < #x do
+ local v, j = lib.unpack("!>i", x, i)
+ testlib.test(OTHER,"index_unpack"..num, j == i + 4 and v == k)
+ i = j; k = k + 1
+ num = num + 1
+end
+
+testlib.testing(OTHER, "absolute")
+-- alignments are relative to 'absolute' positions
+x = lib.pack("!8 xd", 12)
+testlib.test(OTHER,"absolute_unpack1",lib.unpack("!8d", x, 3) == 12)
+
+
+testlib.test(OTHER,"absolute_pack1",lib.pack("<lhbxxH", -2, 10, -10, 250) ==
+ string.char(254, 255, 255, 255) ..ln_pad.. string.char(10, 0, 246, 0, 0, 250, 0))
+
+a,b,c,d = lib.unpack("<lhbxxH",
+ string.char(254, 255, 255, 255) ..ln_pad.. string.char(10, 0, 246, 0, 0, 250, 0))
+testlib.test(OTHER,"absolute_unpack2",a == -2 and b == 10 and c == -10 and d == 250)
+
+testlib.test(OTHER,"absolute_pack2",lib.pack(">lBxxH", -20, 10, 250) ==
+ ln_pad..string.char(255, 255, 255, 236, 10, 0, 0, 0, 250))
+
+
+testlib.testing(OTHER, "position")
+
+a, b, c, d = lib.unpack(">lBxxH",
+ ln_pad..string.char(255, 255, 255, 236, 10, 0, 0, 0, 250))
+-- the 'd' return val is position in string, so will depend on size of long 'l'
+local vald = 10 + string.len(l_pad)
+testlib.test(OTHER,"position_unpack1",a == -20 and b == 10 and c == 250 and d == vald)
+
+a,b,c,d,e = lib.unpack(">fdfH",
+ '000'..lib.pack(">fdfH", 3.5, -24e-5, 200.5, 30000),
+ 4)
+testlib.test(OTHER,"position_unpack2",a == 3.5 and b == -24e-5 and c == 200.5 and d == 30000 and e == 22)
+
+a,b,c,d,e = lib.unpack("<fdxxfH",
+ '000'..lib.pack("<fdxxfH", -13.5, 24e5, 200.5, 300),
+ 4)
+testlib.test(OTHER,"position_unpack3",a == -13.5 and b == 24e5 and c == 200.5 and d == 300 and e == 24)
+
+x = lib.pack(">I2fi4I2", 10, 20, -30, 40001)
+testlib.test(OTHER,"position_pack1",string.len(x) == 2+4+4+2)
+testlib.test(OTHER,"position_unpack4",lib.unpack(">f", x, 3) == 20)
+a,b,c,d = lib.unpack(">i2fi4I2", x)
+testlib.test(OTHER,"position_unpack5",a == 10 and b == 20 and c == -30 and d == 40001)
+
+testlib.testing(OTHER, "string length")
+local s = "hello hello"
+x = lib.pack(" b c0 ", string.len(s), s)
+testlib.test(OTHER,"stringlen_unpack1",lib.unpack("bc0", x) == s)
+x = lib.pack("Lc0", string.len(s), s)
+testlib.test(OTHER,"stringlen_unpack2",lib.unpack(" L c0 ", x) == s)
+x = lib.pack("cc3b", s, s, 0)
+testlib.test(OTHER,"stringlen_pack1",x == "hhel\0")
+testlib.test(OTHER,"stringlen_unpack3",lib.unpack("xxxxb", x) == 0)
+
+testlib.testing(OTHER, "padding")
+testlib.test(OTHER,"padding_pack1",lib.pack("<!l", 3) == string.char(3, 0, 0, 0)..l_pad)
+testlib.test(OTHER,"padding_pack2",lib.pack("<!xl", 3) == l_pad..string.char(0, 0, 0, 0, 3, 0, 0, 0)..l_pad)
+testlib.test(OTHER,"padding_pack3",lib.pack("<!xxl", 3) == l_pad..string.char(0, 0, 0, 0, 3, 0, 0, 0)..l_pad)
+testlib.test(OTHER,"padding_pack4",lib.pack("<!xxxl", 3) == l_pad..string.char(0, 0, 0, 0, 3, 0, 0, 0)..l_pad)
+
+testlib.test(OTHER,"padding_unpack1",lib.unpack("<!l", string.char(3, 0, 0, 0)..l_pad) == 3)
+testlib.test(OTHER,"padding_unpack2",lib.unpack("<!xl", l_pad..string.char(0, 0, 0, 0, 3, 0, 0, 0)..l_pad) == 3)
+testlib.test(OTHER,"padding_unpack3",lib.unpack("<!xxl", l_pad..string.char(0, 0, 0, 0, 3, 0, 0, 0)..l_pad) == 3)
+testlib.test(OTHER,"padding_unpack4",lib.unpack("<!xxxl", l_pad..string.char(0, 0, 0, 0, 3, 0, 0, 0)..l_pad) == 3)
+
+testlib.testing(OTHER, "format")
+testlib.test(OTHER,"format_pack1",lib.pack("<!2 b l h", 2, 3, 5) == string.char(2, 0, 3, 0)..l_pad..string.char(0, 0, 5, 0))
+a,b,c = lib.unpack("<!2blh", string.char(2, 0, 3, 0)..l_pad..string.char(0, 0, 5, 0))
+testlib.test(OTHER,"format_pack2",a == 2 and b == 3 and c == 5)
+
+testlib.test(OTHER,"format_pack3",lib.pack("<!8blh", 2, 3, 5) == string.char(2, 0, 0, 0)..l_pad..string.char(3, 0, 0, 0)..l_pad..string.char(5, 0))
+
+a,b,c = lib.unpack("<!8blh", string.char(2, 0, 0, 0)..l_pad..string.char(3, 0, 0, 0)..l_pad..string.char(5, 0))
+testlib.test(OTHER,"format_pack4",a == 2 and b == 3 and c == 5)
+
+testlib.test(OTHER,"format_pack5",lib.pack(">sh", "aloi", 3) == "aloi\0\0\3")
+testlib.test(OTHER,"format_pack6",lib.pack(">!sh", "aloi", 3) == "aloi\0\0\0\3")
+
+x = "aloi\0\0\0\0\3\2\0\0"
+a, b, c = lib.unpack("<!si4", x)
+testlib.test(OTHER,"format_unpack1",a == "aloi" and b == 2*256+3 and c == string.len(x)+1)
+
+x = lib.pack("!4sss", "hi", "hello", "bye")
+a,b,c = lib.unpack("sss", x)
+testlib.test(OTHER,"format_unpack2",a == "hi" and b == "hello" and c == "bye")
+a, i = lib.unpack("s", x, 1)
+testlib.test(OTHER,"format_unpack3",a == "hi")
+a, i = lib.unpack("s", x, i)
+testlib.test(OTHER,"format_unpack4",a == "hello")
+a, i = lib.unpack("s", x, i)
+testlib.test(OTHER,"format_unpack5",a == "bye")
+
+
+
+-- test for weird conditions
+testlib.testing(OTHER, "weird conditions")
+testlib.test(OTHER,"weird_pack1",lib.pack(">>>h <!!!<h", 10, 10) == string.char(0, 10, 10, 0))
+testlib.test(OTHER,"weird_pack2",not pcall(lib.pack, "!3l", 10))
+testlib.test(OTHER,"weird_pack3",not pcall(lib.pack, "3", 10))
+testlib.test(OTHER,"weird_pack4",not pcall(lib.pack, "i33", 10))
+testlib.test(OTHER,"weird_pack5",not pcall(lib.pack, "I33", 10))
+testlib.test(OTHER,"weird_pack6",lib.pack("") == "")
+testlib.test(OTHER,"weird_pack7",lib.pack(" ") == "")
+testlib.test(OTHER,"weird_pack8",lib.pack(">>><<<!!") == "")
+testlib.test(OTHER,"weird_unpack1",not pcall(lib.unpack, "c0", "alo"))
+testlib.test(OTHER,"weird_unpack2",not pcall(lib.unpack, "s", "alo"))
+testlib.test(OTHER,"weird_unpack3",lib.unpack("s", "alo\0") == "alo")
+testlib.test(OTHER,"weird_pack9",not pcall(lib.pack, "c4", "alo"))
+testlib.test(OTHER,"weird_pack10",pcall(lib.pack, "c3", "alo"))
+testlib.test(OTHER,"weird_unpack4",not pcall(lib.unpack, "c4", "alo"))
+testlib.test(OTHER,"weird_unpack5",pcall(lib.unpack, "c3", "alo"))
+testlib.test(OTHER,"weird_unpack6",not pcall(lib.unpack, "bc0", "\4alo"))
+testlib.test(OTHER,"weird_unpack7",pcall(lib.unpack, "bc0", "\3alo"))
+
+testlib.test(OTHER,"weird_unpack8",not pcall(lib.unpack, "b", "alo", 4))
+testlib.test(OTHER,"weird_unpack9",lib.unpack("b", "alo\3", 4) == 3)
+
+testlib.test(OTHER,"weird_pack11",not pcall(lib.pack, "\250\22", "alo"))
+testlib.test(OTHER,"weird_pack12",not pcall(lib.pack, 1, "alo"))
+testlib.test(OTHER,"weird_pack13",not pcall(lib.pack, nil, "alo"))
+testlib.test(OTHER,"weird_pack14",not pcall(lib.pack, {}, "alo"))
+testlib.test(OTHER,"weird_pack15",not pcall(lib.pack, true, "alo"))
+testlib.test(OTHER,"weird_unpack10",not pcall(lib.unpack, "\250\22", "\3alo"))
+testlib.test(OTHER,"weird_unpack11",not pcall(lib.unpack, 1, "\3alo"))
+testlib.test(OTHER,"weird_unpack12",not pcall(lib.unpack, nil, "\3alo"))
+testlib.test(OTHER,"weird_unpack13",not pcall(lib.unpack, {}, "\3alo"))
+testlib.test(OTHER,"weird_unpack14",not pcall(lib.unpack, true, "\3alo"))
+
+-- done
+testlib.getResults()
diff --git a/test/lua/testlib.lua b/test/lua/testlib.lua
new file mode 100644
index 0000000..e002c8b
--- /dev/null
+++ b/test/lua/testlib.lua
@@ -0,0 +1,174 @@
+----------------------------------------
+-- library name: testlib.lua
+--
+-- Provides common functions for other lua test scripts to use.
+----------------------------------------
+--[[
+ This library aims to codify the most common practices used in testing
+ Wireshark's lua features. The intent is to reduce boilerplate code
+ so test scripts can focus on test cases.
+
+ Tests are nominally classified into named groups.
+ (In practice, most test files just use a single group called "other",
+ but this should be tidied up at some point.)
+ A test script must call testlib.init() with a table of
+ group names and the number of tests expected to be run in each group.
+ This number can be zero if you want to declare a group but don't
+ need to check that a specific number of tests is run.
+
+ Suggested use (abridged):
+
+ local testlib = require("testlib")
+ testlib.init({ other = 3 })
+ testlib.testing("other", "example tests")
+ testlib.test("other", "firsttest", 1+1 == 2)
+ testlib.test("other", "funccall", pcall(my_function, func_args), "function should succeed")
+ testlib.test("other", "funccall", not pcall(my_function2, func_args), "function expected to give error")
+ testlib.getResults()
+
+ For information on specific functions, keep reading.
+--]]
+
+----------------------------------------
+-- This is the module object, which will be returned at the end of this file.
+local M = {
+ ["groups"] = {},
+}
+
+----------------------------------------
+-- Initialize the test suite. Define one or more testing groups,
+-- giving the expected number of tests to run for each.
+-- (Telling it to "expect" zero tests for a group just skips
+-- the check that a specific number of tests ran in that group.)
+-- May be called repeatedly if you want to define group names
+-- at runtime.
+M.init = function(t)
+ for group, expected in pairs(t) do
+ M.groups[group] = {
+ ["expected"] = expected,
+ ["passed"] = 0,
+ ["failed"] = 0,
+ ["total"] = 0,
+ ["packets"] = 0,
+ }
+ end
+end
+
+----------------------------------------
+-- Indicate a passed test in the named group.
+M.pass = function(group)
+ M.groups[group].passed = M.groups[group].passed + 1
+ M.groups[group].total = M.groups[group].total + 1
+end
+
+----------------------------------------
+-- Indicate a failed test in the named group.
+M.fail = function(group)
+ M.groups[group].failed = M.groups[group].failed + 1
+ M.groups[group].total = M.groups[group].total + 1
+end
+
+----------------------------------------
+-- There are some tests which track the number of packets they're testing.
+-- Use this function to count a single packet as being "seen" by a group.
+M.countPacket = function(group)
+ M.groups[group].packets = M.groups[group].packets + 1
+end
+
+----------------------------------------
+-- Get the number of packets that have been counted under the named group.
+M.getPktCount = function(group)
+ return M.groups[group].packets
+end
+
+----------------------------------------
+-- Print a banner reporting test progress.
+-- Has no material affect on test progression, but is useful for
+-- understanding the test results.
+M.testing = function(group, msg)
+ if msg == nil then
+ msg, group = group, nil
+ end
+ if group then
+ if M.groups[group].packets > 0 then
+ print(string.format("\n-------- Testing %s -- %s for packet # %d --------\n",
+ group, msg, M.groups[group].packets))
+ else
+ print(string.format("\n-------- Testing %s -- %s --------\n",
+ group, msg))
+ end
+ else
+ print(string.format("\n-------- Testing %s --------\n", msg))
+ end
+end
+
+----------------------------------------
+-- Core function: test a condition, report and track its status.
+-- The output format shown here is what was commonly used in test scripts,
+-- but can be changed.
+M.test = function(group, name, cond, msg)
+ -- io.stdout:write() doesn't add a newline like print() does
+ io.stdout:write(string.format("test %s --> %s-%d-%d...",
+ group, name, M.groups[group].total, M.groups[group].packets))
+ if cond then
+ io.stdout:write("passed\n")
+ M.pass(group)
+ return true
+ else
+ io.stdout:write("failed!\n")
+ M.fail(group)
+ if msg then
+ print(string.format("Got the following error: '%s'", msg))
+ end
+ -- Using error() causes the entire test script to abort.
+ -- This is how the lua test suite typically operates.
+ -- If a test script wants to continue with subsequent tests
+ -- after a failed test, this behaviour could be made
+ -- configurable in this module.
+ error(name .. " test failed!")
+ return false
+ end
+end
+
+----------------------------------------
+-- Call this at the finale of a test script to output the results of testing.
+-- This is where the number of tests run is compared to what was expected,
+-- if applicable.
+-- Scripts which run over empty.pcap will usually call this at the end of
+-- the file.
+-- Scripts which test by creating a protocol object will call this from
+-- the object's .init() method *the second time it is called*.
+-- Others usually call it in a tap listener's .draw() method,
+-- which tshark calls once when it reaches the end of the pcap.
+M.getResults = function()
+ local rv = true
+ print("\n===== Test Results =====")
+ for group, num in pairs(M.groups) do
+ if num.expected > 0 and num.total ~= num.expected then
+ rv = false
+ print("Something didn't run or ran too much... tests failed!")
+ print(string.format("%s: expected %d tests but ran %d tests",
+ group, num.expected, num.total))
+ end
+ if num.failed > 0 then
+ rv = false
+ print(string.format("%s: passed %d/%d, FAILED %d/%d",
+ group, num.passed, num.total, num.failed, num.total))
+ else
+ print(string.format("%s: passed %d/%d",
+ group, num.passed, num.total))
+ end
+ end
+ if rv then
+ -- The python wrapper which performs our lua testing
+ -- expects to see this string in the output if there were no failures.
+ print("All tests passed!")
+ else
+ print("Some tests failed!")
+ end
+ return rv
+end
+
+----------------------------------------
+-- That's the end of this library. Return the module we've created.
+return M
diff --git a/test/lua/try_heuristics.lua b/test/lua/try_heuristics.lua
new file mode 100644
index 0000000..fcd6d09
--- /dev/null
+++ b/test/lua/try_heuristics.lua
@@ -0,0 +1,61 @@
+-- Define a new protocol that runs TCP heuristics and on failure runs UDP heuristics
+--
+-- This expects to be run against dns_port.pcap, so it should end up resolving all packets to DNS with the UDP heuristic
+local test_proto = Proto("test", "Test Protocol")
+
+-- Have all tests passed so far?
+-- Anything that fails should set this to false, which will suppress the "".
+all_ok = true
+
+-- The number of frames expected
+-- Final test status is output with last frame
+LAST_FRAME = 4
+
+function test_proto.dissector(buf, pinfo, root)
+ print("Dissector function run")
+
+ orig_proto_name = tostring(pinfo.cols.protocol)
+
+ -- Run TCP heuristic dissectors
+ -- Dissection should fail, and the protocol name should be unchanged
+ tcp_success = DissectorTable.try_heuristics("tcp", buf, pinfo, root)
+ curr_proto_name = tostring(pinfo.cols.protocol)
+
+ if tcp_success then
+ all_ok = false
+ print("tcp heuristics were not expected to report success, but did!")
+ end
+
+ if curr_proto_name ~= orig_proto_name then
+ all_ok = false
+ print("after tcp heuristics were run, protocol " .. orig_proto_name .. " was not expected to change, but became " .. curr_proto_name .. "!")
+ end
+
+ -- Run UDP heuristic dissectors
+ -- Dissection should succeed, and the protocol name should be changed to DNS
+ udp_success = DissectorTable.try_heuristics("udp", buf, pinfo, root)
+ curr_proto_name = tostring(pinfo.cols.protocol)
+
+ if not udp_success then
+ all_ok = false
+ print("udp heuristics were expected to report success, but did not!")
+ end
+
+ if curr_proto_name ~= "DNS" then
+ all_ok = false
+ print("after udp heuristics were run, protocol should be changed to DNS, but became " .. curr_proto_name .. "!")
+ end
+
+ -- If we're on the last frame, report success or failure
+ if pinfo.number == LAST_FRAME then
+ if all_ok then
+ print("All tests passed!")
+ else
+ print("Some tests failed!")
+ end
+ end
+end
+
+-- Invoke test_proto on the expected UDP traffic
+DissectorTable.get("udp.port"):add(65333, test_proto)
+DissectorTable.get("udp.port"):add(65346, test_proto)
diff --git a/test/lua/tvb.lua b/test/lua/tvb.lua
new file mode 100644
index 0000000..baf702c
--- /dev/null
+++ b/test/lua/tvb.lua
@@ -0,0 +1,922 @@
+----------------------------------------
+-- script-name: tvb.lua
+-- This tests the Tvb/TvbRange and proto_add_XXX_item API.
+----------------------------------------
+local testlib = require("testlib")
+
+local FRAME = "frame"
+local OTHER = "other"
+
+-- expected number of runs per type
+--
+-- CHANGE THIS TO MATCH HOW MANY TESTS THERE ARE
+--
+-- The number of tests in a specific category (other than FRAME) is the
+-- number of times execute() is called by any function below testing().
+-- From the user's perspective, it can be calculated with the following
+-- formula:
+--
+-- N = number of execute() you call +
+-- number of verifyFields() * (1 + number of fields) +
+-- number of verifyResults() * (1 + 2 * number of values)
+--
+-- if one happens to know the number of fields and the number of values.
+--
+local n_frames = 1
+local taptests = { [FRAME]=n_frames, [OTHER]=391*n_frames }
+
+testlib.init(taptests)
+
+------------- test script ------------
+
+----------------------------------------
+-- creates a Proto object for our testing
+local test_proto = Proto("test","Test Protocol")
+
+local numinits = 0
+function test_proto.init()
+ numinits = numinits + 1
+ if numinits == 2 then
+ testlib.getResults()
+ end
+end
+
+
+----------------------------------------
+-- a table of all of our Protocol's fields
+range_string = {
+ { 0, 200, "The first part" },
+ { 201, 233, "The second part" },
+ { 234, 255, "The last part" },
+}
+
+local testfield =
+{
+ basic =
+ {
+ STRING = ProtoField.string ("test.basic.string", "Basic string"),
+ BOOLEAN = ProtoField.bool ("test.basic.boolean", "Basic boolean", 16, {"yes","no"}, 0x0001),
+ UINT8 = ProtoField.uint8 ("test.basic.uint8", "Basic uint8 with range string", base.RANGE_STRING, range_string ),
+ UINT16 = ProtoField.uint16 ("test.basic.uint16", "Basic uint16"),
+ UINT32 = ProtoField.uint32 ("test.basic.uint32", "Basic uint32 test with a unit string", base.UINT_STRING, { "femtoFarads" }),
+ INT24 = ProtoField.int24 ("test.basic.uint24", "Basic uint24"),
+ BYTES = ProtoField.bytes ("test.basic.bytes", "Basic Bytes"),
+ UINT_BYTES = ProtoField.ubytes ("test.basic.ubytes", "Basic Uint Bytes"),
+ OID = ProtoField.oid ("test.basic.oid", "Basic OID"),
+ REL_OID = ProtoField.rel_oid("test.basic.rel_oid", "Basic Relative OID"),
+ ABSOLUTE_LOCAL = ProtoField.absolute_time("test.basic.absolute.local","Basic absolute local"),
+ ABSOLUTE_UTC = ProtoField.absolute_time("test.basic.absolute.utc", "Basic absolute utc", base.UTC),
+ IPv4 = ProtoField.ipv4 ("test.basic.ipv4", "Basic ipv4 address"),
+ IPv6 = ProtoField.ipv6 ("test.basic.ipv6", "Basic ipv6 address"),
+ ETHER = ProtoField.ether ("test.basic.ether", "Basic ethernet address"),
+ -- GUID = ProtoField.guid ("test.basic.guid", "Basic GUID"),
+ },
+
+ time =
+ {
+ ABSOLUTE_LOCAL = ProtoField.absolute_time("test.time.absolute.local","Time absolute local"),
+ ABSOLUTE_UTC = ProtoField.absolute_time("test.time.absolute.utc", "Time absolute utc", base.UTC),
+ },
+
+ bytes =
+ {
+ BYTES = ProtoField.bytes ("test.bytes.bytes", "Bytes"),
+ UINT_BYTES = ProtoField.ubytes ("test.bytes.ubytes", "Uint Bytes"),
+ OID = ProtoField.oid ("test.bytes.oid", "OID"),
+ REL_OID = ProtoField.rel_oid("test.bytes.rel_oid", "Relative OID"),
+ -- GUID = ProtoField.guid ("test.bytes.guid", "GUID"),
+ },
+}
+
+-- create a flat array table of the above that can be registered
+local pfields = {}
+for _,t in pairs(testfield) do
+ for k,v in pairs(t) do
+ pfields[#pfields+1] = v
+ end
+end
+
+-- register them
+test_proto.fields = pfields
+
+print("test_proto ProtoFields registered")
+
+
+local getfield =
+{
+ basic =
+ {
+ STRING = Field.new ("test.basic.string"),
+ BOOLEAN = Field.new ("test.basic.boolean"),
+ UINT8 = Field.new ("test.basic.uint8"),
+ UINT16 = Field.new ("test.basic.uint16"),
+ INT24 = Field.new ("test.basic.uint24"),
+ BYTES = Field.new ("test.basic.bytes"),
+ UINT_BYTES = Field.new ("test.basic.ubytes"),
+ OID = Field.new ("test.basic.oid"),
+ REL_OID = Field.new ("test.basic.rel_oid"),
+ ABSOLUTE_LOCAL = Field.new ("test.basic.absolute.local"),
+ ABSOLUTE_UTC = Field.new ("test.basic.absolute.utc"),
+ IPv4 = Field.new ("test.basic.ipv4"),
+ IPv6 = Field.new ("test.basic.ipv6"),
+ ETHER = Field.new ("test.basic.ether"),
+ -- GUID = Field.new ("test.basic.guid"),
+ },
+
+ time =
+ {
+ ABSOLUTE_LOCAL = Field.new ("test.time.absolute.local"),
+ ABSOLUTE_UTC = Field.new ("test.time.absolute.utc"),
+ },
+
+ bytes =
+ {
+ BYTES = Field.new ("test.bytes.bytes"),
+ UINT_BYTES = Field.new ("test.bytes.ubytes"),
+ OID = Field.new ("test.bytes.oid"),
+ REL_OID = Field.new ("test.bytes.rel_oid"),
+ -- GUID = Field.new ("test.bytes.guid"),
+ },
+}
+
+print("test_proto Fields created")
+
+local function addMatchFields(match_fields, ... )
+ match_fields[#match_fields + 1] = { ... }
+end
+
+local function getFieldInfos(name)
+ local base, field = name:match("([^.]+)%.(.+)")
+ if not base or not field then
+ error("failed to get base.field from '" .. name .. "'")
+ end
+ local t = { getfield[base][field]() }
+ return t
+end
+
+local function verifyFields(name, match_fields)
+ local finfos = getFieldInfos(name)
+
+ testlib.test(OTHER, "verify-fields-size-" .. name, #finfos == #match_fields,
+ "#finfos=" .. #finfos .. ", #match_fields=" .. #match_fields)
+
+ for i, t in ipairs(match_fields) do
+ if type(t) ~= 'table' then
+ error("verifyFields didn't get a table inside the matches table")
+ end
+ if #t ~= 1 then
+ error("verifyFields matches table's table is not size 1")
+ end
+ local result = finfos[i]()
+ local value = t[1]
+ print(
+ name .. " got:",
+ "\n\tfinfos [" .. i .. "]='" .. tostring( result ) .. "'",
+ "\n\tmatches[" .. i .. "]='" .. tostring( value ) .. "'"
+ )
+ testlib.test(OTHER, "verify-fields-value-" .. name .. "-" .. i, result == value )
+ end
+end
+
+
+local function addMatchValues(match_values, ... )
+ match_values[#match_values + 1] = { ... }
+end
+
+local function addMatchFieldValues(match_fields, match_values, match_both, ...)
+ addMatchFields(match_fields, match_both)
+ addMatchValues(match_values, match_both, ...)
+end
+
+local result_values = {}
+local function resetResults()
+ result_values = {}
+end
+
+local function treeAddPField(...)
+ local t = { pcall ( TreeItem.add_packet_field, ... ) }
+ if t[1] == nil then
+ return nil, t[2]
+ end
+ -- it gives back a TreeItem, then the results
+ if typeof(t[2]) ~= 'TreeItem' then
+ return nil, "did not get a TreeItem returned from TreeItem.add_packet_field, "..
+ "got a '" .. typeof(t[2]) .."'"
+ end
+
+ if #t ~= 4 then
+ return nil, "did not get 3 return values from TreeItem.add_packet_field"
+ end
+
+ result_values[#result_values + 1] = { t[3], t[4] }
+
+ return true
+end
+
+local function verifyResults(name, match_values)
+ testlib.test(OTHER, "verify-results-size-" .. name, #result_values == #match_values,
+ "#result_values=" .. #result_values ..
+ ", #match_values=" .. #match_values)
+
+ for j, t in ipairs(match_values) do
+ if type(t) ~= 'table' then
+ error("verifyResults didn't get a table inside the matches table")
+ end
+ for i, match in ipairs(t) do
+ local r = result_values[j][i]
+ print(
+ name .. " got:",
+ "\n\tresults[" .. j .. "][" .. i .. "]='" .. tostring( r ) .. "'",
+ "\n\tmatches[" .. j .. "][" .. i .. "]='" .. tostring( match ) .. "'"
+ )
+ local result_type, match_type
+ if type(match) == 'userdata' then
+ match_type = typeof(match)
+ else
+ match_type = type(match)
+ end
+ if type(r) == 'userdata' then
+ result_type = typeof(r)
+ else
+ result_type = type(r)
+ end
+ testlib.test(OTHER, "verify-results-type-" .. name .. "-" .. i, result_type == match_type )
+ testlib.test(OTHER, "verify-results-value-" .. name .. "-" .. i, r == match )
+ end
+ end
+end
+
+-- Compute the difference in seconds between local time and UTC
+-- from http://lua-users.org/wiki/TimeZone
+local function get_timezone()
+ local now = os.time()
+ return os.difftime(now, os.time(os.date("!*t", now)))
+end
+local timezone = get_timezone()
+print ("timezone = " .. timezone)
+
+----------------------------------------
+-- The following creates the callback function for the dissector.
+-- The 'tvbuf' is a Tvb object, 'pktinfo' is a Pinfo object, and 'root' is a TreeItem object.
+function test_proto.dissector(tvbuf,pktinfo,root)
+
+ testlib.countPacket(FRAME)
+ testlib.countPacket(OTHER)
+
+ testlib.testing(OTHER, "Basic string")
+
+ local tree = root:add(test_proto, tvbuf:range(0,tvbuf:len()))
+
+ -- create a fake Tvb to use for testing
+ local teststring = "this is the string for the first test"
+ local bytearray = ByteArray.new(teststring, true)
+ local tvb_string = bytearray:tvb("Basic string")
+
+ local function callTreeAdd(tree,...)
+ tree:add(...)
+ end
+
+ local string_match_fields = {}
+
+ testlib.test(OTHER, "basic-tvb_get_string", tvb_string:range():string() == teststring )
+
+ testlib.test(OTHER, "basic-string", tree:add(testfield.basic.STRING, tvb_string:range(0,tvb_string:len())) ~= nil )
+ addMatchFields(string_match_fields, teststring)
+
+ testlib.test(OTHER, "basic-string", pcall (callTreeAdd, tree, testfield.basic.STRING, tvb_string:range() ) )
+ addMatchFields(string_match_fields, teststring)
+
+ verifyFields("basic.STRING", string_match_fields)
+
+----------------------------------------
+ testlib.testing(OTHER, "Basic boolean")
+
+ local barray_bytes_hex = "00FF00018000"
+ local barray_bytes = ByteArray.new(barray_bytes_hex)
+ local tvb_bytes = barray_bytes:tvb("Basic bytes")
+ local bool_match_fields = {}
+
+ testlib.test(OTHER, "basic-boolean", pcall (callTreeAdd, tree, testfield.basic.BOOLEAN, tvb_bytes:range(0,2)) )
+ addMatchFields(bool_match_fields, true)
+
+ testlib.test(OTHER, "basic-boolean", pcall (callTreeAdd, tree, testfield.basic.BOOLEAN, tvb_bytes:range(2,2)) )
+ addMatchFields(bool_match_fields, true)
+
+ testlib.test(OTHER, "basic-boolean", pcall (callTreeAdd, tree, testfield.basic.BOOLEAN, tvb_bytes:range(4,2)) )
+ addMatchFields(bool_match_fields, false)
+
+ verifyFields("basic.BOOLEAN", bool_match_fields )
+
+----------------------------------------
+ testlib.testing(OTHER, "Basic uint16")
+
+ local uint16_match_fields = {}
+
+ testlib.test(OTHER, "basic-uint16", pcall (callTreeAdd, tree, testfield.basic.UINT16, tvb_bytes:range(0,2)) )
+ addMatchFields(uint16_match_fields, 255)
+
+ testlib.test(OTHER, "basic-uint16", pcall (callTreeAdd, tree, testfield.basic.UINT16, tvb_bytes:range(2,2)) )
+ addMatchFields(uint16_match_fields, 1)
+
+ testlib.test(OTHER, "basic-uint16", pcall (callTreeAdd, tree, testfield.basic.UINT16, tvb_bytes:range(4,2)) )
+ addMatchFields(uint16_match_fields, 32768)
+
+ verifyFields("basic.UINT16", uint16_match_fields)
+
+----------------------------------------
+ testlib.testing(OTHER, "Basic uint16-le")
+
+ local function callTreeAddLE(tree,...)
+ tree:add_le(...)
+ end
+
+ testlib.test(OTHER, "basic-uint16-le", pcall (callTreeAddLE, tree, testfield.basic.UINT16, tvb_bytes:range(0,2)) )
+ addMatchFields(uint16_match_fields, 65280)
+
+ testlib.test(OTHER, "basic-uint16-le", pcall (callTreeAddLE, tree, testfield.basic.UINT16, tvb_bytes:range(2,2)) )
+ addMatchFields(uint16_match_fields, 256)
+
+ testlib.test(OTHER, "basic-uint16-le", pcall (callTreeAddLE, tree, testfield.basic.UINT16, tvb_bytes:range(4,2)) )
+ addMatchFields(uint16_match_fields, 128)
+
+ verifyFields("basic.UINT16", uint16_match_fields)
+
+----------------------------------------
+ testlib.testing(OTHER, "Basic int24")
+
+ local int24_match_fields = {}
+
+ testlib.test(OTHER, "basic-int24", pcall (callTreeAdd, tree, testfield.basic.INT24, tvb_bytes:range(0,3)) )
+ addMatchFields(int24_match_fields, 65280)
+
+ testlib.test(OTHER, "basic-int24", pcall (callTreeAdd, tree, testfield.basic.INT24, tvb_bytes:range(3,3)) )
+ addMatchFields(int24_match_fields, 98304)
+
+ verifyFields("basic.INT24", int24_match_fields)
+
+----------------------------------------
+ testlib.testing(OTHER, "Basic int24-le")
+
+ testlib.test(OTHER, "basic-int24", pcall (callTreeAddLE, tree, testfield.basic.INT24, tvb_bytes:range(0,3)) )
+ addMatchFields(int24_match_fields, 65280)
+
+ testlib.test(OTHER, "basic-int24", pcall (callTreeAddLE, tree, testfield.basic.INT24, tvb_bytes:range(3,3)) )
+ addMatchFields(int24_match_fields, 32769)
+
+ verifyFields("basic.INT24", int24_match_fields)
+
+----------------------------------------
+ testlib.testing(OTHER, "Basic bytes")
+
+ local bytes_match_fields = {}
+
+ testlib.test(OTHER, "basic-tvb_get_string_bytes",
+ string.lower(tostring(tvb_bytes:range():bytes())) == string.lower(barray_bytes_hex))
+
+ testlib.test(OTHER, "basic-bytes", pcall (callTreeAdd, tree, testfield.basic.BYTES, tvb_bytes:range()) )
+ addMatchFields(bytes_match_fields, barray_bytes)
+
+ -- TODO: it's silly that tree:add_packet_field() requires an encoding argument
+ -- need to fix that separately in a bug fix
+ testlib.test(OTHER, "add_pfield-bytes", treeAddPField(tree, testfield.basic.BYTES,
+ tvb_bytes:range(), ENC_BIG_ENDIAN))
+ addMatchFields(bytes_match_fields, barray_bytes)
+
+ verifyFields("basic.BYTES", bytes_match_fields)
+
+----------------------------------------
+ testlib.testing(OTHER, "Basic uint bytes")
+
+ local len_string = string.format("%02x", barray_bytes:len())
+ local barray_uint_bytes = ByteArray.new(len_string) .. barray_bytes
+ local tvb_uint_bytes = barray_uint_bytes:tvb("Basic UINT_BYTES")
+ local uint_bytes_match_fields = {}
+
+ testlib.test(OTHER, "basic-uint-bytes", pcall (callTreeAdd, tree, testfield.basic.UINT_BYTES,
+ tvb_uint_bytes:range(0,1)) )
+ addMatchFields(uint_bytes_match_fields, barray_bytes)
+
+ testlib.test(OTHER, "add_pfield-uint-bytes", treeAddPField(tree, testfield.basic.UINT_BYTES,
+ tvb_uint_bytes:range(0,1), ENC_BIG_ENDIAN) )
+ addMatchFields(uint_bytes_match_fields, barray_bytes)
+
+ verifyFields("basic.UINT_BYTES", uint_bytes_match_fields)
+
+----------------------------------------
+ testlib.testing(OTHER, "Basic OID")
+
+ -- note: the tvb being dissected and compared isn't actually a valid OID.
+ -- tree:add() and tree:add_packet-field() don't care about its validity right now.
+
+ local oid_match_fields = {}
+
+ testlib.test(OTHER, "basic-oid", pcall(callTreeAdd, tree, testfield.basic.OID, tvb_bytes:range()) )
+ addMatchFields(oid_match_fields, barray_bytes)
+
+ testlib.test(OTHER, "add_pfield-oid", treeAddPField(tree, testfield.basic.OID,
+ tvb_bytes:range(), ENC_BIG_ENDIAN) )
+ addMatchFields(oid_match_fields, barray_bytes)
+
+ verifyFields("basic.OID", oid_match_fields)
+
+----------------------------------------
+ testlib.testing(OTHER, "Basic REL_OID")
+
+ -- note: the tvb being dissected and compared isn't actually a valid OID.
+ -- tree:add() and tree:add_packet-field() don't care about its validity right now.
+
+ local rel_oid_match_fields = {}
+
+ testlib.test(OTHER, "basic-rel-oid", pcall(callTreeAdd, tree, testfield.basic.REL_OID, tvb_bytes:range()))
+ addMatchFields(rel_oid_match_fields, barray_bytes)
+
+ testlib.test(OTHER, "add_pfield-rel_oid", treeAddPField(tree, testfield.basic.REL_OID,
+ tvb_bytes:range(), ENC_BIG_ENDIAN) )
+ addMatchFields(rel_oid_match_fields, barray_bytes)
+
+ verifyFields("basic.REL_OID", rel_oid_match_fields)
+
+ -- TODO: a FT_GUID is not really a ByteArray, so we can't simply treat it as one
+ -- local barray_guid = ByteArray.new("00FF0001 80001234 567890AB CDEF00FF")
+ -- local tvb_guid = barray_guid:tvb("Basic GUID")
+ -- local guid_match_fields = {}
+
+ -- testlib.test(OTHER, "basic-guid", pcall(callTreeAdd, tree, testfield.basic.GUID, tvb_guid:range()) )
+ -- addMatchFields(guid_match_fields, barray_guid)
+
+ -- testlib.test(OTHER, "add_pfield-guid", treeAddPField(tree, testfield.basic.GUID,
+ -- tvb_guid:range(), ENC_BIG_ENDIAN) )
+ -- addMatchFields(guid_match_fields, barray_guid)
+
+ -- verifyFields("basic.GUID", guid_match_fields)
+
+----------------------------------------
+ testlib.testing(OTHER, "tree:add ipv6")
+
+ local tvb = ByteArray.new("20010db8 00000000 0000ff00 00428329"):tvb("IPv6")
+ local IPv6 = testfield.basic.IPv6
+ local ipv6_match_fields = {}
+
+ testlib.test(OTHER, "ipv6", pcall (callTreeAdd, tree, IPv6, tvb:range(0,16)))
+ addMatchFields(ipv6_match_fields, Address.ipv6('2001:0db8:0000:0000:0000:ff00:0042:8329'))
+
+ verifyFields("basic.IPv6", ipv6_match_fields)
+
+----------------------------------------
+ testlib.testing(OTHER, "tree:add ipv4")
+
+ local tvb = ByteArray.new("7f000001"):tvb("IPv4")
+ local IPv4 = testfield.basic.IPv4
+ local ipv4_match_fields = {}
+
+ testlib.test(OTHER, "ipv4", pcall (callTreeAdd, tree, IPv4, tvb:range(0,4)))
+ addMatchFields(ipv4_match_fields, Address.ip('127.0.0.1'))
+
+ -- TODO: currently, tree:add_le only works for numeric values, not IPv4
+ -- addresses. Test this in the future.
+
+ -- testlib.test(OTHER, "ipv4", pcall (callTreeAddLE, tree, IPv4, tvb:range(0,4)))
+ -- addMatchFields(ipv4_match_fields, Address.ip('1.0.0.127'))
+
+ verifyFields("basic.IPv4", ipv4_match_fields)
+
+----------------------------------------
+ testlib.testing(OTHER, "tree:add ether")
+
+ local tvb = ByteArray.new("010203040506"):tvb("Ether")
+ local tvb0 = ByteArray.new("000000000000"):tvb("Ether0")
+ local ether = testfield.basic.ETHER
+ local ether_match_fields = {}
+
+ testlib.test(OTHER, "ether", pcall (callTreeAdd, tree, ether, tvb:range(0,6)))
+ addMatchFields(ether_match_fields, Address.ether('01:02:03:04:05:06'))
+
+ testlib.test(OTHER, "ether0", pcall (callTreeAdd, tree, ether, tvb0:range(0,6)))
+ addMatchFields(ether_match_fields, Address.ether('11:22:33'))
+
+ verifyFields("basic.ETHER", ether_match_fields)
+
+----------------------------------------
+ testlib.testing(OTHER, "tree:add_packet_field Bytes")
+
+ resetResults()
+ bytes_match_fields = {}
+ local bytes_match_values = {}
+
+ -- something to make this easier to read
+ local function addMatch(...)
+ addMatchFieldValues(bytes_match_fields, bytes_match_values, ...)
+ end
+
+ local bytesstring1 = "deadbeef0123456789DEADBEEFabcdef"
+ local bytesstring = ByteArray.new(bytesstring1) -- the binary version of above, for comparing
+ local bytestvb1 = ByteArray.new(bytesstring1, true):tvb("Bytes hex-string 1")
+ local bytesstring2 = " de:ad:be:ef:01:23:45:67:89:DE:AD:BE:EF:ab:cd:ef"
+ local bytestvb2 = ByteArray.new(bytesstring2 .. "-f0-00 foobar", true):tvb("Bytes hex-string 2")
+
+ local bytestvb1_decode = bytestvb1:range():bytes(ENC_STR_HEX + ENC_SEP_NONE + ENC_SEP_COLON + ENC_SEP_DASH)
+ testlib.test(OTHER, "tvb_get_string_bytes", string.lower(tostring(bytestvb1_decode)) == string.lower(tostring(bytesstring1)))
+
+ testlib.test(OTHER, "add_pfield-bytes1", treeAddPField(tree, testfield.bytes.BYTES,
+ bytestvb1:range(),
+ ENC_STR_HEX + ENC_SEP_NONE +
+ ENC_SEP_COLON + ENC_SEP_DASH))
+ addMatch(bytesstring, string.len(bytesstring1))
+
+ testlib.test(OTHER, "add_pfield-bytes2", treeAddPField(tree, testfield.bytes.BYTES,
+ bytestvb2:range(),
+ ENC_STR_HEX + ENC_SEP_NONE +
+ ENC_SEP_COLON + ENC_SEP_DASH))
+ addMatch(bytesstring, string.len(bytesstring2))
+
+ verifyResults("add_pfield-bytes", bytes_match_values)
+ verifyFields("bytes.BYTES", bytes_match_fields)
+
+ -- extra test of ByteArray
+ local b64padded = ByteArray.new("dGVzdA==", true):base64_decode():raw()
+ local b64unpadded = ByteArray.new("dGVzdA", true):base64_decode():raw()
+ testlib.test(OTHER, "bytearray_base64_padded", b64padded == "test")
+ testlib.test(OTHER, "bytearray_base64_unpadded", b64unpadded == "test")
+
+
+----------------------------------------
+ testlib.testing(OTHER, "tree:add_packet_field OID")
+
+ resetResults()
+ bytes_match_fields = {}
+ bytes_match_values = {}
+
+ testlib.test(OTHER, "add_pfield-oid1", treeAddPField(tree, testfield.bytes.OID,
+ bytestvb1:range(),
+ ENC_STR_HEX + ENC_SEP_NONE +
+ ENC_SEP_COLON + ENC_SEP_DASH))
+ addMatch(bytesstring, string.len(bytesstring1))
+
+ testlib.test(OTHER, "add_pfield-oid2", treeAddPField(tree, testfield.bytes.OID,
+ bytestvb2:range(),
+ ENC_STR_HEX + ENC_SEP_NONE +
+ ENC_SEP_COLON + ENC_SEP_DASH))
+ addMatch(bytesstring, string.len(bytesstring2))
+
+ verifyResults("add_pfield-oid", bytes_match_values)
+ verifyFields("bytes.OID", bytes_match_fields)
+
+
+----------------------------------------
+ testlib.testing(OTHER, "tree:add_packet_field REL_OID")
+
+ resetResults()
+ bytes_match_fields = {}
+ bytes_match_values = {}
+
+ testlib.test(OTHER, "add_pfield-rel_oid1", treeAddPField(tree, testfield.bytes.REL_OID,
+ bytestvb1:range(),
+ ENC_STR_HEX + ENC_SEP_NONE +
+ ENC_SEP_COLON + ENC_SEP_DASH))
+ addMatch(bytesstring, string.len(bytesstring1))
+
+ testlib.test(OTHER, "add_pfield-rel_oid2", treeAddPField(tree, testfield.bytes.REL_OID,
+ bytestvb2:range(),
+ ENC_STR_HEX + ENC_SEP_NONE +
+ ENC_SEP_COLON + ENC_SEP_DASH))
+ addMatch(bytesstring, string.len(bytesstring2))
+
+ verifyResults("add_pfield-rel_oid", bytes_match_values)
+ verifyFields("bytes.REL_OID", bytes_match_fields)
+
+
+----------------------------------------
+ testlib.testing(OTHER, "tree:add Time")
+
+ local tvb = ByteArray.new("00000000 00000000 0000FF0F 00FF000F"):tvb("Time")
+ local ALOCAL = testfield.time.ABSOLUTE_LOCAL
+ local alocal_match_fields = {}
+
+ testlib.test(OTHER, "time-local", pcall (callTreeAdd, tree, ALOCAL, tvb:range(0,8)) )
+ addMatchFields(alocal_match_fields, NSTime())
+
+ testlib.test(OTHER, "time-local", pcall (callTreeAdd, tree, ALOCAL, tvb:range(8,8)) )
+ addMatchFields(alocal_match_fields, NSTime( 0x0000FF0F, 0x00FF000F) )
+
+ testlib.test(OTHER, "time-local-le", pcall (callTreeAddLE, tree, ALOCAL, tvb:range(0,8)) )
+ addMatchFields(alocal_match_fields, NSTime())
+
+ testlib.test(OTHER, "time-local-le", pcall (callTreeAddLE, tree, ALOCAL, tvb:range(8,8)) )
+ addMatchFields(alocal_match_fields, NSTime( 0x0FFF0000, 0x0F00FF00 ) )
+
+ verifyFields("time.ABSOLUTE_LOCAL", alocal_match_fields)
+
+ local AUTC = testfield.time.ABSOLUTE_UTC
+ local autc_match_fields = {}
+
+ testlib.test(OTHER, "time-utc", pcall (callTreeAdd, tree, AUTC, tvb:range(0,8)) )
+ addMatchFields(autc_match_fields, NSTime())
+
+ testlib.test(OTHER, "time-utc", pcall (callTreeAdd, tree, AUTC, tvb:range(8,8)) )
+ addMatchFields(autc_match_fields, NSTime( 0x0000FF0F, 0x00FF000F) )
+
+ testlib.test(OTHER, "time-utc-le", pcall (callTreeAddLE, tree, AUTC, tvb:range(0,8)) )
+ addMatchFields(autc_match_fields, NSTime())
+
+ testlib.test(OTHER, "time-utc-le", pcall (callTreeAddLE, tree, AUTC, tvb:range(8,8)) )
+ addMatchFields(autc_match_fields, NSTime( 0x0FFF0000, 0x0F00FF00 ) )
+
+ verifyFields("time.ABSOLUTE_UTC", autc_match_fields )
+
+----------------------------------------
+ testlib.testing(OTHER, "tree:add_packet_field Time bytes")
+
+ resetResults()
+ local autc_match_values = {}
+
+ -- something to make this easier to read
+ addMatch = function(...)
+ addMatchFieldValues(autc_match_fields, autc_match_values, ...)
+ end
+
+ -- tree:add_packet_field(ALOCAL, tvb:range(0,8), ENC_BIG_ENDIAN)
+ testlib.test(OTHER, "add_pfield-time-bytes-local", treeAddPField ( tree, AUTC, tvb:range(0,8), ENC_BIG_ENDIAN) )
+ addMatch( NSTime(), 8)
+
+ testlib.test(OTHER, "add_pfield-time-bytes-local", treeAddPField ( tree, AUTC, tvb:range(8,8), ENC_BIG_ENDIAN) )
+ addMatch( NSTime( 0x0000FF0F, 0x00FF000F), 16)
+
+ testlib.test(OTHER, "add_pfield-time-bytes-local-le", treeAddPField ( tree, AUTC, tvb:range(0,8), ENC_LITTLE_ENDIAN) )
+ addMatch( NSTime(), 8)
+
+ testlib.test(OTHER, "add_pfield-time-bytes-local-le", treeAddPField ( tree, AUTC, tvb:range(8,8), ENC_LITTLE_ENDIAN) )
+ addMatch( NSTime( 0x0FFF0000, 0x0F00FF00 ), 16)
+
+ verifyFields("time.ABSOLUTE_UTC", autc_match_fields)
+
+ verifyResults("add_pfield-time-bytes-local", autc_match_values)
+
+----------------------------------------
+ testlib.testing(OTHER, "tree:add_packet_field Time string ENC_ISO_8601_DATE_TIME")
+
+ resetResults()
+ autc_match_values = {}
+
+ local datetimestring1 = "2013-03-01T22:14:48+00:00" -- this is 1362176088 seconds epoch time
+ local tvb1 = ByteArray.new(datetimestring1, true):tvb("Date_Time string 1")
+ local datetimestring2 = " 2013-03-02T03:14:48+05:00" -- this is 1362176088 seconds epoch time
+ local tvb2 = ByteArray.new(datetimestring2 .. " foobar", true):tvb("Date_Time string 2")
+ local datetimestring3 = " 2013-03-01T16:44-05:30" -- this is 1362176040 seconds epoch time
+ local tvb3 = ByteArray.new(datetimestring3, true):tvb("Date_Time string 3")
+ local datetimestring4 = "2013-03-02T01:44:00+03:30" -- this is 1362176040 seconds epoch time
+ local tvb4 = ByteArray.new(datetimestring4, true):tvb("Date_Time string 4")
+ local datetimestring5 = "2013-03-01T22:14:48Z" -- this is 1362176088 seconds epoch time
+ local tvb5 = ByteArray.new(datetimestring5, true):tvb("Date_Time string 5")
+ local datetimestring6 = "2013-03-01T22:14Z" -- this is 1362176040 seconds epoch time
+ local tvb6 = ByteArray.new(datetimestring6, true):tvb("Date_Time string 6")
+
+ testlib.test(OTHER, "add_pfield-datetime-local", treeAddPField ( tree, AUTC, tvb1:range(), ENC_ISO_8601_DATE_TIME) )
+ addMatch( NSTime( 1362176088, 0), string.len(datetimestring1))
+
+ testlib.test(OTHER, "add_pfield-datetime-local", treeAddPField ( tree, AUTC, tvb2:range(), ENC_ISO_8601_DATE_TIME) )
+ addMatch( NSTime( 1362176088, 0), string.len(datetimestring2))
+
+ testlib.test(OTHER, "add_pfield-datetime-local", treeAddPField ( tree, AUTC, tvb3:range(), ENC_ISO_8601_DATE_TIME) )
+ addMatch( NSTime( 1362176040, 0), string.len(datetimestring3))
+
+ testlib.test(OTHER, "add_pfield-datetime-local", treeAddPField ( tree, AUTC, tvb4:range(), ENC_ISO_8601_DATE_TIME) )
+ addMatch( NSTime( 1362176040, 0), string.len(datetimestring4))
+
+ testlib.test(OTHER, "add_pfield-datetime-local", treeAddPField ( tree, AUTC, tvb5:range(), ENC_ISO_8601_DATE_TIME) )
+ addMatch( NSTime( 1362176088, 0), string.len(datetimestring5))
+
+ testlib.test(OTHER, "add_pfield-datetime-local", treeAddPField ( tree, AUTC, tvb6:range(), ENC_ISO_8601_DATE_TIME) )
+ addMatch( NSTime( 1362176040, 0), string.len(datetimestring6))
+
+ verifyFields("time.ABSOLUTE_UTC", autc_match_fields)
+
+ verifyResults("add_pfield-datetime-local", autc_match_values)
+
+----------------------------------------
+ testlib.testing(OTHER, "tree:add_packet_field Time string ENC_ISO_8601_DATE")
+
+ resetResults()
+ autc_match_values = {}
+
+ local datestring1 = "2013-03-01" -- this is 1362096000 seconds epoch time
+ local d_tvb1 = ByteArray.new(datestring1, true):tvb("Date string 1")
+ local datestring2 = " 2013-03-01" -- this is 1362096000 seconds epoch time
+ local d_tvb2 = ByteArray.new(datestring2 .. " foobar", true):tvb("Date string 2")
+
+ testlib.test(OTHER, "add_pfield-date-local", treeAddPField ( tree, AUTC, d_tvb1:range(), ENC_ISO_8601_DATE) )
+ addMatch( NSTime( 1362096000, 0), string.len(datestring1))
+
+ testlib.test(OTHER, "add_pfield-date-local", treeAddPField ( tree, AUTC, d_tvb2:range(), ENC_ISO_8601_DATE) )
+ addMatch( NSTime( 1362096000, 0), string.len(datestring2))
+
+ verifyFields("time.ABSOLUTE_UTC", autc_match_fields)
+
+ verifyResults("add_pfield-date-local", autc_match_values)
+
+----------------------------------------
+ testlib.testing(OTHER, "tree:add_packet_field Time string ENC_ISO_8601_TIME")
+
+ resetResults()
+ autc_match_values = {}
+
+ local timestring1 = "22:14:48" -- this is 80088 seconds
+ local t_tvb1 = ByteArray.new(timestring1, true):tvb("Time string 1")
+ local timestring2 = " 22:14:48" -- this is 80088 seconds
+ local t_tvb2 = ByteArray.new(timestring2 .. " foobar", true):tvb("Time string 2")
+
+ local now = os.date("!*t")
+ now.hour = 22
+ now.min = 14
+ now.sec = 48
+ local timebase = os.time( now )
+ timebase = timebase + timezone
+ print ("timebase = " .. tostring(timebase) .. ", timezone=" .. timezone)
+
+ testlib.test(OTHER, "add_pfield-time-local", treeAddPField ( tree, AUTC, t_tvb1:range(), ENC_ISO_8601_TIME) )
+ addMatch( NSTime( timebase, 0), string.len(timestring1))
+
+ testlib.test(OTHER, "add_pfield-time-local", treeAddPField ( tree, AUTC, t_tvb2:range(), ENC_ISO_8601_TIME) )
+ addMatch( NSTime( timebase, 0), string.len(timestring2))
+
+ verifyFields("time.ABSOLUTE_UTC", autc_match_fields)
+
+ verifyResults("add_pfield-time-local", autc_match_values)
+
+----------------------------------------
+ testlib.testing(OTHER, "tree:add_packet_field Time string ENC_IMF_DATE_TIME")
+
+ resetResults()
+ autc_match_values = {}
+
+ local imfstring1 = "Fri, 01 Mar 13 22:14:48 GMT" -- this is 1362176088 seconds epoch time
+ local imf_tvb1 = ByteArray.new(imfstring1, true):tvb("Internet Message Format Time string 1")
+ local imfstring2 = " Fri, 01 Mar 13 22:14:48 GMT" -- this is 1362176088 seconds epoch time
+ local imf_tvb2 = ByteArray.new(imfstring2 .. " foobar", true):tvb("Internet Message Format Time string 2")
+ local imfstring3 = "Fri, 01 Mar 2013 22:14:48 GMT" -- this is 1362176088 seconds epoch time
+ local imf_tvb3 = ByteArray.new(imfstring3, true):tvb("Internet Message Format Time string 3")
+ local imfstring4 = " Fri, 01 Mar 2013 22:14:48 GMT" -- this is 1362176088 seconds epoch time
+ local imf_tvb4 = ByteArray.new(imfstring4 .. " foobar", true):tvb("Internet Message Format Time string 4")
+
+ testlib.test(OTHER, "add_pfield-time-local", treeAddPField ( tree, AUTC, imf_tvb1:range(), ENC_IMF_DATE_TIME) )
+ addMatch( NSTime( 1362176088, 0), string.len(imfstring1))
+
+ testlib.test(OTHER, "add_pfield-time-local", treeAddPField ( tree, AUTC, imf_tvb2:range(), ENC_IMF_DATE_TIME) )
+ addMatch( NSTime( 1362176088, 0), string.len(imfstring2))
+
+ testlib.test(OTHER, "add_pfield-time-local", treeAddPField ( tree, AUTC, imf_tvb3:range(), ENC_IMF_DATE_TIME) )
+ addMatch( NSTime( 1362176088, 0), string.len(imfstring3))
+
+ testlib.test(OTHER, "add_pfield-time-local", treeAddPField ( tree, AUTC, imf_tvb4:range(), ENC_IMF_DATE_TIME) )
+ addMatch( NSTime( 1362176088, 0), string.len(imfstring4))
+
+ verifyFields("time.ABSOLUTE_UTC", autc_match_fields)
+
+ verifyResults("add_pfield-imf-date-time-local", autc_match_values)
+
+----------------------------------------
+ testlib.testing(OTHER, "tree:add_packet_field Time string ENC_ISO_8601_DATE_TIME_BASIC")
+
+ resetResults()
+ autc_match_values = {}
+
+ local datetimestring1 = "20130301T221448+0000" -- this is 1362176088 seconds epoch time
+ local tvb1 = ByteArray.new(datetimestring1, true):tvb("Date_Time string 1")
+ local datetimestring2 = " 20130301171448-0500" -- this is 1362176088 seconds epoch time
+ local tvb2 = ByteArray.new(datetimestring2 .. " foobar", true):tvb("Date_Time string 2")
+ local datetimestring3 = " 20130301T1644-0530" -- this is 1362176040 seconds epoch time
+ local tvb3 = ByteArray.new(datetimestring3, true):tvb("Date_Time string 3")
+ local datetimestring4 = "20130302 014400+0330" -- this is 1362176040 seconds epoch time
+ local tvb4 = ByteArray.new(datetimestring4, true):tvb("Date_Time string 4")
+ local datetimestring5 = "20130301T221448Z" -- this is 1362176088 seconds epoch time
+ local tvb5 = ByteArray.new(datetimestring5, true):tvb("Date_Time string 5")
+ local datetimestring6 = "201303012214Z" -- this is 1362176040 seconds epoch time
+ local tvb6 = ByteArray.new(datetimestring6, true):tvb("Date_Time string 6")
+
+ testlib.test(OTHER, "add_pfield-datetime-local", treeAddPField ( tree, AUTC, tvb1:range(), ENC_ISO_8601_DATE_TIME_BASIC) )
+ addMatch( NSTime( 1362176088, 0), string.len(datetimestring1))
+
+ testlib.test(OTHER, "add_pfield-datetime-local", treeAddPField ( tree, AUTC, tvb2:range(), ENC_ISO_8601_DATE_TIME_BASIC) )
+ addMatch( NSTime( 1362176088, 0), string.len(datetimestring2))
+
+ testlib.test(OTHER, "add_pfield-datetime-local", treeAddPField ( tree, AUTC, tvb3:range(), ENC_ISO_8601_DATE_TIME_BASIC) )
+ addMatch( NSTime( 1362176040, 0), string.len(datetimestring3))
+
+ testlib.test(OTHER, "add_pfield-datetime-local", treeAddPField ( tree, AUTC, tvb4:range(), ENC_ISO_8601_DATE_TIME_BASIC) )
+ addMatch( NSTime( 1362176040, 0), string.len(datetimestring4))
+
+ testlib.test(OTHER, "add_pfield-datetime-local", treeAddPField ( tree, AUTC, tvb5:range(), ENC_ISO_8601_DATE_TIME_BASIC) )
+ addMatch( NSTime( 1362176088, 0), string.len(datetimestring5))
+
+ testlib.test(OTHER, "add_pfield-datetime-local", treeAddPField ( tree, AUTC, tvb6:range(), ENC_ISO_8601_DATE_TIME_BASIC) )
+ addMatch( NSTime( 1362176040, 0), string.len(datetimestring6))
+
+ verifyFields("time.ABSOLUTE_UTC", autc_match_fields)
+
+ verifyResults("add_pfield-datetime-local", autc_match_values)
+
+----------------------------------------
+ testlib.testing(OTHER, "TvbRange subsets")
+
+ resetResults()
+
+ local offset = 5
+ local len = 10
+ local b_offset = 3
+ local b_len = 2
+ local range
+ local range_raw
+ local expected
+
+ -- This is the same data from the "tree:add_packet_field Bytes" test
+ -- copied here for clarity
+ local bytesstring1 = "deadbeef0123456789DEADBEEFabcdef"
+ local bytestvb1 = ByteArray.new(bytesstring1, true):tvb("Bytes hex-string 1")
+
+ -- tvbrange with no offset or length (control test case)
+ range = bytestvb1()
+ range_raw = range:raw()
+ expected = range:bytes():raw()
+ testlib.test(OTHER, "tvbrange_raw", range_raw == expected,
+ string.format('range_raw="%s" expected="%s"', range_raw, expected))
+ range_raw = range:raw(b_offset)
+ expected = range:bytes():raw(b_offset)
+ testlib.test(OTHER, "tvbrange_raw_offset", range_raw == expected,
+ string.format('range_raw="%s" expected="%s"', range_raw, expected))
+ range_raw = range:raw(0, b_len)
+ expected = range:bytes():raw(0, b_len)
+ testlib.test(OTHER, "tvbrange_raw_len", range_raw == expected,
+ string.format('range_raw="%s" expected="%s"', range_raw, expected))
+ range_raw = range:raw(b_offset, b_len)
+ expected = range:bytes():raw(b_offset, b_len)
+ testlib.test(OTHER, "tvbrange_raw_offset_len", range_raw == expected,
+ string.format('range_raw="%s" expected="%s"', range_raw, expected))
+
+ -- tvbrange with len only
+ range = bytestvb1(0, len)
+ range_raw = range:raw()
+ expected = range:bytes():raw()
+ testlib.test(OTHER, "tvbrange_len_raw", range_raw == expected,
+ string.format('range_raw="%s" expected="%s"', range_raw, expected))
+ range_raw = range:raw(b_offset)
+ expected = range:bytes():raw(b_offset)
+ testlib.test(OTHER, "tvbrange_len_raw_offset", range_raw == expected,
+ string.format('range_raw="%s" expected="%s"', range_raw, expected))
+ range_raw = range:raw(0, b_len)
+ expected = range:bytes():raw(0, b_len)
+ testlib.test(OTHER, "tvbrange_len_raw_len", range_raw == expected,
+ string.format('range_raw="%s" expected="%s"', range_raw, expected))
+ range_raw = range:raw(b_offset, b_len)
+ expected = range:bytes():raw(b_offset, b_len)
+ testlib.test(OTHER, "tvbrange_len_raw_offset_len", range_raw == expected,
+ string.format('range_raw="%s" expected="%s"', range_raw, expected))
+
+ -- tvbrange with offset only
+ range = bytestvb1(offset)
+ range_raw = range:raw()
+ expected = range:bytes():raw()
+ testlib.test(OTHER, "tvbrange_offset_raw", range_raw == expected,
+ string.format('range_raw="%s" expected="%s"', range_raw, expected))
+ range_raw = range:raw(b_offset)
+ expected = range:bytes():raw(b_offset)
+ testlib.test(OTHER, "tvbrange_offset_raw_offset", range_raw == expected,
+ string.format('range_raw="%s" expected="%s"', range_raw, expected))
+ range_raw = range:raw(0, b_len)
+ expected = range:bytes():raw(0, b_len)
+ testlib.test(OTHER, "tvbrange_offset_raw_len", range_raw == expected,
+ string.format('range_raw="%s" expected="%s"', range_raw, expected))
+ range_raw = range:raw(b_offset, b_len)
+ expected = range:bytes():raw(b_offset, b_len)
+ testlib.test(OTHER, "tvbrange_offset_raw_offset_len", range_raw == expected,
+ string.format('range_raw="%s" expected="%s"', range_raw, expected))
+
+ -- tvbrange with offset and len
+ range = bytestvb1(offset, len)
+ range_raw = range:raw()
+ expected = range:bytes():raw()
+ testlib.test(OTHER, "tvbrange_offset_len_raw", range_raw == expected,
+ string.format('range_raw="%s" expected="%s"', range_raw, expected))
+ range_raw = range:raw(b_offset)
+ expected = range:bytes():raw(b_offset)
+ testlib.test(OTHER, "tvbrange_offset_len_raw_offset", range_raw == expected,
+ string.format('range_raw="%s" expected="%s"', range_raw, expected))
+ range_raw = range:raw(0, b_len)
+ expected = range:bytes():raw(0, b_len)
+ testlib.test(OTHER, "tvbrange_offset_len_raw_len", range_raw == expected,
+ string.format('range_raw="%s" expected="%s"', range_raw, expected))
+ range_raw = range:raw(b_offset, b_len)
+ expected = range:bytes():raw(b_offset, b_len)
+ testlib.test(OTHER, "tvbrange_offset_len_raw_offset_len", range_raw == expected,
+ string.format('range_raw="%s" expected="%s"', range_raw, expected))
+
+----------------------------------------
+
+ testlib.pass(FRAME)
+end
+
+----------------------------------------
+-- we want to have our protocol dissection invoked for a specific UDP port,
+-- so get the udp dissector table and add our protocol to it
+DissectorTable.get("udp.port"):add(65333, test_proto)
+DissectorTable.get("udp.port"):add(65346, test_proto)
+
+print("test_proto dissector registered")
diff --git a/test/lua/unicode.lua b/test/lua/unicode.lua
new file mode 100644
index 0000000..3510e57
--- /dev/null
+++ b/test/lua/unicode.lua
@@ -0,0 +1,55 @@
+--
+-- Unicode tests
+--
+
+local errors = 0
+
+function assertEqual(what, a, b)
+ if a == b then
+ return true
+ end
+ print('ERROR:', what)
+ print('Expected:', tostring(a))
+ print(' Actual:', tostring(b))
+ errors = errors + 1
+end
+
+-- script name check
+local scriptname = (debug.getinfo(1, 'S').source or ''):gsub("^@.*[/\\]", "")
+assertEqual('script name', 'script-Ф-€-中.lua', scriptname)
+
+-- loadfile
+local code, err = loadfile('load-Ф-€-中.lua')
+assertEqual('loadfile', nil, err)
+assertEqual('loadfile contents', 'Contents of Ф-€-中', code and code())
+
+-- dofile
+local ok, result = pcall(dofile, 'load-Ф-€-中.lua')
+assertEqual('dofile pcall', true, ok)
+assertEqual('dofile contents', 'Contents of Ф-€-中', result)
+
+-- io.open (read)
+local fr, err = io.open('load-Ф-€-中.lua')
+assertEqual('io.open (read)', nil, err)
+assertEqual('io.read', 'return "Contents of Ф-€-中"\n', fr and fr:read('*a'))
+if fr then fr:close() end
+
+-- io.open (write)
+local fw, err = io.open('written-by-lua-Ф-€-中.txt', 'w')
+assertEqual('io.open (write)', nil, err)
+if fw then
+ local _, err = fw:write('Feedback from Lua: Ф-€-中\n')
+ assertEqual('io.write', nil, err)
+end
+if fw then fw:close() end
+
+-- Check for Unicode in personal plugins directory path.
+local pdir_expected = 'unicode-Ф-€-中-testcases'
+local pdir = Dir.personal_plugins_path()
+pdir = pdir:gsub('.*[/\\]unicode-.*-.*-testcases[/\\].*', pdir_expected)
+assertEqual('Unicode in Dir.personal_plugins_path', pdir_expected, pdir)
+
+if errors ~= 0 then
+ error('Failed tests: ' .. errors)
+end
+print("All tests passed!")
diff --git a/test/lua/util.lua b/test/lua/util.lua
new file mode 100644
index 0000000..0578a2a
--- /dev/null
+++ b/test/lua/util.lua
@@ -0,0 +1,118 @@
+-- test script for wslua utility functions
+
+local testlib = require("testlib")
+
+local GET_PREF = "get"
+local SET_PREF = "set"
+local RESET_PREF = "reset"
+local OTHER = "other"
+testlib.init( {
+ [GET_PREF] = 14,
+ [SET_PREF] = 37,
+ [RESET_PREF] = 11,
+ [OTHER] = 0
+} )
+
+local console_open
+
+--------------------------
+
+-- Note: This tests expects some specific default values
+testlib.testing("get_preference")
+
+success = pcall(get_preference)
+testlib.test(GET_PREF,"get_preference-empty-0", not success)
+testlib.test(GET_PREF,"get_preference-empty-1",get_preference("") == nil)
+testlib.test(GET_PREF,"get_preference-unknown-0",get_preference("g") == nil)
+testlib.test(GET_PREF,"get_preference-unknown-1",get_preference("gui") == nil)
+testlib.test(GET_PREF,"get_preference-unknown-2",get_preference("gui.") == nil)
+testlib.test(GET_PREF,"get_preference-unknown-3",get_preference("gui.ask") == nil)
+testlib.test(GET_PREF,"get_preference-unknown-4",get_preference("ugi.ask_unsaved") == nil)
+testlib.test(GET_PREF,"get_preference-uint-0",get_preference("gui.fileopen.preview") == 3)
+testlib.test(GET_PREF,"get_preference-bool-0",get_preference("gui.ask_unsaved") == true)
+testlib.test(GET_PREF,"get_preference-bool-1",get_preference("gui.interfaces_show_hidden") == false)
+-- gui.console_open is persistent (in the Windows registry) and for that
+-- reason does not have a default value.
+console_open = get_preference("gui.console_open")
+testlib.test(GET_PREF,"get_preference-enum-0",console_open == "NEVER" or console_open == "AUTOMATIC" or console_open == "ALWAYS")
+testlib.test(GET_PREF,"get_preference-string-0",get_preference("gui.window_title") == "")
+testlib.test(GET_PREF,"get_preference-range-0",get_preference("http.tls.port") == "443")
+success = pcall(get_preference, "user_dlt.encaps_table")
+testlib.test(GET_PREF,"get_preference-uat-0", not success)
+
+--------------------------
+
+testlib.testing("set_preference")
+
+success = pcall(set_preference)
+testlib.test(SET_PREF,"set_preference-empty-0", not success)
+testlib.test(SET_PREF,"set_preference-empty-1",set_preference("") == nil)
+testlib.test(SET_PREF,"set_preference-unknown-0",set_preference("g") == nil)
+testlib.test(SET_PREF,"set_preference-unknown-1",set_preference("gui") == nil)
+testlib.test(SET_PREF,"set_preference-unknown-2",set_preference("gui.") == nil)
+testlib.test(SET_PREF,"set_preference-unknown-3",set_preference("gui.ask") == nil)
+testlib.test(SET_PREF,"set_preference-unknown-4",set_preference("ugi.ask_unsaved") == nil)
+success = pcall(set_preference,"gui.fileopen.preview")
+testlib.test(SET_PREF,"set_preference-uint-0", not success)
+success = pcall(set_preference,"gui.fileopen.preview",true)
+testlib.test(SET_PREF,"set_preference-uint-1", not success)
+success = pcall(set_preference,"gui.fileopen.preview","string")
+testlib.test(SET_PREF,"set_preference-uint-2", not success)
+testlib.test(SET_PREF,"set_preference-uint-3",set_preference("gui.fileopen.preview",3) == false)
+testlib.test(SET_PREF,"set_preference-uint-4",set_preference("gui.fileopen.preview",42) == true)
+testlib.test(SET_PREF,"set_preference-uint-4-get",get_preference("gui.fileopen.preview") == 42)
+success = pcall(set_preference,"gui.ask_unsaved")
+testlib.test(SET_PREF,"set_preference-bool-0", not success)
+success = pcall(set_preference,"gui.ask_unsaved",42)
+testlib.test(SET_PREF,"set_preference-bool-1", not success)
+success = pcall(set_preference,"gui.ask_unsaved","string")
+testlib.test(SET_PREF,"set_preference-bool-2", not success)
+testlib.test(SET_PREF,"set_preference-bool-3",set_preference("gui.ask_unsaved", true) == false)
+testlib.test(SET_PREF,"set_preference-bool-4",set_preference("gui.ask_unsaved", false) == true)
+success = pcall(set_preference,"gui.console_open")
+testlib.test(SET_PREF,"set_preference-enum-0", not success)
+success = pcall(set_preference,"gui.console_open",true)
+testlib.test(SET_PREF,"set_preference-enum-1", not success)
+-- false means unchanged
+testlib.test(SET_PREF,"set_preference-enum-2",set_preference("gui.console_open",console_open) == false)
+success = pcall(set_preference,"gui.window_title")
+testlib.test(SET_PREF,"set_preference-string-0", not success)
+success = pcall(set_preference,"gui.window_title",true)
+testlib.test(SET_PREF,"set_preference-string-1", not success)
+testlib.test(SET_PREF,"set_preference-string-2",set_preference("gui.window_title","Title") == true)
+testlib.test(SET_PREF,"set_preference-string-2-get",get_preference("gui.window_title") == "Title")
+testlib.test(SET_PREF,"set_preference-string-3",set_preference("gui.window_title","Title") == false)
+testlib.test(SET_PREF,"set_preference-string-4",set_preference("gui.window_title","") == true)
+testlib.test(SET_PREF,"set_preference-string-4-get",get_preference("gui.window_title") == "")
+testlib.test(SET_PREF,"set_preference-string-5",set_preference("gui.window_title","") == false)
+success = pcall(set_preference,"http.tls.port")
+testlib.test(SET_PREF,"set_preference-range-0", not success)
+success = pcall(set_preference,"http.tls.port","65536") -- Number too big
+testlib.test(SET_PREF,"set_preference-range-1", not success)
+success = pcall(set_preference,"http.tls.port","http") -- Syntax error
+testlib.test(SET_PREF,"set_preference-range-2", not success)
+testlib.test(SET_PREF,"set_preference-range-3",set_preference("http.tls.port","443") == false)
+testlib.test(SET_PREF,"set_preference-range-4",set_preference("http.tls.port","443-444") == true)
+testlib.test(SET_PREF,"set_preference-range-4-get",get_preference("http.tls.port") == "443-444")
+testlib.test(SET_PREF,"set_preference-range-5",set_preference("http.tls.port","443-444") == false)
+success = pcall(set_preference, "user_dlt.encaps_table")
+testlib.test(SET_PREF,"set_preference-uat-0", not success)
+
+--------------------------
+
+testlib.testing("reset_preference")
+
+success = pcall(set_preference)
+testlib.test(RESET_PREF,"reset_preference-empty-0", not success)
+testlib.test(RESET_PREF,"reset_preference-empty-1",reset_preference("") == nil)
+testlib.test(RESET_PREF,"reset_preference-unknown-0",reset_preference("unknown") == nil)
+testlib.test(RESET_PREF,"reset_preference-uint-0",reset_preference("gui.fileopen.preview") == true)
+testlib.test(RESET_PREF,"reset_preference-uint-0-get",get_preference("gui.fileopen.preview") == 3)
+testlib.test(RESET_PREF,"reset_preference-bool-0",reset_preference("gui.ask_unsaved") == true)
+testlib.test(RESET_PREF,"reset_preference-bool-0-get",get_preference("gui.ask_unsaved") == true)
+testlib.test(RESET_PREF,"reset_preference-string-0",reset_preference("gui.window_title") == true)
+testlib.test(RESET_PREF,"reset_preference-string-0-get",get_preference("gui.window_title") == "")
+testlib.test(RESET_PREF,"reset_preference-range-0",reset_preference("http.tls.port") == true)
+testlib.test(RESET_PREF,"reset_preference-range-0-get",get_preference("http.tls.port") == "443")
+
+testlib.getResults()
diff --git a/test/lua/verify_dissector.lua b/test/lua/verify_dissector.lua
new file mode 100644
index 0000000..b391f85
--- /dev/null
+++ b/test/lua/verify_dissector.lua
@@ -0,0 +1,380 @@
+-- This is a test script for tshark.
+-- This script runs inside tshark.
+-- FIRST run tshark with the "dns_dissector.lua" plugin, with the dns_port.pcap file,
+-- and with full tree output (-V switch). Pipe that to a file named testin.txt.
+-- This verify script then reads in that testin.txt. The filename can be specified
+-- using the "verify_file" argument.
+--
+-- tshark -r bogus.cap -X lua_script:<path_to_testdir>/lua/verify_dns_dissector.lua
+
+local function testing(...)
+ print("---- Testing "..tostring(...).." ----")
+end
+
+local lines = {
+ {
+ "MyDNS Protocol",
+ "Transaction ID: 42",
+ "Flags: 0x0100",
+ "0... .... .... .... = Response: this is a query",
+ "[Expert Info (Chat/Request): DNS query message]",
+ "[DNS query message]",
+ "[Severity level: Chat]",
+ "[Group: Request]",
+ ".000 0... .... .... = Opcode: 0",
+ ".... ..0. .... .... = Truncated: False",
+ ".... ...1 .... .... = Recursion desired: yes",
+ ".... .... .0.. .... = World War Z - Reserved for future use: 0x0",
+ ".... .... ...0 .... = Checking disabled: False",
+ "Number of Questions: 1",
+ "Number of Answer RRs: 0",
+ "Number of Authority RRs: 0",
+ "Number of Additional RRs: 0",
+ "Queries",
+ "us.pool.ntp.org: type A (IPv4 host address) (1), class IN (Internet) (1)",
+ "Name: us.pool.ntp.org",
+ "[Name Length: 17]",
+ "[Label Count: 4]",
+ "Type: A (IPv4 host address) (1)",
+ "Class: IN (Internet) (1)",
+ },
+
+ {
+ "MyDNS Protocol",
+ "Transaction ID: 42",
+ "Flags: 0x8180",
+ "1... .... .... .... = Response: this is a response",
+ "[Expert Info (Chat/Response): It's a response!]",
+ "[It's a response!]",
+ "[Severity level: Chat]",
+ "[Group: Response]",
+ ".000 0... .... .... = Opcode: 0",
+ ".... .0.. .... .... = Authoritative: False",
+ ".... ..0. .... .... = Truncated: False",
+ ".... .... 1... .... = Recursion available: True",
+ ".... .... .0.. .... = World War Z - Reserved for future use: 0x0",
+ ".... .... ..0. .... = Authenticated: no",
+ ".... .... .... 0000 = Response code: No Error (0)",
+ ".... .... ...0 .... = Checking disabled: False",
+ "DNS answer to life, the universe, and everything",
+ "[Expert Info (Note/Comment): DNS answer to life, the universe, and everything]",
+ "[DNS answer to life, the universe, and everything]",
+ "[Severity level: Note]",
+ "[Group: Comment]",
+ "Number of Questions: 1",
+ "Number of Answer RRs: 15",
+ "Number of Authority RRs: 6",
+ "Number of Additional RRs: 2",
+ "Queries",
+ "us.pool.ntp.org: type A (IPv4 host address) (1), class IN (Internet) (1)",
+ "Name: us.pool.ntp.org",
+ "[Name Length: 17]",
+ "[Label Count: 4]",
+ "Type: A (IPv4 host address) (1)",
+ "Class: IN (Internet) (1)",
+ },
+
+ {
+ "MyDNS Protocol",
+ "Transaction ID: 43",
+ "Flags: 0x0100",
+ "0... .... .... .... = Response: this is a query",
+ "[Expert Info (Chat/Request): DNS query message]",
+ "[DNS query message]",
+ "[Severity level: Chat]",
+ "[Group: Request]",
+ ".000 0... .... .... = Opcode: 0",
+ ".... ..0. .... .... = Truncated: False",
+ ".... ...1 .... .... = Recursion desired: yes",
+ ".... .... .0.. .... = World War Z - Reserved for future use: 0x0",
+ ".... .... ...0 .... = Checking disabled: False",
+ "Number of Questions: 1",
+ "Number of Answer RRs: 0",
+ "Number of Authority RRs: 0",
+ "Number of Additional RRs: 0",
+ "Queries",
+ "us.pool.ntp.org: type A (IPv4 host address) (1), class IN (Internet) (1)",
+ "Name: us.pool.ntp.org",
+ "[Name Length: 17]",
+ "[Label Count: 4]",
+ "Type: A (IPv4 host address) (1)",
+ "Class: IN (Internet) (1)",
+ },
+
+ {
+ "MyDNS Protocol",
+ "Transaction ID: 43",
+ "Flags: 0x8180",
+ "1... .... .... .... = Response: this is a response",
+ "[Expert Info (Chat/Response): It's a response!]",
+ "[It's a response!]",
+ "[Severity level: Chat]",
+ "[Group: Response]",
+ ".000 0... .... .... = Opcode: 0",
+ ".... .0.. .... .... = Authoritative: False",
+ ".... ..0. .... .... = Truncated: False",
+ ".... .... 1... .... = Recursion available: True",
+ ".... .... .0.. .... = World War Z - Reserved for future use: 0x0",
+ ".... .... ..0. .... = Authenticated: no",
+ ".... .... .... 0000 = Response code: No Error (0)",
+ ".... .... ...0 .... = Checking disabled: False",
+ "Number of Questions: 1",
+ "Number of Answer RRs: 15",
+ "Number of Authority RRs: 6",
+ "Number of Additional RRs: 2",
+ "Queries",
+ "us.pool.ntp.org: type A (IPv4 host address) (1), class IN (Internet) (1)",
+ "Name: us.pool.ntp.org",
+ "[Name Length: 17]",
+ "[Label Count: 4]",
+ "Type: A (IPv4 host address) (1)",
+ "Class: IN (Internet) (1)",
+ },
+}
+
+-- we're going to see those two sets of output twice: both by the normal
+-- dissector, then the first one by the heuristic, then the second one by
+-- a conversation match
+local numtests = 1 + #lines[1] + #lines[2] + #lines[3] + #lines[4]
+
+local hasHeuristic = true
+
+local verify_file = "testin.txt"
+
+-- grab passed-in arguments
+local args = { ... }
+if #args > 0 then
+ for _, arg in ipairs(args) do
+ local name, value = arg:match("(.+)=(.+)")
+ if arg == "no_heur" then
+ numtests = numtests - 1
+ elseif name == "verify_file" and value then
+ verify_file = value
+ end
+ end
+end
+
+print("going to run "..numtests.." tests")
+
+-- for an example of what we're reading through to verify, look at end of this file
+print("opening file "..verify_file)
+local file = io.open(verify_file, "r")
+local line = file:read()
+
+local pktidx = 1
+local total = 0
+local found = false
+
+while line do
+ -- eat beginning whitespace
+ line = line:gsub("^%s+","",1)
+ if line:find("^Frame %d+:") then
+ pktidx = line:match("^Frame (%d+):")
+ testing("Frame "..pktidx)
+ pktidx = tonumber(pktidx)
+ if pktidx > 4 then pktidx = pktidx - 4 end
+ line = file:read()
+ elseif line:find("%[Heuristic dissector used%]") then
+ -- start again, because it now repeats
+ -- but we should not see this [Heuristic dissector used] line again
+ -- or it's an error in setting the conversation
+ if found then
+ error("Heuristic dissector ran twice - conversation setting not working?")
+ return
+ end
+ found = true
+ total = total + 1
+ line = file:read()
+ elseif line == lines[pktidx][1] then
+ -- we've matched the first line of our section
+ -- now verify the rest is sequential
+ for i, v in ipairs(lines[pktidx]) do
+ io.stdout:write("testing Frame "..pktidx..", line "..i.."...")
+ if not line then
+ -- ended too soon
+ io.stdout:write("failed!\n")
+ error("Ran out of file lines!")
+ return
+ end
+ -- eat beginning whitespace
+ line = line:gsub("^%s+","",1)
+ if line ~= v then
+ io.stdout:write("failed!\n")
+ print("Got this:'"..line.."', expected this:'"..v.."'")
+ error("mismatched lines!")
+ return
+ end
+ io.stdout:write("passed\n")
+ total = total + 1
+ line = file:read()
+ end
+ else
+ line = file:read()
+ end
+end
+
+print(total.." of "..numtests.." tests run and passed")
+
+if total ~= numtests then
+ error("Did not find all our lines to test!")
+ return
+end
+
+print("\n-----------------------------\n")
+-- must print out the following for success (the test shell sciprt looks for this)
+print("All tests passed!\n\n")
+
+
+----------------------------------------------------------
+-- We should see something like this:
+--[[
+Frame 1: 75 bytes on wire (600 bits), 75 bytes captured (600 bits)
+ Encapsulation type: Ethernet (1)
+ Arrival Time: Sep 26, 2004 23:18:04.938672000 EDT
+ [Time shift for this packet: 0.000000000 seconds]
+ Epoch Time: 1096255084.938672000 seconds
+ [Time delta from previous captured frame: 0.000000000 seconds]
+ [Time delta from previous displayed frame: 0.000000000 seconds]
+ [Time since reference or first frame: 0.000000000 seconds]
+ Frame Number: 1
+ Frame Length: 75 bytes (600 bits)
+ Capture Length: 75 bytes (600 bits)
+ [Frame is marked: False]
+ [Frame is ignored: False]
+ [Protocols in frame: eth:ethertype:ip:udp:mydns]
+Ethernet II, Src: AmbitMic_6c:40:4e (00:d0:59:6c:40:4e), Dst: Cisco-Li_82:b2:53 (00:0c:41:82:b2:53)
+ Destination: Cisco-Li_82:b2:53 (00:0c:41:82:b2:53)
+ Address: Cisco-Li_82:b2:53 (00:0c:41:82:b2:53)
+ .... ..0. .... .... .... .... = LG bit: Globally unique address (factory default)
+ .... ...0 .... .... .... .... = IG bit: Individual address (unicast)
+ Source: AmbitMic_6c:40:4e (00:d0:59:6c:40:4e)
+ Address: AmbitMic_6c:40:4e (00:d0:59:6c:40:4e)
+ .... ..0. .... .... .... .... = LG bit: Globally unique address (factory default)
+ .... ...0 .... .... .... .... = IG bit: Individual address (unicast)
+ Type: IP (0x0800)
+Internet Protocol Version 4, Src: 192.168.50.50 (192.168.50.50), Dst: 192.168.0.1 (192.168.0.1)
+ Version: 4
+ Header Length: 20 bytes
+ Differentiated Services Field: 0x00 (DSCP 0x00: Default; ECN: 0x00: Not-ECT (Not ECN-Capable Transport))
+ 0000 00.. = Differentiated Services Codepoint: Default (0x00)
+ .... ..00 = Explicit Congestion Notification: Not-ECT (Not ECN-Capable Transport) (0x00)
+ Total Length: 61
+ Identification: 0x0a41 (2625)
+ Flags: 0x00
+ 0... .... = Reserved bit: Not set
+ .0.. .... = Don't fragment: Not set
+ ..0. .... = More fragments: Not set
+ Fragment offset: 0
+ Time to live: 128
+ Protocol: UDP (17)
+ Header checksum: 0x7ceb [correct]
+ [Good: True]
+ [Bad: False]
+ Source: 192.168.50.50 (192.168.50.50)
+ Destination: 192.168.0.1 (192.168.0.1)
+User Datagram Protocol, Src Port: 65282 (65282), Dst Port: 65333 (65333)
+ Source Port: 65282 (65282)
+ Destination Port: 65333 (65333)
+ Length: 41
+ Checksum: 0x07a9 [validation disabled]
+ [Good Checksum: False]
+ [Bad Checksum: False]
+ [Stream index: 0]
+MyDNS Protocol
+ Transaction ID: 43
+ Flags: 0x0100
+ 0... .... .... .... = Response: this is a query
+ .000 0... .... .... = Opcode: 0
+ .... ..0. .... .... = Truncated: False
+ .... ...1 .... .... = Recursion desired: yes
+ .... .... .0.. .... = World War Z - Reserved for future use: 0x0
+ .... .... ...0 .... = Checking disabled: False
+ Number of Questions: 1
+ Number of Answer RRs: 0
+ Number of Authority RRs: 0
+ Number of Additional RRs: 0
+ Queries
+ us.pool.ntp.org: type A (IPv4 host address) (1), class IN (Internet) (1)
+ Name: us.pool.ntp.org
+ [Name Length: 17]
+ [Label Count: 4]
+ Type: A (IPv4 host address) (1)
+ Class: IN (Internet) (1)
+
+Frame 2: 540 bytes on wire (4320 bits), 540 bytes captured (4320 bits)
+ Encapsulation type: Ethernet (1)
+ Arrival Time: Sep 26, 2004 23:18:04.945618000 EDT
+ [Time shift for this packet: 0.000000000 seconds]
+ Epoch Time: 1096255084.945618000 seconds
+ [Time delta from previous captured frame: 0.006946000 seconds]
+ [Time delta from previous displayed frame: 0.006946000 seconds]
+ [Time since reference or first frame: 0.006946000 seconds]
+ Frame Number: 2
+ Frame Length: 540 bytes (4320 bits)
+ Capture Length: 540 bytes (4320 bits)
+ [Frame is marked: False]
+ [Frame is ignored: False]
+ [Protocols in frame: eth:ethertype:ip:udp:mydns]
+Ethernet II, Src: Cisco-Li_82:b2:53 (00:0c:41:82:b2:53), Dst: AmbitMic_6c:40:4e (00:d0:59:6c:40:4e)
+ Destination: AmbitMic_6c:40:4e (00:d0:59:6c:40:4e)
+ Address: AmbitMic_6c:40:4e (00:d0:59:6c:40:4e)
+ .... ..0. .... .... .... .... = LG bit: Globally unique address (factory default)
+ .... ...0 .... .... .... .... = IG bit: Individual address (unicast)
+ Source: Cisco-Li_82:b2:53 (00:0c:41:82:b2:53)
+ Address: Cisco-Li_82:b2:53 (00:0c:41:82:b2:53)
+ .... ..0. .... .... .... .... = LG bit: Globally unique address (factory default)
+ .... ...0 .... .... .... .... = IG bit: Individual address (unicast)
+ Type: IP (0x0800)
+Internet Protocol Version 4, Src: 192.168.0.1 (192.168.0.1), Dst: 192.168.50.50 (192.168.50.50)
+ Version: 4
+ Header Length: 20 bytes
+ Differentiated Services Field: 0x00 (DSCP 0x00: Default; ECN: 0x00: Not-ECT (Not ECN-Capable Transport))
+ 0000 00.. = Differentiated Services Codepoint: Default (0x00)
+ .... ..00 = Explicit Congestion Notification: Not-ECT (Not ECN-Capable Transport) (0x00)
+ Total Length: 526
+ Identification: 0x2153 (8531)
+ Flags: 0x00
+ 0... .... = Reserved bit: Not set
+ .0.. .... = Don't fragment: Not set
+ ..0. .... = More fragments: Not set
+ Fragment offset: 0
+ Time to live: 63
+ Protocol: UDP (17)
+ Header checksum: 0xa508 [correct]
+ [Good: True]
+ [Bad: False]
+ Source: 192.168.0.1 (192.168.0.1)
+ Destination: 192.168.50.50 (192.168.50.50)
+User Datagram Protocol, Src Port: 65333 (65333), Dst Port: 65282 (65282)
+ Source Port: 65333 (65333)
+ Destination Port: 65282 (65282)
+ Length: 506
+ Checksum: 0xf9d5 [validation disabled]
+ [Good Checksum: False]
+ [Bad Checksum: False]
+ [Stream index: 0]
+MyDNS Protocol
+ Transaction ID: 43
+ Flags: 0x8180
+ 1... .... .... .... = Response: this is a response
+ .000 0... .... .... = Opcode: 0
+ .... .0.. .... .... = Authoritative: False
+ .... ..0. .... .... = Truncated: False
+ .... .... 1... .... = Recursion available: True
+ .... .... .0.. .... = World War Z - Reserved for future use: 0x0
+ .... .... ..0. .... = Authenticated: no
+ .... .... .... 0000 = Response code: No Error (0)
+ .... .... ...0 .... = Checking disabled: False
+ Number of Questions: 1
+ Number of Answer RRs: 15
+ Number of Authority RRs: 6
+ Number of Additional RRs: 2
+ Queries
+ us.pool.ntp.org: type A (IPv4 host address) (1), class IN (Internet) (1)
+ Name: us.pool.ntp.org
+ [Name Length: 17]
+ [Label Count: 4]
+ Type: A (IPv4 host address) (1)
+ Class: IN (Internet) (1)
+]]
+
diff --git a/test/lua/verify_globals.lua b/test/lua/verify_globals.lua
new file mode 100644
index 0000000..dbed8ce
--- /dev/null
+++ b/test/lua/verify_globals.lua
@@ -0,0 +1,135 @@
+-- verify_globals.lua
+
+-- ignore things that change on different machines or every release
+-- the following items still have to exist, but their values don't have to match
+local filter = {
+ -- differences by machine
+ "DATA_DIR",
+ "USER_DIR",
+ "package.cpath",
+ "package.path",
+ "package.loaded",
+ "run_user_scripts_when_superuser",
+ "running_superuser",
+
+ -- differences in Lua versions
+ "_VERSION",
+ "package.config",
+
+ -- differences caused by changes in wireshark 1.11
+ "NSTime",
+ "Proto",
+ 'Listener["<metatable>"].__index',
+ ".__index"
+ }
+
+-- the following items don't have to exist
+local ignore = {
+ -- not sure why this was removed in wireshark 1.11, but it was
+ "TreeItem.set_expert_flags",
+
+ -- in Lua 5.1 only
+ "debug.getfenv",
+ "debug.setfenv",
+ "gcinfo",
+ "getfenv",
+ "io.gfind",
+ "setfenv",
+ "math.mod",
+ "newproxy",
+ "string.gfind",
+ "table.foreach",
+ "table.foreachi",
+ "table.getn",
+ "table.setn",
+
+ -- in Lua 5.2+ only
+ "bit32",
+ "debug.getuservalu",
+ "debug.setuservalu",
+ "debug.upvalueid",
+ "debug.upvaluejoin",
+ "package.searchers",
+ "package.searchpath",
+ "rawlen",
+ "table.pack",
+ "table.unpack",
+
+}
+
+
+local arg={...} -- get passed-in args
+
+-- arg1 = path to find inspect
+-- arg2 = filename to read in (optional, unless 'verify' is set)
+-- arg3 = 'verify' to verify all of read-in file is in _G (default); 'new' to output all items in _G that are not in read-in file
+-- arg4 = 'nometa' to ignore metatables; 'meta' otherwise (default)
+
+local add_path = "lua/?.lua;"
+if #arg > 0 then
+ add_path = arg[1].."?.lua;"
+end
+
+print("package.path = " .. package.path)
+
+-- need the path to find inspect.lua
+local old_path = package.path
+package.path = add_path .. package.path
+
+local inspect = require("inspect")
+
+package.path = old_path -- return path to original
+
+print("-- Wireshark version: " .. get_version())
+
+if #arg == 1 then
+ -- no more args, so just output globals
+ print(inspect(_G, { serialize = true, filter = inspect.makeFilter(filter) }))
+ return
+end
+
+local file = assert(io.open(arg[2], "r"))
+local input = file:read("*all")
+input = inspect.marshal(input)
+
+local nometa = false
+if #arg > 3 and arg[4] == "nometa" then
+ nometa = true
+end
+
+if #arg == 2 or arg[3] == "verify" then
+ print(string.rep("\n", 2))
+ print("Verifying input file '"..arg[2].."' is contained within the global table")
+ local ret, diff = inspect.compare(input, _G, {
+ ['filter'] = inspect.makeFilter(filter),
+ ['ignore'] = inspect.makeFilter(ignore),
+ ['nonumber'] = true,
+ ['nometa'] = nometa
+ })
+ if not ret then
+ print("Comparison failed - global table does not have all the items in the input file!")
+ print(string.rep("\n", 2))
+ print(string.rep("-", 80))
+ print("Differences are:")
+ print(inspect(diff))
+ else
+ print("\n-----------------------------\n")
+ print("All tests passed!\n\n")
+ end
+ return
+elseif #arg > 2 and arg[3] == "new" then
+ local ret, diff = inspect.compare(_G, input, {
+ ['filter'] = inspect.makeFilter(filter),
+ ['ignore'] = inspect.makeFilter(ignore),
+ ['nonumber'] = true,
+ ['keep'] = true,
+ ['nometa'] = nometa
+ })
+ if not ret then
+ print(inspect(diff))
+ else
+ print("\n-----------------------------\n")
+ print("No new items!\n\n")
+ end
+end
+
diff --git a/test/matchers.py b/test/matchers.py
new file mode 100644
index 0000000..46005ca
--- /dev/null
+++ b/test/matchers.py
@@ -0,0 +1,66 @@
+#
+# Wireshark tests
+#
+# Copyright (c) 2018 Peter Wu <peter@lekensteyn.nl>
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+'''Helpers for matching test results.'''
+
+import re
+
+class MatchAny(object):
+ '''Matches any other value.'''
+
+ def __init__(self, type=None):
+ self.type = type
+
+ def __eq__(self, other):
+ return self.type is None or self.type == type(other)
+
+ def __repr__(self):
+ return '<MatchAny type=%s>' % (self.type.__name__,)
+
+
+class MatchObject(object):
+ '''Matches all expected fields of an object, ignoring excess others.'''
+
+ def __init__(self, fields):
+ self.fields = fields
+
+ def __eq__(self, other):
+ return all(other.get(k) == v for k, v in self.fields.items())
+
+ def __repr__(self):
+ return '<MatchObject fields=%r>' % (self.fields,)
+
+
+class MatchList(object):
+ '''Matches elements of a list. Optionally checks list length.'''
+
+ def __init__(self, item, n=None, match_element=all):
+ self.item = item
+ self.n = n
+ self.match_element = match_element
+
+ def __eq__(self, other):
+ if self.n is not None and len(other) != self.n:
+ return False
+ return self.match_element(self.item == elm for elm in other)
+
+ def __repr__(self):
+ return '<MatchList item=%r n=%r match_element=%s>' % \
+ (self.item, self.n, self.match_element.__name__)
+
+
+class MatchRegExp(object):
+ '''Matches a string against a regular expression.'''
+
+ def __init__(self, pattern):
+ self.pattern = pattern
+
+ def __eq__(self, other):
+ return type(other) == str and re.match(self.pattern, other)
+
+ def __repr__(self):
+ return '<MatchRegExp pattern=%r>' % (self.pattern)
diff --git a/test/protobuf_lang_files/complex_proto_files/complex_syntax.proto b/test/protobuf_lang_files/complex_proto_files/complex_syntax.proto
new file mode 100644
index 0000000..359ead4
--- /dev/null
+++ b/test/protobuf_lang_files/complex_proto_files/complex_syntax.proto
@@ -0,0 +1,82 @@
+// Test more complex syntax of *.proto files.
+syntax = "proto3";
+package wireshark.protobuf.test.complex.syntax;
+import "google/protobuf/descriptor.proto";
+
+// equal to "testing.multiline.strings"
+option java_package = "testing."
+ 'multiline.'
+"strings";
+
+// user defined options for messages
+extend google.protobuf.MessageOptions {
+ bool disabled = 1071;
+ bool ignored = 1072;
+ TestMultiLinesOption mlinemsg = 1073;
+}
+
+// user defined options for oneof types
+extend google.protobuf.OneofOptions {
+ bool required = 1071;
+}
+
+// user defined options for fields
+extend google.protobuf.FieldOptions {
+ FieldRules rules = 1071;
+}
+// test extend google.protobuf.FieldOptions twice
+extend google.protobuf.FieldOptions {
+ string multilines = 1072;
+}
+
+message FieldRules {
+ oneof type {
+ BoolRules bool = 13;
+ StringRules string = 14;
+ }
+ repeated uint32 repeated_uint = 15;
+}
+
+message StringRules {
+ uint64 min_bytes = 2;
+ BoolRules morebool = 3;
+ string astr = 4;
+}
+
+message BoolRules {
+ bool const = 1;
+ repeated bool repeated_bool = 2;
+ repeated int32 repeated_int = 3;
+}
+
+message TestMultiLinesOption {
+ string mlines = 1;
+}
+
+message ComplexDefinedMessage {
+ option (mlinemsg).mlines = "first line"
+ "second line";
+
+ // test complex field options
+ string fieldWithComplexOption1 = 1 [(wireshark.protobuf.test.complex.syntax.rules).string = {min_bytes: 1}];
+ string fieldWithComplexOption2 = 2 [(rules).string = {min_bytes: 2 astr: "abc" }];
+ string fieldWithComplexOption3 = 3 [(rules).string.morebool = {const: true, repeated_bool: [false, true], repeated_int: [1, 2]}];
+ string fieldWithComplexOption4 = 4 [(rules).string = {min_bytes: 1; morebool { const: true }}];
+ string fieldWithComplexOption5 = 5 [(rules).repeated_uint = 1, (rules).repeated_uint = 2];
+
+ // test oneof custom option
+ oneof oneofWithOption {
+ option (wireshark.protobuf.test.complex.syntax.required) = true;
+ int32 field1 = 11;
+ string field2 = 12;
+ }
+
+ // test multilines strings
+ uint32 fieldWithMultilineStringOption = 20 [(wireshark.protobuf.test.complex.syntax.multilines) = "first line"
+ 'Second line' ];
+}
+
+// add this message for testing whether this file was successfully parsed
+message TestFileParsed {
+ optional int32 last_field_for_wireshark_test = 1;
+}
diff --git a/test/protobuf_lang_files/complex_proto_files/unittest_custom_options.proto b/test/protobuf_lang_files/complex_proto_files/unittest_custom_options.proto
new file mode 100644
index 0000000..d04c9b7
--- /dev/null
+++ b/test/protobuf_lang_files/complex_proto_files/unittest_custom_options.proto
@@ -0,0 +1,395 @@
+// This file is from https://github.com/protocolbuffers/protobuf/blob/3.14.x/src/google/protobuf/unittest_custom_options.proto
+// To reduce the file size, some comments have been removed.
+// Message 'TestFileParsed' is added at the end of file for testing whether this file was successfully parsed.
+
+syntax = "proto2";
+
+option cc_generic_services = true;
+option java_generic_services = true;
+option py_generic_services = true;
+
+option (file_opt1) = 9876543210;
+
+import "google/protobuf/any.proto";
+import "google/protobuf/descriptor.proto";
+
+package protobuf_unittest;
+
+// Some simple test custom options of various types.
+extend google.protobuf.FileOptions {
+ optional uint64 file_opt1 = 7736974;
+}
+
+extend google.protobuf.MessageOptions {
+ optional int32 message_opt1 = 7739036;
+}
+
+extend google.protobuf.FieldOptions {
+ optional fixed64 field_opt1 = 7740936;
+ optional int32 field_opt2 = 7753913 [default = 42];
+}
+
+extend google.protobuf.OneofOptions {
+ optional int32 oneof_opt1 = 7740111;
+}
+
+extend google.protobuf.EnumOptions {
+ optional sfixed32 enum_opt1 = 7753576;
+}
+
+extend google.protobuf.EnumValueOptions {
+ optional int32 enum_value_opt1 = 1560678;
+}
+
+extend google.protobuf.ServiceOptions {
+ optional sint64 service_opt1 = 7887650;
+}
+
+enum MethodOpt1 {
+ METHODOPT1_VAL1 = 1;
+ METHODOPT1_VAL2 = 2;
+}
+
+extend google.protobuf.MethodOptions {
+ optional MethodOpt1 method_opt1 = 7890860;
+}
+
+message TestMessageWithCustomOptions {
+ option message_set_wire_format = false;
+ option (message_opt1) = -56;
+
+ optional string field1 = 1 [ctype = CORD, (field_opt1) = 8765432109];
+
+ oneof AnOneof {
+ option (oneof_opt1) = -99;
+
+ int32 oneof_field = 2;
+ }
+
+ enum AnEnum {
+ option (enum_opt1) = -789;
+
+ ANENUM_VAL1 = 1;
+ ANENUM_VAL2 = 2 [(enum_value_opt1) = 123];
+ }
+}
+
+message CustomOptionFooRequest {}
+
+message CustomOptionFooResponse {}
+
+message CustomOptionFooClientMessage {}
+
+message CustomOptionFooServerMessage {}
+
+service TestServiceWithCustomOptions {
+ option (service_opt1) = -9876543210;
+
+ rpc Foo(CustomOptionFooRequest) returns (CustomOptionFooResponse) {
+ option (method_opt1) = METHODOPT1_VAL2;
+ }
+}
+
+message DummyMessageContainingEnum {
+ enum TestEnumType {
+ TEST_OPTION_ENUM_TYPE1 = 22;
+ TEST_OPTION_ENUM_TYPE2 = -23;
+ }
+}
+
+message DummyMessageInvalidAsOptionType {}
+
+extend google.protobuf.MessageOptions {
+ optional bool bool_opt = 7706090;
+ optional int32 int32_opt = 7705709;
+ optional int64 int64_opt = 7705542;
+ optional uint32 uint32_opt = 7704880;
+ optional uint64 uint64_opt = 7702367;
+ optional sint32 sint32_opt = 7701568;
+ optional sint64 sint64_opt = 7700863;
+ optional fixed32 fixed32_opt = 7700307;
+ optional fixed64 fixed64_opt = 7700194;
+ optional sfixed32 sfixed32_opt = 7698645;
+ optional sfixed64 sfixed64_opt = 7685475;
+ optional float float_opt = 7675390;
+ optional double double_opt = 7673293;
+ optional string string_opt = 7673285;
+ optional bytes bytes_opt = 7673238;
+ optional DummyMessageContainingEnum.TestEnumType enum_opt = 7673233;
+ optional DummyMessageInvalidAsOptionType message_type_opt = 7665967;
+}
+
+message CustomOptionMinIntegerValues {
+ option (bool_opt) = false;
+ option (int32_opt) = -0x80000000;
+ option (int64_opt) = -0x8000000000000000;
+ option (uint32_opt) = 0;
+ option (uint64_opt) = 0;
+ option (sint32_opt) = -0x80000000;
+ option (sint64_opt) = -0x8000000000000000;
+ option (fixed32_opt) = 0;
+ option (fixed64_opt) = 0;
+ option (sfixed32_opt) = -0x80000000;
+ option (sfixed64_opt) = -0x8000000000000000;
+}
+
+message CustomOptionMaxIntegerValues {
+ option (bool_opt) = true;
+ option (int32_opt) = 0x7FFFFFFF;
+ option (int64_opt) = 0x7FFFFFFFFFFFFFFF;
+ option (uint32_opt) = 0xFFFFFFFF;
+ option (uint64_opt) = 0xFFFFFFFFFFFFFFFF;
+ option (sint32_opt) = 0x7FFFFFFF;
+ option (sint64_opt) = 0x7FFFFFFFFFFFFFFF;
+ option (fixed32_opt) = 0xFFFFFFFF;
+ option (fixed64_opt) = 0xFFFFFFFFFFFFFFFF;
+ option (sfixed32_opt) = 0x7FFFFFFF;
+ option (sfixed64_opt) = 0x7FFFFFFFFFFFFFFF;
+}
+
+message CustomOptionOtherValues {
+ option (int32_opt) = -100; // To test sign-extension.
+ option (float_opt) = 12.3456789;
+ option (double_opt) = 1.234567890123456789;
+ option (string_opt) = "Hello, \"World\"";
+ option (bytes_opt) = "Hello\0World";
+ option (enum_opt) = TEST_OPTION_ENUM_TYPE2;
+}
+
+message SettingRealsFromPositiveInts {
+ option (float_opt) = 12;
+ option (double_opt) = 154;
+}
+
+message SettingRealsFromNegativeInts {
+ option (float_opt) = -12;
+ option (double_opt) = -154;
+}
+
+message ComplexOptionType1 {
+ optional int32 foo = 1;
+ optional int32 foo2 = 2;
+ optional int32 foo3 = 3;
+ repeated int32 foo4 = 4;
+
+ extensions 100 to max;
+}
+
+message ComplexOptionType2 {
+ optional ComplexOptionType1 bar = 1;
+ optional int32 baz = 2;
+
+ message ComplexOptionType4 {
+ optional int32 waldo = 1;
+
+ extend google.protobuf.MessageOptions {
+ optional ComplexOptionType4 complex_opt4 = 7633546;
+ }
+ }
+
+ optional ComplexOptionType4 fred = 3;
+ repeated ComplexOptionType4 barney = 4;
+
+ extensions 100 to max;
+}
+
+message ComplexOptionType3 {
+ optional int32 qux = 1;
+
+ optional group ComplexOptionType5 = 2 {
+ optional int32 plugh = 3;
+ }
+}
+
+extend ComplexOptionType1 {
+ optional int32 quux = 7663707;
+ optional ComplexOptionType3 corge = 7663442;
+}
+
+extend ComplexOptionType2 {
+ optional int32 grault = 7650927;
+ optional ComplexOptionType1 garply = 7649992;
+}
+
+extend google.protobuf.MessageOptions {
+ optional protobuf_unittest.ComplexOptionType1 complex_opt1 = 7646756;
+ optional ComplexOptionType2 complex_opt2 = 7636949;
+ optional ComplexOptionType3 complex_opt3 = 7636463;
+ optional group ComplexOpt6 = 7595468 {
+ optional int32 xyzzy = 7593951;
+ }
+}
+
+message VariousComplexOptions {
+ option (.protobuf_unittest.complex_opt1).foo = 42;
+ option (protobuf_unittest.complex_opt1).(.protobuf_unittest.quux) = 324;
+ option (.protobuf_unittest.complex_opt1).(protobuf_unittest.corge).qux = 876;
+ option (protobuf_unittest.complex_opt1).foo4 = 99;
+ option (protobuf_unittest.complex_opt1).foo4 = 88;
+ option (complex_opt2).baz = 987;
+ option (complex_opt2).(grault) = 654;
+ option (complex_opt2).bar.foo = 743;
+ option (complex_opt2).bar.(quux) = 1999;
+ option (complex_opt2).bar.(protobuf_unittest.corge).qux = 2008;
+ option (complex_opt2).(garply).foo = 741;
+ option (complex_opt2).(garply).(.protobuf_unittest.quux) = 1998;
+ option (complex_opt2).(protobuf_unittest.garply).(corge).qux = 2121;
+ option (ComplexOptionType2.ComplexOptionType4.complex_opt4).waldo = 1971;
+ option (complex_opt2).fred.waldo = 321;
+ option (complex_opt2).barney = {
+ waldo: 101
+ };
+ option (complex_opt2).barney = {
+ waldo: 212
+ };
+ option (protobuf_unittest.complex_opt3).qux = 9;
+ option (complex_opt3).complexoptiontype5.plugh = 22;
+ option (complexopt6).xyzzy = 24;
+}
+
+message AggregateMessageSet {
+ option message_set_wire_format = true;
+
+ extensions 4 to max;
+}
+
+message AggregateMessageSetElement {
+ extend AggregateMessageSet {
+ optional AggregateMessageSetElement message_set_extension = 15447542;
+ }
+ optional string s = 1;
+}
+
+message Aggregate {
+ optional int32 i = 1;
+ optional string s = 2;
+
+ optional Aggregate sub = 3;
+
+ optional google.protobuf.FileOptions file = 4;
+ extend google.protobuf.FileOptions {
+ optional Aggregate nested = 15476903;
+ }
+
+ optional AggregateMessageSet mset = 5;
+
+ optional google.protobuf.Any any = 6;
+}
+
+extend google.protobuf.FileOptions {
+ optional Aggregate fileopt = 15478479;
+}
+extend google.protobuf.MessageOptions {
+ optional Aggregate msgopt = 15480088;
+}
+extend google.protobuf.FieldOptions {
+ optional Aggregate fieldopt = 15481374;
+}
+extend google.protobuf.EnumOptions {
+ optional Aggregate enumopt = 15483218;
+}
+extend google.protobuf.EnumValueOptions {
+ optional Aggregate enumvalopt = 15486921;
+}
+extend google.protobuf.ServiceOptions {
+ optional Aggregate serviceopt = 15497145;
+}
+extend google.protobuf.MethodOptions {
+ optional Aggregate methodopt = 15512713;
+}
+
+option (fileopt) = {
+ s: 'FileAnnotation'
+ i: 100
+
+ sub { s: 'NestedFileAnnotation' }
+
+ file {
+ [protobuf_unittest.fileopt] { s: 'FileExtensionAnnotation' }
+ }
+
+ mset {
+ [protobuf_unittest.AggregateMessageSetElement.message_set_extension] {
+ s: 'EmbeddedMessageSetElement'
+ }
+ }
+
+ any {
+ [type.googleapis.com/protobuf_unittest.AggregateMessageSetElement] {
+ s: 'EmbeddedMessageSetElement'
+ }
+ }
+};
+
+message AggregateMessage {
+ option (msgopt) = {
+ i: 101
+ s: 'MessageAnnotation'
+ };
+
+ optional int32 fieldname = 1 [(fieldopt) = { s: 'FieldAnnotation' }];
+}
+
+service AggregateService {
+ option (serviceopt) = {
+ s: 'ServiceAnnotation'
+ };
+
+ rpc Method(AggregateMessage) returns (AggregateMessage) {
+ option (methodopt) = {
+ s: 'MethodAnnotation'
+ };
+ }
+}
+
+enum AggregateEnum {
+ option (enumopt) = {
+ s: 'EnumAnnotation'
+ };
+
+ VALUE = 1 [(enumvalopt) = { s: 'EnumValueAnnotation' }];
+}
+
+message NestedOptionType {
+ message NestedMessage {
+ option (message_opt1) = 1001;
+
+ optional int32 nested_field = 1 [(field_opt1) = 1002];
+ }
+ enum NestedEnum {
+ option (enum_opt1) = 1003;
+
+ NESTED_ENUM_VALUE = 1 [(enum_value_opt1) = 1004];
+ }
+ extend google.protobuf.FileOptions {
+ optional int32 nested_extension = 7912573 [(field_opt2) = 1005];
+ }
+}
+
+message OldOptionType {
+ enum TestEnum { OLD_VALUE = 0; }
+ required TestEnum value = 1;
+}
+
+message NewOptionType {
+ enum TestEnum {
+ OLD_VALUE = 0;
+ NEW_VALUE = 1;
+ }
+ required TestEnum value = 1;
+}
+
+extend google.protobuf.MessageOptions {
+ optional OldOptionType required_enum_opt = 106161807;
+}
+
+message TestMessageWithRequiredEnumOption {
+ option (required_enum_opt) = {
+ value: OLD_VALUE
+ };
+}
+
+// add this message for testing whether this file was successfully parsed
+message TestFileParsed {
+ optional int32 last_field_for_wireshark_test = 1;
+}
diff --git a/test/protobuf_lang_files/user_defined_types/addressbook.proto b/test/protobuf_lang_files/user_defined_types/addressbook.proto
new file mode 100644
index 0000000..bc619b1
--- /dev/null
+++ b/test/protobuf_lang_files/user_defined_types/addressbook.proto
@@ -0,0 +1,30 @@
+// This file comes from the official Protobuf example with a little modification.
+syntax = "proto3";
+package tutorial;
+import "google/protobuf/timestamp.proto";
+
+message Person {
+ string name = 1;
+ int32 id = 2; // Unique ID number for this person.
+ string email = 3;
+
+ enum PhoneType {
+ MOBILE = 0;
+ HOME = 1;
+ WORK = 2;
+ }
+
+ message PhoneNumber {
+ string number = 1;
+ PhoneType type = 2;
+ }
+
+ repeated PhoneNumber phone = 4;
+ google.protobuf.Timestamp last_updated = 5;
+ bytes portrait_image = 6;
+}
+
+message AddressBook {
+ repeated Person people = 1;
+}
+
diff --git a/test/protobuf_lang_files/user_defined_types/greet.proto b/test/protobuf_lang_files/user_defined_types/greet.proto
new file mode 100644
index 0000000..f14b125
--- /dev/null
+++ b/test/protobuf_lang_files/user_defined_types/greet.proto
@@ -0,0 +1,22 @@
+// This file is from https://github.com/grpc/grpc-dotnet/blob/v2.42.x/examples/Browser/Proto/greet.proto
+
+syntax = "proto3";
+
+package greet;
+
+// The greeting service definition.
+service Greeter {
+ // Sends a greeting
+ rpc SayHello (HelloRequest) returns (HelloReply);
+ rpc SayHellos (HelloRequest) returns (stream HelloReply);
+}
+
+// The request message containing the user's name.
+message HelloRequest {
+ string name = 1;
+}
+
+// The response message containing the greetings
+message HelloReply {
+ string message = 1;
+}
diff --git a/test/protobuf_lang_files/user_defined_types/person_search_service.proto b/test/protobuf_lang_files/user_defined_types/person_search_service.proto
new file mode 100644
index 0000000..1511f71
--- /dev/null
+++ b/test/protobuf_lang_files/user_defined_types/person_search_service.proto
@@ -0,0 +1,15 @@
+// A gRPC service that searches for persons based on certain attributes.
+syntax = "proto3";
+package tutorial;
+import "addressbook.proto";
+
+message PersonSearchRequest {
+ repeated string name = 1;
+ repeated int32 id = 2;
+ repeated string phoneNumber = 3;
+}
+
+service PersonSearchService {
+ rpc Search (PersonSearchRequest) returns (stream Person) {}
+}
+
diff --git a/test/protobuf_lang_files/user_defined_types/test_default_value.proto b/test/protobuf_lang_files/user_defined_types/test_default_value.proto
new file mode 100644
index 0000000..25aee6a
--- /dev/null
+++ b/test/protobuf_lang_files/user_defined_types/test_default_value.proto
@@ -0,0 +1,59 @@
+// Test default values of Protobuf fields
+syntax = "proto2";
+package wireshark.protobuf.test;
+
+message TestDefaultValueMessage {
+ enum EnumFoo {
+ ENUM_FOO_V_FIRST = 1;
+ ENUM_FOO_V_SECOND = 0x2;
+ ENUM_FOO_V_THIRD = 3;
+ ENUM_FOO_V_FOURTH = - 4;
+ }
+
+ // The format of field name is:
+ // <type> "With" ( "Value" | "DefaultValue" | "NoValue" ) [ "_" <correct_value_in_wireshark> ]
+ // The "DefaultValue" fields should be wrapped with generated mark ("[" and "]") of Wireshark tree item.
+ // The "NoValue" fields should not appear in Wireshark.
+
+ // The default value is overridden to 8 at running time.
+ required int32 int32WithValue_8 = 1 [ default = 2 ];
+ // The default value is overridden to ENUM_FOO_V_THIRD at running time.
+ optional EnumFoo enumFooWithValue_Third = 2 [ default = ENUM_FOO_V_SECOND ];
+
+ // default values of bool
+ optional bool boolWithDefaultValue_False = 11;
+ optional bool boolWithDefaultValue_True = 12 [ default = true ];
+
+ // default values of enum
+ optional EnumFoo enumFooWithDefaultValue_First = 21;
+ optional EnumFoo enumFooWithDefaultValue_Second = 22 [ default = ENUM_FOO_V_SECOND ];
+ optional EnumFoo enumFooWithDefaultValue_Fouth = 23 [ default = ENUM_FOO_V_FOURTH ];
+
+ // default values of integer number
+ optional int32 int32WithDefaultValue_0 = 31;
+ optional int64 int64WithDefaultValue_Negative1152921504606846976 = 32 [ default = - 1152921504606846976 ];
+ optional uint32 uint32WithDefaultValue_11 = 33 [ default = 11 ];
+ optional uint64 uint64WithDefaultValue_1152921504606846976 = 34 [ default = 1152921504606846976 ]; // equals to 2^60
+ optional sint32 sint32WithDefaultValue_Negative12 = 35 [ default = -12 ];
+ optional sint64 sint64WithDefaultValue_0 = 36; // default value is zero
+ optional fixed64 fixed64WithDefaultValue_1152921504606846976 = 37 [ default = 1152921504606846976 ];
+ optional sfixed32 sfixed32WithDefaultValue_Negative31 = 38 [ default = -0X1f ]; // -21
+
+ // default values of float and double
+ optional float floatWithDefaultValue_0point23 = 41 [ default = 0.23 ];
+ optional double doubleWithDefaultValue_Negative0point12345678 = 42 [ default = -0.12345678 ];
+
+ // default values of string and bytes
+ optional string stringWithNoValue = 51; // default value must not appear because not declared
+ optional string stringWithDefaultValue_SymbolPi = 52 [ default = "The symbol \'\xF0\x9D\x9B\x91\' is mathematical bold small Pi."];
+ optional bytes bytesWithNoValue = 53; // default value must not appear because not declared
+ // '\'nnn is octal value of a byte, '\x'nn is hex value of a byte
+ optional bytes bytesWithDefaultValue_1F2F890D0A00004B = 54 [ default = "\x1F\x2F\211\r\n\000\x0\x4B" ];
+
+ // others
+ repeated int32 repeatedFieldWithNoValue = 81; // should not appear
+ required int32 missingRequiredField = 82; // for testing required field. (comment this line if you regenerated stub code)
+ // test taking keyword as identification feature
+ optional int32 message = 83;
+ optional int32 optional = 84;
+}
diff --git a/test/protobuf_lang_files/user_defined_types/test_leading_dot.proto b/test/protobuf_lang_files/user_defined_types/test_leading_dot.proto
new file mode 100644
index 0000000..3ee7de8
--- /dev/null
+++ b/test/protobuf_lang_files/user_defined_types/test_leading_dot.proto
@@ -0,0 +1,24 @@
+syntax="proto3";
+
+package a.b;
+
+message a {
+ string param1 = 1;
+
+ message b {
+ string param2 = 2;
+
+ message c {
+ string param3 = 3;
+ }
+ }
+}
+
+message msg {
+ a.b.c param4 = 4; /* the full name of the type is a.b.a.b.c */
+ .a.b.c param5 = 5; /* the full name of the type is a.b.c */
+}
+
+message c {
+ string param6 = 6;
+}
diff --git a/test/protobuf_lang_files/user_defined_types/test_map.proto b/test/protobuf_lang_files/user_defined_types/test_map.proto
new file mode 100644
index 0000000..9a6847c
--- /dev/null
+++ b/test/protobuf_lang_files/user_defined_types/test_map.proto
@@ -0,0 +1,17 @@
+syntax="proto3";
+
+package test.map;
+
+message Foo {
+ int32 param1 = 1;
+}
+
+message MapMaster {
+ oneof Abc {
+ string param2 = 2;
+ string param3 = 3;
+ }
+
+ map<string, int64> param4 = 4;
+ map<sint32, Foo> param5 = 5;
+}
diff --git a/test/protobuf_lang_files/well_know_types/google/protobuf/any.proto b/test/protobuf_lang_files/well_know_types/google/protobuf/any.proto
new file mode 100644
index 0000000..8ff870d
--- /dev/null
+++ b/test/protobuf_lang_files/well_know_types/google/protobuf/any.proto
@@ -0,0 +1,22 @@
+// This file is from https://github.com/protocolbuffers/protobuf/blob/3.14.x/src/google/protobuf/any.proto
+// To reduce the file size, some comments have been removed.
+
+syntax = "proto3";
+
+package google.protobuf;
+
+option csharp_namespace = "Google.Protobuf.WellKnownTypes";
+option go_package = "google.golang.org/protobuf/types/known/anypb";
+option java_package = "com.google.protobuf";
+option java_outer_classname = "AnyProto";
+option java_multiple_files = true;
+option objc_class_prefix = "GPB";
+
+message Any {
+ // A URL/resource name that uniquely identifies the type of the serialized
+ // protocol buffer message.
+ string type_url = 1;
+
+ // Must be a valid serialized protocol buffer of the above specified type.
+ bytes value = 2;
+}
diff --git a/test/protobuf_lang_files/well_know_types/google/protobuf/descriptor.proto b/test/protobuf_lang_files/well_know_types/google/protobuf/descriptor.proto
new file mode 100644
index 0000000..ece32e1
--- /dev/null
+++ b/test/protobuf_lang_files/well_know_types/google/protobuf/descriptor.proto
@@ -0,0 +1,282 @@
+// This file is from https://github.com/protocolbuffers/protobuf/blob/3.14.x/src/google/protobuf/descriptor.proto
+// To reduce the file size, some comments have been removed.
+
+syntax = "proto2";
+
+package google.protobuf;
+
+option go_package = "google.golang.org/protobuf/types/descriptorpb";
+option java_package = "com.google.protobuf";
+option java_outer_classname = "DescriptorProtos";
+option csharp_namespace = "Google.Protobuf.Reflection";
+option objc_class_prefix = "GPB";
+option cc_enable_arenas = true;
+
+option optimize_for = SPEED;
+
+message FileDescriptorSet {
+ repeated FileDescriptorProto file = 1;
+}
+
+message FileDescriptorProto {
+ optional string name = 1;
+ optional string package = 2;
+ repeated string dependency = 3;
+ repeated int32 public_dependency = 10;
+ repeated int32 weak_dependency = 11;
+ repeated DescriptorProto message_type = 4;
+ repeated EnumDescriptorProto enum_type = 5;
+ repeated ServiceDescriptorProto service = 6;
+ repeated FieldDescriptorProto extension = 7;
+ optional FileOptions options = 8;
+ optional SourceCodeInfo source_code_info = 9;
+ optional string syntax = 12;
+}
+
+message DescriptorProto {
+ optional string name = 1;
+ repeated FieldDescriptorProto field = 2;
+ repeated FieldDescriptorProto extension = 6;
+ repeated DescriptorProto nested_type = 3;
+ repeated EnumDescriptorProto enum_type = 4;
+
+ message ExtensionRange {
+ optional int32 start = 1;
+ optional int32 end = 2;
+ optional ExtensionRangeOptions options = 3;
+ }
+ repeated ExtensionRange extension_range = 5;
+ repeated OneofDescriptorProto oneof_decl = 8;
+ optional MessageOptions options = 7;
+
+ message ReservedRange {
+ optional int32 start = 1;
+ optional int32 end = 2;
+ }
+ repeated ReservedRange reserved_range = 9;
+ repeated string reserved_name = 10;
+}
+
+message ExtensionRangeOptions {
+ repeated UninterpretedOption uninterpreted_option = 999;
+ extensions 1000 to max;
+}
+
+message FieldDescriptorProto {
+ enum Type {
+ TYPE_DOUBLE = 1;
+ TYPE_FLOAT = 2;
+ TYPE_INT64 = 3;
+ TYPE_UINT64 = 4;
+ TYPE_INT32 = 5;
+ TYPE_FIXED64 = 6;
+ TYPE_FIXED32 = 7;
+ TYPE_BOOL = 8;
+ TYPE_STRING = 9;
+ TYPE_GROUP = 10;
+ TYPE_MESSAGE = 11;
+ TYPE_BYTES = 12;
+ TYPE_UINT32 = 13;
+ TYPE_ENUM = 14;
+ TYPE_SFIXED32 = 15;
+ TYPE_SFIXED64 = 16;
+ TYPE_SINT32 = 17;
+ TYPE_SINT64 = 18;
+ }
+
+ enum Label {
+ LABEL_OPTIONAL = 1;
+ LABEL_REQUIRED = 2;
+ LABEL_REPEATED = 3;
+ }
+
+ optional string name = 1;
+ optional int32 number = 3;
+ optional Label label = 4;
+ optional Type type = 5;
+ optional string type_name = 6;
+ optional string extendee = 2;
+ optional string default_value = 7;
+ optional int32 oneof_index = 9;
+ optional string json_name = 10;
+ optional FieldOptions options = 8;
+ optional bool proto3_optional = 17;
+}
+
+message OneofDescriptorProto {
+ optional string name = 1;
+ optional OneofOptions options = 2;
+}
+
+message EnumDescriptorProto {
+ optional string name = 1;
+ repeated EnumValueDescriptorProto value = 2;
+ optional EnumOptions options = 3;
+
+ message EnumReservedRange {
+ optional int32 start = 1;
+ optional int32 end = 2;
+ }
+
+ repeated EnumReservedRange reserved_range = 4;
+ repeated string reserved_name = 5;
+}
+
+message EnumValueDescriptorProto {
+ optional string name = 1;
+ optional int32 number = 2;
+ optional EnumValueOptions options = 3;
+}
+
+message ServiceDescriptorProto {
+ optional string name = 1;
+ repeated MethodDescriptorProto method = 2;
+ optional ServiceOptions options = 3;
+}
+
+message MethodDescriptorProto {
+ optional string name = 1;
+ optional string input_type = 2;
+ optional string output_type = 3;
+ optional MethodOptions options = 4;
+ optional bool client_streaming = 5 [default = false];
+ optional bool server_streaming = 6 [default = false];
+}
+
+message FileOptions {
+ optional string java_package = 1;
+ optional string java_outer_classname = 8;
+ optional bool java_multiple_files = 10 [default = false];
+ optional bool java_generate_equals_and_hash = 20 [deprecated=true];
+ optional bool java_string_check_utf8 = 27 [default = false];
+ enum OptimizeMode {
+ SPEED = 1;
+ CODE_SIZE = 2;
+ LITE_RUNTIME = 3;
+ }
+ optional OptimizeMode optimize_for = 9 [default = SPEED];
+ optional string go_package = 11;
+ optional bool cc_generic_services = 16 [default = false];
+ optional bool java_generic_services = 17 [default = false];
+ optional bool py_generic_services = 18 [default = false];
+ optional bool php_generic_services = 42 [default = false];
+ optional bool deprecated = 23 [default = false];
+ optional bool cc_enable_arenas = 31 [default = true];
+ optional string objc_class_prefix = 36;
+ optional string csharp_namespace = 37;
+ optional string swift_prefix = 39;
+ optional string php_class_prefix = 40;
+ optional string php_namespace = 41;
+ optional string php_metadata_namespace = 44;
+ optional string ruby_package = 45;
+ repeated UninterpretedOption uninterpreted_option = 999;
+ extensions 1000 to max;
+ reserved 38;
+}
+
+message MessageOptions {
+ optional bool message_set_wire_format = 1 [default = false];
+ optional bool no_standard_descriptor_accessor = 2 [default = false];
+ optional bool deprecated = 3 [default = false];
+ optional bool map_entry = 7;
+ reserved 8;
+ reserved 9;
+ repeated UninterpretedOption uninterpreted_option = 999;
+ extensions 1000 to max;
+}
+
+message FieldOptions {
+ optional CType ctype = 1 [default = STRING];
+ enum CType {
+ STRING = 0;
+ CORD = 1;
+ STRING_PIECE = 2;
+ }
+ optional bool packed = 2;
+ optional JSType jstype = 6 [default = JS_NORMAL];
+ enum JSType {
+ JS_NORMAL = 0;
+ JS_STRING = 1;
+ JS_NUMBER = 2;
+ }
+ optional bool lazy = 5 [default = false];
+ optional bool deprecated = 3 [default = false];
+ optional bool weak = 10 [default = false];
+ repeated UninterpretedOption uninterpreted_option = 999;
+ extensions 1000 to max;
+
+ reserved 4;
+}
+
+message OneofOptions {
+ repeated UninterpretedOption uninterpreted_option = 999;
+ extensions 1000 to max;
+}
+
+message EnumOptions {
+ optional bool allow_alias = 2;
+ optional bool deprecated = 3 [default = false];
+ reserved 5;
+ repeated UninterpretedOption uninterpreted_option = 999;
+ extensions 1000 to max;
+}
+
+message EnumValueOptions {
+ optional bool deprecated = 1 [default = false];
+ repeated UninterpretedOption uninterpreted_option = 999;
+ extensions 1000 to max;
+}
+
+message ServiceOptions {
+ optional bool deprecated = 33 [default = false];
+ repeated UninterpretedOption uninterpreted_option = 999;
+ extensions 1000 to max;
+}
+
+message MethodOptions {
+ optional bool deprecated = 33 [default = false];
+ enum IdempotencyLevel {
+ IDEMPOTENCY_UNKNOWN = 0;
+ NO_SIDE_EFFECTS = 1;
+ IDEMPOTENT = 2;
+ }
+ optional IdempotencyLevel idempotency_level = 34
+ [default = IDEMPOTENCY_UNKNOWN];
+ repeated UninterpretedOption uninterpreted_option = 999;
+ extensions 1000 to max;
+}
+
+message UninterpretedOption {
+ message NamePart {
+ required string name_part = 1;
+ required bool is_extension = 2;
+ }
+ repeated NamePart name = 2;
+ optional string identifier_value = 3;
+ optional uint64 positive_int_value = 4;
+ optional int64 negative_int_value = 5;
+ optional double double_value = 6;
+ optional bytes string_value = 7;
+ optional string aggregate_value = 8;
+}
+
+message SourceCodeInfo {
+ repeated Location location = 1;
+ message Location {
+ repeated int32 path = 1 [packed = true];
+ repeated int32 span = 2 [packed = true];
+ optional string leading_comments = 3;
+ optional string trailing_comments = 4;
+ repeated string leading_detached_comments = 6;
+ }
+}
+
+message GeneratedCodeInfo {
+ repeated Annotation annotation = 1;
+ message Annotation {
+ repeated int32 path = 1 [packed = true];
+ optional string source_file = 2;
+ optional int32 begin = 3;
+ optional int32 end = 4;
+ }
+}
diff --git a/test/protobuf_lang_files/well_know_types/google/protobuf/timestamp.proto b/test/protobuf_lang_files/well_know_types/google/protobuf/timestamp.proto
new file mode 100644
index 0000000..397b518
--- /dev/null
+++ b/test/protobuf_lang_files/well_know_types/google/protobuf/timestamp.proto
@@ -0,0 +1,20 @@
+// This file is from https://github.com/protocolbuffers/protobuf/blob/master/src/google/protobuf/timestamp.proto
+// To reduce the file size, some comments have been removed.
+
+syntax = "proto3";
+
+package google.protobuf;
+
+message Timestamp {
+
+ // Represents seconds of UTC time since Unix epoch
+ // 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to
+ // 9999-12-31T23:59:59Z inclusive.
+ int64 seconds = 1;
+
+ // Non-negative fractions of a second at nanosecond resolution. Negative
+ // second values with fractions must still have non-negative nanos values
+ // that count forward in time. Must be from 0 to 999,999,999
+ // inclusive.
+ int32 nanos = 2;
+}
diff --git a/test/sampleif.py b/test/sampleif.py
new file mode 100755
index 0000000..4cb367c
--- /dev/null
+++ b/test/sampleif.py
@@ -0,0 +1,59 @@
+#!/usr/bin/env python3
+#
+# Wireshark test dummy extcap
+#
+# Copyright (c) 2018-2019 Peter Wu <peter@lekensteyn.nl>
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+import argparse, codecs, os, sys
+
+parser = argparse.ArgumentParser()
+
+# Actions
+parser.add_argument('--extcap-interfaces', action='store_true')
+parser.add_argument('--extcap-dlts', action='store_true')
+parser.add_argument('--extcap-config', action='store_true')
+parser.add_argument('--capture', action='store_true')
+parser.add_argument('--extcap-version')
+
+parser.add_argument('--extcap-interface', metavar='IFACE')
+
+parser.add_argument('--extcap-capture-filter', metavar='CFILTER')
+parser.add_argument('--fifo', metavar='FIFO')
+
+
+def extcap_interfaces():
+ print("extcap {version=1.0}")
+ print("interface {value=sampleif}{display=Remote dumpcap}")
+
+
+def extcap_dlts():
+ # Required for the interface to show up in the interface list
+ print("dlt {number=147}{name=USER0}{display=Remote capture dependent DLT}")
+
+
+def extcap_config():
+ print("arg {number=0}{call=--test1}{display=Remote SSH server address}{type=string}{tooltip=bla}{required=true}{group=Server}")
+ print("arg {number=1}{call=--test2}{display=[7] Urządzenie kompozytowe USB}{type=string}{tooltip=X}{group=Capture}")
+
+
+def main():
+ # In Python 3.6 and older, the encoding of stdout depends on the locale.
+ # Do not rely on that and force a sane encoding instead. Python 3.7 has
+ # improved, see https://www.python.org/dev/peps/pep-0540/
+ sys.stdout = codecs.getwriter('utf-8')(sys.stdout.detach())
+
+ args = parser.parse_args()
+ if args.extcap_interfaces:
+ return extcap_interfaces()
+
+ if args.extcap_dlts:
+ return extcap_dlts()
+ elif args.extcap_config:
+ return extcap_config()
+ else:
+ parser.error('Unsupported')
+ return 1
+
+sys.exit(main())
diff --git a/test/subprocesstest.py b/test/subprocesstest.py
new file mode 100644
index 0000000..e8e534e
--- /dev/null
+++ b/test/subprocesstest.py
@@ -0,0 +1,125 @@
+#
+# Wireshark tests
+# By Gerald Combs <gerald@wireshark.org>
+#
+# Ported from a set of Bash scripts which were copyright 2005 Ulf Lamping
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+'''Subprocess test case superclass'''
+
+import os
+import os.path
+import re
+import subprocess
+import sys
+import enum
+
+# To do:
+# - Add a subprocesstest.SkipUnlessCapture decorator?
+# - Try to catch crashes? See the comments below in waitProcess.
+
+process_timeout = 300 # Seconds
+
+class ExitCodes(enum.IntEnum):
+ OK = 0
+ COMMAND_LINE = 1
+ INVALID_INTERFACE = 2
+ INVALID_FILE_ERROR = 3
+ INVALID_FILTER_ERROR = 4
+ INVALID_CAPABILITY = 5
+ IFACE_NO_LINK_TYPES = 6
+ IFACE_HAS_NO_TIMESTAMP_TYPES = 7
+ INIT_FAILED = 8
+ OPEN_ERROR = 9
+ PCAP_NOT_SUPPORTED = 10
+ DUMPCAP_NOT_SUPPORTED = 11
+ NO_INTERFACES = 12
+ PCAP_ERROR = 13
+
+def run(*args, capture_output=False, stdout=None, stderr=None, encoding='utf-8', **kwargs):
+ ''' Wrapper for subprocess.run() that captures and decodes output.'''
+
+ # If the user told us what to do with standard streams use that.
+ if capture_output or stdout or stderr:
+ return subprocess.run(*args, capture_output=capture_output, stdout=stdout, stderr=stderr, encoding=encoding, **kwargs)
+
+ # If the user doesn't want to capture output try to ensure the child inherits the parents stdout and stderr on
+ # all platforms, so pytest can reliably capture it and do the right thing. Otherwise the child output may be interleaved
+ # on the console with the parent and mangle pytest's status output.
+ #
+ # Make the child inherit only the parents stdout and stderr.
+ return subprocess.run(*args, close_fds=True, stdout=sys.stdout, stderr=sys.stderr, encoding=encoding, **kwargs)
+
+def check_run(*args, **kwargs):
+ ''' Same as run(), also check child process returns 0 (success)'''
+ proc = run(*args, check=True, **kwargs)
+ return proc
+
+def cat_dhcp_command(mode):
+ '''Create a command string for dumping dhcp.pcap to stdout'''
+ # XXX Do this in Python in a thread?
+ sd_cmd = ''
+ if sys.executable:
+ sd_cmd = '"{}" '.format(sys.executable)
+ this_dir = os.path.dirname(__file__)
+ sd_cmd += os.path.join(this_dir, 'util_dump_dhcp_pcap.py ' + mode)
+ return sd_cmd
+
+def cat_cap_file_command(cap_files):
+ '''Create a command string for dumping one or more capture files to stdout'''
+ # XXX Do this in Python in a thread?
+ if isinstance(cap_files, str):
+ cap_files = [ cap_files ]
+ quoted_paths = ' '.join('"{}"'.format(cap_file) for cap_file in cap_files)
+ if sys.platform.startswith('win32'):
+ # https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-xp/bb491026(v=technet.10)
+ # says that the `type` command "displays the contents of a text
+ # file." Copy to the console instead.
+ return 'copy {} CON'.format(quoted_paths)
+ return 'cat {}'.format(quoted_paths)
+
+def count_output(text, search_pat=None):
+ '''Returns the number of output lines (search_pat=None), otherwise returns a match count.'''
+
+ if not text:
+ return 0
+
+ if not search_pat:
+ return len(text.splitlines())
+
+ match_count = 0
+
+ search_re = re.compile(search_pat)
+ for line in text.splitlines():
+ if search_re.search(line):
+ match_count += 1
+
+ return match_count
+
+def grep_output(text, search_pat):
+ return count_output(text, search_pat) > 0
+
+def check_packet_count(cmd_capinfos, num_packets, cap_file):
+ '''Make sure a capture file contains a specific number of packets.'''
+ got_num_packets = False
+ capinfos_testout = subprocess.run([cmd_capinfos, cap_file], capture_output=True, check=True, encoding='utf-8')
+ assert capinfos_testout.returncode == 0
+ assert capinfos_testout.stdout
+ count_pat = r'Number of packets:\s+{}'.format(num_packets)
+ if re.search(count_pat, capinfos_testout.stdout):
+ got_num_packets = True
+ assert got_num_packets, 'Failed to capture exactly {} packets'.format(num_packets)
+
+def get_capture_info(cmd_capinfos, capinfos_args, cap_file):
+ '''Run capinfos on a capture file and log its output.
+
+ capinfos_args must be a sequence.'''
+
+ capinfos_cmd = [cmd_capinfos]
+ if capinfos_args:
+ capinfos_cmd += capinfos_args
+ capinfos_cmd.append(cap_file)
+ capinfos_data = subprocess.check_output(capinfos_cmd)
+ capinfos_stdout = capinfos_data.decode('UTF-8', 'replace')
+ return capinfos_stdout
diff --git a/test/suite_capture.py b/test/suite_capture.py
new file mode 100644
index 0000000..ffcd6cd
--- /dev/null
+++ b/test/suite_capture.py
@@ -0,0 +1,599 @@
+#
+# Wireshark tests
+# By Gerald Combs <gerald@wireshark.org>
+#
+# Ported from a set of Bash scripts which were copyright 2005 Ulf Lamping
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+'''Capture tests'''
+
+import glob
+import hashlib
+import os
+import socket
+import subprocess
+import subprocesstest
+from subprocesstest import cat_dhcp_command, cat_cap_file_command, count_output, grep_output, check_packet_count
+import sys
+import threading
+import time
+import uuid
+import sysconfig
+import pytest
+
+capture_duration = 5
+
+testout_pcap = 'testout.pcap'
+testout_pcapng = 'testout.pcapng'
+snapshot_len = 96
+
+class UdpTrafficGenerator(threading.Thread):
+ def __init__(self):
+ super().__init__(daemon=True)
+ self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+ self.stopped = False
+
+ def run(self):
+ while not self.stopped:
+ time.sleep(.05)
+ self.sock.sendto(b'Wireshark test\n', ('127.0.0.1', 9))
+
+ def stop(self):
+ if not self.stopped:
+ self.stopped = True
+ self.join()
+
+
+@pytest.fixture
+def traffic_generator():
+ '''
+ Traffic generator factory. Invoking it returns a tuple (start_func, cfilter)
+ where cfilter is a capture filter to match the generated traffic.
+ start_func can be invoked to start generating traffic and returns a function
+ which can be used to stop traffic generation early.
+ Currently generates a bunch of UDP traffic to localhost.
+ '''
+ threads = []
+ def start_processes():
+ thread = UdpTrafficGenerator()
+ thread.start()
+ threads.append(thread)
+ return thread.stop
+ try:
+ yield start_processes, 'udp port 9'
+ finally:
+ for thread in threads:
+ thread.stop()
+
+
+@pytest.fixture(scope='session')
+def wireshark_k(wireshark_command):
+ return tuple(list(wireshark_command) + ['-k'])
+
+
+def capture_command(*args, shell=False):
+ cmd_args = list(args)
+ if type(cmd_args[0]) != str:
+ # Assume something like ['wireshark', '-k']
+ cmd_args = list(cmd_args[0]) + list(cmd_args)[1:]
+ if shell:
+ cmd_args = ' '.join(cmd_args)
+ return cmd_args
+
+
+@pytest.fixture
+def check_capture_10_packets(capture_interface, cmd_capinfos, traffic_generator, result_file):
+ start_traffic, cfilter = traffic_generator
+ def check_capture_10_packets_real(self, cmd=None, to_stdout=False, env=None):
+ assert cmd is not None
+ testout_file = result_file(testout_pcap)
+ stop_traffic = start_traffic()
+ if to_stdout:
+ subprocesstest.check_run(capture_command(cmd,
+ '-i', '"{}"'.format(capture_interface),
+ '-p',
+ '-w', '-',
+ '-c', '10',
+ '-a', 'duration:{}'.format(capture_duration),
+ '-f', '"{}"'.format(cfilter),
+ '>', testout_file,
+ shell=True
+ ),
+ shell=True, env=env)
+ else:
+ subprocesstest.check_run(capture_command(cmd,
+ '-i', capture_interface,
+ '-p',
+ '-w', testout_file,
+ '-c', '10',
+ '-a', 'duration:{}'.format(capture_duration),
+ '-f', cfilter,
+ ), env=env)
+ stop_traffic()
+ check_packet_count(cmd_capinfos, 10, testout_file)
+ return check_capture_10_packets_real
+
+
+@pytest.fixture
+def check_capture_fifo(cmd_capinfos, result_file):
+ if sys.platform == 'win32':
+ pytest.skip('Test requires OS fifo support.')
+
+ def check_capture_fifo_real(self, cmd=None, env=None):
+ assert cmd is not None
+ testout_file = result_file(testout_pcap)
+ fifo_file = result_file('testout.fifo')
+ try:
+ # If a previous test left its fifo laying around, e.g. from a failure, remove it.
+ os.unlink(fifo_file)
+ except Exception:
+ pass
+ os.mkfifo(fifo_file)
+ slow_dhcp_cmd = cat_dhcp_command('slow')
+ fifo_proc = subprocess.Popen(
+ ('{0} > {1}'.format(slow_dhcp_cmd, fifo_file)),
+ shell=True)
+ subprocesstest.check_run(capture_command(cmd,
+ '-i', fifo_file,
+ '-p',
+ '-w', testout_file,
+ '-a', 'duration:{}'.format(capture_duration),
+ ), env=env)
+ fifo_proc.kill()
+ assert os.path.isfile(testout_file)
+ check_packet_count(cmd_capinfos, 8, testout_file)
+ return check_capture_fifo_real
+
+
+@pytest.fixture
+def check_capture_stdin(cmd_capinfos, result_file):
+ # Capturing always requires dumpcap, hence the dependency on it.
+ def check_capture_stdin_real(self, cmd=None, env=None):
+ # Similar to suite_io.check_io_4_packets.
+ assert cmd is not None
+ testout_file = result_file(testout_pcap)
+ slow_dhcp_cmd = cat_dhcp_command('slow')
+ capture_cmd = capture_command(cmd,
+ '-i', '-',
+ '-w', testout_file,
+ '-a', 'duration:{}'.format(capture_duration),
+ shell=True
+ )
+ is_gui = type(cmd) != str and '-k' in cmd[0]
+ if is_gui:
+ capture_cmd += ' --log-level=info'
+ if sysconfig.get_platform().startswith('mingw'):
+ pytest.skip('FIXME Pipes are broken with the MSYS2 shell')
+ pipe_proc = subprocesstest.check_run(slow_dhcp_cmd + ' | ' + capture_cmd, shell=True, capture_output=True, env=env)
+ if is_gui:
+ # Wireshark uses stdout and not stderr for diagnostic messages
+ # XXX: Confirm this
+ assert grep_output(pipe_proc.stdout, 'Wireshark is up and ready to go'), 'No startup message.'
+ assert grep_output(pipe_proc.stdout, 'Capture started'), 'No capture start message.'
+ assert grep_output(pipe_proc.stdout, 'Capture stopped'), 'No capture stop message.'
+ assert os.path.isfile(testout_file)
+ check_packet_count(cmd_capinfos, 8, testout_file)
+ return check_capture_stdin_real
+
+
+@pytest.fixture
+def check_capture_read_filter(capture_interface, traffic_generator, cmd_capinfos, result_file):
+ start_traffic, cfilter = traffic_generator
+ def check_capture_read_filter_real(self, cmd=None, env=None):
+ assert cmd is not None
+ testout_file = result_file(testout_pcap)
+ stop_traffic = start_traffic()
+ subprocesstest.check_run(capture_command(cmd,
+ '-i', capture_interface,
+ '-p',
+ '-w', testout_file,
+ '-2',
+ '-R', 'dcerpc.cn_call_id==123456', # Something unlikely.
+ '-c', '10',
+ '-a', 'duration:{}'.format(capture_duration),
+ '-f', cfilter,
+ ), env=env)
+ stop_traffic()
+ check_packet_count(cmd_capinfos, 0, testout_file)
+ return check_capture_read_filter_real
+
+@pytest.fixture
+def check_capture_snapshot_len(capture_interface, cmd_tshark, traffic_generator, cmd_capinfos, result_file):
+ start_traffic, cfilter = traffic_generator
+ def check_capture_snapshot_len_real(self, cmd=None, env=None):
+ assert cmd is not None
+ stop_traffic = start_traffic()
+ testout_file = result_file(testout_pcap)
+ subprocesstest.check_run(capture_command(cmd,
+ '-i', capture_interface,
+ '-p',
+ '-w', testout_file,
+ '-s', str(snapshot_len),
+ '-a', 'duration:{}'.format(capture_duration),
+ '-f', cfilter,
+ ), env=env)
+ stop_traffic()
+ assert os.path.isfile(testout_file)
+
+ # Use tshark to filter out all packets larger than 68 bytes.
+ testout2_file = result_file('testout2.pcap')
+
+ subprocesstest.check_run((cmd_tshark,
+ '-r', testout_file,
+ '-w', testout2_file,
+ '-Y', 'frame.cap_len>{}'.format(snapshot_len),
+ ), env=env)
+ check_packet_count(cmd_capinfos, 0, testout2_file)
+ return check_capture_snapshot_len_real
+
+
+@pytest.fixture
+def check_dumpcap_autostop_stdin(cmd_dumpcap, cmd_capinfos, result_file):
+ def check_dumpcap_autostop_stdin_real(self, packets=None, filesize=None, env=None):
+ # Similar to check_capture_stdin.
+ testout_file = result_file(testout_pcap)
+ cat100_dhcp_cmd = cat_dhcp_command('cat100')
+ condition='oops:invalid'
+
+ if packets is not None:
+ condition = 'packets:{}'.format(packets)
+ elif filesize is not None:
+ condition = 'filesize:{}'.format(filesize)
+ else:
+ raise AssertionError('Need one of packets or filesize')
+
+ cmd_ = '"{}"'.format(cmd_dumpcap)
+ capture_cmd = ' '.join((cmd_,
+ '-i', '-',
+ '-w', testout_file,
+ '-a', condition,
+ ))
+ if sysconfig.get_platform().startswith('mingw'):
+ pytest.skip('FIXME Pipes are broken with the MSYS2 shell')
+ subprocesstest.check_run(cat100_dhcp_cmd + ' | ' + capture_cmd, shell=True, env=env)
+ assert os.path.isfile(testout_file)
+
+ if packets is not None:
+ check_packet_count(cmd_capinfos, packets, testout_file)
+ elif filesize is not None:
+ capturekb = os.path.getsize(testout_file) / 1000
+ assert capturekb >= filesize
+ return check_dumpcap_autostop_stdin_real
+
+
+@pytest.fixture
+def check_dumpcap_ringbuffer_stdin(cmd_dumpcap, cmd_capinfos, result_file):
+ def check_dumpcap_ringbuffer_stdin_real(self, packets=None, filesize=None, env=None):
+ # Similar to check_capture_stdin.
+ rb_unique = 'dhcp_rb_' + uuid.uuid4().hex[:6] # Random ID
+ testout_file = result_file('testout.{}.pcapng'.format(rb_unique))
+ testout_glob = result_file('testout.{}_*.pcapng'.format(rb_unique))
+ cat100_dhcp_cmd = cat_dhcp_command('cat100')
+ condition='oops:invalid'
+
+ if packets is not None:
+ condition = 'packets:{}'.format(packets)
+ elif filesize is not None:
+ condition = 'filesize:{}'.format(filesize)
+ else:
+ raise AssertionError('Need one of packets or filesize')
+
+ cmd_ = '"{}"'.format(cmd_dumpcap)
+ capture_cmd = ' '.join((cmd_,
+ '-i', '-',
+ '-w', testout_file,
+ '-a', 'files:2',
+ '-b', condition,
+ ))
+ if sysconfig.get_platform().startswith('mingw'):
+ pytest.skip('FIXME Pipes are broken with the MSYS2 shell')
+ subprocesstest.check_run(cat100_dhcp_cmd + ' | ' + capture_cmd, shell=True, env=env)
+
+ rb_files = glob.glob(testout_glob)
+ assert len(rb_files) == 2
+
+ for rbf in rb_files:
+ assert os.path.isfile(rbf)
+ if packets is not None:
+ check_packet_count(cmd_capinfos, packets, rbf)
+ elif filesize is not None:
+ capturekb = os.path.getsize(rbf) / 1000
+ assert capturekb >= filesize
+ return check_dumpcap_ringbuffer_stdin_real
+
+
+@pytest.fixture
+def check_dumpcap_pcapng_sections(cmd_dumpcap, cmd_tshark, cmd_capinfos, capture_file, result_file):
+ if sys.platform == 'win32':
+ pytest.skip('Test requires OS fifo support.')
+ def check_dumpcap_pcapng_sections_real(self, multi_input=False, multi_output=False, env=None):
+ # Make sure we always test multiple SHBs in an input.
+ in_files_l = [ [
+ capture_file('many_interfaces.pcapng.1'),
+ capture_file('many_interfaces.pcapng.2')
+ ] ]
+ if multi_input:
+ in_files_l.append([ capture_file('many_interfaces.pcapng.3') ])
+ fifo_files = []
+ fifo_procs = []
+ # Default values for our validity tests
+ check_val_d = {
+ 'filename': None,
+ 'packet_count': 0,
+ 'idb_count': 0,
+ 'ua_pt1_count': 0,
+ 'ua_pt2_count': 0,
+ 'ua_pt3_count': 0,
+ 'ua_dc_count': 0,
+ }
+ check_vals = [ check_val_d ]
+
+ for in_files in in_files_l:
+ fifo_file = result_file('dumpcap_pcapng_sections_{}.fifo'.format(len(fifo_files) + 1))
+ fifo_files.append(fifo_file)
+ # If a previous test left its fifo laying around, e.g. from a failure, remove it.
+ try:
+ os.unlink(fifo_file)
+ except Exception: pass
+ os.mkfifo(fifo_file)
+ cat_cmd = cat_cap_file_command(in_files)
+ fifo_procs.append(subprocess.Popen(('{0} > {1}'.format(cat_cmd, fifo_file)), shell=True))
+
+ if multi_output:
+ rb_unique = 'sections_rb_' + uuid.uuid4().hex[:6] # Random ID
+ testout_file = result_file('testout.{}.pcapng'.format(rb_unique))
+ testout_glob = result_file('testout.{}_*.pcapng'.format(rb_unique))
+ check_vals.append(check_val_d.copy())
+ # check_vals[]['filename'] will be filled in below
+ else:
+ testout_file = result_file(testout_pcapng)
+ check_vals[0]['filename'] = testout_file
+
+ # Capture commands
+ if not multi_input and not multi_output:
+ # Passthrough SHBs, single output file
+ capture_cmd_args = (
+ '-i', fifo_files[0],
+ '-w', testout_file
+ )
+ check_vals[0]['packet_count'] = 79
+ check_vals[0]['idb_count'] = 22
+ check_vals[0]['ua_pt1_count'] = 1
+ check_vals[0]['ua_pt2_count'] = 1
+ elif not multi_input and multi_output:
+ # Passthrough SHBs, multiple output files
+ capture_cmd_args = (
+ '-i', fifo_files[0],
+ '-w', testout_file,
+ '-a', 'files:2',
+ '-b', 'packets:53'
+ )
+ check_vals[0]['packet_count'] = 53
+ check_vals[0]['idb_count'] = 11
+ check_vals[0]['ua_pt1_count'] = 1
+ check_vals[1]['packet_count'] = 26
+ check_vals[1]['idb_count'] = 22
+ check_vals[1]['ua_pt1_count'] = 1
+ check_vals[1]['ua_pt2_count'] = 1
+ elif multi_input and not multi_output:
+ # Dumpcap SHBs, single output file
+ capture_cmd_args = (
+ '-i', fifo_files[0],
+ '-i', fifo_files[1],
+ '-w', testout_file
+ )
+ check_vals[0]['packet_count'] = 88
+ check_vals[0]['idb_count'] = 33
+ check_vals[0]['ua_dc_count'] = 1
+ else:
+ # Dumpcap SHBs, multiple output files
+ capture_cmd_args = (
+ '-i', fifo_files[0],
+ '-i', fifo_files[1],
+ '-w', testout_file,
+ '-a', 'files:2',
+ '-b', 'packets:53'
+ )
+ check_vals[0]['packet_count'] = 53
+ check_vals[0]['idb_count'] = 11
+ check_vals[0]['ua_dc_count'] = 1
+ check_vals[1]['packet_count'] = 35
+ check_vals[1]['idb_count'] = 33
+ check_vals[1]['ua_dc_count'] = 1
+
+ capture_cmd = capture_command(cmd_dumpcap, *capture_cmd_args)
+
+ subprocesstest.check_run(capture_cmd, env=env)
+ for fifo_proc in fifo_procs: fifo_proc.kill()
+
+ rb_files = []
+ if multi_output:
+ rb_files = sorted(glob.glob(testout_glob))
+ assert len(rb_files) == 2
+ check_vals[0]['filename'] = rb_files[0]
+ check_vals[1]['filename'] = rb_files[1]
+
+ for rbf in rb_files:
+ assert os.path.isfile(rbf)
+
+ # Output tests
+
+ if not multi_input and not multi_output:
+ # Check strict bit-for-bit passthrough.
+ in_hash = hashlib.sha256()
+ out_hash = hashlib.sha256()
+ for in_file in in_files_l[0]:
+ in_cap_file = capture_file(in_file)
+ with open(in_cap_file, 'rb') as f:
+ in_hash.update(f.read())
+ with open(testout_file, 'rb') as f:
+ out_hash.update(f.read())
+ assert in_hash.hexdigest() == out_hash.hexdigest()
+
+ # many_interfaces.pcapng.1 : 64 packets written by "Passthrough test #1"
+ # many_interfaces.pcapng.2 : 15 packets written by "Passthrough test #2"
+ # many_interfaces.pcapng.3 : 9 packets written by "Passthrough test #3"
+ # Each has 11 interfaces.
+ idb_compare_eq = True
+ if multi_input and multi_output:
+ # Having multiple inputs forces the use of threads. In our
+ # case this means that non-packet block counts in the first
+ # file in is nondeterministic.
+ idb_compare_eq = False
+ for check_val in check_vals:
+ check_packet_count(cmd_capinfos, check_val['packet_count'], check_val['filename'])
+
+ tshark_proc = subprocesstest.check_run(capture_command(cmd_tshark,
+ '-r', check_val['filename'],
+ '-V',
+ '-X', 'read_format:MIME Files Format'
+ ), capture_output=True, env=env)
+ # XXX Are there any other sanity checks we should run?
+ if idb_compare_eq:
+ assert count_output(tshark_proc.stdout, r'Block \d+: Interface Description Block \d+') \
+ == check_val['idb_count']
+ else:
+ assert count_output(tshark_proc.stdout, r'Block \d+: Interface Description Block \d+') \
+ >= check_val['idb_count']
+ idb_compare_eq = True
+ assert count_output(tshark_proc.stdout, r'Option: User Application = Passthrough test #1') \
+ == check_val['ua_pt1_count']
+ assert count_output(tshark_proc.stdout, r'Option: User Application = Passthrough test #2') \
+ == check_val['ua_pt2_count']
+ assert count_output(tshark_proc.stdout, r'Option: User Application = Passthrough test #3') \
+ == check_val['ua_pt3_count']
+ assert count_output(tshark_proc.stdout, r'Option: User Application = Dumpcap \(Wireshark\)') \
+ == check_val['ua_dc_count']
+ return check_dumpcap_pcapng_sections_real
+
+
+class TestWiresharkCapture:
+ def test_wireshark_capture_10_packets_to_file(self, request, wireshark_k, check_capture_10_packets, make_screenshot_on_error, test_env):
+ '''Capture 10 packets from the network to a file using Wireshark'''
+ disabled = request.config.getoption('--disable-gui', default=False)
+ if disabled:
+ pytest.skip('GUI tests are disabled via --disable-gui')
+ with make_screenshot_on_error():
+ check_capture_10_packets(self, cmd=wireshark_k, env=test_env)
+
+ # Wireshark doesn't currently support writing to stdout while capturing.
+ # def test_wireshark_capture_10_packets_to_stdout(self, wireshark_k, check_capture_10_packets):
+ # '''Capture 10 packets from the network to stdout using Wireshark'''
+ # check_capture_10_packets(self, cmd=wireshark_k, to_stdout=True)
+
+ def test_wireshark_capture_from_fifo(self, request, wireshark_k, check_capture_fifo, make_screenshot_on_error, test_env):
+ '''Capture from a fifo using Wireshark'''
+ disabled = request.config.getoption('--disable-gui', default=False)
+ if disabled:
+ pytest.skip('GUI tests are disabled via --disable-gui')
+ with make_screenshot_on_error():
+ check_capture_fifo(self, cmd=wireshark_k, env=test_env)
+
+ def test_wireshark_capture_from_stdin(self, request, wireshark_k, check_capture_stdin, make_screenshot_on_error, test_env):
+ '''Capture from stdin using Wireshark'''
+ disabled = request.config.getoption('--disable-gui', default=False)
+ if disabled:
+ pytest.skip('GUI tests are disabled via --disable-gui')
+ with make_screenshot_on_error():
+ check_capture_stdin(self, cmd=wireshark_k, env=test_env)
+
+ def test_wireshark_capture_snapshot_len(self, request, wireshark_k, check_capture_snapshot_len, make_screenshot_on_error, test_env):
+ '''Capture truncated packets using Wireshark'''
+ disabled = request.config.getoption('--disable-gui', default=False)
+ if disabled:
+ pytest.skip('GUI tests are disabled via --disable-gui')
+ with make_screenshot_on_error():
+ check_capture_snapshot_len(self, cmd=wireshark_k, env=test_env)
+
+
+class TestTsharkCapture:
+ def test_tshark_capture_10_packets_to_file(self, cmd_tshark, check_capture_10_packets, test_env):
+ '''Capture 10 packets from the network to a file using TShark'''
+ check_capture_10_packets(self, cmd=cmd_tshark, env=test_env)
+
+ def test_tshark_capture_10_packets_to_stdout(self, cmd_tshark, check_capture_10_packets, test_env):
+ '''Capture 10 packets from the network to stdout using TShark'''
+ check_capture_10_packets(self, cmd=cmd_tshark, to_stdout=True, env=test_env)
+
+ def test_tshark_capture_from_fifo(self, cmd_tshark, check_capture_fifo, test_env):
+ '''Capture from a fifo using TShark'''
+ check_capture_fifo(self, cmd=cmd_tshark, env=test_env)
+
+ def test_tshark_capture_from_stdin(self, cmd_tshark, check_capture_stdin, test_env):
+ '''Capture from stdin using TShark'''
+ check_capture_stdin(self, cmd=cmd_tshark, env=test_env)
+
+ def test_tshark_capture_snapshot_len(self, cmd_tshark, check_capture_snapshot_len, test_env):
+ '''Capture truncated packets using TShark'''
+ check_capture_snapshot_len(self, cmd=cmd_tshark, env=test_env)
+
+
+class TestDumpcapCapture:
+ def test_dumpcap_capture_10_packets_to_file(self, cmd_dumpcap, check_capture_10_packets, base_env):
+ '''Capture 10 packets from the network to a file using Dumpcap'''
+ check_capture_10_packets(self, cmd=cmd_dumpcap, env=base_env)
+
+ def test_dumpcap_capture_10_packets_to_stdout(self, cmd_dumpcap, check_capture_10_packets, base_env):
+ '''Capture 10 packets from the network to stdout using Dumpcap'''
+ check_capture_10_packets(self, cmd=cmd_dumpcap, to_stdout=True, env=base_env)
+
+ def test_dumpcap_capture_from_fifo(self, cmd_dumpcap, check_capture_fifo, base_env):
+ '''Capture from a fifo using Dumpcap'''
+ check_capture_fifo(self, cmd=cmd_dumpcap, env=base_env)
+
+ def test_dumpcap_capture_from_stdin(self, cmd_dumpcap, check_capture_stdin, base_env):
+ '''Capture from stdin using Dumpcap'''
+ check_capture_stdin(self, cmd=cmd_dumpcap, env=base_env)
+
+ def test_dumpcap_capture_snapshot_len(self, check_capture_snapshot_len, cmd_dumpcap, base_env):
+ '''Capture truncated packets using Dumpcap'''
+ check_capture_snapshot_len(self, cmd=cmd_dumpcap, env=base_env)
+
+
+class TestDumpcapAutostop:
+ # duration, filesize, packets, files
+ def test_dumpcap_autostop_filesize(self, check_dumpcap_autostop_stdin, base_env):
+ '''Capture from stdin using Dumpcap until we reach a file size limit'''
+ check_dumpcap_autostop_stdin(self, filesize=15, env=base_env)
+
+ def test_dumpcap_autostop_packets(self, check_dumpcap_autostop_stdin, base_env):
+ '''Capture from stdin using Dumpcap until we reach a packet limit'''
+ check_dumpcap_autostop_stdin(self, packets=97, env=base_env) # Last prime before 100. Arbitrary.
+
+
+class TestDumpcapRingbuffer:
+ # duration, interval, filesize, packets, files
+ def test_dumpcap_ringbuffer_filesize(self, check_dumpcap_ringbuffer_stdin, base_env):
+ '''Capture from stdin using Dumpcap and write multiple files until we reach a file size limit'''
+ check_dumpcap_ringbuffer_stdin(self, filesize=15, env=base_env)
+
+ def test_dumpcap_ringbuffer_packets(self, check_dumpcap_ringbuffer_stdin, base_env):
+ '''Capture from stdin using Dumpcap and write multiple files until we reach a packet limit'''
+ check_dumpcap_ringbuffer_stdin(self, packets=47, env=base_env) # Last prime before 50. Arbitrary.
+
+
+class TestDumpcapPcapngSections:
+ def test_dumpcap_pcapng_single_in_single_out(self, check_dumpcap_pcapng_sections, base_env):
+ '''Capture from a single pcapng source using Dumpcap and write a single file'''
+ if sys.byteorder == 'big':
+ pytest.skip('this test is supported on little endian only')
+ check_dumpcap_pcapng_sections(self, env=base_env)
+
+ def test_dumpcap_pcapng_single_in_multi_out(self, check_dumpcap_pcapng_sections, base_env):
+ '''Capture from a single pcapng source using Dumpcap and write two files'''
+ if sys.byteorder == 'big':
+ pytest.skip('this test is supported on little endian only')
+ check_dumpcap_pcapng_sections(self, multi_output=True, env=base_env)
+
+ def test_dumpcap_pcapng_multi_in_single_out(self, check_dumpcap_pcapng_sections, base_env):
+ '''Capture from two pcapng sources using Dumpcap and write a single file'''
+ if sys.byteorder == 'big':
+ pytest.skip('this test is supported on little endian only')
+ check_dumpcap_pcapng_sections(self, multi_input=True, env=base_env)
+
+ def test_dumpcap_pcapng_multi_in_multi_out(self, check_dumpcap_pcapng_sections, base_env):
+ '''Capture from two pcapng sources using Dumpcap and write two files'''
+ if sys.byteorder == 'big':
+ pytest.skip('this test is supported on little endian only')
+ check_dumpcap_pcapng_sections(self, multi_input=True, multi_output=True, env=base_env)
diff --git a/test/suite_clopts.py b/test/suite_clopts.py
new file mode 100644
index 0000000..55686b2
--- /dev/null
+++ b/test/suite_clopts.py
@@ -0,0 +1,329 @@
+#
+# Wireshark tests
+# By Gerald Combs <gerald@wireshark.org>
+#
+# Ported from a set of Bash scripts which were copyright 2005 Ulf Lamping
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+'''Command line option tests'''
+
+import json
+import sys
+import os.path
+import subprocess
+import subprocesstest
+from subprocesstest import ExitCodes, grep_output, count_output
+import shutil
+import pytest
+
+#glossaries = ('fields', 'protocols', 'values', 'decodes', 'defaultprefs', 'currentprefs')
+
+glossaries = ('decodes', 'values')
+testout_pcap = 'testout.pcap'
+
+
+class TestDumpcapOptions:
+ # XXX Should we generate individual test functions instead of looping?
+ def test_dumpcap_invalid_chars(self, cmd_dumpcap, base_env):
+ '''Invalid dumpcap parameters'''
+ for char_arg in 'CEFGHJKNOQRTUVWXYejloxz':
+ process = subprocesstest.run((cmd_dumpcap, '-' + char_arg), env=base_env)
+ assert process.returncode == ExitCodes.COMMAND_LINE
+
+ # XXX Should we generate individual test functions instead of looping?
+ def test_dumpcap_valid_chars(self, cmd_dumpcap, base_env):
+ for char_arg in 'hv':
+ process = subprocesstest.run((cmd_dumpcap, '-' + char_arg), env=base_env)
+ assert process.returncode == ExitCodes.OK
+
+ # XXX Should we generate individual test functions instead of looping?
+ def test_dumpcap_interface_chars(self, cmd_dumpcap, base_env):
+ '''Valid dumpcap parameters requiring capture permissions'''
+ valid_returns = [ExitCodes.OK, ExitCodes.INVALID_INTERFACE]
+ for char_arg in 'DL':
+ process = subprocesstest.run((cmd_dumpcap, '-' + char_arg), env=base_env)
+ assert process.returncode in valid_returns
+
+
+class TestDumpcapClopts:
+ def test_dumpcap_invalid_capfilter(self, cmd_dumpcap, capture_interface, result_file, base_env):
+ '''Invalid capture filter'''
+ invalid_filter = '__invalid_protocol'
+ # $DUMPCAP -f 'jkghg' -w './testout.pcap' > ./testout.txt 2>&1
+ testout_file = result_file(testout_pcap)
+ process = subprocesstest.run((cmd_dumpcap, '-f', invalid_filter, '-w', testout_file), capture_output=True, env=base_env)
+ assert grep_output(process.stderr, 'Invalid capture filter "' + invalid_filter + '" for interface')
+
+ def test_dumpcap_invalid_interface_name(self, cmd_dumpcap, capture_interface, result_file, base_env):
+ '''Invalid capture interface name'''
+ invalid_interface = '__invalid_interface'
+ # $DUMPCAP -i invalid_interface -w './testout.pcap' > ./testout.txt 2>&1
+ testout_file = result_file(testout_pcap)
+ process = subprocesstest.run((cmd_dumpcap, '-i', invalid_interface, '-w', testout_file), capture_output=True, env=base_env)
+ assert grep_output(process.stderr, 'There is no device named "__invalid_interface"') or \
+ grep_output(process.stderr, 'The capture session could not be initiated on capture device "__invalid_interface"')
+
+ def test_dumpcap_invalid_interface_index(self, cmd_dumpcap, capture_interface, result_file, base_env):
+ '''Invalid capture interface index'''
+ invalid_index = '0'
+ # $DUMPCAP -i 0 -w './testout.pcap' > ./testout.txt 2>&1
+ testout_file = result_file(testout_pcap)
+ process = subprocesstest.run((cmd_dumpcap, '-i', invalid_index, '-w', testout_file), capture_output=True, env=base_env)
+ assert grep_output(process.stderr, 'There is no interface with that adapter index')
+
+
+class TestBasicClopts:
+ def test_existing_file(self, cmd_tshark, capture_file, test_env):
+ # $TSHARK -r "${CAPTURE_DIR}dhcp.pcap" > ./testout.txt 2>&1
+ process = subprocesstest.run((cmd_tshark, '-r', capture_file('dhcp.pcap')), env=test_env)
+ assert process.returncode == ExitCodes.OK
+
+ def test_nonexistent_file(self, cmd_tshark, capture_file, test_env):
+ # $TSHARK - r ThisFileDontExist.pcap > ./testout.txt 2 > &1
+ process = subprocesstest.run((cmd_tshark, '-r', capture_file('__ceci_nest_pas_une.pcap')), env=test_env)
+ assert process.returncode == ExitCodes.INVALID_FILE_ERROR
+
+
+class TestTsharkOptions:
+ # XXX Should we generate individual test functions instead of looping?
+ def test_tshark_invalid_chars(self, cmd_tshark, test_env):
+ '''Invalid tshark parameters'''
+ for char_arg in 'ABCEFHJKMNORTUWXYZabcdefijkmorstuwyz':
+ process = subprocesstest.run((cmd_tshark, '-' + char_arg), env=test_env)
+ assert process.returncode == ExitCodes.COMMAND_LINE
+
+ # XXX Should we generate individual test functions instead of looping?
+ def test_tshark_valid_chars(self, cmd_tshark, test_env):
+ for char_arg in 'Ghv':
+ process = subprocesstest.run((cmd_tshark, '-' + char_arg), env=test_env)
+ assert process.returncode == ExitCodes.OK
+
+ # XXX Should we generate individual test functions instead of looping?
+ def test_tshark_interface_chars(self, cmd_tshark, cmd_dumpcap, test_env):
+ '''Valid tshark parameters requiring capture permissions'''
+ # These options require dumpcap, but may fail with a pacp error
+ # if WinPcap or Npcap are not present
+ valid_returns = [ExitCodes.OK, ExitCodes.PCAP_ERROR, ExitCodes.INVALID_CAPABILITY]
+ for char_arg in 'DL':
+ process = subprocesstest.run((cmd_tshark, '-' + char_arg), env=test_env)
+ assert process.returncode in valid_returns
+
+ def test_tshark_disable_protos(self, cmd_tshark, capture_file, test_env):
+ '''--disable-protocol/--enable-protocol from !16923'''
+ process = subprocesstest.run((cmd_tshark, "-r", capture_file("http.pcap"),
+ "--disable-protocol", "ALL",
+ "--enable-protocol", "eth,ip",
+ "-Tjson", "-eeth.type", "-eip.proto", "-ehttp.host",
+ ), capture_output=True, env=test_env)
+ assert process.returncode == ExitCodes.OK
+ obj = json.loads(process.stdout)[0]['_source']['layers']
+ assert obj.get('eth.type', 'NOT FOUND') == ['0x0800']
+ assert obj.get('ip.proto', 'NOT FOUND') == ['6']
+ assert obj.get('http.host', 'NOT FOUND') == 'NOT FOUND'
+
+
+class TestTsharkCaptureClopts:
+ def test_tshark_invalid_capfilter(self, cmd_tshark, capture_interface, result_file, test_env):
+ '''Invalid capture filter'''
+ invalid_filter = '__invalid_protocol'
+ # $TSHARK -f 'jkghg' -w './testout.pcap' > ./testout.txt 2>&1
+ testout_file = result_file(testout_pcap)
+ process = subprocesstest.run((cmd_tshark, '-f', invalid_filter, '-w', testout_file ), capture_output=True, env=test_env)
+ assert grep_output(process.stderr, 'Invalid capture filter "' + invalid_filter + '" for interface')
+
+ def test_tshark_invalid_interface_name(self, cmd_tshark, capture_interface, result_file, test_env):
+ '''Invalid capture interface name'''
+ invalid_interface = '__invalid_interface'
+ # $TSHARK -i invalid_interface -w './testout.pcap' > ./testout.txt 2>&1
+ testout_file = result_file(testout_pcap)
+ process = subprocesstest.run((cmd_tshark, '-i', invalid_interface, '-w', testout_file), capture_output=True, env=test_env)
+ assert grep_output(process.stderr, 'There is no device named "__invalid_interface"') or \
+ grep_output(process.stderr, 'The capture session could not be initiated on capture device "__invalid_interface"')
+
+ def test_tshark_invalid_interface_index(self, cmd_tshark, capture_interface, result_file, test_env):
+ '''Invalid capture interface index'''
+ invalid_index = '0'
+ # $TSHARK -i 0 -w './testout.pcap' > ./testout.txt 2>&1
+ testout_file = result_file(testout_pcap)
+ process = subprocesstest.run((cmd_tshark, '-i', invalid_index, '-w', testout_file), capture_output=True, env=test_env)
+ assert grep_output(process.stderr, 'There is no interface with that adapter index')
+
+
+class TestTsharkNameResolutionClopts:
+ def test_tshark_valid_name_resolution(self, cmd_tshark, capture_file, test_env):
+ # $TSHARK -N mnNtdv -a duration:1 > ./testout.txt 2>&1
+ process = subprocesstest.run((cmd_tshark,
+ '-r', capture_file('empty.pcap'),
+ '-N', 'mnNtdv',
+ ), env=test_env)
+ assert process.returncode == 0
+
+ # XXX Add invalid name resolution.
+
+class TestTsharkUnicodeClopts:
+ def test_tshark_unicode_display_filter(self, cmd_tshark, capture_file, test_env):
+ '''Unicode (UTF-8) display filter'''
+ process = subprocesstest.run((cmd_tshark, '-r', capture_file('http.pcap'), '-Y', 'tcp.flags.str == "·······AP···"'), capture_output=True, env=test_env)
+ assert grep_output(process.stdout, 'HEAD.*/v4/iuident.cab')
+
+
+class TestTsharkDumpGlossaries:
+ def test_tshark_dump_glossary(self, cmd_tshark, base_env):
+ for glossary in glossaries:
+ process = subprocesstest.run((cmd_tshark, '-G', glossary), capture_output=True, env=base_env)
+ assert not process.stderr, 'Found error output while printing glossary ' + glossary
+
+ def test_tshark_glossary_valid_utf8(self, cmd_tshark, base_env):
+ for glossary in glossaries:
+ env = base_env
+ env['LANG'] = 'en_US.UTF-8'
+ # subprocess.run() returns bytes here.
+ proc = subprocess.run((cmd_tshark, '-G', glossary), capture_output=True, env=env)
+ assert proc.returncode == 0
+ proc.stdout.decode('UTF-8')
+
+ def test_tshark_glossary_plugin_count(self, cmd_tshark, base_env, features):
+ if not features.have_plugins:
+ pytest.skip('Test requires binary plugin support.')
+ process = subprocesstest.run((cmd_tshark, '-G', 'plugins'), capture_output=True, env=base_env)
+ assert count_output(process.stdout, 'dissector') >= 10, 'Fewer than 10 dissector plugins found'
+
+ def test_tshark_elastic_mapping(self, cmd_tshark, dirs, base_env):
+ def get_ip_props(obj):
+ return obj['mappings']['properties']['layers']['properties']['ip']['properties']
+ baseline_file = os.path.join(dirs.baseline_dir, 'elastic-mapping-ip-subset.json')
+ with open(baseline_file) as f:
+ expected_obj = json.load(f)
+ keys_to_check = get_ip_props(expected_obj).keys()
+ proc = subprocesstest.run((cmd_tshark, '-G', 'elastic-mapping', '--elastic-mapping-filter', 'ip'), capture_output=True, env=base_env)
+ actual_obj = json.loads(proc.stdout)
+ ip_props = get_ip_props(actual_obj)
+ for key in list(ip_props.keys()):
+ if key not in keys_to_check:
+ del ip_props[key]
+ assert actual_obj == expected_obj
+
+ def test_tshark_unicode_folders(self, cmd_tshark, unicode_env, features):
+ '''Folders output with unicode'''
+ if not features.have_lua:
+ pytest.skip('Test requires Lua scripting support.')
+ if sys.platform == 'win32' and not features.have_lua_unicode:
+ pytest.skip('Test requires a patched Lua build with UTF-8 support.')
+ proc = subprocesstest.run((cmd_tshark, '-G', 'folders'), capture_output=True, env=unicode_env.env)
+ out = proc.stdout
+ pluginsdir = [x.split('\t', 1)[1] for x in out.splitlines() if x.startswith('Personal Lua Plugins:')]
+ assert [unicode_env.pluginsdir] == pluginsdir
+
+
+class TestTsharkZExpert:
+ def test_tshark_z_expert_all(self, cmd_tshark, capture_file, test_env):
+ proc = subprocesstest.run((cmd_tshark, '-q', '-z', 'expert',
+ '-o', 'tcp.check_checksum:TRUE',
+ '-r', capture_file('http-ooo-fuzzed.pcapng')), capture_output=True, env=test_env)
+ # http2-data-reassembly.pcap has Errors, Warnings, Notes, and Chats
+ # when TCP checksum are verified.
+ assert grep_output(proc.stdout, 'Errors')
+ assert grep_output(proc.stdout, 'Warns')
+ assert grep_output(proc.stdout, 'Notes')
+ assert grep_output(proc.stdout, 'Chats')
+
+ def test_tshark_z_expert_error(self, cmd_tshark, capture_file, test_env):
+ proc = subprocesstest.run((cmd_tshark, '-q', '-z', 'expert,error',
+ '-o', 'tcp.check_checksum:TRUE',
+ '-r', capture_file('http-ooo-fuzzed.pcapng')), capture_output=True, env=test_env)
+ assert grep_output(proc.stdout, 'Errors')
+ assert not grep_output(proc.stdout, 'Warns')
+ assert not grep_output(proc.stdout, 'Notes')
+ assert not grep_output(proc.stdout, 'Chats')
+
+ def test_tshark_z_expert_warn(self, cmd_tshark, capture_file, test_env):
+ proc = subprocesstest.run((cmd_tshark, '-q', '-z', 'expert,warn',
+ '-o', 'tcp.check_checksum:TRUE',
+ '-r', capture_file('http-ooo-fuzzed.pcapng')), capture_output=True, env=test_env)
+ assert grep_output(proc.stdout, 'Errors')
+ assert grep_output(proc.stdout, 'Warns')
+ assert not grep_output(proc.stdout, 'Notes')
+ assert not grep_output(proc.stdout, 'Chats')
+
+ def test_tshark_z_expert_note(self, cmd_tshark, capture_file, test_env):
+ proc = subprocesstest.run((cmd_tshark, '-q', '-z', 'expert,note',
+ '-o', 'tcp.check_checksum:TRUE',
+ '-r', capture_file('http-ooo-fuzzed.pcapng')), capture_output=True, env=test_env)
+ assert grep_output(proc.stdout, 'Errors')
+ assert grep_output(proc.stdout, 'Warns')
+ assert grep_output(proc.stdout, 'Notes')
+ assert not grep_output(proc.stdout, 'Chats')
+
+ def test_tshark_z_expert_chat(self, cmd_tshark, capture_file, test_env):
+ proc = subprocesstest.run((cmd_tshark, '-q', '-z', 'expert,chat',
+ '-o', 'tcp.check_checksum:TRUE',
+ '-r', capture_file('http-ooo-fuzzed.pcapng')), capture_output=True, env=test_env)
+ assert grep_output(proc.stdout, 'Errors')
+ assert grep_output(proc.stdout, 'Warns')
+ assert grep_output(proc.stdout, 'Notes')
+ assert grep_output(proc.stdout, 'Chats')
+
+ def test_tshark_z_expert_comment(self, cmd_tshark, capture_file, test_env):
+ proc = subprocesstest.run((cmd_tshark, '-q', '-z', 'expert,comment',
+ '-r', capture_file('sip.pcapng')), capture_output=True, env=test_env)
+ assert grep_output(proc.stdout, 'Notes')
+ assert grep_output(proc.stdout, 'Comments')
+
+ def test_tshark_z_expert_invalid_filter(self, cmd_tshark, capture_file, test_env):
+ invalid_filter = '__invalid_protocol'
+ proc = subprocesstest.run((cmd_tshark, '-q', '-z', 'expert,' + invalid_filter,
+ '-r', capture_file('http-ooo.pcap')), capture_output=True, env=test_env)
+ assert proc.returncode == ExitCodes.COMMAND_LINE
+ assert grep_output(proc.stdout, 'Filter "' + invalid_filter + '" is invalid')
+
+ def test_tshark_z_expert_error_invalid_filter(self, cmd_tshark, capture_file, test_env):
+ invalid_filter = '__invalid_protocol'
+ proc = subprocesstest.run((cmd_tshark, '-q', '-z', 'expert,error,' + invalid_filter,
+ '-r', capture_file('http-ooo.pcap')), capture_output=True, env=test_env)
+ assert proc.returncode == ExitCodes.COMMAND_LINE
+ assert grep_output(proc.stdout, 'Filter "' + invalid_filter + '" is invalid')
+
+ def test_tshark_z_expert_filter(self, cmd_tshark, capture_file, test_env):
+ proc = subprocesstest.run((cmd_tshark, '-q', '-z', 'expert,udp',
+ '-o', 'tcp.check_checksum:TRUE',
+ '-r', capture_file('http-ooo-fuzzed.pcapng')), capture_output=True, env=test_env)
+ # Filtering for UDP should produce no expert infos.
+ assert not grep_output(proc.stdout, 'Errors')
+ assert not grep_output(proc.stdout, 'Warns')
+ assert not grep_output(proc.stdout, 'Notes')
+ assert not grep_output(proc.stdout, 'Chats')
+
+ def test_tshark_z_expert_error_filter(self, cmd_tshark, capture_file, test_env):
+ proc = subprocesstest.run((cmd_tshark, '-q', '-z', 'expert,note,http', # tls is a filter
+ '-o', 'tcp.check_checksum:TRUE',
+ '-r', capture_file('http-ooo-fuzzed.pcapng')), capture_output=True, env=test_env)
+ # Filtering for HTTP and Note level expert info should produce only
+ # Error and Warning level expert infos with checksumming turned on.
+ # The Note warnings on are packets with TCP but not HTTP, and we're
+ # filtering out the Chat level.
+ assert grep_output(proc.stdout, 'Errors')
+ assert grep_output(proc.stdout, 'Warns')
+ assert not grep_output(proc.stdout, 'Notes')
+ assert not grep_output(proc.stdout, 'Chats')
+
+
+class TestTsharkExtcap:
+ # dumpcap dependency has been added to run this test only with capture support
+ def test_tshark_extcap_interfaces(self, cmd_tshark, cmd_dumpcap, test_env, home_path):
+ # Script extcaps don't work with the current code on windows.
+ # https://www.wireshark.org/docs/wsdg_html_chunked/ChCaptureExtcap.html
+ # TODO: skip this test until it will get fixed.
+ if sys.platform == 'win32':
+ pytest.skip('FIXME extcap .py scripts needs special treatment on Windows')
+ extcap_dir_path = os.path.join(home_path, 'extcap')
+ os.makedirs(extcap_dir_path)
+ test_env['WIRESHARK_EXTCAP_DIR'] = extcap_dir_path
+ source_file = os.path.join(os.path.dirname(__file__), 'sampleif.py')
+ shutil.copy2(source_file, extcap_dir_path)
+ # Ensure the test extcap_tool is properly loaded
+ proc = subprocesstest.run((cmd_tshark, '-D'), capture_output=True, env=test_env)
+ assert count_output(proc.stdout, 'sampleif') == 1
+ # Ensure tshark lists 2 interfaces in the preferences
+ proc = subprocesstest.run((cmd_tshark, '-G', 'currentprefs'), capture_output=True, env=test_env)
+ assert count_output(proc.stdout, 'extcap.sampleif.test') == 2
diff --git a/test/suite_decryption.py b/test/suite_decryption.py
new file mode 100644
index 0000000..985a11f
--- /dev/null
+++ b/test/suite_decryption.py
@@ -0,0 +1,1437 @@
+#
+# Wireshark tests
+# By Gerald Combs <gerald@wireshark.org>
+#
+# Ported from a set of Bash scripts which were copyright 2005 Ulf Lamping
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+'''Decryption tests'''
+
+import os.path
+import shutil
+import subprocess
+from subprocesstest import grep_output, count_output
+import sys
+import sysconfig
+import types
+import pytest
+import binascii
+
+class TestDecrypt80211:
+ def test_80211_wep(self, cmd_tshark, capture_file, test_env):
+ '''IEEE 802.11 WEP'''
+ # Included in git sources test/captures/wep.pcapng.gz
+ stdout = subprocess.check_output((cmd_tshark,
+ '-o', 'wlan.enable_decryption: TRUE',
+ '-r', capture_file('wep.pcapng.gz'),
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, 'Who has 192.168.5.1')
+ assert grep_output(stdout, r'Echo \(ping\) request')
+
+ def test_80211_wpa_psk(self, cmd_tshark, capture_file, test_env):
+ '''IEEE 802.11 WPA PSK'''
+ # https://gitlab.com/wireshark/wireshark/-/wikis/uploads/__moin_import__/attachments/SampleCaptures/wpa-Induction.pcap
+ stdout = subprocess.check_output((cmd_tshark,
+ '-o', 'wlan.enable_decryption: TRUE',
+ '-Tfields',
+ '-e', 'http.request.uri',
+ '-r', capture_file('wpa-Induction.pcap.gz'),
+ '-Y', 'http',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, 'favicon.ico')
+
+ def test_80211_wpa_eap(self, cmd_tshark, capture_file, test_env):
+ '''IEEE 802.11 WPA EAP (EAPOL Rekey)'''
+ # Included in git sources test/captures/wpa-eap-tls.pcap.gz
+ stdout = subprocess.check_output((cmd_tshark,
+ '-o', 'wlan.enable_decryption: TRUE',
+ '-r', capture_file('wpa-eap-tls.pcap.gz'),
+ '-Y', 'wlan.analysis.tk==7d9987daf5876249b6c773bf454a0da7',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, 'Group Message')
+
+ def test_80211_wpa_eapol_incomplete_rekeys(self, cmd_tshark, capture_file, test_env):
+ '''WPA decode with message1+2 only and secure bit set on message 2'''
+ # Included in git sources test/captures/wpa-test-decode.pcap.gz
+ stdout = subprocess.check_output((cmd_tshark,
+ '-o', 'wlan.enable_decryption: TRUE',
+ '-r', capture_file('wpa-test-decode.pcap.gz'),
+ '-Y', 'icmp.resp_to == 4263',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, 'Echo')
+
+ def test_80211_wpa_psk_mfp(self, cmd_tshark, capture_file, test_env):
+ '''WPA decode management frames with MFP enabled (802.11w)'''
+ # Included in git sources test/captures/wpa-test-decode-mgmt.pcap.gz
+ stdout = subprocess.check_output((cmd_tshark,
+ '-o', 'wlan.enable_decryption: TRUE',
+ '-r', capture_file('wpa-test-decode-mgmt.pcap.gz'),
+ '-Y', 'wlan.fixed.reason_code == 2 || wlan.fixed.category_code == 3',
+ ), encoding='utf-8', env=test_env)
+ assert count_output(stdout, '802.11.*SN=.*FN=.*Flags=') == 3
+
+ def test_80211_wpa2_psk_mfp(self, cmd_tshark, capture_file, features, test_env):
+ '''IEEE 802.11 decode WPA2 PSK with MFP enabled (802.11w)'''
+ # Included in git sources test/captures/wpa2-psk-mfp.pcapng.gz
+ stdout = subprocess.check_output((cmd_tshark,
+ '-o', 'wlan.enable_decryption: TRUE',
+ '-r', capture_file('wpa2-psk-mfp.pcapng.gz'),
+ '-Y', 'wlan.analysis.tk == 4e30e8c019bea43ea5262b10853b818d || wlan.analysis.gtk == 70cdbf2e5bc0ca22e53930818a5d80e4',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, 'Who has 192.168.5.5') # Verifies GTK is correct
+ assert grep_output(stdout, 'DHCP Request') # Verifies TK is correct
+ assert grep_output(stdout, r'Echo \(ping\) request') # Verifies TK is correct
+
+ def test_80211_wpa_tdls(self, cmd_tshark, capture_file, features, test_env):
+ '''WPA decode traffic in a TDLS (Tunneled Direct-Link Setup) session (802.11z)'''
+ # Included in git sources test/captures/wpa-test-decode-tdls.pcap.gz
+ stdout = subprocess.check_output((cmd_tshark,
+ #'-ouat:80211_keys:"wpa-pwd","12345678"',
+ '-o', 'wlan.enable_decryption: TRUE',
+ '-r', capture_file('wpa-test-decode-tdls.pcap.gz'),
+ '-Y', 'icmp',
+ ), encoding='utf-8', env=test_env)
+ assert count_output(stdout, 'ICMP.*Echo .ping') == 2
+
+ def test_80211_wpa3_personal(self, cmd_tshark, capture_file, test_env):
+ '''IEEE 802.11 decode WPA3 personal / SAE'''
+ # Included in git sources test/captures/wpa3-sae.pcapng.gz
+ stdout = subprocess.check_output((cmd_tshark,
+ '-o', 'wlan.enable_decryption: TRUE',
+ '-r', capture_file('wpa3-sae.pcapng.gz'),
+ '-Y', 'wlan.analysis.tk == 20a2e28f4329208044f4d7edca9e20a6 || wlan.analysis.gtk == 1fc82f8813160031d6bf87bca22b6354',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, 'Who has 192.168.5.18')
+ assert grep_output(stdout, 'DHCP ACK')
+
+ def test_80211_owe(self, cmd_tshark, capture_file, test_env):
+ '''IEEE 802.11 decode OWE'''
+ # Included in git sources test/captures/owe.pcapng.gz
+ stdout = subprocess.check_output((cmd_tshark,
+ '-o', 'wlan.enable_decryption: TRUE',
+ '-r', capture_file('owe.pcapng.gz'),
+ '-Y', 'wlan.analysis.tk == 10f3deccc00d5c8f629fba7a0fff34aa || wlan.analysis.gtk == 016b04ae9e6050bcc1f940dda9ffff2b',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, 'Who has 192.168.5.2')
+ assert grep_output(stdout, 'DHCP ACK')
+
+ def test_80211_wpa3_suite_b_192(self, cmd_tshark, capture_file, test_env):
+ '''IEEE 802.11 decode WPA3 Suite B 192-bit'''
+ # Included in git sources test/captures/wpa3-suiteb-192.pcapng.gz
+ stdout = subprocess.check_output((cmd_tshark,
+ '-o', 'wlan.enable_decryption: TRUE',
+ '-r', capture_file('wpa3-suiteb-192.pcapng.gz'),
+ '-Tfields',
+ '-e' 'wlan.rsn.ie.gtk_kde.gtk',
+ '-e' 'wlan.analysis.kck',
+ '-e' 'wlan.analysis.kek',
+ ), encoding='utf-8', env=test_env)
+ # Verify that correct PTKs (KCK, KEK) are derived and GTK correctly dissected
+ assert count_output(stdout, '^29f92526ccda5a5dfa0ffa44c26f576ee2d45bae7c5f63369103b1edcab206ea\t' \
+ 'f49ac1a15121f1a597a60a469870450a588ef1f73a1017b1\t' \
+ '0289b022b4f54262048d3493834ae591e811870c4520ee1395dd215a6092fbfb$') == 1
+ assert count_output(stdout, '^29f92526ccda5a5dfa0ffa44c26f576ee2d45bae7c5f63369103b1edcab206ea\t' \
+ '1027c8d5b155ff574158bc50083e28f02e9636a2ac694901\t' \
+ 'd4814a364419fa881a8593083f51497fe9e30556a91cc5d0b11cd2b3226038e1$') == 1
+ assert count_output(stdout, '^29f92526ccda5a5dfa0ffa44c26f576ee2d45bae7c5f63369103b1edcab206ea\t' \
+ '35db5e208c9caff2a4e00a54c5346085abaa6f422ef6df81\t' \
+ 'a14d0d683c01bc631bf142e82dc4995d87364eeacfab75d74cf470683bd10c51$') == 1
+
+ def test_80211_wpa1_gtk_rekey(self, cmd_tshark, capture_file, test_env):
+ '''Decode WPA1 with multiple GTK rekeys'''
+ # Included in git sources test/captures/wpa1-gtk-rekey.pcapng.gz
+ stdout = subprocess.check_output((cmd_tshark,
+ '-o', 'wlan.enable_decryption: TRUE',
+ '-r', capture_file('wpa1-gtk-rekey.pcapng.gz'),
+ '-Y', 'wlan.analysis.tk == "d0e57d224c1bb8806089d8c23154074c" || wlan.analysis.gtk == "6eaf63f4ad7997ced353723de3029f4d" || wlan.analysis.gtk == "fb42811bcb59b7845376246454fbdab7"',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, 'DHCP Discover')
+ assert count_output(stdout, 'ICMP.*Echo .ping') == 8
+
+ def test_80211_wpa_extended_key_id_rekey(self, cmd_tshark, capture_file, test_env):
+ '''WPA decode for Extended Key ID'''
+ # Included in git sources test/captures/wpa_ptk_extended_key_id.pcap.gz
+ stdout = subprocess.check_output((cmd_tshark,
+ '-o', 'wlan.enable_decryption: TRUE',
+ '-r', capture_file('wpa_ptk_extended_key_id.pcap.gz'),
+ '-Tfields',
+ '-e' 'wlan.fc.type_subtype',
+ '-e' 'wlan.ra',
+ '-e' 'wlan.analysis.tk',
+ '-e' 'wlan.analysis.gtk',
+ '-e' 'wlan.rsn.ie.ptk.keyid',
+ ), encoding='utf-8', env=test_env)
+ # Verify frames are decoded with the correct key
+ assert count_output(stdout, '^0x0020\t33:33:00:00:00:16\t\t234a9a6ddcca3cb728751cea49d01bb0\t$') == 5
+ assert count_output(stdout, '^0x0020\t33:33:ff:00:00:00\t\t234a9a6ddcca3cb728751cea49d01bb0\t$') == 1
+ assert count_output(stdout, '^0x0020\t33:33:ff:00:03:00\t\t234a9a6ddcca3cb728751cea49d01bb0\t$') == 1
+ assert count_output(stdout, '^0x0020\tff:ff:ff:ff:ff:ff\t\t234a9a6ddcca3cb728751cea49d01bb0\t$') == 4
+ assert count_output(stdout, '^0x0028\t02:00:00:00:03:00\t618b4d1829e2a496d7fd8c034a6d024d\t\t$') == 2
+ assert count_output(stdout, '^0x0028\t02:00:00:00:00:00\t618b4d1829e2a496d7fd8c034a6d024d\t\t$') == 1
+ # Verify RSN PTK KeyID parsing
+ assert count_output(stdout, '^0x0028\t02:00:00:00:00:00\t\t\t1$') == 1
+ assert count_output(stdout, '^0x0028\t02:00:00:00:00:00\tf31ecff5452f4c286cf66ef50d10dabe\t\t0$') == 1
+ assert count_output(stdout, '^0x0028\t02:00:00:00:00:00\t28dd851decf3f1c2a35df8bcc22fa1d2\t\t1$') == 1
+
+ def test_80211_wpa_ccmp_256(self, cmd_tshark, capture_file, features, test_env):
+ '''IEEE 802.11 decode CCMP-256'''
+ # Included in git sources test/captures/wpa-ccmp-256.pcapng.gz
+ stdout = subprocess.check_output((cmd_tshark,
+ '-o', 'wlan.enable_decryption: TRUE',
+ '-r', capture_file('wpa-ccmp-256.pcapng.gz'),
+ '-Y', 'wlan.analysis.tk == 4e6abbcf9dc0943936700b6825952218f58a47dfdf51dbb8ce9b02fd7d2d9e40 || wlan.analysis.gtk == 502085ca205e668f7e7c61cdf4f731336bb31e4f5b28ec91860174192e9b2190',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, 'Who has 192.168.5.5') # Verifies GTK is correct
+ assert grep_output(stdout, 'DHCP Request') # Verifies TK is correct
+ assert grep_output(stdout, r'Echo \(ping\) request') # Verifies TK is correct
+
+ def test_80211_wpa_gcmp(self, cmd_tshark, capture_file, features, test_env):
+ '''IEEE 802.11 decode GCMP'''
+ # Included in git sources test/captures/wpa-gcmp.pcapng.gz
+ stdout = subprocess.check_output((cmd_tshark,
+ '-o', 'wlan.enable_decryption: TRUE',
+ '-r', capture_file('wpa-gcmp.pcapng.gz'),
+ '-Y', 'wlan.analysis.tk == 755a9c1c9e605d5ff62849e4a17a935c || wlan.analysis.gtk == 7ff30f7a8dd67950eaaf2f20a869a62d',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, 'Who has 192.168.5.5') # Verifies GTK is correct
+ assert grep_output(stdout, 'DHCP Request') # Verifies TK is correct
+ assert grep_output(stdout, r'Echo \(ping\) request') # Verifies TK is correct
+
+ def test_80211_wpa_gcmp_256(self, cmd_tshark, capture_file, features, test_env):
+ '''IEEE 802.11 decode GCMP-256'''
+ # Included in git sources test/captures/wpa-gcmp-256.pcapng.gz
+ stdout = subprocess.check_output((cmd_tshark,
+ '-o', 'wlan.enable_decryption: TRUE',
+ '-r', capture_file('wpa-gcmp-256.pcapng.gz'),
+ '-Y', 'wlan.analysis.tk == b3dc2ff2d88d0d34c1ddc421cea17f304af3c46acbbe7b6d808b6ebf1b98ec38 || wlan.analysis.gtk == a745ee2313f86515a155c4cb044bc148ae234b9c72707f772b69c2fede3e4016',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, 'Who has 192.168.5.5') # Verifies GTK is correct
+ assert grep_output(stdout, 'DHCP Request') # Verifies TK is correct
+ assert grep_output(stdout, r'Echo \(ping\) request') # Verifies TK is correct
+
+ def test_80211_wpa2_ft_psk_no_roam(self, cmd_tshark, capture_file, test_env):
+ '''IEEE 802.11 decode WPA2 FT PSK (without roam verification)'''
+ # Included in git sources test/captures/wpa2-ft-psk.pcapng.gz
+ stdout = subprocess.check_output((cmd_tshark,
+ '-o', 'wlan.enable_decryption: TRUE',
+ '-r', capture_file('wpa2-ft-psk.pcapng.gz'),
+ '-Y', 'wlan.analysis.tk == ba60c7be2944e18f31949508a53ee9d6 || wlan.analysis.gtk == 6eab6a5f8d880f81104ed65ab0c74449',
+ ), encoding='utf-8', env=test_env)
+ # Verifies that traffic from initial authentication can be decrypted (both TK and GTK)
+ assert count_output(stdout, 'DHCP Discover') == 2
+ assert count_output(stdout, 'DHCP Offer') == 1
+ assert count_output(stdout, 'DHCP Request') == 2
+ assert count_output(stdout, 'DHCP ACK') == 1
+ assert count_output(stdout, 'ARP.*Who has') == 3
+ assert count_output(stdout, 'ARP.*is at') == 1
+ assert count_output(stdout, r'ICMP.*Echo \(ping\)') == 2
+
+ def test_80211_wpa2_ft_psk_roam(self, cmd_tshark, capture_file, features, test_env):
+ '''IEEE 802.11 decode WPA2 FT PSK'''
+ # Included in git sources test/captures/wpa2-ft-psk.pcapng.gz
+
+ # Verify TK and GTK for both initial authentication (AP1) and roam(AP2).
+ stdout = subprocess.check_output((cmd_tshark,
+ '-o', 'wlan.enable_decryption: TRUE',
+ '-r', capture_file('wpa2-ft-psk.pcapng.gz'),
+ '-Y', 'wlan.analysis.tk == ba60c7be2944e18f31949508a53ee9d6 || wlan.analysis.gtk == 6eab6a5f8d880f81104ed65ab0c74449 || wlan.analysis.tk == a6a3304e5a8fabe0dc427cc41a707858 || wlan.analysis.gtk == a6cc605e10878f86b20a266c9b58d230',
+ ), encoding='utf-8', env=test_env)
+ assert count_output(stdout, 'DHCP Discover') == 2
+ assert count_output(stdout, 'DHCP Offer') == 1
+ assert count_output(stdout, 'DHCP Request') == 2
+ assert count_output(stdout, 'DHCP ACK') == 1
+ assert count_output(stdout, 'ARP.*Who has') == 5
+ assert count_output(stdout, 'ARP.*is at') == 2
+ assert count_output(stdout, r'ICMP.*Echo \(ping\)') == 4
+
+ def test_80211_wpa2_ft_eap(self, cmd_tshark, capture_file, test_env):
+ '''IEEE 802.11 decode WPA2 FT EAP'''
+ # Included in git sources test/captures/wpa2-ft-eap.pcapng.gz
+ stdout = subprocess.check_output((cmd_tshark,
+ '-o', 'wlan.enable_decryption: TRUE',
+ '-r', capture_file('wpa2-ft-eap.pcapng.gz'),
+ '-Y', 'wlan.analysis.tk == 65471b64605bf2a04af296284cb4ae2a || wlan.analysis.gtk == 1783a5c28e046df6fb58cf4406c4b22c',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, 'Who has 192.168.1.1') # Verifies GTK decryption
+ assert grep_output(stdout, r'Echo \(ping\) request') # Verifies TK decryption
+
+class TestDecrypt80211UserTk:
+ def test_80211_user_tk_tkip(self, cmd_tshark, capture_file, test_env):
+ '''IEEE 802.11 decode TKIP using user TK'''
+ # Included in git sources test/captures/wpa1-gtk-rekey.pcapng.gz
+ stdout = subprocess.check_output((cmd_tshark,
+ '-o', 'wlan.enable_decryption: TRUE',
+ '-r', capture_file('wpa1-gtk-rekey.pcapng.gz'),
+ '-Y', 'wlan.analysis.tk == "d0e57d224c1bb8806089d8c23154074c" || wlan.analysis.gtk == "6eaf63f4ad7997ced353723de3029f4d" || wlan.analysis.gtk == "fb42811bcb59b7845376246454fbdab7"',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, 'DHCP Discover')
+ assert count_output(stdout, 'ICMP.*Echo .ping') == 8
+
+ def test_80211_user_tk_ccmp(self, cmd_tshark, capture_file, features, test_env):
+ '''IEEE 802.11 decode CCMP-128 using user TK'''
+ # Included in git sources test/captures/wpa2-psk-mfp.pcapng.gz
+ stdout = subprocess.check_output((cmd_tshark,
+ '-o', 'wlan.enable_decryption: TRUE',
+ '-r', capture_file('wpa2-psk-mfp.pcapng.gz'),
+ '-Y', 'wlan.analysis.tk == 4e30e8c019bea43ea5262b10853b818d || wlan.analysis.gtk == 70cdbf2e5bc0ca22e53930818a5d80e4',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, 'Who has 192.168.5.5') # Verifies GTK decryption
+ assert grep_output(stdout, 'DHCP Request') # Verifies TK decryption
+ assert grep_output(stdout, r'Echo \(ping\) request') # Verifies TK decryption
+
+ def test_80211_user_tk_ccmp_256(self, cmd_tshark, capture_file, features, test_env):
+ '''IEEE 802.11 decode CCMP-256 using user TK'''
+ # Included in git sources test/captures/wpa-ccmp-256.pcapng.gz
+ stdout = subprocess.check_output((cmd_tshark,
+ '-o', 'wlan.enable_decryption: TRUE',
+ '-r', capture_file('wpa-ccmp-256.pcapng.gz'),
+ '-Y', 'wlan.analysis.tk == 4e6abbcf9dc0943936700b6825952218f58a47dfdf51dbb8ce9b02fd7d2d9e40 || wlan.analysis.gtk == 502085ca205e668f7e7c61cdf4f731336bb31e4f5b28ec91860174192e9b2190',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, 'Who has 192.168.5.5') # Verifies GTK decryption
+ assert grep_output(stdout, 'DHCP Request') # Verifies TK decryption
+ assert grep_output(stdout, r'Echo \(ping\) request') # Verifies TK decryption
+
+ def test_80211_user_tk_gcmp(self, cmd_tshark, capture_file, features, test_env):
+ '''IEEE 802.11 decode GCMP using user TK'''
+ # Included in git sources test/captures/wpa-gcmp.pcapng.gz
+ stdout = subprocess.check_output((cmd_tshark,
+ '-o', 'wlan.enable_decryption: TRUE',
+ '-r', capture_file('wpa-gcmp.pcapng.gz'),
+ '-Y', 'wlan.analysis.tk == 755a9c1c9e605d5ff62849e4a17a935c || wlan.analysis.gtk == 7ff30f7a8dd67950eaaf2f20a869a62d',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, 'Who has 192.168.5.5') # Verifies GTK decryption
+ assert grep_output(stdout, 'DHCP Request') # Verifies TK decryption
+ assert grep_output(stdout, r'Echo \(ping\) request') # Verifies TK decryption
+
+ def test_80211_wpa_gcmp_256(self, cmd_tshark, capture_file, features, test_env):
+ '''IEEE 802.11 decode GCMP-256 using user TK'''
+ # Included in git sources test/captures/wpa-gcmp-256.pcapng.gz
+ stdout = subprocess.check_output((cmd_tshark,
+ '-o', 'wlan.enable_decryption: TRUE',
+ '-r', capture_file('wpa-gcmp-256.pcapng.gz'),
+ '-Y', 'wlan.analysis.tk == b3dc2ff2d88d0d34c1ddc421cea17f304af3c46acbbe7b6d808b6ebf1b98ec38 || wlan.analysis.gtk == a745ee2313f86515a155c4cb044bc148ae234b9c72707f772b69c2fede3e4016',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, 'Who has 192.168.5.5') # Verifies GTK decryption
+ assert grep_output(stdout, 'DHCP Request') # Verifies TK decryption
+ assert grep_output(stdout, r'Echo \(ping\) request') # Verifies TK decryption
+
+
+class TestDecryptDTLS:
+ def test_dtls_rsa(self, cmd_tshark, capture_file, features, test_env):
+ '''DTLS'''
+ if not features.have_gnutls:
+ pytest.skip('Requires GnuTLS.')
+ # https://gitlab.com/wireshark/wireshark/-/wikis/uploads/__moin_import__/attachments/SampleCaptures/snakeoil.tgz
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('snakeoil-dtls.pcap'),
+ '-Tfields',
+ '-e', 'data.data',
+ '-Y', 'data',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, '697420776f726b20210a')
+
+ def test_dtls_psk_aes128ccm8(self, cmd_tshark, capture_file, test_env):
+ '''DTLS 1.2 with PSK, AES-128-CCM-8'''
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('dtls12-aes128ccm8.pcap'),
+ '-o', 'dtls.psk:ca19e028a8a372ad2d325f950fcaceed',
+ '-x'
+ ), encoding='utf-8', env=test_env)
+ dt_count = count_output(stdout, 'Decrypted DTLS')
+ wfm_count = count_output(stdout, 'Works for me!.')
+ assert dt_count == 7 and wfm_count == 2
+
+ def test_dtls_dsb_aes128ccm8(self, cmd_tshark, capture_file, test_env):
+ '''DTLS 1.2 with master secrets in a pcapng Decryption Secrets Block.'''
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('dtls12-aes128ccm8-dsb.pcapng'),
+ '-x'
+ ), encoding='utf-8', env=test_env)
+ dt_count = count_output(stdout, 'Decrypted DTLS')
+ wfm_count = count_output(stdout, 'Works for me!.')
+ assert dt_count == 7 and wfm_count == 2
+
+ def test_dtls_udt(self, cmd_tshark, dirs, capture_file, features, test_env):
+ '''UDT over DTLS 1.2 with RSA key'''
+ if not features.have_gnutls:
+ pytest.skip('Requires GnuTLS.')
+ key_file = os.path.join(dirs.key_dir, 'udt-dtls.key')
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('udt-dtls.pcapng.gz'),
+ '-o', 'dtls.keys_list:0.0.0.0,0,data,{}'.format(key_file),
+ '-Y', 'dtls && udt.type==ack',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, 'UDT')
+
+
+class TestDecryptTLS:
+ def test_tls_rsa(self, cmd_tshark, capture_file, features, test_env):
+ '''TLS using the server's private RSA key.'''
+ if not features.have_gnutls:
+ pytest.skip('Requires GnuTLS.')
+ # https://gitlab.com/wireshark/wireshark/-/wikis/uploads/__moin_import__/attachments/SampleCaptures/snakeoil2_070531.tgz
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('rsasnakeoil2.pcap'),
+ '-Tfields',
+ '-e', 'http.request.uri',
+ '-Y', 'http',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, 'favicon.ico')
+
+ def test_tls_rsa_pq(self, cmd_tshark, dirs, capture_file, features, test_env):
+ '''TLS using the server's private key with p < q
+ (test whether libgcrypt is correctly called)'''
+ if not features.have_gnutls:
+ pytest.skip('Requires GnuTLS.')
+ key_file = os.path.join(dirs.key_dir, 'rsa-p-lt-q.key')
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('rsa-p-lt-q.pcap'),
+ '-o', 'tls.keys_list:0.0.0.0,443,http,{}'.format(key_file),
+ '-Tfields',
+ '-e', 'http.request.uri',
+ '-Y', 'http',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, '/')
+
+ def test_tls_rsa_privkeys_uat(self, cmd_tshark, dirs, capture_file, features, test_env):
+ '''Check TLS decryption works using the rsa_keys UAT.'''
+ if not features.have_gnutls:
+ pytest.skip('Requires GnuTLS.')
+ key_file = os.path.join(dirs.key_dir, 'rsa-p-lt-q.key')
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('rsa-p-lt-q.pcap'),
+ '-o', 'uat:rsa_keys:"{}",""'.format(key_file.replace('\\', '\\x5c')),
+ '-Tfields',
+ '-e', 'http.request.uri',
+ '-Y', 'http',
+ ), encoding='utf-8', env=test_env)
+ assert '/' in stdout
+
+ def test_tls_rsa_with_password(self, cmd_tshark, capture_file, features, test_env):
+ '''TLS using the server's private key with password'''
+ if not features.have_gnutls:
+ pytest.skip('Requires GnuTLS.')
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('dmgr.pcapng'),
+ '-Tfields',
+ '-e', 'http.request.uri',
+ '-Y', 'http',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, 'unsecureLogon.jsp')
+
+ def test_tls_master_secret(self, cmd_tshark, dirs, capture_file, test_env):
+ '''TLS using the master secret and ssl.keylog_file preference aliasing'''
+ key_file = os.path.join(dirs.key_dir, 'dhe1_keylog.dat')
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('dhe1.pcapng.gz'),
+ '-o', 'ssl.keylog_file: {}'.format(key_file),
+ '-o', 'tls.desegment_ssl_application_data: FALSE',
+ '-o', 'http.tls.port: 443',
+ '-Tfields',
+ '-e', 'http.request.method',
+ '-e', 'http.request.uri',
+ '-e', 'http.request.version',
+ '-Y', 'http',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, r'GET\s+/test\s+HTTP/1.0')
+
+ def test_tls12_renegotiation(self, cmd_tshark, dirs, capture_file, features, test_env):
+ '''TLS 1.2 with renegotiation'''
+ if not features.have_gnutls:
+ pytest.skip('Requires GnuTLS.')
+ key_file = os.path.join(dirs.key_dir, 'rsasnakeoil2.key')
+ # Test protocol alias while at it (ssl -> tls)
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('tls-renegotiation.pcap'),
+ '-o', 'tls.keys_list:0.0.0.0,4433,http,{}'.format(key_file),
+ '-d', 'tcp.port==4433,ssl',
+ '-Tfields',
+ '-e', 'http.content_length',
+ '-Y', 'http',
+ ), encoding='utf-8', env=test_env)
+ count_0 = count_output(stdout, '^0$')
+ count_2151 = count_output(stdout, '^2151$')
+ assert count_0 == 1 and count_2151 == 1
+
+ def test_tls12_psk_aes128ccm(self, cmd_tshark, capture_file, test_env):
+ '''TLS 1.2 with PSK, AES-128-CCM'''
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('tls12-aes128ccm.pcap'),
+ '-o', 'tls.psk:ca19e028a8a372ad2d325f950fcaceed',
+ '-q',
+ '-z', 'follow,tls,ascii,0',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, 'http://www.gnu.org/software/gnutls')
+
+ def test_tls12_psk_aes256gcm(self, cmd_tshark, capture_file, test_env):
+ '''TLS 1.2 with PSK, AES-256-GCM'''
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('tls12-aes256gcm.pcap'),
+ '-o', 'tls.psk:ca19e028a8a372ad2d325f950fcaceed',
+ '-q',
+ '-z', 'follow,tls,ascii,0',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, 'http://www.gnu.org/software/gnutls')
+
+ def test_tls12_chacha20poly1305(self, cmd_tshark, dirs, features, capture_file, test_env):
+ '''TLS 1.2 with ChaCha20-Poly1305'''
+ key_file = os.path.join(dirs.key_dir, 'tls12-chacha20poly1305.keys')
+ ciphers=[
+ 'ECDHE-ECDSA-CHACHA20-POLY1305',
+ 'ECDHE-RSA-CHACHA20-POLY1305',
+ 'DHE-RSA-CHACHA20-POLY1305',
+ 'RSA-PSK-CHACHA20-POLY1305',
+ 'DHE-PSK-CHACHA20-POLY1305',
+ 'ECDHE-PSK-CHACHA20-POLY1305',
+ 'PSK-CHACHA20-POLY1305',
+ ]
+ stream = 0
+ for cipher in ciphers:
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('tls12-chacha20poly1305.pcap'),
+ '-o', 'tls.keylog_file: {}'.format(key_file),
+ '-q',
+ '-z', 'follow,tls,ascii,{}'.format(stream),
+ ), encoding='utf-8', env=test_env)
+ stream += 1
+ assert grep_output(stdout, 'Cipher is {}'.format(cipher))
+
+ def test_tls13_chacha20poly1305(self, cmd_tshark, dirs, features, capture_file, test_env):
+ '''TLS 1.3 with ChaCha20-Poly1305'''
+ key_file = os.path.join(dirs.key_dir, 'tls13-20-chacha20poly1305.keys')
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('tls13-20-chacha20poly1305.pcap'),
+ '-o', 'tls.keylog_file: {}'.format(key_file),
+ '-q',
+ '-z', 'follow,tls,ascii,0',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, 'TLS13-CHACHA20-POLY1305-SHA256')
+
+ def test_tls13_rfc8446(self, cmd_tshark, dirs, features, capture_file, test_env):
+ '''TLS 1.3 (normal session, then early data followed by normal data).'''
+ key_file = os.path.join(dirs.key_dir, 'tls13-rfc8446.keys')
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('tls13-rfc8446.pcap'),
+ '-otls.keylog_file:{}'.format(key_file),
+ '-Y', 'http',
+ '-Tfields',
+ '-e', 'frame.number',
+ '-e', 'http.request.uri',
+ '-e', 'http.file_data',
+ '-E', 'separator=|',
+ ), encoding='utf-8', env=test_env)
+ first_response = binascii.hexlify(b'Request for /first, version TLSv1.3, Early data: no\n').decode("ascii")
+ early_response = binascii.hexlify(b'Request for /early, version TLSv1.3, Early data: yes\n').decode("ascii")
+ second_response = binascii.hexlify(b'Request for /second, version TLSv1.3, Early data: yes\n').decode("ascii")
+ assert [
+ r'5|/first|',
+ fr'6||{first_response}',
+ r'8|/early|',
+ fr'10||{early_response}',
+ r'12|/second|',
+ fr'13||{second_response}',
+ ] == stdout.splitlines()
+
+ def test_tls13_rfc8446_noearly(self, cmd_tshark, dirs, features, capture_file, test_env):
+ '''TLS 1.3 (with undecryptable early data).'''
+ key_file = os.path.join(dirs.key_dir, 'tls13-rfc8446-noearly.keys')
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('tls13-rfc8446.pcap'),
+ '-otls.keylog_file:{}'.format(key_file),
+ '-Y', 'http',
+ '-Tfields',
+ '-e', 'frame.number',
+ '-e', 'http.request.uri',
+ '-e', 'http.file_data',
+ '-E', 'separator=|',
+ ), encoding='utf-8', env=test_env)
+ first_response = binascii.hexlify(b'Request for /first, version TLSv1.3, Early data: no\n').decode("ascii")
+ early_response = binascii.hexlify(b'Request for /early, version TLSv1.3, Early data: yes\n').decode("ascii")
+ second_response = binascii.hexlify(b'Request for /second, version TLSv1.3, Early data: yes\n').decode("ascii")
+ assert [
+ r'5|/first|',
+ fr'6||{first_response}',
+ fr'10||{early_response}',
+ r'12|/second|',
+ fr'13||{second_response}',
+ ] == stdout.splitlines()
+
+ def test_tls12_dsb(self, cmd_tshark, capture_file, test_env):
+ '''TLS 1.2 with master secrets in pcapng Decryption Secrets Blocks.'''
+ output = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('tls12-dsb.pcapng'),
+ '-Tfields',
+ '-e', 'http.host',
+ '-e', 'http.response.code',
+ '-Y', 'http',
+ ), encoding='utf-8', env=test_env)
+ assert 'example.com\t\n\t200\nexample.net\t\n\t200\n' == output
+
+ def test_tls_over_tls(self, cmd_tshark, dirs, capture_file, features, test_env):
+ '''TLS using the server's private key with p < q
+ (test whether libgcrypt is correctly called)'''
+ if not features.have_gnutls:
+ pytest.skip('Requires GnuTLS.')
+ key_file = os.path.join(dirs.key_dir, 'tls-over-tls.key')
+ output = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('tls-over-tls.pcapng.gz'),
+ '-o', 'tls.keys_list:0.0.0.0,443,http,{}'.format(key_file),
+ '-z', 'expert,tls.handshake.certificates',
+ '-Tfields',
+ '-e', 'tls.handshake.certificate_length',
+ '-Y', 'tls.handshake.certificates',
+ ), encoding='utf-8', env=test_env)
+ assert '1152,1115,1352\n1152\n1412,1434,1382\n' == output
+
+
+class TestDecryptZigbee:
+ def test_zigbee(self, cmd_tshark, capture_file, test_env):
+ '''ZigBee'''
+ # https://gitlab.com/wireshark/wireshark/-/issues/7022
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('sample_control4_2012-03-24.pcap'),
+ '-Tfields',
+ '-e', 'data.data',
+ '-Y', 'zbee_aps',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, '3067636338652063342e646d2e747620')
+
+
+class TestDecryptANSIC122:
+ def test_ansi_c1222(self, cmd_tshark, capture_file, test_env):
+ '''ANSI C12.22'''
+ # https://gitlab.com/wireshark/wireshark/-/issues/9196
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('c1222_std_example8.pcap'),
+ '-o', 'c1222.decrypt: TRUE',
+ '-o', 'c1222.baseoid: 2.16.124.113620.1.22.0',
+ '-Tfields',
+ '-e', 'c1222.data',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, '00104d414e55464143545552455220534e2092')
+
+
+class TestDecryptDVBCI:
+ def test_dvb_ci(self, cmd_tshark, capture_file, test_env):
+ '''DVB-CI'''
+ # simplified version of the sample capture in
+ # https://gitlab.com/wireshark/wireshark/-/issues/6700
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('dvb-ci_UV1_0000.pcap'),
+ '-o', 'dvb-ci.sek: 00000000000000000000000000000000',
+ '-o', 'dvb-ci.siv: 00000000000000000000000000000000',
+ '-Tfields',
+ '-e', 'dvb-ci.cc.sac.padding',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, '800000000000000000000000')
+
+
+class TestDecryptIPsec:
+ def test_ipsec_esp(self, cmd_tshark, capture_file, test_env):
+ '''IPsec ESP'''
+ # https://gitlab.com/wireshark/wireshark/-/issues/12671
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('esp-bug-12671.pcapng.gz'),
+ '-o', 'esp.enable_encryption_decode: TRUE',
+ '-Tfields',
+ '-e', 'data.data',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, '08090a0b0c0d0e0f1011121314151617')
+
+
+class TestDecryptIkeIsakmp:
+ def test_ikev1_certs(self, cmd_tshark, capture_file, test_env):
+ '''IKEv1 (ISAKMP) with certificates'''
+ # https://gitlab.com/wireshark/wireshark/-/issues/7951
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('ikev1-certs.pcap'),
+ '-Tfields',
+ '-e', 'x509sat.printableString',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, 'OpenSwan')
+
+ def test_ikev1_simultaneous(self, cmd_tshark, capture_file, test_env):
+ '''IKEv1 (ISAKMP) simultaneous exchanges'''
+ # https://gitlab.com/wireshark/wireshark/-/issues/12610
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('ikev1-bug-12610.pcapng.gz'),
+ '-Tfields',
+ '-e', 'isakmp.hash',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, 'b52521f774967402c9f6cee95fd17e5b')
+
+ def test_ikev1_unencrypted(self, cmd_tshark, capture_file, test_env):
+ '''IKEv1 (ISAKMP) unencrypted phase 1'''
+ # https://gitlab.com/wireshark/wireshark/-/issues/12620
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('ikev1-bug-12620.pcapng.gz'),
+ '-Tfields',
+ '-e', 'isakmp.hash',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, '40043b640f4373250d5ac3a1fb63153c')
+
+ def test_ikev2_3des_sha160(self, cmd_tshark, capture_file, test_env):
+ '''IKEv2 decryption test (3DES-CBC/SHA1_160)'''
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('ikev2-decrypt-3des-sha1_160.pcap'),
+ '-Tfields',
+ '-e', 'isakmp.auth.data',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, '02f7a0d5f1fdc8ea81039818c65bb9bd09af9b8917319b887ff9ba3046c344c7')
+
+ def test_ikev2_aes128_ccm12(self, cmd_tshark, capture_file, test_env):
+ '''IKEv2 decryption test (AES-128-CCM-12) - with CBC-MAC verification'''
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('ikev2-decrypt-aes128ccm12.pcap'),
+ '-Tfields',
+ '-e', 'isakmp.auth.data',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, 'c2104394299e1ffe7908ea720ad5d13717a0d454e4fa0a2128ea689411f479c4')
+
+ def test_ikev2_aes128_ccm12_2(self, cmd_tshark, capture_file, test_env):
+ '''IKEv2 decryption test (AES-128-CCM-12 using CTR mode, without checksum)'''
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('ikev2-decrypt-aes128ccm12-2.pcap'),
+ '-Tfields',
+ '-e', 'isakmp.auth.data',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, 'aaa281c87b4a19046c57271d557488ca413b57228cb951f5fa9640992a0285b9')
+
+ def test_ikev2_aes192ctr_sha512(self, cmd_tshark, capture_file, test_env):
+ '''IKEv2 decryption test (AES-192-CTR/SHA2-512)'''
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('ikev2-decrypt-aes192ctr.pcap'),
+ '-Tfields',
+ '-e', 'isakmp.auth.data',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, '3ec23dcf9348485638407c754547aeb3085290082c49f583fdbae59263a20b4a')
+
+ def test_ikev2_aes256cbc_sha256(self, cmd_tshark, capture_file, test_env):
+ '''IKEv2 decryption test (AES-256-CBC/SHA2-256)'''
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('ikev2-decrypt-aes256cbc.pcapng'),
+ '-Tfields',
+ '-e', 'isakmp.auth.data',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, 'e1a8d550064201a7ec024a85758d0673c61c5c510ac13bcd225d6327f50da3d3')
+
+ def test_ikev2_aes256ccm16(self, cmd_tshark, capture_file, test_env):
+ '''IKEv2 decryption test (AES-256-CCM-16)'''
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('ikev2-decrypt-aes256ccm16.pcapng'),
+ '-Tfields',
+ '-e', 'isakmp.auth.data',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, 'fa2e74bdc01e30fb0b3ddc9723c9449095969da51f69e560209d2c2b7940210a')
+
+ def test_ikev2_aes256gcm16(self, cmd_tshark, capture_file, test_env):
+ '''IKEv2 decryption test (AES-256-GCM-16)'''
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('ikev2-decrypt-aes256gcm16.pcap'),
+ '-Tfields',
+ '-e', 'isakmp.auth.data',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, '9ab71f14ab553cad873a1aa70b99df155dee77cdcf3694b3b7527acbb9712ded')
+
+ def test_ikev2_aes256gcm8(self, cmd_tshark, capture_file, test_env):
+ '''IKEv2 decryption test (AES-256-GCM-8)'''
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('ikev2-decrypt-aes256gcm8.pcap'),
+ '-Tfields',
+ '-e', 'isakmp.auth.data',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, '4a66d822d0afbc22ad9a92a2cf4287c920ad8ac3b069a4a7e75fe0a5d499f914')
+
+
+class TestDecryptHttp2:
+ def test_http2(self, cmd_tshark, capture_file, features, test_env):
+ '''HTTP2 (HPACK)'''
+ if not features.have_nghttp2:
+ pytest.skip('Requires nghttp2.')
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('packet-h2-14_headers.pcapng'),
+ '-Tfields',
+ '-e', 'http2.header.value',
+ '-d', 'tcp.port==3000,http2',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, 'nghttp2')
+
+
+class TestDecryptKerberos:
+ def test_kerberos(self, cmd_tshark, dirs, features, capture_file, test_env):
+ '''Kerberos'''
+ # Files are from krb-816.zip on the SampleCaptures page.
+ if not features.have_kerberos:
+ pytest.skip('Requires kerberos.')
+ keytab_file = os.path.join(dirs.key_dir, 'krb-816.keytab')
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('krb-816.pcap.gz'),
+ '-o', 'kerberos.decrypt: TRUE',
+ '-o', 'kerberos.file: {}'.format(keytab_file),
+ '-Tfields',
+ '-e', 'kerberos.keyvalue',
+ ), encoding='utf-8', env=test_env)
+ # keyvalue: ccda7d48219f73c3b28311c4ba7242b3
+ assert grep_output(stdout, 'ccda7d48219f73c3b28311c4ba7242b3')
+
+
+@pytest.fixture
+def run_wireguard_test(cmd_tshark, capture_file, result_file, features, test_env):
+ def runOne(self, args, keylog=None, pcap_file='wireguard-ping-tcp.pcap'):
+ if keylog:
+ keylog_file = result_file('wireguard.keys')
+ args += ['-owg.keylog_file:%s' % keylog_file]
+ with open(keylog_file, 'w') as f:
+ f.write("\n".join(keylog))
+ stdout = subprocess.check_output([cmd_tshark, '-r', capture_file(pcap_file)] + args, encoding='utf-8', env=test_env)
+ return stdout.splitlines()
+ return runOne
+
+
+class TestDecryptWireguard:
+ # The "foo_alt" keys are similar as "foo" except that some bits are changed.
+ # The crypto library should be able to handle this and internally the
+ # dissector uses MSB to recognize whether a private key is set.
+ key_Spriv_i = 'AKeZaHwBxjiKLFnkY2unvEdOTtg4AL+M9dQXfopFVFk='
+ key_Spriv_i_alt = 'B6eZaHwBxjiKLFnkY2unvEdOTtg4AL+M9dQXfopFVJk='
+ key_Spub_i = 'Igge9KzRytKNwrgkzDE/8hrLu6Ly0OqVdvOPWhA5KR4='
+ key_Spriv_r = 'cFIxTUyBs1Qil414hBwEgvasEax8CKJ5IS5ZougplWs='
+ key_Spub_r = 'YDCttCs9e1J52/g9vEnwJJa+2x6RqaayAYMpSVQfGEY='
+ key_Epriv_i0 = 'sLGLJSOQfyz7JNJ5ZDzFf3Uz1rkiCMMjbWerNYcPFFU='
+ key_Epriv_i0_alt = 't7GLJSOQfyz7JNJ5ZDzFf3Uz1rkiCMMjbWerNYcPFJU='
+ key_Epriv_r0 = 'QC4/FZKhFf0b/eXEcCecmZNt6V6PXmRa4EWG1PIYTU4='
+ key_Epriv_i1 = 'ULv83D+y3vA0t2mgmTmWz++lpVsrP7i4wNaUEK2oX0E='
+ key_Epriv_r1 = 'sBv1dhsm63cbvWMv/XML+bvynBp9PTdY9Vvptu3HQlg='
+ # Ephemeral keys and PSK for wireguard-psk.pcap
+ key_Epriv_i2 = 'iCv2VTi/BC/q0egU931KXrrQ4TSwXaezMgrhh7uCbXs='
+ key_Epriv_r2 = '8G1N3LnEqYC7+NW/b6mqceVUIGBMAZSm+IpwG1U0j0w='
+ key_psk2 = '//////////////////////////////////////////8='
+ key_Epriv_i3 = '+MHo9sfkjPsjCx7lbVhRLDvMxYvTirOQFDSdzAW6kUQ='
+ key_Epriv_r3 = '0G6t5j1B/We65MXVEBIGuRGYadwB2ITdvJovtAuATmc='
+ key_psk3 = 'iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIg='
+ # dummy key that should not work with anything.
+ key_dummy = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx='
+
+ def test_mac1_public(self, run_wireguard_test):
+ """Check that MAC1 identification using public keys work."""
+ lines = run_wireguard_test(self, [
+ '-ouat:wg_keys:"Public","%s"' % self.key_Spub_i,
+ '-ouat:wg_keys:"Public","%s"' % self.key_Spub_r,
+ '-Y', 'wg.receiver_pubkey',
+ '-Tfields',
+ '-e', 'frame.number',
+ '-e', 'wg.receiver_pubkey',
+ '-e', 'wg.receiver_pubkey.known_privkey',
+ ])
+ assert len(lines) == 4
+ assert '1\t%s\tFalse' % self.key_Spub_r in lines
+ assert '2\t%s\tFalse' % self.key_Spub_i in lines
+ assert '13\t%s\tFalse' % self.key_Spub_r in lines
+ assert '14\t%s\tFalse' % self.key_Spub_i in lines
+
+ def test_mac1_private(self, run_wireguard_test):
+ """Check that MAC1 identification using private keys work."""
+ lines = run_wireguard_test(self, [
+ '-ouat:wg_keys:"Private","%s"' % self.key_Spriv_i,
+ '-ouat:wg_keys:"Private","%s"' % self.key_Spriv_r,
+ '-Y', 'wg.receiver_pubkey',
+ '-Tfields',
+ '-e', 'frame.number',
+ '-e', 'wg.receiver_pubkey',
+ '-e', 'wg.receiver_pubkey.known_privkey',
+ ])
+ assert len(lines) == 4
+ assert '1\t%s\tTrue' % self.key_Spub_r in lines
+ assert '2\t%s\tTrue' % self.key_Spub_i in lines
+ assert '13\t%s\tTrue' % self.key_Spub_r in lines
+ assert '14\t%s\tTrue' % self.key_Spub_i in lines
+
+ def test_decrypt_initiation_sprivr(self, run_wireguard_test):
+ """Check for partial decryption using Spriv_r."""
+ lines = run_wireguard_test(self, [
+ '-ouat:wg_keys:"Private","%s"' % self.key_Spriv_r,
+ '-Y', 'wg.type==1',
+ '-Tfields',
+ '-e', 'frame.number',
+ '-e', 'wg.static',
+ '-e', 'wg.static.known_pubkey',
+ '-e', 'wg.static.known_privkey',
+ '-e', 'wg.timestamp.nanoseconds',
+ ])
+ # static pubkey is unknown because Spub_i is not added to wg_keys.
+ assert '1\t%s\tFalse\tFalse\t%s' % (self.key_Spub_i, '356537872') in lines
+ assert '13\t%s\tFalse\tFalse\t%s' % (self.key_Spub_i, '490514356') in lines
+
+ def test_decrypt_initiation_ephemeral_only(self, run_wireguard_test):
+ """Check for partial decryption using Epriv_i."""
+ lines = run_wireguard_test(self, [
+ '-ouat:wg_keys:"Public","%s"' % self.key_Spub_r,
+ '-Y', 'wg.type==1',
+ '-Tfields',
+ '-e', 'frame.number',
+ '-e', 'wg.ephemeral.known_privkey',
+ '-e', 'wg.static',
+ '-e', 'wg.timestamp.nanoseconds',
+ ], keylog=[
+ 'LOCAL_EPHEMERAL_PRIVATE_KEY=%s' % self.key_Epriv_i0,
+ 'LOCAL_EPHEMERAL_PRIVATE_KEY=%s' % self.key_Epriv_i1,
+ ])
+ # The current implementation tries to write as much decrypted data as
+ # possible, even if the full handshake cannot be derived.
+ assert '1\tTrue\t%s\t%s' % (self.key_Spub_i, '') in lines
+ assert '13\tTrue\t%s\t%s' % (self.key_Spub_i, '') in lines
+
+ def test_decrypt_full_initiator(self, run_wireguard_test):
+ """
+ Check for full handshake decryption using Spriv_r + Epriv_i.
+ The public key Spub_r is provided via the key log as well.
+ """
+ lines = run_wireguard_test(self, [
+ '-Tfields',
+ '-e', 'frame.number',
+ '-e', 'wg.ephemeral.known_privkey',
+ '-e', 'wg.static',
+ '-e', 'wg.timestamp.nanoseconds',
+ '-e', 'wg.handshake_ok',
+ '-e', 'icmp.type',
+ '-e', 'tcp.dstport',
+ ], keylog=[
+ ' REMOTE_STATIC_PUBLIC_KEY = %s' % self.key_Spub_r,
+ ' LOCAL_STATIC_PRIVATE_KEY = %s' % self.key_Spriv_i_alt,
+ ' LOCAL_EPHEMERAL_PRIVATE_KEY = %s' % self.key_Epriv_i0_alt,
+ ' LOCAL_EPHEMERAL_PRIVATE_KEY = %s' % self.key_Epriv_i1,
+ ])
+ assert '1\tTrue\t%s\t%s\t\t\t' % (self.key_Spub_i, '356537872') in lines
+ assert '2\tFalse\t\t\tTrue\t\t' in lines
+ assert '3\t\t\t\t\t8\t' in lines
+ assert '4\t\t\t\t\t0\t' in lines
+ assert '13\tTrue\t%s\t%s\t\t\t' % (self.key_Spub_i, '490514356') in lines
+ assert '14\tFalse\t\t\tTrue\t\t' in lines
+ assert '17\t\t\t\t\t\t443' in lines
+ assert '18\t\t\t\t\t\t49472' in lines
+
+ def test_decrypt_wg_full_initiator_dsb(self, run_wireguard_test):
+ """
+ Similar to test_decrypt_full_initiator, but using decryption keys
+ embedded in the pcapng file. The embedded secrets do not contain leading
+ spaces nor spaces around the '=' character.
+ """
+ lines = run_wireguard_test(self, [
+ '-Tfields',
+ '-e', 'frame.number',
+ '-e', 'wg.ephemeral.known_privkey',
+ '-e', 'wg.static',
+ '-e', 'wg.timestamp.nanoseconds',
+ '-e', 'wg.handshake_ok',
+ '-e', 'icmp.type',
+ '-e', 'tcp.dstport',
+ ], pcap_file='wireguard-ping-tcp-dsb.pcapng')
+ assert '1\tTrue\t%s\t%s\t\t\t' % (self.key_Spub_i, '356537872') in lines
+ assert '2\tFalse\t\t\tTrue\t\t' in lines
+ assert '3\t\t\t\t\t8\t' in lines
+ assert '4\t\t\t\t\t0\t' in lines
+ assert '13\tTrue\t%s\t%s\t\t\t' % (self.key_Spub_i, '490514356') in lines
+ assert '14\tFalse\t\t\tTrue\t\t' in lines
+ assert '17\t\t\t\t\t\t443' in lines
+ assert '18\t\t\t\t\t\t49472' in lines
+
+ def test_decrypt_full_responder(self, run_wireguard_test):
+ """Check for full handshake decryption using responder secrets."""
+ lines = run_wireguard_test(self, [
+ '-Tfields',
+ '-e', 'frame.number',
+ '-e', 'wg.ephemeral.known_privkey',
+ '-e', 'wg.static',
+ '-e', 'wg.timestamp.nanoseconds',
+ '-e', 'wg.handshake_ok',
+ '-e', 'icmp.type',
+ '-e', 'tcp.dstport',
+ ], keylog=[
+ 'REMOTE_STATIC_PUBLIC_KEY=%s' % self.key_Spub_i,
+ 'LOCAL_STATIC_PRIVATE_KEY=%s' % self.key_Spriv_r,
+ 'LOCAL_EPHEMERAL_PRIVATE_KEY=%s' % self.key_Epriv_r0,
+ 'LOCAL_EPHEMERAL_PRIVATE_KEY=%s' % self.key_Epriv_r1,
+ ])
+ assert '1\tFalse\t%s\t%s\t\t\t' % (self.key_Spub_i, '356537872') in lines
+ assert '2\tTrue\t\t\tTrue\t\t' in lines
+ assert '3\t\t\t\t\t8\t' in lines
+ assert '4\t\t\t\t\t0\t' in lines
+ assert '13\tFalse\t%s\t%s\t\t\t' % (self.key_Spub_i, '490514356') in lines
+ assert '14\tTrue\t\t\tTrue\t\t' in lines
+ assert '17\t\t\t\t\t\t443' in lines
+ assert '18\t\t\t\t\t\t49472' in lines
+
+ def test_decrypt_psk_initiator(self, run_wireguard_test):
+ """Check whether PSKs enable decryption for initiation keys."""
+ lines = run_wireguard_test(self, [
+ '-Tfields',
+ '-e', 'frame.number',
+ '-e', 'wg.handshake_ok',
+ ], keylog=[
+ 'REMOTE_STATIC_PUBLIC_KEY = %s' % self.key_Spub_r,
+ 'LOCAL_STATIC_PRIVATE_KEY = %s' % self.key_Spriv_i,
+ 'LOCAL_EPHEMERAL_PRIVATE_KEY=%s' % self.key_Epriv_i2,
+ 'PRESHARED_KEY=%s' % self.key_psk2,
+ 'LOCAL_EPHEMERAL_PRIVATE_KEY=%s' % self.key_Epriv_r3,
+ 'PRESHARED_KEY=%s' % self.key_psk3,
+ ], pcap_file='wireguard-psk.pcap')
+ assert '2\tTrue' in lines
+ assert '4\tTrue' in lines
+
+ def test_decrypt_psk_responder(self, run_wireguard_test):
+ """Check whether PSKs enable decryption for responder keys."""
+ lines = run_wireguard_test(self, [
+ '-Tfields',
+ '-e', 'frame.number',
+ '-e', 'wg.handshake_ok',
+ ], keylog=[
+ 'REMOTE_STATIC_PUBLIC_KEY=%s' % self.key_Spub_i,
+ 'LOCAL_STATIC_PRIVATE_KEY=%s' % self.key_Spriv_r,
+ # Epriv_r2 needs psk2. This tests handling of duplicate ephemeral
+ # keys with multiple PSKs. It should not have adverse effects.
+ 'LOCAL_EPHEMERAL_PRIVATE_KEY=%s' % self.key_Epriv_r2,
+ 'PRESHARED_KEY=%s' % self.key_dummy,
+ 'LOCAL_EPHEMERAL_PRIVATE_KEY=%s' % self.key_Epriv_r2,
+ 'PRESHARED_KEY=%s' % self.key_psk2,
+ 'LOCAL_EPHEMERAL_PRIVATE_KEY=%s' % self.key_Epriv_i3,
+ 'PRESHARED_KEY=%s' % self.key_psk3,
+ # Epriv_i3 needs psk3, this tests that additional keys again have no
+ # bad side-effects.
+ 'LOCAL_EPHEMERAL_PRIVATE_KEY=%s' % self.key_Epriv_i3,
+ 'PRESHARED_KEY=%s' % self.key_dummy,
+ ], pcap_file='wireguard-psk.pcap')
+ assert '2\tTrue' in lines
+ assert '4\tTrue' in lines
+
+ def test_decrypt_psk_wrong_orderl(self, run_wireguard_test):
+ """Check that the wrong order of lines indeed fail decryption."""
+ lines = run_wireguard_test(self, [
+ '-Tfields',
+ '-e', 'frame.number',
+ '-e', 'wg.handshake_ok',
+ ], keylog=[
+ 'REMOTE_STATIC_PUBLIC_KEY=%s' % self.key_Spub_i,
+ 'LOCAL_STATIC_PRIVATE_KEY=%s' % self.key_Spriv_r,
+ 'LOCAL_EPHEMERAL_PRIVATE_KEY=%s' % self.key_Epriv_r2,
+ 'LOCAL_EPHEMERAL_PRIVATE_KEY=%s' % self.key_Epriv_i3,
+ 'PRESHARED_KEY=%s' % self.key_psk2, # note: swapped with previous line
+ 'PRESHARED_KEY=%s' % self.key_psk3,
+ ], pcap_file='wireguard-psk.pcap')
+ assert '2\tFalse' in lines
+ assert '4\tFalse' in lines
+
+
+class TestDecryptKnxip:
+ # Capture files for these tests contain single telegrams.
+ # For realistic (live captured) KNX/IP telegram sequences, see:
+ # https://gitlab.com/wireshark/wireshark/-/issues/14825
+
+ def test_knxip_data_security_decryption_ok(self, cmd_tshark, capture_file, test_env):
+ '''KNX/IP: Data Security decryption OK'''
+ # capture_file('knxip_DataSec.pcap') contains KNX/IP ConfigReq DataSec PropExtValueWriteCon telegram
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('knxip_DataSec.pcap'),
+ '-o', 'kip.key_1:00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, ' DataSec ')
+ assert grep_output(stdout, ' PropExtValueWriteCon ')
+
+ def test_knxip_data_security_decryption_fails(self, cmd_tshark, capture_file, test_env):
+ '''KNX/IP: Data Security decryption fails'''
+ # capture_file('knxip_DataSec.pcap') contains KNX/IP ConfigReq DataSec PropExtValueWriteCon telegram
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('knxip_DataSec.pcap'),
+ '-o', 'kip.key_1:""', # "" is really necessary, otherwise test fails
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, ' DataSec ')
+ assert not grep_output(stdout, ' PropExtValueWriteCon ')
+
+ def test_knxip_secure_wrapper_decryption_ok(self, cmd_tshark, capture_file, test_env):
+ '''KNX/IP: SecureWrapper decryption OK'''
+ # capture_file('knxip_SecureWrapper.pcap') contains KNX/IP SecureWrapper RoutingInd telegram
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('knxip_SecureWrapper.pcap'),
+ '-o', 'kip.key_1:00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, ' SecureWrapper ')
+ assert grep_output(stdout, ' RoutingInd ')
+
+ def test_knxip_secure_wrapper_decryption_fails(self, cmd_tshark, capture_file, test_env):
+ '''KNX/IP: SecureWrapper decryption fails'''
+ # capture_file('knxip_SecureWrapper.pcap') contains KNX/IP SecureWrapper RoutingInd telegram
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('knxip_SecureWrapper.pcap'),
+ '-o', 'kip.key_1:""', # "" is really necessary, otherwise test fails
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, ' SecureWrapper ')
+ assert not grep_output(stdout, ' RoutingInd ')
+
+ def test_knxip_timer_notify_authentication_ok(self, cmd_tshark, capture_file, test_env):
+ '''KNX/IP: TimerNotify authentication OK'''
+ # capture_file('knxip_TimerNotify.pcap') contains KNX/IP TimerNotify telegram
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('knxip_TimerNotify.pcap'),
+ '-o', 'kip.key_1:00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, ' TimerNotify ')
+ assert grep_output(stdout, ' OK$')
+
+ def test_knxip_timer_notify_authentication_fails(self, cmd_tshark, capture_file, test_env):
+ '''KNX/IP: TimerNotify authentication fails'''
+ # capture_file('knxip_TimerNotify.pcap') contains KNX/IP TimerNotify telegram
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('knxip_TimerNotify.pcap'),
+ '-o', 'kip.key_1:""', # "" is really necessary, otherwise test fails
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, ' TimerNotify ')
+ assert not grep_output(stdout, ' OK$')
+
+ def test_knxip_keyring_xml_import(self, cmd_tshark, dirs, capture_file, test_env):
+ '''KNX/IP: keyring.xml import'''
+ # key_file "keyring.xml" contains KNX decryption keys
+ key_file = os.path.join(dirs.key_dir, 'knx_keyring.xml')
+ # capture_file('empty.pcap') is empty
+ # Write extracted key info to stdout
+ stdout = subprocess.check_output((cmd_tshark,
+ '-o', 'kip.key_file:' + key_file,
+ '-o', 'kip.key_info_file:-',
+ '-r', capture_file('empty.pcap'),
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, '^MCA 224[.]0[.]23[.]12 key A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF$')
+ assert grep_output(stdout, '^GA 1/7/131 sender 1[.]1[.]1$')
+ assert grep_output(stdout, '^GA 1/7/131 sender 1[.]1[.]3$')
+ assert grep_output(stdout, '^GA 1/7/131 sender 1[.]1[.]4$')
+ assert grep_output(stdout, '^GA 1/7/132 sender 1[.]1[.]2$')
+ assert grep_output(stdout, '^GA 1/7/132 sender 1[.]1[.]4$')
+ assert grep_output(stdout, '^GA 6/7/191 sender 1[.]1[.]1$')
+ assert grep_output(stdout, '^GA 0/1/0 sender 1[.]1[.]1$')
+ assert grep_output(stdout, '^GA 0/1/0 sender 1[.]1[.]3$')
+ assert grep_output(stdout, '^GA 0/1/0 sender 1[.]1[.]4$')
+ assert grep_output(stdout, '^GA 0/1/0 key A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF$')
+ assert grep_output(stdout, '^GA 1/7/131 key A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF$')
+ assert grep_output(stdout, '^GA 1/7/132 key A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF$')
+ assert grep_output(stdout, '^GA 6/7/191 key A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 AA AB AC AD AE AF$')
+ assert grep_output(stdout, '^IA 1[.]1[.]1 key B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF$')
+ assert grep_output(stdout, '^IA 1[.]1[.]1 SeqNr 45678$')
+ assert grep_output(stdout, '^IA 1[.]1[.]2 key B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF$')
+ assert grep_output(stdout, '^IA 1[.]1[.]2 SeqNr 34567$')
+ assert grep_output(stdout, '^IA 1[.]1[.]3 key B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF$')
+ assert grep_output(stdout, '^IA 1[.]1[.]3 SeqNr 23456$')
+ assert grep_output(stdout, '^IA 1[.]1[.]4 key B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF$')
+ assert grep_output(stdout, '^IA 1[.]1[.]4 SeqNr 12345$')
+ assert grep_output(stdout, '^IA 2[.]1[.]0 key B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF$')
+ assert grep_output(stdout, '^IA 2[.]1[.]0 SeqNr 1234$')
+
+
+@pytest.fixture(scope='session')
+def softhsm_paths(features):
+ if sys.platform == 'win32':
+ search_path = os.getenv('PATH') + r';C:\SoftHSM2\bin'
+ else:
+ search_path = None
+ softhsm_tool = shutil.which('softhsm2-util', path=search_path)
+ if not softhsm_tool:
+ # Note: do not fallback to SoftHSMv1. While available on Ubuntu 14.04
+ # (and 16.04), it is built with botan < 1.11.10 which causes a crash due
+ # to a conflict with the GMP library that is also used by GnuTLS/nettle.
+ # See https://github.com/randombit/botan/issues/1090
+ pytest.skip('SoftHSM is not found')
+ # Find provider library path.
+ bindir = os.path.dirname(softhsm_tool)
+ libdir = os.path.join(os.path.dirname(bindir), 'lib')
+ if sys.platform == 'win32':
+ libdirs = [libdir, bindir]
+ if features.have_x64:
+ name = 'softhsm2-x64.dll'
+ else:
+ name = 'softhsm2.dll'
+ else:
+ # Look in a variety of paths, Debian/Ubuntu, Fedora, RHEL/CentOS
+ madir = sysconfig.get_config_var('multiarchsubdir')
+ libdir_archs = (libdir, libdir + '64')
+ libdir_subs = ('softhsm', 'pkcs11', '')
+ libdirs = [os.path.join(libdir + madir, 'softhsm')] if madir else []
+ libdirs += [os.path.join(arch, sub) for sub in libdir_subs for arch in libdir_archs]
+ name = 'libsofthsm2.so'
+ for libdir in libdirs:
+ provider = os.path.join(libdir, name)
+ if os.path.exists(provider):
+ break
+ else:
+ # Even if p11-kit can automatically locate it, do not rely on it.
+ pytest.skip('SoftHSM provider library not detected')
+ # Now check whether the import tool is usable. SoftHSM < 2.3.0 did not
+ # set CKA_DECRYPT when using softhsm2-tool --import and therefore cannot be
+ # used to import keys for decryption. Use GnuTLS p11tool as workaround.
+ softhsm_version = subprocess.check_output([softhsm_tool, '--version'],
+ universal_newlines=True).strip()
+ use_p11tool = softhsm_version in ('2.0.0', '2.1.0', '2.2.0')
+ if use_p11tool and not shutil.which('p11tool'):
+ pytest.skip('SoftHSM available, but GnuTLS p11tool is unavailable')
+ return use_p11tool, softhsm_tool, provider
+
+
+@pytest.fixture
+def softhsm(softhsm_paths, home_path, base_env):
+ '''Creates a temporary SoftHSM token store (and set it in the environment),
+ returns a function to populate that token store and the path to the PKCS #11
+ provider library.'''
+ use_p11tool, softhsm_tool, provider = softhsm_paths
+ conf_path = os.path.join(home_path, 'softhsm-test.conf')
+ db_path = os.path.join(home_path, 'softhsm-test-tokens')
+ os.makedirs(db_path)
+ with open(conf_path, 'w') as f:
+ f.write('directories.tokendir = %s\n' % db_path)
+ f.write('objectstore.backend = file\n')
+ # Avoid syslog spam
+ f.write('log.level = ERROR\n')
+ base_env['SOFTHSM2_CONF'] = conf_path
+
+ tool_env = base_env.copy()
+ if sys.platform == 'win32':
+ # Ensure that softhsm2-util can find the library.
+ tool_env['PATH'] += ';%s' % os.path.dirname(provider)
+
+ # Initialize tokens store.
+ token_name = 'Wireshark-Test-Tokens'
+ pin = 'Secret'
+ subprocess.check_call([softhsm_tool, '--init-token', '--slot', '0',
+ '--label', token_name, '--so-pin', 'Supersecret', '--pin', pin],
+ env=tool_env)
+ if use_p11tool:
+ tool_env['GNUTLS_PIN'] = pin
+
+ # Arbitrary IDs and labels.
+ ids = iter(range(0xab12, 0xffff))
+ def import_key(keyfile):
+ '''Returns a PKCS #11 URI to identify the imported key.'''
+ label = os.path.basename(keyfile)
+ obj_id = '%x' % next(ids)
+ if not use_p11tool:
+ tool_args = [softhsm_tool, '--import', keyfile, '--label', label,
+ '--id', obj_id, '--pin', pin, '--token', token_name]
+ else:
+ # Fallback for SoftHSM < 2.3.0
+ tool_args = ['p11tool', '--provider', provider, '--batch',
+ '--login', '--write', 'pkcs11:token=%s' % token_name,
+ '--load-privkey', keyfile, '--label', label, '--id', obj_id]
+ subprocess.check_call(tool_args, env=tool_env)
+ id_str = '%{}{}%{}{}'.format(*obj_id)
+ return 'pkcs11:token=%s;id=%s;type=private' % (token_name, id_str)
+
+ return types.SimpleNamespace(import_key=import_key, provider=provider, pin=pin)
+
+
+class TestDecryptPkcs11:
+ def test_tls_pkcs11(self, cmd_tshark, dirs, capture_file, features, softhsm, test_env):
+ '''Check that a RSA key in a PKCS #11 token enables decryption.'''
+ if not features.have_pkcs11:
+ pytest.skip('Requires GnuTLS with PKCS #11 support.')
+ key_file = os.path.join(dirs.key_dir, 'rsa-p-lt-q.p8')
+ key_uri = softhsm.import_key(key_file)
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('rsa-p-lt-q.pcap'),
+ '-o', 'uat:pkcs11_libs:"{}"'.format(softhsm.provider.replace('\\', '\\x5c')),
+ '-o', 'uat:rsa_keys:"{}","{}"'.format(key_uri, softhsm.pin),
+ '-Tfields',
+ '-e', 'http.request.uri',
+ '-Y', 'http',
+ ), encoding='utf-8', env=test_env)
+ assert '/' in stdout
+
+class TestDecryptSmb2:
+ BAD_KEY = 'ffffffffffffffffffffffffffffffff'
+
+ @staticmethod
+ def check_bad_key(cmd_tshark, cap, disp_filter, sesid, seskey, s2ckey, c2skey, env=None):
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', cap,
+ '-o', 'uat:smb2_seskey_list:{},{},{},{}'.format(sesid, seskey, s2ckey, c2skey),
+ '-Y', disp_filter,
+ ), encoding='utf-8', env=env)
+ assert 'Encrypted SMB' in stdout
+
+ #
+ # SMB3.0 CCM bad keys tests
+ #
+ def test_smb300_bad_seskey(self, features, cmd_tshark, capture_file, test_env):
+ '''Check that a bad session key doesn't crash'''
+ TestDecryptSmb2.check_bad_key(cmd_tshark, capture_file('smb300-aes-128-ccm.pcap.gz'),
+ 'frame.number == 7', '1900009c003c0000', self.BAD_KEY, '""', '""', env=test_env)
+
+ def test_smb300_bad_s2ckey(self, features, cmd_tshark, capture_file, test_env):
+ '''Check that a bad s2c key doesn't crash'''
+ TestDecryptSmb2.check_bad_key(cmd_tshark, capture_file('smb300-aes-128-ccm.pcap.gz'),
+ 'frame.number == 7', '1900009c003c0000', '""', self.BAD_KEY, '""', env=test_env)
+
+ def test_smb300_bad_c2skey(self, features, cmd_tshark, capture_file, test_env):
+ '''Check that a bad c2s key doesn't crash'''
+ TestDecryptSmb2.check_bad_key(cmd_tshark, capture_file('smb300-aes-128-ccm.pcap.gz'),
+ 'frame.number == 7', '1900009c003c0000', '""', '""', self.BAD_KEY, env=test_env)
+
+ def test_smb300_bad_deckey(self, features, cmd_tshark, capture_file, test_env):
+ '''Check that bad decryption keys doesn't crash'''
+ TestDecryptSmb2.check_bad_key(cmd_tshark, capture_file('smb300-aes-128-ccm.pcap.gz'),
+ 'frame.number == 7', '1900009c003c0000', '""', self.BAD_KEY, self.BAD_KEY, env=test_env)
+
+ def test_smb300_bad_allkey(self, features, cmd_tshark, capture_file, test_env):
+ '''Check that all bad keys doesn't crash'''
+ TestDecryptSmb2.check_bad_key(cmd_tshark, capture_file('smb300-aes-128-ccm.pcap.gz'),
+ 'frame.number == 7', '1900009c003c0000', self.BAD_KEY, self.BAD_KEY, self.BAD_KEY, env=test_env)
+
+ #
+ # SMB3.1.1 CCM bad key tests
+ #
+ def test_smb311_bad_seskey(self, features, cmd_tshark, capture_file, test_env):
+ '''Check that a bad session key doesn't crash'''
+ TestDecryptSmb2.check_bad_key(cmd_tshark, capture_file('smb311-aes-128-ccm.pcap.gz'),
+ 'frame.number == 7', '2900009c003c0000', self.BAD_KEY, '""', '""', env=test_env)
+
+ def test_smb311_bad_s2ckey(self, features, cmd_tshark, capture_file, test_env):
+ '''Check that a bad s2c key doesn't crash'''
+ TestDecryptSmb2.check_bad_key(cmd_tshark, capture_file('smb311-aes-128-ccm.pcap.gz'),
+ 'frame.number == 7', '2900009c003c0000', '""', self.BAD_KEY, '""', env=test_env)
+
+ def test_smb311_bad_c2skey(self, features, cmd_tshark, capture_file, test_env):
+ '''Check that a bad c2s key doesn't crash'''
+ TestDecryptSmb2.check_bad_key(cmd_tshark, capture_file('smb311-aes-128-ccm.pcap.gz'),
+ 'frame.number == 7', '2900009c003c0000', '""', '""', self.BAD_KEY, env=test_env)
+
+ def test_smb311_bad_deckey(self, features, cmd_tshark, capture_file, test_env):
+ '''Check that bad decryption keys doesn't crash'''
+ TestDecryptSmb2.check_bad_key(cmd_tshark, capture_file('smb311-aes-128-ccm.pcap.gz'),
+ 'frame.number == 7', '2900009c003c0000', '""', self.BAD_KEY, self.BAD_KEY, env=test_env)
+
+ def test_smb311_bad_allkey(self, features, cmd_tshark, capture_file, test_env):
+ '''Check that all bad keys doesn't crash'''
+ TestDecryptSmb2.check_bad_key(cmd_tshark, capture_file('smb311-aes-128-ccm.pcap.gz'),
+ 'frame.number == 7', '2900009c003c0000', self.BAD_KEY, self.BAD_KEY, self.BAD_KEY, env=test_env)
+
+ #
+ # Decryption tests
+ #
+
+ def check_tree(cmd_tshark, cap, tree, sesid, seskey, s2ckey, c2skey, env=None):
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', cap,
+ '-o', 'uat:smb2_seskey_list:{},{},{},{}'.format(sesid, seskey, s2ckey, c2skey),
+ '-Tfields',
+ '-e', 'smb2.tree',
+ '-Y', 'smb2.tree == "{}"'.format(tree.replace('\\', '\\\\')),
+ ), encoding='utf-8', env=env)
+ assert tree == stdout.strip()
+
+ # SMB3.0 CCM
+ def test_smb300_aes128ccm_seskey(self, features, cmd_tshark, capture_file, test_env):
+ '''Check SMB 3.0 AES128CCM decryption with session key.'''
+ TestDecryptSmb2.check_tree(cmd_tshark, capture_file('smb300-aes-128-ccm.pcap.gz'),
+ r'\\dfsroot1.foo.test\IPC$', '1900009c003c0000',
+ '9a9ea16a0cdbeb6064772318073f172f', '""', '""', env=test_env)
+
+ def test_smb300_aes128ccm_deckey(self, features, cmd_tshark, capture_file, test_env):
+ '''Check SMB 3.0 AES128CCM decryption with decryption keys.'''
+ TestDecryptSmb2.check_tree(cmd_tshark, capture_file('smb300-aes-128-ccm.pcap.gz'),
+ r'\\dfsroot1.foo.test\IPC$', '1900009c003c0000',
+ '""', '8be6cc53d4beba29387e69aef035d497','bff985870e81784d533fdc09497b8eab', env=test_env)
+
+
+ # SMB3.1.1 AES-CCM-128
+ def test_smb311_aes128ccm_seskey(self, features, cmd_tshark, capture_file, test_env):
+ '''Check SMB 3.1.1 AES128CCM decryption with session key.'''
+ TestDecryptSmb2.check_tree(cmd_tshark, capture_file('smb311-aes-128-ccm.pcap.gz'),
+ r'\\dfsroot1.foo.test\IPC$', '2900009c003c0000',
+ 'f1fa528d3cd182cca67bd4596dabd885', '""', '""', env=test_env)
+
+ def test_smb311_aes128ccm_deckey(self, features, cmd_tshark, capture_file, test_env):
+ '''Check SMB 3.1.1 AES128CCM decryption with decryption keys.'''
+ TestDecryptSmb2.check_tree(cmd_tshark, capture_file('smb311-aes-128-ccm.pcap.gz'),
+ r'\\dfsroot1.foo.test\IPC$', '2900009c003c0000',
+ '""', '763d5552dbc9650b700869467a5857e4', '35e69833c6578e438c8701cb40bf483e', env=test_env)
+
+ # SMB3.1.1 AES-GCM-128
+ def test_smb311_aes128gcm_seskey(self, features, cmd_tshark, capture_file, test_env):
+ '''Check SMB 3.1.1 AES128GCM decryption with session key.'''
+ TestDecryptSmb2.check_tree(cmd_tshark, capture_file('smb311-aes-128-gcm.pcap.gz'),
+ r'\\dfsroot1.foo.test\IPC$', '3900000000400000',
+ 'e79161ded03bda1449b2c8e58f753953', '""', '""', env=test_env)
+
+ def test_smb311_aes128gcm_deckey(self, features, cmd_tshark, capture_file, test_env):
+ '''Check SMB 3.1.1 AES128GCM decryption with decryption keys.'''
+ TestDecryptSmb2.check_tree(cmd_tshark, capture_file('smb311-aes-128-gcm.pcap.gz'),
+ r'\\dfsroot1.foo.test\IPC$', '3900000000400000',
+ '""', 'b02f5de25e0562075c3dc329fa2aa396', '7201623a31754e6581864581209dd3d2', env=test_env)
+
+ # SMB3.1.1 AES-CCM-256
+ def test_smb311_aes256ccm_seskey(self, features, cmd_tshark, capture_file, test_env):
+ '''Check SMB 3.1.1 AES256CCM decryption with session key.'''
+ TestDecryptSmb2.check_tree(cmd_tshark, capture_file('smb311-aes-256-ccm.pcap.gz'),
+ r'\\172.31.9.163\IPC$', 'd6fdb96d00000000',
+ '6b559c2e60519e344581d086a6d3d050',
+ '""',
+ '""', env=test_env)
+
+ def test_smb311_aes256ccm_deckey(self, features, cmd_tshark, capture_file, test_env):
+ '''Check SMB 3.1.1 AES256CCM decryption with decryption keys.'''
+ TestDecryptSmb2.check_tree(cmd_tshark, capture_file('smb311-aes-256-ccm.pcap.gz'),
+ r'\\172.31.9.163\IPC$', 'd6fdb96d00000000',
+ '""',
+ '014fccd4a53554bf5b54b27a32512b35fca262b90e088a5efa7d6c952418578b',
+ '1d34170138a77dac4abbe0149253c8b977a71f399081cda6cbaf62359670c1c5', env=test_env)
+
+ # SMB3.1.1 AES-GCM-256
+ def test_smb311_aes256gcm_seskey(self, features, cmd_tshark, capture_file, test_env):
+ '''Check SMB 3.1.1 AES256GCM decryption with session key.'''
+ TestDecryptSmb2.check_tree(cmd_tshark, capture_file('smb311-aes-256-gcm.pcap.gz'),
+ r'\\172.31.9.163\IPC$', '56dc03ab00000000',
+ '6a5004adfbdef1abd5879800675324e5',
+ '""',
+ '""', env=test_env)
+
+ def test_smb311_aes256gcm_deckey(self, features, cmd_tshark, capture_file, test_env):
+ '''Check SMB 3.1.1 AES256GCM decryption with decryption keys.'''
+ TestDecryptSmb2.check_tree(cmd_tshark, capture_file('smb311-aes-256-gcm.pcap.gz'),
+ r'\\172.31.9.163\IPC$', '56dc03ab00000000',
+ '""',
+ '46b64f320a0f856b63b3a0dc2c058a67267830a8cbdd44a088fbf1d0308a981f',
+ '484c30bf3e17e322e0d217764d4584a325ec0495519c3f1547e0f996ab76c4c4', env=test_env)
+
+ def check_partial(home_path, cmd_tshark, full_cap, pkt_skip, tree, sesid, s2ckey, c2skey, env=None):
+ # generate a trace without NegProt and SessionSetup
+ partial_cap = os.path.join(home_path, 'short.pcap')
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', full_cap,
+ '-Y', 'frame.number >= %d'%pkt_skip,
+ '-w', partial_cap,
+ ), encoding='utf-8', env=env)
+ TestDecryptSmb2.check_tree(cmd_tshark, partial_cap, tree, sesid, '""', s2ckey, c2skey)
+
+ def test_smb311_aes128gcm_partial(self, features, home_path, cmd_tshark, capture_file, test_env):
+ '''Check SMB 3.1.1 AES128GCM decryption in capture missing session setup'''
+ TestDecryptSmb2.check_partial(home_path, cmd_tshark,
+ capture_file('smb311-aes-128-gcm.pcap.gz'), 7,
+ r'\\dfsroot1.foo.test\IPC$', '3900000000400000',
+ 'b02f5de25e0562075c3dc329fa2aa396', '7201623a31754e6581864581209dd3d2', env=test_env)
+
+ def test_smb311_aes128gcm_partial_keyswap(self, features, home_path, cmd_tshark, capture_file, test_env):
+ '''Check SMB 3.1.1 AES128GCM decryption in capture missing session setup with keys in wrong order'''
+ TestDecryptSmb2.check_partial(home_path, cmd_tshark,
+ capture_file('smb311-aes-128-gcm.pcap.gz'), 7,
+ r'\\dfsroot1.foo.test\IPC$', '3900000000400000',
+ '7201623a31754e6581864581209dd3d2', 'b02f5de25e0562075c3dc329fa2aa396', env=test_env)
+
+ def test_smb311_aes256gcm_partial(self, features, home_path, cmd_tshark, capture_file, test_env):
+ '''Check SMB 3.1.1 AES128GCM decryption in capture missing session setup'''
+ TestDecryptSmb2.check_partial(home_path, cmd_tshark,
+ capture_file('smb311-aes-256-gcm.pcap.gz'), 7,
+ r'\\172.31.9.163\IPC$', '56dc03ab00000000',
+ '46b64f320a0f856b63b3a0dc2c058a67267830a8cbdd44a088fbf1d0308a981f',
+ '484c30bf3e17e322e0d217764d4584a325ec0495519c3f1547e0f996ab76c4c4', env=test_env)
+
+ def test_smb311_aes256gcm_partial_keyswap(self, features, home_path, cmd_tshark, capture_file, test_env):
+ '''Check SMB 3.1.1 AES256GCM decryption in capture missing session setup with keys in wrong order'''
+ TestDecryptSmb2.check_partial(home_path, cmd_tshark,
+ capture_file('smb311-aes-256-gcm.pcap.gz'), 7,
+ r'\\172.31.9.163\IPC$', '56dc03ab00000000',
+ '484c30bf3e17e322e0d217764d4584a325ec0495519c3f1547e0f996ab76c4c4',
+ '46b64f320a0f856b63b3a0dc2c058a67267830a8cbdd44a088fbf1d0308a981f', env=test_env)
diff --git a/test/suite_dfilter/__init__.py b/test/suite_dfilter/__init__.py
new file mode 100644
index 0000000..0b66e0b
--- /dev/null
+++ b/test/suite_dfilter/__init__.py
@@ -0,0 +1,8 @@
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+import pytest
+
+# Enable assertion introspection
+pytest.register_assert_rewrite('suite_dfilter.dfiltertest')
+
diff --git a/test/suite_dfilter/dfiltertest.py b/test/suite_dfilter/dfiltertest.py
new file mode 100644
index 0000000..8ca3005
--- /dev/null
+++ b/test/suite_dfilter/dfiltertest.py
@@ -0,0 +1,115 @@
+# Copyright (c) 2013 by Gilbert Ramirez <gram@alumni.rice.edu>
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+import subprocesstest
+from subprocesstest import count_output, grep_output
+import pytest
+import logging
+
+
+@pytest.fixture
+def dfilter_cmd(cmd_tshark, capture_file, request):
+ def wrapped(dfilter, frame_number=None, prefs=None, read_filter=False):
+ cmd = [
+ cmd_tshark,
+ "-n", # No name resolution
+ "-r", # Next arg is trace file to read
+ capture_file(request.instance.trace_file),
+ ]
+ if frame_number:
+ cmd.extend([
+ "-2", # two-pass mode
+ "--selected-frame={}".format(frame_number)
+ ])
+ if read_filter:
+ cmd.extend([
+ "-2", # two-pass mode
+ "-R", # read filter (requires two-pass mode)
+ dfilter
+ ])
+ else:
+ cmd.extend([
+ "-Y", # packet display filter (used to be -R)
+ dfilter
+ ])
+ if prefs:
+ cmd.extend([
+ "-o",
+ prefs
+ ])
+ return cmd
+ return wrapped
+
+@pytest.fixture(scope='session')
+def cmd_dftest(program):
+ return program('dftest')
+
+
+@pytest.fixture
+def checkDFilterCount(dfilter_cmd, base_env):
+ def checkDFilterCount_real(dfilter, expected_count, prefs=None):
+ """Run a display filter and expect a certain number of packets."""
+ proc = subprocesstest.check_run(dfilter_cmd(dfilter, prefs=prefs),
+ capture_output=True,
+ universal_newlines=True,
+ env=base_env)
+ if proc.stderr:
+ logging.debug(proc.stderr)
+ assert count_output(proc.stdout) == expected_count
+ return checkDFilterCount_real
+
+@pytest.fixture
+def checkDFilterCountWithSelectedFrame(dfilter_cmd, base_env):
+ def checkDFilterCount_real(dfilter, expected_count, selected_frame, prefs=None):
+ """Run a display filter and expect a certain number of packets."""
+ proc = subprocesstest.check_run(dfilter_cmd(dfilter, frame_number=selected_frame, prefs=prefs),
+ capture_output=True,
+ universal_newlines=True,
+ env=base_env)
+ if proc.stderr:
+ logging.debug(proc.stderr)
+ assert count_output(proc.stdout) == expected_count
+ return checkDFilterCount_real
+
+@pytest.fixture
+def checkDFilterCountReadFilter(dfilter_cmd, base_env):
+ def checkDFilterCount_real(dfilter, expected_count):
+ """Run a read filter in two pass mode and expect a certain number of packets."""
+ proc = subprocesstest.check_run(dfilter_cmd(dfilter, read_filter=True),
+ capture_output=True,
+ universal_newlines=True,
+ env=base_env)
+ if proc.stderr:
+ logging.debug(proc.stderr)
+ assert count_output(proc.stdout) == expected_count
+ return checkDFilterCount_real
+
+@pytest.fixture
+def checkDFilterFail(cmd_dftest, base_env):
+ def checkDFilterFail_real(dfilter, error_message):
+ """Run a display filter and expect dftest to fail."""
+ proc = subprocesstest.run([cmd_dftest, '--', dfilter],
+ capture_output=True,
+ universal_newlines=True,
+ env=base_env)
+ if proc.stderr:
+ logging.debug(proc.stderr)
+ assert proc.returncode == 4
+ assert error_message in proc.stderr
+ return checkDFilterFail_real
+
+@pytest.fixture
+def checkDFilterSucceed(cmd_dftest, base_env):
+ def checkDFilterSucceed_real(dfilter, expect_stdout=None):
+ """Run a display filter and expect dftest to succeed."""
+ proc = subprocesstest.run([cmd_dftest, '--', dfilter],
+ capture_output=True,
+ universal_newlines=True,
+ env=base_env)
+ if proc.stderr:
+ logging.debug(proc.stderr)
+ assert proc.returncode == 0
+ if expect_stdout:
+ assert expect_stdout in proc.stdout
+ return checkDFilterSucceed_real
diff --git a/test/suite_dfilter/group_bytes.py b/test/suite_dfilter/group_bytes.py
new file mode 100644
index 0000000..c622b07
--- /dev/null
+++ b/test/suite_dfilter/group_bytes.py
@@ -0,0 +1,25 @@
+# Copyright (c) 2013 by Gilbert Ramirez <gram@alumni.rice.edu>
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+import pytest
+from suite_dfilter.dfiltertest import *
+
+
+class TestDfilterBytes:
+ trace_file = "arp.pcap"
+
+ def test_bytes_1(self, checkDFilterCount):
+ dfilter = "arp.dst.hw == 00:64"
+ checkDFilterCount(dfilter, 1)
+
+ def test_ipv6_2(self, checkDFilterCount):
+ dfilter = "arp.dst.hw == 00:00"
+ checkDFilterCount(dfilter, 0)
+
+class TestDfilterBytesSyntax:
+
+ def test_oid_1(self, checkDFilterSucceed):
+ # OID value on the RHS is similar to a field name with all digits.
+ dfilter = "snmp.name == 1.3.6.1.2.1.1.3.0"
+ checkDFilterSucceed(dfilter)
diff --git a/test/suite_dfilter/group_columns.py b/test/suite_dfilter/group_columns.py
new file mode 100644
index 0000000..0547437
--- /dev/null
+++ b/test/suite_dfilter/group_columns.py
@@ -0,0 +1,54 @@
+# Copyright (c) 2013 by Gilbert Ramirez <gram@alumni.rice.edu>
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+import pytest
+from suite_dfilter.dfiltertest import *
+
+class TestDfilterColumns:
+ trace_file = "http.pcap"
+
+ def test_exists_1(self, checkDFilterCount):
+ dfilter = "_ws.col.info"
+ checkDFilterCount(dfilter, 1)
+
+ def test_exists_2(self, checkDFilterFail):
+ # Column not in the default configuration
+ dfilter = "_ws.col.expert"
+ error = f'"{dfilter}" is neither a field nor a protocol name'
+ checkDFilterFail(dfilter, error)
+
+ def test_exists_3(self, checkDFilterFail):
+ # Column not registered as field (it behaves unusally if filtered)
+ dfilter = "_ws.col.delta_time_dis"
+ error = f'"{dfilter}" is neither a field nor a protocol name'
+ checkDFilterFail(dfilter, error)
+
+ def test_func_1(self, checkDFilterCount):
+ dfilter = "len(_ws.col.protocol) == 4"
+ checkDFilterCount(dfilter, 1)
+
+ def test_matches_1(self, checkDFilterSucceed):
+ dfilter = '_ws.col.info matches "^HEAD"'
+ checkDFilterSucceed(dfilter)
+
+ def test_equal_1(self, checkDFilterCount):
+ dfilter = '_ws.col.protocol == "HTTP"'
+ checkDFilterCount(dfilter, 1)
+
+ def test_equal_2(self, checkDFilterCount):
+ dfilter = '_ws.col.def_dst == "207.46.134.94"'
+ checkDFilterCount(dfilter, 1)
+
+ def test_not_equal_1(self, checkDFilterCount):
+ dfilter = '_ws.col.def_src != "10.0.0.5"'
+ checkDFilterCount(dfilter, 0)
+
+ def test_read_filter(self, checkDFilterCountReadFilter):
+ dfilter = '_ws.col.protocol == "HTTP"'
+ checkDFilterCountReadFilter(dfilter, 1)
+
+ def test_add_column(self, checkDFilterCount):
+ # Add column to configuration
+ dfilter = '_ws.col.expert == "Chat"'
+ checkDFilterCount(dfilter, 1, 'gui.column.format:"Expert","%a"')
diff --git a/test/suite_dfilter/group_double.py b/test/suite_dfilter/group_double.py
new file mode 100644
index 0000000..449360b
--- /dev/null
+++ b/test/suite_dfilter/group_double.py
@@ -0,0 +1,91 @@
+# Copyright (c) 2013 by Gilbert Ramirez <gram@alumni.rice.edu>
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+import pytest
+from suite_dfilter.dfiltertest import *
+
+
+class TestDfilterDouble:
+
+ trace_file = "icmp.pcapng.gz"
+
+ def test_eq_1(self, checkDFilterCount):
+ dfilter = "icmp.resptime == 492.204"
+ checkDFilterCount(dfilter, 1)
+
+ def test_eq_2(self, checkDFilterCount):
+ dfilter = "icmp.resptime == 492.205"
+ checkDFilterCount(dfilter, 0)
+
+ def test_eq_3(self, checkDFilterCount):
+ dfilter = "icmp.resptime == 492204e-3"
+ checkDFilterCount(dfilter, 1)
+
+ def test_eq_4(self, checkDFilterCount):
+ dfilter = "icmp.resptime == 492205e-3"
+ checkDFilterCount(dfilter, 0)
+
+ def test_ne_1(self, checkDFilterCount):
+ dfilter = "icmp.resptime != 492.204"
+ checkDFilterCount(dfilter, 0)
+
+ def test_ne_2(self, checkDFilterCount):
+ dfilter = "icmp.resptime != 492.205"
+ checkDFilterCount(dfilter, 1)
+
+ def test_ne_3(self, checkDFilterCount):
+ dfilter = "icmp.resptime != 492204e-3"
+ checkDFilterCount(dfilter, 0)
+
+ def test_ne_4(self, checkDFilterCount):
+ dfilter = "icmp.resptime != 492205e-3"
+ checkDFilterCount(dfilter, 1)
+
+ def test_gt_1(self, checkDFilterCount):
+ dfilter = "icmp.resptime > 492"
+ checkDFilterCount(dfilter, 1)
+
+ def test_gt_2(self, checkDFilterCount):
+ dfilter = "icmp.resptime > 492.203"
+ checkDFilterCount(dfilter, 1)
+
+ def test_gt_3(self, checkDFilterCount):
+ dfilter = "icmp.resptime > 493"
+ checkDFilterCount(dfilter, 0)
+
+ def test_ge_1(self, checkDFilterCount):
+ dfilter = "icmp.resptime >= 493"
+ checkDFilterCount(dfilter, 0)
+
+ def test_ge_2(self, checkDFilterCount):
+ dfilter = "icmp.resptime >= 492"
+ checkDFilterCount(dfilter, 1)
+
+ def test_ge_3(self, checkDFilterCount):
+ dfilter = "icmp.resptime >= 492.204"
+ checkDFilterCount(dfilter, 1)
+
+ def test_lt_1(self, checkDFilterCount):
+ dfilter = "icmp.resptime < 493"
+ checkDFilterCount(dfilter, 1)
+
+ def test_lt_2(self, checkDFilterCount):
+ dfilter = "icmp.resptime < 492"
+ checkDFilterCount(dfilter, 0)
+
+ def test_lt_3(self, checkDFilterCount):
+ dfilter = "icmp.resptime < 492.204"
+ checkDFilterCount(dfilter, 0)
+
+ def test_le_1(self, checkDFilterCount):
+ dfilter = "icmp.resptime <= 492.204"
+ checkDFilterCount(dfilter, 1)
+
+ def test_le_2(self, checkDFilterCount):
+ dfilter = "icmp.resptime <= 493"
+ checkDFilterCount(dfilter, 1)
+
+ def test_le_3(self, checkDFilterCount):
+ dfilter = "icmp.resptime <= 492"
+ checkDFilterCount(dfilter, 0)
diff --git a/test/suite_dfilter/group_ether.py b/test/suite_dfilter/group_ether.py
new file mode 100644
index 0000000..9c43f2b
--- /dev/null
+++ b/test/suite_dfilter/group_ether.py
@@ -0,0 +1,108 @@
+# Copyright (c) 2013 by Gilbert Ramirez <gram@alumni.rice.edu>
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+import pytest
+from suite_dfilter.dfiltertest import *
+
+
+class TestDfilterEther:
+ trace_file = "ipx_rip.pcap"
+
+ ### Note: Bytes test does not yet test FT_INT64.
+
+ def test_eq_1(self, checkDFilterCount):
+ dfilter = "eth.dst == ff:ff:ff:ff:ff:ff"
+ checkDFilterCount(dfilter, 1)
+
+ def test_eq_2(self, checkDFilterCount):
+ dfilter = "eth.src == ff:ff:ff:ff:ff:ff"
+ checkDFilterCount(dfilter, 0)
+
+ def test_ne_1(self, checkDFilterCount):
+ dfilter = "eth.dst != ff:ff:ff:ff:ff:ff"
+ checkDFilterCount(dfilter, 0)
+
+ def test_ne_2(self, checkDFilterCount):
+ dfilter = "eth.src != ff:ff:ff:ff:ff:ff"
+ checkDFilterCount(dfilter, 1)
+
+ def test_gt_1(self, checkDFilterCount):
+ dfilter = "eth.src > 00:aa:00:a3:e3:ff"
+ checkDFilterCount(dfilter, 0)
+
+ def test_gt_2(self, checkDFilterCount):
+ dfilter = "eth.src > 00:aa:00:a3:e3:a4"
+ checkDFilterCount(dfilter, 0)
+
+ def test_gt_3(self, checkDFilterCount):
+ dfilter = "eth.src > 00:aa:00:a3:e3:00"
+ checkDFilterCount(dfilter, 1)
+
+ def test_ge_1(self, checkDFilterCount):
+ dfilter = "eth.src >= 00:aa:00:a3:e3:ff"
+ checkDFilterCount(dfilter, 0)
+
+ def test_ge_2(self, checkDFilterCount):
+ dfilter = "eth.src >= 00:aa:00:a3:e3:a4"
+ checkDFilterCount(dfilter, 1)
+
+ def test_ge_3(self, checkDFilterCount):
+ dfilter = "eth.src >= 00:aa:00:a3:e3:00"
+ checkDFilterCount(dfilter, 1)
+
+ def test_lt_1(self, checkDFilterCount):
+ dfilter = "eth.src < 00:aa:00:a3:e3:ff"
+ checkDFilterCount(dfilter, 1)
+
+ def test_lt_2(self, checkDFilterCount):
+ dfilter = "eth.src < 00:aa:00:a3:e3:a4"
+ checkDFilterCount(dfilter, 0)
+
+ def test_lt_3(self, checkDFilterCount):
+ dfilter = "eth.src < 00:aa:00:a3:e3:00"
+ checkDFilterCount(dfilter, 0)
+
+ def test_le_1(self, checkDFilterCount):
+ dfilter = "eth.src <= 00:aa:00:a3:e3:ff"
+ checkDFilterCount(dfilter, 1)
+
+ def test_le_2(self, checkDFilterCount):
+ dfilter = "eth.src <= 00:aa:00:a3:e3:a4"
+ checkDFilterCount(dfilter, 1)
+
+ def test_le_3(self, checkDFilterCount):
+ dfilter = "eth.src <= 00:aa:00:a3:e3:00"
+ checkDFilterCount(dfilter, 0)
+
+ def test_slice_1(self, checkDFilterCount):
+ dfilter = "eth.src[0:3] == 00:aa:00"
+ checkDFilterCount(dfilter, 1)
+
+ def test_slice_2(self, checkDFilterCount):
+ dfilter = "eth.src[-3:3] == a3:e3:a4"
+ checkDFilterCount(dfilter, 1)
+
+ def test_slice_3(self, checkDFilterCount):
+ dfilter = "eth.src[1:4] == aa:00:a3:e3"
+ checkDFilterCount(dfilter, 1)
+
+ def test_slice_4(self, checkDFilterCount):
+ dfilter = "eth.src[0] == 00"
+ checkDFilterCount(dfilter, 1)
+
+ def test_contains_1(self, checkDFilterCount):
+ dfilter = "ipx.src.node contains a3"
+ checkDFilterCount(dfilter, 1)
+
+ def test_contains_2(self, checkDFilterCount):
+ dfilter = "ipx.src.node contains a3:e3"
+ checkDFilterCount(dfilter, 1)
+
+ def test_contains_3(self, checkDFilterCount):
+ dfilter = "ipx.src.node contains 00:aa:00:a3:e3:a4"
+ checkDFilterCount(dfilter, 1)
+
+ def test_contains_4(self, checkDFilterCount):
+ dfilter = "ipx.src.node contains aa:e3"
+ checkDFilterCount(dfilter, 0)
diff --git a/test/suite_dfilter/group_function.py b/test/suite_dfilter/group_function.py
new file mode 100644
index 0000000..08c9795
--- /dev/null
+++ b/test/suite_dfilter/group_function.py
@@ -0,0 +1,87 @@
+# Copyright (c) 2019 by Dario Lombardo <lomato@gmail.com>
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+import pytest
+from suite_dfilter.dfiltertest import *
+
+class TestFunctionString:
+ trace_file = "dhcp.pcap"
+
+ def test_matches_1(self, checkDFilterCount):
+ dfilter = "string(frame.number) matches \"[13579]$\""
+ checkDFilterCount(dfilter, 2)
+
+ def test_contains_1(self, checkDFilterCount):
+ dfilter = "string(eth.src) contains \"00:08:74\""
+ checkDFilterCount(dfilter, 2)
+
+ def test_fail_1(self, checkDFilterFail):
+ # Invalid filter (only non-string fields are supported)
+ dfilter = "string(dhcp.server) == hostname"
+ error = 'String conversion for field "dhcp.server" is not supported'
+ checkDFilterFail(dfilter, error)
+
+ def test_fail_2(self, checkDFilterFail):
+ # Invalid field: value
+ dfilter = "string(123) == \"123\""
+ error = 'Only fields can be used as parameter for string()'
+ checkDFilterFail(dfilter, error)
+
+ def test_fail_3(self, checkDFilterFail):
+ # Invalid field: protocol
+ dfilter = "string(dhcp) == hostname"
+ error = 'String conversion for field "dhcp" is not supported'
+ checkDFilterFail(dfilter, error)
+
+ def test_fail_4(self, checkDFilterFail):
+ # Invalid field: bytes
+ dfilter = "string(dhcp.option.value) == \"hostname\""
+ error = 'String conversion for field "dhcp.option.value" is not supported'
+ checkDFilterFail(dfilter, error)
+
+class TestFunctionMaxMin:
+ trace_file = "sip.pcapng"
+
+ def test_min_1(self, checkDFilterCount):
+ dfilter = 'min(udp.srcport, udp.dstport) == 5060'
+ checkDFilterCount(dfilter, 5)
+
+ def test_min_2(self, checkDFilterCount):
+ dfilter = 'min(udp.srcport, udp.dstport) == 5070'
+ checkDFilterCount(dfilter, 0)
+
+ def test_max_1(self, checkDFilterCount):
+ dfilter = 'max(udp.srcport, udp.dstport) == 5070'
+ checkDFilterCount(dfilter, 3)
+
+ def test_max_2(self, checkDFilterCount):
+ dfilter = 'max(udp.srcport, udp.dstport) == 5060'
+ checkDFilterCount(dfilter, 2)
+
+ def test_max_3(self, checkDFilterCount):
+ dfilter = 'max(udp.srcport, udp.dstport) < 5060'
+ checkDFilterCount(dfilter, 1)
+
+ def test_max_4(self, checkDFilterCount):
+ dfilter = 'max(5060, udp.dstport) == udp.srcport'
+ checkDFilterCount(dfilter, 2)
+
+ def test_max_5(self, checkDFilterFail):
+ error = 'Constant expression is invalid on the LHS'
+ dfilter = 'max(5060, 5070) == udp.srcport'
+ checkDFilterFail(dfilter, error)
+
+class TestFunctionAbs:
+ trace_file = "dhcp.pcapng"
+
+ def test_function_abs_1(self, checkDFilterCount):
+ dfilter = 'udp.dstport == abs(-67)'
+ checkDFilterCount(dfilter, 2)
+
+class TestFunctionNested:
+ trace_file = 'http.pcap'
+
+ def test_function_nested_1(self, checkDFilterCount):
+ dfilter = 'abs(min(tcp.srcport, tcp.dstport)) == 80'
+ checkDFilterCount(dfilter, 1)
diff --git a/test/suite_dfilter/group_integer.py b/test/suite_dfilter/group_integer.py
new file mode 100644
index 0000000..b9ea99b
--- /dev/null
+++ b/test/suite_dfilter/group_integer.py
@@ -0,0 +1,195 @@
+# Copyright (c) 2013 by Gilbert Ramirez <gram@alumni.rice.edu>
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+import pytest
+from suite_dfilter.dfiltertest import *
+
+
+class TestDfilterInteger:
+ trace_file = "ntp.pcap"
+
+ def test_eq_1(self, checkDFilterCount):
+ dfilter = "ip.version == 4"
+ checkDFilterCount(dfilter, 1)
+
+ def test_eq_2(self, checkDFilterCount):
+ dfilter = "ip.version == 6"
+ checkDFilterCount(dfilter, 0)
+
+ def test_eq_3(self, checkDFilterFail):
+ # Invalid filter (only one equals sign)
+ dfilter = "ip.version = 4"
+ error = '"=" was unexpected in this context.'
+ checkDFilterFail(dfilter, error)
+
+ def test_eq_4(self, checkDFilterFail):
+ # Invalid filter
+ dfilter = "ip.version == the quick brown fox jumps over the lazy dog"
+ error = '"quick" was unexpected in this context.'
+ checkDFilterFail(dfilter, error)
+
+ def test_eq_5(self, checkDFilterFail):
+ # Invalid filter
+ dfilter = "ip.version == 4 the quick brown fox jumps over the lazy dog"
+ error = '"the" was unexpected in this context.'
+ checkDFilterFail(dfilter, error)
+
+ def test_eq_6(self, checkDFilterCount):
+ dfilter = "udp.srcport == 123"
+ checkDFilterCount(dfilter, 1)
+
+ def test_eq_7(self, checkDFilterCount):
+ dfilter = "udp.srcport == 0173"
+ checkDFilterCount(dfilter, 1)
+
+ def test_eq_8(self, checkDFilterCount):
+ dfilter = "udp.srcport == 0x7B"
+ checkDFilterCount(dfilter, 1)
+
+ def test_eq_9(self, checkDFilterCount):
+ dfilter = "udp.srcport == 0b1111011"
+ checkDFilterCount(dfilter, 1)
+
+ def test_ne_1(self, checkDFilterCount):
+ dfilter = "ip.version != 0"
+ checkDFilterCount(dfilter, 1)
+
+ def test_ne_2(self, checkDFilterCount):
+ dfilter = "ip.version != 4"
+ checkDFilterCount(dfilter, 0)
+
+ def test_u_gt_1(self, checkDFilterCount):
+ dfilter = "ip.version > 3"
+ checkDFilterCount(dfilter, 1)
+
+ def test_u_gt_2(self, checkDFilterCount):
+ dfilter = "ip.version > 4"
+ checkDFilterCount(dfilter, 0)
+
+ def test_u_gt_3(self, checkDFilterCount):
+ dfilter = "ip.version > 5"
+ checkDFilterCount(dfilter, 0)
+
+ def test_u_ge_1(self, checkDFilterCount):
+ dfilter = "ip.version >= 3"
+ checkDFilterCount(dfilter, 1)
+
+ def test_u_ge_2(self, checkDFilterCount):
+ dfilter = "ip.version >= 4"
+ checkDFilterCount(dfilter, 1)
+
+ def test_u_ge_3(self, checkDFilterCount):
+ dfilter = "ip.version >= 5"
+ checkDFilterCount(dfilter, 0)
+
+ def test_u_lt_1(self, checkDFilterCount):
+ dfilter = "ip.version < 3"
+ checkDFilterCount(dfilter, 0)
+
+ def test_u_lt_2(self, checkDFilterCount):
+ dfilter = "ip.version < 4"
+ checkDFilterCount(dfilter, 0)
+
+ def test_u_lt_3(self, checkDFilterCount):
+ dfilter = "ip.version < 5"
+ checkDFilterCount(dfilter, 1)
+
+ def test_u_le_1(self, checkDFilterCount):
+ dfilter = "ip.version <= 3"
+ checkDFilterCount(dfilter, 0)
+
+ def test_u_le_2(self, checkDFilterCount):
+ dfilter = "ip.version <= 4"
+ checkDFilterCount(dfilter, 1)
+
+ def test_u_le_3(self, checkDFilterCount):
+ dfilter = "ip.version <= 5"
+ checkDFilterCount(dfilter, 1)
+
+ def test_s_gt_1(self, checkDFilterCount):
+ dfilter = "ntp.precision > -12"
+ checkDFilterCount(dfilter, 1)
+
+ def test_s_gt_2(self, checkDFilterCount):
+ dfilter = "ntp.precision > -11"
+ checkDFilterCount(dfilter, 0)
+
+ def test_s_gt_3(self, checkDFilterCount):
+ dfilter = "ntp.precision > -10"
+ checkDFilterCount(dfilter, 0)
+
+ def test_s_ge_1(self, checkDFilterCount):
+ dfilter = "ntp.precision >= -12"
+ checkDFilterCount(dfilter, 1)
+
+ def test_s_ge_2(self, checkDFilterCount):
+ dfilter = "ntp.precision >= -11"
+ checkDFilterCount(dfilter, 1)
+
+ def test_s_ge_3(self, checkDFilterCount):
+ dfilter = "ntp.precision >= -10"
+ checkDFilterCount(dfilter, 0)
+
+ def test_s_lt_1(self, checkDFilterCount):
+ dfilter = "ntp.precision < -12"
+ checkDFilterCount(dfilter, 0)
+
+ def test_s_lt_2(self, checkDFilterCount):
+ dfilter = "ntp.precision < -11"
+ checkDFilterCount(dfilter, 0)
+
+ def test_s_lt_3(self, checkDFilterCount):
+ dfilter = "ntp.precision < -10"
+ checkDFilterCount(dfilter, 1)
+
+ def test_s_le_1(self, checkDFilterCount):
+ dfilter = "ntp.precision <= -12"
+ checkDFilterCount(dfilter, 0)
+
+ def test_s_le_2(self, checkDFilterCount):
+ dfilter = "ntp.precision <= -11"
+ checkDFilterCount(dfilter, 1)
+
+ def test_s_le_3(self, checkDFilterCount):
+ dfilter = "ntp.precision <= -10"
+ checkDFilterCount(dfilter, 1)
+
+ def test_bool_eq_1(self, checkDFilterCount):
+ dfilter = "ip.flags.df == 0"
+ checkDFilterCount(dfilter, 1)
+
+ def test_bool_eq_2(self, checkDFilterCount):
+ dfilter = "ip.flags.df == 1"
+ checkDFilterCount(dfilter, 0)
+
+ def test_bool_ne_1(self, checkDFilterCount):
+ dfilter = "ip.flags.df != 1"
+ checkDFilterCount(dfilter, 1)
+
+ def test_bool_ne_2(self, checkDFilterCount):
+ dfilter = "ip.flags.df != 0"
+ checkDFilterCount(dfilter, 0)
+
+class TestDfilterInteger1Byte:
+
+ trace_file = "ipx_rip.pcap"
+
+ def test_ipx_1(self, checkDFilterCount):
+ dfilter = "ipx.src.net == 0x28"
+ checkDFilterCount(dfilter, 1)
+
+ def test_ipx_2(self, checkDFilterCount):
+ dfilter = "ipx.src.net == 0x29"
+ checkDFilterCount(dfilter, 0)
+
+class TestDfilterUint64:
+ trace_file = "nfs.pcap"
+
+ def test_uint64_1(self, checkDFilterCount):
+ dfilter = "nfs.fattr3.size == 264032"
+ checkDFilterCount(dfilter, 1)
+
+ def test_uint64_2(self, checkDFilterCount):
+ dfilter = "nfs.fattr3.size == 264000"
+ checkDFilterCount(dfilter, 0)
diff --git a/test/suite_dfilter/group_ipv4.py b/test/suite_dfilter/group_ipv4.py
new file mode 100644
index 0000000..4ae9346
--- /dev/null
+++ b/test/suite_dfilter/group_ipv4.py
@@ -0,0 +1,130 @@
+# Copyright (c) 2013 by Gilbert Ramirez <gram@alumni.rice.edu>
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+import pytest
+from suite_dfilter.dfiltertest import *
+
+
+class TestDfilterIpv4:
+ trace_file = "nfs.pcap"
+
+ def test_eq_1(self, checkDFilterCount):
+ dfilter = "ip.src == 172.25.100.14"
+ checkDFilterCount(dfilter, 1)
+
+ def test_eq_2(self, checkDFilterCount):
+ dfilter = "ip.src == 255.255.255.255"
+ checkDFilterCount(dfilter, 0)
+
+ def test_ne_1(self, checkDFilterCount):
+ dfilter = "ip.src != 172.25.100.14"
+ checkDFilterCount(dfilter, 1)
+
+ def test_ne_2(self, checkDFilterCount):
+ dfilter = "ip.src != 255.255.255.255"
+ checkDFilterCount(dfilter, 2)
+
+ def test_gt_1(self, checkDFilterCount):
+ dfilter = "ip.dst > 198.95.230.200"
+ checkDFilterCount(dfilter, 0)
+
+ def test_gt_2(self, checkDFilterCount):
+ dfilter = "ip.dst > 198.95.230.20"
+ checkDFilterCount(dfilter, 0)
+
+ def test_gt_3(self, checkDFilterCount):
+ dfilter = "ip.dst > 198.95.230.10"
+ checkDFilterCount(dfilter, 1)
+
+ def test_ge_1(self, checkDFilterCount):
+ dfilter = "ip.dst >= 198.95.230.200"
+ checkDFilterCount(dfilter, 0)
+
+ def test_ge_2(self, checkDFilterCount):
+ dfilter = "ip.dst >= 198.95.230.20"
+ checkDFilterCount(dfilter, 1)
+
+ def test_ge_3(self, checkDFilterCount):
+ dfilter = "ip.dst >= 198.95.230.10"
+ checkDFilterCount(dfilter, 1)
+
+ def test_lt_1(self, checkDFilterCount):
+ dfilter = "ip.src < 172.25.100.140"
+ checkDFilterCount(dfilter, 1)
+
+ def test_lt_2(self, checkDFilterCount):
+ dfilter = "ip.src < 172.25.100.14"
+ checkDFilterCount(dfilter, 0)
+
+ def test_lt_3(self, checkDFilterCount):
+ dfilter = "ip.src < 172.25.100.10"
+ checkDFilterCount(dfilter, 0)
+
+ def test_le_1(self, checkDFilterCount):
+ dfilter = "ip.src <= 172.25.100.140"
+ checkDFilterCount(dfilter, 1)
+
+ def test_le_2(self, checkDFilterCount):
+ dfilter = "ip.src <= 172.25.100.14"
+ checkDFilterCount(dfilter, 1)
+
+ def test_le_3(self, checkDFilterCount):
+ dfilter = "ip.src <= 172.25.100.10"
+ checkDFilterCount(dfilter, 0)
+
+ def test_cidr_eq_1(self, checkDFilterCount):
+ dfilter = "ip.src == 172.25.100.14/32"
+ checkDFilterCount(dfilter, 1)
+
+ def test_cidr_eq_2(self, checkDFilterCount):
+ dfilter = "ip.src == 172.25.100.0/24"
+ checkDFilterCount(dfilter, 1)
+
+ def test_cidr_eq_3(self, checkDFilterCount):
+ dfilter = "ip.src == 172.25.0.0/16"
+ checkDFilterCount(dfilter, 1)
+
+ def test_cidr_eq_4(self, checkDFilterCount):
+ dfilter = "ip.src == 172.0.0.0/8"
+ checkDFilterCount(dfilter, 1)
+
+ def test_cidr_ne_1(self, checkDFilterCount):
+ dfilter = "ip.src != 172.25.100.14/32"
+ checkDFilterCount(dfilter, 1)
+
+ def test_cidr_ne_2(self, checkDFilterCount):
+ dfilter = "ip.src != 172.25.100.0/24"
+ checkDFilterCount(dfilter, 1)
+
+ def test_cidr_ne_3(self, checkDFilterCount):
+ dfilter = "ip.src != 172.25.0.0/16"
+ checkDFilterCount(dfilter, 1)
+
+ def test_cidr_ne_4(self, checkDFilterCount):
+ dfilter = "ip.src != 200.0.0.0/8"
+ checkDFilterCount(dfilter, 2)
+
+ def test_slice_1(self, checkDFilterCount):
+ dfilter = "ip.src[0:2] == ac:19"
+ checkDFilterCount(dfilter, 1)
+
+ def test_slice_2(self, checkDFilterCount):
+ dfilter = "ip.src[0:2] == 00:00"
+ checkDFilterCount(dfilter, 0)
+
+ def test_slice_3(self, checkDFilterCount):
+ dfilter = "ip.src[2:2] == 64:0e"
+ checkDFilterCount(dfilter, 1)
+
+ def test_slice_4(self, checkDFilterCount):
+ dfilter = "ip.src[2:2] == ff:ff"
+ checkDFilterCount(dfilter, 0)
+
+ def test_count_1(self, checkDFilterCount):
+ dfilter = "count(ip.src) == 1"
+ checkDFilterCount(dfilter, 2)
+
+ def test_count_2(self, checkDFilterCount):
+ dfilter = "count(ip.addr) == 2"
+ checkDFilterCount(dfilter, 2)
diff --git a/test/suite_dfilter/group_ipv6.py b/test/suite_dfilter/group_ipv6.py
new file mode 100644
index 0000000..a60433d
--- /dev/null
+++ b/test/suite_dfilter/group_ipv6.py
@@ -0,0 +1,173 @@
+# Copyright (c) 2013 by Gilbert Ramirez <gram@alumni.rice.edu>
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+import pytest
+from suite_dfilter.dfiltertest import *
+
+
+class TestDfilterIpv6:
+ trace_file = "ipv6.pcap"
+
+ def test_eq_1(self, checkDFilterCount):
+ dfilter = "ipv6.dst == ff05::9999"
+ checkDFilterCount(dfilter, 1)
+
+ def test_eq_2(self, checkDFilterCount):
+ dfilter = "ipv6.dst == ff05::9990"
+ checkDFilterCount(dfilter, 0)
+
+ def test_ne_1(self, checkDFilterCount):
+ dfilter = "ipv6.dst != ff05::9990"
+ checkDFilterCount(dfilter, 1)
+
+ def test_ne_2(self, checkDFilterCount):
+ dfilter = "ipv6.dst != ff05::9999"
+ checkDFilterCount(dfilter, 0)
+
+ def test_gt_1(self, checkDFilterCount):
+ dfilter = "ipv6.dst > ff05::0000"
+ checkDFilterCount(dfilter, 1)
+
+ def test_gt_2(self, checkDFilterCount):
+ dfilter = "ipv6.dst > ff05::9999"
+ checkDFilterCount(dfilter, 0)
+
+ def test_ge_1(self, checkDFilterCount):
+ dfilter = "ipv6.dst >= ff05::9999"
+ checkDFilterCount(dfilter, 1)
+
+ def test_ge_2(self, checkDFilterCount):
+ dfilter = "ipv6.dst >= ff05::a000"
+ checkDFilterCount(dfilter, 0)
+
+ def test_lt_1(self, checkDFilterCount):
+ dfilter = "ipv6.dst < ff05::a000"
+ checkDFilterCount(dfilter, 1)
+
+ def test_lt_2(self, checkDFilterCount):
+ dfilter = "ipv6.dst < ff05::9999"
+ checkDFilterCount(dfilter, 0)
+
+ def test_le_1(self, checkDFilterCount):
+ dfilter = "ipv6.dst <= ff05::9999"
+ checkDFilterCount(dfilter, 1)
+
+ def test_le_2(self, checkDFilterCount):
+ dfilter = "ipv6.dst <= ff05::9998"
+ checkDFilterCount(dfilter, 0)
+
+ def test_cidr_eq_1(self, checkDFilterCount):
+ dfilter = "ipv6.dst == ff05::9999/128"
+ checkDFilterCount(dfilter, 1)
+
+ def test_cidr_eq_2(self, checkDFilterCount):
+ dfilter = "ipv6.dst == ff05::0/64"
+ checkDFilterCount(dfilter, 1)
+
+ def test_cidr_eq_3(self, checkDFilterCount):
+ dfilter = "ipv6.dst == ff05::ffff/112"
+ checkDFilterCount(dfilter, 1)
+
+ def test_cidr_eq_4(self, checkDFilterCount):
+ dfilter = "ipv6.dst == ff04::0/64"
+ checkDFilterCount(dfilter, 0)
+
+ def test_cidr_ne_1(self, checkDFilterCount):
+ dfilter = "ipv6.dst != ff05::9999/128"
+ checkDFilterCount(dfilter, 0)
+
+ def test_cidr_ne_2(self, checkDFilterCount):
+ dfilter = "ipv6.dst != ff05::0/64"
+ checkDFilterCount(dfilter, 0)
+
+ def test_cidr_ne_3(self, checkDFilterCount):
+ dfilter = "ipv6.dst != ff05::ffff/112"
+ checkDFilterCount(dfilter, 0)
+
+ def test_cidr_ne_4(self, checkDFilterCount):
+ dfilter = "ipv6.dst != ff04::00/64"
+ checkDFilterCount(dfilter, 1)
+
+ def test_slice_1(self, checkDFilterCount):
+ dfilter = "ipv6.dst[14:2] == 99:99"
+ checkDFilterCount(dfilter, 1)
+
+ def test_slice_2(self, checkDFilterCount):
+ dfilter = "ipv6.dst[14:2] == 00:00"
+ checkDFilterCount(dfilter, 0)
+
+ def test_slice_3(self, checkDFilterCount):
+ dfilter = "ipv6.dst[15:1] == 99"
+ checkDFilterCount(dfilter, 1)
+
+ def test_slice_4(self, checkDFilterCount):
+ dfilter = "ipv6.dst[15:1] == 00"
+
+ #
+ # Test some addresses are parsed correctly
+ #
+
+ def test_unspecified_1(self, checkDFilterSucceed):
+ dfilter = "ipv6.dst == ::"
+ checkDFilterSucceed(dfilter)
+
+ def test_unspecified_2(self, checkDFilterSucceed):
+ dfilter = "ipv6.dst == ::/128"
+ checkDFilterSucceed(dfilter)
+
+ def test_loopback_1(self, checkDFilterSucceed):
+ dfilter = "ipv6.dst == ::1"
+ checkDFilterSucceed(dfilter)
+
+ def test_loopback_2(self, checkDFilterSucceed):
+ dfilter = "ipv6.dst == ::1/128"
+ checkDFilterSucceed(dfilter)
+
+ def test_compress_1(self, checkDFilterSucceed):
+ dfilter = "ipv6.dst == ::2000"
+ checkDFilterSucceed(dfilter)
+
+ def test_compress_2(self, checkDFilterSucceed):
+ dfilter = "ipv6.dst == ::2000/64"
+ checkDFilterSucceed(dfilter)
+
+ def test_compress_3(self, checkDFilterSucceed):
+ dfilter = "ipv6.dst == ::1:2000"
+ checkDFilterSucceed(dfilter)
+
+ def test_compress_4(self, checkDFilterSucceed):
+ dfilter = "ipv6.dst == 2000::"
+ checkDFilterSucceed(dfilter)
+
+ def test_compress_5(self, checkDFilterSucceed):
+ dfilter = "ipv6.dst == 2000::/120"
+ checkDFilterSucceed(dfilter)
+
+ def test_compress_6(self, checkDFilterSucceed):
+ dfilter = "ipv6.dst == 2000:1::"
+ checkDFilterSucceed(dfilter)
+
+ def test_ula_1(self, checkDFilterSucceed):
+ dfilter = "ipv6.dst == fd93:c15b:7ae0:2e41:0000:0000:0000:0000"
+ checkDFilterSucceed(dfilter)
+
+ def test_ula_2(self, checkDFilterSucceed):
+ dfilter = "ipv6.dst == fd93:c15b:7ae0:2e41:ffff:ffff:ffff:ffff"
+ checkDFilterSucceed(dfilter)
+
+ def test_ula_3(self, checkDFilterSucceed):
+ dfilter = "ipv6.dst == fd93:c15b:7ae0:2e41:3f32:35c9:40aa:1243"
+ checkDFilterSucceed(dfilter)
+
+ def test_ula_4(self, checkDFilterSucceed):
+ dfilter = "ipv6.dst == fd93:c15b:7ae0:2e41::2:1"
+ checkDFilterSucceed(dfilter)
+
+ def test_mapped_ipv4_1(self, checkDFilterSucceed):
+ dfilter = "ipv6.dst == ::13.1.68.3"
+ checkDFilterSucceed(dfilter)
+
+ def test_mapped_ipv4_2(self, checkDFilterSucceed):
+ dfilter = "ipv6.dst == ::FFFF:129.144.52.38"
+ checkDFilterSucceed(dfilter)
diff --git a/test/suite_dfilter/group_membership.py b/test/suite_dfilter/group_membership.py
new file mode 100644
index 0000000..2b98365
--- /dev/null
+++ b/test/suite_dfilter/group_membership.py
@@ -0,0 +1,112 @@
+# Copyright (c) 2018 Peter Wu <peter@lekensteyn.nl>
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+import pytest
+from suite_dfilter.dfiltertest import *
+
+
+class TestDfilterMembership:
+ trace_file = "http.pcap"
+
+ def test_membership_match_1(self, checkDFilterCount):
+ dfilter = 'tcp.port in {80, 3267}'
+ checkDFilterCount(dfilter, 1)
+
+ def test_membership_match_2(self, checkDFilterCount):
+ dfilter = 'tcp.port in {80,3267}'
+ checkDFilterCount(dfilter, 1)
+
+ def test_membership_match_3(self, checkDFilterCount):
+ dfilter = 'tcp.port in {80 ,3267}'
+ checkDFilterCount(dfilter, 1)
+
+ def test_membership_match_4(self, checkDFilterCount):
+ dfilter = 'tcp.port in {80 , 3267}'
+ checkDFilterCount(dfilter, 1)
+
+ def test_membership_match_5(self, checkDFilterCount):
+ dfilter = 'tcp.port in { 80 , 3267 }'
+ checkDFilterCount(dfilter, 1)
+
+ def test_membership_any_1(self, checkDFilterCount):
+ dfilter = 'any tcp.port in {80, 3267}'
+ checkDFilterCount(dfilter, 1)
+
+ def test_membership_any_2(self, checkDFilterCount):
+ dfilter = 'any tcp.port in {70, 80, 90}'
+ checkDFilterCount(dfilter, 1)
+
+ def test_membership_all_1(self, checkDFilterCount):
+ dfilter = 'all tcp.port in {80, 3267}'
+ checkDFilterCount(dfilter, 1)
+
+ def test_membership_all_2(self, checkDFilterCount):
+ dfilter = 'all tcp.port in {70, 80, 90}'
+ checkDFilterCount(dfilter, 0)
+
+ def test_membership_range_match_1(self, checkDFilterCount):
+ dfilter = 'tcp.port in {80..81}'
+ checkDFilterCount(dfilter, 1)
+
+ def test_membership_range_match_2(self, checkDFilterCount):
+ dfilter = 'tcp.port in {80 ..81}'
+ checkDFilterCount(dfilter, 1)
+
+ def test_membership_range_match_3(self, checkDFilterCount):
+ dfilter = 'tcp.port in {80.. 81}'
+ checkDFilterCount(dfilter, 1)
+
+ def test_membership_range_match_4(self, checkDFilterCount):
+ dfilter = 'tcp.port in {80 .. 81}'
+ checkDFilterCount(dfilter, 1)
+
+ def test_membership_3_range_no_match(self, checkDFilterCount):
+ dfilter = 'tcp.dstport in {1 .. 79, 81 .. 65535}'
+ checkDFilterCount(dfilter, 0)
+
+ def test_membership_4_range_no_match_multiple(self, checkDFilterCount):
+ # Verifies that multiple fields cannot satisfy different conditions.
+ dfilter = 'tcp.port in {1 .. 79,81 .. 3266,3268 .. 65535}'
+ checkDFilterCount(dfilter, 0)
+
+ def test_membership_5_negative_range_float(self, checkDFilterCount):
+ dfilter = 'frame.time_delta in {-2.0 .. 0.0}'
+ checkDFilterCount(dfilter, 1)
+
+ def test_membership_6_both_negative_range_float(self, checkDFilterCount):
+ dfilter = 'frame.time_delta in {-20 .. -0.7}'
+ checkDFilterCount(dfilter, 0)
+
+ def test_membership_7_string(self, checkDFilterCount):
+ dfilter = 'http.request.method in {"GET", "HEAD"}'
+ checkDFilterCount(dfilter, 1)
+
+ def test_membership_8_ip_range(self, checkDFilterCount):
+ dfilter = 'ip.addr in { 10.0.0.5 .. 10.0.0.9 , 10.0.0.1..10.0.0.1 }'
+ checkDFilterCount(dfilter, 1)
+
+ def test_membership_9_range_invalid_float(self, checkDFilterFail):
+ # expression should be parsed as "0.1 .. .7"
+ # .7 is the identifier (protocol) named "7"
+ dfilter = 'frame.time_delta in {0.1...7}'
+ error = '"7" is not a valid protocol or protocol field'
+ checkDFilterFail(dfilter, error)
+
+ def test_membership_10_bad_lhs_number(self, checkDFilterFail):
+ dfilter = '123 in {ip}'
+ error = 'Only a field may be tested for membership in a set.'
+ checkDFilterFail(dfilter, error)
+
+ def test_membership_11_bad_rhs_string(self, checkDFilterFail):
+ dfilter = 'frame.number in {1, "foo"}'
+ error = 'Unsigned integer (32 bits) cannot be converted from a string'
+ checkDFilterFail(dfilter, error)
+
+ def test_membership_12_value_string(self, checkDFilterCount):
+ dfilter = 'tcp.checksum.status in {"Unverified", "Good"}'
+ checkDFilterCount(dfilter, 1)
+
+ def test_membership_arithmetic_1(self, checkDFilterCountWithSelectedFrame):
+ dfilter = 'frame.time_epoch in {${frame.time_epoch}-46..${frame.time_epoch}+43}'
+ checkDFilterCountWithSelectedFrame(dfilter, 1, 1)
diff --git a/test/suite_dfilter/group_scanner.py b/test/suite_dfilter/group_scanner.py
new file mode 100644
index 0000000..799ef68
--- /dev/null
+++ b/test/suite_dfilter/group_scanner.py
@@ -0,0 +1,34 @@
+# Copyright (c) 2013 by Gilbert Ramirez <gram@alumni.rice.edu>
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+import pytest
+from suite_dfilter.dfiltertest import *
+
+
+class TestDfilterScanner:
+ trace_file = "http.pcap"
+
+ def test_dquote_1(self, checkDFilterCount):
+ dfilter = 'http.request.method == "HEAD"'
+ checkDFilterCount(dfilter, 1)
+
+ def test_dquote_2(self, checkDFilterCount):
+ dfilter = 'http.request.method == "\\x48EAD"'
+ checkDFilterCount(dfilter, 1)
+
+ def test_dquote_3(self, checkDFilterCount):
+ dfilter = 'http.request.method == "\\x58EAD"'
+ checkDFilterCount(dfilter, 0)
+
+ def test_dquote_4(self, checkDFilterCount):
+ dfilter = 'http.request.method == "\\110EAD"'
+ checkDFilterCount(dfilter, 1)
+
+ def test_dquote_5(self, checkDFilterCount):
+ dfilter = 'http.request.method == "\\111EAD"'
+ checkDFilterCount(dfilter, 0)
+
+ def test_dquote_6(self, checkDFilterFail):
+ dfilter = r'http.request.method == "\HEAD"'
+ checkDFilterFail(dfilter, 'not a valid character escape sequence')
diff --git a/test/suite_dfilter/group_slice.py b/test/suite_dfilter/group_slice.py
new file mode 100644
index 0000000..0f490bc
--- /dev/null
+++ b/test/suite_dfilter/group_slice.py
@@ -0,0 +1,94 @@
+# Copyright (c) 2013 by Gilbert Ramirez <gram@alumni.rice.edu>
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+import pytest
+from suite_dfilter.dfiltertest import *
+
+
+class TestDfilterRange:
+ trace_file = "ipx_rip.pcap"
+
+ def test_slice_1_pos(self, checkDFilterCount):
+ dfilter = "ipx.src.node[1] == aa"
+ checkDFilterCount(dfilter, 1)
+
+ def test_slice_2_pos(self, checkDFilterCount):
+ dfilter = "ipx.src.node[1] == bb"
+ checkDFilterCount(dfilter, 0)
+
+ def test_slice_1_neg(self, checkDFilterCount):
+ dfilter = "ipx[-2:] == 04:53"
+ checkDFilterCount(dfilter, 1)
+
+ def test_slice_1_hex_pos(self, checkDFilterCount):
+ dfilter = "ipx.src.node[1] == 0xaa"
+ checkDFilterCount(dfilter, 1)
+
+ def test_slice_1_hex_neg(self, checkDFilterCount):
+ dfilter = "ipx.src.node[1] == 0xbb"
+ checkDFilterCount(dfilter, 0)
+
+ def test_slice_2_pos(self, checkDFilterCount):
+ dfilter = "ipx.src.node[3:2] == a3:e3"
+ checkDFilterCount(dfilter, 1)
+
+ def test_slice_2_neg(self, checkDFilterCount):
+ dfilter = "ipx.src.node[3:2] == cc:dd"
+ checkDFilterCount(dfilter, 0)
+
+ def test_slice_string_1(self, checkDFilterFail):
+ dfilter = "frame == \"00\"[1]"
+ checkDFilterFail(dfilter, "Range is not supported for entity")
+
+ def test_slice_unparsed_1(self, checkDFilterFail):
+ dfilter = "frame == b[1]"
+ checkDFilterFail(dfilter, "Range is not supported for entity")
+
+ def test_slice_func_1(self, checkDFilterSucceed):
+ dfilter = "string(ipx.src.node)[3:2] == \"cc:dd\""
+ checkDFilterSucceed(dfilter)
+
+ # [i:j] i = start_offset, j = length
+ # [i-j] i = start_offset, j = end_offset, inclusive.
+ # [i] i = start_offset, length = 1
+ # [:j] start_offset = 0, length = j
+ # [i:] start_offset = i, end_offset = end_of_field
+
+ def test_slice_range_1(self, checkDFilterSucceed):
+ # :5 is a length
+ dfilter = "frame[5:5] == 11:22:33:44:55"
+ checkDFilterSucceed(dfilter)
+
+ def test_slice_range_2(self, checkDFilterSucceed):
+ # end offset is inclusive
+ dfilter = "frame[5-10] == 11:22:33:44:55:66"
+ checkDFilterSucceed(dfilter)
+
+ def test_slice_range_3(self, checkDFilterSucceed):
+ dfilter = "frame[5] == 11"
+ checkDFilterSucceed(dfilter)
+
+ def test_slice_range_4(self, checkDFilterSucceed):
+ dfilter = "frame[:20] contains be:ef"
+ checkDFilterSucceed(dfilter)
+
+ def test_slice_range_5(self, checkDFilterSucceed):
+ dfilter = "frame[20:] contains :12345678"
+ checkDFilterSucceed(dfilter)
+
+ def test_slice_exists_1(self, checkDFilterCount):
+ dfilter = "frame[59]"
+ checkDFilterCount(dfilter, 1)
+
+ def test_slice_exists_2(self, checkDFilterCount):
+ dfilter = "frame[60]"
+ checkDFilterCount(dfilter, 0)
+
+ def test_slice_exists_3(self, checkDFilterCount):
+ dfilter = "frame[50-59]"
+ checkDFilterCount(dfilter, 1)
+
+ def test_slice_exists_4(self, checkDFilterCount):
+ dfilter = "frame[50-60]"
+ checkDFilterCount(dfilter, 0)
diff --git a/test/suite_dfilter/group_string.py b/test/suite_dfilter/group_string.py
new file mode 100644
index 0000000..8d77b3e
--- /dev/null
+++ b/test/suite_dfilter/group_string.py
@@ -0,0 +1,240 @@
+#
+# Copyright (c) 2013 by Gilbert Ramirez <gram@alumni.rice.edu>
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+import pytest
+from suite_dfilter.dfiltertest import *
+
+
+class TestDfilterString:
+ trace_file = "http.pcap"
+
+ def test_eq_1(self, checkDFilterCount):
+ dfilter = 'http.request.method == "HEAD"'
+ checkDFilterCount(dfilter, 1)
+
+ def test_eq_2(self, checkDFilterCount):
+ dfilter = 'http.request.method == "POST"'
+ checkDFilterCount(dfilter, 0)
+
+ def test_gt_1(self, checkDFilterCount):
+ dfilter = 'http.request.method > "HEAC"'
+ checkDFilterCount(dfilter, 1)
+
+ def test_gt_2(self, checkDFilterCount):
+ dfilter = 'http.request.method > "HEAD"'
+ checkDFilterCount(dfilter, 0)
+
+ def test_gt_3(self, checkDFilterCount):
+ dfilter = 'http.request.method > "HEAE"'
+ checkDFilterCount(dfilter, 0)
+
+ def test_ge_1(self, checkDFilterCount):
+ dfilter = 'http.request.method >= "HEAC"'
+ checkDFilterCount(dfilter, 1)
+
+ def test_ge_2(self, checkDFilterCount):
+ dfilter = 'http.request.method >= "HEAD"'
+ checkDFilterCount(dfilter, 1)
+
+ def test_ge_3(self, checkDFilterCount):
+ dfilter = 'http.request.method >= "HEAE"'
+ checkDFilterCount(dfilter, 0)
+
+ def test_lt_1(self, checkDFilterCount):
+ dfilter = 'http.request.method < "HEAC"'
+ checkDFilterCount(dfilter, 0)
+
+ def test_lt_2(self, checkDFilterCount):
+ dfilter = 'http.request.method < "HEAD"'
+ checkDFilterCount(dfilter, 0)
+
+ def test_lt_3(self, checkDFilterCount):
+ dfilter = 'http.request.method < "HEAE"'
+ checkDFilterCount(dfilter, 1)
+
+ def test_le_1(self, checkDFilterCount):
+ dfilter = 'http.request.method <= "HEAC"'
+ checkDFilterCount(dfilter, 0)
+
+ def test_le_2(self, checkDFilterCount):
+ dfilter = 'http.request.method <= "HEAD"'
+ checkDFilterCount(dfilter, 1)
+
+ def test_le_3(self, checkDFilterCount):
+ dfilter = 'http.request.method <= "HEAE"'
+ checkDFilterCount(dfilter, 1)
+
+ def test_slice_1(self, checkDFilterCount):
+ dfilter = 'http.request.method[0] == "H"'
+ checkDFilterCount(dfilter, 1)
+
+ def test_slice_2(self, checkDFilterCount):
+ dfilter = 'http.request.method[0] == "P"'
+ checkDFilterCount(dfilter, 0)
+
+ def test_slice_3(self, checkDFilterCount):
+ dfilter = 'http.request.method[0:4] == "HEAD"'
+ checkDFilterCount(dfilter, 1)
+
+ def test_slice_4(self, checkDFilterCount):
+ dfilter = 'http.request.method[0:4] != "HEAD"'
+ checkDFilterCount(dfilter, 0)
+
+ def test_slice_5(self, checkDFilterCount):
+ dfilter = 'http.request.method[1:2] == "EA"'
+ checkDFilterCount(dfilter, 1)
+
+ def test_slice_6(self, checkDFilterCount):
+ dfilter = 'http.request.method[1:2] > "EA"'
+ checkDFilterCount(dfilter, 0)
+
+ def test_slice_7(self, checkDFilterCount):
+ dfilter = 'http.request.method[-1] == "D"'
+ checkDFilterCount(dfilter, 1)
+
+ def test_slice_8(self, checkDFilterCount):
+ dfilter = 'http.request.method[-2] == "D"'
+ checkDFilterCount(dfilter, 0)
+
+ def xxxtest_stringz_1(self):
+ return self.DFilterCount(pkt_tftp,
+ 'tftp.type == "octet"', 1)
+
+ def xxxtest_stringz_2(self):
+ return self.DFilterCount(pkt_tftp,
+ 'tftp.type == "junk"', 0)
+
+ def test_contains_1(self, checkDFilterCount):
+ dfilter = 'http.request.method contains "E"'
+ checkDFilterCount(dfilter, 1)
+
+ def test_contains_2(self, checkDFilterCount):
+ dfilter = 'http.request.method contains "EA"'
+ checkDFilterCount(dfilter, 1)
+
+ def test_contains_3(self, checkDFilterCount):
+ dfilter = 'http.request.method contains "HEAD"'
+ checkDFilterCount(dfilter, 1)
+
+ def test_contains_4(self, checkDFilterCount):
+ dfilter = 'http.request.method contains "POST"'
+ checkDFilterCount(dfilter, 0)
+
+ def test_contains_5(self, checkDFilterCount):
+ dfilter = 'http.request.method contains "\x50\x4f\x53\x54"' # "POST"
+ checkDFilterCount(dfilter, 0)
+
+ def test_contains_6(self, checkDFilterCount):
+ dfilter = 'http.request.method contains "\x48\x45\x41\x44"' # "HEAD"
+ checkDFilterCount(dfilter, 1)
+
+ def test_contains_7(self, checkDFilterCount):
+ dfilter = 'http.request.method contains 48:45:41:44' # "48:45:41:44"
+ checkDFilterCount(dfilter, 0)
+
+ def test_contains_fail_0(self, checkDFilterCount):
+ dfilter = 'http.user_agent contains "update"'
+ checkDFilterCount(dfilter, 0)
+
+ def test_contains_fail_1(self, checkDFilterCount):
+ dfilter = 'http.user_agent contains "UPDATE"'
+ checkDFilterCount(dfilter, 0)
+
+ def test_contains_upper_0(self, checkDFilterCount):
+ dfilter = 'upper(http.user_agent) contains "UPDATE"'
+ checkDFilterCount(dfilter, 1)
+
+ def test_contains_upper_1(self, checkDFilterCount):
+ dfilter = 'upper(http.user_agent) contains "update"'
+ checkDFilterCount(dfilter, 0)
+
+ def test_contains_upper_2(self, checkDFilterFail):
+ dfilter = 'upper(tcp.seq) == 4'
+ checkDFilterFail(dfilter, 'Only string type fields can be used')
+
+ def test_contains_lower_0(self, checkDFilterCount):
+ dfilter = 'lower(http.user_agent) contains "UPDATE"'
+ checkDFilterCount(dfilter, 0)
+
+ def test_contains_lower_1(self, checkDFilterCount):
+ dfilter = 'lower(http.user_agent) contains "update"'
+ checkDFilterCount(dfilter, 1)
+
+ def test_eq_lower_1(self, checkDFilterFail):
+ dfilter = 'lower(tcp.seq) == 4'
+ checkDFilterFail(dfilter, 'Only string type fields can be used')
+
+ def test_string_len(self, checkDFilterCount):
+ dfilter = 'len(http.request.method) == 4'
+ checkDFilterCount(dfilter, 1)
+
+ def test_eq_unicode(self, checkDFilterCount):
+ dfilter = 'tcp.flags.str == "·······AP···"'
+ checkDFilterCount(dfilter, 1)
+
+ def test_contains_unicode(self, checkDFilterCount):
+ dfilter = 'tcp.flags.str contains "·······AP···"'
+ checkDFilterCount(dfilter, 1)
+
+ def test_value_string_1(self, checkDFilterCount):
+ dfilter = 'tcp.checksum.status == "Unverified" || tcp.checksum.status == "Good"'
+ checkDFilterCount(dfilter, 1)
+
+class TestDfilterStringz:
+ trace_file = "tftp.pcap"
+
+ def test_stringz_1(self, checkDFilterCount):
+ dfilter = 'tftp.type == octet'
+ checkDFilterCount(dfilter, 1)
+
+ def test_stringz_2(self, checkDFilterCount):
+ dfilter = 'tftp.type == "octet"'
+ checkDFilterCount(dfilter, 1)
+
+ def test_stringz_3(self, checkDFilterCount):
+ dfilter = 'tftp.type == junk'
+ checkDFilterCount(dfilter, 0)
+
+class TestDfilterStringIndex:
+ trace_file = "data-utf8.pcap"
+
+ def test_index_1(self, checkDFilterCount):
+ dfilter = 'data.text[3] == "á"'
+ prefs = "data.show_as_text:true"
+ checkDFilterCount(dfilter, 1, prefs)
+
+ def test_index_2(self, checkDFilterCount):
+ dfilter = 'data.text[3] == "a"'
+ prefs = "data.show_as_text:true"
+ checkDFilterCount(dfilter, 0, prefs)
+
+ def test_index_3(self, checkDFilterCount):
+ dfilter = 'data.text[40:] == "cão preguiçoso"'
+ prefs = "data.show_as_text:true"
+ checkDFilterCount(dfilter, 1, prefs)
+
+ def test_index_4(self, checkDFilterCount):
+ # Byte offset
+ dfilter = '@data.text[41:] == "cão preguiçoso"'
+ prefs = "data.show_as_text:true"
+ checkDFilterCount(dfilter, 1, prefs)
+
+ def test_index_5(self, checkDFilterCount):
+ # Byte offset
+ dfilter = '@data.text[41:] == 63:c3:a3:6f:20:70:72:65:67:75:69:c3:a7:6f:73:6f'
+ prefs = "data.show_as_text:true"
+ checkDFilterCount(dfilter, 1, prefs)
+
+ def test_strlen_1(self, checkDFilterCount):
+ dfilter = 'len(data.text) == 54'
+ prefs = "data.show_as_text:true"
+ checkDFilterCount(dfilter, 1, prefs)
+
+ def test_strlen_2(self, checkDFilterCount):
+ # Byte length
+ dfilter = 'len(@data.text) == 57'
+ prefs = "data.show_as_text:true"
+ checkDFilterCount(dfilter, 1, prefs)
+
diff --git a/test/suite_dfilter/group_syntax.py b/test/suite_dfilter/group_syntax.py
new file mode 100644
index 0000000..580d812
--- /dev/null
+++ b/test/suite_dfilter/group_syntax.py
@@ -0,0 +1,487 @@
+# Copyright (c) 2013 by Gilbert Ramirez <gram@alumni.rice.edu>
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+import pytest
+from suite_dfilter.dfiltertest import *
+
+
+class TestDfilterSyntax:
+ trace_file = "http.pcap"
+
+ def test_exists_1(self, checkDFilterCount):
+ dfilter = "frame"
+ checkDFilterCount(dfilter, 1)
+
+ def test_exists_2(self, checkDFilterCount):
+ # Protocol using minus
+ dfilter = "mac-lte"
+ checkDFilterCount(dfilter, 0)
+
+ def test_exists_3(self, checkDFilterCount):
+ # Protocol starting with digit
+ dfilter = "9p or http"
+ checkDFilterCount(dfilter, 1)
+
+ def test_exists_4(self, checkDFilterCount):
+ # Protocol with dot
+ dfilter = "_ws.expert"
+ checkDFilterCount(dfilter, 1)
+
+ def test_exists_5(self, checkDFilterSucceed):
+ # Protocol field name with leading digit and minus
+ dfilter = "diameter.3GPP-Reporting-Reason"
+ checkDFilterSucceed(dfilter)
+
+ def test_commute_1(self, checkDFilterCount):
+ dfilter = "ip.proto == 6"
+ checkDFilterCount(dfilter, 1)
+
+ def test_commute_2(self, checkDFilterCount):
+ dfilter = "6 == ip.proto"
+ checkDFilterCount(dfilter, 1)
+
+ def test_commute_3(self, checkDFilterFail):
+ dfilter = "6 == 7"
+ error = "Constant expression is invalid"
+ checkDFilterFail(dfilter, error)
+
+ def test_func_1(self, checkDFilterCount):
+ dfilter = "len(frame) == 207"
+ checkDFilterCount(dfilter, 1)
+
+ def test_value_string_1(self, checkDFilterSucceed):
+ dfilter = 'eth.fcs.status=="Bad"'
+ checkDFilterSucceed(dfilter)
+
+ def test_matches_1(self, checkDFilterSucceed):
+ dfilter = 'http.request.method matches "^HEAD"'
+ checkDFilterSucceed(dfilter)
+
+ def test_matches_2(self, checkDFilterFail):
+ dfilter = 'http.request.method matches HEAD'
+ checkDFilterFail(dfilter, 'requires a double quoted string')
+
+ def test_matches_3(self, checkDFilterFail):
+ dfilter = 'http.request.method matches "^HEAD" matches "^POST"'
+ checkDFilterFail(dfilter, '"matches" was unexpected in this context.')
+
+ def test_matches_4(self, checkDFilterCount):
+ dfilter = r'http.host matches r"update\.microsoft\.c.."'
+ checkDFilterCount(dfilter, 1)
+
+ def test_matches_5(self, checkDFilterSucceed):
+ # case insensitive
+ dfilter = 'http.request.method matches "^head"'
+ checkDFilterSucceed(dfilter)
+
+ def test_equal_1(self, checkDFilterCount):
+ dfilter = 'ip.addr == 10.0.0.5'
+ checkDFilterCount(dfilter, 1)
+
+ def test_equal_2(self, checkDFilterCount):
+ dfilter = 'ip.addr == 207.46.134.94'
+ checkDFilterCount(dfilter, 1)
+
+ def test_equal_3(self, checkDFilterCount):
+ dfilter = 'ip.addr == 10.0.0.5 or ip.addr == 207.46.134.94'
+ checkDFilterCount(dfilter, 1)
+
+ def test_equal_4(self, checkDFilterCount):
+ dfilter = 'ip.addr == 10.0.0.5 and ip.addr == 207.46.134.94'
+ checkDFilterCount(dfilter, 1)
+
+ def test_not_equal_1(self, checkDFilterCount):
+ dfilter = 'ip.addr != 10.0.0.5'
+ checkDFilterCount(dfilter, 0)
+
+ def test_not_equal_2(self, checkDFilterCount):
+ dfilter = 'ip.addr != 207.46.134.94'
+ checkDFilterCount(dfilter, 0)
+
+ def test_not_equal_3(self, checkDFilterCount):
+ dfilter = 'ip.addr != 10.0.0.5 and ip.addr != 207.46.134.94'
+ checkDFilterCount(dfilter, 0)
+
+ def test_not_equal_4(self, checkDFilterCount):
+ dfilter = 'ip.addr != 10.0.0.5 or ip.addr != 207.46.134.94'
+ checkDFilterCount(dfilter, 0)
+
+ def test_deprecated_1(self, checkDFilterSucceed):
+ dfilter = "bootp"
+ checkDFilterSucceed(dfilter, "Deprecated token \"bootp\"")
+
+ def test_charconst_bytes_1(self, checkDFilterCount):
+ # Bytes as a character constant.
+ dfilter = "frame contains 'H'"
+ checkDFilterCount(dfilter, 1)
+
+ def test_charconst_bytes_2(self, checkDFilterCount):
+ dfilter = "frame[54] == 'H'"
+ checkDFilterCount(dfilter, 1)
+
+ def test_charconst_invalid(self, checkDFilterFail):
+ dfilter = r"ip.proto == '\Z'"
+ checkDFilterFail(dfilter, "isn't a valid character constant")
+
+ def test_bool_1(self, checkDFilterCount):
+ dfilter = "tcp.flags.push == 1"
+ checkDFilterCount(dfilter, 1)
+
+ def test_bool_2(self, checkDFilterCount):
+ dfilter = "tcp.flags.push == True"
+ checkDFilterCount(dfilter, 1)
+
+ def test_bool_2(self, checkDFilterCount):
+ dfilter = "tcp.flags.push == FALSE"
+ checkDFilterCount(dfilter, 0)
+
+ def test_misc_1(self, checkDFilterSucceed):
+ # Issue #18418
+ dfilter = "icmp and ((icmp.type > 0 and icmp.type < 8) or icmp.type > 8)"
+ checkDFilterSucceed(dfilter)
+
+ def test_whitespace(self, checkDFilterSucceed):
+ dfilter = '\ttcp.stream \r\n== 1'
+ checkDFilterSucceed(dfilter)
+
+ def test_func_name_clash1(self, checkDFilterFail):
+ # "tcp" is a (non-existent) function, not a protocol
+ error = "Function 'tcp' does not exist"
+ dfilter = 'frame == tcp()'
+ checkDFilterFail(dfilter, error)
+
+class TestDfilterEquality:
+ trace_file = "sip.pcapng"
+
+ def test_all_eq_1(self, checkDFilterCount):
+ dfilter = "udp.port === 5060"
+ checkDFilterCount(dfilter, 2)
+
+ def test_any_ne_1(self, checkDFilterCount):
+ dfilter = "udp.port !== 5060"
+ checkDFilterCount(dfilter, 4)
+
+ def test_any_eq_1(self, checkDFilterCount):
+ dfilter = "udp.port == 5060"
+ checkDFilterCount(dfilter, 5)
+
+ def test_all_ne_1(self, checkDFilterCount):
+ dfilter = "udp.port != 5060"
+ checkDFilterCount(dfilter, 1)
+
+ def test_root_1(self, checkDFilterCount):
+ dfilter = "udp.srcport == .udp.dstport"
+ checkDFilterCount(dfilter, 2)
+
+ def test_literal_3(self, checkDFilterCount):
+ dfilter = "frame[0:10] contains :00:01:6c"
+ checkDFilterCount(dfilter, 1)
+
+ def test_literal_4(self, checkDFilterCount):
+ dfilter = "frame[0:10] contains :00016c"
+ checkDFilterCount(dfilter, 1)
+
+ def test_literal_5(self, checkDFilterCount):
+ dfilter = "frame[0:10] contains :00.01.6c"
+ checkDFilterCount(dfilter, 1)
+
+ def test_literal_6(self, checkDFilterCount):
+ dfilter = "frame[0:10] contains :00-01-6c"
+ checkDFilterCount(dfilter, 1)
+
+ def test_rhs_bias_1(self, checkDFilterCount):
+ # Protocol "Fibre Channel" on the RHS
+ dfilter = 'frame[37] == fc'
+ checkDFilterCount(dfilter, 0)
+
+ def test_rhs_bias_2(self, checkDFilterCount):
+ # Byte 0xFC on the RHS
+ dfilter = 'frame[37] == :fc'
+ checkDFilterCount(dfilter, 1)
+
+ def test_rhs_bias_3(self, checkDFilterCount):
+ # Byte 0xFC on the RHS
+ dfilter = 'frame[37] == fc:'
+ checkDFilterCount(dfilter, 1)
+
+ def test_rhs_bias_4(self, checkDFilterCount):
+ # Protocol "Fibre Channel" on the RHS
+ dfilter = 'frame[37] == .fc'
+ checkDFilterCount(dfilter, 0)
+
+ def test_rhs_bias_5(self, checkDFilterSucceed):
+ # Protocol "Fibre Channel" on the RHS (with warning)
+ dfilter = 'frame contains fc'
+ checkDFilterSucceed(dfilter, 'Interpreting "fc" as Fibre Channel')
+
+ def test_rhs_bias_6(self, checkDFilterSucceed):
+ # Protocol "Fibre Channel" on the RHS (without warning)
+ dfilter = 'frame contains .fc'
+ checkDFilterSucceed(dfilter)
+
+ def test_rhs_bias_7(self, checkDFilterSucceed):
+ # Byte 0xFC on the RHS
+ dfilter = 'frame contains fc:'
+ checkDFilterSucceed(dfilter)
+
+class TestDfilterBitwise:
+ trace_file = "http.pcap"
+
+ def test_exists_1(self, checkDFilterCount):
+ dfilter = "tcp.flags & 0x8"
+ checkDFilterCount(dfilter, 1)
+
+ def test_exists_2(self, checkDFilterCount):
+ dfilter = "eth[0] & 1"
+ checkDFilterCount(dfilter, 0)
+
+ def test_equal_1(self, checkDFilterCount):
+ dfilter = "tcp.flags & 0x0F == 8"
+ checkDFilterCount(dfilter, 1)
+
+ def test_equal_2(self, checkDFilterCount):
+ dfilter = "tcp.srcport != tcp.dstport & 0x0F"
+ checkDFilterCount(dfilter, 1)
+
+class TestDfilterUnaryMinus:
+ trace_file = "http.pcap"
+
+ def test_minus_const_1(self, checkDFilterCount):
+ dfilter = "tcp.window_size_scalefactor == -1"
+ checkDFilterCount(dfilter, 1)
+
+ def test_minus_const_2(self, checkDFilterCount):
+ dfilter = "tcp.window_size_scalefactor == -2"
+ checkDFilterCount(dfilter, 0)
+
+ def test_plus_const_1(self, checkDFilterCount):
+ dfilter = "tcp.window_size_scalefactor == +1"
+ checkDFilterCount(dfilter, 0)
+
+ def test_unary_1(self, checkDFilterCount):
+ dfilter = "tcp.window_size_scalefactor == -tcp.dstport"
+ checkDFilterCount(dfilter, 0)
+
+ def test_unary_2(self, checkDFilterCount):
+ dfilter = "tcp.window_size_scalefactor == +tcp.dstport"
+ checkDFilterCount(dfilter, 0)
+
+ def test_unary_3(self, checkDFilterFail):
+ error = 'Constant expression is invalid on the LHS'
+ dfilter = "-2 == tcp.dstport"
+ checkDFilterFail(dfilter, error)
+
+ def test_unary_4(self, checkDFilterCount):
+ dfilter = "tcp.window_size_scalefactor == -{tcp.dstport * 20}"
+ checkDFilterCount(dfilter, 0)
+
+ def test_unary_invalid_1(self, checkDFilterFail):
+ error = 'FT_PROTOCOL cannot be negated'
+ dfilter = "-tcp"
+ checkDFilterFail(dfilter, error)
+
+class TestDfilterArithmetic:
+ trace_file = "dhcp.pcap"
+
+ def test_add_1(self, checkDFilterCount):
+ dfilter = "udp.dstport == udp.srcport + 1"
+ checkDFilterCount(dfilter, 2)
+
+ def test_add_2(self, checkDFilterCount):
+ dfilter = "udp.dstport == 66 + 1"
+ checkDFilterCount(dfilter, 2)
+
+ def test_add_3(self, checkDFilterCount):
+ dfilter = "udp.dstport == 66+1"
+ checkDFilterCount(dfilter, 2)
+
+ def test_add_4(self, checkDFilterFail):
+ error = 'Unknown type for left side of +'
+ dfilter = "1 + 2 == frame.number"
+ checkDFilterFail(dfilter, error)
+
+ def test_add_5(self, checkDFilterFail):
+ error = 'Unknown type for left side of +'
+ dfilter = "1 + 2 == 2 + 1"
+ checkDFilterFail(dfilter, error)
+
+ def test_add_6(self, checkDFilterFail):
+ error = 'Unknown type for left side of -'
+ dfilter = "1 - 2"
+ checkDFilterFail(dfilter, error)
+
+ def test_sub_1(self, checkDFilterCount):
+ dfilter = "udp.srcport == udp.dstport - 1"
+ checkDFilterCount(dfilter, 2)
+
+ def test_sub_2(self, checkDFilterCount):
+ dfilter = "udp.dstport == 68 - 1"
+ checkDFilterCount(dfilter, 2)
+
+ def test_sub_3(self, checkDFilterCount):
+ dfilter = "udp.length == ip.len - 20"
+ checkDFilterCount(dfilter, 4)
+
+ def test_sub_no_space_1(self, checkDFilterFail):
+ # Minus operator requires whitespace preceding it.
+ error = '"68-1" is not a valid number'
+ dfilter = "udp.dstport == 68-1"
+ checkDFilterFail(dfilter, error)
+
+ def test_sub_no_space_2(self, checkDFilterFail):
+ # Different case, 68-67 should not be parsed
+ # as bytes separated by hyphen XX-XX-XX
+ # Minus operator still requires whitespace preceding it.
+ error = '"68-67" is not a valid number'
+ dfilter = "frame.number == 68-67"
+ checkDFilterFail(dfilter, error)
+
+ def test_expr_1(self, checkDFilterCount):
+ dfilter = 'udp.port * { 10 / {5 - 4} } == udp.port * { {50 + 50} / 2 - 40 }'
+ checkDFilterCount(dfilter, 4)
+
+ def test_expr_2(self, checkDFilterCount):
+ dfilter = 'udp.dstport * { udp.srcport / {5 - 4} } == udp.srcport * { 2 * udp.dstport - 68 }'
+ checkDFilterCount(dfilter, 2)
+
+class TestDfilterFieldReference:
+ trace_file = "ipoipoip.pcap"
+
+ def test_ref_1(self, checkDFilterCountWithSelectedFrame):
+ dfilter = 'frame.number < ${frame.number}'
+ # select frame 2, expect 1 frames out of 2.
+ checkDFilterCountWithSelectedFrame(dfilter, 1, 2)
+
+ def test_ref_2(self, checkDFilterCountWithSelectedFrame):
+ dfilter = 'ip.src#3 == ${ip.src#4}'
+ # select frame 1, expect 1 frames out of 2.
+ checkDFilterCountWithSelectedFrame(dfilter, 1, 1)
+
+class TestDfilterLayer:
+ trace_file = "ipoipoip.pcap"
+
+ def test_layer_1(self, checkDFilterCount):
+ dfilter = 'ip.addr#2 == 4.4.4.4'
+ checkDFilterCount(dfilter, 1)
+
+ def test_layer_2(self, checkDFilterCount):
+ dfilter = 'ip.addr#5'
+ checkDFilterCount(dfilter, 1)
+
+ def test_layer_3(self, checkDFilterCount):
+ dfilter = 'ip.addr#6'
+ checkDFilterCount(dfilter, 0)
+
+ def test_layer_4(self, checkDFilterCount):
+ dfilter = 'ip.dst#[2-4] == 8.8.8.8'
+ checkDFilterCount(dfilter, 1)
+
+ def test_layer_5(self, checkDFilterCount):
+ dfilter = 'ip.dst#[-1] == 8.8.8.8'
+ checkDFilterCount(dfilter, 0)
+
+ def test_layer_6(self, checkDFilterCount):
+ dfilter = 'ip.dst#[-1] == 9.9.9.9'
+ checkDFilterCount(dfilter, 1)
+
+ def test_layer_7(self, checkDFilterCount):
+ dfilter = 'ip.dst#[-5] == 2.2.2.2'
+ checkDFilterCount(dfilter, 1)
+
+class TestDfilterQuantifiers:
+ trace_file = "ipoipoip.pcap"
+
+ def test_any_1(self, checkDFilterCount):
+ dfilter = 'any ip.addr > 1.1.1.1'
+ checkDFilterCount(dfilter, 2)
+
+ def test_all_1(self, checkDFilterCount):
+ dfilter = 'all ip.addr > 1.1.1.1'
+ checkDFilterCount(dfilter, 1)
+
+class TestDfilterRawModifier:
+ trace_file = "s7comm-fuzz.pcapng.gz"
+
+ def test_regular(self, checkDFilterCount):
+ dfilter = 's7comm.blockinfo.blocktype == "0\uFFFD"'
+ checkDFilterCount(dfilter, 3)
+
+ def test_raw1(self, checkDFilterCount):
+ dfilter = '@s7comm.blockinfo.blocktype == 30:aa'
+ checkDFilterCount(dfilter, 2)
+
+ def test_raw2(self, checkDFilterCount):
+ dfilter = '@s7comm.blockinfo.blocktype == 30:fe'
+ checkDFilterCount(dfilter, 1)
+
+ def test_raw_ref(self, checkDFilterCountWithSelectedFrame):
+ dfilter = '@s7comm.blockinfo.blocktype == ${@s7comm.blockinfo.blocktype}'
+ # select frame 3, expect 2 frames out of 3.
+ checkDFilterCountWithSelectedFrame(dfilter, 2, 3)
+
+class TestDfilterRawSlice:
+ trace_file = "http.pcap"
+
+ def test_raw_slice1(self, checkDFilterFail):
+ dfilter = 'tcp.port[1] == 0xc3'
+ checkDFilterFail(dfilter, "cannot be sliced")
+
+ def test_raw_slice2(self, checkDFilterCount):
+ dfilter = '@tcp.port[1] == 0xc3'
+ checkDFilterCount(dfilter, 1)
+
+ def test_raw_slice3(self, checkDFilterFail):
+ dfilter = 'tcp.port[0:] == 0c:c3'
+ checkDFilterFail(dfilter, "cannot be sliced")
+
+ def test_raw_slice4(self, checkDFilterCount):
+ dfilter = '@tcp.port[0:] == 0c:c3'
+ checkDFilterCount(dfilter, 1)
+
+class TestDfilterXor:
+ trace_file = "ipoipoip.pcap"
+
+ def test_xor_1(self, checkDFilterCount):
+ dfilter = 'ip.src == 7.7.7.7 xor ip.dst == 7.7.7.7'
+ checkDFilterCount(dfilter, 1)
+
+ def test_xor_2(self, checkDFilterCount):
+ dfilter = 'ip.src == 7.7.7.7 ^^ ip.dst == 7.7.7.7'
+ checkDFilterCount(dfilter, 1)
+
+ def test_xor_3(self, checkDFilterCount):
+ dfilter = 'ip.src == 9.9.9.9 xor ip.dst == 9.9.9.9'
+ checkDFilterCount(dfilter, 0)
+
+ def test_xor_4(self, checkDFilterCount):
+ dfilter = 'ip.src == 9.9.9.9 ^^ ip.dst == 9.9.9.9'
+ checkDFilterCount(dfilter, 0)
+
+class TestDfilterTFSValueString:
+ trace_file = "http.pcap"
+
+ def test_tfs_1(self, checkDFilterCount):
+ dfilter = 'ip.flags.df == True'
+ checkDFilterCount(dfilter, 1)
+
+ def test_tfs_2(self, checkDFilterCount):
+ dfilter = 'ip.flags.df == "True"'
+ checkDFilterCount(dfilter, 1)
+
+ def test_tfs_3(self, checkDFilterCount):
+ dfilter = 'ip.flags.df == "Set"'
+ checkDFilterCount(dfilter, 1)
+
+ def test_tfs_4(self, checkDFilterCount):
+ dfilter = 'frame.ignored == False'
+ checkDFilterCount(dfilter, 1)
+
+ def test_tfs_5(self, checkDFilterCount):
+ dfilter = 'frame.ignored == "False"'
+ checkDFilterCount(dfilter, 1)
+
+ def test_tfs_6(self, checkDFilterFail):
+ error = 'expected "True" or "False", not "Unset"'
+ dfilter = 'frame.ignored == "Unset"'
+ checkDFilterFail(dfilter, error)
diff --git a/test/suite_dfilter/group_time.py b/test/suite_dfilter/group_time.py
new file mode 100644
index 0000000..016c1d6
--- /dev/null
+++ b/test/suite_dfilter/group_time.py
@@ -0,0 +1,148 @@
+# Copyright (c) 2013 by Gilbert Ramirez <gram@alumni.rice.edu>
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+import pytest
+from suite_dfilter.dfiltertest import *
+
+
+class TestDfilterTime:
+ trace_file = "http.pcap"
+
+ def test_eq_1(self, checkDFilterCount):
+ dfilter = 'frame.time == "Dec 31, 2002 13:55:31.3"'
+ checkDFilterCount(dfilter, 1)
+
+ def test_eq_2(self, checkDFilterCount):
+ dfilter = 'frame.time == "Jan 31, 2002 13:55:31.3"'
+ checkDFilterCount(dfilter, 0)
+
+ def test_eq_3(self, checkDFilterCount):
+ dfilter = 'frame.time == "2002-12-31 13:55:31.3"'
+ checkDFilterCount(dfilter, 1)
+
+ def test_eq_4(self, checkDFilterCount):
+ dfilter = 'frame.time == 1041342931.3'
+ checkDFilterCount(dfilter, 1)
+
+ def test_ne_1(self, checkDFilterCount):
+ dfilter = 'frame.time != "Dec 31, 2002 13:55:31.3"'
+ checkDFilterCount(dfilter, 0)
+
+ def test_ne_2(self, checkDFilterCount):
+ dfilter = 'frame.time != "Jan 31, 2002 13:55:31.3"'
+ checkDFilterCount(dfilter, 1)
+
+ def test_gt_1(self, checkDFilterCount):
+ dfilter = 'frame.time > "Dec 31, 2002 13:54:31.3"'
+ checkDFilterCount(dfilter, 1)
+
+ def test_gt_2(self, checkDFilterCount):
+ dfilter = 'frame.time > "Dec 31, 2002 13:55:31.3"'
+ checkDFilterCount(dfilter, 0)
+
+ def test_gt_3(self, checkDFilterCount):
+ dfilter = 'frame.time > "Dec 31, 2002 13:56:31.3"'
+ checkDFilterCount(dfilter, 0)
+
+ def test_ge_1(self, checkDFilterCount):
+ dfilter = 'frame.time >= "Dec 31, 2002 13:54:31.3"'
+ checkDFilterCount(dfilter, 1)
+
+ def test_ge_2(self, checkDFilterCount):
+ dfilter = 'frame.time >= "Dec 31, 2002 13:55:31.3"'
+ checkDFilterCount(dfilter, 1)
+
+ def test_ge_3(self, checkDFilterCount):
+ dfilter = 'frame.time >= "Dec 31, 2002 13:56:31.3"'
+ checkDFilterCount(dfilter, 0)
+
+ def test_lt_1(self, checkDFilterCount):
+ dfilter = 'frame.time < "Dec 31, 2002 13:54:31.3"'
+ checkDFilterCount(dfilter, 0)
+
+ def test_lt_2(self, checkDFilterCount):
+ dfilter = 'frame.time < "Dec 31, 2002 13:55:31.3"'
+ checkDFilterCount(dfilter, 0)
+
+ def test_lt_3(self, checkDFilterCount):
+ dfilter = 'frame.time < "Dec 31, 2002 13:56:31.3"'
+ checkDFilterCount(dfilter, 1)
+
+ def test_le_1(self, checkDFilterCount):
+ dfilter = 'frame.time <= "Dec 31, 2002 13:54:31.3"'
+ checkDFilterCount(dfilter, 0)
+
+ def test_le_2(self, checkDFilterCount):
+ dfilter = 'frame.time <= "Dec 31, 2002 13:55:31.3"'
+ checkDFilterCount(dfilter, 1)
+
+ def test_le_3(self, checkDFilterCount):
+ dfilter = 'frame.time <= "Dec 31, 2002 13:56:31.3"'
+ checkDFilterCount(dfilter, 1)
+
+ def test_utc_time_1(self, checkDFilterCount):
+ dfilter = 'frame.time == "Dec 31, 2002 13:55:31.3 UTC"'
+ checkDFilterCount(dfilter, 1)
+
+ def test_utc_time_2(self, checkDFilterCount):
+ dfilter = 'frame.time == "2002-12-31 13:55:31.3 UTC"'
+ checkDFilterCount(dfilter, 1)
+
+ def test_bad_time_2(self, checkDFilterFail):
+ # Miliseconds can only occur after seconds.
+ dfilter = 'frame.time == "2002-12-31 13:55.3"'
+ error = 'requires a seconds field'
+ checkDFilterFail(dfilter, error)
+
+ def test_bad_time_3(self, checkDFilterFail):
+ # Reject months in a different locale (mrt is March in nl_NL.UTF-8).
+ dfilter = 'frame.time == "mrt 1, 2000 00:00:00"'
+ error = '"mrt 1, 2000 00:00:00" is not a valid absolute time. Example: "Nov 12, 1999 08:55:44.123" or "2011-07-04 12:34:56"'
+ checkDFilterFail(dfilter, error)
+
+class TestDfilterTimeRelative:
+ trace_file = "nfs.pcap"
+
+ def test_relative_time_1(self, checkDFilterCount):
+ dfilter = "frame.time_delta == 0.7"
+ checkDFilterCount(dfilter, 1)
+
+ def test_relative_time_2(self, checkDFilterCount):
+ dfilter = "frame.time_delta > 0.7"
+ checkDFilterCount(dfilter, 0)
+
+ def test_relative_time_3(self, checkDFilterCount):
+ dfilter = "frame.time_delta < 0.7"
+ checkDFilterCount(dfilter, 1)
+
+class TestDfilterTimezone:
+ trace_file = "http.pcap"
+
+ # These are all the same value expressed in different
+ # ways and timezones
+
+ def test_time_1(self, checkDFilterCount):
+ dfilter = 'frame.time == "2002-12-31 13:55:31.3"'
+ checkDFilterCount(dfilter, 1)
+
+ def test_time_2(self, checkDFilterCount):
+ dfilter = 'frame.time == "2002-12-31 13:55:31.3Z"'
+ checkDFilterCount(dfilter, 1)
+
+ def test_time_3(self, checkDFilterCount):
+ dfilter = 'frame.time == "2002-12-31 15:55:31.3 +02:00"'
+ checkDFilterCount(dfilter, 1)
+
+ def test_time_4(self, checkDFilterCount):
+ # Foxtrot time zone
+ dfilter = 'frame.time == "2002-12-31 19:55:31.3 F"'
+ checkDFilterCount(dfilter, 1)
+
+ def test_time_5(self, checkDFilterCount):
+ dfilter = 'frame.time == "2002-12-31 05:55:31.3 PST"'
+ checkDFilterCount(dfilter, 1)
+
+ def test_time_6(self, checkDFilterCount):
+ dfilter = 'frame.time == "2002-12-31 07:55:31.3 CST"'
+ checkDFilterCount(dfilter, 1)
diff --git a/test/suite_dfilter/group_tvb.py b/test/suite_dfilter/group_tvb.py
new file mode 100644
index 0000000..3d09d92
--- /dev/null
+++ b/test/suite_dfilter/group_tvb.py
@@ -0,0 +1,57 @@
+# Copyright (c) 2013 by Gilbert Ramirez <gram@alumni.rice.edu>
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+import pytest
+from suite_dfilter.dfiltertest import *
+
+
+class TestDfilterProtocol:
+ trace_file = "http.pcap"
+
+ def test_slice_1(self, checkDFilterCount):
+ dfilter = "ip[0:2] == 45:00"
+ checkDFilterCount(dfilter, 1)
+
+ def test_slice_2(self, checkDFilterCount):
+ dfilter = "ip[0:2] == 00:00"
+ checkDFilterCount(dfilter, 0)
+
+ def test_slice_3(self, checkDFilterCount):
+ dfilter = "ip[2:2] == 00:c1"
+ checkDFilterCount(dfilter, 1)
+
+ def test_contains_1(self, checkDFilterCount):
+ dfilter = "eth contains 6b"
+ checkDFilterCount(dfilter, 1)
+
+ def test_contains_2(self, checkDFilterCount):
+ dfilter = "eth contains 09:6b:88"
+ checkDFilterCount(dfilter, 1)
+
+ def test_contains_3(self, checkDFilterCount):
+ dfilter = "eth contains 00:e0:81:00:b0:28:00:09:6b:88:f5:c9:08:00"
+ checkDFilterCount(dfilter, 1)
+
+ def test_contains_4(self, checkDFilterCount):
+ dfilter = "eth contains ff:ff:ff"
+ checkDFilterCount(dfilter, 0)
+
+ def test_contains_5(self, checkDFilterCount):
+ dfilter = 'http contains "HEAD"'
+ checkDFilterCount(dfilter, 1)
+
+ def test_protocol_1(self, checkDFilterSucceed):
+ dfilter = 'frame contains aa.bb.ff'
+ checkDFilterSucceed(dfilter)
+
+ def test_protocol_2(self, checkDFilterFail):
+ dfilter = 'frame contains aa.bb.hh'
+ checkDFilterFail(dfilter, 'is not a valid byte string')
+
+ def test_protocol_3(self, checkDFilterFail):
+ dfilter = 'ip.port == 5'
+ ### XXX This should say something about ip.port not being a valid
+ # protocol or protocol field but ip.port is interpreted as
+ # a literal/unparsed value.
+ checkDFilterFail(dfilter, 'Constant expression is invalid')
diff --git a/test/suite_dissection.py b/test/suite_dissection.py
new file mode 100644
index 0000000..ee90c93
--- /dev/null
+++ b/test/suite_dissection.py
@@ -0,0 +1,929 @@
+#
+# Wireshark tests
+# By Gerald Combs <gerald@wireshark.org>
+#
+# Ported from a set of Bash scripts which were copyright 2005 Ulf Lamping
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+'''Dissection tests'''
+
+import sys
+import os.path
+import subprocess
+from subprocesstest import count_output, grep_output
+import pytest
+
+
+class TestDissectDtnTcpcl:
+ def test_tcpclv3_xfer(self, cmd_tshark, capture_file, test_env):
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('dtn_tcpclv3_bpv6_transfer.pcapng'),
+ '-Tfields', '-etcpcl.ack.length',
+ ), encoding='utf-8', env=test_env)
+ assert count_output(stdout, r'1064') == 2
+
+ def test_tcpclv4_xfer(self, cmd_tshark, capture_file, test_env):
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('dtn_tcpclv4_bpv7_transfer.pcapng'),
+ '-Tfields', '-etcpcl.v4.xfer_ack.ack_len',
+ ), encoding='utf-8', env=test_env)
+ assert count_output(stdout, r'199') == 2
+
+
+class TestDissectBpv7:
+ def test_bpv7_admin_status(self, cmd_tshark, capture_file, test_env):
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('dtn_udpcl_bpv7_bpsec_bib_admin.pcapng'),
+ '-Tfields', '-ebpv7.status_rep.identity',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, r'Source: ipn:93.185, DTN Time: 1396536125, Seq: 281')
+
+ def test_bpv7_bpsec_bib(self, cmd_tshark, capture_file, test_env):
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('dtn_udpcl_bpv7_bpsec_bib_admin.pcapng'),
+ '-Tfields', '-ebpsec.asb.ctxid',
+ ), encoding='utf-8', env=test_env)
+ assert count_output(stdout, r'1') == 1
+
+ def test_bpv7_bpsec_bib_admin_type(self, cmd_tshark, capture_file, test_env):
+ # BIB doesn't alter payload
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('dtn_udpcl_bpv7_bpsec_bib_admin.pcapng'),
+ '-Tfields', '-ebpv7.admin_rec.type_code',
+ ), encoding='utf-8', env=test_env)
+ assert count_output(stdout, r'1') == 1
+
+ def test_bpv7_bpsec_bcb(self, cmd_tshark, capture_file, test_env):
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('dtn_udpcl_bpv7_bpsec_bcb_admin.pcapng'),
+ '-Tfields', '-ebpsec.asb.ctxid',
+ ), encoding='utf-8', env=test_env)
+ assert count_output(stdout, r'2') == 1
+
+ def test_bpv7_bpsec_bcb_admin_type(self, cmd_tshark, capture_file, test_env):
+ # BCB inhibits payload dissection
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('dtn_udpcl_bpv7_bpsec_bcb_admin.pcapng'),
+ '-Tfields', '-ebpv7.admin_rec.type_code',
+ ), encoding='utf-8', env=test_env)
+ assert count_output(stdout, r'1') == 0
+
+
+class TestDissectCose:
+ '''
+ These test captures were generated from the COSE example files with command:
+ for FN in test/captures/cose*.cbordiag; do python3 tools/generate_cbor_pcap.py --content-type 'application/cose' --infile $FN --outfile ${FN%.cbordiag}.pcap; done
+ '''
+ def test_cose_sign_tagged(self, cmd_tshark, capture_file, test_env):
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('cose_sign_tagged.pcap'),
+ '-Tfields', '-ecose.msg.signature',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, 'e2aeafd40d69d19dfe6e52077c5d7ff4e408282cbefb5d06cbf414af2e19d982ac45ac98b8544c908b4507de1e90b717c3d34816fe926a2b98f53afd2fa0f30a')
+
+ def test_cose_sign1_tagged(self, cmd_tshark, capture_file, test_env):
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('cose_sign1_tagged.pcap'),
+ '-Tfields', '-ecose.msg.signature',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, '8eb33e4ca31d1c465ab05aac34cc6b23d58fef5c083106c4d25a91aef0b0117e2af9a291aa32e14ab834dc56ed2a223444547e01f11d3b0916e5a4c345cacb36')
+
+ def test_cose_encrypt_tagged(self, cmd_tshark, capture_file, test_env):
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('cose_encrypt_tagged.pcap'),
+ '-Tfields', '-ecose.kid',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, '6f75722d736563726574')
+
+ def test_cose_encrypt0_tagged(self, cmd_tshark, capture_file, test_env):
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('cose_encrypt0_tagged.pcap'),
+ '-Tfields', '-ecose.iv',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, '89f52f65a1c580933b5261a78c')
+
+ def test_cose_mac_tagged(self, cmd_tshark, capture_file, test_env):
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('cose_mac_tagged.pcap'),
+ '-Tfields', '-ecose.kid',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, '30313863306165352d346439622d343731622d626664362d656566333134626337303337')
+
+ def test_cose_mac0_tagged(self, cmd_tshark, capture_file, test_env):
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('cose_mac0_tagged.pcap'),
+ '-Tfields', '-ecose.msg.mac_tag',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, '726043745027214f')
+
+ def test_cose_keyset(self, cmd_tshark, capture_file, test_env):
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('cose_keyset.pcap'),
+ '-Tfields', '-ecose.key.k',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, '849b57219dae48de646d07dbb533566e976686457c1491be3a76dcea6c427188')
+
+
+class TestDissectGprpc:
+ def test_grpc_with_json(self, cmd_tshark, features, dirs, capture_file, test_env):
+ '''gRPC with JSON payload'''
+ if not features.have_nghttp2:
+ pytest.skip('Requires nghttp2.')
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('grpc_person_search_json_with_image.pcapng.gz'),
+ '-d', 'tcp.port==50052,http2',
+ '-2',
+ '-Y', 'grpc.message_length == 208 && json.value.string == "87561234"',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, 'GRPC/JSON')
+
+ def test_grpc_with_protobuf(self, cmd_tshark, features, dirs, capture_file, test_env):
+ '''gRPC with Protobuf payload'''
+ if not features.have_nghttp2:
+ pytest.skip('Requires nghttp2.')
+ well_know_types_dir = os.path.join(dirs.protobuf_lang_files_dir, 'well_know_types').replace('\\', '/')
+ user_defined_types_dir = os.path.join(dirs.protobuf_lang_files_dir, 'user_defined_types').replace('\\', '/')
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('grpc_person_search_protobuf_with_image.pcapng.gz'),
+ '-o', 'uat:protobuf_search_paths: "{}","{}"'.format(well_know_types_dir, 'FALSE'),
+ '-o', 'uat:protobuf_search_paths: "{}","{}"'.format(user_defined_types_dir, 'TRUE'),
+ '-d', 'tcp.port==50051,http2',
+ '-2',
+ '-Y', 'protobuf.message.name == "tutorial.PersonSearchRequest"'
+ ' || (grpc.message_length == 66 && protobuf.field.value.string == "Jason"'
+ ' && protobuf.field.value.int64 == 1602601886)',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, 'tutorial.PersonSearchService/Search') # grpc request
+ assert grep_output(stdout, 'tutorial.Person') # grpc response
+
+ def test_grpc_streaming_mode_reassembly(self, cmd_tshark, features, dirs, capture_file, test_env):
+ '''gRPC/HTTP2 streaming mode reassembly'''
+ if not features.have_nghttp2:
+ pytest.skip('Requires nghttp2.')
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('grpc_stream_reassembly_sample.pcapng.gz'),
+ '-d', 'tcp.port==50051,http2',
+ '-d', 'tcp.port==44363,http2',
+ '-2', # make http2.body.reassembled.in available
+ '-Y', # Case1: In frame28, one http DATA contains 4 completed grpc messages (json data seq=1,2,3,4).
+ '(frame.number == 28 && grpc && json.value.number == "1" && json.value.number == "2"'
+ ' && json.value.number == "3" && json.value.number == "4" && http2.body.reassembled.in == 45) ||'
+ # Case2: In frame28, last grpc message (the 5th) only has 4 bytes, which need one more byte
+ # to be a message head. a completed message is reassembled in frame45. (json data seq=5)
+ '(frame.number == 45 && grpc && http2.body.fragment == 28 && json.value.number == "5"'
+ ' && http2.body.reassembled.in == 61) ||'
+ # Case3: In frame45, one http DATA frame contains two partial fragment, one is part of grpc
+ # message of previous http DATA (frame28), another is first part of grpc message of next http
+ # DATA (which will be reassembled in next http DATA frame61). (json data seq=6)
+ '(frame.number == 61 && grpc && http2.body.fragment == 45 && json.value.number == "6") ||'
+ # Case4: A big grpc message across frame100, frame113, frame126 and finally reassembled in frame139.
+ '(frame.number == 100 && grpc && http2.body.reassembled.in == 139) ||'
+ '(frame.number == 113 && !grpc && http2.body.reassembled.in == 139) ||'
+ '(frame.number == 126 && !grpc && http2.body.reassembled.in == 139) ||'
+ '(frame.number == 139 && grpc && json.value.number == "9") ||'
+ # Case5: An large grpc message of 200004 bytes.
+ '(frame.number == 164 && grpc && grpc.message_length == 200004)',
+ ), encoding='utf-8', env=test_env)
+ assert count_output(stdout, 'DATA') == 8
+
+ def test_grpc_http2_fake_headers(self, cmd_tshark, features, dirs, capture_file, test_env):
+ '''HTTP2/gRPC fake headers (used when HTTP2 initial HEADERS frame is missing)'''
+ if not features.have_nghttp2:
+ pytest.skip('Requires nghttp2.')
+ well_know_types_dir = os.path.join(dirs.protobuf_lang_files_dir, 'well_know_types').replace('\\', '/')
+ user_defined_types_dir = os.path.join(dirs.protobuf_lang_files_dir, 'user_defined_types').replace('\\', '/')
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('grpc_person_search_protobuf_with_image-missing_headers.pcapng.gz'),
+ '-o', 'uat:protobuf_search_paths: "{}","{}"'.format(well_know_types_dir, 'FALSE'),
+ '-o', 'uat:protobuf_search_paths: "{}","{}"'.format(user_defined_types_dir, 'TRUE'),
+ '-o', 'uat:http2_fake_headers: "{}","{}","{}","{}","{}","{}"'.format(
+ '50051','3','IN',':path','/tutorial.PersonSearchService/Search','TRUE'),
+ '-o', 'uat:http2_fake_headers: "{}","{}","{}","{}","{}","{}"'.format(
+ '50051','0','IN','content-type','application/grpc','TRUE'),
+ '-o', 'uat:http2_fake_headers: "{}","{}","{}","{}","{}","{}"'.format(
+ '50051','0','OUT','content-type','application/grpc','TRUE'),
+ '-d', 'tcp.port==50051,http2',
+ '-2',
+ '-Y', 'protobuf.field.value.string == "Jason" || protobuf.field.value.string == "Lily"',
+ ), encoding='utf-8', env=test_env)
+ assert count_output(stdout, 'DATA') == 2
+
+
+class TestDissectGrpcWeb:
+ def test_grpc_web_unary_call_over_http1(self, cmd_tshark, features, dirs, capture_file, test_env):
+ '''gRPC-Web unary call over http1'''
+ well_know_types_dir = os.path.join(dirs.protobuf_lang_files_dir, 'well_know_types').replace('\\', '/')
+ user_defined_types_dir = os.path.join(dirs.protobuf_lang_files_dir, 'user_defined_types').replace('\\', '/')
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('grpc_web.pcapng.gz'),
+ '-o', 'uat:protobuf_search_paths: "{}","{}"'.format(well_know_types_dir, 'FALSE'),
+ '-o', 'uat:protobuf_search_paths: "{}","{}"'.format(user_defined_types_dir, 'TRUE'),
+ '-o', 'protobuf.preload_protos: TRUE',
+ '-o', 'protobuf.pbf_as_hf: TRUE',
+ '-d', 'tcp.port==57226,http',
+ '-2',
+ '-Y', '(tcp.stream eq 0) && (pbf.greet.HelloRequest.name == "88888888"'
+ '|| pbf.greet.HelloRequest.name == "99999999"'
+ '|| pbf.greet.HelloReply.message == "Hello 99999999")',
+ ), encoding='utf-8', env=test_env)
+ assert count_output(stdout, 'greet.HelloRequest') == 2
+ assert count_output(stdout, 'greet.HelloReply') == 1
+
+ def test_grpc_web_unary_call_over_http2(self, cmd_tshark, features, dirs, capture_file, test_env):
+ '''gRPC-Web unary call over http2'''
+ if not features.have_nghttp2:
+ pytest.skip('Requires nghttp2.')
+ well_know_types_dir = os.path.join(dirs.protobuf_lang_files_dir, 'well_know_types').replace('\\', '/')
+ user_defined_types_dir = os.path.join(dirs.protobuf_lang_files_dir, 'user_defined_types').replace('\\', '/')
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('grpc_web.pcapng.gz'),
+ '-o', 'uat:protobuf_search_paths: "{}","{}"'.format(well_know_types_dir, 'FALSE'),
+ '-o', 'uat:protobuf_search_paths: "{}","{}"'.format(user_defined_types_dir, 'TRUE'),
+ '-o', 'protobuf.preload_protos: TRUE',
+ '-o', 'protobuf.pbf_as_hf: TRUE',
+ '-d', 'tcp.port==57228,http2',
+ '-2',
+ '-Y', '(tcp.stream eq 1) && (pbf.greet.HelloRequest.name == "88888888"'
+ '|| pbf.greet.HelloRequest.name == "99999999"'
+ '|| pbf.greet.HelloReply.message == "Hello 99999999")',
+ ), encoding='utf-8', env=test_env)
+ assert count_output(stdout, 'greet.HelloRequest') == 2
+ assert count_output(stdout, 'greet.HelloReply') == 1
+
+ def test_grpc_web_reassembly_and_stream_over_http2(self, cmd_tshark, features, dirs, capture_file, test_env):
+ '''gRPC-Web data reassembly and server stream over http2'''
+ if not features.have_nghttp2:
+ pytest.skip('Requires nghttp2.')
+ well_know_types_dir = os.path.join(dirs.protobuf_lang_files_dir, 'well_know_types').replace('\\', '/')
+ user_defined_types_dir = os.path.join(dirs.protobuf_lang_files_dir, 'user_defined_types').replace('\\', '/')
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('grpc_web.pcapng.gz'),
+ '-o', 'uat:protobuf_search_paths: "{}","{}"'.format(well_know_types_dir, 'FALSE'),
+ '-o', 'uat:protobuf_search_paths: "{}","{}"'.format(user_defined_types_dir, 'TRUE'),
+ '-o', 'protobuf.preload_protos: TRUE',
+ '-o', 'protobuf.pbf_as_hf: TRUE',
+ '-d', 'tcp.port==57228,http2',
+ '-2',
+ '-Y', '(tcp.stream eq 2) && ((pbf.greet.HelloRequest.name && grpc.message_length == 80004)'
+ '|| (pbf.greet.HelloReply.message && (grpc.message_length == 23 || grpc.message_length == 80012)))',
+ ), encoding='utf-8', env=test_env)
+ assert count_output(stdout, 'greet.HelloRequest') == 2
+ assert count_output(stdout, 'greet.HelloReply') == 4
+
+ def test_grpc_web_text_unary_call_over_http1(self, cmd_tshark, features, dirs, capture_file, test_env):
+ '''gRPC-Web-Text unary call over http1'''
+ well_know_types_dir = os.path.join(dirs.protobuf_lang_files_dir, 'well_know_types').replace('\\', '/')
+ user_defined_types_dir = os.path.join(dirs.protobuf_lang_files_dir, 'user_defined_types').replace('\\', '/')
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('grpc_web.pcapng.gz'),
+ '-o', 'uat:protobuf_search_paths: "{}","{}"'.format(well_know_types_dir, 'FALSE'),
+ '-o', 'uat:protobuf_search_paths: "{}","{}"'.format(user_defined_types_dir, 'TRUE'),
+ '-o', 'protobuf.preload_protos: TRUE',
+ '-o', 'protobuf.pbf_as_hf: TRUE',
+ '-d', 'tcp.port==57226,http',
+ '-2',
+ '-Y', '(tcp.stream eq 5) && (pbf.greet.HelloRequest.name == "88888888"'
+ '|| pbf.greet.HelloRequest.name == "99999999"'
+ '|| pbf.greet.HelloReply.message == "Hello 99999999")',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, 'GRPC-Web-Text')
+ assert count_output(stdout, 'greet.HelloRequest') == 2
+ assert count_output(stdout, 'greet.HelloReply') == 1
+
+ def test_grpc_web_text_unary_call_over_http2(self, cmd_tshark, features, dirs, capture_file, test_env):
+ '''gRPC-Web-Text unary call over http2'''
+ if not features.have_nghttp2:
+ pytest.skip('Requires nghttp2.')
+ well_know_types_dir = os.path.join(dirs.protobuf_lang_files_dir, 'well_know_types').replace('\\', '/')
+ user_defined_types_dir = os.path.join(dirs.protobuf_lang_files_dir, 'user_defined_types').replace('\\', '/')
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('grpc_web.pcapng.gz'),
+ '-o', 'uat:protobuf_search_paths: "{}","{}"'.format(well_know_types_dir, 'FALSE'),
+ '-o', 'uat:protobuf_search_paths: "{}","{}"'.format(user_defined_types_dir, 'TRUE'),
+ '-o', 'protobuf.preload_protos: TRUE',
+ '-o', 'protobuf.pbf_as_hf: TRUE',
+ '-d', 'tcp.port==57228,http2',
+ '-2',
+ '-Y', '(tcp.stream eq 6) && (pbf.greet.HelloRequest.name == "88888888"'
+ '|| pbf.greet.HelloRequest.name == "99999999"'
+ '|| pbf.greet.HelloReply.message == "Hello 99999999")',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, 'GRPC-Web-Text')
+ assert count_output(stdout, 'greet.HelloRequest') == 2
+ assert count_output(stdout, 'greet.HelloReply') == 1
+
+ def test_grpc_web_text_reassembly_and_stream_over_http2(self, cmd_tshark, features, dirs, capture_file, test_env):
+ '''gRPC-Web-Text data reassembly and server stream over http2'''
+ if not features.have_nghttp2:
+ pytest.skip('Requires nghttp2.')
+ well_know_types_dir = os.path.join(dirs.protobuf_lang_files_dir, 'well_know_types').replace('\\', '/')
+ user_defined_types_dir = os.path.join(dirs.protobuf_lang_files_dir, 'user_defined_types').replace('\\', '/')
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('grpc_web.pcapng.gz'),
+ '-o', 'uat:protobuf_search_paths: "{}","{}"'.format(well_know_types_dir, 'FALSE'),
+ '-o', 'uat:protobuf_search_paths: "{}","{}"'.format(user_defined_types_dir, 'TRUE'),
+ '-o', 'protobuf.preload_protos: TRUE',
+ '-o', 'protobuf.pbf_as_hf: TRUE',
+ '-d', 'tcp.port==57228,http2',
+ '-2',
+ '-Y', '(tcp.stream eq 8) && ((pbf.greet.HelloRequest.name && grpc.message_length == 80004)'
+ '|| (pbf.greet.HelloReply.message && (grpc.message_length == 23 || grpc.message_length == 80012)))',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, 'GRPC-Web-Text')
+ assert count_output(stdout, 'greet.HelloRequest') == 2
+ assert count_output(stdout, 'greet.HelloReply') == 4
+
+ def test_grpc_web_text_reassembly_over_http1(self, cmd_tshark, features, dirs, capture_file, test_env):
+ '''gRPC-Web-Text data reassembly over http1'''
+ well_know_types_dir = os.path.join(dirs.protobuf_lang_files_dir, 'well_know_types').replace('\\', '/')
+ user_defined_types_dir = os.path.join(dirs.protobuf_lang_files_dir, 'user_defined_types').replace('\\', '/')
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('grpc_web.pcapng.gz'),
+ '-o', 'uat:protobuf_search_paths: "{}","{}"'.format(well_know_types_dir, 'FALSE'),
+ '-o', 'uat:protobuf_search_paths: "{}","{}"'.format(user_defined_types_dir, 'TRUE'),
+ '-o', 'protobuf.preload_protos: TRUE',
+ '-o', 'protobuf.pbf_as_hf: TRUE',
+ '-d', 'tcp.port==57226,http',
+ '-2',
+ '-Y', '(tcp.stream eq 7) && (grpc.message_length == 80004 || grpc.message_length == 80010)',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, 'GRPC-Web-Text')
+ assert count_output(stdout, 'greet.HelloRequest') == 1
+ assert count_output(stdout, 'greet.HelloReply') == 1
+
+ def test_grpc_web_server_stream_over_http1(self, cmd_tshark, features, dirs, capture_file, test_env):
+ '''gRPC-Web data server stream over http1'''
+ well_know_types_dir = os.path.join(dirs.protobuf_lang_files_dir, 'well_know_types').replace('\\', '/')
+ user_defined_types_dir = os.path.join(dirs.protobuf_lang_files_dir, 'user_defined_types').replace('\\', '/')
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('grpc_web.pcapng.gz'),
+ '-o', 'uat:protobuf_search_paths: "{}","{}"'.format(well_know_types_dir, 'FALSE'),
+ '-o', 'uat:protobuf_search_paths: "{}","{}"'.format(user_defined_types_dir, 'TRUE'),
+ '-o', 'protobuf.preload_protos: TRUE',
+ '-o', 'protobuf.pbf_as_hf: TRUE',
+ '-d', 'tcp.port==57226,http',
+ '-2',
+ '-Y', '(tcp.stream eq 9) && ((pbf.greet.HelloRequest.name && grpc.message_length == 10)'
+ '|| (pbf.greet.HelloReply.message && grpc.message_length == 18))',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, 'GRPC-Web')
+ assert count_output(stdout, 'greet.HelloRequest') == 1
+ assert count_output(stdout, 'greet.HelloReply') == 9
+
+ def test_grpc_web_reassembly_and_stream_over_http1(self, cmd_tshark, features, dirs, capture_file, test_env):
+ '''gRPC-Web data reassembly and server stream over http1'''
+ well_know_types_dir = os.path.join(dirs.protobuf_lang_files_dir, 'well_know_types').replace('\\', '/')
+ user_defined_types_dir = os.path.join(dirs.protobuf_lang_files_dir, 'user_defined_types').replace('\\', '/')
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('grpc_web.pcapng.gz'),
+ '-o', 'uat:protobuf_search_paths: "{}","{}"'.format(well_know_types_dir, 'FALSE'),
+ '-o', 'uat:protobuf_search_paths: "{}","{}"'.format(user_defined_types_dir, 'TRUE'),
+ '-o', 'protobuf.preload_protos: TRUE',
+ '-o', 'protobuf.pbf_as_hf: TRUE',
+ '-d', 'tcp.port==57226,http',
+ '-2',
+ '-Y', '(tcp.stream eq 10) && ((pbf.greet.HelloRequest.name && grpc.message_length == 80004)'
+ '|| (pbf.greet.HelloReply.message && (grpc.message_length == 23 || grpc.message_length == 80012)))',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, 'GRPC-Web')
+ assert count_output(stdout, 'greet.HelloRequest') == 2
+ assert count_output(stdout, 'greet.HelloReply') == 6
+
+
+
+class TestDissectHttp:
+ def test_http_brotli_decompression(self, cmd_tshark, features, dirs, capture_file, test_env):
+ '''HTTP brotli decompression'''
+ if not features.have_brotli:
+ pytest.skip('Requires brotli.')
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('http-brotli.pcapng'),
+ '-Y', 'http.response.code==200',
+ '-Tfields', '-etext',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, 'This is a test file for testing brotli decompression in Wireshark')
+
+class TestDissectHttp2:
+ def test_http2_data_reassembly(self, cmd_tshark, features, dirs, capture_file, test_env):
+ '''HTTP2 data reassembly'''
+ if not features.have_nghttp2:
+ pytest.skip('Requires nghttp2.')
+ key_file = os.path.join(dirs.key_dir, 'http2-data-reassembly.keys')
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('http2-data-reassembly.pcap'),
+ '-o', 'tls.keylog_file: {}'.format(key_file),
+ '-d', 'tcp.port==8443,tls',
+ '-Y', 'http2.data.data matches "PNG" && http2.data.data matches "END"',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, 'DATA')
+
+ def test_http2_brotli_decompression(self, cmd_tshark, features, dirs, capture_file, test_env):
+ '''HTTP2 brotli decompression'''
+ if not features.have_nghttp2:
+ pytest.skip('Requires nghttp2.')
+ if not features.have_brotli:
+ pytest.skip('Requires brotli.')
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('http2-brotli.pcapng'),
+ '-Y', 'http2.data.data matches "This is a test file for testing brotli decompression in Wireshark"',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, 'DATA')
+
+ def test_http2_follow_0(self, cmd_tshark, features, dirs, capture_file, test_env):
+ '''Follow HTTP/2 Stream ID 0 test'''
+ if not features.have_nghttp2:
+ pytest.skip('Requires nghttp2.')
+ key_file = os.path.join(dirs.key_dir, 'http2-data-reassembly.keys')
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('http2-data-reassembly.pcap'),
+ '-o', 'tls.keylog_file: {}'.format(key_file),
+ '-z', 'follow,http2,hex,0,0'
+ ), encoding='utf-8', env=test_env)
+ # Stream ID 0 bytes
+ assert grep_output(stdout, '00000000 00 00 12 04 00 00 00 00')
+ # Stream ID 1 bytes, decrypted but compressed by HPACK
+ assert not grep_output(stdout, '00000000 00 00 2c 01 05 00 00 00')
+ # Stream ID 1 bytes, decrypted and uncompressed, human readable
+ assert not grep_output(stdout, '00000000 3a 6d 65 74 68 6f 64 3a')
+
+ def test_http2_follow_1(self, cmd_tshark, features, dirs, capture_file, test_env):
+ '''Follow HTTP/2 Stream ID 1 test'''
+ if not features.have_nghttp2:
+ pytest.skip('Requires nghttp2.')
+ key_file = os.path.join(dirs.key_dir, 'http2-data-reassembly.keys')
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('http2-data-reassembly.pcap'),
+ '-o', 'tls.keylog_file: {}'.format(key_file),
+ '-z', 'follow,http2,hex,0,1'
+ ), encoding='utf-8', env=test_env)
+ # Stream ID 0 bytes
+ assert not grep_output(stdout, '00000000 00 00 12 04 00 00 00 00')
+ # Stream ID 1 bytes, decrypted but compressed by HPACK
+ assert not grep_output(stdout, '00000000 00 00 2c 01 05 00 00 00')
+ # Stream ID 1 bytes, decrypted and uncompressed, human readable
+ assert grep_output(stdout, '00000000 3a 6d 65 74 68 6f 64 3a')
+
+class TestDissectHttp2:
+ def test_http3_qpack_reassembly(self, cmd_tshark, features, dirs, capture_file, test_env):
+ '''HTTP/3 QPACK encoder stream reassembly'''
+ if not features.have_nghttp3:
+ pytest.skip('Requires nghttp3.')
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('http3-qpack-reassembly-anon.pcapng'),
+ '-Y', 'http3.frame_type == "HEADERS"',
+ '-T', 'fields', '-e', 'http3.headers.method',
+ '-e', 'http3.headers.authority',
+ '-e', 'http3.headers.referer', '-e', 'http3.headers.user_agent',
+ '-e', 'http3.qpack.encoder.icnt'
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, 'POST')
+ assert grep_output(stdout, 'googlevideo.com')
+ assert grep_output(stdout, 'https://www.youtube.com')
+ assert grep_output(stdout, 'Mozilla/5.0')
+ assert grep_output(stdout, '21') # Total number of QPACK insertions
+
+class TestDissectProtobuf:
+ def test_protobuf_udp_message_mapping(self, cmd_tshark, features, dirs, capture_file, test_env):
+ '''Test Protobuf UDP Message Mapping and parsing google.protobuf.Timestamp features'''
+ well_know_types_dir = os.path.join(dirs.protobuf_lang_files_dir, 'well_know_types').replace('\\', '/')
+ user_defined_types_dir = os.path.join(dirs.protobuf_lang_files_dir, 'user_defined_types').replace('\\', '/')
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('protobuf_udp_addressbook_with_image_ts.pcapng'),
+ '-o', 'uat:protobuf_search_paths: "{}","{}"'.format(well_know_types_dir, 'FALSE'),
+ '-o', 'uat:protobuf_search_paths: "{}","{}"'.format(user_defined_types_dir, 'TRUE'),
+ '-o', 'uat:protobuf_udp_message_types: "8127","tutorial.AddressBook"',
+ '-o', 'protobuf.preload_protos: TRUE',
+ '-o', 'protobuf.pbf_as_hf: TRUE',
+ '-Y', 'pbf.tutorial.Person.name == "Jason"'
+ ' && pbf.tutorial.Person.last_updated > "2020-10-15"'
+ ' && pbf.tutorial.Person.last_updated < "2020-10-19"',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, 'tutorial.AddressBook')
+
+ def test_protobuf_message_type_leading_with_dot(self, cmd_tshark, features, dirs, capture_file, test_env):
+ '''Test Protobuf Message type is leading with dot'''
+ well_know_types_dir = os.path.join(dirs.protobuf_lang_files_dir, 'well_know_types').replace('\\', '/')
+ user_defined_types_dir = os.path.join(dirs.protobuf_lang_files_dir, 'user_defined_types').replace('\\', '/')
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('protobuf_test_leading_dot.pcapng'),
+ '-o', 'uat:protobuf_search_paths: "{}","{}"'.format(well_know_types_dir, 'FALSE'),
+ '-o', 'uat:protobuf_search_paths: "{}","{}"'.format(user_defined_types_dir, 'TRUE'),
+ '-o', 'uat:protobuf_udp_message_types: "8123","a.b.msg"',
+ '-o', 'protobuf.preload_protos: TRUE',
+ '-o', 'protobuf.pbf_as_hf: TRUE',
+ '-Y', 'pbf.a.b.a.b.c.param3 contains "in a.b.a.b.c" && pbf.a.b.c.param6 contains "in a.b.c"',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, 'PB[(]a.b.msg[)]')
+
+ def test_protobuf_map_and_oneof_types(self, cmd_tshark, features, dirs, capture_file, test_env):
+ '''Test Protobuf map and oneof types, and taking keyword as identification'''
+ well_know_types_dir = os.path.join(dirs.protobuf_lang_files_dir, 'well_know_types').replace('\\', '/')
+ user_defined_types_dir = os.path.join(dirs.protobuf_lang_files_dir, 'user_defined_types').replace('\\', '/')
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('protobuf_test_map_and_oneof_types.pcapng'),
+ '-o', 'uat:protobuf_search_paths: "{}","{}"'.format(well_know_types_dir, 'FALSE'),
+ '-o', 'uat:protobuf_search_paths: "{}","{}"'.format(user_defined_types_dir, 'TRUE'),
+ '-o', 'uat:protobuf_udp_message_types: "8124","test.map.MapMaster"',
+ '-o', 'protobuf.preload_protos: TRUE',
+ '-o', 'protobuf.pbf_as_hf: TRUE',
+ '-Y', 'pbf.test.map.MapMaster.param3 == "I\'m param3 for oneof test."' # test oneof type
+ ' && pbf.test.map.MapMaster.param4MapEntry.value == 1234' # test map type
+ ' && pbf.test.map.Foo.param1 == 88 && pbf.test.map.MapMaster.param5MapEntry.key == 88'
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, 'PB[(]test.map.MapMaster[)]')
+
+ def test_protobuf_default_value(self, cmd_tshark, features, dirs, capture_file, test_env):
+ '''Test Protobuf feature adding missing fields with default values'''
+ well_know_types_dir = os.path.join(dirs.protobuf_lang_files_dir, 'well_know_types').replace('\\', '/')
+ user_defined_types_dir = os.path.join(dirs.protobuf_lang_files_dir, 'user_defined_types').replace('\\', '/')
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('protobuf_test_default_value.pcapng'),
+ '-o', 'uat:protobuf_search_paths: "{}","{}"'.format(well_know_types_dir, 'FALSE'),
+ '-o', 'uat:protobuf_search_paths: "{}","{}"'.format(user_defined_types_dir, 'TRUE'),
+ '-o', 'uat:protobuf_udp_message_types: "8128","wireshark.protobuf.test.TestDefaultValueMessage"',
+ '-o', 'protobuf.preload_protos: TRUE',
+ '-o', 'protobuf.pbf_as_hf: TRUE',
+ '-o', 'protobuf.add_default_value: all',
+ '-O', 'protobuf',
+ '-Y', 'pbf.wireshark.protobuf.test.TestDefaultValueMessage.enumFooWithDefaultValue_Fouth == -4'
+ ' && pbf.wireshark.protobuf.test.TestDefaultValueMessage.boolWithDefaultValue_False == false'
+ ' && pbf.wireshark.protobuf.test.TestDefaultValueMessage.int32WithDefaultValue_0 == 0'
+ ' && pbf.wireshark.protobuf.test.TestDefaultValueMessage.doubleWithDefaultValue_Negative0point12345678 == -0.12345678'
+ ' && pbf.wireshark.protobuf.test.TestDefaultValueMessage.stringWithDefaultValue_SymbolPi contains "Pi."'
+ ' && pbf.wireshark.protobuf.test.TestDefaultValueMessage.bytesWithDefaultValue_1F2F890D0A00004B == 1f:2f:89:0d:0a:00:00:4b'
+ ' && pbf.wireshark.protobuf.test.TestDefaultValueMessage.optional' # test taking keyword 'optional' as identification
+ ' && pbf.wireshark.protobuf.test.TestDefaultValueMessage.message' # test taking keyword 'message' as identification
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, 'floatWithDefaultValue_0point23: 0.23') # another default value will be displayed
+ assert grep_output(stdout, 'missing required field \'missingRequiredField\'') # check the missing required field export warn
+
+ def test_protobuf_field_subdissector(self, cmd_tshark, features, dirs, capture_file, test_env):
+ '''Test "protobuf_field" subdissector table'''
+ if not features.have_lua:
+ pytest.skip('Test requires Lua scripting support.')
+ well_know_types_dir = os.path.join(dirs.protobuf_lang_files_dir, 'well_know_types').replace('\\', '/')
+ user_defined_types_dir = os.path.join(dirs.protobuf_lang_files_dir, 'user_defined_types').replace('\\', '/')
+ lua_file = os.path.join(dirs.lua_dir, 'protobuf_test_field_subdissector_table.lua')
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('protobuf_udp_addressbook_with_image_ts.pcapng'),
+ '-o', 'uat:protobuf_search_paths: "{}","{}"'.format(well_know_types_dir, 'FALSE'),
+ '-o', 'uat:protobuf_search_paths: "{}","{}"'.format(user_defined_types_dir, 'TRUE'),
+ '-o', 'uat:protobuf_udp_message_types: "8127","tutorial.AddressBook"',
+ '-o', 'protobuf.preload_protos: TRUE',
+ '-o', 'protobuf.pbf_as_hf: TRUE',
+ '-X', 'lua_script:{}'.format(lua_file),
+ '-Y', 'pbf.tutorial.Person.name == "Jason" && pbf.tutorial.Person.last_updated && png',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, 'PB[(]tutorial.AddressBook[)]')
+
+ def test_protobuf_called_by_custom_dissector(self, cmd_tshark, features, dirs, capture_file, test_env):
+ '''Test Protobuf invoked by other dissector (passing type by pinfo.private)'''
+ if not features.have_lua:
+ pytest.skip('Test requires Lua scripting support.')
+ well_know_types_dir = os.path.join(dirs.protobuf_lang_files_dir, 'well_know_types').replace('\\', '/')
+ user_defined_types_dir = os.path.join(dirs.protobuf_lang_files_dir, 'user_defined_types').replace('\\', '/')
+ lua_file = os.path.join(dirs.lua_dir, 'protobuf_test_called_by_custom_dissector.lua')
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('protobuf_tcp_addressbook.pcapng.gz'),
+ '-o', 'uat:protobuf_search_paths: "{}","{}"'.format(well_know_types_dir, 'FALSE'),
+ '-o', 'uat:protobuf_search_paths: "{}","{}"'.format(user_defined_types_dir, 'TRUE'),
+ '-o', 'protobuf.preload_protos: TRUE',
+ '-o', 'protobuf.pbf_as_hf: TRUE',
+ '-X', 'lua_script:{}'.format(lua_file),
+ '-d', 'tcp.port==18127,addrbook',
+ '-Y', 'pbf.tutorial.Person.name == "Jason" && pbf.tutorial.Person.last_updated',
+ ), encoding='utf-8', env=test_env)
+ assert grep_output(stdout, 'tutorial.AddressBook')
+
+ def test_protobuf_complex_syntax(self, cmd_tshark, features, dirs, capture_file, test_env):
+ '''Test Protobuf parsing complex syntax .proto files'''
+ well_know_types_dir = os.path.join(dirs.protobuf_lang_files_dir, 'well_know_types').replace('\\', '/')
+ complex_proto_files_dir = os.path.join(dirs.protobuf_lang_files_dir, 'complex_proto_files').replace('\\', '/')
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('protobuf_udp_addressbook_with_image_ts.pcapng'),
+ '-o', 'uat:protobuf_search_paths: "{}","{}"'.format(well_know_types_dir, 'FALSE'),
+ '-o', 'uat:protobuf_search_paths: "{}","{}"'.format(complex_proto_files_dir, 'TRUE'),
+ '-o', 'protobuf.preload_protos: TRUE',
+ '-o', 'protobuf.pbf_as_hf: TRUE',
+ '-Y', 'pbf.wireshark.protobuf.test.complex.syntax.TestFileParsed.last_field_for_wireshark_test'
+ ' && pbf.protobuf_unittest.TestFileParsed.last_field_for_wireshark_test',
+ ), encoding='utf-8', env=test_env)
+ # the output must be empty and not contain something like:
+ # tshark: "pbf.xxx.TestFileParsed.last_field_for_wireshark_test" is neither a field nor a protocol name.
+ # or
+ # tshark: Protobuf: Error(s)
+ assert not grep_output(stdout, '.last_field_for_wireshark_test')
+ assert not grep_output(stdout, 'Protobuf: Error')
+
+class TestDissectTcp:
+ @staticmethod
+ def check_tcp_out_of_order(cmd_tshark, dirs, test_env, extraArgs=[]):
+ capture_file = os.path.join(dirs.capture_dir, 'http-ooo.pcap')
+ stdout = subprocess.check_output([cmd_tshark,
+ '-r', capture_file,
+ '-otcp.reassemble_out_of_order:TRUE',
+ '-Y', 'http',
+ ] + extraArgs, encoding='utf-8', env=test_env)
+ assert count_output(stdout, 'HTTP') == 5
+ assert grep_output(stdout, r'^\s*4\s.*PUT /1 HTTP/1.1')
+ assert grep_output(stdout, r'^\s*7\s.*GET /2 HTTP/1.1')
+ assert grep_output(stdout, r'^\s*10\s.*PUT /3 HTTP/1.1')
+ assert grep_output(stdout, r'^\s*11\s.*PUT /4 HTTP/1.1')
+ assert grep_output(stdout, r'^\s*15\s.*PUT /5 HTTP/1.1')
+
+ def test_tcp_out_of_order_onepass(self, cmd_tshark, dirs, test_env):
+ self.check_tcp_out_of_order(cmd_tshark, dirs, test_env)
+
+ def test_tcp_out_of_order_twopass(self, cmd_tshark, dirs, test_env):
+ self.check_tcp_out_of_order(cmd_tshark, dirs, test_env, extraArgs=['-2'])
+
+ def test_tcp_out_of_order_data_after_syn(self, cmd_tshark, capture_file, test_env):
+ '''Test when the first non-empty segment is OoO.'''
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('dns-ooo.pcap'),
+ '-otcp.reassemble_out_of_order:TRUE',
+ '-Y', 'dns', '-Tfields', '-edns.qry.name',
+ ), encoding='utf-8', env=test_env)
+ assert stdout.strip() == 'example.com'
+
+ def test_tcp_out_of_order_first_gap(self, cmd_tshark, capture_file, test_env):
+ '''
+ Test reporting of "reassembled_in" in the OoO frame that contains the
+ initial segment (Bug 15420). Additionally, test for proper reporting
+ when the initial segment is retransmitted.
+ For PDU H123 (where H is the HTTP Request header and 1, 2 and 3 are part
+ of the body), the order is: (SYN) 2 H H 1 3 H.
+ '''
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('http-ooo2.pcap'),
+ '-otcp.reassemble_out_of_order:TRUE',
+ '-Tfields',
+ '-eframe.number', '-etcp.reassembled_in', '-e_ws.col.info',
+ '-2',
+ ), encoding='utf-8', env=test_env)
+ lines = stdout.split('\n')
+ # 2 - start of OoO MSP
+ assert '2\t6\t[TCP Previous segment not captured]' in lines[1]
+ assert '[TCP segment of a reassembled PDU]' in lines[1]
+ # H - first time that the start of the MSP is delivered
+ assert '3\t6\t[TCP Out-Of-Order]' in lines[2]
+ assert '[TCP segment of a reassembled PDU]' in lines[2]
+ # H - first retransmission. Because this is before the reassembly
+ # completes we can add it to the reassembly
+ assert '4\t6\t[TCP Retransmission]' in lines[3]
+ assert '[TCP segment of a reassembled PDU]' in lines[3]
+ # 1 - continue reassembly
+ assert '5\t6\t[TCP Out-Of-Order]' in lines[4]
+ assert '[TCP segment of a reassembled PDU]' in lines[4]
+ # 3 - finish reassembly
+ assert '6\t\tPUT /0 HTTP/1.1' in lines[5]
+ # H - second retransmission. This is after the reassembly completes
+ # so we do not add it to the ressembly (but throw a ReassemblyError.)
+ assert '7\t\t' in lines[6]
+ assert '[TCP segment of a reassembled PDU]' not in lines[6]
+
+ def test_tcp_reassembly_more_data_1(self, cmd_tshark, capture_file, test_env):
+ '''
+ Tests that reassembly also works when a new packet begins at the same
+ sequence number as the initial segment. This models behavior with the
+ ZeroWindowProbe: the initial segment contains a single byte. The second
+ segment contains that byte, plus the remainder.
+ '''
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('retrans-tls.pcap'),
+ '-Ytls', '-Tfields', '-eframe.number', '-etls.record.length',),
+ encoding='utf-8', env=test_env)
+ # First pass dissection actually accepted the first frame as TLS, but
+ # subsequently requested reassembly.
+ assert stdout == '1\t\n2\t16\n'
+
+ def test_tcp_reassembly_more_data_2(self, cmd_tshark, capture_file, test_env):
+ '''
+ Like test_tcp_reassembly_more_data_1, but checks the second pass (-2).
+ '''
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('retrans-tls.pcap'),
+ '-Ytls', '-Tfields', '-eframe.number', '-etls.record.length', '-2'),
+ encoding='utf-8', env=test_env)
+ assert stdout == '2\t16\n'
+
+class TestDissectGit:
+ def test_git_prot(self, cmd_tshark, capture_file, features, test_env):
+ '''
+ Check for Git protocol version 2, flush and delimiter packets.
+ Ensure there are no malformed packets.
+ '''
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('gitOverTCP.pcap'),
+ '-Ygit', '-Tfields', '-egit.version', '-egit.packet_type',
+ '-zexpert', '-e_ws.expert',
+ ), encoding='utf-8', env=test_env)
+ # `epan/dissectors/packet-git.c` parses the Git Protocol version
+ # from ASCII '1' or '2' to integers 49 or 50 in grep output.
+ # 0x0000 are flush packets.
+ # 0x0001 are delimiter packets.
+ # Pre-existing git Malformed Packets in this pcap were addressed
+ # with the parsing of the delimiter packets. This test ensures
+ # pcap gitOverTCP's delim packets are parsed and that there are no
+ # malformed packets with "Expert Info/Errors" in the same pcap.
+ # Additional test cases for other scenarios, i.e actually malformed
+ # git packets, might be required.
+ assert stdout == '50\t\t\n\t0\t\n\t\t\n\t1,0\t\n'
+
+class TestDissectTls:
+ @staticmethod
+ def check_tls_handshake_reassembly(cmd_tshark, capture_file, test_env,
+ extraArgs=[]):
+ # Include -zexpert just to be sure that no exception has occurred. It
+ # is not strictly necessary as the extension to be matched is the last
+ # one in the handshake message.
+ stdout = subprocess.check_output([cmd_tshark,
+ '-r', capture_file('tls-fragmented-handshakes.pcap.gz'),
+ '-zexpert',
+ '-Ytls.handshake.extension.data',
+ '-Tfields', '-etls.handshake.extension.data'] + extraArgs,
+ encoding='utf-8', env=test_env)
+ stdout = stdout.replace(',', '\n')
+ # Expected output are lines with 0001, 0002, ..., 03e8
+ expected = ''.join('%04x\n' % i for i in range(1, 1001))
+ assert stdout == expected
+
+ @staticmethod
+ def check_tls_reassembly_over_tcp_reassembly(cmd_tshark, capture_file, test_env,
+ extraArgs=[]):
+ stdout = subprocess.check_output([cmd_tshark,
+ '-r', capture_file('tls-fragmented-over-tcp-segmented.pcapng.gz'),
+ '-zexpert,note',
+ '-Yhttp.host',
+ '-Tfields', '-ehttp.host'] + extraArgs,
+ encoding='utf-8', env=test_env)
+ stdout = stdout.replace(',', '\n')
+ assert stdout == 'reports.crashlytics.com\n'
+
+ def test_tls_handshake_reassembly(self, cmd_tshark, capture_file, test_env):
+ '''Verify that TCP and TLS handshake reassembly works.'''
+ self.check_tls_handshake_reassembly(cmd_tshark, capture_file, test_env)
+
+ def test_tls_handshake_reassembly_2(self, cmd_tshark, capture_file, test_env):
+ '''Verify that TCP and TLS handshake reassembly works (second pass).'''
+ self.check_tls_handshake_reassembly(
+ cmd_tshark, capture_file, test_env, extraArgs=['-2'])
+
+ def test_tls_reassembly_over_tcp_reassembly(self, cmd_tshark, capture_file, features, test_env):
+ '''Verify that TLS reassembly over TCP reassembly works.'''
+ if not features.have_gnutls:
+ pytest.skip('Requires GnuTLS.')
+ self.check_tls_reassembly_over_tcp_reassembly(cmd_tshark, capture_file, test_env)
+
+ def test_tls_reassembly_over_tcp_reassembly_2(self, cmd_tshark, capture_file, features, test_env):
+ '''Verify that TLS reassembly over TCP reassembly works (second pass).'''
+ # pinfo->curr_layer_num can be different on the second pass than the
+ # first pass, because the HTTP dissector isn't called for the first
+ # TLS record on the second pass.
+ if not features.have_gnutls:
+ pytest.skip('Requires GnuTLS.')
+ self.check_tls_reassembly_over_tcp_reassembly(cmd_tshark, capture_file,
+ test_env, extraArgs=['-2'])
+
+ @staticmethod
+ def check_tls_out_of_order(cmd_tshark, capture_file, test_env, extraArgs=[]):
+ stdout = subprocess.check_output([cmd_tshark,
+ '-r', capture_file('challenge01_ooo_stream.pcapng.gz'),
+ '-otcp.reassemble_out_of_order:TRUE',
+ '-q',
+ '-zhttp,stat,png or image-jfif',
+ ] + extraArgs, encoding='utf-8', env=test_env)
+ assert grep_output(stdout, r'200 OK\s*11')
+
+ def test_tls_out_of_order(self, cmd_tshark, capture_file, features, test_env):
+ '''Verify that TLS reassembly over TCP reassembly works.'''
+ if not features.have_gnutls:
+ pytest.skip('Requires GnuTLS.')
+ self.check_tls_out_of_order(cmd_tshark, capture_file, test_env)
+
+ def test_tls_out_of_order_second_pass(self, cmd_tshark, capture_file, features, test_env):
+ '''Verify that TLS reassembly over TCP reassembly works (second pass).'''
+ if not features.have_gnutls:
+ pytest.skip('Requires GnuTLS.')
+ self.check_tls_out_of_order(cmd_tshark, capture_file,
+ test_env, extraArgs=['-2'])
+
+class TestDissectQuic:
+ @staticmethod
+ def check_quic_tls_handshake_reassembly(cmd_tshark, capture_file, test_env,
+ extraArgs=[]):
+ # An assortment of QUIC carrying TLS handshakes that need to be
+ # reassembled, including fragmented in one packet, fragmented in
+ # multiple packets, fragmented in multiple out of order packets,
+ # retried, retried with overlap from the original packets, and retried
+ # with one of the original packets missing (but all data there.)
+ # Include -zexpert just to be sure that nothing Warn or higher occured.
+ # Note level expert infos may be expected with the overlaps and
+ # retransmissions.
+ stdout = subprocess.check_output([cmd_tshark,
+ '-r', capture_file('quic-fragmented-handshakes.pcapng.gz'),
+ '-zexpert,warn',
+ '-Ytls.handshake.type',
+ '-o', 'gui.column.format:"Handshake Type","%Cus:tls.handshake.type:0:R"',
+ ] + extraArgs,
+ encoding='utf-8', env=test_env)
+ assert count_output(stdout, 'Client Hello') == 18
+ assert count_output(stdout, 'Server Hello') == 2
+ assert count_output(stdout, 'Finished') == 2
+ assert count_output(stdout, 'New Session Ticket,New Session Ticket') == 1
+ assert count_output(stdout, 'Certificate') == 2
+ assert not grep_output(stdout, 'Warns')
+ assert not grep_output(stdout, 'Errors')
+
+ def test_quic_tls_handshake_reassembly(self, cmd_tshark, capture_file, test_env):
+ '''Verify that QUIC and TLS handshake reassembly works.'''
+ self.check_quic_tls_handshake_reassembly(cmd_tshark, capture_file, test_env)
+
+ def test_quic_tls_handshake_reassembly_2(self, cmd_tshark, capture_file, test_env):
+ '''Verify that QUIC and TLS handshake reassembly works (second pass).'''
+ self.check_quic_tls_handshake_reassembly(
+ cmd_tshark, capture_file, test_env, extraArgs=['-2'])
+
+class TestDecompressSmb2:
+ @staticmethod
+ def extract_compressed_payload(cmd_tshark, capture_file, test_env, frame_num):
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('smb311-lz77-lz77huff-lznt1.pcap.gz'),
+ '-Tfields', '-edata.data',
+ '-Y', 'frame.number == %d'%frame_num,
+ ), encoding='utf-8', env=test_env)
+ assert b'a'*4096 == bytes.fromhex(stdout.strip())
+
+ def test_smb311_read_lz77(self, cmd_tshark, capture_file, test_env):
+ self.extract_compressed_payload(cmd_tshark, capture_file, test_env, 1)
+
+ def test_smb311_read_lz77huff(self, cmd_tshark, capture_file, test_env):
+ self.extract_compressed_payload(cmd_tshark, capture_file, test_env, 2)
+
+ def test_smb311_read_lznt1(self, cmd_tshark, capture_file, test_env):
+ if sys.byteorder == 'big':
+ pytest.skip('this test is supported on little endian only')
+ self.extract_compressed_payload(cmd_tshark, capture_file, test_env, 3)
+
+ def extract_chained_compressed_payload(self, cmd_tshark, capture_file, test_env, frame_num):
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('smb311-chained-patternv1-lznt1.pcapng.gz'),
+ '-Tfields', '-edata.data',
+ '-Y', 'frame.number == %d'%frame_num,
+ ), encoding='utf-8', env=test_env)
+ assert b'\xaa'*256 == bytes.fromhex(stdout.strip())
+
+ def test_smb311_chained_lznt1_patternv1(self, cmd_tshark, capture_file, test_env):
+ if sys.byteorder == 'big':
+ pytest.skip('this test is supported on little endian only')
+ self.extract_chained_compressed_payload(cmd_tshark, capture_file, test_env, 1)
+
+ def test_smb311_chained_none_patternv1(self, cmd_tshark, capture_file, test_env):
+ self.extract_chained_compressed_payload(cmd_tshark, capture_file, test_env, 2)
+
+class TestDissectCommunityId:
+ @staticmethod
+ def check_baseline(dirs, output, baseline):
+ baseline_file = os.path.join(dirs.baseline_dir, baseline)
+ with open(baseline_file) as f:
+ baseline_data = f.read()
+
+ assert output == baseline_data
+
+ def test_communityid(self, cmd_tshark, features, dirs, capture_file, test_env):
+ # Run tshark on our Community ID test pcap, enabling the
+ # postdissector (it is disabled by default), and asking for
+ # the Community ID value as field output. Verify that this
+ # exits successfully:
+ stdout = subprocess.check_output(
+ (cmd_tshark,
+ '--enable-protocol', 'communityid',
+ '-r', capture_file('communityid.pcap.gz'),
+ '-Tfields', '-ecommunityid',
+ ), encoding='utf-8', env=test_env)
+
+ self.check_baseline(dirs, stdout, 'communityid.txt')
+
+ def test_communityid_filter(self, cmd_tshark, features, dirs, capture_file, test_env):
+ # Run tshark on our Community ID test pcap, enabling the
+ # postdissector and filtering the result.
+ stdout = subprocess.check_output(
+ (cmd_tshark,
+ '--enable-protocol', 'communityid',
+ '-r', capture_file('communityid.pcap.gz'),
+ '-Tfields', '-ecommunityid',
+ 'communityid=="1:d/FP5EW3wiY1vCndhwleRRKHowQ="'
+ ), encoding='utf-8', env=test_env)
+
+ self.check_baseline(dirs, stdout, 'communityid-filtered.txt')
+
+class TestDecompressMongo:
+ def test_decompress_zstd(self, cmd_tshark, capture_file, test_env):
+ stdout = subprocess.check_output((cmd_tshark,
+ '-d', 'tcp.port==27017,mongo',
+ '-r', capture_file('mongo-zstd.pcapng'),
+ '-Tfields', '-emongo.element.name'
+ ), encoding='utf-8', env=test_env)
+ # Check the element names of the decompressed body.
+ assert 'drop,lsid,id,$db' == stdout.strip()
diff --git a/test/suite_dissectors/dissectorstest.py b/test/suite_dissectors/dissectorstest.py
new file mode 100644
index 0000000..899df59
--- /dev/null
+++ b/test/suite_dissectors/dissectorstest.py
@@ -0,0 +1,116 @@
+#
+# Wireshark dissector tests
+# By Atli Guðmundsson <atli@tern.is>
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+import inspect
+import json
+import subprocess
+import pytest
+
+
+class _dissection_validator_real:
+ '''
+ Collects a set of byte bundles, matching json objects and a protocol
+ name and verifies that a byte bundle converts into the matching json
+ object using the following execution chain:
+
+ byte bundle -> text2pcap -> tshark <protocol> -> json
+
+ Note: The idea for this approach came about when it was realized that
+ calling text2pcap and tshark for each byte bundle resulted in
+ unacceptable overhead during execution of the unittests.
+ '''
+
+ def __init__(self, protocol, request, cmd_tshark, cmd_text2pcap, result_file, env):
+ self.dissection_list = []
+ self.protocol = protocol
+ self.cmd_tshark = cmd_tshark
+ self.cmd_text2pcap = cmd_text2pcap
+ self.test_case = request.instance
+ self.result_file = result_file
+ self.env = env
+
+ def add_dissection(self, byte_list, expected_result, line_no=None):
+ '''Adds a byte bundle and an expected result to the set of byte
+ bundles to verify.
+
+ byte bundles must be iterable.'''
+
+ hex_string = ' '.join('{:02x}'.format(ele) for ele in bytes(byte_list))
+
+ if line_no is None:
+ caller = inspect.getframeinfo(inspect.stack()[1][0])
+ line_no = caller.lineno
+
+ self.dissection_list.append((line_no, hex_string, expected_result))
+
+# Uncomment the following lines to record in a text file all the dissector byte
+# bundles, in the order they are presented:
+#
+# with open("full.txt", 'a') as f:
+# f.write("0 {}\n".format(hex_string))
+
+# Then use the following command to convert full.txt into a pcap file,
+# replacing <port> with the default port of your protocol:
+# # text2pcap -u <port>,<port> full.txt out.pcap
+
+ def check_dissections(self):
+ '''Processes and verifies all added byte bundles and their expected
+ results. At the end of processing the current set is emptied.'''
+
+ text_file = self.result_file('txt')
+ pcap_file = self.result_file('pcap')
+
+ # create our text file of hex encoded messages
+ with open(text_file, 'w') as f:
+ for line_no, hex_string, expected_result in self.dissection_list:
+ f.write("0 {}\n".format(hex_string))
+
+ # generate our pcap file by feeding the messages to text2pcap
+ subprocess.check_call((
+ self.cmd_text2pcap,
+ '-u', '1234,1234',
+ text_file, pcap_file
+ ), env=self.env)
+
+ # generate our dissection from our pcap file
+ tshark_stdout = subprocess.check_output((
+ self.cmd_tshark,
+ '-r', pcap_file,
+ '-T', 'json',
+ '-d', 'udp.port==1234,{}'.format(self.protocol),
+ '-J', self.protocol
+ ), encoding='utf-8', env=self.env)
+
+ dissections = json.loads(tshark_stdout)
+ for (line_no, hex_string, expected_result), dissection in zip(self.dissection_list, dissections):
+
+ # strip away everything except the protocol
+ result = dissection['_source']['layers']
+ assert self.protocol in result
+ result = result[self.protocol]
+
+ # verify that the dissection is as expected
+ assert expected_result == result, \
+ "expected != result, while dissecting [{}] from line {}.".format(hex_string, line_no)
+
+ # cleanup for next test
+ self.dissection_list = []
+
+
+@pytest.fixture
+def dissection_validator(request, cmd_tshark, cmd_text2pcap, result_file, test_env):
+
+ def generate_validator(protocol):
+ retval = _dissection_validator_real(
+ protocol,
+ request,
+ cmd_tshark,
+ cmd_text2pcap,
+ result_file,
+ test_env)
+ return retval
+
+ return generate_validator
diff --git a/test/suite_dissectors/group_asterix.py b/test/suite_dissectors/group_asterix.py
new file mode 100644
index 0000000..b5b1b4f
--- /dev/null
+++ b/test/suite_dissectors/group_asterix.py
@@ -0,0 +1,3875 @@
+#
+# Wireshark ASTERIX dissector tests
+# By Atli Guðmundsson <atli@tern.is>
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+'''ASTERIX dissector tests'''
+
+import inspect
+import pytest
+
+# Wireshark modules
+from suite_dissectors.dissectorstest import *
+
+
+class TestAsterix:
+
+ def test_for_asterix(self, dissection_validator):
+ '''Verifies that the asterix dissector is installed and accessible'''
+
+ tester = dissection_validator('asterix')
+ tester.add_dissection(
+ [0x13, 0x00, 0x03],
+ {
+ "asterix.category": "19",
+ "asterix.length": "3"
+ }
+ )
+ tester.check_dissections()
+
+
+class _asterix_validator_real:
+
+ def __init__(self, category, dissection_validator):
+ self.category = category
+ self.validator = dissection_validator("asterix")
+
+ def add_dissection(self, byte_list, field, expected_message, line_no=None):
+ '''pre-wrap asterix category messages with proper asterix structure'''
+
+ total_length = len(byte_list) + 3
+ byte_list = [
+ self.category,
+ (total_length // 256) % 256,
+ total_length % 256
+ ] + byte_list
+ expected_result = {
+ "asterix.category": "{}".format(self.category),
+ "asterix.length": "{}".format(total_length),
+ "asterix.message":
+ {
+ "asterix.fspec": "",
+ field: expected_message
+ }
+ }
+ if line_no is None:
+ caller = inspect.getframeinfo(inspect.stack()[1][0])
+ line_no = caller.lineno
+ self.validator.add_dissection(byte_list, expected_result, line_no)
+
+ def check_dissections(self):
+ self.validator.check_dissections()
+
+
+@pytest.fixture
+def asterix_validator(dissection_validator):
+
+ def generate_asterix_validator(category):
+ retval = _asterix_validator_real(category, dissection_validator)
+ return retval
+
+ return generate_asterix_validator
+
+
+class _asterix_re_validator_real(_asterix_validator_real):
+
+ def __init__(self, category, re_byte_list, dissection_validator):
+ super().__init__(category, dissection_validator)
+ self.re_byte_list = re_byte_list
+
+ def add_re_dissection(self, byte_list, field, expected_message, line_no=None):
+ '''pre-wrap asterix RE messages with proper asterix RE structure'''
+
+ re_length = len(byte_list) + 1
+ byte_list = self.re_byte_list + [
+ re_length % 256
+ ] + byte_list
+ expected_result = {
+ "asterix.re_field_len": "{}".format(re_length),
+ "asterix.fspec": "",
+ "asterix.{:03}_RE_{}".format(self.category, field): expected_message
+ }
+ if line_no is None:
+ caller = inspect.getframeinfo(inspect.stack()[1][0])
+ line_no = caller.lineno
+ self.add_dissection(byte_list, "asterix.{:03}_RE".format(
+ self.category), expected_result, line_no)
+
+
+@pytest.fixture
+def asterix_re_validator(dissection_validator):
+
+ def generate_re_asterix_validator(category, re_byte_list):
+ retval = _asterix_re_validator_real(
+ category, re_byte_list, dissection_validator)
+ return retval
+
+ return generate_re_asterix_validator
+
+
+def fspec_local(key, idx, value):
+ result = {
+ "asterix.fspec": "",
+ "asterix.{}".format(key):
+ {
+ "asterix.{}_{}".format(key, idx): value
+ }
+ }
+ return result
+
+
+def fspec_global(key, idx, value):
+ result = {
+ "asterix.fspec": "",
+ "asterix.{}".format(key):
+ {
+ "asterix.{}".format(idx): value
+ }
+ }
+ return result
+
+
+def dict_local(vmap, key, idx, value):
+ result = vmap.copy()
+ result["asterix.{}_{}".format(key, idx)] = value
+ return result
+
+
+def dict_global(vmap, key, value):
+ result = vmap.copy()
+ result["asterix.{}".format(key)] = value
+ return result
+
+
+def dict_fspec_local(vmap, key, idx, value):
+ result = {
+ "asterix.fspec": "",
+ "asterix.{}".format(key): dict_local(vmap, key, idx, value)
+ }
+ return result
+
+
+def dict_fspec_global(vmap, key, idx, value):
+ result = {
+ "asterix.fspec": "",
+ "asterix.{}".format(key): dict_global(vmap, idx, value)
+ }
+ return result
+
+
+def counter_local(vmap, counter, key, idx, value):
+ result = {
+ "asterix.fspec": "",
+ "asterix.{}".format(key):
+ {
+ "asterix.counter": counter,
+ "asterix.{}".format(key): dict_local(vmap, key, idx, value)
+ }
+ }
+ return result
+
+
+class TestCategory019:
+ '''
+ Unittest case for ASTERIX Category 019
+
+ Online specification:
+ https://www.eurocontrol.int/publications/cat019-multilateration-system-status-messages-part-18
+
+ Part 18 : Category 019 (1.3)
+ Multilateration System
+ Status Messages
+
+ Standard User Application Profile
+
+ FRN Data Item Information Length
+ 1 I019/010 Data Source Identifier 2
+ 2 I019/000 Message Type 1
+ 3 I019/140 Time of Day 3
+ 4 I019/550 System Status 1
+ 5 I019/551 Tracking Processor Detailed Status 1
+ 6 I019/552 Remote Sensor Detailed Status 1+
+ 7 I019/553 Reference Transponder Detailed Status 1+
+ FX - Field Extension Indicator -
+ 8 I019/600 Position of the MLT System Reference point 8
+ 9 I019/610 Height of the MLT System Reference point 2
+ 10 I019/620 WGS-84 Undulation 1
+ 11 - Spare -
+ 12 - Spare -
+ 13 RE Reserved Expansion Field -
+ 14 SP Special Purpose Field -
+ FX - Field Extension Indicator -
+ '''
+
+ def test_for_fields(self, asterix_validator):
+ '''verifies existence of all fields and their maximum value'''
+
+ validator = asterix_validator(19)
+
+ validator.add_dissection(
+ [0x80, 0xff, 0x00],
+ "asterix.019_010",
+ {
+ "asterix.019_010_SAC": "0xff",
+ "asterix.019_010_SIC": "0x00"
+ }
+ )
+ validator.add_dissection(
+ [0x80, 0x00, 0xff],
+ "asterix.019_010",
+ {
+ "asterix.019_010_SAC": "0x00",
+ "asterix.019_010_SIC": "0xff"
+ }
+ )
+ validator.add_dissection(
+ [0x40, 0x03],
+ "asterix.019_000",
+ {
+ "asterix.019_000_VALUE": "3"
+ }
+ )
+ validator.add_dissection(
+ [0x20, 0xa8, 0xbf, 0xff],
+ "asterix.019_140",
+ {
+ "asterix.019_140_VALUE": "86399.9921875"
+ }
+ )
+ validator.add_dissection(
+ [0x10, 0xc0],
+ "asterix.019_550",
+ {
+ "asterix.019_550_NOGO": "3",
+ "asterix.019_550_OVL": "0",
+ "asterix.019_550_TSV": "0",
+ "asterix.019_550_TTF": "0"
+ }
+ )
+ validator.add_dissection(
+ [0x10, 0x20],
+ "asterix.019_550",
+ {
+ "asterix.019_550_NOGO": "0",
+ "asterix.019_550_OVL": "1",
+ "asterix.019_550_TSV": "0",
+ "asterix.019_550_TTF": "0"
+ }
+ )
+ validator.add_dissection(
+ [0x10, 0x10],
+ "asterix.019_550",
+ {
+ "asterix.019_550_NOGO": "0",
+ "asterix.019_550_OVL": "0",
+ "asterix.019_550_TSV": "1",
+ "asterix.019_550_TTF": "0"
+ }
+ )
+ validator.add_dissection(
+ [0x10, 0x08],
+ "asterix.019_550",
+ {
+ "asterix.019_550_NOGO": "0",
+ "asterix.019_550_OVL": "0",
+ "asterix.019_550_TSV": "0",
+ "asterix.019_550_TTF": "1"
+ }
+ )
+ validator.add_dissection(
+ [0x08, 0x80],
+ "asterix.019_551",
+ {
+ "asterix.019_551_TP1A": "1",
+ "asterix.019_551_TP1B": "0",
+ "asterix.019_551_TP2A": "0",
+ "asterix.019_551_TP2B": "0",
+ "asterix.019_551_TP3A": "0",
+ "asterix.019_551_TP3B": "0",
+ "asterix.019_551_TP4A": "0",
+ "asterix.019_551_TP4B": "0"
+ }
+ )
+ validator.add_dissection(
+ [0x08, 0x40],
+ "asterix.019_551",
+ {
+ "asterix.019_551_TP1A": "0",
+ "asterix.019_551_TP1B": "1",
+ "asterix.019_551_TP2A": "0",
+ "asterix.019_551_TP2B": "0",
+ "asterix.019_551_TP3A": "0",
+ "asterix.019_551_TP3B": "0",
+ "asterix.019_551_TP4A": "0",
+ "asterix.019_551_TP4B": "0"
+ }
+ )
+ validator.add_dissection(
+ [0x08, 0x20],
+ "asterix.019_551",
+ {
+ "asterix.019_551_TP1A": "0",
+ "asterix.019_551_TP1B": "0",
+ "asterix.019_551_TP2A": "1",
+ "asterix.019_551_TP2B": "0",
+ "asterix.019_551_TP3A": "0",
+ "asterix.019_551_TP3B": "0",
+ "asterix.019_551_TP4A": "0",
+ "asterix.019_551_TP4B": "0"
+ }
+ )
+ validator.add_dissection(
+ [0x08, 0x10],
+ "asterix.019_551",
+ {
+ "asterix.019_551_TP1A": "0",
+ "asterix.019_551_TP1B": "0",
+ "asterix.019_551_TP2A": "0",
+ "asterix.019_551_TP2B": "1",
+ "asterix.019_551_TP3A": "0",
+ "asterix.019_551_TP3B": "0",
+ "asterix.019_551_TP4A": "0",
+ "asterix.019_551_TP4B": "0"
+ }
+ )
+ validator.add_dissection(
+ [0x08, 0x08],
+ "asterix.019_551",
+ {
+ "asterix.019_551_TP1A": "0",
+ "asterix.019_551_TP1B": "0",
+ "asterix.019_551_TP2A": "0",
+ "asterix.019_551_TP2B": "0",
+ "asterix.019_551_TP3A": "1",
+ "asterix.019_551_TP3B": "0",
+ "asterix.019_551_TP4A": "0",
+ "asterix.019_551_TP4B": "0"
+ }
+ )
+ validator.add_dissection(
+ [0x08, 0x04],
+ "asterix.019_551",
+ {
+ "asterix.019_551_TP1A": "0",
+ "asterix.019_551_TP1B": "0",
+ "asterix.019_551_TP2A": "0",
+ "asterix.019_551_TP2B": "0",
+ "asterix.019_551_TP3A": "0",
+ "asterix.019_551_TP3B": "1",
+ "asterix.019_551_TP4A": "0",
+ "asterix.019_551_TP4B": "0"
+ }
+ )
+ validator.add_dissection(
+ [0x08, 0x02],
+ "asterix.019_551",
+ {
+ "asterix.019_551_TP1A": "0",
+ "asterix.019_551_TP1B": "0",
+ "asterix.019_551_TP2A": "0",
+ "asterix.019_551_TP2B": "0",
+ "asterix.019_551_TP3A": "0",
+ "asterix.019_551_TP3B": "0",
+ "asterix.019_551_TP4A": "1",
+ "asterix.019_551_TP4B": "0"
+ }
+ )
+ validator.add_dissection(
+ [0x08, 0x01],
+ "asterix.019_551",
+ {
+ "asterix.019_551_TP1A": "0",
+ "asterix.019_551_TP1B": "0",
+ "asterix.019_551_TP2A": "0",
+ "asterix.019_551_TP2B": "0",
+ "asterix.019_551_TP3A": "0",
+ "asterix.019_551_TP3B": "0",
+ "asterix.019_551_TP4A": "0",
+ "asterix.019_551_TP4B": "1"
+ }
+ )
+ validator.add_dissection(
+ [0x04, 0x00],
+ "asterix.019_552",
+ {
+ "asterix.counter": "0"
+ }
+ )
+ validator.add_dissection(
+ [0x04, 0x01, 0xff, 0x00],
+ "asterix.019_552",
+ {
+ "asterix.counter": "1",
+ "asterix.019_552":
+ {
+ "asterix.019_552_RSI": "0xff",
+ "asterix.019_552_RS1090": "0",
+ "asterix.019_552_TX1030": "0",
+ "asterix.019_552_TX1090": "0",
+ "asterix.019_552_RSS": "0",
+ "asterix.019_552_RSO": "0"
+ }
+ }
+ )
+ validator.add_dissection(
+ [0x04, 0x01, 0x00, 0x40],
+ "asterix.019_552",
+ {
+ "asterix.counter": "1",
+ "asterix.019_552":
+ {
+ "asterix.019_552_RSI": "0x00",
+ "asterix.019_552_RS1090": "1",
+ "asterix.019_552_TX1030": "0",
+ "asterix.019_552_TX1090": "0",
+ "asterix.019_552_RSS": "0",
+ "asterix.019_552_RSO": "0"
+ }
+ }
+ )
+ validator.add_dissection(
+ [0x04, 0x01, 0x00, 0x20],
+ "asterix.019_552",
+ {
+ "asterix.counter": "1",
+ "asterix.019_552":
+ {
+ "asterix.019_552_RSI": "0x00",
+ "asterix.019_552_RS1090": "0",
+ "asterix.019_552_TX1030": "1",
+ "asterix.019_552_TX1090": "0",
+ "asterix.019_552_RSS": "0",
+ "asterix.019_552_RSO": "0"
+ }
+ }
+ )
+ validator.add_dissection(
+ [0x04, 0x01, 0x00, 0x10],
+ "asterix.019_552",
+ {
+ "asterix.counter": "1",
+ "asterix.019_552":
+ {
+ "asterix.019_552_RSI": "0x00",
+ "asterix.019_552_RS1090": "0",
+ "asterix.019_552_TX1030": "0",
+ "asterix.019_552_TX1090": "1",
+ "asterix.019_552_RSS": "0",
+ "asterix.019_552_RSO": "0"
+ }
+ }
+ )
+ validator.add_dissection(
+ [0x04, 0x01, 0x00, 0x08],
+ "asterix.019_552",
+ {
+ "asterix.counter": "1",
+ "asterix.019_552":
+ {
+ "asterix.019_552_RSI": "0x00",
+ "asterix.019_552_RS1090": "0",
+ "asterix.019_552_TX1030": "0",
+ "asterix.019_552_TX1090": "0",
+ "asterix.019_552_RSS": "1",
+ "asterix.019_552_RSO": "0"
+ }
+ }
+ )
+ validator.add_dissection(
+ [0x04, 0x01, 0x00, 0x04],
+ "asterix.019_552",
+ {
+ "asterix.counter": "1",
+ "asterix.019_552":
+ {
+ "asterix.019_552_RSI": "0x00",
+ "asterix.019_552_RS1090": "0",
+ "asterix.019_552_TX1030": "0",
+ "asterix.019_552_TX1090": "0",
+ "asterix.019_552_RSS": "0",
+ "asterix.019_552_RSO": "1"
+ }
+ }
+ )
+ validator.add_dissection(
+ [0x04, 0x03, 0x12, 0x34, 0x56, 0x78, 0x9a, 0x0c],
+ "asterix.019_552",
+ {
+ "asterix.counter": "3",
+ "asterix.019_552":
+ {
+ "asterix.019_552_RSI": "18",
+ "asterix.019_552_RS1090": "0",
+ "asterix.019_552_TX1030": "1",
+ "asterix.019_552_TX1090": "1",
+ "asterix.019_552_RSS": "0",
+ "asterix.019_552_RSO": "1"
+ },
+ "asterix.019_552":
+ {
+ "asterix.019_552_RSI": "86",
+ "asterix.019_552_RS1090": "1",
+ "asterix.019_552_TX1030": "1",
+ "asterix.019_552_TX1090": "1",
+ "asterix.019_552_RSS": "1",
+ "asterix.019_552_RSO": "0"
+ },
+ "asterix.019_552":
+ {
+ "asterix.019_552_RSI": "0x9a",
+ "asterix.019_552_RS1090": "0",
+ "asterix.019_552_TX1030": "0",
+ "asterix.019_552_TX1090": "0",
+ "asterix.019_552_RSS": "1",
+ "asterix.019_552_RSO": "1"
+ }
+ }
+ )
+ validator.add_dissection(
+ [0x02, 0xc0],
+ "asterix.019_553",
+ {
+ "asterix.019_553_REFTR1": "3",
+ "asterix.019_553_REFTR2": "0",
+ "asterix.FX": "0"
+ }
+ )
+ validator.add_dissection(
+ [0x02, 0x0c],
+ "asterix.019_553",
+ {
+ "asterix.019_553_REFTR1": "0",
+ "asterix.019_553_REFTR2": "3",
+ "asterix.FX": "0"
+ }
+ )
+ '''TODO: check this testcase, it has too many subitems
+ validator.add_dissection(
+ [0x02, 0x01, 0x01, 0x0c],
+ "asterix.019_553",
+ {
+ "asterix.019_553_Ref_Trans_1_Status": "0",
+ "asterix.019_553_Ref_Trans_2_Status": "0",
+ "asterix.019_553_Ref_Trans_3_Status": "0",
+ "asterix.019_553_Ref_Trans_4_Status": "0",
+ "asterix.019_553_Ref_Trans_5_Status": "0",
+ "asterix.019_553_Ref_Trans_6_Status": "3",
+ "asterix.FX": "0"
+ }
+ )
+ '''
+ validator.add_dissection(
+ [0x01, 0x80, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
+ "asterix.019_600",
+ {
+ "asterix.019_600_LAT": "90",
+ "asterix.019_600_LON": "0"
+ }
+ )
+ validator.add_dissection(
+ [0x01, 0x80, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
+ "asterix.019_600",
+ {
+ "asterix.019_600_LAT": "-90",
+ "asterix.019_600_LON": "0"
+ }
+ )
+ validator.add_dissection(
+ [0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00],
+ "asterix.019_600",
+ {
+ "asterix.019_600_LAT": "0",
+ "asterix.019_600_LON": "180"
+ }
+ )
+ validator.add_dissection(
+ [0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00],
+ "asterix.019_600",
+ {
+ "asterix.019_600_LAT": "0",
+ "asterix.019_600_LON": "-180"
+ }
+ )
+ validator.add_dissection(
+ [0x01, 0x40, 0x7f, 0xff],
+ "asterix.019_610",
+ {
+ "asterix.019_610_VALUE": "8191.75"
+ }
+ )
+ validator.add_dissection(
+ [0x01, 0x40, 0x80, 0x00],
+ "asterix.019_610",
+ {
+ "asterix.019_610_VALUE": "-8192"
+ }
+ )
+ validator.add_dissection(
+ [0x01, 0x20, 0x7f],
+ "asterix.019_620",
+ {
+ "asterix.019_620_VALUE": "127"
+ }
+ )
+ validator.add_dissection(
+ [0x01, 0x20, 0x81],
+ "asterix.019_620",
+ {
+ "asterix.019_620_VALUE": "-127"
+ }
+ )
+
+ validator.check_dissections()
+
+ def test_undefined_value_handling(self, asterix_validator):
+ '''verifies that the dissector can dissect undefined field values by setting
+ the maximum value of bits or by setting all undefined bits'''
+
+ validator = asterix_validator(19)
+
+ validator.add_dissection(
+ [0x40, 0xff],
+ "asterix.019_000",
+ {
+ "asterix.019_000_VALUE": "255"
+ }
+ )
+ validator.add_dissection(
+ [0x20, 0xff, 0xff, 0xff],
+ "asterix.019_140",
+ {
+ "asterix.019_140_VALUE": "131071.9921875"
+ }
+ )
+ validator.add_dissection(
+ [0x10, 0x07],
+ "asterix.019_550",
+ {
+ "asterix.019_550_NOGO": "0",
+ "asterix.019_550_OVL": "0",
+ "asterix.019_550_TSV": "0",
+ "asterix.019_550_TTF": "0"
+ }
+ )
+ validator.add_dissection(
+ [0x04, 0x01, 0x00, 0x83],
+ "asterix.019_552",
+ {
+ "asterix.counter": "1",
+ "asterix.019_552":
+ {
+ "asterix.019_552_RSI": "0x00",
+ "asterix.019_552_RS1090": "0",
+ "asterix.019_552_TX1030": "0",
+ "asterix.019_552_TX1090": "0",
+ "asterix.019_552_RSS": "0",
+ "asterix.019_552_RSO": "0"
+ }
+ }
+ )
+ validator.add_dissection(
+ [0x02, 0x32],
+ "asterix.019_553",
+ {
+ "asterix.019_553_REFTR1": "0",
+ "asterix.019_553_REFTR2": "0",
+ "asterix.FX": "0"
+ }
+ )
+ '''TODO: check this testcase, it has too many subitems
+ validator.add_dissection(
+ [0x02, 0x33, 0x33, 0x32],
+ "asterix.019_553",
+ {
+ "asterix.019_553_REFTR1": "0",
+ "asterix.019_553_REFTR2": "0",
+ "asterix.019_553_REFTR3": "0",
+ "asterix.019_553_REFTR4": "0",
+ "asterix.019_553_Ref_Trans_5_Status": "0",
+ "asterix.019_553_Ref_Trans_6_Status": "0",
+ "asterix.FX": "0"
+ }
+ )
+ '''
+ validator.add_dissection(
+ [0x01, 0x80, 0x7f, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00],
+ "asterix.019_600",
+ {
+ "asterix.019_600_LAT": "359.999999832362",
+ "asterix.019_600_LON": "0"
+ }
+ )
+ validator.add_dissection(
+ [0x01, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
+ "asterix.019_600",
+ {
+ "asterix.019_600_LAT": "-360",
+ "asterix.019_600_LON": "0"
+ }
+ )
+ validator.add_dissection(
+ [0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff, 0xff],
+ "asterix.019_600",
+ {
+ "asterix.019_600_LAT": "0",
+ "asterix.019_600_LON": "359.999999832362"
+ }
+ )
+ validator.add_dissection(
+ [0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00],
+ "asterix.019_600",
+ {
+ "asterix.019_600_LAT": "0",
+ "asterix.019_600_LON": "-360"
+ }
+ )
+ validator.add_dissection(
+ [0x01, 0x20, 0x80],
+ "asterix.019_620",
+ {
+ "asterix.019_620_VALUE": "-128"
+ }
+ )
+ validator.add_dissection(
+ [0x01, 0x10],
+ "asterix.spare",
+ ""
+ )
+ validator.add_dissection(
+ [0x01, 0x08],
+ "asterix.spare",
+ ""
+ )
+ '''TODO: re-enable RE and SP tests when implemented
+ validator.add_dissection(
+ [0x01, 0x04, 0x02, 0x00],
+ "asterix.019_RE",
+ {
+ "asterix.re_field_len": "2",
+ "asterix.fspec": ""
+ }
+ )
+ validator.add_dissection(
+ [0x01, 0x04, 0x10, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
+ 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff],
+ "asterix.019_RE",
+ {
+ "asterix.fspec": "",
+ "asterix.re_field_len": "16"
+ }
+ )
+ validator.add_dissection(
+ [0x01, 0x02, 0x01],
+ "asterix.019_SP",
+ ""
+ )
+ validator.add_dissection(
+ [0x01, 0x02, 0x10, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
+ 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff],
+ "asterix.019_SP",
+ ""
+ )
+ '''
+
+ validator.check_dissections()
+
+
+class TestCategory034:
+ '''
+ Unittest case for ASTERIX Category 034
+
+ Online specification:
+ https://www.eurocontrol.int/publications/cat034-monoradar-service-messages-part-2b-next-version-cat-002
+
+ Part 2b
+ Transmission of Monoradar Service Messages
+
+ Standard User Application Profile
+
+ FRN Data Item Information Length
+ 1 I034/010 Data Source Identifier 2
+ 2 I034/000 Message Type 1
+ 3 I034/030 Time-of-Day 3
+ 4 I034/020 Sector Number 1
+ 5 I034/041 Antenna Rotation Period 2
+ 6 I034/050 System Configuration and Status 1+
+ 7 I034/060 System Processing Mode 1+
+ FX N/A. Field Extension Indicator N/A.
+ 8 I034/070 Message Count Values (1+2*N)
+ 9 I034/100 Generic Polar Window 8
+ 10 I034/110 Data Filter 1
+ 11 I034/120 3D-Position of Data Source 8
+ 12 I034/090 Collimation Error 2
+ 13 RE-Data Item Reserved Expansion Field 1+1+
+ 14 SP-Data Item Special Purpose Field 1+1+
+ FX N/A. Field Extension Indicator n.a.
+ '''
+
+ def test_for_fields(self, asterix_validator):
+ '''verifies existence of all fields and their maximum value'''
+
+ validator = asterix_validator(34)
+
+ validator.add_dissection(
+ [0x80, 0xff, 0x00],
+ "asterix.034_010",
+ {
+ "asterix.034_010_SAC": "0xff",
+ "asterix.034_010_SIC": "0x00"
+ }
+ )
+ validator.add_dissection(
+ [0x80, 0x00, 0xff],
+ "asterix.034_010",
+ {
+ "asterix.034_010_SAC": "0x00",
+ "asterix.034_010_SIC": "0xff"
+ }
+ )
+ validator.add_dissection(
+ [0x40, 0x04],
+ "asterix.034_000",
+ {
+ "asterix.034_000_VALUE": "4"
+ }
+ )
+ validator.add_dissection(
+ [0x20, 0xa8, 0xbf, 0xff],
+ "asterix.034_030",
+ {
+ "asterix.034_030_VALUE": "86399.9921875"
+ }
+ )
+ validator.add_dissection(
+ [0x10, 0xff],
+ "asterix.034_020",
+ {
+ "asterix.034_020_VALUE": "358.59375"
+ }
+ )
+ validator.add_dissection(
+ [0x08, 0xff, 0xff],
+ "asterix.034_041",
+ {
+ "asterix.034_041_VALUE": "511.9921875"
+ }
+ )
+ x_050_COM = {
+ "asterix.034_050_COM_NOGO": "0",
+ "asterix.034_050_COM_RDPC": "0",
+ "asterix.034_050_COM_RDPR": "0",
+ "asterix.034_050_COM_OVLRDP": "0",
+ "asterix.034_050_COM_OVLXMT": "0",
+ "asterix.034_050_COM_MSC": "0",
+ "asterix.034_050_COM_TSV": "0"
+ }
+ validator.add_dissection(
+ [0x04, 0x80, 0x80],
+ "asterix.034_050",
+ dict_fspec_local(x_050_COM, "034_050_COM", "NOGO", "1")
+ )
+ validator.add_dissection(
+ [0x04, 0x80, 0x40],
+ "asterix.034_050",
+ dict_fspec_local(x_050_COM, "034_050_COM", "RDPC", "1")
+ )
+ validator.add_dissection(
+ [0x04, 0x80, 0x20],
+ "asterix.034_050",
+ dict_fspec_local(x_050_COM, "034_050_COM", "RDPR", "1")
+ )
+ validator.add_dissection(
+ [0x04, 0x80, 0x10],
+ "asterix.034_050",
+ dict_fspec_local(x_050_COM, "034_050_COM", "OVLRDP", "1")
+ )
+ validator.add_dissection(
+ [0x04, 0x80, 0x08],
+ "asterix.034_050",
+ dict_fspec_local(x_050_COM, "034_050_COM", "OVLXMT", "1")
+ )
+ validator.add_dissection(
+ [0x04, 0x80, 0x04],
+ "asterix.034_050",
+ dict_fspec_local(x_050_COM, "034_050_COM", "MSC", "1")
+ )
+ validator.add_dissection(
+ [0x04, 0x80, 0x02],
+ "asterix.034_050",
+ dict_fspec_local(x_050_COM, "034_050_COM", "TSV", "1")
+ )
+ x_050_PSR = {
+ "asterix.034_050_PSR_ANT": "0",
+ "asterix.034_050_PSR_CHAB": "0",
+ "asterix.034_050_PSR_OVL": "0",
+ "asterix.034_050_PSR_MSC": "0"
+ }
+ validator.add_dissection(
+ [0x04, 0x10, 0x80],
+ "asterix.034_050",
+ dict_fspec_local(x_050_PSR, "034_050_PSR", "ANT", "1")
+ )
+ validator.add_dissection(
+ [0x04, 0x10, 0x60],
+ "asterix.034_050",
+ dict_fspec_local(x_050_PSR, "034_050_PSR", "CHAB", "3")
+ )
+ validator.add_dissection(
+ [0x04, 0x10, 0x10],
+ "asterix.034_050",
+ dict_fspec_local(x_050_PSR, "034_050_PSR", "OVL", "1")
+ )
+ validator.add_dissection(
+ [0x04, 0x10, 0x08],
+ "asterix.034_050",
+ dict_fspec_local(x_050_PSR, "034_050_PSR", "MSC", "1")
+ )
+ x_050_SSR = {
+ "asterix.034_050_SSR_ANT": "0",
+ "asterix.034_050_SSR_CHAB": "0",
+ "asterix.034_050_SSR_OVL": "0",
+ "asterix.034_050_SSR_MSC": "0"
+ }
+ validator.add_dissection(
+ [0x04, 0x08, 0x80],
+ "asterix.034_050",
+ dict_fspec_local(x_050_SSR, "034_050_SSR", "ANT", "1")
+ )
+ validator.add_dissection(
+ [0x04, 0x08, 0x60],
+ "asterix.034_050",
+ dict_fspec_local(x_050_SSR, "034_050_SSR", "CHAB", "3")
+ )
+ validator.add_dissection(
+ [0x04, 0x08, 0x10],
+ "asterix.034_050",
+ dict_fspec_local(x_050_SSR, "034_050_SSR", "OVL", "1")
+ )
+ validator.add_dissection(
+ [0x04, 0x08, 0x08],
+ "asterix.034_050",
+ dict_fspec_local(x_050_SSR, "034_050_SSR", "MSC", "1")
+ )
+ x_050_MDS = {
+ "asterix.034_050_MDS_ANT": "0",
+ "asterix.034_050_MDS_CHAB": "0",
+ "asterix.034_050_MDS_OVLSUR": "0",
+ "asterix.034_050_MDS_MSC": "0",
+ "asterix.034_050_MDS_SCF": "0",
+ "asterix.034_050_MDS_DLF": "0",
+ "asterix.034_050_MDS_OVLSCF": "0",
+ "asterix.034_050_MDS_OVLDLF": "0"
+ }
+ validator.add_dissection(
+ [0x04, 0x04, 0x80, 0x00],
+ "asterix.034_050",
+ dict_fspec_local(x_050_MDS, "034_050_MDS", "ANT", "1")
+ )
+ validator.add_dissection(
+ [0x04, 0x04, 0x60, 0x00],
+ "asterix.034_050",
+ dict_fspec_local(x_050_MDS, "034_050_MDS", "CHAB", "3")
+ )
+ validator.add_dissection(
+ [0x04, 0x04, 0x10, 0x00],
+ "asterix.034_050",
+ dict_fspec_local(x_050_MDS, "034_050_MDS", "OVLSUR", "1")
+ )
+ validator.add_dissection(
+ [0x04, 0x04, 0x08, 0x00],
+ "asterix.034_050",
+ dict_fspec_local(x_050_MDS, "034_050_MDS", "MSC", "1")
+ )
+ validator.add_dissection(
+ [0x04, 0x04, 0x04, 0x00],
+ "asterix.034_050",
+ dict_fspec_local(x_050_MDS, "034_050_MDS", "SCF", "1")
+ )
+ validator.add_dissection(
+ [0x04, 0x04, 0x02, 0x00],
+ "asterix.034_050",
+ dict_fspec_local(x_050_MDS, "034_050_MDS", "DLF", "1")
+ )
+ validator.add_dissection(
+ [0x04, 0x04, 0x01, 0x00],
+ "asterix.034_050",
+ dict_fspec_local(x_050_MDS, "034_050_MDS", "OVLSCF", "1")
+ )
+ validator.add_dissection(
+ [0x04, 0x04, 0x00, 0x80],
+ "asterix.034_050",
+ dict_fspec_local(x_050_MDS, "034_050_MDS", "OVLDLF", "1")
+ )
+ x_060_COM = {
+ "asterix.034_060_COM_REDRDP": "0",
+ "asterix.034_060_COM_REDXMT": "0"
+ }
+ validator.add_dissection(
+ [0x02, 0x80, 0x70],
+ "asterix.034_060",
+ dict_fspec_local(x_060_COM, "034_060_COM", "REDRDP", "7")
+ )
+ validator.add_dissection(
+ [0x02, 0x80, 0x0e],
+ "asterix.034_060",
+ dict_fspec_local(x_060_COM, "034_060_COM", "REDXMT", "7")
+ )
+ x_060_PSR = {
+ "asterix.034_060_PSR_POL": "0",
+ "asterix.034_060_PSR_REDRAD": "0",
+ "asterix.034_060_PSR_STC": "0"
+ }
+ validator.add_dissection(
+ [0x02, 0x10, 0x80],
+ "asterix.034_060",
+ dict_fspec_local(x_060_PSR, "034_060_PSR", "POL", "1")
+ )
+ validator.add_dissection(
+ [0x02, 0x10, 0x70],
+ "asterix.034_060",
+ dict_fspec_local(x_060_PSR, "034_060_PSR", "REDRAD", "7")
+ )
+ validator.add_dissection(
+ [0x02, 0x10, 0x0c],
+ "asterix.034_060",
+ dict_fspec_local(x_060_PSR, "034_060_PSR", "STC", "3")
+ )
+ validator.add_dissection(
+ [0x02, 0x08, 0xe0],
+ "asterix.034_060",
+ fspec_local("034_060_SSR", "REDRAD", "7")
+ )
+ x_060_06 = {
+ "asterix.034_060_MDS_REDRAD": "0",
+ "asterix.034_060_MDS_CLU": "0"
+ }
+ validator.add_dissection(
+ [0x02, 0x04, 0xe0],
+ "asterix.034_060",
+ dict_fspec_local(x_060_06, "034_060_MDS", "REDRAD", "7")
+ )
+ validator.add_dissection(
+ [0x02, 0x04, 0x10],
+ "asterix.034_060",
+ dict_fspec_local(x_060_06, "034_060_MDS", "CLU", "1")
+ )
+ x_070 = {
+ "asterix.034_070_TYP": "0",
+ "asterix.034_070_COUNT": "0"
+ }
+ validator.add_dissection(
+ [0x01, 0x80, 0x01, 0x80, 0x00],
+ "asterix.034_070",
+ {
+ "asterix.counter": "1",
+ "asterix.034_070":
+ dict_local(x_070, "034_070", "TYP", "16")
+ }
+ )
+ validator.add_dissection(
+ [0x01, 0x80, 0x03, 0x80, 0x00, 0x87, 0xff, 0x07, 0xff],
+ "asterix.034_070",
+ {
+ "asterix.counter": "3",
+ "asterix.034_070":
+ dict_local(x_070, "034_070", "TYPE", "16"),
+ "asterix.034_070":
+ {
+ "asterix.034_070_TYP": "16",
+ "asterix.034_070_COUNT": "2047"
+ },
+ "asterix.034_070":
+ dict_local(x_070, "034_070", "COUNT", "2047"),
+ }
+ )
+ x_100 = {
+ "asterix.034_100_RHOST": "0",
+ "asterix.034_100_RHOEND": "0",
+ "asterix.034_100_THETAST": "0",
+ "asterix.034_100_THETAEND": "0"
+ }
+ validator.add_dissection(
+ [0x01, 0x40, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
+ "asterix.034_100",
+ dict_local(x_100, "034_100", "RHOST", "255.99609375")
+ )
+ validator.add_dissection(
+ [0x01, 0x40, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00],
+ "asterix.034_100",
+ dict_local(x_100, "034_100", "RHOEND", "255.99609375")
+ )
+ validator.add_dissection(
+ [0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00],
+ "asterix.034_100",
+ dict_local(x_100, "034_100", "THETAST", "359.994506835938")
+ )
+ validator.add_dissection(
+ [0x01, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff],
+ "asterix.034_100",
+ dict_local(x_100, "034_100", "THETAEND", "359.994506835938")
+ )
+ validator.add_dissection(
+ [0x01, 0x20, 0x09],
+ "asterix.034_110",
+ {
+ "asterix.034_110_VALUE": "9"
+ }
+ )
+ x_120 = {
+ "asterix.034_120_HGT": "0",
+ "asterix.034_120_LAT": "0",
+ "asterix.034_120_LON": "0"
+ }
+ validator.add_dissection(
+ [0x01, 0x10, 0x7f, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
+ "asterix.034_120",
+ dict_local(x_120, "034_120", "HGT", "32767")
+ )
+ validator.add_dissection(
+ [0x01, 0x10, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00],
+ "asterix.034_120",
+ dict_local(x_120, "034_120", "HGT", "32768")
+ )
+ validator.add_dissection(
+ [0x01, 0x10, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00],
+ "asterix.034_120",
+ dict_local(x_120, "034_120", "LAT", "90")
+ )
+ validator.add_dissection(
+ [0x01, 0x10, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00],
+ "asterix.034_120",
+ dict_local(x_120, "034_120", "LAT", "-90")
+ )
+ validator.add_dissection(
+ [0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff],
+ "asterix.034_120",
+ dict_local(x_120, "034_120", "LON", "179.999978542328")
+ )
+ validator.add_dissection(
+ [0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00],
+ "asterix.034_120",
+ dict_local(x_120, "034_120", "LON", "-180")
+ )
+ '''TODO: re-enable RE and SP tests when implemented
+ x_090 = {
+ "asterix.034_090_RE": "0",
+ "asterix.034_090_AE": "0"
+ }
+ validator.add_dissection(
+ [0x01, 0x08, 0x7f, 0x00],
+ "asterix.034_090",
+ dict_local(x_090, "034_090", "RE", "0.9921875")
+ )
+ validator.add_dissection(
+ [0x01, 0x08, 0x80, 0x00],
+ "asterix.034_090",
+ dict_local(x_090, "034_090", "RE", "-1")
+ )
+ validator.add_dissection(
+ [0x01, 0x08, 0x00, 0x80],
+ "asterix.034_090",
+ dict_local(x_090, "034_090", "AE", "-2.8125")
+ )
+ '''
+
+ validator.check_dissections()
+
+ def test_undefined_value_handling(self, asterix_validator):
+ '''verifies that the dissector can dissect undefined field values by setting
+ the maximum value of bits or by setting all undefined bits'''
+
+ validator = asterix_validator(34)
+
+ validator.add_dissection(
+ [0x40, 0xff],
+ "asterix.034_000",
+ {
+ "asterix.034_000_VALUE": "255"
+ }
+ )
+ validator.add_dissection(
+ [0x20, 0xff, 0xff, 0xff],
+ "asterix.034_030",
+ {
+ "asterix.034_030_VALUE": "131071.9921875"
+ }
+ )
+ validator.add_dissection(
+ [0x04, 0x63, 0x00],
+ "asterix.034_050",
+ {
+ "asterix.fspec": "",
+ "asterix.spare": ""
+ }
+ )
+ validator.add_dissection(
+ [0x04, 0x80, 0x01],
+ "asterix.034_050",
+ {
+ "asterix.fspec": "",
+ "asterix.034_050_COM":
+ {
+ "asterix.034_050_COM_NOGO": "0",
+ "asterix.034_050_COM_RDPC": "0",
+ "asterix.034_050_COM_RDPR": "0",
+ "asterix.034_050_COM_OVLRDP": "0",
+ "asterix.034_050_COM_OVLXMT": "0",
+ "asterix.034_050_COM_MSC": "0",
+ "asterix.034_050_COM_TSV": "0"
+ }
+ }
+ )
+ validator.add_dissection(
+ [0x04, 0x10, 0x07],
+ "asterix.034_050",
+ {
+ "asterix.fspec": "",
+ "asterix.034_050_PSR":
+ {
+ "asterix.034_050_PSR_ANT": "0",
+ "asterix.034_050_PSR_CHAB": "0",
+ "asterix.034_050_PSR_OVL": "0",
+ "asterix.034_050_PSR_MSC": "0"
+ }
+ }
+ )
+ validator.add_dissection(
+ [0x04, 0x08, 0x07],
+ "asterix.034_050",
+ {
+ "asterix.fspec": "",
+ "asterix.034_050_SSR":
+ {
+ "asterix.034_050_SSR_ANT": "0",
+ "asterix.034_050_SSR_CHAB": "0",
+ "asterix.034_050_SSR_OVL": "0",
+ "asterix.034_050_SSR_MSC": "0"
+ }
+ }
+ )
+ validator.add_dissection(
+ [0x04, 0x04, 0x00, 0x7f],
+ "asterix.034_050",
+ {
+ "asterix.fspec": "",
+ "asterix.034_050_MDS":
+ {
+ "asterix.034_050_MDS_ANT": "0",
+ "asterix.034_050_MDS_CHAB": "0",
+ "asterix.034_050_MDS_OVLSUR": "0",
+ "asterix.034_050_MDS_MSC": "0",
+ "asterix.034_050_MDS_SCF": "0",
+ "asterix.034_050_MDS_DLF": "0",
+ "asterix.034_050_MDS_OVLSCF": "0",
+ "asterix.034_050_MDS_OVLDLF": "0"
+ }
+ }
+ )
+ validator.add_dissection(
+ [0x02, 0x63, 0x00],
+ "asterix.034_060",
+ {
+ "asterix.fspec": "",
+ "asterix.spare": ""
+ }
+ )
+ validator.add_dissection(
+ [0x02, 0x80, 0x81],
+ "asterix.034_060",
+ {
+ "asterix.fspec": "",
+ "asterix.034_060_COM":
+ {
+ "asterix.034_060_COM_REDRDP": "0",
+ "asterix.034_060_COM_REDXMT": "0"
+ }
+ }
+ )
+ validator.add_dissection(
+ [0x02, 0x10, 0x03],
+ "asterix.034_060",
+ {
+ "asterix.fspec": "",
+ "asterix.034_060_PSR":
+ {
+ "asterix.034_060_PSR_POL": "0",
+ "asterix.034_060_PSR_REDRAD": "0",
+ "asterix.034_060_PSR_STC": "0"
+ }
+ }
+ )
+ validator.add_dissection(
+ [0x02, 0x08, 0x1f],
+ "asterix.034_060",
+ fspec_local("034_060_SSR", "REDRAD", "0")
+ )
+ validator.add_dissection(
+ [0x02, 0x04, 0x0f],
+ "asterix.034_060",
+ {
+ "asterix.fspec": "",
+ "asterix.034_060_MDS":
+ {
+ "asterix.034_060_MDS_REDRAD": "0",
+ "asterix.034_060_MDS_CLU": "0"
+ }
+ }
+ )
+ x_070 = {
+ "asterix.034_070_TYP": "0",
+ "asterix.034_070_COUNT": "0"
+ }
+ validator.add_dissection(
+ [0x01, 0x80, 0x01, 0xf8, 0x00],
+ "asterix.034_070",
+ {
+ "asterix.counter": "1",
+ "asterix.034_070":
+ dict_local(x_070, "034_070", "TYP", "31")
+ }
+ )
+ validator.add_dissection(
+ [0x01, 0x20, 0xff],
+ "asterix.034_110",
+ {
+ "asterix.034_110_VALUE": "255"
+ }
+ )
+ '''TODO: re-enable RE and SP tests when implemented
+ validator.add_dissection(
+ [0x01, 0x04, 0x02, 0xfe],
+ "asterix.034_RE",
+ {
+ "asterix.re_field_len": "2",
+ "asterix.fspec": ""
+ }
+ )
+ validator.add_dissection(
+ [0x01, 0x02, 0x01],
+ "asterix.034_SP",
+ ""
+ )
+ validator.add_dissection(
+ [0x01, 0x02, 0x11, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+ 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff],
+ "asterix.034_SP",
+ ""
+ )
+ '''
+
+ validator.check_dissections()
+
+
+class TestCategory048:
+ '''
+ Unittest case for ASTERIX Category 048
+
+ Online specification:
+ https://www.eurocontrol.int/publications/cat048-monoradar-target-reports-part-4-next-version-cat-001
+ https://www.eurocontrol.int/publications/cat048-reserved-expansion-field-part-4-appendix
+
+ Part 4 Category 048
+ Monoradar Target Reports
+
+ Standard User Application Profile
+
+ FRN Data Item Information Length
+ 1 I048/010 Data Source Identifier 2
+ 2 I048/140 Time-of-Day 3
+ 3 I048/020 Target Report Descriptor 1+
+ 4 I048/040 Measured Position in Slant Polar Coordinates 4
+ 5 I048/070 Mode-3/A Code in Octal Representation 2
+ 6 I048/090 Flight Level in Binary Representation 2
+ 7 I048/130 Radar Plot Characteristics 1+1+
+ FX n.a. Field Extension Indicator n.a.
+ 8 I048/220 Aircraft Address 3
+ 9 I048/240 Aircraft Identification 6
+ 10 I048/250 Mode S MB Data 1+8*n
+ 11 I048/161 Track Number 2
+ 12 I048/042 Calculated Position in Cartesian Coordinates 4
+ 13 I048/200 Calculated Track Velocity in Polar Representation 4
+ 14 I048/170 Track Status 1+
+ FX n.a. Field Extension Indicator n.a.
+ 15 I048/210 Track Quality 4
+ 16 I048/030 Warning/Error Conditions 1+
+ 17 I048/080 Mode-3/A Code Confidence Indicator 2
+ 18 I048/100 Mode-C Code and Confidence Indicator 4
+ 19 I048/110 Height Measured by 3D Radar 2
+ 20 I048/120 Radial Doppler Speed 1+
+ 21 I048/230 Communications / ACAS Capability and Flight Status 2
+ FX n.a. Field Extension Indicator n.a.
+ 22 I048/260 ACAS Resolution Advisory Report 7
+ 23 I048/055 Mode-1 Code in Octal Representation 1
+ 24 I048/050 Mode-2 Code in Octal Representation 2
+ 25 I048/065 Mode-1 Code Confidence Indicator 1
+ 26 I048/060 Mode-2 Code Confidence Indicator 2
+ 27 SP-Data Item Special Purpose Field 1+1+
+ 28 RE-Data Item Reserved Expansion Field 1+1+
+ FX n.a. Field Extension Indicator n.a.
+ '''
+
+ def test_for_fields(self, asterix_re_validator):
+ '''verifies existence of all fields and their maximum value'''
+
+ validator = asterix_re_validator(48, [0x01, 0x01, 0x01, 0x02])
+
+ validator.add_dissection(
+ [0x80, 0xff, 0x00],
+ "asterix.048_010",
+ {
+ "asterix.048_010_SAC": "0xff",
+ "asterix.048_010_SIC": "0x00"
+ }
+ )
+ validator.add_dissection(
+ [0x80, 0x00, 0xff],
+ "asterix.048_010",
+ {
+ "asterix.048_010_SAC": "0x00",
+ "asterix.048_010_SIC": "0xff"
+ }
+ )
+ validator.add_dissection(
+ [0x40, 0xa8, 0xbf, 0xff],
+ "asterix.048_140",
+ {
+ "asterix.048_140_VALUE": "86399.9921875"
+ }
+ )
+ x_020 = {
+ "asterix.048_020_TYP": "0",
+ "asterix.048_020_SIM": "0",
+ "asterix.048_020_RDP": "0",
+ "asterix.048_020_SPI": "0",
+ "asterix.048_020_RAB": "0",
+ "asterix.FX": "0"
+ }
+ validator.add_dissection(
+ [0x20, 0xe0],
+ "asterix.048_020",
+ dict_local(x_020, "048_020", "TYP", "7")
+ )
+ validator.add_dissection(
+ [0x20, 0x08],
+ "asterix.048_020",
+ dict_local(x_020, "048_020", "RDP", "1")
+ )
+ validator.add_dissection(
+ [0x20, 0x04],
+ "asterix.048_020",
+ dict_local(x_020, "048_020", "SPI", "1")
+ )
+ validator.add_dissection(
+ [0x20, 0x02],
+ "asterix.048_020",
+ dict_local(x_020, "048_020", "RAB", "1")
+ )
+ x_020.update({
+ "asterix.048_020_TST": "0",
+ "asterix.048_020_ERR": "0",
+ "asterix.048_020_XPP": "0",
+ "asterix.048_020_ME": "0",
+ "asterix.048_020_MI": "0",
+ "asterix.048_020_FOEFRI": "0"
+ })
+ validator.add_dissection(
+ [0x20, 0x01, 0x80],
+ "asterix.048_020",
+ dict_local(x_020, "048_020", "TST", "1")
+ )
+ validator.add_dissection(
+ [0x20, 0x01, 0x40],
+ "asterix.048_020",
+ dict_local(x_020, "048_020", "ERR", "1")
+ )
+ validator.add_dissection(
+ [0x20, 0x01, 0x20],
+ "asterix.048_020",
+ dict_local(x_020, "048_020", "XPP", "1")
+ )
+ validator.add_dissection(
+ [0x20, 0x01, 0x10],
+ "asterix.048_020",
+ dict_local(x_020, "048_020", "ME", "1")
+ )
+ validator.add_dissection(
+ [0x20, 0x01, 0x08],
+ "asterix.048_020",
+ dict_local(x_020, "048_020", "MI", "1")
+ )
+ validator.add_dissection(
+ [0x20, 0x01, 0x06],
+ "asterix.048_020",
+ dict_local(x_020, "048_020", "FOEFRI", "3")
+ )
+ x_040 = {
+ "asterix.048_040_RHO": "0",
+ "asterix.048_040_THETA": "0"
+ }
+ validator.add_dissection(
+ [0x10, 0xff, 0xff, 0x00, 0x00],
+ "asterix.048_040",
+ dict_local(x_040, "048_040", "RHO", "255.99609375")
+ )
+ validator.add_dissection(
+ [0x10, 0x00, 0x00, 0xff, 0xff],
+ "asterix.048_040",
+ dict_local(x_040, "048_040", "THETA", "359.994506835938")
+ )
+ x_070 = {
+ "asterix.048_070_V": "0",
+ "asterix.048_070_G": "0",
+ "asterix.048_070_L": "0",
+ "asterix.048_070_MODE3A": "0"
+ }
+ validator.add_dissection(
+ [0x08, 0x80, 0x00],
+ "asterix.048_070",
+ dict_local(x_070, "048_070", "V", "1")
+ )
+ validator.add_dissection(
+ [0x08, 0x40, 0x00],
+ "asterix.048_070",
+ dict_local(x_070, "048_070", "G", "1")
+ )
+ validator.add_dissection(
+ [0x08, 0x20, 0x00],
+ "asterix.048_070",
+ dict_local(x_070, "048_070", "L", "1")
+ )
+ validator.add_dissection(
+ [0x08, 0x0e, 0x00],
+ "asterix.048_070",
+ dict_local(x_070, "048_070", "MODE3A", "3584") # 07000
+ )
+ validator.add_dissection(
+ [0x08, 0x01, 0xc0],
+ "asterix.048_070",
+ dict_local(x_070, "048_070", "MODE3A", "448") # 0700
+ )
+ validator.add_dissection(
+ [0x08, 0x00, 0x38],
+ "asterix.048_070",
+ dict_local(x_070, "048_070", "MODE3A", "56") # 070
+ )
+ validator.add_dissection(
+ [0x08, 0x00, 0x07],
+ "asterix.048_070",
+ dict_local(x_070, "048_070", "MODE3A", "7") # 07
+ )
+ x_090 = {
+ "asterix.048_090_V": "0",
+ "asterix.048_090_G": "0",
+ "asterix.048_090_FL": "0"
+ }
+ validator.add_dissection(
+ [0x04, 0x80, 0x00],
+ "asterix.048_090",
+ dict_local(x_090, "048_090", "V", "1")
+ )
+ validator.add_dissection(
+ [0x04, 0x40, 0x00],
+ "asterix.048_090",
+ dict_local(x_090, "048_090", "G", "1")
+ )
+ validator.add_dissection(
+ [0x04, 0x1f, 0xff],
+ "asterix.048_090",
+ dict_local(x_090, "048_090", "FL", "2047.75")
+ )
+ validator.add_dissection(
+ [0x04, 0x20, 0x00],
+ "asterix.048_090",
+ dict_local(x_090, "048_090", "FL", "2048")
+ )
+ validator.add_dissection(
+ [0x02, 0x80, 0xff],
+ "asterix.048_130",
+ fspec_local("048_130_SRL", "VALUE", "11.2060546875")
+ )
+ validator.add_dissection(
+ [0x02, 0x40, 0xff],
+ "asterix.048_130",
+ fspec_local("048_130_SRR", "VALUE", "255")
+ )
+ validator.add_dissection(
+ [0x02, 0x20, 0x7f],
+ "asterix.048_130",
+ fspec_local("048_130_SAM", "VALUE", "127")
+ )
+ validator.add_dissection(
+ [0x02, 0x20, 0x80],
+ "asterix.048_130",
+ fspec_local("048_130_SAM", "VALUE", "-128")
+ )
+ validator.add_dissection(
+ [0x02, 0x10, 0xff],
+ "asterix.048_130",
+ fspec_local("048_130_PRL", "VALUE", "11.2060546875")
+ )
+ validator.add_dissection(
+ [0x02, 0x08, 0x7f],
+ "asterix.048_130",
+ fspec_local("048_130_PAM", "VALUE", "127")
+ )
+ validator.add_dissection(
+ [0x02, 0x08, 0x80],
+ "asterix.048_130",
+ fspec_local("048_130_PAM", "VALUE", "-128")
+ )
+ validator.add_dissection(
+ [0x02, 0x04, 0x7f],
+ "asterix.048_130",
+ fspec_local("048_130_RPD", "VALUE", "0.49609375")
+ )
+ validator.add_dissection(
+ [0x02, 0x04, 0x80],
+ "asterix.048_130",
+ fspec_local("048_130_RPD", "VALUE", "-0.5")
+ )
+ validator.add_dissection(
+ [0x02, 0x02, 0x7f],
+ "asterix.048_130",
+ fspec_local("048_130_APD", "VALUE", "2.79052734375")
+ )
+ validator.add_dissection(
+ [0x02, 0x02, 0x80],
+ "asterix.048_130",
+ fspec_local("048_130_APD", "VALUE", "-2.8125")
+ )
+ validator.add_dissection(
+ [0x01, 0x80, 0xff, 0xff, 0xff],
+ "asterix.048_220",
+ {
+ "asterix.048_220_VALUE": '0xffffff'
+ }
+ )
+ validator.add_dissection(
+ [0x01, 0x80, 0xff, 0xff, 0xff],
+ "asterix.048_220",
+ {
+ "asterix.048_220_VALUE": '0xffffff'
+ }
+ )
+ validator.add_dissection(
+ [0x01, 0x40, 0x04, 0x20, 0xda, 0x83, 0x0c, 0x79],
+ "asterix.048_240",
+ {
+ "asterix.048_240_VALUE": "ABCZ 019"
+ }
+ )
+ x_250 = {
+ "asterix.048_250_MBDATA": "00:00:00:00:00:00:00",
+ "asterix.048_250_BDS1": "0",
+ "asterix.048_250_BDS2": "0"
+ }
+ validator.add_dissection(
+ [0x01, 0x20,
+ 0x01,
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+ 0x00],
+ "asterix.048_250",
+ {
+ "asterix.counter": "1",
+ "asterix.048_250":
+ dict_global(x_250, "048_250_MBDATA", '0x0011223344556677'),
+ }
+ )
+ '''TODO: result seems correct, check dict format
+ validator.add_dissection(
+ [0x01, 0x20,
+ 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xf0],
+ "asterix.048_250",
+ {
+ "asterix.counter": "1",
+ "asterix.048_250":
+ dict_global(x_250, "048_250_BDS1", "15"),
+ }
+ )
+ validator.add_dissection(
+ [0x01, 0x20,
+ 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0f],
+ "asterix.048_250",
+ {
+ "asterix.counter": "1",
+ "asterix.048_250":
+ dict_global(x_250, "BDS2", "15"),
+ }
+ )
+ validator.add_dissection(
+ [0x01, 0x20,
+ 0x03,
+ 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+ 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xf0,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0f],
+ "asterix.048_250",
+ {
+ "asterix.counter": "3",
+ "asterix.048_250":
+ dict_global(x_250, "048_250_MBDATA", '0x0011223344556677'),
+ "asterix.048_250":
+ dict_global(x_250, "048_250_BDS1", "15"),
+ "asterix.048_250":
+ dict_global(x_250, "048_250_BDS2", "15"),
+ }
+ )
+ '''
+ validator.add_dissection(
+ [0x01, 0x10, 0x0f, 0xff],
+ "asterix.048_161",
+ {
+ "asterix.048_161_TRN": "4095"
+ }
+ )
+ x_042 = {
+ "asterix.048_042_X": "0",
+ "asterix.048_042_Y": "0"
+ }
+ validator.add_dissection(
+ [0x01, 0x08, 0x7f, 0xff, 0x00, 0x00],
+ "asterix.048_042",
+ dict_local(x_042, "048_042", "X", "255.9921875")
+ )
+ validator.add_dissection(
+ [0x01, 0x08, 0x80, 0x00, 0x00, 0x00],
+ "asterix.048_042",
+ dict_local(x_042, "048_042", "X", "-256")
+ )
+ validator.add_dissection(
+ [0x01, 0x08, 0x00, 0x0, 0x7f, 0xff],
+ "asterix.048_042",
+ dict_local(x_042, "048_042", "Y", "255.9921875")
+ )
+ validator.add_dissection(
+ [0x01, 0x08, 0x00, 0x0, 0x80, 0x00],
+ "asterix.048_042",
+ dict_local(x_042, "048_042", "Y", "-256")
+ )
+ x_200 = {
+ "asterix.048_200_GSP": "0",
+ "asterix.048_200_HDG": "0"
+ }
+ validator.add_dissection(
+ [0x01, 0x04, 0xff, 0xff, 0x00, 0x00],
+ "asterix.048_200",
+ dict_local(x_200, "048_200", "GSP", "3.99993896484375")
+ )
+ validator.add_dissection(
+ [0x01, 0x04, 0x00, 0x00, 0xff, 0xff],
+ "asterix.048_200",
+ dict_local(x_200, "048_200", "HDG", "359.994506835938")
+ )
+ x_170 = {
+ "asterix.048_170_CNF": "0",
+ "asterix.048_170_RAD": "0",
+ "asterix.048_170_DOU": "0",
+ "asterix.048_170_MAH": "0",
+ "asterix.048_170_CDM": "0",
+ "asterix.FX": "0"
+ }
+ validator.add_dissection(
+ [0x01, 0x02, 0x80],
+ "asterix.048_170",
+ dict_local(x_170, "048_170", "CNF", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x02, 0x60],
+ "asterix.048_170",
+ dict_local(x_170, "048_170", "RAD", "3")
+ )
+ validator.add_dissection(
+ [0x01, 0x02, 0x10],
+ "asterix.048_170",
+ dict_local(x_170, "048_170", "DOU", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x02, 0x08],
+ "asterix.048_170",
+ dict_local(x_170, "048_170", "MAH", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x02, 0x06],
+ "asterix.048_170",
+ dict_local(x_170, "048_170", "CDM", "3")
+ )
+ x_170.update({
+ "asterix.048_170_TRE": "0",
+ "asterix.048_170_GHO": "0",
+ "asterix.048_170_SUP": "0",
+ "asterix.048_170_TCC": "0"
+ })
+ validator.add_dissection(
+ [0x01, 0x02, 0x01, 0x80],
+ "asterix.048_170",
+ dict_local(x_170, "048_170", "TRE", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x02, 0x01, 0x40],
+ "asterix.048_170",
+ dict_local(x_170, "048_170", "GHO", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x02, 0x01, 0x20],
+ "asterix.048_170",
+ dict_local(x_170, "048_170", "SUP", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x02, 0x01, 0x10],
+ "asterix.048_170",
+ dict_local(x_170, "048_170", "TCC", "1")
+ )
+ x_210 = {
+ "asterix.048_210_SIGX": "0",
+ "asterix.048_210_SIGY": "0",
+ "asterix.048_210_SIGV": "0",
+ "asterix.048_210_SIGH": "0"
+ }
+ validator.add_dissection(
+ [0x01, 0x01, 0x80, 0xff, 0x00, 0x00, 0x00],
+ "asterix.048_210",
+ dict_local(x_210, "048_210", "SIGX", "1.9921875")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x80, 0x00, 0xff, 0x00, 0x00],
+ "asterix.048_210",
+ dict_local(x_210, "048_210", "SIGY", "1.9921875")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x80, 0x00, 0x00, 0xff, 0x00],
+ "asterix.048_210",
+ dict_local(x_210, "048_210", "SIGV", "0.01556396484375")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x80, 0x00, 0x00, 0x00, 0xff],
+ "asterix.048_210",
+ dict_local(x_210, "048_210", "SIGH", "22.412109375")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x40, 0x2e],
+ "asterix.048_030",
+ {
+ "asterix.048_030_Subitem": "23",
+ "asterix.FX": "0"
+ }
+ )
+ '''TODO: check this test, not according to the specs
+ validator.add_dissection(
+ [0x01, 0x01, 0x40, 0x2f, 0x03, 0x05, 0x06],
+ "asterix.048_030",
+ {
+ "asterix.048_030_WE": "23",
+ "asterix.048_030_1_WE": "1",
+ "asterix.048_030_2_WE": "2",
+ "asterix.048_030_3_WE": "3",
+ "asterix.FX": "0"
+ }
+ )
+ '''
+ x_080 = {
+ "asterix.048_080_QA4": "0",
+ "asterix.048_080_QA2": "0",
+ "asterix.048_080_QA1": "0",
+ "asterix.048_080_QB4": "0",
+ "asterix.048_080_QB2": "0",
+ "asterix.048_080_QB1": "0",
+ "asterix.048_080_QC4": "0",
+ "asterix.048_080_QC2": "0",
+ "asterix.048_080_QC1": "0",
+ "asterix.048_080_QD4": "0",
+ "asterix.048_080_QD2": "0",
+ "asterix.048_080_QD1": "0"
+ }
+ validator.add_dissection(
+ [0x01, 0x01, 0x20, 0x08, 0x00],
+ "asterix.048_080",
+ dict_local(x_080, "048_080", "QA4", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x20, 0x04, 0x00],
+ "asterix.048_080",
+ dict_local(x_080, "048_080", "QA2", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x20, 0x02, 0x00],
+ "asterix.048_080",
+ dict_local(x_080, "048_080", "QA1", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x20, 0x01, 0x00],
+ "asterix.048_080",
+ dict_local(x_080, "048_080", "QB4", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x20, 0x00, 0x80],
+ "asterix.048_080",
+ dict_local(x_080, "048_080", "QB2", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x20, 0x00, 0x40],
+ "asterix.048_080",
+ dict_local(x_080, "048_080", "QB1", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x20, 0x00, 0x20],
+ "asterix.048_080",
+ dict_local(x_080, "048_080", "QC4", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x20, 0x00, 0x10],
+ "asterix.048_080",
+ dict_local(x_080, "048_080", "QC2", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x20, 0x00, 0x08],
+ "asterix.048_080",
+ dict_local(x_080, "048_080", "QC1", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x20, 0x00, 0x04],
+ "asterix.048_080",
+ dict_local(x_080, "048_080", "QD4", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x20, 0x00, 0x02],
+ "asterix.048_080",
+ dict_local(x_080, "048_080", "QD2", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x20, 0x00, 0x01],
+ "asterix.048_080",
+ dict_local(x_080, "048_080", "QD1", "1")
+ )
+ '''TODO: A,B,C,D values need to go to single subitem 'MODEC'
+ x_100 = {
+ "asterix.048_100_V": "0",
+ "asterix.048_100_G": "0",
+ "asterix.048_100_C1": "0",
+ "asterix.048_100_A1": "0",
+ "asterix.048_100_C2": "0",
+ "asterix.048_100_A2": "0",
+ "asterix.048_100_C4": "0",
+ "asterix.048_100_A4": "0",
+ "asterix.048_100_B1": "0",
+ "asterix.048_100_D1": "0",
+ "asterix.048_100_B2": "0",
+ "asterix.048_100_D2": "0",
+ "asterix.048_100_B4": "0",
+ "asterix.048_100_D4": "0",
+ "asterix.048_100_QC1": "0",
+ "asterix.048_100_QA1": "0",
+ "asterix.048_100_QC2": "0",
+ "asterix.048_100_QA2": "0",
+ "asterix.048_100_QC4": "0",
+ "asterix.048_100_QA4": "0",
+ "asterix.048_100_QB1": "0",
+ "asterix.048_100_QD1": "0",
+ "asterix.048_100_QB2": "0",
+ "asterix.048_100_QD2": "0",
+ "asterix.048_100_QB4": "0",
+ "asterix.048_100_QD4": "0"
+ }
+ validator.add_dissection(
+ [0x01, 0x01, 0x10, 0x80, 0x00, 0x00, 0x00],
+ "asterix.048_100",
+ dict_local(x_100, "048_100", "V", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x10, 0x40, 0x00, 0x00, 0x00],
+ "asterix.048_100",
+ dict_local(x_100, "048_100", "G", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x10, 0x08, 0x00, 0x00, 0x00],
+ "asterix.048_100",
+ dict_local(x_100, "048_100", "C1", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x10, 0x04, 0x00, 0x00, 0x00],
+ "asterix.048_100",
+ dict_local(x_100, "048_100", "A1", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x10, 0x02, 0x00, 0x00, 0x00],
+ "asterix.048_100",
+ dict_local(x_100, "048_100", "C2", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x10, 0x01, 0x00, 0x00, 0x00],
+ "asterix.048_100",
+ dict_local(x_100, "048_100", "A2", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x10, 0x00, 0x80, 0x00, 0x00],
+ "asterix.048_100",
+ dict_local(x_100, "048_100", "C4", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x10, 0x00, 0x40, 0x00, 0x00],
+ "asterix.048_100",
+ dict_local(x_100, "048_100", "A4", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x10, 0x00, 0x20, 0x00, 0x00],
+ "asterix.048_100",
+ dict_local(x_100, "048_100", "B1", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x10, 0x00, 0x10, 0x00, 0x00],
+ "asterix.048_100",
+ dict_local(x_100, "048_100", "D1", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x10, 0x00, 0x08, 0x00, 0x00],
+ "asterix.048_100",
+ dict_local(x_100, "048_100", "B2", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x10, 0x00, 0x04, 0x00, 0x00],
+ "asterix.048_100",
+ dict_local(x_100, "048_100", "D2", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x10, 0x00, 0x02, 0x00, 0x00],
+ "asterix.048_100",
+ dict_local(x_100, "048_100", "B4", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x10, 0x00, 0x01, 0x00, 0x00],
+ "asterix.048_100",
+ dict_local(x_100, "048_100", "D4", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x10, 0x00, 0x00, 0x08, 0x00],
+ "asterix.048_100",
+ dict_local(x_100, "048_100", "QC1", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x10, 0x00, 0x00, 0x04, 0x00],
+ "asterix.048_100",
+ dict_local(x_100, "048_100", "QA1", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x10, 0x00, 0x00, 0x02, 0x00],
+ "asterix.048_100",
+ dict_local(x_100, "048_100", "QC2", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x10, 0x00, 0x00, 0x01, 0x00],
+ "asterix.048_100",
+ dict_local(x_100, "048_100", "QA2", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x10, 0x00, 0x00, 0x00, 0x80],
+ "asterix.048_100",
+ dict_local(x_100, "048_100", "QC4", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x10, 0x00, 0x00, 0x00, 0x40],
+ "asterix.048_100",
+ dict_local(x_100, "048_100", "QA4", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x10, 0x00, 0x00, 0x00, 0x20],
+ "asterix.048_100",
+ dict_local(x_100, "048_100", "QB1", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x10, 0x00, 0x00, 0x00, 0x10],
+ "asterix.048_100",
+ dict_local(x_100, "048_100", "QD1", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x10, 0x00, 0x00, 0x00, 0x08],
+ "asterix.048_100",
+ dict_local(x_100, "048_100", "QB2", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x10, 0x00, 0x00, 0x00, 0x04],
+ "asterix.048_100",
+ dict_local(x_100, "048_100", "QD2", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x10, 0x00, 0x00, 0x00, 0x02],
+ "asterix.048_100",
+ dict_local(x_100, "048_100", "QB4", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x10, 0x00, 0x00, 0x00, 0x01],
+ "asterix.048_100",
+ dict_local(x_100, "048_100", "QD4", "1")
+ )
+ '''
+ validator.add_dissection(
+ [0x01, 0x01, 0x08, 0x1f, 0xff],
+ "asterix.048_110",
+ {
+ "asterix.048_110_3DH": "204775"
+ }
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x08, 0x20, 0x00],
+ "asterix.048_110",
+ {
+ "asterix.048_110_3DH": "-204800"
+ }
+ )
+ x_120_01 = {
+ "asterix.048_120_CAL_D": "0",
+ "asterix.048_120_CAL_CAL": "0"
+ }
+ validator.add_dissection(
+ [0x01, 0x01, 0x04, 0x80, 0x80, 0x00],
+ "asterix.048_120",
+ dict_fspec_local(x_120_01, "048_120_CAL", "D", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x04, 0x80, 0x01, 0xff],
+ "asterix.048_120",
+ dict_fspec_local(x_120_01, "048_120_CAL", "CAL", "511")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x04, 0x80, 0x02, 0x00],
+ "asterix.048_120",
+ dict_fspec_local(x_120_01, "048_120_CAL", "CAL", "-512")
+ )
+ x_120_RDS = {
+ "asterix.048_120_RDS_DOP": "0",
+ "asterix.048_120_RDS_AMB": "0",
+ "asterix.048_120_RDS_FRQ": "0"
+ }
+ validator.add_dissection(
+ [0x01, 0x01, 0x04, 0x40, 0x01, 0x7f, 0xff, 0x00, 0x00, 0x00, 0x00],
+ "asterix.048_120",
+ counter_local(x_120_RDS, "1", "048_120_RDS", "DOP", "32767")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x04, 0x40, 0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00],
+ "asterix.048_120",
+ counter_local(x_120_RDS, "1", "048_120_RDS", "DOP", "32768")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x04, 0x40, 0x01, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00],
+ "asterix.048_120",
+ counter_local(x_120_RDS, "1", "048_120_RDS", "AMB", "65535")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x04, 0x40, 0x01, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff],
+ "asterix.048_120",
+ counter_local(x_120_RDS, "1", "048_120_RDS", "FRQ", "65535")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x04, 0x40, 0x03, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff,
+ 0xff],
+ "asterix.048_120",
+ {
+ "asterix.fspec": "",
+ "asterix.048_120_RDS":
+ {
+ "asterix.counter": "3",
+ "asterix.048_120_RDS":
+ dict_local(x_120_RDS, "048_120_RDS", "DOP", "-32768"),
+ "asterix.048_120_RDS":
+ dict_local(x_120_RDS, "048_120_RDS", "AMB", "65535"),
+ "asterix.048_120_RDS":
+ dict_local(x_120_RDS, "048_120_RDS", "FRQ", "65535")
+ }
+ }
+ )
+ x_230 = {
+ "asterix.048_230_COM": "0",
+ "asterix.048_230_STAT": "0",
+ "asterix.048_230_SI": "0",
+ "asterix.048_230_MSSC": "0",
+ "asterix.048_230_ARC": "0",
+ "asterix.048_230_AIC": "0",
+ "asterix.048_230_B1A": "0",
+ "asterix.048_230_B1B": "0"
+ }
+ validator.add_dissection(
+ [0x01, 0x01, 0x02, 0xe0, 0x00],
+ "asterix.048_230",
+ dict_local(x_230, "048_230", "COM", "7")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x02, 0x1c, 0x00],
+ "asterix.048_230",
+ dict_local(x_230, "048_230", "STAT", "7")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x02, 0x02, 0x00],
+ "asterix.048_230",
+ dict_local(x_230, "048_230", "SI", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x02, 0x00, 0x80],
+ "asterix.048_230",
+ dict_local(x_230, "048_230", "MSSC", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x02, 0x00, 0x40],
+ "asterix.048_230",
+ dict_local(x_230, "048_230", "ARC", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x02, 0x00, 0x20],
+ "asterix.048_230",
+ dict_local(x_230, "048_230", "AIC", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x02, 0x00, 0x10],
+ "asterix.048_230",
+ dict_local(x_230, "048_230", "B1A", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x02, 0x00, 0x0f],
+ "asterix.048_230",
+ dict_local(x_230, "048_230", "B1B", "15")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x01, 0x80, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77],
+ "asterix.048_260",
+ {
+ "asterix.048_260_VALUE": '0x0011223344556677'
+ }
+ )
+ x_055 = {
+ "asterix.048_055_V": "0",
+ "asterix.048_055_G": "0",
+ "asterix.048_055_L": "0",
+ "asterix.048_055_MODE1": "0"
+ }
+ validator.add_dissection(
+ [0x01, 0x01, 0x01, 0x40, 0x80],
+ "asterix.048_055",
+ dict_local(x_055, "048_055", "V", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x01, 0x40, 0x40],
+ "asterix.048_055",
+ dict_local(x_055, "048_055", "G", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x01, 0x40, 0x20],
+ "asterix.048_055",
+ dict_local(x_055, "048_055", "L", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x01, 0x40, 0x1f],
+ "asterix.048_055",
+ dict_local(x_055, "048_055", "MODE1", "31")
+ )
+ x_050 = {
+ "asterix.048_050_V": "0",
+ "asterix.048_050_G": "0",
+ "asterix.048_050_L": "0",
+ "asterix.048_050_MODE2": "0"
+ }
+ validator.add_dissection(
+ [0x01, 0x01, 0x01, 0x20, 0x80, 0x00],
+ "asterix.048_050",
+ dict_local(x_050, "048_050", "V", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x01, 0x20, 0x40, 0x00],
+ "asterix.048_050",
+ dict_local(x_050, "048_050", "G", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x01, 0x20, 0x20, 0x00],
+ "asterix.048_050",
+ dict_local(x_050, "048_050", "L", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x01, 0x20, 0x0f, 0xff],
+ "asterix.048_050",
+ dict_local(x_050, "048_050", "MODE2", "4095")
+ )
+ x_065 = {
+ "asterix.048_065_QA4": "0",
+ "asterix.048_065_QA2": "0",
+ "asterix.048_065_QA1": "0",
+ "asterix.048_065_QB2": "0",
+ "asterix.048_065_QB1": "0"
+ }
+ validator.add_dissection(
+ [0x01, 0x01, 0x01, 0x10, 0x10],
+ "asterix.048_065",
+ dict_local(x_065, "048_065", "QA4", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x01, 0x10, 0x08],
+ "asterix.048_065",
+ dict_local(x_065, "048_065", "QA2", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x01, 0x10, 0x04],
+ "asterix.048_065",
+ dict_local(x_065, "048_065", "QA1", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x01, 0x10, 0x02],
+ "asterix.048_065",
+ dict_local(x_065, "048_065", "QB2", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x01, 0x10, 0x01],
+ "asterix.048_065",
+ dict_local(x_065, "048_065", "QB1", "1")
+ )
+ x_060 = {
+ "asterix.048_060_QA4": "0",
+ "asterix.048_060_QA2": "0",
+ "asterix.048_060_QA1": "0",
+ "asterix.048_060_QB4": "0",
+ "asterix.048_060_QB2": "0",
+ "asterix.048_060_QB1": "0",
+ "asterix.048_060_QC4": "0",
+ "asterix.048_060_QC2": "0",
+ "asterix.048_060_QC1": "0",
+ "asterix.048_060_QD4": "0",
+ "asterix.048_060_QD2": "0",
+ "asterix.048_060_QD1": "0"
+ }
+ validator.add_dissection(
+ [0x01, 0x01, 0x01, 0x08, 0x08, 0x00],
+ "asterix.048_060",
+ dict_local(x_060, "048_060", "QA4", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x01, 0x08, 0x04, 0x00],
+ "asterix.048_060",
+ dict_local(x_060, "048_060", "QA2", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x01, 0x08, 0x02, 0x00],
+ "asterix.048_060",
+ dict_local(x_060, "048_060", "QA1", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x01, 0x08, 0x01, 0x00],
+ "asterix.048_060",
+ dict_local(x_060, "048_060", "QB4", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x01, 0x08, 0x00, 0x80],
+ "asterix.048_060",
+ dict_local(x_060, "048_060", "QB2", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x01, 0x08, 0x00, 0x40],
+ "asterix.048_060",
+ dict_local(x_060, "048_060", "QB1", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x01, 0x08, 0x00, 0x20],
+ "asterix.048_060",
+ dict_local(x_060, "048_060", "QC4", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x01, 0x08, 0x00, 0x10],
+ "asterix.048_060",
+ dict_local(x_060, "048_060", "QC2", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x01, 0x08, 0x00, 0x08],
+ "asterix.048_060",
+ dict_local(x_060, "048_060", "QC1", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x01, 0x08, 0x00, 0x04],
+ "asterix.048_060",
+ dict_local(x_060, "048_060", "QD4", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x01, 0x08, 0x00, 0x02],
+ "asterix.048_060",
+ dict_local(x_060, "048_060", "QD2", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x01, 0x08, 0x00, 0x01],
+ "asterix.048_060",
+ dict_local(x_060, "048_060", "QD1", "1")
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x01, 0x04, 0x01],
+ "asterix.048_SP",
+ ""
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x01, 0x04, 0x10, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66,
+ 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff],
+ "asterix.048_SP",
+ ""
+ )
+ '''TODO: re-enable RE and SP tests when implemented
+ x_re_md5 = {
+ "asterix.048_RE_MD5_01_M5": "0",
+ "asterix.048_RE_MD5_01_ID": "0",
+ "asterix.048_RE_MD5_01_DA": "0",
+ "asterix.048_RE_MD5_01_M1": "0",
+ "asterix.048_RE_MD5_01_M2": "0",
+ "asterix.048_RE_MD5_01_M3": "0",
+ "asterix.048_RE_MD5_01_MC": "0"
+ }
+ validator.add_re_dissection(
+ [0x80, 0x80, 0x80],
+ "MD5",
+ dict_fspec_local(x_re_md5, "048_RE_MD5_01", "M5", "1")
+ )
+ validator.add_re_dissection(
+ [0x80, 0x80, 0x40],
+ "MD5",
+ dict_fspec_local(x_re_md5, "048_RE_MD5_01", "ID", "1")
+ )
+ validator.add_re_dissection(
+ [0x80, 0x80, 0x20],
+ "MD5",
+ dict_fspec_local(x_re_md5, "048_RE_MD5_01", "DA", "1")
+ )
+ validator.add_re_dissection(
+ [0x80, 0x80, 0x10],
+ "MD5",
+ dict_fspec_local(x_re_md5, "048_RE_MD5_01", "M1", "1")
+ )
+ validator.add_re_dissection(
+ [0x80, 0x80, 0x08],
+ "MD5",
+ dict_fspec_local(x_re_md5, "048_RE_MD5_01", "M2", "1")
+ )
+ validator.add_re_dissection(
+ [0x80, 0x80, 0x04],
+ "MD5",
+ dict_fspec_local(x_re_md5, "048_RE_MD5_01", "M3", "1")
+ )
+ validator.add_re_dissection(
+ [0x80, 0x80, 0x02],
+ "MD5",
+ dict_fspec_local(x_re_md5, "048_RE_MD5_01", "MC", "1")
+ )
+ x_re_pmn = {
+ "asterix.048_RE_MD5_02_PIN": "0",
+ "asterix.048_RE_MD5_02_NAV": "0",
+ "asterix.048_RE_MD5_02_NAT": "0",
+ "asterix.048_RE_MD5_02_MIS": "0"
+ }
+ validator.add_re_dissection(
+ [0x80, 0x40, 0x3f, 0xff, 0x00, 0x00],
+ "MD5",
+ dict_fspec_local(x_re_pmn, "048_RE_MD5_02", "PIN", "16383")
+ )
+ validator.add_re_dissection(
+ [0x80, 0x40, 0x00, 0x00, 0x20, 0x00],
+ "MD5",
+ dict_fspec_local(x_re_pmn, "048_RE_MD5_02", "NAV", "1")
+ )
+ validator.add_re_dissection(
+ [0x80, 0x40, 0x00, 0x00, 0x1f, 0x00],
+ "MD5",
+ dict_fspec_local(x_re_pmn, "048_RE_MD5_02", "NAT", "31")
+ )
+ validator.add_re_dissection(
+ [0x80, 0x40, 0x00, 0x00, 0x00, 0x3f],
+ "MD5",
+ dict_fspec_local(x_re_pmn, "048_RE_MD5_02", "MIS", "63")
+ )
+ x_re_pos = {
+ "asterix.048_RE_MD5_03_LAT": "0",
+ "asterix.048_RE_MD5_03_LON": "0"
+ }
+ validator.add_re_dissection(
+ [0x80, 0x20, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00],
+ "MD5",
+ dict_fspec_local(x_re_pos, "048_RE_MD5_03", "LAT", "90")
+ )
+ validator.add_re_dissection(
+ [0x80, 0x20, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00],
+ "MD5",
+ dict_fspec_local(x_re_pos, "048_RE_MD5_03", "LAT", "-90")
+ )
+ validator.add_re_dissection(
+ [0x80, 0x20, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff],
+ "MD5",
+ dict_fspec_local(x_re_pos, "048_RE_MD5_03",
+ "LON", "179.999978542328")
+ )
+ validator.add_re_dissection(
+ [0x80, 0x20, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00],
+ "MD5",
+ dict_fspec_local(x_re_pos, "048_RE_MD5_03", "LON", "-180")
+ )
+ x_re_ga = {
+ "asterix.048_RE_MD5_04_RES": "0",
+ "asterix.048_RE_MD5_04_GA": "0"
+ }
+ validator.add_re_dissection(
+ [0x80, 0x10, 0x40, 0x00],
+ "MD5",
+ dict_fspec_local(x_re_ga, "048_RE_MD5_04", "RES", "1")
+ )
+ validator.add_re_dissection(
+ [0x80, 0x10, 0x1f, 0xff],
+ "MD5",
+ dict_fspec_local(x_re_ga, "048_RE_MD5_04", "GA", "204775")
+ )
+ validator.add_re_dissection(
+ [0x80, 0x10, 0x20, 0x00],
+ "MD5",
+ dict_fspec_local(x_re_ga, "048_RE_MD5_04", "GA", "-204800")
+ )
+ x_re_em1 = {
+ "asterix.048_RE_MD5_05_V": "0",
+ "asterix.048_RE_MD5_05_G": "0",
+ "asterix.048_RE_MD5_05_L": "0",
+ "asterix.048_RE_MD5_05_MODE3A": "0"
+ }
+ validator.add_re_dissection(
+ [0x80, 0x08, 0x80, 0x00],
+ "MD5",
+ dict_fspec_local(x_re_em1, "048_RE_MD5_05", "V", "1")
+ )
+ validator.add_re_dissection(
+ [0x80, 0x08, 0x40, 0x00],
+ "MD5",
+ dict_fspec_local(x_re_em1, "048_RE_MD5_05", "G", "1")
+ )
+ validator.add_re_dissection(
+ [0x80, 0x08, 0x20, 0x00],
+ "MD5",
+ dict_fspec_local(x_re_em1, "048_RE_MD5_05", "L", "1")
+ )
+ validator.add_re_dissection(
+ [0x80, 0x08, 0x0f, 0xff],
+ "MD5",
+ dict_fspec_local(x_re_em1, "048_RE_MD5_05", "MODE3A", "4095")
+ )
+ validator.add_re_dissection(
+ [0x80, 0x04, 0x7f],
+ "MD5",
+ fspec_local("048_RE_MD5_06", "TOS", "0.9921875")
+ )
+ validator.add_re_dissection(
+ [0x80, 0x04, 0x80],
+ "MD5",
+ fspec_local("048_RE_MD5_06", "TOS", "-1")
+ )
+ x_re_xp = {
+ "asterix.048_RE_MD5_07_XP": "0",
+ "asterix.048_RE_MD5_07_X5": "0",
+ "asterix.048_RE_MD5_07_XC": "0",
+ "asterix.048_RE_MD5_07_X3": "0",
+ "asterix.048_RE_MD5_07_X2": "0",
+ "asterix.048_RE_MD5_07_X1": "0"
+ }
+ validator.add_re_dissection(
+ [0x80, 0x02, 0x20],
+ "MD5",
+ dict_fspec_local(x_re_xp, "048_RE_MD5_07", "XP", "1")
+ )
+ validator.add_re_dissection(
+ [0x80, 0x02, 0x10],
+ "MD5",
+ dict_fspec_local(x_re_xp, "048_RE_MD5_07", "X5", "1")
+ )
+ validator.add_re_dissection(
+ [0x80, 0x02, 0x08],
+ "MD5",
+ dict_fspec_local(x_re_xp, "048_RE_MD5_07", "XC", "1")
+ )
+ validator.add_re_dissection(
+ [0x80, 0x02, 0x04],
+ "MD5",
+ dict_fspec_local(x_re_xp, "048_RE_MD5_07", "X3", "1")
+ )
+ validator.add_re_dissection(
+ [0x80, 0x02, 0x02],
+ "MD5",
+ dict_fspec_local(x_re_xp, "048_RE_MD5_07", "X2", "1")
+ )
+ validator.add_re_dissection(
+ [0x80, 0x02, 0x01],
+ "MD5",
+ dict_fspec_local(x_re_xp, "048_RE_MD5_07", "X1", "1")
+ )
+ x_re_md5 = {
+ "asterix.048_RE_M5N_01_M5": "0",
+ "asterix.048_RE_M5N_01_ID": "0",
+ "asterix.048_RE_M5N_01_DA": "0",
+ "asterix.048_RE_M5N_01_M1": "0",
+ "asterix.048_RE_M5N_01_M2": "0",
+ "asterix.048_RE_M5N_01_M3": "0",
+ "asterix.048_RE_M5N_01_MC": "0"
+ }
+ validator.add_re_dissection(
+ [0x40, 0x80, 0x80],
+ "M5N",
+ dict_fspec_local(x_re_md5, "048_RE_M5N_01", "M5", "1")
+ )
+ validator.add_re_dissection(
+ [0x40, 0x80, 0x40],
+ "M5N",
+ dict_fspec_local(x_re_md5, "048_RE_M5N_01", "ID", "1")
+ )
+ validator.add_re_dissection(
+ [0x40, 0x80, 0x20],
+ "M5N",
+ dict_fspec_local(x_re_md5, "048_RE_M5N_01", "DA", "1")
+ )
+ validator.add_re_dissection(
+ [0x40, 0x80, 0x10],
+ "M5N",
+ dict_fspec_local(x_re_md5, "048_RE_M5N_01", "M1", "1")
+ )
+ validator.add_re_dissection(
+ [0x40, 0x80, 0x08],
+ "M5N",
+ dict_fspec_local(x_re_md5, "048_RE_M5N_01", "M2", "1")
+ )
+ validator.add_re_dissection(
+ [0x40, 0x80, 0x04],
+ "M5N",
+ dict_fspec_local(x_re_md5, "048_RE_M5N_01", "M3", "1")
+ )
+ validator.add_re_dissection(
+ [0x40, 0x80, 0x02],
+ "M5N",
+ dict_fspec_local(x_re_md5, "048_RE_M5N_01", "MC", "1")
+ )
+ x_re_pmn = {
+ "asterix.048_RE_M5N_02_PIN": "0",
+ "asterix.048_RE_M5N_02_NOV": "0",
+ "asterix.048_RE_M5N_02_NO": "0"
+ }
+ validator.add_re_dissection(
+ [0x40, 0x40, 0x3f, 0xff, 0x00, 0x00],
+ "M5N",
+ dict_fspec_local(x_re_pmn, "048_RE_M5N_02", "PIN", "16383")
+ )
+ validator.add_re_dissection(
+ [0x40, 0x40, 0x00, 0x00, 0x08, 0x00],
+ "M5N",
+ dict_fspec_local(x_re_pmn, "048_RE_M5N_02", "NOV", "1")
+ )
+ validator.add_re_dissection(
+ [0x40, 0x40, 0x00, 0x00, 0x07, 0xff],
+ "M5N",
+ dict_fspec_local(x_re_pmn, "048_RE_M5N_02", "NO", "2047")
+ )
+ x_re_pos = {
+ "asterix.048_RE_M5N_03_LAT": "0",
+ "asterix.048_RE_M5N_03_LON": "0"
+ }
+ validator.add_re_dissection(
+ [0x40, 0x20, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00],
+ "M5N",
+ dict_fspec_local(x_re_pos, "048_RE_M5N_03", "LAT", "90")
+ )
+ validator.add_re_dissection(
+ [0x40, 0x20, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00],
+ "M5N",
+ dict_fspec_local(x_re_pos, "048_RE_M5N_03", "LAT", "-90")
+ )
+ validator.add_re_dissection(
+ [0x40, 0x20, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff],
+ "M5N",
+ dict_fspec_local(x_re_pos, "048_RE_M5N_03",
+ "LON", "179.999978542328")
+ )
+ validator.add_re_dissection(
+ [0x40, 0x20, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00],
+ "M5N",
+ dict_fspec_local(x_re_pos, "048_RE_M5N_03", "LON", "-180")
+ )
+ x_re_ga = {
+ "asterix.048_RE_M5N_04_RES": "0",
+ "asterix.048_RE_M5N_04_GA": "0"
+ }
+ validator.add_re_dissection(
+ [0x40, 0x10, 0x40, 0x00],
+ "M5N",
+ dict_fspec_local(x_re_ga, "048_RE_M5N_04", "RES", "1")
+ )
+ validator.add_re_dissection(
+ [0x40, 0x10, 0x1f, 0xff],
+ "M5N",
+ dict_fspec_local(x_re_ga, "048_RE_M5N_04", "GA", "204775")
+ )
+ validator.add_re_dissection(
+ [0x40, 0x10, 0x20, 0x00],
+ "M5N",
+ dict_fspec_local(x_re_ga, "048_RE_M5N_04", "GA", "-204800")
+ )
+ x_re_em1 = {
+ "asterix.048_RE_M5N_05_V": "0",
+ "asterix.048_RE_M5N_05_G": "0",
+ "asterix.048_RE_M5N_05_L": "0",
+ "asterix.048_RE_M5N_05_MODE3A": "0"
+ }
+ validator.add_re_dissection(
+ [0x40, 0x08, 0x80, 0x00],
+ "M5N",
+ dict_fspec_local(x_re_em1, "048_RE_M5N_05", "V", "1")
+ )
+ validator.add_re_dissection(
+ [0x40, 0x08, 0x40, 0x00],
+ "M5N",
+ dict_fspec_local(x_re_em1, "048_RE_M5N_05", "G", "1")
+ )
+ validator.add_re_dissection(
+ [0x40, 0x08, 0x20, 0x00],
+ "M5N",
+ dict_fspec_local(x_re_em1, "048_RE_M5N_05", "L", "1")
+ )
+ validator.add_re_dissection(
+ [0x40, 0x08, 0x0f, 0xff],
+ "M5N",
+ dict_fspec_local(x_re_em1, "048_RE_M5N_05", "MODE3A", "4095")
+ )
+ validator.add_re_dissection(
+ [0x40, 0x04, 0x7f],
+ "M5N",
+ fspec_local("048_RE_M5N_06", "TOS", "0.9921875")
+ )
+ validator.add_re_dissection(
+ [0x40, 0x04, 0x80],
+ "M5N",
+ fspec_local("048_RE_M5N_06", "TOS", "-1")
+ )
+ x_re_xp = {
+ "asterix.048_RE_M5N_07_XP": "0",
+ "asterix.048_RE_M5N_07_X5": "0",
+ "asterix.048_RE_M5N_07_XC": "0",
+ "asterix.048_RE_M5N_07_X3": "0",
+ "asterix.048_RE_M5N_07_X2": "0",
+ "asterix.048_RE_M5N_07_X1": "0"
+ }
+ validator.add_re_dissection(
+ [0x40, 0x02, 0x20],
+ "M5N",
+ dict_fspec_local(x_re_xp, "048_RE_M5N_07", "XP", "1")
+ )
+ validator.add_re_dissection(
+ [0x40, 0x02, 0x10],
+ "M5N",
+ dict_fspec_local(x_re_xp, "048_RE_M5N_07", "X5", "1")
+ )
+ validator.add_re_dissection(
+ [0x40, 0x02, 0x08],
+ "M5N",
+ dict_fspec_local(x_re_xp, "048_RE_M5N_07", "XC", "1")
+ )
+ validator.add_re_dissection(
+ [0x40, 0x02, 0x04],
+ "M5N",
+ dict_fspec_local(x_re_xp, "048_RE_M5N_07", "X3", "1")
+ )
+ validator.add_re_dissection(
+ [0x40, 0x02, 0x02],
+ "M5N",
+ dict_fspec_local(x_re_xp, "048_RE_M5N_07", "X2", "1")
+ )
+ validator.add_re_dissection(
+ [0x40, 0x02, 0x01],
+ "M5N",
+ dict_fspec_local(x_re_xp, "048_RE_M5N_07", "X1", "1")
+ )
+ validator.add_re_dissection(
+ [0x40, 0x01, 0x80, 0x1f],
+ "M5N",
+ fspec_local("048_RE_M5N_08", "FOM", "31")
+ )
+ validator.add_re_dissection(
+ [0x20, 0x06],
+ "M4E",
+ {
+ "asterix.048_RE_M4E_FOE_FRI": "3",
+ "asterix.FX": "0"
+ }
+ )
+ validator.add_re_dissection(
+ [0x10, 0x80, 0xff],
+ "RPC",
+ fspec_local("048_RE_RPC_01", "SCO", "255")
+ )
+ validator.add_re_dissection(
+ [0x10, 0x40, 0xff, 0xff],
+ "RPC",
+ fspec_local("048_RE_RPC_02", "SCR", "6553.5")
+ )
+ validator.add_re_dissection(
+ [0x10, 0x20, 0xff, 0xff],
+ "RPC",
+ fspec_local("048_RE_RPC_03", "RW", "255.99609375")
+ )
+ validator.add_re_dissection(
+ [0x10, 0x10, 0xff, 0xff],
+ "RPC",
+ fspec_local("048_RE_RPC_04", "AR", "255.99609375")
+ )
+ validator.add_re_dissection(
+ [0x08, 0xff, 0xff, 0xff],
+ "ERR",
+ {
+ "asterix.048_RE_ERR_RHO": "65535.99609375"
+ }
+ )
+ '''
+
+ validator.check_dissections()
+
+ def test_undefined_value_handling(self, asterix_re_validator):
+ '''verifies that the dissector can dissect undefined field values by
+ setting the maximum value of bits or by setting all undefined bits'''
+
+ validator = asterix_re_validator(48, [0x01, 0x01, 0x01, 0x02])
+
+ validator.add_dissection(
+ [0x08, 0x10, 0x00],
+ "asterix.048_070",
+ {
+ "asterix.048_070_V": "0",
+ "asterix.048_070_G": "0",
+ "asterix.048_070_L": "0",
+ "asterix.048_070_MODE3A": "0"
+ }
+ )
+ validator.add_dissection(
+ [0x01, 0x10, 0xf0, 0x00],
+ "asterix.048_161",
+ {
+ "asterix.048_161_TRN": "0"
+ }
+ )
+ validator.add_dissection(
+ [0x01, 0x02, 0x01, 0x0e],
+ "asterix.048_170",
+ {
+ "asterix.048_170_CNF": "0",
+ "asterix.048_170_RAD": "0",
+ "asterix.048_170_DOU": "0",
+ "asterix.048_170_MAH": "0",
+ "asterix.048_170_CDM": "0",
+ "asterix.048_170_TRE": "0",
+ "asterix.048_170_GHO": "0",
+ "asterix.048_170_SUP": "0",
+ "asterix.048_170_TCC": "0",
+ "asterix.FX": "0"
+ }
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x40, 0xfe],
+ "asterix.048_030",
+ {
+ "asterix.048_030_Subitem": "127",
+ "asterix.FX": "0"
+ }
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x20, 0xf0, 0x00],
+ "asterix.048_080",
+ {
+ "asterix.048_080_QA4": "0",
+ "asterix.048_080_QA2": "0",
+ "asterix.048_080_QA1": "0",
+ "asterix.048_080_QB4": "0",
+ "asterix.048_080_QB2": "0",
+ "asterix.048_080_QB1": "0",
+ "asterix.048_080_QC4": "0",
+ "asterix.048_080_QC2": "0",
+ "asterix.048_080_QC1": "0",
+ "asterix.048_080_QD4": "0",
+ "asterix.048_080_QD2": "0",
+ "asterix.048_080_QD1": "0"
+ }
+ )
+ '''TODO: A,B,C,D values need to go to single subitem 'MODEC'
+ validator.add_dissection(
+ [0x01, 0x01, 0x10, 0x30, 0x00, 0xf0, 0x00],
+ "asterix.048_100",
+ {
+ "asterix.048_100_V": "0",
+ "asterix.048_100_G": "0",
+ "asterix.048_100_A1": "0",
+ "asterix.048_100_C2": "0",
+ "asterix.048_100_A2": "0",
+ "asterix.048_100_C4": "0",
+ "asterix.048_100_A4": "0",
+ "asterix.048_100_B1": "0",
+ "asterix.048_100_D1": "0",
+ "asterix.048_100_B2": "0",
+ "asterix.048_100_D2": "0",
+ "asterix.048_100_B4": "0",
+ "asterix.048_100_D4": "0",
+ "asterix.048_100_QC1": "0",
+ "asterix.048_100_QA1": "0",
+ "asterix.048_100_QC2": "0",
+ "asterix.048_100_QA2": "0",
+ "asterix.048_100_QC4": "0",
+ "asterix.048_100_QA4": "0",
+ "asterix.048_100_QB1": "0",
+ "asterix.048_100_QD1": "0",
+ "asterix.048_100_QB2": "0",
+ "asterix.048_100_QD2": "0",
+ "asterix.048_100_QB4": "0",
+ "asterix.048_100_QD4": "0"
+ }
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x04, 0x80, 0x7c, 0x00],
+ "asterix.048_120",
+ {
+ "asterix.fspec": "",
+ "asterix.048_120_01":
+ {
+ "asterix.048_120_01_D": "0",
+ "asterix.048_120_01_CAL": "0"
+ }
+ }
+ )
+ '''
+ validator.add_dissection(
+ [0x01, 0x01, 0x04, 0x3e],
+ "asterix.048_120",
+ {
+ "asterix.fspec": ""
+ }
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x02, 0x01, 0x00],
+ "asterix.048_230",
+ {
+ "asterix.048_230_COM": "0",
+ "asterix.048_230_STAT": "0",
+ "asterix.048_230_SI": "0",
+ "asterix.048_230_MSSC": "0",
+ "asterix.048_230_ARC": "0",
+ "asterix.048_230_AIC": "0",
+ "asterix.048_230_B1A": "0",
+ "asterix.048_230_B1B": "0"
+ }
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x01, 0x20, 0x10, 0x00],
+ "asterix.048_050",
+ {
+ "asterix.048_050_V": "0",
+ "asterix.048_050_G": "0",
+ "asterix.048_050_L": "0",
+ "asterix.048_050_MODE2": "0"
+ }
+ )
+ validator.add_dissection(
+ [0x01, 0x01, 0x01, 0x10, 0xe0],
+ "asterix.048_065",
+ {
+ "asterix.048_065_QA4": "0",
+ "asterix.048_065_QA2": "0",
+ "asterix.048_065_QA1": "0",
+ "asterix.048_065_QB2": "0",
+ "asterix.048_065_QB1": "0"
+ }
+ )
+ '''TODO: re-enable RE and SP tests when implemented
+ x_re_md5 = {
+ "asterix.048_RE_MD5_01_M5": "0",
+ "asterix.048_RE_MD5_01_ID": "0",
+ "asterix.048_RE_MD5_01_DA": "0",
+ "asterix.048_RE_MD5_01_M1": "0",
+ "asterix.048_RE_MD5_01_M2": "0",
+ "asterix.048_RE_MD5_01_M3": "0",
+ "asterix.048_RE_MD5_01_MC": "0"
+ }
+ validator.add_re_dissection(
+ [0x80, 0x80, 0x01, 0x00],
+ "MD5",
+ dict_fspec_local(x_re_md5, "048_RE_MD5_01", "M5", "0")
+ )
+ x_re_pmn = {
+ "asterix.048_RE_MD5_02_PIN": "0",
+ "asterix.048_RE_MD5_02_NAV": "0",
+ "asterix.048_RE_MD5_02_NAT": "0",
+ "asterix.048_RE_MD5_02_MIS": "0"
+ }
+ validator.add_re_dissection(
+ [0x80, 0x40, 0xc0, 0x00, 0xc0, 0xc0],
+ "MD5",
+ dict_fspec_local(x_re_pmn, "048_RE_MD5_02", "PIN", "0")
+ )
+ x_re_em1 = {
+ "asterix.048_RE_MD5_05_V": "0",
+ "asterix.048_RE_MD5_05_G": "0",
+ "asterix.048_RE_MD5_05_L": "0",
+ "asterix.048_RE_MD5_05_MODE3A": "0"
+ }
+ validator.add_re_dissection(
+ [0x80, 0x08, 0x10, 0x00],
+ "MD5",
+ dict_fspec_local(x_re_em1, "048_RE_MD5_05", "V", "0")
+ )
+ x_re_md5 = {
+ "asterix.048_RE_M5N_01_M5": "0",
+ "asterix.048_RE_M5N_01_ID": "0",
+ "asterix.048_RE_M5N_01_DA": "0",
+ "asterix.048_RE_M5N_01_M1": "0",
+ "asterix.048_RE_M5N_01_M2": "0",
+ "asterix.048_RE_M5N_01_M3": "0",
+ "asterix.048_RE_M5N_01_MC": "0"
+ }
+ validator.add_re_dissection(
+ [0x40, 0x80, 0x01, 0x00],
+ "M5N",
+ dict_fspec_local(x_re_md5, "048_RE_M5N_01", "M5", "0")
+ )
+ x_re_pmn = {
+ "asterix.048_RE_M5N_02_PIN": "0",
+ "asterix.048_RE_M5N_02_NOV": "0",
+ "asterix.048_RE_M5N_02_NO": "0"
+ }
+ validator.add_re_dissection(
+ [0x40, 0x40, 0xc0, 0x00, 0xf0, 0x00],
+ "M5N",
+ dict_fspec_local(x_re_pmn, "048_RE_M5N_02", "PIN", "0")
+ )
+ x_re_em1 = {
+ "asterix.048_RE_M5N_05_V": "0",
+ "asterix.048_RE_M5N_05_G": "0",
+ "asterix.048_RE_M5N_05_L": "0",
+ "asterix.048_RE_M5N_05_MODE3A": "0"
+ }
+ validator.add_re_dissection(
+ [0x40, 0x08, 0x10, 0x00],
+ "M5N",
+ dict_fspec_local(x_re_em1, "048_RE_M5N_05", "V", "0")
+ )
+ validator.add_re_dissection(
+ [0x40, 0x01, 0x80, 0xe0],
+ "M5N",
+ fspec_local("048_RE_M5N_08", "FOM", "0")
+ )
+ validator.add_re_dissection(
+ [0x20, 0xf8],
+ "M4E",
+ {
+ "asterix.048_RE_M4E_FOE_FRI": "0",
+ "asterix.FX": "0"
+ }
+ )
+ validator.add_re_dissection(
+ [0x20, 0x01, 0x00],
+ "M4E",
+ {
+ "asterix.048_RE_M4E_FOE_FRI": "0",
+ "asterix.FX": "1"
+ }
+ )
+ '''
+
+ validator.check_dissections()
+
+
+class TestCategory063:
+ '''
+ Unittest case for ASTERIX Category 063
+
+ Online specification:
+ https://www.eurocontrol.int/publications/cat063-sensor-status-messages-part-10
+
+ Part 10: Category 63 (1.4)
+ Sensor Status Messages
+
+ Standard User Application Profile
+
+ FRN Data Item Information Length
+ 1 I063/010 Data Source Identifier 2
+ 2 I063/015 Service Identification 1
+ 3 I063/030 Time of Message 3
+ 4 I063/050 Sensor Identifier 2
+ 5 I063/060 Sensor Configuration and Status 1+1
+ 6 I063/070 Time Stamping Bias 2
+ 7 I063/080 SSR/Mode S Range Gain and Bias 4
+ FX - Field extension indicator -
+ 8 I063/081 SSR/Mode S Azimuth Bias 2
+ 9 I063/090 PSR Range Gain and Bias 4
+ 10 I063/091 PSR Azimuth Bias 2
+ 11 I063/092 PSR Elevation Bias 2
+ 12 - spare -
+ 13 RE Reserved Expansion Field 1+1+
+ 14 SP Special Purpose Field 1+1+
+ FX - Field extension indicator -
+ '''
+
+ def test_for_fields(self, asterix_validator):
+ '''verifies existence of all fields and their maximum value'''
+
+ validator = asterix_validator(63)
+
+ validator.add_dissection(
+ [0x80, 0xff, 0x00],
+ "asterix.063_010",
+ {
+ "asterix.063_010_SAC": "0xff",
+ "asterix.063_010_SIC": "0x00"
+ }
+ )
+ validator.add_dissection(
+ [0x80, 0x00, 0xff],
+ "asterix.063_010",
+ {
+ "asterix.063_010_SAC": "0x00",
+ "asterix.063_010_SIC": "0xff"
+ }
+ )
+ validator.add_dissection(
+ [0x40, 0xff],
+ "asterix.063_015",
+ {
+ "asterix.063_015_VALUE": "0xff"
+ }
+ )
+ validator.add_dissection(
+ [0x20, 0xa8, 0xbf, 0xff],
+ "asterix.063_030",
+ {
+ "asterix.063_030_VALUE": "86399.9921875"
+ }
+ )
+ validator.add_dissection(
+ [0x10, 0xff, 0x00],
+ "asterix.063_050",
+ {
+ "asterix.063_050_SAC": "0xff",
+ "asterix.063_050_SIC": "0x00"
+ }
+ )
+ validator.add_dissection(
+ [0x10, 0x00, 0xff],
+ "asterix.063_050",
+ {
+ "asterix.063_050_SAC": "0x00",
+ "asterix.063_050_SIC": "0xff"
+ }
+ )
+ validator.add_dissection(
+ [0x08, 0xc0],
+ "asterix.063_060",
+ {
+ "asterix.063_060_CON": "3",
+ "asterix.063_060_PSR": "0",
+ "asterix.063_060_SSR": "0",
+ "asterix.063_060_MDS": "0",
+ "asterix.063_060_ADS": "0",
+ "asterix.063_060_MLT": "0",
+ "asterix.FX": "0"
+ }
+ )
+ validator.add_dissection(
+ [0x08, 0x20],
+ "asterix.063_060",
+ {
+ "asterix.063_060_CON": "0",
+ "asterix.063_060_PSR": "1",
+ "asterix.063_060_SSR": "0",
+ "asterix.063_060_MDS": "0",
+ "asterix.063_060_ADS": "0",
+ "asterix.063_060_MLT": "0",
+ "asterix.FX": "0"
+ }
+ )
+ validator.add_dissection(
+ [0x08, 0x10],
+ "asterix.063_060",
+ {
+ "asterix.063_060_CON": "0",
+ "asterix.063_060_PSR": "0",
+ "asterix.063_060_SSR": "1",
+ "asterix.063_060_MDS": "0",
+ "asterix.063_060_ADS": "0",
+ "asterix.063_060_MLT": "0",
+ "asterix.FX": "0"
+ }
+ )
+ validator.add_dissection(
+ [0x08, 0x08],
+ "asterix.063_060",
+ {
+ "asterix.063_060_CON": "0",
+ "asterix.063_060_PSR": "0",
+ "asterix.063_060_SSR": "0",
+ "asterix.063_060_MDS": "1",
+ "asterix.063_060_ADS": "0",
+ "asterix.063_060_MLT": "0",
+ "asterix.FX": "0"
+ }
+ )
+ validator.add_dissection(
+ [0x08, 0x04],
+ "asterix.063_060",
+ {
+ "asterix.063_060_CON": "0",
+ "asterix.063_060_PSR": "0",
+ "asterix.063_060_SSR": "0",
+ "asterix.063_060_MDS": "0",
+ "asterix.063_060_ADS": "1",
+ "asterix.063_060_MLT": "0",
+ "asterix.FX": "0"
+ }
+ )
+ validator.add_dissection(
+ [0x08, 0x02],
+ "asterix.063_060",
+ {
+ "asterix.063_060_CON": "0",
+ "asterix.063_060_PSR": "0",
+ "asterix.063_060_SSR": "0",
+ "asterix.063_060_MDS": "0",
+ "asterix.063_060_ADS": "0",
+ "asterix.063_060_MLT": "1",
+ "asterix.FX": "0"
+ }
+ )
+ validator.add_dissection(
+ [0x08, 0x01, 0x80],
+ "asterix.063_060",
+ {
+ "asterix.063_060_CON": "0",
+ "asterix.063_060_PSR": "0",
+ "asterix.063_060_SSR": "0",
+ "asterix.063_060_MDS": "0",
+ "asterix.063_060_ADS": "0",
+ "asterix.063_060_MLT": "0",
+ "asterix.063_060_OPS": "1",
+ "asterix.063_060_ODP": "0",
+ "asterix.063_060_OXT": "0",
+ "asterix.063_060_MSC": "0",
+ "asterix.063_060_TSV": "0",
+ "asterix.063_060_NPW": "0",
+ "asterix.FX": "0"
+ }
+ )
+ validator.add_dissection(
+ [0x08, 0x01, 0x40],
+ "asterix.063_060",
+ {
+ "asterix.063_060_CON": "0",
+ "asterix.063_060_PSR": "0",
+ "asterix.063_060_SSR": "0",
+ "asterix.063_060_MDS": "0",
+ "asterix.063_060_ADS": "0",
+ "asterix.063_060_MLT": "0",
+ "asterix.063_060_OPS": "0",
+ "asterix.063_060_ODP": "1",
+ "asterix.063_060_OXT": "0",
+ "asterix.063_060_MSC": "0",
+ "asterix.063_060_TSV": "0",
+ "asterix.063_060_NPW": "0",
+ "asterix.FX": "0"
+ }
+ )
+ validator.add_dissection(
+ [0x08, 0x01, 0x20],
+ "asterix.063_060",
+ {
+ "asterix.063_060_CON": "0",
+ "asterix.063_060_PSR": "0",
+ "asterix.063_060_SSR": "0",
+ "asterix.063_060_MDS": "0",
+ "asterix.063_060_ADS": "0",
+ "asterix.063_060_MLT": "0",
+ "asterix.063_060_OPS": "0",
+ "asterix.063_060_ODP": "0",
+ "asterix.063_060_OXT": "1",
+ "asterix.063_060_MSC": "0",
+ "asterix.063_060_TSV": "0",
+ "asterix.063_060_NPW": "0",
+ "asterix.FX": "0"
+ }
+ )
+ validator.add_dissection(
+ [0x08, 0x01, 0x10],
+ "asterix.063_060",
+ {
+ "asterix.063_060_CON": "0",
+ "asterix.063_060_PSR": "0",
+ "asterix.063_060_SSR": "0",
+ "asterix.063_060_MDS": "0",
+ "asterix.063_060_ADS": "0",
+ "asterix.063_060_MLT": "0",
+ "asterix.063_060_OPS": "0",
+ "asterix.063_060_ODP": "0",
+ "asterix.063_060_OXT": "0",
+ "asterix.063_060_MSC": "1",
+ "asterix.063_060_TSV": "0",
+ "asterix.063_060_NPW": "0",
+ "asterix.FX": "0"
+ }
+ )
+ validator.add_dissection(
+ [0x08, 0x01, 0x08],
+ "asterix.063_060",
+ {
+ "asterix.063_060_CON": "0",
+ "asterix.063_060_PSR": "0",
+ "asterix.063_060_SSR": "0",
+ "asterix.063_060_MDS": "0",
+ "asterix.063_060_ADS": "0",
+ "asterix.063_060_MLT": "0",
+ "asterix.063_060_OPS": "0",
+ "asterix.063_060_ODP": "0",
+ "asterix.063_060_OXT": "0",
+ "asterix.063_060_MSC": "0",
+ "asterix.063_060_TSV": "1",
+ "asterix.063_060_NPW": "0",
+ "asterix.FX": "0"
+ }
+ )
+ validator.add_dissection(
+ [0x08, 0x01, 0x04],
+ "asterix.063_060",
+ {
+ "asterix.063_060_CON": "0",
+ "asterix.063_060_PSR": "0",
+ "asterix.063_060_SSR": "0",
+ "asterix.063_060_MDS": "0",
+ "asterix.063_060_ADS": "0",
+ "asterix.063_060_MLT": "0",
+ "asterix.063_060_OPS": "0",
+ "asterix.063_060_ODP": "0",
+ "asterix.063_060_OXT": "0",
+ "asterix.063_060_MSC": "0",
+ "asterix.063_060_TSV": "0",
+ "asterix.063_060_NPW": "1",
+ "asterix.FX": "0"
+ }
+ )
+ validator.add_dissection(
+ [0x04, 0xff, 0xff],
+ "asterix.063_070",
+ {
+ "asterix.063_070_VALUE": "-1"
+ }
+ )
+ validator.add_dissection(
+ [0x02, 0x7f, 0xff, 0x00, 0x00],
+ "asterix.063_080",
+ {
+ "asterix.063_080_SRG": "0.32767",
+ "asterix.063_080_SRB": "0"
+ }
+ )
+ validator.add_dissection(
+ [0x02, 0x80, 0x00, 0x00, 0x00],
+ "asterix.063_080",
+ {
+ "asterix.063_080_SRG": "-0.32768",
+ "asterix.063_080_SRB": "0"
+ }
+ )
+ validator.add_dissection(
+ [0x02, 0x00, 0x00, 0x7f, 0xff],
+ "asterix.063_080",
+ {
+ "asterix.063_080_SRG": "0",
+ "asterix.063_080_SRB": "255.9921875"
+ }
+ )
+ validator.add_dissection(
+ [0x02, 0x00, 0x00, 0x80, 0x00],
+ "asterix.063_080",
+ {
+ "asterix.063_080_SRG": "0",
+ "asterix.063_080_SRB": "-256"
+ }
+ )
+ validator.add_dissection(
+ [0x01, 0x80, 0x7f, 0xff],
+ "asterix.063_081",
+ {
+ "asterix.063_081_VALUE": "179.994506835938"
+ }
+ )
+ validator.add_dissection(
+ [0x01, 0x80, 0x80, 0x00],
+ "asterix.063_081",
+ {
+ "asterix.063_081_VALUE": "-180"
+ }
+ )
+ validator.add_dissection(
+ [0x01, 0x40, 0x7f, 0xff, 0x00, 0x00],
+ "asterix.063_090",
+ {
+ "asterix.063_090_PRG": "0.32767",
+ "asterix.063_090_PRB": "0"
+ }
+ )
+ validator.add_dissection(
+ [0x01, 0x40, 0x80, 0x00, 0x00, 0x00],
+ "asterix.063_090",
+ {
+ "asterix.063_090_PRG": "-0.32768",
+ "asterix.063_090_PRB": "0"
+ }
+ )
+ validator.add_dissection(
+ [0x01, 0x40, 0x00, 0x00, 0x7f, 0xff],
+ "asterix.063_090",
+ {
+ "asterix.063_090_PRG": "0",
+ "asterix.063_090_PRB": "255.9921875"
+ }
+ )
+ validator.add_dissection(
+ [0x01, 0x40, 0x00, 0x00, 0x80, 0x00],
+ "asterix.063_090",
+ {
+ "asterix.063_090_PRG": "0",
+ "asterix.063_090_PRB": "-256"
+ }
+ )
+ validator.add_dissection(
+ [0x01, 0x20, 0x7f, 0xff],
+ "asterix.063_091",
+ {
+ "asterix.063_091_VALUE": "179.994506835938"
+ }
+ )
+ validator.add_dissection(
+ [0x01, 0x20, 0x80, 0x00],
+ "asterix.063_091",
+ {
+ "asterix.063_091_VALUE": "-180"
+ }
+ )
+ validator.add_dissection(
+ [0x01, 0x10, 0x7f, 0xff],
+ "asterix.063_092",
+ {
+ "asterix.063_092_VALUE": "179.994506835938"
+ }
+ )
+ validator.add_dissection(
+ [0x01, 0x10, 0x80, 0x00],
+ "asterix.063_092",
+ {
+ "asterix.063_092_VALUE": "-180"
+ }
+ )
+
+ validator.check_dissections()
+
+ def test_undefined_value_handling(self, asterix_validator):
+ '''verifies that the dissector can dissect undefined field values by
+ setting the maximum value of bits or by setting all undefined bits'''
+
+ validator = asterix_validator(63)
+
+ validator.add_dissection(
+ [0x01, 0x08],
+ "asterix.spare",
+ ""
+ )
+ '''TODO: re-enable RE and SP tests when implemented
+ validator.add_dissection(
+ [0x01, 0x04, 0x02, 0x00],
+ "asterix.063_RE",
+ {
+ "asterix.re_field_len": "2",
+ "asterix.fspec": ""
+ }
+ )
+ validator.add_dissection(
+ [0x01, 0x04, 0x10, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
+ 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff],
+ "asterix.063_RE",
+ {
+ "asterix.fspec": "",
+ "asterix.re_field_len": "16"
+ }
+ )
+ validator.add_dissection(
+ [0x01, 0x02, 0x01],
+ "asterix.063_SP",
+ ""
+ )
+ validator.add_dissection(
+ [0x01, 0x02, 0x10, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
+ 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff],
+ "asterix.063_SP",
+ ""
+ )
+ '''
+
+ validator.check_dissections()
+
+
+class TestCategory065:
+ '''
+ Unittest case for ASTERIX Category 065
+
+ Online specification:
+ https://www.eurocontrol.int/publications/cat065-surveillance-data-processing-system-sdps-service-status-messages-part-15
+ https://www.eurocontrol.int/publications/cat065-coding-rules-reserved-expansion-field-part-15-appendix
+
+ Part 15 Category 65 (1.4)
+ SDPS Service Status Reports
+
+ Standard User Application Profile
+
+ FRN Data Item Information Length
+ 1 I065/010 Data Source Identifier 2
+ 2 I065/000 Message Type 1
+ 3 I065/015 Service Identification 1
+ 4 I065/030 Time of Message 3
+ 5 I065/020 Batch Number 1
+ 6 I065/040 SDPS Configuration and Status 1
+ 7 I065/050 Service Status Report 1
+ FX - Field extension indicator -
+ 8 - Spare -
+ 9 - Spare -
+ 10 - Spare -
+ 11 - Spare -
+ 12 - Spare -
+ 13 RE Reserved Expansion Field 1+1+
+ 14 SP Special Purpose Field 1+1+
+ FX - Field extension indicator -
+ '''
+
+ def test_for_fields(self, asterix_validator):
+ '''verifies existence of all fields and their maximum value'''
+
+ validator = asterix_validator(65)
+
+ validator.add_dissection(
+ [0x80, 0xff, 0x00],
+ "asterix.065_010",
+ {
+ "asterix.065_010_SAC": "0xff",
+ "asterix.065_010_SIC": "0x00"
+ }
+ )
+ validator.add_dissection(
+ [0x80, 0x00, 0xff],
+ "asterix.065_010",
+ {
+ "asterix.065_010_SAC": "0x00",
+ "asterix.065_010_SIC": "0xff"
+ }
+ )
+ validator.add_dissection(
+ [0x40, 0x03],
+ "asterix.065_000",
+ {
+ "asterix.065_000_VALUE": "3"
+ }
+ )
+ validator.add_dissection(
+ [0x20, 0xff],
+ "asterix.065_015",
+ {
+ "asterix.065_015_VALUE": "0xff"
+ }
+ )
+ validator.add_dissection(
+ [0x10, 0xa8, 0xbf, 0xff],
+ "asterix.065_030",
+ {
+ "asterix.065_030_VALUE": "86399.9921875"
+ }
+ )
+ validator.add_dissection(
+ [0x08, 0xff],
+ "asterix.065_020",
+ {
+ "asterix.065_020_VALUE": "255"
+ }
+ )
+ validator.add_dissection(
+ [0x04, 0xc0],
+ "asterix.065_040",
+ {
+ "asterix.065_040_NOGO": "3",
+ "asterix.065_040_OVL": "0",
+ "asterix.065_040_TSV": "0",
+ "asterix.065_040_PSS": "0",
+ "asterix.065_040_STTN": "0"
+ }
+ )
+ validator.add_dissection(
+ [0x04, 0x20],
+ "asterix.065_040",
+ {
+ "asterix.065_040_NOGO": "0",
+ "asterix.065_040_OVL": "1",
+ "asterix.065_040_TSV": "0",
+ "asterix.065_040_PSS": "0",
+ "asterix.065_040_STTN": "0"
+ }
+ )
+ validator.add_dissection(
+ [0x04, 0x10],
+ "asterix.065_040",
+ {
+ "asterix.065_040_NOGO": "0",
+ "asterix.065_040_OVL": "0",
+ "asterix.065_040_TSV": "1",
+ "asterix.065_040_PSS": "0",
+ "asterix.065_040_STTN": "0"
+ }
+ )
+ validator.add_dissection(
+ [0x04, 0x0c],
+ "asterix.065_040",
+ {
+ "asterix.065_040_NOGO": "0",
+ "asterix.065_040_OVL": "0",
+ "asterix.065_040_TSV": "0",
+ "asterix.065_040_PSS": "3",
+ "asterix.065_040_STTN": "0"
+ }
+ )
+ validator.add_dissection(
+ [0x04, 0x02],
+ "asterix.065_040",
+ {
+ "asterix.065_040_NOGO": "0",
+ "asterix.065_040_OVL": "0",
+ "asterix.065_040_TSV": "0",
+ "asterix.065_040_PSS": "0",
+ "asterix.065_040_STTN": "1"
+ }
+ )
+ validator.add_dissection(
+ [0x02, 0xff],
+ "asterix.065_050",
+ {
+ "asterix.065_050_VALUE": "255"
+ }
+ )
+ '''TODO: re-enable RE and SP tests when implemented
+ validator.add_dissection(
+ [0x01, 0x04, 0x02, 0x00],
+ "asterix.065_RE",
+ {
+ "asterix.re_field_len": "2",
+ "asterix.fspec": ""
+ }
+ )
+ validator.add_dissection(
+ [0x01, 0x04, 0x0a, 0x80, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00],
+ "asterix.065_RE",
+ {
+ "asterix.re_field_len": "10",
+ "asterix.fspec": "",
+ "asterix.065_RE_SRP":
+ {
+ "asterix.065_RE_SRP_Latitude": "90",
+ "asterix.065_RE_SRP_Longitude": "0"
+ }
+ }
+ )
+ validator.add_dissection(
+ [0x01, 0x04, 0x0a, 0x80, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00],
+ "asterix.065_RE",
+ {
+ "asterix.re_field_len": "10",
+ "asterix.fspec": "",
+ "asterix.065_RE_SRP":
+ {
+ "asterix.065_RE_SRP_Latitude": "-90",
+ "asterix.065_RE_SRP_Longitude": "0"
+ }
+ }
+ )
+ validator.add_dissection(
+ [0x01, 0x04, 0x0a, 0x80, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00,
+ 0x00],
+ "asterix.065_RE",
+ {
+ "asterix.re_field_len": "10",
+ "asterix.fspec": "",
+ "asterix.065_RE_SRP":
+ {
+ "asterix.065_RE_SRP_Latitude": "0",
+ "asterix.065_RE_SRP_Longitude": "180"
+ }
+ }
+ )
+ validator.add_dissection(
+ [0x01, 0x04, 0x0a, 0x80, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00,
+ 0x00],
+ "asterix.065_RE",
+ {
+ "asterix.re_field_len": "10",
+ "asterix.fspec": "",
+ "asterix.065_RE_SRP":
+ {
+ "asterix.065_RE_SRP_Latitude": "0",
+ "asterix.065_RE_SRP_Longitude": "-180"
+ }
+ }
+ )
+ validator.add_dissection(
+ [0x01, 0x04, 0x04, 0x40, 0xff, 0xfc],
+ "asterix.065_RE",
+ {
+ "asterix.re_field_len": "4",
+ "asterix.fspec": "",
+ "asterix.065_RE_ARL":
+ {
+ "asterix.065_RE_ARL_ARL": "65532"
+ }
+ }
+ )
+ '''
+
+ validator.check_dissections()
+
+ def test_undefined_value_handling(self, asterix_validator):
+ '''verifies that the dissector can dissect undefined field values by
+ setting the maximum value of bits or by setting all undefined bits'''
+
+ validator = asterix_validator(65)
+
+ validator.add_dissection(
+ [0x40, 0xff],
+ "asterix.065_000",
+ {
+ "asterix.065_000_VALUE": "255"
+ }
+ )
+ validator.add_dissection(
+ [0x10, 0xff, 0xff, 0xff],
+ "asterix.065_030",
+ {
+ "asterix.065_030_VALUE": "131071.9921875"
+ }
+ )
+ validator.add_dissection(
+ [0x04, 0x01],
+ "asterix.065_040",
+ {
+ "asterix.065_040_NOGO": "0",
+ "asterix.065_040_OVL": "0",
+ "asterix.065_040_TSV": "0",
+ "asterix.065_040_PSS": "0",
+ "asterix.065_040_STTN": "0"
+ }
+ )
+ validator.add_dissection(
+ [0x01, 0x80],
+ "asterix.spare",
+ ""
+ )
+ validator.add_dissection(
+ [0x01, 0x40],
+ "asterix.spare",
+ ""
+ )
+ validator.add_dissection(
+ [0x01, 0x20],
+ "asterix.spare",
+ ""
+ )
+ validator.add_dissection(
+ [0x01, 0x10],
+ "asterix.spare",
+ ""
+ )
+ validator.add_dissection(
+ [0x01, 0x08],
+ "asterix.spare",
+ ""
+ )
+ '''TODO: re-enable RE and SP tests when implemented
+ validator.add_dissection(
+ [0x01, 0x04, 0x0a, 0x80, 0x7f, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00,
+ 0x00],
+ "asterix.065_RE",
+ {
+ "asterix.re_field_len": "10",
+ "asterix.fspec": "",
+ "asterix.065_RE_SRP":
+ {
+ "asterix.065_RE_SRP_Latitude": "359.999999832362",
+ "asterix.065_RE_SRP_Longitude": "0"
+ }
+ }
+ )
+ validator.add_dissection(
+ [0x01, 0x04, 0x0a, 0x80, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00],
+ "asterix.065_RE",
+ {
+ "asterix.re_field_len": "10",
+ "asterix.fspec": "",
+ "asterix.065_RE_SRP":
+ {
+ "asterix.065_RE_SRP_Latitude": "-360",
+ "asterix.065_RE_SRP_Longitude": "0"
+ }
+ }
+ )
+ validator.add_dissection(
+ [0x01, 0x04, 0x0a, 0x80, 0x00, 0x00, 0x00, 0x00, 0x7f, 0xff, 0xff,
+ 0xff],
+ "asterix.065_RE",
+ {
+ "asterix.re_field_len": "10",
+ "asterix.fspec": "",
+ "asterix.065_RE_SRP":
+ {
+ "asterix.065_RE_SRP_Latitude": "0",
+ "asterix.065_RE_SRP_Longitude": "359.999999832362"
+ }
+ }
+ )
+ validator.add_dissection(
+ [0x01, 0x04, 0x0a, 0x80, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
+ 0x00],
+ "asterix.065_RE",
+ {
+ "asterix.re_field_len": "10",
+ "asterix.fspec": "",
+ "asterix.065_RE_SRP":
+ {
+ "asterix.065_RE_SRP_Latitude": "0",
+ "asterix.065_RE_SRP_Longitude": "-360"
+ }
+ }
+ )
+ validator.add_dissection(
+ [0x01, 0x04, 0x04, 0x40, 0xff, 0xff],
+ "asterix.065_RE",
+ {
+ "asterix.re_field_len": "4",
+ "asterix.fspec": "",
+ "asterix.065_RE_ARL":
+ {
+ "asterix.065_RE_ARL_ARL": "65535"
+ }
+ }
+ )
+ validator.add_dissection(
+ [0x01, 0x02, 0x01],
+ "asterix.065_SP",
+ ""
+ )
+ validator.add_dissection(
+ [0x01, 0x02, 0x10, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
+ 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee, 0xff],
+ "asterix.065_SP",
+ ""
+ )
+ '''
+
+ validator.check_dissections()
diff --git a/test/suite_dissectors/group_netperfmeter.py b/test/suite_dissectors/group_netperfmeter.py
new file mode 100644
index 0000000..04b5aeb
--- /dev/null
+++ b/test/suite_dissectors/group_netperfmeter.py
@@ -0,0 +1,430 @@
+#
+# Wireshark tests
+#
+# Copyright 2021 by Thomas Dreibholz <dreibh [AT] simula.no>
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+'''NetPerfMeter tests'''
+
+import subprocess
+import pytest
+
+
+class TestNetperfmeter:
+
+ def test_netperfmeter_test_control(self, cmd_tshark, capture_file, test_env):
+ '''Checks whether the NetPerfMeter dissector correctly handles NetPerfMeter Control via SCTP.'''
+
+ # Test: Identify and decode NetPerfMeter Control via SCTP
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('netperfmeter.pcapng.gz'),
+ '-Y', 'sctp && netperfmeter && ((netperfmeter.message_type != 5) && (netperfmeter.message_type != 4))'
+ ),
+ encoding='utf-8', env=test_env)
+
+ result = ''.join([x.strip()+"\n" for x in stdout.splitlines()])
+
+ assert """\
+8 0.019316433 192.168.0.20 → 192.168.0.27 SCTP, NetPerfMeter 260 NetPerfMeter Add Flow
+10 0.038537718 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 88 NetPerfMeter Acknowledge
+14 0.326752277 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 88 NetPerfMeter Acknowledge
+18 0.333703948 192.168.0.20 → 192.168.0.27 SCTP, NetPerfMeter 276 SACK (Ack=1, Arwnd=106496) NetPerfMeter Add Flow
+19 0.340092259 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 104 SACK (Ack=1, Arwnd=106496) NetPerfMeter Acknowledge
+23 0.547510935 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 88 NetPerfMeter Acknowledge
+24 0.548336846 192.168.0.20 → 192.168.0.27 SCTP, NetPerfMeter 276 SACK (Ack=3, Arwnd=106496) NetPerfMeter Add Flow
+25 0.556582544 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 104 SACK (Ack=2, Arwnd=106496) NetPerfMeter Acknowledge
+28 0.768799828 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 88 NetPerfMeter Acknowledge
+29 0.769562835 192.168.0.20 → 192.168.0.27 SCTP, NetPerfMeter 276 SACK (Ack=5, Arwnd=106496) NetPerfMeter Add Flow
+30 0.777872331 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 104 SACK (Ack=3, Arwnd=106496) NetPerfMeter Acknowledge
+33 0.986925179 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 88 NetPerfMeter Acknowledge
+37 0.992962317 192.168.0.20 → 192.168.0.27 SCTP, NetPerfMeter 276 SACK (Ack=7, Arwnd=106496) NetPerfMeter Add Flow
+38 1.000163511 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 104 SACK (Ack=4, Arwnd=106496) NetPerfMeter Acknowledge
+41 1.245101828 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 88 NetPerfMeter Acknowledge
+45 1.248598897 192.168.0.20 → 192.168.0.27 SCTP, NetPerfMeter 276 SACK (Ack=9, Arwnd=106496) NetPerfMeter Add Flow
+46 1.257101874 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 104 SACK (Ack=5, Arwnd=106496) NetPerfMeter Acknowledge
+49 1.502117462 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 88 NetPerfMeter Acknowledge
+53 1.509411259 192.168.0.20 → 192.168.0.27 SCTP, NetPerfMeter 276 SACK (Ack=11, Arwnd=106496) NetPerfMeter Add Flow
+54 1.518356124 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 104 SACK (Ack=6, Arwnd=106496) NetPerfMeter Acknowledge
+57 1.762124577 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 88 NetPerfMeter Acknowledge
+61 1.768546288 192.168.0.20 → 192.168.0.27 SCTP, NetPerfMeter 276 SACK (Ack=13, Arwnd=106496) NetPerfMeter Add Flow
+62 1.776275446 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 104 SACK (Ack=7, Arwnd=106496) NetPerfMeter Acknowledge
+65 1.996204594 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 88 NetPerfMeter Acknowledge
+69 2.003084950 192.168.0.20 → 192.168.0.27 SCTP, NetPerfMeter 276 SACK (Ack=15, Arwnd=106496) NetPerfMeter Add Flow
+70 2.012723649 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 104 SACK (Ack=8, Arwnd=106496) NetPerfMeter Acknowledge
+73 2.253277911 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 88 NetPerfMeter Acknowledge
+77 2.259089003 192.168.0.20 → 192.168.0.27 SCTP, NetPerfMeter 276 SACK (Ack=17, Arwnd=106496) NetPerfMeter Add Flow
+78 2.267758027 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 104 SACK (Ack=9, Arwnd=106496) NetPerfMeter Acknowledge
+81 2.513148441 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 88 NetPerfMeter Acknowledge
+85 2.519444777 192.168.0.20 → 192.168.0.27 SCTP, NetPerfMeter 276 SACK (Ack=19, Arwnd=106496) NetPerfMeter Add Flow
+86 2.526479512 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 104 SACK (Ack=10, Arwnd=106496) NetPerfMeter Acknowledge
+89 2.772395957 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 88 NetPerfMeter Acknowledge
+93 2.781575331 192.168.0.20 → 192.168.0.27 SCTP, NetPerfMeter 276 SACK (Ack=21, Arwnd=106496) NetPerfMeter Add Flow
+94 2.789065601 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 104 SACK (Ack=11, Arwnd=106496) NetPerfMeter Acknowledge
+97 2.998736571 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 88 NetPerfMeter Acknowledge
+101 3.005046187 192.168.0.20 → 192.168.0.27 SCTP, NetPerfMeter 276 SACK (Ack=23, Arwnd=106496) NetPerfMeter Add Flow
+102 3.011025634 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 104 SACK (Ack=12, Arwnd=106496) NetPerfMeter Acknowledge
+105 3.255120658 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 88 NetPerfMeter Acknowledge
+109 3.262979723 192.168.0.20 → 192.168.0.27 SCTP, NetPerfMeter 276 SACK (Ack=25, Arwnd=106496) NetPerfMeter Add Flow
+110 3.270638348 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 104 SACK (Ack=13, Arwnd=106496) NetPerfMeter Acknowledge
+113 3.518145868 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 88 NetPerfMeter Acknowledge
+118 3.536880998 192.168.0.20 → 192.168.0.27 SCTP, NetPerfMeter 276 SACK (Ack=27, Arwnd=106496) NetPerfMeter Add Flow
+119 3.541489068 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 104 SACK (Ack=14, Arwnd=106496) NetPerfMeter Acknowledge
+123 3.776536632 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 88 NetPerfMeter Acknowledge
+124 3.777268092 192.168.0.20 → 192.168.0.27 SCTP, NetPerfMeter 276 SACK (Ack=29, Arwnd=106496) NetPerfMeter Add Flow
+125 3.784200653 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 104 SACK (Ack=15, Arwnd=106496) NetPerfMeter Acknowledge
+128 3.995220129 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 88 NetPerfMeter Acknowledge
+129 3.995907203 192.168.0.20 → 192.168.0.27 SCTP, NetPerfMeter 276 SACK (Ack=31, Arwnd=106496) NetPerfMeter Add Flow
+131 4.006264635 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 104 SACK (Ack=16, Arwnd=106496) NetPerfMeter Acknowledge
+135 4.215292054 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 88 NetPerfMeter Acknowledge
+136 4.216018889 192.168.0.20 → 192.168.0.27 SCTP, NetPerfMeter 276 SACK (Ack=33, Arwnd=106496) NetPerfMeter Add Flow
+137 4.222906817 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 104 SACK (Ack=17, Arwnd=106496) NetPerfMeter Acknowledge
+141 4.430858169 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 88 NetPerfMeter Acknowledge
+142 4.431619137 192.168.0.20 → 192.168.0.27 SCTP, NetPerfMeter 276 SACK (Ack=35, Arwnd=106496) NetPerfMeter Add Flow
+143 4.439186831 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 104 SACK (Ack=18, Arwnd=106496) NetPerfMeter Acknowledge
+147 4.647960736 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 88 NetPerfMeter Acknowledge
+148 4.648753903 192.168.0.20 → 192.168.0.27 SCTP, NetPerfMeter 276 SACK (Ack=37, Arwnd=106496) NetPerfMeter Add Flow
+149 4.654062259 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 104 SACK (Ack=19, Arwnd=106496) NetPerfMeter Acknowledge
+153 4.861696359 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 88 NetPerfMeter Acknowledge
+158 4.881874024 192.168.0.20 → 192.168.0.27 SCTP, NetPerfMeter 276 SACK (Ack=39, Arwnd=106496) NetPerfMeter Add Flow
+159 4.886932549 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 104 SACK (Ack=20, Arwnd=106496) NetPerfMeter Acknowledge
+163 5.095411239 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 88 NetPerfMeter Acknowledge
+184 5.101147570 192.168.0.20 → 192.168.0.27 SCTP, NetPerfMeter 96 SACK (Ack=41, Arwnd=106496) NetPerfMeter Start Measurement
+227 5.315482367 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 88 NetPerfMeter Acknowledge
+2084 15.615367349 192.168.0.20 → 192.168.0.27 SCTP, NetPerfMeter 80 NetPerfMeter Stop Measurement
+2086 16.091680420 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 88 NetPerfMeter Acknowledge
+2087 16.092542043 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2088 16.092542469 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2089 16.092542579 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2090 16.092542691 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2093 16.098744445 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2095 16.099492702 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2096 16.099493075 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2097 16.099493204 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2098 16.099493337 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2101 16.108240278 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2102 16.109665125 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2103 16.109665219 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2104 16.109665258 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2105 16.109665298 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2106 16.109665335 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2107 16.109665374 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2108 16.109665413 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2109 16.109665451 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2114 16.115534573 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2116 16.117085522 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2117 16.117085740 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2118 16.117085774 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2119 16.117085808 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2120 16.117085841 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2121 16.117085874 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2122 16.117085906 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2123 16.117085940 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2125 16.117208639 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2129 16.117847682 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2131 16.120936939 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2132 16.121564917 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2134 16.124001266 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2135 16.126359615 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2136 16.126359784 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2137 16.126359829 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2138 16.126359875 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2139 16.126359923 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2140 16.126359972 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2141 16.126360016 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2142 16.126360065 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2144 16.126516782 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2145 16.126516838 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2150 16.126568776 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2151 16.126568857 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2152 16.126568903 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2153 16.126568947 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1088 NetPerfMeter Results
+2154 16.126568990 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2155 16.126569037 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2156 16.126569084 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2162 16.128296076 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2163 16.128991998 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2164 16.128992266 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2166 16.132186659 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2168 16.133696852 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2169 16.133697204 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2170 16.133697304 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2171 16.133697400 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2172 16.133697505 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2175 16.136109923 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2177 16.138000289 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2178 16.138000795 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2179 16.138000952 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2180 16.138001087 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2181 16.138001222 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2182 16.138001355 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2183 16.138001497 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2184 16.138001654 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2189 16.138407582 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2190 16.138407852 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2191 16.138407948 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1360 NetPerfMeter Results
+2193 16.138949169 192.168.0.20 → 192.168.0.27 SCTP, NetPerfMeter 100 SACK (Ack=113, Arwnd=106496) NetPerfMeter Remove Flow
+2194 16.147965640 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 104 SACK (Ack=23, Arwnd=106496) NetPerfMeter Acknowledge
+2195 16.149160472 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2197 16.149694877 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2199 16.359112863 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 400 NetPerfMeter Results
+2200 16.360439472 192.168.0.20 → 192.168.0.27 SCTP, NetPerfMeter 100 SACK (Ack=117, Arwnd=106496) NetPerfMeter Remove Flow
+2201 16.367838301 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 104 SACK (Ack=24, Arwnd=106496) NetPerfMeter Acknowledge
+2202 16.369999711 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2204 16.370249698 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 252 NetPerfMeter Results
+2205 16.371333521 192.168.0.20 → 192.168.0.27 SCTP, NetPerfMeter 100 SACK (Ack=120, Arwnd=106496) NetPerfMeter Remove Flow
+2206 16.377931209 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 104 SACK (Ack=25, Arwnd=106496) NetPerfMeter Acknowledge
+2207 16.379416052 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2209 16.379921676 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2211 16.586758032 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 460 NetPerfMeter Results
+2212 16.588004878 192.168.0.20 → 192.168.0.27 SCTP, NetPerfMeter 100 SACK (Ack=124, Arwnd=106496) NetPerfMeter Remove Flow
+2213 16.596287178 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 104 SACK (Ack=26, Arwnd=106496) NetPerfMeter Acknowledge
+2214 16.600862615 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2216 16.601572074 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 256 NetPerfMeter Results
+2217 16.602770488 192.168.0.20 → 192.168.0.27 SCTP, NetPerfMeter 100 SACK (Ack=127, Arwnd=106496) NetPerfMeter Remove Flow
+2218 16.608528578 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 104 SACK (Ack=27, Arwnd=106496) NetPerfMeter Acknowledge
+2219 16.610851595 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2221 16.611228721 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2224 16.820428495 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 368 NetPerfMeter Results
+2226 16.821725312 192.168.0.20 → 192.168.0.27 SCTP, NetPerfMeter 100 SACK (Ack=131, Arwnd=106496) NetPerfMeter Remove Flow
+2227 16.829665670 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 104 SACK (Ack=28, Arwnd=106496) NetPerfMeter Acknowledge
+2228 16.831477557 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2230 16.831711400 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 276 NetPerfMeter Results
+2233 16.832859448 192.168.0.20 → 192.168.0.27 SCTP, NetPerfMeter 100 SACK (Ack=134, Arwnd=106496) NetPerfMeter Remove Flow
+2235 16.838963861 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 104 SACK (Ack=29, Arwnd=106496) NetPerfMeter Acknowledge
+2236 16.839917250 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2238 16.841055807 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 236 NetPerfMeter Results
+2241 16.842312060 192.168.0.20 → 192.168.0.27 SCTP, NetPerfMeter 100 SACK (Ack=137, Arwnd=106496) NetPerfMeter Remove Flow
+2243 16.847748197 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 104 SACK (Ack=30, Arwnd=106496) NetPerfMeter Acknowledge
+2244 16.848933463 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2246 16.849525492 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 236 NetPerfMeter Results
+2249 16.850661714 192.168.0.20 → 192.168.0.27 SCTP, NetPerfMeter 100 SACK (Ack=140, Arwnd=106496) NetPerfMeter Remove Flow
+2251 16.857615760 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 104 SACK (Ack=31, Arwnd=106496) NetPerfMeter Acknowledge
+2252 16.859140443 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2254 16.859653107 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 976 NetPerfMeter Results
+2257 16.860923512 192.168.0.20 → 192.168.0.27 SCTP, NetPerfMeter 100 SACK (Ack=143, Arwnd=106496) NetPerfMeter Remove Flow
+2259 16.866293943 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 104 SACK (Ack=32, Arwnd=106496) NetPerfMeter Acknowledge
+2260 16.867822941 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2262 16.868668201 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2265 17.079265007 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 368 NetPerfMeter Results
+2267 17.080555093 192.168.0.20 → 192.168.0.27 SCTP, NetPerfMeter 100 SACK (Ack=147, Arwnd=106496) NetPerfMeter Remove Flow
+2268 17.089928582 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 104 SACK (Ack=33, Arwnd=106496) NetPerfMeter Acknowledge
+2269 17.091479195 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2271 17.092073003 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 272 NetPerfMeter Results
+2274 17.093044526 192.168.0.20 → 192.168.0.27 SCTP, NetPerfMeter 100 SACK (Ack=150, Arwnd=106496) NetPerfMeter Remove Flow
+2276 17.099098185 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 104 SACK (Ack=34, Arwnd=106496) NetPerfMeter Acknowledge
+2277 17.100201203 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2279 17.100852674 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 244 NetPerfMeter Results
+2282 17.101916382 192.168.0.20 → 192.168.0.27 SCTP, NetPerfMeter 100 SACK (Ack=153, Arwnd=106496) NetPerfMeter Remove Flow
+2284 17.109026614 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 104 SACK (Ack=35, Arwnd=106496) NetPerfMeter Acknowledge
+2285 17.112907819 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2287 17.115302865 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 244 NetPerfMeter Results
+2290 17.116443045 192.168.0.20 → 192.168.0.27 SCTP, NetPerfMeter 100 SACK (Ack=156, Arwnd=106496) NetPerfMeter Remove Flow
+2292 17.122058351 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 104 SACK (Ack=36, Arwnd=106496) NetPerfMeter Acknowledge
+2293 17.125840461 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2295 17.126459769 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 976 NetPerfMeter Results
+2297 17.126760188 192.168.0.20 → 192.168.0.27 SCTP, NetPerfMeter 100 SACK (Ack=159, Arwnd=106496) NetPerfMeter Remove Flow
+2300 17.132579296 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 104 SACK (Ack=37, Arwnd=106496) NetPerfMeter Acknowledge
+2301 17.133301477 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2302 17.133302153 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 284 NetPerfMeter Results
+2304 17.133706810 192.168.0.20 → 192.168.0.27 SCTP, NetPerfMeter 100 SACK (Ack=162, Arwnd=106496) NetPerfMeter Remove Flow
+2305 17.138731552 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 104 SACK (Ack=38, Arwnd=106496) NetPerfMeter Acknowledge
+2306 17.139818471 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2308 17.140335127 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 288 NetPerfMeter Results
+2309 17.140830809 192.168.0.20 → 192.168.0.27 SCTP, NetPerfMeter 100 SACK (Ack=165, Arwnd=106496) NetPerfMeter Remove Flow
+2310 17.145622016 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 104 SACK (Ack=39, Arwnd=106496) NetPerfMeter Acknowledge
+2311 17.147059541 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2313 17.148571671 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2314 17.149475099 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2316 17.150223037 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2318 17.359940788 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 740 NetPerfMeter Results
+2319 17.361102522 192.168.0.20 → 192.168.0.27 SCTP, NetPerfMeter 100 SACK (Ack=171, Arwnd=106496) NetPerfMeter Remove Flow
+2320 17.368203507 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 104 SACK (Ack=40, Arwnd=106496) NetPerfMeter Acknowledge
+2321 17.370823736 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2323 17.371236232 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 280 NetPerfMeter Results
+2324 17.372205596 192.168.0.20 → 192.168.0.27 SCTP, NetPerfMeter 100 SACK (Ack=174, Arwnd=106496) NetPerfMeter Remove Flow
+2325 17.378113171 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 104 SACK (Ack=41, Arwnd=106496) NetPerfMeter Acknowledge
+2326 17.379408121 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2328 17.379940226 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 284 NetPerfMeter Results
+2329 17.380772832 192.168.0.20 → 192.168.0.27 SCTP, NetPerfMeter 100 SACK (Ack=177, Arwnd=106496) NetPerfMeter Remove Flow
+2330 17.389000119 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 104 SACK (Ack=42, Arwnd=106496) NetPerfMeter Acknowledge
+2331 17.389893116 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2332 17.389893325 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 380 NetPerfMeter Results
+2334 17.390667295 192.168.0.20 → 192.168.0.27 SCTP, NetPerfMeter 100 SACK (Ack=180, Arwnd=106496) NetPerfMeter Remove Flow
+2335 17.395701306 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 104 SACK (Ack=43, Arwnd=106496) NetPerfMeter Acknowledge
+2336 17.397791412 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1468 NetPerfMeter Results
+2338 17.398332887 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 284 NetPerfMeter Results
+""".replace("\r\n", "\n") in result
+
+ def test_netperfmeter_test_udp(self, cmd_tshark, capture_file, test_env):
+ '''Checks whether the NetPerfMeter dissector correctly handles NetPerfMeter Data via UDP.'''
+
+ # Test: Identify and decode NetPerfMeter Data via UDP
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('netperfmeter.pcapng.gz'),
+ '-Y', 'frame.number >= 1 && frame.number <= 512 && udp && netperfmeter'
+ ),
+ encoding='utf-8', env=test_env)
+
+ result = ''.join([x.strip()+"\n" for x in stdout.splitlines()])
+
+ assert """\
+26 0.556893098 192.168.0.20 → 192.168.0.27 UDP, NetPerfMeter 70 NetPerfMeter Identify Flow
+31 0.778199411 192.168.0.20 → 192.168.0.27 UDP, NetPerfMeter 70 NetPerfMeter Identify Flow
+166 5.097058561 192.168.0.20 → 192.168.0.27 UDP, NetPerfMeter 1068 NetPerfMeter Data
+167 5.097156368 192.168.0.20 → 192.168.0.27 UDP, NetPerfMeter 556 NetPerfMeter Data
+203 5.188581678 192.168.0.27 → 192.168.0.20 UDP, NetPerfMeter 1068 NetPerfMeter Data
+204 5.198869201 192.168.0.27 → 192.168.0.20 UDP, NetPerfMeter 556 NetPerfMeter Data
+229 5.347412858 192.168.0.20 → 192.168.0.27 UDP, NetPerfMeter 1068 NetPerfMeter Data
+248 5.521667162 192.168.0.27 → 192.168.0.20 UDP, NetPerfMeter 1068 NetPerfMeter Data
+249 5.529727434 192.168.0.27 → 192.168.0.20 UDP, NetPerfMeter 556 NetPerfMeter Data
+251 5.597939044 192.168.0.20 → 192.168.0.27 UDP, NetPerfMeter 1068 NetPerfMeter Data
+252 5.597979296 192.168.0.20 → 192.168.0.27 UDP, NetPerfMeter 556 NetPerfMeter Data
+315 5.848599107 192.168.0.20 → 192.168.0.27 UDP, NetPerfMeter 1068 NetPerfMeter Data
+326 5.869626418 192.168.0.27 → 192.168.0.20 UDP, NetPerfMeter 1068 NetPerfMeter Data
+327 5.870477253 192.168.0.27 → 192.168.0.20 UDP, NetPerfMeter 556 NetPerfMeter Data
+336 6.099006262 192.168.0.20 → 192.168.0.27 UDP, NetPerfMeter 1068 NetPerfMeter Data
+337 6.099035694 192.168.0.20 → 192.168.0.27 UDP, NetPerfMeter 556 NetPerfMeter Data
+374 6.239221234 192.168.0.27 → 192.168.0.20 UDP, NetPerfMeter 1068 NetPerfMeter Data
+375 6.240243736 192.168.0.27 → 192.168.0.20 UDP, NetPerfMeter 556 NetPerfMeter Data
+406 6.349592731 192.168.0.20 → 192.168.0.27 UDP, NetPerfMeter 1068 NetPerfMeter Data
+429 6.538916191 192.168.0.27 → 192.168.0.20 UDP, NetPerfMeter 1068 NetPerfMeter Data
+430 6.540208385 192.168.0.27 → 192.168.0.20 UDP, NetPerfMeter 556 NetPerfMeter Data
+438 6.600112279 192.168.0.20 → 192.168.0.27 UDP, NetPerfMeter 556 NetPerfMeter Data
+439 6.600127896 192.168.0.20 → 192.168.0.27 UDP, NetPerfMeter 1068 NetPerfMeter Data
+499 6.850796522 192.168.0.20 → 192.168.0.27 UDP, NetPerfMeter 1068 NetPerfMeter Data
+509 6.874579699 192.168.0.27 → 192.168.0.20 UDP, NetPerfMeter 1068 NetPerfMeter Data
+510 6.875289205 192.168.0.27 → 192.168.0.20 UDP, NetPerfMeter 556 NetPerfMeter Data
+""".replace("\r\n", "\n") in result
+
+ def test_netperfmeter_test_dccp(self, cmd_tshark, capture_file, test_env):
+ '''Checks whether the NetPerfMeter dissector correctly handles NetPerfMeter Data via DCCP.'''
+
+ # Test: Identify and decode NetPerfMeter Data via DCCP
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('netperfmeter.pcapng.gz'),
+ '-Y', 'frame.number >= 1 && frame.number <= 256 && dccp && netperfmeter'
+ ),
+ encoding='utf-8', env=test_env)
+
+ result = ''.join([x.strip()+"\n" for x in stdout.splitlines()])
+
+ assert """\
+39 1.000448305 192.168.0.20 → 192.168.0.27 DCCP, NetPerfMeter 106 NetPerfMeter Identify Flow
+47 1.257376250 192.168.0.20 → 192.168.0.27 DCCP, NetPerfMeter 106 NetPerfMeter Identify Flow
+55 1.518626642 192.168.0.20 → 192.168.0.27 DCCP, NetPerfMeter 106 NetPerfMeter Identify Flow
+63 1.776552210 192.168.0.20 → 192.168.0.27 DCCP, NetPerfMeter 106 NetPerfMeter Identify Flow
+71 2.013038051 192.168.0.20 → 192.168.0.27 DCCP, NetPerfMeter 106 NetPerfMeter Identify Flow
+79 2.268029558 192.168.0.20 → 192.168.0.27 DCCP, NetPerfMeter 106 NetPerfMeter Identify Flow
+87 2.526765502 192.168.0.20 → 192.168.0.27 DCCP, NetPerfMeter 106 NetPerfMeter Identify Flow
+95 2.789401573 192.168.0.20 → 192.168.0.27 DCCP, NetPerfMeter 106 NetPerfMeter Identify Flow
+103 3.011188128 192.168.0.20 → 192.168.0.27 DCCP, NetPerfMeter 106 NetPerfMeter Identify Flow
+111 3.270945041 192.168.0.20 → 192.168.0.27 DCCP, NetPerfMeter 106 NetPerfMeter Identify Flow
+168 5.097388740 192.168.0.20 → 192.168.0.27 DCCP, NetPerfMeter 1112 NetPerfMeter Data
+169 5.097563303 192.168.0.20 → 192.168.0.27 DCCP, NetPerfMeter 1112 NetPerfMeter Data
+170 5.097680252 192.168.0.20 → 192.168.0.27 DCCP, NetPerfMeter 600 NetPerfMeter Data
+171 5.097804675 192.168.0.20 → 192.168.0.27 DCCP, NetPerfMeter 600 NetPerfMeter Data
+172 5.097860862 192.168.0.20 → 192.168.0.27 DCCP, NetPerfMeter 336 NetPerfMeter Data
+173 5.097960425 192.168.0.20 → 192.168.0.27 DCCP, NetPerfMeter 1104 NetPerfMeter Data
+174 5.098168605 192.168.0.20 → 192.168.0.27 DCCP, NetPerfMeter 1104 NetPerfMeter Data
+175 5.098268064 192.168.0.20 → 192.168.0.27 DCCP, NetPerfMeter 592 NetPerfMeter Data
+176 5.098379939 192.168.0.20 → 192.168.0.27 DCCP, NetPerfMeter 592 NetPerfMeter Data
+177 5.098474409 192.168.0.20 → 192.168.0.27 DCCP, NetPerfMeter 336 NetPerfMeter Data
+205 5.203489906 192.168.0.27 → 192.168.0.20 DCCP, NetPerfMeter 1088 NetPerfMeter Data
+206 5.208120579 192.168.0.27 → 192.168.0.20 DCCP, NetPerfMeter 1088 NetPerfMeter Data
+207 5.211621270 192.168.0.27 → 192.168.0.20 DCCP, NetPerfMeter 576 NetPerfMeter Data
+208 5.216629302 192.168.0.27 → 192.168.0.20 DCCP, NetPerfMeter 576 NetPerfMeter Data
+209 5.218637208 192.168.0.27 → 192.168.0.20 DCCP, NetPerfMeter 320 NetPerfMeter Data
+210 5.220923234 192.168.0.27 → 192.168.0.20 DCCP, NetPerfMeter 1088 NetPerfMeter Data
+211 5.224470647 192.168.0.27 → 192.168.0.20 DCCP, NetPerfMeter 1088 NetPerfMeter Data
+212 5.228633904 192.168.0.27 → 192.168.0.20 DCCP, NetPerfMeter 576 NetPerfMeter Data
+213 5.235096316 192.168.0.27 → 192.168.0.20 DCCP, NetPerfMeter 576 NetPerfMeter Data
+214 5.235387030 192.168.0.27 → 192.168.0.20 DCCP, NetPerfMeter 320 NetPerfMeter Data
+230 5.347723929 192.168.0.20 → 192.168.0.27 DCCP, NetPerfMeter 1088 NetPerfMeter Data
+231 5.348299245 192.168.0.20 → 192.168.0.27 DCCP, NetPerfMeter 1088 NetPerfMeter Data
+236 5.432621676 192.168.0.20 → 192.168.0.27 DCCP, NetPerfMeter 320 NetPerfMeter Data
+237 5.433090508 192.168.0.20 → 192.168.0.27 DCCP, NetPerfMeter 320 NetPerfMeter Data
+238 5.458215001 192.168.0.27 → 192.168.0.20 DCCP, NetPerfMeter 1104 NetPerfMeter Data
+240 5.472252869 192.168.0.27 → 192.168.0.20 DCCP, NetPerfMeter 1104 NetPerfMeter Data
+250 5.597889485 192.168.0.20 → 192.168.0.27 DCCP, NetPerfMeter 1096 NetPerfMeter Data
+255 5.598126766 192.168.0.20 → 192.168.0.27 DCCP, NetPerfMeter 1088 NetPerfMeter Data
+256 5.598378615 192.168.0.20 → 192.168.0.27 DCCP, NetPerfMeter 576 NetPerfMeter Data
+""".replace("\r\n", "\n") in result
+
+ def test_netperfmeter_test_tcp(self, cmd_tshark, capture_file, test_env):
+ '''Checks whether the NetPerfMeter dissector correctly handles NetPerfMeter Data via TCP.'''
+
+ # Test: Identify and decode NetPerfMeter Data via TCP
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('netperfmeter.pcapng.gz'),
+ '-Y', 'frame.number >= 1 && frame.number <= 512 && tcp && netperfmeter'
+ ),
+ encoding='utf-8', env=test_env)
+
+ result = ''.join([x.strip()+"\n" for x in stdout.splitlines()])
+
+ assert """\
+12 0.038833197 192.168.0.20 → 192.168.0.27 TCP, NetPerfMeter 94 NetPerfMeter Identify Flow
+20 0.340423798 192.168.0.20 → 192.168.0.27 TCP, NetPerfMeter 94 NetPerfMeter Identify Flow
+164 5.096822593 192.168.0.20 → 192.168.0.27 TCP, NetPerfMeter 1092 NetPerfMeter Data
+165 5.096933125 192.168.0.20 → 192.168.0.27 TCP, NetPerfMeter 580 NetPerfMeter Data
+199 5.180197902 192.168.0.27 → 192.168.0.20 TCP, NetPerfMeter 1092 NetPerfMeter Data
+201 5.183618768 192.168.0.27 → 192.168.0.20 TCP, NetPerfMeter 580 NetPerfMeter Data
+228 5.347212980 192.168.0.20 → 192.168.0.27 TCP, NetPerfMeter 1092 NetPerfMeter Data
+243 5.510843364 192.168.0.27 → 192.168.0.20 TCP, NetPerfMeter 1092 NetPerfMeter Data
+246 5.518285725 192.168.0.27 → 192.168.0.20 TCP, NetPerfMeter 580 NetPerfMeter Data
+253 5.598004664 192.168.0.20 → 192.168.0.27 TCP, NetPerfMeter 580 NetPerfMeter Data
+254 5.598037007 192.168.0.20 → 192.168.0.27 TCP, NetPerfMeter 1092 NetPerfMeter Data
+313 5.843608886 192.168.0.27 → 192.168.0.20 TCP, NetPerfMeter 1092 NetPerfMeter Data
+316 5.848649435 192.168.0.20 → 192.168.0.27 TCP, NetPerfMeter 1092 NetPerfMeter Data
+320 5.852294838 192.168.0.27 → 192.168.0.20 TCP, NetPerfMeter 580 NetPerfMeter Data
+335 6.098962324 192.168.0.20 → 192.168.0.27 TCP, NetPerfMeter 580 NetPerfMeter Data
+342 6.099194942 192.168.0.20 → 192.168.0.27 TCP, NetPerfMeter 1092 NetPerfMeter Data
+370 6.178557080 192.168.0.27 → 192.168.0.20 TCP, NetPerfMeter 1092 NetPerfMeter Data
+372 6.186668259 192.168.0.27 → 192.168.0.20 TCP, NetPerfMeter 580 NetPerfMeter Data
+408 6.349677977 192.168.0.20 → 192.168.0.27 TCP, NetPerfMeter 1092 NetPerfMeter Data
+425 6.512522597 192.168.0.27 → 192.168.0.20 TCP, NetPerfMeter 1092 NetPerfMeter Data
+427 6.521373219 192.168.0.27 → 192.168.0.20 TCP, NetPerfMeter 580 NetPerfMeter Data
+436 6.600056667 192.168.0.20 → 192.168.0.27 TCP, NetPerfMeter 580 NetPerfMeter Data
+441 6.600170332 192.168.0.20 → 192.168.0.27 TCP, NetPerfMeter 1092 NetPerfMeter Data
+497 6.846781911 192.168.0.27 → 192.168.0.20 TCP, NetPerfMeter 1092 NetPerfMeter Data
+502 6.850917051 192.168.0.20 → 192.168.0.27 TCP, NetPerfMeter 1092 NetPerfMeter Data
+507 6.857231771 192.168.0.27 → 192.168.0.20 TCP, NetPerfMeter 580 NetPerfMeter Data
+""".replace("\r\n", "\n") in result
+
+ def test_netperfmeter_test_sctp(self, cmd_tshark, capture_file, test_env):
+ '''Checks whether the NetPerfMeter dissector correctly handles NetPerfMeter Data via SCTP.'''
+
+ # Test: Identify and decode NetPerfMeter Data via SCTP
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('netperfmeter.pcapng.gz'),
+ '-Y', 'frame.number >= 1 && frame.number <= 256 && sctp && netperfmeter && ((netperfmeter.message_type == 5) || (netperfmeter.message_type == 4))'
+ ),
+ encoding='utf-8', env=test_env)
+
+ result = ''.join([x.strip()+"\n" for x in stdout.splitlines()])
+
+ assert """\
+120 3.541753666 192.168.0.20 → 192.168.0.27 SCTP, NetPerfMeter 92 NetPerfMeter Identify Flow
+126 3.784578040 192.168.0.20 → 192.168.0.27 SCTP, NetPerfMeter 92 NetPerfMeter Identify Flow
+132 4.006622016 192.168.0.20 → 192.168.0.27 SCTP, NetPerfMeter 92 NetPerfMeter Identify Flow
+138 4.223204664 192.168.0.20 → 192.168.0.27 SCTP, NetPerfMeter 92 NetPerfMeter Identify Flow
+144 4.439513544 192.168.0.20 → 192.168.0.27 SCTP, NetPerfMeter 92 NetPerfMeter Identify Flow
+150 4.654398275 192.168.0.20 → 192.168.0.27 SCTP, NetPerfMeter 92 NetPerfMeter Identify Flow
+160 4.887196553 192.168.0.20 → 192.168.0.27 SCTP, NetPerfMeter 92 NetPerfMeter Identify Flow
+178 5.098706269 192.168.0.20 → 192.168.0.27 SCTP, NetPerfMeter 1088 NetPerfMeter Data
+180 5.098939899 192.168.0.20 → 192.168.0.27 SCTP, NetPerfMeter 660 NetPerfMeter Data
+181 5.099244178 192.168.0.20 → 192.168.0.27 SCTP, NetPerfMeter, NetPerfMeter 1232 NetPerfMeter Data NetPerfMeter Data
+182 5.099428646 192.168.0.20 → 192.168.0.27 SCTP, NetPerfMeter 1088 NetPerfMeter Data
+183 5.099642887 192.168.0.20 → 192.168.0.27 SCTP, NetPerfMeter 1088 NetPerfMeter Data
+215 5.242589734 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1104 SACK (Ack=11, Arwnd=106496) NetPerfMeter Data
+216 5.242748399 192.168.0.20 → 192.168.0.27 SCTP, NetPerfMeter, NetPerfMeter 1248 SACK (Ack=0, Arwnd=211968) NetPerfMeter Data NetPerfMeter Data
+218 5.247412901 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 680 NetPerfMeter Data
+220 5.252114400 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 208 SACK (Ack=13, Arwnd=105344) NetPerfMeter Data
+221 5.266387026 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1088 NetPerfMeter Data
+223 5.266637245 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1088 NetPerfMeter Data
+224 5.273527654 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1104 SACK (Ack=1, Arwnd=106496) NetPerfMeter Data
+232 5.349726358 192.168.0.20 → 192.168.0.27 SCTP, NetPerfMeter 208 SACK (Ack=5, Arwnd=212992) NetPerfMeter Data
+235 5.355361743 192.168.0.27 → 192.168.0.20 SCTP, NetPerfMeter 1104 SACK (Ack=14, Arwnd=106368) NetPerfMeter Data
+242 5.475302128 192.168.0.20 → 192.168.0.27 SCTP, NetPerfMeter 208 SACK (Ack=6, Arwnd=212992) NetPerfMeter Data
+""".replace("\r\n", "\n") in result
diff --git a/test/suite_extcaps.py b/test/suite_extcaps.py
new file mode 100644
index 0000000..2b38379
--- /dev/null
+++ b/test/suite_extcaps.py
@@ -0,0 +1,90 @@
+#
+# Wireshark tests
+# By Gerald Combs <gerald@wireshark.org>
+#
+# Copyright (c) 2019 Dario Lombardo <lomato@gmail.com>
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+'''extcap tests'''
+
+import subprocess
+import re
+import os
+import sys
+import pytest
+
+
+@pytest.fixture
+def check_extcap_execution(cmd_extcap, program_path, base_env):
+ def check_extcap_interface_execution(extcap_name, interface):
+ ''' Check if an extcap runs flawlessly for interface configuration. '''
+
+ subprocess.check_call([cmd_extcap(extcap_name), '--extcap-interface',
+ interface, '--extcap-dlts'], cwd=program_path, env=base_env)
+ subprocess.check_call([cmd_extcap(extcap_name), '--extcap-interface',
+ interface, '--extcap-config'], cwd=program_path, env=base_env)
+
+ def extcap_get_interfaces(extcap_output):
+ ''' Extract the interface name from extcap. '''
+ parser = re.compile("{value=(.*?)}")
+ interfaces = []
+ for line in extcap_output.splitlines():
+ if line.startswith('interface '):
+ interfaces.append(parser.findall(line)[0])
+ return interfaces
+
+ def check_extcap_execution_real(extcap_name, always_present=True):
+ '''
+ Check if an extcap runs flawlessly.
+ always_present: at least one interface is always offered by the extcap.
+ '''
+
+ subprocess.check_call([cmd_extcap(extcap_name), '--help'], cwd=program_path, env=base_env)
+ extcap_stdout = subprocess.check_output(
+ [cmd_extcap(extcap_name), '--extcap-interfaces'], cwd=program_path, encoding='utf-8', env=base_env)
+ interfaces = extcap_get_interfaces(extcap_stdout)
+ if always_present:
+ assert len(interfaces) > 0
+ for interface in interfaces:
+ check_extcap_interface_execution(extcap_name, interface)
+
+ return check_extcap_execution_real
+
+
+class TestExtcaps:
+ def test_androiddump(self, check_extcap_execution):
+ ''' extcap interface tests for androiddump '''
+ check_extcap_execution("androiddump", always_present=False)
+
+ def test_ciscodump(self, check_extcap_execution):
+ ''' extcap interface tests for ciscodump '''
+ check_extcap_execution("ciscodump")
+
+ def test_dpauxmon(self, check_extcap_execution):
+ ''' extcap interface tests for dpauxmon '''
+ if not sys.platform.startswith('linux'):
+ pytest.skip('dpauxmon available on Linux only')
+ check_extcap_execution("dpauxmon")
+
+ def test_randpktdump(self, check_extcap_execution):
+ ''' extcap interface tests for randpktdump '''
+ check_extcap_execution("randpktdump")
+
+ def test_sdjournal(self, check_extcap_execution):
+ ''' extcap interface tests for sdjournal '''
+ if not sys.platform.startswith('linux'):
+ pytest.skip('sdjournal is available on Linux only')
+ check_extcap_execution("sdjournal")
+
+ def test_sshdump(self, check_extcap_execution):
+ ''' extcap interface tests for sshdump '''
+ check_extcap_execution("sshdump")
+
+ def test_wifidump(self, check_extcap_execution):
+ ''' extcap interface tests for wifidump '''
+ check_extcap_execution("wifidump")
+
+ def test_udpdump(self, check_extcap_execution):
+ ''' extcap interface tests for udpdump '''
+ check_extcap_execution("udpdump")
diff --git a/test/suite_fileformats.py b/test/suite_fileformats.py
new file mode 100644
index 0000000..37ee4bc
--- /dev/null
+++ b/test/suite_fileformats.py
@@ -0,0 +1,242 @@
+#
+# Wireshark tests
+# By Gerald Combs <gerald@wireshark.org>
+#
+# Ported from a set of Bash scripts which were copyright 2005 Ulf Lamping
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+'''File format conversion tests'''
+
+import os.path
+from subprocesstest import count_output
+import subprocess
+import pytest
+
+# XXX Currently unused. It would be nice to be able to use this below.
+time_output_args = ('-Tfields', '-e', 'frame.number', '-e', 'frame.time_epoch', '-e', 'frame.time_delta')
+
+# Microsecond pcap, direct read was used to generate the baseline:
+# tshark -Tfields -e frame.number -e frame.time_epoch -e frame.time_delta \
+# -r captures/dhcp.pcap > baseline/ff-ts-usec-pcap-direct.txt
+baseline_file = 'ff-ts-usec-pcap-direct.txt'
+
+
+@pytest.fixture(scope='session')
+def fileformats_baseline_str(dirs):
+ with open(os.path.join(dirs.baseline_dir, baseline_file), 'r') as f:
+ return f.read()
+
+
+class TestFileFormatPcap:
+ def test_pcap_usec_stdin(self, cmd_tshark, capture_file, fileformats_baseline_str, test_env):
+ '''Microsecond pcap direct vs microsecond pcap stdin'''
+ capture_stdout = subprocess.check_output(' '.join((cmd_tshark,
+ '-r', '-',
+ '-Tfields',
+ '-e', 'frame.number', '-e', 'frame.time_epoch', '-e', 'frame.time_delta',
+ '<', capture_file('dhcp.pcap')
+ )),
+ shell=True, encoding='utf-8', env=test_env)
+ assert capture_stdout == fileformats_baseline_str
+
+ def test_pcap_nsec_stdin(self, cmd_tshark, capture_file, fileformats_baseline_str, test_env):
+ '''Microsecond pcap direct vs nanosecond pcap stdin'''
+ capture_stdout = subprocess.check_output(' '.join((cmd_tshark,
+ '-r', '-',
+ '-Tfields',
+ '-e', 'frame.number', '-e', 'frame.time_epoch', '-e', 'frame.time_delta',
+ '<', capture_file('dhcp-nanosecond.pcap')
+ )),
+ shell=True, encoding='utf-8', env=test_env)
+ assert capture_stdout == fileformats_baseline_str
+
+ def test_pcap_nsec_direct(self, cmd_tshark, capture_file, fileformats_baseline_str, test_env):
+ '''Microsecond pcap direct vs nanosecond pcap direct'''
+ capture_stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('dhcp-nanosecond.pcap'),
+ '-Tfields',
+ '-e', 'frame.number', '-e', 'frame.time_epoch', '-e', 'frame.time_delta',
+ ),
+ encoding='utf-8', env=test_env)
+ assert capture_stdout == fileformats_baseline_str
+
+
+class TestFileFormatsPcapng:
+ def test_pcapng_usec_stdin(self, cmd_tshark, capture_file, fileformats_baseline_str, test_env):
+ '''Microsecond pcap direct vs microsecond pcapng stdin'''
+ capture_stdout = subprocess.check_output(' '.join((cmd_tshark,
+ '-r', '-',
+ '-Tfields',
+ '-e', 'frame.number', '-e', 'frame.time_epoch', '-e', 'frame.time_delta'
+ '<', capture_file('dhcp.pcapng')
+ )),
+ shell=True, encoding='utf-8', env=test_env)
+ assert capture_stdout == fileformats_baseline_str
+
+ def test_pcapng_usec_direct(self, cmd_tshark, capture_file, fileformats_baseline_str, test_env):
+ '''Microsecond pcap direct vs microsecond pcapng direct'''
+ capture_stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('dhcp.pcapng'),
+ '-Tfields',
+ '-e', 'frame.number', '-e', 'frame.time_epoch', '-e', 'frame.time_delta',
+ ),
+ encoding='utf-8', env=test_env)
+ assert capture_stdout == fileformats_baseline_str
+
+ def test_pcapng_nsec_stdin(self, cmd_tshark, capture_file, fileformats_baseline_str, test_env):
+ '''Microsecond pcap direct vs nanosecond pcapng stdin'''
+ capture_stdout = subprocess.check_output(' '.join((cmd_tshark,
+ '-r', '-',
+ '-Tfields',
+ '-e', 'frame.number', '-e', 'frame.time_epoch', '-e', 'frame.time_delta'
+ '<', capture_file('dhcp-nanosecond.pcapng')
+ )),
+ shell=True, encoding='utf-8', env=test_env)
+ assert capture_stdout == fileformats_baseline_str
+
+ def test_pcapng_nsec_direct(self, cmd_tshark, capture_file, fileformats_baseline_str, test_env):
+ '''Microsecond pcap direct vs nanosecond pcapng direct'''
+ capture_stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('dhcp-nanosecond.pcapng'),
+ '-Tfields',
+ '-e', 'frame.number', '-e', 'frame.time_epoch', '-e', 'frame.time_delta',
+ ),
+ encoding='utf-8', env=test_env)
+ assert capture_stdout == fileformats_baseline_str
+
+@pytest.fixture
+def check_pcapng_dsb_fields(request, cmd_tshark):
+ '''Factory that checks whether the DSB within the capture file matches.'''
+ def check_dsb_fields_real(outfile, fields, env=None):
+ proc_stdout = subprocess.check_output((cmd_tshark,
+ '-r', outfile,
+ '-Xread_format:MIME Files Format',
+ '-Tfields',
+ '-e', 'pcapng.dsb.secrets_type',
+ '-e', 'pcapng.dsb.secrets_length',
+ '-e', 'pcapng.dsb.secrets_data',
+ '-Y', 'pcapng.dsb.secrets_data'
+ ), encoding='utf-8', env=env)
+ # Convert "t1,t2 l1,l2 v1,2" -> [(t1, l1, v1), (t2, l2, v2)]
+ output = proc_stdout.strip()
+ actual = list(zip(*[x.split(",") for x in output.split('\t')]))
+ def format_field(field):
+ t, l, v = field
+ v_hex = ''.join('%02x' % c for c in v)
+ return ('0x%08x' % t, str(l), v_hex)
+ fields = [format_field(field) for field in fields]
+ assert fields == actual
+ return check_dsb_fields_real
+
+
+class TestFileFormatsPcapngDsb:
+ def test_pcapng_dsb_1(self, cmd_tshark, dirs, capture_file, result_file, check_pcapng_dsb_fields, base_env):
+ '''Check that DSBs are preserved while rewriting files.'''
+ dsb_keys1 = os.path.join(dirs.key_dir, 'tls12-dsb-1.keys')
+ dsb_keys2 = os.path.join(dirs.key_dir, 'tls12-dsb-2.keys')
+ outfile = result_file('tls12-dsb-same.pcapng')
+ subprocess.run((cmd_tshark,
+ '-r', capture_file('tls12-dsb.pcapng'),
+ '-w', outfile,
+ ), check=True, env=base_env)
+ with open(dsb_keys1, 'r') as f:
+ dsb1_contents = f.read().encode('utf8')
+ with open(dsb_keys2, 'r') as f:
+ dsb2_contents = f.read().encode('utf8')
+ check_pcapng_dsb_fields(outfile, (
+ (0x544c534b, len(dsb1_contents), dsb1_contents),
+ (0x544c534b, len(dsb2_contents), dsb2_contents),
+ ), env=base_env)
+
+ def test_pcapng_dsb_2(self, cmd_editcap, dirs, capture_file, result_file, check_pcapng_dsb_fields, base_env):
+ '''Insert a single DSB into a pcapng file.'''
+ key_file = os.path.join(dirs.key_dir, 'dhe1_keylog.dat')
+ outfile = result_file('dhe1-dsb.pcapng')
+ subprocess.run((cmd_editcap,
+ '--inject-secrets', 'tls,%s' % key_file,
+ capture_file('dhe1.pcapng.gz'), outfile
+ ), check=True, env=base_env)
+ with open(key_file, 'rb') as f:
+ keylog_contents = f.read()
+ check_pcapng_dsb_fields(outfile, (
+ (0x544c534b, len(keylog_contents), keylog_contents),
+ ), env=base_env)
+
+ def test_pcapng_dsb_3(self, cmd_editcap, dirs, capture_file, result_file, check_pcapng_dsb_fields, base_env):
+ '''Insert two DSBs into a pcapng file.'''
+ key_file1 = os.path.join(dirs.key_dir, 'dhe1_keylog.dat')
+ key_file2 = os.path.join(dirs.key_dir, 'http2-data-reassembly.keys')
+ outfile = result_file('dhe1-dsb.pcapng')
+ subprocess.run((cmd_editcap,
+ '--inject-secrets', 'tls,%s' % key_file1,
+ '--inject-secrets', 'tls,%s' % key_file2,
+ capture_file('dhe1.pcapng.gz'), outfile
+ ), check=True, env=base_env)
+ with open(key_file1, 'rb') as f:
+ keylog1_contents = f.read()
+ with open(key_file2, 'rb') as f:
+ keylog2_contents = f.read()
+ check_pcapng_dsb_fields(outfile, (
+ (0x544c534b, len(keylog1_contents), keylog1_contents),
+ (0x544c534b, len(keylog2_contents), keylog2_contents),
+ ), env=base_env)
+
+ def test_pcapng_dsb_4(self, cmd_editcap, dirs, capture_file, result_file, check_pcapng_dsb_fields, base_env):
+ '''Insert a single DSB into a pcapng file with existing DSBs.'''
+ dsb_keys1 = os.path.join(dirs.key_dir, 'tls12-dsb-1.keys')
+ dsb_keys2 = os.path.join(dirs.key_dir, 'tls12-dsb-2.keys')
+ key_file = os.path.join(dirs.key_dir, 'dhe1_keylog.dat')
+ outfile = result_file('tls12-dsb-extra.pcapng')
+ subprocess.run((cmd_editcap,
+ '--inject-secrets', 'tls,%s' % key_file,
+ capture_file('tls12-dsb.pcapng'), outfile
+ ), check=True, env=base_env)
+ with open(dsb_keys1, 'r') as f:
+ dsb1_contents = f.read().encode('utf8')
+ with open(dsb_keys2, 'r') as f:
+ dsb2_contents = f.read().encode('utf8')
+ with open(key_file, 'rb') as f:
+ keylog_contents = f.read()
+ # New DSBs are inserted before the first record. Due to the current
+ # implementation, this is inserted before other (existing) DSBs. This
+ # might change in the future if it is deemed more logical.
+ check_pcapng_dsb_fields(outfile, (
+ (0x544c534b, len(keylog_contents), keylog_contents),
+ (0x544c534b, len(dsb1_contents), dsb1_contents),
+ (0x544c534b, len(dsb2_contents), dsb2_contents),
+ ), env=base_env)
+
+ def test_pcapng_dsb_bad_key(self, cmd_editcap, dirs, capture_file, result_file, check_pcapng_dsb_fields, base_env):
+ '''Insertion of a RSA key file is not very effective.'''
+ rsa_keyfile = os.path.join(dirs.key_dir, 'rsasnakeoil2.key')
+ p12_keyfile = os.path.join(dirs.key_dir, 'key.p12')
+ outfile = result_file('rsasnakeoil2-dsb.pcapng')
+ proc = subprocess.run((cmd_editcap,
+ '--inject-secrets', 'tls,%s' % rsa_keyfile,
+ '--inject-secrets', 'tls,%s' % p12_keyfile,
+ capture_file('rsasnakeoil2.pcap'), outfile
+ ), capture_output=True, encoding='utf-8', check=True, env=base_env)
+ assert count_output(proc.stderr, 'unsupported private key file') == 2
+ with open(rsa_keyfile, 'rb') as f:
+ dsb1_contents = f.read()
+ with open(p12_keyfile, 'rb') as f:
+ dsb2_contents = f.read()
+ check_pcapng_dsb_fields(outfile, (
+ (0x544c534b, len(dsb1_contents), dsb1_contents),
+ (0x544c534b, len(dsb2_contents), dsb2_contents),
+ ), env=base_env)
+
+
+class TestFileFormatMime:
+ def test_mime_pcapng_gz(self, cmd_tshark, capture_file, test_env):
+ '''Test that the full uncompressed contents is shown.'''
+ proc_stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('icmp.pcapng.gz'),
+ '-Xread_format:MIME Files Format',
+ '-Tfields',
+ '-e', 'frame.len',
+ '-e', 'pcapng.block.length',
+ '-e', 'pcapng.block.length_trailer',
+ ), encoding='utf-8', env=test_env)
+ assert proc_stdout.strip() == '480\t128,88,132,132\t128,88,132,132'
diff --git a/test/suite_follow.py b/test/suite_follow.py
new file mode 100644
index 0000000..3916330
--- /dev/null
+++ b/test/suite_follow.py
@@ -0,0 +1,342 @@
+#
+# Wireshark tests
+#
+# Copyright (c) 2018 Peter Wu <peter@lekensteyn.nl>
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+'''Follow Stream tests'''
+
+import subprocess
+import pytest
+
+
+class TestFollowTcp:
+ def test_follow_tcp_bad_conditions(self, cmd_tshark, capture_file):
+ '''Checks whether Follow TCP correctly handles lots of edge cases.'''
+ # Edge cases include:
+ # 1. two sequential segments
+ # 2. out-of-order (swapped two sequential segments)
+ # 3. Bad overlap (second overlap with different data should be ignored)
+ # 4. Ignore bad retransmitted data, but extend with remaining data.
+ # 5. Check handling of overlapping data while fragments are incomplete
+ # (out-of-order - cannot add fragments to stream)
+ # 6. lost but acked segments
+ # 7. lost 3/5 fragments, but acked
+ # Not checked: lost and not acked (currently truncated, is that OK?)
+ proc_stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('tcp-badsegments.pcap'),
+ '-qz', 'follow,tcp,hex,0',
+ ), encoding='utf-8')
+
+ assert """\
+===================================================================
+Follow: tcp,hex
+Filter: tcp.stream eq 0
+Node 0: 10.0.0.1:32323
+Node 1: 10.0.0.2:80
+00000000 47 45 54 20 2f 20 48 54 54 50 2f 31 2e 31 0d 0a GET / HT TP/1.1..
+00000010 48 6f 73 74 3a 6c 6f 63 61 6c 68 6f 73 74 0d 0a Host:loc alhost..
+00000020 58 2d 53 77 61 70 70 65 64 3a 20 31 73 74 0d 0a X-Swappe d: 1st..
+00000030 58 2d 53 77 61 70 70 65 64 3a 20 32 6e 64 0d 0a X-Swappe d: 2nd..
+00000040 58 2d 4f 76 65 72 6c 61 70 2d 50 61 63 6b 65 74 X-Overla p-Packet
+00000050 3a 20 65 78 74 72 61 20 64 61 74 61 2d 2d 0d 0a : extra data--..
+00000060 58 2d 4f 6f 4f 2d 4f 76 65 72 6c 61 70 3a 20 74 X-OoO-Ov erlap: t
+00000070 68 69 73 20 69 73 20 64 65 6c 61 79 65 64 0d 0a his is d elayed..
+00000080 58 2d 4f 6f 4f 2d 4f 76 65 72 6c 61 70 32 3a 20 X-OoO-Ov erlap2:
+00000090 73 65 63 6f 6e 64 20 64 65 6c 61 79 65 64 0d 0a second d elayed..
+000000A0 58 2d 4f 6f 4f 2d 4f 76 65 72 6c 61 70 33 3a 65 X-OoO-Ov erlap3:e
+000000B0 78 74 65 6e 64 20 66 72 61 67 6d 65 6e 74 0d 0a xtend fr agment..
+000000C0 5b 33 32 20 62 79 74 65 73 20 6d 69 73 73 69 6e [32 byte s missin
+000000D0 67 20 69 6e 20 63 61 70 74 75 72 65 20 66 69 6c g in cap ture fil
+000000E0 65 5d 00 e].
+000000E3 58 2d 4d 69 73 73 69 6e 67 2d 42 75 74 2d 41 63 X-Missin g-But-Ac
+000000F3 6b 65 64 2d 50 72 65 76 69 6f 75 73 3a 31 0d 0a ked-Prev ious:1..
+00000103 5b 31 36 20 62 79 74 65 73 20 6d 69 73 73 69 6e [16 byte s missin
+00000113 67 20 69 6e 20 63 61 70 74 75 72 65 20 66 69 6c g in cap ture fil
+00000123 65 5d 00 e].
+00000126 3a :
+00000127 5b 31 33 20 62 79 74 65 73 20 6d 69 73 73 69 6e [13 byte s missin
+00000137 67 20 69 6e 20 63 61 70 74 75 72 65 20 66 69 6c g in cap ture fil
+00000147 65 5d 00 e].
+0000014A 0d .
+0000014B 5b 31 20 62 79 74 65 73 20 6d 69 73 73 69 6e 67 [1 bytes missing
+0000015B 20 69 6e 20 63 61 70 74 75 72 65 20 66 69 6c 65 in capt ure file
+0000016B 5d 00 ].
+0000016D 58 2d 4d 69 73 73 69 6e 67 2d 33 2d 4f 75 74 2d X-Missin g-3-Out-
+0000017D 4f 66 2d 35 2d 42 75 74 2d 41 43 4b 3a 59 0d 0a Of-5-But -ACK:Y..
+0000018D 0d 0a ..
+===================================================================
+""".replace("\r\n", "\n") in proc_stdout
+
+ def test_follow_websocket(self, cmd_tshark, capture_file):
+ '''Checks whether Follow Websocket correctly handles masked data.'''
+ proc_stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('websocket.pcapng.gz'),
+ '-qz', 'follow,websocket,hex,0',
+ ), encoding='utf-8')
+
+ assert """\
+===================================================================
+Follow: websocket,hex
+Filter: tcp.stream eq 0
+Node 0: 127.0.0.1:44380
+Node 1: 127.0.0.1:8080
+00000000 7b 22 74 79 70 65 22 3a 22 72 65 71 75 65 73 74 {"type": "request
+00000010 22 2c 22 64 61 74 61 22 3a 22 31 32 36 35 33 30 ","data" :"126530
+00000020 37 34 34 37 34 30 39 34 39 39 30 31 38 22 7d 0a 74474094 99018"}.
+ 00000000 7b 22 74 79 70 65 22 3a 22 72 65 73 70 6f 6e 73 {"type": "respons
+ 00000010 65 22 2c 22 64 61 74 61 22 3a 22 31 32 36 35 33 e","data ":"12653
+ 00000020 30 37 34 34 37 34 30 39 34 39 39 30 31 38 22 7d 07447409 499018"}
+ 00000030 0a .
+00000030 7b 22 74 79 70 65 22 3a 22 72 65 71 75 65 73 74 {"type": "request
+00000040 22 2c 22 64 61 74 61 22 3a 22 37 38 32 30 36 32 ","data" :"782062
+00000050 33 35 30 33 34 38 39 32 35 35 35 31 31 22 7d 0a 35034892 55511"}.
+ 00000031 7b 22 74 79 70 65 22 3a 22 72 65 73 70 6f 6e 73 {"type": "respons
+ 00000041 65 22 2c 22 64 61 74 61 22 3a 22 37 38 32 30 36 e","data ":"78206
+ 00000051 32 33 35 30 33 34 38 39 32 35 35 35 31 31 22 7d 23503489 255511"}
+ 00000061 0a .
+00000060 7b 22 74 79 70 65 22 3a 22 72 65 71 75 65 73 74 {"type": "request
+00000070 22 2c 22 64 61 74 61 22 3a 22 31 34 32 36 39 33 ","data" :"142693
+00000080 39 37 32 39 33 36 33 37 39 36 30 39 39 22 7d 0a 97293637 96099"}.
+ 00000062 7b 22 74 79 70 65 22 3a 22 72 65 73 70 6f 6e 73 {"type": "respons
+ 00000072 65 22 2c 22 64 61 74 61 22 3a 22 31 34 32 36 39 e","data ":"14269
+ 00000082 33 39 37 32 39 33 36 33 37 39 36 30 39 39 22 7d 39729363 796099"}
+ 00000092 0a .
+00000090 7b 22 74 79 70 65 22 3a 22 72 65 71 75 65 73 74 {"type": "request
+000000A0 22 2c 22 64 61 74 61 22 3a 22 31 30 33 30 31 37 ","data" :"103017
+000000B0 37 39 31 35 36 35 32 31 34 34 31 31 36 22 7d 0a 79156521 44116"}.
+ 00000093 7b 22 74 79 70 65 22 3a 22 72 65 73 70 6f 6e 73 {"type": "respons
+ 000000A3 65 22 2c 22 64 61 74 61 22 3a 22 31 30 33 30 31 e","data ":"10301
+ 000000B3 37 37 39 31 35 36 35 32 31 34 34 31 31 36 22 7d 77915652 144116"}
+ 000000C3 0a .
+===================================================================
+""".replace("\r\n", "\n") in proc_stdout
+
+ def test_follow_websocket_fragmented(self, cmd_tshark, capture_file):
+ '''Checks whether Follow Websocket correctly handles fragmented data.'''
+ proc_stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('websocket-fragmented.pcapng.gz'),
+ '-qz', 'follow,websocket,hex,0',
+ ), encoding='utf-8')
+
+ assert """\
+===================================================================
+Follow: websocket,hex
+Filter: tcp.stream eq 0
+Node 0: 127.0.0.1:59700
+Node 1: 127.0.0.1:8080
+00000000 7b 22 74 79 70 65 22 3a 22 72 65 71 75 65 73 74 {"type": "request
+00000010 22 2c 22 64 61 74 61 22 3a 22 37 34 35 33 36 33 ","data" :"745363
+00000020 31 36 30 32 33 33 38 32 37 33 33 37 32 36 32 32 16023382 73372622
+00000030 38 33 38 32 34 39 31 36 39 38 32 35 39 39 39 38 83824916 98259998
+00000040 37 30 30 36 32 36 34 37 36 33 37 35 34 39 30 35 70062647 63754905
+00000050 31 36 34 32 34 32 32 33 33 37 39 37 30 31 37 38 16424223 37970178
+00000060 37 32 36 30 38 33 38 32 31 36 35 33 32 37 33 35 72608382 16532735
+00000070 31 39 32 38 39 33 32 39 31 36 36 33 35 38 37 36 19289329 16635876
+00000080 36 34 37 35 30 30 31 39 32 36 32 30 33 38 31 37 64750019 26203817
+00000090 33 30 37 34 31 36 31 32 33 36 36 38 35 38 31 30 30741612 36685810
+000000A0 33 32 36 34 32 36 35 31 38 37 37 31 34 33 31 38 32642651 87714318
+000000B0 31 22 7d 0a 1"}.
+ 00000000 7b 22 74 79 70 65 22 3a 22 72 65 73 70 6f 6e 73 {"type": "respons
+ 00000010 65 22 2c 22 64 61 74 61 22 3a 22 37 34 35 33 36 e","data ":"74536
+ 00000020 33 31 36 30 32 33 33 38 32 37 33 33 37 32 36 32 31602338 27337262
+ 00000030 32 38 33 38 32 34 39 31 36 39 38 32 35 39 39 39 28382491 69825999
+ 00000040 38 37 30 30 36 32 36 34 37 36 33 37 35 34 39 30 87006264 76375490
+ 00000050 35 31 36 34 32 34 32 32 33 33 37 39 37 30 31 37 51642422 33797017
+ 00000060 38 37 32 36 30 38 33 38 32 31 36 35 33 32 37 33 87260838 21653273
+ 00000070 35 31 39 32 38 39 33 32 39 31 36 36 33 35 38 37 51928932 91663587
+ 00000080 36 36 34 37 35 30 30 31 39 32 36 32 30 33 38 31 66475001 92620381
+ 00000090 37 33 30 37 34 31 36 31 32 33 36 36 38 35 38 31 73074161 23668581
+ 000000A0 30 33 32 36 34 32 36 35 31 38 37 37 31 34 33 31 03264265 18771431
+ 000000B0 38 31 22 7d 0a 81"}.
+000000B4 7b 22 74 79 70 65 22 3a 22 72 65 71 75 65 73 74 {"type": "request
+000000C4 22 2c 22 64 61 74 61 22 3a 22 35 30 30 30 37 39 ","data" :"500079
+000000D4 37 37 35 32 38 33 33 37 32 35 36 31 33 38 30 30 77528337 25613800
+000000E4 36 36 30 36 30 39 30 35 37 30 30 37 38 35 35 38 66060905 70078558
+000000F4 35 30 39 38 31 35 35 33 31 36 36 39 30 34 38 34 50981553 16690484
+00000104 30 34 39 39 31 36 31 38 34 38 37 37 34 34 39 33 04991618 48774493
+00000114 32 35 34 34 35 36 38 31 32 35 30 36 37 37 30 39 25445681 25067709
+00000124 35 38 32 38 35 34 30 36 39 36 36 34 34 36 32 30 58285406 96644620
+00000134 30 34 38 34 38 34 32 39 35 39 34 34 34 35 34 33 04848429 59444543
+00000144 31 39 33 39 39 39 32 35 39 32 39 38 37 32 31 38 19399925 92987218
+00000154 39 34 36 38 31 38 33 33 30 35 33 38 33 38 36 30 94681833 05383860
+00000164 32 38 22 7d 0a 28"}.
+ 000000B5 7b 22 74 79 70 65 22 3a 22 72 65 73 70 6f 6e 73 {"type": "respons
+ 000000C5 65 22 2c 22 64 61 74 61 22 3a 22 35 30 30 30 37 e","data ":"50007
+ 000000D5 39 37 37 35 32 38 33 33 37 32 35 36 31 33 38 30 97752833 72561380
+ 000000E5 30 36 36 30 36 30 39 30 35 37 30 30 37 38 35 35 06606090 57007855
+ 000000F5 38 35 30 39 38 31 35 35 33 31 36 36 39 30 34 38 85098155 31669048
+ 00000105 34 30 34 39 39 31 36 31 38 34 38 37 37 34 34 39 40499161 84877449
+ 00000115 33 32 35 34 34 35 36 38 31 32 35 30 36 37 37 30 32544568 12506770
+ 00000125 39 35 38 32 38 35 34 30 36 39 36 36 34 34 36 32 95828540 69664462
+ 00000135 30 30 34 38 34 38 34 32 39 35 39 34 34 34 35 34 00484842 95944454
+ 00000145 33 31 39 33 39 39 39 32 35 39 32 39 38 37 32 31 31939992 59298721
+ 00000155 38 39 34 36 38 31 38 33 33 30 35 33 38 33 38 36 89468183 30538386
+ 00000165 30 32 38 22 7d 0a 028"}.
+===================================================================
+""".replace("\r\n", "\n") in proc_stdout
+
+ def test_follow_websocket_compressed(self, cmd_tshark, capture_file):
+ '''Checks whether Follow Websocket correctly handles compressed data.'''
+ proc_stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('websocket-compressed.pcapng.gz'),
+ '-qz', 'follow,websocket,hex,0',
+ ), encoding='utf-8')
+
+ assert """\
+===================================================================
+Follow: websocket,hex
+Filter: tcp.stream eq 0
+Node 0: 127.0.0.1:55256
+Node 1: 127.0.0.1:8080
+00000000 7b 22 74 79 70 65 22 3a 22 72 65 71 75 65 73 74 {"type": "request
+00000010 22 2c 22 64 61 74 61 22 3a 22 33 38 35 37 34 34 ","data" :"385744
+00000020 35 34 33 35 38 33 37 32 39 37 33 30 37 33 38 37 54358372 97307387
+00000030 31 35 33 32 32 35 32 38 37 39 34 30 34 34 32 38 15322528 79404428
+00000040 36 30 33 38 36 31 34 30 38 33 30 33 36 33 38 36 60386140 83036386
+00000050 36 34 38 37 30 39 36 33 31 39 36 30 32 36 33 34 64870963 19602634
+00000060 39 36 38 30 37 36 34 31 33 30 33 35 35 39 30 30 96807641 30355900
+00000070 37 30 37 36 36 30 33 33 31 35 32 33 34 36 32 33 70766033 15234623
+00000080 34 30 33 39 35 39 34 37 35 30 36 39 31 35 39 34 40395947 50691594
+00000090 38 34 34 35 37 36 37 39 38 38 34 30 30 33 34 34 84457679 88400344
+000000A0 36 39 32 35 38 36 32 36 33 30 39 34 34 32 35 36 69258626 30944256
+000000B0 32 22 7d 0a 2"}.
+ 00000000 7b 22 74 79 70 65 22 3a 22 72 65 73 70 6f 6e 73 {"type": "respons
+ 00000010 65 22 2c 22 64 61 74 61 22 3a 22 33 38 35 37 34 e","data ":"38574
+ 00000020 34 35 34 33 35 38 33 37 32 39 37 33 30 37 33 38 45435837 29730738
+ 00000030 37 31 35 33 32 32 35 32 38 37 39 34 30 34 34 32 71532252 87940442
+ 00000040 38 36 30 33 38 36 31 34 30 38 33 30 33 36 33 38 86038614 08303638
+ 00000050 36 36 34 38 37 30 39 36 33 31 39 36 30 32 36 33 66487096 31960263
+ 00000060 34 39 36 38 30 37 36 34 31 33 30 33 35 35 39 30 49680764 13035590
+ 00000070 30 37 30 37 36 36 30 33 33 31 35 32 33 34 36 32 07076603 31523462
+ 00000080 33 34 30 33 39 35 39 34 37 35 30 36 39 31 35 39 34039594 75069159
+ 00000090 34 38 34 34 35 37 36 37 39 38 38 34 30 30 33 34 48445767 98840034
+ 000000A0 34 36 39 32 35 38 36 32 36 33 30 39 34 34 32 35 46925862 63094425
+ 000000B0 36 32 22 7d 0a 62"}.
+000000B4 7b 22 74 79 70 65 22 3a 22 72 65 71 75 65 73 74 {"type": "request
+000000C4 22 2c 22 64 61 74 61 22 3a 22 36 31 37 30 30 39 ","data" :"617009
+000000D4 33 36 30 38 33 33 38 33 34 38 39 30 37 31 39 38 36083383 48907198
+000000E4 34 39 33 35 35 38 38 38 33 31 30 35 30 36 32 31 49355888 31050621
+000000F4 33 38 31 33 35 39 31 38 30 38 34 37 33 39 36 32 38135918 08473962
+00000104 35 31 36 37 35 37 33 34 30 31 31 36 36 31 34 37 51675734 01166147
+00000114 38 34 34 34 33 31 33 30 39 31 35 30 33 33 35 37 84443130 91503357
+00000124 31 31 36 35 38 37 39 34 32 36 34 32 30 34 36 34 11658794 26420464
+00000134 37 39 38 39 30 33 39 30 37 31 39 30 34 35 35 39 79890390 71904559
+00000144 33 31 37 35 37 30 31 34 31 33 33 31 35 33 33 34 31757014 13315334
+00000154 31 34 30 35 39 32 38 36 34 37 33 30 35 32 31 38 14059286 47305218
+00000164 39 32 22 7d 0a 92"}.
+ 000000B5 7b 22 74 79 70 65 22 3a 22 72 65 73 70 6f 6e 73 {"type": "respons
+ 000000C5 65 22 2c 22 64 61 74 61 22 3a 22 36 31 37 30 30 e","data ":"61700
+ 000000D5 39 33 36 30 38 33 33 38 33 34 38 39 30 37 31 39 93608338 34890719
+ 000000E5 38 34 39 33 35 35 38 38 38 33 31 30 35 30 36 32 84935588 83105062
+ 000000F5 31 33 38 31 33 35 39 31 38 30 38 34 37 33 39 36 13813591 80847396
+ 00000105 32 35 31 36 37 35 37 33 34 30 31 31 36 36 31 34 25167573 40116614
+ 00000115 37 38 34 34 34 33 31 33 30 39 31 35 30 33 33 35 78444313 09150335
+ 00000125 37 31 31 36 35 38 37 39 34 32 36 34 32 30 34 36 71165879 42642046
+ 00000135 34 37 39 38 39 30 33 39 30 37 31 39 30 34 35 35 47989039 07190455
+ 00000145 39 33 31 37 35 37 30 31 34 31 33 33 31 35 33 33 93175701 41331533
+ 00000155 34 31 34 30 35 39 32 38 36 34 37 33 30 35 32 31 41405928 64730521
+ 00000165 38 39 32 22 7d 0a 892"}.
+===================================================================
+""".replace("\r\n", "\n") in proc_stdout
+
+ def test_follow_websocket_compressed_fragmented(self, cmd_tshark, capture_file):
+ '''Checks whether Follow Websocket correctly handles compressed and fragmented data.'''
+ proc_stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('websocket-compressed-fragmented.pcapng.gz'),
+ '-qz', 'follow,websocket,hex,0',
+ ), encoding='utf-8')
+
+ assert """\
+===================================================================
+Follow: websocket,hex
+Filter: tcp.stream eq 0
+Node 0: 127.0.0.1:39964
+Node 1: 127.0.0.1:8080
+00000000 7b 22 74 79 70 65 22 3a 22 72 65 71 75 65 73 74 {"type": "request
+00000010 22 2c 22 64 61 74 61 22 3a 22 36 30 36 32 39 36 ","data" :"606296
+00000020 32 34 35 35 32 37 33 39 33 32 32 35 33 36 35 33 24552739 32253653
+00000030 38 37 33 33 35 37 37 37 30 39 37 32 36 31 38 36 87335777 09726186
+00000040 36 34 38 38 36 32 39 32 30 34 31 32 30 38 36 37 64886292 04120867
+00000050 34 32 30 33 38 33 39 38 33 33 35 32 34 35 31 35 42038398 33524515
+00000060 35 32 38 34 33 39 36 36 35 31 36 36 39 35 31 37 52843966 51669517
+00000070 36 34 37 35 35 30 35 39 39 37 36 34 30 32 38 33 64755059 97640283
+00000080 35 31 39 34 33 35 35 34 39 34 30 38 36 33 38 37 51943554 94086387
+00000090 34 34 37 31 35 33 35 33 31 37 33 34 33 33 38 34 44715353 17343384
+000000A0 31 35 31 30 37 35 37 30 35 39 37 33 37 37 30 39 15107570 59737709
+000000B0 39 39 30 30 34 31 36 38 38 38 38 38 35 32 38 30 99004168 88885280
+000000C0 32 36 34 35 38 32 36 35 35 32 39 36 38 33 35 38 26458265 52968358
+000000D0 38 39 30 37 33 35 31 31 37 36 36 31 36 33 31 38 89073511 76616318
+000000E0 39 33 31 31 33 33 39 38 33 39 34 30 30 37 39 36 93113398 39400796
+000000F0 32 33 38 38 38 39 33 34 31 37 35 35 36 34 36 35 23888934 17556465
+00000100 37 35 30 38 33 37 39 32 36 39 35 31 36 31 33 37 75083792 69516137
+00000110 32 34 38 38 38 30 33 33 39 33 32 31 35 37 32 34 24888033 93215724
+00000120 38 34 38 36 31 36 39 39 30 30 33 38 31 36 31 35 84861699 00381615
+00000130 34 38 35 34 34 38 33 39 34 30 34 37 37 35 34 35 48544839 40477545
+00000140 33 32 36 33 36 35 39 33 22 7d 0a 32636593 "}.
+ 00000000 7b 22 74 79 70 65 22 3a 22 72 65 73 70 6f 6e 73 {"type": "respons
+ 00000010 65 22 2c 22 64 61 74 61 22 3a 22 36 30 36 32 39 e","data ":"60629
+ 00000020 36 32 34 35 35 32 37 33 39 33 32 32 35 33 36 35 62455273 93225365
+ 00000030 33 38 37 33 33 35 37 37 37 30 39 37 32 36 31 38 38733577 70972618
+ 00000040 36 36 34 38 38 36 32 39 32 30 34 31 32 30 38 36 66488629 20412086
+ 00000050 37 34 32 30 33 38 33 39 38 33 33 35 32 34 35 31 74203839 83352451
+ 00000060 35 35 32 38 34 33 39 36 36 35 31 36 36 39 35 31 55284396 65166951
+ 00000070 37 36 34 37 35 35 30 35 39 39 37 36 34 30 32 38 76475505 99764028
+ 00000080 33 35 31 39 34 33 35 35 34 39 34 30 38 36 33 38 35194355 49408638
+ 00000090 37 34 34 37 31 35 33 35 33 31 37 33 34 33 33 38 74471535 31734338
+ 000000A0 34 31 35 31 30 37 35 37 30 35 39 37 33 37 37 30 41510757 05973770
+ 000000B0 39 39 39 30 30 34 31 36 38 38 38 38 38 35 32 38 99900416 88888528
+ 000000C0 30 32 36 34 35 38 32 36 35 35 32 39 36 38 33 35 02645826 55296835
+ 000000D0 38 38 39 30 37 33 35 31 31 37 36 36 31 36 33 31 88907351 17661631
+ 000000E0 38 39 33 31 31 33 33 39 38 33 39 34 30 30 37 39 89311339 83940079
+ 000000F0 36 32 33 38 38 38 39 33 34 31 37 35 35 36 34 36 62388893 41755646
+ 00000100 35 37 35 30 38 33 37 39 32 36 39 35 31 36 31 33 57508379 26951613
+ 00000110 37 32 34 38 38 38 30 33 33 39 33 32 31 35 37 32 72488803 39321572
+ 00000120 34 38 34 38 36 31 36 39 39 30 30 33 38 31 36 31 48486169 90038161
+ 00000130 35 34 38 35 34 34 38 33 39 34 30 34 37 37 35 34 54854483 94047754
+ 00000140 35 33 32 36 33 36 35 39 33 22 7d 0a 53263659 3"}.
+0000014B 7b 22 74 79 70 65 22 3a 22 72 65 71 75 65 73 74 {"type": "request
+0000015B 22 2c 22 64 61 74 61 22 3a 22 34 31 30 35 32 30 ","data" :"410520
+0000016B 34 31 33 30 36 36 36 36 32 32 30 38 32 35 33 31 41306666 22082531
+0000017B 36 36 33 34 37 39 32 39 32 31 31 39 39 37 32 35 66347929 21199725
+0000018B 35 38 35 38 37 39 32 32 30 39 31 36 31 38 37 39 58587922 09161879
+0000019B 36 38 36 37 32 36 33 32 34 35 39 35 34 35 39 35 68672632 45954595
+000001AB 34 31 31 32 38 31 36 32 39 38 37 33 31 31 32 32 41128162 98731122
+000001BB 31 36 39 38 31 38 35 37 32 33 38 34 36 37 39 34 16981857 23846794
+000001CB 37 36 36 33 30 38 38 36 34 39 33 36 36 37 34 35 76630886 49366745
+000001DB 32 37 37 39 39 33 31 37 34 37 32 33 39 37 37 39 27799317 47239779
+000001EB 38 38 39 30 31 32 33 39 33 33 37 37 35 30 35 36 88901239 33775056
+000001FB 31 34 30 34 37 38 31 32 39 32 30 36 31 30 34 32 14047812 92061042
+0000020B 35 30 34 38 35 39 34 35 34 37 35 33 30 39 34 35 50485945 47530945
+0000021B 37 32 35 30 35 33 34 33 39 32 31 36 34 35 37 35 72505343 92164575
+0000022B 37 34 39 30 34 33 30 36 35 38 32 31 39 36 39 35 74904306 58219695
+0000023B 37 37 37 34 32 39 35 39 39 32 34 32 37 38 32 32 77742959 92427822
+0000024B 37 38 38 31 37 33 36 38 38 38 39 31 37 37 32 31 78817368 88917721
+0000025B 36 39 38 30 35 31 38 38 35 39 32 32 32 32 34 37 69805188 59222247
+0000026B 32 36 31 37 39 38 35 36 32 31 34 33 30 38 33 35 26179856 21430835
+0000027B 38 38 38 38 39 36 38 33 39 38 33 38 33 34 31 33 88889683 98383413
+0000028B 31 35 34 35 35 33 22 7d 0a 154553"} .
+ 0000014C 7b 22 74 79 70 65 22 3a 22 72 65 73 70 6f 6e 73 {"type": "respons
+ 0000015C 65 22 2c 22 64 61 74 61 22 3a 22 34 31 30 35 32 e","data ":"41052
+ 0000016C 30 34 31 33 30 36 36 36 36 32 32 30 38 32 35 33 04130666 62208253
+ 0000017C 31 36 36 33 34 37 39 32 39 32 31 31 39 39 37 32 16634792 92119972
+ 0000018C 35 35 38 35 38 37 39 32 32 30 39 31 36 31 38 37 55858792 20916187
+ 0000019C 39 36 38 36 37 32 36 33 32 34 35 39 35 34 35 39 96867263 24595459
+ 000001AC 35 34 31 31 32 38 31 36 32 39 38 37 33 31 31 32 54112816 29873112
+ 000001BC 32 31 36 39 38 31 38 35 37 32 33 38 34 36 37 39 21698185 72384679
+ 000001CC 34 37 36 36 33 30 38 38 36 34 39 33 36 36 37 34 47663088 64936674
+ 000001DC 35 32 37 37 39 39 33 31 37 34 37 32 33 39 37 37 52779931 74723977
+ 000001EC 39 38 38 39 30 31 32 33 39 33 33 37 37 35 30 35 98890123 93377505
+ 000001FC 36 31 34 30 34 37 38 31 32 39 32 30 36 31 30 34 61404781 29206104
+ 0000020C 32 35 30 34 38 35 39 34 35 34 37 35 33 30 39 34 25048594 54753094
+ 0000021C 35 37 32 35 30 35 33 34 33 39 32 31 36 34 35 37 57250534 39216457
+ 0000022C 35 37 34 39 30 34 33 30 36 35 38 32 31 39 36 39 57490430 65821969
+ 0000023C 35 37 37 37 34 32 39 35 39 39 32 34 32 37 38 32 57774295 99242782
+ 0000024C 32 37 38 38 31 37 33 36 38 38 38 39 31 37 37 32 27881736 88891772
+ 0000025C 31 36 39 38 30 35 31 38 38 35 39 32 32 32 32 34 16980518 85922224
+ 0000026C 37 32 36 31 37 39 38 35 36 32 31 34 33 30 38 33 72617985 62143083
+ 0000027C 35 38 38 38 38 39 36 38 33 39 38 33 38 33 34 31 58888968 39838341
+ 0000028C 33 31 35 34 35 35 33 22 7d 0a 3154553" }.
+===================================================================
+""".replace("\r\n", "\n") in proc_stdout
diff --git a/test/suite_follow_dccp.py b/test/suite_follow_dccp.py
new file mode 100644
index 0000000..c28ee3c
--- /dev/null
+++ b/test/suite_follow_dccp.py
@@ -0,0 +1,87 @@
+#
+# Wireshark tests
+#
+# Copyright 2020-2021 by Thomas Dreibholz <dreibh [AT] simula.no>
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+'''Follow DCCP Stream tests'''
+
+import subprocess
+import pytest
+
+
+class TestFollowDCCP:
+ def test_follow_dccp_existing_flow(self, cmd_tshark, capture_file, test_env):
+ '''Checks whether Follow DCCP correctly handles an existing flow.'''
+
+ # Test 1:
+ # 1. Identification of DCCP Flow #9
+ # 2. Selection and decoding of DCCP Flow #9
+ proc = subprocess.run((cmd_tshark,
+ '-r', capture_file('netperfmeter.pcapng.gz'),
+ '-qz', 'follow,dccp,hex,9',
+ ), check=True, capture_output=True, encoding='utf-8', env=test_env)
+
+ result = ''.join([x.strip()+"\n" for x in proc.stdout.splitlines()])
+ assert r"""
+===================================================================
+Follow: dccp,hex
+Filter: dccp.stream eq 9
+Node 0: 192.168.0.20:42807
+Node 1: 192.168.0.27:9000
+00000000 04 00 00 1a 00 00 00 0d 4b cd f3 aa 30 3c 67 74 ........ K...0<gt
+00000010 12 12 01 be 09 2f f4 24 00 00 ...../.$ ..
+0000001A 05 03 01 00 00 00 00 0d 12 12 01 be 09 2f f4 24 ........ ...../.$
+0000002A 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........
+0000003A 00 00 00 00 00 00 00 00 00 05 bc a2 0c c3 83 68 ........ .......h
+0000004A 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d .. !"#$% &'()*+,-
+0000005A 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d ./012345 6789:;<=
+0000006A 3e 3f 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d >?@ABCDE FGHIJKLM
+0000007A 4e 4f 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d NOPQRSTU VWXYZ[\]
+0000008A 5e 5f 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d ^_`abcde fghijklm
+0000009A 6e 6f 70 71 72 73 74 75 76 77 78 79 7a 7b 7c 7d nopqrstu vwxyz{|}
+000000AA 7e 7f 1e 1f 20 21 22 23 24 25 26 27 28 29 2a 2b ~... !"# $%&'()*+
+000000BA 2c 2d 2e 2f 30 31 32 33 34 35 36 37 38 39 3a 3b ,-./0123 456789:;
+000000CA 3c 3d 3e 3f 40 41 42 43 44 45 46 47 48 49 4a 4b <=>?@ABC DEFGHIJK
+000000DA 4c 4d 4e 4f 50 51 52 53 54 55 56 57 58 59 5a 5b LMNOPQRS TUVWXYZ[
+000000EA 5c 5d 5e 5f 60 61 62 63 64 65 66 67 68 69 6a 6b \]^_`abc defghijk
+000000FA 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a 7b lmnopqrs tuvwxyz{
+0000010A 7c 7d 7e 7f 1e 1f 20 21 22 23 24 25 26 27 28 29 |}~... ! "#$%&'()
+00000000 05 03 01 00 00 00 00 0d 12 12 01 be 09 2f f4 24 ........ ...../.$
+00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ........
+00000020 00 00 00 00 00 00 00 00 00 05 bc a2 0c c5 76 71 ........ ......vq
+00000030 7f 7e 7d 7c 7b 7a 79 78 77 76 75 74 73 72 71 70 .~}|{zyx wvutsrqp
+00000040 6f 6e 6d 6c 6b 6a 69 68 67 66 65 64 63 62 61 60 onmlkjih gfedcba`
+00000050 5f 5e 5d 5c 5b 5a 59 58 57 56 55 54 53 52 51 50 _^]\[ZYX WVUTSRQP
+00000060 4f 4e 4d 4c 4b 4a 49 48 47 46 45 44 43 42 41 40 ONMLKJIH GFEDCBA@
+00000070 3f 3e 3d 3c 3b 3a 39 38 37 36 35 34 33 32 31 30 ?>=<;:98 76543210
+00000080 2f 2e 2d 2c 2b 2a 29 28 27 26 25 24 23 22 21 20 /.-,+*)( '&%$#"!
+00000090 1f 1e 7f 7e 7d 7c 7b 7a 79 78 77 76 75 74 73 72 ...~}|{z yxwvutsr
+000000A0 71 70 6f 6e 6d 6c 6b 6a 69 68 67 66 65 64 63 62 qponmlkj ihgfedcb
+000000B0 61 60 5f 5e 5d 5c 5b 5a 59 58 57 56 55 54 53 52 a`_^]\[Z YXWVUTSR
+000000C0 51 50 4f 4e 4d 4c 4b 4a 49 48 47 46 45 44 43 42 QPONMLKJ IHGFEDCB
+000000D0 41 40 3f 3e 3d 3c 3b 3a 39 38 37 36 35 34 33 32 A@?>=<;: 98765432
+000000E0 31 30 2f 2e 2d 2c 2b 2a 29 28 27 26 25 24 23 22 10/.-,+* )('&%$#"
+000000F0 21 20 1f 1e 7f 7e 7d 7c 7b 7a 79 78 77 76 75 74 ! ...~}| {zyxwvut
+""".replace("\r\n", "\n") in result
+
+
+ def test_follow_dccp_non_existing_flow(self, cmd_tshark, capture_file, test_env):
+ '''Checks whether Follow DCCP correctly handles a non-existing existing flow.'''
+
+ # Test 2:
+ # Trying identification of not-existing DCCP Flow #10
+ proc = subprocess.run((cmd_tshark,
+ '-r', capture_file('netperfmeter.pcapng.gz'),
+ '-qz', 'follow,dccp,hex,10',
+ ), check=True, capture_output=True, encoding='utf-8', env=test_env)
+
+ assert """\
+===================================================================
+Follow: dccp,hex
+Filter: dccp.stream eq 10
+Node 0: :0
+Node 1: :0
+===================================================================
+""".replace("\r\n", "\n") in proc.stdout
diff --git a/test/suite_follow_multistream.py b/test/suite_follow_multistream.py
new file mode 100644
index 0000000..d7017dd
--- /dev/null
+++ b/test/suite_follow_multistream.py
@@ -0,0 +1,62 @@
+#
+# Wireshark tests
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+'''Follow QUIC/HTTP2 Stream tests'''
+
+import subprocess
+import pytest
+
+
+class TestFollowMultistream:
+ def test_follow_http2_multistream(self, cmd_tshark, capture_file, test_env, features):
+ '''Checks whether Follow HTTP2 correctly handles multiple streams on the same packet.'''
+ # If we don't have nghttp2, we output the compressed headers.
+ # We could test against the expected output in that case, but
+ # just skip for now.
+ if not features.have_nghttp2:
+ pytest.skip('Requires nghttp2.')
+ # Test 1:
+ # 1. While following stream 25 we should ignore stream 21 at frame 65
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('http2_follow_multistream.pcapng'),
+ '-qz', 'follow,http2,raw,0,25',
+ ), encoding='utf-8', universal_newlines=True, env=test_env)
+
+ assert """\
+===================================================================
+Follow: http2,raw
+Filter: tcp.stream eq 0 and http2.streamid eq 25
+Node 0: 10.9.0.2:59246
+Node 1: 104.16.40.2:443
+3a6d6574686f643a204745540a3a706174683a202f6d656469612f6a732f66697265666f785f666972737472756e2d62756e646c652e3464633733383035353861622e6a730a3a617574686f726974793a207777772e6d6f7a696c6c612e6f72670a3a736368656d653a2068747470730a757365722d6167656e743a204d6f7a696c6c612f352e3020285831313b204c696e7578207838365f36343b2072763a35322e3029204765636b6f2f32303130303130312046697265666f782f35322e300a6163636570743a202a2f2a0a6163636570742d6c616e67756167653a20656e2d55532c656e3b713d302e350a6163636570742d656e636f64696e673a20677a69702c206465666c6174652c2062720a726566657265723a2068747470733a2f2f7777772e6d6f7a696c6c612e6f72672f656e2d55532f66697265666f782f35322e302e312f666972737472756e2f0a0a
+00000408000000001900be0000
+ 3a7374617475733a203230300a646174653a205765642c203239204d617220323031372031313a35393a333020474d540a636f6e74656e742d747970653a206170706c69636174696f6e2f6a6176617363726970743b20636861727365743d227574662d38220a6163636573732d636f6e74726f6c2d616c6c6f772d6f726967696e3a202a0a63616368652d636f6e74726f6c3a206d61782d6167653d3331353336303030302c207075626c69632c20696d6d757461626c650a636f6e74656e742d656e636f64696e673a20677a69700a636f6e74656e742d73656375726974792d706f6c6963793a207363726970742d737263202773656c6627202a2e6d6f7a696c6c612e6e6574202a2e6d6f7a696c6c612e6f7267202a2e6d6f7a696c6c612e636f6d2027756e736166652d696e6c696e65272027756e736166652d6576616c27202a2e6f7074696d697a656c792e636f6d206f7074696d697a656c792e73332e616d617a6f6e6177732e636f6d207777772e676f6f676c657461676d616e616765722e636f6d207777772e676f6f676c652d616e616c79746963732e636f6d207461676d616e616765722e676f6f676c652e636f6d207777772e796f75747562652e636f6d20732e7974696d672e636f6d3b20696d672d737263202773656c6627202a2e6d6f7a696c6c612e6e6574202a2e6d6f7a696c6c612e6f7267202a2e6d6f7a696c6c612e636f6d20646174613a202a2e6f7074696d697a656c792e636f6d207777772e676f6f676c657461676d616e616765722e636f6d207777772e676f6f676c652d616e616c79746963732e636f6d202a2e74696c65732e6d6170626f782e636f6d206170692e6d6170626f782e636f6d206372656174697665636f6d6d6f6e732e6f72672061642e646f75626c65636c69636b2e6e65743b2064656661756c742d737263202773656c6627202a2e6d6f7a696c6c612e6e6574202a2e6d6f7a696c6c612e6f7267202a2e6d6f7a696c6c612e636f6d3b206672616d652d737263202a2e6f7074696d697a656c792e636f6d207777772e676f6f676c657461676d616e616765722e636f6d207777772e676f6f676c652d616e616c79746963732e636f6d207777772e796f75747562652d6e6f636f6f6b69652e636f6d20747261636b6572746573742e6f7267207777772e73757276657967697a6d6f2e636f6d206163636f756e74732e66697265666f782e636f6d206163636f756e74732e66697265666f782e636f6d2e636e207777772e796f75747562652e636f6d3b207374796c652d737263202773656c6627202a2e6d6f7a696c6c612e6e6574202a2e6d6f7a696c6c612e6f7267202a2e6d6f7a696c6c612e636f6d2027756e736166652d696e6c696e65273b20636f6e6e6563742d737263202773656c6627202a2e6d6f7a696c6c612e6e6574202a2e6d6f7a696c6c612e6f7267202a2e6d6f7a696c6c612e636f6d202a2e6f7074696d697a656c792e636f6d207777772e676f6f676c657461676d616e616765722e636f6d207777772e676f6f676c652d616e616c79746963732e636f6d202a2e74696c65732e6d6170626f782e636f6d206170692e6d6170626f782e636f6d3b206368696c642d737263202a2e6f7074696d697a656c792e636f6d207777772e676f6f676c657461676d616e616765722e636f6d207777772e676f6f676c652d616e616c79746963732e636f6d207777772e796f75747562652d6e6f636f6f6b69652e636f6d20747261636b6572746573742e6f7267207777772e73757276657967697a6d6f2e636f6d206163636f756e74732e66697265666f782e636f6d206163636f756e74732e66697265666f782e636f6d2e636e207777772e796f75747562652e636f6d0a6c6173742d6d6f6469666965643a205468752c203032204d617220323031372031303a33363a333220474d540a7374726963742d7472616e73706f72742d73656375726974793a206d61782d6167653d33313533363030300a766172793a204163636570742d456e636f64696e670a782d6261636b656e642d7365727665723a206434373333376330323631352e626564726f636b2d70726f642e75732d776573742e6d6f7a2e776f726b730a782d636c61636b732d6f766572686561643a20474e55205465727279205072617463686574740a782d636f6e74656e742d747970652d6f7074696f6e733a206e6f736e6966660a782d6672616d652d6f7074696f6e733a2044454e590a782d726f626f74732d7461673a206e6f6f64700a782d7873732d70726f74656374696f6e3a20313b206d6f64653d626c6f636b0a63662d63616368652d7374617475733a204849540a7365727665723a20636c6f7564666c6172652d6e67696e780a63662d7261793a20333437326139643836653930373334332d414d530a0a
+ 696628747970656f66204d6f7a696c6c613d3d22756e646566696e65642229766172204d6f7a696c6c613d7b7d3b4d6f7a696c6c612e467861496672616d653d66756e6374696f6e28297b2275736520737472696374223b76617220652c742c6e3d21312c722c692c732c6f2c753d66756e6374696f6e2865297b72657475726e20655b652e6c656e6774682d315d3d3d3d222f223f652e73756273747228302c652e6c656e6774682d31293a657d2c613d66756e6374696f6e2861297b653d612c723d242822236678612d696672616d652d636f6e66696722292e646174612822686f737422292c733d2428222366786122292c6f3d732e64617461282273726322292c77696e646f772e6164644576656e744c697374656e657228226d657373616765222c682c2130292c652e75736572456d61696c26262f282e2b2940282e2b295c2e282e2b297b322c7d2f2e7465737428652e75736572456d61696c292626286f3d6f2b2226656d61696c3d222b656e636f6465555249436f6d706f6e656e7428652e75736572456d61696c29292c723d752872292c4d6f7a696c6c612e436c69656e742e46697265666f784d616a6f7256657273696f6e3e3d34362626286f3d6f2e7265706c6163652822636f6e746578743d696672616d65222c22636f6e746578743d66785f666972737472756e5f76322229292c6f2b3d22266f726967696e3d222b656e636f6465555249436f6d706f6e656e742877696e646f772e6c6f636174696f6e2e6f726967696e292c743d652e67614576656e744e616d657c7c22667861222c652e646973747269627574696f6e2626652e646973747269627574696f6e213d3d2264656661756c7422262628693d242822236678612d696672616d652d636f6e66696722292e6461746128652e646973747269627574696f6e2e746f4c6f7765724361736528292b22486f737422292c69262628693d752869292c6f3d6f2e7265706c61636528722c69292c723d6929292c732e617474722822737263222c6f292c73657454696d656f75742866756e6374696f6e28297b6e7c7c6628343030297d2c32353030297d2c663d66756e6374696f6e2865297b732e6373732822686569676874222c652b22707822292e616464436c61737328226c6f6164656422297d2c6c3d66756e6374696f6e28652c6e297b76617220723d77696e646f772e646174614c617965727c7c5b5d2c693d7b6576656e743a742c696e746572616374696f6e3a657d3b6e2626242e657874656e6428692c6e292c722e707573682869297d2c633d66756e6374696f6e28742c6e297b747970656f6620655b745d3d3d2266756e6374696f6e222626655b745d286e297d2c683d66756e6374696f6e2865297b696628652e6f726967696e213d3d722972657475726e3b76617220743d4a534f4e2e706172736528652e64617461293b73776974636828742e636f6d6d616e64297b63617365226c6f61646564223a702874293b627265616b3b6361736522726573697a65223a642874293b627265616b3b6361736522666f726d5f656e61626c6564223a762874293b627265616b3b6361736522666f726d5f64697361626c6564223a6d2874293b627265616b3b63617365226e6176696761746564223a672874293b627265616b3b63617365227369676e75705f6d7573745f766572696679223a792874293b627265616b3b6361736522766572696669636174696f6e5f636f6d706c657465223a622874293b627265616b3b63617365226c6f67696e223a772874297d7d2c703d66756e6374696f6e2865297b6e3d21302c6c28226678612d6c6f6164656422292c6328226f6e4c6f61646564222c65297d2c643d66756e6374696f6e2865297b6628652e646174612e686569676874292c6328226f6e526573697a65222c65297d2c763d66756e6374696f6e2865297b6328226f6e466f726d456e61626c6564222c65297d2c6d3d66756e6374696f6e2865297b6328226f6e466f726d44697361626c6564222c65297d2c673d66756e6374696f6e2865297b6328226f6e4e6176696761746564222c65297d2c793d66756e6374696f6e2865297b652e646174612e6861734f776e50726f70657274792822656d61696c4f7074496e222926266c2822656d61696c206f70742d696e222c7b6c6162656c3a652e646174612e656d61696c4f7074496e7d292c6c28226678612d7369676e757022292c6328226f6e5369676e75704d757374566572696679222c65297d2c623d66756e6374696f6e2865297b6c28226678612d766572696669656422292c6328226f6e566572696669636174696f6e436f6d706c657465222c65297d2c773d66756e6374696f6e2874297b6c28226678612d7369676e696e22292c747970656f6620655b226f6e4c6f67696e225d3d3d2266756e6374696f6e223f6328226f6e4c6f67696e222c74293a4d6f7a696c6c612e5574696c732e646f526564697265637428722b222f73657474696e677322297d3b72657475726e7b696e69743a66756e6374696f6e2865297b6128657c7c7b7d297d7d7d2877696e646f772e6a51756572792c77696e646f772e4d6f7a696c6c61292c66756e6374696f6e2865297b2275736520737472696374223b652e436c69656e742e67657446697265666f7844657461696c732866756e6374696f6e2874297b652e467861496672616d652e696e6974287b646973747269627574696f6e3a742e646973747269627574696f6e2c67614576656e744e616d653a22666972737472756e2d667861227d297d297d2877696e646f772e4d6f7a696c6c61293b0a
+===================================================================
+""" in stdout
+
+
+ def test_follow_quic_multistream(self, cmd_tshark, capture_file, test_env):
+ '''Checks whether Follow QUIC correctly handles multiple streams on the same packet.'''
+
+ # Test 2:
+ # 1. While following stream 40 we should ignore stream 36 at frame 426, and stream 11 at frame 623
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('quic_follow_multistream.pcapng'),
+ '-qz', 'follow,quic,raw,0,40',
+ ), encoding='utf-8', universal_newlines=True, env=test_env)
+
+ assert """\
+===================================================================
+Follow: quic,raw
+Filter: quic.connection.number eq 0 and quic.stream.stream_id eq 40
+Node 0: 10.0.2.15:57172
+Node 1: 172.217.5.110:443
+01143000d1aed782e4a7dd81a3a280a0df9f9e9d9c9b
+ 010f3700d982fd819380adf89eeab0aeac00424203500200e4d7dcfefe2fa7a5df3632669896a543445e8a68d10ebc13d704f6af5e7460740ba4e67985c9c8c6b3b9b6b65ce9af3404e6213f749da27b3c523060867c215f30e43343a1a055e4924757cba0cd8fb3b9219712521b197ba3b55d06907660c4a29fee6cb94abce7d1800346c603f68021dad3fdfb7bbada302be0f3e9456a7134b3f0f9c82deb126fa10875b570f96976ea311b77d2e620d2a3cd652555db30dc8da347a1f2be10bc129a180d576cb394a9cfef499a0a037f3a34c6e3047507f07fff215080120182587a6277871ae3e16e2c44ecebdbe77ce1ca6f447a35d2c89749a5054e09679b28fa46bbc58dd10b404ac36436c3e2305f72ff18b847b85e181c0f0ba058c05780a2a12f8ccac69fdf819e4e60b2765852c6304c44064b9ae30b170c8181fde020cee9cb83994176942023abd0c35b0ca571ca0f77a63764b5a170a24f76a4734ef16f858af2950a69b4da1902b8968722e9a328c2f9176d3e71159c82d564e776b8ee6fb648ae94b537ec0efd0898e679d60d230e5f7748a399af565014ddcc22b74b794474155c26c7d02ec9a4180edb2ccd322cd3081a21ed51c0e8e157adb448af052c34dd636a80cb426540cdf0444fcab899105702754b35a95257f9574ab8459a2d20366ed939d280266974f236815603dbc57c250b35dcc0e5a6fa35da21198550625e157282755cfedd0b3f1e1de923dd3f83b1548aeb7411f1e41ae97ca47bb77b467a2462cc8cc8cc1ae95b3edd1c46faee87c1f7e365b7b8782dcbe1a5a7130fa25b94765b32be7029ed5bfeafc40c
+ 000103
+
+===================================================================
+""" in stdout
diff --git a/test/suite_io.py b/test/suite_io.py
new file mode 100644
index 0000000..4376d1b
--- /dev/null
+++ b/test/suite_io.py
@@ -0,0 +1,83 @@
+#
+# Wireshark tests
+# By Gerald Combs <gerald@wireshark.org>
+#
+# Ported from a set of Bash scripts which were copyright 2005 Ulf Lamping
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+'''File I/O tests'''
+
+import io
+import os.path
+import subprocess
+from subprocesstest import cat_dhcp_command, check_packet_count
+import sys
+import pytest
+
+testout_pcap = 'testout.pcap'
+baseline_file = 'io-rawshark-dhcp-pcap.txt'
+
+
+@pytest.fixture(scope='session')
+def io_baseline_str(dirs):
+ with open(os.path.join(dirs.baseline_dir, baseline_file), 'r') as f:
+ return f.read()
+
+
+def check_io_4_packets(capture_file, result_file, cmd_tshark, cmd_capinfos, from_stdin=False, to_stdout=False, env=None):
+ # Test direct->direct, stdin->direct, and direct->stdout file I/O.
+ # Similar to suite_capture.check_capture_10_packets and
+ # suite_capture.check_capture_stdin.
+
+ testout_file = result_file(testout_pcap)
+ if from_stdin and to_stdout:
+ # XXX If we support this, should we bother with separate stdin->direct
+ # and direct->stdout tests?
+ pytest.fail('Stdin and stdout not supported in the same test.')
+ elif from_stdin:
+ # cat -B "${CAPTURE_DIR}dhcp.pcap" | $DUT -r - -w ./testout.pcap 2>./testout.txt
+ cat_dhcp_cmd = cat_dhcp_command('cat')
+ stdin_cmd = '{0} | "{1}" -r - -w "{2}"'.format(cat_dhcp_cmd, cmd_tshark, testout_file)
+ subprocess.check_call(stdin_cmd, shell=True, env=env)
+ elif to_stdout:
+ # $DUT -r "${CAPTURE_DIR}dhcp.pcap" -w - > ./testout.pcap 2>./testout.txt
+ stdout_cmd = '"{0}" -r "{1}" -w - > "{2}"'.format(cmd_tshark, capture_file('dhcp.pcap'), testout_file)
+ subprocess.check_call(stdout_cmd, shell=True, env=env)
+ else: # direct->direct
+ # $DUT -r "${CAPTURE_DIR}dhcp.pcap" -w ./testout.pcap > ./testout.txt 2>&1
+ subprocess.check_call((cmd_tshark,
+ '-r', capture_file('dhcp.pcap'),
+ '-w', testout_file,
+ ), env=env)
+ assert os.path.isfile(testout_file)
+ check_packet_count(cmd_capinfos, 4, testout_file)
+
+
+class TestTsharkIO:
+ def test_tshark_io_stdin_direct(self, cmd_tshark, cmd_capinfos, capture_file, result_file, test_env):
+ '''Read from stdin and write direct using TShark'''
+ check_io_4_packets(capture_file, result_file, cmd_tshark, cmd_capinfos, from_stdin=True, env=test_env)
+
+ def test_tshark_io_direct_stdout(self, cmd_tshark, cmd_capinfos, capture_file, result_file, test_env):
+ '''Read direct and write to stdout using TShark'''
+ check_io_4_packets(capture_file, result_file, cmd_tshark, cmd_capinfos, to_stdout=True, env=test_env)
+
+ def test_tshark_io_direct_direct(self, cmd_tshark, cmd_capinfos, capture_file, result_file, test_env):
+ '''Read direct and write direct using TShark'''
+ check_io_4_packets(capture_file, result_file, cmd_tshark, cmd_capinfos, env=test_env)
+
+
+class TestRawsharkIO:
+ if sys.byteorder != 'little':
+ pytest.skip('Requires a little endian system')
+ def test_rawshark_io_stdin(self, cmd_rawshark, capture_file, result_file, io_baseline_str, test_env):
+ '''Read from stdin using Rawshark'''
+ # tail -c +25 "${CAPTURE_DIR}dhcp.pcap" | $RAWSHARK -dencap:1 -R "udp.port==68" -nr - > $IO_RAWSHARK_DHCP_PCAP_TESTOUT 2> /dev/null
+ # diff -u --strip-trailing-cr $IO_RAWSHARK_DHCP_PCAP_BASELINE $IO_RAWSHARK_DHCP_PCAP_TESTOUT > $DIFF_OUT 2>&1
+ capture_file = capture_file('dhcp.pcap')
+ testout_file = result_file(testout_pcap)
+ raw_dhcp_cmd = cat_dhcp_command('raw')
+ rawshark_cmd = '{0} | "{1}" -r - -n -dencap:1 -R "udp.port==68"'.format(raw_dhcp_cmd, cmd_rawshark)
+ rawshark_stdout = subprocess.check_output(rawshark_cmd, shell=True, encoding='utf-8', env=test_env)
+ assert rawshark_stdout == io_baseline_str
diff --git a/test/suite_mergecap.py b/test/suite_mergecap.py
new file mode 100644
index 0000000..95e657b
--- /dev/null
+++ b/test/suite_mergecap.py
@@ -0,0 +1,270 @@
+#
+# Wireshark tests
+# By Gerald Combs <gerald@wireshark.org>
+#
+# Ported from a set of Bash scripts which were copyright 2005 Ulf Lamping
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+'''Mergecap tests'''
+
+import re
+import subprocess
+from subprocesstest import grep_output
+
+testout_pcap = 'testout.pcap'
+testout_pcapng = 'testout.pcapng'
+
+file_type_to_descr = {
+ 'pcap': 'Wireshark/tcpdump/... - pcap',
+ 'pcapng': 'Wireshark/... - pcapng',
+}
+
+# common checking code:
+# arg 1 = return value from mergecap command
+# arg 2 = file type string
+# arg 3 = file encap
+# arg 4 = number of IDBs generated
+# arg 5 = number of file packets merged
+# arg 6 = number of some IDB packets merged
+def check_mergecap(mergecap_proc, file_type, encapsulation, tot_packets, generated_idbs, idb_packets, cmd_capinfos, testout_file, env=None):
+
+ assert mergecap_proc.returncode == 0
+ assert grep_output(mergecap_proc.stderr, 'merging complete')
+ assert file_type in file_type_to_descr, 'Invalid file type'
+
+ capinfos_stdout = subprocess.check_output([cmd_capinfos, '-t', '-E', '-I', '-c', testout_file], encoding='utf-8', env=env)
+
+ file_descr = file_type_to_descr[file_type]
+ type_pat = r'File type:\s+{}'.format(file_descr)
+ assert re.search(type_pat, capinfos_stdout), \
+ 'Failed to generate a {} file'.format(file_type)
+
+ encap_pat = r'File encapsulation:\s+{}'.format(encapsulation)
+ assert re.search(encap_pat, capinfos_stdout), \
+ 'Failed to generate an {} encapsulation'.format(encapsulation)
+
+ pkt_pat = r'Number of packets:\s+{}'.format(tot_packets)
+ assert re.search(pkt_pat, capinfos_stdout), \
+ 'Failed to generate {} packets'.format(tot_packets)
+
+ gidb_pat = r'Number of interfaces in file:\s+{}'.format(generated_idbs)
+ assert re.search(gidb_pat, capinfos_stdout), \
+ 'Failed to generate {} IDBs'.format(generated_idbs)
+
+ midb_pat = r'\s+Number of packets\s+=\s+{}'.format(idb_packets)
+ assert re.search(midb_pat, capinfos_stdout), \
+ 'Failed to merge {} IDB packets'.format(idb_packets)
+
+
+class TestMergecapPcap:
+ def test_mergecap_basic_1_pcap_pcap(self, cmd_mergecap, capture_file, result_file, cmd_capinfos, test_env):
+ '''Merge a single pcap file to pcap'''
+ # $MERGECAP -vF pcap -w testout.pcap "${CAPTURE_DIR}dhcp.pcap" > testout.txt 2>&1
+ testout_file = result_file(testout_pcap)
+ mergecap_proc = subprocess.run((cmd_mergecap,
+ '-V',
+ '-F', 'pcap',
+ '-w', testout_file,
+ capture_file('dhcp.pcap'),
+ ), capture_output=True, encoding='utf-8', env=test_env)
+ check_mergecap(mergecap_proc, 'pcap', 'Ethernet', 4, 1, 4, cmd_capinfos, testout_file, test_env)
+
+ def test_mergecap_basic_2_pcap_pcap(self, cmd_mergecap, capture_file, result_file, cmd_capinfos, test_env):
+ '''Merge two pcap files to pcap'''
+ # $MERGECAP -vF pcap -w testout.pcap "${CAPTURE_DIR}dhcp.pcap" "${CAPTURE_DIR}dhcp.pcap" > testout.txt 2>&1
+ testout_file = result_file(testout_pcap)
+ mergecap_proc = subprocess.run((cmd_mergecap,
+ '-V',
+ '-F', 'pcap',
+ '-w', testout_file,
+ capture_file('dhcp.pcap'), capture_file('dhcp.pcap'),
+ ), capture_output=True, encoding='utf-8', env=test_env)
+ check_mergecap(mergecap_proc, 'pcap', 'Ethernet', 8, 1, 8, cmd_capinfos, testout_file, test_env)
+
+ def test_mergecap_basic_3_empty_pcap_pcap(self, cmd_mergecap, capture_file, result_file, cmd_capinfos, test_env):
+ '''Merge three pcap files to pcap, two empty'''
+ # $MERGECAP -vF pcap -w testout.pcap "${CAPTURE_DIR}empty.pcap" "${CAPTURE_DIR}dhcp.pcap" "${CAPTURE_DIR}empty.pcap" > testout.txt 2>&1
+ testout_file = result_file(testout_pcap)
+ mergecap_proc = subprocess.run((cmd_mergecap,
+ '-V',
+ '-F', 'pcap',
+ '-w', testout_file,
+ capture_file('empty.pcap'), capture_file('dhcp.pcap'), capture_file('empty.pcap'),
+ ), capture_output=True, encoding='utf-8', env=test_env)
+ check_mergecap(mergecap_proc, 'pcap', 'Ethernet', 4, 1, 4, cmd_capinfos, testout_file, test_env)
+
+ def test_mergecap_basic_2_nano_pcap_pcap(self, cmd_mergecap, capture_file, result_file, cmd_capinfos, test_env):
+ '''Merge two pcap files to pcap, one with nanosecond timestamps'''
+ # $MERGECAP -vF pcap -w testout.pcap "${CAPTURE_DIR}dhcp-nanosecond.pcap" "${CAPTURE_DIR}rsasnakeoil2.pcap" > testout.txt 2>&1
+ testout_file = result_file(testout_pcap)
+ mergecap_proc = subprocess.run((cmd_mergecap,
+ '-V',
+ '-F', 'pcap',
+ '-w', testout_file,
+ capture_file('dhcp-nanosecond.pcap'), capture_file('rsasnakeoil2.pcap'),
+ ), capture_output=True, encoding='utf-8', env=test_env)
+ check_mergecap(mergecap_proc, 'pcap', 'Ethernet', 62, 1, 62, cmd_capinfos, testout_file, test_env)
+
+
+class TestMergecapPcapng:
+ def test_mergecap_basic_1_pcap_pcapng(self, cmd_mergecap, capture_file, result_file, cmd_capinfos, test_env):
+ '''Merge a single pcap file to pcapng'''
+ # $MERGECAP -v -w testout.pcap "${CAPTURE_DIR}dhcp.pcap" > testout.txt 2>&1
+ testout_file = result_file(testout_pcapng)
+ mergecap_proc = subprocess.run((cmd_mergecap,
+ '-V',
+ '-w', testout_file,
+ capture_file('dhcp.pcap'),
+ ), capture_output=True, encoding='utf-8', env=test_env)
+ check_mergecap(mergecap_proc, 'pcapng', 'Ethernet', 4, 1, 4, cmd_capinfos, testout_file, test_env)
+
+ def test_mergecap_basic_2_pcap_pcapng(self, cmd_mergecap, capture_file, result_file, cmd_capinfos, test_env):
+ '''Merge two pcap files to pcapng'''
+ # $MERGECAP -v -w testout.pcap "${CAPTURE_DIR}dhcp.pcap" "${CAPTURE_DIR}dhcp.pcap" > testout.txt 2>&1
+ testout_file = result_file(testout_pcapng)
+ mergecap_proc = subprocess.run((cmd_mergecap,
+ '-V',
+ '-w', testout_file,
+ capture_file('dhcp.pcap'), capture_file('dhcp.pcap'),
+ ), capture_output=True, encoding='utf-8', env=test_env)
+ check_mergecap(mergecap_proc, 'pcapng', 'Ethernet', 8, 1, 8, cmd_capinfos, testout_file, test_env)
+
+ def test_mergecap_basic_2_pcap_none_pcapng(self, cmd_mergecap, capture_file, result_file, cmd_capinfos, test_env):
+ '''Merge two pcap files to pcapng, "none" merge mode'''
+ # $MERGECAP -vI 'none' -w testout.pcap "${CAPTURE_DIR}dhcp.pcap" "${CAPTURE_DIR}dhcp.pcap" > testout.txt 2>&1
+ testout_file = result_file(testout_pcapng)
+ mergecap_proc = subprocess.run((cmd_mergecap,
+ '-V',
+ '-I', 'none',
+ '-w', testout_file,
+ capture_file('dhcp.pcap'), capture_file('dhcp.pcap'),
+ ), capture_output=True, encoding='utf-8', env=test_env)
+ check_mergecap(mergecap_proc, 'pcapng', 'Ethernet', 8, 2, 4, cmd_capinfos, testout_file, test_env)
+
+ def test_mergecap_basic_2_pcap_all_pcapng(self, cmd_mergecap, capture_file, result_file, cmd_capinfos, test_env):
+ '''Merge two pcap files to pcapng, "all" merge mode'''
+ # $MERGECAP -vI 'all' -w testout.pcap "${CAPTURE_DIR}dhcp.pcap" "${CAPTURE_DIR}dhcp.pcap" > testout.txt 2>&1
+ testout_file = result_file(testout_pcapng)
+ mergecap_proc = subprocess.run((cmd_mergecap,
+ '-V',
+ '-I', 'all',
+ '-w', testout_file,
+ capture_file('dhcp.pcap'), capture_file('dhcp.pcap'),
+ ), capture_output=True, encoding='utf-8', env=test_env)
+ check_mergecap(mergecap_proc, 'pcapng', 'Ethernet', 8, 1, 8, cmd_capinfos, testout_file, test_env)
+
+ def test_mergecap_basic_2_pcap_any_pcapng(self, cmd_mergecap, capture_file, result_file, cmd_capinfos, test_env):
+ '''Merge two pcap files to pcapng, "any" merge mode'''
+ # $MERGECAP -vI 'any' -w testout.pcap "${CAPTURE_DIR}dhcp.pcap" "${CAPTURE_DIR}dhcp.pcap" > testout.txt 2>&1
+ testout_file = result_file(testout_pcapng)
+ mergecap_proc = subprocess.run((cmd_mergecap,
+ '-V',
+ '-I', 'any',
+ '-w', testout_file,
+ capture_file('dhcp.pcap'), capture_file('dhcp.pcap'),
+ ), capture_output=True, encoding='utf-8', env=test_env)
+ check_mergecap(mergecap_proc, 'pcapng', 'Ethernet', 8, 1, 8, cmd_capinfos, testout_file, test_env)
+
+ def test_mergecap_basic_1_pcapng_pcapng(self, cmd_mergecap, capture_file, result_file, cmd_capinfos, test_env):
+ '''Merge a single pcapng file to pcapng'''
+ # $MERGECAP -v -w testout.pcap "${CAPTURE_DIR}dhcp.pcapng" > testout.txt 2>&1
+ testout_file = result_file(testout_pcapng)
+ mergecap_proc = subprocess.run((cmd_mergecap,
+ '-V',
+ '-w', testout_file,
+ capture_file('dhcp.pcapng'),
+ ), capture_output=True, encoding='utf-8', env=test_env)
+ check_mergecap(mergecap_proc, 'pcapng', 'Ethernet', 4, 1, 4, cmd_capinfos, testout_file, test_env)
+
+ def test_mergecap_1_pcapng_many_pcapng(self, cmd_mergecap, capture_file, result_file, cmd_capinfos, test_env):
+ '''Merge one pcapng file with many interfaces to pcapng'''
+ # $MERGECAP -v -w testout.pcap "${CAPTURE_DIR}many_interfaces.pcapng.1" > testout.txt 2>&1
+ testout_file = result_file(testout_pcapng)
+ mergecap_proc = subprocess.run((cmd_mergecap,
+ '-V',
+ '-w', testout_file,
+ capture_file('many_interfaces.pcapng.1'),
+ ), capture_output=True, encoding='utf-8', env=test_env)
+ check_mergecap(mergecap_proc, 'pcapng', 'Per packet', 64, 11, 62, cmd_capinfos, testout_file, test_env)
+
+ def test_mergecap_3_pcapng_pcapng(self, cmd_mergecap, capture_file, result_file, cmd_capinfos, test_env):
+ '''Merge multiple pcapng files with many interfaces to pcapng'''
+ # $MERGECAP -v -w testout.pcap "${CAPTURE_DIR}"many_interfaces.pcapng* > testout.txt 2>&1
+ testout_file = result_file(testout_pcapng)
+ mergecap_proc = subprocess.run((cmd_mergecap,
+ '-V',
+ '-w', testout_file,
+ capture_file('many_interfaces.pcapng.1'),
+ capture_file('many_interfaces.pcapng.2'),
+ capture_file('many_interfaces.pcapng.3'),
+ ), capture_output=True, encoding='utf-8', env=test_env)
+ check_mergecap( mergecap_proc, 'pcapng', 'Per packet', 88, 11, 86, cmd_capinfos, testout_file, test_env)
+
+ def test_mergecap_3_pcapng_none_pcapng(self, cmd_mergecap, capture_file, result_file, cmd_capinfos, test_env):
+ '''Merge multiple pcapng files with many interfaces to pcapng, "none" merge mode'''
+ # $MERGECAP -vI 'none' -w testout.pcap "${CAPTURE_DIR}"many_interfaces.pcapng* > testout.txt 2>&1
+ testout_file = result_file(testout_pcapng)
+ mergecap_proc = subprocess.run((cmd_mergecap,
+ '-V',
+ '-I', 'none',
+ '-w', testout_file,
+ capture_file('many_interfaces.pcapng.1'),
+ capture_file('many_interfaces.pcapng.2'),
+ capture_file('many_interfaces.pcapng.3'),
+ ), capture_output=True, encoding='utf-8', env=test_env)
+ check_mergecap(mergecap_proc, 'pcapng', 'Per packet', 88, 33, 62, cmd_capinfos, testout_file, test_env)
+
+ def test_mergecap_3_pcapng_all_pcapng(self, cmd_mergecap, capture_file, result_file, cmd_capinfos, test_env):
+ '''Merge multiple pcapng files to pcapng in "none" mode, then merge that to "all" mode.'''
+ # build a pcapng of all the interfaces repeated by using mode 'none'
+ # $MERGECAP -vI 'none' -w testin.pcap "${CAPTURE_DIR}"many_interfaces.pcapng* > testout.txt 2>&1
+ testin_file = result_file('testin.pcapng')
+ subprocess.check_call((cmd_mergecap,
+ '-V',
+ '-I', 'none',
+ '-w', testin_file,
+ capture_file('many_interfaces.pcapng.1'),
+ capture_file('many_interfaces.pcapng.2'),
+ capture_file('many_interfaces.pcapng.3'),
+ ))
+ # the above generated 33 IDBs, 88 total pkts, 62 in first IDB
+
+ # and use that generated pcap for our test
+ # $MERGECAP -vI 'all' -w testout.pcap ./testin.pcap ./testin.pcap ./testin.pcap > testout.txt 2>&1
+ testout_file = result_file(testout_pcapng)
+ mergecap_proc = subprocess.run((cmd_mergecap,
+ '-V',
+ '-I', 'all',
+ '-w', testout_file,
+ testin_file, testin_file, testin_file,
+ ), capture_output=True, encoding='utf-8', env=test_env)
+ # check for 33 IDBs, 88*3=264 total pkts, 62*3=186 in first IDB
+ check_mergecap(mergecap_proc, 'pcapng', 'Per packet', 264, 33, 186, cmd_capinfos, testout_file, test_env)
+
+ def test_mergecap_3_pcapng_any_pcapng(self, cmd_mergecap, capture_file, result_file, cmd_capinfos, test_env):
+ '''Merge multiple pcapng files to pcapng in "none" mode, then merge that to "all" mode.'''
+ # build a pcapng of all the interfaces repeated by using mode 'none'
+ # $MERGECAP -vI 'none' -w testin.pcap "${CAPTURE_DIR}"many_interfaces.pcapng* > testout.txt 2>&1
+ testin_file = result_file('testin.pcapng')
+ subprocess.check_call((cmd_mergecap,
+ '-V',
+ '-I', 'none',
+ '-w', testin_file,
+ capture_file('many_interfaces.pcapng.1'),
+ capture_file('many_interfaces.pcapng.2'),
+ capture_file('many_interfaces.pcapng.3'),
+ ), env=test_env)
+ # the above generated 33 IDBs, 88 total pkts, 62 in first IDB
+
+ # and use that generated pcap for our test
+ # $MERGECAP -vI 'any' -w testout.pcap ./testin.pcap ./testin.pcap ./testin.pcap > testout.txt 2>&1
+ testout_file = result_file(testout_pcapng)
+ mergecap_proc = subprocess.run((cmd_mergecap,
+ '-V',
+ '-I', 'any',
+ '-w', testout_file,
+ testin_file, testin_file, testin_file,
+ ), capture_output=True, encoding='utf-8', env=test_env)
+ # check for 11 IDBs, 88*3=264 total pkts, 86*3=258 in first IDB
+ check_mergecap(mergecap_proc, 'pcapng', 'Per packet', 264, 11, 258, cmd_capinfos, testout_file, test_env)
diff --git a/test/suite_nameres.py b/test/suite_nameres.py
new file mode 100644
index 0000000..9e80bda
--- /dev/null
+++ b/test/suite_nameres.py
@@ -0,0 +1,106 @@
+#
+# Wireshark tests
+# By Gerald Combs <gerald@wireshark.org>
+#
+# Ported from a set of Bash scripts which were copyright 2005 Ulf Lamping
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+'''Name resolution tests'''
+
+import os.path
+import shutil
+import subprocess
+from subprocesstest import grep_output
+import pytest
+
+tf_str = { True: 'TRUE', False: 'FALSE' }
+
+custom_profile_name = 'Custom Profile'
+
+@pytest.fixture
+def nameres_setup(program_path, conf_path):
+ bundle_path = os.path.join(program_path, 'Wireshark.app', 'Contents', 'MacOS')
+ if os.path.isdir(bundle_path):
+ # Don't modify our application bundle.
+ global_path = None
+ else:
+ global_path = program_path
+ custom_profile_path = os.path.join(conf_path, 'profiles', custom_profile_name)
+ os.makedirs(custom_profile_path)
+ this_dir = os.path.dirname(__file__)
+ hosts_path_pfx = os.path.join(this_dir, 'hosts.')
+
+ if global_path is not None:
+ shutil.copyfile(hosts_path_pfx + 'global', os.path.join(global_path, 'hosts'))
+ shutil.copyfile(hosts_path_pfx + 'personal', os.path.join(conf_path, 'hosts'))
+ shutil.copyfile(hosts_path_pfx + 'custom', os.path.join(custom_profile_path, 'hosts'))
+ return global_path is not None
+
+
+@pytest.fixture
+def check_name_resolution(cmd_tshark, capture_file, nameres_setup, test_env):
+ def check_name_resolution_real(o_net_name, o_external_name_res, custom_profile, grep_str, fail_on_match=False):
+ if grep_str.startswith('global') and not nameres_setup:
+ pytest.skip('Global name resolution tests would require modifying the application bundle')
+ tshark_cmd = (cmd_tshark,
+ '-r', capture_file('dns+icmp.pcapng.gz'),
+ '-o', 'nameres.network_name: ' + tf_str[o_net_name],
+ '-o', 'nameres.use_external_name_resolver: ' + tf_str[o_external_name_res],
+ )
+ if custom_profile:
+ tshark_cmd += ('-C', custom_profile_name)
+ proc = subprocess.run(tshark_cmd, check=True, capture_output=True, encoding='utf-8', env=test_env)
+ if fail_on_match:
+ assert not grep_output(proc.stdout, grep_str)
+ else:
+ assert grep_output(proc.stdout, grep_str)
+ return check_name_resolution_real
+
+
+class TestNameResolution:
+
+ def test_name_resolution_net_t_ext_f_hosts_f_global(self, check_name_resolution):
+ '''Name resolution, no external, global profile.'''
+ # nameres.network_name: True
+ # nameres.use_external_name_resolver: False
+ # Profile: Default
+ check_name_resolution(True, False, False, 'global-8-8-8-8')
+
+ def test_name_resolution_net_t_ext_f_hosts_f_personal(self, check_name_resolution):
+ '''Name resolution, no external, personal profile.'''
+ # nameres.network_name: True
+ # nameres.use_external_name_resolver: False
+ # Profile: Default
+ check_name_resolution(True, False, False, 'personal-8-8-4-4')
+
+ def test_name_resolution_net_t_ext_f_hosts_f_custom(self, check_name_resolution):
+ '''Name resolution, no external, no profile hosts, custom profile.'''
+ # nameres.network_name: True
+ # nameres_use_external_name_resolver: False
+ # Profile: Custom
+ check_name_resolution(True, False, True, 'custom-4-2-2-2')
+
+ def test_hosts_any(self, cmd_tshark, capture_file):
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('dns+icmp.pcapng.gz'),
+ '-qz', 'hosts',
+ ), encoding='utf-8')
+ assert '174.137.42.65\twww.wireshark.org' in stdout
+ assert 'fe80::6233:4bff:fe13:c558\tCrunch.local' in stdout
+
+ def test_hosts_ipv4(self, cmd_tshark, capture_file):
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('dns+icmp.pcapng.gz'),
+ '-qz', 'hosts,ipv4',
+ ), encoding='utf-8')
+ assert '174.137.42.65\twww.wireshark.org' in stdout
+ assert 'fe80::6233:4bff:fe13:c558\tCrunch.local' not in stdout
+
+ def test_hosts_ipv6(self, cmd_tshark, capture_file):
+ stdout = subprocess.check_output((cmd_tshark,
+ '-r', capture_file('dns+icmp.pcapng.gz'),
+ '-qz', 'hosts,ipv6',
+ ), encoding='utf-8')
+ assert '174.137.42.65\twww.wireshark.org' not in stdout
+ assert 'fe80::6233:4bff:fe13:c558\tCrunch.local' in stdout
diff --git a/test/suite_outputformats.py b/test/suite_outputformats.py
new file mode 100644
index 0000000..0e5e492
--- /dev/null
+++ b/test/suite_outputformats.py
@@ -0,0 +1,92 @@
+#
+# Wireshark tests
+# By Gerald Combs <gerald@wireshark.org>
+#
+# Copyright (c) 2018 Dario Lombardo <lomato@gmail.com>
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+'''outputformats tests'''
+
+import json
+import os.path
+import subprocess
+from matchers import *
+import pytest
+
+@pytest.fixture
+def check_outputformat(cmd_tshark, request, dirs, capture_file):
+ def check_outputformat_real(format_option, pcap_file='dhcp.pcap',
+ extra_args=[], expected=None, multiline=False, env=None):
+ ''' Check a capture file against a sample, in json format. '''
+ tshark_proc = subprocess.run([cmd_tshark, '-r', capture_file(pcap_file),
+ '-T', format_option] + extra_args,
+ check=True, capture_output=True, encoding='utf-8', env=env)
+
+ # If a filename is given, load the expected values from those.
+ if isinstance(expected, str):
+ testdata = open(os.path.join(dirs.baseline_dir, expected)).read()
+ if multiline:
+ expected = [json.loads(line) for line in testdata.splitlines()]
+ else:
+ expected = json.loads(testdata)
+ actual = tshark_proc.stdout
+ if multiline:
+ actual = actual.splitlines()
+ assert len(expected) == len(actual)
+ for expectedObj, actualStr in zip(expected, actual):
+ assert expectedObj == json.loads(actualStr)
+ else:
+ actual = json.loads(actual)
+ assert expected == actual
+
+ return check_outputformat_real
+
+
+class TestOutputFormats:
+ maxDiff = 1000000
+
+ def test_outputformat_json(self, check_outputformat, base_env):
+ '''Decode some captures into json'''
+ check_outputformat("json", expected="dhcp.json", env=base_env)
+
+ def test_outputformat_jsonraw(self, check_outputformat, base_env):
+ '''Decode some captures into jsonraw'''
+ check_outputformat("jsonraw", expected="dhcp.jsonraw", env=base_env)
+
+ def test_outputformat_ek(self, check_outputformat, base_env):
+ '''Decode some captures into ek'''
+ check_outputformat("ek", expected="dhcp.ek", multiline=True, env=base_env)
+
+ def test_outputformat_ek_raw(self, check_outputformat, base_env):
+ '''Decode some captures into ek, with raw data'''
+ check_outputformat("ek", expected="dhcp-raw.ek", multiline=True, extra_args=['-x'], env=base_env)
+
+ def test_outputformat_json_select_field(self, check_outputformat, base_env):
+ '''Checks that the -e option works with -Tjson.'''
+ check_outputformat("json", extra_args=['-eframe.number', '-c1'], expected=[
+ {
+ "_index": "packets-2004-12-05",
+ "_type": "doc",
+ "_score": None,
+ "_source": {
+ "layers": {
+ "frame.number": [
+ "1"
+ ]
+ }
+ }
+ }
+ ], env=base_env)
+
+ def test_outputformat_ek_select_field(self, check_outputformat, base_env):
+ '''Checks that the -e option works with -Tek.'''
+ check_outputformat("ek", extra_args=['-eframe.number', '-c1'], expected=[
+ {"index": {"_index": "packets-2004-12-05", "_type": "doc"}},
+ {"timestamp": "1102274184317", "layers": {"frame_number": ["1"]}}
+ ], multiline=True, env=base_env)
+
+ def test_outputformat_ek_filter_field(self, check_outputformat, base_env):
+ ''' Check that the option -j works with -Tek.'''
+ check_outputformat("ek", extra_args=['-j', 'dhcp'], expected="dhcp-filter.ek",
+ multiline=True, env=base_env)
diff --git a/test/suite_release.py b/test/suite_release.py
new file mode 100644
index 0000000..3c6756f
--- /dev/null
+++ b/test/suite_release.py
@@ -0,0 +1,47 @@
+#
+# Wireshark tests
+#
+# Copyright (c) 2019 Gerald Combs <gerald@wireshark.org>
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+'''Release tests'''
+
+import re
+import subprocess
+import types
+import pytest
+
+@pytest.fixture
+def wireshark_features(request, cmd_wireshark, make_env):
+ '''
+ Returns an object describing available features in Wireshark. Tests
+ will be skipped unless --enable-release is passed on the command line.
+ '''
+ enabled = request.config.getoption('--enable-release', default=False)
+ if not enabled:
+ pytest.skip('Release tests are not enabled via --enable-release')
+ disabled = request.config.getoption('--disable-gui', default=False)
+ if disabled:
+ pytest.skip('GUI tests are disabled via --disable-gui')
+
+ try:
+ wireshark_v = subprocess.check_output(
+ (cmd_wireshark, '--version'),
+ stderr=subprocess.PIPE,
+ universal_newlines=True,
+ env=make_env()
+ )
+ wireshark_v = re.sub(r'\s+', ' ', wireshark_v)
+ except subprocess.CalledProcessError as ex:
+ print('Failed to detect Wireshark features: %s' % (ex,))
+ wireshark_v = ''
+ return types.SimpleNamespace(
+ have_automatic_updates='with automatic updates' in wireshark_v,
+ )
+
+class TestReleaseAutomaticUpdates:
+ def test_automatic_updates_present(self, wireshark_features):
+ '''Checks whether Wireshark was built with automatic updates.'''
+
+ assert wireshark_features.have_automatic_updates
diff --git a/test/suite_sharkd.py b/test/suite_sharkd.py
new file mode 100644
index 0000000..db736dc
--- /dev/null
+++ b/test/suite_sharkd.py
@@ -0,0 +1,1492 @@
+#
+# 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 pytest
+from matchers import *
+
+
+@pytest.fixture(scope='session')
+def cmd_sharkd(program):
+ return program('sharkd')
+
+
+@pytest.fixture
+def run_sharkd_session(cmd_sharkd, base_env):
+ def run_sharkd_session_real(sharkd_commands):
+ sharkd_proc = subprocess.Popen(
+ (cmd_sharkd, '-'), stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, encoding='utf-8', env=base_env)
+ sharkd_proc.stdin.write('\n'.join(sharkd_commands))
+ stdout, stderr = sharkd_proc.communicate()
+
+ assert 'Hello in child.' in stderr
+
+ outputs = []
+ for line in stdout.splitlines():
+ line = line.strip()
+ if not line:
+ continue
+ try:
+ jdata = json.loads(line)
+ except json.JSONDecodeError:
+ pytest.fail('Invalid JSON: %r' % line)
+ outputs.append(jdata)
+ return tuple(outputs)
+ return run_sharkd_session_real
+
+
+@pytest.fixture
+def check_sharkd_session(run_sharkd_session):
+ 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)
+ assert expected_outputs == actual_outputs
+ return check_sharkd_session_real
+
+
+class TestSharkd:
+ def test_sharkd_req_load_bad_pcap(self, check_sharkd_session, capture_file):
+ check_sharkd_session((
+ {"jsonrpc":"2.0", "id":1, "method":"load",
+ "params":{"file": capture_file('non-existant.pcap')}
+ },
+ ), (
+ {"jsonrpc":"2.0","id":1,"error":{"code":-2001,"message":"Unable to open the file"}},
+ ))
+
+ def test_sharkd_req_load_truncated_pcap(self, check_sharkd_session, capture_file):
+ check_sharkd_session((
+ {"jsonrpc":"2.0", "id":1, "method":"load",
+ "params":{"file": capture_file('trunc.pcap')}
+ },
+ ), (
+ {"jsonrpc":"2.0","id":1,"result":{"status":"Less data was read than was expected","err":-12}},
+ ))
+
+ def test_sharkd_req_status_no_pcap(self, check_sharkd_session):
+ check_sharkd_session((
+ {"jsonrpc":"2.0", "id":1, "method":"status"},
+ ), (
+ {"jsonrpc":"2.0","id":1,"result":{"frames":0,"duration":0.000000000,"columns":["No.","Time","Source","Destination","Protocol","Length","Info"]}},
+ ))
+
+ def test_sharkd_req_status(self, check_sharkd_session, capture_file):
+ check_sharkd_session((
+ {"jsonrpc":"2.0", "id":1, "method":"load",
+ "params":{"file": capture_file('dhcp.pcap')}
+ },
+ {"jsonrpc":"2.0", "id":2, "method":"status"},
+ ), (
+ {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}},
+ {"jsonrpc":"2.0","id":2,"result":{"frames": 4, "duration": 0.070345000,
+ "filename": "dhcp.pcap", "filesize": 1400,
+ "columns":["No.","Time","Source","Destination","Protocol","Length","Info"]}},
+ ))
+
+ def test_sharkd_req_analyse(self, check_sharkd_session, capture_file):
+ check_sharkd_session((
+ {"jsonrpc":"2.0", "id":1, "method":"load",
+ "params":{"file": capture_file('dhcp.pcap')}
+ },
+ {"jsonrpc":"2.0", "id":2, "method":"analyse"},
+ ), (
+ {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}},
+ {"jsonrpc":"2.0","id":2,"result":{"frames": 4, "protocols": ["frame", "eth", "ethertype", "ip", "udp",
+ "dhcp"], "first": 1102274184.317452908, "last": 1102274184.387798071}},
+ ))
+
+ def test_sharkd_req_info(self, check_sharkd_session):
+ matchTapNameList = MatchList(
+ {"tap": MatchAny(str), "name": MatchAny(str)})
+ matchNameDescriptionList = MatchList(
+ {"name": MatchAny(str), "description": MatchAny(str)})
+ check_sharkd_session((
+ {"jsonrpc":"2.0", "id":1, "method":"info"},
+ ), (
+ {"jsonrpc":"2.0","id":1,"result":{
+ "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)),
+ "capture_types": matchNameDescriptionList,
+ "encap_types": matchNameDescriptionList,
+ "nstat": matchTapNameList,
+ }},
+ ))
+
+ def test_sharkd_req_check(self, check_sharkd_session, capture_file):
+ check_sharkd_session((
+ {"jsonrpc":"2.0", "id":1, "method":"load",
+ "params":{"file": capture_file('dhcp.pcap')}
+ },
+ {"jsonrpc":"2.0", "id":2, "method":"check"},
+ {"jsonrpc":"2.0", "id":3, "method":"check", "params":{"filter": "garbage filter"}},
+ {"jsonrpc":"2.0", "id":4, "method":"check", "params":{"field": "garbage field"}},
+ {"jsonrpc":"2.0", "id":5, "method":"check", "params":{"filter": "ip", "field": "ip"}},
+ ), (
+ {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}},
+ {"jsonrpc":"2.0","id":2,"result":{"status":"OK"}},
+ {"jsonrpc":"2.0","id":3,"error":{"code":-5001,"message":"Filter invalid - \"filter\" was unexpected in this context."}},
+ {"jsonrpc":"2.0","id":4,"error":{"code":-5002,"message":"Field garbage field not found"}},
+ {"jsonrpc":"2.0","id":5,"result":{"status":"OK"}},
+ ))
+
+ def test_sharkd_req_complete_field(self, check_sharkd_session):
+ check_sharkd_session((
+ {"jsonrpc":"2.0", "id":1, "method":"complete"},
+ {"jsonrpc":"2.0", "id":2, "method":"complete", "params":{"field": "frame.le"}},
+ {"jsonrpc":"2.0", "id":3, "method":"complete", "params":{"field": "garbage.nothing.matches"}},
+ ), (
+ {"jsonrpc":"2.0","id":1,"result":{}},
+ {"jsonrpc":"2.0","id":2,"result":{"field": MatchList(
+ {"f": "frame.len", "t": 7, "n": "Frame length on the wire"}, match_element=any)}
+ },
+ {"jsonrpc":"2.0","id":3,"result":{"field": []}},
+ ))
+
+ def test_sharkd_req_complete_pref(self, check_sharkd_session):
+ check_sharkd_session((
+ {"jsonrpc":"2.0", "id":1, "method":"complete", "params":{"pref": "tcp."}},
+ {"jsonrpc":"2.0", "id":2, "method":"complete", "params":{"pref": "garbage.nothing.matches"}},
+ ), (
+ {"jsonrpc":"2.0","id":1,"result":{"pref": MatchList(
+ {"f": "tcp.check_checksum", "d": "Validate the TCP checksum if possible"}, match_element=any)}
+ },
+ {"jsonrpc":"2.0","id":2,"result":{"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((
+ {"jsonrpc":"2.0", "id":1, "method":"load",
+ "params":{"file": capture_file('dhcp.pcap')}
+ },
+ {"jsonrpc":"2.0", "id":2, "method":"frames"},
+ ), (
+ {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}},
+ {"jsonrpc":"2.0","id":2,"result":
+ MatchList({
+ "c": MatchList(MatchAny(str)),
+ "num": MatchAny(int),
+ "bg": MatchAny(str),
+ "fg": MatchAny(str),
+ })
+ },
+ ))
+
+ def test_sharkd_req_frames_delta_times(self, check_sharkd_session, capture_file):
+ check_sharkd_session((
+ {"jsonrpc":"2.0", "id":1, "method":"load",
+ "params":{"file": capture_file('logistics_multicast.pcapng')}
+ },
+ {"jsonrpc":"2.0", "id":2, "method":"frames","params":{"filter":"frame.number==1||frame.number==800","column0":"frame.time_relative:1","column1":"frame.time_delta:1","column2":"frame.time_delta_displayed:1"}},
+ ), (
+ {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}},
+ {"jsonrpc":"2.0","id":2,"result":
+ [
+ {"c":["0.000000000","0.000000000","0.000000000"],"num":1,"bg":"feffd0","fg":"12272e"},
+ {"c":["191.872111000","0.193716000","191.872111000"],"num":800,"bg":"feffd0","fg":"12272e"},
+ ],
+ },
+ ))
+
+ 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((
+ {"jsonrpc":"2.0", "id":1, "method":"load",
+ "params":{"file": capture_file('dhcp.pcap')}
+ },
+ {"jsonrpc":"2.0", "id":2, "method":"tap"},
+ {"jsonrpc":"2.0", "id":3, "method":"tap", "params":{"tap0": "garbage tap"}},
+ {"jsonrpc":"2.0", "id":4, "method":"tap", "params":{"tap0": "conv:Ethernet", "filter": "garbage filter"}},
+ ), (
+ {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}},
+ {"jsonrpc":"2.0","id":2,"error":{"code":-32600,"message":"Mandatory parameter tap0 is missing"}},
+ {"jsonrpc":"2.0","id":3,"error":{"code":-11012,"message":"sharkd_session_process_tap() garbage tap not recognized"}},
+ {"jsonrpc":"2.0","id":4,"error":{"code":-11013,"message":"sharkd_session_process_tap() name=conv:Ethernet error=Filter \"garbage filter\" is invalid - \"filter\" was unexpected in this context."}},
+ ))
+
+ def test_sharkd_req_tap(self, check_sharkd_session, capture_file):
+ check_sharkd_session((
+ {"jsonrpc":"2.0", "id":1, "method":"load",
+ "params":{"file": capture_file('dhcp.pcap')}
+ },
+ {"jsonrpc":"2.0", "id":2, "method":"tap"},
+ {"jsonrpc":"2.0", "id":3, "method":"tap", "params":{"tap0": "conv:Ethernet", "tap1": "endpt:TCP"}},
+ ), (
+ {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}},
+ {"jsonrpc":"2.0","id":2,"error":{"code":-32600,"message":"Mandatory parameter tap0 is missing"}},
+ {"jsonrpc":"2.0","id":3,"result":{
+ "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",
+ }
+ ],
+ },
+ ]
+ }},
+ ))
+
+ def test_sharkd_req_tap_rtp_streams(self, check_sharkd_session, capture_file):
+ check_sharkd_session((
+ {"jsonrpc":"2.0", "id":1, "method":"load",
+ "params":{"file": capture_file('sip-rtp.pcapng')}
+ },
+ {"jsonrpc":"2.0", "id":2, "method":"tap", "params":{"tap0": "rtp-streams"}},
+ {"jsonrpc":"2.0", "id":2, "method":"tap", "params":{"tap0": "rtp-analyse:200.57.7.204_8000_200.57.7.196_40376_0xd2bd4e3e"}},
+ ), (
+ {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}},
+ {"jsonrpc":"2.0","id":2,"result":{
+ "taps":[{
+ "tap":"rtp-streams",
+ "type":"rtp-streams",
+ "streams":[{
+ "ssrc":"0xd2bd4e3e",
+ "payload":"g711A",
+ "saddr":"200.57.7.204",
+ "sport":8000,
+ "daddr":"200.57.7.196",
+ "dport":40376,
+ "start_time":8.479371,
+ "duration": 24.124055,
+ "pkts":548,
+ "lost":0,
+ "lost_percent":0.0,
+ "max_delta":5843.742000,
+ "min_delta":0.159,
+ "mean_delta":44.102477,
+ "min_jitter":0.388213,
+ "max_jitter":7.406751,
+ "mean_jitter":2.517173,
+ "expectednr":548,
+ "totalnr":548,
+ "problem":False,
+ "ipver":4
+ }]
+ }]
+ }},
+ {"jsonrpc":"2.0","id":2,"result":
+ {"taps":[{
+ "tap":"rtp-analyse:200.57.7.204_8000_200.57.7.196_40376_0xd2bd4e3e",
+ "type":"rtp-analyse",
+ "ssrc":"0xd2bd4e3e",
+ "max_delta":5843.742000,
+ "max_delta_nr":168,
+ "max_jitter":7.406751,
+ "mean_jitter":2.517173,
+ "max_skew":319.289000,
+ "total_nr":548,
+ "seq_err":0,
+ "duration":24124.055000,
+ "items": MatchAny()
+ }]
+ }},
+ ))
+
+ def test_sharkd_req_tap_phs(self, check_sharkd_session, capture_file):
+ check_sharkd_session((
+ {"jsonrpc":"2.0", "id":1, "method":"load",
+ "params":{"file": capture_file('protohier-with-comments.pcapng')}
+ },
+ {"jsonrpc":"2.0", "id":2, "method":"tap", "params":{"tap0": "phs"}},
+ {"jsonrpc":"2.0", "id":3, "method":"load",
+ "params":{"file": capture_file('protohier-without-comments.pcapng')}
+ },
+ {"jsonrpc":"2.0", "id":4, "method":"tap", "params":{"tap0": "phs"}},
+ {"jsonrpc":"2.0", "id":5, "method":"tap", "params":{"tap0": "phs", "filter": "ipv6"}},
+ ), (
+ {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}},
+ {"jsonrpc":"2.0","id":2,"result":{
+ "taps":[{
+ "tap":"phs",
+ "type":"phs",
+ "filter":"",
+ "protos":[{
+ "proto":"eth",
+ "frames":115,
+ "bytes":22186,
+ "protos":[{
+ "proto":"ipv6",
+ "frames":39,
+ "bytes":7566,
+ "protos":[{
+ "proto":"icmpv6",
+ "frames":36,
+ "bytes":3684
+ },{
+ "proto":"udp",
+ "frames":3,
+ "bytes":3882,
+ "protos":[{
+ "proto":"data",
+ "frames":3,
+ "bytes":3882
+ }]
+ }]
+ },{
+ "proto":"ip",
+ "frames":70,
+ "bytes":14260,
+ "protos":[{
+ "proto":"udp",
+ "frames":60,
+ "bytes":13658,
+ "protos":[{
+ "proto":"mdns",
+ "frames":1,
+ "bytes":138
+ },{
+ "proto":"ssdp",
+ "frames":30,
+ "bytes":8828
+ },{
+ "proto":"nbns",
+ "frames":20,
+ "bytes":2200
+ },{
+ "proto":"nbdgm",
+ "frames":1,
+ "bytes":248,
+ "protos":[{
+ "proto":"smb",
+ "frames":1,
+ "bytes":248,
+ "protos":[{
+ "proto":"mailslot",
+ "frames":1,
+ "bytes":248,
+ "protos":[{
+ "proto":"browser",
+ "frames":1,
+ "bytes":248
+ }]
+ }]
+ }]
+ },{"proto":"dhcp",
+ "frames":4,
+ "bytes":1864
+ },{
+ "proto":"dns",
+ "frames":4,
+ "bytes":380
+ }]
+ },{
+ "proto":"igmp",
+ "frames":10,
+ "bytes":602
+ }]
+ },{
+ "proto":"arp",
+ "frames":6,
+ "bytes":360
+ }]
+ }]
+ }]
+ }},
+ {"jsonrpc":"2.0","id":3,"result":{"status":"OK"}},
+ {"jsonrpc":"2.0","id":4,"result":{
+ "taps":[{
+ "tap":"phs",
+ "type":"phs",
+ "filter":"",
+ "protos":[{
+ "proto":"eth",
+ "frames":115,
+ "bytes":22186,
+ "protos":[{
+ "proto":"ipv6",
+ "frames":39,
+ "bytes":7566,
+ "protos":[{
+ "proto":"icmpv6",
+ "frames":36,
+ "bytes":3684
+ },{
+ "proto":"udp",
+ "frames":3,
+ "bytes":3882,
+ "protos":[{
+ "proto":"data",
+ "frames":3,
+ "bytes":3882
+ }]
+ }]
+ },{
+ "proto":"ip",
+ "frames":70,
+ "bytes":14260,
+ "protos":[{
+ "proto":"udp",
+ "frames":60,
+ "bytes":13658,
+ "protos":[{
+ "proto":"mdns",
+ "frames":1,
+ "bytes":138
+ },{
+ "proto":"ssdp",
+ "frames":30,
+ "bytes":8828
+ },{
+ "proto":"nbns",
+ "frames":20,
+ "bytes":2200
+ },{
+ "proto":"nbdgm",
+ "frames":1,
+ "bytes":248,
+ "protos":[{
+ "proto":"smb",
+ "frames":1,
+ "bytes":248,
+ "protos":[{
+ "proto":"mailslot",
+ "frames":1,
+ "bytes":248,
+ "protos":[{
+ "proto":"browser",
+ "frames":1,
+ "bytes":248
+ }]
+ }]
+ }]
+ },{"proto":"dhcp",
+ "frames":4,
+ "bytes":1864
+ },{
+ "proto":"dns",
+ "frames":4,
+ "bytes":380
+ }]
+ },{
+ "proto":"igmp",
+ "frames":10,
+ "bytes":602
+ }]
+ },{
+ "proto":"arp",
+ "frames":6,
+ "bytes":360
+ }]
+ }]
+ }]
+ }},
+ {"jsonrpc": "2.0", "id": 5, "result": {
+ "taps": [{
+ "tap": "phs",
+ "type": "phs",
+ "filter": "ipv6",
+ "protos": [{
+ "bytes": 7566,
+ "frames": 39,
+ "proto": "eth",
+ "protos": [{
+ "bytes": 7566,
+ "frames": 39,
+ "proto": "ipv6",
+ "protos": [{
+ "bytes": 3684,
+ "frames": 36,
+ "proto": "icmpv6"
+ },{
+ "bytes": 3882,
+ "frames": 3,
+ "proto": "udp",
+ "protos": [{
+ "bytes": 3882,
+ "frames": 3,
+ "proto": "data"
+ }]
+ }]
+ }]
+ }]
+ }]
+ }},
+ ))
+
+ def test_sharkd_req_tap_voip_calls(self, check_sharkd_session, capture_file):
+ check_sharkd_session((
+ {"jsonrpc":"2.0", "id":1, "method":"load",
+ "params":{"file": capture_file('sip-rtp.pcapng')}
+ },
+ {"jsonrpc":"2.0", "id":2, "method":"tap", "params":{"tap0": "voip-calls"}},
+ ), (
+ {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}},
+ {"jsonrpc":"2.0","id":2,"result":{
+ "taps":[{
+ "tap":"voip-calls",
+ "type":"voip-calls",
+ "calls":[{
+ "call":0,
+ "start_time":0.000000,
+ "stop_time":8.524137,
+ "initial_speaker":"200.57.7.195",
+ "from":"<sip:200.57.7.195:55061;user=phone>",
+ "to":"\"francisco@bestel.com\" <sip:francisco@bestel.com:55060>",
+ "protocol":"SIP",
+ "packets":5,
+ "state":"IN CALL",
+ "comment":"INVITE 200"
+ },{
+ "call":1,
+ "start_time":24.665953,
+ "stop_time":24.692752,
+ "initial_speaker":"200.57.7.195",
+ "from":"\"Ivan Alizade\" <sip:5514540002@200.57.7.195:55061;user=phone>",
+ "to":"\"francisco@bestel.com\" <sip:francisco@bestel.com:55060>",
+ "protocol":"SIP",
+ "packets":3,
+ "state":"CALL SETUP",
+ "comment":"INVITE"
+ }]
+ }]
+ }},
+ ))
+
+ def test_sharkd_req_tap_voip_convs(self, check_sharkd_session, capture_file):
+ check_sharkd_session((
+ {"jsonrpc":"2.0", "id":1, "method":"load",
+ "params":{"file": capture_file('sip-rtp.pcapng')}
+ },
+ {"jsonrpc":"2.0", "id":2, "method":"tap", "params":{"tap0": "voip-convs:"}},
+ {"jsonrpc":"2.0", "id":3, "method":"tap", "params":{"tap0": "voip-convs:0"}},
+ {"jsonrpc":"2.0", "id":4, "method":"tap", "params":{"tap0": "voip-convs:0-1"}},
+ {"jsonrpc":"2.0", "id":5, "method":"tap", "params":{"tap0": "voip-convs:garbage"}},
+ {"jsonrpc":"2.0", "id":6, "method":"tap", "params":{"tap0": "voip-convs:999"}},
+ {"jsonrpc":"2.0", "id":7, "method":"tap", "params":{"tap0": "voip-convs:0,999,0-1,999-999,1,1"}},
+ ), (
+ {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}},
+ {"jsonrpc":"2.0","id":2,"result":{
+ "taps":[{
+ "tap":"voip-convs:",
+ "type":"voip-convs",
+ "convs":[{
+ "frame":1,
+ "call":0,
+ "time":"0.000000",
+ "dst_addr":"200.57.7.204",
+ "dst_port":5061,
+ "src_addr":"200.57.7.195",
+ "src_port":5060,
+ "label":"INVITE SDP (g711A g729 g723 g711U)",
+ "comment":"SIP INVITE From: <sip:200.57.7.195:55061;user=phone> To:\"francisco@bestel.com\" <sip:francisco@bestel.com:55060> Call-ID:12013223@200.57.7.195 CSeq:1"
+ },{
+ "frame":2,
+ "call":0,
+ "time":"0.007889",
+ "dst_addr":"200.57.7.195",
+ "dst_port":5060,
+ "src_addr":"200.57.7.204",
+ "src_port":5061,
+ "label":"100 Trying",
+ "comment":"SIP Status 100 Trying"
+ },{
+ "frame":3,
+ "call":0,
+ "time":"0.047524",
+ "dst_addr":"200.57.7.195",
+ "dst_port":5060,
+ "src_addr":"200.57.7.204",
+ "src_port":5061,
+ "label":"180 Ringing",
+ "comment":"SIP Status 180 Ringing"
+ },{
+ "frame":6,
+ "call":0,
+ "time":"8.477925",
+ "dst_addr":"200.57.7.195",
+ "dst_port":5060,
+ "src_addr":"200.57.7.204",
+ "src_port":5061,
+ "label":"200 Ok SDP (g711A g711U GSM iLBC speex telephone-event)",
+ "comment":"SIP Status 200 Ok"
+ },{
+ "frame":7,
+ "call":0,
+ "time":"8.479371",
+ "dst_addr":"200.57.7.196",
+ "dst_port":40376,
+ "src_addr":"200.57.7.204",
+ "src_port":8000,
+ "label":"RTP (g711A) ",
+ "comment":"RTP, 548 packets. Duration: 24.12s SSRC: 0xD2BD4E3E"
+ },{
+ "frame":10,
+ "call":0,
+ "time":"8.524137",
+ "dst_addr":"200.57.7.204",
+ "dst_port":5061,
+ "src_addr":"200.57.7.195",
+ "src_port":5060,
+ "label":"ACK",
+ "comment":"SIP Request INVITE ACK 200 CSeq:1"
+ },{
+ "frame":352,
+ "call":1,
+ "time":"24.665953",
+ "dst_addr":"200.57.7.204",
+ "dst_port":5061,
+ "src_addr":"200.57.7.195",
+ "src_port":5060,
+ "label":"INVITE SDP (g711A g729 g723)",
+ "comment":"SIP INVITE From: \"Ivan Alizade\" <sip:5514540002@200.57.7.195:55061;user=phone> To:\"francisco@bestel.com\" <sip:francisco@bestel.com:55060> Call-ID:12015624@200.57.7.195 CSeq:1"
+ },{
+ "frame":353,
+ "call":1,
+ "time":"24.674680",
+ "dst_addr":"200.57.7.195",
+ "dst_port":5060,
+ "src_addr":"200.57.7.204",
+ "src_port":5061,
+ "label":"100 Trying",
+ "comment":"SIP Status 100 Trying"
+ },{
+ "frame":354,
+ "call":1,
+ "time":"24.692752",
+ "dst_addr":"200.57.7.195",
+ "dst_port":5060,
+ "src_addr":"200.57.7.204",
+ "src_port":5061,
+ "label":"180 Ringing",
+ "comment":"SIP Status 180 Ringing"
+ }]
+ }]
+ }},
+ {"jsonrpc":"2.0","id":3,"result":{
+ "taps":[{
+ "tap":"voip-convs:0",
+ "type":"voip-convs",
+ "convs":[{
+ "frame":1,
+ "call":0,
+ "time":"0.000000",
+ "dst_addr":"200.57.7.204",
+ "dst_port":5061,
+ "src_addr":"200.57.7.195",
+ "src_port":5060,
+ "label":"INVITE SDP (g711A g729 g723 g711U)",
+ "comment":"SIP INVITE From: <sip:200.57.7.195:55061;user=phone> To:\"francisco@bestel.com\" <sip:francisco@bestel.com:55060> Call-ID:12013223@200.57.7.195 CSeq:1"
+ },{
+ "frame":2,
+ "call":0,
+ "time":"0.007889",
+ "dst_addr":"200.57.7.195",
+ "dst_port":5060,
+ "src_addr":"200.57.7.204",
+ "src_port":5061,
+ "label":"100 Trying",
+ "comment":"SIP Status 100 Trying"
+ },{
+ "frame":3,
+ "call":0,
+ "time":"0.047524",
+ "dst_addr":"200.57.7.195",
+ "dst_port":5060,
+ "src_addr":"200.57.7.204",
+ "src_port":5061,
+ "label":"180 Ringing",
+ "comment":"SIP Status 180 Ringing"
+ },{
+ "frame":6,
+ "call":0,
+ "time":"8.477925",
+ "dst_addr":"200.57.7.195",
+ "dst_port":5060,
+ "src_addr":"200.57.7.204",
+ "src_port":5061,
+ "label":"200 Ok SDP (g711A g711U GSM iLBC speex telephone-event)",
+ "comment":"SIP Status 200 Ok"
+ },{
+ "frame":7,
+ "call":0,
+ "time":"8.479371",
+ "dst_addr":"200.57.7.196",
+ "dst_port":40376,
+ "src_addr":"200.57.7.204",
+ "src_port":8000,
+ "label":"RTP (g711A) ",
+ "comment":"RTP, 548 packets. Duration: 24.12s SSRC: 0xD2BD4E3E"
+ },{
+ "frame":10,
+ "call":0,
+ "time":"8.524137",
+ "dst_addr":"200.57.7.204",
+ "dst_port":5061,
+ "src_addr":"200.57.7.195",
+ "src_port":5060,"label":"ACK","comment":"SIP Request INVITE ACK 200 CSeq:1"
+ }]
+ }]
+ }},
+ {"jsonrpc":"2.0","id":4,"result":{
+ "taps":[{
+ "tap":"voip-convs:0-1",
+ "type":"voip-convs",
+ "convs":[{
+ "frame":1,
+ "call":0,
+ "time":"0.000000",
+ "dst_addr":"200.57.7.204",
+ "dst_port":5061,
+ "src_addr":"200.57.7.195",
+ "src_port":5060,
+ "label":"INVITE SDP (g711A g729 g723 g711U)",
+ "comment":"SIP INVITE From: <sip:200.57.7.195:55061;user=phone> To:\"francisco@bestel.com\" <sip:francisco@bestel.com:55060> Call-ID:12013223@200.57.7.195 CSeq:1"
+ },{
+ "frame":2,
+ "call":0,
+ "time":"0.007889",
+ "dst_addr":"200.57.7.195",
+ "dst_port":5060,
+ "src_addr":"200.57.7.204",
+ "src_port":5061,
+ "label":"100 Trying",
+ "comment":"SIP Status 100 Trying"
+ },{
+ "frame":3,
+ "call":0,
+ "time":"0.047524",
+ "dst_addr":"200.57.7.195",
+ "dst_port":5060,
+ "src_addr":"200.57.7.204",
+ "src_port":5061,
+ "label":"180 Ringing",
+ "comment":"SIP Status 180 Ringing"
+ },{
+ "frame":6,
+ "call":0,
+ "time":"8.477925",
+ "dst_addr":"200.57.7.195",
+ "dst_port":5060,
+ "src_addr":"200.57.7.204",
+ "src_port":5061,
+ "label":"200 Ok SDP (g711A g711U GSM iLBC speex telephone-event)",
+ "comment":"SIP Status 200 Ok"
+ },{
+ "frame":7,
+ "call":0,
+ "time":"8.479371",
+ "dst_addr":"200.57.7.196",
+ "dst_port":40376,
+ "src_addr":"200.57.7.204",
+ "src_port":8000,
+ "label":"RTP (g711A) ",
+ "comment":"RTP, 548 packets. Duration: 24.12s SSRC: 0xD2BD4E3E"
+ },{
+ "frame":10,
+ "call":0,
+ "time":"8.524137",
+ "dst_addr":"200.57.7.204",
+ "dst_port":5061,
+ "src_addr":"200.57.7.195",
+ "src_port":5060,
+ "label":"ACK",
+ "comment":"SIP Request INVITE ACK 200 CSeq:1"
+ },{
+ "frame":352,
+ "call":1,
+ "time":"24.665953",
+ "dst_addr":"200.57.7.204",
+ "dst_port":5061,
+ "src_addr":"200.57.7.195",
+ "src_port":5060,
+ "label":"INVITE SDP (g711A g729 g723)",
+ "comment":"SIP INVITE From: \"Ivan Alizade\" <sip:5514540002@200.57.7.195:55061;user=phone> To:\"francisco@bestel.com\" <sip:francisco@bestel.com:55060> Call-ID:12015624@200.57.7.195 CSeq:1"
+ },{
+ "frame":353,
+ "call":1,
+ "time":"24.674680",
+ "dst_addr":"200.57.7.195",
+ "dst_port":5060,
+ "src_addr":"200.57.7.204",
+ "src_port":5061,
+ "label":"100 Trying",
+ "comment":"SIP Status 100 Trying"
+ },{
+ "frame":354,
+ "call":1,
+ "time":"24.692752",
+ "dst_addr":"200.57.7.195",
+ "dst_port":5060,
+ "src_addr":"200.57.7.204",
+ "src_port":5061,
+ "label":"180 Ringing",
+ "comment":"SIP Status 180 Ringing"
+ }]
+ }]
+ }},
+ {"jsonrpc":"2.0","id":5,"error":{
+ "code":-11014,"message":"sharkd_session_process_tap() voip-convs=voip-convs:garbage invalid 'convs' parameter"
+ }},
+ {"jsonrpc":"2.0","id":6,"result":{
+ "taps":[{
+ "tap":"voip-convs:999",
+ "type":"voip-convs",
+ "convs":[]
+ }]
+ }},
+ {"jsonrpc":"2.0","id":7,"result":{
+ "taps":[{
+ "tap":"voip-convs:0,999,0-1,999-999,1,1",
+ "type":"voip-convs",
+ "convs":[{
+ "frame":1,
+ "call":0,
+ "time":"0.000000",
+ "dst_addr":"200.57.7.204",
+ "dst_port":5061,
+ "src_addr":"200.57.7.195",
+ "src_port":5060,
+ "label":"INVITE SDP (g711A g729 g723 g711U)",
+ "comment":"SIP INVITE From: <sip:200.57.7.195:55061;user=phone> To:\"francisco@bestel.com\" <sip:francisco@bestel.com:55060> Call-ID:12013223@200.57.7.195 CSeq:1"
+ },{
+ "frame":2,
+ "call":0,
+ "time":"0.007889",
+ "dst_addr":"200.57.7.195",
+ "dst_port":5060,
+ "src_addr":"200.57.7.204",
+ "src_port":5061,
+ "label":"100 Trying",
+ "comment":"SIP Status 100 Trying"
+ },{
+ "frame":3,
+ "call":0,
+ "time":"0.047524",
+ "dst_addr":"200.57.7.195",
+ "dst_port":5060,
+ "src_addr":"200.57.7.204",
+ "src_port":5061,
+ "label":"180 Ringing",
+ "comment":"SIP Status 180 Ringing"
+ },{
+ "frame":6,
+ "call":0,
+ "time":"8.477925",
+ "dst_addr":"200.57.7.195",
+ "dst_port":5060,
+ "src_addr":"200.57.7.204",
+ "src_port":5061,
+ "label":"200 Ok SDP (g711A g711U GSM iLBC speex telephone-event)",
+ "comment":"SIP Status 200 Ok"
+ },{
+ "frame":7,
+ "call":0,
+ "time":"8.479371",
+ "dst_addr":"200.57.7.196",
+ "dst_port":40376,
+ "src_addr":"200.57.7.204",
+ "src_port":8000,
+ "label":"RTP (g711A) ",
+ "comment":"RTP, 548 packets. Duration: 24.12s SSRC: 0xD2BD4E3E"
+ },{
+ "frame":10,
+ "call":0,
+ "time":"8.524137",
+ "dst_addr":"200.57.7.204",
+ "dst_port":5061,
+ "src_addr":"200.57.7.195",
+ "src_port":5060,
+ "label":"ACK",
+ "comment":"SIP Request INVITE ACK 200 CSeq:1"
+ },{
+ "frame":352,
+ "call":1,
+ "time":"24.665953",
+ "dst_addr":"200.57.7.204",
+ "dst_port":5061,
+ "src_addr":"200.57.7.195",
+ "src_port":5060,
+ "label":"INVITE SDP (g711A g729 g723)",
+ "comment":"SIP INVITE From: \"Ivan Alizade\" <sip:5514540002@200.57.7.195:55061;user=phone> To:\"francisco@bestel.com\" <sip:francisco@bestel.com:55060> Call-ID:12015624@200.57.7.195 CSeq:1"
+ },{
+ "frame":353,
+ "call":1,
+ "time":"24.674680",
+ "dst_addr":"200.57.7.195",
+ "dst_port":5060,
+ "src_addr":"200.57.7.204",
+ "src_port":5061,
+ "label":"100 Trying",
+ "comment":"SIP Status 100 Trying"
+ },{
+ "frame":354,
+ "call":1,
+ "time":"24.692752",
+ "dst_addr":"200.57.7.195",
+ "dst_port":5060,
+ "src_addr":"200.57.7.204",
+ "src_port":5061,
+ "label":"180 Ringing",
+ "comment":"SIP Status 180 Ringing"
+ }]
+ }]
+ }},
+ ))
+
+ def test_sharkd_req_tap_hosts(self, check_sharkd_session, capture_file):
+ matchAddrNameList = MatchList(
+ {"name": MatchAny(str), "addr": MatchAny(str)})
+ check_sharkd_session((
+ {"jsonrpc":"2.0", "id":1, "method":"load",
+ "params":{"file": capture_file('dns-mdns.pcap')}
+ },
+ {"jsonrpc":"2.0", "id":2, "method":"tap", "params":{"tap0": "hosts:"}},
+ {"jsonrpc":"2.0", "id":3, "method":"tap", "params":{"tap0": "hosts:ip"}},
+ {"jsonrpc":"2.0", "id":4, "method":"tap", "params":{"tap0": "hosts:ipv4"}},
+ {"jsonrpc":"2.0", "id":5, "method":"tap", "params":{"tap0": "hosts:ipv6"}},
+ {"jsonrpc":"2.0", "id":6, "method":"tap", "params":{"tap0": "hosts:invalid"}},
+ {"jsonrpc":"2.0", "id":7, "method":"tap", "params":{"tap0": "hosts:ipv4,ipv6"}},
+ {"jsonrpc":"2.0", "id":8, "method":"tap", "params":{"tap0": "hosts:ipv4,ipv6,invalid"}},
+ ), (
+ {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}},
+ {"jsonrpc":"2.0","id":2,"result":{
+ "taps":[{
+ "tap":"hosts:",
+ "type":"hosts",
+ "ipv4_hosts":matchAddrNameList,
+ "ipv6_hosts":matchAddrNameList,
+ }]
+ }},
+ {"jsonrpc":"2.0","id":3,"result":{
+ "taps":[{
+ "tap":"hosts:ip",
+ "type":"hosts",
+ "ipv4_hosts":matchAddrNameList,
+ }]
+ }},
+ {"jsonrpc":"2.0","id":4,"result":{
+ "taps":[{
+ "tap":"hosts:ipv4",
+ "type":"hosts",
+ "ipv4_hosts":matchAddrNameList,
+ }]
+ }},
+ {"jsonrpc":"2.0","id":5,"result":{
+ "taps":[{
+ "tap":"hosts:ipv6",
+ "type":"hosts",
+ "ipv6_hosts":matchAddrNameList,
+ }]
+ }},
+ {"jsonrpc":"2.0","id":6,"error":{"code":-11015,"message":"sharkd_session_process_tap() hosts=hosts:invalid invalid 'protos' parameter"}},
+ {"jsonrpc":"2.0","id":7,"result":{
+ "taps":[{
+ "tap":"hosts:ipv4,ipv6",
+ "type":"hosts",
+ "ipv4_hosts":matchAddrNameList,
+ "ipv6_hosts":matchAddrNameList,
+ }]
+ }},
+ {"jsonrpc":"2.0","id":8,"error":{"code":-11015,"message":"sharkd_session_process_tap() hosts=hosts:ipv4,ipv6,invalid invalid 'protos' parameter"}},
+ ))
+
+ def test_sharkd_req_tap_eo_http(self, check_sharkd_session, capture_file):
+ check_sharkd_session((
+ {"jsonrpc":"2.0", "id":1, "method":"load",
+ "params":{"file": capture_file('http-ooo.pcap')}
+ },
+ {"jsonrpc":"2.0", "id":2, "method":"tap", "params":{"tap0": "eo:http"}},
+ ), (
+ {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}},
+ {"jsonrpc":"2.0","id":2,"result":{
+ "taps":[{
+ "tap":"eo:http",
+ "type":"eo",
+ "proto":"HTTP",
+ "objects":[{
+ "pkt":11,
+ "filename":"4",
+ "_download":"eo:http_0",
+ "len":5,
+ "sha1":"4a4121ecd766ed16943a0c7b54c18f743e90c3f6"
+ },{
+ "pkt":13,
+ "_download":"eo:http_1",
+ "len":5,
+ "sha1":"29a51e7382d06ff40467272f02e413ca7b51636e"
+ },{
+ "pkt":14,
+ "_download":"eo:http_2",
+ "len":5,
+ "sha1":"f6d0c643351580307b2eaa6a7560e76965496bc7"}]
+ }]
+ }}
+ ))
+
+ def test_sharkd_req_follow_bad(self, check_sharkd_session, capture_file):
+ # Unrecognized taps currently produce no output (not even err).
+ check_sharkd_session((
+ {"jsonrpc":"2.0", "id":1, "method":"load",
+ "params":{"file": capture_file('dhcp.pcap')}
+ },
+ {"jsonrpc":"2.0", "id":2, "method":"follow"},
+ {"jsonrpc":"2.0", "id":3, "method":"follow",
+ "params":{"follow": "garbage follow", "filter": "ip"}
+ },
+ {"jsonrpc":"2.0", "id":4, "method":"follow",
+ "params":{"follow": "HTTP", "filter": "garbage filter"}
+ },
+ ), (
+ {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}},
+ {"jsonrpc":"2.0","id":2,"error":{"code":-32600,"message":"Mandatory parameter follow is missing"}},
+ {"jsonrpc":"2.0","id":3,"error":{"code":-12001,"message":"sharkd_session_process_follow() follower=garbage follow not found"}},
+ {"jsonrpc":"2.0","id":4,
+ "error":{"code":-12002,"message":"sharkd_session_process_follow() name=HTTP error=Filter \"garbage filter\" is invalid - \"filter\" was unexpected in this context."}
+ },
+ ))
+
+ def test_sharkd_req_follow_no_match(self, check_sharkd_session, capture_file):
+ check_sharkd_session((
+ {"jsonrpc":"2.0", "id":1, "method":"load",
+ "params":{"file": capture_file('dhcp.pcap')}
+ },
+ {"jsonrpc":"2.0", "id":2, "method":"follow",
+ "params":{"follow": "HTTP", "filter": "ip"}
+ },
+ ), (
+ {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}},
+ {"jsonrpc":"2.0","id":2,
+ "result":{"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((
+ {"jsonrpc":"2.0", "id":1, "method":"load",
+ "params":{"file": capture_file('dhcp.pcap')}
+ },
+ {"jsonrpc":"2.0", "id":2, "method":"follow",
+ "params":{"follow": "UDP", "filter": "frame.number==1"}
+ },
+ ), (
+ {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}},
+ {"jsonrpc":"2.0","id":2,
+ "result":{
+ "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((
+ {"jsonrpc":"2.0", "id":1, "method":"load",
+ "params":{"file": capture_file('dhcp.pcap')}
+ },
+ {"jsonrpc":"2.0", "id":2, "method":"iograph"},
+ {"jsonrpc":"2.0", "id":3, "method":"iograph",
+ "params":{"graph0": "garbage graph name"}
+ },
+ ), (
+ {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}},
+ {"jsonrpc":"2.0","id":2,"error":{"code":-32600,"message":"Mandatory parameter graph0 is missing"}},
+ {"jsonrpc":"2.0","id":3,"result":{"iograph": []}},
+ ))
+
+ def test_sharkd_req_iograph_basic(self, check_sharkd_session, capture_file):
+ check_sharkd_session((
+ {"jsonrpc":"2.0", "id":1, "method":"load",
+ "params":{"file": capture_file('dhcp.pcap')}
+ },
+ {"jsonrpc":"2.0", "id":1, "method":"iograph",
+ "params":{"graph0": "max:udp.length", "filter0": "udp.length"}
+ },
+ {"jsonrpc":"2.0", "id":2, "method":"iograph",
+ "params":{"graph0": "packets", "graph1": "bytes"}
+ },
+ {"jsonrpc":"2.0", "id":3, "method":"iograph",
+ "params":{"graph0": "packets", "filter0": "garbage filter"}
+ },
+ ), (
+ {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}},
+ {"jsonrpc":"2.0","id":1,"result":{"iograph": [{"items": [308.000000]}]}},
+ {"jsonrpc":"2.0","id":2,"result":{"iograph": [{"items": [4.000000]}, {"items": [1312.000000]}]}},
+ {"jsonrpc":"2.0","id":3,"error":{"code":-6001,"message":"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((
+ {"jsonrpc":"2.0", "id":1, "method":"load",
+ "params":{"file": capture_file('dhcp.pcap')}
+ },
+ {"jsonrpc":"2.0", "id":2, "method":"intervals",
+ "params":{"filter": "garbage filter"}
+ },
+ ), (
+ {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}},
+ {"jsonrpc":"2.0","id":2,"error":{"code":-7001,"message":"Invalid filter parameter: garbage filter"}},
+ ))
+
+ def test_sharkd_req_intervals_basic(self, check_sharkd_session, capture_file):
+ check_sharkd_session((
+ {"jsonrpc":"2.0", "id":1, "method":"load",
+ "params":{"file": capture_file('dhcp.pcap')}
+ },
+ {"jsonrpc":"2.0", "id":2, "method":"intervals"},
+ {"jsonrpc":"2.0", "id":3, "method":"intervals",
+ "params":{"interval": 1}
+ },
+ {"jsonrpc":"2.0", "id":4, "method":"intervals",
+ "params":{"filter": "frame.number <= 2"}
+ },
+ ), (
+ {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}},
+ {"jsonrpc":"2.0","id":2,"result":{"intervals":[[0,4,1312]],"last":0,"frames":4,"bytes":1312}},
+ {"jsonrpc":"2.0","id":3,"result":{"intervals":[[0,2,656],[70,2,656]],"last":70,"frames":4,"bytes":1312}},
+ {"jsonrpc":"2.0","id":4,"result":{"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((
+ {"jsonrpc":"2.0", "id":1, "method":"load",
+ "params":{"file": capture_file('dhcp.pcap')}
+ },
+ {"jsonrpc":"2.0", "id":2, "method":"frame",
+ "params":{"frame": 2}
+ },
+ ), (
+ {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}},
+ {"jsonrpc":"2.0","id":2,"result":{"fol": [["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((
+ {"jsonrpc":"2.0", "id":1, "method":"load",
+ "params":{"file": capture_file('dhcp.pcap')}
+ },
+ {"jsonrpc":"2.0", "id":2, "method":"frame",
+ "params":{"frame": 2, "proto": True}
+ },
+ ), (
+ {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}},
+ {"jsonrpc":"2.0","id":2,"result":
+ MatchObject({
+ "tree": MatchList({
+ "l": "Dynamic Host Configuration Protocol (Offer)",
+ "t": "proto",
+ "f": "dhcp",
+ "fn": "dhcp",
+ "e": MatchAny(int),
+ "n": MatchList({
+ "l": "Padding: 0000000000000000000000000000000000000000000000000000",
+ "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",
+ "fn": "dhcp.option.padding"
+ }, 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((
+ {"jsonrpc":"2.0", "id":1, "method":"load",
+ "params":{"file": capture_file('dhcp.pcap')}
+ },
+ {"jsonrpc":"2.0", "id":2, "method":"setcomment",
+ "params":{"frame": 99999, "comment": "meh\nbaz"}
+ },
+ {"jsonrpc":"2.0", "id":3, "method":"setcomment",
+ "params":{"frame": 3, "comment": "foo\nbar"}
+ },
+ {"jsonrpc":"2.0", "id":4, "method":"frame",
+ "params":{"frame": 3}
+ },
+
+ ), (
+ {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}},
+ {"jsonrpc":"2.0","id":2,"error":{"code":-3002,"message":"Frame number is out of range"}},
+ {"jsonrpc":"2.0","id":3,"result":{"status":"OK"}},
+ {"jsonrpc":"2.0","id":4,"result":{"comment":["foo\nbar"],"fol": MatchAny(list)}},
+ ))
+
+ def test_sharkd_req_setconf_bad(self, check_sharkd_session):
+ check_sharkd_session((
+ {"jsonrpc":"2.0", "id":1, "method":"setconf",
+ "params":{"name": "uat:garbage-pref", "value": "\"\""}
+ },
+ ), (
+ {"jsonrpc":"2.0","id":1,"error":{"code":-4005,"message":"Unable to set the preference"}},
+ ))
+
+ def test_sharkd_req_dumpconf_bad(self, check_sharkd_session):
+ check_sharkd_session((
+ {"jsonrpc":"2.0", "id":1, "method":"dumpconf",
+ "params":{"pref": "bad.preference"}
+ },
+ {"jsonrpc":"2.0", "id":2, "method":"dumpconf",
+ "params":{"pref": "invalid-garbage-preference"}
+ },
+ {"jsonrpc":"2.0", "id":3, "method":"dumpconf",
+ "params":{"pref": "uat:custom_http_header_fields"}
+ },
+ ), (
+ {"jsonrpc":"2.0","id":1,"error":{"code":-9001,"message":"Invalid pref bad.preference."}},
+ {"jsonrpc":"2.0","id":2,"error":{"code":-9002,"message":"Invalid pref invalid-garbage-preference."}},
+ {"jsonrpc":"2.0","id":3,"error":{"code":-9002,"message":"Invalid pref uat:custom_http_header_fields."}},
+ ))
+
+ def test_sharkd_req_dumpconf_all(self, check_sharkd_session):
+ check_sharkd_session((
+ {"jsonrpc":"2.0", "id":1, "method":"dumpconf"},
+ ), (
+ {"jsonrpc":"2.0","id":1,"result":{"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: too
+ check_sharkd_session((
+ {"jsonrpc":"2.0", "id":1, "method":"load",
+ "params":{"file": capture_file('tls12-dsb.pcapng')}
+ },
+ {"jsonrpc":"2.0", "id":2, "method":"download",
+ "params":{"token": "ssl-secrets"}
+ },
+ ), (
+ {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}},
+ {"jsonrpc":"2.0","id":2,"result":{"file": "keylog.txt", "mime": "text/plain",
+ "data": MatchRegExp(r'Q0xJRU5UX1JBTkRPTSBm.+')}
+ },
+ ))
+
+ def test_sharkd_req_download_rtp_stream(self, check_sharkd_session, capture_file):
+ check_sharkd_session((
+ {"jsonrpc":"2.0", "id":1, "method":"load",
+ "params":{"file": capture_file('sip-rtp.pcapng')}
+ },
+ {"jsonrpc":"2.0", "id":2, "method":"download",
+ "params":{"token": "rtp:200.57.7.204_8000_200.57.7.196_40376_0xd2bd4e3e"}},
+ {"jsonrpc":"2.0", "id":3, "method":"download",
+ "params":{"token": "rtp:1.1.1.1_8000_1.1.1.2_9000_0xdddddddd"}},
+ ), (
+ {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}},
+ {"jsonrpc":"2.0","id":2,"result":{
+ "file":"rtp:200.57.7.204_8000_200.57.7.196_40376_0xd2bd4e3e",
+ "mime":"audio/x-wav",
+ "data":MatchRegExp(r'UklGRv.+')}
+ },
+ {"jsonrpc":"2.0","id":3,"error":{"code":-10003,"message":"no rtp data available"}},
+ ))
+
+ def test_sharkd_req_download_bad_tokens(self, check_sharkd_session, capture_file):
+ check_sharkd_session((
+ {"jsonrpc":"2.0", "id":1, "method":"load",
+ "params":{"file": capture_file('tls12-dsb.pcapng')}
+ },
+ {"jsonrpc":"2.0", "id":2, "method":"download",
+ "params":{"token": "BOGUSTOKEN"}
+ },
+ {"jsonrpc":"2.0", "id":3, "method":"download",
+ "params":{}
+ },
+ ), (
+ {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}},
+ {"jsonrpc":"2.0","id":2,"error":{"code":-10004,"message":"unrecognized token"}},
+ {"jsonrpc":"2.0","id":3,"error":{"code":-10005,"message":"missing token"}},
+ ))
+
+ def test_sharkd_req_download_eo_http_with_prior_tap_eo_http(self, check_sharkd_session, capture_file):
+ check_sharkd_session((
+ {"jsonrpc":"2.0", "id":1, "method":"load",
+ "params":{"file": capture_file('http-ooo.pcap')}
+ },
+ {"jsonrpc":"2.0", "id":2, "method":"tap", "params":{"tap0": "eo:http"}},
+ {"jsonrpc":"2.0", "id":3, "method":"download",
+ "params":{"token": "eo:http_0"}},
+ {"jsonrpc":"2.0", "id":4, "method":"download",
+ "params":{"token": "eo:http_1"}},
+ {"jsonrpc":"2.0", "id":5, "method":"download",
+ "params":{"token": "eo:http_2"}},
+ {"jsonrpc":"2.0", "id":6, "method":"download",
+ "params":{"token": "eo:http_999"}},
+ ), (
+ {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}},
+ {"jsonrpc":"2.0","id":2,"result":{
+ "taps":[{
+ "tap":"eo:http",
+ "type":"eo",
+ "proto":"HTTP",
+ "objects":[{
+ "pkt":11,
+ "filename":"4",
+ "_download":"eo:http_0",
+ "len":5,
+ "sha1":"4a4121ecd766ed16943a0c7b54c18f743e90c3f6"
+ },{
+ "pkt":13,
+ "_download":"eo:http_1",
+ "len":5,
+ "sha1":"29a51e7382d06ff40467272f02e413ca7b51636e"
+ },{
+ "pkt":14,
+ "_download":"eo:http_2",
+ "len":5,
+ "sha1":"f6d0c643351580307b2eaa6a7560e76965496bc7"}]
+ }]
+ }},
+ {"jsonrpc":"2.0","id":3,"result":{
+ "file":"4","mime":"application/octet-stream","data":"Zm91cgo="}},
+ {"jsonrpc":"2.0","id":4,"result":{
+ "file":"eo:http_1","mime":"application/octet-stream","data":"QVRBDQo="}},
+ {"jsonrpc":"2.0","id":5,"result":{
+ "file":"eo:http_2","mime":"application/octet-stream","data":"MA0KDQo="}},
+ {"jsonrpc":"2.0","id":6,"result":{}},
+ ))
+ def test_sharkd_req_download_eo_http_without_prior_tap_eo_http(self, check_sharkd_session, capture_file):
+ check_sharkd_session((
+ {"jsonrpc":"2.0", "id":1, "method":"load",
+ "params":{"file": capture_file('http-ooo.pcap')}
+ },
+ {"jsonrpc":"2.0", "id":2, "method":"download",
+ "params":{"token": "eo:http_0"}},
+ {"jsonrpc":"2.0", "id":3, "method":"download",
+ "params":{"token": "eo:http_1"}},
+ {"jsonrpc":"2.0", "id":4, "method":"download",
+ "params":{"token": "eo:http_2"}},
+ {"jsonrpc":"2.0", "id":5, "method":"download",
+ "params":{"token": "eo:http_999"}},
+ ), (
+ {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}},
+ {"jsonrpc":"2.0","id":2,"result":{
+ "file":"4","mime":"application/octet-stream","data":"Zm91cgo="}},
+ {"jsonrpc":"2.0","id":3,"result":{
+ "file":"eo:http_1","mime":"application/octet-stream","data":"QVRBDQo="}},
+ {"jsonrpc":"2.0","id":4,"result":{
+ "file":"eo:http_2","mime":"application/octet-stream","data":"MA0KDQo="}},
+ {"jsonrpc":"2.0","id":5,"result":{}},
+ ))
+ def test_sharkd_req_bye(self, check_sharkd_session):
+ check_sharkd_session((
+ {"jsonrpc":"2.0", "id":1, "method":"bye"},
+ ), (
+ {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}},
+ ))
+
+ def test_sharkd_bad_request(self, check_sharkd_session):
+ check_sharkd_session((
+ {"jsonrpc":"2.0", "id":1, "method":"dud"},
+ ), (
+ {'jsonrpc': '2.0', 'id': 1, 'error': {'code': -32601, 'message': 'The method dud is not supported'}},
+ ))
+
+ def test_sharkd_config(self, check_sharkd_session):
+ check_sharkd_session((
+ {"jsonrpc":"2.0", "id":1, "method":"setconf",
+ "params":{"name": "uat:custom_http_header_fields", "value": "\"X-Header-Name\", \"Description\""}
+ },
+ {"jsonrpc":"2.0", "id":2, "method":"setconf",
+ "params":{"name": "tcp.check_checksum", "value": "true"}
+ },
+ {"jsonrpc":"2.0", "id":3, "method":"dumpconf",
+ "params":{"pref": "tcp.check_checksum"}
+ },
+ {"jsonrpc":"2.0", "id":4, "method":"setconf",
+ "params":{"name": "tcp.check_checksum", "value": "false"}
+ },
+ {"jsonrpc":"2.0", "id":5, "method":"dumpconf",
+ "params":{"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).
+ {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}},
+ {"jsonrpc":"2.0","id":2,"result":{"status":"OK"}},
+ {"jsonrpc":"2.0","id":3,"result":{"prefs":{"tcp.check_checksum":{"b":1}}}},
+ {"jsonrpc":"2.0","id":4,"result":{"status":"OK"}},
+ {"jsonrpc":"2.0","id":5,"result":{"prefs":{"tcp.check_checksum":{"b":0}}}},
+ ))
+
+ def test_sharkd_config_enum(self, check_sharkd_session):
+ '''Dump default enum preference value, change it and restore it.'''
+ check_sharkd_session((
+ {"jsonrpc":"2.0", "id":1, "method":"dumpconf",
+ "params":{"pref": "wlan.ignore_wep"}
+ },
+ {"jsonrpc":"2.0", "id":2, "method":"setconf",
+ "params":{"name": "wlan.ignore_wep", "value": "Yes - with IV"}
+ },
+ {"jsonrpc":"2.0", "id":3, "method":"dumpconf",
+ "params":{"pref": "wlan.ignore_wep"}
+ },
+ {"jsonrpc":"2.0", "id":4, "method":"setconf",
+ "params":{"name": "wlan.ignore_wep", "value": "No"}
+ },
+ {"jsonrpc":"2.0", "id":5, "method":"dumpconf",
+ "params":{"pref": "wlan.ignore_wep"}
+ },
+ ), (
+ {"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"}]}}}},
+ {"jsonrpc":"2.0","id":2,"result":{"status":"OK"}},
+ {"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"}]}}}},
+ {"jsonrpc":"2.0","id":4,"result":{"status":"OK"}},
+ {"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"}]}}}},
+ ))
+
+ def test_sharkd_nested_file(self, check_sharkd_session, capture_file):
+ '''Request a frame from a file with a deep level of nesting.'''
+ check_sharkd_session((
+ {"jsonrpc":"2.0", "id":1, "method":"load",
+ "params":{"file": capture_file("http2-data-reassembly.pcap")}
+ },
+ {"jsonrpc":"2.0", "id":2, "method":"frame",
+ "params":{"frame": "4", "proto": "yes"}
+ },
+ ), (
+ {"jsonrpc":"2.0","id":1,"result":{"status":"OK"}},
+ MatchAny(),
+ ))
diff --git a/test/suite_text2pcap.py b/test/suite_text2pcap.py
new file mode 100644
index 0000000..6163f18
--- /dev/null
+++ b/test/suite_text2pcap.py
@@ -0,0 +1,549 @@
+#
+# Wireshark tests
+# By Gerald Combs <gerald@wireshark.org>
+#
+# Ported from a set of Bash scripts which were copyright 2005 Ulf Lamping
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+'''Text2pcap tests'''
+
+import re
+import subprocess
+from subprocesstest import get_capture_info, grep_output
+import json
+import pytest
+
+testin_txt = 'testin.txt'
+testout_pcap = 'testout.pcap'
+testout_pcapng = 'testout.pcapng'
+
+file_type_to_descr = {
+ 'pcap': 'Wireshark/tcpdump/... - pcap',
+ 'pcapng': 'Wireshark/... - pcapng',
+}
+
+file_type_to_testout = {
+ 'pcap': testout_pcap,
+ 'pcapng': testout_pcapng,
+}
+
+encap_to_link_type_long = {
+ 'Ethernet': 1,
+ 'Raw IP': 14,
+ 'Linux cooked-mode capture v1': 113,
+ 'IEEE 802.11 plus radiotap radio header': 127,
+ 'DVB-CI (Common Interface)': 235,
+}
+
+encap_to_link_type = {
+ 'ether': 1,
+ 'rawip': 14,
+ 'linux-sll': 113,
+ 'ieee-802-11-radiotap': 127,
+ 'dvbci': 235,
+}
+
+def check_capinfos_info(cmd_capinfos, cap_file):
+ cap_info = {
+ 'filetype': None,
+ 'encapsulation': None,
+ 'packets': None,
+ 'datasize': None,
+ 'timeend': None,
+ }
+ str_pats = {
+ 'filetype': 'File type',
+ 'encapsulation': 'File encapsulation',
+ 'timeend': 'Last packet time',
+ }
+ int_pats = {
+ 'packets': 'Number of packets',
+ 'datasize': 'Data size',
+ }
+ capinfos_out = get_capture_info(cmd_capinfos, ('-tEcdMe',), cap_file)
+
+ for ci_line in capinfos_out.splitlines():
+ for sp_key in str_pats:
+ str_pat = r'{}:\s+([\S ]+)'.format(str_pats[sp_key])
+ str_res = re.search(str_pat, ci_line)
+ if str_res is not None:
+ cap_info[sp_key] = str_res.group(1)
+
+ for ip_key in int_pats:
+ int_pat = r'{}:\s+(\d+)'.format(int_pats[ip_key])
+ int_res = re.search(int_pat, ci_line)
+ if int_res is not None:
+ cap_info[ip_key] = int(int_res.group(1))
+
+ return cap_info
+
+def get_capinfos_cmp_info(cii):
+ cmp_keys = ('encapsulation', 'packets', 'datasize')
+ return { k: v for k, v in cii.items() if k in cmp_keys }
+
+def compare_capinfos_info(cii1, cii2, filename1, filename2):
+ cii_cmp_i1 = get_capinfos_cmp_info(cii1)
+ cii_cmp_i2 = get_capinfos_cmp_info(cii2)
+ assert cii_cmp_i1 == cii_cmp_i2
+
+@pytest.fixture
+def check_text2pcap(cmd_tshark, cmd_text2pcap, cmd_capinfos, capture_file, result_file, base_env):
+ def check_text2pcap_real(cap_filename, file_type, expected_packets=None, expected_datasize=None):
+ # Perform the following actions
+ # - Get information for the input pcap file with capinfos
+ # - Generate an ASCII hexdump with TShark
+ # - Convert the ASCII hexdump back to pcap using text2pcap
+ # - Get information for the output pcap file with capinfos
+ # - Check that file type, encapsulation type, number of packets and data size
+ # in the output file are the same as in the input file
+
+ cap_file = capture_file(cap_filename)
+ pre_cap_info = check_capinfos_info(cmd_capinfos, cap_file)
+ # Due to limitations of "tshark -x", the output might contain more than one
+ # data source which is subsequently interpreted as additional frame data.
+ # See https://gitlab.com/wireshark/wireshark/-/issues/14639
+ if expected_packets is not None:
+ assert pre_cap_info['packets'] != expected_packets
+ pre_cap_info['packets'] = expected_packets
+ if expected_datasize is not None:
+ assert pre_cap_info['datasize'] != expected_datasize
+ pre_cap_info['datasize'] = expected_datasize
+ assert pre_cap_info['encapsulation'] in encap_to_link_type
+
+ assert file_type in file_type_to_testout, 'Invalid file type'
+
+ # text2pcap_generate_input()
+ # $TSHARK -o 'gui.column.format:"Time","%t"' -tad -P -x -r $1 > testin.txt
+ testin_file = result_file(testin_txt)
+ tshark_cmd = '{cmd} -r {cf} -o gui.column.format:"Time","%t" -t ad -P --hexdump frames > {of}'.format(
+ cmd = cmd_tshark,
+ cf = cap_file,
+ of = testin_file,
+ )
+ subprocess.check_call(tshark_cmd, shell=True, env=base_env)
+
+ testout_fname = file_type_to_testout[file_type]
+ testout_file = result_file(testout_fname)
+ # The first word is the file type (the rest might be compression info)
+ filetype_flag = pre_cap_info['filetype'].split()[0]
+ # We want the -a flag, because the tshark -x format is a hex+ASCII
+ # format where the ASCII can be confused for hex bytes without it.
+ # XXX: -t ISO also works now too for this output
+ text2pcap_cmd = '{cmd} -a -F {filetype} -l {linktype} -t "%Y-%m-%d %H:%M:%S.%f" {in_f} {out_f}'.format(
+ cmd = cmd_text2pcap,
+ filetype = filetype_flag,
+ linktype = encap_to_link_type[pre_cap_info['encapsulation']],
+ in_f = testin_file,
+ out_f = testout_file,
+ )
+ proc = subprocess.run(text2pcap_cmd, shell=True, check=True, capture_output=True, encoding='utf-8', env=base_env)
+ assert grep_output(proc.stderr, 'potential packet'), "text2pcap didn't complete"
+ assert not grep_output(proc.stderr, 'Inconsistent offset'), 'text2pcap detected inconsistent offset'
+
+ post_cap_info = check_capinfos_info(cmd_capinfos, testout_file)
+ compare_capinfos_info(pre_cap_info, post_cap_info, cap_file, testout_fname)
+ return check_text2pcap_real
+
+
+class TestText2pcapPcap:
+ def test_text2pcap_empty_pcap(self, check_text2pcap):
+ '''Test text2pcap with empty.pcap.'''
+ check_text2pcap('empty.pcap', 'pcap')
+
+ def test_text2pcap_dhcp_pcap(self, check_text2pcap):
+ '''Test text2pcap with dhcp.pcap.'''
+ check_text2pcap('dhcp.pcap', 'pcap')
+
+ def test_text2pcap_dhcp_nanosecond_pcap(self, check_text2pcap):
+ '''Test text2pcap with dhcp-nanosecond.pcap.'''
+ check_text2pcap('dhcp-nanosecond.pcap', 'pcap')
+
+ def test_text2pcap_segmented_fpm_pcap(self, check_text2pcap):
+ '''Test text2pcap with segmented_fpm.pcap.'''
+ check_text2pcap('segmented_fpm.pcap', 'pcap')
+
+ def test_text2pcap_c1222_std_example8_pcap(self, check_text2pcap):
+ '''Test text2pcap with c1222_std_example8.pcap.'''
+ check_text2pcap('c1222_std_example8.pcap', 'pcap')
+
+ def test_text2pcap_dns_port_pcap(self, check_text2pcap):
+ '''Test text2pcap with dns_port.pcap.'''
+ check_text2pcap('dns_port.pcap', 'pcap')
+
+ def test_text2pcap_dvb_ci_uv1_0000_pcap(self, check_text2pcap):
+ '''Test text2pcap with dvb-ci_UV1_0000.pcap.'''
+ check_text2pcap('dvb-ci_UV1_0000.pcap', 'pcap')
+
+ def test_text2pcap_ikev1_certs_pcap(self, check_text2pcap):
+ '''Test text2pcap with ikev1-certs.pcap.'''
+ check_text2pcap('ikev1-certs.pcap', 'pcap')
+
+ def test_text2pcap_rsa_p_lt_q_pcap(self, check_text2pcap):
+ '''Test text2pcap with rsa-p-lt-q.pcap.'''
+ check_text2pcap('rsa-p-lt-q.pcap', 'pcap')
+
+ def test_text2pcap_rsasnakeoil2_pcap(self, check_text2pcap):
+ '''Test text2pcap with rsasnakeoil2.pcap.'''
+ check_text2pcap('rsasnakeoil2.pcap', 'pcap')
+
+ def test_text2pcap_sample_control4_2012_03_24_pcap(self, check_text2pcap):
+ '''Test text2pcap with sample_control4_2012-03-24.pcap.'''
+ # Tests handling additional data source (decrypted ZigBee packets)
+ # Either tshark must not output the additional data source,
+ # or text2pcap must ignore it.
+ check_text2pcap('sample_control4_2012-03-24.pcap', 'pcap')
+
+ def test_text2pcap_snakeoil_dtls_pcap(self, check_text2pcap):
+ '''Test text2pcap with snakeoil-dtls.pcap.'''
+ check_text2pcap('snakeoil-dtls.pcap', 'pcap')
+
+ def test_text2pcap_wpa_eap_tls_pcap_gz(self, check_text2pcap):
+ '''Test text2pcap with wpa-eap-tls.pcap.gz.'''
+ # Tests handling additional data source (reassemblies)
+ # Either tshark must not output the additional data source,
+ # or text2pcap must ignore it.
+ check_text2pcap('wpa-eap-tls.pcap.gz', 'pcap')
+
+ def test_text2pcap_wpa_induction_pcap(self, check_text2pcap):
+ '''Test text2pcap with wpa-Induction.pcap.gz.'''
+ check_text2pcap('wpa-Induction.pcap.gz', 'pcap')
+
+
+class TestText2pcapPcapng:
+ def test_text2pcap_dhcp_pcapng(self, check_text2pcap):
+ '''Test text2pcap with dhcp.pcapng.'''
+ check_text2pcap('dhcp.pcapng', 'pcapng')
+
+ def test_text2pcap_dhcp_nanosecond_pcapng(self, check_text2pcap):
+ '''Test text2pcap with dhcp-nanosecond.pcapng.'''
+ check_text2pcap('dhcp-nanosecond.pcapng', 'pcapng')
+
+ def test_text2pcap_dhe1_pcapng_gz(self, check_text2pcap):
+ '''Test text2pcap with dhe1.pcapng.gz.'''
+ check_text2pcap('dhe1.pcapng.gz', 'pcapng')
+
+ def test_text2pcap_dmgr_pcapng(self, check_text2pcap):
+ '''Test text2pcap with dmgr.pcapng.'''
+ check_text2pcap('dmgr.pcapng', 'pcapng')
+
+ def test_text2pcap_dns_icmp_pcapng_gz(self, check_text2pcap):
+ '''Test text2pcap with dns+icmp.pcapng.gz.'''
+ # This file needs (and thus tests) the -a flag to identify when the
+ # start of the ASCII dump looks like hex.
+ check_text2pcap('dns+icmp.pcapng.gz', 'pcapng')
+
+ def test_text2pcap_packet_h2_14_headers_pcapng(self, check_text2pcap):
+ '''Test text2pcap with packet-h2-14_headers.pcapng.'''
+ check_text2pcap('packet-h2-14_headers.pcapng', 'pcapng')
+
+ def test_text2pcap_sip_pcapng(self, check_text2pcap):
+ '''Test text2pcap with sip.pcapng.'''
+ check_text2pcap('sip.pcapng', 'pcapng')
+
+
+@pytest.fixture
+def check_rawip(run_text2pcap_capinfos_tshark):
+ def check_rawip_real(pdata, packets, datasize):
+
+ assert {'encapsulation': 'rawip4', 'packets': packets, \
+ 'datasize': datasize, 'expert': ''} == \
+ run_text2pcap_capinfos_tshark(pdata, ("-l228",))
+ return check_rawip_real
+
+
+class TestText2pcapParsing:
+ def test_text2pcap_eol_hash(self, cmd_text2pcap, cmd_capinfos, capture_file, result_file, base_env):
+ '''Test text2pcap hash sign at the end-of-line.'''
+ txt_fname = 'text2pcap_hash_eol.txt'
+ testout_file = result_file(testout_pcap)
+ proc = subprocess.run((cmd_text2pcap,
+ '-F', 'pcapng',
+ '-t', '%Y-%m-%d %H:%M:%S.',
+ capture_file(txt_fname),
+ testout_file,
+ ), check=True, capture_output=True, encoding='utf-8', env=base_env)
+ assert not grep_output(proc.stderr, 'Inconsistent offset'), 'text2pcap failed to parse the hash sign at the end of the line'
+ assert grep_output(proc.stderr, r'Directive \[ test_directive'), 'text2pcap failed to parse #TEXT2PCAP test_directive'
+ pre_cmp_info = {'encapsulation': 'ether', 'packets': 1, 'datasize': 96, 'timeend': '2015-10-01 21:16:24.317453000'}
+ post_cmp_info = check_capinfos_info(cmd_capinfos, testout_file)
+ compare_capinfos_info(pre_cmp_info, post_cmp_info, txt_fname, testout_pcap)
+
+ def test_text2pcap_doc_no_line_limit(self, check_rawip):
+ '''
+ Verify: There is no limit on the width or number of bytes per line and
+ Bytes/hex numbers can be uppercase or lowercase.
+ '''
+ pdata = "0000 45 00 00 21 00 01 00 00 40 11\n" \
+ "000A 7C C9 7F 00 00 01" \
+ " 7f 00 00 01 ff 98 00 13 00 0d b5 48 66 69 72 73\n" \
+ "0020 74\n"
+ check_rawip(pdata, 1, 33)
+
+ def test_text2pcap_doc_ignore_text(self, check_rawip):
+ '''
+ Verify: the text dump at the end of the line is ignored. Any hex numbers
+ in this text are also ignored. Any lines of text between the bytestring
+ lines is ignored. Any line where the first non-whitespace character is
+ '#' will be ignored as a comment.
+ '''
+ pdata = "0000 45 00 00 21 00 01 00 00 40 11 7c c9 7f 00 00 01 bad\n" \
+ "0010 7f 00 00 01 ff 98 00 13 00 0d b5 48 66 69 72 73 - 42\n" \
+ "0020 74\n" \
+ "0021\n" \
+ "That 0021 should probably be ignored as it this: 00 20\n" \
+ "0000 45 00 00 22 00 01 00 00 40 11 7c c8 7f 00 00 01\n" \
+ "0010 7f 00 00 01 ff 99 00 13 00 0e bc e9 73 65 63 6f ...\n" \
+ " \t# 0020 12 34 56<-- comment, ignore this!\n" \
+ "0020 6e 64\n" \
+ "12 34 56 78 90 # ignore this due to missing offset!\n"
+ check_rawip(pdata, 2, 67)
+
+ def test_text2pcap_doc_leading_text_ignored(self, check_rawip):
+ '''
+ Verify: Any test before the offset is ignored, including email
+ forwarding characters '>'. An offset is a hex number longer than two
+ characters. An offset of zero is indicative of starting a new packet.
+ '''
+ pdata = "> >> 000 45 00 00 21 00 01 00 00 40 11 7c c9 7f 00 00 01\n" \
+ "> >> 010 7f 00 00 01 ff 98 00 13 00 0d b5 48 66 69 72 73\n" \
+ "> >> 020 74\n" \
+ "> >> 000 45 00 00 22 00 01 00 00 40 11 7c c8 7f 00 00 01\n" \
+ "> >> 010 7f 00 00 01 ff 99 00 13 00 0e bc e9 73 65 63 6f\n" \
+ "> >> 020 6e 64\n"
+ check_rawip(pdata, 2, 67)
+
+ def test_text2pcap_doc_require_offset(self, check_rawip):
+ '''Any line which has only bytes without a leading offset is ignored.'''
+ pdata = "45 00 00 21 00 01 00 00 40 11 7c c9 7f 00 00 01\n" \
+ "7f 00 00 01 ff 98 00 13 00 0d b5 48 66 69 72 73\n"
+ check_rawip(pdata, 0, 0)
+
+ def test_text2pcap_eol_missing(self, check_rawip):
+ '''Verify that the last LF can be missing.'''
+ pdata = "0000 45 00 00 21 00 01 00 00 40 11 7c c9 7f 00 00 01\n" \
+ "0010 7f 00 00 01 ff 98 00 13 00 0d b5 48 66 69 72 73\n" \
+ "0020 74"
+ check_rawip(pdata, 1, 33)
+
+
+@pytest.fixture
+def run_text2pcap_capinfos_tshark(cmd_text2pcap, cmd_tshark, cmd_capinfos, result_file, base_env):
+ def run_text2pcap_capinfos_tshark_real(content, args):
+
+ testin_file = result_file(testin_txt)
+ testout_file = result_file(testout_pcap)
+
+ with open(testin_file, "w") as f:
+ f.write(content)
+ subprocess.check_call((cmd_text2pcap,) + args + (testin_file, testout_file), env=base_env)
+
+ capinfo = get_capinfos_cmp_info(check_capinfos_info(cmd_capinfos, testout_file))
+
+ stdout = subprocess.check_output((cmd_tshark, '-q', '-z', 'expert,warn',
+ '-o', 'udp.check_checksum: TRUE',
+ '-o', 'tcp.check_checksum: TRUE',
+ '-o', 'sctp.checksum:TRUE',
+ '-r', testout_file),
+ encoding='utf-8', env=base_env)
+ capinfo['expert'] = stdout
+ return capinfo
+ return run_text2pcap_capinfos_tshark_real
+
+
+class TestText2pcapHeaders:
+ '''Test TCP, UDP or SCTP header without -4 or -6 option'''
+ maxDiff = None
+
+ def test_text2pcap_tcp(self, run_text2pcap_capinfos_tshark):
+ '''Test TCP over IPv4'''
+ assert {'encapsulation': 'ether', 'packets': 1, \
+ 'datasize': 60, 'expert': ''} == \
+ run_text2pcap_capinfos_tshark(
+ "0000: ff ff ff ff\n", ("-T", "1234,1234"))
+
+ def test_text2pcap_udp(self, run_text2pcap_capinfos_tshark):
+ '''Test UDP over IPv4'''
+ assert {'encapsulation': 'ether', 'packets': 1, \
+ 'datasize': 60, 'expert': ''} == \
+ run_text2pcap_capinfos_tshark(
+ "0000: ff ff ff ff\n", ("-u", "1234,1234"))
+
+ def test_text2pcap_sctp(self, run_text2pcap_capinfos_tshark):
+ '''Test SCTP over IPv4'''
+ assert {'encapsulation': 'ether', 'packets': 1, \
+ 'datasize': 70, 'expert': ''}, \
+ run_text2pcap_capinfos_tshark(
+ "0000 00 03 00 18 00 00 00 00 00 00 00 00 00 00 00 03\n" +
+ "0010 01 00 03 03 00 00 00 08\n",
+ ("-s", "2905,2905,3"))
+
+ def test_text2pcap_sctp_data(self, run_text2pcap_capinfos_tshark):
+ '''Test SCTP DATA over IPv4'''
+ assert {'encapsulation': 'ether', 'packets': 1, \
+ 'datasize': 70, 'expert': ''} == \
+ run_text2pcap_capinfos_tshark(
+ "0000: 01 00 03 03 00 00 00 08\n",
+ ("-S", "2905,2905,3"))
+
+
+@pytest.fixture
+def run_text2pcap_ipv4(run_text2pcap_capinfos_tshark):
+ def run_text2pcap_ipv4_real(content, args):
+ return run_text2pcap_capinfos_tshark(content,
+ ("-4", "127.0.0.1,127.0.0.1") + args)
+ return run_text2pcap_ipv4_real
+
+
+class TestText2pcapIpv4:
+ '''Test TCP, UDP or SCTP header with -4 option'''
+ maxDiff = None
+
+ def test_text2pcap_ipv4_tcp(self, run_text2pcap_ipv4):
+ '''Test TCP over IPv4'''
+ assert {'encapsulation': 'ether', 'packets': 1, \
+ 'datasize': 60, 'expert': ''} == \
+ run_text2pcap_ipv4("0000: ff ff ff ff\n", ("-T", "1234,1234"))
+
+ def test_text2pcap_ipv4_udp(self, run_text2pcap_ipv4):
+ '''Test UDP over IPv4'''
+ assert {'encapsulation': 'ether', 'packets': 1, \
+ 'datasize': 60, 'expert': ''} == \
+ run_text2pcap_ipv4("0000: ff ff ff ff\n", ("-u", "1234,1234"))
+
+ def test_text2pcap_ipv4_sctp(self, run_text2pcap_ipv4):
+ '''Test SCTP over IPv4'''
+ assert {'encapsulation': 'ether', 'packets': 1, \
+ 'datasize': 70, 'expert': ''} == \
+ run_text2pcap_ipv4(
+ "0000 00 03 00 18 00 00 00 00 00 00 00 00 00 00 00 03\n" +
+ "0010 01 00 03 03 00 00 00 08\n",
+ ("-s", "2905,2905,3"))
+
+ def test_text2pcap_ipv4_sctp_data(self, run_text2pcap_ipv4):
+ '''Test SCTP DATA over IPv4'''
+ assert {'encapsulation': 'ether', 'packets': 1, \
+ 'datasize': 70, 'expert': ''} == \
+ run_text2pcap_ipv4("0000: 01 00 03 03 00 00 00 08\n",
+ ("-S", "2905,2905,3"))
+
+
+@pytest.fixture
+def run_text2pcap_ipv6(cmd_tshark, run_text2pcap_capinfos_tshark, result_file, base_env):
+
+ def run_text2pcap_ipv6_real(content, text2pcap_args, tshark_args = ()):
+ #Run the common text2pcap tests
+ result = run_text2pcap_capinfos_tshark(content,
+ ("-6", "::1,::1") + text2pcap_args)
+
+ #Decode the output pcap in JSON format
+ stdout = subprocess.check_output((cmd_tshark, '-T', 'json',
+ '-r', result_file(testout_pcap)) + tshark_args,
+ encoding='utf-8', env=base_env)
+ data = json.loads(stdout)
+
+ #Add IPv6 payload length and payload length tree to the result dict
+ ipv6 = data[0]['_source']['layers']['ipv6']
+ result['ipv6'] = {
+ 'plen': ipv6.get('ipv6.plen', None),
+ 'plen_tree': ipv6.get('ipv6.plen_tree', None)}
+ return result
+ return run_text2pcap_ipv6_real
+
+
+class TestText2pcapIpv6:
+ '''Test TCP, UDP or SCTP header with -6 option'''
+ maxDiff = None
+
+ def test_text2pcap_ipv6_tcp(self, run_text2pcap_ipv6):
+ '''Test TCP over IPv6'''
+ assert {'encapsulation': 'ether', 'packets': 1, \
+ 'datasize': 78, 'expert': '', \
+ 'ipv6': {'plen': '24', 'plen_tree': None}} == \
+ run_text2pcap_ipv6("0000: ff ff ff ff\n", ("-T", "1234,1234"))
+
+ def test_text2pcap_ipv6_udp(self, run_text2pcap_ipv6):
+ '''Test UDP over IPv6'''
+ assert {'encapsulation': 'ether', 'packets': 1, \
+ 'datasize': 66, 'expert': '', \
+ 'ipv6': {'plen': '12', 'plen_tree': None}} == \
+ run_text2pcap_ipv6("0000: ff ff ff ff\n", ("-u", "1234,1234"))
+
+ def test_text2pcap_ipv6_sctp(self, run_text2pcap_ipv6):
+ '''Test SCTP over IPv6'''
+ assert {'encapsulation': 'ether', 'packets': 1, \
+ 'datasize': 90, 'expert': '', \
+ 'ipv6': {'plen': '36', 'plen_tree': None}} == \
+ run_text2pcap_ipv6(
+ "0000 00 03 00 18 00 00 00 00 00 00 00 00 00 00 00 03\n" +
+ "0010 01 00 03 03 00 00 00 08\n",
+ ("-s", "2905,2905,3"))
+
+ def test_text2pcap_ipv6_sctp_data(self, run_text2pcap_ipv6):
+ '''Test SCTP DATA over IPv6'''
+ assert {'encapsulation': 'ether', 'packets': 1, \
+ 'datasize': 90, 'expert': '', \
+ 'ipv6': {'plen': '36', 'plen_tree': None}} == \
+ run_text2pcap_ipv6("0000: 01 00 03 03 00 00 00 08\n",
+ ("-S", "2905,2905,3"))
+
+
+class TestText2pcapIProto:
+ '''Test -i <proto> for IPv4 and IPv6'''
+ maxDiff = None
+
+ def test_text2pcap_i_icmp(self, run_text2pcap_capinfos_tshark):
+ '''Test -i <proto> without -4 or -6'''
+ assert {'encapsulation': 'ether', 'packets': 1, \
+ 'datasize': 98, 'expert': ''} == \
+ run_text2pcap_capinfos_tshark(
+ "0000 08 00 bb b3 d7 3b 00 00 51 a7 d6 7d 00 04 51 e4\n" +
+ "0010 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17\n" +
+ "0020 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27\n" +
+ "0030 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37\n",
+ ("-i", "1"))
+
+ def test_text2pcap_i_icmp_ipv4(self, run_text2pcap_capinfos_tshark):
+ '''Test -i <proto> with IPv4 (-4) header'''
+ assert {'encapsulation': 'ether', 'packets': 1, \
+ 'datasize': 98, 'expert': ''} == \
+ run_text2pcap_capinfos_tshark(
+ "0000 08 00 bb b3 d7 3b 00 00 51 a7 d6 7d 00 04 51 e4\n" +
+ "0010 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17\n" +
+ "0020 18 19 1a 1b 1c 1d 1e 1f 20 21 22 23 24 25 26 27\n" +
+ "0030 28 29 2a 2b 2c 2d 2e 2f 30 31 32 33 34 35 36 37\n",
+ ("-i", "1", "-4", "127.0.0.1,127.0.0.1"))
+
+ def test_text2pcap_i_icmpv6_ipv6(self, run_text2pcap_capinfos_tshark):
+ '''Test -i <proto> with IPv6 (-6) header'''
+ assert {'encapsulation': 'ether', 'packets': 1, \
+ 'datasize': 86, 'expert': ''} == \
+ run_text2pcap_capinfos_tshark(
+ "0000 87 00 f2 62 00 00 00 00 fe 80 00 00 00 00 00 00\n" +
+ "0010 00 00 00 00 00 00 00 02 01 01 52 54 00 12 34 56\n",
+ ("-i", "58", "-6", "::1,::1"))
+
+ def test_text2pcap_i_sctp_ipv6(self, run_text2pcap_capinfos_tshark):
+ '''Test -i <proto> with IPv6 (-6) header'''
+ assert {'encapsulation': 'ether', 'packets': 1, \
+ 'datasize': 90, 'expert': ''} == \
+ run_text2pcap_capinfos_tshark(
+ "0000 0b 59 0b 59 00 00 00 00 26 98 58 51 00 03 00 18\n" +
+ "0010 00 00 00 00 00 00 00 00 00 00 00 03 01 00 03 03\n" +
+ "0020 00 00 00 08\n",
+ ("-i", "132", "-6", "::1,::1"))
+
+
+class TestText2pcapOtherOptions:
+ '''Test other command line options'''
+ def test_text2pcap_option_N(self, cmd_text2pcap, cmd_tshark, capture_file, result_file, base_env):
+ '''Test -N <intf-name> option'''
+ testin_file = result_file(testin_txt)
+ testout_file = result_file(testout_pcapng)
+
+ with open(testin_file, 'w') as f:
+ f.write("0000 00\n")
+ f.close()
+ subprocess.check_call((cmd_text2pcap, "-F", "pcapng", "-N", "your-interface-name", testin_file, testout_file), env=base_env)
+ stdout = subprocess.check_output((cmd_tshark, "-r", testout_file, "-Tfields", "-eframe.interface_name", "-c1"), encoding='utf-8', env=base_env)
+ assert stdout.rstrip() == "your-interface-name"
diff --git a/test/suite_unittests.py b/test/suite_unittests.py
new file mode 100644
index 0000000..65abf9b
--- /dev/null
+++ b/test/suite_unittests.py
@@ -0,0 +1,105 @@
+#
+# Wireshark tests
+# By
+# Gerald Combs <gerald@wireshark.org>
+# Gilbert Ramirez <gram [AT] alumni.rice.edu>
+#
+# Ported from a set of Bash scripts which were copyright 2005 Ulf Lamping
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+'''EPAN unit tests'''
+
+import subprocess
+import pytest
+
+
+class TestUnitTests:
+ def test_unit_exntest(self, program, base_env):
+ '''exntest'''
+ subprocess.check_call(program('exntest'), env=base_env)
+
+ def test_unit_oids_test(self, program, base_env):
+ '''oids_test'''
+ subprocess.check_call(program('oids_test'), env=base_env)
+
+ def test_unit_reassemble_test(self, program, base_env):
+ '''reassemble_test'''
+ subprocess.check_call(program('reassemble_test'), env=base_env)
+
+ def test_unit_tvbtest(self, program, base_env):
+ '''tvbtest'''
+ subprocess.check_call(program('tvbtest'), env=base_env)
+
+ def test_unit_wmem_test(self, program, base_env):
+ '''wmem_test'''
+ subprocess.check_call((program('wmem_test'),
+ '--verbose'
+ ), env=base_env)
+
+ def test_unit_wscbor_test(self, program, base_env):
+ '''wscbor_test'''
+ subprocess.check_call(program('wscbor_test'), env=base_env)
+
+ def test_unit_epan(self, program, base_env):
+ '''epan unit tests'''
+ subprocess.check_call((program('test_epan'),
+ '--verbose'
+ ), env=base_env)
+
+ def test_unit_wsutil(self, program, base_env):
+ '''wsutil unit tests'''
+ subprocess.check_call((program('test_wsutil'),
+ '--verbose'
+ ), env=base_env)
+
+ def test_unit_fieldcount(self, cmd_tshark, test_env):
+ '''fieldcount'''
+ subprocess.check_call((cmd_tshark, '-G', 'fieldcount'), env=test_env)
+
+class Proto:
+ """Data for a protocol."""
+ def __init__(self, line):
+ data = line.split("\t")
+ assert len(data) == 3, "expected 3 columns in %s" % data
+ assert data[0] == "P"
+ self.name = data[1]
+ self.abbrev = data[2]
+
+class Field:
+ """Data for a field."""
+ def __init__(self, line):
+ data = line.split("\t")
+ assert len(data) == 8, "expected 8 columns in %s" % data
+ assert data[0] == "F"
+ self.name = data[1]
+ self.abbrev = data[2]
+ self.ftype = data[3]
+ self.parent = data[4]
+ self.base = data[5]
+ self.bitmask = int(data[6],0)
+ self.blurb = data[7]
+
+
+class TestUnitFtSanity:
+ def test_unit_ftsanity(self, cmd_tshark, base_env):
+ """Looks for problems in field type definitions."""
+ tshark_proc = subprocess.run((cmd_tshark, "-G", "fields"),
+ check=True, capture_output=True, encoding='utf-8', env=base_env)
+
+ lines = tshark_proc.stdout.splitlines()
+ # XXX We don't currently check protos.
+ protos = [Proto(x) for x in lines if x[0] == "P"]
+ fields = [Field(x) for x in lines if x[0] == "F"]
+
+ err_list = []
+ for field in fields:
+ if field.bitmask != 0:
+ if field.ftype.find("FT_UINT") != 0 and \
+ field.ftype.find("FT_INT") != 0 and \
+ field.ftype != "FT_BOOLEAN" and \
+ field.ftype != "FT_CHAR":
+ err_list.append("%s has a bitmask 0x%x but is type %s" % \
+ (field.abbrev, field.bitmask, field.ftype))
+
+ assert len(err_list) == 0, 'Found field type errors: \n' + '\n'.join(err_list)
diff --git a/test/suite_wslua.py b/test/suite_wslua.py
new file mode 100644
index 0000000..b63975c
--- /dev/null
+++ b/test/suite_wslua.py
@@ -0,0 +1,313 @@
+#
+# Wireshark tests
+# By Gerald Combs <gerald@wireshark.org>
+#
+# Ported from a set of Bash scripts which were copyright 2005 Ulf Lamping
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+'''Wireshark Lua scripting tests'''
+
+import sys
+import filecmp
+import os.path
+import shutil
+import subprocess
+import pytest
+import logging
+
+dhcp_pcap = 'dhcp.pcap'
+dns_port_pcap = 'dns_port.pcap'
+empty_pcap = 'empty.pcap'
+segmented_fpm_pcap = 'segmented_fpm.pcap'
+sip_pcapng = 'sip.pcapng'
+sipmsg_log = 'sipmsg.log'
+wpa_induction_pcap_gz = 'wpa-Induction.pcap.gz'
+
+
+@pytest.fixture
+def check_lua_script(cmd_tshark, features, dirs, capture_file, test_env):
+ if not features.have_lua:
+ pytest.skip('Test requires Lua scripting support.')
+ def check_lua_script_real(lua_script, cap_file, check_passed, *args):
+ tshark_cmd = [cmd_tshark,
+ '-r', capture_file(cap_file),
+ '-X', 'lua_script:' + os.path.join(dirs.lua_dir, lua_script)
+ ]
+ tshark_cmd += args
+ tshark_proc = subprocess.run(tshark_cmd, check=True, capture_output=True, encoding='utf-8', env=test_env)
+
+ if check_passed:
+ logging.info(tshark_proc.stdout)
+ logging.info(tshark_proc.stderr)
+ if not 'All tests passed!' in tshark_proc.stdout:
+ pytest.fail("Some test failed, check the logs (eg: pytest --lf --log-cli-level=info)")
+
+ return tshark_proc
+ return check_lua_script_real
+
+
+@pytest.fixture
+def check_lua_script_verify(check_lua_script, result_file):
+ def check_lua_script_verify_real(lua_script, cap_file, check_stage_1=False, heur_regmode=None):
+ # First run tshark with the dissector script.
+ if heur_regmode is None:
+ tshark_proc = check_lua_script(lua_script, cap_file, check_stage_1,
+ '-V'
+ )
+ else:
+ tshark_proc = check_lua_script(lua_script, cap_file, check_stage_1,
+ '-V',
+ '-X', 'lua_script1:heur_regmode={}'.format(heur_regmode)
+ )
+
+ # then dump tshark's output to a verification file.
+ verify_file = result_file('testin.txt')
+ with open(verify_file, 'w', newline='\n') as f:
+ f.write(tshark_proc.stdout)
+
+ # finally run tshark again with the verification script and the verification file.
+ if heur_regmode is None:
+ check_lua_script('verify_dissector.lua', empty_pcap, True,
+ '-X', 'lua_script1:verify_file=' + verify_file,
+ )
+ else:
+ check_lua_script('verify_dissector.lua', empty_pcap, True,
+ '-X', 'lua_script1:verify_file=' + verify_file,
+ '-X', 'lua_script1:no_heur',
+ )
+ return check_lua_script_verify_real
+
+
+class TestWslua:
+ def test_wslua_dir(self, check_lua_script):
+ '''wslua directory functions'''
+ check_lua_script('dir.lua', empty_pcap, True)
+
+ def test_wslua_util(self, check_lua_script):
+ '''wslua utility functions'''
+ check_lua_script('util.lua', empty_pcap, True)
+
+ # Mode_1, mode_2, and mode_3, and fpm were all under wslua_step_dissector_test
+ # in the Bash version.
+ def test_wslua_dissector_mode_1(self, check_lua_script_verify):
+ '''wslua dissector functions, mode 1'''
+ check_lua_script_verify('dissector.lua', dns_port_pcap)
+
+ def test_wslua_dissector_mode_2(self, check_lua_script_verify):
+ '''wslua dissector functions, mode 2'''
+ check_lua_script_verify('dissector.lua', dns_port_pcap, heur_regmode=2)
+
+ def test_wslua_dissector_mode_3(self, check_lua_script_verify):
+ '''wslua dissector functions, mode 3'''
+ check_lua_script_verify('dissector.lua', dns_port_pcap, heur_regmode=3)
+
+ def test_wslua_dissector_fpm(self, check_lua_script):
+ '''wslua dissector functions, fpm'''
+ tshark_fpm_tcp_proc = check_lua_script('dissectFPM.lua', segmented_fpm_pcap, False,
+ '-T', 'fields',
+ '-e', 'frame.number',
+ '-e', 'fpm',
+ '-e', 'fpm.version',
+ '-e', 'fpm.type',
+ '-e', 'fpm.length',
+ '-o', 'fpm.dissect_tcp:true'
+ )
+
+ tshark_fpm_no_tcp_proc = check_lua_script('dissectFPM.lua', segmented_fpm_pcap, False,
+ '-T', 'fields',
+ '-e', 'frame.number',
+ '-e', 'fpm',
+ '-e', 'fpm.version',
+ '-e', 'fpm.type',
+ '-e', 'fpm.length',
+ '-o', 'fpm.dissect_tcp:false'
+ )
+
+ assert tshark_fpm_tcp_proc.stdout == tshark_fpm_no_tcp_proc.stdout
+
+ def test_wslua_field(self, check_lua_script):
+ '''wslua fields'''
+ check_lua_script('field.lua', dhcp_pcap, True, '-q', '-c1')
+
+ # reader, writer, and acme_reader were all under wslua_step_file_test
+ # in the Bash version.
+ def test_wslua_file_reader(self, check_lua_script, cmd_tshark, capture_file, test_env):
+ '''wslua file reader'''
+ cap_file_1 = capture_file(dhcp_pcap)
+ cap_file_2 = capture_file(wpa_induction_pcap_gz)
+
+ # First run tshark with the pcap_file_reader script.
+ lua_proc_1 = check_lua_script('pcap_file.lua', cap_file_1, False)
+ lua_proc_2 = check_lua_script('pcap_file.lua', cap_file_2, False)
+ lua_out = lua_proc_1.stdout + lua_proc_2.stdout
+
+ # then run tshark again without the script
+ tshark_proc_1 = subprocess.run((cmd_tshark, '-r', cap_file_1), check=True, capture_output=True, encoding='utf-8', env=test_env)
+ tshark_proc_2 = subprocess.run((cmd_tshark, '-r', cap_file_2), check=True, capture_output=True, encoding='utf-8', env=test_env)
+ tshark_out = tshark_proc_1.stdout + tshark_proc_2.stdout
+
+ assert lua_out == tshark_out
+
+ def test_wslua_file_writer(self, check_lua_script, capture_file, result_file):
+ '''wslua file writer'''
+ cap_file_1 = capture_file(dhcp_pcap)
+ cap_file_2 = result_file('lua_writer.pcap')
+
+ # Generate a new capture file using the Lua writer.
+ check_lua_script('pcap_file.lua', cap_file_1, False,
+ '-w', cap_file_2,
+ '-F', 'lua_pcap2',
+ )
+ assert filecmp.cmp(cap_file_1, cap_file_2), cap_file_1 + ' differs from ' + cap_file_2
+
+ def test_wslua_file_acme_reader(self, check_lua_script, cmd_tshark, capture_file, result_file, test_env):
+ '''wslua acme file reader'''
+
+ cap_file = result_file('lua_acme_reader.pcap')
+ # Read an acme sipmsg.log using the acme Lua reader, writing it out as pcapng.
+ check_lua_script('acme_file.lua', sipmsg_log, False,
+ '-w', cap_file,
+ '-F', 'pcapng',
+ )
+
+ # Read lua_acme_reader.pcap and sip.pcapng and compare their verbose outputs.
+ tshark_proc_1 = subprocess.run((cmd_tshark,
+ '-r', cap_file,
+ '-V'
+ ), check=True, capture_output=True, encoding='utf-8', env=test_env)
+ tshark_proc_2 = subprocess.run((cmd_tshark,
+ '-r', capture_file(sip_pcapng),
+ '-V'
+ ), check=True, capture_output=True, encoding='utf-8', env=test_env)
+
+ assert tshark_proc_1.stdout == tshark_proc_2.stdout
+
+ def test_wslua_listener(self, check_lua_script):
+ '''wslua listener'''
+ check_lua_script('listener.lua', dhcp_pcap, True)
+
+ def test_wslua_nstime(self, check_lua_script):
+ '''wslua nstime'''
+ check_lua_script('nstime.lua', dhcp_pcap, True, '-q')
+
+ def test_wslua_pinfo(self, check_lua_script):
+ '''wslua pinfo'''
+ check_lua_script('pinfo.lua', dhcp_pcap, True)
+
+ def test_wslua_proto(self, check_lua_script):
+ '''wslua proto'''
+ check_lua_script('proto.lua', empty_pcap, True)
+
+ def test_wslua_byte_array(self, check_lua_script):
+ '''wslua byte_array'''
+ check_lua_script('byte_array.lua', empty_pcap, True)
+
+ def test_wslua_protofield_tree(self, check_lua_script):
+ '''wslua protofield with a tree'''
+ check_lua_script('protofield.lua', dns_port_pcap, True,
+ '-V',
+ '-Y', 'test.filtered==1',
+ )
+
+ def test_wslua_protofield_no_tree(self, check_lua_script):
+ '''wslua protofield without a tree'''
+ check_lua_script('protofield.lua', dns_port_pcap, True,
+ '-Y', 'test.filtered==1',
+ )
+
+ def test_wslua_int64(self, check_lua_script):
+ '''wslua int64'''
+ check_lua_script('int64.lua', empty_pcap, True)
+
+ def test_wslua_args_1(self, check_lua_script):
+ '''wslua args 1'''
+ check_lua_script('script_args.lua', empty_pcap, True,
+ '-X', 'lua_script1:1',
+ )
+
+ def test_wslua_args_2(self, check_lua_script):
+ '''wslua args 2'''
+ check_lua_script('script_args.lua', empty_pcap, True,
+ '-X', 'lua_script1:3',
+ '-X', 'lua_script1:foo',
+ '-X', 'lua_script1:bar',
+ )
+
+ def test_wslua_args_3(self, check_lua_script, dirs):
+ '''wslua args 3'''
+ check_lua_script('script_args.lua', empty_pcap, True,
+ '-X', 'lua_script:' + os.path.join(dirs.lua_dir, 'script_args.lua'),
+ '-X', 'lua_script1:3',
+ '-X', 'lua_script2:1',
+ '-X', 'lua_script1:foo',
+ '-X', 'lua_script1:bar',
+ )
+
+ def test_wslua_args_4(self, check_lua_script):
+ '''wslua args 4'''
+ tshark_proc = check_lua_script('script_args.lua', empty_pcap, False)
+ assert 'All tests passed!' not in tshark_proc.stdout
+
+ def test_wslua_args_5(self, check_lua_script):
+ '''wslua args 5'''
+ tshark_proc = check_lua_script('script_args.lua', empty_pcap, False,
+ '-X', 'lua_script1:3',
+ )
+ assert 'All tests passed!' not in tshark_proc.stdout
+
+ def test_wslua_globals(self, check_lua_script, dirs):
+ '''wslua globals'''
+ check_lua_script('verify_globals.lua', empty_pcap, True,
+ '-X', 'lua_script1:' + os.path.join(dirs.lua_dir, ''),
+ '-X', 'lua_script1:' + os.path.join(dirs.lua_dir, 'globals_2.2.txt'),
+ )
+
+ def test_wslua_struct(self, check_lua_script):
+ '''wslua struct'''
+ check_lua_script('struct.lua', empty_pcap, True)
+
+ def test_wslua_tvb_tree(self, check_lua_script):
+ '''wslua tvb with a tree'''
+ check_lua_script('tvb.lua', dns_port_pcap, True, '-c1', '-V')
+
+ def test_wslua_tvb_no_tree(self, check_lua_script):
+ '''wslua tvb without a tree'''
+ check_lua_script('tvb.lua', dns_port_pcap, True, '-c1')
+
+ def test_wslua_try_heuristics(self, check_lua_script):
+ '''wslua try_heuristics'''
+ check_lua_script('try_heuristics.lua', dns_port_pcap, True)
+
+ def test_wslua_add_packet_field(self, check_lua_script):
+ '''wslua add_packet_field'''
+ check_lua_script('add_packet_field.lua', dns_port_pcap, True)
+
+class TestWsluaUnicode:
+ def test_wslua_unicode(self, cmd_tshark, features, dirs, capture_file, unicode_env):
+ '''Check handling of unicode paths.'''
+ if not features.have_lua:
+ pytest.skip('Test requires Lua scripting support.')
+ if sys.platform == 'win32' and not features.have_lua_unicode:
+ pytest.skip('Test requires a patched Lua build with UTF-8 support.')
+
+ # Prepare test environment, put files in the right places.
+ uni_script = os.path.join(unicode_env.pluginsdir, 'script-Ф-€-中.lua')
+ shutil.copy(os.path.join(dirs.lua_dir, 'unicode.lua'), uni_script)
+ with open(unicode_env.path('load-Ф-€-中.lua'), 'w', encoding='utf8') as f:
+ f.write('return "Contents of Ф-€-中"\n')
+ uni_pcap = unicode_env.path('file-Ф-€-中.pcap')
+ shutil.copy(capture_file('empty.pcap'), uni_pcap)
+
+ # Run process from a Unicode path as working directory.
+ proc = subprocess.Popen((cmd_tshark, '-r', uni_pcap), env=unicode_env.env,
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ cwd=unicode_env.path())
+ stdout, stderr = proc.communicate(timeout=60)
+ stdout_str = stdout.decode('utf8', 'replace')
+ stderr_str = stderr.decode('utf8', 'replace')
+ assert 'All tests passed!' in stdout_str
+ assert stderr_str == ""
+ with open(unicode_env.path('written-by-lua-Ф-€-中.txt'), encoding='utf8') as f:
+ assert f.read() == 'Feedback from Lua: Ф-€-中\n'
diff --git a/test/travis-upload-artifacts.sh b/test/travis-upload-artifacts.sh
new file mode 100755
index 0000000..6f96b11
--- /dev/null
+++ b/test/travis-upload-artifacts.sh
@@ -0,0 +1,34 @@
+#!/bin/bash
+# Publishes artifacts from a Travis CI build.
+#
+# Copyright (C) 2019 Peter Wu <peter@lekensteyn.nl>
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Currently it dumps a base64-encoded xz-compressed tarball as Travis CI
+# does not have a nice way to publish artifacts (like Gitlab does).
+#
+
+shopt -s nullglob
+files=(*screenshot.png)
+
+if [ ${#files[@]} -eq 0 ]; then
+ echo "No artifacts found"
+ exit
+fi
+
+output=travis.tar.xz
+tar -cJvf "$output" "${files[@]}"
+
+# Print some details for an integrity check.
+ls -l "$output"
+openssl dgst -sha256 "$output"
+
+# Upload to other services just in case the log output is corrupted.
+curl -F 'f:1=<-' ix.io < "$output"
+
+# Dump the contents to the log (note: Travis has a 4MiB limit)
+cat <<EOF
+base64 -d > $output <<ARTIFACTS_BASE64
+$(base64 < "$output" | tr -d '\n' | fold -w200)
+ARTIFACTS_BASE64
+EOF
diff --git a/test/util_dump_dhcp_pcap.py b/test/util_dump_dhcp_pcap.py
new file mode 100755
index 0000000..28b3823
--- /dev/null
+++ b/test/util_dump_dhcp_pcap.py
@@ -0,0 +1,45 @@
+#!/usr/bin/env python3
+#
+# Wireshark tests
+# By Gerald Combs <gerald@wireshark.org>
+#
+# Ported from a set of Bash scripts which were copyright 2005 Ulf Lamping
+#
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+'''Write captures/dhcp.pcap to stdout, optionally writing only packet records or writing them slowly.'''
+
+import argparse
+import os
+import os.path
+import time
+import sys
+
+def main():
+ parser = argparse.ArgumentParser(description='Dump dhcp.pcap')
+ parser.add_argument('dump_type', choices=['cat', 'cat100', 'slow', 'raw'],
+ help='cat: Just dump the file. cat100: Dump 100 packet records. slow: Dump the file, pause, and dump its packet records. raw: Dump only the packet records.')
+ args = parser.parse_args()
+
+ dhcp_pcap = os.path.join(os.path.dirname(__file__), 'captures', 'dhcp.pcap')
+
+ dhcp_fd = open(dhcp_pcap, 'rb')
+ contents = dhcp_fd.read()
+ if args.dump_type != 'raw':
+ os.write(1, contents)
+ if args.dump_type == 'cat100':
+ # The capture contains 4 packets. Write 96 more.
+ for _ in range(24):
+ os.write(1, contents[24:])
+ if args.dump_type.startswith('cat'):
+ sys.exit(0)
+ if args.dump_type == 'slow':
+ time.sleep(1.5)
+ # slow, raw
+ os.write(1, contents[24:])
+
+ sys.exit(0)
+
+if __name__ == '__main__':
+ main()
+