b64c7b899b7593d9072ffa785be47beccc3c297f
[platform/upstream/connectedhomeip.git] / src / protocols / bdx / BdxMessages.cpp
1 /*
2  *
3  *    Copyright (c) 2020-2021 Project CHIP Authors
4  *    All rights reserved.
5  *
6  *    Licensed under the Apache License, Version 2.0 (the "License");
7  *    you may not use this file except in compliance with the License.
8  *    You may obtain a copy of the License at
9  *
10  *        http://www.apache.org/licenses/LICENSE-2.0
11  *
12  *    Unless required by applicable law or agreed to in writing, software
13  *    distributed under the License is distributed on an "AS IS" BASIS,
14  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  *    See the License for the specific language governing permissions and
16  *    limitations under the License.
17  */
18
19 /**
20  *    @file
21  *      Implements utility methods for working with some complex BDX messages.
22  */
23
24 #include <protocols/bdx/BdxMessages.h>
25
26 #include <support/BufferReader.h>
27 #include <support/BufferWriter.h>
28 #include <support/CodeUtils.h>
29
30 #include <limits>
31 #include <utility>
32
33 namespace {
34 constexpr uint8_t kVersionMask = 0x0F;
35 } // namespace
36
37 using namespace chip;
38 using namespace chip::bdx;
39 using namespace chip::Encoding::LittleEndian;
40
41 // WARNING: this function should never return early, since MessageSize() relies on it to calculate
42 // the size of the message (even if the message is incomplete or filled out incorrectly).
43 BufferWriter & TransferInit::WriteToBuffer(BufferWriter & aBuffer) const
44 {
45     uint8_t proposedTransferCtl = 0;
46     bool widerange = (StartOffset > std::numeric_limits<uint32_t>::max()) || (MaxLength > std::numeric_limits<uint32_t>::max());
47
48     proposedTransferCtl |= Version & kVersionMask;
49     proposedTransferCtl = proposedTransferCtl | TransferCtlOptions.Raw();
50
51     BitFlags<uint8_t, RangeControlFlags> rangeCtlFlags;
52     rangeCtlFlags.Set(kRange_DefLen, MaxLength > 0);
53     rangeCtlFlags.Set(kRange_StartOffset, StartOffset > 0);
54     rangeCtlFlags.Set(kRange_Widerange, widerange);
55
56     aBuffer.Put(proposedTransferCtl);
57     aBuffer.Put(rangeCtlFlags.Raw());
58     aBuffer.Put16(MaxBlockSize);
59
60     if (StartOffset > 0)
61     {
62         if (widerange)
63         {
64             aBuffer.Put64(StartOffset);
65         }
66         else
67         {
68             aBuffer.Put32(static_cast<uint32_t>(StartOffset));
69         }
70     }
71
72     if (MaxLength > 0)
73     {
74         if (widerange)
75         {
76             aBuffer.Put64(MaxLength);
77         }
78         else
79         {
80             aBuffer.Put32(static_cast<uint32_t>(MaxLength));
81         }
82     }
83
84     aBuffer.Put16(FileDesLength);
85     if (FileDesignator != nullptr)
86     {
87         aBuffer.Put(FileDesignator, static_cast<size_t>(FileDesLength));
88     }
89
90     if (Metadata != nullptr)
91     {
92         aBuffer.Put(Metadata, static_cast<size_t>(MetadataLength));
93     }
94     return aBuffer;
95 }
96
97 CHIP_ERROR TransferInit::Parse(System::PacketBufferHandle aBuffer)
98 {
99     CHIP_ERROR err = CHIP_NO_ERROR;
100     uint8_t proposedTransferCtl;
101     uint8_t rangeCtl;
102     uint32_t tmpUint32Value = 0; // Used for reading non-wide length and offset fields
103     uint8_t * bufStart      = aBuffer->Start();
104     Reader bufReader(bufStart, aBuffer->DataLength());
105     BitFlags<uint8_t, RangeControlFlags> rangeCtlFlags;
106
107     SuccessOrExit(bufReader.Read8(&proposedTransferCtl).Read8(&rangeCtl).Read16(&MaxBlockSize).StatusCode());
108
109     Version = proposedTransferCtl & kVersionMask;
110     TransferCtlOptions.SetRaw(static_cast<uint8_t>(proposedTransferCtl & ~kVersionMask));
111     rangeCtlFlags.SetRaw(rangeCtl);
112
113     StartOffset = 0;
114     if (rangeCtlFlags.Has(kRange_StartOffset))
115     {
116         if (rangeCtlFlags.Has(kRange_Widerange))
117         {
118             SuccessOrExit(bufReader.Read64(&StartOffset).StatusCode());
119         }
120         else
121         {
122             SuccessOrExit(bufReader.Read32(&tmpUint32Value).StatusCode());
123             StartOffset = tmpUint32Value;
124         }
125     }
126
127     MaxLength = 0;
128     if (rangeCtlFlags.Has(kRange_DefLen))
129     {
130         if (rangeCtlFlags.Has(kRange_Widerange))
131         {
132             SuccessOrExit(bufReader.Read64(&MaxLength).StatusCode());
133         }
134         else
135         {
136             SuccessOrExit(bufReader.Read32(&tmpUint32Value).StatusCode());
137             MaxLength = tmpUint32Value;
138         }
139     }
140
141     SuccessOrExit(bufReader.Read16(&FileDesLength).StatusCode());
142
143     VerifyOrExit(bufReader.HasAtLeast(FileDesLength), err = CHIP_ERROR_MESSAGE_INCOMPLETE);
144     FileDesignator = &bufStart[bufReader.OctetsRead()];
145
146     // Rest of message is metadata (could be empty)
147     Metadata       = nullptr;
148     MetadataLength = 0;
149     if (bufReader.Remaining() > FileDesLength)
150     {
151         uint16_t metadataStartIndex = static_cast<uint16_t>(bufReader.OctetsRead() + FileDesLength);
152         Metadata                    = &bufStart[metadataStartIndex];
153         MetadataLength              = static_cast<uint16_t>(aBuffer->DataLength() - metadataStartIndex);
154     }
155
156     // Retain ownership of the packet buffer so that the FileDesignator and Metadata pointers remain valid.
157     Buffer = std::move(aBuffer);
158
159 exit:
160     if (bufReader.StatusCode() != CHIP_NO_ERROR)
161     {
162         err = bufReader.StatusCode();
163     }
164     return err;
165 }
166
167 size_t TransferInit::MessageSize() const
168 {
169     BufferWriter emptyBuf(nullptr, 0);
170     return WriteToBuffer(emptyBuf).Needed();
171 }
172
173 bool TransferInit::operator==(const TransferInit & another) const
174 {
175     if ((MetadataLength != another.MetadataLength) || (FileDesLength != another.FileDesLength))
176     {
177         return false;
178     }
179
180     bool fileDesMatches = true;
181     if (FileDesLength > 0)
182     {
183         fileDesMatches = (memcmp(FileDesignator, another.FileDesignator, FileDesLength) == 0);
184     }
185
186     bool metadataMatches = true;
187     if (MetadataLength > 0)
188     {
189         metadataMatches = (memcmp(Metadata, another.Metadata, MetadataLength) == 0);
190     }
191
192     return ((Version == another.Version) && (TransferCtlOptions.Raw() == another.TransferCtlOptions.Raw()) &&
193             (StartOffset == another.StartOffset) && (MaxLength == another.MaxLength) && (MaxBlockSize == another.MaxBlockSize) &&
194             fileDesMatches && metadataMatches);
195 }
196
197 // WARNING: this function should never return early, since MessageSize() relies on it to calculate
198 // the size of the message (even if the message is incomplete or filled out incorrectly).
199 Encoding::LittleEndian::BufferWriter & SendAccept::WriteToBuffer(Encoding::LittleEndian::BufferWriter & aBuffer) const
200 {
201     uint8_t transferCtl = 0;
202
203     transferCtl |= Version & kVersionMask;
204     transferCtl = transferCtl | TransferCtlFlags.Raw();
205
206     aBuffer.Put(transferCtl);
207     aBuffer.Put16(MaxBlockSize);
208
209     if (Metadata != nullptr)
210     {
211         aBuffer.Put(Metadata, static_cast<size_t>(MetadataLength));
212     }
213     return aBuffer;
214 }
215
216 CHIP_ERROR SendAccept::Parse(System::PacketBufferHandle aBuffer)
217 {
218     CHIP_ERROR err      = CHIP_NO_ERROR;
219     uint8_t transferCtl = 0;
220     uint8_t * bufStart  = aBuffer->Start();
221     Reader bufReader(bufStart, aBuffer->DataLength());
222
223     SuccessOrExit(bufReader.Read8(&transferCtl).Read16(&MaxBlockSize).StatusCode());
224
225     Version = transferCtl & kVersionMask;
226
227     // Only one of these values should be set. It is up to the caller to verify this.
228     TransferCtlFlags.SetRaw(static_cast<uint8_t>(transferCtl & ~kVersionMask));
229
230     // Rest of message is metadata (could be empty)
231     Metadata       = nullptr;
232     MetadataLength = 0;
233     if (bufReader.Remaining() > 0)
234     {
235         Metadata       = &bufStart[bufReader.OctetsRead()];
236         MetadataLength = bufReader.Remaining();
237     }
238
239     // Retain ownership of the packet buffer so that the Metadata pointer remains valid.
240     Buffer = std::move(aBuffer);
241
242 exit:
243     if (bufReader.StatusCode() != CHIP_NO_ERROR)
244     {
245         err = bufReader.StatusCode();
246     }
247     return err;
248 }
249
250 size_t SendAccept::MessageSize() const
251 {
252     BufferWriter emptyBuf(nullptr, 0);
253     return WriteToBuffer(emptyBuf).Needed();
254 }
255
256 bool SendAccept::operator==(const SendAccept & another) const
257 {
258     if (MetadataLength != another.MetadataLength)
259     {
260         return false;
261     }
262
263     bool metadataMatches = true;
264     if (MetadataLength > 0)
265     {
266         metadataMatches = (memcmp(Metadata, another.Metadata, MetadataLength) == 0);
267     }
268
269     return ((Version == another.Version) && (TransferCtlFlags.Raw() == another.TransferCtlFlags.Raw()) &&
270             (MaxBlockSize == another.MaxBlockSize) && metadataMatches);
271 }
272
273 // WARNING: this function should never return early, since MessageSize() relies on it to calculate
274 // the size of the message (even if the message is incomplete or filled out incorrectly).
275 Encoding::LittleEndian::BufferWriter & ReceiveAccept::WriteToBuffer(Encoding::LittleEndian::BufferWriter & aBuffer) const
276 {
277     uint8_t transferCtl = 0;
278     bool widerange      = (StartOffset > std::numeric_limits<uint32_t>::max()) || (Length > std::numeric_limits<uint32_t>::max());
279
280     transferCtl |= Version & kVersionMask;
281     transferCtl = transferCtl | TransferCtlFlags.Raw();
282
283     BitFlags<uint8_t, RangeControlFlags> rangeCtlFlags;
284     rangeCtlFlags.Set(kRange_DefLen, Length > 0);
285     rangeCtlFlags.Set(kRange_StartOffset, StartOffset > 0);
286     rangeCtlFlags.Set(kRange_Widerange, widerange);
287
288     aBuffer.Put(transferCtl);
289     aBuffer.Put(rangeCtlFlags.Raw());
290     aBuffer.Put16(MaxBlockSize);
291
292     if (StartOffset > 0)
293     {
294         if (widerange)
295         {
296             aBuffer.Put64(StartOffset);
297         }
298         else
299         {
300             aBuffer.Put32(static_cast<uint32_t>(StartOffset));
301         }
302     }
303
304     if (Length > 0)
305     {
306         if (widerange)
307         {
308             aBuffer.Put64(Length);
309         }
310         else
311         {
312             aBuffer.Put32(static_cast<uint32_t>(Length));
313         }
314     }
315
316     if (Metadata != nullptr)
317     {
318         aBuffer.Put(Metadata, static_cast<size_t>(MetadataLength));
319     }
320     return aBuffer;
321 }
322
323 CHIP_ERROR ReceiveAccept::Parse(System::PacketBufferHandle aBuffer)
324 {
325     CHIP_ERROR err          = CHIP_NO_ERROR;
326     uint8_t transferCtl     = 0;
327     uint8_t rangeCtl        = 0;
328     uint32_t tmpUint32Value = 0; // Used for reading non-wide length and offset fields
329     uint8_t * bufStart      = aBuffer->Start();
330     Reader bufReader(bufStart, aBuffer->DataLength());
331     BitFlags<uint8_t, RangeControlFlags> rangeCtlFlags;
332
333     SuccessOrExit(bufReader.Read8(&transferCtl).Read8(&rangeCtl).Read16(&MaxBlockSize).StatusCode());
334
335     Version = transferCtl & kVersionMask;
336
337     // Only one of these values should be set. It is up to the caller to verify this.
338     TransferCtlFlags.SetRaw(static_cast<uint8_t>(transferCtl & ~kVersionMask));
339
340     rangeCtlFlags.SetRaw(rangeCtl);
341
342     StartOffset = 0;
343     if (rangeCtlFlags.Has(kRange_StartOffset))
344     {
345         if (rangeCtlFlags.Has(kRange_Widerange))
346         {
347             SuccessOrExit(bufReader.Read64(&StartOffset).StatusCode());
348         }
349         else
350         {
351             SuccessOrExit(bufReader.Read32(&tmpUint32Value).StatusCode());
352             StartOffset = tmpUint32Value;
353         }
354     }
355
356     Length = 0;
357     if (rangeCtlFlags.Has(kRange_DefLen))
358     {
359         if (rangeCtlFlags.Has(kRange_Widerange))
360         {
361             SuccessOrExit(bufReader.Read64(&Length).StatusCode());
362         }
363         else
364         {
365             SuccessOrExit(bufReader.Read32(&tmpUint32Value).StatusCode());
366             Length = tmpUint32Value;
367         }
368     }
369
370     // Rest of message is metadata (could be empty)
371     Metadata       = nullptr;
372     MetadataLength = 0;
373     if (bufReader.Remaining() > 0)
374     {
375         Metadata       = &bufStart[bufReader.OctetsRead()];
376         MetadataLength = bufReader.Remaining();
377     }
378
379     // Retain ownership of the packet buffer so that the Metadata pointer remains valid.
380     Buffer = std::move(aBuffer);
381
382 exit:
383     if (bufReader.StatusCode() != CHIP_NO_ERROR)
384     {
385         err = bufReader.StatusCode();
386     }
387     return err;
388 }
389
390 size_t ReceiveAccept::MessageSize() const
391 {
392     BufferWriter emptyBuf(nullptr, 0);
393     return WriteToBuffer(emptyBuf).Needed();
394 }
395
396 bool ReceiveAccept::operator==(const ReceiveAccept & another) const
397 {
398     if (MetadataLength != another.MetadataLength)
399     {
400         return false;
401     }
402
403     bool metadataMatches = true;
404     if (MetadataLength > 0)
405     {
406         metadataMatches = (memcmp(Metadata, another.Metadata, MetadataLength) == 0);
407     }
408
409     return ((Version == another.Version) && (TransferCtlFlags.Raw() == another.TransferCtlFlags.Raw()) &&
410             (StartOffset == another.StartOffset) && (MaxBlockSize == another.MaxBlockSize) && (Length == another.Length) &&
411             metadataMatches);
412 }
413
414 // WARNING: this function should never return early, since MessageSize() relies on it to calculate
415 // the size of the message (even if the message is incomplete or filled out incorrectly).
416 Encoding::LittleEndian::BufferWriter & CounterMessage::WriteToBuffer(Encoding::LittleEndian::BufferWriter & aBuffer) const
417 {
418     return aBuffer.Put32(BlockCounter);
419 }
420
421 CHIP_ERROR CounterMessage::Parse(System::PacketBufferHandle aBuffer)
422 {
423     uint8_t * bufStart = aBuffer->Start();
424     Reader bufReader(bufStart, aBuffer->DataLength());
425     return bufReader.Read32(&BlockCounter).StatusCode();
426 }
427
428 size_t CounterMessage::MessageSize() const
429 {
430     BufferWriter emptyBuf(nullptr, 0);
431     return WriteToBuffer(emptyBuf).Needed();
432 }
433
434 bool CounterMessage::operator==(const CounterMessage & another) const
435 {
436     return (BlockCounter == another.BlockCounter);
437 }
438
439 // WARNING: this function should never return early, since MessageSize() relies on it to calculate
440 // the size of the message (even if the message is incomplete or filled out incorrectly).
441 Encoding::LittleEndian::BufferWriter & DataBlock::WriteToBuffer(Encoding::LittleEndian::BufferWriter & aBuffer) const
442 {
443     aBuffer.Put32(BlockCounter);
444     if (Data != nullptr)
445     {
446         aBuffer.Put(Data, DataLength);
447     }
448     return aBuffer;
449 }
450
451 CHIP_ERROR DataBlock::Parse(System::PacketBufferHandle aBuffer)
452 {
453     CHIP_ERROR err     = CHIP_NO_ERROR;
454     uint8_t * bufStart = aBuffer->Start();
455     Reader bufReader(bufStart, aBuffer->DataLength());
456
457     SuccessOrExit(bufReader.Read32(&BlockCounter).StatusCode());
458
459     // Rest of message is data
460     Data       = nullptr;
461     DataLength = 0;
462     if (bufReader.Remaining() > 0)
463     {
464         Data       = &bufStart[bufReader.OctetsRead()];
465         DataLength = bufReader.Remaining();
466     }
467
468     // Retain ownership of the packet buffer so that the Data pointer remains valid.
469     Buffer = std::move(aBuffer);
470
471 exit:
472     if (bufReader.StatusCode() != CHIP_NO_ERROR)
473     {
474         err = bufReader.StatusCode();
475     }
476     return err;
477 }
478
479 size_t DataBlock::MessageSize() const
480 {
481     BufferWriter emptyBuf(nullptr, 0);
482     return WriteToBuffer(emptyBuf).Needed();
483 }
484
485 bool DataBlock::operator==(const DataBlock & another) const
486 {
487     if (DataLength != another.DataLength)
488     {
489         return false;
490     }
491
492     bool dataMatches = true;
493     if (DataLength > 0)
494     {
495         dataMatches = memcmp(Data, another.Data, DataLength) == 0;
496     }
497
498     return ((BlockCounter == another.BlockCounter) && dataMatches);
499 }