diff --git a/include/dns.h b/include/dns.h index 9848e0b..e3d8f6e 100644 --- a/include/dns.h +++ b/include/dns.h @@ -121,30 +121,117 @@ namespace Tins { /** * \brief Struct that represent DNS queries. */ - struct Query { - std::string name; - uint16_t type, qclass; + class Query { + public: + /** + * \brief Constructs a DNS query. + * + * \param nm The name of the domain being resolved. + * \param tp The query type. + * \param cl The query class. + */ + Query(const std::string &nm, QueryType tp, QueryClass cl) + : name_(nm), type_(tp), qclass_(cl) {} - Query(const std::string &nm, uint16_t t, uint16_t c) : - name(nm), type(t), qclass(c) {} - Query() {} + /** + * \brief Default constructs this Query. + */ + Query() : type_(), qclass_() {} + + /** + * \brief Setter for the name field. + * + * \param nm The name to be set. + */ + void dname(const std::string &nm) { + name_ = nm; + } + + /** + * \brief Setter for the query type field. + * + * \param tp The query type to be set. + */ + void type(QueryType tp) { + type_ = tp; + } + + /** + * \brief Setter for the query class field. + * + * \param cl The query class to be set. + */ + void query_class(QueryClass cl) { + qclass_ = cl; + } + + /** + * \brief Getter for the name field. + */ + const std::string &dname() const { return name_; } + + /** + * \brief Getter for the query type field. + */ + QueryType type() const { return type_; } + + /** + * \brief Getter for the query class field. + */ + QueryClass query_class() const { return qclass_; } + private: + std::string name_; + QueryType type_; + QueryClass qclass_; }; /** * \brief Struct that represent DNS resource records. */ - struct Resource { - std::string dname, addr; - uint16_t type, qclass; - uint32_t ttl; - + class Resource { + public: Resource(const std::string &nm, const std::string &ad, - uint16_t t, uint16_t c, uint32_t tt) : - dname(nm), addr(ad), type(t), qclass(c), ttl(tt) {} + uint16_t t, uint16_t c, uint32_t tt) + : dname_(nm), addr_(ad), type_(t), qclass_(c), ttl_(tt) {} + + Resource() : type_(), qclass_(), ttl_() {} + + /** + * \brief Getter for the dname field. + * + * This returns the domain name for which this record + * provides an answer. + */ + const std::string &dname() const { return dname_; } + + /** + * Getter for the type field. + */ + const std::string &data() const { return addr_; } + + /** + * Getter for the query type field. + */ + uint16_t type() const { return type_; } + + /** + * Getter for the query class field. + */ + uint16_t query_class() const { return qclass_; } + + /** + * Getter for the type field. + */ + uint32_t ttl() const { return ttl_; } + private: + std::string dname_, addr_; + uint16_t type_, qclass_; + uint32_t ttl_; }; typedef std::list queries_type; typedef std::list resources_type; + typedef IPv4Address address_type; /** * \brief Default constructor. @@ -154,9 +241,7 @@ namespace Tins { DNS(); /** - * \brief Constructor which creates a DNS object from a buffer - * and adds all identifiable PDUs found in the buffer as - * children of this one. + * \brief Constructor which creates a DNS object from a buffer. * \param buffer The buffer from which this PDU will be * constructed. * \param total_sz The total size of the buffer. @@ -253,28 +338,28 @@ namespace Tins { * * \return uint16_t containing the value of the questions field. */ - uint16_t questions() const { return Endian::be_to_host(dns.questions); } + uint16_t questions_count() const { return Endian::be_to_host(dns.questions); } /** * \brief Setter for the answers field. * * \return uint16_t containing the value of the answers field. */ - uint16_t answers() const { return Endian::be_to_host(dns.answers); } + uint16_t answers_count() const { return Endian::be_to_host(dns.answers); } /** * \brief Setter for the authority field. * * \return uint16_t containing the value of the authority field. */ - uint16_t authority() const { return Endian::be_to_host(dns.authority); } + uint16_t authority_count() const { return Endian::be_to_host(dns.authority); } /** * \brief Setter for the additional field. * * \return uint16_t containing the value of the additional field. */ - uint16_t additional() const { return Endian::be_to_host(dns.additional); } + uint16_t additional_count() const { return Endian::be_to_host(dns.additional); } /** * \brief Getter for the PDU's type. @@ -373,14 +458,6 @@ namespace Tins { void rcode(uint8_t new_rcode); // Methods - /** - * \brief Add a query to perform. - * - * \param name The name to be resolved. - * \param type The type of this query. - * \param qclass The class of this query. - */ - void add_query(const std::string &name, QueryType type, QueryClass qclass); /** * \brief Add a query to perform. @@ -398,8 +475,8 @@ namespace Tins { * \param ttl The time-to-live of this answer. * \param ip The ip address of the resolved name. */ - void add_answer(const std::string &name, QueryType type, QueryClass qclass, - uint32_t ttl, IPv4Address ip); + void add_answer(const std::string &name, + const DNSResourceRecord::info &info, address_type ip); /** * \brief Add a query response. @@ -410,8 +487,8 @@ namespace Tins { * \param ttl The time-to-live of this answer. * \param dname The domain of the resolved name. */ - void add_answer(const std::string &name, QueryType type, QueryClass qclass, - uint32_t ttl, const std::string &dname); + void add_answer(const std::string &name, + const DNSResourceRecord::info &info, const std::string &dname); /** * \brief Add a query response. @@ -423,8 +500,8 @@ namespace Tins { * \param data The data of this option. * \param sz The size of the data. */ - void add_answer(const std::string &name, QueryType type, QueryClass qclass, - uint32_t ttl, const uint8_t *data, uint32_t sz); + void add_answer(const std::string &name, + const DNSResourceRecord::info &info, const uint8_t *data, uint32_t sz); /** * \brief Add an authority record. @@ -436,8 +513,8 @@ namespace Tins { * \param data The data of this option. * \param sz The size of the data. */ - void add_authority(const std::string &name, QueryType type, QueryClass qclass, - uint32_t ttl, const uint8_t *data, uint32_t sz); + void add_authority(const std::string &name, + const DNSResourceRecord::info &info, const uint8_t *data, uint32_t sz); /** * \brief Add an additional record. @@ -448,8 +525,8 @@ namespace Tins { * \param ttl The time-to-live of this record. * \param ip The ip address of the resolved name. */ - void add_additional(const std::string &name, QueryType type, QueryClass qclass, - uint32_t ttl, uint32_t ip); + void add_additional(const std::string &name, + const DNSResourceRecord::info &info, uint32_t ip); /** @@ -457,14 +534,14 @@ namespace Tins { * \return std::list containing the queries in this * record. */ - queries_type dns_queries() const; + queries_type queries() const; /** * \brief Getter for this PDU's DNS answers * \return std::list containing the answers in this * record. */ - resources_type dns_answers(); + resources_type answers() const; /** * \sa PDU::clone @@ -472,6 +549,17 @@ namespace Tins { DNS *clone() const { return new DNS(*this); } + + /** + * Helper function to create a resource record information + * + * \param type The type of the query. + * \param qclass The class of the query. + * \param ttl The time-to-live of the query. + */ + static DNSResourceRecord::info make_info(QueryType type, QueryClass qclass, uint32_t ttl) { + return DNSResourceRecord::info((uint16_t)type, (uint16_t)qclass, ttl); + } private: struct dnshdr { uint16_t id; @@ -516,23 +604,23 @@ namespace Tins { void unparse_domain_name(const std::string &dn, std::string &out) const; void write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *parent); uint8_t *serialize_list(const ResourcesType &lst, uint8_t *buffer) const; - void compose_name(const uint8_t *ptr, uint32_t sz, std::string &out); - void convert_resources(const ResourcesType &lst, std::list &res); - DNSResourceRecord make_record(const std::string &name, QueryType type, QueryClass qclass, uint32_t ttl, uint32_t ip); - DNSResourceRecord make_record(const std::string &name, QueryType type, QueryClass qclass, uint32_t ttl, const std::string &dname); - DNSResourceRecord make_record(const std::string &name, QueryType type, QueryClass qclass, uint32_t ttl, const uint8_t *ptr, uint32_t len); - void add_suffix(uint32_t index, const uint8_t *data, uint32_t sz); - uint32_t build_suffix_map(uint32_t index, const ResourcesType &lst); - uint32_t build_suffix_map(uint32_t index, const QueriesType &lst); - void build_suffix_map(); - bool contains_dname(uint16_t type); + void compose_name(const uint8_t *ptr, uint32_t sz, std::string &out) const; + void convert_resources(const ResourcesType &lst, std::list &res) const; + DNSResourceRecord make_record(const std::string &name, const DNSResourceRecord::info &info, uint32_t ip); + DNSResourceRecord make_record(const std::string &name, const DNSResourceRecord::info &info, const std::string &dname); + DNSResourceRecord make_record(const std::string &name, const DNSResourceRecord::info &info, const uint8_t *ptr, uint32_t len); + void add_suffix(uint32_t index, const uint8_t *data, uint32_t sz) const; + uint32_t build_suffix_map(uint32_t index, const ResourcesType &lst) const; + uint32_t build_suffix_map(uint32_t index, const QueriesType &lst) const; + void build_suffix_map() const ; + static bool contains_dname(uint16_t type); dnshdr dns; uint32_t extra_size; - std::list queries; + std::list queries_; ResourcesType ans, arity, addit; - SuffixMap suffixes; - SuffixIndices suffix_indices; + mutable SuffixMap suffixes; + mutable SuffixIndices suffix_indices; }; }; diff --git a/include/dns_record.h b/include/dns_record.h index 414c854..c73c427 100644 --- a/include/dns_record.h +++ b/include/dns_record.h @@ -37,9 +37,14 @@ public: /** * \brief The type used to store resource records' information. */ - struct Info { + struct info { uint16_t type, qclass; uint32_t ttl; + + info(uint16_t tp, uint16_t qc, uint32_t tm) + : type(tp), qclass(qc), ttl(tm) { } + + info() : type(), qclass(), ttl() {} } __attribute__((packed)); /** @@ -143,14 +148,14 @@ public: /** * \brief Returns a reference to the info field. */ - Info &info() { + info &information() { return info_; } /** * \brief Returns a const reference to the info field. */ - const Info &info() const { + const info &information() const { return info_; } @@ -165,7 +170,7 @@ private: DNSRRImpl *clone_impl() const; size_t impl_size() const; - Info info_; + info info_; std::vector data; DNSRRImpl *impl; }; diff --git a/src/dns.cpp b/src/dns.cpp index 8dd0939..67d736e 100644 --- a/src/dns.cpp +++ b/src/dns.cpp @@ -40,7 +40,7 @@ DNS::DNS(const uint8_t *buffer, uint32_t total_sz) : extra_size(0) { throw std::runtime_error("Not enough size for a DNS header in the buffer."); std::memcpy(&dns, buffer, sizeof(dnshdr)); const uint8_t *end(buffer + total_sz); - uint16_t nquestions(questions()); + uint16_t nquestions(questions_count()); buffer += sizeof(dnshdr); total_sz -= sizeof(dnshdr); for(uint16_t i(0); i < nquestions; ++i) { @@ -50,19 +50,19 @@ DNS::DNS(const uint8_t *buffer, uint32_t total_sz) : extra_size(0) { Query query; if((ptr + (sizeof(uint16_t) * 2)) >= end) throw std::runtime_error("Not enough size for a given query."); - query.name = string(buffer, ptr); + query.dname(string(buffer, ptr)); ptr++; const uint16_t *opt_ptr = reinterpret_cast(ptr); - query.type = *(opt_ptr++); - query.qclass = *(opt_ptr++); - queries.push_back(query); + query.type((QueryType)*(opt_ptr++)); + query.query_class((QueryClass)*(opt_ptr++)); + queries_.push_back(query); total_sz -= reinterpret_cast(opt_ptr) - buffer; extra_size += reinterpret_cast(opt_ptr) - buffer; buffer = reinterpret_cast(opt_ptr); } - buffer = build_resource_list(ans, buffer, total_sz, answers()); - buffer = build_resource_list(arity, buffer, total_sz, authority()); - build_resource_list(addit, buffer, total_sz, additional()); + buffer = build_resource_list(ans, buffer, total_sz, answers_count()); + buffer = build_resource_list(arity, buffer, total_sz, authority_count()); + build_resource_list(addit, buffer, total_sz, additional_count()); } const uint8_t *DNS::build_resource_list(ResourcesType &lst, const uint8_t *ptr, uint32_t &sz, uint16_t nrecs) { @@ -135,68 +135,73 @@ bool DNS::contains_dname(uint16_t type) { type == PTR || type == NS; } -void DNS::add_query(const string &name, QueryType type, QueryClass qclass) { +void DNS::add_query(const Query &query) { string new_str; - parse_domain_name(name, new_str); + parse_domain_name(query.dname(), new_str); - queries.push_back( - Query(new_str, - Endian::host_to_be(type), - Endian::host_to_be(qclass)) + queries_.push_back( + Query( + new_str, + (QueryType)Endian::host_to_be(query.type()), + (QueryClass)Endian::host_to_be(query.query_class()) + ) ); extra_size += new_str.size() + 1 + (sizeof(uint16_t) << 1); - dns.questions = Endian::host_to_be(queries.size()); + dns.questions = Endian::host_to_be(queries_.size()); } -void DNS::add_query(const Query &query) { - add_query( - query.name, - static_cast(query.type), - static_cast(query.qclass) - ); -} - -void DNS::add_answer(const string &name, QueryType type, QueryClass qclass, uint32_t ttl, IPv4Address ip) { - ans.push_back(make_record(name, type, qclass, ttl, Endian::host_to_be((uint32_t)ip))); +void DNS::add_answer(const string &name, const DNSResourceRecord::info &info, + address_type ip) +{ + ans.push_back(make_record(name, info, Endian::host_to_be((uint32_t)ip))); dns.answers = Endian::host_to_be(ans.size()); } -void DNS::add_answer(const std::string &name, QueryType type, QueryClass qclass, - uint32_t ttl, const std::string &dname) { +void DNS::add_answer(const std::string &name, const DNSResourceRecord::info &info, + const std::string &dname) +{ string new_str; parse_domain_name(dname, new_str); - DNSResourceRecord res = make_record(name, type, qclass, ttl, new_str); + DNSResourceRecord res = make_record(name, info, new_str); ans.push_back(res); dns.answers = Endian::host_to_be(ans.size()); } -void DNS::add_answer(const std::string &name, QueryType type, QueryClass qclass, - uint32_t ttl, const uint8_t *data, uint32_t sz) { - ans.push_back(make_record(name, type, qclass, ttl, data, sz)); +void DNS::add_answer(const std::string &name, const DNSResourceRecord::info &info, + const uint8_t *data, uint32_t sz) +{ + ans.push_back(make_record(name, info, data, sz)); dns.answers = Endian::host_to_be(ans.size()); } -void DNS::add_authority(const string &name, QueryType type, - QueryClass qclass, uint32_t ttl, const uint8_t *data, uint32_t sz) { - arity.push_back(make_record(name, type, qclass, ttl, data, sz)); +void DNS::add_authority(const string &name, const DNSResourceRecord::info &info, + const uint8_t *data, uint32_t sz) +{ + arity.push_back(make_record(name, info, data, sz)); dns.authority = Endian::host_to_be(arity.size()); } -void DNS::add_additional(const string &name, QueryType type, QueryClass qclass, uint32_t ttl, uint32_t ip) { - addit.push_back(make_record(name, type, qclass, ttl, ip)); +void DNS::add_additional(const string &name, const DNSResourceRecord::info &info, +uint32_t ip) +{ + addit.push_back(make_record(name, info, ip)); dns.additional = Endian::host_to_be(addit.size()); } -DNSResourceRecord DNS::make_record(const std::string &name, QueryType type, QueryClass qclass, uint32_t ttl, uint32_t ip) { +DNSResourceRecord DNS::make_record(const std::string &name, const DNSResourceRecord::info &info, uint32_t ip) { ip = Endian::host_to_be(ip); - return make_record(name, type, qclass, ttl, reinterpret_cast(&ip), sizeof(ip)); + return make_record(name, info, reinterpret_cast(&ip), sizeof(ip)); } -DNSResourceRecord DNS::make_record(const std::string &name, QueryType type, QueryClass qclass, uint32_t ttl, const std::string &dname) { - return make_record(name, type, qclass, ttl, reinterpret_cast(dname.c_str()), dname.size() + 1); +DNSResourceRecord DNS::make_record(const std::string &name, + const DNSResourceRecord::info &info, const std::string &dname) +{ + return make_record(name, info, reinterpret_cast(dname.c_str()), dname.size() + 1); } -DNSResourceRecord DNS::make_record(const std::string &name, QueryType type, QueryClass qclass, uint32_t ttl, const uint8_t *ptr, uint32_t len) { +DNSResourceRecord DNS::make_record(const std::string &name, + const DNSResourceRecord::info &info, const uint8_t *ptr, uint32_t len) +{ string nm; parse_domain_name(name, nm); uint16_t index = find_domain_name(nm); @@ -205,19 +210,19 @@ DNSResourceRecord DNS::make_record(const std::string &name, QueryType type, Quer res = make_offseted_record(Endian::host_to_be(index), ptr, len); else res = make_named_record(nm, ptr, len); - res.info().type = Endian::host_to_be(type); - res.info().qclass = Endian::host_to_be(qclass); - res.info().ttl = Endian::host_to_be(ttl); + res.information().type = Endian::host_to_be(info.type); + res.information().qclass = Endian::host_to_be(info.qclass); + res.information().ttl = Endian::host_to_be(info.ttl); extra_size += res.size(); return res; } uint32_t DNS::find_domain_name(const std::string &dname) { uint16_t index(sizeof(dnshdr)); - list::const_iterator it(queries.begin()); - for(; it != queries.end() && it->name != dname; ++it) - index += it->name.size() + 1 + (sizeof(uint16_t) << 1); - if(it != queries.end() || + list::const_iterator it(queries_.begin()); + for(; it != queries_.end() && it->dname() != dname; ++it) + index += it->dname().size() + 1 + (sizeof(uint16_t) << 1); + if(it != queries_.end() || find_domain_name(dname, ans, index) || find_domain_name(dname, arity, index) || find_domain_name(dname, addit, index)) @@ -271,12 +276,13 @@ void DNS::write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *par assert(total_sz >= sizeof(dns) + extra_size); std::memcpy(buffer, &dns, sizeof(dns)); buffer += sizeof(dns); - for(list::const_iterator it(queries.begin()); it != queries.end(); ++it) { - std::memcpy(buffer, it->name.c_str(), it->name.size() + 1); - buffer += it->name.size() + 1; - *((uint16_t*)buffer) = it->type; + for(list::const_iterator it(queries_.begin()); it != queries_.end(); ++it) { + std::copy(it->dname().begin(), it->dname().end(), buffer); + buffer += it->dname().size(); + *buffer++ = 0; + *((uint16_t*)buffer) = it->type(); buffer += sizeof(uint16_t); - *((uint16_t*)buffer) = it->qclass; + *((uint16_t*)buffer) = it->query_class(); buffer += sizeof(uint16_t); } buffer = serialize_list(ans, buffer); @@ -290,7 +296,7 @@ uint8_t *DNS::serialize_list(const ResourcesType &lst, uint8_t *buffer) const { return buffer; } -void DNS::add_suffix(uint32_t index, const uint8_t *data, uint32_t sz) { +void DNS::add_suffix(uint32_t index, const uint8_t *data, uint32_t sz) const { uint32_t i(0), suff_sz(data[0]); SuffixMap::iterator it; while((i + suff_sz + 1 <= sz || (suff_sz == 0xc0 && i + 1 < sz)) && suff_sz) { @@ -309,7 +315,7 @@ void DNS::add_suffix(uint32_t index, const uint8_t *data, uint32_t sz) { } } -uint32_t DNS::build_suffix_map(uint32_t index, const ResourcesType &lst) { +uint32_t DNS::build_suffix_map(uint32_t index, const ResourcesType &lst) const { const string *str; for(ResourcesType::const_iterator it(lst.begin()); it != lst.end(); ++it) { str = it->dname(); @@ -319,38 +325,38 @@ uint32_t DNS::build_suffix_map(uint32_t index, const ResourcesType &lst) { } else index += sizeof(uint16_t); - index += sizeof(DNSResourceRecord::Info) + sizeof(uint16_t); + index += sizeof(DNSResourceRecord::info) + sizeof(uint16_t); uint32_t sz(it->data_size()); const uint8_t *ptr = it->data_ptr(); - if(Endian::be_to_host(it->info().type) == MX) { + if(Endian::be_to_host(it->information().type) == MX) { ptr += 2; sz -= 2; index += 2; } - if(contains_dname(it->info().type)) + if(contains_dname(it->information().type)) add_suffix(index, ptr, sz); index += sz; } return index; } -uint32_t DNS::build_suffix_map(uint32_t index, const list &lst) { +uint32_t DNS::build_suffix_map(uint32_t index, const list &lst) const { for(list::const_iterator it(lst.begin()); it != lst.end(); ++it) { - add_suffix(index, (uint8_t*)it->name.c_str(), it->name.size()); - index += it->name.size() + 1 + (sizeof(uint16_t) << 1); + add_suffix(index, (uint8_t*)it->dname().c_str(), it->dname().size()); + index += it->dname().size() + 1 + (sizeof(uint16_t) << 1); } return index; } -void DNS::build_suffix_map() { +void DNS::build_suffix_map() const { uint32_t index(sizeof(dnshdr)); - index = build_suffix_map(index, queries); + index = build_suffix_map(index, queries_); index = build_suffix_map(index, ans); index = build_suffix_map(index, arity); build_suffix_map(index, addit); } -void DNS::compose_name(const uint8_t *ptr, uint32_t sz, std::string &out) { +void DNS::compose_name(const uint8_t *ptr, uint32_t sz, std::string &out) const { uint32_t i(0); while(i < sz) { if(i && ptr[i]) @@ -360,7 +366,6 @@ void DNS::compose_name(const uint8_t *ptr, uint32_t sz, std::string &out) { index &= 0x3fff; SuffixMap::iterator it(suffixes.find(index)); SuffixIndices::iterator suff_it(suffix_indices.find(index)); - //assert(it != suffixes.end() && suff_it == suffix_indices.end()); if(it == suffixes.end() || suff_it == suffix_indices.end()) throw std::runtime_error("Malformed DNS packet"); bool first(true); @@ -391,7 +396,7 @@ void DNS::compose_name(const uint8_t *ptr, uint32_t sz, std::string &out) { } } -void DNS::convert_resources(const ResourcesType &lst, std::list &res) { +void DNS::convert_resources(const ResourcesType &lst, std::list &res) const { if(!suffixes.size()) build_suffix_map(); const string *str_ptr; @@ -410,31 +415,38 @@ void DNS::convert_resources(const ResourcesType &lst, std::list &res) if(sz == 4) addr = IPv4Address(*(uint32_t*)ptr).to_string(); else { - if(Endian::be_to_host(it->info().type) == MX) { + if(Endian::be_to_host(it->information().type) == MX) { ptr += 2; sz -= 2; } compose_name(ptr, sz, addr); } res.push_back( - Resource(dname, addr, Endian::be_to_host(it->info().type), - Endian::host_to_be(it->info().qclass), Endian::be_to_host(it->info().ttl) + Resource(dname, addr, Endian::be_to_host(it->information().type), + Endian::host_to_be(it->information().qclass), + Endian::be_to_host(it->information().ttl) ) ); } } -DNS::queries_type DNS::dns_queries() const { +DNS::queries_type DNS::queries() const { queries_type output; - for(std::list::const_iterator it(queries.begin()); it != queries.end(); ++it) { + for(std::list::const_iterator it(queries_.begin()); it != queries_.end(); ++it) { string dn; - unparse_domain_name(it->name, dn); - output.push_back(Query(dn, Endian::be_to_host(it->type), Endian::be_to_host(it->qclass))); + unparse_domain_name(it->dname(), dn); + output.push_back( + Query( + dn, + (QueryType)Endian::be_to_host(it->type()), + (QueryClass)Endian::be_to_host(it->query_class()) + ) + ); } return output; } -DNS::resources_type DNS::dns_answers() { +DNS::resources_type DNS::answers() const { resources_type res; convert_resources(ans, res); return res; diff --git a/src/dns_record.cpp b/src/dns_record.cpp index 84550a6..c7b9ef6 100644 --- a/src/dns_record.cpp +++ b/src/dns_record.cpp @@ -104,7 +104,7 @@ uint32_t DNSResourceRecord::write(uint8_t *buffer) const { buffer += sz; std::memcpy(buffer, &info_, sizeof(info_)); buffer += sizeof(info_); - *((uint16_t*)buffer) = Endian::host_to_be(data.size()); + *((uint16_t*)buffer) = Endian::host_to_be(data.size()); buffer += sizeof(uint16_t); std::copy(data.begin(), data.end(), buffer); return sz + sizeof(info_) + sizeof(uint16_t) + data.size();