hare

The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

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:
Anet/dns/encoding.ha | 104+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Anet/dns/types.ha | 124+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mscripts/gen-stdlib | 9+++++++++
Mstdlib.mk | 32++++++++++++++++++++++++++++++++
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 \