From 4f03dd8c089296274ef6da14de5e2901d63d00fe Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Thu, 2 Feb 2023 21:54:52 -0600 Subject: [PATCH] Implement support for alternate ports with Sunshine Fixes #529 --- Limelight/Database/TemporaryHost.h | 1 + Limelight/Network/AppAssetRetriever.m | 2 +- Limelight/Network/ConnectionHelper.h | 4 +- Limelight/Network/ConnectionHelper.m | 4 +- Limelight/Network/DiscoveryManager.m | 2 +- Limelight/Network/DiscoveryWorker.m | 6 +- Limelight/Network/HttpManager.h | 4 +- Limelight/Network/HttpManager.m | 100 ++++++++++++++++-- Limelight/Network/MDNSManager.m | 9 +- Limelight/Network/ServerInfoResponse.h | 4 + Limelight/Network/ServerInfoResponse.m | 45 +++++++- Limelight/Network/WakeOnLanManager.m | 17 +-- Limelight/Stream/Connection.m | 3 +- Limelight/Stream/StreamConfiguration.h | 1 + Limelight/Stream/StreamConfiguration.m | 2 +- Limelight/Stream/StreamManager.m | 7 +- Limelight/Utility/Utils.h | 4 + Limelight/Utility/Utils.m | 67 ++++++++++++ .../ViewControllers/MainFrameViewController.m | 7 +- 19 files changed, 247 insertions(+), 42 deletions(-) diff --git a/Limelight/Database/TemporaryHost.h b/Limelight/Database/TemporaryHost.h index f3c39e33..76af87a7 100644 --- a/Limelight/Database/TemporaryHost.h +++ b/Limelight/Database/TemporaryHost.h @@ -15,6 +15,7 @@ @property (atomic) PairState pairState; @property (atomic, nullable, retain) NSString * activeAddress; @property (atomic, nullable, retain) NSString * currentGame; +@property (atomic) unsigned short httpsPort; @property (atomic, nullable, retain) NSData *serverCert; @property (atomic, nullable, retain) NSString *address; diff --git a/Limelight/Network/AppAssetRetriever.m b/Limelight/Network/AppAssetRetriever.m index f17aa5f9..75580dbb 100644 --- a/Limelight/Network/AppAssetRetriever.m +++ b/Limelight/Network/AppAssetRetriever.m @@ -20,7 +20,7 @@ static const int MAX_ATTEMPTS = 5; - (void) main { int attempts = 0; while (![self isCancelled] && attempts++ < MAX_ATTEMPTS) { - HttpManager* hMan = [[HttpManager alloc] initWithHost:_host.activeAddress uniqueId:[IdManager getUniqueId] serverCert:_host.serverCert]; + HttpManager* hMan = [[HttpManager alloc] initWithHost:_host]; AppAssetResponse* appAssetResp = [[AppAssetResponse alloc] init]; [hMan executeRequestSynchronously:[HttpRequest requestForResponse:appAssetResp withUrlRequest:[hMan newAppAssetRequestWithAppId:self.app.id]]]; diff --git a/Limelight/Network/ConnectionHelper.h b/Limelight/Network/ConnectionHelper.h index 52fa67b1..cd88599e 100644 --- a/Limelight/Network/ConnectionHelper.h +++ b/Limelight/Network/ConnectionHelper.h @@ -7,14 +7,14 @@ // #import "AppListResponse.h" +#import "TemporaryHost.h" #ifndef ConnectionHelper_h #define ConnectionHelper_h - @interface ConnectionHelper : NSObject -+(AppListResponse*) getAppListForHostWithHostIP:(NSString*) hostIP serverCert:(NSData*) serverCert uniqueID:(NSString*) uniqueId; ++(AppListResponse*) getAppListForHost:(TemporaryHost*)host; @end diff --git a/Limelight/Network/ConnectionHelper.m b/Limelight/Network/ConnectionHelper.m index e0c3e509..f312427d 100644 --- a/Limelight/Network/ConnectionHelper.m +++ b/Limelight/Network/ConnectionHelper.m @@ -14,8 +14,8 @@ @implementation ConnectionHelper -+(AppListResponse*) getAppListForHostWithHostIP:(NSString*) hostIP serverCert:(NSData*) cert uniqueID:(NSString*) uniqueId { - HttpManager* hMan = [[HttpManager alloc] initWithHost:hostIP uniqueId:uniqueId serverCert:cert]; ++(AppListResponse*) getAppListForHost:(TemporaryHost*)host { + HttpManager* hMan = [[HttpManager alloc] initWithHost:host]; // Try up to 5 times to get the app list AppListResponse* appListResp = nil; diff --git a/Limelight/Network/DiscoveryManager.m b/Limelight/Network/DiscoveryManager.m index e54824fc..28a7313e 100644 --- a/Limelight/Network/DiscoveryManager.m +++ b/Limelight/Network/DiscoveryManager.m @@ -120,7 +120,7 @@ } - (ServerInfoResponse*) getServerInfoResponseForAddress:(NSString*)address { - HttpManager* hMan = [[HttpManager alloc] initWithHost:address uniqueId:_uniqueId serverCert:nil]; + HttpManager* hMan = [[HttpManager alloc] initWithAddress:address httpsPort:0 serverCert:nil]; ServerInfoResponse* serverInfoResponse = [[ServerInfoResponse alloc] init]; [hMan executeRequestSynchronously:[HttpRequest requestForResponse:serverInfoResponse withUrlRequest:[hMan newServerInfoRequest:false] fallbackError:401 fallbackRequest:[hMan newHttpServerInfoRequest]]]; return serverInfoResponse; diff --git a/Limelight/Network/DiscoveryWorker.m b/Limelight/Network/DiscoveryWorker.m index c2f97888..a3c80aa7 100644 --- a/Limelight/Network/DiscoveryWorker.m +++ b/Limelight/Network/DiscoveryWorker.m @@ -103,8 +103,8 @@ static const float POLL_RATE = 2.0f; // Poll every 2 seconds ServerInfoResponse* serverInfoResp = [self requestInfoAtAddress:address cert:_host.serverCert]; receivedResponse = [self checkResponse:serverInfoResp]; if (receivedResponse) { - [serverInfoResp populateHost:_host]; _host.activeAddress = address; + [serverInfoResp populateHost:_host]; // Update the database using the response DataManager *dataManager = [[DataManager alloc] init]; @@ -126,9 +126,7 @@ static const float POLL_RATE = 2.0f; // Poll every 2 seconds } - (ServerInfoResponse*) requestInfoAtAddress:(NSString*)address cert:(NSData*)cert { - HttpManager* hMan = [[HttpManager alloc] initWithHost:address - uniqueId:_uniqueId - serverCert:cert]; + HttpManager* hMan = [[HttpManager alloc] initWithAddress:address httpsPort:0 serverCert:cert]; ServerInfoResponse* response = [[ServerInfoResponse alloc] init]; [hMan executeRequestSynchronously:[HttpRequest requestForResponse:response withUrlRequest:[hMan newServerInfoRequest:true] diff --git a/Limelight/Network/HttpManager.h b/Limelight/Network/HttpManager.h index 45415650..687f45c3 100644 --- a/Limelight/Network/HttpManager.h +++ b/Limelight/Network/HttpManager.h @@ -9,10 +9,12 @@ #import "HttpResponse.h" #import "HttpRequest.h" #import "StreamConfiguration.h" +#import "TemporaryHost.h" @interface HttpManager : NSObject -- (id) initWithHost:(NSString*) host uniqueId:(NSString*) uniqueId serverCert:(NSData*) serverCert; +- (id) initWithHost:(TemporaryHost*) host; +- (id) initWithAddress:(NSString*) hostAddressPortString httpsPort:(unsigned short) httpsPort serverCert:(NSData*) serverCert; - (void) setServerCert:(NSData*) serverCert; - (NSURLRequest*) newPairRequest:(NSData*)salt clientCert:(NSData*)clientCert; - (NSURLRequest*) newUnpairRequest; diff --git a/Limelight/Network/HttpManager.m b/Limelight/Network/HttpManager.m index 5336117a..6870f8d2 100644 --- a/Limelight/Network/HttpManager.m +++ b/Limelight/Network/HttpManager.m @@ -10,6 +10,7 @@ #import "HttpRequest.h" #import "CryptoManager.h" #import "TemporaryApp.h" +#import "ServerInfoResponse.h" #include #include @@ -23,8 +24,8 @@ @implementation HttpManager { NSURLSession* _urlSession; + NSString* _urlSafeHostName; NSString* _baseHTTPURL; - NSString* _baseHTTPSURL; NSString* _uniqueId; NSString* _deviceName; NSData* _serverCert; @@ -32,12 +33,12 @@ NSData* _requestResp; dispatch_semaphore_t _requestLock; + TemporaryHost *_host; // May be nil + NSString* _baseHTTPSURL; + NSError* _error; } -static const NSString* HTTP_PORT = @"47989"; -static const NSString* HTTPS_PORT = @"47984"; - + (NSData*) fixXmlVersion:(NSData*) xmlData { NSString* dataString = [[NSString alloc] initWithData:xmlData encoding:NSUTF8StringEncoding]; NSString* xmlString = [dataString stringByReplacingOccurrencesOfString:@"UTF-16" withString:@"UTF-8" options:NSCaseInsensitiveSearch range:NSMakeRange(0, [dataString length])]; @@ -49,7 +50,13 @@ static const NSString* HTTPS_PORT = @"47984"; _serverCert = serverCert; } -- (id) initWithHost:(NSString*) host uniqueId:(NSString*) uniqueId serverCert:(NSData*) serverCert { +- (id) initWithHost:(TemporaryHost*) host { + self = [self initWithAddress:host.activeAddress httpsPort:host.httpsPort serverCert:host.serverCert]; + _host = host; + return self; +} + +- (id) initWithAddress:(NSString*) hostAddressPortString httpsPort:(unsigned short)httpsPort serverCert:(NSData*) serverCert { self = [super init]; // Use the same UID for all Moonlight clients to allow them // quit games started on another Moonlight client. @@ -61,21 +68,64 @@ static const NSString* HTTPS_PORT = @"47984"; NSURLSessionConfiguration* config = [NSURLSessionConfiguration ephemeralSessionConfiguration]; _urlSession = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil]; + NSString* address = [Utils addressPortStringToAddress:hostAddressPortString]; + unsigned short port = [Utils addressPortStringToPort:hostAddressPortString]; + // If this is an IPv6 literal, we must properly enclose it in brackets - NSString* urlSafeHost; - if ([host containsString:@":"]) { - urlSafeHost = [NSString stringWithFormat:@"[%@]", host]; + if ([address containsString:@":"]) { + _urlSafeHostName = [NSString stringWithFormat:@"[%@]", address]; } else { - urlSafeHost = host; + _urlSafeHostName = address; } - _baseHTTPURL = [NSString stringWithFormat:@"http://%@:%@", urlSafeHost, HTTP_PORT]; - _baseHTTPSURL = [NSString stringWithFormat:@"https://%@:%@", urlSafeHost, HTTPS_PORT]; + _baseHTTPURL = [NSString stringWithFormat:@"http://%@:%u", _urlSafeHostName, port]; + + if (httpsPort) { + _baseHTTPSURL = [NSString stringWithFormat:@"https://%@:%u", _urlSafeHostName, httpsPort]; + } return self; } +- (BOOL) ensureHttpsUrlPopulated:(bool)fastFail { + if (!_baseHTTPSURL) { + // Use the caller's provided port if one was specified + if (_host && _host.httpsPort != 0) { + _baseHTTPSURL = [NSString stringWithFormat:@"https://%@:%u", _urlSafeHostName, _host.httpsPort]; + } + else { + // Query the host to retrieve the HTTPS port + ServerInfoResponse* serverInfoResponse = [[ServerInfoResponse alloc] init]; + [self executeRequestSynchronously:[HttpRequest requestForResponse:serverInfoResponse withUrlRequest:[self newHttpServerInfoRequest:false]]]; + TemporaryHost* dummyHost = [[TemporaryHost alloc] init]; + if (![serverInfoResponse isStatusOk]) { + return NO; + } + [serverInfoResponse populateHost:dummyHost]; + + // Pass the port back if the caller provided storage for it + if (_host) { + _host.httpsPort = dummyHost.httpsPort; + } + + _baseHTTPSURL = [NSString stringWithFormat:@"https://%@:%u", _urlSafeHostName, dummyHost.httpsPort]; + } + } + + return YES; +} + - (void) executeRequestSynchronously:(HttpRequest*)request { + // This is a special case to handle failure of HTTPS port fetching + if (!request.request) { + if (request.response) { + request.response.statusCode = EHOSTDOWN; + request.response.statusMessage = @"Host is unreachable"; + } + + return; + } + [_respData setLength:0]; _error = nil; @@ -174,11 +224,19 @@ static const NSString* HTTPS_PORT = @"47984"; } - (NSURLRequest*) newPairChallenge { + if (![self ensureHttpsUrlPopulated:NO]) { + return nil; + } + NSString* urlString = [NSString stringWithFormat:@"%@/pair?uniqueid=%@&devicename=%@&updateState=1&phrase=pairchallenge", _baseHTTPSURL, _uniqueId, _deviceName]; return [self createRequestFromString:urlString timeout:NORMAL_TIMEOUT_SEC]; } - (NSURLRequest *)newAppListRequest { + if (![self ensureHttpsUrlPopulated:NO]) { + return nil; + } + NSString* urlString = [NSString stringWithFormat:@"%@/applist?uniqueid=%@", _baseHTTPSURL, _uniqueId]; return [self createRequestFromString:urlString timeout:NORMAL_TIMEOUT_SEC]; } @@ -189,6 +247,10 @@ static const NSString* HTTPS_PORT = @"47984"; return [self newHttpServerInfoRequest:fastFail]; } + if (![self ensureHttpsUrlPopulated:fastFail]) { + return nil; + } + NSString* urlString = [NSString stringWithFormat:@"%@/serverinfo?uniqueid=%@", _baseHTTPSURL, _uniqueId]; return [self createRequestFromString:urlString timeout:(fastFail ? SHORT_TIMEOUT_SEC : NORMAL_TIMEOUT_SEC)]; } @@ -203,6 +265,10 @@ static const NSString* HTTPS_PORT = @"47984"; } - (NSURLRequest*) newLaunchRequest:(StreamConfiguration*)config { + if (![self ensureHttpsUrlPopulated:NO]) { + return nil; + } + // Using an FPS value over 60 causes SOPS to default to 720p60, // so force it to 0 to ensure the correct resolution is set. We // used to use 60 here but that locked the frame rate to 60 FPS @@ -225,6 +291,10 @@ static const NSString* HTTPS_PORT = @"47984"; } - (NSURLRequest*) newResumeRequest:(StreamConfiguration*)config { + if (![self ensureHttpsUrlPopulated:NO]) { + return nil; + } + NSString* urlString = [NSString stringWithFormat:@"%@/resume?uniqueid=%@&rikey=%@&rikeyid=%d&surroundAudioInfo=%d", _baseHTTPSURL, _uniqueId, [Utils bytesToHex:config.riKey], config.riKeyId, @@ -235,11 +305,19 @@ static const NSString* HTTPS_PORT = @"47984"; } - (NSURLRequest*) newQuitAppRequest { + if (![self ensureHttpsUrlPopulated:NO]) { + return nil; + } + NSString* urlString = [NSString stringWithFormat:@"%@/cancel?uniqueid=%@", _baseHTTPSURL, _uniqueId]; return [self createRequestFromString:urlString timeout:LONG_TIMEOUT_SEC]; } - (NSURLRequest*) newAppAssetRequestWithAppId:(NSString *)appId { + if (![self ensureHttpsUrlPopulated:NO]) { + return nil; + } + NSString* urlString = [NSString stringWithFormat:@"%@/appasset?uniqueid=%@&appid=%@&AssetType=2&AssetIdx=0", _baseHTTPSURL, _uniqueId, appId]; return [self createRequestFromString:urlString timeout:NORMAL_TIMEOUT_SEC]; } diff --git a/Limelight/Network/MDNSManager.m b/Limelight/Network/MDNSManager.m index 530f47ef..bf49c2a2 100644 --- a/Limelight/Network/MDNSManager.m +++ b/Limelight/Network/MDNSManager.m @@ -72,16 +72,21 @@ static NSString* NV_SERVICE_TYPE = @"_nvstream._tcp"; 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)); + unsigned short port = ntohs(((struct sockaddr_in*)addr)->sin_port); + return [NSString stringWithFormat: @"%s:%u", addrStr, port]; } else { struct sockaddr_in6* sin6 = (struct sockaddr_in6*)addr; inet_ntop(addr->sa_family, &sin6->sin6_addr, addrStr, sizeof(addrStr)); + unsigned short port = ntohs(((struct sockaddr_in6*)addr)->sin6_port); if (sin6->sin6_scope_id != 0) { // Link-local addresses with scope IDs are special - return [NSString stringWithFormat: @"%s%%%u", addrStr, sin6->sin6_scope_id]; + return [NSString stringWithFormat: @"[%s%%%u]:%u", addrStr, sin6->sin6_scope_id, port]; + } + else { + return [NSString stringWithFormat: @"[%s]:%u", addrStr, port]; } } - return [NSString stringWithFormat: @"%s", addrStr]; } + (BOOL)isAddress:(uint8_t*)address inSubnet:(uint8_t*)subnet netmask:(int)bits { diff --git a/Limelight/Network/ServerInfoResponse.h b/Limelight/Network/ServerInfoResponse.h index d9c0fc26..dab491cb 100644 --- a/Limelight/Network/ServerInfoResponse.h +++ b/Limelight/Network/ServerInfoResponse.h @@ -11,6 +11,7 @@ #define TAG_HOSTNAME @"hostname" #define TAG_EXTERNAL_IP @"ExternalIP" +#define TAG_HTTPS_PORT @"HttpsPort" #define TAG_LOCAL_IP @"LocalIP" #define TAG_UNIQUE_ID @"uniqueid" #define TAG_MAC_ADDRESS @"mac" @@ -18,6 +19,9 @@ #define TAG_STATE @"state" #define TAG_CURRENT_GAME @"currentgame" +// Sunshine extension +#define TAG_EXTERNAL_PORT @"ExternalPort" + @interface ServerInfoResponse : HttpResponse - (void) populateWithData:(NSData *)data; diff --git a/Limelight/Network/ServerInfoResponse.m b/Limelight/Network/ServerInfoResponse.m index 4b1e6aff..08f55953 100644 --- a/Limelight/Network/ServerInfoResponse.m +++ b/Limelight/Network/ServerInfoResponse.m @@ -23,17 +23,58 @@ host.mac = [[self getStringTag:TAG_MAC_ADDRESS] trim]; host.currentGame = [[self getStringTag:TAG_CURRENT_GAME] trim]; + NSInteger httpsPort; + if ([self getIntTag:TAG_HTTPS_PORT value:&httpsPort]) { + host.httpsPort = (unsigned short)httpsPort; + } + else { + // Use the default if it's not specified + host.httpsPort = 47984; + } + // We might get an IPv4 loopback address if we're using GS IPv6 Forwarder NSString *lanAddr = [[self getStringTag:TAG_LOCAL_IP] trim]; if (![lanAddr hasPrefix:@"127."]) { - host.localAddress = lanAddr; + unsigned short localPort; + + // If we reached this host through this port, store our port there + if (host.activeAddress && [lanAddr isEqualToString:[Utils addressPortStringToAddress:host.activeAddress]]) { + localPort = [Utils addressPortStringToPort:host.activeAddress]; + } + else if (host.localAddress) { + // If there's an existing local address, use the port from that + localPort = [Utils addressPortStringToPort:host.localAddress]; + } + else { + // If all else fails, use 47989 + localPort = 47989; + } + + host.localAddress = [Utils addressAndPortToAddressPortString:lanAddr port:localPort]; + } + + // This is a Sunshine extension for WAN port remapping + NSInteger externalHttpPort; + if (![self getIntTag:TAG_EXTERNAL_PORT value:&externalHttpPort]) { + // Use our active port if it's not specified + if (host.activeAddress) { + externalHttpPort = [Utils addressPortStringToPort:host.activeAddress]; + } + else { + // Otherwise use the default + externalHttpPort = 47989; + } } // Modern GFE versions don't actually give us a WAN address anymore // so we leave the one that we populated from mDNS discovery via STUN. NSString *wanAddr = [[self getStringTag:TAG_EXTERNAL_IP] trim]; if (wanAddr) { - host.externalAddress = wanAddr; + host.externalAddress = [Utils addressAndPortToAddressPortString:wanAddr port:externalHttpPort]; + } + else if (host.externalAddress) { + // If we have an external address (via STUN) already, we still need to populate the port + host.externalAddress = [Utils addressAndPortToAddressPortString:[Utils addressPortStringToAddress:host.externalAddress] port:externalHttpPort]; } NSString *state = [[self getStringTag:TAG_STATE] trim]; diff --git a/Limelight/Network/WakeOnLanManager.m b/Limelight/Network/WakeOnLanManager.m index 8801e84f..199e932e 100644 --- a/Limelight/Network/WakeOnLanManager.m +++ b/Limelight/Network/WakeOnLanManager.m @@ -38,29 +38,32 @@ static const int ports[numPorts] = { NSData* wolPayload = [WakeOnLanManager createPayload:host]; for (int i = 0; i < 5; i++) { - const char* address; + NSString* address; struct addrinfo hints, *res, *curr; // try all ip addresses if (i == 0 && host.localAddress != nil) { - address = [host.localAddress UTF8String]; + address = host.localAddress; } else if (i == 1 && host.externalAddress != nil) { - address = [host.externalAddress UTF8String]; + address = host.externalAddress; } else if (i == 2 && host.address != nil) { - address = [host.address UTF8String]; + address = host.address; } else if (i == 3 && host.ipv6Address != nil) { - address = [host.ipv6Address UTF8String]; + address = host.ipv6Address; } else if (i == 4) { - address = "255.255.255.255"; + address = @"255.255.255.255"; } else { // Requested address wasn't present continue; } + // Get the raw address from the address+port string + NSString* rawAddress = [Utils addressPortStringToAddress:address]; + memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_UNSPEC; hints.ai_flags = AI_ADDRCONFIG; - if (getaddrinfo(address, NULL, &hints, &res) != 0 || res == NULL) { + if (getaddrinfo([rawAddress UTF8String], NULL, &hints, &res) != 0 || res == NULL) { // Failed to resolve address Log(LOG_E, @"Failed to resolve WOL address"); continue; diff --git a/Limelight/Stream/Connection.m b/Limelight/Stream/Connection.m index 7cc84e3d..c49822f1 100644 --- a/Limelight/Stream/Connection.m +++ b/Limelight/Stream/Connection.m @@ -344,8 +344,9 @@ void ClSetHdrMode(bool enabled) videoStatsLock = [[NSLock alloc] init]; } + NSString *rawAddress = [Utils addressPortStringToAddress:config.host]; strncpy(_hostString, - [config.host cStringUsingEncoding:NSUTF8StringEncoding], + [rawAddress cStringUsingEncoding:NSUTF8StringEncoding], sizeof(_hostString)); strncpy(_appVersionString, [config.appVersion cStringUsingEncoding:NSUTF8StringEncoding], diff --git a/Limelight/Stream/StreamConfiguration.h b/Limelight/Stream/StreamConfiguration.h index ba3e533b..4cfa3942 100644 --- a/Limelight/Stream/StreamConfiguration.h +++ b/Limelight/Stream/StreamConfiguration.h @@ -9,6 +9,7 @@ @interface StreamConfiguration : NSObject @property NSString* host; +@property unsigned short httpsPort; @property NSString* appVersion; @property NSString* gfeVersion; @property NSString* appID; diff --git a/Limelight/Stream/StreamConfiguration.m b/Limelight/Stream/StreamConfiguration.m index 5a857450..2a57b7ef 100644 --- a/Limelight/Stream/StreamConfiguration.m +++ b/Limelight/Stream/StreamConfiguration.m @@ -9,5 +9,5 @@ #import "StreamConfiguration.h" @implementation StreamConfiguration -@synthesize host, appID, width, height, frameRate, bitRate, riKeyId, riKey, gamepadMask, appName, optimizeGameSettings, playAudioOnPC, swapABXYButtons, audioConfiguration, enableHdr, multiController, allowHevc, serverCert, rtspSessionUrl; +@synthesize host, httpsPort, appID, width, height, frameRate, bitRate, riKeyId, riKey, gamepadMask, appName, optimizeGameSettings, playAudioOnPC, swapABXYButtons, audioConfiguration, enableHdr, multiController, allowHevc, serverCert, rtspSessionUrl; @end diff --git a/Limelight/Stream/StreamManager.m b/Limelight/Stream/StreamManager.m index 18c7bad4..deeebd27 100644 --- a/Limelight/Stream/StreamManager.m +++ b/Limelight/Stream/StreamManager.m @@ -39,10 +39,8 @@ - (void)main { [CryptoManager generateKeyPairUsingSSL]; - NSString* uniqueId = [IdManager getUniqueId]; - HttpManager* hMan = [[HttpManager alloc] initWithHost:_config.host - uniqueId:uniqueId + HttpManager* hMan = [[HttpManager alloc] initWithAddress:_config.host httpsPort:_config.httpsPort serverCert:_config.serverCert]; ServerInfoResponse* serverInfoResp = [[ServerInfoResponse alloc] init]; @@ -67,7 +65,8 @@ return; } - if (_config.width > 4096 || _config.height > 4096) { + // Only perform this check on GFE (as indicated by MJOLNIR in state value) + if ((_config.width > 4096 || _config.height > 4096) && [serverState containsString:@"MJOLNIR"]) { // Pascal added support for 8K HEVC encoding support. Maxwell 2 could encode HEVC but only up to 4K. // We can't directly identify Pascal, but we can look for HEVC Main10 which was added in the same generation. NSString* codecSupport = [serverInfoResp getStringTag:@"ServerCodecModeSupport"]; diff --git a/Limelight/Utility/Utils.h b/Limelight/Utility/Utils.h index b2a6ebd2..5d761df8 100644 --- a/Limelight/Utility/Utils.h +++ b/Limelight/Utility/Utils.h @@ -27,6 +27,10 @@ FOUNDATION_EXPORT NSString *const deviceName; + (NSData*) hexToBytes:(NSString*) hex; + (void) addHelpOptionToDialog:(UIAlertController*)dialog; + (BOOL) isActiveNetworkVPN; ++ (BOOL) parseAddressPortString:(NSString*)addressPort address:(NSRange*)address port:(NSRange*)port; ++ (NSString*) addressPortStringToAddress:(NSString*)addressPort; ++ (unsigned short) addressPortStringToPort:(NSString*)addressPort; ++ (NSString*) addressAndPortToAddressPortString:(NSString*)address port:(unsigned short)port; @end diff --git a/Limelight/Utility/Utils.m b/Limelight/Utility/Utils.m index d0f19ec7..1960bb05 100644 --- a/Limelight/Utility/Utils.m +++ b/Limelight/Utility/Utils.m @@ -73,6 +73,73 @@ NSString *const deviceName = @"roth"; #endif } ++ (BOOL) parseAddressPortString:(NSString*)addressPort address:(NSRange*)address port:(NSRange*)port { + if (![addressPort containsString:@":"]) { + // If there's no port or IPv6 separator, the whole thing is an address + *address = NSMakeRange(0, [addressPort length]); + *port = NSMakeRange(NSNotFound, 0); + return TRUE; + } + + NSInteger locationOfOpeningBracket = [addressPort rangeOfString:@"["].location; + NSInteger locationOfClosingBracket = [addressPort rangeOfString:@"]"].location; + if (locationOfOpeningBracket != NSNotFound || locationOfClosingBracket != NSNotFound) { + // If we have brackets, it's an IPv6 address + if (locationOfOpeningBracket == NSNotFound || locationOfClosingBracket == NSNotFound || + locationOfClosingBracket < locationOfOpeningBracket) { + // Invalid address format + return FALSE; + } + + // Cut at the brackets + *address = NSMakeRange(locationOfOpeningBracket + 1, locationOfClosingBracket - locationOfOpeningBracket - 1); + } + else { + // It's an IPv4 address, so just cut at the port separator + *address = NSMakeRange(0, [addressPort rangeOfString:@":"].location); + } + + NSUInteger remainingStringLocation = address->location + address->length; + NSRange remainingStringRange = NSMakeRange(remainingStringLocation, [addressPort length] - remainingStringLocation); + NSInteger locationOfPortSeparator = [addressPort rangeOfString:@":" options:0 range:remainingStringRange].location; + if (locationOfPortSeparator != NSNotFound) { + *port = NSMakeRange(locationOfPortSeparator + 1, [addressPort length] - locationOfPortSeparator - 1); + } + else { + *port = NSMakeRange(NSNotFound, 0); + } + + return TRUE; +} + ++ (NSString*) addressPortStringToAddress:(NSString*)addressPort { + NSRange addressRange, portRange; + if (![self parseAddressPortString:addressPort address:&addressRange port:&portRange]) { + return nil; + } + + return [addressPort substringWithRange:addressRange]; +} + ++ (unsigned short) addressPortStringToPort:(NSString*)addressPort { + NSRange addressRange, portRange; + if (![self parseAddressPortString:addressPort address:&addressRange port:&portRange] || portRange.location == NSNotFound) { + return 47989; + } + + return [[addressPort substringWithRange:portRange] integerValue]; +} + ++ (NSString*) addressAndPortToAddressPortString:(NSString*)address port:(unsigned short)port { + if ([address containsString:@":"]) { + // IPv6 addresses require escaping + return [NSString stringWithFormat:@"[%@]:%u", address, port]; + } + else { + return [NSString stringWithFormat:@"%@:%u", address, port]; + } +} + @end @implementation NSString (NSStringWithTrim) diff --git a/Limelight/ViewControllers/MainFrameViewController.m b/Limelight/ViewControllers/MainFrameViewController.m index c1f1c067..e92fad2e 100644 --- a/Limelight/ViewControllers/MainFrameViewController.m +++ b/Limelight/ViewControllers/MainFrameViewController.m @@ -168,7 +168,7 @@ static NSMutableSet* hostList; // Exempt this host from discovery while handling the applist query [self->_discMan pauseDiscoveryForHost:host]; - AppListResponse* appListResp = [ConnectionHelper getAppListForHostWithHostIP:host.activeAddress serverCert:host.serverCert uniqueID:self->_uniqueId]; + AppListResponse* appListResp = [ConnectionHelper getAppListForHost:host]; [self->_discMan resumeDiscoveryForHost:host]; @@ -373,7 +373,7 @@ static NSMutableSet* hostList; return; } - HttpManager* hMan = [[HttpManager alloc] initWithHost:host.activeAddress uniqueId:self->_uniqueId serverCert:host.serverCert]; + HttpManager* hMan = [[HttpManager alloc] initWithHost:host]; ServerInfoResponse* serverInfoResp = [[ServerInfoResponse alloc] init]; // Exempt this host from discovery while handling the serverinfo request @@ -593,6 +593,7 @@ static NSMutableSet* hostList; - (void) prepareToStreamApp:(TemporaryApp *)app { _streamConfig = [[StreamConfiguration alloc] init]; _streamConfig.host = app.host.activeAddress; + _streamConfig.httpsPort = app.host.httpsPort; _streamConfig.appID = app.id; _streamConfig.appName = app.name; _streamConfig.serverCert = app.host.serverCert; @@ -718,7 +719,7 @@ static NSMutableSet* hostList; Log(LOG_I, @"Quitting application: %@", currentApp.name); [self showLoadingFrame: ^{ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ - HttpManager* hMan = [[HttpManager alloc] initWithHost:app.host.activeAddress uniqueId:self->_uniqueId serverCert:app.host.serverCert]; + HttpManager* hMan = [[HttpManager alloc] initWithHost:app.host]; HttpResponse* quitResponse = [[HttpResponse alloc] init]; HttpRequest* quitRequest = [HttpRequest requestForResponse: quitResponse withUrlRequest:[hMan newQuitAppRequest]];