From 77f7e9fc912c338cc8988c90ebb84bdb9fa968f6 Mon Sep 17 00:00:00 2001 From: Ben Date: Sun, 2 Aug 2026 09:23:37 +0200 Subject: [PATCH] [client] Handle interface lookup errors in iOS DNS index helper (#6999) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Describe your changes `getInterfaceIndex` in the iOS upstream DNS resolver dereferenced the result of `net.InterfaceByName` before checking the error, so a missing interface (e.g. during teardown or renaming) caused a nil-pointer panic instead of a DNS client error. The helper now returns a wrapped error before touching the interface; the only caller, `GetClientPrivate`, already propagates the error. The helper moved to an un-build-tagged file so it can be unit-tested on host platforms while remaining available to the iOS build. Added a test covering the missing-interface path. Verified with the new host test, an iOS arm64 CGO compile, and `git diff --check`. ## Issue ticket number and link N/A ## Stack Standalone PR based on `main`. ### Checklist - [x] Is it a bug fix - [ ] Is a typo/documentation fix - [ ] Is a feature enhancement - [ ] It is a refactor - [x] Created tests that fail without the change (if possible) - [x] This change does **not** modify the public API, gRPC protocols, functionality behavior, CLI / service flags, or introduce a new feature — **OR** I have discussed it with the NetBird team beforehand (link the issue / Slack thread in the description). See [CONTRIBUTING.md](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTING.md#discuss-changes-with-the-netbird-team-first). > By submitting this pull request, you confirm that you have read and agree to the terms of the [Contributor License Agreement](https://github.com/netbirdio/netbird/blob/main/CONTRIBUTOR_LICENSE_AGREEMENT.md). ## Documentation Select exactly one: - [ ] I added/updated documentation for this change - [x] Documentation is **not needed** for this change (explain why) Internal crash fix in the iOS DNS path; no user-facing behavior or configuration changes. ### Docs PR URL (required if "docs added" is checked) N/A ## Summary by CodeRabbit * **Bug Fixes** * Improved handling of network interface lookup failures with clearer error messages that identify the affected interface. * Added validation for network interface lookups, including reliable error handling when an interface cannot be found. * **Tests** * Added coverage for both successful interface resolution and missing-interface scenarios. --- client/internal/dns/interface_index.go | 15 +++++++++ client/internal/dns/interface_index_test.go | 35 +++++++++++++++++++++ client/internal/dns/upstream_ios.go | 5 --- 3 files changed, 50 insertions(+), 5 deletions(-) create mode 100644 client/internal/dns/interface_index.go create mode 100644 client/internal/dns/interface_index_test.go diff --git a/client/internal/dns/interface_index.go b/client/internal/dns/interface_index.go new file mode 100644 index 000000000..9e7dca080 --- /dev/null +++ b/client/internal/dns/interface_index.go @@ -0,0 +1,15 @@ +package dns + +import ( + "fmt" + "net" +) + +func getInterfaceIndex(interfaceName string) (int, error) { + iface, err := net.InterfaceByName(interfaceName) + if err != nil { + return 0, fmt.Errorf("lookup interface %q: %w", interfaceName, err) + } + + return iface.Index, nil +} diff --git a/client/internal/dns/interface_index_test.go b/client/internal/dns/interface_index_test.go new file mode 100644 index 000000000..9b146398a --- /dev/null +++ b/client/internal/dns/interface_index_test.go @@ -0,0 +1,35 @@ +package dns + +import ( + "net" + "testing" +) + +func TestGetInterfaceIndexExisting(t *testing.T) { + interfaces, err := net.Interfaces() + if err != nil { + t.Fatalf("list network interfaces: %v", err) + } + if len(interfaces) == 0 { + t.Fatal("expected at least one network interface") + } + + iface := interfaces[0] + index, err := getInterfaceIndex(iface.Name) + if err != nil { + t.Fatalf("look up existing interface %q: %v", iface.Name, err) + } + if index != iface.Index { + t.Fatalf("expected interface index %d, got %d", iface.Index, index) + } +} + +func TestGetInterfaceIndexMissing(t *testing.T) { + index, err := getInterfaceIndex("netbird-interface-that-does-not-exist") + if index != 0 { + t.Fatalf("expected missing interface index to be 0, got %d", index) + } + if err == nil { + t.Fatal("expected missing interface lookup to return an error") + } +} diff --git a/client/internal/dns/upstream_ios.go b/client/internal/dns/upstream_ios.go index b989bf0f9..793d87fca 100644 --- a/client/internal/dns/upstream_ios.go +++ b/client/internal/dns/upstream_ios.go @@ -130,8 +130,3 @@ func GetClientPrivate(iface privateClientIface, upstreamIP netip.Addr, dialTimeo } return client, nil } - -func getInterfaceIndex(interfaceName string) (int, error) { - iface, err := net.InterfaceByName(interfaceName) - return iface.Index, err -}