mirror of
https://github.com/vdukhovni/postfix
synced 2025-08-22 01:49:47 +00:00
postfix-3.9-20231024
This commit is contained in:
parent
57d8e8fa35
commit
d31cde7580
@ -27491,3 +27491,13 @@ Apologies for any names omitted.
|
||||
src/util/dict_utf8.c, src/util/midna_domain.c,
|
||||
src/util/printable.c, src/util/stringops.h,
|
||||
src/util/valid_utf8_string.c.
|
||||
|
||||
Cleanup: added unit tests to the readlline module, with
|
||||
multiline input that contains embedded comments, input that
|
||||
contains a null byte, text not ending in newline. File:
|
||||
readlline.c.
|
||||
|
||||
20231024
|
||||
|
||||
Cleanup: emit place holder text when no SASL authentication
|
||||
failure reason is available. File: smtpd/smtpd_sasl_glue.c.
|
||||
|
@ -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 "20231012"
|
||||
#define MAIL_RELEASE_DATE "20231024"
|
||||
#define MAIL_VERSION_NUMBER "3.9"
|
||||
|
||||
#ifdef SNAPSHOT
|
||||
|
@ -930,7 +930,9 @@ static void psc_smtpd_read_event(int event, void *context)
|
||||
}
|
||||
|
||||
/*
|
||||
* Avoid complaints from Postfix maps about malformed content.
|
||||
* Avoid complaints from Postfix maps about malformed content. Note:
|
||||
* this will stop at the first null byte, just like the code that
|
||||
* parses the command name or command arguments.
|
||||
*/
|
||||
#define PSC_BAD_UTF8(str) \
|
||||
(var_smtputf8_enable && !valid_utf8_stringz(str))
|
||||
|
@ -346,8 +346,8 @@ int smtpd_sasl_authenticate(SMTPD_STATE *state,
|
||||
if (status != XSASL_AUTH_DONE) {
|
||||
sasl_username = xsasl_server_get_username(state->sasl_server);
|
||||
msg_warn("%s: SASL %s authentication failed: %s, sasl_username=%s",
|
||||
state->namaddr, sasl_method,
|
||||
STR(state->sasl_reply),
|
||||
state->namaddr, sasl_method, *STR(state->sasl_reply) ?
|
||||
STR(state->sasl_reply) : "(reason unavailable)",
|
||||
sasl_username ? sasl_username : "(unavailable)");
|
||||
/* RFC 4954 Section 6. */
|
||||
if (status == XSASL_AUTH_TEMP)
|
||||
|
@ -145,7 +145,7 @@ TESTPROG= dict_open dup2_pass_on_exec events exec_command fifo_open \
|
||||
vstream timecmp dict_cache midna_domain casefold strcasecmp_utf8 \
|
||||
vbuf_print split_qnameval vstream msg_logger byte_mask \
|
||||
known_tcp_ports dict_stream find_inet binhash hash_fnv argv \
|
||||
clean_env inet_prefix_top printable
|
||||
clean_env inet_prefix_top printable readlline
|
||||
PLUGIN_MAP_SO = $(LIB_PREFIX)pcre$(LIB_SUFFIX) $(LIB_PREFIX)lmdb$(LIB_SUFFIX) \
|
||||
$(LIB_PREFIX)cdb$(LIB_SUFFIX) $(LIB_PREFIX)sdbm$(LIB_SUFFIX)
|
||||
HTABLE_FIX = NORANDOMIZE=1
|
||||
@ -370,6 +370,11 @@ printable: $(LIB)
|
||||
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
|
||||
mv junk $@.o
|
||||
|
||||
readlline: $(LIB)
|
||||
mv $@.o junk
|
||||
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
|
||||
mv junk $@.o
|
||||
|
||||
hex_quote: $(LIB)
|
||||
mv $@.o junk
|
||||
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(SYSLIBS)
|
||||
@ -624,7 +629,7 @@ tests: all valid_hostname_test mac_expand_test dict_test unescape_test \
|
||||
miss_endif_regexp_test split_qnameval_test vstring_test \
|
||||
vstream_test byte_mask_tests mystrtok_test known_tcp_ports_test \
|
||||
binhash_test argv_test inet_prefix_top_test printable_test \
|
||||
valid_utf8_string_test
|
||||
valid_utf8_string_test readlline_test
|
||||
|
||||
dict_tests: all dict_test \
|
||||
dict_pcre_tests dict_cidr_test dict_thash_test dict_static_test \
|
||||
@ -659,6 +664,9 @@ unescape_test: unescape unescape.in unescape.ref
|
||||
printable_test: printable
|
||||
$(SHLIB_ENV) ${VALGRIND} ./printable
|
||||
|
||||
readlline_test: readlline
|
||||
$(SHLIB_ENV) ${VALGRIND} ./readlline
|
||||
|
||||
valid_utf8_string_test: valid_utf8_string
|
||||
$(SHLIB_ENV) ${VALGRIND} ./valid_utf8_string
|
||||
|
||||
|
@ -190,6 +190,7 @@ int main(int argc, char **argv)
|
||||
for (pass = fail = 0, tp = testcases; tp < testcases + NUM_TESTS; tp++) {
|
||||
char *input;
|
||||
char *actual;
|
||||
int ok = 0;
|
||||
|
||||
/*
|
||||
* Notes:
|
||||
@ -206,13 +207,17 @@ int main(int argc, char **argv)
|
||||
if (strcmp(actual, tp->expected) != 0) {
|
||||
vstream_fprintf(VSTREAM_ERR, "input: >%s<, got: >%s<, want: >%s<\n",
|
||||
tp->input, actual, tp->expected);
|
||||
vstream_fprintf(VSTREAM_ERR, "FAIL %s\n", tp->name);
|
||||
fail++;
|
||||
} else {
|
||||
vstream_fprintf(VSTREAM_ERR, "input: >%s<, got and want: >%s<\n",
|
||||
tp->input, actual);
|
||||
ok = 1;
|
||||
}
|
||||
if (ok) {
|
||||
vstream_fprintf(VSTREAM_ERR, "PASS %s\n", tp->name);
|
||||
pass++;
|
||||
} else {
|
||||
vstream_fprintf(VSTREAM_ERR, "FAIL %s\n", tp->name);
|
||||
fail++;
|
||||
}
|
||||
myfree(input);
|
||||
}
|
||||
|
@ -85,9 +85,15 @@ VSTRING *readllines(VSTRING *buf, VSTREAM *fp, int *lineno, int *first_line)
|
||||
int next;
|
||||
ssize_t start;
|
||||
char *cp;
|
||||
int my_lineno = 0, my_first_line, got_null = 0;
|
||||
|
||||
VSTRING_RESET(buf);
|
||||
|
||||
if (lineno == 0)
|
||||
lineno = &my_lineno;
|
||||
if (first_line == 0)
|
||||
first_line = &my_first_line;
|
||||
|
||||
/*
|
||||
* Ignore comment lines, all whitespace lines, and empty lines. Terminate
|
||||
* at EOF or at the beginning of the next logical line.
|
||||
@ -95,16 +101,19 @@ VSTRING *readllines(VSTRING *buf, VSTREAM *fp, int *lineno, int *first_line)
|
||||
for (;;) {
|
||||
/* Read one line, possibly not newline terminated. */
|
||||
start = LEN(buf);
|
||||
while ((ch = VSTREAM_GETC(fp)) != VSTREAM_EOF && ch != '\n')
|
||||
while ((ch = VSTREAM_GETC(fp)) != VSTREAM_EOF && ch != '\n') {
|
||||
VSTRING_ADDCH(buf, ch);
|
||||
if (lineno != 0 && (ch == '\n' || LEN(buf) > start))
|
||||
if (ch == 0)
|
||||
got_null = 1;
|
||||
}
|
||||
if (ch == '\n' || LEN(buf) > start)
|
||||
*lineno += 1;
|
||||
/* Ignore comment line, all whitespace line, or empty line. */
|
||||
for (cp = STR(buf) + start; cp < END(buf) && ISSPACE(*cp); cp++)
|
||||
/* void */ ;
|
||||
if (cp == END(buf) || *cp == '#')
|
||||
vstring_truncate(buf, start);
|
||||
else if (start == 0 && lineno != 0 && first_line != 0)
|
||||
if (start == 0)
|
||||
*first_line = *lineno;
|
||||
/* Terminate at EOF or at the beginning of the next logical line. */
|
||||
if (ch == VSTREAM_EOF)
|
||||
@ -118,6 +127,20 @@ VSTRING *readllines(VSTRING *buf, VSTREAM *fp, int *lineno, int *first_line)
|
||||
}
|
||||
VSTRING_TERMINATE(buf);
|
||||
|
||||
/*
|
||||
* This code does not care about embedded null bytes, but callers do.
|
||||
*/
|
||||
if (got_null) {
|
||||
const char *why = "text after null byte may be ignored";
|
||||
|
||||
if (*first_line == *lineno)
|
||||
msg_warn("%s, line %d: %s",
|
||||
VSTREAM_PATH(fp), *lineno, why);
|
||||
else
|
||||
msg_warn("%s, line %d-%d: %s",
|
||||
VSTREAM_PATH(fp), *first_line, *lineno, why);
|
||||
}
|
||||
|
||||
/*
|
||||
* Invalid input: continuing text without preceding text. Allowing this
|
||||
* would complicate "postconf -e", which implements its own multi-line
|
||||
@ -136,3 +159,205 @@ VSTRING *readllines(VSTRING *buf, VSTREAM *fp, int *lineno, int *first_line)
|
||||
*/
|
||||
return (LEN(buf) > 0 ? buf : 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Stand-alone test program.
|
||||
*/
|
||||
#ifdef TEST
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <msg.h>
|
||||
#include <msg_vstream.h>
|
||||
#include <stringops.h>
|
||||
#include <vstream.h>
|
||||
#include <vstring.h>
|
||||
|
||||
/*
|
||||
* Test cases. Note: the input and exp_output fields are converted with
|
||||
* unescape(). Embedded null bytes must be specified as \\0.
|
||||
*/
|
||||
struct testcase {
|
||||
const char *name;
|
||||
const char *input;
|
||||
const char *exp_output;
|
||||
int exp_first_line;
|
||||
int exp_last_line;
|
||||
};
|
||||
|
||||
static const struct testcase testcases[] = {
|
||||
{"leading space before non-comment",
|
||||
" abcde\nfghij\n",
|
||||
"fghij",
|
||||
2, 2
|
||||
/* Expect "logical line must not start with whitespace" */
|
||||
},
|
||||
{"leading space before leading comment",
|
||||
" #abcde\nfghij\n",
|
||||
"fghij",
|
||||
2, 2
|
||||
},
|
||||
{"leading #comment at beginning of line",
|
||||
"#abc\ndef",
|
||||
"def",
|
||||
2, 2,
|
||||
},
|
||||
{"empty line before non-comment",
|
||||
"\nabc\n",
|
||||
"abc",
|
||||
2, 2,
|
||||
},
|
||||
{"whitespace line before non-comment",
|
||||
" \nabc\n",
|
||||
"abc",
|
||||
2, 2,
|
||||
},
|
||||
{"missing newline at end of non-comment",
|
||||
"abc def",
|
||||
"abc def",
|
||||
1, 1,
|
||||
},
|
||||
{"missing newline at end of comment",
|
||||
"#abc def",
|
||||
"",
|
||||
1, 1,
|
||||
},
|
||||
{"embedded null, single-line",
|
||||
"abc\\0def",
|
||||
"abc\\0def",
|
||||
1, 1,
|
||||
/* Expect "line 1: text after null byte may be ignored" */
|
||||
},
|
||||
{"embedded null, multiline",
|
||||
"abc\\0\n def",
|
||||
"abc\\0 def",
|
||||
1, 2,
|
||||
/* Expect "line 1-2: text after null byte may be ignored" */
|
||||
},
|
||||
{"embedded null in comment",
|
||||
"#abc\\0\ndef",
|
||||
"def",
|
||||
2, 2,
|
||||
/* Expect "line 2: text after null byte may be ignored" */
|
||||
},
|
||||
{"multiline input",
|
||||
"abc\n def\n",
|
||||
"abc def",
|
||||
1, 2,
|
||||
},
|
||||
{"multiline input with embedded #comment after space",
|
||||
"abc\n #def\n ghi",
|
||||
"abc ghi",
|
||||
1, 3,
|
||||
},
|
||||
{"multiline input with embedded #comment flush left",
|
||||
"abc\n#def\n ghi",
|
||||
"abc ghi",
|
||||
1, 3,
|
||||
},
|
||||
{"multiline input with embedded whitespace line",
|
||||
"abc\n \n ghi",
|
||||
"abc ghi",
|
||||
1, 3,
|
||||
},
|
||||
{"multiline input with embedded empty line",
|
||||
"abc\n\n ghi",
|
||||
"abc ghi",
|
||||
1, 3,
|
||||
},
|
||||
{"multiline input with embedded #comment after space",
|
||||
"abc\n #def\n",
|
||||
"abc",
|
||||
1, 2,
|
||||
},
|
||||
{"multiline input with embedded #comment flush left",
|
||||
"abc\n#def\n",
|
||||
"abc",
|
||||
1, 2,
|
||||
},
|
||||
{"empty line at end of file",
|
||||
"\n",
|
||||
"",
|
||||
1, 1,
|
||||
},
|
||||
{"whitespace line at end of file",
|
||||
"\n \n",
|
||||
"",
|
||||
2, 2,
|
||||
},
|
||||
{"whitespace at end of file",
|
||||
"abc\n ",
|
||||
"abc",
|
||||
1, 2,
|
||||
},
|
||||
};
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
const struct testcase *tp;
|
||||
VSTRING *inp_buf = vstring_alloc(100);
|
||||
VSTRING *exp_buf = vstring_alloc(100);
|
||||
VSTRING *out_buf = vstring_alloc(100);
|
||||
VSTRING *esc_buf = vstring_alloc(100);
|
||||
VSTREAM *fp;
|
||||
int last_line;
|
||||
int first_line;
|
||||
int pass;
|
||||
int fail;
|
||||
|
||||
#define NUM_TESTS sizeof(testcases)/sizeof(testcases[0])
|
||||
|
||||
msg_vstream_init(basename(argv[0]), VSTREAM_ERR);
|
||||
util_utf8_enable = 1;
|
||||
|
||||
for (pass = fail = 0, tp = testcases; tp < testcases + NUM_TESTS; tp++) {
|
||||
int ok = 0;
|
||||
|
||||
vstream_fprintf(VSTREAM_ERR, "RUN %s\n", tp->name);
|
||||
unescape(inp_buf, tp->input);
|
||||
unescape(exp_buf, tp->exp_output);
|
||||
if ((fp = vstream_memopen(inp_buf, O_RDONLY)) == 0)
|
||||
msg_panic("open memory stream for reading: %m");
|
||||
vstream_control(fp, CA_VSTREAM_CTL_PATH("memory buffer"),
|
||||
CA_VSTREAM_CTL_END);
|
||||
last_line = 0;
|
||||
if (readllines(out_buf, fp, &last_line, &first_line) == 0) {
|
||||
VSTRING_RESET(out_buf);
|
||||
VSTRING_TERMINATE(out_buf);
|
||||
}
|
||||
if (LEN(out_buf) != LEN(exp_buf)) {
|
||||
msg_warn("unexpected output length, got: %ld, want: %ld",
|
||||
(long) LEN(out_buf), (long) LEN(exp_buf));
|
||||
} else if (memcmp(STR(out_buf), STR(exp_buf), LEN(out_buf)) != 0) {
|
||||
msg_warn("unexpected output: got: >%s<, want: >%s<",
|
||||
STR(escape(esc_buf, STR(out_buf), LEN(out_buf))),
|
||||
tp->exp_output);
|
||||
} else if (first_line != tp->exp_first_line) {
|
||||
msg_warn("unexpected first_line: got: %d, want: %d",
|
||||
first_line, tp->exp_first_line);
|
||||
} else if (last_line != tp->exp_last_line) {
|
||||
msg_warn("unexpected last_line: got: %d, want: %d",
|
||||
last_line, tp->exp_last_line);
|
||||
} else {
|
||||
vstream_fprintf(VSTREAM_ERR, "got and want: >%s<\n",
|
||||
tp->exp_output);
|
||||
ok = 1;
|
||||
}
|
||||
if (ok) {
|
||||
vstream_fprintf(VSTREAM_ERR, "PASS %s\n", tp->name);
|
||||
pass++;
|
||||
} else {
|
||||
vstream_fprintf(VSTREAM_ERR, "FAIL %s\n", tp->name);
|
||||
fail++;
|
||||
}
|
||||
vstream_fclose(fp);
|
||||
}
|
||||
vstring_free(inp_buf);
|
||||
vstring_free(exp_buf);
|
||||
vstring_free(out_buf);
|
||||
vstring_free(esc_buf);
|
||||
|
||||
msg_info("PASS=%d FAIL=%d", pass, fail);
|
||||
return (fail > 0);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -198,6 +198,7 @@ int main(int argc, char **argv)
|
||||
for (pass = fail = 0, tp = testcases; tp < testcases + NUM_TESTS; tp++) {
|
||||
int actual_l;
|
||||
int actual_z;
|
||||
int ok = 0;
|
||||
|
||||
/*
|
||||
* Notes:
|
||||
@ -214,20 +215,22 @@ int main(int argc, char **argv)
|
||||
"input: >%s<, 'actual_l' got: >%s<, want: >%s<\n",
|
||||
tp->input, valid_to_str(actual_l),
|
||||
valid_to_str(tp->expected));
|
||||
vstream_fprintf(VSTREAM_ERR, "FAIL %s\n", tp->name);
|
||||
fail++;
|
||||
} else if (actual_z != tp->expected) {
|
||||
vstream_fprintf(VSTREAM_ERR,
|
||||
"input: >%s<, 'actual_z' got: >%s<, want: >%s<\n",
|
||||
tp->input, valid_to_str(actual_z),
|
||||
valid_to_str(tp->expected));
|
||||
vstream_fprintf(VSTREAM_ERR, "FAIL %s\n", tp->name);
|
||||
fail++;
|
||||
} else {
|
||||
vstream_fprintf(VSTREAM_ERR, "input: >%s<, got and want: >%s<\n",
|
||||
tp->input, valid_to_str(actual_l));
|
||||
ok = 1;
|
||||
}
|
||||
if (ok) {
|
||||
vstream_fprintf(VSTREAM_ERR, "PASS %s\n", tp->name);
|
||||
pass++;
|
||||
} else {
|
||||
vstream_fprintf(VSTREAM_ERR, "FAIL %s\n", tp->name);
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
msg_info("PASS=%d FAIL=%d", pass, fail);
|
||||
|
Loading…
x
Reference in New Issue
Block a user