]> git.kaiwu.me - haproxy.git/commitdiff
MINOR: log: add app_log_raw() and send_log_raw() for binary-safe logging
authorDragan Dosen <ddosen@haproxy.com>
Wed, 1 Jul 2026 08:18:49 +0000 (08:18 +0000)
committerWilly Tarreau <w@1wt.eu>
Wed, 1 Jul 2026 21:51:17 +0000 (23:51 +0200)
app_log() and send_log() build the message with vsnprintf(), which stops
at the first NUL byte and therefore cannot emit an arbitrary binary
payload.

Add two variants that pass a pre-built <msg> of <len> bytes straight to
__send_log() without formatting it, so embedded NUL bytes are preserved:

  * app_log_raw()  : takes an explicit list of loggers and a tag
  * send_log_raw() : derives both from a proxy

The send path still strips trailing LF / NUL bytes (kept for the legacy
text logs), so the message must be self-terminating by its own encoding
and must not rely on a meaningful trailing '\n' or NUL.

include/haproxy/log.h
src/log.c

index f1610a29708c6659810bc42fdd3f13a7f490fd52..061d61801691692fa9b8dc34ea7fce5c18bdb7a4 100644 (file)
@@ -142,6 +142,8 @@ static inline void sess_log_embryonic(struct session *sess)
 void app_log(struct list *loggers, struct buffer *tag, int level, const char *format, ...)
        __attribute__ ((format(printf, 4, 5)));
 
+void app_log_raw(struct list *loggers, struct buffer *tag, int level, char *msg, size_t len);
+
 /*
  * add to the logformat linked list
  */
@@ -173,6 +175,8 @@ int parse_logger(char **args, struct list *loggers, int do_del, const char *file
 void send_log(struct proxy *p, int level, const char *format, ...)
        __attribute__ ((format(printf, 3, 4)));
 
+void send_log_raw(struct proxy *p, int level, char *msg, size_t len);
+
 /*
  * returns log format for <fmt> or LOG_FORMAT_UNSPEC if not found.
  */
index 1acc1a0c4677bed60e7f669140ae7bf84cbc37c7..68826f49cd126ca015755ebe1b9ca150903c6e8c 100644 (file)
--- a/src/log.c
+++ b/src/log.c
@@ -3208,6 +3208,23 @@ void send_log(struct proxy *p, int level, const char *format, ...)
                   logline, data_len, default_rfc5424_sd_log_format, 2);
 }
 
+/*
+ * Same as send_log() but emits the pre-built message <msg> of <len> bytes
+ * as-is (no vsnprintf), so embedded NUL bytes are preserved, which makes
+ * it usable for binary payloads. The send path still strips trailing '\n'
+ * / NUL bytes, so <msg> must be self-terminating by its own encoding and
+ * not carry a meaningful trailing '\n' or NUL.
+ */
+void send_log_raw(struct proxy *p, int level, char *msg, size_t len)
+{
+       if (level < 0 || msg == NULL)
+               return;
+
+       __send_log(NULL, (p ? &p->loggers : NULL),
+                  (p ? &p->log_tag : NULL), level,
+                  msg, len, default_rfc5424_sd_log_format, 2);
+}
+
 /*
  * This function builds a log header according to <hdr> settings.
  *
@@ -5456,6 +5473,21 @@ void app_log(struct list *loggers, struct buffer *tag, int level, const char *fo
        __send_log(NULL, loggers, tag, level, logline, data_len, default_rfc5424_sd_log_format, 2);
 }
 
+/*
+ * Same as app_log() but emits the pre-built message <msg> of <len> bytes
+ * as-is (no vsnprintf), so embedded NUL bytes are preserved, which makes
+ * it usable for binary payloads. The send path still strips trailing '\n'
+ * / NUL bytes, so <msg> must be self-terminating by its own encoding and
+ * not carry a meaningful trailing '\n' or NUL.
+ */
+void app_log_raw(struct list *loggers, struct buffer *tag, int level, char *msg, size_t len)
+{
+       if (level < 0 || msg == NULL)
+               return;
+
+       __send_log(NULL, loggers, tag, level, msg, len, default_rfc5424_sd_log_format, 2);
+}
+
 /*
  * This function sets up the initial state for a log message by preparing
  * the buffer, setting default values for the log level and facility, and