1 /*
2  * Copyright (c) 2011-2015, Intel Corporation
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without modification,
6  * are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice, this
9  * list of conditions and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above copyright notice,
12  * this list of conditions and the following disclaimer in the documentation and/or
13  * other materials provided with the distribution.
14  *
15  * 3. Neither the name of the copyright holder nor the names of its contributors
16  * may be used to endorse or promote products derived from this software without
17  * specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
23  * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
26  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 #include "Message.h"
31 #include "Socket.h"
32 #include "Iterator.hpp"
33 #include <asio.hpp>
34 #include <vector>
35 #include <numeric>
36 #include <cassert>
37 
38 using std::string;
39 
CMessage(MsgType ucMsgId)40 CMessage::CMessage(MsgType ucMsgId) : _ucMsgId(ucMsgId), _uiIndex(0)
41 {
42 }
43 
CMessage()44 CMessage::CMessage() : _ucMsgId(MsgType::EInvalid), _uiIndex(0)
45 {
46 }
47 
48 // Msg Id
getMsgId() const49 CMessage::MsgType CMessage::getMsgId() const
50 {
51     return _ucMsgId;
52 }
53 
isValidAccess(size_t offset,size_t size) const54 bool CMessage::isValidAccess(size_t offset, size_t size) const
55 {
56     return offset + size <= getMessageDataSize();
57 }
58 
59 // Data
writeData(const void * pvData,size_t size)60 void CMessage::writeData(const void *pvData, size_t size)
61 {
62     assert(isValidAccess(_uiIndex, size));
63 
64     auto first = MAKE_ARRAY_ITERATOR(static_cast<const uint8_t *>(pvData), size);
65     auto last = first + size;
66     auto destFirst = begin(mData) + _uiIndex;
67 
68     std::copy(first, last, destFirst);
69 
70     _uiIndex += size;
71 }
72 
readData(void * pvData,size_t size)73 void CMessage::readData(void *pvData, size_t size)
74 {
75     assert(isValidAccess(_uiIndex, size));
76 
77     auto first = begin(mData) + _uiIndex;
78     auto last = first + size;
79     auto destFirst = MAKE_ARRAY_ITERATOR(static_cast<uint8_t *>(pvData), size);
80 
81     std::copy(first, last, destFirst);
82 
83     _uiIndex += size;
84 }
85 
writeString(const string & strData)86 void CMessage::writeString(const string &strData)
87 {
88     // Size
89     uint32_t size = static_cast<uint32_t>(strData.length());
90 
91     writeData(&size, sizeof(size));
92 
93     // Content
94     writeData(strData.c_str(), size);
95 }
96 
readString(string & strData)97 void CMessage::readString(string &strData)
98 {
99     // Size
100     uint32_t uiSize;
101 
102     readData(&uiSize, sizeof(uiSize));
103 
104     // Data
105     std::vector<char> string(uiSize + 1);
106 
107     // Content
108     readData(string.data(), uiSize);
109 
110     // NULL-terminate string
111     string.back() = '\0';
112 
113     // Output
114     strData = string.data();
115 }
116 
getStringSize(const string & strData) const117 size_t CMessage::getStringSize(const string &strData) const
118 {
119     // Return string length plus room to store its length
120     return strData.length() + sizeof(uint32_t);
121 }
122 
123 // Remaining data size
getRemainingDataSize() const124 size_t CMessage::getRemainingDataSize() const
125 {
126     return getMessageDataSize() - _uiIndex;
127 }
128 
129 // Send/Receive
serialize(Socket && socket,bool bOut,string & strError)130 CMessage::Result CMessage::serialize(Socket &&socket, bool bOut, string &strError)
131 {
132     asio::ip::tcp::socket &asioSocket = socket.get();
133 
134     if (bOut) {
135         asio::error_code ec;
136 
137         // Make room for data to send
138         allocateData(getDataSize());
139 
140         // Get data from derived
141         fillDataToSend();
142 
143         // Finished providing data?
144         assert(_uiIndex == getMessageDataSize());
145 
146         // First send sync word
147         uint16_t uiSyncWord = SYNC_WORD;
148 
149         if (!asio::write(asioSocket, asio::buffer(&uiSyncWord, sizeof(uiSyncWord)), ec)) {
150 
151             if (ec == asio::error::eof) {
152                 return peerDisconnected;
153             }
154             return error;
155         }
156 
157         // Size
158         uint32_t uiSize = (uint32_t)(sizeof(_ucMsgId) + getMessageDataSize());
159 
160         if (!asio::write(asioSocket, asio::buffer(&uiSize, sizeof(uiSize)), ec)) {
161 
162             strError += string("Size write failed: ") + ec.message();
163             return error;
164         }
165 
166         // Msg Id
167         if (!asio::write(asioSocket, asio::buffer(&_ucMsgId, sizeof(_ucMsgId)), ec)) {
168 
169             strError += string("Msg write failed: ") + ec.message();
170             return error;
171         }
172 
173         // Data
174         if (!asio::write(asioSocket, asio::buffer(mData), ec)) {
175 
176             strError = string("Data write failed: ") + ec.message();
177             return error;
178         }
179 
180         // Checksum
181         uint8_t ucChecksum = computeChecksum();
182 
183         if (!asio::write(asioSocket, asio::buffer(&ucChecksum, sizeof(ucChecksum)), ec)) {
184 
185             strError = string("Checksum write failed: ") + ec.message();
186             return error;
187         }
188 
189     } else {
190         // First read sync word
191         uint16_t uiSyncWord = 0;
192         asio::error_code ec;
193 
194         if (!asio::read(asioSocket, asio::buffer(&uiSyncWord, sizeof(uiSyncWord)), ec)) {
195             strError = string("Sync read failed: ") + ec.message();
196             if (ec == asio::error::eof) {
197                 return peerDisconnected;
198             }
199             return error;
200         }
201 
202         // Check Sync word
203         if (uiSyncWord != SYNC_WORD) {
204 
205             strError = "Sync word incorrect";
206             return error;
207         }
208 
209         // Size
210         uint32_t uiSize = 0;
211 
212         if (!asio::read(asioSocket, asio::buffer(&uiSize, sizeof(uiSize)), ec)) {
213             strError = string("Size read failed: ") + ec.message();
214             return error;
215         }
216 
217         // Msg Id
218         if (!asio::read(asioSocket, asio::buffer(&_ucMsgId, sizeof(_ucMsgId)), ec)) {
219             strError = string("Msg id read failed: ") + ec.message();
220             return error;
221         }
222 
223         // Data
224 
225         // Allocate
226         allocateData(uiSize - sizeof(_ucMsgId));
227 
228         // Data receive
229         if (!asio::read(asioSocket, asio::buffer(mData), ec)) {
230             strError = string("Data read failed: ") + ec.message();
231             return error;
232         }
233 
234         // Checksum
235         uint8_t ucChecksum = 0;
236 
237         if (!asio::read(asioSocket, asio::buffer(&ucChecksum, sizeof(ucChecksum)), ec)) {
238             strError = string("Checksum read failed: ") + ec.message();
239             return error;
240         }
241         // Compare
242         if (ucChecksum != computeChecksum()) {
243 
244             strError = "Received checksum != computed checksum";
245             return error;
246         }
247 
248         // Collect data in derived
249         collectReceivedData();
250     }
251 
252     return success;
253 }
254 
255 // Checksum
computeChecksum() const256 uint8_t CMessage::computeChecksum() const
257 {
258     return accumulate(begin(mData), end(mData), static_cast<uint8_t>(_ucMsgId));
259 }
260 
261 // Allocation of room to store the message
allocateData(size_t size)262 void CMessage::allocateData(size_t size)
263 {
264     // Remove previous one
265     mData.clear();
266 
267     // Do allocate
268     mData.resize(size);
269 
270     // Reset Index
271     _uiIndex = 0;
272 }
273