diff options
-rw-r--r-- | protocols/Steam/Steam.vcxproj | 1 | ||||
-rw-r--r-- | protocols/Steam/Steam.vcxproj.filters | 3 | ||||
-rw-r--r-- | protocols/Steam/src/stdafx.h | 2 | ||||
-rw-r--r-- | protocols/Steam/src/steam_login.cpp | 76 | ||||
-rw-r--r-- | protocols/Steam/src/steam_proto.cpp | 2 | ||||
-rw-r--r-- | protocols/Steam/src/steam_proto.h | 25 | ||||
-rw-r--r-- | protocols/Steam/src/steam_server.cpp | 153 | ||||
-rw-r--r-- | protocols/Steam/src/steam_utils.cpp | 57 | ||||
-rw-r--r-- | protocols/Steam/src/steam_ws.cpp | 237 |
9 files changed, 321 insertions, 235 deletions
diff --git a/protocols/Steam/Steam.vcxproj b/protocols/Steam/Steam.vcxproj index ce3c8d4cc9..e1a2f61bfa 100644 --- a/protocols/Steam/Steam.vcxproj +++ b/protocols/Steam/Steam.vcxproj @@ -68,6 +68,7 @@ <ClCompile Include="src\steam_request.cpp" />
<ClCompile Include="src\steam_server.cpp" />
<ClCompile Include="src\steam_utils.cpp" />
+ <ClCompile Include="src\steam_ws.cpp" />
<ClCompile Include="src\steam_xstatus.cpp" />
<ClInclude Include="src\api\app_info.h" />
<ClInclude Include="src\api\avatar.h" />
diff --git a/protocols/Steam/Steam.vcxproj.filters b/protocols/Steam/Steam.vcxproj.filters index 13f6be78d9..e11da99ae2 100644 --- a/protocols/Steam/Steam.vcxproj.filters +++ b/protocols/Steam/Steam.vcxproj.filters @@ -84,6 +84,9 @@ <ClCompile Include="src\protobuf-c\steammessages_clientserver_friends.pb-c.cpp">
<Filter>Source Files\protobuf</Filter>
</ClCompile>
+ <ClCompile Include="src\steam_ws.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="src\resource.h">
diff --git a/protocols/Steam/src/stdafx.h b/protocols/Steam/src/stdafx.h index 368e3e5bf6..2beb51b714 100644 --- a/protocols/Steam/src/stdafx.h +++ b/protocols/Steam/src/stdafx.h @@ -66,6 +66,8 @@ extern HANDLE hExtraXStatus; #define now() time(0)
+int64_t getRandomInt();
+
#include "steam_dialogs.h"
#include "api/enums.h"
diff --git a/protocols/Steam/src/steam_login.cpp b/protocols/Steam/src/steam_login.cpp index c852f2e6f7..d607657485 100644 --- a/protocols/Steam/src/steam_login.cpp +++ b/protocols/Steam/src/steam_login.cpp @@ -20,18 +20,11 @@ bool CSteamProto::IsMe(const char *steamId) return m_iSteamId == _atoi64(steamId); } -void CSteamProto::LoginFailed() -{ - m_bTerminated = true; - - m_iStatus = m_iDesiredStatus = ID_STATUS_OFFLINE; - ProtoBroadcastAck(NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)m_iStatus, m_iStatus); -} - void CSteamProto::Logout() { m_bTerminated = true; + m_iStatus = m_iDesiredStatus = ID_STATUS_OFFLINE; ProtoBroadcastAck(NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)m_iStatus, m_iStatus); } @@ -55,13 +48,15 @@ void CSteamProto::OnGotHosts(const JSONNode &root, void*) void CSteamProto::Login() { + m_iSessionId = 0; + CMsgClientHello hello; hello.protocol_version = STEAM_PROTOCOL_VERSION; hello.has_protocol_version = true; WSSend(EMsg::ClientHello, hello); ptrA username(getUStringA("Username")); if (username == NULL) { - LoginFailed(); + Logout(); return; } @@ -74,7 +69,7 @@ void CSteamProto::OnGotRsaKey(const uint8_t *buf, size_t cbLen) { proto::AuthenticationGetPasswordRSAPublicKeyResponse reply(buf, cbLen); if (reply == nullptr || !reply->publickey_exp || !reply->publickey_mod) { - LoginFailed(); + Logout(); return; } @@ -125,7 +120,7 @@ INT_PTR CALLBACK CSteamProto::EnterEmailCode(void *param) ppro->SendConfirmationCode(true, T2Utf(es.ptszResult)); mir_free(es.ptszResult); } - else ppro->LoginFailed(); + else ppro->Logout(); return 0; } @@ -140,7 +135,7 @@ INT_PTR CALLBACK CSteamProto::EnterTotpCode(void *param) ppro->SendConfirmationCode(false, T2Utf(es.ptszResult)); mir_free(es.ptszResult); } - else ppro->LoginFailed(); + else ppro->Logout(); return 0; } @@ -148,7 +143,7 @@ void CSteamProto::OnGotConfirmationCode(const uint8_t *buf, size_t cbLen) { proto::AuthenticationUpdateAuthSessionWithSteamGuardCodeResponse reply(buf, cbLen); if (reply == nullptr) - LoginFailed(); + Logout(); else SendPollRequest(); } @@ -173,7 +168,7 @@ void CSteamProto::OnBeginSession(const uint8_t *buf, size_t cbLen) { proto::AuthenticationBeginAuthSessionViaCredentialsResponse reply(buf, cbLen); if (reply == nullptr) { - LoginFailed(); + Logout(); return; } @@ -207,7 +202,7 @@ void CSteamProto::OnBeginSession(const uint8_t *buf, size_t cbLen) } debugLogA("Unsupported confirmation code: %i", conf->confirmation_type); - LoginFailed(); + Logout(); } // no confirmation needed - we've done @@ -215,7 +210,7 @@ void CSteamProto::OnBeginSession(const uint8_t *buf, size_t cbLen) } else { debugLogA("Something went wrong: %s", reply->extended_error_message); - LoginFailed(); + Logout(); } } @@ -250,7 +245,7 @@ void CSteamProto::OnPollSession(const uint8_t *buf, size_t cbLen) { proto::AuthenticationPollAuthSessionStatusResponse reply(buf, cbLen); if (reply == nullptr) { - LoginFailed(); + Logout(); return; } @@ -272,11 +267,54 @@ void CSteamProto::OnPollSession(const uint8_t *buf, size_t cbLen) m_szAccessToken = reply->access_token; m_szRefreshToken = reply->refresh_token; - OnLoggedIn(); + // sending logon packet + ptrA szAccountName(getUStringA(DBKEY_ACCOUNT_NAME)), szPassword(getUStringA("Password")); + T2Utf szMachineName(m_wszDeviceName); + + MBinBuffer machineId(getBlob(DBKEY_MACHINE_ID)); + if (!machineId.length()) { + uint8_t random[100], hashOut[20]; + Utils_GetRandom(random, sizeof(random)); + mir_sha1_hash(random, sizeof(random), hashOut); + + db_set_blob(0, m_szModuleName, DBKEY_MACHINE_ID, hashOut, sizeof(hashOut)); + machineId.append(hashOut, sizeof(hashOut)); + } + + CMsgIPAddress privateIp; + privateIp.ip_case = CMSG_IPADDRESS__IP_V4; + privateIp.v4 = 0; + + CMsgClientLogon request; + request.access_token = reply->access_token; + request.account_name = szAccountName; + request.password = szPassword; + request.machine_name = szMachineName; + request.client_language = "english"; + request.client_os_type = 16; request.has_client_os_type = true; + request.should_remember_password = false; request.has_should_remember_password = true; + request.obfuscated_private_ip = &privateIp; + request.protocol_version = STEAM_PROTOCOL_VERSION; request.has_protocol_version = true; + request.client_package_version = 1771; request.has_client_package_version = true; + request.supports_rate_limit_response = request.has_supports_rate_limit_response = true; + request.steamguard_dont_remember_computer = false; request.has_steamguard_dont_remember_computer = true; + request.chat_mode = 2; request.has_chat_mode = true; + request.cell_id = 7; request.has_cell_id = true; + request.machine_id.data = machineId.data(); + request.machine_id.len = machineId.length(); + WSSend(EMsg::ClientLogon, request); } -void CSteamProto::OnLoggedIn() +void CSteamProto::OnClientLogon(const uint8_t *buf, size_t cbLen) { + proto::MsgClientLogonResponse reply(buf, cbLen); + if (reply == nullptr) { + Logout(); + return; + } + + m_impl.m_heartBeat.Start(1000); + // go to online now ProtoBroadcastAck(NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)ID_STATUS_CONNECTING, m_iStatus = m_iDesiredStatus); diff --git a/protocols/Steam/src/steam_proto.cpp b/protocols/Steam/src/steam_proto.cpp index 27c65aee33..158b5e215b 100644 --- a/protocols/Steam/src/steam_proto.cpp +++ b/protocols/Steam/src/steam_proto.cpp @@ -263,8 +263,6 @@ int CSteamProto::SetStatus(int new_status) m_iDesiredStatus = new_status;
if (new_status == ID_STATUS_OFFLINE) {
- m_iStatus = m_iDesiredStatus = ID_STATUS_OFFLINE;
-
if (!Miranda_IsTerminated())
SetAllContactStatuses(ID_STATUS_OFFLINE);
diff --git a/protocols/Steam/src/steam_proto.h b/protocols/Steam/src/steam_proto.h index 1c042c0916..49befe2800 100644 --- a/protocols/Steam/src/steam_proto.h +++ b/protocols/Steam/src/steam_proto.h @@ -15,7 +15,7 @@ #define DBKEY_CLIENT_ID "ClientID"
#define DBKEY_STEAM_ID "SteamID"
#define DBKEY_ACCOUNT_NAME "Username"
-#define DBKEY_MACHINE_ID "MachineId"
+#define DBKEY_MACHINE_ID "DeviceId"
struct SendAuthParam
{
@@ -73,17 +73,24 @@ class CSteamProto : public PROTO<CSteamProto> friend class CSteamProto;
CSteamProto &m_proto;
- CTimer m_poll;
+ CTimer m_poll, m_heartBeat;
void OnPoll(CTimer *)
{
m_proto.SendPollRequest();
}
+ void OnHeartBeat(CTimer *)
+ {
+ m_proto.SendHeartBeat();
+ }
+
CProtoImpl(CSteamProto &pro) :
m_proto(pro),
- m_poll(Miranda_GetSystemWindow(), UINT_PTR(this))
+ m_poll(Miranda_GetSystemWindow(), UINT_PTR(this)),
+ m_heartBeat(Miranda_GetSystemWindow(), UINT_PTR(this)+1)
{
m_poll.OnEvent = Callback(this, &CProtoImpl::OnPoll);
+ m_heartBeat.OnEvent = Callback(this, &CProtoImpl::OnHeartBeat);
}
}
m_impl;
@@ -91,7 +98,7 @@ class CSteamProto : public PROTO<CSteamProto> ptrW m_password;
bool m_bTerminated, m_bPollCanceled;
time_t m_idleTS;
- int64_t m_iSteamId, m_iClientId;
+ int64_t m_iSteamId, m_iClientId, m_iSessionId;
MBinBuffer m_requestId;
int64_t GetId(const char *pszSetting);
@@ -101,7 +108,7 @@ class CSteamProto : public PROTO<CSteamProto> void SetId(MCONTACT, const char *pszSetting, int64_t id);
// polling
- CMStringA m_szRefreshToken, m_szAccessToken, m_szUmqId, m_szChatToken;
+ CMStringA m_szRefreshToken, m_szAccessToken, m_szUmqId;
ULONG hAuthProcess = 1;
ULONG hMessageProcess = 1;
int m_iPollingInterval;
@@ -131,20 +138,20 @@ class CSteamProto : public PROTO<CSteamProto> bool SendRequest(HttpRequest *request, HttpCallback callback, void *param = nullptr);
bool SendRequest(HttpRequest *request, JsonCallback callback, void *param = nullptr);
+ void SendHeartBeat();
+
// login
bool IsOnline();
bool IsMe(const char *steamId);
void Login();
- void LoginFailed();
void Logout();
- void OnLoggedIn();
-
static INT_PTR CALLBACK EnterTotpCode(void *param);
static INT_PTR CALLBACK EnterEmailCode(void *param);
- void OnBeginSession(const uint8_t *buf, size_t cbLen);
+ void OnBeginSession(const uint8_t *buf, size_t cbLen);
+ void OnClientLogon(const uint8_t *buf, size_t cbLen);
void OnGotRsaKey(const uint8_t *buf, size_t cbLen);
void OnGotConfirmationCode(const uint8_t *buf, size_t cbLen);
void OnPollSession(const uint8_t *buf, size_t cbLen);
diff --git a/protocols/Steam/src/steam_server.cpp b/protocols/Steam/src/steam_server.cpp index c2b43e5b88..d940e42ddd 100644 --- a/protocols/Steam/src/steam_server.cpp +++ b/protocols/Steam/src/steam_server.cpp @@ -17,154 +17,9 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. #include "stdafx.h" -void __cdecl CSteamProto::ServerThread(void *) +void CSteamProto::SendHeartBeat() { - // load web socket servers first if needed - int iTimeDiff = db_get_dw(0, STEAM_MODULE, DBKEY_HOSTS_DATE); - int iHostCount = db_get_dw(0, STEAM_MODULE, DBKEY_HOSTS_COUNT); - if (!iHostCount || time(0) - iTimeDiff > 3600 * 24 * 7) { // once a week - if (!SendRequest(new GetHostsRequest(), &CSteamProto::OnGotHosts)) { - LoginFailed(); - return; - } - } - - srand(time(0)); - m_ws = nullptr; - - CMStringA szHost; - do { - szHost.Format("Host%d", rand() % iHostCount); - szHost = db_get_sm(0, STEAM_MODULE, szHost); - szHost.Insert(0, "wss://"); - szHost += "/cmsocket/"; - } - while (ServerThreadStub(szHost)); -} - -bool CSteamProto::ServerThreadStub(const char *szHost) -{ - WebSocket<CSteamProto> ws(this); - - NLHR_PTR pReply(ws.connect(m_hNetlibUser, szHost)); - if (pReply == nullptr) { - debugLogA("websocket connection failed"); - return false; - } - - if (pReply->resultCode != 101) { - debugLogA("websocket connection failed: %d", pReply->resultCode); - return false; - } - - m_ws = &ws; - - debugLogA("Websocket connection succeeded"); - - // Send init packets - Login(); - - ws.run(); - m_ws = nullptr; - return false; -} - -///////////////////////////////////////////////////////////////////////////////////////// - -void WebSocket<CSteamProto>::process(const uint8_t *buf, size_t cbLen) -{ - uint32_t dwSign = *(uint32_t *)buf; - EMsg msgType = (EMsg)(dwSign & ~STEAM_PROTOCOL_MASK); - - // now process the body - if (msgType == EMsg::Multi) { - buf += 8; cbLen -= 8; - p->ProcessMulti(buf, cbLen); - } - else p->ProcessMessage(buf, cbLen); -} - -void CSteamProto::ProcessMulti(const uint8_t *buf, size_t cbLen) -{ - proto::MsgMulti pMulti(buf, cbLen); - if (pMulti == nullptr) { - debugLogA("Unable to decode multi message, exiting"); - return; - } - - debugLogA("processing %s multi message of size %d", (pMulti->size_unzipped) ? "zipped" : "normal", pMulti->message_body.len); - - ptrA tmp; - if (pMulti->size_unzipped) { - tmp = (char *)mir_alloc(pMulti->size_unzipped + 1); - cbLen = FreeImage_ZLibGUnzip((uint8_t*)tmp.get(), pMulti->size_unzipped, pMulti->message_body.data, (unsigned)pMulti->message_body.len); - if (!cbLen) { - debugLogA("Unable to unzip multi message, exiting"); - return; - } - - buf = (const uint8_t *)tmp.get(); - } - else { - buf = pMulti->message_body.data; - cbLen = pMulti->message_body.len; - } - - while ((int)cbLen > 0) { - uint32_t cbPacketLen = *(uint32_t *)buf; buf += sizeof(uint32_t); cbLen -= sizeof(uint32_t); - Netlib_Dump(HNETLIBCONN(m_ws->getConn()), buf, cbLen, false, 0); - ProcessMessage(buf, cbPacketLen); - buf += cbPacketLen; cbLen -= cbPacketLen; - } -} - -void CSteamProto::ProcessMessage(const uint8_t *buf, size_t cbLen) -{ - uint32_t dwSign = *(uint32_t *)buf; buf += sizeof(uint32_t); cbLen -= sizeof(uint32_t); - EMsg msgType = (EMsg)(dwSign & ~STEAM_PROTOCOL_MASK); - bool bIsProto = (dwSign & STEAM_PROTOCOL_MASK) != 0; - - CMsgProtoBufHeader hdr; - - if (msgType == EMsg::ChannelEncryptRequest || msgType == EMsg::ChannelEncryptResult) { - hdr.has_jobid_source = hdr.has_jobid_target = true; - hdr.jobid_source = *(int64_t *)buf; buf += sizeof(int64_t); - hdr.jobid_target = *(int64_t *)buf; buf += sizeof(int64_t); - } - else if (bIsProto) { - uint32_t hdrLen = *(uint32_t *)buf; buf += sizeof(uint32_t); cbLen -= sizeof(uint32_t); - proto::MsgProtoBufHeader tmpHeader(buf, hdrLen); - if (tmpHeader == nullptr) { - debugLogA("Unable to decode message header, exiting"); - return; - } - - memcpy(&hdr, tmpHeader, sizeof(hdr)); - buf += hdrLen; cbLen -= hdrLen; - } - else { - debugLogA("Got unknown header, exiting"); - return; - } - - MsgCallback pCallback = 0; - { - mir_cslock lck(m_csRequests); - if (auto *pReq = m_arRequests.find((ProtoRequest *)&hdr.jobid_target)) { - pCallback = pReq->pCallback; - m_arRequests.remove(pReq); - } - } - - if (pCallback) { - (this->*pCallback)(buf, cbLen); - return; - } - - // persistent callbacks - switch (msgType) { - case EMsg::ClientLogOnResponse: - OnLoggedIn(); - break; - } + CMsgClientHeartBeat packet; + packet.has_send_reply = packet.send_reply = true; + WSSend(EMsg::ClientHeartBeat, packet); } diff --git a/protocols/Steam/src/steam_utils.cpp b/protocols/Steam/src/steam_utils.cpp index ed90bc5178..800abe47e8 100644 --- a/protocols/Steam/src/steam_utils.cpp +++ b/protocols/Steam/src/steam_utils.cpp @@ -1,67 +1,12 @@ #include "stdafx.h"
-static int64_t getRandomInt()
+int64_t getRandomInt()
{
int64_t ret;
Utils_GetRandom(&ret, sizeof(ret));
return (ret >= 0) ? ret : -ret;
}
-void CSteamProto::WSSend(EMsg msgType, const ProtobufCppMessage &msg)
-{
- CMsgProtoBufHeader hdr;
- hdr.has_client_sessionid = hdr.has_steamid = hdr.has_jobid_source = hdr.has_jobid_target = true;
-
- switch (msgType) {
- case EMsg::ClientHello:
- hdr.jobid_source = -1;
- break;
-
- default:
- hdr.jobid_source = getRandomInt();
- break;
- }
-
- hdr.jobid_target = -1;
-
- WSSendHeader(msgType, hdr, msg);
-}
-
-void CSteamProto::WSSendHeader(EMsg msgType, const CMsgProtoBufHeader &hdr, const ProtobufCppMessage &msg)
-{
- uint32_t hdrLen = (uint32_t)protobuf_c_message_get_packed_size(&hdr);
- MBinBuffer hdrbuf(hdrLen);
- protobuf_c_message_pack(&hdr, (uint8_t *)hdrbuf.data());
- hdrbuf.appendBefore(&hdrLen, sizeof(hdrLen));
-
- uint32_t type = (uint32_t)msgType;
- type |= STEAM_PROTOCOL_MASK;
- hdrbuf.appendBefore(&type, sizeof(type));
-
- MBinBuffer body(protobuf_c_message_get_packed_size(&msg));
- protobuf_c_message_pack(&msg, body.data());
-
- hdrbuf.append(body);
- m_ws->sendBinary(hdrbuf.data(), hdrbuf.length());
-}
-
-void CSteamProto::WSSendService(const char *pszServiceName, const ProtobufCppMessage &msg, MsgCallback pCallback)
-{
- CMsgProtoBufHeader hdr;
- hdr.has_client_sessionid = hdr.has_steamid = hdr.has_jobid_source = hdr.has_jobid_target = true;
- hdr.jobid_source = getRandomInt();
- hdr.jobid_target = -1;
- hdr.target_job_name = (char*)pszServiceName;
- hdr.realm = 1; hdr.has_realm = true;
-
- if (pCallback) {
- mir_cslock lck(m_csRequests);
- m_arRequests.insert(new ProtoRequest(hdr.jobid_source, pCallback));
- }
-
- WSSendHeader(EMsg::ServiceMethodCallFromClientNonAuthed, hdr, msg);
-}
-
/////////////////////////////////////////////////////////////////////////////////////////
int64_t CSteamProto::GetId(MCONTACT hContact, const char *pszSetting)
diff --git a/protocols/Steam/src/steam_ws.cpp b/protocols/Steam/src/steam_ws.cpp new file mode 100644 index 0000000000..e6ee7f7d7a --- /dev/null +++ b/protocols/Steam/src/steam_ws.cpp @@ -0,0 +1,237 @@ +/* +Copyright (C) 2012-24 Miranda NG team (https://miranda-ng.org) + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation version 2 +of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "stdafx.h" + +void __cdecl CSteamProto::ServerThread(void *) +{ + // load web socket servers first if needed + int iTimeDiff = db_get_dw(0, STEAM_MODULE, DBKEY_HOSTS_DATE); + int iHostCount = db_get_dw(0, STEAM_MODULE, DBKEY_HOSTS_COUNT); + if (!iHostCount || time(0) - iTimeDiff > 3600 * 24 * 7) { // once a week + if (!SendRequest(new GetHostsRequest(), &CSteamProto::OnGotHosts)) { + Logout(); + return; + } + } + + srand(time(0)); + m_ws = nullptr; + + CMStringA szHost; + do { + szHost.Format("Host%d", rand() % iHostCount); + szHost = db_get_sm(0, STEAM_MODULE, szHost); + szHost.Insert(0, "wss://"); + szHost += "/cmsocket/"; + } + while (ServerThreadStub(szHost)); +} + +bool CSteamProto::ServerThreadStub(const char *szHost) +{ + WebSocket<CSteamProto> ws(this); + + NLHR_PTR pReply(ws.connect(m_hNetlibUser, szHost)); + if (pReply == nullptr) { + debugLogA("websocket connection failed"); + return false; + } + + if (pReply->resultCode != 101) { + debugLogA("websocket connection failed: %d", pReply->resultCode); + return false; + } + + m_ws = &ws; + + debugLogA("Websocket connection succeeded"); + + // Send init packets + Login(); + + ws.run(); + + Logout(); + m_impl.m_heartBeat.Stop(); + m_ws = nullptr; + return false; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void WebSocket<CSteamProto>::process(const uint8_t *buf, size_t cbLen) +{ + uint32_t dwSign = *(uint32_t *)buf; + EMsg msgType = (EMsg)(dwSign & ~STEAM_PROTOCOL_MASK); + + // now process the body + if (msgType == EMsg::Multi) { + buf += 8; cbLen -= 8; + p->ProcessMulti(buf, cbLen); + } + else p->ProcessMessage(buf, cbLen); +} + +void CSteamProto::ProcessMulti(const uint8_t *buf, size_t cbLen) +{ + proto::MsgMulti pMulti(buf, cbLen); + if (pMulti == nullptr) { + debugLogA("Unable to decode multi message, exiting"); + return; + } + + debugLogA("processing %s multi message of size %d", (pMulti->size_unzipped) ? "zipped" : "normal", pMulti->message_body.len); + + ptrA tmp; + if (pMulti->size_unzipped) { + tmp = (char *)mir_alloc(pMulti->size_unzipped + 1); + cbLen = FreeImage_ZLibGUnzip((uint8_t*)tmp.get(), pMulti->size_unzipped, pMulti->message_body.data, (unsigned)pMulti->message_body.len); + if (!cbLen) { + debugLogA("Unable to unzip multi message, exiting"); + return; + } + + buf = (const uint8_t *)tmp.get(); + } + else { + buf = pMulti->message_body.data; + cbLen = pMulti->message_body.len; + } + + while ((int)cbLen > 0) { + uint32_t cbPacketLen = *(uint32_t *)buf; buf += sizeof(uint32_t); cbLen -= sizeof(uint32_t); + ProcessMessage(buf, cbPacketLen); + buf += cbPacketLen; cbLen -= cbPacketLen; + } +} + +void CSteamProto::ProcessMessage(const uint8_t *buf, size_t cbLen) +{ + Netlib_Dump(HNETLIBCONN(m_ws->getConn()), buf, cbLen, false, 0); + + uint32_t dwSign = *(uint32_t *)buf; buf += sizeof(uint32_t); cbLen -= sizeof(uint32_t); + EMsg msgType = (EMsg)(dwSign & ~STEAM_PROTOCOL_MASK); + bool bIsProto = (dwSign & STEAM_PROTOCOL_MASK) != 0; + + CMsgProtoBufHeader hdr; + + if (msgType == EMsg::ChannelEncryptRequest || msgType == EMsg::ChannelEncryptResult) { + hdr.has_jobid_source = hdr.has_jobid_target = true; + hdr.jobid_source = *(int64_t *)buf; buf += sizeof(int64_t); + hdr.jobid_target = *(int64_t *)buf; buf += sizeof(int64_t); + } + else if (bIsProto) { + uint32_t hdrLen = *(uint32_t *)buf; buf += sizeof(uint32_t); cbLen -= sizeof(uint32_t); + proto::MsgProtoBufHeader tmpHeader(buf, hdrLen); + if (tmpHeader == nullptr) { + debugLogA("Unable to decode message header, exiting"); + return; + } + + memcpy(&hdr, tmpHeader, sizeof(hdr)); + buf += hdrLen; cbLen -= hdrLen; + + if (hdr.has_client_sessionid) + m_iSessionId = hdr.client_sessionid; + } + else { + debugLogA("Got unknown header, exiting"); + return; + } + + MsgCallback pCallback = 0; + { + mir_cslock lck(m_csRequests); + if (auto *pReq = m_arRequests.find((ProtoRequest *)&hdr.jobid_target)) { + pCallback = pReq->pCallback; + m_arRequests.remove(pReq); + } + } + + if (pCallback) { + (this->*pCallback)(buf, cbLen); + return; + } + + // persistent callbacks + switch (msgType) { + case EMsg::ClientLogOnResponse: + OnClientLogon(buf, cbLen); + break; + } +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void CSteamProto::WSSend(EMsg msgType, const ProtobufCppMessage &msg) +{ + CMsgProtoBufHeader hdr; + hdr.has_client_sessionid = hdr.has_steamid = hdr.has_jobid_source = hdr.has_jobid_target = true; + hdr.steamid = m_iSteamId; + hdr.client_sessionid = m_iSessionId; + + switch (msgType) { + case EMsg::ClientHello: + hdr.jobid_source = -1; + break; + + default: + hdr.jobid_source = getRandomInt(); + break; + } + + hdr.jobid_target = -1; + + WSSendHeader(msgType, hdr, msg); +} + +void CSteamProto::WSSendHeader(EMsg msgType, const CMsgProtoBufHeader &hdr, const ProtobufCppMessage &msg) +{ + uint32_t hdrLen = (uint32_t)protobuf_c_message_get_packed_size(&hdr); + MBinBuffer hdrbuf(hdrLen); + protobuf_c_message_pack(&hdr, (uint8_t *)hdrbuf.data()); + hdrbuf.appendBefore(&hdrLen, sizeof(hdrLen)); + + uint32_t type = (uint32_t)msgType; + type |= STEAM_PROTOCOL_MASK; + hdrbuf.appendBefore(&type, sizeof(type)); + + MBinBuffer body(protobuf_c_message_get_packed_size(&msg)); + protobuf_c_message_pack(&msg, body.data()); + + hdrbuf.append(body); + Netlib_Dump(HNETLIBCONN(m_ws->getConn()), hdrbuf.data(), hdrbuf.length(), true, 0); + m_ws->sendBinary(hdrbuf.data(), hdrbuf.length()); +} + +void CSteamProto::WSSendService(const char *pszServiceName, const ProtobufCppMessage &msg, MsgCallback pCallback) +{ + CMsgProtoBufHeader hdr; + hdr.has_client_sessionid = hdr.has_steamid = hdr.has_jobid_source = hdr.has_jobid_target = true; + hdr.jobid_source = getRandomInt(); + hdr.jobid_target = -1; + hdr.target_job_name = (char *)pszServiceName; + hdr.realm = 1; hdr.has_realm = true; + + if (pCallback) { + mir_cslock lck(m_csRequests); + m_arRequests.insert(new ProtoRequest(hdr.jobid_source, pCallback)); + } + + WSSendHeader(EMsg::ServiceMethodCallFromClientNonAuthed, hdr, msg); +} |