]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: cli: allow custom pattern for payload
authorWilliam Lallemand <wlallemand@haproxy.com>
Mon, 27 Nov 2023 15:04:20 +0000 (16:04 +0100)
committerWilliam Lallemand <wlallemand@haproxy.com>
Tue, 28 Nov 2023 18:12:32 +0000 (19:12 +0100)
The CLI payload syntax has some limitation, it can't handle payloads
with empty lines, which is a common problem when uploading a PEM file
over the CLI.

This patch implements a way to customize the ending pattern of the CLI,
so we can't look for other things than empty lines.

A char cli_payload_pat[8] is used in the appctx to store the customized
pattern. The pattern can't be more than 7 characters and can still empty
to match an empty line.

The cli_io_handler() identifies the pattern and stores it, and
cli_parse_request() identifies the end of the payload.

If the customized pattern between "<<" and "\n" is more than 7
characters, it is not considered as a pattern.

This patch only implements the parser for the 'stats socket', another
patch is needed for the 'master CLI'.

include/haproxy/applet-t.h
src/cli.c

index 66672de0ec15c0d7e93c247e9df5804ee198663a..bd96403c58648c3aeb05c792d158a12b52df7602 100644 (file)
@@ -70,6 +70,7 @@ struct appctx {
                                                       if the command is terminated or the session released */
        int cli_severity_output;        /* used within the cli_io_handler to format severity output of informational feedback */
        int cli_level;              /* the level of CLI which can be lowered dynamically */
+       char cli_payload_pat[8];        /* Payload pattern */
        uint32_t cli_anon_key;       /* the key to anonymise with the hash in cli */
        struct buffer_wait buffer_wait; /* position in the list of objects waiting for a buffer */
        struct task *t;                  /* task associated to the applet */
index b176d523251c81f2569c7dbb3f54b84d73b06476..24184287a733b5f596aabedc07b44f8ee8d097ac 100644 (file)
--- a/src/cli.c
+++ b/src/cli.c
@@ -757,13 +757,22 @@ static int cli_parse_request(struct appctx *appctx)
                if (!*p)
                        break;
 
-               if (strcmp(p, PAYLOAD_PATTERN) == 0) {
-                       /* payload pattern recognized here, this is not an arg anymore,
-                        * the payload starts at the first byte that follows the zero
-                        * after the pattern.
-                        */
-                       payload = p + strlen(PAYLOAD_PATTERN) + 1;
-                       break;
+               /* first check if the '<<' is present, but this is not enough
+                * because we don't know if this is the end of the string */
+               if (strncmp(p, PAYLOAD_PATTERN, strlen(PAYLOAD_PATTERN)) == 0) {
+                       int pat_len = strlen(appctx->cli_payload_pat);
+
+                       /* then if the customized pattern is empty, check if the next character is '\0' */
+                       if (pat_len == 0 && p[strlen(PAYLOAD_PATTERN)] == '\0') {
+                               payload = p + strlen(PAYLOAD_PATTERN) + 1;
+                               break;
+                       }
+
+                       /* else if we found the customized pattern at the end of the string */
+                       if (strcmp(p + strlen(PAYLOAD_PATTERN), appctx->cli_payload_pat) == 0) {
+                               payload = p + strlen(PAYLOAD_PATTERN) + pat_len + 1;
+                               break;
+                       }
                }
 
                args[i] = p;
@@ -1010,29 +1019,53 @@ static void cli_io_handler(struct appctx *appctx)
                        appctx->st0 = CLI_ST_PROMPT;
 
                        if (appctx->st1 & APPCTX_CLI_ST1_PAYLOAD) {
-                               /* empty line */
-                               if (!len) {
-                                       /* remove the last two \n */
-                                       appctx->chunk->data -= 2;
-                                       appctx->chunk->area[appctx->chunk->data] = 0;
-                                       cli_parse_request(appctx);
-                                       chunk_reset(appctx->chunk);
-                                       /* NB: cli_sock_parse_request() may have put
-                                        * another CLI_ST_O_* into appctx->st0.
-                                        */
-
-                                       appctx->st1 &= ~APPCTX_CLI_ST1_PAYLOAD;
+                               /* look for a pattern */
+                               if (len == strlen(appctx->cli_payload_pat)) {
+                                       /* here use 'len' because str still contains the \n */
+                                       if (strncmp(str, appctx->cli_payload_pat, len) == 0) {
+                                               /* remove the last two \n */
+                                               appctx->chunk->data -= strlen(appctx->cli_payload_pat) + 2;
+                                               appctx->chunk->area[appctx->chunk->data] = 0;
+                                               cli_parse_request(appctx);
+                                               chunk_reset(appctx->chunk);
+                                               /* NB: cli_sock_parse_request() may have put
+                                                * another CLI_ST_O_* into appctx->st0.
+                                                */
+
+                                               appctx->st1 &= ~APPCTX_CLI_ST1_PAYLOAD;
+                                       }
                                }
                        }
                        else {
+                               char *last_arg;
                                /*
                                 * Look for the "payload start" pattern at the end of a line
                                 * Its location is not remembered here, this is just to switch
                                 * to a gathering mode.
+                                * The pattern must start by << followed by 0
+                                * to 7 characters, and finished by the end of
+                                * the command (\n or ;).
                                 */
-                               if (strcmp(appctx->chunk->area + appctx->chunk->data - strlen(PAYLOAD_PATTERN), PAYLOAD_PATTERN) == 0) {
-                                       appctx->st1 |= APPCTX_CLI_ST1_PAYLOAD;
-                                       appctx->chunk->data++; // keep the trailing \0 after '<<'
+                               /* look for the first space starting by the end of the line */
+                               for (last_arg = appctx->chunk->area + appctx->chunk->data; last_arg != appctx->chunk->area; last_arg--) {
+                                       if (*last_arg == ' ' || *last_arg == '\t') {
+                                               last_arg++;
+                                               break;
+                                       }
+                               }
+                               if (strncmp(last_arg, PAYLOAD_PATTERN, strlen(PAYLOAD_PATTERN)) == 0) {
+                                       ssize_t pat_len = strlen(last_arg + strlen(PAYLOAD_PATTERN));
+
+                                       /* A customized pattern can't be more than 7 characters
+                                        * if it's more, don't make it a payload
+                                        */
+                                       if (pat_len < sizeof(appctx->cli_payload_pat)) {
+                                               appctx->st1 |= APPCTX_CLI_ST1_PAYLOAD;
+                                               /* copy the customized pattern, don't store the << */
+                                               strncpy(appctx->cli_payload_pat, last_arg + strlen(PAYLOAD_PATTERN), sizeof(appctx->cli_payload_pat)-1);
+                                               appctx->cli_payload_pat[sizeof(appctx->cli_payload_pat)-1] = '\0';
+                                               appctx->chunk->data++; // keep the trailing \0 after the pattern
+                                       }
                                }
                                else {
                                        /* no payload, the command is complete: parse the request */