/** * @file * Implementation for the TransferSession class. * // TODO: Support Asynchronous mode. Currently, only Synchronous mode is supported. */ #include #include #include #include #include #include #include #include #include #include #include #include namespace { constexpr uint8_t kBdxVersion = 0; ///< The version of this implementation of the BDX spec /** * @brief * Allocate a new PacketBuffer and write data from a BDX message struct. */ CHIP_ERROR WriteToPacketBuffer(const ::chip::bdx::BdxMessage & msgStruct, ::chip::System::PacketBufferHandle & msgBuf) { size_t msgDataSize = msgStruct.MessageSize(); ::chip::Encoding::LittleEndian::PacketBufferWriter bbuf(chip::MessagePacketBuffer::New(msgDataSize), msgDataSize); if (bbuf.IsNull()) { return CHIP_ERROR_NO_MEMORY; } msgStruct.WriteToBuffer(bbuf); msgBuf = bbuf.Finalize(); if (msgBuf.IsNull()) { return CHIP_ERROR_NO_MEMORY; } return CHIP_NO_ERROR; } template void PrepareOutgoingMessageEvent(MessageType messageType, chip::bdx::TransferSession::OutputEventType & pendingOutput, chip::bdx::TransferSession::MessageTypeData & outputMsgType) { static_assert(std::is_same, uint8_t>::value, "Cast is not safe"); pendingOutput = chip::bdx::TransferSession::OutputEventType::kMsgToSend; outputMsgType.ProtocolId = chip::Protocols::MessageTypeTraits::ProtocolId(); outputMsgType.MessageType = static_cast(messageType); } } // anonymous namespace namespace chip { namespace bdx { TransferSession::TransferSession() { mSuppportedXferOpts.ClearAll(); } void TransferSession::PollOutput(OutputEvent & event, System::Clock::Timestamp curTime) { event = OutputEvent(OutputEventType::kNone); if (mShouldInitTimeoutStart) { mTimeoutStartTime = curTime; mShouldInitTimeoutStart = false; } if (mAwaitingResponse && ((curTime - mTimeoutStartTime) >= mTimeout)) { event = OutputEvent(OutputEventType::kTransferTimeout); mState = TransferState::kErrorState; mAwaitingResponse = false; return; } switch (mPendingOutput) { case OutputEventType::kNone: event = OutputEvent(OutputEventType::kNone); break; case OutputEventType::kInternalError: event = OutputEvent::StatusReportEvent(OutputEventType::kInternalError, mStatusReportData); break; case OutputEventType::kStatusReceived: event = OutputEvent::StatusReportEvent(OutputEventType::kStatusReceived, mStatusReportData); break; case OutputEventType::kMsgToSend: event = OutputEvent::MsgToSendEvent(mMsgTypeData, std::move(mPendingMsgHandle)); mTimeoutStartTime = curTime; break; case OutputEventType::kInitReceived: event = OutputEvent::TransferInitEvent(mTransferRequestData, std::move(mPendingMsgHandle)); break; case OutputEventType::kAcceptReceived: event = OutputEvent::TransferAcceptEvent(mTransferAcceptData, std::move(mPendingMsgHandle)); break; case OutputEventType::kQueryReceived: event = OutputEvent(OutputEventType::kQueryReceived); break; case OutputEventType::kQueryWithSkipReceived: event = OutputEvent::QueryWithSkipEvent(mBytesToSkip); break; case OutputEventType::kBlockReceived: event = OutputEvent::BlockDataEvent(mBlockEventData, std::move(mPendingMsgHandle)); break; case OutputEventType::kAckReceived: event = OutputEvent(OutputEventType::kAckReceived); break; case OutputEventType::kAckEOFReceived: event = OutputEvent(OutputEventType::kAckEOFReceived); break; default: event = OutputEvent(OutputEventType::kNone); break; } // If there's no other pending output but an error occurred or was received, then continue to output the error. // This ensures that when the TransferSession encounters an error and needs to send a StatusReport, both a kMsgToSend and a // kInternalError output event will be emitted. if (event.EventType == OutputEventType::kNone && mState == TransferState::kErrorState) { event = OutputEvent::StatusReportEvent(OutputEventType::kInternalError, mStatusReportData); } mPendingOutput = OutputEventType::kNone; } void TransferSession::GetNextAction(OutputEvent & event) { PollOutput(event, System::SystemClock().GetMonotonicTimestamp()); } CHIP_ERROR TransferSession::StartTransfer(TransferRole role, const TransferInitData & initData, System::Clock::Timeout timeout) { VerifyOrReturnError(mState == TransferState::kUnitialized, CHIP_ERROR_INCORRECT_STATE); mRole = role; mTimeout = timeout; // Set transfer parameters. They may be overridden later by an Accept message mSuppportedXferOpts = initData.TransferCtlFlags; mMaxSupportedBlockSize = initData.MaxBlockSize; mStartOffset = initData.StartOffset; mTransferLength = initData.Length; // Prepare TransferInit message TransferInit initMsg; initMsg.TransferCtlOptions = initData.TransferCtlFlags; initMsg.Version = kBdxVersion; initMsg.MaxBlockSize = mMaxSupportedBlockSize; initMsg.StartOffset = mStartOffset; initMsg.MaxLength = mTransferLength; initMsg.FileDesignator = initData.FileDesignator; initMsg.FileDesLength = initData.FileDesLength; initMsg.Metadata = initData.Metadata; initMsg.MetadataLength = initData.MetadataLength; ReturnErrorOnFailure(WriteToPacketBuffer(initMsg, mPendingMsgHandle)); const MessageType msgType = (mRole == TransferRole::kSender) ? MessageType::SendInit : MessageType::ReceiveInit; #if CHIP_AUTOMATION_LOGGING ChipLogAutomation("Sending BDX Message"); initMsg.LogMessage(msgType); #endif // CHIP_AUTOMATION_LOGGING mState = TransferState::kAwaitingAccept; mAwaitingResponse = true; PrepareOutgoingMessageEvent(msgType, mPendingOutput, mMsgTypeData); return CHIP_NO_ERROR; } CHIP_ERROR TransferSession::WaitForTransfer(TransferRole role, BitFlags xferControlOpts, uint16_t maxBlockSize, System::Clock::Timeout timeout) { VerifyOrReturnError(mState == TransferState::kUnitialized, CHIP_ERROR_INCORRECT_STATE); // Used to determine compatibility with any future TransferInit parameters mRole = role; mTimeout = timeout; mSuppportedXferOpts = xferControlOpts; mMaxSupportedBlockSize = maxBlockSize; mState = TransferState::kAwaitingInitMsg; return CHIP_NO_ERROR; } CHIP_ERROR TransferSession::AcceptTransfer(const TransferAcceptData & acceptData) { MessageType msgType; const BitFlags proposedControlOpts(mTransferRequestData.TransferCtlFlags); VerifyOrReturnError(mState == TransferState::kNegotiateTransferParams, CHIP_ERROR_INCORRECT_STATE); VerifyOrReturnError(mPendingOutput == OutputEventType::kNone, CHIP_ERROR_INCORRECT_STATE); // Don't allow a Control method that wasn't supported by the initiator // MaxBlockSize can't be larger than the proposed value VerifyOrReturnError(proposedControlOpts.Has(acceptData.ControlMode), CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(acceptData.MaxBlockSize <= mTransferRequestData.MaxBlockSize, CHIP_ERROR_INVALID_ARGUMENT); mTransferMaxBlockSize = acceptData.MaxBlockSize; if (mRole == TransferRole::kSender) { mStartOffset = acceptData.StartOffset; mTransferLength = acceptData.Length; ReceiveAccept acceptMsg; acceptMsg.TransferCtlFlags.Set(acceptData.ControlMode); acceptMsg.Version = mTransferVersion; acceptMsg.MaxBlockSize = acceptData.MaxBlockSize; acceptMsg.StartOffset = acceptData.StartOffset; acceptMsg.Length = acceptData.Length; acceptMsg.Metadata = acceptData.Metadata; acceptMsg.MetadataLength = acceptData.MetadataLength; ReturnErrorOnFailure(WriteToPacketBuffer(acceptMsg, mPendingMsgHandle)); msgType = MessageType::ReceiveAccept; #if CHIP_AUTOMATION_LOGGING ChipLogAutomation("Sending BDX Message"); acceptMsg.LogMessage(msgType); #endif // CHIP_AUTOMATION_LOGGING } else { SendAccept acceptMsg; acceptMsg.TransferCtlFlags.Set(acceptData.ControlMode); acceptMsg.Version = mTransferVersion; acceptMsg.MaxBlockSize = acceptData.MaxBlockSize; acceptMsg.Metadata = acceptData.Metadata; acceptMsg.MetadataLength = acceptData.MetadataLength; ReturnErrorOnFailure(WriteToPacketBuffer(acceptMsg, mPendingMsgHandle)); msgType = MessageType::SendAccept; #if CHIP_AUTOMATION_LOGGING ChipLogAutomation("Sending BDX Message"); acceptMsg.LogMessage(msgType); #endif // CHIP_AUTOMATION_LOGGING } mState = TransferState::kTransferInProgress; if ((mRole == TransferRole::kReceiver && mControlMode == TransferControlFlags::kSenderDrive) || (mRole == TransferRole::kSender && mControlMode == TransferControlFlags::kReceiverDrive)) { mAwaitingResponse = true; } PrepareOutgoingMessageEvent(msgType, mPendingOutput, mMsgTypeData); return CHIP_NO_ERROR; } CHIP_ERROR TransferSession::RejectTransfer(StatusCode reason) { VerifyOrReturnError(mState == TransferState::kNegotiateTransferParams, CHIP_ERROR_INCORRECT_STATE); VerifyOrReturnError(mPendingOutput == OutputEventType::kNone, CHIP_ERROR_INCORRECT_STATE); PrepareStatusReport(reason); mState = TransferState::kTransferDone; return CHIP_NO_ERROR; } CHIP_ERROR TransferSession::PrepareBlockQuery() { const MessageType msgType = MessageType::BlockQuery; VerifyOrReturnError(mState == TransferState::kTransferInProgress, CHIP_ERROR_INCORRECT_STATE); VerifyOrReturnError(mRole == TransferRole::kReceiver, CHIP_ERROR_INCORRECT_STATE); VerifyOrReturnError(mPendingOutput == OutputEventType::kNone, CHIP_ERROR_INCORRECT_STATE); VerifyOrReturnError(!mAwaitingResponse, CHIP_ERROR_INCORRECT_STATE); BlockQuery queryMsg; queryMsg.BlockCounter = mNextQueryNum; ReturnErrorOnFailure(WriteToPacketBuffer(queryMsg, mPendingMsgHandle)); #if CHIP_AUTOMATION_LOGGING ChipLogAutomation("Sending BDX Message"); queryMsg.LogMessage(msgType); #endif // CHIP_AUTOMATION_LOGGING mAwaitingResponse = true; mLastQueryNum = mNextQueryNum++; PrepareOutgoingMessageEvent(msgType, mPendingOutput, mMsgTypeData); return CHIP_NO_ERROR; } CHIP_ERROR TransferSession::PrepareBlockQueryWithSkip(const uint64_t & bytesToSkip) { const MessageType msgType = MessageType::BlockQueryWithSkip; VerifyOrReturnError(mState == TransferState::kTransferInProgress, CHIP_ERROR_INCORRECT_STATE); VerifyOrReturnError(mRole == TransferRole::kReceiver, CHIP_ERROR_INCORRECT_STATE); VerifyOrReturnError(mPendingOutput == OutputEventType::kNone, CHIP_ERROR_INCORRECT_STATE); VerifyOrReturnError(!mAwaitingResponse, CHIP_ERROR_INCORRECT_STATE); BlockQueryWithSkip queryMsg; queryMsg.BlockCounter = mNextQueryNum; queryMsg.BytesToSkip = bytesToSkip; ReturnErrorOnFailure(WriteToPacketBuffer(queryMsg, mPendingMsgHandle)); #if CHIP_AUTOMATION_LOGGING ChipLogAutomation("Sending BDX Message"); queryMsg.LogMessage(msgType); #endif // CHIP_AUTOMATION_LOGGING mAwaitingResponse = true; mLastQueryNum = mNextQueryNum++; PrepareOutgoingMessageEvent(msgType, mPendingOutput, mMsgTypeData); return CHIP_NO_ERROR; } CHIP_ERROR TransferSession::PrepareBlock(const BlockData & inData) { VerifyOrReturnError(mState == TransferState::kTransferInProgress, CHIP_ERROR_INCORRECT_STATE); VerifyOrReturnError(mRole == TransferRole::kSender, CHIP_ERROR_INCORRECT_STATE); VerifyOrReturnError(mPendingOutput == OutputEventType::kNone, CHIP_ERROR_INCORRECT_STATE); VerifyOrReturnError(!mAwaitingResponse, CHIP_ERROR_INCORRECT_STATE); // Verify non-zero data is provided and is no longer than MaxBlockSize (BlockEOF may contain 0 length data) VerifyOrReturnError((inData.Data != nullptr) && (inData.Length <= mTransferMaxBlockSize), CHIP_ERROR_INVALID_ARGUMENT); DataBlock blockMsg; blockMsg.BlockCounter = mNextBlockNum; blockMsg.Data = inData.Data; blockMsg.DataLength = inData.Length; ReturnErrorOnFailure(WriteToPacketBuffer(blockMsg, mPendingMsgHandle)); const MessageType msgType = inData.IsEof ? MessageType::BlockEOF : MessageType::Block; #if CHIP_AUTOMATION_LOGGING ChipLogAutomation("Sending BDX Message"); blockMsg.LogMessage(msgType); #endif // CHIP_AUTOMATION_LOGGING if (msgType == MessageType::BlockEOF) { mState = TransferState::kAwaitingEOFAck; } mAwaitingResponse = true; mLastBlockNum = mNextBlockNum++; PrepareOutgoingMessageEvent(msgType, mPendingOutput, mMsgTypeData); return CHIP_NO_ERROR; } CHIP_ERROR TransferSession::PrepareBlockAck() { VerifyOrReturnError(mRole == TransferRole::kReceiver, CHIP_ERROR_INCORRECT_STATE); VerifyOrReturnError((mState == TransferState::kTransferInProgress) || (mState == TransferState::kReceivedEOF), CHIP_ERROR_INCORRECT_STATE); VerifyOrReturnError(mPendingOutput == OutputEventType::kNone, CHIP_ERROR_INCORRECT_STATE); CounterMessage ackMsg; ackMsg.BlockCounter = mLastBlockNum; const MessageType msgType = (mState == TransferState::kReceivedEOF) ? MessageType::BlockAckEOF : MessageType::BlockAck; ReturnErrorOnFailure(WriteToPacketBuffer(ackMsg, mPendingMsgHandle)); #if CHIP_AUTOMATION_LOGGING ChipLogAutomation("Sending BDX Message"); ackMsg.LogMessage(msgType); #endif // CHIP_AUTOMATION_LOGGING if (mState == TransferState::kTransferInProgress) { if (mControlMode == TransferControlFlags::kSenderDrive) { // In Sender Drive, a BlockAck is implied to also be a query for the next Block, so expect to receive a Block // message. mLastQueryNum = ackMsg.BlockCounter + 1; mAwaitingResponse = true; } } else if (mState == TransferState::kReceivedEOF) { mState = TransferState::kTransferDone; mAwaitingResponse = false; } PrepareOutgoingMessageEvent(msgType, mPendingOutput, mMsgTypeData); return CHIP_NO_ERROR; } CHIP_ERROR TransferSession::AbortTransfer(StatusCode reason) { VerifyOrReturnError((mState != TransferState::kUnitialized) && (mState != TransferState::kTransferDone) && (mState != TransferState::kErrorState), CHIP_ERROR_INCORRECT_STATE); PrepareStatusReport(reason); return CHIP_NO_ERROR; } void TransferSession::Reset() { mPendingOutput = OutputEventType::kNone; mState = TransferState::kUnitialized; mSuppportedXferOpts.ClearAll(); mTransferVersion = 0; mMaxSupportedBlockSize = 0; mStartOffset = 0; mTransferLength = 0; mTransferMaxBlockSize = 0; mPendingMsgHandle = nullptr; mNumBytesProcessed = 0; mLastBlockNum = 0; mNextBlockNum = 0; mLastQueryNum = 0; mNextQueryNum = 0; mTimeout = System::Clock::kZero; mTimeoutStartTime = System::Clock::kZero; mShouldInitTimeoutStart = true; mAwaitingResponse = false; } CHIP_ERROR TransferSession::HandleMessageReceived(const PayloadHeader & payloadHeader, System::PacketBufferHandle msg, System::Clock::Timestamp curTime) { VerifyOrReturnError(!msg.IsNull(), CHIP_ERROR_INVALID_ARGUMENT); if (payloadHeader.HasProtocol(Protocols::BDX::Id)) { ReturnErrorOnFailure(HandleBdxMessage(payloadHeader, std::move(msg))); mTimeoutStartTime = curTime; } else if (payloadHeader.HasMessageType(Protocols::SecureChannel::MsgType::StatusReport)) { ReturnErrorOnFailure(HandleStatusReportMessage(payloadHeader, std::move(msg))); } else { return CHIP_ERROR_INVALID_MESSAGE_TYPE; } return CHIP_NO_ERROR; } // Return CHIP_ERROR only if there was a problem decoding the message. Otherwise, call PrepareStatusReport(). CHIP_ERROR TransferSession::HandleBdxMessage(const PayloadHeader & header, System::PacketBufferHandle msg) { VerifyOrReturnError(!msg.IsNull(), CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(mPendingOutput == OutputEventType::kNone, CHIP_ERROR_INCORRECT_STATE); const MessageType msgType = static_cast(header.GetMessageType()); #if CHIP_AUTOMATION_LOGGING ChipLogAutomation("Handling received BDX Message"); #endif // CHIP_AUTOMATION_LOGGING switch (msgType) { case MessageType::SendInit: case MessageType::ReceiveInit: HandleTransferInit(msgType, std::move(msg)); break; case MessageType::SendAccept: HandleSendAccept(std::move(msg)); break; case MessageType::ReceiveAccept: HandleReceiveAccept(std::move(msg)); break; case MessageType::BlockQuery: HandleBlockQuery(std::move(msg)); break; case MessageType::BlockQueryWithSkip: HandleBlockQueryWithSkip(std::move(msg)); break; case MessageType::Block: HandleBlock(std::move(msg)); break; case MessageType::BlockEOF: HandleBlockEOF(std::move(msg)); break; case MessageType::BlockAck: HandleBlockAck(std::move(msg)); break; case MessageType::BlockAckEOF: HandleBlockAckEOF(std::move(msg)); break; default: return CHIP_ERROR_INVALID_MESSAGE_TYPE; } return CHIP_NO_ERROR; } /** * @brief * Parse a StatusReport message and prepare to emit an OutputEvent with the message data. * * NOTE: BDX does not currently expect to ever use a "Success" general code, so it will be treated as an error along with any * other code. */ CHIP_ERROR TransferSession::HandleStatusReportMessage(const PayloadHeader & header, System::PacketBufferHandle msg) { VerifyOrReturnError(!msg.IsNull(), CHIP_ERROR_INVALID_ARGUMENT); mState = TransferState::kErrorState; mAwaitingResponse = false; Protocols::SecureChannel::StatusReport report; ReturnErrorOnFailure(report.Parse(std::move(msg))); VerifyOrReturnError((report.GetProtocolId() == Protocols::BDX::Id), CHIP_ERROR_INVALID_MESSAGE_TYPE); mStatusReportData.statusCode = static_cast(report.GetProtocolCode()); mPendingOutput = OutputEventType::kStatusReceived; return CHIP_NO_ERROR; } void TransferSession::HandleTransferInit(MessageType msgType, System::PacketBufferHandle msgData) { VerifyOrReturn(mState == TransferState::kAwaitingInitMsg, PrepareStatusReport(StatusCode::kUnexpectedMessage)); if (mRole == TransferRole::kSender) { VerifyOrReturn(msgType == MessageType::ReceiveInit, PrepareStatusReport(StatusCode::kUnexpectedMessage)); } else { VerifyOrReturn(msgType == MessageType::SendInit, PrepareStatusReport(StatusCode::kUnexpectedMessage)); } TransferInit transferInit; const CHIP_ERROR err = transferInit.Parse(msgData.Retain()); VerifyOrReturn(err == CHIP_NO_ERROR, PrepareStatusReport(StatusCode::kBadMessageContents)); ResolveTransferControlOptions(transferInit.TransferCtlOptions); mTransferVersion = std::min(kBdxVersion, transferInit.Version); mTransferMaxBlockSize = std::min(mMaxSupportedBlockSize, transferInit.MaxBlockSize); // Accept for now, they may be changed or rejected by the peer if this is a ReceiveInit mStartOffset = transferInit.StartOffset; mTransferLength = transferInit.MaxLength; // Store the Request data to share with the caller for verification mTransferRequestData.TransferCtlFlags = transferInit.TransferCtlOptions; mTransferRequestData.MaxBlockSize = transferInit.MaxBlockSize; mTransferRequestData.StartOffset = transferInit.StartOffset; mTransferRequestData.Length = transferInit.MaxLength; mTransferRequestData.FileDesignator = transferInit.FileDesignator; mTransferRequestData.FileDesLength = transferInit.FileDesLength; mTransferRequestData.Metadata = transferInit.Metadata; mTransferRequestData.MetadataLength = transferInit.MetadataLength; mPendingMsgHandle = std::move(msgData); mPendingOutput = OutputEventType::kInitReceived; mState = TransferState::kNegotiateTransferParams; #if CHIP_AUTOMATION_LOGGING transferInit.LogMessage(msgType); #endif // CHIP_AUTOMATION_LOGGING } void TransferSession::HandleReceiveAccept(System::PacketBufferHandle msgData) { VerifyOrReturn(mRole == TransferRole::kReceiver, PrepareStatusReport(StatusCode::kUnexpectedMessage)); VerifyOrReturn(mState == TransferState::kAwaitingAccept, PrepareStatusReport(StatusCode::kUnexpectedMessage)); ReceiveAccept rcvAcceptMsg; const CHIP_ERROR err = rcvAcceptMsg.Parse(msgData.Retain()); VerifyOrReturn(err == CHIP_NO_ERROR, PrepareStatusReport(StatusCode::kBadMessageContents)); // Verify that Accept parameters are compatible with the original proposed parameters ReturnOnFailure(VerifyProposedMode(rcvAcceptMsg.TransferCtlFlags)); mTransferMaxBlockSize = rcvAcceptMsg.MaxBlockSize; mStartOffset = rcvAcceptMsg.StartOffset; mTransferLength = rcvAcceptMsg.Length; // Note: if VerifyProposedMode() returned with no error, then mControlMode must match the proposed mode in the ReceiveAccept // message mTransferAcceptData.ControlMode = mControlMode; mTransferAcceptData.MaxBlockSize = rcvAcceptMsg.MaxBlockSize; mTransferAcceptData.StartOffset = rcvAcceptMsg.StartOffset; mTransferAcceptData.Length = rcvAcceptMsg.Length; mTransferAcceptData.Metadata = rcvAcceptMsg.Metadata; mTransferAcceptData.MetadataLength = rcvAcceptMsg.MetadataLength; mPendingMsgHandle = std::move(msgData); mPendingOutput = OutputEventType::kAcceptReceived; mAwaitingResponse = (mControlMode == TransferControlFlags::kSenderDrive); mState = TransferState::kTransferInProgress; #if CHIP_AUTOMATION_LOGGING rcvAcceptMsg.LogMessage(MessageType::ReceiveAccept); #endif // CHIP_AUTOMATION_LOGGING } void TransferSession::HandleSendAccept(System::PacketBufferHandle msgData) { VerifyOrReturn(mRole == TransferRole::kSender, PrepareStatusReport(StatusCode::kUnexpectedMessage)); VerifyOrReturn(mState == TransferState::kAwaitingAccept, PrepareStatusReport(StatusCode::kUnexpectedMessage)); SendAccept sendAcceptMsg; const CHIP_ERROR err = sendAcceptMsg.Parse(msgData.Retain()); VerifyOrReturn(err == CHIP_NO_ERROR, PrepareStatusReport(StatusCode::kBadMessageContents)); // Verify that Accept parameters are compatible with the original proposed parameters ReturnOnFailure(VerifyProposedMode(sendAcceptMsg.TransferCtlFlags)); // Note: if VerifyProposedMode() returned with no error, then mControlMode must match the proposed mode in the SendAccept // message mTransferMaxBlockSize = sendAcceptMsg.MaxBlockSize; mTransferAcceptData.ControlMode = mControlMode; mTransferAcceptData.MaxBlockSize = sendAcceptMsg.MaxBlockSize; mTransferAcceptData.StartOffset = mStartOffset; // Not included in SendAccept msg, so use member mTransferAcceptData.Length = mTransferLength; // Not included in SendAccept msg, so use member mTransferAcceptData.Metadata = sendAcceptMsg.Metadata; mTransferAcceptData.MetadataLength = sendAcceptMsg.MetadataLength; mPendingMsgHandle = std::move(msgData); mPendingOutput = OutputEventType::kAcceptReceived; mAwaitingResponse = (mControlMode == TransferControlFlags::kReceiverDrive); mState = TransferState::kTransferInProgress; #if CHIP_AUTOMATION_LOGGING sendAcceptMsg.LogMessage(MessageType::SendAccept); #endif // CHIP_AUTOMATION_LOGGING } void TransferSession::HandleBlockQuery(System::PacketBufferHandle msgData) { VerifyOrReturn(mRole == TransferRole::kSender, PrepareStatusReport(StatusCode::kUnexpectedMessage)); VerifyOrReturn(mState == TransferState::kTransferInProgress, PrepareStatusReport(StatusCode::kUnexpectedMessage)); VerifyOrReturn(mAwaitingResponse, PrepareStatusReport(StatusCode::kUnexpectedMessage)); BlockQuery query; const CHIP_ERROR err = query.Parse(std::move(msgData)); VerifyOrReturn(err == CHIP_NO_ERROR, PrepareStatusReport(StatusCode::kBadMessageContents)); VerifyOrReturn(query.BlockCounter == mNextBlockNum, PrepareStatusReport(StatusCode::kBadBlockCounter)); mPendingOutput = OutputEventType::kQueryReceived; mAwaitingResponse = false; mLastQueryNum = query.BlockCounter; #if CHIP_AUTOMATION_LOGGING query.LogMessage(MessageType::BlockQuery); #endif // CHIP_AUTOMATION_LOGGING } void TransferSession::HandleBlockQueryWithSkip(System::PacketBufferHandle msgData) { VerifyOrReturn(mRole == TransferRole::kSender, PrepareStatusReport(StatusCode::kUnexpectedMessage)); VerifyOrReturn(mState == TransferState::kTransferInProgress, PrepareStatusReport(StatusCode::kUnexpectedMessage)); VerifyOrReturn(mAwaitingResponse, PrepareStatusReport(StatusCode::kUnexpectedMessage)); BlockQueryWithSkip query; const CHIP_ERROR err = query.Parse(std::move(msgData)); VerifyOrReturn(err == CHIP_NO_ERROR, PrepareStatusReport(StatusCode::kBadMessageContents)); VerifyOrReturn(query.BlockCounter == mNextBlockNum, PrepareStatusReport(StatusCode::kBadBlockCounter)); mPendingOutput = OutputEventType::kQueryWithSkipReceived; mAwaitingResponse = false; mLastQueryNum = query.BlockCounter; mBytesToSkip.BytesToSkip = query.BytesToSkip; #if CHIP_AUTOMATION_LOGGING query.LogMessage(MessageType::BlockQueryWithSkip); #endif // CHIP_AUTOMATION_LOGGING } void TransferSession::HandleBlock(System::PacketBufferHandle msgData) { VerifyOrReturn(mRole == TransferRole::kReceiver, PrepareStatusReport(StatusCode::kUnexpectedMessage)); VerifyOrReturn(mState == TransferState::kTransferInProgress, PrepareStatusReport(StatusCode::kUnexpectedMessage)); VerifyOrReturn(mAwaitingResponse, PrepareStatusReport(StatusCode::kUnexpectedMessage)); Block blockMsg; const CHIP_ERROR err = blockMsg.Parse(msgData.Retain()); VerifyOrReturn(err == CHIP_NO_ERROR, PrepareStatusReport(StatusCode::kBadMessageContents)); VerifyOrReturn(blockMsg.BlockCounter == mLastQueryNum, PrepareStatusReport(StatusCode::kBadBlockCounter)); VerifyOrReturn((blockMsg.DataLength > 0) && (blockMsg.DataLength <= mTransferMaxBlockSize), PrepareStatusReport(StatusCode::kBadMessageContents)); if (IsTransferLengthDefinite()) { VerifyOrReturn(mNumBytesProcessed + blockMsg.DataLength <= mTransferLength, PrepareStatusReport(StatusCode::kLengthMismatch)); } mBlockEventData.Data = blockMsg.Data; mBlockEventData.Length = blockMsg.DataLength; mBlockEventData.IsEof = false; mBlockEventData.BlockCounter = blockMsg.BlockCounter; mPendingMsgHandle = std::move(msgData); mPendingOutput = OutputEventType::kBlockReceived; mNumBytesProcessed += blockMsg.DataLength; mLastBlockNum = blockMsg.BlockCounter; mAwaitingResponse = false; #if CHIP_AUTOMATION_LOGGING blockMsg.LogMessage(MessageType::Block); #endif // CHIP_AUTOMATION_LOGGING } void TransferSession::HandleBlockEOF(System::PacketBufferHandle msgData) { VerifyOrReturn(mRole == TransferRole::kReceiver, PrepareStatusReport(StatusCode::kUnexpectedMessage)); VerifyOrReturn(mState == TransferState::kTransferInProgress, PrepareStatusReport(StatusCode::kUnexpectedMessage)); VerifyOrReturn(mAwaitingResponse, PrepareStatusReport(StatusCode::kUnexpectedMessage)); BlockEOF blockEOFMsg; const CHIP_ERROR err = blockEOFMsg.Parse(msgData.Retain()); VerifyOrReturn(err == CHIP_NO_ERROR, PrepareStatusReport(StatusCode::kBadMessageContents)); VerifyOrReturn(blockEOFMsg.BlockCounter == mLastQueryNum, PrepareStatusReport(StatusCode::kBadBlockCounter)); VerifyOrReturn(blockEOFMsg.DataLength <= mTransferMaxBlockSize, PrepareStatusReport(StatusCode::kBadMessageContents)); mBlockEventData.Data = blockEOFMsg.Data; mBlockEventData.Length = blockEOFMsg.DataLength; mBlockEventData.IsEof = true; mBlockEventData.BlockCounter = blockEOFMsg.BlockCounter; mPendingMsgHandle = std::move(msgData); mPendingOutput = OutputEventType::kBlockReceived; mNumBytesProcessed += blockEOFMsg.DataLength; mLastBlockNum = blockEOFMsg.BlockCounter; mAwaitingResponse = false; mState = TransferState::kReceivedEOF; #if CHIP_AUTOMATION_LOGGING blockEOFMsg.LogMessage(MessageType::BlockEOF); #endif // CHIP_AUTOMATION_LOGGING } void TransferSession::HandleBlockAck(System::PacketBufferHandle msgData) { VerifyOrReturn(mRole == TransferRole::kSender, PrepareStatusReport(StatusCode::kUnexpectedMessage)); VerifyOrReturn(mState == TransferState::kTransferInProgress, PrepareStatusReport(StatusCode::kUnexpectedMessage)); VerifyOrReturn(mAwaitingResponse, PrepareStatusReport(StatusCode::kUnexpectedMessage)); BlockAck ackMsg; const CHIP_ERROR err = ackMsg.Parse(std::move(msgData)); VerifyOrReturn(err == CHIP_NO_ERROR, PrepareStatusReport(StatusCode::kBadMessageContents)); VerifyOrReturn(ackMsg.BlockCounter == mLastBlockNum, PrepareStatusReport(StatusCode::kBadBlockCounter)); mPendingOutput = OutputEventType::kAckReceived; // In Receiver Drive, the Receiver can send a BlockAck to indicate receipt of the message and reset the timeout. // In this case, the Sender should wait to receive a BlockQuery next. mAwaitingResponse = (mControlMode == TransferControlFlags::kReceiverDrive); #if CHIP_AUTOMATION_LOGGING ackMsg.LogMessage(MessageType::BlockAck); #endif // CHIP_AUTOMATION_LOGGING } void TransferSession::HandleBlockAckEOF(System::PacketBufferHandle msgData) { VerifyOrReturn(mRole == TransferRole::kSender, PrepareStatusReport(StatusCode::kUnexpectedMessage)); VerifyOrReturn(mState == TransferState::kAwaitingEOFAck, PrepareStatusReport(StatusCode::kUnexpectedMessage)); VerifyOrReturn(mAwaitingResponse, PrepareStatusReport(StatusCode::kUnexpectedMessage)); BlockAckEOF ackMsg; const CHIP_ERROR err = ackMsg.Parse(std::move(msgData)); VerifyOrReturn(err == CHIP_NO_ERROR, PrepareStatusReport(StatusCode::kBadMessageContents)); VerifyOrReturn(ackMsg.BlockCounter == mLastBlockNum, PrepareStatusReport(StatusCode::kBadBlockCounter)); mPendingOutput = OutputEventType::kAckEOFReceived; mAwaitingResponse = false; mState = TransferState::kTransferDone; #if CHIP_AUTOMATION_LOGGING ackMsg.LogMessage(MessageType::BlockAckEOF); #endif // CHIP_AUTOMATION_LOGGING } void TransferSession::ResolveTransferControlOptions(const BitFlags & proposed) { // Must specify at least one synchronous option // if (!proposed.HasAny(TransferControlFlags::kSenderDrive, TransferControlFlags::kReceiverDrive)) { PrepareStatusReport(StatusCode::kTransferMethodNotSupported); return; } // Ensure there are options supported by both nodes. Async gets priority. // If there is only one common option, choose that one. Otherwise the application must pick. const BitFlags commonOpts(proposed & mSuppportedXferOpts); if (!commonOpts.HasAny()) { PrepareStatusReport(StatusCode::kTransferMethodNotSupported); } else if (commonOpts.HasOnly(TransferControlFlags::kAsync)) { mControlMode = TransferControlFlags::kAsync; } else if (commonOpts.HasOnly(TransferControlFlags::kReceiverDrive)) { mControlMode = TransferControlFlags::kReceiverDrive; } else if (commonOpts.HasOnly(TransferControlFlags::kSenderDrive)) { mControlMode = TransferControlFlags::kSenderDrive; } } CHIP_ERROR TransferSession::VerifyProposedMode(const BitFlags & proposed) { TransferControlFlags mode; // Must specify only one mode in Accept messages if (proposed.HasOnly(TransferControlFlags::kAsync)) { mode = TransferControlFlags::kAsync; } else if (proposed.HasOnly(TransferControlFlags::kReceiverDrive)) { mode = TransferControlFlags::kReceiverDrive; } else if (proposed.HasOnly(TransferControlFlags::kSenderDrive)) { mode = TransferControlFlags::kSenderDrive; } else { PrepareStatusReport(StatusCode::kBadMessageContents); return CHIP_ERROR_INTERNAL; } // Verify the proposed mode is supported by this instance if (mSuppportedXferOpts.Has(mode)) { mControlMode = mode; } else { PrepareStatusReport(StatusCode::kTransferMethodNotSupported); return CHIP_ERROR_INTERNAL; } return CHIP_NO_ERROR; } void TransferSession::PrepareStatusReport(StatusCode code) { mStatusReportData.statusCode = code; Protocols::SecureChannel::StatusReport report(Protocols::SecureChannel::GeneralStatusCode::kFailure, Protocols::BDX::Id, to_underlying(code)); size_t msgSize = report.Size(); Encoding::LittleEndian::PacketBufferWriter bbuf(chip::MessagePacketBuffer::New(msgSize), msgSize); VerifyOrExit(!bbuf.IsNull(), mPendingOutput = OutputEventType::kInternalError); report.WriteToBuffer(bbuf); mPendingMsgHandle = bbuf.Finalize(); if (mPendingMsgHandle.IsNull()) { ChipLogError(BDX, "%s: error preparing message: %" CHIP_ERROR_FORMAT, __FUNCTION__, CHIP_ERROR_NO_MEMORY.Format()); mPendingOutput = OutputEventType::kInternalError; } else { PrepareOutgoingMessageEvent(Protocols::SecureChannel::MsgType::StatusReport, mPendingOutput, mMsgTypeData); } exit: mState = TransferState::kErrorState; mAwaitingResponse = false; // Prevent triggering timeout } bool TransferSession::IsTransferLengthDefinite() const { return (mTransferLength > 0); } const char * TransferSession::OutputEvent::ToString(OutputEventType outputEventType) { switch (outputEventType) { case OutputEventType::kNone: return "None"; case OutputEventType::kMsgToSend: return "MsgToSend"; case OutputEventType::kInitReceived: return "InitReceived"; case OutputEventType::kAcceptReceived: return "AcceptReceived"; case OutputEventType::kBlockReceived: return "BlockReceived"; case OutputEventType::kQueryReceived: return "QueryReceived"; case OutputEventType::kQueryWithSkipReceived: return "QueryWithSkipReceived"; case OutputEventType::kAckReceived: return "AckReceived"; case OutputEventType::kAckEOFReceived: return "AckEOFReceived"; case OutputEventType::kStatusReceived: return "StatusReceived"; case OutputEventType::kInternalError: return "InternalError"; case OutputEventType::kTransferTimeout: return "TransferTimeout"; default: return "Unknown"; } } TransferSession::OutputEvent TransferSession::OutputEvent::TransferInitEvent(TransferInitData data, System::PacketBufferHandle msg) { OutputEvent event(OutputEventType::kInitReceived); event.MsgData = std::move(msg); event.transferInitData = data; return event; } /** * @brief * Convenience method for constructing an OutputEvent with TransferAcceptData that does not contain Metadata */ TransferSession::OutputEvent TransferSession::OutputEvent::TransferAcceptEvent(TransferAcceptData data) { OutputEvent event(OutputEventType::kAcceptReceived); event.transferAcceptData = data; return event; } /** * @brief * Convenience method for constructing an OutputEvent with TransferAcceptData that contains Metadata */ TransferSession::OutputEvent TransferSession::OutputEvent::TransferAcceptEvent(TransferAcceptData data, System::PacketBufferHandle msg) { OutputEvent event = TransferAcceptEvent(data); event.MsgData = std::move(msg); return event; } TransferSession::OutputEvent TransferSession::OutputEvent::BlockDataEvent(BlockData data, System::PacketBufferHandle msg) { OutputEvent event(OutputEventType::kBlockReceived); event.MsgData = std::move(msg); event.blockdata = data; return event; } /** * @brief * Convenience method for constructing an event with kInternalError or kOutputStatusReceived */ TransferSession::OutputEvent TransferSession::OutputEvent::StatusReportEvent(OutputEventType type, StatusReportData data) { OutputEvent event(type); event.statusData = data; return event; } TransferSession::OutputEvent TransferSession::OutputEvent::MsgToSendEvent(MessageTypeData typeData, System::PacketBufferHandle msg) { OutputEvent event(OutputEventType::kMsgToSend); event.MsgData = std::move(msg); event.msgTypeData = typeData; return event; } TransferSession::OutputEvent TransferSession::OutputEvent::QueryWithSkipEvent(TransferSkipData bytesToSkip) { OutputEvent event(OutputEventType::kQueryWithSkipReceived); event.bytesToSkip = bytesToSkip; return event; } } // namespace bdx } // namespace chip