From c51f713eebe21071f22d0830fdaeb274b1a77059 Mon Sep 17 00:00:00 2001 From: Jeremy Harris Date: Sun, 8 Aug 2021 17:34:49 +0100 Subject: [PATCH] Expansions: mask_n operator --- doc/doc-docbook/spec.xfpt | 17 ++++++++++++++--- doc/doc-txt/NewStuff | 2 ++ src/src/expand.c | 23 +++++++++++++++++------ test/scripts/0000-Basic/0002 | 2 ++ test/stdout/0002 | 2 ++ 5 files changed, 37 insertions(+), 9 deletions(-) diff --git a/doc/doc-docbook/spec.xfpt b/doc/doc-docbook/spec.xfpt index 0385de6db..e766b69e2 100644 --- a/doc/doc-docbook/spec.xfpt +++ b/doc/doc-docbook/spec.xfpt @@ -11228,7 +11228,8 @@ empty. The parsing correctly handles SMTPUTF8 Unicode in the string. -.vitem &*${mask:*&<&'IP&~address'&>&*/*&<&'bit&~count'&>&*}*& +.vitem &*${mask:*&<&'IP&~address'&>&*/*&<&'bit&~count'&>&*}*& &&& + &*${mask_n:*&<&'IP&~address'&>&*/*&<&'bit&~count'&>&*}*& .cindex "masked IP address" .cindex "IP address" "masking" .cindex "CIDR notation" @@ -11242,8 +11243,14 @@ the result back to text, with mask appended. For example, .code ${mask:10.111.131.206/28} .endd -returns the string &"10.111.131.192/28"&. Since this operation is expected to -be mostly used for looking up masked addresses in files, the result for an IPv6 +returns the string &"10.111.131.192/28"&. + +Since this operation is expected to +be mostly used for looking up masked addresses in files, the +.new +normal +.wen +result for an IPv6 address uses dots to separate components instead of colons, because colon terminates a key string in lsearch files. So, for example, .code @@ -11253,6 +11260,10 @@ returns the string .code 3ffe.ffff.836f.0a00.000a.0800.2000.0000/99 .endd +.new +If the optional form &*mask_n*& is used, IPv6 address result are instead +returned in normailsed form, using colons and with zero-compression. +.wen Letters in IPv6 addresses are always output in lower case. diff --git a/doc/doc-txt/NewStuff b/doc/doc-txt/NewStuff index 478446b41..1d6190b29 100644 --- a/doc/doc-txt/NewStuff +++ b/doc/doc-txt/NewStuff @@ -11,6 +11,8 @@ Version 4.96 1. A new ACL condition: seen. Records/tests a timestamp against a key. + 2. A variant of the "mask" expansion operator to give normalised IPv6. + Version 4.95 ------------ diff --git a/src/src/expand.c b/src/src/expand.c index 4fb935528..83c0ad051 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -7333,11 +7333,11 @@ while (*s) int count; uschar *endptr; int binary[4]; - int mask, maskoffset; - int type = string_is_ip_address(sub, &maskoffset); + int type, mask, maskoffset; + BOOL normalised; uschar buffer[64]; - if (type == 0) + if ((type = string_is_ip_address(sub, &maskoffset)) == 0) { expand_string_message = string_sprintf("\"%s\" is not an IP address", sub); @@ -7353,13 +7353,18 @@ while (*s) mask = Ustrtol(sub + maskoffset + 1, &endptr, 10); - if (*endptr != 0 || mask < 0 || mask > ((type == 4)? 32 : 128)) + if (*endptr || mask < 0 || mask > (type == 4 ? 32 : 128)) { expand_string_message = string_sprintf("mask value too big in \"%s\"", sub); goto EXPAND_FAILED; } + /* If an optional 'n' was given, ipv6 gets normalised output: + colons rather than dots, and zero-compressed. */ + + normalised = arg && *arg == 'n'; + /* Convert the address to binary integer(s) and apply the mask */ sub[maskoffset] = 0; @@ -7368,8 +7373,14 @@ while (*s) /* Convert to masked textual format and add to output. */ - yield = string_catn(yield, buffer, - host_nmtoa(count, binary, mask, buffer, '.')); + if (type == 4 || !normalised) + yield = string_catn(yield, buffer, + host_nmtoa(count, binary, mask, buffer, '.')); + else + { + ipv6_nmtoa(binary, buffer); + yield = string_fmt_append(yield, "%s/%d", buffer, mask); + } continue; } diff --git a/test/scripts/0000-Basic/0002 b/test/scripts/0000-Basic/0002 index cc289e04e..db3eae6be 100644 --- a/test/scripts/0000-Basic/0002 +++ b/test/scripts/0000-Basic/0002 @@ -248,6 +248,8 @@ mask: ${mask:192.168.10.206/33} mask: ${mask:192.168.10.206/0} mask: ${mask:192.168.10.206} mask: ${mask:a.b.c.d} +mask: ${mask:2a00:2:3:4:5:6:7:8/79} +mask_n: ${mask_n:2a00:2:3:4:5:6:7:8/79} ipv6denorm: ${ipv6denorm:::1} ipv6denorm: ${ipv6denorm:fe00::1} ipv6denorm: ${ipv6denorm:192.168.0.1} diff --git a/test/stdout/0002 b/test/stdout/0002 index 0b9a95cd5..5c8c2520c 100644 --- a/test/stdout/0002 +++ b/test/stdout/0002 @@ -230,6 +230,8 @@ newline tab\134backslash ~tilde\177DEL\200\201. > mask: 0.0.0.0/0 > Failed: missing mask value in "192.168.10.206" > Failed: "a.b.c.d" is not an IP address +> mask: 2a00.0002.0003.0004.0004.0000.0000.0000/79 +> mask_n: 2a00:2:3:4:4::/79 > ipv6denorm: 0000:0000:0000:0000:0000:0000:0000:0001 > ipv6denorm: fe00:0000:0000:0000:0000:0000:0000:0001 > ipv6denorm: 0000:0000:0000:0000:0000:ffff:c0a8:0001 -- 2.30.2