1 /*
2  * Copyright (C) 2010-2013 Codership Oy <info@codership.com>
3  */
4 
5 #ifndef GCOMM_DATAGRAM_HPP
6 #define GCOMM_DATAGRAM_HPP
7 
8 #include "gu_buffer.hpp"
9 #include "gu_serialize.hpp"
10 #include "gu_utils.hpp"
11 #include "gu_throw.hpp"
12 
13 #include <limits>
14 #include <cassert>
15 
16 #include <cstring>
17 #include <stdint.h>
18 
19 namespace gcomm
20 {
21 
22     //!
23     // @class NetHeader
24     //
25     // @brief Header for datagrams sent over network
26     //
27     // Header structure is the following (MSB first)
28     //
29     // | version(4) | reserved(2) | F_CRC(2) | len(24) |
30     // |                   CRC(32)                     |
31     //
32     class NetHeader
33     {
34     public:
35 
36         typedef enum checksum
37         {
38             CS_NONE = 0,
39             CS_CRC32,
40             CS_CRC32C
41         } checksum_t;
42 
43         static checksum_t checksum_type (int i);
44 
NetHeader()45         NetHeader()
46             :
47             len_(),
48             crc32_()
49         { }
50 
NetHeader(uint32_t len,int version)51         NetHeader(uint32_t len, int version)
52             :
53             len_(len),
54             crc32_(0)
55         {
56             if (len > len_mask_)
57                 gu_throw_error(EINVAL) << "msg too long " << len_;
58             len_ |= (static_cast<uint32_t>(version) << version_shift_);
59         }
60 
len() const61         uint32_t len() const { return (len_ & len_mask_); }
62 
set_crc32(uint32_t crc32,checksum_t type)63         void set_crc32(uint32_t crc32, checksum_t type)
64         {
65             assert (CS_CRC32 == type || CS_CRC32C == type);
66             crc32_ = crc32;
67             CS_CRC32 == type ? len_  |= F_CRC32 : len_ |= F_CRC32C;
68         }
69 
has_crc32() const70         bool has_crc32()  const { return (len_ & F_CRC32);  }
has_crc32c() const71         bool has_crc32c() const { return (len_ & F_CRC32C); }
72 
crc32() const73         uint32_t crc32()  const { return crc32_; }
74 
version() const75         int version() const
76         {
77             return ((len_ & version_mask_) >> version_shift_);
78         }
79 
80         friend size_t serialize(const NetHeader& hdr, gu::byte_t* buf,
81                                 size_t buflen, size_t offset);
82 
83         friend size_t unserialize(const gu::byte_t* buf, size_t buflen,
84                                   size_t offset, NetHeader& hdr);
85 
86         friend size_t serial_size(const NetHeader& hdr);
87 
88         static const size_t serial_size_ = 8;
89 
90     private:
91 
92         static const uint32_t len_mask_      = 0x00ffffff;
93         static const uint32_t flags_mask_    = 0x0f000000;
94         static const uint32_t flags_shift_   = 24;
95         static const uint32_t version_mask_  = 0xf0000000;
96         static const uint32_t version_shift_ = 28;
97 
98         static const uint32_t F_CRC32  = 1 << 24; /* backward compatible */
99         static const uint32_t F_CRC32C = 1 << 25;
100 
101         uint32_t len_;
102         uint32_t crc32_;
103     };
104 
serialize(const NetHeader & hdr,gu::byte_t * buf,size_t buflen,size_t offset)105     inline size_t serialize(const NetHeader& hdr, gu::byte_t* buf,
106                             size_t buflen, size_t offset)
107     {
108         offset = gu::serialize4(hdr.len_, buf, buflen, offset);
109         offset = gu::serialize4(hdr.crc32_, buf, buflen, offset);
110         return offset;
111     }
112 
unserialize(const gu::byte_t * buf,size_t buflen,size_t offset,NetHeader & hdr)113     inline size_t unserialize(const gu::byte_t* buf, size_t buflen,
114                               size_t offset, NetHeader& hdr)
115     {
116         offset = gu::unserialize4(buf, buflen, offset, hdr.len_);
117         offset = gu::unserialize4(buf, buflen, offset, hdr.crc32_);
118 
119         switch (hdr.version())
120         {
121         case 0:
122             if ((hdr.len_ & NetHeader::flags_mask_) &
123                 ~(NetHeader::F_CRC32 | NetHeader::F_CRC32C))
124             {
125                 gu_throw_error(EPROTO)
126                     << "invalid flags "
127                     << ((hdr.len_ & NetHeader::flags_mask_) >>
128                         NetHeader::flags_shift_);
129             }
130             break;
131         default:
132             gu_throw_error(EPROTO) << "invalid protocol version "
133                                    << hdr.version();
134         }
135 
136         return offset;
137     }
138 
serial_size(const NetHeader & hdr)139     inline size_t serial_size(const NetHeader& hdr)
140     {
141         return NetHeader::serial_size_;
142     }
143 
144     /*!
145      * @brief  Datagram container
146      *
147      * Datagram class provides consistent interface for managing
148      * datagrams/byte buffers.
149      */
150     class Datagram
151     {
152     public:
Datagram()153         Datagram()
154             :
155             header_       (),
156             header_offset_(header_size_),
157             payload_      (new gu::Buffer()),
158             offset_       (0)
159         { }
160         /*!
161          * @brief Construct new datagram from byte buffer
162          *
163          * @param[in] buf Const pointer to data buffer
164          * @param[in] buflen Length of data buffer
165          *
166          * @throws std::bad_alloc
167          */
168 
Datagram(const gu::Buffer & buf,size_t offset=0)169         Datagram(const gu::Buffer& buf, size_t offset = 0)
170             :
171             header_       (),
172             header_offset_(header_size_),
173             payload_      (new gu::Buffer(buf)),
174             offset_       (offset)
175         {
176             assert(offset_ <= payload_->size());
177         }
178 
Datagram(const gu::SharedBuffer & buf,size_t offset=0)179         Datagram(const gu::SharedBuffer& buf, size_t offset = 0)
180             :
181             header_       (),
182             header_offset_(header_size_),
183             payload_      (buf),
184             offset_       (offset)
185         {
186             assert(offset_ <= payload_->size());
187         }
188 
189         /*!
190          * @brief Copy constructor.
191          *
192          * @note Only for normalized datagrams.
193          *
194          * @param[in] dgram Datagram to make copy from
195          * @param[in] off
196          * @throws std::bad_alloc
197          */
Datagram(const Datagram & dgram,size_t off=std::numeric_limits<size_t>::max ())198         Datagram(const Datagram& dgram,
199                  size_t off = std::numeric_limits<size_t>::max()) :
200             // header_(dgram.header_),
201             header_offset_(dgram.header_offset_),
202             payload_(dgram.payload_),
203             offset_(off == std::numeric_limits<size_t>::max() ? dgram.offset_ : off)
204         {
205             assert(offset_ <= dgram.len());
206             memcpy(header_ + header_offset_,
207                    dgram.header_ + dgram.header_offset(),
208                    dgram.header_len());
209         }
210 
211         /*!
212          * @brief Destruct datagram
213          */
~Datagram()214         ~Datagram() { }
215 
normalize()216         void normalize()
217         {
218             const gu::SharedBuffer old_payload(payload_);
219             payload_ = gu::SharedBuffer(new gu::Buffer);
220             payload_->reserve(header_len() + old_payload->size() - offset_);
221 
222             if (header_len() > offset_)
223             {
224                 payload_->insert(payload_->end(),
225                                  header_ + header_offset_ + offset_,
226                                  header_ + header_size_);
227                 offset_ = 0;
228             }
229             else
230             {
231                 offset_ -= header_len();
232             }
233             header_offset_ = header_size_;
234             payload_->insert(payload_->end(),
235                              old_payload->begin()
236                              + gu::Buffer::difference_type(offset_),
237                              old_payload->end());
238             offset_ = 0;
239         }
240 
header()241         gu::byte_t* header() { return header_; }
header() const242         const gu::byte_t* header() const { return header_; }
header_size() const243         size_t header_size()   const { return header_size_; }
header_len() const244         size_t header_len()    const { return (header_size_ - header_offset_); }
header_offset() const245         size_t header_offset() const { return header_offset_; }
246 
set_header_offset(const size_t off)247         void set_header_offset(const size_t off)
248         {
249             // assert(off <= header_size_);
250             if (off > header_size_) gu_throw_fatal << "out of hdrspace";
251             header_offset_ = off;
252         }
253 
payload() const254         const gu::Buffer& payload() const
255         {
256             assert(payload_);
257             return *payload_;
258         }
259 
payload()260         gu::Buffer& payload()
261         {
262             assert(payload_);
263             return *payload_;
264         }
265 
len() const266         size_t len() const
267         {
268             return (header_size_ - header_offset_ + payload_->size());
269         }
270 
offset() const271         size_t offset() const { return offset_; }
272 
273     private:
274 
275         friend uint16_t crc16(const Datagram&, size_t);
276         friend uint32_t crc32(NetHeader::checksum_t, const Datagram&, size_t);
277 
278         static const size_t header_size_ = 128;
279         gu::byte_t          header_[header_size_];
280         size_t              header_offset_;
281         gu::SharedBuffer    payload_;
282         size_t              offset_;
283     };
284 
285     uint16_t crc16(const Datagram& dg, size_t offset = 0);
286     uint32_t crc32(NetHeader::checksum_t type, const Datagram& dg,
287                    size_t offset = 0);
288 
289     /* returns true if checksum fails */
check_cs(const NetHeader & hdr,const Datagram & dg)290     inline bool check_cs (const NetHeader& hdr, const Datagram& dg)
291     {
292         if (hdr.has_crc32c())
293             return (crc32(NetHeader::CS_CRC32C, dg) != hdr.crc32());
294 
295         if (hdr.has_crc32())
296             return (crc32(NetHeader::CS_CRC32, dg)  != hdr.crc32());
297 
298         return (hdr.crc32() != 0);
299     }
300 } /* namespace gcomm */
301 
302 #endif // GCOMM_DATAGRAM_HPP
303