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