From 7f83b348ccf4cd815e9758ab9ca1012e66324e9d Mon Sep 17 00:00:00 2001 From: Jeremy Harris Date: Fri, 14 Aug 2020 13:09:53 +0100 Subject: [PATCH] Fix ${readsocket } eol-replacement. Bug 2630 --- doc/doc-txt/ChangeLog | 4 ++++ src/src/expand.c | 11 +++++++---- src/src/functions.h | 2 +- src/src/lookups/readsock.c | 6 ++++-- src/src/macros.h | 6 ++++-- src/src/readconf.c | 4 ++-- src/src/string.c | 30 +++++++++++++++++----------- test/scripts/0000-Basic/0373 | 28 +++++++++++++++++++++++++- test/stdout/0373 | 38 ++++++++++++++++++++++++++++++++++++ 9 files changed, 106 insertions(+), 23 deletions(-) diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog index d56ff240b..8c4126e89 100644 --- a/doc/doc-txt/ChangeLog +++ b/doc/doc-txt/ChangeLog @@ -100,6 +100,10 @@ JH/20 Bug 2631: ACL dnslist conditions now ignore and log any lookups returns not in 127.0.0.0/8 to help in spotting list domains taken over by a domain-parking registrar. +JH/21 Bug 2630: Fix trace eol-replacement string for the ${readsocket } + expansion. Previously when a whitespace character was specified it + was not inserted after removing the newline. + Exim version 4.94 ----------------- diff --git a/src/src/expand.c b/src/src/expand.c index 7b8462eef..9bb30de4f 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -4916,7 +4916,7 @@ while (*s) { expand_string_message = string_sprintf("lookup of \"%s\" gave DEFER: %s", - string_printing2(key, FALSE), search_error_message); + string_printing2(key, SP_TAB), search_error_message); goto EXPAND_FAILED; } if (expand_setup > 0) expand_nmax = expand_setup; @@ -5330,11 +5330,14 @@ while (*s) while ((item = string_nextinlist(&list, &sep, NULL, 0))) g = string_append_listele(g, ',', item); - /* possibly plus an EOL string */ + /* possibly plus an EOL string. Process with escapes, to protect + from list-processing. The only current user of eol= in search + options is the readsock expansion. */ + if (sub_arg[3] && *sub_arg[3]) g = string_append_listele(g, ',', - string_sprintf("eol=%s", sub_arg[3])); - + string_sprintf("eol=%s", + string_printing2(sub_arg[3], SP_TAB|SP_SPACE))); } /* Gat a (possibly cached) handle for the connection */ diff --git a/src/src/functions.h b/src/src/functions.h index f56ab3eac..e5cf7f140 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -532,7 +532,7 @@ extern int string_is_ip_address(const uschar *, int *); #ifdef SUPPORT_I18N extern BOOL string_is_utf8(const uschar *); #endif -extern const uschar *string_printing2(const uschar *, BOOL); +extern const uschar *string_printing2(const uschar *, int); extern uschar *string_split_message(uschar *); extern uschar *string_unprinting(uschar *); #ifdef SUPPORT_I18N diff --git a/src/src/lookups/readsock.c b/src/src/lookups/readsock.c index c2088b7a5..cfc9b4ad8 100644 --- a/src/src/lookups/readsock.c +++ b/src/src/lookups/readsock.c @@ -186,7 +186,9 @@ FILE * fp; gstring * yield; int ret = DEFER; -DEBUG(D_lookup) debug_printf_indent("readsock: file=\"%s\" key=\"%s\" len=%d opts=\"%s\"\n", filename, keystring, length, opts); +DEBUG(D_lookup) + debug_printf_indent("readsock: file=\"%s\" key=\"%s\" len=%d opts=\"%s\"\n", + filename, keystring, length, opts); /* Parse options */ @@ -200,7 +202,7 @@ if (opts) for (uschar * s; s = string_nextinlist(&opts, &sep, NULL, 0); ) lf.do_tls = TRUE; #endif else if (Ustrncmp(s, "eol=", 4) == 0) - eol = s + 4; + eol = string_unprinting(s + 4); else if (Ustrcmp(s, "cache=yes") == 0) lf.cache = TRUE; else if (Ustrcmp(s, "send=no") == 0) diff --git a/src/src/macros.h b/src/src/macros.h index 4c5279f0a..baac435ec 100644 --- a/src/src/macros.h +++ b/src/src/macros.h @@ -41,9 +41,11 @@ manipulate them. */ /* For almost all calls to convert things to printing characters, we want to -allow tabs. A macro just makes life a bit easier. */ +allow tabs & spaces. A macro just makes life a bit easier. */ -#define string_printing(s) string_printing2((s), TRUE) +#define string_printing(s) string_printing2((s), 0) +#define SP_TAB BIT(0) +#define SP_SPACE BIT(1) /* We need a special return code for "no recipients and failed to send an error diff --git a/src/src/readconf.c b/src/src/readconf.c index e3c9ed72d..dabe86348 100644 --- a/src/src/readconf.c +++ b/src/src/readconf.c @@ -1546,7 +1546,7 @@ if (flags & opt_fn_print) { if (flags & opt_fn_print_label) printf("%s = ", name); printf("%s\n", smtp_receive_timeout_s - ? string_printing2(smtp_receive_timeout_s, FALSE) + ? string_printing2(smtp_receive_timeout_s, SP_TAB) : readconf_printtime(smtp_receive_timeout)); } else if (*str == '$') @@ -2463,7 +2463,7 @@ switch(ol->type & opt_mask) case opt_rewrite: /* Show the text value */ s = *(USS value); if (!no_labels) printf("%s = ", name); - printf("%s\n", s ? string_printing2(s, FALSE) : US""); + printf("%s\n", s ? string_printing2(s, SP_TAB) : US""); break; case opt_int: diff --git a/src/src/string.c b/src/src/string.c index 1b08e7fb8..53ff0a834 100644 --- a/src/src/string.c +++ b/src/src/string.c @@ -281,17 +281,17 @@ return ch; /* This function is called for critical strings. It checks for any non-printing characters, and if any are found, it makes a new copy of the string with suitable escape sequences. It is most often called by the -macro string_printing(), which sets allow_tab TRUE. +macro string_printing(), which sets flags to 0. Arguments: s the input string - allow_tab TRUE to allow tab as a printing character + flags Bit 0: convert tabs. Bit 1: convert spaces. Returns: string with non-printers encoded as printing sequences */ const uschar * -string_printing2(const uschar *s, BOOL allow_tab) +string_printing2(const uschar *s, int flags) { int nonprintcount = 0; int length = 0; @@ -301,7 +301,10 @@ uschar *ss, *tt; while (*t != 0) { int c = *t++; - if (!mac_isprint(c) || (!allow_tab && c == '\t')) nonprintcount++; + if ( !mac_isprint(c) + || flags & SP_TAB && c == '\t' + || flags & SP_SPACE && c == ' ' + ) nonprintcount++; length++; } @@ -310,17 +313,19 @@ if (nonprintcount == 0) return s; /* Get a new block of store guaranteed big enough to hold the expanded string. */ -ss = store_get(length + nonprintcount * 3 + 1, is_tainted(s)); +tt = ss = store_get(length + nonprintcount * 3 + 1, is_tainted(s)); /* Copy everything, escaping non printers. */ -t = s; -tt = ss; - -while (*t != 0) +for (t = s; *t; ) { int c = *t; - if (mac_isprint(c) && (allow_tab || c != '\t')) *tt++ = *t++; else + if ( mac_isprint(c) + && (!(flags & SP_TAB) || c != '\t') + && (!(flags & SP_SPACE) || c != ' ') + ) + *tt++ = *t++; + else { *tt++ = '\\'; switch (*t) @@ -944,7 +949,10 @@ else s = ss; if (!*s || *++s != sep || sep_is_special) break; } - while (g->ptr > 0 && isspace(g->s[g->ptr-1])) g->ptr--; + /* while (g->ptr > 0 && isspace(g->s[g->ptr-1])) g->ptr--; */ + while ( g->ptr > 0 && isspace(g->s[g->ptr-1]) + && (g->ptr == 1 || g->s[g->ptr-2] != '\\') ) + g->ptr--; buffer = string_from_gstring(g); gstring_release_unused(g); } diff --git a/test/scripts/0000-Basic/0373 b/test/scripts/0000-Basic/0373 index 5d8bbee88..37c98e0ab 100644 --- a/test/scripts/0000-Basic/0373 +++ b/test/scripts/0000-Basic/0373 @@ -60,7 +60,7 @@ quit # # Tests of IPv4 sockets # -server PORT_S 11 +server PORT_S 17 QUERY-1 >LF>ANSWER-1 >*eof @@ -89,6 +89,24 @@ QUERY-10 >*eof >LF>ANSWER-11 >*eof +QUERY-12 +>>ANSWER-12\x0d\x0aANSWER-12\x0d\x0a +>*eof +QUERY-13 +>>ANSWER-13\x0d\x0aANSWER-13\x0d\x0a +>*eof +QUERY-14 +>>ANSWER-14\x0d\x0aANSWER-14\x0d\x0a +>*eof +QUERY-15 +>>ANSWER-15\x0d\x0aANSWER-15\x0d\x0a +>*eof +QUERY-16 +>>ANSWER-16\x0d\x0aANSWER-16\x0d\x0a +>*eof +QUERY-17 +>>ANSWER-17\x0d\x0aANSWER-17\x0d\x0a +>*eof **** millisleep 500 exim -be @@ -104,6 +122,14 @@ ipv4 cases 9 sock error >>${readsocket{inet:127.0.0.1:PORT_S}{QUERY-9\n}{1s}{}{sock error}}<< 10 ANSWER-10\\n >>${readsocket{inet:badloop:PORT_S}{QUERY-10\n}}<< 11 ANSWER-11 >>${readsocket{inet:thisloop:PORT_S}{QUERY-11\n}{2s:shutdown=no}}<< + +eol-replacement arg +12 ANSWER-12x2 (no arg) >>${escape:${readsocket{inet:127.0.0.1:PORT_S}{QUERY-12\n}{2s}}}<< +13 ANSWER-13x2 (empty arg) >>${escape:${readsocket{inet:127.0.0.1:PORT_S}{QUERY-13\n}{2s}{}}}<< +14 ANSWER-14x2 X >>${escape:${readsocket{inet:127.0.0.1:PORT_S}{QUERY-14\n}{2s}{X}}}<< +15 ANSWER-15x2 XYZZY >>${escape:${readsocket{inet:127.0.0.1:PORT_S}{QUERY-15\n}{2s}{XYZZY}}}<< +16 ANSWER-16x2 (space) >>${escape:${readsocket{inet:127.0.0.1:PORT_S}{QUERY-16\n}{2s}{ }}}<< +17 ANSWER-17x2 (newline) >>${escape:${readsocket{inet:127.0.0.1:PORT_S}{QUERY-17\n}{2s}{\n}}}<< **** # exim -be diff --git a/test/stdout/0373 b/test/stdout/0373 index 513f36468..1e97d0dec 100644 --- a/test/stdout/0373 +++ b/test/stdout/0373 @@ -30,6 +30,14 @@ << > 11 ANSWER-11 >><< > +> eol-replacement arg +> 12 ANSWER-12x2 (no arg) >>ANSWER-12\r\nANSWER-12\r\n<< +> 13 ANSWER-13x2 (empty arg) >>ANSWER-13\r\nANSWER-13\r\n<< +> 14 ANSWER-14x2 X >>ANSWER-14\rXANSWER-14\rX<< +> 15 ANSWER-15x2 XYZZY >>ANSWER-15\rXYZZYANSWER-15\rXYZZY<< +> 16 ANSWER-16x2 (space) >>ANSWER-16\r ANSWER-16\r << +> 17 ANSWER-17x2 (newline) >>ANSWER-17\r\nANSWER-17\r\n<< +> > Failed: failed to connect to any address for 127.0.0.1: Connection refused > > caching of response value @@ -144,6 +152,36 @@ Listening on port 1224 ... Connection request from [ip4.ip4.ip4.ip4] >LF>ANSWER-11 >*eof +Listening on port 1224 ... +Connection request from [127.0.0.1] +QUERY-12 +>>ANSWER-12\x0d\x0aANSWER-12\x0d\x0a +>*eof +Listening on port 1224 ... +Connection request from [127.0.0.1] +QUERY-13 +>>ANSWER-13\x0d\x0aANSWER-13\x0d\x0a +>*eof +Listening on port 1224 ... +Connection request from [127.0.0.1] +QUERY-14 +>>ANSWER-14\x0d\x0aANSWER-14\x0d\x0a +>*eof +Listening on port 1224 ... +Connection request from [127.0.0.1] +QUERY-15 +>>ANSWER-15\x0d\x0aANSWER-15\x0d\x0a +>*eof +Listening on port 1224 ... +Connection request from [127.0.0.1] +QUERY-16 +>>ANSWER-16\x0d\x0aANSWER-16\x0d\x0a +>*eof +Listening on port 1224 ... +Connection request from [127.0.0.1] +QUERY-17 +>>ANSWER-17\x0d\x0aANSWER-17\x0d\x0a +>*eof End of script Listening on TESTSUITE/test-socket ... Connection request -- 2.30.2