From 52c6de4182864d49c553541783e5ed1db0e71907 Mon Sep 17 00:00:00 2001 From: Sandro Noel Date: Tue, 23 Dec 2025 10:14:54 -0700 Subject: [PATCH 1/7] Added FreeBSD Support and instrcutions --- README.md | 27 ++++++++++++++++++++++ zeroconf/Cargo.toml | 3 +++ zeroconf/src/bonjour/browser.rs | 10 +++++++++ zeroconf/src/ffi/mod.rs | 40 +++++++++++++++++++++++++++++++++ zeroconf/src/lib.rs | 12 +++++----- 5 files changed, 86 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 5a523fe..41fd6c8 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,33 @@ On Windows: Bonjour must be installed. It comes bundled with [iTunes][] or [Bonjour Print Services][]. Further redistribution & bundling details are available on the [Apple Developer Site][]. +On FreeBSD: + +Bonjour/mDNSResponder must be installed. + +```bash +sudo pkg install mDNSResponder +``` + +After installing mDNSResponder, you need to enable and start the service: + +``` bash +sudo sysrc mdnsd_enable="YES" +sudo service mdnsd start +``` + +You might also need the development headers/libraries for linking. Check if you have the necessary files: +Check for mDNS headers + +``` bash +ls /usr/local/include/dns_sd.h +``` + +Check for libraries + +``` bash +ls /usr/local/lib/libdns_sd.so* + ## Examples ### Register a service diff --git a/zeroconf/Cargo.toml b/zeroconf/Cargo.toml index 7c927c8..5aa3f64 100644 --- a/zeroconf/Cargo.toml +++ b/zeroconf/Cargo.toml @@ -42,6 +42,9 @@ bonjour-sys = "0.3.0" [target.'cfg(target_vendor = "pc")'.dependencies] bonjour-sys = "0.3.0" +[target.'cfg(target_os = "freebsd")'.dependencies] +bonjour-sys = "0.3.0" + [package.metadata.docs.rs] default-target = "x86_64-unknown-linux-gnu" targets = ["x86_64-apple-darwin", "x86_64-pc-windows-msvc"] diff --git a/zeroconf/src/bonjour/browser.rs b/zeroconf/src/bonjour/browser.rs index bebf7aa..a5c2371 100644 --- a/zeroconf/src/bonjour/browser.rs +++ b/zeroconf/src/bonjour/browser.rs @@ -14,6 +14,8 @@ use bonjour_sys::sockaddr_in; use bonjour_sys::{DNSServiceErrorType, DNSServiceFlags, DNSServiceRef}; #[cfg(target_vendor = "apple")] use libc::sockaddr_in; +#[cfg(target_os = "freebsd")] +use libc::sockaddr_in; use libc::{c_char, c_uchar, c_void}; use std::any::Any; use std::ffi::CString; @@ -325,6 +327,14 @@ unsafe fn handle_get_address_info( IpAddr::from(s_addr).to_string() }; + #[cfg(target_os = "freebsd")] + let ip = { + let address = address as *const sockaddr_in; + assert_not_null!(address); + let s_addr = unsafe { (*address).sin_addr.s_addr.to_le_bytes() }; + IpAddr::from(s_addr).to_string() + }; + let hostname = unsafe { c_str::copy_raw(hostname) }; let domain = bonjour_util::normalize_domain( diff --git a/zeroconf/src/ffi/mod.rs b/zeroconf/src/ffi/mod.rs index 6ff1458..ba94848 100644 --- a/zeroconf/src/ffi/mod.rs +++ b/zeroconf/src/ffi/mod.rs @@ -132,3 +132,43 @@ pub(crate) mod bonjour { } } } + +#[cfg(target_os = "freebsd")] +pub(crate) mod bonjour { + use crate::Result; + use libc::{fd_set, suseconds_t, time_t, timeval}; + use std::time::Duration; + use std::{mem, ptr}; + + /// Performs a unix `select()` on the specified `sock_fd` and `timeout`. Returns the select result + /// or `Err` if the result is negative. + /// + /// # Safety + /// This function is unsafe because it directly interfaces with C-library system calls. + pub unsafe fn read_select(sock_fd: i32, timeout: Duration) -> Result { + let mut read_flags: fd_set = unsafe { mem::zeroed() }; + + unsafe { libc::FD_ZERO(&mut read_flags) }; + unsafe { libc::FD_SET(sock_fd, &mut read_flags) }; + + let tv_sec = timeout.as_secs() as time_t; + let tv_usec = timeout.subsec_micros() as suseconds_t; + let mut timeout = timeval { tv_sec, tv_usec }; + + let result = unsafe { + libc::select( + sock_fd + 1, + &mut read_flags, + ptr::null_mut(), + ptr::null_mut(), + &mut timeout, + ) + }; + + if result < 0 { + Err("select(): returned error status".into()) + } else { + Ok(result as u32) + } + } +} diff --git a/zeroconf/src/lib.rs b/zeroconf/src/lib.rs index a0c7b42..a1a655c 100644 --- a/zeroconf/src/lib.rs +++ b/zeroconf/src/lib.rs @@ -194,7 +194,7 @@ extern crate derive_builder; extern crate zeroconf_macros; #[cfg(target_os = "linux")] extern crate avahi_sys; -#[cfg(any(target_vendor = "apple", target_vendor = "pc"))] +#[cfg(any(target_vendor = "apple", target_vendor = "pc", target_os = "freebsd"))] extern crate bonjour_sys; #[macro_use] extern crate derive_getters; @@ -225,7 +225,7 @@ pub mod txt_record; #[cfg(target_os = "linux")] pub mod avahi; -#[cfg(any(target_vendor = "apple", target_vendor = "pc"))] +#[cfg(any(target_vendor = "apple", target_vendor = "pc", target_os = "freebsd"))] pub mod bonjour; pub use browser::{BrowserEvent, ServiceBrowserCallback, ServiceDiscovery, ServiceRemoval}; @@ -237,21 +237,21 @@ pub use service_type::*; #[cfg(target_os = "linux")] pub type MdnsBrowser = avahi::browser::AvahiMdnsBrowser; /// Type alias for the platform-specific mDNS browser implementation -#[cfg(any(target_vendor = "apple", target_vendor = "pc"))] +#[cfg(any(target_vendor = "apple", target_vendor = "pc", target_os = "freebsd"))] pub type MdnsBrowser = bonjour::browser::BonjourMdnsBrowser; /// Type alias for the platform-specific mDNS service implementation #[cfg(target_os = "linux")] pub type MdnsService = avahi::service::AvahiMdnsService; /// Type alias for the platform-specific mDNS service implementation -#[cfg(any(target_vendor = "apple", target_vendor = "pc"))] +#[cfg(any(target_vendor = "apple", target_vendor = "pc", target_os = "freebsd"))] pub type MdnsService = bonjour::service::BonjourMdnsService; /// Type alias for the platform-specific structure responsible for polling the mDNS event loop #[cfg(target_os = "linux")] pub type EventLoop = avahi::event_loop::AvahiEventLoop; /// Type alias for the platform-specific structure responsible for polling the mDNS event loop -#[cfg(any(target_vendor = "apple", target_vendor = "pc"))] +#[cfg(any(target_vendor = "apple", target_vendor = "pc", target_os = "freebsd"))] pub type EventLoop = bonjour::event_loop::BonjourEventLoop; /// Type alias for the platform-specific structure responsible for storing and accessing TXT @@ -260,7 +260,7 @@ pub type EventLoop = bonjour::event_loop::BonjourEventLoop; pub type TxtRecord = avahi::txt_record::AvahiTxtRecord; /// Type alias for the platform-specific structure responsible for storing and accessing TXT /// record data -#[cfg(any(target_vendor = "apple", target_vendor = "pc"))] +#[cfg(any(target_vendor = "apple", target_vendor = "pc", target_os = "freebsd"))] pub type TxtRecord = bonjour::txt_record::BonjourTxtRecord; /// Result type for this library From 762c2944cc06a6ad6018ce32573c0ca0e1f1e366 Mon Sep 17 00:00:00 2001 From: Sandro Noel Date: Wed, 24 Dec 2025 12:41:28 -0700 Subject: [PATCH 2/7] Removed duplicated blocks. --- zeroconf/src/bonjour/browser.rs | 4 +--- zeroconf/src/ffi/mod.rs | 42 +-------------------------------- 2 files changed, 2 insertions(+), 44 deletions(-) diff --git a/zeroconf/src/bonjour/browser.rs b/zeroconf/src/bonjour/browser.rs index a5c2371..e08fd39 100644 --- a/zeroconf/src/bonjour/browser.rs +++ b/zeroconf/src/bonjour/browser.rs @@ -12,9 +12,7 @@ use crate::{EventLoop, NetworkInterface, Result, ServiceType, TxtRecord}; #[cfg(target_vendor = "pc")] use bonjour_sys::sockaddr_in; use bonjour_sys::{DNSServiceErrorType, DNSServiceFlags, DNSServiceRef}; -#[cfg(target_vendor = "apple")] -use libc::sockaddr_in; -#[cfg(target_os = "freebsd")] +#[cfg(any(target_vendor = "apple", target_os = "freebsd"))] use libc::sockaddr_in; use libc::{c_char, c_uchar, c_void}; use std::any::Any; diff --git a/zeroconf/src/ffi/mod.rs b/zeroconf/src/ffi/mod.rs index ba94848..ad03124 100644 --- a/zeroconf/src/ffi/mod.rs +++ b/zeroconf/src/ffi/mod.rs @@ -51,7 +51,7 @@ impl UnwrapMutOrNull for Option<*mut T> { } } -#[cfg(target_vendor = "apple")] +#[cfg(any(target_vendor = "apple", target_os = "freebsd"))] pub(crate) mod bonjour { use crate::Result; use libc::{fd_set, suseconds_t, time_t, timeval}; @@ -132,43 +132,3 @@ pub(crate) mod bonjour { } } } - -#[cfg(target_os = "freebsd")] -pub(crate) mod bonjour { - use crate::Result; - use libc::{fd_set, suseconds_t, time_t, timeval}; - use std::time::Duration; - use std::{mem, ptr}; - - /// Performs a unix `select()` on the specified `sock_fd` and `timeout`. Returns the select result - /// or `Err` if the result is negative. - /// - /// # Safety - /// This function is unsafe because it directly interfaces with C-library system calls. - pub unsafe fn read_select(sock_fd: i32, timeout: Duration) -> Result { - let mut read_flags: fd_set = unsafe { mem::zeroed() }; - - unsafe { libc::FD_ZERO(&mut read_flags) }; - unsafe { libc::FD_SET(sock_fd, &mut read_flags) }; - - let tv_sec = timeout.as_secs() as time_t; - let tv_usec = timeout.subsec_micros() as suseconds_t; - let mut timeout = timeval { tv_sec, tv_usec }; - - let result = unsafe { - libc::select( - sock_fd + 1, - &mut read_flags, - ptr::null_mut(), - ptr::null_mut(), - &mut timeout, - ) - }; - - if result < 0 { - Err("select(): returned error status".into()) - } else { - Ok(result as u32) - } - } -} From e986281d8bf5ceb5a5d85ca0c7ce491d09483494 Mon Sep 17 00:00:00 2001 From: Walker Crouse Date: Mon, 23 Feb 2026 13:09:41 -0500 Subject: [PATCH 3/7] add FreeBSD GitHub Actions job Signed-off-by: Walker Crouse --- .github/workflows/rust.yml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index a999eee..940bd6e 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -49,3 +49,26 @@ jobs: - name: Run Clippy run: cargo clippy --features "${{ matrix.features }}" -- -D warnings + + build-freebsd: + name: zeroconf-rs (FreeBSD) + runs-on: ubuntu-latest + strategy: + matrix: + features: [serde, ""] + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Build and test on FreeBSD + uses: vmactions/freebsd-vm@v1 + with: + usesh: true + prepare: | + pkg install -y curl rust mDNSResponder llvm + run: | + cargo build --features "${{ matrix.features }}" + cargo test --features "${{ matrix.features }}" -- --skip service_register_is_browsable + cargo fmt -- --check + cargo clippy --features "${{ matrix.features }}" -- -D warnings From c3696a70781e01414f3ec9b8b950eaceef0e3452 Mon Sep 17 00:00:00 2001 From: Walker Crouse Date: Mon, 23 Feb 2026 17:14:19 -0500 Subject: [PATCH 4/7] upgrade bonjour-sys Signed-off-by: Walker Crouse --- Cargo.lock | 35 ++++++++++++++++------------------- zeroconf/Cargo.toml | 6 +++--- 2 files changed, 19 insertions(+), 22 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4d5f8ee..e891780 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -73,22 +73,22 @@ dependencies = [ [[package]] name = "bindgen" -version = "0.68.1" +version = "0.69.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "726e4313eb6ec35d2730258ad4e15b547ee75d6afaa1361a922e78e59b7d8078" +checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" dependencies = [ "bitflags", "cexpr", "clang-sys", + "itertools", "lazy_static", "lazycell", "log", - "peeking_take_while", "prettyplease", "proc-macro2", "quote", "regex", - "rustc-hash", + "rustc-hash 1.1.0", "shlex", "syn 2.0.110", "which", @@ -96,25 +96,22 @@ dependencies = [ [[package]] name = "bindgen" -version = "0.69.5" +version = "0.72.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" +checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" dependencies = [ "bitflags", "cexpr", "clang-sys", "itertools", - "lazy_static", - "lazycell", "log", "prettyplease", "proc-macro2", "quote", "regex", - "rustc-hash", + "rustc-hash 2.1.1", "shlex", "syn 2.0.110", - "which", ] [[package]] @@ -125,11 +122,11 @@ checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" [[package]] name = "bonjour-sys" -version = "0.3.1" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be8655c4daf1447c831970e4134e8312a15c63fbff109b353cfe934c22f2b61a" +checksum = "73af71be57caf454918b16e5f4ac6bfcbe2dcd68c381f080a4cd1df598a24627" dependencies = [ - "bindgen 0.68.1", + "bindgen 0.72.1", "libc", ] @@ -473,12 +470,6 @@ version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" -[[package]] -name = "peeking_take_while" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" - [[package]] name = "prettyplease" version = "0.2.37" @@ -542,6 +533,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + [[package]] name = "rustix" version = "0.38.44" diff --git a/zeroconf/Cargo.toml b/zeroconf/Cargo.toml index 5aa3f64..d690a31 100644 --- a/zeroconf/Cargo.toml +++ b/zeroconf/Cargo.toml @@ -37,13 +37,13 @@ clap = { version = "4.4.4", features = ["derive"] } avahi-sys = "0.10.1" [target.'cfg(target_vendor = "apple")'.dependencies] -bonjour-sys = "0.3.0" +bonjour-sys = { version = "0.4.0" } [target.'cfg(target_vendor = "pc")'.dependencies] -bonjour-sys = "0.3.0" +bonjour-sys = { version = "0.4.0" } [target.'cfg(target_os = "freebsd")'.dependencies] -bonjour-sys = "0.3.0" +bonjour-sys = { version = "0.4.0" } [package.metadata.docs.rs] default-target = "x86_64-unknown-linux-gnu" From 38ccd0a403f8ecd24a2945457d1b9686cce00fbb Mon Sep 17 00:00:00 2001 From: Walker Crouse Date: Mon, 23 Feb 2026 18:59:24 -0500 Subject: [PATCH 5/7] update github actions job Signed-off-by: Walker Crouse --- .github/workflows/rust.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 940bd6e..575fd71 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -67,6 +67,8 @@ jobs: usesh: true prepare: | pkg install -y curl rust mDNSResponder llvm + sysrc mdnsd_enable="YES" + service mdnsd start run: | cargo build --features "${{ matrix.features }}" cargo test --features "${{ matrix.features }}" -- --skip service_register_is_browsable From 6eda13030bb27dbfdc04d2cf5be97afc06c46f91 Mon Sep 17 00:00:00 2001 From: Walker Crouse Date: Tue, 24 Feb 2026 10:04:04 -0500 Subject: [PATCH 6/7] update README: Signed-off-by: Walker Crouse --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 41fd6c8..8fd641e 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,7 @@ Check for libraries ``` bash ls /usr/local/lib/libdns_sd.so* +``` ## Examples From 8cc15573cd5d77d8245a7c7180f0f1a80b44ad6c Mon Sep 17 00:00:00 2001 From: Walker Crouse Date: Tue, 24 Feb 2026 10:07:22 -0500 Subject: [PATCH 7/7] combine identical conditionally compiled blocks Signed-off-by: Walker Crouse --- zeroconf/src/bonjour/browser.rs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/zeroconf/src/bonjour/browser.rs b/zeroconf/src/bonjour/browser.rs index e08fd39..e28b553 100644 --- a/zeroconf/src/bonjour/browser.rs +++ b/zeroconf/src/bonjour/browser.rs @@ -308,7 +308,7 @@ unsafe fn handle_get_address_info( let port: u16 = ctx.resolved_port.to_be(); // on macOS the bytes are swapped for the ip - #[cfg(target_vendor = "apple")] + #[cfg(any(target_vendor = "apple", target_os = "freebsd"))] let ip = { let address = address as *const sockaddr_in; assert_not_null!(address); @@ -325,14 +325,6 @@ unsafe fn handle_get_address_info( IpAddr::from(s_addr).to_string() }; - #[cfg(target_os = "freebsd")] - let ip = { - let address = address as *const sockaddr_in; - assert_not_null!(address); - let s_addr = unsafe { (*address).sin_addr.s_addr.to_le_bytes() }; - IpAddr::from(s_addr).to_string() - }; - let hostname = unsafe { c_str::copy_raw(hostname) }; let domain = bonjour_util::normalize_domain(