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::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 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 vector_view<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
|
||||
/// fully qualified domain string and the query type (qtype). At the
|
||||
/// appropriate time we will call operator() which prints a properly binary-
|
||||
/// Helper class to construct or parse a question. An object is constructed
|
||||
/// with a fully qualified domain string and the query type (qtype). At the
|
||||
/// appropriate time we will call print() which prints a properly binary-
|
||||
/// formatted question for the question section in a DNS query; generally the
|
||||
/// user does not need to do this.
|
||||
///
|
||||
|
@ -52,36 +55,41 @@ namespace ircd::rfc1035
|
|||
///
|
||||
struct ircd::rfc1035::question
|
||||
{
|
||||
string_view fqdn;
|
||||
uint16_t qtype;
|
||||
uint16_t qclass {0x01};
|
||||
size_t namelen {0};
|
||||
char name[192];
|
||||
|
||||
/// 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
|
||||
question(const string_view &fqdn, const uint16_t &qtype = 0x01) // "A"
|
||||
:fqdn{fqdn}
|
||||
,qtype{qtype}
|
||||
{}
|
||||
/// Supply fully qualified domain name and numerical query type
|
||||
question(const string_view &fqdn, const uint16_t &qtype);
|
||||
|
||||
/// Supply fully qualified domain name and name of query type i.e "A"
|
||||
question(const string_view &fqdn, const string_view &qtype)
|
||||
:fqdn{fqdn}
|
||||
,qtype{rfc1035::qtype.at(qtype)}
|
||||
:question{fqdn, rfc1035::qtype.at(qtype)}
|
||||
{}
|
||||
|
||||
question() = default;
|
||||
};
|
||||
|
||||
/// Helper class to parse an answer.
|
||||
///
|
||||
struct ircd::rfc1035::answer
|
||||
{
|
||||
uint16_t qtype;
|
||||
uint16_t qclass;
|
||||
uint32_t ttl;
|
||||
uint16_t rdlength;
|
||||
const_raw_buffer rdata;
|
||||
char name[256];
|
||||
const_buffer rdata;
|
||||
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
|
||||
|
@ -105,6 +113,8 @@ struct ircd::rfc1035::header
|
|||
uint16_t ancount; ///< number of answer entries
|
||||
uint16_t nscount; ///< number of authority entries
|
||||
uint16_t arcount; ///< number of resource entries
|
||||
|
||||
std::string debug() const;
|
||||
}
|
||||
__attribute__((packed));
|
||||
|
||||
|
|
182
ircd/rfc1035.cc
182
ircd/rfc1035.cc
|
@ -19,18 +19,20 @@
|
|||
|
||||
ircd::mutable_buffer
|
||||
ircd::rfc1035::make_query(const mutable_buffer &out,
|
||||
const uint16_t &id,
|
||||
const question &q)
|
||||
{
|
||||
const ilist<const question> questions{q};
|
||||
return make_query(out, questions);
|
||||
return make_query(out, id, questions);
|
||||
}
|
||||
|
||||
ircd::mutable_buffer
|
||||
ircd::rfc1035::make_query(const mutable_buffer &out,
|
||||
const uint16_t &id,
|
||||
const vector_view<const question> &questions)
|
||||
{
|
||||
header h{0};
|
||||
h.id = rand::integer(1, 65535);
|
||||
h.id = id;
|
||||
h.qdcount = bswap(uint16_t(questions.size()));
|
||||
return make_query(out, h, questions);
|
||||
}
|
||||
|
@ -45,9 +47,9 @@ ircd::rfc1035::make_query(const mutable_buffer &out,
|
|||
|
||||
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);
|
||||
|
@ -56,19 +58,53 @@ ircd::rfc1035::make_query(const mutable_buffer &out,
|
|||
for(const auto &question : questions)
|
||||
sb([&question](const mutable_buffer &buf)
|
||||
{
|
||||
return size(question(buf));
|
||||
return size(question.print(buf));
|
||||
});
|
||||
|
||||
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::rfc1035::question::operator()(const mutable_buffer &buf)
|
||||
ircd::rfc1035::question::print(const mutable_buffer &buf)
|
||||
const
|
||||
{
|
||||
const size_t required
|
||||
{
|
||||
1 + size(fqdn) + 1 + 2 + 2
|
||||
namelen + 1 + 2 + 2
|
||||
};
|
||||
|
||||
if(unlikely(size(buf) < required))
|
||||
|
@ -77,18 +113,10 @@ const
|
|||
"Not enough space in question buffer; %zu bytes required", required
|
||||
};
|
||||
|
||||
char *pos{data(buf)};
|
||||
ircd::tokens(fqdn, '.', [&pos, &buf](const string_view &label)
|
||||
char *pos
|
||||
{
|
||||
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(buf)));
|
||||
});
|
||||
data(buf) + copy(buf, const_buffer{name, namelen})
|
||||
};
|
||||
|
||||
assert(*pos == '\0'); pos += 1;
|
||||
*(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
|
||||
{
|
||||
"Answer input buffer is too small"
|
||||
};
|
||||
|
||||
const uint8_t *pos(data(in));
|
||||
if(*pos & uint8_t(128))
|
||||
throw error
|
||||
{
|
||||
"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))
|
||||
namelen = parse_name(name, in);
|
||||
const char *pos(data(in) + namelen);
|
||||
if(unlikely(pos + 2 + 2 + 4 + 2 > end(in)))
|
||||
throw error
|
||||
{
|
||||
"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;
|
||||
rdlength = bswap(*(const uint16_t *)pos); pos += 2;
|
||||
|
||||
if(qclass != 1)
|
||||
if(unlikely(qclass != 1))
|
||||
throw error
|
||||
{
|
||||
"Resource record not for IN (internet); corrupt data?"
|
||||
};
|
||||
|
||||
if(pos + rdlength > end(in))
|
||||
if(unlikely(pos + rdlength > end(in)))
|
||||
throw error
|
||||
{
|
||||
"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)
|
||||
|
|
Loading…
Reference in a new issue