error was at byte 23. This is the slash ('/') in header name
"header/bizarre", which is not a valid HTTP character for a header name.
-show events [<sink>] [-w] [-n] [-0]
+show events [<sink>] [-w] [-n] [-0] [-s]
With no option, this lists all known event sinks and their types. With an
option, it will dump all available events in the designated sink if it is of
type buffer. If option "-w" is passed after the sink name, then once the end
may be used to enable both options at once. By default, all events are
delimited by a line feed character ('\n' or 10 or 0x0A). It is possible to
change this to the NUL character ('\0' or 0) by passing the "-0" argument.
+ Option "-s" sanitizes the output, so that binary contents resulting from
+ captured or decoded data do not alter the terminal's rendering. Whitespace
+ characters (space and tab) are replaced with a space so that each record
+ stays on a single line, and the other control characters (0x00 to 0x1F and
+ 0x7F) as well as non-ASCII chars (0x80 and above) are replaced with a '?'.
+ The output length is preserved. This way, dumping a ring cannot inject ANSI
+ escape sequences nor writing direction codes into the operator's terminal,
+ which is useful when dumping to a terminal, or to a program whose input must
+ not be interpreted as special (e.g. monitoring scripts whose output is copied
+ verbatim into reports).
show fd [-!plcfbs]* [[<tgid>]/[<fd>] | <fd>]
Dump the list of either all open file descriptors or just the one number <fd>
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
+#include <ctype.h>
#include <stdlib.h>
#include <haproxy/api.h>
#include <haproxy/applet.h>
return ret;
}
+/* Same as applet_append_line() but sanitises each event before appending it,
+ * so that dumping a ring cannot inject control chars (CR, LF, BS, NUL, ANSI
+ * escapes etc) nor non-ASCII into the operator's terminal, as rings may hold
+ * contents from untrusted sources (forwarded syslog lines, log-format output
+ * emitted without "+E"). For every input byte, control chars are replaced
+ * with '?', and everything else verbatim. This function is selected via
+ * RING_WF_SANITIZE in <ctx->flags>.
+ */
+static ssize_t show_ring_append_sanitized(void *ctx, struct ist v1, struct ist v2, size_t ofs, size_t len, char delim)
+{
+ struct appctx *appctx = ctx;
+ char *p;
+ size_t i;
+
+ if (unlikely(len + 1 > b_size(&trash))) {
+ /* too large a message to ever fit, let's skip it */
+ return -2;
+ }
+
+ chunk_reset(&trash);
+ vp_peek_ofs(v1, v2, ofs, trash.area, len);
+ for (i = 0, p = trash.area; i < len; i++, p++) {
+ if (isblank((unsigned char)*p))
+ *p = ' ';
+ else if (iscntrl((unsigned char)*p) || (unsigned char)*p >= 0x80)
+ *p = '?';
+ }
+ /* the delimiter is always emitted, including when it is a NUL byte
+ * (option "-0"), otherwise records would be glued together.
+ */
+ trash.data += len;
+ trash.area[trash.data++] = delim;
+
+ if (applet_putchk(appctx, &trash) == -1) {
+ if (applet_output_data(appctx) > 0)
+ return -1; // buffer is full
+ else
+ return -2; // buffer empty, output will never fit
+ }
+ return len;
+}
+
/* This function dumps all events from the ring whose pointer is in <p0> into
* the appctx's output buffer, and takes from <o0> the seek offset into the
* buffer's history (0 for oldest known event). It looks at <i0> for boolean
MT_LIST_DELETE(&appctx->wait_entry);
- ret = ring_dispatch_messages(ring, appctx, &ctx->ofs, &last_ofs, ctx->flags, applet_append_line,
+ ret = ring_dispatch_messages(ring, appctx, &ctx->ofs, &last_ofs, ctx->flags,
+ (ctx->flags & RING_WF_SANITIZE) ? show_ring_append_sanitized : applet_append_line,
(ctx->flags & RING_WF_END_ZERO) ? 0 : '\n', NULL);
if (ret && (ctx->flags & RING_WF_WAIT_MODE)) {
if (!*args[1]) {
/* no arg => report the list of supported sink */
- chunk_printf(&trash, "Supported events sinks are listed below. Add -0(zero), -w(wait), -n(new). Any key to stop.\n");
+ chunk_printf(&trash, "Supported events sinks are listed below. Add -0(zero), -w(wait), -n(new), -s(anitize). Any key to stop.\n");
list_for_each_entry(sink, &sink_list, sink_list) {
chunk_appendf(&trash, " %-10s : type=%s, %u dropped, %s\n",
sink->name,
ring_flags |= RING_WF_END_ZERO;
else if (strcmp(args[arg], "-nw") == 0 || strcmp(args[arg], "-wn") == 0)
ring_flags |= RING_WF_WAIT_MODE | RING_WF_SEEK_NEW;
+ else if (strcmp(args[arg], "-s") == 0)
+ ring_flags |= RING_WF_SANITIZE;
else
return cli_err(appctx, "unknown option");
}