From cf6829287a500eeafeccc68ba19d6f19b923c1c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20M=C3=A4rkl?= Date: Fri, 11 Oct 2019 15:24:46 +0200 Subject: [PATCH] Fix RP-Version 8.0 Compatibility, #57 --- lib/include/chiaki/session.h | 20 +++++-- lib/src/ctrl.c | 6 ++- lib/src/regist.c | 40 ++++++++++++-- lib/src/session.c | 100 +++++++++++++++++++++++++---------- 4 files changed, 128 insertions(+), 38 deletions(-) diff --git a/lib/include/chiaki/session.h b/lib/include/chiaki/session.h index 54dfad4..08a2832 100644 --- a/lib/include/chiaki/session.h +++ b/lib/include/chiaki/session.h @@ -42,12 +42,23 @@ extern "C" { #define CHIAKI_RP_APPLICATION_REASON_INVALID_PSN_ID 0x80108b02 #define CHIAKI_RP_APPLICATION_REASON_IN_USE 0x80108b10 #define CHIAKI_RP_APPLICATION_REASON_CRASH 0x80108b15 -#define CHIAKI_RP_APPLICATION_REASON_CLIENT_OUTDATED 0x80108b11 +#define CHIAKI_RP_APPLICATION_REASON_RP_VERSION 0x80108b11 // unknown: 0x80108bff -#define CHIAKI_RP_CLIENT_VERSION "9.0" +CHIAKI_EXPORT const char *chiaki_rp_application_reason_string(uint32_t reason); -const char *chiaki_rp_application_reason_string(uint32_t reason); +typedef enum { + CHIAKI_RP_VERSION_UNKNOWN = 0, + CHIAKI_RP_VERSION_8_0 = 800, + CHIAKI_RP_VERSION_9_0 = 900 +} ChiakiRpVersion; + +/** + * @return RP-Version string or NULL + */ +CHIAKI_EXPORT const char *chiaki_rp_version_string(ChiakiRpVersion version); + +CHIAKI_EXPORT ChiakiRpVersion chiaki_rp_version_parse(const char *rp_version_str); #define CHIAKI_RP_DID_SIZE 32 @@ -94,6 +105,7 @@ typedef enum { CHIAKI_QUIT_REASON_SESSION_REQUEST_CONNECTION_REFUSED, CHIAKI_QUIT_REASON_SESSION_REQUEST_RP_IN_USE, CHIAKI_QUIT_REASON_SESSION_REQUEST_RP_CRASH, + CHIAKI_QUIT_REASON_SESSION_REQUEST_RP_VERSION_MISMATCH, CHIAKI_QUIT_REASON_CTRL_UNKNOWN, CHIAKI_QUIT_REASON_CTRL_CONNECT_FAILED, CHIAKI_QUIT_REASON_CTRL_CONNECTION_REFUSED, @@ -156,6 +168,8 @@ typedef struct chiaki_session_t ChiakiConnectVideoProfile video_profile; } connect_info; + ChiakiRpVersion rp_version; + uint8_t nonce[CHIAKI_RPCRYPT_KEY_SIZE]; ChiakiRPCrypt rpcrypt; char session_id[CHIAKI_SESSION_ID_SIZE_MAX]; // zero-terminated diff --git a/lib/src/ctrl.c b/lib/src/ctrl.c index 3d7c2e2..f1fdece 100644 --- a/lib/src/ctrl.c +++ b/lib/src/ctrl.c @@ -570,16 +570,18 @@ static ChiakiErrorCode ctrl_connect(ChiakiCtrl *ctrl) "Connection: keep-alive\r\n" "Content-Length: 0\r\n" "RP-Auth: %s\r\n" - "RP-Version: " CHIAKI_RP_CLIENT_VERSION "\r\n" + "RP-Version: %s\r\n" "RP-Did: %s\r\n" "RP-ControllerType: 3\r\n" "RP-ClientType: 11\r\n" "RP-OSType: %s\r\n" "RP-ConPath: 1\r\n\r\n"; + const char *rp_version = chiaki_rp_version_string(session->rp_version); + char buf[512]; int request_len = snprintf(buf, sizeof(buf), request_fmt, - session->connect_info.hostname, SESSION_CTRL_PORT, auth_b64, did_b64, ostype_b64); + session->connect_info.hostname, SESSION_CTRL_PORT, auth_b64, rp_version ? rp_version : "", did_b64, ostype_b64); if(request_len < 0 || request_len >= sizeof(buf)) goto error; diff --git a/lib/src/regist.c b/lib/src/regist.c index 2bed5c2..16a4f62 100644 --- a/lib/src/regist.c +++ b/lib/src/regist.c @@ -106,13 +106,16 @@ static void regist_event_simple(ChiakiRegist *regist, ChiakiRegistEventType type regist->cb(&event, regist->cb_user); } -static const char * const request_fmt = +static const char * const request_head_fmt = "POST /sce/rp/regist HTTP/1.1\r\n" "HOST: 10.0.2.15\r\n" // random lol "User-Agent: remoteplay Windows\r\n" "Connection: close\r\n" - "Content-Length: %llu\r\n" - "RP-Version: " CHIAKI_RP_CLIENT_VERSION "\r\n\r\n"; + "Content-Length: %llu\r\n"; + +static const char * const request_rp_version_fmt = "RP-Version: %s\r\n"; + +static const char * const request_tail = "\r\n"; static const char * const request_inner_account_id_fmt = "Client-Type: Windows\r\n" @@ -123,6 +126,28 @@ static const char * const request_inner_online_id_fmt = "Np-Online-Id: %s\r\n"; +static int request_header_format(char *buf, size_t buf_size, size_t payload_size, ChiakiRpVersion rp_version) +{ + int cur = snprintf(buf, buf_size, request_head_fmt, (unsigned long long)payload_size); + if(cur < 0 || cur >= payload_size) + return -1; + if(rp_version >= CHIAKI_RP_VERSION_9_0) + { + const char *rp_version_str = chiaki_rp_version_string(rp_version); + size_t s = buf_size - cur; + int r = snprintf(buf + cur, s, request_rp_version_fmt, rp_version_str); + if(r < 0 || r >= s) + return -1; + cur += r; + } + size_t tail_size = strlen(request_tail) + 1; + if(cur + tail_size > payload_size) + return -1; + memcpy(buf + cur, request_tail, tail_size); + cur += (int)tail_size - 1; + return cur; +} + CHIAKI_EXPORT ChiakiErrorCode chiaki_regist_request_payload_format(uint8_t *buf, size_t *buf_size, ChiakiRPCrypt *crypt, const char *psn_online_id, const uint8_t *psn_account_id) { @@ -179,14 +204,19 @@ static void *regist_thread_func(void *user) goto fail; } + ChiakiRpVersion rp_version = regist->info.psn_online_id ? CHIAKI_RP_VERSION_8_0 : CHIAKI_RP_VERSION_9_0; char request_header[0x100]; - int request_header_size = snprintf(request_header, sizeof(request_header), request_fmt, (unsigned long long)payload_size); - if(request_header_size >= sizeof(request_header)) + int request_header_size = request_header_format(request_header, sizeof(request_header), payload_size, rp_version); + + if(request_header_size < 0 || request_header_size >= sizeof(request_header)) { CHIAKI_LOGE(regist->log, "Regist failed to format request"); goto fail; } + CHIAKI_LOGV(regist->log, "Regist formatted request header:"); + chiaki_log_hexdump(regist->log, CHIAKI_LOG_VERBOSE, (uint8_t *)request_header, request_header_size); + struct addrinfo *addrinfos; int r = getaddrinfo(regist->info.host, NULL, NULL, &addrinfos); if(r != 0) diff --git a/lib/src/session.c b/lib/src/session.c index 9ea2e21..ce45fd7 100644 --- a/lib/src/session.c +++ b/lib/src/session.c @@ -44,6 +44,8 @@ #define SESSION_EXPECT_TIMEOUT_MS 5000 +static void *session_thread_func(void *arg); +static bool session_thread_request_session(ChiakiSession *session, ChiakiRpVersion *server_version_out); const char *chiaki_rp_application_reason_string(uint32_t reason) { @@ -57,13 +59,34 @@ const char *chiaki_rp_application_reason_string(uint32_t reason) return "Remote is already in use"; case CHIAKI_RP_APPLICATION_REASON_CRASH: return "Remote Play on Console crashed"; - case CHIAKI_RP_APPLICATION_REASON_CLIENT_OUTDATED: - return "Client outdated"; + case CHIAKI_RP_APPLICATION_REASON_RP_VERSION: + return "RP-Version mismatch"; default: return "unknown"; } } +const char *chiaki_rp_version_string(ChiakiRpVersion version) +{ + switch(version) + { + case CHIAKI_RP_VERSION_8_0: + return "9.0"; + case CHIAKI_RP_VERSION_9_0: + return "9.0"; + default: + return NULL; + } +} + +CHIAKI_EXPORT ChiakiRpVersion chiaki_rp_version_parse(const char *rp_version_str) +{ + if(strcmp(rp_version_str, "8.0") == 0) + return CHIAKI_RP_VERSION_8_0; + if(strcmp(rp_version_str, "9.0") == 0) + return CHIAKI_RP_VERSION_9_0; + return CHIAKI_RP_VERSION_UNKNOWN; +} CHIAKI_EXPORT void chiaki_connect_video_profile_preset(ChiakiConnectVideoProfile *profile, ChiakiVideoResolutionPreset resolution, ChiakiVideoFPSPreset fps) { @@ -124,6 +147,8 @@ CHIAKI_EXPORT const char *chiaki_quit_reason_string(ChiakiQuitReason reason) return "Remote Play on Console is already in use"; case CHIAKI_QUIT_REASON_SESSION_REQUEST_RP_CRASH: return "Remote Play on Console has crashed"; + case CHIAKI_QUIT_REASON_SESSION_REQUEST_RP_VERSION_MISMATCH: + return "RP-Version mismatch"; case CHIAKI_QUIT_REASON_CTRL_UNKNOWN: return "Unknown Ctrl Error"; case CHIAKI_QUIT_REASON_CTRL_CONNECTION_REFUSED: @@ -140,7 +165,6 @@ CHIAKI_EXPORT const char *chiaki_quit_reason_string(ChiakiQuitReason reason) } } -static void *session_thread_func(void *arg); CHIAKI_EXPORT ChiakiErrorCode chiaki_session_init(ChiakiSession *session, ChiakiConnectInfo *connect_info, ChiakiLog *log) @@ -148,8 +172,8 @@ CHIAKI_EXPORT ChiakiErrorCode chiaki_session_init(ChiakiSession *session, Chiaki memset(session, 0, sizeof(ChiakiSession)); session->log = log; - session->quit_reason = CHIAKI_QUIT_REASON_NONE; + session->rp_version = CHIAKI_RP_VERSION_9_0; ChiakiErrorCode err = chiaki_cond_init(&session->state_cond); if(err != CHIAKI_ERR_SUCCESS) @@ -288,8 +312,6 @@ void chiaki_session_send_event(ChiakiSession *session, ChiakiEvent *event) } -static bool session_thread_request_session(ChiakiSession *session); - static bool session_check_state_pred(void *user) { ChiakiSession *session = user; @@ -346,7 +368,16 @@ static void *session_thread_func(void *arg) CHIAKI_LOGI(session->log, "Starting session request"); - success = session_thread_request_session(session); + ChiakiRpVersion server_rp_version = CHIAKI_RP_VERSION_UNKNOWN; + success = session_thread_request_session(session, &server_rp_version); + + if(!success && server_rp_version != CHIAKI_RP_VERSION_UNKNOWN) + { + CHIAKI_LOGI(session->log, "Attempting to re-request session with Server's RP-Version"); + session->rp_version = server_rp_version; + success = session_thread_request_session(session, NULL); + } + if(!success) QUIT(quit); @@ -536,32 +567,27 @@ static void parse_session_response(SessionResponse *response, ChiakiHttpResponse { memset(response, 0, sizeof(SessionResponse)); + for(ChiakiHttpHeader *header=http_response->headers; header; header=header->next) + { + if(strcmp(header->key, "RP-Nonce") == 0) + response->nonce = header->value; + else if(strcasecmp(header->key, "RP-Version") == 0) + response->rp_version = header->value; + else if(strcmp(header->key, "RP-Application-Reason") == 0) + response->error_code = (uint32_t)strtoul(header->value, NULL, 0x10); + } + if(http_response->code == 200) - { - for(ChiakiHttpHeader *header=http_response->headers; header; header=header->next) - { - if(strcmp(header->key, "RP-Nonce") == 0) - response->nonce = header->value; - else if(strcmp(header->key, "RP-Version") == 0) - response->rp_version = header->value; - } response->success = response->nonce != NULL; - } else - { - for(ChiakiHttpHeader *header=http_response->headers; header; header=header->next) - { - if(strcmp(header->key, "RP-Application-Reason") == 0) - { - response->error_code = (uint32_t)strtoul(header->value, NULL, 0x10); - } - } response->success = false; - } } -static bool session_thread_request_session(ChiakiSession *session) +/** + * @param server_version_out if NULL, version mismatch means to fail the entire session, otherwise report the version here + */ +static bool session_thread_request_session(ChiakiSession *session, ChiakiRpVersion *server_version_out) { chiaki_socket_t session_sock = CHIAKI_INVALID_SOCKET; for(struct addrinfo *ai=session->connect_info.host_addrinfos; ai; ai=ai->ai_next) @@ -650,7 +676,7 @@ static bool session_thread_request_session(ChiakiSession *session) "Connection: close\r\n" "Content-Length: 0\r\n" "RP-Registkey: %s\r\n" - "Rp-Version: " CHIAKI_RP_CLIENT_VERSION "\r\n" + "Rp-Version: %s\r\n" "\r\n"; size_t regist_key_len = sizeof(session->connect_info.regist_key); @@ -671,9 +697,11 @@ static bool session_thread_request_session(ChiakiSession *session) return false; } + const char *rp_version_str = chiaki_rp_version_string(session->rp_version); + char buf[512]; int request_len = snprintf(buf, sizeof(buf), session_request_fmt, - session->connect_info.hostname, SESSION_PORT, regist_key_hex); + session->connect_info.hostname, SESSION_PORT, regist_key_hex, rp_version_str ? rp_version_str : ""); if(request_len < 0 || request_len >= sizeof(buf)) { CHIAKI_SOCKET_CLOSE(session_sock); @@ -682,6 +710,7 @@ static bool session_thread_request_session(ChiakiSession *session) } CHIAKI_LOGI(session->log, "Sending session request"); + chiaki_log_hexdump(session->log, CHIAKI_LOG_VERBOSE, (uint8_t *)buf, request_len); int sent = send(session_sock, buf, (size_t)request_len, 0); if(sent < 0) @@ -739,6 +768,18 @@ static bool session_thread_request_session(ChiakiSession *session) session->quit_reason = CHIAKI_QUIT_REASON_SESSION_REQUEST_UNKNOWN; } } + else if(response.error_code == CHIAKI_RP_APPLICATION_REASON_RP_VERSION && server_version_out && response.rp_version) + { + CHIAKI_LOGI(session->log, "Reported RP-Version mismatch. ours = %s, server = %s", rp_version_str ? rp_version_str : "", response.rp_version); + *server_version_out = chiaki_rp_version_parse(response.rp_version); + if(*server_version_out != CHIAKI_RP_VERSION_UNKNOWN) + CHIAKI_LOGI(session->log, "Detected Server RP-Version %s", chiaki_rp_version_string(*server_version_out)); + else + { + CHIAKI_LOGE(session->log, "Server RP-Version is unknown"); + session->quit_reason = CHIAKI_QUIT_REASON_SESSION_REQUEST_RP_VERSION_MISMATCH; + } + } else { CHIAKI_LOGE(session->log, "Reported Application Reason: %#x (%s)", (unsigned int)response.error_code, chiaki_rp_application_reason_string(response.error_code)); @@ -750,6 +791,9 @@ static bool session_thread_request_session(ChiakiSession *session) case CHIAKI_RP_APPLICATION_REASON_CRASH: session->quit_reason = CHIAKI_QUIT_REASON_SESSION_REQUEST_RP_CRASH; break; + case CHIAKI_RP_APPLICATION_REASON_RP_VERSION: + session->quit_reason = CHIAKI_QUIT_REASON_SESSION_REQUEST_RP_VERSION_MISMATCH; + break; default: session->quit_reason = CHIAKI_QUIT_REASON_SESSION_REQUEST_UNKNOWN; break;