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.
* 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
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
For details on how to run the TLSRPT collection and reporting infrastructure,
see the documentation at https://github.com/sys4/tlsrpt-reporter.
+C\bCo\bon\bnn\bne\bec\bct\bti\bio\bon\bn r\bre\beu\bus\bse\be v\bve\ber\brs\bsu\bus\bs s\bse\bes\bss\bsi\bio\bon\bn r\bre\bes\bsu\bum\bmp\bpt\bti\bio\bon\bn
+
+The Postfix SMTP client implements two kinds of reuse:
+
+ * S\bSM\bMT\bTP\bP C\bCo\bon\bnn\bne\bec\bct\bti\bio\bon\bn r\bre\beu\bus\bse\be:\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.
+
+ * T\bTL\bLS\bS S\bSe\bes\bss\bsi\bio\bon\bn r\bre\bes\bsu\bum\bmp\bpt\bti\bio\bon\bn:\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.
+
+Of course there is a third case:
+
+ * C\bCo\bom\bmb\bbi\bin\bne\bed\bd r\bre\beu\bus\bse\be a\ban\bnd\bd r\bre\bes\bsu\bum\bmp\bpt\bti\bio\bon\bn:\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.
+
+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.
+
T\bTL\bLS\bSR\bRP\bPT\bT S\bSt\bta\bat\btu\bus\bs l\blo\bog\bgg\bgi\bin\bng\bg
With TLSRPT support turned on, the Postfix TLSRPT client will not only report
<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>
<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>
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
<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>
<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>
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
Oemer
Kozmenko
Oleksandr
+Bataille
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
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)
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
$(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)
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
/* 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
/* 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>
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)
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 =
extern DICT *dict_sqlite_open(const char *, int, int);
-
/* AUTHOR(S)
/* Axel Steiner
/* ast@treibsand.com
--- /dev/null
+/*++
+/* 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);
+}
* 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
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)
/*
* 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 */
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 */
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 */
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 */
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 */
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 */
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