Line data Source code
1 : /** 2 : * @file 3 : * This file defines a TransferSession state machine that contains the main logic governing a Bulk Data Transfer session. It 4 : * provides APIs for starting a transfer or preparing to receive a transfer request, providing input to be processed, and 5 : * accessing output data (including messages to be sent, message data received by the TransferSession, or state information). 6 : */ 7 : 8 : #pragma once 9 : 10 : #include <lib/core/CHIPError.h> 11 : #include <protocols/bdx/BdxMessages.h> 12 : #include <system/SystemClock.h> 13 : #include <system/SystemPacketBuffer.h> 14 : #include <transport/raw/MessageHeader.h> 15 : 16 : #include <type_traits> 17 : 18 : namespace chip { 19 : namespace bdx { 20 : 21 : enum class TransferRole : uint8_t 22 : { 23 : kReceiver = 0, 24 : kSender = 1, 25 : }; 26 : 27 : class DLL_EXPORT TransferSession 28 : { 29 : public: 30 : enum class OutputEventType : uint16_t 31 : { 32 : kNone = 0, 33 : kMsgToSend, 34 : kInitReceived, 35 : kAcceptReceived, 36 : kBlockReceived, 37 : kQueryReceived, 38 : kQueryWithSkipReceived, 39 : kAckReceived, 40 : kAckEOFReceived, 41 : kStatusReceived, 42 : kInternalError, 43 : kTransferTimeout 44 : }; 45 : 46 : struct TransferInitData 47 : { 48 : TransferControlFlags TransferCtlFlags; 49 : 50 : uint16_t MaxBlockSize = 0; 51 : uint64_t StartOffset = 0; 52 : uint64_t Length = 0; 53 : 54 : const uint8_t * FileDesignator = nullptr; 55 : uint16_t FileDesLength = 0; 56 : 57 : // Additional metadata (optional, TLV format) 58 : const uint8_t * Metadata = nullptr; 59 : size_t MetadataLength = 0; 60 : }; 61 : 62 : struct TransferAcceptData 63 : { 64 : TransferControlFlags ControlMode; 65 : 66 : uint16_t MaxBlockSize = 0; 67 : uint64_t StartOffset = 0; ///< Not used for SendAccept message 68 : uint64_t Length = 0; ///< Not used for SendAccept message 69 : 70 : // Additional metadata (optional, TLV format) 71 : const uint8_t * Metadata = nullptr; 72 : size_t MetadataLength = 0; 73 : }; 74 : 75 : struct StatusReportData 76 : { 77 : StatusCode statusCode; 78 : }; 79 : 80 : struct BlockData 81 : { 82 : const uint8_t * Data = nullptr; 83 : size_t Length = 0; 84 : bool IsEof = false; 85 : uint32_t BlockCounter = 0; 86 : }; 87 : 88 : struct MessageTypeData 89 : { 90 : Protocols::Id ProtocolId; // Should only ever be SecureChannel or BDX 91 : uint8_t MessageType; 92 : 93 9 : MessageTypeData() : ProtocolId(Protocols::NotSpecified), MessageType(0) {} 94 : 95 0 : bool HasProtocol(Protocols::Id protocol) const { return ProtocolId == protocol; } 96 0 : bool HasMessageType(uint8_t type) const { return MessageType == type; } 97 : template <typename TMessageType, typename = std::enable_if_t<std::is_enum<TMessageType>::value>> 98 0 : bool HasMessageType(TMessageType type) const 99 : { 100 0 : return HasProtocol(Protocols::MessageTypeTraits<TMessageType>::ProtocolId()) && HasMessageType(to_underlying(type)); 101 : } 102 : }; 103 : 104 : struct TransferSkipData 105 : { 106 : uint64_t BytesToSkip = 0; 107 : }; 108 : 109 : /** 110 : * @brief 111 : * All output data processed by the TransferSession object will be passed to the caller using this struct via PollOutput(). 112 : * 113 : * NOTE: Some sub-structs may contain pointers to data in a PacketBuffer (see Blockdata). In this case, the MsgData field MUST 114 : * be populated with a PacketBufferHandle that encapsulates the respective PacketBuffer, in order to ensure valid memory 115 : * access. 116 : * 117 : * NOTE: MsgData can contain messages that have been received or messages that should be sent by the caller. The underlying 118 : * buffer will always start at the data, never at the payload header. Outgoing messages do not have a header prepended. 119 : */ 120 : struct OutputEvent 121 : { 122 : OutputEventType EventType; 123 : System::PacketBufferHandle MsgData; 124 : union 125 : { 126 : TransferInitData transferInitData; 127 : TransferAcceptData transferAcceptData; 128 : BlockData blockdata; 129 : StatusReportData statusData; 130 : MessageTypeData msgTypeData; 131 : TransferSkipData bytesToSkip; 132 : }; 133 : 134 0 : OutputEvent() : EventType(OutputEventType::kNone) { statusData = { StatusCode::kUnknown }; } 135 368 : OutputEvent(OutputEventType type) : EventType(type) { statusData = { StatusCode::kUnknown }; } 136 : 137 : const char * ToString(OutputEventType outputEventType); 138 : 139 : static OutputEvent TransferInitEvent(TransferInitData data, System::PacketBufferHandle msg); 140 : static OutputEvent TransferAcceptEvent(TransferAcceptData data); 141 : static OutputEvent TransferAcceptEvent(TransferAcceptData data, System::PacketBufferHandle msg); 142 : static OutputEvent BlockDataEvent(BlockData data, System::PacketBufferHandle msg); 143 : static OutputEvent StatusReportEvent(OutputEventType type, StatusReportData data); 144 : static OutputEvent MsgToSendEvent(MessageTypeData typeData, System::PacketBufferHandle msg); 145 : static OutputEvent QueryWithSkipEvent(TransferSkipData bytesToSkip); 146 : }; 147 : 148 : /** 149 : * @brief 150 : * Indicates the presence of pending output and includes any data for the caller to take action on. 151 : * 152 : * This method should be called frequently in order to be notified about any messages received. It should also be called after 153 : * most other methods in order to notify the user of any message that needs to be sent, or errors that occurred internally. 154 : * 155 : * It is possible that consecutive calls to this method may emit different outputs depending on the state of the 156 : * TransferSession object. 157 : * 158 : * Note that if the type outputted is kMsgToSend, the caller is expected to send the message immediately, and the session 159 : * timeout timer will begin at curTime. 160 : * 161 : * See OutputEventType for all possible output event types. 162 : * 163 : * @param event Reference to an OutputEvent struct that will be filled out with any pending output data 164 : * @param curTime Current time 165 : */ 166 : void PollOutput(OutputEvent & event, System::Clock::Timestamp curTime); 167 : 168 : /** 169 : * @brief 170 : * Initializes the TransferSession object and prepares a TransferInit message (emitted via PollOutput()). 171 : * 172 : * A TransferSession object must be initialized with either StartTransfer() or WaitForTransfer(). 173 : * 174 : * @param role Inidcates whether this object will be sending or receiving data 175 : * @param initData Data for initializing this object and for populating a TransferInit message 176 : * The role parameter will determine whether to populate a ReceiveInit or SendInit 177 : * @param timeout The amount of time to wait for a response before considering the transfer failed 178 : * 179 : * @return CHIP_ERROR Result of initialization and preparation of a TransferInit message. May also indicate if the 180 : * TransferSession object is unable to handle this request. 181 : */ 182 : CHIP_ERROR StartTransfer(TransferRole role, const TransferInitData & initData, System::Clock::Timeout timeout); 183 : 184 : /** 185 : * @brief 186 : * Initialize the TransferSession object and prepare to receive a TransferInit message at some point. 187 : * 188 : * A TransferSession object must be initialized with either StartTransfer() or WaitForTransfer(). 189 : * 190 : * @param role Inidcates whether this object will be sending or receiving data 191 : * @param xferControlOpts Indicates all supported control modes. Used to respond to a TransferInit message 192 : * @param maxBlockSize The max Block size that this object supports. 193 : * @param timeout The amount of time to wait for a response before considering the transfer failed 194 : * 195 : * @return CHIP_ERROR Result of initialization. May also indicate if the TransferSession object is unable to handle this 196 : * request. 197 : */ 198 : CHIP_ERROR WaitForTransfer(TransferRole role, BitFlags<TransferControlFlags> xferControlOpts, uint16_t maxBlockSize, 199 : System::Clock::Timeout timeout); 200 : 201 : /** 202 : * @brief 203 : * Indicate that all transfer parameters are acceptable and prepare a SendAccept or ReceiveAccept message (depending on role). 204 : * 205 : * @param acceptData Data used to populate an Accept message (some fields may differ from the original Init message) 206 : * 207 : * @return CHIP_ERROR Result of preparation of an Accept message. May also indicate if the TransferSession object is unable to 208 : * handle this request. 209 : */ 210 : CHIP_ERROR AcceptTransfer(const TransferAcceptData & acceptData); 211 : 212 : /** 213 : * @brief 214 : * Reject a TransferInit message. Use Reset() to prepare this object for another transfer. 215 : * 216 : * @param reason A StatusCode indicating the reason for rejecting the transfer 217 : * 218 : * @return CHIP_ERROR The result of the preparation of a StatusReport message. May also indicate if the TransferSession object 219 : * is unable to handle this request. 220 : */ 221 : CHIP_ERROR RejectTransfer(StatusCode reason); 222 : 223 : /** 224 : * @brief 225 : * Prepare a BlockQuery message. The Block counter will be populated automatically. 226 : * 227 : * @return CHIP_ERROR The result of the preparation of a BlockQuery message. May also indicate if the TransferSession object 228 : * is unable to handle this request. 229 : */ 230 : CHIP_ERROR PrepareBlockQuery(); 231 : 232 : /** 233 : * @brief 234 : * Prepare a BlockQueryWithSkip message. The Block counter will be populated automatically. 235 : * 236 : * @param bytesToSkip Number of bytes to seek skip 237 : * 238 : * @return CHIP_ERROR The result of the preparation of a BlockQueryWithSkip message. May also indicate if the TransferSession 239 : * object is unable to handle this request. 240 : */ 241 : CHIP_ERROR PrepareBlockQueryWithSkip(const uint64_t & bytesToSkip); 242 : 243 : /** 244 : * @brief 245 : * Prepare a Block message. The Block counter will be populated automatically. 246 : * 247 : * @param inData Contains data for filling out the Block message 248 : * 249 : * @return CHIP_ERROR The result of the preparation of a Block message. May also indicate if the TransferSession object 250 : * is unable to handle this request. 251 : */ 252 : CHIP_ERROR PrepareBlock(const BlockData & inData); 253 : 254 : /** 255 : * @brief 256 : * Prepare a BlockAck message. The Block counter will be populated automatically. 257 : * 258 : * @return CHIP_ERROR The result of the preparation of a BlockAck message. May also indicate if the TransferSession object 259 : * is unable to handle this request. 260 : */ 261 : CHIP_ERROR PrepareBlockAck(); 262 : 263 : /** 264 : * @brief 265 : * Prematurely end a transfer with a StatusReport. Must still call Reset() to prepare the TransferSession for another 266 : * transfer. 267 : * 268 : * @param reason The StatusCode reason for ending the transfer. 269 : * 270 : * @return CHIP_ERROR May return an error if there is no transfer in progress. 271 : */ 272 : CHIP_ERROR AbortTransfer(StatusCode reason); 273 : 274 : /** 275 : * @brief 276 : * Reset all TransferSession parameters. The TransferSession object must then be re-initialized with StartTransfer() or 277 : * WaitForTransfer(). 278 : */ 279 : void Reset(); 280 : 281 : /** 282 : * @brief 283 : * Process a message intended for this TransferSession object. 284 : * 285 : * @param payloadHeader A PayloadHeader containing the Protocol type and Message Type 286 : * @param msg A PacketBufferHandle pointing to the message buffer to process. May be BDX or StatusReport protocol. 287 : * Buffer is expected to start at data (not header). 288 : * @param curTime Current time 289 : * 290 : * @return CHIP_ERROR Indicates any problems in decoding the message, or if the message is not of the BDX or StatusReport 291 : * protocols. 292 : */ 293 : CHIP_ERROR HandleMessageReceived(const PayloadHeader & payloadHeader, System::PacketBufferHandle msg, 294 : System::Clock::Timestamp curTime); 295 : 296 : TransferControlFlags GetControlMode() const { return mControlMode; } 297 0 : uint64_t GetStartOffset() const { return mStartOffset; } 298 0 : uint64_t GetTransferLength() const { return mTransferLength; } 299 0 : uint16_t GetTransferBlockSize() const { return mTransferMaxBlockSize; } 300 : uint32_t GetNextBlockNum() const { return mNextBlockNum; } 301 : uint32_t GetNextQueryNum() const { return mNextQueryNum; } 302 : size_t GetNumBytesProcessed() const { return mNumBytesProcessed; } 303 0 : const uint8_t * GetFileDesignator(uint16_t & fileDesignatorLen) const 304 : { 305 0 : fileDesignatorLen = mTransferRequestData.FileDesLength; 306 0 : return mTransferRequestData.FileDesignator; 307 : } 308 : 309 : TransferSession(); 310 : 311 : private: 312 : enum class TransferState : uint8_t 313 : { 314 : kUnitialized, 315 : kAwaitingInitMsg, 316 : kAwaitingAccept, 317 : kNegotiateTransferParams, 318 : kTransferInProgress, 319 : kAwaitingEOFAck, 320 : kReceivedEOF, 321 : kTransferDone, 322 : kErrorState, 323 : }; 324 : 325 : // Incoming message handlers 326 : CHIP_ERROR HandleBdxMessage(const PayloadHeader & header, System::PacketBufferHandle msg); 327 : CHIP_ERROR HandleStatusReportMessage(const PayloadHeader & header, System::PacketBufferHandle msg); 328 : void HandleTransferInit(MessageType msgType, System::PacketBufferHandle msgData); 329 : void HandleReceiveAccept(System::PacketBufferHandle msgData); 330 : void HandleSendAccept(System::PacketBufferHandle msgData); 331 : void HandleBlockQuery(System::PacketBufferHandle msgData); 332 : void HandleBlockQueryWithSkip(System::PacketBufferHandle msgData); 333 : void HandleBlock(System::PacketBufferHandle msgData); 334 : void HandleBlockEOF(System::PacketBufferHandle msgData); 335 : void HandleBlockAck(System::PacketBufferHandle msgData); 336 : void HandleBlockAckEOF(System::PacketBufferHandle msgData); 337 : 338 : /** 339 : * @brief 340 : * Used when handling a TransferInit message. Determines if there are any compatible Transfer control modes between the two 341 : * transfer peers. 342 : */ 343 : void ResolveTransferControlOptions(const BitFlags<TransferControlFlags> & proposed); 344 : 345 : /** 346 : * @brief 347 : * Used when handling an Accept message. Verifies that the chosen control mode is compatible with the orignal supported modes. 348 : */ 349 : CHIP_ERROR VerifyProposedMode(const BitFlags<TransferControlFlags> & proposed); 350 : 351 : void PrepareStatusReport(StatusCode code); 352 : bool IsTransferLengthDefinite() const; 353 : 354 : OutputEventType mPendingOutput = OutputEventType::kNone; 355 : TransferState mState = TransferState::kUnitialized; 356 : TransferRole mRole; 357 : 358 : // Indicate supported options pre- transfer accept 359 : BitFlags<TransferControlFlags> mSuppportedXferOpts; 360 : uint16_t mMaxSupportedBlockSize = 0; 361 : 362 : // Used to govern transfer once it has been accepted 363 : TransferControlFlags mControlMode; 364 : uint8_t mTransferVersion = 0; 365 : uint64_t mStartOffset = 0; ///< 0 represents no offset 366 : uint64_t mTransferLength = 0; ///< 0 represents indefinite length 367 : uint16_t mTransferMaxBlockSize = 0; 368 : 369 : // Used to store event data before it is emitted via PollOutput() 370 : System::PacketBufferHandle mPendingMsgHandle; 371 : StatusReportData mStatusReportData; 372 : TransferInitData mTransferRequestData; 373 : TransferAcceptData mTransferAcceptData; 374 : BlockData mBlockEventData; 375 : MessageTypeData mMsgTypeData; 376 : TransferSkipData mBytesToSkip; 377 : 378 : size_t mNumBytesProcessed = 0; 379 : 380 : uint32_t mLastBlockNum = 0; 381 : uint32_t mNextBlockNum = 0; 382 : uint32_t mLastQueryNum = 0; 383 : uint32_t mNextQueryNum = 0; 384 : 385 : System::Clock::Timeout mTimeout = System::Clock::kZero; 386 : System::Clock::Timestamp mTimeoutStartTime = System::Clock::kZero; 387 : bool mShouldInitTimeoutStart = true; 388 : bool mAwaitingResponse = false; 389 : }; 390 : 391 : } // namespace bdx 392 : } // namespace chip