Portability: OpenBSD 6.0. Files: makedefs, util/sys_defs.h,
dns/dns_str_resflags.c.
+
+20160521
+
+ Bugfix (introduced: Postfix beta): the never-used function
+ mvect_free() attempted to free memory that it has not
+ allocated. File: util/mvect.c.
+
+ 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.
Disable -DSNAPSHOT and -DNONPROD in makedefs.
- In-memory file, so that we can say pcre:{name=value, ...},
- pcre:{name=value, ...} without having to open a file. This
- would not work for lmdb:, hash:, btree: etc., but it should
- make texthash: equivalent to inline:. This would not be a
- great example of user-friendliness, but it wouuld simplify
- regression tests.
+ Modeline support in config files to enable/disable trailing
+ #comment, and to give hints about how to handle an LHS or
+ RHS.
Maintainability: replace lengthy libmilter-API argument lists
with named parameters, as with the libtls API.
-
- Wat's up with all the lowercase() calls in dict*c when
- casefolding he search key?
Fix buflen integer overflow detection in dict*sql.c.
Log command=good/bad statistics in postscreen?
- Remember multiple access map BCC actions, for consistency
- with header_checks.
-
smtpd_checks tests either must use a DNS dummy resolver
(override the res_search API) or all names must be under
- porcupine.org (but that does not work for address->name
+ test.postfix.org (but that does not work for address->name
lookups, and cannot simulate some errors).
Reporting the original Message-ID in a bounce message
* Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only.
*/
-#define MAIL_RELEASE_DATE "20160515"
+#define MAIL_RELEASE_DATE "20160522"
#define MAIL_VERSION_NUMBER "3.2"
#ifdef SNAPSHOT
myaddrinfo_test format_tv_test ip_match_test name_mask_tests \
base32_code_test dict_thash_test surrogate_test timecmp_test \
dict_static_test dict_inline_test midna_domain_test casefold_test \
- dict_utf8_test strcasecmp_utf8_test vbuf_print_test
+ dict_utf8_test strcasecmp_utf8_test vbuf_print_test dict_regexp_test
root_tests:
rm -f dict_pcre.tmp
dict_regexp_test: dict_open dict_regexp.in dict_regexp.map dict_regexp.ref
- $(SHLIB_ENV) ./dict_open regexp:dict_regexp.map read <dict_regexp.in | sed 's/uid=[0-9][0-9][0-9]*/uid=USER/' >dict_regexp.tmp
+ $(SHLIB_ENV) ./dict_open regexp:dict_regexp.map read <dict_regexp.in 2>&1 | sed 's/uid=[0-9][0-9][0-9]*/uid=USER/' >dict_regexp.tmp
diff dict_regexp.ref dict_regexp.tmp
rm -f dict_regexp.tmp
dict_pcre.o: dict_pcre.h
dict_pcre.o: mac_parse.h
dict_pcre.o: msg.h
+dict_pcre.o: mvect.h
dict_pcre.o: myflock.h
dict_pcre.o: mymalloc.h
dict_pcre.o: readlline.h
dict_regexp.o: dict_regexp.h
dict_regexp.o: mac_parse.h
dict_regexp.o: msg.h
+dict_regexp.o: mvect.h
dict_regexp.o: myflock.h
dict_regexp.o: mymalloc.h
dict_regexp.o: readlline.h
load_file.o: vstream.h
load_file.o: warn_stat.h
load_lib.o: load_lib.c
-load_lib.o: load_lib.h
-load_lib.o: msg.h
load_lib.o: sys_defs.h
lowercase.o: check_arg.h
lowercase.o: lowercase.c
#include "mac_parse.h"
#include "pcre.h"
#include "warn_stat.h"
+#include "mvect.h"
/*
* Backwards compatibility.
*/
typedef struct DICT_PCRE_RULE {
int op; /* DICT_PCRE_OP_MATCH/IF/ENDIF */
- int nesting; /* level of IF/ENDIF nesting */
int lineno; /* source file line number */
struct DICT_PCRE_RULE *next; /* next rule in dict */
} DICT_PCRE_RULE;
pcre *pattern; /* compiled pattern */
pcre_extra *hints; /* hints to speed pattern execution */
int match; /* positive or negative match */
+ struct DICT_PCRE_RULE *endif_rule; /* matching endif rule */
} DICT_PCRE_IF_RULE;
/*
return;
#endif
default:
- msg_warn("pcre map %s, line %d: unknown re_exec error: %d",
+ msg_warn("pcre map %s, line %d: unknown pcre_exec error: %d",
mapname, lineno, errval);
return;
}
}
+ /*
+ * Inlined to reduce function call overhead in the time-critical loop.
+ */
+#define DICT_PCRE_EXEC(ctxt, map, line, pattern, hints, match, str, len) \
+ ((ctxt).matches = pcre_exec((pattern), (hints), (str), (len), \
+ NULL_STARTOFFSET, NULL_EXEC_OPTIONS, \
+ (ctxt).offsets, PCRE_MAX_CAPTURE * 3), \
+ (ctxt).matches > 0 ? (match) : \
+ (ctxt).matches == PCRE_ERROR_NOMATCH ? !(match) : \
+ (dict_pcre_exec_error((map), (line), (ctxt).matches), 0))
+
/* dict_pcre_lookup - match string and perform optional substitution */
static const char *dict_pcre_lookup(DICT *dict, const char *lookup_string)
DICT_PCRE_MATCH_RULE *match_rule;
int lookup_len = strlen(lookup_string);
DICT_PCRE_EXPAND_CONTEXT ctxt;
- int nesting = 0;
dict->error = 0;
}
for (rule = dict_pcre->head; rule; rule = rule->next) {
- /*
- * Skip rules inside failed IF/ENDIF.
- */
- if (nesting < rule->nesting)
- continue;
-
switch (rule->op) {
/*
*/
case DICT_PCRE_OP_MATCH:
match_rule = (DICT_PCRE_MATCH_RULE *) rule;
- ctxt.matches = pcre_exec(match_rule->pattern, match_rule->hints,
- lookup_string, lookup_len,
- NULL_STARTOFFSET, NULL_EXEC_OPTIONS,
- ctxt.offsets, PCRE_MAX_CAPTURE * 3);
-
- if (ctxt.matches > 0) {
- if (!match_rule->match)
- continue; /* Negative rule matched */
- } else if (ctxt.matches == PCRE_ERROR_NOMATCH) {
- if (match_rule->match)
- continue; /* Positive rule did not
- * match */
- } else {
- dict_pcre_exec_error(dict->name, rule->lineno, ctxt.matches);
- continue; /* pcre_exec failed */
- }
+ if (!DICT_PCRE_EXEC(ctxt, dict->name, rule->lineno,
+ match_rule->pattern, match_rule->hints,
+ match_rule->match, lookup_string, lookup_len))
+ continue;
/*
* Skip $number substitutions when the replacement text contains
*/
case DICT_PCRE_OP_IF:
if_rule = (DICT_PCRE_IF_RULE *) rule;
- ctxt.matches = pcre_exec(if_rule->pattern, if_rule->hints,
- lookup_string, lookup_len,
- NULL_STARTOFFSET, NULL_EXEC_OPTIONS,
- ctxt.offsets, PCRE_MAX_CAPTURE * 3);
-
- if (ctxt.matches > 0) {
- if (!if_rule->match)
- continue; /* Negative rule matched */
- } else if (ctxt.matches == PCRE_ERROR_NOMATCH) {
- if (if_rule->match)
- continue; /* Positive rule did not
- * match */
- } else {
- dict_pcre_exec_error(dict->name, rule->lineno, ctxt.matches);
- continue; /* pcre_exec failed */
- }
- nesting++;
- continue;
+ if (DICT_PCRE_EXEC(ctxt, dict->name, rule->lineno,
+ if_rule->pattern, if_rule->hints,
+ if_rule->match, lookup_string, lookup_len))
+ continue;
+ /* An IF without matching ENDIF has no "endif" rule. */
+ if ((rule = if_rule->endif_rule) == 0)
+ break;
+ /* FALLTHROUGH */
/*
- * ENDIF after successful IF.
+ * ENDIF after IF.
*/
case DICT_PCRE_OP_ENDIF:
- nesting--;
continue;
default:
/* dict_pcre_rule_alloc - fill in a generic rule structure */
-static DICT_PCRE_RULE *dict_pcre_rule_alloc(int op, int nesting,
- int lineno,
- size_t size)
+static DICT_PCRE_RULE *dict_pcre_rule_alloc(int op, int lineno, size_t size)
{
DICT_PCRE_RULE *rule;
rule = (DICT_PCRE_RULE *) mymalloc(size);
rule->op = op;
- rule->nesting = nesting;
rule->lineno = lineno;
rule->next = 0;
* Save the result.
*/
match_rule = (DICT_PCRE_MATCH_RULE *)
- dict_pcre_rule_alloc(DICT_PCRE_OP_MATCH, nesting, lineno,
+ dict_pcre_rule_alloc(DICT_PCRE_OP_MATCH, lineno,
sizeof(DICT_PCRE_MATCH_RULE));
match_rule->match = regexp.match;
match_rule->max_sub = prescan_context.max_sub;
* Save the result.
*/
if_rule = (DICT_PCRE_IF_RULE *)
- dict_pcre_rule_alloc(DICT_PCRE_OP_IF, nesting, lineno,
+ dict_pcre_rule_alloc(DICT_PCRE_OP_IF, lineno,
sizeof(DICT_PCRE_IF_RULE));
if_rule->match = regexp.match;
if_rule->pattern = engine.pattern;
/*
* Save the result.
*/
- rule = dict_pcre_rule_alloc(DICT_PCRE_OP_ENDIF, nesting, lineno,
+ rule = dict_pcre_rule_alloc(DICT_PCRE_OP_ENDIF, lineno,
sizeof(DICT_PCRE_RULE));
return (rule);
}
DICT *dict_pcre_open(const char *mapname, int open_flags, int dict_flags)
{
+ const char myname[] = "dict_pcre_open";
DICT_PCRE *dict_pcre;
VSTREAM *map_fp = 0;
struct stat st;
int lineno;
int nesting = 0;
char *p;
+ DICT_PCRE_RULE **rule_stack = 0;
+ MVECT mvect;
/*
* Let the optimizer worry about eliminating redundant code.
if (rule == 0)
continue;
if (rule->op == DICT_PCRE_OP_IF) {
+ if (rule_stack == 0)
+ rule_stack = (DICT_PCRE_RULE **) mvect_alloc(&mvect,
+ sizeof(*rule_stack), nesting + 1,
+ (MVECT_FN) 0, (MVECT_FN) 0);
+ else
+ rule_stack =
+ (DICT_PCRE_RULE **) mvect_realloc(&mvect, nesting + 1);
+ rule_stack[nesting] = rule;
nesting++;
} else if (rule->op == DICT_PCRE_OP_ENDIF) {
- nesting--;
+ DICT_PCRE_IF_RULE *if_rule;
+
+ if (nesting-- <= 0)
+ 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",
+ myname, rule_stack[nesting]->op);
+ if_rule = (DICT_PCRE_IF_RULE *) rule_stack[nesting];
+ if_rule->endif_rule = rule;
}
if (last_rule == 0)
dict_pcre->head = rule;
last_rule = rule;
}
- if (nesting)
- msg_warn("pcre map %s, line %d: more IFs than ENDIFs",
- mapname, lineno);
+ while (nesting-- > 0)
+ msg_warn("pcre map %s, line %d: IF has no matching ENDIF",
+ mapname, rule_stack[nesting]->lineno);
+
+ if (rule_stack)
+ (void) mvect_free(&mvect);
DICT_PCRE_OPEN_RETURN(DICT_DEBUG (&dict_pcre->dict));
}
endif
# trailing whitespace above
!
+# dangling endif and if
+endif
+endif
+if /./
+if /./
./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 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
#include "dict_regexp.h"
#include "mac_parse.h"
#include "warn_stat.h"
+#include "mvect.h"
/*
* Support for IF/ENDIF based on an idea by Bert Driehuis.
*/
typedef struct DICT_REGEXP_RULE {
int op; /* DICT_REGEXP_OP_MATCH/IF/ENDIF */
- int nesting; /* Level of search nesting */
int lineno; /* source file line number */
struct DICT_REGEXP_RULE *next; /* next rule in dict */
} DICT_REGEXP_RULE;
DICT_REGEXP_RULE rule; /* generic members */
regex_t *expr; /* the condition */
int match; /* positive or negative match */
+ struct DICT_REGEXP_RULE *endif_rule;/* matching endif rule */
} DICT_REGEXP_IF_RULE;
/*
DICT_REGEXP_MATCH_RULE *match_rule;
DICT_REGEXP_EXPAND_CONTEXT expand_context;
int error;
- int nesting = 0;
dict->error = 0;
}
for (rule = dict_regexp->head; rule; rule = rule->next) {
- /*
- * Skip rules inside failed IF/ENDIF.
- */
- if (nesting < rule->nesting)
- continue;
-
switch (rule->op) {
/*
if (DICT_REGEXP_REGEXEC(error, dict->name, rule->lineno,
if_rule->expr, if_rule->match, lookup_string,
NULL_SUBSTITUTIONS, NULL_MATCH_RESULT))
- nesting++;
- continue;
+ continue;
+ /* An IF without matching ENDIF has no "endif" rule. */
+ if ((rule = if_rule->endif_rule) == 0)
+ break;
+ /* FALLTHROUGH */
/*
- * ENDIF after successful IF.
+ * ENDIF after IF.
*/
case DICT_REGEXP_OP_ENDIF:
- nesting--;
continue;
default:
/* dict_regexp_rule_alloc - fill in a generic rule structure */
-static DICT_REGEXP_RULE *dict_regexp_rule_alloc(int op, int nesting,
- int lineno,
- size_t size)
+static DICT_REGEXP_RULE *dict_regexp_rule_alloc(int op, int lineno, size_t size)
{
DICT_REGEXP_RULE *rule;
rule = (DICT_REGEXP_RULE *) mymalloc(size);
rule->op = op;
- rule->nesting = nesting;
rule->lineno = lineno;
rule->next = 0;
second_exp = 0;
}
match_rule = (DICT_REGEXP_MATCH_RULE *)
- dict_regexp_rule_alloc(DICT_REGEXP_OP_MATCH, nesting, lineno,
+ dict_regexp_rule_alloc(DICT_REGEXP_OP_MATCH, lineno,
sizeof(DICT_REGEXP_MATCH_RULE));
match_rule->first_exp = first_exp;
match_rule->first_match = first_pat.match;
if ((expr = dict_regexp_compile_pat(mapname, lineno, &pattern)) == 0)
return (0);
if_rule = (DICT_REGEXP_IF_RULE *)
- dict_regexp_rule_alloc(DICT_REGEXP_OP_IF, nesting, lineno,
+ dict_regexp_rule_alloc(DICT_REGEXP_OP_IF, lineno,
sizeof(DICT_REGEXP_IF_RULE));
if_rule->expr = expr;
if_rule->match = pattern.match;
if (*p)
msg_warn("regexp map %s, line %d: ignoring extra text after ENDIF",
mapname, lineno);
- rule = dict_regexp_rule_alloc(DICT_REGEXP_OP_ENDIF, nesting, lineno,
+ rule = dict_regexp_rule_alloc(DICT_REGEXP_OP_ENDIF, lineno,
sizeof(DICT_REGEXP_RULE));
return (rule);
}
DICT *dict_regexp_open(const char *mapname, int open_flags, int dict_flags)
{
+ const char myname[] = "dict_regexp_open";
DICT_REGEXP *dict_regexp;
VSTREAM *map_fp = 0;
struct stat st;
size_t max_sub = 0;
int nesting = 0;
char *p;
+ DICT_REGEXP_RULE **rule_stack = 0;
+ MVECT mvect;
/*
* Let the optimizer worry about eliminating redundant code.
if (((DICT_REGEXP_MATCH_RULE *) rule)->max_sub > max_sub)
max_sub = ((DICT_REGEXP_MATCH_RULE *) rule)->max_sub;
} else if (rule->op == DICT_REGEXP_OP_IF) {
+ if (rule_stack == 0)
+ rule_stack = (DICT_REGEXP_RULE **) mvect_alloc(&mvect,
+ sizeof(*rule_stack), nesting + 1,
+ (MVECT_FN) 0, (MVECT_FN) 0);
+ else
+ rule_stack =
+ (DICT_REGEXP_RULE **) mvect_realloc(&mvect, nesting + 1);
+ rule_stack[nesting] = rule;
nesting++;
} else if (rule->op == DICT_REGEXP_OP_ENDIF) {
- nesting--;
+ DICT_REGEXP_IF_RULE *if_rule;
+
+ if (nesting-- <= 0)
+ 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",
+ myname, rule_stack[nesting]->op);
+ if_rule = (DICT_REGEXP_IF_RULE *) rule_stack[nesting];
+ if_rule->endif_rule = rule;
}
if (last_rule == 0)
dict_regexp->head = rule;
last_rule = rule;
}
- if (nesting)
- msg_warn("regexp map %s, line %d: more IFs than ENDIFs",
- mapname, lineno);
+ while (nesting-- > 0)
+ msg_warn("regexp map %s, line %d: IF has no matching ENDIF",
+ mapname, rule_stack[nesting]->lineno);
+
+ if (rule_stack)
+ (void) mvect_free(&mvect);
/*
* Allocate space for only as many matched substrings as used in the
endif
# trailing whitespace above
!
+# dangling endif and if
+endif
+endif
+if /./
+if /./
./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
+./dict_open: warning: regexp map dict_regexp.map, line 24: ignoring ENDIF without matching IF
+./dict_open: warning: regexp map dict_regexp.map, line 25: ignoring ENDIF without matching IF
+./dict_open: warning: regexp map dict_regexp.map, line 27: IF has no matching ENDIF
+./dict_open: warning: regexp map dict_regexp.map, line 26: IF has no matching ENDIF
+owner=untrusted (uid=USER)
> get true
true: not found
> get true1
if (vect->wipe_fn)
vect->wipe_fn(vect->ptr, vect->nelm);
myfree(vect->ptr);
- myfree((void *) vect);
return (0);
}