diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f0fed4db..6acc5bdb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,7 +4,6 @@ on: push: branches: [ main ] pull_request: - branches: [ main ] env: CARGO_TERM_COLOR: always @@ -13,12 +12,6 @@ env: VERBOSE: 1 jobs: - spell-check: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: crate-ci/typos@master - build-ubuntu: runs-on: ubuntu-latest steps: diff --git a/.github/workflows/fuzz.yml b/.github/workflows/fuzz.yml new file mode 100644 index 00000000..67310f0d --- /dev/null +++ b/.github/workflows/fuzz.yml @@ -0,0 +1,45 @@ +name: Fuzz + +on: + push: + branches: [main] + pull_request: + +jobs: + fuzz: + runs-on: ubuntu-latest + strategy: + # Run every target even if one finds a crash + fail-fast: false + matrix: + target: + - dns + - ether + - icmp + - icmpv6 + - ipv4 + - ipv6 + - tcp + - udp + - vlan + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@nightly + - uses: taiki-e/install-action@v2 + with: + tool: cargo-fuzz + - uses: Swatinem/rust-cache@v2 + with: + workspaces: fuzz + # 15 minutes per target on pushes to main, 1 minute on pull requests. + - run: | + cargo +nightly fuzz run --target x86_64-unknown-linux-gnu ${{ matrix.target }} -- \ + -max_total_time=${{ github.event_name == 'push' && '900' || '60' }} + # Preserve the crashing input so the failure can be reproduced locally + # with `cargo +nightly fuzz run `. + - if: failure() + uses: actions/upload-artifact@v4 + with: + name: fuzz-artifacts-${{ matrix.target }} + path: fuzz/artifacts/${{ matrix.target }}/ + if-no-files-found: ignore diff --git a/build.rs b/build.rs index 71a70cc0..f4cc5dbe 100644 --- a/build.rs +++ b/build.rs @@ -22,5 +22,9 @@ fn print_link_search_path() { fn print_link_search_path() {} fn main() { + // `pnettest` holds live-network integration tests that require elevated + // privileges and real interfaces; gate them behind an opt-in custom cfg. + // Declare it so `unexpected_cfgs` doesn't flag the `#[cfg(pnet_test_network)]`. + println!("cargo::rustc-check-cfg=cfg(pnet_test_network)"); print_link_search_path(); } diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index d12c41f5..ed28b951 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -4,6 +4,7 @@ name = "pnet-fuzz" version = "0.0.1" authors = ["Automatically generated"] publish = false +edition = "2021" [package.metadata] cargo-fuzz = true @@ -25,6 +26,10 @@ path = "fuzzers/ether.rs" name = "ipv4" path = "fuzzers/ipv4.rs" +[[bin]] +name = "ipv6" +path = "fuzzers/ipv6.rs" + [[bin]] name = "tcp" path = "fuzzers/tcp.rs" @@ -33,10 +38,18 @@ path = "fuzzers/tcp.rs" name = "udp" path = "fuzzers/udp.rs" -[[bin]] -name = "gre" -path = "fuzzers/gre.rs" - [[bin]] name = "vlan" path = "fuzzers/vlan.rs" + +[[bin]] +name = "icmp" +path = "fuzzers/icmp.rs" + +[[bin]] +name = "icmpv6" +path = "fuzzers/icmpv6.rs" + +[[bin]] +name = "dns" +path = "fuzzers/dns.rs" diff --git a/fuzz/fuzzers/dns.rs b/fuzz/fuzzers/dns.rs new file mode 100644 index 00000000..506d04c6 --- /dev/null +++ b/fuzz/fuzzers/dns.rs @@ -0,0 +1,78 @@ +#![no_main] + +use std::hint::black_box; + +use libfuzzer_sys::fuzz_target; +use pnet::packet::dns::{DnsPacket, DnsQuery, DnsResponse}; +use pnet::packet::{FromPacket, Packet, PacketSize}; + +fn drive_query(q: &DnsQuery) { + for b in q.qname.iter() { + black_box(*b); + } + black_box(q.qtype); + black_box(q.qclass); + for b in q.payload.iter() { + black_box(*b); + } + // Unchecked label parsing: indexes/slices `qname` and unwraps from_utf8. + black_box(q.get_qname_parsed()); +} + +fn drive_response(r: &DnsResponse) { + black_box(r.name_tag); + black_box(r.rtype); + black_box(r.rclass); + black_box(r.ttl); + black_box(r.data_len); + for b in r.data.iter() { + black_box(*b); + } + for b in r.payload.iter() { + black_box(*b); + } +} + +fuzz_target!(|data: &[u8]| { + if let Some(dns) = DnsPacket::new(data) { + // Fixed header. get_opcode()/get_rcode() map a u4 onto a small enum and + // panic (unreachable!) on unmapped values; get_*_count drive the + // variable-length section parsers below. + black_box(dns.get_id()); + black_box(dns.get_is_response()); + black_box(dns.get_opcode()); + black_box(dns.get_is_authoriative()); + black_box(dns.get_is_truncated()); + black_box(dns.get_is_recursion_desirable()); + black_box(dns.get_is_recursion_available()); + black_box(dns.get_zero_reserved()); + black_box(dns.get_is_answer_authenticated()); + black_box(dns.get_is_non_authenticated_data()); + black_box(dns.get_rcode()); + black_box(dns.get_query_count()); + black_box(dns.get_response_count()); + black_box(dns.get_authority_rr_count()); + black_box(dns.get_additional_rr_count()); + + // Variable-length sections. Each call parses the section and the + // per-record label/length fields. + for q in dns.get_queries() { + drive_query(&q); + } + for r in dns.get_responses() { + drive_response(&r); + } + for r in dns.get_authorities() { + drive_response(&r); + } + for r in dns.get_additional() { + drive_response(&r); + } + + for b in dns.payload().iter() { + black_box(*b); + } + black_box(dns.packet_size()); + black_box(dns.from_packet()); + } +}); diff --git a/fuzz/fuzzers/ether.rs b/fuzz/fuzzers/ether.rs index d0337212..e04b2f28 100644 --- a/fuzz/fuzzers/ether.rs +++ b/fuzz/fuzzers/ether.rs @@ -1,20 +1,18 @@ #![no_main] -extern crate libfuzzer_sys; -extern crate pnet; +use std::hint::black_box; + +use libfuzzer_sys::fuzz_target; use pnet::packet::Packet; use pnet::packet::ethernet::EthernetPacket; -#[export_name="rust_fuzzer_test_input"] -pub extern fn go(data: &[u8]) { +fuzz_target!(|data: &[u8]| { if let Some(eth) = EthernetPacket::new(data) { - let _s = eth.get_source(); - let _d = eth.get_destination(); - let _t = eth.get_ethertype(); - let pl = eth.payload(); - for b in pl.iter() { - *b; + black_box(eth.get_source()); + black_box(eth.get_destination()); + black_box(eth.get_ethertype()); + for b in eth.payload().iter() { + black_box(*b); } } - -} +}); diff --git a/fuzz/fuzzers/gre.rs b/fuzz/fuzzers/gre.rs deleted file mode 100644 index 51e1d762..00000000 --- a/fuzz/fuzzers/gre.rs +++ /dev/null @@ -1,32 +0,0 @@ -#![no_main] -extern crate libfuzzer_sys; -extern crate pnet; - -use pnet::packet::gre::GrePacket; - -#[export_name="rust_fuzzer_test_input"] -pub extern fn go(data: &[u8]) { - if let Some(gre) = GrePacket::new(data) { - for b in gre.get_checksum_raw().iter() { - *b; - } - - for b in gre.get_offset_raw().iter() { - *b; - } - - for b in gre.get_key_raw().iter() { - *b; - } - - - for b in gre.get_sequence_raw().iter() { - *b; - } - - - for b in gre.get_routing_raw().iter() { - *b; - } - } -} diff --git a/fuzz/fuzzers/icmp.rs b/fuzz/fuzzers/icmp.rs new file mode 100644 index 00000000..a0101646 --- /dev/null +++ b/fuzz/fuzzers/icmp.rs @@ -0,0 +1,77 @@ +#![no_main] + +use std::hint::black_box; + +use libfuzzer_sys::fuzz_target; +use pnet::packet::icmp::destination_unreachable::DestinationUnreachablePacket; +use pnet::packet::icmp::echo_reply::EchoReplyPacket; +use pnet::packet::icmp::echo_request::EchoRequestPacket; +use pnet::packet::icmp::time_exceeded::TimeExceededPacket; +use pnet::packet::icmp::{self, IcmpPacket}; +use pnet::packet::{FromPacket, Packet, PacketSize}; + +fuzz_target!(|data: &[u8]| { + if let Some(icmp) = IcmpPacket::new(data) { + black_box(icmp.get_icmp_type()); + black_box(icmp.get_icmp_code()); + black_box(icmp.get_checksum()); + for b in icmp.payload().iter() { + black_box(*b); + } + black_box(icmp.packet_size()); + black_box(icmp.from_packet()); + black_box(icmp::checksum(&icmp)); + } + + // Drive every type-specific ICMP parser from the same buffer. + if let Some(p) = EchoReplyPacket::new(data) { + black_box(p.get_icmp_type()); + black_box(p.get_icmp_code()); + black_box(p.get_checksum()); + black_box(p.get_identifier()); + black_box(p.get_sequence_number()); + for b in p.payload().iter() { + black_box(*b); + } + black_box(p.packet_size()); + black_box(p.from_packet()); + } + + if let Some(p) = EchoRequestPacket::new(data) { + black_box(p.get_icmp_type()); + black_box(p.get_icmp_code()); + black_box(p.get_checksum()); + black_box(p.get_identifier()); + black_box(p.get_sequence_number()); + for b in p.payload().iter() { + black_box(*b); + } + black_box(p.packet_size()); + black_box(p.from_packet()); + } + + if let Some(p) = DestinationUnreachablePacket::new(data) { + black_box(p.get_icmp_type()); + black_box(p.get_icmp_code()); + black_box(p.get_checksum()); + black_box(p.get_unused()); + black_box(p.get_next_hop_mtu()); + for b in p.payload().iter() { + black_box(*b); + } + black_box(p.packet_size()); + black_box(p.from_packet()); + } + + if let Some(p) = TimeExceededPacket::new(data) { + black_box(p.get_icmp_type()); + black_box(p.get_icmp_code()); + black_box(p.get_checksum()); + black_box(p.get_unused()); + for b in p.payload().iter() { + black_box(*b); + } + black_box(p.packet_size()); + black_box(p.from_packet()); + } +}); diff --git a/fuzz/fuzzers/icmpv6.rs b/fuzz/fuzzers/icmpv6.rs new file mode 100644 index 00000000..8186aa12 --- /dev/null +++ b/fuzz/fuzzers/icmpv6.rs @@ -0,0 +1,156 @@ +#![no_main] + +use std::hint::black_box; + +use libfuzzer_sys::fuzz_target; +use pnet::packet::icmpv6::ndp::{ + NdpOptionPacket, NeighborAdvertPacket, NeighborSolicitPacket, RedirectPacket, + RouterAdvertPacket, RouterSolicitPacket, +}; +use pnet::packet::icmpv6::echo_reply::EchoReplyPacket; +use pnet::packet::icmpv6::echo_request::EchoRequestPacket; +use pnet::packet::icmpv6::Icmpv6Packet; +use pnet::packet::{FromPacket, Packet, PacketSize}; + +/// Drive every read path of a single NDP option. +fn drive_option(opt: &NdpOptionPacket) { + black_box(opt.get_option_type()); + black_box(opt.get_length()); + black_box(opt.packet_size()); + for b in opt.payload().iter() { + black_box(*b); + } +} + +fuzz_target!(|data: &[u8]| { + if let Some(icmpv6) = Icmpv6Packet::new(data) { + black_box(icmpv6.get_icmpv6_type()); + black_box(icmpv6.get_icmpv6_code()); + black_box(icmpv6.get_checksum()); + for b in icmpv6.payload().iter() { + black_box(*b); + } + black_box(icmpv6.packet_size()); + black_box(icmpv6.from_packet()); + } + + if let Some(p) = NdpOptionPacket::new(data) { + drive_option(&p); + black_box(p.from_packet()); + } + + if let Some(p) = RouterSolicitPacket::new(data) { + black_box(p.get_icmpv6_type()); + black_box(p.get_icmpv6_code()); + black_box(p.get_checksum()); + black_box(p.get_reserved()); + for opt in p.get_options_iter() { + drive_option(&opt); + } + black_box(p.get_options()); + for b in p.payload().iter() { + black_box(*b); + } + black_box(p.packet_size()); + black_box(p.from_packet()); + } + + if let Some(p) = RouterAdvertPacket::new(data) { + black_box(p.get_icmpv6_type()); + black_box(p.get_icmpv6_code()); + black_box(p.get_checksum()); + black_box(p.get_hop_limit()); + black_box(p.get_flags()); + black_box(p.get_lifetime()); + black_box(p.get_reachable_time()); + black_box(p.get_retrans_time()); + for opt in p.get_options_iter() { + drive_option(&opt); + } + black_box(p.get_options()); + for b in p.payload().iter() { + black_box(*b); + } + black_box(p.packet_size()); + black_box(p.from_packet()); + } + + if let Some(p) = NeighborSolicitPacket::new(data) { + black_box(p.get_icmpv6_type()); + black_box(p.get_icmpv6_code()); + black_box(p.get_checksum()); + black_box(p.get_reserved()); + black_box(p.get_target_addr()); + for opt in p.get_options_iter() { + drive_option(&opt); + } + black_box(p.get_options()); + for b in p.payload().iter() { + black_box(*b); + } + black_box(p.packet_size()); + black_box(p.from_packet()); + } + + if let Some(p) = NeighborAdvertPacket::new(data) { + black_box(p.get_icmpv6_type()); + black_box(p.get_icmpv6_code()); + black_box(p.get_checksum()); + black_box(p.get_flags()); + black_box(p.get_reserved()); + black_box(p.get_target_addr()); + for opt in p.get_options_iter() { + drive_option(&opt); + } + black_box(p.get_options()); + for b in p.payload().iter() { + black_box(*b); + } + black_box(p.packet_size()); + black_box(p.from_packet()); + } + + if let Some(p) = RedirectPacket::new(data) { + black_box(p.get_icmpv6_type()); + black_box(p.get_icmpv6_code()); + black_box(p.get_checksum()); + black_box(p.get_reserved()); + black_box(p.get_target_addr()); + black_box(p.get_dest_addr()); + for opt in p.get_options_iter() { + drive_option(&opt); + } + black_box(p.get_options()); + for b in p.payload().iter() { + black_box(*b); + } + black_box(p.packet_size()); + black_box(p.from_packet()); + } + + if let Some(p) = EchoReplyPacket::new(data) { + black_box(p.get_icmpv6_type()); + black_box(p.get_icmpv6_code()); + black_box(p.get_checksum()); + black_box(p.get_identifier()); + black_box(p.get_sequence_number()); + for b in p.payload().iter() { + black_box(*b); + } + black_box(p.packet_size()); + black_box(p.from_packet()); + } + + if let Some(p) = EchoRequestPacket::new(data) { + black_box(p.get_icmpv6_type()); + black_box(p.get_icmpv6_code()); + black_box(p.get_checksum()); + black_box(p.get_identifier()); + black_box(p.get_sequence_number()); + for b in p.payload().iter() { + black_box(*b); + } + black_box(p.packet_size()); + black_box(p.from_packet()); + } +}); diff --git a/fuzz/fuzzers/ipv4.rs b/fuzz/fuzzers/ipv4.rs index ace65917..e723a66b 100644 --- a/fuzz/fuzzers/ipv4.rs +++ b/fuzz/fuzzers/ipv4.rs @@ -1,19 +1,48 @@ #![no_main] -extern crate libfuzzer_sys; -extern crate pnet; -use pnet::packet::Packet; -use pnet::packet::ipv4::Ipv4Packet; +use std::hint::black_box; -#[export_name="rust_fuzzer_test_input"] -pub extern fn go(data: &[u8]) { +use libfuzzer_sys::fuzz_target; +use pnet::packet::ipv4::{self, Ipv4Packet}; +use pnet::packet::{FromPacket, Packet, PacketSize}; + +fuzz_target!(|data: &[u8]| { if let Some(ipv4) = Ipv4Packet::new(data) { - let options = ipv4.get_options_raw(); - for o in options.iter() { - *o; + // Every fixed-header getter. + black_box(ipv4.get_version()); + black_box(ipv4.get_header_length()); + black_box(ipv4.get_dscp()); + black_box(ipv4.get_ecn()); + black_box(ipv4.get_total_length()); + black_box(ipv4.get_identification()); + black_box(ipv4.get_flags()); + black_box(ipv4.get_fragment_offset()); + black_box(ipv4.get_ttl()); + black_box(ipv4.get_next_level_protocol()); + black_box(ipv4.get_checksum()); + black_box(ipv4.get_source()); + black_box(ipv4.get_destination()); + + // Variable-length options: raw bytes, parsed Vec, and iterator. + for b in ipv4.get_options_raw().iter() { + black_box(*b); + } + for opt in ipv4.get_options() { + black_box(&opt); } + for opt in ipv4.get_options_iter() { + black_box(opt.packet_size()); + for b in opt.payload().iter() { + black_box(*b); + } + } + + // Payload, computed size, owned conversion and checksum. for b in ipv4.payload().iter() { - *b; + black_box(*b); } + black_box(ipv4.packet_size()); + black_box(ipv4.from_packet()); + black_box(ipv4::checksum(&ipv4)); } -} +}); diff --git a/fuzz/fuzzers/ipv6.rs b/fuzz/fuzzers/ipv6.rs new file mode 100644 index 00000000..6c15692f --- /dev/null +++ b/fuzz/fuzzers/ipv6.rs @@ -0,0 +1,61 @@ +#![no_main] + +use std::hint::black_box; + +use libfuzzer_sys::fuzz_target; +use pnet::packet::ipv6::{ExtensionPacket, FragmentPacket, Ipv6Packet, RoutingPacket}; +use pnet::packet::{FromPacket, Packet, PacketSize}; + +fuzz_target!(|data: &[u8]| { + if let Some(ipv6) = Ipv6Packet::new(data) { + black_box(ipv6.get_version()); + black_box(ipv6.get_traffic_class()); + black_box(ipv6.get_flow_label()); + black_box(ipv6.get_payload_length()); + black_box(ipv6.get_next_header()); + black_box(ipv6.get_hop_limit()); + black_box(ipv6.get_source()); + black_box(ipv6.get_destination()); + + for b in ipv6.payload().iter() { + black_box(*b); + } + black_box(ipv6.packet_size()); + black_box(ipv6.from_packet()); + } + + // Drive the IPv6 extension-header parsers from the same buffer. + if let Some(ext) = ExtensionPacket::new(data) { + black_box(ext.get_next_header()); + black_box(ext.get_hdr_ext_len()); + for b in ext.payload().iter() { + black_box(*b); + } + black_box(ext.packet_size()); + black_box(ext.from_packet()); + } + + if let Some(routing) = RoutingPacket::new(data) { + black_box(routing.get_next_header()); + black_box(routing.get_hdr_ext_len()); + black_box(routing.get_routing_type()); + black_box(routing.get_segments_left()); + for b in routing.payload().iter() { + black_box(*b); + } + black_box(routing.packet_size()); + black_box(routing.from_packet()); + } + + if let Some(fragment) = FragmentPacket::new(data) { + black_box(fragment.get_next_header()); + black_box(fragment.get_reserved()); + black_box(fragment.get_fragment_offset_with_flags()); + black_box(fragment.get_id()); + for b in fragment.payload().iter() { + black_box(*b); + } + black_box(fragment.packet_size()); + black_box(fragment.from_packet()); + } +}); diff --git a/fuzz/fuzzers/tcp.rs b/fuzz/fuzzers/tcp.rs index 791cc93e..20eb4cbd 100644 --- a/fuzz/fuzzers/tcp.rs +++ b/fuzz/fuzzers/tcp.rs @@ -1,19 +1,45 @@ #![no_main] -extern crate libfuzzer_sys; -extern crate pnet; -use pnet::packet::Packet; +use std::hint::black_box; + +use libfuzzer_sys::fuzz_target; use pnet::packet::tcp::TcpPacket; +use pnet::packet::{FromPacket, Packet, PacketSize}; -#[export_name="rust_fuzzer_test_input"] -pub extern fn go(data: &[u8]) { +fuzz_target!(|data: &[u8]| { if let Some(tcp) = TcpPacket::new(data) { - let options = tcp.get_options_iter(); - for o in options { - o.payload(); + // Every fixed-header getter. + black_box(tcp.get_source()); + black_box(tcp.get_destination()); + black_box(tcp.get_sequence()); + black_box(tcp.get_acknowledgement()); + black_box(tcp.get_data_offset()); + black_box(tcp.get_reserved()); + black_box(tcp.get_flags()); + black_box(tcp.get_window()); + black_box(tcp.get_checksum()); + black_box(tcp.get_urgent_ptr()); + + // Variable-length options: raw bytes, parsed Vec, and iterator. + for b in tcp.get_options_raw().iter() { + black_box(*b); + } + for opt in tcp.get_options() { + black_box(&opt); } + for opt in tcp.get_options_iter() { + black_box(opt.get_number()); + black_box(opt.packet_size()); + for b in opt.payload().iter() { + black_box(*b); + } + } + + // Payload, computed size and owned conversion. for b in tcp.payload().iter() { - *b; + black_box(*b); } + black_box(tcp.packet_size()); + black_box(tcp.from_packet()); } -} +}); diff --git a/fuzz/fuzzers/udp.rs b/fuzz/fuzzers/udp.rs index d080b01b..13443292 100644 --- a/fuzz/fuzzers/udp.rs +++ b/fuzz/fuzzers/udp.rs @@ -1,15 +1,22 @@ #![no_main] -extern crate libfuzzer_sys; -extern crate pnet; -use pnet::packet::Packet; +use std::hint::black_box; + +use libfuzzer_sys::fuzz_target; use pnet::packet::udp::UdpPacket; +use pnet::packet::{FromPacket, Packet, PacketSize}; -#[export_name="rust_fuzzer_test_input"] -pub extern fn go(data: &[u8]) { +fuzz_target!(|data: &[u8]| { if let Some(udp) = UdpPacket::new(data) { + black_box(udp.get_source()); + black_box(udp.get_destination()); + black_box(udp.get_length()); + black_box(udp.get_checksum()); + for b in udp.payload().iter() { - *b; + black_box(*b); } + black_box(udp.packet_size()); + black_box(udp.from_packet()); } -} +}); diff --git a/fuzz/fuzzers/vlan.rs b/fuzz/fuzzers/vlan.rs index 349b8eed..3216f40f 100644 --- a/fuzz/fuzzers/vlan.rs +++ b/fuzz/fuzzers/vlan.rs @@ -1,15 +1,15 @@ #![no_main] -extern crate libfuzzer_sys; -extern crate pnet; +use std::hint::black_box; + +use libfuzzer_sys::fuzz_target; use pnet::packet::Packet; use pnet::packet::vlan::VlanPacket; -#[export_name="rust_fuzzer_test_input"] -pub extern fn go(data: &[u8]) { +fuzz_target!(|data: &[u8]| { if let Some(vlan) = VlanPacket::new(data) { for b in vlan.payload().iter() { - *b; + black_box(*b); } } -} +}); diff --git a/pnet_macros/src/decorator.rs b/pnet_macros/src/decorator.rs index bdf64d0f..d5758c6b 100644 --- a/pnet_macros/src/decorator.rs +++ b/pnet_macros/src/decorator.rs @@ -554,7 +554,7 @@ fn generate_packet_impl( format!( "/// Populates a {name}Packet using a {name} structure #[inline] - #[cfg_attr(feature = \"clippy\", allow(used_underscore_binding))] + #[allow(clippy::used_underscore_binding)] pub fn populate(&mut self, packet: &{name}) {{ let _self = self; {set_fields} @@ -692,7 +692,7 @@ fn generate_packet_size_impls( let s = format!( " impl<'a> ::pnet_macros_support::packet::PacketSize for {name}<'a> {{ - #[cfg_attr(feature = \"clippy\", allow(used_underscore_binding))] + #[allow(clippy::used_underscore_binding)] fn packet_size(&self) -> usize {{ let _self = self; {size} @@ -743,7 +743,7 @@ fn generate_packet_trait_impls( fn packet{u_mut}<'p>(&'p {mut_} self) -> &'p {mut_} [u8] {{ &{mut_} self.packet[..] }} #[inline] - #[cfg_attr(feature = \"clippy\", allow(used_underscore_binding))] + #[allow(clippy::used_underscore_binding)] fn payload{u_mut}<'p>(&'p {mut_} self) -> &'p {mut_} [u8] {{ let _self = self; {pre} @@ -865,7 +865,7 @@ fn generate_debug_impls(packet: &Packet) -> Result ::core::fmt::Debug for {packet}<'p> {{ - #[cfg_attr(feature = \"clippy\", allow(used_underscore_binding))] + #[allow(clippy::used_underscore_binding)] fn fmt(&self, fmt: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {{ let _self = self; write!(fmt, @@ -955,7 +955,7 @@ fn handle_misc_field( /// Set the value of the {name} field. #[inline] #[allow(trivial_numeric_casts)] - #[cfg_attr(feature = \"clippy\", allow(used_underscore_binding))] + #[allow(clippy::used_underscore_binding)] pub fn set_{name}(&mut self, val: {ty_str}) {{ use pnet_macros_support::packet::PrimitiveValues; let _self = self; @@ -991,7 +991,7 @@ fn handle_misc_field( /// Get the value of the {name} field #[inline] #[allow(trivial_numeric_casts)] - #[cfg_attr(feature = \"clippy\", allow(used_underscore_binding))] + #[allow(clippy::used_underscore_binding)] pub fn get_{name}(&self) -> {ty_str} {{ {ctor} }} @@ -1021,7 +1021,7 @@ fn handle_vec_primitive( /// Get the value of the {name} field (copies contents) #[inline] #[allow(trivial_numeric_casts, unused_parens, unused_braces)] - #[cfg_attr(feature = \"clippy\", allow(used_underscore_binding))] + #[allow(clippy::used_underscore_binding)] pub fn get_{name}(&self) -> Vec<{inner_ty_str}> {{ use core::cmp::min; let _self = self; @@ -1088,7 +1088,7 @@ fn handle_vec_primitive( /// Set the value of the {name} field (copies contents) #[inline] #[allow(trivial_numeric_casts)] - #[cfg_attr(feature = \"clippy\", allow(used_underscore_binding))] + #[allow(clippy::used_underscore_binding)] pub fn set_{name}(&mut self, vals: &[{inner_ty_str}]) {{ let mut _self = self; let current_offset = {co}; @@ -1135,7 +1135,7 @@ fn handle_vector_field( /// Get the raw &[u8] value of the {name} field, without copying #[inline] #[allow(trivial_numeric_casts)] - #[cfg_attr(feature = \"clippy\", allow(used_underscore_binding))] + #[allow(clippy::used_underscore_binding)] pub fn get_{name}_raw(&self) -> &[u8] {{ use core::cmp::min; let _self = self; @@ -1153,7 +1153,7 @@ fn handle_vector_field( /// Get the raw &mut [u8] value of the {name} field, without copying #[inline] #[allow(trivial_numeric_casts)] - #[cfg_attr(feature = \"clippy\", allow(used_underscore_binding))] + #[allow(clippy::used_underscore_binding)] pub fn get_{name}_raw_mut(&mut self) -> &mut [u8] {{ use core::cmp::min; let _self = self; @@ -1247,7 +1247,7 @@ fn handle_vector_field( /// Set the value of the {name} field. #[inline] #[allow(trivial_numeric_casts)] - #[cfg_attr(feature = \"clippy\", allow(used_underscore_binding))] + #[allow(clippy::used_underscore_binding)] pub fn set_{name}(&mut self, vals: &Vec<{inner_ty_str}>) {{ use pnet_macros_support::packet::PrimitiveValues; let _self = self; @@ -1276,7 +1276,7 @@ fn handle_vector_field( /// Get the value of the {name} field #[inline] #[allow(trivial_numeric_casts)] - #[cfg_attr(feature = \"clippy\", allow(used_underscore_binding))] + #[allow(clippy::used_underscore_binding)] pub fn get_{name}(&self) -> Vec<{inner_ty_str}> {{ let _self = self; let length = {packet_length}; @@ -1309,7 +1309,7 @@ fn handle_vector_field( /// Get the value of the {name} field (copies contents) #[inline] #[allow(trivial_numeric_casts)] - #[cfg_attr(feature = \"clippy\", allow(used_underscore_binding))] + #[allow(clippy::used_underscore_binding)] pub fn get_{name}(&self) -> Vec<{inner_ty_str}> {{ use pnet_macros_support::packet::FromPacket; use core::cmp::min; @@ -1326,8 +1326,8 @@ fn handle_vector_field( /// Get the value of the {name} field as iterator #[inline] #[allow(trivial_numeric_casts)] - #[cfg_attr(feature = \"clippy\", allow(used_underscore_binding))] - pub fn get_{name}_iter(&self) -> {inner_ty_str}Iterable {{ + #[allow(clippy::used_underscore_binding)] + pub fn get_{name}_iter(&self) -> {inner_ty_str}Iterable<'_> {{ use core::cmp::min; let _self = self; let current_offset = {co}; @@ -1347,7 +1347,7 @@ fn handle_vector_field( /// Set the value of the {name} field (copies contents) #[inline] #[allow(trivial_numeric_casts)] - #[cfg_attr(feature = \"clippy\", allow(used_underscore_binding))] + #[allow(clippy::used_underscore_binding)] pub fn set_{name}(&mut self, vals: &[{inner_ty_str}]) {{ use pnet_macros_support::packet::PacketSize; let _self = self; @@ -1511,7 +1511,7 @@ fn generate_mutator_str( format!( "#[inline] #[allow(trivial_numeric_casts)] - #[cfg_attr(feature = \"clippy\", allow(used_underscore_binding))] + #[allow(clippy::used_underscore_binding)] fn set_{name}(_self: &mut {struct_name}, val: {ty}) {{ let co = {co}; {operations} @@ -1528,7 +1528,7 @@ fn generate_mutator_str( "{comment} #[inline] #[allow(trivial_numeric_casts)] - #[cfg_attr(feature = \"clippy\", allow(used_underscore_binding))] + #[allow(clippy::used_underscore_binding)] pub fn set_{name}(&mut self, val: {ty}) {{ let _self = self; let co = {co}; @@ -1557,7 +1557,7 @@ fn generate_mutator_with_offset_str( format!( "#[inline] #[allow(trivial_numeric_casts)] - #[cfg_attr(feature = \"clippy\", allow(used_underscore_binding))] + #[allow(clippy::used_underscore_binding)] fn set_{name}(_self: &mut {struct_name}, val: {ty}, offset: usize) {{ let co = {co} + offset; {operations} @@ -1654,7 +1654,7 @@ fn generate_accessor_str( format!( "#[inline(always)] #[allow(trivial_numeric_casts, unused_parens)] - #[cfg_attr(feature = \"clippy\", allow(used_underscore_binding))] + #[allow(clippy::used_underscore_binding)] fn get_{name}(_self: &{struct_name}) -> {ty} {{ let co = {co}; {operations} @@ -1671,7 +1671,7 @@ fn generate_accessor_str( "{comment} #[inline] #[allow(trivial_numeric_casts, unused_parens)] - #[cfg_attr(feature = \"clippy\", allow(used_underscore_binding))] + #[allow(clippy::used_underscore_binding)] pub fn get_{name}(&self) -> {ty} {{ let _self = self; let co = {co}; @@ -1701,7 +1701,7 @@ fn generate_accessor_with_offset_str( format!( "#[inline(always)] #[allow(trivial_numeric_casts, unused_parens)] - #[cfg_attr(feature = \"clippy\", allow(used_underscore_binding))] + #[allow(clippy::used_underscore_binding)] fn get_{name}(_self: &{struct_name}, offset: usize) -> {ty} {{ let co = {co} + offset; {operations} diff --git a/pnet_macros/tests/compile-fail/length_expr.stderr b/pnet_macros/tests/compile-fail/length_expr.stderr index a3815ebc..c1a8198a 100644 --- a/pnet_macros/tests/compile-fail/length_expr.stderr +++ b/pnet_macros/tests/compile-fail/length_expr.stderr @@ -1,5 +1,5 @@ error: Only field names, constants, integers, basic arithmetic expressions (+ - * / %) and parentheses are allowed in the "length" attribute - --> $DIR/length_expr.rs:16:16 + --> tests/compile-fail/length_expr.rs:16:16 | -16 | #[length = "banana + 7.5"] //~ ERROR Only field names, constants, integers, basic arithmetic expressions (+ - * / %) and parentheses ... +16 | #[length = "banana + 7.5"] //~ ERROR Only field names, constants, integers, basic arithmetic expressions (+ - * / %) and parenth... | ^^^^^^^^^^^^^^ diff --git a/pnet_macros/tests/compile-fail/length_expr_parentheses.stderr b/pnet_macros/tests/compile-fail/length_expr_parentheses.stderr index ea0c96ac..7e19908d 100644 --- a/pnet_macros/tests/compile-fail/length_expr_parentheses.stderr +++ b/pnet_macros/tests/compile-fail/length_expr_parentheses.stderr @@ -1,11 +1,3 @@ -error: this file contains an unclosed delimiter - --> tests/compile-fail/length_expr_parentheses.rs:15:1 - | -15 | #[packet] - | ^^^^^^^^^ unclosed delimiter - | - = note: this error originates in the derive macro `::pnet_macros::Packet` (in Nightly builds, run with -Z macro-backtrace for more info) - error: cannot parse string into token stream --> tests/compile-fail/length_expr_parentheses.rs:15:1 | diff --git a/pnet_macros/tests/compile-fail/payload_fn2.stderr b/pnet_macros/tests/compile-fail/payload_fn2.stderr index 8d3ce476..066764f6 100644 --- a/pnet_macros/tests/compile-fail/payload_fn2.stderr +++ b/pnet_macros/tests/compile-fail/payload_fn2.stderr @@ -1,14 +1,5 @@ error: unknown attribute: payload - --> $DIR/payload_fn2.rs:16:7 + --> tests/compile-fail/payload_fn2.rs:16:7 | 16 | #[payload(length_fn = "length_of_payload")] //~ ERROR: unknown attribute: payload | ^^^^^^^ - -error[E0412]: cannot find type `PacketWithPayload2Packet` in this scope - --> $DIR/payload_fn2.rs:20:26 - | -14 | pub struct PacketWithPayload2 { - | ----------------------------- similarly named struct `PacketWithPayload2` defined here -... -20 | fn length_of_payload(_: &PacketWithPayload2Packet) -> usize { //~ ERROR cannot find type `PacketWithPayload2Packet` in this scope - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: a struct with a similar name exists: `PacketWithPayload2` diff --git a/pnet_packet/Cargo.toml b/pnet_packet/Cargo.toml index 9f58a74a..ec48a9ee 100644 --- a/pnet_packet/Cargo.toml +++ b/pnet_packet/Cargo.toml @@ -19,6 +19,7 @@ pnet_macros = { path = "../pnet_macros", version = "0.35.0" } [features] std = ["pnet_base/std"] default = ["std"] +benchmark = [] [dev-dependencies] hex = "0.4.3" diff --git a/pnet_packet/src/dns.rs b/pnet_packet/src/dns.rs index 27c5463f..7d73766a 100644 --- a/pnet_packet/src/dns.rs +++ b/pnet_packet/src/dns.rs @@ -1,6 +1,6 @@ use alloc::string::String; use alloc::vec::Vec; -use core::{fmt, str}; +use core::fmt; use pnet_macros::packet; use pnet_macros_support::packet::{Packet, PacketSize, PrimitiveValues}; use pnet_macros_support::types::{u1, u16be, u32be, u4}; @@ -294,12 +294,27 @@ pub struct Dns { pub payload: Vec, } +// Size of one parsed sub-record within `slice`, or None if it does not fully fit. +// `packet_size()` of a DnsResponse is `12 + data_len`, where `data_len` is read +// from the (untrusted) packet, so a record can claim to be larger than the bytes +// actually present. Rejecting records that overrun the buffer keeps the section +// lengths — and therefore every downstream slice — within bounds. +fn fitting_query_size(slice: &[u8]) -> Option { + let size = DnsQueryPacket::new(slice)?.packet_size(); + (size > 0 && size <= slice.len()).then_some(size) +} + +fn fitting_response_size(slice: &[u8]) -> Option { + let size = DnsResponsePacket::new(slice)?.packet_size(); + (size > 0 && size <= slice.len()).then_some(size) +} + fn queries_length(packet: &DnsPacket) -> usize { let base = 12; let mut length = 0; for _ in 0..packet.get_query_count() { - match DnsQueryPacket::new(&packet.packet()[base + length..]) { - Some(query) => length += query.packet_size(), + match packet.packet().get(base + length..).and_then(fitting_query_size) { + Some(size) => length += size, None => break, } } @@ -309,9 +324,9 @@ fn queries_length(packet: &DnsPacket) -> usize { fn responses_length(packet: &DnsPacket) -> usize { let base = 12 + queries_length(packet); let mut length = 0; - for _ in 0..packet.get_query_count() { - match DnsResponsePacket::new(&packet.packet()[base + length..]) { - Some(query) => length += query.packet_size(), + for _ in 0..packet.get_response_count() { + match packet.packet().get(base + length..).and_then(fitting_response_size) { + Some(size) => length += size, None => break, } } @@ -321,9 +336,9 @@ fn responses_length(packet: &DnsPacket) -> usize { fn authority_length(packet: &DnsPacket) -> usize { let base = 12 + queries_length(packet) + responses_length(packet); let mut length = 0; - for _ in 0..packet.get_query_count() { - match DnsResponsePacket::new(&packet.packet()[base + length..]) { - Some(query) => length += query.packet_size(), + for _ in 0..packet.get_authority_rr_count() { + match packet.packet().get(base + length..).and_then(fitting_response_size) { + Some(size) => length += size, None => break, } } @@ -333,82 +348,104 @@ fn authority_length(packet: &DnsPacket) -> usize { fn additional_length(packet: &DnsPacket) -> usize { let base = 12 + queries_length(packet) + responses_length(packet) + authority_length(packet); let mut length = 0; - for _ in 0..packet.get_query_count() { - match DnsResponsePacket::new(&packet.packet()[base + length..]) { - Some(query) => length += query.packet_size(), + for _ in 0..packet.get_additional_rr_count() { + match packet.packet().get(base + length..).and_then(fitting_response_size) { + Some(size) => length += size, None => break, } } length } -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -pub enum Opcode { - StandardQuery, - InverseQuery, - ServerStatusRequest, - Reserved, +#[allow(non_snake_case)] +#[allow(non_upper_case_globals)] +pub mod Opcodes { + use super::Opcode; + + pub const StandardQuery: Opcode = Opcode(0); + pub const InverseQuery: Opcode = Opcode(1); + pub const ServerStatusRequest: Opcode = Opcode(2); + pub const Reserved: Opcode = Opcode(3); +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct Opcode(pub u8); + +impl Opcode { + pub fn new(value: u8) -> Self { + Self(value) + } } impl PrimitiveValues for Opcode { type T = (u8,); + fn to_primitive_values(&self) -> (u8,) { - match self { - Self::StandardQuery => (0,), - Self::InverseQuery => (1,), - Self::ServerStatusRequest => (2,), - Self::Reserved => (3,), - } + (self.0,) } } -impl Opcode { - pub fn new(value: u8) -> Self { - match value { - 0 => Self::StandardQuery, - 1 => Self::InverseQuery, - 2 => Self::ServerStatusRequest, - 3 => Self::Reserved, - _ => unreachable!(), - } +impl fmt::Display for Opcode { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{}", + match self { + &Opcodes::StandardQuery => "StandardQuery", // 0 + &Opcodes::InverseQuery => "InverseQuery", // 1 + &Opcodes::ServerStatusRequest => "ServerStatusRequest", // 2 + &Opcodes::Reserved => "Reserved", // 3 + _ => "unknown", + } + ) } } -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -pub enum Retcode { - NoError, - FormatError, - ServerFailure, - RecordNotExists, - RequestTypeUnsupported, - ServerPolicyError, +#[allow(non_snake_case)] +#[allow(non_upper_case_globals)] +pub mod Retcodes { + use super::Retcode; + + pub const NoError: Retcode = Retcode(0); + pub const FormatError: Retcode = Retcode(1); + pub const ServerFailure: Retcode = Retcode(2); + pub const RecordNotExists: Retcode = Retcode(3); + pub const RequestTypeUnsupported: Retcode = Retcode(4); + pub const ServerPolicyError: Retcode = Retcode(5); +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct Retcode(pub u8); + +impl Retcode { + pub fn new(value: u8) -> Self { + Self(value) + } } impl PrimitiveValues for Retcode { type T = (u8,); + fn to_primitive_values(&self) -> (u8,) { - match self { - Self::NoError => (0,), - Self::FormatError => (1,), - Self::ServerFailure => (2,), - Self::RecordNotExists => (3,), - Self::RequestTypeUnsupported => (4,), - Self::ServerPolicyError => (5,), - } + (self.0,) } } -impl Retcode { - pub fn new(value: u8) -> Self { - match value { - 0 => Self::NoError, - 1 => Self::FormatError, - 2 => Self::ServerFailure, - 3 => Self::RecordNotExists, - 4 => Self::RequestTypeUnsupported, - 5 => Self::ServerPolicyError, - _ => unreachable!(), - } +impl fmt::Display for Retcode { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{}", + match self { + &Retcodes::NoError => "NoError", // 0 + &Retcodes::FormatError => "FormatError", // 1 + &Retcodes::ServerFailure => "ServerFailure", // 2 + &Retcodes::RecordNotExists => "RecordNotExists", // 3 + &Retcodes::RequestTypeUnsupported => "RequestTypeUnsupported", // 4 + &Retcodes::ServerPolicyError => "ServerPolicyError", // 5 + _ => "unknown", + } + ) } } @@ -425,7 +462,16 @@ pub struct DnsQuery { } fn qname_length(packet: &DnsQueryPacket) -> usize { - packet.packet().iter().take_while(|w| *w != &0).count() + 1 + // The qname is a zero-terminated sequence of bytes, followed by the fixed + // qtype (2 bytes) and qclass (2 bytes). Never report a length that would push + // those trailing fixed fields past the end of the buffer, otherwise the + // generated qtype/qclass accessors would index out of bounds. + let data = packet.packet(); + let max = data.len().saturating_sub(4); + match data.iter().take(max).position(|&b| b == 0) { + Some(zero_idx) => zero_idx + 1, + None => max, + } } impl DnsQuery { @@ -433,19 +479,21 @@ impl DnsQuery { let name = &self.qname; let mut qname = String::new(); let mut offset = 0; - loop { - let label_len = name[offset] as usize; + // Walk the length-prefixed labels with checked access so malformed names + // (truncated labels, missing terminator, non-UTF-8 bytes) cannot panic. + while let Some(&label_len) = name.get(offset) { + let label_len = label_len as usize; if label_len == 0 { break; } + let label = match name.get(offset + 1..offset + 1 + label_len) { + Some(label) => label, + None => break, + }; if !qname.is_empty() { qname.push('.'); } - qname.push_str( - str::from_utf8(&name[offset + 1..offset + 1 + label_len]) - .ok() - .unwrap(), - ); + qname.push_str(&String::from_utf8_lossy(label)); offset += label_len + 1; } qname @@ -472,13 +520,13 @@ fn test_dns_query_packet() { let packet = DnsPacket::new(b"\x9b\xa0\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00\x05_ldap\x04_tcp\x02dc\x06_msdcs\x05S4DOM\x07PRIVATE\x00\x00!\x00\x01").unwrap(); assert_eq!(packet.get_id(), 39840); assert_eq!(packet.get_is_response(), 0); - assert_eq!(packet.get_opcode(), Opcode::StandardQuery); + assert_eq!(packet.get_opcode(), Opcodes::StandardQuery); assert_eq!(packet.get_is_authoriative(), 0); assert_eq!(packet.get_is_truncated(), 0); assert_eq!(packet.get_is_recursion_desirable(), 1); assert_eq!(packet.get_is_recursion_available(), 0); assert_eq!(packet.get_zero_reserved(), 0); - assert_eq!(packet.get_rcode(), Retcode::NoError); + assert_eq!(packet.get_rcode(), Retcodes::NoError); assert_eq!(packet.get_query_count(), 1); assert_eq!(packet.get_response_count(), 0); assert_eq!(packet.get_authority_rr_count(), 0); @@ -500,13 +548,13 @@ fn test_dns_response_packet() { let packet = DnsPacket::new(b"\xbc\x12\x85\x80\x00\x01\x00\x01\x00\x00\x00\x00\x05s4dc1\x05samba\x08windows8\x07private\x00\x00\x01\x00\x01\xc0\x0c\x00\x01\x00\x01\x00\x00\x03\x84\x00\x04\xc0\xa8z\xbd").unwrap(); assert_eq!(packet.get_id(), 48146); assert_eq!(packet.get_is_response(), 1); - assert_eq!(packet.get_opcode(), Opcode::StandardQuery); + assert_eq!(packet.get_opcode(), Opcodes::StandardQuery); assert_eq!(packet.get_is_authoriative(), 1); assert_eq!(packet.get_is_truncated(), 0); assert_eq!(packet.get_is_recursion_desirable(), 1); assert_eq!(packet.get_is_recursion_available(), 1); assert_eq!(packet.get_zero_reserved(), 0); - assert_eq!(packet.get_rcode(), Retcode::NoError); + assert_eq!(packet.get_rcode(), Retcodes::NoError); assert_eq!(packet.get_query_count(), 1); assert_eq!(packet.get_response_count(), 1); assert_eq!(packet.get_authority_rr_count(), 0); diff --git a/pnet_packet/src/gre.rs b/pnet_packet/src/gre.rs deleted file mode 100644 index 77fbaf43..00000000 --- a/pnet_packet/src/gre.rs +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright (c) 2016 Robert Collins -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Minimal GRE Packet implementation: suitable for inspection not generation (e.g. checksum not -//! implemented). - -#[cfg(test)] -use crate::Packet; - -use alloc::vec::Vec; - -use pnet_macros::packet; -use pnet_macros_support::types::*; - -/// GRE (Generic Routing Encapsulation) Packet. -/// -/// See RFCs 1701, 2784, 2890, 7676, 2637 -/// -/// Current status of implementation: -/// -/// - [RFC 1701](https://tools.ietf.org/html/rfc1701) except for source routing and checksums. -/// Processing a source routed packet will panic. Checksums are able to be inspected, but not -/// calculated or verified. -/// -/// - [RFC 2784](https://tools.ietf.org/html/rfc2784) except for checksums (same as 1701 status). -/// Note that it is possible to generate noncompliant packets by setting any of the reserved bits -/// (but see 2890). -/// -/// - [RFC 2890](https://tools.ietf.org/html/rfc2890) implemented. -/// -/// - [RFC 7676](https://tools.ietf.org/html/rfc7676) has no packet changes - compliance is up to -/// the user. -/// -/// - [RFC 2637](https://tools.ietf.org/html/rfc2637) not implemented. -/// -/// Note that routing information from RFC 1701 is not implemented, packets -/// with `routing_present` true will currently cause a panic. -#[packet] -pub struct Gre { - pub checksum_present: u1, - pub routing_present: u1, - pub key_present: u1, - pub sequence_present: u1, - pub strict_source_route: u1, - pub recursion_control: u3, - pub zero_flags: u5, - pub version: u3, - pub protocol_type: u16be, // 0x800 for ipv4 [basically an ethertype - #[length_fn = "gre_checksum_length"] - pub checksum: Vec, - #[length_fn = "gre_offset_length"] - pub offset: Vec, - #[length_fn = "gre_key_length"] - pub key: Vec, - #[length_fn = "gre_sequence_length"] - pub sequence: Vec, - #[length_fn = "gre_routing_length"] - pub routing: Vec, - #[payload] - pub payload: Vec, -} - -fn gre_checksum_length(gre: &GrePacket) -> usize { - (gre.get_checksum_present() | gre.get_routing_present()) as usize * 2 -} - -fn gre_offset_length(gre: &GrePacket) -> usize { - (gre.get_checksum_present() | gre.get_routing_present()) as usize * 2 -} - -fn gre_key_length(gre: &GrePacket) -> usize { - gre.get_key_present() as usize * 4 -} - -fn gre_sequence_length(gre: &GrePacket) -> usize { - gre.get_sequence_present() as usize * 4 -} - -fn gre_routing_length(gre: &GrePacket) -> usize { - if 0 == gre.get_routing_present() { - 0 - } else { - panic!("Source routed GRE packets not supported") - } -} - - -/// `u16be`, but we can't use that directly in a `Vec` :( -#[packet] -pub struct U16BE { - number: u16be, - #[length = "0"] - #[payload] - unused: Vec, -} - -/// `u32be`, but we can't use that directly in a `Vec` :( -#[packet] -pub struct U32BE { - number: u32be, - #[length = "0"] - #[payload] - unused: Vec, -} - -#[test] -fn gre_packet_test() { - let mut packet = [0u8; 4]; - { - let mut gre_packet = MutableGrePacket::new(&mut packet[..]).unwrap(); - gre_packet.set_protocol_type(0x0800); - assert_eq!(gre_packet.payload().len(), 0); - } - - let ref_packet = [0x00 /* no flags */, - 0x00 /* no flags, version 0 */, - 0x08 /* protocol 0x0800 */, - 0x00]; - - assert_eq!(&ref_packet[..], &packet[..]); -} - -#[test] -fn gre_checksum_test() { - let mut packet = [0u8; 8]; - { - let mut gre_packet = MutableGrePacket::new(&mut packet[..]).unwrap(); - gre_packet.set_checksum_present(1); - assert_eq!(gre_packet.payload().len(), 0); - assert_eq!(gre_packet.get_checksum().len(), 1); - assert_eq!(gre_packet.get_offset().len(), 1); - } - - let ref_packet = [0x80 /* checksum on */, - 0x00 /* no flags, version 0 */, - 0x00 /* protocol 0x0000 */, - 0x00, - 0x00 /* 16 bits of checksum */, - 0x00, - 0x00 /* 16 bits of offset */, - 0x00]; - - assert_eq!(&ref_packet[..], &packet[..]); -} diff --git a/pnet_packet/src/icmpv6.rs b/pnet_packet/src/icmpv6.rs index 5da9c5ed..b23a62be 100644 --- a/pnet_packet/src/icmpv6.rs +++ b/pnet_packet/src/icmpv6.rs @@ -299,12 +299,9 @@ pub mod ndp { /// Calculate a length of a `NdpOption`'s payload. fn ndp_option_payload_length(option: &NdpOptionPacket) -> usize { - let len = option.get_length(); - if len > 0 { - ((len * 8) - 2) as usize - } else { - 0 - } + let len = option.get_length() as usize; + let want = (len * 8).saturating_sub(2); + want.min(option.packet().len().saturating_sub(2)) } /// Router Solicitation Message [RFC 4861 § 4.1] diff --git a/pnet_packet/src/ipv6.rs b/pnet_packet/src/ipv6.rs index b3aa634d..92f4c1b2 100644 --- a/pnet_packet/src/ipv6.rs +++ b/pnet_packet/src/ipv6.rs @@ -37,8 +37,8 @@ pub struct Ipv6 { } impl<'p> ExtensionIterable<'p> { - pub fn new(buf: &[u8]) -> ExtensionIterable { - ExtensionIterable { buf: buf } + pub fn new(buf: &[u8]) -> ExtensionIterable<'_> { + ExtensionIterable { buf } } } diff --git a/pnet_packet/src/lib.rs b/pnet_packet/src/lib.rs index 7069dcc3..dc4839c3 100644 --- a/pnet_packet/src/lib.rs +++ b/pnet_packet/src/lib.rs @@ -10,7 +10,6 @@ #![allow(missing_docs)] #![deny(warnings)] #![no_std] -#![macro_use] extern crate alloc; @@ -28,7 +27,6 @@ pub mod dhcp; pub mod dns; pub mod ethernet; pub mod flowcontrol; -pub mod gre; pub mod icmp; pub mod icmpv6; pub mod ip; diff --git a/pnet_transport/src/lib.rs b/pnet_transport/src/lib.rs index 70cf9ec1..02dc2e21 100644 --- a/pnet_transport/src/lib.rs +++ b/pnet_transport/src/lib.rs @@ -17,7 +17,6 @@ //! implemented in the kernel such as TCP and UDP. #![deny(warnings)] -#![macro_use] extern crate libc; extern crate pnet_packet; @@ -322,15 +321,15 @@ macro_rules! transport_channel_iterator { #[doc = "Return a packet iterator with packets of type `"] #[doc = $tyname] #[doc = "` for some transport receiver."] - pub fn $func(tr: &mut TransportReceiver) -> $iter { - $iter { tr: tr } + pub fn $func(tr: &mut TransportReceiver) -> $iter<'_> { + $iter { tr } } impl<'a> $iter<'a> { #[doc = "Get the next (`"] #[doc = $tyname ] #[doc = "`, `IpAddr`) pair for the given channel."] - pub fn next(&mut self) -> io::Result<($ty, IpAddr)> { + pub fn next(&mut self) -> io::Result<($ty<'_>, IpAddr)> { let mut caddr: pnet_sys::SockAddrStorage = unsafe { mem::zeroed() }; let res = pnet_sys::recv_from(self.tr.socket.fd, &mut self.tr.buffer[..], &mut caddr); @@ -401,7 +400,7 @@ macro_rules! transport_channel_iterator { /// Wait only for a timespan of `t` to receive some data, then return. If no data was /// received, then `Ok(None)` is returned. #[cfg(unix)] - pub fn next_with_timeout(&mut self, t: Duration) -> io::Result> { + pub fn next_with_timeout(&mut self, t: Duration) -> io::Result, IpAddr)>> { let socket_fd = self.tr.socket.fd; let old_timeout = match pnet_sys::get_socket_receive_timeout(socket_fd) { diff --git a/src/lib.rs b/src/lib.rs index 37098de7..e9c47683 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,13 +9,9 @@ #![deny(missing_docs)] #![deny(warnings)] #![cfg_attr(not(feature = "std"), no_std)] -#![cfg_attr(feature = "nightly", feature(custom_attribute, plugin))] -#![cfg_attr(feature = "nightly", plugin(pnet_macros_plugin))] -#![cfg_attr(feature = "clippy", feature(plugin))] #![cfg_attr(feature = "benchmark", feature(test))] -#![cfg_attr(feature = "clippy", plugin(clippy))] // We can't implement Iterator since we use streaming iterators -#![cfg_attr(feature = "clippy", allow(should_implement_trait))] +#![allow(clippy::should_implement_trait)] //! # libpnet //! @@ -148,7 +144,7 @@ pub mod transport { pub mod util; -// NOTE should probably have a cfg(pnet_test_network) here, but cargo doesn't -// allow custom --cfg flags -#[cfg(all(test, std))] +// These are live-network integration tests; opt in with +// `RUSTFLAGS="--cfg pnet_test_network" cargo test` (needs privileges/interfaces). +#[cfg(all(test, pnet_test_network))] mod pnettest;