Reputation: 150
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
Upvotes: 0
Views: 458
Reputation: 150
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]))
}
}
Upvotes: 1