2018-04-09 21:43:24 +07:00
|
|
|
#include "dnsmessages.hh"
|
2018-04-12 21:22:35 +07:00
|
|
|
#include "record-types.hh"
|
2018-04-09 21:43:24 +07:00
|
|
|
using namespace std;
|
|
|
|
|
2018-04-13 18:37:17 +07:00
|
|
|
DNSMessageReader::DNSMessageReader(const char* in, uint16_t size)
|
|
|
|
{
|
|
|
|
if(size < sizeof(dnsheader))
|
|
|
|
throw std::runtime_error("DNS message too small");
|
|
|
|
memcpy(&dh, in, sizeof(dh));
|
2018-04-15 21:29:17 +07:00
|
|
|
payload.reserve(size-12);
|
|
|
|
payload.insert(payload.begin(), (const unsigned char*)in + 12, (const unsigned char*)in + size);
|
|
|
|
|
2018-04-16 05:14:18 +07:00
|
|
|
if(dh.qdcount) { // AXFR can skip this
|
|
|
|
xfrName(d_qname);
|
|
|
|
d_qtype = (DNSType) getUInt16();
|
|
|
|
d_qclass = (DNSClass) getUInt16();
|
|
|
|
}
|
2018-04-17 17:32:56 +07:00
|
|
|
|
2018-04-13 18:37:17 +07:00
|
|
|
if(dh.arcount) {
|
2018-04-17 17:32:56 +07:00
|
|
|
auto nowpos=payloadpos;
|
|
|
|
skipRRs(ntohs(dh.ancount) + ntohs(dh.nscount) + ntohs(dh.arcount) - 1);
|
2018-04-15 21:29:17 +07:00
|
|
|
if(getUInt8() == 0 && getUInt16() == (uint16_t)DNSType::OPT) {
|
2018-04-16 05:14:18 +07:00
|
|
|
xfrUInt16(d_bufsize);
|
2018-04-15 21:29:17 +07:00
|
|
|
getUInt8(); // extended RCODE
|
2018-04-16 05:14:18 +07:00
|
|
|
d_ednsVersion = getUInt8();
|
|
|
|
auto flags=getUInt8();
|
2018-04-13 18:37:17 +07:00
|
|
|
d_doBit = flags & 0x80;
|
2018-04-15 21:29:17 +07:00
|
|
|
getUInt8(); getUInt16(); // ignore rest
|
2018-04-13 20:23:00 +07:00
|
|
|
d_haveEDNS = true;
|
2018-04-13 18:37:17 +07:00
|
|
|
}
|
2018-04-17 17:32:56 +07:00
|
|
|
payloadpos=nowpos;
|
2018-04-13 18:37:17 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-16 05:14:18 +07:00
|
|
|
void DNSMessageReader::xfrName(DNSName& res, uint16_t* pos)
|
2018-04-09 21:43:24 +07:00
|
|
|
{
|
2018-04-16 05:14:18 +07:00
|
|
|
if(!pos) pos = &payloadpos;
|
|
|
|
res.clear();
|
2018-04-09 21:43:24 +07:00
|
|
|
for(;;) {
|
2018-04-16 05:14:18 +07:00
|
|
|
uint8_t labellen= getUInt8(pos);
|
|
|
|
if(labellen & 0xc0) {
|
|
|
|
uint16_t labellen2 = getUInt8(pos);
|
|
|
|
uint16_t newpos = ((labellen & ~0xc0) << 8) | labellen2;
|
|
|
|
newpos -= sizeof(dnsheader); // includes struct dnsheader
|
2018-04-17 17:32:56 +07:00
|
|
|
// cout<< res<<" "<<(*pos -2) << " -> "<<newpos<<endl;
|
2018-04-16 05:14:18 +07:00
|
|
|
if(newpos < *pos) {
|
|
|
|
res=res+getName(&newpos);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
throw std::runtime_error("forward compression: " + std::to_string(newpos) + " >= " + std::to_string(*pos));
|
|
|
|
}
|
|
|
|
}
|
2018-04-12 21:31:43 +07:00
|
|
|
if(!labellen) // end of DNSName
|
2018-04-09 21:43:24 +07:00
|
|
|
break;
|
2018-04-16 05:14:18 +07:00
|
|
|
DNSLabel label = getBlob(labellen, pos);
|
|
|
|
res.push_back(label);
|
2018-04-09 21:43:24 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-13 18:37:17 +07:00
|
|
|
void DNSMessageReader::getQuestion(DNSName& name, DNSType& type) const
|
2018-04-09 21:43:24 +07:00
|
|
|
{
|
2018-04-13 18:37:17 +07:00
|
|
|
name = d_qname; type = d_qtype;
|
2018-04-09 21:43:24 +07:00
|
|
|
}
|
|
|
|
|
2018-04-13 18:37:17 +07:00
|
|
|
bool DNSMessageReader::getEDNS(uint16_t* bufsize, bool* doBit) const
|
2018-04-12 21:16:31 +07:00
|
|
|
{
|
2018-04-13 18:37:17 +07:00
|
|
|
if(!d_haveEDNS)
|
|
|
|
return false;
|
|
|
|
*bufsize = d_bufsize;
|
2018-04-17 17:48:01 +07:00
|
|
|
*doBit = d_doBit;
|
2018-04-13 18:37:17 +07:00
|
|
|
return true;
|
2018-04-12 21:16:31 +07:00
|
|
|
}
|
|
|
|
|
2018-04-17 17:32:56 +07:00
|
|
|
void DNSMessageReader::skipRRs(int num)
|
|
|
|
{
|
|
|
|
for(int n = 0; n < num; ++n) {
|
|
|
|
getName();
|
|
|
|
payloadpos += 8; // type, class, ttl
|
|
|
|
auto len = getUInt16();
|
|
|
|
payloadpos += len;
|
|
|
|
if(payloadpos >= payload.size())
|
|
|
|
throw std::out_of_range("Asked to skip beyond end of packet");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-16 05:14:18 +07:00
|
|
|
bool DNSMessageReader::getRR(DNSSection& section, DNSName& name, DNSType& type, uint32_t& ttl, std::unique_ptr<RRGen>& content)
|
|
|
|
{
|
|
|
|
if(payloadpos == payload.size())
|
|
|
|
return false;
|
2018-10-11 18:57:21 +07:00
|
|
|
if(rrpos < ntohs(dh.ancount))
|
|
|
|
section = DNSSection::Answer;
|
|
|
|
else if(rrpos < ntohs(dh.ancount) + ntohs(dh.nscount))
|
|
|
|
section = DNSSection::Authority;
|
|
|
|
else
|
|
|
|
section = DNSSection::Additional;
|
|
|
|
++rrpos;
|
2018-04-16 05:14:18 +07:00
|
|
|
name = getName();
|
|
|
|
type=(DNSType)getUInt16();
|
|
|
|
/* uint16_t lclass = */ getUInt16(); // class
|
|
|
|
xfrUInt32(ttl);
|
|
|
|
auto len = getUInt16();
|
2018-04-19 17:39:43 +07:00
|
|
|
d_endofrecord = payloadpos + len;
|
|
|
|
// this should care about RP, AFSDB too (RFC3597).. if anyone cares
|
2018-04-16 17:33:17 +07:00
|
|
|
#define CONVERT(x) if(type == DNSType::x) { content = std::make_unique<x##Gen>(*this);} else
|
|
|
|
CONVERT(A) CONVERT(AAAA) CONVERT(NS) CONVERT(SOA) CONVERT(MX) CONVERT(CNAME)
|
2018-04-18 02:45:33 +07:00
|
|
|
CONVERT(NAPTR) CONVERT(SRV)
|
2018-04-29 03:57:13 +07:00
|
|
|
CONVERT(TXT) CONVERT(RRSIG)
|
2018-04-16 17:33:17 +07:00
|
|
|
CONVERT(PTR)
|
|
|
|
{
|
|
|
|
content = std::make_unique<UnknownGen>(type, getBlob(len));
|
2018-04-16 05:14:18 +07:00
|
|
|
}
|
2018-04-16 17:33:17 +07:00
|
|
|
#undef CONVERT
|
2018-04-16 05:14:18 +07:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// this is required to make the std::unique_ptr to DNSZone work. Long story.
|
2018-04-14 20:48:01 +07:00
|
|
|
DNSMessageWriter::~DNSMessageWriter() = default;
|
|
|
|
|
2018-04-17 21:22:44 +07:00
|
|
|
void DNSMessageWriter::randomizeID()
|
|
|
|
{
|
2018-04-17 22:10:32 +07:00
|
|
|
dh.id = random();
|
2018-04-17 21:22:44 +07:00
|
|
|
}
|
|
|
|
|
2018-04-16 05:14:18 +07:00
|
|
|
void DNSMessageWriter::xfrName(const DNSName& name, bool compress)
|
2018-04-14 05:12:53 +07:00
|
|
|
{
|
|
|
|
DNSName oname(name);
|
2018-04-16 05:14:18 +07:00
|
|
|
// cout<<"Attempt to emit "<<oname<<" (compress = "<<compress<<", d_nocompress= "<<d_nocompress<<")"<<endl;
|
2018-04-14 05:12:53 +07:00
|
|
|
DNSName fname(oname), flast;
|
|
|
|
|
2018-04-15 01:16:56 +07:00
|
|
|
if(compress && !d_nocompress) {
|
|
|
|
auto node = d_comptree->find(fname, flast);
|
|
|
|
|
|
|
|
if(node) {
|
2018-04-16 05:14:18 +07:00
|
|
|
// cout<<" Did lookup for "<<oname<<", left: "<<fname<<", node: "<<flast<<", pos: "<<node->namepos<<endl;
|
2018-04-17 17:32:56 +07:00
|
|
|
if(flast.size() >= 1) {
|
2018-04-15 01:16:56 +07:00
|
|
|
uint16_t pos = node->namepos;
|
2018-04-16 05:14:18 +07:00
|
|
|
// cout<<" Using the pointer we found to pos "<<pos<<", have to emit "<<fname.size()<<" labels first"<<endl;
|
|
|
|
|
|
|
|
DNSName sname(oname);
|
2018-04-15 01:16:56 +07:00
|
|
|
for(const auto& lab : fname) {
|
2018-04-16 05:14:18 +07:00
|
|
|
auto anode = d_comptree->add(sname);
|
2018-04-15 01:16:56 +07:00
|
|
|
if(!anode->namepos) {
|
2018-04-16 05:14:18 +07:00
|
|
|
// cout<<"Storing that "<<sname<<" can be found at " << payloadpos + 12 << endl;
|
|
|
|
anode->namepos = payloadpos + 12;
|
2018-04-15 01:16:56 +07:00
|
|
|
}
|
2018-04-16 05:14:18 +07:00
|
|
|
sname.pop_front();
|
|
|
|
xfrUInt8(lab.size());
|
|
|
|
xfrBlob(lab.d_s);
|
2018-04-14 05:12:53 +07:00
|
|
|
}
|
2018-04-16 05:14:18 +07:00
|
|
|
xfrUInt8((pos>>8) | (uint8_t)0xc0 );
|
|
|
|
xfrUInt8(pos & 0xff);
|
2018-04-15 01:16:56 +07:00
|
|
|
return;
|
2018-04-14 05:12:53 +07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-04-15 01:16:56 +07:00
|
|
|
// if we are here, we know we need to write out the whole thing
|
2018-04-14 05:12:53 +07:00
|
|
|
for(const auto& l : name) {
|
2018-04-15 01:16:56 +07:00
|
|
|
if(!d_nocompress) { // even with compress=false, we want to store this name, unless this is a nocompress message (AXFR)
|
|
|
|
auto anode = d_comptree->add(oname);
|
|
|
|
if(!anode->namepos) {
|
2018-04-17 17:32:56 +07:00
|
|
|
// cout<<"Storing that "<<oname<<" can be found at " << payloadpos + 12 << endl;
|
2018-04-15 01:16:56 +07:00
|
|
|
anode->namepos = payloadpos + 12;
|
|
|
|
}
|
2018-04-14 05:12:53 +07:00
|
|
|
}
|
|
|
|
oname.pop_front();
|
2018-04-16 05:14:18 +07:00
|
|
|
xfrUInt8(l.size());
|
|
|
|
xfrBlob(l.d_s);
|
2018-04-14 05:12:53 +07:00
|
|
|
}
|
2018-04-16 05:14:18 +07:00
|
|
|
xfrUInt8(0);
|
2018-04-14 05:12:53 +07:00
|
|
|
}
|
|
|
|
|
2018-04-15 01:16:56 +07:00
|
|
|
static void nboInc(uint16_t& counter) // network byte order inc
|
|
|
|
{
|
|
|
|
counter = htons(ntohs(counter) + 1);
|
|
|
|
}
|
|
|
|
|
2018-04-29 15:51:45 +07:00
|
|
|
void DNSMessageWriter::putRR(DNSSection section, const DNSName& name, uint32_t ttl, const std::unique_ptr<RRGen>& content, DNSClass dclass)
|
2018-04-09 21:43:24 +07:00
|
|
|
{
|
2018-04-12 03:12:57 +07:00
|
|
|
auto cursize = payloadpos;
|
2018-04-09 21:43:24 +07:00
|
|
|
try {
|
2018-04-16 05:14:18 +07:00
|
|
|
xfrName(name);
|
2018-04-29 15:51:45 +07:00
|
|
|
xfrUInt16((int)content->getType()); xfrUInt16((int)dclass);
|
2018-04-16 05:14:18 +07:00
|
|
|
xfrUInt32(ttl);
|
|
|
|
auto pos = xfrUInt16(0); // placeholder
|
2018-04-09 22:46:31 +07:00
|
|
|
content->toMessage(*this);
|
2018-04-16 05:14:18 +07:00
|
|
|
xfrUInt16At(pos, payloadpos-pos-2);
|
2018-04-09 21:43:24 +07:00
|
|
|
}
|
|
|
|
catch(...) {
|
2018-04-12 03:12:57 +07:00
|
|
|
payloadpos = cursize;
|
2018-04-09 21:43:24 +07:00
|
|
|
throw;
|
|
|
|
}
|
|
|
|
switch(section) {
|
|
|
|
case DNSSection::Question:
|
|
|
|
throw runtime_error("Can't add questions to a DNS Message with putRR");
|
|
|
|
case DNSSection::Answer:
|
2018-04-15 01:16:56 +07:00
|
|
|
if(dh.nscount || dh.arcount) throw runtime_error("Can't add answer RRs out of order to a DNS Message");
|
|
|
|
nboInc(dh.ancount);
|
2018-04-09 21:43:24 +07:00
|
|
|
break;
|
|
|
|
case DNSSection::Authority:
|
2018-04-15 01:16:56 +07:00
|
|
|
if(dh.arcount) throw runtime_error("Can't add authority RRs out of order to a DNS Message");
|
|
|
|
nboInc(dh.nscount);
|
2018-04-09 21:43:24 +07:00
|
|
|
break;
|
|
|
|
case DNSSection::Additional:
|
2018-04-15 01:16:56 +07:00
|
|
|
nboInc(dh.arcount);
|
2018-04-09 21:43:24 +07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-13 22:39:44 +07:00
|
|
|
void DNSMessageWriter::putEDNS(uint16_t bufsize, RCode ercode, bool doBit)
|
2018-04-12 05:04:59 +07:00
|
|
|
{
|
|
|
|
auto cursize = payloadpos;
|
|
|
|
try {
|
2018-04-16 05:14:18 +07:00
|
|
|
xfrUInt8(0); xfrUInt16((uint16_t)DNSType::OPT); // 'root' name, our type
|
|
|
|
xfrUInt16(bufsize); xfrUInt8(((int)ercode)>>4); xfrUInt8(0); xfrUInt8(doBit ? 0x80 : 0); xfrUInt8(0);
|
|
|
|
xfrUInt16(0);
|
2018-04-12 05:04:59 +07:00
|
|
|
}
|
2018-04-15 01:16:56 +07:00
|
|
|
catch(...) { // went beyond message size, roll it all back
|
2018-04-12 05:04:59 +07:00
|
|
|
payloadpos = cursize;
|
|
|
|
throw;
|
|
|
|
}
|
2018-04-15 01:16:56 +07:00
|
|
|
nboInc(dh.arcount);
|
2018-04-12 05:04:59 +07:00
|
|
|
}
|
|
|
|
|
2018-04-29 15:51:45 +07:00
|
|
|
DNSMessageWriter::DNSMessageWriter(const DNSName& name, DNSType type, DNSClass qclass, int maxsize) : d_qname(name), d_qtype(type), d_qclass(qclass)
|
2018-04-09 21:43:24 +07:00
|
|
|
{
|
2018-04-13 20:23:00 +07:00
|
|
|
memset(&dh, 0, sizeof(dh));
|
2018-04-16 05:14:18 +07:00
|
|
|
payload.resize(maxsize - sizeof(dh));
|
2018-04-13 20:23:00 +07:00
|
|
|
clearRRs();
|
2018-04-09 21:43:24 +07:00
|
|
|
}
|
|
|
|
|
2018-04-13 20:23:00 +07:00
|
|
|
void DNSMessageWriter::clearRRs()
|
2018-04-09 21:43:24 +07:00
|
|
|
{
|
2018-04-14 20:48:01 +07:00
|
|
|
d_comptree = std::make_unique<DNSNode>();
|
2018-04-13 20:23:00 +07:00
|
|
|
dh.qdcount = htons(1) ; dh.ancount = dh.arcount = dh.nscount = 0;
|
|
|
|
payloadpos=0;
|
2018-04-16 05:14:18 +07:00
|
|
|
xfrName(d_qname, false);
|
|
|
|
xfrUInt16((uint16_t)d_qtype);
|
2018-04-29 15:51:45 +07:00
|
|
|
xfrUInt16((uint16_t)d_qclass);
|
2018-04-09 21:43:24 +07:00
|
|
|
}
|
2018-04-13 20:23:00 +07:00
|
|
|
|
2018-04-14 05:12:53 +07:00
|
|
|
string DNSMessageWriter::serialize()
|
2018-04-09 21:43:24 +07:00
|
|
|
{
|
2018-04-13 20:23:00 +07:00
|
|
|
try {
|
2018-04-17 17:32:56 +07:00
|
|
|
if(haveEDNS && !d_serialized) {
|
|
|
|
d_serialized=true;
|
|
|
|
cout<<"\tAdding EDNS to DNS Message"<<endl;
|
2018-04-14 05:12:53 +07:00
|
|
|
putEDNS(payload.size() + sizeof(dnsheader), d_ercode, d_doBit);
|
2018-04-13 20:23:00 +07:00
|
|
|
}
|
2018-04-14 05:12:53 +07:00
|
|
|
std::string ret((const char*)&dh, ((const char*)&dh) + sizeof(dnsheader));
|
2018-04-16 05:14:18 +07:00
|
|
|
if(payloadpos)
|
|
|
|
ret.append((const unsigned char*)&payload.at(0), (const unsigned char*)&payload.at(payloadpos-1)+1);
|
2018-04-14 05:12:53 +07:00
|
|
|
return ret;
|
2018-04-13 20:23:00 +07:00
|
|
|
}
|
|
|
|
catch(std::out_of_range& e) {
|
2018-04-16 05:14:18 +07:00
|
|
|
cout<<"Got truncated while adding EDNS! Truncating. haveEDNS="<<haveEDNS<<", payloadpos="<<payloadpos<<endl;
|
2018-04-14 05:12:53 +07:00
|
|
|
DNSMessageWriter act(d_qname, d_qtype);
|
|
|
|
act.dh = dh;
|
2018-04-13 22:39:44 +07:00
|
|
|
act.putEDNS(payload.size() + sizeof(dnsheader), d_ercode, d_doBit);
|
2018-04-14 05:12:53 +07:00
|
|
|
std::string ret((const char*)&act.dh, ((const char*)&act.dh) + sizeof(dnsheader));
|
|
|
|
ret.append((const unsigned char*)&act.payload.at(0), (const unsigned char*)&act.payload.at(act.payloadpos));
|
|
|
|
return ret;
|
2018-04-13 20:23:00 +07:00
|
|
|
}
|
2018-04-09 21:43:24 +07:00
|
|
|
}
|
|
|
|
|
2018-04-13 22:39:44 +07:00
|
|
|
void DNSMessageWriter::setEDNS(uint16_t newsize, bool doBit, RCode ercode)
|
2018-04-13 20:23:00 +07:00
|
|
|
{
|
|
|
|
if(newsize > sizeof(dnsheader))
|
|
|
|
payload.resize(newsize - sizeof(dnsheader));
|
|
|
|
d_doBit = doBit;
|
2018-04-13 22:39:44 +07:00
|
|
|
d_ercode = ercode;
|
2018-04-13 20:23:00 +07:00
|
|
|
haveEDNS=true;
|
|
|
|
}
|