0
0
Fork 0
mirror of https://github.com/matrix-construct/construct synced 2024-11-29 02:02:38 +01:00

ircd::rfc1035: Various question / answer development; add header debug.

This commit is contained in:
Jason Volk 2018-01-28 14:16:49 -08:00
parent 68b408a065
commit 7057e1d98d
2 changed files with 165 additions and 61 deletions

View file

@ -33,14 +33,17 @@ namespace ircd::rfc1035
extern const std::array<string_view, 25> rcode; extern const std::array<string_view, 25> rcode;
extern const std::unordered_map<string_view, uint16_t> qtype; extern const std::unordered_map<string_view, uint16_t> qtype;
const_buffer make_name(const mutable_buffer &out, const string_view &fqdn);
size_t parse_name(const mutable_buffer &out, const const_buffer &in);
mutable_buffer make_query(const mutable_buffer &, const header &, const vector_view<const question> &); mutable_buffer make_query(const mutable_buffer &, const header &, const vector_view<const question> &);
mutable_buffer make_query(const mutable_buffer &, const vector_view<const question> &); mutable_buffer make_query(const mutable_buffer &, const uint16_t &id, const vector_view<const question> &);
mutable_buffer make_query(const mutable_buffer &, const question &); mutable_buffer make_query(const mutable_buffer &, const uint16_t &id, const question &);
} }
/// Helper class to construct a question. An object is constructed with a /// Helper class to construct or parse a question. An object is constructed
/// fully qualified domain string and the query type (qtype). At the /// with a fully qualified domain string and the query type (qtype). At the
/// appropriate time we will call operator() which prints a properly binary- /// appropriate time we will call print() which prints a properly binary-
/// formatted question for the question section in a DNS query; generally the /// formatted question for the question section in a DNS query; generally the
/// user does not need to do this. /// user does not need to do this.
/// ///
@ -52,36 +55,41 @@ namespace ircd::rfc1035
/// ///
struct ircd::rfc1035::question struct ircd::rfc1035::question
{ {
string_view fqdn;
uint16_t qtype; uint16_t qtype;
uint16_t qclass {0x01}; uint16_t qclass {0x01};
size_t namelen {0};
char name[192];
/// Composes the question into buffer, returns used portion /// Composes the question into buffer, returns used portion
mutable_buffer operator()(const mutable_buffer &) const; mutable_buffer print(const mutable_buffer &) const;
const_buffer parse(const const_buffer &);
/// Trivial constructor; supply fully qualified domain name and query type /// Supply fully qualified domain name and numerical query type
question(const string_view &fqdn, const uint16_t &qtype = 0x01) // "A" question(const string_view &fqdn, const uint16_t &qtype);
:fqdn{fqdn}
,qtype{qtype}
{}
/// Supply fully qualified domain name and name of query type i.e "A" /// Supply fully qualified domain name and name of query type i.e "A"
question(const string_view &fqdn, const string_view &qtype) question(const string_view &fqdn, const string_view &qtype)
:fqdn{fqdn} :question{fqdn, rfc1035::qtype.at(qtype)}
,qtype{rfc1035::qtype.at(qtype)}
{} {}
question() = default;
}; };
/// Helper class to parse an answer.
///
struct ircd::rfc1035::answer struct ircd::rfc1035::answer
{ {
uint16_t qtype; uint16_t qtype;
uint16_t qclass; uint16_t qclass;
uint32_t ttl; uint32_t ttl;
uint16_t rdlength; uint16_t rdlength;
const_raw_buffer rdata; const_buffer rdata;
char name[256]; size_t namelen;
char name[192];
answer(const const_raw_buffer &); const_buffer parse(const const_buffer &);
answer() = default;
}; };
/// Direct representation of the DNS header. This is laid out for /// Direct representation of the DNS header. This is laid out for
@ -105,6 +113,8 @@ struct ircd::rfc1035::header
uint16_t ancount; ///< number of answer entries uint16_t ancount; ///< number of answer entries
uint16_t nscount; ///< number of authority entries uint16_t nscount; ///< number of authority entries
uint16_t arcount; ///< number of resource entries uint16_t arcount; ///< number of resource entries
std::string debug() const;
} }
__attribute__((packed)); __attribute__((packed));

View file

