blob: 55f34b8b503a7f9153cb85eaec3d65dbaf1fb90b [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>
Willy Tarreau48fbcae2020-06-03 18:09:46 +020039#include <haproxy/tools.h>
Willy Tarreaud6788052020-05-27 15:59:00 +020040#include <haproxy/version.h>
Willy Tarreau79e57332018-10-02 16:01:16 +020041
Willy Tarreau79e57332018-10-02 16:01:16 +020042
43/* this struct is used between calls to smp_fetch_hdr() or smp_fetch_cookie() */
Christopher Fauletef453ed2018-10-24 21:39:27 +020044static THREAD_LOCAL struct http_hdr_ctx static_http_hdr_ctx;
Richard Russo458eafb2019-07-31 11:45:56 -070045/* this is used to convert raw connection buffers to htx */
46static THREAD_LOCAL struct buffer static_raw_htx_chunk;
47static THREAD_LOCAL char *static_raw_htx_buf;
Christopher Fauletef453ed2018-10-24 21:39:27 +020048
Christopher Faulet89dc4992019-04-17 12:02:59 +020049#define SMP_REQ_CHN(smp) (smp->strm ? &smp->strm->req : NULL)
50#define SMP_RES_CHN(smp) (smp->strm ? &smp->strm->res : NULL)
Willy Tarreau79e57332018-10-02 16:01:16 +020051
Richard Russo458eafb2019-07-31 11:45:56 -070052/* This function returns the static htx chunk, where raw connections get
53 * converted to HTX as needed for samplxsing.
54 */
55struct buffer *get_raw_htx_chunk(void)
56{
57 chunk_reset(&static_raw_htx_chunk);
58 return &static_raw_htx_chunk;
59}
60
61static int alloc_raw_htx_chunk_per_thread()
62{
63 static_raw_htx_buf = malloc(global.tune.bufsize);
64 if (!static_raw_htx_buf)
65 return 0;
66 chunk_init(&static_raw_htx_chunk, static_raw_htx_buf, global.tune.bufsize);
67 return 1;
68}
69
70static void free_raw_htx_chunk_per_thread()
71{
Willy Tarreau61cfdf42021-02-20 10:46:51 +010072 ha_free(&static_raw_htx_buf);
Richard Russo458eafb2019-07-31 11:45:56 -070073}
74
75REGISTER_PER_THREAD_ALLOC(alloc_raw_htx_chunk_per_thread);
76REGISTER_PER_THREAD_FREE(free_raw_htx_chunk_per_thread);
77
Willy Tarreau79e57332018-10-02 16:01:16 +020078/*
79 * Returns the data from Authorization header. Function may be called more
80 * than once so data is stored in txn->auth_data. When no header is found
81 * or auth method is unknown auth_method is set to HTTP_AUTH_WRONG to avoid
82 * searching again for something we are unable to find anyway. However, if
83 * the result if valid, the cache is not reused because we would risk to
84 * have the credentials overwritten by another stream in parallel.
Willy Tarreaueae83722020-04-29 11:52:51 +020085 * The caller is responsible for passing a sample with a valid stream/txn,
86 * and a valid htx.
Willy Tarreau79e57332018-10-02 16:01:16 +020087 */
88
Christopher Fauletcd761952019-07-15 13:58:29 +020089static int get_http_auth(struct sample *smp, struct htx *htx)
Willy Tarreau79e57332018-10-02 16:01:16 +020090{
Christopher Faulet311c7ea2018-10-24 21:41:55 +020091 struct stream *s = smp->strm;
Willy Tarreau79e57332018-10-02 16:01:16 +020092 struct http_txn *txn = s->txn;
Christopher Faulet6d1dd462019-07-15 14:36:03 +020093 struct http_hdr_ctx ctx = { .blk = NULL };
94 struct ist hdr;
Willy Tarreau79e57332018-10-02 16:01:16 +020095 struct buffer auth_method;
Christopher Faulet6d1dd462019-07-15 14:36:03 +020096 char *p;
Willy Tarreau79e57332018-10-02 16:01:16 +020097 int len;
98
99#ifdef DEBUG_AUTH
100 printf("Auth for stream %p: %d\n", s, txn->auth.method);
101#endif
Willy Tarreau79e57332018-10-02 16:01:16 +0200102 if (txn->auth.method == HTTP_AUTH_WRONG)
103 return 0;
104
105 txn->auth.method = HTTP_AUTH_WRONG;
106
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200107 if (txn->flags & TX_USE_PX_CONN)
108 hdr = ist("Proxy-Authorization");
109 else
110 hdr = ist("Authorization");
Willy Tarreau79e57332018-10-02 16:01:16 +0200111
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200112 ctx.blk = NULL;
113 if (!http_find_header(htx, hdr, &ctx, 0))
114 return 0;
Willy Tarreau79e57332018-10-02 16:01:16 +0200115
Willy Tarreau17254932020-09-02 07:08:47 +0200116 p = memchr(ctx.value.ptr, ' ', ctx.value.len);
117 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 +0200118 return 0;
Willy Tarreau17254932020-09-02 07:08:47 +0200119 len = p - ctx.value.ptr;
Willy Tarreau79e57332018-10-02 16:01:16 +0200120
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200121 if (chunk_initlen(&auth_method, ctx.value.ptr, 0, len) != 1)
122 return 0;
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200123
Remi Tricot-Le Breton68c4eae2021-10-29 15:25:18 +0200124 /* According to RFC7235, there could be multiple spaces between the
125 * scheme and its value, we must skip all of them.
126 */
127 while (p < istend(ctx.value) && *p == ' ')
128 ++p;
129
130 chunk_initlen(&txn->auth.method_data, p, 0, istend(ctx.value) - p);
Willy Tarreau79e57332018-10-02 16:01:16 +0200131
132 if (!strncasecmp("Basic", auth_method.area, auth_method.data)) {
133 struct buffer *http_auth = get_trash_chunk();
134
135 len = base64dec(txn->auth.method_data.area,
136 txn->auth.method_data.data,
137 http_auth->area, global.tune.bufsize - 1);
138
139 if (len < 0)
140 return 0;
141
142
143 http_auth->area[len] = '\0';
144
145 p = strchr(http_auth->area, ':');
146
147 if (!p)
148 return 0;
149
150 txn->auth.user = http_auth->area;
151 *p = '\0';
152 txn->auth.pass = p+1;
153
154 txn->auth.method = HTTP_AUTH_BASIC;
155 return 1;
Remi Tricot-Le Bretonf5dd3372021-10-01 15:36:53 +0200156 } else if (!strncasecmp("Bearer", auth_method.area, auth_method.data)) {
157 txn->auth.method = HTTP_AUTH_BEARER;
158 return 1;
Willy Tarreau79e57332018-10-02 16:01:16 +0200159 }
160
161 return 0;
162}
163
164/* This function ensures that the prerequisites for an L7 fetch are ready,
165 * which means that a request or response is ready. If some data is missing,
166 * a parsing attempt is made. This is useful in TCP-based ACLs which are able
Christopher Faulet5ec8bcb2019-04-17 12:04:12 +0200167 * to extract data from L7. If <vol> is non-null during a prefetch, another
168 * test is made to ensure the required information is not gone.
Christopher Fauletef453ed2018-10-24 21:39:27 +0200169 *
170 * The function returns :
171 * NULL with SMP_F_MAY_CHANGE in the sample flags if some data is missing to
172 * decide whether or not an HTTP message is present ;
173 * NULL if the requested data cannot be fetched or if it is certain that
Willy Tarreaueae83722020-04-29 11:52:51 +0200174 * we'll never have any HTTP message there; this includes null strm or chn.
Willy Tarreaua6d98792020-08-12 14:04:52 +0200175 * NULL if the sample's direction does not match the channel's (i.e. the
176 * function was asked to work on the wrong channel)
Christopher Fauletef453ed2018-10-24 21:39:27 +0200177 * The HTX message if ready
178 */
Christopher Faulet778f5ed2020-04-29 15:51:55 +0200179struct htx *smp_prefetch_htx(struct sample *smp, struct channel *chn, struct check *check, int vol)
Christopher Fauletef453ed2018-10-24 21:39:27 +0200180{
Christopher Fauletef453ed2018-10-24 21:39:27 +0200181 struct stream *s = smp->strm;
Christopher Fauletef453ed2018-10-24 21:39:27 +0200182 struct http_txn *txn = NULL;
183 struct htx *htx = NULL;
Christopher Faulet89dc4992019-04-17 12:02:59 +0200184 struct http_msg *msg;
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100185 struct htx_sl *sl;
Christopher Fauletef453ed2018-10-24 21:39:27 +0200186
Willy Tarreaua6d98792020-08-12 14:04:52 +0200187 if (chn &&
188 (((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ && (chn->flags & CF_ISRESP)) ||
189 ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES && !(chn->flags & CF_ISRESP))))
190 return 0;
191
Christopher Fauletef453ed2018-10-24 21:39:27 +0200192 /* Note: it is possible that <s> is NULL when called before stream
193 * initialization (eg: tcp-request connection), so this function is the
194 * one responsible for guarding against this case for all HTTP users.
Christopher Faulet778f5ed2020-04-29 15:51:55 +0200195 *
196 * In the health check context, the stream and the channel must be NULL
197 * and <check> must be set. In this case, only the input buffer,
198 * corresponding to the response, is considered. It is the caller
199 * responsibility to provide <check>.
Christopher Fauletef453ed2018-10-24 21:39:27 +0200200 */
Christopher Faulet778f5ed2020-04-29 15:51:55 +0200201 BUG_ON(check && (s || chn));
202 if (!s || !chn) {
203 if (check) {
204 htx = htxbuf(&check->bi);
205
206 /* Analyse not yet started */
207 if (htx_is_empty(htx) || htx->first == -1)
208 return NULL;
209
210 sl = http_get_stline(htx);
211 if (vol && !sl) {
212 /* The start-line was already forwarded, it is too late to fetch anything */
213 return NULL;
214 }
215 goto end;
216 }
217
Christopher Fauletef453ed2018-10-24 21:39:27 +0200218 return NULL;
Christopher Faulet778f5ed2020-04-29 15:51:55 +0200219 }
Christopher Fauletef453ed2018-10-24 21:39:27 +0200220
Christopher Faulet75f619a2021-03-08 19:12:58 +0100221 if (!s->txn && !http_create_txn(s))
222 return NULL;
Christopher Faulet89dc4992019-04-17 12:02:59 +0200223 txn = s->txn;
224 msg = (!(chn->flags & CF_ISRESP) ? &txn->req : &txn->rsp);
Christopher Fauletef453ed2018-10-24 21:39:27 +0200225
Christopher Fauleteca88542019-04-03 10:12:42 +0200226 if (IS_HTX_STRM(s)) {
Christopher Faulet89dc4992019-04-17 12:02:59 +0200227 htx = htxbuf(&chn->buf);
Christopher Fauletef453ed2018-10-24 21:39:27 +0200228
Christopher Faulet89dc4992019-04-17 12:02:59 +0200229 if (msg->msg_state == HTTP_MSG_ERROR || (htx->flags & HTX_FL_PARSING_ERROR))
230 return NULL;
Christopher Fauletef453ed2018-10-24 21:39:27 +0200231
Christopher Faulet89dc4992019-04-17 12:02:59 +0200232 if (msg->msg_state < HTTP_MSG_BODY) {
233 /* Analyse not yet started */
Christopher Faulet29f17582019-05-23 11:03:26 +0200234 if (htx_is_empty(htx) || htx->first == -1) {
Christopher Fauletef453ed2018-10-24 21:39:27 +0200235 /* Parsing is done by the mux, just wait */
236 smp->flags |= SMP_F_MAY_CHANGE;
237 return NULL;
238 }
239 }
Christopher Faulet297fbb42019-05-13 14:41:27 +0200240 sl = http_get_stline(htx);
Christopher Faulet5ec8bcb2019-04-17 12:04:12 +0200241 if (vol && !sl) {
Christopher Faulet89dc4992019-04-17 12:02:59 +0200242 /* The start-line was already forwarded, it is too late to fetch anything */
243 return NULL;
244 }
Christopher Fauletef453ed2018-10-24 21:39:27 +0200245 }
Christopher Fauleteca88542019-04-03 10:12:42 +0200246 else { /* RAW mode */
Christopher Faulet89dc4992019-04-17 12:02:59 +0200247 struct buffer *buf;
248 struct h1m h1m;
Christopher Faulete4ab11b2019-06-11 15:05:37 +0200249 struct http_hdr hdrs[global.tune.max_http_hdr];
Christopher Faulet89dc4992019-04-17 12:02:59 +0200250 union h1_sl h1sl;
251 unsigned int flags = HTX_FL_NONE;
252 int ret;
Christopher Fauletef453ed2018-10-24 21:39:27 +0200253
Christopher Faulet89dc4992019-04-17 12:02:59 +0200254 /* no HTTP fetch on the response in TCP mode */
255 if (chn->flags & CF_ISRESP)
256 return NULL;
Christopher Fauletef453ed2018-10-24 21:39:27 +0200257
Christopher Faulet89dc4992019-04-17 12:02:59 +0200258 /* Now we are working on the request only */
259 buf = &chn->buf;
260 if (b_head(buf) + b_data(buf) > b_wrap(buf))
261 b_slow_realign(buf, trash.area, 0);
Christopher Fauletef453ed2018-10-24 21:39:27 +0200262
Christopher Faulet89dc4992019-04-17 12:02:59 +0200263 h1m_init_req(&h1m);
264 ret = h1_headers_to_hdr_list(b_head(buf), b_stop(buf),
265 hdrs, sizeof(hdrs)/sizeof(hdrs[0]), &h1m, &h1sl);
266 if (ret <= 0) {
267 /* Invalid or too big*/
268 if (ret < 0 || channel_full(&s->req, global.tune.maxrewrite))
Christopher Fauletef453ed2018-10-24 21:39:27 +0200269 return NULL;
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100270
Christopher Faulet89dc4992019-04-17 12:02:59 +0200271 /* wait for a full request */
272 smp->flags |= SMP_F_MAY_CHANGE;
273 return NULL;
274 }
Christopher Fauletf1ba18d2018-11-26 21:37:08 +0100275
Ilya Shipitsin46a030c2020-07-05 16:36:08 +0500276 /* OK we just got a valid HTTP message. We have to convert it
Christopher Faulet89dc4992019-04-17 12:02:59 +0200277 * into an HTX message.
278 */
279 if (unlikely(h1sl.rq.v.len == 0)) {
280 /* try to convert HTTP/0.9 requests to HTTP/1.0 */
281 if (h1sl.rq.meth != HTTP_METH_GET || !h1sl.rq.u.len)
Christopher Fauletef453ed2018-10-24 21:39:27 +0200282 return NULL;
Christopher Faulet89dc4992019-04-17 12:02:59 +0200283 h1sl.rq.v = ist("HTTP/1.0");
Christopher Fauletef453ed2018-10-24 21:39:27 +0200284 }
Christopher Faulet89dc4992019-04-17 12:02:59 +0200285
286 /* Set HTX start-line flags */
287 if (h1m.flags & H1_MF_VER_11)
288 flags |= HTX_SL_F_VER_11;
289 if (h1m.flags & H1_MF_XFER_ENC)
290 flags |= HTX_SL_F_XFER_ENC;
291 flags |= HTX_SL_F_XFER_LEN;
292 if (h1m.flags & H1_MF_CHNK)
293 flags |= HTX_SL_F_CHNK;
294 else if (h1m.flags & H1_MF_CLEN)
295 flags |= HTX_SL_F_CLEN;
296
Richard Russo458eafb2019-07-31 11:45:56 -0700297 htx = htx_from_buf(get_raw_htx_chunk());
Christopher Faulet89dc4992019-04-17 12:02:59 +0200298 sl = htx_add_stline(htx, HTX_BLK_REQ_SL, flags, h1sl.rq.m, h1sl.rq.u, h1sl.rq.v);
299 if (!sl || !htx_add_all_headers(htx, hdrs))
Christopher Fauletef453ed2018-10-24 21:39:27 +0200300 return NULL;
Willy Tarreauce9bbf52019-05-13 08:32:31 +0200301 sl->info.req.meth = h1sl.rq.meth;
Christopher Faulet89dc4992019-04-17 12:02:59 +0200302 }
303
304 /* OK we just got a valid HTTP message. If not already done by
305 * HTTP analyzers, we have some minor preparation to perform so
306 * that further checks can rely on HTTP tests.
307 */
308 if (sl && msg->msg_state < HTTP_MSG_BODY) {
309 if (!(chn->flags & CF_ISRESP)) {
310 txn->meth = sl->info.req.meth;
311 if (txn->meth == HTTP_METH_GET || txn->meth == HTTP_METH_HEAD)
312 s->flags |= SF_REDIRECTABLE;
Christopher Fauletef453ed2018-10-24 21:39:27 +0200313 }
Christopher Faulet89dc4992019-04-17 12:02:59 +0200314 else
315 txn->status = sl->info.res.status;
316 if (sl->flags & HTX_SL_F_VER_11)
317 msg->flags |= HTTP_MSGF_VER_11;
Christopher Fauletef453ed2018-10-24 21:39:27 +0200318 }
319
320 /* everything's OK */
Christopher Faulet778f5ed2020-04-29 15:51:55 +0200321 end:
Christopher Fauletef453ed2018-10-24 21:39:27 +0200322 return htx;
323}
324
Willy Tarreau79e57332018-10-02 16:01:16 +0200325/* This function fetches the method of current HTTP request and stores
326 * it in the global pattern struct as a chunk. There are two possibilities :
327 * - if the method is known (not HTTP_METH_OTHER), its identifier is stored
328 * in <len> and <ptr> is NULL ;
329 * - if the method is unknown (HTTP_METH_OTHER), <ptr> points to the text and
330 * <len> to its length.
331 * This is intended to be used with pat_match_meth() only.
332 */
333static int smp_fetch_meth(const struct arg *args, struct sample *smp, const char *kw, void *private)
334{
Christopher Faulet89dc4992019-04-17 12:02:59 +0200335 struct channel *chn = SMP_REQ_CHN(smp);
Willy Tarreau79e57332018-10-02 16:01:16 +0200336 struct http_txn *txn;
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200337 int meth;
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200338
Willy Tarreaua6d98792020-08-12 14:04:52 +0200339 txn = smp->strm->txn;
340 if (!txn)
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200341 return 0;
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200342
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200343 meth = txn->meth;
344 smp->data.type = SMP_T_METH;
345 smp->data.u.meth.meth = meth;
346 if (meth == HTTP_METH_OTHER) {
Willy Tarreaua6d98792020-08-12 14:04:52 +0200347 struct htx *htx;
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200348 struct htx_sl *sl;
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200349
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200350 if ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_RES) {
351 /* ensure the indexes are not affected */
352 return 0;
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200353 }
Willy Tarreaua6d98792020-08-12 14:04:52 +0200354
Christopher Faulet6f97a612021-04-15 09:28:02 +0200355 htx = smp_prefetch_htx(smp, chn, NULL, 1);
Willy Tarreaua6d98792020-08-12 14:04:52 +0200356 if (!htx)
357 return 0;
358
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200359 sl = http_get_stline(htx);
360 smp->flags |= SMP_F_CONST;
361 smp->data.u.meth.str.area = HTX_SL_REQ_MPTR(sl);
362 smp->data.u.meth.str.data = HTX_SL_REQ_MLEN(sl);
Willy Tarreau79e57332018-10-02 16:01:16 +0200363 }
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200364 smp->flags |= SMP_F_VOL_1ST;
Willy Tarreau79e57332018-10-02 16:01:16 +0200365 return 1;
366}
367
368static int smp_fetch_rqver(const struct arg *args, struct sample *smp, const char *kw, void *private)
369{
Christopher Faulet89dc4992019-04-17 12:02:59 +0200370 struct channel *chn = SMP_REQ_CHN(smp);
Christopher Faulet778f5ed2020-04-29 15:51:55 +0200371 struct htx *htx = smp_prefetch_htx(smp, chn, NULL, 1);
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200372 struct htx_sl *sl;
Willy Tarreau79e57332018-10-02 16:01:16 +0200373 char *ptr;
374 int len;
375
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200376 if (!htx)
377 return 0;
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200378
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200379 sl = http_get_stline(htx);
380 len = HTX_SL_REQ_VLEN(sl);
381 ptr = HTX_SL_REQ_VPTR(sl);
Willy Tarreau79e57332018-10-02 16:01:16 +0200382
383 while ((len-- > 0) && (*ptr++ != '/'));
384 if (len <= 0)
385 return 0;
386
387 smp->data.type = SMP_T_STR;
388 smp->data.u.str.area = ptr;
389 smp->data.u.str.data = len;
390
391 smp->flags = SMP_F_VOL_1ST | SMP_F_CONST;
392 return 1;
393}
394
395static int smp_fetch_stver(const struct arg *args, struct sample *smp, const char *kw, void *private)
396{
Christopher Faulet89dc4992019-04-17 12:02:59 +0200397 struct channel *chn = SMP_RES_CHN(smp);
Christopher Fauletf98e6262020-05-06 09:42:04 +0200398 struct check *check = objt_check(smp->sess->origin);
Christopher Faulet16032ab2020-04-30 11:30:00 +0200399 struct htx *htx = smp_prefetch_htx(smp, chn, check, 1);
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200400 struct htx_sl *sl;
Willy Tarreau79e57332018-10-02 16:01:16 +0200401 char *ptr;
402 int len;
403
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200404 if (!htx)
405 return 0;
Willy Tarreau79e57332018-10-02 16:01:16 +0200406
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200407 sl = http_get_stline(htx);
408 len = HTX_SL_RES_VLEN(sl);
409 ptr = HTX_SL_RES_VPTR(sl);
Willy Tarreau79e57332018-10-02 16:01:16 +0200410
411 while ((len-- > 0) && (*ptr++ != '/'));
412 if (len <= 0)
413 return 0;
414
415 smp->data.type = SMP_T_STR;
416 smp->data.u.str.area = ptr;
417 smp->data.u.str.data = len;
418
419 smp->flags = SMP_F_VOL_1ST | SMP_F_CONST;
420 return 1;
421}
422
423/* 3. Check on Status Code. We manipulate integers here. */
424static int smp_fetch_stcode(const struct arg *args, struct sample *smp, const char *kw, void *private)
425{
Christopher Faulet89dc4992019-04-17 12:02:59 +0200426 struct channel *chn = SMP_RES_CHN(smp);
Christopher Fauletf98e6262020-05-06 09:42:04 +0200427 struct check *check = objt_check(smp->sess->origin);
Christopher Faulet16032ab2020-04-30 11:30:00 +0200428 struct htx *htx = smp_prefetch_htx(smp, chn, check, 1);
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200429 struct htx_sl *sl;
Willy Tarreau79e57332018-10-02 16:01:16 +0200430 char *ptr;
431 int len;
432
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200433 if (!htx)
434 return 0;
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200435
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200436 sl = http_get_stline(htx);
437 len = HTX_SL_RES_CLEN(sl);
438 ptr = HTX_SL_RES_CPTR(sl);
Willy Tarreau79e57332018-10-02 16:01:16 +0200439
440 smp->data.type = SMP_T_SINT;
441 smp->data.u.sint = __strl2ui(ptr, len);
442 smp->flags = SMP_F_VOL_1ST;
443 return 1;
444}
445
446static int smp_fetch_uniqueid(const struct arg *args, struct sample *smp, const char *kw, void *private)
447{
Tim Duesterhusa17e6622020-03-05 20:19:02 +0100448 struct ist unique_id;
Tim Duesterhus2825b4b2020-02-28 15:13:34 +0100449
Willy Tarreau79e57332018-10-02 16:01:16 +0200450 if (LIST_ISEMPTY(&smp->sess->fe->format_unique_id))
451 return 0;
452
Willy Tarreaua1062a42020-04-29 11:50:38 +0200453 if (!smp->strm)
454 return 0;
455
Tim Duesterhusa17e6622020-03-05 20:19:02 +0100456 unique_id = stream_generate_unique_id(smp->strm, &smp->sess->fe->format_unique_id);
457 if (!isttest(unique_id))
Tim Duesterhus2825b4b2020-02-28 15:13:34 +0100458 return 0;
459
Tim Duesterhusa17e6622020-03-05 20:19:02 +0100460 smp->data.u.str.area = smp->strm->unique_id.ptr;
461 smp->data.u.str.data = smp->strm->unique_id.len;
Tim Duesterhus2825b4b2020-02-28 15:13:34 +0100462 smp->data.type = SMP_T_STR;
Willy Tarreau79e57332018-10-02 16:01:16 +0200463 smp->flags = SMP_F_CONST;
464 return 1;
465}
466
467/* Returns a string block containing all headers including the
Joseph Herlant942eea32018-11-15 13:57:22 -0800468 * empty line which separes headers from the body. This is useful
469 * for some headers analysis.
Willy Tarreau79e57332018-10-02 16:01:16 +0200470 */
471static int smp_fetch_hdrs(const struct arg *args, struct sample *smp, const char *kw, void *private)
472{
Christopher Faulete596d182020-05-05 17:46:34 +0200473 /* possible keywords: req.hdrs, res.hdrs */
474 struct channel *chn = ((kw[2] == 'q') ? SMP_REQ_CHN(smp) : SMP_RES_CHN(smp));
Christopher Fauletf98e6262020-05-06 09:42:04 +0200475 struct check *check = ((kw[2] == 's') ? objt_check(smp->sess->origin) : NULL);
Christopher Faulet16032ab2020-04-30 11:30:00 +0200476 struct htx *htx = smp_prefetch_htx(smp, chn, check, 1);
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200477 struct buffer *temp;
478 int32_t pos;
Willy Tarreau79e57332018-10-02 16:01:16 +0200479
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200480 if (!htx)
481 return 0;
482 temp = get_trash_chunk();
483 for (pos = htx_get_first(htx); pos != -1; pos = htx_get_next(htx, pos)) {
484 struct htx_blk *blk = htx_get_blk(htx, pos);
485 enum htx_blk_type type = htx_get_blk_type(blk);
Willy Tarreau79e57332018-10-02 16:01:16 +0200486
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200487 if (type == HTX_BLK_HDR) {
488 struct ist n = htx_get_blk_name(htx, blk);
489 struct ist v = htx_get_blk_value(htx, blk);
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200490
Christopher Faulet53a899b2019-10-08 16:38:42 +0200491 if (!h1_format_htx_hdr(n, v, temp))
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200492 return 0;
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200493 }
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200494 else if (type == HTX_BLK_EOH) {
495 if (!chunk_memcat(temp, "\r\n", 2))
496 return 0;
497 break;
498 }
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200499 }
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200500 smp->data.type = SMP_T_STR;
501 smp->data.u.str = *temp;
Willy Tarreau79e57332018-10-02 16:01:16 +0200502 return 1;
503}
504
505/* Returns the header request in a length/value encoded format.
506 * This is useful for exchanges with the SPOE.
507 *
508 * A "length value" is a multibyte code encoding numbers. It uses the
509 * SPOE format. The encoding is the following:
510 *
511 * Each couple "header name" / "header value" is composed
512 * like this:
513 * "length value" "header name bytes"
514 * "length value" "header value bytes"
515 * When the last header is reached, the header name and the header
516 * value are empty. Their length are 0
517 */
518static int smp_fetch_hdrs_bin(const struct arg *args, struct sample *smp, const char *kw, void *private)
519{
Christopher Faulete596d182020-05-05 17:46:34 +0200520 /* possible keywords: req.hdrs_bin, res.hdrs_bin */
521 struct channel *chn = ((kw[2] == 'q') ? SMP_REQ_CHN(smp) : SMP_RES_CHN(smp));
Christopher Fauletf98e6262020-05-06 09:42:04 +0200522 struct check *check = ((kw[2] == 's') ? objt_check(smp->sess->origin) : NULL);
Christopher Faulet16032ab2020-04-30 11:30:00 +0200523 struct htx *htx = smp_prefetch_htx(smp, chn, check, 1);
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200524 struct buffer *temp;
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200525 char *p, *end;
526 int32_t pos;
527 int ret;
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200528
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200529 if (!htx)
530 return 0;
531 temp = get_trash_chunk();
532 p = temp->area;
533 end = temp->area + temp->size;
534 for (pos = htx_get_first(htx); pos != -1; pos = htx_get_next(htx, pos)) {
535 struct htx_blk *blk = htx_get_blk(htx, pos);
536 enum htx_blk_type type = htx_get_blk_type(blk);
537 struct ist n, v;
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200538
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200539 if (type == HTX_BLK_HDR) {
540 n = htx_get_blk_name(htx,blk);
541 v = htx_get_blk_value(htx, blk);
Willy Tarreau79e57332018-10-02 16:01:16 +0200542
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200543 /* encode the header name. */
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200544 ret = encode_varint(n.len, &p, end);
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200545 if (ret == -1)
546 return 0;
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200547 if (p + n.len > end)
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200548 return 0;
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200549 memcpy(p, n.ptr, n.len);
550 p += n.len;
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200551
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200552 /* encode the header value. */
553 ret = encode_varint(v.len, &p, end);
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200554 if (ret == -1)
555 return 0;
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200556 if (p + v.len > end)
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200557 return 0;
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200558 memcpy(p, v.ptr, v.len);
559 p += v.len;
Willy Tarreau79e57332018-10-02 16:01:16 +0200560
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200561 }
562 else if (type == HTX_BLK_EOH) {
563 /* encode the end of the header list with empty
564 * header name and header value.
565 */
566 ret = encode_varint(0, &p, end);
567 if (ret == -1)
568 return 0;
569 ret = encode_varint(0, &p, end);
570 if (ret == -1)
571 return 0;
572 break;
573 }
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200574 }
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200575
576 /* Initialise sample data which will be filled. */
577 smp->data.type = SMP_T_BIN;
578 smp->data.u.str.area = temp->area;
579 smp->data.u.str.data = p - temp->area;
580 smp->data.u.str.size = temp->size;
Willy Tarreau79e57332018-10-02 16:01:16 +0200581 return 1;
582}
583
584/* returns the longest available part of the body. This requires that the body
585 * has been waited for using http-buffer-request.
586 */
587static int smp_fetch_body(const struct arg *args, struct sample *smp, const char *kw, void *private)
588{
Christopher Faulete596d182020-05-05 17:46:34 +0200589 /* possible keywords: req.body, res.body */
590 struct channel *chn = ((kw[2] == 'q') ? SMP_REQ_CHN(smp) : SMP_RES_CHN(smp));
Christopher Fauletf98e6262020-05-06 09:42:04 +0200591 struct check *check = ((kw[2] == 's') ? objt_check(smp->sess->origin) : NULL);
Christopher Faulet16032ab2020-04-30 11:30:00 +0200592 struct htx *htx = smp_prefetch_htx(smp, chn, check, 1);
Willy Tarreau79e57332018-10-02 16:01:16 +0200593 struct buffer *temp;
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200594 int32_t pos;
Christopher Fauleta9ffc412020-11-25 08:08:08 +0100595 int finished = 0;
Willy Tarreau79e57332018-10-02 16:01:16 +0200596
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200597 if (!htx)
598 return 0;
Willy Tarreau79e57332018-10-02 16:01:16 +0200599
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200600 temp = get_trash_chunk();
601 for (pos = htx_get_first(htx); pos != -1; pos = htx_get_next(htx, pos)) {
602 struct htx_blk *blk = htx_get_blk(htx, pos);
603 enum htx_blk_type type = htx_get_blk_type(blk);
Willy Tarreau79e57332018-10-02 16:01:16 +0200604
Christopher Fauletd1ac2b92020-12-02 19:12:22 +0100605 if (type == HTX_BLK_TLR || type == HTX_BLK_EOT) {
Christopher Fauleta9ffc412020-11-25 08:08:08 +0100606 finished = 1;
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200607 break;
Christopher Fauleta9ffc412020-11-25 08:08:08 +0100608 }
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200609 if (type == HTX_BLK_DATA) {
Christopher Faulet53a899b2019-10-08 16:38:42 +0200610 if (!h1_format_htx_data(htx_get_blk_value(htx, blk), temp, 0))
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200611 return 0;
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200612 }
Willy Tarreau79e57332018-10-02 16:01:16 +0200613 }
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200614
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200615 smp->data.type = SMP_T_BIN;
616 smp->data.u.str = *temp;
617 smp->flags = SMP_F_VOL_TEST;
Willy Tarreau9dc92b22020-06-15 18:01:10 +0200618
Christopher Fauleta9ffc412020-11-25 08:08:08 +0100619 if (!finished && (check || (chn && !channel_full(chn, global.tune.maxrewrite) &&
620 !(chn->flags & (CF_EOI|CF_SHUTR|CF_READ_ERROR)))))
Willy Tarreau9dc92b22020-06-15 18:01:10 +0200621 smp->flags |= SMP_F_MAY_CHANGE;
622
Willy Tarreau79e57332018-10-02 16:01:16 +0200623 return 1;
624}
625
626
627/* returns the available length of the body. This requires that the body
628 * has been waited for using http-buffer-request.
629 */
630static int smp_fetch_body_len(const struct arg *args, struct sample *smp, const char *kw, void *private)
631{
Christopher Faulete596d182020-05-05 17:46:34 +0200632 /* possible keywords: req.body_len, res.body_len */
633 struct channel *chn = ((kw[2] == 'q') ? SMP_REQ_CHN(smp) : SMP_RES_CHN(smp));
Christopher Fauletf98e6262020-05-06 09:42:04 +0200634 struct check *check = ((kw[2] == 's') ? objt_check(smp->sess->origin) : NULL);
Christopher Faulet16032ab2020-04-30 11:30:00 +0200635 struct htx *htx = smp_prefetch_htx(smp, chn, check, 1);
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200636 int32_t pos;
637 unsigned long long len = 0;
Christopher Fauletc16317d2018-12-12 14:11:22 +0100638
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200639 if (!htx)
640 return 0;
Christopher Fauletc16317d2018-12-12 14:11:22 +0100641
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200642 for (pos = htx_get_first(htx); pos != -1; pos = htx_get_next(htx, pos)) {
643 struct htx_blk *blk = htx_get_blk(htx, pos);
644 enum htx_blk_type type = htx_get_blk_type(blk);
Christopher Fauletc16317d2018-12-12 14:11:22 +0100645
Christopher Fauletd1ac2b92020-12-02 19:12:22 +0100646 if (type == HTX_BLK_TLR || type == HTX_BLK_EOT)
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200647 break;
648 if (type == HTX_BLK_DATA)
649 len += htx_get_blksz(blk);
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200650 }
Willy Tarreau79e57332018-10-02 16:01:16 +0200651
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200652 smp->data.type = SMP_T_SINT;
653 smp->data.u.sint = len;
654 smp->flags = SMP_F_VOL_TEST;
Willy Tarreau79e57332018-10-02 16:01:16 +0200655 return 1;
656}
657
658
659/* returns the advertised length of the body, or the advertised size of the
660 * chunks available in the buffer. This requires that the body has been waited
661 * for using http-buffer-request.
662 */
663static int smp_fetch_body_size(const struct arg *args, struct sample *smp, const char *kw, void *private)
664{
Christopher Faulete596d182020-05-05 17:46:34 +0200665 /* possible keywords: req.body_size, res.body_size */
666 struct channel *chn = ((kw[2] == 'q') ? SMP_REQ_CHN(smp) : SMP_RES_CHN(smp));
Christopher Fauletf98e6262020-05-06 09:42:04 +0200667 struct check *check = ((kw[2] == 's') ? objt_check(smp->sess->origin) : NULL);
Christopher Faulet16032ab2020-04-30 11:30:00 +0200668 struct htx *htx = smp_prefetch_htx(smp, chn, check, 1);
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200669 int32_t pos;
670 unsigned long long len = 0;
Christopher Faulet89dc4992019-04-17 12:02:59 +0200671
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200672 if (!htx)
673 return 0;
Christopher Fauletc16317d2018-12-12 14:11:22 +0100674
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200675 for (pos = htx_get_first(htx); pos != -1; pos = htx_get_next(htx, pos)) {
676 struct htx_blk *blk = htx_get_blk(htx, pos);
677 enum htx_blk_type type = htx_get_blk_type(blk);
Christopher Fauletc16317d2018-12-12 14:11:22 +0100678
Christopher Fauletd1ac2b92020-12-02 19:12:22 +0100679 if (type == HTX_BLK_TLR || type == HTX_BLK_EOT)
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200680 break;
681 if (type == HTX_BLK_DATA)
682 len += htx_get_blksz(blk);
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200683 }
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200684 if (htx->extra != ULLONG_MAX)
685 len += htx->extra;
Willy Tarreau79e57332018-10-02 16:01:16 +0200686
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200687 smp->data.type = SMP_T_SINT;
688 smp->data.u.sint = len;
689 smp->flags = SMP_F_VOL_TEST;
Willy Tarreau79e57332018-10-02 16:01:16 +0200690 return 1;
691}
692
693
694/* 4. Check on URL/URI. A pointer to the URI is stored. */
695static int smp_fetch_url(const struct arg *args, struct sample *smp, const char *kw, void *private)
696{
Christopher Faulet89dc4992019-04-17 12:02:59 +0200697 struct channel *chn = SMP_REQ_CHN(smp);
Christopher Faulet778f5ed2020-04-29 15:51:55 +0200698 struct htx *htx = smp_prefetch_htx(smp, chn, NULL, 1);
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200699 struct htx_sl *sl;
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200700
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200701 if (!htx)
702 return 0;
703 sl = http_get_stline(htx);
704 smp->data.type = SMP_T_STR;
705 smp->data.u.str.area = HTX_SL_REQ_UPTR(sl);
706 smp->data.u.str.data = HTX_SL_REQ_ULEN(sl);
707 smp->flags = SMP_F_VOL_1ST | SMP_F_CONST;
Willy Tarreau79e57332018-10-02 16:01:16 +0200708 return 1;
709}
710
711static int smp_fetch_url_ip(const struct arg *args, struct sample *smp, const char *kw, void *private)
712{
Christopher Faulet89dc4992019-04-17 12:02:59 +0200713 struct channel *chn = SMP_REQ_CHN(smp);
Christopher Faulet778f5ed2020-04-29 15:51:55 +0200714 struct htx *htx = smp_prefetch_htx(smp, chn, NULL, 1);
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200715 struct htx_sl *sl;
Willy Tarreau79e57332018-10-02 16:01:16 +0200716 struct sockaddr_storage addr;
717
Amaury Denoyellec89d5332021-05-10 11:23:34 +0200718 memset(&addr, 0, sizeof(addr));
719
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200720 if (!htx)
721 return 0;
722 sl = http_get_stline(htx);
Amaury Denoyellec89d5332021-05-10 11:23:34 +0200723 if (url2sa(HTX_SL_REQ_UPTR(sl), HTX_SL_REQ_ULEN(sl), &addr, NULL) < 0)
724 return 0;
Willy Tarreau79e57332018-10-02 16:01:16 +0200725
Willy Tarreau48584642021-05-09 10:32:54 +0200726 if (addr.ss_family != AF_INET)
Willy Tarreau79e57332018-10-02 16:01:16 +0200727 return 0;
728
729 smp->data.type = SMP_T_IPV4;
730 smp->data.u.ipv4 = ((struct sockaddr_in *)&addr)->sin_addr;
731 smp->flags = 0;
732 return 1;
733}
734
735static int smp_fetch_url_port(const struct arg *args, struct sample *smp, const char *kw, void *private)
736{
Christopher Faulet89dc4992019-04-17 12:02:59 +0200737 struct channel *chn = SMP_REQ_CHN(smp);
Christopher Faulet778f5ed2020-04-29 15:51:55 +0200738 struct htx *htx = smp_prefetch_htx(smp, chn, NULL, 1);
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200739 struct htx_sl *sl;
Willy Tarreau79e57332018-10-02 16:01:16 +0200740 struct sockaddr_storage addr;
741
Amaury Denoyellec89d5332021-05-10 11:23:34 +0200742 memset(&addr, 0, sizeof(addr));
743
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200744 if (!htx)
745 return 0;
746 sl = http_get_stline(htx);
Amaury Denoyellec89d5332021-05-10 11:23:34 +0200747 if (url2sa(HTX_SL_REQ_UPTR(sl), HTX_SL_REQ_ULEN(sl), &addr, NULL) < 0)
748 return 0;
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200749
Willy Tarreau48584642021-05-09 10:32:54 +0200750 if (addr.ss_family != AF_INET)
Willy Tarreau79e57332018-10-02 16:01:16 +0200751 return 0;
752
753 smp->data.type = SMP_T_SINT;
Willy Tarreau48584642021-05-09 10:32:54 +0200754 smp->data.u.sint = get_host_port(&addr);
Willy Tarreau79e57332018-10-02 16:01:16 +0200755 smp->flags = 0;
756 return 1;
757}
758
759/* Fetch an HTTP header. A pointer to the beginning of the value is returned.
760 * Accepts an optional argument of type string containing the header field name,
761 * and an optional argument of type signed or unsigned integer to request an
762 * explicit occurrence of the header. Note that in the event of a missing name,
763 * headers are considered from the first one. It does not stop on commas and
764 * returns full lines instead (useful for User-Agent or Date for example).
765 */
766static int smp_fetch_fhdr(const struct arg *args, struct sample *smp, const char *kw, void *private)
767{
Christopher Faulet89dc4992019-04-17 12:02:59 +0200768 /* possible keywords: req.fhdr, res.fhdr */
769 struct channel *chn = ((kw[2] == 'q') ? SMP_REQ_CHN(smp) : SMP_RES_CHN(smp));
Christopher Fauletf98e6262020-05-06 09:42:04 +0200770 struct check *check = ((kw[2] == 's') ? objt_check(smp->sess->origin) : NULL);
Christopher Faulet16032ab2020-04-30 11:30:00 +0200771 struct htx *htx = smp_prefetch_htx(smp, chn, check, 1);
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200772 struct http_hdr_ctx *ctx = smp->ctx.a[0];
773 struct ist name;
Willy Tarreau79e57332018-10-02 16:01:16 +0200774 int occ = 0;
Willy Tarreau79e57332018-10-02 16:01:16 +0200775
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200776 if (!ctx) {
777 /* first call */
778 ctx = &static_http_hdr_ctx;
779 ctx->blk = NULL;
780 smp->ctx.a[0] = ctx;
781 }
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200782
Christopher Faulet623af932021-01-29 11:22:15 +0100783 if (args[0].type != ARGT_STR)
784 return 0;
Tim Duesterhus92c696e2021-02-28 16:11:36 +0100785 name = ist2(args[0].data.str.area, args[0].data.str.data);
Willy Tarreau79e57332018-10-02 16:01:16 +0200786
Christopher Faulet623af932021-01-29 11:22:15 +0100787 if (args[1].type == ARGT_SINT)
788 occ = args[1].data.sint;
Willy Tarreau79e57332018-10-02 16:01:16 +0200789
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200790 if (!htx)
791 return 0;
Willy Tarreau79e57332018-10-02 16:01:16 +0200792
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200793 if (ctx && !(smp->flags & SMP_F_NOT_LAST))
794 /* search for header from the beginning */
795 ctx->blk = NULL;
Willy Tarreau79e57332018-10-02 16:01:16 +0200796
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200797 if (!occ && !(smp->opt & SMP_OPT_ITERATE))
798 /* no explicit occurrence and single fetch => last header by default */
799 occ = -1;
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200800
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200801 if (!occ)
802 /* prepare to report multiple occurrences for ACL fetches */
803 smp->flags |= SMP_F_NOT_LAST;
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200804
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200805 smp->data.type = SMP_T_STR;
806 smp->flags |= SMP_F_VOL_HDR | SMP_F_CONST;
807 if (http_get_htx_fhdr(htx, name, occ, ctx, &smp->data.u.str.area, &smp->data.u.str.data))
808 return 1;
Willy Tarreau79e57332018-10-02 16:01:16 +0200809 smp->flags &= ~SMP_F_NOT_LAST;
810 return 0;
811}
812
813/* 6. Check on HTTP header count. The number of occurrences is returned.
814 * Accepts exactly 1 argument of type string. It does not stop on commas and
815 * returns full lines instead (useful for User-Agent or Date for example).
816 */
817static int smp_fetch_fhdr_cnt(const struct arg *args, struct sample *smp, const char *kw, void *private)
818{
Christopher Faulet89dc4992019-04-17 12:02:59 +0200819 /* possible keywords: req.fhdr_cnt, res.fhdr_cnt */
820 struct channel *chn = ((kw[2] == 'q') ? SMP_REQ_CHN(smp) : SMP_RES_CHN(smp));
Christopher Fauletf98e6262020-05-06 09:42:04 +0200821 struct check *check = ((kw[2] == 's') ? objt_check(smp->sess->origin) : NULL);
Christopher Faulet16032ab2020-04-30 11:30:00 +0200822 struct htx *htx = smp_prefetch_htx(smp, chn, check, 1);
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200823 struct http_hdr_ctx ctx;
824 struct ist name;
Willy Tarreau79e57332018-10-02 16:01:16 +0200825 int cnt;
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200826
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200827 if (!htx)
828 return 0;
Willy Tarreau79e57332018-10-02 16:01:16 +0200829
Christopher Faulet623af932021-01-29 11:22:15 +0100830 if (args->type == ARGT_STR) {
Tim Duesterhus92c696e2021-02-28 16:11:36 +0100831 name = ist2(args->data.str.area, args->data.str.data);
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200832 } else {
Tim Duesterhus68a088d2021-02-28 16:11:37 +0100833 name = IST_NULL;
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200834 }
Willy Tarreau79e57332018-10-02 16:01:16 +0200835
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200836 ctx.blk = NULL;
837 cnt = 0;
838 while (http_find_header(htx, name, &ctx, 1))
839 cnt++;
Willy Tarreau79e57332018-10-02 16:01:16 +0200840 smp->data.type = SMP_T_SINT;
841 smp->data.u.sint = cnt;
842 smp->flags = SMP_F_VOL_HDR;
843 return 1;
844}
845
846static int smp_fetch_hdr_names(const struct arg *args, struct sample *smp, const char *kw, void *private)
847{
Christopher Faulet89dc4992019-04-17 12:02:59 +0200848 /* possible keywords: req.hdr_names, res.hdr_names */
849 struct channel *chn = ((kw[2] == 'q') ? SMP_REQ_CHN(smp) : SMP_RES_CHN(smp));
Christopher Fauletf98e6262020-05-06 09:42:04 +0200850 struct check *check = ((kw[2] == 's') ? objt_check(smp->sess->origin) : NULL);
Christopher Faulet16032ab2020-04-30 11:30:00 +0200851 struct htx *htx = smp_prefetch_htx(smp, chn, check, 1);
Willy Tarreau79e57332018-10-02 16:01:16 +0200852 struct buffer *temp;
853 char del = ',';
854
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200855 int32_t pos;
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200856
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200857 if (!htx)
858 return 0;
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200859
Christopher Faulet623af932021-01-29 11:22:15 +0100860 if (args->type == ARGT_STR)
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200861 del = *args[0].data.str.area;
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200862
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200863 temp = get_trash_chunk();
864 for (pos = htx_get_first(htx); pos != -1; pos = htx_get_next(htx, pos)) {
865 struct htx_blk *blk = htx_get_blk(htx, pos);
866 enum htx_blk_type type = htx_get_blk_type(blk);
867 struct ist n;
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200868
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200869 if (type == HTX_BLK_EOH)
870 break;
871 if (type != HTX_BLK_HDR)
872 continue;
873 n = htx_get_blk_name(htx, blk);
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200874
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200875 if (temp->data)
876 temp->area[temp->data++] = del;
Tim Duesterhus9f7ed8a2021-11-08 09:05:04 +0100877 chunk_istcat(temp, n);
Willy Tarreau79e57332018-10-02 16:01:16 +0200878 }
879
880 smp->data.type = SMP_T_STR;
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200881 smp->data.u.str = *temp;
Willy Tarreau79e57332018-10-02 16:01:16 +0200882 smp->flags = SMP_F_VOL_HDR;
883 return 1;
884}
885
886/* Fetch an HTTP header. A pointer to the beginning of the value is returned.
887 * Accepts an optional argument of type string containing the header field name,
888 * and an optional argument of type signed or unsigned integer to request an
889 * explicit occurrence of the header. Note that in the event of a missing name,
890 * headers are considered from the first one.
891 */
892static int smp_fetch_hdr(const struct arg *args, struct sample *smp, const char *kw, void *private)
893{
Christopher Faulet89dc4992019-04-17 12:02:59 +0200894 /* possible keywords: req.hdr / hdr, res.hdr / shdr */
895 struct channel *chn = ((kw[0] == 'h' || kw[2] == 'q') ? SMP_REQ_CHN(smp) : SMP_RES_CHN(smp));
Christopher Fauletf98e6262020-05-06 09:42:04 +0200896 struct check *check = ((kw[0] == 's' || kw[2] == 's') ? objt_check(smp->sess->origin) : NULL);
Christopher Faulet16032ab2020-04-30 11:30:00 +0200897 struct htx *htx = smp_prefetch_htx(smp, chn, check, 1);
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200898 struct http_hdr_ctx *ctx = smp->ctx.a[0];
899 struct ist name;
Willy Tarreau79e57332018-10-02 16:01:16 +0200900 int occ = 0;
Willy Tarreau79e57332018-10-02 16:01:16 +0200901
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200902 if (!ctx) {
903 /* first call */
904 ctx = &static_http_hdr_ctx;
905 ctx->blk = NULL;
906 smp->ctx.a[0] = ctx;
907 }
Willy Tarreau79e57332018-10-02 16:01:16 +0200908
Christopher Faulet623af932021-01-29 11:22:15 +0100909 if (args[0].type != ARGT_STR)
910 return 0;
Tim Duesterhus92c696e2021-02-28 16:11:36 +0100911 name = ist2(args[0].data.str.area, args[0].data.str.data);
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200912
Christopher Faulet623af932021-01-29 11:22:15 +0100913 if (args[1].type == ARGT_SINT)
914 occ = args[1].data.sint;
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200915
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200916 if (!htx)
917 return 0;
Willy Tarreau79e57332018-10-02 16:01:16 +0200918
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200919 if (ctx && !(smp->flags & SMP_F_NOT_LAST))
920 /* search for header from the beginning */
921 ctx->blk = NULL;
Willy Tarreau79e57332018-10-02 16:01:16 +0200922
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200923 if (!occ && !(smp->opt & SMP_OPT_ITERATE))
924 /* no explicit occurrence and single fetch => last header by default */
925 occ = -1;
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200926
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200927 if (!occ)
928 /* prepare to report multiple occurrences for ACL fetches */
929 smp->flags |= SMP_F_NOT_LAST;
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200930
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200931 smp->data.type = SMP_T_STR;
932 smp->flags |= SMP_F_VOL_HDR | SMP_F_CONST;
933 if (http_get_htx_hdr(htx, name, occ, ctx, &smp->data.u.str.area, &smp->data.u.str.data))
934 return 1;
Willy Tarreau79e57332018-10-02 16:01:16 +0200935
936 smp->flags &= ~SMP_F_NOT_LAST;
937 return 0;
938}
939
Christopher Fauletc1f40dd2019-05-16 10:07:30 +0200940/* Same than smp_fetch_hdr() but only relies on the sample direction to choose
941 * the right channel. So instead of duplicating the code, we just change the
942 * keyword and then fallback on smp_fetch_hdr().
943 */
944static int smp_fetch_chn_hdr(const struct arg *args, struct sample *smp, const char *kw, void *private)
945{
946 kw = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ ? "req.hdr" : "res.hdr");
947 return smp_fetch_hdr(args, smp, kw, private);
948}
949
Willy Tarreau79e57332018-10-02 16:01:16 +0200950/* 6. Check on HTTP header count. The number of occurrences is returned.
951 * Accepts exactly 1 argument of type string.
952 */
953static int smp_fetch_hdr_cnt(const struct arg *args, struct sample *smp, const char *kw, void *private)
954{
Christopher Faulet89dc4992019-04-17 12:02:59 +0200955 /* possible keywords: req.hdr_cnt / hdr_cnt, res.hdr_cnt / shdr_cnt */
956 struct channel *chn = ((kw[0] == 'h' || kw[2] == 'q') ? SMP_REQ_CHN(smp) : SMP_RES_CHN(smp));
Christopher Fauletf98e6262020-05-06 09:42:04 +0200957 struct check *check = ((kw[0] == 's' || kw[2] == 's') ? objt_check(smp->sess->origin) : NULL);
Christopher Faulet16032ab2020-04-30 11:30:00 +0200958 struct htx *htx = smp_prefetch_htx(smp, chn, check, 1);
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200959 struct http_hdr_ctx ctx;
960 struct ist name;
Willy Tarreau79e57332018-10-02 16:01:16 +0200961 int cnt;
Christopher Faulet311c7ea2018-10-24 21:41:55 +0200962
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200963 if (!htx)
964 return 0;
Willy Tarreau79e57332018-10-02 16:01:16 +0200965
Christopher Faulet623af932021-01-29 11:22:15 +0100966 if (args->type == ARGT_STR) {
Tim Duesterhus92c696e2021-02-28 16:11:36 +0100967 name = ist2(args->data.str.area, args->data.str.data);
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200968 } else {
Tim Duesterhus68a088d2021-02-28 16:11:37 +0100969 name = IST_NULL;
Willy Tarreau79e57332018-10-02 16:01:16 +0200970 }
Willy Tarreau79e57332018-10-02 16:01:16 +0200971
Christopher Faulet6d1dd462019-07-15 14:36:03 +0200972 ctx.blk = NULL;
973 cnt = 0;
974 while (http_find_header(htx, name, &ctx, 0))
975 cnt++;
Willy Tarreau79e57332018-10-02 16:01:16 +0200976
977 smp->data.type = SMP_T_SINT;
978 smp->data.u.sint = cnt;
979 smp->flags = SMP_F_VOL_HDR;
980 return 1;
981}
982
983/* Fetch an HTTP header's integer value. The integer value is returned. It
984 * takes a mandatory argument of type string and an optional one of type int
985 * to designate a specific occurrence. It returns an unsigned integer, which
986 * may or may not be appropriate for everything.
987 */
988static int smp_fetch_hdr_val(const struct arg *args, struct sample *smp, const char *kw, void *private)
989{
990 int ret = smp_fetch_hdr(args, smp, kw, private);
991
992 if (ret > 0) {
993 smp->data.type = SMP_T_SINT;
994 smp->data.u.sint = strl2ic(smp->data.u.str.area,
995 smp->data.u.str.data);
996 }
997
998 return ret;
999}
1000
1001/* Fetch an HTTP header's IP value. takes a mandatory argument of type string
1002 * and an optional one of type int to designate a specific occurrence.
Willy Tarreau7b0e00d2021-03-25 14:12:29 +01001003 * It returns an IPv4 or IPv6 address. Addresses surrounded by invalid chars
1004 * are rejected. However IPv4 addresses may be followed with a colon and a
1005 * valid port number.
Willy Tarreau79e57332018-10-02 16:01:16 +02001006 */
1007static int smp_fetch_hdr_ip(const struct arg *args, struct sample *smp, const char *kw, void *private)
1008{
Tim Duesterhus5cd00872020-06-26 15:44:48 +02001009 struct buffer *temp = get_trash_chunk();
Willy Tarreau7b0e00d2021-03-25 14:12:29 +01001010 int ret, len;
1011 int port;
Willy Tarreau79e57332018-10-02 16:01:16 +02001012
1013 while ((ret = smp_fetch_hdr(args, smp, kw, private)) > 0) {
Tim Duesterhus5cd00872020-06-26 15:44:48 +02001014 if (smp->data.u.str.data < temp->size - 1) {
1015 memcpy(temp->area, smp->data.u.str.area,
1016 smp->data.u.str.data);
1017 temp->area[smp->data.u.str.data] = '\0';
Willy Tarreau7b0e00d2021-03-25 14:12:29 +01001018 len = url2ipv4((char *) temp->area, &smp->data.u.ipv4);
Willy Tarreau645dc082021-03-31 11:41:36 +02001019 if (len > 0 && len == smp->data.u.str.data) {
Willy Tarreau7b0e00d2021-03-25 14:12:29 +01001020 /* plain IPv4 address */
1021 smp->data.type = SMP_T_IPV4;
1022 break;
1023 } else if (len > 0 && temp->area[len] == ':' &&
1024 strl2irc(temp->area + len + 1, smp->data.u.str.data - len - 1, &port) == 0 &&
1025 port >= 0 && port <= 65535) {
1026 /* IPv4 address suffixed with ':' followed by a valid port number */
Tim Duesterhus5cd00872020-06-26 15:44:48 +02001027 smp->data.type = SMP_T_IPV4;
1028 break;
1029 } else if (inet_pton(AF_INET6, temp->area, &smp->data.u.ipv6)) {
1030 smp->data.type = SMP_T_IPV6;
1031 break;
Willy Tarreau79e57332018-10-02 16:01:16 +02001032 }
1033 }
1034
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001035 /* if the header doesn't match an IP address, fetch next one */
1036 if (!(smp->flags & SMP_F_NOT_LAST))
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001037 return 0;
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001038 }
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001039 return ret;
1040}
Willy Tarreau79e57332018-10-02 16:01:16 +02001041
Christopher Faulete720c322020-09-02 17:25:18 +02001042/* 8. Check on URI PATH. A pointer to the PATH is stored. The path starts at the
1043 * first '/' after the possible hostname. It ends before the possible '?' except
1044 * for 'pathq' keyword.
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001045 */
1046static int smp_fetch_path(const struct arg *args, struct sample *smp, const char *kw, void *private)
1047{
1048 struct channel *chn = SMP_REQ_CHN(smp);
Christopher Faulet778f5ed2020-04-29 15:51:55 +02001049 struct htx *htx = smp_prefetch_htx(smp, chn, NULL, 1);
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001050 struct htx_sl *sl;
1051 struct ist path;
Amaury Denoyellec453f952021-07-06 11:40:12 +02001052 struct http_uri_parser parser;
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001053
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001054 if (!htx)
1055 return 0;
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001056
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001057 sl = http_get_stline(htx);
Amaury Denoyellec453f952021-07-06 11:40:12 +02001058 parser = http_uri_parser_init(htx_sl_req_uri(sl));
Christopher Faulete720c322020-09-02 17:25:18 +02001059
Yves Lafonb4d37082021-02-11 11:01:28 +01001060 if (kw[4] == 'q' && (kw[0] == 'p' || kw[0] == 'b')) // pathq or baseq
Amaury Denoyellec453f952021-07-06 11:40:12 +02001061 path = http_parse_path(&parser);
Christopher Faulete720c322020-09-02 17:25:18 +02001062 else
Amaury Denoyellec453f952021-07-06 11:40:12 +02001063 path = iststop(http_parse_path(&parser), '?');
Christopher Faulete720c322020-09-02 17:25:18 +02001064
Tim Duesterhused526372020-03-05 17:56:33 +01001065 if (!isttest(path))
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001066 return 0;
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001067
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001068 /* OK, we got the '/' ! */
1069 smp->data.type = SMP_T_STR;
1070 smp->data.u.str.area = path.ptr;
Jerome Magnin4fb196c2020-02-21 10:49:12 +01001071 smp->data.u.str.data = path.len;
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001072 smp->flags = SMP_F_VOL_1ST | SMP_F_CONST;
Willy Tarreau79e57332018-10-02 16:01:16 +02001073 return 1;
1074}
1075
1076/* This produces a concatenation of the first occurrence of the Host header
1077 * followed by the path component if it begins with a slash ('/'). This means
1078 * that '*' will not be added, resulting in exactly the first Host entry.
1079 * If no Host header is found, then the path is returned as-is. The returned
1080 * value is stored in the trash so it does not need to be marked constant.
1081 * The returned sample is of type string.
1082 */
1083static int smp_fetch_base(const struct arg *args, struct sample *smp, const char *kw, void *private)
1084{
Christopher Faulet89dc4992019-04-17 12:02:59 +02001085 struct channel *chn = SMP_REQ_CHN(smp);
Christopher Faulet778f5ed2020-04-29 15:51:55 +02001086 struct htx *htx = smp_prefetch_htx(smp, chn, NULL, 1);
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001087 struct htx_sl *sl;
Willy Tarreau79e57332018-10-02 16:01:16 +02001088 struct buffer *temp;
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001089 struct http_hdr_ctx ctx;
1090 struct ist path;
Amaury Denoyellec453f952021-07-06 11:40:12 +02001091 struct http_uri_parser parser;
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001092
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001093 if (!htx)
1094 return 0;
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001095
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001096 ctx.blk = NULL;
1097 if (!http_find_header(htx, ist("Host"), &ctx, 0) || !ctx.value.len)
1098 return smp_fetch_path(args, smp, kw, private);
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001099
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001100 /* OK we have the header value in ctx.value */
1101 temp = get_trash_chunk();
Tim Duesterhus77508502022-03-15 13:11:06 +01001102 chunk_istcat(temp, ctx.value);
Willy Tarreau79e57332018-10-02 16:01:16 +02001103
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001104 /* now retrieve the path */
1105 sl = http_get_stline(htx);
Amaury Denoyellec453f952021-07-06 11:40:12 +02001106 parser = http_uri_parser_init(htx_sl_req_uri(sl));
1107 path = http_parse_path(&parser);
Tim Duesterhused526372020-03-05 17:56:33 +01001108 if (isttest(path)) {
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001109 size_t len;
Willy Tarreau79e57332018-10-02 16:01:16 +02001110
Yves Lafonb4d37082021-02-11 11:01:28 +01001111 if (kw[4] == 'q' && kw[0] == 'b') { // baseq
1112 len = path.len;
1113 } else {
1114 for (len = 0; len < path.len && *(path.ptr + len) != '?'; len++)
1115 ;
1116 }
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001117
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001118 if (len && *(path.ptr) == '/')
1119 chunk_memcat(temp, path.ptr, len);
Willy Tarreau79e57332018-10-02 16:01:16 +02001120 }
1121
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001122 smp->data.type = SMP_T_STR;
1123 smp->data.u.str = *temp;
Willy Tarreau79e57332018-10-02 16:01:16 +02001124 smp->flags = SMP_F_VOL_1ST;
1125 return 1;
1126}
1127
1128/* This produces a 32-bit hash of the concatenation of the first occurrence of
1129 * the Host header followed by the path component if it begins with a slash ('/').
1130 * This means that '*' will not be added, resulting in exactly the first Host
1131 * entry. If no Host header is found, then the path is used. The resulting value
1132 * is hashed using the path hash followed by a full avalanche hash and provides a
1133 * 32-bit integer value. This fetch is useful for tracking per-path activity on
1134 * high-traffic sites without having to store whole paths.
1135 */
1136static int smp_fetch_base32(const struct arg *args, struct sample *smp, const char *kw, void *private)
1137{
Christopher Faulet89dc4992019-04-17 12:02:59 +02001138 struct channel *chn = SMP_REQ_CHN(smp);
Christopher Faulet778f5ed2020-04-29 15:51:55 +02001139 struct htx *htx = smp_prefetch_htx(smp, chn, NULL, 1);
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001140 struct htx_sl *sl;
1141 struct http_hdr_ctx ctx;
1142 struct ist path;
Willy Tarreau79e57332018-10-02 16:01:16 +02001143 unsigned int hash = 0;
Amaury Denoyellec453f952021-07-06 11:40:12 +02001144 struct http_uri_parser parser;
Willy Tarreau79e57332018-10-02 16:01:16 +02001145
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001146 if (!htx)
1147 return 0;
Dragan Dosen8861e1c2019-02-12 19:50:31 +01001148
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001149 ctx.blk = NULL;
1150 if (http_find_header(htx, ist("Host"), &ctx, 0)) {
1151 /* OK we have the header value in ctx.value */
1152 while (ctx.value.len--)
1153 hash = *(ctx.value.ptr++) + (hash << 6) + (hash << 16) - hash;
Willy Tarreau79e57332018-10-02 16:01:16 +02001154 }
1155
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001156 /* now retrieve the path */
1157 sl = http_get_stline(htx);
Amaury Denoyellec453f952021-07-06 11:40:12 +02001158 parser = http_uri_parser_init(htx_sl_req_uri(sl));
1159 path = http_parse_path(&parser);
Tim Duesterhused526372020-03-05 17:56:33 +01001160 if (isttest(path)) {
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001161 size_t len;
Willy Tarreau79e57332018-10-02 16:01:16 +02001162
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001163 for (len = 0; len < path.len && *(path.ptr + len) != '?'; len++)
1164 ;
Willy Tarreau79e57332018-10-02 16:01:16 +02001165
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001166 if (len && *(path.ptr) == '/') {
1167 while (len--)
1168 hash = *(path.ptr++) + (hash << 6) + (hash << 16) - hash;
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001169 }
Willy Tarreau79e57332018-10-02 16:01:16 +02001170 }
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001171
Willy Tarreau79e57332018-10-02 16:01:16 +02001172 hash = full_hash(hash);
1173
1174 smp->data.type = SMP_T_SINT;
1175 smp->data.u.sint = hash;
1176 smp->flags = SMP_F_VOL_1ST;
1177 return 1;
1178}
1179
1180/* This concatenates the source address with the 32-bit hash of the Host and
1181 * path as returned by smp_fetch_base32(). The idea is to have per-source and
1182 * per-path counters. The result is a binary block from 8 to 20 bytes depending
1183 * on the source address length. The path hash is stored before the address so
1184 * that in environments where IPv6 is insignificant, truncating the output to
1185 * 8 bytes would still work.
1186 */
1187static int smp_fetch_base32_src(const struct arg *args, struct sample *smp, const char *kw, void *private)
1188{
Christopher Faulet8da67aa2022-03-29 17:53:09 +02001189 const struct sockaddr_storage *src = (smp->strm ? cs_src(smp->strm->csf) : NULL);
Willy Tarreau79e57332018-10-02 16:01:16 +02001190 struct buffer *temp;
Willy Tarreau79e57332018-10-02 16:01:16 +02001191
Christopher Faulet6fc817a2021-10-25 07:48:27 +02001192 if (!src)
Willy Tarreau79e57332018-10-02 16:01:16 +02001193 return 0;
1194
1195 if (!smp_fetch_base32(args, smp, kw, private))
1196 return 0;
1197
1198 temp = get_trash_chunk();
1199 *(unsigned int *) temp->area = htonl(smp->data.u.sint);
1200 temp->data += sizeof(unsigned int);
1201
Christopher Faulet6fc817a2021-10-25 07:48:27 +02001202 switch (src->ss_family) {
Willy Tarreau79e57332018-10-02 16:01:16 +02001203 case AF_INET:
1204 memcpy(temp->area + temp->data,
Christopher Faulet6fc817a2021-10-25 07:48:27 +02001205 &((struct sockaddr_in *)src)->sin_addr,
Willy Tarreau79e57332018-10-02 16:01:16 +02001206 4);
1207 temp->data += 4;
1208 break;
1209 case AF_INET6:
1210 memcpy(temp->area + temp->data,
Christopher Faulet6fc817a2021-10-25 07:48:27 +02001211 &((struct sockaddr_in6 *)src)->sin6_addr,
Willy Tarreau79e57332018-10-02 16:01:16 +02001212 16);
1213 temp->data += 16;
1214 break;
1215 default:
1216 return 0;
1217 }
1218
1219 smp->data.u.str = *temp;
1220 smp->data.type = SMP_T_BIN;
1221 return 1;
1222}
1223
1224/* Extracts the query string, which comes after the question mark '?'. If no
1225 * question mark is found, nothing is returned. Otherwise it returns a sample
1226 * of type string carrying the whole query string.
1227 */
1228static int smp_fetch_query(const struct arg *args, struct sample *smp, const char *kw, void *private)
1229{
Christopher Faulet89dc4992019-04-17 12:02:59 +02001230 struct channel *chn = SMP_REQ_CHN(smp);
Christopher Faulet778f5ed2020-04-29 15:51:55 +02001231 struct htx *htx = smp_prefetch_htx(smp, chn, NULL, 1);
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001232 struct htx_sl *sl;
Willy Tarreau79e57332018-10-02 16:01:16 +02001233 char *ptr, *end;
1234
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001235 if (!htx)
1236 return 0;
Willy Tarreau79e57332018-10-02 16:01:16 +02001237
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001238 sl = http_get_stline(htx);
1239 ptr = HTX_SL_REQ_UPTR(sl);
1240 end = HTX_SL_REQ_UPTR(sl) + HTX_SL_REQ_ULEN(sl);
Willy Tarreau79e57332018-10-02 16:01:16 +02001241
1242 /* look up the '?' */
1243 do {
1244 if (ptr == end)
1245 return 0;
1246 } while (*ptr++ != '?');
1247
1248 smp->data.type = SMP_T_STR;
1249 smp->data.u.str.area = ptr;
1250 smp->data.u.str.data = end - ptr;
1251 smp->flags = SMP_F_VOL_1ST | SMP_F_CONST;
1252 return 1;
1253}
1254
1255static int smp_fetch_proto_http(const struct arg *args, struct sample *smp, const char *kw, void *private)
1256{
Christopher Faulet89dc4992019-04-17 12:02:59 +02001257 struct channel *chn = SMP_REQ_CHN(smp);
Christopher Faulet778f5ed2020-04-29 15:51:55 +02001258 struct htx *htx = smp_prefetch_htx(smp, chn, NULL, 0);
Willy Tarreau79e57332018-10-02 16:01:16 +02001259
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001260 if (!htx)
1261 return 0;
1262 smp->data.type = SMP_T_BOOL;
Willy Tarreau79e57332018-10-02 16:01:16 +02001263 smp->data.u.sint = 1;
1264 return 1;
1265}
1266
1267/* return a valid test if the current request is the first one on the connection */
1268static int smp_fetch_http_first_req(const struct arg *args, struct sample *smp, const char *kw, void *private)
1269{
Willy Tarreau79512b62020-04-29 11:52:13 +02001270 if (!smp->strm)
1271 return 0;
1272
Willy Tarreau79e57332018-10-02 16:01:16 +02001273 smp->data.type = SMP_T_BOOL;
1274 smp->data.u.sint = !(smp->strm->txn->flags & TX_NOT_FIRST);
1275 return 1;
1276}
1277
Christopher Fauleta4063562019-08-02 11:51:37 +02001278/* Fetch the authentication method if there is an Authorization header. It
1279 * relies on get_http_auth()
1280 */
1281static int smp_fetch_http_auth_type(const struct arg *args, struct sample *smp, const char *kw, void *private)
1282{
1283 struct channel *chn = SMP_REQ_CHN(smp);
Christopher Faulet778f5ed2020-04-29 15:51:55 +02001284 struct htx *htx = smp_prefetch_htx(smp, chn, NULL, 1);
Christopher Fauleta4063562019-08-02 11:51:37 +02001285 struct http_txn *txn;
1286
1287 if (!htx)
1288 return 0;
1289
1290 txn = smp->strm->txn;
1291 if (!get_http_auth(smp, htx))
1292 return 0;
1293
1294 switch (txn->auth.method) {
1295 case HTTP_AUTH_BASIC:
1296 smp->data.u.str.area = "Basic";
1297 smp->data.u.str.data = 5;
1298 break;
1299 case HTTP_AUTH_DIGEST:
1300 /* Unexpected because not supported */
1301 smp->data.u.str.area = "Digest";
1302 smp->data.u.str.data = 6;
1303 break;
Remi Tricot-Le Bretonf5dd3372021-10-01 15:36:53 +02001304 case HTTP_AUTH_BEARER:
1305 smp->data.u.str.area = "Bearer";
1306 smp->data.u.str.data = 6;
1307 break;
Christopher Fauleta4063562019-08-02 11:51:37 +02001308 default:
1309 return 0;
1310 }
1311
1312 smp->data.type = SMP_T_STR;
1313 smp->flags = SMP_F_CONST;
1314 return 1;
1315}
1316
1317/* Fetch the user supplied if there is an Authorization header. It relies on
1318 * get_http_auth()
1319 */
1320static int smp_fetch_http_auth_user(const struct arg *args, struct sample *smp, const char *kw, void *private)
1321{
1322 struct channel *chn = SMP_REQ_CHN(smp);
Christopher Faulet778f5ed2020-04-29 15:51:55 +02001323 struct htx *htx = smp_prefetch_htx(smp, chn, NULL, 1);
Christopher Fauleta4063562019-08-02 11:51:37 +02001324 struct http_txn *txn;
1325
1326 if (!htx)
1327 return 0;
1328
1329 txn = smp->strm->txn;
Remi Tricot-Le Bretonf5dd3372021-10-01 15:36:53 +02001330 if (!get_http_auth(smp, htx) || txn->auth.method != HTTP_AUTH_BASIC)
Christopher Fauleta4063562019-08-02 11:51:37 +02001331 return 0;
1332
1333 smp->data.type = SMP_T_STR;
1334 smp->data.u.str.area = txn->auth.user;
1335 smp->data.u.str.data = strlen(txn->auth.user);
1336 smp->flags = SMP_F_CONST;
1337 return 1;
1338}
1339
1340/* Fetch the password supplied if there is an Authorization header. It relies on
1341 * get_http_auth()
1342 */
1343static int smp_fetch_http_auth_pass(const struct arg *args, struct sample *smp, const char *kw, void *private)
1344{
1345 struct channel *chn = SMP_REQ_CHN(smp);
Christopher Faulet778f5ed2020-04-29 15:51:55 +02001346 struct htx *htx = smp_prefetch_htx(smp, chn, NULL, 1);
Christopher Fauleta4063562019-08-02 11:51:37 +02001347 struct http_txn *txn;
1348
1349 if (!htx)
1350 return 0;
1351
1352 txn = smp->strm->txn;
Remi Tricot-Le Bretonf5dd3372021-10-01 15:36:53 +02001353 if (!get_http_auth(smp, htx) || txn->auth.method != HTTP_AUTH_BASIC)
Christopher Fauleta4063562019-08-02 11:51:37 +02001354 return 0;
1355
1356 smp->data.type = SMP_T_STR;
1357 smp->data.u.str.area = txn->auth.pass;
1358 smp->data.u.str.data = strlen(txn->auth.pass);
1359 smp->flags = SMP_F_CONST;
1360 return 1;
1361}
1362
Remi Tricot-Le Bretonf5dd3372021-10-01 15:36:53 +02001363static int smp_fetch_http_auth_bearer(const struct arg *args, struct sample *smp, const char *kw, void *private)
1364{
1365 struct channel *chn = SMP_REQ_CHN(smp);
1366 struct htx *htx = smp_prefetch_htx(smp, chn, NULL, 1);
1367 struct http_txn *txn;
1368 struct buffer bearer_val = {};
1369
1370 if (!htx)
1371 return 0;
1372
1373 if (args->type == ARGT_STR) {
1374 struct http_hdr_ctx ctx;
1375 struct ist hdr_name = ist2(args->data.str.area, args->data.str.data);
1376
1377 ctx.blk = NULL;
1378 if (http_find_header(htx, hdr_name, &ctx, 0)) {
Remi Tricot-Le Breton7da35bf2021-10-29 15:25:19 +02001379 struct ist type = istsplit(&ctx.value, ' ');
1380
1381 /* There must be "at least" one space character between
1382 * the scheme and the following value so ctx.value might
1383 * still have leading spaces here (see RFC7235).
1384 */
1385 ctx.value = istskip(ctx.value, ' ');
1386
1387 if (isteqi(type, ist("Bearer")) && istlen(ctx.value))
1388 chunk_initlen(&bearer_val, istptr(ctx.value), 0, istlen(ctx.value));
Remi Tricot-Le Bretonf5dd3372021-10-01 15:36:53 +02001389 }
1390 }
1391 else {
1392 txn = smp->strm->txn;
1393 if (!get_http_auth(smp, htx) || txn->auth.method != HTTP_AUTH_BEARER)
1394 return 0;
1395
1396 bearer_val = txn->auth.method_data;
1397 }
1398
1399 smp->data.type = SMP_T_STR;
1400 smp->data.u.str = bearer_val;
1401 smp->flags = SMP_F_CONST;
1402 return 1;
1403}
1404
Willy Tarreau79e57332018-10-02 16:01:16 +02001405/* Accepts exactly 1 argument of type userlist */
1406static int smp_fetch_http_auth(const struct arg *args, struct sample *smp, const char *kw, void *private)
1407{
Christopher Faulet89dc4992019-04-17 12:02:59 +02001408 struct channel *chn = SMP_REQ_CHN(smp);
Christopher Faulet778f5ed2020-04-29 15:51:55 +02001409 struct htx *htx = smp_prefetch_htx(smp, chn, NULL, 1);
Willy Tarreau79e57332018-10-02 16:01:16 +02001410
Christopher Faulet623af932021-01-29 11:22:15 +01001411 if (args->type != ARGT_USR)
Willy Tarreau79e57332018-10-02 16:01:16 +02001412 return 0;
1413
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001414 if (!htx)
1415 return 0;
Remi Tricot-Le Bretonf5dd3372021-10-01 15:36:53 +02001416 if (!get_http_auth(smp, htx) || smp->strm->txn->auth.method != HTTP_AUTH_BASIC)
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001417 return 0;
Willy Tarreau79e57332018-10-02 16:01:16 +02001418
1419 smp->data.type = SMP_T_BOOL;
1420 smp->data.u.sint = check_user(args->data.usr, smp->strm->txn->auth.user,
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001421 smp->strm->txn->auth.pass);
Willy Tarreau79e57332018-10-02 16:01:16 +02001422 return 1;
1423}
1424
1425/* Accepts exactly 1 argument of type userlist */
1426static int smp_fetch_http_auth_grp(const struct arg *args, struct sample *smp, const char *kw, void *private)
1427{
Christopher Faulet89dc4992019-04-17 12:02:59 +02001428 struct channel *chn = SMP_REQ_CHN(smp);
Christopher Faulet778f5ed2020-04-29 15:51:55 +02001429 struct htx *htx = smp_prefetch_htx(smp, chn, NULL, 1);
Christopher Faulet89dc4992019-04-17 12:02:59 +02001430
Christopher Faulet623af932021-01-29 11:22:15 +01001431 if (args->type != ARGT_USR)
Willy Tarreau79e57332018-10-02 16:01:16 +02001432 return 0;
1433
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001434 if (!htx)
1435 return 0;
Remi Tricot-Le Bretonf5dd3372021-10-01 15:36:53 +02001436 if (!get_http_auth(smp, htx) || smp->strm->txn->auth.method != HTTP_AUTH_BASIC)
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001437 return 0;
Willy Tarreau79e57332018-10-02 16:01:16 +02001438
Willy Tarreau79e57332018-10-02 16:01:16 +02001439 /* if the user does not belong to the userlist or has a wrong password,
1440 * report that it unconditionally does not match. Otherwise we return
1441 * a string containing the username.
1442 */
1443 if (!check_user(args->data.usr, smp->strm->txn->auth.user,
1444 smp->strm->txn->auth.pass))
1445 return 0;
1446
1447 /* pat_match_auth() will need the user list */
1448 smp->ctx.a[0] = args->data.usr;
1449
1450 smp->data.type = SMP_T_STR;
1451 smp->flags = SMP_F_CONST;
1452 smp->data.u.str.area = smp->strm->txn->auth.user;
1453 smp->data.u.str.data = strlen(smp->strm->txn->auth.user);
1454
1455 return 1;
1456}
1457
1458/* Fetch a captured HTTP request header. The index is the position of
1459 * the "capture" option in the configuration file
1460 */
1461static int smp_fetch_capture_req_hdr(const struct arg *args, struct sample *smp, const char *kw, void *private)
1462{
Willy Tarreau0898c2d2020-04-29 11:44:54 +02001463 struct proxy *fe;
Willy Tarreau79e57332018-10-02 16:01:16 +02001464 int idx;
1465
Christopher Faulet623af932021-01-29 11:22:15 +01001466 if (args->type != ARGT_SINT)
Willy Tarreau79e57332018-10-02 16:01:16 +02001467 return 0;
1468
Willy Tarreau0898c2d2020-04-29 11:44:54 +02001469 if (!smp->strm)
1470 return 0;
1471
1472 fe = strm_fe(smp->strm);
Willy Tarreau79e57332018-10-02 16:01:16 +02001473 idx = args->data.sint;
1474
1475 if (idx > (fe->nb_req_cap - 1) || smp->strm->req_cap == NULL || smp->strm->req_cap[idx] == NULL)
1476 return 0;
1477
1478 smp->data.type = SMP_T_STR;
1479 smp->flags |= SMP_F_CONST;
1480 smp->data.u.str.area = smp->strm->req_cap[idx];
1481 smp->data.u.str.data = strlen(smp->strm->req_cap[idx]);
1482
1483 return 1;
1484}
1485
1486/* Fetch a captured HTTP response header. The index is the position of
1487 * the "capture" option in the configuration file
1488 */
1489static int smp_fetch_capture_res_hdr(const struct arg *args, struct sample *smp, const char *kw, void *private)
1490{
Willy Tarreau0898c2d2020-04-29 11:44:54 +02001491 struct proxy *fe;
Willy Tarreau79e57332018-10-02 16:01:16 +02001492 int idx;
1493
Christopher Faulet623af932021-01-29 11:22:15 +01001494 if (args->type != ARGT_SINT)
Willy Tarreau79e57332018-10-02 16:01:16 +02001495 return 0;
1496
Willy Tarreau0898c2d2020-04-29 11:44:54 +02001497 if (!smp->strm)
1498 return 0;
1499
1500 fe = strm_fe(smp->strm);
Willy Tarreau79e57332018-10-02 16:01:16 +02001501 idx = args->data.sint;
1502
1503 if (idx > (fe->nb_rsp_cap - 1) || smp->strm->res_cap == NULL || smp->strm->res_cap[idx] == NULL)
1504 return 0;
1505
1506 smp->data.type = SMP_T_STR;
1507 smp->flags |= SMP_F_CONST;
1508 smp->data.u.str.area = smp->strm->res_cap[idx];
1509 smp->data.u.str.data = strlen(smp->strm->res_cap[idx]);
1510
1511 return 1;
1512}
1513
1514/* Extracts the METHOD in the HTTP request, the txn->uri should be filled before the call */
1515static int smp_fetch_capture_req_method(const struct arg *args, struct sample *smp, const char *kw, void *private)
1516{
1517 struct buffer *temp;
Willy Tarreau0898c2d2020-04-29 11:44:54 +02001518 struct http_txn *txn;
Willy Tarreau79e57332018-10-02 16:01:16 +02001519 char *ptr;
1520
Willy Tarreau0898c2d2020-04-29 11:44:54 +02001521 if (!smp->strm)
1522 return 0;
1523
1524 txn = smp->strm->txn;
Willy Tarreau79e57332018-10-02 16:01:16 +02001525 if (!txn || !txn->uri)
1526 return 0;
1527
1528 ptr = txn->uri;
1529
1530 while (*ptr != ' ' && *ptr != '\0') /* find first space */
1531 ptr++;
1532
1533 temp = get_trash_chunk();
1534 temp->area = txn->uri;
1535 temp->data = ptr - txn->uri;
1536 smp->data.u.str = *temp;
1537 smp->data.type = SMP_T_STR;
1538 smp->flags = SMP_F_CONST;
1539
1540 return 1;
1541
1542}
1543
1544/* Extracts the path in the HTTP request, the txn->uri should be filled before the call */
1545static int smp_fetch_capture_req_uri(const struct arg *args, struct sample *smp, const char *kw, void *private)
1546{
Willy Tarreau0898c2d2020-04-29 11:44:54 +02001547 struct http_txn *txn;
Willy Tarreau79e57332018-10-02 16:01:16 +02001548 struct ist path;
1549 const char *ptr;
Amaury Denoyellec453f952021-07-06 11:40:12 +02001550 struct http_uri_parser parser;
Willy Tarreau79e57332018-10-02 16:01:16 +02001551
Willy Tarreau0898c2d2020-04-29 11:44:54 +02001552 if (!smp->strm)
1553 return 0;
1554
1555 txn = smp->strm->txn;
Willy Tarreau79e57332018-10-02 16:01:16 +02001556 if (!txn || !txn->uri)
1557 return 0;
1558
1559 ptr = txn->uri;
1560
1561 while (*ptr != ' ' && *ptr != '\0') /* find first space */
1562 ptr++;
1563
1564 if (!*ptr)
1565 return 0;
1566
Christopher Faulet78337bb2018-11-15 14:35:18 +01001567 /* skip the first space and find space after URI */
1568 path = ist2(++ptr, 0);
1569 while (*ptr != ' ' && *ptr != '\0')
1570 ptr++;
1571 path.len = ptr - path.ptr;
Willy Tarreau79e57332018-10-02 16:01:16 +02001572
Amaury Denoyellec453f952021-07-06 11:40:12 +02001573 parser = http_uri_parser_init(path);
1574 path = http_parse_path(&parser);
Tim Duesterhused526372020-03-05 17:56:33 +01001575 if (!isttest(path))
Willy Tarreau79e57332018-10-02 16:01:16 +02001576 return 0;
1577
1578 smp->data.u.str.area = path.ptr;
1579 smp->data.u.str.data = path.len;
1580 smp->data.type = SMP_T_STR;
1581 smp->flags = SMP_F_CONST;
1582
1583 return 1;
1584}
1585
1586/* Retrieves the HTTP version from the request (either 1.0 or 1.1) and emits it
1587 * as a string (either "HTTP/1.0" or "HTTP/1.1").
1588 */
1589static int smp_fetch_capture_req_ver(const struct arg *args, struct sample *smp, const char *kw, void *private)
1590{
Willy Tarreau0898c2d2020-04-29 11:44:54 +02001591 struct http_txn *txn;
1592
1593 if (!smp->strm)
1594 return 0;
Willy Tarreau79e57332018-10-02 16:01:16 +02001595
Willy Tarreau0898c2d2020-04-29 11:44:54 +02001596 txn = smp->strm->txn;
Christopher Faulet09f88362021-04-01 16:00:29 +02001597 if (!txn || txn->req.msg_state < HTTP_MSG_BODY)
Willy Tarreau79e57332018-10-02 16:01:16 +02001598 return 0;
1599
1600 if (txn->req.flags & HTTP_MSGF_VER_11)
1601 smp->data.u.str.area = "HTTP/1.1";
1602 else
1603 smp->data.u.str.area = "HTTP/1.0";
1604
1605 smp->data.u.str.data = 8;
1606 smp->data.type = SMP_T_STR;
1607 smp->flags = SMP_F_CONST;
1608 return 1;
1609
1610}
1611
1612/* Retrieves the HTTP version from the response (either 1.0 or 1.1) and emits it
1613 * as a string (either "HTTP/1.0" or "HTTP/1.1").
1614 */
1615static int smp_fetch_capture_res_ver(const struct arg *args, struct sample *smp, const char *kw, void *private)
1616{
Willy Tarreau0898c2d2020-04-29 11:44:54 +02001617 struct http_txn *txn;
1618
1619 if (!smp->strm)
1620 return 0;
Willy Tarreau79e57332018-10-02 16:01:16 +02001621
Willy Tarreau0898c2d2020-04-29 11:44:54 +02001622 txn = smp->strm->txn;
Christopher Faulet09f88362021-04-01 16:00:29 +02001623 if (!txn || txn->rsp.msg_state < HTTP_MSG_BODY)
Willy Tarreau79e57332018-10-02 16:01:16 +02001624 return 0;
1625
1626 if (txn->rsp.flags & HTTP_MSGF_VER_11)
1627 smp->data.u.str.area = "HTTP/1.1";
1628 else
1629 smp->data.u.str.area = "HTTP/1.0";
1630
1631 smp->data.u.str.data = 8;
1632 smp->data.type = SMP_T_STR;
1633 smp->flags = SMP_F_CONST;
1634 return 1;
1635
1636}
1637
1638/* Iterate over all cookies present in a message. The context is stored in
1639 * smp->ctx.a[0] for the in-header position, smp->ctx.a[1] for the
1640 * end-of-header-value, and smp->ctx.a[2] for the hdr_ctx. Depending on
1641 * the direction, multiple cookies may be parsed on the same line or not.
Maciej Zdebdea7c202020-11-13 09:38:06 +00001642 * If provided, the searched cookie name is in args, in args->data.str. If
1643 * the input options indicate that no iterating is desired, then only last
1644 * value is fetched if any. If no cookie name is provided, the first cookie
1645 * value found is fetched. The returned sample is of type CSTR. Can be used
1646 * to parse cookies in other files.
Willy Tarreau79e57332018-10-02 16:01:16 +02001647 */
1648static int smp_fetch_cookie(const struct arg *args, struct sample *smp, const char *kw, void *private)
1649{
Christopher Faulet89dc4992019-04-17 12:02:59 +02001650 /* possible keywords: req.cookie / cookie / cook, res.cookie / scook / set-cookie */
1651 struct channel *chn = ((kw[0] == 'c' || kw[2] == 'q') ? SMP_REQ_CHN(smp) : SMP_RES_CHN(smp));
Christopher Fauletf98e6262020-05-06 09:42:04 +02001652 struct check *check = ((kw[0] == 's' || kw[2] == 's') ? objt_check(smp->sess->origin) : NULL);
Christopher Faulet16032ab2020-04-30 11:30:00 +02001653 struct htx *htx = smp_prefetch_htx(smp, chn, check, 1);
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001654 struct http_hdr_ctx *ctx = smp->ctx.a[2];
1655 struct ist hdr;
Christopher Faulet97fc8da2020-11-13 13:41:04 +01001656 char *cook = NULL;
1657 size_t cook_l = 0;
Willy Tarreau79e57332018-10-02 16:01:16 +02001658 int found = 0;
1659
Christopher Faulet623af932021-01-29 11:22:15 +01001660 if (args->type == ARGT_STR) {
Christopher Faulet97fc8da2020-11-13 13:41:04 +01001661 cook = args->data.str.area;
1662 cook_l = args->data.str.data;
1663 }
Willy Tarreau79e57332018-10-02 16:01:16 +02001664
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001665 if (!ctx) {
1666 /* first call */
1667 ctx = &static_http_hdr_ctx;
1668 ctx->blk = NULL;
1669 smp->ctx.a[2] = ctx;
1670 }
Willy Tarreau79e57332018-10-02 16:01:16 +02001671
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001672 if (!htx)
1673 return 0;
Willy Tarreau79e57332018-10-02 16:01:16 +02001674
Christopher Faulet16032ab2020-04-30 11:30:00 +02001675 hdr = (!(check || (chn && chn->flags & CF_ISRESP)) ? ist("Cookie") : ist("Set-Cookie"));
Willy Tarreau79e57332018-10-02 16:01:16 +02001676
Maciej Zdebdea7c202020-11-13 09:38:06 +00001677 /* OK so basically here, either we want only one value or we want to
1678 * iterate over all of them and we fetch the next one. In this last case
1679 * SMP_OPT_ITERATE option is set.
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001680 */
Willy Tarreau79e57332018-10-02 16:01:16 +02001681
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001682 if (!(smp->flags & SMP_F_NOT_LAST)) {
1683 /* search for the header from the beginning, we must first initialize
1684 * the search parameters.
Willy Tarreau79e57332018-10-02 16:01:16 +02001685 */
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001686 smp->ctx.a[0] = NULL;
1687 ctx->blk = NULL;
Willy Tarreau79e57332018-10-02 16:01:16 +02001688 }
Willy Tarreau79e57332018-10-02 16:01:16 +02001689
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001690 smp->flags |= SMP_F_VOL_HDR;
1691 while (1) {
1692 /* Note: smp->ctx.a[0] == NULL every time we need to fetch a new header */
1693 if (!smp->ctx.a[0]) {
1694 if (!http_find_header(htx, hdr, ctx, 0))
1695 goto out;
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001696
Christopher Faulet97fc8da2020-11-13 13:41:04 +01001697 if (ctx->value.len < cook_l + 1)
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001698 continue;
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001699
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001700 smp->ctx.a[0] = ctx->value.ptr;
1701 smp->ctx.a[1] = smp->ctx.a[0] + ctx->value.len;
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001702 }
1703
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001704 smp->data.type = SMP_T_STR;
1705 smp->flags |= SMP_F_CONST;
1706 smp->ctx.a[0] = http_extract_cookie_value(smp->ctx.a[0], smp->ctx.a[1],
Christopher Faulet97fc8da2020-11-13 13:41:04 +01001707 cook, cook_l,
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001708 (smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ,
1709 &smp->data.u.str.area,
1710 &smp->data.u.str.data);
1711 if (smp->ctx.a[0]) {
1712 found = 1;
Maciej Zdebdea7c202020-11-13 09:38:06 +00001713 if (smp->opt & SMP_OPT_ITERATE) {
1714 /* iterate on cookie value */
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001715 smp->flags |= SMP_F_NOT_LAST;
1716 return 1;
Willy Tarreau79e57332018-10-02 16:01:16 +02001717 }
Maciej Zdebdea7c202020-11-13 09:38:06 +00001718 if (args->data.str.data == 0) {
1719 /* No cookie name, first occurrence returned */
1720 break;
1721 }
Willy Tarreau79e57332018-10-02 16:01:16 +02001722 }
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001723 /* if we're looking for last occurrence, let's loop */
Willy Tarreau79e57332018-10-02 16:01:16 +02001724 }
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001725
Willy Tarreau79e57332018-10-02 16:01:16 +02001726 /* all cookie headers and values were scanned. If we're looking for the
1727 * last occurrence, we may return it now.
1728 */
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001729 out:
Willy Tarreau79e57332018-10-02 16:01:16 +02001730 smp->flags &= ~SMP_F_NOT_LAST;
1731 return found;
1732}
1733
Christopher Fauletc1f40dd2019-05-16 10:07:30 +02001734/* Same than smp_fetch_cookie() but only relies on the sample direction to
1735 * choose the right channel. So instead of duplicating the code, we just change
1736 * the keyword and then fallback on smp_fetch_cookie().
1737 */
1738static int smp_fetch_chn_cookie(const struct arg *args, struct sample *smp, const char *kw, void *private)
1739{
1740 kw = ((smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ ? "req.cook" : "res.cook");
1741 return smp_fetch_cookie(args, smp, kw, private);
1742}
1743
Willy Tarreau79e57332018-10-02 16:01:16 +02001744/* Iterate over all cookies present in a request to count how many occurrences
1745 * match the name in args and args->data.str.len. If <multi> is non-null, then
1746 * multiple cookies may be parsed on the same line. The returned sample is of
1747 * type UINT. Accepts exactly 1 argument of type string.
1748 */
1749static int smp_fetch_cookie_cnt(const struct arg *args, struct sample *smp, const char *kw, void *private)
1750{
Christopher Faulet89dc4992019-04-17 12:02:59 +02001751 /* possible keywords: req.cook_cnt / cook_cnt, res.cook_cnt / scook_cnt */
1752 struct channel *chn = ((kw[0] == 'c' || kw[2] == 'q') ? SMP_REQ_CHN(smp) : SMP_RES_CHN(smp));
Christopher Fauletf98e6262020-05-06 09:42:04 +02001753 struct check *check = ((kw[0] == 's' || kw[2] == 's') ? objt_check(smp->sess->origin) : NULL);
Christopher Faulet16032ab2020-04-30 11:30:00 +02001754 struct htx *htx = smp_prefetch_htx(smp, chn, check, 1);
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001755 struct http_hdr_ctx ctx;
1756 struct ist hdr;
Willy Tarreau79e57332018-10-02 16:01:16 +02001757 char *val_beg, *val_end;
Christopher Faulet97fc8da2020-11-13 13:41:04 +01001758 char *cook = NULL;
1759 size_t cook_l = 0;
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001760 int cnt;
Willy Tarreau79e57332018-10-02 16:01:16 +02001761
Christopher Faulet623af932021-01-29 11:22:15 +01001762 if (args->type == ARGT_STR){
Christopher Faulet97fc8da2020-11-13 13:41:04 +01001763 cook = args->data.str.area;
1764 cook_l = args->data.str.data;
1765 }
Willy Tarreau79e57332018-10-02 16:01:16 +02001766
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001767 if (!htx)
1768 return 0;
Willy Tarreau79e57332018-10-02 16:01:16 +02001769
Christopher Faulet16032ab2020-04-30 11:30:00 +02001770 hdr = (!(check || (chn && chn->flags & CF_ISRESP)) ? ist("Cookie") : ist("Set-Cookie"));
Willy Tarreau79e57332018-10-02 16:01:16 +02001771
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001772 val_end = val_beg = NULL;
1773 ctx.blk = NULL;
1774 cnt = 0;
1775 while (1) {
1776 /* Note: val_beg == NULL every time we need to fetch a new header */
1777 if (!val_beg) {
1778 if (!http_find_header(htx, hdr, &ctx, 0))
1779 break;
Willy Tarreau79e57332018-10-02 16:01:16 +02001780
Christopher Faulet97fc8da2020-11-13 13:41:04 +01001781 if (ctx.value.len < cook_l + 1)
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001782 continue;
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001783
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001784 val_beg = ctx.value.ptr;
1785 val_end = val_beg + ctx.value.len;
Willy Tarreau79e57332018-10-02 16:01:16 +02001786 }
1787
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001788 smp->data.type = SMP_T_STR;
1789 smp->flags |= SMP_F_CONST;
1790 while ((val_beg = http_extract_cookie_value(val_beg, val_end,
Christopher Faulet97fc8da2020-11-13 13:41:04 +01001791 cook, cook_l,
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001792 (smp->opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ,
1793 &smp->data.u.str.area,
1794 &smp->data.u.str.data))) {
1795 cnt++;
Willy Tarreau79e57332018-10-02 16:01:16 +02001796 }
1797 }
1798
1799 smp->data.type = SMP_T_SINT;
1800 smp->data.u.sint = cnt;
1801 smp->flags |= SMP_F_VOL_HDR;
1802 return 1;
1803}
1804
1805/* Fetch an cookie's integer value. The integer value is returned. It
1806 * takes a mandatory argument of type string. It relies on smp_fetch_cookie().
1807 */
1808static int smp_fetch_cookie_val(const struct arg *args, struct sample *smp, const char *kw, void *private)
1809{
1810 int ret = smp_fetch_cookie(args, smp, kw, private);
1811
1812 if (ret > 0) {
1813 smp->data.type = SMP_T_SINT;
1814 smp->data.u.sint = strl2ic(smp->data.u.str.area,
1815 smp->data.u.str.data);
1816 }
1817
1818 return ret;
1819}
1820
1821/************************************************************************/
1822/* The code below is dedicated to sample fetches */
1823/************************************************************************/
1824
1825/* This scans a URL-encoded query string. It takes an optionally wrapping
Ilya Shipitsin46a030c2020-07-05 16:36:08 +05001826 * string whose first contiguous chunk has its beginning in ctx->a[0] and end
Willy Tarreau79e57332018-10-02 16:01:16 +02001827 * in ctx->a[1], and the optional second part in (ctx->a[2]..ctx->a[3]). The
1828 * pointers are updated for next iteration before leaving.
1829 */
1830static int smp_fetch_param(char delim, const char *name, int name_len, const struct arg *args, struct sample *smp, const char *kw, void *private)
1831{
1832 const char *vstart, *vend;
1833 struct buffer *temp;
1834 const char **chunks = (const char **)smp->ctx.a;
1835
1836 if (!http_find_next_url_param(chunks, name, name_len,
1837 &vstart, &vend, delim))
1838 return 0;
1839
1840 /* Create sample. If the value is contiguous, return the pointer as CONST,
1841 * if the value is wrapped, copy-it in a buffer.
1842 */
1843 smp->data.type = SMP_T_STR;
1844 if (chunks[2] &&
1845 vstart >= chunks[0] && vstart <= chunks[1] &&
1846 vend >= chunks[2] && vend <= chunks[3]) {
1847 /* Wrapped case. */
1848 temp = get_trash_chunk();
1849 memcpy(temp->area, vstart, chunks[1] - vstart);
1850 memcpy(temp->area + ( chunks[1] - vstart ), chunks[2],
1851 vend - chunks[2]);
1852 smp->data.u.str.area = temp->area;
1853 smp->data.u.str.data = ( chunks[1] - vstart ) + ( vend - chunks[2] );
1854 } else {
1855 /* Contiguous case. */
1856 smp->data.u.str.area = (char *)vstart;
1857 smp->data.u.str.data = vend - vstart;
1858 smp->flags = SMP_F_VOL_1ST | SMP_F_CONST;
1859 }
1860
1861 /* Update context, check wrapping. */
1862 chunks[0] = vend;
1863 if (chunks[2] && vend >= chunks[2] && vend <= chunks[3]) {
1864 chunks[1] = chunks[3];
1865 chunks[2] = NULL;
1866 }
1867
1868 if (chunks[0] < chunks[1])
1869 smp->flags |= SMP_F_NOT_LAST;
1870
1871 return 1;
1872}
1873
1874/* This function iterates over each parameter of the query string. It uses
1875 * ctx->a[0] and ctx->a[1] to store the beginning and end of the current
1876 * parameter. Since it uses smp_fetch_param(), ctx->a[2..3] are both NULL.
1877 * An optional parameter name is passed in args[0], otherwise any parameter is
1878 * considered. It supports an optional delimiter argument for the beginning of
1879 * the string in args[1], which defaults to "?".
1880 */
1881static int smp_fetch_url_param(const struct arg *args, struct sample *smp, const char *kw, void *private)
1882{
Christopher Faulet89dc4992019-04-17 12:02:59 +02001883 struct channel *chn = SMP_REQ_CHN(smp);
Willy Tarreau79e57332018-10-02 16:01:16 +02001884 char delim = '?';
1885 const char *name;
1886 int name_len;
1887
Christopher Faulet623af932021-01-29 11:22:15 +01001888 if ((args[0].type && args[0].type != ARGT_STR) ||
Willy Tarreau79e57332018-10-02 16:01:16 +02001889 (args[1].type && args[1].type != ARGT_STR))
1890 return 0;
1891
1892 name = "";
1893 name_len = 0;
1894 if (args->type == ARGT_STR) {
1895 name = args->data.str.area;
1896 name_len = args->data.str.data;
1897 }
1898
1899 if (args[1].type)
1900 delim = *args[1].data.str.area;
1901
1902 if (!smp->ctx.a[0]) { // first call, find the query string
Christopher Faulet778f5ed2020-04-29 15:51:55 +02001903 struct htx *htx = smp_prefetch_htx(smp, chn, NULL, 1);
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001904 struct htx_sl *sl;
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001905
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001906 if (!htx)
1907 return 0;
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001908
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001909 sl = http_get_stline(htx);
1910 smp->ctx.a[0] = http_find_param_list(HTX_SL_REQ_UPTR(sl), HTX_SL_REQ_ULEN(sl), delim);
1911 if (!smp->ctx.a[0])
1912 return 0;
Willy Tarreau79e57332018-10-02 16:01:16 +02001913
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001914 smp->ctx.a[1] = HTX_SL_REQ_UPTR(sl) + HTX_SL_REQ_ULEN(sl);
Willy Tarreau79e57332018-10-02 16:01:16 +02001915
1916 /* Assume that the context is filled with NULL pointer
1917 * before the first call.
1918 * smp->ctx.a[2] = NULL;
1919 * smp->ctx.a[3] = NULL;
1920 */
1921 }
1922
1923 return smp_fetch_param(delim, name, name_len, args, smp, kw, private);
1924}
1925
1926/* This function iterates over each parameter of the body. This requires
1927 * that the body has been waited for using http-buffer-request. It uses
1928 * ctx->a[0] and ctx->a[1] to store the beginning and end of the first
Ilya Shipitsin46a030c2020-07-05 16:36:08 +05001929 * contiguous part of the body, and optionally ctx->a[2..3] to reference the
Willy Tarreau79e57332018-10-02 16:01:16 +02001930 * optional second part if the body wraps at the end of the buffer. An optional
1931 * parameter name is passed in args[0], otherwise any parameter is considered.
1932 */
1933static int smp_fetch_body_param(const struct arg *args, struct sample *smp, const char *kw, void *private)
1934{
Christopher Faulet89dc4992019-04-17 12:02:59 +02001935 struct channel *chn = SMP_REQ_CHN(smp);
Willy Tarreau79e57332018-10-02 16:01:16 +02001936 const char *name;
1937 int name_len;
1938
Christopher Faulet623af932021-01-29 11:22:15 +01001939 if (args[0].type && args[0].type != ARGT_STR)
Willy Tarreau79e57332018-10-02 16:01:16 +02001940 return 0;
1941
1942 name = "";
1943 name_len = 0;
1944 if (args[0].type == ARGT_STR) {
1945 name = args[0].data.str.area;
1946 name_len = args[0].data.str.data;
1947 }
1948
1949 if (!smp->ctx.a[0]) { // first call, find the query string
Christopher Faulete596d182020-05-05 17:46:34 +02001950 struct htx *htx = smp_prefetch_htx(smp, chn, NULL, 1);
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001951 struct buffer *temp;
1952 int32_t pos;
Willy Tarreau79e57332018-10-02 16:01:16 +02001953
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001954 if (!htx)
1955 return 0;
Willy Tarreau79e57332018-10-02 16:01:16 +02001956
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001957 temp = get_trash_chunk();
1958 for (pos = htx_get_first(htx); pos != -1; pos = htx_get_next(htx, pos)) {
1959 struct htx_blk *blk = htx_get_blk(htx, pos);
1960 enum htx_blk_type type = htx_get_blk_type(blk);
Willy Tarreau79e57332018-10-02 16:01:16 +02001961
Christopher Fauletd1ac2b92020-12-02 19:12:22 +01001962 if (type == HTX_BLK_TLR || type == HTX_BLK_EOT)
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001963 break;
1964 if (type == HTX_BLK_DATA) {
Christopher Faulet53a899b2019-10-08 16:38:42 +02001965 if (!h1_format_htx_data(htx_get_blk_value(htx, blk), temp, 0))
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001966 return 0;
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001967 }
Willy Tarreau79e57332018-10-02 16:01:16 +02001968 }
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001969
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001970 smp->ctx.a[0] = temp->area;
1971 smp->ctx.a[1] = temp->area + temp->data;
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001972
Christopher Faulet6d1dd462019-07-15 14:36:03 +02001973 /* Assume that the context is filled with NULL pointer
1974 * before the first call.
1975 * smp->ctx.a[2] = NULL;
1976 * smp->ctx.a[3] = NULL;
1977 */
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001978
Willy Tarreau79e57332018-10-02 16:01:16 +02001979 }
Christopher Faulet311c7ea2018-10-24 21:41:55 +02001980
Willy Tarreau79e57332018-10-02 16:01:16 +02001981 return smp_fetch_param('&', name, name_len, args, smp, kw, private);
1982}
1983
1984/* Return the signed integer value for the specified url parameter (see url_param
1985 * above).
1986 */
1987static int smp_fetch_url_param_val(const struct arg *args, struct sample *smp, const char *kw, void *private)
1988{
1989 int ret = smp_fetch_url_param(args, smp, kw, private);
1990
1991 if (ret > 0) {
1992 smp->data.type = SMP_T_SINT;
1993 smp->data.u.sint = strl2ic(smp->data.u.str.area,
1994 smp->data.u.str.data);
1995 }
1996
1997 return ret;
1998}
1999
2000/* This produces a 32-bit hash of the concatenation of the first occurrence of
2001 * the Host header followed by the path component if it begins with a slash ('/').
2002 * This means that '*' will not be added, resulting in exactly the first Host
2003 * entry. If no Host header is found, then the path is used. The resulting value
2004 * is hashed using the url hash followed by a full avalanche hash and provides a
2005 * 32-bit integer value. This fetch is useful for tracking per-URL activity on
2006 * high-traffic sites without having to store whole paths.
2007 * this differs from the base32 functions in that it includes the url parameters
2008 * as well as the path
2009 */
2010static int smp_fetch_url32(const struct arg *args, struct sample *smp, const char *kw, void *private)
2011{
Christopher Faulet89dc4992019-04-17 12:02:59 +02002012 struct channel *chn = SMP_REQ_CHN(smp);
Christopher Faulet778f5ed2020-04-29 15:51:55 +02002013 struct htx *htx = smp_prefetch_htx(smp, chn, NULL, 1);
Christopher Faulet6d1dd462019-07-15 14:36:03 +02002014 struct http_hdr_ctx ctx;
2015 struct htx_sl *sl;
2016 struct ist path;
Willy Tarreau79e57332018-10-02 16:01:16 +02002017 unsigned int hash = 0;
Amaury Denoyellec453f952021-07-06 11:40:12 +02002018 struct http_uri_parser parser;
Willy Tarreau79e57332018-10-02 16:01:16 +02002019
Christopher Faulet6d1dd462019-07-15 14:36:03 +02002020 if (!htx)
2021 return 0;
Christopher Faulet311c7ea2018-10-24 21:41:55 +02002022
Christopher Faulet6d1dd462019-07-15 14:36:03 +02002023 ctx.blk = NULL;
2024 if (http_find_header(htx, ist("Host"), &ctx, 1)) {
2025 /* OK we have the header value in ctx.value */
2026 while (ctx.value.len--)
2027 hash = *(ctx.value.ptr++) + (hash << 6) + (hash << 16) - hash;
Willy Tarreau79e57332018-10-02 16:01:16 +02002028 }
Christopher Faulet311c7ea2018-10-24 21:41:55 +02002029
Christopher Faulet6d1dd462019-07-15 14:36:03 +02002030 /* now retrieve the path */
2031 sl = http_get_stline(htx);
Amaury Denoyellec453f952021-07-06 11:40:12 +02002032 parser = http_uri_parser_init(htx_sl_req_uri(sl));
2033 path = http_parse_path(&parser);
Christopher Faulet6d1dd462019-07-15 14:36:03 +02002034 if (path.len && *(path.ptr) == '/') {
2035 while (path.len--)
2036 hash = *(path.ptr++) + (hash << 6) + (hash << 16) - hash;
Willy Tarreau79e57332018-10-02 16:01:16 +02002037 }
Christopher Faulet311c7ea2018-10-24 21:41:55 +02002038
Willy Tarreau79e57332018-10-02 16:01:16 +02002039 hash = full_hash(hash);
2040
2041 smp->data.type = SMP_T_SINT;
2042 smp->data.u.sint = hash;
2043 smp->flags = SMP_F_VOL_1ST;
2044 return 1;
2045}
2046
2047/* This concatenates the source address with the 32-bit hash of the Host and
2048 * URL as returned by smp_fetch_base32(). The idea is to have per-source and
2049 * per-url counters. The result is a binary block from 8 to 20 bytes depending
2050 * on the source address length. The URL hash is stored before the address so
2051 * that in environments where IPv6 is insignificant, truncating the output to
2052 * 8 bytes would still work.
2053 */
2054static int smp_fetch_url32_src(const struct arg *args, struct sample *smp, const char *kw, void *private)
2055{
Christopher Faulet8da67aa2022-03-29 17:53:09 +02002056 const struct sockaddr_storage *src = (smp->strm ? cs_src(smp->strm->csf) : NULL);
Willy Tarreau79e57332018-10-02 16:01:16 +02002057 struct buffer *temp;
Willy Tarreau79e57332018-10-02 16:01:16 +02002058
Christopher Faulet6fc817a2021-10-25 07:48:27 +02002059 if (!src)
Willy Tarreau79e57332018-10-02 16:01:16 +02002060 return 0;
2061
2062 if (!smp_fetch_url32(args, smp, kw, private))
2063 return 0;
2064
2065 temp = get_trash_chunk();
2066 *(unsigned int *) temp->area = htonl(smp->data.u.sint);
2067 temp->data += sizeof(unsigned int);
2068
Christopher Faulet6fc817a2021-10-25 07:48:27 +02002069 switch (src->ss_family) {
Willy Tarreau79e57332018-10-02 16:01:16 +02002070 case AF_INET:
2071 memcpy(temp->area + temp->data,
Christopher Faulet6fc817a2021-10-25 07:48:27 +02002072 &((struct sockaddr_in *)src)->sin_addr,
Willy Tarreau79e57332018-10-02 16:01:16 +02002073 4);
2074 temp->data += 4;
2075 break;
2076 case AF_INET6:
2077 memcpy(temp->area + temp->data,
Christopher Faulet6fc817a2021-10-25 07:48:27 +02002078 &((struct sockaddr_in6 *)src)->sin6_addr,
Willy Tarreau79e57332018-10-02 16:01:16 +02002079 16);
2080 temp->data += 16;
2081 break;
2082 default:
2083 return 0;
2084 }
2085
2086 smp->data.u.str = *temp;
2087 smp->data.type = SMP_T_BIN;
2088 return 1;
2089}
2090
2091/************************************************************************/
2092/* Other utility functions */
2093/************************************************************************/
2094
2095/* This function is used to validate the arguments passed to any "hdr" fetch
2096 * keyword. These keywords support an optional positive or negative occurrence
2097 * number. We must ensure that the number is greater than -MAX_HDR_HISTORY. It
2098 * is assumed that the types are already the correct ones. Returns 0 on error,
2099 * non-zero if OK. If <err> is not NULL, it will be filled with a pointer to an
2100 * error message in case of error, that the caller is responsible for freeing.
2101 * The initial location must either be freeable or NULL.
2102 * Note: this function's pointer is checked from Lua.
2103 */
2104int val_hdr(struct arg *arg, char **err_msg)
2105{
2106 if (arg && arg[1].type == ARGT_SINT && arg[1].data.sint < -MAX_HDR_HISTORY) {
2107 memprintf(err_msg, "header occurrence must be >= %d", -MAX_HDR_HISTORY);
2108 return 0;
2109 }
2110 return 1;
2111}
2112
2113/************************************************************************/
2114/* All supported sample fetch keywords must be declared here. */
2115/************************************************************************/
2116
2117/* Note: must not be declared <const> as its list will be overwritten */
2118static struct sample_fetch_kw_list sample_fetch_keywords = {ILH, {
2119 { "base", smp_fetch_base, 0, NULL, SMP_T_STR, SMP_USE_HRQHV },
2120 { "base32", smp_fetch_base32, 0, NULL, SMP_T_SINT, SMP_USE_HRQHV },
2121 { "base32+src", smp_fetch_base32_src, 0, NULL, SMP_T_BIN, SMP_USE_HRQHV },
Yves Lafonb4d37082021-02-11 11:01:28 +01002122 { "baseq", smp_fetch_base, 0, NULL, SMP_T_STR, SMP_USE_HRQHV },
Willy Tarreau79e57332018-10-02 16:01:16 +02002123
2124 /* capture are allocated and are permanent in the stream */
2125 { "capture.req.hdr", smp_fetch_capture_req_hdr, ARG1(1,SINT), NULL, SMP_T_STR, SMP_USE_HRQHP },
2126
2127 /* retrieve these captures from the HTTP logs */
2128 { "capture.req.method", smp_fetch_capture_req_method, 0, NULL, SMP_T_STR, SMP_USE_HRQHP },
2129 { "capture.req.uri", smp_fetch_capture_req_uri, 0, NULL, SMP_T_STR, SMP_USE_HRQHP },
2130 { "capture.req.ver", smp_fetch_capture_req_ver, 0, NULL, SMP_T_STR, SMP_USE_HRQHP },
2131
2132 { "capture.res.hdr", smp_fetch_capture_res_hdr, ARG1(1,SINT), NULL, SMP_T_STR, SMP_USE_HRSHP },
2133 { "capture.res.ver", smp_fetch_capture_res_ver, 0, NULL, SMP_T_STR, SMP_USE_HRQHP },
2134
2135 /* cookie is valid in both directions (eg: for "stick ...") but cook*
2136 * are only here to match the ACL's name, are request-only and are used
2137 * for ACL compatibility only.
2138 */
2139 { "cook", smp_fetch_cookie, ARG1(0,STR), NULL, SMP_T_STR, SMP_USE_HRQHV },
Christopher Fauletc1f40dd2019-05-16 10:07:30 +02002140 { "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 +02002141 { "cook_cnt", smp_fetch_cookie_cnt, ARG1(0,STR), NULL, SMP_T_SINT, SMP_USE_HRQHV },
2142 { "cook_val", smp_fetch_cookie_val, ARG1(0,STR), NULL, SMP_T_SINT, SMP_USE_HRQHV },
2143
2144 /* hdr is valid in both directions (eg: for "stick ...") but hdr_* are
2145 * only here to match the ACL's name, are request-only and are used for
2146 * ACL compatibility only.
2147 */
Christopher Fauletc1f40dd2019-05-16 10:07:30 +02002148 { "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 +02002149 { "hdr_cnt", smp_fetch_hdr_cnt, ARG1(0,STR), NULL, SMP_T_SINT, SMP_USE_HRQHV },
2150 { "hdr_ip", smp_fetch_hdr_ip, ARG2(0,STR,SINT), val_hdr, SMP_T_IPV4, SMP_USE_HRQHV },
2151 { "hdr_val", smp_fetch_hdr_val, ARG2(0,STR,SINT), val_hdr, SMP_T_SINT, SMP_USE_HRQHV },
2152
Christopher Fauleta4063562019-08-02 11:51:37 +02002153 { "http_auth_type", smp_fetch_http_auth_type, 0, NULL, SMP_T_STR, SMP_USE_HRQHV },
2154 { "http_auth_user", smp_fetch_http_auth_user, 0, NULL, SMP_T_STR, SMP_USE_HRQHV },
2155 { "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 +02002156 { "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 +02002157 { "http_auth", smp_fetch_http_auth, ARG1(1,USR), NULL, SMP_T_BOOL, SMP_USE_HRQHV },
2158 { "http_auth_group", smp_fetch_http_auth_grp, ARG1(1,USR), NULL, SMP_T_STR, SMP_USE_HRQHV },
2159 { "http_first_req", smp_fetch_http_first_req, 0, NULL, SMP_T_BOOL, SMP_USE_HRQHP },
2160 { "method", smp_fetch_meth, 0, NULL, SMP_T_METH, SMP_USE_HRQHP },
2161 { "path", smp_fetch_path, 0, NULL, SMP_T_STR, SMP_USE_HRQHV },
Christopher Faulete720c322020-09-02 17:25:18 +02002162 { "pathq", smp_fetch_path, 0, NULL, SMP_T_STR, SMP_USE_HRQHV },
Willy Tarreau79e57332018-10-02 16:01:16 +02002163 { "query", smp_fetch_query, 0, NULL, SMP_T_STR, SMP_USE_HRQHV },
2164
2165 /* HTTP protocol on the request path */
2166 { "req.proto_http", smp_fetch_proto_http, 0, NULL, SMP_T_BOOL, SMP_USE_HRQHP },
2167 { "req_proto_http", smp_fetch_proto_http, 0, NULL, SMP_T_BOOL, SMP_USE_HRQHP },
2168
2169 /* HTTP version on the request path */
2170 { "req.ver", smp_fetch_rqver, 0, NULL, SMP_T_STR, SMP_USE_HRQHV },
2171 { "req_ver", smp_fetch_rqver, 0, NULL, SMP_T_STR, SMP_USE_HRQHV },
2172
2173 { "req.body", smp_fetch_body, 0, NULL, SMP_T_BIN, SMP_USE_HRQHV },
2174 { "req.body_len", smp_fetch_body_len, 0, NULL, SMP_T_SINT, SMP_USE_HRQHV },
2175 { "req.body_size", smp_fetch_body_size, 0, NULL, SMP_T_SINT, SMP_USE_HRQHV },
2176 { "req.body_param", smp_fetch_body_param, ARG1(0,STR), NULL, SMP_T_BIN, SMP_USE_HRQHV },
2177
2178 { "req.hdrs", smp_fetch_hdrs, 0, NULL, SMP_T_BIN, SMP_USE_HRQHV },
2179 { "req.hdrs_bin", smp_fetch_hdrs_bin, 0, NULL, SMP_T_BIN, SMP_USE_HRQHV },
2180
2181 /* HTTP version on the response path */
2182 { "res.ver", smp_fetch_stver, 0, NULL, SMP_T_STR, SMP_USE_HRSHV },
2183 { "resp_ver", smp_fetch_stver, 0, NULL, SMP_T_STR, SMP_USE_HRSHV },
2184
Christopher Faulete596d182020-05-05 17:46:34 +02002185 { "res.body", smp_fetch_body, 0, NULL, SMP_T_BIN, SMP_USE_HRSHV },
2186 { "res.body_len", smp_fetch_body_len, 0, NULL, SMP_T_SINT, SMP_USE_HRSHV },
2187 { "res.body_size", smp_fetch_body_size, 0, NULL, SMP_T_SINT, SMP_USE_HRSHV },
2188
2189 { "res.hdrs", smp_fetch_hdrs, 0, NULL, SMP_T_BIN, SMP_USE_HRSHV },
2190 { "res.hdrs_bin", smp_fetch_hdrs_bin, 0, NULL, SMP_T_BIN, SMP_USE_HRSHV },
2191
Willy Tarreau79e57332018-10-02 16:01:16 +02002192 /* explicit req.{cook,hdr} are used to force the fetch direction to be request-only */
2193 { "req.cook", smp_fetch_cookie, ARG1(0,STR), NULL, SMP_T_STR, SMP_USE_HRQHV },
2194 { "req.cook_cnt", smp_fetch_cookie_cnt, ARG1(0,STR), NULL, SMP_T_SINT, SMP_USE_HRQHV },
2195 { "req.cook_val", smp_fetch_cookie_val, ARG1(0,STR), NULL, SMP_T_SINT, SMP_USE_HRQHV },
2196
2197 { "req.fhdr", smp_fetch_fhdr, ARG2(0,STR,SINT), val_hdr, SMP_T_STR, SMP_USE_HRQHV },
2198 { "req.fhdr_cnt", smp_fetch_fhdr_cnt, ARG1(0,STR), NULL, SMP_T_SINT, SMP_USE_HRQHV },
2199 { "req.hdr", smp_fetch_hdr, ARG2(0,STR,SINT), val_hdr, SMP_T_STR, SMP_USE_HRQHV },
2200 { "req.hdr_cnt", smp_fetch_hdr_cnt, ARG1(0,STR), NULL, SMP_T_SINT, SMP_USE_HRQHV },
2201 { "req.hdr_ip", smp_fetch_hdr_ip, ARG2(0,STR,SINT), val_hdr, SMP_T_IPV4, SMP_USE_HRQHV },
2202 { "req.hdr_names", smp_fetch_hdr_names, ARG1(0,STR), NULL, SMP_T_STR, SMP_USE_HRQHV },
2203 { "req.hdr_val", smp_fetch_hdr_val, ARG2(0,STR,SINT), val_hdr, SMP_T_SINT, SMP_USE_HRQHV },
2204
2205 /* explicit req.{cook,hdr} are used to force the fetch direction to be response-only */
2206 { "res.cook", smp_fetch_cookie, ARG1(0,STR), NULL, SMP_T_STR, SMP_USE_HRSHV },
2207 { "res.cook_cnt", smp_fetch_cookie_cnt, ARG1(0,STR), NULL, SMP_T_SINT, SMP_USE_HRSHV },
2208 { "res.cook_val", smp_fetch_cookie_val, ARG1(0,STR), NULL, SMP_T_SINT, SMP_USE_HRSHV },
2209
2210 { "res.fhdr", smp_fetch_fhdr, ARG2(0,STR,SINT), val_hdr, SMP_T_STR, SMP_USE_HRSHV },
2211 { "res.fhdr_cnt", smp_fetch_fhdr_cnt, ARG1(0,STR), NULL, SMP_T_SINT, SMP_USE_HRSHV },
2212 { "res.hdr", smp_fetch_hdr, ARG2(0,STR,SINT), val_hdr, SMP_T_STR, SMP_USE_HRSHV },
2213 { "res.hdr_cnt", smp_fetch_hdr_cnt, ARG1(0,STR), NULL, SMP_T_SINT, SMP_USE_HRSHV },
2214 { "res.hdr_ip", smp_fetch_hdr_ip, ARG2(0,STR,SINT), val_hdr, SMP_T_IPV4, SMP_USE_HRSHV },
2215 { "res.hdr_names", smp_fetch_hdr_names, ARG1(0,STR), NULL, SMP_T_STR, SMP_USE_HRSHV },
2216 { "res.hdr_val", smp_fetch_hdr_val, ARG2(0,STR,SINT), val_hdr, SMP_T_SINT, SMP_USE_HRSHV },
2217
2218 /* scook is valid only on the response and is used for ACL compatibility */
2219 { "scook", smp_fetch_cookie, ARG1(0,STR), NULL, SMP_T_STR, SMP_USE_HRSHV },
2220 { "scook_cnt", smp_fetch_cookie_cnt, ARG1(0,STR), NULL, SMP_T_SINT, SMP_USE_HRSHV },
2221 { "scook_val", smp_fetch_cookie_val, ARG1(0,STR), NULL, SMP_T_SINT, SMP_USE_HRSHV },
Willy Tarreau79e57332018-10-02 16:01:16 +02002222
2223 /* shdr is valid only on the response and is used for ACL compatibility */
2224 { "shdr", smp_fetch_hdr, ARG2(0,STR,SINT), val_hdr, SMP_T_STR, SMP_USE_HRSHV },
2225 { "shdr_cnt", smp_fetch_hdr_cnt, ARG1(0,STR), NULL, SMP_T_SINT, SMP_USE_HRSHV },
2226 { "shdr_ip", smp_fetch_hdr_ip, ARG2(0,STR,SINT), val_hdr, SMP_T_IPV4, SMP_USE_HRSHV },
2227 { "shdr_val", smp_fetch_hdr_val, ARG2(0,STR,SINT), val_hdr, SMP_T_SINT, SMP_USE_HRSHV },
2228
2229 { "status", smp_fetch_stcode, 0, NULL, SMP_T_SINT, SMP_USE_HRSHP },
2230 { "unique-id", smp_fetch_uniqueid, 0, NULL, SMP_T_STR, SMP_SRC_L4SRV },
2231 { "url", smp_fetch_url, 0, NULL, SMP_T_STR, SMP_USE_HRQHV },
2232 { "url32", smp_fetch_url32, 0, NULL, SMP_T_SINT, SMP_USE_HRQHV },
2233 { "url32+src", smp_fetch_url32_src, 0, NULL, SMP_T_BIN, SMP_USE_HRQHV },
2234 { "url_ip", smp_fetch_url_ip, 0, NULL, SMP_T_IPV4, SMP_USE_HRQHV },
2235 { "url_port", smp_fetch_url_port, 0, NULL, SMP_T_SINT, SMP_USE_HRQHV },
2236 { "url_param", smp_fetch_url_param, ARG2(0,STR,STR), NULL, SMP_T_STR, SMP_USE_HRQHV },
2237 { "urlp" , smp_fetch_url_param, ARG2(0,STR,STR), NULL, SMP_T_STR, SMP_USE_HRQHV },
2238 { "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 +02002239
Willy Tarreau79e57332018-10-02 16:01:16 +02002240 { /* END */ },
2241}};
2242
Willy Tarreau0108d902018-11-25 19:14:37 +01002243INITCALL1(STG_REGISTER, sample_register_fetches, &sample_fetch_keywords);
Willy Tarreau79e57332018-10-02 16:01:16 +02002244
2245/*
2246 * Local variables:
2247 * c-indent-level: 8
2248 * c-basic-offset: 8
2249 * End:
2250 */