diff --git a/include/dns.h b/include/dns.h index 29443ab..14b236b 100644 --- a/include/dns.h +++ b/include/dns.h @@ -23,6 +23,8 @@ #define __DNS_H #include +#include +#include #include "pdu.h" namespace Tins { @@ -33,6 +35,209 @@ namespace Tins { 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(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. * @@ -114,25 +319,122 @@ namespace Tins { * \param new_rcode The new rcode value to be set. */ 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: struct dnshdr { uint16_t id; uint16_t - qr:1, - opcode:4, - aa:1, - tc:1, rd:1, - ra:1, - z:1, - ad:1, + tc:1, + aa:1, + opcode:4, + qr:1, + rcode:4, cd:1, - rcode:4; - uint32_t questions, answers, + ad:1, + z:1, + ra:1; + uint16_t questions, answers, 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 &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 &lst); + uint8_t *serialize_list(const std::list &lst, uint8_t *buffer) const; + ResourceRecord *make_record(const std::string &name, QueryType type, QueryClass qclass, uint32_t ttl, uint32_t ip); + dnshdr dns; + uint32_t extra_size; + std::list queries; + std::list ans, arity, addit; }; }; diff --git a/include/pdu.h b/include/pdu.h index bdd03c8..6801ee9 100644 --- a/include/pdu.h +++ b/include/pdu.h @@ -87,7 +87,8 @@ namespace Tins { DHCP, EAPOL, RC4EAPOL, - RSNEAPOL + RSNEAPOL, + DNS }; /** \brief PDU constructor diff --git a/src/dns.cpp b/src/dns.cpp index ed18715..4957e19 100644 --- a/src/dns.cpp +++ b/src/dns.cpp @@ -19,9 +19,36 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ +#include +#include //borrame +#include #include "dns.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 &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) { 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) { 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::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 &lst, uint16_t &out) { + list::const_iterator it(lst.begin()); + NamedResourceRecord *named; + while(it != lst.end()) { + named = dynamic_cast(*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::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 &lst, uint8_t *buffer) const { + for(list::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; +}