hare

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

commit 92736948b8e09bc346946fbb2bdf7e2655f847f2
parent 7acb0566920a1f74fc21fb8a7411c852b3249374
Author: Conrad Hoffmann <ch@bitfehler.net>
Date:   Thu, 10 Aug 2023 17:30:53 +0200

net::dns: support for OPT records (EDNS, RFC 6891)

Usability could be improved, but this is enough to allow an application
to use EDNS(0), which is a requirement for e.g. DNSSEC.

Signed-off-by: Conrad Hoffmann <ch@bitfehler.net>

Diffstat:
Mnet/dns/decode.ha | 30++++++++++++++++++++++++++++++
Mnet/dns/encode.ha | 13+++++++++++++
Mnet/dns/types.ha | 20+++++++++++++++++++-
3 files changed, 62 insertions(+), 1 deletion(-)

diff --git a/net/dns/decode.ha b/net/dns/decode.ha @@ -212,6 +212,8 @@ fn decode_rdata(dec: *decoder, rtype: rtype, rlen: size) (rdata | format) = { return decode_mx(&sub); case rtype::NS => return decode_ns(&sub); + case rtype::OPT => + return decode_opt(&sub); case rtype::PTR => return decode_ptr(&sub); case rtype::SOA => @@ -295,6 +297,34 @@ fn decode_ns(dec: *decoder) (rdata | format) = { }; }; +fn decode_opt(dec: *decoder) (rdata | format) = { + let success = false; + let r = opt { + options = [], + }; + defer if (!success) { + for (let i = 0z; i < len(r.options); i += 1) { + free(r.options[i].data); + }; + free(r.options); + }; + for (len(dec.cur) > 0) { + let o = edns_opt { + code = decode_u16(dec)?, + data = [], + }; + let sz = decode_u16(dec)?; + if (len(dec.cur) < sz) { + return format; + }; + append(o.data, dec.cur[..sz]...); + dec.cur = dec.cur[sz..]; + append(r.options, o); + }; + success = true; + return r; +}; + fn decode_ptr(dec: *decoder) (rdata | format) = { return ptr { name = decode_name(dec)?, diff --git a/net/dns/encode.ha b/net/dns/encode.ha @@ -106,6 +106,8 @@ fn encode_rdata(enc: *encoder, rdata: rdata) (void | error) = { match (rdata) { case let d: unknown_rdata => return encode_raw(enc, d); + case let d: opt => + return encode_opt(enc, d); case let d: txt => return encode_txt(enc, d); case => @@ -113,6 +115,17 @@ fn encode_rdata(enc: *encoder, rdata: rdata) (void | error) = { }; }; +fn encode_opt(enc: *encoder, opt: opt) (void | error) = { + for (let i = 0z; i < len(opt.options); i += 1) { + if (len(opt.options[i].data) > 65535) { + return errors::invalid; + }; + encode_u16(enc, opt.options[i].code)?; + encode_u16(enc, len(opt.options[i].data): u16)?; + encode_raw(enc, opt.options[i].data)?; + }; +}; + fn encode_txt(enc: *encoder, txt: txt) (void | error) = { for (let i = 0z; i < len(txt); i += 1) { if (len(txt[i]) > 255) return errors::invalid; diff --git a/net/dns/types.ha b/net/dns/types.ha @@ -17,6 +17,7 @@ export type rtype = enum u16 { TXT = 16, AAAA = 28, SRV = 33, + OPT = 41, SSHFP = 44, DNSKEY = 48, TSIG = 250, @@ -34,6 +35,7 @@ export type qtype = enum u16 { TXT = 16, AAAA = 28, SRV = 33, + OPT = 41, SSHFP = 44, DNSKEY = 48, // ... @@ -144,6 +146,12 @@ export type rrecord = struct { rdata: rdata, }; +// An EDNS (RFC 6891) option, as contained in [[opt]] records. +export type edns_opt = struct { + code: u16, + data: []u8, +}; + // An A record. export type a = ip::addr4; @@ -173,6 +181,11 @@ export type ns = struct { name: []str, }; +// An OPT record (EDNS, RFC 6891). +export type opt = struct { + options: []edns_opt, +}; + // A PTR record. export type ptr = struct { name: []str, @@ -223,7 +236,7 @@ export type txt = [][]u8; export type unknown_rdata = []u8; // Tagged union of supported rdata types. -export type rdata = (a | aaaa | caa | cname | mx | ns | ptr | soa | srv +export type rdata = (a | aaaa | caa | cname | mx | ns | opt | ptr | soa | srv | sshfp | tsig | txt | unknown_rdata); // A DNS message, Hare representation. See [[encode]] and [[decode]] for the DNS @@ -276,6 +289,11 @@ fn rrecord_finish(rr: *rrecord) void = { strings::freeall(mx.name); case let ns: ns => strings::freeall(ns.name); + case let opt: opt => + for (let i = 0z; i < len(opt.options); i += 1) { + free(opt.options[i].data); + }; + free(opt.options); case let ptr: ptr => strings::freeall(ptr.name); case let so: soa =>