[client/idd] network: publish guest information

This commit is contained in:
Geoffrey McRae
2026-10-01 09:02:03 +10:00
parent b7f36f39a2
commit 0992f695ad
3 changed files with 206 additions and 4 deletions

View File

@@ -191,6 +191,7 @@ struct NetworkConnection
bool authResultReceived;
bool authKeyValid;
bool capabilitiesComplete;
bool guestInfoReceived;
bool connectFailed;
uint32_t expectedCapabilities;
uint32_t receivedCapabilities;
@@ -263,6 +264,50 @@ static char * networkEndpoint(const char * address, uint16_t port)
return endpoint;
}
static bool networkGuestText(char * destination, size_t capacity,
const uint8_t * source, uint16_t length)
{
if (!destination || !capacity || length >= capacity ||
(length && (!source || memchr(source, 0, length))))
return false;
if (length)
memcpy(destination, source, length);
destination[length] = 0;
return true;
}
static bool networkGuestOS(LGNetCoreGuestOS source,
LG_TransportGuestOS * destination)
{
if (!destination)
return false;
switch (source)
{
case LG_NET_CORE_GUEST_OS_LINUX:
*destination = LG_TRANSPORT_OS_LINUX;
return true;
case LG_NET_CORE_GUEST_OS_BSD:
*destination = LG_TRANSPORT_OS_BSD;
return true;
case LG_NET_CORE_GUEST_OS_OSX:
*destination = LG_TRANSPORT_OS_OSX;
return true;
case LG_NET_CORE_GUEST_OS_WINDOWS:
*destination = LG_TRANSPORT_OS_WINDOWS;
return true;
case LG_NET_CORE_GUEST_OS_OTHER:
*destination = LG_TRANSPORT_OS_OTHER;
return true;
}
return false;
}
static bool networkCapabilityOfferValid(
const LGNetCapability * capability)
{
@@ -1086,6 +1131,7 @@ static bool networkConnectionReadyToActivateNL(
return this->helloAcknowledged &&
this->authenticationComplete &&
this->capabilitiesComplete &&
this->guestInfoReceived &&
this->receivedCapabilities == this->expectedCapabilities &&
networkConnectionRequiredCapabilitiesSelected(this);
}
@@ -1438,16 +1484,75 @@ static bool networkConnectionHandleCore(NetworkConnection * this,
if (packet->envelope.flags != responseFlags ||
packet->envelope.requestID != NETWORK_HELLO_REQUEST_ID ||
packet->envelope.componentEpoch != 0 ||
packet->envelope.sessionEpoch != sessionEpoch)
packet->envelope.sessionEpoch != sessionEpoch ||
packet->envelope.serviceVersion != LG_NET_CORE_VERSION_CURRENT)
return false;
return networkConnectionHandleCapability(this, packet);
case LG_NET_CORE_MESSAGE_GUEST_INFO:
{
LGNetCoreGuestInfo info;
if (packet->envelope.flags != responseFlags ||
packet->envelope.requestID != NETWORK_HELLO_REQUEST_ID ||
packet->envelope.componentEpoch != 0 ||
packet->envelope.sessionEpoch != sessionEpoch ||
packet->envelope.serviceVersion != LG_NET_CORE_VERSION_CURRENT ||
lgNetCoreGuestInfoDecode(&info,
packet->payload, packet->payloadLength) != LG_NET_PARSE_OK)
return false;
LG_TransportSession guest = { 0 };
if (!networkGuestOS(info.os, &guest.os) ||
!networkGuestText(guest.version, sizeof(guest.version),
info.version, info.versionLength) ||
!networkGuestText(guest.osName, sizeof(guest.osName),
info.osName, info.osNameLength) ||
!networkGuestText(guest.capture, sizeof(guest.capture),
info.capture, info.captureLength) ||
!networkGuestText(guest.cpuModel, sizeof(guest.cpuModel),
info.cpuModel, info.cpuModelLength))
return false;
memcpy(guest.uuid, info.uuid, sizeof(guest.uuid));
guest.uuidValid = networkBytesNonzero(guest.uuid, sizeof(guest.uuid));
guest.cpus = info.processors;
guest.cores = info.cores;
guest.sockets = info.sockets;
LG_LOCK(this->lock);
const bool valid = this->helloAcknowledged &&
this->authenticationComplete && !this->capabilitiesComplete &&
!this->guestInfoReceived &&
this->receivedCapabilities == this->expectedCapabilities &&
networkConnectionRequiredCapabilitiesSelected(this);
if (valid)
{
memcpy(this->session.version, guest.version,
sizeof(this->session.version));
memcpy(this->session.uuid, guest.uuid, sizeof(this->session.uuid));
memcpy(this->session.osName, guest.osName,
sizeof(this->session.osName));
memcpy(this->session.capture, guest.capture,
sizeof(this->session.capture));
memcpy(this->session.cpuModel, guest.cpuModel,
sizeof(this->session.cpuModel));
this->session.uuidValid = guest.uuidValid;
this->session.os = guest.os;
this->session.cpus = guest.cpus;
this->session.cores = guest.cores;
this->session.sockets = guest.sockets;
this->guestInfoReceived = true;
}
LG_UNLOCK(this->lock);
return valid;
}
case LG_NET_CORE_MESSAGE_CAPABILITIES_DONE:
{
if (packet->envelope.flags != responseFlags ||
packet->envelope.requestID != NETWORK_HELLO_REQUEST_ID ||
packet->envelope.componentEpoch != 0 ||
packet->envelope.sessionEpoch != sessionEpoch ||
packet->envelope.serviceVersion != LG_NET_CORE_VERSION_CURRENT ||
packet->payloadLength)
return false;
@@ -1455,6 +1560,7 @@ static bool networkConnectionHandleCore(NetworkConnection * this,
LG_LOCK(this->lock);
const bool valid = !this->capabilitiesComplete &&
this->helloAcknowledged &&
this->guestInfoReceived &&
this->receivedCapabilities == this->expectedCapabilities &&
networkConnectionRequiredCapabilitiesSelected(this);
this->capabilitiesComplete = valid;
@@ -2689,6 +2795,7 @@ LG_TransportStatus networkConnectionConnect(NetworkConnection * this,
this->helloAcknowledged = false;
this->authenticationComplete = false;
this->capabilitiesComplete = false;
this->guestInfoReceived = false;
this->connectFailed = false;
this->expectedCapabilities = 0;
this->receivedCapabilities = 0;
@@ -2713,8 +2820,6 @@ LG_TransportStatus networkConnectionConnect(NetworkConnection * this,
memset(this->serviceMaxDatagram, 0, sizeof(this->serviceMaxDatagram));
lgNetStreamParserReset(&this->receiveParser);
memset(&this->session, 0, sizeof(this->session));
snprintf(this->session.version, sizeof(this->session.version),
"LG network protocol %u", LG_NET_PROTOCOL_VERSION_CURRENT);
snprintf(this->session.name, sizeof(this->session.name),
"%s", this->endpoint);
this->session.os = LG_TRANSPORT_OS_OTHER;

View File

@@ -27,6 +27,7 @@
#include "CDebug.h"
#include "CSRWLock.h"
#include "platform/CPlatformInfo.h"
#include "VersionInfo.h"
#include <LGProtocol/NetworkAuth.h>
#include <LGProtocol/NetworkIdentity.h>
@@ -174,6 +175,35 @@ namespace
return false;
}
size_t UTF8PrefixLength(const uint8_t * value, size_t length,
size_t maximum)
{
size_t prefix = (std::min)(length, maximum);
while (prefix && prefix < length &&
(value[prefix] & UINT8_C(0xC0)) == UINT8_C(0x80))
--prefix;
return prefix;
}
size_t ConsumeUTF8Field(const uint8_t * value, size_t length,
size_t fieldMaximum, size_t& remaining)
{
const size_t maximum = (std::min)(fieldMaximum, remaining);
const size_t result = UTF8PrefixLength(value, length, maximum);
remaining -= result;
return result;
}
uint8_t TopologyCount(int count)
{
if (count <= 0)
return 0;
return static_cast<uint8_t>((std::min)(count,
static_cast<int>(UINT8_MAX)));
}
bool MakeAddress(const std::wstring& source, uint16_t port,
QUIC_ADDR& address)
{
@@ -2059,6 +2089,71 @@ retry:
return sent;
}
bool SendGuestInformation(Connection& connection, uint64_t requestID)
{
static const char CAPTURE[] = "Looking Glass IDD Driver";
uint32_t maxPayload;
{
CSRWSharedLock guard(connection.lock);
if (connection.closing)
return false;
maxPayload = connection.maxPayload;
}
if (maxPayload < LG_NET_CORE_GUEST_INFO_HEADER_WIRE_SIZE)
return false;
const std::string& osName = CPlatformInfo::GetProductName();
const std::string& cpuModel = CPlatformInfo::GetCPUModel();
LGNetCoreGuestInfo info = {};
memcpy(info.uuid, CPlatformInfo::GetUUID(), sizeof(info.uuid));
info.os = LG_NET_CORE_GUEST_OS_WINDOWS;
info.processors = TopologyCount(CPlatformInfo::GetProcCount());
info.cores = TopologyCount(CPlatformInfo::GetCoreCount());
info.sockets = TopologyCount(CPlatformInfo::GetSocketCount());
info.version = reinterpret_cast<const uint8_t *>(LG_VERSION_STR);
info.osName = reinterpret_cast<const uint8_t *>(osName.data());
info.capture = reinterpret_cast<const uint8_t *>(CAPTURE);
info.cpuModel = reinterpret_cast<const uint8_t *>(cpuModel.data());
size_t remaining = maxPayload -
LG_NET_CORE_GUEST_INFO_HEADER_WIRE_SIZE;
info.versionLength = static_cast<uint16_t>(ConsumeUTF8Field(
info.version, strlen(LG_VERSION_STR),
LG_NET_CORE_GUEST_MAX_VERSION_LENGTH, remaining));
info.osNameLength = static_cast<uint16_t>(ConsumeUTF8Field(
info.osName, osName.size(), LG_NET_CORE_GUEST_MAX_OS_NAME_LENGTH,
remaining));
info.captureLength = static_cast<uint16_t>(ConsumeUTF8Field(
info.capture, strlen(CAPTURE), LG_NET_CORE_GUEST_MAX_CAPTURE_LENGTH,
remaining));
info.cpuModelLength = static_cast<uint16_t>(ConsumeUTF8Field(
info.cpuModel, cpuModel.size(),
LG_NET_CORE_GUEST_MAX_CPU_MODEL_LENGTH, remaining));
const size_t payloadSize = lgNetCoreGuestInfoSize(&info);
if (!payloadSize || payloadSize > maxPayload)
return false;
std::vector<uint8_t> payload;
try
{
payload.resize(payloadSize);
}
catch (...)
{
return false;
}
return lgNetCoreGuestInfoEncode(
payload.data(), payload.size(), &info) &&
SendPacket(connection, LG_NET_SERVICE_CORE,
LG_NET_CORE_MESSAGE_GUEST_INFO,
LG_NET_ENVELOPE_RESPONSE | LG_NET_ENVELOPE_FINAL,
0, requestID, payload.data(),
static_cast<uint32_t>(payload.size()));
}
bool PublishCapabilities(Connection& connection)
{
LGNetCapability selected[NETWORK_SERVICE_COUNT] = {};
@@ -2148,6 +2243,8 @@ retry:
return false;
if (videoSelected && !video.PeerConnected(VideoPeer(connection)))
return false;
if (!SendGuestInformation(connection, requestID))
return false;
if (!SendPacket(connection, LG_NET_SERVICE_CORE,
LG_NET_CORE_MESSAGE_CAPABILITIES_DONE,
LG_NET_ENVELOPE_RESPONSE | LG_NET_ENVELOPE_FINAL,