]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-3.2-20160527
authorWietse Venema <wietse@porcupine.org>
Fri, 27 May 2016 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <postfix-users@dukhovni.org>
Sun, 29 May 2016 17:35:04 +0000 (13:35 -0400)
18 files changed:
postfix/HISTORY
postfix/RELEASE_NOTES
postfix/html/cidr_table.5.html
postfix/man/man5/cidr_table.5
postfix/proto/cidr_table
postfix/src/global/mail_version.h
postfix/src/util/cidr_match.c
postfix/src/util/cidr_match.h
postfix/src/util/dict_cidr.c
postfix/src/util/dict_cidr.in
postfix/src/util/dict_cidr.map
postfix/src/util/dict_cidr.ref
postfix/src/util/dict_pcre.c
postfix/src/util/dict_pcre.in
postfix/src/util/dict_pcre.ref
postfix/src/util/dict_regexp.c
postfix/src/util/dict_regexp.ref
postfix/src/util/mvect.c

index 4d58b31efddde95e155cc3c4cc2135e0977f5f0a..617174d71a47cd7db1fac4018883e591f3685399 100644 (file)
@@ -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.
index 117bb6b28e069e9539a308e383470ba4b9fac14d..94fab09e306a4a16e5f876b9e63671abe1b6d1f8 100644 (file)
@@ -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.
index 67b6ceb6c5f6b7b96a529978d3cacb102c2450ca..c8f63f9da962abfa5eacdd7ce636f635c458e819 100644 (file)
@@ -30,30 +30,39 @@ CIDR_TABLE(5)                                                    CIDR_TABLE(5)
 <b>TABLE FORMAT</b>
        The general form of a Postfix CIDR table is:
 
-       <i>network</i><b>_</b><i>address</i><b>/</b><i>network</i><b>_</b><i>mask     result</i>
-              When  a  search  string matches the specified network block, use
-              the corresponding <i>result</i> value. Specify 0.0.0.0/0 to match every
-              IPv4 address, and ::/0 to match every IPv6 address.
+       <i>pattern     result</i>
+              When a search string matches the specified <i>pattern</i>, use the cor-
+              responding <i>result</i> value. The <i>pattern</i> must be  in  <i>network/prefix</i>
+              or <i>network</i><b>_</b><i>address</i> 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 ":".
+       <b>!</b><i>pattern     result</i>
+              When  a  search string does not match the specified <i>pattern</i>, use
+              the specified <i>result</i> value. The <i>pattern</i> must be in  <i>network/pre-</i>
+              <i>fix</i>  or <i>network</i><b>_</b><i>address</i> form (see ADDRESS PATTERN SYNTAX below).
 
-              The  <i>network</i><b>_</b><i>mask</i>  is  the number of high-order bits in the <i>net-</i>
-              <i>work</i><b>_</b><i>address</i> 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.
+       <b>if</b> <i>pattern</i>
 
-              Note: address information may be enclosed inside "[]"  but  this
-              form is not required.
+       <b>endif</b>  When a search string matches the specified <i>pattern</i>,  match  that
+              search  string  against  the patterns between <b>if</b> and <b>endif</b>.  The
+              <i>pattern</i> must be in <i>network/prefix</i> or <i>network</i><b>_</b><i>address</i>  form  (see
+              ADDRESS PATTERN SYNTAX below). The <b>if</b>..<b>endif</b> can nest.
 
-              IPv6 support is available in Postfix 2.2 and later.
+              Note: do not prepend whitespace to text between <b>if</b>..<b>endif</b>.
 
-       <i>network</i><b>_</b><i>address     result</i>
-              When  a search string matches the specified network address, use
-              the corresponding <i>result</i> value.
+              This feature is available in Postfix 3.2 and later.
+
+       <b>if !</b><i>pattern</i>
+
+       <b>endif</b>  When a search string does not match the specified <i>pattern</i>, match
+              that search string against the patterns between  <b>if</b>  and  <b>endif</b>.
+              The  <i>pattern</i>  must  be in <i>network/prefix</i> or <i>network</i><b>_</b><i>address</i> form
+              (see ADDRESS PATTERN SYNTAX below). The <b>if</b>..<b>endif</b> can nest.
+
+              Note: do not prepend whitespace to text between <b>if</b>..<b>endif</b>.
+
+              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.
 
