commit d2a2845657c24023d126026b8e420ee2b89434fc
parent 77fa67b9b3bab2ce965da147f9b7dc67a40d66e7
Author: Drew DeVault <sir@cmpwn.com>
Date: Sun, 20 Jun 2021 10:27:02 -0400
net::dns: draft remainder of decoder API
Signed-off-by: Drew DeVault <sir@cmpwn.com>
Diffstat:
2 files changed, 98 insertions(+), 3 deletions(-)
diff --git a/net/dns/encoding.ha b/net/dns/encoding.ha
@@ -21,12 +21,24 @@ export fn encode(buf: []u8, msg: *message) size = {
for (let i = 0z; i < len(msg.questions); i += 1) {
z += question_encode(buf[z..], &msg.questions[i]);
};
+ for (let i = 0z; i < len(msg.answers); i += 1) {
+ z += rrecord_encode(buf[z..], &msg.answers[i]);
+ };
+ for (let i = 0z; i < len(msg.authority); i += 1) {
+ z += rrecord_encode(buf[z..], &msg.authority[i]);
+ };
+ for (let i = 0z; i < len(msg.additional); i += 1) {
+ z += rrecord_encode(buf[z..], &msg.additional[i]);
+ };
return z;
};
-// Decodes a DNS message.
-export fn decode(buf: []u8, msg: *message) void = {
+// Decodes a DNS message's header only, leaving the other fields unmodified. See
+// [[decode]] to completely decode the message (requiring the use of the heap),
+// or [[decode_question]] and [[decode_rrecord]] for the iterative decoders
+// (which can be used statically).
+export fn decode_header(buf: []u8, msg: *message) size = {
let z = 0z;
msg.header.id = endian::begetu16(buf[z..]);
z += 2;
@@ -41,6 +53,43 @@ export fn decode(buf: []u8, msg: *message) void = {
z += 2;
msg.header.arcount = endian::begetu16(buf[z..]);
z += 2;
+ return z;
+};
+
+// Decodes a DNS message, heap allocating the resources necessary to represent
+// it in Hare's type system. The caller must use [[message_free]] to free the
+// return value.
+export fn decode(buf: []u8) *message = {
+ abort(); // TODO
+ return alloc(message { ... });
+};
+
+// Partially decodes a [[question]], returning a byte slice (borrowed from the
+// buf parameter) which represents the value of the qname field (leaving the
+// corresponding field in the question structure untouched). See [[name_decode]]
+// to interpret the return value.
+export fn decode_question(buf: []u8, q: *question) []u8 = {
+ abort(); // TODO
+ return [];
+};
+
+// Partially decodes a [[rrecord]], returning a byte slice (borrowed from the
+// buf parameter) which represents the value of the name field (leaving the
+// corresponding field in the question structure untouched). See [[name_decode]]
+// to interpret the return value.
+export fn decode_rrecord(buf: []u8, r: *rrecord) []u8 = {
+ abort(); // TODO
+ return [];
+};
+
+// Decodes a name from a question or resource record, returning the decoded name
+// and the remainder of the buffer. The caller should pass the returned buffer
+// into decode_name again to retrieve the next name. When the return value is an
+// empty string, all of the names have been decoded. It is a programming error
+// to call decode_name again after this, and the program will abort.
+export fn decode_name(buf: []u8) ([]u8, str) = {
+ abort(); // TODO
+ return (buf, "");
};
fn question_encode(buf: []u8, q: *question) size = {
@@ -64,6 +113,36 @@ fn question_encode(buf: []u8, q: *question) size = {
return z;
};
+fn rrecord_encode(buf: []u8, r: *rrecord) size = {
+ // TODO: Assert that the labels are all valid ASCII?
+ let z = 0z;
+ for (let i = 0z; i < len(r.name); i += 1) {
+ assert(len(r.name[i]) < 256);
+ buf[z] = len(r.name[i]): u8;
+ z += 1;
+ let label = fmt::bsprintf(buf[z..], "{}", r.name[i]);
+ z += len(label);
+ };
+ // Root
+ buf[z] = 0;
+ z += 1;
+
+ endian::beputu16(buf[z..], r.rtype);
+ z += 2;
+ endian::beputu16(buf[z..], r.class);
+ z += 2;
+ endian::beputu32(buf[z..], r.ttl);
+ z += 4;
+
+ assert(len(r.rdata) <= 0xFFFF);
+ endian::beputu16(buf[z..], len(r.rdata): u16);
+ z += 2;
+
+ buf[z..len(r.rdata)] = r.rdata[..];
+ z += len(r.rdata);
+ return z;
+};
+
fn op_encode(op: *op) u16 = endian::htonu16(
(op.qr: u16 << 15u16) |
(op.opcode: u16 << 11u16) |
diff --git a/net/dns/types.ha b/net/dns/types.ha
@@ -115,10 +115,26 @@ export type question = struct {
qclass: qclass,
};
+// A resource record item.
+export type rrecord = struct {
+ name: []str,
+ rtype: rtype,
+ class: class,
+ ttl: u32,
+ rdata: []u8,
+};
+
// A DNS message, Hare representation. See [[encode]] and [[decode]] for the DNS
// representation.
export type message = struct {
header: header,
questions: []question,
- // TODO: rest of the stuff
+ answers: []rrecord,
+ authority: []rrecord,
+ additional: []rrecord,
+};
+
+// Frees a [[message]] and the resources associated with it.
+export fn message_free(msg: *message) void = {
+ abort(); // TODO
};