[idd] network: bind clipboard to input authority

This commit is contained in:
Geoffrey McRae
2026-10-07 16:05:07 +11:00
parent f2eadca4ab
commit 0fecbe4989
9 changed files with 507 additions and 76 deletions

View File

@@ -70,6 +70,12 @@ InputTargetState CInputHub::Source::GetState(
return owner->GetState(Key(source)); return owner->GetState(Key(source));
} }
bool CInputHub::Source::WithAuthority(const InputSourceId& source,
InputAuthorityAction action, void * opaque)
{
return owner->WithAuthority(Key(source), action, opaque);
}
void CInputHub::Source::Failed() void CInputHub::Source::Failed()
{ {
owner->Failed(*this); owner->Failed(*this);
@@ -565,6 +571,20 @@ InputTargetState CInputHub::GetState(const SourceKey& source)
return result; return result;
} }
bool CInputHub::WithAuthority(const SourceKey& source,
InputAuthorityAction action, void * opaque)
{
if (!action)
return false;
CSRWExclusiveLock lock(m_lock);
if (!SourceValid(source) || !CheckState() || !OwnerValid(source))
return false;
action(opaque);
return true;
}
void CInputHub::Failed(Source& source) void CInputHub::Failed(Source& source)
{ {
CSRWExclusiveLock lock(m_lock); CSRWExclusiveLock lock(m_lock);

View File

@@ -57,6 +57,8 @@ private:
SourceKey Key(const InputSourceId& source) const; SourceKey Key(const InputSourceId& source) const;
InputTargetState GetState(const InputSourceId& source) override; InputTargetState GetState(const InputSourceId& source) override;
bool WithAuthority(const InputSourceId& source,
InputAuthorityAction action, void * opaque) override;
void Failed() override; void Failed() override;
InputResult Claim(const InputSourceId& source) override; InputResult Claim(const InputSourceId& source) override;
InputResult Touch(const InputSourceId& source) override; InputResult Touch(const InputSourceId& source) override;
@@ -94,6 +96,8 @@ private:
void AdvanceInteractionSerial(); void AdvanceInteractionSerial();
bool CheckState(); bool CheckState();
InputTargetState GetState(const SourceKey& source); InputTargetState GetState(const SourceKey& source);
bool WithAuthority(const SourceKey& source,
InputAuthorityAction action, void * opaque);
void Failed(Source& source); void Failed(Source& source);
InputResult Claim(const SourceKey& source); InputResult Claim(const SourceKey& source);
InputResult Touch(const SourceKey& source); InputResult Touch(const SourceKey& source);

View File

@@ -38,6 +38,8 @@ struct InputTargetState
uint8_t keyboardLEDs = 0; uint8_t keyboardLEDs = 0;
}; };
using InputAuthorityAction = void (*)(void * opaque);
enum class InputResult enum class InputResult
{ {
ACCEPTED, ACCEPTED,
@@ -52,6 +54,10 @@ public:
virtual ~IInputTarget() = default; virtual ~IInputTarget() = default;
virtual InputTargetState GetState(const InputSourceId& source) = 0; virtual InputTargetState GetState(const InputSourceId& source) = 0;
// The action runs synchronously while input ownership cannot change and
// must not call back through this input target.
virtual bool WithAuthority(const InputSourceId& source,
InputAuthorityAction action, void * opaque) = 0;
virtual void Failed() = 0; virtual void Failed() = 0;
virtual InputResult Claim(const InputSourceId& source) = 0; virtual InputResult Claim(const InputSourceId& source) = 0;
virtual InputResult Touch(const InputSourceId& source) = 0; virtual InputResult Touch(const InputSourceId& source) = 0;

View File

@@ -20,6 +20,7 @@
#include "transport/network/CNetworkClipboard.h" #include "transport/network/CNetworkClipboard.h"
#include "CDebug.h"
#include "CSRWLock.h" #include "CSRWLock.h"
#include <LGProtocol/NetworkClipboard.h> #include <LGProtocol/NetworkClipboard.h>
@@ -48,6 +49,7 @@ namespace
constexpr uint32_t MAX_FILE_READ_LENGTH = KVMFR_CLIPBOARD_FILE_READ_BYTES; constexpr uint32_t MAX_FILE_READ_LENGTH = KVMFR_CLIPBOARD_FILE_READ_BYTES;
constexpr uint32_t MIN_CHUNK_LENGTH = constexpr uint32_t MIN_CHUNK_LENGTH =
(MAX_CHUNK_LENGTH + MAX_SEND_COUNT - 3U) / (MAX_SEND_COUNT - 2U); (MAX_CHUNK_LENGTH + MAX_SEND_COUNT - 3U) / (MAX_SEND_COUNT - 2U);
constexpr uint32_t CLAIM_INPUT_REQUIRED = 4;
constexpr LGNetClipboardClaimFlags CLAIM_FLAGS = constexpr LGNetClipboardClaimFlags CLAIM_FLAGS =
LG_NET_CLIPBOARD_CLAIM_READ | LG_NET_CLIPBOARD_CLAIM_WRITE; LG_NET_CLIPBOARD_CLAIM_READ | LG_NET_CLIPBOARD_CLAIM_WRITE;
@@ -247,10 +249,12 @@ struct CNetworkClipboard::Impl
struct TargetItem struct TargetItem
{ {
uint64_t serial = 0; uint64_t serial = 0;
KVMFRClipboardMessage record = {}; NetworkClipboardPeer peer = {};
std::vector<uint8_t> data = {}; uint64_t claimEpoch = 0;
bool wasBlocked = false; KVMFRClipboardMessage record = {};
std::vector<uint8_t> data = {};
bool wasBlocked = false;
}; };
CSRWLock m_lifecycleLock; CSRWLock m_lifecycleLock;
@@ -388,20 +392,24 @@ struct CNetworkClipboard::Impl
return nullptr; return nullptr;
} }
bool QueueSendBatchNL(std::vector<SendItem>& batch); bool QueueSendBatchNL(std::vector<SendItem>& batch);
bool QueueTargetNL(TargetItem&& item); bool QueueTargetNL(TargetItem&& item);
bool SelectBulkNL( bool QueueOwnerTargetNL(TargetItem&& item);
bool PeerOwnsInput(const NetworkClipboardPeer& peer);
bool WithInputAuthority(const NetworkClipboardPeer& peer,
NetworkClipboardInputAction action, void * opaque);
bool SelectBulkNL(
const PeerState& peer, LGNetService service, uint32_t payloadLength) const; const PeerState& peer, LGNetService service, uint32_t payloadLength) const;
bool PayloadFitsNL(const PeerState& peer, LGNetService service, bool bulk, bool PayloadFitsNL(const PeerState& peer, LGNetService service, bool bulk,
uint32_t payloadLength) const; uint32_t payloadLength) const;
uint32_t ChunkLengthNL( uint32_t ChunkLengthNL(
const PeerState& peer, LGNetService service, uint32_t headerLength, const PeerState& peer, LGNetService service, uint32_t headerLength,
bool bulk) const; bool bulk) const;
bool BuildSendForDeliveryNL(SendItem& item, bool BuildSendForDeliveryNL(SendItem& item,
const NetworkClipboardPeer& peer, LGNetService service, const NetworkClipboardPeer& peer, LGNetService service,
uint16_t messageType, LGNetEnvelopeFlags flags, uint64_t componentEpoch, uint16_t messageType, LGNetEnvelopeFlags flags, uint64_t componentEpoch,
uint64_t requestID, uint32_t payloadLength, bool bulk); uint64_t requestID, uint32_t payloadLength, bool bulk);
bool BuildSendNL(SendItem& item, const NetworkClipboardPeer& peer, bool BuildSendNL(SendItem& item, const NetworkClipboardPeer& peer,
LGNetService service, uint16_t messageType, LGNetEnvelopeFlags flags, LGNetService service, uint16_t messageType, LGNetEnvelopeFlags flags,
uint64_t componentEpoch, uint64_t requestID, uint32_t payloadLength, uint64_t componentEpoch, uint64_t requestID, uint32_t payloadLength,
bool preferBulk); bool preferBulk);
@@ -412,6 +420,7 @@ struct CNetworkClipboard::Impl
void NotifyTargetReady(); void NotifyTargetReady();
void NotifyFailure(); void NotifyFailure();
void DropOwnerNL(bool clearHelper); void DropOwnerNL(bool clearHelper);
void ExpireOwnerNL(bool clearHelper, uint32_t detail);
void ResetTransfersNL(); void ResetTransfersNL();
void ClearRemoteTransfersNL(uint64_t offerID); void ClearRemoteTransfersNL(uint64_t offerID);
bool PublishRemoteOfferNL(); bool PublishRemoteOfferNL();
@@ -433,13 +442,14 @@ struct CNetworkClipboard::Impl
std::vector<SendItem>& batch, const KVMFRClipboardMessage& record); std::vector<SendItem>& batch, const KVMFRClipboardMessage& record);
NetworkClipboardPacketResult HandleClaimNL( NetworkClipboardPacketResult HandleClaimNL(
PeerState& peer, const LGNetPacketView& packet, uint64_t nowMs, PeerState& peer, const LGNetPacketView& packet, bool inputOwner,
CSRWExclusiveLock& stateLock); uint64_t nowMs, CSRWExclusiveLock& stateLock);
NetworkClipboardPacketResult HandleClipboardNL( NetworkClipboardPacketResult HandleClipboardNL(
PeerState& peer, const LGNetPacketView& packet, bool bulk, uint64_t nowMs, PeerState& peer, const LGNetPacketView& packet, bool bulk,
CSRWExclusiveLock& stateLock); bool inputOwner, uint64_t nowMs, CSRWExclusiveLock& stateLock);
NetworkClipboardPacketResult HandleFileNL( NetworkClipboardPacketResult HandleFileNL(
PeerState& peer, const LGNetPacketView& packet, bool bulk); PeerState& peer, const LGNetPacketView& packet, bool bulk,
bool inputOwner);
ClipboardChannelResult HandleHelperClipboardNL( ClipboardChannelResult HandleHelperClipboardNL(
const KVMFRClipboardMessage& record, const uint8_t * data); const KVMFRClipboardMessage& record, const uint8_t * data);
@@ -620,6 +630,42 @@ bool CNetworkClipboard::Impl::QueueTargetNL(TargetItem&& item)
return true; return true;
} }
bool CNetworkClipboard::Impl::QueueOwnerTargetNL(TargetItem&& item)
{
if (!m_claimEpoch || !m_owner.client || !m_owner.generation)
return false;
item.peer = m_owner;
item.claimEpoch = m_claimEpoch;
return QueueTargetNL(std::move(item));
}
bool CNetworkClipboard::Impl::PeerOwnsInput(
const NetworkClipboardPeer& peer)
{
CSRWExclusiveLock callbackLock(m_senderCallbackLock);
INetworkClipboardSender * sender;
{
CSRWSharedLock lock(m_lock);
sender = m_sender;
}
return sender && sender->ClipboardPeerOwnsInput(peer);
}
bool CNetworkClipboard::Impl::WithInputAuthority(
const NetworkClipboardPeer& peer, NetworkClipboardInputAction action,
void * opaque)
{
CSRWExclusiveLock callbackLock(m_senderCallbackLock);
INetworkClipboardSender * sender;
{
CSRWSharedLock lock(m_lock);
sender = m_sender;
}
return sender &&
sender->ClipboardWithInputAuthority(peer, action, opaque);
}
void CNetworkClipboard::Impl::PumpSends() void CNetworkClipboard::Impl::PumpSends()
{ {
struct BlockedRoute struct BlockedRoute
@@ -681,6 +727,25 @@ void CNetworkClipboard::Impl::PumpSends()
{ {
CSRWExclusiveLock lock(m_lock); CSRWExclusiveLock lock(m_lock);
if (result == NetworkClipboardSendResult::UNAUTHORIZED)
{
if (m_claimEpoch && SamePeer(m_owner, action.peer))
ExpireOwnerNL(true, CLAIM_INPUT_REQUIRED);
else
{
const auto pending = std::find_if(
m_sends.begin(), m_sends.end(),
[&action](const SendItem& item)
{ return item.serial == action.serial; });
if (pending != m_sends.end())
{
m_sendBytes -= pending->payload.size();
m_sends.erase(pending);
}
}
return;
}
const auto pending = std::find_if(m_sends.begin(), m_sends.end(), const auto pending = std::find_if(m_sends.begin(), m_sends.end(),
[&action](const SendItem& item) [&action](const SendItem& item)
{ return item.serial == action.serial; }); { return item.serial == action.serial; });
@@ -737,24 +802,63 @@ void CNetworkClipboard::Impl::PumpTargets()
} }
} }
ClipboardChannelResult result; struct TargetContext
{ {
CSRWExclusiveLock callbackLock(m_targetCallbackLock); Impl * owner = nullptr;
const TargetItem * action = nullptr;
ClipboardChannelResult result = ClipboardChannelResult::FAILED;
} context;
context.owner = this;
context.action = &action;
const auto deliver = [](void * value)
{
TargetContext& targetContext = *static_cast<TargetContext *>(value);
CSRWExclusiveLock callbackLock(
targetContext.owner->m_targetCallbackLock);
IClipboardTarget * target; IClipboardTarget * target;
{ {
CSRWExclusiveLock lock(m_lock); CSRWExclusiveLock lock(targetContext.owner->m_lock);
target = m_target; target = targetContext.owner->m_target;
m_targetCallbackActive = target != nullptr; targetContext.owner->m_targetCallbackActive = target != nullptr;
} }
result = target ? target->SendClipboard(action.record, targetContext.result = target ?
action.data.empty() ? nullptr : action.data.data()) : target->SendClipboard(targetContext.action->record,
ClipboardChannelResult::FAILED; targetContext.action->data.empty() ? nullptr :
targetContext.action->data.data()) :
ClipboardChannelResult::FAILED;
{
CSRWExclusiveLock lock(targetContext.owner->m_lock);
targetContext.owner->m_targetCallbackActive = false;
}
};
const bool authorized = !action.claimEpoch ||
WithInputAuthority(action.peer, deliver, &context);
if (!authorized)
{
{ {
CSRWExclusiveLock lock(m_lock); CSRWExclusiveLock lock(m_lock);
m_targetCallbackActive = false; if (!m_targets.empty() &&
m_targets.front().serial == action.serial)
{
if (m_claimEpoch == action.claimEpoch &&
SamePeer(m_owner, action.peer))
ExpireOwnerNL(true, CLAIM_INPUT_REQUIRED);
else
{
const TargetItem& item = m_targets.front();
m_targetBytes -= sizeof(item.record) + item.data.size();
m_targets.pop_front();
}
}
} }
ReleaseHubOwner();
continue;
} }
const ClipboardChannelResult result = context.result;
{ {
CSRWExclusiveLock lock(m_lock); CSRWExclusiveLock lock(m_lock);
if (m_targets.empty() || m_targets.front().serial != action.serial) if (m_targets.empty() || m_targets.front().serial != action.serial)
@@ -827,7 +931,7 @@ void CNetworkClipboard::Impl::NotifyTargetReady()
IClipboardTarget * target = nullptr; IClipboardTarget * target = nullptr;
{ {
CSRWExclusiveLock lock(m_lock); CSRWExclusiveLock lock(m_lock);
if (!m_receiveReadyPending) if (!m_receiveReadyPending || m_hubReleasePending)
return; return;
m_receiveReadyPending = false; m_receiveReadyPending = false;
target = m_started ? m_target : nullptr; target = m_started ? m_target : nullptr;
@@ -1128,7 +1232,7 @@ bool CNetworkClipboard::Impl::PublishRemoteOfferNL()
item.record.type = KVMFR_CLIPBOARD_MESSAGE_OFFER; item.record.type = KVMFR_CLIPBOARD_MESSAGE_OFFER;
item.record.clipboardGeneration = m_remoteOffer.offerID; item.record.clipboardGeneration = m_remoteOffer.offerID;
item.record.token = m_remoteOffer.formatMask; item.record.token = m_remoteOffer.formatMask;
if (!QueueTargetNL(std::move(item))) if (!QueueOwnerTargetNL(std::move(item)))
return false; return false;
m_remoteOffer.published = true; m_remoteOffer.published = true;
return true; return true;
@@ -1139,6 +1243,12 @@ void CNetworkClipboard::Impl::DropOwnerNL(bool clearHelper)
if (!m_claimEpoch) if (!m_claimEpoch)
return; return;
if (clearHelper)
{
m_targets.clear();
m_targetBytes = 0;
}
bool cleanupQueued = true; bool cleanupQueued = true;
if (clearHelper && m_remoteOffer.published && m_remoteOffer.offerID) if (clearHelper && m_remoteOffer.published && m_remoteOffer.offerID)
{ {
@@ -1227,11 +1337,34 @@ void CNetworkClipboard::Impl::DropOwnerNL(bool clearHelper)
m_remoteOfferSerial = 0; m_remoteOfferSerial = 0;
m_claimDeadline = 0; m_claimDeadline = 0;
m_claimLeaseMs = 0; m_claimLeaseMs = 0;
if (m_sourceBlocked)
m_receiveReadyPending = true;
m_sourceBlocked = false;
m_sourceBlockedService = 0;
m_sourceBlockedTransfer = 0;
}
void CNetworkClipboard::Impl::ExpireOwnerNL(
bool clearHelper, uint32_t detail)
{
if (!m_claimEpoch)
return;
const NetworkClipboardPeer owner = m_owner;
const uint64_t epoch = m_claimEpoch;
const uint64_t serial = m_claimSerial;
DropOwnerNL(clearHelper);
m_claimSerial = serial;
if (!QueueClaimStatusNL(
owner, epoch, LG_NET_CLIPBOARD_CLAIM_EXPIRED, 0, 0, detail))
m_failed = true;
m_claimSerial = 0;
} }
NetworkClipboardPacketResult CNetworkClipboard::Impl::HandleClaimNL( NetworkClipboardPacketResult CNetworkClipboard::Impl::HandleClaimNL(
PeerState& peer, const LGNetPacketView& packet, uint64_t nowMs, PeerState& peer, const LGNetPacketView& packet, bool inputOwner,
CSRWExclusiveLock& stateLock) uint64_t nowMs, CSRWExclusiveLock& stateLock)
{ {
if (!DeliveryFlags( if (!DeliveryFlags(
packet.envelope, LG_NET_ENVELOPE_ACK_REQUIRED, false) || packet.envelope, LG_NET_ENVELOPE_ACK_REQUIRED, false) ||
@@ -1251,6 +1384,15 @@ NetworkClipboardPacketResult CNetworkClipboard::Impl::HandleClaimNL(
NetworkClipboardPacketResult::HANDLED : NetworkClipboardPacketResult::HANDLED :
NetworkClipboardPacketResult::INVALID; NetworkClipboardPacketResult::INVALID;
} }
if (!inputOwner)
{
peer.lastClaimEpoch = claim.claimEpoch;
return QueueClaimStatusNL(peer.peer, claim.claimEpoch,
LG_NET_CLIPBOARD_CLAIM_REJECTED, 0, 0,
CLAIM_INPUT_REQUIRED) ?
NetworkClipboardPacketResult::HANDLED :
NetworkClipboardPacketResult::INVALID;
}
const bool exactOwner = const bool exactOwner =
IsOwnerNL(peer.peer, claim.claimEpoch) && claim.ownerID == m_ownerID; IsOwnerNL(peer.peer, claim.claimEpoch) && claim.ownerID == m_ownerID;
@@ -1350,17 +1492,20 @@ NetworkClipboardPacketResult CNetworkClipboard::Impl::HandleClaimNL(
} }
NetworkClipboardPacketResult CNetworkClipboard::Impl::HandleClipboardNL( NetworkClipboardPacketResult CNetworkClipboard::Impl::HandleClipboardNL(
PeerState& peer, const LGNetPacketView& packet, bool bulk, uint64_t nowMs, PeerState& peer, const LGNetPacketView& packet, bool bulk,
CSRWExclusiveLock& stateLock) bool inputOwner, uint64_t nowMs, CSRWExclusiveLock& stateLock)
{ {
const LGNetEnvelope& envelope = packet.envelope; const LGNetEnvelope& envelope = packet.envelope;
if (envelope.messageType == LG_NET_CLIPBOARD_MESSAGE_CLAIM) if (envelope.messageType == LG_NET_CLIPBOARD_MESSAGE_CLAIM)
{ {
if (bulk) if (bulk)
return NetworkClipboardPacketResult::INVALID; return NetworkClipboardPacketResult::INVALID;
return HandleClaimNL(peer, packet, nowMs, stateLock); return HandleClaimNL(peer, packet, inputOwner, nowMs, stateLock);
} }
if (!inputOwner)
return NetworkClipboardPacketResult::IGNORED;
const bool transferData = const bool transferData =
envelope.messageType == LG_NET_CLIPBOARD_MESSAGE_DATA_BEGIN || envelope.messageType == LG_NET_CLIPBOARD_MESSAGE_DATA_BEGIN ||
envelope.messageType == LG_NET_CLIPBOARD_MESSAGE_DATA_CHUNK || envelope.messageType == LG_NET_CLIPBOARD_MESSAGE_DATA_CHUNK ||
@@ -1418,7 +1563,7 @@ NetworkClipboardPacketResult CNetworkClipboard::Impl::HandleClipboardNL(
item.record.version = KVMFR_CLIPBOARD_VERSION; item.record.version = KVMFR_CLIPBOARD_VERSION;
item.record.type = KVMFR_CLIPBOARD_MESSAGE_CLEAR; item.record.type = KVMFR_CLIPBOARD_MESSAGE_CLEAR;
item.record.clipboardGeneration = m_remoteOffer.offerID; item.record.clipboardGeneration = m_remoteOffer.offerID;
if (!QueueTargetNL(std::move(item))) if (!QueueOwnerTargetNL(std::move(item)))
return NetworkClipboardPacketResult::INVALID; return NetworkClipboardPacketResult::INVALID;
} }
ClearRemoteTransfersNL(m_remoteOffer.offerID); ClearRemoteTransfersNL(m_remoteOffer.offerID);
@@ -1455,7 +1600,7 @@ NetworkClipboardPacketResult CNetworkClipboard::Impl::HandleClipboardNL(
clear.record.version = KVMFR_CLIPBOARD_VERSION; clear.record.version = KVMFR_CLIPBOARD_VERSION;
clear.record.type = KVMFR_CLIPBOARD_MESSAGE_CLEAR; clear.record.type = KVMFR_CLIPBOARD_MESSAGE_CLEAR;
clear.record.clipboardGeneration = m_remoteOffer.offerID; clear.record.clipboardGeneration = m_remoteOffer.offerID;
if (!QueueTargetNL(std::move(clear))) if (!QueueOwnerTargetNL(std::move(clear)))
return NetworkClipboardPacketResult::INVALID; return NetworkClipboardPacketResult::INVALID;
} }
ClearRemoteTransfersNL(m_remoteOffer.offerID); ClearRemoteTransfersNL(m_remoteOffer.offerID);
@@ -1596,7 +1741,7 @@ NetworkClipboardPacketResult CNetworkClipboard::Impl::HandleClipboardNL(
item.record.clipboardGeneration = transfer.offerID; item.record.clipboardGeneration = transfer.offerID;
item.record.transfer = transfer.localID; item.record.transfer = transfer.localID;
item.record.format = transfer.format; item.record.format = transfer.format;
if (!QueueTargetNL(std::move(item))) if (!QueueOwnerTargetNL(std::move(item)))
return NetworkClipboardPacketResult::INVALID; return NetworkClipboardPacketResult::INVALID;
try try
{ {
@@ -1673,7 +1818,7 @@ NetworkClipboardPacketResult CNetworkClipboard::Impl::HandleClipboardNL(
{ {
return NetworkClipboardPacketResult::INVALID; return NetworkClipboardPacketResult::INVALID;
} }
if (!QueueTargetNL(std::move(item))) if (!QueueOwnerTargetNL(std::move(item)))
return NetworkClipboardPacketResult::INVALID; return NetworkClipboardPacketResult::INVALID;
transfer = FindClipboardWireNL(chunkValue.requestID); transfer = FindClipboardWireNL(chunkValue.requestID);
@@ -1715,7 +1860,7 @@ NetworkClipboardPacketResult CNetworkClipboard::Impl::HandleClipboardNL(
item.record.flags = KVMFR_CLIPBOARD_FLAG_END; item.record.flags = KVMFR_CLIPBOARD_FLAG_END;
if (!transfer->offset) if (!transfer->offset)
item.record.flags |= KVMFR_CLIPBOARD_FLAG_BEGIN; item.record.flags |= KVMFR_CLIPBOARD_FLAG_BEGIN;
if (!QueueTargetNL(std::move(item))) if (!QueueOwnerTargetNL(std::move(item)))
return NetworkClipboardPacketResult::INVALID; return NetworkClipboardPacketResult::INVALID;
m_clipboardTransfers.erase( m_clipboardTransfers.erase(
std::remove_if(m_clipboardTransfers.begin(), m_clipboardTransfers.end(), std::remove_if(m_clipboardTransfers.begin(), m_clipboardTransfers.end(),
@@ -1789,7 +1934,7 @@ NetworkClipboardPacketResult CNetworkClipboard::Impl::HandleClipboardNL(
item.record.clipboardGeneration = transfer->offerID; item.record.clipboardGeneration = transfer->offerID;
item.record.transfer = transfer->localID; item.record.transfer = transfer->localID;
item.record.format = transfer->format; item.record.format = transfer->format;
if (!QueueTargetNL(std::move(item))) if (!QueueOwnerTargetNL(std::move(item)))
return NetworkClipboardPacketResult::INVALID; return NetworkClipboardPacketResult::INVALID;
m_clipboardTransfers.erase( m_clipboardTransfers.erase(
std::remove_if(m_clipboardTransfers.begin(), m_clipboardTransfers.end(), std::remove_if(m_clipboardTransfers.begin(), m_clipboardTransfers.end(),
@@ -1806,7 +1951,8 @@ NetworkClipboardPacketResult CNetworkClipboard::Impl::HandleClipboardNL(
} }
NetworkClipboardPacketResult CNetworkClipboard::Impl::HandleFileNL( NetworkClipboardPacketResult CNetworkClipboard::Impl::HandleFileNL(
PeerState& peer, const LGNetPacketView& packet, bool bulk) PeerState& peer, const LGNetPacketView& packet, bool bulk,
bool inputOwner)
{ {
const LGNetEnvelope& envelope = packet.envelope; const LGNetEnvelope& envelope = packet.envelope;
const bool transferData = const bool transferData =
@@ -1820,7 +1966,7 @@ NetworkClipboardPacketResult CNetworkClipboard::Impl::HandleFileNL(
else if (envelope.messageType == LG_NET_FILE_MESSAGE_ACQUIRED || else if (envelope.messageType == LG_NET_FILE_MESSAGE_ACQUIRED ||
envelope.messageType == LG_NET_FILE_MESSAGE_STATUS) envelope.messageType == LG_NET_FILE_MESSAGE_STATUS)
expected = LG_NET_ENVELOPE_RESPONSE; expected = LG_NET_ENVELOPE_RESPONSE;
if (!IsOwnerNL(peer.peer, envelope.componentEpoch)) if (!inputOwner || !IsOwnerNL(peer.peer, envelope.componentEpoch))
return NetworkClipboardPacketResult::IGNORED; return NetworkClipboardPacketResult::IGNORED;
if ((bulk && !transferData) || !DeliveryFlags(envelope, expected, bulk)) if ((bulk && !transferData) || !DeliveryFlags(envelope, expected, bulk))
return NetworkClipboardPacketResult::INVALID; return NetworkClipboardPacketResult::INVALID;
@@ -1845,7 +1991,7 @@ NetworkClipboardPacketResult CNetworkClipboard::Impl::HandleFileNL(
clear.record.version = KVMFR_CLIPBOARD_VERSION; clear.record.version = KVMFR_CLIPBOARD_VERSION;
clear.record.type = KVMFR_CLIPBOARD_MESSAGE_CLEAR; clear.record.type = KVMFR_CLIPBOARD_MESSAGE_CLEAR;
clear.record.clipboardGeneration = m_remoteOffer.offerID; clear.record.clipboardGeneration = m_remoteOffer.offerID;
if (!QueueTargetNL(std::move(clear))) if (!QueueOwnerTargetNL(std::move(clear)))
return NetworkClipboardPacketResult::INVALID; return NetworkClipboardPacketResult::INVALID;
} }
ClearRemoteTransfersNL(m_remoteOffer.offerID); ClearRemoteTransfersNL(m_remoteOffer.offerID);
@@ -1912,7 +2058,7 @@ NetworkClipboardPacketResult CNetworkClipboard::Impl::HandleFileNL(
item.record.clipboardGeneration = acquisition.offerID; item.record.clipboardGeneration = acquisition.offerID;
item.record.transfer = acquisition.localID; item.record.transfer = acquisition.localID;
item.record.format = KVMFR_CLIPBOARD_FORMAT_FILES; item.record.format = KVMFR_CLIPBOARD_FORMAT_FILES;
if (!QueueTargetNL(std::move(item))) if (!QueueOwnerTargetNL(std::move(item)))
return NetworkClipboardPacketResult::INVALID; return NetworkClipboardPacketResult::INVALID;
try try
{ {
@@ -1951,7 +2097,7 @@ NetworkClipboardPacketResult CNetworkClipboard::Impl::HandleFileNL(
item.record.transfer = acquisition->localID; item.record.transfer = acquisition->localID;
item.record.format = KVMFR_CLIPBOARD_FORMAT_FILES; item.record.format = KVMFR_CLIPBOARD_FORMAT_FILES;
item.record.token = KVMFR_CLIPBOARD_FILE_ERROR_NONE; item.record.token = KVMFR_CLIPBOARD_FILE_ERROR_NONE;
if (!QueueTargetNL(std::move(item))) if (!QueueOwnerTargetNL(std::move(item)))
return NetworkClipboardPacketResult::INVALID; return NetworkClipboardPacketResult::INVALID;
acquisition = FindAcquisitionWireNL(lease.clientID); acquisition = FindAcquisitionWireNL(lease.clientID);
if (acquisition) if (acquisition)
@@ -1985,7 +2131,7 @@ NetworkClipboardPacketResult CNetworkClipboard::Impl::HandleFileNL(
item.record.clipboardGeneration = acquisition->offerID; item.record.clipboardGeneration = acquisition->offerID;
item.record.transfer = localID; item.record.transfer = localID;
item.record.format = KVMFR_CLIPBOARD_FORMAT_FILES; item.record.format = KVMFR_CLIPBOARD_FORMAT_FILES;
if (!QueueTargetNL(std::move(item))) if (!QueueOwnerTargetNL(std::move(item)))
return NetworkClipboardPacketResult::INVALID; return NetworkClipboardPacketResult::INVALID;
const uint64_t offerID = acquisition->offerID; const uint64_t offerID = acquisition->offerID;
m_fileAcquisitions.erase( m_fileAcquisitions.erase(
@@ -2097,7 +2243,7 @@ NetworkClipboardPacketResult CNetworkClipboard::Impl::HandleFileNL(
item.record.flags = item.record.flags =
transfer.operation == KVMFR_CLIPBOARD_FILE_OP_READ ? transfer.length : 0; transfer.operation == KVMFR_CLIPBOARD_FILE_OP_READ ? transfer.length : 0;
item.record.token = transfer.operation; item.record.token = transfer.operation;
if (!QueueTargetNL(std::move(item))) if (!QueueOwnerTargetNL(std::move(item)))
return NetworkClipboardPacketResult::INVALID; return NetworkClipboardPacketResult::INVALID;
try try
{ {
@@ -2182,7 +2328,7 @@ NetworkClipboardPacketResult CNetworkClipboard::Impl::HandleFileNL(
{ {
return NetworkClipboardPacketResult::INVALID; return NetworkClipboardPacketResult::INVALID;
} }
if (!QueueTargetNL(std::move(item))) if (!QueueOwnerTargetNL(std::move(item)))
return NetworkClipboardPacketResult::INVALID; return NetworkClipboardPacketResult::INVALID;
transfer = FindFileWireNL(chunkValue.requestID); transfer = FindFileWireNL(chunkValue.requestID);
@@ -2227,7 +2373,7 @@ NetworkClipboardPacketResult CNetworkClipboard::Impl::HandleFileNL(
if (!transfer->responseOffset) if (!transfer->responseOffset)
item.record.flags |= KVMFR_CLIPBOARD_FLAG_BEGIN; item.record.flags |= KVMFR_CLIPBOARD_FLAG_BEGIN;
item.record.token = transfer->operation; item.record.token = transfer->operation;
if (!QueueTargetNL(std::move(item))) if (!QueueOwnerTargetNL(std::move(item)))
return NetworkClipboardPacketResult::INVALID; return NetworkClipboardPacketResult::INVALID;
m_fileTransfers.erase( m_fileTransfers.erase(
std::remove_if(m_fileTransfers.begin(), m_fileTransfers.end(), std::remove_if(m_fileTransfers.begin(), m_fileTransfers.end(),
@@ -2282,7 +2428,7 @@ NetworkClipboardPacketResult CNetworkClipboard::Impl::HandleFileNL(
item.record.transfer = localID; item.record.transfer = localID;
item.record.format = KVMFR_CLIPBOARD_FORMAT_FILES; item.record.format = KVMFR_CLIPBOARD_FORMAT_FILES;
item.record.token = KVMFR_CLIPBOARD_FILE_ERROR_CANCELLED; item.record.token = KVMFR_CLIPBOARD_FILE_ERROR_CANCELLED;
if (!QueueTargetNL(std::move(item))) if (!QueueOwnerTargetNL(std::move(item)))
return NetworkClipboardPacketResult::INVALID; return NetworkClipboardPacketResult::INVALID;
m_fileTransfers.erase( m_fileTransfers.erase(
std::remove_if(m_fileTransfers.begin(), m_fileTransfers.end(), std::remove_if(m_fileTransfers.begin(), m_fileTransfers.end(),
@@ -2320,7 +2466,7 @@ NetworkClipboardPacketResult CNetworkClipboard::Impl::HandleFileNL(
item.record.transfer = localID; item.record.transfer = localID;
item.record.format = KVMFR_CLIPBOARD_FORMAT_FILES; item.record.format = KVMFR_CLIPBOARD_FORMAT_FILES;
item.record.token = error; item.record.token = error;
if (!QueueTargetNL(std::move(item))) if (!QueueOwnerTargetNL(std::move(item)))
return NetworkClipboardPacketResult::INVALID; return NetworkClipboardPacketResult::INVALID;
m_fileAcquisitions.erase( m_fileAcquisitions.erase(
std::remove_if(m_fileAcquisitions.begin(), m_fileAcquisitions.end(), std::remove_if(m_fileAcquisitions.begin(), m_fileAcquisitions.end(),
@@ -2344,7 +2490,7 @@ NetworkClipboardPacketResult CNetworkClipboard::Impl::HandleFileNL(
item.record.transfer = localID; item.record.transfer = localID;
item.record.format = KVMFR_CLIPBOARD_FORMAT_FILES; item.record.format = KVMFR_CLIPBOARD_FORMAT_FILES;
item.record.token = error; item.record.token = error;
if (!QueueTargetNL(std::move(item))) if (!QueueOwnerTargetNL(std::move(item)))
return NetworkClipboardPacketResult::INVALID; return NetworkClipboardPacketResult::INVALID;
m_fileTransfers.erase( m_fileTransfers.erase(
std::remove_if(m_fileTransfers.begin(), m_fileTransfers.end(), std::remove_if(m_fileTransfers.begin(), m_fileTransfers.end(),
@@ -2441,8 +2587,10 @@ ClipboardChannelResult CNetworkClipboard::Impl::HandleHelperClipboardNL(
QueueTargetNL(std::move(item)); QueueTargetNL(std::move(item));
return ClipboardChannelResult::ACCEPTED; return ClipboardChannelResult::ACCEPTED;
} }
return record.type == KVMFR_CLIPBOARD_MESSAGE_CANCEL && return ((record.type == KVMFR_CLIPBOARD_MESSAGE_CANCEL &&
ValidCancel(record) ? ValidCancel(record)) ||
(record.type == KVMFR_CLIPBOARD_MESSAGE_DATA &&
ValidData(record))) ?
ClipboardChannelResult::ACCEPTED : ClipboardChannelResult::ACCEPTED :
ClipboardChannelResult::FAILED; ClipboardChannelResult::FAILED;
} }
@@ -3247,6 +3395,40 @@ CNetworkClipboard::~CNetworkClipboard()
DetachSender(); DetachSender();
} }
bool CNetworkClipboard::PeerOwnsInput(
const NetworkClipboardPeer& peer)
{
return m_impl->PeerOwnsInput(peer);
}
bool CNetworkClipboard::RevokeUnauthorizedOwner()
{
NetworkClipboardPeer owner = {};
uint64_t epoch = 0;
{
CSRWSharedLock lock(m_impl->m_lock);
if (!m_impl->m_claimEpoch)
return false;
owner = m_impl->m_owner;
epoch = m_impl->m_claimEpoch;
}
if (PeerOwnsInput(owner))
return false;
CSRWExclusiveLock lock(m_impl->m_lock);
if (m_impl->m_claimEpoch != epoch || !SamePeer(m_impl->m_owner, owner))
return false;
DEBUG_INFO("Revoking network clipboard owner without input control: "
"client=%u generation=%u claim=%llu",
static_cast<unsigned int>(owner.client),
static_cast<unsigned int>(owner.generation),
static_cast<unsigned long long>(epoch));
m_impl->ExpireOwnerNL(true, CLAIM_INPUT_REQUIRED);
return true;
}
bool CNetworkClipboard::AttachSender(INetworkClipboardSender& sender) bool CNetworkClipboard::AttachSender(INetworkClipboardSender& sender)
{ {
CSRWExclusiveLock lifecycleLock(m_impl->m_lifecycleLock); CSRWExclusiveLock lifecycleLock(m_impl->m_lifecycleLock);
@@ -3370,7 +3552,10 @@ NetworkClipboardPacketResult CNetworkClipboard::HandlePacket(
packet.payloadLength > LG_NET_MAX_PAYLOAD_LENGTH) packet.payloadLength > LG_NET_MAX_PAYLOAD_LENGTH)
return NetworkClipboardPacketResult::INVALID; return NetworkClipboardPacketResult::INVALID;
CSRWExclusiveLock processLock(m_impl->m_processLock); CSRWExclusiveLock processLock(m_impl->m_processLock);
RevokeUnauthorizedOwner();
const bool inputOwner = PeerOwnsInput(peer);
NetworkClipboardPacketResult result; NetworkClipboardPacketResult result;
{ {
CSRWExclusiveLock callbackLock(m_impl->m_targetCallbackLock); CSRWExclusiveLock callbackLock(m_impl->m_targetCallbackLock);
@@ -3399,10 +3584,11 @@ NetworkClipboardPacketResult CNetworkClipboard::HandlePacket(
result = packet.envelope.service == LG_NET_SERVICE_CLIPBOARD ? result = packet.envelope.service == LG_NET_SERVICE_CLIPBOARD ?
m_impl->HandleClipboardNL( m_impl->HandleClipboardNL(
*state, packet, bulk, GetTickCount64(), lock) : *state, packet, bulk, inputOwner, GetTickCount64(), lock) :
m_impl->HandleFileNL(*state, packet, bulk); m_impl->HandleFileNL(*state, packet, bulk, inputOwner);
} }
RevokeUnauthorizedOwner();
m_impl->PumpTargets(); m_impl->PumpTargets();
m_impl->PumpSends(); m_impl->PumpSends();
processLock.Unlock(); processLock.Unlock();
@@ -3417,20 +3603,11 @@ void CNetworkClipboard::Process(uint64_t nowMs)
if (!processLock) if (!processLock)
return; return;
RevokeUnauthorizedOwner();
{ {
CSRWExclusiveLock lock(m_impl->m_lock); CSRWExclusiveLock lock(m_impl->m_lock);
if (m_impl->m_claimEpoch && DeadlineReached(nowMs, m_impl->m_claimDeadline)) if (m_impl->m_claimEpoch && DeadlineReached(nowMs, m_impl->m_claimDeadline))
{ m_impl->ExpireOwnerNL(true, 0);
const NetworkClipboardPeer owner = m_impl->m_owner;
const uint64_t epoch = m_impl->m_claimEpoch;
const uint64_t serial = m_impl->m_claimSerial;
m_impl->DropOwnerNL(true);
m_impl->m_claimSerial = serial;
if (!m_impl->QueueClaimStatusNL(
owner, epoch, LG_NET_CLIPBOARD_CLAIM_EXPIRED, 0, 0, 0))
m_impl->m_failed = true;
m_impl->m_claimSerial = 0;
}
} }
m_impl->PumpTargets(); m_impl->PumpTargets();
@@ -3448,6 +3625,7 @@ void CNetworkClipboard::SendReady(
CSRWExclusiveLock processLock = CSRWExclusiveLock::Try(m_impl->m_processLock); CSRWExclusiveLock processLock = CSRWExclusiveLock::Try(m_impl->m_processLock);
if (!processLock) if (!processLock)
return; return;
RevokeUnauthorizedOwner();
{ {
CSRWSharedLock lock(m_impl->m_lock); CSRWSharedLock lock(m_impl->m_lock);
if (!m_impl->FindPeerNL(peer)) if (!m_impl->FindPeerNL(peer))
@@ -3585,6 +3763,17 @@ ClipboardChannelResult CNetworkClipboard::SendClipboard(
{ {
CSRWExclusiveLock processLock(m_impl->m_processLock); CSRWExclusiveLock processLock(m_impl->m_processLock);
ClipboardChannelResult result; ClipboardChannelResult result;
if (RevokeUnauthorizedOwner())
{
{
CSRWExclusiveLock lock(m_impl->m_lock);
m_impl->m_receiveReadyPending = true;
}
m_impl->PumpSends();
processLock.Unlock();
return ClipboardChannelResult::BUSY;
}
{ {
CSRWExclusiveLock lock(m_impl->m_lock); CSRWExclusiveLock lock(m_impl->m_lock);
if (!m_impl->m_started || !m_impl->m_target || !m_impl->m_stateKnown || if (!m_impl->m_started || !m_impl->m_target || !m_impl->m_stateKnown ||
@@ -3592,15 +3781,29 @@ ClipboardChannelResult CNetworkClipboard::SendClipboard(
record.generation != m_impl->m_generation || record.generation != m_impl->m_generation ||
record.version != KVMFR_CLIPBOARD_VERSION || (record.length && !data)) record.version != KVMFR_CLIPBOARD_VERSION || (record.length && !data))
return ClipboardChannelResult::FAILED; return ClipboardChannelResult::FAILED;
if (m_impl->m_hubReleasePending)
{
m_impl->m_receiveReadyPending = true;
return ClipboardChannelResult::BUSY;
}
result = kvmfrClipboardFileMessageType(record.type) ? result = kvmfrClipboardFileMessageType(record.type) ?
m_impl->HandleHelperFileNL(record, data) : m_impl->HandleHelperFileNL(record, data) :
m_impl->HandleHelperClipboardNL(record, data); m_impl->HandleHelperClipboardNL(record, data);
} }
RevokeUnauthorizedOwner();
m_impl->PumpSends(); m_impl->PumpSends();
bool deferNotification = false;
{
CSRWSharedLock lock(m_impl->m_lock);
deferNotification = m_impl->m_hubReleasePending;
}
processLock.Unlock(); processLock.Unlock();
m_impl->NotifyTargetReady(); if (!deferNotification)
m_impl->NotifyFailure(); {
m_impl->NotifyTargetReady();
m_impl->NotifyFailure();
}
return result; return result;
} }

View File

@@ -45,10 +45,13 @@ struct NetworkClipboardPeerInfo
uint32_t fileBulkPayload = 0; uint32_t fileBulkPayload = 0;
}; };
using NetworkClipboardInputAction = void (*)(void * opaque);
enum class NetworkClipboardSendResult enum class NetworkClipboardSendResult
{ {
ACCEPTED, ACCEPTED,
BLOCKED, BLOCKED,
UNAUTHORIZED,
FAILED, FAILED,
}; };
@@ -64,6 +67,10 @@ class INetworkClipboardSender
public: public:
virtual ~INetworkClipboardSender() = default; virtual ~INetworkClipboardSender() = default;
virtual bool ClipboardPeerOwnsInput(
const NetworkClipboardPeer& peer) = 0;
virtual bool ClipboardWithInputAuthority(const NetworkClipboardPeer& peer,
NetworkClipboardInputAction action, void * opaque) = 0;
virtual bool ClipboardPeerInfo( virtual bool ClipboardPeerInfo(
const NetworkClipboardPeer& peer, NetworkClipboardPeerInfo& info) = 0; const NetworkClipboardPeer& peer, NetworkClipboardPeerInfo& info) = 0;
virtual NetworkClipboardSendResult SendClipboard( virtual NetworkClipboardSendResult SendClipboard(
@@ -78,6 +85,9 @@ private:
struct Impl; struct Impl;
std::unique_ptr<Impl> m_impl; std::unique_ptr<Impl> m_impl;
bool PeerOwnsInput(const NetworkClipboardPeer& peer);
bool RevokeUnauthorizedOwner();
public: public:
CNetworkClipboard(); CNetworkClipboard();
~CNetworkClipboard() override; ~CNetworkClipboard() override;

View File

@@ -30,6 +30,7 @@
#include "platform/CPlatformInfo.h" #include "platform/CPlatformInfo.h"
#include "VersionInfo.h" #include "VersionInfo.h"
#include <LGProtocol/NetworkAuth.h> #include <LGProtocol/NetworkAuth.h>
#include <LGProtocol/NetworkClipboard.h>
#include <LGProtocol/NetworkIdentity.h> #include <LGProtocol/NetworkIdentity.h>
#include <LGProtocol/NetworkPacket.h> #include <LGProtocol/NetworkPacket.h>
#include <LGProtocol/NetworkRoute.h> #include <LGProtocol/NetworkRoute.h>
@@ -745,6 +746,96 @@ struct CNetworkServer::Impl
return NetworkAudioSendResult::FAILED; return NetworkAudioSendResult::FAILED;
} }
std::shared_ptr<CNetworkSession> ClipboardInputSession(
const NetworkClipboardPeer& peer)
{
std::shared_ptr<CNetworkSession> session;
{
CSRWSharedLock guard(lock);
for (Connection * connection : connections)
{
if (connection->client != peer.client ||
connection->generation != peer.generation)
continue;
CSRWSharedLock connectionGuard(connection->lock);
const uint16_t inputMask = UINT16_C(1) << LG_NET_SERVICE_INPUT;
if (connection->closing ||
connection->handshake != HandshakeState::READY ||
!(connection->selectedServices & inputMask) ||
!connection->inputClaimantID || !connection->inputClaimEpoch)
return {};
session = connection->session;
break;
}
}
return session;
}
bool ClipboardPeerOwnsInput(const NetworkClipboardPeer& peer)
{
const std::shared_ptr<CNetworkSession> session =
ClipboardInputSession(peer);
return session && session->InputState().owned;
}
bool ClipboardWithInputAuthority(const NetworkClipboardPeer& peer,
NetworkClipboardInputAction action, void * opaque)
{
if (!action)
return false;
const std::shared_ptr<CNetworkSession> session =
ClipboardInputSession(peer);
if (!session)
return false;
struct AuthorityContext
{
Impl * owner = nullptr;
CNetworkSession * session = nullptr;
NetworkClipboardPeer peer = {};
NetworkClipboardInputAction action = nullptr;
void * opaque = nullptr;
bool invoked = false;
} context;
context.owner = this;
context.session = session.get();
context.peer = peer;
context.action = action;
context.opaque = opaque;
const auto invoke = [](void * value)
{
AuthorityContext& authorityContext =
*static_cast<AuthorityContext *>(value);
CSRWSharedLock guard(authorityContext.owner->lock);
for (Connection * connection : authorityContext.owner->connections)
{
if (connection->client != authorityContext.peer.client ||
connection->generation != authorityContext.peer.generation)
continue;
{
CSRWSharedLock connectionGuard(connection->lock);
const uint16_t inputMask = UINT16_C(1) << LG_NET_SERVICE_INPUT;
if (connection->closing ||
connection->handshake != HandshakeState::READY ||
!(connection->selectedServices & inputMask) ||
!connection->inputClaimantID || !connection->inputClaimEpoch ||
connection->session.get() != authorityContext.session)
return;
}
authorityContext.action(authorityContext.opaque);
authorityContext.invoked = true;
return;
}
};
return session->WithInputAuthority(invoke, &context) && context.invoked;
}
bool ClipboardPeerInfo(const NetworkClipboardPeer& peer, bool ClipboardPeerInfo(const NetworkClipboardPeer& peer,
NetworkClipboardPeerInfo& info) NetworkClipboardPeerInfo& info)
{ {
@@ -786,17 +877,12 @@ struct CNetworkServer::Impl
return false; return false;
} }
NetworkClipboardSendResult SendClipboard( NetworkClipboardSendResult SendClipboardLocked(
const NetworkClipboardPeer& peer, bool bulk, LGNetService service, const NetworkClipboardPeer& peer, bool bulk, LGNetService service,
uint16_t messageType, LGNetEnvelopeFlags flags, uint16_t messageType, LGNetEnvelopeFlags flags,
uint64_t componentEpoch, uint64_t requestID, uint64_t componentEpoch, uint64_t requestID,
const void * payload, uint32_t payloadLength) const void * payload, uint32_t payloadLength)
{ {
if (service != LG_NET_SERVICE_CLIPBOARD &&
service != LG_NET_SERVICE_FILE)
return NetworkClipboardSendResult::FAILED;
CSRWSharedLock guard(lock);
for (Connection * connection : connections) for (Connection * connection : connections)
{ {
if (connection->client != peer.client || if (connection->client != peer.client ||
@@ -823,6 +909,65 @@ struct CNetworkServer::Impl
return NetworkClipboardSendResult::FAILED; return NetworkClipboardSendResult::FAILED;
} }
NetworkClipboardSendResult SendClipboard(
const NetworkClipboardPeer& peer, bool bulk, LGNetService service,
uint16_t messageType, LGNetEnvelopeFlags flags,
uint64_t componentEpoch, uint64_t requestID,
const void * payload, uint32_t payloadLength)
{
if (service != LG_NET_SERVICE_CLIPBOARD &&
service != LG_NET_SERVICE_FILE)
return NetworkClipboardSendResult::FAILED;
const bool claimResult =
service == LG_NET_SERVICE_CLIPBOARD &&
messageType == LG_NET_CLIPBOARD_MESSAGE_CLAIM_RESULT;
if (claimResult)
{
CSRWSharedLock guard(lock);
return SendClipboardLocked(peer, bulk, service, messageType, flags,
componentEpoch, requestID, payload, payloadLength);
}
struct SendContext
{
Impl * owner = nullptr;
NetworkClipboardPeer peer = {};
bool bulk = false;
LGNetService service = 0;
uint16_t messageType = 0;
LGNetEnvelopeFlags flags = 0;
uint64_t componentEpoch = 0;
uint64_t requestID = 0;
const void * payload = nullptr;
uint32_t payloadLength = 0;
NetworkClipboardSendResult result =
NetworkClipboardSendResult::FAILED;
} context;
context.owner = this;
context.peer = peer;
context.bulk = bulk;
context.service = service;
context.messageType = messageType;
context.flags = flags;
context.componentEpoch = componentEpoch;
context.requestID = requestID;
context.payload = payload;
context.payloadLength = payloadLength;
const auto send = [](void * value)
{
SendContext& sendContext = *static_cast<SendContext *>(value);
sendContext.result = sendContext.owner->SendClipboardLocked(
sendContext.peer, sendContext.bulk, sendContext.service,
sendContext.messageType, sendContext.flags,
sendContext.componentEpoch, sendContext.requestID,
sendContext.payload, sendContext.payloadLength);
};
return ClipboardWithInputAuthority(peer, send, &context) ?
context.result : NetworkClipboardSendResult::UNAUTHORIZED;
}
bool VideoPeerInfo(const NetworkVideoPeer& peer, bool VideoPeerInfo(const NetworkVideoPeer& peer,
NetworkVideoPeerInfo& info) NetworkVideoPeerInfo& info)
{ {
@@ -3016,12 +3161,15 @@ retry:
memset(connection.keyboardKeys, 0, sizeof(connection.keyboardKeys)); memset(connection.keyboardKeys, 0, sizeof(connection.keyboardKeys));
} }
static void InvalidateInputClaim(Connection& connection, void InvalidateInputClaim(Connection& connection,
uint64_t claimEpoch) uint64_t claimEpoch)
{ {
CSRWExclusiveLock guard(connection.lock); {
if (connection.inputClaimEpoch == claimEpoch) CSRWExclusiveLock guard(connection.lock);
ClearInputStateLocked(connection, true); if (connection.inputClaimEpoch == claimEpoch)
ClearInputStateLocked(connection, true);
}
clipboard.Process(GetTickCount64());
} }
bool SendInputStatus(Connection& connection, uint16_t messageType, bool SendInputStatus(Connection& connection, uint16_t messageType,
@@ -3131,6 +3279,7 @@ retry:
ClearInputStateLocked(connection, false); ClearInputStateLocked(connection, false);
} }
clipboard.Process(GetTickCount64());
const LGNetInputStatusCode code = InputStatusCode(result); const LGNetInputStatusCode code = InputStatusCode(result);
return SendInputStatus(connection, LG_NET_INPUT_MESSAGE_CLAIM_RESULT, return SendInputStatus(connection, LG_NET_INPUT_MESSAGE_CLAIM_RESULT,
code, claim.claimEpoch, packet.envelope.requestID, code, claim.claimEpoch, packet.envelope.requestID,
@@ -3233,6 +3382,9 @@ retry:
if (result == InputResult::ACCEPTED && if (result == InputResult::ACCEPTED &&
packet.envelope.messageType == LG_NET_INPUT_MESSAGE_KEEPALIVE) packet.envelope.messageType == LG_NET_INPUT_MESSAGE_KEEPALIVE)
return SendInputLEDs(connection); return SendInputLEDs(connection);
if (result == InputResult::ACCEPTED &&
packet.envelope.messageType == LG_NET_INPUT_MESSAGE_RELEASE)
clipboard.Process(GetTickCount64());
const LGNetInputStatusCode code = const LGNetInputStatusCode code =
packet.envelope.messageType == LG_NET_INPUT_MESSAGE_RELEASE && packet.envelope.messageType == LG_NET_INPUT_MESSAGE_RELEASE &&
@@ -5231,6 +5383,20 @@ NetworkAudioSendResult CNetworkServer::SendAudio(
NetworkAudioSendResult::FAILED; NetworkAudioSendResult::FAILED;
} }
bool CNetworkServer::ClipboardPeerOwnsInput(
const NetworkClipboardPeer& peer)
{
return m_impl && m_impl->ClipboardPeerOwnsInput(peer);
}
bool CNetworkServer::ClipboardWithInputAuthority(
const NetworkClipboardPeer& peer, NetworkClipboardInputAction action,
void * opaque)
{
return m_impl &&
m_impl->ClipboardWithInputAuthority(peer, action, opaque);
}
bool CNetworkServer::ClipboardPeerInfo( bool CNetworkServer::ClipboardPeerInfo(
const NetworkClipboardPeer& peer, NetworkClipboardPeerInfo& info) const NetworkClipboardPeer& peer, NetworkClipboardPeerInfo& info)
{ {

View File

@@ -59,6 +59,10 @@ public:
uint64_t componentEpoch, uint64_t requestID, uint64_t componentEpoch, uint64_t requestID,
const void * payload, uint32_t payloadLength) override; const void * payload, uint32_t payloadLength) override;
bool ClipboardPeerOwnsInput(
const NetworkClipboardPeer& peer) override;
bool ClipboardWithInputAuthority(const NetworkClipboardPeer& peer,
NetworkClipboardInputAction action, void * opaque) override;
bool ClipboardPeerInfo(const NetworkClipboardPeer& peer, bool ClipboardPeerInfo(const NetworkClipboardPeer& peer,
NetworkClipboardPeerInfo& info) override; NetworkClipboardPeerInfo& info) override;
NetworkClipboardSendResult SendClipboard( NetworkClipboardSendResult SendClipboard(

View File

@@ -116,6 +116,13 @@ InputTargetState CNetworkInputSource::GetState(
return m_target ? m_target->GetState(source) : InputTargetState {}; return m_target ? m_target->GetState(source) : InputTargetState {};
} }
bool CNetworkInputSource::WithAuthority(const InputSourceId& source,
InputAuthorityAction action, void * opaque) const
{
CSRWSharedLock lock(m_lock);
return m_target && m_target->WithAuthority(source, action, opaque);
}
InputResult CNetworkInputSource::Claim(const InputSourceId& source) InputResult CNetworkInputSource::Claim(const InputSourceId& source)
{ {
CSRWSharedLock lock(m_lock); CSRWSharedLock lock(m_lock);
@@ -519,6 +526,13 @@ InputTargetState CNetworkSession::InputState() const
return m_open ? m_input.GetState(m_source) : InputTargetState {}; return m_open ? m_input.GetState(m_source) : InputTargetState {};
} }
bool CNetworkSession::WithInputAuthority(
InputAuthorityAction action, void * opaque) const
{
CSRWSharedLock lock(m_lifecycleLock);
return m_open && m_input.WithAuthority(m_source, action, opaque);
}
InputResult CNetworkSession::ClaimInput() InputResult CNetworkSession::ClaimInput()
{ {
CSRWSharedLock lock(m_lifecycleLock); CSRWSharedLock lock(m_lifecycleLock);

View File

@@ -97,6 +97,8 @@ public:
void Stop() override; void Stop() override;
InputTargetState GetState(const InputSourceId& source) const; InputTargetState GetState(const InputSourceId& source) const;
bool WithAuthority(const InputSourceId& source,
InputAuthorityAction action, void * opaque) const;
InputResult Claim(const InputSourceId& source); InputResult Claim(const InputSourceId& source);
InputResult Touch(const InputSourceId& source); InputResult Touch(const InputSourceId& source);
InputResult Release(const InputSourceId& source, bool reset); InputResult Release(const InputSourceId& source, bool reset);
@@ -197,6 +199,8 @@ public:
bool TakeControlStatus(NetworkControlStatus& status); bool TakeControlStatus(NetworkControlStatus& status);
InputTargetState InputState() const; InputTargetState InputState() const;
bool WithInputAuthority(
InputAuthorityAction action, void * opaque) const;
InputResult ClaimInput(); InputResult ClaimInput();
InputResult TouchInput(); InputResult TouchInput();
InputResult ReleaseInput(bool reset); InputResult ReleaseInput(bool reset);