Fix SSDP multicast on macOS Sequoia+

Corrige les problèmes de multicast SSDP sur macOS Sequoia+ en ajustant les interfaces multicast et en utilisant Terminal.app pour l'exécution des binaires en debug et release.

- Dans le Makefile : modifie les cibles 'run' et 'run-release' pour lancer l'application via Terminal.app, nécessaire pour le multicast sur macOS Sequoia+
- Dans pmoupnp/src/ssdp/client.rs : améliore la gestion des interfaces multicast en rejoignant le groupe sur toutes les interfaces non loopback et en configurant explicitement l'interface de sortie
- Dans pmoupnp/src/ssdp/server.rs : corrige la configuration multicast du serveur en maintenant l'interface de sortie correcte et en désactivant le multicast loopback
This commit is contained in:
2026-02-19 16:23:02 +01:00
parent 1e882ba6c3
commit 7276945b92
3 changed files with 55 additions and 35 deletions

View File

@@ -75,27 +75,42 @@ impl SsdpClient {
let bind_addr: SocketAddr = "0.0.0.0:0".parse().unwrap();
socket2.bind(&bind_addr.into())?;
// Le client SSDP n'a pas besoin de join_multicast_v4 :
// il envoie des M-SEARCH et reçoit des réponses unicast.
// Le join est seulement utile pour recevoir les NOTIFY multicast,
// ce dont le SsdpServer s'occupe.
//
// On configure uniquement l'interface de sortie multicast sur
// l'IP principale pour que send_to vers 239.255.255.250 fonctionne.
// Sur macOS, join_multicast_v4 sur des interfaces bridge/VM écrase
// IP_MULTICAST_IF et provoque EHOSTUNREACH (errno 65).
let socket: UdpSocket = socket2.into();
socket.set_read_timeout(Some(Duration::from_secs(1)))?;
socket.set_multicast_loop_v4(true)?; // utile en dev local
let multicast_addr = SSDP_MULTICAST_ADDR.parse().unwrap();
for iface in get_if_addrs::get_if_addrs()? {
if let std::net::IpAddr::V4(ipv4) = iface.ip() {
if !ipv4.is_loopback() {
match socket.join_multicast_v4(&multicast_addr, &ipv4) {
Ok(()) => {
debug!("SSDP: joined {} on {}", SSDP_MULTICAST_ADDR, ipv4);
}
Err(e) => {
warn!(
"SSDP: failed to join {} on {}: {}",
SSDP_MULTICAST_ADDR, ipv4, e
);
}
}
}
}
}
// Sur macOS, chaque join_multicast_v4 écrase IP_MULTICAST_IF.
// On remet explicitement l'interface de sortie sur l'IP principale
// pour que send_to vers 239.255.255.250 utilise la bonne interface.
let local_ip: std::net::Ipv4Addr = pmoutils::guess_local_ip()
.parse()
.unwrap_or("127.0.0.1".parse().unwrap());
.unwrap_or("0.0.0.0".parse().unwrap());
let socket2 = Socket::from(socket);
socket2.set_multicast_if_v4(&local_ip)?;
debug!(
"SSDP client: multicast outgoing interface set to {}",
local_ip
);
let socket: UdpSocket = socket2.into();
socket.set_read_timeout(Some(Duration::from_secs(1)))?;
socket.set_multicast_loop_v4(true)?; // utile en dev local
info!("✅ SSDP client ready on {}", addr);

View File

@@ -74,30 +74,33 @@ impl SsdpServer {
let bind_addr: SocketAddr = format!("0.0.0.0:{}", SSDP_PORT).parse().unwrap();
socket2.bind(&bind_addr.into())?;
// Configurer l'interface de sortie multicast sur l'IP principale.
// Sur macOS, sans cela le kernel peut router les paquets multicast
// via une interface bridge/VM qui n'a pas de route, causant
// EHOSTUNREACH (errno 65).
let local_ip: std::net::Ipv4Addr = pmoutils::guess_local_ip()
.parse()
.unwrap_or("127.0.0.1".parse().unwrap());
socket2.set_multicast_if_v4(&local_ip)?;
debug!(
"SSDP server: multicast outgoing interface set to {}",
local_ip
);
// Convertir en UdpSocket standard
let socket: UdpSocket = socket2.into();
let mut socket: UdpSocket = socket2.into();
// Rejoindre le groupe multicast via INADDR_ANY (l'OS choisit l'interface)
// Rejoindre le groupe multicast
socket.join_multicast_v4(
&SSDP_MULTICAST_ADDR.parse().unwrap(),
&"0.0.0.0".parse().unwrap(),
)?;
// Sur macOS, join_multicast_v4 peut positionner IP_MULTICAST_IF
// sur une interface bridge/VM. On remet explicitement l'interface
// de sortie sur l'IP principale.
let local_ip: std::net::Ipv4Addr = pmoutils::guess_local_ip()
.parse()
.unwrap_or("0.0.0.0".parse().unwrap());
{
let socket2 = Socket::from(socket);
socket2.set_multicast_if_v4(&local_ip)?;
debug!(
"SSDP server: multicast outgoing interface set to {}",
local_ip
);
socket = socket2.into();
}
socket.set_read_timeout(Some(Duration::from_secs(1)))?;
socket.set_multicast_loop_v4(true)?; // utile en dev local
socket.set_multicast_loop_v4(false)?;
let socket = Arc::new(socket);
self.socket = Some(socket.clone());