From 4ff0b6a38b34733b00f7655358733516cc75f968 Mon Sep 17 00:00:00 2001 From: Max Inden Date: Mon, 2 Sep 2024 13:21:10 +0200 Subject: [PATCH 01/15] refactor: enforce immutability of ifap --- src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 4af7c4a..ae6e5d5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -74,10 +74,11 @@ fn get_interface_mtu_linux_macos(socket: &UdpSocket) -> Result { use libc::{ifreq, ioctl}; // Get the interface list. - let mut ifap: *mut ifaddrs = ptr::null_mut(); // Do not modify this pointer. + let mut ifap: *mut ifaddrs = ptr::null_mut(); if unsafe { getifaddrs(&mut ifap) } != 0 { return Err(Error::last_os_error()); } + let ifap = ifap; // Do not modify this pointer. // First, find the name of the interface with the local IP address determined above. let mut cursor = ifap; From 0ef2797ae71fa6180d7c13237ea7c2573389fca5 Mon Sep 17 00:00:00 2001 From: Max Inden Date: Mon, 2 Sep 2024 13:42:35 +0200 Subject: [PATCH 02/15] refactor: enforce immutability of addr_table and if_table --- src/lib.rs | 99 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 51 insertions(+), 48 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index ae6e5d5..5a40e51 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -177,58 +177,61 @@ fn get_interface_mtu_windows(socket: &UdpSocket) -> Result { let mut res = default_result(); // Get a list of all unicast IP addresses with associated metadata. - let mut addr_table: *mut MIB_UNICASTIPADDRESS_TABLE = ptr::null_mut(); // Do not modify this pointer. - if unsafe { GetUnicastIpAddressTable(AF_UNSPEC, &mut addr_table) } == NO_ERROR { - let addrs = unsafe { - slice::from_raw_parts::( - &(*addr_table).Table[0], - (*addr_table).NumEntries as usize, - ) - }; - - // Get a list of all interfaces with associated metadata. - let mut if_table: *mut MIB_IPINTERFACE_TABLE = ptr::null_mut(); // Do not modify this pointer. - if unsafe { GetIpInterfaceTable(AF_UNSPEC, &mut if_table) } == NO_ERROR { - let ifaces = unsafe { - slice::from_raw_parts::( - &(*if_table).Table[0], - (*if_table).NumEntries as usize, - ) - }; - - // Run through the list of addresses and find the one that matches the local IP - // address. - 'addr_loop: for addr in addrs { - let af = unsafe { addr.Address.si_family }; - let ip = socket.local_addr()?.ip(); - if (af == AF_INET && ip.is_ipv4() || af == AF_INET6 && ip.is_ipv6()) - && match ip { - IpAddr::V4(ip) => { - u32::from(ip).to_be() - == unsafe { addr.Address.Ipv4.sin_addr.S_un.S_addr } - } - IpAddr::V6(ip) => { - ip.octets() == unsafe { addr.Address.Ipv6.sin6_addr.u.Byte } - } - } - { - // For the matching address, find local interface and its MTU. - for iface in ifaces { - if iface.InterfaceIndex == addr.InterfaceIndex { - res = iface.NlMtu.try_into().or(res); - break 'addr_loop; - } - } + let mut addr_table: *mut MIB_UNICASTIPADDRESS_TABLE = ptr::null_mut(); + if unsafe { GetUnicastIpAddressTable(AF_UNSPEC, &mut addr_table) } != NO_ERROR { + return Err(Error::last_os_error()); + } + let addr_table = addr_table; // Do not modify this pointer. + + let addrs = unsafe { + slice::from_raw_parts::( + &(*addr_table).Table[0], + (*addr_table).NumEntries as usize, + ) + }; + + // Get a list of all interfaces with associated metadata. + let mut if_table: *mut MIB_IPINTERFACE_TABLE = ptr::null_mut(); + if unsafe { GetIpInterfaceTable(AF_UNSPEC, &mut if_table) } != NO_ERROR { + let error = Error::last_os_error(); + unsafe { FreeMibTable(addr_table as *const c_void) }; + return Err(error); + } + let if_table = if_table; // Do not modify this pointer. + + let ifaces = unsafe { + slice::from_raw_parts::( + &(*if_table).Table[0], + (*if_table).NumEntries as usize, + ) + }; + + // Run through the list of addresses and find the one that matches the local IP + // address. + 'addr_loop: for addr in addrs { + let af = unsafe { addr.Address.si_family }; + let ip = socket.local_addr()?.ip(); + if (af == AF_INET && ip.is_ipv4() || af == AF_INET6 && ip.is_ipv6()) + && match ip { + IpAddr::V4(ip) => { + u32::from(ip).to_be() == unsafe { addr.Address.Ipv4.sin_addr.S_un.S_addr } + } + IpAddr::V6(ip) => ip.octets() == unsafe { addr.Address.Ipv6.sin6_addr.u.Byte }, + } + { + // For the matching address, find local interface and its MTU. + for iface in ifaces { + if iface.InterfaceIndex == addr.InterfaceIndex { + res = iface.NlMtu.try_into().or(res); + break 'addr_loop; } } - unsafe { FreeMibTable(if_table as *const c_void) }; - } else { - res = Err(Error::last_os_error()); } - unsafe { FreeMibTable(addr_table as *const c_void) }; - } else { - res = Err(Error::last_os_error()); } + + unsafe { FreeMibTable(if_table as *const c_void) }; + unsafe { FreeMibTable(addr_table as *const c_void) }; + res } From 06ac3bd8377549a017ae2bf0509e132f6939453b Mon Sep 17 00:00:00 2001 From: Lars Eggert Date: Mon, 2 Sep 2024 15:42:35 +0300 Subject: [PATCH 03/15] doc: Add an example --- src/lib.rs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 4af7c4a..0400db3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,7 +20,23 @@ fn default_result() -> Result { )) } -/// Return the MTU of the interface that is used to reach the given remote socket address. +/// Return the maximum transmission unit (MTU) of the local network interface towards the +/// destination `SocketAddr` given in `remote`. +/// +/// The returned MTU may exceed the maximum IP packet size of 65,535 bytes on some +/// platforms for some remote destinations. (For example, loopback destinations on +/// Windows.) +/// +/// # Examples +/// +/// ``` +/// use std::net::ToSocketAddrs; +/// use mtu::get_interface_mtu; +/// +/// let saddr ="localhost:443".to_socket_addrs().unwrap().next().unwrap(); +/// let mtu = get_interface_mtu(&saddr).unwrap(); +/// println!("MTU towards {:?} is {}", saddr, mtu); +/// ``` /// /// # Errors /// From 902d5875cfd1dafca55b23ff57214dc0d077fba0 Mon Sep 17 00:00:00 2001 From: Lars Eggert Date: Mon, 2 Sep 2024 15:45:09 +0300 Subject: [PATCH 04/15] Reformat and add `.rustfmt.toml` --- .rustfmt.toml | 13 +++++++++++++ src/lib.rs | 3 ++- 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 .rustfmt.toml diff --git a/.rustfmt.toml b/.rustfmt.toml new file mode 100644 index 0000000..39c3ae7 --- /dev/null +++ b/.rustfmt.toml @@ -0,0 +1,13 @@ +# Keep in sync with `Cargo.toml` `edition`. +# +# `rustfmt` envoked not through `cargo fmt` but directly does not pick up Rust +# edition in `Cargo.toml`. Thus duplicate here. +edition = "2021" + +comment_width=100 +wrap_comments=true + +imports_granularity="Crate" +group_imports="StdExternalCrate" + +format_code_in_doc_comments=true diff --git a/src/lib.rs b/src/lib.rs index 0400db3..9e625f3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -31,9 +31,10 @@ fn default_result() -> Result { /// /// ``` /// use std::net::ToSocketAddrs; +/// /// use mtu::get_interface_mtu; /// -/// let saddr ="localhost:443".to_socket_addrs().unwrap().next().unwrap(); +/// let saddr = "localhost:443".to_socket_addrs().unwrap().next().unwrap(); /// let mtu = get_interface_mtu(&saddr).unwrap(); /// println!("MTU towards {:?} is {}", saddr, mtu); /// ``` From e94c1fed1d68948d608dd9dc5b93ca2d7c86b4c6 Mon Sep 17 00:00:00 2001 From: Lars Eggert Date: Mon, 2 Sep 2024 16:09:51 +0300 Subject: [PATCH 05/15] ci: Test on oldest OS runners, too --- .github/workflows/check.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 30d5483..a701757 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -22,7 +22,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-latest, macos-14, windows-latest] + os: [ubuntu-20.04, ubuntu-latest, macos-12, macos-latest, windows-2019, windows-latest] # Keep low end in sync with Cargo.toml rust-toolchain: [1.76.0, stable, nightly] type: [debug] From 6799a952b970b86b828c1173b2e201b527b778d8 Mon Sep 17 00:00:00 2001 From: Lars Eggert Date: Mon, 2 Sep 2024 16:12:52 +0300 Subject: [PATCH 06/15] Codecov upload only on `-latest` --- .github/workflows/check.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index a701757..dbd7431 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -108,4 +108,4 @@ jobs: verbose: true env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} - if: matrix.type == 'debug' && matrix.rust-toolchain == 'stable' + if: matrix.type == 'debug' && matrix.rust-toolchain == 'stable' && endsWith(matrix.os, '-latest') From 8f34f6c9019727228511079da55bc0a14a592a25 Mon Sep 17 00:00:00 2001 From: Lars Eggert Date: Mon, 2 Sep 2024 16:16:13 +0300 Subject: [PATCH 07/15] Fewer permutations --- .github/workflows/check.yml | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index dbd7431..756b9bf 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -22,7 +22,7 @@ jobs: strategy: fail-fast: false matrix: - os: [ubuntu-20.04, ubuntu-latest, macos-12, macos-latest, windows-2019, windows-latest] + os: [ubuntu-latest, macos-latest, windows-latest] # Keep low end in sync with Cargo.toml rust-toolchain: [1.76.0, stable, nightly] type: [debug] @@ -30,12 +30,21 @@ jobs: - os: ubuntu-latest rust-toolchain: stable type: release - - os: macos-14 + - os: macos-latest rust-toolchain: stable type: release - os: windows-latest rust-toolchain: stable type: release + - os: ubuntu-20.04 + rust-toolchain: stable + type: debug + - os: macos-12 + rust-toolchain: stable + type: debug + - os: windows-2019 + rust-toolchain: stable + type: debug env: BUILD_TYPE: ${{ matrix.type == 'release' && '--release' || '' }} runs-on: ${{ matrix.os }} From 9bf01aa431ad052abfe1b7902c46852e7279d492 Mon Sep 17 00:00:00 2001 From: Max Inden Date: Mon, 2 Sep 2024 14:04:31 +0200 Subject: [PATCH 08/15] ci: run with sanitizers --- .github/workflows/check.yml | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 30d5483..c5a8e8c 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -59,7 +59,7 @@ jobs: uses: ./neqo/.github/actions/rust with: version: ${{ matrix.rust-toolchain }} - components: rustfmt, clippy, llvm-tools-preview + components: rustfmt, clippy, llvm-tools-preview, ${{ matrix.rust-toolchain == 'nightly' && 'rust-src' || '' }} tools: cargo-llvm-cov, cargo-nextest, cargo-hack, cargo-machete token: ${{ secrets.GITHUB_TOKEN }} @@ -74,6 +74,23 @@ jobs: RUST_LOG=trace cargo +${{ matrix.rust-toolchain }} llvm-cov nextest $BUILD_TYPE --no-fail-fast --lcov --output-path lcov.info cargo +${{ matrix.rust-toolchain }} bench --no-run + - name: Run tests with sanitizers + if: (matrix.os == 'ubuntu-latest' || matrix.os == 'macos-14') && matrix.rust-toolchain == 'nightly' + run: | + if [ "${{ matrix.os }}" = "ubuntu-latest" ]; then + TARGET="x86_64-unknown-linux-gnu" + SANITIZERS="address thread leak memory" + elif [ "${{ matrix.os }}" = "macos-14" ]; then + TARGET="aarch64-apple-darwin" + # no memory and leak sanitizer support yet + SANITIZERS="address thread" + fi + + for sanitizer in $SANITIZERS; do + echo "Running tests with $sanitizer sanitizer..." + RUSTFLAGS="-Z sanitizer=$sanitizer" cargo +nightly test -Z build-std --target $TARGET + done + - name: Check formatting run: | if [ "${{ matrix.rust-toolchain }}" != "nightly" ]; then From aca1efd156dc03f44047e104b9bb0dbfe973b9f4 Mon Sep 17 00:00:00 2001 From: Lars Eggert Date: Mon, 2 Sep 2024 17:11:05 +0300 Subject: [PATCH 09/15] Merge fixes --- .github/workflows/check.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index e0e18a4..d06ee39 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -84,12 +84,12 @@ jobs: cargo +${{ matrix.rust-toolchain }} bench --no-run - name: Run tests with sanitizers - if: (matrix.os == 'ubuntu-latest' || matrix.os == 'macos-14') && matrix.rust-toolchain == 'nightly' + if: (matrix.os == 'ubuntu-latest' || matrix.os == 'macos-latest') && matrix.rust-toolchain == 'nightly' run: | if [ "${{ matrix.os }}" = "ubuntu-latest" ]; then TARGET="x86_64-unknown-linux-gnu" SANITIZERS="address thread leak memory" - elif [ "${{ matrix.os }}" = "macos-14" ]; then + elif [ "${{ matrix.os }}" = "macos-latest" ]; then TARGET="aarch64-apple-darwin" # no memory and leak sanitizer support yet SANITIZERS="address thread" @@ -97,7 +97,7 @@ jobs: for sanitizer in $SANITIZERS; do echo "Running tests with $sanitizer sanitizer..." - RUSTFLAGS="-Z sanitizer=$sanitizer" cargo +nightly test -Z build-std --target $TARGET + RUSTFLAGS="-Z sanitizer=$sanitizer" cargo +nightly test -Z build-std --target "$TARGET" done - name: Check formatting From 5bb8d2b1dc422a5a01fa156bdf56fc9199e96baa Mon Sep 17 00:00:00 2001 From: Lars Eggert Date: Mon, 2 Sep 2024 17:12:57 +0300 Subject: [PATCH 10/15] ci: Add `actionlint` workflow --- .github/actionlint-matcher.json | 17 +++++++++++++++++ .github/workflows/actionlint.yml | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 .github/actionlint-matcher.json create mode 100644 .github/workflows/actionlint.yml diff --git a/.github/actionlint-matcher.json b/.github/actionlint-matcher.json new file mode 100644 index 0000000..4613e16 --- /dev/null +++ b/.github/actionlint-matcher.json @@ -0,0 +1,17 @@ +{ + "problemMatcher": [ + { + "owner": "actionlint", + "pattern": [ + { + "regexp": "^(?:\\x1b\\[\\d+m)?(.+?)(?:\\x1b\\[\\d+m)*:(?:\\x1b\\[\\d+m)*(\\d+)(?:\\x1b\\[\\d+m)*:(?:\\x1b\\[\\d+m)*(\\d+)(?:\\x1b\\[\\d+m)*: (?:\\x1b\\[\\d+m)*(.+?)(?:\\x1b\\[\\d+m)* \\[(.+?)\\]$", + "file": 1, + "line": 2, + "column": 3, + "message": 4, + "code": 5 + } + ] + } + ] +} diff --git a/.github/workflows/actionlint.yml b/.github/workflows/actionlint.yml new file mode 100644 index 0000000..5b06b8c --- /dev/null +++ b/.github/workflows/actionlint.yml @@ -0,0 +1,32 @@ +name: Lint GitHub Actions workflows +on: + push: + branches: ["main"] + paths: [".github/**"] + pull_request: + branches: ["main"] + paths: [".github/**"] + merge_group: + +concurrency: + group: ${{ github.workflow }}-${{ github.ref_name }} + cancel-in-progress: true + +permissions: + contents: read + +jobs: + actionlint: + runs-on: ubuntu-latest + defaults: + run: + shell: bash + steps: + - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 + - name: Download actionlint + id: get_actionlint + run: bash <(curl https://raw.githubusercontent.com/rhysd/actionlint/main/scripts/download-actionlint.bash) + - name: Check workflow files + run: | + echo "::add-matcher::.github/actionlint-matcher.json" + ${{ steps.get_actionlint.outputs.executable }} -color From 57396d9009270bff288fa250156383e6fd2fec57 Mon Sep 17 00:00:00 2001 From: Lars Eggert Date: Mon, 2 Sep 2024 17:22:54 +0300 Subject: [PATCH 11/15] ci: Add `.codecov.yml` file --- .codecov.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 .codecov.yml diff --git a/.codecov.yml b/.codecov.yml new file mode 100644 index 0000000..a04e2e3 --- /dev/null +++ b/.codecov.yml @@ -0,0 +1,15 @@ +ignore: + +# Do not notify until at least three results have been uploaded from the CI pipeline. +# (This corresponds to the three main platforms we support: Linux, macOS, and Windows.) +codecov: + notify: + after_n_builds: 3 +comment: + after_n_builds: 3 + +coverage: + status: + project: + default: + threshold: 0.05% From c8740874e764557c99938c4005f12151c5ce4859 Mon Sep 17 00:00:00 2001 From: Lars Eggert Date: Mon, 2 Sep 2024 17:27:49 +0300 Subject: [PATCH 12/15] Fix from @mxinden --- .github/workflows/check.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index c5a8e8c..3eaf585 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -88,7 +88,7 @@ jobs: for sanitizer in $SANITIZERS; do echo "Running tests with $sanitizer sanitizer..." - RUSTFLAGS="-Z sanitizer=$sanitizer" cargo +nightly test -Z build-std --target $TARGET + RUSTFLAGS="-Z sanitizer=$sanitizer" RUSTDOCFLAGS="-Z sanitizer=$sanitizer" cargo +nightly test -Z build-std --target $TARGET done - name: Check formatting From e93c3137f5ecd468f8d62e4edf707b5983f581ec Mon Sep 17 00:00:00 2001 From: Lars Eggert Date: Mon, 2 Sep 2024 17:42:26 +0300 Subject: [PATCH 13/15] Update src/lib.rs Co-authored-by: Max Inden --- src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib.rs b/src/lib.rs index 87bede4..c279494 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,7 +21,7 @@ fn default_result() -> Result { } /// Return the maximum transmission unit (MTU) of the local network interface towards the -/// destination `SocketAddr` given in `remote`. +/// destination [`SocketAddr`] given in `remote`. /// /// The returned MTU may exceed the maximum IP packet size of 65,535 bytes on some /// platforms for some remote destinations. (For example, loopback destinations on From bdfa8dcc47cfebfcaa5753f61b9383129ff72fe8 Mon Sep 17 00:00:00 2001 From: Lars Eggert Date: Mon, 2 Sep 2024 17:42:47 +0300 Subject: [PATCH 14/15] Update src/lib.rs Co-authored-by: Max Inden --- src/lib.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index c279494..2c9a971 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -30,11 +30,9 @@ fn default_result() -> Result { /// # Examples /// /// ``` -/// use std::net::ToSocketAddrs; -/// -/// use mtu::get_interface_mtu; -/// -/// let saddr = "localhost:443".to_socket_addrs().unwrap().next().unwrap(); +/// # use std::net::ToSocketAddrs; +/// # use mtu::get_interface_mtu; +/// let saddr = "127.0.0.1:443".parse().unwrap(); /// let mtu = get_interface_mtu(&saddr).unwrap(); /// println!("MTU towards {:?} is {}", saddr, mtu); /// ``` From 473763d04e9f44f1c7ec42d259ef95bdb69db9ce Mon Sep 17 00:00:00 2001 From: Lars Eggert Date: Mon, 2 Sep 2024 17:47:13 +0300 Subject: [PATCH 15/15] Remove `use` comments --- src/lib.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 2c9a971..c2a939b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -30,10 +30,8 @@ fn default_result() -> Result { /// # Examples /// /// ``` -/// # use std::net::ToSocketAddrs; -/// # use mtu::get_interface_mtu; /// let saddr = "127.0.0.1:443".parse().unwrap(); -/// let mtu = get_interface_mtu(&saddr).unwrap(); +/// let mtu = mtu::get_interface_mtu(&saddr).unwrap(); /// println!("MTU towards {:?} is {}", saddr, mtu); /// ``` ///