From f9873ba63a6c02e9350d71c8b85f5e5c46c68f46 Mon Sep 17 00:00:00 2001 From: William Lallemand Date: Tue, 5 May 2015 17:37:14 +0200 Subject: [PATCH] MEDIUM: cfgparse: introduce weak and strong quoting This patch introduces quoting which allows to write configuration string including spaces without escaping them. Strong (with single quotes) and weak (with double quotes) quoting are supported. Weak quoting supports escaping and special characters when strong quoting does not interpret anything. This patch could break configuration files where ' and " where used. --- doc/configuration.txt | 64 ++++++++++++++++++++++++++++++++++++++----- src/cfgparse.c | 43 +++++++++++++++++++++++++---- 2 files changed, 95 insertions(+), 12 deletions(-) diff --git a/doc/configuration.txt b/doc/configuration.txt index 85d94d985..9a6202ded 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -39,8 +39,9 @@ Summary 2. Configuring HAProxy 2.1. Configuration file format -2.2. Time format -2.3. Examples +2.2. Quoting and escaping +2.3. Time format +2.4. Examples 3. Global parameters 3.1. Process management and security @@ -366,12 +367,61 @@ HAProxy's configuration process involves 3 major sources of parameters : The configuration file syntax consists in lines beginning with a keyword referenced in this manual, optionally followed by one or several parameters -delimited by spaces. If spaces have to be entered in strings, then they must be -preceded by a backslash ('\') to be escaped. Backslashes also have to be -escaped by doubling them. +delimited by spaces. -2.2. Time format +2.2. Quoting and escaping +------------------------- + +HAProxy's configuration introduces a quoting and escaping system similar to +many programming languages. The configuration file supports 3 types: escaping +with a backslash, weak quoting with double quotes, and strong quoting with +single quotes. + +If spaces have to be entered in strings, then they must be escaped by preceding +them by a backslash ('\') or by quoting them. Backslashes also have to be +escaped by doubling or strong quoting them. + +Escaping is achieved by preceding a special character by a backslash ('\'): + + \ to mark a space and differentiate it from a delimiter + \# to mark a hash and differentiate it from a comment + \\ to use a backslash + \' to use a single quote and differentiate it from strong quoting + \" to use a double quote and differentiate it from weak quoting + +Weak quoting is achieved by using double quotes (""). Weak quoting prevents +the interpretation of: + + space as a parameter separator + ' single quote as a strong quoting delimiter + # hash as a comment start + +But interpretation of escaping and special characters are not prevented by weak +quoting. + +Strong quoting is achieved by using single quotes (''). Inside single quotes, +nothing is interpreted, it's the efficient way to quote regexes. + +Quoted and escaped strings are replaced in memory by their interpreted +equivalent, it allows you to perform concatenation. + + Example: + # those are equivalents: + log-format %{+Q}o\ %t\ %s\ %{-Q}r + log-format "%{+Q}o %t %s %{-Q}r" + log-format '%{+Q}o %t %s %{-Q}r' + log-format "%{+Q}o %t"' %s %{-Q}r' + log-format "%{+Q}o %t"' %s'\ %{-Q}r + + # those are equivalents: + reqrep "^([^\ :]*)\ /static/(.*)" \1\ /\2 + reqrep "^([^ :]*)\ /static/(.*)" '\1 /\2' + reqrep "^([^ :]*)\ /static/(.*)" "\1 /\2" + reqrep "^([^ :]*)\ /static/(.*)" "\1\ /\2" + + +2.3. Time format ---------------- Some parameters involve values representing time, such as timeouts. These @@ -388,7 +438,7 @@ for every keyword. Supported units are : - d : days. 1d = 24h = 1440m = 86400s = 86400000ms -2.3. Examples +2.4. Examples ------------- # Simple configuration for an HTTP proxy listening on port 80 on all diff --git a/src/cfgparse.c b/src/cfgparse.c index 4d0a91a1e..96ee79873 100644 --- a/src/cfgparse.c +++ b/src/cfgparse.c @@ -6207,6 +6207,8 @@ int readcfgfile(const char *file) char *end; char *args[MAX_LINE_ARGS + 1]; char *line = thisline; + int dquote = 0; /* double quote */ + int squote = 0; /* simple quote */ linenum++; @@ -6224,15 +6226,31 @@ int readcfgfile(const char *file) /* skip leading spaces */ while (isspace((unsigned char)*line)) line++; - + arg = 0; args[arg] = line; while (*line && arg < MAX_LINE_ARGS) { + if (*line == '"' && !squote) { /* double quote outside single quotes */ + if (dquote) + dquote = 0; + else + dquote = 1; + memmove(line, line + 1, end - (line + 1)); + end--; + } + else if (*line == '\'' && !dquote) { /* single quote outside double quotes */ + if (squote) + squote = 0; + else + squote = 1; + memmove(line, line + 1, end - (line + 1)); + end--; + } + else if (*line == '\\' && !squote) { /* first, we'll replace \\, \, \#, \r, \n, \t, \xXX with their * C equivalent value. Other combinations left unchanged (eg: \1). */ - if (*line == '\\') { int skip = 0; if (line[1] == ' ' || line[1] == '\\' || line[1] == '#') { *line = line[1]; @@ -6241,7 +6259,7 @@ int readcfgfile(const char *file) else if (line[1] == 'r') { *line = '\r'; skip = 1; - } + } else if (line[1] == 'n') { *line = '\n'; skip = 1; @@ -6264,6 +6282,12 @@ int readcfgfile(const char *file) Alert("parsing [%s:%d] : invalid or incomplete '\\x' sequence in '%s'.\n", file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; } + } else if (line[1] == '"') { + *line = '"'; + skip = 1; + } else if (line[1] == '\'') { + *line = '\''; + skip = 1; } if (skip) { memmove(line + 1, line + 1 + skip, end - (line + skip)); @@ -6271,12 +6295,12 @@ int readcfgfile(const char *file) } line++; } - else if (*line == '#' || *line == '\n' || *line == '\r') { + else if ((!squote && !dquote && *line == '#') || *line == '\n' || *line == '\r') { /* end of string, end of loop */ *line = 0; break; } - else if (isspace((unsigned char)*line)) { + else if (!squote && !dquote && isspace((unsigned char)*line)) { /* a non-escaped space is an argument separator */ *line++ = '\0'; while (isspace((unsigned char)*line)) @@ -6287,6 +6311,15 @@ int readcfgfile(const char *file) line++; } } + if (dquote) { + Alert("parsing [%s:%d] : Mismatched double quotes.\n", file, linenum); + err_code |= ERR_ALERT | ERR_FATAL; + } + + if (squote) { + Alert("parsing [%s:%d] : Mismatched simple quotes.\n", file, linenum); + err_code |= ERR_ALERT | ERR_FATAL; + } /* empty line */ if (!**args)