summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeremy Harris <jgh146exb@wizmail.org>2021-08-08 17:34:49 +0100
committerJeremy Harris <jgh146exb@wizmail.org>2021-08-08 17:34:49 +0100
commitc51f713eebe21071f22d0830fdaeb274b1a77059 (patch)
tree95da2d585f5a8556aed7bed8f5f056d0d6c12d56
parentb003ce30f0038c2042303d18fc24f278de06a1f5 (diff)
Expansions: mask_n operator
-rw-r--r--doc/doc-docbook/spec.xfpt17
-rw-r--r--doc/doc-txt/NewStuff2
-rw-r--r--src/src/expand.c23
-rw-r--r--test/scripts/0000-Basic/00022
-rw-r--r--test/stdout/00022
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