From 44dbdbb116b0096ca087feac68bc79ba4a21fdbb Mon Sep 17 00:00:00 2001 From: Matias Fontanini Date: Fri, 30 Mar 2012 21:42:43 -0300 Subject: [PATCH] DNS pdu forging and sniffing is working. Copy constructor/assignment operator are still pending. --- include/arp.h | 2 +- include/dns.h | 60 +++++++++++---------- src/dns.cpp | 141 ++++++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 154 insertions(+), 49 deletions(-) diff --git a/include/arp.h b/include/arp.h index 67502de..79493bd 100644 --- a/include/arp.h +++ b/include/arp.h @@ -53,7 +53,7 @@ namespace Tins { ARP(uint32_t target_ip = 0, uint32_t sender_ip = 0, const uint8_t *target_hw = 0, const uint8_t *sender_hw = 0); /** - * \brief Constructor which creates an TCP object from a buffer and adds all identifiable + * \brief Constructor which creates an ARP object from a buffer and adds all identifiable * PDUs found in the buffer as children of this one. * \param buffer The buffer from which this PDU will be constructed. * \param total_sz The total size of the buffer. diff --git a/include/dns.h b/include/dns.h index ff2e27f..dd73528 100644 --- a/include/dns.h +++ b/include/dns.h @@ -111,6 +111,7 @@ namespace Tins { Query(const std::string &nm, uint16_t t, uint16_t c) : name(nm), type(t), qclass(c) {} + Query() {} }; /** @@ -133,6 +134,16 @@ 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. + * \param buffer The buffer from which this PDU will be + * constructed. + * \param total_sz The total size of the buffer. + */ + DNS(const uint8_t *buffer, uint32_t total_sz); + /** * \brief Destructor. */ @@ -431,66 +442,60 @@ namespace Tins { uint32_t ttl; } __attribute__((packed)) info; - virtual ~ResourceRecord() {} - 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; } - }; - - template struct SizedResourceRecord : public ResourceRecord { - uint8_t data[S]; + uint8_t *data; + uint16_t data_sz; - SizedResourceRecord(uint8_t *d) { - std::memcpy(data, d, S); + ResourceRecord(uint8_t *d = 0, uint16_t len = 0) : data_sz(len) { + if(d) + std::memcpy(data, d, data_sz); } + virtual ~ResourceRecord() {} 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); + *((uint16_t*)buffer) = Utils::net_to_host_s(data_sz); buffer += sizeof(uint16_t); - std::memcpy(buffer, data, S); - return sz + sizeof(info) + sizeof(uint16_t) + S; + std::memcpy(buffer, data, data_sz); + return sz + sizeof(info) + sizeof(uint16_t) + data_sz; } - + 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; } uint32_t data_size() const { - return S; + return data_sz; } - const uint8_t *data_pointer() const { return data; } + virtual const std::string *dname_pointer() const { return 0; } }; - template struct OffsetedResourceRecord : public SizedResourceRecord { + struct OffsetedResourceRecord : public ResourceRecord { uint16_t offset; - OffsetedResourceRecord(uint16_t off, uint8_t *data) : SizedResourceRecord(data), offset(off | 0xc0) {} + OffsetedResourceRecord(uint16_t off, uint8_t *data = 0, uint16_t len = 0) : ResourceRecord(data,len), offset(off | 0xc0) {} 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); } + uint32_t size() const { return sizeof(ResourceRecord::info) + sizeof(offset) + data_sz + sizeof(uint16_t); } }; - template struct NamedResourceRecord : public SizedResourceRecord { + struct NamedResourceRecord : public ResourceRecord { std::string name; - NamedResourceRecord(const std::string &nm, uint8_t *data) : SizedResourceRecord(data), name(nm) {} + NamedResourceRecord(const std::string &nm, uint8_t *data = 0, uint16_t len = 0) : ResourceRecord(data,len), name(nm) {} 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); } + uint32_t size() const { return sizeof(ResourceRecord::info) + name.size() + 1 + data_sz + sizeof(uint16_t); } bool matches(const std::string &dname) { return dname == name; @@ -503,6 +508,7 @@ namespace Tins { typedef std::map SuffixMap; + const uint8_t *build_resource_list(std::list &lst, const uint8_t *ptr, uint32_t &sz, uint16_t nrecs); 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) const; @@ -513,7 +519,7 @@ namespace Tins { 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); + void add_suffix(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(); diff --git a/src/dns.cpp b/src/dns.cpp index 7d9c3c0..03dca8f 100644 --- a/src/dns.cpp +++ b/src/dns.cpp @@ -21,6 +21,7 @@ #include //borrame #include +#include #include #include "dns.h" @@ -32,6 +33,86 @@ Tins::DNS::DNS() : PDU(255), extra_size(0) { std::memset(&dns, 0, sizeof(dns)); } +Tins::DNS::DNS(const uint8_t *buffer, uint32_t total_sz) : PDU(255), extra_size(0) { + if(total_sz < sizeof(dnshdr)) + 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()); + buffer += sizeof(dnshdr); + for(uint16_t i(0); i < nquestions; ++i) { + const uint8_t *ptr(buffer); + while(ptr < end && *ptr) + ptr++; + Query query; + if((ptr + (sizeof(uint16_t) << 1)) > end) + throw std::runtime_error("Not enough size for a given query."); + query.name = string(buffer, ptr); + ptr++; + const uint16_t *opt_ptr = reinterpret_cast(ptr); + query.type = *(opt_ptr++); + query.qclass = *(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()); +} + +const uint8_t *Tins::DNS::build_resource_list(list &lst, const uint8_t *ptr, uint32_t &sz, uint16_t nrecs) { + const uint8_t *ptr_end(ptr + sz); + const uint8_t *parse_start(ptr); + for(uint16_t i(0); i < nrecs; ++i) { + const uint8_t *this_opt_start(ptr); + if(ptr + sizeof(uint16_t) > ptr_end) + throw std::runtime_error("Not enough size for a given resource."); + ResourceRecord *res; + if((*ptr & 0xc0)) { + uint16_t offset(*reinterpret_cast(ptr)); + offset = Utils::net_to_host_s(offset) & 0x3fff; + res = new OffsetedResourceRecord(Utils::net_to_host_s(offset)); + ptr += sizeof(uint16_t); + } + else { + const uint8_t *str_end(ptr), *end(ptr + sz); + while(str_end < end && *str_end) + str_end++; + if(str_end == end) + throw std::runtime_error("Not enough size for a resource domain name."); + str_end++; + res = new NamedResourceRecord(string(ptr, str_end)); + ptr = str_end; + } + if(ptr + sizeof(res->info) > ptr_end) + throw std::runtime_error("Not enough size for a resource info."); + std::memcpy(&res->info, ptr, sizeof(res->info)); + ptr += sizeof(res->info); + if(ptr + sizeof(uint16_t) > ptr_end) + throw std::runtime_error("Not enough size for resource data size."); + res->data_sz = Utils::net_to_host_s( + *reinterpret_cast(ptr) + ); + ptr += sizeof(uint16_t); + if(ptr + res->data_sz > ptr_end) + throw std::runtime_error("Not enough size for resource data"); + res->data = new uint8_t[res->data_sz]; + if(res->data_sz == 4) + *(uint32_t*)res->data = Utils::net_to_host_l(*(uint32_t*)ptr); + else { + std::memcpy(res->data, ptr, res->data_sz); + } + + ptr += res->data_sz; + extra_size += ptr - this_opt_start; + lst.push_back(res); + } + sz -= ptr - parse_start; + return ptr; +} + Tins::DNS::~DNS() { free_list(ans); free_list(arity); @@ -40,6 +121,7 @@ Tins::DNS::~DNS() { void Tins::DNS::free_list(std::list &lst) { while(lst.size()) { + delete[] lst.front()->data; delete lst.front(); lst.pop_front(); } @@ -131,9 +213,9 @@ Tins::DNS::ResourceRecord *Tins::DNS::make_record(const std::string &name, Query ResourceRecord *res; ip = Utils::net_to_host_l(ip); if(index) - res = new OffsetedResourceRecord<4>(Utils::net_to_host_s(index), (uint8_t*)&ip); + res = new OffsetedResourceRecord(Utils::net_to_host_s(index), (uint8_t*)&ip, sizeof(uint32_t)); else - res = new NamedResourceRecord<4>(nm, (uint8_t*)&ip); + res = new NamedResourceRecord(nm, (uint8_t*)&ip, sizeof(uint32_t)); 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); @@ -181,15 +263,18 @@ void Tins::DNS::unparse_domain_name(const std::string &dn, std::string &out) con if(dn.size()) { uint32_t index(1), len(dn[0]); while(index + len < dn.size() && len) { + if(index != 1) + out.push_back('.'); out.append(dn.begin() + index, dn.begin() + index + len); - out.push_back('.'); index += len; if(index < dn.size() - 1) len = dn[index]; index++; } - if(index < dn.size()) + if(index < dn.size()) { + out.push_back('.'); out.append(dn.begin() + index, dn.end()); + } } } @@ -216,9 +301,9 @@ uint8_t *Tins::DNS::serialize_list(const std::list &lst, uint8_ return buffer; } -void Tins::DNS::build_suffix_map(uint32_t index, const uint8_t *data, uint32_t sz) { +void Tins::DNS::add_suffix(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) { + 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; @@ -232,15 +317,15 @@ uint32_t Tins::DNS::build_suffix_map(uint32_t index, const list 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()); + add_suffix(index, (uint8_t*)str->c_str(), str->size()); index += str->size() + 1; } else index += sizeof(uint16_t); - index += sizeof(ResourceRecord::Info); + index += sizeof(ResourceRecord::Info) + sizeof(uint16_t); uint32_t sz((*it)->data_size()); if(sz > 4) - build_suffix_map(index, (*it)->data_pointer(), sz); + add_suffix(index, (*it)->data_pointer(), sz); index += sz; } return index; @@ -249,7 +334,7 @@ uint32_t Tins::DNS::build_suffix_map(uint32_t index, const list 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()); + add_suffix(index, (uint8_t*)it->name.c_str(), it->name.size()); index += it->name.size() + 1 + (sizeof(uint16_t) << 1); } return index; @@ -268,15 +353,21 @@ void Tins::DNS::compose_name(const uint8_t *ptr, uint32_t sz, std::string &out) while(i < sz) { if(i) out.push_back('.'); - - if(ptr[i] & 0xc0) { - uint16_t index = Utils::net_to_host_s(*((uint16_t*)ptr)); + if((ptr[i] & 0xc0)) { + uint16_t index = Utils::net_to_host_s(*((uint16_t*)(ptr + i))); + index &= 0x3fff; SuffixMap::iterator it(suffixes.find(index)); - if(it == suffixes.end()) - std::cout << "Could not find " << ptr + i << "\n"; - else + assert(it != suffixes.end()); + bool first(true); + do { + if(!first) + out.push_back('.'); + first = false; out += it->second; - i += 2; + index += it->second.size() + 1; + it = suffixes.find(index); + } while(it != suffixes.end()); + break; } else { uint8_t suff_sz(ptr[i]); @@ -296,15 +387,22 @@ void Tins::DNS::convert_resources(const std::list &lst, std::li uint32_t sz; for(list::const_iterator it(lst.begin()); it != lst.end(); ++it) { string dname, addr; - if((str_ptr = (*it)->dname_pointer())) + if((str_ptr = (*it)->dname_pointer())) compose_name(reinterpret_cast(str_ptr->c_str()), str_ptr->size(), dname); + else { + uint16_t offset = static_cast(*it)->offset; + compose_name((uint8_t*)&offset, 2, dname); + } ptr = (*it)->data_pointer(); sz = (*it)->data_size(); if(sz == 4) addr = Utils::ip_to_string(*(uint32_t*)ptr); - else + else compose_name(ptr, sz, addr); - res.push_back(Resource(dname, addr, (*it)->info.type, (*it)->info.qclass, (*it)->info.ttl)); + res.push_back( + Resource(dname, addr, Utils::net_to_host_s((*it)->info.type), + Utils::net_to_host_s((*it)->info.qclass), Utils::net_to_host_l((*it)->info.ttl)) + ); } } @@ -313,7 +411,7 @@ list Tins::DNS::dns_queries() const { 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)); + output.push_back(Query(dn, Utils::net_to_host_s(it->type), Utils::net_to_host_s(it->qclass))); } return output; } @@ -324,3 +422,4 @@ list Tins::DNS::dns_answers() { return res; } +