2
0
mirror of https://github.com/vdukhovni/postfix synced 2025-08-22 01:49:47 +00:00

postfix-3.11-20250323

This commit is contained in:
Wietse Z Venema 2025-03-23 00:00:00 -05:00 committed by Viktor Dukhovni
parent f8a6a51f22
commit ba7dbdd88a
12 changed files with 481 additions and 19 deletions

View File

@ -29015,3 +29015,32 @@ Apologies for any names omitted.
Dovecot auth client did not attempt to create a new connection
after an I/O error on an existing connection. Reported by
Oleksandr Kozmenko. File: xsasl/xsasl_dovecot_server.c.
20240315
Code health: two typos canceled each other's effect. Fix
by Michael Tokarev. No change in compiler output. File:
util/vstring_vstream.c.
20250316
Bugfix (defect introduced: date 19991116): when appending a
setting to a main.cf or master.cf file that did not end in
a newline character, the "postconf -e" command did not add
an extra newline character before appending the new setting,
causing information to become garbled. Fix by Michael
Tokarev. File: postconf/postconf_edit.c.
20259317
Documentation: added text to clarify the difference between
SMTP connection reuse and TLS session resumption, and that
these can be combined together. File: proto/TLSRPT_README.html.
20250321
Safety: the SQLite client now logs a warning when a query
uses double quotes instead of the Postfix-recommended single
quotes. Oscar Bataille reported that the non-recommended
form is not protected against SQL injection. Files:
global/dict_sqlite.c, global/dict_sqlite_test.c.

View File

@ -7,6 +7,7 @@ TTaabbllee ooff CCoonntteennttss
* Introduction
* Building Postfix with TLSRPT support
* Turning on TLSRPT
* Connection reuse versus session resumption
* TLSRPT Status logging
* Delivering TLSRPT summaries via email
* MTA-STS Support via smtp_tls_policy_maps
@ -27,7 +28,7 @@ A policy for domain example.com could look like this:
Instead of mailto:, a policy may specify an https: destination.
The diagram below shows how Postfix TLS handshake success and failure events
The diagram below shows how successful or failed Postfix TLS handshake events
are collected and processed into daily summary reports.
Postfix SMTP and TLSRPT client TLSRPT collector, Email or HTTP
@ -124,6 +125,36 @@ Notes:
For details on how to run the TLSRPT collection and reporting infrastructure,
see the documentation at https://github.com/sys4/tlsrpt-reporter.
CCoonnnneeccttiioonn rreeuussee vveerrssuuss sseessssiioonn rreessuummppttiioonn
The Postfix SMTP client implements two kinds of reuse:
* SSMMTTPP CCoonnnneeccttiioonn rreeuussee:: a Postfix SMTP client creates a new SMTP connection,
sends one email message, and saves the connection instead of closing it.
Later, some SMTP client reuses that connection, sends an email message, and
saves or closes the connection depending on whether it has reached some
reuse limit. Each connection can be used by only one Postfix SMTP client at
a time.
* TTLLSS SSeessssiioonn rreessuummppttiioonn:: a Postfix SMTP client saves the result from a "new"
TLS handshake. Later, one or more SMTP clients create a new SMTP connection
and resume the saved TLS session on their new connection.
Of course there is a third case:
* CCoommbbiinneedd rreeuussee aanndd rreessuummppttiioonn:: a Postfix SMTP client creates a new SMTP
connection, sends one email message, saves the result from a "new" TLS
handshake, and also saves the connection instead of closing it. Later, one
SMTP client reuses (and saves) that connection, one client at a time, and
one or more clients create a new SMTP connection and resume the saved TLS
session on their new connection.
In all cases, there is no TLS handshake when a saved SMTP connection is reused,
and there is no "new" TLS handshake when a saved TLS session is resumed.
As described next, Postfix will by default log and generate only a TLSRPT event
for a "new" TLS handshake.
TTLLSSRRPPTT SSttaattuuss llooggggiinngg
With TLSRPT support turned on, the Postfix TLSRPT client will not only report

