diff options
| author | Alejandro Soto <alejandro@34project.org> | 2025-08-25 16:03:33 -0600 |
|---|---|---|
| committer | Alejandro Soto <alejandro@34project.org> | 2025-08-25 16:06:35 -0600 |
| commit | 377749b2902b67b4262c5be3c0c48c05785dc0f2 (patch) | |
| tree | 560d69fd38eaf34e85f0419d47c1b2ee03c6e992 /sys | |
| parent | 07cca17fd5168805d4c270e05833184247812a62 (diff) | |
sys/ns/rr: format
Diffstat (limited to 'sys')
| -rw-r--r-- | sys/ns/rr.nix | 611 |
1 files changed, 306 insertions, 305 deletions
diff --git a/sys/ns/rr.nix b/sys/ns/rr.nix index 8b4d119..7f089d1 100644 --- a/sys/ns/rr.nix +++ b/sys/ns/rr.nix @@ -79,366 +79,367 @@ in { zones = mkOption { default = {}; - type = with lib.types; attrsOf (submodule ({ - config, - name, - ... - }: let - nameOption = args @ { - defaultZone ? "${name}.", - permitRelative ? true, + type = with lib.types; + attrsOf (submodule ({ + config, + name, ... - }: - mkOption (removeAttrs args ["defaultZone" "permitRelative"] - // { - type = domainRefType; - - apply = value: let - zone = - throwIfNot - (hasSuffix "." defaultZone) - "zone expression '${defaultZone}' must be absolute, not relative" - defaultZone; - in - if value == "@" - then zone - else if hasSuffix "." value - then value - else if permitRelative - then "${value}.${zone}" - else throw "zone expression '${value}' in zone '${zone}' must be absolute, not relative"; - }); - - rrType = options: - mkOption { - default = []; - type = listOf (submodule { - options = - options - // { - name = nameOption {}; - - ttl = mkOption { - type = int; - default = config.defaultTTL; - }; - }; - }); - }; - - rrConfig = { - rrs, - type, - format, - applyName ? (rr: rr.name), - }: (map - (rr: { - inherit type; - inherit (rr) ttl; - - data = format rr; - name = applyName rr; - }) - rrs); - in { - options = { - local = mkOption { - type = unspecified; - default = globalConfig.local; - readOnly = true; - }; + }: let + nameOption = args @ { + defaultZone ? "${name}.", + permitRelative ? true, + ... + }: + mkOption (removeAttrs args ["defaultZone" "permitRelative"] + // { + type = domainRefType; + + apply = value: let + zone = + throwIfNot + (hasSuffix "." defaultZone) + "zone expression '${defaultZone}' must be absolute, not relative" + defaultZone; + in + if value == "@" + then zone + else if hasSuffix "." value + then value + else if permitRelative + then "${value}.${zone}" + else throw "zone expression '${value}' in zone '${zone}' must be absolute, not relative"; + }); - defaultTTL = mkOption { - type = int; - default = 3600; - }; + rrType = options: + mkOption { + default = []; + type = listOf (submodule { + options = + options + // { + name = nameOption {}; + + ttl = mkOption { + type = int; + default = config.defaultTTL; + }; + }; + }); + }; - ptrName = mkOption { - type = nullOr str; - default = null; - }; + rrConfig = { + rrs, + type, + format, + applyName ? (rr: rr.name), + }: (map + (rr: { + inherit type; + inherit (rr) ttl; + + data = format rr; + name = applyName rr; + }) + rrs); + in { + options = { + local = mkOption { + type = unspecified; + default = globalConfig.local; + readOnly = true; + }; - defaultPtr = { - v4 = mkOption { - type = nullOr str; - default = null; + defaultTTL = mkOption { + type = int; + default = 3600; }; - v6 = mkOption { + ptrName = mkOption { type = nullOr str; default = null; }; - }; - - nsdConfig = mkOption { - type = attrsOf unspecified; - default = {}; - }; - - content = mkOption { - type = lines; - readOnly = true; - }; - - nullSerialHash = mkOption { - type = nullOr str; - default = null; - }; - - rr = mkOption { - default = []; - type = - listOf - (submodule { - options = { - name = nameOption {}; - - ttl = mkOption { - type = int; - }; - - class = mkOption { - type = enum ["IN"]; - default = "IN"; - }; - - type = mkOption { - type = enum rrTypes; - }; - - data = mkOption { - type = listOf (either int str); - default = []; - }; - }; - }); - }; - soa = { - authorityZone = nameOption { - default = "@"; - permitRelative = false; - }; + defaultPtr = { + v4 = mkOption { + type = nullOr str; + default = null; + }; - ttl = mkOption { - type = int; - default = config.defaultTTL; + v6 = mkOption { + type = nullOr str; + default = null; + }; }; - primary = nameOption { - default = "ns1"; - defaultZone = config.soa.authorityZone; + nsdConfig = mkOption { + type = attrsOf unspecified; + default = {}; }; - hostmaster = mkOption { - type = emailType; - default = "hostmaster"; - - apply = address: let - split = splitString "@" address; - - user = head split; - domain = - if length split == 2 - then head (tail split) - else removeSuffix "." config.soa.authorityZone; - in - if hasSuffix "." address - then address - else "${replaceStrings ["."] ["\\."] user}.${domain}."; + content = mkOption { + type = lines; + readOnly = true; }; - serial = mkOption { - type = nullOr int; + nullSerialHash = mkOption { + type = nullOr str; default = null; }; - refresh = mkOption { - type = int; - default = 3 * 3600; + rr = mkOption { + default = []; + type = + listOf + (submodule { + options = { + name = nameOption {}; + + ttl = mkOption { + type = int; + }; + + class = mkOption { + type = enum ["IN"]; + default = "IN"; + }; + + type = mkOption { + type = enum rrTypes; + }; + + data = mkOption { + type = listOf (either int str); + default = []; + }; + }; + }); }; - retry = mkOption { - type = int; - default = 3600; - }; + soa = { + authorityZone = nameOption { + default = "@"; + permitRelative = false; + }; - expire = mkOption { - type = int; - default = 7 * 24 * 3600; - }; + ttl = mkOption { + type = int; + default = config.defaultTTL; + }; - negativeTTL = mkOption { - type = int; - default = 3600; - }; - }; + primary = nameOption { + default = "ns1"; + defaultZone = config.soa.authorityZone; + }; - a = rrType { - ipv4 = mkOption { - type = str; - }; + hostmaster = mkOption { + type = emailType; + default = "hostmaster"; - ptr = mkOption { - type = nullOr str; - default = config.defaultPtr.v4; - }; - }; + apply = address: let + split = splitString "@" address; - aaaa = rrType { - ipv6 = mkOption { - type = str; - }; + user = head split; + domain = + if length split == 2 + then head (tail split) + else removeSuffix "." config.soa.authorityZone; + in + if hasSuffix "." address + then address + else "${replaceStrings ["."] ["\\."] user}.${domain}."; + }; - ptr = mkOption { - type = nullOr str; - default = config.defaultPtr.v6; - }; - }; + serial = mkOption { + type = nullOr int; + default = null; + }; - cname = rrType { - target = nameOption {}; - }; + refresh = mkOption { + type = int; + default = 3 * 3600; + }; - mx = rrType { - host = nameOption {}; + retry = mkOption { + type = int; + default = 3600; + }; - priority = mkOption { - type = int; + expire = mkOption { + type = int; + default = 7 * 24 * 3600; + }; + + negativeTTL = mkOption { + type = int; + default = 3600; + }; }; - }; - ns = rrType { - host = nameOption {}; - }; + a = rrType { + ipv4 = mkOption { + type = str; + }; - ptr = rrType { - target = nameOption {}; - }; + ptr = mkOption { + type = nullOr str; + default = config.defaultPtr.v4; + }; + }; - srv = rrType { - host = nameOption {}; + aaaa = rrType { + ipv6 = mkOption { + type = str; + }; - port = mkOption { - type = port; + ptr = mkOption { + type = nullOr str; + default = config.defaultPtr.v6; + }; }; - priority = mkOption { - type = int; + cname = rrType { + target = nameOption {}; }; - proto = mkOption { - type = enum ["tcp" "udp"]; - }; + mx = rrType { + host = nameOption {}; - service = mkOption { - type = str; + priority = mkOption { + type = int; + }; }; - weight = mkOption { - type = int; + ns = rrType { + host = nameOption {}; }; - }; - txt = rrType { - text = mkOption { - type = strMatching "[^\"\n\\]*\n?"; - apply = removeSuffix "\n"; + ptr = rrType { + target = nameOption {}; }; - }; - }; - - config = { - nsdConfig.data = config.content; - - content = let - rrLine = rr: concatMapStringsSep " " toString ([rr.name rr.ttl rr.class rr.type] ++ rr.data); - in - '' - $ORIGIN ${name}. - $TTL ${toString config.defaultTTL} - '' - + concatLines (map rrLine config.rr); - - rr = mkMerge [ - (mkOrder 0 (singleton { - inherit (config.soa) ttl; - - name = "${name}."; - type = "SOA"; - - data = with config.soa; [ - primary - hostmaster - (throwIf (serial == null) "No serial defined for zone ${name}" serial) - refresh - retry - expire - negativeTTL - ]; - })) - - (mkOrder 1 (rrConfig { - rrs = config.ns; - type = "NS"; - format = rr: [rr.host]; - })) - - (rrConfig { - rrs = config.a; - type = "A"; - format = rr: [rr.ipv4]; - }) - (rrConfig { - rrs = config.aaaa; - type = "AAAA"; - format = rr: [rr.ipv6]; - }) + srv = rrType { + host = nameOption {}; - (rrConfig { - rrs = config.cname; - type = "CNAME"; - format = rr: [rr.target]; - }) + port = mkOption { + type = port; + }; - (rrConfig { - rrs = config.mx; - type = "MX"; - format = rr: [rr.priority rr.host]; - }) + priority = mkOption { + type = int; + }; - (rrConfig { - rrs = config.ptr; - type = "PTR"; - format = rr: [rr.target]; - }) + proto = mkOption { + type = enum ["tcp" "udp"]; + }; - (rrConfig { - rrs = config.srv; - type = "SRV"; + service = mkOption { + type = str; + }; - format = rr: [rr.priority rr.weight rr.port rr.host]; - applyName = rr: "_${rr.service}._${rr.proto}.${rr.name}"; - }) + weight = mkOption { + type = int; + }; + }; - (rrConfig { - rrs = config.txt; - type = "TXT"; + txt = rrType { + text = mkOption { + type = strMatching "[^\"\n\\]*\n?"; + apply = removeSuffix "\n"; + }; + }; + }; - format = rr: let - # nsd-zonecheck: text string is longer than 255 characters, try splitting it into multiple parts - txtFragments = text: let - max = 255; - length = stringLength text; + config = { + nsdConfig.data = config.content; + + content = let + rrLine = rr: concatMapStringsSep " " toString ([rr.name rr.ttl rr.class rr.type] ++ rr.data); + in + '' + $ORIGIN ${name}. + $TTL ${toString config.defaultTTL} + '' + + concatLines (map rrLine config.rr); + + rr = mkMerge [ + (mkOrder 0 (singleton { + inherit (config.soa) ttl; + + name = "${name}."; + type = "SOA"; + + data = with config.soa; [ + primary + hostmaster + (throwIf (serial == null) "No serial defined for zone ${name}" serial) + refresh + retry + expire + negativeTTL + ]; + })) + + (mkOrder 1 (rrConfig { + rrs = config.ns; + type = "NS"; + format = rr: [rr.host]; + })) + + (rrConfig { + rrs = config.a; + type = "A"; + format = rr: [rr.ipv4]; + }) + + (rrConfig { + rrs = config.aaaa; + type = "AAAA"; + format = rr: [rr.ipv6]; + }) + + (rrConfig { + rrs = config.cname; + type = "CNAME"; + format = rr: [rr.target]; + }) + + (rrConfig { + rrs = config.mx; + type = "MX"; + format = rr: [rr.priority rr.host]; + }) + + (rrConfig { + rrs = config.ptr; + type = "PTR"; + format = rr: [rr.target]; + }) + + (rrConfig { + rrs = config.srv; + type = "SRV"; + + format = rr: [rr.priority rr.weight rr.port rr.host]; + applyName = rr: "_${rr.service}._${rr.proto}.${rr.name}"; + }) + + (rrConfig { + rrs = config.txt; + type = "TXT"; + + format = rr: let + # nsd-zonecheck: text string is longer than 255 characters, try splitting it into multiple parts + txtFragments = text: let + max = 255; + length = stringLength text; + in + singleton (substring 0 max text) ++ optionals (length > max) (txtFragments (substring max length text)); in - singleton (substring 0 max text) ++ optionals (length > max) (txtFragments (substring max length text)); - in - map (fragment: "\"${fragment}\"") (txtFragments rr.text); - }) - ]; - }; - })); + map (fragment: "\"${fragment}\"") (txtFragments rr.text); + }) + ]; + }; + })); }; }; |
