diff --git a/Limelight/Database/TemporaryHost.h b/Limelight/Database/TemporaryHost.h
index f029427..5f8679b 100644
--- a/Limelight/Database/TemporaryHost.h
+++ b/Limelight/Database/TemporaryHost.h
@@ -20,6 +20,7 @@
@property (nonatomic, nullable, retain) NSString *address;
@property (nonatomic, nullable, retain) NSString *externalAddress;
@property (nonatomic, nullable, retain) NSString *localAddress;
+@property (nonatomic, nullable, retain) NSString *ipv6Address;
@property (nonatomic, nullable, retain) NSString *mac;
@property (nonatomic) int serverCodecModeSupport;
diff --git a/Limelight/Database/TemporaryHost.m b/Limelight/Database/TemporaryHost.m
index 765dd53..2a4e7e7 100644
--- a/Limelight/Database/TemporaryHost.m
+++ b/Limelight/Database/TemporaryHost.m
@@ -26,6 +26,7 @@
self.address = host.address;
self.externalAddress = host.externalAddress;
self.localAddress = host.localAddress;
+ self.ipv6Address = host.ipv6Address;
self.mac = host.mac;
self.name = host.name;
self.uuid = host.uuid;
@@ -60,6 +61,9 @@
if (self.localAddress != nil) {
parentHost.localAddress = self.localAddress;
}
+ if (self.ipv6Address != nil) {
+ parentHost.ipv6Address = self.ipv6Address;
+ }
if (self.mac != nil) {
parentHost.mac = self.mac;
}
diff --git a/Limelight/Limelight.xcdatamodeld/.xccurrentversion b/Limelight/Limelight.xcdatamodeld/.xccurrentversion
index fe08733..8055370 100644
--- a/Limelight/Limelight.xcdatamodeld/.xccurrentversion
+++ b/Limelight/Limelight.xcdatamodeld/.xccurrentversion
@@ -3,6 +3,6 @@
_XCCurrentVersionName
- Moonlight v1.3.xcdatamodel
+ Moonlight v1.4.xcdatamodel
diff --git a/Limelight/Limelight.xcdatamodeld/Moonlight v1.4.xcdatamodel/contents b/Limelight/Limelight.xcdatamodeld/Moonlight v1.4.xcdatamodel/contents
new file mode 100644
index 0000000..ce5077c
--- /dev/null
+++ b/Limelight/Limelight.xcdatamodeld/Moonlight v1.4.xcdatamodel/contents
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Limelight/Network/DiscoveryManager.m b/Limelight/Network/DiscoveryManager.m
index af25e95..51d8d5f 100644
--- a/Limelight/Network/DiscoveryManager.m
+++ b/Limelight/Network/DiscoveryManager.m
@@ -130,6 +130,9 @@
if (host.localAddress != nil) {
existingHost.localAddress = host.localAddress;
}
+ if (host.ipv6Address != nil) {
+ existingHost.ipv6Address = host.ipv6Address;
+ }
if (host.externalAddress != nil) {
existingHost.externalAddress = host.externalAddress;
}
diff --git a/Limelight/Network/DiscoveryWorker.m b/Limelight/Network/DiscoveryWorker.m
index 9095146..e4dc084 100644
--- a/Limelight/Network/DiscoveryWorker.m
+++ b/Limelight/Network/DiscoveryWorker.m
@@ -52,6 +52,9 @@ static const float POLL_RATE = 2.0f; // Poll every 2 seconds
if (_host.externalAddress != nil) {
[array addObject:_host.externalAddress];
}
+ if (_host.ipv6Address != nil) {
+ [array addObject:_host.ipv6Address];
+ }
// Remove duplicate addresses from the list.
// This is done using an array rather than a set
@@ -117,7 +120,7 @@ static const float POLL_RATE = 2.0f; // Poll every 2 seconds
_host.online = receivedResponse;
if (receivedResponse) {
- Log(LOG_D, @"Received response from: %@\n{\n\t address:%@ \n\t localAddress:%@ \n\t externalAddress:%@ \n\t uuid:%@ \n\t mac:%@ \n\t pairState:%d \n\t online:%d \n\t activeAddress:%@ \n}", _host.name, _host.address, _host.localAddress, _host.externalAddress, _host.uuid, _host.mac, _host.pairState, _host.online, _host.activeAddress);
+ Log(LOG_D, @"Received response from: %@\n{\n\t address:%@ \n\t localAddress:%@ \n\t externalAddress:%@ \n\t ipv6Address:%@ \n\t uuid:%@ \n\t mac:%@ \n\t pairState:%d \n\t online:%d \n\t activeAddress:%@ \n}", _host.name, _host.address, _host.localAddress, _host.externalAddress, _host.ipv6Address, _host.uuid, _host.mac, _host.pairState, _host.online, _host.activeAddress);
}
}
diff --git a/Limelight/Network/MDNSManager.m b/Limelight/Network/MDNSManager.m
index 07a3a72..9b30e9b 100644
--- a/Limelight/Network/MDNSManager.m
+++ b/Limelight/Network/MDNSManager.m
@@ -63,11 +63,137 @@ static NSString* NV_SERVICE_TYPE = @"_nvstream._tcp";
[mDNSBrowser stop];
}
++ (NSString*)sockAddrToString:(NSData*)addrData {
+ char addrStr[INET6_ADDRSTRLEN];
+ struct sockaddr* addr = (struct sockaddr*)[addrData bytes];
+ if (addr->sa_family == AF_INET) {
+ inet_ntop(addr->sa_family, &((struct sockaddr_in*)addr)->sin_addr, addrStr, sizeof(addrStr));
+ }
+ else {
+ inet_ntop(addr->sa_family, &((struct sockaddr_in6*)addr)->sin6_addr, addrStr, sizeof(addrStr));
+ }
+ return [NSString stringWithFormat: @"%s", addrStr];
+}
+
++ (BOOL)isAddress:(uint8_t*)address inSubnet:(uint8_t*)subnet netmask:(int)bits {
+ for (int i = 0; i < bits; i++) {
+ uint8_t mask = 1 << (i % 8);
+ if ((address[i / 8] & mask) != (subnet[i / 8] & mask)) {
+ return NO;
+ }
+ }
+ return YES;
+}
+
++ (BOOL)isLocalIpv6Address:(NSData*)addrData {
+ struct sockaddr_in6* sin6 = (struct sockaddr_in6*)[addrData bytes];
+ if (sin6->sin6_family != AF_INET6) {
+ return NO;
+ }
+
+ uint8_t* addrBytes = sin6->sin6_addr.s6_addr;
+ uint8_t prefix[2];
+
+ // fe80::/10
+ prefix[0] = 0xfe;
+ prefix[1] = 0x80;
+ if ([MDNSManager isAddress:addrBytes inSubnet:prefix netmask:10]) {
+ // Link-local
+ return YES;
+ }
+
+ // fec0::/10
+ prefix[0] = 0xfe;
+ prefix[1] = 0xc0;
+ if ([MDNSManager isAddress:addrBytes inSubnet:prefix netmask:10]) {
+ // Site local
+ return YES;
+ }
+
+ // fc00::/7
+ prefix[0] = 0xfc;
+ prefix[1] = 0x00;
+ if ([MDNSManager isAddress:addrBytes inSubnet:prefix netmask:7]) {
+ // ULA
+ return YES;
+ }
+
+ return NO;
+}
+
++ (NSString*)getBestIpv6Address:(NSArray*)addresses {
+ for (NSData* addrData in addresses) {
+ struct sockaddr_in6* sin6 = (struct sockaddr_in6*)[addrData bytes];
+ if (sin6->sin6_family != AF_INET6) {
+ continue;
+ }
+
+ if ([MDNSManager isLocalIpv6Address:addrData]) {
+ // Skip non-global addresses
+ continue;
+ }
+
+ uint8_t* addrBytes = sin6->sin6_addr.s6_addr;
+ uint8_t prefix[2];
+
+ // 2002::/16
+ prefix[0] = 0x20;
+ prefix[1] = 0x02;
+ if ([MDNSManager isAddress:addrBytes inSubnet:prefix netmask:16]) {
+ Log(LOG_I, @"Ignoring 6to4 address: %@", [MDNSManager sockAddrToString:addrData]);
+ continue;
+ }
+
+ // 2001::/32
+ prefix[0] = 0x20;
+ prefix[1] = 0x01;
+ if ([MDNSManager isAddress:addrBytes inSubnet:prefix netmask:32]) {
+ Log(LOG_I, @"Ignoring Teredo address: %@", [MDNSManager sockAddrToString:addrData]);
+ continue;
+ }
+
+ return [MDNSManager sockAddrToString:addrData];
+ }
+
+ return nil;
+}
+
- (void)netServiceDidResolveAddress:(NSNetService *)service {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
- Log(LOG_D, @"Resolved address: %@ -> %@", service, service.hostName);
+ NSArray* addresses = [service addresses];
+
+ for (NSData* addrData in addresses) {
+ Log(LOG_I, @"Resolved address: %@ -> %@", [service hostName], [MDNSManager sockAddrToString: addrData]);
+ }
+
TemporaryHost* host = [[TemporaryHost alloc] init];
+ // First, look for an IPv4 record for the local address
+ for (NSData* addrData in addresses) {
+ struct sockaddr_in* sin = (struct sockaddr_in*)[addrData bytes];
+ if (sin->sin_family != AF_INET) {
+ continue;
+ }
+
+ host.localAddress = [MDNSManager sockAddrToString:addrData];
+ Log(LOG_I, @"Local address chosen: %@ -> %@", [service hostName], host.localAddress);
+ break;
+ }
+
+ if (host.localAddress == nil) {
+ // If we didn't find an IPv4 record, look for a local IPv6 record
+ for (NSData* addrData in addresses) {
+ if ([MDNSManager isLocalIpv6Address:addrData]) {
+ host.localAddress = [MDNSManager sockAddrToString:addrData];
+ Log(LOG_I, @"Local address chosen: %@ -> %@", [service hostName], host.localAddress);
+ break;
+ }
+ }
+ }
+
+ host.ipv6Address = [MDNSManager getBestIpv6Address:addresses];
+ Log(LOG_I, @"IPv6 address chosen: %@ -> %@", [service hostName], host.ipv6Address);
+
// Since we discovered this host over mDNS, we know we're on the same network
// as the PC and we can use our current WAN address as a likely candidate
// for our PC's external address.
@@ -82,7 +208,7 @@ static NSString* NV_SERVICE_TYPE = @"_nvstream._tcp";
Log(LOG_E, @"STUN failed to get WAN address: %d", err);
}
- host.activeAddress = host.localAddress = service.hostName;
+ host.activeAddress = host.localAddress;
host.name = service.hostName;
[self.callback updateHost:host];
});
diff --git a/Limelight/Network/WakeOnLanManager.m b/Limelight/Network/WakeOnLanManager.m
index 91c7e67..afb2a72 100644
--- a/Limelight/Network/WakeOnLanManager.m
+++ b/Limelight/Network/WakeOnLanManager.m
@@ -33,7 +33,7 @@ static const int ports[numPorts] = {7, 9, 47998, 47999, 48000};
+ (void) wakeHost:(TemporaryHost*)host {
NSData* wolPayload = [WakeOnLanManager createPayload:host];
- for (int i = 0; i < 4; i++) {
+ for (int i = 0; i < 5; i++) {
const char* address;
struct addrinfo hints, *res, *curr;
@@ -44,7 +44,9 @@ static const int ports[numPorts] = {7, 9, 47998, 47999, 48000};
address = [host.externalAddress UTF8String];
} else if (i == 2 && host.address != nil) {
address = [host.address UTF8String];
- } else if (i == 3) {
+ } else if (i == 3 && host.ipv6Address != nil) {
+ address = [host.ipv6Address UTF8String];
+ } else if (i == 4) {
address = "255.255.255.255";
} else {
// Requested address wasn't present
diff --git a/Limelight/ViewControllers/MainFrameViewController.m b/Limelight/ViewControllers/MainFrameViewController.m
index 3f8a965..c15a700 100644
--- a/Limelight/ViewControllers/MainFrameViewController.m
+++ b/Limelight/ViewControllers/MainFrameViewController.m
@@ -861,6 +861,9 @@ static NSMutableSet* hostList;
if (host.activeAddress == nil) {
host.activeAddress = host.address;
}
+ if (host.activeAddress == nil) {
+ host.activeAddress = host.ipv6Address;
+ }
}
}
}
@@ -869,7 +872,7 @@ static NSMutableSet* hostList;
dispatch_async(dispatch_get_main_queue(), ^{
Log(LOG_D, @"New host list:");
for (TemporaryHost* host in hosts) {
- Log(LOG_D, @"Host: \n{\n\t name:%@ \n\t address:%@ \n\t localAddress:%@ \n\t externalAddress:%@ \n\t uuid:%@ \n\t mac:%@ \n\t pairState:%d \n\t online:%d \n\t activeAddress:%@ \n}", host.name, host.address, host.localAddress, host.externalAddress, host.uuid, host.mac, host.pairState, host.online, host.activeAddress);
+ Log(LOG_D, @"Host: \n{\n\t name:%@ \n\t address:%@ \n\t localAddress:%@ \n\t externalAddress:%@ \n\t ipv6Address:%@ \n\t uuid:%@ \n\t mac:%@ \n\t pairState:%d \n\t online:%d \n\t activeAddress:%@ \n}", host.name, host.address, host.localAddress, host.externalAddress, host.ipv6Address, host.uuid, host.mac, host.pairState, host.online, host.activeAddress);
}
@synchronized(hostList) {
[hostList removeAllObjects];
diff --git a/Moonlight.xcodeproj/project.pbxproj b/Moonlight.xcodeproj/project.pbxproj
index ba89ca0..b9aac21 100644
--- a/Moonlight.xcodeproj/project.pbxproj
+++ b/Moonlight.xcodeproj/project.pbxproj
@@ -166,6 +166,7 @@
9832D1341BBCD5C50036EF48 /* TemporaryApp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TemporaryApp.h; path = Database/TemporaryApp.h; sourceTree = ""; };
9832D1351BBCD5C50036EF48 /* TemporaryApp.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TemporaryApp.m; path = Database/TemporaryApp.m; sourceTree = ""; };
98517B1B21CE0A9000481377 /* Moonlight v1.3.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "Moonlight v1.3.xcdatamodel"; sourceTree = ""; };
+ 98608BDD22DC0C2C000E5672 /* Moonlight v1.4.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "Moonlight v1.4.xcdatamodel"; sourceTree = ""; };
9865DC3B2132922E0005B9B9 /* GameController.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = GameController.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS11.4.sdk/System/Library/Frameworks/GameController.framework; sourceTree = DEVELOPER_DIR; };
986CCE6C2133E45300168291 /* Moonlight v1.2.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "Moonlight v1.2.xcdatamodel"; sourceTree = ""; };
98878AE0206A226D00586E90 /* OSPortabilityDefs.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OSPortabilityDefs.h; sourceTree = ""; };
@@ -1509,6 +1510,7 @@
FB290D0519B2C406004C83CF /* Limelight.xcdatamodeld */ = {
isa = XCVersionGroup;
children = (
+ 98608BDD22DC0C2C000E5672 /* Moonlight v1.4.xcdatamodel */,
98517B1B21CE0A9000481377 /* Moonlight v1.3.xcdatamodel */,
986CCE6C2133E45300168291 /* Moonlight v1.2.xcdatamodel */,
98132E8C20BC9A62007A053F /* Moonlight v1.1.xcdatamodel */,
@@ -1518,7 +1520,7 @@
FB4678F21A51BDCB00377732 /* Limelight 0.3.0.xcdatamodel */,
FB290D0619B2C406004C83CF /* Limelight.xcdatamodel */,
);
- currentVersion = 98517B1B21CE0A9000481377 /* Moonlight v1.3.xcdatamodel */;
+ currentVersion = 98608BDD22DC0C2C000E5672 /* Moonlight v1.4.xcdatamodel */;
path = Limelight.xcdatamodeld;
sourceTree = "";
versionGroupType = wrapper.xcdatamodel;