View File

@ -25,6 +25,7 @@
<li> <a href="#intro"> Introduction </a> </li>
<li> <a href="#building"> Building Postfix with TLSRPT support </a>
<li> <a href="#using"> Turning on TLSRPT </a> </li>
<li> <a href="#reusing"> Connection reuse versus session resumption </a> </li>
<li> <a href="#logging"> TLSRPT Status logging </a> </li>
<li> <a href="#delivering"> Delivering TLSRPT summaries via email</a> </li>
<li> <a href="#mta-sts"> MTA-STS Support via smtp_tls_policy_maps </a> </li>
@ -52,8 +53,8 @@ _smtp._tls.example.com. IN TXT "v=TLSRPTv1; rua=mailto:smtp-tls-report@example.c
<p> Instead of <tt>mailto:</tt>, a policy may specify an <tt>https:</tt>
destination. </p>
<p> The diagram below shows how Postfix TLS handshake success and
failure events are collected and processed into daily summary
<p> The diagram below shows how successful or failed Postfix TLS
handshake events are collected and processed into daily summary
reports. </p>
<blockquote>
@ -203,6 +204,49 @@ programs should create sockets there. </p>
infrastructure, see the documentation at
<a href="https://github.com/sys4/tlsrpt-reporter">https://github.com/sys4/tlsrpt-reporter</a>.
<h2> <a name="reusing"> Connection reuse versus session resumption
</a> </h2>
<p> The Postfix SMTP client implements two kinds of reuse: </p>
<ul>
<li> <p> <b> SMTP Connection reuse: </b> a Postfix SMTP client
creates a new SMTP connection, sends one email message, and saves
the connection instead of closing it. Later, some SMTP client reuses
that connection, sends an email message, and saves or closes the
connection depending on whether it has reached some reuse limit.
Each connection can be used by only one Postfix SMTP client at a
time. </p>
<li> <p> <b> TLS Session resumption: </b> a Postfix SMTP client
saves the result from a "new" TLS handshake. Later, one or more
SMTP clients create a new SMTP connection and resume the saved TLS
session on their new connection. <p>
</ul>
<p> Of course there is a third case: </p>
<ul>
<li> <p> <b> Combined reuse and resumption: </b> a Postfix SMTP
client creates a new SMTP connection, sends one email message, saves
the result from a "new" TLS handshake, and also saves the connection
instead of closing it. Later, one SMTP client reuses (and saves)
that connection, one client at a time, and one or more clients
create a new SMTP connection and resume the saved TLS session on
their new connection. <p>
</ul>
<p> In all cases, there is no TLS handshake when a saved SMTP connection
is reused, and there is no "new" TLS handshake when a saved TLS session
is resumed. </p>
<p> As described next, Postfix will by default log and generate only a
TLSRPT event for a "new" TLS handshake. </p>
<h2> <a name="logging"> TLSRPT Status logging </a> </h2>
<p> With TLSRPT support turned on, the Postfix TLSRPT client will

View File

