commit f12fd1e1c39a98b53b930153666170a272c02679
parent f85bebe9a2ffd1836477606708b542aa73d0d536
Author: Kirill Elagin <kirelagin@gmail.com>
Date: Fri, 9 Apr 2021 08:22:40 -0400
parent f85bebe9a2ffd1836477606708b542aa73d0d536
Author: Kirill Elagin <kirelagin@gmail.com>
Date: Fri, 9 Apr 2021 08:22:40 -0400
Merge branch 'string-length'
17 files changed, 110 insertions(+), 22 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md @@ -14,6 +14,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- Options that correspond to domain names are now limited to 255 characters + (as required by the standard). This change is not breaking, since, + even though the restriction was not represented in option types, + using a longer string would not work anyway. +- Fix: character-string data of arbitrary length is now correctly split + into string literals each of which is no longer than 255 characters, + as required by the zone file format specification. + ## [1.0.0]
diff --git a/dns/default.nix b/dns/default.nix @@ -7,8 +7,14 @@ { lib }: let - types = import ./types { inherit lib; }; - combinators = import ./combinators.nix { inherit lib; }; + dnslib = { + util = import ./util { inherit lib; }; + inherit types; + }; + types = import ./types { lib = lib'; }; + lib' = lib // { dns = dnslib; }; + + combinators = import ./combinators.nix { lib = lib'; }; evalZone = name: zone: (lib.evalModules {
diff --git a/dns/types/default.nix b/dns/types/default.nix @@ -7,6 +7,7 @@ { lib }: { + inherit (import ./simple.nix { inherit lib; }) domain-name; inherit (import ./zone.nix { inherit lib; }) zone subzone; record = import ./record.nix { inherit lib; }; records = import ./records { inherit lib; };
diff --git a/dns/types/records/CAA.nix b/dns/types/records/CAA.nix @@ -4,6 +4,8 @@ # SPDX-License-Identifier: MPL-2.0 or MIT # +# RFC 8659 + { lib }: let @@ -25,7 +27,7 @@ in description = "One of the defined property tags"; }; value = mkOption { - type = types.str; + type = types.str; # section 4.1.1: not limited in length example = "ca.example.net"; description = "Value of the property"; };
diff --git a/dns/types/records/CNAME.nix b/dns/types/records/CNAME.nix @@ -4,10 +4,12 @@ # SPDX-License-Identifier: MPL-2.0 or MIT # +# RFC 1035, 3.3.1 + { lib }: let - inherit (lib) mkOption types; + inherit (lib) dns mkOption; in @@ -15,7 +17,7 @@ in rtype = "CNAME"; options = { cname = mkOption { - type = types.str; + type = dns.types.domain-name; example = "www.test.com"; description = "A <domain-name> which specifies the canonical or primary name for the owner. The owner name is an alias"; };
diff --git a/dns/types/records/DKIM.nix b/dns/types/records/DKIM.nix @@ -7,10 +7,12 @@ # This is a “fake” record type, not actually part of DNS. # It gets compiled down to a TXT record. +# RFC 6376 + { lib }: let - inherit (lib) mkOption types; + inherit (lib) dns mkOption types; in @@ -69,7 +71,8 @@ rec { (lib.filterAttrs (k: _v: k != "selector")) (lib.mapAttrsToList (k: v: "${k}=${v}")) ]; - in ''"${lib.concatStringsSep "; " items + ";"}"''; + result = lib.concatStringsSep "; " items + ";"; + in dns.util.writeCharacterString result; nameFixup = name: self: "${self.selector}._domainkey.${name}"; }
diff --git a/dns/types/records/DMARC.nix b/dns/types/records/DMARC.nix @@ -7,10 +7,12 @@ # This is a “fake” record type, not actually part of DNS. # It gets compiled down to a TXT record. +# RFC 7208 + { lib }: let - inherit (lib) mkOption types; + inherit (lib) dns mkOption types; in @@ -98,7 +100,8 @@ rec { (lib.filterAttrs (_k: v: v != null && v != "")) (lib.mapAttrsToList (k: v: "${k}=${v}")) ]; - in ''"${lib.concatStringsSep "; " items + ";"}"''; + result = lib.concatStringsSep "; " items + ";"; + in dns.util.writeCharacterString result; nameFixup = name: _self: "_dmarc.${name}"; }
diff --git a/dns/types/records/DNSKEY.nix b/dns/types/records/DNSKEY.nix @@ -2,7 +2,10 @@ # # SPDX-License-Identifier: MPL-2.0 or MIT +# RFC 4034, 2 + { lib }: + let inherit (builtins) isInt split; inherit (lib) concatStrings flatten mkOption types;
diff --git a/dns/types/records/DS.nix b/dns/types/records/DS.nix @@ -2,7 +2,10 @@ # # SPDX-License-Identifier: MPL-2.0 or MIT +# RFC 4034, 5 + { lib }: + let inherit (lib) mkOption types;
diff --git a/dns/types/records/MX.nix b/dns/types/records/MX.nix @@ -4,10 +4,12 @@ # SPDX-License-Identifier: MPL-2.0 or MIT # +# RFC 1035, 3.3.9 + { lib }: let - inherit (lib) mkOption types; + inherit (lib) dns mkOption types; in @@ -20,7 +22,7 @@ in description = "The preference given to this RR among others at the same owner. Lower values are preferred"; }; exchange = mkOption { - type = types.str; + type = dns.types.domain-name; example = "smtp.example.com."; description = "A <domain-name> which specifies a host willing to act as a mail exchange for the owner name"; };
diff --git a/dns/types/records/NS.nix b/dns/types/records/NS.nix @@ -4,10 +4,12 @@ # SPDX-License-Identifier: MPL-2.0 or MIT # +# RFC 1035, 3.3.11 + { lib }: let - inherit (lib) mkOption types; + inherit (lib) dns mkOption; in @@ -15,7 +17,7 @@ in rtype = "NS"; options = { nsdname = mkOption { - type = types.str; + type = dns.types.domain-name; example = "ns2.example.com"; description = "A <domain-name> which specifies a host which should be authoritative for the specified class and domain"; };
diff --git a/dns/types/records/PTR.nix b/dns/types/records/PTR.nix @@ -4,10 +4,12 @@ # SPDX-License-Identifier: MPL-2.0 or MIT # +# RFC 1035, 3.3.12 + { lib }: let - inherit (lib) mkOption types; + inherit (lib) dns mkOption; in @@ -15,7 +17,7 @@ in rtype = "PTR"; options = { ptrdname = mkOption { - type = types.str; + type = dns.types.domain-name; example = "4-3-2-1.dynamic.example.com."; description = "A <domain-name> which points to some location in the domain name space"; };
diff --git a/dns/types/records/SOA.nix b/dns/types/records/SOA.nix @@ -4,11 +4,13 @@ # SPDX-License-Identifier: MPL-2.0 or MIT # +# RFC 1035, 3.3.13 + { lib }: let inherit (lib) concatStringsSep removeSuffix replaceStrings; - inherit (lib) mkOption types; + inherit (lib) dns mkOption types; in @@ -16,12 +18,12 @@ in rtype = "SOA"; options = { nameServer = mkOption { - type = types.str; + type = dns.types.domain-name; example = "ns1.example.com"; description = "The <domain-name> of the name server that was the original or primary source of data for this zone. Don't forget the dot at the end!"; }; adminEmail = mkOption { - type = types.str; + type = dns.types.domain-name; example = "admin@example.com"; description = "An email address of the person responsible for this zone. (Note: in traditional zone files you are supposed to put a dot instead of `@` in your address; you can use `@` with this module and it is recommended to do so. Also don't put the dot at the end!)"; apply = s: replaceStrings ["@"] ["."] (removeSuffix "." s);
diff --git a/dns/types/records/SRV.nix b/dns/types/records/SRV.nix @@ -4,10 +4,12 @@ # SPDX-License-Identifier: MPL-2.0 or MIT # +# RFC 2782 + { lib }: let - inherit (lib) mkOption types; + inherit (lib) dns mkOption types; in @@ -42,7 +44,7 @@ in description = "The port on this target host of this service"; }; target = mkOption { - type = types.str; + type = dns.types.domain-name; example = ""; description = "The domain name of the target host"; };
diff --git a/dns/types/records/TXT.nix b/dns/types/records/TXT.nix @@ -4,10 +4,12 @@ # SPDX-License-Identifier: MPL-2.0 or MIT # +# RFC 1035, 3.3.14 + { lib }: let - inherit (lib) mkOption types; + inherit (lib) dns mkOption types; in @@ -20,6 +22,6 @@ in description = "Arbitrary information"; }; }; - dataToString = {data, ...}: ''"${data}"''; + dataToString = { data, ... }: dns.util.writeCharacterString data; fromString = data: { inherit data; }; }
diff --git a/dns/types/simple.nix b/dns/types/simple.nix @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2021 Kirill Elagin <https://kir.elagin.me/> +# +# SPDX-License-Identifier: MPL-2.0 or MIT + +{ lib }: + +let + inherit (builtins) stringLength; + +in + +{ + # RFC 1035, 3.1 + domain-name = lib.types.addCheck lib.types.str (s: stringLength s <= 255); +}
diff --git a/dns/util/default.nix b/dns/util/default.nix @@ -0,0 +1,28 @@ +# SPDX-FileCopyrightText: 2021 Kirill Elagin <https://kir.elagin.me/> +# +# SPDX-License-Identifier: MPL-2.0 or MIT + +{ lib }: + +let + inherit (builtins) genList stringLength substring; + inherit (lib.strings) concatMapStringsSep; + + # : int -> str -> [str], such that each output str is <= n bytes + splitInGroupsOf = n: s: + let + groupCount = (stringLength s - 1) / n + 1; + in genList (i: substring (i * n) n s) groupCount; + + # : str -> str + # Prepares a Nix string to be written to a zone file as a character-string + # literal: breaks it into chunks of 255 (per RFC 1035, 3.3) and encloses + # each chunk in quotation marks. + writeCharacterString = s: + if stringLength s <= 255 + then ''"${s}"'' + else concatMapStringsSep " " (x: ''"${x}"'') (splitInGroupsOf 255 s); + +in { + inherit writeCharacterString; +}