├── vcpkg.json ├── tests ├── ui │ ├── tests.txt │ ├── emf2022-badge │ │ └── actions.json │ ├── split-poll-step │ │ └── actions.json │ └── mouse-step │ │ └── actions.json ├── bad-crcs │ ├── devices-reference.txt │ ├── capture.pcap │ └── reference.txt ├── double-setup │ ├── devices-reference.txt │ ├── capture.pcap │ └── reference.txt ├── split-poll │ ├── devices-reference.txt │ ├── capture.pcap │ └── reference.txt ├── iso-ambiguous │ ├── devices-reference.txt │ ├── capture.pcap │ └── reference.txt ├── hackrf-restart-failure │ ├── devices-reference.txt │ └── capture.pcap ├── mouse │ ├── capture.pcap │ └── devices-reference.txt ├── hid │ ├── amp │ │ ├── descriptor.bin │ │ └── reference.txt │ ├── hub │ │ ├── descriptor.bin │ │ └── reference.txt │ ├── ps5 │ │ ├── descriptor.bin │ │ └── reference.txt │ ├── kvm-0 │ │ ├── descriptor.bin │ │ └── reference.txt │ ├── kvm-1 │ │ ├── descriptor.bin │ │ └── reference.txt │ ├── mouse │ │ ├── descriptor.bin │ │ └── reference.txt │ ├── headset │ │ ├── descriptor.bin │ │ └── reference.txt │ ├── tests.txt │ ├── keyboard-0 │ │ ├── descriptor.bin │ │ └── reference.txt │ └── keyboard-1 │ │ ├── descriptor.bin │ │ └── reference.txt ├── split-enum │ ├── capture.pcap │ └── devices-reference.txt ├── split-nyet │ └── capture.pcap ├── address-reuse │ └── capture.pcap ├── emf2022-badge │ └── capture.pcap ├── hackrf-connect │ ├── capture.pcap │ └── devices-reference.txt ├── hackrf-dfu-enum │ ├── capture.pcap │ └── devices-reference.txt ├── iso-unambiguous │ └── capture.pcap ├── ksolti-core-enum │ └── capture.pcap ├── bad-descriptor-length │ ├── capture.pcap │ ├── reference.txt │ └── devices-reference.txt ├── analyzer-test-bad-cable │ ├── capture.pcap │ └── devices-reference.txt ├── ls-keepalive-divided-transaction │ ├── capture.pcapng │ └── devices-reference.txt ├── ls-keepalive-more-divided-transactions │ ├── capture.pcapng │ └── devices-reference.txt └── tests.txt ├── screenshot.png ├── appimage ├── dist │ ├── icon.png │ └── packetry.desktop ├── docker │ └── patches │ │ ├── gtk-4.14.4-pin-libsass.patch │ │ ├── gtk-4.14.4-pin-sysprof.patch │ │ ├── gtk-4.14.4-pin-fribidi.patch │ │ ├── gtk-4.14.4-pin-gi-docgen.patch │ │ ├── gtk-4.14.4-pin-pango.patch │ │ └── gtk-4.14.4-gcc-lt-9.patch ├── packetry.AppDir │ └── usr │ │ └── share │ │ └── doc │ │ ├── libgraphene-1.0-0 │ │ └── copyright │ │ ├── libglib2.0-0 │ │ └── copyright │ │ └── libgdk-pixbuf-2.0-0 │ │ └── copyright └── action.yml ├── docs ├── images │ ├── action-bar.png │ ├── detail-pane.png │ ├── device-pane.png │ ├── power-menu.png │ ├── status-bar.png │ ├── packetry-window.png │ ├── traffic-pane-packets.png │ ├── traffic-pane-hierarchical.png │ └── traffic-pane-transactions.png ├── requirements.txt ├── source │ ├── index.rst │ ├── what_is_packetry.rst │ ├── conf.py │ ├── keyboard_shortcuts_macos.rst │ ├── keyboard_shortcuts_linux_windows.rst │ └── user_interface.rst ├── Makefile └── make.bat ├── src ├── build.rs ├── database │ ├── mod.rs │ └── counter.rs ├── ui │ ├── capture.rs │ ├── settings.rs │ ├── row_data.rs │ ├── item_widget.rs │ ├── item_connector.rs │ └── power.rs ├── util │ ├── rcu.rs │ ├── id.rs │ ├── vec_map.rs │ ├── mod.rs │ └── dump.rs ├── fuzzer.rs ├── cli.rs ├── version.rs ├── backend │ └── transfer_queue.rs ├── main.rs └── event.rs ├── .gitignore ├── .readthedocs.yaml ├── wix ├── manual-licenses │ ├── LICENSE-derive-into-owned-0.2.0.txt │ ├── LICENSE-ndk-context-0.1.1.txt │ ├── LICENSE-convert_case-0.4.0.txt │ ├── LICENSE-malloc_buf-0.0.6.txt │ ├── LICENSE-byteorder_slice-3.0.0.txt │ ├── LICENSE-kernel32-sys-0.2.2.txt │ ├── LICENSE-winapi-build-0.1.1.txt │ ├── LICENSE-ws2_32-sys-0.2.1.txt │ ├── LICENSE-dark-light-1.1.1.txt │ ├── LICENSE-winapi-i686-pc-windows-gnu-0.4.0.txt │ ├── LICENSE-winapi-x86_64-pc-windows-gnu-0.4.0.txt │ ├── LICENSE-merge_derive-0.1.0.txt │ ├── LICENSE-logwise_proc-0.1.1.txt │ └── LICENSE-ryu-1.0.18.txt ├── generate_components.py ├── required-dlls.txt ├── vcpkg_licenses.py └── rust_licenses.py ├── LICENSE ├── README.md ├── CHANGELOG.md ├── Cargo.toml ├── CODE_OF_CONDUCT.md └── .github └── workflows └── appimage.yml /vcpkg.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": ["gtk", "pkgconf"] 3 | } 4 | -------------------------------------------------------------------------------- /tests/ui/tests.txt: -------------------------------------------------------------------------------- 1 | emf2022-badge 2 | mouse-step 3 | split-poll-step 4 | -------------------------------------------------------------------------------- /tests/bad-crcs/devices-reference.txt: -------------------------------------------------------------------------------- 1 | Device 7: Unknown 2 | No device descriptor 3 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greatscottgadgets/packetry/HEAD/screenshot.png -------------------------------------------------------------------------------- /tests/double-setup/devices-reference.txt: -------------------------------------------------------------------------------- 1 | Device 43: Unknown 2 | No device descriptor 3 | -------------------------------------------------------------------------------- /tests/split-poll/devices-reference.txt: -------------------------------------------------------------------------------- 1 | Device 14: Unknown 2 | No device descriptor 3 | -------------------------------------------------------------------------------- /tests/iso-ambiguous/devices-reference.txt: -------------------------------------------------------------------------------- 1 | Device 27: Unknown 2 | No device descriptor 3 | -------------------------------------------------------------------------------- /tests/hackrf-restart-failure/devices-reference.txt: -------------------------------------------------------------------------------- 1 | Device 7: Unknown 2 | No device descriptor 3 | -------------------------------------------------------------------------------- /appimage/dist/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greatscottgadgets/packetry/HEAD/appimage/dist/icon.png -------------------------------------------------------------------------------- /tests/mouse/capture.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greatscottgadgets/packetry/HEAD/tests/mouse/capture.pcap -------------------------------------------------------------------------------- /docs/images/action-bar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greatscottgadgets/packetry/HEAD/docs/images/action-bar.png -------------------------------------------------------------------------------- /docs/images/detail-pane.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greatscottgadgets/packetry/HEAD/docs/images/detail-pane.png -------------------------------------------------------------------------------- /docs/images/device-pane.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greatscottgadgets/packetry/HEAD/docs/images/device-pane.png -------------------------------------------------------------------------------- /docs/images/power-menu.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greatscottgadgets/packetry/HEAD/docs/images/power-menu.png -------------------------------------------------------------------------------- /docs/images/status-bar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greatscottgadgets/packetry/HEAD/docs/images/status-bar.png -------------------------------------------------------------------------------- /tests/bad-crcs/capture.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greatscottgadgets/packetry/HEAD/tests/bad-crcs/capture.pcap -------------------------------------------------------------------------------- /tests/hid/amp/descriptor.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greatscottgadgets/packetry/HEAD/tests/hid/amp/descriptor.bin -------------------------------------------------------------------------------- /tests/hid/hub/descriptor.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greatscottgadgets/packetry/HEAD/tests/hid/hub/descriptor.bin -------------------------------------------------------------------------------- /tests/hid/ps5/descriptor.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greatscottgadgets/packetry/HEAD/tests/hid/ps5/descriptor.bin -------------------------------------------------------------------------------- /tests/hid/kvm-0/descriptor.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greatscottgadgets/packetry/HEAD/tests/hid/kvm-0/descriptor.bin -------------------------------------------------------------------------------- /tests/hid/kvm-1/descriptor.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greatscottgadgets/packetry/HEAD/tests/hid/kvm-1/descriptor.bin -------------------------------------------------------------------------------- /tests/hid/mouse/descriptor.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greatscottgadgets/packetry/HEAD/tests/hid/mouse/descriptor.bin -------------------------------------------------------------------------------- /tests/split-enum/capture.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greatscottgadgets/packetry/HEAD/tests/split-enum/capture.pcap -------------------------------------------------------------------------------- /tests/split-nyet/capture.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greatscottgadgets/packetry/HEAD/tests/split-nyet/capture.pcap -------------------------------------------------------------------------------- /tests/split-poll/capture.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greatscottgadgets/packetry/HEAD/tests/split-poll/capture.pcap -------------------------------------------------------------------------------- /docs/images/packetry-window.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greatscottgadgets/packetry/HEAD/docs/images/packetry-window.png -------------------------------------------------------------------------------- /tests/address-reuse/capture.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greatscottgadgets/packetry/HEAD/tests/address-reuse/capture.pcap -------------------------------------------------------------------------------- /tests/double-setup/capture.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greatscottgadgets/packetry/HEAD/tests/double-setup/capture.pcap -------------------------------------------------------------------------------- /tests/emf2022-badge/capture.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greatscottgadgets/packetry/HEAD/tests/emf2022-badge/capture.pcap -------------------------------------------------------------------------------- /tests/hackrf-connect/capture.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greatscottgadgets/packetry/HEAD/tests/hackrf-connect/capture.pcap -------------------------------------------------------------------------------- /tests/hid/headset/descriptor.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greatscottgadgets/packetry/HEAD/tests/hid/headset/descriptor.bin -------------------------------------------------------------------------------- /tests/hid/tests.txt: -------------------------------------------------------------------------------- 1 | amp 2 | headset 3 | hub 4 | keyboard-0 5 | keyboard-1 6 | kvm-0 7 | kvm-1 8 | mouse 9 | ps5 10 | -------------------------------------------------------------------------------- /tests/iso-ambiguous/capture.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greatscottgadgets/packetry/HEAD/tests/iso-ambiguous/capture.pcap -------------------------------------------------------------------------------- /tests/hackrf-dfu-enum/capture.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greatscottgadgets/packetry/HEAD/tests/hackrf-dfu-enum/capture.pcap -------------------------------------------------------------------------------- /tests/hid/keyboard-0/descriptor.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greatscottgadgets/packetry/HEAD/tests/hid/keyboard-0/descriptor.bin -------------------------------------------------------------------------------- /tests/hid/keyboard-1/descriptor.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greatscottgadgets/packetry/HEAD/tests/hid/keyboard-1/descriptor.bin -------------------------------------------------------------------------------- /tests/iso-unambiguous/capture.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greatscottgadgets/packetry/HEAD/tests/iso-unambiguous/capture.pcap -------------------------------------------------------------------------------- /tests/ksolti-core-enum/capture.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greatscottgadgets/packetry/HEAD/tests/ksolti-core-enum/capture.pcap -------------------------------------------------------------------------------- /docs/images/traffic-pane-packets.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greatscottgadgets/packetry/HEAD/docs/images/traffic-pane-packets.png -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | sphinx==7.2.6 2 | sphinx_rtd_theme==2.0.0 3 | readthedocs-sphinx-search==0.3.2 4 | sphinx-inline-tabs==2023.4.21 5 | -------------------------------------------------------------------------------- /tests/bad-descriptor-length/capture.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greatscottgadgets/packetry/HEAD/tests/bad-descriptor-length/capture.pcap -------------------------------------------------------------------------------- /docs/images/traffic-pane-hierarchical.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greatscottgadgets/packetry/HEAD/docs/images/traffic-pane-hierarchical.png -------------------------------------------------------------------------------- /docs/images/traffic-pane-transactions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greatscottgadgets/packetry/HEAD/docs/images/traffic-pane-transactions.png -------------------------------------------------------------------------------- /tests/analyzer-test-bad-cable/capture.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greatscottgadgets/packetry/HEAD/tests/analyzer-test-bad-cable/capture.pcap -------------------------------------------------------------------------------- /tests/hackrf-restart-failure/capture.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greatscottgadgets/packetry/HEAD/tests/hackrf-restart-failure/capture.pcap -------------------------------------------------------------------------------- /src/build.rs: -------------------------------------------------------------------------------- 1 | //! Code to be executed at build time. 2 | 3 | fn main() { 4 | built::write_built_file().expect("Failed to acquire build-time information"); 5 | } 6 | -------------------------------------------------------------------------------- /tests/ls-keepalive-divided-transaction/capture.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greatscottgadgets/packetry/HEAD/tests/ls-keepalive-divided-transaction/capture.pcapng -------------------------------------------------------------------------------- /appimage/dist/packetry.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=packetry 3 | Exec=packetry 4 | Icon=icon 5 | Type=Application 6 | Categories=Utility; 7 | X-AppImage-Version=0.1.0 8 | -------------------------------------------------------------------------------- /tests/ls-keepalive-more-divided-transactions/capture.pcapng: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/greatscottgadgets/packetry/HEAD/tests/ls-keepalive-more-divided-transactions/capture.pcapng -------------------------------------------------------------------------------- /tests/hid/hub/reference.txt: -------------------------------------------------------------------------------- 1 | 2 | ○ Input report (64 bytes): 3 | └── Array of 64 buttons: bytes 0-63 [Vendor Usage 0x01 — Vendor Usage 0x40] 4 | 5 | ○ Output report (64 bytes): 6 | └── Array of 64 buttons: bytes 0-63 [Vendor Usage 0x01 — Vendor Usage 0x40] 7 | -------------------------------------------------------------------------------- /tests/hid/keyboard-1/reference.txt: -------------------------------------------------------------------------------- 1 | 2 | ○ Input report #3 (3 bytes): 3 | └── Array of 1 button: bytes 1-2 [Pointer — System Display Toggle LCD Autoscale] 4 | 5 | ○ Input report #4 (3 bytes): 6 | └── Array of 1 button: bytes 1-2 [Consumer Control — AC Soft Key Left] 7 | -------------------------------------------------------------------------------- /tests/tests.txt: -------------------------------------------------------------------------------- 1 | address-reuse 2 | analyzer-test-bad-cable 3 | bad-crcs 4 | bad-descriptor-length 5 | double-setup 6 | emf2022-badge 7 | hackrf-connect 8 | hackrf-dfu-enum 9 | hackrf-restart-failure 10 | iso-ambiguous 11 | iso-unambiguous 12 | ksolti-core-enum 13 | ls-keepalive-more-divided-transactions 14 | ls-keepalive-divided-transaction 15 | mouse 16 | split-enum 17 | split-poll 18 | -------------------------------------------------------------------------------- /docs/source/index.rst: -------------------------------------------------------------------------------- 1 | ====================== 2 | Packetry Documentation 3 | ====================== 4 | 5 | .. toctree:: 6 | :maxdepth: 2 7 | :caption: Introduction 8 | 9 | what_is_packetry 10 | user_interface 11 | keyboard_shortcuts_linux_windows 12 | keyboard_shortcuts_macos 13 | 14 | .. toctree:: 15 | :maxdepth: 2 16 | :caption: Quick Start 17 | 18 | quick_start 19 | -------------------------------------------------------------------------------- /appimage/docker/patches/gtk-4.14.4-pin-libsass.patch: -------------------------------------------------------------------------------- 1 | diff --git a/subprojects/libsass.wrap b/subprojects/libsass.wrap 2 | index 0e52fb4..3507404 100644 3 | --- a/subprojects/libsass.wrap 4 | +++ b/subprojects/libsass.wrap 5 | @@ -1,5 +1,5 @@ 6 | [wrap-git] 7 | directory=libsass 8 | url=https://github.com/lazka/libsass.git 9 | -revision=meson 10 | +revision=aac79dccd3c8f7e8f22125f87a119f3b1ee9d487 11 | depth=1 12 | -------------------------------------------------------------------------------- /appimage/docker/patches/gtk-4.14.4-pin-sysprof.patch: -------------------------------------------------------------------------------- 1 | diff --git a/subprojects/sysprof.wrap b/subprojects/sysprof.wrap 2 | index 29dcbdc..04daedd 100644 3 | --- a/subprojects/sysprof.wrap 4 | +++ b/subprojects/sysprof.wrap 5 | @@ -1,7 +1,7 @@ 6 | [wrap-git] 7 | directory = sysprof 8 | url = https://gitlab.gnome.org/GNOME/sysprof.git 9 | -revision = master 10 | +revision = 47.2 11 | depth = 1 12 | 13 | [provide] 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /docs/build/ 2 | /target 3 | /tests/*/dump 4 | /tests/*/output.txt 5 | /tests/*/devices-output.txt 6 | /tests/ui/*/output.txt 7 | /tests/hid/*/output.txt 8 | /vcpkg 9 | /wix/LICENSE-dynamic-libraries.txt 10 | /wix/LICENSE-packetry.txt 11 | /wix/LICENSE-static-libraries.txt 12 | /wix/dll-components.wxi 13 | /wix/dll-references.wxi 14 | /wix/full-licenses 15 | /wix/license-components.wxi 16 | /wix/license-references.wxi 17 | HITL-*.pcap 18 | -------------------------------------------------------------------------------- /appimage/docker/patches/gtk-4.14.4-pin-fribidi.patch: -------------------------------------------------------------------------------- 1 | diff --git a/subprojects/fribidi.wrap b/subprojects/fribidi.wrap 2 | index 20ba8ed..7ea197e 100644 3 | --- a/subprojects/fribidi.wrap 4 | +++ b/subprojects/fribidi.wrap 5 | @@ -2,7 +2,7 @@ 6 | directory = fribidi 7 | url = https://github.com/fribidi/fribidi.git 8 | push-url = git@github.com:fribidi/fribidi.git 9 | -revision = master 10 | +revision = v1.0.16 11 | depth = 1 12 | 13 | [provide] 14 | -------------------------------------------------------------------------------- /tests/hid/mouse/reference.txt: -------------------------------------------------------------------------------- 1 | 2 | ○ Input report #1 (7 bytes): 3 | ├── Button 1: byte 1 bit 0 4 | ├── Button 2: byte 1 bit 1 5 | ├── Button 3: byte 1 bit 2 6 | ├── Button 4: byte 1 bit 3 7 | ├── Button 5: byte 1 bit 4 8 | ├── Padding: byte 1 bits 5-7 9 | ├── X: byte 2 bit 0 — byte 3 bit 3 (values -2047 to 2047) 10 | ├── Y: byte 3 bit 4 — byte 4 bit 7 (values -2047 to 2047) 11 | ├── Wheel: byte 5 (values -127 to 127) 12 | └── AC Pan: byte 6 (values -127 to 127) 13 | -------------------------------------------------------------------------------- /appimage/docker/patches/gtk-4.14.4-pin-gi-docgen.patch: -------------------------------------------------------------------------------- 1 | diff --git a/subprojects/gi-docgen.wrap b/subprojects/gi-docgen.wrap 2 | index 85c85da..2d71071 100644 3 | --- a/subprojects/gi-docgen.wrap 4 | +++ b/subprojects/gi-docgen.wrap 5 | @@ -2,7 +2,7 @@ 6 | directory = gi-docgen 7 | url = https://gitlab.gnome.org/GNOME/gi-docgen.git 8 | push-url = ssh://git@ssh.gitlab.gnome.org:GNOME/gi-docgen.git 9 | -revision = main 10 | +revision = 2025.3 11 | depth = 1 12 | 13 | [provide] 14 | -------------------------------------------------------------------------------- /tests/hid/kvm-1/reference.txt: -------------------------------------------------------------------------------- 1 | 2 | ○ Input report #1 (5 bytes): 3 | ├── Button 1: byte 1 bit 0 4 | ├── Button 2: byte 1 bit 1 5 | ├── Button 3: byte 1 bit 2 6 | ├── Button 4: byte 1 bit 3 7 | ├── Button 5: byte 1 bit 4 8 | ├── Padding: byte 1 bits 5-7 9 | ├── X: byte 2 (values -127 to 127) 10 | ├── Y: byte 3 (values -127 to 127) 11 | └── Wheel: byte 4 (values -127 to 127) 12 | 13 | ○ Input report #2 (2 bytes): 14 | ├── System Power Down: byte 1 bit 0 15 | ├── System Sleep: byte 1 bit 1 16 | ├── System Wake Up: byte 1 bit 2 17 | └── Padding: byte 1 bits 3-7 18 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | # .readthedocs.yaml 2 | # Read the Docs configuration file 3 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 4 | 5 | # Required 6 | version: 2 7 | 8 | # Set the OS, Python version and other tools 9 | build: 10 | os: ubuntu-22.04 11 | tools: 12 | python: "3.12" 13 | 14 | # Build documentation in the "docs/" directory with Sphinx 15 | sphinx: 16 | configuration: docs/source/conf.py 17 | 18 | # Build PDF for docs 19 | formats: 20 | - pdf 21 | 22 | python: 23 | install: 24 | - requirements: docs/requirements.txt 25 | -------------------------------------------------------------------------------- /tests/double-setup/reference.txt: -------------------------------------------------------------------------------- 1 | ○─ Polling 1 times for unidentified transfer on endpoint 43.4 OUT 2 | ├─── SETUP transaction on 43.4 3 | │ └── SETUP packet on 43.4, CRC 1F 4 | ○─┼─ 1 invalid groups 5 | └─┼─── 1 malformed packet 6 | │ └── Malformed 0-byte packet 7 | ○─ Polling 1 times for unidentified transfer on endpoint 43.4 OUT 8 | └─── SETUP transaction on 43.4 9 | └── SETUP packet on 43.4, CRC 1F 10 | ○─ Polling 1 times for unidentified transfer on endpoint 43.4 OUT 11 | └─── SETUP transaction on 43.4 12 | └── SETUP packet on 43.4, CRC 1F 13 | -------------------------------------------------------------------------------- /src/database/mod.rs: -------------------------------------------------------------------------------- 1 | //! Storage primitives for the capture database. 2 | 3 | mod counter; 4 | mod stream; 5 | mod data_stream; 6 | mod index_stream; 7 | mod compact_index; 8 | 9 | pub use counter::{Counter, CounterSet, Snapshot}; 10 | 11 | pub use data_stream::{ 12 | DataReader, 13 | DataWriter, 14 | DataSnapshot, 15 | DataReaderOps, 16 | data_stream, 17 | data_stream_with_block_size, 18 | }; 19 | 20 | pub use compact_index::{ 21 | CompactReader, 22 | CompactWriter, 23 | CompactSnapshot, 24 | CompactReaderOps, 25 | compact_index, 26 | }; 27 | -------------------------------------------------------------------------------- /docs/source/what_is_packetry.rst: -------------------------------------------------------------------------------- 1 | ================= 2 | What is Packetry? 3 | ================= 4 | 5 | Packetry is a fast, intuitive USB 2.0 protocol analysis application for use with `Cynthion `__ or for exploring previously captured USB traffic. Designed from the ground up to handle the challenges of USB analysis, Packetry enables live capture and visual representation of physical layer packet data for High Speed (480 Mbps), Full Speed (12 Mbps), and Low Speed (1.5 Mbps) USB. 6 | 7 | .. image:: ../images/packetry-window.png 8 | :alt: Packetry main window 9 | -------------------------------------------------------------------------------- /tests/ui/emf2022-badge/actions.json: -------------------------------------------------------------------------------- 1 | {"Open":"tests/emf2022-badge/capture.pcap"} 2 | {"Update":3710} 3 | {"Update":4406} 4 | {"SetExpanded":["traffic-hierarchical",10,true]} 5 | {"SetExpanded":["traffic-hierarchical",12,true]} 6 | {"SetExpanded":["traffic-hierarchical",33,true]} 7 | {"SetExpanded":["traffic-hierarchical",39,true]} 8 | {"SetExpanded":["traffic-hierarchical",44,true]} 9 | {"SetExpanded":["traffic-hierarchical",46,true]} 10 | {"SetExpanded":["traffic-hierarchical",33,false]} 11 | {"SetExpanded":["traffic-hierarchical",10,false]} 12 | {"SetExpanded":["traffic-hierarchical",29,false]} 13 | -------------------------------------------------------------------------------- /tests/bad-crcs/reference.txt: -------------------------------------------------------------------------------- 1 | ○─ Polling 1 times for unidentified transfer on endpoint 7.1 IN 2 | ├─── IN transaction on 7.1, NAK 3 | │ ├── IN packet on 7.1, CRC 1B 4 | │ └── NAK packet 5 | ○─┼─ 1 invalid groups 6 | └─┼─── 1 malformed packet 7 | │ └── Malformed packet (possibly IN, but bad CRC) of 3 bytes: [69, B7, DB] 8 | ○─┼─ 1 invalid groups 9 | └─┼─── 1 malformed packet 10 | │ └── Malformed packet (possibly IN, but bad CRC) of 3 bytes: [69, B7, DB] 11 | ○─┼─ 1 invalid groups 12 | └─┼─── 1 malformed packet 13 | │ └── Malformed packet (possibly SOF, but bad CRC) of 3 bytes: [A5, BB, CE] 14 | -------------------------------------------------------------------------------- /src/ui/capture.rs: -------------------------------------------------------------------------------- 1 | //! Unified access to different forms of capture. 2 | 3 | use crate::capture::{CaptureReader, CaptureSnapshot}; 4 | 5 | #[derive(Clone)] 6 | pub enum CaptureState { 7 | Ongoing(CaptureSnapshot), 8 | Complete 9 | } 10 | 11 | #[derive(Clone)] 12 | pub struct Capture { 13 | pub reader: CaptureReader, 14 | pub state: CaptureState, 15 | } 16 | 17 | impl Capture { 18 | pub fn set_snapshot(&mut self, snapshot: CaptureSnapshot) { 19 | self.state = CaptureState::Ongoing(snapshot); 20 | } 21 | 22 | pub fn set_completed(&mut self) { 23 | self.state = CaptureState::Complete; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /appimage/docker/patches/gtk-4.14.4-pin-pango.patch: -------------------------------------------------------------------------------- 1 | diff --git a/subprojects/pango.wrap b/subprojects/pango.wrap 2 | index 7156644..fa6715b 100644 3 | --- a/subprojects/pango.wrap 4 | +++ b/subprojects/pango.wrap 5 | @@ -2,8 +2,8 @@ 6 | directory = pango 7 | url = https://gitlab.gnome.org/GNOME/pango.git 8 | push-url = ssh://git@ssh.gitlab.gnome.org:GNOME/pango.git 9 | -# needs to be main, because we build the nightly docs from the subproject 10 | -revision = main 11 | +# needs to be 1.54.0 because later versions require versions of glib and fontconfig that are not supplied by gtk-4.14.4 12 | +revision = 1.54.0 13 | depth = 1 14 | 15 | [provide] 16 | -------------------------------------------------------------------------------- /tests/hid/headset/reference.txt: -------------------------------------------------------------------------------- 1 | 2 | ○ Input report (4 bytes): 3 | ├── Volume Increment: byte 0 bit 0 4 | ├── Volume Decrement: byte 0 bit 1 5 | ├── Mute: byte 0 bit 2 6 | ├── Consumer usage 0x00: byte 0 bit 3 7 | ├── Hook Switch: byte 0 bit 4 8 | ├── Consumer usage 0x00: byte 0 bit 5 9 | ├── Consumer usage 0x00: byte 0 bit 6 10 | ├── Consumer usage 0x00: byte 0 bit 7 11 | ├── Consumer usage 0x00: byte 1 12 | ├── Consumer usage 0x00: byte 2 13 | └── Consumer usage 0x00: byte 3 14 | 15 | ○ Output report (4 bytes): 16 | ├── Consumer usage 0x00: byte 0 17 | ├── Consumer usage 0x00: byte 1 18 | ├── Consumer usage 0x00: byte 2 19 | └── Consumer usage 0x00: byte 3 20 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | SOURCEDIR = source 8 | BUILDDIR = build 9 | 10 | # Put it first so that "make" without argument is like "make help". 11 | help: 12 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 13 | 14 | .PHONY: help Makefile 15 | 16 | # Catch-all target: route all unknown targets to Sphinx using the new 17 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 18 | %: Makefile 19 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -------------------------------------------------------------------------------- /tests/hid/kvm-0/reference.txt: -------------------------------------------------------------------------------- 1 | 2 | ○ Input report (8 bytes): 3 | ├── Keyboard LeftControl: byte 0 bit 0 4 | ├── Keyboard LeftShift: byte 0 bit 1 5 | ├── Keyboard LeftAlt: byte 0 bit 2 6 | ├── Keyboard Left GUI: byte 0 bit 3 7 | ├── Keyboard RightControl: byte 0 bit 4 8 | ├── Keyboard RightShift: byte 0 bit 5 9 | ├── Keyboard RightAlt: byte 0 bit 6 10 | ├── Keyboard Right GUI: byte 0 bit 7 11 | ├── Padding: byte 1 12 | └── Array of 6 buttons: bytes 2-7 [Keyboard/Keypad usage 0x00 — Keyboard LANG2] 13 | 14 | ○ Output report (1 byte): 15 | ├── Num Lock: byte 0 bit 0 16 | ├── Caps Lock: byte 0 bit 1 17 | ├── Scroll Lock: byte 0 bit 2 18 | ├── Compose: byte 0 bit 3 19 | ├── Kana: byte 0 bit 4 20 | └── Padding: byte 0 bits 5-7 21 | -------------------------------------------------------------------------------- /tests/hid/keyboard-0/reference.txt: -------------------------------------------------------------------------------- 1 | 2 | ○ Input report (8 bytes): 3 | ├── Keyboard LeftControl: byte 0 bit 0 4 | ├── Keyboard LeftShift: byte 0 bit 1 5 | ├── Keyboard LeftAlt: byte 0 bit 2 6 | ├── Keyboard Left GUI: byte 0 bit 3 7 | ├── Keyboard RightControl: byte 0 bit 4 8 | ├── Keyboard RightShift: byte 0 bit 5 9 | ├── Keyboard RightAlt: byte 0 bit 6 10 | ├── Keyboard Right GUI: byte 0 bit 7 11 | ├── Padding: byte 1 12 | └── Array of 6 buttons: bytes 2-7 [Keyboard/Keypad usage 0x00 — Keyboard/Keypad usage 0xFF] 13 | 14 | ○ Output report (1 byte): 15 | ├── Num Lock: byte 0 bit 0 16 | ├── Caps Lock: byte 0 bit 1 17 | ├── Scroll Lock: byte 0 bit 2 18 | ├── Compose: byte 0 bit 3 19 | ├── Kana: byte 0 bit 4 20 | └── Padding: byte 0 bits 5-7 21 | -------------------------------------------------------------------------------- /appimage/docker/patches/gtk-4.14.4-gcc-lt-9.patch: -------------------------------------------------------------------------------- 1 | diff --git a/subprojects/glib/gio/tests/meson.build b/subprojects/glib/gio/tests/meson.build 2 | index b4a6492..509f6e1 100644 3 | --- a/subprojects/glib/gio/tests/meson.build 4 | +++ b/subprojects/glib/gio/tests/meson.build 5 | @@ -928,7 +928,7 @@ if not meson.is_cross_build() 6 | test_resources_binary = custom_target('test_resources.o', 7 | input : test_gresource_binary, 8 | output : 'test_resources.o', 9 | - command : cc.cmd_array() + ['-Wl,-z,noexecstack', '-r', '-Wl,-b,binary', 10 | + command : cc.cmd_array() + ['-Wl,-z,noexecstack', '-r', '-Wl,-b,binary', '-nostdlib', 11 | '@INPUT@', '-o','@OUTPUT@']) 12 | 13 | # Rename symbol to match the one in the C file 14 | -------------------------------------------------------------------------------- /src/ui/settings.rs: -------------------------------------------------------------------------------- 1 | use std::path::PathBuf; 2 | 3 | use preferences::{AppInfo, Preferences}; 4 | use serde::{Serialize, Deserialize}; 5 | 6 | const APP_INFO: AppInfo = AppInfo { 7 | name: "Packetry", 8 | author: "Great Scott Gadgets" 9 | }; 10 | 11 | #[derive(Default, Serialize, Deserialize)] 12 | pub struct Settings { 13 | pub last_used_directory: Option, 14 | } 15 | 16 | impl Settings { 17 | pub fn load() -> Settings { 18 | match ::load(&APP_INFO, "settings") { 19 | Ok(settings) => settings, 20 | Err(_) => Settings { 21 | last_used_directory: std::env::current_dir().ok() 22 | } 23 | } 24 | } 25 | 26 | pub fn save(&mut self) { 27 | let _ = ::save(self, &APP_INFO, "settings"); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=sphinx-build 9 | ) 10 | set SOURCEDIR=source 11 | set BUILDDIR=build 12 | 13 | if "%1" == "" goto help 14 | 15 | %SPHINXBUILD% >NUL 2>NUL 16 | if errorlevel 9009 ( 17 | echo. 18 | echo.The 'sphinx-build' command was not found. Make sure you have Sphinx 19 | echo.installed, then set the SPHINXBUILD environment variable to point 20 | echo.to the full path of the 'sphinx-build' executable. Alternatively you 21 | echo.may add the Sphinx directory to PATH. 22 | echo. 23 | echo.If you don't have Sphinx installed, grab it from 24 | echo.http://sphinx-doc.org/ 25 | exit /b 1 26 | ) 27 | 28 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 29 | goto end 30 | 31 | :help 32 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 33 | 34 | :end 35 | popd 36 | -------------------------------------------------------------------------------- /docs/source/conf.py: -------------------------------------------------------------------------------- 1 | import sphinx_rtd_theme 2 | 3 | extensions = [ 4 | 'sphinx_rtd_theme' 5 | ] 6 | 7 | # -- Project information ----------------------------------------------------- 8 | 9 | project = 'Packetry' 10 | copyright = '2023-2024, Great Scott Gadgets' 11 | author = 'Great Scott Gadgets' 12 | 13 | version = '' 14 | release = '' 15 | 16 | 17 | # -- General configuration --------------------------------------------------- 18 | 19 | extensions = [ 20 | 'sphinx.ext.autodoc', 21 | 'sphinx_inline_tabs', 22 | ] 23 | 24 | templates_path = ['_templates'] 25 | exclude_patterns = ['_build'] 26 | source_suffix = '.rst' 27 | master_doc = 'index' 28 | language = 'en' 29 | exclude_patterns = [] 30 | pygments_style = None 31 | 32 | 33 | # -- Options for HTML output ------------------------------------------------- 34 | # run pip install sphinx_rtd_theme if you get sphinx_rtd_theme errors 35 | html_theme = "sphinx_rtd_theme" 36 | html_css_files = ['status.css'] 37 | -------------------------------------------------------------------------------- /docs/source/keyboard_shortcuts_macos.rst: -------------------------------------------------------------------------------- 1 | ========================== 2 | Keyboard Shortcuts - macOS 3 | ========================== 4 | 5 | General 6 | ------- 7 | 8 | +-----------+-------------------------------------------+ 9 | | Keypress | Command | 10 | +===========+===========================================+ 11 | | ``⌘ + O`` | Open capture file | 12 | +-----------+-------------------------------------------+ 13 | | ``⌘ + E`` | Interrupt loading of a large capture file | 14 | +-----------+-------------------------------------------+ 15 | | ``⌘ + S`` | Save current capture to file | 16 | +-----------+-------------------------------------------+ 17 | 18 | Live Capture 19 | ------------ 20 | 21 | +-----------+--------------------------+ 22 | | Keypress | Command | 23 | +===========+==========================+ 24 | | ``⌘ + B`` | Begin live capture | 25 | +-----------+--------------------------+ 26 | | ``⌘ + E`` | End live capture | 27 | +-----------+--------------------------+ 28 | -------------------------------------------------------------------------------- /src/util/rcu.rs: -------------------------------------------------------------------------------- 1 | //! Implementation of the read-copy-update (RCU) pattern. 2 | 3 | use std::sync::Arc; 4 | use arc_swap::ArcSwapAny; 5 | 6 | /// Trait to implement the read-copy-update pattern for a single writer. 7 | pub trait SingleWriterRcu { 8 | /// Copy the value, apply a function to it, and replace the current value. 9 | fn update(&self, f: F); 10 | 11 | /// Copy the value, apply a function to it, and replace the the current 12 | /// value if the function returns true. 13 | fn maybe_update bool>(&self, f: F); 14 | } 15 | 16 | impl SingleWriterRcu for ArcSwapAny> 17 | where T: Clone 18 | { 19 | fn update(&self, f: F) { 20 | let mut copy = self.load().as_ref().clone(); 21 | f(&mut copy); 22 | self.swap(Arc::new(copy)); 23 | } 24 | 25 | fn maybe_update bool>(&self, f: F) { 26 | let mut copy = self.load().as_ref().clone(); 27 | if f(&mut copy) { 28 | self.swap(Arc::new(copy)); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /tests/ui/split-poll-step/actions.json: -------------------------------------------------------------------------------- 1 | {"Open":"tests/split-poll/capture.pcap"} 2 | {"Update":1} 3 | {"Update":2} 4 | {"SetExpanded":["devices",0,true]} 5 | {"SetExpanded":["traffic-hierarchical",0,true]} 6 | {"Update":3} 7 | {"Update":4} 8 | {"SetExpanded":["traffic-hierarchical",2,true]} 9 | {"Update":5} 10 | {"Update":6} 11 | {"Update":7} 12 | {"Update":8} 13 | {"Update":9} 14 | {"Update":10} 15 | {"Update":11} 16 | {"Update":12} 17 | {"Update":13} 18 | {"Update":14} 19 | {"Update":15} 20 | {"Update":16} 21 | {"Update":17} 22 | {"Update":18} 23 | {"Update":19} 24 | {"Update":20} 25 | {"Update":21} 26 | {"Update":22} 27 | {"Update":23} 28 | {"Update":24} 29 | {"Update":25} 30 | {"Update":26} 31 | {"Update":27} 32 | {"Update":28} 33 | {"Update":29} 34 | {"Update":30} 35 | {"Update":31} 36 | {"Update":32} 37 | {"Update":33} 38 | {"Update":34} 39 | {"Update":35} 40 | {"Update":36} 41 | {"Update":37} 42 | {"Update":38} 43 | {"Update":39} 44 | {"Update":40} 45 | {"SetExpanded":["traffic-hierarchical",0,false]} 46 | {"SetExpanded":["traffic-hierarchical",1,false]} 47 | {"SetExpanded":["devices",0,false]} 48 | -------------------------------------------------------------------------------- /wix/manual-licenses/LICENSE-derive-into-owned-0.2.0.txt: -------------------------------------------------------------------------------- 1 | Copyright 2022 Joonas Koivunen and contributors 2 | 3 | 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: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | 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. 8 | -------------------------------------------------------------------------------- /wix/manual-licenses/LICENSE-ndk-context-0.1.1.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /docs/source/keyboard_shortcuts_linux_windows.rst: -------------------------------------------------------------------------------- 1 | ==================================== 2 | Keyboard Shortcuts - Linux & Windows 3 | ==================================== 4 | 5 | General 6 | ------- 7 | 8 | +----------------+-------------------------------------------+ 9 | | Keypress | Command | 10 | +================+===========================================+ 11 | | ``Ctrl + O`` | Open capture file | 12 | +----------------+-------------------------------------------+ 13 | | ``Ctrl + E`` | Interrupt loading of a large capture file | 14 | +----------------+-------------------------------------------+ 15 | | ``Ctrl + S`` | Save current capture to file | 16 | +----------------+-------------------------------------------+ 17 | 18 | Live Capture 19 | ------------ 20 | 21 | +---------------+--------------------------+ 22 | | Keypress | Command | 23 | +===============+==========================+ 24 | | ``Ctrl + B`` | Begin live capture | 25 | +---------------+--------------------------+ 26 | | ``Ctrl + E`` | End live capture | 27 | +---------------+--------------------------+ 28 | -------------------------------------------------------------------------------- /wix/manual-licenses/LICENSE-convert_case-0.4.0.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 David Purdum 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /wix/manual-licenses/LICENSE-malloc_buf-0.0.6.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Steven Sheldon 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /wix/manual-licenses/LICENSE-byteorder_slice-3.0.0.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019-2022 courvoif 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /wix/manual-licenses/LICENSE-kernel32-sys-0.2.2.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Peter Atashian 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /wix/manual-licenses/LICENSE-winapi-build-0.1.1.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Peter Atashian 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /wix/manual-licenses/LICENSE-ws2_32-sys-0.2.1.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Peter Atashian 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /wix/manual-licenses/LICENSE-dark-light-1.1.1.txt: -------------------------------------------------------------------------------- 1 | Copyright 2021-2024 Corey Farwell 2 | Copyright 2021-2024 Eduardo Flores 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of 5 | this software and associated documentation files (the “Software”), to deal in 6 | the Software without restriction, including without limitation the rights to 7 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 8 | of the Software, and to permit persons to whom the Software is furnished to do 9 | so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | -------------------------------------------------------------------------------- /wix/manual-licenses/LICENSE-winapi-i686-pc-windows-gnu-0.4.0.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Peter Atashian 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /wix/manual-licenses/LICENSE-winapi-x86_64-pc-windows-gnu-0.4.0.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Peter Atashian 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /wix/manual-licenses/LICENSE-merge_derive-0.1.0.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 Robin Krahl 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | of the Software, and to permit persons to whom the Software is furnished to do 10 | so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /wix/manual-licenses/LICENSE-logwise_proc-0.1.1.txt: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024-2025 Drew Crawford; https://sealedabstract.com/code/logwise 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /tests/hackrf-dfu-enum/devices-reference.txt: -------------------------------------------------------------------------------- 1 | Device 11: LPC 2 | Device descriptor 3 | Length: 18 bytes 4 | Type: 0x01 5 | USB Version: 2.00 6 | Class: 0x00: (Defined at Interface level) 7 | Subclass: 0x00 8 | Protocol: 0x00 9 | Max EP0 packet size: 64 bytes 10 | Vendor ID: 0x1FC9: NXP Semiconductors 11 | Product ID: 0x000C: LPC4330FET180 [ARM Cortex M4 + M0] (device firmware upgrade mode) 12 | Version: 1.00 13 | Manufacturer string: #1 'NXP' 14 | Product string: #2 'LPC' 15 | Serial string: #3 'ABCD' 16 | Configuration 1 17 | Configuration descriptor 18 | Length: 9 bytes 19 | Type: 0x02 20 | Total length: 27 bytes 21 | Number of interfaces: 1 22 | Configuration number: 1 23 | Configuration string: (none) 24 | Attributes: 0xC0 25 | Max power: 100mA 26 | Interface 0: Application Specific Interface 27 | Interface descriptor 28 | Length: 9 bytes 29 | Type: 0x04 30 | Interface number: 0 31 | Alternate setting: 0 32 | Number of endpoints: 0 33 | Class: 0xFE: Application Specific Interface 34 | Subclass: 0x01: Device Firmware Update 35 | Protocol: 0x01 36 | Interface string: #4 'DFU' 37 | Class descriptor 0x21, 9 bytes 38 | -------------------------------------------------------------------------------- /wix/manual-licenses/LICENSE-ryu-1.0.18.txt: -------------------------------------------------------------------------------- 1 | Boost Software License - Version 1.0 - August 17th, 2003 2 | 3 | Permission is hereby granted, free of charge, to any person or organization 4 | obtaining a copy of the software and accompanying documentation covered by 5 | this license (the "Software") to use, reproduce, display, distribute, 6 | execute, and transmit the Software, and to prepare derivative works of the 7 | Software, and to permit third-parties to whom the Software is furnished to 8 | do so, all subject to the following: 9 | 10 | The copyright notices in the Software and this entire statement, including 11 | the above license grant, this restriction and the following disclaimer, 12 | must be included in all copies of the Software, in whole or in part, and 13 | all derivative works of the Software, unless such copies or derivative 14 | works are solely in the form of machine-executable object code generated by 15 | a source language processor. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /src/fuzzer.rs: -------------------------------------------------------------------------------- 1 | //! Fuzz the USB packet decoder. 2 | 3 | #![allow(dead_code)] 4 | #![no_main] 5 | 6 | #[macro_use] 7 | extern crate bitfield; 8 | 9 | use std::sync::Arc; 10 | use std::fs::File; 11 | 12 | use anyhow::Error; 13 | use libfuzzer_sys::{arbitrary::{Arbitrary, Unstructured}, fuzz_target}; 14 | 15 | pub mod built { include!(concat!(env!("OUT_DIR"), "/built.rs")); } 16 | mod capture; 17 | mod database; 18 | mod decoder; 19 | mod event; 20 | mod file; 21 | mod usb; 22 | mod util; 23 | mod version; 24 | 25 | use capture::{CaptureMetadata, create_capture}; 26 | use decoder::Decoder; 27 | use file::{GenericSaver, PcapSaver}; 28 | 29 | fuzz_target!(|data: &[u8]| { 30 | let mut input = Unstructured::new(data); 31 | let packets = Vec::<(Vec::, u32)>::arbitrary(&mut input).unwrap(); 32 | let mut timestamp = u32::arbitrary(&mut input).unwrap() as u64; 33 | let (writer, _reader) = create_capture().unwrap(); 34 | let mut decoder = Decoder::new(writer).unwrap(); 35 | let metadata = Arc::new(CaptureMetadata::default()); 36 | let file = File::create("fuzz.pcap").unwrap(); 37 | let mut saver = PcapSaver::new(file, metadata).unwrap(); 38 | let handle_packets = || { 39 | for (packet, time_delta) in packets { 40 | timestamp += time_delta as u64; 41 | saver.add_packet(&packet, timestamp)?; 42 | decoder.handle_raw_packet(&packet, timestamp)?; 43 | } 44 | Ok(()) 45 | }; 46 | let result: Result<(), Error> = handle_packets(); 47 | saver.close().unwrap(); 48 | result.unwrap(); 49 | }); 50 | 51 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2022-2024, Great Scott Gadgets 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /appimage/packetry.AppDir/usr/share/doc/libgraphene-1.0-0/copyright: -------------------------------------------------------------------------------- 1 | Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: graphene 3 | Source: https://github.com/ebassi/graphene/releases 4 | Files-Excluded: 5 | subprojects/mutest/docs/markdeep.min.js 6 | subprojects/mutest/docs/markdeep.js 7 | 8 | Files: * 9 | Copyright: 2014-2019 Emmanuele Bassi 10 | License: Expat 11 | 12 | Files: debian/* 13 | Copyright: 14 | 2017-2019 Jeremy Bicha 15 | 2017 Laurent Bigonville 16 | 2019 Simon McVittie 17 | License: Expat 18 | 19 | License: Expat 20 | Permission is hereby granted, free of charge, to any person obtaining 21 | a copy of this software and associated documentation files (the 22 | "Software"), to deal in the Software without restriction, including 23 | without limitation the rights to use, copy, modify, merge, publish, 24 | distribute, sublicense, and/or sell copies of the Software, and to 25 | permit persons to whom the Software is furnished to do so, subject to 26 | the following conditions: 27 | . 28 | The above copyright notice and this permission notice shall be included 29 | in all copies or substantial portions of the Software. 30 | . 31 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 32 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 33 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 34 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 35 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 36 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 37 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 38 | -------------------------------------------------------------------------------- /tests/analyzer-test-bad-cable/devices-reference.txt: -------------------------------------------------------------------------------- 1 | Device 1: USB Analyzer Test Device 2 | Device descriptor 3 | Length: 18 bytes 4 | Type: 0x01 5 | USB Version: 2.00 6 | Class: 0x00: (Defined at Interface level) 7 | Subclass: 0x00 8 | Protocol: 0x00 9 | Max EP0 packet size: 64 bytes 10 | Vendor ID: 0x1209: Generic 11 | Product ID: 0x000A: pid.codes Test PID 12 | Version: 0.01 13 | Manufacturer string: #1 'Cynthion Project' 14 | Product string: #2 'USB Analyzer Test Device' 15 | Serial string: (none) 16 | Configuration 1 17 | Configuration descriptor 18 | Length: 9 bytes 19 | Type: 0x02 20 | Total length: 25 bytes 21 | Number of interfaces: 1 22 | Configuration number: 1 23 | Configuration string: (none) 24 | Attributes: 0x80 25 | Max power: 500mA 26 | Interface 0: Vendor Specific Class 27 | Interface descriptor 28 | Length: 9 bytes 29 | Type: 0x04 30 | Interface number: 0 31 | Alternate setting: 0 32 | Number of endpoints: 1 33 | Class: 0xFF: Vendor Specific Class 34 | Subclass: 0xFF: Vendor Specific Subclass 35 | Protocol: 0xFF: Vendor Specific Protocol 36 | Interface string: (none) 37 | Endpoint 1 IN (interrupt) 38 | Endpoint descriptor 39 | Length: 7 bytes 40 | Type: 0x05 41 | Endpoint address: 0x81 42 | Attributes: 0x03 43 | Max packet size: 512 bytes 44 | Interval: 0x05 45 | -------------------------------------------------------------------------------- /tests/ls-keepalive-divided-transaction/devices-reference.txt: -------------------------------------------------------------------------------- 1 | Device 25: USB Analyzer Test Device 2 | Device descriptor 3 | Length: 18 bytes 4 | Type: 0x01 5 | USB Version: 2.00 6 | Class: 0x00: (Defined at Interface level) 7 | Subclass: 0x00 8 | Protocol: 0x00 9 | Max EP0 packet size: 8 bytes 10 | Vendor ID: 0x1209: Generic 11 | Product ID: 0x000A: pid.codes Test PID 12 | Version: 0.01 13 | Manufacturer string: #1 'Cynthion Project' 14 | Product string: #2 'USB Analyzer Test Device' 15 | Serial string: (none) 16 | Configuration 1 17 | Configuration descriptor 18 | Length: 9 bytes 19 | Type: 0x02 20 | Total length: 25 bytes 21 | Number of interfaces: 1 22 | Configuration number: 1 23 | Configuration string: (none) 24 | Attributes: 0x80 25 | Max power: 500mA 26 | Interface 0: Vendor Specific Class 27 | Interface descriptor 28 | Length: 9 bytes 29 | Type: 0x04 30 | Interface number: 0 31 | Alternate setting: 0 32 | Number of endpoints: 1 33 | Class: 0xFF: Vendor Specific Class 34 | Subclass: 0xFF: Vendor Specific Subclass 35 | Protocol: 0xFF: Vendor Specific Protocol 36 | Interface string: (none) 37 | Endpoint 3 IN (interrupt) 38 | Endpoint descriptor 39 | Length: 7 bytes 40 | Type: 0x05 41 | Endpoint address: 0x83 42 | Attributes: 0x03 43 | Max packet size: 8 bytes 44 | Interval: 0x05 45 | -------------------------------------------------------------------------------- /src/cli.rs: -------------------------------------------------------------------------------- 1 | //! CLI wrapper program. 2 | //! 3 | //! Necessary on Windows, where the GUI binary cannot have console output. 4 | 5 | use std::process::{Command, ExitCode}; 6 | 7 | #[cfg(unix)] 8 | use std::os::unix::process::ExitStatusExt; 9 | 10 | #[cfg(windows)] 11 | const MAIN_BINARY: &str = "packetry.exe"; 12 | #[cfg(not(windows))] 13 | const MAIN_BINARY: &str = "packetry"; 14 | 15 | fn main() -> ExitCode { 16 | // Find the main Packetry executable. 17 | let packetry_binary_path = std::env::current_exe() 18 | .expect("Failed to find path to current executable") 19 | .parent() 20 | .expect("Failed to find parent directory of current executable") 21 | .join(MAIN_BINARY); 22 | 23 | // Prepare to call it, passing through all arguments we were passed. 24 | let mut command = Command::new(packetry_binary_path); 25 | command.args(std::env::args().skip(1)); 26 | 27 | // If on Windows, tell the child that it needs to attach to our console. 28 | #[cfg(windows)] 29 | command.env("PACKETRY_ATTACH_CONSOLE", "1"); 30 | 31 | // Spawn the main binary as a child process, and wait for its exit status. 32 | let exit_status = command 33 | .status() 34 | .expect("Failed to start main packetry binary"); 35 | 36 | // Try to exit with the same code the child did. 37 | match exit_status.code() { 38 | Some(code) => ExitCode::from(code as u8), 39 | None => { 40 | #[cfg(unix)] 41 | if let Some(signal) = exit_status.signal() { 42 | panic!("Packetry was terminated by signal {signal}"); 43 | } 44 | panic!("Packetry was terminated without an exit code"); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /tests/mouse/devices-reference.txt: -------------------------------------------------------------------------------- 1 | Device 4: USB Optical Mouse 2 | Device descriptor 3 | Length: 18 bytes 4 | Type: 0x01 5 | USB Version: 2.00 6 | Class: 0x00: (Defined at Interface level) 7 | Subclass: 0x00 8 | Protocol: 0x00 9 | Max EP0 packet size: 8 bytes 10 | Vendor ID: 0x1BCF: Sunplus Innovation Technology Inc. 11 | Product ID: 0x0005: Optical Mouse 12 | Version: 0.14 13 | Manufacturer string: (none) 14 | Product string: #2 'USB Optical Mouse' 15 | Serial string: (none) 16 | Configuration 1 17 | Configuration descriptor 18 | Length: 9 bytes 19 | Type: 0x02 20 | Total length: 34 bytes 21 | Number of interfaces: 1 22 | Configuration number: 1 23 | Configuration string: (none) 24 | Attributes: 0xA0 25 | Max power: 98mA 26 | Interface 0: Human Interface Device 27 | Interface descriptor 28 | Length: 9 bytes 29 | Type: 0x04 30 | Interface number: 0 31 | Alternate setting: 0 32 | Number of endpoints: 1 33 | Class: 0x03: Human Interface Device 34 | Subclass: 0x01: Boot Interface Subclass 35 | Protocol: 0x02: Mouse 36 | Interface string: (none) 37 | HID descriptor 38 | Length: 9 bytes 39 | Type: 0x21 40 | HID Version: 1.10 41 | Country code: 0x00: Not supported 42 | Available descriptors 43 | HID report descriptor, 75 bytes 44 | Endpoint 1 IN (interrupt) 45 | Endpoint descriptor 46 | Length: 7 bytes 47 | Type: 0x05 48 | Endpoint address: 0x81 49 | Attributes: 0x03 50 | Max packet size: 7 bytes 51 | Interval: 0x0A 52 | -------------------------------------------------------------------------------- /src/version.rs: -------------------------------------------------------------------------------- 1 | //! Version information. 2 | 3 | use std::fmt::Write; 4 | 5 | use crate::built::*; 6 | 7 | pub fn version() -> String { 8 | const MOD: &str = match GIT_DIRTY { 9 | Some(true) => "-modified", 10 | Some(false) | None => "" 11 | }; 12 | 13 | match GIT_VERSION { 14 | None => PKG_VERSION.to_string(), 15 | Some(description) => format!("{PKG_VERSION} (git {description}{MOD})") 16 | } 17 | } 18 | 19 | pub fn version_info(with_dependencies: bool) -> String { 20 | let gtk_version = format!("{}.{}.{}", 21 | gtk::major_version(), 22 | gtk::minor_version(), 23 | gtk::micro_version()); 24 | 25 | const COMMIT: &str = match GIT_COMMIT_HASH { 26 | Some(commit) => commit, 27 | None => "unknown" 28 | }; 29 | 30 | const MODIFIED: &str = match GIT_DIRTY { 31 | Some(true) => " (modified)", 32 | Some(false) | None => "" 33 | }; 34 | 35 | #[allow(clippy::const_is_empty)] 36 | const FEATURE_LIST: &str = if FEATURES.is_empty() { 37 | "(none)" 38 | } else { 39 | FEATURES_LOWERCASE_STR 40 | }; 41 | 42 | const DEBUG_STR: &str = if DEBUG {"yes"} else {"no"}; 43 | 44 | let output = format!("\ 45 | Runtime information: 46 | GTK version: {gtk_version} 47 | 48 | Packetry build information: 49 | Git commit: {COMMIT}{MODIFIED} 50 | Cargo package version: {PKG_VERSION} 51 | Enabled features: {FEATURE_LIST} 52 | 53 | Rust compiler: 54 | Version: {RUSTC_VERSION} 55 | Target: {TARGET} ({CFG_ENDIAN}-endian, {CFG_POINTER_WIDTH}-bit) 56 | Optimization level: {OPT_LEVEL} 57 | Debug build: {DEBUG_STR}"); 58 | 59 | if with_dependencies { 60 | DEPENDENCIES 61 | .iter() 62 | .fold( 63 | format!("{output}\n\nBuilt with dependencies:"), 64 | |mut string, (pkg, ver)| { 65 | write!(string, "\n {pkg} {ver}").unwrap(); 66 | string 67 | } 68 | ) 69 | } else { 70 | output 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /wix/generate_components.py: -------------------------------------------------------------------------------- 1 | from contextlib import redirect_stdout 2 | import os 3 | 4 | dll_components = open('wix/dll-components.wxi', 'w') 5 | dll_references = open('wix/dll-references.wxi', 'w') 6 | license_components = open('wix/license-components.wxi', 'w') 7 | license_references = open('wix/license-references.wxi', 'w') 8 | 9 | output_files = [ 10 | dll_components, 11 | dll_references, 12 | license_components, 13 | license_references 14 | ] 15 | 16 | def component_name(filename): 17 | return filename.replace('-', '_').replace('+', '_') 18 | 19 | for file in output_files: 20 | print("", file=file) 21 | 22 | bin_dir = '$(env.VCPKG_INSTALLED_DIR)/x64-windows/bin' 23 | 24 | for line in open('wix/required-dlls.txt', 'r'): 25 | filename, guid = line.rstrip().split(' ') 26 | component = component_name(filename) 27 | with redirect_stdout(dll_components): 28 | print(f" ") 29 | print(f" ") 33 | print(f" ") 34 | with redirect_stdout(dll_references): 35 | print(f" ") 36 | 37 | for filename in os.listdir('wix/full-licenses'): 38 | component = component_name(filename) 39 | with redirect_stdout(license_components): 40 | print(f" ") 41 | print(f" ") 45 | print(f" ") 46 | with redirect_stdout(license_references): 47 | print(f" ") 48 | 49 | for file in output_files: 50 | print("", file=file) 51 | -------------------------------------------------------------------------------- /tests/hackrf-connect/devices-reference.txt: -------------------------------------------------------------------------------- 1 | Device 29: HackRF One 2 | Device descriptor 3 | Length: 18 bytes 4 | Type: 0x01 5 | USB Version: 2.00 6 | Class: 0x00: (Defined at Interface level) 7 | Subclass: 0x00 8 | Protocol: 0x00 9 | Max EP0 packet size: 64 bytes 10 | Vendor ID: 0x1D50: OpenMoko, Inc. 11 | Product ID: 0x6089: Great Scott Gadgets HackRF One SDR 12 | Version: 1.06 13 | Manufacturer string: #1 'Great Scott Gadgets' 14 | Product string: #2 'HackRF One' 15 | Serial string: #4 '0000000000000000325866e6215c4023' 16 | Configuration 1 17 | Configuration descriptor 18 | Length: 9 bytes 19 | Type: 0x02 20 | Total length: 32 bytes 21 | Number of interfaces: 1 22 | Configuration number: 1 23 | Configuration string: #3 'Transceiver' 24 | Attributes: 0x80 25 | Max power: 500mA 26 | Interface 0: Vendor Specific Class 27 | Interface descriptor 28 | Length: 9 bytes 29 | Type: 0x04 30 | Interface number: 0 31 | Alternate setting: 0 32 | Number of endpoints: 2 33 | Class: 0xFF: Vendor Specific Class 34 | Subclass: 0xFF: Vendor Specific Subclass 35 | Protocol: 0xFF: Vendor Specific Protocol 36 | Interface string: (none) 37 | Endpoint 1 IN (bulk) 38 | Endpoint descriptor 39 | Length: 7 bytes 40 | Type: 0x05 41 | Endpoint address: 0x81 42 | Attributes: 0x02 43 | Max packet size: 512 bytes 44 | Interval: 0x00 45 | Endpoint 2 OUT (bulk) 46 | Endpoint descriptor 47 | Length: 7 bytes 48 | Type: 0x05 49 | Endpoint address: 0x02 50 | Attributes: 0x02 51 | Max packet size: 512 bytes 52 | Interval: 0x00 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Packetry 2 | 3 | A fast, intuitive USB 2.0 protocol analysis application for use with [Cynthion](https://greatscottgadgets.com/cynthion/). 4 | 5 | ![Screenshot of Packetry](screenshot.png) 6 | 7 | ## Documentation 8 | 9 | Read the latest [Packetry Documentation](https://packetry.readthedocs.io/). 10 | 11 | ## Development 12 | 13 | Packetry is written in [Rust](https://rust-lang.org/), with its GUI using [GTK 4](https://gtk.org) via the [gtk-rs](https://gtk-rs.org/) bindings. 14 | 15 | To build it, you need a working Rust development environment. The minimum supported Rust version is 1.85.1. 16 | 17 | You must also have the GTK 4 headers installed and discoverable via `pkg-config`, as this is required for Rust to build the gtk-rs crates. 18 | 19 | ### Building and running 20 | 21 | To build, run `cargo build` after installing the necessary prerequisites (see below). Run with `cargo run`. 22 | 23 | If you pass a capture filename as an argument, Packetry will attempt to load it. The current supported file format is a `.pcap` file with the `LINKTYPE_USB_2_0` link layer header type. 24 | 25 | Note: Do not build with `--all-features`. All the optional features currently in the package are for debug/test purposes only, and will prevent or degrade normal use of the application if enabled. See `Cargo.toml` for details. 26 | 27 | ### Installing prerequisites 28 | 29 | #### Linux 30 | 31 | Install the Rust build tools, other essential build tools, and GTK 4 headers. 32 | 33 | On Debian based systems it may be sufficient to use the command: 34 | 35 | `apt install rustc cargo build-essential libgtk-4-dev` 36 | 37 | For Fedora systems: 38 | 39 | `yum install rust cargo make gcc gcc-c++ gtk4-devel pango-devel` 40 | 41 | For other distributions, a similar set of packages should be required. 42 | 43 | Note that Packetry requires a minimum Rust version of 1.85.1. If your distribution's packages are older than this, use [rustup](https://rustup.rs/) to get the latest Rust toolchain and manage your Rust installation. 44 | 45 | #### macOS 46 | 47 | Install Rust with [rustup](https://rustup.rs/), and install [Homebrew](https://brew.sh/). 48 | 49 | Install GTK 4 with `brew install gtk4`. 50 | 51 | #### Windows 52 | 53 | Follow the installation instructions from [GUI development with Rust and GTK 4](https://gtk-rs.org/gtk4-rs/stable/latest/book/installation_windows.html). 54 | 55 | You can use either the MSVC or GNU toolchains. 56 | -------------------------------------------------------------------------------- /wix/required-dlls.txt: -------------------------------------------------------------------------------- 1 | atk-1.0-0.dll 5edbbda4-db12-4e43-8f0c-218ebab94d93 2 | brotlicommon.dll 0be65eb3-8928-4817-925a-5550e666683b 3 | brotlidec.dll a1aa709a-a611-47bf-91d4-2f655270bec5 4 | brotlienc.dll ef53d54e-d61b-4e7f-8a2b-c8347bd0884e 5 | bz2.dll 4a8a184b-11a9-4493-a709-7d0ec8614f14 6 | cairo-2.dll d8ce6c10-20fc-4128-a9ac-6ef047cb96e2 7 | cairo-gobject-2.dll 9992fb8f-f693-4dcd-a387-1f86b4b41aac 8 | cairo-script-interpreter-2.dll 75cc1bb6-d916-4f82-aea3-1179f5eb7ba5 9 | charset-1.dll 56827d0e-68e1-4e4b-8278-659184d9e570 10 | epoxy-0.dll 9aeef0ec-6c93-480d-a44c-ced8dfabfb2f 11 | ffi-8.dll 7c63276a-0848-4f46-96fa-8df568a2bb61 12 | fontconfig-1.dll aa8ad7de-59cc-435c-97cb-e328bbff8460 13 | freetype.dll 74abc4a2-df13-48bb-8696-96f92a2c0be0 14 | fribidi-0.dll e462dcd8-2226-4f73-9f7e-d2b3b58eba49 15 | gdk_pixbuf-2.0-0.dll c24a4c43-def6-4c24-8cb8-54ece1b6bb9c 16 | getopt.dll f7d09e7c-fccc-4558-b478-5f8a3e88e4e4 17 | gio-2.0-0.dll a91cae44-99b8-4d47-9682-7985abc3bbe1 18 | glib-2.0-0.dll 9c81fa23-c96b-4802-aab0-3401300d7bc3 19 | gmodule-2.0-0.dll 6bf333ce-3f3a-49ba-b035-fc952b63725b 20 | gobject-2.0-0.dll 7bc6b550-918a-4d1b-91d9-358f02a242ba 21 | graphene-1.0-0.dll 2874cda9-9885-4495-9763-38a51a37b9d8 22 | gthread-2.0-0.dll 4c314fb1-7224-48e2-af39-81984fa6c19e 23 | gtk-4-1.dll 6fe10843-e106-4883-bf29-a6a94cd2b1cd 24 | harfbuzz.dll a12abb2a-7edf-40d6-adb2-39cd5a9d8ef8 25 | harfbuzz-gobject.dll 202cb447-b82f-47c0-9356-c353b675fd3d 26 | harfbuzz-subset.dll 58296888-07a8-4258-9054-5fa089e3c855 27 | iconv-2.dll 18abffeb-462a-4186-8b8a-0149c8b9f062 28 | intl-8.dll 628852d1-0ccd-40cc-84f4-ae6fee75db16 29 | jpeg62.dll 086f589c-d8af-474d-9564-8d65ef6f4401 30 | libexpat.dll b7c975d0-6e53-49b4-b7ed-c75daad6616e 31 | liblzma.dll abd4caa2-4acd-4ec1-85a1-8d621c672529 32 | libpng16.dll b489d04b-74d9-4001-b185-0573968a6da2 33 | pango-1.0-0.dll 9e6b8c45-d189-4b8a-aaa9-1f83c92421c2 34 | pangocairo-1.0-0.dll fde9897b-09a2-4201-94f7-2284dcc7246f 35 | pangoft2-1.0-0.dll d1b0f76f-747a-4e81-9c98-24c91d7c7e21 36 | pangowin32-1.0-0.dll 1e350ffc-f0e7-4739-aeb4-2c0f93616259 37 | pcre2-16.dll 34b8d08a-979f-41be-a894-0ac7ec47e670 38 | pcre2-32.dll 86190ec9-596b-412b-b005-d24256086457 39 | pcre2-8.dll 32f1711c-a8f7-4f09-890e-eb3a69347b7d 40 | pcre2-posix.dll c153933b-e57e-432a-9a29-ff8a4889615e 41 | pixman-1-0.dll 1c88f629-1474-4dd5-9b23-7b6dcfb4143c 42 | pthreadVC3.dll 88992acc-c1bd-4d6c-8155-ce1e07cd66af 43 | pthreadVCE3.dll efeab01d-ac1e-42be-8cb1-75b0a419503f 44 | pthreadVSE3.dll 09cc7e5d-ebc3-42bb-9bd9-26a9bdac449e 45 | sass-1.dll e67bbd5b-edf9-4f2b-a04a-241e628ba95f 46 | tiff.dll f181c65d-b7fc-41ab-9fcf-49b3a5996ba3 47 | turbojpeg.dll 8cfa1139-79e4-4e80-9c76-fbe5d404d9c5 48 | zlib1.dll e10ca69b-ce5e-4368-bc78-5109ba0e6b75 49 | -------------------------------------------------------------------------------- /wix/vcpkg_licenses.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import shutil 3 | import json 4 | import re 5 | import os 6 | 7 | dest_dir = 'wix/full-licenses' 8 | 9 | vcpkg_result = subprocess.run( 10 | ['./vcpkg/vcpkg', 'depend-info', 'gtk', '--triplet=x64-windows'], 11 | capture_output=True) 12 | 13 | vcpkg_result.check_returncode() 14 | 15 | packages = set() 16 | 17 | # Find all packages we depend on. 18 | for line in vcpkg_result.stderr.decode().rstrip().split('\n'): 19 | header, remainder = line.split(': ') 20 | package = header.split('[')[0] 21 | packages.add(package) 22 | dependencies = remainder.split(', ') 23 | packages |= set(dependencies) 24 | 25 | # Discard empty package names. 26 | packages.discard('') 27 | 28 | # Discard vcpkg build tools. 29 | packages = set(p for p in packages if not p.startswith('vcpkg-')) 30 | 31 | # gperf is needed to build fontconfig, but is not linked to. 32 | packages.discard('gperf') 33 | 34 | # gettext is needed to build many dependencies, but is not linked to. 35 | packages.discard('gettext') 36 | 37 | # getopt and pthread are virtual packages. 38 | packages.discard('getopt') 39 | packages.discard('pthread') 40 | 41 | # sassc is used to build GTK, but is not linked to. 42 | packages.discard('sassc') 43 | 44 | versions = {} 45 | 46 | # These packages are missing license information in vcpkg. 47 | licenses = { 48 | 'libiconv': 'LGPL-2.1-or-later', 49 | 'egl-registry': 'Apache-2.0 OR MIT', 50 | 'liblzma': 'BSD-0-Clause', 51 | 'libsass': 'MIT', 52 | } 53 | 54 | # These packages are missing homepage information in vcpkg. 55 | homepages = { 56 | 'fribidi': 'https://github.com/fribidi/fribidi', 57 | } 58 | 59 | for package in packages: 60 | metadata = json.load(open(f'vcpkg/ports/{package}/vcpkg.json')) 61 | version = metadata.get('version-semver', 62 | metadata.get('version', 63 | metadata.get('version-date'))) 64 | if version is None: 65 | raise ValueError(f"Couldn't find a version for package {package}") 66 | versions[package] = version 67 | if metadata.get('homepage') is not None: 68 | homepages[package] = metadata['homepage'] 69 | if metadata.get('license') is not None: 70 | licenses[package] = metadata['license'] 71 | 72 | try: 73 | os.mkdir(dest_dir) 74 | except FileExistsError: 75 | pass 76 | 77 | print("The following libraries are dynamically linked into Packetry:") 78 | 79 | for package in sorted(packages): 80 | license_src = f'vcpkg/installed/x64-windows/share/{package}/copyright' 81 | license_dest = os.path.join(dest_dir, f'LICENSE-{package}.txt') 82 | shutil.copyfile(license_src, license_dest) 83 | print() 84 | print(f"{package} version {versions[package]}") 85 | print(f"Homepage: {homepages[package]}") 86 | print(f"License type: {licenses[package]}") 87 | print(f"License text: {license_dest}") 88 | -------------------------------------------------------------------------------- /tests/split-enum/devices-reference.txt: -------------------------------------------------------------------------------- 1 | Device 12: Unknown 2 | No device descriptor 3 | Device 14: USB Device 4 | Device descriptor 5 | Length: 18 bytes 6 | Type: 0x01 7 | USB Version: 2.00 8 | Class: 0x00: (Defined at Interface level) 9 | Subclass: 0x00 10 | Protocol: 0x00 11 | Max EP0 packet size: 8 bytes 12 | Vendor ID: 0x0C45: Microdia 13 | Product ID: 0x7403: Foot Switch 14 | Version: 0.01 15 | Manufacturer string: #1 (not seen) 16 | Product string: #2 'USB Device' 17 | Serial string: (none) 18 | Configuration 1 19 | Configuration descriptor 20 | Length: 9 bytes 21 | Type: 0x02 22 | Total length: 59 bytes 23 | Number of interfaces: 2 24 | Configuration number: 1 25 | Configuration string: (none) 26 | Attributes: 0xA0 27 | Max power: 100mA 28 | Interface 0: Human Interface Device 29 | Interface descriptor 30 | Length: 9 bytes 31 | Type: 0x04 32 | Interface number: 0 33 | Alternate setting: 0 34 | Number of endpoints: 1 35 | Class: 0x03: Human Interface Device 36 | Subclass: 0x01: Boot Interface Subclass 37 | Protocol: 0x01: Keyboard 38 | Interface string: (none) 39 | HID descriptor 40 | Length: 9 bytes 41 | Type: 0x21 42 | HID Version: 1.00 43 | Country code: 0x00: Not supported 44 | Available descriptors 45 | HID report descriptor, 77 bytes 46 | Endpoint 1 IN (interrupt) 47 | Endpoint descriptor 48 | Length: 7 bytes 49 | Type: 0x05 50 | Endpoint address: 0x81 51 | Attributes: 0x03 52 | Max packet size: 8 bytes 53 | Interval: 0x0A 54 | Interface 1: Human Interface Device 55 | Interface descriptor 56 | Length: 9 bytes 57 | Type: 0x04 58 | Interface number: 1 59 | Alternate setting: 0 60 | Number of endpoints: 1 61 | Class: 0x03: Human Interface Device 62 | Subclass: 0x01: Boot Interface Subclass 63 | Protocol: 0x02: Mouse 64 | Interface string: (none) 65 | HID descriptor 66 | Length: 9 bytes 67 | Type: 0x21 68 | HID Version: 1.00 69 | Country code: 0x00: Not supported 70 | Available descriptors 71 | HID report descriptor, 91 bytes 72 | Endpoint 2 IN (interrupt) 73 | Endpoint descriptor 74 | Length: 7 bytes 75 | Type: 0x05 76 | Endpoint address: 0x82 77 | Attributes: 0x03 78 | Max packet size: 5 bytes 79 | Interval: 0x0A 80 | -------------------------------------------------------------------------------- /tests/ls-keepalive-more-divided-transactions/devices-reference.txt: -------------------------------------------------------------------------------- 1 | Device 58: QuickFire Rapid keyboard 2 | Device descriptor 3 | Length: 18 bytes 4 | Type: 0x01 5 | USB Version: 1.10 6 | Class: 0x00: (Defined at Interface level) 7 | Subclass: 0x00 8 | Protocol: 0x00 9 | Max EP0 packet size: 8 bytes 10 | Vendor ID: 0x2516: Cooler Master Co., Ltd. 11 | Product ID: 0x0004: Storm QuickFire Rapid Mechanical Keyboard 12 | Version: 0.01 13 | Manufacturer string: #1 'CM Storm' 14 | Product string: #2 'QuickFire Rapid keyboard' 15 | Serial string: (none) 16 | Configuration 1 17 | Configuration descriptor 18 | Length: 9 bytes 19 | Type: 0x02 20 | Total length: 59 bytes 21 | Number of interfaces: 2 22 | Configuration number: 1 23 | Configuration string: (none) 24 | Attributes: 0xA0 25 | Max power: 100mA 26 | Interface 0: Human Interface Device 27 | Interface descriptor 28 | Length: 9 bytes 29 | Type: 0x04 30 | Interface number: 0 31 | Alternate setting: 0 32 | Number of endpoints: 1 33 | Class: 0x03: Human Interface Device 34 | Subclass: 0x01: Boot Interface Subclass 35 | Protocol: 0x01: Keyboard 36 | Interface string: (none) 37 | HID descriptor 38 | Length: 9 bytes 39 | Type: 0x21 40 | HID Version: 1.10 41 | Country code: 0x00: Not supported 42 | Available descriptors 43 | HID report descriptor, 62 bytes 44 | Endpoint 1 IN (interrupt) 45 | Endpoint descriptor 46 | Length: 7 bytes 47 | Type: 0x05 48 | Endpoint address: 0x81 49 | Attributes: 0x03 50 | Max packet size: 8 bytes 51 | Interval: 0x01 52 | Interface 1: Human Interface Device 53 | Interface descriptor 54 | Length: 9 bytes 55 | Type: 0x04 56 | Interface number: 1 57 | Alternate setting: 0 58 | Number of endpoints: 1 59 | Class: 0x03: Human Interface Device 60 | Subclass: 0x01: Boot Interface Subclass 61 | Protocol: 0x02: Mouse 62 | Interface string: (none) 63 | HID descriptor 64 | Length: 9 bytes 65 | Type: 0x21 66 | HID Version: 1.10 67 | Country code: 0x00: Not supported 68 | Available descriptors 69 | HID report descriptor, 166 bytes 70 | Endpoint 2 IN (interrupt) 71 | Endpoint descriptor 72 | Length: 7 bytes 73 | Type: 0x05 74 | Endpoint address: 0x82 75 | Attributes: 0x03 76 | Max packet size: 8 bytes 77 | Interval: 0x01 78 | -------------------------------------------------------------------------------- /tests/split-poll/reference.txt: -------------------------------------------------------------------------------- 1 | ○── Polling 4 times for interrupt transfer on endpoint 14.1 IN 2 | ├──── Starting IN transaction on 14.1 3 | │ ├── SPLIT packet starting low speed interrupt transaction on hub 12 port 2 4 | │ └── IN packet on 14.1, CRC 0A 5 | ├──── Completing IN transaction on 14.1, NAK 6 | │ ├── SPLIT packet completing low speed interrupt transaction on hub 12 port 2 7 | │ ├── IN packet on 14.1, CRC 0A 8 | │ └── NAK packet 9 | ├──── Starting IN transaction on 14.1 10 | │ ├── SPLIT packet starting low speed interrupt transaction on hub 12 port 2 11 | │ └── IN packet on 14.1, CRC 0A 12 | ├──── Completing IN transaction on 14.1, NAK 13 | │ ├── SPLIT packet completing low speed interrupt transaction on hub 12 port 2 14 | │ ├── IN packet on 14.1, CRC 0A 15 | │ └── NAK packet 16 | ├──── Starting IN transaction on 14.1 17 | │ ├── SPLIT packet starting low speed interrupt transaction on hub 12 port 2 18 | │ └── IN packet on 14.1, CRC 0A 19 | ├──── Completing IN transaction on 14.1, NAK 20 | │ ├── SPLIT packet completing low speed interrupt transaction on hub 12 port 2 21 | │ ├── IN packet on 14.1, CRC 0A 22 | │ └── NAK packet 23 | ├──── Starting IN transaction on 14.1 24 | │ ├── SPLIT packet starting low speed interrupt transaction on hub 12 port 2 25 | │ └── IN packet on 14.1, CRC 0A 26 | ├──── Completing IN transaction on 14.1, NAK 27 | │ ├── SPLIT packet completing low speed interrupt transaction on hub 12 port 2 28 | │ ├── IN packet on 14.1, CRC 0A 29 | │ └── NAK packet 30 | │○─ Polling 4 times for interrupt transfer on endpoint 14.2 IN 31 | │├─── Starting IN transaction on 14.2 32 | ││ ├── SPLIT packet starting low speed interrupt transaction on hub 12 port 2 33 | ││ └── IN packet on 14.2, CRC 19 34 | │├─── Completing IN transaction on 14.2, NAK 35 | ││ ├── SPLIT packet completing low speed interrupt transaction on hub 12 port 2 36 | ││ ├── IN packet on 14.2, CRC 19 37 | ││ └── NAK packet 38 | │├─── Starting IN transaction on 14.2 39 | ││ ├── SPLIT packet starting low speed interrupt transaction on hub 12 port 2 40 | ││ └── IN packet on 14.2, CRC 19 41 | │├─── Completing IN transaction on 14.2, NAK 42 | ││ ├── SPLIT packet completing low speed interrupt transaction on hub 12 port 2 43 | ││ ├── IN packet on 14.2, CRC 19 44 | ││ └── NAK packet 45 | │├─── Starting IN transaction on 14.2 46 | ││ ├── SPLIT packet starting low speed interrupt transaction on hub 12 port 2 47 | ││ └── IN packet on 14.2, CRC 19 48 | │├─── Completing IN transaction on 14.2, NAK 49 | ││ ├── SPLIT packet completing low speed interrupt transaction on hub 12 port 2 50 | ││ ├── IN packet on 14.2, CRC 19 51 | ││ └── NAK packet 52 | │├─── Starting IN transaction on 14.2 53 | ││ ├── SPLIT packet starting low speed interrupt transaction on hub 12 port 2 54 | ││ └── IN packet on 14.2, CRC 19 55 | │└─── Completing IN transaction on 14.2, NAK 56 | │ ├── SPLIT packet completing low speed interrupt transaction on hub 12 port 2 57 | │ ├── IN packet on 14.2, CRC 19 58 | │ └── NAK packet 59 | -------------------------------------------------------------------------------- /src/util/id.rs: -------------------------------------------------------------------------------- 1 | //! The Id type and its traits. 2 | //! 3 | //! Used to enforce type safety of indices used in the capture database. 4 | 5 | use std::fmt::{Debug, Display}; 6 | use std::marker::PhantomData; 7 | use std::mem::size_of; 8 | use std::ops::{Add, AddAssign, Sub, SubAssign}; 9 | use std::ops::Range; 10 | 11 | #[derive(Copy, Clone)] 12 | pub struct Id { 13 | _marker: PhantomData, 14 | pub value: u64 15 | } 16 | 17 | impl Eq for Id {} 18 | 19 | impl Ord for Id { 20 | fn cmp(&self, other: &Self) -> std::cmp::Ordering { 21 | self.value.cmp(&other.value) 22 | } 23 | } 24 | 25 | impl PartialOrd for Id { 26 | fn partial_cmp(&self, other: &Self) -> Option { 27 | Some(self.cmp(other)) 28 | } 29 | } 30 | 31 | impl Display for Id { 32 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) 33 | -> Result<(), std::fmt::Error> 34 | { 35 | write!(f, "{}", self.value) 36 | } 37 | } 38 | 39 | impl Debug for Id { 40 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) 41 | -> Result<(), std::fmt::Error> 42 | { 43 | write!(f, "{}", self.value) 44 | } 45 | } 46 | 47 | impl PartialEq> for Id { 48 | fn eq(&self, other: &Id) -> bool { 49 | self.value.eq(&other.value) 50 | } 51 | } 52 | 53 | impl Add for Id { 54 | type Output = Self; 55 | 56 | fn add(self, other: u64) -> Self { 57 | Id::::from(self.value + other) 58 | } 59 | } 60 | 61 | impl AddAssign for Id { 62 | fn add_assign(&mut self, other: u64) { 63 | self.value += other 64 | } 65 | } 66 | 67 | impl Sub for Id { 68 | type Output = Self; 69 | 70 | fn sub(self, other: u64) -> Self { 71 | Id::::from(self.value - other) 72 | } 73 | } 74 | 75 | impl SubAssign for Id { 76 | fn sub_assign(&mut self, other: u64) { 77 | self.value -= other 78 | } 79 | } 80 | 81 | impl Sub> for Id { 82 | type Output = u64; 83 | 84 | fn sub(self, other: Id) -> u64 { 85 | self.value - other.value 86 | } 87 | } 88 | 89 | impl From for Id { 90 | fn from(i: u64) -> Self { 91 | Id:: { 92 | _marker: PhantomData, 93 | value: i 94 | } 95 | } 96 | } 97 | 98 | impl From> for u64 { 99 | fn from(id: Id) -> Self { 100 | id.value 101 | } 102 | } 103 | 104 | impl Id { 105 | pub const fn constant(i: u64) -> Self { 106 | Id:: { 107 | _marker: PhantomData, 108 | value: i 109 | } 110 | } 111 | 112 | pub fn from_offset(offset: u64) -> Id { 113 | Id { 114 | _marker: PhantomData, 115 | value: offset / size_of::() as u64, 116 | } 117 | } 118 | 119 | pub fn offset(&self) -> u64 { 120 | self.value * size_of::() as u64 121 | } 122 | 123 | pub fn offset_range(&self) -> Range { 124 | let size = size_of::() as u64; 125 | let start = self.value * size; 126 | let end = start + size; 127 | start..end 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /tests/hid/amp/reference.txt: -------------------------------------------------------------------------------- 1 | 2 | ○ Input report #3 (2 bytes): 3 | ├── Volume Increment: byte 1 bit 0 4 | ├── Volume Decrement: byte 1 bit 1 5 | ├── Play/Pause: byte 1 bit 2 6 | ├── Voice Command: byte 1 bit 3 7 | ├── Scan Previous Track: byte 1 bit 4 8 | ├── Scan Next Track: byte 1 bit 5 9 | └── Padding: byte 1 bits 6-7 10 | 11 | ○ Input report #5 (17 bytes): 12 | ├── Consumer usage 0x00: byte 1 (values 0 to 1) 13 | ├── Consumer usage 0x00: byte 2 (values 0 to 1) 14 | ├── Consumer usage 0x00: byte 3 (values 0 to 1) 15 | ├── Consumer usage 0x00: byte 4 (values 0 to 1) 16 | ├── Consumer usage 0x00: byte 5 (values 0 to 1) 17 | ├── Consumer usage 0x00: byte 6 (values 0 to 1) 18 | ├── Consumer usage 0x00: byte 7 (values 0 to 1) 19 | ├── Consumer usage 0x00: byte 8 (values 0 to 1) 20 | ├── Consumer usage 0x00: byte 9 (values 0 to 1) 21 | ├── Consumer usage 0x00: byte 10 (values 0 to 1) 22 | ├── Consumer usage 0x00: byte 11 (values 0 to 1) 23 | ├── Consumer usage 0x00: byte 12 (values 0 to 1) 24 | ├── Consumer usage 0x00: byte 13 (values 0 to 1) 25 | ├── Consumer usage 0x00: byte 14 (values 0 to 1) 26 | ├── Consumer usage 0x00: byte 15 (values 0 to 1) 27 | └── Consumer usage 0x00: byte 16 (values 0 to 1) 28 | 29 | ○ Output report #4 (39 bytes): 30 | ├── Consumer usage 0x00: byte 1 (values 0 to 1) 31 | ├── Consumer usage 0x00: byte 2 (values 0 to 1) 32 | ├── Consumer usage 0x00: byte 3 (values 0 to 1) 33 | ├── Consumer usage 0x00: byte 4 (values 0 to 1) 34 | ├── Consumer usage 0x00: byte 5 (values 0 to 1) 35 | ├── Consumer usage 0x00: byte 6 (values 0 to 1) 36 | ├── Consumer usage 0x00: byte 7 (values 0 to 1) 37 | ├── Consumer usage 0x00: byte 8 (values 0 to 1) 38 | ├── Consumer usage 0x00: byte 9 (values 0 to 1) 39 | ├── Consumer usage 0x00: byte 10 (values 0 to 1) 40 | ├── Consumer usage 0x00: byte 11 (values 0 to 1) 41 | ├── Consumer usage 0x00: byte 12 (values 0 to 1) 42 | ├── Consumer usage 0x00: byte 13 (values 0 to 1) 43 | ├── Consumer usage 0x00: byte 14 (values 0 to 1) 44 | ├── Consumer usage 0x00: byte 15 (values 0 to 1) 45 | ├── Consumer usage 0x00: byte 16 (values 0 to 1) 46 | ├── Consumer usage 0x00: byte 17 (values 0 to 1) 47 | ├── Consumer usage 0x00: byte 18 (values 0 to 1) 48 | ├── Consumer usage 0x00: byte 19 (values 0 to 1) 49 | ├── Consumer usage 0x00: byte 20 (values 0 to 1) 50 | ├── Consumer usage 0x00: byte 21 (values 0 to 1) 51 | ├── Consumer usage 0x00: byte 22 (values 0 to 1) 52 | ├── Consumer usage 0x00: byte 23 (values 0 to 1) 53 | ├── Consumer usage 0x00: byte 24 (values 0 to 1) 54 | ├── Consumer usage 0x00: byte 25 (values 0 to 1) 55 | ├── Consumer usage 0x00: byte 26 (values 0 to 1) 56 | ├── Consumer usage 0x00: byte 27 (values 0 to 1) 57 | ├── Consumer usage 0x00: byte 28 (values 0 to 1) 58 | ├── Consumer usage 0x00: byte 29 (values 0 to 1) 59 | ├── Consumer usage 0x00: byte 30 (values 0 to 1) 60 | ├── Consumer usage 0x00: byte 31 (values 0 to 1) 61 | ├── Consumer usage 0x00: byte 32 (values 0 to 1) 62 | ├── Consumer usage 0x00: byte 33 (values 0 to 1) 63 | ├── Consumer usage 0x00: byte 34 (values 0 to 1) 64 | ├── Consumer usage 0x00: byte 35 (values 0 to 1) 65 | ├── Consumer usage 0x00: byte 36 (values 0 to 1) 66 | ├── Consumer usage 0x00: byte 37 (values 0 to 1) 67 | └── Consumer usage 0x00: byte 38 (values 0 to 1) 68 | -------------------------------------------------------------------------------- /src/ui/row_data.rs: -------------------------------------------------------------------------------- 1 | //! GObject subclasses for row data in each UI view. 2 | 3 | use gtk::glib; 4 | use gtk::subclass::prelude::*; 5 | 6 | #[cfg(any(test, feature="record-ui-test"))] 7 | use gtk::prelude::Cast; 8 | 9 | use crate::item::{TrafficItem, DeviceItem}; 10 | use crate::ui::tree_list_model::ItemNodeRc; 11 | 12 | /// Trait implemented by each of our row data types. 13 | pub trait GenericRowData where Item: Clone { 14 | /// Create a row for the given node. 15 | fn new(node: Result, String>) -> Self where Self: Sized; 16 | 17 | /// Fetch the node for this row. 18 | fn node(&self) -> Result, String>; 19 | } 20 | 21 | /// Trait for converting an arbitrary GObject to one of our row data types. 22 | pub trait ToGenericRowData { 23 | #[cfg(any(test, feature="record-ui-test"))] 24 | fn to_generic_row_data(self) -> Box>; 25 | } 26 | 27 | /// Define the outer type exposed to our Rust code. 28 | macro_rules! row_data { 29 | ($row_data: ident, $item: ident) => { 30 | glib::wrapper! { 31 | pub struct $row_data(ObjectSubclass); 32 | } 33 | 34 | impl GenericRowData<$item> for $row_data { 35 | fn new(node: Result, String>) -> $row_data{ 36 | let row: $row_data = glib::Object::new::<$row_data>(); 37 | row.imp().node.replace(Some(node)); 38 | row 39 | } 40 | 41 | fn node(&self) -> Result, String> { 42 | self.imp().node.borrow().as_ref().unwrap().clone() 43 | } 44 | } 45 | 46 | impl ToGenericRowData<$item> for glib::Object { 47 | #[cfg(any(test, feature="record-ui-test"))] 48 | fn to_generic_row_data(self) -> Box> { 49 | Box::new(self.downcast::<$row_data>().unwrap()) 50 | } 51 | } 52 | } 53 | } 54 | 55 | // Repeat the above boilerplate for each row type. 56 | row_data!(TrafficRowData, TrafficItem); 57 | row_data!(DeviceRowData, DeviceItem); 58 | 59 | /// The internal implementation module. 60 | mod imp { 61 | use gtk::glib::{self, subclass::prelude::*}; 62 | use std::cell::RefCell; 63 | 64 | use crate::item::{TrafficItem, DeviceItem}; 65 | use crate::ui::tree_list_model::ItemNodeRc; 66 | 67 | /// Define the inner type to be used in the GObject type system. 68 | macro_rules! row_data { 69 | ($row_data: ident, $item: ident) => { 70 | #[derive(Default)] 71 | pub struct $row_data { 72 | pub(super) node: RefCell, String>>>, 74 | } 75 | 76 | #[glib::object_subclass] 77 | impl ObjectSubclass for $row_data { 78 | const NAME: &'static str = stringify!($row_data); 79 | type Type = super::$row_data; 80 | } 81 | 82 | impl ObjectImpl for $row_data {} 83 | } 84 | } 85 | 86 | // Repeat the above boilerplate for each row type. 87 | row_data!(TrafficRowData, TrafficItem); 88 | row_data!(DeviceRowData, DeviceItem); 89 | } 90 | -------------------------------------------------------------------------------- /src/backend/transfer_queue.rs: -------------------------------------------------------------------------------- 1 | //! Helper type for managing a queue of USB bulk transfers. 2 | 3 | use std::sync::mpsc; 4 | 5 | use anyhow::{Context, Error}; 6 | use futures_channel::oneshot; 7 | use futures_util::{future::FusedFuture, FutureExt, select_biased}; 8 | use nusb::{Endpoint, transfer::{Buffer, Bulk, In}}; 9 | 10 | /// A queue of inbound USB transfers, feeding received data to a channel. 11 | pub struct TransferQueue { 12 | endpoint: Endpoint, 13 | data_tx: mpsc::Sender, 14 | transfer_length: usize, 15 | } 16 | 17 | impl TransferQueue { 18 | /// Create a new transfer queue. 19 | pub fn new( 20 | mut endpoint: Endpoint, 21 | data_tx: mpsc::Sender, 22 | num_transfers: usize, 23 | transfer_length: usize 24 | ) -> TransferQueue { 25 | while endpoint.pending() < num_transfers { 26 | let request = endpoint.allocate(transfer_length); 27 | endpoint.submit(request); 28 | } 29 | TransferQueue { endpoint, data_tx, transfer_length } 30 | } 31 | 32 | /// Process the queue, sending data to the channel until stopped. 33 | pub async fn process( 34 | &mut self, 35 | reuse_rx: mpsc::Receiver, 36 | mut stop_rx: oneshot::Receiver<()>, 37 | ) -> Result<(), Error> { 38 | use nusb::transfer::TransferError::Cancelled; 39 | 40 | loop { 41 | select_biased!( 42 | _ = stop_rx => { 43 | // Stop requested. Cancel all transfers. 44 | self.endpoint.cancel_all(); 45 | } 46 | completion = self.endpoint.next_complete().fuse() => { 47 | match completion.status { 48 | Ok(()) => { 49 | 50 | // Send data to decoder thread. 51 | self.data_tx.send(completion.buffer) 52 | .context( 53 | "Failed sending capture data to channel")?; 54 | if !stop_rx.is_terminated() { 55 | // See if we have a transfer ready for reuse. 56 | let buffer = reuse_rx 57 | .try_recv() 58 | .ok() 59 | .unwrap_or_else(|| 60 | self.endpoint.allocate( 61 | self.transfer_length)); 62 | // Submit next transfer. 63 | self.endpoint.submit(buffer); 64 | } 65 | }, 66 | Err(Cancelled) if stop_rx.is_terminated() => { 67 | // Transfer cancelled during shutdown. Drop it. 68 | drop(completion); 69 | if self.endpoint.pending() == 0 { 70 | // All cancellations now handled. 71 | return Ok(()); 72 | } 73 | }, 74 | Err(usb_error) => { 75 | // Transfer failed. 76 | return Err(Error::from(usb_error)); 77 | } 78 | } 79 | } 80 | ); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/util/vec_map.rs: -------------------------------------------------------------------------------- 1 | //! A fast Vec-based map type, for use where keys are small bounded integers. 2 | 3 | use std::iter::FilterMap; 4 | use std::ops::{Index, IndexMut}; 5 | use std::marker::PhantomData; 6 | use std::slice::Iter; 7 | 8 | use crate::util::id::Id; 9 | 10 | pub trait Key { 11 | fn id(self) -> usize; 12 | 13 | fn key(id: usize) -> Self; 14 | } 15 | 16 | #[derive(Clone)] 17 | pub struct VecMap where K: Key { 18 | _marker: PhantomData, 19 | vec: Vec>, 20 | } 21 | 22 | impl VecMap where K: Key { 23 | pub fn new() -> Self { 24 | VecMap:: { 25 | _marker: PhantomData, 26 | vec: Vec::new(), 27 | } 28 | } 29 | 30 | pub fn with_capacity(size: u8) -> Self { 31 | VecMap:: { 32 | _marker: PhantomData, 33 | vec: Vec::with_capacity(size as usize), 34 | } 35 | } 36 | 37 | pub fn len(&self) -> usize { 38 | self.vec.len() 39 | } 40 | 41 | pub fn push(&mut self, value: V) -> K { 42 | self.vec.push(Some(value)); 43 | K::key(self.vec.len()) 44 | } 45 | 46 | pub fn get(&self, index: K) -> Option<&V> { 47 | match self.vec.get(index.id()) { 48 | Some(opt) => opt.as_ref(), 49 | None => None 50 | } 51 | } 52 | 53 | pub fn get_mut(&mut self, index: K) -> Option<&mut V> { 54 | match self.vec.get_mut(index.id()) { 55 | Some(opt) => opt.as_mut(), 56 | None => None 57 | } 58 | } 59 | 60 | pub fn last_mut(&mut self) -> Option<&mut V> { 61 | match self.vec.len() { 62 | 0 => None, 63 | n => self.get_mut(K::key(n - 1)), 64 | } 65 | } 66 | 67 | pub fn set(&mut self, index: K, value: V) { 68 | let id = index.id(); 69 | if id >= self.vec.len() { 70 | self.vec.resize_with(id + 1, || {None}) 71 | } 72 | self.vec[id] = Some(value); 73 | } 74 | 75 | pub fn iter_pairs(&self) -> impl Iterator { 76 | let range = 0..self.vec.len(); 77 | range.filter_map(|i| self.vec[i].as_ref().map(|j| (K::key(i), j))) 78 | } 79 | } 80 | 81 | impl Default for VecMap where K: Key { 82 | fn default() -> Self { 83 | Self::new() 84 | } 85 | } 86 | 87 | impl Key for T where T: From + Into { 88 | fn id(self) -> usize { 89 | self.into() as usize 90 | } 91 | 92 | fn key(id: usize) -> T { 93 | T::from(id.try_into().unwrap()) 94 | } 95 | } 96 | 97 | impl Key for Id { 98 | fn id(self) -> usize { 99 | self.value as usize 100 | } 101 | 102 | fn key(id: usize) -> Id { 103 | Id::::from(id as u64) 104 | } 105 | } 106 | 107 | impl Index for VecMap 108 | where K: Key 109 | { 110 | type Output = V; 111 | 112 | fn index(&self, index: K) -> &V { 113 | self.vec[index.id()].as_ref().unwrap() 114 | } 115 | } 116 | 117 | impl IndexMut for VecMap 118 | where K: Key 119 | { 120 | fn index_mut(&mut self, index: K) -> &mut V { 121 | self.vec[index.id()].as_mut().unwrap() 122 | } 123 | } 124 | 125 | #[allow(clippy::type_complexity)] 126 | impl<'v, K, V> IntoIterator for &'v VecMap where K: Key { 127 | type Item = &'v V; 128 | type IntoIter = 129 | FilterMap>, fn(&Option) -> Option<&V>>; 130 | 131 | fn into_iter(self) -> Self::IntoIter { 132 | self.vec.iter().filter_map(Option::::as_ref) 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /tests/bad-descriptor-length/reference.txt: -------------------------------------------------------------------------------- 1 | ○─ Getting configuration descriptor #0 for device 16, reading 255 bytes 2 | ├─── SETUP transaction on 16.0 with 8 data bytes, ACK: [80, 06, 00, 02, 00, 00, FF, 00] 3 | │ ├── SETUP packet on 16.0, CRC 1E 4 | │ ├── DATA0 packet with CRC A4E9 and 8 data bytes: [80, 06, 00, 02, 00, 00, FF, 00] 5 | │ └── ACK packet 6 | ├─── IN transaction on 16.0, NAK 7 | │ ├── IN packet on 16.0, CRC 1E 8 | │ └── NAK packet (9 times) 9 | ├─── IN transaction on 16.0 with 64 data bytes, ACK: [09, 02, 1D, 01, 03, 01, 04, A0, 32, 08, 0B, 00, 02, 01, 00, 20, 05, 09, 04, 00, 00, 01, 01, 01, 20, 05, 09, 24, 01, 00, 02, 04, 50, 00, 00, 08, 24, 0A, 04, 07, 07, 00, 00, 11, 24, 02, 0A, 01, 01, 00, 04, 02, 03, 00, 00, 00, 00, 00, 00, 00, 0C, 24, 03, 10] 10 | │ ├── IN packet on 16.0, CRC 1E 11 | │ ├── DATA1 packet with CRC C464 and 64 data bytes: [09, 02, 1D, 01, 03, 01, 04, A0, 32, 08, 0B, 00, 02, 01, 00, 20, 05, 09, 04, 00, 00, 01, 01, 01, 20, 05, 09, 24, 01, 00, 02, 04, 50, 00, 00, 08, 24, 0A, 04, 07, 07, 00, 00, 11, 24, 02, 0A, 01, 01, 00, 04, 02, 03, 00, 00, 00, 00, 00, 00, 00, 0C, 24, 03, 10] 12 | │ └── ACK packet 13 | ├─── IN transaction on 16.0, NAK 14 | │ ├── IN packet on 16.0, CRC 1E 15 | │ └── NAK packet (5 times) 16 | ├─── IN transaction on 16.0 with 64 data bytes, ACK: [02, 03, 0A, 16, 04, 00, 00, 00, 12, 24, 06, 16, 0A, 03, 00, 00, 00, 0C, 00, 00, 00, 0C, 00, 00, 00, 00, 10, 24, 09, 19, DA, 0B, 01, 16, 02, 03, 00, 00, 00, 00, 00, 00, 07, 05, 87, 03, 10, 00, 08, 09, 04, 01, 00, 00, 01, 02, 20, 05, 09, 04, 01, 01, 01, 01] 17 | │ ├── IN packet on 16.0, CRC 1E 18 | │ ├── DATA0 packet with CRC 0D37 and 64 data bytes: [02, 03, 0A, 16, 04, 00, 00, 00, 12, 24, 06, 16, 0A, 03, 00, 00, 00, 0C, 00, 00, 00, 0C, 00, 00, 00, 00, 10, 24, 09, 19, DA, 0B, 01, 16, 02, 03, 00, 00, 00, 00, 00, 00, 07, 05, 87, 03, 10, 00, 08, 09, 04, 01, 00, 00, 01, 02, 20, 05, 09, 04, 01, 01, 01, 01] 19 | │ └── ACK packet 20 | ├─── IN transaction on 16.0, NAK 21 | │ ├── IN packet on 16.0, CRC 1E 22 | │ └── NAK packet (10 times) 23 | ├─── IN transaction on 16.0 with 64 data bytes, ACK: [02, 20, 05, 10, 24, 01, 0A, 00, 01, 01, 00, 00, 00, 02, 03, 00, 00, 00, 00, 06, 24, 02, 01, 02, 10, 07, 05, 04, 09, F8, 00, 01, 08, 25, 01, 00, 00, 00, 00, 00, 09, 04, 01, 02, 01, 01, 02, 20, 05, 10, 24, 01, 0A, 00, 01, 01, 00, 00, 00, 02, 03, 00, 00, 00] 24 | │ ├── IN packet on 16.0, CRC 1E 25 | │ ├── DATA1 packet with CRC BBBE and 64 data bytes: [02, 20, 05, 10, 24, 01, 0A, 00, 01, 01, 00, 00, 00, 02, 03, 00, 00, 00, 00, 06, 24, 02, 01, 02, 10, 07, 05, 04, 09, F8, 00, 01, 08, 25, 01, 00, 00, 00, 00, 00, 09, 04, 01, 02, 01, 01, 02, 20, 05, 10, 24, 01, 0A, 00, 01, 01, 00, 00, 00, 02, 03, 00, 00, 00] 26 | │ └── ACK packet 27 | ├─── IN transaction on 16.0, NAK 28 | │ ├── IN packet on 16.0, CRC 1E 29 | │ └── NAK packet (16 times) 30 | ├─── IN transaction on 16.0 with 63 data bytes, ACK: [00, 06, 24, 02, 01, 03, 18, 07, 05, 04, 09, 74, 01, 01, 08, 25, 01, 00, 00, 00, 00, 00, 09, 04, 01, 03, 01, 01, 02, 20, 05, 10, 24, 01, 0A, 00, 01, 01, 00, 00, 00, 02, 03, 00, 00, 00, 00, 06, 24, 02, 01, 04, 20, 07, 05, 04, 09, F0, 01, 01, 08, 25, 01] 31 | │ ├── IN packet on 16.0, CRC 1E 32 | │ ├── DATA0 packet with CRC 1E7A and 63 data bytes: [00, 06, 24, 02, 01, 03, 18, 07, 05, 04, 09, 74, 01, 01, 08, 25, 01, 00, 00, 00, 00, 00, 09, 04, 01, 03, 01, 01, 02, 20, 05, 10, 24, 01, 0A, 00, 01, 01, 00, 00, 00, 02, 03, 00, 00, 00, 00, 06, 24, 02, 01, 04, 20, 07, 05, 04, 09, F0, 01, 01, 08, 25, 01] 33 | │ └── ACK packet 34 | └─── OUT transaction on 16.0 with no data, ACK 35 | ├── OUT packet on 16.0, CRC 1E 36 | ├── DATA1 packet with CRC 0000 and no data 37 | └── ACK packet 38 | -------------------------------------------------------------------------------- /appimage/action.yml: -------------------------------------------------------------------------------- 1 | name: "AppImage" 2 | description: "Builds a Linux AppImage on Debian 10 (Buster)" 3 | inputs: 4 | executable: 5 | description: "Executable file to package into an AppImage." 6 | required: true 7 | icon-file: 8 | description: "Icon file to use for the AppImage." 9 | required: true 10 | desktop-file: 11 | description: "Desktop file to use for the AppImage." 12 | required: true 13 | appdir: 14 | description: "Path to use for the AppImage filesystem." 15 | required: true 16 | 17 | runs: 18 | using: composite 19 | 20 | steps: 21 | - name: Build packetry-x86_64.AppDir (Linux) 22 | shell: bash 23 | run: | 24 | DEPLOY_GTK_VERSION=4 linuxdeploy-x86_64.AppImage \ 25 | --appimage-extract-and-run \ 26 | --appdir ${{ inputs.appdir }} \ 27 | --executable=${{ inputs.executable }} \ 28 | --icon-file ${{ inputs.icon-file }} \ 29 | --plugin gtk \ 30 | --desktop-file ${{ inputs.desktop-file }} 31 | 32 | - name: Shrink packetry-x86_64.AppDir (Linux) 33 | shell: bash 34 | run: | 35 | # cd to AppDir lib directory 36 | cd $GITHUB_WORKSPACE/${{ inputs.appdir }}/usr/lib 37 | 38 | # remove any system libraries we don't want to include in case 39 | # of conflicts with a user's system 40 | rm -f libX* 41 | rm -f libblkid* 42 | rm -f libbsd* 43 | rm -f libpixman* 44 | rm -f libxcb-render* 45 | rm -f libzstd* 46 | 47 | # add libharfbuzz even though we don't link to it directly as 48 | # some systems will attempt to dlopen(3) its own version if it 49 | # is not present 50 | cp -a /opt/gtk-*/lib/x86_64-linux-gnu/libharfbuzz.* . 51 | cp -a /opt/gtk-*/lib/x86_64-linux-gnu/libharfbuzz-gobject.* . 52 | cp -a /opt/gtk-*/lib/x86_64-linux-gnu/libharfbuzz-icu.* . 53 | cp -a /opt/gtk-*/lib/x86_64-linux-gnu/libharfbuzz-subset.* . 54 | 55 | # remove duplicate library files and replace with symlinks 56 | fdupes --sameline --order=name . | while read -r lib ; do 57 | src=$(echo $lib | rev | cut -d' ' -f1 | rev) 58 | dupes=$(echo $lib | rev | cut -d' ' -f2- | rev) 59 | rm $dupes 60 | for dst in $dupes ; do 61 | ln -s $src $dst 62 | done 63 | done 64 | 65 | # strip all libraries 66 | strip ./lib*.* 67 | 68 | # return to workspace directory 69 | cd $GITHUB_WORKSPACE 70 | 71 | - name: Gather licenses (Linux) 72 | shell: bash 73 | run: | 74 | # cd to AppDir /usr/share/doc directory 75 | cd $GITHUB_WORKSPACE/${{ inputs.appdir }}/usr/share/doc 76 | 77 | # remove any licenses for system libraries we don't distribute 78 | rm -rf libblkid1 79 | rm -rf libbsd0 80 | rm -rf libxau6 81 | rm -rf libxcb-render0 82 | rm -rf libxcursor1 83 | rm -rf libxdamage1 84 | rm -rf libxdmcp6 85 | rm -rf libxext6 86 | rm -rf libxfixes3 87 | rm -rf libxi6 88 | rm -rf libxinerama1 89 | rm -rf libxrandr2 90 | rm -rf libxrender1 91 | rm -rf libzstd1 92 | 93 | # Return to workspace directory 94 | cd $GITHUB_WORKSPACE 95 | 96 | - name: Convert icon to application/x-executable on all themes (Linux) 97 | shell: bash 98 | run: | 99 | cp /dev/null ${{ inputs.appdir }}/usr/share/icons/hicolor/512x512/apps/$(basename ${{ inputs.icon-file }}) 100 | 101 | - name: Build packetry-x86_64.AppImage (Linux) 102 | shell: bash 103 | run: | 104 | appimagetool-x86_64.AppImage \ 105 | --appimage-extract-and-run \ 106 | ${{ inputs.appdir }}/ 107 | -------------------------------------------------------------------------------- /tests/ui/mouse-step/actions.json: -------------------------------------------------------------------------------- 1 | {"Open":"tests/mouse/capture.pcap"} 2 | {"Update":1} 3 | {"SetExpanded":["traffic-hierarchical",0,true]} 4 | {"SetExpanded":["traffic-hierarchical",1,true]} 5 | {"Update":2} 6 | {"SetExpanded":["traffic-hierarchical",3,true]} 7 | {"SetExpanded":["traffic-hierarchical",4,true]} 8 | {"Update":3} 9 | {"Update":4} 10 | {"Update":5} 11 | {"SetExpanded":["traffic-hierarchical",8,true]} 12 | {"Update":6} 13 | {"Update":7} 14 | {"SetExpanded":["traffic-hierarchical",11,true]} 15 | {"Update":8} 16 | {"Update":9} 17 | {"SetExpanded":["traffic-hierarchical",14,true]} 18 | {"Update":10} 19 | {"Update":11} 20 | {"Update":12} 21 | {"Update":13} 22 | {"Update":14} 23 | {"Update":15} 24 | {"Update":16} 25 | {"Update":17} 26 | {"Update":18} 27 | {"Update":19} 28 | {"Update":20} 29 | {"Update":21} 30 | {"Update":22} 31 | {"Update":23} 32 | {"Update":24} 33 | {"Update":25} 34 | {"Update":26} 35 | {"Update":27} 36 | {"Update":28} 37 | {"Update":29} 38 | {"Update":30} 39 | {"Update":31} 40 | {"Update":32} 41 | {"Update":33} 42 | {"Update":34} 43 | {"Update":35} 44 | {"Update":36} 45 | {"Update":37} 46 | {"Update":38} 47 | {"Update":39} 48 | {"Update":40} 49 | {"Update":41} 50 | {"Update":42} 51 | {"Update":43} 52 | {"Update":44} 53 | {"Update":45} 54 | {"Update":46} 55 | {"Update":47} 56 | {"Update":48} 57 | {"Update":49} 58 | {"Update":50} 59 | {"Update":51} 60 | {"Update":52} 61 | {"Update":53} 62 | {"Update":54} 63 | {"Update":55} 64 | {"Update":56} 65 | {"Update":57} 66 | {"Update":58} 67 | {"Update":59} 68 | {"Update":60} 69 | {"Update":61} 70 | {"Update":62} 71 | {"Update":63} 72 | {"Update":64} 73 | {"Update":65} 74 | {"Update":66} 75 | {"Update":67} 76 | {"Update":68} 77 | {"Update":69} 78 | {"Update":70} 79 | {"Update":71} 80 | {"Update":72} 81 | {"Update":73} 82 | {"Update":74} 83 | {"Update":75} 84 | {"Update":76} 85 | {"Update":77} 86 | {"Update":78} 87 | {"Update":79} 88 | {"Update":80} 89 | {"Update":81} 90 | {"Update":82} 91 | {"Update":83} 92 | {"Update":84} 93 | {"Update":85} 94 | {"Update":86} 95 | {"Update":87} 96 | {"Update":88} 97 | {"Update":89} 98 | {"Update":90} 99 | {"Update":91} 100 | {"Update":92} 101 | {"Update":93} 102 | {"Update":94} 103 | {"Update":95} 104 | {"Update":96} 105 | {"Update":97} 106 | {"Update":98} 107 | {"Update":99} 108 | {"Update":100} 109 | {"Update":101} 110 | {"Update":102} 111 | {"Update":103} 112 | {"Update":104} 113 | {"Update":105} 114 | {"Update":106} 115 | {"Update":107} 116 | {"Update":108} 117 | {"Update":109} 118 | {"Update":110} 119 | {"Update":111} 120 | {"Update":112} 121 | {"Update":113} 122 | {"Update":114} 123 | {"Update":115} 124 | {"Update":116} 125 | {"Update":117} 126 | {"Update":118} 127 | {"Update":119} 128 | {"Update":120} 129 | {"Update":121} 130 | {"Update":122} 131 | {"Update":123} 132 | {"Update":124} 133 | {"Update":125} 134 | {"Update":126} 135 | {"Update":127} 136 | {"Update":128} 137 | {"Update":129} 138 | {"Update":130} 139 | {"Update":131} 140 | {"Update":132} 141 | {"Update":133} 142 | {"Update":134} 143 | {"Update":135} 144 | {"Update":136} 145 | {"Update":137} 146 | {"Update":138} 147 | {"Update":139} 148 | {"Update":140} 149 | {"Update":141} 150 | {"Update":142} 151 | {"Update":143} 152 | {"Update":144} 153 | {"Update":145} 154 | {"Update":146} 155 | {"Update":147} 156 | {"Update":148} 157 | {"Update":149} 158 | {"Update":150} 159 | {"Update":151} 160 | {"Update":152} 161 | {"Update":153} 162 | {"Update":154} 163 | {"Update":155} 164 | {"Update":156} 165 | {"Update":157} 166 | {"Update":158} 167 | {"Update":159} 168 | {"Update":160} 169 | {"Update":161} 170 | {"Update":162} 171 | {"Update":163} 172 | {"Update":164} 173 | {"SetExpanded":["traffic-hierarchical",29,true]} 174 | {"SetExpanded":["traffic-hierarchical",29,false]} 175 | {"SetExpanded":["traffic-hierarchical",3,false]} 176 | {"SetExpanded":["traffic-hierarchical",0,false]} 177 | -------------------------------------------------------------------------------- /src/util/mod.rs: -------------------------------------------------------------------------------- 1 | //! Utility code that doesn't belong anywhere specific. 2 | 3 | use std::ops::Range; 4 | 5 | use anyhow::{Error, bail}; 6 | use num_format::{Locale, ToFormattedString}; 7 | use humansize::{SizeFormatter, BINARY}; 8 | use itertools::Itertools; 9 | 10 | pub mod id; 11 | pub mod dump; 12 | pub mod vec_map; 13 | pub mod rcu; 14 | 15 | use id::Id; 16 | 17 | pub fn fmt_count(count: u64) -> String { 18 | count.to_formatted_string(&Locale::en) 19 | } 20 | 21 | pub fn fmt_size(size: u64) -> String { 22 | if size == 1 { 23 | "1 byte".to_string() 24 | } else if size < 1024 { 25 | format!("{size} bytes") 26 | } else { 27 | format!("{}", SizeFormatter::new(size, BINARY)) 28 | } 29 | } 30 | 31 | pub fn handle_thread_panic(result: std::thread::Result) 32 | -> Result 33 | { 34 | match result { 35 | Ok(x) => Ok(x), 36 | Err(panic) => { 37 | let msg = match ( 38 | panic.downcast_ref::<&str>(), 39 | panic.downcast_ref::()) 40 | { 41 | (Some(&s), _) => s, 42 | (_, Some(s)) => s, 43 | (None, None) => "" 44 | }; 45 | bail!("Worker thread panic: {msg}"); 46 | } 47 | } 48 | } 49 | 50 | pub fn titlecase(text: &str) -> String { 51 | format!("{}{}", 52 | text 53 | .chars() 54 | .take(1) 55 | .map(|c| c.to_uppercase().to_string()) 56 | .join(""), 57 | text 58 | .chars() 59 | .skip(1) 60 | .collect::() 61 | ) 62 | } 63 | 64 | pub struct Bytes<'src> { 65 | pub partial: bool, 66 | pub bytes: &'src [u8], 67 | } 68 | 69 | impl<'src> Bytes<'src> { 70 | pub fn first(max: usize, bytes: &'src [u8]) -> Self { 71 | if bytes.len() > max { 72 | Bytes { 73 | partial: true, 74 | bytes: &bytes[0..max], 75 | } 76 | } else { 77 | Bytes { 78 | partial: false, 79 | bytes, 80 | } 81 | } 82 | } 83 | 84 | fn looks_like_ascii(&self) -> bool { 85 | let mut num_printable = 0; 86 | for &byte in self.bytes { 87 | if byte == 0 || byte >= 0x80 { 88 | // Outside ASCII range. 89 | return false; 90 | } 91 | // Count printable and pseudo-printable characters. 92 | let printable = match byte { 93 | c if (0x20..0x7E).contains(&c) => true, // printable range 94 | 0x09 => true, // tab 95 | 0x0A => true, // new line 96 | 0x0D => true, // carriage return 97 | _ => false 98 | }; 99 | if printable { 100 | num_printable += 1; 101 | } 102 | } 103 | // If the string is at least half printable, treat as ASCII. 104 | num_printable > 0 && num_printable >= self.bytes.len() / 2 105 | } 106 | } 107 | 108 | impl std::fmt::Display for Bytes<'_> { 109 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 110 | if self.looks_like_ascii() { 111 | write!(f, "'{}'", String::from_utf8( 112 | self.bytes.iter() 113 | .flat_map(|c| {std::ascii::escape_default(*c)}) 114 | .collect::>()).unwrap())? 115 | } else { 116 | write!(f, "{:02X?}", self.bytes)? 117 | }; 118 | if self.partial { 119 | write!(f, "...") 120 | } else { 121 | Ok(()) 122 | } 123 | } 124 | } 125 | 126 | pub trait RangeLength { 127 | fn len(&self) -> u64; 128 | } 129 | 130 | impl RangeLength for Range> { 131 | fn len(&self) -> u64 { 132 | self.end.value - self.start.value 133 | } 134 | } 135 | 136 | impl RangeLength for Range { 137 | fn len(&self) -> u64 { 138 | self.end - self.start 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /docs/source/user_interface.rst: -------------------------------------------------------------------------------- 1 | ============== 2 | User Interface 3 | ============== 4 | 5 | Action Bar 6 | ---------- 7 | 8 | Across the top of the Packetry window is the Action Bar used to control Packetry. 9 | 10 | .. image:: ../images/action-bar.png 11 | :alt: Action Bar 12 | 13 | - **Open Button**: Clears Traffic Pane and Device Pane and loads a ``.pcap`` file with the ``LINKTYPE_USB_2_0`` link-layer header type. 14 | - **Save Button**: Saves a ``.pcap`` file with the ``LINKTYPE_USB_2_0`` link-layer header type. 15 | - **Capture Button**: Clears Traffic Pane and Device Pane and starts capturing USB data with the connected capture device. 16 | - **Stop Button**: Stops capturing USB data. Stops reading USB data from file. 17 | - **Device Drop-down**: Selects a capture device. This list is updated automatically as devices are attached or unplugged. 18 | - **Speed Drop-down**: Selects the target device speed for live capture. 19 | - **Target Power Switch**: Turns on or off power to the target device (requires a Cynthion analyzer and suitable gateware). 20 | - **Power Menu**: Where target power control is available, opens a menu for additional power control settings. 21 | 22 | .. image:: ../images/power-menu.png 23 | :alt: Power Menu 24 | 25 | Traffic Pane 26 | ------------ 27 | 28 | Beneath the Action Bar is the Traffic Pane. This is Packetry's main view of captured USB data. 29 | At the top of the Traffic Pane there are buttons to switch between different views of the captured data. 30 | 31 | Each packet is displayed with a precise time stamp relative to the start of capture. 32 | Higher level groups have a time stamp matching the first packet that is a part of the group. 33 | 34 | The Traffic Pane is cleared at the start of each capture or when a file is loaded. 35 | 36 | Hierarchical View 37 | ^^^^^^^^^^^^^^^^^ 38 | 39 | .. image:: ../images/traffic-pane-hierarchical.png 40 | :alt: Traffic Pane Hierarchical View 41 | 42 | This displays an interactive hierarchical view, allowing you to expand high level events to explore their constituent low level transactions and packets. 43 | 44 | .. note:: 45 | 46 | Since multiple high level events can be happening at the same time, this view may show packets out of chronological order. 47 | An interleaved view that still shows packets in order is in progress, tracked here: https://github.com/greatscottgadgets/packetry/pull/150 48 | 49 | Transactions View 50 | ^^^^^^^^^^^^^^^^^ 51 | 52 | .. image:: ../images/traffic-pane-transactions.png 53 | :alt: Traffic Pane Transactions View 54 | 55 | This combines packets into expandable groups for each USB transaction. 56 | It is more verbose than the hierarchical view, but guarantees that all items are shown in chronological order. 57 | 58 | Packets View 59 | ^^^^^^^^^^^^ 60 | 61 | .. image:: ../images/traffic-pane-packets.png 62 | :alt: Traffic Pane Packets View 63 | 64 | This shows all packets captured on the wire in order, with no grouping. 65 | 66 | Device Pane 67 | ----------- 68 | 69 | To the right of the Traffic Pane is the Device Pane. The Device Pane summarizes information about observed USB devices on the target bus. 70 | 71 | .. image:: ../images/device-pane.png 72 | :alt: Device Pane 73 | 74 | Much of the information that can be displayed in the Device Pane is discovered only if Packetry observes the initial enumeration of a USB device by the target host. To allow this to take place in a live capture, connect the target device after starting a capture, not before. 75 | 76 | The Device Pane is cleared at the start of each capture or when a file is loaded. 77 | 78 | 79 | Detail Pane 80 | ----------- 81 | 82 | The Detail Pane is located below the Traffic Pane. It provides a detailed view of an item selected in the Traffic Pane and allows copying of data from that item. 83 | 84 | .. image:: ../images/detail-pane.png 85 | :alt: Detail Pane 86 | 87 | Depending on the item selected, the Detail Pane may provide different information. For example, when a string descriptor request transfer is selected, the Detail Pane summarizes the request including the actual string as text. By expanding that transfer in the Traffic Pane you can select a transaction or packet within the transfer to view packet data in hexadecimal in the Detail Pane. 88 | 89 | 90 | Status Bar 91 | ---------- 92 | 93 | At the bottom of the Packetry window is the Status Bar. 94 | 95 | .. image:: ../images/status-bar.png 96 | :alt: Status Bar 97 | 98 | The Status Bar tells you what Packetry is doing. 99 | -------------------------------------------------------------------------------- /tests/iso-ambiguous/reference.txt: -------------------------------------------------------------------------------- 1 | ○── 18 ambiguous transactions on endpoint 27.3 IN 2 | ├──── IN transaction on 27.3 with 192 data bytes: [00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00]... 3 | │ ├── IN packet on 27.3, CRC 0B 4 | │ └── DATA0 packet with CRC E17B and 192 data bytes: [00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00]... 5 | ├──── IN transaction on 27.3 with 64 data bytes: [00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00] 6 | │ ├── IN packet on 27.3, CRC 0B 7 | │ └── DATA0 packet with CRC D0BF and 64 data bytes: [00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00] 8 | ├──── IN transaction on 27.3 with 192 data bytes: [00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00]... 9 | │ ├── IN packet on 27.3, CRC 0B 10 | │ └── DATA0 packet with CRC E17B and 192 data bytes: [00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00]... (16 times) 11 | │○─ 7 ambiguous transactions on endpoint 27.3 OUT 12 | │├─── OUT transaction on 27.3 with 192 data bytes: [00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00]... 13 | ││ ├── OUT packet on 27.3, CRC 0B 14 | ││ └── DATA0 packet with CRC E17B and 192 data bytes: [00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00]... (6 times) 15 | │└─── OUT transaction on 27.3 with 192 data bytes: [00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00]... 16 | │ ├── OUT packet on 27.3, CRC 0B 17 | │ └── DATA0 packet with CRC E17B and 192 data bytes: [00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00]... 18 | -------------------------------------------------------------------------------- /tests/hid/ps5/reference.txt: -------------------------------------------------------------------------------- 1 | 2 | ○ Input report #1 (64 bytes): 3 | ├── X: byte 1 4 | ├── Y: byte 2 5 | ├── Z: byte 3 6 | ├── Rz: byte 4 7 | ├── Rx: byte 5 8 | ├── Ry: byte 6 9 | ├── Vendor Usage 0x20: byte 7 10 | ├── Hat Switch: byte 8 bits 0-3 (values 0 to 7) 11 | ├── Button 1: byte 8 bit 4 12 | ├── Button 2: byte 8 bit 5 13 | ├── Button 3: byte 8 bit 6 14 | ├── Button 4: byte 8 bit 7 15 | ├── Button 5: byte 9 bit 0 16 | ├── Button 6: byte 9 bit 1 17 | ├── Button 7: byte 9 bit 2 18 | ├── Button 8: byte 9 bit 3 19 | ├── Button 9: byte 9 bit 4 20 | ├── Button 10: byte 9 bit 5 21 | ├── Button 11: byte 9 bit 6 22 | ├── Button 12: byte 9 bit 7 23 | ├── Button 13: byte 10 bit 0 24 | ├── Button 14: byte 10 bit 1 25 | ├── Button 15: byte 10 bit 2 26 | ├── Vendor Usage 0x21: byte 10 bit 3 27 | ├── Vendor Usage 0x21: byte 10 bit 4 28 | ├── Vendor Usage 0x21: byte 10 bit 5 29 | ├── Vendor Usage 0x21: byte 10 bit 6 30 | ├── Vendor Usage 0x21: byte 10 bit 7 31 | ├── Vendor Usage 0x21: byte 11 bit 0 32 | ├── Vendor Usage 0x21: byte 11 bit 1 33 | ├── Vendor Usage 0x21: byte 11 bit 2 34 | ├── Vendor Usage 0x21: byte 11 bit 3 35 | ├── Vendor Usage 0x21: byte 11 bit 4 36 | ├── Vendor Usage 0x21: byte 11 bit 5 37 | ├── Vendor Usage 0x21: byte 11 bit 6 38 | ├── Vendor Usage 0x21: byte 11 bit 7 39 | ├── Vendor Usage 0x22: byte 12 40 | ├── Vendor Usage 0x22: byte 13 41 | ├── Vendor Usage 0x22: byte 14 42 | ├── Vendor Usage 0x22: byte 15 43 | ├── Vendor Usage 0x22: byte 16 44 | ├── Vendor Usage 0x22: byte 17 45 | ├── Vendor Usage 0x22: byte 18 46 | ├── Vendor Usage 0x22: byte 19 47 | ├── Vendor Usage 0x22: byte 20 48 | ├── Vendor Usage 0x22: byte 21 49 | ├── Vendor Usage 0x22: byte 22 50 | ├── Vendor Usage 0x22: byte 23 51 | ├── Vendor Usage 0x22: byte 24 52 | ├── Vendor Usage 0x22: byte 25 53 | ├── Vendor Usage 0x22: byte 26 54 | ├── Vendor Usage 0x22: byte 27 55 | ├── Vendor Usage 0x22: byte 28 56 | ├── Vendor Usage 0x22: byte 29 57 | ├── Vendor Usage 0x22: byte 30 58 | ├── Vendor Usage 0x22: byte 31 59 | ├── Vendor Usage 0x22: byte 32 60 | ├── Vendor Usage 0x22: byte 33 61 | ├── Vendor Usage 0x22: byte 34 62 | ├── Vendor Usage 0x22: byte 35 63 | ├── Vendor Usage 0x22: byte 36 64 | ├── Vendor Usage 0x22: byte 37 65 | ├── Vendor Usage 0x22: byte 38 66 | ├── Vendor Usage 0x22: byte 39 67 | ├── Vendor Usage 0x22: byte 40 68 | ├── Vendor Usage 0x22: byte 41 69 | ├── Vendor Usage 0x22: byte 42 70 | ├── Vendor Usage 0x22: byte 43 71 | ├── Vendor Usage 0x22: byte 44 72 | ├── Vendor Usage 0x22: byte 45 73 | ├── Vendor Usage 0x22: byte 46 74 | ├── Vendor Usage 0x22: byte 47 75 | ├── Vendor Usage 0x22: byte 48 76 | ├── Vendor Usage 0x22: byte 49 77 | ├── Vendor Usage 0x22: byte 50 78 | ├── Vendor Usage 0x22: byte 51 79 | ├── Vendor Usage 0x22: byte 52 80 | ├── Vendor Usage 0x22: byte 53 81 | ├── Vendor Usage 0x22: byte 54 82 | ├── Vendor Usage 0x22: byte 55 83 | ├── Vendor Usage 0x22: byte 56 84 | ├── Vendor Usage 0x22: byte 57 85 | ├── Vendor Usage 0x22: byte 58 86 | ├── Vendor Usage 0x22: byte 59 87 | ├── Vendor Usage 0x22: byte 60 88 | ├── Vendor Usage 0x22: byte 61 89 | ├── Vendor Usage 0x22: byte 62 90 | └── Vendor Usage 0x22: byte 63 91 | 92 | ○ Output report #2 (48 bytes): 93 | ├── Vendor Usage 0x23: byte 1 94 | ├── Vendor Usage 0x23: byte 2 95 | ├── Vendor Usage 0x23: byte 3 96 | ├── Vendor Usage 0x23: byte 4 97 | ├── Vendor Usage 0x23: byte 5 98 | ├── Vendor Usage 0x23: byte 6 99 | ├── Vendor Usage 0x23: byte 7 100 | ├── Vendor Usage 0x23: byte 8 101 | ├── Vendor Usage 0x23: byte 9 102 | ├── Vendor Usage 0x23: byte 10 103 | ├── Vendor Usage 0x23: byte 11 104 | ├── Vendor Usage 0x23: byte 12 105 | ├── Vendor Usage 0x23: byte 13 106 | ├── Vendor Usage 0x23: byte 14 107 | ├── Vendor Usage 0x23: byte 15 108 | ├── Vendor Usage 0x23: byte 16 109 | ├── Vendor Usage 0x23: byte 17 110 | ├── Vendor Usage 0x23: byte 18 111 | ├── Vendor Usage 0x23: byte 19 112 | ├── Vendor Usage 0x23: byte 20 113 | ├── Vendor Usage 0x23: byte 21 114 | ├── Vendor Usage 0x23: byte 22 115 | ├── Vendor Usage 0x23: byte 23 116 | ├── Vendor Usage 0x23: byte 24 117 | ├── Vendor Usage 0x23: byte 25 118 | ├── Vendor Usage 0x23: byte 26 119 | ├── Vendor Usage 0x23: byte 27 120 | ├── Vendor Usage 0x23: byte 28 121 | ├── Vendor Usage 0x23: byte 29 122 | ├── Vendor Usage 0x23: byte 30 123 | ├── Vendor Usage 0x23: byte 31 124 | ├── Vendor Usage 0x23: byte 32 125 | ├── Vendor Usage 0x23: byte 33 126 | ├── Vendor Usage 0x23: byte 34 127 | ├── Vendor Usage 0x23: byte 35 128 | ├── Vendor Usage 0x23: byte 36 129 | ├── Vendor Usage 0x23: byte 37 130 | ├── Vendor Usage 0x23: byte 38 131 | ├── Vendor Usage 0x23: byte 39 132 | ├── Vendor Usage 0x23: byte 40 133 | ├── Vendor Usage 0x23: byte 41 134 | ├── Vendor Usage 0x23: byte 42 135 | ├── Vendor Usage 0x23: byte 43 136 | ├── Vendor Usage 0x23: byte 44 137 | ├── Vendor Usage 0x23: byte 45 138 | ├── Vendor Usage 0x23: byte 46 139 | └── Vendor Usage 0x23: byte 47 140 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), 6 | and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 7 | 8 | 11 | 12 | ## [0.5.0] - 2025-09-29 13 | 14 | ### Added 15 | 16 | - Add support for non-packet events from Cynthion analyzer. 17 | - Add support for VBUS power control with Cynthion analyzer. 18 | - Add support for the PcapNG file format with custom non-packet blocks. 19 | - Add decoding of HID report descriptors. 20 | - Add facility to dump the capture database for debugging. 21 | 22 | ### Changed 23 | 24 | - The device list is maintained automatically and the scan button is removed. 25 | - USB capture devices are kept open whilst selected for use. 26 | - Filename extensions are added automatically when saving. 27 | - File dialogs remember the last used directory. 28 | - Backtraces are always captured when an error occurs. 29 | - Panics in worker threads result in an error dialog rather than a crash. 30 | - UI updates are made from a snapshot of the database state. 31 | - Capture backends have been ported to the nusb 0.2 API. 32 | - Most of the UI is now defined in XML and editable with Cambalache. 33 | - Source code has been significantly reorganised. 34 | 35 | ### Fixed 36 | 37 | - Stop descriptors from being intermingled when a device address is reused. 38 | - Fix a decoder crash when a double SETUP packet is seen. 39 | - Fix crashes in GTK where the view could get out of sync with the model. 40 | - Fix descriptions of invalid groups leaking internal details. 41 | - Fix inconsistent pane sizing when window is first shown. 42 | - Fix units in text shown in progress bar. 43 | - Handle runtime GTK version mismatches gracefully. 44 | 45 | 46 | ## [0.4.0] - 2024-10-29 47 | 48 | ### Added 49 | 50 | - Add context menu system and options to save data. 51 | - Add initial support for HID descriptors. 52 | 53 | 54 | ## [0.3.0] - 2024-10-21 55 | 56 | ### Added 57 | 58 | - Add keyboard shortcuts. 59 | - Add transaction-level and packet-level views of capture. 60 | - Update documentation for multiple traffic views. 61 | - Add backend API for USB capture devices. 62 | - Add support for iCE40-usbtrace capture device. 63 | - Display all descriptor types in device view. 64 | - Add connecting lines to test output. 65 | 66 | ### Fixed 67 | 68 | - Fix handling of alternate interface settings. 69 | - Handle descriptors that are longer than defined in the specification. 70 | - Fix interpretation of isochronous transactions, including ambiguous cases. 71 | 72 | 73 | ## [0.2.2] - 2024-09-02 74 | 75 | ### Added 76 | 77 | - Add fuzzer to help find decoder bugs. 78 | - Document clearing of Traffic and Device panes. 79 | - Document both functions of Stop button. 80 | 81 | ### Changed 82 | 83 | - Clean up GObject subclasses. 84 | - Implement iterators for stream types, speeding up file saving. 85 | 86 | ### Fixed 87 | 88 | - Treat SETUP packets with non-zero EP num as indicating OUT direction. 89 | - Don't try to find the endpoint for a malformed packet. 90 | - Add libharfbuzz to Linux AppImage, fixing symbol lookup error. 91 | 92 | 93 | ## [0.2.1] - 2024-08-15 94 | 95 | ### Changed 96 | 97 | - Update documentation for 0.2.0. 98 | 99 | ### Fixed 100 | 101 | - Use 24-bit rather than 16-bit increments for timestamps, fixing slow file 102 | save. 103 | 104 | 105 | ## [0.2.0] - 2024-08-13 106 | 107 | ### Added 108 | 109 | - Add detail pane. 110 | - Add packetry-cli wrapper program, enabling command-line options on Windows. 111 | - Add Linux AppImage build. 112 | - Use usb.ids database to interpret various ID values. 113 | - Use GIO File abstraction, supporting file operations over MTP or SMB, for 114 | example. 115 | - Add information about command line options to Application instance. 116 | 117 | ### Changed 118 | - Bump nusb dependency to 0.1.10 and remove workaround for 0.1.9. 119 | - Handle opening files in the standard way for a GTK application. 120 | 121 | ### Fixed 122 | - Avoid underflow in UI code when capture is completely empty. 123 | - Validate packet CRCs and lengths, and diagnose malformed packets. 124 | 125 | 126 | ## [0.1.0] - 2024-07-16 127 | 128 | ### Added 129 | 130 | - Initial release 131 | 132 | 133 | [Unreleased]: https://github.com/greatscottgadgets/packetry/compare/v0.5.0...HEAD 134 | [0.5.0]: https://github.com/greatscottgadgets/packetry/compare/v0.4.0...v0.5.0 135 | [0.4.0]: https://github.com/greatscottgadgets/packetry/compare/v0.3.0...v0.4.0 136 | [0.3.0]: https://github.com/greatscottgadgets/packetry/compare/v0.2.2...v0.3.0 137 | [0.2.2]: https://github.com/greatscottgadgets/packetry/compare/v0.2.1...v0.2.2 138 | [0.2.1]: https://github.com/greatscottgadgets/packetry/compare/v0.2.0...v0.2.1 139 | [0.2.0]: https://github.com/greatscottgadgets/packetry/compare/v0.1.0...v0.2.0 140 | [0.1.0]: https://github.com/greatscottgadgets/packetry/releases/tag/v0.1.0 141 | -------------------------------------------------------------------------------- /tests/bad-descriptor-length/devices-reference.txt: -------------------------------------------------------------------------------- 1 | Device 16: Unknown 2 | No device descriptor 3 | Configuration 1 4 | Configuration descriptor 5 | Length: 9 bytes 6 | Type: 0x02 7 | Total length: 285 bytes 8 | Number of interfaces: 3 9 | Configuration number: 1 10 | Configuration string: #4 (not seen) 11 | Attributes: 0xA0 12 | Max power: 100mA 13 | Function 5: Audio 14 | Interface association descriptor 15 | Length: 8 bytes 16 | Type: 0x0B 17 | First interface: 0 18 | Interface count: 2 19 | Function class: 0x01: Audio 20 | Function subclass: 0x00 21 | Function protocol: 0x20 22 | Function number: 5 23 | Interface 0: Audio 24 | Interface descriptor 25 | Length: 9 bytes 26 | Type: 0x04 27 | Interface number: 0 28 | Alternate setting: 0 29 | Number of endpoints: 1 30 | Class: 0x01: Audio 31 | Subclass: 0x01: Control Device 32 | Protocol: 0x20 33 | Interface string: #5 (not seen) 34 | Class descriptor 0x24, 9 bytes 35 | Class descriptor 0x24, 8 bytes 36 | Class descriptor 0x24, 17 bytes 37 | Class descriptor 0x24, 12 bytes 38 | Class descriptor 0x24, 18 bytes 39 | Class descriptor 0x24, 16 bytes 40 | Endpoint 7 IN (interrupt) 41 | Endpoint descriptor 42 | Length: 7 bytes 43 | Type: 0x05 44 | Endpoint address: 0x87 45 | Attributes: 0x03 46 | Max packet size: 16 bytes 47 | Interval: 0x08 48 | Interface 1: Audio 49 | Interface descriptor 50 | Length: 9 bytes 51 | Type: 0x04 52 | Interface number: 1 53 | Alternate setting: 0 54 | Number of endpoints: 0 55 | Class: 0x01: Audio 56 | Subclass: 0x02: Streaming 57 | Protocol: 0x20 58 | Interface string: #5 (not seen) 59 | Interface 1 alt 1: Audio 60 | Interface descriptor 61 | Length: 9 bytes 62 | Type: 0x04 63 | Interface number: 1 64 | Alternate setting: 1 65 | Number of endpoints: 1 66 | Class: 0x01: Audio 67 | Subclass: 0x02: Streaming 68 | Protocol: 0x20 69 | Interface string: #5 (not seen) 70 | Class descriptor 0x24, 16 bytes 71 | Class descriptor 0x24, 6 bytes 72 | Endpoint 4 OUT (isochronous) 73 | Endpoint descriptor 74 | Length: 7 bytes 75 | Type: 0x05 76 | Endpoint address: 0x04 77 | Attributes: 0x09 78 | Max packet size: 248 bytes 79 | Interval: 0x01 80 | Class descriptor 0x25, 8 bytes 81 | Interface 1 alt 2: Audio 82 | Interface descriptor 83 | Length: 9 bytes 84 | Type: 0x04 85 | Interface number: 1 86 | Alternate setting: 2 87 | Number of endpoints: 1 88 | Class: 0x01: Audio 89 | Subclass: 0x02: Streaming 90 | Protocol: 0x20 91 | Interface string: #5 (not seen) 92 | Class descriptor 0x24, 16 bytes 93 | Class descriptor 0x24, 6 bytes 94 | Endpoint 4 OUT (isochronous) 95 | Endpoint descriptor 96 | Length: 7 bytes 97 | Type: 0x05 98 | Endpoint address: 0x04 99 | Attributes: 0x09 100 | Max packet size: 372 bytes 101 | Interval: 0x01 102 | Class descriptor 0x25, 8 bytes 103 | Interface 1 alt 3: Audio 104 | Interface descriptor 105 | Length: 9 bytes 106 | Type: 0x04 107 | Interface number: 1 108 | Alternate setting: 3 109 | Number of endpoints: 1 110 | Class: 0x01: Audio 111 | Subclass: 0x02: Streaming 112 | Protocol: 0x20 113 | Interface string: #5 (not seen) 114 | Class descriptor 0x24, 16 bytes 115 | Class descriptor 0x24, 6 bytes 116 | Endpoint 4 OUT (isochronous) 117 | Endpoint descriptor 118 | Length: 7 bytes 119 | Type: 0x05 120 | Endpoint address: 0x04 121 | Attributes: 0x09 122 | Max packet size: 496 bytes 123 | Interval: 0x01 124 | Truncated class descriptor 0x25 (3 of 8 bytes) 125 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "packetry" 3 | version = "0.5.0" 4 | authors = ["Great Scott Gadgets "] 5 | license = "BSD-3-Clause" 6 | description = "A fast, intuitive USB 2.0 protocol analysis application for use with Cynthion." 7 | categories = ["visualization"] 8 | keywords = ["cynthion", "luna", "usb"] 9 | homepage = "https://github.com/greatscottgadgets/packetry" 10 | repository = "https://github.com/greatscottgadgets/packetry" 11 | documentation = "https://packetry.readthedocs.io" 12 | edition = "2024" 13 | rust-version = "1.85.1" 14 | build = "src/build.rs" 15 | default-run = "packetry" 16 | 17 | include = [ 18 | "CHANGELOG.md", 19 | "CODE_OF_CONDUCT.md", 20 | "LICENSE", 21 | "README.md", 22 | "src/**/*", 23 | "tests/**/*", 24 | ] 25 | 26 | [build-dependencies] 27 | built = { version = "0.7.4", features = ["cargo-lock", "git2"] } 28 | 29 | [dependencies] 30 | bytemuck = "1.14.1" 31 | bytemuck_derive = "1.5.0" 32 | gtk = { version = "0.8.0", package = "gtk4", features = ["v4_4"] } 33 | num_enum = "0.7.2" 34 | once_cell = "1.19.0" 35 | pcap-file-gsg = "3.0.0-rc4" 36 | tempfile = "3.9.0" 37 | bitfield = "0.14.0" 38 | num-format = "0.4.4" 39 | humansize = "2.1.3" 40 | derive_more = "0.99.17" 41 | nusb = { version = "0.2.0", features = ["smol"] } 42 | futures-lite = "2.0.1" 43 | futures-channel = "0.3.21" 44 | futures-util = "0.3.21" 45 | serde = { version = "1.0.196", features = ["derive"] } 46 | serde_json = { version = "1.0.113", optional = true } 47 | itertools = "0.12.1" 48 | arc-swap = "1.6.0" 49 | lrumap = "0.1.0" 50 | memmap2 = "0.9.4" 51 | page_size = "0.6.0" 52 | anyhow = { version = "1.0.79", features = ["backtrace"] } 53 | crc = "3.2.1" 54 | usb-ids = "1.2024.4" 55 | dark-light = "1.1.1" 56 | hidreport = "0.4.1" 57 | hut = "0.2.1" 58 | byteorder_slice = "3.0.0" 59 | merge = "0.1.0" 60 | chrono = { version = "0.4.38", default-features = false, features = ["clock"] } 61 | preferences = "2.0.0" 62 | indexmap = "2.11.0" 63 | scoped-panic-hook = "0.1.2" 64 | zip-extensions = { version = "0.8.3", default-features = false, features = ["deflate-flate2", "deflate-flate2-zlib-rs"] } 65 | async-trait = "0.1.89" 66 | portable_async_sleep = { version = "0.1.1", default-features = false } 67 | async-lock = "3.4.1" 68 | 69 | [dev-dependencies] 70 | serde_json = "1.0.113" 71 | rand = "0.8.5" 72 | rand_xorshift = "0.3.0" 73 | libfuzzer-sys = "0.4.7" 74 | 75 | [target.'cfg(target_os = "macos")'.dev-dependencies] 76 | procspawn = "1.0.0" 77 | ctor = "0.2.8" 78 | 79 | [target.'cfg(windows)'.dependencies] 80 | winapi = { version = "0.3.9", features = ["wincon"] } 81 | 82 | # Note: All of the current features are for debugging purposes only! 83 | # 84 | # None of them should be enabled for normal builds of the application. 85 | # 86 | [features] 87 | 88 | # step-decoder: 89 | # 90 | # Used for debugging how transactions and transfers are interpreted 91 | # and displayed as their packets come in one by one. 92 | # 93 | # Listens on TCP port 46563 on 127.0.0.1 while loading a capture, 94 | # and feeds one packet to the decoder each time a byte is received 95 | # from a client connected to that port. 96 | # 97 | # To drive the single stepping, connect with a tool such as netcat, 98 | # i.e. 'nc 127.0.0.1 46563', and press enter to trigger each packet. 99 | # 100 | step-decoder = [] 101 | 102 | # record-ui-test: 103 | # 104 | # Used to record test cases to be executed as part of UI testing. 105 | # 106 | # Writes 'actions.json' and 'output.txt' files in the working directory. 107 | # These files capture the actions taken by the user, and the outputs 108 | # presented by the UI in response. 109 | # 110 | # To add the resulting test case to the test suite, copy these files to 111 | # a new subdirectory of tests/ui, renaming output.txt to reference.txt, 112 | # then add the name of the subdirectory to tests/ui/tests.txt. 113 | # 114 | # The test suite will then repeat these actions and verify that the UI 115 | # responds in the same way as it did previously. 116 | # 117 | # May be used concurrently with step-decoder, in order to produce test 118 | # cases that depend on when the UI was updated in the decoding process. 119 | # 120 | record-ui-test = ["serde_json"] 121 | 122 | # debug-region-map: 123 | # 124 | # Used for debugging the TreeListModel implementation. 125 | # 126 | # Prints information to standard output about the region map data structure 127 | # that is used internally by the model, and the changes being made to it. 128 | # 129 | debug-region-map = [] 130 | 131 | # fuzzer: 132 | # 133 | # Used for fuzzing the decoder. Does not affect the main binary, but enables 134 | # an example target named 'fuzzer'. 135 | # 136 | # Build the fuzzer with: 137 | # 138 | # cargo +nightly rustc --features fuzzer --example fuzzer -- \ 139 | # -C passes='sancov-module' \ 140 | # -C llvm-args='-sanitizer-coverage-level=3' \ 141 | # -C llvm-args='-sanitizer-coverage-inline-8bit-counters' \ 142 | # -Z sanitizer=address 143 | # 144 | # Run with: 145 | # 146 | # target/debug/examples/fuzzer 147 | # 148 | # Not currently working on Windows. 149 | fuzzer = [] 150 | 151 | [[bin]] 152 | name = "packetry" 153 | path = "src/main.rs" 154 | 155 | [[bin]] 156 | name = "packetry-cli" 157 | path = "src/cli.rs" 158 | 159 | [[example]] 160 | name = "fuzzer" 161 | path = "src/fuzzer.rs" 162 | required-features = ["fuzzer"] 163 | 164 | [package.metadata.cargo-all-features] 165 | skip_optional_dependencies = true 166 | -------------------------------------------------------------------------------- /appimage/packetry.AppDir/usr/share/doc/libglib2.0-0/copyright: -------------------------------------------------------------------------------- 1 | This package was debianized by Akira TAGOH on 2 | Thu, 7 Mar 2002 01:05:25 +0900. 3 | 4 | It was downloaded from . 5 | 6 | Original Authors 7 | ---------------- 8 | Peter Mattis 9 | Spencer Kimball 10 | Josh MacDonald 11 | 12 | Please do not mail the original authors asking questions about this 13 | version of GLib. 14 | 15 | GLib Team 16 | --------- 17 | Shawn T. Amundson 18 | Jeff Garzik 19 | Raja R Harinath 20 | Tim Janik 21 | Elliot Lee 22 | Tor Lillqvist 23 | Paolo Molaro 24 | Havoc Pennington 25 | Manish Singh 26 | Owen Taylor 27 | Sebastian Wilhelmi 28 | 29 | The random number generator "Mersenne Twister", which is used by GLib, 30 | was developed and originally coded by: 31 | Makoto Matsumoto 32 | Takuji Nishimura 33 | 34 | Major copyright holders: 35 | 36 | Copyright © 1995-2018 Red Hat, Inc. 37 | Copyright © 2008-2010 Novell, Inc. 38 | Copyright © 2008-2010 Codethink Limited. 39 | Copyright © 2008-2018 Collabora, Ltd. 40 | Copyright © 2018 Endless Mobile, Inc. 41 | Copyright © 2018 Emmanuele Bassi 42 | 43 | License: 44 | 45 | This package is free software; you can redistribute it and/or 46 | modify it under the terms of the GNU Lesser General Public 47 | License as published by the Free Software Foundation; either 48 | version 2 of the License, or (at your option) any later version. 49 | 50 | This package is distributed in the hope that it will be useful, 51 | but WITHOUT ANY WARRANTY; without even the implied warranty of 52 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 53 | Lesser General Public License for more details. 54 | 55 | You should have received a copy of the GNU Lesser General Public 56 | License along with this package; if not, write to the Free Software 57 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 58 | 59 | On Debian systems, the complete text of the GNU Lesser General 60 | Public License can be found in `/usr/share/common-licenses/LGPL'. 61 | 62 | Files: 63 | gobject/tests/taptestrunner.py 64 | Copyright: 65 | 2015 Remko Tronçon 66 | License: Expat 67 | 68 | Files: 69 | tests/gen-casefold-txt.py 70 | tests/gen-casemap-txt.py 71 | Copyright: 72 | 1998-1999 Tom Tromey 73 | 2001 Red Hat Software 74 | License: GPL-2+ 75 | 76 | Files: 77 | debian/debcrossgen 78 | Copyright: 79 | 2017 Jussi Pakkanen 80 | License: Apache-2.0 81 | 82 | License: Apache-2.0 83 | Licensed under the Apache License, Version 2.0 (the "License"); 84 | you may not use this file except in compliance with the License. 85 | You may obtain a copy of the License at 86 | . 87 | http://www.apache.org/licenses/LICENSE-2.0 88 | . 89 | Unless required by applicable law or agreed to in writing, software 90 | distributed under the License is distributed on an "AS IS" BASIS, 91 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 92 | See the License for the specific language governing permissions and 93 | limitations under the License. 94 | . 95 | On Debian systems, a copy of the Apache license is available in 96 | . 97 | 98 | License: Expat 99 | Permission is hereby granted, free of charge, to any person obtaining a copy 100 | of this software and associated documentation files (the "Software"), to deal 101 | in the Software without restriction, including without limitation the rights 102 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 103 | copies of the Software, and to permit persons to whom the Software is 104 | furnished to do so, subject to the following conditions: 105 | . 106 | The above copyright notice and this permission notice shall be included in 107 | all copies or substantial portions of the Software. 108 | . 109 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 110 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 111 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 112 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 113 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 114 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 115 | SOFTWARE. 116 | 117 | License: GPL-2+ 118 | This program is free software; you can redistribute it and/or modify 119 | it under the terms of the GNU General Public License as published by 120 | the Free Software Foundation; either version 2, or (at your option) 121 | any later version. 122 | . 123 | This program is distributed in the hope that it will be useful, 124 | but WITHOUT ANY WARRANTY; without even the implied warranty of 125 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 126 | GNU General Public License for more details. 127 | . 128 | You should have received a copy of the GNU General Public License 129 | along with this program; if not, see . 130 | -------------------------------------------------------------------------------- /wix/rust_licenses.py: -------------------------------------------------------------------------------- 1 | from license_expression import get_spdx_licensing, LicenseSymbol, OR, AND 2 | from tempfile import TemporaryDirectory 3 | import subprocess 4 | import shutil 5 | import os 6 | 7 | licensing = get_spdx_licensing() 8 | 9 | # We accept these licenses unconditionally. 10 | accepted_license_strings = ( 11 | 'MIT', 12 | 'MIT-0', 13 | 'BSD-2-Clause', 14 | 'BSD-3-Clause', 15 | 'BSL-1.0', 16 | 'Unicode-DFS-2016', 17 | 'Unicode-3.0', 18 | 'Zlib', 19 | ) 20 | 21 | # We accept these licenses with manual validation. 22 | conditional_license_strings = ( 23 | # For Apache-2.0, we need to check for NOTICE requirements. 24 | 'Apache-2.0', 25 | 'Apache-2.0 WITH LLVM-exception', 26 | ) 27 | 28 | # These packages have been validated for conditionally accepted licenses. 29 | validated = ( 30 | ('target-lexicon', '0.12.14'), 31 | ('unicode-ident', '1.0.12'), 32 | ) 33 | 34 | # We don't believe these packages are actually linked into Packetry. 35 | excluded = ( 36 | 'fuchsia-zircon', 37 | 'fuchsia-zircon-sys', 38 | ) 39 | 40 | accepted_licenses = [licensing.parse(s) for s in accepted_license_strings] 41 | conditional_licenses = [licensing.parse(s) for s in conditional_license_strings] 42 | 43 | # Validate a license expression is acceptable to us. 44 | def validate_license(package, version, expr): 45 | 46 | # If there is a single license, and it's acceptable, accept it. 47 | if expr in accepted_licenses: 48 | return True 49 | 50 | # If there is a single conditionally accepted license, and we've validated 51 | # it for this specific package version, accept it. 52 | elif expr in conditional_licenses and (package, version) in validated: 53 | return True 54 | 55 | # If the expression is an OR, and some option is acceptable, accept it. 56 | elif isinstance(expr, OR): 57 | for option in expr.get_symbols(): 58 | if validate_license(package, version, option): 59 | return True 60 | else: 61 | return False 62 | 63 | # If the expression is an AND, and all elements are acceptable, accept it. 64 | elif isinstance(expr, AND): 65 | for element in expr.get_symbols(): 66 | if not validate_license(package, version, element): 67 | return False 68 | else: 69 | return True 70 | else: 71 | # Otherwise, return false. 72 | return False 73 | 74 | # Get license information with 'cargo license'. 75 | cargo_result = subprocess.run( 76 | ('cargo', 'license', '--authors', '--do-not-bundle', '--color=never'), 77 | capture_output=True) 78 | 79 | cargo_result.check_returncode() 80 | 81 | # Unpack sources into temporary directory with 'cargo vendor'. 82 | deps = TemporaryDirectory() 83 | subprocess.run( 84 | ('cargo', 'vendor', '--versioned-dirs', deps.name)).check_returncode() 85 | 86 | # Manually collected licenses. 87 | manual_dir = 'wix/manual-licenses' 88 | manual_licenses = os.listdir(manual_dir) 89 | 90 | # Output path for license files. 91 | dest_dir = 'wix/full-licenses' 92 | 93 | try: 94 | os.mkdir(dest_dir) 95 | except FileExistsError: 96 | pass 97 | 98 | print("The following Rust packages are statically linked into Packetry:") 99 | 100 | for line in cargo_result.stdout.decode().rstrip().split("\n"): 101 | package, remainder = line.split(": ") 102 | if package == 'packetry' or package in excluded: 103 | continue 104 | version, license_quoted, by, authors_quoted = remainder.split(", ") 105 | authors = authors_quoted[1:-1].split("|") 106 | license_str = license_quoted[1:-1] 107 | 108 | # Check the license is acceptable for us. 109 | if not validate_license(package, version, licensing.parse(license_str)): 110 | raise ValueError( 111 | f"License '{license_str}' not accepted for {package} {version}.") 112 | 113 | # Where we will write the full license file to. 114 | dest_filename = f'LICENSE-{package}-{version}.txt' 115 | dest_path = os.path.join(dest_dir, dest_filename) 116 | 117 | # Look for a manually collected license. 118 | if dest_filename in manual_licenses: 119 | src_path = os.path.join(manual_dir, dest_filename) 120 | else: 121 | # Look for a license file. 122 | file_paths = ( 123 | ['LICENSE-MIT'], 124 | ['LICENSE-MIT.md'], 125 | ['LICENSE-MIT.txt'], 126 | ['license-mit'], 127 | ['LICENSES', 'MIT.txt'], 128 | ['LICENSE'], 129 | ['LICENSE.txt'], 130 | ['LICENSE.md'], 131 | ['COPYING'], 132 | ['COPYRIGHT-RUST.txt'], 133 | ) 134 | for src_dir in [ 135 | package, 136 | os.path.join(deps.name, f'{package}-{version}') 137 | ]: 138 | for file_path in file_paths: 139 | src_path = os.path.join(src_dir, *file_path) 140 | if os.path.isfile(src_path): 141 | break 142 | else: 143 | continue 144 | break 145 | else: 146 | raise ValueError( 147 | f"No license file found for {package} {version}.") 148 | 149 | # Copy license file to output. 150 | shutil.copyfile(src_path, dest_path) 151 | 152 | print() 153 | print(f"{package} version {version}") 154 | if len(authors) == 1: 155 | print(f"Author: {str.join(', ', authors)}") 156 | else: 157 | print("Authors:") 158 | for author in authors: 159 | print(f" {author}") 160 | print(f"License type: {license_str}") 161 | print(f"License file: full-licenses/{dest_filename}") 162 | print(f"Link: https://crates.io/crates/{package}") 163 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | straithe@greatscottgadgets.com. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /src/ui/item_widget.rs: -------------------------------------------------------------------------------- 1 | //! GObject subclass for the widget we use to display an item. 2 | //! 3 | //! Wraps a GTK box which contains further widgets. 4 | 5 | use std::cell::RefMut; 6 | use gtk::{ 7 | self, 8 | prelude::*, 9 | subclass::prelude::*, 10 | gdk::Rectangle, 11 | glib::{self, SignalHandlerId, clone}, 12 | pango::EllipsizeMode, 13 | EventSequenceState, 14 | Expander, 15 | GestureClick, 16 | Label, 17 | Orientation, 18 | PopoverMenu, 19 | }; 20 | use crate::ui::item_connector::*; 21 | 22 | glib::wrapper! { 23 | /// The outer type exposed to our Rust code. 24 | pub struct ItemWidget(ObjectSubclass) 25 | @extends gtk::Box, gtk::Widget, 26 | @implements gtk::Orientable; 27 | } 28 | 29 | impl ItemWidget { 30 | /// Create a new widget. 31 | pub fn new() -> ItemWidget { 32 | let wrapper: ItemWidget = glib::Object::new::(); 33 | let right_click = GestureClick::new(); 34 | right_click.set_button(3); 35 | right_click.connect_released(clone!(@strong wrapper => 36 | move |gesture, _n, x, y| { 37 | if let Some(context_menu_fn) = wrapper 38 | .imp() 39 | .context_menu_fn 40 | .borrow_mut() 41 | .as_mut() 42 | { 43 | if let Some(new_popover) = context_menu_fn() { 44 | gesture.set_state(EventSequenceState::Claimed); 45 | let mut current_popover = 46 | wrapper.imp().popover.borrow_mut(); 47 | if let Some(old_popover) = current_popover.take() { 48 | old_popover.unparent(); 49 | } 50 | new_popover.set_parent(&wrapper.clone()); 51 | new_popover.set_pointing_to( 52 | Some(&Rectangle::new(x as i32, y as i32, 1, 1)) 53 | ); 54 | new_popover.popup(); 55 | current_popover.replace(new_popover); 56 | } 57 | } 58 | } 59 | )); 60 | wrapper.add_controller(right_click); 61 | wrapper.imp().text_label.replace( 62 | Label::builder() 63 | .margin_top(2) 64 | .margin_bottom(2) 65 | .ellipsize(EllipsizeMode::End) 66 | .build()); 67 | wrapper.imp().connector.replace(ItemConnector::new(None)); 68 | wrapper.imp().expander.replace(Expander::new(None)); 69 | wrapper.append(&wrapper.imp().connector.borrow().clone()); 70 | wrapper.append(&wrapper.imp().expander.borrow().clone()); 71 | wrapper.append(&wrapper.imp().text_label.borrow().clone()); 72 | wrapper.set_orientation(Orientation::Horizontal); 73 | wrapper.set_spacing(5); 74 | wrapper 75 | } 76 | 77 | /// Fetch the Expander from the widget. 78 | pub fn expander(&self) -> RefMut<'_, Expander> { 79 | self.imp().expander.borrow_mut() 80 | } 81 | 82 | /// Store a signal handler to be retained by this widget. 83 | pub fn set_handler(&self, handler: SignalHandlerId) { 84 | self.imp().handler.replace(Some(handler)); 85 | } 86 | 87 | /// Take the signal handler retained by this widget. 88 | pub fn take_handler(&self) -> Option { 89 | self.imp().handler.take() 90 | } 91 | 92 | /// Set the summary text on this widget. 93 | pub fn set_text(&self, text: String) { 94 | self.imp().text_label.borrow_mut().set_text(&text); 95 | } 96 | 97 | /// Set the connecting lines on this widget. 98 | pub fn set_connectors(&self, connectors: String) { 99 | self.imp().connector.borrow_mut().set_shapes(parse_connectors_to_shapes(connectors)); 100 | } 101 | 102 | /// Set the function to build the context menu for this widget. 103 | pub fn set_context_menu_fn(&self, context_menu_fn: F) 104 | where F: FnMut() -> Option + 'static 105 | { 106 | self.imp().context_menu_fn.replace(Some(Box::new(context_menu_fn))); 107 | } 108 | } 109 | 110 | impl Default for ItemWidget { 111 | fn default() -> Self { 112 | Self::new() 113 | } 114 | } 115 | 116 | /// The internal implementation module. 117 | mod imp { 118 | use gtk::{ 119 | self, 120 | prelude::*, 121 | subclass::prelude::*, 122 | glib::{self, SignalHandlerId}, 123 | Expander, 124 | Label, 125 | PopoverMenu, 126 | }; 127 | use std::cell::RefCell; 128 | use crate::ui::item_connector::ItemConnector; 129 | 130 | type PopoverFn = dyn FnMut() -> Option; 131 | 132 | /// The inner type to be used in the GObject type system. 133 | #[derive(Default)] 134 | pub struct ItemWidget { 135 | pub text_label: RefCell