mirror of
https://github.com/rock88/moonlight-nx
synced 2025-02-21 15:08:30 +00:00
Refactoring crypto functions
This commit is contained in:
parent
fe905ae1a2
commit
0255410aa8
21 changed files with 692 additions and 1318 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -26,5 +26,8 @@ DerivedData/
|
|||
*.o
|
||||
*.dylib
|
||||
*.so
|
||||
*.elf
|
||||
*.nacp
|
||||
*.nro
|
||||
third_party/opus
|
||||
third_party/ffmpeg
|
||||
|
|
|
@ -1,842 +0,0 @@
|
|||
/*
|
||||
* This file is part of Moonlight Embedded.
|
||||
*
|
||||
* Copyright (C) 2015-2017 Iwan Timmer
|
||||
*
|
||||
* Moonlight is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Moonlight is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Moonlight; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "http.h"
|
||||
#include "xml.h"
|
||||
#include "mkcert.h"
|
||||
#include "client.h"
|
||||
#include "errors.h"
|
||||
#include "limits.h"
|
||||
|
||||
#include <Limelight.h>
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <openssl/sha.h>
|
||||
#include <openssl/aes.h>
|
||||
#include <openssl/rand.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <openssl/x509.h>
|
||||
#include <openssl/pem.h>
|
||||
#include <openssl/err.h>
|
||||
#include "Log.h"
|
||||
|
||||
#define UNIQUE_FILE_NAME "uniqueid.dat"
|
||||
#define P12_FILE_NAME "client.p12"
|
||||
|
||||
#define UNIQUEID_BYTES 8
|
||||
#define UNIQUEID_CHARS (UNIQUEID_BYTES*2)
|
||||
|
||||
#define CHANNEL_COUNT_STEREO 2
|
||||
#define CHANNEL_COUNT_51_SURROUND 6
|
||||
|
||||
#define CHANNEL_MASK_STEREO 0x3
|
||||
#define CHANNEL_MASK_51_SURROUND 0xFC
|
||||
|
||||
static char unique_id[UNIQUEID_CHARS+1];
|
||||
static X509 *cert;
|
||||
static char cert_hex[4096];
|
||||
static EVP_PKEY *privateKey;
|
||||
|
||||
const char* gs_error;
|
||||
|
||||
typedef unsigned char uuid_t[16];
|
||||
typedef uuid_t uuid;
|
||||
|
||||
uid_t getuid() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
void simple_uuid_generate_random(uuid_t out) {
|
||||
static bool once = false;
|
||||
if (!once) {
|
||||
srand(time(NULL));
|
||||
once = true;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 16; i++) {
|
||||
out[i] = (rand() % 16) * 10 + rand() % 16;
|
||||
}
|
||||
}
|
||||
|
||||
void simple_uuid_unparse(const uuid_t uu, char *out) {
|
||||
sprintf(out, "%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X",
|
||||
uu[0], uu[1], uu[2], uu[3], uu[4], uu[5], uu[6], uu[7],
|
||||
uu[8], uu[9], uu[10], uu[11], uu[12], uu[13], uu[14], uu[15]);
|
||||
LOG_FMT("out = %s\n", out);
|
||||
}
|
||||
|
||||
#define uuid_generate_random simple_uuid_generate_random
|
||||
#define uuid_unparse simple_uuid_unparse
|
||||
|
||||
int mkdirtree(const char* directory) {
|
||||
char buffer[PATH_MAX];
|
||||
char* p = buffer;
|
||||
|
||||
// The passed in string could be a string literal
|
||||
// so we must copy it first
|
||||
strncpy(p, directory, PATH_MAX - 1);
|
||||
buffer[PATH_MAX - 1] = '\0';
|
||||
|
||||
while (*p != 0) {
|
||||
// Find the end of the path element
|
||||
do {
|
||||
p++;
|
||||
} while (*p != 0 && *p != '/');
|
||||
|
||||
char oldChar = *p;
|
||||
*p = 0;
|
||||
|
||||
// Create the directory if it doesn't exist already
|
||||
if (mkdir(buffer, 0775) == -1 && errno != EEXIST) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
*p = oldChar;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
//
|
||||
//static int load_unique_id(const char* keyDirectory) {
|
||||
// char uniqueFilePath[PATH_MAX];
|
||||
// snprintf(uniqueFilePath, PATH_MAX, "%s/%s", keyDirectory, UNIQUE_FILE_NAME);
|
||||
//
|
||||
// FILE *fd = fopen(uniqueFilePath, "r");
|
||||
// if (fd == NULL) {
|
||||
// unsigned char unique_data[UNIQUEID_BYTES];
|
||||
// RAND_bytes(unique_data, UNIQUEID_BYTES);
|
||||
// for (int i = 0; i < UNIQUEID_BYTES; i++) {
|
||||
// sprintf(unique_id + (i * 2), "%02x", unique_data[i]);
|
||||
// }
|
||||
// fd = fopen(uniqueFilePath, "w");
|
||||
// if (fd == NULL)
|
||||
// return GS_FAILED;
|
||||
//
|
||||
// fwrite(unique_id, UNIQUEID_CHARS, 1, fd);
|
||||
// } else {
|
||||
// fread(unique_id, UNIQUEID_CHARS, 1, fd);
|
||||
// }
|
||||
// fclose(fd);
|
||||
// unique_id[UNIQUEID_CHARS] = 0;
|
||||
//
|
||||
// return GS_OK;
|
||||
//}
|
||||
//
|
||||
//static int load_cert(const char* keyDirectory) {
|
||||
// char certificateFilePath[PATH_MAX];
|
||||
// snprintf(certificateFilePath, PATH_MAX, "%s/%s", keyDirectory, CERTIFICATE_FILE_NAME);
|
||||
//
|
||||
// char keyFilePath[PATH_MAX];
|
||||
// snprintf(&keyFilePath[0], PATH_MAX, "%s/%s", keyDirectory, KEY_FILE_NAME);
|
||||
//
|
||||
// FILE *fd = fopen(certificateFilePath, "r");
|
||||
// if (fd == NULL) {
|
||||
// printf("Generating certificate...");
|
||||
// CERT_KEY_PAIR cert = mkcert_generate();
|
||||
// printf("done\n");
|
||||
//
|
||||
// char p12FilePath[PATH_MAX];
|
||||
// snprintf(p12FilePath, PATH_MAX, "%s/%s", keyDirectory, P12_FILE_NAME);
|
||||
//
|
||||
// mkcert_save(certificateFilePath, p12FilePath, keyFilePath, cert);
|
||||
// mkcert_free(cert);
|
||||
// fd = fopen(certificateFilePath, "r");
|
||||
// }
|
||||
//
|
||||
// if (fd == NULL) {
|
||||
// gs_error = "Can't open certificate file";
|
||||
// return GS_FAILED;
|
||||
// }
|
||||
//
|
||||
// if (!(cert = PEM_read_X509(fd, NULL, NULL, NULL))) {
|
||||
// gs_error = "Error loading cert into memory";
|
||||
// return GS_FAILED;
|
||||
// }
|
||||
//
|
||||
// rewind(fd);
|
||||
//
|
||||
// int c;
|
||||
// int length = 0;
|
||||
// while ((c = fgetc(fd)) != EOF) {
|
||||
// sprintf(cert_hex + length, "%02x", c);
|
||||
// length += 2;
|
||||
// }
|
||||
// cert_hex[length] = 0;
|
||||
//
|
||||
// fclose(fd);
|
||||
//
|
||||
// fd = fopen(keyFilePath, "r");
|
||||
// if (fd == NULL) {
|
||||
// gs_error = "Error loading key into memory";
|
||||
// return GS_FAILED;
|
||||
// }
|
||||
//
|
||||
// PEM_read_PrivateKey(fd, &privateKey, NULL, NULL);
|
||||
// fclose(fd);
|
||||
//
|
||||
// return GS_OK;
|
||||
//}
|
||||
//
|
||||
//static int load_server_status(PSERVER_DATA server) {
|
||||
//
|
||||
// uuid_t uuid;
|
||||
// char uuid_str[37];
|
||||
//
|
||||
// int ret;
|
||||
// char url[4096];
|
||||
// int i;
|
||||
//
|
||||
// i = 0;
|
||||
// do {
|
||||
// char *pairedText = NULL;
|
||||
// char *currentGameText = NULL;
|
||||
// char *stateText = NULL;
|
||||
// char *serverCodecModeSupportText = NULL;
|
||||
//
|
||||
// ret = GS_INVALID;
|
||||
//
|
||||
// uuid_generate_random(uuid);
|
||||
// uuid_unparse(uuid, uuid_str);
|
||||
//
|
||||
// // Modern GFE versions don't allow serverinfo to be fetched over HTTPS if the client
|
||||
// // is not already paired. Since we can't pair without knowing the server version, we
|
||||
// // make another request over HTTP if the HTTPS request fails. We can't just use HTTP
|
||||
// // for everything because it doesn't accurately tell us if we're paired.
|
||||
//
|
||||
// printf("server->serverInfo.address: %s\n", server->serverInfo.address);
|
||||
// printf("unique_id: %s\n", unique_id);
|
||||
// printf("uuid_str: %s\n", uuid_str);
|
||||
//
|
||||
// snprintf(url, sizeof(url), "%s://%s:%d/serverinfo?uniqueid=%s&uuid=%s",
|
||||
// i == 0 ? "https" : "http", server->serverInfo.address, i == 0 ? 47984 : 47989, unique_id, uuid_str);
|
||||
//
|
||||
// PHTTP_DATA data = http_create_data();
|
||||
// if (data == NULL) {
|
||||
// ret = GS_OUT_OF_MEMORY;
|
||||
// goto cleanup;
|
||||
// }
|
||||
// if (http_request(url, data) != GS_OK) {
|
||||
// ret = GS_IO_ERROR;
|
||||
// goto cleanup;
|
||||
// }
|
||||
//
|
||||
// if (xml_status(data->memory, data->size) == GS_ERROR) {
|
||||
// ret = GS_ERROR;
|
||||
// goto cleanup;
|
||||
// }
|
||||
//
|
||||
// if (xml_search(data->memory, data->size, "currentgame", ¤tGameText) != GS_OK) {
|
||||
// goto cleanup;
|
||||
// }
|
||||
//
|
||||
// if (xml_search(data->memory, data->size, "PairStatus", &pairedText) != GS_OK)
|
||||
// goto cleanup;
|
||||
//
|
||||
// if (xml_search(data->memory, data->size, "appversion", (char**) &server->serverInfo.serverInfoAppVersion) != GS_OK)
|
||||
// goto cleanup;
|
||||
//
|
||||
// if (xml_search(data->memory, data->size, "state", &stateText) != GS_OK)
|
||||
// goto cleanup;
|
||||
//
|
||||
// if (xml_search(data->memory, data->size, "ServerCodecModeSupport", &serverCodecModeSupportText) != GS_OK)
|
||||
// goto cleanup;
|
||||
//
|
||||
// if (xml_search(data->memory, data->size, "gputype", &server->gpuType) != GS_OK)
|
||||
// goto cleanup;
|
||||
//
|
||||
// if (xml_search(data->memory, data->size, "GsVersion", &server->gsVersion) != GS_OK)
|
||||
// goto cleanup;
|
||||
//
|
||||
// if (xml_search(data->memory, data->size, "hostname", &server->hostname) != GS_OK)
|
||||
// goto cleanup;
|
||||
//
|
||||
// if (xml_search(data->memory, data->size, "GfeVersion", (char**) &server->serverInfo.serverInfoGfeVersion) != GS_OK)
|
||||
// goto cleanup;
|
||||
//
|
||||
// if (xml_modelist(data->memory, data->size, &server->modes) != GS_OK)
|
||||
// goto cleanup;
|
||||
//
|
||||
// // These fields are present on all version of GFE that this client supports
|
||||
// if (!strlen(currentGameText) || !strlen(pairedText) || !strlen(server->serverInfo.serverInfoAppVersion) || !strlen(stateText))
|
||||
// goto cleanup;
|
||||
//
|
||||
// server->paired = pairedText != NULL && strcmp(pairedText, "1") == 0;
|
||||
// server->currentGame = currentGameText == NULL ? 0 : atoi(currentGameText);
|
||||
// server->supports4K = serverCodecModeSupportText != NULL;
|
||||
// server->serverMajorVersion = atoi(server->serverInfo.serverInfoAppVersion);
|
||||
//
|
||||
// if (strstr(stateText, "_SERVER_BUSY") == NULL) {
|
||||
// // After GFE 2.8, current game remains set even after streaming
|
||||
// // has ended. We emulate the old behavior by forcing it to zero
|
||||
// // if streaming is not active.
|
||||
// server->currentGame = 0;
|
||||
// }
|
||||
// ret = GS_OK;
|
||||
//
|
||||
// cleanup:
|
||||
// if (data != NULL)
|
||||
// http_free_data(data);
|
||||
//
|
||||
// if (pairedText != NULL)
|
||||
// free(pairedText);
|
||||
//
|
||||
// if (currentGameText != NULL)
|
||||
// free(currentGameText);
|
||||
//
|
||||
// if (serverCodecModeSupportText != NULL)
|
||||
// free(serverCodecModeSupportText);
|
||||
//
|
||||
// i++;
|
||||
// } while (ret != GS_OK && i < 2);
|
||||
//
|
||||
// if (ret == GS_OK && !server->unsupported) {
|
||||
// if (server->serverMajorVersion > MAX_SUPPORTED_GFE_VERSION) {
|
||||
// gs_error = "Ensure you're running the latest version of Moonlight Embedded or downgrade GeForce Experience and try again";
|
||||
// ret = GS_UNSUPPORTED_VERSION;
|
||||
// } else if (server->serverMajorVersion < MIN_SUPPORTED_GFE_VERSION) {
|
||||
// gs_error = "Moonlight Embedded requires a newer version of GeForce Experience. Please upgrade GFE on your PC and try again.";
|
||||
// ret = GS_UNSUPPORTED_VERSION;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return ret;
|
||||
//}
|
||||
|
||||
static void bytes_to_hex(unsigned char *in, char *out, size_t len) {
|
||||
for (int i = 0; i < len; i++) {
|
||||
sprintf(out + i * 2, "%02x", in[i]);
|
||||
}
|
||||
out[len * 2] = 0;
|
||||
}
|
||||
|
||||
static int sign_it(const char *msg, size_t mlen, unsigned char **sig, size_t *slen, EVP_PKEY *pkey) {
|
||||
LOG_FMT("mlen = %i\n", mlen);
|
||||
int result = GS_FAILED;
|
||||
|
||||
*sig = NULL;
|
||||
*slen = 0;
|
||||
|
||||
EVP_MD_CTX *ctx = EVP_MD_CTX_create(); DEBUG_EMPTY_LOG
|
||||
if (ctx == NULL)
|
||||
return GS_FAILED;
|
||||
|
||||
const EVP_MD *md = EVP_get_digestbyname("SHA256");DEBUG_EMPTY_LOG
|
||||
if (md == NULL)
|
||||
goto cleanup;
|
||||
|
||||
int rc = EVP_DigestInit_ex(ctx, md, NULL);DEBUG_EMPTY_LOG
|
||||
if (rc != 1)
|
||||
goto cleanup;
|
||||
|
||||
rc = EVP_DigestSignInit(ctx, NULL, md, NULL, pkey);DEBUG_EMPTY_LOG
|
||||
if (rc != 1)
|
||||
goto cleanup;
|
||||
|
||||
rc = EVP_DigestSignUpdate(ctx, msg, mlen);DEBUG_EMPTY_LOG
|
||||
if (rc != 1)
|
||||
goto cleanup;
|
||||
|
||||
size_t req = 0;
|
||||
rc = EVP_DigestSignFinal(ctx, NULL, &req);DEBUG_EMPTY_LOG
|
||||
if (rc != 1 || !(req > 0))
|
||||
goto cleanup;
|
||||
|
||||
*sig = OPENSSL_malloc(req);DEBUG_EMPTY_LOG
|
||||
if (*sig == NULL)
|
||||
goto cleanup;
|
||||
|
||||
*slen = req;
|
||||
rc = EVP_DigestSignFinal(ctx, *sig, slen);DEBUG_EMPTY_LOG
|
||||
//LOG_FMT("msg = %s\n", msg);
|
||||
LOG_FMT("rc = %i\n", rc);
|
||||
if (rc != 1 || req != *slen)
|
||||
goto cleanup;
|
||||
|
||||
result = GS_OK;
|
||||
DEBUG_EMPTY_LOG
|
||||
|
||||
cleanup:
|
||||
EVP_MD_CTX_destroy(ctx);
|
||||
ctx = NULL;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool verifySignature(const char *data, int dataLength, char *signature, int signatureLength, const char *cert) {
|
||||
X509* x509;
|
||||
BIO* bio = BIO_new(BIO_s_mem());
|
||||
BIO_puts(bio, cert);
|
||||
x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
|
||||
|
||||
BIO_free(bio);
|
||||
|
||||
if (!x509) {
|
||||
return false;
|
||||
}
|
||||
|
||||
EVP_PKEY* pubKey = X509_get_pubkey(x509);
|
||||
EVP_MD_CTX *mdctx = NULL;
|
||||
mdctx = EVP_MD_CTX_create();
|
||||
EVP_DigestVerifyInit(mdctx, NULL, EVP_sha256(), NULL, pubKey);
|
||||
EVP_DigestVerifyUpdate(mdctx, data, dataLength);
|
||||
int result = EVP_DigestVerifyFinal(mdctx, signature, signatureLength);
|
||||
|
||||
X509_free(x509);
|
||||
EVP_PKEY_free(pubKey);
|
||||
EVP_MD_CTX_destroy(mdctx);
|
||||
|
||||
return result > 0;
|
||||
}
|
||||
|
||||
int gs_unpair(PSERVER_DATA server) {
|
||||
int ret = GS_OK;
|
||||
char url[4096];
|
||||
uuid_t uuid;
|
||||
char uuid_str[37];
|
||||
PHTTP_DATA data = http_create_data();
|
||||
if (data == NULL)
|
||||
return GS_OUT_OF_MEMORY;
|
||||
|
||||
uuid_generate_random(uuid);
|
||||
uuid_unparse(uuid, uuid_str);
|
||||
snprintf(url, sizeof(url), "http://%s:47989/unpair?uniqueid=%s&uuid=%s", server->serverInfo.address, unique_id, uuid_str);
|
||||
ret = http_request(url, data);
|
||||
|
||||
http_free_data(data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int gs_pair(PSERVER_DATA server, char* pin) {
|
||||
int ret = GS_OK;
|
||||
char* result = NULL;
|
||||
char url[4096];
|
||||
uuid_t uuid;
|
||||
char uuid_str[37];
|
||||
|
||||
if (server->paired) {
|
||||
gs_error = "Already paired";
|
||||
return GS_WRONG_STATE;
|
||||
}
|
||||
|
||||
if (server->currentGame != 0) {
|
||||
gs_error = "The computer is currently in a game. You must close the game before pairing";
|
||||
return GS_WRONG_STATE;
|
||||
}
|
||||
|
||||
unsigned char salt_data[16];
|
||||
char salt_hex[33];
|
||||
RAND_bytes(salt_data, 16);
|
||||
bytes_to_hex(salt_data, salt_hex, 16);
|
||||
|
||||
uuid_generate_random(uuid);
|
||||
uuid_unparse(uuid, uuid_str);
|
||||
snprintf(url, sizeof(url), "http://%s:47989/pair?uniqueid=%s&uuid=%s&devicename=roth&updateState=1&phrase=getservercert&salt=%s&clientcert=%s", server->serverInfo.address, unique_id, uuid_str, salt_hex, cert_hex);
|
||||
PHTTP_DATA data = http_create_data();
|
||||
if (data == NULL)
|
||||
return GS_OUT_OF_MEMORY;
|
||||
else if ((ret = http_request(url, data)) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if ((ret = xml_status(data->memory, data->size) != GS_OK))
|
||||
goto cleanup;
|
||||
else if ((ret = xml_search(data->memory, data->size, "paired", &result)) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if (strcmp(result, "1") != 0) {
|
||||
gs_error = "Pairing failed";
|
||||
ret = GS_FAILED;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
free(result);
|
||||
result = NULL;
|
||||
if ((ret = xml_search(data->memory, data->size, "plaincert", &result)) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if (strlen(result)/2 > 8191) {
|
||||
gs_error = "Server certificate too big";
|
||||
ret = GS_FAILED;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
char plaincert[8192];
|
||||
for (int count = 0; count < strlen(result); count += 2) {
|
||||
sscanf(&result[count], "%2hhx", &plaincert[count / 2]);
|
||||
}
|
||||
plaincert[strlen(result)/2] = '\0';
|
||||
|
||||
unsigned char salt_pin[20];
|
||||
unsigned char aes_key_hash[32];
|
||||
AES_KEY enc_key, dec_key;
|
||||
memcpy(salt_pin, salt_data, 16);
|
||||
memcpy(salt_pin+16, pin, 4);
|
||||
|
||||
int hash_length = server->serverMajorVersion >= 7 ? 32 : 20;
|
||||
if (server->serverMajorVersion >= 7)
|
||||
SHA256(salt_pin, 20, aes_key_hash);
|
||||
else
|
||||
SHA1(salt_pin, 20, aes_key_hash);
|
||||
|
||||
AES_set_encrypt_key((unsigned char *)aes_key_hash, 128, &enc_key);
|
||||
AES_set_decrypt_key((unsigned char *)aes_key_hash, 128, &dec_key);
|
||||
|
||||
unsigned char challenge_data[16];
|
||||
unsigned char challenge_enc[16];
|
||||
char challenge_hex[33];
|
||||
RAND_bytes(challenge_data, 16);
|
||||
AES_encrypt(challenge_data, challenge_enc, &enc_key);
|
||||
bytes_to_hex(challenge_enc, challenge_hex, 16);
|
||||
|
||||
uuid_generate_random(uuid);
|
||||
uuid_unparse(uuid, uuid_str);
|
||||
snprintf(url, sizeof(url), "http://%s:47989/pair?uniqueid=%s&uuid=%s&devicename=roth&updateState=1&clientchallenge=%s", server->serverInfo.address, unique_id, uuid_str, challenge_hex);
|
||||
if ((ret = http_request(url, data)) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
free(result);
|
||||
result = NULL;
|
||||
if ((ret = xml_status(data->memory, data->size) != GS_OK))
|
||||
goto cleanup;
|
||||
else if ((ret = xml_search(data->memory, data->size, "paired", &result)) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if (strcmp(result, "1") != 0) {
|
||||
gs_error = "Pairing failed";
|
||||
ret = GS_FAILED;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
free(result);
|
||||
result = NULL;
|
||||
if (xml_search(data->memory, data->size, "challengeresponse", &result) != GS_OK) {
|
||||
ret = GS_INVALID;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
char challenge_response_data_enc[64];
|
||||
char challenge_response_data[64];
|
||||
for (int count = 0; count < strlen(result); count += 2) {
|
||||
sscanf(&result[count], "%2hhx", &challenge_response_data_enc[count / 2]);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 48; i += 16) {
|
||||
AES_decrypt(&challenge_response_data_enc[i], &challenge_response_data[i], &dec_key);
|
||||
}
|
||||
|
||||
char client_secret_data[16];
|
||||
RAND_bytes(client_secret_data, 16);
|
||||
|
||||
const ASN1_BIT_STRING *asnSignature;
|
||||
X509_get0_signature(&asnSignature, NULL, cert);
|
||||
|
||||
char challenge_response[16 + 256 + 16];
|
||||
char challenge_response_hash[32];
|
||||
char challenge_response_hash_enc[32];
|
||||
char challenge_response_hex[65];
|
||||
memcpy(challenge_response, challenge_response_data + hash_length, 16);
|
||||
memcpy(challenge_response + 16, asnSignature->data, 256);
|
||||
memcpy(challenge_response + 16 + 256, client_secret_data, 16);
|
||||
if (server->serverMajorVersion >= 7)
|
||||
SHA256(challenge_response, 16 + 256 + 16, challenge_response_hash);
|
||||
else
|
||||
SHA1(challenge_response, 16 + 256 + 16, challenge_response_hash);
|
||||
|
||||
for (int i = 0; i < 32; i += 16) {
|
||||
AES_encrypt(&challenge_response_hash[i], &challenge_response_hash_enc[i], &enc_key);
|
||||
}
|
||||
bytes_to_hex(challenge_response_hash_enc, challenge_response_hex, 32);
|
||||
|
||||
uuid_generate_random(uuid);
|
||||
uuid_unparse(uuid, uuid_str);
|
||||
snprintf(url, sizeof(url), "http://%s:47989/pair?uniqueid=%s&uuid=%s&devicename=roth&updateState=1&serverchallengeresp=%s", server->serverInfo.address, unique_id, uuid_str, challenge_response_hex);
|
||||
if ((ret = http_request(url, data)) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
free(result);
|
||||
result = NULL;
|
||||
if ((ret = xml_status(data->memory, data->size) != GS_OK))
|
||||
goto cleanup;
|
||||
else if ((ret = xml_search(data->memory, data->size, "paired", &result)) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if (strcmp(result, "1") != 0) {
|
||||
gs_error = "Pairing failed";
|
||||
ret = GS_FAILED;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
free(result);
|
||||
result = NULL;
|
||||
if (xml_search(data->memory, data->size, "pairingsecret", &result) != GS_OK) {
|
||||
ret = GS_INVALID;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
char pairing_secret[16 + 256];
|
||||
for (int count = 0; count < strlen(result); count += 2) {
|
||||
sscanf(&result[count], "%2hhx", &pairing_secret[count / 2]);
|
||||
}
|
||||
|
||||
if (!verifySignature(pairing_secret, 16, pairing_secret+16, 256, plaincert)) {
|
||||
gs_error = "MITM attack detected";
|
||||
ret = GS_FAILED;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
unsigned char *signature = NULL;
|
||||
size_t s_len;
|
||||
if (sign_it(client_secret_data, 16, &signature, &s_len, privateKey) != GS_OK) {
|
||||
gs_error = "Failed to sign data";
|
||||
ret = GS_FAILED;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
char client_pairing_secret[16 + 256];
|
||||
char client_pairing_secret_hex[(16 + 256) * 2 + 1];
|
||||
memcpy(client_pairing_secret, client_secret_data, 16);
|
||||
memcpy(client_pairing_secret + 16, signature, 256);
|
||||
bytes_to_hex(client_pairing_secret, client_pairing_secret_hex, 16 + 256);
|
||||
|
||||
uuid_generate_random(uuid);
|
||||
uuid_unparse(uuid, uuid_str);
|
||||
snprintf(url, sizeof(url), "http://%s:47989/pair?uniqueid=%s&uuid=%s&devicename=roth&updateState=1&clientpairingsecret=%s", server->serverInfo.address, unique_id, uuid_str, client_pairing_secret_hex);
|
||||
if ((ret = http_request(url, data)) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
free(result);
|
||||
result = NULL;
|
||||
if ((ret = xml_status(data->memory, data->size) != GS_OK))
|
||||
goto cleanup;
|
||||
else if ((ret = xml_search(data->memory, data->size, "paired", &result)) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if (strcmp(result, "1") != 0) {
|
||||
gs_error = "Pairing failed";
|
||||
ret = GS_FAILED;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
uuid_generate_random(uuid);
|
||||
uuid_unparse(uuid, uuid_str);
|
||||
snprintf(url, sizeof(url), "https://%s:47984/pair?uniqueid=%s&uuid=%s&devicename=roth&updateState=1&phrase=pairchallenge", server->serverInfo.address, unique_id, uuid_str);
|
||||
if ((ret = http_request(url, data)) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
free(result);
|
||||
result = NULL;
|
||||
if ((ret = xml_status(data->memory, data->size) != GS_OK))
|
||||
goto cleanup;
|
||||
else if ((ret = xml_search(data->memory, data->size, "paired", &result)) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if (strcmp(result, "1") != 0) {
|
||||
gs_error = "Pairing failed";
|
||||
ret = GS_FAILED;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
server->paired = true;
|
||||
|
||||
cleanup:
|
||||
if (ret != GS_OK)
|
||||
gs_unpair(server);
|
||||
|
||||
if (result != NULL)
|
||||
free(result);
|
||||
|
||||
http_free_data(data);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int gs_applist(PSERVER_DATA server, PAPP_LIST *list) {
|
||||
int ret = GS_OK;
|
||||
char url[4096];
|
||||
uuid_t uuid;
|
||||
char uuid_str[37];
|
||||
PHTTP_DATA data = http_create_data();
|
||||
if (data == NULL)
|
||||
return GS_OUT_OF_MEMORY;
|
||||
|
||||
uuid_generate_random(uuid);
|
||||
uuid_unparse(uuid, uuid_str);
|
||||
snprintf(url, sizeof(url), "https://%s:47984/applist?uniqueid=%s&uuid=%s", server->serverInfo.address, unique_id, uuid_str);
|
||||
if (http_request(url, data) != GS_OK)
|
||||
ret = GS_IO_ERROR;
|
||||
else if (xml_status(data->memory, data->size) == GS_ERROR)
|
||||
ret = GS_ERROR;
|
||||
else if (xml_applist(data->memory, data->size, list) != GS_OK)
|
||||
ret = GS_INVALID;
|
||||
|
||||
http_free_data(data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int gs_app_boxart(PSERVER_DATA server, int app_id, char **art_data, size_t *art_data_size) {
|
||||
int ret = GS_OK;
|
||||
char url[4096];
|
||||
uuid_t uuid;
|
||||
char uuid_str[37];
|
||||
PHTTP_DATA data = http_create_data();
|
||||
if (data == NULL)
|
||||
return GS_OUT_OF_MEMORY;
|
||||
|
||||
uuid_generate_random(uuid);
|
||||
uuid_unparse(uuid, uuid_str);
|
||||
snprintf(url, sizeof(url), "https://%s:47984/appasset?uniqueid=%s&uuid=%s&appid=%d&AssetType=2&AssetIdx=0", server->serverInfo.address, unique_id, uuid_str, app_id);
|
||||
if (http_request(url, data) != GS_OK) {
|
||||
ret = GS_IO_ERROR;
|
||||
*art_data_size = 0;
|
||||
*art_data = NULL;
|
||||
}
|
||||
else {
|
||||
*art_data_size = data->size;
|
||||
*art_data = malloc(*art_data_size);
|
||||
memcpy(*art_data, data->memory, *art_data_size);
|
||||
}
|
||||
|
||||
http_free_data(data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int gs_start_app(PSERVER_DATA server, STREAM_CONFIGURATION *config, int appId, bool sops, bool localaudio, int gamepad_mask) {
|
||||
int ret = GS_OK;
|
||||
uuid_t uuid;
|
||||
char* result = NULL;
|
||||
char uuid_str[37];
|
||||
|
||||
PDISPLAY_MODE mode = server->modes;
|
||||
bool correct_mode = false;
|
||||
bool supported_resolution = false;
|
||||
while (mode != NULL) {
|
||||
if (mode->width == config->width && mode->height == config->height) {
|
||||
supported_resolution = true;
|
||||
if (mode->refresh == config->fps)
|
||||
correct_mode = true;
|
||||
}
|
||||
|
||||
mode = mode->next;
|
||||
}
|
||||
|
||||
if (!correct_mode && !server->unsupported)
|
||||
return GS_NOT_SUPPORTED_MODE;
|
||||
else if (sops && !supported_resolution)
|
||||
return GS_NOT_SUPPORTED_SOPS_RESOLUTION;
|
||||
|
||||
if (config->height >= 2160 && !server->supports4K)
|
||||
return GS_NOT_SUPPORTED_4K;
|
||||
|
||||
RAND_bytes(config->remoteInputAesKey, 16);
|
||||
memset(config->remoteInputAesIv, 0, 16);
|
||||
|
||||
srand(time(NULL));
|
||||
char url[4096];
|
||||
u_int32_t rikeyid = 0;
|
||||
char rikey_hex[33];
|
||||
bytes_to_hex(config->remoteInputAesKey, rikey_hex, 16);
|
||||
|
||||
PHTTP_DATA data = http_create_data();
|
||||
if (data == NULL)
|
||||
return GS_OUT_OF_MEMORY;
|
||||
|
||||
uuid_generate_random(uuid);
|
||||
uuid_unparse(uuid, uuid_str);
|
||||
if (server->currentGame == 0) {
|
||||
int channelCounnt = config->audioConfiguration == AUDIO_CONFIGURATION_STEREO ? CHANNEL_COUNT_STEREO : CHANNEL_COUNT_51_SURROUND;
|
||||
int mask = config->audioConfiguration == AUDIO_CONFIGURATION_STEREO ? CHANNEL_MASK_STEREO : CHANNEL_MASK_51_SURROUND;
|
||||
int fps = sops && config->fps > 60 ? 60 : config->fps;
|
||||
snprintf(url, sizeof(url), "https://%s:47984/launch?uniqueid=%s&uuid=%s&appid=%d&mode=%dx%dx%d&additionalStates=1&sops=%d&rikey=%s&rikeyid=%d&localAudioPlayMode=%d&surroundAudioInfo=%d&remoteControllersBitmap=%d&gcmap=%d", server->serverInfo.address, unique_id, uuid_str, appId, config->width, config->height, fps, sops, rikey_hex, rikeyid, localaudio, (mask << 16) + channelCounnt, gamepad_mask, gamepad_mask);
|
||||
} else
|
||||
snprintf(url, sizeof(url), "https://%s:47984/resume?uniqueid=%s&uuid=%s&rikey=%s&rikeyid=%d", server->serverInfo.address, unique_id, uuid_str, rikey_hex, rikeyid);
|
||||
|
||||
if ((ret = http_request(url, data)) == GS_OK)
|
||||
server->currentGame = appId;
|
||||
else
|
||||
goto cleanup;
|
||||
|
||||
if ((ret = xml_status(data->memory, data->size) != GS_OK))
|
||||
goto cleanup;
|
||||
else if ((ret = xml_search(data->memory, data->size, "gamesession", &result)) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if (!strcmp(result, "0")) {
|
||||
ret = GS_FAILED;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
if (result != NULL)
|
||||
free(result);
|
||||
|
||||
http_free_data(data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int gs_quit_app(PSERVER_DATA server) {
|
||||
int ret = GS_OK;
|
||||
char url[4096];
|
||||
uuid_t uuid;
|
||||
char uuid_str[37];
|
||||
char* result = NULL;
|
||||
PHTTP_DATA data = http_create_data();
|
||||
if (data == NULL)
|
||||
return GS_OUT_OF_MEMORY;
|
||||
|
||||
uuid_generate_random(uuid);
|
||||
uuid_unparse(uuid, uuid_str);
|
||||
snprintf(url, sizeof(url), "https://%s:47984/cancel?uniqueid=%s&uuid=%s", server->serverInfo.address, unique_id, uuid_str);
|
||||
if ((ret = http_request(url, data)) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if ((ret = xml_status(data->memory, data->size) != GS_OK))
|
||||
goto cleanup;
|
||||
else if ((ret = xml_search(data->memory, data->size, "cancel", &result)) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if (strcmp(result, "0") == 0) {
|
||||
ret = GS_FAILED;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
if (result != NULL)
|
||||
free(result);
|
||||
|
||||
http_free_data(data);
|
||||
return ret;
|
||||
}
|
||||
//
|
||||
//int gs_init(PSERVER_DATA server, char *address, const char *keyDirectory, int log_level, bool unsupported) {
|
||||
// mkdirtree(keyDirectory);
|
||||
// if (load_unique_id(keyDirectory) != GS_OK)
|
||||
// return GS_FAILED;
|
||||
//
|
||||
// if (load_cert(keyDirectory))
|
||||
// return GS_FAILED;
|
||||
//
|
||||
// http_init(keyDirectory, log_level);
|
||||
//
|
||||
// LiInitializeServerInformation(&server->serverInfo);
|
||||
// server->serverInfo.address = address;
|
||||
// server->unsupported = unsupported;
|
||||
// return load_server_status(server);
|
||||
//}
|
530
libgamestream/client.cpp
Normal file
530
libgamestream/client.cpp
Normal file
|
@ -0,0 +1,530 @@
|
|||
/*
|
||||
* This file is part of Moonlight Embedded.
|
||||
*
|
||||
* Copyright (C) 2015-2017 Iwan Timmer
|
||||
*
|
||||
* Moonlight is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Moonlight is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Moonlight; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "http.h"
|
||||
#include "mkcert.h"
|
||||
#include "client.h"
|
||||
#include "errors.h"
|
||||
#include "limits.h"
|
||||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <Limelight.h>
|
||||
#include "CryptoManager.hpp"
|
||||
#include "Log.h"
|
||||
|
||||
#define CHANNEL_COUNT_STEREO 2
|
||||
#define CHANNEL_COUNT_51_SURROUND 6
|
||||
|
||||
#define CHANNEL_MASK_STEREO 0x3
|
||||
#define CHANNEL_MASK_51_SURROUND 0xFC
|
||||
|
||||
static char* unique_id = "0123456789ABCDEF";
|
||||
const char* gs_error;
|
||||
|
||||
uid_t getuid() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int mkdirtree(const char* directory) {
|
||||
char buffer[PATH_MAX];
|
||||
char* p = buffer;
|
||||
|
||||
// The passed in string could be a string literal
|
||||
// so we must copy it first
|
||||
strncpy(p, directory, PATH_MAX - 1);
|
||||
buffer[PATH_MAX - 1] = '\0';
|
||||
|
||||
while (*p != 0) {
|
||||
// Find the end of the path element
|
||||
do {
|
||||
p++;
|
||||
} while (*p != 0 && *p != '/');
|
||||
|
||||
char oldChar = *p;
|
||||
*p = 0;
|
||||
|
||||
// Create the directory if it doesn't exist already
|
||||
if (mkdir(buffer, 0775) == -1 && errno != EEXIST) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
*p = oldChar;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int load_server_status(PSERVER_DATA server) {
|
||||
int ret;
|
||||
char url[4096];
|
||||
int i;
|
||||
|
||||
i = 0;
|
||||
do {
|
||||
char *pairedText = NULL;
|
||||
char *currentGameText = NULL;
|
||||
char *stateText = NULL;
|
||||
char *serverCodecModeSupportText = NULL;
|
||||
|
||||
ret = GS_INVALID;
|
||||
|
||||
// Modern GFE versions don't allow serverinfo to be fetched over HTTPS if the client
|
||||
// is not already paired. Since we can't pair without knowing the server version, we
|
||||
// make another request over HTTP if the HTTPS request fails. We can't just use HTTP
|
||||
// for everything because it doesn't accurately tell us if we're paired.
|
||||
|
||||
printf("server->serverInfo.address: %s\n", server->serverInfo.address);
|
||||
|
||||
snprintf(url, sizeof(url), "%s://%s:%d/serverinfo?uniqueid=%s",
|
||||
i == 0 ? "https" : "http", server->serverInfo.address, i == 0 ? 47984 : 47989, unique_id);
|
||||
|
||||
Data data;
|
||||
|
||||
if (http_request(url, &data) != GS_OK) {
|
||||
ret = GS_IO_ERROR;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (xml_status(data.bytes(), data.size()) == GS_ERROR) {
|
||||
ret = GS_ERROR;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (xml_search(data.bytes(), data.size(), "currentgame", ¤tGameText) != GS_OK) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (xml_search(data.bytes(), data.size(), "PairStatus", &pairedText) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if (xml_search(data.bytes(), data.size(), "appversion", (char**) &server->serverInfo.serverInfoAppVersion) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if (xml_search(data.bytes(), data.size(), "state", &stateText) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if (xml_search(data.bytes(), data.size(), "ServerCodecModeSupport", &serverCodecModeSupportText) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if (xml_search(data.bytes(), data.size(), "gputype", &server->gpuType) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if (xml_search(data.bytes(), data.size(), "GsVersion", &server->gsVersion) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if (xml_search(data.bytes(), data.size(), "hostname", &server->hostname) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if (xml_search(data.bytes(), data.size(), "GfeVersion", (char**) &server->serverInfo.serverInfoGfeVersion) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if (xml_modelist(data.bytes(), data.size(), &server->modes) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
// These fields are present on all version of GFE that this client supports
|
||||
if (!strlen(currentGameText) || !strlen(pairedText) || !strlen(server->serverInfo.serverInfoAppVersion) || !strlen(stateText))
|
||||
goto cleanup;
|
||||
|
||||
server->paired = pairedText != NULL && strcmp(pairedText, "1") == 0;
|
||||
server->currentGame = currentGameText == NULL ? 0 : atoi(currentGameText);
|
||||
server->supports4K = serverCodecModeSupportText != NULL;
|
||||
server->serverMajorVersion = atoi(server->serverInfo.serverInfoAppVersion);
|
||||
|
||||
if (strstr(stateText, "_SERVER_BUSY") == NULL) {
|
||||
// After GFE 2.8, current game remains set even after streaming
|
||||
// has ended. We emulate the old behavior by forcing it to zero
|
||||
// if streaming is not active.
|
||||
server->currentGame = 0;
|
||||
}
|
||||
ret = GS_OK;
|
||||
|
||||
cleanup:
|
||||
if (pairedText != NULL)
|
||||
free(pairedText);
|
||||
|
||||
if (currentGameText != NULL)
|
||||
free(currentGameText);
|
||||
|
||||
if (serverCodecModeSupportText != NULL)
|
||||
free(serverCodecModeSupportText);
|
||||
|
||||
i++;
|
||||
} while (ret != GS_OK && i < 2);
|
||||
|
||||
if (ret == GS_OK && !server->unsupported) {
|
||||
if (server->serverMajorVersion > MAX_SUPPORTED_GFE_VERSION) {
|
||||
gs_error = "Ensure you're running the latest version of Moonlight Embedded or downgrade GeForce Experience and try again";
|
||||
ret = GS_UNSUPPORTED_VERSION;
|
||||
} else if (server->serverMajorVersion < MIN_SUPPORTED_GFE_VERSION) {
|
||||
gs_error = "Moonlight Embedded requires a newer version of GeForce Experience. Please upgrade GFE on your PC and try again.";
|
||||
ret = GS_UNSUPPORTED_VERSION;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int gs_unpair(PSERVER_DATA server) {
|
||||
int ret = GS_OK;
|
||||
char url[4096];
|
||||
|
||||
Data data;
|
||||
|
||||
snprintf(url, sizeof(url), "http://%s:47989/unpair?uniqueid=%s", server->serverInfo.address, unique_id);
|
||||
ret = http_request(url, &data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int gs_pair_validate(Data &data, char** result) {
|
||||
if (*result) {
|
||||
free(*result);
|
||||
*result = NULL;
|
||||
}
|
||||
|
||||
int ret = GS_OK;
|
||||
if ((ret = xml_status(data.bytes(), data.size()) != GS_OK)) {
|
||||
return ret;
|
||||
} else if ((ret = xml_search(data.bytes(), data.size(), "paired", result)) != GS_OK) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (strcmp(*result, "1") != 0) {
|
||||
gs_error = "Pairing failed";
|
||||
ret = GS_FAILED;
|
||||
}
|
||||
|
||||
if (*result) {
|
||||
free(*result);
|
||||
*result = NULL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int gs_pair_cleanup(int ret, PSERVER_DATA server, char** result) {
|
||||
if (ret != GS_OK) {
|
||||
gs_unpair(server);
|
||||
}
|
||||
if (*result) {
|
||||
free(*result);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
int gs_pair(PSERVER_DATA server, char* pin) {
|
||||
int ret = GS_OK;
|
||||
Data data;
|
||||
char* result = NULL;
|
||||
char url[4096];
|
||||
|
||||
if (server->paired) {
|
||||
gs_error = "Already paired";
|
||||
return GS_WRONG_STATE;
|
||||
}
|
||||
|
||||
if (server->currentGame != 0) {
|
||||
gs_error = "The computer is currently in a game. You must close the game before pairing";
|
||||
return GS_WRONG_STATE;
|
||||
}
|
||||
|
||||
LOG_FMT("Pairing with generation %d server\n", server->serverMajorVersion);
|
||||
LOG("Start pairing stage #1\n");
|
||||
|
||||
Data salt = Data::random_bytes(16);
|
||||
Data salted_pin = salt.append(Data(pin, strlen(pin)));
|
||||
LOG_FMT("PIN: %s, salt %s\n", pin, salt.hex().bytes());
|
||||
|
||||
snprintf(url, sizeof(url), "http://%s:47989/pair?uniqueid=%s&devicename=roth&updateState=1&phrase=getservercert&salt=%s&clientcert=%s", server->serverInfo.address, unique_id, salt.hex().bytes(), CryptoManager::read_cert_from_file().hex().bytes());
|
||||
|
||||
if ((ret = http_request(url, &data)) != GS_OK) {
|
||||
return gs_pair_cleanup(ret, server, &result);
|
||||
}
|
||||
|
||||
if ((ret = gs_pair_validate(data, &result) != GS_OK)) {
|
||||
return gs_pair_cleanup(ret, server, &result);
|
||||
}
|
||||
|
||||
if ((ret = xml_search(data.bytes(), data.size(), "plaincert", &result)) != GS_OK) {
|
||||
return gs_pair_cleanup(ret, server, &result);
|
||||
}
|
||||
|
||||
LOG("Start pairing stage #2\n");
|
||||
|
||||
Data plainCert = Data(result, strlen(result));
|
||||
Data aesKey;
|
||||
|
||||
// Gen 7 servers use SHA256 to get the key
|
||||
int hashLength;
|
||||
if (server->serverMajorVersion >= 7) {
|
||||
aesKey = CryptoManager::create_AES_key_from_salt_SHA256(salted_pin);
|
||||
hashLength = 32;
|
||||
}
|
||||
else {
|
||||
aesKey = CryptoManager::create_AES_key_from_salt_SHA1(salted_pin);
|
||||
hashLength = 20;
|
||||
}
|
||||
|
||||
Data randomChallenge = Data::random_bytes(16);
|
||||
Data encryptedChallenge = CryptoManager::aes_encrypt(randomChallenge, aesKey);
|
||||
|
||||
snprintf(url, sizeof(url), "http://%s:47989/pair?uniqueid=%s&devicename=roth&updateState=1&clientchallenge=%s", server->serverInfo.address, unique_id, encryptedChallenge.hex().bytes());
|
||||
|
||||
if ((ret = http_request(url, &data)) != GS_OK) {
|
||||
return gs_pair_cleanup(ret, server, &result);
|
||||
}
|
||||
|
||||
if ((ret = gs_pair_validate(data, &result) != GS_OK)) {
|
||||
return gs_pair_cleanup(ret, server, &result);
|
||||
}
|
||||
|
||||
if (xml_search(data.bytes(), data.size(), "challengeresponse", &result) != GS_OK) {
|
||||
ret = GS_INVALID;
|
||||
return gs_pair_cleanup(ret, server, &result);
|
||||
}
|
||||
|
||||
LOG("Start pairing stage #3\n");
|
||||
|
||||
Data encServerChallengeResp = Data(result, strlen(result)).hex_to_bytes();
|
||||
Data decServerChallengeResp = CryptoManager::aes_decrypt(encServerChallengeResp, aesKey);
|
||||
Data serverResponse = decServerChallengeResp.subdata(0, hashLength);
|
||||
Data serverChallenge = decServerChallengeResp.subdata(hashLength, 16);
|
||||
|
||||
Data clientSecret = Data::random_bytes(16);
|
||||
Data challengeRespHashInput = serverChallenge.append(CryptoManager::get_signature_from_cert(CryptoManager::read_cert_from_file())).append(clientSecret);
|
||||
|
||||
Data challengeRespHash;
|
||||
|
||||
if (server->serverMajorVersion >= 7) {
|
||||
challengeRespHash = CryptoManager::SHA256_hash_data(challengeRespHashInput);
|
||||
}
|
||||
else {
|
||||
challengeRespHash = CryptoManager::SHA1_hash_data(challengeRespHashInput);
|
||||
}
|
||||
Data challengeRespEncrypted = CryptoManager::aes_encrypt(challengeRespHash, aesKey);
|
||||
|
||||
snprintf(url, sizeof(url), "http://%s:47989/pair?uniqueid=%s&devicename=roth&updateState=1&serverchallengeresp=%s", server->serverInfo.address, unique_id, challengeRespEncrypted.hex().bytes());
|
||||
|
||||
if ((ret = http_request(url, &data)) != GS_OK) {
|
||||
return gs_pair_cleanup(ret, server, &result);
|
||||
}
|
||||
|
||||
if ((ret = gs_pair_validate(data, &result) != GS_OK)) {
|
||||
return gs_pair_cleanup(ret, server, &result);
|
||||
}
|
||||
|
||||
if (xml_search(data.bytes(), data.size(), "pairingsecret", &result) != GS_OK) {
|
||||
ret = GS_INVALID;
|
||||
return gs_pair_cleanup(ret, server, &result);
|
||||
}
|
||||
|
||||
LOG("Start pairing stage #4\n");
|
||||
|
||||
Data serverSecretResp = Data(result, strlen(result)).hex_to_bytes();
|
||||
Data serverSecret = serverSecretResp.subdata(0, 16);
|
||||
Data serverSignature = serverSecretResp.subdata(16, 256);
|
||||
|
||||
if (!CryptoManager::verify_signature(serverSecret, serverSignature, plainCert.hex_to_bytes())) {
|
||||
gs_error = "MITM attack detected";
|
||||
ret = GS_FAILED;
|
||||
return gs_pair_cleanup(ret, server, &result);
|
||||
}
|
||||
|
||||
Data serverChallengeRespHashInput = randomChallenge.append(CryptoManager::get_signature_from_cert(plainCert.hex_to_bytes())).append(serverSecret);
|
||||
Data serverChallengeRespHash;
|
||||
if (server->serverMajorVersion >= 7) {
|
||||
serverChallengeRespHash = CryptoManager::SHA256_hash_data(serverChallengeRespHashInput);
|
||||
}
|
||||
else {
|
||||
serverChallengeRespHash = CryptoManager::SHA1_hash_data(serverChallengeRespHashInput);
|
||||
}
|
||||
|
||||
Data clientPairingSecret = clientSecret.append(CryptoManager::sign_data(clientSecret, CryptoManager::read_key_from_file()));
|
||||
|
||||
snprintf(url, sizeof(url), "http://%s:47989/pair?uniqueid=%s&devicename=roth&updateState=1&clientpairingsecret=%s", server->serverInfo.address, unique_id, clientPairingSecret.hex().bytes());
|
||||
if ((ret = http_request(url, &data)) != GS_OK) {
|
||||
return gs_pair_cleanup(ret, server, &result);
|
||||
}
|
||||
|
||||
if ((ret = gs_pair_validate(data, &result) != GS_OK)) {
|
||||
return gs_pair_cleanup(ret, server, &result);
|
||||
}
|
||||
|
||||
LOG("Start pairing stage #5\n");
|
||||
|
||||
snprintf(url, sizeof(url), "https://%s:47984/pair?uniqueid=%s&devicename=roth&updateState=1&phrase=pairchallenge", server->serverInfo.address, unique_id);
|
||||
if ((ret = http_request(url, &data)) != GS_OK) {
|
||||
return gs_pair_cleanup(ret, server, &result);
|
||||
}
|
||||
|
||||
if ((ret = gs_pair_validate(data, &result) != GS_OK)) {
|
||||
return gs_pair_cleanup(ret, server, &result);
|
||||
}
|
||||
|
||||
server->paired = true;
|
||||
|
||||
return gs_pair_cleanup(ret, server, &result);
|
||||
}
|
||||
|
||||
int gs_applist(PSERVER_DATA server, PAPP_LIST *list) {
|
||||
int ret = GS_OK;
|
||||
char url[4096];
|
||||
Data data;
|
||||
|
||||
snprintf(url, sizeof(url), "https://%s:47984/applist?uniqueid=%s", server->serverInfo.address, unique_id);
|
||||
|
||||
if (http_request(url, &data) != GS_OK)
|
||||
ret = GS_IO_ERROR;
|
||||
else if (xml_status(data.bytes(), data.size()) == GS_ERROR)
|
||||
ret = GS_ERROR;
|
||||
else if (xml_applist(data.bytes(), data.size(), list) != GS_OK)
|
||||
ret = GS_INVALID;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int gs_app_boxart(PSERVER_DATA server, int app_id, char **art_data, size_t *art_data_size) {
|
||||
int ret = GS_OK;
|
||||
char url[4096];
|
||||
Data data;
|
||||
|
||||
snprintf(url, sizeof(url), "https://%s:47984/appasset?uniqueid=%s&appid=%d&AssetType=2&AssetIdx=0", server->serverInfo.address, unique_id, app_id);
|
||||
|
||||
if (http_request(url, &data) != GS_OK) {
|
||||
ret = GS_IO_ERROR;
|
||||
*art_data_size = 0;
|
||||
*art_data = NULL;
|
||||
}
|
||||
else {
|
||||
*art_data_size = data.size();
|
||||
*art_data = (char *)malloc(*art_data_size);
|
||||
memcpy(*art_data, data.bytes(), *art_data_size);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int gs_start_app(PSERVER_DATA server, STREAM_CONFIGURATION *config, int appId, bool sops, bool localaudio, int gamepad_mask) {
|
||||
int ret = GS_OK;
|
||||
char* result = NULL;
|
||||
|
||||
PDISPLAY_MODE mode = server->modes;
|
||||
bool correct_mode = false;
|
||||
bool supported_resolution = false;
|
||||
while (mode != NULL) {
|
||||
if (mode->width == config->width && mode->height == config->height) {
|
||||
supported_resolution = true;
|
||||
if (mode->refresh == config->fps)
|
||||
correct_mode = true;
|
||||
}
|
||||
|
||||
mode = mode->next;
|
||||
}
|
||||
|
||||
if (!correct_mode && !server->unsupported)
|
||||
return GS_NOT_SUPPORTED_MODE;
|
||||
else if (sops && !supported_resolution)
|
||||
return GS_NOT_SUPPORTED_SOPS_RESOLUTION;
|
||||
|
||||
if (config->height >= 2160 && !server->supports4K)
|
||||
return GS_NOT_SUPPORTED_4K;
|
||||
|
||||
Data rand = Data::random_bytes(16);
|
||||
memcpy(config->remoteInputAesKey, rand.bytes(), 16);
|
||||
|
||||
char url[4096];
|
||||
u_int32_t rikeyid = 0;
|
||||
|
||||
Data data;
|
||||
|
||||
if (server->currentGame == 0) {
|
||||
int channelCounnt = config->audioConfiguration == AUDIO_CONFIGURATION_STEREO ? CHANNEL_COUNT_STEREO : CHANNEL_COUNT_51_SURROUND;
|
||||
int mask = config->audioConfiguration == AUDIO_CONFIGURATION_STEREO ? CHANNEL_MASK_STEREO : CHANNEL_MASK_51_SURROUND;
|
||||
int fps = sops && config->fps > 60 ? 60 : config->fps;
|
||||
snprintf(url, sizeof(url), "https://%s:47984/launch?uniqueid=%s&appid=%d&mode=%dx%dx%d&additionalStates=1&sops=%d&rikey=%s&rikeyid=%d&localAudioPlayMode=%d&surroundAudioInfo=%d&remoteControllersBitmap=%d&gcmap=%d", server->serverInfo.address, unique_id, appId, config->width, config->height, fps, sops, rand.hex().bytes(), rikeyid, localaudio, (mask << 16) + channelCounnt, gamepad_mask, gamepad_mask);
|
||||
} else
|
||||
snprintf(url, sizeof(url), "https://%s:47984/resume?uniqueid=%s&rikey=%s&rikeyid=%d", server->serverInfo.address, unique_id, rand.hex().bytes(), rikeyid);
|
||||
|
||||
if ((ret = http_request(url, &data)) == GS_OK)
|
||||
server->currentGame = appId;
|
||||
else
|
||||
goto cleanup;
|
||||
|
||||
if ((ret = xml_status(data.bytes(), data.size()) != GS_OK))
|
||||
goto cleanup;
|
||||
else if ((ret = xml_search(data.bytes(), data.size(), "gamesession", &result)) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if (!strcmp(result, "0")) {
|
||||
ret = GS_FAILED;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
if (result != NULL)
|
||||
free(result);
|
||||
|
||||
return ret;
|
||||
return GS_FAILED;
|
||||
}
|
||||
|
||||
int gs_quit_app(PSERVER_DATA server) {
|
||||
int ret = GS_OK;
|
||||
char url[4096];
|
||||
char* result = NULL;
|
||||
Data data;
|
||||
|
||||
snprintf(url, sizeof(url), "https://%s:47984/cancel?uniqueid=%s", server->serverInfo.address, unique_id);
|
||||
if ((ret = http_request(url, &data)) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if ((ret = xml_status(data.bytes(), data.size()) != GS_OK))
|
||||
goto cleanup;
|
||||
else if ((ret = xml_search(data.bytes(), data.size(), "cancel", &result)) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if (strcmp(result, "0") == 0) {
|
||||
ret = GS_FAILED;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
cleanup:
|
||||
if (result != NULL)
|
||||
free(result);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int gs_init(PSERVER_DATA server, char *address, const char *keyDirectory, int log_level, bool unsupported) {
|
||||
if (!CryptoManager::certs_exists()) {
|
||||
LOG("No certs, generate new...\n");
|
||||
|
||||
if (!CryptoManager::generate_certs()) {
|
||||
LOG("Failed to generate certs...\n");
|
||||
return GS_FAILED;
|
||||
}
|
||||
}
|
||||
|
||||
http_init(keyDirectory, log_level);
|
||||
|
||||
LiInitializeServerInformation(&server->serverInfo);
|
||||
server->serverInfo.address = address;
|
||||
server->unsupported = unsupported;
|
||||
return load_server_status(server);
|
||||
}
|
|
@ -19,7 +19,9 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
extern "C" {
|
||||
#include "xml.h"
|
||||
}
|
||||
|
||||
#include <Limelight.h>
|
||||
|
||||
|
@ -43,7 +45,7 @@ typedef struct _SERVER_DATA {
|
|||
} SERVER_DATA, *PSERVER_DATA;
|
||||
|
||||
int mkdirtree(const char* directory);
|
||||
//int gs_init(PSERVER_DATA server, char* address, const char *keyDirectory, int logLevel, bool unsupported);
|
||||
int gs_init(PSERVER_DATA server, char* address, const char *keyDirectory, int logLevel, bool unsupported);
|
||||
int gs_app_boxart(PSERVER_DATA server, int app_id, char **art_data, size_t *art_data_size);
|
||||
int gs_start_app(PSERVER_DATA server, PSTREAM_CONFIGURATION config, int appId, bool sops, bool localaudio, int gamepad_mask);
|
||||
int gs_applist(PSERVER_DATA server, PAPP_LIST *app_list);
|
||||
|
|
|
@ -1,129 +0,0 @@
|
|||
/*
|
||||
* This file is part of Moonlight Embedded.
|
||||
*
|
||||
* Copyright (C) 2015 Iwan Timmer
|
||||
*
|
||||
* Moonlight is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Moonlight is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Moonlight; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "http.h"
|
||||
#include "errors.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <curl/curl.h>
|
||||
|
||||
static CURL *curl;
|
||||
|
||||
static bool debug;
|
||||
|
||||
static size_t _write_curl(void *contents, size_t size, size_t nmemb, void *userp)
|
||||
{
|
||||
size_t realsize = size * nmemb;
|
||||
PHTTP_DATA mem = (PHTTP_DATA)userp;
|
||||
|
||||
mem->memory = realloc(mem->memory, mem->size + realsize + 1);
|
||||
if(mem->memory == NULL)
|
||||
return 0;
|
||||
|
||||
memcpy(&(mem->memory[mem->size]), contents, realsize);
|
||||
mem->size += realsize;
|
||||
mem->memory[mem->size] = 0;
|
||||
|
||||
return realsize;
|
||||
}
|
||||
|
||||
int http_init(const char* keyDirectory, int logLevel) {
|
||||
curl = curl_easy_init();
|
||||
debug = logLevel >= 2;
|
||||
if (!curl)
|
||||
return GS_FAILED;
|
||||
|
||||
char certificateFilePath[4096];
|
||||
sprintf(certificateFilePath, "%s/%s", keyDirectory, CERTIFICATE_FILE_NAME);
|
||||
|
||||
char keyFilePath[4096];
|
||||
sprintf(&keyFilePath[0], "%s/%s", keyDirectory, KEY_FILE_NAME);
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
|
||||
curl_easy_setopt(curl, CURLOPT_SSLENGINE_DEFAULT, 1L);
|
||||
curl_easy_setopt(curl, CURLOPT_SSLCERTTYPE,"PEM");
|
||||
curl_easy_setopt(curl, CURLOPT_SSLCERT, certificateFilePath);
|
||||
curl_easy_setopt(curl, CURLOPT_SSLKEYTYPE, "PEM");
|
||||
curl_easy_setopt(curl, CURLOPT_SSLKEY, keyFilePath);
|
||||
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, _write_curl);
|
||||
curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L);
|
||||
curl_easy_setopt(curl, CURLOPT_SSL_SESSIONID_CACHE, 0L);
|
||||
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
int http_request(char* url, PHTTP_DATA data) {
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, data);
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url);
|
||||
|
||||
if (debug)
|
||||
printf("Request %s\n", url);
|
||||
|
||||
if (data->size > 0) {
|
||||
free(data->memory);
|
||||
data->memory = malloc(1);
|
||||
if(data->memory == NULL)
|
||||
return GS_OUT_OF_MEMORY;
|
||||
|
||||
data->size = 0;
|
||||
}
|
||||
CURLcode res = curl_easy_perform(curl);
|
||||
|
||||
if(res != CURLE_OK) {
|
||||
gs_error = curl_easy_strerror(res);
|
||||
return GS_FAILED;
|
||||
} else if (data->memory == NULL) {
|
||||
return GS_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
if (debug)
|
||||
printf("Response:\n%s\n\n", data->memory);
|
||||
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
void http_cleanup() {
|
||||
curl_easy_cleanup(curl);
|
||||
}
|
||||
|
||||
PHTTP_DATA http_create_data() {
|
||||
PHTTP_DATA data = malloc(sizeof(HTTP_DATA));
|
||||
if (data == NULL)
|
||||
return NULL;
|
||||
|
||||
data->memory = malloc(1);
|
||||
if(data->memory == NULL) {
|
||||
free(data);
|
||||
return NULL;
|
||||
}
|
||||
data->size = 0;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
void http_free_data(PHTTP_DATA data) {
|
||||
if (data != NULL) {
|
||||
if (data->memory != NULL)
|
||||
free(data->memory);
|
||||
|
||||
free(data);
|
||||
}
|
||||
}
|
|
@ -1,12 +1,31 @@
|
|||
#include "NetworkClient.hpp"
|
||||
#include "Settings.hpp"
|
||||
#include "CryptoManager.hpp"
|
||||
/*
|
||||
* This file is part of Moonlight Embedded.
|
||||
*
|
||||
* Copyright (C) 2015 Iwan Timmer
|
||||
*
|
||||
* Moonlight is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Moonlight is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Moonlight; if not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "http.h"
|
||||
#include "errors.h"
|
||||
#include "Log.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <string.h>
|
||||
#include <curl/curl.h>
|
||||
|
||||
static CURL *curl;
|
||||
static bool init_curl_completed = false;
|
||||
static bool debug;
|
||||
|
||||
struct HTTP_DATA {
|
||||
char *memory;
|
||||
|
@ -24,25 +43,22 @@ static size_t _write_curl(void *contents, size_t size, size_t nmemb, void *userp
|
|||
memcpy(&(mem->memory[mem->size]), contents, realsize);
|
||||
mem->size += realsize;
|
||||
mem->memory[mem->size] = 0;
|
||||
|
||||
return realsize;
|
||||
}
|
||||
|
||||
static void init_curl() {
|
||||
if (init_curl_completed) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::string key_directory = Settings::settings()->working_dir() + "/key";
|
||||
|
||||
int http_init(const char* key_directory, int log_level) {
|
||||
curl = curl_easy_init();
|
||||
|
||||
debug = log_level >= 2;
|
||||
|
||||
if (!curl)
|
||||
return GS_FAILED;
|
||||
|
||||
char certificateFilePath[4096];
|
||||
sprintf(certificateFilePath, "%s/%s", key_directory.c_str(), CERTIFICATE_FILE_NAME);
|
||||
|
||||
sprintf(certificateFilePath, "%s/%s", key_directory, CERTIFICATE_FILE_NAME);
|
||||
|
||||
char keyFilePath[4096];
|
||||
sprintf(&keyFilePath[0], "%s/%s", key_directory.c_str(), KEY_FILE_NAME);
|
||||
|
||||
sprintf(&keyFilePath[0], "%s/%s", key_directory, KEY_FILE_NAME);
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
|
||||
curl_easy_setopt(curl, CURLOPT_SSLENGINE_DEFAULT, 1L);
|
||||
curl_easy_setopt(curl, CURLOPT_SSLCERTTYPE,"PEM");
|
||||
|
@ -54,43 +70,40 @@ static void init_curl() {
|
|||
curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L);
|
||||
curl_easy_setopt(curl, CURLOPT_SSL_SESSIONID_CACHE, 0L);
|
||||
|
||||
init_curl_completed = true;
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
int NetworkClient::perform(std::string url, bool use_https, Data* out) {
|
||||
init_curl();
|
||||
|
||||
std::string _url;
|
||||
|
||||
if (use_https) {
|
||||
_url = "https://" + url;
|
||||
} else {
|
||||
_url = "http://" + url;
|
||||
}
|
||||
|
||||
LOG_FMT("Perform %s\n", _url.c_str());
|
||||
int http_request(char* url, Data* data) {
|
||||
if (debug)
|
||||
printf("Request %s\n", url);
|
||||
|
||||
HTTP_DATA* http_data = (HTTP_DATA*)malloc(sizeof(HTTP_DATA));
|
||||
http_data->memory = (char*)malloc(1);
|
||||
http_data->size = 0;
|
||||
|
||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, http_data);
|
||||
curl_easy_setopt(curl, CURLOPT_URL, _url.c_str());
|
||||
curl_easy_setopt(curl, CURLOPT_URL, url);
|
||||
|
||||
CURLcode res = curl_easy_perform(curl);
|
||||
|
||||
if (res != CURLE_OK) {
|
||||
gs_error = curl_easy_strerror(res);
|
||||
return NetworkClientError;
|
||||
return GS_FAILED;
|
||||
} else if (http_data->memory == NULL) {
|
||||
gs_error = "Out of memory";
|
||||
return NetworkClientError;
|
||||
return GS_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
*out = Data(http_data->memory, (int)http_data->size);
|
||||
*data = Data(http_data->memory, http_data->size);
|
||||
|
||||
free(http_data->memory);
|
||||
free(http_data);
|
||||
|
||||
return NetworkClientOK;
|
||||
if (debug)
|
||||
printf("Response:\n%s\n\n", http_data->memory);
|
||||
|
||||
return GS_OK;
|
||||
}
|
||||
|
||||
void http_cleanup() {
|
||||
curl_easy_cleanup(curl);
|
||||
}
|
|
@ -19,17 +19,10 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "Data.hpp"
|
||||
|
||||
#define CERTIFICATE_FILE_NAME "client.pem"
|
||||
#define KEY_FILE_NAME "key.pem"
|
||||
|
||||
typedef struct _HTTP_DATA {
|
||||
char *memory;
|
||||
size_t size;
|
||||
} HTTP_DATA, *PHTTP_DATA;
|
||||
|
||||
//int http_init(const char* keyDirectory, int logLevel);
|
||||
PHTTP_DATA http_create_data();
|
||||
int http_request(char* url, PHTTP_DATA data);
|
||||
void http_free_data(PHTTP_DATA data);
|
||||
int http_init(const char* key_directory, int log_level);
|
||||
int http_request(char* url, Data* data);
|
||||
|
|
|
@ -139,7 +139,7 @@ static void XMLCALL _xml_write_data(void *userData, const XML_Char *s, int len)
|
|||
}
|
||||
}
|
||||
|
||||
int xml_search(char* data, size_t len, char* node, char** result) {
|
||||
int xml_search(unsigned char* data, size_t len, char* node, char** result) {
|
||||
struct xml_query search;
|
||||
search.data = node;
|
||||
search.start = 0;
|
||||
|
@ -166,7 +166,7 @@ int xml_search(char* data, size_t len, char* node, char** result) {
|
|||
return GS_OK;
|
||||
}
|
||||
|
||||
int xml_applist(char* data, size_t len, PAPP_LIST *app_list) {
|
||||
int xml_applist(unsigned char* data, size_t len, PAPP_LIST *app_list) {
|
||||
struct xml_query query;
|
||||
query.memory = calloc(1, 1);
|
||||
query.size = 0;
|
||||
|
@ -189,7 +189,7 @@ int xml_applist(char* data, size_t len, PAPP_LIST *app_list) {
|
|||
return GS_OK;
|
||||
}
|
||||
|
||||
int xml_modelist(char* data, size_t len, PDISPLAY_MODE *mode_list) {
|
||||
int xml_modelist(unsigned char* data, size_t len, PDISPLAY_MODE *mode_list) {
|
||||
struct xml_query query = {0};
|
||||
query.memory = calloc(1, 1);
|
||||
XML_Parser parser = XML_ParserCreate("UTF-8");
|
||||
|
@ -210,7 +210,7 @@ int xml_modelist(char* data, size_t len, PDISPLAY_MODE *mode_list) {
|
|||
|
||||
}
|
||||
|
||||
int xml_status(char* data, size_t len) {
|
||||
int xml_status(unsigned char* data, size_t len) {
|
||||
int status = 0;
|
||||
XML_Parser parser = XML_ParserCreate("UTF-8");
|
||||
XML_SetUserData(parser, &status);
|
||||
|
|
|
@ -33,7 +33,7 @@ typedef struct _DISPLAY_MODE {
|
|||
struct _DISPLAY_MODE *next;
|
||||
} DISPLAY_MODE, *PDISPLAY_MODE;
|
||||
|
||||
int xml_search(char* data, size_t len, char* node, char** result);
|
||||
int xml_applist(char* data, size_t len, PAPP_LIST *app_list);
|
||||
int xml_modelist(char* data, size_t len, PDISPLAY_MODE *mode_list);
|
||||
int xml_status(char* data, size_t len);
|
||||
int xml_search(unsigned char* data, size_t len, char* node, char** result);
|
||||
int xml_applist(unsigned char* data, size_t len, PAPP_LIST *app_list);
|
||||
int xml_modelist(unsigned char* data, size_t len, PDISPLAY_MODE *mode_list);
|
||||
int xml_status(unsigned char* data, size_t len);
|
||||
|
|
|
@ -49,9 +49,9 @@
|
|||
3652F002245B6961001FABF3 /* AddHostWindow.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3652F000245B6961001FABF3 /* AddHostWindow.cpp */; };
|
||||
3652F005245C28C6001FABF3 /* GameStreamClient.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3652F003245C28C6001FABF3 /* GameStreamClient.cpp */; };
|
||||
3652F010245C2919001FABF3 /* xml.c in Sources */ = {isa = PBXBuildFile; fileRef = 3652F008245C2918001FABF3 /* xml.c */; };
|
||||
3652F011245C2919001FABF3 /* client.c in Sources */ = {isa = PBXBuildFile; fileRef = 3652F00C245C2918001FABF3 /* client.c */; };
|
||||
3652F011245C2919001FABF3 /* client.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3652F00C245C2918001FABF3 /* client.cpp */; };
|
||||
3652F012245C2919001FABF3 /* mkcert.c in Sources */ = {isa = PBXBuildFile; fileRef = 3652F00D245C2918001FABF3 /* mkcert.c */; };
|
||||
3652F013245C2919001FABF3 /* http.c in Sources */ = {isa = PBXBuildFile; fileRef = 3652F00F245C2918001FABF3 /* http.c */; };
|
||||
3652F013245C2919001FABF3 /* http.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3652F00F245C2918001FABF3 /* http.cpp */; };
|
||||
3652F065245C292B001FABF3 /* list.c in Sources */ = {isa = PBXBuildFile; fileRef = 3652F017245C292B001FABF3 /* list.c */; };
|
||||
3652F066245C292B001FABF3 /* compress.c in Sources */ = {isa = PBXBuildFile; fileRef = 3652F01B245C292B001FABF3 /* compress.c */; };
|
||||
3652F067245C292B001FABF3 /* packet.c in Sources */ = {isa = PBXBuildFile; fileRef = 3652F01D245C292B001FABF3 /* packet.c */; };
|
||||
|
@ -90,16 +90,6 @@
|
|||
36A0C03A2461E4C00083289C /* SettingsWindow.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 36A0C0382461E4C00083289C /* SettingsWindow.cpp */; };
|
||||
36A0C03D2461F03C0083289C /* Settings.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 36A0C03B2461F03C0083289C /* Settings.cpp */; };
|
||||
36ADAB6A246F171D0058D1D6 /* RetroAudioRenderer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 3661D3002469EFFA0060EE24 /* RetroAudioRenderer.cpp */; };
|
||||
36ADABB0246F262D0058D1D6 /* compare.c in Sources */ = {isa = PBXBuildFile; fileRef = 36ADAB90246F262D0058D1D6 /* compare.c */; };
|
||||
36ADABB1246F262D0058D1D6 /* unpack.c in Sources */ = {isa = PBXBuildFile; fileRef = 36ADAB97246F262D0058D1D6 /* unpack.c */; };
|
||||
36ADABB2246F262D0058D1D6 /* isnull.c in Sources */ = {isa = PBXBuildFile; fileRef = 36ADAB99246F262D0058D1D6 /* isnull.c */; };
|
||||
36ADABB3246F262D0058D1D6 /* copy.c in Sources */ = {isa = PBXBuildFile; fileRef = 36ADAB9B246F262D0058D1D6 /* copy.c */; };
|
||||
36ADABB4246F262D0058D1D6 /* unparse.c in Sources */ = {isa = PBXBuildFile; fileRef = 36ADAB9F246F262D0058D1D6 /* unparse.c */; };
|
||||
36ADABB5246F262D0058D1D6 /* pack.c in Sources */ = {isa = PBXBuildFile; fileRef = 36ADABA2246F262D0058D1D6 /* pack.c */; };
|
||||
36ADABB6246F262D0058D1D6 /* clear.c in Sources */ = {isa = PBXBuildFile; fileRef = 36ADABA5246F262D0058D1D6 /* clear.c */; };
|
||||
36ADABB7246F262D0058D1D6 /* randutils.c in Sources */ = {isa = PBXBuildFile; fileRef = 36ADABA6246F262D0058D1D6 /* randutils.c */; };
|
||||
36ADABB9246F262D0058D1D6 /* gen_uuid.c in Sources */ = {isa = PBXBuildFile; fileRef = 36ADABA9246F262D0058D1D6 /* gen_uuid.c */; };
|
||||
36ADABBA246F262D0058D1D6 /* parse.c in Sources */ = {isa = PBXBuildFile; fileRef = 36ADABAA246F262D0058D1D6 /* parse.c */; };
|
||||
36B406982459F5CB005BD903 /* moonlight_glfw.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 36B406962459F460005BD903 /* moonlight_glfw.cpp */; };
|
||||
36D3F8442469B5C400CDEF9B /* MoonlightSession.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 36D3F8422469B5C400CDEF9B /* MoonlightSession.cpp */; };
|
||||
36DBDE9A2450BCD50057C8D3 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 36DBDE992450BCD50057C8D3 /* CoreFoundation.framework */; };
|
||||
|
@ -112,8 +102,6 @@
|
|||
36DFE218245A278900FC51CE /* rglgen.c in Sources */ = {isa = PBXBuildFile; fileRef = 36DFE0DE245A1FEC00FC51CE /* rglgen.c */; };
|
||||
36E6378C246FFFF30032F5FB /* CryptoManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 36E6378A246FFFF30032F5FB /* CryptoManager.cpp */; };
|
||||
36E63790247010C70032F5FB /* Data.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 36E6378E247010C70032F5FB /* Data.cpp */; };
|
||||
36E63794247030290032F5FB /* NetworkClient.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 36E63792247030290032F5FB /* NetworkClient.cpp */; };
|
||||
36E63797247036A30032F5FB /* ServerInfoRequest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 36E63795247036A30032F5FB /* ServerInfoRequest.cpp */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
|
@ -229,10 +217,10 @@
|
|||
3652F009245C2918001FABF3 /* client.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = client.h; sourceTree = "<group>"; };
|
||||
3652F00A245C2918001FABF3 /* http.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = http.h; sourceTree = "<group>"; };
|
||||
3652F00B245C2918001FABF3 /* errors.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = errors.h; sourceTree = "<group>"; };
|
||||
3652F00C245C2918001FABF3 /* client.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = client.c; sourceTree = "<group>"; };
|
||||
3652F00C245C2918001FABF3 /* client.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = client.cpp; sourceTree = "<group>"; };
|
||||
3652F00D245C2918001FABF3 /* mkcert.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mkcert.c; sourceTree = "<group>"; };
|
||||
3652F00E245C2918001FABF3 /* xml.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = xml.h; sourceTree = "<group>"; };
|
||||
3652F00F245C2918001FABF3 /* http.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = http.c; sourceTree = "<group>"; };
|
||||
3652F00F245C2918001FABF3 /* http.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = http.cpp; sourceTree = "<group>"; };
|
||||
3652F017245C292B001FABF3 /* list.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = list.c; sourceTree = "<group>"; };
|
||||
3652F01B245C292B001FABF3 /* compress.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = compress.c; sourceTree = "<group>"; };
|
||||
3652F01D245C292B001FABF3 /* packet.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = packet.c; sourceTree = "<group>"; };
|
||||
|
@ -308,22 +296,6 @@
|
|||
36A0C03B2461F03C0083289C /* Settings.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Settings.cpp; sourceTree = "<group>"; };
|
||||
36A0C03C2461F03C0083289C /* Settings.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Settings.hpp; sourceTree = "<group>"; };
|
||||
36A0C03E2461FFF10083289C /* build_opus_lakka_switch.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = build_opus_lakka_switch.sh; sourceTree = "<group>"; };
|
||||
36ADAB90246F262D0058D1D6 /* compare.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = compare.c; sourceTree = "<group>"; };
|
||||
36ADAB94246F262D0058D1D6 /* uuid.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = uuid.h; sourceTree = "<group>"; };
|
||||
36ADAB96246F262D0058D1D6 /* randutils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = randutils.h; sourceTree = "<group>"; };
|
||||
36ADAB97246F262D0058D1D6 /* unpack.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = unpack.c; sourceTree = "<group>"; };
|
||||
36ADAB98246F262D0058D1D6 /* uuidd.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = uuidd.h; sourceTree = "<group>"; };
|
||||
36ADAB99246F262D0058D1D6 /* isnull.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = isnull.c; sourceTree = "<group>"; };
|
||||
36ADAB9B246F262D0058D1D6 /* copy.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = copy.c; sourceTree = "<group>"; };
|
||||
36ADAB9F246F262D0058D1D6 /* unparse.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = unparse.c; sourceTree = "<group>"; };
|
||||
36ADABA1246F262D0058D1D6 /* c.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = c.h; sourceTree = "<group>"; };
|
||||
36ADABA2246F262D0058D1D6 /* pack.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = pack.c; sourceTree = "<group>"; };
|
||||
36ADABA5246F262D0058D1D6 /* clear.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = clear.c; sourceTree = "<group>"; };
|
||||
36ADABA6246F262D0058D1D6 /* randutils.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = randutils.c; sourceTree = "<group>"; };
|
||||
36ADABA8246F262D0058D1D6 /* uuid_time.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = uuid_time.c; sourceTree = "<group>"; };
|
||||
36ADABA9246F262D0058D1D6 /* gen_uuid.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = gen_uuid.c; sourceTree = "<group>"; };
|
||||
36ADABAA246F262D0058D1D6 /* parse.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = parse.c; sourceTree = "<group>"; };
|
||||
36ADABAF246F262D0058D1D6 /* uuidP.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = uuidP.h; sourceTree = "<group>"; };
|
||||
36B406962459F460005BD903 /* moonlight_glfw.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = moonlight_glfw.cpp; sourceTree = "<group>"; };
|
||||
36D3F8422469B5C400CDEF9B /* MoonlightSession.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = MoonlightSession.cpp; sourceTree = "<group>"; };
|
||||
36D3F8432469B5C400CDEF9B /* MoonlightSession.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = MoonlightSession.hpp; sourceTree = "<group>"; };
|
||||
|
@ -352,10 +324,6 @@
|
|||
36E6378B246FFFF30032F5FB /* CryptoManager.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = CryptoManager.hpp; sourceTree = "<group>"; };
|
||||
36E6378E247010C70032F5FB /* Data.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Data.cpp; sourceTree = "<group>"; };
|
||||
36E6378F247010C70032F5FB /* Data.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = Data.hpp; sourceTree = "<group>"; };
|
||||
36E63792247030290032F5FB /* NetworkClient.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = NetworkClient.cpp; sourceTree = "<group>"; };
|
||||
36E63793247030290032F5FB /* NetworkClient.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = NetworkClient.hpp; sourceTree = "<group>"; };
|
||||
36E63795247036A30032F5FB /* ServerInfoRequest.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ServerInfoRequest.cpp; sourceTree = "<group>"; };
|
||||
36E63796247036A30032F5FB /* ServerInfoRequest.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ServerInfoRequest.hpp; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
|
@ -543,9 +511,9 @@
|
|||
3652F006245C2918001FABF3 /* libgamestream */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
3652F00C245C2918001FABF3 /* client.c */,
|
||||
3652F00C245C2918001FABF3 /* client.cpp */,
|
||||
3652F009245C2918001FABF3 /* client.h */,
|
||||
3652F00F245C2918001FABF3 /* http.c */,
|
||||
3652F00F245C2918001FABF3 /* http.cpp */,
|
||||
3652F00A245C2918001FABF3 /* http.h */,
|
||||
3652F00D245C2918001FABF3 /* mkcert.c */,
|
||||
3652F007245C2918001FABF3 /* mkcert.h */,
|
||||
|
@ -663,33 +631,9 @@
|
|||
path = video;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
36ADAB8F246F262D0058D1D6 /* libuuid */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
36ADAB90246F262D0058D1D6 /* compare.c */,
|
||||
36ADAB94246F262D0058D1D6 /* uuid.h */,
|
||||
36ADAB96246F262D0058D1D6 /* randutils.h */,
|
||||
36ADAB97246F262D0058D1D6 /* unpack.c */,
|
||||
36ADAB98246F262D0058D1D6 /* uuidd.h */,
|
||||
36ADAB99246F262D0058D1D6 /* isnull.c */,
|
||||
36ADAB9B246F262D0058D1D6 /* copy.c */,
|
||||
36ADAB9F246F262D0058D1D6 /* unparse.c */,
|
||||
36ADABA1246F262D0058D1D6 /* c.h */,
|
||||
36ADABA2246F262D0058D1D6 /* pack.c */,
|
||||
36ADABA5246F262D0058D1D6 /* clear.c */,
|
||||
36ADABA6246F262D0058D1D6 /* randutils.c */,
|
||||
36ADABA8246F262D0058D1D6 /* uuid_time.c */,
|
||||
36ADABA9246F262D0058D1D6 /* gen_uuid.c */,
|
||||
36ADABAA246F262D0058D1D6 /* parse.c */,
|
||||
36ADABAF246F262D0058D1D6 /* uuidP.h */,
|
||||
);
|
||||
path = libuuid;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
36B406932459F41E005BD903 /* src */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
36E6379124702FEC0032F5FB /* network */,
|
||||
36E63789246FFFDC0032F5FB /* crypto */,
|
||||
36DFE0D5245A1FEC00FC51CE /* glsym */,
|
||||
36DFE0CA2459FA3F00FC51CE /* nanogui_resources */,
|
||||
|
@ -794,7 +738,6 @@
|
|||
36DFDCF62459F80600FC51CE /* third_party */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
36ADAB8F246F262D0058D1D6 /* libuuid */,
|
||||
3652F014245C292B001FABF3 /* moonlight-common-c */,
|
||||
3652ECC3245B3AFF001FABF3 /* nanogui */,
|
||||
);
|
||||
|
@ -836,17 +779,6 @@
|
|||
path = crypto;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
36E6379124702FEC0032F5FB /* network */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
36E63792247030290032F5FB /* NetworkClient.cpp */,
|
||||
36E63793247030290032F5FB /* NetworkClient.hpp */,
|
||||
36E63795247036A30032F5FB /* ServerInfoRequest.cpp */,
|
||||
36E63796247036A30032F5FB /* ServerInfoRequest.hpp */,
|
||||
);
|
||||
path = network;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
|
@ -925,12 +857,10 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
36E63794247030290032F5FB /* NetworkClient.cpp in Sources */,
|
||||
36DFE218245A278900FC51CE /* rglgen.c in Sources */,
|
||||
3652F075245C292B001FABF3 /* VideoStream.c in Sources */,
|
||||
3652EFE0245B3B00001FABF3 /* imageview.cpp in Sources */,
|
||||
3652EFDB245B3B00001FABF3 /* texture.cpp in Sources */,
|
||||
36ADABB6246F262D0058D1D6 /* clear.c in Sources */,
|
||||
36A0C03D2461F03C0083289C /* Settings.cpp in Sources */,
|
||||
3652F079245C292B001FABF3 /* RtpReorderQueue.c in Sources */,
|
||||
3652F065245C292B001FABF3 /* list.c in Sources */,
|
||||
|
@ -944,11 +874,9 @@
|
|||
3652EFDE245B3B00001FABF3 /* tabwidget.cpp in Sources */,
|
||||
3652F07A245C292B001FABF3 /* SdpGenerator.c in Sources */,
|
||||
3652EFEB245B3B00001FABF3 /* imagepanel.cpp in Sources */,
|
||||
36ADABB7246F262D0058D1D6 /* randutils.c in Sources */,
|
||||
36E63790247010C70032F5FB /* Data.cpp in Sources */,
|
||||
36A0C0372461DBA30083289C /* AddHostButton.cpp in Sources */,
|
||||
3652F06E245C292B001FABF3 /* rs.c in Sources */,
|
||||
36ADABB2246F262D0058D1D6 /* isnull.c in Sources */,
|
||||
3652EFD1245B3B00001FABF3 /* colorpicker.cpp in Sources */,
|
||||
3652F010245C2919001FABF3 /* xml.c in Sources */,
|
||||
3652F066245C292B001FABF3 /* compress.c in Sources */,
|
||||
|
@ -971,7 +899,6 @@
|
|||
3652EFE1245B3B00001FABF3 /* progressbar.cpp in Sources */,
|
||||
3652EFF5245B3B00001FABF3 /* popupbutton.cpp in Sources */,
|
||||
3652EFF0245B3B00001FABF3 /* colorwheel.cpp in Sources */,
|
||||
36ADABBA246F262D0058D1D6 /* parse.c in Sources */,
|
||||
3652F068245C292B001FABF3 /* unix.c in Sources */,
|
||||
36E6378C246FFFF30032F5FB /* CryptoManager.cpp in Sources */,
|
||||
3652F07B245C292B001FABF3 /* RtspParser.c in Sources */,
|
||||
|
@ -993,32 +920,25 @@
|
|||
3652F071245C292B001FABF3 /* InputStream.c in Sources */,
|
||||
3652EFDD245B3B00001FABF3 /* texture_gl.cpp in Sources */,
|
||||
36ADAB6A246F171D0058D1D6 /* RetroAudioRenderer.cpp in Sources */,
|
||||
3652F013245C2919001FABF3 /* http.c in Sources */,
|
||||
3652F013245C2919001FABF3 /* http.cpp in Sources */,
|
||||
3652F069245C292B001FABF3 /* peer.c in Sources */,
|
||||
3602C3B7245D903000368900 /* HostButton.cpp in Sources */,
|
||||
3652EFD0245B3B00001FABF3 /* vscrollpanel.cpp in Sources */,
|
||||
36ADABB9246F262D0058D1D6 /* gen_uuid.c in Sources */,
|
||||
3652F06D245C292B001FABF3 /* win32.c in Sources */,
|
||||
36E63797247036A30032F5FB /* ServerInfoRequest.cpp in Sources */,
|
||||
3652F012245C2919001FABF3 /* mkcert.c in Sources */,
|
||||
3652F002245B6961001FABF3 /* AddHostWindow.cpp in Sources */,
|
||||
3652EFF8245B4EE2001FABF3 /* Application.cpp in Sources */,
|
||||
36ADABB0246F262D0058D1D6 /* compare.c in Sources */,
|
||||
3652F07E245C292B001FABF3 /* AudioStream.c in Sources */,
|
||||
3652F076245C292B001FABF3 /* Connection.c in Sources */,
|
||||
3652F011245C2919001FABF3 /* client.c in Sources */,
|
||||
36ADABB3246F262D0058D1D6 /* copy.c in Sources */,
|
||||
3652F011245C2919001FABF3 /* client.cpp in Sources */,
|
||||
3652EFE4245B3B00001FABF3 /* combobox.cpp in Sources */,
|
||||
3603E93C246316400051287D /* InputController.cpp in Sources */,
|
||||
36ADABB1246F262D0058D1D6 /* unpack.c in Sources */,
|
||||
36ADABB5246F262D0058D1D6 /* pack.c in Sources */,
|
||||
3652EFF2245B3B00001FABF3 /* textbox.cpp in Sources */,
|
||||
3652F074245C292B001FABF3 /* Platform.c in Sources */,
|
||||
3661D2FF2469E0C00060EE24 /* GLVideoRenderer.cpp in Sources */,
|
||||
3652EFCD245B3B00001FABF3 /* widget.cpp in Sources */,
|
||||
3652F073245C292B001FABF3 /* PlatformSockets.c in Sources */,
|
||||
3652F08A245C8569001FABF3 /* ContentWindow.cpp in Sources */,
|
||||
36ADABB4246F262D0058D1D6 /* unparse.c in Sources */,
|
||||
3652EFE5245B3B00001FABF3 /* theme.cpp in Sources */,
|
||||
367CD95A245DE25F00A95738 /* StreamWindow.cpp in Sources */,
|
||||
3652F07F245C292B001FABF3 /* LinkedBlockingQueue.c in Sources */,
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
#include "GameStreamClient.hpp"
|
||||
#include "ServerInfoRequest.hpp"
|
||||
#include "Settings.hpp"
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
|
@ -79,10 +78,11 @@ void GameStreamClient::connect(const std::string &address, ServerCallback<SERVER
|
|||
|
||||
perform_async([this, address, callback] {
|
||||
// TODO: mem leak here :(
|
||||
int status = ServerInfoRequest::request(address, &m_server_data[address]);
|
||||
std::string key_dir = Settings::settings()->working_dir() + "/key";
|
||||
int status = gs_init(&m_server_data[address], (char *)(new std::string(address))->c_str(), key_dir.c_str(), 0, false);
|
||||
|
||||
nanogui::async([this, address, callback, status] {
|
||||
if (status == NetworkClientOK) {
|
||||
if (status == GS_OK) {
|
||||
Settings::settings()->add_host(address);
|
||||
callback(GSResult<SERVER_DATA>::success(m_server_data[address]));
|
||||
} else {
|
||||
|
|
|
@ -2,11 +2,8 @@
|
|||
#include <vector>
|
||||
#include <functional>
|
||||
#include <map>
|
||||
|
||||
extern "C" {
|
||||
#include "client.h"
|
||||
#include "errors.h"
|
||||
}
|
||||
#include "client.h"
|
||||
#include "errors.h"
|
||||
|
||||
#pragma once
|
||||
|
||||
|
|
|
@ -13,9 +13,9 @@
|
|||
static const int SHA1_HASH_LENGTH = 20;
|
||||
static const int SHA256_HASH_LENGTH = 32;
|
||||
|
||||
static Data* m_cert = nullptr;
|
||||
static Data* m_p12 = nullptr;
|
||||
static Data* m_key = nullptr;
|
||||
static Data m_cert;
|
||||
static Data m_p12;
|
||||
static Data m_key;
|
||||
|
||||
bool CryptoManager::certs_exists() {
|
||||
if (load_certs()) {
|
||||
|
@ -25,7 +25,7 @@ bool CryptoManager::certs_exists() {
|
|||
}
|
||||
|
||||
bool CryptoManager::load_certs() {
|
||||
if (m_key == nullptr || m_cert == nullptr || m_p12 == nullptr) {
|
||||
if (m_key.is_empty() || m_cert.is_empty() || m_p12.is_empty()) {
|
||||
auto key_dir = Settings::settings()->working_dir() + "/key/";
|
||||
mkdirtree(key_dir.c_str());
|
||||
|
||||
|
@ -34,9 +34,9 @@ bool CryptoManager::load_certs() {
|
|||
Data key = Data::read_from_file(key_dir + KEY_FILE_NAME);
|
||||
|
||||
if (!cert.is_empty() && !p12.is_empty() && !key.is_empty()) {
|
||||
m_cert = &cert;
|
||||
m_p12 = &p12;
|
||||
m_key = &key;
|
||||
m_cert = cert;
|
||||
m_p12 = p12;
|
||||
m_key = key;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -59,32 +59,44 @@ bool CryptoManager::generate_certs() {
|
|||
cert.write_to_file(key_dir + CERTIFICATE_FILE_NAME);
|
||||
p12.write_to_file(key_dir + P12_FILE_NAME);
|
||||
key.write_to_file(key_dir + KEY_FILE_NAME);
|
||||
m_cert = &cert;
|
||||
m_p12 = &p12;
|
||||
m_key = &key;
|
||||
m_cert = cert;
|
||||
m_p12 = p12;
|
||||
m_key = key;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Data CryptoManager::read_cert_from_file() {
|
||||
return m_cert;
|
||||
}
|
||||
|
||||
Data CryptoManager::read_p12_from_file() {
|
||||
return m_p12;
|
||||
}
|
||||
|
||||
Data CryptoManager::read_key_from_file() {
|
||||
return m_key;
|
||||
}
|
||||
|
||||
Data CryptoManager::SHA1_hash_data(Data data) {
|
||||
char sha1[SHA1_HASH_LENGTH];
|
||||
SHA1((unsigned char*)data.bytes(), data.size(), (unsigned char*)sha1);
|
||||
unsigned char sha1[SHA1_HASH_LENGTH];
|
||||
SHA1(data.bytes(), data.size(), sha1);
|
||||
return Data(sha1, sizeof(sha1));
|
||||
}
|
||||
|
||||
Data CryptoManager::SHA256_hash_data(Data data) {
|
||||
char sha256[SHA256_HASH_LENGTH];
|
||||
SHA256((unsigned char*)data.bytes(), data.size(), (unsigned char*)sha256);
|
||||
unsigned char sha256[SHA256_HASH_LENGTH];
|
||||
SHA256(data.bytes(), data.size(), sha256);
|
||||
return Data(sha256, sizeof(sha256));
|
||||
}
|
||||
|
||||
Data CryptoManager::create_AES_key_from_salt_SHA1(Data salted_pin) {
|
||||
return SHA1_hash_data(salted_pin).subdata(16);
|
||||
return SHA1_hash_data(salted_pin).subdata(0, 16);
|
||||
}
|
||||
|
||||
Data CryptoManager::create_AES_key_from_salt_SHA256(Data salted_pin) {
|
||||
return SHA256_hash_data(salted_pin).subdata(16);
|
||||
return SHA256_hash_data(salted_pin).subdata(0, 16);
|
||||
}
|
||||
|
||||
static int get_encrypt_size(Data data) {
|
||||
|
@ -115,13 +127,13 @@ Data CryptoManager::aes_encrypt(Data data, Data key) {
|
|||
|
||||
Data CryptoManager::aes_decrypt(Data data, Data key) {
|
||||
AES_KEY aes_key;
|
||||
AES_set_decrypt_key((unsigned char*)key.bytes(), 128, &aes_key);
|
||||
AES_set_decrypt_key(key.bytes(), 128, &aes_key);
|
||||
unsigned char* buffer = (unsigned char*)malloc(data.size());
|
||||
|
||||
// AES_decrypt only decrypts the first 16 bytes so iterate the entire buffer
|
||||
int block_offset = 0;
|
||||
while (block_offset < data.size()) {
|
||||
AES_decrypt((unsigned char*)data.bytes() + block_offset, buffer + block_offset, &aes_key);
|
||||
AES_decrypt(data.bytes() + block_offset, buffer + block_offset, &aes_key);
|
||||
block_offset += 16;
|
||||
}
|
||||
|
||||
|
@ -133,7 +145,7 @@ Data CryptoManager::aes_decrypt(Data data, Data key) {
|
|||
Data CryptoManager::pem_to_der(Data pem_cert_bytes) {
|
||||
X509* x509;
|
||||
|
||||
BIO* bio = BIO_new_mem_buf((unsigned char*)pem_cert_bytes.bytes(), pem_cert_bytes.size());
|
||||
BIO* bio = BIO_new_mem_buf(pem_cert_bytes.bytes(), pem_cert_bytes.size());
|
||||
x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
|
||||
BIO_free(bio);
|
||||
|
||||
|
@ -149,9 +161,8 @@ Data CryptoManager::pem_to_der(Data pem_cert_bytes) {
|
|||
}
|
||||
|
||||
bool CryptoManager::verify_signature(Data data, Data signature, Data cert) {
|
||||
X509* x509;
|
||||
BIO* bio = BIO_new_mem_buf(cert.bytes(), cert.size());
|
||||
x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
|
||||
X509* x509 = PEM_read_bio_X509(bio, NULL, NULL, NULL);
|
||||
|
||||
BIO_free(bio);
|
||||
|
||||
|
@ -165,7 +176,7 @@ bool CryptoManager::verify_signature(Data data, Data signature, Data cert) {
|
|||
mdctx = EVP_MD_CTX_create();
|
||||
EVP_DigestVerifyInit(mdctx, NULL, EVP_sha256(), NULL, pub_key);
|
||||
EVP_DigestVerifyUpdate(mdctx, data.bytes(), data.size());
|
||||
int result = EVP_DigestVerifyFinal(mdctx, (unsigned char*)signature.bytes(), signature.size());
|
||||
int result = EVP_DigestVerifyFinal(mdctx, signature.bytes(), signature.size());
|
||||
|
||||
X509_free(x509);
|
||||
EVP_PKEY_free(pub_key);
|
||||
|
@ -175,19 +186,16 @@ bool CryptoManager::verify_signature(Data data, Data signature, Data cert) {
|
|||
|
||||
Data CryptoManager::sign_data(Data data, Data key) {
|
||||
BIO* bio = BIO_new_mem_buf(key.bytes(), key.size());
|
||||
|
||||
EVP_PKEY* pkey;
|
||||
pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
|
||||
EVP_PKEY* pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
|
||||
|
||||
BIO_free(bio);
|
||||
|
||||
if (!pkey) {
|
||||
LOG("Unable to parse private key in memory!");
|
||||
exit(-1);
|
||||
return Data();
|
||||
}
|
||||
|
||||
EVP_MD_CTX *mdctx = NULL;
|
||||
mdctx = EVP_MD_CTX_create();
|
||||
EVP_MD_CTX *mdctx = EVP_MD_CTX_create();
|
||||
EVP_DigestSignInit(mdctx, NULL, EVP_sha256(), NULL, pkey);
|
||||
EVP_DigestSignUpdate(mdctx, data.bytes(), data.size());
|
||||
size_t slen;
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
#include <stdio.h>
|
||||
#include "Data.hpp"
|
||||
#include "client.h"
|
||||
|
||||
extern "C" {
|
||||
#include "mkcert.h"
|
||||
#include "client.h"
|
||||
#include "mkcert.h"
|
||||
}
|
||||
|
||||
#pragma once
|
||||
|
@ -17,6 +17,11 @@ public:
|
|||
static bool certs_exists();
|
||||
static bool load_certs();
|
||||
static bool generate_certs();
|
||||
|
||||
static Data read_cert_from_file();
|
||||
static Data read_p12_from_file();
|
||||
static Data read_key_from_file();
|
||||
|
||||
static Data SHA1_hash_data(Data data);
|
||||
static Data SHA256_hash_data(Data data);
|
||||
static Data create_AES_key_from_salt_SHA1(Data salted_pin);
|
||||
|
|
|
@ -3,17 +3,18 @@
|
|||
#include <cstdlib>
|
||||
#include "Log.h"
|
||||
|
||||
Data::Data(char* bytes, size_t size) {
|
||||
Data::Data(unsigned char* bytes, size_t size) {
|
||||
if (bytes && size > 0) {
|
||||
m_bytes = (char *)malloc(sizeof(char) * size);
|
||||
memcpy(m_bytes, bytes, sizeof(char) * size);
|
||||
m_bytes = (unsigned char *)malloc(size);
|
||||
memcpy(m_bytes, bytes, size);
|
||||
m_size = size;
|
||||
}
|
||||
}
|
||||
|
||||
Data::Data(size_t capacity) {
|
||||
if (capacity > 0) {
|
||||
m_bytes = (char *)malloc(sizeof(char) * capacity);
|
||||
m_bytes = (unsigned char *)malloc(capacity + 1);
|
||||
m_bytes[capacity] = '\0';
|
||||
m_size = capacity;
|
||||
}
|
||||
}
|
||||
|
@ -24,15 +25,30 @@ Data::~Data() {
|
|||
}
|
||||
}
|
||||
|
||||
Data Data::subdata(size_t size) {
|
||||
if (size > m_size) {
|
||||
Data Data::subdata(size_t start, size_t size) {
|
||||
if (start + size > m_size) {
|
||||
LOG("Invalid data length...\n");
|
||||
exit(-1);
|
||||
}
|
||||
return Data(m_bytes, size);
|
||||
return Data(&m_bytes[start], size);
|
||||
}
|
||||
|
||||
Data::Data(const Data& that): Data(that.m_bytes, that.m_size) {}
|
||||
Data Data::append(Data other) {
|
||||
if (is_empty()) {
|
||||
return other;
|
||||
}
|
||||
|
||||
Data data(m_size + other.m_size);
|
||||
memcpy(data.m_bytes, m_bytes, m_size);
|
||||
memcpy(&data.m_bytes[m_size], other.m_bytes, other.m_size);
|
||||
return data;
|
||||
}
|
||||
|
||||
Data::Data(const Data& that): Data(0) {
|
||||
m_bytes = (unsigned char *)malloc(that.size());
|
||||
memcpy(m_bytes, that.bytes(), that.size());
|
||||
m_size = that.size();
|
||||
}
|
||||
|
||||
Data& Data::operator=(const Data& that) {
|
||||
if (this != &that) {
|
||||
|
@ -40,17 +56,18 @@ Data& Data::operator=(const Data& that) {
|
|||
free(m_bytes);
|
||||
}
|
||||
|
||||
m_bytes = (char *)malloc(sizeof(char) * that.m_size);
|
||||
memcpy(m_bytes, that.m_bytes, sizeof(char) * that.m_size);
|
||||
m_bytes = (unsigned char *)malloc(that.m_size);
|
||||
memcpy(m_bytes, that.m_bytes, that.m_size);
|
||||
m_size = that.m_size;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
Data Data::random_bytes(size_t size) {
|
||||
char* bytes = (char*)malloc(sizeof(char) * size);
|
||||
unsigned char* bytes = (unsigned char*)malloc(sizeof(char) * size);
|
||||
arc4random_buf(bytes, size);
|
||||
Data random_data(bytes, size);
|
||||
Data random_data(size);
|
||||
memcpy(random_data.m_bytes, bytes, size);
|
||||
free(bytes);
|
||||
return random_data;
|
||||
}
|
||||
|
@ -87,7 +104,7 @@ Data Data::hex_to_bytes() const {
|
|||
return data;
|
||||
}
|
||||
|
||||
Data Data::bytes_to_hex() const {
|
||||
Data Data::hex() const {
|
||||
int counter = 0;
|
||||
Data hex(m_size * 2);
|
||||
char fmt[3] = {'\0','\0','\0'};
|
||||
|
@ -96,6 +113,7 @@ Data Data::bytes_to_hex() const {
|
|||
hex.m_bytes[counter++] = fmt[0];
|
||||
hex.m_bytes[counter++] = fmt[1];
|
||||
}
|
||||
hex.m_bytes[m_size * 2] = '\0';
|
||||
return hex;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,13 +5,13 @@
|
|||
class Data {
|
||||
public:
|
||||
Data(): Data(0) {};
|
||||
Data(char* bytes, size_t size);
|
||||
Data(unsigned char* bytes, size_t size): Data((char *)bytes, size) {};
|
||||
Data(unsigned char* bytes, size_t size);
|
||||
Data(char* bytes, size_t size): Data((unsigned char *)bytes, size) {};
|
||||
Data(size_t capacity);
|
||||
|
||||
~Data();
|
||||
|
||||
char* bytes() const {
|
||||
unsigned char* bytes() const {
|
||||
return m_bytes;
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,8 @@ public:
|
|||
return m_size;
|
||||
}
|
||||
|
||||
Data subdata(size_t size);
|
||||
Data subdata(size_t start, size_t size);
|
||||
Data append(Data other);
|
||||
|
||||
Data(const Data& that);
|
||||
Data& operator=(const Data& that);
|
||||
|
@ -28,7 +29,7 @@ public:
|
|||
static Data read_from_file(std::string path);
|
||||
|
||||
Data hex_to_bytes() const;
|
||||
Data bytes_to_hex() const;
|
||||
Data hex() const;
|
||||
|
||||
bool is_empty() const {
|
||||
return m_size == 0;
|
||||
|
@ -37,6 +38,6 @@ public:
|
|||
void write_to_file(std::string path);
|
||||
|
||||
private:
|
||||
char* m_bytes = nullptr;
|
||||
unsigned char* m_bytes = nullptr;
|
||||
size_t m_size = 0;
|
||||
};
|
||||
|
|
|
@ -17,10 +17,6 @@
|
|||
|
||||
#include <GLFW/glfw3.h>
|
||||
|
||||
#include "Data.hpp"
|
||||
#include "NetworkClient.hpp"
|
||||
#include "ServerInfoRequest.hpp"
|
||||
|
||||
extern retro_input_state_t input_state_cb;
|
||||
|
||||
static int mouse_x = 0, mouse_y = 0;
|
||||
|
@ -117,10 +113,6 @@ int main(int argc, const char * argv[]) {
|
|||
Settings::settings()->set_working_dir("/Users/rock88/Documents/RetroArch/system/moonlight");
|
||||
#endif
|
||||
|
||||
// SERVER_DATA d5;
|
||||
// std::string a = "www.google.ru";
|
||||
// ServerInfoRequest::request(a, &d5);
|
||||
|
||||
nanogui::init();
|
||||
nanogui::ref<Application> app = new Application(Size(width, height), Size(fb_width, fb_height));
|
||||
|
||||
|
|
|
@ -11,10 +11,7 @@
|
|||
#include "Application.hpp"
|
||||
#include "InputController.hpp"
|
||||
#include "Settings.hpp"
|
||||
|
||||
extern "C" {
|
||||
#include "client.h"
|
||||
}
|
||||
#include "client.h"
|
||||
|
||||
struct retro_hw_render_callback hw_render;
|
||||
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
#include "Data.hpp"
|
||||
#include <string>
|
||||
#pragma once
|
||||
|
||||
#define NetworkClientOK 0
|
||||
#define NetworkClientError 1
|
||||
|
||||
static const std::string unique_id = "0123456789ABCDEF";
|
||||
|
||||
class NetworkClient {
|
||||
public:
|
||||
static int perform(std::string url, bool use_https, Data* out);
|
||||
};
|
|
@ -1,109 +0,0 @@
|
|||
#include "ServerInfoRequest.hpp"
|
||||
#include "CryptoManager.hpp"
|
||||
#include "Log.h"
|
||||
|
||||
extern "C" {
|
||||
#include "errors.h"
|
||||
}
|
||||
|
||||
int ServerInfoRequest::request(std::string address, PSERVER_DATA server) {
|
||||
LiInitializeServerInformation(&server->serverInfo);
|
||||
char* addr = (char *)malloc(sizeof(char) * address.length() + 1);
|
||||
addr[address.length()] = '\0';
|
||||
memcpy(addr, address.c_str(), sizeof(char) * address.length());
|
||||
server->serverInfo.address = addr;
|
||||
|
||||
int result = NetworkClientError;
|
||||
char url[1024];
|
||||
|
||||
bool certs_exists = CryptoManager::certs_exists();
|
||||
|
||||
if (certs_exists) {
|
||||
snprintf(url, sizeof(url), "%s:47984/serverinfo?uniqueid=%s", server->serverInfo.address, unique_id.c_str());
|
||||
} else {
|
||||
snprintf(url, sizeof(url), "%s:47989/serverinfo", server->serverInfo.address);
|
||||
}
|
||||
|
||||
Data data;
|
||||
if (NetworkClient::perform(url, certs_exists, &data) != NetworkClientOK) {
|
||||
return NetworkClientError;
|
||||
}
|
||||
|
||||
char *pairedText = NULL;
|
||||
char *currentGameText = NULL;
|
||||
char *stateText = NULL;
|
||||
char *serverCodecModeSupportText = NULL;
|
||||
|
||||
if (xml_status(data.bytes(), data.size()) == GS_ERROR)
|
||||
goto cleanup;
|
||||
|
||||
if (xml_search(data.bytes(), data.size(), "currentgame", ¤tGameText) != GS_OK) {
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (xml_search(data.bytes(), data.size(), "PairStatus", &pairedText) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if (xml_search(data.bytes(), data.size(), "appversion", (char**) &server->serverInfo.serverInfoAppVersion) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if (xml_search(data.bytes(), data.size(), "state", &stateText) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if (xml_search(data.bytes(), data.size(), "ServerCodecModeSupport", &serverCodecModeSupportText) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if (xml_search(data.bytes(), data.size(), "gputype", &server->gpuType) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if (xml_search(data.bytes(), data.size(), "GsVersion", &server->gsVersion) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if (xml_search(data.bytes(), data.size(), "hostname", &server->hostname) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if (xml_search(data.bytes(), data.size(), "GfeVersion", (char**) &server->serverInfo.serverInfoGfeVersion) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
if (xml_modelist(data.bytes(), data.size(), &server->modes) != GS_OK)
|
||||
goto cleanup;
|
||||
|
||||
// These fields are present on all version of GFE that this client supports
|
||||
if (!strlen(currentGameText) || !strlen(pairedText) || !strlen(server->serverInfo.serverInfoAppVersion) || !strlen(stateText))
|
||||
goto cleanup;
|
||||
|
||||
server->paired = pairedText != NULL && strcmp(pairedText, "1") == 0;
|
||||
server->currentGame = currentGameText == NULL ? 0 : atoi(currentGameText);
|
||||
server->supports4K = serverCodecModeSupportText != NULL;
|
||||
server->serverMajorVersion = atoi(server->serverInfo.serverInfoAppVersion);
|
||||
|
||||
if (strstr(stateText, "_SERVER_BUSY") == NULL) {
|
||||
// After GFE 2.8, current game remains set even after streaming
|
||||
// has ended. We emulate the old behavior by forcing it to zero
|
||||
// if streaming is not active.
|
||||
server->currentGame = 0;
|
||||
}
|
||||
result = NetworkClientOK;
|
||||
|
||||
cleanup:
|
||||
if (pairedText != NULL)
|
||||
free(pairedText);
|
||||
|
||||
if (currentGameText != NULL)
|
||||
free(currentGameText);
|
||||
|
||||
if (serverCodecModeSupportText != NULL)
|
||||
free(serverCodecModeSupportText);
|
||||
|
||||
if (result == GS_OK && !server->unsupported) {
|
||||
if (server->serverMajorVersion > MAX_SUPPORTED_GFE_VERSION) {
|
||||
gs_error = "Ensure you're running the latest version of Moonlight Embedded or downgrade GeForce Experience and try again";
|
||||
result = NetworkClientError;
|
||||
} else if (server->serverMajorVersion < MIN_SUPPORTED_GFE_VERSION) {
|
||||
gs_error = "Moonlight Embedded requires a newer version of GeForce Experience. Please upgrade GFE on your PC and try again.";
|
||||
result = NetworkClientError;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
#include "NetworkClient.hpp"
|
||||
|
||||
extern "C" {
|
||||
#include "client.h"
|
||||
}
|
||||
|
||||
#pragma once
|
||||
|
||||
class ServerInfoRequest {
|
||||
public:
|
||||
static int request(std::string address, PSERVER_DATA server);
|
||||
};
|
Loading…
Add table
Reference in a new issue