@ -25,6 +25,7 @@
<li> <a href="#intro"> Introduction </a> </li>
<li> <a href="#building"> Building Postfix with TLSRPT support </a>
<li> <a href="#using"> Turning on TLSRPT </a> </li>
<li> <a href="#reusing"> Connection reuse versus session resumption </a> </li>
<li> <a href="#logging"> TLSRPT Status logging </a> </li>
<li> <a href="#delivering"> Delivering TLSRPT summaries via email</a> </li>
<li> <a href="#mta-sts"> MTA-STS Support via smtp_tls_policy_maps </a> </li>
@ -52,8 +53,8 @@ _smtp._tls.example.com. IN TXT "v=TLSRPTv1; rua=mailto:smtp-tls-report@example.c
<p> Instead of <tt>mailto:</tt>, a policy may specify an <tt>https:</tt>
destination. </p>
<p> The diagram below shows how Postfix TLS handshake success and
failure events are collected and processed into daily summary
<p> The diagram below shows how successful or failed Postfix TLS
handshake events are collected and processed into daily summary
reports. </p>
<blockquote>
@ -203,6 +204,49 @@ programs should create sockets there. </p>
infrastructure, see the documentation at
https://github.com/sys4/tlsrpt-reporter.
<h2> <a name="reusing"> Connection reuse versus session resumption
</a> </h2>
<p> The Postfix SMTP client implements two kinds of reuse: </p>
<ul>
<li> <p> <b> SMTP Connection reuse: </b> a Postfix SMTP client
creates a new SMTP connection, sends one email message, and saves
the connection instead of closing it. Later, some SMTP client reuses
that connection, sends an email message, and saves or closes the
connection depending on whether it has reached some reuse limit.
Each connection can be used by only one Postfix SMTP client at a
time. </p>
<li> <p> <b> TLS Session resumption: </b> a Postfix SMTP client
saves the result from a "new" TLS handshake. Later, one or more
SMTP clients create a new SMTP connection and resume the saved TLS
session on their new connection. <p>
</ul>
<p> Of course there is a third case: </p>
<ul>
<li> <p> <b> Combined reuse and resumption: </b> a Postfix SMTP
client creates a new SMTP connection, sends one email message, saves
the result from a "new" TLS handshake, and also saves the connection
instead of closing it. Later, one SMTP client reuses (and saves)
that connection, one client at a time, and one or more clients
create a new SMTP connection and resume the saved TLS session on
their new connection. <p>
</ul>
<p> In all cases, there is no TLS handshake when a saved SMTP connection
is reused, and there is no "new" TLS handshake when a saved TLS session
is resumed. </p>
<p> As described next, Postfix will by default log and generate only a
TLSRPT event for a "new" TLS handshake. </p>
<h2> <a name="logging"> TLSRPT Status logging </a> </h2>
<p> With TLSRPT support turned on, the Postfix TLSRPT client will

View File

@ -104,3 +104,4 @@ Gueven
Oemer
Kozmenko
Oleksandr
Bataille

View File

@ -131,7 +131,7 @@ TESTPROG= domain_list dot_lockfile mail_addr_crunch mail_addr_find \
fold_addr smtp_reply_footer mail_addr_map normalize_mailhost_addr \
haproxy_srvr map_search delivered_hdr login_sender_match \
compat_level config_known_tcp_ports hfrom_format rfc2047_code \
ascii_header_text sendopts_test
ascii_header_text sendopts_test dict_sqlite_test
LIBS = ../../lib/lib$(LIB_PREFIX)util$(LIB_SUFFIX)
LIB_DIR = ../../lib
@ -408,6 +408,10 @@ ascii_header_text: ascii_header_text.c $(LIB) $(LIBS)
sendopts_test: sendopts_test.c $(LIB) $(LIBS)
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS)
dict_sqlite_test: dict_sqlite_test.c dict_sqlite.o $(LIB) $(LIBS)
$(CC) $(CFLAGS) -DTEST -o $@ $@.c dict_sqlite.o $(LIB) $(LIBS) \
$(SYSLIBS) $(AUXLIBS_SQLITE)
config_known_tcp_ports: config_known_tcp_ports.c $(LIB) $(LIBS)
$(CC) $(CFLAGS) -DTEST -o $@ $@.c $(LIB) $(LIBS) $(SYSLIBS)
@ -421,7 +425,7 @@ tests: tok822_test mime_tests strip_addr_test tok822_limit_test \
normalize_mailhost_addr_test haproxy_srvr_test map_search_test \
delivered_hdr_test login_sender_match_test compat_level_test \
config_known_tcp_ports_test hfrom_format_test rfc2047_code_test \
ascii_header_text_test test_sendopts
ascii_header_text_test test_sendopts test_dict_sqlite
mime_tests: mime_test mime_nest mime_8bit mime_dom mime_trunc mime_cvt \
mime_cvt2 mime_cvt3 mime_garb1 mime_garb2 mime_garb3 mime_garb4
@ -794,7 +798,10 @@ ascii_header_text_test: update ascii_header_text
$(SHLIB_ENV) $(VALGRIND) ./ascii_header_text
test_sendopts: update sendopts_test
-$(SHLIB_ENV) $(VALGRIND) ./sendopts_test
$(SHLIB_ENV) $(VALGRIND) ./sendopts_test
test_dict_sqlite: update dict_sqlite_test
$(SHLIB_ENV) $(VALGRIND) ./dict_sqlite_test
clean:
rm -f *.o $(LIB) *core $(TESTPROG) junk $(MAPS)
@ -1331,6 +1338,19 @@ dict_sqlite.o: db_common.h
dict_sqlite.o: dict_sqlite.c
dict_sqlite.o: dict_sqlite.h
dict_sqlite.o: string_list.h
dict_sqlite_test.o: ../../include/argv.h
dict_sqlite_test.o: ../../include/check_arg.h
dict_sqlite_test.o: ../../include/dict.h
dict_sqlite_test.o: ../../include/msg.h
dict_sqlite_test.o: ../../include/msg_vstream.h
dict_sqlite_test.o: ../../include/myflock.h
dict_sqlite_test.o: ../../include/stringops.h
dict_sqlite_test.o: ../../include/sys_defs.h
dict_sqlite_test.o: ../../include/vbuf.h
dict_sqlite_test.o: ../../include/vstream.h
dict_sqlite_test.o: ../../include/vstring.h
dict_sqlite_test.o: dict_sqlite.h
dict_sqlite_test.o: dict_sqlite_test.c
domain_list.o: ../../include/argv.h
domain_list.o: ../../include/check_arg.h
domain_list.o: ../../include/match_list.h

