int gs_start_app(PSERVER_DATA server, STREAM_CONFIGURATION *config, int appId, bool sops, bool localaudio) { uuid_t uuid; char uuid_str[37]; RAND_bytes(config->remoteInputAesKey, 16); memset(config->remoteInputAesIv, 0, 16); srand(time(NULL)); char url[4096]; u_int32_t rikeyid = 1; 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; sprintf(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", server->address, unique_id, uuid_str, appId, config->width, config->height, config->fps, sops, rikey_hex, rikeyid, localaudio, (mask << 16) + channelCounnt); } else sprintf(url, "https://%s:47984/resume?uniqueid=%s&uuid=%s&rikey=%s&rikeyid=%d", server->address, unique_id, uuid_str, rikey_hex, rikeyid); int ret = http_request(url, data); if (ret == GS_OK) server->currentGame = appId; 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); sprintf(url, "https://%s:47984/cancel?uniqueid=%s&uuid=%s", server->address, unique_id, uuid_str); if ((ret = http_request(url, data)) != GS_OK) goto cleanup; 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_start_app(PSERVER_DATA server, STREAM_CONFIGURATION *config, int appId, bool sops, bool localaudio) { int ret = GS_OK; uuid_t uuid; char* result = NULL; char uuid_str[37]; 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; sprintf(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", server->address, unique_id, uuid_str, appId, config->width, config->height, config->fps, sops, rikey_hex, rikeyid, localaudio, (mask << 16) + channelCounnt); } else sprintf(url, "https://%s:47984/resume?uniqueid=%s&uuid=%s&rikey=%s&rikeyid=%d", server->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_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) { 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); sprintf(url, "https://%s:47984/cancel?uniqueid=%s&uuid=%s", server->address, unique_id, uuid_str); int ret = http_request(url, data); 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); sprintf(url, "https://%s:47984/applist?uniqueid=%s&uuid=%s", server->address, unique_id, uuid_str); if (http_request(url, data) != GS_OK) ret = GS_IO_ERROR; else if (xml_applist(data->memory, data->size, list) != GS_OK) ret = GS_INVALID; http_free_data(data); return ret; }
void MoonlightInstance::NvHTTPRequest(int32_t /*result*/, int32_t callbackId, std::string url) { char* _url = strdup(url.c_str()); PHTTP_DATA data = http_create_data(); int err; if (data == NULL) { pp::VarDictionary ret; ret.Set("callbackId", pp::Var(callbackId)); ret.Set("type", pp::Var("reject")); ret.Set("ret", pp::Var("Error when creating data buffer.")); PostMessage(ret); goto clean_data; } err = http_request(_url , data); if (err) { pp::VarDictionary ret; ret.Set("callbackId", pp::Var(callbackId)); ret.Set("type", pp::Var("reject")); ret.Set("ret", pp::Var(err)); PostMessage(ret); goto clean_data; } { pp::VarDictionary ret; ret.Set("callbackId", pp::Var(callbackId)); ret.Set("type", pp::Var("resolve")); ret.Set("ret", pp::Var(data->memory)); PostMessage(ret); } clean_data: http_free_data(data); free(_url); }
int gs_pair(const char* address, const char* pin) { int ret = GS_OK; char url[4096]; unsigned char salt_data[16]; char salt_hex[33]; RAND_bytes(salt_data, 16); bytes_to_hex(salt_data, salt_hex, 16); sprintf(url, "http://%s:47989/pair?uniqueid=%s&devicename=roth&updateState=1&phrase=getservercert&salt=%s&clientcert=%s", address, g_UniqueId, salt_hex, g_CertHex); 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; unsigned char salt_pin[20]; unsigned char aes_key_hash[20]; AES_KEY enc_key, dec_key; memcpy(salt_pin, salt_data, 16); memcpy(salt_pin+16, pin, 4); 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); sprintf(url, "http://%s:47989/pair?uniqueid=%s&devicename=roth&updateState=1&clientchallenge=%s", address, g_UniqueId, challenge_hex); if ((ret = http_request(url, data)) != GS_OK) goto cleanup; char *result; if (xml_search(data->memory, data->size, "challengeresponse", &result) != GS_OK) { ret = GS_INVALID; goto cleanup; } unsigned char challenge_response_data_enc[48]; unsigned char challenge_response_data[48]; for (int count = 0; count < strlen(result); count += 2) { sscanf(&result[count], "%2hhx", &challenge_response_data_enc[count / 2]); } free(result); for (int i = 0; i < 48; i += 16) { AES_decrypt(&challenge_response_data_enc[i], &challenge_response_data[i], &dec_key); } unsigned char client_secret_data[16]; RAND_bytes(client_secret_data, 16); unsigned char challenge_response[16 + 256 + 16]; unsigned char challenge_response_hash[32]; unsigned char challenge_response_hash_enc[32]; char challenge_response_hex[65]; memcpy(challenge_response, challenge_response_data + 20, 16); memcpy(challenge_response + 16, g_Cert->signature->data, 256); memcpy(challenge_response + 16 + 256, client_secret_data, 16); 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); sprintf(url, "http://%s:47989/pair?uniqueid=%s&devicename=roth&updateState=1&serverchallengeresp=%s", address, g_UniqueId, challenge_response_hex); if ((ret = http_request(url, data)) != GS_OK) goto cleanup; if (xml_search(data->memory, data->size, "pairingsecret", &result) != GS_OK) { ret = GS_INVALID; goto cleanup; } unsigned char *signature = NULL; size_t s_len; if (sign_it(client_secret_data, 16, &signature, &s_len, g_PrivateKey) != GS_OK) { gs_error = "Failed to sign data"; ret = GS_FAILED; goto cleanup; } unsigned 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); sprintf(url, "http://%s:47989/pair?uniqueid=%s&devicename=roth&updateState=1&clientpairingsecret=%s", address, g_UniqueId, client_pairing_secret_hex); if ((ret = http_request(url, data)) != GS_OK) goto cleanup; sprintf(url, "https://%s:47984/pair?uniqueid=%s&devicename=roth&updateState=1&phrase=pairchallenge", address, g_UniqueId); if ((ret = http_request(url, data)) != GS_OK) goto cleanup; cleanup: http_free_data(data); return ret; }
int gs_pair(PSERVER_DATA server, char* pin) { int ret = GS_OK; 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); sprintf(url, "http://%s:47989/pair?uniqueid=%s&uuid=%s&devicename=roth&updateState=1&phrase=getservercert&salt=%s&clientcert=%s", server->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; unsigned char salt_pin[20]; unsigned char aes_key_hash[20]; AES_KEY aes_key; memcpy(salt_pin, salt_data, 16); memcpy(salt_pin+16, salt_pin, 4); SHA1(salt_pin, 20, aes_key_hash); AES_set_encrypt_key((unsigned char *)aes_key_hash, 128, &aes_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, &aes_key); bytes_to_hex(challenge_enc, challenge_hex, 16); uuid_generate_random(uuid); uuid_unparse(uuid, uuid_str); sprintf(url, "http://%s:47989/pair?uniqueid=%s&uuid=%s&devicename=roth&updateState=1&clientchallenge=%s", server->address, unique_id, uuid_str, challenge_hex); if ((ret = http_request(url, data)) != GS_OK) goto cleanup; char *result; if (xml_search(data->memory, data->size, "challengeresponse", &result) != GS_OK) { ret = GS_INVALID; goto cleanup; } char challenge_response_data_enc[48]; char challenge_response_data[48]; for (int count = 0; count < strlen(result); count++) { sscanf(&result[count], "%2hhx", &challenge_response_data_enc[count / 2]); } free(result); for (int i = 0; i < 48; i += 16) { AES_decrypt(&challenge_response_data_enc[i], &challenge_response_data[i], &aes_key); } char client_secret_data[16]; RAND_bytes(client_secret_data, 16); char challenge_response[16 + 256 + 16]; char challenge_response_hash[32]; char challenge_response_hash_enc[32]; char challenge_response_hex[33]; memcpy(challenge_response, challenge_response_data + 20, 16); memcpy(challenge_response + 16, cert->signature->data, 256); memcpy(challenge_response + 16 + 256, client_secret_data, 16); 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], &aes_key); } bytes_to_hex(challenge_response_hash_enc, challenge_response_hex, 32); uuid_generate_random(uuid); uuid_unparse(uuid, uuid_str); sprintf(url, "http://%s:47989/pair?uniqueid=%s&uuid=%s&devicename=roth&updateState=1&serverchallengeresp=%s", server->address, unique_id, uuid_str, challenge_response_hex); if ((ret = http_request(url, data)) != GS_OK) goto cleanup; if (xml_search(data->memory, data->size, "pairingsecret", &result) != GS_OK) { ret = GS_INVALID; 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); sprintf(url, "http://%s:47989/pair?uniqueid=%s&uuid=%s&devicename=roth&updateState=1&clientpairingsecret=%s", server->address, unique_id, uuid_str, client_pairing_secret_hex); if ((ret = http_request(url, data)) != GS_OK) goto cleanup; uuid_generate_random(uuid); uuid_unparse(uuid, uuid_str); sprintf(url, "https://%s:47984/pair?uniqueid=%s&uuid=%s&devicename=roth&updateState=1&phrase=pairchallenge", server->address, unique_id, uuid_str); if ((ret = http_request(url, data)) != GS_OK) goto cleanup; server->paired = true; cleanup: http_free_data(data); return ret; }
static int load_server_status(PSERVER_DATA server) { char *pairedText = NULL; char *currentGameText = NULL; char *versionText = NULL; char *stateText = NULL; uuid_t uuid; char uuid_str[37]; int ret = GS_INVALID; char url[4096]; uuid_generate_random(uuid); uuid_unparse(uuid, uuid_str); sprintf(url, "https://%s:47984/serverinfo?uniqueid=%s&uuid=%s", server->address, 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_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", &versionText) != GS_OK) goto cleanup; if (xml_search(data->memory, data->size, "state", &stateText) != GS_OK) goto cleanup; server->paired = pairedText != NULL && strcmp(pairedText, "1") == 0; server->currentGame = currentGameText == NULL ? 0 : atoi(currentGameText); char *versionSep = strstr(versionText, "."); if (versionSep != NULL) { *versionSep = 0; } server->serverMajorVersion = atoi(versionText); if (strstr(stateText, "_SERVER_AVAILABLE")) { // 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 (versionText != NULL) free(versionText); 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); sprintf(url, "http://%s:47989/pair?uniqueid=%s&uuid=%s&devicename=roth&updateState=1&phrase=getservercert&salt=%s&clientcert=%s", server->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_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'; printf("%d / %d\n", strlen(result)/2, strlen(plaincert)); 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); sprintf(url, "http://%s:47989/pair?uniqueid=%s&uuid=%s&devicename=roth&updateState=1&clientchallenge=%s", server->address, unique_id, uuid_str, challenge_hex); if ((ret = http_request(url, data)) != GS_OK) goto cleanup; free(result); result = NULL; 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[48]; char challenge_response_data[48]; 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); 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, cert->signature->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); sprintf(url, "http://%s:47989/pair?uniqueid=%s&uuid=%s&devicename=roth&updateState=1&serverchallengeresp=%s", server->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_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); sprintf(url, "http://%s:47989/pair?uniqueid=%s&uuid=%s&devicename=roth&updateState=1&clientpairingsecret=%s", server->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_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); sprintf(url, "https://%s:47984/pair?uniqueid=%s&uuid=%s&devicename=roth&updateState=1&phrase=pairchallenge", server->address, unique_id, uuid_str); if ((ret = http_request(url, data)) != GS_OK) goto cleanup; free(result); result = NULL; 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; }
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 *versionText = NULL; char *stateText = NULL; char *heightText = 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. sprintf(url, "%s://%s:%d/serverinfo?uniqueid=%s&uuid=%s", i == 0 ? "https" : "http", server->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_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", &versionText) != GS_OK) goto cleanup; if (xml_search(data->memory, data->size, "state", &stateText) != GS_OK) goto cleanup; if (xml_search(data->memory, data->size, "Height", &heightText) != 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, "GfeVersion", &server->gfeVersion) != GS_OK) goto cleanup; // These fields are present on all version of GFE that this client supports if (!strlen(currentGameText) || !strlen(pairedText) || !strlen(versionText) || !strlen(stateText)) goto cleanup; server->paired = pairedText != NULL && strcmp(pairedText, "1") == 0; server->currentGame = currentGameText == NULL ? 0 : atoi(currentGameText); server->supports4K = heightText != NULL && serverCodecModeSupportText != NULL && atoi(heightText) >= 2160; server->serverMajorVersion = atoi(versionText); if (strstr(stateText, "_SERVER_AVAILABLE")) { // 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 (versionText != NULL) free(versionText); if (heightText != NULL) free(heightText); if (serverCodecModeSupportText != NULL) free(serverCodecModeSupportText); i++; } while (ret != GS_OK && i < 2); if (ret == GS_OK) { 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; }