commit b05ee4aa74a95be49c78198ca601000b47c93da2 Author: Christopher Faulet Date: Wed Apr 17 12:02:59 2019 +0200 BUG/MAJOR: http_fetch: Get the channel depending on the keyword used All HTTP samples are buggy because the channel tested in the prefetch functions (HTX and legacy HTTP) is chosen depending on the sample direction and not the keyword really used. It means the request channel is used if the sample is called during the request analysis and the response channel is used if it is called during the response analysis, regardless the sample really called. For instance, if you use the sample "req.ver" in an http-response rule, the response channel will be prefeched because it is called during the response analysis, while the request channel should have been used instead. So some assumptions on the validity of the sample may be made on the wrong channel. It is the first bug. Then the same error is done in some samples themselves. So fetches are performed on the wrong channel. For instance, the header extraction (req.fhdr, res.fhdr, req.hdr, res.hdr...). If the sample "req.hdr" is used in an http-response rule, then the matching is done on the response headers and not the request ones. It is the second bug. Finally, the last one but not the least, in some samples, the right channel is used. But because the prefetch was done on the wrong one, this channel may be in a undefined state. For instance, using the sample "req.ver" in an http-response rule leads to a matching on a posibility released buffer. To fix all these bugs, the right channel is now chosen in sample fetches, before the prefetch. If the same function is used to fetch requests and responses elements, then the keyword is used to choose the right one. This channel is then used by the functions smp_prefetch_htx() and smp_prefetch_http(). Of course, it is also used by the samples themselves to extract information. This patch must be backported to all supported versions. For version 1.8 and priors, it must be totally refactored. First because there is no HTX into these versions. Then the buffers API has changed in HAProxy 1.9. The files http_fetch.{ch} doesn't exist on old versions. (cherry picked from commit 89dc49935997856dd4f864b654d3601107ec1967) Signed-off-by: Christopher Faulet (cherry picked from commit a89ca0b50b6b30ab75fbb7193a1132c0f89520fc) [cf: Changes made in src/proto_http.c because src/http_fetch.c doesn't exist] Signed-off-by: Christopher Faulet diff --git a/include/proto/proto_http.h b/include/proto/proto_http.h index 50efff4c..a2b365da 100644 --- a/include/proto/proto_http.h +++ b/include/proto/proto_http.h @@ -132,7 +132,7 @@ struct action_kw *action_http_res_custom(const char *kw); int val_hdr(struct arg *arg, char **err_msg); int smp_prefetch_http(struct proxy *px, struct stream *s, unsigned int opt, - const struct arg *args, struct sample *smp, int req_vol); + const struct channel *chn, struct sample *smp, int req_vol); enum act_return http_action_req_capture_by_id(struct act_rule *rule, struct proxy *px, struct session *sess, struct stream *s, int flags); @@ -144,11 +144,11 @@ int parse_qvalue(const char *qvalue, const char **end); /* Note: these functions *do* modify the sample. Even in case of success, at * least the type and uint value are modified. */ -#define CHECK_HTTP_MESSAGE_FIRST() \ - do { int r = smp_prefetch_http(smp->px, smp->strm, smp->opt, args, smp, 1); if (r <= 0) return r; } while (0) +#define CHECK_HTTP_MESSAGE_FIRST(chn) \ + do { int r = smp_prefetch_http(smp->px, smp->strm, smp->opt, (chn), smp, 1); if (r <= 0) return r; } while (0) -#define CHECK_HTTP_MESSAGE_FIRST_PERM() \ - do { int r = smp_prefetch_http(smp->px, smp->strm, smp->opt, args, smp, 0); if (r <= 0) return r; } while (0) +#define CHECK_HTTP_MESSAGE_FIRST_PERM(chn) \ + do { int r = smp_prefetch_http(smp->px, smp->strm, smp->opt, (chn), smp, 0); if (r <= 0) return r; } while (0) static inline void http_req_keywords_register(struct action_kw_list *kw_list) { diff --git a/src/hlua.c b/src/hlua.c index 93cb86d2..d40c012a 100644 --- a/src/hlua.c +++ b/src/hlua.c @@ -6458,7 +6458,7 @@ static int hlua_applet_http_init(struct appctx *ctx, struct proxy *px, struct st const char *error; /* Wait for a full HTTP request. */ - if (!smp_prefetch_http(px, strm, 0, NULL, &smp, 0)) { + if (!smp_prefetch_http(px, strm, 0, req, &smp, 0)) { if (smp.flags & SMP_F_MAY_CHANGE) return -1; return 0; diff --git a/src/proto_http.c b/src/proto_http.c index 8b087c5b..9beaa137 100644 --- a/src/proto_http.c +++ b/src/proto_http.c @@ -9447,6 +9447,8 @@ struct redirect_rule *http_parse_redirect_rule(const char *file, int linenum, st /************************************************************************/ /* The code below is dedicated to ACL parsing and matching */ /************************************************************************/ +#define SMP_REQ_CHN(smp) (smp->strm ? &smp->strm->req : NULL) +#define SMP_RES_CHN(smp) (smp->strm ? &smp->strm->res : NULL) /* This function ensures that the prerequisites for an L7 fetch are ready, @@ -9463,7 +9465,7 @@ struct redirect_rule *http_parse_redirect_rule(const char *file, int linenum, st * 1 if an HTTP message is ready */ int smp_prefetch_http(struct proxy *px, struct stream *s, unsigned int opt, - const struct arg *args, struct sample *smp, int req_vol) + const struct channel *chn, struct sample *smp, int req_vol) { struct http_txn *txn; struct http_msg *msg; @@ -9472,7 +9474,7 @@ int smp_prefetch_http(struct proxy *px, struct stream *s, unsigned int opt, * initialization (eg: tcp-request connection), so this function is the * one responsible for guarding against this case for all HTTP users. */ - if (!s) + if (!s || !chn) return 0; if (!s->txn) { @@ -9481,78 +9483,78 @@ int smp_prefetch_http(struct proxy *px, struct stream *s, unsigned int opt, http_init_txn(s); } txn = s->txn; - msg = &txn->req; - /* Check for a dependency on a request */ smp->data.type = SMP_T_BOOL; - if ((opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ) { - /* If the buffer does not leave enough free space at the end, - * we must first realign it. - */ - if (s->req.buf->p > s->req.buf->data && - s->req.buf->i + s->req.buf->p > s->req.buf->data + s->req.buf->size - global.tune.maxrewrite) - buffer_slow_realign(s->req.buf); - - if (unlikely(txn->req.msg_state < HTTP_MSG_BODY)) { - if (msg->msg_state == HTTP_MSG_ERROR) - return 0; + if (chn->flags & CF_ISRESP) { + /* Check for a dependency on a response */ + if (txn->rsp.msg_state < HTTP_MSG_BODY) { + smp->flags |= SMP_F_MAY_CHANGE; + return 0; + } + goto end; + } - /* Try to decode HTTP request */ - if (likely(msg->next < s->req.buf->i)) - http_msg_analyzer(msg, &txn->hdr_idx); + /* Check for a dependency on a request */ + msg = &txn->req; - /* Still no valid request ? */ - if (unlikely(msg->msg_state < HTTP_MSG_BODY)) { - if ((msg->msg_state == HTTP_MSG_ERROR) || - buffer_full(s->req.buf, global.tune.maxrewrite)) { - return 0; - } - /* wait for final state */ - smp->flags |= SMP_F_MAY_CHANGE; - return 0; - } + if (req_vol && (smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) { + return 0; /* data might have moved and indexes changed */ + } - /* OK we just got a valid HTTP request. We have some minor - * preparation to perform so that further checks can rely - * on HTTP tests. - */ + /* If the buffer does not leave enough free space at the end, we must + * first realign it. + */ + if (chn->buf->p > chn->buf->data && + chn->buf->i + chn->buf->p > chn->buf->data + chn->buf->size - global.tune.maxrewrite) + buffer_slow_realign(chn->buf); - /* If the request was parsed but was too large, we must absolutely - * return an error so that it is not processed. At the moment this - * cannot happen, but if the parsers are to change in the future, - * we want this check to be maintained. - */ - if (unlikely(s->req.buf->i + s->req.buf->p > - s->req.buf->data + s->req.buf->size - global.tune.maxrewrite)) { - msg->err_state = msg->msg_state; - msg->msg_state = HTTP_MSG_ERROR; - smp->data.u.sint = 1; - return 1; - } + if (unlikely(msg->msg_state < HTTP_MSG_BODY)) { + if (msg->msg_state == HTTP_MSG_ERROR) + return 0; - txn->meth = find_http_meth(msg->chn->buf->p, msg->sl.rq.m_l); - if (txn->meth == HTTP_METH_GET || txn->meth == HTTP_METH_HEAD) - s->flags |= SF_REDIRECTABLE; + /* Try to decode HTTP request */ + if (likely(msg->next < chn->buf->i)) + http_msg_analyzer(msg, &txn->hdr_idx); - if (unlikely(msg->sl.rq.v_l == 0) && !http_upgrade_v09_to_v10(txn)) + /* Still no valid request ? */ + if (unlikely(msg->msg_state < HTTP_MSG_BODY)) { + if ((msg->msg_state == HTTP_MSG_ERROR) || + buffer_full(chn->buf, global.tune.maxrewrite)) { return 0; + } + /* wait for final state */ + smp->flags |= SMP_F_MAY_CHANGE; + return 0; } - if (req_vol && txn->rsp.msg_state != HTTP_MSG_RPBEFORE) { - return 0; /* data might have moved and indexes changed */ + /* OK we just got a valid HTTP message. We have some minor + * preparation to perform so that further checks can rely + * on HTTP tests. + */ + + /* If the message was parsed but was too large, we must absolutely + * return an error so that it is not processed. At the moment this + * cannot happen, but if the parsers are to change in the future, + * we want this check to be maintained. + */ + if (unlikely(chn->buf->i + chn->buf->p > + chn->buf->data + chn->buf->size - global.tune.maxrewrite)) { + msg->err_state = msg->msg_state; + msg->msg_state = HTTP_MSG_ERROR; + smp->data.u.sint = 1; + return 1; } - /* otherwise everything's ready for the request */ - } - else { - /* Check for a dependency on a response */ - if (txn->rsp.msg_state < HTTP_MSG_BODY) { - smp->flags |= SMP_F_MAY_CHANGE; + txn->meth = find_http_meth(chn->buf->p, msg->sl.rq.m_l); + if (txn->meth == HTTP_METH_GET || txn->meth == HTTP_METH_HEAD) + s->flags |= SF_REDIRECTABLE; + + if (unlikely(msg->sl.rq.v_l == 0) && !http_upgrade_v09_to_v10(txn)) return 0; - } } + end: /* everything's OK */ smp->data.u.sint = 1; return 1; @@ -9592,19 +9594,21 @@ static int pat_parse_meth(const char *text, struct pattern *pattern, int mflags, static int smp_fetch_meth(const struct arg *args, struct sample *smp, const char *kw, void *private) { + struct channel *chn = SMP_REQ_CHN(smp); int meth; struct http_txn *txn; - CHECK_HTTP_MESSAGE_FIRST_PERM(); + CHECK_HTTP_MESSAGE_FIRST_PERM(chn); txn = smp->strm->txn; meth = txn->meth; smp->data.type = SMP_T_METH; smp->data.u.meth.meth = meth; if (meth == HTTP_METH_OTHER) { - if (txn->rsp.msg_state != HTTP_MSG_RPBEFORE) + if ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) { /* ensure the indexes are not affected */ return 0; + } smp->flags |= SMP_F_CONST; smp->data.u.meth.str.len = txn->req.sl.rq.m_l; smp->data.u.meth.str.str = txn->req.chn->buf->p; @@ -9646,15 +9650,16 @@ static struct pattern *pat_match_meth(struct sample *smp, struct pattern_expr *e static int smp_fetch_rqver(const struct arg *args, struct sample *smp, const char *kw, void *private) { + struct channel *chn = SMP_REQ_CHN(smp); struct http_txn *txn; char *ptr; int len; - CHECK_HTTP_MESSAGE_FIRST(); + CHECK_HTTP_MESSAGE_FIRST(chn); txn = smp->strm->txn; len = txn->req.sl.rq.v_l; - ptr = txn->req.chn->buf->p + txn->req.sl.rq.v; + ptr = chn->buf->p + txn->req.sl.rq.v; while ((len-- > 0) && (*ptr++ != '/')); if (len <= 0) @@ -9671,18 +9676,17 @@ smp_fetch_rqver(const struct arg *args, struct sample *smp, const char *kw, void static int smp_fetch_stver(const struct arg *args, struct sample *smp, const char *kw, void *private) { + struct channel *chn = SMP_RES_CHN(smp); struct http_txn *txn; char *ptr; int len; - CHECK_HTTP_MESSAGE_FIRST(); + CHECK_HTTP_MESSAGE_FIRST(chn); txn = smp->strm->txn; - if (txn->rsp.msg_state < HTTP_MSG_BODY) - return 0; len = txn->rsp.sl.st.v_l; - ptr = txn->rsp.chn->buf->p; + ptr = chn->buf->p; while ((len-- > 0) && (*ptr++ != '/')); if (len <= 0) @@ -9700,18 +9704,19 @@ smp_fetch_stver(const struct arg *args, struct sample *smp, const char *kw, void static int smp_fetch_stcode(const struct arg *args, struct sample *smp, const char *kw, void *private) { + struct channel *chn = SMP_RES_CHN(smp); struct http_txn *txn; char *ptr; int len; - CHECK_HTTP_MESSAGE_FIRST(); + CHECK_HTTP_MESSAGE_FIRST(chn); txn = smp->strm->txn; if (txn->rsp.msg_state < HTTP_MSG_BODY) return 0; len = txn->rsp.sl.st.c_l; - ptr = txn->rsp.chn->buf->p + txn->rsp.sl.st.c; + ptr = chn->buf->p + txn->rsp.sl.st.c; smp->data.type = SMP_T_SINT; smp->data.u.sint = __strl2ui(ptr, len); @@ -9746,20 +9751,21 @@ smp_fetch_uniqueid(const struct arg *args, struct sample *smp, const char *kw, v static int smp_fetch_hdrs(const struct arg *args, struct sample *smp, const char *kw, void *private) { + struct channel *chn = SMP_REQ_CHN(smp); struct http_msg *msg; struct hdr_idx *idx; struct http_txn *txn; - CHECK_HTTP_MESSAGE_FIRST(); + CHECK_HTTP_MESSAGE_FIRST(chn); txn = smp->strm->txn; idx = &txn->hdr_idx; msg = &txn->req; smp->data.type = SMP_T_STR; - smp->data.u.str.str = msg->chn->buf->p + hdr_idx_first_pos(idx); + smp->data.u.str.str = chn->buf->p + hdr_idx_first_pos(idx); smp->data.u.str.len = msg->eoh - hdr_idx_first_pos(idx) + 1 + - (msg->chn->buf->p[msg->eoh] == '\r'); + (chn->buf->p[msg->eoh] == '\r'); return 1; } @@ -9780,7 +9786,7 @@ smp_fetch_hdrs(const struct arg *args, struct sample *smp, const char *kw, void static int smp_fetch_hdrs_bin(const struct arg *args, struct sample *smp, const char *kw, void *private) { - struct http_msg *msg; + struct channel *chn = SMP_REQ_CHN(smp); struct chunk *temp; struct hdr_idx *idx; const char *cur_ptr, *cur_next, *p; @@ -9793,7 +9799,7 @@ smp_fetch_hdrs_bin(const struct arg *args, struct sample *smp, const char *kw, v char *buf; char *end; - CHECK_HTTP_MESSAGE_FIRST(); + CHECK_HTTP_MESSAGE_FIRST(chn); temp = get_trash_chunk(); buf = temp->str; @@ -9801,11 +9807,10 @@ smp_fetch_hdrs_bin(const struct arg *args, struct sample *smp, const char *kw, v txn = smp->strm->txn; idx = &txn->hdr_idx; - msg = &txn->req; /* Build array of headers. */ old_idx = 0; - cur_next = msg->chn->buf->p + hdr_idx_first_pos(idx); + cur_next = chn->buf->p + hdr_idx_first_pos(idx); while (1) { cur_idx = idx->v[old_idx].next; if (!cur_idx) @@ -9880,25 +9885,23 @@ smp_fetch_hdrs_bin(const struct arg *args, struct sample *smp, const char *kw, v static int smp_fetch_body(const struct arg *args, struct sample *smp, const char *kw, void *private) { + struct channel *chn = SMP_REQ_CHN(smp); struct http_msg *msg; unsigned long len; unsigned long block1; char *body; struct chunk *temp; - CHECK_HTTP_MESSAGE_FIRST(); + CHECK_HTTP_MESSAGE_FIRST(chn); - if ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ) - msg = &smp->strm->txn->req; - else - msg = &smp->strm->txn->rsp; + msg = &smp->strm->txn->req; len = http_body_bytes(msg); - body = b_ptr(msg->chn->buf, -http_data_rewind(msg)); + body = b_ptr(chn->buf, -http_data_rewind(msg)); block1 = len; - if (block1 > msg->chn->buf->data + msg->chn->buf->size - body) - block1 = msg->chn->buf->data + msg->chn->buf->size - body; + if (block1 > chn->buf->data + chn->buf->size - body) + block1 = chn->buf->data + chn->buf->size - body; if (block1 == len) { /* buffer is not wrapped (or empty) */ @@ -9911,7 +9914,7 @@ smp_fetch_body(const struct arg *args, struct sample *smp, const char *kw, void /* buffer is wrapped, we need to defragment it */ temp = get_trash_chunk(); memcpy(temp->str, body, block1); - memcpy(temp->str + block1, msg->chn->buf->data, len - block1); + memcpy(temp->str + block1, chn->buf->data, len - block1); smp->data.type = SMP_T_BIN; smp->data.u.str.str = temp->str; smp->data.u.str.len = len; @@ -9927,15 +9930,12 @@ smp_fetch_body(const struct arg *args, struct sample *smp, const char *kw, void static int smp_fetch_body_len(const struct arg *args, struct sample *smp, const char *kw, void *private) { + struct channel *chn = SMP_REQ_CHN(smp); struct http_msg *msg; - CHECK_HTTP_MESSAGE_FIRST(); - - if ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ) - msg = &smp->strm->txn->req; - else - msg = &smp->strm->txn->rsp; + CHECK_HTTP_MESSAGE_FIRST(chn); + msg = &smp->strm->txn->req; smp->data.type = SMP_T_SINT; smp->data.u.sint = http_body_bytes(msg); @@ -9951,15 +9951,12 @@ smp_fetch_body_len(const struct arg *args, struct sample *smp, const char *kw, v static int smp_fetch_body_size(const struct arg *args, struct sample *smp, const char *kw, void *private) { + struct channel *chn = SMP_REQ_CHN(smp); struct http_msg *msg; - CHECK_HTTP_MESSAGE_FIRST(); - - if ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ) - msg = &smp->strm->txn->req; - else - msg = &smp->strm->txn->rsp; + CHECK_HTTP_MESSAGE_FIRST(chn); + msg = &smp->strm->txn->req; smp->data.type = SMP_T_SINT; smp->data.u.sint = msg->body_len; @@ -9972,14 +9969,15 @@ smp_fetch_body_size(const struct arg *args, struct sample *smp, const char *kw, static int smp_fetch_url(const struct arg *args, struct sample *smp, const char *kw, void *private) { + struct channel *chn = SMP_REQ_CHN(smp); struct http_txn *txn; - CHECK_HTTP_MESSAGE_FIRST(); + CHECK_HTTP_MESSAGE_FIRST(chn); txn = smp->strm->txn; smp->data.type = SMP_T_STR; smp->data.u.str.len = txn->req.sl.rq.u_l; - smp->data.u.str.str = txn->req.chn->buf->p + txn->req.sl.rq.u; + smp->data.u.str.str = chn->buf->p + txn->req.sl.rq.u; smp->flags = SMP_F_VOL_1ST | SMP_F_CONST; return 1; } @@ -9987,13 +9985,14 @@ smp_fetch_url(const struct arg *args, struct sample *smp, const char *kw, void * static int smp_fetch_url_ip(const struct arg *args, struct sample *smp, const char *kw, void *private) { + struct channel *chn = SMP_REQ_CHN(smp); struct http_txn *txn; struct sockaddr_storage addr; - CHECK_HTTP_MESSAGE_FIRST(); + CHECK_HTTP_MESSAGE_FIRST(chn); txn = smp->strm->txn; - url2sa(txn->req.chn->buf->p + txn->req.sl.rq.u, txn->req.sl.rq.u_l, &addr, NULL); + url2sa(chn->buf->p + txn->req.sl.rq.u, txn->req.sl.rq.u_l, &addr, NULL); if (((struct sockaddr_in *)&addr)->sin_family != AF_INET) return 0; @@ -10006,13 +10005,14 @@ smp_fetch_url_ip(const struct arg *args, struct sample *smp, const char *kw, voi static int smp_fetch_url_port(const struct arg *args, struct sample *smp, const char *kw, void *private) { + struct channel *chn = SMP_REQ_CHN(smp); struct http_txn *txn; struct sockaddr_storage addr; - CHECK_HTTP_MESSAGE_FIRST(); + CHECK_HTTP_MESSAGE_FIRST(chn); txn = smp->strm->txn; - url2sa(txn->req.chn->buf->p + txn->req.sl.rq.u, txn->req.sl.rq.u_l, &addr, NULL); + url2sa(chn->buf->p + txn->req.sl.rq.u, txn->req.sl.rq.u_l, &addr, NULL); if (((struct sockaddr_in *)&addr)->sin_family != AF_INET) return 0; @@ -10032,6 +10032,8 @@ smp_fetch_url_port(const struct arg *args, struct sample *smp, const char *kw, v static int smp_fetch_fhdr(const struct arg *args, struct sample *smp, const char *kw, void *private) { + /* possible keywords: req.fhdr, res.fhdr */ + struct channel *chn = ((kw[2] == 'q') ? SMP_REQ_CHN(smp) : SMP_RES_CHN(smp)); struct hdr_idx *idx; struct hdr_ctx *ctx = smp->ctx.a[0]; const struct http_msg *msg; @@ -10056,10 +10058,9 @@ smp_fetch_fhdr(const struct arg *args, struct sample *smp, const char *kw, void occ = args[1].data.sint; } - CHECK_HTTP_MESSAGE_FIRST(); - + CHECK_HTTP_MESSAGE_FIRST(chn); idx = &smp->strm->txn->hdr_idx; - msg = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ) ? &smp->strm->txn->req : &smp->strm->txn->rsp; + msg = (!(chn->flags & CF_ISRESP) ? &smp->strm->txn->req : &smp->strm->txn->rsp); if (ctx && !(smp->flags & SMP_F_NOT_LAST)) /* search for header from the beginning */ @@ -10089,9 +10090,10 @@ smp_fetch_fhdr(const struct arg *args, struct sample *smp, const char *kw, void static int smp_fetch_fhdr_cnt(const struct arg *args, struct sample *smp, const char *kw, void *private) { + /* possible keywords: req.fhdr_cnt, res.fhdr_cnt */ + struct channel *chn = ((kw[2] == 'q') ? SMP_REQ_CHN(smp) : SMP_RES_CHN(smp)); struct hdr_idx *idx; struct hdr_ctx ctx; - const struct http_msg *msg; int cnt; const char *name = NULL; int len = 0; @@ -10101,14 +10103,12 @@ smp_fetch_fhdr_cnt(const struct arg *args, struct sample *smp, const char *kw, v len = args->data.str.len; } - CHECK_HTTP_MESSAGE_FIRST(); + CHECK_HTTP_MESSAGE_FIRST(chn); idx = &smp->strm->txn->hdr_idx; - msg = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ) ? &smp->strm->txn->req : &smp->strm->txn->rsp; - ctx.idx = 0; cnt = 0; - while (http_find_full_header2(name, len, msg->chn->buf->p, idx, &ctx)) + while (http_find_full_header2(name, len, chn->buf->p, idx, &ctx)) cnt++; smp->data.type = SMP_T_SINT; @@ -10120,24 +10120,24 @@ smp_fetch_fhdr_cnt(const struct arg *args, struct sample *smp, const char *kw, v static int smp_fetch_hdr_names(const struct arg *args, struct sample *smp, const char *kw, void *private) { + /* possible keywords: req.hdr_names, res.hdr_names */ + struct channel *chn = ((kw[2] == 'q') ? SMP_REQ_CHN(smp) : SMP_RES_CHN(smp)); struct hdr_idx *idx; struct hdr_ctx ctx; - const struct http_msg *msg; struct chunk *temp; char del = ','; if (args && args->type == ARGT_STR) del = *args[0].data.str.str; - CHECK_HTTP_MESSAGE_FIRST(); + CHECK_HTTP_MESSAGE_FIRST(chn); idx = &smp->strm->txn->hdr_idx; - msg = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ) ? &smp->strm->txn->req : &smp->strm->txn->rsp; temp = get_trash_chunk(); ctx.idx = 0; - while (http_find_next_header(msg->chn->buf->p, idx, &ctx)) { + while (http_find_next_header(chn->buf->p, idx, &ctx)) { if (temp->len) temp->str[temp->len++] = del; memcpy(temp->str + temp->len, ctx.line, ctx.del); @@ -10160,6 +10160,8 @@ smp_fetch_hdr_names(const struct arg *args, struct sample *smp, const char *kw, static int smp_fetch_hdr(const struct arg *args, struct sample *smp, const char *kw, void *private) { + /* possible keywords: req.hdr / hdr, res.hdr / shdr */ + struct channel *chn = ((kw[0] == 'h' || kw[2] == 'q') ? SMP_REQ_CHN(smp) : SMP_RES_CHN(smp)); struct hdr_idx *idx; struct hdr_ctx *ctx = smp->ctx.a[0]; const struct http_msg *msg; @@ -10184,10 +10186,10 @@ smp_fetch_hdr(const struct arg *args, struct sample *smp, const char *kw, void * occ = args[1].data.sint; } - CHECK_HTTP_MESSAGE_FIRST(); + CHECK_HTTP_MESSAGE_FIRST(chn); idx = &smp->strm->txn->hdr_idx; - msg = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ) ? &smp->strm->txn->req : &smp->strm->txn->rsp; + msg = (!(chn->flags & CF_ISRESP) ? &smp->strm->txn->req : &smp->strm->txn->rsp); if (ctx && !(smp->flags & SMP_F_NOT_LAST)) /* search for header from the beginning */ @@ -10216,9 +10218,10 @@ smp_fetch_hdr(const struct arg *args, struct sample *smp, const char *kw, void * static int smp_fetch_hdr_cnt(const struct arg *args, struct sample *smp, const char *kw, void *private) { + /* possible keywords: req.hdr_cnt / hdr_cnt, res.hdr_cnt / shdr_cnt */ + struct channel *chn = ((kw[0] == 'h' || kw[2] == 'q') ? SMP_REQ_CHN(smp) : SMP_RES_CHN(smp)); struct hdr_idx *idx; struct hdr_ctx ctx; - const struct http_msg *msg; int cnt; const char *name = NULL; int len = 0; @@ -10228,14 +10231,13 @@ smp_fetch_hdr_cnt(const struct arg *args, struct sample *smp, const char *kw, vo len = args->data.str.len; } - CHECK_HTTP_MESSAGE_FIRST(); + CHECK_HTTP_MESSAGE_FIRST(chn); idx = &smp->strm->txn->hdr_idx; - msg = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ) ? &smp->strm->txn->req : &smp->strm->txn->rsp; ctx.idx = 0; cnt = 0; - while (http_find_header2(name, len, msg->chn->buf->p, idx, &ctx)) + while (http_find_header2(name, len, chn->buf->p, idx, &ctx)) cnt++; smp->data.type = SMP_T_SINT; @@ -10300,13 +10302,14 @@ smp_fetch_hdr_ip(const struct arg *args, struct sample *smp, const char *kw, voi static int smp_fetch_path(const struct arg *args, struct sample *smp, const char *kw, void *private) { + struct channel *chn = SMP_REQ_CHN(smp); struct http_txn *txn; char *ptr, *end; - CHECK_HTTP_MESSAGE_FIRST(); + CHECK_HTTP_MESSAGE_FIRST(chn); txn = smp->strm->txn; - end = txn->req.chn->buf->p + txn->req.sl.rq.u + txn->req.sl.rq.u_l; + end = chn->buf->p + txn->req.sl.rq.u + txn->req.sl.rq.u_l; ptr = http_get_path(txn); if (!ptr) return 0; @@ -10333,16 +10336,17 @@ smp_fetch_path(const struct arg *args, struct sample *smp, const char *kw, void static int smp_fetch_base(const struct arg *args, struct sample *smp, const char *kw, void *private) { + struct channel *chn = SMP_REQ_CHN(smp); struct http_txn *txn; char *ptr, *end, *beg; struct hdr_ctx ctx; struct chunk *temp; - CHECK_HTTP_MESSAGE_FIRST(); + CHECK_HTTP_MESSAGE_FIRST(chn); txn = smp->strm->txn; ctx.idx = 0; - if (!http_find_header2("Host", 4, txn->req.chn->buf->p, &txn->hdr_idx, &ctx) || !ctx.vlen) + if (!http_find_header2("Host", 4, chn->buf->p, &txn->hdr_idx, &ctx) || !ctx.vlen) return smp_fetch_path(args, smp, kw, private); /* OK we have the header value in ctx.line+ctx.val for ctx.vlen bytes */ @@ -10353,7 +10357,7 @@ smp_fetch_base(const struct arg *args, struct sample *smp, const char *kw, void smp->data.u.str.len = ctx.vlen; /* now retrieve the path */ - end = txn->req.chn->buf->p + txn->req.sl.rq.u + txn->req.sl.rq.u_l; + end = chn->buf->p + txn->req.sl.rq.u + txn->req.sl.rq.u_l; beg = http_get_path(txn); if (!beg) beg = end; @@ -10380,17 +10384,18 @@ smp_fetch_base(const struct arg *args, struct sample *smp, const char *kw, void int smp_fetch_base32(const struct arg *args, struct sample *smp, const char *kw, void *private) { + struct channel *chn = SMP_REQ_CHN(smp); struct http_txn *txn; struct hdr_ctx ctx; unsigned int hash = 0; char *ptr, *beg, *end; int len; - CHECK_HTTP_MESSAGE_FIRST(); + CHECK_HTTP_MESSAGE_FIRST(chn); txn = smp->strm->txn; ctx.idx = 0; - if (http_find_header2("Host", 4, txn->req.chn->buf->p, &txn->hdr_idx, &ctx)) { + if (http_find_header2("Host", 4, chn->buf->p, &txn->hdr_idx, &ctx)) { /* OK we have the header value in ctx.line+ctx.val for ctx.vlen bytes */ ptr = ctx.line + ctx.val; len = ctx.vlen; @@ -10399,7 +10404,7 @@ smp_fetch_base32(const struct arg *args, struct sample *smp, const char *kw, voi } /* now retrieve the path */ - end = txn->req.chn->buf->p + txn->req.sl.rq.u + txn->req.sl.rq.u_l; + end = chn->buf->p + txn->req.sl.rq.u + txn->req.sl.rq.u_l; beg = http_get_path(txn); if (!beg) beg = end; @@ -10466,13 +10471,14 @@ smp_fetch_base32_src(const struct arg *args, struct sample *smp, const char *kw, static int smp_fetch_query(const struct arg *args, struct sample *smp, const char *kw, void *private) { + struct channel *chn = SMP_REQ_CHN(smp); struct http_txn *txn; char *ptr, *end; - CHECK_HTTP_MESSAGE_FIRST(); + CHECK_HTTP_MESSAGE_FIRST(chn); txn = smp->strm->txn; - ptr = txn->req.chn->buf->p + txn->req.sl.rq.u; + ptr = chn->buf->p + txn->req.sl.rq.u; end = ptr + txn->req.sl.rq.u_l; /* look up the '?' */ @@ -10491,11 +10497,13 @@ smp_fetch_query(const struct arg *args, struct sample *smp, const char *kw, void static int smp_fetch_proto_http(const struct arg *args, struct sample *smp, const char *kw, void *private) { + struct channel *chn = SMP_REQ_CHN(smp); + /* Note: hdr_idx.v cannot be NULL in this ACL because the ACL is tagged * as a layer7 ACL, which involves automatic allocation of hdr_idx. */ - CHECK_HTTP_MESSAGE_FIRST_PERM(); + CHECK_HTTP_MESSAGE_FIRST_PERM(chn); smp->data.type = SMP_T_BOOL; smp->data.u.sint = 1; @@ -10515,11 +10523,12 @@ smp_fetch_http_first_req(const struct arg *args, struct sample *smp, const char static int smp_fetch_http_auth(const struct arg *args, struct sample *smp, const char *kw, void *private) { + struct channel *chn = SMP_REQ_CHN(smp); if (!args || args->type != ARGT_USR) return 0; - CHECK_HTTP_MESSAGE_FIRST(); + CHECK_HTTP_MESSAGE_FIRST(chn); if (!get_http_auth(smp->strm)) return 0; @@ -10534,10 +10543,12 @@ smp_fetch_http_auth(const struct arg *args, struct sample *smp, const char *kw, static int smp_fetch_http_auth_grp(const struct arg *args, struct sample *smp, const char *kw, void *private) { + struct channel *chn = SMP_REQ_CHN(smp); + if (!args || args->type != ARGT_USR) return 0; - CHECK_HTTP_MESSAGE_FIRST(); + CHECK_HTTP_MESSAGE_FIRST(chn); if (!get_http_auth(smp->strm)) return 0; @@ -10828,10 +10839,10 @@ smp_fetch_capture_res_ver(const struct arg *args, struct sample *smp, const char */ int smp_fetch_cookie(const struct arg *args, struct sample *smp, const char *kw, void *private) { - struct http_txn *txn; + /* possible keywords: req.cookie / cookie / cook, res.cookie / scook / set-cookie */ + struct channel *chn = ((kw[0] == 'c' || kw[2] == 'q') ? SMP_REQ_CHN(smp) : SMP_RES_CHN(smp)); struct hdr_idx *idx; struct hdr_ctx *ctx = smp->ctx.a[2]; - const struct http_msg *msg; const char *hdr_name; int hdr_name_len; char *sol; @@ -10848,17 +10859,14 @@ int smp_fetch_cookie(const struct arg *args, struct sample *smp, const char *kw, smp->ctx.a[2] = ctx; } - CHECK_HTTP_MESSAGE_FIRST(); + CHECK_HTTP_MESSAGE_FIRST(chn); - txn = smp->strm->txn; idx = &smp->strm->txn->hdr_idx; - if ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ) { - msg = &txn->req; + if (!(chn->flags & CF_ISRESP)) { hdr_name = "Cookie"; hdr_name_len = 6; } else { - msg = &txn->rsp; hdr_name = "Set-Cookie"; hdr_name_len = 10; } @@ -10872,7 +10880,7 @@ int smp_fetch_cookie(const struct arg *args, struct sample *smp, const char *kw, * next one. */ - sol = msg->chn->buf->p; + sol = chn->buf->p; if (!(smp->flags & SMP_F_NOT_LAST)) { /* search for the header from the beginning, we must first initialize * the search parameters. @@ -10929,10 +10937,10 @@ int smp_fetch_cookie(const struct arg *args, struct sample *smp, const char *kw, static int smp_fetch_cookie_cnt(const struct arg *args, struct sample *smp, const char *kw, void *private) { - struct http_txn *txn; + /* possible keywords: req.cook_cnt / cook_cnt, res.cook_cnt / scook_cnt */ + struct channel *chn = ((kw[0] == 'c' || kw[2] == 'q') ? SMP_REQ_CHN(smp) : SMP_RES_CHN(smp)); struct hdr_idx *idx; struct hdr_ctx ctx; - const struct http_msg *msg; const char *hdr_name; int hdr_name_len; int cnt; @@ -10942,22 +10950,19 @@ smp_fetch_cookie_cnt(const struct arg *args, struct sample *smp, const char *kw, if (!args || args->type != ARGT_STR) return 0; - CHECK_HTTP_MESSAGE_FIRST(); + CHECK_HTTP_MESSAGE_FIRST(chn); - txn = smp->strm->txn; idx = &smp->strm->txn->hdr_idx; - if ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ) { - msg = &txn->req; + if (!(chn->flags & CF_ISRESP)) { hdr_name = "Cookie"; hdr_name_len = 6; } else { - msg = &txn->rsp; hdr_name = "Set-Cookie"; hdr_name_len = 10; } - sol = msg->chn->buf->p; + sol = chn->buf->p; val_end = val_beg = NULL; ctx.idx = 0; cnt = 0; @@ -11287,6 +11292,7 @@ smp_fetch_param(char delim, const char *name, int name_len, const struct arg *ar static int smp_fetch_url_param(const struct arg *args, struct sample *smp, const char *kw, void *private) { + struct channel *chn = SMP_REQ_CHN(smp); struct http_msg *msg; char delim = '?'; const char *name; @@ -11308,16 +11314,16 @@ smp_fetch_url_param(const struct arg *args, struct sample *smp, const char *kw, delim = *args[1].data.str.str; if (!smp->ctx.a[0]) { // first call, find the query string - CHECK_HTTP_MESSAGE_FIRST(); + CHECK_HTTP_MESSAGE_FIRST(chn); msg = &smp->strm->txn->req; - smp->ctx.a[0] = find_param_list(msg->chn->buf->p + msg->sl.rq.u, + smp->ctx.a[0] = find_param_list(chn->buf->p + msg->sl.rq.u, msg->sl.rq.u_l, delim); if (!smp->ctx.a[0]) return 0; - smp->ctx.a[1] = msg->chn->buf->p + msg->sl.rq.u + msg->sl.rq.u_l; + smp->ctx.a[1] = chn->buf->p + msg->sl.rq.u + msg->sl.rq.u_l; /* Assume that the context is filled with NULL pointer * before the first call. @@ -11339,6 +11345,7 @@ smp_fetch_url_param(const struct arg *args, struct sample *smp, const char *kw, static int smp_fetch_body_param(const struct arg *args, struct sample *smp, const char *kw, void *private) { + struct channel *chn = SMP_REQ_CHN(smp); struct http_msg *msg; unsigned long len; unsigned long block1; @@ -11357,19 +11364,15 @@ smp_fetch_body_param(const struct arg *args, struct sample *smp, const char *kw, } if (!smp->ctx.a[0]) { // first call, find the query string - CHECK_HTTP_MESSAGE_FIRST(); - - if ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ) - msg = &smp->strm->txn->req; - else - msg = &smp->strm->txn->rsp; + CHECK_HTTP_MESSAGE_FIRST(chn); + msg = &smp->strm->txn->req; len = http_body_bytes(msg); - body = b_ptr(msg->chn->buf, -http_data_rewind(msg)); + body = b_ptr(chn->buf, -http_data_rewind(msg)); block1 = len; - if (block1 > msg->chn->buf->data + msg->chn->buf->size - body) - block1 = msg->chn->buf->data + msg->chn->buf->size - body; + if (block1 > chn->buf->data + chn->buf->size - body) + block1 = chn->buf->data + chn->buf->size - body; if (block1 == len) { /* buffer is not wrapped (or empty) */ @@ -11386,8 +11389,8 @@ smp_fetch_body_param(const struct arg *args, struct sample *smp, const char *kw, /* buffer is wrapped, we need to defragment it */ smp->ctx.a[0] = body; smp->ctx.a[1] = body + block1; - smp->ctx.a[2] = msg->chn->buf->data; - smp->ctx.a[3] = msg->chn->buf->data + ( len - block1 ); + smp->ctx.a[2] = chn->buf->data; + smp->ctx.a[3] = chn->buf->data + ( len - block1 ); } } return smp_fetch_param('&', name, name_len, args, smp, kw, private); @@ -11422,17 +11425,18 @@ smp_fetch_url_param_val(const struct arg *args, struct sample *smp, const char * static int smp_fetch_url32(const struct arg *args, struct sample *smp, const char *kw, void *private) { + struct channel *chn = SMP_REQ_CHN(smp); struct http_txn *txn; struct hdr_ctx ctx; unsigned int hash = 0; char *ptr, *beg, *end; int len; - CHECK_HTTP_MESSAGE_FIRST(); + CHECK_HTTP_MESSAGE_FIRST(chn); txn = smp->strm->txn; ctx.idx = 0; - if (http_find_header2("Host", 4, txn->req.chn->buf->p, &txn->hdr_idx, &ctx)) { + if (http_find_header2("Host", 4, chn->buf->p, &txn->hdr_idx, &ctx)) { /* OK we have the header value in ctx.line+ctx.val for ctx.vlen bytes */ ptr = ctx.line + ctx.val; len = ctx.vlen; @@ -11441,7 +11445,7 @@ smp_fetch_url32(const struct arg *args, struct sample *smp, const char *kw, void } /* now retrieve the path */ - end = txn->req.chn->buf->p + txn->req.sl.rq.u + txn->req.sl.rq.u_l; + end = chn->buf->p + txn->req.sl.rq.u + txn->req.sl.rq.u_l; beg = http_get_path(txn); if (!beg) beg = end;