From 9b5dd5f9454348cefd344a8bba51bc4786293e8d Mon Sep 17 00:00:00 2001 From: shanemeagher Date: Fri, 9 Jun 2017 22:43:54 +0800 Subject: [PATCH] Allow avahi support using dns-sd support for Discovery rust-mdns is still the default but can also be specified explicitly with --feature "with-internal-mdns" switch at build time. Added --feature "with-external-mdns" switch to build librespot to use avahi for discovery using dns-sd package. This commit does not provide option for building without mdns. --- .gitignore | 2 + Cargo.lock | 12 +++ Cargo.toml | 9 +- README.md | 5 ++ contrib/Dockerfile | 3 + contrib/docker-build-avahi.sh | 24 ++++++ src/discovery.rs | 153 ++++++++++++++++++++++++++-------- src/lib.rs | 12 ++- 8 files changed, 184 insertions(+), 36 deletions(-) create mode 100755 contrib/docker-build-avahi.sh diff --git a/.gitignore b/.gitignore index 50242a6a..6eb80511 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ target .cargo spotify_appkey.key .vagrant/ +.settings/ +.project diff --git a/Cargo.lock b/Cargo.lock index d8128db1..576857ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -121,6 +121,15 @@ dependencies = [ "quick-error 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "dns-sd" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.29 (registry+https://github.com/rust-lang/crates.io-index)", + "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "dtoa" version = "0.4.1" @@ -267,6 +276,8 @@ version = "0.1.0" dependencies = [ "alsa 0.0.1 (git+https://github.com/plietar/rust-alsa)", "base64 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "dns-sd 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", "futures 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)", "getopts 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1075,6 +1086,7 @@ dependencies = [ "checksum conv 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "78ff10625fd0ac447827aa30ea8b861fead473bb60aeb73af6c1c58caf0d1299" "checksum custom_derive 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "ef8ae57c4978a2acd8b869ce6b9ca1dfe817bff704c220209fdef2c0b75a01b9" "checksum dns-parser 0.3.2 (git+https://github.com/plietar/dns-parser)" = "" +"checksum dns-sd 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d748509dea20228f63ba519bf142ce2593396386125b01f5b0d6412dab972087" "checksum dtoa 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "80c8b71fd71146990a9742fc06dcbbde19161a267e0ad4e572c35162f4578c90" "checksum env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3ddf21e73e016298f5cb37d6ef8e8da8e39f91f9ec8b0df44b7deb16a9f8cd5b" "checksum error-chain 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e92ecf0a508c8e074c0e6fa8fe0fa38414848ad4dfc4db6f74c5e9753330b248" diff --git a/Cargo.toml b/Cargo.toml index f4e63498..447d9a4b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,12 +31,14 @@ path = "protocol" [dependencies] base64 = "0.5.0" +cfg-if = "0.1.2" +dns-sd = { version = "~0.1.3", optional = true } env_logger = "0.4.0" futures = "0.1.8" getopts = "0.2.14" hyper = "0.11.2" log = "0.3.5" -mdns = { git = "https://github.com/plietar/rust-mdns" } +mdns = { git = "https://github.com/plietar/rust-mdns", optional = true } num-bigint = "0.1.35" protobuf = "1.1" rand = "0.3.13" @@ -66,7 +68,10 @@ pulseaudio-backend = ["libpulse-sys"] with-tremor = ["librespot-audio/with-tremor"] with-lewton = ["librespot-audio/with-lewton"] -default = ["portaudio-backend"] +with-internal-mdns = ["mdns"] +with-external-mdns = ["dns-sd"] + +default = ["portaudio-backend", "with-internal-mdns"] [package.metadata.deb] maintainer = "nobody" diff --git a/README.md b/README.md index 999a27cf..41f4a477 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,11 @@ The following backends are currently available : - PortAudio - PulseAudio +## mdns - Internal or External +*librespot* allows for the use of external avahi mdns. Internal or External mdns can be specified at compile time using +`--feature "with-internal-mdns"` or `--feature "with-external-mdns"` respectively. The internal mdns is used by default if none is specified. Currently, it is not possible to build without mdns. +`libavahi-compat-libdnssd-dev` must be installed to compile with-external-mdns. + ## Cross-compiling A cross compilation environment is provided as a docker image. Build the image from the root of the project with the following command : diff --git a/contrib/Dockerfile b/contrib/Dockerfile index 68a39b72..f6aec140 100644 --- a/contrib/Dockerfile +++ b/contrib/Dockerfile @@ -4,6 +4,8 @@ # # The resulting image can be used to build librespot for linux x86_64, armhf and armel. # $ docker run -v /tmp/librespot-build:/build librespot-cross +# To build librespot with avahi support +# $ docker run -v /tmp/librespot-build:/build librespot-cross /src/contrib/docker-build-avahi.sh # # The compiled binaries will be located in /tmp/librespot-build # @@ -23,6 +25,7 @@ RUN apt-get update RUN apt-get install -y curl git build-essential crossbuild-essential-arm64 crossbuild-essential-armel crossbuild-essential-armhf crossbuild-essential-mipsel RUN apt-get install -y libasound2-dev libasound2-dev:arm64 libasound2-dev:armel libasound2-dev:armhf libasound2-dev:mipsel +RUN apt-get install -y libavahi-compat-libdnssd-dev libavahi-compat-libdnssd-dev:arm64 libavahi-compat-libdnssd-dev:armel libavahi-compat-libdnssd-dev:armhf libavahi-compat-libdnssd-dev:mipsel RUN curl https://sh.rustup.rs -sSf | sh -s -- -y ENV PATH="/root/.cargo/bin/:${PATH}" diff --git a/contrib/docker-build-avahi.sh b/contrib/docker-build-avahi.sh new file mode 100755 index 00000000..d171997c --- /dev/null +++ b/contrib/docker-build-avahi.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +set -eux + +cargo build --release --no-default-features --features "alsa-backend with-external-mdns" +cp /usr/lib/x86_64-linux-gnu/libdns_sd.so.1 /build/release + +export PKG_CONFIG_ALLOW_CROSS=0 + +export PKG_CONFIG_PATH=/usr/lib/aarch64-unknown-linux-gnu/pkgconfig +cargo build --release --target aarch64-unknown-linux-gnu --no-default-features --features "alsa-backend with-external-mdns" +cp /usr/lib/aarch64-linux-gnu/libdns_sd.so.1 /build/aarch64-unknown-linux-gnu/release + +export PKG_CONFIG_PATH=/usr/lib/arm-linux-gnueabi/pkgconfig +cargo build --release --target arm-unknown-linux-gnueabi --no-default-features --features "alsa-backend with-external-mdns" +cp /usr/lib/arm-linux-gnueabi/libdns_sd.so.1 /build/arm-unknown-linux-gnueabi/release + +export PKG_CONFIG_PATH=/usr/lib/arm-linux-gnueabihf/pkgconfig +cargo build --release --target arm-unknown-linux-gnueabihf --no-default-features --features "alsa-backend with-external-mdns" +cp /usr/lib/arm-linux-gnueabihf/libdns_sd.so.1 /build/arm-unknown-linux-gnueabihf/release + +export PKG_CONFIG_PATH=/usr/lib/mipsel-linux-gnu/pkgconfig +cargo build --release --target mipsel-unknown-linux-gnu --no-default-features --features "alsa-backend with-external-mdns" +cp /usr/libmipsel-linux-gnu/libdns_sd.so.1 /build/mipsel-unknown-linux-gnu/release + diff --git a/src/discovery.rs b/src/discovery.rs index 3eaa5f0a..31b47a38 100644 --- a/src/discovery.rs +++ b/src/discovery.rs @@ -6,7 +6,6 @@ use futures::sync::mpsc; use futures::{Future, Stream, BoxFuture, Poll, Async}; use hyper::server::{Service, NewService, Request, Response, Http}; use hyper::{self, Get, Post, StatusCode}; -use mdns; use num_bigint::BigUint; use rand; use std::collections::BTreeMap; @@ -21,6 +20,16 @@ use core::authentication::Credentials; use core::util; use core::config::ConnectConfig; +cfg_if! { + if #[cfg(feature = "with-internal-mdns")] { + use mdns; + } else if #[cfg(feature = "with-external-mdns")] { + use dns_sd::DNSService; + } else { + use mdns; + } +} + #[derive(Clone)] struct Discovery(Arc); struct DiscoveryInner { @@ -201,39 +210,117 @@ impl NewService for Discovery { } } -pub struct DiscoveryStream { - credentials: mpsc::UnboundedReceiver, - _svc: mdns::Service, - task: Box>, +cfg_if! { + if #[cfg(feature = "with-internal-mdns")] { + pub struct DiscoveryStream { + credentials: mpsc::UnboundedReceiver, + _svc: mdns::Service, + task: Box>, + } + } else if #[cfg(feature = "with-external-mdns")] { + pub struct DiscoveryStream { + credentials: mpsc::UnboundedReceiver, + _svc: DNSService, + task: Box>, + } + } else { + pub struct DiscoveryStream { + credentials: mpsc::UnboundedReceiver, + _svc: mdns::Service, + task: Box>, + } + } } -pub fn discovery(handle: &Handle, config: ConnectConfig, device_id: String) - -> io::Result -{ - let (discovery, creds_rx) = Discovery::new(config.clone(), device_id); - - let listener = TcpListener::bind(&"0.0.0.0:0".parse().unwrap(), handle)?; - let addr = listener.local_addr()?; - - let http = Http::new(); - let handle_ = handle.clone(); - let task = Box::new(listener.incoming().for_each(move |(socket, addr)| { - http.bind_connection(&handle_, socket, addr, discovery.clone()); - Ok(()) - })); - - let responder = mdns::Responder::spawn(&handle)?; - let svc = responder.register( - "_spotify-connect._tcp".to_owned(), - config.name, - addr.port(), - &["VERSION=1.0", "CPath=/"]); - - Ok(DiscoveryStream { - credentials: creds_rx, - _svc: svc, - task: task, - }) +cfg_if! { + if #[cfg(feature = "with-internal-mdns")] { + pub fn discovery(handle: &Handle, config: ConnectConfig, device_id: String) + -> io::Result + { + let (discovery, creds_rx) = Discovery::new(config.clone(), device_id); + + let listener = TcpListener::bind(&"0.0.0.0:0".parse().unwrap(), handle)?; + let addr = listener.local_addr()?; + + let http = Http::new(); + let handle_ = handle.clone(); + let task = Box::new(listener.incoming().for_each(move |(socket, addr)| { + http.bind_connection(&handle_, socket, addr, discovery.clone()); + Ok(()) + })); + + let responder = mdns::Responder::spawn(&handle)?; + let svc = responder.register( + "_spotify-connect._tcp".to_owned(), + config.name, + addr.port(), + &["VERSION=1.0", "CPath=/"]); + + Ok(DiscoveryStream { + credentials: creds_rx, + _svc: svc, + task: task, + }) + } + } else if #[cfg(feature = "with-external-mdns")] { + pub fn discovery(handle: &Handle, config: ConnectConfig, device_id: String) + -> io::Result + { + let (discovery, creds_rx) = Discovery::new(config.clone(), device_id); + + let listener = TcpListener::bind(&"0.0.0.0:0".parse().unwrap(), handle)?; + let port = listener.local_addr().unwrap().port(); + + let http = Http::new(); + let handle_ = handle.clone(); + let task = Box::new(listener.incoming().for_each(move |(socket, addr)| { + http.bind_connection(&handle_, socket, addr, discovery.clone()); + Ok(()) + })); + + let svc = DNSService::register(Some(&*config.name), + "_spotify-connect._tcp", + None, + None, + port, + &["VERSION=1.0", "CPath=/"]).unwrap(); + + Ok(DiscoveryStream { + credentials: creds_rx, + _svc: svc, + task: task, + }) + } + } else { + pub fn discovery(handle: &Handle, config: ConnectConfig, device_id: String) + -> io::Result + { + let (discovery, creds_rx) = Discovery::new(config.clone(), device_id); + + let listener = TcpListener::bind(&"0.0.0.0:0".parse().unwrap(), handle)?; + let addr = listener.local_addr()?; + + let http = Http::new(); + let handle_ = handle.clone(); + let task = Box::new(listener.incoming().for_each(move |(socket, addr)| { + http.bind_connection(&handle_, socket, addr, discovery.clone()); + Ok(()) + })); + + let responder = mdns::Responder::spawn(&handle)?; + let svc = responder.register( + "_spotify-connect._tcp".to_owned(), + config.name, + addr.port(), + &["VERSION=1.0", "CPath=/"]); + + Ok(DiscoveryStream { + credentials: creds_rx, + _svc: svc, + task: task, + }) + } + } } impl Stream for DiscoveryStream { @@ -248,4 +335,4 @@ impl Stream for DiscoveryStream { Ok(self.credentials.poll().unwrap()) } -} +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index b9c920ec..07efa516 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,6 +5,7 @@ // TODO: many items from tokio-core::io have been deprecated in favour of tokio-io #![allow(deprecated)] +#[macro_use] extern crate cfg_if; #[macro_use] extern crate log; #[macro_use] extern crate serde_json; #[macro_use] extern crate serde_derive; @@ -13,7 +14,6 @@ extern crate base64; extern crate crypto; extern crate futures; extern crate hyper; -extern crate mdns; extern crate num_bigint; extern crate protobuf; extern crate rand; @@ -34,6 +34,16 @@ extern crate portaudio_rs; #[cfg(feature = "libpulse-sys")] extern crate libpulse_sys; +cfg_if! { + if #[cfg(feature = "with-internal-mdns")] { + extern crate mdns; + } else if #[cfg(feature = "with-external-mdns")] { + extern crate dns_sd; + } else { + extern crate mdns; + } +} + pub mod audio_backend; pub mod discovery; pub mod keymaster;