Skip to content

Commit

Permalink
Merge "Handle malformed DNS packets." into R2.20
Browse files Browse the repository at this point in the history
  • Loading branch information
Zuul authored and opencontrail-ci-admin committed Apr 22, 2016
2 parents df8e4ca + c2a0c73 commit 306e1e1
Show file tree
Hide file tree
Showing 14 changed files with 520 additions and 138 deletions.
2 changes: 1 addition & 1 deletion src/dns/bind/bind_resolver.cc
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ void BindResolver::DnsRcvHandler(const boost::system::error_code &error,
bool del = false;
if (!error) {
if (cb_)
cb_(pkt_buf_);
cb_(pkt_buf_, length);
else
del = true;
} else {
Expand Down
2 changes: 1 addition & 1 deletion src/dns/bind/bind_resolver.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

class BindResolver {
public:
typedef boost::function<void(uint8_t *)> Callback;
typedef boost::function<void(uint8_t *, std::size_t)> Callback;
static const int max_pkt_size = 1024;
static const uint8_t max_dns_servers = 2;

Expand Down
263 changes: 172 additions & 91 deletions src/dns/bind/bind_util.cc
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ uint16_t BindUtil::DnsClass(const std::string &cl) {

std::string BindUtil::DnsClass(uint16_t cl) {
DnsTypeNumIter iter = g_dns_class_num_map.find(cl);
if (iter == g_dns_type_num_map.end())
if (iter == g_dns_class_num_map.end())
return "";
return iter->second;
}
Expand Down Expand Up @@ -236,199 +236,280 @@ uint8_t *BindUtil::AddUpdate(uint8_t *ptr, const DnsItem &item,
return ptr;
}

uint8_t *BindUtil::ReadName(uint8_t *dns, uint8_t *ptr, std::string &name,
uint16_t &plen, uint16_t &offset) {
bool BindUtil::ReadName(uint8_t *dns, uint16_t dnslen, int *remlen,
std::string &name, uint16_t &plen, uint16_t &offset) {
if (*remlen <= 0) {
return false;
}

uint8_t *ptr = dns + (dnslen - *remlen);
std::size_t len = *ptr;
while (len) {
if (len & 0xC0) {
uint16_t dummy;
if ((len & 0xC0) == 0xC0) {
plen = name.size() ? name.size() - 1 : 0;
ptr = ReadShort(ptr, offset);
ReadName(dns, dns + (offset & ~0xC000), name, dummy, dummy);
return ptr;
if (ReadShort(dns, dnslen, remlen, offset) == false)
return false;
int offset_remlen = dnslen - (offset & ~0xC000);
uint16_t dummy;
return ReadName(dns, dnslen, &offset_remlen, name, dummy, dummy);
} else {
*remlen -= (len + 1);
if (*remlen < 0) {
return false;
}
name.append((char *)ptr+1, len);
ptr += len + 1;
len = *ptr;
if (len)
name.append(".");
}
}
ptr++;

return ptr;
*remlen -= 1;
return true;
}

uint8_t *BindUtil::ReadData(uint8_t *buf, uint8_t *ptr, DnsItem &item) {
bool BindUtil::ReadData(uint8_t *dns, uint16_t dnslen, int *remlen,
DnsItem &item) {
uint16_t length;
if (ReadShort(dns, dnslen, remlen, length) == false)
return false;

boost::system::error_code ec;
if (item.type == DNS_A_RECORD) {
uint32_t ip;
ptr = ReadWord(ptr, ip);
if (ReadWord(dns, dnslen, remlen, ip) == false)
return false;
boost::asio::ip::address_v4 addr(ip);
item.data = addr.to_string(ec);
return true;
} else if(item.type == DNS_AAAA_RECORD) {
if (*remlen < 16) {
return false;
}
uint8_t *ptr = dns + (dnslen - *remlen);
boost::asio::ip::address_v6::bytes_type ip;
memcpy(&ip[0], ptr, 16);
boost::asio::ip::address_v6 addr(ip);
item.data = addr.to_string(ec);
ptr += 16;
*remlen -= 16;
return true;
} else if(item.type == DNS_TYPE_SOA) {
ptr = ReadName(buf, ptr, item.soa.primary_ns,
item.soa.ns_plen, item.soa.ns_offset);
ptr = ReadName(buf, ptr, item.soa.mailbox,
item.soa.mailbox_plen, item.soa.mailbox_offset);
ptr = ReadWord(ptr, item.soa.serial);
ptr = ReadWord(ptr, item.soa.refresh);
ptr = ReadWord(ptr, item.soa.retry);
ptr = ReadWord(ptr, item.soa.expiry);
ptr = ReadWord(ptr, item.soa.ttl);
if (ReadName(dns, dnslen, remlen, item.soa.primary_ns,
item.soa.ns_plen, item.soa.ns_offset) == false)
return false;
if (ReadName(dns, dnslen, remlen, item.soa.mailbox,
item.soa.mailbox_plen, item.soa.mailbox_offset) == false)
return false;
if (ReadWord(dns, dnslen, remlen, item.soa.serial) == false)
return false;
if (ReadWord(dns, dnslen, remlen, item.soa.refresh) == false)
return false;
if (ReadWord(dns, dnslen, remlen, item.soa.retry) == false)
return false;
if (ReadWord(dns, dnslen, remlen, item.soa.expiry) == false)
return false;
return ReadWord(dns, dnslen, remlen, item.soa.ttl);
} else if(item.type == DNS_PTR_RECORD ||
item.type == DNS_CNAME_RECORD ||
item.type == DNS_NS_RECORD ||
item.type == DNS_TXT_RECORD) {
ptr = ReadName(buf, ptr, item.data, item.data_plen, item.data_offset);
return ReadName(dns, dnslen, remlen, item.data, item.data_plen, item.data_offset);
} else if(item.type == DNS_MX_RECORD) {
ptr = ReadShort(ptr, item.priority);
ptr = ReadName(buf, ptr, item.data, item.data_plen, item.data_offset);
} else {
DNS_BIND_TRACE(DnsBindError,
"Unsupported data type in DNS response : " << item.type);
return NULL;
if (ReadShort(dns, dnslen, remlen, item.priority) == false)
return false;
return ReadName(dns, dnslen, remlen, item.data, item.data_plen, item.data_offset);
}

return ptr;
DNS_BIND_TRACE(DnsBindError,
"Unsupported data type in DNS response : " << item.type);
return false;
}

uint8_t *BindUtil::ReadQuestionEntry(uint8_t *buf, uint8_t *ptr,
DnsItem &item) {
ptr = ReadName(buf, ptr, item.name, item.name_plen, item.name_offset);
ptr = ReadShort(ptr, item.type);
ptr = ReadShort(ptr, item.eclass);
bool BindUtil::ReadQuestionEntry(uint8_t *dns, uint16_t dnslen, int *remlen,
DnsItem &item) {
if (ReadName(dns, dnslen, remlen, item.name, item.name_plen,
item.name_offset) == false)
return false;

return ptr;
if (ReadShort(dns, dnslen, remlen, item.type) == false)
return false;

return ReadShort(dns, dnslen, remlen, item.eclass);
}

uint8_t *BindUtil::ReadAnswerEntry(uint8_t *buf, uint8_t *ptr, DnsItem &item) {
ptr = ReadQuestionEntry(buf, ptr, item);
ptr = ReadWord(ptr, item.ttl);
uint16_t length;
ptr = ReadShort(ptr, length);
ptr = ReadData(buf, ptr, item);
bool BindUtil::ReadAnswerEntry(uint8_t *dns, uint16_t dnslen, int *remlen,
DnsItem &item) {
if (ReadQuestionEntry(dns, dnslen, remlen, item) == false)
return false;

return ptr;
if (ReadWord(dns, dnslen, remlen, item.ttl) == false)
return false;

return ReadData(dns, dnslen, remlen, item);
}

int BindUtil::ParseDnsQuery(uint8_t *buf, DnsItems &items) {
dnshdr *dns = (dnshdr *) buf;
uint16_t ques_rrcount = ntohs(dns->ques_rrcount);
bool BindUtil::ParseDnsQuery(uint8_t *dns, uint16_t dnslen, uint16_t *parsed_length,
DnsItems &items) {
uint16_t xid = 0;
*parsed_length = 0;
if (dnslen <= sizeof(dnshdr)) {
DNS_BIND_TRACE(DnsBindError,
"Invalid DNS Query with header missing - dropping it");
return false;
}

uint8_t *ptr = (uint8_t *) (dns + 1);
dnshdr *hdr = (dnshdr *) dns;
xid = ntohs(hdr->xid);
uint16_t ques_rrcount = ntohs(hdr->ques_rrcount);

int remlen = dnslen - sizeof(dnshdr);
for (unsigned int i = 0; i < ques_rrcount; ++i) {
DnsItem item;
item.offset = (ptr - buf) | 0xC000;
ptr = ReadQuestionEntry(buf, ptr, item);
item.offset = (dnslen - remlen) | 0xC000;
if (ReadQuestionEntry(dns, dnslen, &remlen, item) == false) {
DNS_BIND_TRACE(DnsBindError, "DNS Query Parse error in question "
"section - xid : " << xid << " - dropping it");
return false;
}
items.push_back(item);
}

return ptr - buf;
*parsed_length = dnslen - remlen;
return true;

}

bool BindUtil::ParseDnsResponse(uint8_t *buf, uint16_t &xid, dns_flags &flags,
DnsItems &ques, DnsItems &ans,
bool BindUtil::ParseDnsResponse(uint8_t *dns, uint16_t dnslen, uint16_t &xid,
dns_flags &flags, DnsItems &ques, DnsItems &ans,
DnsItems &auth, DnsItems &add) {
dnshdr *dns = (dnshdr *) buf;
xid = ntohs(dns->xid);
flags = dns->flags;
if (dnslen < sizeof(dnshdr)) {
DNS_BIND_TRACE(DnsBindError,
"Invalid DNS Response with header missing - dropping it");
return false;
}

dnshdr *hdr = (dnshdr *) dns;
xid = ntohs(hdr->xid);
flags = hdr->flags;

uint16_t ques_rrcount = ntohs(dns->ques_rrcount);
uint16_t ans_rrcount = ntohs(dns->ans_rrcount);
uint16_t auth_rrcount = ntohs(dns->auth_rrcount);
uint16_t add_rrcount = ntohs(dns->add_rrcount);
uint16_t ques_rrcount = ntohs(hdr->ques_rrcount);
uint16_t ans_rrcount = ntohs(hdr->ans_rrcount);
uint16_t auth_rrcount = ntohs(hdr->auth_rrcount);
uint16_t add_rrcount = ntohs(hdr->add_rrcount);

int remlen = dnslen - sizeof(dnshdr);
std::string errmsg;

// question section
uint8_t *ptr = (uint8_t *) (dns + 1);
for (unsigned int i = 0; i < ques_rrcount; ++i) {
DnsItem item;
ptr = ReadQuestionEntry(buf, ptr, item);
if (ReadQuestionEntry(dns, dnslen, &remlen, item) == false) {
errmsg = "Parse error in question section";
goto error;
}
ques.push_back(item);
}

// answer section
for (unsigned int i = 0; i < ans_rrcount; ++i) {
DnsItem item;
ptr = ReadAnswerEntry(buf, ptr, item);
if (!ptr) goto error;
if (ReadAnswerEntry(dns, dnslen, &remlen, item) == false) {
errmsg = "Parse error in answer section";
goto error;
}
ans.push_back(item);
}

// authority section
for (unsigned int i = 0; i < auth_rrcount; ++i) {
DnsItem item;
ptr = ReadAnswerEntry(buf, ptr, item);
if (!ptr) goto error;
if (ReadAnswerEntry(dns, dnslen, &remlen, item) == false) {
errmsg = "Parse error in authority section";
goto error;
}
auth.push_back(item);
}

// additional section
for (unsigned int i = 0; i < add_rrcount; ++i) {
DnsItem item;
ptr = ReadAnswerEntry(buf, ptr, item);
if (!ptr) goto error;
if (ReadAnswerEntry(dns, dnslen, &remlen, item) == false) {
errmsg = "Parse error in additional section";
goto error;
}
add.push_back(item);
}

return true;

error:
DNS_BIND_TRACE(DnsBindError,
"Unsupported type in DNS response." <<
"Invalid DNS response : " << errmsg <<
" xid : " << xid << " - dropping it");
return false;
}

int BindUtil::ParseDnsUpdate(uint8_t *buf, DnsUpdateData &data) {
dnshdr *dns = (dnshdr *) buf;
uint16_t zone_count = ntohs(dns->ques_rrcount);
uint16_t prereq_count = ntohs(dns->ans_rrcount);
uint16_t update_count = ntohs(dns->auth_rrcount);
bool BindUtil::ParseDnsUpdate(uint8_t *dns, uint16_t dnslen,
DnsUpdateData &data) {
if (dnslen <= sizeof(dnshdr)) {
DNS_BIND_TRACE(DnsBindError, "Invalid DNS Update with header missing "
"- dropping it");
return false;
}

dnshdr *hdr = (dnshdr *) dns;
uint16_t zone_count = ntohs(hdr->ques_rrcount);
uint16_t prereq_count = ntohs(hdr->ans_rrcount);
uint16_t update_count = ntohs(hdr->auth_rrcount);
uint16_t xid = ntohs(hdr->xid);

if (zone_count != 1) {
DNS_BIND_TRACE(DnsBindError,
"Invalid zone count in Update request : " << zone_count);
return 0;
return false;
}

if (prereq_count != 0) {
// Not supporting pre-requisites now
DNS_BIND_TRACE(DnsBindError,
"Update has pre-requisites; dropping the request");
return 0;
DNS_BIND_TRACE(DnsBindError, "Update has pre-requisites, " <<
"which is not supported; dropping the request");
return false;
}

uint8_t *ptr = (uint8_t *) (dns + 1);
int remlen = dnslen - sizeof(dnshdr);
std::string errmsg;

// Read zone
uint16_t zone_type, zone_class, plen, offset;
ptr = ReadName(buf, ptr, data.zone, plen, offset);
ptr = ReadShort(ptr, zone_type);
ptr = ReadShort(ptr, zone_class);
if (ReadName(dns, dnslen, &remlen, data.zone, plen, offset) == false ||
ReadShort(dns, dnslen, &remlen, zone_type) == false ||
ReadShort(dns, dnslen, &remlen, zone_class) == false) {
errmsg = "Parse error in reading zone";
goto error;
}

// Read Updates
for (unsigned int i = 0; i < update_count; ++i) {
DnsItem item;
ptr = ReadName(buf, ptr, item.name, plen, offset);
ptr = ReadShort(ptr, item.type);
ptr = ReadShort(ptr, item.eclass);
ptr = ReadWord(ptr, item.ttl);
ptr = ReadData(buf, ptr, item);
if (ptr == NULL) {
DNS_BIND_TRACE(DnsBindError,
"Unsupported type in Update request : dropping");
return 0;
if (ReadName(dns, dnslen, &remlen, item.name, plen, offset) == false ||
ReadShort(dns, dnslen, &remlen, item.type) == false ||
ReadShort(dns, dnslen, &remlen, item.eclass) == false ||
ReadWord(dns, dnslen, &remlen, item.ttl) == false ||
ReadData(dns, dnslen, &remlen, item) == false) {
errmsg = "Parse error";
goto error;
}
data.items.push_back(item);
}

return ptr - buf;
return true;

error:
DNS_BIND_TRACE(DnsBindError,
"Invalid DNS Update : " << errmsg <<
" xid : " << xid << " - dropping it");
return false;
}

void BindUtil::BuildDnsHeader(dnshdr *dns, uint16_t xid, DnsReq req,
Expand Down

0 comments on commit 306e1e1

Please sign in to comment.