mirror of
https://github.com/mfontanini/libtins
synced 2026-01-23 02:35:57 +01:00
DNS is working. Constructor from buffer is not implemented yet.
This commit is contained in:
320
include/dns.h
320
include/dns.h
@@ -23,6 +23,8 @@
|
|||||||
#define __DNS_H
|
#define __DNS_H
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <list>
|
||||||
|
#include <string>
|
||||||
#include "pdu.h"
|
#include "pdu.h"
|
||||||
|
|
||||||
namespace Tins {
|
namespace Tins {
|
||||||
@@ -33,6 +35,209 @@ namespace Tins {
|
|||||||
RESPONSE = 1
|
RESPONSE = 1
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Query types enum.
|
||||||
|
*/
|
||||||
|
enum QueryType {
|
||||||
|
A = 1,
|
||||||
|
NS,
|
||||||
|
MD,
|
||||||
|
MF,
|
||||||
|
CNAME,
|
||||||
|
SOA,
|
||||||
|
MB,
|
||||||
|
MG,
|
||||||
|
MR,
|
||||||
|
NULL_R,
|
||||||
|
WKS,
|
||||||
|
PTR,
|
||||||
|
HINFO,
|
||||||
|
MINFO,
|
||||||
|
MX,
|
||||||
|
TXT,
|
||||||
|
RP,
|
||||||
|
AFSDB,
|
||||||
|
X25,
|
||||||
|
ISDN,
|
||||||
|
RT,
|
||||||
|
NSAP,
|
||||||
|
NSAP_PTR,
|
||||||
|
SIG,
|
||||||
|
KEY,
|
||||||
|
PX,
|
||||||
|
GPOS,
|
||||||
|
AAAA,
|
||||||
|
LOC,
|
||||||
|
NXT,
|
||||||
|
EID,
|
||||||
|
NIMLOC,
|
||||||
|
SRV,
|
||||||
|
ATMA,
|
||||||
|
NAPTR,
|
||||||
|
KX,
|
||||||
|
CERT,
|
||||||
|
A6,
|
||||||
|
DNAM,
|
||||||
|
SINK,
|
||||||
|
OPT,
|
||||||
|
APL,
|
||||||
|
DS,
|
||||||
|
SSHFP,
|
||||||
|
IPSECKEY,
|
||||||
|
RRSIG,
|
||||||
|
NSEC,
|
||||||
|
DNSKEY,
|
||||||
|
DHCID,
|
||||||
|
NSEC3,
|
||||||
|
NSEC3PARAM
|
||||||
|
};
|
||||||
|
|
||||||
|
enum QueryClass {
|
||||||
|
IN = 1,
|
||||||
|
CH = 3,
|
||||||
|
HS = 4,
|
||||||
|
ANY = 255
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Default constructor.
|
||||||
|
*
|
||||||
|
* This constructor initializes every field to 0.
|
||||||
|
*/
|
||||||
|
DNS();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Destructor.
|
||||||
|
*/
|
||||||
|
~DNS();
|
||||||
|
|
||||||
|
// Getters
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Setter for the id field.
|
||||||
|
*
|
||||||
|
* \return uint16_t containing the value of the id field.
|
||||||
|
*/
|
||||||
|
uint16_t id() { return dns.id; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Setter for the query response field.
|
||||||
|
*
|
||||||
|
* \return QRType containing the value of the query response
|
||||||
|
* field.
|
||||||
|
*/
|
||||||
|
QRType type() { return static_cast<QRType>(dns.qr); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Setter for the opcode field.
|
||||||
|
*
|
||||||
|
* \return uint8_t containing the value of the opcode field.
|
||||||
|
*/
|
||||||
|
uint8_t opcode() { return dns.opcode; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Setter for the authoritative answer field.
|
||||||
|
*
|
||||||
|
* \return uint8_t containing the value of the authoritative
|
||||||
|
* answer field.
|
||||||
|
*/
|
||||||
|
uint8_t authoritative_answer() { return dns.aa; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Setter for the truncated field.
|
||||||
|
*
|
||||||
|
* \return uint8_t containing the value of the truncated field.
|
||||||
|
*/
|
||||||
|
uint8_t truncated() { return dns.tc; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Setter for the recursion desired field.
|
||||||
|
*
|
||||||
|
* \return uint8_t containing the value of the recursion
|
||||||
|
* desired field.
|
||||||
|
*/
|
||||||
|
uint8_t recursion_desired() { return dns.rd; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Setter for the recursion available field.
|
||||||
|
*
|
||||||
|
* \return uint8_t containing the value of the recursion
|
||||||
|
* available field.
|
||||||
|
*/
|
||||||
|
uint8_t recursion_available() { return dns.ra; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Setter for the z desired field.
|
||||||
|
*
|
||||||
|
* \return uint8_t containing the value of the z field.
|
||||||
|
*/
|
||||||
|
uint8_t z() { return dns.z; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Setter for the authenticated data field.
|
||||||
|
*
|
||||||
|
* \return uint8_t containing the value of the authenticated
|
||||||
|
* data field.
|
||||||
|
*/
|
||||||
|
uint8_t authenticated_data() { return dns.ad; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Setter for the checking disabled field.
|
||||||
|
*
|
||||||
|
* \return uint8_t containing the value of the checking
|
||||||
|
* disabled field.
|
||||||
|
*/
|
||||||
|
uint8_t checking_disabled() { return dns.cd; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Setter for the rcode field.
|
||||||
|
*
|
||||||
|
* \return uint8_t containing the value of the rcode field.
|
||||||
|
*/
|
||||||
|
uint8_t rcode() { return dns.rcode; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Setter for the questions field.
|
||||||
|
*
|
||||||
|
* \return uint16_t containing the value of the questions field.
|
||||||
|
*/
|
||||||
|
uint16_t questions() { return dns.questions; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Setter for the answers field.
|
||||||
|
*
|
||||||
|
* \return uint16_t containing the value of the answers field.
|
||||||
|
*/
|
||||||
|
uint16_t answers() { return dns.answers; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Setter for the authority field.
|
||||||
|
*
|
||||||
|
* \return uint16_t containing the value of the authority field.
|
||||||
|
*/
|
||||||
|
uint16_t authority() { return dns.authority; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Setter for the additional field.
|
||||||
|
*
|
||||||
|
* \return uint16_t containing the value of the additional field.
|
||||||
|
*/
|
||||||
|
uint16_t additional() { return dns.additional; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Getter for the PDU's type.
|
||||||
|
*
|
||||||
|
* \return Returns the PDUType corresponding to the PDU.
|
||||||
|
*/
|
||||||
|
PDUType pdu_type() const { return PDU::DNS; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief The header's size
|
||||||
|
*/
|
||||||
|
uint32_t header_size() const;
|
||||||
|
|
||||||
|
// Setters
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Setter for the id field.
|
* \brief Setter for the id field.
|
||||||
*
|
*
|
||||||
@@ -114,25 +319,122 @@ namespace Tins {
|
|||||||
* \param new_rcode The new rcode value to be set.
|
* \param new_rcode The new rcode value to be set.
|
||||||
*/
|
*/
|
||||||
void rcode(uint8_t new_rcode);
|
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 response.
|
||||||
|
*
|
||||||
|
* \param name The resolved name.
|
||||||
|
* \param type The type of this answer.
|
||||||
|
* \param qclass The class of this answer.
|
||||||
|
* \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, uint32_t ip);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Add an authority record.
|
||||||
|
*
|
||||||
|
* \param name The resolved name.
|
||||||
|
* \param type The type of this record.
|
||||||
|
* \param qclass The class of this record.
|
||||||
|
* \param ttl The time-to-live of this record.
|
||||||
|
* \param ip The ip address of the resolved name.
|
||||||
|
*/
|
||||||
|
void add_authority(const std::string &name, QueryType type, QueryClass qclass,
|
||||||
|
uint32_t ttl, uint32_t ip);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Add an additional record.
|
||||||
|
*
|
||||||
|
* \param name The resolved name.
|
||||||
|
* \param type The type of this record.
|
||||||
|
* \param qclass The class of this record.
|
||||||
|
* \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);
|
||||||
private:
|
private:
|
||||||
struct dnshdr {
|
struct dnshdr {
|
||||||
uint16_t id;
|
uint16_t id;
|
||||||
uint16_t
|
uint16_t
|
||||||
qr:1,
|
|
||||||
opcode:4,
|
|
||||||
aa:1,
|
|
||||||
tc:1,
|
|
||||||
rd:1,
|
rd:1,
|
||||||
ra:1,
|
tc:1,
|
||||||
z:1,
|
aa:1,
|
||||||
ad:1,
|
opcode:4,
|
||||||
|
qr:1,
|
||||||
|
rcode:4,
|
||||||
cd:1,
|
cd:1,
|
||||||
rcode:4;
|
ad:1,
|
||||||
uint32_t questions, answers,
|
z:1,
|
||||||
|
ra:1;
|
||||||
|
uint16_t questions, answers,
|
||||||
authority, additional;
|
authority, additional;
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
struct Query {
|
||||||
|
std::string name;
|
||||||
|
uint16_t type, qclass;
|
||||||
|
|
||||||
|
Query(const std::string &nm, uint16_t t, uint16_t c) :
|
||||||
|
name(nm), type(t), qclass(c) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ResourceRecord {
|
||||||
|
struct {
|
||||||
|
uint16_t type, qclass;
|
||||||
|
uint32_t ttl;
|
||||||
|
uint16_t dlen;
|
||||||
|
uint32_t data;
|
||||||
|
} __attribute__((packed)) info;
|
||||||
|
|
||||||
|
virtual ~ResourceRecord() {}
|
||||||
|
uint32_t write(uint8_t *buffer) const;
|
||||||
|
virtual uint32_t do_write(uint8_t *buffer) const = 0;
|
||||||
|
virtual uint32_t size() const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct OffsetedResourceRecord : public ResourceRecord {
|
||||||
|
uint16_t offset;
|
||||||
|
|
||||||
|
OffsetedResourceRecord(uint16_t off) : offset(off | 0xc0) {}
|
||||||
|
|
||||||
|
uint32_t do_write(uint8_t *buffer) const;
|
||||||
|
uint32_t size() const { return sizeof(info) + sizeof(offset); }
|
||||||
|
};
|
||||||
|
|
||||||
|
struct NamedResourceRecord : public ResourceRecord {
|
||||||
|
std::string name;
|
||||||
|
|
||||||
|
NamedResourceRecord(const std::string &nm) : name(nm) {}
|
||||||
|
|
||||||
|
uint32_t do_write(uint8_t *buffer) const;
|
||||||
|
uint32_t size() const { return sizeof(info) + name.size() + 1; }
|
||||||
|
};
|
||||||
|
|
||||||
|
uint32_t find_domain_name(const std::string &dname);
|
||||||
|
bool find_domain_name(const std::string &dname, const std::list<ResourceRecord*> &lst, uint16_t &out);
|
||||||
|
void parse_domain_name(const std::string &dn, std::string &out);
|
||||||
|
void write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *parent);
|
||||||
|
void free_list(std::list<ResourceRecord*> &lst);
|
||||||
|
uint8_t *serialize_list(const std::list<ResourceRecord*> &lst, uint8_t *buffer) const;
|
||||||
|
ResourceRecord *make_record(const std::string &name, QueryType type, QueryClass qclass, uint32_t ttl, uint32_t ip);
|
||||||
|
|
||||||
dnshdr dns;
|
dnshdr dns;
|
||||||
|
uint32_t extra_size;
|
||||||
|
std::list<Query> queries;
|
||||||
|
std::list<ResourceRecord*> ans, arity, addit;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -87,7 +87,8 @@ namespace Tins {
|
|||||||
DHCP,
|
DHCP,
|
||||||
EAPOL,
|
EAPOL,
|
||||||
RC4EAPOL,
|
RC4EAPOL,
|
||||||
RSNEAPOL
|
RSNEAPOL,
|
||||||
|
DNS
|
||||||
};
|
};
|
||||||
|
|
||||||
/** \brief PDU constructor
|
/** \brief PDU constructor
|
||||||
|
|||||||
154
src/dns.cpp
154
src/dns.cpp
@@ -19,9 +19,36 @@
|
|||||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
#include <iostream> //borrame
|
||||||
|
#include <cassert>
|
||||||
#include "dns.h"
|
#include "dns.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
|
||||||
|
using std::string;
|
||||||
|
using std::list;
|
||||||
|
|
||||||
|
|
||||||
|
Tins::DNS::DNS() : PDU(255), extra_size(0) {
|
||||||
|
std::memset(&dns, 0, sizeof(dns));
|
||||||
|
}
|
||||||
|
|
||||||
|
Tins::DNS::~DNS() {
|
||||||
|
free_list(ans);
|
||||||
|
free_list(arity);
|
||||||
|
free_list(addit);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Tins::DNS::free_list(std::list<ResourceRecord*> &lst) {
|
||||||
|
while(lst.size()) {
|
||||||
|
delete lst.front();
|
||||||
|
lst.pop_front();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Tins::DNS::header_size() const {
|
||||||
|
return sizeof(dns) + extra_size;
|
||||||
|
}
|
||||||
|
|
||||||
void Tins::DNS::id(uint16_t new_id) {
|
void Tins::DNS::id(uint16_t new_id) {
|
||||||
dns.id = new_id;
|
dns.id = new_id;
|
||||||
@@ -66,3 +93,130 @@ void Tins::DNS::checking_disabled(uint8_t new_cd) {
|
|||||||
void Tins::DNS::rcode(uint8_t new_rcode) {
|
void Tins::DNS::rcode(uint8_t new_rcode) {
|
||||||
dns.rcode = new_rcode;
|
dns.rcode = new_rcode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Tins::DNS::add_query(const string &name, QueryType type, QueryClass qclass) {
|
||||||
|
string new_str;
|
||||||
|
parse_domain_name(name, new_str);
|
||||||
|
|
||||||
|
queries.push_back(
|
||||||
|
Query(new_str,
|
||||||
|
Utils::net_to_host_s(type),
|
||||||
|
Utils::net_to_host_s(qclass))
|
||||||
|
);
|
||||||
|
extra_size += new_str.size() + 1 + (sizeof(uint16_t) << 1);
|
||||||
|
dns.questions = Utils::net_to_host_s(queries.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Tins::DNS::add_answer(const string &name, QueryType type, QueryClass qclass, uint32_t ttl, uint32_t ip) {
|
||||||
|
ResourceRecord *res = make_record(name, type, qclass, ttl, ip);
|
||||||
|
ans.push_back(res);
|
||||||
|
dns.answers = Utils::net_to_host_s(ans.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Tins::DNS::add_authority(const string &name, QueryType type, QueryClass qclass, uint32_t ttl, uint32_t ip) {
|
||||||
|
ResourceRecord *res = make_record(name, type, qclass, ttl, ip);
|
||||||
|
arity.push_back(res);
|
||||||
|
dns.authority = Utils::net_to_host_s(arity.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Tins::DNS::add_additional(const string &name, QueryType type, QueryClass qclass, uint32_t ttl, uint32_t ip) {
|
||||||
|
ResourceRecord *res = make_record(name, type, qclass, ttl, ip);
|
||||||
|
addit.push_back(res);
|
||||||
|
dns.additional = Utils::net_to_host_s(addit.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
Tins::DNS::ResourceRecord *Tins::DNS::make_record(const std::string &name, QueryType type, QueryClass qclass, uint32_t ttl, uint32_t ip) {
|
||||||
|
string nm;
|
||||||
|
parse_domain_name(name, nm);
|
||||||
|
uint16_t index = find_domain_name(nm);
|
||||||
|
ResourceRecord *res;
|
||||||
|
if(index)
|
||||||
|
res = new OffsetedResourceRecord(Utils::net_to_host_s(index));
|
||||||
|
else
|
||||||
|
res = new NamedResourceRecord(nm);
|
||||||
|
res->info.type = Utils::net_to_host_s(type);
|
||||||
|
res->info.qclass = Utils::net_to_host_s(qclass);
|
||||||
|
res->info.ttl = Utils::net_to_host_l(ttl);
|
||||||
|
res->info.dlen = Utils::net_to_host_s(sizeof(uint32_t));
|
||||||
|
res->info.data = Utils::net_to_host_l(ip);
|
||||||
|
extra_size += res->size();
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Tins::DNS::find_domain_name(const std::string &dname) {
|
||||||
|
uint16_t index(sizeof(dnshdr));
|
||||||
|
list<Query>::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() ||
|
||||||
|
find_domain_name(dname, ans, index) ||
|
||||||
|
find_domain_name(dname, arity, index) ||
|
||||||
|
find_domain_name(dname, addit, index))
|
||||||
|
return index;
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Tins::DNS::find_domain_name(const std::string &dname, const std::list<ResourceRecord*> &lst, uint16_t &out) {
|
||||||
|
list<ResourceRecord*>::const_iterator it(lst.begin());
|
||||||
|
NamedResourceRecord *named;
|
||||||
|
while(it != lst.end()) {
|
||||||
|
named = dynamic_cast<NamedResourceRecord*>(*it);
|
||||||
|
if(named && named->name == dname)
|
||||||
|
break;
|
||||||
|
out += (*it)->size();
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
return it != lst.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Tins::DNS::parse_domain_name(const std::string &dn, std::string &out) {
|
||||||
|
size_t last_index(0), index;
|
||||||
|
while((index = dn.find('.', last_index+1)) != string::npos) {
|
||||||
|
out.push_back(index - last_index);
|
||||||
|
out.append(dn.begin() + last_index, dn.begin() + index);
|
||||||
|
last_index = index + 1; //skip dot
|
||||||
|
}
|
||||||
|
out.push_back(dn.size() - last_index);
|
||||||
|
out.append(dn.begin() + last_index, dn.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Tins::DNS::write_serialization(uint8_t *buffer, uint32_t total_sz, const PDU *parent) {
|
||||||
|
assert(total_sz >= sizeof(dns) + extra_size);
|
||||||
|
std::memcpy(buffer, &dns, sizeof(dns));
|
||||||
|
buffer += sizeof(dns);
|
||||||
|
for(list<Query>::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;
|
||||||
|
buffer += sizeof(uint16_t);
|
||||||
|
*((uint16_t*)buffer) = it->qclass;
|
||||||
|
buffer += sizeof(uint16_t);
|
||||||
|
}
|
||||||
|
buffer = serialize_list(ans, buffer);
|
||||||
|
buffer = serialize_list(arity, buffer);
|
||||||
|
buffer = serialize_list(addit, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t *Tins::DNS::serialize_list(const std::list<ResourceRecord*> &lst, uint8_t *buffer) const {
|
||||||
|
for(list<ResourceRecord*>::const_iterator it(lst.begin()); it != lst.end(); ++it)
|
||||||
|
buffer += (*it)->write(buffer);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Tins::DNS::ResourceRecord::write(uint8_t *buffer) const {
|
||||||
|
uint32_t sz(do_write(buffer));
|
||||||
|
buffer += sz;
|
||||||
|
std::memcpy(buffer, &info, sizeof(info));
|
||||||
|
return sz + sizeof(info);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Tins::DNS::OffsetedResourceRecord::do_write(uint8_t *buffer) const {
|
||||||
|
std::memcpy(buffer, &offset, sizeof(offset));
|
||||||
|
return sizeof(offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Tins::DNS::NamedResourceRecord::do_write(uint8_t *buffer) const {
|
||||||
|
std::memcpy(buffer, name.c_str(), name.size() + 1);
|
||||||
|
return name.size() + 1;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user