diff options
Diffstat (limited to 'test')
273 files changed, 31324 insertions, 0 deletions
diff --git a/test/README.test b/test/README.test new file mode 100644 index 00000000..a8ec1cfe --- /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 00000000..0b79470b --- /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 00000000..a5caa808 --- /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 00000000..49d0a197 --- /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 00000000..df063df4 --- /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 00000000..56066182 --- /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 00000000..4e61bf8a --- /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 00000000..69a75262 --- /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 00000000..d1a85d25 --- /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 00000000..f4ac417f --- /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 00000000..c6fd0208 --- /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 Binary files differnew file mode 100644 index 00000000..f9fca0c1 --- /dev/null +++ b/test/captures/arp.pcap diff --git a/test/captures/c1222_std_example8.pcap b/test/captures/c1222_std_example8.pcap Binary files differnew file mode 100644 index 00000000..ad517f04 --- /dev/null +++ b/test/captures/c1222_std_example8.pcap diff --git a/test/captures/challenge01_ooo_stream.pcapng.gz b/test/captures/challenge01_ooo_stream.pcapng.gz Binary files differnew file mode 100644 index 00000000..aa96da0d --- /dev/null +++ b/test/captures/challenge01_ooo_stream.pcapng.gz diff --git a/test/captures/communityid.pcap.gz b/test/captures/communityid.pcap.gz Binary files differnew file mode 100644 index 00000000..a7ecc4eb --- /dev/null +++ b/test/captures/communityid.pcap.gz diff --git a/test/captures/cose_encrypt0_tagged.cbordiag b/test/captures/cose_encrypt0_tagged.cbordiag new file mode 100644 index 00000000..36b9d62e --- /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 Binary files differnew file mode 100644 index 00000000..51d1cbf6 --- /dev/null +++ b/test/captures/cose_encrypt0_tagged.pcap diff --git a/test/captures/cose_encrypt_tagged.cbordiag b/test/captures/cose_encrypt_tagged.cbordiag new file mode 100644 index 00000000..30767f9d --- /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 Binary files differnew file mode 100644 index 00000000..71f5ad8c --- /dev/null +++ b/test/captures/cose_encrypt_tagged.pcap diff --git a/test/captures/cose_keyset.cbordiag b/test/captures/cose_keyset.cbordiag new file mode 100644 index 00000000..11b1df36 --- /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 Binary files differnew file mode 100644 index 00000000..5b98d31a --- /dev/null +++ b/test/captures/cose_keyset.pcap diff --git a/test/captures/cose_mac0_tagged.cbordiag b/test/captures/cose_mac0_tagged.cbordiag new file mode 100644 index 00000000..ead2910d --- /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 Binary files differnew file mode 100644 index 00000000..0454b19c --- /dev/null +++ b/test/captures/cose_mac0_tagged.pcap diff --git a/test/captures/cose_mac_tagged.cbordiag b/test/captures/cose_mac_tagged.cbordiag new file mode 100644 index 00000000..91d9b89b --- /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 Binary files differnew file mode 100644 index 00000000..3c685b29 --- /dev/null +++ b/test/captures/cose_mac_tagged.pcap diff --git a/test/captures/cose_sign1_tagged.cbordiag b/test/captures/cose_sign1_tagged.cbordiag new file mode 100644 index 00000000..64063320 --- /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 Binary files differnew file mode 100644 index 00000000..f3c84401 --- /dev/null +++ b/test/captures/cose_sign1_tagged.pcap diff --git a/test/captures/cose_sign_tagged.cbordiag b/test/captures/cose_sign_tagged.cbordiag new file mode 100644 index 00000000..92003a4c --- /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 Binary files differnew file mode 100644 index 00000000..428acb9a --- /dev/null +++ b/test/captures/cose_sign_tagged.pcap diff --git a/test/captures/data-utf8.pcap b/test/captures/data-utf8.pcap Binary files differnew file mode 100644 index 00000000..286acab9 --- /dev/null +++ b/test/captures/data-utf8.pcap diff --git a/test/captures/dhcp-nanosecond.pcap b/test/captures/dhcp-nanosecond.pcap Binary files differnew file mode 100644 index 00000000..7c932068 --- /dev/null +++ b/test/captures/dhcp-nanosecond.pcap diff --git a/test/captures/dhcp-nanosecond.pcapng b/test/captures/dhcp-nanosecond.pcapng Binary files differnew file mode 100644 index 00000000..3a65e4ef --- /dev/null +++ b/test/captures/dhcp-nanosecond.pcapng diff --git a/test/captures/dhcp.pcap b/test/captures/dhcp.pcap Binary files differnew file mode 100644 index 00000000..a42d6102 --- /dev/null +++ b/test/captures/dhcp.pcap diff --git a/test/captures/dhcp.pcapng b/test/captures/dhcp.pcapng Binary files differnew file mode 100644 index 00000000..530c64ce --- /dev/null +++ b/test/captures/dhcp.pcapng diff --git a/test/captures/dhe1.pcapng.gz b/test/captures/dhe1.pcapng.gz Binary files differnew file mode 100644 index 00000000..38112576 --- /dev/null +++ b/test/captures/dhe1.pcapng.gz diff --git a/test/captures/dmgr.pcapng b/test/captures/dmgr.pcapng Binary files differnew file mode 100644 index 00000000..10eb29d4 --- /dev/null +++ b/test/captures/dmgr.pcapng diff --git a/test/captures/dns+icmp.pcapng.gz b/test/captures/dns+icmp.pcapng.gz Binary files differnew file mode 100644 index 00000000..ddd09fa1 --- /dev/null +++ b/test/captures/dns+icmp.pcapng.gz diff --git a/test/captures/dns-mdns.pcap b/test/captures/dns-mdns.pcap Binary files differnew file mode 100644 index 00000000..7410b1e7 --- /dev/null +++ b/test/captures/dns-mdns.pcap diff --git a/test/captures/dns-ooo.pcap b/test/captures/dns-ooo.pcap Binary files differnew file mode 100644 index 00000000..594b925f --- /dev/null +++ b/test/captures/dns-ooo.pcap diff --git a/test/captures/dns_port.pcap b/test/captures/dns_port.pcap Binary files differnew file mode 100644 index 00000000..6974505a --- /dev/null +++ b/test/captures/dns_port.pcap diff --git a/test/captures/dtls12-aes128ccm8-dsb.pcapng b/test/captures/dtls12-aes128ccm8-dsb.pcapng Binary files differnew file mode 100644 index 00000000..9a9bf4e5 --- /dev/null +++ b/test/captures/dtls12-aes128ccm8-dsb.pcapng diff --git a/test/captures/dtls12-aes128ccm8.pcap b/test/captures/dtls12-aes128ccm8.pcap Binary files differnew file mode 100644 index 00000000..3e293c14 --- /dev/null +++ b/test/captures/dtls12-aes128ccm8.pcap diff --git a/test/captures/dtn_tcpclv3_bpv6_transfer.pcapng b/test/captures/dtn_tcpclv3_bpv6_transfer.pcapng Binary files differnew file mode 100644 index 00000000..d8e07646 --- /dev/null +++ b/test/captures/dtn_tcpclv3_bpv6_transfer.pcapng diff --git a/test/captures/dtn_tcpclv4_bpv7_transfer.pcapng b/test/captures/dtn_tcpclv4_bpv7_transfer.pcapng Binary files differnew file mode 100644 index 00000000..4bdf152f --- /dev/null +++ b/test/captures/dtn_tcpclv4_bpv7_transfer.pcapng 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 00000000..4b55f652 --- /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 Binary files differnew file mode 100644 index 00000000..1625c19c --- /dev/null +++ b/test/captures/dtn_udpcl_bpv7_bpsec_bcb_admin.pcapng 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 00000000..fdb4ade2 --- /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 Binary files differnew file mode 100644 index 00000000..245fecaf --- /dev/null +++ b/test/captures/dtn_udpcl_bpv7_bpsec_bib_admin.pcapng diff --git a/test/captures/dvb-ci_UV1_0000.pcap b/test/captures/dvb-ci_UV1_0000.pcap Binary files differnew file mode 100644 index 00000000..aa192ca0 --- /dev/null +++ b/test/captures/dvb-ci_UV1_0000.pcap diff --git a/test/captures/empty.pcap b/test/captures/empty.pcap Binary files differnew file mode 100644 index 00000000..a3243045 --- /dev/null +++ b/test/captures/empty.pcap diff --git a/test/captures/esp-bug-12671.pcapng.gz b/test/captures/esp-bug-12671.pcapng.gz Binary files differnew file mode 100644 index 00000000..a31e8c54 --- /dev/null +++ b/test/captures/esp-bug-12671.pcapng.gz diff --git a/test/captures/gitOverTCP.pcap b/test/captures/gitOverTCP.pcap Binary files differnew file mode 100644 index 00000000..e2c33f46 --- /dev/null +++ b/test/captures/gitOverTCP.pcap diff --git a/test/captures/grpc_person_search_json_with_image.pcapng.gz b/test/captures/grpc_person_search_json_with_image.pcapng.gz Binary files differnew file mode 100644 index 00000000..4de8621c --- /dev/null +++ b/test/captures/grpc_person_search_json_with_image.pcapng.gz 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 Binary files differnew file mode 100644 index 00000000..c128bb37 --- /dev/null +++ b/test/captures/grpc_person_search_protobuf_with_image-missing_headers.pcapng.gz diff --git a/test/captures/grpc_person_search_protobuf_with_image.pcapng.gz b/test/captures/grpc_person_search_protobuf_with_image.pcapng.gz Binary files differnew file mode 100644 index 00000000..076c7bc6 --- /dev/null +++ b/test/captures/grpc_person_search_protobuf_with_image.pcapng.gz diff --git a/test/captures/grpc_stream_reassembly_sample.pcapng.gz b/test/captures/grpc_stream_reassembly_sample.pcapng.gz Binary files differnew file mode 100644 index 00000000..f5e22e0b --- /dev/null +++ b/test/captures/grpc_stream_reassembly_sample.pcapng.gz diff --git a/test/captures/grpc_web.pcapng.gz b/test/captures/grpc_web.pcapng.gz Binary files differnew file mode 100644 index 00000000..5d527147 --- /dev/null +++ b/test/captures/grpc_web.pcapng.gz diff --git a/test/captures/http-brotli.pcapng b/test/captures/http-brotli.pcapng Binary files differnew file mode 100644 index 00000000..5d7a499d --- /dev/null +++ b/test/captures/http-brotli.pcapng diff --git a/test/captures/http-ooo-fuzzed.pcapng b/test/captures/http-ooo-fuzzed.pcapng Binary files differnew file mode 100644 index 00000000..7cb871c3 --- /dev/null +++ b/test/captures/http-ooo-fuzzed.pcapng diff --git a/test/captures/http-ooo.pcap b/test/captures/http-ooo.pcap Binary files differnew file mode 100644 index 00000000..be0b5d2f --- /dev/null +++ b/test/captures/http-ooo.pcap diff --git a/test/captures/http-ooo2.pcap b/test/captures/http-ooo2.pcap Binary files differnew file mode 100644 index 00000000..8cab45c9 --- /dev/null +++ b/test/captures/http-ooo2.pcap diff --git a/test/captures/http.pcap b/test/captures/http.pcap Binary files differnew file mode 100644 index 00000000..145c2b05 --- /dev/null +++ b/test/captures/http.pcap diff --git a/test/captures/http2-brotli.pcapng b/test/captures/http2-brotli.pcapng Binary files differnew file mode 100644 index 00000000..7e07e857 --- /dev/null +++ b/test/captures/http2-brotli.pcapng diff --git a/test/captures/http2-data-reassembly.pcap b/test/captures/http2-data-reassembly.pcap Binary files differnew file mode 100644 index 00000000..b0f15ee9 --- /dev/null +++ b/test/captures/http2-data-reassembly.pcap diff --git a/test/captures/http2_follow_multistream.pcapng b/test/captures/http2_follow_multistream.pcapng Binary files differnew file mode 100644 index 00000000..afba90e3 --- /dev/null +++ b/test/captures/http2_follow_multistream.pcapng diff --git a/test/captures/http3-qpack-reassembly-anon.pcapng b/test/captures/http3-qpack-reassembly-anon.pcapng Binary files differnew file mode 100644 index 00000000..4a7e02d2 --- /dev/null +++ b/test/captures/http3-qpack-reassembly-anon.pcapng diff --git a/test/captures/icmp.pcapng.gz b/test/captures/icmp.pcapng.gz Binary files differnew file mode 100644 index 00000000..653b9e5f --- /dev/null +++ b/test/captures/icmp.pcapng.gz diff --git a/test/captures/ieee802.3cb-ping.pcapng.gz b/test/captures/ieee802.3cb-ping.pcapng.gz Binary files differnew file mode 100644 index 00000000..71b4beb3 --- /dev/null +++ b/test/captures/ieee802.3cb-ping.pcapng.gz diff --git a/test/captures/ikev1-bug-12610.pcapng.gz b/test/captures/ikev1-bug-12610.pcapng.gz Binary files differnew file mode 100644 index 00000000..b4d6be6c --- /dev/null +++ b/test/captures/ikev1-bug-12610.pcapng.gz diff --git a/test/captures/ikev1-bug-12620.pcapng.gz b/test/captures/ikev1-bug-12620.pcapng.gz Binary files differnew file mode 100644 index 00000000..14724fcd --- /dev/null +++ b/test/captures/ikev1-bug-12620.pcapng.gz diff --git a/test/captures/ikev1-certs.pcap b/test/captures/ikev1-certs.pcap Binary files differnew file mode 100644 index 00000000..6922aa37 --- /dev/null +++ b/test/captures/ikev1-certs.pcap diff --git a/test/captures/ikev2-decrypt-3des-sha1_160.pcap b/test/captures/ikev2-decrypt-3des-sha1_160.pcap Binary files differnew file mode 100644 index 00000000..ffdc7b51 --- /dev/null +++ b/test/captures/ikev2-decrypt-3des-sha1_160.pcap diff --git a/test/captures/ikev2-decrypt-aes128ccm12-2.pcap b/test/captures/ikev2-decrypt-aes128ccm12-2.pcap Binary files differnew file mode 100644 index 00000000..5ffecbee --- /dev/null +++ b/test/captures/ikev2-decrypt-aes128ccm12-2.pcap diff --git a/test/captures/ikev2-decrypt-aes128ccm12.pcap b/test/captures/ikev2-decrypt-aes128ccm12.pcap Binary files differnew file mode 100644 index 00000000..66dabfe6 --- /dev/null +++ b/test/captures/ikev2-decrypt-aes128ccm12.pcap diff --git a/test/captures/ikev2-decrypt-aes192ctr.pcap b/test/captures/ikev2-decrypt-aes192ctr.pcap Binary files differnew file mode 100644 index 00000000..31f16cb9 --- /dev/null +++ b/test/captures/ikev2-decrypt-aes192ctr.pcap diff --git a/test/captures/ikev2-decrypt-aes256cbc.pcapng b/test/captures/ikev2-decrypt-aes256cbc.pcapng Binary files differnew file mode 100644 index 00000000..ce3d247c --- /dev/null +++ b/test/captures/ikev2-decrypt-aes256cbc.pcapng diff --git a/test/captures/ikev2-decrypt-aes256ccm16.pcapng b/test/captures/ikev2-decrypt-aes256ccm16.pcapng Binary files differnew file mode 100644 index 00000000..78874618 --- /dev/null +++ b/test/captures/ikev2-decrypt-aes256ccm16.pcapng diff --git a/test/captures/ikev2-decrypt-aes256gcm16.pcap b/test/captures/ikev2-decrypt-aes256gcm16.pcap Binary files differnew file mode 100644 index 00000000..1e77424e --- /dev/null +++ b/test/captures/ikev2-decrypt-aes256gcm16.pcap diff --git a/test/captures/ikev2-decrypt-aes256gcm8.pcap b/test/captures/ikev2-decrypt-aes256gcm8.pcap Binary files differnew file mode 100644 index 00000000..a0d74de8 --- /dev/null +++ b/test/captures/ikev2-decrypt-aes256gcm8.pcap diff --git a/test/captures/ipoipoip.pcap b/test/captures/ipoipoip.pcap Binary files differnew file mode 100644 index 00000000..4c15e1a2 --- /dev/null +++ b/test/captures/ipoipoip.pcap diff --git a/test/captures/ipv6.pcap b/test/captures/ipv6.pcap Binary files differnew file mode 100644 index 00000000..bdfb72ce --- /dev/null +++ b/test/captures/ipv6.pcap diff --git a/test/captures/ipx_rip.pcap b/test/captures/ipx_rip.pcap Binary files differnew file mode 100644 index 00000000..89533cb1 --- /dev/null +++ b/test/captures/ipx_rip.pcap diff --git a/test/captures/knxip_DataSec.pcap b/test/captures/knxip_DataSec.pcap Binary files differnew file mode 100644 index 00000000..7e28cd40 --- /dev/null +++ b/test/captures/knxip_DataSec.pcap diff --git a/test/captures/knxip_SecureWrapper.pcap b/test/captures/knxip_SecureWrapper.pcap Binary files differnew file mode 100644 index 00000000..0be64d8c --- /dev/null +++ b/test/captures/knxip_SecureWrapper.pcap diff --git a/test/captures/knxip_TimerNotify.pcap b/test/captures/knxip_TimerNotify.pcap Binary files differnew file mode 100644 index 00000000..4e7ae378 --- /dev/null +++ b/test/captures/knxip_TimerNotify.pcap diff --git a/test/captures/krb-816.pcap.gz b/test/captures/krb-816.pcap.gz Binary files differnew file mode 100644 index 00000000..59002a62 --- /dev/null +++ b/test/captures/krb-816.pcap.gz diff --git a/test/captures/logistics_multicast.pcapng b/test/captures/logistics_multicast.pcapng Binary files differnew file mode 100644 index 00000000..10684805 --- /dev/null +++ b/test/captures/logistics_multicast.pcapng diff --git a/test/captures/many_interfaces.pcapng.1 b/test/captures/many_interfaces.pcapng.1 Binary files differnew file mode 100644 index 00000000..960e35d9 --- /dev/null +++ b/test/captures/many_interfaces.pcapng.1 diff --git a/test/captures/many_interfaces.pcapng.2 b/test/captures/many_interfaces.pcapng.2 Binary files differnew file mode 100644 index 00000000..7056f46c --- /dev/null +++ b/test/captures/many_interfaces.pcapng.2 diff --git a/test/captures/many_interfaces.pcapng.3 b/test/captures/many_interfaces.pcapng.3 Binary files differnew file mode 100644 index 00000000..48367c0a --- /dev/null +++ b/test/captures/many_interfaces.pcapng.3 diff --git a/test/captures/mongo-zstd.pcapng b/test/captures/mongo-zstd.pcapng Binary files differnew file mode 100644 index 00000000..565815ff --- /dev/null +++ b/test/captures/mongo-zstd.pcapng diff --git a/test/captures/netperfmeter.pcapng.gz b/test/captures/netperfmeter.pcapng.gz Binary files differnew file mode 100644 index 00000000..b62386a3 --- /dev/null +++ b/test/captures/netperfmeter.pcapng.gz diff --git a/test/captures/nfs.pcap b/test/captures/nfs.pcap Binary files differnew file mode 100644 index 00000000..e71d80be --- /dev/null +++ b/test/captures/nfs.pcap diff --git a/test/captures/ntp.pcap b/test/captures/ntp.pcap Binary files differnew file mode 100644 index 00000000..815ed719 --- /dev/null +++ b/test/captures/ntp.pcap diff --git a/test/captures/owe.pcapng.gz b/test/captures/owe.pcapng.gz Binary files differnew file mode 100644 index 00000000..930d6bc5 --- /dev/null +++ b/test/captures/owe.pcapng.gz diff --git a/test/captures/packet-h2-14_headers.pcapng b/test/captures/packet-h2-14_headers.pcapng Binary files differnew file mode 100644 index 00000000..5f4f0e5d --- /dev/null +++ b/test/captures/packet-h2-14_headers.pcapng diff --git a/test/captures/protobuf_tcp_addressbook.pcapng.gz b/test/captures/protobuf_tcp_addressbook.pcapng.gz Binary files differnew file mode 100644 index 00000000..7c985c3a --- /dev/null +++ b/test/captures/protobuf_tcp_addressbook.pcapng.gz diff --git a/test/captures/protobuf_test_default_value.pcapng b/test/captures/protobuf_test_default_value.pcapng Binary files differnew file mode 100644 index 00000000..37144ee7 --- /dev/null +++ b/test/captures/protobuf_test_default_value.pcapng diff --git a/test/captures/protobuf_test_leading_dot.pcapng b/test/captures/protobuf_test_leading_dot.pcapng Binary files differnew file mode 100644 index 00000000..1ec0dcc7 --- /dev/null +++ b/test/captures/protobuf_test_leading_dot.pcapng diff --git a/test/captures/protobuf_test_map_and_oneof_types.pcapng b/test/captures/protobuf_test_map_and_oneof_types.pcapng Binary files differnew file mode 100644 index 00000000..fa1b298b --- /dev/null +++ b/test/captures/protobuf_test_map_and_oneof_types.pcapng diff --git a/test/captures/protobuf_udp_addressbook_with_image_ts.pcapng b/test/captures/protobuf_udp_addressbook_with_image_ts.pcapng Binary files differnew file mode 100644 index 00000000..d6cc8645 --- /dev/null +++ b/test/captures/protobuf_udp_addressbook_with_image_ts.pcapng diff --git a/test/captures/protohier-with-comments.pcapng b/test/captures/protohier-with-comments.pcapng Binary files differnew file mode 100644 index 00000000..4a4d78e4 --- /dev/null +++ b/test/captures/protohier-with-comments.pcapng diff --git a/test/captures/protohier-without-comments.pcapng b/test/captures/protohier-without-comments.pcapng Binary files differnew file mode 100644 index 00000000..942e9202 --- /dev/null +++ b/test/captures/protohier-without-comments.pcapng diff --git a/test/captures/quic-fragmented-handshakes.pcapng.gz b/test/captures/quic-fragmented-handshakes.pcapng.gz Binary files differnew file mode 100644 index 00000000..f029a46f --- /dev/null +++ b/test/captures/quic-fragmented-handshakes.pcapng.gz diff --git a/test/captures/quic_follow_multistream.pcapng b/test/captures/quic_follow_multistream.pcapng Binary files differnew file mode 100644 index 00000000..10d98b5d --- /dev/null +++ b/test/captures/quic_follow_multistream.pcapng diff --git a/test/captures/retrans-tls.pcap b/test/captures/retrans-tls.pcap Binary files differnew file mode 100644 index 00000000..90be0de7 --- /dev/null +++ b/test/captures/retrans-tls.pcap diff --git a/test/captures/rsa-p-lt-q.pcap b/test/captures/rsa-p-lt-q.pcap Binary files differnew file mode 100644 index 00000000..2d2a33ff --- /dev/null +++ b/test/captures/rsa-p-lt-q.pcap diff --git a/test/captures/rsasnakeoil2.pcap b/test/captures/rsasnakeoil2.pcap Binary files differnew file mode 100644 index 00000000..a1c6bd4f --- /dev/null +++ b/test/captures/rsasnakeoil2.pcap diff --git a/test/captures/s7comm-fuzz.pcapng.gz b/test/captures/s7comm-fuzz.pcapng.gz Binary files differnew file mode 100644 index 00000000..5eaa4251 --- /dev/null +++ b/test/captures/s7comm-fuzz.pcapng.gz diff --git a/test/captures/sample_control4_2012-03-24.pcap b/test/captures/sample_control4_2012-03-24.pcap Binary files differnew file mode 100644 index 00000000..cd33b1cd --- /dev/null +++ b/test/captures/sample_control4_2012-03-24.pcap diff --git a/test/captures/segmented_fpm.pcap b/test/captures/segmented_fpm.pcap Binary files differnew file mode 100644 index 00000000..f86e1038 --- /dev/null +++ b/test/captures/segmented_fpm.pcap diff --git a/test/captures/sip-rtp.pcapng b/test/captures/sip-rtp.pcapng Binary files differnew file mode 100644 index 00000000..5ea96eef --- /dev/null +++ b/test/captures/sip-rtp.pcapng diff --git a/test/captures/sip.pcapng b/test/captures/sip.pcapng Binary files differnew file mode 100644 index 00000000..7d1f6f29 --- /dev/null +++ b/test/captures/sip.pcapng diff --git a/test/captures/sipmsg.log b/test/captures/sipmsg.log new file mode 100644 index 00000000..d69c99c0 --- /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 Binary files differnew file mode 100644 index 00000000..6f0c8d76 --- /dev/null +++ b/test/captures/smb300-aes-128-ccm.pcap.gz diff --git a/test/captures/smb311-aes-128-ccm.pcap.gz b/test/captures/smb311-aes-128-ccm.pcap.gz Binary files differnew file mode 100644 index 00000000..fa4e1969 --- /dev/null +++ b/test/captures/smb311-aes-128-ccm.pcap.gz diff --git a/test/captures/smb311-aes-128-gcm.pcap.gz b/test/captures/smb311-aes-128-gcm.pcap.gz Binary files differnew file mode 100644 index 00000000..02e34a90 --- /dev/null +++ b/test/captures/smb311-aes-128-gcm.pcap.gz diff --git a/test/captures/smb311-aes-256-ccm.pcap.gz b/test/captures/smb311-aes-256-ccm.pcap.gz Binary files differnew file mode 100644 index 00000000..cfc71eb1 --- /dev/null +++ b/test/captures/smb311-aes-256-ccm.pcap.gz diff --git a/test/captures/smb311-aes-256-gcm.pcap.gz b/test/captures/smb311-aes-256-gcm.pcap.gz Binary files differnew file mode 100644 index 00000000..c7e1b1a3 --- /dev/null +++ b/test/captures/smb311-aes-256-gcm.pcap.gz diff --git a/test/captures/smb311-chained-patternv1-lznt1.pcapng.gz b/test/captures/smb311-chained-patternv1-lznt1.pcapng.gz Binary files differnew file mode 100644 index 00000000..92e5a044 --- /dev/null +++ b/test/captures/smb311-chained-patternv1-lznt1.pcapng.gz diff --git a/test/captures/smb311-lz77-lz77huff-lznt1.pcap.gz b/test/captures/smb311-lz77-lz77huff-lznt1.pcap.gz Binary files differnew file mode 100644 index 00000000..f14bfbaf --- /dev/null +++ b/test/captures/smb311-lz77-lz77huff-lznt1.pcap.gz diff --git a/test/captures/snakeoil-dtls.pcap b/test/captures/snakeoil-dtls.pcap Binary files differnew file mode 100644 index 00000000..ef5fd211 --- /dev/null +++ b/test/captures/snakeoil-dtls.pcap diff --git a/test/captures/tcp-badsegments.pcap b/test/captures/tcp-badsegments.pcap Binary files differnew file mode 100644 index 00000000..a48b9164 --- /dev/null +++ b/test/captures/tcp-badsegments.pcap diff --git a/test/captures/tcp-exp-option-tarr.pcap.gz b/test/captures/tcp-exp-option-tarr.pcap.gz Binary files differnew file mode 100644 index 00000000..12f5ec36 --- /dev/null +++ b/test/captures/tcp-exp-option-tarr.pcap.gz diff --git a/test/captures/text2pcap_hash_eol.txt b/test/captures/text2pcap_hash_eol.txt new file mode 100644 index 00000000..b7c6d2f6 --- /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 Binary files differnew file mode 100644 index 00000000..f88c22a0 --- /dev/null +++ b/test/captures/tftp.pcap diff --git a/test/captures/tls-fragmented-handshakes.pcap.gz b/test/captures/tls-fragmented-handshakes.pcap.gz Binary files differnew file mode 100644 index 00000000..9f97664d --- /dev/null +++ b/test/captures/tls-fragmented-handshakes.pcap.gz diff --git a/test/captures/tls-fragmented-over-tcp-segmented.pcapng.gz b/test/captures/tls-fragmented-over-tcp-segmented.pcapng.gz Binary files differnew file mode 100644 index 00000000..ed9e96fb --- /dev/null +++ b/test/captures/tls-fragmented-over-tcp-segmented.pcapng.gz diff --git a/test/captures/tls-over-tls.pcapng.gz b/test/captures/tls-over-tls.pcapng.gz Binary files differnew file mode 100644 index 00000000..1192bc69 --- /dev/null +++ b/test/captures/tls-over-tls.pcapng.gz diff --git a/test/captures/tls-renegotiation.pcap b/test/captures/tls-renegotiation.pcap Binary files differnew file mode 100644 index 00000000..7d772a58 --- /dev/null +++ b/test/captures/tls-renegotiation.pcap diff --git a/test/captures/tls12-aes128ccm.pcap b/test/captures/tls12-aes128ccm.pcap Binary files differnew file mode 100644 index 00000000..6f032a70 --- /dev/null +++ b/test/captures/tls12-aes128ccm.pcap diff --git a/test/captures/tls12-aes256gcm.pcap b/test/captures/tls12-aes256gcm.pcap Binary files differnew file mode 100644 index 00000000..576739c8 --- /dev/null +++ b/test/captures/tls12-aes256gcm.pcap diff --git a/test/captures/tls12-chacha20poly1305.pcap b/test/captures/tls12-chacha20poly1305.pcap Binary files differnew file mode 100644 index 00000000..1eaa8542 --- /dev/null +++ b/test/captures/tls12-chacha20poly1305.pcap diff --git a/test/captures/tls12-dsb.pcapng b/test/captures/tls12-dsb.pcapng Binary files differnew file mode 100644 index 00000000..d9bf1ab5 --- /dev/null +++ b/test/captures/tls12-dsb.pcapng diff --git a/test/captures/tls13-20-chacha20poly1305.pcap b/test/captures/tls13-20-chacha20poly1305.pcap Binary files differnew file mode 100644 index 00000000..da2246d6 --- /dev/null +++ b/test/captures/tls13-20-chacha20poly1305.pcap diff --git a/test/captures/tls13-rfc8446.pcap b/test/captures/tls13-rfc8446.pcap Binary files differnew file mode 100644 index 00000000..4500f593 --- /dev/null +++ b/test/captures/tls13-rfc8446.pcap diff --git a/test/captures/trunc.pcap b/test/captures/trunc.pcap Binary files differnew file mode 100644 index 00000000..8dd75ed5 --- /dev/null +++ b/test/captures/trunc.pcap diff --git a/test/captures/udt-dtls.pcapng.gz b/test/captures/udt-dtls.pcapng.gz Binary files differnew file mode 100644 index 00000000..67750f37 --- /dev/null +++ b/test/captures/udt-dtls.pcapng.gz diff --git a/test/captures/websocket-compressed-fragmented.pcapng.gz b/test/captures/websocket-compressed-fragmented.pcapng.gz Binary files differnew file mode 100644 index 00000000..001a8915 --- /dev/null +++ b/test/captures/websocket-compressed-fragmented.pcapng.gz diff --git a/test/captures/websocket-compressed.pcapng.gz b/test/captures/websocket-compressed.pcapng.gz Binary files differnew file mode 100644 index 00000000..8276c066 --- /dev/null +++ b/test/captures/websocket-compressed.pcapng.gz diff --git a/test/captures/websocket-fragmented.pcapng.gz b/test/captures/websocket-fragmented.pcapng.gz Binary files differnew file mode 100644 index 00000000..99078b53 --- /dev/null +++ b/test/captures/websocket-fragmented.pcapng.gz diff --git a/test/captures/websocket.pcapng.gz b/test/captures/websocket.pcapng.gz Binary files differnew file mode 100644 index 00000000..b83981fd --- /dev/null +++ b/test/captures/websocket.pcapng.gz diff --git a/test/captures/wep.pcapng.gz b/test/captures/wep.pcapng.gz Binary files differnew file mode 100644 index 00000000..e41f688e --- /dev/null +++ b/test/captures/wep.pcapng.gz diff --git a/test/captures/wireguard-ping-tcp-dsb.pcapng b/test/captures/wireguard-ping-tcp-dsb.pcapng Binary files differnew file mode 100644 index 00000000..d15790f3 --- /dev/null +++ b/test/captures/wireguard-ping-tcp-dsb.pcapng diff --git a/test/captures/wireguard-ping-tcp.pcap b/test/captures/wireguard-ping-tcp.pcap Binary files differnew file mode 100644 index 00000000..79255edb --- /dev/null +++ b/test/captures/wireguard-ping-tcp.pcap diff --git a/test/captures/wireguard-psk.pcap b/test/captures/wireguard-psk.pcap Binary files differnew file mode 100644 index 00000000..a38088b7 --- /dev/null +++ b/test/captures/wireguard-psk.pcap diff --git a/test/captures/wpa-Induction.pcap.gz b/test/captures/wpa-Induction.pcap.gz Binary files differnew file mode 100644 index 00000000..27e36703 --- /dev/null +++ b/test/captures/wpa-Induction.pcap.gz diff --git a/test/captures/wpa-ccmp-256.pcapng.gz b/test/captures/wpa-ccmp-256.pcapng.gz Binary files differnew file mode 100644 index 00000000..d65bb1ee --- /dev/null +++ b/test/captures/wpa-ccmp-256.pcapng.gz diff --git a/test/captures/wpa-eap-tls.pcap.gz b/test/captures/wpa-eap-tls.pcap.gz Binary files differnew file mode 100644 index 00000000..307e5fad --- /dev/null +++ b/test/captures/wpa-eap-tls.pcap.gz diff --git a/test/captures/wpa-gcmp-256.pcapng.gz b/test/captures/wpa-gcmp-256.pcapng.gz Binary files differnew file mode 100644 index 00000000..83ece8e1 --- /dev/null +++ b/test/captures/wpa-gcmp-256.pcapng.gz diff --git a/test/captures/wpa-gcmp.pcapng.gz b/test/captures/wpa-gcmp.pcapng.gz Binary files differnew file mode 100644 index 00000000..e3b799b7 --- /dev/null +++ b/test/captures/wpa-gcmp.pcapng.gz diff --git a/test/captures/wpa-test-decode-mgmt.pcap.gz b/test/captures/wpa-test-decode-mgmt.pcap.gz Binary files differnew file mode 100644 index 00000000..b4e04d21 --- /dev/null +++ b/test/captures/wpa-test-decode-mgmt.pcap.gz diff --git a/test/captures/wpa-test-decode-tdls.pcap.gz b/test/captures/wpa-test-decode-tdls.pcap.gz Binary files differnew file mode 100644 index 00000000..f166dda3 --- /dev/null +++ b/test/captures/wpa-test-decode-tdls.pcap.gz diff --git a/test/captures/wpa-test-decode.pcap.gz b/test/captures/wpa-test-decode.pcap.gz Binary files differnew file mode 100644 index 00000000..050f94ec --- /dev/null +++ b/test/captures/wpa-test-decode.pcap.gz diff --git a/test/captures/wpa1-gtk-rekey.pcapng.gz b/test/captures/wpa1-gtk-rekey.pcapng.gz Binary files differnew file mode 100644 index 00000000..88e4c067 --- /dev/null +++ b/test/captures/wpa1-gtk-rekey.pcapng.gz diff --git a/test/captures/wpa2-ft-eap.pcapng.gz b/test/captures/wpa2-ft-eap.pcapng.gz Binary files differnew file mode 100644 index 00000000..5ec811a3 --- /dev/null +++ b/test/captures/wpa2-ft-eap.pcapng.gz diff --git a/test/captures/wpa2-ft-psk.pcapng.gz b/test/captures/wpa2-ft-psk.pcapng.gz Binary files differnew file mode 100644 index 00000000..15793144 --- /dev/null +++ b/test/captures/wpa2-ft-psk.pcapng.gz diff --git a/test/captures/wpa2-psk-mfp.pcapng.gz b/test/captures/wpa2-psk-mfp.pcapng.gz Binary files differnew file mode 100644 index 00000000..da445aec --- /dev/null +++ b/test/captures/wpa2-psk-mfp.pcapng.gz diff --git a/test/captures/wpa3-sae.pcapng.gz b/test/captures/wpa3-sae.pcapng.gz Binary files differnew file mode 100644 index 00000000..75582706 --- /dev/null +++ b/test/captures/wpa3-sae.pcapng.gz diff --git a/test/captures/wpa3-suiteb-192.pcapng.gz b/test/captures/wpa3-suiteb-192.pcapng.gz Binary files differnew file mode 100644 index 00000000..663dee91 --- /dev/null +++ b/test/captures/wpa3-suiteb-192.pcapng.gz diff --git a/test/captures/wpa_ptk_extended_key_id.pcap.gz b/test/captures/wpa_ptk_extended_key_id.pcap.gz Binary files differnew file mode 100644 index 00000000..c093018f --- /dev/null +++ b/test/captures/wpa_ptk_extended_key_id.pcap.gz diff --git a/test/config/80211_keys.tmpl b/test/config/80211_keys.tmpl new file mode 100644 index 00000000..da825ac8 --- /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 00000000..f8f33613 --- /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 00000000..4c070e10 --- /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 00000000..869822e2 --- /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 00000000..24fbb217 --- /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 00000000..e457d2c3 --- /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 00000000..674564e6 --- /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 00000000..8daa5cb7 --- /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 00000000..8f6eda57 --- /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 00000000..ceee4025 --- /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 00000000..d799713e --- /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 00000000..9f1b75c4 --- /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 00000000..4a7901be --- /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 00000000..98819f30 --- /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 00000000..8a3e2fbd --- /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 Binary files differnew file mode 100644 index 00000000..43a66ddf --- /dev/null +++ b/test/keys/key.p12 diff --git a/test/keys/knx_keyring.xml b/test/keys/knx_keyring.xml new file mode 100644 index 00000000..c9b3cdd2 --- /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 Binary files differnew file mode 100644 index 00000000..aa0b8f13 --- /dev/null +++ b/test/keys/krb-816.keytab diff --git a/test/keys/rsa-p-lt-q.key b/test/keys/rsa-p-lt-q.key new file mode 100644 index 00000000..8fdfa85e --- /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 00000000..d9fca7ee --- /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 00000000..26022734 --- /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 00000000..49ec5074 --- /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 00000000..adae9e84 --- /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 00000000..cbdbcad7 --- /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 00000000..e6d535e8 --- /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 00000000..d32fd4a2 --- /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 00000000..e858b9a5 --- /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 00000000..15e7e4c1 --- /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 00000000..9195e25c --- /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 00000000..b4db542e --- /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 00000000..f159ba2d --- /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 00000000..d1a2a6c9 --- /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 00000000..8e4631bd --- /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 00000000..c9ff8ea7 --- /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 00000000..da52d74a --- /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 00000000..836aa7b4 --- /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 00000000..f049b81e --- /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 00000000..d73b13a0 --- /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 00000000..4e324572 --- /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 00000000..6b4aff9c --- /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 00000000..6a703d38 --- /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 00000000..8716f2af --- /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 00000000..f7e2f66c --- /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 00000000..b949a387 --- /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 00000000..52bd3d03 --- /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 00000000..cc038989 --- /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 00000000..7a16365e --- /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 00000000..379b2f5a --- /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 00000000..9d2223f6 --- /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 00000000..56ca3fbe --- /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 00000000..bc6a2fb2 --- /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 00000000..e002c8b5 --- /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 00000000..fcd6d095 --- /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 00000000..baf702cd --- /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 00000000..3510e574 --- /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 00000000..0578a2a9 --- /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 00000000..b391f859 --- /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 00000000..dbed8ceb --- /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 00000000..46005cac --- /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 00000000..359ead4a --- /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 00000000..d04c9b72 --- /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 00000000..bc619b15 --- /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 00000000..f14b1250 --- /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 00000000..1511f714 --- /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 00000000..25aee6a4 --- /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 00000000..3ee7de87 --- /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 00000000..9a6847c5 --- /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 00000000..8ff870dd --- /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 00000000..ece32e15 --- /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 00000000..397b5189 --- /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 00000000..4cb367cf --- /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 00000000..e8e534e8 --- /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 00000000..ffcd6cd5 --- /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 00000000..55686b2c --- /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 00000000..985a11f6 --- /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 00000000..0b66e0bd --- /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 00000000..8ca30054 --- /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 00000000..c622b07c --- /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 00000000..05474374 --- /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 00000000..449360bf --- /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 00000000..9c43f2b2 --- /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 00000000..08c9795c --- /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 00000000..b9ea99b8 --- /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 00000000..4ae93466 --- /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 00000000..a60433dd --- /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 00000000..2b983654 --- /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 00000000..799ef68e --- /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 00000000..0f490bc0 --- /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 00000000..8d77b3e6 --- /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 00000000..580d812c --- /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 00000000..016c1d62 --- /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 00000000..3d09d926 --- /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 00000000..ee90c930 --- /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 00000000..899df596 --- /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 00000000..b5b1b4fd --- /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 00000000..04b5aeb6 --- /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 00000000..2b383798 --- /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 00000000..37ee4bc8 --- /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 00000000..3916330c --- /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 00000000..c28ee3cb --- /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 00000000..d7017dd1 --- /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 00000000..4376d1b1 --- /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 00000000..95e657b5 --- /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 00000000..9e80bda7 --- /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 00000000..0e5e492b --- /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 00000000..3c6756f5 --- /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 00000000..db736dcc --- /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 00000000..6163f18a --- /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 00000000..65abf9bc --- /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 00000000..b63975cd --- /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 00000000..6f96b118 --- /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 00000000..28b38237 --- /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() + |