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:
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 =>