diff --git a/postfix/HISTORY b/postfix/HISTORY
index 4d58b31ef..617174d71 100644
--- a/postfix/HISTORY
+++ b/postfix/HISTORY
@@ -22316,3 +22316,14 @@ Apologies for any names omitted.
Cleanup: existing if/endif support for pcre and regexp
tables, in preparation for new if/endif support for cidr
tables. Files: util/dict_regexp.c, util/dict_pcre.c.
+
+20160526
+
+ Feature: cidr tables now support if/endif and negation (by
+ prepending "!" to a pattern), just like regexp and pcre
+ tables. The primarily purpose is to improve readability of
+ complex tables. Files: util/cidr_match.[hc], util/dict_cidr.c,
+ proto/cidr_table.
+
+ Cleanup: make regexp: and pcre: parser warning messages more
+ similar. Files: dict_regexp.c, dict_pcre.c.
diff --git a/postfix/RELEASE_NOTES b/postfix/RELEASE_NOTES
index 117bb6b28..94fab09e3 100644
--- a/postfix/RELEASE_NOTES
+++ b/postfix/RELEASE_NOTES
@@ -15,3 +15,12 @@ specifies the release date of a stable release or snapshot release.
If you upgrade from Postfix 3.0 or earlier, read RELEASE_NOTES-3.1
before proceeding.
+
+Major changes with snapshot 20160527
+====================================
+
+Postfix cidr tables now support if..endif, and pattern negation
+with "!", just like regexp and pcre tables. The if..endif can speed
+up lookups by skipping over irrelevant patterns, and can make rule
+maintenance easier because rules for a network can now be placed
+inside if..endif. See the cidr_table(5) manpage for syntax details.
diff --git a/postfix/html/cidr_table.5.html b/postfix/html/cidr_table.5.html
index 67b6ceb6c..c8f63f9da 100644
--- a/postfix/html/cidr_table.5.html
+++ b/postfix/html/cidr_table.5.html
@@ -30,30 +30,39 @@ CIDR_TABLE(5) CIDR_TABLE(5)
TABLE FORMAT
The general form of a Postfix CIDR table is:
- network_address/network_mask result
- When a search string matches the specified network block, use
- the corresponding result value. Specify 0.0.0.0/0 to match every
- IPv4 address, and ::/0 to match every IPv6 address.
+ pattern result
+ When a search string matches the specified pattern, use the cor-
+ responding result value. The pattern must be in network/prefix
+ or network_address form (see ADDRESS PATTERN SYNTAX below).
- An IPv4 network address is a sequence of four decimal octets
- separated by ".", and an IPv6 network address is a sequence of
- three to eight hexadecimal octet pairs separated by ":".
+ !pattern result
+ When a search string does not match the specified pattern, use
+ the specified result value. The pattern must be in network/pre-
+ fix or network_address form (see ADDRESS PATTERN SYNTAX below).
- The network_mask is the number of high-order bits in the net-
- work_address that the search string must match.
+ This feature is available in Postfix 3.2 and later.
- Before comparisons are made, lookup keys and table entries are
- converted from string to binary. Therefore table entries will be
- matched regardless of redundant zero characters.
+ if pattern
- Note: address information may be enclosed inside "[]" but this
- form is not required.
+ endif When a search string matches the specified pattern, match that
+ search string against the patterns between if and endif. The
+ pattern must be in network/prefix or network_address form (see
+ ADDRESS PATTERN SYNTAX below). The if..endif can nest.
- IPv6 support is available in Postfix 2.2 and later.
+ Note: do not prepend whitespace to text between if..endif.
- network_address result
- When a search string matches the specified network address, use
- the corresponding result value.
+ This feature is available in Postfix 3.2 and later.
+
+ if !pattern
+
+ endif When a search string does not match the specified pattern, match
+ that search string against the patterns between if and endif.
+ The pattern must be in network/prefix or network_address form
+ (see ADDRESS PATTERN SYNTAX below). The if..endif can nest.
+
+ Note: do not prepend whitespace to text between if..endif.
+
+ This feature is available in Postfix 3.2 and later.
blank lines and comments
Empty lines and whitespace-only lines are ignored, as are lines
@@ -67,6 +76,27 @@ CIDR_TABLE(5) CIDR_TABLE(5)
Patterns are applied in the order as specified in the table, until a
pattern is found that matches the search string.
+ADDRESS PATTERN SYNTAX
+ Postfix CIDR tables are pattern-based. A pattern is either a net-
+ work_address which requires an exact match, or a network_address/pre-
+ fix_length where the prefix_length part specifies the length of the
+ network_address prefix that must be matched (the other bits in the net-
+ work_address part must be zero).
+
+ An IPv4 network address is a sequence of four decimal octets separated
+ by ".", and an IPv6 network address is a sequence of three to eight
+ hexadecimal octets or octet pairs separated by ":". The pattern
+ 0.0.0.0/0 matches every IPv4 address, and ::/0 matches every IPv6
+ address. IPv6 support is available in Postfix 2.2 and later.
+
+ Before comparisons are made, lookup keys and table entries are con-
+ verted from string to binary. Therefore, IPv6 patterns will be matched
+ regardless of leading zeros (a leading zero in an IPv4 address octet
+ indicates octal notation).
+
+ Note: address information may be enclosed inside "[]" but this form is
+ not required.
+
EXAMPLE SMTPD ACCESS MAP
/etc/postfix/main.cf:
smtpd_client_restrictions = ... cidr:/etc/postfix/client.cidr ...
diff --git a/postfix/man/man5/cidr_table.5 b/postfix/man/man5/cidr_table.5
index b199e39ae..51a58a9d4 100644
--- a/postfix/man/man5/cidr_table.5
+++ b/postfix/man/man5/cidr_table.5
@@ -33,30 +33,42 @@ described in the SYNOPSIS above.
.ad
.fi
The general form of a Postfix CIDR table is:
-.IP "\fInetwork_address\fB/\fInetwork_mask result\fR"
-When a search string matches the specified network block,
-use the corresponding \fIresult\fR value. Specify
-0.0.0.0/0 to match every IPv4 address, and ::/0 to match
-every IPv6 address.
-
-An IPv4 network address is a sequence of four decimal octets
-separated by ".", and an IPv6 network address is a sequence
-of three to eight hexadecimal octet pairs separated by ":".
-
-The \fInetwork_mask\fR is the number of high\-order bits in
-the \fInetwork_address\fR that the search string must match.
-
-Before comparisons are made, lookup keys and table entries
-are converted from string to binary. Therefore table entries
-will be matched regardless of redundant zero characters.
-
-Note: address information may be enclosed inside "[]" but
-this form is not required.
-
-IPv6 support is available in Postfix 2.2 and later.
-.IP "\fInetwork_address result\fR"
-When a search string matches the specified network address,
-use the corresponding \fIresult\fR value.
+.IP "\fIpattern result\fR"
+When a search string matches the specified \fIpattern\fR, use
+the corresponding \fIresult\fR value. The \fIpattern\fR must be
+in \fInetwork/prefix\fR or \fInetwork_address\fR form (see
+ADDRESS PATTERN SYNTAX below).
+.IP "\fB!\fIpattern result\fR"
+When a search string does not match the specified \fIpattern\fR,
+use the specified \fIresult\fR value. The \fIpattern\fR must
+be in \fInetwork/prefix\fR or \fInetwork_address\fR form (see
+ADDRESS PATTERN SYNTAX below).
+.sp
+This feature is available in Postfix 3.2 and later.
+.IP "\fBif \fIpattern\fR"
+.IP "\fBendif\fR"
+When a search string matches the specified \fIpattern\fR, match
+that search string against the patterns between \fBif\fR and
+\fBendif\fR. The \fIpattern\fR must be in \fInetwork/prefix\fR or
+\fInetwork_address\fR form (see ADDRESS PATTERN SYNTAX below). The
+\fBif\fR..\fBendif\fR can nest.
+.sp
+Note: do not prepend whitespace to text between
+\fBif\fR..\fBendif\fR.
+.sp
+This feature is available in Postfix 3.2 and later.
+.IP "\fBif !\fIpattern\fR"
+.IP "\fBendif\fR"
+When a search string does not match the specified \fIpattern\fR,
+match that search string against the patterns between \fBif\fR and
+\fBendif\fR. The \fIpattern\fR must be in \fInetwork/prefix\fR or
+\fInetwork_address\fR form (see ADDRESS PATTERN SYNTAX below). The
+\fBif\fR..\fBendif\fR can nest.
+.sp
+Note: do not prepend whitespace to text between
+\fBif\fR..\fBendif\fR.
+.sp
+This feature is available in Postfix 3.2 and later.
.IP "blank lines and comments"
Empty lines and whitespace\-only lines are ignored, as
are lines whose first non\-whitespace character is a `#'.
@@ -70,6 +82,32 @@ starts with whitespace continues a logical line.
.fi
Patterns are applied in the order as specified in the table, until a
pattern is found that matches the search string.
+.SH "ADDRESS PATTERN SYNTAX"
+.na
+.nf
+.ad
+.fi
+Postfix CIDR tables are pattern\-based. A pattern is either
+a \fInetwork_address\fR which requires an exact match, or a
+\fInetwork_address/prefix_length\fR where the \fIprefix_length\fR
+part specifies the length of the \fInetwork_address\fR prefix
+that must be matched (the other bits in the \fInetwork_address\fR
+part must be zero).
+
+An IPv4 network address is a sequence of four decimal octets
+separated by ".", and an IPv6 network address is a sequence
+of three to eight hexadecimal octets or octet pairs separated by
+":". The pattern 0.0.0.0/0 matches every IPv4 address, and ::/0
+matches every IPv6 address. IPv6 support is available in
+Postfix 2.2 and later.
+
+Before comparisons are made, lookup keys and table entries
+are converted from string to binary. Therefore, IPv6 patterns
+will be matched regardless of leading zeros (a leading zero in
+an IPv4 address octet indicates octal notation).
+
+Note: address information may be enclosed inside "[]" but
+this form is not required.
.SH "EXAMPLE SMTPD ACCESS MAP"
.na
.nf
diff --git a/postfix/proto/cidr_table b/postfix/proto/cidr_table
index 4787a79d3..51d39aa13 100644
--- a/postfix/proto/cidr_table
+++ b/postfix/proto/cidr_table
@@ -25,30 +25,42 @@
# .ad
# .fi
# The general form of a Postfix CIDR table is:
-# .IP "\fInetwork_address\fB/\fInetwork_mask result\fR"
-# When a search string matches the specified network block,
-# use the corresponding \fIresult\fR value. Specify
-# 0.0.0.0/0 to match every IPv4 address, and ::/0 to match
-# every IPv6 address.
-#
-# An IPv4 network address is a sequence of four decimal octets
-# separated by ".", and an IPv6 network address is a sequence
-# of three to eight hexadecimal octet pairs separated by ":".
-#
-# The \fInetwork_mask\fR is the number of high-order bits in
-# the \fInetwork_address\fR that the search string must match.
-#
-# Before comparisons are made, lookup keys and table entries
-# are converted from string to binary. Therefore table entries
-# will be matched regardless of redundant zero characters.
-#
-# Note: address information may be enclosed inside "[]" but
-# this form is not required.
-#
-# IPv6 support is available in Postfix 2.2 and later.
-# .IP "\fInetwork_address result\fR"
-# When a search string matches the specified network address,
-# use the corresponding \fIresult\fR value.
+# .IP "\fIpattern result\fR"
+# When a search string matches the specified \fIpattern\fR, use
+# the corresponding \fIresult\fR value. The \fIpattern\fR must be
+# in \fInetwork/prefix\fR or \fInetwork_address\fR form (see
+# ADDRESS PATTERN SYNTAX below).
+# .IP "\fB!\fIpattern result\fR"
+# When a search string does not match the specified \fIpattern\fR,
+# use the specified \fIresult\fR value. The \fIpattern\fR must
+# be in \fInetwork/prefix\fR or \fInetwork_address\fR form (see
+# ADDRESS PATTERN SYNTAX below).
+# .sp
+# This feature is available in Postfix 3.2 and later.
+# .IP "\fBif \fIpattern\fR"
+# .IP "\fBendif\fR"
+# When a search string matches the specified \fIpattern\fR, match
+# that search string against the patterns between \fBif\fR and
+# \fBendif\fR. The \fIpattern\fR must be in \fInetwork/prefix\fR or
+# \fInetwork_address\fR form (see ADDRESS PATTERN SYNTAX below). The
+# \fBif\fR..\fBendif\fR can nest.
+# .sp
+# Note: do not prepend whitespace to text between
+# \fBif\fR..\fBendif\fR.
+# .sp
+# This feature is available in Postfix 3.2 and later.
+# .IP "\fBif !\fIpattern\fR"
+# .IP "\fBendif\fR"
+# When a search string does not match the specified \fIpattern\fR,
+# match that search string against the patterns between \fBif\fR and
+# \fBendif\fR. The \fIpattern\fR must be in \fInetwork/prefix\fR or
+# \fInetwork_address\fR form (see ADDRESS PATTERN SYNTAX below). The
+# \fBif\fR..\fBendif\fR can nest.
+# .sp
+# Note: do not prepend whitespace to text between
+# \fBif\fR..\fBendif\fR.
+# .sp
+# This feature is available in Postfix 3.2 and later.
# .IP "blank lines and comments"
# Empty lines and whitespace-only lines are ignored, as
# are lines whose first non-whitespace character is a `#'.
@@ -60,6 +72,30 @@
# .fi
# Patterns are applied in the order as specified in the table, until a
# pattern is found that matches the search string.
+# ADDRESS PATTERN SYNTAX
+# .ad
+# .fi
+# Postfix CIDR tables are pattern-based. A pattern is either
+# a \fInetwork_address\fR which requires an exact match, or a
+# \fInetwork_address/prefix_length\fR where the \fIprefix_length\fR
+# part specifies the length of the \fInetwork_address\fR prefix
+# that must be matched (the other bits in the \fInetwork_address\fR
+# part must be zero).
+#
+# An IPv4 network address is a sequence of four decimal octets
+# separated by ".", and an IPv6 network address is a sequence
+# of three to eight hexadecimal octets or octet pairs separated by
+# ":". The pattern 0.0.0.0/0 matches every IPv4 address, and ::/0
+# matches every IPv6 address. IPv6 support is available in
+# Postfix 2.2 and later.
+#
+# Before comparisons are made, lookup keys and table entries
+# are converted from string to binary. Therefore, IPv6 patterns
+# will be matched regardless of leading zeros (a leading zero in
+# an IPv4 address octet indicates octal notation).
+#
+# Note: address information may be enclosed inside "[]" but
+# this form is not required.
# EXAMPLE SMTPD ACCESS MAP
# .nf
# /etc/postfix/main.cf:
diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h
index c5e9d7346..00b7d7b41 100644
--- a/postfix/src/global/mail_version.h
+++ b/postfix/src/global/mail_version.h
@@ -20,7 +20,7 @@
* Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only.
*/
-#define MAIL_RELEASE_DATE "20160522"
+#define MAIL_RELEASE_DATE "20160527"
#define MAIL_VERSION_NUMBER "3.2"
#ifdef SNAPSHOT
diff --git a/postfix/src/util/cidr_match.c b/postfix/src/util/cidr_match.c
index 387c02f62..7a921d75f 100644
--- a/postfix/src/util/cidr_match.c
+++ b/postfix/src/util/cidr_match.c
@@ -14,6 +14,14 @@
/* int cidr_match_execute(info, address)
/* CIDR_MATCH *info;
/* const char *address;
+/* AUXILIARY FUNCTIONS
+/* VSTRING *cidr_match_parse_if(info, address, why)
+/* CIDR_MATCH *info;
+/* char *address;
+/* VSTRING *why;
+/*
+/* void cidr_match_endif(info)
+/* CIDR_MATCH *info;
/* DESCRIPTION
/* This module parses address or address/length patterns and
/* provides simple address matching. The implementation is
@@ -29,6 +37,12 @@
/* (the caller should give the latter to vstring_free()).
/* The pattern argument is destroyed.
/*
+/* cidr_match_parse_if() parses the address that follows an IF
+/* token, and stores the result into the info argument.
+/*
+/* cidr_match_endif() handles the occurrence of an ENDIF token,
+/* and updates the info argument.
+/*
/* cidr_match_execute() matches the specified address against
/* a list of parsed expressions, and returns the matching
/* expression's data structure.
@@ -39,6 +53,11 @@
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
/*--*/
/* System library. */
@@ -88,15 +107,44 @@
(msg_panic("%s: bad address family %d", myname, (f)), 0))
#endif
+/* cidr_match_entry - match one entry */
+
+static inline int cidr_match_entry(CIDR_MATCH *entry,
+ unsigned char *addr_bytes)
+{
+ unsigned char *mp;
+ unsigned char *np;
+ unsigned char *ap;
+
+ /* Unoptimized case: netmask with some or all bits zero. */
+ if (entry->mask_shift < entry->addr_bit_count) {
+ for (np = entry->net_bytes, mp = entry->mask_bytes,
+ ap = addr_bytes; /* void */ ; np++, mp++, ap++) {
+ if (ap >= addr_bytes + entry->addr_byte_count)
+ return (entry->match);
+ if ((*ap & *mp) != *np)
+ break;
+ }
+ }
+ /* Optimized case: all 1 netmask (i.e. no netmask specified). */
+ else {
+ for (np = entry->net_bytes,
+ ap = addr_bytes; /* void */ ; np++, ap++) {
+ if (ap >= addr_bytes + entry->addr_byte_count)
+ return (entry->match);
+ if (*ap != *np)
+ break;
+ }
+ }
+ return (!entry->match);
+}
+
/* cidr_match_execute - match address against compiled CIDR pattern list */
CIDR_MATCH *cidr_match_execute(CIDR_MATCH *list, const char *addr)
{
unsigned char addr_bytes[CIDR_MATCH_ABYTES];
unsigned addr_family;
- unsigned char *mp;
- unsigned char *np;
- unsigned char *ap;
CIDR_MATCH *entry;
addr_family = CIDR_MATCH_ADDR_FAMILY(addr);
@@ -104,27 +152,26 @@ CIDR_MATCH *cidr_match_execute(CIDR_MATCH *list, const char *addr)
return (0);
for (entry = list; entry; entry = entry->next) {
- if (entry->addr_family == addr_family) {
- /* Unoptimized case: netmask with some or all bits zero. */
- if (entry->mask_shift < entry->addr_bit_count) {
- for (np = entry->net_bytes, mp = entry->mask_bytes,
- ap = addr_bytes; /* void */ ; np++, mp++, ap++) {
- if (ap >= addr_bytes + entry->addr_byte_count)
- return (entry);
- if ((*ap & *mp) != *np)
- break;
- }
- }
- /* Optimized case: all 1 netmask (i.e. no netmask specified). */
- else {
- for (np = entry->net_bytes,
- ap = addr_bytes; /* void */ ; np++, ap++) {
- if (ap >= addr_bytes + entry->addr_byte_count)
- return (entry);
- if (*ap != *np)
- break;
- }
- }
+
+ switch (entry->op) {
+
+ case CIDR_MATCH_OP_MATCH:
+ if (entry->addr_family == addr_family)
+ if (cidr_match_entry(entry, addr_bytes))
+ return (entry);
+ break;
+
+ case CIDR_MATCH_OP_IF:
+ if (entry->addr_family == addr_family)
+ if (cidr_match_entry(entry, addr_bytes))
+ continue;
+ /* An IF without matching ENDIF has no end-of block entry. */
+ if ((entry = entry->block_end) == 0)
+ break;
+ /* FALLTHROUGH */
+
+ case CIDR_MATCH_OP_ENDIF:
+ continue;
}
}
return (0);
@@ -141,6 +188,23 @@ VSTRING *cidr_match_parse(CIDR_MATCH *ip, char *pattern, VSTRING *why)
unsigned char *np;
unsigned char *mp;
+ /*
+ * Process negation operators. XXX unlike dict_regexp_get_pat() and
+ * dict_pcre_get_pattern(), dict_cidr_parse_rule() does not allow space
+ * between ! and the remainder of a pattern. However, those spaces are
+ * not documented, they were more a helpful thing.
+ */
+ ip->match = 1;
+ while (*pattern == '!') {
+ ip->match = !ip->match;
+ pattern++;
+ }
+
+ if (*pattern == 0) {
+ vstring_sprintf(why ? why : (why = vstring_alloc(20)), "no pattern");
+ return (why);
+ }
+
/*
* Strip [] from [addr/len] or [addr]/len, destroying the pattern. CIDR
* maps don't need [] to eliminate syntax ambiguity, but matchlists need
@@ -224,7 +288,30 @@ VSTRING *cidr_match_parse(CIDR_MATCH *ip, char *pattern, VSTRING *why)
/*
* Wrap up the result.
*/
+ ip->op = CIDR_MATCH_OP_MATCH;
ip->next = 0;
+ ip->block_end = 0;
return (0);
}
+
+/* cidr_match_parse_if - parse CIDR pattern after IF */
+
+VSTRING *cidr_match_parse_if(CIDR_MATCH *ip, char *pattern, VSTRING *why)
+{
+ VSTRING *ret;
+
+ if ((ret = cidr_match_parse(ip, pattern, why)) == 0)
+ ip->op = CIDR_MATCH_OP_IF;
+ return (ret);
+}
+
+/* cidr_match_endif - handle ENDIF pattern */
+
+void cidr_match_endif(CIDR_MATCH *ip)
+{
+ memset(ip, 0, sizeof(*ip));
+ ip->op = CIDR_MATCH_OP_ENDIF;
+ ip->next = 0; /* maybe not all bits 0 */
+ ip->block_end = 0;
+}
diff --git a/postfix/src/util/cidr_match.h b/postfix/src/util/cidr_match.h
index 153394f80..1fab97671 100644
--- a/postfix/src/util/cidr_match.h
+++ b/postfix/src/util/cidr_match.h
@@ -38,6 +38,8 @@
* Each parsed CIDR pattern can be member of a linked list.
*/
typedef struct CIDR_MATCH {
+ int op; /* operation, match or control flow */
+ int match; /* positive or negative match */
unsigned char net_bytes[CIDR_MATCH_ABYTES]; /* network portion */
unsigned char mask_bytes[CIDR_MATCH_ABYTES]; /* network mask */
unsigned char addr_family; /* AF_XXX */
@@ -45,9 +47,17 @@ typedef struct CIDR_MATCH {
unsigned char addr_bit_count; /* optimization */
unsigned char mask_shift; /* optimization */
struct CIDR_MATCH *next; /* next entry */
+ struct CIDR_MATCH *block_end; /* block terminator */
} CIDR_MATCH;
+#define CIDR_MATCH_OP_MATCH 1 /* Match this pattern */
+#define CIDR_MATCH_OP_IF 2 /* Increase if/endif nesting on match */
+#define CIDR_MATCH_OP_ENDIF 3 /* Decrease if/endif nesting on match */
+
extern VSTRING *cidr_match_parse(CIDR_MATCH *, char *, VSTRING *);
+extern VSTRING *cidr_match_parse_if(CIDR_MATCH *, char *, VSTRING *);
+extern void cidr_match_endif(CIDR_MATCH *);
+
extern CIDR_MATCH *cidr_match_execute(CIDR_MATCH *, const char *);
/* LICENSE
@@ -59,6 +69,11 @@ extern CIDR_MATCH *cidr_match_execute(CIDR_MATCH *, const char *);
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
/*--*/
#endif
diff --git a/postfix/src/util/dict_cidr.c b/postfix/src/util/dict_cidr.c
index e14611a73..112582bc6 100644
--- a/postfix/src/util/dict_cidr.c
+++ b/postfix/src/util/dict_cidr.c
@@ -27,6 +27,11 @@
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
/*--*/
/* System library. */
@@ -54,6 +59,7 @@
#include
#include
#include
+#include
/* Application-specific. */
@@ -63,6 +69,7 @@
typedef struct DICT_CIDR_ENTRY {
CIDR_MATCH cidr_info; /* must be first */
char *value; /* lookup result */
+ int lineno;
} DICT_CIDR_ENTRY;
typedef struct {
@@ -106,7 +113,8 @@ static void dict_cidr_close(DICT *dict)
/* dict_cidr_parse_rule - parse CIDR table rule into network, mask and value */
-static DICT_CIDR_ENTRY *dict_cidr_parse_rule(char *p, VSTRING *why)
+static DICT_CIDR_ENTRY *dict_cidr_parse_rule(char *p, int lineno, int nesting,
+ VSTRING *why)
{
DICT_CIDR_ENTRY *rule;
char *pattern;
@@ -115,33 +123,76 @@ static DICT_CIDR_ENTRY *dict_cidr_parse_rule(char *p, VSTRING *why)
MAI_HOSTADDR_STR hostaddr;
/*
- * Split the rule into key and value. We already eliminated leading
- * whitespace, comments, empty lines or lines with whitespace only. This
- * means a null key can't happen but we will handle this anyway.
+ * IF must be followed by a pattern.
*/
- pattern = p;
- while (*p && !ISSPACE(*p)) /* Skip over key */
- p++;
- if (*p) /* Terminate key */
- *p++ = 0;
- while (*p && ISSPACE(*p)) /* Skip whitespace */
- p++;
- value = p;
- trimblanks(value, 0)[0] = 0; /* Trim trailing blanks */
- if (*pattern == 0) {
- vstring_sprintf(why, "no address pattern");
- return (0);
- }
- if (*value == 0) {
- vstring_sprintf(why, "no lookup result");
- return (0);
+ if (strncasecmp(p, "IF", 2) == 0 && !ISALNUM(p[2])) {
+ p += 2;
+ while (*p && ISSPACE(*p)) /* Skip whitespace */
+ p++;
+ if (*p == 0) {
+ vstring_sprintf(why, "no address pattern");
+ return (0);
+ }
+ trimblanks(p, 0)[0] = 0; /* Trim trailing blanks */
+ if (cidr_match_parse_if(&cidr_info, p, why) != 0)
+ return (0);
+ value = "";
}
/*
- * Parse the pattern, destroying it in the process.
+ * ENDIF must not be followed by other text.
*/
- if (cidr_match_parse(&cidr_info, pattern, why) != 0)
- return (0);
+ else if (strncasecmp(p, "ENDIF", 5) == 0 && !ISALNUM(p[5])) {
+ p += 5;
+ while (*p && ISSPACE(*p)) /* Skip whitespace */
+ p++;
+ if (*p != 0) {
+ vstring_sprintf(why, "garbage after ENDIF");
+ return (0);
+ }
+ if (nesting == 0) {
+ vstring_sprintf(why, "ENDIF without IF");
+ return (0);
+ }
+ cidr_match_endif(&cidr_info);
+ value = "";
+ }
+
+ /*
+ * An address pattern.
+ */
+ else {
+
+ /*
+ * Split the rule into key and value. We already eliminated leading
+ * whitespace, comments, empty lines or lines with whitespace only.
+ * This means a null key can't happen but we will handle this anyway.
+ */
+ pattern = p;
+ while (*p && !ISSPACE(*p)) /* Skip over key */
+ p++;
+ if (*p) /* Terminate key */
+ *p++ = 0;
+ while (*p && ISSPACE(*p)) /* Skip whitespace */
+ p++;
+ value = p;
+ trimblanks(value, 0)[0] = 0; /* Trim trailing blanks */
+ if (*pattern == 0) {
+ vstring_sprintf(why, "no address pattern");
+ return (0);
+ }
+
+ /*
+ * Parse the pattern, destroying it in the process.
+ */
+ if (cidr_match_parse(&cidr_info, pattern, why) != 0)
+ return (0);
+
+ if (*value == 0) {
+ vstring_sprintf(why, "no lookup result");
+ return (0);
+ }
+ }
/*
* Bundle up the result.
@@ -149,6 +200,7 @@ static DICT_CIDR_ENTRY *dict_cidr_parse_rule(char *p, VSTRING *why)
rule = (DICT_CIDR_ENTRY *) mymalloc(sizeof(DICT_CIDR_ENTRY));
rule->cidr_info = cidr_info;
rule->value = mystrdup(value);
+ rule->lineno = lineno;
if (msg_verbose) {
if (inet_ntop(cidr_info.addr_family, cidr_info.net_bytes,
@@ -164,6 +216,7 @@ static DICT_CIDR_ENTRY *dict_cidr_parse_rule(char *p, VSTRING *why)
DICT *dict_cidr_open(const char *mapname, int open_flags, int dict_flags)
{
+ const char myname[] = "dict_cidr_open";
DICT_CIDR *dict_cidr;
VSTREAM *map_fp = 0;
struct stat st;
@@ -173,6 +226,9 @@ DICT *dict_cidr_open(const char *mapname, int open_flags, int dict_flags)
DICT_CIDR_ENTRY *last_rule = 0;
int last_line = 0;
int lineno;
+ int nesting = 0;
+ DICT_CIDR_ENTRY **rule_stack = 0;
+ MVECT mvect;
/*
* Let the optimizer worry about eliminating redundant code.
@@ -225,12 +281,35 @@ DICT *dict_cidr_open(const char *mapname, int open_flags, int dict_flags)
dict_cidr->dict.owner.status = (st.st_uid != 0);
while (readllines(line_buffer, map_fp, &last_line, &lineno)) {
- rule = dict_cidr_parse_rule(vstring_str(line_buffer), why);
+ rule = dict_cidr_parse_rule(vstring_str(line_buffer), lineno,
+ nesting, why);
if (rule == 0) {
msg_warn("cidr map %s, line %d: %s: skipping this rule",
mapname, lineno, vstring_str(why));
continue;
}
+ if (rule->cidr_info.op == CIDR_MATCH_OP_IF) {
+ if (rule_stack == 0)
+ rule_stack = (DICT_CIDR_ENTRY **) mvect_alloc(&mvect,
+ sizeof(*rule_stack), nesting + 1,
+ (MVECT_FN) 0, (MVECT_FN) 0);
+ else
+ rule_stack =
+ (DICT_CIDR_ENTRY **) mvect_realloc(&mvect, nesting + 1);
+ rule_stack[nesting] = rule;
+ nesting++;
+ } else if (rule->cidr_info.op == CIDR_MATCH_OP_ENDIF) {
+ DICT_CIDR_ENTRY *if_rule;
+
+ if (nesting-- <= 0)
+ /* Already handled in dict_cidr_parse_rule(). */
+ msg_panic("%s: ENDIF without IF", myname);
+ if_rule = rule_stack[nesting];
+ if (if_rule->cidr_info.op != CIDR_MATCH_OP_IF)
+ msg_panic("%s: unexpected rule stack element type %d",
+ myname, if_rule->cidr_info.op);
+ if_rule->cidr_info.block_end = &(rule->cidr_info);
+ }
if (last_rule == 0)
dict_cidr->head = rule;
else
@@ -238,5 +317,12 @@ DICT *dict_cidr_open(const char *mapname, int open_flags, int dict_flags)
last_rule = rule;
}
+ while (nesting-- > 0)
+ msg_warn("cidr map %s, line %d: IF has no matching ENDIF",
+ mapname, rule_stack[nesting]->lineno);
+
+ if (rule_stack)
+ (void) mvect_free(&mvect);
+
DICT_CIDR_OPEN_RETURN(DICT_DEBUG (&dict_cidr->dict));
}
diff --git a/postfix/src/util/dict_cidr.in b/postfix/src/util/dict_cidr.in
index 1798a60c9..ee20c1745 100644
--- a/postfix/src/util/dict_cidr.in
+++ b/postfix/src/util/dict_cidr.in
@@ -9,3 +9,15 @@ get 2001:240:5c7:0:2d0:b7ff:fe88:2ca7
get 2001:240:5c7:0:2d0:b7ff:febe:ca9f
get 1.1.1.1
get 1:1:1:1:1:1:1:1
+get 1.2.3.3
+get 1.2.3.4
+get 1.2.3.5
+get 1.2.3.6
+get 1.2.3.7
+get 1.2.3.8
+get ::f3
+get ::f4
+get ::f5
+get ::f6
+get ::f7
+get ::f8
diff --git a/postfix/src/util/dict_cidr.map b/postfix/src/util/dict_cidr.map
index 518e17149..b3b94f9b5 100644
--- a/postfix/src/util/dict_cidr.map
+++ b/postfix/src/util/dict_cidr.map
@@ -7,6 +7,32 @@
172.999.0.0/21 whatever
172.16.1.999 whatever
172.16.1.4
+if 1.2.0.0/16
+if 1.2.3.4/30
+1.2.3.3 1.2.3.3 can't happen
+1.2.3.4 1.2.3.4 can happen
+1.2.3.5 1.2.3.5 can happen
+1.2.3.6 1.2.3.6 can happen
+1.2.3.7 1.2.3.7 can happen
+1.2.3.8 1.2.3.8 can't happen
+endif
+endif
+if !1.2.3.4/30
+1.2.3.3 1.2.3.3 can happen
+1.2.3.8 1.2.3.8 can happen
+endif
+if ::f4/126
+::f3 ::f3 can't happen
+::f4 ::f4 can happen
+::f5 ::f5 can happen
+::f6 ::f6 can happen
+::f7 ::f7 can happen
+::f8 ::f8 can't happen
+endif
+if !::f4/126
+::f3 ::f3 can happen
+::f8 ::f8 can happen
+endif
2001:240:5c7:0:2d0:b7ff:fe88:2ca7 match 2001:240:5c7:0:2d0:b7ff:fe88:2ca7
2001:240:5c7::/64 match netblock 2001:240:5c7::/64
1.0.0.0/0 match 0.0.0.0/0
@@ -16,3 +42,9 @@
[1234 foo
[1234]junk bar
172.16.1.3/3x whatever
+endif
+endif
+if 1:2::3:4
+if 1:2::3:5
+if !
+!
diff --git a/postfix/src/util/dict_cidr.ref b/postfix/src/util/dict_cidr.ref
index 4a1853928..ceb2c99cf 100644
--- a/postfix/src/util/dict_cidr.ref
+++ b/postfix/src/util/dict_cidr.ref
@@ -3,11 +3,17 @@
./dict_open: warning: cidr map dict_cidr.map, line 7: bad net/mask pattern: "172.999.0.0/21": skipping this rule
./dict_open: warning: cidr map dict_cidr.map, line 8: bad address pattern: "172.16.1.999": skipping this rule
./dict_open: warning: cidr map dict_cidr.map, line 9: no lookup result: skipping this rule
-./dict_open: warning: cidr map dict_cidr.map, line 12: non-null host address bits in "1.0.0.0/0", perhaps you should use "0.0.0.0/0" instead: skipping this rule
-./dict_open: warning: cidr map dict_cidr.map, line 14: non-null host address bits in "1::/0", perhaps you should use "::/0" instead: skipping this rule
-./dict_open: warning: cidr map dict_cidr.map, line 16: missing ']' character after "[1234": skipping this rule
-./dict_open: warning: cidr map dict_cidr.map, line 17: garbage after "[1234]": skipping this rule
-./dict_open: warning: cidr map dict_cidr.map, line 18: bad net/mask pattern: "172.16.1.3/3x": skipping this rule
+./dict_open: warning: cidr map dict_cidr.map, line 38: non-null host address bits in "1.0.0.0/0", perhaps you should use "0.0.0.0/0" instead: skipping this rule
+./dict_open: warning: cidr map dict_cidr.map, line 40: non-null host address bits in "1::/0", perhaps you should use "::/0" instead: skipping this rule
+./dict_open: warning: cidr map dict_cidr.map, line 42: missing ']' character after "[1234": skipping this rule
+./dict_open: warning: cidr map dict_cidr.map, line 43: garbage after "[1234]": skipping this rule
+./dict_open: warning: cidr map dict_cidr.map, line 44: bad net/mask pattern: "172.16.1.3/3x": skipping this rule
+./dict_open: warning: cidr map dict_cidr.map, line 45: ENDIF without IF: skipping this rule
+./dict_open: warning: cidr map dict_cidr.map, line 46: ENDIF without IF: skipping this rule
+./dict_open: warning: cidr map dict_cidr.map, line 49: no pattern: skipping this rule
+./dict_open: warning: cidr map dict_cidr.map, line 50: no pattern: skipping this rule
+./dict_open: warning: cidr map dict_cidr.map, line 48: IF has no matching ENDIF
+./dict_open: warning: cidr map dict_cidr.map, line 47: IF has no matching ENDIF
owner=untrusted (uid=USER)
> get 172.16.0.0
172.16.0.0=554 match bad netblock 172.16.0.0/21
@@ -31,3 +37,27 @@ owner=untrusted (uid=USER)
1.1.1.1=match 0.0.0.0/0
> get 1:1:1:1:1:1:1:1
1:1:1:1:1:1:1:1=match ::/0
+> get 1.2.3.3
+1.2.3.3=1.2.3.3 can happen
+> get 1.2.3.4
+1.2.3.4=1.2.3.4 can happen
+> get 1.2.3.5
+1.2.3.5=1.2.3.5 can happen
+> get 1.2.3.6
+1.2.3.6=1.2.3.6 can happen
+> get 1.2.3.7
+1.2.3.7=1.2.3.7 can happen
+> get 1.2.3.8
+1.2.3.8=1.2.3.8 can happen
+> get ::f3
+::f3=::f3 can happen
+> get ::f4
+::f4=::f4 can happen
+> get ::f5
+::f5=::f5 can happen
+> get ::f6
+::f6=::f6 can happen
+> get ::f7
+::f7=::f7 can happen
+> get ::f8
+::f8=::f8 can happen
diff --git a/postfix/src/util/dict_pcre.c b/postfix/src/util/dict_pcre.c
index 17c500d28..8f3032638 100644
--- a/postfix/src/util/dict_pcre.c
+++ b/postfix/src/util/dict_pcre.c
@@ -27,6 +27,11 @@
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
/*--*/
#include "sys_defs.h"
@@ -614,8 +619,8 @@ static DICT_PCRE_RULE *dict_pcre_parse_rule(const char *mapname, int lineno,
while (*p && ISSPACE(*p))
++p;
if (!*p)
- msg_warn("%s, line %d: no replacement text: using empty string",
- mapname, lineno);
+ msg_warn("pcre map %s, line %d: no replacement text: "
+ "using empty string", mapname, lineno);
/*
* Sanity check the $number instances in the replacement text.
@@ -882,6 +887,7 @@ DICT *dict_pcre_open(const char *mapname, int open_flags, int dict_flags)
DICT_PCRE_IF_RULE *if_rule;
if (nesting-- <= 0)
+ /* Already handled in dict_pcre_parse_rule(). */
msg_panic("%s: ENDIF without IF", myname);
if (rule_stack[nesting]->op != DICT_PCRE_OP_IF)
msg_panic("%s: unexpected rule stack element type %d",
diff --git a/postfix/src/util/dict_pcre.in b/postfix/src/util/dict_pcre.in
index 324342e83..b0644bdc6 100644
--- a/postfix/src/util/dict_pcre.in
+++ b/postfix/src/util/dict_pcre.in
@@ -6,6 +6,7 @@ get 3
get true3
get c
get d
+get 1235
get 1234
get 123
get bar/find
diff --git a/postfix/src/util/dict_pcre.ref b/postfix/src/util/dict_pcre.ref
index ca008ed86..23eef8e01 100644
--- a/postfix/src/util/dict_pcre.ref
+++ b/postfix/src/util/dict_pcre.ref
@@ -2,14 +2,14 @@
./dict_open: warning: pcre map dict_pcre.map, line 1: do not prepend whitespace to statements between IF and ENDIF
./dict_open: warning: pcre map dict_pcre.map, line 5: ignoring extra text after ENDIF
./dict_open: warning: pcre map dict_pcre.map, line 8: unknown regexp option "!": skipping this rule
-./dict_open: warning: dict_pcre.map, line 9: no replacement text: using empty string
+./dict_open: warning: pcre map dict_pcre.map, line 9: no replacement text: using empty string
./dict_open: warning: pcre map dict_pcre.map, line 10: out of range replacement index "5": skipping this rule
./dict_open: warning: pcre map dict_pcre.map, line 17: $number found in negative match replacement text: skipping this rule
./dict_open: warning: pcre map dict_pcre.map, line 22: no regexp: skipping this rule
-./dict_open: warning: pcre map dict_pcre.map, line 23: ignoring ENDIF without matching IF
./dict_open: warning: pcre map dict_pcre.map, line 24: ignoring ENDIF without matching IF
+./dict_open: warning: pcre map dict_pcre.map, line 25: ignoring ENDIF without matching IF
+./dict_open: warning: pcre map dict_pcre.map, line 27: IF has no matching ENDIF
./dict_open: warning: pcre map dict_pcre.map, line 26: IF has no matching ENDIF
-./dict_open: warning: pcre map dict_pcre.map, line 25: IF has no matching ENDIF
owner=untrusted (uid=USER)
> get true
true: not found
@@ -27,6 +27,8 @@ true3=3
c=
> get d
d: not found
+> get 1235
+1235=(1)(2)(3)
> get 1234
1234=(1)(2)(3)(4)
> get 123
diff --git a/postfix/src/util/dict_regexp.c b/postfix/src/util/dict_regexp.c
index 953756bf2..1e5c449d3 100644
--- a/postfix/src/util/dict_regexp.c
+++ b/postfix/src/util/dict_regexp.c
@@ -31,6 +31,11 @@
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
/*--*/
/* System library. */
@@ -575,8 +580,8 @@ static DICT_REGEXP_RULE *dict_regexp_parseline(const char *mapname, int lineno,
while (*p && ISSPACE(*p))
++p;
if (!*p) {
- msg_warn("regexp map %s, line %d: using empty replacement string",
- mapname, lineno);
+ msg_warn("regexp map %s, line %d: no replacement text: "
+ "using empty string", mapname, lineno);
}
/*
@@ -819,6 +824,7 @@ DICT *dict_regexp_open(const char *mapname, int open_flags, int dict_flags)
DICT_REGEXP_IF_RULE *if_rule;
if (nesting-- <= 0)
+ /* Already handled in dict_regexp_parseline(). */
msg_panic("%s: ENDIF without IF", myname);
if (rule_stack[nesting]->op != DICT_REGEXP_OP_IF)
msg_panic("%s: unexpected rule stack element type %d",
diff --git a/postfix/src/util/dict_regexp.ref b/postfix/src/util/dict_regexp.ref
index 7b5bc88a9..2a08a3b73 100644
--- a/postfix/src/util/dict_regexp.ref
+++ b/postfix/src/util/dict_regexp.ref
@@ -1,7 +1,7 @@
./dict_open: warning: regexp map dict_regexp.map, line 1: ignoring extra text after IF statement: "fodder"
./dict_open: warning: regexp map dict_regexp.map, line 1: do not prepend whitespace to statements between IF and ENDIF
./dict_open: warning: regexp map dict_regexp.map, line 5: ignoring extra text after ENDIF
-./dict_open: warning: regexp map dict_regexp.map, line 9: using empty replacement string
+./dict_open: warning: regexp map dict_regexp.map, line 9: no replacement text: using empty string
./dict_open: warning: regexp map dict_regexp.map, line 10: out of range replacement index "5": skipping this rule
./dict_open: warning: regexp map dict_regexp.map, line 17: $number found in negative match replacement text: skipping this rule
./dict_open: warning: regexp map dict_regexp.map, line 22: no regexp: skipping this rule
diff --git a/postfix/src/util/mvect.c b/postfix/src/util/mvect.c
index 3353799f3..cf4b0d5bb 100644
--- a/postfix/src/util/mvect.c
+++ b/postfix/src/util/mvect.c
@@ -54,6 +54,11 @@
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
/*--*/
/* System library. */