DCCL v3
codec.cpp
1 // Copyright 2009-2017 Toby Schneider (http://gobysoft.org/index.wt/people/toby)
2 // GobySoft, LLC (for 2013-)
3 // Massachusetts Institute of Technology (for 2007-2014)
4 // Community contributors (see AUTHORS file)
5 //
6 //
7 // This file is part of the Dynamic Compact Control Language Library
8 // ("DCCL").
9 //
10 // DCCL is free software: you can redistribute it and/or modify
11 // it under the terms of the GNU Lesser General Public License as published by
12 // the Free Software Foundation, either version 2.1 of the License, or
13 // (at your option) any later version.
14 //
15 // DCCL is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU Lesser General Public License for more details.
19 //
20 // You should have received a copy of the GNU Lesser General Public License
21 // along with DCCL. If not, see <http://www.gnu.org/licenses/>.
22 #include <algorithm>
23 
24 #include <dlfcn.h> // for shared library loading
25 
26 #include "dccl/codec.h"
27 
28 #if DCCL_HAS_CRYPTOPP
29 #if CRYPTOPP_PATH_USES_PLUS_SIGN
30 #include <crypto++/filters.h>
31 #include <crypto++/sha.h>
32 #include <crypto++/modes.h>
33 #include <crypto++/aes.h>
34 #else
35 #include <cryptopp/filters.h>
36 #include <cryptopp/sha.h>
37 #include <cryptopp/modes.h>
38 #include <cryptopp/aes.h>
39 #endif // CRYPTOPP_PATH_USES_PLUS_SIGN
40 #endif // HAS_CRYPTOPP
41 
42 #include "dccl/codecs2/field_codec_default.h"
43 #include "dccl/codecs3/field_codec_default.h"
44 #include "dccl/codecs3/field_codec_var_bytes.h"
45 #include "dccl/codecs3/field_codec_presence.h"
46 #include "dccl/field_codec_id.h"
47 
48 #include "dccl/option_extensions.pb.h"
49 
50 
51 using dccl::hex_encode;
52 using dccl::hex_decode;
53 
54 using namespace dccl;
55 using namespace dccl::logger;
56 
57 using google::protobuf::FieldDescriptor;
58 using google::protobuf::Descriptor;
59 using google::protobuf::Reflection;
60 
61 const unsigned full_width = 60;
62 
63 
64 //
65 // Codec
66 //
67 
68 dccl::Codec::Codec(const std::string& dccl_id_codec, const std::string& library_path)
69  : strict_(false), id_codec_(dccl_id_codec)
70 {
71  set_default_codecs();
72  FieldCodecManager::add<DefaultIdentifierCodec>(default_id_codec_name());
73 
74  if(!library_path.empty())
75  load_library(library_path);
76  // make sure the id codec exists
77  id_codec();
78 }
79 
81 {
82  for(std::vector<void *>::iterator it = dl_handles_.begin(),
83  n = dl_handles_.end(); it != n; ++it)
84  {
85  unload_library(*it);
86  dlclose(*it);
87  }
88 }
89 
90 
91 void dccl::Codec::set_default_codecs()
92 {
93  // only need to load these once into the static FieldCodecManager
94  static bool defaults_loaded = false;
95 
96  if(!defaults_loaded)
97  {
98  using google::protobuf::FieldDescriptor;
99 
100  // version 2
101  FieldCodecManager::add<v2::DefaultNumericFieldCodec<double> >(default_codec_name());
102  FieldCodecManager::add<v2::DefaultNumericFieldCodec<float> >(default_codec_name());
103  FieldCodecManager::add<v2::DefaultBoolCodec>(default_codec_name());
104  FieldCodecManager::add<v2::DefaultNumericFieldCodec<int32> >(default_codec_name());
105  FieldCodecManager::add<v2::DefaultNumericFieldCodec<int64> >(default_codec_name());
106  FieldCodecManager::add<v2::DefaultNumericFieldCodec<uint32> >(default_codec_name());
107  FieldCodecManager::add<v2::DefaultNumericFieldCodec<uint64> >(default_codec_name());
108  FieldCodecManager::add<v2::DefaultStringCodec, FieldDescriptor::TYPE_STRING>(default_codec_name());
109  FieldCodecManager::add<v2::DefaultBytesCodec, FieldDescriptor::TYPE_BYTES>(default_codec_name());
110  FieldCodecManager::add<v2::DefaultEnumCodec >(default_codec_name());
111  FieldCodecManager::add<v2::DefaultMessageCodec, FieldDescriptor::TYPE_MESSAGE>(default_codec_name());
112 
113  FieldCodecManager::add<v2::TimeCodec<uint64> >("dccl.time2");
114  FieldCodecManager::add<v2::TimeCodec<int64> >("dccl.time2");
115  FieldCodecManager::add<v2::TimeCodec<double> >("dccl.time2");
116 
117  FieldCodecManager::add<v2::StaticCodec<std::string> >("dccl.static2");
118  FieldCodecManager::add<v2::StaticCodec<double> >("dccl.static2");
119  FieldCodecManager::add<v2::StaticCodec<float> >("dccl.static2");
120  FieldCodecManager::add<v2::StaticCodec<int32> >("dccl.static2");
121  FieldCodecManager::add<v2::StaticCodec<int64> >("dccl.static2");
122  FieldCodecManager::add<v2::StaticCodec<uint32> >("dccl.static2");
123  FieldCodecManager::add<v2::StaticCodec<uint64> >("dccl.static2");
124 
125  // version 3
126  FieldCodecManager::add<v3::DefaultNumericFieldCodec<double> >(default_codec_name(3));
127  FieldCodecManager::add<v3::DefaultNumericFieldCodec<float> >(default_codec_name(3));
128  FieldCodecManager::add<v3::DefaultBoolCodec>(default_codec_name(3));
129  FieldCodecManager::add<v3::DefaultNumericFieldCodec<int32> >(default_codec_name(3));
130  FieldCodecManager::add<v3::DefaultNumericFieldCodec<int64> >(default_codec_name(3));
131  FieldCodecManager::add<v3::DefaultNumericFieldCodec<uint32> >(default_codec_name(3));
132  FieldCodecManager::add<v3::DefaultNumericFieldCodec<uint64> >(default_codec_name(3));
133  FieldCodecManager::add<v3::DefaultStringCodec, FieldDescriptor::TYPE_STRING>(default_codec_name(3));
134  FieldCodecManager::add<v3::DefaultBytesCodec, FieldDescriptor::TYPE_BYTES>(default_codec_name(3));
135  FieldCodecManager::add<v3::DefaultEnumCodec >(default_codec_name(3));
136  FieldCodecManager::add<v3::DefaultMessageCodec, FieldDescriptor::TYPE_MESSAGE>(default_codec_name(3));
137 
138  // presence bit codec, which encode empty optional fields with a single bit
139  FieldCodecManager::add<v3::PresenceBitCodec<v3::DefaultNumericFieldCodec<double> > >("dccl.presence");
140  FieldCodecManager::add<v3::PresenceBitCodec<v3::DefaultNumericFieldCodec<float> > >("dccl.presence");
141  FieldCodecManager::add<v3::PresenceBitCodec<v3::DefaultNumericFieldCodec<int32> > >("dccl.presence");
142  FieldCodecManager::add<v3::PresenceBitCodec<v3::DefaultNumericFieldCodec<int64> > >("dccl.presence");
143  FieldCodecManager::add<v3::PresenceBitCodec<v3::DefaultNumericFieldCodec<uint32> > >("dccl.presence");
144  FieldCodecManager::add<v3::PresenceBitCodec<v3::DefaultNumericFieldCodec<uint64> > >("dccl.presence");
145  FieldCodecManager::add<v3::PresenceBitCodec<v3::DefaultEnumCodec > >("dccl.presence");
146 
147  // alternative bytes codec that more efficiently encodes variable length bytes fields
148  FieldCodecManager::add<v3::VarBytesCodec, FieldDescriptor::TYPE_BYTES>("dccl.var_bytes");
149 
150  // for backwards compatibility
151  FieldCodecManager::add<v2::TimeCodec<uint64> >("_time");
152  FieldCodecManager::add<v2::TimeCodec<int64> >("_time");
153  FieldCodecManager::add<v2::TimeCodec<double> >("_time");
154 
155  FieldCodecManager::add<v2::StaticCodec<std::string> >("_static");
156  FieldCodecManager::add<v2::StaticCodec<double> >("_static");
157  FieldCodecManager::add<v2::StaticCodec<float> >("_static");
158  FieldCodecManager::add<v2::StaticCodec<int32> >("_static");
159  FieldCodecManager::add<v2::StaticCodec<int64> >("_static");
160  FieldCodecManager::add<v2::StaticCodec<uint32> >("_static");
161  FieldCodecManager::add<v2::StaticCodec<uint64> >("_static");
162 
163 
164  defaults_loaded = true;
165  }
166 }
167 
168 void dccl::Codec::encode_internal(const google::protobuf::Message& msg, bool header_only, Bitset& head_bits, Bitset& body_bits, int user_id)
169 {
170  const Descriptor* desc = msg.GetDescriptor();
171 
172  dlog.is(DEBUG1, ENCODE) && dlog << "Began encoding message of type: " << desc->full_name() << std::endl;
173 
174  try
175  {
176  unsigned dccl_id = (user_id < 0) ? id(desc) : user_id;
177  size_t head_byte_size = 0;
178 
179  if(!msg.IsInitialized() && !header_only)
180  throw(Exception("Message is not properly initialized. All `required` fields must be set."));
181 
182  if(!id2desc_.count(dccl_id))
183  throw(Exception("Message id " + boost::lexical_cast<std::string>(dccl_id) + " has not been loaded. Call load() before encoding this type."));
184 
185 
186 
187  boost::shared_ptr<FieldCodecBase> codec = FieldCodecManager::find(desc);
188  boost::shared_ptr<internal::FromProtoCppTypeBase> helper = internal::TypeHelper::find(desc);
189 
190  if(codec)
191  {
192  //fixed header
193  id_codec()->field_encode(&head_bits, dccl_id, 0);
194 
195  internal::MessageStack msg_stack;
196  msg_stack.push(msg.GetDescriptor());
197  codec->base_encode(&head_bits, msg, HEAD, strict_);
198 
199  // given header of not even byte size (e.g. 01011), make even byte size (e.g. 00001011)
200  head_byte_size = ceil_bits2bytes(head_bits.size());
201  head_bits.resize(head_byte_size * BITS_IN_BYTE);
202 
203  if(header_only)
204  {
205  dlog.is(DEBUG2, ENCODE) && dlog << "as requested, skipping encoding and encrypting body." << std::endl;
206  }
207  else
208  {
209  codec->base_encode(&body_bits, msg, BODY, strict_);
210  }
211  }
212  else
213  {
214  throw(Exception("Failed to find (dccl.msg).codec `" + desc->options().GetExtension(dccl::msg).codec() + "`"));
215  }
216 
217  }
218  catch(dccl::OutOfRangeException& e)
219  {
220  dlog.is(DEBUG1, ENCODE) && dlog << "Message " << desc->full_name() << " failed to encode because a field was out of bounds and strict == true: " << e.what() << std::endl;
221  throw;
222  }
223  catch(std::exception& e)
224  {
225  std::stringstream ss;
226 
227  ss << "Message " << desc->full_name() << " failed to encode. Reason: " << e.what();
228 
229  dlog.is(DEBUG1, ENCODE) && dlog << ss.str() << std::endl;
230  throw(Exception(ss.str()));
231  }
232 }
233 
234 size_t dccl::Codec::encode(char* bytes, size_t max_len, const google::protobuf::Message& msg, bool header_only /* = false */, int user_id /* = -1 */)
235 {
236  const Descriptor* desc = msg.GetDescriptor();
237  Bitset head_bits;
238  Bitset body_bits;
239  encode_internal(msg, header_only, head_bits, body_bits, user_id);
240 
241  size_t head_byte_size = ceil_bits2bytes(head_bits.size());
242  if (max_len < head_byte_size)
243  {
244  throw std::length_error("max_len must be >= head_byte_size");
245  }
246  head_bits.to_byte_string(bytes, head_byte_size);
247 
248  dlog.is(DEBUG2, ENCODE) && dlog << "Head bytes (bits): " << head_byte_size << "(" << head_bits.size() << ")" << std::endl;
249  dlog.is(DEBUG3, ENCODE) && dlog << "Unencrypted Head (bin): " << head_bits << std::endl;
250  dlog.is(DEBUG3, ENCODE) && dlog << "Unencrypted Head (hex): " << hex_encode(bytes, bytes+head_byte_size) << std::endl;
251 
252  size_t body_byte_size = 0;
253  if (!header_only)
254  {
255  body_byte_size = ceil_bits2bytes(body_bits.size());
256  if (max_len < (head_byte_size + body_byte_size))
257  {
258  throw std::length_error("max_len must be >= (head_byte_size + body_byte_size)");
259  }
260  body_bits.to_byte_string(bytes+head_byte_size, max_len-head_byte_size);
261 
262  dlog.is(DEBUG3, ENCODE) && dlog << "Unencrypted Body (bin): " << body_bits << std::endl;
263  dlog.is(DEBUG3, ENCODE) && dlog << "Unencrypted Body (hex): " << hex_encode(bytes+head_byte_size, bytes+head_byte_size+body_byte_size) << std::endl;
264  dlog.is(DEBUG2, ENCODE) && dlog << "Body bytes (bits): " << body_byte_size << "(" << body_bits.size() << ")" << std::endl;
265 
266  unsigned dccl_id = (user_id < 0) ? id(desc) : user_id;
267  if(!crypto_key_.empty() && !skip_crypto_ids_.count(dccl_id)) {
268  std::string head_bytes(bytes, bytes+head_byte_size);
269  std::string body_bytes(bytes+head_byte_size, bytes+head_byte_size+body_byte_size);
270  encrypt(&body_bytes, head_bytes);
271  std::memcpy(bytes+head_byte_size, body_bytes.data(), body_bytes.size());
272  }
273 
274  dlog.is(logger::DEBUG3, logger::ENCODE) && dlog << "Encrypted Body (hex): " << hex_encode(bytes+head_byte_size, bytes+head_byte_size+body_byte_size) << std::endl;
275  }
276 
277  dlog.is(DEBUG1, ENCODE) && dlog << "Successfully encoded message of type: " << desc->full_name() << std::endl;
278 
279  return head_byte_size + body_byte_size;
280 }
281 
282 
283 void dccl::Codec::encode(std::string* bytes, const google::protobuf::Message& msg, bool header_only /* = false */, int user_id /* = -1 */)
284 {
285  const Descriptor* desc = msg.GetDescriptor();
286  Bitset head_bits;
287  Bitset body_bits;
288  encode_internal(msg, header_only, head_bits, body_bits, user_id);
289 
290  std::string head_bytes = head_bits.to_byte_string();
291 
292  dlog.is(DEBUG2, ENCODE) && dlog << "Head bytes (bits): " << head_bytes.size() << "(" << head_bits.size() << ")" << std::endl;
293  dlog.is(DEBUG3, ENCODE) && dlog << "Unencrypted Head (bin): " << head_bits << std::endl;
294  dlog.is(DEBUG3, ENCODE) && dlog << "Unencrypted Head (hex): " << hex_encode(head_bytes) << std::endl;
295 
296  std::string body_bytes;
297  if (!header_only)
298  {
299  body_bytes = body_bits.to_byte_string();
300 
301  dlog.is(DEBUG3, ENCODE) && dlog << "Unencrypted Body (bin): " << body_bits << std::endl;
302  dlog.is(DEBUG3, ENCODE) && dlog << "Unencrypted Body (hex): " << hex_encode(body_bytes) << std::endl;
303  dlog.is(DEBUG2, ENCODE) && dlog << "Body bytes (bits): " << body_bytes.size() << "(" << body_bits.size() << ")" << std::endl;
304 
305  unsigned dccl_id = (user_id < 0) ? id(desc) : user_id;
306  if(!crypto_key_.empty() && !skip_crypto_ids_.count(dccl_id))
307  encrypt(&body_bytes, head_bytes);
308 
309  dlog.is(logger::DEBUG3, logger::ENCODE) && dlog << "Encrypted Body (hex): " << hex_encode(body_bytes) << std::endl;
310  }
311 
312  dlog.is(DEBUG1, ENCODE) && dlog << "Successfully encoded message of type: " << desc->full_name() << std::endl;
313  *bytes += head_bytes + body_bytes;
314 }
315 
316 unsigned dccl::Codec::id(const std::string& bytes) const
317 {
318  return id(bytes.begin(), bytes.end());
319 }
320 
321 
322 void dccl::Codec::decode(std::string* bytes, google::protobuf::Message* msg)
323 {
324  decode(*bytes, msg);
325  unsigned last_size = size(*msg);
326  bytes->erase(0, last_size);
327 }
328 
329 void dccl::Codec::decode(const std::string& bytes, google::protobuf::Message* msg, bool header_only /* = false */)
330 {
331  decode(bytes.begin(), bytes.end(), msg, header_only);
332 }
333 
334 // makes sure we can actual encode / decode a message of this descriptor given the loaded FieldCodecs
335 // checks all bounds on the message
336 void dccl::Codec::load(const google::protobuf::Descriptor* desc, int user_id /* = -1 */)
337 {
338  try
339  {
340  if(user_id <0 && !desc->options().GetExtension(dccl::msg).has_id())
341  throw(Exception("Missing message option `(dccl.msg).id`. Specify a unique id (e.g. 3) in the body of your .proto message using \"option (dccl.msg).id = 3\""));
342  if(!desc->options().GetExtension(dccl::msg).has_max_bytes())
343  throw(Exception("Missing message option `(dccl.msg).max_bytes`. Specify a maximum (encoded) message size in bytes (e.g. 32) in the body of your .proto message using \"option (dccl.msg).max_bytes = 32\""));
344 
345  if(!desc->options().GetExtension(dccl::msg).has_codec_version())
346  dlog.is(WARN) && dlog << "** NOTE: No (dccl.msg).codec_version set for DCCL Message '" << desc->full_name() << "'. Unless you need backwards compatibility with Goby 2.0 (DCCL2), we highly recommend setting 'option (dccl.msg).codec_version = 3' in the message definition for " << desc->full_name() << " to use the default DCCL3 codecs. If you need compatibility with Goby 2.0, ignore this warning, or set 'option (dccl.msg).codec_version = 2' to remove this warning. **" << std::endl;
347 
348  boost::shared_ptr<FieldCodecBase> codec = FieldCodecManager::find(desc);
349 
350  unsigned dccl_id = (user_id < 0) ? id(desc) : user_id;
351  unsigned head_size_bits, body_size_bits;
352  codec->base_max_size(&head_size_bits, desc, HEAD);
353  codec->base_max_size(&body_size_bits, desc, BODY);
354 
355  unsigned id_bits = 0;
356  id_codec()->field_size(&id_bits, dccl_id, 0);
357  head_size_bits += id_bits;
358 
359  const unsigned byte_size = ceil_bits2bytes(head_size_bits) + ceil_bits2bytes(body_size_bits);
360 
361  if(byte_size > desc->options().GetExtension(dccl::msg).max_bytes())
362  throw(Exception("Actual maximum size of message exceeds allowed maximum (dccl.max_bytes). Tighten bounds, remove fields, improve codecs, or increase the allowed dccl.max_bytes"));
363 
364  codec->base_validate(desc, HEAD);
365  codec->base_validate(desc, BODY);
366 
367  if(id2desc_.count(dccl_id) && desc != id2desc_.find(dccl_id)->second)
368  throw(Exception("`dccl id` " + boost::lexical_cast<std::string>(dccl_id) + " is already in use by Message " + id2desc_.find(dccl_id)->second->full_name() + ": " + boost::lexical_cast<std::string>(id2desc_.find(dccl_id)->second)));
369  else
370  id2desc_.insert(std::make_pair(dccl_id, desc));
371 
372  dlog.is(DEBUG1) && dlog << "Successfully validated message of type: " << desc->full_name() << std::endl;
373 
374  }
375  catch(Exception& e)
376  {
377  try
378  {
379  info(desc, &dlog);
380  }
381  catch(Exception& e)
382  { }
383 
384  dlog.is(DEBUG1) && dlog << "Message " << desc->full_name() << ": " << desc << " failed validation. Reason: "
385  << e.what() << "\n"
386  << "If possible, information about the Message are printed above. " << std::endl;
387 
388  throw;
389  }
390 }
391 
392 
393 void dccl::Codec::unload(const google::protobuf::Descriptor* desc)
394 {
395  unsigned int erased = 0;
396  for (std::map<int32, const google::protobuf::Descriptor*>::iterator it = id2desc_.begin(); it != id2desc_.end();)
397  {
398  if (it->second == desc)
399  {
400  erased++;
401  id2desc_.erase(it++);
402  }
403  else
404  {
405  it++;
406  }
407  }
408  if (erased == 0)
409  {
410  dlog.is(DEBUG1) && dlog << "Message " << desc->full_name() << ": is not loaded. Ignoring unload request." << std::endl;
411  }
412 }
413 
414 
415 void dccl::Codec::unload(size_t dccl_id)
416 {
417  if(id2desc_.count(dccl_id))
418  {
419  id2desc_.erase(dccl_id);
420  }
421  else
422  {
423  dlog.is(DEBUG1) && dlog << "Message with id " << dccl_id << ": is not loaded. Ignoring unload request." << std::endl;
424  return;
425  }
426 }
427 
428 
429 unsigned dccl::Codec::size(const google::protobuf::Message& msg, int user_id /* = -1 */)
430 {
431  const Descriptor* desc = msg.GetDescriptor();
432 
433  boost::shared_ptr<FieldCodecBase> codec = FieldCodecManager::find(desc);
434 
435  unsigned dccl_id = (user_id < 0) ? id(desc) : user_id;
436  unsigned head_size_bits;
437  codec->base_size(&head_size_bits, msg, HEAD);
438 
439  unsigned id_bits = 0;
440  id_codec()->field_size(&id_bits, dccl_id, 0);
441  head_size_bits += id_bits;
442 
443  unsigned body_size_bits;
444  codec->base_size(&body_size_bits, msg, BODY);
445 
446  const unsigned head_size_bytes = ceil_bits2bytes(head_size_bits);
447  const unsigned body_size_bytes = ceil_bits2bytes(body_size_bits);
448  return head_size_bytes + body_size_bytes;
449 }
450 
451 
452 unsigned dccl::Codec::max_size(const google::protobuf::Descriptor* desc) const
453 {
454  boost::shared_ptr<FieldCodecBase> codec = FieldCodecManager::find(desc);
455 
456  unsigned head_size_bits;
457  codec->base_max_size(&head_size_bits, desc, HEAD);
458 
459  unsigned id_bits = 0;
460  id_codec()->field_max_size(&id_bits, 0);
461  head_size_bits += id_bits;
462 
463  unsigned body_size_bits;
464  codec->base_max_size(&body_size_bits, desc, BODY);
465 
466  const unsigned head_size_bytes = ceil_bits2bytes(head_size_bits);
467  const unsigned body_size_bytes = ceil_bits2bytes(body_size_bits);
468  return head_size_bytes + body_size_bytes;
469 }
470 
471 unsigned dccl::Codec::min_size(const google::protobuf::Descriptor* desc) const
472 {
473  boost::shared_ptr<FieldCodecBase> codec = FieldCodecManager::find(desc);
474 
475  unsigned head_size_bits;
476  codec->base_min_size(&head_size_bits, desc, HEAD);
477 
478  unsigned id_bits = 0;
479  id_codec()->field_min_size(&id_bits, 0);
480  head_size_bits += id_bits;
481 
482  unsigned body_size_bits;
483  codec->base_min_size(&body_size_bits, desc, BODY);
484 
485  const unsigned head_size_bytes = ceil_bits2bytes(head_size_bits);
486  const unsigned body_size_bytes = ceil_bits2bytes(body_size_bits);
487  return head_size_bytes + body_size_bytes;
488 }
489 
490 
491 
492 void dccl::Codec::info(const google::protobuf::Descriptor* desc, std::ostream* param_os /*= 0 */, int user_id /* = -1 */) const
493 {
494  std::ostream* os = (param_os) ? param_os : &dlog;
495 
496  if(param_os || dlog.is(INFO))
497  {
498  try
499  {
500  boost::shared_ptr<FieldCodecBase> codec = FieldCodecManager::find(desc);
501 
502  unsigned config_head_bit_size, body_bit_size;
503  codec->base_max_size(&config_head_bit_size, desc, HEAD);
504  codec->base_max_size(&body_bit_size, desc, BODY);
505 
506  unsigned dccl_id = (user_id < 0) ? id(desc) : user_id;
507  unsigned id_bit_size = 0;
508  id_codec()->field_size(&id_bit_size, dccl_id, 0);
509 
510  const unsigned bit_size = id_bit_size + config_head_bit_size + body_bit_size;
511 
512 
513  const unsigned byte_size = ceil_bits2bytes(config_head_bit_size + id_bit_size) + ceil_bits2bytes(body_bit_size);
514 
515  const unsigned allowed_byte_size = desc->options().GetExtension(dccl::msg).max_bytes();
516  const unsigned allowed_bit_size = allowed_byte_size * BITS_IN_BYTE;
517 
518  std::string message_name = boost::lexical_cast<std::string>(dccl_id) + ": " + desc->full_name();
519  std::string guard = std::string((full_width-message_name.size())/2, '=');
520 
521  std::string bits_dccl_head_str = "dccl.id head";
522  std::string bits_user_head_str = "user head";
523  std::string bits_body_str = "body";
524  std::string bits_padding_str = "padding to full byte";
525 
526  const int bits_width = 40;
527  const int spaces = 8;
528  std::string indent = std::string(spaces,' ');
529 
530  *os << guard << " " << message_name << " " << guard << "\n"
531  << "Actual maximum size of message: " << byte_size << " bytes / "
532  << byte_size*BITS_IN_BYTE << " bits\n"
533  << indent << bits_dccl_head_str << std::setfill('.') << std::setw(bits_width-bits_dccl_head_str.size()) << id_bit_size << "\n"
534  << indent << bits_user_head_str << std::setfill('.') << std::setw(bits_width-bits_user_head_str.size()) << config_head_bit_size << "\n"
535  << indent << bits_body_str << std::setfill('.') << std::setw(bits_width-bits_body_str.size()) << body_bit_size << "\n"
536  << indent << bits_padding_str << std::setfill('.') << std::setw(bits_width-bits_padding_str.size()) << byte_size * BITS_IN_BYTE - bit_size << "\n"
537  << "Allowed maximum size of message: " << allowed_byte_size << " bytes / "
538  << allowed_bit_size << " bits\n";
539 
540  std::string header_str = "Header";
541  std::string header_guard = std::string((full_width-header_str.size())/2, '-');
542  *os << header_guard << " " << header_str << " " << header_guard << std::endl;
543  *os << bits_dccl_head_str << std::setfill('.') << std::setw(bits_width-bits_dccl_head_str.size()+spaces) << id_bit_size << " {" << id_codec()->name() << "}\n";
544  codec->base_info(os, desc, HEAD);
545 // *os << std::string(header_str.size() + 2 + 2*header_guard.size(), '-') << std::endl;
546 
547  std::string body_str = "Body";
548  std::string body_guard = std::string((full_width-body_str.size())/2, '-');
549  *os << body_guard << " " << body_str << " " << body_guard << std::endl;
550  codec->base_info(os, desc, BODY);
551 // *os << std::string(body_str.size() + 2 + 2*body_guard.size(), '-') << std::endl;
552 
553 // *os << std::string(desc->full_name().size() + 2 + 2*guard.size(), '=') << std::endl;
554  }
555  catch(Exception& e)
556  {
557  dlog.is(DEBUG1) && dlog << "Message " << desc->full_name() << " cannot provide information due to invalid configuration. Reason: " << e.what() << std::endl;
558  }
559 
560  os->flush();
561 
562  }
563 
564 }
565 
566 
567 void dccl::Codec::encrypt(std::string* s, const std::string& nonce /* message head */)
568 {
569 #if DCCL_HAS_CRYPTOPP
570  using namespace CryptoPP;
571 
572  std::string iv;
573  SHA256 hash;
574  StringSource unused(nonce, true, new HashFilter(hash, new StringSink(iv)));
575 
576  CTR_Mode<AES>::Encryption encryptor;
577  encryptor.SetKeyWithIV((byte*)crypto_key_.c_str(), crypto_key_.size(), (byte*)iv.c_str());
578 
579  std::string cipher;
580  StreamTransformationFilter in(encryptor, new StringSink(cipher));
581  in.Put((byte*)s->c_str(), s->size());
582  in.MessageEnd();
583  *s = cipher;
584 #endif
585 }
586 
587 void dccl::Codec::decrypt(std::string* s, const std::string& nonce)
588 {
589 #if DCCL_HAS_CRYPTOPP
590  using namespace CryptoPP;
591 
592  std::string iv;
593  SHA256 hash;
594  StringSource unused(nonce, true, new HashFilter(hash, new StringSink(iv)));
595 
596  CTR_Mode<AES>::Decryption decryptor;
597  decryptor.SetKeyWithIV((byte*)crypto_key_.c_str(), crypto_key_.size(), (byte*)iv.c_str());
598 
599  std::string recovered;
600 
601  StreamTransformationFilter out(decryptor, new StringSink(recovered));
602  out.Put((byte*)s->c_str(), s->size());
603  out.MessageEnd();
604  *s = recovered;
605 #endif
606 }
607 
608 void dccl::Codec::load_library(const std::string& library_path)
609 {
610  void* handle = dlopen(library_path.c_str(), RTLD_LAZY);
611  if(handle)
612  dl_handles_.push_back(handle);
613  load_library(handle);
614 }
615 
616 void dccl::Codec::load_library(void* dl_handle)
617 {
618  if(!dl_handle)
619  throw(Exception("Null shared library handle passed to load_library"));
620 
621  // load any shared library codecs
622  void (*dccl_load_ptr)(dccl::Codec*);
623  dccl_load_ptr = (void (*)(dccl::Codec*)) dlsym(dl_handle, "dccl3_load");
624  if(dccl_load_ptr)
625  (*dccl_load_ptr)(this);
626 }
627 
628 void dccl::Codec::unload_library(void* dl_handle)
629 {
630  if(!dl_handle)
631  throw(Exception("Null shared library handle passed to unload_library"));
632 
633  // unload any shared library codecs
634  void (*dccl_unload_ptr)(dccl::Codec*);
635  dccl_unload_ptr = (void (*)(dccl::Codec*)) dlsym(dl_handle, "dccl3_unload");
636  if(dccl_unload_ptr)
637  (*dccl_unload_ptr)(this);
638 }
639 
640 
641 void dccl::Codec::set_crypto_passphrase(const std::string& passphrase, const std::set<unsigned>& do_not_encrypt_ids_ /*= std::set<unsigned>()*/)
642 {
643  if(!crypto_key_.empty())
644  crypto_key_.clear();
645  skip_crypto_ids_.clear();
646 
647 #if DCCL_HAS_CRYPTOPP
648  using namespace CryptoPP;
649 
650  SHA256 hash;
651  StringSource unused(passphrase, true, new HashFilter(hash, new StringSink(crypto_key_)));
652 
653  dlog.is(DEBUG1) && dlog << "Cryptography enabled with given passphrase" << std::endl;
654 #else
655  dlog.is(DEBUG1) && dlog << "Cryptography disabled because DCCL was compiled without support of Crypto++. Install Crypto++ and recompile to enable cryptography." << std::endl;
656 #endif
657 
658  skip_crypto_ids_ = do_not_encrypt_ids_;
659 }
660 
661 void dccl::Codec::info_all(std::ostream* param_os /*= 0 */) const
662 {
663  std::ostream* os = (param_os) ? param_os : &dlog;
664 
665  if(param_os || dlog.is(INFO))
666  {
667  std::string codec_str = "Dynamic Compact Control Language (DCCL) Codec";
668  std::string codec_guard = std::string((full_width-codec_str.size())/2, '|');
669  *os << codec_guard << " " << codec_str << " " << codec_guard << std::endl;
670 
671  *os << id2desc_.size() << " messages loaded.\n";
672  *os << "Field sizes are in bits unless otherwise noted." << std::endl;
673 
674  for(std::map<int32, const google::protobuf::Descriptor*>::const_iterator it = id2desc_.begin(), n = id2desc_.end(); it != n; ++it)
675  info(it->second, os, it->first);
676 
677 // *os << std::string(codec_str.size() + 2 + 2*codec_guard.size(), '|') << std::endl;
678  }
679 
680 }
The Dynamic CCL enCODer/DECoder. This is the main class you will use to load, encode and decode DCCL ...
Definition: codec.h:56
void hex_decode(const std::string &in, std::string *out)
Decodes a (little-endian) hexadecimal string to a byte string. Index 0 and 1 (first byte) of in are w...
Definition: binary.h:49
virtual ~Codec()
Destructor.
Definition: codec.cpp:80
void info(std::ostream *os=0, int user_id=-1) const
Writes a human readable summary (including field sizes) of the provided DCCL type to the stream provi...
Definition: codec.h:155
Codec(const std::string &dccl_id_codec=default_id_codec_name(), const std::string &library_path="")
Instantiate a Codec, optionally with a non-default identifier field codec.
Definition: codec.cpp:68
void load()
All messages must be explicited loaded and validated (size checks, option extensions checks...
Definition: codec.h:96
unsigned max_size()
Provides the encoded maximum size (in bytes) of msg.
Definition: codec.h:305
void encode(std::string *bytes, const google::protobuf::Message &msg, bool header_only=false, int user_id=-1)
Encodes a DCCL message.
Definition: codec.cpp:283
void unload_library(void *dl_handle)
Remove codecs and/or unload messages present in the given shared library handle.
Definition: codec.cpp:628
void load_library(void *dl_handle)
Add codecs and/or load messages present in the given shared library handle.
Definition: codec.cpp:616
static boost::shared_ptr< FieldCodecBase > find(const google::protobuf::FieldDescriptor *field, bool has_codec_group, const std::string &codec_group)
Find the codec for a given field. For embedded messages, prefers (dccl.field).codec (inside field) ov...
unsigned min_size()
Provides the encoded minimum size (in bytes) of msg.
Definition: codec.h:316
unsigned size(const google::protobuf::Message &msg, int user_id=-1)
Provides the encoded size (in bytes) of msg. This is useful if you need to know the size of a message...
Definition: codec.cpp:429
unsigned id() const
Gives the DCCL id (defined by the custom message option extension "(dccl.msg).id" in the ...
Definition: codec.h:174
void unload()
Unload a given message.
Definition: codec.h:103
void hex_encode(CharIterator begin, CharIterator end, std::string *out, bool upper_case=false)
Encodes a (little-endian) hexadecimal string from a byte string. Index 0 of begin is written to index...
Definition: binary.h:100
Dynamic Compact Control Language namespace.
void set_crypto_passphrase(const std::string &passphrase, const std::set< unsigned > &do_not_encrypt_ids_=std::set< unsigned >())
Set a passphrase to be used when encoded messages to encrypt them and to decrypt messages after decod...
Definition: codec.cpp:641
bool is(logger::Verbosity verbosity, logger::Group group=logger::GENERAL)
Indicates the verbosity of the Logger until the next std::flush or std::endl. The boolean return is u...
Definition: logger.h:145
Exception class for DCCL.
Definition: exception.h:31
std::string to_byte_string()
Returns the value of the Bitset to a byte string, where each character represents 8 bits of the Bitse...
Definition: bitset.h:310
CharIterator decode(CharIterator begin, CharIterator end, google::protobuf::Message *msg, bool header_only=false)
Decode a DCCL message when the type is known at compile time.
Definition: codec.h:439
void info_all(std::ostream *os=0) const
Writes a human readable summary (including field sizes) of all the loaded (validated) DCCL types...
Definition: codec.cpp:661
A variable size container of bits (subclassed from std::deque<bool>) with an optional hierarchy...
Definition: bitset.h:38