20230330
Safety: the long form { name = value } in import_environment
- or export_environment is not documented, but accepted, and
- it was stored in the process environment as the invalid
- form "name = value" instead of the expected "name=value".
- Found during code maintenance. Also refined an "empty name"
- check. Files: clean_env.c, split_nameval.c.
+ or export_environment is not documented, but it is accepted,
+ and it was stored in the process environment as the invalid
+ form "name = value, thus not setting or overriding an entry
+ for "name". This form is now stored as the expected
+ "name=value". Found during code maintenance. Also refined
+ the "missing attribute name" detection. Files: clean_env.c,
+ split_nameval.c.
20230402
example, inline maps. Added Valgrind support to the namadr_list
unit test. Files: util/match_list.c, global/namadr_list.in,
util/Makefile.in.
+
+20240406
+
+ Bugfix (introduced: 20230402): after a change in the DNS_RR
+ structure, the dns_rr_copy() function had not been updated,
+ causing the Postfix SMTP client to panic as it detected a
+ double-free() attempt. Reported by Florian Piekert. File:
+ dns/dns_rr.c.
+
+ Usability: Postfix does not support #comments after other
+ text, but people add them anyway, with unexpected results.
+ The postconf command now warns for trailing comments in
+ main.cf files. Similar warnings are planned for database
+ client configuration files. Files: util/mystrtok.c,
+ util/mystrtok.ref, util/match_list.c, global/namadr_list.ref,
+ postconf/postconf_dbms.c, postconf/test71.ref.
+
+ TODO: #comment after text in DB client configuration files.
+
+ TOIDO: test for dns_rr_copy() + dns_rr_free().
bitcount
bytecount
ipproto
+cw
{
DNS_RR *rr;
+ /*
+ * Note: if this function is changed, update dns_rr_copy().
+ */
rr = (DNS_RR *) mymalloc(sizeof(*rr));
rr->qname = mystrdup(qname);
rr->rname = mystrdup(rname);
DNS_RR *dns_rr_copy(DNS_RR *src)
{
- ssize_t len = sizeof(*src) + src->data_len - 1;
DNS_RR *dst;
/*
- * Combine struct assignment and data copy in one block copy operation.
+ * Note: struct copy, because dns_rr_create() would not copy all fields.
*/
- dst = (DNS_RR *) mymalloc(len);
- memcpy((void *) dst, (void *) src, len);
+ dst = (DNS_RR *) mymalloc(sizeof(*dst));
+ memcpy((void *) dst, (void *) src, sizeof(*dst));
dst->qname = mystrdup(src->qname);
dst->rname = mystrdup(src->rname);
+ if (dst->data)
+ dst->data = mymemdup(dst->data, dst->data_len);
dst->next = 0;
return (dst);
}
* Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only.
*/
-#define MAIL_RELEASE_DATE "20230402"
+#define MAIL_RELEASE_DATE "20230406"
#define MAIL_VERSION_NUMBER "3.8"
#ifdef SNAPSHOT
./namadr_list: warning: non-existent:/tmp/nosuchfile is unavailable. open file /tmp/nosuchfile: No such file or directory
./namadr_list: warning: command line: non-existent:/tmp/nosuchfile: table lookup problem
bar/168.100.3.3: ERROR
-./namadr_list: warning: command line: comment at end of line is not supported: #text
+./namadr_list: warning: command line: #comment after other text is not allowed: #text ...
foo/1.2.3.4: YES
-./namadr_list: warning: command line: comment at end of line is not supported: #text
+./namadr_list: warning: command line: #comment after other text is not allowed: #text ...
fool/1.2.3.4: NO
foo/1.2.3.4: YES
bar/1.2.3.4: YES
test31 test32 test33 test34 test35 test36 test37 test39 test40 test41 \
test42 test43 test44 test45 test46 test47 test48 test49 test50 test51 \
test52 test53 test54 test55 test56 test57 test58 test59 test60 test61 \
- test62 test63 test64 test65 test66 test67 test68 test69 test70
+ test62 test63 test64 test65 test66 test67 test68 test69 test70 test71
root_tests:
touch -t 197101010000 main.cf
$(HTABLE_FIX) $(SHLIB_ENV) $(VALGRIND) ./$(PROG) -nc . >test70.tmp 2>&1
diff test70.ref test70.tmp
- rm -f main.cf master.cf test70.tmp test70.cf
+ rm -f main.cf master.cf test70.tmp
+
+test71: $(PROG) test71.ref
+ rm -f main.cf master.cf
+ touch main.cf master.cf
+ echo "smtpd_client_restrictions = inline:{" >>main.cf
+ echo " { aaa0 = #aaa1 } #aaa2" >>main.cf
+ echo " }" >>main.cf
+ echo "smtpd_helo_restrictions = pcre:{" >>main.cf
+ echo " { /bbb0 #bbb1/ } #bbb2" >>main.cf
+ echo " }" >>main.cf
+ echo "smtpd_sender_restrictions = regexp:{" >>main.cf
+ echo " { /ccc0 #ccc1/ } #ccc2" >>main.cf
+ echo " }" >>main.cf
+ touch -t 197101010000 main.cf
+ $(HTABLE_FIX) $(SHLIB_ENV) $(VALGRIND) ./$(PROG) -nc . >test71.tmp 2>&1
+ diff test71.ref test71.tmp
+ rm -f main.cf master.cf test71.tmp
printfck: $(OBJS) $(PROG)
rm -rf printfck
postconf_dbms.o: ../../include/dict_ldap.h
postconf_dbms.o: ../../include/dict_memcache.h
postconf_dbms.o: ../../include/dict_mysql.h
+postconf_dbms.o: ../../include/dict_pcre.h
postconf_dbms.o: ../../include/dict_pgsql.h
postconf_dbms.o: ../../include/dict_proxy.h
+postconf_dbms.o: ../../include/dict_regexp.h
postconf_dbms.o: ../../include/dict_sqlite.h
postconf_dbms.o: ../../include/htable.h
postconf_dbms.o: ../../include/mac_expand.h
/* Google, Inc.
/* 111 8th Avenue
/* New York, NY 10011, USA
+/*
+/* Wietse Venema
/*--*/
/* System library. */
#include <dict_pgsql.h>
#include <dict_sqlite.h>
#include <dict_memcache.h>
+#include <dict_regexp.h>
+#include <dict_pcre.h>
/* Application-specific. */
*/
typedef struct {
const char *db_type;
+ int db_class;
const char **db_suffixes;
} PCF_DBMS_INFO;
+#define PCF_DBMS_CLASS_CLIENT (1) /* DB name is client config path */
+#define PCF_DBMS_CLASS_REGEX (2) /* DB name contains regex patterns */
+
static const PCF_DBMS_INFO pcf_dbms_info[] = {
- DICT_TYPE_LDAP, pcf_ldap_suffixes,
- DICT_TYPE_MYSQL, pcf_mysql_suffixes,
- DICT_TYPE_PGSQL, pcf_pgsql_suffixes,
- DICT_TYPE_SQLITE, pcf_sqlite_suffixes,
- DICT_TYPE_MEMCACHE, pcf_memcache_suffixes,
- 0,
+ {DICT_TYPE_LDAP, PCF_DBMS_CLASS_CLIENT, pcf_ldap_suffixes},
+ {DICT_TYPE_MYSQL, PCF_DBMS_CLASS_CLIENT, pcf_mysql_suffixes},
+ {DICT_TYPE_PGSQL, PCF_DBMS_CLASS_CLIENT, pcf_pgsql_suffixes},
+ {DICT_TYPE_SQLITE, PCF_DBMS_CLASS_CLIENT, pcf_sqlite_suffixes},
+ {DICT_TYPE_MEMCACHE, PCF_DBMS_CLASS_CLIENT, pcf_memcache_suffixes},
+ {DICT_TYPE_REGEXP, PCF_DBMS_CLASS_REGEX},
+ {DICT_TYPE_PCRE, PCF_DBMS_CLASS_REGEX},
+ {0},
};
+ /*
+ * Workaround to prevent a false warning about "#comment after other text",
+ * when an inline pcre or regexp pattern contains "#text".
+ */
+#define PCF_DBMS_RECURSE 1 /* Parse inline {map-entry} */
+#define PCF_DBMS_NO_RECURSE 0 /* Don't parse inline {map-entry} */
+
/* pcf_check_dbms_client - look for unused names in client configuration */
static void pcf_check_dbms_client(const PCF_DBMS_INFO *dp, const char *cf_file)
static void pcf_register_dbms_helper(char *str_value,
const char *(flag_parameter) (const char *, int, PCF_MASTER_ENT *),
- PCF_MASTER_ENT *local_scope)
+ PCF_MASTER_ENT *local_scope,
+ int recurse)
{
const PCF_DBMS_INFO *dp;
char *db_type;
* Naive parsing. We don't really know if this substring specifies a
* database or some other text.
*/
- while ((db_type = mystrtokq(&str_value, CHARS_COMMA_SP, CHARS_BRACE)) != 0) {
+ while ((db_type = mystrtokq_cw(&str_value, CHARS_COMMA_SP, CHARS_BRACE,
+ local_scope ? MASTER_CONF_FILE : MAIN_CONF_FILE)) != 0) {
if (*db_type == CHARS_BRACE[0]) {
if ((err = extpar(&db_type, CHARS_BRACE, EXTPAR_FLAG_NONE)) != 0) {
/* XXX Encapsulate this in pcf_warn() function. */
msg_warn("%s: %s", MAIN_CONF_FILE, err);
myfree(err);
}
- pcf_register_dbms_helper(db_type, flag_parameter, local_scope);
+ if (recurse)
+ pcf_register_dbms_helper(db_type, flag_parameter, local_scope,
+ recurse);
continue;
}
if (*prefix == '/') {
for (dp = pcf_dbms_info; dp->db_type != 0; dp++) {
if (strcmp(db_type, dp->db_type) == 0) {
- pcf_check_dbms_client(dp, prefix);
+ if (dp->db_class == PCF_DBMS_CLASS_CLIENT)
+ pcf_check_dbms_client(dp, prefix);
break;
}
}
* local or global namespace.
*/
if (*prefix != '.') {
+ int next_recurse = recurse;
+
if (*prefix == CHARS_BRACE[0]) {
if ((err = extpar(&prefix, CHARS_BRACE, EXTPAR_FLAG_NONE)) != 0) {
/* XXX Encapsulate this in pcf_warn() function. */
msg_warn("%s: %s", MAIN_CONF_FILE, err);
myfree(err);
}
- pcf_register_dbms_helper(prefix, flag_parameter, local_scope);
+ for (dp = pcf_dbms_info; dp->db_type != 0; dp++) {
+ if (strcmp(db_type, dp->db_type) == 0) {
+ if (dp->db_class == PCF_DBMS_CLASS_REGEX)
+ next_recurse = PCF_DBMS_NO_RECURSE;
+ break;
+ }
+ }
+ pcf_register_dbms_helper(prefix, flag_parameter, local_scope,
+ next_recurse);
continue;
} else {
for (dp = pcf_dbms_info; dp->db_type != 0; dp++) {
if (strcmp(db_type, dp->db_type) == 0) {
- for (cpp = dp->db_suffixes; *cpp; cpp++) {
- vstring_sprintf(candidate ? candidate :
+ if (dp->db_class == PCF_DBMS_CLASS_CLIENT) {
+ for (cpp = dp->db_suffixes; *cpp; cpp++) {
+ vstring_sprintf(candidate ? candidate :
(candidate = vstring_alloc(30)),
- "%s_%s", prefix, *cpp);
- flag_parameter(STR(candidate),
+ "%s_%s", prefix, *cpp);
+ flag_parameter(STR(candidate),
PCF_PARAM_FLAG_DBMS | PCF_PARAM_FLAG_USER,
- local_scope);
+ local_scope);
+ }
}
break;
}
buffer = vstring_alloc(100);
bufp = pcf_expand_parameter_value(buffer, PCF_SHOW_EVAL, param_value,
local_scope);
- pcf_register_dbms_helper(bufp, flag_parameter, local_scope);
+ pcf_register_dbms_helper(bufp, flag_parameter, local_scope, PCF_DBMS_RECURSE);
}
#endif
--- /dev/null
+./postconf: warning: main.cf: #comment after other text is not allowed: #aaa1 ...
+./postconf: warning: main.cf: #comment after other text is not allowed: #aaa2 ...
+./postconf: warning: main.cf: #comment after other text is not allowed: #ccc2 ...
+./postconf: warning: main.cf: #comment after other text is not allowed: #bbb2 ...
+config_directory = .
+smtpd_client_restrictions = inline:{ { aaa0 = #aaa1 } #aaa2 }
+smtpd_helo_restrictions = pcre:{ { /bbb0 #bbb1/ } #bbb2 }
+smtpd_sender_restrictions = regexp:{ { /ccc0 #ccc1/ } #ccc2 }
myrand.o: myrand.h
myrand.o: sys_defs.h
mystrtok.o: check_arg.h
+mystrtok.o: msg.h
mystrtok.o: mystrtok.c
mystrtok.o: stringops.h
mystrtok.o: sys_defs.h
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
+/*
+/* Wietse Venema
/*--*/
/* System library. */
* If there is an error, implement graceful degradation by inserting a
* pseudo table whose lookups fail with a warning message.
*/
- while ((start = mystrtokq(&bp, delim, CHARS_BRACE)) != 0) {
- if (*start == '#') {
- msg_warn("%s: comment at end of line is not supported: %s %s",
- match_list->pname, start, bp);
- break;
- }
+ while ((start = mystrtokq_cw(&bp, delim, CHARS_BRACE,
+ match_list->pname)) != 0) {
for (match = init_match, item = start; *item == '!'; item++)
match = !match;
if (*item == 0)
/* char *mystrtokdq(bufp, delimiters)
/* char **bufp;
/* const char *delimiters;
+/*
+/* char *mystrtok_cw(bufp, delimiters, blame)
+/* char **bufp;
+/* const char *delimiters;
+/* const char *blame;
+/*
+/* char *mystrtokq_cw(bufp, delimiters, parens, blame)
+/* char **bufp;
+/* const char *delimiters;
+/* const char *parens;
+/* const char *blame;
+/*
+/* char *mystrtokdq_cw(bufp, delimiters, blame)
+/* char **bufp;
+/* const char *delimiters;
+/* const char *blame;
/* DESCRIPTION
/* mystrtok() splits a buffer on the specified \fIdelimiters\fR.
/* Tokens are delimited by runs of delimiters, so this routine
/*
/* The result value is the next token, or a null pointer when the
/* end of the buffer was reached.
+/*
+/* mystrtok_cw(), mystrtokq_cw(), and mystrtokdq_cw, log a
+/* warning and return null when the result would look like
+/* comment. The \fBblame\fR argument provides context for
+/* warning messages. Specify a null pointer to disable the
+/* comment check.
/* LICENSE
/* .ad
/* .fi
/* Google, Inc.
/* 111 8th Avenue
/* New York, NY 10011, USA
+/*
+/* Wietse Venema
/*--*/
/* System library. */
-#include "sys_defs.h"
+#include <sys_defs.h>
#include <string.h>
/* Utility library. */
-#include "stringops.h"
+#include <msg.h>
+#include <stringops.h>
-/* mystrtok - safe tokenizer */
+/* mystrtok_warn - warn for #comment after other text */
+
+static void mystrtok_warn(const char *start, const char *bufp, const char *blame)
+{
+ msg_warn("%s: #comment after other text is not allowed: %s %.20s...",
+ blame, start, bufp);
+}
+
+/* mystrtok - ABI compatibility wrapper */
+
+#undef mystrtok
char *mystrtok(char **src, const char *sep)
+{
+ return (mystrtok_cw(src, sep, (char *) 0));
+}
+
+/* mystrtok - safe tokenizer */
+
+char *mystrtok_cw(char **src, const char *sep, const char *blame)
{
char *start = *src;
char *end;
if (*end != 0)
*end++ = 0;
*src = end;
- return (start);
+
+ if (blame && *start == '#') {
+ mystrtok_warn(start, *src, blame);
+ return (0);
+ } else {
+ return (start);
+ }
}
-/* mystrtokq - safe tokenizer with quoting support */
+/* mystrtokq - ABI compatibility wrapper */
+
+#undef mystrtokq
char *mystrtokq(char **src, const char *sep, const char *parens)
+{
+ return (mystrtokq_cw(src, sep, parens, (char *) 0));
+}
+
+/* mystrtokq_cw - safe tokenizer with quoting support */
+
+char *mystrtokq_cw(char **src, const char *sep, const char *parens,
+ const char *blame)
{
char *start = *src;
static char *cp;
}
}
*src = cp;
- return (start);
+
+ if (blame && *start == '#') {
+ mystrtok_warn(start, *src, blame);
+ return (0);
+ } else {
+ return (start);
+ }
}
-/* mystrtokdq - safe tokenizer, double quote and backslash support */
+/* mystrtokdq - ABI compatibility wrapper */
+
+#undef mystrtokdq
char *mystrtokdq(char **src, const char *sep)
+{
+ return (mystrtokdq_cw(src, sep, (char *) 0));
+}
+
+/* mystrtokdq_cw - safe tokenizer, double quote and backslash support */
+
+char *mystrtokdq_cw(char **src, const char *sep, const char *blame)
{
char *cp = *src;
char *start;
}
}
*src = cp;
- return (start);
+
+ if (blame && start && *start == '#') {
+ mystrtok_warn(start, *src, blame);
+ return (0);
+ } else {
+ return (start);
+ }
}
#ifdef TEST
{"mystrtokdq", " foo\\ bar ", {"foo\\ bar"}},
{"mystrtokdq", " foo \\\" bar", {"foo", "\\\"", "bar"}},
{"mystrtokdq", " foo \" bar baz\" ", {"foo", "\" bar baz\""}},
+ {"mystrtok_cw", "#after text"},
+ {"mystrtok_cw", "before-text #after text", {"before-text"}},
+ {"mystrtokq_cw", "#after text"},
+ {"mystrtokq_cw", "{ before text } #after text", "{ before text }"},
+ {"mystrtokdq_cw", "#after text"},
+ {"mystrtokdq_cw", "\"before text\" #after text", {"\"before text\""}},
};
int main(void)
actual = mystrtokq(&cp, CHARS_SPACE, CHARS_BRACE);
} else if (strcmp(tp->action, "mystrtokdq") == 0) {
actual = mystrtokdq(&cp, CHARS_SPACE);
+ } else if (strcmp(tp->action, "mystrtok_cw") == 0) {
+ actual = mystrtok_cw(&cp, CHARS_SPACE, "test");
+ } else if (strcmp(tp->action, "mystrtokq_cw") == 0) {
+ actual = mystrtokq_cw(&cp, CHARS_SPACE, CHARS_BRACE, "test");
+ } else if (strcmp(tp->action, "mystrtokdq_cw") == 0) {
+ actual = mystrtokdq_cw(&cp, CHARS_SPACE, "test");
} else {
msg_panic("invalid command: %s", tp->action);
}
unknown: PASS test 13
unknown: RUN test case 14 mystrtokdq > foo " bar baz" <
unknown: PASS test 14
+unknown: RUN test case 15 mystrtok_cw >#after text<
+unknown: warning: test: #comment after other text is not allowed: #after text...
+unknown: PASS test 15
+unknown: RUN test case 16 mystrtok_cw >before-text #after text<
+unknown: warning: test: #comment after other text is not allowed: #after text...
+unknown: PASS test 16
+unknown: RUN test case 17 mystrtokq_cw >#after text<
+unknown: warning: test: #comment after other text is not allowed: #after text...
+unknown: PASS test 17
+unknown: RUN test case 18 mystrtokq_cw >{ before text } #after text<
+unknown: warning: test: #comment after other text is not allowed: #after text...
+unknown: PASS test 18
+unknown: RUN test case 19 mystrtokdq_cw >#after text<
+unknown: warning: test: #comment after other text is not allowed: #after text...
+unknown: PASS test 19
+unknown: RUN test case 20 mystrtokdq_cw >"before text" #after text<
+unknown: warning: test: #comment after other text is not allowed: #after text...
+unknown: PASS test 20
extern char *mystrtok(char **, const char *);
extern char *mystrtokq(char **, const char *, const char *);
extern char *mystrtokdq(char **, const char *);
+extern char *mystrtok_cw(char **, const char *, const char *);
+extern char *mystrtokq_cw(char **, const char *, const char *, const char *);
+extern char *mystrtokdq_cw(char **, const char *, const char *);
extern char *translit(char *, const char *, const char *);
+#define mystrtok(cp, sp) mystrtok_cw((cp), (sp), (char *) 0)
+#define mystrtokq(cp, sp, pp) mystrtokq_cw((cp), (sp), (pp), (char *) 0)
+#define mystrtokdq(cp, sp) mystrtokdq_cw((cp), (sp), (char *) 0)
+
#define printable(string, replacement) \
printable_except((string), (replacement), (char *) 0)
/* Google, Inc.
/* 111 8th Avenue
/* New York, NY 10011, USA
+/*
+/* Wietse Venema
/*--*/
#endif