generated from meterriblecrew/flake-template
126 lines
5.0 KiB
Nix
126 lines
5.0 KiB
Nix
|
{ lib }:
|
||
|
let
|
||
|
inherit (lib) concatStrings concatMapStringsSep concatStringsSep
|
||
|
getAttrFromPath getBin literalExample mapAttrsToList mkForce mkIf
|
||
|
mkChangedOptionModule mkEnableOption mkOption mod optional optionalString
|
||
|
remove singleton splitString types;
|
||
|
inherit (builtins) attrNames genList head isFunction isString length
|
||
|
removeAttrs replaceStrings stringLength substring tail;
|
||
|
|
||
|
_lib = rec {
|
||
|
|
||
|
# Convert a number base 10 to another number base k (k <= 16)
|
||
|
# l determins the 0 prefixed length of the output.
|
||
|
# Returns the output as a string
|
||
|
klconv = k: l: num:
|
||
|
let
|
||
|
snum = { base = k; d = num; c = 0; result = ""; };
|
||
|
replaceDigit = d: replaceStrings [ "10" "11" "12" "13" "14" "15" ]
|
||
|
[ "a" "b" "c" "d" "e" "f" ] d;
|
||
|
kconvDigit = { d, ... }@args:
|
||
|
args // { d = args.d / args.base;
|
||
|
result = replaceDigit (toString (d - ((d / args.base) * args.base))) + args.result;
|
||
|
};
|
||
|
kconv' = snum:
|
||
|
if snum.d == 0
|
||
|
then if stringLength snum.result < l
|
||
|
then (concatStrings (genList (_: "0") (l - stringLength snum.result))) + snum.result
|
||
|
else snum.result
|
||
|
else kconv' (kconvDigit snum);
|
||
|
in kconv' snum;
|
||
|
|
||
|
# Convert integer to octal code string of 16 or 8 bits
|
||
|
octconv16 = x: let
|
||
|
low = mod x 256;
|
||
|
high = (mod x 65536) / 256;
|
||
|
in "\\" + (substring 0 3 (klconv 8 3 high)) + "\\" + (substring 0 3 (klconv 8 3 low));
|
||
|
octconv8 = x: let s =(klconv 8 3 x); in "\\" + (substring 0 3 s);
|
||
|
|
||
|
# Convert seconds since Epoch to TAI64 into hex-coded string external format
|
||
|
epochToTAI64 = epoch: klconv 16 0 (4611686018427387904 + epoch);
|
||
|
|
||
|
# Quote special characters with octal replacements to suit tinydns-data
|
||
|
quoteDATA = s: replaceStrings [ " " "#" ":" ]
|
||
|
[ "\\040" "\\043" "\\072" ] s;
|
||
|
|
||
|
# Convert string into fixed-length string
|
||
|
# abcd -> \004abcd
|
||
|
fixedString = s: let
|
||
|
prefixShort = short: (octconv8 (stringLength short)) + (quoteDATA short);
|
||
|
fixedString' = s:
|
||
|
if stringLength s > 255
|
||
|
then (prefixShort (substring 0 255 s))+ (fixedString' (substring 255 (stringLength s) s))
|
||
|
else prefixShort s;
|
||
|
in
|
||
|
if stringLength s <= 65280 then fixedString' s
|
||
|
else abort "String payload too big, must be less than 65281 bytes";
|
||
|
|
||
|
# Split target at '.' into a length-prefixed string list suitable for tinydns-data
|
||
|
# abcd.ef. -> \004abcd\002ef\000
|
||
|
splitTarget = target:
|
||
|
concatStrings (map fixedString (splitString "." target));
|
||
|
|
||
|
# Parse DNS records defined in option records.
|
||
|
# Delivers var-args for the data* lambda type functions by supplying default
|
||
|
# values (i.e. "") for missing arguments.
|
||
|
parseRecord = f: l: if isFunction f
|
||
|
then if (length l) > 0
|
||
|
then parseRecord (f (head l)) (tail l)
|
||
|
else parseRecord (f "") []
|
||
|
else f;
|
||
|
|
||
|
# Capture the varargs parser and its varargs into a set containing all
|
||
|
# arguments as a list. Run the captured parser on the args when coercing the
|
||
|
# set to string.
|
||
|
captureParser = parser: { args = [];
|
||
|
__functor = self: arg: self // { args = self.args ++ [ arg ]; };
|
||
|
__toString = self: parser self.args;
|
||
|
};
|
||
|
|
||
|
# Make a DNS record by taking a prefabricated record and parsing the
|
||
|
# additional flags ttl, timestamp and location
|
||
|
dataFlags = record: ttl: ts: location:
|
||
|
"${record}"
|
||
|
+ ":${if ttl == null then "" else if isString ttl then ttl else toString ttl}"
|
||
|
+ ":${if ts == null then "" else if isString ts then ts else epochToTAI64 ts}"
|
||
|
+ ":${location}";
|
||
|
|
||
|
}; # _lib
|
||
|
|
||
|
# Each parser is expected to produce a string.
|
||
|
#
|
||
|
# captureParser collects the (variable amount of) arguments and the specific
|
||
|
# parser. It sets up the result such that, when it is coerced to a string, the
|
||
|
# captured parser is called with the captured arguments.
|
||
|
#
|
||
|
# parseRecord ensures that the specialized parser is called with enough
|
||
|
# arguments eventually. Left-out arguments are replaced with the empty string.
|
||
|
#
|
||
|
# Parser-building functions are collected in lib.
|
||
|
_parsers = with _lib; {
|
||
|
lib = _lib;
|
||
|
|
||
|
# Add the flags TTL, timestamp and location to a record
|
||
|
Flag = captureParser (parseRecord dataFlags);
|
||
|
|
||
|
# Make an AFSDB DNS record
|
||
|
AFSDB = captureParser (parseRecord (cell: server: dataFlags
|
||
|
(":${cell}:18:" + (octconv16 1) + (splitTarget server))));
|
||
|
|
||
|
# Make a SRV DNS record
|
||
|
SRV = captureParser (parseRecord (service: prio: w: port: target: dataFlags
|
||
|
(":${service}:33:" + (concatStrings (map octconv16 [ prio w port ])) + (splitTarget target))));
|
||
|
|
||
|
# Make a TXT DNS record
|
||
|
TXT = captureParser (parseRecord (service: txt: dataFlags
|
||
|
(":${service}:16:" + (fixedString txt))));
|
||
|
|
||
|
# Make a URI DNS record
|
||
|
URI = captureParser (parseRecord (service: prio: weight: pl: dataFlags
|
||
|
(":${service}:256:" + "${octconv16 prio}${octconv16 weight}" + (quoteDATA pl))));
|
||
|
|
||
|
}; # _parsers
|
||
|
|
||
|
# Update the documentation of services.tinydns.*.data when adding new record-building functions, here!
|
||
|
in _parsers
|