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 11 : 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 387 : OutputEvent(OutputEventType type) : EventType(type) { statusData = { StatusCode::kUnknown }; }
136 :
137 : const char * ToString(OutputEventType outputEventType);
138 :
139 : static const char * TypeToString(OutputEventType outputEventType);
140 :
141 : static OutputEvent TransferInitEvent(TransferInitData data, System::PacketBufferHandle msg);
142 : static OutputEvent TransferAcceptEvent(TransferAcceptData data);
143 : static OutputEvent TransferAcceptEvent(TransferAcceptData data, System::PacketBufferHandle msg);
144 : static OutputEvent BlockDataEvent(BlockData data, System::PacketBufferHandle msg);
145 : static OutputEvent StatusReportEvent(OutputEventType type, StatusReportData data);
146 : static OutputEvent MsgToSendEvent(MessageTypeData typeData, System::PacketBufferHandle msg);
147 : static OutputEvent QueryWithSkipEvent(TransferSkipData bytesToSkip);
148 : };
149 :
150 : /**
151 : * @brief
152 : * Indicates the presence of pending output and includes any data for the caller to take action on.
153 : *
154 : * This method should be called frequently in order to be notified about any messages received. It should also be called after
155 : * most other methods in order to notify the user of any message that needs to be sent, or errors that occurred internally.
156 : *
157 : * It is possible that consecutive calls to this method may emit different outputs depending on the state of the
158 : * TransferSession object.
159 : *
160 : * Note that if the type outputted is kMsgToSend, the caller is expected to send the message immediately, and the session
161 : * timeout timer will begin at curTime.
162 : *
163 : * See OutputEventType for all possible output event types.
164 : *
165 : * @param event Reference to an OutputEvent struct that will be filled out with any pending output data
166 : * @param curTime Current time
167 : */
168 : void PollOutput(OutputEvent & event, System::Clock::Timestamp curTime);
169 :
170 : /**
171 : * @brief
172 : * Gets the pending output event from the transfer session in the event param passed in by the caller.
173 : * The output event may contain some data for the caller to act upon. If there is no pending output event,
174 : * the caller will get an event of type OutputEventType::kNone.
175 : *
176 : * It is possible that consecutive calls to this method may emit different outputs depending on the state of the
177 : * TransferSession object. The caller is generally expected to keep calling this method until it gets an event of type
178 : * OutputEventType::kNone.
179 : *
180 : * If the output event type is kMsgToSend, the caller is expected to send the message immediately on the
181 : * relevant exchange. In this case the BDX session timeout timer will start when GetNextAction is called.
182 : *
183 : * See OutputEventType for all possible output event types.
184 : *
185 : * @param event Reference to an OutputEvent struct that will be filled out with any pending output event data
186 : */
187 : void GetNextAction(OutputEvent & event);
188 :
189 : /**
190 : * @brief
191 : * Initializes the TransferSession object and prepares a TransferInit message (emitted via PollOutput()).
192 : *
193 : * A TransferSession object must be initialized with either StartTransfer() or WaitForTransfer().
194 : *
195 : * @param role Inidcates whether this object will be sending or receiving data
196 : * @param initData Data for initializing this object and for populating a TransferInit message
197 : * The role parameter will determine whether to populate a ReceiveInit or SendInit
198 : * @param timeout The amount of time to wait for a response before considering the transfer failed
199 : *
200 : * @return CHIP_ERROR Result of initialization and preparation of a TransferInit message. May also indicate if the
201 : * TransferSession object is unable to handle this request.
202 : */
203 : CHIP_ERROR StartTransfer(TransferRole role, const TransferInitData & initData, System::Clock::Timeout timeout);
204 :
205 : /**
206 : * @brief
207 : * Initialize the TransferSession object and prepare to receive a TransferInit message at some point.
208 : *
209 : * A TransferSession object must be initialized with either StartTransfer() or WaitForTransfer().
210 : *
211 : * @param role Inidcates whether this object will be sending or receiving data
212 : * @param xferControlOpts Indicates all supported control modes. Used to respond to a TransferInit message
213 : * @param maxBlockSize The max Block size that this object supports.
214 : * @param timeout The amount of time to wait for a response before considering the transfer failed
215 : *
216 : * @return CHIP_ERROR Result of initialization. May also indicate if the TransferSession object is unable to handle this
217 : * request.
218 : */
219 : CHIP_ERROR WaitForTransfer(TransferRole role, BitFlags<TransferControlFlags> xferControlOpts, uint16_t maxBlockSize,
220 : System::Clock::Timeout timeout);
221 :
222 : /**
223 : * @brief
224 : * Indicate that all transfer parameters are acceptable and prepare a SendAccept or ReceiveAccept message (depending on role).
225 : *
226 : * @param acceptData Data used to populate an Accept message (some fields may differ from the original Init message)
227 : *
228 : * @return CHIP_ERROR Result of preparation of an Accept message. May also indicate if the TransferSession object is unable to
229 : * handle this request.
230 : */
231 : CHIP_ERROR AcceptTransfer(const TransferAcceptData & acceptData);
232 :
233 : /**
234 : * @brief
235 : * Reject a TransferInit message. Use Reset() to prepare this object for another transfer.
236 : *
237 : * @param reason A StatusCode indicating the reason for rejecting the transfer
238 : *
239 : * @return CHIP_ERROR The result of the preparation of a StatusReport message. May also indicate if the TransferSession object
240 : * is unable to handle this request.
241 : */
242 : CHIP_ERROR RejectTransfer(StatusCode reason);
243 :
244 : /**
245 : * @brief
246 : * Prepare a BlockQuery message. The Block counter will be populated automatically.
247 : *
248 : * @return CHIP_ERROR The result of the preparation of a BlockQuery message. May also indicate if the TransferSession object
249 : * is unable to handle this request.
250 : */
251 : CHIP_ERROR PrepareBlockQuery();
252 :
253 : /**
254 : * @brief
255 : * Prepare a BlockQueryWithSkip message. The Block counter will be populated automatically.
256 : *
257 : * @param bytesToSkip Number of bytes to seek skip
258 : *
259 : * @return CHIP_ERROR The result of the preparation of a BlockQueryWithSkip message. May also indicate if the TransferSession
260 : * object is unable to handle this request.
261 : */
262 : CHIP_ERROR PrepareBlockQueryWithSkip(const uint64_t & bytesToSkip);
263 :
264 : /**
265 : * @brief
266 : * Prepare a Block message. The Block counter will be populated automatically.
267 : *
268 : * @param inData Contains data for filling out the Block message
269 : *
270 : * @return CHIP_ERROR The result of the preparation of a Block message. May also indicate if the TransferSession object
271 : * is unable to handle this request.
272 : */
273 : CHIP_ERROR PrepareBlock(const BlockData & inData);
274 :
275 : /**
276 : * @brief
277 : * Prepare a BlockAck message. The Block counter will be populated automatically.
278 : *
279 : * @return CHIP_ERROR The result of the preparation of a BlockAck message. May also indicate if the TransferSession object
280 : * is unable to handle this request.
281 : */
282 : CHIP_ERROR PrepareBlockAck();
283 :
284 : /**
285 : * @brief
286 : * Prematurely end a transfer with a StatusReport. Must still call Reset() to prepare the TransferSession for another
287 : * transfer.
288 : *
289 : * @param reason The StatusCode reason for ending the transfer.
290 : *
291 : * @return CHIP_ERROR May return an error if there is no transfer in progress.
292 : */
293 : CHIP_ERROR AbortTransfer(StatusCode reason);
294 :
295 : /**
296 : * @brief
297 : * Reset all TransferSession parameters. The TransferSession object must then be re-initialized with StartTransfer() or
298 : * WaitForTransfer().
299 : */
300 : void Reset();
301 :
302 : /**
303 : * @brief
304 : * Process a message intended for this TransferSession object.
305 : *
306 : * @param payloadHeader A PayloadHeader containing the Protocol type and Message Type
307 : * @param msg A PacketBufferHandle pointing to the message buffer to process. May be BDX or StatusReport protocol.
308 : * Buffer is expected to start at data (not header).
309 : * @param curTime Current time
310 : *
311 : * @return CHIP_ERROR Indicates any problems in decoding the message, or if the message is not of the BDX or StatusReport
312 : * protocols.
313 : */
314 : CHIP_ERROR HandleMessageReceived(const PayloadHeader & payloadHeader, System::PacketBufferHandle msg,
315 : System::Clock::Timestamp curTime);
316 :
317 : TransferControlFlags GetControlMode() const { return mControlMode; }
318 0 : uint64_t GetStartOffset() const { return mStartOffset; }
319 0 : uint64_t GetTransferLength() const { return mTransferLength; }
320 0 : uint16_t GetTransferBlockSize() const { return mTransferMaxBlockSize; }
321 : uint32_t GetNextBlockNum() const { return mNextBlockNum; }
322 : uint32_t GetNextQueryNum() const { return mNextQueryNum; }
323 : size_t GetNumBytesProcessed() const { return mNumBytesProcessed; }
324 0 : const uint8_t * GetFileDesignator(uint16_t & fileDesignatorLen) const
325 : {
326 0 : fileDesignatorLen = mTransferRequestData.FileDesLength;
327 0 : return mTransferRequestData.FileDesignator;
328 : }
329 :
330 : TransferSession();
331 :
332 : private:
333 : enum class TransferState : uint8_t
334 : {
335 : kUnitialized,
336 : kAwaitingInitMsg,
337 : kAwaitingAccept,
338 : kNegotiateTransferParams,
339 : kTransferInProgress,
340 : kAwaitingEOFAck,
341 : kReceivedEOF,
342 : kTransferDone,
343 : kErrorState,
344 : };
345 :
346 : // Incoming message handlers
347 : CHIP_ERROR HandleBdxMessage(const PayloadHeader & header, System::PacketBufferHandle msg);
348 : CHIP_ERROR HandleStatusReportMessage(const PayloadHeader & header, System::PacketBufferHandle msg);
349 : void HandleTransferInit(MessageType msgType, System::PacketBufferHandle msgData);
350 : void HandleReceiveAccept(System::PacketBufferHandle msgData);
351 : void HandleSendAccept(System::PacketBufferHandle msgData);
352 : void HandleBlockQuery(System::PacketBufferHandle msgData);
353 : void HandleBlockQueryWithSkip(System::PacketBufferHandle msgData);
354 : void HandleBlock(System::PacketBufferHandle msgData);
355 : void HandleBlockEOF(System::PacketBufferHandle msgData);
356 : void HandleBlockAck(System::PacketBufferHandle msgData);
357 : void HandleBlockAckEOF(System::PacketBufferHandle msgData);
358 :
359 : /**
360 : * @brief
361 : * Used when handling a TransferInit message. Determines if there are any compatible Transfer control modes between the two
362 : * transfer peers.
363 : */
364 : void ResolveTransferControlOptions(const BitFlags<TransferControlFlags> & proposed);
365 :
366 : /**
367 : * @brief
368 : * Used when handling an Accept message. Verifies that the chosen control mode is compatible with the orignal supported modes.
369 : */
370 : CHIP_ERROR VerifyProposedMode(const BitFlags<TransferControlFlags> & proposed);
371 :
372 : void PrepareStatusReport(StatusCode code);
373 : bool IsTransferLengthDefinite() const;
374 :
375 : OutputEventType mPendingOutput = OutputEventType::kNone;
376 : TransferState mState = TransferState::kUnitialized;
377 : TransferRole mRole;
378 :
379 : // Indicate supported options pre- transfer accept
380 : BitFlags<TransferControlFlags> mSuppportedXferOpts;
381 : uint16_t mMaxSupportedBlockSize = 0;
382 :
383 : // Used to govern transfer once it has been accepted
384 : TransferControlFlags mControlMode;
385 : uint8_t mTransferVersion = 0;
386 : uint64_t mStartOffset = 0; ///< 0 represents no offset
387 : uint64_t mTransferLength = 0; ///< 0 represents indefinite length
388 : uint16_t mTransferMaxBlockSize = 0;
389 :
390 : // Used to store event data before it is emitted via PollOutput()
391 : System::PacketBufferHandle mPendingMsgHandle;
392 : StatusReportData mStatusReportData;
393 : TransferInitData mTransferRequestData;
394 : TransferAcceptData mTransferAcceptData;
395 : BlockData mBlockEventData;
396 : MessageTypeData mMsgTypeData;
397 : TransferSkipData mBytesToSkip;
398 :
399 : size_t mNumBytesProcessed = 0;
400 :
401 : uint32_t mLastBlockNum = 0;
402 : uint32_t mNextBlockNum = 0;
403 : uint32_t mLastQueryNum = 0;
404 : uint32_t mNextQueryNum = 0;
405 :
406 : System::Clock::Timeout mTimeout = System::Clock::kZero;
407 : System::Clock::Timestamp mTimeoutStartTime = System::Clock::kZero;
408 : bool mShouldInitTimeoutStart = true;
409 : bool mAwaitingResponse = false;
410 : };
411 :
412 : } // namespace bdx
413 : } // namespace chip
|