mirror of
https://github.com/moonlight-stream/moonlight-ios.git
synced 2026-02-16 02:20:53 +00:00
Add support for zero configuration IPv6 streaming
This commit is contained in:
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,6 @@
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>_XCCurrentVersionName</key>
|
||||
<string>Moonlight v1.3.xcdatamodel</string>
|
||||
<string>Moonlight v1.4.xcdatamodel</string>
|
||||
</dict>
|
||||
</plist>
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="14490.99" systemVersion="18F132" minimumToolsVersion="Xcode 7.3" sourceLanguage="Objective-C" userDefinedModelVersionIdentifier="">
|
||||
<entity name="App" representedClassName="App" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="hdrSupported" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
|
||||
<attribute name="id" attributeType="String" syncable="YES"/>
|
||||
<attribute name="name" attributeType="String" syncable="YES"/>
|
||||
<relationship name="host" maxCount="1" deletionRule="Nullify" destinationEntity="Host" inverseName="appList" inverseEntity="Host" syncable="YES"/>
|
||||
</entity>
|
||||
<entity name="Host" representedClassName="Host" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="address" optional="YES" attributeType="String" syncable="YES"/>
|
||||
<attribute name="externalAddress" optional="YES" attributeType="String" syncable="YES"/>
|
||||
<attribute name="ipv6Address" optional="YES" attributeType="String" syncable="YES"/>
|
||||
<attribute name="localAddress" optional="YES" attributeType="String" syncable="YES"/>
|
||||
<attribute name="mac" optional="YES" attributeType="String" syncable="YES"/>
|
||||
<attribute name="name" attributeType="String" syncable="YES"/>
|
||||
<attribute name="pairState" optional="YES" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="serverCert" optional="YES" attributeType="Binary" syncable="YES"/>
|
||||
<attribute name="serverCodecModeSupport" attributeType="Integer 32" defaultValueString="0" usesScalarValueType="YES" syncable="YES"/>
|
||||
<attribute name="uuid" optional="YES" attributeType="String" syncable="YES"/>
|
||||
<relationship name="appList" optional="YES" toMany="YES" deletionRule="Cascade" destinationEntity="App" inverseName="host" inverseEntity="App" syncable="YES"/>
|
||||
</entity>
|
||||
<entity name="Settings" representedClassName="Settings" syncable="YES" codeGenerationType="class">
|
||||
<attribute name="bitrate" attributeType="Integer 32" defaultValueString="10000" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="enableHdr" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
|
||||
<attribute name="framerate" attributeType="Integer 32" defaultValueString="60" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="height" attributeType="Integer 32" defaultValueString="720" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="multiController" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES" syncable="YES"/>
|
||||
<attribute name="onscreenControls" attributeType="Integer 32" defaultValueString="1" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="optimizeGames" attributeType="Boolean" defaultValueString="YES" usesScalarValueType="YES" syncable="YES"/>
|
||||
<attribute name="playAudioOnPC" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
|
||||
<attribute name="streamingRemotely" attributeType="Boolean" defaultValueString="0" usesScalarValueType="YES" syncable="YES"/>
|
||||
<attribute name="uniqueId" attributeType="String" syncable="YES"/>
|
||||
<attribute name="useHevc" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
|
||||
<attribute name="width" attributeType="Integer 32" defaultValueString="1280" usesScalarValueType="NO" syncable="YES"/>
|
||||
</entity>
|
||||
<elements>
|
||||
<element name="App" positionX="0" positionY="54" width="128" height="105"/>
|
||||
<element name="Host" positionX="0" positionY="0" width="128" height="210"/>
|
||||
<element name="Settings" positionX="0" positionY="0" width="128" height="225"/>
|
||||
</elements>
|
||||
</model>
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<NSData*>*)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<NSData*>* 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];
|
||||
});
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -166,6 +166,7 @@
|
||||
9832D1341BBCD5C50036EF48 /* TemporaryApp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TemporaryApp.h; path = Database/TemporaryApp.h; sourceTree = "<group>"; };
|
||||
9832D1351BBCD5C50036EF48 /* TemporaryApp.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = TemporaryApp.m; path = Database/TemporaryApp.m; sourceTree = "<group>"; };
|
||||
98517B1B21CE0A9000481377 /* Moonlight v1.3.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "Moonlight v1.3.xcdatamodel"; sourceTree = "<group>"; };
|
||||
98608BDD22DC0C2C000E5672 /* Moonlight v1.4.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = "Moonlight v1.4.xcdatamodel"; sourceTree = "<group>"; };
|
||||
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 = "<group>"; };
|
||||
98878AE0206A226D00586E90 /* OSPortabilityDefs.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OSPortabilityDefs.h; sourceTree = "<group>"; };
|
||||
@@ -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 = "<group>";
|
||||
versionGroupType = wrapper.xcdatamodel;
|
||||
|
||||
Reference in New Issue
Block a user