@ -19,18 +19,20 @@
ircd::mutable_buffer ircd::mutable_buffer
ircd::rfc1035::make_query(const mutable_buffer &out, ircd::rfc1035::make_query(const mutable_buffer &out,
const uint16_t &id,
const question &q) const question &q)
{ {
const ilist<const question> questions{q}; const ilist<const question> questions{q};
return make_query(out, questions); return make_query(out, id, questions);
} }
ircd::mutable_buffer ircd::mutable_buffer
ircd::rfc1035::make_query(const mutable_buffer &out, ircd::rfc1035::make_query(const mutable_buffer &out,
const uint16_t &id,
const vector_view<const question> &questions) const vector_view<const question> &questions)
{ {
header h{0}; header h{0};
h.id = rand::integer(1, 65535); h.id = id;
h.qdcount = bswap(uint16_t(questions.size())); h.qdcount = bswap(uint16_t(questions.size()));
return make_query(out, h, questions); return make_query(out, h, questions);
} }
@ -45,9 +47,9 @@ ircd::rfc1035::make_query(const mutable_buffer &out,
sb([&header](const mutable_buffer &buf) sb([&header](const mutable_buffer &buf)
{ {
const const_raw_buffer headbuf const const_buffer headbuf
{ {
reinterpret_cast<const uint8_t *>(&header), sizeof(header) reinterpret_cast<const char *>(&header), sizeof(header)
}; };
return copy(buf, headbuf); return copy(buf, headbuf);
@ -56,19 +58,53 @@ ircd::rfc1035::make_query(const mutable_buffer &out,
for(const auto &question : questions) for(const auto &question : questions)
sb([&question](const mutable_buffer &buf) sb([&question](const mutable_buffer &buf)
{ {
return size(question(buf)); return size(question.print(buf));
}); });
return sb.completed(); return sb.completed();
} }
ircd::rfc1035::question::question(const string_view &fqdn,
const uint16_t &qtype)
:qtype{qtype}
,namelen
{
size(make_name(mutable_buffer{name}, fqdn))
}
{
}
ircd::const_buffer
ircd::rfc1035::question::parse(const const_buffer &in)
{
if(size(in) < 2 + 2 + 2)
throw error
{
"Answer input buffer is too small"
};
namelen = parse_name(name, in);
const char *pos(data(in) + namelen);
if(pos + 2 + 2 > end(in))
throw error
{
"Question input buffer is incomplete (%zu bytes)", size(in)
};
qtype = bswap(*(const uint16_t *)pos); pos += 2;
qclass = bswap(*(const uint16_t *)pos); pos += 2;
assert(size_t(pos - data(in)) <= size(in));
return { data(in), pos };
}
ircd::mutable_buffer ircd::mutable_buffer
ircd::rfc1035::question::operator()(const mutable_buffer &buf) ircd::rfc1035::question::print(const mutable_buffer &buf)
const const
{ {
const size_t required const size_t required
{ {
1 + size(fqdn) + 1 + 2 + 2 namelen + 1 + 2 + 2
}; };
if(unlikely(size(buf) < required)) if(unlikely(size(buf) < required))
@ -77,18 +113,10 @@ const
"Not enough space in question buffer; %zu bytes required", required "Not enough space in question buffer; %zu bytes required", required
}; };
char *pos{data(buf)}; char *pos
ircd::tokens(fqdn, '.', [&pos, &buf](const string_view &label)
{ {
if(unlikely(size(label) >= 64)) data(buf) + copy(buf, const_buffer{name, namelen})
throw error };
{
"Single part of domain cannot exceed 63 characters"
};
*pos++ = size(label);
pos += strlcpy(pos, label, std::distance(pos, end(buf)));
});
assert(*pos == '\0'); pos += 1; assert(*pos == '\0'); pos += 1;
*(uint16_t *)pos = bswap(qtype); pos += 2; *(uint16_t *)pos = bswap(qtype); pos += 2;
@ -101,35 +129,18 @@ const
}; };
} }
ircd::rfc1035::answer::answer(const const_raw_buffer &in) ircd::const_buffer
ircd::rfc1035::answer::parse(const const_buffer &in)
{ {
if(size(in) < 2 + 2 + 2 + 4 + 2) if(unlikely(size(in) < 2 + 2 + 2 + 4 + 2))
throw error throw error
{ {
"Answer input buffer is too small" "Answer input buffer is too small"
}; };
const uint8_t *pos(data(in)); namelen = parse_name(name, in);
if(*pos & uint8_t(128)) const char *pos(data(in) + namelen);
throw error if(unlikely(pos + 2 + 2 + 4 + 2 > end(in)))
{
"Pointer format not implemented"
};
name[0] = '\0';
for(uint8_t len(*pos++); len && pos + len < end(in); len = *pos++)
{
const string_view label
{
reinterpret_cast<const char *>(pos), len
};
strlcat(name, label, sizeof(name));
strlcat(name, ".", sizeof(name));
pos += len;
}
if(pos + 2 + 2 + 4 + 2 > end(in))
throw error throw error
{ {
"Answer input buffer is incomplete (%zu bytes)", size(in) "Answer input buffer is incomplete (%zu bytes)", size(in)
@ -140,19 +151,102 @@ ircd::rfc1035::answer::answer(const const_raw_buffer &in)
ttl = bswap(*(const uint32_t *)pos); pos += 4; ttl = bswap(*(const uint32_t *)pos); pos += 4;
rdlength = bswap(*(const uint16_t *)pos); pos += 2; rdlength = bswap(*(const uint16_t *)pos); pos += 2;
if(qclass != 1) if(unlikely(qclass != 1))
throw error throw error
{ {
"Resource record not for IN (internet); corrupt data?" "Resource record not for IN (internet); corrupt data?"
}; };
if(pos + rdlength > end(in)) if(unlikely(pos + rdlength > end(in)))
throw error throw error
{ {
"Answer input buffer has incomplete data (rdlength: %u)", rdlength "Answer input buffer has incomplete data (rdlength: %u)", rdlength
}; };
rdata = const_raw_buffer{pos, rdlength}; rdata = const_buffer{pos, rdlength};
pos += rdlength;
assert(size_t(pos - data(in)) <= size(in));
return { data(in), pos };
}
ircd::const_buffer
ircd::rfc1035::make_name(const mutable_buffer &out,
const string_view &fqdn)
{
assert(!empty(out));
char *pos{data(out)};
ircd::tokens(fqdn, '.', [&pos, &out](const string_view &label)
{
if(unlikely(size(label) >= 64))
throw error
{
"Single part of domain cannot exceed 63 characters"
};
*pos++ = size(label);
pos += strlcpy(pos, label, std::distance(pos, end(out)));
});
return { data(out), size_t(pos - begin(out)) };
}
size_t
ircd::rfc1035::parse_name(const mutable_buffer &out,
const const_buffer &in)
{
assert(!empty(out));
if(unlikely(empty(in)))
throw error
{
"Name input buffer is too small"
};
const char *pos(data(in));
if(*pos & uint8_t(192))
{
//throw error{"Pointer format not implemented"};
out[0] = '\0';
pos += 2;
return pos - begin(in);
}
out[0] = '\0';
for(uint8_t len(*pos++); len && pos + len < end(in); len = *pos++)
{
const string_view label
{
reinterpret_cast<const char *>(pos), len
};
strlcat(out, label);
strlcat(out, ".");
pos += len;
}
return pos - begin(in);
}
std::string
ircd::rfc1035::header::debug()
const
{
std::stringstream ss;
ss << "id : " << id << '\n';
ss << "opcode : " << uint(opcode) << '\n';
ss << "rcode : " << uint(rcode) << ' ' << (rfc1035::rcode[rcode]) << '\n';
ss << "rd : " << (rd? "recursive" : "not recursive") << '\n';
ss << "tc : " << (tc? "truncated" : "not truncated") << '\n';
ss << "aa : " << (aa? "authoritative" : "not authoritative") << '\n';
ss << "qr : " << (qr? "query" : "response") << '\n';
ss << "cd : " << (cd? "checking disabled" : "checking enabled") << '\n';
ss << "ad : " << (ad? "authentic data" : "not authentic data") << '\n';
ss << "ra : " << (ra? "recursion available" : "recursion unavailable") << '\n';
ss << "qdcount : " << qdcount << '\n';
ss << "ancount : " << ancount << '\n';
ss << "nscount : " << nscount << '\n';
ss << "arcount : " << arcount << '\n';
return ss.str();
} }
decltype(ircd::rfc1035::rcode) decltype(ircd::rfc1035::rcode)