+<b>ADDRESS PATTERN SYNTAX</b>
+       Postfix  CIDR  tables  are  pattern-based.  A  pattern is either a <i>net-</i>
+       <i>work</i><b>_</b><i>address</i> which requires an exact match, or  a  <i>network</i><b>_</b><i>address/pre-</i>
+       <i>fix</i><b>_</b><i>length</i>  where  the  <i>prefix</i><b>_</b><i>length</i>  part specifies the length of the
+       <i>network</i><b>_</b><i>address</i> prefix that must be matched (the other bits in the <i>net-</i>
+       <i>work</i><b>_</b><i>address</i> 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.
+
 <b>EXAMPLE SMTPD ACCESS MAP</b>
        /etc/postfix/<a href="postconf.5.html">main.cf</a>:
            <a href="postconf.5.html#smtpd_client_restrictions">smtpd_client_restrictions</a> = ... <a href="cidr_table.5.html">cidr</a>:/etc/postfix/client.cidr ...
index b199e39aec3356823e9426423037cba84cfea758..51a58a9d4ff48afad983bad1f223893157973933 100644 (file)
@@ -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
index 4787a79d35cb2fe7803d6c57078941c807b8692b..51d39aa13e2b903140904507bf80f646019d9a57 100644 (file)
 # .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 `#'.
 # .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:
index c5e9d73461e2342e03b88c16b1fc9d21ae1b19ac..00b7d7b4151be01450a82fa305a7149fc46665ae 100644 (file)
@@ -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
index 387c02f627e80d5a2e032732c6a9151d58a07d4f..7a921d75ffb7c7266fddf395c3797cdc3bea1985 100644 (file)
 /*     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
 /*     (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.
 /*     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. */
      (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;
+}
index 153394f80e7e007a14ea326cfadf07cdb7fe2f28..1fab97671e3290f6013eb223b9ac255ea60c6516 100644 (file)
@@ -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
index e14611a7345ddd35418a4476ba032af1e9544bdd..112582bc6cf2a6e810c5266904e4a73d11a0e65c 100644 (file)
 /*     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 <cidr_match.h>
 #include <dict_cidr.h>
 #include <warn_stat.h>
+#include <mvect.h>
 
 /* 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 (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 = "";
     }
-    if (*value == 0) {
-       vstring_sprintf(why, "no lookup result");
-       return (0);
+
+    /*
+     * ENDIF must not be followed by other text.
+     */
+    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 = "";
     }
 
     /*
-     * Parse the pattern, destroying it in the process.
+     * An address pattern.
      */
-    if (cidr_match_parse(&cidr_info, pattern, why) != 0)
-       return (0);
+    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));
 }
index 1798a60c9deb3209f2cc7241bd87858f9d3f73b8..ee20c17452d5e1f87058bb12ae2bba54c61dd050 100644 (file)
@@ -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
index 518e17149c83ceb28acfab157766b5ea26074052..b3b94f9b5328884b84a904215181403364ef9baa 100644 (file)
@@ -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 !
+!
index 4a185392884b0ab8c12ac29afb2a59020d41203b..ceb2c99cf4d75047e78c7f3b834db0181178b36d 100644 (file)
@@ -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
index 17c500d286aef1876b93815e0adf9a638814d64b..8f3032638b659e66d6b5c922261e19745df639da 100644 (file)
 /*     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",
index 324342e8313860b8bb1621599981e5da59a59287..b0644bdc6b3b4e6e51cd8239583c59df1e9346e1 100644 (file)
@@ -6,6 +6,7 @@ get 3
 get true3
 get c
 get d
+get 1235
 get 1234
 get 123
 get bar/find
index ca008ed86576bbc69ca441583b6a152adf251404..23eef8e01fd6c2c10a0ede4fd245006df1adf4f3 100644 (file)
@@ -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
index 953756bf2fad0f37829b4a5c86e7de051598d6b6..1e5c449d37b1e1ff6ae1e1d6485c8d965af99a55 100644 (file)
 /*     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",
index 7b5bc88a9c281627f233a0f3e4d7008d130d2af4..2a08a3b73ab62493441cd8aa1334830a3551b5f3 100644 (file)
@@ -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
index 3353799f3d28688dbddc438ce85e6c9b985da14a..cf4b0d5bbfe977318925d92f515195e7b2506387 100644 (file)
 /*     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. */