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