/*********** Ping Pong handling ***********/ /// <summary> /// Receives the ping messages and sends pong messages. /// </summary> /// <param name="result">The ping ping async result.</param> public void PingPongHandler(IAsyncResult result) { PingPongAsyncResult state = (PingPongAsyncResult)result.AsyncState; Socket remoteSocket = state.RemoteSocket; try { _timeoutTimer.Stop(); int bytesRead = remoteSocket.EndReceive(result); if (bytesRead > 0) { byte[] pingPacketBuffer = new byte[bytesRead]; Buffer.BlockCopy(state.Buffer, 0, pingPacketBuffer, 0, pingPacketBuffer.Length); remoteSocket.Send(StatusPacket, StatusPacket.Length, SocketFlags.None); _timeoutTimer.Start(); remoteSocket.BeginReceive(state.Buffer, 0, state.Buffer.Length, SocketFlags.None, PingPongHandler, state); } else { Thread.Sleep(250); CloseConnection(); OnPs4Disconnected?.Invoke(this, "PS4 disconnected. Ping Pong socket got closed."); } } catch (SocketException) { if (remoteSocket != null) { CloseConnection(); OnPs4Disconnected?.Invoke(this, "PS4 disconnected. Ping Pong socket exception."); } } catch (ObjectDisposedException) // When the socket gets closed from outside (session already removed) { } catch (Exception e) { OnPs4Disconnected?.Invoke(this, "PS4 disconnected. Unknown reason: " + e); } }
/*********** Control request ***********/ private void HandleControlRequest(string rpNonce, IPEndPoint ps4Endpoint, PS4RemotePlayData ps4RemotePlayData) { bool connectedSuccess = false; Socket socket = null; try { IPEndPoint ipEndPoint = new IPEndPoint(ps4Endpoint.Address, ControlPort); socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); socket.Connect(ipEndPoint); byte[] rpKeyBuffer = HexUtil.Unhexlify(ps4RemotePlayData.RemotePlay.RpKey); byte[] rpNonceDecoded = Convert.FromBase64String(rpNonce); OnPs4LogInfo?.Invoke(this, "RP-Nonce from \"/sce/rp/session\" response: " + HexUtil.Hexlify(rpNonceDecoded)); Session session = CryptoService.GetSessionForControl(rpKeyBuffer, rpNonceDecoded); string controlAesKey = HexUtil.Hexlify(CryptoService.GetSessionAesKeyForControl(rpKeyBuffer, rpNonceDecoded)); string controlNonce = HexUtil.Hexlify(CryptoService.GetSessionNonceValueForControl(rpNonceDecoded)); OnPs4LogInfo?.Invoke(this, "!!! Control AES Key: " + controlAesKey); OnPs4LogInfo?.Invoke(this, "!!! Control AES Nonce: " + controlNonce + Environment.NewLine); byte[] registrationKeyBuffer = HexUtil.Unhexlify(ps4RemotePlayData.RemotePlay.RegistrationKey); byte[] registrationKeyPadding = { 0, 0, 0, 0, 0, 0, 0, 0 }; byte[] encryptedRegistrationKey = session.Encrypt(ByteUtil.ConcatenateArrays(registrationKeyBuffer, registrationKeyPadding)); string encodedRegistrationKey = Convert.ToBase64String(encryptedRegistrationKey); byte[] randomDid = Guid.NewGuid().ToByteArray(); byte[] didPrefix = { 0x00, 0x18, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x80 }; byte[] didPadding = { 48, 48, 48, 48, 48, 48, 48 }; byte[] encryptedDid = session.Encrypt(ByteUtil.ConcatenateArrays(didPrefix, randomDid, didPadding)); string encodedDid = Convert.ToBase64String(encryptedDid); string osType = "Win10.0.0"; byte[] osTypeBuffer = Encoding.UTF8.GetBytes(osType); byte[] osTypePadding = { 0 }; byte[] encryptedOsType = session.Encrypt(ByteUtil.ConcatenateArrays(osTypeBuffer, osTypePadding)); string encodedOsType = Convert.ToBase64String(encryptedOsType); string host = ps4Endpoint.Address + ":" + ControlPort; string requestData = "GET /sce/rp/session/ctrl HTTP/1.1\r\n" + $"HOST: {host}\r\n" + "User-Agent: remoteplay Windows\r\n" + "Connection: keep-alive\r\n" + "Content-Length: 0\r\n" + $"RP-Auth: {encodedRegistrationKey}\r\n" + "RP-Version: 8.0\r\n" + $"RP-Did: {encodedDid}\r\n" + "RP-ControllerType: 3\r\n" + "RP-ClientType: 11\r\n" + $"RP-OSType: {encodedOsType}\r\n" + "RP-ConPath: 1\r\n" + "\r\n"; socket.Send(Encoding.UTF8.GetBytes(requestData)); byte[] receiveBuffer = new byte[8192]; int readBytes = socket.Receive(receiveBuffer); byte[] response = new byte[readBytes]; Buffer.BlockCopy(receiveBuffer, 0, response, 0, response.Length); string httpResponse = Encoding.ASCII.GetString(receiveBuffer, 0, readBytes); HttpStatusCode statusCode = HttpUtils.GetStatusCode(httpResponse); if (statusCode == HttpStatusCode.OK) { OnPs4LogInfo?.Invoke(this, "\"/sce/rp/session/ctrl\" response: " + Environment.NewLine + httpResponse.Trim() + Environment.NewLine); OnPs4LogInfo?.Invoke(this, "TCP connection to PS4 established" + Environment.NewLine); _clientSocket = socket; _clientSocket.ReceiveTimeout = 0; PingPongAsyncResult connectionStateObject = new PingPongAsyncResult { RemoteSocket = _clientSocket }; connectionStateObject.RemoteSocket.BeginReceive(connectionStateObject.Buffer, 0, connectionStateObject.Buffer.Length, SocketFlags.None, PingPongHandler, connectionStateObject); OnPs4ConnectionSuccess?.Invoke(this, EventArgs.Empty); connectedSuccess = true; InitializeRemotePlayChannel(session, ps4Endpoint); } } catch (Exception e) { OnPs4ConnectionError?.Invoke(this, "Exception occured /sce/rp/session/ctrl" + e); } finally { if (!connectedSuccess) { socket?.Close(); } } }