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)
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)
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  size_t head_byte_size = 0;
167 
168  if(!msg.IsInitialized() && !header_only)
169  throw(Exception("Message is not properly initialized. All `required` fields must be set."));
170 
171  if(!id2desc_.count(id(desc)))
172  throw(Exception("Message id " + boost::lexical_cast<std::string>(id(desc)) + " has not been loaded. Call load() before encoding this type."));
173 
174 
175 
176  boost::shared_ptr<FieldCodecBase> codec = FieldCodecManager::find(desc);
177  boost::shared_ptr<internal::FromProtoCppTypeBase> helper = internal::TypeHelper::find(desc);
178 
179  if(codec)
180  {
181  //fixed header
182  id_codec()->field_encode(&head_bits, id(desc), 0);
183 
184  internal::MessageStack msg_stack;
185  msg_stack.push(msg.GetDescriptor());
186  codec->base_encode(&head_bits, msg, HEAD);
187 
188  // given header of not even byte size (e.g. 01011), make even byte size (e.g. 00001011)
189  head_byte_size = ceil_bits2bytes(head_bits.size());
190  head_bits.resize(head_byte_size * BITS_IN_BYTE);
191 
192  if(header_only)
193  {
194  dlog.is(DEBUG2, ENCODE) && dlog << "as requested, skipping encoding and encrypting body." << std::endl;
195  }
196  else
197  {
198  codec->base_encode(&body_bits, msg, BODY);
199  }
200  }
201  else
202  {
203  throw(Exception("Failed to find (dccl.msg).codec `" + desc->options().GetExtension(dccl::msg).codec() + "`"));
204  }
205 
206  }
207  catch(std::exception& e)
208  {
209  std::stringstream ss;
210 
211  ss << "Message " << desc->full_name() << " failed to encode. Reason: " << e.what();
212 
213  dlog.is(DEBUG1, ENCODE) && dlog << ss.str() << std::endl;
214  throw(Exception(ss.str()));
215  }
216 }
217 
218 size_t dccl::Codec::encode(char* bytes, size_t max_len, const google::protobuf::Message& msg, bool header_only /* = false */)
219 {
220  const Descriptor* desc = msg.GetDescriptor();
221  Bitset head_bits;
222  Bitset body_bits;
223  encode_internal(msg, header_only, head_bits, body_bits);
224 
225  size_t head_byte_size = ceil_bits2bytes(head_bits.size());
226  if (max_len < head_byte_size)
227  {
228  throw std::length_error("max_len must be >= head_byte_size");
229  }
230  head_bits.to_byte_string(bytes, head_byte_size);
231 
232  dlog.is(DEBUG2, ENCODE) && dlog << "Head bytes (bits): " << head_byte_size << "(" << head_bits.size() << ")" << std::endl;
233  dlog.is(DEBUG3, ENCODE) && dlog << "Unencrypted Head (bin): " << head_bits << std::endl;
234  dlog.is(DEBUG3, ENCODE) && dlog << "Unencrypted Head (hex): " << hex_encode(bytes, bytes+head_byte_size) << std::endl;
235 
236  size_t body_byte_size = 0;
237  if (!header_only)
238  {
239  body_byte_size = ceil_bits2bytes(body_bits.size());
240  if (max_len < (head_byte_size + body_byte_size))
241  {
242  throw std::length_error("max_len must be >= (head_byte_size + body_byte_size)");
243  }
244  body_bits.to_byte_string(bytes+head_byte_size, max_len-head_byte_size);
245 
246  dlog.is(DEBUG3, ENCODE) && dlog << "Unencrypted Body (bin): " << body_bits << std::endl;
247  dlog.is(DEBUG3, ENCODE) && dlog << "Unencrypted Body (hex): " << hex_encode(bytes+head_byte_size, bytes+head_byte_size+body_byte_size) << std::endl;
248  dlog.is(DEBUG2, ENCODE) && dlog << "Body bytes (bits): " << body_byte_size << "(" << body_bits.size() << ")" << std::endl;
249 
250  if(!crypto_key_.empty() && !skip_crypto_ids_.count(id(desc))) {
251  std::string head_bytes(bytes, bytes+head_byte_size);
252  std::string body_bytes(bytes+head_byte_size, bytes+head_byte_size+body_byte_size);
253  encrypt(&body_bytes, head_bytes);
254  std::memcpy(bytes+head_byte_size, body_bytes.data(), body_bytes.size());
255  }
256 
257  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;
258  }
259 
260  dlog.is(DEBUG1, ENCODE) && dlog << "Successfully encoded message of type: " << desc->full_name() << std::endl;
261 
262  return head_byte_size + body_byte_size;
263 }
264 
265 
266 void dccl::Codec::encode(std::string* bytes, const google::protobuf::Message& msg, bool header_only /* = false */)
267 {
268  const Descriptor* desc = msg.GetDescriptor();
269  Bitset head_bits;
270  Bitset body_bits;
271  encode_internal(msg, header_only, head_bits, body_bits);
272 
273  std::string head_bytes = head_bits.to_byte_string();
274 
275  dlog.is(DEBUG2, ENCODE) && dlog << "Head bytes (bits): " << head_bytes.size() << "(" << head_bits.size() << ")" << std::endl;
276  dlog.is(DEBUG3, ENCODE) && dlog << "Unencrypted Head (bin): " << head_bits << std::endl;
277  dlog.is(DEBUG3, ENCODE) && dlog << "Unencrypted Head (hex): " << hex_encode(head_bytes) << std::endl;
278 
279  std::string body_bytes;
280  if (!header_only)
281  {
282  body_bytes = body_bits.to_byte_string();
283 
284  dlog.is(DEBUG3, ENCODE) && dlog << "Unencrypted Body (bin): " << body_bits << std::endl;
285  dlog.is(DEBUG3, ENCODE) && dlog << "Unencrypted Body (hex): " << hex_encode(body_bytes) << std::endl;
286  dlog.is(DEBUG2, ENCODE) && dlog << "Body bytes (bits): " << body_bytes.size() << "(" << body_bits.size() << ")" << std::endl;
287 
288  if(!crypto_key_.empty() && !skip_crypto_ids_.count(id(desc)))
289  encrypt(&body_bytes, head_bytes);
290 
291  dlog.is(logger::DEBUG3, logger::ENCODE) && dlog << "Encrypted Body (hex): " << hex_encode(body_bytes) << std::endl;
292  }
293 
294  dlog.is(DEBUG1, ENCODE) && dlog << "Successfully encoded message of type: " << desc->full_name() << std::endl;
295  *bytes += head_bytes + body_bytes;
296 }
297 
298 unsigned dccl::Codec::id(const std::string& bytes)
299 {
300  return id(bytes.begin(), bytes.end());
301 }
302 
303 
304 void dccl::Codec::decode(std::string* bytes, google::protobuf::Message* msg)
305 {
306  decode(*bytes, msg);
307  unsigned last_size = size(*msg);
308  bytes->erase(0, last_size);
309 }
310 
311 void dccl::Codec::decode(const std::string& bytes, google::protobuf::Message* msg, bool header_only /* = false */)
312 {
313  decode(bytes.begin(), bytes.end(), msg, header_only);
314 }
315 
316 // makes sure we can actual encode / decode a message of this descriptor given the loaded FieldCodecs
317 // checks all bounds on the message
318 void dccl::Codec::load(const google::protobuf::Descriptor* desc)
319 {
320  try
321  {
322  if(!desc->options().GetExtension(dccl::msg).has_id())
323  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\""));
324  if(!desc->options().GetExtension(dccl::msg).has_max_bytes())
325  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\""));
326 
327  if(!desc->options().GetExtension(dccl::msg).has_codec_version())
328  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;
329 
330  boost::shared_ptr<FieldCodecBase> codec = FieldCodecManager::find(desc);
331 
332  unsigned dccl_id = id(desc);
333  unsigned head_size_bits, body_size_bits;
334  codec->base_max_size(&head_size_bits, desc, HEAD);
335  codec->base_max_size(&body_size_bits, desc, BODY);
336 
337  unsigned id_bits = 0;
338  id_codec()->field_size(&id_bits, dccl_id, 0);
339  head_size_bits += id_bits;
340 
341  const unsigned byte_size = ceil_bits2bytes(head_size_bits) + ceil_bits2bytes(body_size_bits);
342 
343  if(byte_size > desc->options().GetExtension(dccl::msg).max_bytes())
344  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"));
345 
346  codec->base_validate(desc, HEAD);
347  codec->base_validate(desc, BODY);
348 
349  if(id2desc_.count(dccl_id) && desc != id2desc_.find(dccl_id)->second)
350  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)));
351  else
352  id2desc_.insert(std::make_pair(id(desc), desc));
353 
354  dlog.is(DEBUG1) && dlog << "Successfully validated message of type: " << desc->full_name() << std::endl;
355 
356  }
357  catch(Exception& e)
358  {
359  try
360  {
361  info(desc, &dlog);
362  }
363  catch(Exception& e)
364  { }
365 
366  dlog.is(DEBUG1) && dlog << "Message " << desc->full_name() << ": " << desc << " failed validation. Reason: "
367  << e.what() << "\n"
368  << "If possible, information about the Message are printed above. " << std::endl;
369 
370  throw;
371  }
372 }
373 
374 
375 void dccl::Codec::unload(const google::protobuf::Descriptor* desc)
376 {
377  unsigned dccl_id = id(desc);
378  if(id2desc_.count(dccl_id))
379  {
380  id2desc_.erase(dccl_id);
381  }
382  else
383  {
384  dlog.is(DEBUG1) && dlog << "Message " << desc->full_name() << ": is not loaded. Ignoring unload request." << std::endl;
385  return;
386  }
387 
388 }
389 
391 {
392  const Descriptor* desc = msg.GetDescriptor();
393 
394  boost::shared_ptr<FieldCodecBase> codec = FieldCodecManager::find(desc);
395 
396  unsigned dccl_id = id(desc);
397  unsigned head_size_bits;
398  codec->base_size(&head_size_bits, msg, HEAD);
399 
400  unsigned id_bits = 0;
401  id_codec()->field_size(&id_bits, dccl_id, 0);
402  head_size_bits += id_bits;
403 
404  unsigned body_size_bits;
405  codec->base_size(&body_size_bits, msg, BODY);
406 
407  const unsigned head_size_bytes = ceil_bits2bytes(head_size_bits);
408  const unsigned body_size_bytes = ceil_bits2bytes(body_size_bits);
409  return head_size_bytes + body_size_bytes;
410 }
411 
412 unsigned dccl::Codec::max_size(const google::protobuf::Descriptor* desc) const
413 {
414  boost::shared_ptr<FieldCodecBase> codec = FieldCodecManager::find(desc);
415 
416  unsigned head_size_bits;
417  codec->base_max_size(&head_size_bits, desc, HEAD);
418 
419  unsigned id_bits = 0;
420  id_codec()->field_max_size(&id_bits, 0);
421  head_size_bits += id_bits;
422 
423  unsigned body_size_bits;
424  codec->base_max_size(&body_size_bits, desc, BODY);
425 
426  const unsigned head_size_bytes = ceil_bits2bytes(head_size_bits);
427  const unsigned body_size_bytes = ceil_bits2bytes(body_size_bits);
428  return head_size_bytes + body_size_bytes;
429 }
430 
431 unsigned dccl::Codec::min_size(const google::protobuf::Descriptor* desc) const
432 {
433  boost::shared_ptr<FieldCodecBase> codec = FieldCodecManager::find(desc);
434 
435  unsigned head_size_bits;
436  codec->base_min_size(&head_size_bits, desc, HEAD);
437 
438  unsigned id_bits = 0;
439  id_codec()->field_min_size(&id_bits, 0);
440  head_size_bits += id_bits;
441 
442  unsigned body_size_bits;
443  codec->base_min_size(&body_size_bits, desc, BODY);
444 
445  const unsigned head_size_bytes = ceil_bits2bytes(head_size_bits);
446  const unsigned body_size_bytes = ceil_bits2bytes(body_size_bits);
447  return head_size_bytes + body_size_bytes;
448 }
449 
450 
451 
452 void dccl::Codec::info(const google::protobuf::Descriptor* desc, std::ostream* param_os /*= 0 */ ) const
453 {
454  std::ostream* os = (param_os) ? param_os : &dlog;
455 
456  if(param_os || dlog.is(INFO))
457  {
458  try
459  {
460  boost::shared_ptr<FieldCodecBase> codec = FieldCodecManager::find(desc);
461 
462  unsigned config_head_bit_size, body_bit_size;
463  codec->base_max_size(&config_head_bit_size, desc, HEAD);
464  codec->base_max_size(&body_bit_size, desc, BODY);
465 
466  unsigned dccl_id = id(desc);
467  unsigned id_bit_size = 0;
468  id_codec()->field_size(&id_bit_size, dccl_id, 0);
469 
470  const unsigned bit_size = id_bit_size + config_head_bit_size + body_bit_size;
471 
472 
473  const unsigned byte_size = ceil_bits2bytes(config_head_bit_size + id_bit_size) + ceil_bits2bytes(body_bit_size);
474 
475  const unsigned allowed_byte_size = desc->options().GetExtension(dccl::msg).max_bytes();
476  const unsigned allowed_bit_size = allowed_byte_size * BITS_IN_BYTE;
477 
478  std::string message_name = boost::lexical_cast<std::string>(dccl_id) + ": " + desc->full_name();
479  std::string guard = std::string((full_width-message_name.size())/2, '=');
480 
481  std::string bits_dccl_head_str = "dccl.id head";
482  std::string bits_user_head_str = "user head";
483  std::string bits_body_str = "body";
484  std::string bits_padding_str = "padding to full byte";
485 
486  const int bits_width = 40;
487  const int spaces = 8;
488  std::string indent = std::string(spaces,' ');
489 
490  *os << guard << " " << message_name << " " << guard << "\n"
491  << "Actual maximum size of message: " << byte_size << " bytes / "
492  << byte_size*BITS_IN_BYTE << " bits\n"
493  << indent << bits_dccl_head_str << std::setfill('.') << std::setw(bits_width-bits_dccl_head_str.size()) << id_bit_size << "\n"
494  << indent << bits_user_head_str << std::setfill('.') << std::setw(bits_width-bits_user_head_str.size()) << config_head_bit_size << "\n"
495  << indent << bits_body_str << std::setfill('.') << std::setw(bits_width-bits_body_str.size()) << body_bit_size << "\n"
496  << indent << bits_padding_str << std::setfill('.') << std::setw(bits_width-bits_padding_str.size()) << byte_size * BITS_IN_BYTE - bit_size << "\n"
497  << "Allowed maximum size of message: " << allowed_byte_size << " bytes / "
498  << allowed_bit_size << " bits\n";
499 
500  std::string header_str = "Header";
501  std::string header_guard = std::string((full_width-header_str.size())/2, '-');
502  *os << header_guard << " " << header_str << " " << header_guard << std::endl;
503  *os << bits_dccl_head_str << std::setfill('.') << std::setw(bits_width-bits_dccl_head_str.size()+spaces) << id_bit_size << " {" << id_codec()->name() << "}\n";
504  codec->base_info(os, desc, HEAD);
505 // *os << std::string(header_str.size() + 2 + 2*header_guard.size(), '-') << std::endl;
506 
507  std::string body_str = "Body";
508  std::string body_guard = std::string((full_width-body_str.size())/2, '-');
509  *os << body_guard << " " << body_str << " " << body_guard << std::endl;
510  codec->base_info(os, desc, BODY);
511 // *os << std::string(body_str.size() + 2 + 2*body_guard.size(), '-') << std::endl;
512 
513 // *os << std::string(desc->full_name().size() + 2 + 2*guard.size(), '=') << std::endl;
514  }
515  catch(Exception& e)
516  {
517  dlog.is(DEBUG1) && dlog << "Message " << desc->full_name() << " cannot provide information due to invalid configuration. Reason: " << e.what() << std::endl;
518  }
519 
520  os->flush();
521 
522  }
523 
524 }
525 
526 
527 void dccl::Codec::encrypt(std::string* s, const std::string& nonce /* message head */)
528 {
529 #if DCCL_HAS_CRYPTOPP
530  using namespace CryptoPP;
531 
532  std::string iv;
533  SHA256 hash;
534  StringSource unused(nonce, true, new HashFilter(hash, new StringSink(iv)));
535 
536  CTR_Mode<AES>::Encryption encryptor;
537  encryptor.SetKeyWithIV((byte*)crypto_key_.c_str(), crypto_key_.size(), (byte*)iv.c_str());
538 
539  std::string cipher;
540  StreamTransformationFilter in(encryptor, new StringSink(cipher));
541  in.Put((byte*)s->c_str(), s->size());
542  in.MessageEnd();
543  *s = cipher;
544 #endif
545 }
546 
547 void dccl::Codec::decrypt(std::string* s, const std::string& nonce)
548 {
549 #if DCCL_HAS_CRYPTOPP
550  using namespace CryptoPP;
551 
552  std::string iv;
553  SHA256 hash;
554  StringSource unused(nonce, true, new HashFilter(hash, new StringSink(iv)));
555 
556  CTR_Mode<AES>::Decryption decryptor;
557  decryptor.SetKeyWithIV((byte*)crypto_key_.c_str(), crypto_key_.size(), (byte*)iv.c_str());
558 
559  std::string recovered;
560 
561  StreamTransformationFilter out(decryptor, new StringSink(recovered));
562  out.Put((byte*)s->c_str(), s->size());
563  out.MessageEnd();
564  *s = recovered;
565 #endif
566 }
567 
568 void dccl::Codec::load_library(const std::string& library_path)
569 {
570  void* handle = dlopen(library_path.c_str(), RTLD_LAZY);
571  if(handle)
572  dl_handles_.push_back(handle);
573  load_library(handle);
574 }
575 
576 void dccl::Codec::load_library(void* dl_handle)
577 {
578  if(!dl_handle)
579  throw(Exception("Null shared library handle passed to load_library"));
580 
581  // load any shared library codecs
582  void (*dccl_load_ptr)(dccl::Codec*);
583  dccl_load_ptr = (void (*)(dccl::Codec*)) dlsym(dl_handle, "dccl3_load");
584  if(dccl_load_ptr)
585  (*dccl_load_ptr)(this);
586 }
587 
588 void dccl::Codec::unload_library(void* dl_handle)
589 {
590  if(!dl_handle)
591  throw(Exception("Null shared library handle passed to unload_library"));
592 
593  // unload any shared library codecs
594  void (*dccl_unload_ptr)(dccl::Codec*);
595  dccl_unload_ptr = (void (*)(dccl::Codec*)) dlsym(dl_handle, "dccl3_unload");
596  if(dccl_unload_ptr)
597  (*dccl_unload_ptr)(this);
598 }
599 
600 
601 void dccl::Codec::set_crypto_passphrase(const std::string& passphrase, const std::set<unsigned>& do_not_encrypt_ids_ /*= std::set<unsigned>()*/)
602 {
603  if(!crypto_key_.empty())
604  crypto_key_.clear();
605  skip_crypto_ids_.clear();
606 
607 #if DCCL_HAS_CRYPTOPP
608  using namespace CryptoPP;
609 
610  SHA256 hash;
611  StringSource unused(passphrase, true, new HashFilter(hash, new StringSink(crypto_key_)));
612 
613  dlog.is(DEBUG1) && dlog << "Cryptography enabled with given passphrase" << std::endl;
614 #else
615  dlog.is(DEBUG1) && dlog << "Cryptography disabled because DCCL was compiled without support of Crypto++. Install Crypto++ and recompile to enable cryptography." << std::endl;
616 #endif
617 
618  skip_crypto_ids_ = do_not_encrypt_ids_;
619 }
620 
621 void dccl::Codec::info_all(std::ostream* param_os /*= 0 */) const
622 {
623  std::ostream* os = (param_os) ? param_os : &dlog;
624 
625  if(param_os || dlog.is(INFO))
626  {
627  std::string codec_str = "Dynamic Compact Control Language (DCCL) Codec";
628  std::string codec_guard = std::string((full_width-codec_str.size())/2, '|');
629  *os << codec_guard << " " << codec_str << " " << codec_guard << std::endl;
630 
631  *os << id2desc_.size() << " messages loaded.\n";
632  *os << "Field sizes are in bits unless otherwise noted." << std::endl;
633 
634  for(std::map<int32, const google::protobuf::Descriptor*>::const_iterator it = id2desc_.begin(), n = id2desc_.end(); it != n; ++it)
635  info(it->second, os);
636 
637 // *os << std::string(codec_str.size() + 2 + 2*codec_guard.size(), '|') << std::endl;
638  }
639 
640 }
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:47
virtual ~Codec()
Destructor.
Definition: codec.cpp:79
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:274
void unload_library(void *dl_handle)
Remove codecs and/or unload messages present in the given shared library handle.
Definition: codec.cpp:588
void load_library(void *dl_handle)
Add codecs and/or load messages present in the given shared library handle.
Definition: codec.cpp:576
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 size(const google::protobuf::Message &msg)
Provides the encoded size (in bytes) of msg. This is useful if you need to know the size of a message...
Definition: codec.cpp:390
unsigned min_size()
Provides the encoded minimum size (in bytes) of msg.
Definition: codec.h:285
unsigned id() const
Gives the DCCL id (defined by the custom message option extension "(dccl.msg).id" in the ...
Definition: codec.h:156
void info(std::ostream *os=0) const
Writes a human readable summary (including field sizes) of the provided DCCL type to the stream provi...
Definition: codec.h:139
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:98
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:601
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:30
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:405
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:621
A variable size container of bits (subclassed from std::deque<bool>) with an optional hierarchy...
Definition: bitset.h:38
void encode(std::string *bytes, const google::protobuf::Message &msg, bool header_only=false)
Encodes a DCCL message.
Definition: codec.cpp:266