View File

@ -34,9 +34,13 @@
/* Must be O_RDONLY.
/* .IP dict_flags
/* See dict_open(3).
/* DIAGNOSTICS
/* dict_sqlite_open() logs a warning when the query parameter value
/* does not use the recommended '' quotes to protect against SQL
/* injection (bad examples; no quotes or "" quotes).
/* SEE ALSO
/* dict(3) generic dictionary manager
/* sqlite_table(5) sqlite client configuration
/* sqlite_table(5) Postfix sqlite client configuration
/* AUTHOR(S)
/* Axel Steiner
/* ast@treibsand.com
@ -46,12 +50,16 @@
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
/*
/* Wietse Venema
/* porcupine.org
/*--*/
/* System library. */
#include <sys_defs.h>
#include <string.h>
#include <ctype.h>
#ifdef HAS_SQLITE
#include <sqlite3.h>
@ -250,6 +258,38 @@ static const char *dict_sqlite_lookup(DICT *dict, const char *name)
retval : 0);
}
/* flag_non_recommended_query - as the name says. */
static void flag_non_recommended_query(const char *query,
const char *sqlitecf)
{
const char *cp;
int in_quote;
const int squote = '\'';
const int dquote = '"';
for (in_quote = 0, cp = query; *cp != 0; cp++) {
if (in_quote == 0) {
if (*cp == squote || *cp == dquote)
in_quote = *cp;
} else if (*cp == in_quote) {
in_quote = 0;
}
if (in_quote == squote)
continue;
if (*cp == '%') {
if (cp[1] == '%') {
cp += 1;
} else if (ISALNUM(cp[1])) {
msg_warn("%s:%s: query >%s< contains >%.2s< without the "
"recommended '' quotes", DICT_TYPE_SQLITE, sqlitecf,
query, cp);
cp += 1;
}
}
}
}
/* sqlite_parse_config - parse sqlite configuration file */
static void sqlite_parse_config(DICT_SQLITE *dict_sqlite, const char *sqlitecf)
@ -268,6 +308,8 @@ static void sqlite_parse_config(DICT_SQLITE *dict_sqlite, const char *sqlitecf)
db_common_sql_build_query(buf, dict_sqlite->parser);
dict_sqlite->query = vstring_export(buf);
}
/* Flag %[a-zA-Z0-9] if not protected with ''. */
flag_non_recommended_query(dict_sqlite->query, sqlitecf);
dict_sqlite->result_format =
cfg_get_str(dict_sqlite->parser, "result_format", "%s", 1, 0);
dict_sqlite->expansion_limit =

