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:
parent
68b408a065
commit
7057e1d98d
2 changed files with 165 additions and 61 deletions
|
@ -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));
|
||||||
|
|
||||||
|
|
182
ircd/rfc1035.cc
182
ircd/rfc1035.cc
|
@ -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)
|
||||||
|
|
Loading…
Reference in a new issue