1 /*
2  * SRT - Secure, Reliable, Transport
3  * Copyright (c) 2018 Haivision Systems Inc.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  */
10 
11 /*****************************************************************************
12 Copyright (c) 2001 - 2011, The Board of Trustees of the University of Illinois.
13 All rights reserved.
14 
15 Redistribution and use in source and binary forms, with or without
16 modification, are permitted provided that the following conditions are
17 met:
18 
19 * Redistributions of source code must retain the above
20   copyright notice, this list of conditions and the
21   following disclaimer.
22 
23 * Redistributions in binary form must reproduce the
24   above copyright notice, this list of conditions
25   and the following disclaimer in the documentation
26   and/or other materials provided with the distribution.
27 
28 * Neither the name of the University of Illinois
29   nor the names of its contributors may be used to
30   endorse or promote products derived from this
31   software without specific prior written permission.
32 
33 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
34 IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
35 THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
36 PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
37 CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
38 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
39 PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
40 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
41 LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
42 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
43 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
44 *****************************************************************************/
45 
46 /*****************************************************************************
47 written by
48    Yunhong Gu, last updated 02/12/2011
49 modified by
50    Haivision Systems Inc.
51 *****************************************************************************/
52 
53 
54 //////////////////////////////////////////////////////////////////////////////
55 //    0                   1                   2                   3
56 //    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
57 //   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
58 //   |                        Packet Header                          |
59 //   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
60 //   |                                                               |
61 //   ~              Data / Control Information Field                 ~
62 //   |                                                               |
63 //   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
64 //
65 //    0                   1                   2                   3
66 //    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
67 //   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
68 //   |0|                        Sequence Number                      |
69 //   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
70 //   |ff |o|kf |r|               Message Number                      |
71 //   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
72 //   |                          Time Stamp                           |
73 //   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
74 //   |                     Destination Socket ID                     |
75 //   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
76 //
77 //   bit 0:
78 //      0: Data Packet
79 //      1: Control Packet
80 //   bit ff:
81 //      11: solo message packet
82 //      10: first packet of a message
83 //      01: last packet of a message
84 //   bit o:
85 //      0: in order delivery not required
86 //      1: in order delivery required
87 //   bit kf: HaiCrypt Key Flags
88 //      00: not encrypted
89 //      01: encrypted with even key
90 //      10: encrypted with odd key
91 //   bit r: retransmission flag (set to 1 if this packet was sent again)
92 //
93 //    0                   1                   2                   3
94 //    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
95 //   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
96 //   |1|            Type             |             Reserved          |
97 //   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
98 //   |                       Additional Info                         |
99 //   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
100 //   |                          Time Stamp                           |
101 //   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
102 //   |                     Destination Socket ID                     |
103 //   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
104 //
105 //   bit 1-15: Message type -- see @a UDTMessageType
106 //      0: Protocol Connection Handshake (UMSG_HANDSHAKE}
107 //              Add. Info:    Undefined
108 //              Control Info: Handshake information (see @a CHandShake)
109 //      1: Keep-alive (UMSG_KEEPALIVE)
110 //              Add. Info:    Undefined
111 //              Control Info: None
112 //      2: Acknowledgement (UMSG_ACK)
113 //              Add. Info:    The ACK sequence number
114 //              Control Info: The sequence number to which (but not include) all the previous packets have beed received
115 //              Optional:     RTT
116 //                            RTT Variance
117 //                            available receiver buffer size (in bytes)
118 //                            advertised flow window size (number of packets)
119 //                            estimated bandwidth (number of packets per second)
120 //      3: Negative Acknowledgement (UMSG_LOSSREPORT)
121 //              Add. Info:    Undefined
122 //              Control Info: Loss list (see loss list coding below)
123 //      4: Congestion/Delay Warning (UMSG_CGWARNING)
124 //              Add. Info:    Undefined
125 //              Control Info: None
126 //      5: Shutdown (UMSG_SHUTDOWN)
127 //              Add. Info:    Undefined
128 //              Control Info: None
129 //      6: Acknowledgement of Acknowledement (UMSG_ACKACK)
130 //              Add. Info:    The ACK sequence number
131 //              Control Info: None
132 //      7: Message Drop Request (UMSG_DROPREQ)
133 //              Add. Info:    Message ID
134 //              Control Info: first sequence number of the message
135 //                            last seqeunce number of the message
136 //      8: Error Signal from the Peer Side (UMSG_PEERERROR)
137 //              Add. Info:    Error code
138 //              Control Info: None
139 //      0x7FFF: Explained by bits 16 - 31 (UMSG_EXT)
140 //
141 //   bit 16 - 31:
142 //      This space is used for future expansion or user defined control packets.
143 //
144 //    0                   1                   2                   3
145 //    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
146 //   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
147 //   |1|                 Sequence Number a (first)                   |
148 //   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
149 //   |0|                 Sequence Number b (last)                    |
150 //   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
151 //   |0|                 Sequence Number (single)                    |
152 //   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
153 //
154 //   Loss List Field Coding:
155 //      For any consectutive lost seqeunce numbers that the differnece between
156 //      the last and first is more than 1, only record the first (a) and the
157 //      the last (b) sequence numbers in the loss list field, and modify the
158 //      the first bit of a to 1.
159 //      For any single loss or consectutive loss less than 2 packets, use
160 //      the original sequence numbers in the field.
161 
162 #include "platform_sys.h"
163 
164 #include <cstring>
165 #include "packet.h"
166 #include "handshake.h"
167 #include "logging.h"
168 #include "handshake.h"
169 
170 namespace srt_logging
171 {
172     extern Logger inlog;
173 }
174 using namespace srt_logging;
175 
176 // Set up the aliases in the constructure
CPacket()177 srt::CPacket::CPacket():
178     m_extra_pad(),
179     m_data_owned(false),
180     m_iSeqNo((int32_t&)(m_nHeader[SRT_PH_SEQNO])),
181     m_iMsgNo((int32_t&)(m_nHeader[SRT_PH_MSGNO])),
182     m_iTimeStamp((int32_t&)(m_nHeader[SRT_PH_TIMESTAMP])),
183     m_iID((int32_t&)(m_nHeader[SRT_PH_ID])),
184     m_pcData((char*&)(m_PacketVector[PV_DATA].dataRef()))
185 {
186     m_nHeader.clear();
187 
188     // The part at PV_HEADER will be always set to a builtin buffer
189     // containing SRT header.
190     m_PacketVector[PV_HEADER].set(m_nHeader.raw(), HDR_SIZE);
191 
192     // The part at PV_DATA is zero-initialized. It should be
193     // set (through m_pcData and setLength()) to some externally
194     // provided buffer before calling CChannel::sendto().
195     m_PacketVector[PV_DATA].set(NULL, 0);
196 }
197 
getData()198 char* srt::CPacket::getData()
199 {
200     return (char*)m_PacketVector[PV_DATA].dataRef();
201 }
202 
allocate(size_t alloc_buffer_size)203 void srt::CPacket::allocate(size_t alloc_buffer_size)
204 {
205     if (m_data_owned)
206     {
207         if (getLength() == alloc_buffer_size)
208             return; // already allocated
209 
210         // Would be nice to reallocate; for now just allocate again.
211         delete [] m_pcData;
212     }
213     m_PacketVector[PV_DATA].set(new char[alloc_buffer_size], alloc_buffer_size);
214     m_data_owned = true;
215 }
216 
deallocate()217 void srt::CPacket::deallocate()
218 {
219     if (m_data_owned)
220         delete [] (char*)m_PacketVector[PV_DATA].data();
221     m_PacketVector[PV_DATA].set(NULL, 0);
222 }
223 
release()224 char* srt::CPacket::release()
225 {
226     // When not owned, release returns NULL.
227     char* buffer = NULL;
228     if (m_data_owned)
229     {
230         buffer = getData();
231         m_data_owned = false;
232     }
233 
234     deallocate(); // won't delete because m_data_owned == false
235     return buffer;
236 }
237 
~CPacket()238 srt::CPacket::~CPacket()
239 {
240     // PV_HEADER is always owned, PV_DATA may use a "borrowed" buffer.
241     // Delete the internal buffer only if it was declared as owned.
242     if (m_data_owned)
243         delete[](char*)m_PacketVector[PV_DATA].data();
244 }
245 
246 
getLength() const247 size_t srt::CPacket::getLength() const
248 {
249    return m_PacketVector[PV_DATA].size();
250 }
251 
setLength(size_t len)252 void srt::CPacket::setLength(size_t len)
253 {
254    m_PacketVector[PV_DATA].setLength(len);
255 }
256 
pack(UDTMessageType pkttype,const int32_t * lparam,void * rparam,size_t size)257 void srt::CPacket::pack(UDTMessageType pkttype, const int32_t* lparam, void* rparam, size_t size)
258 {
259     // Set (bit-0 = 1) and (bit-1~15 = type)
260     setControl(pkttype);
261     HLOGC(inlog.Debug, log << "pack: type=" << MessageTypeStr(pkttype)
262             << " ARG=" << (lparam ? Sprint(*lparam) : std::string("NULL"))
263             << " [ " << (rparam ? Sprint(*(int32_t*)rparam) : std::string()) << " ]");
264 
265    // Set additional information and control information field
266    switch (pkttype)
267    {
268    case UMSG_ACK: //0010 - Acknowledgement (ACK)
269       // ACK packet seq. no.
270       if (NULL != lparam)
271          m_nHeader[SRT_PH_MSGNO] = *lparam;
272 
273       // data ACK seq. no.
274       // optional: RTT (microsends), RTT variance (microseconds) advertised flow window size (packets), and estimated link capacity (packets per second)
275       m_PacketVector[PV_DATA].set(rparam, size);
276 
277       break;
278 
279    case UMSG_ACKACK: //0110 - Acknowledgement of Acknowledgement (ACK-2)
280       // ACK packet seq. no.
281       m_nHeader[SRT_PH_MSGNO] = *lparam;
282 
283       // control info field should be none
284       // but "writev" does not allow this
285       m_PacketVector[PV_DATA].set((void *)&m_extra_pad, 4);
286 
287       break;
288 
289    case UMSG_LOSSREPORT: //0011 - Loss Report (NAK)
290       // loss list
291       m_PacketVector[PV_DATA].set(rparam, size);
292 
293       break;
294 
295    case UMSG_CGWARNING: //0100 - Congestion Warning
296       // control info field should be none
297       // but "writev" does not allow this
298       m_PacketVector[PV_DATA].set((void *)&m_extra_pad, 4);
299 
300       break;
301 
302    case UMSG_KEEPALIVE: //0001 - Keep-alive
303       if (lparam)
304       {
305           // XXX EXPERIMENTAL. Pass the 32-bit integer here.
306           m_nHeader[SRT_PH_MSGNO] = *lparam;
307       }
308       // control info field should be none
309       // but "writev" does not allow this
310       m_PacketVector[PV_DATA].set((void *)&m_extra_pad, 4);
311 
312       break;
313 
314    case UMSG_HANDSHAKE: //0000 - Handshake
315       // control info filed is handshake info
316       m_PacketVector[PV_DATA].set(rparam, size);
317 
318       break;
319 
320    case UMSG_SHUTDOWN: //0101 - Shutdown
321       // control info field should be none
322       // but "writev" does not allow this
323       m_PacketVector[PV_DATA].set((void *)&m_extra_pad, 4);
324 
325       break;
326 
327    case UMSG_DROPREQ: //0111 - Message Drop Request
328       // msg id
329       m_nHeader[SRT_PH_MSGNO] = *lparam;
330 
331       //first seq no, last seq no
332       m_PacketVector[PV_DATA].set(rparam, size);
333 
334       break;
335 
336    case UMSG_PEERERROR: //1000 - Error Signal from the Peer Side
337       // Error type
338       m_nHeader[SRT_PH_MSGNO] = *lparam;
339 
340       // control info field should be none
341       // but "writev" does not allow this
342       m_PacketVector[PV_DATA].set((void *)&m_extra_pad, 4);
343 
344       break;
345 
346    case UMSG_EXT: //0x7FFF - Reserved for user defined control packets
347       // for extended control packet
348       // "lparam" contains the extended type information for bit 16 - 31
349       // "rparam" is the control information
350       m_nHeader[SRT_PH_SEQNO] |= *lparam;
351 
352       if (NULL != rparam)
353       {
354          m_PacketVector[PV_DATA].set(rparam, size);
355       }
356       else
357       {
358          m_PacketVector[PV_DATA].set((void *)&m_extra_pad, 4);
359       }
360 
361       break;
362 
363    default:
364       break;
365    }
366 }
367 
toNL()368 void srt::CPacket::toNL()
369 {
370     // XXX USE HtoNLA!
371     if (isControl())
372     {
373         for (ptrdiff_t i = 0, n = getLength() / 4; i < n; ++i)
374             *((uint32_t*)m_pcData + i) = htonl(*((uint32_t*)m_pcData + i));
375     }
376 
377     // convert packet header into network order
378     uint32_t* p = m_nHeader;
379     for (int j = 0; j < 4; ++j)
380     {
381         *p = htonl(*p);
382         ++p;
383     }
384 }
385 
toHL()386 void srt::CPacket::toHL()
387 {
388     // convert back into local host order
389     uint32_t* p = m_nHeader;
390     for (int k = 0; k < 4; ++k)
391     {
392         *p = ntohl(*p);
393         ++p;
394     }
395 
396     if (isControl())
397     {
398         for (ptrdiff_t l = 0, n = getLength() / 4; l < n; ++l)
399             *((uint32_t*) m_pcData + l) = ntohl(*((uint32_t*) m_pcData + l));
400     }
401 }
402 
403 
getPacketVector()404 IOVector* srt::CPacket::getPacketVector()
405 {
406    return m_PacketVector;
407 }
408 
getType() const409 UDTMessageType srt::CPacket::getType() const
410 {
411     return UDTMessageType(SEQNO_MSGTYPE::unwrap(m_nHeader[SRT_PH_SEQNO]));
412 }
413 
getExtendedType() const414 int srt::CPacket::getExtendedType() const
415 {
416     return SEQNO_EXTTYPE::unwrap(m_nHeader[SRT_PH_SEQNO]);
417 }
418 
getAckSeqNo() const419 int32_t srt::CPacket::getAckSeqNo() const
420 {
421    // read additional information field
422    // This field is used only in UMSG_ACK and UMSG_ACKACK,
423    // so 'getAckSeqNo' symbolically defines the only use of it
424    // in case of CONTROL PACKET.
425    return m_nHeader[SRT_PH_MSGNO];
426 }
427 
getControlFlags() const428 uint16_t srt::CPacket::getControlFlags() const
429 {
430     // This returns exactly the "extended type" value,
431     // which is not used at all in case when the standard
432     // type message is interpreted. This can be used to pass
433     // additional special flags.
434     return SEQNO_EXTTYPE::unwrap(m_nHeader[SRT_PH_SEQNO]);
435 }
436 
getMsgBoundary() const437 PacketBoundary srt::CPacket::getMsgBoundary() const
438 {
439     return PacketBoundary(MSGNO_PACKET_BOUNDARY::unwrap(m_nHeader[SRT_PH_MSGNO]));
440 }
441 
getMsgOrderFlag() const442 bool srt::CPacket::getMsgOrderFlag() const
443 {
444     return 0!=  MSGNO_PACKET_INORDER::unwrap(m_nHeader[SRT_PH_MSGNO]);
445 }
446 
getMsgSeq(bool has_rexmit) const447 int32_t srt::CPacket::getMsgSeq(bool has_rexmit) const
448 {
449     if ( has_rexmit )
450     {
451         return MSGNO_SEQ::unwrap(m_nHeader[SRT_PH_MSGNO]);
452     }
453     else
454     {
455         return MSGNO_SEQ_OLD::unwrap(m_nHeader[SRT_PH_MSGNO]);
456     }
457 }
458 
getRexmitFlag() const459 bool srt::CPacket::getRexmitFlag() const
460 {
461     // return false; //
462     return 0 !=  MSGNO_REXMIT::unwrap(m_nHeader[SRT_PH_MSGNO]);
463 }
464 
getMsgCryptoFlags() const465 EncryptionKeySpec srt::CPacket::getMsgCryptoFlags() const
466 {
467     return EncryptionKeySpec(MSGNO_ENCKEYSPEC::unwrap(m_nHeader[SRT_PH_MSGNO]));
468 }
469 
470 // This is required as the encryption/decryption happens in place.
471 // This is required to clear off the flags after decryption or set
472 // crypto flags after encrypting a packet.
setMsgCryptoFlags(EncryptionKeySpec spec)473 void srt::CPacket::setMsgCryptoFlags(EncryptionKeySpec spec)
474 {
475     int32_t clr_msgno = m_nHeader[SRT_PH_MSGNO] & ~MSGNO_ENCKEYSPEC::mask;
476     m_nHeader[SRT_PH_MSGNO] = clr_msgno | EncryptionKeyBits(spec);
477 }
478 
getMsgTimeStamp() const479 uint32_t srt::CPacket::getMsgTimeStamp() const
480 {
481    // SRT_DEBUG_TSBPD_WRAP may enable smaller timestamp for faster wraparoud handling tests
482    return (uint32_t)m_nHeader[SRT_PH_TIMESTAMP] & TIMESTAMP_MASK;
483 }
484 
clone() const485 srt::CPacket* srt::CPacket::clone() const
486 {
487    CPacket* pkt = new CPacket;
488    memcpy((pkt->m_nHeader), m_nHeader, HDR_SIZE);
489    pkt->m_pcData = new char[m_PacketVector[PV_DATA].size()];
490    memcpy((pkt->m_pcData), m_pcData, m_PacketVector[PV_DATA].size());
491    pkt->m_PacketVector[PV_DATA].setLength(m_PacketVector[PV_DATA].size());
492 
493    return pkt;
494 }
495 
496 namespace srt {
497 
498 // Useful for debugging
PacketMessageFlagStr(uint32_t msgno_field)499 std::string PacketMessageFlagStr(uint32_t msgno_field)
500 {
501     using namespace std;
502 
503     stringstream out;
504 
505     static const char* const boundary [] = { "PB_SUBSEQUENT", "PB_LAST", "PB_FIRST", "PB_SOLO" };
506     static const char* const order [] = { "ORD_RELAXED", "ORD_REQUIRED" };
507     static const char* const crypto [] = { "EK_NOENC", "EK_EVEN", "EK_ODD", "EK*ERROR" };
508     static const char* const rexmit [] = { "SN_ORIGINAL", "SN_REXMIT" };
509 
510     out << boundary[MSGNO_PACKET_BOUNDARY::unwrap(msgno_field)] << " ";
511     out << order[MSGNO_PACKET_INORDER::unwrap(msgno_field)] << " ";
512     out << crypto[MSGNO_ENCKEYSPEC::unwrap(msgno_field)] << " ";
513     out << rexmit[MSGNO_REXMIT::unwrap(msgno_field)];
514 
515     return out.str();
516 }
517 
SprintSpecialWord(std::ostream & os,int32_t val)518 inline void SprintSpecialWord(std::ostream& os, int32_t val)
519 {
520     if (val & LOSSDATA_SEQNO_RANGE_FIRST)
521         os << "<" << (val & (~LOSSDATA_SEQNO_RANGE_FIRST)) << ">";
522     else
523         os << val;
524 }
525 
526 } // namespace srt
527 
528 #if ENABLE_LOGGING
Info()529 std::string srt::CPacket::Info()
530 {
531     std::ostringstream os;
532     os << "TARGET=@" << m_iID << " ";
533 
534     if (isControl())
535     {
536         os << "CONTROL: size=" << getLength() << " type=" << MessageTypeStr(getType(), getExtendedType());
537 
538         if (getType() == UMSG_HANDSHAKE)
539         {
540             os << " HS: ";
541             // For handshake we already have a parsing method
542             CHandShake hs;
543             hs.load_from(m_pcData, getLength());
544             os << hs.show();
545         }
546         else
547         {
548             // This is a value that some messages use for some purposes.
549             // The "ack seq no" is one of the purposes, used by UMSG_ACK and UMSG_ACKACK.
550             // This is simply the SRT_PH_MSGNO field used as a message number in data packets.
551             os << " ARG: 0x";
552             os << std::hex << getAckSeqNo() << " ";
553             os << std::dec << getAckSeqNo();
554 
555             // It would be nice to see the extended packet data, but this
556             // requires strictly a message-dependent interpreter. So let's simply
557             // display all numbers in the array with the following restrictions:
558             // - all data contained in the buffer are considered 32-bit integer
559             // - sign flag will be cleared before displaying, with additional mark
560             size_t wordlen = getLength()/4; // drop any remainder if present
561             int32_t* array = (int32_t*)m_pcData;
562             os << " [ ";
563             for (size_t i = 0; i < wordlen; ++i)
564             {
565                 SprintSpecialWord(os, array[i]);
566                 os << " ";
567             }
568             os << "]";
569         }
570     }
571     else
572     {
573         // It's hard to extract the information about peer's supported rexmit flag.
574         // This is only a log, nothing crucial, so we can risk displaying incorrect message number.
575         // Declaring that the peer supports rexmit flag cuts off the highest bit from
576         // the displayed number.
577         os << "DATA: size=" << getLength()
578             << " " << BufferStamp(m_pcData, getLength())
579             << " #" << getMsgSeq(true) << " %" << getSeqNo()
580             << " " << MessageFlagStr();
581     }
582 
583     return os.str();
584 }
585 #endif
586