From ceac7593990a42898fc5aa0044b6a65354c9aa51 Mon Sep 17 00:00:00 2001 From: Miles Aikens Date: Sun, 30 Dec 2018 00:46:44 -0500 Subject: [PATCH 1/3] Fix for #32 - correctly advertise IP Addresses for multiple network interfaces, without including loopback addresses. --- .../librespot/core/ZeroconfAuthenticator.java | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/core/src/main/java/xyz/gianlu/librespot/core/ZeroconfAuthenticator.java b/core/src/main/java/xyz/gianlu/librespot/core/ZeroconfAuthenticator.java index 8b5c9dcd..c5fd7988 100644 --- a/core/src/main/java/xyz/gianlu/librespot/core/ZeroconfAuthenticator.java +++ b/core/src/main/java/xyz/gianlu/librespot/core/ZeroconfAuthenticator.java @@ -20,10 +20,7 @@ import java.io.DataInputStream; import java.io.IOException; import java.io.OutputStream; -import java.net.InetAddress; -import java.net.ServerSocket; -import java.net.Socket; -import java.net.URLDecoder; +import java.net.*; import java.security.GeneralSecurityException; import java.security.MessageDigest; import java.util.*; @@ -72,12 +69,25 @@ public class ZeroconfAuthenticator implements Closeable { int port = session.random.nextInt((MAX_PORT - MIN_PORT) + 1) + MIN_PORT; new Thread(this.runner = new HttpRunner(port)).start(); - - ServiceInstance service = new ServiceInstance(new ServiceName("librespot._spotify-connect._tcp.local."), 0, 0, port, Name.fromString("local."), InetAddress.getAllByName("localhost"), "VERSION=1.0", "CPath=/"); + ArrayList addressArrayList = new ArrayList<>(); + Enumeration networkInterfaces= NetworkInterface.getNetworkInterfaces(); + while(networkInterfaces.hasMoreElements()){ + NetworkInterface networkInterface = networkInterfaces.nextElement(); + Enumeration interfaceAddresses = networkInterface.getInetAddresses(); + if(!networkInterface.isLoopback()) { + while (interfaceAddresses.hasMoreElements()) { + InetAddress address = interfaceAddresses.nextElement(); + if(!address.isLoopbackAddress()) + addressArrayList.add(address); + } + } + } + InetAddress[] addressArray = addressArrayList.toArray(new InetAddress[addressArrayList.size()]); + addressArrayList.add(InetAddress.getByName(InetAddress.getLocalHost().getCanonicalHostName())); + ServiceInstance service = new ServiceInstance(new ServiceName("librespot._spotify-connect._tcp.local."), 0, 0, port, Name.fromString("local."), addressArray, "VERSION=1.0", "CPath=/"); spotifyConnectService = mDnsService.register(service); if (spotifyConnectService == null) throw new IOException("Failed registering SpotifyConnect service!"); - LOGGER.info("SpotifyConnect service registered successfully!"); } @@ -297,4 +307,4 @@ public void close() throws IOException { serverSocket.close(); } } -} +} \ No newline at end of file From 24fe884751e8ef2dffb6e475c8887c25ef594bd3 Mon Sep 17 00:00:00 2001 From: Gianlu Date: Sun, 30 Dec 2018 10:51:41 +0100 Subject: [PATCH 2/3] Added some configuration options + clean up --- conf.properties | 7 +- .../gianlu/librespot/AbsConfiguration.java | 3 +- .../librespot/DefaultConfiguration.java | 10 +++ .../gianlu/librespot/FileConfiguration.java | 19 ++++++ .../xyz/gianlu/librespot/core/Session.java | 6 +- .../librespot/core/ZeroconfAuthenticator.java | 64 ++++++++++++++----- 6 files changed, 91 insertions(+), 18 deletions(-) diff --git a/conf.properties b/conf.properties index cbd837e5..3b4734b2 100644 --- a/conf.properties +++ b/conf.properties @@ -12,4 +12,9 @@ auth.password= ## Spotify authentication blob (BLOB) auth.blob= ## Cache enabled -cache.enabled=false \ No newline at end of file +cache.enabled=false +# Zeroconf +## Listen on all interfaces (overrides `zeroconf.interfaces`) +zeroconf.listenAll=true +## Listen on this interfaces (comma separated list of names) +zeroconf.interfaces= \ No newline at end of file diff --git a/core/src/main/java/xyz/gianlu/librespot/AbsConfiguration.java b/core/src/main/java/xyz/gianlu/librespot/AbsConfiguration.java index 775e6a14..5de8a763 100644 --- a/core/src/main/java/xyz/gianlu/librespot/AbsConfiguration.java +++ b/core/src/main/java/xyz/gianlu/librespot/AbsConfiguration.java @@ -3,13 +3,14 @@ import org.jetbrains.annotations.Nullable; import xyz.gianlu.librespot.core.AuthConfiguration; import xyz.gianlu.librespot.core.Session; +import xyz.gianlu.librespot.core.ZeroconfAuthenticator; import xyz.gianlu.librespot.player.CacheManager; import xyz.gianlu.librespot.player.Player; /** * @author Gianlu */ -public abstract class AbsConfiguration implements Player.PlayerConfiguration, CacheManager.CacheConfiguration, AuthConfiguration { +public abstract class AbsConfiguration implements Player.PlayerConfiguration, CacheManager.CacheConfiguration, AuthConfiguration, ZeroconfAuthenticator.Configuration { @Nullable public abstract String deviceName(); diff --git a/core/src/main/java/xyz/gianlu/librespot/DefaultConfiguration.java b/core/src/main/java/xyz/gianlu/librespot/DefaultConfiguration.java index 827f5841..c4be9a88 100644 --- a/core/src/main/java/xyz/gianlu/librespot/DefaultConfiguration.java +++ b/core/src/main/java/xyz/gianlu/librespot/DefaultConfiguration.java @@ -83,4 +83,14 @@ public Session.DeviceType deviceType() { public Strategy strategy() { return Strategy.ZEROCONF; } + + @Override + public boolean zeroconfListenAll() { + return true; + } + + @Override + public @NotNull String[] zeroconfInterfaces() { + return new String[0]; + } } diff --git a/core/src/main/java/xyz/gianlu/librespot/FileConfiguration.java b/core/src/main/java/xyz/gianlu/librespot/FileConfiguration.java index e8b3ec6d..8fd63abf 100644 --- a/core/src/main/java/xyz/gianlu/librespot/FileConfiguration.java +++ b/core/src/main/java/xyz/gianlu/librespot/FileConfiguration.java @@ -73,6 +73,14 @@ private > E getEnum(@NotNull Class clazz, @NotNull String k } } + @NotNull + private String[] getStringArray(@NotNull String key, @NotNull String[] fallback) { + String str = properties.getProperty(key, null); + if (str == null) return fallback; + else if ((str = str.trim()).isEmpty()) return new String[0]; + else return Utils.split(str, ','); + } + @Override public boolean cacheEnabled() { return getBoolean("cache.enabled", defaults.cacheEnabled()); @@ -133,4 +141,15 @@ public float normalisationPregain() { public Strategy strategy() { return getEnum(Strategy.class, "auth.strategy", defaults.strategy()); } + + @Override + public boolean zeroconfListenAll() { + return getBoolean("zeroconf.listenAll", defaults.zeroconfListenAll()); + } + + @NotNull + @Override + public String[] zeroconfInterfaces() { + return getStringArray("zeroconf.interfaces", defaults.zeroconfInterfaces()); + } } diff --git a/core/src/main/java/xyz/gianlu/librespot/core/Session.java b/core/src/main/java/xyz/gianlu/librespot/core/Session.java index 04711fc9..0a9a8d85 100644 --- a/core/src/main/java/xyz/gianlu/librespot/core/Session.java +++ b/core/src/main/java/xyz/gianlu/librespot/core/Session.java @@ -405,9 +405,12 @@ public static class Builder { private final Inner inner; private Authentication.LoginCredentials loginCredentials = null; private AuthConfiguration authConf; + private ZeroconfAuthenticator.Configuration zeroconfConf; public Builder(@NotNull DeviceType deviceType, @NotNull String deviceName, @NotNull AbsConfiguration configuration) { this.inner = new Inner(deviceType, deviceName, configuration); + this.authConf = configuration; + this.zeroconfConf = configuration; } public Builder(@NotNull AbsConfiguration configuration) { @@ -421,6 +424,7 @@ public Builder(@NotNull AbsConfiguration configuration) { this.inner = new Inner(deviceType, deviceName, configuration); this.authConf = configuration; + this.zeroconfConf = configuration; } public Builder facebook() throws IOException { @@ -433,7 +437,7 @@ public Builder facebook() throws IOException { } public Builder zeroconf() throws IOException { - try (ZeroconfAuthenticator authenticator = new ZeroconfAuthenticator(inner)) { + try (ZeroconfAuthenticator authenticator = new ZeroconfAuthenticator(inner, zeroconfConf)) { loginCredentials = authenticator.lockUntilCredentials(); return this; } catch (InterruptedException ex) { diff --git a/core/src/main/java/xyz/gianlu/librespot/core/ZeroconfAuthenticator.java b/core/src/main/java/xyz/gianlu/librespot/core/ZeroconfAuthenticator.java index c5fd7988..a025ee1c 100644 --- a/core/src/main/java/xyz/gianlu/librespot/core/ZeroconfAuthenticator.java +++ b/core/src/main/java/xyz/gianlu/librespot/core/ZeroconfAuthenticator.java @@ -62,35 +62,62 @@ public class ZeroconfAuthenticator implements Closeable { private final Session.Inner session; private final DiffieHellman keys; - ZeroconfAuthenticator(Session.Inner session) throws IOException { + ZeroconfAuthenticator(Session.Inner session, Configuration conf) throws IOException { this.session = session; this.keys = new DiffieHellman(session.random); this.mDnsService = new MulticastDNSService(); int port = session.random.nextInt((MAX_PORT - MIN_PORT) + 1) + MIN_PORT; new Thread(this.runner = new HttpRunner(port)).start(); - ArrayList addressArrayList = new ArrayList<>(); - Enumeration networkInterfaces= NetworkInterface.getNetworkInterfaces(); - while(networkInterfaces.hasMoreElements()){ - NetworkInterface networkInterface = networkInterfaces.nextElement(); - Enumeration interfaceAddresses = networkInterface.getInetAddresses(); - if(!networkInterface.isLoopback()) { - while (interfaceAddresses.hasMoreElements()) { - InetAddress address = interfaceAddresses.nextElement(); - if(!address.isLoopbackAddress()) - addressArrayList.add(address); - } + + InetAddress[] bound; + if (conf.zeroconfListenAll()) { + bound = getAllInterfacesAddresses(); + } else { + String[] interfaces = conf.zeroconfInterfaces(); + if (interfaces.length == 0) { + bound = new InetAddress[]{InetAddress.getLoopbackAddress()}; + } else { + List list = new ArrayList<>(); + for (String str : interfaces) addAddressForInterfaceName(list, str); + bound = list.toArray(new InetAddress[0]); } } - InetAddress[] addressArray = addressArrayList.toArray(new InetAddress[addressArrayList.size()]); - addressArrayList.add(InetAddress.getByName(InetAddress.getLocalHost().getCanonicalHostName())); - ServiceInstance service = new ServiceInstance(new ServiceName("librespot._spotify-connect._tcp.local."), 0, 0, port, Name.fromString("local."), addressArray, "VERSION=1.0", "CPath=/"); + + LOGGER.debug("Registering service on " + Arrays.toString(bound)); + + ServiceInstance service = new ServiceInstance(new ServiceName("librespot._spotify-connect._tcp.local."), 0, 0, port, Name.fromString("local."), bound, "VERSION=1.0", "CPath=/"); spotifyConnectService = mDnsService.register(service); if (spotifyConnectService == null) throw new IOException("Failed registering SpotifyConnect service!"); LOGGER.info("SpotifyConnect service registered successfully!"); } + private static void addAddressForInterfaceName(List list, @NotNull String name) throws SocketException { + NetworkInterface nif = NetworkInterface.getByName(name); + if (nif == null) { + LOGGER.warn(String.format("Interface %s doesn't exists.", name)); + return; + } + + addAddressOfInterface(list, nif); + } + + private static void addAddressOfInterface(List list, @NotNull NetworkInterface nif) { + LOGGER.trace(String.format("Adding addresses of %s (displayName: %s)", nif.getName(), nif.getDisplayName())); + Enumeration ias = nif.getInetAddresses(); + while (ias.hasMoreElements()) + list.add(ias.nextElement()); + } + + @NotNull + private static InetAddress[] getAllInterfacesAddresses() throws SocketException { + List list = new ArrayList<>(); + Enumeration is = NetworkInterface.getNetworkInterfaces(); + while (is.hasMoreElements()) addAddressOfInterface(list, is.nextElement()); + return list.toArray(new InetAddress[0]); + } + @Override public void close() throws IOException { mDnsService.unregister(spotifyConnectService); @@ -204,6 +231,13 @@ Authentication.LoginCredentials lockUntilCredentials() throws InterruptedExcepti } } + public interface Configuration { + boolean zeroconfListenAll(); + + @NotNull + String[] zeroconfInterfaces(); + } + private class HttpRunner implements Runnable, Closeable { private final ServerSocket serverSocket; private volatile boolean shouldStop = false; From 11fe79bbbc2023d86e819d317adcb665b18bfa7b Mon Sep 17 00:00:00 2001 From: Gianlu Date: Sun, 30 Dec 2018 11:12:04 +0100 Subject: [PATCH 3/3] Fixed Travis CI for pull request --- .travis.yml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 51a7de72..acba3f55 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,11 @@ language: java before_install: - - echo $GPG_SECRET_KEYS | base64 --decode | $GPG_EXECUTABLE --import - - echo $GPG_OWNERTRUST | base64 --decode | $GPG_EXECUTABLE --import-ownertrust + - | + if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then + echo $GPG_SECRET_KEYS | base64 --decode | $GPG_EXECUTABLE --import + echo $GPG_OWNERTRUST | base64 --decode | $GPG_EXECUTABLE --import-ownertrust + fi install: - mvn install -DskipTests=true -Dgpg.skip -Dmaven.javadoc.skip=true -B -V