mirror of
https://github.com/moonlight-stream/moonlight-ios.git
synced 2025-07-27 06:32:59 +00:00
250 lines
11 KiB
Objective-C
250 lines
11 KiB
Objective-C
//
|
|
// HttpManager.m
|
|
// Moonlight
|
|
//
|
|
// Created by Diego Waxemberg on 10/16/14.
|
|
// Copyright (c) 2014 Moonlight Stream. All rights reserved.
|
|
//
|
|
|
|
#import "HttpManager.h"
|
|
#import "HttpRequest.h"
|
|
#import "CryptoManager.h"
|
|
#import "App.h"
|
|
|
|
#include <libxml2/libxml/xmlreader.h>
|
|
#include <string.h>
|
|
|
|
@implementation HttpManager {
|
|
NSURLSession* _urlSession;
|
|
NSString* _baseHTTPURL;
|
|
NSString* _baseHTTPSURL;
|
|
NSString* _host;
|
|
NSString* _uniqueId;
|
|
NSString* _deviceName;
|
|
NSData* _cert;
|
|
NSMutableData* _respData;
|
|
NSData* _requestResp;
|
|
dispatch_semaphore_t _requestLock;
|
|
|
|
BOOL _errorOccurred;
|
|
}
|
|
|
|
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])];
|
|
|
|
return [xmlString dataUsingEncoding:NSUTF8StringEncoding];
|
|
}
|
|
|
|
- (id) initWithHost:(NSString*) host uniqueId:(NSString*) uniqueId deviceName:(NSString*) deviceName cert:(NSData*) cert {
|
|
self = [super init];
|
|
_host = host;
|
|
_uniqueId = uniqueId;
|
|
_deviceName = deviceName;
|
|
_cert = cert;
|
|
_baseHTTPURL = [NSString stringWithFormat:@"http://%@:%@", host, HTTP_PORT];
|
|
_baseHTTPSURL = [NSString stringWithFormat:@"https://%@:%@", host, HTTPS_PORT];
|
|
_requestLock = dispatch_semaphore_create(0);
|
|
_respData = [[NSMutableData alloc] init];
|
|
NSURLSessionConfiguration* config = [NSURLSessionConfiguration ephemeralSessionConfiguration];
|
|
_urlSession = [NSURLSession sessionWithConfiguration:config delegate:self delegateQueue:nil];
|
|
return self;
|
|
}
|
|
|
|
- (void) executeRequestSynchronously:(HttpRequest*)request {
|
|
Log(LOG_D, @"Making Request: %@", request);
|
|
[_respData setLength:0];
|
|
[[_urlSession dataTaskWithRequest:request.request completionHandler:^(NSData * __nullable data, NSURLResponse * __nullable response, NSError * __nullable error) {
|
|
|
|
if (error != NULL) {
|
|
Log(LOG_D, @"Connection error: %@", error);
|
|
_errorOccurred = true;
|
|
}
|
|
else {
|
|
Log(LOG_D, @"Received response: %@", response);
|
|
|
|
if (data != NULL) {
|
|
Log(LOG_D, @"\n\nReceived data: %@\n\n", [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]);
|
|
[_respData appendData:data];
|
|
if ([[NSString alloc] initWithData:_respData encoding:NSUTF8StringEncoding] != nil) {
|
|
_requestResp = [HttpManager fixXmlVersion:_respData];
|
|
} else {
|
|
_requestResp = _respData;
|
|
}
|
|
}
|
|
}
|
|
|
|
dispatch_semaphore_signal(_requestLock);
|
|
}] resume];
|
|
dispatch_semaphore_wait(_requestLock, DISPATCH_TIME_FOREVER);
|
|
|
|
if (!_errorOccurred && request.response) {
|
|
[request.response populateWithData:_requestResp];
|
|
|
|
// If the fallback error code was detected, issue the fallback request
|
|
if (request.response.statusCode == request.fallbackError && request.fallbackRequest != NULL) {
|
|
Log(LOG_D, @"Request failed with fallback error code: %d", request.fallbackError);
|
|
request.request = request.fallbackRequest;
|
|
request.fallbackError = 0;
|
|
request.fallbackRequest = NULL;
|
|
[self executeRequestSynchronously:request];
|
|
}
|
|
}
|
|
_errorOccurred = false;
|
|
}
|
|
|
|
- (NSURLRequest*) createRequestFromString:(NSString*) urlString enableTimeout:(BOOL)normalTimeout {
|
|
NSURL* url = [[NSURL alloc] initWithString:urlString];
|
|
NSMutableURLRequest* request = [NSMutableURLRequest requestWithURL:url];
|
|
if (normalTimeout) {
|
|
// Timeout the request after 5 seconds
|
|
[request setTimeoutInterval:5];
|
|
}
|
|
else {
|
|
// Timeout the request after 60 seconds
|
|
[request setTimeoutInterval:60];
|
|
}
|
|
return request;
|
|
}
|
|
|
|
- (NSURLRequest*) newPairRequest:(NSData*)salt {
|
|
NSString* urlString = [NSString stringWithFormat:@"%@/pair?uniqueid=%@&devicename=%@&updateState=1&phrase=getservercert&salt=%@&clientcert=%@",
|
|
_baseHTTPSURL, _uniqueId, _deviceName, [self bytesToHex:salt], [self bytesToHex:_cert]];
|
|
// This call blocks while waiting for the user to input the PIN on the PC
|
|
return [self createRequestFromString:urlString enableTimeout:FALSE];
|
|
}
|
|
|
|
- (NSURLRequest*) newUnpairRequest {
|
|
NSString* urlString = [NSString stringWithFormat:@"%@/unpair?uniqueid=%@", _baseHTTPSURL, _uniqueId];
|
|
return [self createRequestFromString:urlString enableTimeout:TRUE];
|
|
}
|
|
|
|
- (NSURLRequest*) newChallengeRequest:(NSData*)challenge {
|
|
NSString* urlString = [NSString stringWithFormat:@"%@/pair?uniqueid=%@&devicename=%@&updateState=1&clientchallenge=%@",
|
|
_baseHTTPSURL, _uniqueId, _deviceName, [self bytesToHex:challenge]];
|
|
return [self createRequestFromString:urlString enableTimeout:TRUE];
|
|
}
|
|
|
|
- (NSURLRequest*) newChallengeRespRequest:(NSData*)challengeResp {
|
|
NSString* urlString = [NSString stringWithFormat:@"%@/pair?uniqueid=%@&devicename=%@&updateState=1&serverchallengeresp=%@",
|
|
_baseHTTPSURL, _uniqueId, _deviceName, [self bytesToHex:challengeResp]];
|
|
return [self createRequestFromString:urlString enableTimeout:TRUE];
|
|
}
|
|
|
|
- (NSURLRequest*) newClientSecretRespRequest:(NSString*)clientPairSecret {
|
|
NSString* urlString = [NSString stringWithFormat:@"%@/pair?uniqueid=%@&devicename=%@&updateState=1&clientpairingsecret=%@", _baseHTTPSURL, _uniqueId, _deviceName, clientPairSecret];
|
|
return [self createRequestFromString:urlString enableTimeout:TRUE];
|
|
}
|
|
|
|
- (NSURLRequest*) newPairChallenge {
|
|
NSString* urlString = [NSString stringWithFormat:@"%@/pair?uniqueid=%@&devicename=%@&updateState=1&phrase=pairchallenge", _baseHTTPSURL, _uniqueId, _deviceName];
|
|
return [self createRequestFromString:urlString enableTimeout:TRUE];
|
|
}
|
|
|
|
- (NSURLRequest *)newAppListRequest {
|
|
NSString* urlString = [NSString stringWithFormat:@"%@/applist?uniqueid=%@", _baseHTTPSURL, _uniqueId];
|
|
return [self createRequestFromString:urlString enableTimeout:TRUE];
|
|
}
|
|
|
|
- (NSURLRequest *)newServerInfoRequest {
|
|
NSString* urlString = [NSString stringWithFormat:@"%@/serverinfo?uniqueid=%@", _baseHTTPSURL, _uniqueId];
|
|
return [self createRequestFromString:urlString enableTimeout:TRUE];
|
|
}
|
|
|
|
- (NSURLRequest *)newHttpServerInfoRequest {
|
|
NSString* urlString = [NSString stringWithFormat:@"%@/serverinfo", _baseHTTPURL];
|
|
return [self createRequestFromString:urlString enableTimeout:TRUE];
|
|
}
|
|
|
|
- (NSURLRequest*) newLaunchRequest:(NSString*)appId width:(int)width height:(int)height refreshRate:(int)refreshRate rikey:(NSString*)rikey rikeyid:(int)rikeyid {
|
|
NSString* urlString = [NSString stringWithFormat:@"%@/launch?uniqueid=%@&appid=%@&mode=%dx%dx%d&additionalStates=1&sops=1&rikey=%@&rikeyid=%d", _baseHTTPSURL, _uniqueId, appId, width, height, refreshRate, rikey, rikeyid];
|
|
// This blocks while the app is launching
|
|
return [self createRequestFromString:urlString enableTimeout:FALSE];
|
|
}
|
|
|
|
- (NSURLRequest*) newResumeRequestWithRiKey:(NSString*)riKey riKeyId:(int)riKeyId {
|
|
NSString* urlString = [NSString stringWithFormat:@"%@/resume?uniqueid=%@&rikey=%@&rikeyid=%d", _baseHTTPSURL, _uniqueId, riKey, riKeyId];
|
|
// This blocks while the app is resuming
|
|
return [self createRequestFromString:urlString enableTimeout:FALSE];
|
|
}
|
|
|
|
- (NSURLRequest*) newQuitAppRequest {
|
|
NSString* urlString = [NSString stringWithFormat:@"%@/cancel?uniqueid=%@", _baseHTTPSURL, _uniqueId];
|
|
return [self createRequestFromString:urlString enableTimeout:FALSE];
|
|
}
|
|
|
|
- (NSURLRequest*) newAppAssetRequestWithAppId:(NSString *)appId {
|
|
NSString* urlString = [NSString stringWithFormat:@"%@/appasset?uniqueid=%@&appid=%@&AssetType=2&AssetIdx=0", _baseHTTPSURL, _uniqueId, appId];
|
|
return [self createRequestFromString:urlString enableTimeout:FALSE];
|
|
}
|
|
|
|
- (NSString*) bytesToHex:(NSData*)data {
|
|
const unsigned char* bytes = [data bytes];
|
|
NSMutableString *hex = [[NSMutableString alloc] init];
|
|
for (int i = 0; i < [data length]; i++) {
|
|
[hex appendFormat:@"%02X" , bytes[i]];
|
|
}
|
|
return hex;
|
|
}
|
|
|
|
// Returns an array containing the certificate
|
|
- (NSArray*)getCertificate:(SecIdentityRef) identity {
|
|
SecCertificateRef certificate = nil;
|
|
|
|
SecIdentityCopyCertificate(identity, &certificate);
|
|
|
|
return [[NSArray alloc] initWithObjects:(__bridge id)certificate, nil];
|
|
}
|
|
|
|
// Returns the identity
|
|
- (SecIdentityRef)getClientCertificate {
|
|
SecIdentityRef identityApp = nil;
|
|
CFDataRef p12Data = (__bridge CFDataRef)[CryptoManager readP12FromFile];
|
|
|
|
CFStringRef password = CFSTR("limelight");
|
|
const void *keys[] = { kSecImportExportPassphrase };
|
|
const void *values[] = { password };
|
|
CFDictionaryRef options = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
|
|
CFArrayRef items = CFArrayCreate(NULL, 0, 0, NULL);
|
|
OSStatus securityError = SecPKCS12Import(p12Data, options, &items);
|
|
|
|
if (securityError == errSecSuccess) {
|
|
//Log(LOG_D, @"Success opening p12 certificate. Items: %ld", CFArrayGetCount(items));
|
|
CFDictionaryRef identityDict = CFArrayGetValueAtIndex(items, 0);
|
|
identityApp = (SecIdentityRef)CFDictionaryGetValue(identityDict, kSecImportItemIdentity);
|
|
} else {
|
|
Log(LOG_E, @"Error opening Certificate.");
|
|
}
|
|
|
|
CFRelease(options);
|
|
CFRelease(password);
|
|
|
|
return identityApp;
|
|
}
|
|
|
|
- (void)URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge completionHandler:(nonnull void (^)(NSURLSessionAuthChallengeDisposition, NSURLCredential * __nullable))completionHandler {
|
|
// Allow untrusted server certificates
|
|
if([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust])
|
|
{
|
|
completionHandler(NSURLSessionAuthChallengeUseCredential,
|
|
[NSURLCredential credentialForTrust: challenge.protectionSpace.serverTrust]);
|
|
}
|
|
// Respond to client certificate challenge with our certificate
|
|
else if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodClientCertificate])
|
|
{
|
|
SecIdentityRef identity = [self getClientCertificate];
|
|
NSArray* certArray = [self getCertificate:identity];
|
|
NSURLCredential* newCredential = [NSURLCredential credentialWithIdentity:identity certificates:certArray persistence:NSURLCredentialPersistencePermanent];
|
|
completionHandler(NSURLSessionAuthChallengeUseCredential, newCredential);
|
|
}
|
|
else
|
|
{
|
|
completionHandler(NSURLSessionAuthChallengePerformDefaultHandling, NULL);
|
|
}
|
|
}
|
|
|
|
@end
|