View File

@ -23,7 +23,6 @@
extern DICT *dict_sqlite_open(const char *, int, int);
/* AUTHOR(S)
/* Axel Steiner
/* ast@treibsand.com

View File

@ -0,0 +1,247 @@
/*++
/* NAME
/* dict_sqlite_test 1t
/* SUMMARY
/* dict_sqlite unit test
/* SYNOPSIS
/* ./dict_sqlite_test
/* DESCRIPTION
/* dict_sqlite_test runs and logs each configured test, reports if
/* a test is a PASS or FAIL, and returns an exit status of zero if
/* all tests are a PASS.
/*
/* Each test creates a temporary test database and a corresponding
/* Postfix sqlite client configuration file, both having unique
/* names. Otherwise, each test is hermetic.
/* LICENSE
/* .ad
/* .fi
/* The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/* Wietse Venema porcupine.org
/*--*/
/*
/*
* System library.
*/
#include <sys_defs.h>
#include <stdlib.h>
#include <string.h>
/*
* Utility library.
*/
#include <msg.h>
#include <msg_vstream.h>
#include <stringops.h>
/*
* Global library.
*/
#include <dict_sqlite.h>
/*
* TODO(wietse) make this a proper VSTREAM interface or test helper API.
*/
/* vstream_swap - capture output for testing */
static void vstream_swap(VSTREAM *one, VSTREAM *two)
{
VSTREAM save;
save = *one;
*one = *two;
*two = save;
}
/*
* Override the printable.c module because it may break some tests.
*
* TODO(wietse) move this to a fake_printable.c module that can override all
* printable.c global symbols.
*/
int util_utf8_enable;
char *printable_except(char *string, int replacement, const char *except)
{
return (string);
}
/*
* Scaffolding for dict_sqlite(3) tests.
*/
/* create_and_populate_db - create an empty database and optionally populate */
static void create_and_populate_db(char *dbpath, const char *commands)
{
int fd;
/*
* Create an empty database file with a unique name. Assume that an
* adversary cannot rename or remove the file.
*/
if ((fd = mkstemp(dbpath)) < 0)
msg_fatal("mkstemp(\"%s\"): %m", dbpath);
if (close(fd) < 0)
msg_fatal("close %s: %m", dbpath);
/*
* TODO(wietse) Open the database file, prepare and execute commands
* to populate the database, and close the database.
*/
if (commands) {
msg_fatal("commands are not yet supported");
}
}
/* create_and_populate_cf - create sqlite_table(5) configuration file */
static void create_and_populate_cf(char *cfpath, const char *dbpath,
const char *cftext)
{
int fd;
VSTREAM *fp;
/*
* Create an empty sqlite_table(5) configuration file with a unique name.
* Assume that an adversary cannot rename or remove the file.
*/
if ((fd = mkstemp(cfpath)) < 0)
msg_fatal("mkstemp(\"%s\"): %m", cfpath);
if ((fp = vstream_fdopen(fd, O_WRONLY)) == 0)
msg_fatal("vstream_fdopen: %m");
(void) vstream_fprintf(fp, "%s\ndbpath = %s\n", cftext, dbpath);
if (vstream_fclose(fp) != 0)
msg_fatal("vstream_fdclose: %m");
}
/*
* Test structure. Some tests may come their own.
*/
typedef struct TEST_CASE {
const char *label;
int (*action) (const struct TEST_CASE *);
const char *commands; /* commands or null */
const char *settings; /* sqlite_table(5) */
const char *exp_warning; /* substring match or null */
} TEST_CASE;
#define PASS (0)
#define FAIL (1)
#define PATH_TEMPLATE "/tmp/test-XXXXXXX"
/* test_flag_non_recommended_query - flag non-recommended query payloads */
static int test_flag_non_recommended_query(const TEST_CASE *tp)
{
static VSTRING *msg_buf;
VSTREAM *memory_stream;
const char template[] = PATH_TEMPLATE;
char dbpath[sizeof(template)];
char cfpath[sizeof(template)];
DICT *dict;
if (msg_buf == 0)
msg_buf = vstring_alloc(100);
/* Prepare scaffolding database and configuration files. */
memcpy(dbpath, template, sizeof(dbpath));
create_and_populate_db(dbpath, tp->commands);
memcpy(cfpath, template, sizeof(cfpath));
create_and_populate_cf(cfpath, dbpath, tp->settings);
/* Run the test with custom STDERR stream. */
VSTRING_RESET(msg_buf);
VSTRING_TERMINATE(msg_buf);
if ((memory_stream = vstream_memopen(msg_buf, O_WRONLY)) == 0)
msg_fatal("open memory stream: %m");
vstream_swap(VSTREAM_ERR, memory_stream);
if ((dict = dict_sqlite_open(cfpath, O_RDONLY, DICT_FLAG_UTF8_REQUEST)) != 0)
dict_close(dict);
vstream_swap(memory_stream, VSTREAM_ERR);
if (vstream_fclose(memory_stream))
msg_fatal("close memory stream: %m");
/* Cleanup scaffolding database and configuration files. */
if (unlink(dbpath) < 0)
msg_fatal("unlink %s: %m", dbpath);
if (unlink(cfpath) < 0)
msg_fatal("unlink %s: %m", cfpath);
/* Verify the results. */
if (tp->exp_warning == 0 && VSTRING_LEN(msg_buf) > 0) {
msg_warn("got warning ``%s'', want ``null''", vstring_str(msg_buf));
return (FAIL);
}
if (tp->exp_warning != 0
&& strstr(vstring_str(msg_buf), tp->exp_warning) == 0) {
msg_warn("got warning ``%s'', want ``%s''",
vstring_str(msg_buf), tp->exp_warning);
return (FAIL);
}
return (PASS);
}
/*
* The list of test cases.
*/
static const TEST_CASE test_cases[] = {
/*
* Tests to flag non-recommended query forms. These create an empty test
* database, and open it with the dict_sqlite client without querying it.
*/
{.label = "no_dynamic_payload",
.action = test_flag_non_recommended_query,
.settings = "query = select a from b where c = 5",
},
{.label = "dynamic_payload_inside_recommended_quotes",
.action = test_flag_non_recommended_query,
.settings = "query = select a from b where c = 'xx%syy'",
},
{.label = "dynamic_payload_without_quotes",
.action = test_flag_non_recommended_query,
.settings = "query = select s from b where c = xx%syy",
.exp_warning = "contains >%s< without the recommended '' quotes",
},
{.label = "payload_inside_double_quotes",
.action = test_flag_non_recommended_query,
.settings = "query = select s from b where c = \"xx%syy\"",
.exp_warning = "contains >%s< without the recommended '' quotes",
},
/*
* TODO: Tests that actually populate a test database, and that query it
* with the dict_sqlite client.
*/
{0},
};
int main(int argc, char **argv)
{
const TEST_CASE *tp;
int pass = 0;
int fail = 0;
msg_vstream_init(sane_basename((VSTRING *) 0, argv[0]), VSTREAM_ERR);
for (tp = test_cases; tp->label != 0; tp++) {
int test_failed;
msg_info("RUN %s", tp->label);
test_failed = tp->action(tp);
if (test_failed) {
msg_info("FAIL %s", tp->label);
fail++;
} else {
msg_info("PASS %s", tp->label);
pass++;
}
}
msg_info("PASS=%d FAIL=%d", pass, fail);
exit(fail != 0);
}

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 "20250304"
#define MAIL_RELEASE_DATE "20250323"
#define MAIL_VERSION_NUMBER "3.11"
#ifdef SNAPSHOT

