diff --git a/include/dns.h b/include/dns.h index 14b236b..ff2e27f 100644 --- a/include/dns.h +++ b/include/dns.h @@ -24,8 +24,11 @@ #include #include +#include #include +#include #include "pdu.h" +#include "utils.h" namespace Tins { class DNS : public PDU { @@ -99,6 +102,30 @@ namespace Tins { ANY = 255 }; + /** + * \brief Struct that represent DNS queries. + */ + 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) {} + }; + + /** + * \brief Struct that represent DNS resource records. + */ + struct Resource { + std::string dname, addr; + uint16_t type, qclass; + uint32_t ttl; + + 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) {} + }; + /** * \brief Default constructor. * @@ -201,28 +228,28 @@ namespace Tins { * * \return uint16_t containing the value of the questions field. */ - uint16_t questions() { return dns.questions; } + uint16_t questions() { return Utils::net_to_host_s(dns.questions); } /** * \brief Setter for the answers field. * * \return uint16_t containing the value of the answers field. */ - uint16_t answers() { return dns.answers; } + uint16_t answers() { return Utils::net_to_host_s(dns.answers); } /** * \brief Setter for the authority field. * * \return uint16_t containing the value of the authority field. */ - uint16_t authority() { return dns.authority; } + uint16_t authority() { return Utils::net_to_host_s(dns.authority); } /** * \brief Setter for the additional field. * * \return uint16_t containing the value of the additional field. */ - uint16_t additional() { return dns.additional; } + uint16_t additional() { return Utils::net_to_host_s(dns.additional); } /** * \brief Getter for the PDU's type. @@ -365,6 +392,21 @@ namespace Tins { */ void add_additional(const std::string &name, QueryType type, QueryClass qclass, uint32_t ttl, uint32_t ip); + + + /** + * \brief Getter for this PDU's DNS queries. + * \return std::list containing the queries in this + * record. + */ + std::list dns_queries() const; + + /** + * \brief Getter for this PDU's DNS answers + * \return std::list containing the answers in this + * record. + */ + std::list dns_answers(); private: struct dnshdr { uint16_t id; @@ -383,58 +425,104 @@ namespace Tins { 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 { + struct Info { 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 write(uint8_t *buffer) const = 0; virtual uint32_t do_write(uint8_t *buffer) const = 0; virtual uint32_t size() const = 0; + virtual bool matches(const std::string &dname) { return false; } + virtual uint32_t data_size() const = 0; + virtual const uint8_t *data_pointer() const = 0; + virtual const std::string *dname_pointer() const { return 0; } }; - struct OffsetedResourceRecord : public ResourceRecord { + template struct SizedResourceRecord : public ResourceRecord { + uint8_t data[S]; + + SizedResourceRecord(uint8_t *d) { + std::memcpy(data, d, S); + } + + uint32_t write(uint8_t *buffer) const { + uint32_t sz(do_write(buffer)); + buffer += sz; + std::memcpy(buffer, &info, sizeof(info)); + buffer += sizeof(info); + *((uint16_t*)buffer) = Utils::net_to_host_s(S); + buffer += sizeof(uint16_t); + std::memcpy(buffer, data, S); + return sz + sizeof(info) + sizeof(uint16_t) + S; + } + + uint32_t data_size() const { + return S; + } + + const uint8_t *data_pointer() const { + return data; + } + }; + + template struct OffsetedResourceRecord : public SizedResourceRecord { uint16_t offset; - OffsetedResourceRecord(uint16_t off) : offset(off | 0xc0) {} + OffsetedResourceRecord(uint16_t off, uint8_t *data) : SizedResourceRecord(data), offset(off | 0xc0) {} - uint32_t do_write(uint8_t *buffer) const; - uint32_t size() const { return sizeof(info) + sizeof(offset); } + uint32_t do_write(uint8_t *buffer) const { + std::memcpy(buffer, &offset, sizeof(offset)); + return sizeof(offset); + } + uint32_t size() const { return sizeof(ResourceRecord::info) + sizeof(offset) + S + sizeof(uint16_t); } }; - struct NamedResourceRecord : public ResourceRecord { + template struct NamedResourceRecord : public SizedResourceRecord { std::string name; - NamedResourceRecord(const std::string &nm) : name(nm) {} + NamedResourceRecord(const std::string &nm, uint8_t *data) : SizedResourceRecord(data), name(nm) {} - uint32_t do_write(uint8_t *buffer) const; - uint32_t size() const { return sizeof(info) + name.size() + 1; } + uint32_t do_write(uint8_t *buffer) const { + std::memcpy(buffer, name.c_str(), name.size() + 1); + return name.size() + 1; + } + + uint32_t size() const { return sizeof(ResourceRecord::info) + name.size() + 1 + S + sizeof(uint16_t); } + + bool matches(const std::string &dname) { + return dname == name; + } + + const std::string *dname_pointer() const { + return &name; + } }; + typedef std::map SuffixMap; + 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 parse_domain_name(const std::string &dn, std::string &out) const; + 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); void free_list(std::list &lst); uint8_t *serialize_list(const std::list &lst, uint8_t *buffer) const; + void compose_name(const uint8_t *ptr, uint32_t sz, std::string &out); + void convert_resources(const std::list &lst, std::list &res); ResourceRecord *make_record(const std::string &name, QueryType type, QueryClass qclass, uint32_t ttl, uint32_t ip); + void build_suffix_map(uint32_t index, const uint8_t *data, uint32_t sz); + uint32_t build_suffix_map(uint32_t index, const std::list &lst); + uint32_t build_suffix_map(uint32_t index, const std::list &lst); + void build_suffix_map(); dnshdr dns; uint32_t extra_size; std::list queries; std::list ans, arity, addit; + SuffixMap suffixes; }; }; diff --git a/src/dns.cpp b/src/dns.cpp index 4957e19..7d9c3c0 100644 --- a/src/dns.cpp +++ b/src/dns.cpp @@ -19,11 +19,10 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include #include //borrame +#include #include #include "dns.h" -#include "utils.h" using std::string; using std::list; @@ -130,15 +129,14 @@ Tins::DNS::ResourceRecord *Tins::DNS::make_record(const std::string &name, Query parse_domain_name(name, nm); uint16_t index = find_domain_name(nm); ResourceRecord *res; + ip = Utils::net_to_host_l(ip); if(index) - res = new OffsetedResourceRecord(Utils::net_to_host_s(index)); + res = new OffsetedResourceRecord<4>(Utils::net_to_host_s(index), (uint8_t*)&ip); else - res = new NamedResourceRecord(nm); + res = new NamedResourceRecord<4>(nm, (uint8_t*)&ip); 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; } @@ -159,10 +157,8 @@ uint32_t Tins::DNS::find_domain_name(const std::string &dname) { 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) + if((*it)->matches(dname)) break; out += (*it)->size(); ++it; @@ -170,7 +166,7 @@ bool Tins::DNS::find_domain_name(const std::string &dname, const std::list= sizeof(dns) + extra_size); std::memcpy(buffer, &dns, sizeof(dns)); @@ -204,19 +216,111 @@ uint8_t *Tins::DNS::serialize_list(const std::list &lst, uint8_ 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); +void Tins::DNS::build_suffix_map(uint32_t index, const uint8_t *data, uint32_t sz) { + uint32_t i(0), suff_sz(data[0]); + while(i + suff_sz + 1 < sz && suff_sz) { + i++; + suffixes.insert(std::make_pair(index + i - 1, string(data + i, data + i + suff_sz))); + i += suff_sz; + if(i < sz) + suff_sz = data[i]; + } } -uint32_t Tins::DNS::OffsetedResourceRecord::do_write(uint8_t *buffer) const { - std::memcpy(buffer, &offset, sizeof(offset)); - return sizeof(offset); +uint32_t Tins::DNS::build_suffix_map(uint32_t index, const list &lst) { + const string *str; + for(list::const_iterator it(lst.begin()); it != lst.end(); ++it) { + str = (*it)->dname_pointer(); + if(str) { + build_suffix_map(index, (uint8_t*)str->c_str(), str->size()); + index += str->size() + 1; + } + else + index += sizeof(uint16_t); + index += sizeof(ResourceRecord::Info); + uint32_t sz((*it)->data_size()); + if(sz > 4) + build_suffix_map(index, (*it)->data_pointer(), sz); + index += sz; + } + return index; } -uint32_t Tins::DNS::NamedResourceRecord::do_write(uint8_t *buffer) const { - std::memcpy(buffer, name.c_str(), name.size() + 1); - return name.size() + 1; + +uint32_t Tins::DNS::build_suffix_map(uint32_t index, const list &lst) { + for(list::const_iterator it(lst.begin()); it != lst.end(); ++it) { + build_suffix_map(index, (uint8_t*)it->name.c_str(), it->name.size()); + index += it->name.size() + 1 + (sizeof(uint16_t) << 1); + } + return index; } + +void Tins::DNS::build_suffix_map() { + uint32_t index(sizeof(dnshdr)); + index = build_suffix_map(index, queries); + index = build_suffix_map(index, ans); + index = build_suffix_map(index, arity); + build_suffix_map(index, addit); +} + +void Tins::DNS::compose_name(const uint8_t *ptr, uint32_t sz, std::string &out) { + uint32_t i(0); + while(i < sz) { + if(i) + out.push_back('.'); + + if(ptr[i] & 0xc0) { + uint16_t index = Utils::net_to_host_s(*((uint16_t*)ptr)); + SuffixMap::iterator it(suffixes.find(index)); + if(it == suffixes.end()) + std::cout << "Could not find " << ptr + i << "\n"; + else + out += it->second; + i += 2; + } + else { + uint8_t suff_sz(ptr[i]); + i++; + if(i + suff_sz < sz) + out.append(ptr + i, ptr + i + suff_sz); + i += suff_sz; + } + } +} + +void Tins::DNS::convert_resources(const std::list &lst, std::list &res) { + if(!suffixes.size()) + build_suffix_map(); + const string *str_ptr; + const uint8_t *ptr; + uint32_t sz; + for(list::const_iterator it(lst.begin()); it != lst.end(); ++it) { + string dname, addr; + if((str_ptr = (*it)->dname_pointer())) + compose_name(reinterpret_cast(str_ptr->c_str()), str_ptr->size(), dname); + ptr = (*it)->data_pointer(); + sz = (*it)->data_size(); + if(sz == 4) + addr = Utils::ip_to_string(*(uint32_t*)ptr); + else + compose_name(ptr, sz, addr); + res.push_back(Resource(dname, addr, (*it)->info.type, (*it)->info.qclass, (*it)->info.ttl)); + } +} + +list Tins::DNS::dns_queries() const { + list output; + 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, it->type, it->qclass)); + } + return output; +} + +list Tins::DNS::dns_answers() { + list res; + convert_resources(ans, res); + return res; +} +