etherparse-0.13.0/.cargo_vcs_info.json0000644000000001360000000000100132570ustar { "git": { "sha1": "05ac511f82c277cf24479d046637824d7efeb210" }, "path_in_vcs": "" }etherparse-0.13.0/Cargo.lock0000644000000165700000000000100112430ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "arrayvec" version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" [[package]] name = "assert_matches" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9" [[package]] name = "autocfg" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "bit-set" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e11e16035ea35e4e5997b393eacbf6f63983188f7a2ad25bfb13465f5ad59de" dependencies = [ "bit-vec", ] [[package]] name = "bit-vec" version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "byteorder" version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "etherparse" version = "0.13.0" dependencies = [ "arrayvec", "assert_matches", "proptest", ] [[package]] name = "fastrand" version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf" dependencies = [ "instant", ] [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "getrandom" version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d39cd93900197114fa1fcb7ae84ca742095eed9442088988ae74fa744e930e77" dependencies = [ "cfg-if", "libc", "wasi", ] [[package]] name = "instant" version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" dependencies = [ "cfg-if", ] [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" version = "0.2.121" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "efaa7b300f3b5fe8eb6bf21ce3895e1751d9665086af2d64b42f19701015ff4f" [[package]] name = "num-traits" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" dependencies = [ "autocfg", ] [[package]] name = "ppv-lite86" version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872" [[package]] name = "proptest" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e0d9cc07f18492d879586c92b485def06bc850da3118075cd45d50e9c95b0e5" dependencies = [ "bit-set", "bitflags", "byteorder", "lazy_static", "num-traits", "quick-error 2.0.1", "rand", "rand_chacha", "rand_xorshift", "regex-syntax", "rusty-fork", "tempfile", ] [[package]] name = "quick-error" version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" [[package]] name = "quick-error" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" [[package]] name = "rand" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha", "rand_core", ] [[package]] name = "rand_chacha" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", "rand_core", ] [[package]] name = "rand_core" version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" dependencies = [ "getrandom", ] [[package]] name = "rand_xorshift" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d25bf25ec5ae4a3f1b92f929810509a2f53d7dca2f50b794ff57e3face536c8f" dependencies = [ "rand_core", ] [[package]] name = "redox_syscall" version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ae183fc1b06c149f0c1793e1eb447c8b04bfe46d48e9e48bfb8d2d7ed64ecf0" dependencies = [ "bitflags", ] [[package]] name = "regex-syntax" version = "0.6.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" [[package]] name = "remove_dir_all" version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" dependencies = [ "winapi", ] [[package]] name = "rusty-fork" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb3dcc6e454c328bb824492db107ab7c0ae8fcffe4ad210136ef014458c1bc4f" dependencies = [ "fnv", "quick-error 1.2.3", "tempfile", "wait-timeout", ] [[package]] name = "tempfile" version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" dependencies = [ "cfg-if", "fastrand", "libc", "redox_syscall", "remove_dir_all", "winapi", ] [[package]] name = "wait-timeout" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" dependencies = [ "libc", ] [[package]] name = "wasi" version = "0.10.2+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" etherparse-0.13.0/Cargo.toml0000644000000023220000000000100112540ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" name = "etherparse" version = "0.13.0" authors = ["Julian Schmid "] exclude = [ ".gitignore", ".travis.yml", ".github/*", ".gitlab-ci.yml", ".travis/*", "appveyor.yml", ] description = "A library for parsing & writing a bunch of packet based protocols (EthernetII, IPv4, IPv6, UDP, TCP ...)." readme = "README.md" keywords = [ "ipv4", "ipv6", "vlan", "udp", "tcp", ] categories = [ "network-programming", "parser-implementations", ] license = "MIT OR Apache-2.0" repository = "https://github.com/JulianSchmid/etherparse" [dependencies.arrayvec] version = "0.7.2" [dev-dependencies.assert_matches] version = "1.5.0" [dev-dependencies.proptest] version = "1.0.0" etherparse-0.13.0/Cargo.toml.orig000064400000000000000000000012601046102023000147350ustar 00000000000000[package] name = "etherparse" version = "0.13.0" authors = ["Julian Schmid "] edition = "2018" repository = "https://github.com/JulianSchmid/etherparse" description = "A library for parsing & writing a bunch of packet based protocols (EthernetII, IPv4, IPv6, UDP, TCP ...)." categories = ["network-programming", "parser-implementations"] keywords = ["ipv4", "ipv6", "vlan", "udp", "tcp"] license = "MIT OR Apache-2.0" readme = "README.md" exclude = [ ".gitignore", ".travis.yml", ".github/*", ".gitlab-ci.yml", ".travis/*", "appveyor.yml" ] [dependencies] arrayvec = "0.7.2" [dev-dependencies] assert_matches = "1.5.0" proptest = "1.0.0" etherparse-0.13.0/LICENSE-APACHE000064400000000000000000000261341046102023000140010ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.etherparse-0.13.0/LICENSE-MIT000064400000000000000000000020551046102023000135050ustar 00000000000000MIT License Copyright (c) 2022 Julian Schmid Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.etherparse-0.13.0/README.md000064400000000000000000000405201046102023000133270ustar 00000000000000[![Crates.io](https://img.shields.io/crates/v/etherparse.svg)](https://crates.io/crates/etherparse) [![docs.rs](https://docs.rs/etherparse/badge.svg)](https://docs.rs/etherparse) [![Build Status Github](https://github.com/JulianSchmid/etherparse/actions/workflows/main.yml/badge.svg?branch=master)](https://github.com/JulianSchmid/etherparse/actions/workflows/main.yml) [![Build Status Appveyor](https://ci.appveyor.com/api/projects/status/kbr91rpaim1mvcca?svg=true)](https://ci.appveyor.com/project/JulianSchmid/etherparse) [![Build Status Gitlab](https://gitlab.com/julian.schmid/etherparse/badges/master/pipeline.svg)](https://gitlab.com/julian.schmid/etherparse/-/commits/master) [![Codecov](https://codecov.io/gh/JulianSchmid/etherparse/branch/master/graph/badge.svg?token=yjfRLgScR6)](https://codecov.io/gh/JulianSchmid/etherparse) # etherparse A zero allocation library for parsing & writing a bunch of packet based protocols (EthernetII, IPv4, IPv6, UDP, TCP ...). Currently supported are: * Ethernet II * IEEE 802.1Q VLAN Tagging Header * IPv4 * IPv6 (supporting the most common extension headers, but not all) * UDP * TCP * ICMP & ICMPv6 (not all message types are supported) ## Usage Add the following to your `Cargo.toml`: ```toml [dependencies] etherparse = "0.13" ``` ## What is etherparse? Etherparse is intended to provide the basic network parsing functions that allow for easy analysis, transformation or generation of recorded network data. Some key points are: * It is completly written in Rust and thoroughly tested. * Special attention has been paid to not use allocations or syscalls. * The package is still in development and can & will still change. * The current focus of development is on the most popular protocols in the internet & transport layer. ## How to parse network packages? Etherparse gives you two options for parsing network packages automatically: ### Slicing the packet Here the different components in a packet are seperated without parsing all their fields. For each header a slice is generated that allows access to the fields of a header. ```rust match SlicedPacket::from_ethernet(&packet) { Err(value) => println!("Err {:?}", value), Ok(value) => { println!("link: {:?}", value.link); println!("vlan: {:?}", value.vlan); println!("ip: {:?}", value.ip); println!("transport: {:?}", value.transport); } } ``` This is the faster option if your code is not interested in all fields of all the headers. It is a good choice if you just want filter or find packages based on a subset of the headers and/or their fields. Depending from which point downward you want to slice a package check out the functions: * [`SlicedPacket::from_ethernet`](https://docs.rs/etherparse/~0/etherparse/struct.SlicedPacket.html#method.from_ethernet) for parsing from an Ethernet II header downwards * [`SlicedPacket::from_ether_type`](https://docs.rs/etherparse/~0/etherparse/struct.SlicedPacket.html#method.from_ether_type) for parsing a slice starting after an Ethernet II header * [`SlicedPacket::from_ip`](https://docs.rs/etherparse/~0/etherparse/struct.SlicedPacket.html#method.from_ip) for parsing from an IPv4 or IPv6 downwards ### Deserializing all headers into structs This option deserializes all known headers and transferes their contents to header structs. ```rust match PacketHeaders::from_ethernet_slice(&packet) { Err(value) => println!("Err {:?}", value), Ok(value) => { println!("link: {:?}", value.link); println!("vlan: {:?}", value.vlan); println!("ip: {:?}", value.ip); println!("transport: {:?}", value.transport); } } ``` This option is slower then slicing when only few fields are accessed. But it can be the faster option or useful if you are interested in most fields anyways or if you want to re-serialize the headers with modified values. Depending from which point downward you want to unpack a package check out the functions * [`PacketHeaders::from_ethernet_slice`](https://docs.rs/etherparse/~0/etherparse/struct.PacketHeaders.html#method.from_ethernet_slice) for parsing from an Ethernet II header downwards * [`PacketHeaders::from_ether_type`](https://docs.rs/etherparse/~0/etherparse/struct.PacketHeaders.html#method.from_ether_type) for parsing a slice starting after an Ethernet II header * [`PacketHeaders::from_ip_slice`](https://docs.rs/etherparse/~0/etherparse/struct.PacketHeaders.html#method.from_ip_slice) for parsing from an IPv4 or IPv6 downwards ### Manually slicing & parsing packets It is also possible to manually slice & parse a packet. For each header type there is are metods that create a slice or struct from a memory slice. Have a look at the documentation for the Slice.from_slice methods, if you want to create your own slices: * [`Ethernet2HeaderSlice::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.Ethernet2HeaderSlice.html#method.from_slice) * [`SingleVlanHeaderSlice::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.SingleVlanHeaderSlice.html#method.from_slice) * [`DoubleVlanHeaderSlice::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.DoubleVlanHeaderSlice.html#method.from_slice) * [`Ipv4HeaderSlice::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.Ipv4HeaderSlice.html#method.from_slice) * [`Ipv4ExtensionsSlice::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.Ipv4ExtensionsSlice.html#method.from_slice) * [`Ipv6HeaderSlice::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.Ipv6HeaderSlice.html#method.from_slice) * [`Ipv6ExtensionsSlice::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.Ipv6ExtensionsSlice.html#method.from_slice) * [`Ipv6RawExtensionHeaderSlice::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.Ipv6RawExtensionHeaderSlice.html#method.from_slice) * [`IpAuthenticationHeaderSlice::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.IpAuthenticationHeaderSlice.html#method.from_slice) * [`Ipv6FragmentHeaderSlice::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.Ipv6FragmentHeaderSlice.html#method.from_slice) * [`UdpHeaderSlice::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.UdpHeaderSlice.html#method.from_slice) * [`TcpHeaderSlice::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.TcpHeaderSlice.html#method.from_slice) * [`Icmpv4Slice::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.Icmpv4Slice.html#method.from_slice) * [`Icmpv6Slice::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.Icmpv6Slice.html#method.from_slice) And for deserialization into the corresponding header structs have a look at: * [`Ethernet2Header::read`](https://docs.rs/etherparse/~0/etherparse/struct.Ethernet2Header.html#method.read) & [`Ethernet2Header::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.Ethernet2Header.html#method.from_slice) * [`SingleVlanHeader::read`](https://docs.rs/etherparse/~0/etherparse/struct.SingleVlanHeader.html#method.read) & [`SingleVlanHeader::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.SingleVlanHeader.html#method.from_slice) * [`DoubleVlanHeader::read`](https://docs.rs/etherparse/~0/etherparse/struct.DoubleVlanHeader.html#method.read) & [`DoubleVlanHeader::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.DoubleVlanHeader.html#method.from_slice) * [`IpHeader::read`](https://docs.rs/etherparse/~0/etherparse/enum.IpHeader.html#method.read) & [`IpHeader::from_slice`](https://docs.rs/etherparse/~0/etherparse/enum.IpHeader.html#method.from_slice) * [`Ipv4Header::read`](https://docs.rs/etherparse/~0/etherparse/struct.Ipv4Header.html#method.read) & [`Ipv4Header::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.Ipv4Header.html#method.from_slice) * [`Ipv4Extensions::read`](https://docs.rs/etherparse/~0/etherparse/struct.Ipv4Extensions.html#method.read) & [`Ipv4Extensions::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.Ipv4Extensions.html#method.from_slice) * [`Ipv6Header::read`](https://docs.rs/etherparse/~0/etherparse/struct.Ipv6Header.html#method.read) & [`Ipv6Header::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.Ipv6Header.html#method.from_slice) * [`Ipv6Extensions::read`](https://docs.rs/etherparse/~0/etherparse/struct.Ipv6Extensions.html#method.read) & [`Ipv6Extensions::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.Ipv6Extensions.html#method.from_slice) * [`Ipv6RawExtensionHeader::read`](https://docs.rs/etherparse/~0/etherparse/struct.Ipv6RawExtensionHeader.html#method.read) & [`Ipv6RawExtensionHeader::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.Ipv6RawExtensionHeader.html#method.from_slice) * [`IpAuthenticationHeader::read`](https://docs.rs/etherparse/~0/etherparse/struct.IpAuthenticationHeader.html#method.read) & [`IpAuthenticationHeader::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.IpAuthenticationHeader.html#method.from_slice) * [`Ipv6FragmentHeader::read`](https://docs.rs/etherparse/~0/etherparse/struct.Ipv6FragmentHeader.html#method.read) & [`Ipv6FragmentHeader::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.Ipv6FragmentHeader.html#method.from_slice) * [`UdpHeader::read`](https://docs.rs/etherparse/~0/etherparse/struct.UdpHeader.html#method.read) & [`UdpHeader::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.UdpHeader.html#method.from_slice) * [`TcpHeader::read`](https://docs.rs/etherparse/~0/etherparse/struct.TcpHeader.html#method.read) & [`TcpHeader::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.TcpHeader.html#method.from_slice) * [`Icmpv4Header::read`](https://docs.rs/etherparse/~0/etherparse/struct.Icmpv4Header.html#method.read) & [`Icmpv4Header::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.Icmpv4Header.html#method.from_slice) * [`Icmpv6Header::read`](https://docs.rs/etherparse/~0/etherparse/struct.Icmpv6Header.html#method.read) & [`Icmpv6Header::from_slice`](https://docs.rs/etherparse/~0/etherparse/struct.Icmpv6Header.html#method.from_slice) ## How to generate fake packet data? ### Packet Builder The PacketBuilder struct provides a high level interface for quickly creating network packets. The PacketBuilder will automatically set fields which can be deduced from the content and compositions of the packet itself (e.g. checksums, lengths, ethertype, ip protocol number). [Example:](examples/write_udp.rs) ```rust use etherparse::PacketBuilder; let builder = PacketBuilder:: ethernet2([1,2,3,4,5,6], //source mac [7,8,9,10,11,12]) //destination mac .ipv4([192,168,1,1], //source ip [192,168,1,2], //desitination ip 20) //time to life .udp(21, //source port 1234); //desitnation port //payload of the udp packet let payload = [1,2,3,4,5,6,7,8]; //get some memory to store the result let mut result = Vec::::with_capacity(builder.size(payload.len())); //serialize //this will automatically set all length fields, checksums and identifiers (ethertype & protocol) //before writing the packet out to "result" builder.write(&mut result, &payload).unwrap(); ``` There is also an [example for TCP packets](examples/write_tcp.rs) available. Check out the [PacketBuilder documentation](https://docs.rs/etherparse/~0/etherparse/struct.PacketBuilder.html) for more informations. ### Manually serialising each header Alternativly it is possible to manually build a packet ([example](examples/write_ipv4_udp.rs)). Generally each struct representing a header has a "write" method that allows it to be serialized. These write methods sometimes automatically calculate checksums and fill them in. In case this is unwanted behavior (e.g. if you want to generate a packet with an invalid checksum), it is also possible to call a "write_raw" method that will simply serialize the data without doing checksum calculations. Read the documentations of the different methods for a more details: * [`Ethernet2Header::write`](https://docs.rs/etherparse/~0/etherparse/struct.Ethernet2Header.html#method.write) * [`SingleVlanHeader::write`](https://docs.rs/etherparse/~0/etherparse/struct.SingleVlanHeader.html#method.write) * [`DoubleVlanHeader::write`](https://docs.rs/etherparse/~0/etherparse/struct.DoubleVlanHeader.html#method.write) * [`Ipv4Header::write`](https://docs.rs/etherparse/~0/etherparse/struct.Ipv4Header.html#method.write) * [`Ipv4Header::write_raw`](https://docs.rs/etherparse/~0/etherparse/struct.Ipv4Header.html#method.write_raw) * [`Ipv4Extensions::write`](https://docs.rs/etherparse/~0/etherparse/struct.Ipv4Extensions.html#method.write) * [`Ipv6Header::write`](https://docs.rs/etherparse/~0/etherparse/struct.Ipv6Header.html#method.write) * [`Ipv6Extensions::write`](https://docs.rs/etherparse/~0/etherparse/struct.Ipv6Extensions.html#method.write) * [`Ipv6RawExtensionHeader::write`](https://docs.rs/etherparse/~0/etherparse/struct.Ipv6RawExtensionHeader.html#method.write) * [`IpAuthenticationHeader::write`](https://docs.rs/etherparse/~0/etherparse/struct.IpAuthenticationHeader.html#method.write) * [`Ipv6FragmentHeader::write`](https://docs.rs/etherparse/~0/etherparse/struct.Ipv6FragmentHeader.html#method.write) * [`UdpHeader::write`](https://docs.rs/etherparse/~0/etherparse/struct.UdpHeader.html#method.write) * [`TcpHeader::write`](https://docs.rs/etherparse/~0/etherparse/struct.TcpHeader.html#method.write) * [`Icmpv4Header::write`](https://docs.rs/etherparse/~0/etherparse/struct.Icmpv4Header.html#method.write) * [`Icmpv6Header::write`](https://docs.rs/etherparse/~0/etherparse/struct.Icmpv6Header.html#method.write) ## Roadmap * MutPacketSlice -> modifaction of fields in slices directly? * Reserializing SlicedPacket & MutSlicedPacket with corrected checksums & id's * IEEE 802.3 ## References * Darpa Internet Program Protocol Specification [RFC 791](https://tools.ietf.org/html/rfc791) * Internet Protocol, Version 6 (IPv6) Specification [RFC 8200](https://tools.ietf.org/html/rfc8200) * [IANA Protocol Numbers](https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml) * [Internet Protocol Version 6 (IPv6) Parameters](https://www.iana.org/assignments/ipv6-parameters/ipv6-parameters.xhtml) * [Wikipedia IEEE_802.1Q](https://en.wikipedia.org/w/index.php?title=IEEE_802.1Q&oldid=820983900) * User Datagram Protocol (UDP) [RFC 768](https://tools.ietf.org/html/rfc768) * Transmission Control Protocol [RFC 793](https://tools.ietf.org/html/rfc793) * TCP Extensions for High Performance [RFC 7323](https://tools.ietf.org/html/rfc7323) * The Addition of Explicit Congestion Notification (ECN) to IP [RFC 3168](https://tools.ietf.org/html/rfc3168) * Robust Explicit Congestion Notification (ECN) Signaling with Nonces [RFC 3540](https://tools.ietf.org/html/rfc3540) * IP Authentication Header [RFC 4302](https://tools.ietf.org/html/rfc4302) * Mobility Support in IPv6 [RFC 6275](https://tools.ietf.org/html/rfc6275) * Host Identity Protocol Version 2 (HIPv2) [RFC 7401](https://tools.ietf.org/html/rfc7401) * Shim6: Level 3 Multihoming Shim Protocol for IPv6 [RFC 5533](https://tools.ietf.org/html/rfc5533) * Computing the Internet Checksum [RFC 1071](https://datatracker.ietf.org/doc/html/rfc1071) * Internet Control Message Protocol [RFC 792](https://datatracker.ietf.org/doc/html/rfc792) * [IANA Internet Control Message Protocol (ICMP) Parameters](https://www.iana.org/assignments/icmp-parameters/icmp-parameters.xhtml) * Requirements for Internet Hosts -- Communication Layers [RFC 1122](https://datatracker.ietf.org/doc/html/rfc1122) * Requirements for IP Version 4 Routers [RFC 1812](https://datatracker.ietf.org/doc/html/rfc1812) * Internet Control Message Protocol (ICMPv6) for the Internet Protocol Version 6 (IPv6) Specification [RFC 4443](https://datatracker.ietf.org/doc/html/rfc4443) * ICMP Router Discovery Messages [RFC 1256](https://datatracker.ietf.org/doc/html/rfc1256) * [Internet Control Message Protocol version 6 (ICMPv6) Parameters](https://www.iana.org/assignments/icmpv6-parameters/icmpv6-parameters.xhtml) * Multicast Listener Discovery (MLD) for IPv6 [RFC 2710](https://datatracker.ietf.org/doc/html/rfc2710) * Neighbor Discovery for IP version 6 (IPv6) [RFC 4861](https://datatracker.ietf.org/doc/html/rfc4861) ## License Licensed under either of Apache License, Version 2.0 or MIT license at your option. The corresponding license texts can be found in the LICENSE-APACHE file and the LICENSE-MIT file. ### Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you shall be licensed as above, without any additional terms or conditions. etherparse-0.13.0/README.tpl000064400000000000000000000023531046102023000135300ustar 00000000000000[![Crates.io](https://img.shields.io/crates/v/etherparse.svg)](https://crates.io/crates/etherparse) [![docs.rs](https://docs.rs/etherparse/badge.svg)](https://docs.rs/etherparse) [![Build Status Github](https://github.com/JulianSchmid/etherparse/actions/workflows/main.yml/badge.svg?branch=master)](https://github.com/JulianSchmid/etherparse/actions/workflows/main.yml) [![Build Status Appveyor](https://ci.appveyor.com/api/projects/status/kbr91rpaim1mvcca?svg=true)](https://ci.appveyor.com/project/JulianSchmid/etherparse) [![Build Status Gitlab](https://gitlab.com/julian.schmid/etherparse/badges/master/pipeline.svg)](https://gitlab.com/julian.schmid/etherparse/-/commits/master) [![Codecov](https://codecov.io/gh/JulianSchmid/etherparse/branch/master/graph/badge.svg?token=yjfRLgScR6)](https://codecov.io/gh/JulianSchmid/etherparse) # {{crate}} {{readme}} ## License Licensed under either of Apache License, Version 2.0 or MIT license at your option. The corresponding license texts can be found in the LICENSE-APACHE file and the LICENSE-MIT file. ### Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you shall be licensed as above, without any additional terms or conditions.etherparse-0.13.0/changelog.md000064400000000000000000000217071046102023000143270ustar 00000000000000# Changelog: ## 0.12.0 * Add `payload_ether_type` method to `SlicedPacket` & `PacketHeaders` ## 0.11.0 ### New Features: * Added partial ICMP and ICMPv6 support (thanks to @robs-zeynet for the PR with the initial implementation). * Added `PacketBuilder::::write` that allows writing without specifying a transport protocol (thanks to @karpawich for the PR) * Added functions [SlicedPacket::from_ether_type](https://docs.rs/etherparse/0.11.0/etherparse/struct.SlicedPacket.html#method.from_ether_type) & [PacketHeaders::from_ether_type](https://docs.rs/etherparse/0.11.0/etherparse/struct.PacketHeaders.html#method.from_ether_type) to slice & decode messages based on the starting `ether type` * `IpHeader::set_payload_len` added to set the length fields in the ip header (thanks to @agrover for the PR). * `InternetSlice::is_fragmenting_payload` added to check for fragmentation (thanks to @agrover for the PR). ### Breaking Changes: * `Ipv4Header::new` changed `protocol` argument type from `IpNumber` to `u8`. * `TransportHeader::Icmpv4` & `TransportHeader::Icmpv6` enum values added * `TransportSlice::Icmpv4`& `TransportSlice::Icmpv6` enum values added ## 0.10.1: Corrected Fragmentation Handling, Additional IP Extension Headers Support & Qualitiy of Life Improvements With this version the support for IPv6 gets extended and bugs in the parsing of fragmented packets as well as authentification headers are fixed. Additionally a bunch of performance improvements are included and new methods have been added (e.g. the method `to_bytes` for headers with static sizes). It has been almost two years since the last update and I think it is fair to say that I underestimated the effort it would take to introduce partial support for IPv6 extension headers. As it was so long sice the last update a bunch of changes have piled on. This also means there are some breaking changes in this version. The next versions will hopefully be smaller and contain some qualitiy of life improvements. Special thanks to @Bren2010 for reporting the errors with fragmented packets. ### Extension headers added to `IpHeader` & `InternetSlice` With the added support for authentification headers (for both IPV4 and IPV6) and additional IPV6 extension headers support a place to store the results when parsing headers or slicing them had be chosen. After some though I decided to put the results into the enum values as a second argument. So the signature of `IpHeader` has changed from ```rust pub enum IpHeader { Version4(Ipv4Header), Version6(Ipv6Header) } ``` to ```rust pub enum IpHeader { Version4(Ipv4Header, Ipv4Extensions), Version6(Ipv6Header, Ipv6Extensions) } ``` and the signature of `InternetSlice` has changed from ```rust pub enum InternetSlice<'a> { Ipv4(Ipv4HeaderSlice<'a>), Ipv6(Ipv6HeaderSlice<'a>, [Option<(u8, Ipv6ExtensionHeaderSlice<'a>)>; IPV6_MAX_NUM_HEADER_EXTENSIONS]), } ``` to ```rust pub enum InternetSlice<'a> { Ipv4(Ipv4HeaderSlice<'a>, Ipv4ExtensionsSlice<'a>), Ipv6(Ipv6HeaderSlice<'a>, Ipv6ExtensionsSlice<'a>), } ``` ### `source()` & `destination()` return static arrays: Previously when slicing packets the the methods for accessing the `source` & `destionation` returned a slice reference: ```rust pub fn source(&self) -> &'a [u8] { ... } ``` which becomes a problem if you want to copy it to an actual header as the header structs expect an fixed-sized array. E.g. `[u8;4]` for IPv4: ```rust Ipv4Header::new( ... // expects [u8;4], so we have to convert the slice into an fixed-sized array [ slice.source()[0], slice.source()[1], slice.source()[2], slice.source()[3], ], ... ) ``` To get around this problem the return types of the `source` & `destination` methods have been changed to return fixed-sized arrays for `Ipv4HeaderSlice`, `Ipv6HeaderSlice` & `Ethernet2HeaderSlice`. E.g. for IPv4 the signature is now ```rust pub fn source(&self) -> [u8;4] { ... } ``` which enables you to simply pass address values to `Ipv4Header::new`: ```rust Ipv4Header::new( ... // much better slice.source(), ... ) ``` Not only makes this change it easier to copy address values from a slice to a header, but it also should bring a minor performance improvements (together with other changes). Fixed-sized arrays don't require slice range checks when acessed and the arrays are small enough that they fit in one or two registers on 64bit systems. ### `UdpHeader::calc_checksum_ipv4*` & `UdpHeader::calc_checksum*` now use a constant for the `protocol` field in the pseudo header Previously checksum calculation functions for udp used a protocol value either given as an argument or taken from the ipv4 headers protocol field in it's checksum calculation. After having a closer look at [RFC 768](https://tools.ietf.org/html/rfc768) and what Wireshark does, this seems to have been a mistake. Specifically when an authentifiction header is present between the ip header and the udp header. In this case `ip_number::UDP` (17) should be used and not the value of the ipv4 header `protocol` field (which will be `ip_number::AUTH` (51)). To resolve this I changed the checksum calculation to always use `ip_number::UDP` and remove all arguments that allow the user to pass in the protocol number from the outside. Which means ```rust impl UdpHeader { pub fn calc_checksum_ipv4_raw(&self, source: [u8;4], destination: [u8;4], protocol: u8, payload: &[u8]) -> Result { // ... } ``` looses the `protocol` argument ```rust impl UdpHeader { pub fn calc_checksum_ipv4_raw(&self, source: [u8;4], destination: [u8;4], payload: &[u8]) -> Result { ``` and ```rust impl UdpHeader { pub fn with_ipv4_checksum(source_port: u16, destination_port: u16, ip_header: &Ipv4Header, payload: &[u8]) -> Result { // ... } pub fn calc_checksum_ipv4(&self, ip_header: &Ipv4Header, payload: &[u8]) -> Result { // .... } ``` will no longer use `ip_header.protocol` in their checksum calculations. ### General: * Corrected decoding & handling of authentification headers & encapsulating security payload for IPv6 packets. * Added support for authentifaction headers in IPv4 packets. * Corrected handling of fragmented packets. `InternetSlice::from_*` & `PacketHeaders::from_*` no longer try to decode packets that have been flaged as fragmented (IPv4 & IPv6). Thanks to @Bren2010 for making a PR & noticing the issue. * Added support for parsing "IPv6 Fragment Headers" & "Authentification Headers" ### Fixed bugs: * The length field in authentification fields was assumed to be in 8 octet units (same as hop-by-hop options header & the routing header). This was incorrect, the length field is in 4 octet units and the code has been corrected to support this. * For the "Encapsulating Security Payload header" it was incorrectly assumed, that the basic build up is the same as for the other header extensions (with a next_header & header length field at the start of the header). Parsing of packets will now stop as soon as a "Encapsulating Security Payload header" is encountered. ### Breaking API changes: * Renamed `TcpOptionElement::Nop` to `TcpOptionElement::Noop` * Renamed `Ipv6ExtensionHeader` to `Ipv6RawExtensionHeader` * Renamed `Ipv6ExtensionHeaderSlice` to `Ipv6RawExtensionHeaderSlice` * Reduced the list of supported headers as `Ipv6RawExtensionHeader` & `Ipv6RawExtensionHeaderSlice` to: * Hop-by-Hop Options Header * Routing Header * Destination Options Header * Mobility Header * Host Identity Protocol * Shim6 Header * Renamed `IpTrafficClass::IPv6AuthenticationHeader` to `IpNumber::AuthenticationHeader`. * Renamed `IpTrafficClass::IPv6EncapSecurityPayload` to `IpNumber::EncapsulatingSecurityPayload` * Renamed `ReadError::VlanDoubleTaggingUnexpectedOuterTpid` to `ReadError::DoubleVlanOuterNonVlanEtherType` * Moved the extensions out of the Ipv6Header[Slice] and into the PacketHeaders & SlicedPacket struct. * `TcpOptionReadError::UnexpectedEndOfSlice` changed from a single value This change had been a long time coming. Originally I coupled the IPv6 header extensions to the ipv6 header under the assumption that they only exist in IPv6. But this was not correct, the authentication header and encapsulating security payload are present in IPv6 as well as IPv4. So seperating this form IPv6 made sense. * Ipv6ExtensionHeader was extended with a slice pointing to the data of the header * Moved `TCP_OPTION_ID_*` contants into a new module `tcp_options::KIND_*` (the old constants still present but marked as deprecated). * Return type of `Ethernet2HeaderSlice::{destination, source}` changed to `[u8;6]` (previously `&'a [u8]`) ### API changes with deprecation warning: The following changes will cause a deprecation warning: * Renamed `IpTrafficClass` to `IpNumber`. Traffic class was just the wrong name and confusing as there is a traffic class field in IPv6 headers. * Renamed `read_from_slice` methods to `from_slice`. etherparse-0.13.0/examples/read_by_slicing.rs000064400000000000000000000061301046102023000173500ustar 00000000000000extern crate etherparse; use etherparse::*; fn main() { //setup some network data to parse let builder = PacketBuilder:: ethernet2([1,2,3,4,5,6], //source mac [7,8,9,10,11,12]) //destionation mac .ipv4([192,168,1,1], //source ip [192,168,1,2], //desitionation ip 20) //time to life .udp(21, //source port 1234); //desitnation port //payload of the udp packet let payload = [1,2,3,4,5,6,7,8]; //get some memory to store the serialized data let mut serialized = Vec::::with_capacity( builder.size(payload.len())); builder.write(&mut serialized, &payload).unwrap(); //slice the packet into the different header components let sliced_packet = SlicedPacket::from_ethernet(&serialized); //print some informations about the sliced packet match sliced_packet { Err(value) => println!("Err {:?}", value), Ok(value) => { println!("Ok"); use crate::LinkSlice::*; use crate::InternetSlice::*; use crate::TransportSlice::*; use crate::VlanSlice::*; match value.link { Some(Ethernet2(value)) => println!(" Ethernet2 {:?} => {:?}", value.source(), value.destination()), None => {} } match value.vlan { Some(SingleVlan(value)) => println!(" SingleVlan {:?}", value.vlan_identifier()), Some(DoubleVlan(value)) => println!(" DoubleVlan {:?}, {:?}", value.outer().vlan_identifier(), value.inner().vlan_identifier()), None => {} } match value.ip { Some(Ipv4(value, extensions)) => { println!(" Ipv4 {:?} => {:?}", value.source_addr(), value.destination_addr()); if false == extensions.is_empty() { println!(" {:?}", extensions); } }, Some(Ipv6(value, extensions)) => { println!(" Ipv6 {:?} => {:?}", value.source_addr(), value.destination_addr()); if false == extensions.is_empty() { println!(" {:?}", extensions); } }, None => {} } match value.transport { Some(Icmpv4(value)) => println!(" Icmpv4 {:?}", value), Some(Icmpv6(value)) => println!(" Icmpv6 {:?}", value), Some(Udp(value)) => println!(" UDP {:?} -> {:?}", value.source_port(), value.destination_port()), Some(Tcp(value)) => { println!(" TCP {:?} -> {:?}", value.source_port(), value.destination_port()); let options: Vec> = value.options_iterator().collect(); println!(" {:?}", options); } Some(Unknown(ip_protocol)) => println!(" Unknwon Protocol (ip protocol number {:?}", ip_protocol), None => {} } } } }etherparse-0.13.0/examples/write_ipv4_udp.rs000064400000000000000000000045471046102023000172110ustar 00000000000000extern crate etherparse; use etherparse::*; fn main() { let with_udp_checksum = true; //Any struct implementing the "Write" trait can be used to write to (e.g. File). //For this example lets use a simple Vec as it implements the write trait. let mut out = Vec::::with_capacity( //lets reserve enough memory to avoid unnecessary allocations Ethernet2Header::SERIALIZED_SIZE + Ipv4Header::SERIALIZED_SIZE + UdpHeader::SERIALIZED_SIZE + 8 //payload ); //setup the actual payload of the udp packet let udp_payload = [1,2,3,4,5,6,7,8]; //Lets start out with an ethernet II header containing the mac addresses Ethernet2Header{ destination: [1,2,3,4,5,6], source: [11,12,13,14,15,16], ether_type: ether_type::IPV4 }.write(&mut out).unwrap(); //create the ipv4 header with the helper function //Note: It is also possible to define the rest of the header values via Ipv4Header {...} let ip_header = Ipv4Header::new( //payload length (UdpHeader::SERIALIZED_SIZE + udp_payload.len()) as u16, //time to live 20, //contained protocol is udp ip_number::UDP, //source ip address [192,168,1,42], //destination ip address [192,168,1,1] ); //write the ipv4 header //The "write" call automatically calculates the ipv4 checksum. //Alternatively "write_raw" can be used to skip the checksum //calculation and just write out the checksum set in the header. ip_header.write(&mut out).unwrap(); //write the udp header //There is the option to write it with a checksum or without. //If yes, the ipv4 header & payload are needed to calculate the header if with_udp_checksum { UdpHeader::with_ipv4_checksum( //source port 0, //destination port 42, //ip header &ip_header, //udp payload &udp_payload ).unwrap().write(&mut out).unwrap(); } else { //write the header with the checksum disabled UdpHeader::without_ipv4_checksum( //source port 0, //destination port 42, //payload length udp_payload.len() ).unwrap().write(&mut out).unwrap(); } println!("{:?}", &out); }etherparse-0.13.0/examples/write_tcp.rs000064400000000000000000000023461046102023000162400ustar 00000000000000extern crate etherparse; use etherparse::*; fn main() { //setup the packet headers let builder = PacketBuilder:: ethernet2([1,2,3,4,5,6], //source mac [7,8,9,10,11,12]) //destionation mac .ipv4([192,168,1,1], //source ip [192,168,1,2], //desitionation ip 20) //time to life .tcp(21, //source port 1234, //desitnation port 1, //sequence number 26180) //window size //set additional tcp header fields .ns() //set the ns flag //supported flags: ns(), fin(), syn(), rst(), psh(), ece(), cwr() .ack(123) //ack flag + the ack number .urg(23) //urg flag + urgent pointer //tcp header options .options(&[ TcpOptionElement::Noop, TcpOptionElement::MaximumSegmentSize(1234) ]).unwrap(); //payload of the tcp packet let payload = [1,2,3,4,5,6,7,8]; //get some memory to store the result let mut result = Vec::::with_capacity( builder.size(payload.len())); //serialize //this will automatically set all length fields, checksums and identifiers (ethertype & protocol) builder.write(&mut result, &payload).unwrap(); println!("{:?}", result); }etherparse-0.13.0/examples/write_udp.rs000064400000000000000000000014731046102023000162420ustar 00000000000000extern crate etherparse; use etherparse::*; fn main() { //setup the packet headers let builder = PacketBuilder:: ethernet2([1,2,3,4,5,6], //source mac [7,8,9,10,11,12]) //destionation mac .ipv4([192,168,1,1], //source ip [192,168,1,2], //desitionation ip 20) //time to life .udp(21, //source port 1234); //desitnation port //payload of the udp packet let payload = [1,2,3,4,5,6,7,8]; //get some memory to store the result let mut result = Vec::::with_capacity( builder.size(payload.len())); //serialize //this will automatically set all length fields, checksums and identifiers (ethertype & protocol) builder.write(&mut result, &payload).unwrap(); println!("{:?}", result); }etherparse-0.13.0/scripts/README.md000064400000000000000000000016621046102023000150220ustar 00000000000000# Scripts This folder contains some helpfull scripts that can be helpfull during development. ## Pre-Requirements To run the scripts in this folder you have to install a few tools on your system. You can use the following script to install the required tools (just replace `brew` with the packet manager of your choice). ```sh # nightly toolchain rustup toolchain install nightly # llvm toolchain for nightly rustup component add --toolchain nightly llvm-tools-preview # cargo-binutils and rustfilt for nightly cargo +nightly install cargo-binutils rustfilt # jq from your package manager of choice (just replace brew with apt or a similar manager) brew install jq ``` ## coverage.bash `coverage.bash` calculates the region & line based code coverage of the tests. Just execute it and it will write the reports to `target/coverage`. Note: When executing this script `cargo clean` be executed and previous coverage data will be deleted. etherparse-0.13.0/scripts/coverage.bash000075500000000000000000000076061046102023000162040ustar 00000000000000#!/usr/bin/env bash # PRE-REQUIREMENTS: # # To execute this script you have to have a few additional things installed # on your system. You can run the following commands to do so: # # ```sh # # nightly toolchain # rustup toolchain install nightly # # # llvm toolchain for nightly # rustup component add --toolchain nightly llvm-tools-preview # # # cargo-binutils and rustfilt for nightly # cargo +nightly install cargo-binutils rustfilt # # # jq from your package manager of choice (just replace brew with apt or a similar manager) # brew install jq # ``` # switch to the etherparse root directory pushd "$(dirname "${BASH_SOURCE[0]}")/.." # folder for all the coverage files coverage_dir="target/coverage" # make sure no cached data is used (can interfere with the instrumentalisation and testruns) cargo clean # remove previous runs and setup the result folders rm -rf "${coverage_dir}" mkdir -p "${coverage_dir}" mkdir -p "${coverage_dir}/raw" # run the instrumented tests RUSTFLAGS="-Zinstrument-coverage" \ LLVM_PROFILE_FILE="${coverage_dir}/raw/coverage-%m.profraw" \ cargo +nightly test --tests # determine the filenames of the run executables RUSTFLAGS="-Zinstrument-coverage" \ LLVM_PROFILE_FILE="${coverage_dir}/raw/coverage-%m.profraw" \ cargo +nightly test --no-run --message-format=json | jq -r "select(.profile.test == true) | .filenames[]" | grep -v dSYM - > "${coverage_dir}/raw/filenames.txt" cargo profdata -- merge -sparse "${coverage_dir}/raw/coverage-"*".profraw" -o "${coverage_dir}/raw/merge.profdata" cargo cov -- report \ --use-color \ --summary-only \ --Xdemangler=rustfilt \ --ignore-filename-regex='/.cargo/registry' \ --ignore-filename-regex='/.rustup/toolchains' \ --ignore-filename-regex='/rustc' \ "--instr-profile=${coverage_dir}/raw/merge.profdata" \ $(printf -- "-object %s " $(cat "${coverage_dir}/raw/filenames.txt")) \ > "${coverage_dir}/report_all.txt" cargo cov -- report \ --use-color \ --summary-only \ --Xdemangler=rustfilt \ --ignore-filename-regex='/.cargo/registry' \ --ignore-filename-regex='/.rustup/toolchains' \ --ignore-filename-regex='/rustc' \ --ignore-filename-regex='tests/' \ "--instr-profile=${coverage_dir}/raw/merge.profdata" \ $(printf -- "-object %s " $(cat "${coverage_dir}/raw/filenames.txt")) \ > "${coverage_dir}/report_without_tests.txt" cargo cov -- show --format=html \ --Xdemangler=rustfilt \ --ignore-filename-regex='/.cargo/registry' \ --ignore-filename-regex='/.rustup/toolchains' \ --ignore-filename-regex='/rustc' \ "--instr-profile=${coverage_dir}/raw/merge.profdata" \ $(printf -- "-object %s " $(cat "${coverage_dir}/raw/filenames.txt")) \ "--output-dir=${coverage_dir}/html_all" cargo cov -- show --format=html \ --Xdemangler=rustfilt \ --ignore-filename-regex='/.cargo/registry' \ --ignore-filename-regex='/.rustup/toolchains' \ --ignore-filename-regex='/rustc' \ --ignore-filename-regex='tests/' \ "--instr-profile=${coverage_dir}/raw/merge.profdata" \ $(printf -- "-object %s " $(cat "${coverage_dir}/raw/filenames.txt")) \ "--output-dir=${coverage_dir}/html_without_tests" cargo cov -- export --format=lcov \ --Xdemangler=rustfilt \ --ignore-filename-regex='/.cargo/registry' \ --ignore-filename-regex='/.rustup/toolchains' \ --ignore-filename-regex='/rustc' \ "--instr-profile=${coverage_dir}/raw/merge.profdata" \ $(printf -- "-object %s " $(cat "${coverage_dir}/raw/filenames.txt")) \ > "${coverage_dir}/export.lcov.txt" cargo cov -- export --format=text \ --Xdemangler=rustfilt \ --ignore-filename-regex='/.cargo/registry' \ --ignore-filename-regex='/.rustup/toolchains' \ --ignore-filename-regex='/rustc' \ "--instr-profile=${coverage_dir}/raw/merge.profdata" \ $(printf -- "-object %s " $(cat "${coverage_dir}/raw/filenames.txt")) \ > "${coverage_dir}/export.json" popdetherparse-0.13.0/src/checksum.rs000064400000000000000000001002521046102023000150060ustar 00000000000000/// Helper for calculating the sum of all 16 bit words checksums used in /// in checksum fields in TCP and UDP headers. #[derive(Clone, Debug, Default, Eq, PartialEq)] pub struct Sum16BitWords { /// Partial sum #[cfg(target_pointer_width = "64")] sum: u64, /// Partial sum #[cfg(target_pointer_width = "32")] sum: u32, } impl Sum16BitWords { pub fn new() -> Sum16BitWords { Sum16BitWords { sum: 0 } } /// Add the given slice to the checksum. In case the slice /// has a length that is not multiple of 2 the last byte /// will be padded with 0. #[inline] #[cfg(target_pointer_width = "32")] pub fn add_slice(self, slice: &[u8]) -> Sum16BitWords { Sum16BitWords { sum: u32_16bit_word::add_slice(self.sum, slice) } } /// Add the given slice to the checksum. In case the slice /// has a length that is not multiple of 2 the last byte /// will be padded with 0. #[inline] #[cfg(target_pointer_width = "64")] pub fn add_slice(self, slice: &[u8]) -> Sum16BitWords { Sum16BitWords { sum: u64_16bit_word::add_slice(self.sum, slice) } } /// Add a 2 byte word. #[inline] #[cfg(target_pointer_width = "32")] pub fn add_2bytes(self, value: [u8;2]) -> Sum16BitWords { Sum16BitWords { sum: u32_16bit_word::add_2bytes(self.sum, value) } } /// Add a 2 byte word. #[inline] #[cfg(target_pointer_width = "64")] pub fn add_2bytes(self, value: [u8;2]) -> Sum16BitWords { Sum16BitWords { sum: u64_16bit_word::add_2bytes(self.sum, value) } } /// Add a 4 byte word. #[inline] #[cfg(target_pointer_width = "32")] pub fn add_4bytes(&mut self, value: [u8;4]) -> Sum16BitWords { Sum16BitWords { sum: u32_16bit_word::add_4bytes(self.sum, value) } } /// Add a 4 byte word. #[inline] #[cfg(target_pointer_width = "64")] pub fn add_4bytes(&mut self, value: [u8;4]) -> Sum16BitWords { Sum16BitWords { sum: u64_16bit_word::add_4bytes(self.sum, value) } } /// Add a 8 byte word. #[inline] #[cfg(target_pointer_width = "32")] pub fn add_8bytes(&mut self, value: [u8;8]) -> Sum16BitWords { self .add_4bytes([value[0], value[1], value[2], value[3]]) .add_4bytes([value[4], value[5], value[6], value[7]]) } /// Add a 8 byte word. #[inline] #[cfg(target_pointer_width = "64")] pub fn add_8bytes(&mut self, value: [u8;8]) -> Sum16BitWords { Sum16BitWords { sum: u64_16bit_word::add_8bytes(self.sum, value) } } /// Add a 16 bytes. #[inline] pub fn add_16bytes(&mut self, value: [u8;16]) -> Sum16BitWords { self .add_8bytes([value[0], value[1], value[2], value[3], value[4], value[5], value[6], value[7]]) .add_8bytes([value[8], value[9], value[10], value[11], value[12], value[13], value[14], value[15]]) } /// Converts summed up words from an u32 to an u16 ones complement /// which can be used in a ipv4 checksum. #[inline] #[cfg(target_pointer_width = "32")] pub fn ones_complement(&self) -> u16 { u32_16bit_word::ones_complement(self.sum) } /// Converts summed up words from an u32 to an u16 ones complement /// which can be used in a ipv4 checksum. #[inline] #[cfg(target_pointer_width = "64")] pub fn ones_complement(&self) -> u16 { u64_16bit_word::ones_complement(self.sum) } /// Converts summed up words from an u32 to an u16 ones complement /// with 0 beeing replaced by 0xffff (usefull for TCP and UDP). /// /// This kind of checksum is used in TCP and UDP headers. #[inline] #[cfg(target_pointer_width = "32")] pub fn to_ones_complement_with_no_zero(&self) -> u16 { u32_16bit_word::ones_complement_with_no_zero(self.sum) } /// Converts summed up words from an u32 to an u16 ones complement /// with 0 beeing replaced by 0xffff (usefull for TCP and UDP). /// /// This kind of checksum is used in TCP and UDP headers. #[inline] #[cfg(target_pointer_width = "64")] pub fn to_ones_complement_with_no_zero(&self) -> u16 { u64_16bit_word::ones_complement_with_no_zero(self.sum) } } #[cfg(test)] mod sum16_bit_words_tests { use super::*; #[test] fn new() { assert_eq!( 0xffff, Sum16BitWords::new().ones_complement() ); } #[test] fn add_slice() { assert_eq!( !u16::from_ne_bytes([0x12, 0x34]), Sum16BitWords::new().add_slice(&[0x12, 0x34]) .ones_complement() ); } #[test] fn add_2bytes() { assert_eq!( !u16::from_ne_bytes([0xf0, 0x0f]), Sum16BitWords::new() .add_2bytes([0xf0, 0x0f]) .ones_complement() ); } #[test] fn add_4bytes() { assert_eq!( !( u16::from_ne_bytes([0x12, 0x34]) + u16::from_ne_bytes([0x56, 0x78]) ), Sum16BitWords::new() .add_4bytes([0x12, 0x34, 0x56, 0x78]) .ones_complement() ); } #[test] fn add_8bytes() { assert_eq!( !( u16::from_ne_bytes([0x12, 0x34]) + u16::from_ne_bytes([0x56, 0x78]) + u16::from_ne_bytes([0x23, 0x22]) + u16::from_ne_bytes([0x34, 0x11]) ), Sum16BitWords::new() .add_8bytes([0x12, 0x34, 0x56, 0x78, 0x23, 0x22, 0x34, 0x11]) .ones_complement() ); } #[test] fn add_16bytes() { assert_eq!( u32_16bit_word::ones_complement( u32_16bit_word::add_4bytes( u32_16bit_word::add_4bytes( u32_16bit_word::add_4bytes( u32_16bit_word::add_4bytes( 0, [0x12, 0x34, 0x56, 0x78] ), [0x9a, 0xbc, 0xde, 0xf0] ), [0x0f, 0xed, 0xcb, 0xa9] ), [0x87, 0x65, 0x43, 0x21] ) ), Sum16BitWords::new() .add_16bytes( [ 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x0f, 0xed, 0xcb, 0xa9, 0x87, 0x65, 0x43, 0x21, ] ) .ones_complement() ); } #[test] fn ones_complement() { assert_eq!( !u16::from_ne_bytes([0xf0, 0x0f]), Sum16BitWords::new() .add_2bytes([0xf0, 0x0f]) .ones_complement() ); } #[test] fn to_ones_complement_with_no_zero() { // normal case assert_eq!( !u16::from_ne_bytes([0xf0, 0x0f]), Sum16BitWords::new() .add_2bytes([0xf0, 0x0f]) .to_ones_complement_with_no_zero() ); // zero case assert_eq!( 0xffffu16, Sum16BitWords::new() // ones complement would result in 0 // will be converted to 0xffff as 0 // is a reserved value .add_2bytes([0xff, 0xff]) .to_ones_complement_with_no_zero() ); } #[test] fn debug() { let input = Sum16BitWords::new(); assert_eq!( &format!( "Sum16BitWords {{ sum: {} }}", input.sum ), &format!("{:?}", input) ); } #[test] fn default() { let d: Sum16BitWords = Default::default(); assert_eq!(d.sum, 0); } #[test] fn clone_eq() { let value = Sum16BitWords::new(); assert_eq!( value.clone(), value ) } } /// Helper functions for calculating a 16 bit checksum using /// a u32 to sum up all values. pub mod u32_16bit_word { /// Add a 4 byte word. #[inline] pub fn add_4bytes(start: u32, value: [u8;4]) -> u32 { let (sum, carry) = start.overflowing_add( u32::from_ne_bytes(value) ); sum + (carry as u32) } /// Add a 2 byte word. #[inline] pub fn add_2bytes(start: u32, value: [u8;2]) -> u32 { let (sum, carry) = start.overflowing_add( u32::from( u16::from_ne_bytes(value) ) ); sum + (carry as u32) } /// Add the given slice to the checksum. In case the slice /// has a length that is not multiple of 2 the last byte /// will be padded with 0. #[inline] pub fn add_slice(start_sum: u32, slice: &[u8]) -> u32 { let mut sum : u32 = start_sum; // sum up all 4 byte values let end_32 = slice.len() - (slice.len() % 4); for i in (0..end_32).step_by(4) { sum = add_4bytes( sum, // SAFETY: // Guranteed to always have at least 4 bytes to read // from i. As end_32 is gurenateed to be a multiple of // 4 bytes with a size equal or less then slice.len(). unsafe { [ *slice.get_unchecked(i), *slice.get_unchecked(i + 1), *slice.get_unchecked(i + 2), *slice.get_unchecked(i + 3), ] } ); } // in case 2 bytes are left add them as an word if slice.len() - end_32 >= 2 { sum = add_2bytes( sum, // SAFETY: // If check gurantees there to be at least // 2 bytes. unsafe { [ *slice.get_unchecked(end_32), *slice.get_unchecked(end_32 + 1), ] } ); } // unaligned end pad the last byte with if 0 != slice.len() % 2 { sum = add_2bytes( sum, // SAFETY: // If check gurantees there to be at least // 2 bytes. unsafe { [ *slice.get_unchecked(slice.len() - 1), 0 ] } ); } // done sum } /// Converts summed up words from an u32 to an u16 with 0 beeing replaced by 0xffff (usefull /// for TCP and UDP headers). /// /// This kind of checksum is used in TCP and udp headers. #[inline] pub fn ones_complement_with_no_zero(sum: u32) -> u16 { // In case of 0 use the ones complement (zero is reserved // value for no checksum). let u16value = ones_complement(sum); if u16value == 0 { 0xffff } else { u16value } } /// Converts summed up words from an u32 to an u16 which can be used in a ipv4. #[inline] pub fn ones_complement(sum: u32) -> u16 { // Add the upper 16 bits to the lower 16 bits twice. // // Notes: Two carry adds are needed as the first one could // result in an additional carry add. let first = ((sum >> 16) & 0xffff) + (sum & 0xffff); let u16value = (((first >> 16) & 0xffff) + (first & 0xffff)) as u16; // switch back to big endian (allows to use // native endinaess during calculations). !u16value } #[cfg(test)] mod tests { use super::*; #[test] fn add_4bytes_test() { // trivial case assert_eq!( 0, add_4bytes(0, [0,0,0,0]) ); // check that the carry gets added assert_eq!( 0xffff_ffff, // normal overflow would result in 0xffff_fffe add_4bytes(0xffff_ffff, [0xff,0xff,0xff,0xff]) ); // non max & min values assert_eq!( 0x1234_5678 + u32::from_ne_bytes([0x23,0x45,0x67,0x89]), add_4bytes(0x1234_5678, [0x23,0x45,0x67,0x89]) ); } #[test] fn add_2bytes_test() { // trivial case assert_eq!( 0, add_2bytes(0, [0,0]) ); // check that the carry gets added assert_eq!( 0x0000_ffff, // normal overflow would result in 0x10000fffe add_2bytes(0xffff_ffff, [0xff,0xff]) ); // non max & min values assert_eq!( 0x1234_5678 + u32::from(u16::from_ne_bytes([0x23,0x45])), add_2bytes(0x1234_5678, [0x23,0x45]) ); } #[test] fn add_slice_test() { // empty assert_eq!( 0x1234, add_slice(0x1234, &[]) ); // aligned assert_eq!( 0x1 + u32::from_ne_bytes([0x11, 0x12, 0x13, 0x14]) + u32::from_ne_bytes([0x15, 0x16, 0x17, 0x18]) + u32::from_ne_bytes([0x19, 0x1a, 0x1b, 0x1c]) + u32::from_ne_bytes([0x1d, 0x1e, 0x1f, 0x10]), add_slice( 0x1, &[ 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x10, ] ) ); // aligned with carry assert_eq!( 0x1 + 0x3 + // expected carry u32::from_ne_bytes([0xf1, 0x11, 0x10, 0xf0]).wrapping_add( u32::from_ne_bytes([0xf2, 0x12, 0x11, 0xf1]).wrapping_add( u32::from_ne_bytes([0xf3, 0x13, 0x12, 0xf2]).wrapping_add( u32::from_ne_bytes([0xf4, 0x14, 0x13, 0xf3]) ) ) ), add_slice( 0x1, &[ 0xf1, 0x11, 0x10, 0xf0, 0xf2, 0x12, 0x11, 0xf1, 0xf3, 0x13, 0x12, 0xf2, 0xf4, 0x14, 0x13, 0xf3, ] ) ); // 1 byte unalgined assert_eq!( 0x1 + u32::from_ne_bytes([0x11, 0x12, 0x13, 0x14]) + u32::from_ne_bytes([0x15, 0x16, 0x17, 0x18]) + u32::from_ne_bytes([0x19, 0x1a, 0x1b, 0x1c]) + u32::from(u16::from_ne_bytes([0x1d, 0x1e])) + u32::from(u16::from_ne_bytes([0x1f, 0x00])), add_slice( 0x1, &[ 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, ] ) ); // 2 byte unaligned assert_eq!( 0x1 + u32::from_ne_bytes([0x11, 0x12, 0x13, 0x14]) + u32::from_ne_bytes([0x15, 0x16, 0x17, 0x18]) + u32::from_ne_bytes([0x19, 0x1a, 0x1b, 0x1c]) + u32::from(u16::from_ne_bytes([0x1d, 0x1e])), add_slice( 0x1, &[ 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, ] ) ); // 4 byte unaligned assert_eq!( 0x1 + u32::from_ne_bytes([0x11, 0x12, 0x13, 0x14]) + u32::from_ne_bytes([0x15, 0x16, 0x17, 0x18]) + u32::from_ne_bytes([0x19, 0x1a, 0x1b, 0x1c]) + u32::from(u16::from_ne_bytes([0x1d, 0x00])), add_slice( 0x1, &[ 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, ] ) ); } #[test] fn ones_complement_with_no_zero_test() { // zero case assert_eq!( 0xffff, ones_complement_with_no_zero(0) ); // 0xffff should stay 0xffff (0 is reserved for no checksum) assert_eq!( 0xffff, ones_complement_with_no_zero(0xffff) ); // big endian conversion check assert_eq!( !0x1234u16, ones_complement_with_no_zero(0x1234), ); // add of the upper and lower 16 bits without a carry assert_eq!( !(0x2345u16+0x1234), ones_complement_with_no_zero(0x2345_1234), ); // add which in itself will again produce a carry assert_eq!( !(((0x1456u32+0xf123u32+1u32) & 0xffff) as u16), ones_complement_with_no_zero(0x1456_f123), ); } #[test] fn ones_complement_test() { // zero case assert_eq!( 0xffff, ones_complement(0) ); // check that zero is not reserved assert_eq!( 0, ones_complement(0xffff) ); // big endian conversion check assert_eq!( !0x1234u16, ones_complement(0x1234), ); // add of the upper and lower 16 bits without a carry assert_eq!( !(0x2345u16+0x1234u16), ones_complement(0x2345_1234), ); // add which in itself will again produce a carry assert_eq!( !(((0x1456u32+0xf123u32+1u32) & 0xffff) as u16), ones_complement(0x1456_f123), ); } } } /// Helper functions for calculating a 16 bit checksum using /// a u64 to sum up all values. pub mod u64_16bit_word { /// Add a 8 byte word. #[inline] pub fn add_8bytes(start: u64, value: [u8;8]) -> u64 { let (sum, carry) = start.overflowing_add( u64::from_ne_bytes(value) ); sum + (carry as u64) } /// Add a 4 byte word. #[inline] pub fn add_4bytes(start: u64, value: [u8;4]) -> u64 { let (sum, carry) = start.overflowing_add( u64::from( u32::from_ne_bytes(value) ) ); sum + (carry as u64) } /// Add a 2 byte word. #[inline] pub fn add_2bytes(start: u64, value: [u8;2]) -> u64 { let (sum, carry) = start.overflowing_add( u64::from( u16::from_ne_bytes(value) ) ); sum + (carry as u64) } /// Add the given slice to the checksum. In case the slice /// has a length that is not multiple of 2 the last byte /// will be padded with 0. #[inline] pub fn add_slice(start_sum: u64, slice: &[u8]) -> u64 { let mut sum : u64 = start_sum; // sum up all 4 byte values let end_64 = slice.len() - (slice.len() % 8); for i in (0..end_64).step_by(8) { sum = add_8bytes( sum, // SAFETY: // Guranteed to always have at least 8 bytes to read // from i. As end_64 is gurenateed to be a multiple of // 8 bytes with a size equal or less then slice.len(). unsafe { [ *slice.get_unchecked(i), *slice.get_unchecked(i + 1), *slice.get_unchecked(i + 2), *slice.get_unchecked(i + 3), *slice.get_unchecked(i + 4), *slice.get_unchecked(i + 5), *slice.get_unchecked(i + 6), *slice.get_unchecked(i + 7), ] } ); } // in case 4 or more bytes are left add the first 4 bytes let end_32 = if slice.len() - end_64 >= 4 { sum = add_4bytes( sum, // SAFETY: // If check gurantees there to be at least // 2 bytes. unsafe { [ *slice.get_unchecked(end_64), *slice.get_unchecked(end_64 + 1), *slice.get_unchecked(end_64 + 2), *slice.get_unchecked(end_64 + 3), ] } ); // shift by 4 end_64 + 4 } else { end_64 }; // in case 2 bytes are left add them as an word if slice.len() - end_32 >= 2 { sum = add_2bytes( sum, // SAFETY: // If check gurantees there to be at least // 2 bytes. unsafe { [ *slice.get_unchecked(end_32), *slice.get_unchecked(end_32 + 1), ] } ); } // unaligned end pad the last byte with if 0 != slice.len() % 2 { sum = add_2bytes( sum, // SAFETY: // If check gurantees there to be at least // 2 bytes. unsafe { [ *slice.get_unchecked(slice.len() - 1), 0 ] } ); } // done sum } /// Converts summed up words from an u64 to an u16 with 0 beeing replaced by 0xffff (usefull /// for TCP and UDP headers). /// /// This kind of checksum is used in TCP and udp headers. #[inline] pub fn ones_complement_with_no_zero(sum: u64) -> u16 { // In case of 0 use the ones complement (zero is reserved // value for no checksum). let u16value = ones_complement(sum); if u16value == 0 { 0xffff } else { u16value } } /// Converts summed up words from an u64 to an u16 which can be used in a ipv4. #[inline] pub fn ones_complement(sum: u64) -> u16 { let first = ((sum >> 48) & 0xffff) + ((sum >> 32) & 0xffff) + ((sum >> 16) & 0xffff) + (sum & 0xffff); // Add the upper 16 bits to the lower 16 bits twice. // // Notes: Two carry adds are needed as the first one could // result in an additional carry add. let second = ((first >> 16) & 0xffff) + (first & 0xffff) ; let u16value = ( ((second >> 16) & 0xffff) + (second & 0xffff) ) as u16; // switch back to big endian (allows to use // native endinaess during calculations). !u16value } #[cfg(test)] mod tests { use super::*; #[test] fn add_8bytes_test() { // trivial case assert_eq!( 0, add_8bytes(0, [0,0,0,0,0,0,0,0]) ); // check that the carry gets added assert_eq!( 0xffff_ffff_ffff_ffff, // normal overflow would result in 0xffff_ffff_ffff_fffe add_8bytes(0xffff_ffff_ffff_ffff, [0xff,0xff,0xff,0xff, 0xff,0xff,0xff,0xff]) ); // non max & min values assert_eq!( 0x1234_5678_1234_5678 + u64::from_ne_bytes([0x23,0x45,0x67,0x89,0x11,0x22,0x33,0x44]), add_8bytes(0x1234_5678_1234_5678, [0x23,0x45,0x67,0x89,0x11,0x22,0x33,0x44]) ); } #[test] fn add_4bytes_test() { // trivial case assert_eq!( 0, add_4bytes(0, [0,0,0,0]) ); // check that the carry gets added assert_eq!( 0xffff_ffff, // normal overflow would result in 0xffff_fffe add_4bytes(0xffff_ffff_ffff_ffff, [0xff,0xff,0xff,0xff]) ); // non max & min values assert_eq!( 0x1234_5678_1234_5678 + u64::from(u32::from_ne_bytes([0x23,0x45,0x67,0x89])), add_4bytes(0x1234_5678_1234_5678, [0x23,0x45,0x67,0x89]) ); } #[test] fn add_2bytes_test() { // trivial case assert_eq!( 0, add_2bytes(0, [0,0]) ); // check that the carry gets added assert_eq!( 0xffff, // normal overflow would result in 0xfffe add_2bytes(0xffff_ffff_ffff_ffff, [0xff,0xff]) ); // non max & min values assert_eq!( 0x9876_0123_1234_5678 + u64::from(u16::from_ne_bytes([0x23,0x45])), add_2bytes(0x9876_0123_1234_5678, [0x23,0x45]) ); } #[test] fn add_slice_test() { // empty assert_eq!( 0x1234, add_slice(0x1234, &[]) ); // aligned assert_eq!( 0x1 + u64::from_ne_bytes([0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18]) + u64::from_ne_bytes([0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x10]), add_slice( 0x1, &[ 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x10, ] ) ); // aligned with carry assert_eq!( 0x1 + 0x1 + // expected carry u64::from_ne_bytes([0xf1, 0x11, 0x10, 0xf0, 0xf2, 0x12, 0x11, 0xf1]).wrapping_add( u64::from_ne_bytes([0xf3, 0x13, 0x12, 0xf2, 0xf4, 0x14, 0x13, 0xf3]) ), add_slice( 0x1, &[ 0xf1, 0x11, 0x10, 0xf0, 0xf2, 0x12, 0x11, 0xf1, 0xf3, 0x13, 0x12, 0xf2, 0xf4, 0x14, 0x13, 0xf3, ] ) ); // unaligned access { let base_data = [ 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x00, ]; // 1 byte unaligned assert_eq!( 0x1 + u64::from_ne_bytes([0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18]) + u64::from(u32::from_ne_bytes([0x19, 0x1a, 0x1b, 0x1c])) + u64::from(u16::from_ne_bytes([0x1d, 0x1e])) + u64::from(u16::from_ne_bytes([0x1f, 0x00])), add_slice( 0x1, &base_data[.. base_data.len() - 1] ) ); // 2 byte unaligned assert_eq!( 0x1 + u64::from_ne_bytes([0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18]) + u64::from(u32::from_ne_bytes([0x19, 0x1a, 0x1b, 0x1c])) + u64::from(u16::from_ne_bytes([0x1d, 0x1e])), add_slice( 0x1, &base_data[.. base_data.len() - 2] ) ); // 3 byte unaligned assert_eq!( 0x1 + u64::from_ne_bytes([0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18]) + u64::from(u32::from_ne_bytes([0x19, 0x1a, 0x1b, 0x1c])) + u64::from(u16::from_ne_bytes([0x1d, 0x00])), add_slice( 0x1, &base_data[.. base_data.len() - 3] ) ); // 4 byte unaligned assert_eq!( 0x1 + u64::from_ne_bytes([0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18]) + u64::from(u32::from_ne_bytes([0x19, 0x1a, 0x1b, 0x1c])), add_slice( 0x1, &base_data[.. base_data.len() - 4] ) ); // 5 byte unaligned assert_eq!( 0x1 + u64::from_ne_bytes([0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18]) + u64::from(u16::from_ne_bytes([0x19, 0x1a])) + u64::from(u16::from_ne_bytes([0x1b, 0x00])), add_slice( 0x1, &base_data[.. base_data.len() - 5] ) ); // 6 byte unaligned assert_eq!( 0x1 + u64::from_ne_bytes([0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18]) + u64::from(u16::from_ne_bytes([0x19, 0x1a])), add_slice( 0x1, &base_data[.. base_data.len() - 6] ) ); // 6 byte unaligned assert_eq!( 0x1 + u64::from_ne_bytes([0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18]) + u64::from(u16::from_ne_bytes([0x19, 0x00])), add_slice( 0x1, &base_data[.. base_data.len() - 7] ) ); } } #[test] fn ones_complement_with_no_zero_test() { // zero case assert_eq!( 0xffff, ones_complement_with_no_zero(0) ); // 0xffff should stay 0xffff (0 is reserved for no checksum) assert_eq!( 0xffff, ones_complement_with_no_zero(0xffff) ); // big endian conversion check assert_eq!( !0x1234u16, ones_complement_with_no_zero(0x1234), ); // add of the upper and lower 16 bits without a carry assert_eq!( !(0x2456u16+0x1345+0x2345u16+0x1234u16), ones_complement_with_no_zero(0x2456_1345_2345_1234), ); // add which in itself will again produce two as carry assert_eq!( !(((0x1234+0xf234u32+0x1456u32+0xf123u32+2u32) & 0xffff) as u16), ones_complement_with_no_zero(0x1234_f234_1456_f123), ); } #[test] fn ones_complement_test() { // zero case assert_eq!( 0xffff, ones_complement(0) ); // check that zero is not reserved assert_eq!( 0, ones_complement(0xffff) ); // big endian conversion check assert_eq!( !0x1234u16, ones_complement(0x1234), ); // add of the upper and lower 16 bits without a carry assert_eq!( !(0x2456u16+0x1345+0x2345u16+0x1234u16), ones_complement(0x2456_1345_2345_1234), ); // add which in itself will again produce two as carry assert_eq!( !(((0x1234+0xf234u32+0x1456u32+0xf123u32+2u32) & 0xffff) as u16), ones_complement(0x1234_f234_1456_f123), ); // will result in a first 16bit sum that will have to be // carry added twice assert_eq!( !1, ones_complement(0x02f6_e312_7fd7_9a20), ); } } } etherparse-0.13.0/src/internet/ip.rs000064400000000000000000000554221046102023000154540ustar 00000000000000use super::super::*; ///Internet protocol headers version 4 & 6 #[derive(Clone, Debug, Eq, PartialEq)] #[allow(clippy::large_enum_variant)] pub enum IpHeader { Version4(Ipv4Header, Ipv4Extensions), Version6(Ipv6Header, Ipv6Extensions) } impl IpHeader { /// Renamed to `IpHeader::from_slice` #[deprecated( since = "0.10.1", note = "Renamed to `IpHeader::from_slice`" )] #[inline] pub fn read_from_slice(slice: &[u8]) -> Result<(IpHeader, u8, &[u8]), ReadError> { IpHeader::from_slice(slice) } /// Read an IpvHeader from a slice and return the header & unused parts of the slice. pub fn from_slice(slice: &[u8]) -> Result<(IpHeader, u8, &[u8]), ReadError> { use crate::ReadError::*; if slice.is_empty() { Err(UnexpectedEndOfSlice(1)) } else { match slice[0] >> 4 { 4 => { let (header, rest) = Ipv4Header::from_slice(slice)?; Ipv4Extensions::from_slice(header.protocol, rest).map( |(ext, next_protocol, rest)| (IpHeader::Version4(header, ext), next_protocol, rest) ) }, 6 => { let (header, rest) = Ipv6Header::from_slice(slice)?; Ipv6Extensions::from_slice(header.next_header, rest).map( |(ext, next_protocol, rest)| (IpHeader::Version6(header, ext), next_protocol, rest) ) }, version => Err(ReadError::IpUnsupportedVersion(version)) } } } ///Reads an IP (v4 or v6) header from the current position. pub fn read(reader: &mut T) -> Result<(IpHeader, u8), ReadError> { let value = { let mut buf = [0;1]; reader.read_exact(&mut buf)?; buf[0] }; match value >> 4 { 4 => { let header = Ipv4Header::read_without_version(reader, value & 0xf)?; Ipv4Extensions::read(reader, header.protocol).map( |(ext, next)| (IpHeader::Version4(header, ext), next) ) }, 6 => { let header = Ipv6Header::read_without_version(reader, value & 0xf)?; Ipv6Extensions::read(reader, header.next_header).map( |(ext, next)| (IpHeader::Version6(header, ext), next) ) }, version => Err(ReadError::IpUnsupportedVersion(version)) } } ///Writes an IP (v4 or v6) header to the current position pub fn write(&self, writer: &mut T) -> Result<(), WriteError> { use crate::IpHeader::*; match *self { Version4(ref header, ref extensions) => { header.write(writer)?; extensions.write(writer, header.protocol) } Version6(ref header, ref extensions) => { header.write(writer)?; extensions.write(writer, header.next_header) } } } /// Returns the size when the ip header & extensions are serialized pub fn header_len(&self) -> usize { use crate::IpHeader::*; match *self { Version4(ref header, ref extensions) => { header.header_len() + extensions.header_len() } Version6(_, ref extensions) => { Ipv6Header::SERIALIZED_SIZE + extensions.header_len() } } } /// Returns the last next header number following the ip header /// and header extensions. pub fn next_header(&self) -> Result { use crate::IpHeader::*; match *self { Version4(ref header, ref extensions) => { extensions.next_header(header.protocol) } Version6(ref header, ref extensions) => { extensions.next_header(header.next_header) } } } /// Sets all the next_header fields in the ipv4 & ipv6 header /// as well as in all extension headers and returns the ether /// type number. /// /// The given number will be set as the last "next_header" or /// protocol number. pub fn set_next_headers(&mut self, last_next_header: u8) -> EtherType { use IpHeader::*; match self { Version4(ref mut header, ref mut extensions) => { header.protocol = extensions.set_next_headers(last_next_header); EtherType::Ipv4 }, Version6(ref mut header, ref mut extensions) => { header.next_header = extensions.set_next_headers(last_next_header); EtherType::Ipv6 }, } } /// Tries to set the length field in the ip header given the length of data /// after the ip header and extension header(s). /// /// If the payload length is too large to be stored in the length fields /// of the ip header an error is returned. /// /// Note that this function will automatically add the length of the extension /// headers is they are present. pub fn set_payload_len(&mut self, len: usize) -> Result<(), ValueError> { use crate::ValueError::*; match self { IpHeader::Version4(ipv4_hdr, exts) => { if let Some(complete_len) = len.checked_add(exts.header_len()) { ipv4_hdr.set_payload_len(complete_len) } else { Err(Ipv4PayloadLengthTooLarge(len)) } }, IpHeader::Version6(ipv6_hdr, exts) => { if let Some(complete_len) = len.checked_add(exts.header_len()) { ipv6_hdr.set_payload_length(complete_len) } else { Err(Ipv6PayloadLengthTooLarge(len)) } }, } } } /// This type has been deprecated please use [IpNumber] instead. /// /// IPv6 headers have a field called `traffic_class` which has nothing /// to do this enum. This unlucky coincedence got even the developer /// of this library confused enough to write that the next header number /// should be written into the `traffic_class` field instead of the /// `next_header` field. /// /// To avoid such confusions in the future the enum has been renamed /// to [IpNumber], which also closer to the name /// "Assigned Internet Protocol Numbers" used on iana.org . #[deprecated( since = "0.10.1", note = "Please use the type IpNumber instead" )] pub type IpTrafficClass = IpNumber; /// Identifiers for the next_header field in ipv6 headers and protocol field in ipv4 headers. /// /// `u8` contants of the ip numbers can be found in the module [`ip_number`]. /// /// The list was extracted from #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum IpNumber { ///IPv6 Hop-by-Hop Option \[[RFC8200](https://datatracker.ietf.org/doc/html/rfc8200)\] IPv6HeaderHopByHop = 0, ///Internet Control Message \[[RFC792](https://datatracker.ietf.org/doc/html/rfc792)\] Icmp = 1, ///Internet Group Management \[[RFC1112](https://datatracker.ietf.org/doc/html/rfc1112)\] Igmp = 2, ///Gateway-to-Gateway \[[RFC823](https://datatracker.ietf.org/doc/html/rfc823)\] Ggp = 3, ///IPv4 encapsulation \[[RFC2003](https://datatracker.ietf.org/doc/html/rfc2003)\] IPv4 = 4, ///Stream \[[RFC1190](https://datatracker.ietf.org/doc/html/rfc1190)\] \[[RFC1819](https://datatracker.ietf.org/doc/html/rfc1819)\] Stream = 5, ///Transmission Control \[[RFC793](https://datatracker.ietf.org/doc/html/rfc793)\] Tcp = 6, ///CBT \[Tony_Ballardie\] Cbt = 7, ///Exterior Gateway Protocol \[[RFC888](https://datatracker.ietf.org/doc/html/rfc888)\] \[David_Mills\] Egp = 8, ///any private interior gateway (used by Cisco for their IGRP) \[Internet_Assigned_Numbers_Authority\] Igp = 9, ///BBN RCC Monitoring \[Steve_Chipman\] BbnRccMon = 10, ///Network Voice Protocol \[[RFC741](https://datatracker.ietf.org/doc/html/rfc741)\]\[Steve_Casner\] NvpII = 11, ///PUP Pup = 12, ///ARGUS (deprecated) \[Robert_W_Scheifler\] Argus = 13, ///EMCON \[mystery contact\] Emcon = 14, ///Cross Net Debugger \[Haverty, J., "XNET Formats for Internet Protocol Version 4", IEN 158, October 1980.\]\[Jack_Haverty\] Xnet = 15, ///Chaos \[J_Noel_Chiappa\] Chaos = 16, ///User Datagram \[[RFC768](https://datatracker.ietf.org/doc/html/rfc768)\]\[Jon_Postel\] Udp = 17, ///Multiplexing \[Cohen, D. and J. Postel, "Multiplexing Protocol", IEN 90, USC/Information Sciences Institute, May 1979.\]\[Jon_Postel\] Mux = 18, ///DCN Measurement Subsystems \[David_Mills\] DcnMeas = 19, ///Host Monitoring \[[RFC869](https://datatracker.ietf.org/doc/html/rfc869)\]\[Bob_Hinden\] Hmp = 20, ///Packet Radio Measurement \[Zaw_Sing_Su\] Prm = 21, ///XEROX NS IDP XnsIdp = 22, ///Trunk-1 \[Barry_Boehm\] Trunk1 = 23, ///Trunk-2 \[Barry_Boehm\] Trunk2 = 24, ///Leaf-1 \[Barry_Boehm\] Leaf1 = 25, ///Leaf-2 \[Barry_Boehm\] Leaf2 = 26, ///Reliable Data Protocol \[[RFC908](https://datatracker.ietf.org/doc/html/rfc908)\] \[Bob_Hinden\] Rdp = 27, ///Internet Reliable Transaction \[[RFC938](https://datatracker.ietf.org/doc/html/rfc938)\] \[Trudy_Miller\] Irtp = 28, ///ISO Transport Protocol Class 4 \[[RFC905](https://datatracker.ietf.org/doc/html/rfc905)\] \[\] IsoTp4 = 29, ///Bulk Data Transfer Protocol \[[RFC969](https://datatracker.ietf.org/doc/html/rfc969)\] \[David_Clark\] NetBlt = 30, ///MFE Network Services Protocol \[Shuttleworth, B., "A Documentary of MFENet, a National Computer Network", UCRL-52317, Lawrence Livermore Labs, Livermore, California, June 1977.\] \[Barry_Howard\] MfeNsp = 31, ///MERIT Internodal Protocol \[Hans_Werner_Braun\] MeritInp = 32, ///Datagram Congestion Control Protocol \[[RFC4340](https://datatracker.ietf.org/doc/html/rfc4340)\] Dccp = 33, ///Third Party Connect Protocol \[Stuart_A_Friedberg\] ThirdPartyConnectProtocol = 34, ///Inter-Domain Policy Routing Protocol \[Martha_Steenstrup\] Idpr = 35, ///XTP \[Greg_Chesson\] Xtp = 36, ///Datagram Delivery Protocol \[Wesley_Craig\] Ddp = 37, ///IDPR Control Message Transport Proto \[Martha_Steenstrup\] IdprCmtp = 38, ///TP++ Transport Protocol \[Dirk_Fromhein\] TpPlusPlus = 39, ///IL Transport Protocol \[Dave_Presotto\] Il = 40, ///IPv6 encapsulation \[[RFC2473](https://datatracker.ietf.org/doc/html/rfc2473)\] Ipv6 = 41, ///Source Demand Routing Protocol \[Deborah_Estrin\] Sdrp = 42, ///Routing Header for IPv6 \[Steve_Deering\] IPv6RouteHeader = 43, ///Fragment Header for IPv6 \[Steve_Deering\] IPv6FragmentationHeader = 44, ///Inter-Domain Routing Protocol \[Sue_Hares\] Idrp = 45, ///Reservation Protocol \[[RFC2205](https://datatracker.ietf.org/doc/html/rfc2205)\]\[[RFC3209](https://datatracker.ietf.org/doc/html/rfc3209)\]\[Bob_Braden\] Rsvp = 46, ///Generic Routing Encapsulation \[[RFC2784](https://datatracker.ietf.org/doc/html/rfc2784)\]\[Tony_Li\] Gre = 47, ///Dynamic Source Routing Protocol \[[RFC4728](https://datatracker.ietf.org/doc/html/rfc4728)\] Dsr = 48, ///BNA \[Gary Salamon\] Bna = 49, ///Encapsulating Security Payload \[[RFC4303](https://datatracker.ietf.org/doc/html/rfc4303)\] EncapsulatingSecurityPayload = 50, ///Authentication Header \[[RFC4302](https://datatracker.ietf.org/doc/html/rfc4302)\] AuthenticationHeader = 51, ///Integrated Net Layer Security TUBA \[K_Robert_Glenn\] Inlsp = 52, ///IP with Encryption (deprecated) \[John_Ioannidis\] Swipe = 53, ///NBMA Address Resolution Protocol \[[RFC1735](https://datatracker.ietf.org/doc/html/rfc1735)\] Narp = 54, ///IP Mobility \[Charlie_Perkins\] Mobile = 55, ///Transport Layer Security Protocol using Kryptonet key management \[Christer_Oberg\] Tlsp = 56, ///SKIP \[Tom_Markson\] Skip = 57, ///ICMP for IPv6 \[[RFC8200](https://datatracker.ietf.org/doc/html/rfc8200)\] IPv6Icmp = 58, ///No Next Header for IPv6 \[[RFC8200](https://datatracker.ietf.org/doc/html/rfc8200)\] IPv6NoNextHeader = 59, ///Destination Options for IPv6 \[[RFC8200](https://datatracker.ietf.org/doc/html/rfc8200)\] IPv6DestinationOptions = 60, ///any host internal protocol \[Internet_Assigned_Numbers_Authority\] AnyHostInternalProtocol = 61, ///CFTP \[Forsdick, H., "CFTP", Network Message, Bolt Beranek and Newman, January 1982.\]\[Harry_Forsdick\] Cftp = 62, ///any local network \[Internet_Assigned_Numbers_Authority\] AnyLocalNetwork = 63, ///SATNET and Backroom EXPAK \[Steven_Blumenthal\] SatExpak = 64, ///Kryptolan \[Paul Liu\] Krytolan = 65, ///MIT Remote Virtual Disk Protocol \[Michael_Greenwald\] Rvd = 66, ///Internet Pluribus Packet Core \[Steven_Blumenthal\] Ippc = 67, ///any distributed file system \[Internet_Assigned_Numbers_Authority\] AnyDistributedFileSystem = 68, ///SATNET Monitoring \[Steven_Blumenthal\] SatMon = 69, ///VISA Protocol \[Gene_Tsudik\] Visa = 70, ///Internet Packet Core Utility \[Steven_Blumenthal\] Ipcv = 71, ///Computer Protocol Network Executive \[David Mittnacht\] Cpnx = 72, ///Computer Protocol Heart Beat \[David Mittnacht\] Cphb = 73, ///Wang Span Network \[Victor Dafoulas\] Wsn = 74, ///Packet Video Protocol \[Steve_Casner\] Pvp = 75, ///Backroom SATNET Monitoring \[Steven_Blumenthal\] BrSatMon = 76, ///SUN ND PROTOCOL-Temporary \[William_Melohn\] SunNd = 77, ///WIDEBAND Monitoring \[Steven_Blumenthal\] WbMon = 78, ///WIDEBAND EXPAK \[Steven_Blumenthal\] WbExpak = 79, ///ISO Internet Protocol \[Marshall_T_Rose\] IsoIp = 80, ///VMTP \[Dave_Cheriton\] Vmtp = 81, ///SECURE-VMTP \[Dave_Cheriton\] SecureVmtp = 82, ///VINES \[Brian Horn\] Vines = 83, ///Transaction Transport Protocol or Internet Protocol Traffic Manager \[Jim_Stevens\] TtpOrIptm = 84, ///NSFNET-IGP \[Hans_Werner_Braun\] NsfnetIgp = 85, ///Dissimilar Gateway Protocol \[M/A-COM Government Systems, "Dissimilar Gateway Protocol Specification, Draft Version", Contract no. CS901145, November 16, 1987.\]\[Mike_Little\] Dgp = 86, ///TCF \[Guillermo_A_Loyola\] Tcf = 87, ///EIGRP \[[RFC7868](https://datatracker.ietf.org/doc/html/rfc7868)\] Eigrp = 88, ///OSPFIGP \[[RFC1583](https://datatracker.ietf.org/doc/html/rfc1583)\]\[[RFC2328](https://datatracker.ietf.org/doc/html/rfc2328)\]\[[RFC5340](https://datatracker.ietf.org/doc/html/rfc5340)\]\[John_Moy\] Ospfigp = 89, ///Sprite RPC Protocol \[Welch, B., "The Sprite Remote Procedure Call System", Technical Report, UCB/Computer Science Dept., 86/302, University of California at Berkeley, June 1986.\]\[Bruce Willins\] SpriteRpc = 90, ///Locus Address Resolution Protocol \[Brian Horn\] Larp = 91, ///Multicast Transport Protocol \[Susie_Armstrong\] Mtp = 92, ///AX.25 Frames \[Brian_Kantor\] Ax25 = 93, ///IP-within-IP Encapsulation Protocol \[John_Ioannidis\] Ipip = 94, ///Mobile Internetworking Control Pro. (deprecated) \[John_Ioannidis\] Micp = 95, ///Semaphore Communications Sec. Pro. \[Howard_Hart\] SccSp = 96, ///Ethernet-within-IP Encapsulation \[[RFC3378](https://datatracker.ietf.org/doc/html/rfc3378)\] EtherIp = 97, ///Encapsulation Header \[[RFC1241](https://datatracker.ietf.org/doc/html/rfc1241)\]\[Robert_Woodburn\] Encap = 98, ///GMTP \[\[RXB5\]\] Gmtp = 100, ///Ipsilon Flow Management Protocol \[Bob_Hinden\]\[November 1995, 1997.\] Ifmp = 101, ///PNNI over IP \[Ross_Callon\] Pnni = 102, ///Protocol Independent Multicast \[[RFC7761](https://datatracker.ietf.org/doc/html/rfc7761)\]\[Dino_Farinacci\] Pim = 103, ///ARIS \[Nancy_Feldman\] Aris = 104, ///SCPS \[Robert_Durst\] Scps = 105, ///QNX \[Michael_Hunter\] Qnx = 106, ///Active Networks \[Bob_Braden\] ActiveNetworks = 107, ///IP Payload Compression Protocol \[[RFC2393](https://datatracker.ietf.org/doc/html/rfc2393)\] IpComp = 108, ///Sitara Networks Protocol \[Manickam_R_Sridhar\] SitraNetworksProtocol = 109, ///Compaq Peer Protocol \[Victor_Volpe\] CompaqPeer = 110, ///IPX in IP \[CJ_Lee\] IpxInIp = 111, ///Virtual Router Redundancy Protocol \[[RFC5798](https://datatracker.ietf.org/doc/html/rfc5798)\] Vrrp = 112, ///PGM Reliable Transport Protocol \[Tony_Speakman\] Pgm = 113, ///any 0-hop protocol \[Internet_Assigned_Numbers_Authority\] AnyZeroHopProtocol = 114, ///Layer Two Tunneling Protocol \[[RFC3931](https://datatracker.ietf.org/doc/html/rfc3931)\]\[Bernard_Aboba\] Layer2TunnelingProtocol = 115, ///D-II Data Exchange (DDX) \[John_Worley\] Ddx = 116, ///Interactive Agent Transfer Protocol \[John_Murphy\] Iatp = 117, ///Schedule Transfer Protocol \[Jean_Michel_Pittet\] Stp = 118, ///SpectraLink Radio Protocol \[Mark_Hamilton\] Srp = 119, ///UTI \[Peter_Lothberg\] Uti = 120, ///Simple Message Protocol \[Leif_Ekblad\] SimpleMessageProtocol = 121, ///Simple Multicast Protocol (deprecated) \[Jon_Crowcroft\]\[draft-perlman-simple-multicast\] Sm = 122, ///Performance Transparency Protocol \[Michael_Welzl\] Ptp = 123, ///ISIS over IPv4 \[Tony_Przygienda\] IsisOverIpv4 = 124, ///FIRE \[Criag_Partridge\] Fire = 125, ///Combat Radio Transport Protocol \[Robert_Sautter\] Crtp = 126, ///Combat Radio User Datagram \[Robert_Sautter\] Crudp = 127, ///SSCOPMCE \[Kurt_Waber\] Sscopmce = 128, ///IPLT \[\[Hollbach\]\] Iplt = 129, ///Secure Packet Shield \[Bill_McIntosh\] Sps = 130, ///Private IP Encapsulation within IP \[Bernhard_Petri\] Pipe = 131, ///Stream Control Transmission Protocol \[Randall_R_Stewart\] Sctp = 132, ///Fibre Channel \[Murali_Rajagopal\]\[[RFC6172](https://datatracker.ietf.org/doc/html/rfc6172)\] Fc = 133, ///RSVP-E2E-IGNORE \[[RFC3175](https://datatracker.ietf.org/doc/html/rfc3175)\] RsvpE2eIgnore = 134, ///MobilityHeader \[[RFC6275](https://datatracker.ietf.org/doc/html/rfc6275)\] MobilityHeader = 135, ///UDPLite \[[RFC3828](https://datatracker.ietf.org/doc/html/rfc3828)\] UdpLite = 136, /// \[[RFC4023](https://datatracker.ietf.org/doc/html/rfc4023)\] MplsInIp = 137, ///MANET Protocols \[[RFC5498](https://datatracker.ietf.org/doc/html/rfc5498)\] Manet = 138, ///Host Identity Protocol \[[RFC7401](https://datatracker.ietf.org/doc/html/rfc7401)\] Hip = 139, ///Shim6 Protocol \[[RFC5533](https://datatracker.ietf.org/doc/html/rfc5533)\] Shim6 = 140, ///Wrapped Encapsulating Security Payload \[[RFC5840](https://datatracker.ietf.org/doc/html/rfc5840)\] Wesp = 141, ///Robust Header Compression \[[RFC5858](https://datatracker.ietf.org/doc/html/rfc5858)\] Rohc = 142, ///Use for experimentation and testing ExperimentalAndTesting0 = 253, ///Use for experimentation and testing ExperimentalAndTesting1 = 254 } impl IpNumber { /// Returns true if the given number is the internet number of an IPV6 extension header. pub fn is_ipv6_ext_header_value(value: u8) -> bool { use crate::ip_number::*; matches!( value, IPV6_HOP_BY_HOP | IPV6_ROUTE | IPV6_FRAG | ENCAP_SEC | AUTH | IPV6_DEST_OPTIONS | MOBILITY | HIP | SHIM6 | EXP0 | EXP1 ) } } /// `u8` constants for the most used ip protocol numbers. /// /// The constants only exist for convenience. You can get equivalent values by /// casting the enum values of [`IpNumber`] to a u8 value. /// /// ``` /// use etherparse::{ip_number, IpNumber}; /// /// assert_eq!(ip_number::TCP, IpNumber::Tcp as u8); /// ``` /// /// The list original values were copied from /// pub mod ip_number { use crate::IpNumber::*; ///IPv6 Hop-by-Hop Option \[[RFC8200](https://datatracker.ietf.org/doc/html/rfc8200)\] pub const IPV6_HOP_BY_HOP: u8 = IPv6HeaderHopByHop as u8; //0 ///Internet Control Message \[[RFC792](https://datatracker.ietf.org/doc/html/rfc792)\] pub const ICMP: u8 = Icmp as u8; //1 ///Internet Group Management \[[RFC1112](https://datatracker.ietf.org/doc/html/rfc1112)\] pub const IGMP: u8 = Igmp as u8; //2 ///Gateway-to-Gateway \[[RFC823](https://datatracker.ietf.org/doc/html/rfc823)\] pub const GGP: u8 = Ggp as u8; //3 ///IPv4 encapsulation \[[RFC2003](https://datatracker.ietf.org/doc/html/rfc2003)\] pub const IPV4: u8 = IPv4 as u8; //4 ///Stream \[[RFC1190](https://datatracker.ietf.org/doc/html/rfc1190)\] \[[RFC1819](https://datatracker.ietf.org/doc/html/rfc1819)\] pub const STREAM: u8 = Stream as u8; //5 ///Transmission Control \[[RFC793](https://datatracker.ietf.org/doc/html/rfc793)\] pub const TCP: u8 = Tcp as u8; //6 ///User Datagram \[[RFC768](https://datatracker.ietf.org/doc/html/rfc768)\] \[Jon_Postel\] pub const UDP: u8 = Udp as u8; //17 ///IPv6 encapsulation \[[RFC2473](https://datatracker.ietf.org/doc/html/rfc2473)\] pub const IPV6: u8 = Ipv6 as u8; //41 ///Routing Header for IPv6 \[Steve_Deering\] pub const IPV6_ROUTE: u8 = IPv6RouteHeader as u8; //43 ///Fragment Header for IPv6 \[Steve_Deering\] pub const IPV6_FRAG: u8 = IPv6FragmentationHeader as u8; //44 ///Encapsulating Security Payload \[[RFC4303](https://datatracker.ietf.org/doc/html/rfc4303)\] pub const ENCAP_SEC: u8 = EncapsulatingSecurityPayload as u8; //50 ///Authentication Header \[[RFC4302](https://datatracker.ietf.org/doc/html/rfc4302)\] pub const AUTH: u8 = AuthenticationHeader as u8; //51 ///IPv6 ICMP next-header type \[[RFC4443](https://datatracker.ietf.org/doc/html/rfc4443)\] pub const IPV6_ICMP: u8 = IPv6Icmp as u8; // 58 ///Destination Options for IPv6 \[[RFC8200](https://datatracker.ietf.org/doc/html/rfc8200)\] pub const IPV6_DEST_OPTIONS: u8 = IPv6DestinationOptions as u8; //60 ///MobilityHeader \[[RFC6275](https://datatracker.ietf.org/doc/html/rfc6275)\] pub const MOBILITY: u8 = MobilityHeader as u8; //135 ///Host Identity Protocol \[[RFC7401](https://datatracker.ietf.org/doc/html/rfc7401)\] pub const HIP: u8 = Hip as u8; //139 ///Shim6 Protocol \[[RFC5533](https://datatracker.ietf.org/doc/html/rfc5533)\] pub const SHIM6: u8 = Shim6 as u8; //140 ///Use for experimentation and testing pub const EXP0: u8 = ExperimentalAndTesting0 as u8; //253 ///Use for experimentation and testing pub const EXP1: u8 = ExperimentalAndTesting1 as u8; //254 } etherparse-0.13.0/src/internet/ip_authentication.rs000064400000000000000000000260361046102023000205520ustar 00000000000000use super::super::*; use std::fmt::{Debug, Formatter}; use std::slice::from_raw_parts; /// Deprecated use [IpAuthenticationHeader] instead. #[deprecated( since = "0.10.1", note = "Please use the type IpAuthenticationHeader instead" )] pub type IPv6AuthenticationHeader = IpAuthenticationHeader; /// IP Authentication Header (rfc4302) #[derive(Clone)] pub struct IpAuthenticationHeader { /// IP protocol number specifying the next header or transport layer protocol. /// /// See [IpNumber] or [ip_number] for a definition of the known values. pub next_header: u8, /// Security Parameters Index pub spi: u32, /// This unsigned 32-bit field contains a counter value that /// increases by one for each packet sent. pub sequence_number: u32, /// Length in 4-octets (maximum valid value is 0xfe) of data filled in the /// `raw_icv_buffer`. raw_icv_len: u8, /// Buffer containing the "Encoded Integrity Check Value-ICV" (variable). /// The length of the used data can be set via the `variable` (must be a multiple of 4 bytes). raw_icv_buffer: [u8;0xfe*4], } impl Debug for IpAuthenticationHeader { fn fmt(&self, formatter: &mut Formatter) -> Result<(), std::fmt::Error> { write!(formatter, "IpAuthenticationHeader {{ next_header: {}, spi: {}, sequence_number: {}, raw_icv: {:?} }}", self.next_header, self.spi, self.sequence_number, self.raw_icv()) } } impl PartialEq for IpAuthenticationHeader { fn eq(&self, other: &Self) -> bool { self.next_header == other.next_header && self.spi == other.spi && self.sequence_number == other.sequence_number && self.raw_icv() == other.raw_icv() } } impl Eq for IpAuthenticationHeader {} impl<'a> IpAuthenticationHeader { pub const MAX_ICV_LEN: usize = 0xfe*4; /// Create a new authentication header with the given parameters. /// /// Note: The length of the raw_icv slice must be a multiple of 4 /// and the maximum allowed length is 1016 bytes /// (`IpAuthenticationHeader::MAX_ICV_LEN`). If the slice length does /// not fullfill these requirements the value is not copied and an /// `Err(ValueError::IpAuthenticationHeaderBadIcvLength)` is returned. /// If successfull an Ok(()) is returned. pub fn new( next_header: u8, spi: u32, sequence_number: u32, raw_icv: &'a [u8] ) -> Result { if raw_icv.len() > IpAuthenticationHeader::MAX_ICV_LEN || 0 != raw_icv.len() % 4 { use ValueError::*; Err(IpAuthenticationHeaderBadIcvLength(raw_icv.len())) } else { let mut result = IpAuthenticationHeader { next_header, spi, sequence_number, raw_icv_len: (raw_icv.len() / 4) as u8, raw_icv_buffer: [0;IpAuthenticationHeader::MAX_ICV_LEN] }; result.raw_icv_buffer[..raw_icv.len()].copy_from_slice(raw_icv); Ok(result) } } /// Read an authentication header from a slice and return the header & unused parts of the slice. pub fn from_slice(slice: &'a [u8]) -> Result<(IpAuthenticationHeader, &'a[u8]), ReadError> { let s = IpAuthenticationHeaderSlice::from_slice(slice)?; let rest = &slice[s.slice().len()..]; let header = s.to_header(); Ok(( header, rest )) } /// Read an authentication header from the current reader position. pub fn read(reader: &mut T) -> Result { let start = { let mut start = [0;4+4+4]; reader.read_exact(&mut start)?; start }; let next_header = start[0]; let payload_len = start[1]; // payload len must be at least 1 if payload_len < 1 { use ReadError::*; Err(IpAuthenticationHeaderTooSmallPayloadLength(payload_len)) } else { // read the rest of the header Ok(IpAuthenticationHeader { next_header, spi: u32::from_be_bytes( [ start[4], start[5], start[6], start[7], ] ), sequence_number: u32::from_be_bytes( [ start[8], start[9], start[10], start[11], ] ), raw_icv_len: payload_len - 1, raw_icv_buffer: { let mut buffer = [0;0xfe*4]; reader.read_exact(&mut buffer[..usize::from(payload_len - 1)*4])?; buffer }, }) } } /// Returns a slice the raw icv value. pub fn raw_icv(&self) -> &[u8] { &self.raw_icv_buffer[..usize::from(self.raw_icv_len)*4] } /// Sets the icv value to the given raw value. The length of the slice must be /// a multiple of 4 and the maximum allowed length is 1016 bytes /// (`IpAuthenticationHeader::MAX_ICV_LEN`). If the slice length does /// not fullfill these requirements the value is not copied and an /// `Err(ValueError::IpAuthenticationHeaderBadIcvLength)` is returned. /// If successfull an Ok(()) is returned. pub fn set_raw_icv(&mut self, raw_icv: &[u8]) -> Result<(),ValueError> { if raw_icv.len() > IpAuthenticationHeader::MAX_ICV_LEN || 0 != raw_icv.len() % 4 { use ValueError::*; Err(IpAuthenticationHeaderBadIcvLength(raw_icv.len())) } else { self.raw_icv_buffer[..raw_icv.len()].copy_from_slice(raw_icv); self.raw_icv_len = (raw_icv.len() / 4) as u8; Ok(()) } } /// Writes the given authentication header to the current position. pub fn write(&self, writer: &mut T) -> Result<(), WriteError> { let spi_be = self.spi.to_be_bytes(); let sequence_number_be = self.sequence_number.to_be_bytes(); debug_assert!(self.raw_icv_len != 0xff); writer.write_all( &[ self.next_header, self.raw_icv_len + 1, 0, 0, spi_be[0], spi_be[1], spi_be[2], spi_be[3], sequence_number_be[0], sequence_number_be[1], sequence_number_be[2], sequence_number_be[3], ] )?; writer.write_all(self.raw_icv())?; Ok(()) } ///Length of the header in bytes. pub fn header_len(&self) -> usize { 12 + usize::from(self.raw_icv_len)*4 } } /// A slice containing an IP Authentication Header (rfc4302) #[derive(Clone, Debug, Eq, PartialEq)] pub struct IpAuthenticationHeaderSlice<'a> { slice: &'a [u8] } impl<'a> IpAuthenticationHeaderSlice<'a> { /// Creates a ip authentication header slice from a slice. pub fn from_slice(slice: &'a[u8]) -> Result, ReadError> { // check slice length use crate::ReadError::*; if slice.len() < 12 { return Err(UnexpectedEndOfSlice(12)); } // SAFETY: // Safe the slice length gets checked to be at least 12 beforehand. let payload_len_enc = unsafe { *slice.get_unchecked(1) }; // check header length minimum size if payload_len_enc < 1 { return Err(IpAuthenticationHeaderTooSmallPayloadLength(payload_len_enc)); } // check length // note: The unit is different then all other ipv6 extension headers. // In the other headers the lenth is in 8 octets, but for authentication // headers the length is in 4 octets. let len = ((payload_len_enc as usize) + 2)*4; if slice.len() < len { return Err(UnexpectedEndOfSlice(len)); } // all good Ok(IpAuthenticationHeaderSlice{ // SAFETY: // Safe as slice len is checked to be at last len above. slice: unsafe { from_raw_parts( slice.as_ptr(), len ) } }) } /// Creates a ip authentication header slice from a slice (assumes slice size & content was validated before). /// /// # Safety /// /// This method assumes that the slice was previously validated to contain /// a valid authentification header. This means the slice length must at /// least be at least 8 and `(slice[1] + 2)*4`. The data that the /// slice points must also be valid (meaning no nullptr or alike allowed). /// /// If these precondtions are not fullfilled the behavior of this function /// and the methods of the return IpAuthenticationHeaderSlice will be undefined. pub unsafe fn from_slice_unchecked(slice: &'a[u8]) -> IpAuthenticationHeaderSlice<'a> { IpAuthenticationHeaderSlice{ slice: from_raw_parts( slice.as_ptr(), ((*slice.get_unchecked(1) as usize) + 2)*4 ) } } /// Returns the slice containing the authentification header. #[inline] pub fn slice(&self) -> &'a[u8] { self.slice } /// Returns the IP protocol number of the next header or transport layer protocol. /// /// See [IpNumber] or [ip_number] for a definition of the known values. #[inline] pub fn next_header(&self) -> u8 { // SAFETY: // Safe as slice length is checked in the constructor // to be at least 12. unsafe { *self.slice.get_unchecked(0) } } /// Read the security parameters index from the slice #[inline] pub fn spi(&self) -> u32 { // SAFETY: // Safe as slice length is checked in the constructor // to be at least 12. unsafe { get_unchecked_be_u32(self.slice.as_ptr().add(4)) } } /// This unsigned 32-bit field contains a counter value that /// increases by one for each packet sent. #[inline] pub fn sequence_number(&self) -> u32 { // SAFETY: // Safe as slice length is checked in the constructor // to be at least 12. unsafe { get_unchecked_be_u32(self.slice.as_ptr().add(8)) } } /// Return a slice with the raw integrity check value pub fn raw_icv(&self) -> &'a[u8] { &self.slice[12..] } /// Decode some of the fields and copy the results to a /// Ipv6ExtensionHeader struct together with a slice pointing /// to the non decoded parts. pub fn to_header(&self) -> IpAuthenticationHeader { IpAuthenticationHeader::new( self.next_header(), self.spi(), self.sequence_number(), self.raw_icv(), ).unwrap() } } etherparse-0.13.0/src/internet/ipv4.rs000064400000000000000000000611121046102023000157170ustar 00000000000000use super::super::*; use std::net::Ipv4Addr; use std::fmt::{Debug, Formatter}; use std::slice::from_raw_parts; /// IPv4 header without options. #[derive(Clone)] pub struct Ipv4Header { pub differentiated_services_code_point: u8, pub explicit_congestion_notification: u8, /// Length of the payload of the ipv4 packet in bytes (does not contain the options). /// /// This field does not directly exist in an ipv4 header but instead is decoded from /// & encoded to the total_size field together with the options length (using the ihl). /// /// Headers where the total length is smaller then then the minimum header size itself /// are not representable in this struct. pub payload_len: u16, pub identification: u16, pub dont_fragment: bool, pub more_fragments: bool, pub fragments_offset: u16, pub time_to_live: u8, pub protocol: u8, pub header_checksum: u16, pub source: [u8;4], pub destination: [u8;4], /// Length of the options in the options_buffer in bytes. options_len: u8, options_buffer: [u8;40] } impl SerializedSize for Ipv4Header { /// Size of the header itself (without options) in bytes. const SERIALIZED_SIZE:usize = 20; } const IPV4_MAX_OPTIONS_LENGTH: usize = 10*4; impl Ipv4Header { ///Constructs an Ipv4Header with standard values for non specified values. pub fn new(payload_len: u16, time_to_live: u8, protocol: u8, source: [u8;4], destination: [u8;4]) -> Ipv4Header { Ipv4Header { differentiated_services_code_point: 0, explicit_congestion_notification: 0, payload_len, identification: 0, dont_fragment: true, more_fragments: false, fragments_offset: 0, time_to_live, protocol, header_checksum: 0, source, destination, options_len: 0, options_buffer: [0;40] } } ///Length of the header in 4 bytes (often also called IHL - Internet Header Lenght). /// ///The minimum allowed length of a header is 5 (= 20 bytes) and the maximum length is 15 (= 60 bytes). pub fn ihl(&self) -> u8 { (self.options_len/4) + 5 } ///Returns a slice to the options part of the header (empty if no options are present). pub fn options(&self) -> &[u8] { &self.options_buffer[..usize::from(self.options_len)] } ///Length of the header (includes options) in bytes. #[inline] pub fn header_len(&self) -> usize { Ipv4Header::SERIALIZED_SIZE + usize::from(self.options_len) } ///Returns the total length of the header + payload in bytes. pub fn total_len(&self) -> u16 { self.payload_len + (Ipv4Header::SERIALIZED_SIZE as u16) + u16::from(self.options_len) } ///Sets the payload length if the value is not too big. Otherwise an error is returned. pub fn set_payload_len(&mut self, value: usize) -> Result<(), ValueError> { if usize::from(self.max_payload_len()) < value { use crate::ValueError::*; Err(Ipv4PayloadLengthTooLarge(value)) } else { self.payload_len = value as u16; Ok(()) } } ///Returns the maximum payload size based on the current options size. pub fn max_payload_len(&self) -> u16 { std::u16::MAX - u16::from(self.options_len) - (Ipv4Header::SERIALIZED_SIZE as u16) } ///Sets the options & header_length based on the provided length. ///The length of the given slice must be a multiple of 4 and maximum 40 bytes. ///If the length is not fullfilling these constraints, no data is set and ///an error is returned. pub fn set_options(&mut self, data: &[u8]) -> Result<(), ValueError> { use crate::ValueError::*; //check that the options length is within bounds if (IPV4_MAX_OPTIONS_LENGTH < data.len()) || (0 != data.len() % 4) { Err(Ipv4OptionsLengthBad(data.len())) } else { //copy the data to the buffer self.options_buffer[..data.len()].copy_from_slice(data); //set the header length self.options_len = data.len() as u8; Ok(()) } } /// Renamed to `Ipv4Header::from_slice` #[deprecated( since = "0.10.1", note = "Renamed to `Ipv4Header::from_slice`" )] #[inline] pub fn read_from_slice(slice: &[u8]) -> Result<(Ipv4Header, &[u8]), ReadError> { Ipv4Header::from_slice(slice) } /// Read an Ipv4Header from a slice and return the header & unused parts of the slice. pub fn from_slice(slice: &[u8]) -> Result<(Ipv4Header, &[u8]), ReadError> { let header = Ipv4HeaderSlice::from_slice(slice)?.to_header(); let rest = &slice[header.header_len()..]; Ok(( header, rest )) } /// Reads an IPv4 header from the current position. pub fn read(reader: &mut T) -> Result { let mut first_byte : [u8;1] = [0;1]; reader.read_exact(&mut first_byte)?; let version = first_byte[0] >> 4; if 4 != version { return Err(ReadError::Ipv4UnexpectedVersion(version)); } Ipv4Header::read_without_version(reader, first_byte[0]) } /// Reads an IPv4 header assuming the version & ihl field have already been read. pub fn read_without_version(reader: &mut T, first_byte: u8) -> Result { let mut header_raw : [u8;20] = [0;20]; header_raw[0] = first_byte; reader.read_exact(&mut header_raw[1..])?; let ihl = header_raw[0] & 0xf; if ihl < 5 { use crate::ReadError::*; return Err(Ipv4HeaderLengthBad(ihl)); } let (dscp, ecn) = { let value = header_raw[1]; (value >> 2, value & 0x3) }; let header_length = u16::from(ihl)*4; let total_length = u16::from_be_bytes([header_raw[2], header_raw[3]]); if total_length < header_length { use crate::ReadError::*; return Err(Ipv4TotalLengthTooSmall(total_length)); } let identification = u16::from_be_bytes([header_raw[4], header_raw[5]]); let (dont_fragment, more_fragments, fragments_offset) = ( 0 != (header_raw[6] & 0b0100_0000), 0 != (header_raw[6] & 0b0010_0000), u16::from_be_bytes( [header_raw[6] & 0b0001_1111, header_raw[7]] ) ); Ok(Ipv4Header{ differentiated_services_code_point: dscp, explicit_congestion_notification: ecn, payload_len: total_length - header_length, identification, dont_fragment, more_fragments, fragments_offset, time_to_live: header_raw[8], protocol: header_raw[9], header_checksum: u16::from_be_bytes([header_raw[10], header_raw[11]]), source: [header_raw[12], header_raw[13], header_raw[14], header_raw[15]], destination: [header_raw[16], header_raw[17], header_raw[18], header_raw[19]], options_len: (ihl - 5)*4, options_buffer: { let mut values: [u8;40] = [0;40]; let options_len = usize::from(ihl - 5)*4; if options_len > 0 { reader.read_exact(&mut values[..options_len])?; } values }, }) } /// Checks if the values in this header are valid values for an ipv4 header. /// /// Specifically it will be checked, that: /// * payload_len + options_len is not too big to be encoded in the total_size header field /// * differentiated_services_code_point is not greater then 0x3f /// * explicit_congestion_notification is not greater then 0x3 /// * fragments_offset is not greater then 0x1fff pub fn check_ranges(&self) -> Result<(), ValueError> { use crate::ErrorField::*; //check ranges max_check_u8(self.differentiated_services_code_point, 0x3f, Ipv4Dscp)?; max_check_u8(self.explicit_congestion_notification, 0x3, Ipv4Ecn)?; max_check_u16(self.fragments_offset, 0x1fff, Ipv4FragmentsOffset)?; max_check_u16(self.payload_len, self.max_payload_len(), Ipv4PayloadLength)?; Ok(()) } /// Writes a given IPv4 header to the current position (this method automatically calculates the header length and checksum). pub fn write(&self, writer: &mut T) -> Result<(), WriteError> { //check ranges self.check_ranges()?; //write with recalculations self.write_ipv4_header_internal(writer, self.calc_header_checksum_unchecked()) } /// Writes a given IPv4 header to the current position (this method just writes the specified checksum and does note compute it). pub fn write_raw(&self, writer: &mut T) -> Result<(), WriteError> { //check ranges self.check_ranges()?; //write self.write_ipv4_header_internal(writer, self.header_checksum) } /// Write the given header with the checksum and header length specified in the seperate arguments fn write_ipv4_header_internal(&self, write: &mut T, header_checksum: u16) -> Result<(), WriteError> { let total_len_be = self.total_len().to_be_bytes(); let id_be = self.identification.to_be_bytes(); let frag_and_flags = { let frag_be: [u8;2] = self.fragments_offset.to_be_bytes(); let flags = { let mut result = 0; if self.dont_fragment { result |= 64; } if self.more_fragments { result |= 32; } result }; [ flags | (frag_be[0] & 0x1f), frag_be[1], ] }; let header_checksum_be = header_checksum.to_be_bytes(); let header_raw = [ (4 << 4) | self.ihl(), (self.differentiated_services_code_point << 2) | self.explicit_congestion_notification, total_len_be[0], total_len_be[1], id_be[0], id_be[1], frag_and_flags[0], frag_and_flags[1], self.time_to_live, self.protocol, header_checksum_be[0], header_checksum_be[1], self.source[0], self.source[1], self.source[2], self.source[3], self.destination[0], self.destination[1], self.destination[2], self.destination[3], ]; write.write_all(&header_raw)?; //options write.write_all(self.options())?; //done Ok(()) } /// Calculate header checksum of the current ipv4 header. pub fn calc_header_checksum(&self) -> Result { //check ranges self.check_ranges()?; //calculate the checksum Ok(self.calc_header_checksum_unchecked()) } /// Calculate the header checksum under the assumtion that all value ranges in the header are correct fn calc_header_checksum_unchecked(&self) -> u16 { checksum::Sum16BitWords::new() .add_2bytes( [ (4 << 4) | self.ihl(), (self.differentiated_services_code_point << 2) | self.explicit_congestion_notification ] ) .add_2bytes(self.total_len().to_be_bytes()) .add_2bytes(self.identification.to_be_bytes()) .add_2bytes( { let frag_off_be = self.fragments_offset.to_be_bytes(); let flags = { let mut result = 0; if self.dont_fragment { result |= 64; } if self.more_fragments { result |= 32; } result }; [ flags | (frag_off_be[0] & 0x1f), frag_off_be[1] ] } ) .add_2bytes([self.time_to_live, self.protocol]) .add_4bytes(self.source) .add_4bytes(self.destination) .add_slice(self.options()) .ones_complement() .to_be() } /// Returns true if the payload is fragmented. /// /// Either data is missing (more_fragments set) or there is /// an fragment offset. #[inline] pub fn is_fragmenting_payload(&self) -> bool { self.more_fragments || (0 != self.fragments_offset) } } //NOTE: I would have prefered to NOT write my own Default, Debug & PartialEq implementation but there are no // default implementations availible for [u8;40] and the alternative of using [u32;10] would lead // to unsafe casting. Writing impl Debug for [u8;40] in a crate is also illegal as it could lead // to an implementation collision between crates. // So the only option left to me was to write an implementation myself and deal with the added complexity // and potential added error source. impl Default for Ipv4Header { fn default() -> Ipv4Header { Ipv4Header { differentiated_services_code_point: 0, explicit_congestion_notification: 0, payload_len: 0, identification: 0, dont_fragment: true, more_fragments: false, fragments_offset: 0, time_to_live: 0, protocol: 0, header_checksum: 0, source: [0;4], destination: [0;4], options_len: 0, options_buffer: [0;40] } } } impl Debug for Ipv4Header { fn fmt(&self, fotmatter: &mut Formatter) -> Result<(), std::fmt::Error> { write!(fotmatter, "Ipv4Header {{ ihl: {}, differentiated_services_code_point: {}, explicit_congestion_notification: {}, payload_len: {}, identification: {}, dont_fragment: {}, more_fragments: {}, fragments_offset: {}, time_to_live: {}, protocol: {}, header_checksum: {}, source: {:?}, destination: {:?}, options: {:?} }}", self.ihl(), self.differentiated_services_code_point, self.explicit_congestion_notification, self.payload_len, self.identification, self.dont_fragment, self.more_fragments, self.fragments_offset, self.time_to_live, self.protocol, self.header_checksum, self.source, self.destination, self.options()) } } impl std::cmp::PartialEq for Ipv4Header { fn eq(&self, other: &Ipv4Header) -> bool { self.differentiated_services_code_point == other.differentiated_services_code_point && self.explicit_congestion_notification == other.explicit_congestion_notification && self.payload_len == other.payload_len && self.identification == other.identification && self.dont_fragment == other.dont_fragment && self.more_fragments == other.more_fragments && self.fragments_offset == other.fragments_offset && self.time_to_live == other.time_to_live && self.protocol == other.protocol && self.header_checksum == other.header_checksum && self.source == other.source && self.destination == other.destination && self.options_len == other.options_len && self.options() == other.options() } } impl std::cmp::Eq for Ipv4Header {} /// A slice containing an ipv4 header of a network package. #[derive(Clone, Debug, Eq, PartialEq)] pub struct Ipv4HeaderSlice<'a> { slice: &'a [u8] } impl<'a> Ipv4HeaderSlice<'a> { /// Creates a slice containing an ipv4 header (including header options). pub fn from_slice(slice: &'a[u8]) -> Result, ReadError> { //check length use crate::ReadError::*; if slice.len() < Ipv4Header::SERIALIZED_SIZE { return Err(UnexpectedEndOfSlice(Ipv4Header::SERIALIZED_SIZE)); } //read version & ihl let (version, ihl) = unsafe { let value = slice.get_unchecked(0); (value >> 4, value & 0xf) }; //check version if 4 != version { return Err(Ipv4UnexpectedVersion(version)); } //check that the ihl is correct if ihl < 5 { use crate::ReadError::*; return Err(Ipv4HeaderLengthBad(ihl)); } //check that the slice contains enough data for the entire header + options let header_length = (usize::from(ihl))*4; if slice.len() < header_length { return Err(UnexpectedEndOfSlice(header_length)); } // check the total_length can contain the header // // SAFETY: // Safe as the slice length is checked to be at least // SERIALIZED_SIZE (20) at the start. let total_length = unsafe { get_unchecked_be_u16(slice.as_ptr().add(2)) }; if total_length < header_length as u16 { return Err(Ipv4TotalLengthTooSmall(total_length)) } //all good Ok(Ipv4HeaderSlice { // SAFETY: // Safe as the slice length is checked to be at least // header_length or greater above. slice: unsafe { from_raw_parts( slice.as_ptr(), header_length ) } }) } /// Returns the slice containing the ipv4 header #[inline] pub fn slice(&self) -> &'a [u8] { self.slice } /// Read the "version" field of the IPv4 header (should be 4). #[inline] pub fn version(&self) -> u8 { // SAFETY: // Safe as the slice length is checked to be at least // SERIALIZED_SIZE (20) in the constructor. unsafe { *self.slice.get_unchecked(0) >> 4 } } /// Read the "ip header length" (length of the ipv4 header + options in multiples of 4 bytes). #[inline] pub fn ihl(&self) -> u8 { // SAFETY: // Safe as the slice length is checked to be at least // SERIALIZED_SIZE (20) in the constructor. unsafe { *self.slice.get_unchecked(0) & 0xf } } /// Read the "differentiated_services_code_point" from the slice. #[inline] pub fn dcp(&self) -> u8 { // SAFETY: // Safe as the slice length is checked to be at least // SERIALIZED_SIZE (20) in the constructor. unsafe { *self.slice.get_unchecked(1) >> 2 } } /// Read the "explicit_congestion_notification" from the slice. #[inline] pub fn ecn(&self) -> u8 { // SAFETY: // Safe as the slice length is checked to be at least // SERIALIZED_SIZE (20) in the constructor. unsafe { *self.slice.get_unchecked(1) & 0x3 } } /// Read the "total length" from the slice (total length of ip header + payload). #[inline] pub fn total_len(&self) -> u16 { // SAFETY: // Safe as the slice length is checked to be at least // SERIALIZED_SIZE (20) in the constructor. unsafe { get_unchecked_be_u16(self.slice.as_ptr().add(2)) } } /// Determine the payload length based on the ihl & total_length field of the header. #[inline] pub fn payload_len(&self) -> u16 { self.total_len() - u16::from(self.ihl())*4 } /// Read the "identification" field from the slice. #[inline] pub fn identification(&self) -> u16 { // SAFETY: // Safe as the slice length is checked to be at least // SERIALIZED_SIZE (20) in the constructor. unsafe { get_unchecked_be_u16(self.slice.as_ptr().add(4)) } } /// Read the "dont fragment" flag from the slice. #[inline] pub fn dont_fragment(&self) -> bool { // SAFETY: // Safe as the slice length is checked to be at least // SERIALIZED_SIZE (20) in the constructor. unsafe { 0 != (*self.slice.get_unchecked(6) & 0x40) } } /// Read the "more fragments" flag from the slice. #[inline] pub fn more_fragments(&self) -> bool { // SAFETY: // Safe as the slice length is checked to be at least // SERIALIZED_SIZE (20) in the constructor. unsafe { 0 != (*self.slice.get_unchecked(6) & 0x20) } } /// Read the "fragment_offset" field from the slice. #[inline] pub fn fragments_offset(&self) -> u16 { u16::from_be_bytes( // SAFETY: // Safe as the slice length is checked to be at least // SERIALIZED_SIZE (20) in the constructor. unsafe { [ *self.slice.get_unchecked(6) & 0x1f, *self.slice.get_unchecked(7) ] } ) } /// Read the "time_to_live" field from the slice. #[inline] pub fn ttl(&self) -> u8 { // SAFETY: // Safe as the slice length is checked to be at least // SERIALIZED_SIZE (20) in the constructor. unsafe { *self.slice.get_unchecked(8) } } /// Read the "protocol" field from the slice. #[inline] pub fn protocol(&self) -> u8 { // SAFETY: // Safe as the slice length is checked to be at least // SERIALIZED_SIZE (20) in the constructor. unsafe { *self.slice.get_unchecked(9) } } /// Read the "header checksum" field from the slice. #[inline] pub fn header_checksum(&self) -> u16 { // SAFETY: // Safe as the slice length is checked to be at least // SERIALIZED_SIZE (20) in the constructor. unsafe { get_unchecked_be_u16(self.slice.as_ptr().add(10)) } } /// Returns a slice containing the ipv4 source address. #[inline] pub fn source(&self) -> [u8;4] { // SAFETY: // Safe as the slice length is checked to be at least // SERIALIZED_SIZE (20) in the constructor. unsafe { get_unchecked_4_byte_array(self.slice.as_ptr().add(12)) } } /// Return the ipv4 source address as an std::net::Ipv4Addr pub fn source_addr(&self) -> Ipv4Addr { Ipv4Addr::from(self.source()) } /// Returns a slice containing the ipv4 source address. #[inline] pub fn destination(&self) -> [u8;4] { // SAFETY: // Safe as the slice length is checked to be at least // SERIALIZED_SIZE (20) in the constructor. unsafe { get_unchecked_4_byte_array(self.slice.as_ptr().add(16)) } } /// Return the ipv4 destination address as an std::net::Ipv4Addr pub fn destination_addr(&self) -> Ipv4Addr { Ipv4Addr::from(self.destination()) } /// Returns a slice containing the ipv4 header options (empty when there are no options). #[inline] pub fn options(&self) -> &'a [u8] { // SAFETY: // Safe as the slice length is checked to be at least // SERIALIZED_SIZE (20) in the constructor. unsafe { from_raw_parts( self.slice.as_ptr().add(20), self.slice.len() - 20 ) } } /// Returns true if the payload is fragmented. /// /// Either data is missing (more_fragments set) or there is /// an fragment offset. #[inline] pub fn is_fragmenting_payload(&self) -> bool { self.more_fragments() || (0 != self.fragments_offset()) } /// Decode all the fields and copy the results to a Ipv4Header struct pub fn to_header(&self) -> Ipv4Header { let options = self.options(); Ipv4Header { differentiated_services_code_point: self.dcp(), explicit_congestion_notification: self.ecn(), payload_len: self.payload_len(), identification: self.identification(), dont_fragment: self.dont_fragment(), more_fragments: self.more_fragments(), fragments_offset: self.fragments_offset(), time_to_live: self.ttl(), protocol: self.protocol(), header_checksum: self.header_checksum(), source: self.source(), destination: self.destination(), options_len: options.len() as u8, options_buffer: { let mut result: [u8;40] = [0;40]; result[..options.len()].copy_from_slice(options); result } } } } etherparse-0.13.0/src/internet/ipv4_extensions.rs000064400000000000000000000124651046102023000202050ustar 00000000000000use super::super::*; /// IPv4 extension headers present after the ip header. /// /// Currently supported: /// * Authentication Header /// /// Currently not supported: /// - Encapsulating Security Payload Header (ESP) #[derive(Clone, Debug, Eq, PartialEq, Default)] pub struct Ipv4Extensions { pub auth: Option, } /// Slices of the IPv4 extension headers present after the ip header. /// /// Currently supported: /// * Authentication Header /// /// Currently not supported: /// * Encapsulating Security Payload Header (ESP) #[derive(Clone, Debug, Eq, PartialEq, Default)] pub struct Ipv4ExtensionsSlice<'a> { pub auth: Option>, } impl Ipv4Extensions { /// Read all known ipv4 extensions and return an `Ipv4ExtensionSlices` with the /// identified slices, the final ip number and a slice pointing to the non parsed data. pub fn from_slice(start_protocol: u8, slice: &[u8]) -> Result<(Ipv4Extensions, u8, &[u8]), ReadError> { Ipv4ExtensionsSlice::from_slice(start_protocol, slice).map( |v| (v.0.to_header(), v.1, v.2) ) } /// Reads the known ipv4 extension headers from the reader and returns the /// headers together with the internet protocol number identifying the protocol /// that will be next. pub fn read(reader: &mut T, start_ip_number: u8) -> Result<(Ipv4Extensions, u8), ReadError> { use ip_number::*; if AUTH == start_ip_number { let header = IpAuthenticationHeader::read(reader)?; let next_ip_number = header.next_header; Ok(( Ipv4Extensions{ auth: Some(header) }, next_ip_number, )) } else { Ok((Default::default(), start_ip_number)) } } /// Write the extensions to the writer. pub fn write(&self, writer: &mut T, start_ip_number: u8) -> Result<(), WriteError> { use ip_number::*; use IpNumber::*; use ValueError::*; match self.auth { Some(ref header) => if AUTH == start_ip_number { header.write(writer) } else { Err(Ipv4ExtensionNotReferenced(AuthenticationHeader).into()) }, None => Ok(()) } } ///Length of the all present headers in bytes. pub fn header_len(&self) -> usize { if let Some(ref header) = self.auth { header.header_len() } else { 0 } } /// Sets all the next_header fields of the headers based on the adviced default order /// with the given protocol number as last "next header" value. The return value is the protocol /// number of the first existing extension header that should be entered in the ipv4 header as /// protocol_number. /// /// If no extension headers are present the value of the argument is returned. pub fn set_next_headers(&mut self, last_protocol_number: u8) -> u8 { use ip_number::*; let mut next = last_protocol_number; if let Some(ref mut header) = self.auth { header.next_header = next; next = AUTH; } next } /// Return next header based on the extension headers and /// the first ip protocol number. /// /// In case a header is never /// referenced a ValueError::Ipv4ExtensionNotReferenced is returned. pub fn next_header(&self, first_next_header: u8) -> Result { use ip_number::*; if let Some(ref auth) = self.auth { if first_next_header == AUTH { Ok(auth.next_header) } else { Err( ValueError::Ipv4ExtensionNotReferenced( IpNumber::AuthenticationHeader ) ) } } else { Ok(first_next_header) } } /// Returns true if no IPv4 extension header is present (all fields `None`). #[inline] pub fn is_empty(&self) -> bool { self.auth.is_none() } } impl<'a> Ipv4ExtensionsSlice<'a> { /// Read all known ipv4 extensions and return an `Ipv4ExtensionSlices` with the /// identified slices, the final ip number and a slice pointing to the non parsed data. pub fn from_slice(start_ip_number: u8, start_slice: &'a [u8]) -> Result<(Ipv4ExtensionsSlice, u8, &[u8]), ReadError> { use ip_number::*; if AUTH == start_ip_number { let header = IpAuthenticationHeaderSlice::from_slice(start_slice)?; let rest = &start_slice[header.slice().len()..]; let next_header = header.next_header(); Ok(( Ipv4ExtensionsSlice{ auth: Some(header) }, next_header, rest )) } else { Ok((Default::default(), start_ip_number, start_slice)) } } /// Convert the slices into actual headers. pub fn to_header(&self) -> Ipv4Extensions { Ipv4Extensions { auth: self.auth.as_ref().map(|v| v.to_header()) } } /// Returns true if no IPv4 extension header is present (all fields `None`). #[inline] pub fn is_empty(&self) -> bool { self.auth.is_none() } }etherparse-0.13.0/src/internet/ipv6.rs000064400000000000000000000413651046102023000157310ustar 00000000000000use super::super::*; use std::net::Ipv6Addr; use std::slice::from_raw_parts; ///IPv6 header according to rfc8200. #[derive(Clone, Debug, Eq, PartialEq, Default)] pub struct Ipv6Header { pub traffic_class: u8, ///If non 0 serves as a hint to router and switches with multiple outbound paths that these packets should stay on the same path, so that they will not be reordered. pub flow_label: u32, ///The length of the payload and extension headers in bytes (0 in case of jumbo payloads). pub payload_length: u16, /// IP protocol number specifying the next header or transport layer protocol. /// /// See [IpNumber] or [ip_number] for a definitions of ids. pub next_header: u8, ///The number of hops the packet can take before it is discarded. pub hop_limit: u8, ///IPv6 source address pub source: [u8;16], ///IPv6 destination address pub destination: [u8;16] } impl SerializedSize for Ipv6Header { ///Size of the header itself in bytes. const SERIALIZED_SIZE:usize = 40; } impl Ipv6Header { /// Renamed to `Ipv6Header::from_slice` #[deprecated( since = "0.10.1", note = "Renamed to `Ipv6Header::from_slice`" )] #[inline] pub fn read_from_slice(slice: &[u8]) -> Result<(Ipv6Header, &[u8]), ReadError> { Ipv6Header::from_slice(slice) } /// Read an Ipv6Header from a slice and return the header & unused parts of the slice. #[inline] pub fn from_slice(slice: &[u8]) -> Result<(Ipv6Header, &[u8]), ReadError> { Ok(( Ipv6HeaderSlice::from_slice(slice)?.to_header(), &slice[Ipv6Header::SERIALIZED_SIZE..] )) } ///Reads an IPv6 header from the current position. pub fn read(reader: &mut T) -> Result { let mut value : [u8;1] = [0;1]; reader.read_exact(&mut value)?; let version = value[0] >> 4; if 6 != version { return Err(ReadError::Ipv6UnexpectedVersion(version)); } match Ipv6Header::read_without_version(reader, value[0] & 0xf) { Ok(value) => Ok(value), Err(err) => Err(ReadError::IoError(err)) } } ///Reads an IPv6 header assuming the version & flow_label field have already been read. pub fn read_without_version(reader: &mut T, version_rest: u8) -> Result { let mut buffer : [u8;8+32-1] = [0;8+32-1]; reader.read_exact(&mut buffer[..])?; Ok(Ipv6Header{ traffic_class: (version_rest << 4) | (buffer[0] >> 4), flow_label: u32::from_be_bytes([0, buffer[0] & 0xf, buffer[1], buffer[2]]), payload_length: u16::from_be_bytes([buffer[3], buffer[4]]), next_header: buffer[5], hop_limit: buffer[6], source: [ buffer[7], buffer[8], buffer[9], buffer[10], buffer[11], buffer[12], buffer[13], buffer[14], buffer[15], buffer[16], buffer[17], buffer[18], buffer[19], buffer[20], buffer[21], buffer[22], ], destination: [ buffer[23], buffer[24], buffer[25], buffer[26], buffer[27], buffer[28], buffer[29], buffer[30], buffer[31], buffer[32], buffer[33], buffer[34], buffer[35], buffer[36], buffer[37], buffer[38], ] }) } ///Takes a slice and skips an ipv6 header extensions and returns the next_header ip number & the slice past the header. pub fn skip_header_extension_in_slice(slice: &[u8], next_header: u8) -> Result<(u8, &[u8]), ReadError> { use crate::ip_number::*; if slice.len() >= 2 { //determine the length let len = match next_header { IPV6_FRAG => 8, AUTH => (usize::from(slice[1]) + 2)*4, IPV6_HOP_BY_HOP | IPV6_ROUTE | IPV6_DEST_OPTIONS | MOBILITY | HIP | SHIM6 => { (usize::from(slice[1]) + 1)*8 }, // not a ipv6 header extension that can be skipped _ => return Ok((next_header, slice)) }; if slice.len() < len { Err(ReadError::UnexpectedEndOfSlice(len)) } else { Ok((slice[0], &slice[len..])) } } else { Err(ReadError::UnexpectedEndOfSlice(2)) } } /// Returns true if the given ip protocol number is a skippable header extension. /// /// A skippable header extension is an extension header for which it is known how /// to determine the protocol number of the following header as well as how many /// octets have to be skipped to reach the start of the following header. pub fn is_skippable_header_extension(ip_protocol_number: u8) -> bool { use crate::ip_number::*; //Note: EncapsulatingSecurityPayload & ExperimentalAndTesting0 can not be skipped matches!(ip_protocol_number, IPV6_HOP_BY_HOP | IPV6_ROUTE | IPV6_FRAG | AUTH | IPV6_DEST_OPTIONS | MOBILITY | HIP | SHIM6) } ///Takes a slice & ip protocol number (identifying the first header type) and returns next_header id & the slice past after all ipv6 header extensions. pub fn skip_all_header_extensions_in_slice(slice: &[u8], next_header: u8) -> Result<(u8, &[u8]), ReadError> { let mut next_header = next_header; let mut rest = slice; for _i in 0..IPV6_MAX_NUM_HEADER_EXTENSIONS { let (n_id, n_rest) = Ipv6Header::skip_header_extension_in_slice(rest, next_header)?; if n_rest.len() == rest.len() { return Ok((next_header, rest)) } else { next_header = n_id; rest = n_rest; } } // final check if Ipv6Header::is_skippable_header_extension(next_header) { Err(ReadError::Ipv6TooManyHeaderExtensions) } else { Ok((next_header, rest)) } } ///Skips the ipv6 header extension and returns the next ip protocol number pub fn skip_header_extension(reader: &mut T, next_header: u8) -> Result { use crate::ip_number::*; let (next_header, rest_length) = match next_header { IPV6_FRAG => { let mut buf = [0; 1]; reader.read_exact(&mut buf)?; (buf[0], 7) }, AUTH => { let mut buf = [0; 2]; reader.read_exact(&mut buf)?; (buf[0], i64::from(buf[1])*4 + 6) }, IPV6_HOP_BY_HOP | IPV6_ROUTE | IPV6_DEST_OPTIONS | MOBILITY | HIP | SHIM6 => { let mut buf = [0; 2]; reader.read_exact(&mut buf)?; (buf[0],i64::from(buf[1])*8 + 6) }, // not a ipv6 header extension that can be skipped _ => return Ok(next_header) }; //Sadly seek does not return an error if the seek could not be fullfilled. //Some implementations do not even truncate the returned position to the //last valid one. std::io::Cursor for example just moves the position //over the border of the given slice (e.g. returns position 15 even when //the given slice contains only 1 element). //The only option, to detect that we are in an invalid state, is to move the //seek offset to one byte before the end and then execute a normal read to //trigger an error. reader.seek(io::SeekFrom::Current(rest_length - 1))?; { let mut buf = [0; 1]; reader.read_exact(&mut buf)?; } Ok(next_header) } ///Skips all ipv6 header extensions and returns the next ip protocol number pub fn skip_all_header_extensions(reader: &mut T, next_header: u8) -> Result { let mut next_header = next_header; for _i in 0..IPV6_MAX_NUM_HEADER_EXTENSIONS { if Ipv6Header::is_skippable_header_extension(next_header) { next_header = Ipv6Header::skip_header_extension(reader, next_header)?; } else { return Ok(next_header); } } //final check if Ipv6Header::is_skippable_header_extension(next_header) { Err(ReadError::Ipv6TooManyHeaderExtensions) } else { Ok(next_header) } } ///Writes a given IPv6 header to the current position. pub fn write(&self, writer: &mut T) -> Result<(), WriteError> { use crate::ErrorField::*; fn max_check_u32(value: u32, max: u32, field: ErrorField) -> Result<(), WriteError> { if value <= max { Ok(()) } else { Err( WriteError::ValueError( ValueError::U32TooLarge{ value, max, field } ) ) } } // check value ranges max_check_u32(self.flow_label, 0xfffff, Ipv6FlowLabel)?; // serialize header let flow_label_be = self.flow_label.to_be_bytes(); let payload_len_be = self.payload_length.to_be_bytes(); let header_raw = [ (6 << 4) | (self.traffic_class >> 4), (self.traffic_class << 4) | flow_label_be[1], flow_label_be[2], flow_label_be[3], payload_len_be[0], payload_len_be[1], self.next_header, self.hop_limit, self.source[0], self.source[1], self.source[2], self.source[3], self.source[4], self.source[5], self.source[6], self.source[7], self.source[8], self.source[9], self.source[10], self.source[11], self.source[12], self.source[13], self.source[14], self.source[15], self.destination[0], self.destination[1], self.destination[2], self.destination[3], self.destination[4], self.destination[5], self.destination[6], self.destination[7], self.destination[8], self.destination[9], self.destination[10], self.destination[11], self.destination[12], self.destination[13], self.destination[14], self.destination[15], ]; writer.write_all(&header_raw)?; Ok(()) } /// Length of the serialized header in bytes. /// /// The function always returns the constant Ipv6Header::SERIALIZED_SIZE /// and exists to keep the methods consistent with other headers. #[inline] pub fn header_len(&self) -> usize { Ipv6Header::SERIALIZED_SIZE } ///Sets the field total_length based on the size of the payload and the options. Returns an error if the payload is too big to fit. pub fn set_payload_length(&mut self, size: usize) -> Result<(), ValueError> { //check that the total length fits into the field const MAX_PAYLOAD_LENGTH: usize = std::u16::MAX as usize; if MAX_PAYLOAD_LENGTH < size { return Err(ValueError::Ipv6PayloadLengthTooLarge(size)); } self.payload_length = size as u16; Ok(()) } } ///A slice containing an ipv6 header of a network package. #[derive(Clone, Debug, Eq, PartialEq)] pub struct Ipv6HeaderSlice<'a> { slice: &'a [u8] } impl<'a> Ipv6HeaderSlice<'a, > { /// Creates a slice containing an ipv6 header (without header extensions). pub fn from_slice(slice: &'a[u8]) -> Result, ReadError> { // check length use crate::ReadError::*; if slice.len() < Ipv6Header::SERIALIZED_SIZE { return Err(UnexpectedEndOfSlice(Ipv6Header::SERIALIZED_SIZE)); } // read version & ihl // // SAFETY: // This is safe as the slice len is checked to be // at least 40 bytes at the start of the function. let version = unsafe { slice.get_unchecked(0) >> 4 }; // check version if 6 != version { return Err(Ipv6UnexpectedVersion(version)); } // all good Ok(Ipv6HeaderSlice { // SAFETY: // This is safe as the slice length is checked to be // at least Ipv6Header::SERIALIZED_SIZE (40) // at the start of the function. slice: unsafe { from_raw_parts( slice.as_ptr(), Ipv6Header::SERIALIZED_SIZE ) } }) } ///Returns the slice containing the ipv6 header #[inline] pub fn slice(&self) -> &'a [u8] { self.slice } ///Read the "version" field from the slice (should be 6). #[inline] pub fn version(&self) -> u8 { // SAFETY: // Safe as the slice length is set to // Ipv6Header::SERIALIZED_SIZE (40) during construction // of the struct. unsafe { *self.slice.get_unchecked(0) >> 4 } } ///Read the "traffic class" field from the slice. #[inline] pub fn traffic_class(&self) -> u8 { // SAFETY: // Safe as the slice length is set to // Ipv6Header::SERIALIZED_SIZE (40) during construction // of the struct. unsafe { (self.slice.get_unchecked(0) << 4) | (self.slice.get_unchecked(1) >> 4) } } ///Read the "flow label" field from the slice. #[inline] pub fn flow_label(&self) -> u32 { u32::from_be_bytes( // SAFETY: // Safe as the slice length is set to // Ipv6Header::SERIALIZED_SIZE (40) during construction // of the struct. unsafe { [ 0, *self.slice.get_unchecked(1) & 0xf, *self.slice.get_unchecked(2), *self.slice.get_unchecked(3) ] } ) } ///Read the "payload length" field from the slice. The length should contain the length of all extension headers and payload. #[inline] pub fn payload_length(&self) -> u16 { // SAFETY: // Safe as the slice length is set to // Ipv6Header::SERIALIZED_SIZE (40) during construction // of the struct. unsafe { get_unchecked_be_u16(self.slice.as_ptr().add(4)) } } /// Read the "next header" field from the slice. /// /// The next header value specifies what the next header or transport /// layer protocol is (see [IpNumber] or [ip_number] for a definitions of ids). #[inline] pub fn next_header(&self) -> u8 { // SAFETY: // Safe as the slice length is set to // Ipv6Header::SERIALIZED_SIZE (40) during construction // of the struct. unsafe { *self.slice.get_unchecked(6) } } ///Read the "hop limit" field from the slice. The hop limit specifies the number of hops the packet can take before it is discarded. #[inline] pub fn hop_limit(&self) -> u8 { // SAFETY: // Safe as the slice length is set to // Ipv6Header::SERIALIZED_SIZE (40) during construction // of the struct. unsafe { *self.slice.get_unchecked(7) } } ///Returns a slice containing the IPv6 source address. #[inline] pub fn source(&self) -> [u8;16] { // SAFETY: // Safe as the slice length is set to // Ipv6Header::SERIALIZED_SIZE (40) during construction // of the struct. unsafe { get_unchecked_16_byte_array(self.slice.as_ptr().add(8)) } } ///Return the ipv6 source address as an std::net::Ipv6Addr pub fn source_addr(&self) -> Ipv6Addr { Ipv6Addr::from(self.source()) } ///Returns a slice containing the IPv6 destination address. #[inline] pub fn destination(&self) -> [u8;16] { // SAFETY: // Safe as the slice length is set to // Ipv6Header::SERIALIZED_SIZE (40) during construction // of the struct. unsafe { get_unchecked_16_byte_array(self.slice.as_ptr().add(24)) } } ///Return the ipv6 destination address as an std::net::Ipv6Addr pub fn destination_addr(&self) -> Ipv6Addr { Ipv6Addr::from(self.destination()) } ///Decode all the fields and copy the results to a Ipv6Header struct pub fn to_header(&self) -> Ipv6Header { Ipv6Header { traffic_class: self.traffic_class(), flow_label: self.flow_label(), payload_length: self.payload_length(), next_header: self.next_header(), hop_limit: self.hop_limit(), source: self.source(), destination: self.destination() } } } etherparse-0.13.0/src/internet/ipv6_extensions.rs000064400000000000000000001172161046102023000202070ustar 00000000000000use super::super::*; use std::slice::from_raw_parts; /// IPv6 extension headers present after the ip header. /// /// Currently supported: /// /// * Authentication Header /// * Hop by Hop Options Header /// * Destination Options Header (before and after routing headers) /// * Routing Header /// * Fragment /// * Authentication Header /// /// Currently not supported: //// /// * Encapsulating Security Payload Header (ESP) /// * Host Identity Protocol (HIP) /// * IP Mobility /// * Site Multihoming by IPv6 Intermediation (SHIM6) #[derive(Clone, Debug, Eq, PartialEq, Default)] pub struct Ipv6Extensions { pub hop_by_hop_options: Option, pub destination_options: Option, pub routing: Option, pub fragment: Option, pub auth: Option, } impl Ipv6Extensions { /// Reads as many extension headers as possible from the slice. /// /// Returns the found ipv6 extension headers, the next header ip number after the read /// headers and a slice containing the rest of the packet after the read headers. /// /// Note that this function can only handle ipv6 extensions if each extension header does /// occur at most once, except for destination options headers which are allowed to /// exist once in front of a routing header and once after a routing header. /// /// In case that more extension headers then can fit into a `Ipv6Extensions` struct are /// encountered, the parsing is stoped at the point where the data would no longer fit into /// the struct. In such a scenario a struct with the data that could be parsed is returned /// together with the next header ip number and slice containing the unparsed data. /// /// It is in the responsibility of the caller to handle a scenario like this. /// /// The reason that no error is generated, is that even though according to RFC 8200 packets /// "should" not contain more then one occurence of an extension header the RFC also specifies /// that "IPv6 nodes must accept and attempt to process extension headers in any order and /// occurring any number of times in the same packet". So packets with multiple headers "should" /// not exist, but are still valid IPv6 packets. As such this function does not generate a /// parsing error, as it is not an invalid packet, but if packets like these are encountered /// the user of this function has to themself decide how to handle packets like these. /// /// The only exception is if an hop by hop header is located somewhere else then directly at /// the start. In this case an `ReadError::Ipv6HopByHopHeaderNotAtStart` error is generated as /// the hop by hop header is required to be located directly after the IPv6 header according /// to RFC 8200. pub fn from_slice(start_ip_number: u8, slice: &[u8]) -> Result<(Ipv6Extensions, u8, &[u8]), ReadError> { let mut result: Ipv6Extensions = Default::default(); let mut rest = slice; let mut next_header = start_ip_number; use ip_number::*; use ReadError::*; // the hop by hop header is required to occur directly after the ipv6 header if IPV6_HOP_BY_HOP == next_header { let slice = Ipv6RawExtensionHeaderSlice::from_slice(rest)?; rest = &rest[slice.slice().len()..]; next_header = slice.next_header(); result.hop_by_hop_options = Some(slice.to_header()); } loop { match next_header { IPV6_HOP_BY_HOP => { return Err(Ipv6HopByHopHeaderNotAtStart); }, IPV6_DEST_OPTIONS => { if let Some(ref mut routing) = result.routing { // if the routing header is already present // this this a "final destination options" header if routing.final_destination_options.is_some() { // more then one header of this type found -> abort parsing return Ok((result, next_header, rest)) } else { let slice = Ipv6RawExtensionHeaderSlice::from_slice(rest)?; rest = &rest[slice.slice().len()..]; next_header = slice.next_header(); routing.final_destination_options = Some(slice.to_header()); } } else if result.destination_options.is_some() { // more then one header of this type found -> abort parsing return Ok((result, next_header, rest)); } else { let slice = Ipv6RawExtensionHeaderSlice::from_slice(rest)?; rest = &rest[slice.slice().len()..]; next_header = slice.next_header(); result.destination_options = Some(slice.to_header()); } }, IPV6_ROUTE => { if result.routing.is_some() { // more then one header of this type found -> abort parsing return Ok((result, next_header, rest)) } else { let slice = Ipv6RawExtensionHeaderSlice::from_slice(rest)?; rest = &rest[slice.slice().len()..]; next_header = slice.next_header(); result.routing = Some( Ipv6RoutingExtensions { routing: slice.to_header(), final_destination_options: None, } ); } }, IPV6_FRAG => { if result.fragment.is_some() { // more then one header of this type found -> abort parsing return Ok((result, next_header, rest)) } else { let slice = Ipv6FragmentHeaderSlice::from_slice(rest)?; rest = &rest[slice.slice().len()..]; next_header = slice.next_header(); result.fragment = Some(slice.to_header()); } }, AUTH => { if result.auth.is_some() { // more then one header of this type found -> abort parsing return Ok((result, next_header, rest)) } else { let slice = IpAuthenticationHeaderSlice::from_slice(rest)?; rest = &rest[slice.slice().len()..]; next_header = slice.next_header(); result.auth = Some(slice.to_header()); } }, _ => { // done parsing, the next header is not a known header extension return Ok((result, next_header, rest)) } } } //should not be hit } /// Reads as many extension headers as possible from the reader and returns the found ipv6 /// extension headers and the next header ip number. /// /// If no extension headers are present an unfilled struct and the original `first_header` /// ip number is returned. /// /// Note that this function can only handle ipv6 extensions if each extension header does /// occur at most once, except for destination options headers which are allowed to /// exist once in front of a routing header and once after a routing header. /// /// In case that more extension headers then can fit into a `Ipv6Extensions` struct are /// encountered, the parsing is stoped at the point where the data would no longer fit into /// the struct. In such a scenario a struct with the data that could be parsed is returned /// together with the next header ip number that identfies which header could be read next. /// /// It is in the responsibility of the caller to handle a scenario like this. /// /// The reason that no error is generated, is that even though according to RFC 8200, packets /// "should" not contain more then one occurence of an extension header, the RFC also specifies /// that "IPv6 nodes must accept and attempt to process extension headers in any order and /// occurring any number of times in the same packet". So packets with multiple headers "should" /// not exist, but are still valid IPv6 packets. As such this function does not generate a /// parsing error, as it is not an invalid packet, but if packets like these are encountered /// the user of this function has to themself decide how to handle packets like these. /// /// The only exception is if an hop by hop header is located somewhere else then directly at /// the start. In this case an `ReadError::Ipv6HopByHopHeaderNotAtStart` error is generated as /// the hop by hop header is required to be located directly after the IPv6 header according /// to RFC 8200. pub fn read(reader: &mut T, start_ip_number: u8) -> Result<(Ipv6Extensions, u8), ReadError> { let mut result: Ipv6Extensions = Default::default(); let mut next_protocol = start_ip_number; use ip_number::*; use ReadError::*; // the hop by hop header is required to occur directly after the ipv6 header if IPV6_HOP_BY_HOP == next_protocol { let header = Ipv6RawExtensionHeader::read(reader)?; next_protocol = header.next_header; result.hop_by_hop_options = Some(header); } loop { match next_protocol { IPV6_HOP_BY_HOP => { return Err(Ipv6HopByHopHeaderNotAtStart); }, IPV6_DEST_OPTIONS => { if let Some(ref mut routing) = result.routing { // if the routing header is already present // asume this is a "final destination options" header if routing.final_destination_options.is_some() { // more then one header of this type found -> abort parsing return Ok((result, next_protocol)); } else { let header = Ipv6RawExtensionHeader::read(reader)?; next_protocol = header.next_header; routing.final_destination_options = Some(header); } } else if result.destination_options.is_some() { // more then one header of this type found -> abort parsing return Ok((result, next_protocol)); } else { let header = Ipv6RawExtensionHeader::read(reader)?; next_protocol = header.next_header; result.destination_options = Some(header); } }, IPV6_ROUTE => { if result.routing.is_some() { // more then one header of this type found -> abort parsing return Ok((result, next_protocol)); } else { let header = Ipv6RawExtensionHeader::read(reader)?; next_protocol = header.next_header; result.routing = Some( Ipv6RoutingExtensions{ routing: header, final_destination_options: None, } ); } }, IPV6_FRAG => { if result.fragment.is_some() { // more then one header of this type found -> abort parsing return Ok((result, next_protocol)); } else { let header = Ipv6FragmentHeader::read(reader)?; next_protocol = header.next_header; result.fragment = Some(header); } }, AUTH => { if result.auth.is_some() { // more then one header of this type found -> abort parsing return Ok((result, next_protocol)); } else { let header = IpAuthenticationHeader::read(reader)?; next_protocol = header.next_header; result.auth = Some(header); } }, _ => { // done parsing, the next header is not a known header extension return Ok((result, next_protocol)) } } } //should not be hit } /// Writes the given headers to a writer based on the order defined in the next_header fields of /// the headers and the first header_id passed to this function. /// /// It is required that all next header are correctly set in the headers and no other ipv6 header /// extensions follow this header. If this is not the case a `ValueError::Ipv6ExtensionNotReferenced` pub fn write(&self, writer: &mut T, first_header: u8) -> Result<(), WriteError> { use ip_number::*; use IpNumber::*; use ValueError::*; /// Struct flagging if a header needs to be written. struct NeedsWrite { pub hop_by_hop_options: bool, pub destination_options: bool, pub routing: bool, pub fragment: bool, pub auth: bool, pub final_destination_options: bool } let mut needs_write = NeedsWrite { hop_by_hop_options: self.hop_by_hop_options.is_some(), destination_options: self.destination_options.is_some(), routing: self.routing.is_some(), fragment: self.fragment.is_some(), auth: self.auth.is_some(), final_destination_options: if let Some(ref routing) = self.routing { routing.final_destination_options.is_some() } else { false } }; let mut next_header = first_header; let mut route_written = false; // check if hop by hop header should be written first if IPV6_HOP_BY_HOP == next_header { let header = &self.hop_by_hop_options.as_ref().unwrap(); header.write(writer)?; next_header = header.next_header; needs_write.hop_by_hop_options = false; } loop { match next_header { IPV6_HOP_BY_HOP => { // Only trigger a "hop by hop not at start" error // if we actually still have to write a hop by hop header. // // The ip number for hop by hop is 0, which could be used // as a placeholder by user and later replaced. So let's // not be overzealous and allow a next header with hop // by hop if it is not part of this extensions struct. if needs_write.hop_by_hop_options { // the hop by hop header is only allowed at the start return Err(Ipv6ExtensionHopByHopNotAtStart.into()); } else { break; } }, IPV6_DEST_OPTIONS => { // the destination options are allowed to be written twice // once before a routing header and once after. if route_written { if needs_write.final_destination_options { let header = &self.routing.as_ref().unwrap().final_destination_options.as_ref().unwrap(); header.write(writer)?; next_header = header.next_header; needs_write.final_destination_options = false; } else { break; } } else if needs_write.destination_options { let header = &self.destination_options.as_ref().unwrap(); header.write(writer)?; next_header = header.next_header; needs_write.destination_options = false; } else { break; } }, IPV6_ROUTE => { if needs_write.routing { let header = &self.routing.as_ref().unwrap().routing; header.write(writer)?; next_header = header.next_header; needs_write.routing = false; // for destination options route_written = true; } else { break; } }, IPV6_FRAG => { if needs_write.fragment { let header = &self.fragment.as_ref().unwrap(); header.write(writer)?; next_header = header.next_header; needs_write.fragment = false; } else { break; } }, AUTH => { if needs_write.auth { let header = &self.auth.as_ref().unwrap(); header.write(writer)?; next_header = header.next_header; needs_write.auth = false; } else { break; } }, _ => { // reached an unknown next_header id, proceed to check if everything was written break; } } } // check that all header have been written if needs_write.hop_by_hop_options { Err(Ipv6ExtensionNotReferenced(IPv6HeaderHopByHop).into()) } else if needs_write.destination_options { Err(Ipv6ExtensionNotReferenced(IPv6DestinationOptions).into()) } else if needs_write.routing { Err(Ipv6ExtensionNotReferenced(IPv6RouteHeader).into()) } else if needs_write.fragment { Err(Ipv6ExtensionNotReferenced(IPv6FragmentationHeader).into()) } else if needs_write.auth { Err(Ipv6ExtensionNotReferenced(AuthenticationHeader).into()) } else if needs_write.final_destination_options { Err(Ipv6ExtensionNotReferenced(IPv6DestinationOptions).into()) } else { Ok(()) } } /// Length of the all present headers in bytes. pub fn header_len(&self) -> usize { let mut result = 0; if let Some(ref header) = self.hop_by_hop_options { result += header.header_len(); } if let Some(ref header) = self.destination_options { result += header.header_len(); } if let Some(ref header) = self.routing { result += header.routing.header_len(); if let Some(ref header) = header.final_destination_options { result += header.header_len(); } } if let Some(ref header) = self.fragment { result += header.header_len(); } if let Some(ref header) = self.auth { result += header.header_len(); } result } /// Sets all the next_header fields of the headers based on the adviced default order /// with the given protocol number as last "next header" value. The return value is the protocol /// number of the first existing extension header that should be entered in the ipv6 header as /// next_header. /// /// If no extension headers are present the value of the argument is returned. pub fn set_next_headers(&mut self, last_protocol_number: u8) -> u8 { use ip_number::*; let mut next = last_protocol_number; // go through the proposed order of extension headers from // RFC 8200 backwards. The header order defined in RFC8200 is: // // * IPv6 header // * Hop-by-Hop Options header // * Destination Options header // * Routing header // * Fragment header // * Authentication header // * Encapsulating Security Payload header // * Destination Options header // * Upper-Layer header // if let Some(ref mut routing) = self.routing { if let Some(ref mut header) = routing.final_destination_options { header.next_header = next; next = IPV6_DEST_OPTIONS; } } if let Some(ref mut header) = self.auth { header.next_header = next; next = AUTH; } if let Some(ref mut header) = self.fragment { header.next_header = next; next = IPV6_FRAG; } if let Some(ref mut routing) = self.routing { routing.routing.next_header = next; next = IPV6_ROUTE; } if let Some(ref mut header) = self.destination_options { header.next_header = next; next = IPV6_DEST_OPTIONS; } if let Some(ref mut header) = self.hop_by_hop_options { header.next_header = next; next = IPV6_HOP_BY_HOP; } next } /// Return next header based on the extension headers and /// the first ip protocol number. pub fn next_header(&self, first_next_header: u8) -> Result { use ValueError::*; use ip_number::*; /// Struct flagging if a header needs to be referenced. struct OutstandingRef { pub hop_by_hop_options: bool, pub destination_options: bool, pub routing: bool, pub fragment: bool, pub auth: bool, pub final_destination_options: bool } let mut outstanding_refs = OutstandingRef { hop_by_hop_options: self.hop_by_hop_options.is_some(), destination_options: self.destination_options.is_some(), routing: self.routing.is_some(), fragment: self.fragment.is_some(), auth: self.auth.is_some(), final_destination_options: if let Some(ref routing) = self.routing { routing.final_destination_options.is_some() } else { false } }; let mut next = first_next_header; let mut route_refed = false; // check if hop by hop header should be written first if IPV6_HOP_BY_HOP == next { if let Some(ref header) = self.hop_by_hop_options { next = header.next_header; outstanding_refs.hop_by_hop_options = false; } } loop { match next { IPV6_HOP_BY_HOP => { // Only trigger a "hop by hop not at start" error // if we actually still have to write a hop by hop header. // // The ip number for hop by hop is 0, which could be used // as a placeholder by user and later replaced. So let's // not be overzealous and allow a next header with hop // by hop if it is not part of this extensions struct. if outstanding_refs.hop_by_hop_options { // the hop by hop header is only allowed at the start return Err(Ipv6ExtensionHopByHopNotAtStart); } else { break; } }, IPV6_DEST_OPTIONS => { // the destination options are allowed to be written twice // once before a routing header and once after. if route_refed { if outstanding_refs.final_destination_options { let header = &self.routing.as_ref().unwrap().final_destination_options.as_ref().unwrap(); next = header.next_header; outstanding_refs.final_destination_options = false; } else { break; } } else if outstanding_refs.destination_options { let header = &self.destination_options.as_ref().unwrap(); next = header.next_header; outstanding_refs.destination_options = false; } else { break; } }, IPV6_ROUTE => { if outstanding_refs.routing { let header = &self.routing.as_ref().unwrap().routing; next = header.next_header; outstanding_refs.routing = false; // for destination options route_refed = true; } else { break; } }, IPV6_FRAG => { if outstanding_refs.fragment { let header = &self.fragment.as_ref().unwrap(); next = header.next_header; outstanding_refs.fragment = false; } else { break; } }, AUTH => { if outstanding_refs.auth { let header = &self.auth.as_ref().unwrap(); next = header.next_header; outstanding_refs.auth = false; } else { break; } }, _ => break, } } // assume all done if outstanding_refs.hop_by_hop_options { return Err( Ipv6ExtensionNotReferenced(IpNumber::IPv6HeaderHopByHop) ); } if outstanding_refs.destination_options { return Err( Ipv6ExtensionNotReferenced(IpNumber::IPv6DestinationOptions) ); } if outstanding_refs.routing { return Err( Ipv6ExtensionNotReferenced(IpNumber::IPv6RouteHeader) ); } if outstanding_refs.fragment { return Err( Ipv6ExtensionNotReferenced(IpNumber::IPv6FragmentationHeader) ); } if outstanding_refs.auth { return Err( Ipv6ExtensionNotReferenced(IpNumber::AuthenticationHeader) ); } if outstanding_refs.final_destination_options { return Err( Ipv6ExtensionNotReferenced(IpNumber::IPv6DestinationOptions) ); } Ok(next) } /// Returns true if a fragmentation header is present in /// the extensions that fragments the payload. /// /// Note: A fragmentation header can still be present /// even if the return value is false in case the fragmentation /// headers don't fragment the payload. This is the case if /// the offset of all fragmentation header is 0 and the /// more fragment bit is not set. #[inline] pub fn is_fragmenting_payload(&self) -> bool { if let Some(frag) = self.fragment.as_ref() { frag.is_fragmenting_payload() } else { false } } /// Returns true if no IPv6 extension header is present (all fields `None`). #[inline] pub fn is_empty(&self) -> bool { self.hop_by_hop_options.is_none() && self.destination_options.is_none() && self.routing.is_none() && self.fragment.is_none() && self.auth.is_none() } } /// In case a route header is present it is also possible /// to attach a "final destination" header. #[derive(Clone, Debug, Eq, PartialEq)] pub struct Ipv6RoutingExtensions { pub routing: Ipv6RawExtensionHeader, pub final_destination_options: Option } /// Slice containing the IPv6 extension headers present after the ip header. /// /// Currently supported: /// * Authentication Header /// * Hop by Hop Options Header /// * Destination Options Header (before and after routing headers) /// * Routing Header /// * Fragment /// * Authentication Header /// /// Currently not supported: /// * Encapsulating Security Payload Header (ESP) /// * Host Identity Protocol (HIP) /// * IP Mobility /// * Site Multihoming by IPv6 Intermediation (SHIM6) #[derive(Clone, Debug, Eq, PartialEq, Default)] pub struct Ipv6ExtensionsSlice<'a> { /// IP protocol number of the first header present in the slice. first_header: Option, /// True if a fragment header is present in the ipv6 header extensions that causes the payload to be fragmented. fragmented: bool, /// Slice containing ipv6 extension headers. slice: &'a [u8] } impl<'a> Ipv6ExtensionsSlice<'a> { /// Collects all ipv6 extension headers in a slice & checks if /// a fragmentation header that fragments the packet is present. pub fn from_slice(start_ip_number: u8, start_slice: &'a [u8]) -> Result<(Ipv6ExtensionsSlice, u8, &'a[u8]), ReadError> { let mut rest = start_slice; let mut next_header = start_ip_number; let mut fragmented = false; use ip_number::*; use ReadError::*; // the hop by hop header is required to occur directly after the ipv6 header if IPV6_HOP_BY_HOP == next_header { let slice = Ipv6RawExtensionHeaderSlice::from_slice(rest)?; rest = &rest[slice.slice().len()..]; next_header = slice.next_header(); } loop { match next_header { IPV6_HOP_BY_HOP => { return Err(Ipv6HopByHopHeaderNotAtStart); }, IPV6_DEST_OPTIONS | IPV6_ROUTE => { let slice = Ipv6RawExtensionHeaderSlice::from_slice(rest)?; // SAFETY: // Ipv6RawExtensionHeaderSlice::from_slice always generates // a subslice from the given slice rest. Therefor it is guranteed // that len is always greater or equal the len of rest. rest = unsafe { let len = slice.slice().len(); from_raw_parts( rest.as_ptr().add(len), rest.len() - len ) }; next_header = slice.next_header(); }, IPV6_FRAG => { let slice = Ipv6FragmentHeaderSlice::from_slice(rest)?; // SAFETY: // Ipv6FragmentHeaderSlice::from_slice always generates // a subslice from the given slice rest. Therefor it is guranteed // that len is always greater or equal the len of rest. rest = unsafe { let len = slice.slice().len(); from_raw_parts( rest.as_ptr().add(len), rest.len() - len ) }; next_header = slice.next_header(); // check if the fragment header actually causes fragmentation fragmented = fragmented || slice.is_fragmenting_payload(); }, AUTH => { let slice = IpAuthenticationHeaderSlice::from_slice(rest)?; // SAFETY: // IpAuthenticationHeaderSlice::from_slice always generates // a subslice from the given slice rest. Therefor it is guranteed // that len is always greater or equal the len of rest. rest = unsafe { let len = slice.slice().len(); from_raw_parts( rest.as_ptr().add(len), rest.len() - len ) }; next_header = slice.next_header(); }, // done parsing, the next header is not a known/supported header extension _ => break, } } Ok((Ipv6ExtensionsSlice{ first_header: if rest.len() != start_slice.len() { Some(start_ip_number) } else { None }, fragmented, slice: &start_slice[..start_slice.len() - rest.len()], }, next_header, rest)) } /// Returns true if a fragmentation header is present in /// the extensions that fragments the payload. /// /// Note: A fragmentation header can still be present /// even if the return value is false in case the fragmentation /// headers don't fragment the payload. This is the case if /// the offset of all fragmentation header is 0 and the /// more fragment bit is not set. #[inline] pub fn is_fragmenting_payload(&self) -> bool { self.fragmented } /// Returns the ip protocol number of the first header in the slice /// if the slice contains an ipv6 extension header. If no ipv6 header /// is present None is returned. /// /// None is only returned if the slice length of this struct is 0. #[inline] pub fn first_header(&self) -> Option { self.first_header } /// Slice containing the ipv6 extension headers. #[inline] pub fn slice(&self) -> &'a[u8] { self.slice } /// Returns true if no IPv6 extension header is present (slice is empty). #[inline] pub fn is_empty(&self) -> bool { self.slice.is_empty() } } /// Enum containing a slice of a supported ipv6 extension header. /// /// This enum is used as item type when iterating over a list of extension headers /// with an [Ipv6ExtensionSliceIter]. /// /// Note the following extension headers are missing from /// this enum and currently not supported (list taken on 2021-07-17 /// from ): /// /// * Encapsulating Security Payload \[[RFC4303](https://datatracker.ietf.org/doc/html/rfc4303)\] /// * Mobility Header \[[RFC6275](https://datatracker.ietf.org/doc/html/rfc6275)\] /// * Host Identity Protocol \[[RFC7401](https://datatracker.ietf.org/doc/html/rfc7401)\] /// * Shim6 Protocol \[[RFC5533](https://datatracker.ietf.org/doc/html/rfc5533)\] /// * 253 Use for experimentation and testing \[[RFC3692](https://datatracker.ietf.org/doc/html/rfc3692)\]\[[RFC4727](https://datatracker.ietf.org/doc/html/rfc4727)\] /// * 254 Use for experimentation and testing \[[RFC3692](https://datatracker.ietf.org/doc/html/rfc3692)\]\[[RFC4727](https://datatracker.ietf.org/doc/html/rfc4727)\] #[derive(Clone, Debug, Eq, PartialEq)] pub enum Ipv6ExtensionSlice<'a> { /// IPv6 Hop-by-Hop Option \[[RFC8200](https://datatracker.ietf.org/doc/html/rfc8200)\] HopByHop(Ipv6RawExtensionHeaderSlice<'a>), /// Routing Header for IPv6 \[[RFC8200](https://datatracker.ietf.org/doc/html/rfc8200)\] \[[RFC5095](https://datatracker.ietf.org/doc/html/rfc5095)\] Routing(Ipv6RawExtensionHeaderSlice<'a>), /// Fragment Header for IPv6 \[[RFC8200](https://datatracker.ietf.org/doc/html/rfc8200)\] Fragment(Ipv6FragmentHeaderSlice<'a>), /// Destination Options for IPv6 \[[RFC8200](https://datatracker.ietf.org/doc/html/rfc8200)\] DestinationOptions(Ipv6RawExtensionHeaderSlice<'a>), /// Authentication Header \[[RFC4302](https://datatracker.ietf.org/doc/html/rfc4302)\] Authentication(IpAuthenticationHeaderSlice<'a>), } impl<'a> IntoIterator for Ipv6ExtensionsSlice<'a> { type Item = Ipv6ExtensionSlice<'a>; type IntoIter = Ipv6ExtensionSliceIter<'a>; fn into_iter(self) -> Self::IntoIter { Ipv6ExtensionSliceIter { // map the next header None value to some non ipv6 ext header // value. next_header: self.first_header.unwrap_or(ip_number::UDP), rest: self.slice, } } } /// Allows iterating over the IPv6 extension headers present in an [Ipv6ExtensionsSlice]. #[derive(Clone, Debug, Eq, PartialEq)] pub struct Ipv6ExtensionSliceIter<'a> { next_header: u8, rest: &'a [u8], } impl<'a> Default for Ipv6ExtensionSliceIter<'a> { fn default() -> Self { Ipv6ExtensionSliceIter { // don't use 0 as this is the reserved value // for the hop by hop header next_header: IpNumber::IPv6NoNextHeader as u8, rest: &[], } } } impl<'a> Iterator for Ipv6ExtensionSliceIter<'a> { type Item = Ipv6ExtensionSlice<'a>; fn next(&mut self) -> Option> { use ip_number::*; use Ipv6ExtensionSlice::*; match self.next_header { // Note on the unsafe calls: // // As the slice contents & length were previously checked by // Ipv6ExtensionsSlice::from_slice the content does not have to be // rechecked. IPV6_HOP_BY_HOP => unsafe { let slice = Ipv6RawExtensionHeaderSlice::from_slice_unchecked(self.rest); let len = slice.slice().len(); self.rest = from_raw_parts( self.rest.as_ptr().add(len), self.rest.len() - len ); self.next_header = slice.next_header(); Some(HopByHop(slice)) }, IPV6_ROUTE => unsafe { let slice = Ipv6RawExtensionHeaderSlice::from_slice_unchecked(self.rest); let len = slice.slice().len(); self.rest = from_raw_parts( self.rest.as_ptr().add(len), self.rest.len() - len ); self.next_header = slice.next_header(); Some(Routing(slice)) }, IPV6_DEST_OPTIONS => unsafe { let slice = Ipv6RawExtensionHeaderSlice::from_slice_unchecked(self.rest); let len = slice.slice().len(); self.rest = from_raw_parts( self.rest.as_ptr().add(len), self.rest.len() - len ); self.next_header = slice.next_header(); Some(DestinationOptions(slice)) }, IPV6_FRAG => unsafe { let slice = Ipv6FragmentHeaderSlice::from_slice_unchecked(self.rest); let len = slice.slice().len(); self.rest = from_raw_parts( self.rest.as_ptr().add(len), self.rest.len() - len ); self.next_header = slice.next_header(); Some(Fragment(slice)) }, AUTH => unsafe { let slice = IpAuthenticationHeaderSlice::from_slice_unchecked(self.rest); let len = slice.slice().len(); self.rest = from_raw_parts( self.rest.as_ptr().add(len), self.rest.len() - len ); self.next_header = slice.next_header(); Some(Authentication(slice)) }, // done parsing, the next header is not a known/supported header extension _ => None, } } } etherparse-0.13.0/src/internet/ipv6_fragment.rs000064400000000000000000000273741046102023000176200ustar 00000000000000use super::super::*; use std::slice::from_raw_parts; /// IPv6 fragment header. #[derive(Clone, Debug, Eq, PartialEq)] pub struct Ipv6FragmentHeader { /// IP protocol number specifying the next header or transport layer protocol. /// /// See [IpNumber] or [ip_number] for a definition of the known values. pub next_header: u8, /// Offset in 8 octets /// /// Note: In the header only 13 bits are used, so the allowed range /// of the value is between 0 and 0x1FFF (inclusive). pub fragment_offset: u16, /// True if more fragment packets will follow. False if this is the last packet. pub more_fragments: bool, /// Identifcation value generated by the source. pub identification: u32 } impl Ipv6FragmentHeader { /// Create a new fragmentation header with the given parameters. /// /// Note that the `fragment_offset` can only support values between 0 and 0x1fff (inclusive). pub const fn new(next_header: u8, fragment_offset: u16, more_fragments: bool, identification: u32) -> Ipv6FragmentHeader { Ipv6FragmentHeader{ next_header, fragment_offset, more_fragments, identification } } /// Read an Ipv6FragmentHeader from a slice and return the header & unused parts of the slice. pub fn from_slice(slice: &[u8]) -> Result<(Ipv6FragmentHeader, &[u8]), ReadError> { let s = Ipv6FragmentHeaderSlice::from_slice(slice)?; let rest = &slice[8..]; let header = s.to_header(); Ok(( header, rest )) } /// Read an fragment header from the current reader position. pub fn read(reader: &mut T) -> Result { let buffer = { let mut buffer : [u8;8] = [0;8]; reader.read_exact(&mut buffer)?; buffer }; Ok(Ipv6FragmentHeader { next_header: buffer[0], fragment_offset: u16::from_be_bytes( [ (buffer[2] >> 3) & 0b0001_1111u8, ((buffer[2] << 5) & 0b1110_0000u8) | (buffer[3] & 0b0001_1111u8) ] ), more_fragments: 0 != buffer[3] & 0b1000_0000u8, identification: u32::from_be_bytes( [ buffer[4], buffer[5], buffer[6], buffer[7], ] ), }) } /// Writes a given IPv6 fragment header to the current position. pub fn write(&self, writer: &mut T) -> Result<(), WriteError> { Ok(writer.write_all(&self.to_bytes()?)?) } /// Length of the header in bytes. #[inline] pub fn header_len(&self) -> usize { 8 } /// Checks if the fragment header actually fragments the packet. /// /// Returns false if the fragment offset is 0 and the more flag /// is not set. Otherwise returns true. /// /// [RFC8200](https://datatracker.ietf.org/doc/html/rfc8200) explicitly /// states that fragment headers that don't fragment the packet payload are /// allowed. See the following quote from /// RFC8200 page 32: /// /// > Revised the text to handle the case of fragments that are whole /// > datagrams (i.e., both the Fragment Offset field and the M flag /// > are zero). If received, they should be processed as a /// > reassembled packet. Any other fragments that match should be /// > processed independently. The Fragment creation process was /// > modified to not create whole datagram fragments (Fragment /// > Offset field and the M flag are zero). See /// > [RFC6946](https://datatracker.ietf.org/doc/html/6946) and /// > [RFC8021](https://datatracker.ietf.org/doc/html/rfc8021) for more /// > information." /// /// ``` /// use etherparse::{Ipv6FragmentHeader, ip_number::UDP}; /// /// // offset 0 & no more fragments result in an unfragmented payload /// { /// let header = Ipv6FragmentHeader::new(UDP, 0, false, 123); /// assert!(false == header.is_fragmenting_payload()); /// } /// /// // offset 0 & but more fragments will come -> fragmented /// { /// let header = Ipv6FragmentHeader::new(UDP, 0, true, 123); /// assert!(header.is_fragmenting_payload()); /// } /// /// // offset non zero & no more fragments will come -> fragmented /// { /// let header = Ipv6FragmentHeader::new(UDP, 1, false, 123); /// assert!(header.is_fragmenting_payload()); /// } /// ``` #[inline] pub fn is_fragmenting_payload(&self) -> bool { self.more_fragments || (0 != self.fragment_offset) } /// Returns the serialized form of the header as a statically /// sized byte array. /// /// The `fragment_offset` is only allowed to have the maximum /// size of `0x1FFF`. #[inline] pub fn to_bytes(&self) -> Result<[u8;8], ValueError> { use ErrorField::*; max_check_u16( self.fragment_offset, 0b0001_1111_1111_1111u16, Ipv6FragmentOffset )?; let fo_be: [u8;2] = self.fragment_offset.to_be_bytes(); let id_be = self.identification.to_be_bytes(); Ok( [ self.next_header, 0, ( ((fo_be[0] << 3) & 0b1111_1000u8) | ((fo_be[1] >> 5) & 0b0000_0111u8) ), ( (fo_be[1] & 0b0001_1111u8) | if self.more_fragments { 0b1000_0000u8 } else { 0 } ), id_be[0], id_be[1], id_be[2], id_be[3], ] ) } } /// Slice containing an IPv6 fragment header. #[derive(Clone, Debug, Eq, PartialEq)] pub struct Ipv6FragmentHeaderSlice<'a> { /// Slice containing the packet data. slice: &'a [u8] } impl<'a> Ipv6FragmentHeaderSlice<'a> { /// Creates a hop by hop header slice from a slice. pub fn from_slice(slice: &'a[u8]) -> Result, ReadError> { // the fragmentation header has the exact size of 8 bytes use crate::ReadError::*; if slice.len() < 8 { Err(UnexpectedEndOfSlice(8)) } else { Ok(Ipv6FragmentHeaderSlice { // SAFETY: // Safe as slice length is checked to be at least 8 before this // code can be reached. slice: unsafe { from_raw_parts( slice.as_ptr(), 8 ) } }) } } /// Creates a hop by hop header slice from a slice (assumes slice size & content was validated before). /// /// # Safety /// /// This function assumes that the passed slice has at least the length /// of 8. If a slice with length less then 8 is passed to this function /// the behavior will be undefined. pub unsafe fn from_slice_unchecked(slice: &'a[u8]) -> Ipv6FragmentHeaderSlice<'a> { // the fragmentation header has the exact size of 8 bytes Ipv6FragmentHeaderSlice { slice: from_raw_parts( slice.as_ptr(), 8 ) } } /// Returns the slice containing the ipv6 fragment header. #[inline] pub fn slice(&self) -> &'a[u8] { self.slice } /// Returns the IP protocol number of the next header. /// /// See [IpNumber] or [ip_number] for a definition of the known values. #[inline] pub fn next_header(&self) -> u8 { // SAFETY: // Slice size checked to be at least 8 bytes in constructor. unsafe { *self.slice.get_unchecked(0) } } /// Fragment offset in 8 octets. /// /// Note: In the header only 13 bits are used, so the allowed range /// of the value is between 0 and 0x1FFF (inclusive). #[inline] pub fn fragment_offset(&self) -> u16 { u16::from_be_bytes( // SAFETY: // Slice size checked to be at least 8 bytes in constructor. unsafe { [ (*self.slice.get_unchecked(2) >> 3) & 0b0001_1111u8, ((*self.slice.get_unchecked(2) << 5) & 0b1110_0000u8) | (*self.slice.get_unchecked(3) & 0b0001_1111u8) ] } ) } /// True if more fragment packets will follow. False if this is the last packet. #[inline] pub fn more_fragments(&self) -> bool { // SAFETY: // Slice size checked to be at least 8 bytes in constructor. unsafe { 0 != *self.slice.get_unchecked(3) & 0b1000_0000u8 } } /// Checks if the fragment header actually fragments the packet. /// /// Returns false if the fragment offset is 0 and the more flag /// is not set. Otherwise returns true. /// /// [RFC8200](https://datatracker.ietf.org/doc/html/rfc8200) explicitly /// states that fragment headers that don't fragment the packet payload are /// allowed. See the following quote from /// RFC8200 page 32: /// /// > Revised the text to handle the case of fragments that are whole /// > datagrams (i.e., both the Fragment Offset field and the M flag /// > are zero). If received, they should be processed as a /// > reassembled packet. Any other fragments that match should be /// > processed independently. The Fragment creation process was /// > modified to not create whole datagram fragments (Fragment /// > Offset field and the M flag are zero). See /// > [RFC6946](https://datatracker.ietf.org/doc/html/6946) and /// > [RFC8021](https://datatracker.ietf.org/doc/html/rfc8021) for more /// > information." /// /// ``` /// use etherparse::Ipv6FragmentHeaderSlice; /// /// { /// let slice = Ipv6FragmentHeaderSlice::from_slice(&[ /// 0, 0, 0, 0, // offset 0 & more_fragments not set /// 1, 2, 3, 4, /// ]).unwrap(); /// assert!(false == slice.is_fragmenting_payload()); /// } /// /// { /// let slice = Ipv6FragmentHeaderSlice::from_slice(&[ /// 0, 0, 0, 0b1000_0000u8, // more_fragments set /// 1, 2, 3, 4, /// ]).unwrap(); /// assert!(slice.is_fragmenting_payload()); /// } /// /// { /// let slice = Ipv6FragmentHeaderSlice::from_slice(&[ /// 0, 0, 1, 0, // non zero offset /// 1, 2, 3, 4, /// ]).unwrap(); /// assert!(slice.is_fragmenting_payload()); /// } /// ``` #[inline] pub fn is_fragmenting_payload(&self) -> bool { // SAFETY: // Slice size checked to be at least 8 bytes in constructor. unsafe { 0 != *self.slice.get_unchecked(2) || 0 != (*self.slice.get_unchecked(3) & 0b1001_1111u8) // exclude the reserved bytes } } /// Identifcation value generated by the source pub fn identification(&self) -> u32 { // SAFETY: // Slice size checked to be at least 8 bytes in constructor. unsafe { get_unchecked_be_u32(self.slice.as_ptr().add(4)) } } /// Decode some of the fields and copy the results to a /// Ipv6FragmentHeader struct. pub fn to_header(&self) -> Ipv6FragmentHeader { Ipv6FragmentHeader{ next_header: self.next_header(), fragment_offset: self.fragment_offset(), more_fragments: self.more_fragments(), identification: self.identification() } } } etherparse-0.13.0/src/internet/ipv6_raw_extension.rs000064400000000000000000000260131046102023000206670ustar 00000000000000use super::super::*; use std::fmt::{Debug, Formatter}; use std::slice::from_raw_parts; ///Maximum number of header extensions allowed (according to the ipv6 rfc8200, & iana protocol numbers). pub const IPV6_MAX_NUM_HEADER_EXTENSIONS: usize = 12; /// Raw IPv6 extension header (undecoded payload). /// /// IPv6 extension header with only minimal data interpretation. NOTE only ipv6 header /// extensions with the first two bytes representing the next header and the header length /// in 8-octets (- 8 octets) can be represented with this struct. This excludes the "Authentication /// Header" (AH) and "Encapsulating Security Payload" (ESP). /// /// The following headers can be represented in a `Ipv6RawExtensionHeader`: /// * Hop by Hop /// * Destination Options /// * Routing /// * Mobility /// * Host Identity Protocol /// * Shim6 Protocol #[derive(Clone)] pub struct Ipv6RawExtensionHeader { /// IP protocol number specifying the next header or transport layer protocol. /// /// See [IpNumber] or [ip_number] for a definition of the known values. pub next_header: u8, /// Length of the extension header in 8 octets (minus the first 8 octets). header_length: u8, //// The data contained in the extension header (excluding next_header & hdr length). payload_buffer: [u8;0xff * 8 + 6], } impl Debug for Ipv6RawExtensionHeader { fn fmt(&self, fotmatter: &mut Formatter) -> Result<(), std::fmt::Error> { write!(fotmatter, "Ipv6RawExtensionHeader {{ next_header: {}, payload: {:?} }}", self.next_header, self.payload()) } } impl PartialEq for Ipv6RawExtensionHeader { fn eq(&self, other: &Self) -> bool { self.next_header == other.next_header && self.payload() == other.payload() } } impl Eq for Ipv6RawExtensionHeader {} impl Ipv6RawExtensionHeader { /// Minimum length of a [Ipv6RawExtensionHeader] payload pub const MIN_PAYLOAD_LEN: usize = 6; /// Maximum length of a [Ipv6RawExtensionHeader] the payload pub const MAX_PAYLOAD_LEN: usize = 0xff*8 + 6; /// Returns true if the given header type ip number can be represented in an `Ipv6ExtensionHeader`. pub fn header_type_supported(next_header: u8) -> bool { use crate::ip_number::*; matches!(next_header, IPV6_HOP_BY_HOP | IPV6_ROUTE | IPV6_DEST_OPTIONS | MOBILITY | HIP | SHIM6) } /// Creates an generic IPv6 extension header with the given data. /// /// # Arguments /// /// * `next_header` - type of content after this header (protocol number) /// * `payload` - slice containing the data of the header. This must NOT contain the `next header` and `extended header length` fields of the header. /// /// Note that `payload` must have at least the length of 6 bytes and only supports /// length increases in steps of 8. This measn that the following expression must be true `(payload.len() + 2) % 8 == 0`. /// The maximum length of the payload is `2046` bytes (`Ipv6RawExtensionHeader::MAX_PAYLOAD_LEN`). /// /// If a payload with a non supported length is provided a `ValueError` is returned. pub fn new_raw(next_header: u8, payload: &[u8]) -> Result { use ValueError::*; if payload.len() < Self::MIN_PAYLOAD_LEN { Err(Ipv6ExtensionPayloadTooSmall(payload.len())) } else if payload.len() > Self::MAX_PAYLOAD_LEN { Err(Ipv6ExtensionPayloadTooLarge(payload.len())) } else if 0 != (payload.len() + 2) % 8 { Err(Ipv6ExtensionPayloadLengthUnaligned(payload.len())) } else { let mut result = Ipv6RawExtensionHeader { next_header, header_length: ((payload.len() - 6) / 8) as u8, payload_buffer: [0;Self::MAX_PAYLOAD_LEN] }; result.payload_buffer[..payload.len()].copy_from_slice(payload); Ok(result) } } /// Read an Ipv6ExtensionHeader from a slice and return the header & unused parts of the slice. pub fn from_slice(slice: &[u8]) -> Result<(Ipv6RawExtensionHeader, &[u8]), ReadError> { let s = Ipv6RawExtensionHeaderSlice::from_slice(slice)?; let rest = &slice[s.slice().len()..]; let header = s.to_header(); Ok(( header, rest )) } /// Return a slice containing the current payload. This does NOT contain /// the `next_header` and `header_length` fields. But everything after these /// two fields. pub fn payload(&self) -> &[u8] { &self.payload_buffer[..(6 + usize::from(self.header_length)*8)] } /// Sets the payload (content of the header after the `next_header` & `header_length` fields). /// /// Note that `payload` must have at least the length of 6 bytes and only supports /// length increases in steps of 8. This measn that the following expression must be true `(payload.len() + 2) % 8 == 0`. /// The maximum length of the payload is `2046` bytes (`Ipv6RawExtensionHeader::MAX_PAYLOAD_LEN`). /// /// If a payload with a non supported length is provided a `ValueError` is returned and the payload of the header is not changed. pub fn set_payload(&mut self, payload: &[u8]) -> Result<(), ValueError> { use ValueError::*; if payload.len() < Self::MIN_PAYLOAD_LEN { Err(Ipv6ExtensionPayloadTooSmall(payload.len())) } else if payload.len() > Self::MAX_PAYLOAD_LEN { Err(Ipv6ExtensionPayloadTooLarge(payload.len())) } else if 0 != (payload.len() + 2) % 8 { Err(Ipv6ExtensionPayloadLengthUnaligned(payload.len())) } else { self.payload_buffer[..payload.len()].copy_from_slice(payload); self.header_length = ((payload.len() - 6) / 8) as u8; Ok(()) } } /// Read an fragment header from the current reader position. pub fn read(reader: &mut T) -> Result { let (next_header, header_length) = { let mut d : [u8;2] = [0;2]; reader.read_exact(&mut d)?; (d[0], d[1]) }; Ok(Ipv6RawExtensionHeader { next_header, header_length, payload_buffer: { let mut buffer = [0;0xff * 8 + 6]; reader.read_exact(&mut buffer[..usize::from(header_length)*8 + 6])?; buffer }, }) } /// Writes a given IPv6 extension header to the current position. pub fn write(&self, writer: &mut W) -> Result<(), WriteError> { writer.write_all(&[self.next_header, self.header_length])?; writer.write_all(self.payload())?; Ok(()) } ///Length of the header in bytes. pub fn header_len(&self) -> usize { 2 + (6 + usize::from(self.header_length)*8) } } /// Slice containing an IPv6 extension header without specific decoding methods (fallback in case no specific implementation is available). /// /// Slice containing an IPv6 extension header with only minimal data interpretation. NOTE only ipv6 header /// extensions with the first two bytes representing the next header and the header length /// in 8-octets (- 8 octets) can be represented with this struct. This excludes the "Authentication /// Header" (AH) and "Encapsulating Security Payload" (ESP). /// /// The following headers can be represented in a Ipv6ExtensionHeaderSlice: /// * HopbyHop /// * Destination Options /// * Routing /// * Mobility /// * Host Identity Protocol /// * Shim6 Protocol #[derive(Clone, Debug, Eq, PartialEq)] pub struct Ipv6RawExtensionHeaderSlice<'a> { /// Slice containing the packet data. slice: &'a [u8], } impl<'a> Ipv6RawExtensionHeaderSlice<'a> { /// Returns true if the given header type ip number can be represented in an `Ipv6ExtensionHeaderSlice`. pub fn header_type_supported(next_header: u8) -> bool { Ipv6RawExtensionHeader::header_type_supported(next_header) } /// Creates a generic ipv6 extension header slice from a slice. pub fn from_slice(slice: &'a[u8]) -> Result, ReadError> { //check length use crate::ReadError::*; if slice.len() < 8 { return Err(UnexpectedEndOfSlice(8)); } //check length let len = ((slice[1] as usize) + 1)*8; //check the length again now that the expected length is known if slice.len() < len { return Err(UnexpectedEndOfSlice(len)); } //all good Ok(Ipv6RawExtensionHeaderSlice { // SAFETY: // Safe as the slice has been checked in the previous if // to have at least the the length of the variable len. slice: unsafe { from_raw_parts( slice.as_ptr(), len ) } }) } /// Creates a raw ipv6 extension header slice from a slice (assumes slice /// size & content was validated before). /// /// # Safety /// /// This method assumes that the slice was previously validated to contain /// a valid & supported raw ipv6 extension header. This means the slice length /// must at least be at least 8 and `(slice[1] + 1)*8`. The data that the /// slice points must also be valid (meaning no nullptr or alike allowed). /// /// If these precondtions are not fullfilled the behavior of this function /// and the methods of the return IpAuthenticationHeaderSlice will be undefined. pub unsafe fn from_slice_unchecked(slice: &'a[u8]) -> Ipv6RawExtensionHeaderSlice<'a> { Ipv6RawExtensionHeaderSlice { slice: from_raw_parts( slice.as_ptr(), ((*slice.get_unchecked(1) as usize) + 1)*8 ) } } /// Returns the slice containing the ipv6 extension header #[inline] pub fn slice(&self) -> &'a[u8] { self.slice } /// Returns the IP protocol number of the next header or transport layer protocol. /// /// See [IpNumber] or [ip_number] for a definition of the known values. #[inline] pub fn next_header(&self) -> u8 { unsafe { *self.slice.get_unchecked(0) } } /// Returns a slice containing the payload data of the header. /// /// This contains all the data after the header length field /// until the end of the header (length specified by the /// hdr ext length field). #[inline] pub fn payload(&self) -> &'a[u8] { unsafe { from_raw_parts( self.slice.as_ptr().add(2), self.slice.len() - 2 ) } } /// Convert the slice to an [Ipv6RawExtensionHeader]. /// /// Decode some of the fields and copy the results to a /// [Ipv6RawExtensionHeader] struct together with a slice pointing /// to the non decoded parts. pub fn to_header(&self) -> Ipv6RawExtensionHeader { Ipv6RawExtensionHeader::new_raw( self.next_header(), self.payload() ).unwrap() } } etherparse-0.13.0/src/internet/mod.rs000064400000000000000000000002511046102023000156110ustar 00000000000000 pub mod ip; pub mod ip_authentication; pub mod ipv4; pub mod ipv4_extensions; pub mod ipv6; pub mod ipv6_extensions; pub mod ipv6_raw_extension; pub mod ipv6_fragment; etherparse-0.13.0/src/lib.rs000064400000000000000000001037001046102023000137530ustar 00000000000000//! A zero allocation library for parsing & writing a bunch of packet based protocols (EthernetII, IPv4, IPv6, UDP, TCP ...). //! //! Currently supported are: //! * Ethernet II //! * IEEE 802.1Q VLAN Tagging Header //! * IPv4 //! * IPv6 (supporting the most common extension headers, but not all) //! * UDP //! * TCP //! * ICMP & ICMPv6 (not all message types are supported) //! //! # Usage //! //! Add the following to your `Cargo.toml`: //! //! ```toml //! [dependencies] //! etherparse = "0.13" //! ``` //! //! # What is etherparse? //! Etherparse is intended to provide the basic network parsing functions that allow for easy analysis, transformation or generation of recorded network data. //! //! Some key points are: //! //! * It is completly written in Rust and thoroughly tested. //! * Special attention has been paid to not use allocations or syscalls. //! * The package is still in development and can & will still change. //! * The current focus of development is on the most popular protocols in the internet & transport layer. //! //! # How to parse network packages? //! Etherparse gives you two options for parsing network packages automatically: //! //! ## Slicing the packet //! Here the different components in a packet are seperated without parsing all their fields. For each header a slice is generated that allows access to the fields of a header. //! ``` //! # use etherparse::{SlicedPacket, PacketBuilder}; //! # let builder = PacketBuilder:: //! # ethernet2([1,2,3,4,5,6], //source mac //! # [7,8,9,10,11,12]) //destionation mac //! # .ipv4([192,168,1,1], //source ip //! # [192,168,1,2], //desitionation ip //! # 20) //time to life //! # .udp(21, //source port //! # 1234); //desitnation port //! # //payload of the udp packet //! # let payload = [1,2,3,4,5,6,7,8]; //! # //get some memory to store the serialized data //! # let mut packet = Vec::::with_capacity( //! # builder.size(payload.len())); //! # builder.write(&mut packet, &payload).unwrap(); //! match SlicedPacket::from_ethernet(&packet) { //! Err(value) => println!("Err {:?}", value), //! Ok(value) => { //! println!("link: {:?}", value.link); //! println!("vlan: {:?}", value.vlan); //! println!("ip: {:?}", value.ip); //! println!("transport: {:?}", value.transport); //! } //! } //! ``` //! This is the faster option if your code is not interested in all fields of all the headers. It is a good choice if you just want filter or find packages based on a subset of the headers and/or their fields. //! //! Depending from which point downward you want to slice a package check out the functions: //! //! * [`SlicedPacket::from_ethernet`] for parsing from an Ethernet II header downwards //! * [`SlicedPacket::from_ether_type`] for parsing a slice starting after an Ethernet II header //! * [`SlicedPacket::from_ip`] for parsing from an IPv4 or IPv6 downwards //! //! ## Deserializing all headers into structs //! This option deserializes all known headers and transferes their contents to header structs. //! ```rust //! # use etherparse::{PacketHeaders, PacketBuilder}; //! # let builder = PacketBuilder:: //! # ethernet2([1,2,3,4,5,6], //source mac //! # [7,8,9,10,11,12]) //destionation mac //! # .ipv4([192,168,1,1], //source ip //! # [192,168,1,2], //desitionation ip //! # 20) //time to life //! # .udp(21, //source port //! # 1234); //desitnation port //! # //payload of the udp packet //! # let payload = [1,2,3,4,5,6,7,8]; //! # //get some memory to store the serialized data //! # let mut packet = Vec::::with_capacity( //! # builder.size(payload.len())); //! # builder.write(&mut packet, &payload).unwrap(); //! match PacketHeaders::from_ethernet_slice(&packet) { //! Err(value) => println!("Err {:?}", value), //! Ok(value) => { //! println!("link: {:?}", value.link); //! println!("vlan: {:?}", value.vlan); //! println!("ip: {:?}", value.ip); //! println!("transport: {:?}", value.transport); //! } //! } //! ``` //! This option is slower then slicing when only few fields are accessed. But it can be the faster option or useful if you are interested in most fields anyways or if you want to re-serialize the headers with modified values. //! //! Depending from which point downward you want to unpack a package check out the functions //! //! * [`PacketHeaders::from_ethernet_slice`] for parsing from an Ethernet II header downwards //! * [`PacketHeaders::from_ether_type`] for parsing a slice starting after an Ethernet II header //! * [`PacketHeaders::from_ip_slice`] for parsing from an IPv4 or IPv6 downwards //! //! ## Manually slicing & parsing packets //! It is also possible to manually slice & parse a packet. For each header type there is are metods that create a slice or struct from a memory slice. //! //! Have a look at the documentation for the Slice.from_slice methods, if you want to create your own slices: //! //! * [`Ethernet2HeaderSlice::from_slice`] //! * [`SingleVlanHeaderSlice::from_slice`] //! * [`DoubleVlanHeaderSlice::from_slice`] //! * [`Ipv4HeaderSlice::from_slice`] //! * [`Ipv4ExtensionsSlice::from_slice`] //! * [`Ipv6HeaderSlice::from_slice`] //! * [`Ipv6ExtensionsSlice::from_slice`] //! * [`Ipv6RawExtensionHeaderSlice::from_slice`] //! * [`IpAuthenticationHeaderSlice::from_slice`] //! * [`Ipv6FragmentHeaderSlice::from_slice`] //! * [`UdpHeaderSlice::from_slice`] //! * [`TcpHeaderSlice::from_slice`] //! * [`Icmpv4Slice::from_slice`] //! * [`Icmpv6Slice::from_slice`] //! //! And for deserialization into the corresponding header structs have a look at: //! //! * [`Ethernet2Header::read`] & [`Ethernet2Header::from_slice`] //! * [`SingleVlanHeader::read`] & [`SingleVlanHeader::from_slice`] //! * [`DoubleVlanHeader::read`] & [`DoubleVlanHeader::from_slice`] //! * [`IpHeader::read`] & [`IpHeader::from_slice`] //! * [`Ipv4Header::read`] & [`Ipv4Header::from_slice`] //! * [`Ipv4Extensions::read`] & [`Ipv4Extensions::from_slice`] //! * [`Ipv6Header::read`] & [`Ipv6Header::from_slice`] //! * [`Ipv6Extensions::read`] & [`Ipv6Extensions::from_slice`] //! * [`Ipv6RawExtensionHeader::read`] & [`Ipv6RawExtensionHeader::from_slice`] //! * [`IpAuthenticationHeader::read`] & [`IpAuthenticationHeader::from_slice`] //! * [`Ipv6FragmentHeader::read`] & [`Ipv6FragmentHeader::from_slice`] //! * [`UdpHeader::read`] & [`UdpHeader::from_slice`] //! * [`TcpHeader::read`] & [`TcpHeader::from_slice`] //! * [`Icmpv4Header::read`] & [`Icmpv4Header::from_slice`] //! * [`Icmpv6Header::read`] & [`Icmpv6Header::from_slice`] //! //! # How to generate fake packet data? //! ## Packet Builder //! The PacketBuilder struct provides a high level interface for quickly creating network packets. The PacketBuilder will automatically set fields which can be deduced from the content and compositions of the packet itself (e.g. checksums, lengths, ethertype, ip protocol number). //! //! [Example:](https://github.com/JulianSchmid/etherparse/blob/0.10.1/examples/write_udp.rs) //! ```rust //! use etherparse::PacketBuilder; //! //! let builder = PacketBuilder:: //! ethernet2([1,2,3,4,5,6], //source mac //! [7,8,9,10,11,12]) //destination mac //! .ipv4([192,168,1,1], //source ip //! [192,168,1,2], //desitination ip //! 20) //time to life //! .udp(21, //source port //! 1234); //desitnation port //! //! //payload of the udp packet //! let payload = [1,2,3,4,5,6,7,8]; //! //! //get some memory to store the result //! let mut result = Vec::::with_capacity(builder.size(payload.len())); //! //! //serialize //! //this will automatically set all length fields, checksums and identifiers (ethertype & protocol) //! //before writing the packet out to "result" //! builder.write(&mut result, &payload).unwrap(); //! ``` //! //! There is also an [example for TCP packets](https://github.com/JulianSchmid/etherparse/blob/0.10.1/examples/write_tcp.rs) available. //! //! Check out the [PacketBuilder documentation](struct.PacketBuilder.html) for more informations. //! //! ## Manually serialising each header //! Alternativly it is possible to manually build a packet ([example](https://github.com/JulianSchmid/etherparse/blob/0.10.1/examples/write_ipv4_udp.rs)). Generally each struct representing a header has a "write" method that allows it to be serialized. These write methods sometimes automatically calculate checksums and fill them in. In case this is unwanted behavior (e.g. if you want to generate a packet with an invalid checksum), it is also possible to call a "write_raw" method that will simply serialize the data without doing checksum calculations. //! //! Read the documentations of the different methods for a more details: //! //! * [`Ethernet2Header::write`] //! * [`SingleVlanHeader::write`] //! * [`DoubleVlanHeader::write`] //! * [`Ipv4Header::write`] //! * [`Ipv4Header::write_raw`] //! * [`Ipv4Extensions::write`] //! * [`Ipv6Header::write`] //! * [`Ipv6Extensions::write`] //! * [`Ipv6RawExtensionHeader::write`] //! * [`IpAuthenticationHeader::write`] //! * [`Ipv6FragmentHeader::write`] //! * [`UdpHeader::write`] //! * [`TcpHeader::write`] //! * [`Icmpv4Header::write`] //! * [`Icmpv6Header::write`] //! //! # Roadmap //! * Documentation //! * Packet Builder //! * MutPacketSlice -> modifaction of fields in slices directly? //! * Reserializing SlicedPacket & MutSlicedPacket with corrected checksums & id's //! * Slicing & reading packet from different layers then ethernet onward (e.g. ip, vlan...) //! * IEEE 802.3 //! //! # References //! * Darpa Internet Program Protocol Specification [RFC 791](https://tools.ietf.org/html/rfc791) //! * Internet Protocol, Version 6 (IPv6) Specification [RFC 8200](https://tools.ietf.org/html/rfc8200) //! * [IANA Protocol Numbers](https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml) //! * [Internet Protocol Version 6 (IPv6) Parameters](https://www.iana.org/assignments/ipv6-parameters/ipv6-parameters.xhtml) //! * [Wikipedia IEEE_802.1Q](https://en.wikipedia.org/w/index.php?title=IEEE_802.1Q&oldid=820983900) //! * User Datagram Protocol (UDP) [RFC 768](https://tools.ietf.org/html/rfc768) //! * Transmission Control Protocol [RFC 793](https://tools.ietf.org/html/rfc793) //! * TCP Extensions for High Performance [RFC 7323](https://tools.ietf.org/html/rfc7323) //! * The Addition of Explicit Congestion Notification (ECN) to IP [RFC 3168](https://tools.ietf.org/html/rfc3168) //! * Robust Explicit Congestion Notification (ECN) Signaling with Nonces [RFC 3540](https://tools.ietf.org/html/rfc3540) //! * IP Authentication Header [RFC 4302](https://tools.ietf.org/html/rfc4302) //! * Mobility Support in IPv6 [RFC 6275](https://tools.ietf.org/html/rfc6275) //! * Host Identity Protocol Version 2 (HIPv2) [RFC 7401](https://tools.ietf.org/html/rfc7401) //! * Shim6: Level 3 Multihoming Shim Protocol for IPv6 [RFC 5533](https://tools.ietf.org/html/rfc5533) //! * Computing the Internet Checksum [RFC 1071](https://datatracker.ietf.org/doc/html/rfc1071) //! * Internet Control Message Protocol [RFC 792](https://datatracker.ietf.org/doc/html/rfc792) //! * [IANA Internet Control Message Protocol (ICMP) Parameters](https://www.iana.org/assignments/icmp-parameters/icmp-parameters.xhtml) //! * Requirements for Internet Hosts -- Communication Layers [RFC 1122](https://datatracker.ietf.org/doc/html/rfc1122) //! * Requirements for IP Version 4 Routers [RFC 1812](https://datatracker.ietf.org/doc/html/rfc1812) //! * Internet Control Message Protocol (ICMPv6) for the Internet Protocol Version 6 (IPv6) Specification [RFC 4443](https://datatracker.ietf.org/doc/html/rfc4443) //! * ICMP Router Discovery Messages [RFC 1256](https://datatracker.ietf.org/doc/html/rfc1256) //! * [Internet Control Message Protocol version 6 (ICMPv6) Parameters](https://www.iana.org/assignments/icmpv6-parameters/icmpv6-parameters.xhtml) //! * Multicast Listener Discovery (MLD) for IPv6 [RFC 2710](https://datatracker.ietf.org/doc/html/rfc2710) //! * Neighbor Discovery for IP version 6 (IPv6) [RFC 4861](https://datatracker.ietf.org/doc/html/rfc4861) // # Reason for 'bool_comparison' disable: // // Clippy triggers triggers errors like the following if the warning stays enabled: // // warning: equality checks against false can be replaced by a negation // --> src/packet_decoder.rs:131:20 // | // 131 | if false == fragmented { // | ^^^^^^^^^^^^^^^^^^^ help: try simplifying it as shown: `!fragmented` // // // I prefer to write `false == value` instead of `!value` as it // is more visually striking and is not as easy to overlook as the single // character '!'. #![allow( clippy::bool_comparison, )] use std::io; use std::fmt; use std::error::Error; mod link; pub use crate::link::LinkSlice; pub use crate::link::ethernet::*; pub use crate::link::vlan_tagging::*; mod internet; pub use crate::internet::ip::*; pub use crate::internet::ip_authentication::*; pub use crate::internet::ipv4::*; pub use crate::internet::ipv4_extensions::*; pub use crate::internet::ipv6::*; pub use crate::internet::ipv6_extensions::*; pub use crate::internet::ipv6_raw_extension::*; pub use crate::internet::ipv6_fragment::*; mod transport; pub use crate::transport::icmp::*; pub use crate::transport::icmpv4_impl::*; pub use crate::transport::icmpv6_impl::*; pub use crate::transport::tcp::*; pub use crate::transport::udp::*; pub use crate::transport::TransportHeader; /// Helpers for calculating checksums. pub mod checksum; mod packet_builder; pub use crate::packet_builder::*; mod packet_decoder; pub use crate::packet_decoder::*; mod packet_slicing; pub use crate::packet_slicing::*; pub mod packet_filter; ///Contains the size when serialized. pub trait SerializedSize { const SERIALIZED_SIZE: usize; } ///Errors that can occur when reading. #[derive(Debug)] pub enum ReadError { ///Whenever an std::io::Error gets triggerd during a write it gets forwarded via this enum value. IoError(std::io::Error), ///Error when an unexpected end of a slice was reached even though more data was expected to be present (expected minimum size as argument). UnexpectedEndOfSlice(usize), ///Error when a slice has a different size then expected. UnexpectedLenOfSlice{ expected: usize, actual: usize }, ///Error when a double vlan tag was expected but the ether type of the the first vlan header does not an vlan header ether type. ///The value is the unexpected ether type value in the outer vlan header. DoubleVlanOuterNonVlanEtherType(u16), ///Error when the ip header version is not supported (only 4 & 6 are supported). The value is the version that was received. IpUnsupportedVersion(u8), ///Error when the ip header version field is not equal 4. The value is the version that was received. Ipv4UnexpectedVersion(u8), ///Error when the ipv4 header length is smaller then the header itself (5). Ipv4HeaderLengthBad(u8), ///Error when the total length field is too small to contain the header itself. Ipv4TotalLengthTooSmall(u16), ///Error when then ip header version field is not equal 6. The value is the version that was received. Ipv6UnexpectedVersion(u8), ///Error when more then 7 header extensions are present (according to RFC82000 this should never happen). Ipv6TooManyHeaderExtensions, ///Error if the ipv6 hop by hop header does not occur directly after the ipv6 header (see rfc8200 chapter 4.1.) Ipv6HopByHopHeaderNotAtStart, ///Error if the header length in the ip authentication header is smaller then the minimum size of 1. IpAuthenticationHeaderTooSmallPayloadLength(u8), ///Error given if the data_offset field in a TCP header is smaller then the minimum size of the tcp header itself. TcpDataOffsetTooSmall(u8), /// Error when the packet size is too big (e.g larger then can be represendted in a length field). /// /// This error can be triggered by /// * `Icmpv6Slice::from_slice` Icmpv6PacketTooBig(usize), } impl ReadError { ///Adds an offset value to the UnexpectedEndOfSlice error. pub fn add_slice_offset(self, offset: usize) -> ReadError { use crate::ReadError::*; match self { UnexpectedEndOfSlice(value) => UnexpectedEndOfSlice(value + offset), UnexpectedLenOfSlice{ expected, actual } => UnexpectedLenOfSlice{ expected: expected + offset, actual: actual + offset }, value => value } } /// Returns the `std::io::Error` value if the `ReadError` is an `IoError`. /// Otherwise `None is returned. pub fn io_error(self) -> Option { match self { ReadError::IoError(value) => Some(value), _ => None } } /// Returns the expected minimum size if the error is an `UnexpectedEndOfSlice`. pub fn unexpected_end_of_slice_min_expected_size(self) -> Option { match self { ReadError::UnexpectedEndOfSlice(value) => Some(value), _ => None } } } impl fmt::Display for ReadError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { use ReadError::*; match self { IoError(err) => err.fmt(f), UnexpectedEndOfSlice(expected_minimum_size) => { // usize write!(f, "ReadError: Unexpected end of slice. The given slice contained less then minimum required {} bytes.", expected_minimum_size) }, UnexpectedLenOfSlice{ expected, actual } => { write!(f, "ReadError: Unexpected length of slice. The given slice contained {} bytes but {} bytes were required.", actual, expected) }, DoubleVlanOuterNonVlanEtherType(ether_type) => { //u16 write!(f, "ReadError: Expected a double vlan header, but the ether type field value {} of the outer vlan header is a non vlan header ether type.", ether_type) }, IpUnsupportedVersion(version_number) => { // u8 write!(f, "ReadError: Unsupported IP version number. The IP header contained the unsupported version number {}.", version_number) }, Ipv4UnexpectedVersion(version_number) => { //u8 write!(f, "ReadError: Unexpected IP version number. Expected an IPv4 Header but the header contained the version number {}.", version_number) }, Ipv4HeaderLengthBad(header_length) => { //u8 write!(f, "ReadError: Bad IPv4 header length. The header length value {} in the IPv4 header is smaller then the ipv4 header.", header_length) }, Ipv4TotalLengthTooSmall(total_length_field) => { //u16 write!(f, "ReadError: Bad IPv4 total length. The total length value {} in the IPv4 header is smaller then the ipv4 header itself.", total_length_field) }, Ipv6UnexpectedVersion(version_number) => { //u8 write!(f, "ReadError: Unexpected IP version number. Expected an IPv6 Header but the header contained the version number {}.", version_number) }, Ipv6TooManyHeaderExtensions => { write!(f, "ReadError: Too many IPv6 header extensions. There are more then 7 extension headers present, this not supported.") }, Ipv6HopByHopHeaderNotAtStart => { write!(f, "ReadError: Encountered an IPv6 hop-by-hop header somwhere else then directly after the IPv6 header. This is not allowed according to RFC 8200.") }, IpAuthenticationHeaderTooSmallPayloadLength(length) => { write!(f, "ReadError: Authentication header payload size is smaller then 1 ({}) which is smaller then the minimum size of the header.", length) }, TcpDataOffsetTooSmall(data_offset) => { //u8 write!(f, "ReadError: TCP data offset too small. The data offset value {} in the tcp header is smaller then the tcp header itself.", data_offset) }, Icmpv6PacketTooBig(size) => { write!(f, "ReadError: ICMPv6 packet length {} is bigger then can be represented in an u32.", size) } } } } impl Error for ReadError { fn source(&self) -> Option<&(dyn Error + 'static)> { match self { ReadError::IoError(ref err) => Some(err), _ => None } } } impl From for ReadError { fn from(err: std::io::Error) -> ReadError { ReadError::IoError(err) } } ///Errors that can occur when writing. #[derive(Debug)] pub enum WriteError { IoError(std::io::Error), ///Error in the data that was given to write ValueError(ValueError), ///Error when a given slice is not big enough to serialize the data. SliceTooSmall(usize), } impl WriteError { /// Returns the `std::io::Error` value if the `WriteError` is an `IoError`. /// Otherwise `None is returned. pub fn io_error(self) -> Option { match self { WriteError::IoError(value) => Some(value), _ => None } } /// Returns the `std::io::Error` value if the `WriteError` is an `ValueError`. /// Otherwise `None` is returned. pub fn value_error(self) -> Option { match self { WriteError::ValueError(value) => Some(value), _ => None } } /// Returns the expected minimum size if the error is an `SliceTooSmall`. pub fn slice_too_small_size(self) -> Option { match self { WriteError::SliceTooSmall(value) => Some(value), _ => None } } } impl From for WriteError { fn from(err: ValueError) -> WriteError { WriteError::ValueError(err) } } impl From for WriteError { fn from(err: std::io::Error) -> WriteError { WriteError::IoError(err) } } impl fmt::Display for WriteError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { use WriteError::*; match self { IoError(err) => err.fmt(f), ValueError(err) => { write!(f, "ValueError: {}", err) }, SliceTooSmall(size) => { write!(f, "SliceTooSmall: The slice given to write to is too small (required to be at least {} bytes large)", size) } } } } impl Error for WriteError { fn source(&self) -> Option<&(dyn Error + 'static)> { use WriteError::*; match self { IoError(ref err) => Some(err), ValueError(ref err) => Some(err), SliceTooSmall(_) => None } } } /// Errors in the given data #[derive(Debug, PartialEq, Eq, Clone)] pub enum ValueError { /// Error when the ipv4 options length is too big or not aligned (cannot be bigger then 40 bytes and must be a multiple of 4 bytes). Ipv4OptionsLengthBad(usize), /// Error when a given payload & ipv4 header is bigger then what fits inside an ipv4 total_length field. Ipv4PayloadLengthTooLarge(usize), /// Error when a given payload & ipv6 header block is bigger then what fits inside an ipv6 payload_length field. Ipv6PayloadLengthTooLarge(usize), /// Error when a given payload size is smaller then 6 octets which is the minimum ipv6 extended header size ([Ipv6RawExtensionHeader::MAX_PAYLOAD_LEN]). Ipv6ExtensionPayloadTooSmall(usize), /// Error when a given payload size is bigger then what fits inside an ipv6 extended header size ([Ipv6RawExtensionHeader::MAX_PAYLOAD_LEN]). Ipv6ExtensionPayloadTooLarge(usize), /// Error when a given payload length is not aligned to be a multiple of 8 octets when 6 is substracted and can not be represented by the header length field. Ipv6ExtensionPayloadLengthUnaligned(usize), /// Error when a given authentication header icv size is not a multiple of 4 bytes or bigger then 1016 bytes and therefor can not be represented in the header length field. IpAuthenticationHeaderBadIcvLength(usize), /// Error when a header in `Ipv4Extensions` is never written as it is never referenced by any of the other `next_header` fields or the initial `protocol`. Ipv4ExtensionNotReferenced(IpNumber), /// Error when a hop-by-hop header is not referenced as the first header after the ipv6 header but as a later extension header. Ipv6ExtensionHopByHopNotAtStart, /// Error when a header in `Ipv6Extensions` is never written as it is never referenced by any of the other `next_header` fields or the initial ip number. Ipv6ExtensionNotReferenced(IpNumber), /// Error when a header in `Ipv6Extensions` is referenced multiple times or is referenced and not defined. Ipv6ExtensionNotDefinedReference(IpNumber), /// Error when a given payload is bigger then what fits inside an udp packet /// Note that a the maximum payload size, as far as udp is conceirned, is max_value(u16) - 8. The 8 is for the size of the udp header itself. UdpPayloadLengthTooLarge(usize), /// Error when a given payload + tcp header options is bigger then what fits inside an tcp packet /// Note that a the maximum size, as far as tcp is conceirned, is max_value(u16) - tcp_header.data_offset()*4. The data_offset is for the size of the udp header itself. TcpLengthTooLarge(usize), /// Error when a u8 field in a header has a larger value then supported. U8TooLarge{value: u8, max: u8, field: ErrorField}, /// Error when a u16 field in a header has a larger value then supported. U16TooLarge{value: u16, max: u16, field: ErrorField}, /// Error when a u32 field in a header has a larger value then supported. U32TooLarge{value: u32, max: u32, field: ErrorField}, /// Error when an Icmpv6 payload is found in an IPv4 packet. Icmpv6InIpv4, } impl Error for ValueError { } impl fmt::Display for ValueError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { use ValueError::*; match self { Ipv4OptionsLengthBad(options_len) => { //usize write!(f, "Bad IPv4 'options_len'. The IPv4 options length ({} bytes) is either not a multiple of 4 bytes or bigger then the maximum of 40 bytes.", options_len) }, Ipv4PayloadLengthTooLarge(total_length) => { //usize write!(f, "IPv4 'total_legnth' too large. The IPv4 header and payload have a larger size ({} bytes) than can be be represented by the 'total_legnth' field in the IPv4 header.", total_length) }, Ipv6PayloadLengthTooLarge(size) => { //usize write!(f, "IPv6 'payload_length' too large. The IPv6 header block & payload size ({} bytes) is larger then what can be be represented by the 'payload_length' field in the IPv6 header.", size) }, Ipv6ExtensionPayloadTooSmall(size) => { write!(f, "IPv6 extensions header payload length is too small. The payload size ({} bytes) is less then 6 octets which is the minimum IPv6 extension header payload size.", size) }, Ipv6ExtensionPayloadTooLarge(size) => { write!(f, "IPv6 extensions header payload length is too large. The payload size ({} bytes) is larger then what can be be represented by the 'extended header size' field in an IPv6 extension header.", size) }, Ipv6ExtensionPayloadLengthUnaligned(size) => { write!(f, "IPv6 extensions header 'payload length ({} bytes) + 2' is not multiple of 8 (+ 2 for the `next_header` and `header_length` fields). This is required as the header length field can only express lengths in multiple of 8 bytes.", size) }, IpAuthenticationHeaderBadIcvLength(size) => { write!(f, "IP authentication header 'raw_icv' value has a length ({} bytes) is either not a multiple of 4 bytes or bigger then the maximum of 1016 bytes.", size) }, Ipv4ExtensionNotReferenced(ip_protocol_number) => { write!(f, "IPv4 extensions '{:?}' is defined but is not referenced by any of the 'next_header' of the other extension headers or the 'protocol' field of the IPv4 header.", ip_protocol_number) } Ipv6ExtensionHopByHopNotAtStart => { write!(f, "IPv6 extensions hop-by-hop is not located directly after the IPv6 header (required by IPv6).") }, Ipv6ExtensionNotReferenced(ip_protocol_number) => { write!(f, "IPv6 extensions '{:?}' is defined but is not referenced by any of the 'next_header' of the other extension headers or the IPv6 header.", ip_protocol_number) }, Ipv6ExtensionNotDefinedReference(ip_protocol_number) => { write!(f, "IPv6 extensions '{:?}' is referenced by the 'next_header' field of an extension headers or the IPv6 header but is not defined in the 'Ipv6Extensions'.", ip_protocol_number) }, UdpPayloadLengthTooLarge(length) => { //usize write!(f, "UDP 'length' too large. The UDP length ({} bytes) is larger then what can be be represented by the 'length' field in the UDP header.", length) }, TcpLengthTooLarge(length) => { //usize write!(f, "TCP length too large. The TCP packet length ({} bytes) is larger then what is supported.", length) }, U8TooLarge{value, max, field} => { write!(f, "The value {} of the field '{}' is larger then the allowed maximum of {}.", value, field, max) }, U16TooLarge{value, max, field} => { write!(f, "The value {} of the field '{}' is larger then the allowed maximum of {}.", value, field, max) }, U32TooLarge{value, max, field} => { write!(f, "The value {} of the field '{}' is larger then the allowed maximum of {}.", value, field, max) }, Icmpv6InIpv4 => { write!(f, "ICMPv6 packet can not be combined with IPv4 headers.") }, } } } ///Fields that can produce errors when serialized. #[derive(Debug, PartialEq, Eq, Clone)] pub enum ErrorField { Ipv4PayloadLength, Ipv4Dscp, Ipv4Ecn, Ipv4FragmentsOffset, Ipv6FlowLabel, /// Ipv6 fragment header fragment offset field. Ipv6FragmentOffset, ///VlanTaggingHeader.priority_code_point VlanTagPriorityCodePoint, ///VlanTaggingHeader.vlan_identifier VlanTagVlanId, } impl fmt::Display for ErrorField { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { use ErrorField::*; match self { Ipv4PayloadLength => write!(f, "Ipv4Header.payload_len"), Ipv4Dscp => write!(f, "Ipv4Header.differentiated_services_code_point"), Ipv4Ecn => write!(f, "Ipv4Header.explicit_congestion_notification"), Ipv4FragmentsOffset => write!(f, "Ipv4Header.fragments_offset"), Ipv6FlowLabel => write!(f, "Ipv6Header.flow_label"), Ipv6FragmentOffset => write!(f, "Ipv6FragmentHeader.fragment_offset"), VlanTagPriorityCodePoint => write!(f, "SingleVlanHeader.priority_code_point"), VlanTagVlanId => write!(f, "SingleVlanHeader.vlan_identifier") } } } fn max_check_u8(value: u8, max: u8, field: ErrorField) -> Result<(), ValueError> { use crate::ValueError::U8TooLarge; if value <= max { Ok(()) } else { Err(U8TooLarge { value, max, field }) } } fn max_check_u16(value: u16, max: u16, field: ErrorField) -> Result<(), ValueError> { use crate::ValueError::U16TooLarge; if value <= max { Ok(()) } else { Err(U16TooLarge{ value, max, field }) } } /// Helper function for reading big endian u16 values from a ptr unchecked. /// /// # Safety /// /// It is in the responsibility of the caller to ensure there are at least 2 /// bytes accessable via the ptr. If this is not the case undefined behavior /// will be triggered. #[inline] unsafe fn get_unchecked_be_u16(ptr: *const u8) -> u16 { u16::from_be_bytes( [ *ptr, *ptr.add(1), ] ) } /// Helper function for reading big endian u32 values from a ptr unchecked. /// /// # Safety /// /// It is in the responsibility of the caller to ensure there are at least 4 /// bytes accessable via the ptr. If this is not the case undefined behavior /// will be triggered. #[inline] unsafe fn get_unchecked_be_u32(ptr: *const u8) -> u32 { u32::from_be_bytes( [ *ptr, *ptr.add(1), *ptr.add(2), *ptr.add(3) ] ) } /// Helper function for reading a 4 byte fixed-size array. /// /// # Safety /// /// It is in the responsibility of the caller to ensure there are at least 4 /// bytes accessable via the ptr. If this is not the case undefined behavior /// will be triggered. #[inline] unsafe fn get_unchecked_4_byte_array(ptr: *const u8) -> [u8;4] { [ *ptr, *ptr.add(1), *ptr.add(2), *ptr.add(3) ] } /// Helper function for reading a 6 byte fixed-size array. /// /// # Safety /// /// It is in the responsibility of the caller to ensure there are at least 6 /// bytes accessable via the ptr. If this is not the case undefined behavior /// will be triggered. #[inline] unsafe fn get_unchecked_6_byte_array(ptr: *const u8) -> [u8;6] { [ *ptr, *ptr.add(1), *ptr.add(2), *ptr.add(3), *ptr.add(4), *ptr.add(5) ] } /// Helper function for reading a 16 byte fixed-size array. /// /// # Safety /// /// It is in the responsibility of the caller to ensure there are at least 16 /// bytes accessable via the ptr. If this is not the case undefined behavior /// will be triggered. #[inline] unsafe fn get_unchecked_16_byte_array(ptr: *const u8) -> [u8;16] { [ *ptr, *ptr.add(1), *ptr.add(2), *ptr.add(3), *ptr.add(4), *ptr.add(5), *ptr.add(6), *ptr.add(7), *ptr.add(8), *ptr.add(9), *ptr.add(10), *ptr.add(11), *ptr.add(12), *ptr.add(13), *ptr.add(14), *ptr.add(15), ] } etherparse-0.13.0/src/link/ethernet.rs000064400000000000000000000174131046102023000157650ustar 00000000000000use super::super::*; use std::slice::from_raw_parts; use std::io; /// Ether type enum present in ethernet II header. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum EtherType { Ipv4 = 0x0800, Ipv6 = 0x86dd, Arp = 0x0806, WakeOnLan = 0x0842, VlanTaggedFrame = 0x8100, ProviderBridging = 0x88A8, VlanDoubleTaggedFrame = 0x9100 } impl EtherType { ///Tries to convert a raw ether type value to the enum. Returns None if the value does not exist in the enum. pub fn from_u16(value: u16) -> Option { use self::EtherType::*; match value { 0x0800 => Some(Ipv4), 0x86dd => Some(Ipv6), 0x0806 => Some(Arp), 0x0842 => Some(WakeOnLan), 0x88A8 => Some(ProviderBridging), 0x8100 => Some(VlanTaggedFrame), 0x9100 => Some(VlanDoubleTaggedFrame), _ => None } } } /// `u16` constants for the most used `ether_type` values. /// /// `ether_type` values are used in the Ethernet II header and the /// vlan headers to identify the next header type. /// /// The constants are equivalent if values of the enum type [`EtherType`] get cast /// to a u16 value. /// /// ``` /// use etherparse::{ether_type, EtherType}; /// /// assert_eq!(ether_type::IPV4, EtherType::Ipv4 as u16); /// ``` pub mod ether_type { use crate::EtherType::*; pub const IPV4: u16 = Ipv4 as u16; pub const IPV6: u16 = Ipv6 as u16; pub const ARP: u16 = Arp as u16; pub const WAKE_ON_LAN: u16 = WakeOnLan as u16; pub const VLAN_TAGGED_FRAME: u16 = VlanTaggedFrame as u16; pub const PROVIDER_BRIDGING: u16 = ProviderBridging as u16; pub const VLAN_DOUBLE_TAGGED_FRAME: u16 = VlanDoubleTaggedFrame as u16; } ///Ethernet II header. #[derive(Clone, Debug, Eq, PartialEq, Default)] pub struct Ethernet2Header { pub source: [u8;6], pub destination: [u8;6], pub ether_type: u16 } impl SerializedSize for Ethernet2Header { ///Serialized size of the header in bytes. const SERIALIZED_SIZE: usize = 14; } impl Ethernet2Header { /// Creates a ethernet slice from an other slice. #[deprecated( since = "0.10.1", note = "Use Ethernet2Header::from_slice instead." )] #[inline] pub fn read_from_slice(slice: &[u8]) -> Result<(Ethernet2Header, &[u8]), ReadError> { Ethernet2Header::from_slice(slice) } /// Read an Ethernet2Header from a slice and return the header & unused parts of the slice. #[inline] pub fn from_slice(slice: &[u8]) -> Result<(Ethernet2Header, &[u8]), ReadError> { Ok(( Ethernet2HeaderSlice::from_slice(slice)?.to_header(), &slice[Ethernet2Header::SERIALIZED_SIZE..] )) } /// Read an Ethernet2Header from a static sized byte array. #[inline] pub fn from_bytes(bytes: [u8;14]) -> Ethernet2Header { Ethernet2Header{ destination: [ bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], ], source: [ bytes[6], bytes[7], bytes[8], bytes[9], bytes[10], bytes[11], ], ether_type: u16::from_be_bytes( [ bytes[12], bytes[13], ] ) } } /// Reads an Ethernet-II header from the current position of the read argument. pub fn read(reader: &mut T) -> Result { let buffer = { let mut buffer = [0;Ethernet2Header::SERIALIZED_SIZE]; reader.read_exact(&mut buffer)?; buffer }; Ok(Ethernet2HeaderSlice{ slice: &buffer }.to_header()) } /// Serialize the header to a given slice. Returns the unused part of the slice. pub fn write_to_slice<'a>(&self, slice: &'a mut [u8]) -> Result<&'a mut [u8], WriteError> { use self::WriteError::*; //length check if slice.len() < Ethernet2Header::SERIALIZED_SIZE { Err(SliceTooSmall(Ethernet2Header::SERIALIZED_SIZE)) } else { slice[..Ethernet2Header::SERIALIZED_SIZE].copy_from_slice(&self.to_bytes()); Ok(&mut slice[Ethernet2Header::SERIALIZED_SIZE..]) } } /// Writes a given Ethernet-II header to the current position of the write argument. #[inline] pub fn write(&self, writer: &mut T) -> Result<(), io::Error> { writer.write_all(&self.to_bytes()) } /// Length of the serialized header in bytes. #[inline] pub fn header_len(&self) -> usize { 14 } /// Returns the serialized form of the header as a statically /// sized byte array. #[inline] pub fn to_bytes(&self) -> [u8;14] { let ether_type_be = self.ether_type.to_be_bytes(); [ self.destination[0], self.destination[1], self.destination[2], self.destination[3], self.destination[4], self.destination[5], self.source[0], self.source[1], self.source[2], self.source[3], self.source[4], self.source[5], ether_type_be[0], ether_type_be[1], ] } } ///A slice containing an ethernet 2 header of a network package. #[derive(Clone, Debug, Eq, PartialEq)] pub struct Ethernet2HeaderSlice<'a> { slice: &'a [u8] } impl<'a> Ethernet2HeaderSlice<'a> { /// Creates a ethernet slice from an other slice. pub fn from_slice(slice: &'a[u8]) -> Result, ReadError>{ //check length use crate::ReadError::*; if slice.len() < Ethernet2Header::SERIALIZED_SIZE { return Err(UnexpectedEndOfSlice(Ethernet2Header::SERIALIZED_SIZE)); } //all done Ok(Ethernet2HeaderSlice { // SAFETY: // Safe as slice length is checked to be at least // Ethernet2Header::SERIALIZED_SIZE (14) before this. slice: unsafe { from_raw_parts( slice.as_ptr(), Ethernet2Header::SERIALIZED_SIZE ) } }) } /// Returns the slice containing the ethernet 2 header #[inline] pub fn slice(&self) -> &'a [u8] { self.slice } /// Read the destination mac address #[inline] pub fn destination(&self) -> [u8;6] { // SAFETY: // Safe as the contructor checks that the slice has // at least the length of Ethernet2Header::SERIALIZED_SIZE (14). unsafe { get_unchecked_6_byte_array(self.slice.as_ptr()) } } /// Read the source mac address #[inline] pub fn source(&self) -> [u8;6] { // SAFETY: // Safe as the contructor checks that the slice has // at least the length of Ethernet2Header::SERIALIZED_SIZE (14). unsafe { get_unchecked_6_byte_array(self.slice.as_ptr().add(6)) } } /// Read the ether_type field of the header (in system native byte order). #[inline] pub fn ether_type(&self) -> u16 { // SAFETY: // Safe as the contructor checks that the slice has // at least the length of Ethernet2Header::SERIALIZED_SIZE (14). unsafe { get_unchecked_be_u16(self.slice.as_ptr().add(12)) } } /// Decode all the fields and copy the results to a Ipv4Header struct pub fn to_header(&self) -> Ethernet2Header { Ethernet2Header { source: self.source(), destination: self.destination(), ether_type: self.ether_type() } } }etherparse-0.13.0/src/link/mod.rs000064400000000000000000000011611046102023000147170ustar 00000000000000pub mod ethernet; pub mod vlan_tagging; /// A slice containing the link layer header (currently only Ethernet II is supported). #[derive(Clone, Debug, Eq, PartialEq)] pub enum LinkSlice<'a> { /// A slice containing an Ethernet II header. Ethernet2(ethernet::Ethernet2HeaderSlice<'a>) } impl<'a> LinkSlice<'a> { /// Convert the link slice to a header (currently just the /// ethernet2 header as this is the only value it can take). pub fn to_header(&self) -> ethernet::Ethernet2Header { use LinkSlice::*; match self { Ethernet2(slice) => slice.to_header(), } } } etherparse-0.13.0/src/link/vlan_tagging.rs000064400000000000000000000352331046102023000166070ustar 00000000000000use super::super::*; use std::io; use std::slice::from_raw_parts; /// IEEE 802.1Q VLAN Tagging Header (can be single or double tagged). #[derive(Clone, Debug, Eq, PartialEq)] pub enum VlanHeader { /// IEEE 802.1Q VLAN Tagging Header Single(SingleVlanHeader), /// IEEE 802.1Q double VLAN Tagging Header Double(DoubleVlanHeader) } impl VlanHeader { /// All ether types that identify a vlan header. pub const VLAN_ETHER_TYPES: [u16;3] = [ ether_type::VLAN_TAGGED_FRAME, ether_type::PROVIDER_BRIDGING, ether_type::VLAN_DOUBLE_TAGGED_FRAME, ]; /// Write the IEEE 802.1Q VLAN single or double tagging header #[inline] pub fn write(&self, writer: &mut T) -> Result<(), WriteError> { use VlanHeader::*; match &self { Single(header) => header.write(writer), Double(header) => header.write(writer), } } /// Length of the serialized header(s) in bytes. #[inline] pub fn header_len(&self) -> usize { use VlanHeader::*; match &self { Single(_) => SingleVlanHeader::SERIALIZED_SIZE, Double(_) => DoubleVlanHeader::SERIALIZED_SIZE, } } } /// A slice containing a single or double vlan header. #[derive(Clone, Debug, Eq, PartialEq)] pub enum VlanSlice<'a> { SingleVlan(SingleVlanHeaderSlice<'a>), DoubleVlan(DoubleVlanHeaderSlice<'a>), } impl<'a> VlanSlice<'a> { /// Decode all the fields and copy the results to a VlanHeader struct #[inline] pub fn to_header(&self) -> VlanHeader { use crate::VlanHeader::*; use crate::VlanSlice::*; match self { SingleVlan(value) => Single(value.to_header()), DoubleVlan(value) => Double(value.to_header()) } } } /// IEEE 802.1Q VLAN Tagging Header #[derive(Clone, Debug, Eq, PartialEq, Default)] pub struct SingleVlanHeader { /// A 3 bit number which refers to the IEEE 802.1p class of service and maps to the frame priority level. pub priority_code_point: u8, /// Indicate that the frame may be dropped under the presence of congestion. pub drop_eligible_indicator: bool, /// 12 bits vland identifier. pub vlan_identifier: u16, /// "Tag protocol identifier": Type id of content after this header. Refer to the "EtherType" for a list of possible supported values. pub ether_type: u16, } impl SerializedSize for SingleVlanHeader { /// Serialized size of the header in bytes. const SERIALIZED_SIZE: usize = 4; } impl SingleVlanHeader { /// Read an SingleVlanHeader from a slice and return the header & unused parts of the slice. #[deprecated( since = "0.10.1", note = "Use SingleVlanHeader::from_slice instead." )] #[inline] pub fn read_from_slice(slice: &[u8]) -> Result<(SingleVlanHeader, &[u8]), ReadError> { SingleVlanHeader::from_slice(slice) } /// Read an SingleVlanHeader from a slice and return the header & unused parts of the slice. #[inline] pub fn from_slice(slice: &[u8]) -> Result<(SingleVlanHeader, &[u8]), ReadError> { Ok(( SingleVlanHeaderSlice::from_slice(slice)?.to_header(), &slice[SingleVlanHeader::SERIALIZED_SIZE .. ] )) } /// Read an SingleVlanHeader from a static sized byte array. #[inline] pub fn from_bytes(bytes: [u8;4]) -> SingleVlanHeader { SingleVlanHeader{ priority_code_point: (bytes[0] >> 5) & 0b0000_0111u8, drop_eligible_indicator: 0 != (bytes[0] & 0b0001_0000u8), vlan_identifier: u16::from_be_bytes( [ bytes[0] & 0b0000_1111u8, bytes[1] ] ), ether_type: u16::from_be_bytes( [ bytes[2], bytes[3], ] ), } } /// Read a IEEE 802.1Q VLAN tagging header pub fn read(reader: &mut T) -> Result { let buffer = { let mut buffer : [u8; SingleVlanHeader::SERIALIZED_SIZE] = [0;SingleVlanHeader::SERIALIZED_SIZE]; reader.read_exact(&mut buffer)?; buffer }; Ok(SingleVlanHeaderSlice{ slice: &buffer }.to_header()) } /// Write the IEEE 802.1Q VLAN tagging header #[inline] pub fn write(&self, writer: &mut T) -> Result<(), WriteError> { writer.write_all(&self.to_bytes()?)?; Ok(()) } /// Length of the serialized header in bytes. #[inline] pub fn header_len(&self) -> usize { 4 } /// Returns the serialized form of the header or an value error in case /// the header values are outside of range. #[inline] pub fn to_bytes(&self) -> Result<[u8;4], ValueError> { use crate::ErrorField::*; // check value ranges max_check_u8(self.priority_code_point, 0x7, VlanTagPriorityCodePoint)?; max_check_u16(self.vlan_identifier, 0xfff, VlanTagVlanId)?; // serialize let id_be = self.vlan_identifier.to_be_bytes(); let eth_type_be = self.ether_type.to_be_bytes(); Ok( [ ( if self.drop_eligible_indicator { id_be[0] | 0x10 } else { id_be[0] } | (self.priority_code_point << 5) ), id_be[1], eth_type_be[0], eth_type_be[1] ] ) } } /// IEEE 802.1Q double VLAN Tagging Header #[derive(Clone, Debug, Eq, PartialEq)] pub struct DoubleVlanHeader { /// The outer vlan tagging header pub outer: SingleVlanHeader, /// The inner vlan tagging header pub inner: SingleVlanHeader } impl SerializedSize for DoubleVlanHeader { /// Serialized size of the header in bytes. const SERIALIZED_SIZE: usize = 8; } impl DoubleVlanHeader { /// Read an DoubleVlanHeader from a slice and return the header & unused parts of the slice. #[deprecated( since = "0.10.1", note = "Use SingleVlanHeader::from_slice instead." )] #[inline] pub fn read_from_slice(slice: &[u8]) -> Result<(DoubleVlanHeader, &[u8]), ReadError> { DoubleVlanHeader::from_slice(slice) } /// Read an DoubleVlanHeader from a slice and return the header & unused parts of the slice. #[inline] pub fn from_slice(slice: &[u8]) -> Result<(DoubleVlanHeader, &[u8]), ReadError> { Ok(( DoubleVlanHeaderSlice::from_slice(slice)?.to_header(), &slice[DoubleVlanHeader::SERIALIZED_SIZE .. ] )) } /// Read a double tagging header from the given source pub fn read(reader: &mut T) -> Result { let outer = SingleVlanHeader::read(reader)?; use crate::ether_type::{ VLAN_TAGGED_FRAME, PROVIDER_BRIDGING, VLAN_DOUBLE_TAGGED_FRAME }; //check that outer ethertype is matching match outer.ether_type { VLAN_TAGGED_FRAME | PROVIDER_BRIDGING | VLAN_DOUBLE_TAGGED_FRAME => { Ok(DoubleVlanHeader{ outer, inner: SingleVlanHeader::read(reader)? }) }, value => { use crate::ReadError::*; Err(DoubleVlanOuterNonVlanEtherType(value)) } } } /// Write the double IEEE 802.1Q VLAN tagging header pub fn write(&self, writer: &mut T) -> Result<(), WriteError> { self.outer.write(writer)?; self.inner.write(writer) } /// Length of the serialized headers in bytes. #[inline] pub fn header_len(&self) -> usize { 8 } /// Returns the serialized form of the headers or an value error in case /// the headers contain values that are outside of range. #[inline] pub fn to_bytes(&self) -> Result<[u8;8], ValueError> { let outer = self.outer.to_bytes()?; let inner = self.inner.to_bytes()?; Ok( [ outer[0], outer[1], outer[2], outer[3], inner[0], inner[1], inner[2], inner[3], ] ) } } impl Default for DoubleVlanHeader { fn default() -> Self { DoubleVlanHeader { outer: SingleVlanHeader { priority_code_point: 0, drop_eligible_indicator: false, vlan_identifier: 0, ether_type: ether_type::VLAN_TAGGED_FRAME, }, inner: Default::default() } } } ///A slice containing a single vlan header of a network package. #[derive(Clone, Debug, Eq, PartialEq)] pub struct SingleVlanHeaderSlice<'a> { slice: &'a [u8] } impl<'a> SingleVlanHeaderSlice<'a> { ///Creates a vlan header slice from a slice. #[inline] pub fn from_slice(slice: &'a[u8]) -> Result, ReadError>{ //check length use crate::ReadError::*; if slice.len() < SingleVlanHeader::SERIALIZED_SIZE { return Err(UnexpectedEndOfSlice(SingleVlanHeader::SERIALIZED_SIZE)); } //all done Ok(SingleVlanHeaderSlice::<'a> { // SAFETY: // Safe as the slice length is checked beforehand to have // at least the length of SingleVlanHeader::SERIALIZED_SIZE (4) slice: unsafe { from_raw_parts( slice.as_ptr(), SingleVlanHeader::SERIALIZED_SIZE ) } }) } /// Returns the slice containing the single vlan header #[inline] pub fn slice(&self) -> &'a [u8] { self.slice } /// Read the "priority_code_point" field from the slice. This is a 3 bit number which refers to the IEEE 802.1p class of service and maps to the frame priority level. #[inline] pub fn priority_code_point(&self) -> u8 { // SAFETY: // Slice len checked in constructor to be at least 4. unsafe { *self.slice.get_unchecked(0) >> 5 } } /// Read the "drop_eligible_indicator" flag from the slice. Indicates that the frame may be dropped under the presence of congestion. #[inline] pub fn drop_eligible_indicator(&self) -> bool { // SAFETY: // Slice len checked in constructor to be at least 4. unsafe { 0 != (*self.slice.get_unchecked(0) & 0x10) } } /// Reads the 12 bits "vland identifier" field from the slice. #[inline] pub fn vlan_identifier(&self) -> u16 { u16::from_be_bytes( // SAFETY: // Slice len checked in constructor to be at least 4. unsafe { [ *self.slice.get_unchecked(0) & 0xf, *self.slice.get_unchecked(1) ] } ) } /// Read the "Tag protocol identifier" field from the slice. Refer to the "EtherType" for a list of possible supported values. #[inline] pub fn ether_type(&self) -> u16 { // SAFETY: // Slice len checked in constructor to be at least 4. unsafe { get_unchecked_be_u16(self.slice.as_ptr().add(2)) } } /// Decode all the fields and copy the results to a SingleVlanHeader struct #[inline] pub fn to_header(&self) -> SingleVlanHeader { SingleVlanHeader { priority_code_point: self.priority_code_point(), drop_eligible_indicator: self.drop_eligible_indicator(), vlan_identifier: self.vlan_identifier(), ether_type: self.ether_type(), } } } /// A slice containing an double vlan header of a network package. #[derive(Clone, Debug, Eq, PartialEq)] pub struct DoubleVlanHeaderSlice<'a> { slice: &'a [u8] } impl<'a> DoubleVlanHeaderSlice<'a> { /// Creates a double header slice from a slice. pub fn from_slice(slice: &'a[u8]) -> Result, ReadError>{ // check length use crate::ReadError::*; if slice.len() < DoubleVlanHeader::SERIALIZED_SIZE { return Err(UnexpectedEndOfSlice(DoubleVlanHeader::SERIALIZED_SIZE)); } // create slice let result = DoubleVlanHeaderSlice { // SAFETY: // Safe as the slice length is checked is before to have // at least the length of DoubleVlanHeader::SERIALIZED_SIZE (8) slice: unsafe { from_raw_parts( slice.as_ptr(), DoubleVlanHeader::SERIALIZED_SIZE, ) } }; use ether_type::*; //check that outer ethertype is matching match result.outer().ether_type() { VLAN_TAGGED_FRAME | PROVIDER_BRIDGING | VLAN_DOUBLE_TAGGED_FRAME => { //all done Ok(result) }, value => { Err(DoubleVlanOuterNonVlanEtherType(value)) } } } /// Returns the slice containing the double vlan header #[inline] pub fn slice(&self) -> &'a [u8] { self.slice } /// Returns a slice with the outer vlan header #[inline] pub fn outer(&self) -> SingleVlanHeaderSlice<'a> { SingleVlanHeaderSlice::<'a> { // SAFETY: // Safe as the constructor checks that the slice has the length // of DoubleVlanHeader::SERIALIZED_SIZE (8) and the // SingleVlanHeader::SERIALIZED_SIZE has a size of 4. slice: unsafe { from_raw_parts( self.slice.as_ptr(), SingleVlanHeader::SERIALIZED_SIZE ) } } } /// Returns a slice with the inner vlan header. #[inline] pub fn inner(&self) -> SingleVlanHeaderSlice<'a> { SingleVlanHeaderSlice::<'a> { // SAFETY: // Safe as the constructor checks that the slice has the length // of DoubleVlanHeader::SERIALIZED_SIZE (8) and the // SingleVlanHeader::SERIALIZED_SIZE has a size of 4. slice: unsafe { from_raw_parts( self.slice.as_ptr().add(SingleVlanHeader::SERIALIZED_SIZE), SingleVlanHeader::SERIALIZED_SIZE ) } } } /// Decode all the fields and copy the results to a DoubleVlanHeader struct pub fn to_header(&self) -> DoubleVlanHeader { DoubleVlanHeader { outer: self.outer().to_header(), inner: self.inner().to_header() } } }etherparse-0.13.0/src/packet_builder.rs000064400000000000000000001525751046102023000162000ustar 00000000000000use super::*; use std::{io, marker}; /// Helper for building packets. /// /// The packet builder allows the easy construction of a packet from the /// ethernet II layer downwards including ipv6, ipv4, the udp header and the /// actual payload. The packet builder automatically calculates lengths & checksums /// for ip & udp and set type identifiers for ethernetII and ip. This makes it /// easy and less error prone to construct custom packets. /// /// # Example: /// /// Generating a packet that starts with an Ethernet II header: /// /// ``` /// use etherparse::PacketBuilder; /// /// let builder = PacketBuilder:: /// ethernet2([1,2,3,4,5,6], //source mac /// [7,8,9,10,11,12]) //destionation mac /// .ipv4([192,168,1,1], //source ip /// [192,168,1,2], //desitionation ip /// 20) //time to life /// .udp(21, //source port /// 1234); //desitnation port /// /// //payload of the udp packet /// let payload = [1,2,3,4,5,6,7,8]; /// /// //get some memory to store the result /// let mut result = Vec::::with_capacity( /// builder.size(payload.len())); /// /// //serialize /// builder.write(&mut result, &payload).unwrap(); /// println!("{:?}", result); /// ``` /// /// # Options /// /// * Starting Options: /// * [`PacketBuilder::ethernet2`] /// * [`PacketBuilder::ip`] /// * [`PacketBuilder::ipv4`] /// * [`PacketBuilder::ipv6`] /// * Options after an Ethernet2 header was added: /// * [`PacketBuilderStep::vlan`] /// * [`PacketBuilderStep::single_vlan`] /// * [`PacketBuilderStep::double_vlan`] /// * [`PacketBuilderStep::ip`] /// * [`PacketBuilderStep::ipv4`] /// * [`PacketBuilderStep::ipv6`] /// * Options after an Vlan header was added: /// * [`PacketBuilderStep::ip`] /// * [`PacketBuilderStep::ipv4`] /// * [`PacketBuilderStep::ipv6`] /// * Options after an IP header was added: /// * [`PacketBuilderStep::write`] /// * [`PacketBuilderStep::tcp`] /// * [`PacketBuilderStep::udp`] /// * [`PacketBuilderStep::icmpv4`] /// * [`PacketBuilderStep::icmpv4_raw`] /// * [`PacketBuilderStep::icmpv4_echo_request`] /// * [`PacketBuilderStep::icmpv4_echo_reply`] /// * [`PacketBuilderStep::icmpv6`] /// * [`PacketBuilderStep::icmpv6_raw`] /// * [`PacketBuilderStep::icmpv6_echo_request`] /// * [`PacketBuilderStep::icmpv6_echo_reply`] /// * Options after an TCP header was added: /// * [`PacketBuilderStep::write`] /// * [`PacketBuilderStep::size`] /// * [`PacketBuilderStep::ns`] /// * [`PacketBuilderStep::fin`] /// * [`PacketBuilderStep::syn`] /// * [`PacketBuilderStep::rst`] /// * [`PacketBuilderStep::psh`] /// * [`PacketBuilderStep::ack`] /// * [`PacketBuilderStep::urg`] /// * [`PacketBuilderStep::ece`] /// * [`PacketBuilderStep::cwr`] /// * [`PacketBuilderStep::options`] /// * [`PacketBuilderStep::options_raw`] /// * Options after an UDP header was added: /// * [`PacketBuilderStep::write`] /// * [`PacketBuilderStep::size`] /// * Options after an ICMPv4 header was added: /// * [`PacketBuilderStep::write`] /// * [`PacketBuilderStep::size`] /// * Options after an ICMPv6 header was added: /// * [`PacketBuilderStep::write`] /// * [`PacketBuilderStep::size`] /// pub struct PacketBuilder {} impl PacketBuilder { /// Start an packet with an ethernetII header. /// /// # Example /// /// Basic usage: /// /// ``` /// # use etherparse::PacketBuilder; /// # /// let builder = PacketBuilder:: /// ethernet2([1,2,3,4,5,6], //source mac /// [7,8,9,10,11,12]) //destionation mac /// .ipv4([192,168,1,1], //source ip /// [192,168,1,2], //desitionation ip /// 20) //time to life /// .udp(21, //source port /// 1234); //desitnation port /// /// //payload of the udp packet /// let payload = [1,2,3,4,5,6,7,8]; /// /// //get some memory to store the result /// let mut result = Vec::::with_capacity( /// builder.size(payload.len())); /// /// //serialize /// builder.write(&mut result, &payload).unwrap(); /// ``` pub fn ethernet2(source: [u8;6], destination: [u8;6]) -> PacketBuilderStep { PacketBuilderStep { state: PacketImpl { ethernet2_header: Some(Ethernet2Header{ source, destination, ether_type: 0 //the type identifier }), vlan_header: None, ip_header: None, transport_header: None }, _marker: marker::PhantomData::{} } } /// Starts a packet with an IPv4 header. /// /// # Example /// /// Basic usage: /// /// ``` /// # use etherparse::PacketBuilder; /// # /// let builder = PacketBuilder:: /// ipv4([192,168,1,1], //source ip /// [192,168,1,2], //desitionation ip /// 20) //time to life /// .udp(21, //source port /// 1234); //desitnation port /// /// //payload of the udp packet /// let payload = [1,2,3,4,5,6,7,8]; /// /// //get some memory to store the result /// let mut result = Vec::::with_capacity( /// builder.size(payload.len())); /// /// //serialize /// builder.write(&mut result, &payload).unwrap(); /// ``` pub fn ipv4(source: [u8;4], destination: [u8;4], time_to_live: u8) -> PacketBuilderStep { PacketBuilderStep { state: PacketImpl { ethernet2_header: None, vlan_header: None, ip_header: None, transport_header: None }, _marker: marker::PhantomData::{} }.ipv4(source, destination, time_to_live) } /// Start a packet with an IPv6 header. /// /// # Example /// /// Basic usage: /// /// ``` /// # use etherparse::PacketBuilder; /// # /// let builder = PacketBuilder:: /// ipv6( /// //source /// [11,12,13,14,15,16,17,18,19,10,21,22,23,24,25,26], /// //destination /// [31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46], /// //hop_limit /// 47) /// .udp(21, //source port /// 1234); //desitnation port /// /// //payload of the udp packet /// let payload = [1,2,3,4,5,6,7,8]; /// /// //get some memory to store the result /// let mut result = Vec::::with_capacity( /// builder.size(payload.len())); /// /// //serialize /// builder.write(&mut result, &payload).unwrap(); /// ``` pub fn ipv6(source: [u8;16], destination: [u8;16], hop_limit: u8) -> PacketBuilderStep { PacketBuilderStep { state: PacketImpl { ethernet2_header: None, vlan_header: None, ip_header: None, transport_header: None }, _marker: marker::PhantomData::{} }.ipv6(source, destination, hop_limit) } /// Starts a packet with an arbitrary IP header (length, protocol/next_header & checksum fields will be overwritten based on the rest of the packet). /// /// # Examples /// /// With an IPv4 header: /// /// ``` /// # use etherparse::*; /// # /// let builder = PacketBuilder:: /// //payload_len, protocol & checksum will be replaced during write /// ip(IpHeader::Version4( /// Ipv4Header::new( /// 0, //payload_len will be replaced during write /// 12, //time_to_live /// ip_number::UDP, //will be replaced during write /// [0,1,2,3], //source /// [4,5,6,7] //destination /// ), /// Default::default())) /// .udp(21, //source port /// 1234); //desitnation port /// /// //payload of the udp packet /// let payload = [1,2,3,4,5,6,7,8]; /// /// //get some memory to store the result /// let mut result = Vec::::with_capacity( /// builder.size(payload.len())); /// /// //serialize /// builder.write(&mut result, &payload).unwrap(); /// ``` /// /// With an IPv6 header: /// /// ``` /// # use etherparse::*; /// # /// let builder = PacketBuilder:: /// ip(IpHeader::Version6( /// Ipv6Header{ /// traffic_class: 0, /// flow_label: 0, /// payload_length: 0, //will be replaced during write /// next_header: 0, //will be replaced during write /// hop_limit: 4, /// source: [0;16], /// destination: [0;16] /// }, /// Default::default())) /// .udp(21, //source port /// 1234); //desitnation port /// /// //payload of the udp packet /// let payload = [1,2,3,4,5,6,7,8]; /// /// //get some memory to store the result /// let mut result = Vec::::with_capacity( /// builder.size(payload.len())); /// /// //serialize /// builder.write(&mut result, &payload).unwrap(); /// ``` pub fn ip(ip_header: IpHeader) -> PacketBuilderStep { PacketBuilderStep { state: PacketImpl { ethernet2_header: None, vlan_header: None, ip_header: None, transport_header: None }, _marker: marker::PhantomData::{} }.ip(ip_header) } } struct PacketImpl { ethernet2_header: Option, ip_header: Option, vlan_header: Option, transport_header: Option } ///An unfinished packet that is build with the packet builder pub struct PacketBuilderStep { state: PacketImpl, _marker: marker::PhantomData } impl PacketBuilderStep { /// Add an IPv4 header /// /// # Example /// /// Basic usage: /// /// ``` /// # use etherparse::PacketBuilder; /// # /// let builder = PacketBuilder:: /// ethernet2([1,2,3,4,5,6], //source mac /// [7,8,9,10,11,12]) //destionation mac /// .ipv4([192,168,1,1], //source ip /// [192,168,1,2], //desitionation ip /// 20) //time to life /// .udp(21, //source port /// 1234); //desitnation port /// /// //payload of the udp packet /// let payload = [1,2,3,4,5,6,7,8]; /// /// //get some memory to store the result /// let mut result = Vec::::with_capacity( /// builder.size(payload.len())); /// /// //serialize /// builder.write(&mut result, &payload).unwrap(); /// ``` pub fn ipv4(mut self, source: [u8;4], destination: [u8;4], time_to_live: u8) -> PacketBuilderStep { //add ip header self.state.ip_header = Some(IpHeader::Version4({ let mut value: Ipv4Header = Default::default(); value.source = source; value.destination = destination; value.time_to_live = time_to_live; value }, Default::default())); //return for next step PacketBuilderStep { state: self.state, _marker: marker::PhantomData::{} } } /// Add an IP header (length, protocol/next_header & checksum fields will be overwritten based on the rest of the packet). /// /// # Examples /// /// With an IPv4 header: /// /// ``` /// # use etherparse::*; /// # /// let builder = PacketBuilder:: /// ethernet2([1,2,3,4,5,6], /// [7,8,9,10,11,12]) /// //payload_len, protocol & checksum will be replaced during write /// .ip(IpHeader::Version4( /// Ipv4Header::new( /// 0, //payload_len will be replaced during write /// 12, //time_to_live /// ip_number::UDP, //will be replaced during write /// [0,1,2,3], //source /// [4,5,6,7] //destination /// ), /// Default::default())); /// ``` /// /// With an IPv6 header: /// /// ``` /// # use etherparse::*; /// # /// let builder = PacketBuilder:: /// ethernet2([1,2,3,4,5,6], /// [7,8,9,10,11,12]) /// .ip(IpHeader::Version6( /// Ipv6Header{ /// traffic_class: 0, /// flow_label: 0, /// payload_length: 0, //will be replaced during write /// next_header: 0, //will be replaced during write /// hop_limit: 4, /// source: [0;16], /// destination: [0;16] /// }, /// Default::default())); /// ``` pub fn ip(mut self, ip_header: IpHeader) -> PacketBuilderStep { //add ip header self.state.ip_header = Some(ip_header); //return for next step PacketBuilderStep { state: self.state, _marker: marker::PhantomData::{} } } /// Add an IPv6 header /// /// # Example /// /// Basic usage: /// /// ``` /// # use etherparse::PacketBuilder; /// # /// let builder = PacketBuilder:: /// ethernet2([1,2,3,4,5,6], /// [7,8,9,10,11,12]) /// .ipv6( /// //source /// [11,12,13,14,15,16,17,18,19,10,21,22,23,24,25,26], /// //destination /// [31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46], /// //hop_limit /// 47) /// .udp(21, //source port /// 1234); //desitnation port /// /// //payload of the udp packet /// let payload = [1,2,3,4,5,6,7,8]; /// /// //get some memory to store the result /// let mut result = Vec::::with_capacity( /// builder.size(payload.len())); /// /// //serialize /// builder.write(&mut result, &payload).unwrap(); /// ``` pub fn ipv6(mut self, source: [u8;16], destination: [u8;16], hop_limit: u8) -> PacketBuilderStep { self.state.ip_header = Some(IpHeader::Version6(Ipv6Header{ traffic_class: 0, flow_label: 0, payload_length: 0, //filled in on write next_header: 0, //filled in on write hop_limit, source, destination }, Default::default())); //return for next step PacketBuilderStep { state: self.state, _marker: marker::PhantomData::{} } } /// Adds a vlan tagging header with the given vlan identifier /// /// # Example /// /// Basic usage: /// /// ``` /// # use etherparse::{PacketBuilder, SingleVlanHeader, VlanHeader}; /// # /// let builder = PacketBuilder:: /// ethernet2([1,2,3,4,5,6], //source mac /// [7,8,9,10,11,12]) //destionation mac /// .vlan(VlanHeader::Single( /// SingleVlanHeader{ /// priority_code_point: 0, /// drop_eligible_indicator: false, /// vlan_identifier: 0x123, /// ether_type: 0 // will be overwritten during write /// })) /// .ipv4([192,168,1,1], //source ip /// [192,168,1,2], //desitionation ip /// 20) //time to life /// .udp(21, //source port /// 1234); //desitnation port /// /// //payload of the udp packet /// let payload = [1,2,3,4,5,6,7,8]; /// /// //get some memory to store the result /// let mut result = Vec::::with_capacity( /// builder.size(payload.len())); /// /// //serialize /// builder.write(&mut result, &payload).unwrap(); /// ``` pub fn vlan(mut self, vlan: VlanHeader) -> PacketBuilderStep { self.state.vlan_header = Some(vlan); //return for next step PacketBuilderStep { state: self.state, _marker: marker::PhantomData::{} } } /// Adds a vlan tagging header with the given vlan identifier /// /// # Example /// /// Basic usage: /// /// ``` /// # use etherparse::{PacketBuilder, SingleVlanHeader, VlanHeader}; /// # /// let builder = PacketBuilder:: /// ethernet2([1,2,3,4,5,6], //source mac /// [7,8,9,10,11,12]) //destionation mac /// .single_vlan(0x123) // vlan identifier /// .ipv4([192,168,1,1], //source ip /// [192,168,1,2], //desitionation ip /// 20) //time to life /// .udp(21, //source port /// 1234); //desitnation port /// /// //payload of the udp packet /// let payload = [1,2,3,4,5,6,7,8]; /// /// //get some memory to store the result /// let mut result = Vec::::with_capacity( /// builder.size(payload.len())); /// /// //serialize /// builder.write(&mut result, &payload).unwrap(); /// ``` pub fn single_vlan(mut self, vlan_identifier: u16) -> PacketBuilderStep { self.state.vlan_header = Some(VlanHeader::Single(SingleVlanHeader { priority_code_point: 0, drop_eligible_indicator: false, vlan_identifier, ether_type: 0, //will be set automatically during write })); //return for next step PacketBuilderStep { state: self.state, _marker: marker::PhantomData::{} } } /// Adds two vlan tagging header with the given vlan identifiers (also known as double vlan tagging). /// /// # Example /// /// Basic usage: /// /// ``` /// # use etherparse::{PacketBuilder, SingleVlanHeader, VlanHeader}; /// # /// let builder = PacketBuilder:: /// ethernet2([1,2,3,4,5,6], //source mac /// [7,8,9,10,11,12]) //destionation mac /// .double_vlan(0x123, // outer vlan identifier /// 0x234) // inner vlan identifier /// .ipv4([192,168,1,1], //source ip /// [192,168,1,2], //desitionation ip /// 20) //time to life /// .udp(21, //source port /// 1234); //desitnation port /// /// //payload of the udp packet /// let payload = [1,2,3,4,5,6,7,8]; /// /// //get some memory to store the result /// let mut result = Vec::::with_capacity( /// builder.size(payload.len())); /// /// //serialize /// builder.write(&mut result, &payload).unwrap(); /// ``` pub fn double_vlan(mut self, outer_vlan_identifier: u16, inner_vlan_identifier: u16) -> PacketBuilderStep { self.state.vlan_header = Some(VlanHeader::Double(DoubleVlanHeader { outer: SingleVlanHeader { priority_code_point: 0, drop_eligible_indicator: false, vlan_identifier: outer_vlan_identifier, ether_type: 0, //will be set automatically during write }, inner: SingleVlanHeader { priority_code_point: 0, drop_eligible_indicator: false, vlan_identifier: inner_vlan_identifier, ether_type: 0, //will be set automatically during write } })); //return for next step PacketBuilderStep { state: self.state, _marker: marker::PhantomData::{} } } } impl PacketBuilderStep { ///Add an ip header (length, protocol/next_header & checksum fields will be overwritten based on the rest of the packet). /// /// # Example IPv4 /// ``` /// # use etherparse::*; /// # /// let builder = PacketBuilder:: /// ethernet2([1,2,3,4,5,6], /// [7,8,9,10,11,12]) /// .single_vlan(0x132) /// //payload_len, protocol & checksum will be replaced during write /// .ip(IpHeader::Version4( /// Ipv4Header::new( /// 0, //payload_len will be replaced during write /// 12, //time_to_live /// ip_number::UDP, //will be replaced during write /// [0,1,2,3], //source /// [4,5,6,7] //destination /// ), /// Default::default() // IPv4 extension headers (default is none) /// )); /// ``` /// /// # Example IPv6 /// ``` /// # use etherparse::*; /// # /// let builder = PacketBuilder:: /// ethernet2([1,2,3,4,5,6], /// [7,8,9,10,11,12]) /// .single_vlan(0x132) /// .ip(IpHeader::Version6( /// Ipv6Header{ /// traffic_class: 0, /// flow_label: 0, /// payload_length: 0, //will be replaced during write /// next_header: 0, //will be replaced during write /// hop_limit: 4, /// source: [0;16], /// destination: [0;16] /// }, /// Default::default() // IPv6 extension headers (default is none) /// )); /// ``` pub fn ip(self, ip_header: IpHeader) -> PacketBuilderStep { //use the method from the Ethernet2Header implementation PacketBuilderStep { state: self.state, _marker: marker::PhantomData::{} }.ip(ip_header) } /// Add an IPv6 header /// /// # Example /// /// Basic usage: /// /// ``` /// # use etherparse::{PacketBuilder, SingleVlanHeader, VlanHeader}; /// # /// let builder = PacketBuilder:: /// ethernet2([1,2,3,4,5,6], //source mac /// [7,8,9,10,11,12]) //destionation mac /// .single_vlan(0x123) // vlan identifier /// .ipv6( /// //source /// [11,12,13,14,15,16,17,18,19,10,21,22,23,24,25,26], /// //destination /// [31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46], /// //hop_limit /// 47) /// .udp(21, //source port /// 1234); //desitnation port /// /// //payload of the udp packet /// let payload = [1,2,3,4,5,6,7,8]; /// /// //get some memory to store the result /// let mut result = Vec::::with_capacity( /// builder.size(payload.len())); /// /// //serialize /// builder.write(&mut result, &payload).unwrap(); /// ``` pub fn ipv6(self, source: [u8;16], destination: [u8;16], hop_limit: u8) -> PacketBuilderStep { //use the method from the Ethernet2Header implementation PacketBuilderStep { state: self.state, _marker: marker::PhantomData::{} }.ipv6(source, destination, hop_limit) } /// Add an IPv4 header /// /// # Example /// /// Basic usage: /// /// ``` /// # use etherparse::{PacketBuilder, SingleVlanHeader, VlanHeader}; /// # /// let builder = PacketBuilder:: /// ethernet2([1,2,3,4,5,6], //source mac /// [7,8,9,10,11,12]) //destionation mac /// .single_vlan(0x123) // vlan identifier /// .ipv4([192,168,1,1], //source ip /// [192,168,1,2], //desitionation ip /// 20) //time to life /// .udp(21, //source port /// 1234); //desitnation port /// /// //payload of the udp packet /// let payload = [1,2,3,4,5,6,7,8]; /// /// //get some memory to store the result /// let mut result = Vec::::with_capacity( /// builder.size(payload.len())); /// /// //serialize /// builder.write(&mut result, &payload).unwrap(); /// ``` pub fn ipv4(self, source: [u8;4], destination: [u8;4], time_to_live: u8) -> PacketBuilderStep { //use the method from the Ethernet2Header implementation PacketBuilderStep { state: self.state, _marker: marker::PhantomData::{} }.ipv4(source, destination, time_to_live) } } impl PacketBuilderStep { /// Adds an ICMPv4 header of the given [`Icmpv4Type`] to the packet. /// /// If an ICMPv4 header gets added the payload used during the builders `write` /// call contains the bytes after the header and has different meanings /// and contents based on the type. Ususally all statically sized values /// known based on the ICMPv4 type & code are part of the header and the /// payload is used to store contains the dynamic parts of the ICMPv4 packet. /// /// Check [`Icmpv4Type`] for a documentation which values are part of the /// header and what is stored as part of the payload. /// /// # Example /// /// Basic usage: /// /// ``` /// # use etherparse::{PacketBuilder, Icmpv4Type, icmpv4}; /// # /// let builder = PacketBuilder:: /// ipv4([192,168,1,1], //source ip /// [192,168,1,2], //desitionation ip /// 20) //time to life /// .icmpv4( /// Icmpv4Type::TimeExceeded( /// icmpv4::TimeExceededCode::TtlExceededInTransit /// ) /// ); /// /// // what is part of the payload depends on the Icmpv4Type /// // /// // In case of `Icmpv4Type::TimeExceeded` the "internet header /// // + 64 bits of the original data datagram" should be given as /// // the payload /// let payload = [1,2,3,4,5,6,7,8]; /// /// //get some memory to store the result /// let mut result = Vec::::with_capacity( /// builder.size(payload.len())); /// /// //serialize /// builder.write(&mut result, &payload).unwrap(); /// ``` pub fn icmpv4(mut self, icmp_type: Icmpv4Type) -> PacketBuilderStep { self.state.transport_header = Some(TransportHeader::Icmpv4(Icmpv4Header{ icmp_type, checksum: 0, // calculated later })); //return for next step PacketBuilderStep { state: self.state, _marker: marker::PhantomData::{} } } /// Adds an ICMPv4 header based on raw numbers. /// /// This can be usefull when trying to build an ICMPv4 packet /// which is not fully supported by etherparse and is the equivalent /// of using [`Icmpv4Type::Unknown`] together with /// [`PacketBuilderStep::icmpv4`]. /// /// # Example /// /// Basic usage: /// /// ``` /// # use etherparse::PacketBuilder; /// # /// let builder = PacketBuilder:: /// ipv4([192,168,1,1], //source ip /// [192,168,1,2], //desitionation ip /// 20) //time to life /// .icmpv4_raw( /// 253, // ICMPv4 type (e.g. 253 is RFC3692-style Experiment 1) /// 0, // ICMPv4 code /// [1,2,3,4] // bytes 5-8 in the ICMPv4 header /// ); /// /// // the payload is written after the 8 byte raw ICMPv4 header /// let payload = [1,2,3,4,5,6,7,8]; /// /// // get some memory to store the result /// let mut result = Vec::::with_capacity( /// builder.size(payload.len())); /// /// // serialize /// builder.write(&mut result, &payload).unwrap(); /// ``` pub fn icmpv4_raw(mut self, type_u8: u8, code_u8: u8, bytes5to8: [u8;4]) -> PacketBuilderStep { let icmp_type = Icmpv4Type::Unknown{type_u8, code_u8, bytes5to8}; self.state.transport_header = Some(TransportHeader::Icmpv4(Icmpv4Header{ icmp_type, checksum: 0, // calculated later })); //return for next step PacketBuilderStep { state: self.state, _marker: marker::PhantomData::{} } } /// Adds an ICMPv4 echo request packet. /// /// # Example /// /// Basic usage: /// /// ``` /// # use etherparse::PacketBuilder; /// # /// let builder = PacketBuilder:: /// ipv4([192,168,1,1], //source ip /// [192,168,1,2], //desitionation ip /// 20) //time to life /// .icmpv4_echo_request( /// 123, // identifier /// 456, // sequence number /// ); /// /// // payload of the echo request /// let payload = [1,2,3,4,5,6,7,8]; /// /// // get some memory to store the result /// let mut result = Vec::::with_capacity( /// builder.size(payload.len())); /// /// // serialize /// builder.write(&mut result, &payload).unwrap(); /// ``` pub fn icmpv4_echo_request(mut self, id: u16, seq: u16) -> PacketBuilderStep { let echo_header = IcmpEchoHeader{ id, seq, }; let icmpv4_echo = Icmpv4Header::new(Icmpv4Type::EchoRequest(echo_header)); self.state.transport_header = Some(TransportHeader::Icmpv4(icmpv4_echo)); //return for next step PacketBuilderStep { state: self.state, _marker: marker::PhantomData::{} } } /// Adds an ICMPv4 echo reply packet. /// /// # Example /// /// Basic usage: /// /// ``` /// # use etherparse::PacketBuilder; /// # /// let builder = PacketBuilder:: /// ipv4([192,168,1,1], //source ip /// [192,168,1,2], //desitionation ip /// 20) //time to life /// .icmpv4_echo_reply( /// 123, // identifier /// 456, // sequence number /// ); /// /// // payload of the echo reply /// let payload = [1,2,3,4,5,6,7,8]; /// /// // get some memory to store the result /// let mut result = Vec::::with_capacity( /// builder.size(payload.len())); /// /// // serialize /// builder.write(&mut result, &payload).unwrap(); /// ``` pub fn icmpv4_echo_reply(mut self, id: u16, seq: u16) -> PacketBuilderStep { let echo_header = IcmpEchoHeader{ id, seq, }; let icmpv4_echo = Icmpv4Header::new(Icmpv4Type::EchoReply(echo_header)); self.state.transport_header = Some(TransportHeader::Icmpv4(icmpv4_echo)); //return for next step PacketBuilderStep { state: self.state, _marker: marker::PhantomData::{} } } /// Adds an ICMPv6 header of the given [`Icmpv6Type`] to the packet. /// /// If an ICMPv6 header gets added the payload used during the builders `write` /// call contains the bytes after the header and has different meanings /// and contents based on the type. Ususally all statically sized values /// known based on the ICMPv6 type & code are part of the header and the /// payload is used to store contains the dynamic parts of the ICMPv6 packet. /// /// Check [`Icmpv6Type`] for a documentation which values are part of the /// header and what is stored as part of the payload. /// /// # Example /// /// Basic usage: /// /// ``` /// # use etherparse::{PacketBuilder, Icmpv6Type, icmpv6}; /// # /// let builder = PacketBuilder:: /// ipv6( /// //source /// [11,12,13,14,15,16,17,18,19,10,21,22,23,24,25,26], /// //destination /// [31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46], /// //hop_limit /// 47) /// .icmpv6( /// Icmpv6Type::TimeExceeded( /// icmpv6::TimeExceededCode::HopLimitExceeded /// ) /// ); /// /// // what is part of the payload depends on the Icmpv6Type /// // /// // In case of `Icmpv6Type::TimeExceeded` "As much of invoking packet /// // as possible without the ICMPv6 packet exceeding the minimum IPv6 MTU" /// // should be given as the payload. /// let payload = [1,2,3,4,5,6,7,8]; /// /// //get some memory to store the result /// let mut result = Vec::::with_capacity( /// builder.size(payload.len())); /// /// //serialize /// builder.write(&mut result, &payload).unwrap(); /// ``` pub fn icmpv6(mut self, icmp_type: Icmpv6Type) -> PacketBuilderStep { self.state.transport_header = Some(TransportHeader::Icmpv6(Icmpv6Header{ icmp_type, checksum: 0, // calculated later })); //return for next step PacketBuilderStep { state: self.state, _marker: marker::PhantomData::{} } } /// Adds an ICMPv6 header based on raw values. /// /// This can be usefull when trying to build an ICMPv6 packet /// which is not fully supported by etherparse and is the equivalent /// of using [`Icmpv6Type::Unknown`] together with /// [`PacketBuilderStep::icmpv6`]. /// /// # Example /// /// Basic usage: /// /// ``` /// # use etherparse::PacketBuilder; /// # /// let builder = PacketBuilder:: /// ipv6( /// //source /// [11,12,13,14,15,16,17,18,19,10,21,22,23,24,25,26], /// //destination /// [31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46], /// //hop_limit /// 47) /// .icmpv4_raw( /// 200, // ICMPv6 type (e.g. 200 is for "private experimentation") /// 0, // ICMPv6 code /// [1,2,3,4] // bytes 5-8 in the ICMPv6 header /// ); /// /// // the payload is written after the 8 byte raw ICMPv6 header /// let payload = [1,2,3,4,5,6,7,8]; /// /// //get some memory to store the result /// let mut result = Vec::::with_capacity( /// builder.size(payload.len())); /// /// //serialize /// builder.write(&mut result, &payload).unwrap(); /// ``` pub fn icmpv6_raw(mut self, type_u8: u8, code_u8: u8, bytes5to8: [u8;4]) -> PacketBuilderStep { let icmp_type = Icmpv6Type::Unknown{type_u8, code_u8, bytes5to8}; self.state.transport_header = Some(TransportHeader::Icmpv6(Icmpv6Header{ icmp_type, checksum: 0, // calculated later })); //return for next step PacketBuilderStep { state: self.state, _marker: marker::PhantomData::{} } } /// Adds an ICMPv6 echo reply packet. /// /// # Example /// /// Basic usage: /// /// ``` /// # use etherparse::PacketBuilder; /// # /// let builder = PacketBuilder:: /// ipv6( /// //source /// [11,12,13,14,15,16,17,18,19,10,21,22,23,24,25,26], /// //destination /// [31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46], /// //hop_limit /// 47) /// .icmpv6_echo_request( /// 123, // identifier /// 456, // sequence number /// ); /// /// // payload of the echo request /// let payload = [1,2,3,4,5,6,7,8]; /// /// //get some memory to store the result /// let mut result = Vec::::with_capacity( /// builder.size(payload.len())); /// /// //serialize /// builder.write(&mut result, &payload).unwrap(); /// ``` pub fn icmpv6_echo_request(mut self, id: u16, seq: u16) -> PacketBuilderStep { let echo_header = IcmpEchoHeader{ id, seq, }; let icmpv6_echo = Icmpv6Header::new(Icmpv6Type::EchoRequest(echo_header)); self.state.transport_header = Some(TransportHeader::Icmpv6(icmpv6_echo)); //return for next step PacketBuilderStep { state: self.state, _marker: marker::PhantomData::{} } } /// Adds an ICMPv6 echo request packet. /// /// # Example /// /// Basic usage: /// /// ``` /// # use etherparse::PacketBuilder; /// # /// let builder = PacketBuilder:: /// ipv6( /// //source /// [11,12,13,14,15,16,17,18,19,10,21,22,23,24,25,26], /// //destination /// [31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46], /// //hop_limit /// 47) /// .icmpv6_echo_reply( /// 123, // identifier /// 456, // sequence number /// ); /// /// // payload of the echo reply /// let payload = [1,2,3,4,5,6,7,8]; /// /// //get some memory to store the result /// let mut result = Vec::::with_capacity( /// builder.size(payload.len())); /// /// //serialize /// builder.write(&mut result, &payload).unwrap(); /// ``` pub fn icmpv6_echo_reply(mut self, id: u16, seq: u16) -> PacketBuilderStep { let echo_header = IcmpEchoHeader{ seq, id, }; let icmpv6_echo = Icmpv6Header::new(Icmpv6Type::EchoReply(echo_header)); self.state.transport_header = Some(TransportHeader::Icmpv6(icmpv6_echo)); //return for next step PacketBuilderStep { state: self.state, _marker: marker::PhantomData::{} } } /// Adds an UDP header. /// /// # Example /// /// Basic usage: /// /// ``` /// # use etherparse::PacketBuilder; /// # /// let builder = PacketBuilder:: /// ethernet2([1,2,3,4,5,6], //source mac /// [7,8,9,10,11,12]) //destionation mac /// .ipv4([192,168,1,1], //source ip /// [192,168,1,2], //desitionation ip /// 20) //time to life /// .udp(21, //source port /// 1234); //desitnation port /// /// //payload of the udp packet /// let payload = [1,2,3,4,5,6,7,8]; /// /// //get some memory to store the result /// let mut result = Vec::::with_capacity( /// builder.size(payload.len())); /// /// //serialize /// builder.write(&mut result, &payload).unwrap(); /// ``` pub fn udp(mut self, source_port: u16, destination_port: u16) -> PacketBuilderStep { self.state.transport_header = Some(TransportHeader::Udp(UdpHeader{ source_port, destination_port, length: 0, //calculated later checksum: 0 //calculated later })); //return for next step PacketBuilderStep { state: self.state, _marker: marker::PhantomData::{} } } /// Adds an TCP header. /// /// # Example /// /// Basic usage: /// /// ``` /// # use etherparse::PacketBuilder; /// # /// let builder = PacketBuilder:: /// ethernet2([1,2,3,4,5,6], // source mac /// [7,8,9,10,11,12]) // destionation mac /// .ipv4([192,168,1,1], // source ip /// [192,168,1,2], // desitionation ip /// 20) // time to life /// .tcp(21, // source port /// 12, // destination port /// 12345, // sequence number /// 4000); // window size /// /// //payload of the udp packet /// let payload = [1,2,3,4,5,6,7,8]; /// /// //get some memory to store the result /// let mut result = Vec::::with_capacity( /// builder.size(payload.len())); /// /// //serialize /// builder.write(&mut result, &payload).unwrap(); /// ``` pub fn tcp(mut self, source_port: u16, destination_port: u16, sequence_number: u32, window_size: u16) -> PacketBuilderStep { self.state.transport_header = Some(TransportHeader::Tcp( TcpHeader::new(source_port, destination_port, sequence_number, window_size) )); //return for next step PacketBuilderStep { state: self.state, _marker: marker::PhantomData::{} } } /// Write all the headers and the payload with the given ip number. /// /// `last_next_header_ip_number` will be set in the last extension header /// or if no extension header exists the ip header as the "next header" or /// "protocol number". pub fn write(mut self, writer: &mut T, last_next_header_ip_number: u8, payload: &[u8]) -> Result<(),WriteError> { self.state.ip_header.as_mut().unwrap().set_next_headers(last_next_header_ip_number); final_write(self, writer, payload) } ///Returns the size of the packet when it is serialized pub fn size(&self, payload_size: usize) -> usize { final_size(self, payload_size) } } impl PacketBuilderStep { /// Write all the headers and the payload. pub fn write(self, writer: &mut T, payload: &[u8]) -> Result<(),WriteError> { final_write(self, writer, payload) } /// Returns the size of the packet when it is serialized pub fn size(&self, payload_size: usize) -> usize { final_size(self, payload_size) } } impl PacketBuilderStep { ///Write all the headers and the payload. pub fn write(self, writer: &mut T, payload: &[u8]) -> Result<(),WriteError> { final_write(self, writer, payload) } ///Returns the size of the packet when it is serialized pub fn size(&self, payload_size: usize) -> usize { final_size(self, payload_size) } } impl PacketBuilderStep { ///Write all the headers and the payload. pub fn write(self, writer: &mut T, payload: &[u8]) -> Result<(),WriteError> { final_write(self, writer, payload) } ///Returns the size of the packet when it is serialized pub fn size(&self, payload_size: usize) -> usize { final_size(self, payload_size) } } impl PacketBuilderStep { ///Set ns flag (ECN-nonce - concealment protection; experimental: see RFC 3540) pub fn ns(mut self) -> PacketBuilderStep { self.state.transport_header.as_mut().unwrap().mut_tcp().unwrap().ns = true; self } ///Set fin flag (No more data from sender) pub fn fin(mut self) -> PacketBuilderStep { self.state.transport_header.as_mut().unwrap().mut_tcp().unwrap().fin = true; self } ///Set the syn flag (synchronize sequence numbers) pub fn syn(mut self) -> PacketBuilderStep { self.state.transport_header.as_mut().unwrap().mut_tcp().unwrap().syn = true; self } ///Sets the rst flag (reset the connection) pub fn rst(mut self) -> PacketBuilderStep { self.state.transport_header.as_mut().unwrap().mut_tcp().unwrap().rst = true; self } ///Sets the psh flag (push function) pub fn psh(mut self) -> PacketBuilderStep { self.state.transport_header.as_mut().unwrap().mut_tcp().unwrap().psh = true; self } ///Sets the ack flag and the acknowledgment_number. pub fn ack(mut self, acknowledgment_number: u32) -> PacketBuilderStep { { let header = self.state.transport_header.as_mut().unwrap().mut_tcp().unwrap(); header.ack = true; header.acknowledgment_number = acknowledgment_number; } self } ///Set the urg flag & the urgent pointer field. /// ///The urgent pointer points to the sequence number of the octet following ///the urgent data. pub fn urg(mut self, urgent_pointer: u16) -> PacketBuilderStep { { let header = self.state.transport_header.as_mut().unwrap().mut_tcp().unwrap(); header.urg = true; header.urgent_pointer = urgent_pointer; } self } ///Sets ece flag (ECN-Echo, RFC 3168) pub fn ece(mut self) -> PacketBuilderStep { self.state.transport_header.as_mut().unwrap().mut_tcp().unwrap().ece = true; self } ///Set cwr flag (Congestion Window Reduced) /// ///This flag is set by the sending host to indicate that it received a TCP segment with the ECE flag set and had responded in congestion control mechanism (added to header by RFC 3168). pub fn cwr(mut self) -> PacketBuilderStep { self.state.transport_header.as_mut().unwrap().mut_tcp().unwrap().cwr = true; self } ///Set the tcp options of the header. pub fn options(mut self, options: &[TcpOptionElement]) -> Result, TcpOptionWriteError> { self.state.transport_header.as_mut().unwrap().mut_tcp().unwrap().set_options(options)?; Ok(self) } ///Set the tcp options of the header (setting the bytes directly). pub fn options_raw(mut self, options: &[u8]) -> Result, TcpOptionWriteError> { self.state.transport_header.as_mut().unwrap().mut_tcp().unwrap().set_options_raw(options)?; Ok(self) } ///Write all the headers and the payload. pub fn write(self, writer: &mut T, payload: &[u8]) -> Result<(),WriteError> { final_write(self, writer, payload) } ///Returns the size of the packet when it is serialized pub fn size(&self, payload_size: usize) -> usize { final_size(self, payload_size) } } ///Write all the headers and the payload. fn final_write(builder: PacketBuilderStep, writer: &mut T, payload: &[u8]) -> Result<(),WriteError> { let ip_ether_type = { use crate::IpHeader::*; match builder.state.ip_header { Some(Version4(_,_)) => ether_type::IPV4, Some(Version6(_,_)) => ether_type::IPV6, None => panic!("Missing ip header") } }; //ethernetII header if let Some(mut eth) = builder.state.ethernet2_header { eth.ether_type = { use crate::VlanHeader::*; //determine the ether type depending on if there is a vlan tagging header match builder.state.vlan_header { Some(Single(_)) => ether_type::VLAN_TAGGED_FRAME, Some(Double(_)) => ether_type::PROVIDER_BRIDGING, //if no vlan header exists, the id is purely defined by the ip type None => ip_ether_type } }; eth.write(writer)?; } //write the vlan header if it exists use crate::VlanHeader::*; match builder.state.vlan_header { Some(Single(mut value)) => { //set ether types value.ether_type = ip_ether_type; //serialize value.write(writer)?; }, Some(Double(mut value)) => { //set ether types value.outer.ether_type = ether_type::VLAN_TAGGED_FRAME; value.inner.ether_type = ip_ether_type; //serialize value.write(writer)?; }, None => {} } //ip header use crate::IpHeader::*; let ip_header = builder.state.ip_header.unwrap(); //transport header let transport = builder.state.transport_header; match transport { None => { // in case no transport header is present the protocol // number and next_header fields are set in the write call // directly and don't need to be set here again. match ip_header { Version4(mut ip, ext) => { ip.set_payload_len(ext.header_len() + payload.len())?; ip.write(writer)?; ext.write(writer, ip.protocol)?; }, Version6(mut ip, ext) => { ip.set_payload_length(ext.header_len() + payload.len())?; ip.write(writer)?; ext.write(writer, ip.next_header)?; } } }, Some(mut transport) => { match ip_header { Version4(mut ip, mut ext) => { //set total length & udp payload length (ip checks that the payload length is ok) let transport_size = transport.header_len() + payload.len(); ip.set_payload_len(ext.header_len() + transport_size)?; use crate::TransportHeader::*; match transport { Icmpv4(_) => {}, Icmpv6(_) => {}, Udp(ref mut udp) => { udp.length = transport_size as u16; } Tcp(_) => {}, } //ip protocol number & next header values of the extension header ip.protocol = ext.set_next_headers( match transport { Icmpv4(_) => ip_number::ICMP, Icmpv6(_) => ip_number::IPV6_ICMP, Udp(_) => ip_number::UDP, Tcp(_) => ip_number::TCP } ); //calculate the udp checksum transport.update_checksum_ipv4(&ip, payload)?; //write (will automatically calculate the checksum) ip.write(writer)?; ext.write(writer, ip.protocol)?; }, Version6(mut ip, mut ext) => { //set total length let transport_size = transport.header_len() + payload.len(); ip.set_payload_length(ext.header_len() + transport_size)?; use crate::TransportHeader::*; match transport { Icmpv4(_) => {}, Icmpv6(_) => {}, Udp(ref mut udp) => { udp.length = transport_size as u16; } Tcp(_) => {} } //set the protocol ip.next_header = ext.set_next_headers( match transport { Icmpv4(_) => ip_number::ICMP, Icmpv6(_) => ip_number::IPV6_ICMP, Udp(_) => ip_number::UDP, Tcp(_) => ip_number::TCP } ); //calculate the udp checksum transport.update_checksum_ipv6(&ip, payload)?; //write (will automatically calculate the checksum) ip.write(writer)?; ext.write(writer, ip.next_header)?; } } //finaly write the udp header & payload transport.write(writer)?; }, } writer.write_all(payload)?; Ok(()) } ///Returns the size of the packet when it is serialized fn final_size(builder: &PacketBuilderStep, payload_size: usize) -> usize { use crate::IpHeader::*; use crate::VlanHeader::*; use crate::TransportHeader::*; (match builder.state.ethernet2_header { Some(_) => Ethernet2Header::SERIALIZED_SIZE, None => 0 }) + match builder.state.vlan_header { Some(Single(_)) => SingleVlanHeader::SERIALIZED_SIZE, Some(Double(_)) => DoubleVlanHeader::SERIALIZED_SIZE, None => 0 } + match builder.state.ip_header { Some(Version4(ref value, ref ext)) => value.header_len() + ext.header_len(), Some(Version6(_, ref ext)) => Ipv6Header::SERIALIZED_SIZE + ext.header_len(), None => 0 } + match builder.state.transport_header { Some(Icmpv4(ref value)) => value.header_len(), Some(Icmpv6(ref value)) => value.header_len(), Some(Udp(_)) => UdpHeader::SERIALIZED_SIZE, Some(Tcp(ref value)) => value.header_len() as usize, None => 0 } + payload_size } #[cfg(test)] mod whitebox_tests { use super::*; //whitebox tests that need internal access #[test] fn size() { assert_eq!(0, PacketBuilderStep:: { state: PacketImpl { ethernet2_header: None, ip_header: None, vlan_header: None, transport_header: None }, _marker: marker::PhantomData::{} }.size(0)); } #[test] #[should_panic] fn final_write_panic_missing_ip() { let mut writer = Vec::new(); final_write( PacketBuilderStep:: { state: PacketImpl { ethernet2_header: None, ip_header: None, vlan_header: None, transport_header: None }, _marker: marker::PhantomData::{} }, &mut writer, &[] ).unwrap(); } } etherparse-0.13.0/src/packet_decoder.rs000064400000000000000000000370631046102023000161510ustar 00000000000000use super::*; /// Decoded packet headers (data link layer and lower). /// /// You can use /// /// * [`PacketHeaders::from_ethernet_slice`] /// * [`PacketHeaders::from_ether_type`] /// * [`PacketHeaders::from_ip_slice`] /// /// depending on your starting header to parse the headers in a slice and get this /// struct as a result. #[derive(Clone, Debug, Eq, PartialEq)] pub struct PacketHeaders<'a> { /// Ethernet II header if present. pub link: Option, /// Single or double vlan headers if present. pub vlan: Option, /// IPv4 or IPv6 header and IP extension headers if present. pub ip: Option, /// TCP or UDP header if present. pub transport: Option, /// Rest of the packet that could not be decoded as a header (usually the payload). pub payload: &'a [u8] } impl<'a> PacketHeaders<'a> { /// Decodes a network packet into different headers from a slice that starts with an Ethernet II header. /// /// The result is returned as a [`PacketHeaders`] struct. /// /// # Example /// /// Basic usage: /// ///``` /// # use etherparse::{Ethernet2Header, SerializedSize, PacketBuilder}; /// # let builder = PacketBuilder:: /// # ethernet2([1,2,3,4,5,6], //source mac /// # [7,8,9,10,11,12]) //destionation mac /// # .ipv4([192,168,1,1], //source ip /// # [192,168,1,2], //desitionation ip /// # 20) //time to life /// # .udp(21, //source port /// # 1234); //desitnation port /// # // payload of the udp packet /// # let payload = [1,2,3,4,5,6,7,8]; /// # // get some memory to store the serialized data /// # let mut complete_packet = Vec::::with_capacity( /// # builder.size(payload.len()) /// # ); /// # builder.write(&mut complete_packet, &payload).unwrap(); /// # /// # // skip ethernet 2 header so we can parse from there downwards /// # let packet = &complete_packet[Ethernet2Header::SERIALIZED_SIZE..]; /// # /// use etherparse::{ether_type, PacketHeaders}; /// /// match PacketHeaders::from_ether_type(ether_type::IPV4, packet) { /// Err(value) => println!("Err {:?}", value), /// Ok(value) => { /// println!("link: {:?}", value.link); /// println!("vlan: {:?}", value.vlan); /// println!("ip: {:?}", value.ip); /// println!("transport: {:?}", value.transport); /// } /// } /// ``` pub fn from_ethernet_slice(packet: &[u8]) -> Result { let (ethernet, mut rest) = Ethernet2Header::from_slice(packet)?; let mut ether_type = ethernet.ether_type; let mut result = PacketHeaders{ link: Some(ethernet), vlan: None, ip: None, transport: None, payload: &[] }; //parse vlan header(s) use ether_type::*; result.vlan = match ether_type { VLAN_TAGGED_FRAME | PROVIDER_BRIDGING | VLAN_DOUBLE_TAGGED_FRAME => { use crate::VlanHeader::*; let (outer, outer_rest) = SingleVlanHeader::from_slice(rest)?; //set the rest & ether_type for the following operations rest = outer_rest; ether_type = outer.ether_type; //parse second vlan header if present match ether_type { //second vlan tagging header VLAN_TAGGED_FRAME | PROVIDER_BRIDGING | VLAN_DOUBLE_TAGGED_FRAME => { let (inner, inner_rest) = SingleVlanHeader::from_slice(rest)?; //set the rest & ether_type for the following operations rest = inner_rest; ether_type = inner.ether_type; Some(Double(DoubleVlanHeader{ outer, inner })) }, //no second vlan header detected -> single vlan header _ => Some(Single(outer)) } }, //no vlan header _ => None }; //parse ip (if present) match ether_type { IPV4 => { let (ip, ip_rest) = Ipv4Header::from_slice(rest)?; let fragmented = ip.is_fragmenting_payload(); let (ip_ext, ip_protocol, ip_ext_rest) = Ipv4Extensions::from_slice(ip.protocol, ip_rest)?; //set the ip result & rest rest = ip_ext_rest; result.ip = Some(IpHeader::Version4(ip, ip_ext)); // only try to decode the transport layer if the payload // is not fragmented if false == fragmented { //parse the transport layer let (transport, transport_rest) = read_transport(ip_protocol, rest)?; //assign to the output rest = transport_rest; result.transport = transport; } }, IPV6 => { let (ip, ip_rest) = Ipv6Header::from_slice(rest)?; let (ip_ext, next_header, ip_ext_rest) = Ipv6Extensions::from_slice(ip.next_header, ip_rest)?; let fragmented = ip_ext.is_fragmenting_payload(); //set the ip result & rest rest = ip_ext_rest; result.ip = Some(IpHeader::Version6(ip, ip_ext)); // only try to decode the transport layer if the payload // is not fragmented if false == fragmented { //parse the transport layer let (transport, transport_rest) = read_transport(next_header, rest)?; rest = transport_rest; result.transport = transport; } }, _ => {} } //finally update the rest slice based on the cursor position result.payload = rest; Ok(result) } /// Tries to decode a network packet into different headers using the /// given `ether_type` number to identify the first header. /// /// The result is returned as a [`PacketHeaders`] struct. Currently supported /// ether type numbers are: /// /// * `ether_type::IPV4` /// * `ether_type::IPV6` /// * `ether_type::VLAN_TAGGED_FRAME` /// * `ether_type::PROVIDER_BRIDGING` /// * `ether_type::VLAN_DOUBLE_TAGGED_FRAME` /// /// If an unsupported ether type is given the given slice will be set as payload /// and all other fields will be set to `None`. /// /// # Example /// /// Basic usage: /// ///``` /// # use etherparse::{Ethernet2Header, SerializedSize, PacketBuilder}; /// # let builder = PacketBuilder:: /// # ethernet2([1,2,3,4,5,6], //source mac /// # [7,8,9,10,11,12]) //destionation mac /// # .ipv4([192,168,1,1], //source ip /// # [192,168,1,2], //desitionation ip /// # 20) //time to life /// # .udp(21, //source port /// # 1234); //desitnation port /// # // payload of the udp packet /// # let payload = [1,2,3,4,5,6,7,8]; /// # // get some memory to store the serialized data /// # let mut complete_packet = Vec::::with_capacity( /// # builder.size(payload.len()) /// # ); /// # builder.write(&mut complete_packet, &payload).unwrap(); /// # /// # // skip ethernet 2 header so we can parse from there downwards /// # let packet = &complete_packet[Ethernet2Header::SERIALIZED_SIZE..]; /// # /// use etherparse::{ether_type, PacketHeaders}; /// /// match PacketHeaders::from_ether_type(ether_type::IPV4, packet) { /// Err(value) => println!("Err {:?}", value), /// Ok(value) => { /// println!("link: {:?}", value.link); /// println!("vlan: {:?}", value.vlan); /// println!("ip: {:?}", value.ip); /// println!("transport: {:?}", value.transport); /// } /// } /// ``` pub fn from_ether_type(mut ether_type: u16, data: &'a [u8]) -> Result { let mut rest = data; let mut result = PacketHeaders{ link: None, vlan: None, ip: None, transport: None, payload: &[] }; //parse vlan header(s) use ether_type::*; result.vlan = match ether_type { VLAN_TAGGED_FRAME | PROVIDER_BRIDGING | VLAN_DOUBLE_TAGGED_FRAME => { use crate::VlanHeader::*; let (outer, outer_rest) = SingleVlanHeader::from_slice(rest)?; //set the rest & ether_type for the following operations rest = outer_rest; ether_type = outer.ether_type; //parse second vlan header if present match ether_type { //second vlan tagging header VLAN_TAGGED_FRAME | PROVIDER_BRIDGING | VLAN_DOUBLE_TAGGED_FRAME => { let (inner, inner_rest) = SingleVlanHeader::from_slice(rest)?; //set the rest & ether_type for the following operations rest = inner_rest; ether_type = inner.ether_type; Some(Double(DoubleVlanHeader{ outer, inner })) }, //no second vlan header detected -> single vlan header _ => Some(Single(outer)) } }, //no vlan header _ => None }; //parse ip (if present) match ether_type { IPV4 => { let (ip, ip_rest) = Ipv4Header::from_slice(rest)?; let fragmented = ip.is_fragmenting_payload(); let (ip_ext, ip_protocol, ip_ext_rest) = Ipv4Extensions::from_slice(ip.protocol, ip_rest)?; //set the ip result & rest rest = ip_ext_rest; result.ip = Some(IpHeader::Version4(ip, ip_ext)); // only try to decode the transport layer if the payload // is not fragmented if false == fragmented { //parse the transport layer let (transport, transport_rest) = read_transport(ip_protocol, rest)?; //assign to the output rest = transport_rest; result.transport = transport; } }, IPV6 => { let (ip, ip_rest) = Ipv6Header::from_slice(rest)?; let (ip_ext, next_header, ip_ext_rest) = Ipv6Extensions::from_slice(ip.next_header, ip_rest)?; let fragmented = ip_ext.is_fragmenting_payload(); //set the ip result & rest rest = ip_ext_rest; result.ip = Some(IpHeader::Version6(ip, ip_ext)); // only try to decode the transport layer if the payload // is not fragmented if false == fragmented { //parse the transport layer let (transport, transport_rest) = read_transport(next_header, rest)?; rest = transport_rest; result.transport = transport; } }, _ => {} } //finally update the rest slice based on the cursor position result.payload = rest; Ok(result) } /// Tries to decode an ip packet and its transport headers. /// /// Assumes the given slice starts with the first byte of the IP header. /// /// # Example /// /// Basic usage: /// /// ``` /// # use etherparse::PacketBuilder; /// # // build a UDP packet /// # let payload = [0u8;18]; /// # let builder = PacketBuilder:: /// # ipv4([192,168,1,1], //source ip /// # [192,168,1,2], //desitionation ip /// # 20) //time to life /// # .udp(21, //source port /// # 1234); //desitnation port /// # /// # // serialize the packet /// # let packet = { /// # let mut packet = Vec::::with_capacity( /// # builder.size(payload.len()) /// # ); /// # builder.write(&mut packet, &payload).unwrap(); /// # packet /// # }; /// use etherparse::PacketHeaders; /// /// match PacketHeaders::from_ip_slice(&packet) { /// Err(value) => println!("Err {:?}", value), /// Ok(value) => { /// println!("link: {:?}", value.link); /// println!("vlan: {:?}", value.vlan); /// println!("ip: {:?}", value.ip); /// println!("transport: {:?}", value.transport); /// } /// } /// ``` pub fn from_ip_slice(packet: &[u8]) -> Result { let mut result = PacketHeaders { link: None, vlan: None, ip: None, transport: None, payload: &[], }; let (transport_proto, rest) = { let (ip, transport_proto, rest) = IpHeader::from_slice(packet)?; // update output result.ip = Some(ip); (transport_proto, rest) }; // try to parse the transport header let (transport, rest) = read_transport(transport_proto, rest)?; // update output result.transport = transport; result.payload = rest; Ok(result) } /// If the slice in the `payload` field contains an ethernet payload /// this method returns the ether type number describing the payload type. /// /// The ether type number can come from an ethernet II header or a /// VLAN header depending on which headers are present. /// /// In case that `ip` and/or `transport` fields are the filled None /// is returned, as the payload contents then are defined by a /// lower layer protocol described in these fields. pub fn payload_ether_type(&self) -> Option { if self.ip.is_some() || self.transport.is_some() { None } else { if let Some(vlan) = &self.vlan { use VlanHeader::*; match vlan { Single(s) => { Some(s.ether_type) }, Double(d) => { Some(d.inner.ether_type) } } } else { if let Some(link) = &self.link { Some(link.ether_type) } else { None } } } } } /// helper function to process transport headers fn read_transport( protocol: u8, rest: &[u8], ) -> Result<(Option, &[u8]), ReadError> { use crate::ip_number::*; match protocol { ICMP => { Ok(Icmpv4Header::from_slice(rest)?) .map( |value| (Some(TransportHeader::Icmpv4(value.0)), value.1)) }, IPV6_ICMP => { Ok(Icmpv6Header::from_slice(rest)?) .map( |value| (Some(TransportHeader::Icmpv6(value.0)), value.1)) }, UDP => Ok(UdpHeader::from_slice(rest) .map(|value| (Some(TransportHeader::Udp(value.0)), value.1))?), TCP => Ok(TcpHeader::from_slice(rest) .map(|value| (Some(TransportHeader::Tcp(value.0)), value.1))?), _ => Ok((None, rest)), } }etherparse-0.13.0/src/packet_filter.rs000064400000000000000000000167101046102023000160250ustar 00000000000000use super::*; #[derive(Debug, Clone, Eq, PartialEq)] pub enum ElementFilter { Any, No, Some(T) } #[derive(Debug, Clone, Eq, PartialEq)] pub enum LinkFilter { Ethernet2 { source: Option<[u8;6]>, destination: Option<[u8;6]> } } #[derive(Debug, Clone, Eq, PartialEq)] pub enum VlanFilter { Single(Option), Double { outer_identifier: Option, inner_identifier: Option } } #[derive(Debug, Clone, Eq, PartialEq)] pub enum IpFilter { Ipv4 { source: Option<[u8;4]>, destination: Option<[u8;4]> }, Ipv6 { source: Option<[u8;16]>, destination: Option<[u8;16]> } } #[derive(Debug, Clone, Eq, PartialEq)] pub enum TransportFilter { Udp { source_port: Option, destination_port: Option }, Tcp { source_port: Option, destination_port: Option } } #[derive(Debug, Clone, Default, Eq, PartialEq)] pub struct Filter { pub link: ElementFilter, pub vlan: ElementFilter, pub ip: ElementFilter, pub transport: ElementFilter, } impl Default for ElementFilter { fn default() -> ElementFilter { ElementFilter::Any } } impl LinkFilter { pub fn applies_to_slice(&self, slice: &LinkSlice) -> bool { use crate::LinkSlice::*; match self { LinkFilter::Ethernet2{ source: expected_source, destination: expected_destination } => match slice { Ethernet2(header) => (match expected_source { Some(e) => header.source() == *e, None => true }) && (match expected_destination { Some(e) => header.destination() == *e, None => true }) } } } } impl VlanFilter { pub fn applies_to_slice(&self, slice: &VlanSlice) -> bool { use crate::VlanSlice::*; match self { VlanFilter::Single(expected_id) => { match slice { SingleVlan(header) => { match expected_id { Some(e) => header.vlan_identifier() == *e, None => true } }, _ => false //not a single vlan header } }, VlanFilter::Double { inner_identifier: expected_inner_id, outer_identifier: expecetd_outer_id } => { match slice { DoubleVlan(header) => { (match expecetd_outer_id { Some(e) => header.outer().vlan_identifier() == *e, None => true }) && (match expected_inner_id { Some(e) => header.inner().vlan_identifier() == *e, None => true }) }, _ => false } } } } } impl IpFilter { pub fn applies_to_slice(&self, slice: &InternetSlice) -> bool { use crate::InternetSlice::*; match self { IpFilter::Ipv4 { source: expected_source, destination: expected_destination } => { match slice { Ipv4(header, _) => { (match expected_source { Some(e) => header.source() == *e, None => true }) && (match expected_destination { Some(e) => header.destination() == *e, None => true }) }, _ => false } }, IpFilter::Ipv6 { source: expected_source, destination: expected_destination } => { match slice { Ipv6(header, _) => { (match expected_source { Some(e) => header.source() == *e, None => true }) && (match expected_destination { Some(e) => header.destination() == *e, None => true }) }, _ => false } } } } } impl TransportFilter { pub fn applies_to_slice(&self, slice: &TransportSlice) -> bool { use crate::TransportSlice::*; match self { TransportFilter::Udp { source_port: expected_source_port, destination_port: expected_destination_port } => { match slice { Udp(header) => { (match expected_source_port { Some(e) => header.source_port() == *e, None => true }) && (match expected_destination_port { Some(e) => header.destination_port() == *e, None => true }) } _ => false } }, TransportFilter::Tcp { source_port: expected_source_port, destination_port: expected_destination_port } => { match slice { Tcp(header) => { (match expected_source_port { Some(e) => header.source_port() == *e, None => true }) && (match expected_destination_port { Some(e) => header.destination_port() == *e, None => true }) } _ => false } } } } } impl Filter { ///Returns true if a given sliced network package fullfills all conditions of this filter. pub fn applies_to_slice(&self, slice: &SlicedPacket) -> bool { (match &self.link { ElementFilter::Any => true, ElementFilter::No => slice.link.is_none(), ElementFilter::Some(filter) => { match &slice.link { Some(value) => filter.applies_to_slice(value), None => false } } }) && (match &self.vlan { ElementFilter::Any => true, ElementFilter::No => slice.vlan.is_none(), ElementFilter::Some(filter) => { match &slice.vlan { Some(value) => filter.applies_to_slice(value), None => false } } }) && (match &self.ip { ElementFilter::Any => true, ElementFilter::No => slice.ip.is_none(), ElementFilter::Some(filter) => { match &slice.ip { Some(value) => filter.applies_to_slice(value), None => false } } }) && (match &self.transport { ElementFilter::Any => true, ElementFilter::No => slice.transport.is_none(), ElementFilter::Some(filter) => { match &slice.transport { Some(value) => filter.applies_to_slice(value), None => false } } }) } }etherparse-0.13.0/src/packet_slicing.rs000064400000000000000000000466771046102023000162070ustar 00000000000000use super::*; #[derive(Clone, Debug, Eq, PartialEq)] pub enum InternetSlice<'a> { /// The ipv6 header & the decoded extension headers. Ipv4(Ipv4HeaderSlice<'a>, Ipv4ExtensionsSlice<'a>), /// The ipv6 header & the decoded extension headers. Ipv6(Ipv6HeaderSlice<'a>, Ipv6ExtensionsSlice<'a>), } impl<'a> InternetSlice<'a> { /// Returns true if the payload is fragmented. pub fn is_fragmenting_payload(&self) -> bool { match self { InternetSlice::Ipv4(v4_hdr, _) => v4_hdr.is_fragmenting_payload(), InternetSlice::Ipv6(_, v6_ext) => v6_ext.is_fragmenting_payload(), } } } #[derive(Clone, Debug, Eq, PartialEq)] pub enum TransportSlice<'a> { /// A slice containing an Icmp4 header Icmpv4(Icmpv4Slice<'a>), /// A slice containing an Icmp6 header Icmpv6(Icmpv6Slice<'a>), /// A slice containing an UDP header. Udp(UdpHeaderSlice<'a>), /// A slice containing a TCP header. Tcp(TcpHeaderSlice<'a>), /// Unknonwn transport layer protocol. The value is the last parsed ip protocol number. Unknown(u8), } /// Packet slice split into multiple slices containing the different headers & payload. /// /// Everything that could not be parsed is stored in a slice in the field "payload". /// /// You can use /// /// * [`SlicedPacket::from_ethernet`] /// * [`SlicedPacket::from_ether_type`] /// * [`SlicedPacket::from_ip`] /// /// depending on your starting header to slice a packet. /// /// # Examples /// /// Basic usage: /// ///``` /// # use etherparse::{SlicedPacket, PacketBuilder}; /// # let builder = PacketBuilder:: /// # ethernet2([1,2,3,4,5,6], //source mac /// # [7,8,9,10,11,12]) //destionation mac /// # .ipv4([192,168,1,1], //source ip /// # [192,168,1,2], //desitionation ip /// # 20) //time to life /// # .udp(21, //source port /// # 1234); //desitnation port /// # //payload of the udp packet /// # let payload = [1,2,3,4,5,6,7,8]; /// # //get some memory to store the serialized data /// # let mut packet = Vec::::with_capacity( /// # builder.size(payload.len())); /// # builder.write(&mut packet, &payload).unwrap(); /// match SlicedPacket::from_ethernet(&packet) { /// Err(value) => println!("Err {:?}", value), /// Ok(value) => { /// println!("link: {:?}", value.link); /// println!("vlan: {:?}", value.vlan); /// println!("ip: {:?}", value.ip); /// println!("transport: {:?}", value.transport); /// } /// } /// ``` #[derive(Clone, Debug, Eq, PartialEq)] pub struct SlicedPacket<'a> { /// Ethernet II header if present. pub link: Option>, /// Single or double vlan headers if present. pub vlan: Option>, /// IPv4 or IPv6 header and IP extension headers if present. pub ip: Option>, /// TCP or UDP header if present. pub transport: Option>, /// The payload field points to the rest of the packet that could not be parsed by etherparse. /// /// Depending on what other fields contain a "Some" values the payload contains the corresponding /// payload. /// /// For example if transport field contains Some(Udp(_)) then the payload field points to the udp payload. /// On the other hand if the transport field contains None then the payload contains the payload of /// next field containing a Some value (in order of transport, ip, vlan, link). pub payload: &'a [u8] } impl<'a> SlicedPacket<'a> { /// Seperates a network packet slice into different slices containing the headers from the ethernet header downwards. /// /// The result is returned as a [`SlicedPacket`] struct. This function assumes the given data starts /// with an ethernet II header. /// /// # Examples /// /// Basic usage: /// ///``` /// # use etherparse::{SlicedPacket, PacketBuilder}; /// # let builder = PacketBuilder:: /// # ethernet2([1,2,3,4,5,6], //source mac /// # [7,8,9,10,11,12]) //destionation mac /// # .ipv4([192,168,1,1], //source ip /// # [192,168,1,2], //desitionation ip /// # 20) //time to life /// # .udp(21, //source port /// # 1234); //desitnation port /// # //payload of the udp packet /// # let payload = [1,2,3,4,5,6,7,8]; /// # //get some memory to store the serialized data /// # let mut packet = Vec::::with_capacity( /// # builder.size(payload.len())); /// # builder.write(&mut packet, &payload).unwrap(); /// match SlicedPacket::from_ethernet(&packet) { /// Err(value) => println!("Err {:?}", value), /// Ok(value) => { /// println!("link: {:?}", value.link); /// println!("vlan: {:?}", value.vlan); /// println!("ip: {:?}", value.ip); /// println!("transport: {:?}", value.transport); /// } /// } /// ``` pub fn from_ethernet(data: &'a [u8]) -> Result { CursorSlice::new(data).slice_ethernet2() } /// Seperates a network packet slice into different slices containing the headers using /// the given `ether_type` number to identify the first header. /// /// The result is returned as a [`SlicedPacket`] struct. Currently supported /// ether type numbers are: /// /// * `ether_type::IPV4` /// * `ether_type::IPV6` /// * `ether_type::VLAN_TAGGED_FRAME` /// * `ether_type::PROVIDER_BRIDGING` /// * `ether_type::VLAN_DOUBLE_TAGGED_FRAME` /// /// If an unsupported ether type is given the given slice will be set as payload /// and all other fields will be set to `None`. /// /// # Example /// /// Basic usage: /// ///``` /// # use etherparse::{Ethernet2Header, SerializedSize, PacketBuilder}; /// # let builder = PacketBuilder:: /// # ethernet2([1,2,3,4,5,6], //source mac /// # [7,8,9,10,11,12]) //destionation mac /// # .ipv4([192,168,1,1], //source ip /// # [192,168,1,2], //desitionation ip /// # 20) //time to life /// # .udp(21, //source port /// # 1234); //desitnation port /// # // payload of the udp packet /// # let payload = [1,2,3,4,5,6,7,8]; /// # // get some memory to store the serialized data /// # let mut complete_packet = Vec::::with_capacity( /// # builder.size(payload.len()) /// # ); /// # builder.write(&mut complete_packet, &payload).unwrap(); /// # /// # // skip ethernet 2 header so we can parse from there downwards /// # let packet = &complete_packet[Ethernet2Header::SERIALIZED_SIZE..]; /// # /// use etherparse::{ether_type, SlicedPacket}; /// /// match SlicedPacket::from_ether_type(ether_type::IPV4, packet) { /// Err(value) => println!("Err {:?}", value), /// Ok(value) => { /// println!("link: {:?}", value.link); /// println!("vlan: {:?}", value.vlan); /// println!("ip: {:?}", value.ip); /// println!("transport: {:?}", value.transport); /// } /// } /// ``` pub fn from_ether_type(ether_type: u16, data: &'a [u8]) -> Result { use ether_type::*; match ether_type { IPV4 => CursorSlice::new(data).slice_ipv4(), IPV6 => CursorSlice::new(data).slice_ipv6(), VLAN_TAGGED_FRAME | PROVIDER_BRIDGING | VLAN_DOUBLE_TAGGED_FRAME => CursorSlice::new(data).slice_vlan(), _ => Ok( SlicedPacket { link: None, vlan: None, ip: None, transport: None, payload: data } ), } } /// Seperates a network packet slice into different slices containing the headers from the ip header downwards. /// /// The result is returned as a [`SlicedPacket`] struct. This function assumes the given data starts /// with an IPv4 or IPv6 header. /// /// # Examples /// /// Basic usage: /// ///``` /// # use etherparse::{SlicedPacket, PacketBuilder}; /// # let builder = PacketBuilder:: /// # ipv4([192,168,1,1], //source ip /// # [192,168,1,2], //desitionation ip /// # 20) //time to life /// # .udp(21, //source port /// # 1234); //desitnation port /// # //payload of the udp packet /// # let payload = [1,2,3,4,5,6,7,8]; /// # //get some memory to store the serialized data /// # let mut packet = Vec::::with_capacity( /// # builder.size(payload.len())); /// # builder.write(&mut packet, &payload).unwrap(); /// match SlicedPacket::from_ip(&packet) { /// Err(value) => println!("Err {:?}", value), /// Ok(value) => { /// //link & vlan fields are empty when parsing from ip downwards /// assert_eq!(None, value.link); /// assert_eq!(None, value.vlan); /// /// //ip & transport (udp or tcp) /// println!("ip: {:?}", value.ip); /// println!("transport: {:?}", value.transport); /// } /// } /// ``` pub fn from_ip(data: &'a [u8]) -> Result { CursorSlice::new(data).slice_ip() } /// If the slice in the `payload` field contains an ethernet payload /// this method returns the ether type number describing the payload type. /// /// The ether type number can come from an ethernet II header or a /// VLAN header depending on which headers are present. /// /// In case that `ip` and/or `transport` fields are the filled None /// is returned, as the payload contents then are defined by a /// lower layer protocol described in these fields. pub fn payload_ether_type(&self) -> Option { if self.ip.is_some() || self.transport.is_some() { None } else { if let Some(vlan) = &self.vlan { use VlanSlice::*; match vlan { SingleVlan(s) => { Some(s.ether_type()) }, DoubleVlan(d) => { Some(d.inner().ether_type()) } } } else { if let Some(link) = &self.link { use LinkSlice::*; match link { Ethernet2(eth) => { Some(eth.ether_type()) } } } else { None } } } } } ///Helper class for slicing packets struct CursorSlice<'a> { pub slice: &'a [u8], pub offset: usize, pub result: SlicedPacket<'a> } impl<'a> CursorSlice<'a> { pub fn new(slice: &'a [u8]) -> CursorSlice<'a> { CursorSlice { offset: 0, slice, result: SlicedPacket { link: None, vlan: None, ip: None, transport: None, payload: slice } } } fn move_by_slice(&mut self, other: &'a[u8]) { unsafe { use std::slice::from_raw_parts; self.slice = from_raw_parts( self.slice.as_ptr().add(other.len()), self.slice.len() - other.len() ); } self.offset += other.len(); } fn move_to_slice(&mut self, other: &'a[u8]) { self.offset += self.slice.len() - other.len(); self.slice = other; } pub fn slice_ethernet2(mut self) -> Result, ReadError> { use LinkSlice::*; use ether_type::*; let result = Ethernet2HeaderSlice::from_slice(self.slice) .map_err(|err| err.add_slice_offset(self.offset) )?; //cache the ether_type for later let ether_type = result.ether_type(); //set the new data self.move_by_slice(result.slice()); self.result.link = Some(Ethernet2(result)); //continue parsing (if required) match ether_type { IPV4 => self.slice_ipv4(), IPV6 => self.slice_ipv6(), VLAN_TAGGED_FRAME | PROVIDER_BRIDGING | VLAN_DOUBLE_TAGGED_FRAME => self.slice_vlan(), _ => self.slice_payload() } } pub fn slice_vlan(mut self) -> Result, ReadError> { use VlanSlice::*; use ether_type::*; let single = SingleVlanHeaderSlice::from_slice(self.slice) .map_err(|err| err.add_slice_offset(self.offset) )?; //check if it is a double vlan header match single.ether_type() { //in case of a double vlan header continue with the inner VLAN_TAGGED_FRAME | PROVIDER_BRIDGING | VLAN_DOUBLE_TAGGED_FRAME => self.slice_double_vlan(), value => { //set the vlan header and continue the normal parsing self.move_by_slice(single.slice()); self.result.vlan = Some(SingleVlan(single)); match value { IPV4 => self.slice_ipv4(), IPV6 => self.slice_ipv6(), _ => self.slice_payload() } } } } pub fn slice_double_vlan(mut self) -> Result, ReadError> { use VlanSlice::*; use ether_type::*; let result = DoubleVlanHeaderSlice::from_slice(self.slice) .map_err(|err| err.add_slice_offset(self.offset) )?; //cache ether_type for later let ether_type = result.inner().ether_type(); //set the new data self.move_by_slice(result.slice()); self.result.vlan = Some(DoubleVlan(result)); //continue parsing (if required) match ether_type { IPV4 => self.slice_ipv4(), IPV6 => self.slice_ipv6(), _ => self.slice_payload() } } pub fn slice_ip(self) -> Result, ReadError> { use ReadError::*; if self.slice.is_empty() { Err(UnexpectedEndOfSlice(self.offset + 1)) } else { match self.slice[0] >> 4 { 4 => self.slice_ipv4(), 6 => self.slice_ipv6(), version => Err(IpUnsupportedVersion(version)) } } } pub fn slice_ipv4(mut self) -> Result, ReadError> { use InternetSlice::*; let ip_header = Ipv4HeaderSlice::from_slice(self.slice) .map_err(|err| err.add_slice_offset(self.offset) )?; let fragmented = ip_header.is_fragmenting_payload(); // move the slice self.move_by_slice(ip_header.slice()); // slice extensions let (ip_ext, protocol, rest) = Ipv4ExtensionsSlice::from_slice(ip_header.protocol(), self.slice) .map_err(|err| err.add_slice_offset(self.offset) )?; // set the new data self.move_to_slice(rest); self.result.ip = Some(Ipv4(ip_header, ip_ext)); if fragmented { self.slice_payload() } else { match protocol { ip_number::UDP => self.slice_udp(), ip_number::TCP => self.slice_tcp(), ip_number::ICMP => self.slice_icmp4(), ip_number::IPV6_ICMP => self.slice_icmp6(), value => { use TransportSlice::*; self.result.transport = Some(Unknown(value)); self.slice_payload() } } } } pub fn slice_ipv6(mut self) -> Result, ReadError> { use crate::InternetSlice::*; let ip = Ipv6HeaderSlice::from_slice(self.slice) .map_err(|err| err.add_slice_offset(self.offset) )?; //move the slice self.move_by_slice(ip.slice()); //extension headers let (ip_ext, next_header, rest) = Ipv6ExtensionsSlice::from_slice(ip.next_header(), self.slice) .map_err(|err| err.add_slice_offset(self.offset) )?; let fragmented = ip_ext.is_fragmenting_payload(); // set the new data self.move_to_slice(rest); self.result.ip = Some(Ipv6(ip, ip_ext)); // only try to decode the transport layer if the payload // is not fragmented if fragmented { self.slice_payload() } else { //parse the data bellow match next_header { ip_number::ICMP => self.slice_icmp4(), ip_number::UDP => self.slice_udp(), ip_number::TCP => self.slice_tcp(), ip_number::IPV6_ICMP => self.slice_icmp6(), value => { use TransportSlice::*; self.result.transport = Some(Unknown(value)); self.slice_payload() } } } } pub fn slice_icmp4(mut self) -> Result, ReadError> { use crate::TransportSlice::*; let result = Icmpv4Slice::from_slice(self.slice) .map_err(|err| err.add_slice_offset(self.offset) )?; //set the new data self.move_by_slice(result.slice()); self.result.transport = Some(Icmpv4(result)); //done self.slice_payload() } pub fn slice_icmp6(mut self) -> Result, ReadError> { use crate::TransportSlice::*; let result = Icmpv6Slice::from_slice(self.slice) .map_err(|err| err.add_slice_offset(self.offset) )?; //set the new data self.move_by_slice(result.slice()); self.result.transport = Some(Icmpv6(result)); //done self.slice_payload() } pub fn slice_udp(mut self) -> Result, ReadError> { use crate::TransportSlice::*; let result = UdpHeaderSlice::from_slice(self.slice) .map_err(|err| err.add_slice_offset(self.offset) )?; //set the new data self.move_by_slice(result.slice()); self.result.transport = Some(Udp(result)); //done self.slice_payload() } pub fn slice_tcp(mut self) -> Result, ReadError> { use crate::TransportSlice::*; let result = TcpHeaderSlice::from_slice(self.slice) .map_err(|err| err.add_slice_offset(self.offset) )?; //set the new data self.move_by_slice(result.slice()); self.result.transport = Some(Tcp(result)); //done self.slice_payload() } pub fn slice_payload(mut self) -> Result, ReadError> { self.result.payload = self.slice; Ok(self.result) } } etherparse-0.13.0/src/transport/icmp.rs000064400000000000000000000024071046102023000161730ustar 00000000000000/// Echo Request & Response common parts between ICMPv4 and ICMPv6. /// /// # RFC 4443 Description (ICMPv6) /// /// Every node MUST implement an ICMPv6 Echo responder function that /// receives Echo Requests and originates corresponding Echo Replies. A /// node SHOULD also implement an application-layer interface for /// originating Echo Requests and receiving Echo Replies, for diagnostic /// purposes. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct IcmpEchoHeader { /// An identifier to aid in matching Echo Replies to Echo Requests. May be zero. pub id: u16, /// A sequence number to aid in matching Echo Replies to Echo Requests. May be zero. pub seq: u16, } impl IcmpEchoHeader { /// Return the seq + id encoded to the on the wire format. #[inline] pub fn to_bytes(&self) -> [u8; 4] { let id_be = self.id.to_be_bytes(); let seq_be = self.seq.to_be_bytes(); [id_be[0], id_be[1], seq_be[0], seq_be[1]] } /// Decodes the seq + id from the on the wire format. #[inline] pub fn from_bytes(bytes5to8: [u8; 4]) -> IcmpEchoHeader { IcmpEchoHeader { id: u16::from_be_bytes([bytes5to8[0], bytes5to8[1]]), seq: u16::from_be_bytes([bytes5to8[2], bytes5to8[3]]), } } } etherparse-0.13.0/src/transport/icmpv4_impl.rs000064400000000000000000001663721046102023000175020ustar 00000000000000use super::super::*; use arrayvec::ArrayVec; use std::slice::from_raw_parts; /// Module containing ICMPv4 related types and constants pub mod icmpv4 { /// ICMPv4 type value indicating a "Echo Reply" message (defined in [RFC 792](https://tools.ietf.org/html/rfc792)). pub const TYPE_ECHO_REPLY: u8 = 0; /// ICMPv4 type value indicating a "Destination Unreachable" message (defined in [RFC 792](https://tools.ietf.org/html/rfc792)). pub const TYPE_DEST_UNREACH: u8 = 3; /// ICMPv4 type value indicating a "Source Quench (Deprecated)" message (defined in in [RFC 792](https://tools.ietf.org/html/rfc792), deprecated in [RFC 6633](https://tools.ietf.org/html/rfc6633)). pub const TYPE_SOURCE_QUENCH: u8 = 4; /// ICMPv4 type value indicating a "Redirect" message (defined in [RFC 792](https://tools.ietf.org/html/rfc792)). pub const TYPE_REDIRECT: u8 = 5; /// ICMPv4 type value indicating a "Alternate Host Address (Deprecated)" message (deprecated in [RFC 6918](https://tools.ietf.org/html/rfc6918)). pub const TYPE_ALTERNATE_HOST_ADDRESS: u8 = 6; /// ICMPv4 type value indicating a "Echo Request" message (defined in [RFC 792](https://tools.ietf.org/html/rfc792)). pub const TYPE_ECHO_REQUEST: u8 = 8; /// ICMPv4 type value indicating a "Router Advertisement" message (defined in [RFC 1256](https://tools.ietf.org/html/rfc1256)). pub const TYPE_ROUTER_ADVERTISEMENT: u8 = 9; /// ICMPv4 type value indicating a "Router Solicitation" message (defined in [RFC 1256](https://tools.ietf.org/html/rfc1256)). pub const TYPE_ROUTER_SOLICITATION: u8 = 10; /// ICMPv4 type value indicating a "Time Exceeded" message (defined in [RFC 792](https://tools.ietf.org/html/rfc792)). pub const TYPE_TIME_EXCEEDED: u8 = 11; /// ICMPv4 type value indicating a "Parameter Problem" message (defined in [RFC 792](https://tools.ietf.org/html/rfc792)). pub const TYPE_PARAMETER_PROBLEM: u8 = 12; /// ICMPv4 type value indicating a "Timestamp" message (defined in [RFC 792](https://tools.ietf.org/html/rfc792)). pub const TYPE_TIMESTAMP: u8 = 13; /// ICMPv4 type value indicating a "Timestamp Reply" message (defined in [RFC 792](https://tools.ietf.org/html/rfc792)). pub const TYPE_TIMESTAMP_REPLY: u8 = 14; /// ICMPv4 type value indicating a "Information Request (Deprecated)" message (defined in in [RFC 792](https://tools.ietf.org/html/rfc792), deprecated in [RFC 6918](https://tools.ietf.org/html/rfc6918)). pub const TYPE_INFO_REQUEST: u8 = 15; /// ICMPv4 type value indicating a "Information Reply (Deprecated)" message (defined in in [RFC 792](https://tools.ietf.org/html/rfc792), deprecated in [RFC 6918](https://tools.ietf.org/html/rfc6918)). pub const TYPE_INFO_REPLY: u8 = 16; /// ICMPv4 type value indicating a "Address Mask Request (Deprecated)" message (defined in in [RFC 950](https://tools.ietf.org/html/rfc950), deprecated in [RFC 6918](https://tools.ietf.org/html/rfc6918)). pub const TYPE_ADDRESS: u8 = 17; /// ICMPv4 type value indicating a "Address Mask Reply (Deprecated)" message (defined in in [RFC 950](https://tools.ietf.org/html/rfc950), deprecated in [RFC 6918](https://tools.ietf.org/html/rfc6918)). pub const TYPE_ADDRESSREPLY: u8 = 18; /// ICMP destination unreachable code for "Net Unreachable" (defined in [RFC 792](https://tools.ietf.org/html/rfc792)) pub const CODE_DST_UNREACH_NET: u8 = 0; /// ICMP destination unreachable code for "Host Unreachable" (defined in [RFC 792](https://tools.ietf.org/html/rfc792)) pub const CODE_DST_UNREACH_HOST: u8 = 1; /// ICMP destination unreachable code for "Protocol Unreachable" (defined in [RFC 792](https://tools.ietf.org/html/rfc792)) pub const CODE_DST_UNREACH_PROTOCOL: u8 = 2; /// ICMP destination unreachable code for "Port Unreachable" (defined in [RFC 792](https://tools.ietf.org/html/rfc792)) pub const CODE_DST_UNREACH_PORT: u8 = 3; /// ICMP destination unreachable code for "Fragmentation Needed and Don't Fragment was Set" (defined in [RFC 792](https://tools.ietf.org/html/rfc792)) pub const CODE_DST_UNREACH_NEED_FRAG: u8 = 4; /// ICMP destination unreachable code for "Source Route Failed" (defined in [RFC 792](https://tools.ietf.org/html/rfc792)) pub const CODE_DST_UNREACH_SOURCE_ROUTE_FAILED: u8 = 5; /// ICMP destination unreachable code for "Destination Network Unknown" (defined in [RFC 1122](https://tools.ietf.org/html/rfc1122)) pub const CODE_DST_UNREACH_NET_UNKNOWN: u8 = 6; /// ICMP destination unreachable code for "Destination Host Unknown" (defined in [RFC 1122](https://tools.ietf.org/html/rfc1122)) pub const CODE_DST_UNREACH_HOST_UNKNOWN: u8 = 7; /// ICMP destination unreachable code for "Source Host Isolated" (defined in [RFC 1122](https://tools.ietf.org/html/rfc1122)) pub const CODE_DST_UNREACH_ISOLATED: u8 = 8; /// ICMP destination unreachable code for "Communication with Destination Network is Administratively Prohibited" (defined in [RFC 1122](https://tools.ietf.org/html/rfc1122)) pub const CODE_DST_UNREACH_NET_PROHIB: u8 = 9; /// ICMP destination unreachable code for "Communication with Destination Host is Administratively Prohibited" (defined in [RFC 1122](https://tools.ietf.org/html/rfc1122)) pub const CODE_DST_UNREACH_HOST_PROHIB: u8 = 10; /// ICMP destination unreachable code for "Destination Network Unreachable for Type of Service" (defined in [RFC 1122](https://tools.ietf.org/html/rfc1122)) pub const CODE_DST_UNREACH_TOS_NET: u8 = 11; /// ICMP destination unreachable code for "Destination Host Unreachable for Type of Service" (defined in [RFC 1122](https://tools.ietf.org/html/rfc1122)) pub const CODE_DST_UNREACH_TOS_HOST: u8 = 12; /// ICMP destination unreachable code for "Communication Administratively Prohibited" (defined in [RFC 1812](https://tools.ietf.org/html/rfc1812)) pub const CODE_DST_UNREACH_FILTER_PROHIB: u8 = 13; /// ICMP destination unreachable code for "Host Precedence Violation" (defined in [RFC 1812](https://tools.ietf.org/html/rfc1812)) pub const CODE_DST_UNREACH_HOST_PRECEDENCE_VIOLATION: u8 = 14; /// ICMP destination unreachable code for "Precedence cutoff in effect" (defined in [RFC 1812](https://tools.ietf.org/html/rfc1812)) pub const CODE_DST_UNREACH_PRECEDENCE_CUTOFF: u8 = 15; /// "Destination Unreachable" ICMP header for IPv4 (without the invoking packet). /// /// # Description in RFC 792: /// /// If, according to the information in the gateway's routing tables, /// the network specified in the internet destination field of a /// datagram is unreachable, e.g., the distance to the network is /// infinity, the gateway may send a destination unreachable message /// to the internet source host of the datagram. In addition, in some /// networks, the gateway may be able to determine if the internet /// destination host is unreachable. Gateways in these networks may /// send destination unreachable messages to the source host when the /// destination host is unreachable. /// /// If, in the destination host, the IP module cannot deliver the /// datagram because the indicated protocol module or process port is /// not active, the destination host may send a destination /// unreachable message to the source host. /// /// Another case is when a datagram must be fragmented to be forwarded /// by a gateway yet the Don't Fragment flag is on. In this case the /// gateway must discard the datagram and may return a destination /// unreachable message. /// /// Codes 0, 1, 4, and 5 may be received from a gateway. Codes 2 and /// 3 may be received from a host. #[derive(Clone, Debug, PartialEq, Eq)] pub enum DestUnreachableHeader { /// Network unreachable error. Network, /// Host unreachable error. Host, /// Transport protocol not supported error. Protocol, /// Port unreachable error. Port, /// Fragmentation would be needed but the don't fragment bit is set. FragmentationNeeded { next_hop_mtu: u16 }, /// Source Route Failed SourceRouteFailed, /// Destination Network Unknown (from [RFC 1122](https://tools.ietf.org/html/rfc1122)) NetworkUnknown, /// Destination Host Unknown (no route to host known) (from [RFC 1122](https://tools.ietf.org/html/rfc1122)) HostUnknown, /// Source Host Isolated - obsolete (from [RFC 1122](https://tools.ietf.org/html/rfc1122)) Isolated, /// Communication with Destination Network is Administratively Prohibited (from [RFC 1122](https://tools.ietf.org/html/rfc1122)) NetworkProhibited, /// Communication with Destination Host is Administratively Prohibited (from [RFC 1122](https://tools.ietf.org/html/rfc1122)) HostProhibited, /// Destination Network Unreachable for Type of Service (from [RFC 1122](https://tools.ietf.org/html/rfc1122)) TosNetwork, /// Destination Host Unreachable for Type of Service (from [RFC 1122](https://tools.ietf.org/html/rfc1122)) TosHost, /// Cannot forward because packet administratively filtered (from [RFC 1812](https://tools.ietf.org/html/rfc1812)) FilterProhibited, /// Required level of precidence not supported (from [RFC 1812](https://tools.ietf.org/html/rfc1812)) HostPrecedenceViolation, /// Packet was below minimum precidence (from [RFC 1812](https://tools.ietf.org/html/rfc1812)) PrecedenceCutoff, } impl DestUnreachableHeader { /// Tries to convert the code [`u8`] value and next_hop_mtu to a [`DestUnreachableHeader`] value. /// /// Returns [`None`] in case the code value is not known as a destination unreachable code. pub fn from_values(code_u8: u8, next_hop_mtu: u16) -> Option { use DestUnreachableHeader::*; match code_u8 { CODE_DST_UNREACH_NET => Some(Network), CODE_DST_UNREACH_HOST => Some(Host), CODE_DST_UNREACH_PROTOCOL => Some(Protocol), CODE_DST_UNREACH_PORT => Some(Port), CODE_DST_UNREACH_NEED_FRAG => Some(FragmentationNeeded { next_hop_mtu }), CODE_DST_UNREACH_SOURCE_ROUTE_FAILED => Some(SourceRouteFailed), CODE_DST_UNREACH_NET_UNKNOWN => Some(NetworkUnknown), CODE_DST_UNREACH_HOST_UNKNOWN => Some(HostUnknown), CODE_DST_UNREACH_ISOLATED => Some(Isolated), CODE_DST_UNREACH_NET_PROHIB => Some(NetworkProhibited), CODE_DST_UNREACH_HOST_PROHIB => Some(HostProhibited), CODE_DST_UNREACH_TOS_NET => Some(TosNetwork), CODE_DST_UNREACH_TOS_HOST => Some(TosHost), CODE_DST_UNREACH_FILTER_PROHIB => Some(FilterProhibited), CODE_DST_UNREACH_HOST_PRECEDENCE_VIOLATION => Some(HostPrecedenceViolation), CODE_DST_UNREACH_PRECEDENCE_CUTOFF => Some(PrecedenceCutoff), _ => None, } } /// Returns the icmp code value of the destination unreachable packet. #[inline] pub fn code_u8(&self) -> u8 { use DestUnreachableHeader::*; match self { Network => CODE_DST_UNREACH_NET, Host => CODE_DST_UNREACH_HOST, Protocol => CODE_DST_UNREACH_PROTOCOL, Port => CODE_DST_UNREACH_PORT, FragmentationNeeded { next_hop_mtu: _ } => CODE_DST_UNREACH_NEED_FRAG, SourceRouteFailed => CODE_DST_UNREACH_SOURCE_ROUTE_FAILED, NetworkUnknown => CODE_DST_UNREACH_NET_UNKNOWN, HostUnknown => CODE_DST_UNREACH_HOST_UNKNOWN, Isolated => CODE_DST_UNREACH_ISOLATED, NetworkProhibited => CODE_DST_UNREACH_NET_PROHIB, HostProhibited => CODE_DST_UNREACH_HOST_PROHIB, TosNetwork => CODE_DST_UNREACH_TOS_NET, TosHost => CODE_DST_UNREACH_TOS_HOST, FilterProhibited => CODE_DST_UNREACH_FILTER_PROHIB, HostPrecedenceViolation => CODE_DST_UNREACH_HOST_PRECEDENCE_VIOLATION, PrecedenceCutoff => CODE_DST_UNREACH_PRECEDENCE_CUTOFF, } } } /// ICMPv4 "Redirect" code value for "Redirect Datagram for the Network (or subnet)". pub const CODE_REDIRECT_FOR_NETWORK: u8 = 0; /// ICMPv4 "Redirect" code value for "Redirect Datagram for the Host". pub const CODE_REDIRECT_FOR_HOST: u8 = 1; /// ICMPv4 "Redirect" code value for "Redirect Datagram for the Type of Service and Network". pub const CODE_REDIRECT_TYPE_OF_SERVICE_AND_NETWORK: u8 = 2; /// ICMPv4 "Redirect" code value for "Redirect Datagram for the Type of Service and Host". pub const CODE_REDIRECT_TYPE_OF_SERVICE_AND_HOST: u8 = 3; /// Code value in an ICMPv4 Redirect message. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum RedirectCode { /// Redirect Datagram for the Network (or subnet) RedirectForNetwork = 0, /// Redirect Datagram for the Host RedirectForHost = 1, /// Redirect Datagram for the Type of Service and Network RedirectForTypeOfServiceAndNetwork = 2, /// Redirect datagrams for the Type of Service and Host RedirectForTypeOfServiceAndHost = 3, } impl RedirectCode { /// Tries to convert a code [`u8`] value to a [`RedirectCode`] value. /// /// Returns [`None`] in case the code value is not known as a redirect code. #[inline] pub fn from_u8(code_u8: u8) -> Option { use RedirectCode::*; match code_u8 { CODE_REDIRECT_FOR_NETWORK => Some(RedirectForNetwork), CODE_REDIRECT_FOR_HOST => Some(RedirectForHost), CODE_REDIRECT_TYPE_OF_SERVICE_AND_NETWORK => Some(RedirectForTypeOfServiceAndNetwork), CODE_REDIRECT_TYPE_OF_SERVICE_AND_HOST => Some(RedirectForTypeOfServiceAndHost), _ => None, } } /// Returns the [`u8`] value of the code. #[inline] pub fn code_u8(&self) -> u8 { *self as u8 } } #[derive(Clone, Debug, PartialEq, Eq)] pub struct RedirectHeader { pub code: RedirectCode, pub gateway_internet_address: [u8; 4], } /// ICMPv4 "Time Exceeded" code value for "Time to Live exceeded in Transit". pub const CODE_TIME_EXCEEDED_TTL_EXCEEDED_IN_TRANSIT: u8 = 0; /// ICMPv4 "Time Exceeded" code value for "Fragment Reassembly Time Exceeded". pub const CODE_TIME_EXCEEDED_FRAG_REASSEMBLY_TIME_EXCEEDED: u8 = 1; /// Code values for ICMPv4 time exceeded message. #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum TimeExceededCode { /// Time-to-live exceeded in transit. TtlExceededInTransit = 0, /// Fragment reassembly time exceeded. FragmentReassemblyTimeExceeded = 1, } impl TimeExceededCode { /// Tries to convert a code [`u8`] value to a [`TimeExceededCode`] value. /// /// Returns [`None`] in case the code value is not known as a time exceeded code. #[inline] pub fn from_u8(code_u8: u8) -> Option { use TimeExceededCode::*; match code_u8 { CODE_TIME_EXCEEDED_TTL_EXCEEDED_IN_TRANSIT => Some(TtlExceededInTransit), CODE_TIME_EXCEEDED_FRAG_REASSEMBLY_TIME_EXCEEDED => Some(FragmentReassemblyTimeExceeded), _ => None } } /// Returns the [`u8`] value of the code. #[inline] pub fn code_u8(&self) -> u8 { *self as u8 } } /// A ICMPv4 timestamp or timestamp response message. #[derive(Clone, Debug, PartialEq, Eq)] pub struct TimestampMessage { pub id: u16, pub seq: u16, pub originate_timestamp: u32, pub receive_timestamp: u32, pub transmit_timestamp: u32, } impl TimestampMessage { /// The size in bytes/octets of a timestamp request or timestamp response message. pub const SERIALIZED_SIZE: usize = 20; /// Decodes the timestamp message part of an ICMPv4 message. pub fn from_bytes(bytes: [u8;16]) -> TimestampMessage { TimestampMessage{ id: u16::from_be_bytes([bytes[0], bytes[1]]), seq: u16::from_be_bytes([bytes[2], bytes[3]]), originate_timestamp: u32::from_be_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]), receive_timestamp: u32::from_be_bytes([bytes[8], bytes[9], bytes[10], bytes[11]]), transmit_timestamp: u32::from_be_bytes([bytes[12], bytes[13], bytes[14], bytes[15]]), } } } /// ICMPv4 "Parameter Problem" code value for "Pointer indicates the error". pub const CODE_PARAMETER_PROBLEM_POINTER_INDICATES_ERROR: u8 = 0; /// ICMPv4 "Parameter Problem" code value for "Missing a Required Option". pub const CODE_PARAMETER_PROBLEM_MISSING_REQUIRED_OPTION: u8 = 1; /// ICMPv4 "Parameter Problem" code value for "Bad Length". pub const CODE_PARAMETER_PROBLEM_BAD_LENGTH: u8 = 2; /// The header of an ICMPv4 Parameter Problems (contents up to /// the offending ip header). #[derive(Clone, Debug, PartialEq, Eq)] pub enum ParameterProblemHeader { /// Identifies the octet where an error was detected. /// /// The value is the pointer pointing to the offending octet in /// the offending packet. PointerIndicatesError(u8), /// Missing a Required Option MissingRequiredOption, /// Bad Length BadLength, } impl ParameterProblemHeader { /// Tries to convert the code [`u8`] value and pointer to a [`ParameterProblemHeader`] value. /// /// Returns [`None`] in case the code value is not known as a parameter problem code. pub fn from_values(code_u8: u8, pointer: u8) -> Option { use ParameterProblemHeader::*; match code_u8 { CODE_PARAMETER_PROBLEM_POINTER_INDICATES_ERROR => Some(PointerIndicatesError(pointer)), CODE_PARAMETER_PROBLEM_MISSING_REQUIRED_OPTION => Some(MissingRequiredOption), CODE_PARAMETER_PROBLEM_BAD_LENGTH => Some(BadLength), _ => None, } } } } // mod icmpv4 use icmpv4::*; /// Starting contents of an ICMPv4 packet without the checksum. #[derive(Clone, Debug, PartialEq, Eq)] pub enum Icmpv4Type { /// In case of an unknown ICMP type and code combination is received the /// header elements are stored raw in this enum value. The `Unknown` value can /// also be passed to the `Icmpv4Header::write` function to write arbitrary ICMP /// packets. /// /// # What is part of the header for `Icmpv4Type::Unknown`? /// /// For unknown ICMP type & code combination the first 8 bytes are stored /// in the [`Icmpv4Header`] and the rest is stored in the payload /// ([`Icmpv4Slice::payload`] or [`PacketHeaders::payload`]). /// /// /// ```text /// 0 1 2 3 4 /// +---------------------------------------------------------------+ - /// | type_u8 | code_u8 | checksum (in Icmpv4Header) | | /// +---------------------------------------------------------------+ | part of header & type /// | bytes5to8 | ↓ /// +---------------------------------------------------------------+ - /// | | | /// ... ... ... | part of payload /// | | ↓ /// +---------------------------------------------------------------+ - /// ``` Unknown { /// ICMP type (present in the first byte of the ICMP packet). type_u8: u8, /// ICMP code (present in the 2nd byte of the ICMP packet). code_u8: u8, /// Bytes located at th 5th, 6th, 7th and 8th position of the ICMP packet. bytes5to8: [u8; 4], }, /// Response to an `EchoRequest` message (defined in RFC792). /// /// # What is part of the header for `Icmpv4Type::EchoReply`? /// /// For the [`Icmpv4Type::EchoReply`] type the first 8 bytes/octets of the /// ICMP packet are part of the header. This includes the `id` and `seq` /// fields. The data part of the ICMP Echo Reply packet is part of the /// payload ([`Icmpv4Slice::payload`] or [`PacketHeaders::payload`]) /// and not part of the [`Icmpv4Header`]. /// /// ```text /// 0 1 2 3 4 /// +---------------------------------------------------------------+ - /// | 0 | 0 | checksum (in Icmpv4Header) | | /// +---------------------------------------------------------------+ | part of header & type /// | [value].id | [value].seq | ↓ /// +---------------------------------------------------------------+ - /// | | | /// ... ... | part of payload /// | | ↓ /// +---------------------------------------------------------------+ - /// ``` EchoReply(IcmpEchoHeader), /// Message sent to inform the client that the destination is unreachable for /// some reason (defined in RFC792). /// /// # What is part of the header for `Icmpv4Type::DestinationUnreachable`? /// /// For the [`Icmpv4Type::DestinationUnreachable`] type the first 8 bytes/octets /// of the ICMP packet are part of the header. This includes the `next_hop_mtu` /// field. The `unused` part is not stored and droped. The offending packet /// is stored in the payload part of the packet ([`Icmpv4Slice::payload`] or /// [`PacketHeaders::payload`]) and is not part of the [`Icmpv4Header`]. /// /// ```text /// 0 1 2 3 4 /// +---------------------------------------------------------------+ - /// | 3 | 0 | checksum (in Icmpv4Header) | | /// +---------------------------------------------------------------+ | part of header & type /// |[v].next_hop...| | ↓ /// +---------------------------------------------------------------+ - /// | | | /// ... Internet Header + 64 bits of Original Data Datagram ... | part of payload /// | | ↓ /// +---------------------------------------------------------------+ - /// ``` DestinationUnreachable(DestUnreachableHeader), /// Requests data packets be sent on an alternative route (defined in RFC792). /// /// # What is part of the header for `Icmpv4Type::Redirect`? /// /// For the [`Icmpv4Type::Redirect`] type the first 8 bytes/octets of the ICMP /// packet are part of the header. This includes the `gateway_internet_address` /// field. The offending packet is stored in the payload part of the packet /// ([`Icmpv4Slice::payload`] or [`PacketHeaders::payload`]) and is not part of /// the [`Icmpv4Header`]. /// /// ```text /// 0 1 2 3 4 /// +---------------------------------------------------------------+ - /// | 5 | [value].code | checksum (in Icmpv4Header) | | /// +---------------------------------------------------------------+ | part of header & type /// | [value].gateway_internet_address | ↓ /// +---------------------------------------------------------------+ - /// | | | /// .. Internet Header + 64 bits of Original Data Datagram ... | part of payload /// | | ↓ /// +---------------------------------------------------------------+ - Redirect(RedirectHeader), /// Requesting an `EchoReply` from the receiver (defined in RFC792) /// /// # What is part of the header for `Icmpv4Type::EchoRequest`? /// /// For the [`Icmpv4Type::EchoRequest`] type the first 8 bytes/octets of the /// ICMP packet are part of the header. This includes the `id` and `seq` /// fields. The data part of the ICMP echo request packet is part of the payload /// ([`Icmpv4Slice::payload`] & [`PacketHeaders::payload`]) and not part of the /// [`Icmpv4Header`]. /// /// ```text /// 0 1 2 3 4 /// +---------------------------------------------------------------+ - /// | 8 | 0 | checksum (in Icmpv4Header) | | /// +---------------------------------------------------------------+ | part of header & type /// | [value].id | [value].seq | ↓ /// +---------------------------------------------------------------+ - /// | | | /// ... ... | part of payload /// | | ↓ /// +---------------------------------------------------------------+ - /// ``` EchoRequest(IcmpEchoHeader), /// Generated when a datagram had to be discarded due to the time to live field /// reaching zero (defined in RFC792). /// /// # What is part of the header for `Icmpv4Type::TimeExceeded`? /// /// For the `Icmpv4Type::TimeExceeded` type the first 8 bytes/octets of the ICMP /// packet are part of the header. The `unused` part is not stored and droped. /// The offending packet is stored in the payload part of the packet /// ([`Icmpv4Slice::payload`] & [`PacketHeaders::payload`]) and is not part of /// the [`Icmpv4Header`]. /// /// ```text /// 0 1 2 3 4 /// +---------------------------------------------------------------+ - /// | 11 | [value as u8] | checksum (in Icmpv4Header) | | /// +---------------------------------------------------------------+ | part of header & type /// | | ↓ /// +---------------------------------------------------------------+ - /// | | | /// ... Internet Header + 64 bits of Original Data Datagram ... | part of payload /// | | ↓ /// +---------------------------------------------------------------+ - TimeExceeded(TimeExceededCode), /// Sent if there is a problem with a parameter in a received packet. /// /// # What is part of the header for `Icmpv4Type::ParameterProblem`? /// /// For the `Icmpv4Type::ParameterProblem` type the first 8 bytes/octets of the ICMP /// packet are part of the header. The `unused` part is not stored and droped. /// The offending packet is stored in the payload part of the packet /// ([`Icmpv4Slice::payload`] & [`PacketHeaders::payload`]) and is not part of /// the [`Icmpv4Header`]. /// /// ```text /// 0 1 2 3 4 /// +---------------------------------------------------------------+ - /// | 12 | [v].code_u8() | checksum (in Icmpv4Header) | | /// +---------------------------------------------------------------+ | part of header & type /// |[value].pointer| | ↓ /// +---------------------------------------------------------------+ - /// | | | /// ... Internet Header + 64 bits of Original Data Datagram ... | part of payload /// | | ↓ /// +---------------------------------------------------------------+ - ParameterProblem(ParameterProblemHeader), /// Timestamp is used for time synchronization. /// /// # What is part of the header for `Icmpv4Type::TimestampRequest`? /// /// For the `Icmpv4Type::TimestampRequest` type the entire ICMP packet is /// contained within the header. The payload data is empty. TimestampRequest(TimestampMessage), /// Anwser to a `TimestampRequest` message. /// /// # What is part of the header for `Icmpv4Type::TimestampReply`? /// /// For the `Icmpv4Type::TimestampReply` type the entire ICMP packet is /// contained within the header. The payload data is empty. TimestampReply(TimestampMessage), } impl Icmpv4Type { /// Returns the length in bytes/octets of the header of /// this ICMPv4 message type. #[inline] pub fn header_len(&self) -> usize { use Icmpv4Type::*; match self { Unknown { type_u8: _, code_u8: _, bytes5to8: _, } | EchoReply(_) | DestinationUnreachable(_) | Redirect(_) | EchoRequest(_) | TimeExceeded(_) | ParameterProblem(_) => 8, TimestampRequest(_) | TimestampReply(_) => TimestampMessage::SERIALIZED_SIZE, } } /// If the ICMP type has a fixed size returns the number of /// bytes that should be present after the header of this type. #[inline] pub fn fixed_payload_size(&self) -> Option { use Icmpv4Type::*; match self { Unknown { type_u8: _, code_u8: _, bytes5to8: _, } | EchoReply(_) | DestinationUnreachable(_) | Redirect(_) | EchoRequest(_) | TimeExceeded(_) | ParameterProblem(_) => None, TimestampRequest(_) | TimestampReply(_) => Some(0), } } /// Calculate the ICMP checksum value. pub fn calc_checksum(&self, payload: &[u8]) -> u16 { use Icmpv4Type::*; match self { Unknown { type_u8, code_u8, bytes5to8, } => checksum::Sum16BitWords::new() .add_2bytes([*type_u8, *code_u8]) .add_4bytes(*bytes5to8), EchoReply(header) => checksum::Sum16BitWords::new() .add_2bytes([TYPE_ECHO_REPLY, 0]) .add_2bytes(header.id.to_be_bytes()) .add_2bytes(header.seq.to_be_bytes()), DestinationUnreachable(ref header) => { use DestUnreachableHeader::*; match header { Network => checksum::Sum16BitWords::new() .add_2bytes([TYPE_DEST_UNREACH, CODE_DST_UNREACH_NET]), Host => checksum::Sum16BitWords::new() .add_2bytes([TYPE_DEST_UNREACH, CODE_DST_UNREACH_HOST]), Protocol => checksum::Sum16BitWords::new() .add_2bytes([TYPE_DEST_UNREACH, CODE_DST_UNREACH_PROTOCOL]), Port => checksum::Sum16BitWords::new() .add_2bytes([TYPE_DEST_UNREACH, CODE_DST_UNREACH_PORT]), FragmentationNeeded { next_hop_mtu } => checksum::Sum16BitWords::new() .add_2bytes([TYPE_DEST_UNREACH, CODE_DST_UNREACH_NEED_FRAG]) .add_2bytes(next_hop_mtu.to_be_bytes()), SourceRouteFailed => checksum::Sum16BitWords::new() .add_2bytes([TYPE_DEST_UNREACH, CODE_DST_UNREACH_SOURCE_ROUTE_FAILED]), NetworkUnknown => checksum::Sum16BitWords::new() .add_2bytes([TYPE_DEST_UNREACH, CODE_DST_UNREACH_NET_UNKNOWN]), HostUnknown => checksum::Sum16BitWords::new() .add_2bytes([TYPE_DEST_UNREACH, CODE_DST_UNREACH_HOST_UNKNOWN]), Isolated => checksum::Sum16BitWords::new() .add_2bytes([TYPE_DEST_UNREACH, CODE_DST_UNREACH_ISOLATED]), NetworkProhibited => checksum::Sum16BitWords::new() .add_2bytes([TYPE_DEST_UNREACH, CODE_DST_UNREACH_NET_PROHIB]), HostProhibited => checksum::Sum16BitWords::new() .add_2bytes([TYPE_DEST_UNREACH, CODE_DST_UNREACH_HOST_PROHIB]), TosNetwork => checksum::Sum16BitWords::new() .add_2bytes([TYPE_DEST_UNREACH, CODE_DST_UNREACH_TOS_NET]), TosHost => checksum::Sum16BitWords::new() .add_2bytes([TYPE_DEST_UNREACH, CODE_DST_UNREACH_TOS_HOST]), FilterProhibited => checksum::Sum16BitWords::new() .add_2bytes([TYPE_DEST_UNREACH, CODE_DST_UNREACH_FILTER_PROHIB]), HostPrecedenceViolation => checksum::Sum16BitWords::new().add_2bytes([ TYPE_DEST_UNREACH, CODE_DST_UNREACH_HOST_PRECEDENCE_VIOLATION, ]), PrecedenceCutoff => checksum::Sum16BitWords::new() .add_2bytes([TYPE_DEST_UNREACH, CODE_DST_UNREACH_PRECEDENCE_CUTOFF]), } } Redirect(header) => checksum::Sum16BitWords::new() .add_2bytes([TYPE_REDIRECT, header.code as u8]) .add_4bytes(header.gateway_internet_address), EchoRequest(header) => checksum::Sum16BitWords::new() .add_2bytes([TYPE_ECHO_REQUEST, 0]) .add_2bytes(header.id.to_be_bytes()) .add_2bytes(header.seq.to_be_bytes()), TimeExceeded(code) => { checksum::Sum16BitWords::new().add_2bytes([TYPE_TIME_EXCEEDED, *code as u8]) } ParameterProblem(header) => { use ParameterProblemHeader::*; match header { PointerIndicatesError(pointer) => checksum::Sum16BitWords::new() .add_2bytes([ TYPE_PARAMETER_PROBLEM, CODE_PARAMETER_PROBLEM_POINTER_INDICATES_ERROR, ]) .add_2bytes([*pointer, 0]), MissingRequiredOption => checksum::Sum16BitWords::new().add_2bytes([ TYPE_PARAMETER_PROBLEM, CODE_PARAMETER_PROBLEM_MISSING_REQUIRED_OPTION, ]), BadLength => checksum::Sum16BitWords::new() .add_2bytes([TYPE_PARAMETER_PROBLEM, CODE_PARAMETER_PROBLEM_BAD_LENGTH]), } } TimestampRequest(msg) => checksum::Sum16BitWords::new() .add_2bytes([TYPE_TIMESTAMP, 0]) .add_2bytes(msg.id.to_be_bytes()) .add_2bytes(msg.seq.to_be_bytes()) .add_4bytes(msg.originate_timestamp.to_be_bytes()) .add_4bytes(msg.receive_timestamp.to_be_bytes()) .add_4bytes(msg.transmit_timestamp.to_be_bytes()), TimestampReply(msg) => checksum::Sum16BitWords::new() .add_2bytes([TYPE_TIMESTAMP_REPLY, 0]) .add_2bytes(msg.id.to_be_bytes()) .add_2bytes(msg.seq.to_be_bytes()) .add_4bytes(msg.originate_timestamp.to_be_bytes()) .add_4bytes(msg.receive_timestamp.to_be_bytes()) .add_4bytes(msg.transmit_timestamp.to_be_bytes()), } .add_slice(payload) .ones_complement() .to_be() } } /// A header of an ICMPv4 packet. /// /// What is part of the header depends on the ICMPv4 type /// and code. But usually the static sized elements are part /// of the header. #[derive(Clone, Debug, PartialEq, Eq)] pub struct Icmpv4Header { /// Type & type specific values & code. pub icmp_type: Icmpv4Type, /// Checksum in the ICMP header. pub checksum: u16, } impl Icmpv4Header { /// Minimum number of bytes/octets an Icmpv4Header takes up /// in serialized form. pub const MIN_SERIALIZED_SIZE: usize = 8; /// Maximum number of bytes/octets an Icmpv4Header takes up /// in serialized form. /// /// Currently this number is determined by the biggest /// supported ICMPv4 header type, which is currently the /// "Timestamp" and "Timestamp Reply Message". pub const MAX_SERIALIZED_SIZE: usize = 20; /// Constructs an [`Icmpv4Header`] using the given type /// and the checksum set to 0. pub fn new(icmp_type: Icmpv4Type) -> Icmpv4Header { // Note: will calculate checksum on send Icmpv4Header { icmp_type, checksum: 0, } } /// Creates a [`Icmpv4Header`] with a checksum calculated based on the given payload. pub fn with_checksum( icmp_type: Icmpv4Type, payload: &[u8], ) -> Icmpv4Header { let checksum = icmp_type.calc_checksum(payload); Icmpv4Header { icmp_type, checksum, } } /// Reads an icmp4 header from a slice directly and returns a tuple containing the resulting header & unused part of the slice. #[inline] pub fn from_slice(slice: &[u8]) -> Result<(Icmpv4Header, &[u8]), ReadError> { let header = Icmpv4Slice::from_slice(slice)?.header(); let rest = &slice[header.header_len()..]; Ok((header, rest)) } /// Reads an ICMPv4 header from the given reader. pub fn read(reader: &mut T) -> Result { let mut bytes = [0u8;Icmpv4Header::MAX_SERIALIZED_SIZE]; // try reading the initial 8 bytes reader.read_exact(&mut bytes[..8])?; match bytes[0] { TYPE_TIMESTAMP_REPLY | TYPE_TIMESTAMP => { if 0 == bytes[1] { // Timetamp messages need additional data read & it and // then set the slice correspondently reader.read_exact(&mut bytes[8..TimestampMessage::SERIALIZED_SIZE])?; Ok(Icmpv4Slice { slice: &bytes[..icmpv4::TimestampMessage::SERIALIZED_SIZE] }.header()) } else { // fallback to unknown Ok(Icmpv4Slice { slice: &bytes[..8] }.header()) } }, _ => Ok(Icmpv4Slice { slice: &bytes[..8] }.header()) } } /// Write the ICMPv4 header to the given writer. pub fn write(&self, writer: &mut T) -> Result<(), WriteError> { writer.write_all(&self.to_bytes()).map_err(WriteError::from) } /// Length in bytes/octets of this header type. #[inline] pub fn header_len(&self) -> usize { self.icmp_type.header_len() } /// If the ICMP type has a fixed size returns the number of /// bytes that should be present after the header of this type. #[inline] pub fn fixed_payload_size(&self) -> Option { self.icmp_type.fixed_payload_size() } /// Calculates & updates the checksum in the header. /// /// Note this method assumes that all unused bytes/octets /// are filled with zeroes. pub fn update_checksum(&mut self, payload: &[u8]) { self.checksum = self.icmp_type.calc_checksum(payload); } /// Converts the header to the on the wire bytes. #[rustfmt::skip] pub fn to_bytes(&self) -> ArrayVec { let checksum_be = self.checksum.to_be_bytes(); let re_zero = |type_u8: u8, code_u8: u8| -> ArrayVec { #[rustfmt::skip] let mut re = ArrayVec::from([ type_u8, code_u8, checksum_be[0], checksum_be[1], 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]); // SAFETY: Safe as u8 has no destruction behavior and as 8 is smaller then 20. unsafe { re.set_len(8); } re }; let re_2u16 = |type_u8: u8, code_u8: u8, a_u16: u16, b_u16: u16| -> ArrayVec { let a = a_u16.to_be_bytes(); let b = b_u16.to_be_bytes(); #[rustfmt::skip] let mut re = ArrayVec::from([ type_u8, code_u8, checksum_be[0], checksum_be[1], a[0], a[1], b[0], b[1], 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]); // SAFETY: Safe as u8 has no destruction behavior and as 8 is smaller then 20. unsafe { re.set_len(8); } re }; let re_4u8 = |type_u8: u8, code_u8: u8, bytes5to8: [u8; 4]| -> ArrayVec { #[rustfmt::skip] let mut re = ArrayVec::from([ type_u8, code_u8, checksum_be[0], checksum_be[1], bytes5to8[0], bytes5to8[1], bytes5to8[2], bytes5to8[3], 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]); // SAFETY: Safe as u8 has no destruction behavior and as 8 is smaller then 20. unsafe { re.set_len(8); } re }; let re_timestamp_msg = |type_u8: u8, msg: &TimestampMessage| -> ArrayVec { let id = msg.id.to_be_bytes(); let seq = msg.seq.to_be_bytes(); let o = msg.originate_timestamp.to_be_bytes(); let r = msg.receive_timestamp.to_be_bytes(); let t = msg.transmit_timestamp.to_be_bytes(); ArrayVec::from([ type_u8, 0, checksum_be[0], checksum_be[1], id[0], id[1], seq[0], seq[1], o[0], o[1], o[2], o[3], r[0], r[1], r[2], r[3], t[0], t[1], t[2], t[3], ]) }; use Icmpv4Type::*; match self.icmp_type { Unknown { type_u8, code_u8, bytes5to8, } => re_4u8(type_u8, code_u8, bytes5to8), EchoReply(echo) => re_2u16(TYPE_ECHO_REPLY, 0, echo.id, echo.seq), DestinationUnreachable(ref dest) => { use DestUnreachableHeader::*; match dest { Network => re_zero(TYPE_DEST_UNREACH, CODE_DST_UNREACH_NET), Host => re_zero(TYPE_DEST_UNREACH, CODE_DST_UNREACH_HOST), Protocol => re_zero(TYPE_DEST_UNREACH, CODE_DST_UNREACH_PROTOCOL), Port => re_zero(TYPE_DEST_UNREACH, CODE_DST_UNREACH_PORT), FragmentationNeeded { next_hop_mtu } => { let m_be = next_hop_mtu.to_be_bytes(); re_4u8( TYPE_DEST_UNREACH, CODE_DST_UNREACH_NEED_FRAG, [0, 0, m_be[0], m_be[1]], ) } SourceRouteFailed => re_zero(TYPE_DEST_UNREACH, CODE_DST_UNREACH_SOURCE_ROUTE_FAILED), NetworkUnknown => re_zero(TYPE_DEST_UNREACH, CODE_DST_UNREACH_NET_UNKNOWN), HostUnknown => re_zero(TYPE_DEST_UNREACH, CODE_DST_UNREACH_HOST_UNKNOWN), Isolated => re_zero(TYPE_DEST_UNREACH, CODE_DST_UNREACH_ISOLATED), NetworkProhibited => re_zero(TYPE_DEST_UNREACH, CODE_DST_UNREACH_NET_PROHIB), HostProhibited => re_zero(TYPE_DEST_UNREACH, CODE_DST_UNREACH_HOST_PROHIB), TosNetwork => re_zero(TYPE_DEST_UNREACH, CODE_DST_UNREACH_TOS_NET), TosHost => re_zero(TYPE_DEST_UNREACH, CODE_DST_UNREACH_TOS_HOST), FilterProhibited => re_zero(TYPE_DEST_UNREACH, CODE_DST_UNREACH_FILTER_PROHIB), HostPrecedenceViolation => re_zero( TYPE_DEST_UNREACH, CODE_DST_UNREACH_HOST_PRECEDENCE_VIOLATION, ), PrecedenceCutoff => { re_zero(TYPE_DEST_UNREACH, CODE_DST_UNREACH_PRECEDENCE_CUTOFF) } } } Redirect(ref msg) => { re_4u8(TYPE_REDIRECT, msg.code as u8, msg.gateway_internet_address) } EchoRequest(echo) => re_2u16(TYPE_ECHO_REQUEST, 0, echo.id, echo.seq), TimeExceeded(code) => re_zero(TYPE_TIME_EXCEEDED, code as u8), ParameterProblem(ref header) => { use ParameterProblemHeader::*; match header { PointerIndicatesError(pointer) => re_4u8( TYPE_PARAMETER_PROBLEM, CODE_PARAMETER_PROBLEM_POINTER_INDICATES_ERROR, [*pointer, 0, 0, 0], ), MissingRequiredOption => re_zero( TYPE_PARAMETER_PROBLEM, CODE_PARAMETER_PROBLEM_MISSING_REQUIRED_OPTION, ), BadLength => re_zero(TYPE_PARAMETER_PROBLEM, CODE_PARAMETER_PROBLEM_BAD_LENGTH), } } TimestampRequest(ref msg) => re_timestamp_msg(TYPE_TIMESTAMP, msg), TimestampReply(ref msg) => re_timestamp_msg(TYPE_TIMESTAMP_REPLY, msg), } } } /// A slice containing an ICMPv4 network package. /// /// Struct allows the selective read of fields in the ICMPv4 /// packet. #[derive(Clone, Debug, Eq, PartialEq)] pub struct Icmpv4Slice<'a> { slice: &'a [u8], } impl<'a> Icmpv4Slice<'a> { /// Creates a slice containing an ICMPv4 packet. /// /// # Errors /// /// The function will return an `Err` `ReadError::UnexpectedEndOfSlice` /// if the given slice is too small. #[inline] pub fn from_slice(slice: &'a [u8]) -> Result, ReadError> { // check length use ReadError::*; if slice.len() < Icmpv4Header::MIN_SERIALIZED_SIZE { return Err(UnexpectedEndOfSlice(Icmpv4Header::MIN_SERIALIZED_SIZE)); } // SAFETY: // Safe as it is previously checked that the slice has // at least the length of Icmpv4Header::MIN_SERIALIZED_SIZE (8). let icmp_type: u8 = unsafe { *slice.get_unchecked(0) }; let icmp_code: u8 = unsafe { *slice.get_unchecked(1) }; // check type specific length match icmp_type { TYPE_TIMESTAMP_REPLY | TYPE_TIMESTAMP => { if 0 == icmp_code && TimestampMessage::SERIALIZED_SIZE != slice.len() { return Err(UnexpectedLenOfSlice { expected: TimestampMessage::SERIALIZED_SIZE, actual: slice.len(), }); } } _ => {} } //done Ok(Icmpv4Slice { slice }) } /// Decode the header values into an [`Icmpv4Header`] struct. #[inline] pub fn header(&self) -> Icmpv4Header { let icmp_type = self.icmp_type(); Icmpv4Header { icmp_type, checksum: self.checksum(), } } /// Number of bytes/octets that will be converted into a /// [`Icmpv4Header`] when [`Icmpv4Slice::header`] gets called. #[inline] pub fn header_len(&self) -> usize { match self.type_u8() { TYPE_TIMESTAMP | TYPE_TIMESTAMP_REPLY => if 0 == self.code_u8() { TimestampMessage::SERIALIZED_SIZE } else { 8 }, _ => 8, } } /// Decode the header values (excluding the checksum) into an [`Icmpv4Type`] enum. pub fn icmp_type(&self) -> Icmpv4Type { use Icmpv4Type::*; unsafe fn timestamp_message(ptr: *const u8) -> TimestampMessage { TimestampMessage { id: get_unchecked_be_u16(ptr.add(4)), seq: get_unchecked_be_u16(ptr.add(6)), originate_timestamp: get_unchecked_be_u32(ptr.add(8)), receive_timestamp: get_unchecked_be_u32(ptr.add(12)), transmit_timestamp: get_unchecked_be_u32(ptr.add(16)), } } match self.type_u8() { TYPE_ECHO_REPLY => { if 0 == self.code_u8() { return EchoReply(IcmpEchoHeader::from_bytes(self.bytes5to8())); } } TYPE_DEST_UNREACH => { use DestUnreachableHeader::*; match self.code_u8() { CODE_DST_UNREACH_NET => return DestinationUnreachable(Network), CODE_DST_UNREACH_HOST => return DestinationUnreachable(Host), CODE_DST_UNREACH_PROTOCOL => return DestinationUnreachable(Protocol), CODE_DST_UNREACH_PORT => return DestinationUnreachable(Port), CODE_DST_UNREACH_NEED_FRAG => { return DestinationUnreachable(FragmentationNeeded { // SAFETY: // Safe as the contructor checks that the slice has // at least the length of Icmpv4Header::MIN_SERIALIZED_SIZE (8). next_hop_mtu: unsafe { get_unchecked_be_u16(self.slice.as_ptr().add(6)) }, }); } CODE_DST_UNREACH_SOURCE_ROUTE_FAILED => return DestinationUnreachable(SourceRouteFailed), CODE_DST_UNREACH_NET_UNKNOWN => return DestinationUnreachable(NetworkUnknown), CODE_DST_UNREACH_HOST_UNKNOWN => return DestinationUnreachable(HostUnknown), CODE_DST_UNREACH_ISOLATED => return DestinationUnreachable(Isolated), CODE_DST_UNREACH_NET_PROHIB => { return DestinationUnreachable(NetworkProhibited) } CODE_DST_UNREACH_HOST_PROHIB => return DestinationUnreachable(HostProhibited), CODE_DST_UNREACH_TOS_NET => return DestinationUnreachable(TosNetwork), CODE_DST_UNREACH_TOS_HOST => return DestinationUnreachable(TosHost), CODE_DST_UNREACH_FILTER_PROHIB => { return DestinationUnreachable(FilterProhibited) } CODE_DST_UNREACH_HOST_PRECEDENCE_VIOLATION => { return DestinationUnreachable(HostPrecedenceViolation) } CODE_DST_UNREACH_PRECEDENCE_CUTOFF => { return DestinationUnreachable(PrecedenceCutoff) } _ => {} } } TYPE_REDIRECT => { use RedirectCode::*; let code = match self.code_u8() { CODE_REDIRECT_FOR_NETWORK => Some(RedirectForNetwork), CODE_REDIRECT_FOR_HOST => Some(RedirectForHost), CODE_REDIRECT_TYPE_OF_SERVICE_AND_NETWORK => { Some(RedirectForTypeOfServiceAndNetwork) } CODE_REDIRECT_TYPE_OF_SERVICE_AND_HOST => Some(RedirectForTypeOfServiceAndHost), _ => None, }; if let Some(code) = code { return Redirect(RedirectHeader { code, gateway_internet_address: self.bytes5to8(), }); } } TYPE_ECHO_REQUEST => { if 0 == self.code_u8() { return EchoRequest(IcmpEchoHeader::from_bytes(self.bytes5to8())); } } TYPE_TIME_EXCEEDED => { use TimeExceededCode::*; match self.code_u8() { CODE_TIME_EXCEEDED_TTL_EXCEEDED_IN_TRANSIT => { return TimeExceeded(TtlExceededInTransit); } CODE_TIME_EXCEEDED_FRAG_REASSEMBLY_TIME_EXCEEDED => { return TimeExceeded(FragmentReassemblyTimeExceeded); } _ => {} } } TYPE_PARAMETER_PROBLEM => { use ParameterProblemHeader::*; match self.code_u8() { CODE_PARAMETER_PROBLEM_POINTER_INDICATES_ERROR => { return ParameterProblem(PointerIndicatesError( // SAFETY: // Safe as the contructor checks that the slice has // at least the length of Icmpv4Header::MIN_SERIALIZED_SIZE (8). unsafe { *self.slice.get_unchecked(4) }, )); } CODE_PARAMETER_PROBLEM_MISSING_REQUIRED_OPTION => { return ParameterProblem(MissingRequiredOption); } CODE_PARAMETER_PROBLEM_BAD_LENGTH => { return ParameterProblem(BadLength); } _ => {} } } TYPE_TIMESTAMP => { if 0 == self.code_u8() { // SAFETY: // Safe as the contructor checks that the slice has // the length of TimestampMessage::SERIALIZED_SIZE (20). unsafe { return TimestampRequest(timestamp_message(self.slice.as_ptr())); } } } TYPE_TIMESTAMP_REPLY => { if 0 == self.code_u8() { // SAFETY: // Safe as the contructor checks that the slice has // the length of TimestampMessage::SERIALIZED_SIZE (20). unsafe { return TimestampReply(timestamp_message(self.slice.as_ptr())); } } } _ => {} } Unknown { type_u8: self.type_u8(), code_u8: self.code_u8(), bytes5to8: self.bytes5to8(), } } /// Returns "type" value in the ICMPv4 header. #[inline] pub fn type_u8(&self) -> u8 { // SAFETY: // Safe as the contructor checks that the slice has // at least the length of Icmpv4Header::MIN_SERIALIZED_SIZE (8). unsafe { *self.slice.get_unchecked(0) } } /// Returns "code" value in the ICMPv4 header. #[inline] pub fn code_u8(&self) -> u8 { // SAFETY: // Safe as the contructor checks that the slice has // at least the length of Icmpv4Header::MIN_SERIALIZED_SIZE (8). unsafe { *self.slice.get_unchecked(1) } } /// Returns "checksum" value in the ICMPv4 header. #[inline] pub fn checksum(&self) -> u16 { // SAFETY: // Safe as the contructor checks that the slice has // at least the length of Icmpv4Header::MIN_SERIALIZED_SIZE (8). unsafe { get_unchecked_be_u16(self.slice.as_ptr().add(2)) } } /// Returns the bytes from position 4 till and including the 8th position /// in the ICMPv4 header. /// /// These bytes located at th 5th, 6th, 7th and 8th position of the ICMP /// packet can depending on the ICMPv4 type and code contain additional data. #[inline] pub fn bytes5to8(&self) -> [u8; 4] { // SAFETY: // Safe as the contructor checks that the slice has // at least the length of Icmpv4Header::MIN_SERIALIZED_SIZE (8). unsafe { [ *self.slice.get_unchecked(4), *self.slice.get_unchecked(5), *self.slice.get_unchecked(6), *self.slice.get_unchecked(7), ] } } /// Returns a slice to the bytes not covered by `.header()`. /// /// The contents of the slice returned by `payload()` depends on the type /// and code of the ICMP packet: /// /// | `.header().icmp_type` or `.icmp_type()` | Payload Content | /// |--------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------| /// | [`Icmpv4Type::EchoReply`]
[`Icmpv4Type::EchoRequest`]
| Data part of the echo message | /// | [`Icmpv4Type::DestinationUnreachable`]
[`Icmpv4Type::Redirect`]
[`Icmpv4Type::TimeExceeded`]
[`Icmpv4Type::ParameterProblem`]
| Internet Header + 64 bits of Original Data Datagram causing the ICMP message | /// | [`Icmpv4Type::TimestampRequest`]
[`Icmpv4Type::TimestampReply`]
| Nothing | /// | [`Icmpv4Type::Unknown`] | Everything after the 8th byte/octet of the ICMP packet. | #[inline] pub fn payload(&self) -> &'a [u8] { // explicitly inlined the code to determine the // length of the payload to make the cecking of the // usafe code easier. let header_len = match self.type_u8() { // SAFETY: // Lenght safe as the contructor checks that the slice has // the length of TimestampMessage::SERIALIZED_SIZE (20) // for the messages types TYPE_TIMESTAMP and TYPE_TIMESTAMP_REPLY. TYPE_TIMESTAMP | TYPE_TIMESTAMP_REPLY => if 0 == self.code_u8() { TimestampMessage::SERIALIZED_SIZE } else { 8 }, // SAFETY: // Lneght safe as the contructor checks that the slice has // at least the length of Icmpv6Header::MIN_SERIALIZED_SIZE(8) for // all message types. _ => 8, }; // SAFETY: // Lenghts have been depending on type in the constructor of the // ICMPv4Slice. unsafe { from_raw_parts(self.slice.as_ptr().add(header_len), self.slice.len() - header_len) } } /// Returns the slice containing the ICMPv4 packet. #[inline] pub fn slice(&self) -> &'a [u8] { self.slice } } etherparse-0.13.0/src/transport/icmpv6_impl.rs000064400000000000000000001417431046102023000174770ustar 00000000000000use super::super::*; use arrayvec::ArrayVec; use std::slice::from_raw_parts; /// Module containing ICMPv6 related types and constants pub mod icmpv6 { /// The maximum number of bytes/octets the ICMPv6 part of a packet can contain. /// /// The value is determined by the maximum value of the "Upper-Layer Packet Length" /// field. This field is not directly part of the packet but used during the checksum /// calculation in the pseudo header. /// /// The "Upper-Layer Packet Length" is represented as an `u32` and defined as /// "...the Payload Length from the IPv6 header, minus the length of any /// extension headers present between the IPv6 header and the upper-layer /// header" (according to RFC 2460 Section 8.1). In other words, the length of the /// ICMPv6 part of the packet. /// /// Therefor the maximum size of an ICMPv6 packet is `u32::MAX`. pub const MAX_ICMPV6_BYTE_LEN: usize = u32::MAX as usize; /// ICMPv6 type value indicating a "Destination Unreachable" message. pub const TYPE_DST_UNREACH: u8 = 1; /// ICMPv6 type value indicating a "Packet Too Big" message. pub const TYPE_PACKET_TOO_BIG: u8 = 2; /// ICMPv6 type value indicating a "Time Exceeded" message. pub const TYPE_TIME_EXCEEDED: u8 = 3; /// ICMPv6 type value indicating a "Parameter Problem" message. pub const TYPE_PARAMETER_PROBLEM: u8 = 4; /// ICMPv6 type value indicating an "Echo Request" message. pub const TYPE_ECHO_REQUEST: u8 = 128; /// ICMPv6 type value indicating an "Echo Reply" message. pub const TYPE_ECHO_REPLY: u8 = 129; /// ICMPv6 type value indicating a "Multicast Listener Query" message. pub const TYPE_MULTICAST_LISTENER_QUERY: u8 = 130; /// ICMPv6 type value indicating a "Multicast Listener Report" message. pub const TYPE_MULTICAST_LISTENER_REPORT: u8 = 131; /// ICMPv6 type value indicating a "Multicast Listener Done" message. pub const TYPE_MULTICAST_LISTENER_REDUCTION: u8 = 132; /// ICMPv6 type value indicating a "Router Solicitation" message. pub const TYPE_ROUTER_SOLICITATION: u8 = 133; /// ICMPv6 type value indicating a "Router Advertisement" message. pub const TYPE_ROUTER_ADVERTISEMENT: u8 = 134; /// ICMPv6 type value indicating a "Neighbor Solicitation" message. pub const TYPE_NEIGHBOR_SOLICITATION: u8 = 135; /// ICMPv6 type value indicating a "Neighbor Advertisement" message. pub const TYPE_NEIGHBOR_ADVERTISEMENT: u8 = 136; /// ICMPv6 type value indicating a "Redirect Message" message. pub const TYPE_REDIRECT_MESSAGE: u8 = 137; /// ICMPv6 type value indicating a "Router Renumbering" message. pub const TYPE_ROUTER_RENUMBERING: u8 = 138; /// ICMPv6 type value indicating a "Inverse Neighbor Discovery Solicitation" message. pub const TYPE_INVERSE_NEIGHBOR_DISCOVERY_SOLICITATION: u8 = 141; /// ICMPv6 type value indicating a "Inverse Neighbor Discovery Advertisement" message. pub const TYPE_INVERSE_NEIGHBOR_DISCOVERY_ADVERTISEMENT: u8 = 142; /// ICMPv6 type value indicating a "Extended Echo Request" message. pub const TYPE_EXT_ECHO_REQUEST: u8 = 160; /// ICMPv6 type value indicating a "Extended Echo Reply" message. pub const TYPE_EXT_ECHO_REPLY: u8 = 161; /// ICMPv6 destination unreachable code for "no route to destination". pub const CODE_DST_UNREACH_NO_ROUTE: u8 = 0; /// ICMPv6 destination unreachable code for "communication with /// destination administratively prohibited". pub const CODE_DST_UNREACH_PROHIBITED: u8 = 1; /// ICMPv6 destination unreachable code for "beyond scope of source address". pub const CODE_DST_UNREACH_BEYOND_SCOPE: u8 = 2; /// ICMPv6 destination unreachable code for "address unreachable". pub const CODE_DST_UNREACH_ADDR: u8 = 3; /// ICMPv6 destination unreachable code for "port unreachable". pub const CODE_DST_UNREACH_PORT: u8 = 4; /// ICMPv6 destination unreachable code for "source address failed ingress/egress policy". pub const CODE_DST_UNREACH_SOURCE_ADDRESS_FAILED_POLICY: u8 = 5; /// ICMPv6 destination unreachable code for "reject route to destination". pub const CODE_DST_UNREACH_REJECT_ROUTE_TO_DEST: u8 = 6; /// "Destination Unreachable" ICMPv6 code containing a reason why a /// destination could not be reached. /// /// # RFC 4443 Description: /// /// A Destination Unreachable message SHOULD be generated by a router, or /// by the IPv6 layer in the originating node, in response to a packet /// that cannot be delivered to its destination address for reasons other /// than congestion. (An ICMPv6 message MUST NOT be generated if a /// packet is dropped due to congestion.) #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum DestUnreachableCode { /// No route to destination NoRoute = 0, /// Communication with destination administratively prohibited Prohibited = 1, /// Beyond scope of source address BeyondScope = 2, /// Address unreachable Address = 3, /// Port unreachable Port = 4, /// Source address failed ingress/egress policy SourceAddressFailedPolicy = 5, /// Reject route to destination RejectRoute = 6, } impl DestUnreachableCode { /// Converts the u8 code value from an ICMPv6 "destination unreachable" /// packet to an `icmpv6::DestUnreachableCode` enum. /// /// # Example Usage: /// /// ``` /// use etherparse::{icmpv6, icmpv6::DestUnreachableCode}; /// let icmp_packet: [u8;8] = [ /// icmpv6::TYPE_DST_UNREACH, icmpv6::CODE_DST_UNREACH_PORT, 0, 0, /// 0, 0, 0, 0, /// ]; /// /// if icmpv6::TYPE_DST_UNREACH == icmp_packet[0] { /// let dst = icmpv6::DestUnreachableCode::from_u8( /// icmp_packet[1] /// ); /// assert_eq!(dst, Some(icmpv6::DestUnreachableCode::Port)); /// } /// ``` pub fn from_u8(code_u8: u8) -> Option { use DestUnreachableCode::*; match code_u8 { CODE_DST_UNREACH_NO_ROUTE => Some(NoRoute), CODE_DST_UNREACH_PROHIBITED => Some(Prohibited), CODE_DST_UNREACH_BEYOND_SCOPE => Some(BeyondScope), CODE_DST_UNREACH_ADDR => Some(Address), CODE_DST_UNREACH_PORT => Some(Port), CODE_DST_UNREACH_SOURCE_ADDRESS_FAILED_POLICY => Some(SourceAddressFailedPolicy), CODE_DST_UNREACH_REJECT_ROUTE_TO_DEST => Some(RejectRoute), _ => None, } } /// Returns the code value of the destination unreachable packet. /// /// This is the second byte of an ICMPv6 packet. #[inline] pub fn code_u8(&self) -> u8 { *self as u8 } } /// ICMPv6 time exceeded code for "hop limit exceeded in transit" pub const CODE_TIME_EXCEEDED_HOP_LIMIT_EXCEEDED: u8 = 0; /// ICMPv6 time exceeded code for "fragment reassembly time exceeded" pub const CODE_TIME_EXCEEDED_FRAGMENT_REASSEMBLY_TIME_EXCEEDED: u8 = 1; /// Code values for ICMPv6 time exceeded message. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum TimeExceededCode { /// "hop limit exceeded in transit" HopLimitExceeded = 0, /// "fragment reassembly time exceeded" FragmentReassemblyTimeExceeded = 1, } impl TimeExceededCode { /// Tries to convert a code [`u8`] value to a [`TimeExceededCode`] value. /// /// Returns [`None`] in case the code value is not known as a time exceeded code. #[inline] pub fn from_u8(code_u8: u8) -> Option { use TimeExceededCode::*; match code_u8 { CODE_TIME_EXCEEDED_HOP_LIMIT_EXCEEDED => Some(HopLimitExceeded), CODE_TIME_EXCEEDED_FRAGMENT_REASSEMBLY_TIME_EXCEEDED => { Some(FragmentReassemblyTimeExceeded) } _ => None, } } /// Returns the [`u8`] value of the code. #[inline] pub fn code_u8(&self) -> u8 { *self as u8 } } /// ICMPv6 parameter problem code for "erroneous header field encountered" (from [RFC 4443](https://tools.ietf.org/html/rfc4443)). pub const CODE_PARAM_PROBLEM_ERR_HEADER_FIELD: u8 = 0; /// ICMPv6 parameter problem code for "unrecognized Next Header type encountered" (from [RFC 4443](https://tools.ietf.org/html/rfc4443)). pub const CODE_PARAM_PROBLEM_UNRECOG_NEXT_HEADER: u8 = 1; /// ICMPv6 parameter problem code for "unrecognized IPv6 option encountered" (from [RFC 4443](https://tools.ietf.org/html/rfc4443)). pub const CODE_PARAM_PROBLEM_UNRECOG_IPV6_OPTION: u8 = 2; /// ICMPv6 parameter problem code for "IPv6 First Fragment has incomplete IPv6 Header Chain" (from [RFC 7112](https://tools.ietf.org/html/rfc7112)). pub const CODE_PARAM_PROBLEM_IPV6_FIRST_FRAG_INCOMP_HEADER_CHAIN: u8 = 3; /// ICMPv6 parameter problem code for "SR Upper-layer Header Error" (from [RFC 8754](https://tools.ietf.org/html/rfc8754)). pub const CODE_PARAM_PROBLEM_SR_UPPER_LAYER_HEADER_ERROR: u8 = 4; /// ICMPv6 parameter problem code for "Unrecognized Next Header type encountered by intermediate node" (from [RFC 8883](https://tools.ietf.org/html/rfc8883)). pub const CODE_PARAM_PROBLEM_UNRECOG_NEXT_HEADER_BY_INTERMEDIATE_NODE: u8 = 5; /// ICMPv6 parameter problem code for "Extension header too big" (from [RFC 8883](https://tools.ietf.org/html/rfc8883)). pub const CODE_PARAM_PROBLEM_EXT_HEADER_TOO_BIG: u8 = 6; /// ICMPv6 parameter problem code for "Extension header chain too long" (from [RFC 8883](https://tools.ietf.org/html/rfc8883)). pub const CODE_PARAM_PROBLEM_EXT_HEADER_CHAIN_TOO_LONG: u8 = 7; /// ICMPv6 parameter problem code for "Too many extension headers" (from [RFC 8883](https://tools.ietf.org/html/rfc8883)). pub const CODE_PARAM_PROBLEM_TOO_MANY_EXT_HEADERS: u8 = 8; /// ICMPv6 parameter problem code for "Too many options in extension header" (from [RFC 8883](https://tools.ietf.org/html/rfc8883)). pub const CODE_PARAM_PROBLEM_TOO_MANY_OPTIONS_EXT_HEADER: u8 = 9; /// ICMPv6 parameter problem code for "Option too big" (from [RFC 8883](https://tools.ietf.org/html/rfc8883)). pub const CODE_PARAM_PROBLEM_OPTION_TOO_BIG: u8 = 10; /// Code values for ICMPv6 parameter problem messages. /// /// Source: #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum ParameterProblemCode { /// Erroneous header field encountered (from [RFC 4443](https://tools.ietf.org/html/rfc4443)) ErroneousHeaderField = 0, /// Unrecognized Next Header type encountered (from [RFC 4443](https://tools.ietf.org/html/rfc4443)) UnrecognizedNextHeader = 1, /// Unrecognized IPv6 option encountered (from [RFC 4443](https://tools.ietf.org/html/rfc4443)) UnrecognizedIpv6Option = 2, /// IPv6 First Fragment has incomplete IPv6 Header Chain (from [RFC 7112](https://tools.ietf.org/html/rfc7112)) Ipv6FirstFragmentIncompleteHeaderChain = 3, /// SR Upper-layer Header Error (from [RFC 8754](https://tools.ietf.org/html/rfc8754)). SrUpperLayerHeaderError = 4, /// Unrecognized Next Header type encountered by intermediate node (from [RFC 8883](https://tools.ietf.org/html/rfc8883)) UnrecognizedNextHeaderByIntermediateNode = 5, /// Extension header too big (from [RFC 8883](https://tools.ietf.org/html/rfc8883)) ExtensionHeaderTooBig = 6, /// Extension header chain too long (from [RFC 8883](https://tools.ietf.org/html/rfc8883)) ExtensionHeaderChainTooLong = 7, /// Too many extension headers (from [RFC 8883](https://tools.ietf.org/html/rfc8883)) TooManyExtensionHeaders = 8, /// Too many options in extension header (from [RFC 8883](https://tools.ietf.org/html/rfc8883)) TooManyOptionsInExtensionHeader = 9, /// Option too big (from [RFC 8883](https://tools.ietf.org/html/rfc8883)) OptionTooBig = 10, } impl ParameterProblemCode { /// Tries to convert a code [`u8`] value to a [`ParameterProblemCode`] value. /// /// Returns [`None`] in case the code value is not known as a parameter problem code. pub fn from_u8(code_u8: u8) -> Option { use ParameterProblemCode::*; match code_u8 { CODE_PARAM_PROBLEM_ERR_HEADER_FIELD => Some(ErroneousHeaderField), CODE_PARAM_PROBLEM_UNRECOG_NEXT_HEADER => Some(UnrecognizedNextHeader), CODE_PARAM_PROBLEM_UNRECOG_IPV6_OPTION => Some(UnrecognizedIpv6Option), CODE_PARAM_PROBLEM_IPV6_FIRST_FRAG_INCOMP_HEADER_CHAIN => { Some(Ipv6FirstFragmentIncompleteHeaderChain) } CODE_PARAM_PROBLEM_SR_UPPER_LAYER_HEADER_ERROR => Some(SrUpperLayerHeaderError), CODE_PARAM_PROBLEM_UNRECOG_NEXT_HEADER_BY_INTERMEDIATE_NODE => { Some(UnrecognizedNextHeaderByIntermediateNode) } CODE_PARAM_PROBLEM_EXT_HEADER_TOO_BIG => Some(ExtensionHeaderTooBig), CODE_PARAM_PROBLEM_EXT_HEADER_CHAIN_TOO_LONG => Some(ExtensionHeaderChainTooLong), CODE_PARAM_PROBLEM_TOO_MANY_EXT_HEADERS => Some(TooManyExtensionHeaders), CODE_PARAM_PROBLEM_TOO_MANY_OPTIONS_EXT_HEADER => Some(TooManyOptionsInExtensionHeader), CODE_PARAM_PROBLEM_OPTION_TOO_BIG => Some(OptionTooBig), _ => None, } } /// Returns the [`u8`] value of the code. #[inline] pub fn code_u8(&self) -> u8 { *self as u8 } } /// ICMPv6 parameter problem header. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct ParameterProblemHeader { /// The code can offer additional informations about what kind of parameter /// problem caused the error. pub code: ParameterProblemCode, /// Identifies the octet offset within the /// invoking packet where the error was detected. /// /// The pointer will point beyond the end of the ICMPv6 /// packet if the field in error is beyond what can fit /// in the maximum size of an ICMPv6 error message. pub pointer: u32, } } // mod icmpv6 use icmpv6::*; /// Different kinds of ICMPv6 messages. /// /// The data stored in this enum corresponds to the statically sized data /// at the start of an ICMPv6 packet without the checksum. If you also need /// the checksum you can package and [`Icmpv6Type`] value in an [`Icmpv6Header`] /// struct. /// /// # Decoding Example (complete packet): /// /// ``` /// # use etherparse::{PacketBuilder}; /// # let mut builder = PacketBuilder:: /// # ethernet2([0;6], [0;6]) /// # .ipv6([0;16], [0;16], 20) /// # .icmpv6_echo_request(1, 2); /// # let payload = [1,2,3,4]; /// # let mut packet = Vec::::with_capacity(builder.size(payload.len())); /// # builder.write(&mut packet, &payload); /// use etherparse::PacketHeaders; /// /// let headers = PacketHeaders::from_ethernet_slice(&packet).unwrap(); /// /// use etherparse::TransportHeader::*; /// match headers.transport { /// Some(Icmpv6(icmp)) => { /// use etherparse::Icmpv6Type::*; /// match icmp.icmp_type { /// // Unknown is used when further decoding is currently not supported for the icmp type & code. /// // You can still further decode the packet on your own by using the raw data in this enum /// // together with `headers.payload` (contains the packet data after the 8th byte) /// Unknown{ type_u8, code_u8, bytes5to8 } => println!("Unknown{{ type_u8: {}, code_u8: {}, bytes5to8: {:?} }}", type_u8, code_u8, bytes5to8), /// DestinationUnreachable(header) => println!("{:?}", header), /// PacketTooBig { mtu } => println!("TimeExceeded{{ mtu: {} }}", mtu), /// TimeExceeded(code) => println!("{:?}", code), /// ParameterProblem(header) => println!("{:?}", header), /// EchoRequest(header) => println!("{:?}", header), /// EchoReply(header) => println!("{:?}", header), /// } /// }, /// _ => {}, /// } /// ``` /// /// # Encoding Example (only ICMPv6 part) /// /// To get the on wire bytes of an Icmpv6Type it needs to get packaged /// into a [`Icmpv6Header`] so the checksum gets calculated. /// /// ``` /// # use etherparse::Ipv6Header; /// # let ip_header: Ipv6Header = Default::default(); /// # let invoking_packet : [u8;0] = []; /// /// use etherparse::{Icmpv6Type, icmpv6::DestUnreachableCode}; /// let t = Icmpv6Type::DestinationUnreachable( /// DestUnreachableCode::Address /// ); /// /// // to calculate the checksum the ip header and the payload /// // (in case of dest unreachable the invoking packet) are needed /// let header = t.to_header(ip_header.source, ip_header.destination, &invoking_packet).unwrap(); /// /// // an ICMPv6 packet is composed of the header and payload /// let mut packet = Vec::with_capacity(header.header_len() + invoking_packet.len()); /// packet.extend_from_slice(&header.to_bytes()); /// packet.extend_from_slice(&invoking_packet); /// # /// # { /// # let checksum_be = header.checksum.to_be_bytes(); /// # assert_eq!( /// # &packet, /// # &[ /// # header.icmp_type.type_u8(), /// # header.icmp_type.code_u8(), /// # checksum_be[0], /// # checksum_be[1], /// # 0,0,0,0 /// # ] /// # ); /// # } /// ``` #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Icmpv6Type { /// In case of an unknown icmp type is received the header elements of /// the first 8 bytes/octets are stored raw in this enum value. /// /// # What is part of the header for `Icmpv6Type::Unknown`? /// /// For unknown ICMPv6 type & code combination the first 8 bytes are stored /// in the [`Icmpv6Header`] and the rest is stored in the payload /// ([`Icmpv6Slice::payload`] or [`PacketHeaders::payload`]). /// /// /// ```text /// 0 1 2 3 4 /// +---------------------------------------------------------------+ - /// | type_u8 | code_u8 | checksum (in Icmpv6Header) | | /// +---------------------------------------------------------------+ | part of header & type /// | bytes5to8 | ↓ /// +---------------------------------------------------------------+ - /// | | | /// ... ... ... | part of payload /// | | ↓ /// +---------------------------------------------------------------+ - /// ``` Unknown { /// ICMPv6 type (present in the first byte of the ICMPv6 packet). type_u8: u8, /// ICMPv6 code (present in the 2nd byte of the ICMPv6 packet). code_u8: u8, /// Bytes located at th 5th, 6th, 7th and 8th position of the ICMP packet. bytes5to8: [u8; 4], }, /// Message sent to inform the client that the destination is unreachable for /// some reason. /// /// # What is part of the header for `Icmpv6Type::DestinationUnreachable`? /// /// For the `Icmpv6Type::DestinationUnreachable` type the first 8 bytes/octets of the ICMPv6 /// packet are part of the header. The `unused` part is not stored and droped. /// The offending packet is stored in the payload part of the packet /// ([`Icmpv6Slice::payload`] & [`PacketHeaders::payload`]) and is not part of /// the [`Icmpv6Header`]. /// /// ```text /// 0 1 2 3 4 /// +---------------------------------------------------------------+ - /// | 1 | [value as u8] | checksum (in Icmpv6Header) | | /// +---------------------------------------------------------------+ | part of header & type /// | | ↓ /// +---------------------------------------------------------------+ - /// | | | /// | ... | /// | | ↓ /// +---------------------------------------------------------------+ - /// ``` /// /// # RFC 4443 Description /// /// A Destination Unreachable message SHOULD be generated by a router, or /// by the IPv6 layer in the originating node, in response to a packet /// that cannot be delivered to its destination address for reasons other /// than congestion. (An ICMPv6 message MUST NOT be generated if a /// packet is dropped due to congestion.) DestinationUnreachable(icmpv6::DestUnreachableCode), /// Sent if a packet to too big to be forwarded. /// /// # What is part of the header for `Icmpv6Type::PacketTooBig`? /// /// For the `Icmpv6Type::PacketTooBig` type the first 8 bytes/octets of the ICMPv6 /// packet are part of the header. The offending packet is stored in the payload part of the packet /// ([`Icmpv6Slice::payload`] & [`PacketHeaders::payload`]) and is not part of /// the [`Icmpv6Header`]. /// /// ```text /// 0 1 2 3 4 /// +---------------------------------------------------------------+ - /// | 2 | 0 | checksum (in Icmpv6Header) | | /// +---------------------------------------------------------------+ | part of header & type /// | mtu | ↓ /// +---------------------------------------------------------------+ - /// | | | /// | ... | /// | | ↓ /// +---------------------------------------------------------------+ - /// ``` /// /// # RFC 4443 Description /// /// A Packet Too Big MUST be sent by a router in response to a packet /// that it cannot forward because the packet is larger than the MTU of /// the outgoing link. The information in this message is used as part /// of the Path MTU Discovery process. PacketTooBig { /// The Maximum Transmission Unit of the next-hop link. mtu: u32, }, /// Generated when a datagram had to be discarded due to the hop limit field /// reaching zero. /// /// # What is part of the header for `Icmpv6Type::TimeExceeded`? /// /// For the `Icmpv6Type::TimeExceeded` type the first 8 bytes/octets of the ICMPv6 /// packet are part of the header. The `unused` part is not stored and droped. /// The offending packet is stored in the payload part of the packet /// ([`Icmpv6Slice::payload`] & [`PacketHeaders::payload`]) and is not part of /// the [`Icmpv6Header`]. /// /// ```text /// 0 1 2 3 4 /// +---------------------------------------------------------------+ - /// | 3 | [value as u8] | checksum (in Icmpv6Header) | | /// +---------------------------------------------------------------+ | part of header & type /// | | ↓ /// +---------------------------------------------------------------+ - /// | | | /// | ... | /// | | ↓ /// +---------------------------------------------------------------+ - /// ``` /// /// # RFC 4443 Description /// /// If a router receives a packet with a Hop Limit of zero, or if a /// router decrements a packet's Hop Limit to zero, it MUST discard the /// packet and originate an ICMPv6 Time Exceeded message with Code 0 to /// the source of the packet. This indicates either a routing loop or /// too small an initial Hop Limit value. /// /// An ICMPv6 Time Exceeded message with Code 1 is used to report /// fragment reassembly timeout, as specified in [IPv6, Section 4.5]. TimeExceeded(icmpv6::TimeExceededCode), /// Sent if there is a problem with a parameter in a received packet. /// /// # What is part of the header for `Icmpv6Type::ParameterProblem`? /// /// For the `Icmpv6Type::ParameterProblem` type the first 8 bytes/octets of the ICMPv6 /// packet are part of the header. The `unused` part is not stored and droped. /// The offending packet is stored in the payload part of the packet /// ([`Icmpv6Slice::payload`] & [`PacketHeaders::payload`]) and is not part of /// the [`Icmpv6Header`]. /// /// ```text /// 0 1 2 3 4 /// +---------------------------------------------------------------+ - /// | 4 | [value].code | checksum (in Icmpv6Header) | | /// +---------------------------------------------------------------+ | part of header & type /// | [value].pointer | ↓ /// +---------------------------------------------------------------+ - /// | | | /// | ... | /// | | ↓ /// +---------------------------------------------------------------+ - /// ``` /// /// # RFC 4443 Description /// /// If an IPv6 node processing a packet finds a problem with a field in /// the IPv6 header or extension headers such that it cannot complete /// processing the packet, it MUST discard the packet and SHOULD /// originate an ICMPv6 Parameter Problem message to the packet's source, /// indicating the type and location of the problem. ParameterProblem(icmpv6::ParameterProblemHeader), /// Requesting an `EchoReply` from the receiver. /// /// # What is part of the header for `Icmpv6Type::EchoRequest`? /// /// For the [`Icmpv6Type::EchoRequest`] type the first 8 bytes/octets of the /// ICMPv6 packet are part of the header. This includes the `id` and `seq` /// fields. The data part of the ICMP echo request packet is part of the payload /// ([`Icmpv6Slice::payload`] & [`PacketHeaders::payload`]) and not part of the /// [`Icmpv6Header`]. /// /// ```text /// 0 1 2 3 4 /// +---------------------------------------------------------------+ - /// | 128 | 0 | checksum (in Icmpv6Header) | | /// +---------------------------------------------------------------+ | part of header & type /// | [value].id | [value].seq | ↓ /// +---------------------------------------------------------------+ - /// | | | /// ... ... | part of payload /// | | ↓ /// +---------------------------------------------------------------+ - /// ``` /// /// # RFC 4443 Description /// /// Every node MUST implement an ICMPv6 Echo responder function that /// receives Echo Requests and originates corresponding Echo Replies. A /// node SHOULD also implement an application-layer interface for /// originating Echo Requests and receiving Echo Replies, for diagnostic /// purposes. EchoRequest(IcmpEchoHeader), /// Response to an `EchoRequest` message. /// /// # What is part of the header for `Icmpv6Type::EchoReply`? /// /// For the [`Icmpv6Type::EchoReply`] type the first 8 bytes/octets of the /// ICMPv6 packet are part of the header. This includes the `id` and `seq` /// fields. The data part of the ICMP echo request packet is part of the payload /// ([`Icmpv6Slice::payload`] & [`PacketHeaders::payload`]) and not part of the /// [`Icmpv6Header`]. /// /// ```text /// 0 1 2 3 4 /// +---------------------------------------------------------------+ - /// | 129 | 0 | checksum (in Icmpv6Header) | | /// +---------------------------------------------------------------+ | part of header & type /// | [value].id | [value].seq | ↓ /// +---------------------------------------------------------------+ - /// | | | /// ... ... | part of payload /// | | ↓ /// +---------------------------------------------------------------+ - /// ``` /// /// # RFC 4443 Description /// /// Every node MUST implement an ICMPv6 Echo responder function that /// receives Echo Requests and originates corresponding Echo Replies. A /// node SHOULD also implement an application-layer interface for /// originating Echo Requests and receiving Echo Replies, for diagnostic /// purposes. /// /// The source address of an Echo Reply sent in response to a unicast /// Echo Request message MUST be the same as the destination address of /// that Echo Request message. /// /// An Echo Reply SHOULD be sent in response to an Echo Request message /// sent to an IPv6 multicast or anycast address. In this case, the /// source address of the reply MUST be a unicast address belonging to /// the interface on which the Echo Request message was received. /// /// The data received in the ICMPv6 Echo Request message MUST be returned /// entirely and unmodified in the ICMPv6 Echo Reply message. EchoReply(IcmpEchoHeader), } impl Icmpv6Type { /// Returns the type value (first byte of the ICMPv6 header) of this type. #[inline] pub fn type_u8(&self) -> u8 { use Icmpv6Type::*; match self { Unknown { type_u8, code_u8: _, bytes5to8: _, } => *type_u8, DestinationUnreachable(_) => TYPE_DST_UNREACH, PacketTooBig { mtu: _ } => TYPE_PACKET_TOO_BIG, TimeExceeded(_) => TYPE_TIME_EXCEEDED, ParameterProblem(_) => TYPE_PARAMETER_PROBLEM, EchoRequest(_) => TYPE_ECHO_REQUEST, EchoReply(_) => TYPE_ECHO_REPLY, } } /// Returns the code value (second byte of the ICMPv6 header) of this type. #[inline] pub fn code_u8(&self) -> u8 { use Icmpv6Type::*; match self { Unknown { type_u8: _, code_u8, bytes5to8: _, } => *code_u8, DestinationUnreachable(code) => code.code_u8(), PacketTooBig { mtu: _ } => 0, TimeExceeded(code) => code.code_u8(), ParameterProblem(header) => header.code.code_u8(), EchoRequest(_) => 0, EchoReply(_) => 0, } } /// Calculates the checksum of the ICMPv6 header. /// ///

/// Warning: Don't use this method to verfy if a checksum of a /// received packet is correct. This method assumes that all unused bytes are /// filled with zeros. If this is not the case the computed checksum value will /// will be incorrect for a received packet. /// /// If you want to verify that a received packet has a correct checksum use /// [`Icmpv6Slice::is_checksum_valid`] instead. ///

pub fn calc_checksum( &self, source_ip: [u8; 16], destination_ip: [u8; 16], payload: &[u8], ) -> Result { // check that the total length fits into the field // // Note according to RFC 2460 the "Upper-Layer Packet Length" used // in the checksum calculation, for protocols that don't contain // their own length information (like ICMPv6), is "the Payload Length // from the IPv6 header, minus the length of any extension headers present // between the IPv6 header and the upper-layer header." let max_payload_len: usize = (std::u32::MAX as usize) - self.header_len(); if max_payload_len < payload.len() { return Err(ValueError::Ipv6PayloadLengthTooLarge(payload.len())); } let msg_len = payload.len() + self.header_len(); // calculate the checksum // NOTE: rfc4443 section 2.3 - Icmp6 *does* use a pseudoheader, // unlike Icmp4 let pseudo_sum = checksum::Sum16BitWords::new() .add_16bytes(source_ip) .add_16bytes(destination_ip) .add_2bytes([0, ip_number::IPV6_ICMP]) .add_4bytes((msg_len as u32).to_be_bytes()); use Icmpv6Type::*; Ok( match self { Unknown { type_u8, code_u8, bytes5to8, } => { pseudo_sum.add_2bytes([*type_u8, *code_u8]) .add_4bytes(*bytes5to8) }, DestinationUnreachable(header) => { pseudo_sum.add_2bytes([TYPE_DST_UNREACH, header.code_u8()]) }, PacketTooBig { mtu } => { pseudo_sum.add_2bytes([TYPE_PACKET_TOO_BIG, 0]) .add_4bytes(mtu.to_be_bytes()) }, TimeExceeded(code) => { pseudo_sum.add_2bytes([TYPE_TIME_EXCEEDED, code.code_u8()]) }, ParameterProblem(header) => { pseudo_sum.add_2bytes([TYPE_PARAMETER_PROBLEM, header.code.code_u8()]) .add_4bytes(header.pointer.to_be_bytes()) } EchoRequest(echo) => { pseudo_sum.add_2bytes([TYPE_ECHO_REQUEST, 0]) .add_4bytes(echo.to_bytes()) } EchoReply(echo) => { pseudo_sum.add_2bytes([TYPE_ECHO_REPLY, 0]) .add_4bytes(echo.to_bytes()) } } .add_slice(payload) .ones_complement() .to_be() ) } /// Creates a header with the correct checksum. pub fn to_header( self, source_ip: [u8; 16], destination_ip: [u8; 16], payload: &[u8], ) -> Result { Ok(Icmpv6Header { checksum: self.calc_checksum(source_ip, destination_ip, payload)?, icmp_type: self, }) } /// Serialized length of the header in bytes/octets. /// /// Note that this size is not the size of the entire /// ICMPv6 packet but only the header. pub fn header_len(&self) -> usize { use Icmpv6Type::*; match self { Unknown { type_u8: _, code_u8: _, bytes5to8: _, } | DestinationUnreachable(_) | PacketTooBig{ mtu: _ } | TimeExceeded(_) | ParameterProblem(_) | EchoRequest(_) | EchoReply(_) => 8, } } /// If the ICMP type has a fixed size returns the number of /// bytes that should be present after the header of this type. #[inline] pub fn fixed_payload_size(&self) -> Option { use Icmpv6Type::*; match self { Unknown { type_u8: _, code_u8: _, bytes5to8: _, } | DestinationUnreachable(_) | PacketTooBig{ mtu: _ } | TimeExceeded(_) | ParameterProblem(_) | EchoRequest(_) | EchoReply(_) => None, } } } /// The statically sized data at the start of an ICMPv6 packet (at least the first 8 bytes of an ICMPv6 packet). #[derive(Clone, Debug, PartialEq, Eq)] pub struct Icmpv6Header { /// Type & type specific values & code. pub icmp_type: Icmpv6Type, /// Checksum in the ICMPv6 header. pub checksum: u16, } impl Icmpv6Header { /// Minimum number of bytes an ICMP header needs to have. /// /// Note that minimum size can be larger depending on /// the type and code. pub const MIN_SERIALIZED_SIZE: usize = 8; /// Maximum number of bytes/octets an Icmpv6Header takes up /// in serialized form. /// /// Currently this number is determined by the biggest /// planned ICMPv6 header type, which is currently the /// "Neighbor Discovery Protocol" "Redirect" message. pub const MAX_SERIALIZED_SIZE: usize = 8 + 16 + 16; /// Setups a new header with the checksum beeing set to 0. #[inline] pub fn new(icmp_type: Icmpv6Type) -> Icmpv6Header { Icmpv6Header { icmp_type, checksum: 0, // will be filled in later } } /// Creates a [`Icmpv6Header`] with a checksum calculated based /// on the given payload & ip addresses from the IPv6 header. pub fn with_checksum( icmp_type: Icmpv6Type, source_ip: [u8; 16], destination_ip: [u8; 16], payload: &[u8], ) -> Result { let checksum = icmp_type.calc_checksum(source_ip, destination_ip, payload)?; Ok(Icmpv6Header { icmp_type, checksum, }) } /// Reads an icmp6 header from a slice directly and returns a tuple /// containing the resulting header & unused part of the slice. #[inline] pub fn from_slice(slice: &[u8]) -> Result<(Icmpv6Header, &[u8]), ReadError> { let header = Icmpv6Slice::from_slice(slice)?.header(); let len = header.header_len(); Ok((header, &slice[len..])) } /// Read a ICMPv6 header from the given reader pub fn read(reader: &mut T) -> Result { // read the initial 8 bytes let mut start = [0u8;8]; reader.read_exact(&mut start)?; Ok(Icmpv6Slice{ slice: &start }.header()) } /// Write the ICMPv6 header to the given writer. pub fn write(&self, writer: &mut T) -> Result<(), WriteError> { writer.write_all(&self.to_bytes()).map_err(WriteError::from) } /// Serialized length of the header in bytes/octets. /// /// Note that this size is not the size of the entire /// ICMPv6 packet but only the header. #[inline] pub fn header_len(&self) -> usize { self.icmp_type.header_len() } /// If the ICMP type has a fixed size returns the number of /// bytes that should be present after the header of this type. #[inline] pub fn fixed_payload_size(&self) -> Option { self.icmp_type.fixed_payload_size() } /// Updates the checksum of the header. pub fn update_checksum( &mut self, source_ip: [u8; 16], destination_ip: [u8; 16], payload: &[u8], ) -> Result<(), ValueError> { self.checksum = self .icmp_type .calc_checksum(source_ip, destination_ip, payload)?; Ok(()) } /// Returns the header on the wire bytes. #[inline] pub fn to_bytes(&self) -> ArrayVec { let checksum_be = self.checksum.to_be_bytes(); let return_trivial = |type_u8: u8, code_u8: u8| -> ArrayVec { #[rustfmt::skip] let mut re = ArrayVec::from([ type_u8, code_u8, checksum_be[0], checksum_be[1], 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]); // SAFETY: Safe as u8 has no destruction behavior and as 8 is smaller then 20. unsafe { re.set_len(8); } re }; let return_4u8 = |type_u8: u8, code_u8: u8, bytes5to8: [u8;4]| -> ArrayVec { #[rustfmt::skip] let mut re = ArrayVec::from([ type_u8, code_u8, checksum_be[0], checksum_be[1], bytes5to8[0], bytes5to8[1], bytes5to8[2], bytes5to8[3], 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]); // SAFETY: Safe as u8 has no destruction behavior and as 8 is smaller then 20. unsafe { re.set_len(8); } re }; use Icmpv6Type::*; match self.icmp_type { Unknown { type_u8, code_u8, bytes5to8, } => { return_4u8(type_u8, code_u8, bytes5to8) }, DestinationUnreachable(header) => { return_trivial(TYPE_DST_UNREACH, header.code_u8()) }, PacketTooBig { mtu } => { return_4u8(TYPE_PACKET_TOO_BIG, 0, mtu.to_be_bytes()) }, TimeExceeded(code) => { return_trivial(TYPE_TIME_EXCEEDED, code.code_u8()) }, ParameterProblem(header) => { return_4u8(TYPE_PARAMETER_PROBLEM, header.code.code_u8(), header.pointer.to_be_bytes()) } EchoRequest(echo) => { return_4u8(TYPE_ECHO_REQUEST, 0, echo.to_bytes()) }, EchoReply(echo) => { return_4u8(TYPE_ECHO_REPLY, 0, echo.to_bytes()) }, } } } /// A slice containing an ICMPv6 network package. /// /// Struct allows the selective read of fields in the ICMPv6 /// packet. #[derive(Clone, Debug, Eq, PartialEq)] pub struct Icmpv6Slice<'a> { slice: &'a [u8], } impl<'a> Icmpv6Slice<'a> { /// Creates a slice containing an ICMPv6 packet. /// /// # Errors /// /// The function will return an `Err` `ReadError::UnexpectedEndOfSlice` /// if the given slice is too small (smaller then `Icmpv6Header::MIN_SERIALIZED_SIZE`) or /// too large (bigger then `icmpv6::MAX_ICMPV6_BYTE_LEN`). #[inline] pub fn from_slice(slice: &'a [u8]) -> Result, ReadError> { //check length use crate::ReadError::*; if slice.len() < Icmpv6Header::MIN_SERIALIZED_SIZE { return Err(UnexpectedEndOfSlice(Icmpv6Header::MIN_SERIALIZED_SIZE)); } if slice.len() > icmpv6::MAX_ICMPV6_BYTE_LEN { return Err(Icmpv6PacketTooBig(slice.len())); } //done Ok(Icmpv6Slice { slice }) } /// Decode the header fields and copy the results to a [`Icmpv6Header`] struct. #[inline] pub fn header(&self) -> Icmpv6Header { Icmpv6Header { icmp_type: self.icmp_type(), checksum: self.checksum(), } } /// Number of bytes/octets that will be converted into a /// [`Icmpv6Header`] when [`Icmpv6Slice::header`] gets called. #[inline] pub fn header_len(&self) -> usize { 8 } /// Decode the header values (excluding the checksum) into an [`Icmpv6Type`] enum. pub fn icmp_type(&self) -> Icmpv6Type { use Icmpv6Type::*; match self.type_u8() { TYPE_DST_UNREACH => { if let Some(code) = DestUnreachableCode::from_u8(self.code_u8()) { return DestinationUnreachable(code); } } TYPE_PACKET_TOO_BIG => { if 0 == self.code_u8() { return PacketTooBig { mtu: u32::from_be_bytes(self.bytes5to8()), }; } } TYPE_TIME_EXCEEDED => { if let Some(code) = TimeExceededCode::from_u8(self.code_u8()) { return TimeExceeded(code); } } TYPE_PARAMETER_PROBLEM => { if let Some(code) = ParameterProblemCode::from_u8(self.code_u8()) { return ParameterProblem( ParameterProblemHeader{ code, pointer: u32::from_be_bytes(self.bytes5to8()) } ); } } TYPE_ECHO_REQUEST => { if 0 == self.code_u8() { return EchoRequest(IcmpEchoHeader::from_bytes(self.bytes5to8())); } } TYPE_ECHO_REPLY => { if 0 == self.code_u8() { return EchoReply(IcmpEchoHeader::from_bytes(self.bytes5to8())); } } _ => {} } Unknown { type_u8: self.type_u8(), code_u8: self.code_u8(), bytes5to8: self.bytes5to8(), } } /// Returns "type" value in the ICMPv6 header. #[inline] pub fn type_u8(&self) -> u8 { // SAFETY: // Safe as the contructor checks that the slice has // at least the length of Icmpv6Header::MIN_SERIALIZED_SIZE (8). unsafe { *self.slice.get_unchecked(0) } } /// Returns "code" value in the ICMPv6 header. #[inline] pub fn code_u8(&self) -> u8 { // SAFETY: // Safe as the contructor checks that the slice has // at least the length of Icmpv6Header::MIN_SERIALIZED_SIZE (8). unsafe { *self.slice.get_unchecked(1) } } /// Returns "checksum" value in the ICMPv6 header. #[inline] pub fn checksum(&self) -> u16 { // SAFETY: // Safe as the contructor checks that the slice has // at least the length of Icmpv6Header::MIN_SERIALIZED_SIZE (8). unsafe { get_unchecked_be_u16(self.slice.as_ptr().add(2)) } } /// Returns if the checksum in the slice is correct. pub fn is_checksum_valid(&self, source_ip: [u8; 16], destination_ip: [u8; 16]) -> bool { // NOTE: rfc4443 section 2.3 - Icmp6 *does* use a pseudoheader, // unlike Icmp4 checksum::Sum16BitWords::new() .add_16bytes(source_ip) .add_16bytes(destination_ip) .add_4bytes((self.slice().len() as u32).to_be_bytes()) .add_2bytes([0, ip_number::IPV6_ICMP]) // NOTE: From RFC 1071 // To check a checksum, the 1's complement sum is computed over the // same set of octets, including the checksum field. If the result // is all 1 bits (-0 in 1's complement arithmetic), the check // succeeds. .add_slice(self.slice) .ones_complement() == 0 } /// Returns the bytes from position 4 till and including the 8th position /// in the ICMPv6 header. /// /// These bytes located at th 5th, 6th, 7th and 8th position of the ICMP /// packet can depending on the ICMPv6 type and code contain additional data. #[inline] pub fn bytes5to8(&self) -> [u8; 4] { // SAFETY: // Safe as the contructor checks that the slice has // at least the length of Icmpv6Header::MIN_SERIALIZED_SIZE (8). unsafe { [ *self.slice.get_unchecked(4), *self.slice.get_unchecked(5), *self.slice.get_unchecked(6), *self.slice.get_unchecked(7), ] } } /// Returns the slice containing the ICMPv6 packet. #[inline] pub fn slice(&self) -> &'a [u8] { self.slice } /// Returns a slice to the bytes not covered by `.header()`. #[inline] pub fn payload(&self) -> &'a [u8] { // SAFETY: // Safe as the contructor checks that the slice has // at least the length of Icmpv6Header::MIN_SERIALIZED_SIZE(8). unsafe { from_raw_parts(self.slice.as_ptr().add(8), self.slice.len() - 8) } } } etherparse-0.13.0/src/transport/mod.rs000064400000000000000000000123431046102023000160220ustar 00000000000000pub mod icmp; pub mod icmpv4_impl; pub mod icmpv6_impl; pub mod udp; pub mod tcp; use super::*; use std::io; ///The possible headers on the transport layer #[derive(Clone, Debug, Eq, PartialEq)] pub enum TransportHeader { Udp(udp::UdpHeader), Tcp(tcp::TcpHeader), Icmpv4(Icmpv4Header), Icmpv6(Icmpv6Header), } impl TransportHeader { /// Returns Result::Some containing the udp header if self has the value Udp. /// Otherwise None is returned. pub fn udp(self) -> Option { use crate::TransportHeader::*; if let Udp(value) = self { Some(value) } else { None } } /// Returns Result::Some containing the udp header if self has the value Udp. /// Otherwise None is returned. pub fn mut_udp(&mut self) -> Option<&mut udp::UdpHeader> { use crate::TransportHeader::*; if let Udp(value) = self { Some(value) } else { None } } /// Returns Result::Some containing the tcp header if self has the value Tcp. /// Otherwise None is returned. pub fn tcp(self) -> Option { use crate::TransportHeader::*; if let Tcp(value) = self { Some(value) } else { None } } /// Returns Result::Some containing a mutable refernce to the tcp header if self has the value Tcp. /// Otherwise None is returned. pub fn mut_tcp(&mut self) -> Option<&mut tcp::TcpHeader> { use crate::TransportHeader::*; if let Tcp(value) = self { Some(value) } else { None } } /// Returns Result::Some containing the ICMPv4 header if self has the value Icmpv4. /// Otherwise None is returned. pub fn icmpv4(self) -> Option { use crate::TransportHeader::*; if let Icmpv4(value) = self { Some(value) } else { None } } /// Returns Result::Some containing the ICMPv4 header if self has the value Icmpv4. /// Otherwise None is returned. pub fn mut_icmpv4(&mut self) -> Option<&mut Icmpv4Header> { use crate::TransportHeader::*; if let Icmpv4(value) = self { Some(value) } else { None } } /// Returns Result::Some containing the ICMPv6 header if self has the value Icmpv6. /// Otherwise None is returned. pub fn icmpv6(self) -> Option { use crate::TransportHeader::*; if let Icmpv6(value) = self { Some(value) } else { None } } /// Returns Result::Some containing the ICMPv6 header if self has the value Icmpv6. /// Otherwise None is returned. pub fn mut_icmpv6(&mut self) -> Option<&mut Icmpv6Header> { use crate::TransportHeader::*; if let Icmpv6(value) = self { Some(value) } else { None } } /// Returns the size of the transport header (in case of UDP fixed, /// in case of TCP cotanining the options). pub fn header_len(&self) -> usize { use crate::TransportHeader::*; match self { Udp(_) => udp::UdpHeader::SERIALIZED_SIZE, Tcp(value) => usize::from(value.header_len()), Icmpv4(value) => value.header_len(), Icmpv6(value) => value.header_len(), } } /// Calculates the checksum for the transport header & sets it in the header for /// an ipv4 header. pub fn update_checksum_ipv4(&mut self, ip_header: &Ipv4Header, payload: &[u8]) -> Result<(), ValueError> { use crate::TransportHeader::*; match self { Udp(header) => { header.checksum = header.calc_checksum_ipv4(ip_header, payload)?; }, Tcp(header) => { header.checksum = header.calc_checksum_ipv4(ip_header, payload)?; }, Icmpv4(header) => { header.update_checksum(payload); }, Icmpv6(_) => return Err(ValueError::Icmpv6InIpv4), } Ok(()) } /// Calculates the checksum for the transport header & sets it in the header for /// an ipv6 header. pub fn update_checksum_ipv6(&mut self, ip_header: &Ipv6Header, payload: &[u8]) -> Result<(), ValueError> { use crate::TransportHeader::*; match self { Icmpv4(header) => header.update_checksum(payload), Icmpv6(header) => header.update_checksum(ip_header.source, ip_header.destination, payload)?, Udp(header) => { header.checksum = header.calc_checksum_ipv6(ip_header, payload)?; }, Tcp(header) => { header.checksum = header.calc_checksum_ipv6(ip_header, payload)?; } } Ok(()) } /// Write the transport header to the given writer. pub fn write(&self, writer: &mut T) -> Result<(), WriteError> { use crate::TransportHeader::*; match self { Icmpv4(value) => value.write(writer), Icmpv6(value) => value.write(writer), Udp(value) => value.write(writer), Tcp(value) => value.write(writer).map_err(WriteError::from) } } } etherparse-0.13.0/src/transport/tcp.rs000064400000000000000000001461241046102023000160360ustar 00000000000000use super::super::*; use std::fmt::{Debug, Formatter}; use std::slice::from_raw_parts; ///The minimum size of the tcp header in bytes pub const TCP_MINIMUM_HEADER_SIZE: usize = 5*4; ///The minimum data offset size (size of the tcp header itself). pub const TCP_MINIMUM_DATA_OFFSET: u8 = 5; ///The maximum allowed value for the data offset (it is a 4 bit value). pub const TCP_MAXIMUM_DATA_OFFSET: u8 = 0xf; ///TCP header according to rfc 793. /// ///Field descriptions copied from RFC 793 page 15++ #[derive(Clone)] pub struct TcpHeader { ///The source port number. pub source_port: u16, ///The destination port number. pub destination_port: u16, ///The sequence number of the first data octet in this segment (except when SYN is present). /// ///If SYN is present the sequence number is the initial sequence number (ISN) ///and the first data octet is ISN+1. ///[copied from RFC 793, page 16] pub sequence_number: u32, ///If the ACK control bit is set this field contains the value of the ///next sequence number the sender of the segment is expecting to ///receive. /// ///Once a connection is established this is always sent. pub acknowledgment_number: u32, ///The number of 32 bit words in the TCP Header. /// ///This indicates where the data begins. The TCP header (even one including options) is an ///integral number of 32 bits long. _data_offset: u8, ///ECN-nonce - concealment protection (experimental: see RFC 3540) pub ns: bool, ///No more data from sender pub fin: bool, ///Synchronize sequence numbers pub syn: bool, ///Reset the connection pub rst: bool, ///Push Function pub psh: bool, ///Acknowledgment field significant pub ack: bool, ///Urgent Pointer field significant pub urg: bool, ///ECN-Echo (RFC 3168) pub ece: bool, ///Congestion Window Reduced (CWR) flag /// ///This flag is set by the sending host to indicate that it received a TCP segment with the ECE flag set and had responded in congestion control mechanism (added to header by RFC 3168). pub cwr: bool, ///The number of data octets beginning with the one indicated in the ///acknowledgment field which the sender of this segment is willing to ///accept. pub window_size: u16, ///Checksum (16 bit one's complement) of the pseudo ip header, this tcp header and the payload. pub checksum: u16, ///This field communicates the current value of the urgent pointer as a ///positive offset from the sequence number in this segment. /// ///The urgent pointer points to the sequence number of the octet following ///the urgent data. This field is only be interpreted in segments with ///the URG control bit set. pub urgent_pointer: u16, ///Buffer containing the options of the header (note that the data_offset defines the actual length). Use the options() method if you want to get a slice that has the actual length of the options. options_buffer: [u8;40] } impl TcpHeader { ///Creates a TcpHeader with the given values and the rest initialized with default values. pub fn new(source_port: u16, destination_port: u16, sequence_number: u32, window_size: u16) -> TcpHeader { TcpHeader { source_port, destination_port, sequence_number, acknowledgment_number: 0, _data_offset: TCP_MINIMUM_DATA_OFFSET, ns: false, fin: false, syn: false, rst: false, psh: false, ack: false, ece: false, urg: false, cwr: false, window_size, checksum: 0, urgent_pointer: 0, options_buffer: [0;40] } } ///The number of 32 bit words in the TCP Header. /// ///This indicates where the data begins. The TCP header (even one including options) is an ///integral number of 32 bits long. pub fn data_offset(&self) -> u8 { self._data_offset } ///Returns the length of the header including the options. pub fn header_len(&self) -> u16 { u16::from(self._data_offset) * 4 } ///Returns the options size in bytes based on the currently set data_offset. Returns None if the data_offset is smaller then the minimum size or bigger then the maximum supported size. pub fn options_len(&self) -> usize { debug_assert!(TCP_MINIMUM_DATA_OFFSET <= self._data_offset); debug_assert!(self._data_offset <= TCP_MAXIMUM_DATA_OFFSET); (self._data_offset - TCP_MINIMUM_DATA_OFFSET) as usize * 4 } ///Returns a slice containing the options of the header (size is determined via the data_offset field. pub fn options(&self) -> &[u8] { &self.options_buffer[..self.options_len()] } ///Sets the options (overwrites the current options) or returns an error when there is not enough space. pub fn set_options(&mut self, options: &[TcpOptionElement]) -> Result<(), TcpOptionWriteError> { //calculate the required size of the options use crate::TcpOptionElement::*; let required_length = options.iter().fold(0, |acc, ref x| { acc + match x { Noop => 1, MaximumSegmentSize(_) => 4, WindowScale(_) => 3, SelectiveAcknowledgementPermitted => 2, SelectiveAcknowledgement(_, rest) => { rest.iter().fold(10, |acc2, ref y| { match y { None => acc2, Some(_) => acc2 + 8 } }) }, Timestamp(_, _) => 10, } }); if self.options_buffer.len() < required_length { Err(TcpOptionWriteError::NotEnoughSpace(required_length)) } else { //reset the options to null self.options_buffer = [0;40]; self._data_offset = TCP_MINIMUM_DATA_OFFSET; //write the options to the buffer //note to whoever: I would have prefered to use std::io::Cursor as it would be less error // prone. But just in case that "no std" support is added later lets // not not rewrite it just yet with cursor. use tcp_option::*; let mut i = 0; for element in options { match element { Noop => { self.options_buffer[i] = KIND_NOOP; i += 1; }, MaximumSegmentSize(value) => { // determine insertion area let insert = &mut self.options_buffer[i..i+4]; i += 4; // write data insert[0] = KIND_MAXIMUM_SEGMENT_SIZE; insert[1] = 4; insert[2..4].copy_from_slice(&value.to_be_bytes()); }, WindowScale(value) => { // determine insertion area let insert = &mut self.options_buffer[i..i+3]; i += 3; // write data insert[0] = KIND_WINDOW_SCALE; insert[1] = 3; insert[2] = *value; }, SelectiveAcknowledgementPermitted => { // determine insertion area let insert = &mut self.options_buffer[i..i+2]; i += 2; // write data insert[0] = KIND_SELECTIVE_ACK_PERMITTED; insert[1] = 2; }, SelectiveAcknowledgement(first, rest) => { //write guranteed data { let insert = &mut self.options_buffer[i..i + 10]; i += 10; insert[0] = KIND_SELECTIVE_ACK; //write the length insert[1] = rest.iter().fold(10, |acc, ref y| { match y { None => acc, Some(_) => acc + 8 } }); // write first insert[2..6].copy_from_slice(&first.0.to_be_bytes()); insert[6..10].copy_from_slice(&first.1.to_be_bytes()); } //write the rest for v in rest { match v { None => {}, Some((a,b)) => { // determine insertion area let insert = &mut self.options_buffer[i..i + 8]; i += 8; // insert insert[0..4].copy_from_slice(&a.to_be_bytes()); insert[4..8].copy_from_slice(&b.to_be_bytes()); } } } }, Timestamp(a, b) => { let insert = &mut self.options_buffer[i..i + 10]; i += 10; insert[0] = KIND_TIMESTAMP; insert[1] = 10; insert[2..6].copy_from_slice(&a.to_be_bytes()); insert[6..10].copy_from_slice(&b.to_be_bytes()); } } } //set the new data offset if i > 0 { self._data_offset = (i / 4) as u8 + TCP_MINIMUM_DATA_OFFSET; if i % 4 != 0 { self._data_offset += 1; } } //done Ok(()) } } ///Sets the options to the data given. pub fn set_options_raw(&mut self, data: &[u8]) -> Result<(), TcpOptionWriteError> { //check length if self.options_buffer.len() < data.len() { Err(TcpOptionWriteError::NotEnoughSpace(data.len())) } else { //reset all to zero to ensure padding self.options_buffer = [0;40]; //set data & data_offset self.options_buffer[..data.len()].copy_from_slice(data); self._data_offset = (data.len() / 4) as u8 + TCP_MINIMUM_DATA_OFFSET; if data.len() % 4 != 0 { self._data_offset += 1; } Ok(()) } } /// Returns an iterator that allows to iterate through all known TCP header options. pub fn options_iterator(&self) -> TcpOptionsIterator { TcpOptionsIterator { options: &self.options_buffer[..self.options_len()] } } /// Renamed to `TcpHeader::from_slice` #[deprecated( since = "0.10.1", note = "Use TcpHeader::from_slice instead." )] #[inline] pub fn read_from_slice(slice: &[u8]) -> Result<(TcpHeader, &[u8]), ReadError> { TcpHeader::from_slice(slice) } /// Reads a tcp header from a slice #[inline] pub fn from_slice(slice: &[u8]) -> Result<(TcpHeader, &[u8]), ReadError> { let h = TcpHeaderSlice::from_slice(slice)?; Ok(( h.to_header(), &slice[h.slice().len()..] )) } /// Read a tcp header from the current position pub fn read(reader: &mut T) -> Result { let raw = { let mut raw : [u8;20] = [0;20]; reader.read_exact(&mut raw)?; raw }; let source_port = u16::from_be_bytes([raw[0], raw[1]]); let destination_port = u16::from_be_bytes([raw[2], raw[3]]); let sequence_number = u32::from_be_bytes([raw[4], raw[5], raw[6], raw[7]]); let acknowledgment_number = u32::from_be_bytes([raw[8], raw[9], raw[10], raw[11]]); let (data_offset, ns) = { let value = raw[12]; ((value & 0xf0) >> 4, 0 != value & 1) }; let flags = raw[13]; Ok(TcpHeader{ source_port, destination_port, sequence_number, acknowledgment_number, ns, fin: 0 != flags & 1, syn: 0 != flags & 2, rst: 0 != flags & 4, psh: 0 != flags & 8, ack: 0 != flags & 16, urg: 0 != flags & 32, ece: 0 != flags & 64, cwr: 0 != flags & 128, window_size: u16::from_be_bytes([raw[14], raw[15]]), checksum: u16::from_be_bytes([raw[16], raw[17]]), urgent_pointer: u16::from_be_bytes([raw[18], raw[19]]), options_buffer: { if data_offset < TCP_MINIMUM_DATA_OFFSET { return Err(ReadError::TcpDataOffsetTooSmall(data_offset)); } else { let mut buffer: [u8;40] = [0;40]; //convert to bytes minus the tcp header size itself let len = ((data_offset - TCP_MINIMUM_DATA_OFFSET) as usize)*4; if len > 0 { reader.read_exact(&mut buffer[..len])?; } buffer } }, _data_offset: data_offset, }) } /// Write the tcp header to a stream (does NOT calculate the checksum). pub fn write(&self, writer: &mut T) -> Result<(), std::io::Error> { //check that the data offset is within range debug_assert!(TCP_MINIMUM_DATA_OFFSET <= self._data_offset); debug_assert!(self._data_offset <= TCP_MAXIMUM_DATA_OFFSET); let src_be = self.source_port.to_be_bytes(); let dst_be = self.destination_port.to_be_bytes(); let seq_be = self.sequence_number.to_be_bytes(); let ack_be = self.acknowledgment_number.to_be_bytes(); let window_be = self.window_size.to_be_bytes(); let checksum_be = self.checksum.to_be_bytes(); let urg_ptr_be = self.urgent_pointer.to_be_bytes(); writer.write_all( &[ src_be[0], src_be[1], dst_be[0], dst_be[1], seq_be[0], seq_be[1], seq_be[2], seq_be[3], ack_be[0], ack_be[1], ack_be[2], ack_be[3], { let value = (self._data_offset << 4) & 0xF0; if self.ns { value | 1 } else { value } }, { let mut value = 0; if self.fin { value |= 1; } if self.syn { value |= 2; } if self.rst { value |= 4; } if self.psh { value |= 8; } if self.ack { value |= 16; } if self.urg { value |= 32; } if self.ece { value |= 64; } if self.cwr { value |= 128; } value }, window_be[0], window_be[1], checksum_be[0], checksum_be[1], urg_ptr_be[0], urg_ptr_be[1] ] )?; //write options if the data_offset is large enough if self._data_offset > TCP_MINIMUM_DATA_OFFSET { let len = ((self._data_offset - TCP_MINIMUM_DATA_OFFSET) as usize)*4; writer.write_all(&self.options_buffer[..len])?; } Ok(()) } /// Calculates the upd header checksum based on a ipv4 header and returns the result. This does NOT set the checksum. pub fn calc_checksum_ipv4(&self, ip_header: &Ipv4Header, payload: &[u8]) -> Result { self.calc_checksum_ipv4_raw(ip_header.source, ip_header.destination, payload) } /// Calculates the checksum for the current header in ipv4 mode and returns the result. This does NOT set the checksum. pub fn calc_checksum_ipv4_raw(&self, source_ip: [u8;4], destination_ip: [u8;4], payload: &[u8]) -> Result { //check that the total length fits into the field let tcp_length = (self._data_offset as usize)*4 + payload.len(); if (std::u16::MAX as usize) < tcp_length { return Err(ValueError::TcpLengthTooLarge(tcp_length)); } // calculate the checksum Ok( self.calc_checksum_post_ip( checksum::Sum16BitWords::new() .add_4bytes(source_ip) .add_4bytes(destination_ip) .add_2bytes([0, ip_number::TCP]) .add_2bytes((tcp_length as u16).to_be_bytes()), payload ) ) } /// Calculates the upd header checksum based on a ipv6 header and returns the result. This does NOT set the checksum.. pub fn calc_checksum_ipv6(&self, ip_header: &Ipv6Header, payload: &[u8]) -> Result { self.calc_checksum_ipv6_raw(ip_header.source, ip_header.destination, payload) } /// Calculates the checksum for the current header in ipv6 mode and returns the result. This does NOT set the checksum. pub fn calc_checksum_ipv6_raw(&self, source: [u8;16], destination: [u8;16], payload: &[u8]) -> Result { //check that the total length fits into the field let tcp_length = (self._data_offset as usize)*4 + payload.len(); if (std::u32::MAX as usize) < tcp_length { return Err(ValueError::TcpLengthTooLarge(tcp_length)); } Ok(self.calc_checksum_post_ip( checksum::Sum16BitWords::new() .add_16bytes(source) .add_16bytes(destination) .add_4bytes((tcp_length as u32).to_be_bytes()) .add_2bytes([0, ip_number::TCP]), payload)) } ///This method takes the sum of the pseudo ip header and calculates the rest of the checksum. fn calc_checksum_post_ip(&self, ip_pseudo_header_sum: checksum::Sum16BitWords, payload: &[u8]) -> u16 { ip_pseudo_header_sum .add_2bytes(self.source_port.to_be_bytes()) .add_2bytes(self.destination_port.to_be_bytes()) .add_4bytes(self.sequence_number.to_be_bytes()) .add_4bytes(self.acknowledgment_number.to_be_bytes()) .add_2bytes( [ { let value = (self._data_offset << 4) & 0xF0; if self.ns { value | 1 } else { value } }, { let mut value = 0; if self.fin { value |= 1; } if self.syn { value |= 2; } if self.rst { value |= 4; } if self.psh { value |= 8; } if self.ack { value |= 16; } if self.urg { value |= 32; } if self.ece { value |= 64; } if self.cwr { value |= 128; } value } ] ) .add_2bytes(self.window_size.to_be_bytes()) .add_2bytes(self.urgent_pointer.to_be_bytes()) .add_slice(&self.options_buffer[..self.options_len()]) .add_slice(payload) .ones_complement() .to_be() } } impl Default for TcpHeader { fn default() -> TcpHeader { TcpHeader { source_port: 0, destination_port: 0, sequence_number: 0, acknowledgment_number: 0, _data_offset: 5, ns: false, fin: false, syn: false, rst: false, psh: false, ack: false, urg: false, ece: false, cwr: false, window_size: 0, checksum: 0, urgent_pointer: 0, options_buffer: [0;40] } } } //NOTE: I would have prefered to NOT write my own Debug & PartialEq implementation but there are no // default implementations availible for [u8;40] and the alternative of using [u32;10] would lead // to unsafe casting. Writing impl Debug for [u8;40] in a crate is also illegal as it could lead // to an implementation collision between crates. // So the only option left to me was to write an implementation myself and deal with the added complexity // and potential added error source. impl Debug for TcpHeader { fn fmt(&self, fmt: &mut Formatter) -> Result<(), std::fmt::Error> { fmt.debug_struct("TcpHeader") .field("source_port", &self.source_port) .field("destination_port", &self.destination_port) .field("sequence_number", &self.sequence_number) .field("acknowledgment_number", &self.acknowledgment_number) .field("data_offset", &self._data_offset) .field("ns", &self.ns) .field("fin", &self.fin) .field("syn", &self.syn) .field("rst", &self.rst) .field("psh", &self.psh) .field("ack", &self.ack) .field("urg", &self.urg) .field("ece", &self.ece) .field("cwr", &self.cwr) .field("window_size", &self.window_size) .field("checksum", &self.checksum) .field("urgent_pointer", &self.urgent_pointer) .field("options", &self.options_iterator()) .finish() } } impl std::cmp::PartialEq for TcpHeader { fn eq(&self, other: &TcpHeader) -> bool { self.source_port == other.source_port && self.destination_port == other.destination_port && self.sequence_number == other.sequence_number && self.acknowledgment_number == other.acknowledgment_number && self._data_offset == other._data_offset && self.ns == other.ns && self.fin == other.fin && self.syn == other.syn && self.rst == other.rst && self.psh == other.psh && self.ack == other.ack && self.urg == other.urg && self.ece == other.ece && self.cwr == other.cwr && self.window_size == other.window_size && self.checksum == other.checksum && self.urgent_pointer == other.urgent_pointer && self.options() == other.options() } } impl std::cmp::Eq for TcpHeader {} ///A slice containing an tcp header of a network package. #[derive(Clone, Debug, Eq, PartialEq)] pub struct TcpHeaderSlice<'a> { slice: &'a [u8] } impl<'a> TcpHeaderSlice<'a> { ///Creates a slice containing an tcp header. pub fn from_slice(slice: &'a[u8]) -> Result, ReadError> { //check length use crate::ReadError::*; if slice.len() < TCP_MINIMUM_HEADER_SIZE { return Err(UnexpectedEndOfSlice(TCP_MINIMUM_HEADER_SIZE)); } // SAFETY: // Safe as it is checked at the start of the function that the // length of the slice is at least TCP_MINIMUM_HEADER_SIZE (20). let data_offset = unsafe { (*slice.get_unchecked(12) & 0xf0) >> 4 }; let len = data_offset as usize * 4; if data_offset < TCP_MINIMUM_DATA_OFFSET { Err(ReadError::TcpDataOffsetTooSmall(data_offset)) } else if slice.len() < len { Err(UnexpectedEndOfSlice(len)) } else { //done Ok(TcpHeaderSlice::<'a>{ // SAFETY: // Safe as there is a check above that the slice length // is at least len. slice: unsafe { from_raw_parts(slice.as_ptr(), len) }, }) } } ///Returns the slice containing the tcp header #[inline] pub fn slice(&self) -> &'a [u8] { self.slice } ///Read the destination port number. #[inline] pub fn source_port(&self) -> u16 { // SAFETY: // Constructor checks that the slice has at least the length // of 20. unsafe { get_unchecked_be_u16(self.slice.as_ptr()) } } ///Read the destination port number. #[inline] pub fn destination_port(&self) -> u16 { // SAFETY: // Constructor checks that the slice has at least the length // of 20. unsafe { get_unchecked_be_u16(self.slice.as_ptr().add(2)) } } ///Read the sequence number of the first data octet in this segment (except when SYN is present). /// ///If SYN is present the sequence number is the initial sequence number (ISN) ///and the first data octet is ISN+1. ///\[copied from RFC 793, page 16\] #[inline] pub fn sequence_number(&self) -> u32 { // SAFETY: // Constructor checks that the slice has at least the length // of 20. unsafe { get_unchecked_be_u32(self.slice.as_ptr().add(4)) } } ///Reads the acknowledgment number. /// ///If the ACK control bit is set this field contains the value of the ///next sequence number the sender of the segment is expecting to ///receive. /// ///Once a connection is established this is always sent. #[inline] pub fn acknowledgment_number(&self) -> u32 { // SAFETY: // Constructor checks that the slice has at least the length // of 20. unsafe { get_unchecked_be_u32(self.slice.as_ptr().add(8)) } } ///Read the number of 32 bit words in the TCP Header. /// ///This indicates where the data begins. The TCP header (even one including options) is an ///integral number of 32 bits long. #[inline] pub fn data_offset(&self) -> u8 { // SAFETY: // Constructor checks that the slice has at least the length // of 20. unsafe { (*self.slice.get_unchecked(12) & 0b1111_0000) >> 4 } } ///ECN-nonce - concealment protection (experimental: see RFC 3540) #[inline] pub fn ns(&self) -> bool { // SAFETY: // Constructor checks that the slice has at least the length // of 20. unsafe { 0 != (*self.slice.get_unchecked(12) & 0b0000_0001) } } ///Read the fin flag (no more data from sender). #[inline] pub fn fin(&self) -> bool { // SAFETY: // Constructor checks that the slice has at least the length // of 20. unsafe { 0 != (*self.slice.get_unchecked(13) & 0b0000_0001) } } ///Reads the syn flag (synchronize sequence numbers). #[inline] pub fn syn(&self) -> bool { // SAFETY: // Constructor checks that the slice has at least the length // of 20. unsafe { 0 != (*self.slice.get_unchecked(13) & 0b0000_0010) } } ///Reads the rst flag (reset the connection). #[inline] pub fn rst(&self) -> bool { // SAFETY: // Constructor checks that the slice has at least the length // of 20. unsafe { 0 != (*self.slice.get_unchecked(13) & 0b0000_0100) } } ///Reads the psh flag (push function). #[inline] pub fn psh(&self) -> bool { // SAFETY: // Constructor checks that the slice has at least the length // of 20. unsafe { 0 != (*self.slice.get_unchecked(13) & 0b0000_1000) } } ///Reads the ack flag (acknowledgment field significant). #[inline] pub fn ack(&self) -> bool { // SAFETY: // Constructor checks that the slice has at least the length // of 20. unsafe { 0 != (*self.slice.get_unchecked(13) & 0b0001_0000) } } ///Reads the urg flag (Urgent Pointer field significant). #[inline] pub fn urg(&self) -> bool { // SAFETY: // Constructor checks that the slice has at least the length // of 20. unsafe { 0 != (*self.slice.get_unchecked(13) & 0b0010_0000) } } ///Read the ECN-Echo flag (RFC 3168). #[inline] pub fn ece(&self) -> bool { // SAFETY: // Constructor checks that the slice has at least the length // of 20. unsafe { 0 != (*self.slice.get_unchecked(13) & 0b0100_0000) } } /// Reads the cwr flag (Congestion Window Reduced). /// /// This flag is set by the sending host to indicate that it received a TCP /// segment with the ECE flag set and had responded in congestion control /// mechanism (added to header by RFC 3168). #[inline] pub fn cwr(&self) -> bool { // SAFETY: // Constructor checks that the slice has at least the length // of 20. unsafe { 0 != (*self.slice.get_unchecked(13) & 0b1000_0000) } } ///The number of data octets beginning with the one indicated in the ///acknowledgment field which the sender of this segment is willing to ///accept. #[inline] pub fn window_size(&self) -> u16 { u16::from_be_bytes( // SAFETY: // Constructor checks that the slice has at least the length // of 20. unsafe { [ *self.slice.get_unchecked(14), *self.slice.get_unchecked(15), ] } ) } ///Checksum (16 bit one's complement) of the pseudo ip header, this tcp header and the payload. #[inline] pub fn checksum(&self) -> u16 { u16::from_be_bytes( // SAFETY: // Constructor checks that the slice has at least the length // of 20. unsafe { [ *self.slice.get_unchecked(16), *self.slice.get_unchecked(17), ] } ) } ///This field communicates the current value of the urgent pointer as a ///positive offset from the sequence number in this segment. /// ///The urgent pointer points to the sequence number of the octet following ///the urgent data. This field is only be interpreted in segments with ///the URG control bit set. #[inline] pub fn urgent_pointer(&self) -> u16 { u16::from_be_bytes( // SAFETY: // Constructor checks that the slice has at least the length // of 20. unsafe { [ *self.slice.get_unchecked(18), *self.slice.get_unchecked(19), ] } ) } ///Options of the header #[inline] pub fn options(&self) -> &[u8] { &self.slice[TCP_MINIMUM_HEADER_SIZE..self.data_offset() as usize*4] } ///Returns an iterator that allows to iterate through all known TCP header options. #[inline] pub fn options_iterator(&self) -> TcpOptionsIterator { TcpOptionsIterator::from_slice(self.options()) } ///Decode all the fields and copy the results to a TcpHeader struct pub fn to_header(&self) -> TcpHeader { TcpHeader { source_port: self.source_port(), destination_port: self.destination_port(), sequence_number: self.sequence_number(), acknowledgment_number: self.acknowledgment_number(), _data_offset: self.data_offset(), ns: self.ns(), fin: self.fin(), syn: self.syn(), rst: self.rst(), psh: self.psh(), ack: self.ack(), ece: self.ece(), urg: self.urg(), cwr: self.cwr(), window_size: self.window_size(), checksum: self.checksum(), urgent_pointer: self.urgent_pointer(), options_buffer: { let options = self.options(); let mut result: [u8;40] = [0;40]; if !options.is_empty() { result[..options.len()].clone_from_slice(options); } result } } } ///Calculates the upd header checksum based on a ipv4 header and returns the result. This does NOT set the checksum. pub fn calc_checksum_ipv4(&self, ip_header: &Ipv4HeaderSlice, payload: &[u8]) -> Result { self.calc_checksum_ipv4_raw(ip_header.source(), ip_header.destination(), payload) } ///Calculates the checksum for the current header in ipv4 mode and returns the result. This does NOT set the checksum. pub fn calc_checksum_ipv4_raw(&self, source_ip: [u8;4], destination_ip: [u8;4], payload: &[u8]) -> Result { //check that the total length fits into the field let tcp_length = self.slice.len() + payload.len(); if (std::u16::MAX as usize) < tcp_length { return Err(ValueError::TcpLengthTooLarge(tcp_length)); } //calculate the checksum Ok( self.calc_checksum_post_ip( checksum::Sum16BitWords::new() .add_4bytes(source_ip) .add_4bytes(destination_ip) .add_2bytes([0, ip_number::TCP]) .add_2bytes((tcp_length as u16).to_be_bytes()), payload ) ) } ///Calculates the upd header checksum based on a ipv6 header and returns the result. This does NOT set the checksum.. pub fn calc_checksum_ipv6(&self, ip_header: &Ipv6HeaderSlice, payload: &[u8]) -> Result { self.calc_checksum_ipv6_raw(ip_header.source(), ip_header.destination(), payload) } ///Calculates the checksum for the current header in ipv6 mode and returns the result. This does NOT set the checksum. pub fn calc_checksum_ipv6_raw(&self, source: [u8;16], destination: [u8;16], payload: &[u8]) -> Result { //check that the total length fits into the field let tcp_length = (self.data_offset() as usize)*4 + payload.len(); if (std::u32::MAX as usize) < tcp_length { return Err(ValueError::TcpLengthTooLarge(tcp_length)); } Ok( self.calc_checksum_post_ip( checksum::Sum16BitWords::new() .add_16bytes(source) .add_16bytes(destination) .add_2bytes([0, ip_number::TCP]) .add_4bytes((tcp_length as u32).to_be_bytes()), payload ) ) } /// This method takes the sum of the pseudo ip header and calculates the rest of the checksum. fn calc_checksum_post_ip(&self, ip_pseudo_header_sum: checksum::Sum16BitWords, payload: &[u8]) -> u16 { ip_pseudo_header_sum .add_slice(&self.slice[..16]) //until checksum .add_slice(&self.slice[18..self.slice.len()]) .add_slice(payload) .ones_complement() .to_be() } } /// Different kinds of options that can be present in the options part of a tcp header. #[derive(Clone, Debug, Eq, PartialEq)] pub enum TcpOptionElement { /// "No-Operation" option. /// /// Description from RFC 793: /// /// This option code may be used between options, for example, to /// align the beginning of a subsequent option on a word boundary. /// There is no guarantee that senders will use this option, so /// receivers must be prepared to process options even if they do /// not begin on a word boundary. Noop, /// "Maximum Segment Size" option. /// /// Description from RFC 793: /// /// If this option is present, then it communicates the maximum /// receive segment size at the TCP which sends this segment. /// This field must only be sent in the initial connection request /// (i.e., in segments with the SYN control bit set). If this //// option is not used, any segment size is allowed. MaximumSegmentSize(u16), WindowScale(u8), SelectiveAcknowledgementPermitted, SelectiveAcknowledgement((u32,u32), [Option<(u32,u32)>;3]), ///Timestamp & echo (first number is the sender timestamp, the second the echo timestamp) Timestamp(u32, u32), } ///Errors that can occour while reading the options of a TCP header. #[derive(Clone, Debug, Eq, PartialEq)] pub enum TcpOptionReadError { ///Returned if an option id was read, but there was not enough memory in the options left to completely read it. UnexpectedEndOfSlice{ option_id: u8, expected_len: u8, actual_len: usize }, ///Returned if the option as an unexpected size argument (e.g. != 4 for maximum segment size). UnexpectedSize{ option_id: u8, size: u8 }, ///Returned if an unknown tcp header option is encountered. /// ///The first element is the identifier and the slice contains the rest of data left in the options. UnknownId(u8), } impl Error for TcpOptionReadError { fn source(&self) -> Option<&(dyn Error + 'static)> { None } } impl fmt::Display for TcpOptionReadError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { use TcpOptionReadError::*; match self { UnexpectedEndOfSlice{option_id, expected_len, actual_len} => { write!(f, "TcpOptionReadError: Not enough memory left in slice to read option of kind {} (expected at least {} bytes, only {} bytes available).", option_id, expected_len, actual_len) }, UnexpectedSize{option_id, size} => { write!(f, "TcpOptionReadError: Length value of the option of kind {} had unexpected value {}.", option_id, size) }, UnknownId(id) => { write!(f, "TcpOptionReadError: Unknown tcp option kind value {}.", id) } } } } ///Errors that can occour when setting the options of a tcp header. #[derive(Clone, Debug, Eq, PartialEq)] pub enum TcpOptionWriteError { ///There is not enough memory to store all options in the options section of the header (maximum 40 bytes). /// ///The options size is limited by the 4 bit data_offset field in the header which describes ///the total tcp header size in multiple of 4 bytes. This leads to a maximum size for the options ///part of the header of 4*(15 - 5) (minus 5 for the size of the tcp header itself). NotEnoughSpace(usize) } impl Error for TcpOptionWriteError { fn source(&self) -> Option<&(dyn Error + 'static)> { None } } impl fmt::Display for TcpOptionWriteError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { use TcpOptionWriteError::*; match self { NotEnoughSpace(size) => { write!(f, "TcpOptionWriteError: Not enough memory to store all options in the options section of a tcp header (maximum 40 bytes can be stored, the options would have needed {} bytes).", size) }, } } } ///Allows iterating over the options after a TCP header. #[derive(Clone, Eq, PartialEq)] pub struct TcpOptionsIterator<'a> { options: &'a [u8] } #[deprecated( since = "0.10.1", note = "Please use tcp_option::KIND_END instead" )] /// Deprecated please use [tcp_option::KIND_END] instead. pub const TCP_OPTION_ID_END: u8 = 0; #[deprecated( since = "0.10.1", note = "Please use tcp_option::KIND_NOOP instead" )] /// Deprecated please use [tcp_option::KIND_NOOP] instead. pub const TCP_OPTION_ID_NOP: u8 = 1; #[deprecated( since = "0.10.1", note = "Please use tcp_option::KIND_MAXIMUM_SEGMENT_SIZE instead" )] /// Deprecated please use [tcp_option::KIND_MAXIMUM_SEGMENT_SIZE] instead. pub const TCP_OPTION_ID_MAXIMUM_SEGMENT_SIZE: u8 = 2; #[deprecated( since = "0.10.1", note = "Please use tcp_option::KIND_WINDOW_SCALE instead" )] /// Deprecated please use [tcp_option::KIND_WINDOW_SCALE] instead. pub const TCP_OPTION_ID_WINDOW_SCALE: u8 = 3; #[deprecated( since = "0.10.1", note = "Please use tcp_option::KIND_SELECTIVE_ACK_PERMITTED instead" )] /// Deprecated please use [tcp_option::KIND_SELECTIVE_ACK_PERMITTED] instead. pub const TCP_OPTION_ID_SELECTIVE_ACK_PERMITTED: u8 = 4; #[deprecated( since = "0.10.1", note = "Please use tcp_option::KIND_SELECTIVE_ACK instead" )] /// Deprecated please use [tcp_option::KIND_SELECTIVE_ACK] instead. pub const TCP_OPTION_ID_SELECTIVE_ACK: u8 = 5; #[deprecated( since = "0.10.1", note = "Please use tcp_option::KIND_TIMESTAMP instead" )] /// Deprecated please use [tcp_option::KIND_TIMESTAMP] instead. pub const TCP_OPTION_ID_TIMESTAMP: u8 = 8; /// Module containing the constants for tcp options (id number & sizes). pub mod tcp_option { /// `u8` identifying the "end of options list" in the tcp option. pub const KIND_END: u8 = 0; /// `u8` identifying a "no operation" tcp option. pub const KIND_NOOP: u8 = 1; /// `u8` identifying a "maximum segment size" tcp option. pub const KIND_MAXIMUM_SEGMENT_SIZE: u8 = 2; /// `u8` identifying a "window scaling" tcp option. pub const KIND_WINDOW_SCALE: u8 = 3; /// `u8` identifying a "selective acknowledgement permitted" tcp option. pub const KIND_SELECTIVE_ACK_PERMITTED: u8 = 4; /// `u8` identifying a "selective acknowledgement" tcp option. pub const KIND_SELECTIVE_ACK: u8 = 5; /// `u8` identifying a "timestamp and echo of previous timestamp" tcp option. pub const KIND_TIMESTAMP: u8 = 8; /// Length in octets/bytes of the "end" tcp option (includes kind value). pub const LEN_END: u8 = 1; /// Length in octets/bytes of the "no operation" tcp option (includes kind value). pub const LEN_NOOP: u8 = 1; /// Length in octets/bytes of the "maximum segment size" tcp option (includes kind value). pub const LEN_MAXIMUM_SEGMENT_SIZE: u8 = 4; /// Length in octets/bytes of the "window scaling" tcp option (includes kind value). pub const LEN_WINDOW_SCALE: u8 = 3; /// Length in octets/bytes of the "selective acknowledgement permitted" tcp option (includes kind value). pub const LEN_SELECTIVE_ACK_PERMITTED: u8 = 2; /// Length in octets/bytes of the "timestamp and echo of previous timestamp" tcp option (includes kind value). pub const LEN_TIMESTAMP: u8 = 10; } impl<'a> TcpOptionsIterator<'a> { ///Creates an options iterator from a slice containing encoded tcp options. pub fn from_slice(options: &'a [u8]) -> TcpOptionsIterator<'a> { TcpOptionsIterator{ options } } ///Returns the non processed part of the options slice. pub fn rest(&self) -> &'a [u8] { self.options } } impl<'a> Iterator for TcpOptionsIterator<'a> { type Item = Result; fn next(&mut self) -> Option { use crate::TcpOptionReadError::*; use crate::TcpOptionElement::*; let expect_specific_size = |expected_size: u8, slice: &[u8]| -> Result<(), TcpOptionReadError> { let id = slice[0]; if slice.len() < expected_size as usize { Err( UnexpectedEndOfSlice{ option_id: id, expected_len: expected_size, actual_len: slice.len() }, ) } else if slice[1] != expected_size { Err(UnexpectedSize{ option_id: slice[0], size: slice[1] }) } else { Ok(()) } }; if self.options.is_empty() { None } else { //first determine the result use tcp_option::*; let result = match self.options[0] { //end KIND_END => { None }, KIND_NOOP => { self.options = &self.options[1..]; Some(Ok(Noop)) }, KIND_MAXIMUM_SEGMENT_SIZE => { match expect_specific_size(LEN_MAXIMUM_SEGMENT_SIZE, self.options) { Err(value) => { Some(Err(value)) }, _ => { // SAFETY: // Safe as the slice size is checked beforehand to be at // least of size LEN_MAXIMUM_SEGMENT_SIZE (4). let value = unsafe { get_unchecked_be_u16(self.options.as_ptr().add(2)) }; self.options = &self.options[4..]; Some(Ok(MaximumSegmentSize(value))) } } }, KIND_WINDOW_SCALE => { match expect_specific_size(LEN_WINDOW_SCALE, self.options) { Err(value) => Some(Err(value)), _ => { let value = self.options[2]; self.options = &self.options[3..]; Some(Ok(WindowScale(value))) } } }, KIND_SELECTIVE_ACK_PERMITTED => { match expect_specific_size(LEN_SELECTIVE_ACK_PERMITTED, self.options) { Err(value) => Some(Err(value)), _ => { self.options = &self.options[2..]; Some(Ok(SelectiveAcknowledgementPermitted)) } } }, KIND_SELECTIVE_ACK => { //check that the length field can be read if self.options.len() < 2 { Some( Err( UnexpectedEndOfSlice { option_id: self.options[0], expected_len: 2, actual_len: self.options.len() } ) ) } else { //check that the length is an allowed one for this option let len = self.options[1]; if len != 10 && len != 18 && len != 26 && len != 34 { Some(Err(UnexpectedSize{ option_id: self.options[0], size: len })) } else if self.options.len() < (len as usize) { Some( Err( UnexpectedEndOfSlice { option_id: self.options[0], expected_len: len, actual_len: self.options.len() } ) ) } else { let mut acks: [Option<(u32,u32)>;3] = [None;3]; // SAFETY: // This is safe as above the len is checked // to be at least 10 and the slice len is // checked to be at least len bytes. let first = unsafe { ( get_unchecked_be_u32(self.options.as_ptr().add(2)), get_unchecked_be_u32(self.options.as_ptr().add(6)), ) }; for (i, item) in acks.iter_mut() .enumerate() .take(3) { let offset = 2 + 8 + (i*8); // SAFETY: // len can only be 10, 18, 26 or 34 // therefore if the offset is smaller then the // len, then at least 8 bytes can be read. unsafe { if offset < (len as usize) { *item = Some( ( get_unchecked_be_u32(self.options.as_ptr().add(offset)), get_unchecked_be_u32(self.options.as_ptr().add(offset + 4)), ) ); } } } //iterate the options self.options = &self.options[len as usize..]; Some(Ok(SelectiveAcknowledgement(first, acks))) } } }, KIND_TIMESTAMP => { match expect_specific_size(LEN_TIMESTAMP, self.options) { Err(value) => Some(Err(value)), _ => unsafe { let t = Timestamp( // SAFETY: // Safe as the len first gets checked to be equal // LEN_TIMESTAMP (10). get_unchecked_be_u32(self.options.as_ptr().add(2)), get_unchecked_be_u32(self.options.as_ptr().add(6)), ); self.options = &self.options[10..]; Some(Ok(t)) } } }, //unknown id _ => { Some(Err(UnknownId(self.options[0]))) }, }; //in case the result was an error or the end move the slice to an end position match result { None | Some(Err(_)) => { let len = self.options.len(); self.options = &self.options[len..len]; }, _ => {} } //finally return the result result } } } impl<'a> Debug for TcpOptionsIterator<'a> { fn fmt(&self, fmt: &mut Formatter) -> Result<(), std::fmt::Error> { let mut list = fmt.debug_list(); // create a copy and iterate over all elements for it in self.clone() { match it { Ok(e) => { list.entry(&e); }, Err(e) => { list.entry(&Result::<(), TcpOptionReadError>::Err(e.clone())); } } } list.finish() } } etherparse-0.13.0/src/transport/udp.rs000064400000000000000000000275701046102023000160430ustar 00000000000000use super::super::*; use std::slice::from_raw_parts; ///Udp header according to rfc768. #[derive(Clone, Debug, Eq, PartialEq, Default)] pub struct UdpHeader { ///Source port of the packet (optional). pub source_port: u16, ///Destination port of the packet. pub destination_port: u16, ///Length of the packet (includes the udp header length of 8 bytes). pub length: u16, ///The checksum of the packet. The checksum is calculated from a pseudo header, the udp header and the payload. The pseudo header is composed of source and destination address, protocol number pub checksum: u16 } impl UdpHeader { /// Returns an udp header for the given parameters pub fn without_ipv4_checksum(source_port: u16, destination_port: u16, payload_length: usize) -> Result { //check that the total length fits into the field const MAX_PAYLOAD_LENGTH: usize = (std::u16::MAX as usize) - UdpHeader::SERIALIZED_SIZE; if MAX_PAYLOAD_LENGTH < payload_length { return Err(ValueError::UdpPayloadLengthTooLarge(payload_length)); } Ok(UdpHeader{ source_port, destination_port, length: (UdpHeader::SERIALIZED_SIZE + payload_length) as u16, //payload plus udp header checksum: 0 }) } /// Calculate an udp header given an ipv4 header and the payload pub fn with_ipv4_checksum(source_port: u16, destination_port: u16, ip_header: &Ipv4Header, payload: &[u8]) -> Result { //check that the total length fits into the field const MAX_PAYLOAD_LENGTH: usize = (std::u16::MAX as usize) - UdpHeader::SERIALIZED_SIZE; if MAX_PAYLOAD_LENGTH < payload.len() { return Err(ValueError::UdpPayloadLengthTooLarge(payload.len())); } let mut result = UdpHeader{ source_port, destination_port, length: (UdpHeader::SERIALIZED_SIZE + payload.len()) as u16, //payload plus udp header checksum: 0 }; result.checksum = result.calc_checksum_ipv4_internal(ip_header.source, ip_header.destination, payload); Ok(result) } /// Calculates the upd header checksum based on a ipv4 header. pub fn calc_checksum_ipv4(&self, ip_header: &Ipv4Header, payload: &[u8]) -> Result { self.calc_checksum_ipv4_raw(ip_header.source, ip_header.destination, payload) } /// Calculates the upd header checksum based on a ipv4 header. pub fn calc_checksum_ipv4_raw(&self, source: [u8;4], destination: [u8;4], payload: &[u8]) -> Result { //check that the total length fits into the field const MAX_PAYLOAD_LENGTH: usize = (std::u16::MAX as usize) - UdpHeader::SERIALIZED_SIZE; if MAX_PAYLOAD_LENGTH < payload.len() { return Err(ValueError::UdpPayloadLengthTooLarge(payload.len())); } Ok(self.calc_checksum_ipv4_internal(source, destination, payload)) } /// Calculates the upd header checksum based on a ipv4 header. fn calc_checksum_ipv4_internal(&self, source: [u8;4], destination: [u8;4], payload: &[u8]) -> u16 { self.calc_checksum_post_ip( //pseudo header checksum::Sum16BitWords::new() .add_4bytes(source) .add_4bytes(destination) .add_2bytes([0, ip_number::UDP]) .add_2bytes(self.length.to_be_bytes()), payload ) } /// Calculate an udp header given an ipv6 header and the payload pub fn with_ipv6_checksum(source_port: u16, destination_port: u16, ip_header: &Ipv6Header, payload: &[u8]) -> Result { //check that the total length fits into the field const MAX_PAYLOAD_LENGTH: usize = (std::u16::MAX as usize) - UdpHeader::SERIALIZED_SIZE; if MAX_PAYLOAD_LENGTH <= payload.len() { return Err(ValueError::UdpPayloadLengthTooLarge(payload.len())); } let mut result = UdpHeader{ source_port, destination_port, length: (UdpHeader::SERIALIZED_SIZE + payload.len()) as u16, //payload plus udp header checksum: 0 }; result.checksum = result.calc_checksum_ipv6_internal(ip_header.source, ip_header.destination, payload); Ok(result) } /// Calculates the checksum of the current udp header given an ipv6 header and the payload. pub fn calc_checksum_ipv6(&self, ip_header: &Ipv6Header, payload: &[u8]) -> Result { self.calc_checksum_ipv6_raw(ip_header.source, ip_header.destination, payload) } /// Calculates the checksum of the current udp header given an ipv6 source & destination address plus the payload. pub fn calc_checksum_ipv6_raw(&self, source: [u8;16], destination: [u8;16], payload: &[u8]) -> Result { //check that the total length fits into the field const MAX_PAYLOAD_LENGTH: usize = (std::u32::MAX as usize) - UdpHeader::SERIALIZED_SIZE; if MAX_PAYLOAD_LENGTH < payload.len() { return Err(ValueError::UdpPayloadLengthTooLarge(payload.len())); } Ok(self.calc_checksum_ipv6_internal(source, destination, payload)) } fn calc_checksum_ipv6_internal(&self, source: [u8;16], destination: [u8;16], payload: &[u8]) -> u16 { self.calc_checksum_post_ip( //pseudo header checksum::Sum16BitWords::new() .add_16bytes(source) .add_16bytes(destination) .add_2bytes([0, ip_number::UDP]) .add_2bytes(self.length.to_be_bytes()), payload ) } /// This method takes the sum of the pseudo ip header and calculates the rest of the checksum. fn calc_checksum_post_ip(&self, ip_pseudo_header_sum: checksum::Sum16BitWords, payload: &[u8]) -> u16 { ip_pseudo_header_sum .add_2bytes(self.source_port.to_be_bytes()) .add_2bytes(self.destination_port.to_be_bytes()) .add_2bytes(self.length.to_be_bytes()) .add_slice(payload) .to_ones_complement_with_no_zero() .to_be() } /// Reads a udp header from a slice directly and returns a tuple containing the resulting header & unused part of the slice. #[deprecated( since = "0.10.1", note = "Use UdpHeader::from_slice instead." )] #[inline] pub fn read_from_slice(slice: &[u8]) -> Result<(UdpHeader, &[u8]), ReadError> { UdpHeader::from_slice(slice) } /// Reads a udp header from a slice directly and returns a tuple containing the resulting header & unused part of the slice. #[inline] pub fn from_slice(slice: &[u8]) -> Result<(UdpHeader, &[u8]), ReadError> { Ok(( UdpHeaderSlice::from_slice(slice)?.to_header(), &slice[UdpHeader::SERIALIZED_SIZE..] )) } /// Read an UdpHeader from a static sized byte array. #[inline] pub fn from_bytes(bytes: [u8;8]) -> UdpHeader { UdpHeader{ source_port: u16::from_be_bytes( [ bytes[0], bytes[1], ] ), destination_port: u16::from_be_bytes( [ bytes[2], bytes[3], ] ), length: u16::from_be_bytes( [ bytes[4], bytes[5], ] ), checksum: u16::from_be_bytes( [ bytes[6], bytes[7], ] ), } } /// Tries to read an udp header from the current position. pub fn read(reader: &mut T) -> Result { let bytes = { let mut bytes : [u8;8] = [0;8]; reader.read_exact(&mut bytes)?; bytes }; Ok(UdpHeader::from_bytes(bytes)) } /// Write the udp header without recalculating the checksum or length. pub fn write(&self, writer: &mut T) -> Result<(), WriteError> { writer.write_all(&self.to_bytes())?; Ok(()) } /// Length of the serialized header in bytes. /// /// The function always returns the constant UdpHeader::SERIALIZED_SIZE /// and exists to keep the methods consistent with other headers. #[inline] pub fn header_len(&self) -> usize { UdpHeader::SERIALIZED_SIZE } /// Returns the serialized form of the header as a statically /// sized byte array. #[inline] pub fn to_bytes(&self) -> [u8;8] { let source_port_be = self.source_port.to_be_bytes(); let destination_port_be = self.destination_port.to_be_bytes(); let length_be = self.length.to_be_bytes(); let checksum = self.checksum.to_be_bytes(); [ source_port_be[0], source_port_be[1], destination_port_be[0], destination_port_be[1], length_be[0], length_be[1], checksum[0], checksum[1], ] } } impl SerializedSize for UdpHeader { ///Size of the header itself const SERIALIZED_SIZE: usize = 8; } ///A slice containing an udp header of a network package. Struct allows the selective read of fields in the header. #[derive(Clone, Debug, Eq, PartialEq)] pub struct UdpHeaderSlice<'a> { slice: &'a [u8] } impl<'a> UdpHeaderSlice<'a> { /// Creates a slice containing an udp header. #[inline] pub fn from_slice(slice: &'a[u8]) -> Result, ReadError> { //check length use crate::ReadError::*; if slice.len() < UdpHeader::SERIALIZED_SIZE { return Err(UnexpectedEndOfSlice(UdpHeader::SERIALIZED_SIZE)); } //done Ok(UdpHeaderSlice{ // SAFETY: // Safe as slice length is checked to be at least // UdpHeader::SERIALIZED_SIZE (8) before this. slice: unsafe { from_raw_parts( slice.as_ptr(), UdpHeader::SERIALIZED_SIZE ) } }) } /// Returns the slice containing the udp header #[inline] pub fn slice(&self) -> &'a [u8] { self.slice } /// Reads the "udp source port" from the slice. #[inline] pub fn source_port(&self) -> u16 { // SAFETY: // Safe as the contructor checks that the slice has // at least the length of UdpHeader::SERIALIZED_SIZE (8). unsafe { get_unchecked_be_u16(self.slice.as_ptr()) } } /// Reads the "udp destination port" from the slice. #[inline] pub fn destination_port(&self) -> u16 { // SAFETY: // Safe as the contructor checks that the slice has // at least the length of UdpHeader::SERIALIZED_SIZE (8). unsafe { get_unchecked_be_u16(self.slice.as_ptr().add(2)) } } /// Reads the "length" from the slice. #[inline] pub fn length(&self) -> u16 { // SAFETY: // Safe as the contructor checks that the slice has // at least the length of UdpHeader::SERIALIZED_SIZE (8). unsafe { get_unchecked_be_u16(self.slice.as_ptr().add(4)) } } /// Reads the "checksum" from the slice. #[inline] pub fn checksum(&self) -> u16 { // SAFETY: // Safe as the contructor checks that the slice has // at least the length of UdpHeader::SERIALIZED_SIZE (8). unsafe { get_unchecked_be_u16(self.slice.as_ptr().add(6)) } } /// Decode all the fields and copy the results to a UdpHeader struct #[inline] pub fn to_header(&self) -> UdpHeader { UdpHeader { source_port: self.source_port(), destination_port: self.destination_port(), length: self.length(), checksum: self.checksum() } } } etherparse-0.13.0/tests/checksum/mod.proptest-regressions000064400000000000000000000001031046102023000217270ustar 00000000000000cc 90412979c0ca21ac286b5cc7202bd85d6784524a2c956df2ea8e0f407b297483etherparse-0.13.0/tests/checksum/mod.rs000064400000000000000000000011701046102023000161370ustar 00000000000000use super::*; use proptest::prelude::*; proptest! { #[test] fn u32_u16_comparison( data in proptest::collection::vec(any::(), 0..0xfffusize) ) { use super::etherparse::checksum::*; let u32_oc = u32_16bit_word::ones_complement( u32_16bit_word::add_slice(0, &data) ); let u64_oc = u64_16bit_word::ones_complement( u64_16bit_word::add_slice(0, &data) ); assert_eq!(u32_oc, u64_oc); let struct_oc = Sum16BitWords::new() .add_slice(&data) .ones_complement(); assert_eq!(u32_oc, struct_oc); } } etherparse-0.13.0/tests/errors/mod.rs000064400000000000000000000362351046102023000156630ustar 00000000000000use super::*; use proptest::prelude::*; proptest! { #[test] fn read_error_display( arg_u8 in any::(), arg_u16 in any::(), arg_usize in any::(), arg2_usize in any::(), ) { //arg_u16 in any::() use super::ReadError::*; //IoError { let custom_error = std::io::Error::new(std::io::ErrorKind::Other, "some error"); assert_eq!( &format!("{}", custom_error), &format!("{}", IoError(custom_error)) ); } //UnexpectedEndOfSlice assert_eq!( &format!("ReadError: Unexpected end of slice. The given slice contained less then minimum required {} bytes.", arg_usize), &format!("{}", UnexpectedEndOfSlice(arg_usize)) ); //UnexpectedLenOfSlice assert_eq!( &format!("ReadError: Unexpected length of slice. The given slice contained {} bytes but {} bytes were required.", arg2_usize, arg_usize), &format!("{}", UnexpectedLenOfSlice{ expected: arg_usize, actual: arg2_usize }) ); //DoubleVlanOuterNonVlanEtherType assert_eq!( &format!("ReadError: Expected a double vlan header, but the ether type field value {} of the outer vlan header is a non vlan header ether type.", arg_u16), &format!("{}", DoubleVlanOuterNonVlanEtherType(arg_u16)) ); //IpUnsupportedVersion assert_eq!( &format!("ReadError: Unsupported IP version number. The IP header contained the unsupported version number {}.", arg_u8), &format!("{}", IpUnsupportedVersion(arg_u8)) ); //Ipv4UnexpectedVersion assert_eq!( &format!("ReadError: Unexpected IP version number. Expected an IPv4 Header but the header contained the version number {}.", arg_u8), &format!("{}", Ipv4UnexpectedVersion(arg_u8)) ); //Ipv4HeaderLengthBad assert_eq!( &format!("ReadError: Bad IPv4 header length. The header length value {} in the IPv4 header is smaller then the ipv4 header.", arg_u8), &format!("{}", Ipv4HeaderLengthBad(arg_u8)) ); //Ipv4TotalLengthTooSmall assert_eq!( &format!("ReadError: Bad IPv4 total length. The total length value {} in the IPv4 header is smaller then the ipv4 header itself.", arg_u16), &format!("{}", Ipv4TotalLengthTooSmall(arg_u16)) ); //Ipv6UnexpectedVersion assert_eq!( &format!("ReadError: Unexpected IP version number. Expected an IPv6 Header but the header contained the version number {}.", arg_u8), &format!("{}", Ipv6UnexpectedVersion(arg_u8)) ); //Ipv6TooManyHeaderExtensions assert_eq!( &format!("ReadError: Too many IPv6 header extensions. There are more then 7 extension headers present, this not supported."), &format!("{}", Ipv6TooManyHeaderExtensions) ); //Ipv6HopByHopHeaderNotAtStart assert_eq!( &format!("ReadError: Encountered an IPv6 hop-by-hop header somwhere else then directly after the IPv6 header. This is not allowed according to RFC 8200."), &format!("{}", Ipv6HopByHopHeaderNotAtStart) ); //IpAuthenticationHeaderTooSmallPayloadLength assert_eq!( &format!("ReadError: Authentication header payload size is smaller then 1 ({}) which is smaller then the minimum size of the header.", arg_u8), &format!("{}", IpAuthenticationHeaderTooSmallPayloadLength(arg_u8)) ); //TcpDataOffsetTooSmall assert_eq!( &format!("ReadError: TCP data offset too small. The data offset value {} in the tcp header is smaller then the tcp header itself.", arg_u8), &format!("{}", TcpDataOffsetTooSmall(arg_u8)) ); //TcpDataOffsetTooSmall assert_eq!( &format!("ReadError: ICMPv6 packet length {} is bigger then can be represented in an u32.", arg_usize), &format!("{}", Icmpv6PacketTooBig(arg_usize)) ); } } /// Check that only for std::io::Error a source is returned #[test] fn read_error_source() { use super::ReadError::*; use std::error::Error; assert_matches!( IoError(std::io::Error::new(std::io::ErrorKind::Other, "some error")).source(), Some(_) ); let none_values = [ UnexpectedEndOfSlice(0), UnexpectedLenOfSlice{ expected: 0, actual: 0 }, DoubleVlanOuterNonVlanEtherType(0), IpUnsupportedVersion(0), Ipv4UnexpectedVersion(0), Ipv4HeaderLengthBad(0), Ipv4TotalLengthTooSmall(0), Ipv6UnexpectedVersion(0), Ipv6TooManyHeaderExtensions, Ipv6HopByHopHeaderNotAtStart, IpAuthenticationHeaderTooSmallPayloadLength(0), TcpDataOffsetTooSmall(0), Icmpv6PacketTooBig(0), ]; for value in &none_values { assert_matches!(value.source(), None); } } #[test] fn read_error_debug() { use super::ReadError::*; let values = [ IoError(std::io::Error::new(std::io::ErrorKind::Other, "some error")), UnexpectedEndOfSlice(0), UnexpectedLenOfSlice{ expected: 0, actual: 0 }, DoubleVlanOuterNonVlanEtherType(0), IpUnsupportedVersion(0), Ipv4UnexpectedVersion(0), Ipv4HeaderLengthBad(0), Ipv4TotalLengthTooSmall(0), Ipv6UnexpectedVersion(0), Ipv6TooManyHeaderExtensions, Ipv6HopByHopHeaderNotAtStart, IpAuthenticationHeaderTooSmallPayloadLength(0), TcpDataOffsetTooSmall(0), Icmpv6PacketTooBig(0), ]; for value in &values { format!("{:?}", value); } } /// Check the write error display fmt generate the expected strings #[test] fn write_error_display() { use WriteError::{IoError, SliceTooSmall}; use ValueError::Ipv4OptionsLengthBad; //IoError { let custom_error = std::io::Error::new(std::io::ErrorKind::Other, "some error"); assert_eq!( &format!("{}", custom_error), &format!("{}", IoError(custom_error)) ); } //ValueError { let value_error = Ipv4OptionsLengthBad(0); assert_eq!( &format!("ValueError: {}", value_error), &format!("{}", WriteError::ValueError(value_error)) ); } //SliceTooSmall { let size = 1234; assert_eq!( &format!("SliceTooSmall: The slice given to write to is too small (required to be at least {} bytes large)", size), &format!("{}", SliceTooSmall(size)) ); } } /// Check the write error display fmt generate the expected strings #[test] fn write_error_source() { use super::WriteError::{IoError, SliceTooSmall}; use std::error::Error; assert_matches!( IoError(std::io::Error::new(std::io::ErrorKind::Other, "some error")).source(), Some(_) ); assert_matches!( WriteError::ValueError(ValueError::Ipv4OptionsLengthBad(0)).source(), Some(_) ); assert_matches!( SliceTooSmall(0).source(), None ); } /// Check that all values return None as source #[test] fn value_error_source() { use ValueError::*; use std::error::Error; let none_values = [ Ipv4OptionsLengthBad(0), Ipv4PayloadLengthTooLarge(0), Ipv6PayloadLengthTooLarge(0), Ipv6ExtensionPayloadTooLarge(0), IpAuthenticationHeaderBadIcvLength(0), Ipv4ExtensionNotReferenced(IpNumber::Icmp), Ipv6ExtensionNotReferenced(IpNumber::Icmp), Ipv6ExtensionNotDefinedReference(IpNumber::Icmp), UdpPayloadLengthTooLarge(0), TcpLengthTooLarge(0), U8TooLarge{ value:0, max:0, field:ErrorField::Ipv4Dscp }, U16TooLarge{ value:0, max:0, field:ErrorField::Ipv4Dscp }, U32TooLarge{ value:0, max:0, field:ErrorField::Ipv4Dscp }, Icmpv6InIpv4, ]; for value in &none_values { assert_matches!(value.source(), None); } } #[test] fn value_error_debug() { use ValueError::*; let values = [ Ipv4OptionsLengthBad(0), Ipv4PayloadLengthTooLarge(0), Ipv6PayloadLengthTooLarge(0), Ipv6ExtensionPayloadTooLarge(0), IpAuthenticationHeaderBadIcvLength(0), Ipv4ExtensionNotReferenced(IpNumber::Icmp), Ipv6ExtensionNotReferenced(IpNumber::Icmp), Ipv6ExtensionNotDefinedReference(IpNumber::Icmp), UdpPayloadLengthTooLarge(0), TcpLengthTooLarge(0), U8TooLarge{ value:0, max:0, field:ErrorField::Ipv4Dscp }, U16TooLarge{ value:0, max:0, field:ErrorField::Ipv4Dscp }, U32TooLarge{ value:0, max:0, field:ErrorField::Ipv4Dscp }, Icmpv6InIpv4, ]; for value in &values { format!("{:?}", value); } } proptest! { #[test] fn value_error_display( value_u8 in any::(), max_u8 in any::(), value_u16 in any::(), max_u16 in any::(), value_u32 in any::(), max_u32 in any::(), arg_usize in any::(), field in error_field_any(), ip_protocol_number in ip_number_any(), ) { use ValueError::*; //Ipv4OptionsLengthBad assert_eq!( &format!("Bad IPv4 'options_len'. The IPv4 options length ({} bytes) is either not a multiple of 4 bytes or bigger then the maximum of 40 bytes.", arg_usize), &format!("{}", Ipv4OptionsLengthBad(arg_usize)) ); //Ipv4PayloadLengthTooLarge assert_eq!( &format!("IPv4 'total_legnth' too large. The IPv4 header and payload have a larger size ({} bytes) than can be be represented by the 'total_legnth' field in the IPv4 header.", arg_usize), &format!("{}", Ipv4PayloadLengthTooLarge(arg_usize)) ); //Ipv6PayloadLengthTooLarge assert_eq!( &format!("IPv6 'payload_length' too large. The IPv6 header block & payload size ({} bytes) is larger then what can be be represented by the 'payload_length' field in the IPv6 header.", arg_usize), &format!("{}", Ipv6PayloadLengthTooLarge(arg_usize)) ); //Ipv6ExtensionPayloadTooSmall assert_eq!( &format!("IPv6 extensions header payload length is too small. The payload size ({} bytes) is less then 6 octets which is the minimum IPv6 extension header payload size.", arg_usize), &format!("{}", Ipv6ExtensionPayloadTooSmall(arg_usize)) ); //Ipv6ExtensionPayloadTooLarge assert_eq!( &format!("IPv6 extensions header payload length is too large. The payload size ({} bytes) is larger then what can be be represented by the 'extended header size' field in an IPv6 extension header.", arg_usize), &format!("{}", Ipv6ExtensionPayloadTooLarge(arg_usize)) ); //Ipv6ExtensionPayloadLengthUnaligned assert_eq!( &format!("IPv6 extensions header 'payload length ({} bytes) + 2' is not multiple of 8 (+ 2 for the `next_header` and `header_length` fields). This is required as the header length field can only express lengths in multiple of 8 bytes.", arg_usize), &format!("{}", Ipv6ExtensionPayloadLengthUnaligned(arg_usize)) ); //IpAuthenticationHeaderBadIcvLength assert_eq!( &format!("IP authentication header 'raw_icv' value has a length ({} bytes) is either not a multiple of 4 bytes or bigger then the maximum of 1016 bytes.", arg_usize), &format!("{}", IpAuthenticationHeaderBadIcvLength(arg_usize)) ); //Ipv4ExtensionNotReferenced assert_eq!( &format!("IPv4 extensions '{:?}' is defined but is not referenced by any of the 'next_header' of the other extension headers or the 'protocol' field of the IPv4 header.", ip_protocol_number), &format!("{}", Ipv4ExtensionNotReferenced(ip_protocol_number)) ); //Ipv6ExtensionHopByHopNotAtStart assert_eq!( "IPv6 extensions hop-by-hop is not located directly after the IPv6 header (required by IPv6).", &format!("{}", Ipv6ExtensionHopByHopNotAtStart) ); //Ipv6ExtensionNotReferenced assert_eq!( &format!("IPv6 extensions '{:?}' is defined but is not referenced by any of the 'next_header' of the other extension headers or the IPv6 header.", ip_protocol_number), &format!("{}", Ipv6ExtensionNotReferenced(ip_protocol_number)) ); //Ipv6ExtensionNotDefinedReference assert_eq!( &format!("IPv6 extensions '{:?}' is referenced by the 'next_header' field of an extension headers or the IPv6 header but is not defined in the 'Ipv6Extensions'.", ip_protocol_number), &format!("{}", Ipv6ExtensionNotDefinedReference(ip_protocol_number)) ); //UdpPayloadLengthTooLarge assert_eq!( &format!("UDP 'length' too large. The UDP length ({} bytes) is larger then what can be be represented by the 'length' field in the UDP header.", arg_usize), &format!("{}", UdpPayloadLengthTooLarge(arg_usize)) ); //TcpLengthTooLarge assert_eq!( &format!("TCP length too large. The TCP packet length ({} bytes) is larger then what is supported.", arg_usize), &format!("{}", TcpLengthTooLarge(arg_usize)) ); //U8TooLarge assert_eq!( &format!("The value {} of the field '{}' is larger then the allowed maximum of {}.", value_u8, field, max_u8), &format!("{}", U8TooLarge{ value: value_u8, max: max_u8, field: field.clone() }) ); //U16TooLarge assert_eq!( &format!("The value {} of the field '{}' is larger then the allowed maximum of {}.", value_u16, field, max_u16), &format!("{}", U16TooLarge{ value: value_u16, max: max_u16, field: field.clone() }) ); //U32TooLarge assert_eq!( &format!("The value {} of the field '{}' is larger then the allowed maximum of {}.", value_u32, field, max_u32), &format!("{}", U32TooLarge{ value: value_u32, max: max_u32, field: field.clone() }) ); // Icmpv6InIpv4 assert_eq!( &format!("ICMPv6 packet can not be combined with IPv4 headers."), &format!("{}", Icmpv6InIpv4) ); } } #[test] fn error_field_display() { use ErrorField::*; assert_eq!("Ipv4Header.payload_len", &format!("{}", Ipv4PayloadLength)); assert_eq!("Ipv4Header.differentiated_services_code_point", &format!("{}", Ipv4Dscp)); assert_eq!("Ipv4Header.explicit_congestion_notification", &format!("{}", Ipv4Ecn)); assert_eq!("Ipv4Header.fragments_offset", &format!("{}", Ipv4FragmentsOffset)); assert_eq!("Ipv6Header.flow_label", &format!("{}", Ipv6FlowLabel)); assert_eq!("Ipv6FragmentHeader.fragment_offset", &format!("{}", Ipv6FragmentOffset)); assert_eq!("SingleVlanHeader.priority_code_point", &format!("{}", VlanTagPriorityCodePoint)); assert_eq!("SingleVlanHeader.vlan_identifier", &format!("{}", VlanTagVlanId)); } etherparse-0.13.0/tests/internet/ip.rs000064400000000000000000000411041046102023000160170ustar 00000000000000use super::super::*; mod ip_header { use super::*; use crate::ip_number::*; use std::io::Cursor; const EXTESION_KNOWN_IP_NUMBERS : [u8;5] = [ AUTH, IPV6_DEST_OPTIONS, IPV6_HOP_BY_HOP, IPV6_FRAG, IPV6_ROUTE, ]; fn combine_v4(v4: &Ipv4Header, ext: &Ipv4Extensions) -> IpHeader { IpHeader::Version4( { let mut v4 = v4.clone(); v4.protocol = if ext.auth.is_some() { AUTH } else { UDP }; v4.header_checksum = v4.calc_header_checksum().unwrap(); v4 }, ext.clone(), ) } fn combine_v6(v6: &Ipv6Header, ext: &Ipv6Extensions) -> IpHeader { let (ext, next_header) = { let mut ext = ext.clone(); let next_header = ext.set_next_headers(UDP); (ext, next_header) }; IpHeader::Version6( { let mut v6 = v6.clone(); v6.next_header = next_header; v6 }, ext, ) } proptest!{ #[test] #[allow(deprecated)] fn read_from_slice( v4 in ipv4_any(), v4_exts in ipv4_extensions_any(), ) { let header = combine_v4(&v4, &v4_exts); let mut buffer = Vec::with_capacity(header.header_len()); header.write(&mut buffer).unwrap(); let actual = IpHeader::read_from_slice(&buffer).unwrap(); assert_eq!(actual.0, header); assert_eq!(actual.1, header.next_header().unwrap()); assert_eq!(actual.2, &buffer[buffer.len()..]); } } proptest!{ #[test] fn from_slice( v4 in ipv4_any(), v4_exts in ipv4_extensions_any(), v6 in ipv6_any(), v6_exts in ipv6_extensions_any(), ) { // v4 { let header = combine_v4(&v4, &v4_exts); let mut buffer = Vec::with_capacity(header.header_len() + 1); header.write(&mut buffer).unwrap(); buffer.push(1); // add some value to check the return slice // read { let actual = IpHeader::from_slice(&buffer).unwrap(); assert_eq!(actual.0, header); assert_eq!(actual.1, header.next_header().unwrap()); assert_eq!(actual.2, &buffer[buffer.len() - 1..]); } // read error ipv4 header IpHeader::from_slice(&buffer[..1]).unwrap_err(); // read error ipv4 extensions if v4_exts.header_len() > 0 { IpHeader::from_slice(&buffer[..v4.header_len() + 1]).unwrap_err(); } } // v6 { let header = combine_v6(&v6, &v6_exts); let mut buffer = Vec::with_capacity(header.header_len() + 1); header.write(&mut buffer).unwrap(); buffer.push(1); // add some value to check the return slice // read { let actual = IpHeader::from_slice(&buffer).unwrap(); assert_eq!(actual.0, header); assert_eq!(actual.1, header.next_header().unwrap()); assert_eq!(actual.2, &buffer[buffer.len() - 1..]); } // read error header IpHeader::from_slice(&buffer[..1]).unwrap_err(); // read error ipv4 extensions if v6_exts.header_len() > 0 { IpHeader::from_slice(&buffer[..Ipv6Header::SERIALIZED_SIZE + 1]).unwrap_err(); } } } } proptest!{ #[test] fn read( v4 in ipv4_any(), v4_exts in ipv4_extensions_any(), v6 in ipv6_any(), v6_exts in ipv6_extensions_any(), ) { // v4 { let header = combine_v4(&v4, &v4_exts); let mut buffer = Vec::with_capacity(header.header_len()); header.write(&mut buffer).unwrap(); // read { let mut cursor = Cursor::new(&buffer); let actual = IpHeader::read(&mut cursor).unwrap(); assert_eq!(actual.0, header); assert_eq!(actual.1, header.next_header().unwrap()); } // read error ipv4 header { let mut cursor = Cursor::new(&buffer[..1]); IpHeader::read(&mut cursor).unwrap_err(); } // read error ipv4 extensions if v4_exts.header_len() > 0 { let mut cursor = Cursor::new(&buffer[..v4.header_len() + 1]); IpHeader::read(&mut cursor).unwrap_err(); } } // v6 { let header = combine_v6(&v6, &v6_exts); let mut buffer = Vec::with_capacity(header.header_len()); header.write(&mut buffer).unwrap(); // read { let mut cursor = Cursor::new(&buffer); let actual = IpHeader::read(&mut cursor).unwrap(); assert_eq!(actual.0, header); assert_eq!(actual.1, header.next_header().unwrap()); } // read error header { let mut cursor = Cursor::new(&buffer[..1]); IpHeader::read(&mut cursor).unwrap_err(); } // read error ipv4 extensions if v6_exts.header_len() > 0 { let mut cursor = Cursor::new(&buffer[..Ipv6Header::SERIALIZED_SIZE + 1]); IpHeader::read(&mut cursor).unwrap_err(); } } } } proptest!{ #[test] fn write( v4 in ipv4_any(), v4_exts in ipv4_extensions_any(), v6 in ipv6_any(), v6_exts in ipv6_extensions_any(), ) { // v4 { let header = combine_v4(&v4, &v4_exts); let mut buffer = Vec::with_capacity(header.header_len()); header.write(&mut buffer).unwrap(); let actual = IpHeader::from_slice(&buffer).unwrap().0; assert_eq!(header, actual); // write error v4 header let mut writer = TestWriter::with_max_size(1); assert_eq!( writer.error_kind(), header.write(&mut writer).unwrap_err().io_error().unwrap().kind() ); // write error v6 extension headers if v4_exts.header_len() > 0 { let mut writer = TestWriter::with_max_size(v4.header_len() + 1); assert_eq!( writer.error_kind(), header.write(&mut writer).unwrap_err().io_error().unwrap().kind() ); } } // v6 { let header = combine_v6(&v6, &v6_exts); // normal write let mut buffer = Vec::with_capacity(header.header_len()); header.write(&mut buffer).unwrap(); let actual = IpHeader::from_slice(&buffer).unwrap().0; assert_eq!(header, actual); // write error v6 header { let mut writer = TestWriter::with_max_size(1); assert_eq!( writer.error_kind(), header.write(&mut writer).unwrap_err().io_error().unwrap().kind() ); } // write error v6 extension headers if v6_exts.header_len() > 0 { let mut writer = TestWriter::with_max_size(Ipv6Header::SERIALIZED_SIZE + 1); assert_eq!( writer.error_kind(), header.write(&mut writer).unwrap_err().io_error().unwrap().kind() ); } } } } proptest!{ #[test] fn header_len( v4 in ipv4_any(), v4_exts in ipv4_extensions_any(), v6 in ipv6_any(), v6_exts in ipv6_extensions_any(), ) { assert_eq!( v4.header_len() + v4_exts.header_len(), IpHeader::Version4(v4, v4_exts).header_len() ); assert_eq!( Ipv6Header::SERIALIZED_SIZE + v6_exts.header_len(), IpHeader::Version6(v6, v6_exts).header_len() ); } } proptest!{ #[test] fn next_header( v4 in ipv4_any(), v4_exts in ipv4_extensions_any(), v6 in ipv6_any(), v6_exts in ipv6_extensions_any(), post_header in any::() .prop_filter("Must be a non ipv6 header relevant ip number".to_owned(), |v| !EXTESION_KNOWN_IP_NUMBERS.iter().any(|&x| v == &x) ) ) { { let mut header = v4.clone(); let mut exts = v4_exts.clone(); header.protocol = exts.set_next_headers(post_header); assert_eq!( Ok(post_header), IpHeader::Version4(header, exts).next_header() ); } { let mut header = v6.clone(); let mut exts = v6_exts.clone(); header.next_header = exts.set_next_headers(post_header); assert_eq!( Ok(post_header), IpHeader::Version6(header, exts).next_header() ); } } } proptest!{ #[test] fn set_payload_len( v4 in ipv4_any(), v4_exts in ipv4_extensions_any(), v6 in ipv6_any(), v6_exts in ipv6_extensions_any(), payload_len in 0usize..10 ) { // ipv4 (with valid payload length) { let mut actual = IpHeader::Version4( v4.clone(), v4_exts.clone() ); actual.set_payload_len(payload_len).unwrap(); assert_eq!( actual, IpHeader::Version4( { let mut re = v4.clone(); re.set_payload_len(v4_exts.header_len() + payload_len).unwrap(); re }, v4_exts.clone() ) ); } // ipv6 (with valid payload length) { let mut actual = IpHeader::Version6( v6.clone(), v6_exts.clone() ); actual.set_payload_len(payload_len).unwrap(); assert_eq!( actual, IpHeader::Version6( { let mut re = v6.clone(); re.set_payload_length(v6_exts.header_len() + payload_len).unwrap(); re }, v6_exts.clone() ) ); } // v4 (with invalid size) { let mut actual = IpHeader::Version4( v4.clone(), v4_exts.clone() ); assert_matches!( actual.set_payload_len(usize::MAX), Err(_) ); } // v6 (with invalid size) { let mut actual = IpHeader::Version6( v6.clone(), v6_exts.clone() ); assert_matches!( actual.set_payload_len(usize::MAX), Err(_) ); } } } proptest!{ #[test] fn debug( v4 in ipv4_any(), v4_exts in ipv4_extensions_any(), v6 in ipv6_any(), v6_exts in ipv6_extensions_any(), ) { assert_eq!( format!( "Version4({:?}, {:?})", v4, v4_exts ), format!("{:?}", IpHeader::Version4(v4, v4_exts)) ); assert_eq!( format!( "Version6({:?}, {:?})", v6, v6_exts ), format!("{:?}", IpHeader::Version6(v6, v6_exts)) ); } } proptest!{ #[test] fn clone_eq( v4 in ipv4_any(), v4_exts in ipv4_extensions_any(), v6 in ipv6_any(), v6_exts in ipv6_extensions_any(), ) { { let v4 = IpHeader::Version4(v4, v4_exts); assert_eq!(v4, v4.clone()); } { let v6 = IpHeader::Version6(v6, v6_exts); assert_eq!(v6, v6.clone()); } } } #[test] fn read_ip_header_version_error() { use std::io::Cursor; let input = Ipv6Header { traffic_class: 1, flow_label: 0x81806, payload_length: 0x8021, next_header: 30, hop_limit: 40, source: [1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16], destination: [21,22,23,24,25,26,27,28, 29,30,31,32,33,34,35,36] }; //serialize let mut buffer: Vec = Vec::with_capacity(20); input.write(&mut buffer).unwrap(); assert_eq!(40, buffer.len()); //corrupt the version buffer[0] = 0xff; //deserialize with read { let mut cursor = Cursor::new(&buffer); assert_matches!(IpHeader::read(&mut cursor), Err(ReadError::IpUnsupportedVersion(0xf))); } //deserialize with read_from_slice assert_matches!( IpHeader::from_slice(&buffer), Err(ReadError::IpUnsupportedVersion(0xf)) ); //also check that an error is thrown when the slice is too small //to even read the version assert_matches!( IpHeader::from_slice(&buffer[buffer.len()..]), Err(ReadError::UnexpectedEndOfSlice(1)) ); } } // mod ip_header mod ip_number { use super::*; #[test] fn is_ipv6_ext_header_value() { use crate::IpNumber; use crate::ip_number::*; let ext_ids = [ IPV6_HOP_BY_HOP, IPV6_ROUTE, IPV6_FRAG, ENCAP_SEC, AUTH, IPV6_DEST_OPTIONS, MOBILITY, HIP, SHIM6 as u8, EXP0 as u8, EXP1 as u8 ]; for i in 0..std::u8::MAX { assert_eq!( ext_ids.contains(&i), IpNumber::is_ipv6_ext_header_value(i) ); } } #[test] fn ip_number_eq_check() { use crate::ip_number::*; use crate::IpNumber::*; let pairs = &[ (IPV6_HOP_BY_HOP, IPv6HeaderHopByHop), (ICMP, Icmp), (IGMP, Igmp), (GGP, Ggp), (IPV4, IPv4), (STREAM, Stream), (TCP, Tcp), (UDP, Udp), (IPV6, Ipv6), (IPV6_ROUTE, IPv6RouteHeader), (IPV6_FRAG, IPv6FragmentationHeader), (ENCAP_SEC, EncapsulatingSecurityPayload), (AUTH, AuthenticationHeader), (IPV6_DEST_OPTIONS, IPv6DestinationOptions), (MOBILITY, MobilityHeader), (HIP, Hip), (SHIM6, Shim6), (EXP0, ExperimentalAndTesting0), (EXP1, ExperimentalAndTesting1), ]; for (raw, enum_value) in pairs { assert_eq!(*raw, *enum_value as u8); } } #[test] fn debug() { assert_eq!( "IPv6HeaderHopByHop", &format!("{:?}", IpNumber::IPv6HeaderHopByHop) ); } #[test] fn clone_eq() { let value = IpNumber::IPv6HeaderHopByHop; assert_eq!(value, value.clone()); } } // mod ip_numberetherparse-0.13.0/tests/internet/ip_authentication.rs000064400000000000000000000170541046102023000211250ustar 00000000000000use super::super::*; use std::io::Cursor; #[test] fn new_and_set_icv() { use ValueError::*; struct Test { icv: &'static [u8], ok: bool } let tests = [ // ok Test{ icv: &[], ok: true }, Test{ icv: &[1,2,3,4], ok: true }, Test{ icv: &[1,2,3,4,5,6,7,8], ok: true }, Test{ icv: &[1,2,3,4,5,6,7,8,9,10,11,12], ok: true }, Test{ icv: &[0;0xfe*4], ok: true }, // unaligned Test{ icv: &[1], ok: false }, Test{ icv: &[1,2,3], ok: false }, Test{ icv: &[1,2,3,4,5], ok: false }, Test{ icv: &[1,2,3,4,5,6,7], ok: false }, // too big Test{ icv: &[0;0xff*4], ok: false }, ]; for test in tests.iter() { // new { let a = IpAuthenticationHeader::new(5, 6, 7, test.icv); if test.ok { let unwrapped = a.unwrap(); assert_eq!(5, unwrapped.next_header); assert_eq!(6, unwrapped.spi); assert_eq!(7, unwrapped.sequence_number); assert_eq!(test.icv, unwrapped.raw_icv()); } else { assert_eq!( Err(IpAuthenticationHeaderBadIcvLength(test.icv.len())), a ); } } // set_raw_icv { let mut header = IpAuthenticationHeader::new(5, 6, 7, &[0;4]).unwrap(); let result = header.set_raw_icv(test.icv); assert_eq!(5, header.next_header); assert_eq!(6, header.spi); assert_eq!(7, header.sequence_number); if test.ok { assert_eq!(Ok(()), result); assert_eq!(test.icv, header.raw_icv()); } else { assert_eq!( Err(IpAuthenticationHeaderBadIcvLength(test.icv.len())), result ); assert_eq!(&[0;4], header.raw_icv()); } } } } proptest! { #[test] fn from_slice_slice_smaller_12(len in 0..12usize) { use ReadError::*; let data = [0;12]; assert_matches!( IpAuthenticationHeaderSlice::from_slice(&data[..len]), Err(UnexpectedEndOfSlice(12)) ); assert_matches!( IpAuthenticationHeader::from_slice(&data[..len]), Err(UnexpectedEndOfSlice(12)) ); } } #[test] fn from_slice_bad_header_len() { use ReadError::*; let data = [0;16]; assert_matches!( IpAuthenticationHeaderSlice::from_slice(&data[..]), Err(IpAuthenticationHeaderTooSmallPayloadLength(0)) ); assert_matches!( IpAuthenticationHeader::from_slice(&data[..]), Err(IpAuthenticationHeaderTooSmallPayloadLength(0)) ); } proptest! { #[test] fn header_len(expected in ip_authentication_any()) { assert_eq!(expected.header_len(), expected.raw_icv().len() + 12); } } proptest! { #[test] fn write_read(expected in ip_authentication_any()) { let buffer = { let mut buffer: Vec = Vec::new(); expected.write(&mut buffer).unwrap(); // add some extra data buffer.push(1); buffer.push(2); buffer }; // from_slice { let actual = IpAuthenticationHeaderSlice::from_slice(&buffer).unwrap(); assert_eq!(actual.slice(), &buffer[..buffer.len()-2]); assert_eq!(actual.next_header(), expected.next_header); assert_eq!(actual.spi(), expected.spi); assert_eq!(actual.sequence_number(), expected.sequence_number); assert_eq!(actual.raw_icv(), expected.raw_icv()); assert_eq!(actual.to_header(), expected); // clone and equal check for slice assert_eq!(actual.clone(), actual); } // from_slice_unchecked unsafe { let actual = IpAuthenticationHeaderSlice::from_slice_unchecked(&buffer); assert_eq!(actual.slice(), &buffer[..buffer.len()-2]); } // from_slice { let (actual, rest) = IpAuthenticationHeader::from_slice(&buffer).unwrap(); assert_eq!(actual, expected); assert_eq!(rest, &buffer[buffer.len()-2..]); } // read { let mut cursor = Cursor::new(&buffer); let actual = IpAuthenticationHeader::read(&mut cursor).unwrap(); assert_eq!(expected, actual); assert_eq!(cursor.position(), (buffer.len()-2) as u64); } // test error when the slice is smaller then the data lenght for len in 0..buffer.len()-3 { use ReadError::*; assert_matches!( IpAuthenticationHeader::from_slice(&buffer[..len]), Err(UnexpectedEndOfSlice(_)) ); assert_matches!( IpAuthenticationHeaderSlice::from_slice(&buffer[..len]), Err(UnexpectedEndOfSlice(_)) ); { let mut cursor = Cursor::new(&buffer[..len]); assert_matches!( IpAuthenticationHeader::read(&mut cursor), Err(IoError(_)) ); } } } } /// Test that an IoError is correctly forwarded #[test] pub fn write_io_error() { let header = IpAuthenticationHeader::new( 1, 2, 3, &[4,5,6,7] ).unwrap(); // iterate through all too short lenghts for len in 0..header.header_len() { let mut writer = TestWriter::with_max_size(len); assert_eq!( writer.error_kind(), header.write(&mut writer).unwrap_err().io_error().unwrap().kind() ); } } #[test] pub fn read_too_small_payload_len() { let input = [0u8;16]; // the 2nd let mut cursor = Cursor::new(&input); assert_matches!( IpAuthenticationHeader::read(&mut cursor), Err(ReadError::IpAuthenticationHeaderTooSmallPayloadLength(0)) ); } /// Dummy test for the clone function #[test] pub fn clone() { let a = IpAuthenticationHeader::new(0,0,0,&[0;4]); assert_eq!(a.clone(), a); } #[test] pub fn partial_eq() { let a = IpAuthenticationHeader::new(0,0,0,&[0;4]); //equal assert!(a == IpAuthenticationHeader::new(0,0,0,&[0;4])); //not equal tests assert!(a != IpAuthenticationHeader::new(1,0,0,&[0;4])); assert!(a != IpAuthenticationHeader::new(0,1,0,&[0;4])); assert!(a != IpAuthenticationHeader::new(0,0,1,&[0;4])); assert!(a != IpAuthenticationHeader::new(0,0,0,&[0,1,0,0])); assert!(a != IpAuthenticationHeader::new(0,0,1,&[])); assert!(a != IpAuthenticationHeader::new(0,0,1,&[0;8])); } proptest! { #[test] /// Test for the manually implemented debug trait fn debug(input in ip_authentication_any()) { assert_eq!( &format!( "IpAuthenticationHeader {{ next_header: {}, spi: {}, sequence_number: {}, raw_icv: {:?} }}", input.next_header, input.spi, input.sequence_number, input.raw_icv()), &format!("{:?}", input) ); let buffer = { let mut buffer = Vec::with_capacity(input.header_len()); input.write(&mut buffer).unwrap(); buffer }; let slice = IpAuthenticationHeaderSlice::from_slice(&buffer).unwrap(); assert_eq!( &format!( "IpAuthenticationHeaderSlice {{ slice: {:?} }}", slice.slice() ), &format!("{:?}", slice) ); } } etherparse-0.13.0/tests/internet/ipv4.rs000064400000000000000000000666411046102023000163060ustar 00000000000000use super::super::*; use std::io::Cursor; use proptest::prelude::*; mod header { use super::*; #[test] fn default() { let default : Ipv4Header = Default::default(); assert_eq!(5, default.ihl()); assert_eq!(0, default.differentiated_services_code_point); assert_eq!(0, default.explicit_congestion_notification); assert_eq!(0, default.payload_len); assert_eq!(0, default.identification); assert_eq!(true, default.dont_fragment); assert_eq!(false, default.more_fragments); assert_eq!(0, default.fragments_offset); assert_eq!(0, default.time_to_live); assert_eq!(0, default.protocol); assert_eq!(0, default.header_checksum); assert_eq!([0;4], default.source); assert_eq!([0;4], default.destination); } proptest! { #[test] fn eq(a in ipv4_any(), b in ipv4_any()) { //check identity equality assert!(a == a); assert!(b == b); //check every field //differentiated_services_code_point assert_eq!( a.differentiated_services_code_point == b.differentiated_services_code_point, a == { let mut other = a.clone(); other.differentiated_services_code_point = b.differentiated_services_code_point; other } ); //explicit_congestion_notification assert_eq!( a.explicit_congestion_notification == b.explicit_congestion_notification, a == { let mut other = a.clone(); other.explicit_congestion_notification = b.explicit_congestion_notification; other } ); //payload_len assert_eq!( a.payload_len == b.payload_len, a == { let mut other = a.clone(); other.payload_len = b.payload_len; other } ); //identification assert_eq!( a.identification == b.identification, a == { let mut other = a.clone(); other.identification = b.identification; other } ); //dont_fragment assert_eq!( a.dont_fragment == b.dont_fragment, a == { let mut other = a.clone(); other.dont_fragment = b.dont_fragment; other } ); //more_fragments assert_eq!( a.more_fragments == b.more_fragments, a == { let mut other = a.clone(); other.more_fragments = b.more_fragments; other } ); //fragments_offset assert_eq!( a.fragments_offset == b.fragments_offset, a == { let mut other = a.clone(); other.fragments_offset = b.fragments_offset; other } ); //time_to_live assert_eq!( a.time_to_live == b.time_to_live, a == { let mut other = a.clone(); other.time_to_live = b.time_to_live; other } ); //protocol assert_eq!( a.protocol == b.protocol, a == { let mut other = a.clone(); other.protocol = b.protocol; other } ); //header_checksum assert_eq!( a.header_checksum == b.header_checksum, a == { let mut other = a.clone(); other.header_checksum = b.header_checksum; other } ); //source assert_eq!( a.source == b.source, a == { let mut other = a.clone(); other.source = b.source; other } ); //destination assert_eq!( a.destination == b.destination, a == { let mut other = a.clone(); other.destination = b.destination; other } ); //options assert_eq!( a.options() == b.options(), a == { let mut other = a.clone(); other.set_options(b.options()).unwrap(); other } ); } } proptest! { #[test] fn debug(input in ipv4_any()) { assert_eq!(&format!("Ipv4Header {{ ihl: {}, differentiated_services_code_point: {}, explicit_congestion_notification: {}, payload_len: {}, identification: {}, dont_fragment: {}, more_fragments: {}, fragments_offset: {}, time_to_live: {}, protocol: {}, header_checksum: {}, source: {:?}, destination: {:?}, options: {:?} }}", input.ihl(), input.differentiated_services_code_point, input.explicit_congestion_notification, input.payload_len, input.identification, input.dont_fragment, input.more_fragments, input.fragments_offset, input.time_to_live, input.protocol, input.header_checksum, input.source, input.destination, input.options() ), &format!("{:?}", input) ); } } proptest! { #[test] fn new(source_ip in prop::array::uniform4(any::()), dest_ip in prop::array::uniform4(any::()), ttl in any::(), payload_len in any::()) { let result = Ipv4Header::new( payload_len, ttl, ip_number::UDP, source_ip, dest_ip ); assert_eq!(result.differentiated_services_code_point, 0); assert_eq!(result.explicit_congestion_notification, 0); assert_eq!(result.payload_len, payload_len); assert_eq!(result.identification, 0); assert_eq!(result.dont_fragment, true); assert_eq!(result.more_fragments, false); assert_eq!(result.fragments_offset, 0); assert_eq!(result.time_to_live, ttl); assert_eq!(result.protocol, ip_number::UDP); assert_eq!(result.header_checksum, 0); assert_eq!(result.source, source_ip); assert_eq!(result.destination, dest_ip); assert_eq!(result.options(), &[]); } } #[test] fn set_payload_len() { let mut header = Ipv4Header::new(0, 0, ip_number::UDP, [0;4], [0;4]); //add options (to make sure they are included in the calculation) header.set_options(&[1,2,3,4]).unwrap(); //zero check assert_matches!(header.set_payload_len(0), Ok(())); assert_eq!(header.total_len(), 24); //max check const MAX: usize = (std::u16::MAX as usize) - Ipv4Header::SERIALIZED_SIZE - 4; assert_matches!(header.set_payload_len(MAX), Ok(())); assert_eq!(header.total_len(), std::u16::MAX); const OVER_MAX: usize = MAX + 1; assert_matches!(header.set_payload_len(OVER_MAX), Err(ValueError::Ipv4PayloadLengthTooLarge(OVER_MAX))); } #[test] fn set_options() { //length of 1 { let mut header: Ipv4Header = Default::default(); let options = [1,2,3,4]; assert_eq!(header.set_options(&options), Ok(())); assert_eq!(&options, header.options()); assert_eq!(24, header.header_len()); assert_eq!(24, header.total_len()); assert_eq!(6, header.ihl()); //length 0 assert_eq!(header.set_options(&[]), Ok(())); assert_eq!(&options[..0], header.options()); assert_eq!(20, header.header_len()); assert_eq!(20, header.total_len()); assert_eq!(5, header.ihl()); } //maximum length (40) { let mut header: Ipv4Header = Default::default(); let options = [1,2,3,4,5,6,7,8, 9,10,11,12,13,14,15,16, 17,18,19,20,21,22,23,24, 25,26,27,28,29,30,31,32, 33,34,35,36,37,38,39,40]; assert_eq!(header.set_options(&options), Ok(())); assert_eq!(&options[..], header.options()); assert_eq!(60, header.header_len()); assert_eq!(60, header.total_len()); assert_eq!(15, header.ihl()); } //errors { let buffer: [u8;50] = [0;50]; for len in &[ 1usize,2,3, //unaligned 5,6,7, 41,44 //over max ] { let mut header: Ipv4Header = Default::default(); //expect an error use self::ValueError::Ipv4OptionsLengthBad; assert_eq!( Err(Ipv4OptionsLengthBad(*len)), header.set_options(&buffer[..*len]) ); //check value was not taken assert_eq!(&buffer[..0], header.options()); assert_eq!(20, header.header_len()); assert_eq!(20, header.total_len()); assert_eq!(5, header.ihl()); } } } #[test] fn calc_header_checksum() { let base: Ipv4Header = Ipv4Header::new( 40, 4, // ttl ip_number::UDP, [192, 168, 1, 1], // source [212, 10, 11, 123] // destination ); //without options { //dont_fragment && !more_fragments let header = base.clone(); assert_eq!(0xd582, header.calc_header_checksum().unwrap()); // !dont_fragment && more_fragments let header = { let mut header = base.clone(); header.dont_fragment = false; header.more_fragments = true; header }; assert_eq!(0xf582, header.calc_header_checksum().unwrap()); } //with options { let header = { let mut header = base.clone(); header.payload_len = 40 - 8; header.set_options(&[1,2,3,4,5,6,7,8]).unwrap(); header }; assert_eq!(0xc36e, header.calc_header_checksum().unwrap()); } } proptest! { #[test] fn write(ref base_header in ipv4_any()) { let header = { let mut header = base_header.clone(); // set the header checksum to something else to // ensure it is calculated during the write call header.header_checksum = 0; header }; // normal write { //serialize let buffer = { let mut buffer: Vec = Vec::with_capacity(header.header_len()); header.write(&mut buffer).unwrap(); buffer }; assert_eq!(header.header_len(), buffer.len()); //deserialize let mut cursor = Cursor::new(&buffer); let result = Ipv4Header::read(&mut cursor).unwrap(); assert_eq!(header.header_len(), cursor.position() as usize); //check equivalence (with calculated checksum) let header_with_checksum = { let mut h = header.clone(); h.header_checksum = h.calc_header_checksum().unwrap(); h }; assert_eq!(header_with_checksum, result); } // io error for len in 0..header.header_len() { // write { let mut writer = TestWriter::with_max_size(len); assert_eq!( writer.error_kind(), header.write(&mut writer).unwrap_err().io_error().unwrap().kind() ); } // write raw { let mut writer = TestWriter::with_max_size(len); assert_eq!( writer.error_kind(), header.write_raw(&mut writer).unwrap_err().io_error().unwrap().kind() ); } } } } proptest! { #[test] #[allow(deprecated)] fn read_from_slice(ref input in ipv4_any()) { //serialize let mut buffer: Vec = Vec::with_capacity(input.header_len()); input.write_raw(&mut buffer).unwrap(); assert_eq!(input.header_len(), buffer.len()); //deserialize (read_from_slice) let result = Ipv4Header::read_from_slice(&buffer).unwrap(); assert_eq!(input, &result.0); assert_eq!(&buffer[usize::from(input.header_len())..], result.1); } } #[test] fn is_fragmenting_payload() { // not fragmenting { let mut header : Ipv4Header = Default::default(); header.fragments_offset = 0; header.more_fragments = false; assert_eq!(false, header.is_fragmenting_payload()); } // fragmenting based on offset { let mut header : Ipv4Header = Default::default(); header.fragments_offset = 1; header.more_fragments = false; assert!(header.is_fragmenting_payload()); } // fragmenting based on more_fragments { let mut header : Ipv4Header = Default::default(); header.fragments_offset = 0; header.more_fragments = true; assert!(header.is_fragmenting_payload()); } } } mod slice { use super::*; #[test] fn is_fragmenting_payload() { // not fragmenting { let buffer = { let mut header : Ipv4Header = Default::default(); header.fragments_offset = 0; header.more_fragments = false; let mut buffer = Vec::with_capacity(header.header_len()); header.write(&mut buffer).unwrap(); buffer }; let slice = Ipv4HeaderSlice::from_slice(&buffer).unwrap(); assert_eq!(false, slice.is_fragmenting_payload()); } // fragmenting based on offset { let buffer = { let mut header : Ipv4Header = Default::default(); header.fragments_offset = 1; header.more_fragments = false; let mut buffer = Vec::with_capacity(header.header_len()); header.write(&mut buffer).unwrap(); buffer }; let slice = Ipv4HeaderSlice::from_slice(&buffer).unwrap(); assert!(slice.is_fragmenting_payload()); } // fragmenting based on more_fragments { let buffer = { let mut header : Ipv4Header = Default::default(); header.fragments_offset = 0; header.more_fragments = true; let mut buffer = Vec::with_capacity(header.header_len()); header.write(&mut buffer).unwrap(); buffer }; let slice = Ipv4HeaderSlice::from_slice(&buffer).unwrap(); assert!(slice.is_fragmenting_payload()); } } } #[test] fn range_errors() { use crate::ValueError::*; use crate::ErrorField::*; fn test_range_methods(input: &Ipv4Header, expected: ValueError) { //check_ranges assert_eq!(expected.clone(), input .calc_header_checksum() .unwrap_err()); //write { let mut buffer: Vec = Vec::new(); let result = input.write(&mut buffer); assert_eq!(0, buffer.len()); assert_eq!(Some(expected.clone()), result .unwrap_err() .value_error()); } //write_raw { let mut buffer: Vec = Vec::new(); let result = input.write_raw(&mut buffer); assert_eq!(0, buffer.len()); assert_eq!(Some(expected.clone()), result .unwrap_err() .value_error()); } } //dscp { let value = { let mut value: Ipv4Header = Default::default(); value.differentiated_services_code_point = 0x40; value }; test_range_methods( &value, U8TooLarge{value: 0x40, max: 0x3f, field: Ipv4Dscp} ); } //ecn { let value = { let mut value: Ipv4Header = Default::default(); value.explicit_congestion_notification = 0x4; value }; test_range_methods( &value, U8TooLarge{value: 0x4, max: 0x3, field: Ipv4Ecn} ); } //fragmentation offset { let value = { let mut value: Ipv4Header = Default::default(); value.fragments_offset = 0x2000; value }; test_range_methods( &value, U16TooLarge{value: 0x2000, max: 0x1FFF, field: Ipv4FragmentsOffset} ); } //payload len { const MAX_PAYLOAD_LEN: u16 = std::u16::MAX - (Ipv4Header::SERIALIZED_SIZE as u16) - 8; let value = { let mut value: Ipv4Header = Default::default(); value.set_options(&[1,2,3,4,5,6,7,8]).unwrap(); value.payload_len = MAX_PAYLOAD_LEN + 1; value }; test_range_methods( &value, U16TooLarge{value: MAX_PAYLOAD_LEN + 1, max: MAX_PAYLOAD_LEN, field: Ipv4PayloadLength} ); } } proptest! { #[test] fn read_errors(ref header in ipv4_any()) { use crate::ReadError::*; // non matching version for version in 0..0xf { if 4 != version { let buffer = { let mut buffer: Vec = Vec::with_capacity(header.header_len()); header.write(&mut buffer).unwrap(); //change the ihl buffer[0] = (version << 4) | (buffer[0] & 0xf); //version + ihl buffer }; // read assert_matches!( Ipv4Header::read(&mut Cursor::new(&buffer)), Err(Ipv4UnexpectedVersion(_)) ); // from_slice assert_matches!( Ipv4Header::from_slice(&buffer), Err(Ipv4UnexpectedVersion(_)) ); // from_slice assert_matches!( Ipv4HeaderSlice::from_slice(&buffer), Err(Ipv4UnexpectedVersion(_)) ); } } //bad ihl (smaller then 5) for ihl in 0..5 { let buffer = { let mut buffer: Vec = Vec::with_capacity(header.header_len()); header.write(&mut buffer).unwrap(); //change the ihl buffer[0] = (4 << 4) | ihl; //version + ihl buffer }; // read assert_matches!( Ipv4Header::read(&mut Cursor::new(&buffer)), Err(Ipv4HeaderLengthBad(_)) ); // read_without_version assert_matches!( Ipv4Header::read_without_version( &mut Cursor::new(&buffer[1..]), buffer[0] & 0xf ), Err(Ipv4HeaderLengthBad(_)) ); // from_slice assert_matches!( Ipv4Header::from_slice(&buffer), Err(Ipv4HeaderLengthBad(_)) ); // from_slice assert_matches!( Ipv4HeaderSlice::from_slice(&buffer), Err(Ipv4HeaderLengthBad(_)) ); } //bad total_length for total_length in 0..header.header_len() { let buffer = { let mut buffer: Vec = Vec::with_capacity(header.header_len()); header.write(&mut buffer).unwrap(); //change the total length to be smaller then the header length let total_len_be = (total_length as u16).to_be_bytes(); buffer[2] = total_len_be[0]; buffer[3] = total_len_be[1]; buffer }; // read assert_matches!( Ipv4Header::read(&mut Cursor::new(&buffer)), Err(Ipv4TotalLengthTooSmall(_)) ); // read_without_version assert_matches!( Ipv4Header::read_without_version( &mut Cursor::new(&buffer[1..]), buffer[0] & 0xf ), Err(Ipv4TotalLengthTooSmall(_)) ); // from_slice assert_matches!( Ipv4Header::from_slice(&buffer), Err(Ipv4TotalLengthTooSmall(_)) ); // from_slice assert_matches!( Ipv4HeaderSlice::from_slice(&buffer), Err(Ipv4TotalLengthTooSmall(_)) ); } //io error (bad slice length) { let buffer = { // create a header with some options (so a lenght check can fail there) let header = { let mut header: Ipv4Header = Default::default(); header.set_options(&[1,2,3,4]).unwrap(); header }; // serialize to buffer let mut buffer: Vec = Vec::with_capacity(header.header_len()); header.write(&mut buffer).unwrap(); buffer }; // check that all too small lenghts trigger an error for len in 0..buffer.len() { // read assert_matches!( Ipv4Header::read(&mut Cursor::new(&buffer[..len])), Err(IoError(_)) ); // read_without_version if len > 0 { assert_matches!( Ipv4Header::read_without_version( &mut Cursor::new(&buffer[1..len]), buffer[0] & 0xf ), Err(IoError(_)) ); } // from_slice assert_matches!( Ipv4Header::from_slice(&buffer[..len]), Err(UnexpectedEndOfSlice(_)) ); // from_slice assert_matches!( Ipv4HeaderSlice::from_slice(&buffer[..len]), Err(UnexpectedEndOfSlice(_)) ); } } } } proptest! { #[test] fn readwrite_header_raw(ref input in ipv4_any()) { //serialize let mut buffer: Vec = Vec::with_capacity(input.header_len()); input.write_raw(&mut buffer).unwrap(); assert_eq!(input.header_len(), buffer.len()); //deserialize (read) { let mut cursor = Cursor::new(&buffer); let result = Ipv4Header::read(&mut cursor).unwrap(); assert_eq!(input.header_len() as u64, cursor.position()); //check equivalence assert_eq!(input, &result); } //deserialize (from_slice) { let result = Ipv4Header::from_slice(&buffer).unwrap(); assert_eq!(input, &result.0); assert_eq!(&buffer[usize::from(input.header_len())..], result.1); } //check that the slice implementation also reads the correct values { use std::net::Ipv4Addr; let slice = Ipv4HeaderSlice::from_slice(&buffer[..]).unwrap(); assert_eq!(slice.slice(), &buffer); assert_eq!(slice.version(), 4); assert_eq!(slice.ihl(), input.ihl()); assert_eq!(slice.dcp(), input.differentiated_services_code_point); assert_eq!(slice.ecn(), input.explicit_congestion_notification); assert_eq!(slice.payload_len(), input.payload_len); assert_eq!(slice.total_len(), input.total_len()); assert_eq!(slice.identification(), input.identification); assert_eq!(slice.dont_fragment(), input.dont_fragment); assert_eq!(slice.more_fragments(), input.more_fragments); assert_eq!(slice.fragments_offset(), input.fragments_offset); assert_eq!(slice.ttl(), input.time_to_live); assert_eq!(slice.protocol(), input.protocol); assert_eq!(slice.header_checksum(), input.header_checksum); assert_eq!(slice.source(), input.source); assert_eq!(slice.source_addr(), Ipv4Addr::from(input.source)); assert_eq!(slice.destination(), input.destination); assert_eq!(slice.destination_addr(), Ipv4Addr::from(input.destination)); //check that a convertion back to a header yields the same result as the original write assert_eq!(&slice.to_header(), input); assert_eq!(slice.options(), input.options()); } } } proptest! { #[test] fn slice_eq(ref header in ipv4_any()) { let buffer_a = { let mut buffer: Vec = Vec::with_capacity(header.header_len()); header.write(&mut buffer).unwrap(); buffer }; let buffer_b = { let mut buffer: Vec = Vec::with_capacity(header.header_len()); header.write(&mut buffer).unwrap(); buffer }; assert_eq!( Ipv4HeaderSlice::from_slice(&buffer_a).unwrap(), Ipv4HeaderSlice::from_slice(&buffer_b).unwrap(), ); } } #[test] fn slice_dbg() { let buffer = { let header: Ipv4Header = Default::default(); let mut buffer: Vec = Vec::with_capacity(header.header_len()); header.write(&mut buffer).unwrap(); buffer }; println!("{:?}", Ipv4HeaderSlice::from_slice(&buffer).unwrap()); } proptest! { #[test] fn clone(ref header in ipv4_any()) { // header assert_eq!(header, &header.clone()); // slice let buffer = { let mut buffer: Vec = Vec::with_capacity(header.header_len()); header.write(&mut buffer).unwrap(); buffer }; let slice = Ipv4HeaderSlice::from_slice(&buffer).unwrap(); assert_eq!(slice.clone(), slice.clone()); } } etherparse-0.13.0/tests/internet/ipv4_extensions.rs000064400000000000000000000354751046102023000205660ustar 00000000000000use super::super::*; use crate::ReadError::UnexpectedEndOfSlice; use crate::ip_number::*; use std::io::Cursor; pub mod header { use super::*; #[test] fn from_slice() { let auth_header = IpAuthenticationHeader::new( UDP, 0, 0, &[] ).unwrap(); let buffer = { let mut buffer = Vec::with_capacity(auth_header.header_len()); auth_header.write(&mut buffer).unwrap(); buffer.push(1); buffer.push(2); buffer }; // no auth header { let (header, next, rest) = Ipv4Extensions::from_slice( TCP, &buffer ).unwrap(); assert!(header.auth.is_none()); assert_eq!(TCP, next); assert_eq!(rest, &buffer); } // with auth header { let (actual, next, rest) = Ipv4Extensions::from_slice( AUTH, &buffer ).unwrap(); assert_eq!(actual.auth.unwrap(), auth_header); assert_eq!(UDP, next); assert_eq!(rest, &buffer[auth_header.header_len()..]); } // too small { let err = Ipv4Extensions::from_slice( AUTH, &buffer[..auth_header.header_len() - 1] ).unwrap_err(); const AUTH_HEADER_LEN: usize = 12; assert_matches!( err, UnexpectedEndOfSlice(AUTH_HEADER_LEN) ); } } proptest! { #[test] fn read(auth in ip_authentication_any()) { // None { let mut cursor = Cursor::new(&[]); let (actual, next) = Ipv4Extensions::read(&mut cursor, UDP).unwrap(); assert_eq!(next, UDP); assert_eq!( actual, Ipv4Extensions{ auth: None, } ); } // Some sucessfull { let buffer = { let mut buffer = Vec::with_capacity(auth.header_len()); auth.write(&mut buffer).unwrap(); buffer.push(1); buffer }; let mut cursor = Cursor::new(&buffer); let (actual, next) = Ipv4Extensions::read(&mut cursor, AUTH).unwrap(); assert_eq!(auth.header_len(), cursor.position() as usize); assert_eq!(next, auth.next_header); assert_eq!( actual, Ipv4Extensions{ auth: Some(auth.clone()), } ); } // Some error { let mut cursor = Cursor::new(&[]); let err = Ipv4Extensions::read(&mut cursor, AUTH).unwrap_err(); assert_matches!( err, ReadError::IoError(_) ); } } } #[test] fn write() { // None { let mut buffer = Vec::new(); Ipv4Extensions{ auth: None, }.write(&mut buffer, UDP).unwrap(); assert_eq!(0, buffer.len()); } // Some let auth_header = IpAuthenticationHeader::new( UDP, 0, 0, &[] ).unwrap(); { let mut buffer = Vec::with_capacity(auth_header.header_len()); Ipv4Extensions{ auth: Some(auth_header.clone()), }.write(&mut buffer, AUTH).unwrap(); let (read_header, _) = IpAuthenticationHeader::from_slice(&buffer).unwrap(); assert_eq!(auth_header, read_header); } // Some bad start number { let mut buffer = Vec::new(); let err = Ipv4Extensions{ auth: Some(auth_header.clone()), }.write(&mut buffer, UDP).unwrap_err(); assert_matches!( err, WriteError::ValueError( ValueError::Ipv4ExtensionNotReferenced( IpNumber::AuthenticationHeader ) ) ); } // Some: Write error { let mut writer = TestWriter::with_max_size( auth_header.header_len() - 1 ); let err = Ipv4Extensions{ auth: Some(auth_header.clone()), }.write(&mut writer, AUTH).unwrap_err(); assert_eq!( std::io::ErrorKind::UnexpectedEof, err.io_error().unwrap().kind() ); } } #[test] fn header_len() { // None assert_eq!( 0, Ipv4Extensions{ auth: None, }.header_len() ); // Some { let auth = IpAuthenticationHeader::new( UDP, 0, 0, &[] ).unwrap(); assert_eq!( auth.header_len(), Ipv4Extensions{ auth: Some(auth), }.header_len() ); } // Some with paylaod { let auth = IpAuthenticationHeader::new( UDP, 0, 0, &[ 1, 2, 3, 4 ] ).unwrap(); assert_eq!( auth.header_len(), Ipv4Extensions{ auth: Some(auth), }.header_len() ); } } #[test] fn set_next_headers() { // None { let mut exts = Ipv4Extensions{ auth: None, }; assert_eq!(UDP, exts.set_next_headers(UDP)); } // Some { let mut exts = Ipv4Extensions{ auth: Some( IpAuthenticationHeader::new( TCP, 0, 0, &[] ).unwrap() ), }; assert_eq!(TCP, exts.auth.as_ref().unwrap().next_header); // change from TCP to UDP let re = exts.set_next_headers(UDP); assert_eq!(AUTH, re); assert_eq!(UDP, exts.auth.as_ref().unwrap().next_header); } } #[test] fn next_header() { // None { let exts = Ipv4Extensions{ auth: None, }; assert_eq!(UDP, exts.next_header(UDP).unwrap()); } // Some { let exts = Ipv4Extensions{ auth: Some( IpAuthenticationHeader::new( TCP, 0, 0, &[] ).unwrap() ), }; // auth referenced assert_eq!(TCP, exts.next_header(AUTH).unwrap()); // auth not referenced (error) assert_eq!( ValueError::Ipv4ExtensionNotReferenced( IpNumber::AuthenticationHeader ), exts.next_header(TCP).unwrap_err() ); } } #[test] fn is_empty() { // empty assert!( Ipv4Extensions{ auth: None, }.is_empty() ); // auth assert_eq!( false, Ipv4Extensions{ auth: Some(IpAuthenticationHeader::new(ip_number::UDP, 0, 0, &[]).unwrap()), }.is_empty() ); } proptest! { #[test] fn debug(auth in ip_authentication_any()) { // None assert_eq!( &format!("Ipv4Extensions {{ auth: {:?} }}", Option::::None), &format!( "{:?}", Ipv4Extensions { auth: None, } ) ); // Some assert_eq!( &format!("Ipv4Extensions {{ auth: {:?} }}", Some(auth.clone())), &format!( "{:?}", Ipv4Extensions { auth: Some(auth.clone()), } ) ); } } proptest! { #[test] fn clone_eq(auth in ip_authentication_any()) { // None { let header = Ipv4Extensions{ auth: None, }; assert_eq!( header.clone(), Ipv4Extensions{ auth: None, } ); } // Some { let header = Ipv4Extensions{ auth: Some(auth.clone()), }; assert_eq!( header.clone(), Ipv4Extensions{ auth: Some(auth.clone()), } ); } } } } // mod header mod slice { use super::*; proptest! { #[test] fn from_slice(auth in ip_authentication_any()) { // None { let buffer = [1,2,3,4]; let (slice, next, rest) = Ipv4ExtensionsSlice::from_slice(UDP, &buffer).unwrap(); assert_eq!( slice, Ipv4ExtensionsSlice{ auth: None, } ); assert_eq!(next, UDP); assert_eq!(rest, &buffer); } // Some { let buffer = { let mut buffer = Vec::with_capacity(auth.header_len()); auth.write(&mut buffer).unwrap(); // add some data to check the returned rest slice is correct // and not just nothing buffer.push(1); buffer }; let (slice, next, rest) = Ipv4ExtensionsSlice::from_slice(AUTH, &buffer).unwrap(); assert_eq!( slice, Ipv4ExtensionsSlice{ auth: Some( IpAuthenticationHeaderSlice::from_slice(&buffer).unwrap() ), } ); assert_eq!(next, auth.next_header); assert_eq!(rest, &buffer[auth.header_len()..]); } // Error unexpected end of slice { let err = Ipv4ExtensionsSlice::from_slice(AUTH, &[]).unwrap_err(); const AUTH_HEADER_LEN: usize = 12; assert_matches!( err, UnexpectedEndOfSlice(AUTH_HEADER_LEN) ); } } } proptest! { #[test] fn to_header(auth in ip_authentication_any()) { // None assert_eq!( Ipv4ExtensionsSlice{ auth: None, }.to_header(), Ipv4Extensions{ auth: None, } ); // Some { let buffer = { let mut buffer = Vec::with_capacity(auth.header_len()); auth.write(&mut buffer).unwrap(); buffer }; let slice = Ipv4ExtensionsSlice{ auth: Some( IpAuthenticationHeaderSlice::from_slice(&buffer).unwrap() ), }; assert_eq!( slice.to_header(), Ipv4Extensions{ auth: Some(auth.clone()), } ); } } } #[test] fn is_empty() { // empty assert!( Ipv4ExtensionsSlice{ auth: None, }.is_empty() ); // auth { let buffer = { let auth = IpAuthenticationHeader::new(ip_number::UDP, 0, 0, &[]).unwrap(); let mut buffer = Vec::with_capacity(auth.header_len()); auth.write(&mut buffer).unwrap(); buffer }; assert_eq!( false, Ipv4ExtensionsSlice{ auth: Some(IpAuthenticationHeaderSlice::from_slice(&buffer).unwrap()), }.is_empty() ); } } proptest! { #[test] fn debug(auth in ip_authentication_any()) { // None assert_eq!( &format!("Ipv4ExtensionsSlice {{ auth: {:?} }}", Option::::None), &format!( "{:?}", Ipv4ExtensionsSlice { auth: None, } ) ); // Some let buffer = { let mut buffer = Vec::with_capacity(auth.header_len()); auth.write(&mut buffer).unwrap(); buffer }; let auth_slice = IpAuthenticationHeaderSlice::from_slice(&buffer).unwrap(); assert_eq!( &format!("Ipv4ExtensionsSlice {{ auth: {:?} }}", Some(auth_slice.clone())), &format!( "{:?}", Ipv4ExtensionsSlice { auth: Some(auth_slice.clone()), } ) ); } } proptest! { #[test] fn clone_eq(auth in ip_authentication_any()) { // None { let header = Ipv4ExtensionsSlice{ auth: None, }; assert_eq!( header.clone(), Ipv4ExtensionsSlice{ auth: None, } ); } // Some { let buffer = { let mut buffer = Vec::with_capacity(auth.header_len()); auth.write(&mut buffer).unwrap(); buffer }; let auth_slice = IpAuthenticationHeaderSlice::from_slice(&buffer).unwrap(); let slice = Ipv4ExtensionsSlice { auth: Some(auth_slice.clone()), }; assert_eq!( slice.clone(), Ipv4ExtensionsSlice{ auth: Some(auth_slice.clone()), } ); } } } }etherparse-0.13.0/tests/internet/ipv6.rs000064400000000000000000000374571046102023000163130ustar 00000000000000use super::super::*; #[test] fn read() { use std::io::Cursor; const INPUT: Ipv6Header = Ipv6Header { traffic_class: 1, flow_label: 0x81806, payload_length: 0x8021, next_header: 30, hop_limit: 40, source: [1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16], destination: [21,22,23,24,25,26,27,28, 29,30,31,32,33,34,35,36] }; //serialize let mut buffer: Vec = Vec::with_capacity(20); INPUT.write(&mut buffer).unwrap(); assert_eq!(40, buffer.len()); //deserialize let mut cursor = Cursor::new(&buffer); let result = IpHeader::read(&mut cursor).unwrap(); assert_eq!(40, cursor.position()); assert_eq!(result.0, IpHeader::Version6(INPUT, Default::default())); assert_eq!(result.1, INPUT.next_header); } #[test] fn read_write() { use std::io::Cursor; let input = Ipv6Header { traffic_class: 1, flow_label: 0x81806, payload_length: 0x8021, next_header: 30, hop_limit: 40, source: [1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16], destination: [21,22,23,24,25,26,27,28, 29,30,31,32,33,34,35,36] }; //serialize let mut buffer: Vec = Vec::with_capacity(20); input.write(&mut buffer).unwrap(); //deserialize (with read) { let result = Ipv6Header::read(&mut Cursor::new(&buffer)).unwrap(); //check equivalence assert_eq!(input, result); } //deserialize (with from_slice) { let result = Ipv6Header::from_slice(&buffer).unwrap(); assert_eq!(input, result.0); assert_eq!(&buffer[buffer.len()..], result.1); } //deserialize (with read_from_slice) #[allow(deprecated)] { let result = Ipv6Header::read_from_slice(&buffer).unwrap(); assert_eq!(input, result.0); assert_eq!(&buffer[buffer.len()..], result.1); } } #[test] fn write_errors() { use crate::WriteError::ValueError; use crate::ValueError::*; use crate::ErrorField::*; fn base() -> Ipv6Header { Ipv6Header { traffic_class: 1, flow_label: 0x0, payload_length: 0x8021, next_header: 30, hop_limit: 40, source: [1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16], destination: [21,22,23,24,25,26,27,28, 29,30,31,32,33,34,35,36] } } fn test_write(input: &Ipv6Header) -> Result<(), WriteError> { let mut buffer: Vec = Vec::with_capacity(20); input.write(&mut buffer) } //flow label assert_matches!( test_write(&{ let mut value = base(); value.flow_label = 0x100000; value }), Err(ValueError(U32TooLarge{value: 0x100000, max: 0xFFFFF, field: Ipv6FlowLabel}))); //io error (not enough space) { let header = base(); for len in 0..Ipv6Header::SERIALIZED_SIZE { let mut writer = TestWriter::with_max_size(len); assert_eq!( writer.error_kind(), header.write(&mut writer).unwrap_err().io_error().unwrap().kind() ); } } } #[test] fn read_error() { //wrong ip version { let buffer: [u8;20] = [0;20]; let result = Ipv6Header::read(&mut io::Cursor::new(&buffer)); assert_matches!(result, Err(ReadError::Ipv6UnexpectedVersion(0))) } //io error and unexpected end of slice { let buffer = { let mut buffer: [u8;Ipv6Header::SERIALIZED_SIZE] = [0;Ipv6Header::SERIALIZED_SIZE]; buffer[0] = 0x60; //ip number is needed buffer }; for len in 0..Ipv6Header::SERIALIZED_SIZE { // read assert_matches!( Ipv6Header::read(&mut io::Cursor::new(&buffer[0..len])), Err(ReadError::IoError(_)) ); // read from slice assert_matches!( Ipv6Header::from_slice(&buffer[0..len]), Err(ReadError::UnexpectedEndOfSlice(Ipv6Header::SERIALIZED_SIZE)) ); } } } #[test] fn header_len() { let header : Ipv6Header = Default::default(); assert_eq!(Ipv6Header::SERIALIZED_SIZE, header.header_len()); } #[test] fn is_skippable_header_extension() { use crate::ip_number::*; for i in 0..0xffu8 { let expected = match i { IPV6_HOP_BY_HOP | IPV6_ROUTE | IPV6_FRAG | AUTH | IPV6_DEST_OPTIONS | MOBILITY | HIP | SHIM6 => true, _ => false }; assert_eq!(expected, Ipv6Header::is_skippable_header_extension(i)); } } #[test] fn skip_extension() { use crate::ip_number::*; use std::io::Cursor; { let buffer: [u8; 8] = [0;8]; let mut cursor = Cursor::new(&buffer); assert_matches!(Ipv6Header::skip_header_extension(&mut cursor, ICMP), Ok(ICMP)); assert_eq!(0, cursor.position()); } { let buffer: [u8; 8] = [0;8]; let mut cursor = Cursor::new(&buffer); assert_matches!(Ipv6Header::skip_header_extension(&mut cursor, IPV6_HOP_BY_HOP), Ok(0)); assert_eq!(8, cursor.position()); } { let buffer: [u8; 8*3] = [ 4,2,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, ]; let mut cursor = Cursor::new(&buffer); assert_matches!(Ipv6Header::skip_header_extension(&mut cursor, IPV6_ROUTE), Ok(4)); assert_eq!(8*3, cursor.position()); } { //fragmentation header has a fixed size -> the 2 should be ignored let buffer: [u8; 8*3] = [ 4,2,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, ]; let mut cursor = Cursor::new(&buffer); assert_matches!(Ipv6Header::skip_header_extension(&mut cursor, IPV6_FRAG), Ok(4)); assert_eq!(8, cursor.position()); } } #[test] fn skip_all_extensions() { use crate::io::Cursor; //extension header values use crate::ip_number::*; //based on RFC 8200 4.1. Extension Header Order // & IANA https://www.iana.org/assignments/ipv6-parameters/ipv6-parameters.xhtml const EXTENSION_IDS: [u8;9] = [ IPV6_HOP_BY_HOP, IPV6_DEST_OPTIONS, IPV6_ROUTE, IPV6_FRAG, AUTH, IPV6_DEST_OPTIONS, MOBILITY, HIP, SHIM6, ]; // note the following ids are extensions but are not skippable: // // - EncapsulatingSecurityPayload // - ExperimentalAndTesting0 // - ExperimentalAndTesting0 //no & single skipping { let buffer: [u8; 8*4] = [ UDP,2,0,0, 0,0,0,0, //set next to udp 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 1,2,3,4, 5,6,7,8, ]; for i in 0..=u8::max_value() { let mut cursor = Cursor::new(&buffer); let reader_result = Ipv6Header::skip_all_header_extensions(&mut cursor, i); let slice_result = Ipv6Header::skip_all_header_extensions_in_slice(&buffer, i).unwrap(); match EXTENSION_IDS.iter().find(|&&x| x == i) { Some(_) => { //ipv6 header extension -> expect skip assert_matches!(reader_result, Ok(UDP)); assert_matches!(slice_result.0, UDP); let len = if i == IPV6_FRAG { //fragmentation header has a fixed size 8 } else if i == AUTH { //authentification headers use 4-octets to describe the length 8 + 2*4 } else { buffer.len() - 8 }; assert_eq!(len, cursor.position() as usize); assert_eq!(&buffer[len..], slice_result.1); }, None => { //non ipv6 header expect no read movement and direct return assert_matches!(reader_result, Ok(next) => assert_eq!(i, next)); assert_eq!(0, cursor.position()); assert_eq!(i, slice_result.0); assert_eq!(&buffer, slice_result.1); } } } } //creates an buffer filled with extension headers with the given ids fn create_buffer(ids: &[u8]) -> Vec { use crate::ip_number::*; let mut prev: u8 = ids[0]; let mut result = Vec::with_capacity(ids.len()*8*4); for (index, value) in ids[1..].iter().enumerate() { let len: u8 = if prev == IPV6_FRAG { 0 } else { (index % 3) as u8 }; //write first line result.extend_from_slice(&[*value, len, 0, 0, 0, 0, 0, 0]); //fill rest with dummy data for _ in 0..len { result.extend_from_slice( if prev == AUTH { // authentification headers interpret the length as in 4-octets &[0;4] } else { // all other headers (excluding the fragmentation header) interpret the length as in 8-octets &[0;8] } ); } //cache prev prev = *value; } //add some dummy data to the end (useful for checking that the returned slice are correct) result.extend_from_slice(&[0, 0, 0, 0, 0, 0, 0, 0]); result } //skip maximum number { let ids = { let mut ids = Vec::with_capacity(IPV6_MAX_NUM_HEADER_EXTENSIONS); while ids.len() < IPV6_MAX_NUM_HEADER_EXTENSIONS { // fill with extension headers until filled ids.extend_from_slice(&EXTENSION_IDS[..std::cmp::min(EXTENSION_IDS.len(), IPV6_MAX_NUM_HEADER_EXTENSIONS - ids.len())]); } ids.push(UDP); ids }; let buffer = create_buffer(&ids); //reader { let mut cursor = Cursor::new(&buffer); let result = Ipv6Header::skip_all_header_extensions(&mut cursor, ids[0]); assert_matches!(result, Ok(UDP)); assert_eq!(buffer.len() - 8, cursor.position() as usize); } //slice { let result = Ipv6Header::skip_all_header_extensions_in_slice(&buffer, ids[0]).unwrap(); assert_eq!(result.0, UDP); assert_eq!(result.1, &buffer[buffer.len() - 8 .. ]); } } //trigger "too many" error { let ids = { let mut ids = Vec::with_capacity(EXTENSION_IDS.len() + 5); ids.extend_from_slice(&EXTENSION_IDS); ids.push(EXTENSION_IDS[0]); ids.push(EXTENSION_IDS[0]); ids.push(EXTENSION_IDS[0]); ids.push(EXTENSION_IDS[0]); ids.push(UDP); ids }; let buffer = create_buffer(&ids); //reader { let mut cursor = Cursor::new(&buffer); let result = Ipv6Header::skip_all_header_extensions(&mut cursor, ids[0]); assert_matches!(result, Err(ReadError::Ipv6TooManyHeaderExtensions)); } //slice { let result = Ipv6Header::skip_all_header_extensions_in_slice(&buffer, ids[0]); assert_matches!(result, Err(ReadError::Ipv6TooManyHeaderExtensions)); } } //trigger missing unexpected eof { let ids = { let mut ids = Vec::with_capacity(EXTENSION_IDS.len() + 1); ids.extend_from_slice(&EXTENSION_IDS); ids.push(UDP); ids }; let buffer = create_buffer(&ids); // check for all offsets for len in 0..buffer.len() - 8 { // minus 8 for the dummy data //reader { let mut cursor = TestReader::new(&buffer[..len]); let result = Ipv6Header::skip_all_header_extensions(&mut cursor, ids[0]); assert_matches!(result, Err(ReadError::IoError(_))); } //slice { let result = Ipv6Header::skip_all_header_extensions_in_slice(&buffer[..len], ids[0]); assert_matches!(result, Err(ReadError::UnexpectedEndOfSlice(_))); } } } } #[test] fn set_payload_lengt() { let mut header = Ipv6Header { traffic_class: 0, flow_label: 0, payload_length: 0, next_header: 0, hop_limit: 0, source: [0;16], destination: [0;16] }; assert_matches!(header.set_payload_length(0), Ok(())); assert_eq!(header.payload_length, 0); const MAX: usize = std::u16::MAX as usize; assert_matches!(header.set_payload_length(MAX), Ok(())); assert_eq!(header.payload_length, MAX as u16); const OVER_MAX: usize = MAX + 1; assert_matches!(header.set_payload_length(OVER_MAX), Err(ValueError::Ipv6PayloadLengthTooLarge(OVER_MAX))); } proptest! { #[test] fn from_slice(ref input in ipv6_any()) { //serialize let mut buffer: Vec = Vec::with_capacity(20); input.write(&mut buffer).unwrap(); //check that a too small slice triggers an error assert_matches!(Ipv6HeaderSlice::from_slice(&buffer[..buffer.len()-1]), Err(ReadError::UnexpectedEndOfSlice(Ipv6Header::SERIALIZED_SIZE))); //check that all the values are read correctly use std::net::Ipv6Addr; let slice = Ipv6HeaderSlice::from_slice(&buffer).unwrap(); assert_eq!(slice.slice(), &buffer[..]); assert_eq!(slice.version(), 6); assert_eq!(slice.traffic_class(), input.traffic_class); assert_eq!(slice.flow_label(), input.flow_label); assert_eq!(slice.payload_length(), input.payload_length); assert_eq!(slice.next_header(), input.next_header); assert_eq!(slice.hop_limit(), input.hop_limit); assert_eq!(slice.source(), input.source); assert_eq!(slice.source_addr(), Ipv6Addr::from(input.source)); assert_eq!(slice.destination(), input.destination); assert_eq!(slice.destination_addr(), Ipv6Addr::from(input.destination)); //test for derive assert_eq!(slice.clone(), slice); //check that the convertion back to a header struct results in the same struct assert_eq!(&slice.to_header(), input); } } #[test] fn from_slice_bad_version() { //write an ipv4 header and check that the bad version number is detected let input = { let mut input: Ipv4Header = Default::default(); //set the options to increase the size, //otherwise an unexpected end of slice error is returned input.set_options( &[0;24] ).unwrap(); input }; //serialize let mut buffer: Vec = Vec::with_capacity(44); input.write_raw(&mut buffer).unwrap(); //check that the unexpected version id is detected use crate::ReadError::*; assert_matches!(Ipv6HeaderSlice::from_slice(&buffer[..]), Err(Ipv6UnexpectedVersion(4))); } #[test] fn dbg() { let header: Ipv6Header = Default::default(); println!("{:?}", header); let mut buffer: Vec = Vec::with_capacity(Ipv6Header::SERIALIZED_SIZE); header.write(&mut buffer).unwrap(); let slice = Ipv6HeaderSlice::from_slice(&buffer[..]).unwrap(); println!("{:?}", slice); } #[test] fn eq() { let header: Ipv6Header = Default::default(); assert!(header.eq(&header.clone())); assert!(false == header.ne(&header.clone())); let mut buffer: Vec = Vec::with_capacity(Ipv6Header::SERIALIZED_SIZE); header.write(&mut buffer).unwrap(); let slice = Ipv6HeaderSlice::from_slice(&buffer[..]).unwrap(); assert!(slice.eq(&slice.clone())); assert!(false == slice.ne(&slice.clone())); } etherparse-0.13.0/tests/internet/ipv6_extensions.rs000064400000000000000000001542151046102023000205620ustar 00000000000000use super::super::*; use crate::ip_number::*; use std::io::Cursor; // IP numbers that are assigned ipv6 header extensions. const EXTESION_KNOWN_IP_NUMBERS : [u8;5] = [ AUTH, IPV6_DEST_OPTIONS, IPV6_HOP_BY_HOP, IPV6_FRAG, IPV6_ROUTE, ]; /// Helper struct that generates test data with dummy /// extension header data. struct ExtensionTestPayload { ip_numbers: Vec, data: Vec, } impl ExtensionTestPayload { pub fn new(ip_numbers: &[u8], header_sizes: &[u8]) -> ExtensionTestPayload { assert!(ip_numbers.len() > 1); assert!(header_sizes.len() > 0); let mut result = ExtensionTestPayload{ ip_numbers: ip_numbers.to_vec(), data: Vec::with_capacity((ip_numbers.len() - 1)*(0xff*8 + 8)), }; for i in 0..ip_numbers.len() - 1 { result.add_payload( ip_numbers[i], ip_numbers[i + 1], header_sizes[i % header_sizes.len()] ) } result } pub fn slice(&self) -> &[u8] { &self.data } fn add_payload(&mut self, ip_number: u8, next_header: u8, header_ext_len: u8) { match ip_number { IPV6_HOP_BY_HOP | IPV6_ROUTE | IPV6_DEST_OPTIONS => { // insert next header & size let mut raw : [u8;0xff*8 + 8] = [0;0xff*8 + 8]; raw[0] = next_header; raw[1] = header_ext_len; // insert payload self.data.extend_from_slice(&raw[..8 + usize::from(header_ext_len)*8]); }, IPV6_FRAG => { // generate payload let mut raw : [u8;8] = [0;8]; raw[0] = next_header; raw[1] = 0; // insert payload self.data.extend_from_slice(&raw[..8]); }, AUTH => { let mut raw : [u8;0xff*4 + 8] = [0;0xff*4 + 8]; raw[0] = next_header; // authentfication header len is defined as // '32-bit words (4-byteunits), minus "2"' let len = if header_ext_len > 0 { raw[1] = header_ext_len; usize::from(header_ext_len)*4 } else { // auth has a minimum size of 1 raw[1] = 1; 4 } + 8; self.data.extend_from_slice(&raw[..len]); }, _ => unreachable!(), } } /// Returns true of the payload will trigger a "hop by hop not /// at start" error which is not ignored because of an early /// parsing abort. pub fn exts_hop_by_hop_error(&self) -> bool { struct ReadState { dest_opt: bool, routing: bool, final_dest_opt: bool, frag: bool, auth: bool, } // state if a header type has already been read let mut read = ReadState { dest_opt: false, routing: false, final_dest_opt: false, frag: false, auth: false, }; for i in 0..self.ip_numbers.len() { match self.ip_numbers[i] { IPV6_HOP_BY_HOP => if i != 0 { return true; }, IPV6_ROUTE => if read.routing { return false; } else { read.routing = true; }, IPV6_DEST_OPTIONS => { // check the kind of destination options (aka is it before or after the routing header) if read.routing { // final dest opt if read.final_dest_opt { return false; } else { read.final_dest_opt = true; } } else { // dst opt if read.dest_opt { return false; } else { read.dest_opt = true; } } } IPV6_FRAG => if read.frag { return false; } else { read.frag = true; }, AUTH => if read.auth { return false; } else { read.auth = true; }, _ => return false, } } return false; } /// Checks the if the extensions match the expected values based /// on this test payload. pub fn assert_extensions(&self, exts: &Ipv6Extensions) -> (usize, u8) { struct ReadState { hop_by_hop: bool, dest_opt: bool, routing: bool, final_dest_opt: bool, frag: bool, auth: bool, } // state if a header type has already been read let mut read = ReadState { hop_by_hop: false, dest_opt: false, routing: false, final_dest_opt: false, frag: false, auth: false, }; let mut slice = &self.data[..]; let mut post_header = self.ip_numbers[0]; for i in 0..self.ip_numbers.len() - 1 { let mut stop = false; match self.ip_numbers[i] { IPV6_HOP_BY_HOP => { assert!(false == read.hop_by_hop); let (header, rest) = Ipv6RawExtensionHeader::from_slice(slice).unwrap(); assert_eq!(&header, exts.hop_by_hop_options.as_ref().unwrap()); slice = rest; read.hop_by_hop = true; }, IPV6_ROUTE => { if read.routing { stop = true; } else { let (header, rest) = Ipv6RawExtensionHeader::from_slice(slice).unwrap(); assert_eq!(&header, &exts.routing.as_ref().unwrap().routing); slice = rest; read.routing = true; } }, IPV6_DEST_OPTIONS => { // check the kind of destination options (aka is it before or after the routing header) if read.routing { // final dest opt if read.final_dest_opt { stop = true; } else { let (header, rest) = Ipv6RawExtensionHeader::from_slice(slice).unwrap(); assert_eq!(&header, exts.routing.as_ref().unwrap().final_destination_options.as_ref().unwrap()); slice = rest; read.final_dest_opt = true; } } else { // dst opt if read.dest_opt { stop = true; } else { let (header, rest) = Ipv6RawExtensionHeader::from_slice(slice).unwrap(); assert_eq!(&header, exts.destination_options.as_ref().unwrap()); slice = rest; read.dest_opt = true; } } } IPV6_FRAG => { if read.frag { // duplicate header -> stop stop = true; } else { let (header, rest) = Ipv6FragmentHeader::from_slice(slice).unwrap(); assert_eq!(&header, exts.fragment.as_ref().unwrap()); slice = rest; read.frag = true; } }, AUTH => { if read.auth { // duplicate header -> stop stop = true; } else { let (header, rest) = IpAuthenticationHeader::from_slice(slice).unwrap(); assert_eq!(&header, exts.auth.as_ref().unwrap()); slice = rest; read.auth = true; } }, _ => { // non extension header -> stop stop = true; } } if stop { post_header = self.ip_numbers[i]; break; } else { post_header = self.ip_numbers[i + 1]; } } // check the non parsed headers are not present if false == read.hop_by_hop { assert!(exts.hop_by_hop_options.is_none()); } if false == read.dest_opt { assert!(exts.destination_options.is_none()); } if false == read.routing { assert!(exts.routing.is_none()); } else { if false == read.final_dest_opt { assert!(exts.routing.as_ref().unwrap().final_destination_options.is_none()); } } if false == read.frag { assert!(exts.fragment.is_none()); } if false == read.auth { assert!(exts.auth.is_none()); } (self.data.len() - slice.len(), post_header) } } /// extension header data. #[derive(Clone)] struct ExtensionTestHeaders { ip_numbers: Vec, data: Ipv6Extensions, } impl ExtensionTestHeaders { pub fn new(ip_numbers: &[u8], header_sizes: &[u8]) -> ExtensionTestHeaders { assert!(ip_numbers.len() > 1); assert!(header_sizes.len() > 0); let mut result = ExtensionTestHeaders{ ip_numbers: ip_numbers.to_vec(), data: Default::default(), }; for i in 0..ip_numbers.len() - 1 { let succ = result.add_payload( ip_numbers[i], ip_numbers[i + 1], header_sizes[i % header_sizes.len()] ); if false == succ { // write was not possible (duplicate) // reduce the list so the current ip number // is the final one result.ip_numbers.truncate(i + 1); break; } } result } pub fn introduce_missing_ref(&mut self, new_header: u8) -> IpNumber { assert!(self.ip_numbers.len() >= 2); // set the next_header of the last extension header and return the id use IpNumber::*; if self.ip_numbers.len() >= 3 { match self.ip_numbers[self.ip_numbers.len()-3] { IPV6_HOP_BY_HOP => { self.data.hop_by_hop_options.as_mut().unwrap().next_header = new_header; }, IPV6_DEST_OPTIONS => { if self.ip_numbers[..self.ip_numbers.len()-3].iter().any(|&x| x == IPV6_ROUTE) { self.data.routing.as_mut().unwrap() .final_destination_options.as_mut().unwrap() .next_header = new_header; } else { self.data.destination_options.as_mut().unwrap().next_header = new_header; } }, IPV6_ROUTE => { self.data.routing.as_mut().unwrap().routing.next_header = new_header; }, IPV6_FRAG => { self.data.fragment.as_mut().unwrap().next_header = new_header; }, AUTH => { self.data.auth.as_mut().unwrap().next_header = new_header; }, _ => unreachable!(), } match self.ip_numbers[self.ip_numbers.len() - 2] { IPV6_HOP_BY_HOP => IPv6HeaderHopByHop, IPV6_DEST_OPTIONS => IPv6DestinationOptions, IPV6_ROUTE => IPv6RouteHeader, IPV6_FRAG => IPv6FragmentationHeader, AUTH => AuthenticationHeader, _ => unreachable!(), } } else { // rewrite start number in case it is just one extension header let missing = self.ip_numbers[0]; self.ip_numbers[0] = new_header; match missing { IPV6_HOP_BY_HOP => IPv6HeaderHopByHop, IPV6_DEST_OPTIONS => IPv6DestinationOptions, IPV6_ROUTE => IPv6RouteHeader, IPV6_FRAG => IPv6FragmentationHeader, AUTH => AuthenticationHeader, _ => unreachable!(), } } } fn add_payload(&mut self, ip_number: u8, next_header: u8, header_ext_len: u8) -> bool { match ip_number { IPV6_HOP_BY_HOP | IPV6_ROUTE | IPV6_DEST_OPTIONS => { use Ipv6RawExtensionHeader as R; let payload : [u8;R::MAX_PAYLOAD_LEN] = [0;R::MAX_PAYLOAD_LEN]; let len = usize::from(header_ext_len)*8 + 6; let raw = Ipv6RawExtensionHeader::new_raw( next_header, &payload[..len], ).unwrap(); match ip_number { IPV6_HOP_BY_HOP => if self.data.hop_by_hop_options.is_none() { self.data.hop_by_hop_options = Some(raw); true } else { false }, IPV6_ROUTE => if self.data.routing.is_none() { self.data.routing = Some( Ipv6RoutingExtensions{ routing: raw, final_destination_options: None, } ); true } else { false }, IPV6_DEST_OPTIONS => if let Some(ref mut route) = self.data.routing { if route.final_destination_options.is_none() { route.final_destination_options = Some(raw); true } else { false } } else { // dest option if self.data.destination_options.is_none() { self.data.destination_options = Some(raw); true } else { false } }, _ => unreachable!(), } }, IPV6_FRAG => if self.data.fragment.is_none() { self.data.fragment = Some( Ipv6FragmentHeader::new( next_header, 0, true, 123 ) ); true } else { false }, AUTH => if self.data.auth.is_none() { use IpAuthenticationHeader as A; let mut len = usize::from(header_ext_len)*4; if len > A::MAX_ICV_LEN { len = A::MAX_ICV_LEN; } let raw_icv : [u8;A::MAX_ICV_LEN] = [0;A::MAX_ICV_LEN]; self.data.auth = Some( IpAuthenticationHeader::new( next_header, 123, 234, &raw_icv[..len] ).unwrap() ); true } else { false }, _ => unreachable!(), } } } pub mod header { use super::*; proptest!{ #[test] fn from_slice( header_size in any::(), post_header in any::() .prop_filter("Must be a non ipv6 header relevant ip number".to_owned(), |v| !EXTESION_KNOWN_IP_NUMBERS.iter().any(|&x| v == &x) ) ) { // no extension headers filled { let some_data = [1,2,3,4]; let actual = Ipv6Extensions::from_slice(post_header, &some_data).unwrap(); assert_eq!(actual.0, Default::default()); assert_eq!(actual.1, post_header); assert_eq!(actual.2, &some_data); } /// Run a test with the given ip numbers fn run_test(ip_numbers: &[u8], header_sizes: &[u8]) { // setup test payload let e = ExtensionTestPayload::new( ip_numbers, header_sizes ); if e.exts_hop_by_hop_error() { // a hop by hop header that is not at the start triggers an error assert_matches!( Ipv6Extensions::from_slice(ip_numbers[0], e.slice()).unwrap_err(), ReadError::Ipv6HopByHopHeaderNotAtStart ); } else { // normal read let (header, next, rest) = Ipv6Extensions::from_slice(ip_numbers[0], e.slice()).unwrap(); let (read_len, expected_post_header) = e.assert_extensions(&header); assert_eq!(next, expected_post_header); assert_eq!(rest, &e.slice()[read_len..]); // unexpected end of slice assert_matches!( Ipv6Extensions::from_slice(ip_numbers[0], &e.slice()[..read_len - 1]).unwrap_err(), ReadError::UnexpectedEndOfSlice(_) ); } } // test the parsing of different extension header combinations for first_header in &EXTESION_KNOWN_IP_NUMBERS { // single header parsing run_test( &[*first_header, post_header], &[header_size], ); for second_header in &EXTESION_KNOWN_IP_NUMBERS { // double header parsing run_test( &[*first_header, *second_header, post_header], &[header_size], ); for third_header in &EXTESION_KNOWN_IP_NUMBERS { // tripple header parsing run_test( &[*first_header, *second_header, *third_header, post_header], &[header_size], ); } } } } } proptest!{ #[test] fn read( header_size in any::(), post_header in any::() .prop_filter("Must be a non ipv6 header relevant ip number".to_owned(), |v| !EXTESION_KNOWN_IP_NUMBERS.iter().any(|&x| v == &x) ) ) { // no extension headers filled { let mut cursor = Cursor::new(&[]); let actual = Ipv6Extensions::read(&mut cursor, post_header).unwrap(); assert_eq!(actual.0, Default::default()); assert_eq!(actual.1, post_header); assert_eq!(0, cursor.position()); } /// Run a test with the given ip numbers fn run_test(ip_numbers: &[u8], header_sizes: &[u8]) { // setup test payload let e = ExtensionTestPayload::new( ip_numbers, header_sizes ); let mut cursor = Cursor::new(e.slice()); if e.exts_hop_by_hop_error() { // a hop by hop header that is not at the start triggers an error assert_matches!( Ipv6Extensions::read(&mut cursor, ip_numbers[0]).unwrap_err(), ReadError::Ipv6HopByHopHeaderNotAtStart ); } else { // normal read let (header, next) = Ipv6Extensions::read(&mut cursor, ip_numbers[0]).unwrap(); let (read_len, expected_post_header) = e.assert_extensions(&header); assert_eq!(next, expected_post_header); assert_eq!(cursor.position() as usize, read_len); // unexpected end of slice { let mut short_cursor = Cursor::new(&e.slice()[..read_len - 1]); assert_matches!( Ipv6Extensions::read(&mut short_cursor, ip_numbers[0]).unwrap_err(), ReadError::IoError(_) ); } } } // test the parsing of different extension header combinations for first_header in &EXTESION_KNOWN_IP_NUMBERS { // single header parsing run_test( &[*first_header, post_header], &[header_size], ); for second_header in &EXTESION_KNOWN_IP_NUMBERS { // double header parsing run_test( &[*first_header, *second_header, post_header], &[header_size], ); for third_header in &EXTESION_KNOWN_IP_NUMBERS { // tripple header parsing run_test( &[*first_header, *second_header, *third_header, post_header], &[header_size], ); } } } } } proptest! { #[test] fn write( header_size in any::(), post_header in any::() .prop_filter("Must be a non ipv6 header relevant ip number".to_owned(), |v| !EXTESION_KNOWN_IP_NUMBERS.iter().any(|&x| v == &x) ) ) { // no extension headers filled { let exts : Ipv6Extensions = Default::default(); let mut buffer = Vec::new(); exts.write(&mut buffer, post_header).unwrap(); assert_eq!(0, buffer.len()); } /// Run a test with the given ip numbers fn run_test(ip_numbers: &[u8], header_sizes: &[u8], post_header: u8) { use ValueError::*; // setup test header let e = ExtensionTestHeaders::new( ip_numbers, header_sizes ); if e.ip_numbers[1..e.ip_numbers.len()-1].iter().any(|&x| x == IPV6_HOP_BY_HOP) { // a hop by hop header that is not at the start triggers an error let mut writer = Vec::with_capacity(e.data.header_len()); assert_eq!( e.data.write(&mut writer, e.ip_numbers[0]).unwrap_err().value_error().unwrap(), Ipv6ExtensionHopByHopNotAtStart ); } else { // normal write { let mut writer = Vec::with_capacity(e.data.header_len()); e.data.write(&mut writer, e.ip_numbers[0]).unwrap(); if *e.ip_numbers.last().unwrap() != IPV6_HOP_BY_HOP { // decoding if there will be no duplicate hop by hop error // will be triggered let (read, read_next, _) = Ipv6Extensions::from_slice( e.ip_numbers[0], &writer ).unwrap(); assert_eq!(e.data, read); assert_eq!(*e.ip_numbers.last().unwrap(), read_next); } } // write error { let mut writer = TestWriter::with_max_size( e.data.header_len() - 1 ); let err = e.data.write( &mut writer, e.ip_numbers[0] ).unwrap_err(); assert_eq!( std::io::ErrorKind::UnexpectedEof, err.io_error().unwrap().kind() ); } // missing reference (skip the last header) { let mut missing_ref = e.clone(); let missing_ip_number = missing_ref.introduce_missing_ref(post_header); let mut writer = Vec::with_capacity(e.data.header_len()); let err = missing_ref.data.write( &mut writer, missing_ref.ip_numbers[0] ).unwrap_err(); assert_eq!( err.value_error().unwrap(), Ipv6ExtensionNotReferenced(missing_ip_number) ); } } } // test the parsing of different extension header combinations for first_header in &EXTESION_KNOWN_IP_NUMBERS { // single header parsing run_test( &[*first_header, post_header], &[header_size], post_header, ); for second_header in &EXTESION_KNOWN_IP_NUMBERS { // double header parsing run_test( &[*first_header, *second_header, post_header], &[header_size], post_header, ); for third_header in &EXTESION_KNOWN_IP_NUMBERS { // tripple header parsing run_test( &[*first_header, *second_header, *third_header, post_header], &[header_size], post_header, ); } } } } } proptest!{ #[test] fn header_len( hop_by_hop_options in ipv6_raw_extension_any(), destination_options in ipv6_raw_extension_any(), routing in ipv6_raw_extension_any(), fragment in ipv6_fragment_any(), auth in ip_authentication_any(), final_destination_options in ipv6_raw_extension_any(), ) { // None { let exts : Ipv6Extensions = Default::default(); assert_eq!(0, exts.header_len()); } // All filled { let exts = Ipv6Extensions{ hop_by_hop_options: Some(hop_by_hop_options.clone()), destination_options: Some(destination_options.clone()), routing: Some( Ipv6RoutingExtensions{ routing: routing.clone(), final_destination_options: Some(final_destination_options.clone()), } ), fragment: Some(fragment.clone()), auth: Some(auth.clone()), }; assert_eq!( exts.header_len(), ( hop_by_hop_options.header_len() + destination_options.header_len() + routing.header_len() + final_destination_options.header_len() + fragment.header_len() + auth.header_len() ) ); } // Routing without final destination options { let exts = Ipv6Extensions{ hop_by_hop_options: Some(hop_by_hop_options.clone()), destination_options: Some(destination_options.clone()), routing: Some( Ipv6RoutingExtensions{ routing: routing.clone(), final_destination_options: None, } ), fragment: Some(fragment.clone()), auth: Some(auth.clone()), }; assert_eq!( exts.header_len(), ( hop_by_hop_options.header_len() + destination_options.header_len() + routing.header_len() + fragment.header_len() + auth.header_len() ) ); } } } proptest! { #[test] fn set_next_headers( hop_by_hop_options in ipv6_raw_extension_any(), destination_options in ipv6_raw_extension_any(), routing in ipv6_raw_extension_any(), fragment in ipv6_fragment_any(), auth in ip_authentication_any(), final_destination_options in ipv6_raw_extension_any(), post_header in any::() .prop_filter("Must be a non ipv6 header relevant ip number".to_owned(), |v| !EXTESION_KNOWN_IP_NUMBERS.iter().any(|&x| v == &x) ), ) { // none filled { let mut exts : Ipv6Extensions = Default::default(); assert_eq!(post_header, exts.set_next_headers(post_header)); assert!(exts.hop_by_hop_options.is_none()); assert!(exts.destination_options.is_none()); assert!(exts.routing.is_none()); assert!(exts.fragment.is_none()); assert!(exts.auth.is_none()); } // all filled { let mut exts = Ipv6Extensions{ hop_by_hop_options: Some(hop_by_hop_options.clone()), destination_options: Some(destination_options.clone()), routing: Some( Ipv6RoutingExtensions{ routing: routing.clone(), final_destination_options: Some(final_destination_options.clone()), } ), fragment: Some(fragment.clone()), auth: Some(auth.clone()), }; assert_eq!(IPV6_HOP_BY_HOP, exts.set_next_headers(post_header)); assert_eq!(IPV6_DEST_OPTIONS, exts.hop_by_hop_options.as_ref().unwrap().next_header); assert_eq!(IPV6_ROUTE, exts.destination_options.as_ref().unwrap().next_header); assert_eq!(IPV6_FRAG, exts.routing.as_ref().unwrap().routing.next_header); assert_eq!(AUTH, exts.fragment.as_ref().unwrap().next_header); assert_eq!(IPV6_DEST_OPTIONS, exts.auth.as_ref().unwrap().next_header); assert_eq!(post_header, exts.routing.as_ref().unwrap().final_destination_options.as_ref().unwrap().next_header); } } } proptest!{ #[test] fn next_header( header_size in any::(), post_header in any::() .prop_filter("Must be a non ipv6 header relevant ip number".to_owned(), |v| !EXTESION_KNOWN_IP_NUMBERS.iter().any(|&x| v == &x) ),) { // test empty { let exts : Ipv6Extensions = Default::default(); assert_eq!(post_header, exts.next_header(post_header).unwrap()); } /// Run a test with the given ip numbers fn run_test(ip_numbers: &[u8], header_sizes: &[u8], post_header: u8) { use ValueError::*; // setup test header let e = ExtensionTestHeaders::new( ip_numbers, header_sizes ); if e.ip_numbers[1..e.ip_numbers.len()-1].iter().any(|&x| x == IPV6_HOP_BY_HOP) { // a hop by hop header that is not at the start triggers an error assert_eq!( e.data.next_header(e.ip_numbers[0]).unwrap_err(), Ipv6ExtensionHopByHopNotAtStart ); } else { // normal header assert_eq!( *e.ip_numbers.last().unwrap(), e.data.next_header(e.ip_numbers[0]).unwrap() ); // missing reference (skip the last header) { let mut missing_ref = e.clone(); let missing_ip_number = missing_ref.introduce_missing_ref(post_header); assert_eq!( missing_ref.data.next_header(missing_ref.ip_numbers[0]).unwrap_err(), Ipv6ExtensionNotReferenced(missing_ip_number) ); } } } // test the parsing of different extension header combinations for first_header in &EXTESION_KNOWN_IP_NUMBERS { // single header parsing run_test( &[*first_header, post_header], &[header_size], post_header, ); for second_header in &EXTESION_KNOWN_IP_NUMBERS { // double header parsing run_test( &[*first_header, *second_header, post_header], &[header_size], post_header, ); for third_header in &EXTESION_KNOWN_IP_NUMBERS { // tripple header parsing run_test( &[*first_header, *second_header, *third_header, post_header], &[header_size], post_header, ); } } } } } #[test] fn is_fragmenting_payload() { // empty assert_eq!( false, Ipv6Extensions{ hop_by_hop_options: None, destination_options: None, routing: None, fragment: None, auth: None, }.is_fragmenting_payload() ); // non fragmenting frag header assert_eq!( false, Ipv6Extensions{ hop_by_hop_options: None, destination_options: None, routing: None, fragment: Some(Ipv6FragmentHeader::new(ip_number::UDP, 0, false, 0)), auth: None, }.is_fragmenting_payload() ); // fragmenting frag header assert!( Ipv6Extensions{ hop_by_hop_options: None, destination_options: None, routing: None, fragment: Some(Ipv6FragmentHeader::new(ip_number::UDP, 0, true, 0)), auth: None, }.is_fragmenting_payload() ); } #[test] fn is_empty() { // empty assert!( Ipv6Extensions{ hop_by_hop_options: None, destination_options: None, routing: None, fragment: None, auth: None, }.is_empty() ); // hop_by_hop_options assert_eq!( false, Ipv6Extensions{ hop_by_hop_options: Some( Ipv6RawExtensionHeader::new_raw(ip_number::UDP, &[1,2,3,4,5,6]).unwrap() ), destination_options: None, routing: None, fragment: None, auth: None, }.is_empty() ); // destination_options assert_eq!( false, Ipv6Extensions{ hop_by_hop_options: None, destination_options: Some( Ipv6RawExtensionHeader::new_raw(ip_number::UDP, &[1,2,3,4,5,6]).unwrap() ), routing: None, fragment: None, auth: None, }.is_empty() ); // routing assert_eq!( false, Ipv6Extensions{ hop_by_hop_options: None, destination_options: None, routing: Some( Ipv6RoutingExtensions{ routing: Ipv6RawExtensionHeader::new_raw(ip_number::UDP, &[1,2,3,4,5,6]).unwrap(), final_destination_options: None, } ), fragment: None, auth: None, }.is_empty() ); // fragment assert_eq!( false, Ipv6Extensions{ hop_by_hop_options: None, destination_options: None, routing: None, fragment: Some(Ipv6FragmentHeader::new(ip_number::UDP, 0, true, 0)), auth: None, }.is_empty() ); // auth assert_eq!( false, Ipv6Extensions{ hop_by_hop_options: None, destination_options: None, routing: None, fragment: None, auth: Some(IpAuthenticationHeader::new(ip_number::UDP, 0, 0, &[]).unwrap()), }.is_empty() ); } #[test] fn debug() { let a : Ipv6Extensions = Default::default(); assert_eq!( &format!( "Ipv6Extensions {{ hop_by_hop_options: {:?}, destination_options: {:?}, routing: {:?}, fragment: {:?}, auth: {:?} }}", a.hop_by_hop_options, a.destination_options, a.routing, a.fragment, a.auth, ), &format!("{:?}", a) ); } #[test] fn clone_eq() { let a : Ipv6Extensions = Default::default(); assert_eq!(a, a.clone()); } #[test] fn default() { let a : Ipv6Extensions = Default::default(); assert_eq!(a.hop_by_hop_options, None); assert_eq!(a.destination_options, None); assert_eq!(a.routing, None); assert_eq!(a.fragment, None); assert_eq!(a.auth, None); } } pub mod slice { use super::*; proptest!{ #[test] fn from_slice( header_size in any::(), post_header in any::() .prop_filter("Must be a non ipv6 header relevant ip number".to_owned(), |v| !EXTESION_KNOWN_IP_NUMBERS.iter().any(|&x| v == &x) ) ) { // no extension headers filled { let some_data = [1,2,3,4]; let actual = Ipv6ExtensionsSlice::from_slice(UDP, &some_data).unwrap(); assert_eq!(actual.0.is_fragmenting_payload(), false); assert_eq!(actual.0.first_header(), None); assert_eq!(actual.0.slice().len(), 0); assert_eq!(actual.1, UDP); assert_eq!(actual.2, &some_data); } /// Run a test with the given ip numbers fn run_test(ip_numbers: &[u8], header_sizes: &[u8]) { // setup test payload let e = ExtensionTestPayload::new( ip_numbers, header_sizes ); if e.ip_numbers[1..].iter().any(|&x| x == IPV6_HOP_BY_HOP) { // a hop by hop header that is not at the start triggers an error assert_matches!( Ipv6ExtensionsSlice::from_slice(ip_numbers[0], e.slice()).unwrap_err(), ReadError::Ipv6HopByHopHeaderNotAtStart ); } else { // normal read let (header, next, rest) = Ipv6ExtensionsSlice::from_slice(ip_numbers[0], e.slice()).unwrap(); assert_eq!(header.first_header(), Some(ip_numbers[0])); assert_eq!(header.slice(), e.slice()); assert_eq!(next, *ip_numbers.last().unwrap()); assert_eq!(rest, &e.slice()[e.slice().len()..]); // unexpected end of slice assert_matches!( Ipv6ExtensionsSlice::from_slice(ip_numbers[0], &e.slice()[..e.slice().len() - 1]).unwrap_err(), ReadError::UnexpectedEndOfSlice(_) ); } } // test the parsing of different extension header combinations for first_header in &EXTESION_KNOWN_IP_NUMBERS { // single header parsing run_test( &[*first_header, post_header], &[header_size], ); for second_header in &EXTESION_KNOWN_IP_NUMBERS { // double header parsing run_test( &[*first_header, *second_header, post_header], &[header_size], ); for third_header in &EXTESION_KNOWN_IP_NUMBERS { // tripple header parsing run_test( &[*first_header, *second_header, *third_header, post_header], &[header_size], ); } } } } } proptest!{ #[test] fn is_fragmenting_payload( hop_by_hop_options in ipv6_raw_extension_any(), destination_options in ipv6_raw_extension_any(), routing in ipv6_raw_extension_any(), auth in ip_authentication_any(), final_destination_options in ipv6_raw_extension_any() ) { // no fragment header { let mut exts = Ipv6Extensions{ hop_by_hop_options: Some(hop_by_hop_options), destination_options: Some(destination_options), routing: Some( Ipv6RoutingExtensions { routing, final_destination_options: Some(final_destination_options), } ), fragment: None, auth: Some(auth), }; let first_ip_number = exts.set_next_headers(UDP); let mut bytes = Vec::with_capacity(exts.header_len()); exts.write(&mut bytes, first_ip_number).unwrap(); let (header, _, _) = Ipv6ExtensionsSlice::from_slice(first_ip_number, &bytes).unwrap(); assert_eq!(false, header.is_fragmenting_payload()); } // different variants of the fragment header with // variants that fragment and variants that don't fragment const FRAG_VARIANTS : [(bool, Ipv6FragmentHeader);4] = [ (false, Ipv6FragmentHeader::new(UDP, 0, false, 123)), (true, Ipv6FragmentHeader::new(UDP, 2, false, 123)), (true, Ipv6FragmentHeader::new(UDP, 0, true, 123)), (true, Ipv6FragmentHeader::new(UDP, 3, true, 123)), ]; for (first_expected, first_header) in FRAG_VARIANTS.iter() { // single fragment header { let bytes = first_header.to_bytes().unwrap(); let (header, _, _) = Ipv6ExtensionsSlice::from_slice(IPV6_FRAG, &bytes).unwrap(); assert_eq!(*first_expected, header.is_fragmenting_payload()); } // two fragment headers for (second_expected, second_header) in FRAG_VARIANTS.iter() { let mut first_mod = first_header.clone(); first_mod.next_header = IPV6_FRAG; let mut bytes = Vec::with_capacity(first_mod.header_len() + second_header.header_len()); bytes.extend_from_slice(&first_mod.to_bytes().unwrap()); bytes.extend_from_slice(&second_header.to_bytes().unwrap()); let (header, _, _) = Ipv6ExtensionsSlice::from_slice(IPV6_FRAG, &bytes).unwrap(); assert_eq!( *first_expected || *second_expected, header.is_fragmenting_payload() ); } } } } #[test] fn is_empty() { // empty { let slice = Ipv6ExtensionsSlice::from_slice( ip_number::UDP, &[] ).unwrap().0; assert!(slice.is_empty()); } // fragment { let bytes = Ipv6FragmentHeader::new(ip_number::UDP, 0, true, 0).to_bytes().unwrap(); let slice = Ipv6ExtensionsSlice::from_slice( ip_number::IPV6_FRAG, &bytes ).unwrap().0; assert_eq!( false, slice.is_empty() ); } } #[test] fn debug() { let a : Ipv6ExtensionsSlice = Default::default(); assert_eq!( "Ipv6ExtensionsSlice { first_header: None, fragmented: false, slice: [] }", &format!("{:?}", a) ); } #[test] fn clone_eq() { let a : Ipv6ExtensionsSlice = Default::default(); assert_eq!(a, a.clone()); } #[test] fn default() { let a : Ipv6ExtensionsSlice = Default::default(); assert_eq!(a.is_fragmenting_payload(), false); assert_eq!(a.first_header(), None); assert_eq!(a.slice().len(), 0); } } pub mod ipv6_routing_extension { use super::*; #[test] fn debug() { let a : Ipv6RoutingExtensions = Ipv6RoutingExtensions{ routing: Ipv6RawExtensionHeader::new_raw( 0, &[0;6], ).unwrap(), final_destination_options: None, }; assert_eq!( &format!( "Ipv6RoutingExtensions {{ routing: {:?}, final_destination_options: {:?} }}", a.routing, a.final_destination_options, ), &format!("{:?}", a) ); } #[test] fn clone_eq() { let a : Ipv6RoutingExtensions = Ipv6RoutingExtensions{ routing: Ipv6RawExtensionHeader::new_raw( 0, &[0;6], ).unwrap(), final_destination_options: None, }; assert_eq!(a, a.clone()); } } pub mod ipv6_extension_slice { use super::*; #[test] fn debug() { use Ipv6ExtensionSlice::*; { let header = Ipv6RawExtensionHeader::new_raw(UDP, &[1,2,3,4,5,6]).unwrap(); let mut buffer = Vec::with_capacity(header.header_len()); header.write(&mut buffer).unwrap(); let slice = Ipv6RawExtensionHeaderSlice::from_slice(&buffer).unwrap(); assert_eq!( format!("HopByHop({:?})", slice), format!("{:?}", HopByHop(slice.clone())) ); assert_eq!( format!("Routing({:?})", slice), format!("{:?}", Routing(slice.clone())) ); assert_eq!( format!("DestinationOptions({:?})", slice), format!("{:?}", DestinationOptions(slice.clone())) ); } { let header = Ipv6FragmentHeader::new(UDP, 1, true, 2); let mut buffer = Vec::with_capacity(header.header_len()); header.write(&mut buffer).unwrap(); let slice = Ipv6FragmentHeaderSlice::from_slice(&buffer).unwrap(); assert_eq!( format!("Fragment({:?})", slice), format!("{:?}", Fragment(slice)) ); } { let header = IpAuthenticationHeader::new(UDP, 1, 2, &[1,2,3,4]).unwrap(); let mut buffer = Vec::with_capacity(header.header_len()); header.write(&mut buffer).unwrap(); let slice = IpAuthenticationHeaderSlice::from_slice(&buffer).unwrap(); assert_eq!( format!("Authentication({:?})", slice), format!("{:?}", Authentication(slice.clone())) ); } } #[test] fn clone_eq() { use Ipv6ExtensionSlice::*; let header = Ipv6RawExtensionHeader::new_raw(UDP, &[1,2,3,4,5,6]).unwrap(); let mut buffer = Vec::with_capacity(header.header_len()); header.write(&mut buffer).unwrap(); let slice = Ipv6RawExtensionHeaderSlice::from_slice(&buffer).unwrap(); let hop = HopByHop(slice.clone()); assert_eq!(hop.clone(), hop.clone()); let route = Routing(slice.clone()); assert_eq!(route.clone(), route.clone()); assert_ne!(route, hop); } } pub mod slice_iter { use super::*; #[test] fn into_iter() { let a : Ipv6ExtensionsSlice = Default::default(); let mut iter = a.into_iter(); assert_eq!(None, iter.next()); } proptest!{ #[test] fn next( header_size in any::(), post_header in any::() .prop_filter("Must be a non ipv6 header relevant ip number".to_owned(), |v| !EXTESION_KNOWN_IP_NUMBERS.iter().any(|&x| v == &x) ) ) { /// Run a test with the given ip numbers fn run_test(ip_numbers: &[u8], header_sizes: &[u8]) { // setup test payload let e = ExtensionTestPayload::new( ip_numbers, header_sizes ); // a hop by hop header that is not at the start triggers an error if false == e.ip_numbers[1..].iter().any(|&x| x == IPV6_HOP_BY_HOP) { // normal read let (header, _, _) = Ipv6ExtensionsSlice::from_slice(ip_numbers[0], e.slice()).unwrap(); let mut iter = header.into_iter(); let mut slice = e.slice(); // go through all expected headers for i in 0..e.ip_numbers.len() - 1 { use Ipv6ExtensionSlice::*; // iterate and check all results let next = iter.next().unwrap(); match e.ip_numbers[i] { IPV6_HOP_BY_HOP => { let header = Ipv6RawExtensionHeaderSlice::from_slice(slice).unwrap(); assert_eq!(next, HopByHop(header.clone())); slice = &slice[header.slice().len()..]; }, IPV6_ROUTE => { let header = Ipv6RawExtensionHeaderSlice::from_slice(slice).unwrap(); assert_eq!(next, Routing(header.clone())); slice = &slice[header.slice().len()..]; }, IPV6_DEST_OPTIONS => { let header = Ipv6RawExtensionHeaderSlice::from_slice(slice).unwrap(); assert_eq!(next, DestinationOptions(header.clone())); slice = &slice[header.slice().len()..]; } IPV6_FRAG => { let header = Ipv6FragmentHeaderSlice::from_slice(slice).unwrap(); assert_eq!(next, Fragment(header.clone())); slice = &slice[header.slice().len()..]; }, AUTH => { let header = IpAuthenticationHeaderSlice::from_slice(slice).unwrap(); assert_eq!(next, Authentication(header.clone())); slice = &slice[header.slice().len()..]; }, _ => unreachable!() } } // expect that all headers have been visited assert_eq!(None, iter.next()); } } // test the parsing of different extension header combinations for first_header in &EXTESION_KNOWN_IP_NUMBERS { // single header parsing run_test( &[*first_header, post_header], &[header_size], ); for second_header in &EXTESION_KNOWN_IP_NUMBERS { // double header parsing run_test( &[*first_header, *second_header, post_header], &[header_size], ); for third_header in &EXTESION_KNOWN_IP_NUMBERS { // tripple header parsing run_test( &[*first_header, *second_header, *third_header, post_header], &[header_size], ); } } } } } #[test] fn debug() { let a : Ipv6ExtensionSliceIter = Default::default(); assert_eq!( "Ipv6ExtensionSliceIter { next_header: 59, rest: [] }", &format!("{:?}", a) ); } #[test] fn clone_eq() { let a : Ipv6ExtensionSliceIter = Default::default(); assert_eq!(a.clone(), a); } #[test] fn default() { let mut a : Ipv6ExtensionSliceIter = Default::default(); assert_eq!(None, a.next()); } }etherparse-0.13.0/tests/internet/ipv6_fragment.rs000064400000000000000000000343461046102023000201700ustar 00000000000000use super::super::*; use std::io::{Cursor, ErrorKind}; pub mod header { use super::*; proptest! { #[test] fn new( next_header in any::(), fragment_offset in any::(), more_fragments in any::(), identification in any::(), ) { let a = Ipv6FragmentHeader::new( next_header, fragment_offset, more_fragments, identification ); assert_eq!(next_header, a.next_header); assert_eq!(fragment_offset, a.fragment_offset); assert_eq!(more_fragments, a.more_fragments); assert_eq!(identification, a.identification); } } proptest! { #[test] fn from_slice( input in ipv6_fragment_any(), dummy_data in proptest::collection::vec(any::(), 0..20) ) { // serialize let mut buffer: Vec = Vec::with_capacity(8 + dummy_data.len()); input.write(&mut buffer).unwrap(); buffer.extend(&dummy_data[..]); // calls with a valid result { let (result, rest) = Ipv6FragmentHeader::from_slice(&buffer[..]).unwrap(); assert_eq!(input, result); assert_eq!(&buffer[8..], rest); } // call with not enough data in the slice for len in 0..=7 { assert_matches!( Ipv6FragmentHeader::from_slice(&buffer[0..len]), Err(ReadError::UnexpectedEndOfSlice(_)) ); } } } proptest! { #[test] fn read( input in ipv6_fragment_any(), dummy_data in proptest::collection::vec(any::(), 0..20) ) { // serialize let mut buffer: Vec = Vec::with_capacity(8 + dummy_data.len()); input.write(&mut buffer).unwrap(); buffer.extend(&dummy_data[..]); // calls with a valid result { let mut cursor = Cursor::new(&buffer); let result = Ipv6FragmentHeader::read(&mut cursor).unwrap(); assert_eq!(input, result); assert_eq!(cursor.position(), 8); } // call with not enough data in the slice for len in 0..=7 { let mut cursor = Cursor::new(&buffer[0..len]); assert_eq!( Ipv6FragmentHeader::read(&mut cursor) .unwrap_err() .io_error() .unwrap() .kind(), ErrorKind::UnexpectedEof ); } } } proptest! { #[test] fn write(input in ipv6_fragment_any()) { // normal write { let mut buffer = Vec::with_capacity(8); input.write(&mut buffer).unwrap(); assert_eq!( &buffer, &input.to_bytes().unwrap() ); } // too big fragment offset for i in 0b001..=0b111u16 { use crate::ValueError::*; use crate::ErrorField::*; let fragment_offset = input.fragment_offset | (i << 13); let input_with_bad_frag_off = { let mut re = input.clone(); re.fragment_offset = fragment_offset; re }; let mut buffer = Vec::with_capacity(8); assert_eq!( input_with_bad_frag_off .write(&mut buffer) .unwrap_err() .value_error() .unwrap(), U16TooLarge{ value: fragment_offset, max: 0b0001_1111_1111_1111, field: Ipv6FragmentOffset } ); } // not enough memory for write for len in 0..8 { let mut writer = TestWriter::with_max_size(len); assert_eq!( ErrorKind::UnexpectedEof, input.write(&mut writer).unwrap_err().io_error().unwrap().kind() ); } } } proptest! { #[test] fn header_len(input in ipv6_fragment_any()) { assert_eq!(8, input.header_len()); } } proptest!{ #[test] fn is_fragmenting_payload( non_zero_offset in 1u16..0b0001_1111_1111_1111u16, identification in any::(), next_header in any::(), ) { // negative case { let header = Ipv6FragmentHeader { next_header, fragment_offset: 0, more_fragments: false, identification }; assert!(false == header.is_fragmenting_payload()); } // positive case (non zero offset) { let header = Ipv6FragmentHeader { next_header, fragment_offset: non_zero_offset, more_fragments: false, identification }; assert!(header.is_fragmenting_payload()); } // positive case (more fragments) { let header = Ipv6FragmentHeader { next_header, fragment_offset: 0, more_fragments: true, identification }; assert!(header.is_fragmenting_payload()); } // positive case (non zero offset & more fragments) { let header = Ipv6FragmentHeader { next_header, fragment_offset: non_zero_offset, more_fragments: true, identification }; assert!(header.is_fragmenting_payload()); } } } proptest! { #[test] fn to_bytes(input in ipv6_fragment_any()) { // normal write { let fragment_offset_be = input.fragment_offset.to_be_bytes(); let id_be = input.identification.to_be_bytes(); assert_eq!( &input.to_bytes().unwrap(), &[ input.next_header, 0, ( (fragment_offset_be[0] << 3 & 0b1111_1000u8) | (fragment_offset_be[1] >> 5 & 0b0000_0111u8) ), ( (fragment_offset_be[1] & 0b0001_1111u8) | if input.more_fragments { 0b1000_0000u8 } else { 0u8 } ), id_be[0], id_be[1], id_be[2], id_be[3], ] ); } // too big fragment offset for i in 0b001..=0b111u16 { use crate::ValueError::*; use crate::ErrorField::*; let fragment_offset = input.fragment_offset | (i << 13); let input_with_bad_frag_off = { let mut re = input.clone(); re.fragment_offset = fragment_offset; re }; assert_eq!( input_with_bad_frag_off .to_bytes() .unwrap_err(), U16TooLarge{ value: fragment_offset, max: 0b0001_1111_1111_1111, field: Ipv6FragmentOffset } ); } } } proptest! { #[test] fn dbg(input in ipv6_fragment_any()) { assert_eq!( &format!( "Ipv6FragmentHeader {{ next_header: {}, fragment_offset: {}, more_fragments: {}, identification: {} }}", input.next_header, input.fragment_offset, input.more_fragments, input.identification ), &format!("{:?}", input) ); } } proptest! { #[test] fn clone_eq(input in ipv6_fragment_any()) { assert_eq!(input, input.clone()); } } } pub mod slice { use super::*; proptest! { #[test] fn from_slice( input in ipv6_fragment_any(), dummy_data in proptest::collection::vec(any::(), 0..20) ) { // serialize let mut buffer: Vec = Vec::with_capacity(8 + dummy_data.len()); input.write(&mut buffer).unwrap(); buffer.extend(&dummy_data[..]); // calls with a valid result { let slice = Ipv6FragmentHeaderSlice::from_slice(&buffer[..]).unwrap(); assert_eq!(slice.slice(), &buffer[..8]); } // call with not enough data in the slice for len in 0..=7 { assert_matches!( Ipv6FragmentHeaderSlice::from_slice(&buffer[0..len]) .unwrap_err() .unexpected_end_of_slice_min_expected_size() .unwrap(), 8 ); } } } proptest! { #[test] fn from_slice_unchecked( input in ipv6_fragment_any(), dummy_data in proptest::collection::vec(any::(), 0..20) ) { // serialize let mut buffer: Vec = Vec::with_capacity(8 + dummy_data.len()); input.write(&mut buffer).unwrap(); buffer.extend(&dummy_data[..]); // calls with a valid result unsafe { let slice = Ipv6FragmentHeaderSlice::from_slice_unchecked(&buffer[..]); assert_eq!(slice.slice(), &buffer[..8]); } } } proptest! { #[test] fn getters(input in ipv6_fragment_any()) { let buffer = input.to_bytes().unwrap(); let slice = Ipv6FragmentHeaderSlice::from_slice(&buffer[..]).unwrap(); assert_eq!(input.next_header, slice.next_header()); assert_eq!(input.fragment_offset, slice.fragment_offset()); assert_eq!(input.more_fragments, slice.more_fragments()); assert_eq!(input.identification, slice.identification()); } } proptest! { #[test] fn is_fragmenting_payload( non_zero_offset in 1u16..0b0001_1111_1111_1111u16, identification in any::(), next_header in any::(), ) { // negative case { let header = Ipv6FragmentHeader { next_header, fragment_offset: 0, more_fragments: false, identification }; // slice let buffer = header.to_bytes().unwrap(); let slice = Ipv6FragmentHeaderSlice::from_slice(&buffer).unwrap(); assert!(false == slice.is_fragmenting_payload()); } // positive case (non zero offset) { let header = Ipv6FragmentHeader { next_header, fragment_offset: non_zero_offset, more_fragments: false, identification }; // slice let buffer = header.to_bytes().unwrap(); let slice = Ipv6FragmentHeaderSlice::from_slice(&buffer).unwrap(); assert!(slice.is_fragmenting_payload()); } // positive case (more fragments) { let header = Ipv6FragmentHeader { next_header, fragment_offset: 0, more_fragments: true, identification }; // slice let buffer = header.to_bytes().unwrap(); let slice = Ipv6FragmentHeaderSlice::from_slice(&buffer).unwrap(); assert!(slice.is_fragmenting_payload()); } // positive case (non zero offset & more fragments) { let header = Ipv6FragmentHeader { next_header, fragment_offset: non_zero_offset, more_fragments: true, identification }; // slice let buffer = header.to_bytes().unwrap(); let slice = Ipv6FragmentHeaderSlice::from_slice(&buffer).unwrap(); assert!(slice.is_fragmenting_payload()); } } } proptest! { #[test] fn to_header(input in ipv6_fragment_any()) { let buffer = input.to_bytes().unwrap(); let slice = Ipv6FragmentHeaderSlice::from_slice(&buffer).unwrap(); assert_eq!(input, slice.to_header()); } } proptest! { #[test] fn dbg(input in ipv6_fragment_any()) { let bytes = input.to_bytes().unwrap(); let slice = Ipv6FragmentHeaderSlice::from_slice( &bytes ).unwrap(); assert_eq!( &format!( "Ipv6FragmentHeaderSlice {{ slice: {:?} }}", slice.slice() ), &format!("{:?}", slice) ); } } proptest! { #[test] fn clone_eq(input in ipv6_fragment_any()) { let bytes = input.to_bytes().unwrap(); let slice = Ipv6FragmentHeaderSlice::from_slice( &bytes ).unwrap(); assert_eq!(slice, slice.clone()); } } } etherparse-0.13.0/tests/internet/ipv6_raw_extension.rs000064400000000000000000000236121046102023000212440ustar 00000000000000use super::super::*; use std::io::ErrorKind; #[test] fn header_new_raw_and_set_payload() { use ValueError::*; struct Test { payload: &'static [u8], expected: Result<(),ValueError> } let tests = [ // ok Test{payload: &[1,2,3,4,5,6], expected: Ok(()) }, Test{payload: &[1,2,3,4,5,6,7,8,9,10,11,12,13,14], expected: Ok(()) }, Test{payload: &[0;0xff*8 + 6], expected: Ok(()) }, // too small Test{payload: &[1,2,3,4,5], expected: Err(Ipv6ExtensionPayloadTooSmall(5)) }, Test{payload: &[1,2,3,4], expected: Err(Ipv6ExtensionPayloadTooSmall(4)) }, Test{payload: &[1], expected: Err(Ipv6ExtensionPayloadTooSmall(1)) }, Test{payload: &[], expected: Err(Ipv6ExtensionPayloadTooSmall(0)) }, // too large Test{payload: &[0;0xff*8 + 7], expected: Err(Ipv6ExtensionPayloadTooLarge(0xff*8 + 7)) }, ]; for test in tests.iter() { // new_raw { let actual = Ipv6RawExtensionHeader::new_raw(123, test.payload); match &test.expected { Ok(_) => { let unpacked = actual.unwrap(); assert_eq!(123, unpacked.next_header); assert_eq!(&test.payload[..], unpacked.payload()); }, Err(err) => { assert_eq!(Err(err.clone()), actual); } } } // set payload { let mut header = Ipv6RawExtensionHeader::new_raw(123, &[0;6]).unwrap(); let result = header.set_payload(test.payload); match &test.expected { Ok(_) => { assert_eq!(Ok(()), result); assert_eq!(test.payload, header.payload()); }, Err(err) => { assert_eq!(Err(err.clone()), result); assert_eq!(&[0;6], header.payload()); } } } } // unaligment errors { let payload = [0;23]; for i in 7..=23 { if 0 != (i - 6) % 8 { assert_eq!( Err(Ipv6ExtensionPayloadLengthUnaligned(i)), Ipv6RawExtensionHeader::new_raw(123, &payload[..i]) ); { let mut header = Ipv6RawExtensionHeader::new_raw(123, &[0;6]).unwrap(); assert_eq!( Err(Ipv6ExtensionPayloadLengthUnaligned(i)), header.set_payload(&payload[..i]) ); assert_eq!(&[0;6], header.payload()); } } } } } #[test] fn slice_from_slice() { // base test let data = { let mut data = [0;6*8]; data[0] = 1; // next header type data[1] = 4; // header length data }; let actual = Ipv6RawExtensionHeaderSlice::from_slice(&data).unwrap(); assert_eq!(1, actual.next_header()); assert_eq!( &data[..5*8], actual.slice() ); assert_eq!( &data[2..5*8], actual.payload() ); { let header = actual.to_header(); assert_eq!(1, header.next_header); assert_eq!(&data[2..5*8], header.payload()); } } #[test] fn slice_from_slice_unchecked() { let data = { let mut data = [0;6*8]; data[0] = 1; // next header type data[1] = 4; // header length data }; let header = unsafe { Ipv6RawExtensionHeaderSlice::from_slice_unchecked(&data) }; assert_eq!(&data[..5*8], header.slice()); } #[test] fn slice_from_slice_error() { // errors: // length smaller then 8 { assert_matches!( Ipv6RawExtensionHeaderSlice::from_slice(&[0;7]), Err(ReadError::UnexpectedEndOfSlice(8)) ); assert_matches!( Ipv6RawExtensionHeader::from_slice(&[0;7]), Err(ReadError::UnexpectedEndOfSlice(8)) ); } // length smaller then spezified size { let data = { let mut data: [u8;4*8 - 1] = [0;4*8 - 1]; // set length field data[1] = 3; data }; assert_matches!( Ipv6RawExtensionHeaderSlice::from_slice(&data), Err(ReadError::UnexpectedEndOfSlice(32)) ); assert_matches!( Ipv6RawExtensionHeader::from_slice(&data), Err(ReadError::UnexpectedEndOfSlice(32)) ); } } #[test] fn extension_from_slice_bad_length() { use crate::ip_number::UDP; use self::ReadError::*; //smaller then minimum extension header size (8 bytes) { let buffer: [u8; 7] = [ UDP,2,0,0, 0,0,0 ]; assert_matches!(Ipv6RawExtensionHeaderSlice::from_slice(&buffer), Err(UnexpectedEndOfSlice(8))); } //smaller then specified size by length field { let buffer: [u8; 8*3-1] = [ UDP,2,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0, ]; // should generate an error let slice = Ipv6RawExtensionHeaderSlice::from_slice(&buffer); assert_matches!(slice, Err(UnexpectedEndOfSlice(_))); } } #[test] fn header_type_supported() { use crate::ip_number::*; for i in 0..0xffu8 { let expected = match i { IPV6_HOP_BY_HOP | IPV6_ROUTE | IPV6_DEST_OPTIONS | MOBILITY | HIP | SHIM6 => true, _ => false }; assert_eq!(expected, Ipv6RawExtensionHeader::header_type_supported(i)); assert_eq!(expected, Ipv6RawExtensionHeaderSlice::header_type_supported(i)); } } proptest! { #[test] fn write_and_read( input in ipv6_raw_extension_any() ) { let mut buffer: Vec = Vec::new(); input.write(&mut buffer).unwrap(); // add some dummy data to check the slice length buffer.push(0); buffer.push(1); { let actual = Ipv6RawExtensionHeaderSlice::from_slice(&buffer).unwrap(); assert_eq!(actual.next_header(), input.next_header); assert_eq!(actual.payload(), input.payload()); assert_eq!(actual.to_header(), input); // slice clone & equal check assert_eq!(actual, actual.clone()); } { let actual = Ipv6RawExtensionHeader::from_slice(&buffer).unwrap(); assert_eq!(input, actual.0); assert_eq!(&buffer[buffer.len() - 2..], actual.1); } { use std::io::Cursor; let mut cursor = Cursor::new(&buffer); let actual = Ipv6RawExtensionHeader::read(&mut cursor).unwrap(); assert_eq!(input, actual); assert_eq!(cursor.position(), (buffer.len() - 2) as u64); } } } #[test] fn read_errors() { use std::io::Cursor; // errors: // length smaller then 8 for i in 0..8 { let buffer = [0u8;7]; let mut cursor = Cursor::new(&buffer[..i]); assert_matches!( Ipv6RawExtensionHeader::read(&mut cursor), Err(ReadError::IoError(_)) ); } // length smaller then spezified size { let buffer = { let mut buffer: [u8;4*8 - 1] = [0;4*8 - 1]; // set length field buffer[1] = 3; buffer }; let mut cursor = Cursor::new(&buffer); assert_matches!( Ipv6RawExtensionHeader::read(&mut cursor), Err(ReadError::IoError(_)) ); } } proptest! { #[test] fn write_errors( input in ipv6_raw_extension_any() ) { // check that all possible "not enough data" cases trigger an error on write for len in 0..input.header_len()-1 { let mut writer = TestWriter::with_max_size(len); assert_eq!( ErrorKind::UnexpectedEof, input.write(&mut writer).unwrap_err().io_error().unwrap().kind() ); } } } proptest! { #[test] fn header_len(input in ipv6_raw_extension_any()) { assert_eq!(input.header_len(), input.payload().len() + 2); } } proptest! { #[test] fn debug(input in ipv6_raw_extension_any()) { // debug trait { assert_eq!( &format!("Ipv6RawExtensionHeader {{ next_header: {}, payload: {:?} }}", input.next_header, input.payload()), &format!("{:?}", input) ); } { let buffer = { let mut buffer = Vec::with_capacity(input.header_len()); input.write(&mut buffer).unwrap(); buffer }; let slice = Ipv6RawExtensionHeaderSlice::from_slice(&buffer).unwrap(); assert_eq!( &format!("Ipv6RawExtensionHeaderSlice {{ slice: {:?} }}", slice.slice()), &format!("{:?}", slice) ); } } } #[test] fn partial_equal() { let a = Ipv6RawExtensionHeader::new_raw( 123, &[ 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, 15,16,17,18,19,20,21,22, 23,24,25,26,27,28,29,30, ] ).unwrap(); assert_eq!(a, a); // non equal next_header { let mut b = a.clone(); b.next_header = 0; assert_ne!(a, b); } // non equal payload data { let b = Ipv6RawExtensionHeader::new_raw( 123, &[ 1, 2, 3, 4, 5, 6, 7, 8, 9,99,11,12,13,14, 15,16,17,18,19,20,21,22, 23,24,25,26,27,28,29,30, ] ).unwrap(); assert_ne!(a, b); } // non equal payload length { let b = Ipv6RawExtensionHeader::new_raw( 123, &[ 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, 15,16,17,18,19,20,21,22, ] ).unwrap(); assert_ne!(a, b); } } etherparse-0.13.0/tests/internet/mod.rs000064400000000000000000000002471046102023000161710ustar 00000000000000pub mod ip; pub mod ip_authentication; pub mod ipv4; pub mod ipv4_extensions; pub mod ipv6; pub mod ipv6_extensions; pub mod ipv6_raw_extension; pub mod ipv6_fragment;etherparse-0.13.0/tests/link/ethernet.rs000064400000000000000000000245551046102023000163450ustar 00000000000000use super::super::*; use std::io::{Cursor, ErrorKind}; mod ether_type { use super::*; #[test] fn to_u16() { use crate::EtherType::*; assert_eq!(0x0800, Ipv4 as u16); assert_eq!(0x86dd, Ipv6 as u16); assert_eq!(0x0806, Arp as u16); assert_eq!(0x0842, WakeOnLan as u16); assert_eq!(0x8100, VlanTaggedFrame as u16); assert_eq!(0x88A8, ProviderBridging as u16); assert_eq!(0x9100, VlanDoubleTaggedFrame as u16); } #[test] fn from_u16() { use crate::EtherType::*; assert_eq!(EtherType::from_u16(0x0800), Some(Ipv4)); assert_eq!(EtherType::from_u16(0x86dd), Some(Ipv6)); assert_eq!(EtherType::from_u16(0x0806), Some(Arp)); assert_eq!(EtherType::from_u16(0x0842), Some(WakeOnLan)); assert_eq!(EtherType::from_u16(0x8100), Some(VlanTaggedFrame)); assert_eq!(EtherType::from_u16(0x88A8), Some(ProviderBridging)); assert_eq!(EtherType::from_u16(0x9100), Some(VlanDoubleTaggedFrame)); assert_eq!(EtherType::from_u16(0x1234), None); } #[test] fn constants() { use crate::EtherType::*; use crate::ether_type::*; let pairs = &[ (Ipv4, IPV4), (Ipv6, IPV6), (Arp, ARP), (WakeOnLan, WAKE_ON_LAN), (VlanTaggedFrame, VLAN_TAGGED_FRAME), (ProviderBridging, PROVIDER_BRIDGING), (VlanDoubleTaggedFrame, VLAN_DOUBLE_TAGGED_FRAME) ]; for (enum_value, constant) in pairs { assert_eq!(enum_value.clone() as u16, *constant); } } #[test] fn dbg() { use crate::EtherType::*; let pairs = &[ (Ipv4, "Ipv4"), (Ipv6, "Ipv6"), (Arp, "Arp"), (WakeOnLan, "WakeOnLan"), (VlanTaggedFrame, "VlanTaggedFrame"), (ProviderBridging, "ProviderBridging"), (VlanDoubleTaggedFrame, "VlanDoubleTaggedFrame") ]; for (enum_value, str_value) in pairs { assert_eq!( str_value, &format!("{:?}", enum_value) ); } } #[test] fn clone_eq() { use crate::EtherType::*; let values = &[ Ipv4, Ipv6, Arp, WakeOnLan, VlanTaggedFrame, ProviderBridging, VlanDoubleTaggedFrame, ]; // clone for v in values { assert_eq!(v, &v.clone()); } // eq for (a_pos, a) in values.iter().enumerate() { for (b_pos, b) in values.iter().enumerate() { assert_eq!( a_pos == b_pos, a == b ); assert_eq!( a_pos != b_pos, a != b ); } } } } mod ethernet2_header { use super::*; proptest! { #[test] fn from_slice( input in ethernet_2_any(), dummy_data in proptest::collection::vec(any::(), 0..20) ) { // serialize let mut buffer: Vec = Vec::with_capacity(14 + dummy_data.len()); input.write(&mut buffer).unwrap(); buffer.extend(&dummy_data[..]); // calls with a valid result { let (result, rest) = Ethernet2Header::from_slice(&buffer[..]).unwrap(); assert_eq!(input, result); assert_eq!(&buffer[14..], rest); } #[allow(deprecated)] { let (result, rest) = Ethernet2Header::read_from_slice(&buffer[..]).unwrap(); assert_eq!(input, result); assert_eq!(&buffer[14..], rest); } // call with not enough data in the slice for len in 0..=13 { assert_matches!( Ethernet2Header::from_slice(&buffer[0..len]), Err(ReadError::UnexpectedEndOfSlice(_)) ); } } } proptest! { #[test] fn from_bytes(input in ethernet_2_any()) { assert_eq!( input, Ethernet2Header::from_bytes(input.to_bytes()) ); } } proptest! { #[test] fn read( input in ethernet_2_any(), dummy_data in proptest::collection::vec(any::(), 0..20) ) { // normal read let mut buffer = Vec::with_capacity(14 + dummy_data.len()); input.write(&mut buffer).unwrap(); buffer.extend(&dummy_data[..]); // calls with a valid result { let mut cursor = Cursor::new(&buffer); let result = Ethernet2Header::read(&mut cursor).unwrap(); assert_eq!(input, result); assert_eq!(cursor.position(), 14); } // unexpected eof for len in 0..=13 { let mut cursor = Cursor::new(&buffer[0..len]); assert_eq!( Ethernet2Header::read(&mut cursor) .unwrap_err() .kind(), ErrorKind::UnexpectedEof ); } } } proptest! { #[test] fn write_to_slice(input in ethernet_2_any()) { // normal write { let mut buffer: [u8;14] = [0;14]; input.write_to_slice(&mut buffer).unwrap(); assert_eq!(buffer, input.to_bytes()); } // len to small for len in 0..14 { let mut buffer: [u8;14] = [0;14]; assert_eq!( input.write_to_slice(&mut buffer[..len]) .unwrap_err() .slice_too_small_size() .unwrap(), Ethernet2Header::SERIALIZED_SIZE ); } } } proptest! { #[test] fn write(input in ethernet_2_any()) { // successfull write { let mut buffer: Vec = Vec::with_capacity(14); input.write(&mut buffer).unwrap(); assert_eq!(&buffer[..], &input.to_bytes()); } // not enough memory for write (unexpected eof) for len in 0..8 { let mut writer = TestWriter::with_max_size(len); assert_eq!( ErrorKind::UnexpectedEof, input.write(&mut writer).unwrap_err().kind() ); } } } proptest! { #[test] fn header_len(input in ethernet_2_any()) { assert_eq!(input.header_len(), 14); } } proptest! { #[test] fn to_bytes(input in ethernet_2_any()) { let ether_type_be = input.ether_type.to_be_bytes(); assert_eq!( input.to_bytes(), [ input.destination[0], input.destination[1], input.destination[2], input.destination[3], input.destination[4], input.destination[5], input.source[0], input.source[1], input.source[2], input.source[3], input.source[4], input.source[5], ether_type_be[0], ether_type_be[1], ] ); } } proptest! { #[test] fn clone_eq(input in ethernet_2_any()) { assert_eq!(input, input.clone()); } } proptest! { #[test] fn dbg(input in ethernet_2_any()) { assert_eq!( &format!( "Ethernet2Header {{ source: {:?}, destination: {:?}, ether_type: {} }}", input.source, input.destination, input.ether_type ), &format!("{:?}", input) ); } } } mod ethernet2_header_slice { use super::*; proptest! { #[test] fn from_slice( input in ethernet_2_any(), dummy_data in proptest::collection::vec(any::(), 0..20) ) { // serialize let mut buffer: Vec = Vec::with_capacity(14 + dummy_data.len()); input.write(&mut buffer).unwrap(); buffer.extend(&dummy_data[..]); // calls with a valid result { let result = Ethernet2HeaderSlice::from_slice(&buffer[..]).unwrap(); assert_eq!(&buffer[..14], result.slice()); } // call with not enough data in the slice for len in 0..=13 { assert_matches!( Ethernet2HeaderSlice::from_slice(&buffer[0..len]), Err(ReadError::UnexpectedEndOfSlice(_)) ); } } } proptest! { #[test] fn getters(input in ethernet_2_any()) { let buffer = input.to_bytes(); let slice = Ethernet2HeaderSlice::from_slice(&buffer).unwrap(); assert_eq!(input.destination, slice.destination()); assert_eq!(input.source, slice.source()); assert_eq!(input.ether_type, slice.ether_type()); } } proptest! { #[test] fn to_header(input in ethernet_2_any()) { let buffer = input.to_bytes(); let slice = Ethernet2HeaderSlice::from_slice(&buffer).unwrap(); assert_eq!(input, slice.to_header()); } } proptest! { #[test] fn clone_eq(input in ethernet_2_any()) { let buffer = input.to_bytes(); let slice = Ethernet2HeaderSlice::from_slice(&buffer).unwrap(); assert_eq!(slice, slice.clone()); } } proptest! { #[test] fn dbg(input in ethernet_2_any()) { let buffer = input.to_bytes(); let slice = Ethernet2HeaderSlice::from_slice(&buffer).unwrap(); assert_eq!( &format!( "Ethernet2HeaderSlice {{ slice: {:?} }}", slice.slice() ), &format!("{:?}", slice) ); } } } etherparse-0.13.0/tests/link/mod.rs000064400000000000000000000017671046102023000153060ustar 00000000000000pub mod ethernet; pub mod vlan_tagging; use super::*; mod link_slice { use super::*; proptest! { #[test] fn debug_clone_eq(ref eth in ethernet_2_unknown()) { let bytes = eth.to_bytes(); let e = Ethernet2HeaderSlice::from_slice(&bytes).unwrap(); let slice = LinkSlice::Ethernet2( e.clone() ); // clone & eq assert_eq!(slice.clone(), slice); // debug assert_eq!( format!("{:?}", slice), format!("Ethernet2({:?})", e), ); } } proptest! { #[test] fn to_header(ref eth in ethernet_2_unknown()) { let bytes = eth.to_bytes(); let slice = LinkSlice::Ethernet2( Ethernet2HeaderSlice::from_slice(&bytes).unwrap() ); // clone & eq assert_eq!( slice.to_header(), *eth ); } } }etherparse-0.13.0/tests/link/vlan_tagging.rs000064400000000000000000000646371046102023000171740ustar 00000000000000use super::super::*; use std::io::{Cursor, ErrorKind}; use proptest::prelude::*; mod vlan_header { use super::*; #[test] fn constants() { use ether_type::*; use VlanHeader as V; assert_eq!(3, V::VLAN_ETHER_TYPES.len()); assert_eq!(VLAN_TAGGED_FRAME, V::VLAN_ETHER_TYPES[0]); assert_eq!(PROVIDER_BRIDGING, V::VLAN_ETHER_TYPES[1]); assert_eq!(VLAN_DOUBLE_TAGGED_FRAME, V::VLAN_ETHER_TYPES[2]); } proptest!{ #[test] fn clone_eq( single in vlan_single_any(), double in vlan_double_any(), ) { // single eq { let value = VlanHeader::Single(single.clone()); assert_eq!(value, value.clone()); } // double { let value = VlanHeader::Double(double); assert_eq!(value, value.clone()); } } } proptest!{ #[test] fn dbg( single in vlan_single_any(), double in vlan_double_any(), ) { // single { let value = VlanHeader::Single(single.clone()); assert_eq!( &format!( "Single({:?})", single ), &format!("{:?}", value) ); } // double { let value = VlanHeader::Double(double.clone()); assert_eq!( &format!( "Double({:?})", double ), &format!("{:?}", value) ); } } } proptest!{ #[test] fn header_len( single in vlan_single_any(), double in vlan_double_any(), ) { // single assert_eq!( SingleVlanHeader::SERIALIZED_SIZE, VlanHeader::Single(single.clone()).header_len() ); // double assert_eq!( DoubleVlanHeader::SERIALIZED_SIZE, VlanHeader::Double(double.clone()).header_len() ); } } proptest!{ #[test] fn write( single in vlan_single_any(), double in vlan_double_any(), ) { // single { let expected = { let mut buffer = Vec::with_capacity(single.header_len()); single.write(&mut buffer).unwrap(); buffer }; let actual = { let mut buffer = Vec::with_capacity(single.header_len()); VlanHeader::Single(single.clone()).write(&mut buffer).unwrap(); buffer }; assert_eq!(expected, actual); } // double { let expected = { let mut buffer = Vec::with_capacity(double.header_len()); double.write(&mut buffer).unwrap(); buffer }; let actual = { let mut buffer = Vec::with_capacity(double.header_len()); VlanHeader::Double(double.clone()).write(&mut buffer).unwrap(); buffer }; assert_eq!(expected, actual); } } } } mod vlan_slice { use super::*; proptest!{ #[test] fn to_header( single in vlan_single_any(), double in vlan_double_any(), ) { // single { let raw = single.to_bytes().unwrap(); let slice = VlanSlice::SingleVlan( SingleVlanHeaderSlice::from_slice(&raw).unwrap() ); assert_eq!( slice.to_header(), VlanHeader::Single(single) ); } // double { let raw = double.to_bytes().unwrap(); let slice = VlanSlice::DoubleVlan( DoubleVlanHeaderSlice::from_slice(&raw).unwrap() ); assert_eq!( slice.to_header(), VlanHeader::Double(double) ); } } } proptest!{ #[test] fn debug( single in vlan_single_any(), double in vlan_double_any(), ) { // single { let raw = single.to_bytes().unwrap(); let s = SingleVlanHeaderSlice::from_slice(&raw).unwrap(); assert_eq!( format!("{:?}", VlanSlice::SingleVlan(s.clone())), format!("SingleVlan({:?})", s) ); } // double { let raw = double.to_bytes().unwrap(); let d = DoubleVlanHeaderSlice::from_slice(&raw).unwrap(); assert_eq!( format!("{:?}", VlanSlice::DoubleVlan(d.clone())), format!("DoubleVlan({:?})", d) ); } } } proptest!{ #[test] fn clone_eq( single in vlan_single_any(), double in vlan_double_any(), ) { // single { let raw = single.to_bytes().unwrap(); let s = VlanSlice::SingleVlan( SingleVlanHeaderSlice::from_slice(&raw).unwrap() ); assert_eq!(s.clone(), s); } // double { let raw = double.to_bytes().unwrap(); let d = VlanSlice::DoubleVlan( DoubleVlanHeaderSlice::from_slice(&raw).unwrap() ); assert_eq!(d.clone(), d); } } } } mod single_vlan_header { use super::*; #[test] fn constants() { assert_eq!(4, SingleVlanHeader::SERIALIZED_SIZE); } proptest!{ #[test] fn from_slice( input in vlan_single_any(), dummy_data in proptest::collection::vec(any::(), 0..20) ) { // serialize let mut buffer: Vec = Vec::with_capacity(input.header_len() + dummy_data.len()); input.write(&mut buffer).unwrap(); buffer.extend(&dummy_data[..]); // normal { let (result, rest) = SingleVlanHeader::from_slice(&buffer).unwrap(); assert_eq!(result, input); assert_eq!(rest, &buffer[4..]); } #[allow(deprecated)] { let (result, rest) = SingleVlanHeader::read_from_slice(&buffer).unwrap(); assert_eq!(result, input); assert_eq!(rest, &buffer[4..]); } // slice length to small for len in 0..4 { assert_eq!( SingleVlanHeader::from_slice(&buffer[..len]) .unwrap_err() .unexpected_end_of_slice_min_expected_size() .unwrap(), 4 ); } } } proptest!{ #[test] fn from_bytes(input in vlan_single_any()) { let actual = SingleVlanHeader::from_bytes( input.to_bytes().unwrap() ); assert_eq!(actual, input); } } proptest!{ #[test] fn read( input in vlan_single_any(), dummy_data in proptest::collection::vec(any::(), 0..20) ) { // serialize let mut buffer: Vec = Vec::with_capacity(input.header_len() + dummy_data.len()); input.write(&mut buffer).unwrap(); buffer.extend(&dummy_data[..]); // normal { let mut cursor = Cursor::new(&buffer); let result = SingleVlanHeader::read(&mut cursor).unwrap(); assert_eq!(result, input); assert_eq!(4, cursor.position()); } // unexpexted eof for len in 0..4 { let mut cursor = Cursor::new(&buffer[0..len]); assert_eq!( SingleVlanHeader::read(&mut cursor) .unwrap_err() .kind(), ErrorKind::UnexpectedEof ); } } } proptest!{ #[test] fn write_and_to_bytes(input in vlan_single_any()) { // normal write { let mut buffer: Vec = Vec::with_capacity(input.header_len()); input.write(&mut buffer).unwrap(); assert_eq!(&buffer[..], &input.to_bytes().unwrap()); { let id_be = input.vlan_identifier.to_be_bytes(); let eth_type_be = input.ether_type.to_be_bytes(); assert_eq!( input.to_bytes().unwrap(), [ ( id_be[0] | if input.drop_eligible_indicator { 0x10 } else { 0 } | (input.priority_code_point << 5) ), id_be[1], eth_type_be[0], eth_type_be[1] ] ); } } // priority_code_point: outside of range error { let mut buffer: Vec = Vec::with_capacity(input.header_len()); for i in 1..=0b0001_1111u8 { let mut bad_input = input.clone(); bad_input.priority_code_point |= i << 3; let expected = ValueError::U8TooLarge{ value: bad_input.priority_code_point, max: 0b111, field: ErrorField::VlanTagPriorityCodePoint }; assert_eq!( expected, bad_input.write(&mut buffer) .unwrap_err() .value_error() .unwrap() ); assert_eq!( expected, bad_input.to_bytes() .unwrap_err() ); } } // vlan_identifier: outside of range error { let mut buffer: Vec = Vec::with_capacity(input.header_len()); for i in 1..=0b1111u16 { let mut bad_input = input.clone(); bad_input.vlan_identifier |= i << 12; let expected = ValueError::U16TooLarge{ value: bad_input.vlan_identifier, max: 0b1111_1111_1111, field: ErrorField::VlanTagVlanId }; assert_eq!( expected, bad_input.write(&mut buffer) .unwrap_err() .value_error() .unwrap() ); assert_eq!( expected, bad_input.to_bytes() .unwrap_err() ); } } // unexpected eof for len in 0..4 { let mut writer = TestWriter::with_max_size(len); assert_eq!( ErrorKind::UnexpectedEof, input.write(&mut writer) .unwrap_err() .io_error() .unwrap() .kind() ); } } } proptest!{ #[test] fn header_len(input in vlan_single_any()) { assert_eq!(4, input.header_len()); } } #[test] fn default() { let actual : SingleVlanHeader = Default::default(); assert_eq!(0, actual.priority_code_point); assert_eq!(false, actual.drop_eligible_indicator); assert_eq!(0, actual.vlan_identifier); assert_eq!(0, actual.ether_type); } proptest!{ #[test] fn clone_eq(input in vlan_single_any()) { assert_eq!(input, input.clone()); } } proptest!{ #[test] fn dbg(input in vlan_single_any()) { assert_eq!( &format!( "SingleVlanHeader {{ priority_code_point: {}, drop_eligible_indicator: {}, vlan_identifier: {}, ether_type: {} }}", input.priority_code_point, input.drop_eligible_indicator, input.vlan_identifier, input.ether_type, ), &format!("{:?}", input) ); } } } mod double_vlan_header { use super::*; #[test] fn constants() { assert_eq!(8, DoubleVlanHeader::SERIALIZED_SIZE); } proptest!{ #[test] fn from_slice( input in vlan_double_any(), dummy_data in proptest::collection::vec(any::(), 0..20), ether_type_non_vlan in any::().prop_filter( "ether_type must not be a vlan ether type", |v| !VlanHeader::VLAN_ETHER_TYPES.iter().any(|&x| v == &x) ) ) { // serialize let mut buffer: Vec = Vec::with_capacity(input.header_len() + dummy_data.len()); input.write(&mut buffer).unwrap(); buffer.extend(&dummy_data[..]); // normal { let (result, rest) = DoubleVlanHeader::from_slice(&buffer).unwrap(); assert_eq!(result, input); assert_eq!(rest, &buffer[8..]); } #[allow(deprecated)] { let (result, rest) = DoubleVlanHeader::read_from_slice(&buffer).unwrap(); assert_eq!(result, input); assert_eq!(rest, &buffer[8..]); } // slice length to small for len in 0..8 { assert_eq!( DoubleVlanHeader::from_slice(&buffer[..len]) .unwrap_err() .unexpected_end_of_slice_min_expected_size() .unwrap(), 8 ); } // bad outer ether type { let mut bad_outer = input.clone(); bad_outer.outer.ether_type = ether_type_non_vlan; let bytes = bad_outer.to_bytes().unwrap(); assert_matches!( DoubleVlanHeader::from_slice(&bytes) .unwrap_err(), ReadError::DoubleVlanOuterNonVlanEtherType(_) ); } } } proptest!{ #[test] fn read( input in vlan_double_any(), dummy_data in proptest::collection::vec(any::(), 0..20), ether_type_non_vlan in any::().prop_filter( "ether_type must not be a vlan ether type", |v| !VlanHeader::VLAN_ETHER_TYPES.iter().any(|&x| v == &x) ) ) { // serialize let mut buffer: Vec = Vec::with_capacity(input.header_len() + dummy_data.len()); input.write(&mut buffer).unwrap(); buffer.extend(&dummy_data[..]); // normal { let mut cursor = Cursor::new(&buffer); let result = DoubleVlanHeader::read(&mut cursor).unwrap(); assert_eq!(result, input); assert_eq!(8, cursor.position()); } // outer & inner error for len in 0..8 { let mut cursor = Cursor::new(&buffer[0..len]); assert_eq!( DoubleVlanHeader::read(&mut cursor) .unwrap_err() .io_error() .unwrap() .kind(), ErrorKind::UnexpectedEof ); } // bad outer ether type { let mut bad_outer = input.clone(); bad_outer.outer.ether_type = ether_type_non_vlan; let bytes = bad_outer.to_bytes().unwrap(); let mut cursor = Cursor::new(&bytes); assert_matches!( DoubleVlanHeader::read(&mut cursor) .unwrap_err(), ReadError::DoubleVlanOuterNonVlanEtherType(_) ); } } } proptest!{ #[test] fn write_and_to_bytes(input in vlan_double_any()) { // normal write { let mut buffer: Vec = Vec::with_capacity(input.header_len()); input.write(&mut buffer).unwrap(); assert_eq!(&buffer[..], &input.to_bytes().unwrap()); { let inner_bytes = input.inner.to_bytes().unwrap(); let outer_bytes = input.outer.to_bytes().unwrap(); assert_eq!( input.to_bytes().unwrap(), [ outer_bytes[0], outer_bytes[1], outer_bytes[2], outer_bytes[3], inner_bytes[0], inner_bytes[1], inner_bytes[2], inner_bytes[3], ] ); } } // bad value outer { let mut bad_input = input.clone(); bad_input.outer.priority_code_point = 0b1000; let mut buffer: Vec = Vec::new(); let expected = ValueError::U8TooLarge{ value: bad_input.outer.priority_code_point, max: 0b111, field: ErrorField::VlanTagPriorityCodePoint }; assert_eq!( bad_input .write(&mut buffer) .unwrap_err() .value_error() .unwrap(), expected ); assert_eq!( bad_input .to_bytes() .unwrap_err(), expected ); } // bad value inner { let mut bad_input = input.clone(); bad_input.inner.priority_code_point = 0b1000; let mut buffer: Vec = Vec::new(); let expected = ValueError::U8TooLarge{ value: bad_input.inner.priority_code_point, max: 0b111, field: ErrorField::VlanTagPriorityCodePoint }; assert_eq!( bad_input .write(&mut buffer) .unwrap_err() .value_error() .unwrap(), expected ); assert_eq!( bad_input .to_bytes() .unwrap_err(), expected ); } } } proptest!{ #[test] fn header_len(input in vlan_double_any()) { assert_eq!(8, input.header_len()); } } #[test] fn default() { let actual : DoubleVlanHeader = Default::default(); assert_eq!( actual.outer, { let mut outer : SingleVlanHeader = Default::default(); outer.ether_type = ether_type::VLAN_TAGGED_FRAME; outer } ); assert_eq!(actual.inner, Default::default()); } proptest!{ #[test] fn clone_eq(input in vlan_double_any()) { assert_eq!(input, input.clone()); } } proptest!{ #[test] fn dbg(input in vlan_double_any()) { assert_eq!( &format!( "DoubleVlanHeader {{ outer: {:?}, inner: {:?} }}", input.outer, input.inner, ), &format!("{:?}", input) ); } } } mod single_vlan_header_slice { use super::*; proptest!{ #[test] fn from_slice( input in vlan_single_any(), dummy_data in proptest::collection::vec(any::(), 0..20) ) { // serialize let mut buffer: Vec = Vec::with_capacity(input.header_len() + dummy_data.len()); input.write(&mut buffer).unwrap(); buffer.extend(&dummy_data[..]); // normal { let slice = SingleVlanHeaderSlice::from_slice(&buffer).unwrap(); assert_eq!(slice.slice(), &buffer[..4]); } // slice length to small for len in 0..4 { assert_eq!( SingleVlanHeaderSlice::from_slice(&buffer[..len]) .unwrap_err() .unexpected_end_of_slice_min_expected_size() .unwrap(), 4 ); } } } proptest!{ #[test] fn getters(input in vlan_single_any()) { let bytes = input.to_bytes().unwrap(); let slice = SingleVlanHeaderSlice::from_slice(&bytes).unwrap(); assert_eq!(input.priority_code_point, slice.priority_code_point()); assert_eq!(input.drop_eligible_indicator, slice.drop_eligible_indicator()); assert_eq!(input.vlan_identifier, slice.vlan_identifier()); assert_eq!(input.ether_type, slice.ether_type()); } } proptest!{ #[test] fn to_header(input in vlan_single_any()) { let bytes = input.to_bytes().unwrap(); let slice = SingleVlanHeaderSlice::from_slice(&bytes).unwrap(); assert_eq!(input, slice.to_header()); } } proptest!{ #[test] fn clone_eq(input in vlan_single_any()) { let bytes = input.to_bytes().unwrap(); let slice = SingleVlanHeaderSlice::from_slice(&bytes).unwrap(); assert_eq!(slice, slice.clone()); } } proptest!{ #[test] fn dbg(input in vlan_single_any()) { let bytes = input.to_bytes().unwrap(); let slice = SingleVlanHeaderSlice::from_slice(&bytes).unwrap(); assert_eq!( &format!( "SingleVlanHeaderSlice {{ slice: {:?} }}", slice.slice(), ), &format!("{:?}", slice) ); } } } mod double_vlan_header_slice { use super::*; proptest!{ #[test] fn from_slice( input in vlan_double_any(), dummy_data in proptest::collection::vec(any::(), 0..20), ether_type_non_vlan in any::().prop_filter( "ether_type must not be a vlan ether type", |v| !VlanHeader::VLAN_ETHER_TYPES.iter().any(|&x| v == &x) ) ) { { // serialize let mut buffer: Vec = Vec::with_capacity(input.header_len() + dummy_data.len()); input.write(&mut buffer).unwrap(); buffer.extend(&dummy_data[..]); // normal { let slice = DoubleVlanHeaderSlice::from_slice(&buffer).unwrap(); assert_eq!(slice.slice(), &buffer[..8]); } // slice length to small for len in 0..8 { assert_eq!( DoubleVlanHeaderSlice::from_slice(&buffer[..len]) .unwrap_err() .unexpected_end_of_slice_min_expected_size() .unwrap(), 8 ); } } // bad outer ether type { let mut bad_outer = input.clone(); bad_outer.outer.ether_type = ether_type_non_vlan; assert_matches!( DoubleVlanHeaderSlice::from_slice(&bad_outer.to_bytes().unwrap()) .unwrap_err(), ReadError::DoubleVlanOuterNonVlanEtherType(_) ); } } } proptest!{ #[test] fn getters(input in vlan_double_any()) { let bytes = input.to_bytes().unwrap(); let slice = DoubleVlanHeaderSlice::from_slice(&bytes).unwrap(); assert_eq!(input.outer, slice.outer().to_header()); assert_eq!(input.inner, slice.inner().to_header()); } } proptest!{ #[test] fn to_header(input in vlan_double_any()) { let bytes = input.to_bytes().unwrap(); let slice = DoubleVlanHeaderSlice::from_slice(&bytes).unwrap(); assert_eq!( DoubleVlanHeader{ outer: input.outer, inner: input.inner, }, slice.to_header() ); } } proptest!{ #[test] fn clone_eq(input in vlan_double_any()) { let bytes = input.to_bytes().unwrap(); let slice = DoubleVlanHeaderSlice::from_slice(&bytes).unwrap(); assert_eq!(slice, slice.clone()); } } proptest!{ #[test] fn dbg(input in vlan_double_any()) { let bytes = input.to_bytes().unwrap(); let slice = DoubleVlanHeaderSlice::from_slice(&bytes).unwrap(); assert_eq!( &format!( "DoubleVlanHeaderSlice {{ slice: {:?} }}", slice.slice(), ), &format!("{:?}", slice) ); } } } etherparse-0.13.0/tests/packet_builder/mod.proptest-regressions000064400000000000000000000022361046102023000231130ustar 00000000000000# Seeds for failure cases proptest has generated in the past. It is # automatically read and these particular cases re-run before any # novel cases are generated. # # It is recommended to check this file in to source control so that # everyone who runs the test benefits from these saved cases. cc d577e10285ad36f88c147f7c53f0951e85410c31a943333fedac312263d977c8 # shrinks to ipv4_source = [0, 0, 0, 0], ipv4_dest = [0, 0, 0, 0], ipv4_time_to_live = 0, icmpv4_type_u8 = 15, icmpv4_code_u8 = 0, icmpv4_bytes5to8 = [0, 0, 0, 0], icmpv4 = Icmpv4Header { icmp_type: EchoReply(IcmpEchoHeader { id: 0, seq: 0 }), checksum: 0 }, payload = [] cc 6220e36db716272237e0db616e62aa7e9cdacf172ed0d059a3de92bd7e44e5f4 # shrinks to ipv6_source = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], ipv6_dest = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], ipv6_hop_limit = 0, icmpv6_type_u8 = 128, icmpv6_code_u8 = 0, icmpv6_bytes5to8 = [0, 0, 0, 0], icmpv6 = Unknown { type_u8: 0, code_u8: 0, bytes5to8: [0, 0, 0, 0] }, echo_id = 0, echo_seq = 0, payload = [] cc 5a7d441c96931785724cc1a64a70b0814ea4eb376f0c3f431e25d810244f9831 cc c44f627b48969e745640922cdc65a84c23581947a07a9c8e6b2ac29281fb6001etherparse-0.13.0/tests/packet_builder/mod.rs000064400000000000000000001454061046102023000173250ustar 00000000000000use etherparse::*; use super::*; #[test] fn eth_ipv4_udp() { //generate let in_payload = [24,25,26,27]; let mut serialized = Vec::new(); PacketBuilder::ethernet2([1,2,3,4,5,6],[7,8,9,10,11,12]) .ipv4([13,14,15,16], [17,18,19,20], 21) .udp(22,23) .write(&mut serialized, &in_payload) .unwrap(); //check the deserialized size let expected_ip_size: usize = UdpHeader::SERIALIZED_SIZE + in_payload.len(); assert_eq!(expected_ip_size + Ethernet2Header::SERIALIZED_SIZE + Ipv4Header::SERIALIZED_SIZE, serialized.len()); //deserialize and check that everything is as expected use std::io::Cursor; use std::io::Read; //deserialize each part of the message and check it let mut cursor = Cursor::new(&serialized); //ethernet 2 header assert_eq!(Ethernet2Header::read(&mut cursor).unwrap(), Ethernet2Header{ source: [1,2,3,4,5,6], destination: [7,8,9,10,11,12], ether_type: ether_type::IPV4 }); //ip header let ip_actual = Ipv4Header::read(&mut cursor).unwrap(); let mut ip_expected = Ipv4Header::new( expected_ip_size as u16, 21, //ttl ip_number::UDP, [13,14,15,16], [17,18,19,20] ); ip_expected.header_checksum = ip_expected.calc_header_checksum().unwrap(); assert_eq!(ip_actual, ip_expected); //udp header let udp_actual = UdpHeader::read(&mut cursor).unwrap(); let udp_expected = UdpHeader::with_ipv4_checksum(22, 23, &ip_expected, &in_payload).unwrap(); assert_eq!(udp_actual, udp_expected); //payload let mut actual_payload: [u8;4] = [0;4]; cursor.read_exact(&mut actual_payload).unwrap(); assert_eq!(actual_payload, in_payload); } #[test] fn ipv4() { let auth_ext = IpAuthenticationHeader::new( 0, 1, 2, &[3,4,5,6] ).unwrap(); //generate let in_payload = [22,23,24,25]; let mut serialized = Vec::new(); let builder = PacketBuilder::ip( IpHeader::Version4( Ipv4Header::new( in_payload.len() as u16, 21, 0, [13,14,15,16], [17,18,19,20] ), Ipv4Extensions{ auth: Some(auth_ext.clone()) } ) ); // check size assert_eq!( builder.size(in_payload.len()), Ipv4Header::SERIALIZED_SIZE + auth_ext.header_len() + in_payload.len() ); // write serialized.reserve(builder.size(in_payload.len())); builder.write(&mut serialized, 200, &in_payload) .unwrap(); //check the deserialized size assert_eq!( Ipv4Header::SERIALIZED_SIZE + auth_ext.header_len() + in_payload.len(), serialized.len() ); //deserialize and check that everything is as expected use std::io::{Cursor, Read}; //deserialize each part of the message and check it let mut cursor = Cursor::new(&serialized); //ip header let ip_actual = Ipv4Header::read(&mut cursor).unwrap(); let mut ip_expected = Ipv4Header::new( (auth_ext.header_len() + in_payload.len()) as u16, 21, //ttl ip_number::AUTH, // should have been set [13,14,15,16], [17,18,19,20] ); ip_expected.header_checksum = ip_expected.calc_header_checksum().unwrap(); assert_eq!(ip_actual, ip_expected); // auth header let auth_actual = IpAuthenticationHeader::read(&mut cursor).unwrap(); assert_eq!( auth_actual, IpAuthenticationHeader::new( 200, // ip number should have been set 1, 2, &[3,4,5,6] ).unwrap() ); //payload let mut actual_payload: [u8;4] = [0;4]; cursor.read_exact(&mut actual_payload).unwrap(); assert_eq!(actual_payload, in_payload); } #[test] fn ipv6() { let auth_ext = IpAuthenticationHeader::new( 0, 1, 2, &[3,4,5,6] ).unwrap(); //generate let in_payload = [48,49,50,51]; let mut serialized = Vec::new(); let builder = PacketBuilder:: ip( IpHeader::Version6( Ipv6Header{ traffic_class: 0, flow_label: 0, payload_length: in_payload.len() as u16, next_header: 0, hop_limit: 47, source: [11,12,13,14,15,16,17,18,19,10,21,22,23,24,25,26], destination: [31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46] }, Ipv6Extensions { hop_by_hop_options: None, destination_options: None, routing: None, fragment: None, auth: Some(auth_ext.clone()), } ) ); // check size assert_eq!( builder.size(in_payload.len()), Ipv6Header::SERIALIZED_SIZE + auth_ext.header_len() + in_payload.len() ); // write builder.write(&mut serialized, 200, &in_payload).unwrap(); //check the deserialized size assert_eq!( Ipv6Header::SERIALIZED_SIZE + auth_ext.header_len() + in_payload.len(), serialized.len() ); //deserialize and check that everything is as expected use std::io::{Cursor, Read}; //deserialize each part of the message and check it let mut cursor = Cursor::new(&serialized); //ip header let ip_actual = Ipv6Header::read(&mut cursor).unwrap(); let ip_expected = Ipv6Header{ traffic_class: 0, flow_label: 0, payload_length: (auth_ext.header_len() + in_payload.len()) as u16, next_header: ip_number::AUTH, // should have been set hop_limit: 47, source: [11,12,13,14,15,16,17,18,19,10,21,22,23,24,25,26], destination: [31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46] }; assert_eq!(ip_actual, ip_expected); // auth header let auth_actual = IpAuthenticationHeader::read(&mut cursor).unwrap(); assert_eq!( auth_actual, IpAuthenticationHeader::new( 200, // ip number should have been set 1, 2, &[3,4,5,6] ).unwrap() ); //payload let mut actual_payload: [u8;4] = [0;4]; cursor.read_exact(&mut actual_payload).unwrap(); assert_eq!(actual_payload, in_payload); } #[test] fn ipv4_udp() { //generate let in_payload = [24,25,26,27]; let mut serialized = Vec::new(); PacketBuilder::ipv4([13,14,15,16], [17,18,19,20], 21) .udp(22,23) .write(&mut serialized, &in_payload) .unwrap(); //check the deserialized size let expected_ip_size: usize = UdpHeader::SERIALIZED_SIZE + in_payload.len(); assert_eq!(expected_ip_size + Ipv4Header::SERIALIZED_SIZE, serialized.len()); //deserialize and check that everything is as expected use std::io::{Cursor, Read}; //deserialize each part of the message and check it let mut cursor = Cursor::new(&serialized); //ip header let ip_actual = Ipv4Header::read(&mut cursor).unwrap(); let mut ip_expected = Ipv4Header::new( expected_ip_size as u16, 21, //ttl ip_number::UDP, [13,14,15,16], [17,18,19,20] ); ip_expected.header_checksum = ip_expected.calc_header_checksum().unwrap(); assert_eq!(ip_actual, ip_expected); //udp header let udp_actual = UdpHeader::read(&mut cursor).unwrap(); let udp_expected = UdpHeader::with_ipv4_checksum(22, 23, &ip_expected, &in_payload).unwrap(); assert_eq!(udp_actual, udp_expected); //payload let mut actual_payload: [u8;4] = [0;4]; cursor.read_exact(&mut actual_payload).unwrap(); assert_eq!(actual_payload, in_payload); } #[test] fn ipv6_udp() { //generate let in_payload = [24,25,26,27]; let mut serialized = Vec::new(); PacketBuilder:: ipv6( //source [11,12,13,14,15,16,17,18,19,10,21,22,23,24,25,26], //destination [31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46], //hop_limit 47, ) .udp(22,23) .write(&mut serialized, &in_payload) .unwrap(); //check the deserialized size let expected_ip_size: usize = UdpHeader::SERIALIZED_SIZE + in_payload.len(); assert_eq!(expected_ip_size + Ipv6Header::SERIALIZED_SIZE, serialized.len()); //deserialize and check that everything is as expected use std::io::{Cursor, Read}; //deserialize each part of the message and check it let mut cursor = Cursor::new(&serialized); //ip header let ip_actual = Ipv6Header::read(&mut cursor).unwrap(); let ip_expected = Ipv6Header{ traffic_class: 0, flow_label: 0, payload_length: (UdpHeader::SERIALIZED_SIZE + in_payload.len()) as u16, next_header: ip_number::UDP, hop_limit: 47, source: [11,12,13,14,15,16,17,18,19,10,21,22,23,24,25,26], destination: [31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46] }; assert_eq!(ip_actual, ip_expected); //udp header let udp_actual = UdpHeader::read(&mut cursor).unwrap(); let udp_expected = UdpHeader::with_ipv6_checksum(22, 23, &ip_expected, &in_payload).unwrap(); assert_eq!(udp_actual, udp_expected); //payload let mut actual_payload: [u8;4] = [0;4]; cursor.read_exact(&mut actual_payload).unwrap(); assert_eq!(actual_payload, in_payload); } #[test] fn ipv4_custom_udp() { //generate let in_payload = [24,25,26,27]; let mut serialized = Vec::new(); PacketBuilder:: ip(IpHeader::Version4(Ipv4Header::new( 0, //payload_len will be replaced during write 12, //time_to_live ip_number::TCP, //will be replaced during write [13,14,15,16], //source [17,18,19,20] //destination ), Default::default())) .udp(22,23) .write(&mut serialized, &in_payload) .unwrap(); //check the deserialized size let expected_ip_size: usize = UdpHeader::SERIALIZED_SIZE + in_payload.len(); assert_eq!(expected_ip_size + Ipv4Header::SERIALIZED_SIZE, serialized.len()); //deserialize and check that everything is as expected use std::io::{Cursor, Read}; //deserialize each part of the message and check it let mut cursor = Cursor::new(&serialized); //ip header let ip_actual = Ipv4Header::read(&mut cursor).unwrap(); let mut ip_expected = Ipv4Header::new( expected_ip_size as u16, 12, //ttl ip_number::UDP, [13,14,15,16], [17,18,19,20] ); ip_expected.header_checksum = ip_expected.calc_header_checksum().unwrap(); assert_eq!(ip_actual, ip_expected); //udp header let udp_actual = UdpHeader::read(&mut cursor).unwrap(); let udp_expected = UdpHeader::with_ipv4_checksum(22, 23, &ip_expected, &in_payload).unwrap(); assert_eq!(udp_actual, udp_expected); //payload let mut actual_payload: [u8;4] = [0;4]; cursor.read_exact(&mut actual_payload).unwrap(); assert_eq!(actual_payload, in_payload); } #[test] fn udp_builder_eth_ipv6_udp() { //generate let in_payload = [50,51,52,53]; let mut serialized = Vec::new(); PacketBuilder::ethernet2([1,2,3,4,5,6], [7,8,9,10,11,12]) .ipv6([11,12,13,14,15,16,17,18,19,10,21,22,23,24,25,26], [31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46], 47, ) .udp(48,49) .write(&mut serialized, &in_payload) .unwrap(); //check the deserialized size assert_eq!(Ethernet2Header::SERIALIZED_SIZE + Ipv6Header::SERIALIZED_SIZE + UdpHeader::SERIALIZED_SIZE + in_payload.len(), serialized.len()); //deserialize and check that everything is as expected use std::io::Cursor; use std::io::Read; //deserialize each part of the message and check it let mut cursor = Cursor::new(&serialized); //ethernet 2 header assert_eq!(Ethernet2Header::read(&mut cursor).unwrap(), Ethernet2Header{ source: [1,2,3,4,5,6], destination: [7,8,9,10,11,12], ether_type: EtherType::Ipv6 as u16 }); //ip header let ip_actual = Ipv6Header::read(&mut cursor).unwrap(); let ip_expected = Ipv6Header{ traffic_class: 0, flow_label: 0, payload_length: (UdpHeader::SERIALIZED_SIZE + in_payload.len()) as u16, next_header: ip_number::UDP, hop_limit: 47, source: [11,12,13,14,15,16,17,18,19,10,21,22,23,24,25,26], destination: [31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46] }; assert_eq!(ip_actual, ip_expected); //udp header let udp_actual = UdpHeader::read(&mut cursor).unwrap(); let udp_expected = UdpHeader::with_ipv6_checksum(48, 49, &ip_expected, &in_payload).unwrap(); assert_eq!(udp_actual, udp_expected); //payload let mut actual_payload: [u8;4] = [0;4]; cursor.read_exact(&mut actual_payload).unwrap(); assert_eq!(actual_payload, in_payload); } #[test] fn udp_builder_eth_single_vlan_ipv4_udp() { //generate let in_payload = [50,51,52,53]; let mut serialized = Vec::new(); PacketBuilder::ethernet2([1,2,3,4,5,6], [7,8,9,10,11,12]) .single_vlan(0x123) .ipv4([13,14,15,16], [17,18,19,20], 21) .udp(48,49) .write(&mut serialized, &in_payload) .unwrap(); //check the deserialized size //check the deserialized size let expected_ip_size: usize = UdpHeader::SERIALIZED_SIZE + in_payload.len(); assert_eq!(expected_ip_size + Ethernet2Header::SERIALIZED_SIZE + Ipv4Header::SERIALIZED_SIZE + SingleVlanHeader::SERIALIZED_SIZE, serialized.len()); //deserialize and check that everything is as expected use std::io::Cursor; use std::io::Read; //deserialize each part of the message and check it let mut cursor = Cursor::new(&serialized); //ethernet 2 header assert_eq!(Ethernet2Header::read(&mut cursor).unwrap(), Ethernet2Header{ source: [1,2,3,4,5,6], destination: [7,8,9,10,11,12], ether_type: EtherType::VlanTaggedFrame as u16 }); //vlan header assert_eq!(SingleVlanHeader::read(&mut cursor).unwrap(), SingleVlanHeader{ priority_code_point: 0, drop_eligible_indicator: false, vlan_identifier: 0x123, ether_type: ether_type::IPV4 }); //ip header let ip_actual = Ipv4Header::read(&mut cursor).unwrap(); let mut ip_expected = Ipv4Header::new( expected_ip_size as u16, //payload_len 21, //ttl ip_number::UDP, [13,14,15,16], [17,18,19,20] ); ip_expected.header_checksum = ip_expected.calc_header_checksum().unwrap(); assert_eq!(ip_actual, ip_expected); //udp header let udp_actual = UdpHeader::read(&mut cursor).unwrap(); let udp_expected = UdpHeader::with_ipv4_checksum(48, 49, &ip_expected, &in_payload).unwrap(); assert_eq!(udp_actual, udp_expected); //payload let mut actual_payload: [u8;4] = [0;4]; cursor.read_exact(&mut actual_payload).unwrap(); assert_eq!(actual_payload, in_payload); } #[test] fn udp_builder_eth_double_vlan_ipv6_udp() { //generate let in_payload = [50,51,52,53]; let mut serialized = Vec::new(); PacketBuilder::ethernet2([1,2,3,4,5,6], [7,8,9,10,11,12]) .double_vlan(0x123, 0x234) .ipv6([11,12,13,14,15,16,17,18,19,10,21,22,23,24,25,26], [31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46], 47, ) .udp(48,49) .write(&mut serialized, &in_payload) .unwrap(); //check the deserialized size assert_eq!(Ethernet2Header::SERIALIZED_SIZE + DoubleVlanHeader::SERIALIZED_SIZE + Ipv6Header::SERIALIZED_SIZE + UdpHeader::SERIALIZED_SIZE + in_payload.len(), serialized.len()); //deserialize and check that everything is as expected use std::io::Cursor; use std::io::Read; //deserialize each part of the message and check it let mut cursor = Cursor::new(&serialized); //ethernet 2 header assert_eq!(Ethernet2Header::read(&mut cursor).unwrap(), Ethernet2Header{ source: [1,2,3,4,5,6], destination: [7,8,9,10,11,12], ether_type: EtherType::ProviderBridging as u16 }); //outer vlan header assert_eq!(SingleVlanHeader::read(&mut cursor).unwrap(), SingleVlanHeader{ priority_code_point: 0, drop_eligible_indicator: false, vlan_identifier: 0x123, ether_type: EtherType::VlanTaggedFrame as u16 }); //inner vlan header assert_eq!(SingleVlanHeader::read(&mut cursor).unwrap(), SingleVlanHeader{ priority_code_point: 0, drop_eligible_indicator: false, vlan_identifier: 0x234, ether_type: EtherType::Ipv6 as u16 }); //ip header let ip_actual = Ipv6Header::read(&mut cursor).unwrap(); let ip_expected = Ipv6Header{ traffic_class: 0, flow_label: 0, payload_length: (UdpHeader::SERIALIZED_SIZE + in_payload.len()) as u16, next_header: ip_number::UDP, hop_limit: 47, source: [11,12,13,14,15,16,17,18,19,10,21,22,23,24,25,26], destination: [31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46] }; assert_eq!(ip_actual, ip_expected); //udp header let udp_actual = UdpHeader::read(&mut cursor).unwrap(); let udp_expected = UdpHeader::with_ipv6_checksum(48, 49, &ip_expected, &in_payload).unwrap(); assert_eq!(udp_actual, udp_expected); //payload let mut actual_payload: [u8;4] = [0;4]; cursor.read_exact(&mut actual_payload).unwrap(); assert_eq!(actual_payload, in_payload); } #[test] fn udp_builder_eth_ip_udp() { //generate let in_payload = [50,51,52,53]; let mut serialized = Vec::new(); PacketBuilder::ethernet2([1,2,3,4,5,6], [7,8,9,10,11,12]) .ip(IpHeader::Version6(Ipv6Header{ traffic_class: 1, flow_label: 2, payload_length: (UdpHeader::SERIALIZED_SIZE + in_payload.len()) as u16, next_header: ip_number::UDP, hop_limit: 47, source: [11,12,13,14,15,16,17,18,19,10,21,22,23,24,25,26], destination: [31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46] }, Default::default())) .udp(48,49) .write(&mut serialized, &in_payload) .unwrap(); //check the deserialized size assert_eq!(Ethernet2Header::SERIALIZED_SIZE + Ipv6Header::SERIALIZED_SIZE + UdpHeader::SERIALIZED_SIZE + in_payload.len(), serialized.len()); //deserialize and check that everything is as expected use std::io::Cursor; use std::io::Read; //deserialize each part of the message and check it let mut cursor = Cursor::new(&serialized); //ethernet 2 header assert_eq!(Ethernet2Header::read(&mut cursor).unwrap(), Ethernet2Header{ source: [1,2,3,4,5,6], destination: [7,8,9,10,11,12], ether_type: EtherType::Ipv6 as u16 }); //ip header let ip_actual = Ipv6Header::read(&mut cursor).unwrap(); let ip_expected = Ipv6Header{ traffic_class: 1, flow_label: 2, payload_length: (UdpHeader::SERIALIZED_SIZE + in_payload.len()) as u16, next_header: ip_number::UDP, hop_limit: 47, source: [11,12,13,14,15,16,17,18,19,10,21,22,23,24,25,26], destination: [31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46] }; assert_eq!(ip_actual, ip_expected); //udp header let udp_actual = UdpHeader::read(&mut cursor).unwrap(); let udp_expected = UdpHeader::with_ipv6_checksum(48, 49, &ip_expected, &in_payload).unwrap(); assert_eq!(udp_actual, udp_expected); //payload let mut actual_payload: [u8;4] = [0;4]; cursor.read_exact(&mut actual_payload).unwrap(); assert_eq!(actual_payload, in_payload); } #[test] fn udp_builder_eth_vlan_ip_udp() { //generate let in_payload = [50,51,52,53]; let mut serialized = Vec::new(); PacketBuilder::ethernet2([1,2,3,4,5,6], [7,8,9,10,11,12]) .vlan(VlanHeader::Single(SingleVlanHeader{ priority_code_point: 1, drop_eligible_indicator: true, vlan_identifier: 0x123, ether_type: 0 //should be overwritten })) .ip(IpHeader::Version6(Ipv6Header{ traffic_class: 1, flow_label: 2, payload_length: (UdpHeader::SERIALIZED_SIZE + in_payload.len()) as u16, next_header: ip_number::UDP, hop_limit: 47, source: [11,12,13,14,15,16,17,18,19,10,21,22,23,24,25,26], destination: [31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46] }, Default::default())) .udp(48,49) .write(&mut serialized, &in_payload) .unwrap(); //check the deserialized size assert_eq!(Ethernet2Header::SERIALIZED_SIZE + SingleVlanHeader::SERIALIZED_SIZE + Ipv6Header::SERIALIZED_SIZE + UdpHeader::SERIALIZED_SIZE + in_payload.len(), serialized.len()); //deserialize and check that everything is as expected use std::io::Cursor; use std::io::Read; //deserialize each part of the message and check it let mut cursor = Cursor::new(&serialized); //ethernet 2 header assert_eq!(Ethernet2Header::read(&mut cursor).unwrap(), Ethernet2Header{ source: [1,2,3,4,5,6], destination: [7,8,9,10,11,12], ether_type: EtherType::VlanTaggedFrame as u16 }); //outer vlan header assert_eq!(SingleVlanHeader::read(&mut cursor).unwrap(), SingleVlanHeader{ priority_code_point: 1, drop_eligible_indicator: true, vlan_identifier: 0x123, ether_type: EtherType::Ipv6 as u16 }); //ip header let ip_actual = Ipv6Header::read(&mut cursor).unwrap(); let ip_expected = Ipv6Header{ traffic_class: 1, flow_label: 2, payload_length: (UdpHeader::SERIALIZED_SIZE + in_payload.len()) as u16, next_header: ip_number::UDP, hop_limit: 47, source: [11,12,13,14,15,16,17,18,19,10,21,22,23,24,25,26], destination: [31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46] }; assert_eq!(ip_actual, ip_expected); //udp header let udp_actual = UdpHeader::read(&mut cursor).unwrap(); let udp_expected = UdpHeader::with_ipv6_checksum(48, 49, &ip_expected, &in_payload).unwrap(); assert_eq!(udp_actual, udp_expected); //payload let mut actual_payload: [u8;4] = [0;4]; cursor.read_exact(&mut actual_payload).unwrap(); assert_eq!(actual_payload, in_payload); } proptest! { #[test] fn tcp_ipv4(ref input in tcp_any()) { //payload let in_payload = [24,25,26,27]; //ip v4 header let mut ip_expected = Ipv4Header::new( in_payload.len() as u16 + input.header_len(), 21, //ttl ip_number::TCP, [13,14,15,16], [17,18,19,20] ); ip_expected.header_checksum = ip_expected.calc_header_checksum().unwrap(); //generated the expected output let expected = { let mut expected = input.clone(); //replace urg & ack if the flags are not set if !expected.ack { expected.acknowledgment_number = 0; } if !expected.urg { expected.urgent_pointer = 0; } //calculate the checksum expected.checksum = expected.calc_checksum_ipv4(&ip_expected, &in_payload[..]).unwrap(); //done expected }; //generate let serialized = { //create builder let mut builder = PacketBuilder::ethernet2([1,2,3,4,5,6],[7,8,9,10,11,12]) .ipv4([13,14,15,16], [17,18,19,20], 21) .tcp(input.source_port, input.destination_port, input.sequence_number, input.window_size) .options_raw(input.options()).unwrap(); //set the flags if input.ns { builder = builder.ns(); } if input.fin { builder = builder.fin(); } if input.syn { builder = builder.syn(); } if input.rst { builder = builder.rst(); } if input.psh { builder = builder.psh(); } if input.ack { builder = builder.ack(input.acknowledgment_number); } if input.urg { builder = builder.urg(input.urgent_pointer); } if input.ece { builder = builder.ece(); } if input.cwr { builder = builder.cwr(); } let mut serialized = Vec::new(); builder.write(&mut serialized, &in_payload).unwrap(); serialized }; //deserialize and check that everything is as expected use std::io::Cursor; use std::io::Read; //deserialize each part of the message and check it let mut cursor = Cursor::new(&serialized); //ethernet 2 header assert_eq!(Ethernet2Header::read(&mut cursor).unwrap(), Ethernet2Header{ source: [1,2,3,4,5,6], destination: [7,8,9,10,11,12], ether_type: ether_type::IPV4 }); //ip header let ip_actual = Ipv4Header::read(&mut cursor).unwrap(); assert_eq!(ip_actual, ip_expected); //tcp header assert_eq!(TcpHeader::read(&mut cursor).unwrap(), expected); //payload let mut actual_payload: [u8;4] = [0;4]; cursor.read_exact(&mut actual_payload).unwrap(); assert_eq!(actual_payload, in_payload); } } proptest! { #[test] fn tcp_ipv6(ref input in tcp_any()) { //payload let in_payload = [24,25,26,27]; //ip v4 header let ip_expected = Ipv6Header{ traffic_class: 0, flow_label: 0, payload_length: (input.header_len() as usize + in_payload.len()) as u16, next_header: ip_number::TCP, hop_limit: 47, source: [11,12,13,14,15,16,17,18,19,10,21,22,23,24,25,26], destination: [31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46] }; //generated the expected output let expected = { let mut expected = input.clone(); //replace urg & ack if the flags are not set if !expected.ack { expected.acknowledgment_number = 0; } if !expected.urg { expected.urgent_pointer = 0; } //calculate the checksum expected.checksum = expected.calc_checksum_ipv6(&ip_expected, &in_payload[..]).unwrap(); //done expected }; //generate let serialized = { //create builder let mut builder = PacketBuilder::ethernet2([1,2,3,4,5,6],[7,8,9,10,11,12]) .ipv6([11,12,13,14,15,16,17,18,19,10,21,22,23,24,25,26], [31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46], 47, ) .tcp(input.source_port, input.destination_port, input.sequence_number, input.window_size) .options_raw(input.options()).unwrap(); //set the flags if input.ns { builder = builder.ns(); } if input.fin { builder = builder.fin(); } if input.syn { builder = builder.syn(); } if input.rst { builder = builder.rst(); } if input.psh { builder = builder.psh(); } if input.ack { builder = builder.ack(input.acknowledgment_number); } if input.urg { builder = builder.urg(input.urgent_pointer); } if input.ece { builder = builder.ece(); } if input.cwr { builder = builder.cwr(); } let mut serialized = Vec::new(); builder.write(&mut serialized, &in_payload).unwrap(); serialized }; //deserialize and check that everything is as expected use std::io::Cursor; use std::io::Read; //deserialize each part of the message and check it let mut cursor = Cursor::new(&serialized); //ethernet 2 header assert_eq!(Ethernet2Header::read(&mut cursor).unwrap(), Ethernet2Header{ source: [1,2,3,4,5,6], destination: [7,8,9,10,11,12], ether_type: EtherType::Ipv6 as u16 }); //ip header let ip_actual = Ipv6Header::read(&mut cursor).unwrap(); assert_eq!(ip_actual, ip_expected); //tcp header assert_eq!(TcpHeader::read(&mut cursor).unwrap(), expected); //payload let mut actual_payload: [u8;4] = [0;4]; cursor.read_exact(&mut actual_payload).unwrap(); assert_eq!(actual_payload, in_payload); } } #[test] fn tcp_options() { let mut serialized = Vec::new(); use crate::TcpOptionElement::*; let options = vec![MaximumSegmentSize(1234), Noop]; PacketBuilder::ethernet2([1,2,3,4,5,6],[7,8,9,10,11,12]) .ipv4([13,14,15,16], [17,18,19,20], 21) .tcp(1, 2, 3, 4) .options(&options).unwrap() .write(&mut serialized, &[]).unwrap(); let decoded = PacketHeaders::from_ethernet_slice(&serialized[..]).unwrap(); let dec_options: Vec> = decoded.transport.unwrap().tcp().unwrap().options_iterator().collect(); assert_eq!( &[Ok(MaximumSegmentSize(1234)), Ok(Noop)], &dec_options[..] ); } #[test] fn size() { //ipv4 no vlan assert_eq!(Ethernet2Header::SERIALIZED_SIZE + Ipv4Header::SERIALIZED_SIZE + UdpHeader::SERIALIZED_SIZE + 123, PacketBuilder::ethernet2([1,2,3,4,5,6],[7,8,9,10,11,12]) .ipv4([13,14,15,16], [17,18,19,20], 21) .udp(22,23) .size(123)); //ipv6 no vlan assert_eq!(Ethernet2Header::SERIALIZED_SIZE + Ipv6Header::SERIALIZED_SIZE + UdpHeader::SERIALIZED_SIZE + 123, PacketBuilder::ethernet2([1,2,3,4,5,6],[7,8,9,10,11,12]) .ipv6([11,12,13,14,15,16,17,18,19,10,21,22,23,24,25,26], [31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46], 47, ) .udp(22,23) .size(123)); //ipv4 single vlan assert_eq!(Ethernet2Header::SERIALIZED_SIZE + SingleVlanHeader::SERIALIZED_SIZE + Ipv4Header::SERIALIZED_SIZE + UdpHeader::SERIALIZED_SIZE + 123, PacketBuilder::ethernet2([1,2,3,4,5,6],[7,8,9,10,11,12]) .single_vlan(0x123) .ipv4([13,14,15,16], [17,18,19,20], 21) .udp(22,23) .size(123)); //ipv6 double vlan assert_eq!(Ethernet2Header::SERIALIZED_SIZE + DoubleVlanHeader::SERIALIZED_SIZE + Ipv6Header::SERIALIZED_SIZE + UdpHeader::SERIALIZED_SIZE + 123, PacketBuilder::ethernet2([1,2,3,4,5,6],[7,8,9,10,11,12]) .double_vlan(0x123, 0x234) .ipv6([11,12,13,14,15,16,17,18,19,10,21,22,23,24,25,26], [31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46], 47, ) .udp(22,23) .size(123)); } proptest! { #[test] fn size_tcp(ref input in tcp_any()) { assert_eq!(Ethernet2Header::SERIALIZED_SIZE + Ipv4Header::SERIALIZED_SIZE + input.header_len() as usize + 123, PacketBuilder::ethernet2([1,2,3,4,5,6],[7,8,9,10,11,12]) .ipv4([13,14,15,16], [17,18,19,20], 21) .tcp(input.source_port, input.destination_port, input.sequence_number, input.window_size) .options_raw(input.options()).unwrap() .size(123)); } } proptest! { #[test] fn ipv4_icmpv4( ipv4_source in any::<[u8;4]>(), ipv4_dest in any::<[u8;4]>(), ipv4_time_to_live in any::(), icmpv4_type_u8 in 15u8..u8::MAX, icmpv4_code_u8 in any::(), icmpv4_bytes5to8 in any::<[u8;4]>(), icmpv4 in icmpv4_type_any(), echo_id in any::(), echo_seq in any::(), payload in proptest::collection::vec(any::(), 0..64), ) { let test_builder = |builder: PacketBuilderStep, icmpv4_type: Icmpv4Type| { use etherparse::Icmpv4Type::*; let adapted_payload = match &icmpv4_type { TimestampRequest(_) | TimestampReply(_) => &[], _ => &payload[..], }; let icmp_expected = Icmpv4Header::with_checksum(icmpv4_type, &adapted_payload); let ip_expected = { let mut expected_ipv4 = Ipv4Header::new( (icmp_expected.header_len() + adapted_payload.len()) as u16, ipv4_time_to_live, ip_number::ICMP, ipv4_source, ipv4_dest ); expected_ipv4.header_checksum = expected_ipv4.calc_header_checksum().unwrap(); expected_ipv4 }; // test builder.size() assert_eq!( builder.size(adapted_payload.len()), Ethernet2Header::SERIALIZED_SIZE + Ipv4Header::SERIALIZED_SIZE + icmp_expected.header_len() + adapted_payload.len() ); // test builder.write() let mut buffer = Vec::::with_capacity(builder.size(adapted_payload.len())); builder.write(&mut buffer, adapted_payload).unwrap(); // decode packets let actual = PacketHeaders::from_ethernet_slice(&buffer).unwrap(); // check the packets could be decoded assert_eq!( Some(Ethernet2Header{ source: [1,2,3,4,5,6], destination: [7,8,9,10,11,12], ether_type: ether_type::IPV4 }), actual.link ); assert_eq!( Some(IpHeader::Version4(ip_expected, Default::default())), actual.ip ); assert_eq!( Some(TransportHeader::Icmpv4(icmp_expected)), actual.transport ); assert_eq!(actual.payload, adapted_payload); }; // icmpv4 { let builder = PacketBuilder::ethernet2([1,2,3,4,5,6],[7,8,9,10,11,12]) .ipv4(ipv4_source, ipv4_dest, ipv4_time_to_live) .icmpv4(icmpv4.clone()); test_builder( builder, icmpv4 ); } // icmpv4_raw { let builder = PacketBuilder::ethernet2([1,2,3,4,5,6],[7,8,9,10,11,12]) .ipv4(ipv4_source, ipv4_dest, ipv4_time_to_live) .icmpv4_raw(icmpv4_type_u8, icmpv4_code_u8, icmpv4_bytes5to8); test_builder( builder, Icmpv4Type::Unknown{ type_u8: icmpv4_type_u8, code_u8: icmpv4_code_u8, bytes5to8: icmpv4_bytes5to8, } ); } // icmpv4_echo_request { let builder = PacketBuilder::ethernet2([1,2,3,4,5,6],[7,8,9,10,11,12]) .ipv4(ipv4_source, ipv4_dest, ipv4_time_to_live) .icmpv4_echo_request(echo_id, echo_seq); test_builder( builder, Icmpv4Type::EchoRequest(IcmpEchoHeader{ id: echo_id, seq: echo_seq, }) ); } // icmp4_echo_reply { let builder = PacketBuilder::ethernet2([1,2,3,4,5,6],[7,8,9,10,11,12]) .ipv4(ipv4_source, ipv4_dest, ipv4_time_to_live) .icmpv4_echo_reply(echo_id, echo_seq); test_builder( builder, Icmpv4Type::EchoReply(IcmpEchoHeader{ id: echo_id, seq: echo_seq, }) ); } } } proptest! { #[test] fn ipv4_icmpv6( ipv4_source in any::<[u8;4]>(), ipv4_dest in any::<[u8;4]>(), ipv4_time_to_live in any::(), icmpv6_type_u8 in 162u8..u8::MAX, icmpv6_code_u8 in any::(), icmpv6_bytes5to8 in any::<[u8;4]>(), icmpv6 in icmpv6_type_any(), echo_id in any::(), echo_seq in any::(), payload in proptest::collection::vec(any::(), 0..64), ) { let test_builder = |builder: PacketBuilderStep, icmpv6_type: Icmpv6Type| { // test builder.size() assert_eq!( builder.size(payload.len()), Ethernet2Header::SERIALIZED_SIZE + Ipv4Header::SERIALIZED_SIZE + icmpv6_type.header_len() + payload.len() ); // test builder.write() let mut buffer = Vec::::with_capacity(builder.size(payload.len())); // should trigger an error, was it is not possible to calculate the checksum assert_matches!( builder.write(&mut buffer, &payload), Err(_) ); }; // icmpv6 { let builder = PacketBuilder::ethernet2([1,2,3,4,5,6],[7,8,9,10,11,12]) .ipv4(ipv4_source, ipv4_dest, ipv4_time_to_live) .icmpv6(icmpv6.clone()); test_builder( builder, icmpv6 ); } // icmpv6_raw { let builder = PacketBuilder::ethernet2([1,2,3,4,5,6],[7,8,9,10,11,12]) .ipv4(ipv4_source, ipv4_dest, ipv4_time_to_live) .icmpv6_raw(icmpv6_type_u8, icmpv6_code_u8, icmpv6_bytes5to8); test_builder( builder, Icmpv6Type::Unknown{ type_u8: icmpv6_type_u8, code_u8: icmpv6_code_u8, bytes5to8: icmpv6_bytes5to8, } ); } // icmpv6_echo_request { let builder = PacketBuilder::ethernet2([1,2,3,4,5,6],[7,8,9,10,11,12]) .ipv4(ipv4_source, ipv4_dest, ipv4_time_to_live) .icmpv6_echo_request(echo_id, echo_seq); test_builder( builder, Icmpv6Type::EchoRequest(IcmpEchoHeader{ id: echo_id, seq: echo_seq, }) ); } // icmp4_echo_reply { let builder = PacketBuilder::ethernet2([1,2,3,4,5,6],[7,8,9,10,11,12]) .ipv4(ipv4_source, ipv4_dest, ipv4_time_to_live) .icmpv6_echo_reply(echo_id, echo_seq); test_builder( builder, Icmpv6Type::EchoReply(IcmpEchoHeader{ id: echo_id, seq: echo_seq, }) ); } } } proptest! { #[test] fn ipv6_icmpv4( ipv6_source in any::<[u8;16]>(), ipv6_dest in any::<[u8;16]>(), ipv6_hop_limit in any::(), icmpv4_type_u8 in 15u8..u8::MAX, icmpv4_code_u8 in any::(), icmpv4_bytes5to8 in any::<[u8;4]>(), icmpv4 in icmpv4_type_any(), echo_id in any::(), echo_seq in any::(), payload in proptest::collection::vec(any::(), 0..64), ) { let test_builder = |builder: PacketBuilderStep, icmpv4_type: Icmpv4Type| { use Icmpv4Type::*; let adapted_payload = match icmpv4_type { TimestampRequest(_) | TimestampReply(_) => &[], _ => &payload[..], }; let icmp_expected = Icmpv4Header::with_checksum(icmpv4_type, &adapted_payload); let ip_expected = Ipv6Header{ traffic_class: 0, flow_label: 0, payload_length: (icmp_expected.header_len() + adapted_payload.len()) as u16, next_header: ip_number::ICMP, hop_limit: ipv6_hop_limit, source: ipv6_source, destination: ipv6_dest }; // test builder.size() assert_eq!( builder.size(adapted_payload.len()), Ethernet2Header::SERIALIZED_SIZE + Ipv6Header::SERIALIZED_SIZE + icmp_expected.header_len() + adapted_payload.len() ); // test builder.write() let mut buffer = Vec::::with_capacity(builder.size(adapted_payload.len())); builder.write(&mut buffer, adapted_payload).unwrap(); // decode packets let actual = PacketHeaders::from_ethernet_slice(&buffer).unwrap(); // check the packets could be decoded assert_eq!( Some(Ethernet2Header{ source: [1,2,3,4,5,6], destination: [7,8,9,10,11,12], ether_type: ether_type::IPV6 }), actual.link ); assert_eq!( Some(IpHeader::Version6(ip_expected, Default::default())), actual.ip ); assert_eq!( Some(TransportHeader::Icmpv4(icmp_expected)), actual.transport ); assert_eq!(actual.payload, adapted_payload); }; // icmpv4 { let builder = PacketBuilder::ethernet2([1,2,3,4,5,6],[7,8,9,10,11,12]) .ipv6(ipv6_source, ipv6_dest, ipv6_hop_limit) .icmpv4(icmpv4.clone()); test_builder( builder, icmpv4 ); } // icmpv4_raw { let builder = PacketBuilder::ethernet2([1,2,3,4,5,6],[7,8,9,10,11,12]) .ipv6(ipv6_source, ipv6_dest, ipv6_hop_limit) .icmpv4_raw(icmpv4_type_u8, icmpv4_code_u8, icmpv4_bytes5to8); test_builder( builder, Icmpv4Type::Unknown{ type_u8: icmpv4_type_u8, code_u8: icmpv4_code_u8, bytes5to8: icmpv4_bytes5to8, } ); } // icmpv4_echo_request { let builder = PacketBuilder::ethernet2([1,2,3,4,5,6],[7,8,9,10,11,12]) .ipv6(ipv6_source, ipv6_dest, ipv6_hop_limit) .icmpv4_echo_request(echo_id, echo_seq); test_builder( builder, Icmpv4Type::EchoRequest(IcmpEchoHeader{ id: echo_id, seq: echo_seq, }) ); } // icmp4_echo_reply { let builder = PacketBuilder::ethernet2([1,2,3,4,5,6],[7,8,9,10,11,12]) .ipv6(ipv6_source, ipv6_dest, ipv6_hop_limit) .icmpv4_echo_reply(echo_id, echo_seq); test_builder( builder, Icmpv4Type::EchoReply(IcmpEchoHeader{ id: echo_id, seq: echo_seq, }) ); } } } proptest! { #[test] fn ipv6_icmpv6( ipv6_source in any::<[u8;16]>(), ipv6_dest in any::<[u8;16]>(), ipv6_hop_limit in any::(), icmpv6_type_u8 in 162u8..u8::MAX, icmpv6_code_u8 in any::(), icmpv6_bytes5to8 in any::<[u8;4]>(), icmpv6 in icmpv6_type_any(), echo_id in any::(), echo_seq in any::(), payload in proptest::collection::vec(any::(), 0..64), ) { let test_builder = |builder: PacketBuilderStep, icmpv6_type: Icmpv6Type| { let icmp_expected = Icmpv6Header::with_checksum( icmpv6_type, ipv6_source, ipv6_dest, &payload ).unwrap(); let ip_expected = Ipv6Header{ traffic_class: 0, flow_label: 0, payload_length: (icmp_expected.header_len() + payload.len()) as u16, next_header: ip_number::IPV6_ICMP, hop_limit: ipv6_hop_limit, source: ipv6_source, destination: ipv6_dest }; // test builder.size() assert_eq!( builder.size(payload.len()), Ethernet2Header::SERIALIZED_SIZE + Ipv6Header::SERIALIZED_SIZE + icmp_expected.header_len() + payload.len() ); // test builder.write() let mut buffer = Vec::::with_capacity(builder.size(payload.len())); builder.write(&mut buffer, &payload).unwrap(); // decode packets let actual = PacketHeaders::from_ethernet_slice(&buffer).unwrap(); // check the packets could be decoded assert_eq!( Some(Ethernet2Header{ source: [1,2,3,4,5,6], destination: [7,8,9,10,11,12], ether_type: ether_type::IPV6 }), actual.link ); assert_eq!( Some(IpHeader::Version6(ip_expected, Default::default())), actual.ip ); assert_eq!( Some(TransportHeader::Icmpv6(icmp_expected)), actual.transport ); assert_eq!(actual.payload, &payload); }; // icmpv6 { let builder = PacketBuilder::ethernet2([1,2,3,4,5,6],[7,8,9,10,11,12]) .ipv6(ipv6_source, ipv6_dest, ipv6_hop_limit) .icmpv6(icmpv6.clone()); test_builder( builder, icmpv6 ); } // icmpv6_raw { let builder = PacketBuilder::ethernet2([1,2,3,4,5,6],[7,8,9,10,11,12]) .ipv6(ipv6_source, ipv6_dest, ipv6_hop_limit) .icmpv6_raw(icmpv6_type_u8, icmpv6_code_u8, icmpv6_bytes5to8); test_builder( builder, Icmpv6Type::Unknown{ type_u8: icmpv6_type_u8, code_u8: icmpv6_code_u8, bytes5to8: icmpv6_bytes5to8, } ); } // icmpv6_echo_request { let builder = PacketBuilder::ethernet2([1,2,3,4,5,6],[7,8,9,10,11,12]) .ipv6(ipv6_source, ipv6_dest, ipv6_hop_limit) .icmpv6_echo_request(echo_id, echo_seq); test_builder( builder, Icmpv6Type::EchoRequest(IcmpEchoHeader{ id: echo_id, seq: echo_seq, }) ); } // icmp4_echo_reply { let builder = PacketBuilder::ethernet2([1,2,3,4,5,6],[7,8,9,10,11,12]) .ipv6(ipv6_source, ipv6_dest, ipv6_hop_limit) .icmpv6_echo_reply(echo_id, echo_seq); test_builder( builder, Icmpv6Type::EchoReply(IcmpEchoHeader{ id: echo_id, seq: echo_seq, }) ); } } } etherparse-0.13.0/tests/packet_compositions/mod.proptest-regressions000064400000000000000000000432651046102023000242220ustar 00000000000000# Seeds for failure cases proptest has generated in the past. It is # automatically read and these particular cases re-run before any # novel cases are generated. # # It is recommended to check this file in to source control so that # everyone who runs the test benefits from these saved cases. cc 93464c2fb682bf96a32f9800d3932df8611a278bf6c993dc3ad6301d17795715 # shrinks to ref eth = Ethernet2Header { source: [0, 0, 0, 0, 0, 0], destination: [0, 0, 0, 0, 0, 0], ether_type: 0 }, ref vlan_outer = SingleVlanHeader { priority_code_point: 0, drop_eligible_indicator: false, vlan_identifier: 0, ether_type: 0 }, ref vlan_inner = SingleVlanHeader { priority_code_point: 0, drop_eligible_indicator: false, vlan_identifier: 0, ether_type: 0 }, ref ipv4 = Ipv4Header { ihl: 7, differentiated_services_code_point: 0, explicit_congestion_notification: 0, payload_len: 0, identification: 0, dont_fragment: false, more_fragments: false, fragments_offset: 0, time_to_live: 0, protocol: 4, header_checksum: 0, source: [0, 0, 0, 0], destination: [0, 0, 0, 0], options: [0, 0, 0, 0, 0, 0, 0, 0] }, ref ipv4_exts = Ipv4Extensions { auth: None }, ref ipv6 = Ipv6Header { traffic_class: 213, flow_label: 798389, payload_length: 24896, next_header: 187, hop_limit: 229, source: [14, 32, 160, 168, 37, 154, 115, 40, 38, 87, 212, 112, 188, 142, 254, 197], destination: [6, 159, 253, 179, 126, 197, 144, 208, 190, 191, 89, 166, 208, 140, 54, 50] }, ref ipv6_exts = Ipv6Extensions { hop_by_hop_options: None, destination_options: None, routing: None, fragment: Some(Ipv6FragmentHeader { next_header: 156, fragment_offset: 2564, more_fragments: false, identification: 3123850911 }), auth: None }, ref udp = UdpHeader { source_port: 45157, destination_port: 34201, length: 57104, checksum: 21037 }, ref tcp = TcpHeader { source_port: 51159, destination_port: 19610, sequence_number: 3703908533, acknowledgment_number: 8047906, data_offset: 13, ns: true, fin: false, syn: false, rst: false, psh: false, ack: false, urg: true, ece: false, cwr: true, window_size: 3326, checksum: 50866, urgent_pointer: 1068, options: [Err(UnknownId(34))] }, ref icmpv4 = Icmpv4Header { icmp_type: TimestampReply(TimestampMessage { id: 54195, seq: 33654, originate_timestamp: 2593543617, receive_timestamp: 534962444, transmit_timestamp: 141913819 }), checksum: 50019 }, ref icmpv6 = Icmpv6Header { icmp_type: Unknown { type_u8: 228, code_u8: 213, bytes5to8: [17, 44, 158, 162] }, checksum: 51305 }, ref payload = [176, 206, 197, 85, 12, 15, 112, 1, 92, 102, 232, 123, 66, 67, 0, 129, 111, 164, 134, 24, 82, 206, 103, 137, 239, 130, 78, 149, 131, 220, 160, 114, 222, 169, 165, 141, 202, 80, 8, 234, 94, 151, 21, 242, 120, 93, 230, 85, 162, 209, 105, 154, 72, 203, 198, 235, 64, 239, 33, 102, 54, 45, 201, 245, 26, 192, 182, 10, 232, 131, 82, 9, 32, 183, 65, 225, 132, 208, 61, 251, 109, 66, 234, 46, 65, 240, 148, 46, 146, 56, 17, 205, 103, 253, 158, 32, 21, 148, 243, 191, 23, 135, 145, 188, 136, 139, 125, 99, 144, 34, 142, 229, 128, 46, 226, 88, 205, 126, 2, 39, 87, 16, 74, 20, 184, 165, 75, 34, 0, 206, 61, 220, 196, 39, 190, 113, 217, 4, 238, 26, 232, 52, 18, 123, 48, 196, 238, 75, 120, 241, 41, 229, 114, 161, 65, 143, 237, 251, 87, 156, 155, 210, 178, 43, 166, 184, 11, 9, 250, 221, 22, 72, 65, 160, 116, 60, 242, 239, 97, 249, 39, 207, 214, 47, 6, 120, 51, 165, 69, 122, 156, 142, 159, 27, 224, 171, 233, 105, 79, 49, 32, 118, 141, 227, 174, 207, 109, 135, 5, 13, 248, 235, 33, 113, 233, 53, 131, 52, 188, 52, 203, 12, 88, 54, 84, 21, 132, 41, 211, 30, 215, 46, 108, 126, 141, 13, 113, 21, 233, 111, 115, 109, 107, 246, 214, 65, 211, 186, 60, 224, 211, 214, 191, 65, 62, 169, 122, 246, 237, 107, 183, 160, 179, 144, 106, 63, 10, 0, 87, 75, 175, 228, 178, 219, 35, 227, 161, 214, 134, 106, 156, 244, 126, 186, 201, 199, 202, 30, 220, 163, 146, 208, 192, 179, 241, 219, 6, 43, 39, 21, 231, 16, 213, 192, 194, 82, 33, 121, 188, 56, 108, 79, 219, 183, 20, 18, 192, 42, 7, 109, 217, 25, 42, 170, 154, 206, 35, 131, 193, 187, 217, 185, 178, 196, 130, 25, 85, 228, 103, 112, 163, 53, 154, 65, 68, 219, 219, 163, 208, 44, 33, 90, 118, 133, 114, 43, 242, 58, 196, 246, 55, 223, 181, 14, 249, 35, 73, 179, 242, 211, 188, 156, 4, 213, 54, 205, 50, 83, 116, 13, 128, 133, 239, 122, 106, 98, 140, 171, 202, 8, 11, 51, 219, 68, 19, 114, 8, 229, 177, 199, 9, 228, 130, 194, 211, 59, 16, 145, 23, 163, 228, 186, 187, 24, 194, 93, 75, 44, 23, 192, 96, 226, 164, 242, 75, 135, 48, 118, 108, 49, 62, 63, 228, 71, 153, 134, 15, 192, 249, 103, 44, 211] cc 19938c0e61de8fbe9f8df17d1325091a1825e2b209a4adb8b21dcd28a0e0f558 # shrinks to ref eth = Ethernet2Header { source: [0, 0, 0, 0, 0, 0], destination: [0, 0, 0, 0, 0, 0], ether_type: 0 }, ref vlan_outer = SingleVlanHeader { priority_code_point: 0, drop_eligible_indicator: false, vlan_identifier: 0, ether_type: 0 }, ref vlan_inner = SingleVlanHeader { priority_code_point: 0, drop_eligible_indicator: false, vlan_identifier: 0, ether_type: 0 }, ref ipv4 = Ipv4Header { ihl: 8, differentiated_services_code_point: 0, explicit_congestion_notification: 0, payload_len: 34240, identification: 0, dont_fragment: false, more_fragments: false, fragments_offset: 0, time_to_live: 0, protocol: 95, header_checksum: 2458, source: [0, 0, 0, 0], destination: [0, 0, 0, 0], options: [80, 229, 92, 224, 82, 126, 48, 60, 105, 201, 96, 77] }, ref ipv4_exts = Ipv4Extensions { auth: None }, ref ipv6 = Ipv6Header { traffic_class: 129, flow_label: 787898, payload_length: 54827, next_header: 33, hop_limit: 254, source: [109, 7, 4, 79, 149, 61, 253, 73, 214, 117, 64, 10, 168, 230, 137, 73], destination: [44, 199, 106, 47, 71, 14, 18, 94, 107, 95, 41, 238, 83, 187, 218, 132] }, ref ipv6_exts = Ipv6Extensions { hop_by_hop_options: Some(Ipv6RawExtensionHeader { next_header: 60, payload: [112, 231, 1, 88, 255, 168, 119, 95, 144, 149, 61, 29, 235, 11, 182, 192, 83, 15, 201, 180, 189, 232, 85, 231, 220, 116, 192, 132, 43, 162, 23, 161, 129, 246, 28, 236, 164, 174, 67, 235, 121, 212, 9, 73, 30, 98, 190, 173, 122, 133, 58, 154, 142, 6, 24, 203, 3, 230, 232, 50, 77, 203, 83, 151, 3, 157, 193, 242, 25, 246, 224, 4, 178, 173, 156, 5, 210, 3, 97, 27, 171, 152, 187, 16, 98, 73, 57, 176, 35, 25, 246, 71, 154, 32, 132, 227, 164, 29, 92, 159, 74, 247, 144, 68, 39, 254, 227, 156, 63, 140, 246, 246, 199, 111, 101, 173, 179, 116, 79, 114, 249, 162, 71, 113, 121, 224, 229, 237, 67, 3, 4, 162, 152, 120, 58, 132, 244, 196, 136, 196, 206, 160, 45, 83, 167, 218, 32, 206, 52, 246, 144, 220, 133, 150, 36, 91, 193, 118, 28, 33, 236, 64, 255, 72, 190, 70, 160, 38, 139, 134, 80, 153, 236, 93, 198, 211, 21, 19, 251, 131, 119, 219, 161, 19, 144, 96, 6, 188, 115, 43, 91, 216, 5, 135, 101, 166, 99, 11, 174, 169, 255, 248, 101, 23, 62, 55, 169, 40, 6, 186, 195, 235, 76, 41] }), destination_options: Some(Ipv6RawExtensionHeader { next_header: 43, payload: [238, 203, 236, 202, 32, 25, 193, 164, 167, 189, 30, 208, 207, 108, 114, 10, 12, 226, 180, 59, 207, 44, 143, 244, 221, 200, 232, 154, 140, 180, 167, 70, 197, 72, 31, 249, 141, 75, 7, 255, 201, 53, 76, 234, 201, 187, 214, 141, 249, 216, 232, 12, 45, 196, 208, 110, 78, 14, 60, 251, 17, 239, 13, 141, 216, 29, 230, 120, 102, 88, 104, 237, 17, 252, 108, 126, 203, 75] }), routing: Some(Ipv6RoutingExtensions { routing: Ipv6RawExtensionHeader { next_header: 44, payload: [254, 77, 166, 70, 182, 207, 149, 153, 212, 40, 122, 249, 15, 84, 41, 126, 254, 103, 2, 162, 52, 216, 226, 175, 148, 253, 5, 153, 50, 16, 32, 44, 139, 24, 73, 245, 17, 9, 50, 18, 176, 70, 177, 29, 220, 255, 253, 255, 94, 39, 69, 225, 93, 176, 139, 48, 98, 210, 151, 80, 3, 105, 114, 59, 232, 171, 163, 235, 40, 56, 9, 85, 180, 225, 71, 230, 216, 128, 194, 109, 150, 198, 175, 68, 186, 112, 223, 48, 61, 245, 191, 34, 3, 207, 250, 27, 110, 21, 229, 221, 166, 76, 220, 214, 215, 104, 137, 46, 134, 94, 106, 89, 129, 218, 113, 234, 119, 79, 84, 147, 98, 202, 148, 239, 67, 99, 223, 222, 139, 13, 237, 170, 164, 89, 15, 185, 202, 252, 2, 156, 33, 28, 194, 52, 180, 232, 239, 202, 23, 123, 215, 81, 236, 65, 80, 192, 136, 184, 237, 135, 205, 183, 104, 66, 253, 128, 176, 245, 213, 65, 120, 202, 15, 130, 202, 55, 28, 94, 189, 8, 11, 59, 112, 96, 196, 186, 15, 96, 32, 60, 193, 8, 95, 44, 110, 224, 32, 71, 96, 140, 69, 124, 69, 241, 153, 87, 65, 15, 171, 113, 248, 239, 156, 78, 174, 47, 99, 190, 159, 163, 29, 197, 75, 161, 4, 209, 213, 236, 86, 120, 74, 15, 147, 85, 135, 147, 242, 220, 144, 55, 202, 170, 71, 90, 107, 103, 170, 8, 231, 169, 231, 170, 153, 184, 158, 99, 127, 228, 243, 191, 139, 69, 75, 133, 185, 212, 104, 214, 233, 171, 0, 135, 73, 14, 31, 2, 90, 187, 82, 205, 161, 69, 251, 143, 243, 15, 56, 250, 98, 175, 82, 196, 216, 95, 249, 127, 84, 181, 211, 50, 81, 36, 26, 247, 224, 3, 92, 61, 120, 67, 163, 170, 185, 61, 254, 91, 248, 20, 150, 19, 49, 71, 52, 102, 152, 209, 105, 219, 65, 151, 19, 101, 102, 133, 216, 94, 237, 221, 232, 168, 51, 28, 214, 231, 179, 180, 235, 17, 36, 19, 33, 54, 232, 131, 150, 95, 96, 84, 13, 6, 20, 28, 160, 92, 193, 206, 231, 10, 238, 240, 6, 77, 44, 78, 6, 253, 142, 54, 72, 135, 39, 144, 95, 132, 194, 5, 25, 225, 46, 143, 153, 93, 213, 32, 114, 214, 230, 61, 21, 189, 86, 34, 12, 85, 75, 242, 112, 3, 251, 4, 129, 141, 153, 47, 228, 157, 65, 13, 82, 38, 80, 34, 7, 52, 172, 210, 141, 83, 27, 39, 100, 16, 0, 216, 114, 134, 195, 220, 156, 79, 174, 220, 88, 252, 193, 210, 93, 190, 229, 6, 16, 63, 190, 46, 5, 126, 28, 10, 51, 102, 19, 8, 153, 157, 142, 125, 6, 40, 100, 68, 139, 231, 69, 159, 46, 98, 36, 25, 200, 140, 107, 101, 15, 70, 25, 89, 211, 3, 17, 253, 9, 50, 39, 60, 47, 185, 135, 17, 218, 116, 65, 107, 110, 122, 227, 202, 155, 71, 164, 119, 189, 84, 128, 8, 180, 93, 177, 45, 15, 198, 16, 79, 179, 46, 103, 85, 91, 229, 254, 12, 152, 129, 160, 104, 16, 217, 157, 157, 61, 137, 189, 194, 132, 234, 243, 123, 91, 70, 132, 5, 222, 200, 134, 26, 129, 182, 254, 254, 151, 165, 184, 13, 85, 106, 44, 20, 79, 183, 130, 223, 209, 88, 35, 174, 160, 91, 199, 118, 168, 40, 189, 181, 59, 38, 74, 43, 24, 80, 25, 224, 73, 119, 241, 101, 41, 109, 115, 24, 35, 204, 181, 100, 33, 78, 109, 253, 192, 21, 137, 4, 203, 143, 243, 152, 96, 237, 209, 26, 217, 68, 239, 59, 1, 200, 219, 177, 22, 196, 180, 1, 102, 202, 126, 216, 32, 221, 143, 99, 223, 7, 129, 183, 252, 35, 59, 15, 204, 56, 18, 118, 229, 215, 81, 147, 172, 69, 116, 46, 51, 169, 157, 22, 69, 178, 97, 224, 190, 198, 11, 216, 188, 108, 161, 120, 196, 181, 172, 21, 41, 124, 197, 106, 58, 193, 102, 16, 67, 127, 109, 45, 135, 60, 110, 30, 155, 88, 173, 34, 14, 78, 117, 93, 158, 51, 117, 168, 226, 43, 44, 173, 185, 20, 111, 151, 32, 95, 226, 103, 101, 76, 229, 117, 14, 56, 187, 185, 131, 185, 50, 68, 20, 173, 69, 94, 131, 252, 114, 133, 98, 55, 143, 45, 12, 25, 226, 189, 170, 73, 70, 163, 98, 27, 195, 211, 38, 108, 243, 46, 5, 140, 56, 85, 136, 98, 154, 22, 112, 91, 192, 81, 51, 252, 190, 222, 16, 151, 178, 51, 209, 208, 15, 72, 17, 127, 219, 117, 10, 93, 193, 133, 55, 125, 98, 95, 35, 63, 115, 88, 44, 80, 120, 10, 224, 207, 98, 243, 227, 236, 149, 9, 163, 166, 250, 134, 32, 144, 182, 144, 212, 237, 231, 157, 18, 39, 46, 116, 226, 106, 195, 193, 129, 171, 121, 5, 135, 72, 160, 170, 139, 83, 138, 70, 124, 115, 12, 219, 197, 250, 209, 205, 250, 55, 107, 37, 26, 107, 141, 164, 107, 93, 45, 26, 7, 240, 168, 25, 169, 241, 21, 22, 142, 216, 164, 17, 50, 214, 204, 32, 31, 184, 179, 11, 134, 255, 229, 160, 130, 167, 149, 190, 141, 191, 64, 247, 35, 182, 183, 9, 119, 116, 199, 43, 91, 48, 101, 117, 52, 145, 248, 62, 25, 82, 129, 253, 53, 206, 51, 195, 80, 45, 83, 239, 194, 4, 108, 177, 156, 196, 42, 215, 45, 2, 2, 251, 9, 122, 230, 239, 39, 83, 129, 88, 192, 181, 57, 235, 22, 25, 122, 54, 9, 242, 32, 96, 178, 29, 2, 9, 212, 157, 250, 227, 114, 138, 238, 202, 121, 90, 101, 42, 137, 159, 27, 112, 225, 206, 201, 104, 201, 177, 177, 26, 103, 227, 100, 190, 231, 117, 136, 230, 180, 121, 54, 60, 113, 26, 49, 140, 66, 76, 150, 183, 116, 193, 170, 130, 166, 214, 204, 212, 125, 75, 19, 17, 79, 245, 198, 176, 15, 17, 43, 92, 169, 227, 25, 11, 194, 245, 93, 126, 247, 254, 74, 148, 187, 231, 153, 196, 193, 177, 125, 67, 183, 79, 219, 77, 89, 233, 42, 45, 38, 232, 164, 146, 228, 179, 204, 107, 191, 254, 232, 61, 172, 148, 144, 56, 60, 178, 90, 211, 72, 255, 93, 3, 25, 220, 180, 82, 70, 85, 209, 97, 92, 7, 232, 204, 201, 202, 235, 31, 75, 60, 157, 149, 147, 168, 175, 138, 116, 118, 127, 123, 98, 115, 205, 37, 81, 74, 136, 150, 89, 83, 204, 201, 105, 154, 27, 1, 104, 193, 102, 17, 247, 204, 236, 134, 110, 165, 141, 123, 21, 229, 56, 215, 184, 3, 251, 7, 181, 246, 50, 133, 74, 50, 36, 224, 12, 171, 200, 245, 193, 110, 42, 93, 115, 215, 182, 128, 107, 175, 64, 170, 131, 206, 74, 124, 194, 150, 191, 102, 85, 139, 127, 117, 35, 239, 137, 225, 68, 108, 118, 250, 127, 250, 128, 167, 149, 240, 21, 238, 117, 98, 181, 186, 162, 83, 152, 255, 80, 111, 235, 55, 133, 209, 43, 118, 151, 148, 140, 253, 249, 178, 148, 174, 254, 236, 250, 172, 27, 220, 189, 20, 26, 201, 253, 187, 109, 55, 51, 26, 243, 44, 65, 59, 131, 116, 15, 52, 222, 174, 63, 49, 150, 113, 71, 98, 228, 48, 27, 236, 183, 240, 184, 87, 21, 146, 248, 224, 54, 46, 81, 109, 129, 243, 104, 48, 239, 36, 8, 232, 9, 229, 82, 164, 3, 186, 86, 202, 128, 224, 218, 19, 161, 92, 187, 55, 41, 203, 143, 139, 54, 50, 120, 253, 62, 26, 232, 113, 97, 136, 6, 53, 89, 90, 200, 202, 246, 102, 193, 14, 244, 179, 226, 253, 205, 189, 236, 98, 51, 154, 217, 83, 254, 238, 229, 32, 197, 124, 71, 165, 235, 224, 67, 190, 207, 23, 232, 240, 34, 203, 137, 64, 93, 65, 240, 205, 71, 61, 36, 104, 99, 125, 94, 9, 255, 131, 204, 210, 17, 210, 205, 112, 188, 146, 246, 237, 76, 128, 24, 198, 43, 184, 72, 22, 77, 196, 8, 77, 138, 105, 155, 165, 215, 253, 162, 248, 172, 95, 79, 102, 199, 90, 251, 122, 74, 24, 69, 65, 112, 172, 227, 140, 202, 104, 235, 119, 220, 80, 78, 234, 21, 129, 138, 250, 188, 87, 131, 20, 185, 76, 24, 103, 231, 145, 48, 207, 167, 230, 18, 30, 80, 190, 139, 36, 22, 165, 21, 176, 240, 227, 82, 246, 112, 184, 21, 226, 116, 175, 147, 250, 109, 236, 83, 52, 112, 156, 180, 111, 220, 43, 77, 112, 98, 193, 125, 145, 31, 38, 115, 213, 67, 95, 62, 81, 208, 123, 8, 158, 157, 171, 133, 246, 210, 56, 169, 221, 27, 153, 121, 210, 134, 24, 202, 90, 183, 78, 229, 99, 153, 245, 135, 122, 55, 158, 129, 216, 147, 80, 150, 203, 182, 220, 9, 95, 65, 222, 120, 144, 133, 148, 45, 134, 7, 113, 74, 219, 238, 229, 1, 112, 173, 189, 232, 176, 219, 14, 143, 14, 134, 108, 209, 218, 59, 252, 192, 185, 255, 142, 96, 87, 1, 77, 243, 219, 46, 78, 253, 128, 249, 182, 149, 144, 174, 176, 198, 64, 3, 200, 129, 217, 102, 131, 119, 102, 74, 10, 212, 86, 143, 165, 108, 235, 36, 100, 18, 3, 241, 8, 113, 92, 201, 114, 216, 97, 120, 199, 196, 172, 29, 179, 205, 252, 163, 199, 187, 139, 42, 103, 99, 51, 51, 8, 205, 180, 149, 177, 245, 77, 111, 26, 246, 112, 174, 236, 221, 168, 72, 137, 38, 59, 10, 89, 6, 68, 66, 158, 17, 246, 149, 239, 165, 221, 28, 144, 252, 247, 102, 194, 215, 90, 15, 206, 93, 133, 197, 15, 81, 155, 143, 200, 201, 112, 105, 60, 84, 52, 179, 179, 18, 67, 178, 126, 113, 15, 45, 26, 159, 223, 161, 249, 141, 31, 179, 43, 94, 8, 125, 194, 219, 26, 65, 57, 166, 236, 185, 24, 63, 206, 215, 22, 85, 117, 41, 197, 182, 147, 46, 202, 167, 206, 154, 89, 200, 95, 238, 93, 125, 4, 101, 195, 253, 179, 29, 13, 234, 225, 171, 72, 82, 224, 60, 191, 74, 113, 217, 161, 10, 13, 202, 196, 144, 104, 46, 71, 49, 212, 22, 181, 250, 28, 27, 95, 151, 158, 25, 84, 226, 200] }, final_destination_options: None }), fragment: Some(Ipv6FragmentHeader { next_header: 109, fragment_offset: 2113, more_fragments: true, identification: 5944605 }), auth: None }, ref udp = UdpHeader { source_port: 27523, destination_port: 52161, length: 45869, checksum: 14910 }, ref tcp = TcpHeader { source_port: 17245, destination_port: 46697, sequence_number: 160328470, acknowledgment_number: 2631620014, data_offset: 10, ns: false, fin: false, syn: false, rst: true, psh: false, ack: true, urg: false, ece: true, cwr: false, window_size: 24158, checksum: 53442, urgent_pointer: 8968, options: [Err(UnknownId(173))] }, ref icmpv4 = Icmpv4Header { icmp_type: Unknown { type_u8: 234, code_u8: 221, bytes5to8: [200, 89, 56, 131] }, checksum: 16430 }, ref icmpv6 = Icmpv6Header { icmp_type: Unknown { type_u8: 30, code_u8: 106, bytes5to8: [52, 110, 228, 155] }, checksum: 38251 }, ref payload = [111, 188, 151, 183, 149, 185, 18, 245, 219, 34, 101, 100, 224, 105, 138, 24, 34, 92, 6, 75, 219, 201, 60, 187, 214, 136, 150, 248, 6, 50, 64, 136, 89, 13, 42, 46, 93, 80, 5, 22, 114, 77, 34, 58, 115, 121, 159, 158, 151, 132, 171, 188, 57, 49, 52, 166, 160, 191, 60, 116, 6, 117, 215, 53, 99, 85, 33, 16, 109, 90, 48, 192, 31, 77, 71, 43, 229, 66, 22, 199, 176, 216, 156, 180, 197, 105, 72, 60, 198, 61, 119, 201, 118, 240, 131, 5, 102, 75, 200, 84, 254, 216, 228, 209, 150, 251, 234, 232, 20, 243, 127, 121, 97, 68, 16, 43, 140, 15, 235, 75, 178, 41, 209, 114, 244, 16, 163, 224, 223, 132, 128, 56, 142, 160, 184, 140, 89, 35, 167, 84, 217, 209, 200, 3, 120, 124, 220, 113, 169, 39, 64, 82, 255, 81, 239, 172, 199, 48, 179, 102, 109, 53, 167, 253, 203, 114, 225, 103, 233, 1, 72, 29, 178, 90, 44, 246, 248, 43, 137, 46, 5, 250, 25, 94, 155, 183, 46, 229, 121, 120, 16, 105, 40, 15, 168, 29, 93, 71, 42, 36, 179, 253, 67, 132, 81, 196, 190, 165, 130, 54, 57, 212, 240, 76, 252, 175, 147, 200, 18, 179, 196, 82, 9, 135, 197, 217, 12, 60, 130, 144, 129, 206, 133, 122, 183, 87, 194, 149, 79, 206, 67, 178, 51, 38, 60, 143, 132, 9, 221, 193, 27, 31, 145, 245, 137, 134, 248, 231, 68, 211, 125, 22, 234, 78, 231, 119, 27, 241, 143, 43, 173, 231, 117, 180, 255, 230, 138, 68, 233, 225, 184, 16, 132, 168, 65, 84, 177, 210, 183, 55, 188, 216, 82, 7, 137, 1, 81, 69, 14, 104, 82, 239, 73, 218, 70, 196, 163, 59, 183, 151, 95, 197, 81, 49, 97, 162, 96, 9, 95, 254, 137, 252, 100, 190, 218, 124, 130, 82, 32, 154, 253, 44, 253, 58, 149, 116, 45, 82, 104, 103, 119, 42, 175, 208, 203, 25, 65, 154, 218, 222, 22, 148, 94, 5, 226, 217, 158, 148, 30, 84, 36, 142, 214, 166, 176, 62, 198, 178, 94, 205, 220, 155, 5, 86, 48, 167, 114, 108, 210, 127, 105, 247, 106, 30, 77, 100, 149, 109, 139, 60, 174, 121, 24, 203, 35, 163, 15, 212, 151, 206, 94, 134, 28, 253, 192, 66, 12, 167, 45, 146, 101] etherparse-0.13.0/tests/packet_compositions/mod.rs000064400000000000000000000525051046102023000204220ustar 00000000000000use super::*; #[derive(Clone, Debug, Eq, PartialEq)] struct ComponentTest { link: Option, vlan: Option, ip: Option, transport: Option, payload: Vec } static VLAN_ETHER_TYPES: &'static [u16] = &[ EtherType::VlanTaggedFrame as u16, EtherType::ProviderBridging as u16, EtherType::VlanDoubleTaggedFrame as u16 ]; impl ComponentTest { fn serialize(&self) -> Vec { let mut buffer = Vec::::with_capacity( match &self.link { Some(header) => header.header_len(), None => 0, } + match &self.vlan { Some(header) => header.header_len(), None => 0, } + match &self.ip { Some(headers) => headers.header_len(), None => 0, } + match &self.transport { Some(header) => header.header_len(), None => 0, } + self.payload.len() ); //fill all the elements match &self.link { Some(header) => header.write(&mut buffer).unwrap(), None => {}, } use crate::VlanHeader::*; match &self.vlan { Some(Single(header)) => header.write(&mut buffer).unwrap(), Some(Double(header)) => header.write(&mut buffer).unwrap(), None => {}, } match &self.ip { Some(IpHeader::Version4(header, exts)) => { header.write_raw(&mut buffer).unwrap(); exts.write(&mut buffer, header.protocol).unwrap(); }, Some(IpHeader::Version6(header, exts)) => { header.write(&mut buffer).unwrap(); exts.write(&mut buffer, header.next_header).unwrap(); }, None => {}, } match &self.transport { Some(TransportHeader::Icmpv6(header)) => header.write(&mut buffer).unwrap(), Some(TransportHeader::Icmpv4(header)) => header.write(&mut buffer).unwrap(), Some(TransportHeader::Udp(header)) => header.write(&mut buffer).unwrap(), Some(TransportHeader::Tcp(header)) => header.write(&mut buffer).unwrap(), None => {} } use std::io::Write; buffer.write(&self.payload[..]).unwrap(); buffer } /// Serialize the headers & payload specified in the headers and check that /// the different decoding & slicing methods for entire packets work correctly. /// /// The following functions will be checked if they work correctly: /// * `SlicedPacket::from_ethernet` /// * `SlicedPacket::from_ip` /// * `PacketHeaders::from_ethernet_slice` /// * `PacketHeaders::from_ip_slice` fn run(&self) { //packet with ethernet2 & vlan headers { //serialize to buffer let buffer = self.serialize(); // PacketHeaders::from_ethernet_slice self.assert_headers( PacketHeaders::from_ethernet_slice(&buffer).unwrap() ); // SlicedPacket::from_ethernet self.assert_sliced_packet( SlicedPacket::from_ethernet(&buffer).unwrap() ); // create unexpected end of slice errors for the different headers for len in self.invalid_ser_lengths() { if let Some(len) = len { assert_matches!( PacketHeaders::from_ethernet_slice(&buffer[..len]), Err(_) ); assert_matches!( SlicedPacket::from_ethernet(&buffer[..len]), Err(_) ); } } } // packet data starting right after the link layer (tests from_ether_type functions) { // remove the link layer let ether_down = { let mut ether_down = self.clone(); ether_down.link = None; ether_down }; // serialize to buffer let buffer = ether_down.serialize(); // PacketHeaders::from_ether_type ether_down.assert_headers( PacketHeaders::from_ether_type( self.link.as_ref().unwrap().ether_type, &buffer[..] ).unwrap() ); // SlicedPacket::from_ether_type ether_down.assert_sliced_packet( SlicedPacket::from_ether_type( self.link.as_ref().unwrap().ether_type, &buffer[..] ).unwrap() ); // create unexpected end of slice errors for the different headers for len in ether_down.invalid_ser_lengths() { if let Some(len) = len { assert_matches!( PacketHeaders::from_ether_type( self.link.as_ref().unwrap().ether_type, &buffer[..len] ), Err(_) ); assert_matches!( SlicedPacket::from_ether_type( self.link.as_ref().unwrap().ether_type, &buffer[..len] ), Err(_) ); } } } // packet from the internet layer down (without ethernet2 & vlan headers) if self.ip.is_some() { // serialize from the ip layer downwards let ip_down = { let mut ip_down = self.clone(); ip_down.link = None; ip_down.vlan = None; ip_down }; // serialize to buffer let buffer = ip_down.serialize(); // PacketHeaders::from_ip_slice ip_down.assert_headers( PacketHeaders::from_ip_slice(&buffer[..]).unwrap() ); // SlicedPacket::from_ip ip_down.assert_sliced_packet( SlicedPacket::from_ip(&buffer).unwrap() ); // create unexpected end of slice errors for the different headers for len in ip_down.invalid_ser_lengths() { if let Some(len) = len { assert_matches!( PacketHeaders::from_ip_slice(&buffer[..len]), Err(_) ); assert_matches!( SlicedPacket::from_ip(&buffer[..len]), Err(_) ); } } } } /// Creates slice lengths at which an too short slice error /// should be triggered. fn invalid_ser_lengths(&self) -> [Option;12] { struct Builder { result: [Option;12], next_index : usize, offset : usize, } impl Builder { fn add(&mut self, header_len:usize) { self.offset += header_len; self.result[self.next_index] = Some(self.offset - 1); self.next_index += 1; } } let mut builder = Builder { result: [None;12], next_index : 0, offset : 0, }; if let Some(link) = self.link.as_ref() { builder.add(link.header_len()); } if let Some(vlan) = self.vlan.as_ref() { use VlanHeader::*; match vlan { Single(single) => builder.add(single.header_len()), Double(double) => { builder.add(double.outer.header_len()); builder.add(double.inner.header_len()); } } } if let Some(ip) = self.ip.as_ref() { use IpHeader::*; match ip { Version4(header, exts) => { builder.add(header.header_len()); if let Some(auth) = exts.auth.as_ref() { builder.add(auth.header_len()); } }, Version6(header, exts) => { builder.add(header.header_len()); if let Some(e) = exts.hop_by_hop_options.as_ref() { builder.add(e.header_len()); } if let Some(e) = exts.destination_options.as_ref() { builder.add(e.header_len()); } if let Some(routing) = exts.routing.as_ref() { builder.add(routing.routing.header_len()); if let Some(e) = routing.final_destination_options.as_ref() { builder.add(e.header_len()); } } if let Some(e) = exts.fragment.as_ref() { builder.add(e.header_len()); } if let Some(e) = exts.auth.as_ref() { builder.add(e.header_len()); } }, } } if let Some(transport) = self.transport.as_ref() { builder.add(transport.header_len()); } builder.result } fn assert_headers(&self, actual: PacketHeaders) { assert_eq!(self.link, actual.link); assert_eq!(self.vlan, actual.vlan); assert_eq!(self.ip, self.ip); assert_eq!(self.transport, actual.transport); assert_eq!(self.payload[..], actual.payload[..]); } fn assert_sliced_packet(&self, result: SlicedPacket) { //assert identity to touch the derives (code coverage hack) assert_eq!(result, result); //ethernet & vlan assert_eq!(self.link, result.link.map(|ref x| x.to_header())); assert_eq!(self.vlan, result.vlan.map(|ref x| x.to_header())); //ip assert_eq!(self.ip, { use crate::InternetSlice::*; use self::IpHeader::*; match result.ip { Some(Ipv4(actual_header, actual_extensions)) => Some( Version4( actual_header.to_header(), Ipv4Extensions{ auth: actual_extensions.auth.map(|ref x| x.to_header()) } ) ), Some(Ipv6(actual_header, actual_extensions)) => Some( Version6( actual_header.to_header(), Ipv6Extensions::from_slice( actual_header.next_header(), actual_extensions.slice() ).unwrap().0 ) ), None => None } } ); //transport assert_eq!( self.transport, match result.transport.as_ref() { Some(TransportSlice::Icmpv4(actual)) => Some(TransportHeader::Icmpv4(actual.header())), Some(TransportSlice::Icmpv6(actual)) => Some(TransportHeader::Icmpv6(actual.header())), Some(TransportSlice::Udp(actual)) => Some(TransportHeader::Udp(actual.to_header())), Some(TransportSlice::Tcp(actual)) => Some(TransportHeader::Tcp(actual.to_header())), Some(TransportSlice::Unknown(_)) => None, None => None } ); // additional check for the contents of Unknown if self.transport.is_none() { match result.transport.as_ref() { Some(TransportSlice::Unknown(ip_num)) => assert_eq!(*ip_num, self.ip.as_ref().unwrap().next_header().unwrap()), None => assert!(result.transport.is_none()), _ => unreachable!(), } } //payload match result.transport.as_ref() { // icmp slices contain the complete payload, the payload itself will be empty Some(TransportSlice::Icmpv4(icmpv4)) => { assert_eq!(&self.payload[..], icmpv4.payload()); assert_eq!(0, result.payload.len()); } Some(TransportSlice::Icmpv6(icmpv6)) => { assert_eq!(&self.payload[..], icmpv6.payload()); assert_eq!(0, result.payload.len()); } // for other cases _ => assert_eq!(&self.payload[..], &result.payload[..]) } } fn run_vlan( &self, outer_vlan: &SingleVlanHeader, inner_vlan: &SingleVlanHeader, ipv4: &Ipv4Header, ipv4_ext: &Ipv4Extensions, ipv6: &Ipv6Header, ipv6_ext: &Ipv6Extensions, udp: &UdpHeader, tcp: &TcpHeader, icmpv4: &Icmpv4Header, icmpv6: &Icmpv6Header, ) { let setup_single = | ether_type: u16| -> ComponentTest { let mut result = self.clone(); result.vlan = Some(VlanHeader::Single({ let mut v = inner_vlan.clone(); v.ether_type = ether_type; v })); result }; let setup_double = |outer_ether_type: u16, inner_ether_type: u16| -> ComponentTest { let mut result = self.clone(); result.vlan = Some(VlanHeader::Double(DoubleVlanHeader{ outer: { let mut v = outer_vlan.clone(); v.ether_type = outer_ether_type; v }, inner: { let mut v = inner_vlan.clone(); v.ether_type = inner_ether_type; v }})); result }; //single setup_single(inner_vlan.ether_type).run(); setup_single(ether_type::IPV4).run_ipv4(ipv4, ipv4_ext, udp, tcp, icmpv4, icmpv6); setup_single(ether_type::IPV6).run_ipv6(ipv6, ipv6_ext, udp, tcp, icmpv4, icmpv6); //double for ether_type in VLAN_ETHER_TYPES { setup_double(*ether_type, inner_vlan.ether_type).run(); setup_double(*ether_type, ether_type::IPV4).run_ipv4(ipv4, ipv4_ext, udp, tcp, icmpv4, icmpv6); setup_double(*ether_type, ether_type::IPV6).run_ipv6(ipv6, ipv6_ext, udp, tcp, icmpv4, icmpv6); } } fn run_ipv4( &self, ip: &Ipv4Header, ip_exts: &Ipv4Extensions, udp: &UdpHeader, tcp: &TcpHeader, icmpv4: &Icmpv4Header, icmpv6: &Icmpv6Header, ) { // fragmenting { let mut test = self.clone(); test.ip = Some({ let mut frag = ip.clone(); if false == frag.is_fragmenting_payload() { frag.more_fragments = true; } let mut header = IpHeader::Version4(frag, ip_exts.clone()); header.set_next_headers(ip.protocol); header }); // run without transport header test.run(); } // non fragmenting { let mut test = self.clone(); test.ip = Some({ let mut non_frag = ip.clone(); non_frag.more_fragments = false; non_frag.fragments_offset = 0; let mut header = IpHeader::Version4(non_frag, ip_exts.clone()); header.set_next_headers(ip.protocol); header }); test.run_transport(udp, tcp, icmpv4, icmpv6); } } fn run_ipv6( &self, ip: &Ipv6Header, ip_exts: &Ipv6Extensions, udp: &UdpHeader, tcp: &TcpHeader, icmpv4: &Icmpv4Header, icmpv6: &Icmpv6Header, ) { // fragmenting { let mut test = self.clone(); test.ip = Some({ let mut frag = ip_exts.clone(); if let Some(frag) = frag.fragment.as_mut() { if false == frag.is_fragmenting_payload() { frag.more_fragments = true; } } else { frag.fragment = Some(Ipv6FragmentHeader::new(ip_number::UDP, 0, true, 0)); } let mut header = IpHeader::Version6(ip.clone(), frag); header.set_next_headers(ip.next_header); header }); test.run(); } // non fragmenting { let mut test = self.clone(); test.ip = Some({ let mut non_frag = ip_exts.clone(); non_frag.fragment = None; let mut header = IpHeader::Version6(ip.clone(), non_frag); header.set_next_headers(ip.next_header); header }); test.run_transport(udp, tcp, icmpv4, icmpv6); } } fn run_transport( &self, udp: &UdpHeader, tcp: &TcpHeader, icmpv4: &Icmpv4Header, icmpv6: &Icmpv6Header, ) { // unknown transport layer self.run(); // udp { let mut test = self.clone(); test.ip.as_mut().unwrap().set_next_headers(ip_number::UDP); test.transport = Some(TransportHeader::Udp(udp.clone())); test.run() } // tcp { let mut test = self.clone(); test.ip.as_mut().unwrap().set_next_headers(ip_number::TCP); test.transport = Some(TransportHeader::Tcp(tcp.clone())); test.run() } // icmpv4 if let Some(payload_size) = icmpv4.fixed_payload_size() { let mut test = self.clone(); test.ip.as_mut().unwrap().set_next_headers(ip_number::ICMP); test.transport = Some(TransportHeader::Icmpv4(icmpv4.clone())); // resize the payload in case it does not have to be as big test.payload.resize(payload_size, 0); test.run() } else { let mut test = self.clone(); test.ip.as_mut().unwrap().set_next_headers(ip_number::ICMP); test.transport = Some(TransportHeader::Icmpv4(icmpv4.clone())); test.run() } // icmpv6 if let Some(payload_size) = icmpv6.fixed_payload_size() { let mut test = self.clone(); test.ip.as_mut().unwrap().set_next_headers(ip_number::IPV6_ICMP); test.transport = Some(TransportHeader::Icmpv6(icmpv6.clone())); // resize the payload in case it does not have to be as big test.payload.resize(payload_size, 0); test.run() } else { let mut test = self.clone(); test.ip.as_mut().unwrap().set_next_headers(ip_number::IPV6_ICMP); test.transport = Some(TransportHeader::Icmpv6(icmpv6.clone())); test.run() } } } proptest! { ///Test that all known packet compositions are parsed correctly. #[test] fn test_compositions(ref eth in ethernet_2_unknown(), ref vlan_outer in vlan_single_unknown(), ref vlan_inner in vlan_single_unknown(), ref ipv4 in ipv4_unknown(), ref ipv4_exts in ipv4_extensions_unknown(), ref ipv6 in ipv6_unknown(), ref ipv6_exts in ipv6_extensions_unknown(), ref udp in udp_any(), ref tcp in tcp_any(), ref icmpv4 in icmpv4_header_any(), ref icmpv6 in icmpv6_header_any(), ref payload in proptest::collection::vec(any::(), 0..1024)) { let setup_eth = | ether_type: u16 | -> ComponentTest { ComponentTest { payload: payload.clone(), link: Some({ let mut result = eth.clone(); result.ether_type = ether_type; result }), vlan: None, ip: None, transport: None } }; //ethernet 2: standalone, ipv4, ipv6 setup_eth(eth.ether_type).run(); setup_eth(EtherType::Ipv4 as u16).run_ipv4(ipv4, ipv4_exts, udp, tcp, icmpv4, icmpv6); setup_eth(EtherType::Ipv6 as u16).run_ipv6(ipv6, ipv6_exts, udp, tcp, icmpv4, icmpv6); //vlans for ether_type in VLAN_ETHER_TYPES { setup_eth(*ether_type).run_vlan(vlan_outer, vlan_inner, ipv4, ipv4_exts, ipv6, ipv6_exts, udp, tcp, icmpv4, icmpv6); } } } ///Test that assert_sliced_packet is panicing when the ethernet header is missing #[test] #[should_panic] fn test_packet_slicing_panics() { let v = Vec::new(); let s = SlicedPacket { link: None, vlan: None, ip: None, transport: None, payload: &v[..] }; ComponentTest { link: Some( Ethernet2Header { source: [0;6], destination: [0;6], ether_type: 0 } ), vlan: None, ip: None, transport: None, payload: vec![] }.assert_sliced_packet(s); } etherparse-0.13.0/tests/packet_decoder/mod.rs000064400000000000000000000075211046102023000172770ustar 00000000000000use super::*; mod packet_headers { use super::*; #[test] fn debug() { let header = PacketHeaders{ link: None, vlan: None, ip: None, transport: None, payload: &[] }; assert_eq!( &format!("{:?}", header), &format!( "PacketHeaders {{ link: {:?}, vlan: {:?}, ip: {:?}, transport: {:?}, payload: {:?} }}", header.link, header.vlan, header.ip, header.transport, header.payload ) ); } #[test] fn clone_eq() { let header = PacketHeaders{ link: None, vlan: None, ip: None, transport: None, payload: &[] }; assert_eq!(header.clone(), header); } proptest! { #[test] fn payload_ether_type( ref eth in ethernet_2_unknown(), ref vlan_outer in vlan_single_unknown(), ref vlan_inner in vlan_single_unknown(), ref ipv4 in ipv4_unknown(), ref udp in udp_any(), ) { use VlanHeader::*; use IpHeader::*; use TransportHeader::*; // none assert_eq!( None, PacketHeaders{ link: None, vlan: None, ip: None, transport: None, payload: &[] }.payload_ether_type() ); // ethernet header only assert_eq!( Some(eth.ether_type), PacketHeaders{ link: Some(eth.clone()), vlan: None, ip: None, transport: None, payload: &[] }.payload_ether_type() ); // single vlan header assert_eq!( Some(vlan_outer.ether_type), PacketHeaders{ link: Some(eth.clone()), vlan: Some(Single(vlan_outer.clone())), ip: None, transport: None, payload: &[] }.payload_ether_type() ); // double vlan header assert_eq!( Some(vlan_inner.ether_type), PacketHeaders{ link: Some(eth.clone()), vlan: Some( Double( DoubleVlanHeader { outer: vlan_outer.clone(), inner: vlan_inner.clone() } ) ), ip: None, transport: None, payload: &[] }.payload_ether_type() ); // ip present assert_eq!( None, PacketHeaders{ link: Some(eth.clone()), vlan: None, ip: Some( Version4(ipv4.clone(), Default::default()) ), transport: None, payload: &[] }.payload_ether_type() ); // transport present assert_eq!( None, PacketHeaders{ link: Some(eth.clone()), vlan: None, ip: Some( Version4(ipv4.clone(), Default::default()) ), transport: Some( Udp(udp.clone()) ), payload: &[] }.payload_ether_type() ); } } }etherparse-0.13.0/tests/packet_filter/mod.rs000064400000000000000000000607541046102023000171660ustar 00000000000000use super::*; use etherparse::packet_filter::*; use proptest::*; #[test] fn default() { let value: ElementFilter = Default::default(); assert_eq!(ElementFilter::Any, value); } ///The packet filter test generates all permutation of packet combinations & filter configurations ///and tests that all of them return the correct result. #[derive(Debug, Clone, Default)] struct PacketFilterTest { link: Option, vlan: Option, ip: Option, transport: Option, filter: Filter } impl PacketFilterTest { ///Add all permutations of vlan data types to the test (none, single, double) ///and then proceeds calling "add_ip_data" with each permutations. fn add_vlan_data(&self, outer_vlan: &SingleVlanHeader, inner_vlan: &SingleVlanHeader, ipv4: &(Ipv4Header, Vec), ipv6: &Ipv6Header, udp: &UdpHeader, tcp: &TcpHeader) { //none { let mut t = self.clone(); t.vlan = None; t.add_transport_data(udp, tcp); } //single { let mut t = self.clone(); t.vlan = Some(VlanHeader::Single(inner_vlan.clone())); t.add_ip_data(ipv4, ipv6, udp, tcp); } //double { let mut t = self.clone(); t.vlan = Some(VlanHeader::Double(DoubleVlanHeader { outer: outer_vlan.clone(), inner: inner_vlan.clone() })); t.add_ip_data(ipv4, ipv6, udp, tcp); } } ///Add all permutations of ip data types to the test (none, v4, v6) ///and then proceeds calling "add_transport_data" with each permutations. fn add_ip_data(&self, ipv4: &(Ipv4Header, Vec), ipv6: &Ipv6Header, udp: &UdpHeader, tcp: &TcpHeader) { //none { let mut t = self.clone(); t.ip = None; t.add_transport_data(udp, tcp); } //ipv4 { let mut t = self.clone(); t.ip = Some(IpHeader::Version4(ipv4.0.clone(), Default::default())); t.add_transport_data(udp, tcp); } //ipv6 { let mut t = self.clone(); t.ip = Some(IpHeader::Version6(ipv6.clone(), Default::default())); t.add_transport_data(udp, tcp); } } ///Add all permutations of transport data types to the test (none, udp, tcp) ///and then proceeds calling "add_link_filter" with each permutations. fn add_transport_data(&self, udp: &UdpHeader, tcp: &TcpHeader) { //none { let mut t = self.clone(); t.transport = None; t.add_link_filter(true); } //tcp { let mut t = self.clone(); t.transport = Some(TransportHeader::Tcp(tcp.clone())); t.add_link_filter(true); } //udp { let mut t = self.clone(); t.transport = Some(TransportHeader::Udp(udp.clone())); t.add_link_filter(true); } } fn add_link_filter(&self, expected_result: bool) { //any { let mut t = self.clone(); t.filter.link = ElementFilter::Any; t.add_vlan_filter(expected_result); } //none { let mut t = self.clone(); t.filter.link = ElementFilter::No; t.add_vlan_filter(match &t.link { None => expected_result, _ => false }); } //some match &self.link { Some(_) => { let mut t = self.clone(); t.filter.link = ElementFilter::Some( LinkFilter::Ethernet2 { source: None, destination: None } ); t.add_vlan_filter(expected_result); }, _ => { //test that the filter results in a negative match let mut t = self.clone(); t.filter.link = ElementFilter::Some( LinkFilter::Ethernet2 { source: None, destination: None } ); t.add_vlan_filter(false); } } } fn add_vlan_filter(&self, expected_result: bool) { //any { let mut t = self.clone(); t.filter.vlan = ElementFilter::Any; t.add_ip_filter(expected_result); } //none { let mut t = self.clone(); t.filter.vlan = ElementFilter::No; t.add_ip_filter(match &t.vlan { None => expected_result, _ => false }); } //single match &self.vlan { Some(VlanHeader::Single(_)) => { let mut t = self.clone(); t.filter.vlan = ElementFilter::Some( VlanFilter::Single(None) ); t.add_ip_filter(expected_result); }, Some(VlanHeader::Double(_)) => { let mut t = self.clone(); t.filter.vlan = ElementFilter::Some( VlanFilter::Double{ outer_identifier: None, inner_identifier: None, } ); t.add_ip_filter(expected_result); }, _ => { //test that the filter results in a negative match let mut t = self.clone(); t.filter.vlan = ElementFilter::Some( VlanFilter::Single(None) ); t.add_ip_filter(false); } } } fn add_ip_filter(&self, expected_result: bool) { //any { let mut t = self.clone(); t.filter.ip = ElementFilter::Any; t.add_transport_filter(expected_result); } //none { let mut t = self.clone(); t.filter.ip = ElementFilter::No; t.add_transport_filter(match &t.ip { None => expected_result, _ => false }); } //some match &self.ip { Some(IpHeader::Version4(_, _)) => { let mut t = self.clone(); t.filter.ip = ElementFilter::Some( IpFilter::Ipv4 { source: None, destination: None } ); t.add_transport_filter(expected_result); }, Some(IpHeader::Version6(_,_)) => { let mut t = self.clone(); t.filter.ip = ElementFilter::Some( IpFilter::Ipv6 { source: None, destination: None } ); t.add_transport_filter(expected_result); }, _ => { //test that the filter results in a negative match let mut t = self.clone(); t.filter.ip = ElementFilter::Some( IpFilter::Ipv4 { source: None, destination: None } ); t.add_transport_filter(false); } } } fn add_transport_filter(&self, expected_result: bool) { //any { let mut t = self.clone(); t.filter.transport = ElementFilter::Any; t.run(expected_result); } //none { let mut t = self.clone(); t.filter.transport = ElementFilter::No; t.run(match &t.transport { None => expected_result, _ => false }); } //some match &self.transport { Some(TransportHeader::Udp(_)) => { let mut t = self.clone(); t.filter.transport = ElementFilter::Some( TransportFilter::Udp { source_port: None, destination_port: None } ); t.run(expected_result); }, Some(TransportHeader::Tcp(_)) => { let mut t = self.clone(); t.filter.transport = ElementFilter::Some( TransportFilter::Tcp { source_port: None, destination_port: None } ); t.run(expected_result); }, _ => { //test that the filter results in a negative match let mut t = self.clone(); t.filter.transport = ElementFilter::Some( TransportFilter::Udp { source_port: None, destination_port: None } ); t.run(false); } } } ///Gives self.filter the headers in self as input and assert the given parameter as a result. fn run(&self, expected_result: bool) { //generate a slice containing the headers let mut link_data = Vec::new(); let mut vlan_data = Vec::new(); let mut ip_data = Vec::new(); let mut transport_data = Vec::new(); let payload = Vec::new(); let slice = SlicedPacket { link: match &self.link { Some(header) => { header.write(&mut link_data).unwrap(); Some(LinkSlice::Ethernet2(Ethernet2HeaderSlice::from_slice(&link_data[..]).unwrap())) }, None => None }, vlan: match &self.vlan { Some(VlanHeader::Single(header)) => { header.write(&mut vlan_data).unwrap(); Some(VlanSlice::SingleVlan(SingleVlanHeaderSlice::from_slice(&vlan_data[..]).unwrap())) }, Some(VlanHeader::Double(header)) => { header.write(&mut vlan_data).unwrap(); Some(VlanSlice::DoubleVlan(DoubleVlanHeaderSlice::from_slice(&vlan_data[..]).unwrap())) }, None => None }, ip: match &self.ip { Some(IpHeader::Version4(header, _)) => { header.write(&mut ip_data).unwrap(); Some( InternetSlice::Ipv4( Ipv4HeaderSlice::from_slice(&ip_data[..]).unwrap(), Default::default() ) ) }, Some(IpHeader::Version6(header, _)) => { header.write(&mut ip_data).unwrap(); Some( InternetSlice::Ipv6( Ipv6HeaderSlice::from_slice(&ip_data[..]).unwrap(), Default::default() ) ) }, None => None }, transport: match &self.transport { Some(TransportHeader::Icmpv4(header)) => { header.write(&mut transport_data).unwrap(); Some(TransportSlice::Icmpv4(Icmpv4Slice::from_slice(&transport_data[..]).unwrap())) }, Some(TransportHeader::Icmpv6(header)) => { header.write(&mut transport_data).unwrap(); Some(TransportSlice::Icmpv6(Icmpv6Slice::from_slice(&transport_data[..]).unwrap())) }, Some(TransportHeader::Udp(header)) => { header.write(&mut transport_data).unwrap(); Some(TransportSlice::Udp(UdpHeaderSlice::from_slice(&transport_data[..]).unwrap())) }, Some(TransportHeader::Tcp(header)) => { header.write(&mut transport_data).unwrap(); Some(TransportSlice::Tcp(TcpHeaderSlice::from_slice(&transport_data[..]).unwrap())) }, None => None }, payload: &payload[..] }; assert_eq!(expected_result, self.filter.applies_to_slice(&slice)); } } ///Test that all known packet compositions are parsed correctly. #[test] fn test_compositions() { //test without link { let test: PacketFilterTest = Default::default(); test.add_vlan_data( &{ //explicitly set the outer vlan ether_type id let mut re : SingleVlanHeader = Default::default(); re.ether_type = EtherType::VlanTaggedFrame as u16; re }, &Default::default(), &Default::default(), &Default::default(), &Default::default(), &Default::default() ); } //test with ethernet2 link { let mut test: PacketFilterTest = Default::default(); test.link = Some(Default::default()); test.add_vlan_data( &{ //explicitly set the outer vlan ether_type id let mut re : SingleVlanHeader = Default::default(); re.ether_type = EtherType::VlanTaggedFrame as u16; re }, &Default::default(), &Default::default(), &Default::default(), &Default::default(), &Default::default() ); } } #[cfg(test)] mod link_filter { use super::*; proptest! { #[test] fn applies_to_slice(ref eth in ethernet_2_unknown()) { use self::LinkFilter::*; //create the slice the filter can be checked against let eth_data = { let mut eth_data = Vec::new(); eth.write(&mut eth_data).unwrap(); eth_data }; let eth_slice = LinkSlice::Ethernet2( Ethernet2HeaderSlice::from_slice(ð_data[..]).unwrap() ); //test ethernet 2 filter with wildcards { let wildcard = Ethernet2 { source: Some(eth.source), destination: Some(eth.destination) }; assert_eq!(true, wildcard.applies_to_slice(ð_slice)); } //matching assert_eq!(true, Ethernet2 { source: Some(eth.source), destination: Some(eth.destination) }.applies_to_slice(ð_slice)); //non matching assert_eq!(false, Ethernet2 { source: Some({ let mut value = eth.source; value[0] = !value[0]; value }), destination: Some(eth.destination) }.applies_to_slice(ð_slice)); assert_eq!(false, Ethernet2 { source: Some(eth.source), destination: Some({ let mut value = eth.destination; value[0] = !value[0]; value }) }.applies_to_slice(ð_slice)); } } } #[cfg(test)] mod vlan_filter { use super::*; proptest! { #[test] fn applies_to_slice(ref vlan_outer in vlan_single_with(EtherType::VlanTaggedFrame as u16), ref vlan_inner in vlan_single_unknown()) { use self::VlanFilter::*; //create the slices the filters can be checked against let single_data = { let mut single_data = Vec::new(); vlan_inner.write(&mut single_data).unwrap(); single_data }; let single_slice = VlanSlice::SingleVlan( SingleVlanHeaderSlice::from_slice(&single_data[..]).unwrap() ); let double_data = { let mut double_data = Vec::new(); DoubleVlanHeader { outer: vlan_outer.clone(), inner: vlan_inner.clone() }.write(&mut double_data).unwrap(); double_data }; let double_slice = VlanSlice::DoubleVlan( DoubleVlanHeaderSlice::from_slice(&double_data[..]).unwrap() ); //test single vlan filter with wildcards { let wildcard = Single(None); assert_eq!(true, wildcard.applies_to_slice(&single_slice)); assert_eq!(false, wildcard.applies_to_slice(&double_slice)); } //matching assert_eq!(true, Single( Some(vlan_inner.vlan_identifier) ).applies_to_slice(&single_slice)); //non matching assert_eq!(false, Single( Some(!vlan_inner.vlan_identifier) ).applies_to_slice(&single_slice)); //test double vlan filter with wildcards { let wildcard = Double { outer_identifier: None, inner_identifier: None }; assert_eq!(true, wildcard.applies_to_slice(&double_slice)); assert_eq!(false, wildcard.applies_to_slice(&single_slice)); } //matching assert_eq!(true, Double { outer_identifier: Some(vlan_outer.vlan_identifier), inner_identifier: Some(vlan_inner.vlan_identifier) }.applies_to_slice(&double_slice)); //non matching assert_eq!(false, Double { outer_identifier: Some(!vlan_outer.vlan_identifier), inner_identifier: Some(vlan_inner.vlan_identifier) }.applies_to_slice(&double_slice)); assert_eq!(false, Double { outer_identifier: Some(vlan_outer.vlan_identifier), inner_identifier: Some(!vlan_inner.vlan_identifier) }.applies_to_slice(&double_slice)); } } } #[cfg(test)] mod ip_filter { use super::*; proptest! { #[test] fn applies_to_slice(ref ipv4 in ipv4_unknown(), ref ipv6 in ipv6_unknown()) { use self::IpFilter::*; //create the slices the filters can be checked against let ipv4_data = { let mut ipv4_data = Vec::new(); ipv4.write(&mut ipv4_data).unwrap(); ipv4_data }; let ipv4_slice = InternetSlice::Ipv4( Ipv4HeaderSlice::from_slice(&ipv4_data[..]).unwrap(), Default::default() ); let ipv6_data = { let mut ipv6_data = Vec::new(); ipv6.write(&mut ipv6_data).unwrap(); ipv6_data }; let ipv6_slice = InternetSlice::Ipv6( Ipv6HeaderSlice::from_slice(&ipv6_data[..]).unwrap(), Default::default() ); //test ipv4 filter with wildcards { let wildcard = Ipv4 { source: None, destination:None }; assert_eq!(true, wildcard.applies_to_slice(&ipv4_slice)); assert_eq!(false, wildcard.applies_to_slice(&ipv6_slice)); } //matching assert_eq!(true, Ipv4 { source: Some(ipv4.source), destination: Some(ipv4.destination) }.applies_to_slice(&ipv4_slice)); //non matching assert_eq!(false, Ipv4 { source: Some({ let mut value = ipv4.source; value[0] = !value[0]; value }), destination: Some(ipv4.destination) }.applies_to_slice(&ipv4_slice)); assert_eq!(false, Ipv4 { source: Some(ipv4.source), destination: Some({ let mut value = ipv4.destination; value[0] = !value[0]; value }) }.applies_to_slice(&ipv4_slice)); //test ipv6 filter with wildcards { let wildcard = Ipv6 { source: None, destination:None }; assert_eq!(true, wildcard.applies_to_slice(&ipv6_slice)); assert_eq!(false, wildcard.applies_to_slice(&ipv4_slice)); } //matching assert_eq!(true, Ipv6 { source: Some(ipv6.source), destination: Some(ipv6.destination) }.applies_to_slice(&ipv6_slice)); //non matching assert_eq!(false, Ipv6 { source: Some({ let mut value = ipv6.source; value[0] = !value[0]; value }), destination: Some(ipv6.destination) }.applies_to_slice(&ipv6_slice)); assert_eq!(false, Ipv6 { source: Some(ipv6.source), destination: Some({ let mut value = ipv6.destination; value[0] = !value[0]; value }) }.applies_to_slice(&ipv6_slice)); } } } #[cfg(test)] mod transport_filter { use super::*; proptest! { #[test] fn applies_to_slice(ref udp in udp_any(), ref tcp in tcp_any()) { use self::TransportFilter::*; //create the slices the filters can be checked against let udp_data = { let mut udp_data = Vec::new(); udp.write(&mut udp_data).unwrap(); udp_data }; let udp_slice = TransportSlice::Udp( UdpHeaderSlice::from_slice(&udp_data[..]).unwrap() ); let tcp_data = { let mut tcp_data = Vec::new(); tcp.write(&mut tcp_data).unwrap(); tcp_data }; let tcp_slice = TransportSlice::Tcp( TcpHeaderSlice::from_slice(&tcp_data[..]).unwrap() ); //test udp filter with wildcards { let wildcard = Udp { source_port: None, destination_port:None }; assert_eq!(true, wildcard.applies_to_slice(&udp_slice)); assert_eq!(false, wildcard.applies_to_slice(&tcp_slice)); } //matching assert_eq!(true, Udp { source_port: Some(udp.source_port), destination_port: Some(udp.destination_port) }.applies_to_slice(&udp_slice)); //non matching assert_eq!(false, Udp { source_port: Some(!udp.source_port), //inverted port destination_port: Some(udp.destination_port) }.applies_to_slice(&udp_slice)); assert_eq!(false, Udp { source_port: Some(udp.source_port), destination_port: Some(!udp.destination_port) //inverted port }.applies_to_slice(&udp_slice)); //test tcp filter with wildcards { let wildcard = Tcp { source_port: None, destination_port:None }; assert_eq!(true, wildcard.applies_to_slice(&tcp_slice)); assert_eq!(false, wildcard.applies_to_slice(&udp_slice)); } //matching assert_eq!(true, Tcp { source_port: Some(tcp.source_port), destination_port: Some(tcp.destination_port) }.applies_to_slice(&tcp_slice)); //non matching assert_eq!(false, Tcp { source_port: Some(!tcp.source_port), //inverted port destination_port: Some(tcp.destination_port) }.applies_to_slice(&tcp_slice)); assert_eq!(false, Tcp { source_port: Some(tcp.source_port), destination_port: Some(!tcp.destination_port) //inverted port }.applies_to_slice(&tcp_slice)); } } } #[test] fn type_derives() { println!("{:?}", TransportFilter::Udp{ source_port: None, destination_port: None }); println!("{:?}", TransportFilter::Tcp{ source_port: None, destination_port: None }); } etherparse-0.13.0/tests/packet_slicing/mod.rs000064400000000000000000000245441046102023000173260ustar 00000000000000use super::*; mod internet_slice { use super::*; #[test] fn debug_clone_eq() { // ipv4 { let mut header : Ipv4Header = Default::default(); header.protocol = ip_number::UDP; let buffer = { let mut buffer = Vec::with_capacity(header.header_len()); header.write(&mut buffer).unwrap(); buffer }; let ipv4 = Ipv4HeaderSlice::from_slice(&buffer).unwrap(); let exts = Ipv4ExtensionsSlice { auth: None }; let slice = InternetSlice::Ipv4(ipv4.clone(), exts.clone()); // clone & eq assert_eq!(slice.clone(), slice); // debug assert_eq!( format!("{:?}", slice), format!("Ipv4({:?}, {:?})", ipv4, exts) ); } // ipv6 { let mut header : Ipv6Header = Default::default(); header.next_header = ip_number::UDP; let buffer = { let mut buffer = Vec::with_capacity(header.header_len()); header.write(&mut buffer).unwrap(); buffer }; let ipv6 = Ipv6HeaderSlice::from_slice(&buffer).unwrap(); let exts = Ipv6ExtensionsSlice::from_slice(ip_number::UDP, &[]).unwrap().0; let slice = InternetSlice::Ipv6(ipv6.clone(), exts.clone()); // clone & eq assert_eq!(slice.clone(), slice); // debug assert_eq!( format!("{:?}", slice), format!("Ipv6({:?}, {:?})", ipv6, exts) ); } } #[test] fn is_fragmenting_payload() { for is_frag in [false, true] { // ipv4 { let mut header : Ipv4Header = Default::default(); header.protocol = ip_number::UDP; header.more_fragments = is_frag; let buffer = { let mut buffer = Vec::with_capacity(header.header_len()); header.write(&mut buffer).unwrap(); buffer }; let ipv4 = Ipv4HeaderSlice::from_slice(&buffer).unwrap(); let exts = Ipv4ExtensionsSlice { auth: None }; let slice = InternetSlice::Ipv4(ipv4.clone(), exts.clone()); assert_eq!(is_frag, slice.is_fragmenting_payload()); } // ipv6 { let mut header : Ipv6Header = Default::default(); header.next_header = ip_number::IPV6_FRAG; let frag_header = Ipv6FragmentHeader{ next_header: ip_number::UDP, fragment_offset: 0, more_fragments: is_frag, identification: 0 }; header.payload_length = frag_header.header_len() as u16; let buffer = { let mut buffer = Vec::with_capacity( header.header_len() + frag_header.header_len() ); header.write(&mut buffer).unwrap(); frag_header.write(&mut buffer).unwrap(); buffer }; let ipv6 = Ipv6HeaderSlice::from_slice(&buffer).unwrap(); let exts = Ipv6ExtensionsSlice::from_slice(ip_number::IPV6_FRAG, &buffer[header.header_len()..]).unwrap().0; let slice = InternetSlice::Ipv6(ipv6.clone(), exts.clone()); // clone & eq assert_eq!(is_frag, slice.is_fragmenting_payload()); } } } } mod transport_slice { use super::*; #[test] fn debug_clone_eq() { // udp { let header : UdpHeader = Default::default(); let raw = header.to_bytes(); let u = UdpHeaderSlice::from_slice(&raw).unwrap(); let slice = TransportSlice::Udp(u.clone()); // clone & eq assert_eq!(slice.clone(), slice); // debug assert_eq!( format!("{:?}", slice), format!("Udp({:?})", u) ); } // tcp { let header : TcpHeader = Default::default(); let buffer = { let mut buffer = Vec::with_capacity(header.header_len() as usize); header.write(&mut buffer).unwrap(); buffer }; let t = TcpHeaderSlice::from_slice(&buffer).unwrap(); let slice = TransportSlice::Tcp(t.clone()); // clone & eq assert_eq!(slice.clone(), slice); // debug assert_eq!( format!("{:?}", slice), format!("Tcp({:?})", t) ); } // unknown { let slice = TransportSlice::Unknown(ip_number::IGMP); // clone & eq assert_eq!(slice.clone(), slice); // debug assert_eq!( format!("{:?}", slice), format!("Unknown({:?})", ip_number::IGMP) ); } } } mod sliced_packet { use super::*; #[test] fn from_ip_errors() { use crate::ReadError::*; //slice length error assert_matches!( SlicedPacket::from_ip(&[]), Err(UnexpectedEndOfSlice(1)) ); //bad protocol number for i in 0u8..std::u8::MAX { if i >> 4 != 4 && i >> 4 != 6 { assert_matches!( SlicedPacket::from_ip(&[i]), Err(IpUnsupportedVersion(_)) ); } } } #[test] fn debug() { let header = SlicedPacket{ link: None, vlan: None, ip: None, transport: None, payload: &[] }; assert_eq!( format!("{:?}", header), format!( "SlicedPacket {{ link: {:?}, vlan: {:?}, ip: {:?}, transport: {:?}, payload: {:?} }}", header.link, header.vlan, header.ip, header.transport, header.payload ) ); } #[test] fn clone_eq() { let header = SlicedPacket{ link: None, vlan: None, ip: None, transport: None, payload: &[] }; assert_eq!(header.clone(), header); } proptest! { #[test] fn payload_ether_type( ref eth in ethernet_2_unknown(), ref vlan_outer in vlan_single_unknown(), ref vlan_inner in vlan_single_unknown(), ref ipv4 in ipv4_unknown(), ref udp in udp_any(), ) { use IpHeader::*; // empty { let s = SlicedPacket{ link: None, vlan: None, ip: None, transport: None, payload: &[] }; assert_eq!(None, s.payload_ether_type()); } // only ethernet { let mut serialized = Vec::with_capacity(eth.header_len()); eth.write(&mut serialized).unwrap(); assert_eq!( Some(eth.ether_type), SlicedPacket::from_ethernet(&serialized) .unwrap() .payload_ether_type() ); } // with single vlan { let mut eth_mod = eth.clone(); eth_mod.ether_type = ether_type::VLAN_TAGGED_FRAME; let mut serialized = Vec::with_capacity( eth_mod.header_len() + vlan_outer.header_len() ); eth_mod.write(&mut serialized).unwrap(); vlan_outer.write(&mut serialized).unwrap(); assert_eq!( Some(vlan_outer.ether_type), SlicedPacket::from_ethernet(&serialized) .unwrap() .payload_ether_type() ); } // with double vlan { let mut eth_mod = eth.clone(); eth_mod.ether_type = ether_type::VLAN_TAGGED_FRAME; let mut vlan_outer_mod = vlan_outer.clone(); vlan_outer_mod.ether_type = ether_type::VLAN_TAGGED_FRAME; let mut serialized = Vec::with_capacity( eth_mod.header_len() + vlan_outer_mod.header_len() + vlan_inner.header_len() ); eth_mod.write(&mut serialized).unwrap(); vlan_outer_mod.write(&mut serialized).unwrap(); vlan_inner.write(&mut serialized).unwrap(); assert_eq!( Some(vlan_inner.ether_type), SlicedPacket::from_ethernet(&serialized) .unwrap() .payload_ether_type() ); } // with ip { let builder = PacketBuilder::ethernet2(eth.source, eth.destination) .ip(Version4(ipv4.clone(), Default::default())); let mut serialized = Vec::with_capacity(builder.size(0)); builder.write(&mut serialized, ipv4.protocol, &[]).unwrap(); assert_eq!( None, SlicedPacket::from_ethernet(&serialized) .unwrap() .payload_ether_type() ); } // with transport { let builder = PacketBuilder::ethernet2(eth.source, eth.destination) .ip(Version4(ipv4.clone(), Default::default())) .udp(udp.source_port, udp.destination_port); let mut serialized = Vec::with_capacity(builder.size(0)); builder.write(&mut serialized, &[]).unwrap(); assert_eq!( None, SlicedPacket::from_ethernet(&serialized) .unwrap() .payload_ether_type() ); } } } } etherparse-0.13.0/tests/proptest_generators/mod.rs000064400000000000000000000502311046102023000204500ustar 00000000000000use super::*; use proptest::*; use proptest::prelude::*; pub fn error_field_any() -> impl Strategy { use ErrorField::*; prop_oneof![ Just(Ipv4PayloadLength), Just(Ipv4Dscp), Just(Ipv4Ecn), Just(Ipv4FragmentsOffset), Just(Ipv6FlowLabel), Just(VlanTagPriorityCodePoint), Just(VlanTagVlanId) ] } pub fn vlan_ethertype_any() -> impl Strategy { prop_oneof![ Just(ether_type::VLAN_TAGGED_FRAME), Just(ether_type::PROVIDER_BRIDGING), Just(ether_type::VLAN_DOUBLE_TAGGED_FRAME), ] } prop_compose! { pub(crate) fn ethernet_2_with(ether_type: u16)( source in prop::array::uniform6(any::()), dest in prop::array::uniform6(any::()), ether_type in proptest::strategy::Just(ether_type)) -> Ethernet2Header { Ethernet2Header { source: source, destination: dest, ether_type: ether_type } } } prop_compose! { pub(crate) fn ethernet_2_any() (ether_type in any::()) (result in ethernet_2_with(ether_type)) -> Ethernet2Header { result } } pub static ETHERNET_KNOWN_ETHER_TYPES: &'static [u16] = &[ ether_type::IPV4, ether_type::IPV6, ether_type::VLAN_TAGGED_FRAME, ether_type::PROVIDER_BRIDGING, ether_type::VLAN_DOUBLE_TAGGED_FRAME ]; prop_compose! { pub(crate) fn ethernet_2_unknown()( source in prop::array::uniform6(any::()), dest in prop::array::uniform6(any::()), ether_type in any::().prop_filter("ether_type must be unknown", |v| !ETHERNET_KNOWN_ETHER_TYPES.iter().any(|&x| v == &x))) -> Ethernet2Header { Ethernet2Header { source: source, destination: dest, ether_type: ether_type } } } prop_compose! { pub(crate) fn vlan_single_unknown()( priority_code_point in prop::bits::u8::between(0,3), drop_eligible_indicator in any::(), vlan_identifier in prop::bits::u16::between(0,12), ether_type in any::().prop_filter("ether_type must be unknown", |v| !ETHERNET_KNOWN_ETHER_TYPES.iter().any(|&x| v == &x))) -> SingleVlanHeader { SingleVlanHeader { priority_code_point: priority_code_point, drop_eligible_indicator: drop_eligible_indicator, vlan_identifier: vlan_identifier, ether_type: ether_type } } } prop_compose! { pub(crate) fn vlan_single_with(ether_type: u16)( priority_code_point in prop::bits::u8::between(0,3), drop_eligible_indicator in any::(), vlan_identifier in prop::bits::u16::between(0,12), ether_type in proptest::strategy::Just(ether_type)) -> SingleVlanHeader { SingleVlanHeader { priority_code_point: priority_code_point, drop_eligible_indicator: drop_eligible_indicator, vlan_identifier: vlan_identifier, ether_type: ether_type } } } prop_compose! { pub(crate) fn vlan_single_any() (ether_type in any::()) (result in vlan_single_with(ether_type)) -> SingleVlanHeader { result } } prop_compose! { pub(crate) fn vlan_double_any() (ether_type in any::()) (result in vlan_double_with(ether_type)) -> DoubleVlanHeader { result } } prop_compose! { pub(crate) fn vlan_double_with(ether_type: u16)( outer_ethertype in vlan_ethertype_any(), inner_ethertype in proptest::strategy::Just(ether_type) )( outer in vlan_single_with(outer_ethertype), inner in vlan_single_with(inner_ethertype) ) -> DoubleVlanHeader { DoubleVlanHeader { outer, inner } } } prop_compose! { pub(crate) fn ipv4_with(protocol: u8) ( ihl in 0u8..10, protocol in proptest::strategy::Just(protocol)) (source in prop::array::uniform4(any::()), dest in prop::array::uniform4(any::()), dscp in prop::bits::u8::between(0,6), ecn in prop::bits::u8::between(0,2), identification in any::(), ttl in any::(), dont_fragment in any::(), more_fragments in any::(), fragments_offset in prop::bits::u16::between(0, 13), header_checksum in any::(), payload_len in 0..(std::u16::MAX - u16::from(ihl*4) - (Ipv4Header::SERIALIZED_SIZE as u16)), protocol in proptest::strategy::Just(protocol), options_len in proptest::strategy::Just(ihl*4), options_part0 in prop::array::uniform32(any::()), options_part1 in prop::array::uniform8(any::()) ) -> Ipv4Header { let mut result: Ipv4Header = Default::default(); { let mut options: [u8;40] = [0;40]; //copy together 40 bytes of random data (the limit for static arrays in proptest 32, //so a 32 & 8 byte array get combined here) let len = usize::from(options_len); if len > 0 { let sub_len = std::cmp::min(len,32); options[..sub_len].copy_from_slice(&options_part0[..sub_len]); } if len > 32 { let sub_len = len - 32; options[32..len].copy_from_slice(&options_part1[..sub_len]); } //set the options result.set_options(&options[..len]).unwrap(); } result.differentiated_services_code_point = dscp; result.explicit_congestion_notification = ecn; result.payload_len = payload_len; result.identification = identification; result.dont_fragment = dont_fragment; result.more_fragments = more_fragments; result.fragments_offset = fragments_offset; result.time_to_live = ttl; result.protocol = protocol; result.header_checksum = header_checksum; result.source = source; result.destination = dest; return result; } } prop_compose! { pub(crate) fn ipv4_any() (protocol in any::()) (result in ipv4_with(protocol)) -> Ipv4Header { result } } static IPV4_KNOWN_PROTOCOLS: &'static [u8] = &[ ip_number::ICMP, ip_number::UDP, ip_number::TCP, ip_number::AUTH, ip_number::IPV6_ICMP, ]; prop_compose! { pub(crate) fn ipv4_unknown() (protocol in any::().prop_filter("protocol must be unknown", |v| !IPV4_KNOWN_PROTOCOLS.iter().any(|&x| v == &x)) ) (header in ipv4_with(protocol) ) -> Ipv4Header { header } } prop_compose! { pub(crate) fn ipv4_extensions_with(next_header: u8) ( has_auth in any::(), auth in ip_authentication_with(next_header) ) -> Ipv4Extensions { if has_auth { Ipv4Extensions{ auth: Some(auth), } } else { Ipv4Extensions{ auth: None, } } } } prop_compose! { pub(crate) fn ipv4_extensions_any() (protocol in any::()) (result in ipv4_extensions_with(protocol)) -> Ipv4Extensions { result } } prop_compose! { pub(crate) fn ipv4_extensions_unknown() ( next_header in any::().prop_filter( "next_header must be unknown", |v| !IPV4_KNOWN_PROTOCOLS.iter().any(|&x| v == &x) ) ) ( result in ipv4_extensions_with(next_header) ) -> Ipv4Extensions { result } } prop_compose! { pub(crate) fn ipv6_with(next_header: u8) ( source in prop::array::uniform16(any::()), dest in prop::array::uniform16(any::()), traffic_class in any::(), flow_label in prop::bits::u32::between(0,20), payload_length in any::(), hop_limit in any::(), next_header in proptest::strategy::Just(next_header) ) -> Ipv6Header { Ipv6Header { traffic_class: traffic_class, flow_label: flow_label, payload_length: payload_length, next_header: next_header, hop_limit: hop_limit, source: source, destination: dest } } } prop_compose! { pub(crate) fn ipv6_any() (next_header in any::()) (result in ipv6_with(next_header) ) -> Ipv6Header { result } } static IPV6_KNOWN_NEXT_HEADERS: &'static [u8] = &[ ip_number::ICMP, ip_number::UDP, ip_number::TCP, ip_number::IPV6_HOP_BY_HOP, ip_number::IPV6_ICMP, ip_number::IPV6_ROUTE, ip_number::IPV6_FRAG, ip_number::AUTH, ip_number::IPV6_DEST_OPTIONS, ip_number::MOBILITY, ip_number::HIP, ip_number::SHIM6, // currently not supported: // - EncapsulatingSecurityPayload // - ExperimentalAndTesting0 // - ExperimentalAndTesting1 ]; prop_compose! { pub(crate) fn ipv6_unknown()( source in prop::array::uniform16(any::()), dest in prop::array::uniform16(any::()), traffic_class in any::(), flow_label in prop::bits::u32::between(0,20), payload_length in any::(), hop_limit in any::(), next_header in any::().prop_filter("next_header must be unknown", |v| !IPV6_KNOWN_NEXT_HEADERS.iter().any(|&x| v == &x)) ) -> Ipv6Header { Ipv6Header { traffic_class: traffic_class, flow_label: flow_label, payload_length: payload_length, next_header: next_header, hop_limit: hop_limit, source: source, destination: dest } } } prop_compose! { pub(crate) fn ipv6_raw_extension_with( next_header: u8, len: u8 ) ( next_header in proptest::strategy::Just(next_header), payload in proptest::collection::vec(any::(), (len as usize)*8 + 6) ) -> Ipv6RawExtensionHeader { Ipv6RawExtensionHeader::new_raw( next_header, &payload[..] ).unwrap() } } prop_compose! { pub(crate) fn ipv6_raw_extension_any() ( next_header in any::(), len in any::() ) ( result in ipv6_raw_extension_with(next_header, len) ) -> Ipv6RawExtensionHeader { result } } prop_compose! { pub(crate) fn ipv6_extensions_with(next_header: u8) ( has_hop_by_hop_options in any::(), hop_by_hop_options in ipv6_raw_extension_any(), has_destination_options in any::(), destination_options in ipv6_raw_extension_any(), has_routing in any::(), routing in ipv6_raw_extension_any(), has_fragment in any::(), fragment in ipv6_fragment_any(), has_auth in any::(), auth in ip_authentication_with(next_header), has_final_destination_options in any::(), final_destination_options in ipv6_raw_extension_any() ) -> Ipv6Extensions { let mut result = Ipv6Extensions { hop_by_hop_options: if has_hop_by_hop_options { Some(hop_by_hop_options) } else { None }, destination_options: if has_destination_options { Some(destination_options) } else { None }, routing: if has_routing { Some( Ipv6RoutingExtensions{ routing, final_destination_options: if has_final_destination_options { Some(final_destination_options) } else { None } } ) } else { None }, fragment: if has_fragment { Some(fragment) } else { None }, auth: if has_auth { Some(auth) } else { None }, }; result.set_next_headers(next_header); result } } prop_compose! { pub(crate) fn ipv6_extensions_any() ( next_header in any::() ) ( result in ipv6_extensions_with(next_header) ) -> Ipv6Extensions { result } } prop_compose! { pub(crate) fn ipv6_extensions_unknown() ( next_header in any::().prop_filter( "next_header must be unknown", |v| !IPV6_KNOWN_NEXT_HEADERS.iter().any(|&x| v == &x) ) ) ( result in ipv6_extensions_with(next_header) ) -> Ipv6Extensions { result } } prop_compose! { pub(crate) fn ipv6_fragment_with( next_header: u8 ) ( next_header in proptest::strategy::Just(next_header), fragment_offset in 0u16..=0b0001_1111_1111_1111u16, more_fragments in any::(), identification in any::(), ) -> Ipv6FragmentHeader { Ipv6FragmentHeader::new( next_header, fragment_offset, more_fragments, identification ) } } prop_compose! { pub(crate) fn ipv6_fragment_any() (next_header in any::()) (result in ipv6_fragment_with(next_header) ) -> Ipv6FragmentHeader { result } } prop_compose! { pub(crate) fn ip_authentication_with( next_header: u8 ) ( next_header in proptest::strategy::Just(next_header), len in 1..0xffu8 ) ( next_header in proptest::strategy::Just(next_header), spi in any::(), sequence_number in any::(), icv in proptest::collection::vec(any::(), (len as usize)*4) ) -> IpAuthenticationHeader { IpAuthenticationHeader::new( next_header, spi, sequence_number, &icv ).unwrap() } } prop_compose! { pub(crate) fn ip_authentication_any() ( next_header in any::() ) ( header in ip_authentication_with(next_header) ) -> IpAuthenticationHeader { header } } prop_compose! { pub(crate) fn udp_any()( source_port in any::(), destination_port in any::(), length in any::(), checksum in any::()) -> UdpHeader { UdpHeader { source_port: source_port, destination_port: destination_port, length: length, checksum: checksum } } } prop_compose! { pub(crate) fn tcp_any() (data_offset in TCP_MINIMUM_DATA_OFFSET..(TCP_MAXIMUM_DATA_OFFSET + 1)) ( source_port in any::(), destination_port in any::(), sequence_number in any::(), acknowledgment_number in any::(), ns in any::(), fin in any::(), syn in any::(), rst in any::(), psh in any::(), ack in any::(), ece in any::(), urg in any::(), cwr in any::(), window_size in any::(), checksum in any::(), urgent_pointer in any::(), options in proptest::collection::vec(any::(), ((data_offset - 5) as usize)*4)) -> TcpHeader { let mut result = TcpHeader::new(source_port, destination_port, sequence_number, window_size); result.acknowledgment_number = acknowledgment_number; result.ns = ns; result.fin = fin; result.syn = syn; result.rst = rst; result.psh = psh; result.ack = ack; result.ece = ece; result.urg = urg; result.cwr = cwr; result.checksum = checksum; result.urgent_pointer = urgent_pointer; result.set_options_raw(&options[..]).unwrap(); result } } pub fn ip_number_any() -> impl Strategy { use IpNumber::*; prop_oneof![ Just(IPv6HeaderHopByHop), Just(Icmp), Just(Igmp), Just(Ggp), Just(IPv4), Just(Stream), Just(Tcp), Just(Cbt), Just(Egp), Just(Igp), Just(BbnRccMon), Just(NvpII), Just(Pup), Just(Argus), Just(Emcon), Just(Xnet), Just(Chaos), Just(Udp), Just(Mux), Just(DcnMeas), Just(Hmp), Just(Prm), Just(XnsIdp), Just(Trunk1), Just(Trunk2), Just(Leaf1), Just(Leaf2), Just(Rdp), Just(Irtp), Just(IsoTp4), Just(NetBlt), Just(MfeNsp), Just(MeritInp), Just(Dccp), Just(ThirdPartyConnectProtocol), Just(Idpr), Just(Xtp), Just(Ddp), Just(IdprCmtp), Just(TpPlusPlus), Just(Il), Just(Ipv6), Just(Sdrp), Just(IPv6RouteHeader), Just(IPv6FragmentationHeader), Just(Idrp), Just(Rsvp), Just(Gre), Just(Dsr), Just(Bna), Just(EncapsulatingSecurityPayload), Just(AuthenticationHeader), Just(Inlsp), Just(Swipe), Just(Narp), Just(Mobile), Just(Tlsp), Just(Skip), Just(IPv6Icmp), Just(IPv6NoNextHeader), Just(IPv6DestinationOptions), Just(AnyHostInternalProtocol), Just(Cftp), Just(AnyLocalNetwork), Just(SatExpak), Just(Krytolan), Just(Rvd), Just(Ippc), Just(AnyDistributedFileSystem), Just(SatMon), Just(Visa), Just(Ipcv), Just(Cpnx), Just(Cphb), Just(Wsn), Just(Pvp), Just(BrSatMon), Just(SunNd), Just(WbMon), Just(WbExpak), Just(IsoIp), Just(Vmtp), Just(SecureVmtp), Just(Vines), Just(TtpOrIptm), Just(NsfnetIgp), Just(Dgp), Just(Tcf), Just(Eigrp), Just(Ospfigp), Just(SpriteRpc), Just(Larp), Just(Mtp), Just(Ax25), Just(Ipip), Just(Micp), Just(SccSp), Just(EtherIp), Just(Encap), Just(Gmtp), Just(Ifmp), Just(Pnni), Just(Pim), Just(Aris), Just(Scps), Just(Qnx), Just(ActiveNetworks), Just(IpComp), Just(SitraNetworksProtocol), Just(CompaqPeer), Just(IpxInIp), Just(Vrrp), Just(Pgm), Just(AnyZeroHopProtocol), Just(Layer2TunnelingProtocol), Just(Ddx), Just(Iatp), Just(Stp), Just(Srp), Just(Uti), Just(SimpleMessageProtocol), Just(Sm), Just(Ptp), Just(IsisOverIpv4), Just(Fire), Just(Crtp), Just(Crudp), Just(Sscopmce), Just(Iplt), Just(Sps), Just(Pipe), Just(Sctp), Just(Fc), Just(RsvpE2eIgnore), Just(MobilityHeader), Just(UdpLite), Just(MplsInIp), Just(Manet), Just(Hip), Just(Shim6), Just(Wesp), Just(Rohc), Just(ExperimentalAndTesting0), Just(ExperimentalAndTesting1) ] } prop_compose! { pub fn icmpv4_type_any() ( bytes in any::<[u8;20]>(), ) -> Icmpv4Type { Icmpv4Header::from_slice(&bytes).unwrap().0.icmp_type } } prop_compose! { pub fn icmpv4_header_any() ( bytes in any::<[u8;20]>(), ) -> Icmpv4Header { Icmpv4Header::from_slice(&bytes).unwrap().0 } } prop_compose! { pub fn icmpv6_type_any() ( bytes in any::<[u8;8]>(), ) -> Icmpv6Type { Icmpv6Header::from_slice(&bytes).unwrap().0.icmp_type } } prop_compose! { pub fn icmpv6_header_any() ( bytes in any::<[u8;8]>(), ) -> Icmpv6Header { Icmpv6Header::from_slice(&bytes).unwrap().0 } } etherparse-0.13.0/tests/test_reader.rs000064400000000000000000000101241046102023000160560ustar 00000000000000use std::io; /// A reader that also throws an error when a seek /// to a non existing position is executed (not normally the behavior). /// /// Note that this is not the default behavior of seek /// but it is needed for testing purposes. pub struct TestReader { data: Vec, cur_offset: usize } impl TestReader { /// Creates a reader with the given data pub fn new(data: &[u8]) -> TestReader { TestReader{ data: { let mut v = Vec::with_capacity(data.len()); v.extend_from_slice(data); v }, cur_offset: 0 } } /// Current offset from the start. pub fn cur_offset(&self) -> usize { self.cur_offset } } impl io::Read for TestReader { fn read(&mut self, buf: &mut [u8]) -> io::Result { if buf.len() > self.data.len() - self.cur_offset { Err(io::Error::new(io::ErrorKind::UnexpectedEof, "failed to fill whole buffer")) } else { buf.clone_from_slice( &self.data[ self.cur_offset..(self.cur_offset + buf.len()) ] ); self.cur_offset += buf.len(); Ok(buf.len()) } } } impl io::Seek for TestReader{ fn seek(&mut self, pos: io::SeekFrom) -> io::Result { use io::SeekFrom::*; let new_offset = match pos { Start(start_offset) => start_offset as i64, End(end_offset) => (self.data.len() as i64) + end_offset, Current(offset) => (self.cur_offset as i64) + offset, }; if new_offset < 0 { Err( io::Error::new( io::ErrorKind::InvalidInput, "invalid seek to a negative or overflowing position", ) ) } else if new_offset > (self.data.len() as i64) { // Note this is not default behavior but is usefull for // testing. Normally a seek over the end is allowed. Err( io::Error::new( io::ErrorKind::InvalidInput, "invalid seek to a negative or overflowing position", ) ) } else { self.cur_offset = new_offset as usize; Ok(self.cur_offset as u64) } } } #[test] fn read() { use io::Read; { let mut reader = TestReader::new(&[1,2,3,4]); { let mut tar: [u8;4] = [0;4]; assert_eq!(4, reader.read(&mut tar).unwrap()); assert_eq!(&tar[..], &[1,2,3,4]); } { let mut tar: [u8;1] = [0]; assert_eq!( io::ErrorKind::UnexpectedEof, reader.read(&mut tar).unwrap_err().kind() ); } } } #[test] fn seek() { use io::Seek; use io::SeekFrom::*; // ok seeks { let mut reader = TestReader::new(&[1,2,3,4]); assert_eq!(2, reader.seek(Start(2)).unwrap()); assert_eq!(reader.cur_offset(), 2); assert_eq!(3, reader.seek(Current(1)).unwrap()); assert_eq!(3, reader.cur_offset()); assert_eq!(1, reader.seek(End(-3)).unwrap()); assert_eq!(1, reader.cur_offset()); } // bad seeks { let mut reader = TestReader::new(&[1,2,3,4]); assert_eq!( io::ErrorKind::InvalidInput, reader.seek(Start(5)).unwrap_err().kind() ); } { let mut reader = TestReader::new(&[1,2,3,4]); reader.seek(Start(2)).unwrap(); assert_eq!( io::ErrorKind::InvalidInput, reader.seek(Current(3)).unwrap_err().kind() ); assert_eq!( io::ErrorKind::InvalidInput, reader.seek(Current(-3)).unwrap_err().kind() ); } { let mut reader = TestReader::new(&[1,2,3,4]); assert_eq!( io::ErrorKind::InvalidInput, reader.seek(End(-5)).unwrap_err().kind() ); assert_eq!( io::ErrorKind::InvalidInput, reader.seek(End(1)).unwrap_err().kind() ); } } etherparse-0.13.0/tests/test_writer.rs000064400000000000000000000061261046102023000161370ustar 00000000000000use std::io; /// Writer that can be configured to returns an error on write if /// more then the specified maximum size has been written. /// /// This writer is used in the tests to check if /// writing code correctly early returns if a write /// triggers an error. pub struct TestWriter { data: Vec, cur_size: usize, max_size: Option, error_kind: io::ErrorKind, } impl TestWriter { /// Create a new test writer without a maximum size pub fn new() -> TestWriter { TestWriter{ data: Vec::new(), cur_size: 0, max_size: None, error_kind: io::ErrorKind::UnexpectedEof, } } /// Create a new error writer that throws an `io::Error` of kind `io::Error::UnexpectedEof` /// if a write would exceed the given maximum size. pub fn with_max_size(max_size: usize) -> TestWriter { TestWriter{ data: Vec::new(), cur_size: 0, max_size: Some(max_size), error_kind: io::ErrorKind::UnexpectedEof, } } /// Create a new error writer that throws an `io::Error` of the given kind /// if a write would exceed the given maximum size. pub fn with_max_size_and_error_kind(max_size: usize, error_kind: io::ErrorKind) -> TestWriter { TestWriter{ data: Vec::new(), cur_size: 0, max_size: Some(max_size), error_kind, } } /// The error kind produced if the size is exceeded pub fn error_kind(&self) -> io::ErrorKind { self.error_kind } } impl io::Write for TestWriter { fn write(&mut self, buf: &[u8]) -> std::io::Result { self.cur_size += buf.len(); if let Some(max_size) = self.max_size { if self.cur_size > max_size { Err(io::Error::new(self.error_kind, "Maximum size exceeded")) } else { Ok(buf.len()) } } else { self.data.extend_from_slice(buf); Ok(buf.len()) } } fn flush(&mut self) -> std::io::Result<()> { Ok(()) } } #[test] fn new() { use io::Write; let mut writer = TestWriter::new(); assert_eq!(true, writer.flush().is_ok()); assert_eq!(4, writer.write(&[1,2,3,4]).unwrap()); } #[test] fn with_max_size() { use io::Write; let mut writer = TestWriter::with_max_size(6); // write within bounds assert_eq!(true, writer.flush().is_ok()); assert_eq!(4, writer.write(&[1,2,3,4]).unwrap()); assert_eq!(true, writer.flush().is_ok()); // on bounds on border assert_eq!(2, writer.write(&[1,2]).unwrap()); // outside of bounds assert_eq!(io::ErrorKind::UnexpectedEof, writer.write(&[1]).unwrap_err().kind()); assert_eq!(true, writer.flush().is_ok()); } #[test] fn new_with_error_kind() { use io::Write; let mut writer = TestWriter::with_max_size_and_error_kind(3, io::ErrorKind::Other); // write within bounds assert_eq!(1, writer.write(&[1]).unwrap()); // outside of bounds assert_eq!(io::ErrorKind::Other, writer.write(&[1,2,3]).unwrap_err().kind()); }etherparse-0.13.0/tests/transport/icmp.rs000064400000000000000000000026371046102023000165530ustar 00000000000000use super::super::*; use proptest::prelude::*; mod icmp_echo_header { use super::*; proptest!{ #[test] fn to_bytes( id in any::(), seq in any::() ) { let id_bytes = id.to_be_bytes(); let seq_bytes = seq.to_be_bytes(); assert_eq!( IcmpEchoHeader{ id, seq }.to_bytes(), [ id_bytes[0], id_bytes[1], seq_bytes[0], seq_bytes[1] ] ); } #[test] fn from_bytes( bytes in any::<[u8;4]>() ) { assert_eq!( IcmpEchoHeader::from_bytes(bytes), IcmpEchoHeader { id: u16::from_be_bytes([bytes[0], bytes[1]]), seq: u16::from_be_bytes([bytes[2], bytes[3]]) } ); } #[test] fn clone_eq( id in any::(), seq in any::() ) { let value = IcmpEchoHeader{ id, seq }; assert_eq!(value.clone(), value); } #[test] fn debug( id in any::(), seq in any::() ) { assert_eq!( format!("{:?}", IcmpEchoHeader{ id, seq }), format!("IcmpEchoHeader {{ id: {:?}, seq: {:?} }}", id, seq) ); } } }etherparse-0.13.0/tests/transport/icmpv4.proptest-regressions000064400000000000000000000010341046102023000226100ustar 00000000000000# Seeds for failure cases proptest has generated in the past. It is # automatically read and these particular cases re-run before any # novel cases are generated. # # It is recommended to check this file in to source control so that # everyone who runs the test benefits from these saved cases. cc 5a34ecd23fb5b11554b661938b02f46d9347ba71e61cc2893fd2702140d9fe6c # shrinks to icmpv4_type = TimestampRequest(TimestampMessage { id: 0, seq: 0, originate_timestamp: 0, receive_timestamp: 0, transmit_timestamp: 0 }), checksum = 0, payload = [] etherparse-0.13.0/tests/transport/icmpv4.rs000064400000000000000000002005251046102023000170210ustar 00000000000000use super::super::*; use proptest::prelude::*; use etherparse::icmpv4::*; #[test] fn constants() { // icmp type numbers according to // https://www.iana.org/assignments/icmp-parameters/icmp-parameters.xhtml#icmp-parameters-types assert_eq!(TYPE_ECHO_REPLY, 0); assert_eq!(TYPE_DEST_UNREACH, 3); assert_eq!(TYPE_SOURCE_QUENCH, 4); assert_eq!(TYPE_REDIRECT, 5); assert_eq!(TYPE_ALTERNATE_HOST_ADDRESS, 6); assert_eq!(TYPE_ECHO_REQUEST, 8); assert_eq!(TYPE_ROUTER_ADVERTISEMENT, 9); assert_eq!(TYPE_ROUTER_SOLICITATION, 10); assert_eq!(TYPE_TIME_EXCEEDED, 11); assert_eq!(TYPE_PARAMETER_PROBLEM, 12); assert_eq!(TYPE_TIMESTAMP, 13); assert_eq!(TYPE_TIMESTAMP_REPLY, 14); assert_eq!(TYPE_INFO_REQUEST, 15); assert_eq!(TYPE_INFO_REPLY, 16); assert_eq!(TYPE_ADDRESS, 17); assert_eq!(TYPE_ADDRESSREPLY, 18); // destination unreachable code numbers according to // https://www.iana.org/assignments/icmp-parameters/icmp-parameters.xhtml#icmp-parameters-codes-3 assert_eq!(0, CODE_DST_UNREACH_NET); assert_eq!(1, CODE_DST_UNREACH_HOST); assert_eq!(2, CODE_DST_UNREACH_PROTOCOL); assert_eq!(3, CODE_DST_UNREACH_PORT); assert_eq!(4, CODE_DST_UNREACH_NEED_FRAG); assert_eq!(5, CODE_DST_UNREACH_SOURCE_ROUTE_FAILED); assert_eq!(6, CODE_DST_UNREACH_NET_UNKNOWN); assert_eq!(7, CODE_DST_UNREACH_HOST_UNKNOWN); assert_eq!(8, CODE_DST_UNREACH_ISOLATED); assert_eq!(9, CODE_DST_UNREACH_NET_PROHIB); assert_eq!(10, CODE_DST_UNREACH_HOST_PROHIB); assert_eq!(11, CODE_DST_UNREACH_TOS_NET); assert_eq!(12, CODE_DST_UNREACH_TOS_HOST); assert_eq!(13, CODE_DST_UNREACH_FILTER_PROHIB); assert_eq!(14, CODE_DST_UNREACH_HOST_PRECEDENCE_VIOLATION); assert_eq!(15, CODE_DST_UNREACH_PRECEDENCE_CUTOFF); // redirect code numbers according to // https://www.iana.org/assignments/icmp-parameters/icmp-parameters.xhtml#icmp-parameters-codes-5 assert_eq!(0, CODE_REDIRECT_FOR_NETWORK); assert_eq!(1, CODE_REDIRECT_FOR_HOST); assert_eq!(2, CODE_REDIRECT_TYPE_OF_SERVICE_AND_NETWORK); assert_eq!(3, CODE_REDIRECT_TYPE_OF_SERVICE_AND_HOST); // time exceeded code numbers according to // https://www.iana.org/assignments/icmp-parameters/icmp-parameters.xhtml#icmp-parameters-codes-11 assert_eq!(0, CODE_TIME_EXCEEDED_TTL_EXCEEDED_IN_TRANSIT); assert_eq!(1, CODE_TIME_EXCEEDED_FRAG_REASSEMBLY_TIME_EXCEEDED); // parameter problem code numbers according to // https://www.iana.org/assignments/icmp-parameters/icmp-parameters.xhtml#icmp-parameters-codes-12 assert_eq!(0, CODE_PARAMETER_PROBLEM_POINTER_INDICATES_ERROR); assert_eq!(1, CODE_PARAMETER_PROBLEM_MISSING_REQUIRED_OPTION); assert_eq!(2, CODE_PARAMETER_PROBLEM_BAD_LENGTH); } mod dest_unreachable_header { use super::*; fn conversion_values(next_hop_mtu: u16) -> [(u8, DestUnreachableHeader); 16] { use DestUnreachableHeader::*; [ (CODE_DST_UNREACH_NET, Network), (CODE_DST_UNREACH_HOST, Host), (CODE_DST_UNREACH_PROTOCOL, Protocol), (CODE_DST_UNREACH_PORT, Port), (CODE_DST_UNREACH_NEED_FRAG, FragmentationNeeded{ next_hop_mtu }), (CODE_DST_UNREACH_SOURCE_ROUTE_FAILED, SourceRouteFailed), (CODE_DST_UNREACH_NET_UNKNOWN, NetworkUnknown), (CODE_DST_UNREACH_HOST_UNKNOWN, HostUnknown), (CODE_DST_UNREACH_ISOLATED, Isolated), (CODE_DST_UNREACH_NET_PROHIB, NetworkProhibited), (CODE_DST_UNREACH_HOST_PROHIB, HostProhibited), (CODE_DST_UNREACH_TOS_NET, TosNetwork), (CODE_DST_UNREACH_TOS_HOST, TosHost), (CODE_DST_UNREACH_FILTER_PROHIB, FilterProhibited), (CODE_DST_UNREACH_HOST_PRECEDENCE_VIOLATION, HostPrecedenceViolation), (CODE_DST_UNREACH_PRECEDENCE_CUTOFF, PrecedenceCutoff), ] } proptest! { #[test] fn from_values( next_hop_mtu in any::(), ) { // valid values { let valid_values = conversion_values(next_hop_mtu); for t in valid_values { assert_eq!(Some(t.1), DestUnreachableHeader::from_values(t.0, next_hop_mtu)); } } // invalid values for code_u8 in 16u8..=u8::MAX { assert_eq!(None, DestUnreachableHeader::from_values(code_u8, next_hop_mtu)); } } } proptest! { #[test] fn code_u8( next_hop_mtu in any::(), ) { let valid_values = conversion_values(next_hop_mtu); for t in valid_values { assert_eq!(t.0, t.1.code_u8()); } } } #[test] fn clone_eq() { use DestUnreachableHeader::*; let values = [ Network, Host, Protocol, Port, FragmentationNeeded { next_hop_mtu: 0 }, SourceRouteFailed, NetworkUnknown, HostUnknown, Isolated, NetworkProhibited, HostProhibited, TosNetwork, TosHost, FilterProhibited, HostPrecedenceViolation, PrecedenceCutoff, ]; for value in values { assert_eq!(value.clone(), value); } } #[test] fn debug() { use DestUnreachableHeader::*; let tests = [ ("Network", Network), ("Host", Host), ("Protocol", Protocol), ("Port", Port), ( "FragmentationNeeded { next_hop_mtu: 0 }", FragmentationNeeded { next_hop_mtu: 0 }, ), ("SourceRouteFailed", SourceRouteFailed), ("NetworkUnknown", NetworkUnknown), ("HostUnknown", HostUnknown), ("Isolated", Isolated), ("NetworkProhibited", NetworkProhibited), ("HostProhibited", HostProhibited), ("TosNetwork", TosNetwork), ("TosHost", TosHost), ("FilterProhibited", FilterProhibited), ("HostPrecedenceViolation", HostPrecedenceViolation), ("PrecedenceCutoff", PrecedenceCutoff), ]; for t in tests { assert_eq!(t.0, format!("{:?}", t.1)); } } } mod redirect_code { use super::*; use etherparse::icmpv4::RedirectCode::*; #[test] fn from_u8() { let tests = [ (CODE_REDIRECT_FOR_NETWORK, RedirectForNetwork), (CODE_REDIRECT_FOR_HOST, RedirectForHost), (CODE_REDIRECT_TYPE_OF_SERVICE_AND_NETWORK, RedirectForTypeOfServiceAndNetwork), (CODE_REDIRECT_TYPE_OF_SERVICE_AND_HOST, RedirectForTypeOfServiceAndHost), ]; for t in tests { assert_eq!(Some(t.1), RedirectCode::from_u8(t.0)); } for code_u8 in 4..=u8::MAX { assert_eq!(None, RedirectCode::from_u8(code_u8)); } } #[test] fn code_u8() { let tests = [ (CODE_REDIRECT_FOR_NETWORK, RedirectForNetwork), (CODE_REDIRECT_FOR_HOST, RedirectForHost), (CODE_REDIRECT_TYPE_OF_SERVICE_AND_NETWORK, RedirectForTypeOfServiceAndNetwork), (CODE_REDIRECT_TYPE_OF_SERVICE_AND_HOST, RedirectForTypeOfServiceAndHost), ]; for t in tests { assert_eq!(t.1.code_u8(), t.0); } } #[test] fn clone_eq() { let tests = [ RedirectForNetwork, RedirectForHost, RedirectForTypeOfServiceAndNetwork, RedirectForTypeOfServiceAndHost, ]; for t in tests { assert_eq!(t.clone(), t); } } #[test] fn debug() { let tests = [ ("RedirectForNetwork", RedirectForNetwork), ("RedirectForHost", RedirectForHost), ("RedirectForTypeOfServiceAndNetwork", RedirectForTypeOfServiceAndNetwork), ("RedirectForTypeOfServiceAndHost", RedirectForTypeOfServiceAndHost), ]; for t in tests { assert_eq!(t.0, format!("{:?}", t.1)); } } } mod redirect_header { use super::*; use etherparse::icmpv4::RedirectCode::*; #[test] fn clone_eq() { let v = RedirectHeader{ code: RedirectForNetwork, gateway_internet_address: [0;4], }; assert_eq!(v.clone(), v); } #[test] fn debug() { let v = RedirectHeader{ code: RedirectForNetwork, gateway_internet_address: [0;4], }; assert_eq!( format!("{:?}", v), format!( "RedirectHeader {{ code: {:?}, gateway_internet_address: {:?} }}", v.code, v.gateway_internet_address ) ); } } mod time_exceeded_code { use super::*; use etherparse::icmpv4::TimeExceededCode::*; #[test] fn from_u8() { assert_eq!( TimeExceededCode::from_u8(CODE_TIME_EXCEEDED_TTL_EXCEEDED_IN_TRANSIT), Some(TtlExceededInTransit) ); assert_eq!( TimeExceededCode::from_u8(CODE_TIME_EXCEEDED_FRAG_REASSEMBLY_TIME_EXCEEDED), Some(FragmentReassemblyTimeExceeded) ); for code_u8 in 2..=u8::MAX { assert_eq!(None, TimeExceededCode::from_u8(code_u8)); } } #[test] fn code_u8() { assert_eq!(TtlExceededInTransit.code_u8(), CODE_TIME_EXCEEDED_TTL_EXCEEDED_IN_TRANSIT); assert_eq!(FragmentReassemblyTimeExceeded.code_u8(), CODE_TIME_EXCEEDED_FRAG_REASSEMBLY_TIME_EXCEEDED); } #[test] fn debug() { let values = [ ("TtlExceededInTransit", TtlExceededInTransit), ("FragmentReassemblyTimeExceeded", FragmentReassemblyTimeExceeded), ]; for (expected, input) in values { assert_eq!(expected, format!("{:?}", input)); } } #[test] fn clone_eq() { let values = [ TtlExceededInTransit, FragmentReassemblyTimeExceeded, ]; for value in values { assert_eq!(value.clone(), value); } } } mod timestamp_message { use super::*; #[test] fn constants() { assert_eq!(20, TimestampMessage::SERIALIZED_SIZE); } proptest!{ #[test] fn from_bytes(bytes in any::<[u8;16]>()) { assert_eq!( TimestampMessage::from_bytes(bytes), TimestampMessage{ id: u16::from_be_bytes([bytes[0], bytes[1]]), seq: u16::from_be_bytes([bytes[2], bytes[3]]), originate_timestamp: u32::from_be_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]), receive_timestamp: u32::from_be_bytes([bytes[8], bytes[9], bytes[10], bytes[11]]), transmit_timestamp: u32::from_be_bytes([bytes[12], bytes[13], bytes[14], bytes[15]]), } ); } } #[test] fn clone_eq() { let v = TimestampMessage { id: 0, seq: 0, originate_timestamp: 0, receive_timestamp: 0, transmit_timestamp: 0, }; assert_eq!(v.clone(), v); } #[test] fn debug() { let v = TimestampMessage { id: 0, seq: 0, originate_timestamp: 0, receive_timestamp: 0, transmit_timestamp: 0, }; assert_eq!( format!("{:?}", v), format!( "TimestampMessage {{ id: {:?}, seq: {:?}, originate_timestamp: {:?}, receive_timestamp: {:?}, transmit_timestamp: {:?} }}", v.id, v.seq, v.originate_timestamp, v.receive_timestamp, v.transmit_timestamp, ) ); } } mod parameter_problem_header { use super::*; use ParameterProblemHeader::*; proptest!{ #[test] fn from_values(pointer in any::()) { { let tests = [ (CODE_PARAMETER_PROBLEM_POINTER_INDICATES_ERROR, PointerIndicatesError(pointer)), (CODE_PARAMETER_PROBLEM_MISSING_REQUIRED_OPTION, MissingRequiredOption), (CODE_PARAMETER_PROBLEM_BAD_LENGTH, BadLength), ]; for t in tests { assert_eq!(Some(t.1), ParameterProblemHeader::from_values(t.0, pointer)); } } for code_u8 in 3..=u8::MAX { assert_eq!(None, ParameterProblemHeader::from_values(code_u8, pointer)); } } } #[test] fn clone_eq() { let tests = [ PointerIndicatesError(0), MissingRequiredOption, BadLength, ]; for t in tests { assert_eq!(t.clone(), t); } } #[test] fn debug() { let tests = [ ("PointerIndicatesError(0)", PointerIndicatesError(0)), ("MissingRequiredOption", MissingRequiredOption), ("BadLength", BadLength), ]; for t in tests { assert_eq!(t.0, format!("{:?}", t.1)); } } } mod icmpv4_type { use super::*; use Icmpv4Type::*; #[test] fn header_len() { let dummy_ts = TimestampMessage{ id: 0, seq: 0, originate_timestamp: 0, receive_timestamp: 0, transmit_timestamp: 0, }; let dummy_echo = IcmpEchoHeader{ id: 0, seq: 0, }; let dummy_redirect = RedirectHeader{ code: RedirectCode::RedirectForNetwork, gateway_internet_address: [0;4], }; let tests = [ (8, Unknown{type_u8: 0, code_u8: 0, bytes5to8: [0;4]}), (8, EchoReply(dummy_echo)), (8, DestinationUnreachable(DestUnreachableHeader::Network)), (8, Redirect(dummy_redirect)), (8, EchoRequest(dummy_echo)), (8, TimeExceeded(TimeExceededCode::TtlExceededInTransit)), (8, ParameterProblem(ParameterProblemHeader::BadLength)), (20, TimestampRequest(dummy_ts.clone())), (20, TimestampReply(dummy_ts)), ]; for t in tests { assert_eq!(t.0, t.1.header_len()); } } #[test] fn fixed_payload_size() { use Icmpv4Type::*; let dummy_ts = TimestampMessage{ id: 0, seq: 0, originate_timestamp: 0, receive_timestamp: 0, transmit_timestamp: 0, }; let dummy_echo = IcmpEchoHeader{ id: 0, seq: 0, }; let dummy_redirect = RedirectHeader{ code: RedirectCode::RedirectForNetwork, gateway_internet_address: [0;4], }; let tests = [ (None, Unknown{type_u8: 0, code_u8: 0, bytes5to8: [0;4]}), (None, EchoReply(dummy_echo)), (None, DestinationUnreachable(DestUnreachableHeader::Network)), (None, Redirect(dummy_redirect)), (None, EchoRequest(dummy_echo)), (None, TimeExceeded(TimeExceededCode::TtlExceededInTransit)), (None, ParameterProblem(ParameterProblemHeader::BadLength)), (Some(0), TimestampRequest(dummy_ts.clone())), (Some(0), TimestampReply(dummy_ts)), ]; for t in tests { assert_eq!(t.0, t.1.fixed_payload_size()); } } proptest!{ #[test] fn calc_checksum( dest_unreach_code_u8 in 0u8..=15, next_hop_mtu in any::(), redirect_code_u8 in 0u8..=3, gateway_internet_address in any::<[u8;4]>(), time_exceeded_code_u8 in 0u8..=1, id in any::(), seq in any::(), originate_timestamp in any::(), receive_timestamp in any::(), transmit_timestamp in any::(), param_problem_code_u8 in 0u8..=2, pointer in any::(), unknown_type_u8 in any::(), unknown_code_u8 in any::(), bytes5to8 in any::<[u8;4]>(), payload in proptest::collection::vec(any::(), 0..1024) ) { let ts = TimestampMessage{ id, seq, originate_timestamp, receive_timestamp, transmit_timestamp, }; let echo = IcmpEchoHeader{ id, seq, }; let redirect = RedirectHeader{ code: RedirectCode::from_u8(redirect_code_u8).unwrap(), gateway_internet_address, }; let dest_unreach = DestUnreachableHeader::from_values(dest_unreach_code_u8, next_hop_mtu).unwrap(); let param_prob = ParameterProblemHeader::from_values(param_problem_code_u8, pointer).unwrap(); let values = [ Unknown { type_u8: unknown_type_u8, code_u8: unknown_code_u8, bytes5to8: bytes5to8, }, EchoReply(echo.clone()), DestinationUnreachable(dest_unreach), Redirect(redirect), EchoRequest(echo), TimeExceeded(TimeExceededCode::from_u8(time_exceeded_code_u8).unwrap()), ParameterProblem(param_prob), TimestampRequest(ts.clone()), TimestampReply(ts), ]; for t in values { let bytes = Icmpv4Header{ icmp_type: t.clone(), checksum: 0, // use zero so the checksum calculation from the bytes works }.to_bytes(); let expected = etherparse::checksum::Sum16BitWords::new() .add_slice(bytes.as_ref()) .add_slice(&payload) .ones_complement() .to_be(); assert_eq!(expected, t.calc_checksum(&payload)); } } } #[test] fn clone_eq() { let dummy_ts = TimestampMessage{ id: 0, seq: 0, originate_timestamp: 0, receive_timestamp: 0, transmit_timestamp: 0, }; let dummy_echo = IcmpEchoHeader{ id: 0, seq: 0, }; let dummy_redirect = RedirectHeader{ code: RedirectCode::RedirectForNetwork, gateway_internet_address: [0;4], }; let tests = [ Unknown{type_u8: 0, code_u8: 0, bytes5to8: [0;4]}, EchoReply(dummy_echo), DestinationUnreachable(DestUnreachableHeader::Network), Redirect(dummy_redirect), EchoRequest(dummy_echo), TimeExceeded(TimeExceededCode::TtlExceededInTransit), ParameterProblem(ParameterProblemHeader::BadLength), TimestampRequest(dummy_ts.clone()), TimestampReply(dummy_ts), ]; for t in tests { assert_eq!(t.clone(), t); } } #[test] fn debug() { let dummy_ts = TimestampMessage{ id: 0, seq: 0, originate_timestamp: 0, receive_timestamp: 0, transmit_timestamp: 0, }; let dummy_echo = IcmpEchoHeader{ id: 0, seq: 0, }; assert_eq!( format!("{:?}", Unknown{type_u8: 0, code_u8: 0, bytes5to8: [0;4]}), format!( "Unknown {{ type_u8: {:?}, code_u8: {:?}, bytes5to8: {:?} }}", 0u8, 0u8, [0u8;4] ) ); assert_eq!( format!("{:?}", EchoReply(dummy_echo)), format!("EchoReply({:?})", dummy_echo) ); assert_eq!( format!("{:?}", DestinationUnreachable(DestUnreachableHeader::Network)), format!("DestinationUnreachable({:?})", DestUnreachableHeader::Network) ); { let dummy_redirect = RedirectHeader{ code: RedirectCode::RedirectForNetwork, gateway_internet_address: [0;4], }; assert_eq!( format!("{:?}", Redirect(dummy_redirect.clone())), format!("Redirect({:?})", dummy_redirect) ); } assert_eq!( format!("{:?}", EchoRequest(dummy_echo)), format!("EchoRequest({:?})", dummy_echo) ); assert_eq!( format!("{:?}", TimeExceeded(TimeExceededCode::TtlExceededInTransit)), format!("TimeExceeded({:?})", TimeExceededCode::TtlExceededInTransit) ); assert_eq!( format!("{:?}", ParameterProblem(ParameterProblemHeader::BadLength)), format!("ParameterProblem({:?})", ParameterProblemHeader::BadLength) ); assert_eq!( format!("{:?}", TimestampRequest(dummy_ts.clone())), format!("TimestampRequest({:?})", dummy_ts) ); assert_eq!( format!("{:?}", TimestampReply(dummy_ts.clone())), format!("TimestampReply({:?})", dummy_ts) ); } } mod icmpv4_header { use super::*; #[test] fn constants() { assert_eq!(8, Icmpv4Header::MIN_SERIALIZED_SIZE); assert_eq!(20, Icmpv4Header::MAX_SERIALIZED_SIZE); } proptest!{ #[test] fn new(icmpv4_type in icmpv4_type_any()) { assert_eq!( Icmpv4Header { icmp_type: icmpv4_type.clone(), checksum: 0, }, Icmpv4Header::new(icmpv4_type) ); } } proptest!{ #[test] fn with_checksum( icmpv4_type in icmpv4_type_any(), payload in proptest::collection::vec(any::(), 0..1024), ) { assert_eq!( Icmpv4Header { icmp_type: icmpv4_type.clone(), checksum: icmpv4_type.calc_checksum(&payload), }, Icmpv4Header::with_checksum(icmpv4_type, &payload) ); } } proptest!{ #[test] fn from_slice( icmpv4_type in icmpv4_type_any(), checksum in any::(), payload in proptest::collection::vec(any::(), 0..1024), ) { use Icmpv4Type::*; // ok case let header = Icmpv4Header { icmp_type: icmpv4_type.clone(), checksum: checksum, }; let buffer = { let mut buffer = Vec::with_capacity(header.header_len() + payload.len()); buffer.extend_from_slice(&header.to_bytes()); match icmpv4_type { // skip the payoad for the timestamp request (those don't have a payload) TimestampRequest(_) | TimestampReply(_) => {}, _ => { buffer.extend_from_slice(&[0u8;36]); } } buffer }; { let (actual, rest) = Icmpv4Header::from_slice(&buffer).unwrap(); assert_eq!(actual, header); assert_eq!(rest, &buffer[header.header_len()..]); } // error case for bad_len in 0..header.header_len() { assert_matches!( Icmpv4Header::from_slice(&buffer[..bad_len]), Err(_) ); } } } proptest!{ #[test] fn read( non_timestamp_type in any::().prop_filter( "type must be a non timestamp type", |v| (*v != icmpv4::TYPE_TIMESTAMP_REPLY && *v != icmpv4::TYPE_TIMESTAMP) ), non_zero_code in 1u8..=u8::MAX, bytes in any::<[u8;icmpv4::TimestampMessage::SERIALIZED_SIZE]>() ) { for (type_u8, code_u8) in [ // non timestamp (non_timestamp_type, bytes[1]), // timestamp with zero code (TYPE_TIMESTAMP_REPLY, 0u8), (TYPE_TIMESTAMP, 0u8), // timestamp with non-zero code (TYPE_TIMESTAMP_REPLY, non_zero_code), (TYPE_TIMESTAMP, non_zero_code), ] { let b = { let mut b = bytes.clone(); b[0] = type_u8; b[1] = code_u8; b }; let expected = Icmpv4Header::from_slice(&b).unwrap().0; // ok case { let mut cursor = std::io::Cursor::new(&b); let actual = Icmpv4Header::read(&mut cursor).unwrap(); assert_eq!(expected, actual); assert_eq!(expected.header_len() as u64, cursor.position()); } // size error case for bad_len in 0..expected.header_len() { let mut cursor = std::io::Cursor::new(&(b.as_ref()[..bad_len])); assert_matches!( Icmpv4Header::read(&mut cursor), Err(_) ); } } } } proptest!{ #[test] fn write( icmpv4_type in icmpv4_type_any(), checksum in any::(), ) { let header = Icmpv4Header { icmp_type: icmpv4_type.clone(), checksum, }; // normal write { let bytes = header.to_bytes(); let mut buffer = Vec::with_capacity(header.header_len()); header.write(&mut buffer).unwrap(); assert_eq!(&bytes[..], &buffer[..]); } // error case for bad_len in 0..icmpv4_type.header_len() { let mut writer = TestWriter::with_max_size(bad_len); header.write(&mut writer).unwrap_err(); } } } proptest!{ #[test] fn header_len( checksum in any::(), icmpv4_type in icmpv4_type_any() ) { let header = Icmpv4Header{ icmp_type: icmpv4_type.clone(), checksum, }; assert_eq!(header.header_len(), icmpv4_type.header_len()); } } proptest!{ #[test] fn fixed_payload_size( checksum in any::(), icmpv4_type in icmpv4_type_any() ) { let header = Icmpv4Header{ icmp_type: icmpv4_type.clone(), checksum, }; assert_eq!(header.fixed_payload_size(), icmpv4_type.fixed_payload_size()); } } proptest!{ #[test] fn update_checksum( icmpv4_type in icmpv4_type_any(), checksum in any::(), payload in proptest::collection::vec(any::(), 0..1024), ) { let mut header = Icmpv4Header { icmp_type: icmpv4_type.clone(), checksum, }; header.update_checksum(&payload); assert_eq!(header.checksum, icmpv4_type.calc_checksum(&payload)); } } proptest!{ #[test] #[rustfmt::skip] fn to_bytes( checksum in any::(), next_hop_mtu in any::(), redirect_code_u8 in 0u8..=3, gateway_internet_address in any::<[u8;4]>(), time_exceeded_code_u8 in 0u8..=1, id in any::(), seq in any::(), originate_timestamp in any::(), receive_timestamp in any::(), transmit_timestamp in any::(), pointer in any::(), unknown_type_u8 in any::(), unknown_code_u8 in any::(), bytes5to8 in any::<[u8;4]>(), ) { use Icmpv4Type::*; use arrayvec::ArrayVec; let ts = TimestampMessage{ id, seq, originate_timestamp, receive_timestamp, transmit_timestamp, }; let ts_bytes = { let id_be = id.to_be_bytes(); let seq_be = seq.to_be_bytes(); let ot = originate_timestamp.to_be_bytes(); let rt = receive_timestamp.to_be_bytes(); let tt = transmit_timestamp.to_be_bytes(); [ 0, 0, 0, 0, id_be[0], id_be[1], seq_be[0], seq_be[1], ot[0], ot[1], ot[2], ot[3], rt[0], rt[1], rt[2], rt[3], tt[0], tt[1], tt[2], tt[3], ] }; let echo = IcmpEchoHeader{ id, seq, }; let redirect = RedirectHeader{ code: RedirectCode::from_u8(redirect_code_u8).unwrap(), gateway_internet_address, }; // test values with no need for subtests let random_values = [ ( Unknown { type_u8: unknown_type_u8, code_u8: unknown_code_u8, bytes5to8: bytes5to8, }, 8, [ unknown_type_u8, unknown_code_u8, 0, 0, bytes5to8[0], bytes5to8[1], bytes5to8[2], bytes5to8[3], 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ], ), ( EchoReply(echo.clone()), 8, { let id_be = id.to_be_bytes(); let seq_be = seq.to_be_bytes(); [ TYPE_ECHO_REPLY, 0, 0, 0, id_be[0], id_be[1], seq_be[0], seq_be[1], 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ] } ), ( Redirect(redirect), 8, { let gip = gateway_internet_address; [ TYPE_REDIRECT, redirect_code_u8, 0, 0, gip[0], gip[1], gip[2], gip[3], 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ] }, ), ( EchoRequest(echo.clone()), 8, { let id_be = id.to_be_bytes(); let seq_be = seq.to_be_bytes(); [ TYPE_ECHO_REQUEST, 0, 0, 0, id_be[0], id_be[1], seq_be[0], seq_be[1], 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ] } ), ( TimeExceeded(TimeExceededCode::from_u8(time_exceeded_code_u8).unwrap()), 8, [ TYPE_TIME_EXCEEDED, time_exceeded_code_u8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ], ), ( TimestampRequest(ts.clone()), 20, { let mut b = ts_bytes; b[0] = TYPE_TIMESTAMP; b } ), ( TimestampReply(ts), 20, { let mut b = ts_bytes; b[0] = TYPE_TIMESTAMP_REPLY; b } ), ]; for t in random_values { let actual = Icmpv4Header{ icmp_type: t.0.clone(), checksum, }.to_bytes(); let mut expected = ArrayVec::from(t.2); unsafe { expected.set_len(t.1) } let checksum_be = checksum.to_be_bytes(); expected[2] = checksum_be[0]; expected[3] = checksum_be[1]; assert_eq!(expected, actual); } // destination unreachable { use DestUnreachableHeader::*; let tests = [ (CODE_DST_UNREACH_NET, [0;2], Network), (CODE_DST_UNREACH_HOST, [0;2], Host), (CODE_DST_UNREACH_PROTOCOL, [0;2], Protocol), (CODE_DST_UNREACH_PORT, [0;2], Port), (CODE_DST_UNREACH_NEED_FRAG, next_hop_mtu.to_be_bytes(), FragmentationNeeded{ next_hop_mtu }), (CODE_DST_UNREACH_SOURCE_ROUTE_FAILED, [0;2], SourceRouteFailed), (CODE_DST_UNREACH_NET_UNKNOWN, [0;2], NetworkUnknown), (CODE_DST_UNREACH_HOST_UNKNOWN, [0;2], HostUnknown), (CODE_DST_UNREACH_ISOLATED, [0;2], Isolated), (CODE_DST_UNREACH_NET_PROHIB, [0;2], NetworkProhibited), (CODE_DST_UNREACH_HOST_PROHIB, [0;2], HostProhibited), (CODE_DST_UNREACH_TOS_NET, [0;2], TosNetwork), (CODE_DST_UNREACH_TOS_HOST, [0;2], TosHost), (CODE_DST_UNREACH_FILTER_PROHIB, [0;2], FilterProhibited), (CODE_DST_UNREACH_HOST_PRECEDENCE_VIOLATION, [0;2], HostPrecedenceViolation), (CODE_DST_UNREACH_PRECEDENCE_CUTOFF, [0;2], PrecedenceCutoff), ]; for t in tests { let checksum_be = checksum.to_be_bytes(); let mut expected = ArrayVec::from([ TYPE_DEST_UNREACH, t.0, checksum_be[0], checksum_be[1], 0, 0, t.1[0], t.1[1], 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]); unsafe { expected.set_len(8); } let actual = Icmpv4Header{ icmp_type: DestinationUnreachable(t.2.clone()), checksum, }.to_bytes(); assert_eq!(expected, actual); } } // parameter problem { use ParameterProblemHeader::*; let tests = [ (CODE_PARAMETER_PROBLEM_POINTER_INDICATES_ERROR, pointer, PointerIndicatesError(pointer)), (CODE_PARAMETER_PROBLEM_MISSING_REQUIRED_OPTION, 0, MissingRequiredOption), (CODE_PARAMETER_PROBLEM_BAD_LENGTH, 0, BadLength), ]; for t in tests { let checksum_be = checksum.to_be_bytes(); let mut expected = ArrayVec::from([ TYPE_PARAMETER_PROBLEM, t.0, checksum_be[0], checksum_be[1], t.1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]); unsafe { expected.set_len(8); } let actual = Icmpv4Header{ icmp_type: ParameterProblem(t.2.clone()), checksum, }.to_bytes(); assert_eq!(expected, actual); } } } } #[test] fn clone_eq() { use Icmpv4Type::*; let header = Icmpv4Header { icmp_type: ParameterProblem(ParameterProblemHeader::BadLength), checksum: 0, }; assert_eq!(header.clone(), header); } #[test] fn debug() { use Icmpv4Type::*; let header = Icmpv4Header { icmp_type: ParameterProblem(ParameterProblemHeader::BadLength), checksum: 0, }; assert_eq!( format!("{:?}", header), format!("Icmpv4Header {{ icmp_type: {:?}, checksum: {:?} }}", header.icmp_type, header.checksum) ); } } mod icmpv4_slice { use super::*; #[test] fn from_slice() { use ReadError::*; // normal case { let bytes = [0u8;8]; let slice = Icmpv4Slice::from_slice(&bytes).unwrap(); assert_eq!(slice.slice(), &bytes); } // smaller then min size error for bad_len in 0..8 { let bytes = [0u8;8]; assert_matches!( Icmpv4Slice::from_slice(&bytes[..bad_len]), Err(UnexpectedEndOfSlice(Icmpv4Header::MIN_SERIALIZED_SIZE)) ); } // timestamp tests for ts_type_u8 in [TYPE_TIMESTAMP, TYPE_TIMESTAMP_REPLY] { let bytes = { let mut bytes = [0u8;26]; bytes[0] = ts_type_u8; bytes }; // valid timestamps { let slice = Icmpv4Slice::from_slice(&bytes[..20]).unwrap(); assert_eq!(slice.slice(), &bytes[..20]); } // too short timestamps for bad_len in 8..20 { assert_matches!( Icmpv4Slice::from_slice(&bytes[..bad_len]), Err(UnexpectedLenOfSlice{ expected: TimestampMessage::SERIALIZED_SIZE, actual: _ }) ); } // too large timestamps for bad_len in 21..26 { assert_matches!( Icmpv4Slice::from_slice(&bytes[..bad_len]), Err(UnexpectedLenOfSlice{ expected: TimestampMessage::SERIALIZED_SIZE, actual: _ }) ); } // timestamp with a non zero code for code_u8 in 1..=u8::MAX { let mut bytes = [0u8;20]; bytes[0] = ts_type_u8; bytes[1] = code_u8; let slice = Icmpv4Slice::from_slice(&bytes[..8]).unwrap(); assert_eq!(slice.slice(), &bytes[..8]); } } } proptest!{ #[test] fn header(bytes in any::<[u8;20]>()) { let slice = Icmpv4Slice::from_slice(&bytes).unwrap(); assert_eq!( Icmpv4Header { icmp_type: slice.icmp_type(), checksum: slice.checksum(), }, slice.header() ); } } #[test] fn header_len() { use Icmpv4Type::*; let dummy_ts = TimestampMessage{ id: 0, seq: 0, originate_timestamp: 0, receive_timestamp: 0, transmit_timestamp: 0, }; let dummy_echo = IcmpEchoHeader{ id: 0, seq: 0, }; let dummy_redirect = RedirectHeader{ code: RedirectCode::RedirectForNetwork, gateway_internet_address: [0;4], }; let tests = [ (Unknown{type_u8: u8::MAX, code_u8: 0, bytes5to8: [0;4]}), (EchoReply(dummy_echo)), (DestinationUnreachable(DestUnreachableHeader::Network)), (Redirect(dummy_redirect)), (EchoRequest(dummy_echo)), (TimeExceeded(TimeExceededCode::TtlExceededInTransit)), (ParameterProblem(ParameterProblemHeader::BadLength)), (TimestampRequest(dummy_ts.clone())), // check that a non zero code value return 8 (Unknown{type_u8: TYPE_TIMESTAMP, code_u8: 1, bytes5to8: [0;4]}), (TimestampReply(dummy_ts)), // check that a non zero code value return 8 (Unknown{type_u8: TYPE_TIMESTAMP_REPLY, code_u8: 1, bytes5to8: [0;4]}), ]; for t in tests { assert_eq!( t.header_len(), Icmpv4Slice::from_slice( &Icmpv4Header::new(t).to_bytes() ).unwrap().header_len() ); } } proptest!{ #[test] fn icmp_type(base_bytes in any::<[u8;20]>()) { use Icmpv4Type::*; let gen_bytes = |type_u8: u8, code_u8: u8| -> [u8;20] { let mut bytes = base_bytes; bytes[0] = type_u8; bytes[1] = code_u8; bytes }; let assert_unknown = |type_u8: u8, code_u8: u8| { let bytes = gen_bytes(type_u8, code_u8); let slice = Icmpv4Slice::from_slice(&bytes).unwrap(); assert_eq!( slice.icmp_type(), Unknown{ type_u8, code_u8, bytes5to8: slice.bytes5to8(), } ); }; // unknown types for type_u8 in 0..=u8::MAX{ match type_u8 { TYPE_ECHO_REPLY | TYPE_DEST_UNREACH | TYPE_REDIRECT | TYPE_ECHO_REQUEST | TYPE_TIME_EXCEEDED | TYPE_PARAMETER_PROBLEM | TYPE_TIMESTAMP | TYPE_TIMESTAMP_REPLY => {}, type_u8 => { assert_unknown(type_u8, base_bytes[1]); } } } // echo reply { // matching code { let bytes = gen_bytes(TYPE_ECHO_REPLY, 0); let slice = Icmpv4Slice::from_slice(&bytes).unwrap(); assert_eq!( slice.icmp_type(), EchoReply(IcmpEchoHeader::from_bytes(slice.bytes5to8())) ); } // unknown code for unknow_code in 1..=u8::MAX { assert_unknown(TYPE_ECHO_REPLY, unknow_code); } } // destination unreachable { use DestUnreachableHeader::*; // trivial code values { let trivial_tests = [ (CODE_DST_UNREACH_NET, Network), (CODE_DST_UNREACH_HOST, Host), (CODE_DST_UNREACH_PROTOCOL, Protocol), (CODE_DST_UNREACH_PORT, Port), // need frag skipped as contains an additional value (CODE_DST_UNREACH_SOURCE_ROUTE_FAILED, SourceRouteFailed), (CODE_DST_UNREACH_NET_UNKNOWN, NetworkUnknown), (CODE_DST_UNREACH_HOST_UNKNOWN, HostUnknown), (CODE_DST_UNREACH_ISOLATED, Isolated), (CODE_DST_UNREACH_NET_PROHIB, NetworkProhibited), (CODE_DST_UNREACH_HOST_PROHIB, HostProhibited), (CODE_DST_UNREACH_TOS_NET, TosNetwork), (CODE_DST_UNREACH_TOS_HOST, TosHost), (CODE_DST_UNREACH_FILTER_PROHIB, FilterProhibited), (CODE_DST_UNREACH_HOST_PRECEDENCE_VIOLATION, HostPrecedenceViolation), (CODE_DST_UNREACH_PRECEDENCE_CUTOFF, PrecedenceCutoff), ]; for (code_u8, expected) in trivial_tests { let bytes = gen_bytes(TYPE_DEST_UNREACH, code_u8); let slice = Icmpv4Slice::from_slice(&bytes).unwrap(); assert_eq!( slice.icmp_type(), DestinationUnreachable(expected) ); } } // need frag { let bytes = gen_bytes(TYPE_DEST_UNREACH, CODE_DST_UNREACH_NEED_FRAG); let slice = Icmpv4Slice::from_slice(&bytes).unwrap(); assert_eq!( slice.icmp_type(), DestinationUnreachable(FragmentationNeeded { next_hop_mtu: u16::from_be_bytes([bytes[6], bytes[7]]) }) ); } // unknown codes for unknow_code in 16..=u8::MAX { assert_unknown(TYPE_ECHO_REPLY, unknow_code); } } // redirect { use RedirectCode::*; // known codes { let trivial_tests = [ (CODE_REDIRECT_FOR_NETWORK, RedirectForNetwork), (CODE_REDIRECT_FOR_HOST, RedirectForHost), (CODE_REDIRECT_TYPE_OF_SERVICE_AND_NETWORK, RedirectForTypeOfServiceAndNetwork), (CODE_REDIRECT_TYPE_OF_SERVICE_AND_HOST, RedirectForTypeOfServiceAndHost), ]; for (code_u8, expected) in trivial_tests { let bytes = gen_bytes(TYPE_REDIRECT, code_u8); let slice = Icmpv4Slice::from_slice(&bytes).unwrap(); assert_eq!( slice.icmp_type(), Redirect(RedirectHeader{ code: expected, gateway_internet_address: slice.bytes5to8(), }) ); } } // unknown codes for unknow_code in 4..=u8::MAX { assert_unknown(TYPE_REDIRECT, unknow_code); } } // echo request { // matching code { let bytes = gen_bytes(TYPE_ECHO_REQUEST, 0); let slice = Icmpv4Slice::from_slice(&bytes).unwrap(); assert_eq!( slice.icmp_type(), EchoRequest(IcmpEchoHeader::from_bytes(slice.bytes5to8())) ); } // unknown code for unknow_code in 1..=u8::MAX { assert_unknown(TYPE_ECHO_REQUEST, unknow_code); } } // time exceeded { use TimeExceededCode::*; // known codes { let trivial_tests = [ (CODE_TIME_EXCEEDED_TTL_EXCEEDED_IN_TRANSIT, TtlExceededInTransit), (CODE_TIME_EXCEEDED_FRAG_REASSEMBLY_TIME_EXCEEDED, FragmentReassemblyTimeExceeded), ]; for (code_u8, expected) in trivial_tests { let bytes = gen_bytes(TYPE_TIME_EXCEEDED, code_u8); let slice = Icmpv4Slice::from_slice(&bytes).unwrap(); assert_eq!( slice.icmp_type(), TimeExceeded(expected) ); } } // unknown code for unknow_code in 2..=u8::MAX { assert_unknown(TYPE_TIME_EXCEEDED, unknow_code); } } // parameter porblem { use ParameterProblemHeader::*; // trivial code values { let trivial_tests = [ (CODE_PARAMETER_PROBLEM_MISSING_REQUIRED_OPTION, MissingRequiredOption), (CODE_PARAMETER_PROBLEM_BAD_LENGTH, BadLength), ]; for (code_u8, expected) in trivial_tests { let bytes = gen_bytes(TYPE_PARAMETER_PROBLEM, code_u8); let slice = Icmpv4Slice::from_slice(&bytes).unwrap(); assert_eq!( slice.icmp_type(), ParameterProblem(expected) ); } } // with pointer { let bytes = gen_bytes(TYPE_PARAMETER_PROBLEM, CODE_PARAMETER_PROBLEM_POINTER_INDICATES_ERROR); let slice = Icmpv4Slice::from_slice(&bytes).unwrap(); assert_eq!( slice.icmp_type(), ParameterProblem(PointerIndicatesError(bytes[4])) ); } // unknown codes for unknow_code in 3..=u8::MAX { assert_unknown(TYPE_PARAMETER_PROBLEM, unknow_code); } } // timestamp { // matching code { let bytes = gen_bytes(TYPE_TIMESTAMP, 0); let slice = Icmpv4Slice::from_slice(&bytes).unwrap(); assert_eq!( slice.icmp_type(), TimestampRequest(TimestampMessage::from_bytes([ bytes[4], bytes[5], bytes[6], bytes[7], bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15], bytes[16], bytes[17], bytes[18], bytes[19], ])) ); } // unknown code for unknow_code in 1..=u8::MAX { assert_unknown(TYPE_TIMESTAMP, unknow_code); } } // timestamp reply { // matching code { let bytes = gen_bytes(TYPE_TIMESTAMP_REPLY, 0); let slice = Icmpv4Slice::from_slice(&bytes).unwrap(); assert_eq!( slice.icmp_type(), TimestampReply(TimestampMessage::from_bytes([ bytes[4], bytes[5], bytes[6], bytes[7], bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15], bytes[16], bytes[17], bytes[18], bytes[19], ])) ); } // unknown code for unknow_code in 1..=u8::MAX { assert_unknown(TYPE_TIMESTAMP_REPLY, unknow_code); } } } } proptest!{ #[test] fn type_u8(bytes in any::<[u8;20]>()) { assert_eq!( bytes[0], Icmpv4Slice::from_slice(&bytes).unwrap().type_u8(), ); } } proptest!{ #[test] fn code_u8(bytes in any::<[u8;20]>()) { assert_eq!( bytes[1], Icmpv4Slice::from_slice(&bytes).unwrap().code_u8(), ); } } proptest!{ #[test] fn checksum(bytes in any::<[u8;20]>()) { assert_eq!( u16::from_be_bytes([bytes[2], bytes[3]]), Icmpv4Slice::from_slice(&bytes).unwrap().checksum(), ); } } proptest!{ #[test] fn bytes5to8(bytes in any::<[u8;20]>()) { assert_eq!( [bytes[4], bytes[5], bytes[6], bytes[7]], Icmpv4Slice::from_slice(&bytes).unwrap().bytes5to8(), ); } } proptest!{ #[test] fn payload( payload in proptest::collection::vec(any::(), 8..26) ) { use Icmpv4Type::*; let dummy_ts = TimestampMessage{ id: 0, seq: 0, originate_timestamp: 0, receive_timestamp: 0, transmit_timestamp: 0, }; let dummy_echo = IcmpEchoHeader{ id: 0, seq: 0, }; let dummy_redirect = RedirectHeader{ code: RedirectCode::RedirectForNetwork, gateway_internet_address: [0;4], }; // tests with variable payloads { let var_tests = [ Unknown{type_u8: 0, code_u8: 0, bytes5to8: [0;4]}, EchoReply(dummy_echo), DestinationUnreachable(DestUnreachableHeader::Network), Redirect(dummy_redirect), EchoRequest(dummy_echo), TimeExceeded(TimeExceededCode::TtlExceededInTransit), ParameterProblem(ParameterProblemHeader::BadLength), // timestamps with non-zero code values Unknown{type_u8: TYPE_TIMESTAMP, code_u8: 1, bytes5to8: [0;4]}, Unknown{type_u8: TYPE_TIMESTAMP_REPLY, code_u8: 1, bytes5to8: [0;4]}, ]; for t in var_tests { let mut bytes = Vec::with_capacity(t.header_len() + payload.len()); Icmpv4Header::new(t.clone()).write(&mut bytes).unwrap(); bytes.extend_from_slice(&payload); assert_eq!( &payload[..], Icmpv4Slice::from_slice(&bytes).unwrap().payload() ); } } // tests with fixed payload sizes { let fixed_tests = [ (0, TimestampRequest(dummy_ts.clone())), (0, TimestampReply(dummy_ts)), ]; for t in fixed_tests { let mut bytes = Vec::with_capacity(t.1.header_len() + t.0); Icmpv4Header::new(t.1.clone()).write(&mut bytes).unwrap(); bytes.extend_from_slice(&payload[..t.0]); assert_eq!( &payload[..t.0], Icmpv4Slice::from_slice(&bytes).unwrap().payload() ); } } } } proptest!{ #[test] fn slice(bytes in proptest::collection::vec(any::(), 20..1024)) { let slice = if bytes[0] == TYPE_TIMESTAMP || bytes[0] == TYPE_TIMESTAMP_REPLY { &bytes[..20] } else { &bytes[..] }; assert_eq!( slice, Icmpv4Slice::from_slice(slice).unwrap().slice(), ); } } proptest!{ #[test] fn clone_eq(bytes in any::<[u8;20]>()) { let slice = Icmpv4Slice::from_slice(&bytes).unwrap(); assert_eq!(slice, slice.clone()); } } proptest!{ #[test] fn debug(bytes in any::<[u8;20]>()) { let slice = Icmpv4Slice::from_slice(&bytes).unwrap(); assert_eq!( format!("{:?}", slice), format!("Icmpv4Slice {{ slice: {:?} }}", &bytes[..]) ); } } } mod icmpv4_regression { use super::*; #[test] fn icmp4_echo_marshall_unmarshall() { let icmp4 = Icmpv4Header { icmp_type: Icmpv4Type::EchoRequest(IcmpEchoHeader { seq: 1, id: 2 }), checksum: 0, }; // serialize let mut buffer: Vec = Vec::with_capacity(256); icmp4.write(&mut buffer).unwrap(); let (new_icmp4, rest) = Icmpv4Header::from_slice(&buffer).unwrap(); assert_eq!(icmp4, new_icmp4); assert_eq!(rest.len(), 0); } #[test] fn ip4_echo_marshall_unmarshall() { let builder = PacketBuilder::ipv4( [192, 168, 1, 1], //source ip [192, 168, 1, 2], //desitionation ip 20) //time to life .icmpv4_echo_request(1, 2); let payload = [0xde, 0xad, 0xbe, 0xef]; //get some memory to store the result let mut result = Vec::::with_capacity(builder.size(payload.len())); //serialize builder.write(&mut result, &payload).unwrap(); let new_ip = PacketHeaders::from_ip_slice(&result).unwrap(); if let Some(TransportHeader::Icmpv4(hdr)) = new_ip.transport { if let Icmpv4Type::EchoRequest(echo) = hdr.icmp_type { assert_eq!(echo.id, 1); assert_eq!(echo.seq, 2); } else { panic!("Not an EchoRequest!?"); } } else { panic!("No transport header found!?") } } const ICMP4_ECHO_REQUEST_BYTES: [u8; 98] = [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x45, 0x00, 0x00, 0x54, 0x13, 0x6f, 0x40, 0x00, 0x40, 0x01, 0x29, 0x38, 0x7f, 0x00, 0x00, 0x01, 0x7f, 0x00, 0x00, 0x01, 0x08, 0x00, 0xc9, 0x99, 0x00, 0x03, 0x00, 0x01, 0x79, 0xc5, 0xd9, 0x61, 0x00, 0x00, 0x00, 0x00, 0x18, 0x68, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, ]; const ICMP4_ECHO_REPLY_BYTES: [u8; 98] = [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x45, 0x00, 0x00, 0x54, 0x13, 0x70, 0x00, 0x00, 0x40, 0x01, 0x69, 0x37, 0x7f, 0x00, 0x00, 0x01, 0x7f, 0x00, 0x00, 0x01, 0x00, 0x00, 0xd1, 0x99, 0x00, 0x03, 0x00, 0x01, 0x79, 0xc5, 0xd9, 0x61, 0x00, 0x00, 0x00, 0x00, 0x18, 0x68, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, ]; // real echo request/reply captured from tcpdump // ping 127.0.0.1 to 127.0.0.1 #[test] fn pcap_echo_session() { let request = PacketHeaders::from_ethernet_slice(&ICMP4_ECHO_REQUEST_BYTES).unwrap(); let request_icmp4 = request.transport.unwrap().icmpv4().unwrap(); match request_icmp4.icmp_type { Icmpv4Type::EchoRequest(echo) => { assert_eq!(echo.seq, 1); assert_eq!(echo.id, 3); // arbitrarily assigned by OS } _ => panic!(r#"Request didn't parse as ICMP4!?"#), } let reply = PacketHeaders::from_ethernet_slice(&ICMP4_ECHO_REPLY_BYTES).unwrap(); let reply_icmp4 = reply.transport.unwrap().icmpv4().unwrap(); match reply_icmp4.icmp_type { Icmpv4Type::EchoReply(echo) => { assert_eq!(echo.seq, 1); assert_eq!(echo.id, 3); // arbitrarily assigned by OS } _ => panic!(r#"Request didn't parse as ICMP4!?"#), } let request_iph = request.ip.unwrap(); let reply_iph = reply.ip.unwrap(); if let IpHeader::Version4(request_ip, _) = request_iph { if let IpHeader::Version4(reply_ip, _) = reply_iph { assert_eq!(reply_ip.source, request_ip.destination); assert_eq!(reply_ip.destination, request_ip.source); } else { panic!("reply ip not v4!?"); } } else { panic!("request ip not v4!?"); } } #[test] fn echo_request_slice() { let echo = SlicedPacket::from_ethernet(&ICMP4_ECHO_REQUEST_BYTES).unwrap(); use TransportSlice::*; let icmp4 = match echo.transport.unwrap() { Icmpv4(icmp4) => icmp4, Icmpv6(_) | Udp(_) | Tcp(_) | Unknown(_) => panic!("Misparsed header!"), }; assert!(matches!(icmp4.icmp_type(), Icmpv4Type::EchoRequest(_))); } #[test] fn verify_icmp4_checksum() { for (pkt, checksum) in [ (ICMP4_ECHO_REQUEST_BYTES, 0xc999), (ICMP4_ECHO_REPLY_BYTES, 0xd199), ] { // make sure we can unmarshall the correct checksum let request = PacketHeaders::from_ethernet_slice(&pkt).unwrap(); let mut icmp4 = request.transport.unwrap().icmpv4().unwrap(); let valid_checksum = icmp4.checksum; assert_ne!(valid_checksum, 0); assert_eq!(valid_checksum, checksum); // reset it and recalculate icmp4.checksum = 0; assert_eq!(icmp4.icmp_type.calc_checksum(request.payload), valid_checksum); } } // TTL unreachable from 'traceroute google.com' const ICMP4_TTL_EXCEEDED_BYTES: [u8; 94] = [ 0x98, 0x8d, 0x46, 0xc5, 0x03, 0x82, 0x60, 0xa4, 0xb7, 0x25, 0x4b, 0x84, 0x08, 0x00, 0x45, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x00, 0xfe, 0x01, 0x5c, 0x87, 0xd4, 0x9c, 0xc9, 0x72, 0xc0, 0xa8, 0x01, 0x6e, 0x0b, 0x00, 0x24, 0x29, 0x00, 0x00, 0x00, 0x00, 0x45, 0x00, 0x00, 0x3c, 0xe3, 0xaf, 0x00, 0x00, 0x01, 0x11, 0x14, 0x84, 0xc0, 0xa8, 0x01, 0x6e, 0xd8, 0xef, 0x26, 0x78, 0xc2, 0x8e, 0x82, 0x9f, 0x00, 0x28, 0x03, 0xed, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, ]; #[test] fn parse_icmp4_ttl_exceeded() { let ttl_exceeded = PacketHeaders::from_ethernet_slice(&ICMP4_TTL_EXCEEDED_BYTES).unwrap(); let ip_header = match ttl_exceeded.ip.unwrap() { IpHeader::Version4(ip4, _) => ip4, _ => panic!("Didn't parse inner v4 IP header!?"), }; assert_eq!( Ipv4Addr::from(ip_header.source), "212.156.201.114".parse::().unwrap() ); let icmp4 = ttl_exceeded.transport.unwrap().icmpv4().unwrap(); let icmp_bytes = icmp4.to_bytes(); assert_eq!(8, icmp_bytes.len()); assert_eq!(icmp_bytes[0], icmpv4::TYPE_TIME_EXCEEDED); assert_eq!(icmp_bytes[1], 0); // code assert_eq!(&icmp_bytes[4..], &[0; 4]); // TTL exceeded doesn't use this field // now unpack the bounced packet in the payload let embedded_pkt = PacketHeaders::from_ip_slice(ttl_exceeded.payload).unwrap(); let ip_header = match embedded_pkt.ip.unwrap() { IpHeader::Version4(ip4, _) => ip4, _ => panic!("Didn't parse inner v4 IP header!?"), }; use std::net::Ipv4Addr; assert_eq!( Ipv4Addr::from(ip_header.source), "192.168.1.110".parse::().unwrap() ); assert_eq!( Ipv4Addr::from(ip_header.destination), "216.239.38.120".parse::().unwrap() ); let udp_header = embedded_pkt.transport.unwrap().udp().unwrap(); assert_eq!(udp_header.source_port, 49806); // numbers read from wireshark assert_eq!(udp_header.destination_port, 33439); } const ICMP4_PORT_UNREACHABLE_BYTES: [u8; 70] = [ 0x98, 0x8d, 0x46, 0xc5, 0x03, 0x82, 0x60, 0xa4, 0xb7, 0x25, 0x4b, 0x84, 0x08, 0x00, 0x45, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x39, 0x01, 0xc0, 0x47, 0xd8, 0xef, 0x26, 0x78, 0xc0, 0xa8, 0x01, 0x6e, 0x03, 0x03, 0xb3, 0xb9, 0x00, 0x00, 0x00, 0x00, 0x45, 0x80, 0x00, 0x3c, 0xe3, 0xd2, 0x00, 0x00, 0x01, 0x11, 0x13, 0xe1, 0xc0, 0xa8, 0x01, 0x6e, 0xd8, 0xef, 0x26, 0x78, 0xb3, 0x4e, 0x82, 0xb2, 0x00, 0x28, 0x13, 0x1a, ]; #[test] fn icmp4_dst_unreachable() { let offset = 14 + 20 + 1; // ethernet + iphdr + icmp_type // test all of the unreachable codes to make sure the maps are right for code_u8 in 0..icmpv4::CODE_DST_UNREACH_PRECEDENCE_CUTOFF { let mut pkt = ICMP4_PORT_UNREACHABLE_BYTES.clone(); pkt[offset] = code_u8; // over write the code let parsed = PacketHeaders::from_ethernet_slice(&pkt).unwrap(); let icmp4 = parsed.transport.unwrap().icmpv4().unwrap(); if let Icmpv4Type::DestinationUnreachable(icmp_code) = icmp4.icmp_type { assert_eq!(code_u8, icmp_code.code_u8()); } else { panic!("Not destination unreachable!?"); } } } } etherparse-0.13.0/tests/transport/icmpv6.proptest-regressions000064400000000000000000000006571046102023000226240ustar 00000000000000# Seeds for failure cases proptest has generated in the past. It is # automatically read and these particular cases re-run before any # novel cases are generated. # # It is recommended to check this file in to source control so that # everyone who runs the test benefits from these saved cases. cc 55bd1154c311f3811677737988feb70774f6e5b164cd29b9ba1ea1a0aa51dc0d # shrinks to checksum = 0, rand_u32 = 0, rand_4bytes = [0, 0, 0, 0] etherparse-0.13.0/tests/transport/icmpv6.rs000064400000000000000000001465031046102023000170300ustar 00000000000000use super::super::*; use proptest::prelude::*; use etherparse::icmpv6::*; use arrayvec::ArrayVec; #[test] fn constants() { // type values according to // https://www.iana.org/assignments/icmpv6-parameters/icmpv6-parameters.xhtml#icmpv6-parameters-codes-16 assert_eq!(1, TYPE_DST_UNREACH); assert_eq!(2, TYPE_PACKET_TOO_BIG); assert_eq!(3, TYPE_TIME_EXCEEDED); assert_eq!(4, TYPE_PARAMETER_PROBLEM); assert_eq!(128, TYPE_ECHO_REQUEST); assert_eq!(129, TYPE_ECHO_REPLY); assert_eq!(130, TYPE_MULTICAST_LISTENER_QUERY); assert_eq!(131, TYPE_MULTICAST_LISTENER_REPORT); assert_eq!(132, TYPE_MULTICAST_LISTENER_REDUCTION); assert_eq!(133, TYPE_ROUTER_SOLICITATION); assert_eq!(134, TYPE_ROUTER_ADVERTISEMENT); assert_eq!(135, TYPE_NEIGHBOR_SOLICITATION); assert_eq!(136, TYPE_NEIGHBOR_ADVERTISEMENT); assert_eq!(137, TYPE_REDIRECT_MESSAGE); assert_eq!(138, TYPE_ROUTER_RENUMBERING); assert_eq!(141, TYPE_INVERSE_NEIGHBOR_DISCOVERY_SOLICITATION); assert_eq!(142, TYPE_INVERSE_NEIGHBOR_DISCOVERY_ADVERTISEMENT); assert_eq!(160, TYPE_EXT_ECHO_REQUEST); assert_eq!(161, TYPE_EXT_ECHO_REPLY); // destination unreachable code values according to // https://www.iana.org/assignments/icmpv6-parameters/icmpv6-parameters.xhtml#icmpv6-parameters-codes-2 assert_eq!(0, CODE_DST_UNREACH_NO_ROUTE); assert_eq!(1, CODE_DST_UNREACH_PROHIBITED); assert_eq!(2, CODE_DST_UNREACH_BEYOND_SCOPE); assert_eq!(3, CODE_DST_UNREACH_ADDR); assert_eq!(4, CODE_DST_UNREACH_PORT); assert_eq!(5, CODE_DST_UNREACH_SOURCE_ADDRESS_FAILED_POLICY); assert_eq!(6, CODE_DST_UNREACH_REJECT_ROUTE_TO_DEST); // time exceeded code values according to // https://www.iana.org/assignments/icmpv6-parameters/icmpv6-parameters.xhtml#icmpv6-parameters-codes-4 assert_eq!(0, CODE_TIME_EXCEEDED_HOP_LIMIT_EXCEEDED); assert_eq!(1, CODE_TIME_EXCEEDED_FRAGMENT_REASSEMBLY_TIME_EXCEEDED); // parameter problem codes according to // https://www.iana.org/assignments/icmpv6-parameters/icmpv6-parameters.xhtml#icmpv6-parameters-codes-5 assert_eq!(0, CODE_PARAM_PROBLEM_ERR_HEADER_FIELD); assert_eq!(1, CODE_PARAM_PROBLEM_UNRECOG_NEXT_HEADER); assert_eq!(2, CODE_PARAM_PROBLEM_UNRECOG_IPV6_OPTION); assert_eq!(3, CODE_PARAM_PROBLEM_IPV6_FIRST_FRAG_INCOMP_HEADER_CHAIN); assert_eq!(4, CODE_PARAM_PROBLEM_SR_UPPER_LAYER_HEADER_ERROR); assert_eq!( 5, CODE_PARAM_PROBLEM_UNRECOG_NEXT_HEADER_BY_INTERMEDIATE_NODE ); assert_eq!(6, CODE_PARAM_PROBLEM_EXT_HEADER_TOO_BIG); assert_eq!(7, CODE_PARAM_PROBLEM_EXT_HEADER_CHAIN_TOO_LONG); assert_eq!(8, CODE_PARAM_PROBLEM_TOO_MANY_EXT_HEADERS); assert_eq!(9, CODE_PARAM_PROBLEM_TOO_MANY_OPTIONS_EXT_HEADER); assert_eq!(10, CODE_PARAM_PROBLEM_OPTION_TOO_BIG); } mod dest_unreachable_code { use super::*; use etherparse::icmpv6::DestUnreachableCode::*; pub const VALID_VALUES: [(DestUnreachableCode, u8);7] = [ (NoRoute, CODE_DST_UNREACH_NO_ROUTE), (Prohibited, CODE_DST_UNREACH_PROHIBITED), (BeyondScope, CODE_DST_UNREACH_BEYOND_SCOPE), (Address, CODE_DST_UNREACH_ADDR), (Port, CODE_DST_UNREACH_PORT), (SourceAddressFailedPolicy, CODE_DST_UNREACH_SOURCE_ADDRESS_FAILED_POLICY), (RejectRoute, CODE_DST_UNREACH_REJECT_ROUTE_TO_DEST), ]; #[test] fn from_u8() { for (code, code_u8) in VALID_VALUES { assert_eq!( code, DestUnreachableCode::from_u8(code_u8).unwrap() ); } for code_u8 in 7u8..=0xff { assert!(DestUnreachableCode::from_u8(code_u8).is_none()); } } #[test] fn code_u8() { for (code, code_u8) in VALID_VALUES { assert_eq!(code.code_u8(), code_u8); } } #[test] fn clone_eq() { for (code, _) in VALID_VALUES { assert_eq!(code.clone(), code); } } #[test] fn debug() { let tests = [ (NoRoute, "NoRoute"), (Prohibited, "Prohibited"), (BeyondScope, "BeyondScope"), (Address, "Address"), (Port, "Port"), (SourceAddressFailedPolicy, "SourceAddressFailedPolicy"), (RejectRoute, "RejectRoute"), ]; for test in tests { assert_eq!(format!("{:?}", test.0), test.1); } } } mod time_exceeded_code { use super::*; use etherparse::icmpv6::TimeExceededCode::*; pub const VALID_VALUES: [(TimeExceededCode, u8);2] = [ (HopLimitExceeded, CODE_TIME_EXCEEDED_HOP_LIMIT_EXCEEDED), (FragmentReassemblyTimeExceeded, CODE_TIME_EXCEEDED_FRAGMENT_REASSEMBLY_TIME_EXCEEDED), ]; #[test] fn from_u8() { for (code, code_u8) in VALID_VALUES { assert_eq!( Some(code), TimeExceededCode::from_u8(code_u8) ); } for code_u8 in 2..=u8::MAX { assert_eq!(None, TimeExceededCode::from_u8(code_u8)); } } #[test] fn from_enum() { for (code, code_u8) in VALID_VALUES { assert_eq!( code.code_u8(), code_u8 ); } } #[test] fn clone_eq() { for (code, _) in VALID_VALUES { assert_eq!(code.clone(), code); } } #[test] fn debug() { let tests = [ (HopLimitExceeded, "HopLimitExceeded"), ( FragmentReassemblyTimeExceeded, "FragmentReassemblyTimeExceeded", ), ]; for test in tests { assert_eq!(format!("{:?}", test.0), test.1); } } } mod parameter_problem_code { use super::*; use ParameterProblemCode::*; pub const VALID_VALUES: [(ParameterProblemCode, u8);11] = [ (ErroneousHeaderField, CODE_PARAM_PROBLEM_ERR_HEADER_FIELD), (UnrecognizedNextHeader, CODE_PARAM_PROBLEM_UNRECOG_NEXT_HEADER), (UnrecognizedIpv6Option, CODE_PARAM_PROBLEM_UNRECOG_IPV6_OPTION), (Ipv6FirstFragmentIncompleteHeaderChain, CODE_PARAM_PROBLEM_IPV6_FIRST_FRAG_INCOMP_HEADER_CHAIN), (SrUpperLayerHeaderError, CODE_PARAM_PROBLEM_SR_UPPER_LAYER_HEADER_ERROR), (UnrecognizedNextHeaderByIntermediateNode, CODE_PARAM_PROBLEM_UNRECOG_NEXT_HEADER_BY_INTERMEDIATE_NODE), (ExtensionHeaderTooBig, CODE_PARAM_PROBLEM_EXT_HEADER_TOO_BIG), (ExtensionHeaderChainTooLong, CODE_PARAM_PROBLEM_EXT_HEADER_CHAIN_TOO_LONG), (TooManyExtensionHeaders, CODE_PARAM_PROBLEM_TOO_MANY_EXT_HEADERS), (TooManyOptionsInExtensionHeader, CODE_PARAM_PROBLEM_TOO_MANY_OPTIONS_EXT_HEADER), (OptionTooBig, CODE_PARAM_PROBLEM_OPTION_TOO_BIG) ]; #[test] fn from_u8() { for t in VALID_VALUES { assert_eq!(Some(t.0), ParameterProblemCode::from_u8(t.1)); } for code_u8 in 11..=u8::MAX { assert_eq!(None, ParameterProblemCode::from_u8(code_u8)); } } #[test] fn code_u8() { for t in VALID_VALUES { assert_eq!(t.0.code_u8(), t.1); } } #[test] fn clone_eq() { for (value, _) in VALID_VALUES { assert_eq!(value.clone(), value); } } #[test] fn debug() { let tests = [ (ErroneousHeaderField, "ErroneousHeaderField"), (UnrecognizedNextHeader, "UnrecognizedNextHeader"), (UnrecognizedIpv6Option, "UnrecognizedIpv6Option"), (UnrecognizedNextHeader, "UnrecognizedNextHeader"), (UnrecognizedIpv6Option, "UnrecognizedIpv6Option"), ( Ipv6FirstFragmentIncompleteHeaderChain, "Ipv6FirstFragmentIncompleteHeaderChain", ), (SrUpperLayerHeaderError, "SrUpperLayerHeaderError"), ( UnrecognizedNextHeaderByIntermediateNode, "UnrecognizedNextHeaderByIntermediateNode", ), (ExtensionHeaderTooBig, "ExtensionHeaderTooBig"), (ExtensionHeaderChainTooLong, "ExtensionHeaderChainTooLong"), (TooManyExtensionHeaders, "TooManyExtensionHeaders"), ( TooManyOptionsInExtensionHeader, "TooManyOptionsInExtensionHeader", ), (OptionTooBig, "OptionTooBig"), ]; for test in tests { assert_eq!(format!("{:?}", test.0), test.1); } } } mod parameter_problem_header { use super::*; #[test] fn clone_eq() { let value = ParameterProblemHeader{ code: ParameterProblemCode::ErroneousHeaderField, pointer: 0, }; assert_eq!(value.clone(), value); } #[test] fn debug() { let value = ParameterProblemHeader{ code: ParameterProblemCode::ErroneousHeaderField, pointer: 0, }; assert_eq!( format!("{:?}", value), format!("ParameterProblemHeader {{ code: {:?}, pointer: {:?} }}", value.code, value.pointer) ); } } mod icmpv6_type { use super::*; proptest! { #[test] fn type_u8( code_u8 in any::(), bytes5to8 in any::<[u8;4]>(), ) { use etherparse::Icmpv6Type::*; use etherparse::{IcmpEchoHeader, icmpv6::*}; { let type_u8_type_pair = [ (TYPE_DST_UNREACH, DestinationUnreachable(DestUnreachableCode::SourceAddressFailedPolicy)), (TYPE_PACKET_TOO_BIG, PacketTooBig{ mtu: u32::from_be_bytes(bytes5to8), }), (TYPE_TIME_EXCEEDED, TimeExceeded(TimeExceededCode::HopLimitExceeded)), (TYPE_PARAMETER_PROBLEM, ParameterProblem(ParameterProblemHeader{ code: ParameterProblemCode::UnrecognizedNextHeader, pointer: u32::from_be_bytes(bytes5to8)})), (TYPE_ECHO_REQUEST, EchoRequest(IcmpEchoHeader::from_bytes(bytes5to8))), (TYPE_ECHO_REPLY, EchoReply(IcmpEchoHeader::from_bytes(bytes5to8))), ]; for test in type_u8_type_pair { assert_eq!(test.0, test.1.type_u8()); } } for t in 0..=u8::MAX { assert_eq!( t, Unknown{ type_u8: t, code_u8, bytes5to8, }.type_u8() ); } } } proptest! { #[test] fn code_u8( code_u8 in any::(), bytes5to8 in any::<[u8;4]>(), ) { use etherparse::Icmpv6Type::*; use etherparse::{IcmpEchoHeader, icmpv6::*}; // types with 0 as code { let code_type_pair = [ (0, PacketTooBig{ mtu: u32::from_be_bytes(bytes5to8), }), (0, EchoRequest(IcmpEchoHeader::from_bytes(bytes5to8))), (0, EchoReply(IcmpEchoHeader::from_bytes(bytes5to8))), ]; for test in code_type_pair { assert_eq!(test.0, test.1.code_u8()); } } // destination unreachable for (code, code_u8) in dest_unreachable_code::VALID_VALUES { assert_eq!(code_u8, DestinationUnreachable(code).code_u8()); } // time exceeded for (code, code_u8) in time_exceeded_code::VALID_VALUES { assert_eq!(code_u8, TimeExceeded(code).code_u8()); } // parameter problem for (code, code_u8) in parameter_problem_code::VALID_VALUES { assert_eq!( code_u8, ParameterProblem( ParameterProblemHeader{ code, pointer: u32::from_be_bytes(bytes5to8), } ).code_u8() ); } // unknown for t in 0..=u8::MAX { assert_eq!( code_u8, Unknown{ type_u8: t, code_u8, bytes5to8, }.code_u8() ); } } } proptest! { #[test] fn calc_checksum( ip_header in ipv6_any(), icmpv6_type in icmpv6_type_any(), type_u8 in any::(), code_u8 in any::(), bytes5to8 in any::<[u8;4]>(), // max length is u32::MAX - header_len (7) bad_len in (std::u32::MAX - 7) as usize..=std::usize::MAX, payload in proptest::collection::vec(any::(), 0..64) ) { use Icmpv6Type::*; // size error case { // SAFETY: In case the error is not triggered // a segmentation fault will be triggered. let too_big_slice = unsafe { //NOTE: The pointer must be initialized with a non null value // otherwise a key constraint of slices is not fullfilled // which can lead to crashes in release mode. use std::ptr::NonNull; std::slice::from_raw_parts( NonNull::::dangling().as_ptr(), bad_len ) }; assert_matches!( icmpv6_type.calc_checksum(ip_header.source, ip_header.destination, too_big_slice), Err(ValueError::Ipv6PayloadLengthTooLarge(_)) ); } // normal cases { let test_checksum_calc = |icmp_type: Icmpv6Type| { let expected_checksum = { etherparse::checksum::Sum16BitWords::new() .add_16bytes(ip_header.source) .add_16bytes(ip_header.destination) .add_2bytes([0, ip_number::IPV6_ICMP]) .add_4bytes(( payload.len() as u32 + icmpv6_type.header_len() as u32 ).to_be_bytes()) .add_slice(&Icmpv6Header { icmp_type: icmp_type.clone(), checksum: 0 // use zero so the checksum gets correct calculated }.to_bytes()) .add_slice(&payload) .ones_complement() .to_be() }; assert_eq!( expected_checksum, icmp_type.calc_checksum( ip_header.source, ip_header.destination, &payload ).unwrap() ); }; // unknown test_checksum_calc( Unknown{ type_u8, code_u8, bytes5to8 } ); // destination unreachable for (code, _) in dest_unreachable_code::VALID_VALUES { test_checksum_calc(DestinationUnreachable(code)); } // packet too big test_checksum_calc(PacketTooBig{ mtu: u32::from_be_bytes(bytes5to8) }); // time exceeded for (code, _) in time_exceeded_code::VALID_VALUES { test_checksum_calc(TimeExceeded(code)); } // parameter problem for (code, _) in parameter_problem_code::VALID_VALUES { test_checksum_calc(ParameterProblem( ParameterProblemHeader{ code, pointer: u32::from_be_bytes(bytes5to8) } )); } // echo request test_checksum_calc(EchoRequest( IcmpEchoHeader::from_bytes(bytes5to8) )); // echo reply test_checksum_calc(EchoReply( IcmpEchoHeader::from_bytes(bytes5to8) )); } } } proptest! { #[test] fn to_header( ip_header in ipv6_any(), icmpv6_type in icmpv6_type_any(), // max length is u32::MAX - header_len (7) bad_len in (std::u32::MAX - 7) as usize..=std::usize::MAX, payload in proptest::collection::vec(any::(), 0..1024) ) { // size error case { // SAFETY: In case the error is not triggered // a segmentation fault will be triggered. let too_big_slice = unsafe { //NOTE: The pointer must be initialized with a non null value // otherwise a key constraint of slices is not fullfilled // which can lead to crashes in release mode. use std::ptr::NonNull; std::slice::from_raw_parts( NonNull::::dangling().as_ptr(), bad_len ) }; assert_matches!( icmpv6_type.to_header(ip_header.source, ip_header.destination, too_big_slice), Err(ValueError::Ipv6PayloadLengthTooLarge(_)) ); } // normal case assert_eq!( icmpv6_type.to_header(ip_header.source, ip_header.destination, &payload).unwrap(), Icmpv6Header { checksum: icmpv6_type.calc_checksum(ip_header.source, ip_header.destination, &payload).unwrap(), icmp_type: icmpv6_type, } ); } } proptest! { #[test] fn header_len( code_u8 in any::(), bytes5to8 in any::<[u8;4]>(), ) { use etherparse::Icmpv6Type::*; use etherparse::{IcmpEchoHeader, icmpv6::*}; let len_8_hdrs = [ DestinationUnreachable(DestUnreachableCode::Prohibited), PacketTooBig{ mtu: u32::from_be_bytes(bytes5to8), }, TimeExceeded(TimeExceededCode::FragmentReassemblyTimeExceeded), ParameterProblem(ParameterProblemHeader{ code: ParameterProblemCode::UnrecognizedIpv6Option, pointer: u32::from_be_bytes(bytes5to8), }), EchoRequest(IcmpEchoHeader::from_bytes(bytes5to8)), EchoReply(IcmpEchoHeader::from_bytes(bytes5to8)), ]; for hdr in len_8_hdrs { assert_eq!(8, hdr.header_len()); } for t in 0..=u8::MAX { assert_eq!( 8, Unknown{ type_u8: t, code_u8, bytes5to8, }.header_len() ); } } } proptest! { #[test] fn fixed_payload_size( code_u8 in any::(), bytes5to8 in any::<[u8;4]>(), ) { use etherparse::Icmpv6Type::*; use etherparse::{IcmpEchoHeader, icmpv6::*}; let variable_payload_headers = [ DestinationUnreachable(DestUnreachableCode::Prohibited), PacketTooBig{ mtu: u32::from_be_bytes(bytes5to8), }, TimeExceeded(TimeExceededCode::HopLimitExceeded), ParameterProblem(ParameterProblemHeader{ code: ParameterProblemCode::SrUpperLayerHeaderError, pointer: u32::from_be_bytes(bytes5to8), }), EchoRequest(IcmpEchoHeader::from_bytes(bytes5to8)), EchoReply(IcmpEchoHeader::from_bytes(bytes5to8)), ]; for hdr in variable_payload_headers { assert_eq!(None, hdr.fixed_payload_size()); } for t in 0..=u8::MAX { assert_eq!( None, Unknown{ type_u8: t, code_u8, bytes5to8, }.fixed_payload_size() ); } } } #[test] fn debug() { assert_eq!( format!( "{:?}", Icmpv6Type::Unknown { type_u8: 0, code_u8: 1, bytes5to8: [2, 3, 4, 5] } ), "Unknown { type_u8: 0, code_u8: 1, bytes5to8: [2, 3, 4, 5] }" ) } proptest! { #[test] fn clone_eq(t in icmpv6_type_any()) { assert_eq!(t, t.clone()); } } } mod icmpv6_header { use super::*; proptest! { #[test] fn new(icmp_type in icmpv6_type_any()) { assert_eq!( Icmpv6Header::new(icmp_type.clone()), Icmpv6Header { icmp_type, checksum: 0, } ); } } proptest! { #[test] fn with_checksum( ip_header in ipv6_any(), icmp_type in icmpv6_type_any(), // max length is u32::MAX - header_len (7) bad_len in (std::u32::MAX - 7) as usize..=std::usize::MAX, payload in proptest::collection::vec(any::(), 0..1024) ) { // error case { // SAFETY: In case the error is not triggered // a segmentation fault will be triggered. let too_big_slice = unsafe { //NOTE: The pointer must be initialized with a non null value // otherwise a key constraint of slices is not fullfilled // which can lead to crashes in release mode. use std::ptr::NonNull; std::slice::from_raw_parts( NonNull::::dangling().as_ptr(), bad_len ) }; assert_matches!( Icmpv6Header::with_checksum(icmp_type.clone(), ip_header.source, ip_header.destination, too_big_slice), Err(ValueError::Ipv6PayloadLengthTooLarge(_)) ); } // non error case assert_eq!( Icmpv6Header::with_checksum(icmp_type.clone(), ip_header.source, ip_header.destination, &payload).unwrap(), Icmpv6Header { icmp_type, checksum: icmp_type.calc_checksum(ip_header.source, ip_header.destination, &payload).unwrap(), } ); } } proptest! { #[test] fn from_slice( icmp_type in icmpv6_type_any(), checksum in any::(), ) { let bytes = { Icmpv6Header { icmp_type: icmp_type.clone(), checksum, }.to_bytes() }; // ok case { let result = Icmpv6Header::from_slice(&bytes).unwrap(); assert_eq!( Icmpv6Header{ icmp_type, checksum, }, result.0, ); assert_eq!(&bytes[8..], result.1); } // size error case for length in 0..8 { assert_matches!( Icmpv6Header::from_slice(&bytes[..length]), Err(ReadError::UnexpectedEndOfSlice(_)) ); } } } proptest! { #[test] fn read( icmp_type in icmpv6_type_any(), checksum in any::(), ) { let header = Icmpv6Header { icmp_type: icmp_type.clone(), checksum, }; let bytes = header.to_bytes(); // ok case { let mut cursor = std::io::Cursor::new(&bytes); let result = Icmpv6Header::read(&mut cursor).unwrap(); assert_eq!(header, result,); assert_eq!(header.header_len() as u64, cursor.position()); } // size error case for length in 0..header.header_len() { let mut cursor = std::io::Cursor::new(&bytes[..length]); assert_matches!( Icmpv6Header::read(&mut cursor), Err(_) ); } } } proptest! { #[test] fn write( icmp_type in icmpv6_type_any(), checksum in any::(), bad_len in 0..8usize ) { // normal case { let mut buffer = Vec::with_capacity(icmp_type.header_len()); let header = Icmpv6Header { icmp_type, checksum, }; header.write(&mut buffer).unwrap(); assert_eq!( &header.to_bytes(), &buffer[..] ); } // error case { let mut writer = TestWriter::with_max_size(bad_len); Icmpv6Header { icmp_type, checksum, }.write(&mut writer).unwrap_err(); } } } proptest! { #[test] fn header_len(icmp_type in icmpv6_type_any(), checksum in any::()) { assert_eq!( icmp_type.header_len(), Icmpv6Header{ icmp_type, checksum }.header_len() ); } } proptest! { #[test] fn fixed_payload_size(icmp_type in icmpv6_type_any(), checksum in any::()) { assert_eq!( icmp_type.fixed_payload_size(), Icmpv6Header{ icmp_type, checksum }.fixed_payload_size() ); } } proptest! { #[test] fn update_checksum( ip_header in ipv6_any(), icmp_type in icmpv6_type_any(), start_checksum in any::(), // max length is u32::MAX - header_len (7) bad_len in (std::u32::MAX - 7) as usize..=std::usize::MAX, payload in proptest::collection::vec(any::(), 0..1024) ) { // error case { // SAFETY: In case the error is not triggered // a segmentation fault will be triggered. let too_big_slice = unsafe { //NOTE: The pointer must be initialized with a non null value // otherwise a key constraint of slices is not fullfilled // which can lead to crashes in release mode. use std::ptr::NonNull; std::slice::from_raw_parts( NonNull::::dangling().as_ptr(), bad_len ) }; assert_matches!( Icmpv6Header{ icmp_type, checksum: 0 }.update_checksum(ip_header.source, ip_header.destination, too_big_slice), Err(ValueError::Ipv6PayloadLengthTooLarge(_)) ); } // normal case assert_eq!( { let mut header = Icmpv6Header{ icmp_type, checksum: start_checksum, }; header.update_checksum(ip_header.source, ip_header.destination, &payload).unwrap(); header }, Icmpv6Header{ icmp_type, checksum: icmp_type.calc_checksum(ip_header.source, ip_header.destination, &payload).unwrap(), } ); } } proptest! { #[test] fn to_bytes( checksum in any::(), rand_u32 in any::(), rand_4bytes in any::<[u8;4]>(), ) { use Icmpv6Type::*; let with_5to8_bytes = |type_u8: u8, code_u8: u8, bytes5to8: [u8;4]| -> ArrayVec { let mut bytes = ArrayVec::::new(); bytes.push(type_u8); bytes.push(code_u8); bytes.try_extend_from_slice(&checksum.to_be_bytes()).unwrap(); bytes.try_extend_from_slice(&bytes5to8).unwrap(); bytes }; let simple_bytes = |type_u8: u8, code_u8: u8| -> ArrayVec { with_5to8_bytes(type_u8, code_u8, [0;4]) }; // destination unreachable for (code, code_u8) in dest_unreachable_code::VALID_VALUES { assert_eq!( Icmpv6Header{ icmp_type: DestinationUnreachable(code), checksum }.to_bytes(), simple_bytes(TYPE_DST_UNREACH, code_u8) ); } // packet too big assert_eq!( Icmpv6Header{ icmp_type: PacketTooBig{ mtu: rand_u32 }, checksum }.to_bytes(), with_5to8_bytes(TYPE_PACKET_TOO_BIG, 0, rand_u32.to_be_bytes()) ); // time exceeded for (code, code_u8) in time_exceeded_code::VALID_VALUES { assert_eq!( Icmpv6Header{ icmp_type: TimeExceeded(code), checksum }.to_bytes(), simple_bytes(TYPE_TIME_EXCEEDED, code_u8) ); } // parameter problem for (code, code_u8) in parameter_problem_code::VALID_VALUES { assert_eq!( Icmpv6Header{ icmp_type: ParameterProblem( ParameterProblemHeader{ code, pointer: rand_u32, } ), checksum }.to_bytes(), with_5to8_bytes(TYPE_PARAMETER_PROBLEM, code_u8, rand_u32.to_be_bytes()) ); } // echo request assert_eq!( Icmpv6Header{ icmp_type: EchoRequest(IcmpEchoHeader { id: u16::from_be_bytes([rand_4bytes[0], rand_4bytes[1]]), seq: u16::from_be_bytes([rand_4bytes[2], rand_4bytes[3]]), }), checksum }.to_bytes(), with_5to8_bytes(TYPE_ECHO_REQUEST, 0, rand_4bytes) ); // echo reply assert_eq!( Icmpv6Header{ icmp_type: EchoReply(IcmpEchoHeader { id: u16::from_be_bytes([rand_4bytes[0], rand_4bytes[1]]), seq: u16::from_be_bytes([rand_4bytes[2], rand_4bytes[3]]), }), checksum }.to_bytes(), with_5to8_bytes(TYPE_ECHO_REPLY, 0, rand_4bytes) ); // unknown for type_u8 in 0..=u8::MAX { for code_u8 in 0..=u8::MAX { assert_eq!( Icmpv6Header{ icmp_type: Unknown { type_u8, code_u8, bytes5to8: rand_4bytes, }, checksum }.to_bytes(), with_5to8_bytes(type_u8, code_u8, rand_4bytes) ); } } } } #[test] fn debug() { let t = Icmpv6Type::Unknown { type_u8: 0, code_u8: 1, bytes5to8: [2, 3, 4, 5], }; assert_eq!( format!( "{:?}", Icmpv6Header { icmp_type: t.clone(), checksum: 7 } ), format!("Icmpv6Header {{ icmp_type: {:?}, checksum: {:?} }}", t, 7) ); } proptest! { #[test] fn clone_eq(icmp_type in icmpv6_type_any(), checksum in any::()) { let header = Icmpv6Header{ icmp_type, checksum }; assert_eq!(header, header.clone()); } } } mod icmpv6_slice { use super::*; proptest! { #[test] fn from_slice(slice in proptest::collection::vec(any::(), 8..1024)) { // ok case assert_eq!(Icmpv6Slice::from_slice(&slice[..]).unwrap().slice(), &slice[..]); // too small size error case for len in 0..8 { assert_matches!( Icmpv6Slice::from_slice(&slice[..len]), Err(ReadError::UnexpectedEndOfSlice(Icmpv6Header::MIN_SERIALIZED_SIZE)) ); } } } proptest! { /// This error can only occur on systems with a pointer size /// bigger then 64 bits. #[cfg(not(any(target_pointer_width = "16", target_pointer_width = "32")))] #[test] fn from_slice_too_big_error( bad_len in ((std::u32::MAX as usize) + 1)..=std::usize::MAX, ) { // too large packet error case { // SAFETY: In case the error is not triggered // a segmentation fault will be triggered. let too_big_slice = unsafe { //NOTE: The pointer must be initialized with a non null value // otherwise a key constraint of slices is not fullfilled // which can lead to crashes in release mode. use std::ptr::NonNull; std::slice::from_raw_parts( NonNull::::dangling().as_ptr(), bad_len ) }; assert_matches!( Icmpv6Slice::from_slice(too_big_slice), Err(ReadError::Icmpv6PacketTooBig(_)) ); } } } proptest! { #[test] fn header( icmp_type in icmpv6_type_any(), checksum in any::() ) { let expected = Icmpv6Header { icmp_type, checksum }; assert_eq!( Icmpv6Slice::from_slice(&expected.to_bytes()).unwrap().header(), expected ); } } proptest!{ #[test] fn icmp_type( checksum in any::<[u8;2]>(), bytes5to8 in any::<[u8;4]>() ) { use Icmpv6Type::*; let gen_bytes = |type_u8: u8, code_u8: u8| -> [u8;8] { [ type_u8, code_u8, checksum[0], checksum[1], bytes5to8[0], bytes5to8[1], bytes5to8[2], bytes5to8[3] ] }; let assert_unknown = |type_u8: u8, code_u8: u8| { assert_eq!( Icmpv6Slice::from_slice(&gen_bytes(type_u8, code_u8)).unwrap().icmp_type(), Unknown{ type_u8, code_u8, bytes5to8, } ); }; // destination unreachable { // known codes for (code, code_u8) in dest_unreachable_code::VALID_VALUES { assert_eq!( Icmpv6Slice::from_slice(&gen_bytes(TYPE_DST_UNREACH, code_u8)).unwrap().icmp_type(), DestinationUnreachable(code) ); } // unknown codes for code_u8 in 7..=u8::MAX { assert_unknown(TYPE_DST_UNREACH, code_u8); } } // packet too big { // known code assert_eq!( Icmpv6Slice::from_slice(&gen_bytes(TYPE_PACKET_TOO_BIG, 0)).unwrap().icmp_type(), PacketTooBig { mtu: u32::from_be_bytes(bytes5to8) } ); // unknown code for code_u8 in 1..=u8::MAX { assert_unknown(TYPE_PACKET_TOO_BIG, code_u8); } } // time exceeded { // known codes for (code, code_u8) in time_exceeded_code::VALID_VALUES { assert_eq!( Icmpv6Slice::from_slice(&gen_bytes(TYPE_TIME_EXCEEDED, code_u8)).unwrap().icmp_type(), TimeExceeded(code) ); } // unknown codes for code_u8 in 2..=u8::MAX { assert_unknown(TYPE_TIME_EXCEEDED, code_u8); } } // parameter problem { // known codes for (code, code_u8) in parameter_problem_code::VALID_VALUES { assert_eq!( Icmpv6Slice::from_slice(&gen_bytes(TYPE_PARAMETER_PROBLEM, code_u8)).unwrap().icmp_type(), ParameterProblem(ParameterProblemHeader{ code, pointer: u32::from_be_bytes(bytes5to8), }) ); } // unknown codes for code_u8 in 11..=u8::MAX { assert_unknown(TYPE_PARAMETER_PROBLEM, code_u8); } } // echo request { // known code assert_eq!( Icmpv6Slice::from_slice(&gen_bytes(TYPE_ECHO_REQUEST, 0)).unwrap().icmp_type(), EchoRequest(IcmpEchoHeader::from_bytes(bytes5to8)) ); // unknown codes for code_u8 in 1..=u8::MAX { assert_unknown(TYPE_ECHO_REPLY, code_u8); } } // echo reply { // known code assert_eq!( Icmpv6Slice::from_slice(&gen_bytes(TYPE_ECHO_REPLY, 0)).unwrap().icmp_type(), EchoReply(IcmpEchoHeader::from_bytes(bytes5to8)) ); // unknown codes for code_u8 in 1..=u8::MAX { assert_unknown(TYPE_ECHO_REPLY, code_u8); } } } } proptest! { #[test] fn header_len( code_u8 in any::(), bytes5to8 in any::<[u8;4]>(), ) { use etherparse::Icmpv6Type::*; use etherparse::{IcmpEchoHeader, icmpv6::*}; let len_8_types = [ DestinationUnreachable(DestUnreachableCode::Prohibited), PacketTooBig{ mtu: u32::from_be_bytes(bytes5to8), }, TimeExceeded(TimeExceededCode::HopLimitExceeded), ParameterProblem( ParameterProblemHeader{ code: ParameterProblemCode::OptionTooBig, pointer: u32::from_be_bytes(bytes5to8), } ), EchoRequest(IcmpEchoHeader::from_bytes(bytes5to8)), EchoReply(IcmpEchoHeader::from_bytes(bytes5to8)), ]; for t in len_8_types { assert_eq!( t.header_len(), Icmpv6Slice::from_slice( &Icmpv6Header::new(t).to_bytes() ).unwrap().header_len() ); } for t in 0..=u8::MAX { let header = Icmpv6Header::new( Unknown{ type_u8: t, code_u8, bytes5to8, } ); assert_eq!( 8, Icmpv6Slice::from_slice( &header.to_bytes() ).unwrap().header_len() ); } } } proptest! { #[test] fn type_u8(slice in proptest::collection::vec(any::(), 8..16)) { assert_eq!( Icmpv6Slice::from_slice(&slice[..]).unwrap().type_u8(), slice[0] ); } } proptest! { #[test] fn code_u8(slice in proptest::collection::vec(any::(), 8..16)) { assert_eq!( Icmpv6Slice::from_slice(&slice[..]).unwrap().code_u8(), slice[1] ); } } proptest! { #[test] fn checksum(slice in proptest::collection::vec(any::(), 8..16)) { assert_eq!( Icmpv6Slice::from_slice(&slice[..]).unwrap().checksum(), u16::from_be_bytes([slice[2], slice[3]]) ); } } proptest! { #[test] fn is_checksum_valid( ip_header in ipv6_any(), icmp_type in icmpv6_type_any(), payload in proptest::collection::vec(any::(), 0..1024), flip_byte in 0usize..1032, ) { // generate slice with a correct checksum let header = Icmpv6Header::with_checksum(icmp_type, ip_header.source, ip_header.destination, &payload).unwrap(); let bytes = { let mut bytes = Vec::with_capacity(header.header_len() + payload.len()); header.write(&mut bytes).unwrap(); bytes.extend_from_slice(&payload); bytes }; // check that the checksum gets reported as ok assert!( Icmpv6Slice::from_slice(&bytes).unwrap().is_checksum_valid(ip_header.source, ip_header.destination) ); // corrupt icmp packet { let mut corrupted_bytes = bytes.clone(); let i = flip_byte % corrupted_bytes.len(); corrupted_bytes[i] = !corrupted_bytes[i]; assert_eq!( false, Icmpv6Slice::from_slice(&corrupted_bytes).unwrap().is_checksum_valid(ip_header.source, ip_header.destination) ); } // corrupt ip source { let mut corrupted_source = ip_header.source; let i = flip_byte % corrupted_source.len(); corrupted_source[i] = !corrupted_source[i]; assert_eq!( false, Icmpv6Slice::from_slice(&bytes).unwrap().is_checksum_valid(corrupted_source, ip_header.destination) ); } // corrupt ip destination { let mut corrupted_dest = ip_header.destination; let i = flip_byte % corrupted_dest.len(); corrupted_dest[i] = !corrupted_dest[i]; assert_eq!( false, Icmpv6Slice::from_slice(&bytes).unwrap().is_checksum_valid(ip_header.source, corrupted_dest) ); } // corrupt length { let mut larger_bytes = bytes.clone(); larger_bytes.push(0); larger_bytes.push(0); assert_eq!( false, Icmpv6Slice::from_slice(&larger_bytes).unwrap().is_checksum_valid(ip_header.source, ip_header.destination) ); } } } proptest! { #[test] fn bytes5to8(slice in proptest::collection::vec(any::(), 8..16)) { assert_eq!( Icmpv6Slice::from_slice(&slice[..]).unwrap().bytes5to8(), [slice[4], slice[5], slice[6], slice[7]] ); } } proptest! { #[test] fn slice(slice in proptest::collection::vec(any::(), 8..16)) { assert_eq!( Icmpv6Slice::from_slice(&slice[..]).unwrap().slice(), &slice[..] ); } } proptest! { #[test] fn payload( type_u8 in any::(), code_u8 in any::(), bytes5to8 in any::<[u8;4]>(), payload in proptest::collection::vec(any::(), 8..16) ) { use etherparse::Icmpv6Type::*; use etherparse::{IcmpEchoHeader, icmpv6::*}; let len_8_types = [ Unknown{ type_u8, code_u8, bytes5to8, }, DestinationUnreachable(DestUnreachableCode::Prohibited), PacketTooBig{ mtu: u32::from_be_bytes(bytes5to8), }, TimeExceeded(TimeExceededCode::HopLimitExceeded), ParameterProblem( ParameterProblemHeader{ code: ParameterProblemCode::ExtensionHeaderChainTooLong, pointer: u32::from_be_bytes(bytes5to8), } ), EchoRequest(IcmpEchoHeader::from_bytes(bytes5to8)), EchoReply(IcmpEchoHeader::from_bytes(bytes5to8)), ]; for t in len_8_types { let mut bytes = Vec::with_capacity(t.header_len() + payload.len()); Icmpv6Header::new(t.clone()).write(&mut bytes).unwrap(); bytes.extend_from_slice(&payload); assert_eq!( Icmpv6Slice::from_slice(&bytes[..]).unwrap().payload(), &payload[..] ); } } } #[test] fn debug() { let data = [0u8; 8]; assert_eq!( format!("{:?}", Icmpv6Slice::from_slice(&data).unwrap()), format!("Icmpv6Slice {{ slice: {:?} }}", &data) ); } proptest! { #[test] fn clone_eq(slice in proptest::collection::vec(any::(), 8..16)) { assert_eq!( Icmpv6Slice::from_slice(&slice).unwrap().clone(), Icmpv6Slice::from_slice(&slice).unwrap() ); } } } mod regression { use super::*; #[test] fn icmp6_echo_marshall_unmarshall() { let icmp6 = Icmpv6Header { icmp_type: Icmpv6Type::EchoRequest(IcmpEchoHeader { seq: 1, id: 2 }), checksum: 0, }; // serialize let mut buffer: Vec = Vec::with_capacity(256); icmp6.write(&mut buffer).unwrap(); let (new_icmp6, rest) = Icmpv6Header::from_slice(&buffer).unwrap(); assert_eq!(icmp6, new_icmp6); assert_eq!(rest.len(), 0); } #[test] fn ip6_echo_marshall_unmarshall() { let builder = PacketBuilder::ipv6( [0xfe, 0x80, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], //source ip [0xfe, 0x80, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 114], //dst ip 20) //time to life .icmpv6_echo_request(1, 2); let payload = [0xde, 0xad, 0xbe, 0xef]; //get some memory to store the result let mut result = Vec::::with_capacity(builder.size(payload.len())); //serialize builder.write(&mut result, &payload).unwrap(); let new_ip = PacketHeaders::from_ip_slice(&result).unwrap(); if let Some(TransportHeader::Icmpv6(hdr)) = new_ip.transport { if let Icmpv6Type::EchoRequest(echo) = hdr.icmp_type { assert_eq!(echo.id, 1); assert_eq!(echo.seq, 2); } else { panic!("Not an EchoRequest!?"); } } else { panic!("No transport header found!?") } } const ICMP6_ECHO_REQUEST_BYTES: [u8; 118] = [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0xdd, 0x60, 0x00, 0xf3, 0xc2, 0x00, 0x40, 0x3a, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 0xd5, 0x2f, 0x00, 0x05, 0x00, 0x01, 0xe3, 0x58, 0xdb, 0x61, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xc0, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, ]; const ICMP6_ECHO_REPLY_BYTES: [u8; 118] = [ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0xdd, 0x60, 0x00, 0xa3, 0xde, 0x00, 0x40, 0x3a, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x81, 0x00, 0xd4, 0x2f, 0x00, 0x05, 0x00, 0x01, 0xe3, 0x58, 0xdb, 0x61, 0x00, 0x00, 0x00, 0x00, 0x1f, 0xc0, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, ]; #[test] fn verify_icmp6_checksum() { for (pkt, checksum) in [ (ICMP6_ECHO_REQUEST_BYTES, 0xd52f), (ICMP6_ECHO_REPLY_BYTES, 0xd42f), ] { // make sure we can unmarshall the correct checksum let request = PacketHeaders::from_ethernet_slice(&pkt).unwrap(); let mut icmp6 = request.transport.unwrap().icmpv6().unwrap(); let valid_checksum = icmp6.checksum; assert_ne!(valid_checksum, 0); assert_eq!(valid_checksum, checksum); // reset it and recalculate icmp6.checksum = 0; let iph = match request.ip { Some(IpHeader::Version6(ipv6, _)) => ipv6, _ => panic!("Failed to parse ipv6 part of packet?!"), }; assert_eq!( icmp6 .icmp_type .calc_checksum(iph.source, iph.destination, request.payload), Ok(valid_checksum) ); } } #[test] fn echo_request_slice() { let echo = SlicedPacket::from_ethernet(&ICMP6_ECHO_REQUEST_BYTES).unwrap(); use TransportSlice::*; let icmp6 = match echo.transport.unwrap() { Icmpv6(icmp6) => icmp6, Icmpv4(_) | Udp(_) | Tcp(_) | Unknown(_) => panic!("Misparsed header!"), }; assert!(matches!( icmp6.header().icmp_type, Icmpv6Type::EchoRequest(_) )); } } etherparse-0.13.0/tests/transport/mod.rs000064400000000000000000000407741046102023000164060ustar 00000000000000pub mod icmp; pub mod icmpv4; pub mod icmpv6; pub mod udp; pub mod tcp; mod transport_header { use super::super::*; use std::slice; use std::io::Cursor; proptest! { #[test] fn debug( tcp in tcp_any(), udp in udp_any(), icmpv4 in icmpv4_header_any(), icmpv6 in icmpv6_header_any(), ) { use TransportHeader::*; assert_eq!( format!("Udp({:?})", udp), format!("{:?}", Udp(udp.clone())), ); assert_eq!( format!("Tcp({:?})", tcp), format!("{:?}", Tcp(tcp.clone())), ); assert_eq!( format!("Icmpv4({:?})", icmpv4), format!("{:?}", Icmpv4(icmpv4.clone())), ); assert_eq!( format!("Icmpv6({:?})", icmpv6), format!("{:?}", Icmpv6(icmpv6.clone())), ); } } proptest! { #[test] fn clone_eq( tcp in tcp_any(), udp in udp_any(), icmpv4 in icmpv4_header_any(), icmpv6 in icmpv6_header_any(), ) { use TransportHeader::*; let values = [ Udp(udp), Tcp(tcp), Icmpv4(icmpv4), Icmpv6(icmpv6), ]; for value in values { assert_eq!(value.clone(), value); } } } #[test] fn udp() { let udp: UdpHeader = Default::default(); assert_eq!(Some(udp.clone()), TransportHeader::Udp(udp).udp()); assert_eq!(None, TransportHeader::Tcp(Default::default()).udp()); } #[test] fn mut_udp() { let udp: UdpHeader = Default::default(); assert_eq!(Some(&mut udp.clone()), TransportHeader::Udp(udp).mut_udp()); assert_eq!(None, TransportHeader::Tcp(Default::default()).mut_udp()); } #[test] fn tcp() { let tcp: TcpHeader = Default::default(); assert_eq!(Some(tcp.clone()), TransportHeader::Tcp(tcp).tcp()); assert_eq!(None, TransportHeader::Udp(Default::default()).tcp()); } #[test] fn mut_tcp() { let tcp: TcpHeader = Default::default(); assert_eq!(Some(&mut tcp.clone()), TransportHeader::Tcp(tcp).mut_tcp()); assert_eq!(None, TransportHeader::Udp(Default::default()).mut_tcp()); } proptest! { #[test] fn icmpv4(icmpv4 in icmpv4_header_any()) { assert_eq!(Some(icmpv4.clone()), TransportHeader::Icmpv4(icmpv4).icmpv4()); assert_eq!(None, TransportHeader::Udp(Default::default()).icmpv4()); } } proptest! { #[test] fn mut_icmpv4(icmpv4 in icmpv4_header_any()) { assert_eq!(Some(&mut icmpv4.clone()), TransportHeader::Icmpv4(icmpv4).mut_icmpv4()); assert_eq!(None, TransportHeader::Udp(Default::default()).mut_icmpv4()); } } proptest! { #[test] fn icmpv6(icmpv6 in icmpv6_header_any()) { assert_eq!(Some(icmpv6.clone()), TransportHeader::Icmpv6(icmpv6).icmpv6()); assert_eq!(None, TransportHeader::Udp(Default::default()).icmpv6()); } } proptest! { #[test] fn mut_icmpv6(icmpv6 in icmpv6_header_any()) { assert_eq!(Some(&mut icmpv6.clone()), TransportHeader::Icmpv6(icmpv6).mut_icmpv6()); assert_eq!(None, TransportHeader::Udp(Default::default()).mut_icmpv6()); } } proptest! { #[test] fn header_size( udp in udp_any(), tcp in tcp_any(), icmpv4 in icmpv4_header_any(), icmpv6 in icmpv6_header_any(), ) { assert_eq!( TransportHeader::Udp(udp).header_len(), UdpHeader::SERIALIZED_SIZE ); assert_eq!( TransportHeader::Tcp(tcp.clone()).header_len(), tcp.header_len() as usize ); assert_eq!( TransportHeader::Icmpv4(icmpv4.clone()).header_len(), icmpv4.header_len() ); assert_eq!( TransportHeader::Icmpv6(icmpv6.clone()).header_len(), icmpv6.header_len() ); } } proptest! { #[test] fn update_checksum_ipv4( ipv4 in ipv4_any(), udp in udp_any(), tcp in tcp_any(), icmpv4 in icmpv4_header_any(), icmpv6 in icmpv6_header_any(), ) { use TransportHeader::*; // udp { // ok case { let mut transport = Udp(udp.clone()); let payload = Vec::new(); transport.update_checksum_ipv4(&ipv4, &payload).unwrap(); assert_eq!(transport.udp().unwrap().checksum, udp.calc_checksum_ipv4(&ipv4, &payload).unwrap()); } // error case { let mut transport = Udp(udp.clone()); let len = (std::u16::MAX as usize) - UdpHeader::SERIALIZED_SIZE + 1; let tcp_payload = unsafe { //NOTE: The pointer must be initialized with a non null value // otherwise a key constraint of slices is not fullfilled // which can lead to crashes in release mode. use std::ptr::NonNull; slice::from_raw_parts( NonNull::::dangling().as_ptr(), len ) }; assert_eq!(Err(ValueError::UdpPayloadLengthTooLarge(len)), transport.update_checksum_ipv4(&ipv4, &tcp_payload)); } } // tcp { //ok case { let mut transport = Tcp(tcp.clone()); let payload = Vec::new(); transport.update_checksum_ipv4(&ipv4, &payload).unwrap(); assert_eq!(transport.tcp().unwrap().checksum, tcp.calc_checksum_ipv4(&ipv4, &payload).unwrap()); } //error case { let mut transport = Tcp(tcp.clone()); let len = (std::u16::MAX - tcp.header_len()) as usize + 1; let tcp_payload = unsafe { //NOTE: The pointer must be initialized with a non null value // otherwise a key constraint of slices is not fullfilled // which can lead to crashes in release mode. use std::ptr::NonNull; slice::from_raw_parts( NonNull::::dangling().as_ptr(), len ) }; assert_eq!(Err(ValueError::TcpLengthTooLarge(std::u16::MAX as usize + 1)), transport.update_checksum_ipv4(&ipv4, &tcp_payload)); } } // icmpv4 { let mut transport = Icmpv4(icmpv4.clone()); let payload = Vec::new(); transport.update_checksum_ipv4(&ipv4, &payload).unwrap(); assert_eq!( transport.icmpv4().unwrap().checksum, icmpv4.icmp_type.calc_checksum(&payload) ); } // icmpv6 (error) assert_eq!( Icmpv6(icmpv6).update_checksum_ipv4(&ipv4, &[]), Err(ValueError::Icmpv6InIpv4) ); } } proptest! { #[test] #[cfg(target_pointer_width = "64")] fn update_checksum_ipv6( ipv6 in ipv6_any(), udp in udp_any(), tcp in tcp_any(), icmpv4 in icmpv4_header_any(), icmpv6 in icmpv6_header_any(), ) { use TransportHeader::*; // udp { //ok case { let mut transport = Udp(udp.clone()); let payload = Vec::new(); transport.update_checksum_ipv6(&ipv6, &payload).unwrap(); assert_eq!(transport.udp().unwrap().checksum, udp.calc_checksum_ipv6(&ipv6, &payload).unwrap()); } //error case { let mut transport = Udp(udp.clone()); let len = (std::u32::MAX as usize) - UdpHeader::SERIALIZED_SIZE + 1; let payload = unsafe { //NOTE: The pointer must be initialized with a non null value // otherwise a key constraint of slices is not fullfilled // which can lead to crashes in release mode. use std::ptr::NonNull; slice::from_raw_parts( NonNull::::dangling().as_ptr(), len ) }; assert_eq!( Err(ValueError::UdpPayloadLengthTooLarge(len)), transport.update_checksum_ipv6(&ipv6, &payload) ); } } // tcp { //ok case { let mut transport = Tcp(tcp.clone()); let payload = Vec::new(); transport.update_checksum_ipv6(&ipv6, &payload).unwrap(); assert_eq!(transport.tcp().unwrap().checksum, tcp.calc_checksum_ipv6(&ipv6, &payload).unwrap()); } //error case { let mut transport = Tcp(tcp.clone()); let len = (std::u32::MAX - tcp.header_len() as u32) as usize + 1; let tcp_payload = unsafe { //NOTE: The pointer must be initialized with a non null value // otherwise a key constraint of slices is not fullfilled // which can lead to crashes in release mode. use std::ptr::NonNull; slice::from_raw_parts( NonNull::::dangling().as_ptr(), len ) }; assert_eq!(Err(ValueError::TcpLengthTooLarge(std::u32::MAX as usize + 1)), transport.update_checksum_ipv6(&ipv6, &tcp_payload)); } } // icmpv4 { let mut transport = Icmpv4(icmpv4.clone()); let payload = Vec::new(); transport.update_checksum_ipv6(&ipv6, &payload).unwrap(); assert_eq!( transport.icmpv4().unwrap().checksum, icmpv4.icmp_type.calc_checksum(&payload) ); } // icmpv6 { // normal case { let mut transport = Icmpv6(icmpv6.clone()); let payload = Vec::new(); transport.update_checksum_ipv6(&ipv6, &payload).unwrap(); assert_eq!( transport.icmpv6().unwrap().checksum, icmpv6.icmp_type.calc_checksum(ipv6.source, ipv6.destination, &payload).unwrap() ); } // error case { let mut transport = Icmpv6(icmpv6.clone()); // SAFETY: In case the error is not triggered // a segmentation fault will be triggered. let too_big_slice = unsafe { //NOTE: The pointer must be initialized with a non null value // otherwise a key constraint of slices is not fullfilled // which can lead to crashes in release mode. use std::ptr::NonNull; std::slice::from_raw_parts( NonNull::::dangling().as_ptr(), (std::u32::MAX - 7) as usize ) }; assert_matches!( transport.update_checksum_ipv6(&ipv6, too_big_slice), Err(ValueError::Ipv6PayloadLengthTooLarge(_)) ); } } } } proptest! { #[test] fn write( udp in udp_any(), tcp in tcp_any(), icmpv4 in icmpv4_header_any(), icmpv6 in icmpv6_header_any(), ) { // udp { //write { let result_input = { let mut buffer = Vec::new(); udp.write(&mut buffer).unwrap(); buffer }; let result_transport = { let mut buffer = Vec::new(); TransportHeader::Udp(udp.clone()).write(&mut buffer).unwrap(); buffer }; assert_eq!(result_input, result_transport); } //trigger an error { let mut a: [u8;0] = []; assert_matches!(TransportHeader::Udp(udp.clone()).write(&mut Cursor::new(&mut a[..])), Err(WriteError::IoError(_))); } } // tcp { //write { let result_input = { let mut buffer = Vec::new(); tcp.write(&mut buffer).unwrap(); buffer }; let result_transport = { let mut buffer = Vec::new(); TransportHeader::Tcp(tcp.clone()).write(&mut buffer).unwrap(); buffer }; assert_eq!(result_input, result_transport); } //trigger an error { let mut a: [u8;0] = []; assert_matches!(TransportHeader::Tcp(tcp.clone()).write(&mut Cursor::new(&mut a[..])), Err(WriteError::IoError(_))); } } // icmpv4 { // normal write { let result_input = { let mut buffer = Vec::new(); icmpv4.write(&mut buffer).unwrap(); buffer }; let result_transport = { let mut buffer = Vec::new(); TransportHeader::Icmpv4(icmpv4.clone()).write(&mut buffer).unwrap(); buffer }; assert_eq!(result_input, result_transport); } // error during write { let mut a: [u8;0] = []; assert_matches!( TransportHeader::Icmpv4(icmpv4.clone()).write(&mut Cursor::new(&mut a[..])), Err(WriteError::IoError(_)) ); } } // icmpv6 { // normal write { let result_input = { let mut buffer = Vec::new(); icmpv6.write(&mut buffer).unwrap(); buffer }; let result_transport = { let mut buffer = Vec::new(); TransportHeader::Icmpv6(icmpv6.clone()).write(&mut buffer).unwrap(); buffer }; assert_eq!(result_input, result_transport); } // error during write { let mut a: [u8;0] = []; assert_matches!( TransportHeader::Icmpv6(icmpv6.clone()).write(&mut Cursor::new(&mut a[..])), Err(WriteError::IoError(_)) ); } } } } }etherparse-0.13.0/tests/transport/tcp.rs000064400000000000000000001512771046102023000164160ustar 00000000000000use super::super::*; use std::io::Cursor; use proptest::prelude::*; use std::slice; mod header { use super::*; #[test] fn default() { let default : TcpHeader = Default::default(); assert_eq!(0, default.source_port); assert_eq!(0, default.destination_port); assert_eq!(0, default.sequence_number); assert_eq!(0, default.acknowledgment_number); assert_eq!(5, default.data_offset()); assert_eq!(false, default.ns); assert_eq!(false, default.fin); assert_eq!(false, default.syn); assert_eq!(false, default.rst); assert_eq!(false, default.psh); assert_eq!(false, default.ack); assert_eq!(false, default.ece); assert_eq!(false, default.urg); assert_eq!(false, default.cwr); assert_eq!(0, default.window_size); assert_eq!(0, default.checksum); assert_eq!(0, default.urgent_pointer); assert_eq!(&[0;40][0..0], &default.options()[..]); } #[test] fn eq() { let options = [ TcpOptionElement::Timestamp(0x00102030, 0x01112131), //10 TcpOptionElement::SelectiveAcknowledgement((0x02122232,0x03132333), [None, None, None]), //20 TcpOptionElement::Timestamp(0x04142434, 0x05152535), //30 TcpOptionElement::Timestamp(0x06162636, 0x07172737), //40 ]; let base : TcpHeader = { let mut base : TcpHeader = Default::default(); base.source_port = 1; base.destination_port = 2; base.sequence_number = 3; base.acknowledgment_number = 4; base.window_size = 6; base.checksum = 7; base.urgent_pointer = 8; base.set_options(&options[..]).unwrap(); base }; //equal { let other = base.clone(); assert_eq!(other, base); } //change every field anc check for neq //source_port { let mut other = base.clone(); other.source_port = 10; assert_ne!(other, base); } //destination_port { let mut other = base.clone(); other.destination_port = 10; assert_ne!(other, base); } //sequence_number { let mut other = base.clone(); other.sequence_number = 10; assert_ne!(other, base); } //acknowledgment_number { let mut other = base.clone(); other.acknowledgment_number = 10; assert_ne!(other, base); } //data_offset { let mut other = base.clone(); other.set_options(&[TcpOptionElement::MaximumSegmentSize(16)]).unwrap(); assert_ne!(other, base); } //ns { let mut other = base.clone(); other.ns = true; assert_ne!(other, base); } //fin { let mut other = base.clone(); other.fin = true; assert_ne!(other, base); } //syn { let mut other = base.clone(); other.syn = true; assert_ne!(other, base); } //rst { let mut other = base.clone(); other.rst = true; assert_ne!(other, base); } //psh { let mut other = base.clone(); other.psh = true; assert_ne!(other, base); } //ack { let mut other = base.clone(); other.ack = true; assert_ne!(other, base); } //ece { let mut other = base.clone(); other.ece = true; assert_ne!(other, base); } //urg { let mut other = base.clone(); other.urg = true; assert_ne!(other, base); } //cwr { let mut other = base.clone(); other.cwr = true; assert_ne!(other, base); } //window_size { let mut other = base.clone(); other.window_size = 10; assert_ne!(other, base); } //checksum { let mut other = base.clone(); other.checksum = 10; assert_ne!(other, base); } //urgent_pointer { let mut other = base.clone(); other.urgent_pointer = 10; assert_ne!(other, base); } //options (first element different) { let mut other = base.clone(); other.set_options(&{ let mut other_options = options.clone(); other_options[0] = TcpOptionElement::Timestamp(0x00102039, 0x01112131); other_options }).unwrap(); assert_ne!(other, base); } //options (last element) { let mut other = base.clone(); other.set_options(&options).unwrap(); let mut other2 = base.clone(); other2.set_options(&{ let mut options2 = options.clone(); options2[3] = TcpOptionElement::Timestamp(0x06162636, 0x97172737); options2 }).unwrap(); assert_ne!(other, other2); } //options (check only relevant data is compared) { let mut other = base.clone(); other.set_options(&options).unwrap(); let mut other2 = base.clone(); other2.set_options(&{ let mut options2 = options.clone(); options2[3] = TcpOptionElement::Timestamp(0x06162636, 0x97172737); options2 }).unwrap(); // reset the data let new_options = [ TcpOptionElement::Timestamp(0x00102030, 0x01112131) ]; other.set_options(&new_options).unwrap(); other2.set_options(&new_options).unwrap(); assert_eq!(other, other2); } // slice (auto generated) { let header = base.clone(); let buffer = { let mut buffer = Vec::with_capacity(header.header_len().into()); header.write(&mut buffer).unwrap(); buffer }; let slice = TcpHeaderSlice::from_slice(&buffer).unwrap(); assert_eq!(slice, slice.clone()); } // TcpOptionReadError { use TcpOptionReadError::*; let value = UnexpectedEndOfSlice{ option_id: 123, expected_len: 5, actual_len: 4, }; assert_eq!(value, value.clone()); } // TcpOptionWriteError { use TcpOptionWriteError::*; let value = NotEnoughSpace(123); assert_eq!(value, value.clone()); } // TcpOptionsIterator { use tcp_option::*; let it = TcpOptionsIterator::from_slice(&[ KIND_END ]); assert_eq!(it, it.clone()); } } #[test] fn debug() { // header { let header: TcpHeader = Default::default(); // normal debug printing assert_eq!( format!( "TcpHeader {{ source_port: {}, destination_port: {}, sequence_number: {}, acknowledgment_number: {}, data_offset: {}, ns: {}, fin: {}, syn: {}, rst: {}, psh: {}, ack: {}, urg: {}, ece: {}, cwr: {}, window_size: {}, checksum: {}, urgent_pointer: {}, options: [] }}", header.source_port, header.destination_port, header.sequence_number, header.acknowledgment_number, header.data_offset(), header.ns, header.fin, header.syn, header.rst, header.psh, header.ack, header.urg, header.ece, header.cwr, header.window_size, header.checksum, header.urgent_pointer ), format!("{:?}", header) ); // multi line debug printing assert_eq!( format!( "TcpHeader {{ source_port: {}, destination_port: {}, sequence_number: {}, acknowledgment_number: {}, data_offset: {}, ns: {}, fin: {}, syn: {}, rst: {}, psh: {}, ack: {}, urg: {}, ece: {}, cwr: {}, window_size: {}, checksum: {}, urgent_pointer: {}, options: [], }}", header.source_port, header.destination_port, header.sequence_number, header.acknowledgment_number, header.data_offset(), header.ns, header.fin, header.syn, header.rst, header.psh, header.ack, header.urg, header.ece, header.cwr, header.window_size, header.checksum, header.urgent_pointer ), format!("{:#?}", header) ); } // slice (auto generated, just make sure the implementation is there) { let header: TcpHeader = Default::default(); println!("{:?}", header); let buffer = { let mut buffer = Vec::with_capacity(header.header_len().into()); header.write(&mut buffer).unwrap(); buffer }; let slice = TcpHeaderSlice::from_slice(&buffer).unwrap(); println!("{:?}", slice); assert_eq!(slice, slice.clone()); } // TcpOptionElement { use TcpOptionElement::*; assert_eq!("Noop", format!("{:?}", Noop)); assert_eq!( "MaximumSegmentSize(123)", format!("{:?}", MaximumSegmentSize(123)) ); assert_eq!( "WindowScale(123)", format!("{:?}", WindowScale(123)) ); assert_eq!( "SelectiveAcknowledgementPermitted", format!("{:?}", SelectiveAcknowledgementPermitted) ); assert_eq!( "SelectiveAcknowledgement((1, 2), [Some((3, 4)), Some((5, 6)), None])", format!("{:?}", SelectiveAcknowledgement((1, 2), [Some((3,4)), Some((5,6)), None]) ) ); assert_eq!( "Timestamp(123, 456)", format!("{:?}", Timestamp(123,456)) ); } // TcpOptionReadError { use TcpOptionReadError::*; assert_eq!( "UnexpectedEndOfSlice { option_id: 1, expected_len: 2, actual_len: 3 }", format!( "{:?}", UnexpectedEndOfSlice{ option_id: 1, expected_len: 2, actual_len: 3 } ) ); } // TcpOptionWriteError { use TcpOptionWriteError::*; assert_eq!( "NotEnoughSpace(0)", format!("{:?}", NotEnoughSpace(0)) ); } // TcpOptionsIterator { use tcp_option::*; assert_eq!( "[MaximumSegmentSize(0), WindowScale(0)]", format!( "{:?}", TcpOptionsIterator::from_slice(&[ KIND_MAXIMUM_SEGMENT_SIZE, 4, 0, 0, KIND_WINDOW_SCALE, 3, 0, KIND_END, ]) ) ); assert_eq!( "[MaximumSegmentSize(0), Err(UnexpectedSize { option_id: 3, size: 0 })]", format!( "{:?}", TcpOptionsIterator::from_slice(&[ KIND_MAXIMUM_SEGMENT_SIZE, 4, 0, 0, KIND_WINDOW_SCALE, 0, 0, 0, ]) ) ); } } #[test] fn options() { let base : TcpHeader = Default::default(); let dummy = [ 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,15, 16,17,18,19,20, 21,22,23,24,25, 26,27,28,29,30, 31,32,33,34,35, 36,37,38,39,40, 41 ]; //ok size -> expect output based on options size for i in 0..40 { let mut header = base.clone(); //set the options header.set_options_raw(&dummy[..i]).unwrap(); //determine the expected options length let mut options_length = i / 4; if i % 4 != 0 { options_length += 1; } options_length = options_length * 4; //expecetd data let mut expected_options = [0;40]; expected_options[..i].copy_from_slice(&dummy[..i]); assert_eq!(options_length, header.options_len()); assert_eq!((options_length / 4) as u8 + TCP_MINIMUM_DATA_OFFSET, header.data_offset()); assert_eq!(&expected_options[..options_length], header.options()); } //too big -> expect error let mut header = base.clone(); use crate::TcpOptionWriteError::*; assert_eq!(Err(NotEnoughSpace(dummy.len())), header.set_options_raw(&dummy[..])); } fn write_options(elements: &[TcpOptionElement]) -> TcpHeader { let mut result : TcpHeader = Default::default(); result.set_options(elements).unwrap(); result } proptest! { #[test] fn set_options_maximum_segment_size(arg in any::()) { use crate::TcpOptionElement::*; assert_eq!(write_options(&[Noop, Noop, MaximumSegmentSize(arg), Noop]).options(), &{ use tcp_option::*; let arg_be = arg.to_be_bytes(); [ KIND_NOOP, KIND_NOOP, KIND_MAXIMUM_SEGMENT_SIZE, 4, arg_be[0], arg_be[1], KIND_NOOP, KIND_END ] } ); } } proptest! { #[test] fn set_options_window_scale(arg in any::()) { use crate::TcpOptionElement::*; use tcp_option::*; assert_eq!(write_options(&[Noop, Noop, WindowScale(arg), Noop]).options(), &[ KIND_NOOP, KIND_NOOP, KIND_WINDOW_SCALE, 3, arg, KIND_NOOP, KIND_END, 0 ] ); } } #[test] fn set_options_selective_ack_perm() { use crate::TcpOptionElement::*; use tcp_option::*; assert_eq!(write_options(&[Noop, Noop, SelectiveAcknowledgementPermitted, Noop]).options(), &[ KIND_NOOP, KIND_NOOP, KIND_SELECTIVE_ACK_PERMITTED, 2, KIND_NOOP, KIND_END, 0, 0 ] ); } proptest! { #[test] fn set_options_selective_ack(args in proptest::collection::vec(any::(), 4*2)) { use crate::TcpOptionElement::*; use tcp_option::*; let args_be : Vec<[u8;4]> = args.iter().map(|v| v.to_be_bytes()).collect(); //1 assert_eq!( write_options(&[Noop, Noop, SelectiveAcknowledgement((args[0], args[1]), [None, None, None]), Noop]).options(), &[ KIND_NOOP, KIND_NOOP, KIND_SELECTIVE_ACK, 10, args_be[0][0], args_be[0][1], args_be[0][2], args_be[0][3], args_be[1][0], args_be[1][1], args_be[1][2], args_be[1][3], KIND_NOOP, KIND_END, 0, 0 ] ); //2 assert_eq!( write_options( &[ Noop, Noop, SelectiveAcknowledgement( (args[0], args[1]), [Some((args[2], args[3])), None, None] ), Noop ] ).options(), [ KIND_NOOP, KIND_NOOP, KIND_SELECTIVE_ACK, 18, args_be[0][0], args_be[0][1], args_be[0][2], args_be[0][3], args_be[1][0], args_be[1][1], args_be[1][2], args_be[1][3], args_be[2][0], args_be[2][1], args_be[2][2], args_be[2][3], args_be[3][0], args_be[3][1], args_be[3][2], args_be[3][3], KIND_NOOP, KIND_END, 0, 0 ] ); //3 assert_eq!( write_options( &[ Noop, Noop, SelectiveAcknowledgement( (args[0], args[1]), [ Some((args[2], args[3])), Some((args[4], args[5])), None ] ), Noop ] ).options(), &[ KIND_NOOP, KIND_NOOP, KIND_SELECTIVE_ACK, 26, args_be[0][0], args_be[0][1], args_be[0][2], args_be[0][3], args_be[1][0], args_be[1][1], args_be[1][2], args_be[1][3], args_be[2][0], args_be[2][1], args_be[2][2], args_be[2][3], args_be[3][0], args_be[3][1], args_be[3][2], args_be[3][3], args_be[4][0], args_be[4][1], args_be[4][2], args_be[4][3], args_be[5][0], args_be[5][1], args_be[5][2], args_be[5][3], KIND_NOOP, KIND_END, 0, 0 ] ); //4 assert_eq!( write_options( &[ Noop, Noop, SelectiveAcknowledgement( (args[0], args[1]), [ Some((args[2], args[3])), Some((args[4], args[5])), Some((args[6], args[7])) ] ), Noop ] ).options(), &[ KIND_NOOP, KIND_NOOP, KIND_SELECTIVE_ACK, 34, args_be[0][0], args_be[0][1], args_be[0][2], args_be[0][3], args_be[1][0], args_be[1][1], args_be[1][2], args_be[1][3], args_be[2][0], args_be[2][1], args_be[2][2], args_be[2][3], args_be[3][0], args_be[3][1], args_be[3][2], args_be[3][3], args_be[4][0], args_be[4][1], args_be[4][2], args_be[4][3], args_be[5][0], args_be[5][1], args_be[5][2], args_be[5][3], args_be[6][0], args_be[6][1], args_be[6][2], args_be[6][3], args_be[7][0], args_be[7][1], args_be[7][2], args_be[7][3], KIND_NOOP, KIND_END, 0, 0 ] ); } } proptest! { #[test] fn set_options_timestamp( arg0 in any::(), arg1 in any::() ) { use crate::TcpOptionElement::*; use tcp_option::*; assert_eq!(write_options(&[Noop, Noop, Timestamp(arg0, arg1), Noop]).options(), &{ let arg0_be = arg0.to_be_bytes(); let arg1_be = arg1.to_be_bytes(); [ KIND_NOOP, KIND_NOOP, KIND_TIMESTAMP, 10, arg0_be[0], arg0_be[1], arg0_be[2], arg0_be[3], arg1_be[0], arg1_be[1], arg1_be[2], arg1_be[3], KIND_NOOP, KIND_END, 0, 0 ] } ); } } #[test] fn set_option_padding() { use crate::TcpOptionElement::*; let mut tcp_header = TcpHeader::default(); tcp_header.set_options(&[MaximumSegmentSize(1400), // 4 SelectiveAcknowledgementPermitted, // 2 Timestamp(2661445915, 0), // 10 Noop, // 1 WindowScale(7)]).unwrap(); // 3 // total 20 // + header 20 = 40 byte assert_eq!(40, tcp_header.header_len()); } #[test] fn set_options_not_enough_memory_error() { use crate::TcpOptionElement::*; assert_eq!(Err(TcpOptionWriteError::NotEnoughSpace(41)), TcpHeader::default().set_options( &[MaximumSegmentSize(1), //4 WindowScale(2), //+3 = 7 SelectiveAcknowledgementPermitted, //+2 = 9 SelectiveAcknowledgement((3,4), [Some((5,6)), None, None]), // + 18 = 27 Timestamp(5, 6), // + 10 = 37 Noop, Noop, Noop, Noop // + 4 ])); //test with all fields filled of the selective ack assert_eq!(Err(TcpOptionWriteError::NotEnoughSpace(41)), TcpHeader::default().set_options( &[Noop, // 1 SelectiveAcknowledgement((3,4), [Some((5,6)), Some((5,6)), Some((5,6))]), // + 34 = 35 MaximumSegmentSize(1), // + 4 = 39 Noop, Noop // + 2 = 41 ])); //test with all fields filled of the selective ack assert_eq!(Err(TcpOptionWriteError::NotEnoughSpace(41)), TcpHeader::default().set_options( &[Noop, // 1 SelectiveAcknowledgement((3,4), [None, None, None]), // + 10 = 11 Timestamp(1,2), // + 10 = 21 Timestamp(1,2), // + 10 = 31 MaximumSegmentSize(1), // + 4 = 35 Noop, Noop, Noop, Noop, Noop, Noop // + 6 = 41 ])); } } // mod header proptest! { #[test] fn read_write(ref input in tcp_any()) { // serialize let mut buffer: Vec = Vec::with_capacity(60); input.write(&mut buffer).unwrap(); // check length assert_eq!(input.data_offset() as usize * 4, buffer.len()); assert_eq!(input.header_len() as usize, buffer.len()); // add some more data to check the returning slice buffer.push(1); // deserialize with read { let result = TcpHeader::read(&mut Cursor::new(&buffer)).unwrap(); // check equivalence (read) assert_eq!(input, &result); } // deserialize with from_slice { let result = TcpHeader::from_slice(&buffer).unwrap(); assert_eq!(input, &result.0); assert_eq!(&buffer[buffer.len()-1..], result.1); } // deserialize with read_from_slice #[allow(deprecated)] { let result = TcpHeader::read_from_slice(&buffer).unwrap(); assert_eq!(input, &result.0); assert_eq!(&buffer[buffer.len()-1..], result.1); } } } proptest! { #[test] fn read_data_offset_too_small(ref input in tcp_any(), data_offset in 0..TCP_MINIMUM_DATA_OFFSET) { //serialize let mut buffer: Vec = Vec::with_capacity(60); input.write(&mut buffer).unwrap(); //insert the too small data offset into the raw stream buffer[12] = (buffer[12] & 0xf) | ((data_offset << 4) & 0xf0); //deserialize assert_matches!(TcpHeader::read(&mut Cursor::new(&buffer)), Err(ReadError::TcpDataOffsetTooSmall(_))); } } proptest! { #[test] fn read_unexpected_eof(ref input in tcp_any()) { //serialize let mut buffer: Vec = Vec::with_capacity(60); input.write(&mut buffer).unwrap(); //deserialize let len = buffer.len() - 1; assert_matches!(TcpHeader::read(&mut Cursor::new(&buffer[..len])), Err(ReadError::IoError(_))); } } #[test] fn write_and_read_length_error() { let headers = { let base_header = TcpHeader::new(1234,4567,9876,789); use TcpOptionElement::*; [ { let mut header = base_header.clone(); header.ns = true; header.fin = true; header.syn = true; header.rst = true; header.psh = true; header.ack = true; header.urg = true; header.ece = true; header.cwr = true; header }, { let mut header = base_header.clone(); header.set_options(&[ MaximumSegmentSize(111), WindowScale(222), SelectiveAcknowledgementPermitted, Timestamp(12,23) ]).unwrap(); header }, { let mut header = base_header.clone(); header.set_options(&[ SelectiveAcknowledgement( (1,2), [Some((3,4)),Some((5,6)), Some((7,8))] ), ]).unwrap(); header } ] }; for header in &headers { // write not enough space for len in 0..usize::from(header.header_len()) { let mut writer = TestWriter::with_max_size(len); assert_eq!( writer.error_kind(), header.write(&mut writer).unwrap_err().kind() ); } let buffer = { let mut buffer = Vec::with_capacity(header.header_len().into()); header.write(&mut buffer).unwrap(); buffer }; for len in 0..buffer.len() { use ReadError::*; // read assert_matches!( TcpHeader::read(&mut Cursor::new(&buffer[0..len])), Err(IoError(_)) ); // from_slice assert_matches!( TcpHeader::from_slice(&buffer[0..len]), Err(UnexpectedEndOfSlice(_)) ); // from_slice assert_matches!( TcpHeaderSlice::from_slice(&buffer[0..len]), Err(UnexpectedEndOfSlice(_)) ); } } } proptest! { #[test] fn packet_slice_from_slice(ref input in tcp_any()) { //serialize let mut buffer: Vec = Vec::with_capacity(60); input.write(&mut buffer).unwrap(); //create slice let slice = TcpHeaderSlice::from_slice(&buffer).unwrap(); //check all fields assert_eq!(input.source_port, slice.source_port()); assert_eq!(input.destination_port, slice.destination_port()); assert_eq!(input.sequence_number, slice.sequence_number()); assert_eq!(input.acknowledgment_number, slice.acknowledgment_number()); assert_eq!(input.data_offset(), slice.data_offset()); assert_eq!(input.ns, slice.ns()); assert_eq!(input.fin, slice.fin()); assert_eq!(input.syn, slice.syn()); assert_eq!(input.rst, slice.rst()); assert_eq!(input.psh, slice.psh()); assert_eq!(input.ack, slice.ack()); assert_eq!(input.ece, slice.ece()); assert_eq!(input.urg, slice.urg()); assert_eq!(input.cwr, slice.cwr()); assert_eq!(input.window_size, slice.window_size()); assert_eq!(input.checksum, slice.checksum()); assert_eq!(input.urgent_pointer, slice.urgent_pointer()); assert_eq!(input.options(), slice.options()); //check the to_header result assert_eq!(input, &slice.to_header()); } } proptest! { #[test] fn packet_slice_from_slice_data_offset_too_small(ref input in tcp_any(), data_offset in 0..TCP_MINIMUM_DATA_OFFSET) { //serialize let mut buffer: Vec = Vec::with_capacity(60); input.write(&mut buffer).unwrap(); //insert the too small data offset into the raw stream buffer[12] = (buffer[12] & 0xf) | ((data_offset << 4) & 0xf0); //deserialize assert_matches!(TcpHeaderSlice::from_slice(&buffer), Err(ReadError::TcpDataOffsetTooSmall(_))); } } proptest! { #[test] fn debug_fmt(ref input in tcp_any()) { assert_eq!(&format!("TcpHeader {{ source_port: {}, destination_port: {}, sequence_number: {}, acknowledgment_number: {}, data_offset: {}, ns: {}, fin: {}, syn: {}, rst: {}, psh: {}, ack: {}, urg: {}, ece: {}, cwr: {}, window_size: {}, checksum: {}, urgent_pointer: {}, options: {:?} }}", input.source_port, input.destination_port, input.sequence_number, input.acknowledgment_number, input.data_offset(), input.ns, input.fin, input.syn, input.rst, input.psh, input.ack, input.urg, input.ece, input.cwr, input.window_size, input.checksum, input.urgent_pointer, input.options_iterator(), ), &format!("{:?}", input) ); } } #[test] fn calc_header_checksum_ipv4() { use crate::TcpOptionElement::*; //checksum == 0xf (no carries) (aka sum == 0xffff) { let tcp_payload = [1,2,3,4,5,6,7,8]; //write the udp header let tcp = TcpHeader::new( //source port 0, //destination port 0, 40905, 0 ); let ip_header = Ipv4Header::new( //payload length tcp.header_len() + (tcp_payload.len() as u16), //time to live 0, //contained protocol is udp ip_number::TCP, //source ip address [0;4], //destination ip address [0;4] ); assert_eq!(Ok(0x0), tcp.calc_checksum_ipv4(&ip_header, &tcp_payload)); assert_eq!(Ok(0x0), tcp.calc_checksum_ipv4_raw(ip_header.source, ip_header.destination, &tcp_payload)); } //a header with options { let tcp_payload = [1,2,3,4,5,6,7,8]; let mut tcp = TcpHeader::new( //source port 69, //destination port 42, 0x24900448, 0x3653 ); tcp.urgent_pointer = 0xE26E; tcp.ns = true; tcp.fin = true; tcp.syn = true; tcp.rst = true; tcp.psh = true; tcp.ack = true; tcp.ece = true; tcp.urg = true; tcp.cwr = true; tcp.set_options(&[ Noop, Noop, Noop, Noop, Timestamp(0x4161008, 0x84161708) ]).unwrap(); let ip_header = Ipv4Header::new( //payload length tcp.header_len() + (tcp_payload.len() as u16), //time to live 20, //contained protocol is udp ip_number::TCP, //source ip address [192,168,1,42], //destination ip address [192,168,1,1] ); //check checksum assert_eq!(Ok(0xdeeb), tcp.calc_checksum_ipv4(&ip_header, &tcp_payload)); assert_eq!(Ok(0xdeeb), tcp.calc_checksum_ipv4_raw(ip_header.source, ip_header.destination, &tcp_payload)); //test PacketSlice version let mut ip_buffer = Vec::new(); ip_header.write(&mut ip_buffer).unwrap(); let ip_slice = Ipv4HeaderSlice::from_slice(&ip_buffer[..]).unwrap(); let mut tcp_buffer = Vec::new(); tcp.write(&mut tcp_buffer).unwrap(); let tcp_slice = TcpHeaderSlice::from_slice(&tcp_buffer[..]).unwrap(); assert_eq!(Ok(0xdeeb), tcp_slice.calc_checksum_ipv4(&ip_slice, &tcp_payload)); assert_eq!(Ok(0xdeeb), tcp_slice.calc_checksum_ipv4_raw(ip_slice.source(), ip_slice.destination(), &tcp_payload)); } //a header with an uneven number of options { let tcp_payload = [1,2,3,4,5,6,7,8,9]; let mut tcp = TcpHeader::new( //source port 69, //destination port 42, 0x24900448, 0x3653 ); tcp.urgent_pointer = 0xE26E; tcp.ns = true; tcp.fin = true; tcp.syn = true; tcp.rst = true; tcp.psh = true; tcp.ack = true; tcp.ece = true; tcp.urg = true; tcp.cwr = true; tcp.set_options(&[ Noop, Noop, Noop, Noop, Timestamp(0x4161008, 0x84161708) ]).unwrap(); let ip_header = Ipv4Header::new( //payload length tcp.header_len() + (tcp_payload.len() as u16), //time to live 20, //contained protocol is udp ip_number::TCP, //source ip address [192,168,1,42], //destination ip address [192,168,1,1] ); //check checksum assert_eq!(Ok(0xd5ea), tcp.calc_checksum_ipv4(&ip_header, &tcp_payload)); assert_eq!(Ok(0xd5ea), tcp.calc_checksum_ipv4_raw(ip_header.source, ip_header.destination, &tcp_payload)); //test PacketSlice version let mut ip_buffer = Vec::new(); ip_header.write(&mut ip_buffer).unwrap(); let ip_slice = Ipv4HeaderSlice::from_slice(&ip_buffer[..]).unwrap(); let mut tcp_buffer = Vec::new(); tcp.write(&mut tcp_buffer).unwrap(); let tcp_slice = TcpHeaderSlice::from_slice(&tcp_buffer[..]).unwrap(); assert_eq!(Ok(0xd5ea), tcp_slice.calc_checksum_ipv4(&ip_slice, &tcp_payload)); assert_eq!(Ok(0xd5ea), tcp_slice.calc_checksum_ipv4_raw(ip_slice.source(), ip_slice.destination(), &tcp_payload)); } } #[test] fn calc_header_checksum_ipv6() { let tcp_payload = [51,52,53,54,55,56,57,58]; //write the tcp header let mut tcp = TcpHeader::new( //source port 69, //destination port 42, 0x24900448, 0x3653 ); tcp.urgent_pointer = 0xE26E; tcp.ns = true; tcp.fin = true; tcp.syn = true; tcp.rst = true; tcp.psh = true; tcp.ack = true; tcp.ece = true; tcp.urg = true; tcp.cwr = true; use crate::TcpOptionElement::*; tcp.set_options(&[ Noop, Noop, Noop, Noop, Timestamp(0x4161008, 0x84161708) ]).unwrap(); let ip_header = Ipv6Header { traffic_class: 1, flow_label: 0x81806, payload_length: tcp_payload.len() as u16 + tcp.header_len(), next_header: ip_number::TCP, hop_limit: 40, source: [1,2,3,4,5,6,7,8, 9,10,11,12,13,14,15,16], destination: [21,22,23,24,25,26,27,28, 29,30,31,32,33,34,35,36] }; //check checksum assert_eq!(Ok(0x786e), tcp.calc_checksum_ipv6(&ip_header, &tcp_payload)); assert_eq!(Ok(0x786e), tcp.calc_checksum_ipv6_raw(ip_header.source, ip_header.destination, &tcp_payload)); //test PacketSlice version let mut ip_buffer = Vec::new(); ip_header.write(&mut ip_buffer).unwrap(); let ip_slice = Ipv6HeaderSlice::from_slice(&ip_buffer[..]).unwrap(); let mut tcp_buffer = Vec::new(); tcp.write(&mut tcp_buffer).unwrap(); let tcp_slice = TcpHeaderSlice::from_slice(&tcp_buffer[..]).unwrap(); assert_eq!(Ok(0x786e), tcp_slice.calc_checksum_ipv6(&ip_slice, &tcp_payload)); assert_eq!(Ok(0x786e), tcp_slice.calc_checksum_ipv6_raw(ip_slice.source(), ip_slice.destination(), &tcp_payload)); } #[test] fn calc_header_checksum_ipv4_error() { //write the udp header let tcp: TcpHeader = Default::default(); let len = (std::u16::MAX - tcp.header_len()) as usize + 1; let mut tcp_payload = Vec::with_capacity(len); tcp_payload.resize(len, 0); let ip_header = Ipv4Header::new(0, 0, ip_number::TCP, [0;4], [0;4]); assert_eq!(Err(ValueError::TcpLengthTooLarge(std::u16::MAX as usize + 1)), tcp.calc_checksum_ipv4(&ip_header, &tcp_payload)); assert_eq!(Err(ValueError::TcpLengthTooLarge(std::u16::MAX as usize + 1)), tcp.calc_checksum_ipv4_raw(ip_header.source, ip_header.destination, &tcp_payload)); //test PacketSlice version let mut ip_buffer = Vec::new(); ip_header.write(&mut ip_buffer).unwrap(); let ip_slice = Ipv4HeaderSlice::from_slice(&ip_buffer[..]).unwrap(); let mut tcp_buffer = Vec::new(); tcp.write(&mut tcp_buffer).unwrap(); let tcp_slice = TcpHeaderSlice::from_slice(&tcp_buffer[..]).unwrap(); assert_eq!(Err(ValueError::TcpLengthTooLarge(std::u16::MAX as usize + 1)), tcp_slice.calc_checksum_ipv4(&ip_slice, &tcp_payload)); assert_eq!(Err(ValueError::TcpLengthTooLarge(std::u16::MAX as usize + 1)), tcp_slice.calc_checksum_ipv4_raw(ip_slice.source(), ip_slice.destination(), &tcp_payload)); } //this test can only run on 64bit systems as we can not represent slices that are too big on 32 bit and bellow #[test] #[cfg(target_pointer_width = "64")] fn calc_header_checksum_ipv6_error() { //write the udp header let tcp: TcpHeader = Default::default(); let len = (std::u32::MAX - tcp.header_len() as u32) as usize + 1; //lets create a slice of that size that points to zero //(as most systems can not allocate blocks of the size of u32::MAX) let tcp_payload = unsafe { //NOTE: The pointer must be initialized with a non null value // otherwise a key constraint of slices is not fullfilled // which can lead to crashes in release mode. use std::ptr::NonNull; slice::from_raw_parts( NonNull::::dangling().as_ptr(), len ) }; let ip_header = Ipv6Header { traffic_class: 1, flow_label: 0x81806, payload_length: 0, //lets assume jumbograms behavior (set to 0, as bigger then u16) next_header: ip_number::TCP, hop_limit: 40, source: [1,2,3,4,5,6,7,8, 9,10,11,12,13,14,15,16], destination: [21,22,23,24,25,26,27,28, 29,30,31,32,33,34,35,36] }; assert_eq!(Err(ValueError::TcpLengthTooLarge(std::u32::MAX as usize + 1)), tcp.calc_checksum_ipv6(&ip_header, &tcp_payload)); assert_eq!(Err(ValueError::TcpLengthTooLarge(std::u32::MAX as usize + 1)), tcp.calc_checksum_ipv6_raw(ip_header.source, ip_header.destination, &tcp_payload)); //test PacketSlice version let mut ip_buffer = Vec::new(); ip_header.write(&mut ip_buffer).unwrap(); let ip_slice = Ipv6HeaderSlice::from_slice(&ip_buffer[..]).unwrap(); let mut tcp_buffer = Vec::new(); tcp.write(&mut tcp_buffer).unwrap(); let tcp_slice = TcpHeaderSlice::from_slice(&tcp_buffer[..]).unwrap(); assert_eq!(Err(ValueError::TcpLengthTooLarge(std::u32::MAX as usize + 1)), tcp_slice.calc_checksum_ipv6(&ip_slice, &tcp_payload)); assert_eq!(Err(ValueError::TcpLengthTooLarge(std::u32::MAX as usize + 1)), tcp_slice.calc_checksum_ipv6_raw(ip_slice.source(), ip_slice.destination(), &tcp_payload)); } #[test] fn options_iterator_method() { let options = [ TcpOptionElement::Timestamp(0x00102030, 0x01112131), //10 TcpOptionElement::SelectiveAcknowledgement((0x02122232,0x03132333), [None, None, None]), //20 TcpOptionElement::Timestamp(0x04142434, 0x05152535), //30 TcpOptionElement::Timestamp(0x06162636, 0x07172737), //40 ]; let base : TcpHeader = { let mut base : TcpHeader = Default::default(); base.set_options(&options[..]).unwrap(); base }; assert_eq!( &options[..], &base .options_iterator() .map(|x| x.unwrap()) .collect::>()[..] ); } #[test] fn options_iterator() { use crate::TcpOptionElement::*; use tcp_option::*; let header = { let mut header : TcpHeader = Default::default(); header.set_options_raw(&[ KIND_NOOP, KIND_NOOP, KIND_MAXIMUM_SEGMENT_SIZE, 4, 0, 1, KIND_END, 0, 0, 0 ]).unwrap(); header }; // TcpHeader::options_iterator { let mut it = header.options_iterator(); let expected = [ Noop, Noop, MaximumSegmentSize(1), ]; for element in expected.iter() { assert_eq!(element, &it.next().unwrap().unwrap()); } assert_eq!(None, it.next()); assert_eq!(0, it.rest().len()); } // TcpHeaderSlice::options_iterator { let mut buffer = Vec::with_capacity(header.header_len().into()); header.write(&mut buffer).unwrap(); let slice = TcpHeaderSlice::from_slice(&buffer).unwrap(); let mut it = slice.options_iterator(); let expected = [ Noop, Noop, MaximumSegmentSize(1), ]; for element in expected.iter() { assert_eq!(element, &it.next().unwrap().unwrap()); } assert_eq!(None, it.next()); assert_eq!(0, it.rest().len()); } } #[test] fn options_iterator_from_slice() { fn expect_elements(buffer: &[u8], expected: &[TcpOptionElement]) { // options iterator via from_slice() let mut it = TcpOptionsIterator::from_slice(buffer); for element in expected.iter() { assert_eq!(element, &it.next().unwrap().unwrap()); } //expect no more elements assert_eq!(None, it.next()); assert_eq!(0, it.rest().len()); } use crate::TcpOptionElement::*; use tcp_option::*; //nop & max segment size expect_elements(&[ KIND_NOOP, KIND_NOOP, KIND_MAXIMUM_SEGMENT_SIZE, 4, 0, 1, KIND_WINDOW_SCALE, 3, 2, KIND_SELECTIVE_ACK_PERMITTED, 2, KIND_SELECTIVE_ACK, 10, 0, 0, 0, 10, 0, 0, 0, 11, KIND_SELECTIVE_ACK, 18, 0, 0, 0, 12, 0, 0, 0, 13, 0, 0, 0, 14, 0, 0, 0, 15, KIND_SELECTIVE_ACK, 26, 0, 0, 0, 16, 0, 0, 0, 17, 0, 0, 0, 18, 0, 0, 0, 19, 0, 0, 0, 20, 0, 0, 0, 21, KIND_SELECTIVE_ACK, 34, 0, 0, 0, 22, 0, 0, 0, 23, 0, 0, 0, 24, 0, 0, 0, 25, 0, 0, 0, 26, 0, 0, 0, 27, 0, 0, 0, 28, 0, 0, 0, 29, KIND_TIMESTAMP, 10, 0, 0, 0, 30, 0, 0, 0, 31, KIND_END, 0, 0, 0, 0 ], &[ Noop, Noop, MaximumSegmentSize(1), WindowScale(2), SelectiveAcknowledgementPermitted, SelectiveAcknowledgement((10,11), [None, None, None]), SelectiveAcknowledgement((12,13), [Some((14,15)), None, None]), SelectiveAcknowledgement((16,17), [Some((18,19)), Some((20,21)), None]), SelectiveAcknowledgement((22,23), [Some((24,25)), Some((26,27)), Some((28,29))]), Timestamp(30,31) ]); } #[test] fn options_iterator_unexpected_eos() { fn expect_unexpected_eos(slice: &[u8]) { for i in 1..slice.len()-1 { let mut it = TcpOptionsIterator::from_slice(&slice[..i]); assert_eq!( Some( Err( TcpOptionReadError::UnexpectedEndOfSlice{ option_id: slice[0], expected_len: match slice[0] { KIND_MAXIMUM_SEGMENT_SIZE => 4, KIND_WINDOW_SCALE => 3, KIND_SELECTIVE_ACK_PERMITTED => 2, KIND_SELECTIVE_ACK => if i < 2 { // the inial check only checks if there // is enough data to read the length field 2 } else { slice[1] }, KIND_TIMESTAMP => 10, _ => panic!("not part of the tests"), }, actual_len: i } ) ), it.next() ); //expect the iterator slice to be moved to the end assert_eq!(0, it.rest().len()); assert_eq!(None, it.next()); } } use tcp_option::*; expect_unexpected_eos(&[KIND_MAXIMUM_SEGMENT_SIZE, 4, 0, 0]); expect_unexpected_eos(&[KIND_WINDOW_SCALE, 3, 0]); expect_unexpected_eos(&[KIND_MAXIMUM_SEGMENT_SIZE, 4, 0, 0]); expect_unexpected_eos(&[KIND_SELECTIVE_ACK_PERMITTED, 2]); expect_unexpected_eos(&[KIND_SELECTIVE_ACK, 10, 0, 0, 0, 0, 0, 0, 0, 0]); expect_unexpected_eos(&[KIND_SELECTIVE_ACK, 18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); expect_unexpected_eos(&[KIND_SELECTIVE_ACK, 26, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); expect_unexpected_eos(&[KIND_SELECTIVE_ACK, 34, 0, 0, 0, 0, 0, 0, 0, 0, //10 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //20 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //30 0, 0, 0, 0]); expect_unexpected_eos(&[KIND_TIMESTAMP, 10, 0, 0, 0, 0, 0, 0, 0, 0]); } #[test] fn options_iterator_unexpected_length() { fn expect_unexpected_size(id: u8, size: u8) { let data = [id, size, 0, 0, 0, 0, 0, 0, 0, 0, //10 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //20 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //30 0, 0, 0, 0]; let mut it = TcpOptionsIterator::from_slice(&data); assert_eq!(Some(Err(TcpOptionReadError::UnexpectedSize {option_id: data[0], size: data[1] })), it.next()); //expect the iterator slice to be moved to the end assert_eq!(0, it.rest().len()); assert_eq!(None, it.next()); assert_eq!(0, it.rest().len()); } use tcp_option::*; expect_unexpected_size(KIND_MAXIMUM_SEGMENT_SIZE, 3); expect_unexpected_size(KIND_MAXIMUM_SEGMENT_SIZE, 5); expect_unexpected_size(KIND_WINDOW_SCALE, 2); expect_unexpected_size(KIND_WINDOW_SCALE, 4); expect_unexpected_size(KIND_MAXIMUM_SEGMENT_SIZE, 3); expect_unexpected_size(KIND_MAXIMUM_SEGMENT_SIZE, 5); expect_unexpected_size(KIND_SELECTIVE_ACK_PERMITTED, 1); expect_unexpected_size(KIND_SELECTIVE_ACK_PERMITTED, 3); expect_unexpected_size(KIND_SELECTIVE_ACK, 9); expect_unexpected_size(KIND_SELECTIVE_ACK, 11); expect_unexpected_size(KIND_SELECTIVE_ACK, 17); expect_unexpected_size(KIND_SELECTIVE_ACK, 19); expect_unexpected_size(KIND_SELECTIVE_ACK, 25); expect_unexpected_size(KIND_SELECTIVE_ACK, 27); expect_unexpected_size(KIND_SELECTIVE_ACK, 33); expect_unexpected_size(KIND_SELECTIVE_ACK, 35); expect_unexpected_size(KIND_TIMESTAMP, 9); expect_unexpected_size(KIND_TIMESTAMP, 11); } #[test] fn options_iterator_unexpected_id() { let data = [255, 2, 0, 0, 0, 0, 0, 0, 0, 0, //10 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //20 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //30 0, 0, 0, 0]; let mut it = TcpOptionsIterator::from_slice(&data); assert_eq!(Some(Err(TcpOptionReadError::UnknownId(255))), it.next()); //expect the iterator slice to be moved to the end assert_eq!(0, it.rest().len()); assert_eq!(None, it.next()); assert_eq!(0, it.rest().len()); } #[test] fn options_iterator_debug() { fn expect_elements(buffer: &[u8], expected: &[TcpOptionElement]) { // options iterator via from_slice() let mut it = TcpOptionsIterator::from_slice(buffer); for element in expected.iter() { assert_eq!(element, &it.next().unwrap().unwrap()); } //expect no more elements assert_eq!(None, it.next()); assert_eq!(0, it.rest().len()); } use crate::TcpOptionElement::*; use tcp_option::*; //nop & max segment size expect_elements(&[ KIND_NOOP, KIND_NOOP, KIND_MAXIMUM_SEGMENT_SIZE, 4, 0, 1, KIND_WINDOW_SCALE, 3, 2, KIND_SELECTIVE_ACK_PERMITTED, 2, KIND_SELECTIVE_ACK, 10, 0, 0, 0, 10, 0, 0, 0, 11, KIND_SELECTIVE_ACK, 18, 0, 0, 0, 12, 0, 0, 0, 13, 0, 0, 0, 14, 0, 0, 0, 15, KIND_SELECTIVE_ACK, 26, 0, 0, 0, 16, 0, 0, 0, 17, 0, 0, 0, 18, 0, 0, 0, 19, 0, 0, 0, 20, 0, 0, 0, 21, KIND_SELECTIVE_ACK, 34, 0, 0, 0, 22, 0, 0, 0, 23, 0, 0, 0, 24, 0, 0, 0, 25, 0, 0, 0, 26, 0, 0, 0, 27, 0, 0, 0, 28, 0, 0, 0, 29, KIND_TIMESTAMP, 10, 0, 0, 0, 30, 0, 0, 0, 31, KIND_END, 0, 0, 0, 0 ], &[ Noop, Noop, MaximumSegmentSize(1), WindowScale(2), SelectiveAcknowledgementPermitted, SelectiveAcknowledgement((10,11), [None, None, None]), SelectiveAcknowledgement((12,13), [Some((14,15)), None, None]), SelectiveAcknowledgement((16,17), [Some((18,19)), Some((20,21)), None]), SelectiveAcknowledgement((22,23), [Some((24,25)), Some((26,27)), Some((28,29))]), Timestamp(30,31) ] ); } proptest! { #[test] fn tcp_options_read_error_display( arg_u8_0 in any::(), arg_u8_1 in any::(), arg_usize in any::() ) { use crate::TcpOptionReadError::*; //UnexpectedEndOfSlice assert_eq!( &format!("TcpOptionReadError: Not enough memory left in slice to read option of kind {} (expected at least {} bytes, only {} bytes available).", arg_u8_0, arg_u8_1, arg_usize), &format!("{}", UnexpectedEndOfSlice{ option_id: arg_u8_0, expected_len: arg_u8_1, actual_len: arg_usize}) ); //UnexpectedSize assert_eq!( &format!("TcpOptionReadError: Length value of the option of kind {} had unexpected value {}.", arg_u8_0, arg_u8_1), &format!("{}", UnexpectedSize{ option_id: arg_u8_0, size: arg_u8_1 }) ); //UnknownId assert_eq!( &format!("TcpOptionReadError: Unknown tcp option kind value {}.", arg_u8_0), &format!("{}", UnknownId(arg_u8_0)) ); } } proptest! { #[test] fn tcp_options_read_error_source( arg_u8_0 in any::(), arg_u8_1 in any::(), arg_usize in any::() ) { use std::error::Error; use crate::TcpOptionReadError::*; assert!(UnexpectedEndOfSlice{ option_id: arg_u8_0, expected_len: arg_u8_1, actual_len: arg_usize}.source().is_none()); assert!(UnexpectedSize{ option_id: arg_u8_0, size: arg_u8_1 }.source().is_none()); assert!(UnknownId(arg_u8_0).source().is_none()); } } proptest! { #[test] fn tcp_options_write_error_display( arg_usize in any::() ) { use crate::TcpOptionWriteError::*; //NotEnoughSpace assert_eq!( &format!("TcpOptionWriteError: Not enough memory to store all options in the options section of a tcp header (maximum 40 bytes can be stored, the options would have needed {} bytes).", arg_usize), &format!("{}", NotEnoughSpace(arg_usize)) ); } } proptest! { #[test] fn tcp_options_write_error_source( arg_usize in any::() ) { use std::error::Error; use crate::TcpOptionWriteError::*; //NotEnoughSpace assert!(NotEnoughSpace(arg_usize).source().is_none()); } } etherparse-0.13.0/tests/transport/udp.rs000064400000000000000000000717531046102023000164200ustar 00000000000000use etherparse::*; use super::super::*; use std::io::{Cursor, ErrorKind}; mod udp_header { use super::*; proptest! { #[test] fn without_ipv4_checksum( source_port in any::(), destination_port in any::(), good_payload_length in 0..=((std::u16::MAX as usize) - UdpHeader::SERIALIZED_SIZE), bad_payload_length in ((std::u16::MAX as usize) - UdpHeader::SERIALIZED_SIZE + 1)..=usize::MAX, ) { // normal working call { let actual = UdpHeader::without_ipv4_checksum( source_port, destination_port, good_payload_length ).unwrap(); assert_eq!( actual, UdpHeader{ source_port, destination_port, length: (UdpHeader::SERIALIZED_SIZE + good_payload_length) as u16, checksum: 0 } ); } // length too large { let actual = UdpHeader::without_ipv4_checksum( source_port, destination_port, bad_payload_length ).unwrap_err(); assert_eq!( actual, ValueError::UdpPayloadLengthTooLarge(bad_payload_length) ); } } } /// Calculat the expected UDP header checksum for the tests. fn expected_udp_ipv4_checksum(source: [u8;4], destination: [u8;4], udp_header: &UdpHeader, payload: &[u8]) -> u16 { ::etherparse::checksum::Sum16BitWords::new() // pseudo header .add_4bytes(source) .add_4bytes(destination) .add_2bytes([0, ip_number::UDP]) .add_2bytes(udp_header.length.to_be_bytes()) // udp header .add_2bytes(udp_header.source_port.to_be_bytes()) .add_2bytes(udp_header.destination_port.to_be_bytes()) .add_2bytes(udp_header.length.to_be_bytes()) .add_2bytes([0, 0]) // checksum as zero (should have no effect) .add_slice(payload) .to_ones_complement_with_no_zero() .to_be() } proptest! { #[test] fn with_ipv4_checksum( source_port in any::(), destination_port in any::(), ipv4 in ipv4_any(), payload in proptest::collection::vec(any::(), 0..20), bad_len in ((std::u16::MAX as usize) - UdpHeader::SERIALIZED_SIZE + 1)..=usize::MAX, ) { // normal case assert_eq!( UdpHeader::with_ipv4_checksum( source_port, destination_port, &ipv4, &payload ).unwrap(), { let mut expected = UdpHeader { source_port, destination_port, length: (UdpHeader::SERIALIZED_SIZE + payload.len()) as u16, checksum: 0, }; let checksum = expected_udp_ipv4_checksum( ipv4.source, ipv4.destination, &expected, &payload ); expected.checksum = checksum; expected } ); // case where the 16 bit word results in a checksum of // 0, but gets converted to 0xffff as 0 is reserved. { let base = UdpHeader { source_port: 0, destination_port, length: (UdpHeader::SERIALIZED_SIZE + payload.len()) as u16, checksum: 0, }; // use the source port to force 0 as a result value // for that first calculate the checksum with the source // set to 0 let sourceless_checksum = !(expected_udp_ipv4_checksum( ipv4.source, ipv4.destination, &base, &payload ).to_le()); assert_eq!( UdpHeader::with_ipv4_checksum( // we now need to add a value that results in the value // 0xffff (which will become 0 via the ones complement rule). 0xffff - sourceless_checksum, destination_port, &ipv4, &payload ).unwrap(), UdpHeader{ source_port: 0xffff - sourceless_checksum, destination_port, length: base.length, checksum: 0xffff } ); } // length error case { // SAFETY: In case the error is not triggered // a segmentation fault will be triggered. let too_big_slice = unsafe { //NOTE: The pointer must be initialized with a non null value // otherwise a key constraint of slices is not fullfilled // which can lead to crashes in release mode. use std::ptr::NonNull; std::slice::from_raw_parts( NonNull::::dangling().as_ptr(), bad_len ) }; assert_eq!( ValueError::UdpPayloadLengthTooLarge(bad_len), UdpHeader::with_ipv4_checksum( source_port, destination_port, &ipv4, &too_big_slice ).unwrap_err() ); } } } proptest! { #[test] fn calc_checksum_ipv4_raw( source_port in any::(), destination_port in any::(), dummy_checksum in any::(), ipv4 in ipv4_any(), payload in proptest::collection::vec(any::(), 0..20), bad_len in ((std::u16::MAX as usize) - UdpHeader::SERIALIZED_SIZE + 1)..=usize::MAX, ) { // normal case { let header = UdpHeader { source_port, destination_port, length: (UdpHeader::SERIALIZED_SIZE + payload.len()) as u16, checksum: dummy_checksum, }; assert_eq!( header.calc_checksum_ipv4_raw( ipv4.source, ipv4.destination, &payload ).unwrap(), expected_udp_ipv4_checksum( ipv4.source, ipv4.destination, &header, &payload ) ); } // case where the 16 bit word results in a checksum of // 0, but gets converted to 0xffff as 0 is reserved. { let base = UdpHeader { source_port: 0, destination_port, length: (UdpHeader::SERIALIZED_SIZE + payload.len()) as u16, checksum: dummy_checksum, }; // use the source port to force 0 as a result value // for that first calculate the checksum with the source // set to 0 let sourceless_checksum = !(expected_udp_ipv4_checksum( ipv4.source, ipv4.destination, &base, &payload ).to_le()); // we now need to add a value that results in the value // 0xffff (which will become 0 via the ones complement rule). let header = { let mut header = base.clone(); header.source_port = 0xffff - sourceless_checksum; header }; assert_eq!( 0xffff, header.calc_checksum_ipv4_raw( ipv4.source, ipv4.destination, &payload ).unwrap() ); } // length error case { let header = UdpHeader { source_port, destination_port, // udp header length itself is ok, but the payload not length: (UdpHeader::SERIALIZED_SIZE + payload.len()) as u16, checksum: dummy_checksum, }; // SAFETY: In case the error is not triggered // a segmentation fault will be triggered. let too_big_slice = unsafe { //NOTE: The pointer must be initialized with a non null value // otherwise a key constraint of slices is not fullfilled // which can lead to crashes in release mode. use std::ptr::NonNull; std::slice::from_raw_parts( NonNull::::dangling().as_ptr(), bad_len ) }; assert_eq!( ValueError::UdpPayloadLengthTooLarge(bad_len), header.calc_checksum_ipv4_raw( ipv4.source, ipv4.destination, too_big_slice ).unwrap_err() ); } } } /// Calculat the expected UDP header checksum for the tests. fn expected_udp_ipv6_checksum(source: [u8;16], destination: [u8;16], udp_header: &UdpHeader, payload: &[u8]) -> u16 { ::etherparse::checksum::Sum16BitWords::new() // pseudo header .add_16bytes(source) .add_16bytes(destination) .add_2bytes([0, ip_number::UDP]) .add_4bytes(u32::from(udp_header.length).to_be_bytes()) // udp header .add_2bytes(udp_header.source_port.to_be_bytes()) .add_2bytes(udp_header.destination_port.to_be_bytes()) .add_2bytes(udp_header.length.to_be_bytes()) .add_2bytes([0, 0]) // checksum as zero (should have no effect) .add_slice(payload) .to_ones_complement_with_no_zero() .to_be() } proptest! { #[test] fn with_ipv6_checksum( source_port in any::(), destination_port in any::(), ipv6 in ipv6_any(), payload in proptest::collection::vec(any::(), 0..20), bad_len in ((std::u16::MAX as usize) - UdpHeader::SERIALIZED_SIZE + 1)..=usize::MAX, ) { // normal case assert_eq!( UdpHeader::with_ipv6_checksum( source_port, destination_port, &ipv6, &payload ).unwrap(), { let mut expected = UdpHeader { source_port, destination_port, length: (UdpHeader::SERIALIZED_SIZE + payload.len()) as u16, checksum: 0, }; let checksum = expected_udp_ipv6_checksum( ipv6.source, ipv6.destination, &expected, &payload ); expected.checksum = checksum; expected } ); // case where the 16 bit word results in a checksum of // 0, but gets converted to 0xffff as 0 is reserved. { let base = UdpHeader { source_port: 0, destination_port, length: (UdpHeader::SERIALIZED_SIZE + payload.len()) as u16, checksum: 0, }; // use the source port to force 0 as a result value // for that first calculate the checksum with the source // set to 0 let sourceless_checksum = !(expected_udp_ipv6_checksum( ipv6.source, ipv6.destination, &base, &payload ).to_le()); assert_eq!( UdpHeader::with_ipv6_checksum( // we now need to add a value that results in the value // 0xffff (which will become 0 via the ones complement rule). 0xffff - sourceless_checksum, destination_port, &ipv6, &payload ).unwrap(), UdpHeader{ source_port: 0xffff - sourceless_checksum, destination_port, length: base.length, checksum: 0xffff } ); } // length error case { // SAFETY: In case the error is not triggered // a segmentation fault will be triggered. let too_big_slice = unsafe { //NOTE: The pointer must be initialized with a non null value // otherwise a key constraint of slices is not fullfilled // which can lead to crashes in release mode. use std::ptr::NonNull; std::slice::from_raw_parts( NonNull::::dangling().as_ptr(), bad_len ) }; assert_eq!( ValueError::UdpPayloadLengthTooLarge(bad_len), UdpHeader::with_ipv6_checksum( source_port, destination_port, &ipv6, &too_big_slice ).unwrap_err() ); } } } proptest! { #[test] fn calc_checksum_ipv6( source_port in any::(), destination_port in any::(), ipv6 in ipv6_any(), payload in proptest::collection::vec(any::(), 0..20), bad_len in ((std::u32::MAX as usize) - UdpHeader::SERIALIZED_SIZE + 1)..=usize::MAX, ) { // normal case assert_eq!( UdpHeader::with_ipv6_checksum( source_port, destination_port, &ipv6, &payload ).unwrap(), { let mut expected = UdpHeader { source_port, destination_port, length: (UdpHeader::SERIALIZED_SIZE + payload.len()) as u16, checksum: 0, }; let checksum = expected_udp_ipv6_checksum( ipv6.source, ipv6.destination, &expected, &payload ); expected.checksum = checksum; expected } ); // case where the 16 bit word results in a checksum of // 0, but gets converted to 0xffff as 0 is reserved. { let base = UdpHeader { source_port: 0, destination_port, length: (UdpHeader::SERIALIZED_SIZE + payload.len()) as u16, checksum: 0, }; // use the source port to force 0 as a result value // for that first calculate the checksum with the source // set to 0 let sourceless_checksum = !(expected_udp_ipv6_checksum( ipv6.source, ipv6.destination, &base, &payload ).to_le()); assert_eq!( UdpHeader::with_ipv6_checksum( // we now need to add a value that results in the value // 0xffff (which will become 0 via the ones complement rule). 0xffff - sourceless_checksum, destination_port, &ipv6, &payload ).unwrap(), UdpHeader{ source_port: 0xffff - sourceless_checksum, destination_port, length: base.length, checksum: 0xffff } ); } // length error case { // SAFETY: In case the error is not triggered // a segmentation fault will be triggered. let too_big_slice = unsafe { //NOTE: The pointer must be initialized with a non null value // otherwise a key constraint of slices is not fullfilled // which can lead to crashes in release mode. use std::ptr::NonNull; std::slice::from_raw_parts( NonNull::::dangling().as_ptr(), bad_len ) }; assert_eq!( ValueError::UdpPayloadLengthTooLarge(bad_len), UdpHeader::with_ipv6_checksum( source_port, destination_port, &ipv6, &too_big_slice ).unwrap_err() ); } } } proptest! { #[test] fn calc_checksum_ipv6_raw( source_port in any::(), destination_port in any::(), dummy_checksum in any::(), ipv6 in ipv6_any(), payload in proptest::collection::vec(any::(), 0..20), bad_len in ((std::u32::MAX as usize) - UdpHeader::SERIALIZED_SIZE + 1)..=usize::MAX, ) { // normal case { let header = UdpHeader { source_port, destination_port, length: (UdpHeader::SERIALIZED_SIZE + payload.len()) as u16, checksum: dummy_checksum, }; assert_eq!( header.calc_checksum_ipv6_raw( ipv6.source, ipv6.destination, &payload ).unwrap(), expected_udp_ipv6_checksum( ipv6.source, ipv6.destination, &header, &payload ) ); } // case where the 16 bit word results in a checksum of // 0, but gets converted to 0xffff as 0 is reserved. { let base = UdpHeader { source_port: 0, destination_port, length: (UdpHeader::SERIALIZED_SIZE + payload.len()) as u16, checksum: dummy_checksum, }; // use the source port to force 0 as a result value // for that first calculate the checksum with the source // set to 0 let sourceless_checksum = !(expected_udp_ipv6_checksum( ipv6.source, ipv6.destination, &base, &payload ).to_le()); // we now need to add a value that results in the value // 0xffff (which will become 0 via the ones complement rule). let header = { let mut header = base.clone(); header.source_port = 0xffff - sourceless_checksum; header }; assert_eq!( 0xffff, header.calc_checksum_ipv6_raw( ipv6.source, ipv6.destination, &payload ).unwrap() ); } // length error case { let header = UdpHeader { source_port, destination_port, // udp header length itself is ok, but the payload not length: (UdpHeader::SERIALIZED_SIZE + payload.len()) as u16, checksum: dummy_checksum, }; // SAFETY: In case the error is not triggered // a segmentation fault will be triggered. let too_big_slice = unsafe { //NOTE: The pointer must be initialized with a non null value // otherwise a key constraint of slices is not fullfilled // which can lead to crashes in release mode. use std::ptr::NonNull; std::slice::from_raw_parts( NonNull::::dangling().as_ptr(), bad_len ) }; assert_eq!( ValueError::UdpPayloadLengthTooLarge(bad_len), header.calc_checksum_ipv6_raw( ipv6.source, ipv6.destination, too_big_slice ).unwrap_err() ); } } } proptest! { #[test] fn from_slice( input in udp_any(), dummy_data in proptest::collection::vec(any::(), 0..20) ) { // serialize let mut buffer: Vec = Vec::with_capacity(8 + dummy_data.len()); input.write(&mut buffer).unwrap(); buffer.extend(&dummy_data[..]); // calls with a valid result { let (result, rest) = UdpHeader::from_slice(&buffer[..]).unwrap(); assert_eq!(result, input); assert_eq!(rest, &buffer[8..]); } #[allow(deprecated)] { let (result, rest) = UdpHeader::read_from_slice(&buffer[..]).unwrap(); assert_eq!(result, input); assert_eq!(rest, &buffer[8..]); } // call with not enough data in the slice for len in 0..8 { assert_matches!( UdpHeader::from_slice(&buffer[0..len]), Err(ReadError::UnexpectedEndOfSlice(_)) ); } } } proptest! { #[test] fn from_bytes(input in udp_any()) { assert_eq!( input, UdpHeader::from_bytes( input.to_bytes() ) ); } } proptest! { #[test] fn read( input in udp_any(), dummy_data in proptest::collection::vec(any::(), 0..20) ) { // serialize let mut buffer: Vec = Vec::with_capacity(input.header_len() + dummy_data.len()); input.write(&mut buffer).unwrap(); buffer.extend(&dummy_data[..]); // normal { let mut cursor = Cursor::new(&buffer); let result = UdpHeader::read(&mut cursor).unwrap(); assert_eq!(result, input); assert_eq!(8, cursor.position()); } // unexpexted eof for len in 0..8 { let mut cursor = Cursor::new(&buffer[0..len]); assert_eq!( UdpHeader::read(&mut cursor) .unwrap_err() .kind(), ErrorKind::UnexpectedEof ); } } } proptest! { #[test] fn write(input in udp_any()) { // normal write { let mut result = Vec::with_capacity(input.header_len()); input.write(&mut result).unwrap(); assert_eq!( &result[..], input.to_bytes() ); } // unexpected eof for len in 0..8 { let mut writer = TestWriter::with_max_size(len); assert_eq!( ErrorKind::UnexpectedEof, input.write(&mut writer) .unwrap_err() .io_error() .unwrap() .kind() ); } } } proptest! { #[test] fn to_bytes(input in udp_any()) { let s_be = input.source_port.to_be_bytes(); let d_be = input.destination_port.to_be_bytes(); let l_be = input.length.to_be_bytes(); let c_be = input.checksum.to_be_bytes(); assert_eq!( input.to_bytes(), [ s_be[0], s_be[1], d_be[0], d_be[1], l_be[0], l_be[1], c_be[0], c_be[1], ] ); } } #[test] fn default() { let actual : UdpHeader = Default::default(); assert_eq!(actual.source_port, 0); assert_eq!(actual.destination_port, 0); assert_eq!(actual.length, 0); assert_eq!(actual.checksum, 0); } proptest! { #[test] fn clone_eq(input in udp_any()) { assert_eq!(input, input.clone()); { let mut other = input.clone(); other.source_port = !input.source_port; assert!(input != other); } } } proptest! { #[test] fn dbg(input in udp_any()) { assert_eq!( &format!( "UdpHeader {{ source_port: {}, destination_port: {}, length: {}, checksum: {} }}", input.source_port, input.destination_port, input.length, input.checksum, ), &format!("{:?}", input) ); } } } mod udp_header_slice { use super::*; proptest! { #[test] fn from_slice( input in udp_any(), dummy_data in proptest::collection::vec(any::(), 0..20) ) { // serialize let mut buffer: Vec = Vec::with_capacity(8 + dummy_data.len()); input.write(&mut buffer).unwrap(); buffer.extend(&dummy_data[..]); // calls with a valid result { let result = UdpHeaderSlice::from_slice(&buffer[..]).unwrap(); assert_eq!(&buffer[..8], result.slice()); } // call with not enough data in the slice for len in 0..8 { assert_matches!( UdpHeaderSlice::from_slice(&buffer[0..len]), Err(ReadError::UnexpectedEndOfSlice(_)) ); } } } proptest! { #[test] fn getters(input in udp_any()) { let bytes = input.to_bytes(); let slice = UdpHeaderSlice::from_slice(&bytes).unwrap(); assert_eq!(slice.source_port(), input.source_port); assert_eq!(slice.destination_port(), input.destination_port); assert_eq!(slice.length(), input.length); assert_eq!(slice.checksum(), input.checksum); } } proptest! { #[test] fn to_header(input in udp_any()) { let bytes = input.to_bytes(); let slice = UdpHeaderSlice::from_slice(&bytes).unwrap(); assert_eq!(input, slice.to_header()); } } proptest! { #[test] fn clone_eq(input in udp_any()) { let bytes = input.to_bytes(); let slice = UdpHeaderSlice::from_slice(&bytes).unwrap(); assert_eq!(slice, slice.clone()); } } proptest! { #[test] fn dbg(input in udp_any()) { let bytes = input.to_bytes(); let slice = UdpHeaderSlice::from_slice(&bytes).unwrap(); assert_eq!( &format!( "UdpHeaderSlice {{ slice: {:?} }}", slice.slice() ), &format!("{:?}", slice) ); } } } etherparse-0.13.0/tests/unit-tests.rs000064400000000000000000000134321046102023000157010ustar 00000000000000extern crate etherparse; use etherparse::*; #[macro_use] extern crate assert_matches; extern crate proptest; use std::io; mod checksum; mod errors; mod link; mod internet; mod transport; mod packet_builder; mod packet_decoder; mod packet_filter; mod packet_slicing; mod proptest_generators; pub use crate::proptest_generators::*; use proptest::prelude::*; mod packet_compositions; mod test_writer; use test_writer::*; mod test_reader; use test_reader::*; #[test] fn test_eq() { assert_eq!(ErrorField::Ipv4PayloadLength, ErrorField::Ipv4PayloadLength); assert_ne!(ErrorField::Ipv4PayloadLength, ErrorField::Ipv4Dscp); } #[test] fn test_debug_write() { //slice { let input = Ethernet2Header{ destination: [1,2,3,4,5,6], source: [10,11,12,13,14,15], ether_type: 0x0800 }; //serialize let mut buffer: Vec = Vec::with_capacity(14); input.write(&mut buffer).unwrap(); println!("{:?}", Ethernet2HeaderSlice::from_slice(&buffer)); } //read error { use crate::ReadError::*; for value in [ IoError(std::io::Error::new(std::io::ErrorKind::Other, "oh no!")), UnexpectedEndOfSlice(0), DoubleVlanOuterNonVlanEtherType(0), IpUnsupportedVersion(0), Ipv4UnexpectedVersion(0), Ipv4HeaderLengthBad(0), Ipv4TotalLengthTooSmall(0), Ipv6UnexpectedVersion(0), Ipv6TooManyHeaderExtensions, TcpDataOffsetTooSmall(0) ].iter() { println!("{:?}", value); } } //write error { use crate::ValueError::Ipv4OptionsLengthBad; use crate::WriteError::*; for value in [ IoError(std::io::Error::new(std::io::ErrorKind::Other, "oh no!")), ValueError(Ipv4OptionsLengthBad(0)), SliceTooSmall(0) ].iter() { println!("{:?}", value); } } //value error { use crate::ValueError::*; for value in [ Ipv4OptionsLengthBad(0), Ipv4PayloadLengthTooLarge(0), Ipv6PayloadLengthTooLarge(0), UdpPayloadLengthTooLarge(0), U8TooLarge{value: 0, max: 0, field: ErrorField::Ipv4Ecn}, U16TooLarge{value: 0, max: 0, field: ErrorField::Ipv4Ecn}, U32TooLarge{value: 0, max: 0, field: ErrorField::Ipv4Ecn} ].iter() { println!("{:?}", value); } } //error field { use crate::ErrorField::*; for value in [ Ipv4PayloadLength, Ipv4Dscp, Ipv4Ecn, Ipv4FragmentsOffset, Ipv6FlowLabel, VlanTagPriorityCodePoint, VlanTagVlanId ].iter() { println!("{:?}", value); } } //PacketHeaders { let dummy = vec![1,2,3,4]; let value = PacketHeaders{ link: None, vlan: None, ip: None, /*ip_extensions: [ None, None, None, None, None, None, None, None, None, None, None, None ],*/ transport: None, payload: &dummy[..] }; println!("{:?}", value); } } #[test] fn test_io_error_to_write_error() { assert_matches!(WriteError::from(std::io::Error::new(std::io::ErrorKind::Other, "oh no!")), WriteError::IoError(_)); } #[test] fn test_io_error_to_read_error() { assert_matches!(ReadError::from(std::io::Error::new(std::io::ErrorKind::Other, "oh no!")), ReadError::IoError(_)); } mod read_error { #[test] fn add_slice_offset() { use super::*; assert_matches!( ReadError::UnexpectedEndOfSlice(2).add_slice_offset(3), ReadError::UnexpectedEndOfSlice(5) ); assert_matches!( ReadError::UnexpectedLenOfSlice{ expected: 7, actual: 10 }.add_slice_offset(2), ReadError::UnexpectedLenOfSlice{ expected: 9, actual: 12 } ); assert_matches!( ReadError::DoubleVlanOuterNonVlanEtherType(2).add_slice_offset(3), ReadError::DoubleVlanOuterNonVlanEtherType(2) ); } #[test] fn io_error() { use super::*; assert_eq!( std::io::ErrorKind::Other, ReadError::IoError(std::io::Error::new(std::io::ErrorKind::Other, "oh no!")) .io_error().unwrap().kind() ); assert!( ReadError::UnexpectedEndOfSlice(0) .io_error().is_none() ); } #[test] fn unexpected_end_of_slice_min_expected_size() { use super::*; assert!( ReadError::IoError(std::io::Error::new(std::io::ErrorKind::Other, "oh no!")) .unexpected_end_of_slice_min_expected_size().is_none() ); assert_eq!( 123, ReadError::UnexpectedEndOfSlice(123) .unexpected_end_of_slice_min_expected_size().unwrap() ); } } mod write_error { #[test] fn io_error() { use super::*; assert_eq!( std::io::ErrorKind::Other, WriteError::IoError(std::io::Error::new(std::io::ErrorKind::Other, "oh no!")) .io_error().unwrap().kind() ); assert!( WriteError::ValueError(ValueError::TcpLengthTooLarge(0)) .io_error().is_none() ); } #[test] fn value_error() { use super::*; assert!( WriteError::IoError(std::io::Error::new(std::io::ErrorKind::Other, "oh no!")) .value_error().is_none() ); assert_eq!( Some(ValueError::TcpLengthTooLarge(0)), WriteError::ValueError(ValueError::TcpLengthTooLarge(0)) .value_error() ); } }