I'm wondering if there is a way to allow the device (in this case MBP - MacOS 14.4) other programs to reuse the 5353 port that bonjour uses.
I'd like to also receive and listen to mDNS queries sent by others.
I do not want to disable mDNS responder completely, but would like to reuse the port.
My exact plan / goal:
The problem is, I'm unable to listen to port 5353 because it's already used by _mdns_responder by Apple.
The weirdest part is that JVM's MulticastSocket is able to bind and listen on port 5353 even with the Apple provider mDNSResponder running.
Thank you
So if anyone in the future comes across this issue, you have to know that you need to set both SO_REUSEPORT (socket reuse port) and SO_REUSEADDR (socket reuse address) since the addr:port is shared between all who listen on mDNS.
Here is the gist of the code (references in code marked as [X]):
[0] - We need to create a socket using C bindings because in order to set socket options, we have to do so before BINDING the socket.
Deps used: rust STD, netif (for network interface fetching) and libc for C bindings.
use std::ffi::c_void;
use std::mem::size_of;
use std::net::{Ipv6Addr, UdpSocket};
use std::os::fd::FromRawFd;
use std::str::FromStr;
use libc::{AF_INET6, bind, c_char, in6_addr, in_addr, perror, setsockopt, SO_REUSEADDR, SO_REUSEPORT, SOCK_DGRAM, sockaddr, sockaddr_in, sockaddr_in6, socket, socklen_t, SOL_SOCKET};
fn main() {
let error_text = "OPT FAILED" as *const _; // Set error text
let multicast_ipv6 = Ipv6Addr::from_str("ff02::fb").unwrap(); // IPv6 representation of mDNS addresss.
let fd = unsafe { socket(AF_INET6, SOCK_DGRAM, 0) }; // Use C bindings to create a socket [0]
let option_value = 1; // Enable the option
let cc = &option_value as *const _; // Rust <=> C type games...
unsafe {
let x = &sockaddr_in6 {
sin6_family: 30, // AF_INET6
sin6_port: 5353u16.to_be(), // Networking => Big Endian
sin6_addr: in6_addr { s6_addr: [0u8; 16] }, // [::] => Any Address
sin6_len: 0,
sin6_flowinfo: 0,
sin6_scope_id: 0,
} as *const _ as *const sockaddr; // Rust <=> C type games
// Set the SO_REUSEADDR option
if setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, cc as *const c_void, size_of::<i32>() as socklen_t) < 0 {
perror(error_text as *const c_char);
}
// Set the SO_REUSEPORT option
if setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, cc as *const c_void, size_of::<i32>() as socklen_t) < 0 {
perror(error_text as *const c_char);
}
if bind(fd, x, size_of::<sockaddr_in6>() as socklen_t) < 0 {
perror(error_text as *const c_char);
}
}
let mut socket = unsafe { UdpSocket::from_raw_fd(fd) }; // Retrieve the socket back from C bindings to Rust type.
// This varies per device, in my case I wanted to join en7 network interface.
let interface = netif::up().unwrap().filter(|x| x.name().contains("en7")).max_by(|x, y| x.scope_id().cmp(&y.scope_id())).unwrap();
socket.join_multicast_v6(&multicast_ipv6, interface.scope_id().unwrap()).expect("Unable to join...");
// Receive packets!
let mut buffer = [0u8; 1000];
loop {
let (size, addr) = socket.recv_from(&mut buffer).unwrap();
println!("Size: {} => {}", size, String::from_utf8_lossy(&buffer[..size]))
}
}