commit 77fa67b9b3bab2ce965da147f9b7dc67a40d66e7
parent 146143d85f20ae5217d4a9d886c6dfc33cc416ad
Author: Drew DeVault <sir@cmpwn.com>
Date: Sun, 20 Jun 2021 10:11:54 -0400
net::dns: initial riggings
Signed-off-by: Drew DeVault <sir@cmpwn.com>
Diffstat:
4 files changed, 269 insertions(+), 0 deletions(-)
diff --git a/net/dns/encoding.ha b/net/dns/encoding.ha
@@ -0,0 +1,104 @@
+// TODO: Error handling
+use endian;
+use fmt;
+
+// Encodes a DNS message, returning its size.
+export fn encode(buf: []u8, msg: *message) size = {
+ let z = 0z;
+ endian::beputu16(buf[z..], msg.header.id);
+ z += 2;
+ endian::beputu16(buf[z..], op_encode(&msg.header.op));
+ z += 2;
+ endian::beputu16(buf[z..], msg.header.qdcount);
+ z += 2;
+ endian::beputu16(buf[z..], msg.header.ancount);
+ z += 2;
+ endian::beputu16(buf[z..], msg.header.nscount);
+ z += 2;
+ endian::beputu16(buf[z..], msg.header.arcount);
+ z += 2;
+
+ for (let i = 0z; i < len(msg.questions); i += 1) {
+ z += question_encode(buf[z..], &msg.questions[i]);
+ };
+
+ return z;
+};
+
+// Decodes a DNS message.
+export fn decode(buf: []u8, msg: *message) void = {
+ let z = 0z;
+ msg.header.id = endian::begetu16(buf[z..]);
+ z += 2;
+ let rawop = endian::begetu16(buf[z..]);
+ op_decode(rawop, &msg.header.op);
+ z += 2;
+ msg.header.qdcount = endian::begetu16(buf[z..]);
+ z += 2;
+ msg.header.ancount = endian::begetu16(buf[z..]);
+ z += 2;
+ msg.header.nscount = endian::begetu16(buf[z..]);
+ z += 2;
+ msg.header.arcount = endian::begetu16(buf[z..]);
+ z += 2;
+};
+
+fn question_encode(buf: []u8, q: *question) size = {
+ // TODO: Assert that the labels are all valid ASCII?
+ let z = 0z;
+ for (let i = 0z; i < len(q.qname); i += 1) {
+ assert(len(q.qname[i]) < 256);
+ buf[z] = len(q.qname[i]): u8;
+ z += 1;
+ let label = fmt::bsprintf(buf[z..], "{}", q.qname[i]);
+ z += len(label);
+ };
+ // Root
+ buf[z] = 0;
+ z += 1;
+ // Trailers
+ endian::beputu16(buf[z..], q.qtype);
+ z += 2;
+ endian::beputu16(buf[z..], q.qclass);
+ z += 2;
+ return z;
+};
+
+fn op_encode(op: *op) u16 = endian::htonu16(
+ (op.qr: u16 << 15u16) |
+ (op.opcode: u16 << 11u16) |
+ (if (op.aa) 0b0000010000000000u16 else 0u16) |
+ (if (op.tc) 0b0000001000000000u16 else 0u16) |
+ (if (op.rd) 0b0000000100000000u16 else 0u16) |
+ (if (op.ra) 0b0000000010000000u16 else 0u16) |
+ op.rcode: u16);
+
+fn op_decode(in: u16, out: *op) void = {
+ let in = endian::ntohu16(in);
+ out.qr = ((in & 0b1000000000000000) >> 15): qr;
+ out.opcode = ((in & 0b01111000000000u16) >> 11): opcode;
+ out.aa = in & 0b0000010000000000u16 != 0;
+ out.tc = in & 0b0000001000000000u16 != 0;
+ out.rd = in & 0b0000000100000000u16 != 0;
+ out.ra = in & 0b0000000010000000u16 != 0;
+ out.rcode = (in & 0b1111): rcode;
+};
+
+@test fn opcode() void = {
+ let opcode = op {
+ qr = qr::RESPONSE,
+ opcode = opcode::IQUERY,
+ aa = false,
+ tc = true,
+ rd = false,
+ ra = true,
+ rcode = rcode::SERVER_FAILURE,
+ };
+ let enc = op_encode(&opcode);
+ let opcode2 = op { ... };
+ op_decode(enc, &opcode2);
+ assert(opcode.qr == opcode2.qr && opcode.opcode == opcode2.opcode &&
+ opcode.aa == opcode2.aa && opcode.tc == opcode2.tc &&
+ opcode.rd == opcode2.rd && opcode.ra == opcode2.ra &&
+ opcode.rcode == opcode2.rcode);
+};
diff --git a/net/dns/types.ha b/net/dns/types.ha
@@ -0,0 +1,124 @@
+// TODO:
+// - Complete RFC 2535 support
+// - Look for other RFCs worth addressing
+use endian;
+
+// Record type
+export type rtype = enum u16 {
+ A = 1,
+ NS = 2,
+ CNAME = 5,
+ SOA = 6,
+ PTR = 12,
+ MX = 15,
+ TXT = 16,
+ AAAA = 28,
+ SRV = 33,
+ DNSKEY = 48,
+};
+
+// Question type
+export type qtype = enum u16 {
+ A = 1,
+ NS = 2,
+ CNAME = 5,
+ SOA = 6,
+ PTR = 12,
+ MX = 15,
+ TXT = 16,
+ AAAA = 28,
+ SRV = 33,
+ DNSKEY = 48,
+ // ...
+ AXFR = 252,
+ // *
+ ALL = 255,
+};
+
+// Class type
+export type class = enum u16 {
+ IN = 1,
+ CS = 2,
+ CH = 3,
+ HS = 4,
+};
+
+// Query class
+export type qclass = enum u16 {
+ IN = 1,
+ CS = 2,
+ CH = 3,
+ HS = 4,
+ // *
+ ANY = 255,
+};
+
+// DNS message header.
+export type header = struct {
+ id: u16,
+ op: op,
+ // Number of questions
+ qdcount: u16,
+ // Number of answers
+ ancount: u16,
+ // Number of name servers
+ nscount: u16,
+ // Number of additional resources
+ arcount: u16,
+};
+
+// Bit indicating if a header precedes a query or response.
+export type qr = enum u8 {
+ QUERY = 0,
+ RESPONSE = 1,
+};
+
+// Operation requested from resolver
+export type opcode = enum u8 {
+ QUERY = 0,
+ IQUERY = 1,
+ STATUS = 2,
+};
+
+// Response code from resolver
+export type rcode = enum u8 {
+ NO_ERROR = 0,
+ FMT_ERROR = 1,
+ SERVER_FAILURE = 2,
+ NAME_ERROR = 3,
+ NOT_IMPLEMENTED = 4,
+ REFUSED = 5,
+};
+
+// Operational information for this message.
+export type op = struct {
+ // Is this a query or a response?
+ qr: qr,
+ // Operation code
+ opcode: opcode,
+ // Authoratative answer bit
+ aa: bool,
+ // Truncation bit
+ tc: bool,
+ // Recursion desired bit
+ rd: bool,
+ // Recursion available bit
+ ra: bool,
+ // Response code
+ rcode: rcode,
+};
+
+// A question section item.
+export type question = struct {
+ qname: []str,
+ qtype: qtype,
+ qclass: qclass,
+};
+
+// 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
+};
diff --git a/scripts/gen-stdlib b/scripts/gen-stdlib
@@ -549,6 +549,14 @@ net() {
gen_ssa net io os strings net::ip errors rt fmt
}
+net_dns() {
+ printf '# net::dns\n'
+ gen_srcs net::dns \
+ encoding.ha \
+ types.ha
+ gen_ssa net::dns endian net net::udp net::ip fmt
+}
+
gensrcs_net_ip() {
gen_srcs net::ip \
ip.ha \
@@ -808,6 +816,7 @@ linux::signalfd
linux::io_uring
linux::vdso
net
+net::dns
net::ip
net::tcp
net::udp
diff --git a/stdlib.mk b/stdlib.mk
@@ -245,6 +245,10 @@ hare_stdlib_deps+=$(stdlib_linux_vdso)
stdlib_net=$(HARECACHE)/net/net.o
hare_stdlib_deps+=$(stdlib_net)
+# gen_lib net::dns
+stdlib_net_dns=$(HARECACHE)/net/dns/net_dns.o
+hare_stdlib_deps+=$(stdlib_net_dns)
+
# gen_lib net::ip
stdlib_net_ip=$(HARECACHE)/net/ip/net_ip.o
hare_stdlib_deps+=$(stdlib_net_ip)
@@ -815,6 +819,18 @@ $(HARECACHE)/net/net.ssa: $(stdlib_net_srcs) $(stdlib_rt) $(stdlib_io) $(stdlib_
@HARECACHE=$(HARECACHE) $(HAREC) $(HAREFLAGS) -o $@ -Nnet \
-t$(HARECACHE)/net/net.td $(stdlib_net_srcs)
+# net::dns
+# net::dns
+stdlib_net_dns_srcs= \
+ $(STDLIB)/net/dns/encoding.ha \
+ $(STDLIB)/net/dns/types.ha
+
+$(HARECACHE)/net/dns/net_dns.ssa: $(stdlib_net_dns_srcs) $(stdlib_rt) $(stdlib_endian) $(stdlib_net) $(stdlib_net_udp) $(stdlib_net_ip) $(stdlib_fmt)
+ @printf 'HAREC \t$@\n'
+ @mkdir -p $(HARECACHE)/net/dns
+ @HARECACHE=$(HARECACHE) $(HAREC) $(HAREFLAGS) -o $@ -Nnet::dns \
+ -t$(HARECACHE)/net/dns/net_dns.td $(stdlib_net_dns_srcs)
+
# net::ip
stdlib_net_ip_srcs= \
$(STDLIB)/net/ip/ip.ha \
@@ -1327,6 +1343,10 @@ hare_testlib_deps+=$(testlib_linux_vdso)
testlib_net=$(TESTCACHE)/net/net.o
hare_testlib_deps+=$(testlib_net)
+# gen_lib net::dns
+testlib_net_dns=$(TESTCACHE)/net/dns/net_dns.o
+hare_testlib_deps+=$(testlib_net_dns)
+
# gen_lib net::ip
testlib_net_ip=$(TESTCACHE)/net/ip/net_ip.o
hare_testlib_deps+=$(testlib_net_ip)
@@ -1917,6 +1937,18 @@ $(TESTCACHE)/net/net.ssa: $(testlib_net_srcs) $(testlib_rt) $(testlib_io) $(test
@HARECACHE=$(TESTCACHE) $(HAREC) $(TESTHAREFLAGS) -o $@ -Nnet \
-t$(TESTCACHE)/net/net.td $(testlib_net_srcs)
+# net::dns
+# net::dns
+testlib_net_dns_srcs= \
+ $(STDLIB)/net/dns/encoding.ha \
+ $(STDLIB)/net/dns/types.ha
+
+$(TESTCACHE)/net/dns/net_dns.ssa: $(testlib_net_dns_srcs) $(testlib_rt) $(testlib_endian) $(testlib_net) $(testlib_net_udp) $(testlib_net_ip) $(testlib_fmt)
+ @printf 'HAREC \t$@\n'
+ @mkdir -p $(TESTCACHE)/net/dns
+ @HARECACHE=$(TESTCACHE) $(HAREC) $(TESTHAREFLAGS) -o $@ -Nnet::dns \
+ -t$(TESTCACHE)/net/dns/net_dns.td $(testlib_net_dns_srcs)
+
# net::ip
testlib_net_ip_srcs= \
$(STDLIB)/net/ip/ip.ha \