MEDIUM: http: ACL and MAP updates through http-(request|response) rules

This patch allows manipulation of ACL and MAP content thanks to any
information available in a session: source IP address, HTTP request or
response header, etc...

It's an update "on the fly" of the content  of the map/acls. This means
it does not resist to reload or restart of HAProxy.
This commit is contained in:
Baptiste Assmann 2014-04-24 22:16:59 +02:00 committed by Willy Tarreau
parent 953f74d1b3
commit fabcbe0de6
3 changed files with 540 additions and 4 deletions

View File

@ -2848,7 +2848,12 @@ http-check send-state
http-request { allow | deny | tarpit | auth [realm <realm>] | redirect <rule> |
add-header <name> <fmt> | set-header <name> <fmt> |
del-header <name> | set-nice <nice> | set-log-level <level> |
set-tos <tos> | set-mark <mark> }
set-tos <tos> | set-mark <mark> |
add-acl(<file name>) <key fmt> |
del-acl(<file name>) <key fmt> |
del-map(<file name>) <key fmt> |
set-map(<file name>) <key fmt> <value fmt>
}
[ { if | unless } <condition> ]
Access control for Layer 7 requests
@ -2944,6 +2949,39 @@ http-request { allow | deny | tarpit | auth [realm <realm>] | redirect <rule> |
downloads). This works on Linux kernels 2.6.32 and above and requires
admin privileges.
- "add-acl" is used to add a new entry into an ACL. The ACL must be loaded
from a file (even a dummy empty file). The file name of the ACL to be
updated is passed between parentheses. It takes one argument: <key fmt>,
which follows log-format rules, to collect content of the new entry. It
performs a lookup in the ACL before insertion, to avoid duplicated (or
more) values. This lookup is done by a linear search and can be expensive
with large lists! It is the equivalent of the "add acl" command from the
stats socket, but can be triggered by an HTTP request.
- "del-acl" is used to delete an entry from an ACL. The ACL must be loaded
from a file (even a dummy empty file). The file name of the ACL to be
updated is passed between parentheses. It takes one argument: <key fmt>,
which follows log-format rules, to collect content of the entry to delete.
It is the equivalent of the "del acl" command from the stats socket, but
can be triggered by an HTTP request.
- "del-map" is used to delete an entry from a MAP. The MAP must be loaded
from a file (even a dummy empty file). The file name of the MAP to be
updated is passed between parentheses. It takes one argument: <key fmt>,
which follows log-format rules, to collect content of the entry to delete.
It takes one argument: "file name" It is the equivalent of the "del map"
command from the stats socket, but can be triggered by an HTTP request.
- "set-map" is used to add a new entry into a MAP. The MAP must be loaded
from a file (even a dummy empty file). The file name of the MAP to be
updated is passed between parentheses. It takes 2 arguments: <key fmt>,
which follows log-format rules, used to collect MAP key, and <value fmt>,
which follows log-format rules, used to collect content for the new entry.
It performs a lookup in the MAP before insertion, to avoid duplicated (or
more) values. This lookup is done by a linear search and can be expensive
with large lists! It is the equivalent of the "set map" command from the
stats socket, but can be triggered by an HTTP request.
There is no limit to the number of http-request statements per instance.
It is important to know that http-request rules are processed very early in
@ -2976,12 +3014,37 @@ http-request { allow | deny | tarpit | auth [realm <realm>] | redirect <rule> |
http-request set-header X-SSL-Client-NotBefore %{+Q}[ssl_c_notbefore]
http-request set-header X-SSL-Client-NotAfter %{+Q}[ssl_c_notafter]
Example:
acl key req.hdr(X-Add-Acl-Key) -m found
acl add path /addacl
acl del path /delacl
acl myhost hdr(Host) -f myhost.lst
http-request add-acl(myhost.lst) %[req.hdr(X-Add-Acl-Key)] if key add
http-request del-acl(myhost.lst) %[req.hdr(X-Add-Acl-Key)] if key del
Example:
acl value req.hdr(X-Value) -m found
acl setmap path /setmap
acl delmap path /delmap
use_backend bk_appli if { hdr(Host),map_str(map.lst) -m found }
http-request set-map(map.lst) %[src] %[req.hdr(X-Value)] if setmap value
http-request del-map(map.lst) %[src] if delmap
See also : "stats http-request", section 3.4 about userlists and section 7
about ACL usage.
http-response { allow | deny | add-header <name> <fmt> | set-nice <nice> |
set-header <name> <fmt> | del-header <name> |
set-log-level <level> | set-mark <mark> | set-tos <tos> }
set-log-level <level> | set-mark <mark> | set-tos <tos> |
add-acl(<file name>) <key fmt> |
del-acl(<file name>) <key fmt> |
del-map(<file name>) <key fmt> |
set-map(<file name>) <key fmt> <value fmt>
}
[ { if | unless } <condition> ]
Access control for Layer 7 responses
@ -3054,6 +3117,39 @@ http-response { allow | deny | add-header <name> <fmt> | set-nice <nice> |
downloads). This works on Linux kernels 2.6.32 and above and requires
admin privileges.
- "add-acl" is used to add a new entry into an ACL. The ACL must be loaded
from a file (even a dummy empty file). The file name of the ACL to be
updated is passed between parentheses. It takes one argument: <key fmt>,
which follows log-format rules, to collect content of the new entry. It
performs a lookup in the ACL before insertion, to avoid duplicated (or
more) values. This lookup is done by a linear search and can be expensive
with large lists! It is the equivalent of the "add acl" command from the
stats socket, but can be triggered by an HTTP response.
- "del-acl" is used to delete an entry from an ACL. The ACL must be loaded
from a file (even a dummy empty file). The file name of the ACL to be
updated is passed between parentheses. It takes one argument: <key fmt>,
which follows log-format rules, to collect content of the entry to delete.
It is the equivalent of the "del acl" command from the stats socket, but
can be triggered by an HTTP response.
- "del-map" is used to delete an entry from a MAP. The MAP must be loaded
from a file (even a dummy empty file). The file name of the MAP to be
updated is passed between parentheses. It takes one argument: <key fmt>,
which follows log-format rules, to collect content of the entry to delete.
It takes one argument: "file name" It is the equivalent of the "del map"
command from the stats socket, but can be triggered by an HTTP response.
- "set-map" is used to add a new entry into a MAP. The MAP must be loaded
from a file (even a dummy empty file). The file name of the MAP to be
updated is passed between parentheses. It takes 2 arguments: <key fmt>,
which follows log-format rules, used to collect MAP key, and <value fmt>,
which follows log-format rules, used to collect content for the new entry.
It performs a lookup in the MAP before insertion, to avoid duplicated (or
more) values. This lookup is done by a linear search and can be expensive
with large lists! It is the equivalent of the "set map" command from the
stats socket, but can be triggered by an HTTP response.
There is no limit to the number of http-response statements per instance.
It is important to know that http-response rules are processed very early in
@ -3061,6 +3157,22 @@ http-response { allow | deny | add-header <name> <fmt> | set-nice <nice> |
added by "add-header"/"set-header" are visible by almost all further ACL
rules.
Example:
acl key_acl res.hdr(X-Acl-Key) -m found
acl myhost hdr(Host) -f myhost.lst
http-response add-acl(myhost.lst) %[res.hdr(X-Acl-Key)] if key_acl
http-response del-acl(myhost.lst) %[res.hdr(X-Acl-Key)] if key_acl
Example:
acl value res.hdr(X-Value) -m found
use_backend bk_appli if { hdr(Host),map_str(map.lst) -m found }
http-response set-map(map.lst) %[src] %[res.hdr(X-Value)] if value
http-response del-map(map.lst) %[src] if ! value
See also : "http-request", section 3.4 about userlists and section 7 about
ACL usage.

View File

@ -252,6 +252,10 @@ enum {
HTTP_REQ_ACT_SET_LOGL,
HTTP_REQ_ACT_SET_TOS,
HTTP_REQ_ACT_SET_MARK,
HTTP_REQ_ACT_ADD_ACL,
HTTP_REQ_ACT_DEL_ACL,
HTTP_REQ_ACT_DEL_MAP,
HTTP_REQ_ACT_SET_MAP,
HTTP_REQ_ACT_MAX /* must always be last */
};
@ -267,6 +271,10 @@ enum {
HTTP_RES_ACT_SET_LOGL,
HTTP_RES_ACT_SET_TOS,
HTTP_RES_ACT_SET_MARK,
HTTP_RES_ACT_ADD_ACL,
HTTP_RES_ACT_DEL_ACL,
HTTP_RES_ACT_DEL_MAP,
HTTP_RES_ACT_SET_MAP,
HTTP_RES_ACT_MAX /* must always be last */
};
@ -404,6 +412,11 @@ struct http_req_rule {
int loglevel; /* log-level value for HTTP_REQ_ACT_SET_LOGL */
int tos; /* tos value for HTTP_REQ_ACT_SET_TOS */
int mark; /* nfmark value for HTTP_REQ_ACT_SET_MARK */
struct {
char *ref; /* MAP or ACL file name to update */
struct list key; /* pattern to retrieve MAP or ACL key */
struct list value; /* pattern to retrieve MAP value */
} map;
} arg; /* arguments used by some actions */
};
@ -421,6 +434,11 @@ struct http_res_rule {
int loglevel; /* log-level value for HTTP_RES_ACT_SET_LOGL */
int tos; /* tos value for HTTP_RES_ACT_SET_TOS */
int mark; /* nfmark value for HTTP_RES_ACT_SET_MARK */
struct {
char *ref; /* MAP or ACL file name to update */
struct list key; /* pattern to retrieve MAP or ACL key */
struct list value; /* pattern to retrieve MAP value */
} map;
} arg; /* arguments used by some actions */
};

