Esempio n. 1
0
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;
}
Esempio n. 2
0
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;
}
Esempio n. 3
0
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;
}
Esempio n. 4
0
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;
}
Esempio n. 5
0
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;
}
Esempio n. 6
0
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);
}
Esempio n. 7
0
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;
}
Esempio n. 8
0
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;
}
Esempio n. 9
0
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", &currentGameText) != 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;
}
Esempio n. 10
0
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;
}
Esempio n. 11
0
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", &currentGameText) != 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;
}