View File

@ -113,8 +113,13 @@ static char *pcf_find_cf_info(VSTRING *buf, VSTREAM *dst)
static char *pcf_next_cf_line(VSTRING *buf, VSTREAM *src, VSTREAM *dst, int *lineno)
{
char *cp;
int last_char;
while (vstring_get(buf, src) != VSTREAM_EOF) {
while ((last_char = vstring_get(buf, src)) != VSTREAM_EOF) {
if (last_char != '\n') {
VSTRING_ADDCH(buf, '\n');
VSTRING_TERMINATE(buf);
}
if (lineno)
*lineno += 1;
if ((cp = pcf_find_cf_info(buf, dst)) != 0)

View File

@ -123,7 +123,7 @@
/*
* Macro to return the last character added to a VSTRING, for consistency.
*/
#define VSTRING_GET_RESULT(vp, baselen) \
#define VSTRING_GET_RESULT(vp, base_len) \
(VSTRING_LEN(vp) > (base_len) ? vstring_end(vp)[-1] : VSTREAM_EOF)
/* vstring_get_flags - read line from file, keep newline */
@ -142,7 +142,7 @@ int vstring_get_flags(VSTRING *vp, VSTREAM *fp, int flags)
break;
}
VSTRING_TERMINATE(vp);
return (VSTRING_GET_RESULT(vp, baselen));
return (VSTRING_GET_RESULT(vp, base_len));
}
/* vstring_get_flags_nonl - read line from file, strip newline */
@ -158,7 +158,7 @@ int vstring_get_flags_nonl(VSTRING *vp, VSTREAM *fp, int flags)
while ((c = VSTREAM_GETC(fp)) != VSTREAM_EOF && c != '\n')
VSTRING_ADDCH(vp, c);
VSTRING_TERMINATE(vp);
return (c == '\n' ? c : VSTRING_GET_RESULT(vp, baselen));
return (c == '\n' ? c : VSTRING_GET_RESULT(vp, base_len));
}
/* vstring_get_flags_null - read null-terminated string from file */
@ -174,7 +174,7 @@ int vstring_get_flags_null(VSTRING *vp, VSTREAM *fp, int flags)
while ((c = VSTREAM_GETC(fp)) != VSTREAM_EOF && c != 0)
VSTRING_ADDCH(vp, c);
VSTRING_TERMINATE(vp);
return (c == 0 ? c : VSTRING_GET_RESULT(vp, baselen));
return (c == 0 ? c : VSTRING_GET_RESULT(vp, base_len));
}
/* vstring_get_flags_bound - read line from file, keep newline, up to bound */
@ -197,7 +197,7 @@ int vstring_get_flags_bound(VSTRING *vp, VSTREAM *fp, int flags,
break;
}
VSTRING_TERMINATE(vp);
return (VSTRING_GET_RESULT(vp, baselen));
return (VSTRING_GET_RESULT(vp, base_len));
}
/* vstring_get_flags_nonl_bound - read line from file, strip newline, up to bound */
@ -217,7 +217,7 @@ int vstring_get_flags_nonl_bound(VSTRING *vp, VSTREAM *fp, int flags,
while (bound-- > 0 && (c = VSTREAM_GETC(fp)) != VSTREAM_EOF && c != '\n')
VSTRING_ADDCH(vp, c);
VSTRING_TERMINATE(vp);
return (c == '\n' ? c : VSTRING_GET_RESULT(vp, baselen));
return (c == '\n' ? c : VSTRING_GET_RESULT(vp, base_len));
}
/* vstring_get_flags_null_bound - read null-terminated string from file */
@ -237,7 +237,7 @@ int vstring_get_flags_null_bound(VSTRING *vp, VSTREAM *fp, int flags,
while (bound-- > 0 && (c = VSTREAM_GETC(fp)) != VSTREAM_EOF && c != 0)
VSTRING_ADDCH(vp, c);
VSTRING_TERMINATE(vp);
return (c == 0 ? c : VSTRING_GET_RESULT(vp, baselen));
return (c == 0 ? c : VSTRING_GET_RESULT(vp, base_len));
}
#ifdef TEST