2
0
mirror of https://github.com/vdukhovni/postfix synced 2025-08-28 12:48:01 +00:00

postfix-3.2-20160527

This commit is contained in:
Wietse Venema 2016-05-27 00:00:00 -05:00 committed by Viktor Dukhovni
parent 84739fb911
commit 4a9d39443f
18 changed files with 534 additions and 128 deletions

View 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.

View 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.

View 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 ...

View 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

View File

@ -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:

View 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

View File

@ -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;
}

View 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

View File

@ -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 <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 (*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));
}

View 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

View 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 !
!

View 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

View File

@ -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",

View File

@ -6,6 +6,7 @@ get 3
get true3
get c
get d
get 1235
get 1234
get 123
get bar/find

View 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

View File

@ -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",

View 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

View File

@ -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. */