blob: 5b83c1906173b0b9d9f0369150298964fbed6456 [file] [log] [blame]
Willy Tarreau79e57332018-10-02 16:01:16 +02001/*
2 * HTTP samples fetching
3 *
4 * Copyright 2000-2018 Willy Tarreau <w@1wt.eu>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 *
11 */
12
13#include <sys/types.h>
14
15#include <ctype.h>
16#include <string.h>
17#include <time.h>
18
Willy Tarreaub2551052020-06-09 09:07:15 +020019#include <haproxy/api.h>
Willy Tarreaudfd3de82020-06-04 23:46:14 +020020#include <haproxy/arg.h>
Willy Tarreauac13aea2020-06-04 10:36:03 +020021#include <haproxy/auth.h>
Willy Tarreau8d366972020-05-27 16:10:29 +020022#include <haproxy/base64.h>
Willy Tarreauf1d32c42020-06-04 21:07:02 +020023#include <haproxy/channel.h>
Willy Tarreauc13ed532020-06-02 10:22:45 +020024#include <haproxy/chunk.h>
Willy Tarreau7ea393d2020-06-04 18:02:10 +020025#include <haproxy/connection.h>
Christopher Faulet8da67aa2022-03-29 17:53:09 +020026#include <haproxy/cs_utils.h>
Willy Tarreauf268ee82020-06-04 17:05:57 +020027#include <haproxy/global.h>
Willy Tarreau5413a872020-06-02 19:33:08 +020028#include <haproxy/h1.h>
Willy Tarreauc6fe8842020-06-04 09:00:02 +020029#include <haproxy/h1_htx.h>
Willy Tarreaucd72d8c2020-06-02 19:11:26 +020030#include <haproxy/http.h>
Willy Tarreauc2b1ff02020-06-04 21:21:03 +020031#include <haproxy/http_ana.h>
Willy Tarreau126ba3a2020-06-04 18:26:43 +020032#include <haproxy/http_fetch.h>
Willy Tarreau87735332020-06-04 09:08:41 +020033#include <haproxy/http_htx.h>
Willy Tarreau16f958c2020-06-03 08:44:35 +020034#include <haproxy/htx.h>
Willy Tarreau8efbdfb2020-06-04 11:29:21 +020035#include <haproxy/obj_type.h>
Willy Tarreaud0ef4392020-06-02 09:38:52 +020036#include <haproxy/pool.h>
Willy Tarreaue6ce10b2020-06-04 15:33:47 +020037#include <haproxy/sample.h>
Willy Tarreaudfd3de82020-06-04 23:46:14 +020038#include <haproxy/stream.h>
Christopher Faulet6fc817a2021-10-25 07:48:27 +020039#include <haproxy/stream_interface.h>
Willy Tarreau48fbcae2020-06-03 18:09:46 +020040#include <haproxy/tools.h>
Willy Tarreaud6788052020-05-27 15:59:00 +020041#include <haproxy/version.h>
Willy Tarreau79e57332018-10-02 16:01:16 +020042
Willy Tarreau79e57332018-10-02 16:01:16 +020043
44/* this struct is used between calls to smp_fetch_hdr() or smp_fetch_cookie() */
Christopher Fauletef453ed2018-10-24 21:39:27 +020045static THREAD_LOCAL struct http_hdr_ctx static_http_hdr_ctx;
Richard Russo458eafb2019-07-31 11:45:56 -070046/* this is used to convert raw connection buffers to htx */
47static THREAD_LOCAL struct buffer static_raw_htx_chunk;
48static THREAD_LOCAL char *static_raw_htx_buf;
Christopher Fauletef453ed2018-10-24 21:39:27 +020049
Christopher Faulet89dc4992019-04-17 12:02:59 +020050#define SMP_REQ_CHN(smp) (smp->strm ? &smp->strm->req : NULL)
51#define SMP_RES_CHN(smp) (smp->strm ? &smp->strm->res : NULL)
Willy Tarreau79e57332018-10-02 16:01:16 +020052
Richard Russo458eafb2019-07-31 11:45:56 -070053/* This function returns the static htx chunk, where raw connections get
54 * converted to HTX as needed for samplxsing.
55 */
56struct buffer *get_raw_htx_chunk(void)
57{
58 chunk_reset(&static_raw_htx_chunk);
59 return &static_raw_htx_chunk;
60}
61
62static int alloc_raw_htx_chunk_per_thread()
63{
64 static_raw_htx_buf = malloc(global.tune.bufsize);
65 if (!static_raw_htx_buf)
66 return 0;
67 chunk_init(&static_raw_htx_chunk, static_raw_htx_buf, global.tune.bufsize);
68 return 1;
69}
70
71static void free_raw_htx_chunk_per_thread()
72{
Willy Tarreau61cfdf42021-02-20 10:46:51 +010073 ha_free(&static_raw_htx_buf);
Richard Russo458eafb2019-07-31 11:45:56 -070074}
75
76REGISTER_PER_THREAD_ALLOC(alloc_raw_htx_chunk_per_thread);
77REGISTER_PER_THREAD_FREE(free_raw_htx_chunk_per_thread);
78
Willy Tarreau79e57332018-10-02 16:01:16 +020079/*
80 * Returns the data from Authorization header. Function may be called more
81 * than once so data is stored in txn->auth_data. When no header is found
82 * or auth method is unknown auth_method is set to HTTP_AUTH_WRONG to avoid
83 * searching again for something we are unable to find anyway. However, if
84 * the result if valid, the cache is not reused because we would risk to
85 * have the credentials overwritten by another stream in parallel.
Willy Tarreaueae83722020-04-29 11:52:51 +020086 * The caller is responsible for passing a sample with a valid stream/txn,
87 * and a valid htx.
Willy Tarreau79e57332018-10-02 16:01:16 +020088 */
89
Christopher Fauletcd761952019-07-15 13:58:29 +020090static int get_http_auth(struct sample *smp, struct htx *htx)
Willy Tarreau79e57332018-10-02 16:01:16 +020091{
Christopher Faulet311c7ea2018-10-24 21:41:55 +020092 struct stream *s = smp->strm;
Willy Tarreau79e57332018-10-02 16:01:16 +020093 struct http_txn *txn = s->txn;
Christopher Faulet6d1dd462019-07-15 14:36:03 +020094 struct http_hdr_ctx ctx = { .blk = NULL };
95 struct ist hdr;
Willy Tarreau79e57332018-10-02 16:01:16 +020096 struct buffer auth_method;
Christopher Faulet6d1dd462019-07-15 14:36:03 +020097 char *p;
Willy Tarreau79e57332018-10-02 16:01:16 +020098 int len;
99
100#ifdef DEBUG_AUTH
101 printf("Auth for stream %p: %d\n", s, txn->auth.method);
102#endif
Willy Tarreau79e57332018-10-02 16:01:16 +0200103 if (txn->auth.method == HTTP_AUTH_WRONG)
104 return 0;
105
106 txn->auth.method = HTTP_AUTH_WRONG;
107
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200108 if (txn->flags & TX_USE_PX_CONN)
109 hdr = ist("Proxy-Authorization");
110 else
111 hdr = ist("Authorization");
Willy Tarreau79e57332018-10-02 16:01:16 +0200112
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200113 ctx.blk = NULL;
114 if (!http_find_header(htx, hdr, &ctx, 0))
115 return 0;
Willy Tarreau79e57332018-10-02 16:01:16 +0200116
Willy Tarreau17254932020-09-02 07:08:47 +0200117 p = memchr(ctx.value.ptr, ' ', ctx.value.len);
118 if (!p || p == ctx.value.ptr) /* if no space was found or if the space is the first character */
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200119 return 0;
Willy Tarreau17254932020-09-02 07:08:47 +0200120 len = p - ctx.value.ptr;
Willy Tarreau79e57332018-10-02 16:01:16 +0200121
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200122 if (chunk_initlen(&auth_method, ctx.value.ptr, 0, len) != 1)
123 return 0;
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200124
Remi Tricot-Le Breton68c4eae2021-10-29 15:25:18 +0200125 /* According to RFC7235, there could be multiple spaces between the
126 * scheme and its value, we must skip all of them.
127 */
128 while (p < istend(ctx.value) && *p == ' ')
129 ++p;
130
131 chunk_initlen(&txn->auth.method_data, p, 0, istend(ctx.value) - p);
Willy Tarreau79e57332018-10-02 16:01:16 +0200132
133 if (!strncasecmp("Basic", auth_method.area, auth_method.data)) {
134 struct buffer *http_auth = get_trash_chunk();
135
136 len = base64dec(txn->auth.method_data.area,
137 txn->auth.method_data.data,
138 http_auth->area, global.tune.bufsize - 1);
139
140 if (len < 0)
141 return 0;
142
143
144 http_auth->area[len] = '\0';
145
146 p = strchr(http_auth->area, ':');
147
148 if (!p)
149 return 0;
150
151 txn->auth.user = http_auth->area;
152 *p = '\0';
153 txn->auth.pass = p+1;
154
155 txn->auth.method = HTTP_AUTH_BASIC;
156 return 1;
Remi Tricot-Le Bretonf5dd3372021-10-01 15:36:53 +0200157 } else if (!strncasecmp("Bearer", auth_method.area, auth_method.data)) {
158 txn->auth.method = HTTP_AUTH_BEARER;
159 return 1;
Willy Tarreau79e57332018-10-02 16:01:16 +0200160 }
161
162 return 0;
163}
164
165/* This function ensures that the prerequisites for an L7 fetch are ready,
166 * which means that a request or response is ready. If some data is missing,
167 * a parsing attempt is made. This is useful in TCP-based ACLs which are able
Christopher Faulet5ec8bcb2019-04-17 12:04:12 +0200168 * to extract data from L7. If <vol> is non-null during a prefetch, another
169 * test is made to ensure the required information is not gone.
Christopher Fauletef453ed2018-10-24 21:39:27 +0200170 *
171 * The function returns :
172 * NULL with SMP_F_MAY_CHANGE in the sample flags if some data is missing to
173 * decide whether or not an HTTP message is present ;
174 * NULL if the requested data cannot be fetched or if it is certain that
Willy Tarreaueae83722020-04-29 11:52:51 +0200175 * we'll never have any HTTP message there; this includes null strm or chn.
Willy Tarreaua6d98792020-08-12 14:04:52 +0200176 * NULL if the sample's direction does not match the channel's (i.e. the
177 * function was asked to work on the wrong channel)
Christopher Fauletef453ed2018-10-24 21:39:27 +0200178 * The HTX message if ready
179 */
Christopher Faulet778f5ed2020-04-29 15:51:55 +0200180struct htx *smp_prefetch_htx(struct sample *smp, struct channel *chn, struct check *check, int vol)
Christopher Fauletef453ed2018-10-24 21:39:27 +0200181{
Christopher Fauletef453ed2018-10-24 21:39:27 +0200182 struct stream *s = smp->strm;
Christopher Fauletef453ed2018-10-24 21:39:27 +0200183 struct http_txn *txn = NULL;
184 struct htx *htx = NULL;
Christopher Faulet89dc4992019-04-17 12:02:59 +0200185 struct http_msg *msg;
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100186 struct htx_sl *sl;
Christopher Fauletef453ed2018-10-24 21:39:27 +0200187
Willy Tarreaua6d98792020-08-12 14:04:52 +0200188 if (chn &&
189 (((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ && (chn->flags & CF_ISRESP)) ||
190 ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES && !(chn->flags & CF_ISRESP))))
191 return 0;
192
Christopher Fauletef453ed2018-10-24 21:39:27 +0200193 /* Note: it is possible that <s> is NULL when called before stream
194 * initialization (eg: tcp-request connection), so this function is the
195 * one responsible for guarding against this case for all HTTP users.
Christopher Faulet778f5ed2020-04-29 15:51:55 +0200196 *
197 * In the health check context, the stream and the channel must be NULL
198 * and <check> must be set. In this case, only the input buffer,
199 * corresponding to the response, is considered. It is the caller
200 * responsibility to provide <check>.
Christopher Fauletef453ed2018-10-24 21:39:27 +0200201 */
Christopher Faulet778f5ed2020-04-29 15:51:55 +0200202 BUG_ON(check && (s || chn));
203 if (!s || !chn) {
204 if (check) {
205 htx = htxbuf(&check->bi);
206
207 /* Analyse not yet started */
208 if (htx_is_empty(htx) || htx->first == -1)
209 return NULL;
210
211 sl = http_get_stline(htx);
212 if (vol && !sl) {
213 /* The start-line was already forwarded, it is too late to fetch anything */
214 return NULL;
215 }
216 goto end;
217 }
218
Christopher Fauletef453ed2018-10-24 21:39:27 +0200219 return NULL;
Christopher Faulet778f5ed2020-04-29 15:51:55 +0200220 }
Christopher Fauletef453ed2018-10-24 21:39:27 +0200221
Christopher Faulet75f619a2021-03-08 19:12:58 +0100222 if (!s->txn && !http_create_txn(s))
223 return NULL;
Christopher Faulet89dc4992019-04-17 12:02:59 +0200224 txn = s->txn;
225 msg = (!(chn->flags & CF_ISRESP) ? &txn->req : &txn->rsp);
Christopher Fauletef453ed2018-10-24 21:39:27 +0200226
Christopher Fauleteca88542019-04-03 10:12:42 +0200227 if (IS_HTX_STRM(s)) {
Christopher Faulet89dc4992019-04-17 12:02:59 +0200228 htx = htxbuf(&chn->buf);
Christopher Fauletef453ed2018-10-24 21:39:27 +0200229
Christopher Faulet89dc4992019-04-17 12:02:59 +0200230 if (msg->msg_state == HTTP_MSG_ERROR || (htx->flags & HTX_FL_PARSING_ERROR))
231 return NULL;
Christopher Fauletef453ed2018-10-24 21:39:27 +0200232
Christopher Faulet89dc4992019-04-17 12:02:59 +0200233 if (msg->msg_state < HTTP_MSG_BODY) {
234 /* Analyse not yet started */
Christopher Faulet29f17582019-05-23 11:03:26 +0200235 if (htx_is_empty(htx) || htx->first == -1) {
Christopher Fauletef453ed2018-10-24 21:39:27 +0200236 /* Parsing is done by the mux, just wait */
237 smp->flags |= SMP_F_MAY_CHANGE;
238 return NULL;
239 }
240 }
Christopher Faulet297fbb42019-05-13 14:41:27 +0200241 sl = http_get_stline(htx);
Christopher Faulet5ec8bcb2019-04-17 12:04:12 +0200242 if (vol && !sl) {
Christopher Faulet89dc4992019-04-17 12:02:59 +0200243 /* The start-line was already forwarded, it is too late to fetch anything */
244 return NULL;
245 }
Christopher Fauletef453ed2018-10-24 21:39:27 +0200246 }
Christopher Fauleteca88542019-04-03 10:12:42 +0200247 else { /* RAW mode */
Christopher Faulet89dc4992019-04-17 12:02:59 +0200248 struct buffer *buf;
249 struct h1m h1m;
Christopher Faulete4ab11b2019-06-11 15:05:37 +0200250 struct http_hdr hdrs[global.tune.max_http_hdr];
Christopher Faulet89dc4992019-04-17 12:02:59 +0200251 union h1_sl h1sl;
252 unsigned int flags = HTX_FL_NONE;
253 int ret;
Christopher Fauletef453ed2018-10-24 21:39:27 +0200254
Christopher Faulet89dc4992019-04-17 12:02:59 +0200255 /* no HTTP fetch on the response in TCP mode */
256 if (chn->flags & CF_ISRESP)
257 return NULL;
Christopher Fauletef453ed2018-10-24 21:39:27 +0200258
Christopher Faulet89dc4992019-04-17 12:02:59 +0200259 /* Now we are working on the request only */
260 buf = &chn->buf;
261 if (b_head(buf) + b_data(buf) > b_wrap(buf))
262 b_slow_realign(buf, trash.area, 0);
Christopher Fauletef453ed2018-10-24 21:39:27 +0200263
Christopher Faulet89dc4992019-04-17 12:02:59 +0200264 h1m_init_req(&h1m);
265 ret = h1_headers_to_hdr_list(b_head(buf), b_stop(buf),
266 hdrs, sizeof(hdrs)/sizeof(hdrs[0]), &h1m, &h1sl);
267 if (ret <= 0) {
268 /* Invalid or too big*/
269 if (ret < 0 || channel_full(&s->req, global.tune.maxrewrite))
Christopher Fauletef453ed2018-10-24 21:39:27 +0200270 return NULL;
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100271
Christopher Faulet89dc4992019-04-17 12:02:59 +0200272 /* wait for a full request */
273 smp->flags |= SMP_F_MAY_CHANGE;
274 return NULL;
275 }
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100276
Ilya Shipitsin46a030c2020-07-05 16:36:08 +0500277 /* OK we just got a valid HTTP message. We have to convert it
Christopher Faulet89dc4992019-04-17 12:02:59 +0200278 * into an HTX message.
279 */
280 if (unlikely(h1sl.rq.v.len == 0)) {
281 /* try to convert HTTP/0.9 requests to HTTP/1.0 */
282 if (h1sl.rq.meth != HTTP_METH_GET || !h1sl.rq.u.len)
Christopher Fauletef453ed2018-10-24 21:39:27 +0200283 return NULL;
Christopher Faulet89dc4992019-04-17 12:02:59 +0200284 h1sl.rq.v = ist("HTTP/1.0");
Christopher Fauletef453ed2018-10-24 21:39:27 +0200285 }
Christopher Faulet89dc4992019-04-17 12:02:59 +0200286
287 /* Set HTX start-line flags */
288 if (h1m.flags & H1_MF_VER_11)
289 flags |= HTX_SL_F_VER_11;
290 if (h1m.flags & H1_MF_XFER_ENC)
291 flags |= HTX_SL_F_XFER_ENC;
292 flags |= HTX_SL_F_XFER_LEN;
293 if (h1m.flags & H1_MF_CHNK)
294 flags |= HTX_SL_F_CHNK;
295 else if (h1m.flags & H1_MF_CLEN)
296 flags |= HTX_SL_F_CLEN;
297
Richard Russo458eafb2019-07-31 11:45:56 -0700298 htx = htx_from_buf(get_raw_htx_chunk());
Christopher Faulet89dc4992019-04-17 12:02:59 +0200299 sl = htx_add_stline(htx, HTX_BLK_REQ_SL, flags, h1sl.rq.m, h1sl.rq.u, h1sl.rq.v);
300 if (!sl || !htx_add_all_headers(htx, hdrs))
Christopher Fauletef453ed2018-10-24 21:39:27 +0200301 return NULL;
Willy Tarreauce9bbf52019-05-13 08:32:31 +0200302 sl->info.req.meth = h1sl.rq.meth;
Christopher Faulet89dc4992019-04-17 12:02:59 +0200303 }
304
305 /* OK we just got a valid HTTP message. If not already done by
306 * HTTP analyzers, we have some minor preparation to perform so
307 * that further checks can rely on HTTP tests.
308 */
309 if (sl && msg->msg_state < HTTP_MSG_BODY) {
310 if (!(chn->flags & CF_ISRESP)) {
311 txn->meth = sl->info.req.meth;
312 if (txn->meth == HTTP_METH_GET || txn->meth == HTTP_METH_HEAD)
313 s->flags |= SF_REDIRECTABLE;
Christopher Fauletef453ed2018-10-24 21:39:27 +0200314 }
Christopher Faulet89dc4992019-04-17 12:02:59 +0200315 else
316 txn->status = sl->info.res.status;
317 if (sl->flags & HTX_SL_F_VER_11)
318 msg->flags |= HTTP_MSGF_VER_11;
Christopher Fauletef453ed2018-10-24 21:39:27 +0200319 }
320
321 /* everything's OK */
Christopher Faulet778f5ed2020-04-29 15:51:55 +0200322 end:
Christopher Fauletef453ed2018-10-24 21:39:27 +0200323 return htx;
324}
325
Willy Tarreau79e57332018-10-02 16:01:16 +0200326/* This function fetches the method of current HTTP request and stores
327 * it in the global pattern struct as a chunk. There are two possibilities :
328 * - if the method is known (not HTTP_METH_OTHER), its identifier is stored
329 * in <len> and <ptr> is NULL ;
330 * - if the method is unknown (HTTP_METH_OTHER), <ptr> points to the text and
331 * <len> to its length.
332 * This is intended to be used with pat_match_meth() only.
333 */
334static int smp_fetch_meth(const struct arg *args, struct sample *smp, const char *kw, void *private)
335{
Christopher Faulet89dc4992019-04-17 12:02:59 +0200336 struct channel *chn = SMP_REQ_CHN(smp);
Willy Tarreau79e57332018-10-02 16:01:16 +0200337 struct http_txn *txn;
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200338 int meth;
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200339
Willy Tarreaua6d98792020-08-12 14:04:52 +0200340 txn = smp->strm->txn;
341 if (!txn)
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200342 return 0;
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200343
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200344 meth = txn->meth;
345 smp->data.type = SMP_T_METH;
346 smp->data.u.meth.meth = meth;
347 if (meth == HTTP_METH_OTHER) {
Willy Tarreaua6d98792020-08-12 14:04:52 +0200348 struct htx *htx;
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200349 struct htx_sl *sl;
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200350
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200351 if ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) {
352 /* ensure the indexes are not affected */
353 return 0;
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200354 }
Willy Tarreaua6d98792020-08-12 14:04:52 +0200355
Christopher Faulet6f97a612021-04-15 09:28:02 +0200356 htx = smp_prefetch_htx(smp, chn, NULL, 1);
Willy Tarreaua6d98792020-08-12 14:04:52 +0200357 if (!htx)
358 return 0;
359
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200360 sl = http_get_stline(htx);
361 smp->flags |= SMP_F_CONST;
362 smp->data.u.meth.str.area = HTX_SL_REQ_MPTR(sl);
363 smp->data.u.meth.str.data = HTX_SL_REQ_MLEN(sl);
Willy Tarreau79e57332018-10-02 16:01:16 +0200364 }
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200365 smp->flags |= SMP_F_VOL_1ST;
Willy Tarreau79e57332018-10-02 16:01:16 +0200366 return 1;
367}
368
369static int smp_fetch_rqver(const struct arg *args, struct sample *smp, const char *kw, void *private)
370{
Christopher Faulet89dc4992019-04-17 12:02:59 +0200371 struct channel *chn = SMP_REQ_CHN(smp);
Christopher Faulet778f5ed2020-04-29 15:51:55 +0200372 struct htx *htx = smp_prefetch_htx(smp, chn, NULL, 1);
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200373 struct htx_sl *sl;
Willy Tarreau79e57332018-10-02 16:01:16 +0200374 char *ptr;
375 int len;
376
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200377 if (!htx)
378 return 0;
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200379
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200380 sl = http_get_stline(htx);
381 len = HTX_SL_REQ_VLEN(sl);
382 ptr = HTX_SL_REQ_VPTR(sl);
Willy Tarreau79e57332018-10-02 16:01:16 +0200383
384 while ((len-- > 0) && (*ptr++ != '/'));
385 if (len <= 0)
386 return 0;
387
388 smp->data.type = SMP_T_STR;
389 smp->data.u.str.area = ptr;
390 smp->data.u.str.data = len;
391
392 smp->flags = SMP_F_VOL_1ST | SMP_F_CONST;
393 return 1;
394}
395
396static int smp_fetch_stver(const struct arg *args, struct sample *smp, const char *kw, void *private)
397{
Christopher Faulet89dc4992019-04-17 12:02:59 +0200398 struct channel *chn = SMP_RES_CHN(smp);
Christopher Fauletf98e6262020-05-06 09:42:04 +0200399 struct check *check = objt_check(smp->sess->origin);
Christopher Faulet16032ab2020-04-30 11:30:00 +0200400 struct htx *htx = smp_prefetch_htx(smp, chn, check, 1);
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200401 struct htx_sl *sl;
Willy Tarreau79e57332018-10-02 16:01:16 +0200402 char *ptr;
403 int len;
404
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200405 if (!htx)
406 return 0;
Willy Tarreau79e57332018-10-02 16:01:16 +0200407
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200408 sl = http_get_stline(htx);
409 len = HTX_SL_RES_VLEN(sl);
410 ptr = HTX_SL_RES_VPTR(sl);
Willy Tarreau79e57332018-10-02 16:01:16 +0200411
412 while ((len-- > 0) && (*ptr++ != '/'));
413 if (len <= 0)
414 return 0;
415
416 smp->data.type = SMP_T_STR;
417 smp->data.u.str.area = ptr;
418 smp->data.u.str.data = len;
419
420 smp->flags = SMP_F_VOL_1ST | SMP_F_CONST;
421 return 1;
422}
423
424/* 3. Check on Status Code. We manipulate integers here. */
425static int smp_fetch_stcode(const struct arg *args, struct sample *smp, const char *kw, void *private)
426{
Christopher Faulet89dc4992019-04-17 12:02:59 +0200427 struct channel *chn = SMP_RES_CHN(smp);
Christopher Fauletf98e6262020-05-06 09:42:04 +0200428 struct check *check = objt_check(smp->sess->origin);
Christopher Faulet16032ab2020-04-30 11:30:00 +0200429 struct htx *htx = smp_prefetch_htx(smp, chn, check, 1);
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200430 struct htx_sl *sl;
Willy Tarreau79e57332018-10-02 16:01:16 +0200431 char *ptr;
432 int len;
433
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200434 if (!htx)
435 return 0;
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200436
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200437 sl = http_get_stline(htx);
438 len = HTX_SL_RES_CLEN(sl);
439 ptr = HTX_SL_RES_CPTR(sl);
Willy Tarreau79e57332018-10-02 16:01:16 +0200440
441 smp->data.type = SMP_T_SINT;
442 smp->data.u.sint = __strl2ui(ptr, len);
443 smp->flags = SMP_F_VOL_1ST;
444 return 1;
445}
446
447static int smp_fetch_uniqueid(const struct arg *args, struct sample *smp, const char *kw, void *private)
448{
Tim Duesterhusa17e6622020-03-05 20:19:02 +0100449 struct ist unique_id;
Tim Duesterhus2825b4b2020-02-28 15:13:34 +0100450
Willy Tarreau79e57332018-10-02 16:01:16 +0200451 if (LIST_ISEMPTY(&smp->sess->fe->format_unique_id))
452 return 0;
453
Willy Tarreaua1062a42020-04-29 11:50:38 +0200454 if (!smp->strm)
455 return 0;
456
Tim Duesterhusa17e6622020-03-05 20:19:02 +0100457 unique_id = stream_generate_unique_id(smp->strm, &smp->sess->fe->format_unique_id);
458 if (!isttest(unique_id))
Tim Duesterhus2825b4b2020-02-28 15:13:34 +0100459 return 0;
460
Tim Duesterhusa17e6622020-03-05 20:19:02 +0100461 smp->data.u.str.area = smp->strm->unique_id.ptr;
462 smp->data.u.str.data = smp->strm->unique_id.len;
Tim Duesterhus2825b4b2020-02-28 15:13:34 +0100463 smp->data.type = SMP_T_STR;
Willy Tarreau79e57332018-10-02 16:01:16 +0200464 smp->flags = SMP_F_CONST;
465 return 1;
466}
467
468/* Returns a string block containing all headers including the
Joseph Herlant942eea32018-11-15 13:57:22 -0800469 * empty line which separes headers from the body. This is useful
470 * for some headers analysis.
Willy Tarreau79e57332018-10-02 16:01:16 +0200471 */
472static int smp_fetch_hdrs(const struct arg *args, struct sample *smp, const char *kw, void *private)
473{
Christopher Faulete596d182020-05-05 17:46:34 +0200474 /* possible keywords: req.hdrs, res.hdrs */
475 struct channel *chn = ((kw[2] == 'q') ? SMP_REQ_CHN(smp) : SMP_RES_CHN(smp));
Christopher Fauletf98e6262020-05-06 09:42:04 +0200476 struct check *check = ((kw[2] == 's') ? objt_check(smp->sess->origin) : NULL);
Christopher Faulet16032ab2020-04-30 11:30:00 +0200477 struct htx *htx = smp_prefetch_htx(smp, chn, check, 1);
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200478 struct buffer *temp;
479 int32_t pos;
Willy Tarreau79e57332018-10-02 16:01:16 +0200480
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200481 if (!htx)
482 return 0;
483 temp = get_trash_chunk();
484 for (pos = htx_get_first(htx); pos != -1; pos = htx_get_next(htx, pos)) {
485 struct htx_blk *blk = htx_get_blk(htx, pos);
486 enum htx_blk_type type = htx_get_blk_type(blk);
Willy Tarreau79e57332018-10-02 16:01:16 +0200487
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200488 if (type == HTX_BLK_HDR) {
489 struct ist n = htx_get_blk_name(htx, blk);
490 struct ist v = htx_get_blk_value(htx, blk);
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200491
Christopher Faulet53a899b2019-10-08 16:38:42 +0200492 if (!h1_format_htx_hdr(n, v, temp))
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200493 return 0;
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200494 }
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200495 else if (type == HTX_BLK_EOH) {
496 if (!chunk_memcat(temp, "\r\n", 2))
497 return 0;
498 break;
499 }
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200500 }
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200501 smp->data.type = SMP_T_STR;
502 smp->data.u.str = *temp;
Willy Tarreau79e57332018-10-02 16:01:16 +0200503 return 1;
504}
505
506/* Returns the header request in a length/value encoded format.
507 * This is useful for exchanges with the SPOE.
508 *
509 * A "length value" is a multibyte code encoding numbers. It uses the
510 * SPOE format. The encoding is the following:
511 *
512 * Each couple "header name" / "header value" is composed
513 * like this:
514 * "length value" "header name bytes"
515 * "length value" "header value bytes"
516 * When the last header is reached, the header name and the header
517 * value are empty. Their length are 0
518 */
519static int smp_fetch_hdrs_bin(const struct arg *args, struct sample *smp, const char *kw, void *private)
520{
Christopher Faulete596d182020-05-05 17:46:34 +0200521 /* possible keywords: req.hdrs_bin, res.hdrs_bin */
522 struct channel *chn = ((kw[2] == 'q') ? SMP_REQ_CHN(smp) : SMP_RES_CHN(smp));
Christopher Fauletf98e6262020-05-06 09:42:04 +0200523 struct check *check = ((kw[2] == 's') ? objt_check(smp->sess->origin) : NULL);
Christopher Faulet16032ab2020-04-30 11:30:00 +0200524 struct htx *htx = smp_prefetch_htx(smp, chn, check, 1);
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200525 struct buffer *temp;
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200526 char *p, *end;
527 int32_t pos;
528 int ret;
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200529
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200530 if (!htx)
531 return 0;
532 temp = get_trash_chunk();
533 p = temp->area;
534 end = temp->area + temp->size;
535 for (pos = htx_get_first(htx); pos != -1; pos = htx_get_next(htx, pos)) {
536 struct htx_blk *blk = htx_get_blk(htx, pos);
537 enum htx_blk_type type = htx_get_blk_type(blk);
538 struct ist n, v;
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200539
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200540 if (type == HTX_BLK_HDR) {
541 n = htx_get_blk_name(htx,blk);
542 v = htx_get_blk_value(htx, blk);
Willy Tarreau79e57332018-10-02 16:01:16 +0200543
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200544 /* encode the header name. */
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200545 ret = encode_varint(n.len, &p, end);
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200546 if (ret == -1)
547 return 0;
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200548 if (p + n.len > end)
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200549 return 0;
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200550 memcpy(p, n.ptr, n.len);
551 p += n.len;
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200552
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200553 /* encode the header value. */
554 ret = encode_varint(v.len, &p, end);
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200555 if (ret == -1)
556 return 0;
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200557 if (p + v.len > end)
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200558 return 0;
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200559 memcpy(p, v.ptr, v.len);
560 p += v.len;
Willy Tarreau79e57332018-10-02 16:01:16 +0200561
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200562 }
563 else if (type == HTX_BLK_EOH) {
564 /* encode the end of the header list with empty
565 * header name and header value.
566 */
567 ret = encode_varint(0, &p, end);
568 if (ret == -1)
569 return 0;
570 ret = encode_varint(0, &p, end);
571 if (ret == -1)
572 return 0;
573 break;
574 }
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200575 }
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200576
577 /* Initialise sample data which will be filled. */
578 smp->data.type = SMP_T_BIN;
579 smp->data.u.str.area = temp->area;
580 smp->data.u.str.data = p - temp->area;
581 smp->data.u.str.size = temp->size;
Willy Tarreau79e57332018-10-02 16:01:16 +0200582 return 1;
583}
584
585/* returns the longest available part of the body. This requires that the body
586 * has been waited for using http-buffer-request.
587 */
588static int smp_fetch_body(const struct arg *args, struct sample *smp, const char *kw, void *private)
589{
Christopher Faulete596d182020-05-05 17:46:34 +0200590 /* possible keywords: req.body, res.body */
591 struct channel *chn = ((kw[2] == 'q') ? SMP_REQ_CHN(smp) : SMP_RES_CHN(smp));
Christopher Fauletf98e6262020-05-06 09:42:04 +0200592 struct check *check = ((kw[2] == 's') ? objt_check(smp->sess->origin) : NULL);
Christopher Faulet16032ab2020-04-30 11:30:00 +0200593 struct htx *htx = smp_prefetch_htx(smp, chn, check, 1);
Willy Tarreau79e57332018-10-02 16:01:16 +0200594 struct buffer *temp;
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200595 int32_t pos;
Christopher Fauleta9ffc412020-11-25 08:08:08 +0100596 int finished = 0;
Willy Tarreau79e57332018-10-02 16:01:16 +0200597
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200598 if (!htx)
599 return 0;
Willy Tarreau79e57332018-10-02 16:01:16 +0200600
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200601 temp = get_trash_chunk();
602 for (pos = htx_get_first(htx); pos != -1; pos = htx_get_next(htx, pos)) {
603 struct htx_blk *blk = htx_get_blk(htx, pos);
604 enum htx_blk_type type = htx_get_blk_type(blk);
Willy Tarreau79e57332018-10-02 16:01:16 +0200605
Christopher Fauletd1ac2b92020-12-02 19:12:22 +0100606 if (type == HTX_BLK_TLR || type == HTX_BLK_EOT) {
Christopher Fauleta9ffc412020-11-25 08:08:08 +0100607 finished = 1;
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200608 break;
Christopher Fauleta9ffc412020-11-25 08:08:08 +0100609 }
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200610 if (type == HTX_BLK_DATA) {
Christopher Faulet53a899b2019-10-08 16:38:42 +0200611 if (!h1_format_htx_data(htx_get_blk_value(htx, blk), temp, 0))
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200612 return 0;
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200613 }
Willy Tarreau79e57332018-10-02 16:01:16 +0200614 }
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200615
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200616 smp->data.type = SMP_T_BIN;
617 smp->data.u.str = *temp;
618 smp->flags = SMP_F_VOL_TEST;
Willy Tarreau9dc92b22020-06-15 18:01:10 +0200619
Christopher Fauleta9ffc412020-11-25 08:08:08 +0100620 if (!finished && (check || (chn && !channel_full(chn, global.tune.maxrewrite) &&
621 !(chn->flags & (CF_EOI|CF_SHUTR|CF_READ_ERROR)))))
Willy Tarreau9dc92b22020-06-15 18:01:10 +0200622 smp->flags |= SMP_F_MAY_CHANGE;
623
Willy Tarreau79e57332018-10-02 16:01:16 +0200624 return 1;
625}
626
627
628/* returns the available length of the body. This requires that the body
629 * has been waited for using http-buffer-request.
630 */
631static int smp_fetch_body_len(const struct arg *args, struct sample *smp, const char *kw, void *private)
632{
Christopher Faulete596d182020-05-05 17:46:34 +0200633 /* possible keywords: req.body_len, res.body_len */
634 struct channel *chn = ((kw[2] == 'q') ? SMP_REQ_CHN(smp) : SMP_RES_CHN(smp));
Christopher Fauletf98e6262020-05-06 09:42:04 +0200635 struct check *check = ((kw[2] == 's') ? objt_check(smp->sess->origin) : NULL);
Christopher Faulet16032ab2020-04-30 11:30:00 +0200636 struct htx *htx = smp_prefetch_htx(smp, chn, check, 1);
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200637 int32_t pos;
638 unsigned long long len = 0;
Christopher Fauletc16317d2018-12-12 14:11:22 +0100639
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200640 if (!htx)
641 return 0;
Christopher Fauletc16317d2018-12-12 14:11:22 +0100642
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200643 for (pos = htx_get_first(htx); pos != -1; pos = htx_get_next(htx, pos)) {
644 struct htx_blk *blk = htx_get_blk(htx, pos);
645 enum htx_blk_type type = htx_get_blk_type(blk);
Christopher Fauletc16317d2018-12-12 14:11:22 +0100646
Christopher Fauletd1ac2b92020-12-02 19:12:22 +0100647 if (type == HTX_BLK_TLR || type == HTX_BLK_EOT)
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200648 break;
649 if (type == HTX_BLK_DATA)
650 len += htx_get_blksz(blk);
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200651 }
Willy Tarreau79e57332018-10-02 16:01:16 +0200652
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200653 smp->data.type = SMP_T_SINT;
654 smp->data.u.sint = len;
655 smp->flags = SMP_F_VOL_TEST;
Willy Tarreau79e57332018-10-02 16:01:16 +0200656 return 1;
657}
658
659
660/* returns the advertised length of the body, or the advertised size of the
661 * chunks available in the buffer. This requires that the body has been waited
662 * for using http-buffer-request.
663 */
664static int smp_fetch_body_size(const struct arg *args, struct sample *smp, const char *kw, void *private)
665{
Christopher Faulete596d182020-05-05 17:46:34 +0200666 /* possible keywords: req.body_size, res.body_size */
667 struct channel *chn = ((kw[2] == 'q') ? SMP_REQ_CHN(smp) : SMP_RES_CHN(smp));
Christopher Fauletf98e6262020-05-06 09:42:04 +0200668 struct check *check = ((kw[2] == 's') ? objt_check(smp->sess->origin) : NULL);
Christopher Faulet16032ab2020-04-30 11:30:00 +0200669 struct htx *htx = smp_prefetch_htx(smp, chn, check, 1);
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200670 int32_t pos;
671 unsigned long long len = 0;
Christopher Faulet89dc4992019-04-17 12:02:59 +0200672
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200673 if (!htx)
674 return 0;
Christopher Fauletc16317d2018-12-12 14:11:22 +0100675
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200676 for (pos = htx_get_first(htx); pos != -1; pos = htx_get_next(htx, pos)) {
677 struct htx_blk *blk = htx_get_blk(htx, pos);
678 enum htx_blk_type type = htx_get_blk_type(blk);
Christopher Fauletc16317d2018-12-12 14:11:22 +0100679
Christopher Fauletd1ac2b92020-12-02 19:12:22 +0100680 if (type == HTX_BLK_TLR || type == HTX_BLK_EOT)
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200681 break;
682 if (type == HTX_BLK_DATA)
683 len += htx_get_blksz(blk);
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200684 }
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200685 if (htx->extra != ULLONG_MAX)
686 len += htx->extra;
Willy Tarreau79e57332018-10-02 16:01:16 +0200687
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200688 smp->data.type = SMP_T_SINT;
689 smp->data.u.sint = len;
690 smp->flags = SMP_F_VOL_TEST;
Willy Tarreau79e57332018-10-02 16:01:16 +0200691 return 1;
692}
693
694
695/* 4. Check on URL/URI. A pointer to the URI is stored. */
696static int smp_fetch_url(const struct arg *args, struct sample *smp, const char *kw, void *private)
697{
Christopher Faulet89dc4992019-04-17 12:02:59 +0200698 struct channel *chn = SMP_REQ_CHN(smp);
Christopher Faulet778f5ed2020-04-29 15:51:55 +0200699 struct htx *htx = smp_prefetch_htx(smp, chn, NULL, 1);
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200700 struct htx_sl *sl;
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200701
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200702 if (!htx)
703 return 0;
704 sl = http_get_stline(htx);
705 smp->data.type = SMP_T_STR;
706 smp->data.u.str.area = HTX_SL_REQ_UPTR(sl);
707 smp->data.u.str.data = HTX_SL_REQ_ULEN(sl);
708 smp->flags = SMP_F_VOL_1ST | SMP_F_CONST;
Willy Tarreau79e57332018-10-02 16:01:16 +0200709 return 1;
710}
711
712static int smp_fetch_url_ip(const struct arg *args, struct sample *smp, const char *kw, void *private)
713{
Christopher Faulet89dc4992019-04-17 12:02:59 +0200714 struct channel *chn = SMP_REQ_CHN(smp);
Christopher Faulet778f5ed2020-04-29 15:51:55 +0200715 struct htx *htx = smp_prefetch_htx(smp, chn, NULL, 1);
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200716 struct htx_sl *sl;
Willy Tarreau79e57332018-10-02 16:01:16 +0200717 struct sockaddr_storage addr;
718
Amaury Denoyellec89d5332021-05-10 11:23:34 +0200719 memset(&addr, 0, sizeof(addr));
720
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200721 if (!htx)
722 return 0;
723 sl = http_get_stline(htx);
Amaury Denoyellec89d5332021-05-10 11:23:34 +0200724 if (url2sa(HTX_SL_REQ_UPTR(sl), HTX_SL_REQ_ULEN(sl), &addr, NULL) < 0)
725 return 0;
Willy Tarreau79e57332018-10-02 16:01:16 +0200726
Willy Tarreau48584642021-05-09 10:32:54 +0200727 if (addr.ss_family != AF_INET)
Willy Tarreau79e57332018-10-02 16:01:16 +0200728 return 0;
729
730 smp->data.type = SMP_T_IPV4;
731 smp->data.u.ipv4 = ((struct sockaddr_in *)&addr)->sin_addr;
732 smp->flags = 0;
733 return 1;
734}
735
736static int smp_fetch_url_port(const struct arg *args, struct sample *smp, const char *kw, void *private)
737{
Christopher Faulet89dc4992019-04-17 12:02:59 +0200738 struct channel *chn = SMP_REQ_CHN(smp);
Christopher Faulet778f5ed2020-04-29 15:51:55 +0200739 struct htx *htx = smp_prefetch_htx(smp, chn, NULL, 1);
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200740 struct htx_sl *sl;
Willy Tarreau79e57332018-10-02 16:01:16 +0200741 struct sockaddr_storage addr;
742
Amaury Denoyellec89d5332021-05-10 11:23:34 +0200743 memset(&addr, 0, sizeof(addr));
744
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200745 if (!htx)
746 return 0;
747 sl = http_get_stline(htx);
Amaury Denoyellec89d5332021-05-10 11:23:34 +0200748 if (url2sa(HTX_SL_REQ_UPTR(sl), HTX_SL_REQ_ULEN(sl), &addr, NULL) < 0)
749 return 0;
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200750
Willy Tarreau48584642021-05-09 10:32:54 +0200751 if (addr.ss_family != AF_INET)
Willy Tarreau79e57332018-10-02 16:01:16 +0200752 return 0;
753
754 smp->data.type = SMP_T_SINT;
Willy Tarreau48584642021-05-09 10:32:54 +0200755 smp->data.u.sint = get_host_port(&addr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200756 smp->flags = 0;
757 return 1;
758}
759
760/* Fetch an HTTP header. A pointer to the beginning of the value is returned.
761 * Accepts an optional argument of type string containing the header field name,
762 * and an optional argument of type signed or unsigned integer to request an
763 * explicit occurrence of the header. Note that in the event of a missing name,
764 * headers are considered from the first one. It does not stop on commas and
765 * returns full lines instead (useful for User-Agent or Date for example).
766 */
767static int smp_fetch_fhdr(const struct arg *args, struct sample *smp, const char *kw, void *private)
768{
Christopher Faulet89dc4992019-04-17 12:02:59 +0200769 /* possible keywords: req.fhdr, res.fhdr */
770 struct channel *chn = ((kw[2] == 'q') ? SMP_REQ_CHN(smp) : SMP_RES_CHN(smp));
Christopher Fauletf98e6262020-05-06 09:42:04 +0200771 struct check *check = ((kw[2] == 's') ? objt_check(smp->sess->origin) : NULL);
Christopher Faulet16032ab2020-04-30 11:30:00 +0200772 struct htx *htx = smp_prefetch_htx(smp, chn, check, 1);
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200773 struct http_hdr_ctx *ctx = smp->ctx.a[0];
774 struct ist name;
Willy Tarreau79e57332018-10-02 16:01:16 +0200775 int occ = 0;
Willy Tarreau79e57332018-10-02 16:01:16 +0200776
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200777 if (!ctx) {
778 /* first call */
779 ctx = &static_http_hdr_ctx;
780 ctx->blk = NULL;
781 smp->ctx.a[0] = ctx;
782 }
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200783
Christopher Faulet623af932021-01-29 11:22:15 +0100784 if (args[0].type != ARGT_STR)
785 return 0;
Tim Duesterhus92c696e2021-02-28 16:11:36 +0100786 name = ist2(args[0].data.str.area, args[0].data.str.data);
Willy Tarreau79e57332018-10-02 16:01:16 +0200787
Christopher Faulet623af932021-01-29 11:22:15 +0100788 if (args[1].type == ARGT_SINT)
789 occ = args[1].data.sint;
Willy Tarreau79e57332018-10-02 16:01:16 +0200790
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200791 if (!htx)
792 return 0;
Willy Tarreau79e57332018-10-02 16:01:16 +0200793
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200794 if (ctx && !(smp->flags & SMP_F_NOT_LAST))
795 /* search for header from the beginning */
796 ctx->blk = NULL;
Willy Tarreau79e57332018-10-02 16:01:16 +0200797
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200798 if (!occ && !(smp->opt & SMP_OPT_ITERATE))
799 /* no explicit occurrence and single fetch => last header by default */
800 occ = -1;
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200801
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200802 if (!occ)
803 /* prepare to report multiple occurrences for ACL fetches */
804 smp->flags |= SMP_F_NOT_LAST;
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200805
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200806 smp->data.type = SMP_T_STR;
807 smp->flags |= SMP_F_VOL_HDR | SMP_F_CONST;
808 if (http_get_htx_fhdr(htx, name, occ, ctx, &smp->data.u.str.area, &smp->data.u.str.data))
809 return 1;
Willy Tarreau79e57332018-10-02 16:01:16 +0200810 smp->flags &= ~SMP_F_NOT_LAST;
811 return 0;
812}
813
814/* 6. Check on HTTP header count. The number of occurrences is returned.
815 * Accepts exactly 1 argument of type string. It does not stop on commas and
816 * returns full lines instead (useful for User-Agent or Date for example).
817 */
818static int smp_fetch_fhdr_cnt(const struct arg *args, struct sample *smp, const char *kw, void *private)
819{
Christopher Faulet89dc4992019-04-17 12:02:59 +0200820 /* possible keywords: req.fhdr_cnt, res.fhdr_cnt */
821 struct channel *chn = ((kw[2] == 'q') ? SMP_REQ_CHN(smp) : SMP_RES_CHN(smp));
Christopher Fauletf98e6262020-05-06 09:42:04 +0200822 struct check *check = ((kw[2] == 's') ? objt_check(smp->sess->origin) : NULL);
Christopher Faulet16032ab2020-04-30 11:30:00 +0200823 struct htx *htx = smp_prefetch_htx(smp, chn, check, 1);
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200824 struct http_hdr_ctx ctx;
825 struct ist name;
Willy Tarreau79e57332018-10-02 16:01:16 +0200826 int cnt;
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200827
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200828 if (!htx)
829 return 0;
Willy Tarreau79e57332018-10-02 16:01:16 +0200830
Christopher Faulet623af932021-01-29 11:22:15 +0100831 if (args->type == ARGT_STR) {
Tim Duesterhus92c696e2021-02-28 16:11:36 +0100832 name = ist2(args->data.str.area, args->data.str.data);
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200833 } else {
Tim Duesterhus68a088d2021-02-28 16:11:37 +0100834 name = IST_NULL;
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200835 }
Willy Tarreau79e57332018-10-02 16:01:16 +0200836
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200837 ctx.blk = NULL;
838 cnt = 0;
839 while (http_find_header(htx, name, &ctx, 1))
840 cnt++;
Willy Tarreau79e57332018-10-02 16:01:16 +0200841 smp->data.type = SMP_T_SINT;
842 smp->data.u.sint = cnt;
843 smp->flags = SMP_F_VOL_HDR;
844 return 1;
845}
846
847static int smp_fetch_hdr_names(const struct arg *args, struct sample *smp, const char *kw, void *private)
848{
Christopher Faulet89dc4992019-04-17 12:02:59 +0200849 /* possible keywords: req.hdr_names, res.hdr_names */
850 struct channel *chn = ((kw[2] == 'q') ? SMP_REQ_CHN(smp) : SMP_RES_CHN(smp));
Christopher Fauletf98e6262020-05-06 09:42:04 +0200851 struct check *check = ((kw[2] == 's') ? objt_check(smp->sess->origin) : NULL);
Christopher Faulet16032ab2020-04-30 11:30:00 +0200852 struct htx *htx = smp_prefetch_htx(smp, chn, check, 1);
Willy Tarreau79e57332018-10-02 16:01:16 +0200853 struct buffer *temp;
854 char del = ',';
855
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200856 int32_t pos;
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200857
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200858 if (!htx)
859 return 0;
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200860
Christopher Faulet623af932021-01-29 11:22:15 +0100861 if (args->type == ARGT_STR)
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200862 del = *args[0].data.str.area;
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200863
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200864 temp = get_trash_chunk();
865 for (pos = htx_get_first(htx); pos != -1; pos = htx_get_next(htx, pos)) {
866 struct htx_blk *blk = htx_get_blk(htx, pos);
867 enum htx_blk_type type = htx_get_blk_type(blk);
868 struct ist n;
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200869
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200870 if (type == HTX_BLK_EOH)
871 break;
872 if (type != HTX_BLK_HDR)
873 continue;
874 n = htx_get_blk_name(htx, blk);
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200875
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200876 if (temp->data)
877 temp->area[temp->data++] = del;
Tim Duesterhus9f7ed8a2021-11-08 09:05:04 +0100878 chunk_istcat(temp, n);
Willy Tarreau79e57332018-10-02 16:01:16 +0200879 }
880
881 smp->data.type = SMP_T_STR;
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200882 smp->data.u.str = *temp;
Willy Tarreau79e57332018-10-02 16:01:16 +0200883 smp->flags = SMP_F_VOL_HDR;
884 return 1;
885}
886
887/* Fetch an HTTP header. A pointer to the beginning of the value is returned.
888 * Accepts an optional argument of type string containing the header field name,
889 * and an optional argument of type signed or unsigned integer to request an
890 * explicit occurrence of the header. Note that in the event of a missing name,
891 * headers are considered from the first one.
892 */
893static int smp_fetch_hdr(const struct arg *args, struct sample *smp, const char *kw, void *private)
894{
Christopher Faulet89dc4992019-04-17 12:02:59 +0200895 /* possible keywords: req.hdr / hdr, res.hdr / shdr */
896 struct channel *chn = ((kw[0] == 'h' || kw[2] == 'q') ? SMP_REQ_CHN(smp) : SMP_RES_CHN(smp));
Christopher Fauletf98e6262020-05-06 09:42:04 +0200897 struct check *check = ((kw[0] == 's' || kw[2] == 's') ? objt_check(smp->sess->origin) : NULL);
Christopher Faulet16032ab2020-04-30 11:30:00 +0200898 struct htx *htx = smp_prefetch_htx(smp, chn, check, 1);
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200899 struct http_hdr_ctx *ctx = smp->ctx.a[0];
900 struct ist name;
Willy Tarreau79e57332018-10-02 16:01:16 +0200901 int occ = 0;
Willy Tarreau79e57332018-10-02 16:01:16 +0200902
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200903 if (!ctx) {
904 /* first call */
905 ctx = &static_http_hdr_ctx;
906 ctx->blk = NULL;
907 smp->ctx.a[0] = ctx;
908 }
Willy Tarreau79e57332018-10-02 16:01:16 +0200909
Christopher Faulet623af932021-01-29 11:22:15 +0100910 if (args[0].type != ARGT_STR)
911 return 0;
Tim Duesterhus92c696e2021-02-28 16:11:36 +0100912 name = ist2(args[0].data.str.area, args[0].data.str.data);
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200913
Christopher Faulet623af932021-01-29 11:22:15 +0100914 if (args[1].type == ARGT_SINT)
915 occ = args[1].data.sint;
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200916
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200917 if (!htx)
918 return 0;
Willy Tarreau79e57332018-10-02 16:01:16 +0200919
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200920 if (ctx && !(smp->flags & SMP_F_NOT_LAST))
921 /* search for header from the beginning */
922 ctx->blk = NULL;
Willy Tarreau79e57332018-10-02 16:01:16 +0200923
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200924 if (!occ && !(smp->opt & SMP_OPT_ITERATE))
925 /* no explicit occurrence and single fetch => last header by default */
926 occ = -1;
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200927
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200928 if (!occ)
929 /* prepare to report multiple occurrences for ACL fetches */
930 smp->flags |= SMP_F_NOT_LAST;
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200931
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200932 smp->data.type = SMP_T_STR;
933 smp->flags |= SMP_F_VOL_HDR | SMP_F_CONST;
934 if (http_get_htx_hdr(htx, name, occ, ctx, &smp->data.u.str.area, &smp->data.u.str.data))
935 return 1;
Willy Tarreau79e57332018-10-02 16:01:16 +0200936
937 smp->flags &= ~SMP_F_NOT_LAST;
938 return 0;
939}
940
Christopher Fauletc1f40dd2019-05-16 10:07:30 +0200941/* Same than smp_fetch_hdr() but only relies on the sample direction to choose
942 * the right channel. So instead of duplicating the code, we just change the
943 * keyword and then fallback on smp_fetch_hdr().
944 */
945static int smp_fetch_chn_hdr(const struct arg *args, struct sample *smp, const char *kw, void *private)
946{
947 kw = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ ? "req.hdr" : "res.hdr");
948 return smp_fetch_hdr(args, smp, kw, private);
949}
950
Willy Tarreau79e57332018-10-02 16:01:16 +0200951/* 6. Check on HTTP header count. The number of occurrences is returned.
952 * Accepts exactly 1 argument of type string.
953 */
954static int smp_fetch_hdr_cnt(const struct arg *args, struct sample *smp, const char *kw, void *private)
955{
Christopher Faulet89dc4992019-04-17 12:02:59 +0200956 /* possible keywords: req.hdr_cnt / hdr_cnt, res.hdr_cnt / shdr_cnt */
957 struct channel *chn = ((kw[0] == 'h' || kw[2] == 'q') ? SMP_REQ_CHN(smp) : SMP_RES_CHN(smp));
Christopher Fauletf98e6262020-05-06 09:42:04 +0200958 struct check *check = ((kw[0] == 's' || kw[2] == 's') ? objt_check(smp->sess->origin) : NULL);
Christopher Faulet16032ab2020-04-30 11:30:00 +0200959 struct htx *htx = smp_prefetch_htx(smp, chn, check, 1);
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200960 struct http_hdr_ctx ctx;
961 struct ist name;
Willy Tarreau79e57332018-10-02 16:01:16 +0200962 int cnt;
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200963
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200964 if (!htx)
965 return 0;
Willy Tarreau79e57332018-10-02 16:01:16 +0200966
Christopher Faulet623af932021-01-29 11:22:15 +0100967 if (args->type == ARGT_STR) {
Tim Duesterhus92c696e2021-02-28 16:11:36 +0100968 name = ist2(args->data.str.area, args->data.str.data);
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200969 } else {
Tim Duesterhus68a088d2021-02-28 16:11:37 +0100970 name = IST_NULL;
Willy Tarreau79e57332018-10-02 16:01:16 +0200971 }
Willy Tarreau79e57332018-10-02 16:01:16 +0200972
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200973 ctx.blk = NULL;
974 cnt = 0;
975 while (http_find_header(htx, name, &ctx, 0))
976 cnt++;
Willy Tarreau79e57332018-10-02 16:01:16 +0200977
978 smp->data.type = SMP_T_SINT;
979 smp->data.u.sint = cnt;
980 smp->flags = SMP_F_VOL_HDR;
981 return 1;
982}
983
984/* Fetch an HTTP header's integer value. The integer value is returned. It
985 * takes a mandatory argument of type string and an optional one of type int
986 * to designate a specific occurrence. It returns an unsigned integer, which
987 * may or may not be appropriate for everything.
988 */
989static int smp_fetch_hdr_val(const struct arg *args, struct sample *smp, const char *kw, void *private)
990{
991 int ret = smp_fetch_hdr(args, smp, kw, private);
992
993 if (ret > 0) {
994 smp->data.type = SMP_T_SINT;
995 smp->data.u.sint = strl2ic(smp->data.u.str.area,
996 smp->data.u.str.data);
997 }
998
999 return ret;
1000}
1001
1002/* Fetch an HTTP header's IP value. takes a mandatory argument of type string
1003 * and an optional one of type int to designate a specific occurrence.
Willy Tarreau7b0e00d2021-03-25 14:12:29 +01001004 * It returns an IPv4 or IPv6 address. Addresses surrounded by invalid chars
1005 * are rejected. However IPv4 addresses may be followed with a colon and a
1006 * valid port number.
Willy Tarreau79e57332018-10-02 16:01:16 +02001007 */
1008static int smp_fetch_hdr_ip(const struct arg *args, struct sample *smp, const char *kw, void *private)
1009{
Tim Duesterhus5cd00872020-06-26 15:44:48 +02001010 struct buffer *temp = get_trash_chunk();
Willy Tarreau7b0e00d2021-03-25 14:12:29 +01001011 int ret, len;
1012 int port;
Willy Tarreau79e57332018-10-02 16:01:16 +02001013
1014 while ((ret = smp_fetch_hdr(args, smp, kw, private)) > 0) {
Tim Duesterhus5cd00872020-06-26 15:44:48 +02001015 if (smp->data.u.str.data < temp->size - 1) {
1016 memcpy(temp->area, smp->data.u.str.area,
1017 smp->data.u.str.data);
1018 temp->area[smp->data.u.str.data] = '\0';
Willy Tarreau7b0e00d2021-03-25 14:12:29 +01001019 len = url2ipv4((char *) temp->area, &smp->data.u.ipv4);
Willy Tarreau645dc082021-03-31 11:41:36 +02001020 if (len > 0 && len == smp->data.u.str.data) {
Willy Tarreau7b0e00d2021-03-25 14:12:29 +01001021 /* plain IPv4 address */
1022 smp->data.type = SMP_T_IPV4;
1023 break;
1024 } else if (len > 0 && temp->area[len] == ':' &&
1025 strl2irc(temp->area + len + 1, smp->data.u.str.data - len - 1, &port) == 0 &&
1026 port >= 0 && port <= 65535) {
1027 /* IPv4 address suffixed with ':' followed by a valid port number */
Tim Duesterhus5cd00872020-06-26 15:44:48 +02001028 smp->data.type = SMP_T_IPV4;
1029 break;
1030 } else if (inet_pton(AF_INET6, temp->area, &smp->data.u.ipv6)) {
1031 smp->data.type = SMP_T_IPV6;
1032 break;
Willy Tarreau79e57332018-10-02 16:01:16 +02001033 }
1034 }
1035
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001036 /* if the header doesn't match an IP address, fetch next one */
1037 if (!(smp->flags & SMP_F_NOT_LAST))
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001038 return 0;
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001039 }
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001040 return ret;
1041}
Willy Tarreau79e57332018-10-02 16:01:16 +02001042
Christopher Faulete720c322020-09-02 17:25:18 +02001043/* 8. Check on URI PATH. A pointer to the PATH is stored. The path starts at the
1044 * first '/' after the possible hostname. It ends before the possible '?' except
1045 * for 'pathq' keyword.
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001046 */
1047static int smp_fetch_path(const struct arg *args, struct sample *smp, const char *kw, void *private)
1048{
1049 struct channel *chn = SMP_REQ_CHN(smp);
Christopher Faulet778f5ed2020-04-29 15:51:55 +02001050 struct htx *htx = smp_prefetch_htx(smp, chn, NULL, 1);
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001051 struct htx_sl *sl;
1052 struct ist path;
Amaury Denoyellec453f952021-07-06 11:40:12 +02001053 struct http_uri_parser parser;
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001054
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001055 if (!htx)
1056 return 0;
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001057
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001058 sl = http_get_stline(htx);
Amaury Denoyellec453f952021-07-06 11:40:12 +02001059 parser = http_uri_parser_init(htx_sl_req_uri(sl));
Christopher Faulete720c322020-09-02 17:25:18 +02001060
Yves Lafonb4d37082021-02-11 11:01:28 +01001061 if (kw[4] == 'q' && (kw[0] == 'p' || kw[0] == 'b')) // pathq or baseq
Amaury Denoyellec453f952021-07-06 11:40:12 +02001062 path = http_parse_path(&parser);
Christopher Faulete720c322020-09-02 17:25:18 +02001063 else
Amaury Denoyellec453f952021-07-06 11:40:12 +02001064 path = iststop(http_parse_path(&parser), '?');
Christopher Faulete720c322020-09-02 17:25:18 +02001065
Tim Duesterhused526372020-03-05 17:56:33 +01001066 if (!isttest(path))
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001067 return 0;
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001068
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001069 /* OK, we got the '/' ! */
1070 smp->data.type = SMP_T_STR;
1071 smp->data.u.str.area = path.ptr;
Jerome Magnin4fb196c2020-02-21 10:49:12 +01001072 smp->data.u.str.data = path.len;
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001073 smp->flags = SMP_F_VOL_1ST | SMP_F_CONST;
Willy Tarreau79e57332018-10-02 16:01:16 +02001074 return 1;
1075}
1076
1077/* This produces a concatenation of the first occurrence of the Host header
1078 * followed by the path component if it begins with a slash ('/'). This means
1079 * that '*' will not be added, resulting in exactly the first Host entry.
1080 * If no Host header is found, then the path is returned as-is. The returned
1081 * value is stored in the trash so it does not need to be marked constant.
1082 * The returned sample is of type string.
1083 */
1084static int smp_fetch_base(const struct arg *args, struct sample *smp, const char *kw, void *private)
1085{
Christopher Faulet89dc4992019-04-17 12:02:59 +02001086 struct channel *chn = SMP_REQ_CHN(smp);
Christopher Faulet778f5ed2020-04-29 15:51:55 +02001087 struct htx *htx = smp_prefetch_htx(smp, chn, NULL, 1);
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001088 struct htx_sl *sl;
Willy Tarreau79e57332018-10-02 16:01:16 +02001089 struct buffer *temp;
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001090 struct http_hdr_ctx ctx;
1091 struct ist path;
Amaury Denoyellec453f952021-07-06 11:40:12 +02001092 struct http_uri_parser parser;
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001093
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001094 if (!htx)
1095 return 0;
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001096
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001097 ctx.blk = NULL;
1098 if (!http_find_header(htx, ist("Host"), &ctx, 0) || !ctx.value.len)
1099 return smp_fetch_path(args, smp, kw, private);
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001100
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001101 /* OK we have the header value in ctx.value */
1102 temp = get_trash_chunk();
Tim Duesterhus77508502022-03-15 13:11:06 +01001103 chunk_istcat(temp, ctx.value);
Willy Tarreau79e57332018-10-02 16:01:16 +02001104
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001105 /* now retrieve the path */
1106 sl = http_get_stline(htx);
Amaury Denoyellec453f952021-07-06 11:40:12 +02001107 parser = http_uri_parser_init(htx_sl_req_uri(sl));
1108 path = http_parse_path(&parser);
Tim Duesterhused526372020-03-05 17:56:33 +01001109 if (isttest(path)) {
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001110 size_t len;
Willy Tarreau79e57332018-10-02 16:01:16 +02001111
Yves Lafonb4d37082021-02-11 11:01:28 +01001112 if (kw[4] == 'q' && kw[0] == 'b') { // baseq
1113 len = path.len;
1114 } else {
1115 for (len = 0; len < path.len && *(path.ptr + len) != '?'; len++)
1116 ;
1117 }
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001118
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001119 if (len && *(path.ptr) == '/')
1120 chunk_memcat(temp, path.ptr, len);
Willy Tarreau79e57332018-10-02 16:01:16 +02001121 }
1122
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001123 smp->data.type = SMP_T_STR;
1124 smp->data.u.str = *temp;
Willy Tarreau79e57332018-10-02 16:01:16 +02001125 smp->flags = SMP_F_VOL_1ST;
1126 return 1;
1127}
1128
1129/* This produces a 32-bit hash of the concatenation of the first occurrence of
1130 * the Host header followed by the path component if it begins with a slash ('/').
1131 * This means that '*' will not be added, resulting in exactly the first Host
1132 * entry. If no Host header is found, then the path is used. The resulting value
1133 * is hashed using the path hash followed by a full avalanche hash and provides a
1134 * 32-bit integer value. This fetch is useful for tracking per-path activity on
1135 * high-traffic sites without having to store whole paths.
1136 */
1137static int smp_fetch_base32(const struct arg *args, struct sample *smp, const char *kw, void *private)
1138{
Christopher Faulet89dc4992019-04-17 12:02:59 +02001139 struct channel *chn = SMP_REQ_CHN(smp);
Christopher Faulet778f5ed2020-04-29 15:51:55 +02001140 struct htx *htx = smp_prefetch_htx(smp, chn, NULL, 1);
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001141 struct htx_sl *sl;
1142 struct http_hdr_ctx ctx;
1143 struct ist path;
Willy Tarreau79e57332018-10-02 16:01:16 +02001144 unsigned int hash = 0;
Amaury Denoyellec453f952021-07-06 11:40:12 +02001145 struct http_uri_parser parser;
Willy Tarreau79e57332018-10-02 16:01:16 +02001146
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001147 if (!htx)
1148 return 0;
Dragan Dosen8861e1c2019-02-12 19:50:31 +01001149
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001150 ctx.blk = NULL;
1151 if (http_find_header(htx, ist("Host"), &ctx, 0)) {
1152 /* OK we have the header value in ctx.value */
1153 while (ctx.value.len--)
1154 hash = *(ctx.value.ptr++) + (hash << 6) + (hash << 16) - hash;
Willy Tarreau79e57332018-10-02 16:01:16 +02001155 }
1156
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001157 /* now retrieve the path */
1158 sl = http_get_stline(htx);
Amaury Denoyellec453f952021-07-06 11:40:12 +02001159 parser = http_uri_parser_init(htx_sl_req_uri(sl));
1160 path = http_parse_path(&parser);
Tim Duesterhused526372020-03-05 17:56:33 +01001161 if (isttest(path)) {
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001162 size_t len;
Willy Tarreau79e57332018-10-02 16:01:16 +02001163
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001164 for (len = 0; len < path.len && *(path.ptr + len) != '?'; len++)
1165 ;
Willy Tarreau79e57332018-10-02 16:01:16 +02001166
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001167 if (len && *(path.ptr) == '/') {
1168 while (len--)
1169 hash = *(path.ptr++) + (hash << 6) + (hash << 16) - hash;
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001170 }
Willy Tarreau79e57332018-10-02 16:01:16 +02001171 }
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001172
Willy Tarreau79e57332018-10-02 16:01:16 +02001173 hash = full_hash(hash);
1174
1175 smp->data.type = SMP_T_SINT;
1176 smp->data.u.sint = hash;
1177 smp->flags = SMP_F_VOL_1ST;
1178 return 1;
1179}
1180
1181/* This concatenates the source address with the 32-bit hash of the Host and
1182 * path as returned by smp_fetch_base32(). The idea is to have per-source and
1183 * per-path counters. The result is a binary block from 8 to 20 bytes depending
1184 * on the source address length. The path hash is stored before the address so
1185 * that in environments where IPv6 is insignificant, truncating the output to
1186 * 8 bytes would still work.
1187 */
1188static int smp_fetch_base32_src(const struct arg *args, struct sample *smp, const char *kw, void *private)
1189{
Christopher Faulet8da67aa2022-03-29 17:53:09 +02001190 const struct sockaddr_storage *src = (smp->strm ? cs_src(smp->strm->csf) : NULL);
Willy Tarreau79e57332018-10-02 16:01:16 +02001191 struct buffer *temp;
Willy Tarreau79e57332018-10-02 16:01:16 +02001192
Christopher Faulet6fc817a2021-10-25 07:48:27 +02001193 if (!src)
Willy Tarreau79e57332018-10-02 16:01:16 +02001194 return 0;
1195
1196 if (!smp_fetch_base32(args, smp, kw, private))
1197 return 0;
1198
1199 temp = get_trash_chunk();
1200 *(unsigned int *) temp->area = htonl(smp->data.u.sint);
1201 temp->data += sizeof(unsigned int);
1202
Christopher Faulet6fc817a2021-10-25 07:48:27 +02001203 switch (src->ss_family) {
Willy Tarreau79e57332018-10-02 16:01:16 +02001204 case AF_INET:
1205 memcpy(temp->area + temp->data,
Christopher Faulet6fc817a2021-10-25 07:48:27 +02001206 &((struct sockaddr_in *)src)->sin_addr,
Willy Tarreau79e57332018-10-02 16:01:16 +02001207 4);
1208 temp->data += 4;
1209 break;
1210 case AF_INET6:
1211 memcpy(temp->area + temp->data,
Christopher Faulet6fc817a2021-10-25 07:48:27 +02001212 &((struct sockaddr_in6 *)src)->sin6_addr,
Willy Tarreau79e57332018-10-02 16:01:16 +02001213 16);
1214 temp->data += 16;
1215 break;
1216 default:
1217 return 0;
1218 }
1219
1220 smp->data.u.str = *temp;
1221 smp->data.type = SMP_T_BIN;
1222 return 1;
1223}
1224
1225/* Extracts the query string, which comes after the question mark '?'. If no
1226 * question mark is found, nothing is returned. Otherwise it returns a sample
1227 * of type string carrying the whole query string.
1228 */
1229static int smp_fetch_query(const struct arg *args, struct sample *smp, const char *kw, void *private)
1230{
Christopher Faulet89dc4992019-04-17 12:02:59 +02001231 struct channel *chn = SMP_REQ_CHN(smp);
Christopher Faulet778f5ed2020-04-29 15:51:55 +02001232 struct htx *htx = smp_prefetch_htx(smp, chn, NULL, 1);
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001233 struct htx_sl *sl;
Willy Tarreau79e57332018-10-02 16:01:16 +02001234 char *ptr, *end;
1235
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001236 if (!htx)
1237 return 0;
Willy Tarreau79e57332018-10-02 16:01:16 +02001238
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001239 sl = http_get_stline(htx);
1240 ptr = HTX_SL_REQ_UPTR(sl);
1241 end = HTX_SL_REQ_UPTR(sl) + HTX_SL_REQ_ULEN(sl);
Willy Tarreau79e57332018-10-02 16:01:16 +02001242
1243 /* look up the '?' */
1244 do {
1245 if (ptr == end)
1246 return 0;
1247 } while (*ptr++ != '?');
1248
1249 smp->data.type = SMP_T_STR;
1250 smp->data.u.str.area = ptr;
1251 smp->data.u.str.data = end - ptr;
1252 smp->flags = SMP_F_VOL_1ST | SMP_F_CONST;
1253 return 1;
1254}
1255
1256static int smp_fetch_proto_http(const struct arg *args, struct sample *smp, const char *kw, void *private)
1257{
Christopher Faulet89dc4992019-04-17 12:02:59 +02001258 struct channel *chn = SMP_REQ_CHN(smp);
Christopher Faulet778f5ed2020-04-29 15:51:55 +02001259 struct htx *htx = smp_prefetch_htx(smp, chn, NULL, 0);
Willy Tarreau79e57332018-10-02 16:01:16 +02001260
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001261 if (!htx)
1262 return 0;
1263 smp->data.type = SMP_T_BOOL;
Willy Tarreau79e57332018-10-02 16:01:16 +02001264 smp->data.u.sint = 1;
1265 return 1;
1266}
1267
1268/* return a valid test if the current request is the first one on the connection */
1269static int smp_fetch_http_first_req(const struct arg *args, struct sample *smp, const char *kw, void *private)
1270{
Willy Tarreau79512b62020-04-29 11:52:13 +02001271 if (!smp->strm)
1272 return 0;
1273
Willy Tarreau79e57332018-10-02 16:01:16 +02001274 smp->data.type = SMP_T_BOOL;
1275 smp->data.u.sint = !(smp->strm->txn->flags & TX_NOT_FIRST);
1276 return 1;
1277}
1278
Christopher Fauleta4063562019-08-02 11:51:37 +02001279/* Fetch the authentication method if there is an Authorization header. It
1280 * relies on get_http_auth()
1281 */
1282static int smp_fetch_http_auth_type(const struct arg *args, struct sample *smp, const char *kw, void *private)
1283{
1284 struct channel *chn = SMP_REQ_CHN(smp);
Christopher Faulet778f5ed2020-04-29 15:51:55 +02001285 struct htx *htx = smp_prefetch_htx(smp, chn, NULL, 1);
Christopher Fauleta4063562019-08-02 11:51:37 +02001286 struct http_txn *txn;
1287
1288 if (!htx)
1289 return 0;
1290
1291 txn = smp->strm->txn;
1292 if (!get_http_auth(smp, htx))
1293 return 0;
1294
1295 switch (txn->auth.method) {
1296 case HTTP_AUTH_BASIC:
1297 smp->data.u.str.area = "Basic";
1298 smp->data.u.str.data = 5;
1299 break;
1300 case HTTP_AUTH_DIGEST:
1301 /* Unexpected because not supported */
1302 smp->data.u.str.area = "Digest";
1303 smp->data.u.str.data = 6;
1304 break;
Remi Tricot-Le Bretonf5dd3372021-10-01 15:36:53 +02001305 case HTTP_AUTH_BEARER:
1306 smp->data.u.str.area = "Bearer";
1307 smp->data.u.str.data = 6;
1308 break;
Christopher Fauleta4063562019-08-02 11:51:37 +02001309 default:
1310 return 0;
1311 }
1312
1313 smp->data.type = SMP_T_STR;
1314 smp->flags = SMP_F_CONST;
1315 return 1;
1316}
1317
1318/* Fetch the user supplied if there is an Authorization header. It relies on
1319 * get_http_auth()
1320 */
1321static int smp_fetch_http_auth_user(const struct arg *args, struct sample *smp, const char *kw, void *private)
1322{
1323 struct channel *chn = SMP_REQ_CHN(smp);
Christopher Faulet778f5ed2020-04-29 15:51:55 +02001324 struct htx *htx = smp_prefetch_htx(smp, chn, NULL, 1);
Christopher Fauleta4063562019-08-02 11:51:37 +02001325 struct http_txn *txn;
1326
1327 if (!htx)
1328 return 0;
1329
1330 txn = smp->strm->txn;
Remi Tricot-Le Bretonf5dd3372021-10-01 15:36:53 +02001331 if (!get_http_auth(smp, htx) || txn->auth.method != HTTP_AUTH_BASIC)
Christopher Fauleta4063562019-08-02 11:51:37 +02001332 return 0;
1333
1334 smp->data.type = SMP_T_STR;
1335 smp->data.u.str.area = txn->auth.user;
1336 smp->data.u.str.data = strlen(txn->auth.user);
1337 smp->flags = SMP_F_CONST;
1338 return 1;
1339}
1340
1341/* Fetch the password supplied if there is an Authorization header. It relies on
1342 * get_http_auth()
1343 */
1344static int smp_fetch_http_auth_pass(const struct arg *args, struct sample *smp, const char *kw, void *private)
1345{
1346 struct channel *chn = SMP_REQ_CHN(smp);
Christopher Faulet778f5ed2020-04-29 15:51:55 +02001347 struct htx *htx = smp_prefetch_htx(smp, chn, NULL, 1);
Christopher Fauleta4063562019-08-02 11:51:37 +02001348 struct http_txn *txn;
1349
1350 if (!htx)
1351 return 0;
1352
1353 txn = smp->strm->txn;
Remi Tricot-Le Bretonf5dd3372021-10-01 15:36:53 +02001354 if (!get_http_auth(smp, htx) || txn->auth.method != HTTP_AUTH_BASIC)
Christopher Fauleta4063562019-08-02 11:51:37 +02001355 return 0;
1356
1357 smp->data.type = SMP_T_STR;
1358 smp->data.u.str.area = txn->auth.pass;
1359 smp->data.u.str.data = strlen(txn->auth.pass);
1360 smp->flags = SMP_F_CONST;
1361 return 1;
1362}
1363
Remi Tricot-Le Bretonf5dd3372021-10-01 15:36:53 +02001364static int smp_fetch_http_auth_bearer(const struct arg *args, struct sample *smp, const char *kw, void *private)
1365{
1366 struct channel *chn = SMP_REQ_CHN(smp);
1367 struct htx *htx = smp_prefetch_htx(smp, chn, NULL, 1);
1368 struct http_txn *txn;
1369 struct buffer bearer_val = {};
1370
1371 if (!htx)
1372 return 0;
1373
1374 if (args->type == ARGT_STR) {
1375 struct http_hdr_ctx ctx;
1376 struct ist hdr_name = ist2(args->data.str.area, args->data.str.data);
1377
1378 ctx.blk = NULL;
1379 if (http_find_header(htx, hdr_name, &ctx, 0)) {
Remi Tricot-Le Breton7da35bf2021-10-29 15:25:19 +02001380 struct ist type = istsplit(&ctx.value, ' ');
1381
1382 /* There must be "at least" one space character between
1383 * the scheme and the following value so ctx.value might
1384 * still have leading spaces here (see RFC7235).
1385 */
1386 ctx.value = istskip(ctx.value, ' ');
1387
1388 if (isteqi(type, ist("Bearer")) && istlen(ctx.value))
1389 chunk_initlen(&bearer_val, istptr(ctx.value), 0, istlen(ctx.value));
Remi Tricot-Le Bretonf5dd3372021-10-01 15:36:53 +02001390 }
1391 }
1392 else {
1393 txn = smp->strm->txn;
1394 if (!get_http_auth(smp, htx) || txn->auth.method != HTTP_AUTH_BEARER)
1395 return 0;
1396
1397 bearer_val = txn->auth.method_data;
1398 }
1399
1400 smp->data.type = SMP_T_STR;
1401 smp->data.u.str = bearer_val;
1402 smp->flags = SMP_F_CONST;
1403 return 1;
1404}
1405
Willy Tarreau79e57332018-10-02 16:01:16 +02001406/* Accepts exactly 1 argument of type userlist */
1407static int smp_fetch_http_auth(const struct arg *args, struct sample *smp, const char *kw, void *private)
1408{
Christopher Faulet89dc4992019-04-17 12:02:59 +02001409 struct channel *chn = SMP_REQ_CHN(smp);
Christopher Faulet778f5ed2020-04-29 15:51:55 +02001410 struct htx *htx = smp_prefetch_htx(smp, chn, NULL, 1);
Willy Tarreau79e57332018-10-02 16:01:16 +02001411
Christopher Faulet623af932021-01-29 11:22:15 +01001412 if (args->type != ARGT_USR)
Willy Tarreau79e57332018-10-02 16:01:16 +02001413 return 0;
1414
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001415 if (!htx)
1416 return 0;
Remi Tricot-Le Bretonf5dd3372021-10-01 15:36:53 +02001417 if (!get_http_auth(smp, htx) || smp->strm->txn->auth.method != HTTP_AUTH_BASIC)
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001418 return 0;
Willy Tarreau79e57332018-10-02 16:01:16 +02001419
1420 smp->data.type = SMP_T_BOOL;
1421 smp->data.u.sint = check_user(args->data.usr, smp->strm->txn->auth.user,
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001422 smp->strm->txn->auth.pass);
Willy Tarreau79e57332018-10-02 16:01:16 +02001423 return 1;
1424}
1425
1426/* Accepts exactly 1 argument of type userlist */
1427static int smp_fetch_http_auth_grp(const struct arg *args, struct sample *smp, const char *kw, void *private)
1428{
Christopher Faulet89dc4992019-04-17 12:02:59 +02001429 struct channel *chn = SMP_REQ_CHN(smp);
Christopher Faulet778f5ed2020-04-29 15:51:55 +02001430 struct htx *htx = smp_prefetch_htx(smp, chn, NULL, 1);
Christopher Faulet89dc4992019-04-17 12:02:59 +02001431
Christopher Faulet623af932021-01-29 11:22:15 +01001432 if (args->type != ARGT_USR)
Willy Tarreau79e57332018-10-02 16:01:16 +02001433 return 0;
1434
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001435 if (!htx)
1436 return 0;
Remi Tricot-Le Bretonf5dd3372021-10-01 15:36:53 +02001437 if (!get_http_auth(smp, htx) || smp->strm->txn->auth.method != HTTP_AUTH_BASIC)
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001438 return 0;
Willy Tarreau79e57332018-10-02 16:01:16 +02001439
Willy Tarreau79e57332018-10-02 16:01:16 +02001440 /* if the user does not belong to the userlist or has a wrong password,
1441 * report that it unconditionally does not match. Otherwise we return
1442 * a string containing the username.
1443 */
1444 if (!check_user(args->data.usr, smp->strm->txn->auth.user,
1445 smp->strm->txn->auth.pass))
1446 return 0;
1447
1448 /* pat_match_auth() will need the user list */
1449 smp->ctx.a[0] = args->data.usr;
1450
1451 smp->data.type = SMP_T_STR;
1452 smp->flags = SMP_F_CONST;
1453 smp->data.u.str.area = smp->strm->txn->auth.user;
1454 smp->data.u.str.data = strlen(smp->strm->txn->auth.user);
1455
1456 return 1;
1457}
1458
1459/* Fetch a captured HTTP request header. The index is the position of
1460 * the "capture" option in the configuration file
1461 */
1462static int smp_fetch_capture_req_hdr(const struct arg *args, struct sample *smp, const char *kw, void *private)
1463{
Willy Tarreau0898c2d2020-04-29 11:44:54 +02001464 struct proxy *fe;
Willy Tarreau79e57332018-10-02 16:01:16 +02001465 int idx;
1466
Christopher Faulet623af932021-01-29 11:22:15 +01001467 if (args->type != ARGT_SINT)
Willy Tarreau79e57332018-10-02 16:01:16 +02001468 return 0;
1469
Willy Tarreau0898c2d2020-04-29 11:44:54 +02001470 if (!smp->strm)
1471 return 0;
1472
1473 fe = strm_fe(smp->strm);
Willy Tarreau79e57332018-10-02 16:01:16 +02001474 idx = args->data.sint;
1475
1476 if (idx > (fe->nb_req_cap - 1) || smp->strm->req_cap == NULL || smp->strm->req_cap[idx] == NULL)
1477 return 0;
1478
1479 smp->data.type = SMP_T_STR;
1480 smp->flags |= SMP_F_CONST;
1481 smp->data.u.str.area = smp->strm->req_cap[idx];
1482 smp->data.u.str.data = strlen(smp->strm->req_cap[idx]);
1483
1484 return 1;
1485}
1486
1487/* Fetch a captured HTTP response header. The index is the position of
1488 * the "capture" option in the configuration file
1489 */
1490static int smp_fetch_capture_res_hdr(const struct arg *args, struct sample *smp, const char *kw, void *private)
1491{
Willy Tarreau0898c2d2020-04-29 11:44:54 +02001492 struct proxy *fe;
Willy Tarreau79e57332018-10-02 16:01:16 +02001493 int idx;
1494
Christopher Faulet623af932021-01-29 11:22:15 +01001495 if (args->type != ARGT_SINT)
Willy Tarreau79e57332018-10-02 16:01:16 +02001496 return 0;
1497
Willy Tarreau0898c2d2020-04-29 11:44:54 +02001498 if (!smp->strm)
1499 return 0;
1500
1501 fe = strm_fe(smp->strm);
Willy Tarreau79e57332018-10-02 16:01:16 +02001502 idx = args->data.sint;
1503
1504 if (idx > (fe->nb_rsp_cap - 1) || smp->strm->res_cap == NULL || smp->strm->res_cap[idx] == NULL)
1505 return 0;
1506
1507 smp->data.type = SMP_T_STR;
1508 smp->flags |= SMP_F_CONST;
1509 smp->data.u.str.area = smp->strm->res_cap[idx];
1510 smp->data.u.str.data = strlen(smp->strm->res_cap[idx]);
1511
1512 return 1;
1513}
1514
1515/* Extracts the METHOD in the HTTP request, the txn->uri should be filled before the call */
1516static int smp_fetch_capture_req_method(const struct arg *args, struct sample *smp, const char *kw, void *private)
1517{
1518 struct buffer *temp;
Willy Tarreau0898c2d2020-04-29 11:44:54 +02001519 struct http_txn *txn;
Willy Tarreau79e57332018-10-02 16:01:16 +02001520 char *ptr;
1521
Willy Tarreau0898c2d2020-04-29 11:44:54 +02001522 if (!smp->strm)
1523 return 0;
1524
1525 txn = smp->strm->txn;
Willy Tarreau79e57332018-10-02 16:01:16 +02001526 if (!txn || !txn->uri)
1527 return 0;
1528
1529 ptr = txn->uri;
1530
1531 while (*ptr != ' ' && *ptr != '\0') /* find first space */
1532 ptr++;
1533
1534 temp = get_trash_chunk();
1535 temp->area = txn->uri;
1536 temp->data = ptr - txn->uri;
1537 smp->data.u.str = *temp;
1538 smp->data.type = SMP_T_STR;
1539 smp->flags = SMP_F_CONST;
1540
1541 return 1;
1542
1543}
1544
1545/* Extracts the path in the HTTP request, the txn->uri should be filled before the call */
1546static int smp_fetch_capture_req_uri(const struct arg *args, struct sample *smp, const char *kw, void *private)
1547{
Willy Tarreau0898c2d2020-04-29 11:44:54 +02001548 struct http_txn *txn;
Willy Tarreau79e57332018-10-02 16:01:16 +02001549 struct ist path;
1550 const char *ptr;
Amaury Denoyellec453f952021-07-06 11:40:12 +02001551 struct http_uri_parser parser;
Willy Tarreau79e57332018-10-02 16:01:16 +02001552
Willy Tarreau0898c2d2020-04-29 11:44:54 +02001553 if (!smp->strm)
1554 return 0;
1555
1556 txn = smp->strm->txn;
Willy Tarreau79e57332018-10-02 16:01:16 +02001557 if (!txn || !txn->uri)
1558 return 0;
1559
1560 ptr = txn->uri;
1561
1562 while (*ptr != ' ' && *ptr != '\0') /* find first space */
1563 ptr++;
1564
1565 if (!*ptr)
1566 return 0;
1567
Christopher Faulet78337bb2018-11-15 14:35:18 +01001568 /* skip the first space and find space after URI */
1569 path = ist2(++ptr, 0);
1570 while (*ptr != ' ' && *ptr != '\0')
1571 ptr++;
1572 path.len = ptr - path.ptr;
Willy Tarreau79e57332018-10-02 16:01:16 +02001573
Amaury Denoyellec453f952021-07-06 11:40:12 +02001574 parser = http_uri_parser_init(path);
1575 path = http_parse_path(&parser);
Tim Duesterhused526372020-03-05 17:56:33 +01001576 if (!isttest(path))
Willy Tarreau79e57332018-10-02 16:01:16 +02001577 return 0;
1578
1579 smp->data.u.str.area = path.ptr;
1580 smp->data.u.str.data = path.len;
1581 smp->data.type = SMP_T_STR;
1582 smp->flags = SMP_F_CONST;
1583
1584 return 1;
1585}
1586
1587/* Retrieves the HTTP version from the request (either 1.0 or 1.1) and emits it
1588 * as a string (either "HTTP/1.0" or "HTTP/1.1").
1589 */
1590static int smp_fetch_capture_req_ver(const struct arg *args, struct sample *smp, const char *kw, void *private)
1591{
Willy Tarreau0898c2d2020-04-29 11:44:54 +02001592 struct http_txn *txn;
1593
1594 if (!smp->strm)
1595 return 0;
Willy Tarreau79e57332018-10-02 16:01:16 +02001596
Willy Tarreau0898c2d2020-04-29 11:44:54 +02001597 txn = smp->strm->txn;
Christopher Faulet09f88362021-04-01 16:00:29 +02001598 if (!txn || txn->req.msg_state < HTTP_MSG_BODY)
Willy Tarreau79e57332018-10-02 16:01:16 +02001599 return 0;
1600
1601 if (txn->req.flags & HTTP_MSGF_VER_11)
1602 smp->data.u.str.area = "HTTP/1.1";
1603 else
1604 smp->data.u.str.area = "HTTP/1.0";
1605
1606 smp->data.u.str.data = 8;
1607 smp->data.type = SMP_T_STR;
1608 smp->flags = SMP_F_CONST;
1609 return 1;
1610
1611}
1612
1613/* Retrieves the HTTP version from the response (either 1.0 or 1.1) and emits it
1614 * as a string (either "HTTP/1.0" or "HTTP/1.1").
1615 */
1616static int smp_fetch_capture_res_ver(const struct arg *args, struct sample *smp, const char *kw, void *private)
1617{
Willy Tarreau0898c2d2020-04-29 11:44:54 +02001618 struct http_txn *txn;
1619
1620 if (!smp->strm)
1621 return 0;
Willy Tarreau79e57332018-10-02 16:01:16 +02001622
Willy Tarreau0898c2d2020-04-29 11:44:54 +02001623 txn = smp->strm->txn;
Christopher Faulet09f88362021-04-01 16:00:29 +02001624 if (!txn || txn->rsp.msg_state < HTTP_MSG_BODY)
Willy Tarreau79e57332018-10-02 16:01:16 +02001625 return 0;
1626
1627 if (txn->rsp.flags & HTTP_MSGF_VER_11)
1628 smp->data.u.str.area = "HTTP/1.1";
1629 else
1630 smp->data.u.str.area = "HTTP/1.0";
1631
1632 smp->data.u.str.data = 8;
1633 smp->data.type = SMP_T_STR;
1634 smp->flags = SMP_F_CONST;
1635 return 1;
1636
1637}
1638
1639/* Iterate over all cookies present in a message. The context is stored in
1640 * smp->ctx.a[0] for the in-header position, smp->ctx.a[1] for the
1641 * end-of-header-value, and smp->ctx.a[2] for the hdr_ctx. Depending on
1642 * the direction, multiple cookies may be parsed on the same line or not.
Maciej Zdebdea7c202020-11-13 09:38:06 +00001643 * If provided, the searched cookie name is in args, in args->data.str. If
1644 * the input options indicate that no iterating is desired, then only last
1645 * value is fetched if any. If no cookie name is provided, the first cookie
1646 * value found is fetched. The returned sample is of type CSTR. Can be used
1647 * to parse cookies in other files.
Willy Tarreau79e57332018-10-02 16:01:16 +02001648 */
1649static int smp_fetch_cookie(const struct arg *args, struct sample *smp, const char *kw, void *private)
1650{
Christopher Faulet89dc4992019-04-17 12:02:59 +02001651 /* possible keywords: req.cookie / cookie / cook, res.cookie / scook / set-cookie */
1652 struct channel *chn = ((kw[0] == 'c' || kw[2] == 'q') ? SMP_REQ_CHN(smp) : SMP_RES_CHN(smp));
Christopher Fauletf98e6262020-05-06 09:42:04 +02001653 struct check *check = ((kw[0] == 's' || kw[2] == 's') ? objt_check(smp->sess->origin) : NULL);
Christopher Faulet16032ab2020-04-30 11:30:00 +02001654 struct htx *htx = smp_prefetch_htx(smp, chn, check, 1);
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001655 struct http_hdr_ctx *ctx = smp->ctx.a[2];
1656 struct ist hdr;
Christopher Faulet97fc8da2020-11-13 13:41:04 +01001657 char *cook = NULL;
1658 size_t cook_l = 0;
Willy Tarreau79e57332018-10-02 16:01:16 +02001659 int found = 0;
1660
Christopher Faulet623af932021-01-29 11:22:15 +01001661 if (args->type == ARGT_STR) {
Christopher Faulet97fc8da2020-11-13 13:41:04 +01001662 cook = args->data.str.area;
1663 cook_l = args->data.str.data;
1664 }
Willy Tarreau79e57332018-10-02 16:01:16 +02001665
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001666 if (!ctx) {
1667 /* first call */
1668 ctx = &static_http_hdr_ctx;
1669 ctx->blk = NULL;
1670 smp->ctx.a[2] = ctx;
1671 }
Willy Tarreau79e57332018-10-02 16:01:16 +02001672
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001673 if (!htx)
1674 return 0;
Willy Tarreau79e57332018-10-02 16:01:16 +02001675
Christopher Faulet16032ab2020-04-30 11:30:00 +02001676 hdr = (!(check || (chn && chn->flags & CF_ISRESP)) ? ist("Cookie") : ist("Set-Cookie"));
Willy Tarreau79e57332018-10-02 16:01:16 +02001677
Maciej Zdebdea7c202020-11-13 09:38:06 +00001678 /* OK so basically here, either we want only one value or we want to
1679 * iterate over all of them and we fetch the next one. In this last case
1680 * SMP_OPT_ITERATE option is set.
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001681 */
Willy Tarreau79e57332018-10-02 16:01:16 +02001682
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001683 if (!(smp->flags & SMP_F_NOT_LAST)) {
1684 /* search for the header from the beginning, we must first initialize
1685 * the search parameters.
Willy Tarreau79e57332018-10-02 16:01:16 +02001686 */
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001687 smp->ctx.a[0] = NULL;
1688 ctx->blk = NULL;
Willy Tarreau79e57332018-10-02 16:01:16 +02001689 }
Willy Tarreau79e57332018-10-02 16:01:16 +02001690
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001691 smp->flags |= SMP_F_VOL_HDR;
1692 while (1) {
1693 /* Note: smp->ctx.a[0] == NULL every time we need to fetch a new header */
1694 if (!smp->ctx.a[0]) {
1695 if (!http_find_header(htx, hdr, ctx, 0))
1696 goto out;
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001697
Christopher Faulet97fc8da2020-11-13 13:41:04 +01001698 if (ctx->value.len < cook_l + 1)
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001699 continue;
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001700
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001701 smp->ctx.a[0] = ctx->value.ptr;
1702 smp->ctx.a[1] = smp->ctx.a[0] + ctx->value.len;
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001703 }
1704
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001705 smp->data.type = SMP_T_STR;
1706 smp->flags |= SMP_F_CONST;
1707 smp->ctx.a[0] = http_extract_cookie_value(smp->ctx.a[0], smp->ctx.a[1],
Christopher Faulet97fc8da2020-11-13 13:41:04 +01001708 cook, cook_l,
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001709 (smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ,
1710 &smp->data.u.str.area,
1711 &smp->data.u.str.data);
1712 if (smp->ctx.a[0]) {
1713 found = 1;
Maciej Zdebdea7c202020-11-13 09:38:06 +00001714 if (smp->opt & SMP_OPT_ITERATE) {
1715 /* iterate on cookie value */
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001716 smp->flags |= SMP_F_NOT_LAST;
1717 return 1;
Willy Tarreau79e57332018-10-02 16:01:16 +02001718 }
Maciej Zdebdea7c202020-11-13 09:38:06 +00001719 if (args->data.str.data == 0) {
1720 /* No cookie name, first occurrence returned */
1721 break;
1722 }
Willy Tarreau79e57332018-10-02 16:01:16 +02001723 }
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001724 /* if we're looking for last occurrence, let's loop */
Willy Tarreau79e57332018-10-02 16:01:16 +02001725 }
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001726
Willy Tarreau79e57332018-10-02 16:01:16 +02001727 /* all cookie headers and values were scanned. If we're looking for the
1728 * last occurrence, we may return it now.
1729 */
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001730 out:
Willy Tarreau79e57332018-10-02 16:01:16 +02001731 smp->flags &= ~SMP_F_NOT_LAST;
1732 return found;
1733}
1734
Christopher Fauletc1f40dd2019-05-16 10:07:30 +02001735/* Same than smp_fetch_cookie() but only relies on the sample direction to
1736 * choose the right channel. So instead of duplicating the code, we just change
1737 * the keyword and then fallback on smp_fetch_cookie().
1738 */
1739static int smp_fetch_chn_cookie(const struct arg *args, struct sample *smp, const char *kw, void *private)
1740{
1741 kw = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ ? "req.cook" : "res.cook");
1742 return smp_fetch_cookie(args, smp, kw, private);
1743}
1744
Willy Tarreau79e57332018-10-02 16:01:16 +02001745/* Iterate over all cookies present in a request to count how many occurrences
1746 * match the name in args and args->data.str.len. If <multi> is non-null, then
1747 * multiple cookies may be parsed on the same line. The returned sample is of
1748 * type UINT. Accepts exactly 1 argument of type string.
1749 */
1750static int smp_fetch_cookie_cnt(const struct arg *args, struct sample *smp, const char *kw, void *private)
1751{
Christopher Faulet89dc4992019-04-17 12:02:59 +02001752 /* possible keywords: req.cook_cnt / cook_cnt, res.cook_cnt / scook_cnt */
1753 struct channel *chn = ((kw[0] == 'c' || kw[2] == 'q') ? SMP_REQ_CHN(smp) : SMP_RES_CHN(smp));
Christopher Fauletf98e6262020-05-06 09:42:04 +02001754 struct check *check = ((kw[0] == 's' || kw[2] == 's') ? objt_check(smp->sess->origin) : NULL);
Christopher Faulet16032ab2020-04-30 11:30:00 +02001755 struct htx *htx = smp_prefetch_htx(smp, chn, check, 1);
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001756 struct http_hdr_ctx ctx;
1757 struct ist hdr;
Willy Tarreau79e57332018-10-02 16:01:16 +02001758 char *val_beg, *val_end;
Christopher Faulet97fc8da2020-11-13 13:41:04 +01001759 char *cook = NULL;
1760 size_t cook_l = 0;
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001761 int cnt;
Willy Tarreau79e57332018-10-02 16:01:16 +02001762
Christopher Faulet623af932021-01-29 11:22:15 +01001763 if (args->type == ARGT_STR){
Christopher Faulet97fc8da2020-11-13 13:41:04 +01001764 cook = args->data.str.area;
1765 cook_l = args->data.str.data;
1766 }
Willy Tarreau79e57332018-10-02 16:01:16 +02001767
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001768 if (!htx)
1769 return 0;
Willy Tarreau79e57332018-10-02 16:01:16 +02001770
Christopher Faulet16032ab2020-04-30 11:30:00 +02001771 hdr = (!(check || (chn && chn->flags & CF_ISRESP)) ? ist("Cookie") : ist("Set-Cookie"));
Willy Tarreau79e57332018-10-02 16:01:16 +02001772
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001773 val_end = val_beg = NULL;
1774 ctx.blk = NULL;
1775 cnt = 0;
1776 while (1) {
1777 /* Note: val_beg == NULL every time we need to fetch a new header */
1778 if (!val_beg) {
1779 if (!http_find_header(htx, hdr, &ctx, 0))
1780 break;
Willy Tarreau79e57332018-10-02 16:01:16 +02001781
Christopher Faulet97fc8da2020-11-13 13:41:04 +01001782 if (ctx.value.len < cook_l + 1)
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001783 continue;
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001784
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001785 val_beg = ctx.value.ptr;
1786 val_end = val_beg + ctx.value.len;
Willy Tarreau79e57332018-10-02 16:01:16 +02001787 }
1788
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001789 smp->data.type = SMP_T_STR;
1790 smp->flags |= SMP_F_CONST;
1791 while ((val_beg = http_extract_cookie_value(val_beg, val_end,
Christopher Faulet97fc8da2020-11-13 13:41:04 +01001792 cook, cook_l,
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001793 (smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ,
1794 &smp->data.u.str.area,
1795 &smp->data.u.str.data))) {
1796 cnt++;
Willy Tarreau79e57332018-10-02 16:01:16 +02001797 }
1798 }
1799
1800 smp->data.type = SMP_T_SINT;
1801 smp->data.u.sint = cnt;
1802 smp->flags |= SMP_F_VOL_HDR;
1803 return 1;
1804}
1805
1806/* Fetch an cookie's integer value. The integer value is returned. It
1807 * takes a mandatory argument of type string. It relies on smp_fetch_cookie().
1808 */
1809static int smp_fetch_cookie_val(const struct arg *args, struct sample *smp, const char *kw, void *private)
1810{
1811 int ret = smp_fetch_cookie(args, smp, kw, private);
1812
1813 if (ret > 0) {
1814 smp->data.type = SMP_T_SINT;
1815 smp->data.u.sint = strl2ic(smp->data.u.str.area,
1816 smp->data.u.str.data);
1817 }
1818
1819 return ret;
1820}
1821
1822/************************************************************************/
1823/* The code below is dedicated to sample fetches */
1824/************************************************************************/
1825
1826/* This scans a URL-encoded query string. It takes an optionally wrapping
Ilya Shipitsin46a030c2020-07-05 16:36:08 +05001827 * string whose first contiguous chunk has its beginning in ctx->a[0] and end
Willy Tarreau79e57332018-10-02 16:01:16 +02001828 * in ctx->a[1], and the optional second part in (ctx->a[2]..ctx->a[3]). The
1829 * pointers are updated for next iteration before leaving.
1830 */
1831static int smp_fetch_param(char delim, const char *name, int name_len, const struct arg *args, struct sample *smp, const char *kw, void *private)
1832{
1833 const char *vstart, *vend;
1834 struct buffer *temp;
1835 const char **chunks = (const char **)smp->ctx.a;
1836
1837 if (!http_find_next_url_param(chunks, name, name_len,
1838 &vstart, &vend, delim))
1839 return 0;
1840
1841 /* Create sample. If the value is contiguous, return the pointer as CONST,
1842 * if the value is wrapped, copy-it in a buffer.
1843 */
1844 smp->data.type = SMP_T_STR;
1845 if (chunks[2] &&
1846 vstart >= chunks[0] && vstart <= chunks[1] &&
1847 vend >= chunks[2] && vend <= chunks[3]) {
1848 /* Wrapped case. */
1849 temp = get_trash_chunk();
1850 memcpy(temp->area, vstart, chunks[1] - vstart);
1851 memcpy(temp->area + ( chunks[1] - vstart ), chunks[2],
1852 vend - chunks[2]);
1853 smp->data.u.str.area = temp->area;
1854 smp->data.u.str.data = ( chunks[1] - vstart ) + ( vend - chunks[2] );
1855 } else {
1856 /* Contiguous case. */
1857 smp->data.u.str.area = (char *)vstart;
1858 smp->data.u.str.data = vend - vstart;
1859 smp->flags = SMP_F_VOL_1ST | SMP_F_CONST;
1860 }
1861
1862 /* Update context, check wrapping. */
1863 chunks[0] = vend;
1864 if (chunks[2] && vend >= chunks[2] && vend <= chunks[3]) {
1865 chunks[1] = chunks[3];
1866 chunks[2] = NULL;
1867 }
1868
1869 if (chunks[0] < chunks[1])
1870 smp->flags |= SMP_F_NOT_LAST;
1871
1872 return 1;
1873}
1874
1875/* This function iterates over each parameter of the query string. It uses
1876 * ctx->a[0] and ctx->a[1] to store the beginning and end of the current
1877 * parameter. Since it uses smp_fetch_param(), ctx->a[2..3] are both NULL.
1878 * An optional parameter name is passed in args[0], otherwise any parameter is
1879 * considered. It supports an optional delimiter argument for the beginning of
1880 * the string in args[1], which defaults to "?".
1881 */
1882static int smp_fetch_url_param(const struct arg *args, struct sample *smp, const char *kw, void *private)
1883{
Christopher Faulet89dc4992019-04-17 12:02:59 +02001884 struct channel *chn = SMP_REQ_CHN(smp);
Willy Tarreau79e57332018-10-02 16:01:16 +02001885 char delim = '?';
1886 const char *name;
1887 int name_len;
1888
Christopher Faulet623af932021-01-29 11:22:15 +01001889 if ((args[0].type && args[0].type != ARGT_STR) ||
Willy Tarreau79e57332018-10-02 16:01:16 +02001890 (args[1].type && args[1].type != ARGT_STR))
1891 return 0;
1892
1893 name = "";
1894 name_len = 0;
1895 if (args->type == ARGT_STR) {
1896 name = args->data.str.area;
1897 name_len = args->data.str.data;
1898 }
1899
1900 if (args[1].type)
1901 delim = *args[1].data.str.area;
1902
1903 if (!smp->ctx.a[0]) { // first call, find the query string
Christopher Faulet778f5ed2020-04-29 15:51:55 +02001904 struct htx *htx = smp_prefetch_htx(smp, chn, NULL, 1);
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001905 struct htx_sl *sl;
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001906
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001907 if (!htx)
1908 return 0;
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001909
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001910 sl = http_get_stline(htx);
1911 smp->ctx.a[0] = http_find_param_list(HTX_SL_REQ_UPTR(sl), HTX_SL_REQ_ULEN(sl), delim);
1912 if (!smp->ctx.a[0])
1913 return 0;
Willy Tarreau79e57332018-10-02 16:01:16 +02001914
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001915 smp->ctx.a[1] = HTX_SL_REQ_UPTR(sl) + HTX_SL_REQ_ULEN(sl);
Willy Tarreau79e57332018-10-02 16:01:16 +02001916
1917 /* Assume that the context is filled with NULL pointer
1918 * before the first call.
1919 * smp->ctx.a[2] = NULL;
1920 * smp->ctx.a[3] = NULL;
1921 */
1922 }
1923
1924 return smp_fetch_param(delim, name, name_len, args, smp, kw, private);
1925}
1926
1927/* This function iterates over each parameter of the body. This requires
1928 * that the body has been waited for using http-buffer-request. It uses
1929 * ctx->a[0] and ctx->a[1] to store the beginning and end of the first
Ilya Shipitsin46a030c2020-07-05 16:36:08 +05001930 * contiguous part of the body, and optionally ctx->a[2..3] to reference the
Willy Tarreau79e57332018-10-02 16:01:16 +02001931 * optional second part if the body wraps at the end of the buffer. An optional
1932 * parameter name is passed in args[0], otherwise any parameter is considered.
1933 */
1934static int smp_fetch_body_param(const struct arg *args, struct sample *smp, const char *kw, void *private)
1935{
Christopher Faulet89dc4992019-04-17 12:02:59 +02001936 struct channel *chn = SMP_REQ_CHN(smp);
Willy Tarreau79e57332018-10-02 16:01:16 +02001937 const char *name;
1938 int name_len;
1939
Christopher Faulet623af932021-01-29 11:22:15 +01001940 if (args[0].type && args[0].type != ARGT_STR)
Willy Tarreau79e57332018-10-02 16:01:16 +02001941 return 0;
1942
1943 name = "";
1944 name_len = 0;
1945 if (args[0].type == ARGT_STR) {
1946 name = args[0].data.str.area;
1947 name_len = args[0].data.str.data;
1948 }
1949
1950 if (!smp->ctx.a[0]) { // first call, find the query string
Christopher Faulete596d182020-05-05 17:46:34 +02001951 struct htx *htx = smp_prefetch_htx(smp, chn, NULL, 1);
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001952 struct buffer *temp;
1953 int32_t pos;
Willy Tarreau79e57332018-10-02 16:01:16 +02001954
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001955 if (!htx)
1956 return 0;
Willy Tarreau79e57332018-10-02 16:01:16 +02001957
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001958 temp = get_trash_chunk();
1959 for (pos = htx_get_first(htx); pos != -1; pos = htx_get_next(htx, pos)) {
1960 struct htx_blk *blk = htx_get_blk(htx, pos);
1961 enum htx_blk_type type = htx_get_blk_type(blk);
Willy Tarreau79e57332018-10-02 16:01:16 +02001962
Christopher Fauletd1ac2b92020-12-02 19:12:22 +01001963 if (type == HTX_BLK_TLR || type == HTX_BLK_EOT)
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001964 break;
1965 if (type == HTX_BLK_DATA) {
Christopher Faulet53a899b2019-10-08 16:38:42 +02001966 if (!h1_format_htx_data(htx_get_blk_value(htx, blk), temp, 0))
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001967 return 0;
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001968 }
Willy Tarreau79e57332018-10-02 16:01:16 +02001969 }
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001970
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001971 smp->ctx.a[0] = temp->area;
1972 smp->ctx.a[1] = temp->area + temp->data;
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001973
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001974 /* Assume that the context is filled with NULL pointer
1975 * before the first call.
1976 * smp->ctx.a[2] = NULL;
1977 * smp->ctx.a[3] = NULL;
1978 */
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001979
Willy Tarreau79e57332018-10-02 16:01:16 +02001980 }
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001981
Willy Tarreau79e57332018-10-02 16:01:16 +02001982 return smp_fetch_param('&', name, name_len, args, smp, kw, private);
1983}
1984
1985/* Return the signed integer value for the specified url parameter (see url_param
1986 * above).
1987 */
1988static int smp_fetch_url_param_val(const struct arg *args, struct sample *smp, const char *kw, void *private)
1989{
1990 int ret = smp_fetch_url_param(args, smp, kw, private);
1991
1992 if (ret > 0) {
1993 smp->data.type = SMP_T_SINT;
1994 smp->data.u.sint = strl2ic(smp->data.u.str.area,
1995 smp->data.u.str.data);
1996 }
1997
1998 return ret;
1999}
2000
2001/* This produces a 32-bit hash of the concatenation of the first occurrence of
2002 * the Host header followed by the path component if it begins with a slash ('/').
2003 * This means that '*' will not be added, resulting in exactly the first Host
2004 * entry. If no Host header is found, then the path is used. The resulting value
2005 * is hashed using the url hash followed by a full avalanche hash and provides a
2006 * 32-bit integer value. This fetch is useful for tracking per-URL activity on
2007 * high-traffic sites without having to store whole paths.
2008 * this differs from the base32 functions in that it includes the url parameters
2009 * as well as the path
2010 */
2011static int smp_fetch_url32(const struct arg *args, struct sample *smp, const char *kw, void *private)
2012{
Christopher Faulet89dc4992019-04-17 12:02:59 +02002013 struct channel *chn = SMP_REQ_CHN(smp);
Christopher Faulet778f5ed2020-04-29 15:51:55 +02002014 struct htx *htx = smp_prefetch_htx(smp, chn, NULL, 1);
Christopher Faulet6d1dd462019-07-15 14:36:03 +02002015 struct http_hdr_ctx ctx;
2016 struct htx_sl *sl;
2017 struct ist path;
Willy Tarreau79e57332018-10-02 16:01:16 +02002018 unsigned int hash = 0;
Amaury Denoyellec453f952021-07-06 11:40:12 +02002019 struct http_uri_parser parser;
Willy Tarreau79e57332018-10-02 16:01:16 +02002020
Christopher Faulet6d1dd462019-07-15 14:36:03 +02002021 if (!htx)
2022 return 0;
Christopher Faulet311c7ea2018-10-24 21:41:55 +02002023
Christopher Faulet6d1dd462019-07-15 14:36:03 +02002024 ctx.blk = NULL;
2025 if (http_find_header(htx, ist("Host"), &ctx, 1)) {
2026 /* OK we have the header value in ctx.value */
2027 while (ctx.value.len--)
2028 hash = *(ctx.value.ptr++) + (hash << 6) + (hash << 16) - hash;
Willy Tarreau79e57332018-10-02 16:01:16 +02002029 }
Christopher Faulet311c7ea2018-10-24 21:41:55 +02002030
Christopher Faulet6d1dd462019-07-15 14:36:03 +02002031 /* now retrieve the path */
2032 sl = http_get_stline(htx);
Amaury Denoyellec453f952021-07-06 11:40:12 +02002033 parser = http_uri_parser_init(htx_sl_req_uri(sl));
2034 path = http_parse_path(&parser);
Christopher Faulet6d1dd462019-07-15 14:36:03 +02002035 if (path.len && *(path.ptr) == '/') {
2036 while (path.len--)
2037 hash = *(path.ptr++) + (hash << 6) + (hash << 16) - hash;
Willy Tarreau79e57332018-10-02 16:01:16 +02002038 }
Christopher Faulet311c7ea2018-10-24 21:41:55 +02002039
Willy Tarreau79e57332018-10-02 16:01:16 +02002040 hash = full_hash(hash);
2041
2042 smp->data.type = SMP_T_SINT;
2043 smp->data.u.sint = hash;
2044 smp->flags = SMP_F_VOL_1ST;
2045 return 1;
2046}
2047
2048/* This concatenates the source address with the 32-bit hash of the Host and
2049 * URL as returned by smp_fetch_base32(). The idea is to have per-source and
2050 * per-url counters. The result is a binary block from 8 to 20 bytes depending
2051 * on the source address length. The URL hash is stored before the address so
2052 * that in environments where IPv6 is insignificant, truncating the output to
2053 * 8 bytes would still work.
2054 */
2055static int smp_fetch_url32_src(const struct arg *args, struct sample *smp, const char *kw, void *private)
2056{
Christopher Faulet8da67aa2022-03-29 17:53:09 +02002057 const struct sockaddr_storage *src = (smp->strm ? cs_src(smp->strm->csf) : NULL);
Willy Tarreau79e57332018-10-02 16:01:16 +02002058 struct buffer *temp;
Willy Tarreau79e57332018-10-02 16:01:16 +02002059
Christopher Faulet6fc817a2021-10-25 07:48:27 +02002060 if (!src)
Willy Tarreau79e57332018-10-02 16:01:16 +02002061 return 0;
2062
2063 if (!smp_fetch_url32(args, smp, kw, private))
2064 return 0;
2065
2066 temp = get_trash_chunk();
2067 *(unsigned int *) temp->area = htonl(smp->data.u.sint);
2068 temp->data += sizeof(unsigned int);
2069
Christopher Faulet6fc817a2021-10-25 07:48:27 +02002070 switch (src->ss_family) {
Willy Tarreau79e57332018-10-02 16:01:16 +02002071 case AF_INET:
2072 memcpy(temp->area + temp->data,
Christopher Faulet6fc817a2021-10-25 07:48:27 +02002073 &((struct sockaddr_in *)src)->sin_addr,
Willy Tarreau79e57332018-10-02 16:01:16 +02002074 4);
2075 temp->data += 4;
2076 break;
2077 case AF_INET6:
2078 memcpy(temp->area + temp->data,
Christopher Faulet6fc817a2021-10-25 07:48:27 +02002079 &((struct sockaddr_in6 *)src)->sin6_addr,
Willy Tarreau79e57332018-10-02 16:01:16 +02002080 16);
2081 temp->data += 16;
2082 break;
2083 default:
2084 return 0;
2085 }
2086
2087 smp->data.u.str = *temp;
2088 smp->data.type = SMP_T_BIN;
2089 return 1;
2090}
2091
2092/************************************************************************/
2093/* Other utility functions */
2094/************************************************************************/
2095
2096/* This function is used to validate the arguments passed to any "hdr" fetch
2097 * keyword. These keywords support an optional positive or negative occurrence
2098 * number. We must ensure that the number is greater than -MAX_HDR_HISTORY. It
2099 * is assumed that the types are already the correct ones. Returns 0 on error,
2100 * non-zero if OK. If <err> is not NULL, it will be filled with a pointer to an
2101 * error message in case of error, that the caller is responsible for freeing.
2102 * The initial location must either be freeable or NULL.
2103 * Note: this function's pointer is checked from Lua.
2104 */
2105int val_hdr(struct arg *arg, char **err_msg)
2106{
2107 if (arg && arg[1].type == ARGT_SINT && arg[1].data.sint < -MAX_HDR_HISTORY) {
2108 memprintf(err_msg, "header occurrence must be >= %d", -MAX_HDR_HISTORY);
2109 return 0;
2110 }
2111 return 1;
2112}
2113
2114/************************************************************************/
2115/* All supported sample fetch keywords must be declared here. */
2116/************************************************************************/
2117
2118/* Note: must not be declared <const> as its list will be overwritten */
2119static struct sample_fetch_kw_list sample_fetch_keywords = {ILH, {
2120 { "base", smp_fetch_base, 0, NULL, SMP_T_STR, SMP_USE_HRQHV },
2121 { "base32", smp_fetch_base32, 0, NULL, SMP_T_SINT, SMP_USE_HRQHV },
2122 { "base32+src", smp_fetch_base32_src, 0, NULL, SMP_T_BIN, SMP_USE_HRQHV },
Yves Lafonb4d37082021-02-11 11:01:28 +01002123 { "baseq", smp_fetch_base, 0, NULL, SMP_T_STR, SMP_USE_HRQHV },
Willy Tarreau79e57332018-10-02 16:01:16 +02002124
2125 /* capture are allocated and are permanent in the stream */
2126 { "capture.req.hdr", smp_fetch_capture_req_hdr, ARG1(1,SINT), NULL, SMP_T_STR, SMP_USE_HRQHP },
2127
2128 /* retrieve these captures from the HTTP logs */
2129 { "capture.req.method", smp_fetch_capture_req_method, 0, NULL, SMP_T_STR, SMP_USE_HRQHP },
2130 { "capture.req.uri", smp_fetch_capture_req_uri, 0, NULL, SMP_T_STR, SMP_USE_HRQHP },
2131 { "capture.req.ver", smp_fetch_capture_req_ver, 0, NULL, SMP_T_STR, SMP_USE_HRQHP },
2132
2133 { "capture.res.hdr", smp_fetch_capture_res_hdr, ARG1(1,SINT), NULL, SMP_T_STR, SMP_USE_HRSHP },
2134 { "capture.res.ver", smp_fetch_capture_res_ver, 0, NULL, SMP_T_STR, SMP_USE_HRQHP },
2135
2136 /* cookie is valid in both directions (eg: for "stick ...") but cook*
2137 * are only here to match the ACL's name, are request-only and are used
2138 * for ACL compatibility only.
2139 */
2140 { "cook", smp_fetch_cookie, ARG1(0,STR), NULL, SMP_T_STR, SMP_USE_HRQHV },
Christopher Fauletc1f40dd2019-05-16 10:07:30 +02002141 { "cookie", smp_fetch_chn_cookie, ARG1(0,STR), NULL, SMP_T_STR, SMP_USE_HRQHV|SMP_USE_HRSHV },
Willy Tarreau79e57332018-10-02 16:01:16 +02002142 { "cook_cnt", smp_fetch_cookie_cnt, ARG1(0,STR), NULL, SMP_T_SINT, SMP_USE_HRQHV },
2143 { "cook_val", smp_fetch_cookie_val, ARG1(0,STR), NULL, SMP_T_SINT, SMP_USE_HRQHV },
2144
2145 /* hdr is valid in both directions (eg: for "stick ...") but hdr_* are
2146 * only here to match the ACL's name, are request-only and are used for
2147 * ACL compatibility only.
2148 */
Christopher Fauletc1f40dd2019-05-16 10:07:30 +02002149 { "hdr", smp_fetch_chn_hdr, ARG2(0,STR,SINT), val_hdr, SMP_T_STR, SMP_USE_HRQHV|SMP_USE_HRSHV },
Willy Tarreau79e57332018-10-02 16:01:16 +02002150 { "hdr_cnt", smp_fetch_hdr_cnt, ARG1(0,STR), NULL, SMP_T_SINT, SMP_USE_HRQHV },
2151 { "hdr_ip", smp_fetch_hdr_ip, ARG2(0,STR,SINT), val_hdr, SMP_T_IPV4, SMP_USE_HRQHV },
2152 { "hdr_val", smp_fetch_hdr_val, ARG2(0,STR,SINT), val_hdr, SMP_T_SINT, SMP_USE_HRQHV },
2153
Christopher Fauleta4063562019-08-02 11:51:37 +02002154 { "http_auth_type", smp_fetch_http_auth_type, 0, NULL, SMP_T_STR, SMP_USE_HRQHV },
2155 { "http_auth_user", smp_fetch_http_auth_user, 0, NULL, SMP_T_STR, SMP_USE_HRQHV },
2156 { "http_auth_pass", smp_fetch_http_auth_pass, 0, NULL, SMP_T_STR, SMP_USE_HRQHV },
Remi Tricot-Le Bretonf5dd3372021-10-01 15:36:53 +02002157 { "http_auth_bearer", smp_fetch_http_auth_bearer, ARG1(0,STR), NULL, SMP_T_STR, SMP_USE_HRQHV },
Willy Tarreau79e57332018-10-02 16:01:16 +02002158 { "http_auth", smp_fetch_http_auth, ARG1(1,USR), NULL, SMP_T_BOOL, SMP_USE_HRQHV },
2159 { "http_auth_group", smp_fetch_http_auth_grp, ARG1(1,USR), NULL, SMP_T_STR, SMP_USE_HRQHV },
2160 { "http_first_req", smp_fetch_http_first_req, 0, NULL, SMP_T_BOOL, SMP_USE_HRQHP },
2161 { "method", smp_fetch_meth, 0, NULL, SMP_T_METH, SMP_USE_HRQHP },
2162 { "path", smp_fetch_path, 0, NULL, SMP_T_STR, SMP_USE_HRQHV },
Christopher Faulete720c322020-09-02 17:25:18 +02002163 { "pathq", smp_fetch_path, 0, NULL, SMP_T_STR, SMP_USE_HRQHV },
Willy Tarreau79e57332018-10-02 16:01:16 +02002164 { "query", smp_fetch_query, 0, NULL, SMP_T_STR, SMP_USE_HRQHV },
2165
2166 /* HTTP protocol on the request path */
2167 { "req.proto_http", smp_fetch_proto_http, 0, NULL, SMP_T_BOOL, SMP_USE_HRQHP },
2168 { "req_proto_http", smp_fetch_proto_http, 0, NULL, SMP_T_BOOL, SMP_USE_HRQHP },
2169
2170 /* HTTP version on the request path */
2171 { "req.ver", smp_fetch_rqver, 0, NULL, SMP_T_STR, SMP_USE_HRQHV },
2172 { "req_ver", smp_fetch_rqver, 0, NULL, SMP_T_STR, SMP_USE_HRQHV },
2173
2174 { "req.body", smp_fetch_body, 0, NULL, SMP_T_BIN, SMP_USE_HRQHV },
2175 { "req.body_len", smp_fetch_body_len, 0, NULL, SMP_T_SINT, SMP_USE_HRQHV },
2176 { "req.body_size", smp_fetch_body_size, 0, NULL, SMP_T_SINT, SMP_USE_HRQHV },
2177 { "req.body_param", smp_fetch_body_param, ARG1(0,STR), NULL, SMP_T_BIN, SMP_USE_HRQHV },
2178
2179 { "req.hdrs", smp_fetch_hdrs, 0, NULL, SMP_T_BIN, SMP_USE_HRQHV },
2180 { "req.hdrs_bin", smp_fetch_hdrs_bin, 0, NULL, SMP_T_BIN, SMP_USE_HRQHV },
2181
2182 /* HTTP version on the response path */
2183 { "res.ver", smp_fetch_stver, 0, NULL, SMP_T_STR, SMP_USE_HRSHV },
2184 { "resp_ver", smp_fetch_stver, 0, NULL, SMP_T_STR, SMP_USE_HRSHV },
2185
Christopher Faulete596d182020-05-05 17:46:34 +02002186 { "res.body", smp_fetch_body, 0, NULL, SMP_T_BIN, SMP_USE_HRSHV },
2187 { "res.body_len", smp_fetch_body_len, 0, NULL, SMP_T_SINT, SMP_USE_HRSHV },
2188 { "res.body_size", smp_fetch_body_size, 0, NULL, SMP_T_SINT, SMP_USE_HRSHV },
2189
2190 { "res.hdrs", smp_fetch_hdrs, 0, NULL, SMP_T_BIN, SMP_USE_HRSHV },
2191 { "res.hdrs_bin", smp_fetch_hdrs_bin, 0, NULL, SMP_T_BIN, SMP_USE_HRSHV },
2192
Willy Tarreau79e57332018-10-02 16:01:16 +02002193 /* explicit req.{cook,hdr} are used to force the fetch direction to be request-only */
2194 { "req.cook", smp_fetch_cookie, ARG1(0,STR), NULL, SMP_T_STR, SMP_USE_HRQHV },
2195 { "req.cook_cnt", smp_fetch_cookie_cnt, ARG1(0,STR), NULL, SMP_T_SINT, SMP_USE_HRQHV },
2196 { "req.cook_val", smp_fetch_cookie_val, ARG1(0,STR), NULL, SMP_T_SINT, SMP_USE_HRQHV },
2197
2198 { "req.fhdr", smp_fetch_fhdr, ARG2(0,STR,SINT), val_hdr, SMP_T_STR, SMP_USE_HRQHV },
2199 { "req.fhdr_cnt", smp_fetch_fhdr_cnt, ARG1(0,STR), NULL, SMP_T_SINT, SMP_USE_HRQHV },
2200 { "req.hdr", smp_fetch_hdr, ARG2(0,STR,SINT), val_hdr, SMP_T_STR, SMP_USE_HRQHV },
2201 { "req.hdr_cnt", smp_fetch_hdr_cnt, ARG1(0,STR), NULL, SMP_T_SINT, SMP_USE_HRQHV },
2202 { "req.hdr_ip", smp_fetch_hdr_ip, ARG2(0,STR,SINT), val_hdr, SMP_T_IPV4, SMP_USE_HRQHV },
2203 { "req.hdr_names", smp_fetch_hdr_names, ARG1(0,STR), NULL, SMP_T_STR, SMP_USE_HRQHV },
2204 { "req.hdr_val", smp_fetch_hdr_val, ARG2(0,STR,SINT), val_hdr, SMP_T_SINT, SMP_USE_HRQHV },
2205
2206 /* explicit req.{cook,hdr} are used to force the fetch direction to be response-only */
2207 { "res.cook", smp_fetch_cookie, ARG1(0,STR), NULL, SMP_T_STR, SMP_USE_HRSHV },
2208 { "res.cook_cnt", smp_fetch_cookie_cnt, ARG1(0,STR), NULL, SMP_T_SINT, SMP_USE_HRSHV },
2209 { "res.cook_val", smp_fetch_cookie_val, ARG1(0,STR), NULL, SMP_T_SINT, SMP_USE_HRSHV },
2210
2211 { "res.fhdr", smp_fetch_fhdr, ARG2(0,STR,SINT), val_hdr, SMP_T_STR, SMP_USE_HRSHV },
2212 { "res.fhdr_cnt", smp_fetch_fhdr_cnt, ARG1(0,STR), NULL, SMP_T_SINT, SMP_USE_HRSHV },
2213 { "res.hdr", smp_fetch_hdr, ARG2(0,STR,SINT), val_hdr, SMP_T_STR, SMP_USE_HRSHV },
2214 { "res.hdr_cnt", smp_fetch_hdr_cnt, ARG1(0,STR), NULL, SMP_T_SINT, SMP_USE_HRSHV },
2215 { "res.hdr_ip", smp_fetch_hdr_ip, ARG2(0,STR,SINT), val_hdr, SMP_T_IPV4, SMP_USE_HRSHV },
2216 { "res.hdr_names", smp_fetch_hdr_names, ARG1(0,STR), NULL, SMP_T_STR, SMP_USE_HRSHV },
2217 { "res.hdr_val", smp_fetch_hdr_val, ARG2(0,STR,SINT), val_hdr, SMP_T_SINT, SMP_USE_HRSHV },
2218
2219 /* scook is valid only on the response and is used for ACL compatibility */
2220 { "scook", smp_fetch_cookie, ARG1(0,STR), NULL, SMP_T_STR, SMP_USE_HRSHV },
2221 { "scook_cnt", smp_fetch_cookie_cnt, ARG1(0,STR), NULL, SMP_T_SINT, SMP_USE_HRSHV },
2222 { "scook_val", smp_fetch_cookie_val, ARG1(0,STR), NULL, SMP_T_SINT, SMP_USE_HRSHV },
Willy Tarreau79e57332018-10-02 16:01:16 +02002223
2224 /* shdr is valid only on the response and is used for ACL compatibility */
2225 { "shdr", smp_fetch_hdr, ARG2(0,STR,SINT), val_hdr, SMP_T_STR, SMP_USE_HRSHV },
2226 { "shdr_cnt", smp_fetch_hdr_cnt, ARG1(0,STR), NULL, SMP_T_SINT, SMP_USE_HRSHV },
2227 { "shdr_ip", smp_fetch_hdr_ip, ARG2(0,STR,SINT), val_hdr, SMP_T_IPV4, SMP_USE_HRSHV },
2228 { "shdr_val", smp_fetch_hdr_val, ARG2(0,STR,SINT), val_hdr, SMP_T_SINT, SMP_USE_HRSHV },
2229
2230 { "status", smp_fetch_stcode, 0, NULL, SMP_T_SINT, SMP_USE_HRSHP },
2231 { "unique-id", smp_fetch_uniqueid, 0, NULL, SMP_T_STR, SMP_SRC_L4SRV },
2232 { "url", smp_fetch_url, 0, NULL, SMP_T_STR, SMP_USE_HRQHV },
2233 { "url32", smp_fetch_url32, 0, NULL, SMP_T_SINT, SMP_USE_HRQHV },
2234 { "url32+src", smp_fetch_url32_src, 0, NULL, SMP_T_BIN, SMP_USE_HRQHV },
2235 { "url_ip", smp_fetch_url_ip, 0, NULL, SMP_T_IPV4, SMP_USE_HRQHV },
2236 { "url_port", smp_fetch_url_port, 0, NULL, SMP_T_SINT, SMP_USE_HRQHV },
2237 { "url_param", smp_fetch_url_param, ARG2(0,STR,STR), NULL, SMP_T_STR, SMP_USE_HRQHV },
2238 { "urlp" , smp_fetch_url_param, ARG2(0,STR,STR), NULL, SMP_T_STR, SMP_USE_HRQHV },
2239 { "urlp_val", smp_fetch_url_param_val, ARG2(0,STR,STR), NULL, SMP_T_SINT, SMP_USE_HRQHV },
Christopher Faulet16032ab2020-04-30 11:30:00 +02002240
Willy Tarreau79e57332018-10-02 16:01:16 +02002241 { /* END */ },
2242}};
2243
Willy Tarreau0108d902018-11-25 19:14:37 +01002244INITCALL1(STG_REGISTER, sample_register_fetches, &sample_fetch_keywords);
Willy Tarreau79e57332018-10-02 16:01:16 +02002245
2246/*
2247 * Local variables:
2248 * c-indent-level: 8
2249 * c-basic-offset: 8
2250 * End:
2251 */