From bf7cbbaadd96a29dc1cb34bc8ab3533f44e6fc86 Mon Sep 17 00:00:00 2001 From: setime Date: Fri, 25 Nov 2022 09:57:14 +0100 Subject: [PATCH] Add an option to specify IPs that zeroconf will bind to (#1071) * added an option to specify ip addresses to which mDNS should bind (ignored by `DNS-SD`) * changed command line option to `zeroconf-interface` to be consistent with `zeroconf-port` use builder pattern to DRY up the code used macro to print warning message * fixing register error * renamed `bind_ip` variables to match the option to `zeroconf_ip`, to be more consistent * Changed user help Modified comments Added block for condition to clean the code Added new modification to the change log Co-authored-by: setime --- CHANGELOG.md | 1 + discovery/src/lib.rs | 50 +++++++++++++++++++++++++++++++------------- src/main.rs | 36 +++++++++++++++++++++++++++++++ 3 files changed, 73 insertions(+), 14 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3f4dd956..c7ae5e98 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -68,6 +68,7 @@ https://github.com/librespot-org/librespot - [all] Check that array indexes are within bounds (panic safety) - [all] Wrap errors in librespot `Error` type (breaking) +- [connect] Add option on which zeroconf will bind. Defaults to all interfaces. Ignored by DNS-SD. - [connect] Add session events - [connect] Add `repeat`, `set_position_ms` and `set_volume` to `spirc.rs` - [contrib] Add `event_handler_example.py` diff --git a/discovery/src/lib.rs b/discovery/src/lib.rs index 3c01003b..1764640b 100644 --- a/discovery/src/lib.rs +++ b/discovery/src/lib.rs @@ -47,6 +47,7 @@ pub struct Discovery { pub struct Builder { server_config: server::Config, port: u16, + zeroconf_ip: Vec, } /// Errors that can occur while setting up a [`Discovery`] instance. @@ -87,6 +88,7 @@ impl Builder { client_id: client_id.into(), }, port: 0, + zeroconf_ip: vec![], } } @@ -102,6 +104,12 @@ impl Builder { self } + /// Set the ip addresses on which it should listen to incoming connections. The default is all interfaces. + pub fn zeroconf_ip(mut self, zeroconf_ip: Vec) -> Self { + self.zeroconf_ip = zeroconf_ip; + self + } + /// Sets the port on which it should listen to incoming connections. /// The default value `0` means any port. pub fn port(mut self, port: u16) -> Self { @@ -117,24 +125,38 @@ impl Builder { let mut port = self.port; let name = self.server_config.name.clone().into_owned(); let server = DiscoveryServer::new(self.server_config, &mut port)??; + let _zeroconf_ip = self.zeroconf_ip; + let svc; #[cfg(feature = "with-dns-sd")] - let svc = dns_sd::DNSService::register( - Some(name.as_ref()), - "_spotify-connect._tcp", - None, - None, - port, - &["VERSION=1.0", "CPath=/"], - )?; + { + svc = dns_sd::DNSService::register( + Some(name.as_ref()), + "_spotify-connect._tcp", + None, + None, + port, + &["VERSION=1.0", "CPath=/"], + )?; + } #[cfg(not(feature = "with-dns-sd"))] - let svc = libmdns::Responder::spawn(&tokio::runtime::Handle::current())?.register( - "_spotify-connect._tcp".to_owned(), - name, - port, - &["VERSION=1.0", "CPath=/"], - ); + { + let _svc = if !_zeroconf_ip.is_empty() { + libmdns::Responder::spawn_with_ip_list( + &tokio::runtime::Handle::current(), + _zeroconf_ip, + )? + } else { + libmdns::Responder::spawn(&tokio::runtime::Handle::current())? + }; + svc = _svc.register( + "_spotify-connect._tcp".to_owned(), + name, + port, + &["VERSION=1.0", "CPath=/"], + ); + } Ok(Discovery { server, _svc: svc }) } diff --git a/src/main.rs b/src/main.rs index 22a5fada..5bf56e0c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -185,6 +185,7 @@ struct Setup { zeroconf_port: u16, player_event_program: Option, emit_sink_events: bool, + zeroconf_ip: Vec, } fn get_setup() -> Setup { @@ -240,6 +241,7 @@ fn get_setup() -> Setup { const VOLUME_CTRL: &str = "volume-ctrl"; const VOLUME_RANGE: &str = "volume-range"; const ZEROCONF_PORT: &str = "zeroconf-port"; + const ZEROCONF_INTERFACE: &str = "zeroconf-interface"; // Mostly arbitrary. const AP_PORT_SHORT: &str = "a"; @@ -258,6 +260,7 @@ fn get_setup() -> Setup { const DISABLE_GAPLESS_SHORT: &str = "g"; const DISABLE_CREDENTIAL_CACHE_SHORT: &str = "H"; const HELP_SHORT: &str = "h"; + const ZEROCONF_INTERFACE_SHORT: &str = "i"; const CACHE_SIZE_LIMIT_SHORT: &str = "M"; const MIXER_TYPE_SHORT: &str = "m"; const ENABLE_VOLUME_NORMALISATION_SHORT: &str = "N"; @@ -570,6 +573,12 @@ fn get_setup() -> Setup { AUTOPLAY, "Explicitly set autoplay {on|off}. Defaults to following the client setting.", "OVERRIDE", + ) + .optopt( + ZEROCONF_INTERFACE_SHORT, + ZEROCONF_INTERFACE, + "Comma-separated interface IP addresses on which zeroconf will bind. Defaults to all interfaces. Ignored by DNS-SD.", + "IP" ); #[cfg(feature = "passthrough-decoder")] @@ -1168,6 +1177,31 @@ fn get_setup() -> Setup { None => SessionConfig::default().autoplay, }; + let zeroconf_ip: Vec = if opt_present(ZEROCONF_INTERFACE) { + if let Some(zeroconf_ip) = opt_str(ZEROCONF_INTERFACE) { + zeroconf_ip + .split(',') + .map(|s| { + s.trim().parse::().unwrap_or_else(|_| { + invalid_error_msg( + ZEROCONF_INTERFACE, + ZEROCONF_INTERFACE_SHORT, + s, + "IPv4 and IPv6 addresses", + "", + ); + exit(1); + }) + }) + .collect() + } else { + warn!("Unable to use zeroconf-interface option, default to all interfaces."); + vec![] + } + } else { + vec![] + }; + let connect_config = { let connect_default_config = ConnectConfig::default(); @@ -1608,6 +1642,7 @@ fn get_setup() -> Setup { zeroconf_port, player_event_program, emit_sink_events, + zeroconf_ip, } } @@ -1640,6 +1675,7 @@ async fn main() { .name(setup.connect_config.name.clone()) .device_type(setup.connect_config.device_type) .port(setup.zeroconf_port) + .zeroconf_ip(setup.zeroconf_ip) .launch() { Ok(d) => discovery = Some(d),