View File

@ -64,6 +64,7 @@
#include <proto/session.h>
#include <proto/stream_interface.h>
#include <proto/task.h>
#include <proto/pattern.h>
const char HTTP_100[] =
"HTTP/1.1 100 Continue\r\n\r\n";
@ -3204,6 +3205,90 @@ http_req_get_intercept_rule(struct proxy *px, struct list *rules, struct session
trash.len += build_logline(s, trash.str + trash.len, trash.size - trash.len, &rule->arg.hdr_add.fmt);
http_header_add_tail2(&txn->req, &txn->hdr_idx, trash.str, trash.len);
break;
case HTTP_REQ_ACT_DEL_ACL:
case HTTP_REQ_ACT_DEL_MAP: {
struct pat_ref *ref;
char *key;
int len;
/* collect reference */
ref = pat_ref_lookup(rule->arg.map.ref);
if (!ref)
continue;
/* collect key */
len = build_logline(s, trash.str, trash.size, &rule->arg.map.key);
key = trash.str;
key[len] = '\0';
/* perform update */
/* returned code: 1=ok, 0=ko */
pat_ref_delete(ref, key);
break;
}
case HTTP_REQ_ACT_ADD_ACL: {
struct pat_ref *ref;
char *key;
struct chunk *trash_key;
int len;
trash_key = get_trash_chunk();
/* collect reference */
ref = pat_ref_lookup(rule->arg.map.ref);
if (!ref)
continue;
/* collect key */
len = build_logline(s, trash_key->str, trash_key->size, &rule->arg.map.key);
key = trash_key->str;
key[len] = '\0';
/* perform update */
/* add entry only if it does not already exist */
if (pat_ref_find_elt(ref, key) == NULL)
pat_ref_add(ref, key, NULL, NULL);
break;
}
case HTTP_REQ_ACT_SET_MAP: {
struct pat_ref *ref;
char *key, *value;
struct chunk *trash_key, *trash_value;
int len;
trash_key = get_trash_chunk();
trash_value = get_trash_chunk();
/* collect reference */
ref = pat_ref_lookup(rule->arg.map.ref);
if (!ref)
continue;
/* collect key */
len = build_logline(s, trash_key->str, trash_key->size, &rule->arg.map.key);
key = trash_key->str;
key[len] = '\0';
/* collect value */
len = build_logline(s, trash_value->str, trash_value->size, &rule->arg.map.value);
value = trash_value->str;
value[len] = '\0';
/* perform update */
if (pat_ref_find_elt(ref, key) != NULL)
/* update entry if it exists */
pat_ref_set(ref, key, value, NULL);
else
/* insert a new entry */
pat_ref_add(ref, key, value, NULL);
break;
}
}
}
@ -3293,6 +3378,90 @@ http_res_get_intercept_rule(struct proxy *px, struct list *rules, struct session
trash.len += build_logline(s, trash.str + trash.len, trash.size - trash.len, &rule->arg.hdr_add.fmt);
http_header_add_tail2(&txn->rsp, &txn->hdr_idx, trash.str, trash.len);
break;
case HTTP_RES_ACT_DEL_ACL:
case HTTP_RES_ACT_DEL_MAP: {
struct pat_ref *ref;
char *key;
int len;
/* collect reference */
ref = pat_ref_lookup(rule->arg.map.ref);
if (!ref)
continue;
/* collect key */
len = build_logline(s, trash.str, trash.size, &rule->arg.map.key);
key = trash.str;
key[len] = '\0';
/* perform update */
/* returned code: 1=ok, 0=ko */
pat_ref_delete(ref, key);
break;
}
case HTTP_RES_ACT_ADD_ACL: {
struct pat_ref *ref;
char *key;
struct chunk *trash_key;
int len;
trash_key = get_trash_chunk();
/* collect reference */
ref = pat_ref_lookup(rule->arg.map.ref);
if (!ref)
continue;
/* collect key */
len = build_logline(s, trash_key->str, trash_key->size, &rule->arg.map.key);
key = trash_key->str;
key[len] = '\0';
/* perform update */
/* check if the entry already exists */
if (pat_ref_find_elt(ref, key) == NULL)
pat_ref_add(ref, key, NULL, NULL);
break;
}
case HTTP_RES_ACT_SET_MAP: {
struct pat_ref *ref;
char *key, *value;
struct chunk *trash_key, *trash_value;
int len;
trash_key = get_trash_chunk();
trash_value = get_trash_chunk();
/* collect reference */
ref = pat_ref_lookup(rule->arg.map.ref);
if (!ref)
continue;
/* collect key */
len = build_logline(s, trash_key->str, trash_key->size, &rule->arg.map.key);
key = trash_key->str;
key[len] = '\0';
/* collect value */
len = build_logline(s, trash_value->str, trash_value->size, &rule->arg.map.value);
value = trash_value->str;
value[len] = '\0';
/* perform update */
if (pat_ref_find_elt(ref, key) != NULL)
/* update entry if it exists */
pat_ref_set(ref, key, value, NULL);
else
/* insert a new entry */
pat_ref_add(ref, key, value, NULL);
break;
}
}
}
@ -8652,8 +8821,125 @@ struct http_req_rule *parse_http_req_cond(const char **args, const char *file, i
redir->cond = NULL;
cur_arg = 2;
return rule;
} else if (strncmp(args[0], "add-acl", 7) == 0) {
/* http-request add-acl(<reference (acl name)>) <key pattern> */
rule->action = HTTP_REQ_ACT_ADD_ACL;
/*
* '+ 8' for 'add-acl('
* '- 9' for 'add-acl(' + trailing ')'
*/
rule->arg.map.ref = strndup(args[0] + 8, strlen(args[0]) - 9);
cur_arg = 1;
if (!*args[cur_arg] ||
(*args[cur_arg+1] && strcmp(args[cur_arg+1], "if") != 0 && strcmp(args[cur_arg+1], "unless") != 0)) {
Alert("parsing [%s:%d]: 'http-request %s' expects exactly 1 argument.\n",
file, linenum, args[0]);
goto out_err;
}
LIST_INIT(&rule->arg.map.key);
proxy->conf.args.ctx = ARGC_HRQ;
parse_logformat_string(args[cur_arg], proxy, &rule->arg.map.key, LOG_OPT_HTTP,
(proxy->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR,
file, linenum);
free(proxy->conf.lfs_file);
proxy->conf.lfs_file = strdup(proxy->conf.args.file);
proxy->conf.lfs_line = proxy->conf.args.line;
cur_arg += 1;
} else if (strncmp(args[0], "del-acl", 7) == 0) {
/* http-request del-acl(<reference (acl name)>) <key pattern> */
rule->action = HTTP_REQ_ACT_DEL_ACL;
/*
* '+ 8' for 'del-acl('
* '- 9' for 'del-acl(' + trailing ')'
*/
rule->arg.map.ref = strndup(args[0] + 8, strlen(args[0]) - 9);
cur_arg = 1;
if (!*args[cur_arg] ||
(*args[cur_arg+1] && strcmp(args[cur_arg+1], "if") != 0 && strcmp(args[cur_arg+1], "unless") != 0)) {
Alert("parsing [%s:%d]: 'http-request %s' expects exactly 1 argument.\n",
file, linenum, args[0]);
goto out_err;
}
LIST_INIT(&rule->arg.map.key);
proxy->conf.args.ctx = ARGC_HRQ;
parse_logformat_string(args[cur_arg], proxy, &rule->arg.map.key, LOG_OPT_HTTP,
(proxy->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR,
file, linenum);
free(proxy->conf.lfs_file);
proxy->conf.lfs_file = strdup(proxy->conf.args.file);
proxy->conf.lfs_line = proxy->conf.args.line;
cur_arg += 1;
} else if (strncmp(args[0], "del-map", 7) == 0) {
/* http-request del-map(<reference (map name)>) <key pattern> */
rule->action = HTTP_REQ_ACT_DEL_MAP;
/*
* '+ 8' for 'del-map('
* '- 9' for 'del-map(' + trailing ')'
*/
rule->arg.map.ref = strndup(args[0] + 8, strlen(args[0]) - 9);
cur_arg = 1;
if (!*args[cur_arg] ||
(*args[cur_arg+1] && strcmp(args[cur_arg+1], "if") != 0 && strcmp(args[cur_arg+1], "unless") != 0)) {
Alert("parsing [%s:%d]: 'http-request %s' expects exactly 1 argument.\n",
file, linenum, args[0]);
goto out_err;
}
LIST_INIT(&rule->arg.map.key);
proxy->conf.args.ctx = ARGC_HRQ;
parse_logformat_string(args[cur_arg], proxy, &rule->arg.map.key, LOG_OPT_HTTP,
(proxy->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR,
file, linenum);
free(proxy->conf.lfs_file);
proxy->conf.lfs_file = strdup(proxy->conf.args.file);
proxy->conf.lfs_line = proxy->conf.args.line;
cur_arg += 1;
} else if (strncmp(args[0], "set-map", 7) == 0) {
/* http-request set-map(<reference (map name)>) <key pattern> <value pattern> */
rule->action = HTTP_REQ_ACT_SET_MAP;
/*
* '+ 8' for 'set-map('
* '- 9' for 'set-map(' + trailing ')'
*/
rule->arg.map.ref = strndup(args[0] + 8, strlen(args[0]) - 9);
cur_arg = 1;
if (!*args[cur_arg] || !*args[cur_arg+1] ||
(*args[cur_arg+2] && strcmp(args[cur_arg+2], "if") != 0 && strcmp(args[cur_arg+2], "unless") != 0)) {
Alert("parsing [%s:%d]: 'http-request %s' expects exactly 2 arguments.\n",
file, linenum, args[0]);
goto out_err;
}
LIST_INIT(&rule->arg.map.key);
LIST_INIT(&rule->arg.map.value);
proxy->conf.args.ctx = ARGC_HRQ;
/* key pattern */
parse_logformat_string(args[cur_arg], proxy, &rule->arg.map.key, LOG_OPT_HTTP,
(proxy->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR,
file, linenum);
/* value pattern */
parse_logformat_string(args[cur_arg + 1], proxy, &rule->arg.map.value, LOG_OPT_HTTP,
(proxy->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR,
file, linenum);
free(proxy->conf.lfs_file);
proxy->conf.lfs_file = strdup(proxy->conf.args.file);
proxy->conf.lfs_line = proxy->conf.args.line;
cur_arg += 2;
} else {
Alert("parsing [%s:%d]: 'http-request' expects 'allow', 'deny', 'auth', 'redirect', 'tarpit', 'add-header', 'set-header', 'set-nice', 'set-tos', 'set-mark', 'set-log-level', but got '%s'%s.\n",
Alert("parsing [%s:%d]: 'http-request' expects 'allow', 'deny', 'auth', 'redirect', 'tarpit', 'add-header', 'set-header', 'set-nice', 'set-tos', 'set-mark', 'set-log-level', 'add-acl', 'del-acl', 'del-map', 'set-map', but got '%s'%s.\n",
file, linenum, args[0], *args[0] ? "" : " (missing argument)");
goto out_err;
}
@ -8824,8 +9110,128 @@ struct http_res_rule *parse_http_res_cond(const char **args, const char *file, i
proxy->conf.lfs_file = strdup(proxy->conf.args.file);
proxy->conf.lfs_line = proxy->conf.args.line;
cur_arg += 1;
} else if (strncmp(args[0], "add-acl", 7) == 0) {
/* http-request add-acl(<reference (acl name)>) <key pattern> */
rule->action = HTTP_RES_ACT_ADD_ACL;
/*
* '+ 8' for 'add-acl('
* '- 9' for 'add-acl(' + trailing ')'
*/
rule->arg.map.ref = strndup(args[0] + 8, strlen(args[0]) - 9);
cur_arg = 1;
if (!*args[cur_arg] ||
(*args[cur_arg+1] && strcmp(args[cur_arg+1], "if") != 0 && strcmp(args[cur_arg+1], "unless") != 0)) {
Alert("parsing [%s:%d]: 'http-response %s' expects exactly 1 argument.\n",
file, linenum, args[0]);
goto out_err;
}
LIST_INIT(&rule->arg.map.key);
proxy->conf.args.ctx = ARGC_HRS;
parse_logformat_string(args[cur_arg], proxy, &rule->arg.map.key, LOG_OPT_HTTP,
(proxy->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR,
file, linenum);
free(proxy->conf.lfs_file);
proxy->conf.lfs_file = strdup(proxy->conf.args.file);
proxy->conf.lfs_line = proxy->conf.args.line;
cur_arg += 1;
} else if (strncmp(args[0], "del-acl", 7) == 0) {
/* http-response del-acl(<reference (acl name)>) <key pattern> */
rule->action = HTTP_RES_ACT_DEL_ACL;
/*
* '+ 8' for 'del-acl('
* '- 9' for 'del-acl(' + trailing ')'
*/
rule->arg.map.ref = strndup(args[0] + 8, strlen(args[0]) - 9);
cur_arg = 1;
if (!*args[cur_arg] ||
(*args[cur_arg+1] && strcmp(args[cur_arg+1], "if") != 0 && strcmp(args[cur_arg+1], "unless") != 0)) {
Alert("parsing [%s:%d]: 'http-response %s' expects exactly 1 argument.\n",
file, linenum, args[0]);
goto out_err;
}
LIST_INIT(&rule->arg.map.key);
proxy->conf.args.ctx = ARGC_HRS;
parse_logformat_string(args[cur_arg], proxy, &rule->arg.map.key, LOG_OPT_HTTP,
(proxy->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR,
file, linenum);
free(proxy->conf.lfs_file);
proxy->conf.lfs_file = strdup(proxy->conf.args.file);
proxy->conf.lfs_line = proxy->conf.args.line;
cur_arg += 1;
} else if (strncmp(args[0], "del-map", 7) == 0) {
/* http-response del-map(<reference (map name)>) <key pattern> */
rule->action = HTTP_RES_ACT_DEL_MAP;
/*
* '+ 8' for 'del-map('
* '- 9' for 'del-map(' + trailing ')'
*/
rule->arg.map.ref = strndup(args[0] + 8, strlen(args[0]) - 9);
cur_arg = 1;
if (!*args[cur_arg] ||
(*args[cur_arg+1] && strcmp(args[cur_arg+1], "if") != 0 && strcmp(args[cur_arg+1], "unless") != 0)) {
Alert("parsing [%s:%d]: 'http-response %s' expects exactly 1 argument.\n",
file, linenum, args[0]);
goto out_err;
}
LIST_INIT(&rule->arg.map.key);
proxy->conf.args.ctx = ARGC_HRS;
parse_logformat_string(args[cur_arg], proxy, &rule->arg.map.key, LOG_OPT_HTTP,
(proxy->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR,
file, linenum);
free(proxy->conf.lfs_file);
proxy->conf.lfs_file = strdup(proxy->conf.args.file);
proxy->conf.lfs_line = proxy->conf.args.line;
cur_arg += 1;
} else if (strncmp(args[0], "set-map", 7) == 0) {
/* http-response set-map(<reference (map name)>) <key pattern> <value pattern> */
rule->action = HTTP_RES_ACT_SET_MAP;
/*
* '+ 8' for 'set-map('
* '- 9' for 'set-map(' + trailing ')'
*/
rule->arg.map.ref = strndup(args[0] + 8, strlen(args[0]) - 9);
cur_arg = 1;
if (!*args[cur_arg] || !*args[cur_arg+1] ||
(*args[cur_arg+2] && strcmp(args[cur_arg+2], "if") != 0 && strcmp(args[cur_arg+2], "unless") != 0)) {
Alert("parsing [%s:%d]: 'http-response %s' expects exactly 2 arguments.\n",
file, linenum, args[0]);
goto out_err;
}
LIST_INIT(&rule->arg.map.key);
LIST_INIT(&rule->arg.map.value);
proxy->conf.args.ctx = ARGC_HRS;
/* key pattern */
parse_logformat_string(args[cur_arg], proxy, &rule->arg.map.key, LOG_OPT_HTTP,
(proxy->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR,
file, linenum);
/* value pattern */
parse_logformat_string(args[cur_arg + 1], proxy, &rule->arg.map.value, LOG_OPT_HTTP,
(proxy->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR,
file, linenum);
free(proxy->conf.lfs_file);
proxy->conf.lfs_file = strdup(proxy->conf.args.file);
proxy->conf.lfs_line = proxy->conf.args.line;
cur_arg += 2;
} else {
Alert("parsing [%s:%d]: 'http-response' expects 'allow', 'deny', 'redirect', 'add-header', 'set-header', 'set-nice', 'set-tos', 'set-mark', 'set-log-level', but got '%s'%s.\n",
Alert("parsing [%s:%d]: 'http-response' expects 'allow', 'deny', 'redirect', 'add-header', 'del-header', 'set-header', 'set-nice', 'set-tos', 'set-mark', 'set-log-level', 'del-acl', 'add-acl', 'del-map', 'set-map', but got '%s'%s.\n",
file, linenum, args[0], *args[0] ? "" : " (missing argument)");
goto out_err;
}