diff --git a/Makefile b/Makefile index 3140a4ac3..5cf4ce0b6 100644 --- a/Makefile +++ b/Makefile @@ -895,7 +895,7 @@ OBJS = src/proto_http.o src/cfgparse.o src/server.o src/stream.o \ src/protocol.o src/lru.o src/hdr_idx.o src/hpack-huff.o \ src/mailers.o src/h2.o src/base64.o src/hash.o src/http.o \ src/http_acl.o src/http_fetch.o src/http_conv.o src/http_act.o \ - src/proto_sockpair.o + src/http_rules.o src/proto_sockpair.o EBTREE_OBJS = $(EBTREE_DIR)/ebtree.o $(EBTREE_DIR)/eb32sctree.o \ $(EBTREE_DIR)/eb32tree.o $(EBTREE_DIR)/eb64tree.o \ diff --git a/include/proto/http_rules.h b/include/proto/http_rules.h new file mode 100644 index 000000000..5e03dd813 --- /dev/null +++ b/include/proto/http_rules.h @@ -0,0 +1,57 @@ +/* + * include/proto/http_rules.h + * This file contains "http" rules definitions + * + * Copyright (C) 2000-2018 Willy Tarreau - w@1wt.eu + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, version 2.1 + * exclusively. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _PROTO_HTTP_RULES_H +#define _PROTO_HTTP_RULES_H + +#include +#include +#include +#include + +extern struct action_kw_list http_req_keywords; +extern struct action_kw_list http_res_keywords; + +struct act_rule *parse_http_req_cond(const char **args, const char *file, int linenum, struct proxy *proxy); +struct act_rule *parse_http_res_cond(const char **args, const char *file, int linenum, struct proxy *proxy); +void free_http_req_rules(struct list *r); +void free_http_res_rules(struct list *r); +struct redirect_rule *http_parse_redirect_rule(const char *file, int linenum, struct proxy *curproxy, + const char **args, char **errmsg, int use_fmt, int dir); + +static inline void http_req_keywords_register(struct action_kw_list *kw_list) +{ + LIST_ADDQ(&http_req_keywords.list, &kw_list->list); +} + +static inline void http_res_keywords_register(struct action_kw_list *kw_list) +{ + LIST_ADDQ(&http_res_keywords.list, &kw_list->list); +} + +#endif /* _PROTO_HTTP_RULES_H */ + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * End: + */ diff --git a/include/proto/proto_http.h b/include/proto/proto_http.h index da6d0c74a..daa72bb63 100644 --- a/include/proto/proto_http.h +++ b/include/proto/proto_http.h @@ -23,7 +23,6 @@ #define _PROTO_PROTO_HTTP_H #include -#include #include #include #include @@ -115,28 +114,8 @@ void http_reset_txn(struct stream *s); void http_end_txn_clean_session(struct stream *s); void http_adjust_conn_mode(struct stream *s, struct http_txn *txn, struct http_msg *msg); -struct act_rule *parse_http_req_cond(const char **args, const char *file, int linenum, struct proxy *proxy); -struct act_rule *parse_http_res_cond(const char **args, const char *file, int linenum, struct proxy *proxy); -void free_http_req_rules(struct list *r); -void free_http_res_rules(struct list *r); void http_reply_and_close(struct stream *s, short status, struct buffer *msg); struct buffer *http_error_message(struct stream *s); -struct redirect_rule *http_parse_redirect_rule(const char *file, int linenum, struct proxy *curproxy, - const char **args, char **errmsg, int use_fmt, int dir); - -struct action_kw *action_http_req_custom(const char *kw); -struct action_kw *action_http_res_custom(const char *kw); - -static inline void http_req_keywords_register(struct action_kw_list *kw_list) -{ - LIST_ADDQ(&http_req_keywords.list, &kw_list->list); -} - -static inline void http_res_keywords_register(struct action_kw_list *kw_list) -{ - LIST_ADDQ(&http_res_keywords.list, &kw_list->list); -} - /* to be used when contents change in an HTTP message */ #define http_msg_move_end(msg, bytes) do { \ diff --git a/include/proto/stream.h b/include/proto/stream.h index 5e7c4ccde..0e1cf66e0 100644 --- a/include/proto/stream.h +++ b/include/proto/stream.h @@ -24,6 +24,7 @@ #include #include +#include #include #include #include diff --git a/include/types/proto_http.h b/include/types/proto_http.h index 5cb058944..1b10c0790 100644 --- a/include/types/proto_http.h +++ b/include/types/proto_http.h @@ -317,9 +317,6 @@ struct hdr_ctx { int prev; /* index of previous header */ }; -extern struct action_kw_list http_req_keywords; -extern struct action_kw_list http_res_keywords; - extern struct pool_head *pool_head_http_txn; #endif /* _TYPES_PROTO_HTTP_H */ diff --git a/src/cache.c b/src/cache.c index 3abb46cea..2024642f9 100644 --- a/src/cache.c +++ b/src/cache.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include diff --git a/src/cfgparse.c b/src/cfgparse.c index e20ab72da..e5d6082f9 100644 --- a/src/cfgparse.c +++ b/src/cfgparse.c @@ -66,6 +66,7 @@ #include #include #include +#include #include #include #include diff --git a/src/flt_spoe.c b/src/flt_spoe.c index 73e8f623a..81af855fd 100644 --- a/src/flt_spoe.c +++ b/src/flt_spoe.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include diff --git a/src/haproxy.c b/src/haproxy.c index 3bf1f3bcd..728e71d9a 100644 --- a/src/haproxy.c +++ b/src/haproxy.c @@ -99,6 +99,7 @@ #include #include #include +#include #include #include #include diff --git a/src/hlua.c b/src/hlua.c index c2be5f9f8..aaa884073 100644 --- a/src/hlua.c +++ b/src/hlua.c @@ -43,6 +43,7 @@ #include #include #include +#include #include #include #include diff --git a/src/http_act.c b/src/http_act.c index 6ed4852f1..8ca6ba482 100644 --- a/src/http_act.c +++ b/src/http_act.c @@ -30,6 +30,7 @@ #include #include +#include #include #include diff --git a/src/http_rules.c b/src/http_rules.c new file mode 100644 index 000000000..ce6724049 --- /dev/null +++ b/src/http_rules.c @@ -0,0 +1,1212 @@ +/* + * HTTP rules parsing and registration + * + * Copyright 2000-2018 Willy Tarreau + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + + +/* List head of all known action keywords for "http-request" */ +struct action_kw_list http_req_keywords = { + .list = LIST_HEAD_INIT(http_req_keywords.list) +}; + +/* List head of all known action keywords for "http-response" */ +struct action_kw_list http_res_keywords = { + .list = LIST_HEAD_INIT(http_res_keywords.list) +}; + +/* + * Return the struct http_req_action_kw associated to a keyword. + */ +static struct action_kw *action_http_req_custom(const char *kw) +{ + return action_lookup(&http_req_keywords.list, kw); +} + +/* + * Return the struct http_res_action_kw associated to a keyword. + */ +static struct action_kw *action_http_res_custom(const char *kw) +{ + return action_lookup(&http_res_keywords.list, kw); +} + +/* parse an "http-request" rule */ +struct act_rule *parse_http_req_cond(const char **args, const char *file, int linenum, struct proxy *proxy) +{ + struct act_rule *rule; + struct action_kw *custom = NULL; + int cur_arg; + char *error; + + rule = calloc(1, sizeof(*rule)); + if (!rule) { + ha_alert("parsing [%s:%d]: out of memory.\n", file, linenum); + goto out_err; + } + + if (!strcmp(args[0], "allow")) { + rule->action = ACT_ACTION_ALLOW; + cur_arg = 1; + } else if (!strcmp(args[0], "deny") || !strcmp(args[0], "block") || !strcmp(args[0], "tarpit")) { + int code; + int hc; + + if (!strcmp(args[0], "tarpit")) { + rule->action = ACT_HTTP_REQ_TARPIT; + rule->deny_status = HTTP_ERR_500; + } + else { + rule->action = ACT_ACTION_DENY; + rule->deny_status = HTTP_ERR_403; + } + cur_arg = 1; + if (strcmp(args[cur_arg], "deny_status") == 0) { + cur_arg++; + if (!args[cur_arg]) { + ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-request %s' rule : missing status code.\n", + file, linenum, proxy_type_str(proxy), proxy->id, args[0]); + goto out_err; + } + + code = atol(args[cur_arg]); + cur_arg++; + for (hc = 0; hc < HTTP_ERR_SIZE; hc++) { + if (http_err_codes[hc] == code) { + rule->deny_status = hc; + break; + } + } + + if (hc >= HTTP_ERR_SIZE) { + ha_warning("parsing [%s:%d] : status code %d not handled, using default code %d.\n", + file, linenum, code, http_err_codes[rule->deny_status]); + } + } + } else if (!strcmp(args[0], "auth")) { + rule->action = ACT_HTTP_REQ_AUTH; + cur_arg = 1; + + while(*args[cur_arg]) { + if (!strcmp(args[cur_arg], "realm")) { + rule->arg.auth.realm = strdup(args[cur_arg + 1]); + cur_arg+=2; + continue; + } else + break; + } + } else if (!strcmp(args[0], "set-nice")) { + rule->action = ACT_HTTP_SET_NICE; + 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)) { + ha_alert("parsing [%s:%d]: 'http-request %s' expects exactly 1 argument (integer value).\n", + file, linenum, args[0]); + goto out_err; + } + rule->arg.nice = atoi(args[cur_arg]); + if (rule->arg.nice < -1024) + rule->arg.nice = -1024; + else if (rule->arg.nice > 1024) + rule->arg.nice = 1024; + cur_arg++; + } else if (!strcmp(args[0], "set-tos")) { +#ifdef IP_TOS + char *err; + rule->action = ACT_HTTP_SET_TOS; + 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)) { + ha_alert("parsing [%s:%d]: 'http-request %s' expects exactly 1 argument (integer/hex value).\n", + file, linenum, args[0]); + goto out_err; + } + + rule->arg.tos = strtol(args[cur_arg], &err, 0); + if (err && *err != '\0') { + ha_alert("parsing [%s:%d]: invalid character starting at '%s' in 'http-request %s' (integer/hex value expected).\n", + file, linenum, err, args[0]); + goto out_err; + } + cur_arg++; +#else + ha_alert("parsing [%s:%d]: 'http-request %s' is not supported on this platform (IP_TOS undefined).\n", file, linenum, args[0]); + goto out_err; +#endif + } else if (!strcmp(args[0], "set-mark")) { +#ifdef SO_MARK + char *err; + rule->action = ACT_HTTP_SET_MARK; + 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)) { + ha_alert("parsing [%s:%d]: 'http-request %s' expects exactly 1 argument (integer/hex value).\n", + file, linenum, args[0]); + goto out_err; + } + + rule->arg.mark = strtoul(args[cur_arg], &err, 0); + if (err && *err != '\0') { + ha_alert("parsing [%s:%d]: invalid character starting at '%s' in 'http-request %s' (integer/hex value expected).\n", + file, linenum, err, args[0]); + goto out_err; + } + cur_arg++; + global.last_checks |= LSTCHK_NETADM; +#else + ha_alert("parsing [%s:%d]: 'http-request %s' is not supported on this platform (SO_MARK undefined).\n", file, linenum, args[0]); + goto out_err; +#endif + } else if (!strcmp(args[0], "set-log-level")) { + rule->action = ACT_HTTP_SET_LOGL; + 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)) { + bad_log_level: + ha_alert("parsing [%s:%d]: 'http-request %s' expects exactly 1 argument (log level name or 'silent').\n", + file, linenum, args[0]); + goto out_err; + } + if (strcmp(args[cur_arg], "silent") == 0) + rule->arg.loglevel = -1; + else if ((rule->arg.loglevel = get_log_level(args[cur_arg]) + 1) == 0) + goto bad_log_level; + cur_arg++; + } else if (strcmp(args[0], "add-header") == 0 || strcmp(args[0], "set-header") == 0) { + rule->action = *args[0] == 'a' ? ACT_HTTP_ADD_HDR : ACT_HTTP_SET_HDR; + 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)) { + ha_alert("parsing [%s:%d]: 'http-request %s' expects exactly 2 arguments.\n", + file, linenum, args[0]); + goto out_err; + } + + rule->arg.hdr_add.name = strdup(args[cur_arg]); + rule->arg.hdr_add.name_len = strlen(rule->arg.hdr_add.name); + LIST_INIT(&rule->arg.hdr_add.fmt); + + proxy->conf.args.ctx = ARGC_HRQ; + error = NULL; + if (!parse_logformat_string(args[cur_arg + 1], proxy, &rule->arg.hdr_add.fmt, LOG_OPT_HTTP, + (proxy->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR, &error)) { + ha_alert("parsing [%s:%d]: 'http-request %s': %s.\n", + file, linenum, args[0], error); + free(error); + goto out_err; + } + 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 if (strcmp(args[0], "replace-header") == 0 || strcmp(args[0], "replace-value") == 0) { + rule->action = args[0][8] == 'h' ? ACT_HTTP_REPLACE_HDR : ACT_HTTP_REPLACE_VAL; + cur_arg = 1; + + if (!*args[cur_arg] || !*args[cur_arg+1] || !*args[cur_arg+2] || + (*args[cur_arg+3] && strcmp(args[cur_arg+3], "if") != 0 && strcmp(args[cur_arg+3], "unless") != 0)) { + ha_alert("parsing [%s:%d]: 'http-request %s' expects exactly 3 arguments.\n", + file, linenum, args[0]); + goto out_err; + } + + rule->arg.hdr_add.name = strdup(args[cur_arg]); + rule->arg.hdr_add.name_len = strlen(rule->arg.hdr_add.name); + LIST_INIT(&rule->arg.hdr_add.fmt); + + error = NULL; + if (!regex_comp(args[cur_arg + 1], &rule->arg.hdr_add.re, 1, 1, &error)) { + ha_alert("parsing [%s:%d] : '%s' : %s.\n", file, linenum, + args[cur_arg + 1], error); + free(error); + goto out_err; + } + + proxy->conf.args.ctx = ARGC_HRQ; + error = NULL; + if (!parse_logformat_string(args[cur_arg + 2], proxy, &rule->arg.hdr_add.fmt, LOG_OPT_HTTP, + (proxy->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR, &error)) { + ha_alert("parsing [%s:%d]: 'http-request %s': %s.\n", + file, linenum, args[0], error); + free(error); + goto out_err; + } + + free(proxy->conf.lfs_file); + proxy->conf.lfs_file = strdup(proxy->conf.args.file); + proxy->conf.lfs_line = proxy->conf.args.line; + cur_arg += 3; + } else if (strcmp(args[0], "del-header") == 0) { + rule->action = ACT_HTTP_DEL_HDR; + 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)) { + ha_alert("parsing [%s:%d]: 'http-request %s' expects exactly 1 argument.\n", + file, linenum, args[0]); + goto out_err; + } + + rule->arg.hdr_add.name = strdup(args[cur_arg]); + rule->arg.hdr_add.name_len = strlen(rule->arg.hdr_add.name); + + proxy->conf.args.ctx = ARGC_HRQ; + 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], "track-sc", 8) == 0) { + struct sample_expr *expr; + unsigned int where; + char *err = NULL; + unsigned int tsc_num; + const char *tsc_num_str; + + cur_arg = 1; + proxy->conf.args.ctx = ARGC_TRK; + + tsc_num_str = &args[0][8]; + if (cfg_parse_track_sc_num(&tsc_num, tsc_num_str, tsc_num_str + strlen(tsc_num_str), &err) == -1) { + ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-request %s' rule : %s.\n", + file, linenum, proxy_type_str(proxy), proxy->id, args[0], err); + free(err); + goto out_err; + } + + expr = sample_parse_expr((char **)args, &cur_arg, file, linenum, &err, &proxy->conf.args); + if (!expr) { + ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-request %s' rule : %s.\n", + file, linenum, proxy_type_str(proxy), proxy->id, args[0], err); + free(err); + goto out_err; + } + + where = 0; + if (proxy->cap & PR_CAP_FE) + where |= SMP_VAL_FE_HRQ_HDR; + if (proxy->cap & PR_CAP_BE) + where |= SMP_VAL_BE_HRQ_HDR; + + if (!(expr->fetch->val & where)) { + ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-request %s' rule :" + " fetch method '%s' extracts information from '%s', none of which is available here.\n", + file, linenum, proxy_type_str(proxy), proxy->id, args[0], + args[cur_arg-1], sample_src_names(expr->fetch->use)); + free(expr); + goto out_err; + } + + if (strcmp(args[cur_arg], "table") == 0) { + cur_arg++; + if (!args[cur_arg]) { + ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-request %s' rule : missing table name.\n", + file, linenum, proxy_type_str(proxy), proxy->id, args[0]); + free(expr); + goto out_err; + } + /* we copy the table name for now, it will be resolved later */ + rule->arg.trk_ctr.table.n = strdup(args[cur_arg]); + cur_arg++; + } + rule->arg.trk_ctr.expr = expr; + rule->action = ACT_ACTION_TRK_SC0 + tsc_num; + rule->check_ptr = check_trk_action; + } else if (strcmp(args[0], "redirect") == 0) { + struct redirect_rule *redir; + char *errmsg = NULL; + + if ((redir = http_parse_redirect_rule(file, linenum, proxy, (const char **)args + 1, &errmsg, 1, 0)) == NULL) { + ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-request %s' rule : %s.\n", + file, linenum, proxy_type_str(proxy), proxy->id, args[0], errmsg); + goto out_err; + } + + /* this redirect rule might already contain a parsed condition which + * we'll pass to the http-request rule. + */ + rule->action = ACT_HTTP_REDIR; + rule->arg.redir = redir; + rule->cond = redir->cond; + redir->cond = NULL; + cur_arg = 2; + return rule; + } else if (strncmp(args[0], "add-acl", 7) == 0) { + /* http-request add-acl() */ + rule->action = ACT_HTTP_ADD_ACL; + /* + * '+ 8' for 'add-acl(' + * '- 9' for 'add-acl(' + trailing ')' + */ + rule->arg.map.ref = my_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)) { + ha_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; + error = NULL; + if (!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, &error)) { + ha_alert("parsing [%s:%d]: 'http-request %s': %s.\n", + file, linenum, args[0], error); + free(error); + goto out_err; + } + 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() */ + rule->action = ACT_HTTP_DEL_ACL; + /* + * '+ 8' for 'del-acl(' + * '- 9' for 'del-acl(' + trailing ')' + */ + rule->arg.map.ref = my_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)) { + ha_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; + error = NULL; + if (!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, &error)) { + ha_alert("parsing [%s:%d]: 'http-request %s': %s.\n", + file, linenum, args[0], error); + free(error); + goto out_err; + } + 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() */ + rule->action = ACT_HTTP_DEL_MAP; + /* + * '+ 8' for 'del-map(' + * '- 9' for 'del-map(' + trailing ')' + */ + rule->arg.map.ref = my_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)) { + ha_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; + error = NULL; + if (!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, &error)) { + ha_alert("parsing [%s:%d]: 'http-request %s': %s.\n", + file, linenum, args[0], error); + free(error); + goto out_err; + } + 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() */ + rule->action = ACT_HTTP_SET_MAP; + /* + * '+ 8' for 'set-map(' + * '- 9' for 'set-map(' + trailing ')' + */ + rule->arg.map.ref = my_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)) { + ha_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 */ + error = NULL; + if (!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, &error)) { + ha_alert("parsing [%s:%d]: 'http-request %s' key: %s.\n", + file, linenum, args[0], error); + free(error); + goto out_err; + } + + /* value pattern */ + error = NULL; + if (!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, &error)) { + ha_alert("parsing [%s:%d]: 'http-request %s' pattern: %s.\n", + file, linenum, args[0], error); + free(error); + goto out_err; + } + 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 if (((custom = action_http_req_custom(args[0])) != NULL)) { + char *errmsg = NULL; + cur_arg = 1; + /* try in the module list */ + rule->from = ACT_F_HTTP_REQ; + rule->kw = custom; + if (custom->parse(args, &cur_arg, proxy, rule, &errmsg) == ACT_RET_PRS_ERR) { + ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-request %s' rule : %s.\n", + file, linenum, proxy_type_str(proxy), proxy->id, args[0], errmsg); + free(errmsg); + goto out_err; + } + } else { + action_build_list(&http_req_keywords.list, &trash); + ha_alert("parsing [%s:%d]: 'http-request' expects 'allow', 'deny', 'auth', 'redirect', " + "'tarpit', 'add-header', 'set-header', 'replace-header', 'replace-value', 'set-nice', " + "'set-tos', 'set-mark', 'set-log-level', 'add-acl', 'del-acl', 'del-map', 'set-map', 'track-sc*'" + "%s%s, but got '%s'%s.\n", + file, linenum, *trash.area ? ", " : "", trash.area, + args[0], *args[0] ? "" : " (missing argument)"); + goto out_err; + } + + if (strcmp(args[cur_arg], "if") == 0 || strcmp(args[cur_arg], "unless") == 0) { + struct acl_cond *cond; + char *errmsg = NULL; + + if ((cond = build_acl_cond(file, linenum, &proxy->acl, proxy, args+cur_arg, &errmsg)) == NULL) { + ha_alert("parsing [%s:%d] : error detected while parsing an 'http-request %s' condition : %s.\n", + file, linenum, args[0], errmsg); + free(errmsg); + goto out_err; + } + rule->cond = cond; + } + else if (*args[cur_arg]) { + ha_alert("parsing [%s:%d]: 'http-request %s' expects 'realm' for 'auth' or" + " either 'if' or 'unless' followed by a condition but found '%s'.\n", + file, linenum, args[0], args[cur_arg]); + goto out_err; + } + + return rule; + out_err: + free(rule); + return NULL; +} + +/* parse an "http-respose" rule */ +struct act_rule *parse_http_res_cond(const char **args, const char *file, int linenum, struct proxy *proxy) +{ + struct act_rule *rule; + struct action_kw *custom = NULL; + int cur_arg; + char *error; + + rule = calloc(1, sizeof(*rule)); + if (!rule) { + ha_alert("parsing [%s:%d]: out of memory.\n", file, linenum); + goto out_err; + } + + if (!strcmp(args[0], "allow")) { + rule->action = ACT_ACTION_ALLOW; + cur_arg = 1; + } else if (!strcmp(args[0], "deny")) { + rule->action = ACT_ACTION_DENY; + cur_arg = 1; + } else if (!strcmp(args[0], "set-nice")) { + rule->action = ACT_HTTP_SET_NICE; + 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)) { + ha_alert("parsing [%s:%d]: 'http-response %s' expects exactly 1 argument (integer value).\n", + file, linenum, args[0]); + goto out_err; + } + rule->arg.nice = atoi(args[cur_arg]); + if (rule->arg.nice < -1024) + rule->arg.nice = -1024; + else if (rule->arg.nice > 1024) + rule->arg.nice = 1024; + cur_arg++; + } else if (!strcmp(args[0], "set-tos")) { +#ifdef IP_TOS + char *err; + rule->action = ACT_HTTP_SET_TOS; + 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)) { + ha_alert("parsing [%s:%d]: 'http-response %s' expects exactly 1 argument (integer/hex value).\n", + file, linenum, args[0]); + goto out_err; + } + + rule->arg.tos = strtol(args[cur_arg], &err, 0); + if (err && *err != '\0') { + ha_alert("parsing [%s:%d]: invalid character starting at '%s' in 'http-response %s' (integer/hex value expected).\n", + file, linenum, err, args[0]); + goto out_err; + } + cur_arg++; +#else + ha_alert("parsing [%s:%d]: 'http-response %s' is not supported on this platform (IP_TOS undefined).\n", file, linenum, args[0]); + goto out_err; +#endif + } else if (!strcmp(args[0], "set-mark")) { +#ifdef SO_MARK + char *err; + rule->action = ACT_HTTP_SET_MARK; + 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)) { + ha_alert("parsing [%s:%d]: 'http-response %s' expects exactly 1 argument (integer/hex value).\n", + file, linenum, args[0]); + goto out_err; + } + + rule->arg.mark = strtoul(args[cur_arg], &err, 0); + if (err && *err != '\0') { + ha_alert("parsing [%s:%d]: invalid character starting at '%s' in 'http-response %s' (integer/hex value expected).\n", + file, linenum, err, args[0]); + goto out_err; + } + cur_arg++; + global.last_checks |= LSTCHK_NETADM; +#else + ha_alert("parsing [%s:%d]: 'http-response %s' is not supported on this platform (SO_MARK undefined).\n", file, linenum, args[0]); + goto out_err; +#endif + } else if (!strcmp(args[0], "set-log-level")) { + rule->action = ACT_HTTP_SET_LOGL; + 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)) { + bad_log_level: + ha_alert("parsing [%s:%d]: 'http-response %s' expects exactly 1 argument (log level name or 'silent').\n", + file, linenum, args[0]); + goto out_err; + } + if (strcmp(args[cur_arg], "silent") == 0) + rule->arg.loglevel = -1; + else if ((rule->arg.loglevel = get_log_level(args[cur_arg]) + 1) == 0) + goto bad_log_level; + cur_arg++; + } else if (strcmp(args[0], "add-header") == 0 || strcmp(args[0], "set-header") == 0) { + rule->action = *args[0] == 'a' ? ACT_HTTP_ADD_HDR : ACT_HTTP_SET_HDR; + 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)) { + ha_alert("parsing [%s:%d]: 'http-response %s' expects exactly 2 arguments.\n", + file, linenum, args[0]); + goto out_err; + } + + rule->arg.hdr_add.name = strdup(args[cur_arg]); + rule->arg.hdr_add.name_len = strlen(rule->arg.hdr_add.name); + LIST_INIT(&rule->arg.hdr_add.fmt); + + proxy->conf.args.ctx = ARGC_HRS; + error = NULL; + if (!parse_logformat_string(args[cur_arg + 1], proxy, &rule->arg.hdr_add.fmt, LOG_OPT_HTTP, + (proxy->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR, &error)) { + ha_alert("parsing [%s:%d]: 'http-response %s': %s.\n", + file, linenum, args[0], error); + free(error); + goto out_err; + } + 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 if (strcmp(args[0], "replace-header") == 0 || strcmp(args[0], "replace-value") == 0) { + rule->action = args[0][8] == 'h' ? ACT_HTTP_REPLACE_HDR : ACT_HTTP_REPLACE_VAL; + cur_arg = 1; + + if (!*args[cur_arg] || !*args[cur_arg+1] || !*args[cur_arg+2] || + (*args[cur_arg+3] && strcmp(args[cur_arg+3], "if") != 0 && strcmp(args[cur_arg+3], "unless") != 0)) { + ha_alert("parsing [%s:%d]: 'http-response %s' expects exactly 3 arguments.\n", + file, linenum, args[0]); + goto out_err; + } + + rule->arg.hdr_add.name = strdup(args[cur_arg]); + rule->arg.hdr_add.name_len = strlen(rule->arg.hdr_add.name); + LIST_INIT(&rule->arg.hdr_add.fmt); + + error = NULL; + if (!regex_comp(args[cur_arg + 1], &rule->arg.hdr_add.re, 1, 1, &error)) { + ha_alert("parsing [%s:%d] : '%s' : %s.\n", file, linenum, + args[cur_arg + 1], error); + free(error); + goto out_err; + } + + proxy->conf.args.ctx = ARGC_HRQ; + error = NULL; + if (!parse_logformat_string(args[cur_arg + 2], proxy, &rule->arg.hdr_add.fmt, LOG_OPT_HTTP, + (proxy->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR, &error)) { + ha_alert("parsing [%s:%d]: 'http-response %s': %s.\n", + file, linenum, args[0], error); + free(error); + goto out_err; + } + + free(proxy->conf.lfs_file); + proxy->conf.lfs_file = strdup(proxy->conf.args.file); + proxy->conf.lfs_line = proxy->conf.args.line; + cur_arg += 3; + } else if (strcmp(args[0], "del-header") == 0) { + rule->action = ACT_HTTP_DEL_HDR; + 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)) { + ha_alert("parsing [%s:%d]: 'http-response %s' expects exactly 1 argument.\n", + file, linenum, args[0]); + goto out_err; + } + + rule->arg.hdr_add.name = strdup(args[cur_arg]); + rule->arg.hdr_add.name_len = strlen(rule->arg.hdr_add.name); + + proxy->conf.args.ctx = ARGC_HRS; + 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], "add-acl", 7) == 0) { + /* http-request add-acl() */ + rule->action = ACT_HTTP_ADD_ACL; + /* + * '+ 8' for 'add-acl(' + * '- 9' for 'add-acl(' + trailing ')' + */ + rule->arg.map.ref = my_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)) { + ha_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; + error = NULL; + if (!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, &error)) { + ha_alert("parsing [%s:%d]: 'http-response %s': %s.\n", + file, linenum, args[0], error); + free(error); + goto out_err; + } + 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() */ + rule->action = ACT_HTTP_DEL_ACL; + /* + * '+ 8' for 'del-acl(' + * '- 9' for 'del-acl(' + trailing ')' + */ + rule->arg.map.ref = my_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)) { + ha_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; + error = NULL; + if (!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, &error)) { + ha_alert("parsing [%s:%d]: 'http-response %s': %s.\n", + file, linenum, args[0], error); + free(error); + goto out_err; + } + 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() */ + rule->action = ACT_HTTP_DEL_MAP; + /* + * '+ 8' for 'del-map(' + * '- 9' for 'del-map(' + trailing ')' + */ + rule->arg.map.ref = my_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)) { + ha_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; + error = NULL; + if (!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, &error)) { + ha_alert("parsing [%s:%d]: 'http-response %s' %s.\n", + file, linenum, args[0], error); + free(error); + goto out_err; + } + 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() */ + rule->action = ACT_HTTP_SET_MAP; + /* + * '+ 8' for 'set-map(' + * '- 9' for 'set-map(' + trailing ')' + */ + rule->arg.map.ref = my_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)) { + ha_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 */ + error = NULL; + if (!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, &error)) { + ha_alert("parsing [%s:%d]: 'http-response %s' name: %s.\n", + file, linenum, args[0], error); + free(error); + goto out_err; + } + + /* value pattern */ + error = NULL; + if (!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, &error)) { + ha_alert("parsing [%s:%d]: 'http-response %s' value: %s.\n", + file, linenum, args[0], error); + free(error); + goto out_err; + } + + 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 if (strcmp(args[0], "redirect") == 0) { + struct redirect_rule *redir; + char *errmsg = NULL; + + if ((redir = http_parse_redirect_rule(file, linenum, proxy, (const char **)args + 1, &errmsg, 1, 1)) == NULL) { + ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-response %s' rule : %s.\n", + file, linenum, proxy_type_str(proxy), proxy->id, args[0], errmsg); + goto out_err; + } + + /* this redirect rule might already contain a parsed condition which + * we'll pass to the http-request rule. + */ + rule->action = ACT_HTTP_REDIR; + rule->arg.redir = redir; + rule->cond = redir->cond; + redir->cond = NULL; + cur_arg = 2; + return rule; + } else if (strncmp(args[0], "track-sc", 8) == 0) { + struct sample_expr *expr; + unsigned int where; + char *err = NULL; + unsigned int tsc_num; + const char *tsc_num_str; + + cur_arg = 1; + proxy->conf.args.ctx = ARGC_TRK; + + tsc_num_str = &args[0][8]; + if (cfg_parse_track_sc_num(&tsc_num, tsc_num_str, tsc_num_str + strlen(tsc_num_str), &err) == -1) { + ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-response %s' rule : %s.\n", + file, linenum, proxy_type_str(proxy), proxy->id, args[0], err); + free(err); + goto out_err; + } + + expr = sample_parse_expr((char **)args, &cur_arg, file, linenum, &err, &proxy->conf.args); + if (!expr) { + ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-response %s' rule : %s.\n", + file, linenum, proxy_type_str(proxy), proxy->id, args[0], err); + free(err); + goto out_err; + } + + where = 0; + if (proxy->cap & PR_CAP_FE) + where |= SMP_VAL_FE_HRS_HDR; + if (proxy->cap & PR_CAP_BE) + where |= SMP_VAL_BE_HRS_HDR; + + if (!(expr->fetch->val & where)) { + ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-response %s' rule :" + " fetch method '%s' extracts information from '%s', none of which is available here.\n", + file, linenum, proxy_type_str(proxy), proxy->id, args[0], + args[cur_arg-1], sample_src_names(expr->fetch->use)); + free(expr); + goto out_err; + } + + if (strcmp(args[cur_arg], "table") == 0) { + cur_arg++; + if (!args[cur_arg]) { + ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-response %s' rule : missing table name.\n", + file, linenum, proxy_type_str(proxy), proxy->id, args[0]); + free(expr); + goto out_err; + } + /* we copy the table name for now, it will be resolved later */ + rule->arg.trk_ctr.table.n = strdup(args[cur_arg]); + cur_arg++; + } + rule->arg.trk_ctr.expr = expr; + rule->action = ACT_ACTION_TRK_SC0 + tsc_num; + rule->check_ptr = check_trk_action; + } else if (((custom = action_http_res_custom(args[0])) != NULL)) { + char *errmsg = NULL; + cur_arg = 1; + /* try in the module list */ + rule->from = ACT_F_HTTP_RES; + rule->kw = custom; + if (custom->parse(args, &cur_arg, proxy, rule, &errmsg) == ACT_RET_PRS_ERR) { + ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-response %s' rule : %s.\n", + file, linenum, proxy_type_str(proxy), proxy->id, args[0], errmsg); + free(errmsg); + goto out_err; + } + } else { + action_build_list(&http_res_keywords.list, &trash); + ha_alert("parsing [%s:%d]: 'http-response' expects 'allow', 'deny', 'redirect', " + "'add-header', 'del-header', 'set-header', 'replace-header', 'replace-value', 'set-nice', " + "'set-tos', 'set-mark', 'set-log-level', 'add-acl', 'del-acl', 'del-map', 'set-map', 'track-sc*'" + "%s%s, but got '%s'%s.\n", + file, linenum, *trash.area ? ", " : "", trash.area, + args[0], *args[0] ? "" : " (missing argument)"); + goto out_err; + } + + if (strcmp(args[cur_arg], "if") == 0 || strcmp(args[cur_arg], "unless") == 0) { + struct acl_cond *cond; + char *errmsg = NULL; + + if ((cond = build_acl_cond(file, linenum, &proxy->acl, proxy, args+cur_arg, &errmsg)) == NULL) { + ha_alert("parsing [%s:%d] : error detected while parsing an 'http-response %s' condition : %s.\n", + file, linenum, args[0], errmsg); + free(errmsg); + goto out_err; + } + rule->cond = cond; + } + else if (*args[cur_arg]) { + ha_alert("parsing [%s:%d]: 'http-response %s' expects" + " either 'if' or 'unless' followed by a condition but found '%s'.\n", + file, linenum, args[0], args[cur_arg]); + goto out_err; + } + + return rule; + out_err: + free(rule); + return NULL; +} + +/* Parses a redirect rule. Returns the redirect rule on success or NULL on error, + * with filled with the error message. If is not null, builds a + * dynamic log-format rule instead of a static string. Parameter indicates + * the direction of the rule, and equals 0 for request, non-zero for responses. + */ +struct redirect_rule *http_parse_redirect_rule(const char *file, int linenum, struct proxy *curproxy, + const char **args, char **errmsg, int use_fmt, int dir) +{ + struct redirect_rule *rule; + int cur_arg; + int type = REDIRECT_TYPE_NONE; + int code = 302; + const char *destination = NULL; + const char *cookie = NULL; + int cookie_set = 0; + unsigned int flags = REDIRECT_FLAG_NONE; + struct acl_cond *cond = NULL; + + cur_arg = 0; + while (*(args[cur_arg])) { + if (strcmp(args[cur_arg], "location") == 0) { + if (!*args[cur_arg + 1]) + goto missing_arg; + + type = REDIRECT_TYPE_LOCATION; + cur_arg++; + destination = args[cur_arg]; + } + else if (strcmp(args[cur_arg], "prefix") == 0) { + if (!*args[cur_arg + 1]) + goto missing_arg; + type = REDIRECT_TYPE_PREFIX; + cur_arg++; + destination = args[cur_arg]; + } + else if (strcmp(args[cur_arg], "scheme") == 0) { + if (!*args[cur_arg + 1]) + goto missing_arg; + + type = REDIRECT_TYPE_SCHEME; + cur_arg++; + destination = args[cur_arg]; + } + else if (strcmp(args[cur_arg], "set-cookie") == 0) { + if (!*args[cur_arg + 1]) + goto missing_arg; + + cur_arg++; + cookie = args[cur_arg]; + cookie_set = 1; + } + else if (strcmp(args[cur_arg], "clear-cookie") == 0) { + if (!*args[cur_arg + 1]) + goto missing_arg; + + cur_arg++; + cookie = args[cur_arg]; + cookie_set = 0; + } + else if (strcmp(args[cur_arg], "code") == 0) { + if (!*args[cur_arg + 1]) + goto missing_arg; + + cur_arg++; + code = atol(args[cur_arg]); + if (code < 301 || code > 308 || (code > 303 && code < 307)) { + memprintf(errmsg, + "'%s': unsupported HTTP code '%s' (must be one of 301, 302, 303, 307 or 308)", + args[cur_arg - 1], args[cur_arg]); + return NULL; + } + } + else if (!strcmp(args[cur_arg],"drop-query")) { + flags |= REDIRECT_FLAG_DROP_QS; + } + else if (!strcmp(args[cur_arg],"append-slash")) { + flags |= REDIRECT_FLAG_APPEND_SLASH; + } + else if (strcmp(args[cur_arg], "if") == 0 || + strcmp(args[cur_arg], "unless") == 0) { + cond = build_acl_cond(file, linenum, &curproxy->acl, curproxy, (const char **)args + cur_arg, errmsg); + if (!cond) { + memprintf(errmsg, "error in condition: %s", *errmsg); + return NULL; + } + break; + } + else { + memprintf(errmsg, + "expects 'code', 'prefix', 'location', 'scheme', 'set-cookie', 'clear-cookie', 'drop-query' or 'append-slash' (was '%s')", + args[cur_arg]); + return NULL; + } + cur_arg++; + } + + if (type == REDIRECT_TYPE_NONE) { + memprintf(errmsg, "redirection type expected ('prefix', 'location', or 'scheme')"); + return NULL; + } + + if (dir && type != REDIRECT_TYPE_LOCATION) { + memprintf(errmsg, "response only supports redirect type 'location'"); + return NULL; + } + + rule = calloc(1, sizeof(*rule)); + rule->cond = cond; + LIST_INIT(&rule->rdr_fmt); + + if (!use_fmt) { + /* old-style static redirect rule */ + rule->rdr_str = strdup(destination); + rule->rdr_len = strlen(destination); + } + else { + /* log-format based redirect rule */ + + /* Parse destination. Note that in the REDIRECT_TYPE_PREFIX case, + * if prefix == "/", we don't want to add anything, otherwise it + * makes it hard for the user to configure a self-redirection. + */ + curproxy->conf.args.ctx = ARGC_RDR; + if (!(type == REDIRECT_TYPE_PREFIX && destination[0] == '/' && destination[1] == '\0')) { + if (!parse_logformat_string(destination, curproxy, &rule->rdr_fmt, LOG_OPT_HTTP, + dir ? (curproxy->cap & PR_CAP_FE) ? SMP_VAL_FE_HRS_HDR : SMP_VAL_BE_HRS_HDR + : (curproxy->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR, + errmsg)) { + return NULL; + } + free(curproxy->conf.lfs_file); + curproxy->conf.lfs_file = strdup(curproxy->conf.args.file); + curproxy->conf.lfs_line = curproxy->conf.args.line; + } + } + + if (cookie) { + /* depending on cookie_set, either we want to set the cookie, or to clear it. + * a clear consists in appending "; path=/; Max-Age=0;" at the end. + */ + rule->cookie_len = strlen(cookie); + if (cookie_set) { + rule->cookie_str = malloc(rule->cookie_len + 10); + memcpy(rule->cookie_str, cookie, rule->cookie_len); + memcpy(rule->cookie_str + rule->cookie_len, "; path=/;", 10); + rule->cookie_len += 9; + } else { + rule->cookie_str = malloc(rule->cookie_len + 21); + memcpy(rule->cookie_str, cookie, rule->cookie_len); + memcpy(rule->cookie_str + rule->cookie_len, "; path=/; Max-Age=0;", 21); + rule->cookie_len += 20; + } + } + rule->type = type; + rule->code = code; + rule->flags = flags; + LIST_INIT(&rule->list); + return rule; + + missing_arg: + memprintf(errmsg, "missing argument for '%s'", args[cur_arg]); + return NULL; +} + +void free_http_res_rules(struct list *r) +{ + struct act_rule *tr, *pr; + + list_for_each_entry_safe(pr, tr, r, list) { + LIST_DEL(&pr->list); + regex_free(&pr->arg.hdr_add.re); + free(pr); + } +} + +void free_http_req_rules(struct list *r) +{ + struct act_rule *tr, *pr; + + list_for_each_entry_safe(pr, tr, r, list) { + LIST_DEL(&pr->list); + if (pr->action == ACT_HTTP_REQ_AUTH) + free(pr->arg.auth.realm); + + regex_free(&pr->arg.hdr_add.re); + free(pr); + } +} + +__attribute__((constructor)) +static void __http_rules_init(void) +{ +} + +/* + * Local variables: + * c-indent-level: 8 + * c-basic-offset: 8 + * End: + */ diff --git a/src/proto_http.c b/src/proto_http.c index 94e672d11..ec5140981 100644 --- a/src/proto_http.c +++ b/src/proto_http.c @@ -88,16 +88,6 @@ const char *stat_status_codes[STAT_STATUS_SIZE] = { }; -/* List head of all known action keywords for "http-request" */ -struct action_kw_list http_req_keywords = { - .list = LIST_HEAD_INIT(http_req_keywords.list) -}; - -/* List head of all known action keywords for "http-response" */ -struct action_kw_list http_res_keywords = { - .list = LIST_HEAD_INIT(http_res_keywords.list) -}; - static int http_apply_redirect_rule(struct redirect_rule *rule, struct stream *s, struct http_txn *txn); static inline int http_msg_forward_body(struct stream *s, struct http_msg *msg); @@ -7666,1142 +7656,6 @@ void http_reset_txn(struct stream *s) s->si[1].hcto = TICK_ETERNITY; } -void free_http_res_rules(struct list *r) -{ - struct act_rule *tr, *pr; - - list_for_each_entry_safe(pr, tr, r, list) { - LIST_DEL(&pr->list); - regex_free(&pr->arg.hdr_add.re); - free(pr); - } -} - -void free_http_req_rules(struct list *r) -{ - struct act_rule *tr, *pr; - - list_for_each_entry_safe(pr, tr, r, list) { - LIST_DEL(&pr->list); - if (pr->action == ACT_HTTP_REQ_AUTH) - free(pr->arg.auth.realm); - - regex_free(&pr->arg.hdr_add.re); - free(pr); - } -} - -/* parse an "http-request" rule */ -struct act_rule *parse_http_req_cond(const char **args, const char *file, int linenum, struct proxy *proxy) -{ - struct act_rule *rule; - struct action_kw *custom = NULL; - int cur_arg; - char *error; - - rule = calloc(1, sizeof(*rule)); - if (!rule) { - ha_alert("parsing [%s:%d]: out of memory.\n", file, linenum); - goto out_err; - } - - if (!strcmp(args[0], "allow")) { - rule->action = ACT_ACTION_ALLOW; - cur_arg = 1; - } else if (!strcmp(args[0], "deny") || !strcmp(args[0], "block") || !strcmp(args[0], "tarpit")) { - int code; - int hc; - - if (!strcmp(args[0], "tarpit")) { - rule->action = ACT_HTTP_REQ_TARPIT; - rule->deny_status = HTTP_ERR_500; - } - else { - rule->action = ACT_ACTION_DENY; - rule->deny_status = HTTP_ERR_403; - } - cur_arg = 1; - if (strcmp(args[cur_arg], "deny_status") == 0) { - cur_arg++; - if (!args[cur_arg]) { - ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-request %s' rule : missing status code.\n", - file, linenum, proxy_type_str(proxy), proxy->id, args[0]); - goto out_err; - } - - code = atol(args[cur_arg]); - cur_arg++; - for (hc = 0; hc < HTTP_ERR_SIZE; hc++) { - if (http_err_codes[hc] == code) { - rule->deny_status = hc; - break; - } - } - - if (hc >= HTTP_ERR_SIZE) { - ha_warning("parsing [%s:%d] : status code %d not handled, using default code %d.\n", - file, linenum, code, http_err_codes[rule->deny_status]); - } - } - } else if (!strcmp(args[0], "auth")) { - rule->action = ACT_HTTP_REQ_AUTH; - cur_arg = 1; - - while(*args[cur_arg]) { - if (!strcmp(args[cur_arg], "realm")) { - rule->arg.auth.realm = strdup(args[cur_arg + 1]); - cur_arg+=2; - continue; - } else - break; - } - } else if (!strcmp(args[0], "set-nice")) { - rule->action = ACT_HTTP_SET_NICE; - 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)) { - ha_alert("parsing [%s:%d]: 'http-request %s' expects exactly 1 argument (integer value).\n", - file, linenum, args[0]); - goto out_err; - } - rule->arg.nice = atoi(args[cur_arg]); - if (rule->arg.nice < -1024) - rule->arg.nice = -1024; - else if (rule->arg.nice > 1024) - rule->arg.nice = 1024; - cur_arg++; - } else if (!strcmp(args[0], "set-tos")) { -#ifdef IP_TOS - char *err; - rule->action = ACT_HTTP_SET_TOS; - 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)) { - ha_alert("parsing [%s:%d]: 'http-request %s' expects exactly 1 argument (integer/hex value).\n", - file, linenum, args[0]); - goto out_err; - } - - rule->arg.tos = strtol(args[cur_arg], &err, 0); - if (err && *err != '\0') { - ha_alert("parsing [%s:%d]: invalid character starting at '%s' in 'http-request %s' (integer/hex value expected).\n", - file, linenum, err, args[0]); - goto out_err; - } - cur_arg++; -#else - ha_alert("parsing [%s:%d]: 'http-request %s' is not supported on this platform (IP_TOS undefined).\n", file, linenum, args[0]); - goto out_err; -#endif - } else if (!strcmp(args[0], "set-mark")) { -#ifdef SO_MARK - char *err; - rule->action = ACT_HTTP_SET_MARK; - 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)) { - ha_alert("parsing [%s:%d]: 'http-request %s' expects exactly 1 argument (integer/hex value).\n", - file, linenum, args[0]); - goto out_err; - } - - rule->arg.mark = strtoul(args[cur_arg], &err, 0); - if (err && *err != '\0') { - ha_alert("parsing [%s:%d]: invalid character starting at '%s' in 'http-request %s' (integer/hex value expected).\n", - file, linenum, err, args[0]); - goto out_err; - } - cur_arg++; - global.last_checks |= LSTCHK_NETADM; -#else - ha_alert("parsing [%s:%d]: 'http-request %s' is not supported on this platform (SO_MARK undefined).\n", file, linenum, args[0]); - goto out_err; -#endif - } else if (!strcmp(args[0], "set-log-level")) { - rule->action = ACT_HTTP_SET_LOGL; - 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)) { - bad_log_level: - ha_alert("parsing [%s:%d]: 'http-request %s' expects exactly 1 argument (log level name or 'silent').\n", - file, linenum, args[0]); - goto out_err; - } - if (strcmp(args[cur_arg], "silent") == 0) - rule->arg.loglevel = -1; - else if ((rule->arg.loglevel = get_log_level(args[cur_arg]) + 1) == 0) - goto bad_log_level; - cur_arg++; - } else if (strcmp(args[0], "add-header") == 0 || strcmp(args[0], "set-header") == 0) { - rule->action = *args[0] == 'a' ? ACT_HTTP_ADD_HDR : ACT_HTTP_SET_HDR; - 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)) { - ha_alert("parsing [%s:%d]: 'http-request %s' expects exactly 2 arguments.\n", - file, linenum, args[0]); - goto out_err; - } - - rule->arg.hdr_add.name = strdup(args[cur_arg]); - rule->arg.hdr_add.name_len = strlen(rule->arg.hdr_add.name); - LIST_INIT(&rule->arg.hdr_add.fmt); - - proxy->conf.args.ctx = ARGC_HRQ; - error = NULL; - if (!parse_logformat_string(args[cur_arg + 1], proxy, &rule->arg.hdr_add.fmt, LOG_OPT_HTTP, - (proxy->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR, &error)) { - ha_alert("parsing [%s:%d]: 'http-request %s': %s.\n", - file, linenum, args[0], error); - free(error); - goto out_err; - } - 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 if (strcmp(args[0], "replace-header") == 0 || strcmp(args[0], "replace-value") == 0) { - rule->action = args[0][8] == 'h' ? ACT_HTTP_REPLACE_HDR : ACT_HTTP_REPLACE_VAL; - cur_arg = 1; - - if (!*args[cur_arg] || !*args[cur_arg+1] || !*args[cur_arg+2] || - (*args[cur_arg+3] && strcmp(args[cur_arg+3], "if") != 0 && strcmp(args[cur_arg+3], "unless") != 0)) { - ha_alert("parsing [%s:%d]: 'http-request %s' expects exactly 3 arguments.\n", - file, linenum, args[0]); - goto out_err; - } - - rule->arg.hdr_add.name = strdup(args[cur_arg]); - rule->arg.hdr_add.name_len = strlen(rule->arg.hdr_add.name); - LIST_INIT(&rule->arg.hdr_add.fmt); - - error = NULL; - if (!regex_comp(args[cur_arg + 1], &rule->arg.hdr_add.re, 1, 1, &error)) { - ha_alert("parsing [%s:%d] : '%s' : %s.\n", file, linenum, - args[cur_arg + 1], error); - free(error); - goto out_err; - } - - proxy->conf.args.ctx = ARGC_HRQ; - error = NULL; - if (!parse_logformat_string(args[cur_arg + 2], proxy, &rule->arg.hdr_add.fmt, LOG_OPT_HTTP, - (proxy->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR, &error)) { - ha_alert("parsing [%s:%d]: 'http-request %s': %s.\n", - file, linenum, args[0], error); - free(error); - goto out_err; - } - - free(proxy->conf.lfs_file); - proxy->conf.lfs_file = strdup(proxy->conf.args.file); - proxy->conf.lfs_line = proxy->conf.args.line; - cur_arg += 3; - } else if (strcmp(args[0], "del-header") == 0) { - rule->action = ACT_HTTP_DEL_HDR; - 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)) { - ha_alert("parsing [%s:%d]: 'http-request %s' expects exactly 1 argument.\n", - file, linenum, args[0]); - goto out_err; - } - - rule->arg.hdr_add.name = strdup(args[cur_arg]); - rule->arg.hdr_add.name_len = strlen(rule->arg.hdr_add.name); - - proxy->conf.args.ctx = ARGC_HRQ; - 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], "track-sc", 8) == 0) { - struct sample_expr *expr; - unsigned int where; - char *err = NULL; - unsigned int tsc_num; - const char *tsc_num_str; - - cur_arg = 1; - proxy->conf.args.ctx = ARGC_TRK; - - tsc_num_str = &args[0][8]; - if (cfg_parse_track_sc_num(&tsc_num, tsc_num_str, tsc_num_str + strlen(tsc_num_str), &err) == -1) { - ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-request %s' rule : %s.\n", - file, linenum, proxy_type_str(proxy), proxy->id, args[0], err); - free(err); - goto out_err; - } - - expr = sample_parse_expr((char **)args, &cur_arg, file, linenum, &err, &proxy->conf.args); - if (!expr) { - ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-request %s' rule : %s.\n", - file, linenum, proxy_type_str(proxy), proxy->id, args[0], err); - free(err); - goto out_err; - } - - where = 0; - if (proxy->cap & PR_CAP_FE) - where |= SMP_VAL_FE_HRQ_HDR; - if (proxy->cap & PR_CAP_BE) - where |= SMP_VAL_BE_HRQ_HDR; - - if (!(expr->fetch->val & where)) { - ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-request %s' rule :" - " fetch method '%s' extracts information from '%s', none of which is available here.\n", - file, linenum, proxy_type_str(proxy), proxy->id, args[0], - args[cur_arg-1], sample_src_names(expr->fetch->use)); - free(expr); - goto out_err; - } - - if (strcmp(args[cur_arg], "table") == 0) { - cur_arg++; - if (!args[cur_arg]) { - ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-request %s' rule : missing table name.\n", - file, linenum, proxy_type_str(proxy), proxy->id, args[0]); - free(expr); - goto out_err; - } - /* we copy the table name for now, it will be resolved later */ - rule->arg.trk_ctr.table.n = strdup(args[cur_arg]); - cur_arg++; - } - rule->arg.trk_ctr.expr = expr; - rule->action = ACT_ACTION_TRK_SC0 + tsc_num; - rule->check_ptr = check_trk_action; - } else if (strcmp(args[0], "redirect") == 0) { - struct redirect_rule *redir; - char *errmsg = NULL; - - if ((redir = http_parse_redirect_rule(file, linenum, proxy, (const char **)args + 1, &errmsg, 1, 0)) == NULL) { - ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-request %s' rule : %s.\n", - file, linenum, proxy_type_str(proxy), proxy->id, args[0], errmsg); - goto out_err; - } - - /* this redirect rule might already contain a parsed condition which - * we'll pass to the http-request rule. - */ - rule->action = ACT_HTTP_REDIR; - rule->arg.redir = redir; - rule->cond = redir->cond; - redir->cond = NULL; - cur_arg = 2; - return rule; - } else if (strncmp(args[0], "add-acl", 7) == 0) { - /* http-request add-acl() */ - rule->action = ACT_HTTP_ADD_ACL; - /* - * '+ 8' for 'add-acl(' - * '- 9' for 'add-acl(' + trailing ')' - */ - rule->arg.map.ref = my_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)) { - ha_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; - error = NULL; - if (!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, &error)) { - ha_alert("parsing [%s:%d]: 'http-request %s': %s.\n", - file, linenum, args[0], error); - free(error); - goto out_err; - } - 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() */ - rule->action = ACT_HTTP_DEL_ACL; - /* - * '+ 8' for 'del-acl(' - * '- 9' for 'del-acl(' + trailing ')' - */ - rule->arg.map.ref = my_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)) { - ha_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; - error = NULL; - if (!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, &error)) { - ha_alert("parsing [%s:%d]: 'http-request %s': %s.\n", - file, linenum, args[0], error); - free(error); - goto out_err; - } - 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() */ - rule->action = ACT_HTTP_DEL_MAP; - /* - * '+ 8' for 'del-map(' - * '- 9' for 'del-map(' + trailing ')' - */ - rule->arg.map.ref = my_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)) { - ha_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; - error = NULL; - if (!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, &error)) { - ha_alert("parsing [%s:%d]: 'http-request %s': %s.\n", - file, linenum, args[0], error); - free(error); - goto out_err; - } - 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() */ - rule->action = ACT_HTTP_SET_MAP; - /* - * '+ 8' for 'set-map(' - * '- 9' for 'set-map(' + trailing ')' - */ - rule->arg.map.ref = my_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)) { - ha_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 */ - error = NULL; - if (!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, &error)) { - ha_alert("parsing [%s:%d]: 'http-request %s' key: %s.\n", - file, linenum, args[0], error); - free(error); - goto out_err; - } - - /* value pattern */ - error = NULL; - if (!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, &error)) { - ha_alert("parsing [%s:%d]: 'http-request %s' pattern: %s.\n", - file, linenum, args[0], error); - free(error); - goto out_err; - } - 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 if (((custom = action_http_req_custom(args[0])) != NULL)) { - char *errmsg = NULL; - cur_arg = 1; - /* try in the module list */ - rule->from = ACT_F_HTTP_REQ; - rule->kw = custom; - if (custom->parse(args, &cur_arg, proxy, rule, &errmsg) == ACT_RET_PRS_ERR) { - ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-request %s' rule : %s.\n", - file, linenum, proxy_type_str(proxy), proxy->id, args[0], errmsg); - free(errmsg); - goto out_err; - } - } else { - action_build_list(&http_req_keywords.list, &trash); - ha_alert("parsing [%s:%d]: 'http-request' expects 'allow', 'deny', 'auth', 'redirect', " - "'tarpit', 'add-header', 'set-header', 'replace-header', 'replace-value', 'set-nice', " - "'set-tos', 'set-mark', 'set-log-level', 'add-acl', 'del-acl', 'del-map', 'set-map', 'track-sc*'" - "%s%s, but got '%s'%s.\n", - file, linenum, *trash.area ? ", " : "", trash.area, - args[0], *args[0] ? "" : " (missing argument)"); - goto out_err; - } - - if (strcmp(args[cur_arg], "if") == 0 || strcmp(args[cur_arg], "unless") == 0) { - struct acl_cond *cond; - char *errmsg = NULL; - - if ((cond = build_acl_cond(file, linenum, &proxy->acl, proxy, args+cur_arg, &errmsg)) == NULL) { - ha_alert("parsing [%s:%d] : error detected while parsing an 'http-request %s' condition : %s.\n", - file, linenum, args[0], errmsg); - free(errmsg); - goto out_err; - } - rule->cond = cond; - } - else if (*args[cur_arg]) { - ha_alert("parsing [%s:%d]: 'http-request %s' expects 'realm' for 'auth' or" - " either 'if' or 'unless' followed by a condition but found '%s'.\n", - file, linenum, args[0], args[cur_arg]); - goto out_err; - } - - return rule; - out_err: - free(rule); - return NULL; -} - -/* parse an "http-respose" rule */ -struct act_rule *parse_http_res_cond(const char **args, const char *file, int linenum, struct proxy *proxy) -{ - struct act_rule *rule; - struct action_kw *custom = NULL; - int cur_arg; - char *error; - - rule = calloc(1, sizeof(*rule)); - if (!rule) { - ha_alert("parsing [%s:%d]: out of memory.\n", file, linenum); - goto out_err; - } - - if (!strcmp(args[0], "allow")) { - rule->action = ACT_ACTION_ALLOW; - cur_arg = 1; - } else if (!strcmp(args[0], "deny")) { - rule->action = ACT_ACTION_DENY; - cur_arg = 1; - } else if (!strcmp(args[0], "set-nice")) { - rule->action = ACT_HTTP_SET_NICE; - 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)) { - ha_alert("parsing [%s:%d]: 'http-response %s' expects exactly 1 argument (integer value).\n", - file, linenum, args[0]); - goto out_err; - } - rule->arg.nice = atoi(args[cur_arg]); - if (rule->arg.nice < -1024) - rule->arg.nice = -1024; - else if (rule->arg.nice > 1024) - rule->arg.nice = 1024; - cur_arg++; - } else if (!strcmp(args[0], "set-tos")) { -#ifdef IP_TOS - char *err; - rule->action = ACT_HTTP_SET_TOS; - 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)) { - ha_alert("parsing [%s:%d]: 'http-response %s' expects exactly 1 argument (integer/hex value).\n", - file, linenum, args[0]); - goto out_err; - } - - rule->arg.tos = strtol(args[cur_arg], &err, 0); - if (err && *err != '\0') { - ha_alert("parsing [%s:%d]: invalid character starting at '%s' in 'http-response %s' (integer/hex value expected).\n", - file, linenum, err, args[0]); - goto out_err; - } - cur_arg++; -#else - ha_alert("parsing [%s:%d]: 'http-response %s' is not supported on this platform (IP_TOS undefined).\n", file, linenum, args[0]); - goto out_err; -#endif - } else if (!strcmp(args[0], "set-mark")) { -#ifdef SO_MARK - char *err; - rule->action = ACT_HTTP_SET_MARK; - 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)) { - ha_alert("parsing [%s:%d]: 'http-response %s' expects exactly 1 argument (integer/hex value).\n", - file, linenum, args[0]); - goto out_err; - } - - rule->arg.mark = strtoul(args[cur_arg], &err, 0); - if (err && *err != '\0') { - ha_alert("parsing [%s:%d]: invalid character starting at '%s' in 'http-response %s' (integer/hex value expected).\n", - file, linenum, err, args[0]); - goto out_err; - } - cur_arg++; - global.last_checks |= LSTCHK_NETADM; -#else - ha_alert("parsing [%s:%d]: 'http-response %s' is not supported on this platform (SO_MARK undefined).\n", file, linenum, args[0]); - goto out_err; -#endif - } else if (!strcmp(args[0], "set-log-level")) { - rule->action = ACT_HTTP_SET_LOGL; - 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)) { - bad_log_level: - ha_alert("parsing [%s:%d]: 'http-response %s' expects exactly 1 argument (log level name or 'silent').\n", - file, linenum, args[0]); - goto out_err; - } - if (strcmp(args[cur_arg], "silent") == 0) - rule->arg.loglevel = -1; - else if ((rule->arg.loglevel = get_log_level(args[cur_arg]) + 1) == 0) - goto bad_log_level; - cur_arg++; - } else if (strcmp(args[0], "add-header") == 0 || strcmp(args[0], "set-header") == 0) { - rule->action = *args[0] == 'a' ? ACT_HTTP_ADD_HDR : ACT_HTTP_SET_HDR; - 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)) { - ha_alert("parsing [%s:%d]: 'http-response %s' expects exactly 2 arguments.\n", - file, linenum, args[0]); - goto out_err; - } - - rule->arg.hdr_add.name = strdup(args[cur_arg]); - rule->arg.hdr_add.name_len = strlen(rule->arg.hdr_add.name); - LIST_INIT(&rule->arg.hdr_add.fmt); - - proxy->conf.args.ctx = ARGC_HRS; - error = NULL; - if (!parse_logformat_string(args[cur_arg + 1], proxy, &rule->arg.hdr_add.fmt, LOG_OPT_HTTP, - (proxy->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR, &error)) { - ha_alert("parsing [%s:%d]: 'http-response %s': %s.\n", - file, linenum, args[0], error); - free(error); - goto out_err; - } - 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 if (strcmp(args[0], "replace-header") == 0 || strcmp(args[0], "replace-value") == 0) { - rule->action = args[0][8] == 'h' ? ACT_HTTP_REPLACE_HDR : ACT_HTTP_REPLACE_VAL; - cur_arg = 1; - - if (!*args[cur_arg] || !*args[cur_arg+1] || !*args[cur_arg+2] || - (*args[cur_arg+3] && strcmp(args[cur_arg+3], "if") != 0 && strcmp(args[cur_arg+3], "unless") != 0)) { - ha_alert("parsing [%s:%d]: 'http-response %s' expects exactly 3 arguments.\n", - file, linenum, args[0]); - goto out_err; - } - - rule->arg.hdr_add.name = strdup(args[cur_arg]); - rule->arg.hdr_add.name_len = strlen(rule->arg.hdr_add.name); - LIST_INIT(&rule->arg.hdr_add.fmt); - - error = NULL; - if (!regex_comp(args[cur_arg + 1], &rule->arg.hdr_add.re, 1, 1, &error)) { - ha_alert("parsing [%s:%d] : '%s' : %s.\n", file, linenum, - args[cur_arg + 1], error); - free(error); - goto out_err; - } - - proxy->conf.args.ctx = ARGC_HRQ; - error = NULL; - if (!parse_logformat_string(args[cur_arg + 2], proxy, &rule->arg.hdr_add.fmt, LOG_OPT_HTTP, - (proxy->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR, &error)) { - ha_alert("parsing [%s:%d]: 'http-response %s': %s.\n", - file, linenum, args[0], error); - free(error); - goto out_err; - } - - free(proxy->conf.lfs_file); - proxy->conf.lfs_file = strdup(proxy->conf.args.file); - proxy->conf.lfs_line = proxy->conf.args.line; - cur_arg += 3; - } else if (strcmp(args[0], "del-header") == 0) { - rule->action = ACT_HTTP_DEL_HDR; - 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)) { - ha_alert("parsing [%s:%d]: 'http-response %s' expects exactly 1 argument.\n", - file, linenum, args[0]); - goto out_err; - } - - rule->arg.hdr_add.name = strdup(args[cur_arg]); - rule->arg.hdr_add.name_len = strlen(rule->arg.hdr_add.name); - - proxy->conf.args.ctx = ARGC_HRS; - 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], "add-acl", 7) == 0) { - /* http-request add-acl() */ - rule->action = ACT_HTTP_ADD_ACL; - /* - * '+ 8' for 'add-acl(' - * '- 9' for 'add-acl(' + trailing ')' - */ - rule->arg.map.ref = my_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)) { - ha_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; - error = NULL; - if (!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, &error)) { - ha_alert("parsing [%s:%d]: 'http-response %s': %s.\n", - file, linenum, args[0], error); - free(error); - goto out_err; - } - 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() */ - rule->action = ACT_HTTP_DEL_ACL; - /* - * '+ 8' for 'del-acl(' - * '- 9' for 'del-acl(' + trailing ')' - */ - rule->arg.map.ref = my_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)) { - ha_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; - error = NULL; - if (!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, &error)) { - ha_alert("parsing [%s:%d]: 'http-response %s': %s.\n", - file, linenum, args[0], error); - free(error); - goto out_err; - } - 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() */ - rule->action = ACT_HTTP_DEL_MAP; - /* - * '+ 8' for 'del-map(' - * '- 9' for 'del-map(' + trailing ')' - */ - rule->arg.map.ref = my_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)) { - ha_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; - error = NULL; - if (!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, &error)) { - ha_alert("parsing [%s:%d]: 'http-response %s' %s.\n", - file, linenum, args[0], error); - free(error); - goto out_err; - } - 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() */ - rule->action = ACT_HTTP_SET_MAP; - /* - * '+ 8' for 'set-map(' - * '- 9' for 'set-map(' + trailing ')' - */ - rule->arg.map.ref = my_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)) { - ha_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 */ - error = NULL; - if (!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, &error)) { - ha_alert("parsing [%s:%d]: 'http-response %s' name: %s.\n", - file, linenum, args[0], error); - free(error); - goto out_err; - } - - /* value pattern */ - error = NULL; - if (!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, &error)) { - ha_alert("parsing [%s:%d]: 'http-response %s' value: %s.\n", - file, linenum, args[0], error); - free(error); - goto out_err; - } - - 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 if (strcmp(args[0], "redirect") == 0) { - struct redirect_rule *redir; - char *errmsg = NULL; - - if ((redir = http_parse_redirect_rule(file, linenum, proxy, (const char **)args + 1, &errmsg, 1, 1)) == NULL) { - ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-response %s' rule : %s.\n", - file, linenum, proxy_type_str(proxy), proxy->id, args[0], errmsg); - goto out_err; - } - - /* this redirect rule might already contain a parsed condition which - * we'll pass to the http-request rule. - */ - rule->action = ACT_HTTP_REDIR; - rule->arg.redir = redir; - rule->cond = redir->cond; - redir->cond = NULL; - cur_arg = 2; - return rule; - } else if (strncmp(args[0], "track-sc", 8) == 0) { - struct sample_expr *expr; - unsigned int where; - char *err = NULL; - unsigned int tsc_num; - const char *tsc_num_str; - - cur_arg = 1; - proxy->conf.args.ctx = ARGC_TRK; - - tsc_num_str = &args[0][8]; - if (cfg_parse_track_sc_num(&tsc_num, tsc_num_str, tsc_num_str + strlen(tsc_num_str), &err) == -1) { - ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-response %s' rule : %s.\n", - file, linenum, proxy_type_str(proxy), proxy->id, args[0], err); - free(err); - goto out_err; - } - - expr = sample_parse_expr((char **)args, &cur_arg, file, linenum, &err, &proxy->conf.args); - if (!expr) { - ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-response %s' rule : %s.\n", - file, linenum, proxy_type_str(proxy), proxy->id, args[0], err); - free(err); - goto out_err; - } - - where = 0; - if (proxy->cap & PR_CAP_FE) - where |= SMP_VAL_FE_HRS_HDR; - if (proxy->cap & PR_CAP_BE) - where |= SMP_VAL_BE_HRS_HDR; - - if (!(expr->fetch->val & where)) { - ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-response %s' rule :" - " fetch method '%s' extracts information from '%s', none of which is available here.\n", - file, linenum, proxy_type_str(proxy), proxy->id, args[0], - args[cur_arg-1], sample_src_names(expr->fetch->use)); - free(expr); - goto out_err; - } - - if (strcmp(args[cur_arg], "table") == 0) { - cur_arg++; - if (!args[cur_arg]) { - ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-response %s' rule : missing table name.\n", - file, linenum, proxy_type_str(proxy), proxy->id, args[0]); - free(expr); - goto out_err; - } - /* we copy the table name for now, it will be resolved later */ - rule->arg.trk_ctr.table.n = strdup(args[cur_arg]); - cur_arg++; - } - rule->arg.trk_ctr.expr = expr; - rule->action = ACT_ACTION_TRK_SC0 + tsc_num; - rule->check_ptr = check_trk_action; - } else if (((custom = action_http_res_custom(args[0])) != NULL)) { - char *errmsg = NULL; - cur_arg = 1; - /* try in the module list */ - rule->from = ACT_F_HTTP_RES; - rule->kw = custom; - if (custom->parse(args, &cur_arg, proxy, rule, &errmsg) == ACT_RET_PRS_ERR) { - ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-response %s' rule : %s.\n", - file, linenum, proxy_type_str(proxy), proxy->id, args[0], errmsg); - free(errmsg); - goto out_err; - } - } else { - action_build_list(&http_res_keywords.list, &trash); - ha_alert("parsing [%s:%d]: 'http-response' expects 'allow', 'deny', 'redirect', " - "'add-header', 'del-header', 'set-header', 'replace-header', 'replace-value', 'set-nice', " - "'set-tos', 'set-mark', 'set-log-level', 'add-acl', 'del-acl', 'del-map', 'set-map', 'track-sc*'" - "%s%s, but got '%s'%s.\n", - file, linenum, *trash.area ? ", " : "", trash.area, - args[0], *args[0] ? "" : " (missing argument)"); - goto out_err; - } - - if (strcmp(args[cur_arg], "if") == 0 || strcmp(args[cur_arg], "unless") == 0) { - struct acl_cond *cond; - char *errmsg = NULL; - - if ((cond = build_acl_cond(file, linenum, &proxy->acl, proxy, args+cur_arg, &errmsg)) == NULL) { - ha_alert("parsing [%s:%d] : error detected while parsing an 'http-response %s' condition : %s.\n", - file, linenum, args[0], errmsg); - free(errmsg); - goto out_err; - } - rule->cond = cond; - } - else if (*args[cur_arg]) { - ha_alert("parsing [%s:%d]: 'http-response %s' expects" - " either 'if' or 'unless' followed by a condition but found '%s'.\n", - file, linenum, args[0], args[cur_arg]); - goto out_err; - } - - return rule; - out_err: - free(rule); - return NULL; -} - -/* Parses a redirect rule. Returns the redirect rule on success or NULL on error, - * with filled with the error message. If is not null, builds a - * dynamic log-format rule instead of a static string. Parameter indicates - * the direction of the rule, and equals 0 for request, non-zero for responses. - */ -struct redirect_rule *http_parse_redirect_rule(const char *file, int linenum, struct proxy *curproxy, - const char **args, char **errmsg, int use_fmt, int dir) -{ - struct redirect_rule *rule; - int cur_arg; - int type = REDIRECT_TYPE_NONE; - int code = 302; - const char *destination = NULL; - const char *cookie = NULL; - int cookie_set = 0; - unsigned int flags = REDIRECT_FLAG_NONE; - struct acl_cond *cond = NULL; - - cur_arg = 0; - while (*(args[cur_arg])) { - if (strcmp(args[cur_arg], "location") == 0) { - if (!*args[cur_arg + 1]) - goto missing_arg; - - type = REDIRECT_TYPE_LOCATION; - cur_arg++; - destination = args[cur_arg]; - } - else if (strcmp(args[cur_arg], "prefix") == 0) { - if (!*args[cur_arg + 1]) - goto missing_arg; - type = REDIRECT_TYPE_PREFIX; - cur_arg++; - destination = args[cur_arg]; - } - else if (strcmp(args[cur_arg], "scheme") == 0) { - if (!*args[cur_arg + 1]) - goto missing_arg; - - type = REDIRECT_TYPE_SCHEME; - cur_arg++; - destination = args[cur_arg]; - } - else if (strcmp(args[cur_arg], "set-cookie") == 0) { - if (!*args[cur_arg + 1]) - goto missing_arg; - - cur_arg++; - cookie = args[cur_arg]; - cookie_set = 1; - } - else if (strcmp(args[cur_arg], "clear-cookie") == 0) { - if (!*args[cur_arg + 1]) - goto missing_arg; - - cur_arg++; - cookie = args[cur_arg]; - cookie_set = 0; - } - else if (strcmp(args[cur_arg], "code") == 0) { - if (!*args[cur_arg + 1]) - goto missing_arg; - - cur_arg++; - code = atol(args[cur_arg]); - if (code < 301 || code > 308 || (code > 303 && code < 307)) { - memprintf(errmsg, - "'%s': unsupported HTTP code '%s' (must be one of 301, 302, 303, 307 or 308)", - args[cur_arg - 1], args[cur_arg]); - return NULL; - } - } - else if (!strcmp(args[cur_arg],"drop-query")) { - flags |= REDIRECT_FLAG_DROP_QS; - } - else if (!strcmp(args[cur_arg],"append-slash")) { - flags |= REDIRECT_FLAG_APPEND_SLASH; - } - else if (strcmp(args[cur_arg], "if") == 0 || - strcmp(args[cur_arg], "unless") == 0) { - cond = build_acl_cond(file, linenum, &curproxy->acl, curproxy, (const char **)args + cur_arg, errmsg); - if (!cond) { - memprintf(errmsg, "error in condition: %s", *errmsg); - return NULL; - } - break; - } - else { - memprintf(errmsg, - "expects 'code', 'prefix', 'location', 'scheme', 'set-cookie', 'clear-cookie', 'drop-query' or 'append-slash' (was '%s')", - args[cur_arg]); - return NULL; - } - cur_arg++; - } - - if (type == REDIRECT_TYPE_NONE) { - memprintf(errmsg, "redirection type expected ('prefix', 'location', or 'scheme')"); - return NULL; - } - - if (dir && type != REDIRECT_TYPE_LOCATION) { - memprintf(errmsg, "response only supports redirect type 'location'"); - return NULL; - } - - rule = calloc(1, sizeof(*rule)); - rule->cond = cond; - LIST_INIT(&rule->rdr_fmt); - - if (!use_fmt) { - /* old-style static redirect rule */ - rule->rdr_str = strdup(destination); - rule->rdr_len = strlen(destination); - } - else { - /* log-format based redirect rule */ - - /* Parse destination. Note that in the REDIRECT_TYPE_PREFIX case, - * if prefix == "/", we don't want to add anything, otherwise it - * makes it hard for the user to configure a self-redirection. - */ - curproxy->conf.args.ctx = ARGC_RDR; - if (!(type == REDIRECT_TYPE_PREFIX && destination[0] == '/' && destination[1] == '\0')) { - if (!parse_logformat_string(destination, curproxy, &rule->rdr_fmt, LOG_OPT_HTTP, - dir ? (curproxy->cap & PR_CAP_FE) ? SMP_VAL_FE_HRS_HDR : SMP_VAL_BE_HRS_HDR - : (curproxy->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR, - errmsg)) { - return NULL; - } - free(curproxy->conf.lfs_file); - curproxy->conf.lfs_file = strdup(curproxy->conf.args.file); - curproxy->conf.lfs_line = curproxy->conf.args.line; - } - } - - if (cookie) { - /* depending on cookie_set, either we want to set the cookie, or to clear it. - * a clear consists in appending "; path=/; Max-Age=0;" at the end. - */ - rule->cookie_len = strlen(cookie); - if (cookie_set) { - rule->cookie_str = malloc(rule->cookie_len + 10); - memcpy(rule->cookie_str, cookie, rule->cookie_len); - memcpy(rule->cookie_str + rule->cookie_len, "; path=/;", 10); - rule->cookie_len += 9; - } else { - rule->cookie_str = malloc(rule->cookie_len + 21); - memcpy(rule->cookie_str, cookie, rule->cookie_len); - memcpy(rule->cookie_str + rule->cookie_len, "; path=/; Max-Age=0;", 21); - rule->cookie_len += 20; - } - } - rule->type = type; - rule->code = code; - rule->flags = flags; - LIST_INIT(&rule->list); - return rule; - - missing_arg: - memprintf(errmsg, "missing argument for '%s'", args[cur_arg]); - return NULL; -} - /* This function executes one of the set-{method,path,query,uri} actions. It * takes the string from the variable 'replace' with length 'len', then modifies * the relevant part of the request line accordingly. Then it updates various @@ -8940,23 +7794,6 @@ void http_set_status(unsigned int status, const char *reason, struct stream *s) http_msg_move_end(&txn->rsp, delta); } -/* - * Return the struct http_req_action_kw associated to a keyword. - */ -struct action_kw *action_http_req_custom(const char *kw) -{ - return action_lookup(&http_req_keywords.list, kw); -} - -/* - * Return the struct http_res_action_kw associated to a keyword. - */ -struct action_kw *action_http_res_custom(const char *kw) -{ - return action_lookup(&http_res_keywords.list, kw); -} - - __attribute__((constructor)) static void __http_protocol_init(void) { diff --git a/src/proto_tcp.c b/src/proto_tcp.c index c000602b6..3cc622a6d 100644 --- a/src/proto_tcp.c +++ b/src/proto_tcp.c @@ -49,6 +49,7 @@ #include #include #include +#include #include #include #include diff --git a/src/queue.c b/src/queue.c index 245fb7dad..a5f129d50 100644 --- a/src/queue.c +++ b/src/queue.c @@ -75,6 +75,7 @@ #include #include +#include #include #include #include diff --git a/src/ssl_sock.c b/src/ssl_sock.c index 0899418f4..5bd0abf9a 100644 --- a/src/ssl_sock.c +++ b/src/ssl_sock.c @@ -91,6 +91,7 @@ #include #include #include +#include #include #include #include diff --git a/src/stick_table.c b/src/stick_table.c index e8d4f3a60..d2aea6d97 100644 --- a/src/stick_table.c +++ b/src/stick_table.c @@ -29,6 +29,7 @@ #include #include +#include #include #include #include diff --git a/src/stream.c b/src/stream.c index 266f3b163..b61634674 100644 --- a/src/stream.c +++ b/src/stream.c @@ -43,6 +43,7 @@ #include #include #include +#include #include #include #include diff --git a/src/vars.c b/src/vars.c index 905e20c4f..7cc06d6dc 100644 --- a/src/vars.c +++ b/src/vars.c @@ -7,6 +7,7 @@ #include #include +#include #include #include #include