Add support for discovering IPv6 addresses with mDNS

This commit is contained in:
Cameron Gutman 2019-07-14 00:07:52 -07:00
parent 68040394fb
commit d00824d49c
3 changed files with 125 additions and 36 deletions

View File

@ -14,6 +14,7 @@ public class ComputerDetails {
public String localAddress;
public String remoteAddress;
public String manualAddress;
public String ipv6Address;
public String macAddress;
public X509Certificate serverCert;
@ -23,17 +24,17 @@ public class ComputerDetails {
public PairingManager.PairState pairState;
public int runningGameId;
public String rawAppList;
public ComputerDetails() {
// Use defaults
state = State.UNKNOWN;
}
public ComputerDetails(ComputerDetails details) {
// Copy details from the other computer
update(details);
}
public void update(ComputerDetails details) {
this.state = details.state;
this.name = details.name;
@ -50,6 +51,9 @@ public class ComputerDetails {
if (details.manualAddress != null) {
this.manualAddress = details.manualAddress;
}
if (details.ipv6Address != null) {
this.ipv6Address = details.ipv6Address;
}
if (details.macAddress != null && !details.macAddress.equals("00:00:00:00:00:00")) {
this.macAddress = details.macAddress;
}
@ -60,7 +64,7 @@ public class ComputerDetails {
this.runningGameId = details.runningGameId;
this.rawAppList = details.rawAppList;
}
@Override
public String toString() {
StringBuilder str = new StringBuilder();
@ -70,6 +74,7 @@ public class ComputerDetails {
str.append("UUID: ").append(uuid).append("\n");
str.append("Local Address: ").append(localAddress).append("\n");
str.append("Remote Address: ").append(remoteAddress).append("\n");
str.append("IPv6 Address: ").append(ipv6Address).append("\n");
str.append("Manual Address: ").append(manualAddress).append("\n");
str.append("MAC Address: ").append(macAddress).append("\n");
str.append("Pair State: ").append(pairState).append("\n");

View File

@ -1,44 +1,65 @@
package com.limelight.nvstream.mdns;
import java.net.Inet4Address;
import java.net.Inet6Address;
public class MdnsComputer {
private Inet4Address ipAddr;
private Inet4Address v4Addr;
private Inet6Address v6Addr;
private String name;
public MdnsComputer(String name, Inet4Address addr) {
public MdnsComputer(String name, Inet4Address v4Addr, Inet6Address v6Addr) {
this.name = name;
this.ipAddr = addr;
this.v4Addr = v4Addr;
this.v6Addr = v6Addr;
}
public String getName() {
return name;
}
public Inet4Address getAddress() {
return ipAddr;
public Inet4Address getAddressV4() {
return v4Addr;
}
public Inet6Address getAddressV6() {
return v6Addr;
}
@Override
public int hashCode() {
return name.hashCode();
}
@Override
public boolean equals(Object o) {
if (o instanceof MdnsComputer) {
MdnsComputer other = (MdnsComputer)o;
if (other.ipAddr.equals(ipAddr) &&
other.name.equals(name)) {
return true;
if (!other.name.equals(name)) {
return false;
}
if ((other.v4Addr != null && v4Addr == null) ||
(other.v4Addr == null && v4Addr != null) ||
(other.v4Addr != null && !other.v4Addr.equals(v4Addr))) {
return false;
}
if ((other.v6Addr != null && v6Addr == null) ||
(other.v6Addr == null && v6Addr != null) ||
(other.v6Addr != null && !other.v6Addr.equals(v6Addr))) {
return false;
}
return true;
}
return false;
}
@Override
public String toString() {
return "["+name+" - "+ipAddr+"]";
return "["+name+" - "+v4Addr+" - "+v6Addr+"]";
}
}

View File

@ -87,7 +87,6 @@ public class MdnsDiscoveryAgent implements ServiceListener {
public boolean useInetAddress(NetworkInterface networkInterface, InetAddress interfaceAddress) {
// This is an copy of jmDNS's implementation, except we omit the multicast check, since
// it seems at least some devices lie about interfaces not supporting multicast when they really do.
// We also will skip IPv6 addresses since GeForce Experience doesn't listen on IPv6 ports.
try {
if (!networkInterface.isUp()) {
return false;
@ -99,10 +98,6 @@ public class MdnsDiscoveryAgent implements ServiceListener {
}
*/
if (interfaceAddress instanceof Inet6Address) {
return false;
}
if (networkInterface.isLoopback()) {
return false;
}
@ -136,7 +131,7 @@ public class MdnsDiscoveryAgent implements ServiceListener {
return instance;
}
}
private static void dereferenceResolver() {
synchronized (MdnsDiscoveryAgent.class) {
if (--resolverRefCount == 0) {
@ -146,11 +141,11 @@ public class MdnsDiscoveryAgent implements ServiceListener {
}
}
}
public MdnsDiscoveryAgent(MdnsDiscoveryListener listener) {
this.listener = listener;
}
private void handleResolvedServiceInfo(ServiceInfo info) {
synchronized (pendingResolution) {
pendingResolution.remove(info.getName());
@ -164,17 +159,85 @@ public class MdnsDiscoveryAgent implements ServiceListener {
return;
}
}
private void handleServiceInfo(ServiceInfo info) throws UnsupportedEncodingException {
private Inet6Address getBestIpv6Address(Inet6Address[] addresses) {
// First try to find a link local address, so we can match the interface identifier
// with a global address (this will work for SLAAC but not DHCPv6).
Inet6Address linkLocalAddr = null;
for (Inet6Address addr : addresses) {
if (addr.isLinkLocalAddress()) {
LimeLog.info("Found link-local address: "+addr.getHostAddress());
linkLocalAddr = addr;
break;
}
}
// We will try once to match a SLAAC interface suffix, then
// pick the first matching address
for (int tries = 0; tries < 2; tries++) {
// We assume the addresses are already sorted in descending order
// of preference from Bonjour.
for (Inet6Address addr : addresses) {
if (addr.isLinkLocalAddress() || addr.isSiteLocalAddress() || addr.isLoopbackAddress()) {
// Link-local, site-local, and loopback aren't global
LimeLog.info("Ignoring non-global address: "+addr.getHostAddress());
continue;
}
byte[] addrBytes = addr.getAddress();
// 2002::/16
if (addrBytes[0] == 0x20 && addrBytes[1] == 0x02) {
// 6to4 has horrible performance
LimeLog.info("Ignoring 6to4 address: "+addr.getHostAddress());
continue;
}
// 2001::/32
else if (addrBytes[0] == 0x20 && addrBytes[1] == 0x01 && addrBytes[2] == 0x00 && addrBytes[3] == 0x00) {
// Teredo also has horrible performance
LimeLog.info("Ignoring Teredo address: "+addr.getHostAddress());
continue;
}
// Compare the final 64-bit interface identifier and skip the address
// if it doesn't match our link-local address.
if (linkLocalAddr != null && tries == 0) {
boolean matched = true;
for (int i = 8; i < 16; i++) {
if (linkLocalAddr.getAddress()[i] != addr.getAddress()[i]) {
matched = false;
break;
}
}
if (!matched) {
LimeLog.info("Ignoring non-matching global address: "+addr.getHostAddress());
continue;
}
}
return addr;
}
}
return null;
}
private void handleServiceInfo(ServiceInfo info) throws UnsupportedEncodingException {
Inet4Address addrs[] = info.getInet4Addresses();
LimeLog.info("mDNS: "+info.getName()+" has "+addrs.length+" addresses");
Inet6Address v6Addrs[] = info.getInet6Addresses();
LimeLog.info("mDNS: "+info.getName()+" has "+addrs.length+" IPv4 addresses");
LimeLog.info("mDNS: "+info.getName()+" has "+v6Addrs.length+" IPv6 addresses");
Inet6Address v6Addr = getBestIpv6Address(v6Addrs);
// Add a computer object for each IPv4 address reported by the PC
for (Inet4Address addr : addrs) {
for (Inet4Address v4Addr : addrs) {
synchronized (computers) {
MdnsComputer computer = new MdnsComputer(info.getName(), addr);
if (computers.put(computer.getAddress(), computer) == null) {
MdnsComputer computer = new MdnsComputer(info.getName(), v4Addr, v6Addr);
if (computers.put(computer.getAddressV4(), computer) == null) {
// This was a new entry
listener.notifyComputerAdded(computer);
}