mirror of
https://github.com/mitch030504/Wiicompiled_VR_Frame.git
synced 2026-10-06 07:00:30 +02:00
fix: harden Discord IPC retries
This commit is contained in:
1 parent
3fc1555871
commit
36faa76dc0
1 file changed
+81
-24
@@ -10,6 +10,7 @@
|
|||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <optional>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
@@ -32,6 +33,7 @@ namespace {
|
|||||||
constexpr uint32_t kHandshakeOpcode = 0;
|
constexpr uint32_t kHandshakeOpcode = 0;
|
||||||
constexpr uint32_t kFrameOpcode = 1;
|
constexpr uint32_t kFrameOpcode = 1;
|
||||||
constexpr size_t kMaxClientIdLength = 32;
|
constexpr size_t kMaxClientIdLength = 32;
|
||||||
|
constexpr auto kConnectionRetryCooldown = std::chrono::seconds(1);
|
||||||
|
|
||||||
bool IsClientId(std::string_view value) {
|
bool IsClientId(std::string_view value) {
|
||||||
return !value.empty() && value.size() <= kMaxClientIdLength &&
|
return !value.empty() && value.size() <= kMaxClientIdLength &&
|
||||||
@@ -87,32 +89,48 @@ std::string BuildActivityPayload(const Activity& activity) {
|
|||||||
bool hasActivityField = false;
|
bool hasActivityField = false;
|
||||||
AppendJsonString(json, hasActivityField, "details", activity.details);
|
AppendJsonString(json, hasActivityField, "details", activity.details);
|
||||||
AppendJsonString(json, hasActivityField, "state", activity.state);
|
AppendJsonString(json, hasActivityField, "state", activity.state);
|
||||||
|
const auto appendSection = [&](std::string_view name, const auto& append) {
|
||||||
|
if (hasActivityField) {
|
||||||
|
json << ',';
|
||||||
|
}
|
||||||
|
json << '\"' << name << "\":{";
|
||||||
|
append();
|
||||||
|
json << '}';
|
||||||
|
hasActivityField = true;
|
||||||
|
};
|
||||||
|
const bool hasAssets = !activity.largeImageKey.empty() || !activity.largeImageText.empty() ||
|
||||||
|
!activity.smallImageKey.empty() || !activity.smallImageText.empty();
|
||||||
|
if (hasAssets) {
|
||||||
|
appendSection("assets", [&] {
|
||||||
|
bool hasAsset = false;
|
||||||
|
AppendJsonString(json, hasAsset, "large_image", activity.largeImageKey);
|
||||||
|
AppendJsonString(json, hasAsset, "large_text", activity.largeImageText);
|
||||||
|
AppendJsonString(json, hasAsset, "small_image", activity.smallImageKey);
|
||||||
|
AppendJsonString(json, hasAsset, "small_text", activity.smallImageText);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (activity.startTimestamp > 0 || activity.endTimestamp > 0) {
|
||||||
|
appendSection("timestamps", [&] {
|
||||||
|
if (activity.startTimestamp > 0) {
|
||||||
|
json << "\"start\":" << activity.startTimestamp;
|
||||||
|
}
|
||||||
|
if (activity.endTimestamp > 0) {
|
||||||
|
if (activity.startTimestamp > 0) {
|
||||||
|
json << ',';
|
||||||
|
}
|
||||||
|
json << "\"end\":" << activity.endTimestamp;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (activity.partySize > 0 && activity.partyMax > 0) {
|
||||||
|
appendSection("party", [&] {
|
||||||
|
json << "\"size\":[" << activity.partySize << ',' << activity.partyMax << ']';
|
||||||
|
});
|
||||||
|
}
|
||||||
if (hasActivityField) {
|
if (hasActivityField) {
|
||||||
json << ',';
|
json << ',';
|
||||||
}
|
}
|
||||||
json << "\"assets\":{";
|
json << "\"instance\":false}}}";
|
||||||
bool hasAsset = false;
|
|
||||||
AppendJsonString(json, hasAsset, "large_image", activity.largeImageKey);
|
|
||||||
AppendJsonString(json, hasAsset, "large_text", activity.largeImageText);
|
|
||||||
AppendJsonString(json, hasAsset, "small_image", activity.smallImageKey);
|
|
||||||
AppendJsonString(json, hasAsset, "small_text", activity.smallImageText);
|
|
||||||
json << "},\"timestamps\":{";
|
|
||||||
bool hasTimestamp = false;
|
|
||||||
if (activity.startTimestamp > 0) {
|
|
||||||
json << "\"start\":" << activity.startTimestamp;
|
|
||||||
hasTimestamp = true;
|
|
||||||
}
|
|
||||||
if (activity.endTimestamp > 0) {
|
|
||||||
if (hasTimestamp) {
|
|
||||||
json << ',';
|
|
||||||
}
|
|
||||||
json << "\"end\":" << activity.endTimestamp;
|
|
||||||
}
|
|
||||||
json << "},\"party\":{";
|
|
||||||
if (activity.partySize > 0 || activity.partyMax > 0) {
|
|
||||||
json << "\"size\":[" << activity.partySize << ',' << activity.partyMax << ']';
|
|
||||||
}
|
|
||||||
json << "},\"instance\":false}}}";
|
|
||||||
return json.str();
|
return json.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,10 +213,12 @@ private:
|
|||||||
}
|
}
|
||||||
const std::string handshake = "{\"v\":1,\"client_id\":\"" + clientId + "\"}";
|
const std::string handshake = "{\"v\":1,\"client_id\":\"" + clientId + "\"}";
|
||||||
if (!WriteFrameLocked(kHandshakeOpcode, handshake)) {
|
if (!WriteFrameLocked(kHandshakeOpcode, handshake)) {
|
||||||
|
RecordFailedConnectionLocked();
|
||||||
CloseLocked();
|
CloseLocked();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!ReadReadyLocked()) {
|
if (!ReadReadyLocked()) {
|
||||||
|
RecordFailedConnectionLocked();
|
||||||
CloseLocked();
|
CloseLocked();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -213,15 +233,28 @@ private:
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!WriteFrameLocked(kFrameOpcode, BuildActivityPayload(activity_))) {
|
if (!WriteFrameLocked(kFrameOpcode, BuildActivityPayload(activity_))) {
|
||||||
|
RecordFailedConnectionLocked();
|
||||||
CloseLocked();
|
CloseLocked();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ConnectionRetryAllowedLocked() const {
|
||||||
|
return !lastFailedConnectionAttempt_ ||
|
||||||
|
std::chrono::steady_clock::now() - *lastFailedConnectionAttempt_ >= kConnectionRetryCooldown;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RecordFailedConnectionLocked() {
|
||||||
|
lastFailedConnectionAttempt_ = std::chrono::steady_clock::now();
|
||||||
|
}
|
||||||
|
|
||||||
#if defined(_WIN32)
|
#if defined(_WIN32)
|
||||||
bool ConnectLocked() {
|
bool ConnectLocked() {
|
||||||
if (connected_) {
|
if (connected_) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (!ConnectionRetryAllowedLocked()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
for (unsigned int index = 0; index < 10; ++index) {
|
for (unsigned int index = 0; index < 10; ++index) {
|
||||||
const std::string name = "\\\\.\\pipe\\discord-ipc-" + std::to_string(index);
|
const std::string name = "\\\\.\\pipe\\discord-ipc-" + std::to_string(index);
|
||||||
handle_ = ::CreateFileA(name.c_str(), GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, nullptr);
|
handle_ = ::CreateFileA(name.c_str(), GENERIC_READ | GENERIC_WRITE, 0, nullptr, OPEN_EXISTING, 0, nullptr);
|
||||||
@@ -229,9 +262,11 @@ private:
|
|||||||
DWORD mode = PIPE_READMODE_BYTE;
|
DWORD mode = PIPE_READMODE_BYTE;
|
||||||
::SetNamedPipeHandleState(handle_, &mode, nullptr, nullptr);
|
::SetNamedPipeHandleState(handle_, &mode, nullptr, nullptr);
|
||||||
connected_ = true;
|
connected_ = true;
|
||||||
|
lastFailedConnectionAttempt_.reset();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
RecordFailedConnectionLocked();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -248,9 +283,25 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool ReadAllLocked(uint8_t* data, size_t size) {
|
bool ReadAllLocked(uint8_t* data, size_t size) {
|
||||||
|
const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(1);
|
||||||
while (size != 0) {
|
while (size != 0) {
|
||||||
|
if (std::chrono::steady_clock::now() >= deadline) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
DWORD available = 0;
|
||||||
|
if (!::PeekNamedPipe(handle_, nullptr, 0, nullptr, &available, nullptr)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (available == 0) {
|
||||||
|
if (std::chrono::steady_clock::now() >= deadline) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
::Sleep(10);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
DWORD read = 0;
|
DWORD read = 0;
|
||||||
if (!::ReadFile(handle_, data, static_cast<DWORD>(size), &read, nullptr) || read == 0) {
|
const DWORD requested = static_cast<DWORD>(std::min<size_t>(size, available));
|
||||||
|
if (!::ReadFile(handle_, data, requested, &read, nullptr) || read == 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
data += read;
|
data += read;
|
||||||
@@ -273,6 +324,9 @@ private:
|
|||||||
if (connected_) {
|
if (connected_) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (!ConnectionRetryAllowedLocked()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
std::array<std::string, 5> roots{};
|
std::array<std::string, 5> roots{};
|
||||||
size_t rootCount = 0;
|
size_t rootCount = 0;
|
||||||
if (const char* runtimeDir = std::getenv("XDG_RUNTIME_DIR"); runtimeDir && *runtimeDir) {
|
if (const char* runtimeDir = std::getenv("XDG_RUNTIME_DIR"); runtimeDir && *runtimeDir) {
|
||||||
@@ -307,11 +361,13 @@ private:
|
|||||||
::setsockopt(socketFd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
|
::setsockopt(socketFd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout));
|
||||||
fd_ = socketFd;
|
fd_ = socketFd;
|
||||||
connected_ = true;
|
connected_ = true;
|
||||||
|
lastFailedConnectionAttempt_.reset();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
::close(socketFd);
|
::close(socketFd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
RecordFailedConnectionLocked();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -375,6 +431,7 @@ private:
|
|||||||
std::mutex mutex_;
|
std::mutex mutex_;
|
||||||
bool connected_ = false;
|
bool connected_ = false;
|
||||||
bool customClient_ = false;
|
bool customClient_ = false;
|
||||||
|
std::optional<std::chrono::steady_clock::time_point> lastFailedConnectionAttempt_;
|
||||||
std::string basicClientId_;
|
std::string basicClientId_;
|
||||||
std::string clientId_;
|
std::string clientId_;
|
||||||
Activity basicActivity_;
|
Activity basicActivity_;
|
||||||
|
|||||||
Reference in new issue
Block a user