diff --git a/lib/include/chiaki/ctrl.h b/lib/include/chiaki/ctrl.h index 38a04bf..4e0258b 100644 --- a/lib/include/chiaki/ctrl.h +++ b/lib/include/chiaki/ctrl.h @@ -37,16 +37,25 @@ typedef struct chiaki_ctrl_t { struct chiaki_session_t *session; ChiakiThread thread; - ChiakiStopPipe stop_pipe; + + bool should_stop; + bool login_pin_entered; + uint8_t *login_pin; // not owned + size_t login_pin_size; + ChiakiStopPipe notif_pipe; + ChiakiMutex notif_mutex; + chiaki_socket_t sock; uint8_t recv_buf[512]; size_t recv_buf_size; + uint64_t crypt_counter_local; uint64_t crypt_counter_remote; } ChiakiCtrl; CHIAKI_EXPORT ChiakiErrorCode chiaki_ctrl_start(ChiakiCtrl *ctrl, struct chiaki_session_t *session); CHIAKI_EXPORT void chiaki_ctrl_stop(ChiakiCtrl *ctrl); CHIAKI_EXPORT ChiakiErrorCode chiaki_ctrl_join(ChiakiCtrl *ctrl); +CHIAKI_EXPORT void chiaki_ctrl_set_login_pin(ChiakiCtrl *ctrl, uint8_t *pin, size_t pin_size); #ifdef __cplusplus } diff --git a/lib/include/chiaki/session.h b/lib/include/chiaki/session.h index 165242a..8942cc1 100644 --- a/lib/include/chiaki/session.h +++ b/lib/include/chiaki/session.h @@ -111,6 +111,7 @@ typedef struct chiaki_audio_stream_info_event_t typedef enum { + CHIAKI_EVENT_LOGIN_PIN_REQUEST, CHIAKI_EVENT_QUIT } ChiakiEventType; @@ -164,7 +165,7 @@ typedef struct chiaki_session_t ChiakiAudioSettingsCallback audio_settings_cb; ChiakiAudioFrameCallback audio_frame_cb; void *audio_cb_user; - ChiakiVideoSampleCallback video_sample_cb; + ChiakiVideoSampleCallback video_sample_cb; void *video_sample_cb_user; ChiakiThread session_thread; @@ -175,6 +176,10 @@ typedef struct chiaki_session_t bool should_stop; bool ctrl_failed; bool ctrl_session_id_received; + bool ctrl_login_pin_requested; + bool login_pin_entered; + uint8_t *login_pin; + size_t login_pin_size; ChiakiCtrl ctrl; @@ -193,6 +198,7 @@ CHIAKI_EXPORT ChiakiErrorCode chiaki_session_start(ChiakiSession *session); CHIAKI_EXPORT ChiakiErrorCode chiaki_session_stop(ChiakiSession *session); CHIAKI_EXPORT ChiakiErrorCode chiaki_session_join(ChiakiSession *session); CHIAKI_EXPORT ChiakiErrorCode chiaki_session_set_controller_state(ChiakiSession *session, ChiakiControllerState *state); +CHIAKI_EXPORT ChiakiErrorCode chiaki_session_set_login_pin(ChiakiSession *session, uint8_t *pin, size_t pin_size); static inline void chiaki_session_set_event_cb(ChiakiSession *session, ChiakiEventCallback cb, void *user) { diff --git a/lib/include/chiaki/stoppipe.h b/lib/include/chiaki/stoppipe.h index 700118c..b5d93b3 100644 --- a/lib/include/chiaki/stoppipe.h +++ b/lib/include/chiaki/stoppipe.h @@ -45,6 +45,7 @@ CHIAKI_EXPORT void chiaki_stop_pipe_fini(ChiakiStopPipe *stop_pipe); CHIAKI_EXPORT void chiaki_stop_pipe_stop(ChiakiStopPipe *stop_pipe); CHIAKI_EXPORT ChiakiErrorCode chiaki_stop_pipe_select_single(ChiakiStopPipe *stop_pipe, chiaki_socket_t fd, uint64_t timeout_ms); static inline ChiakiErrorCode chiaki_stop_pipe_sleep(ChiakiStopPipe *stop_pipe, uint64_t timeout_ms) { return chiaki_stop_pipe_select_single(stop_pipe, CHIAKI_INVALID_SOCKET, timeout_ms); } +CHIAKI_EXPORT ChiakiErrorCode chiaki_stop_pipe_reset(ChiakiStopPipe *stop_pipe); #ifdef __cplusplus } diff --git a/lib/src/ctrl.c b/lib/src/ctrl.c index 332af2a..750838c 100644 --- a/lib/src/ctrl.c +++ b/lib/src/ctrl.c @@ -47,40 +47,77 @@ typedef enum ctrl_message_type_t { CTRL_MESSAGE_TYPE_SESSION_ID = 0x33, CTRL_MESSAGE_TYPE_HEARTBEAT_REQ = 0xfe, - CTRL_MESSAGE_TYPE_HEARTBEAT_REP = 0x1fe + CTRL_MESSAGE_TYPE_HEARTBEAT_REP = 0x1fe, + CTRL_MESSAGE_TYPE_LOGIN_PIN_REQ = 0x4, + CTRL_MESSAGE_TYPE_LOGIN_PIN_REP = 0x8004 } CtrlMessageType; static void *ctrl_thread_func(void *user); +static ChiakiErrorCode ctrl_message_send(ChiakiCtrl *ctrl, CtrlMessageType type, const uint8_t *payload, size_t payload_size); +static void ctrl_message_received_session_id(ChiakiCtrl *ctrl, uint8_t *payload, size_t payload_size); +static void ctrl_message_received_heartbeat_req(ChiakiCtrl *ctrl, uint8_t *payload, size_t payload_size); +static void ctrl_message_received_login_pin_req(ChiakiCtrl *ctrl, uint8_t *payload, size_t payload_size); CHIAKI_EXPORT ChiakiErrorCode chiaki_ctrl_start(ChiakiCtrl *ctrl, ChiakiSession *session) { ctrl->session = session; - ChiakiErrorCode err = chiaki_stop_pipe_init(&ctrl->stop_pipe); + + ctrl->should_stop = false; + ctrl->login_pin_entered = false; + ctrl->login_pin = NULL; + ctrl->login_pin_size = 0; + + ChiakiErrorCode err = chiaki_stop_pipe_init(&ctrl->notif_pipe); if(err != CHIAKI_ERR_SUCCESS) return err; + + err = chiaki_mutex_init(&ctrl->notif_mutex, false); + if(err != CHIAKI_ERR_SUCCESS) + goto error_notif_pipe; + err = chiaki_thread_create(&ctrl->thread, ctrl_thread_func, ctrl); if(err != CHIAKI_ERR_SUCCESS) - { - chiaki_stop_pipe_fini(&ctrl->stop_pipe); - return err; - } + goto error_notif_mutex; + chiaki_thread_set_name(&ctrl->thread, "Chiaki Ctrl"); return err; + +error_notif_mutex: + chiaki_mutex_fini(&ctrl->notif_mutex); +error_notif_pipe: + chiaki_stop_pipe_fini(&ctrl->notif_pipe); + return err; } CHIAKI_EXPORT void chiaki_ctrl_stop(ChiakiCtrl *ctrl) { - chiaki_stop_pipe_stop(&ctrl->stop_pipe); + ChiakiErrorCode err = chiaki_mutex_lock(&ctrl->notif_mutex); + assert(err == CHIAKI_ERR_SUCCESS); + ctrl->should_stop = true; + chiaki_stop_pipe_stop(&ctrl->notif_pipe); + chiaki_mutex_unlock(&ctrl->notif_mutex); } CHIAKI_EXPORT ChiakiErrorCode chiaki_ctrl_join(ChiakiCtrl *ctrl) { ChiakiErrorCode err = chiaki_thread_join(&ctrl->thread, NULL); - chiaki_stop_pipe_fini(&ctrl->stop_pipe); + chiaki_stop_pipe_fini(&ctrl->notif_pipe); + chiaki_mutex_fini(&ctrl->notif_mutex); return err; } +CHIAKI_EXPORT void chiaki_ctrl_set_login_pin(ChiakiCtrl *ctrl, uint8_t *pin, size_t pin_size) +{ + ChiakiErrorCode err = chiaki_mutex_lock(&ctrl->notif_mutex); + assert(err == CHIAKI_ERR_SUCCESS); + ctrl->login_pin_entered = true; + ctrl->login_pin = pin; + ctrl->login_pin_size = pin_size; + chiaki_stop_pipe_stop(&ctrl->notif_pipe); + chiaki_mutex_unlock(&ctrl->notif_mutex); +} + static ChiakiErrorCode ctrl_connect(ChiakiCtrl *ctrl); static void ctrl_message_received(ChiakiCtrl *ctrl, uint16_t msg_type, uint8_t *payload, size_t payload_size); @@ -108,6 +145,9 @@ static void *ctrl_thread_func(void *user) CHIAKI_LOGI(ctrl->session->log, "Ctrl connected"); + err = chiaki_mutex_lock(&ctrl->notif_mutex); + assert(err == CHIAKI_ERR_SUCCESS); + while(true) { bool overflow = false; @@ -141,11 +181,32 @@ static void *ctrl_thread_func(void *user) break; } - err = chiaki_stop_pipe_select_single(&ctrl->stop_pipe, ctrl->sock, UINT64_MAX); - if(err != CHIAKI_ERR_SUCCESS) + chiaki_mutex_unlock(&ctrl->notif_mutex); + err = chiaki_stop_pipe_select_single(&ctrl->notif_pipe, ctrl->sock, UINT64_MAX); + chiaki_mutex_lock(&ctrl->notif_mutex); + if(err == CHIAKI_ERR_CANCELED) { - if(err == CHIAKI_ERR_CANCELED) + if(ctrl->should_stop) + { CHIAKI_LOGI(ctrl->session->log, "Ctrl requested to stop"); + break; + } + + if(ctrl->login_pin_entered) + { + ctrl_message_send(ctrl, CTRL_MESSAGE_TYPE_LOGIN_PIN_REP, ctrl->login_pin, ctrl->login_pin_size); + ctrl->login_pin_entered = false; + chiaki_stop_pipe_reset(&ctrl->notif_pipe); + } + else + { + CHIAKI_LOGE(ctrl->session->log, "Ctrl notif pipe set without state"); + break; + } + } + else if(err != CHIAKI_ERR_SUCCESS) + { + CHIAKI_LOGE(ctrl->session->log, "Ctrl select error: %s", chiaki_error_string(err)); break; } @@ -160,6 +221,8 @@ static void *ctrl_thread_func(void *user) ctrl->recv_buf_size += received; } + chiaki_mutex_unlock(&ctrl->notif_mutex); + CHIAKI_SOCKET_CLOSE(ctrl->sock); return NULL; @@ -194,10 +257,6 @@ static ChiakiErrorCode ctrl_message_send(ChiakiCtrl *ctrl, CtrlMessageType type, return CHIAKI_ERR_SUCCESS; } - -static void ctrl_message_received_session_id(ChiakiCtrl *ctrl, uint8_t *payload, size_t payload_size); -static void ctrl_message_received_heartbeat_req(ChiakiCtrl *ctrl, uint8_t *payload, size_t payload_size); - static void ctrl_message_received(ChiakiCtrl *ctrl, uint16_t msg_type, uint8_t *payload, size_t payload_size) { if(payload_size > 0) @@ -218,6 +277,9 @@ static void ctrl_message_received(ChiakiCtrl *ctrl, uint16_t msg_type, uint8_t * case CTRL_MESSAGE_TYPE_HEARTBEAT_REQ: ctrl_message_received_heartbeat_req(ctrl, payload, payload_size); break; + case CTRL_MESSAGE_TYPE_LOGIN_PIN_REQ: + ctrl_message_received_login_pin_req(ctrl, payload, payload_size); + break; default: CHIAKI_LOGW(ctrl->session->log, "Received Ctrl Message with unknown type %#x", msg_type); chiaki_log_hexdump(ctrl->session->log, CHIAKI_LOG_VERBOSE, payload, payload_size); @@ -265,7 +327,7 @@ static void ctrl_message_received_session_id(ChiakiCtrl *ctrl, uint8_t *payload, continue; if(c >= '0' && c <= '9') continue; - CHIAKI_LOGE(ctrl->session->log, "Received Session Id contains invalid characters"); + CHIAKI_LOGE(ctrl->session->log, "Ctrl received Session Id contains invalid characters"); return; } @@ -276,19 +338,32 @@ static void ctrl_message_received_session_id(ChiakiCtrl *ctrl, uint8_t *payload, chiaki_mutex_unlock(&ctrl->session->state_mutex); chiaki_cond_signal(&ctrl->session->state_cond); - CHIAKI_LOGI(ctrl->session->log, "Received valid Session Id: %s", ctrl->session->session_id); + CHIAKI_LOGI(ctrl->session->log, "Ctrl received valid Session Id: %s", ctrl->session->session_id); } static void ctrl_message_received_heartbeat_req(ChiakiCtrl *ctrl, uint8_t *payload, size_t payload_size) { if(payload_size != 0) - CHIAKI_LOGW(ctrl->session->log, "Received Heartbeat request with non-empty payload"); + CHIAKI_LOGW(ctrl->session->log, "Ctrl received Heartbeat request with non-empty payload"); - CHIAKI_LOGI(ctrl->session->log, "Received Ctrl Heartbeat, sending reply"); + CHIAKI_LOGI(ctrl->session->log, "Ctrl received Heartbeat, sending reply"); ctrl_message_send(ctrl, CTRL_MESSAGE_TYPE_HEARTBEAT_REP, NULL, 0); } +static void ctrl_message_received_login_pin_req(ChiakiCtrl *ctrl, uint8_t *payload, size_t payload_size) +{ + if(payload_size != 0) + CHIAKI_LOGW(ctrl->session->log, "Ctrl received Login PIN request with non-empty payload"); + + CHIAKI_LOGI(ctrl->session->log, "Ctrl received Login PIN request"); + + chiaki_mutex_lock(&ctrl->session->state_mutex); + ctrl->session->ctrl_login_pin_requested = true; + chiaki_mutex_unlock(&ctrl->session->state_mutex); + chiaki_cond_signal(&ctrl->session->state_cond); +} + typedef struct ctrl_response_t { @@ -322,6 +397,7 @@ static void parse_ctrl_response(CtrlResponse *response, ChiakiHttpResponse *http static ChiakiErrorCode ctrl_connect(ChiakiCtrl *ctrl) { + ctrl->crypt_counter_local = 0; ctrl->crypt_counter_remote = 0; ChiakiSession *session = ctrl->session; @@ -361,7 +437,7 @@ static ChiakiErrorCode ctrl_connect(ChiakiCtrl *ctrl) uint8_t auth_enc[CHIAKI_RPCRYPT_KEY_SIZE]; - ChiakiErrorCode err = chiaki_rpcrypt_encrypt(&session->rpcrypt, 0, (uint8_t *)session->connect_info.regist_key, auth_enc, CHIAKI_RPCRYPT_KEY_SIZE); + ChiakiErrorCode err = chiaki_rpcrypt_encrypt(&session->rpcrypt, ctrl->crypt_counter_local++, (uint8_t *)session->connect_info.regist_key, auth_enc, CHIAKI_RPCRYPT_KEY_SIZE); if(err != CHIAKI_ERR_SUCCESS) goto error; char auth_b64[CHIAKI_RPCRYPT_KEY_SIZE*2]; @@ -370,7 +446,7 @@ static ChiakiErrorCode ctrl_connect(ChiakiCtrl *ctrl) goto error; uint8_t did_enc[CHIAKI_RP_DID_SIZE]; - err = chiaki_rpcrypt_encrypt(&session->rpcrypt, 1, (uint8_t *)session->connect_info.did, did_enc, CHIAKI_RP_DID_SIZE); + err = chiaki_rpcrypt_encrypt(&session->rpcrypt, ctrl->crypt_counter_local++, (uint8_t *)session->connect_info.did, did_enc, CHIAKI_RP_DID_SIZE); if(err != CHIAKI_ERR_SUCCESS) goto error; char did_b64[CHIAKI_RP_DID_SIZE*2]; @@ -382,7 +458,7 @@ static ChiakiErrorCode ctrl_connect(ChiakiCtrl *ctrl) size_t ostype_len = strlen(SESSION_OSTYPE) + 1; if(ostype_len > sizeof(ostype_enc)) goto error; - err = chiaki_rpcrypt_encrypt(&session->rpcrypt, 2, (uint8_t *)SESSION_OSTYPE, ostype_enc, ostype_len); + err = chiaki_rpcrypt_encrypt(&session->rpcrypt, ctrl->crypt_counter_local++, (uint8_t *)SESSION_OSTYPE, ostype_enc, ostype_len); if(err != CHIAKI_ERR_SUCCESS) goto error; char ostype_b64[256]; @@ -422,7 +498,7 @@ static ChiakiErrorCode ctrl_connect(ChiakiCtrl *ctrl) size_t header_size; size_t received_size; - err = chiaki_recv_http_header(sock, buf, sizeof(buf), &header_size, &received_size, &ctrl->stop_pipe, CTRL_EXPECT_TIMEOUT); + err = chiaki_recv_http_header(sock, buf, sizeof(buf), &header_size, &received_size, &ctrl->notif_pipe, CTRL_EXPECT_TIMEOUT); if(err != CHIAKI_ERR_SUCCESS) { if(err != CHIAKI_ERR_CANCELED) diff --git a/lib/src/session.c b/lib/src/session.c index c0052df..112221e 100644 --- a/lib/src/session.c +++ b/lib/src/session.c @@ -161,6 +161,10 @@ CHIAKI_EXPORT ChiakiErrorCode chiaki_session_init(ChiakiSession *session, Chiaki session->should_stop = false; session->ctrl_session_id_received = false; + session->ctrl_login_pin_requested = false; + session->login_pin_entered = false; + session->login_pin = NULL; + session->login_pin_size = 0; err = chiaki_stream_connection_init(&session->stream_connection, session); if(err != CHIAKI_ERR_SUCCESS) @@ -204,6 +208,7 @@ CHIAKI_EXPORT void chiaki_session_fini(ChiakiSession *session) { if(!session) return; + free(session->login_pin); free(session->quit_reason_str); chiaki_stream_connection_fini(&session->stream_connection); chiaki_stop_pipe_fini(&session->stop_pipe); @@ -253,6 +258,22 @@ CHIAKI_EXPORT ChiakiErrorCode chiaki_session_set_controller_state(ChiakiSession return CHIAKI_ERR_SUCCESS; } +CHIAKI_EXPORT ChiakiErrorCode chiaki_session_set_login_pin(ChiakiSession *session, uint8_t *pin, size_t pin_size) +{ + uint8_t *buf = malloc(pin_size); + memcpy(buf, pin, pin_size); + if(!buf) + return CHIAKI_ERR_MEMORY; + ChiakiErrorCode err = chiaki_mutex_lock(&session->state_mutex); + assert(err == CHIAKI_ERR_SUCCESS); + if(session->login_pin_entered) + free(session->login_pin); + session->login_pin_entered = true; + session->login_pin = buf; + session->login_pin_size = pin_size; + chiaki_mutex_unlock(&session->state_mutex); +} + static void session_send_event(ChiakiSession *session, ChiakiEvent *event) { if(!session->event_cb) @@ -267,8 +288,24 @@ static bool session_check_state_pred(void *user) { ChiakiSession *session = user; return session->should_stop - || session->ctrl_failed - || session->ctrl_session_id_received; + || session->ctrl_failed; +} + +static bool session_check_state_pred_ctrl_start(void *user) +{ + ChiakiSession *session = user; + return session->should_stop + || session->ctrl_failed + || session->ctrl_session_id_received + || session->ctrl_login_pin_requested; +} + +static bool session_check_state_pred_pin(void *user) +{ + ChiakiSession *session = user; + return session->should_stop + || session->ctrl_failed + || session->login_pin_entered; } #define ENABLE_SENKUSHA @@ -312,11 +349,36 @@ static void *session_thread_func(void *arg) if(err != CHIAKI_ERR_SUCCESS) QUIT(quit); - chiaki_cond_timedwait_pred(&session->state_cond, &session->state_mutex, SESSION_EXPECT_TIMEOUT_MS, session_check_state_pred, session); + chiaki_cond_timedwait_pred(&session->state_cond, &session->state_mutex, SESSION_EXPECT_TIMEOUT_MS, session_check_state_pred_ctrl_start, session); CHECK_STOP(quit_ctrl); + if(session->ctrl_failed) + goto ctrl_failed; + + if(session->ctrl_login_pin_requested) + { + session->ctrl_login_pin_requested = false; + CHIAKI_LOGI(session->log, "Ctrl requested Login PIN"); + ChiakiEvent event = { 0 }; + event.type = CHIAKI_EVENT_LOGIN_PIN_REQUEST; + session_send_event(session, &event); + + chiaki_cond_timedwait_pred(&session->state_cond, &session->state_mutex, UINT64_MAX, session_check_state_pred_pin, session); + CHECK_STOP(quit_ctrl); + if(session->ctrl_failed) + goto ctrl_failed; + + assert(session->login_pin_entered && session->login_pin); + chiaki_ctrl_set_login_pin(&session->ctrl, session->login_pin, session->login_pin_size); + session->login_pin_entered = false; + free(session->login_pin); + session->login_pin = NULL; + session->login_pin_size = 0; + } + if(!session->ctrl_session_id_received) { +ctrl_failed: CHIAKI_LOGE(session->log, "Ctrl has failed, shutting down"); if(session->quit_reason == CHIAKI_QUIT_REASON_NONE) session->quit_reason = CHIAKI_QUIT_REASON_CTRL_UNKNOWN; diff --git a/lib/src/stoppipe.c b/lib/src/stoppipe.c index 60d402c..fd69d91 100644 --- a/lib/src/stoppipe.c +++ b/lib/src/stoppipe.c @@ -136,3 +136,16 @@ CHIAKI_EXPORT ChiakiErrorCode chiaki_stop_pipe_select_single(ChiakiStopPipe *sto return CHIAKI_ERR_TIMEOUT; #endif } + +CHIAKI_EXPORT ChiakiErrorCode chiaki_stop_pipe_reset(ChiakiStopPipe *stop_pipe) +{ +#ifdef _WIN32 + BOOL r = WSAResetEvent(stop_pipe->event); + return r ? CHIAKI_ERR_SUCCESS : CHIAKI_ERR_UNKNOWN; +#else + uint8_t v; + int r; + while((r = read(stop_pipe->fds[0], &v, sizeof(v))) > 0); + return r < 0 ? CHIAKI_ERR_UNKNOWN : CHIAKI_ERR_SUCCESS; +#endif +}