blob: fdf3c42f1188a26288b305210803c04e154f64df [file] [log] [blame]
Willy Tarreau79e57332018-10-02 16:01:16 +02001/*
2 * HTTP sample conversion
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
19#include <common/chunk.h>
20#include <common/compat.h>
21#include <common/config.h>
22#include <common/debug.h>
23#include <common/http.h>
Willy Tarreau0108d902018-11-25 19:14:37 +010024#include <common/initcall.h>
Willy Tarreau79e57332018-10-02 16:01:16 +020025#include <common/memory.h>
26#include <common/standard.h>
27#include <common/version.h>
28
29#include <types/capture.h>
30#include <types/global.h>
31
32#include <proto/arg.h>
33#include <proto/sample.h>
34#include <proto/stream.h>
35
Damien Claisseae6f1252019-10-30 15:57:28 +000036static int smp_check_http_date_unit(struct arg *args, struct sample_conv *conv,
37 const char *file, int line, char **err)
38{
39 return smp_check_date_unit(args, err);
40}
Willy Tarreau79e57332018-10-02 16:01:16 +020041
42/* takes an UINT value on input supposed to represent the time since EPOCH,
43 * adds an optional offset found in args[0] and emits a string representing
Damien Claisseae6f1252019-10-30 15:57:28 +000044 * the date in RFC-1123/5322 format. If optional unit param in args[1] is
45 * provided, decode timestamp in milliseconds ("ms") or microseconds("us"),
46 * and use relevant output date format.
Willy Tarreau79e57332018-10-02 16:01:16 +020047 */
48static int sample_conv_http_date(const struct arg *args, struct sample *smp, void *private)
49{
50 const char day[7][4] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
51 const char mon[12][4] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
52 struct buffer *temp;
53 struct tm *tm;
Damien Claisseae6f1252019-10-30 15:57:28 +000054 int sec_frac = 0;
55 time_t curr_date;
Willy Tarreau79e57332018-10-02 16:01:16 +020056
57 /* add offset */
58 if (args && (args[0].type == ARGT_SINT))
Damien Claisseae6f1252019-10-30 15:57:28 +000059 smp->data.u.sint += args[0].data.sint;
60
61 /* report in milliseconds */
62 if (args && args[1].type == ARGT_SINT && args[1].data.sint == TIME_UNIT_MS) {
63 sec_frac = smp->data.u.sint % 1000;
64 smp->data.u.sint /= 1000;
65 }
66 /* report in microseconds */
67 else if (args && args[1].type == ARGT_SINT && args[1].data.sint == TIME_UNIT_US) {
68 sec_frac = smp->data.u.sint % 1000000;
69 smp->data.u.sint /= 1000000;
70 }
71
72 /* With high numbers, the date returned can be negative, the 55 bits mask prevent this. */
73 curr_date = smp->data.u.sint & 0x007fffffffffffffLL;
Willy Tarreau79e57332018-10-02 16:01:16 +020074
75 tm = gmtime(&curr_date);
76 if (!tm)
77 return 0;
78
79 temp = get_trash_chunk();
Damien Claisseae6f1252019-10-30 15:57:28 +000080 if (args && args[1].type == ARGT_SINT && args[1].data.sint != TIME_UNIT_S) {
81 temp->data = snprintf(temp->area, temp->size - temp->data,
82 "%s, %02d %s %04d %02d:%02d:%02d.%d GMT",
83 day[tm->tm_wday], tm->tm_mday, mon[tm->tm_mon],
84 1900+tm->tm_year,
85 tm->tm_hour, tm->tm_min, tm->tm_sec, sec_frac);
86 } else {
87 temp->data = snprintf(temp->area, temp->size - temp->data,
88 "%s, %02d %s %04d %02d:%02d:%02d GMT",
89 day[tm->tm_wday], tm->tm_mday, mon[tm->tm_mon],
90 1900+tm->tm_year,
91 tm->tm_hour, tm->tm_min, tm->tm_sec);
92 }
Willy Tarreau79e57332018-10-02 16:01:16 +020093
94 smp->data.u.str = *temp;
95 smp->data.type = SMP_T_STR;
96 return 1;
97}
98
99/* Arguments: The list of expected value, the number of parts returned and the separator */
100static int sample_conv_q_preferred(const struct arg *args, struct sample *smp, void *private)
101{
102 const char *al = smp->data.u.str.area;
103 const char *end = al + smp->data.u.str.data;
104 const char *token;
105 int toklen;
106 int qvalue;
107 const char *str;
108 const char *w;
109 int best_q = 0;
110
111 /* Set the constant to the sample, because the output of the
112 * function will be peek in the constant configuration string.
113 */
114 smp->flags |= SMP_F_CONST;
115 smp->data.u.str.size = 0;
116 smp->data.u.str.area = "";
117 smp->data.u.str.data = 0;
118
119 /* Parse the accept language */
120 while (1) {
121
122 /* Jump spaces, quit if the end is detected. */
123 while (al < end && isspace((unsigned char)*al))
124 al++;
125 if (al >= end)
126 break;
127
128 /* Start of the fisrt word. */
129 token = al;
130
131 /* Look for separator: isspace(), ',' or ';'. Next value if 0 length word. */
132 while (al < end && *al != ';' && *al != ',' && !isspace((unsigned char)*al))
133 al++;
134 if (al == token)
135 goto expect_comma;
136
137 /* Length of the token. */
138 toklen = al - token;
139 qvalue = 1000;
140
141 /* Check if the token exists in the list. If the token not exists,
142 * jump to the next token.
143 */
144 str = args[0].data.str.area;
145 w = str;
146 while (1) {
147 if (*str == ';' || *str == '\0') {
148 if (http_language_range_match(token, toklen, w, str - w))
149 goto look_for_q;
150 if (*str == '\0')
151 goto expect_comma;
152 w = str + 1;
153 }
154 str++;
155 }
156 goto expect_comma;
157
158look_for_q:
159
160 /* Jump spaces, quit if the end is detected. */
161 while (al < end && isspace((unsigned char)*al))
162 al++;
163 if (al >= end)
164 goto process_value;
165
166 /* If ',' is found, process the result */
167 if (*al == ',')
168 goto process_value;
169
170 /* If the character is different from ';', look
171 * for the end of the header part in best effort.
172 */
173 if (*al != ';')
174 goto expect_comma;
175
176 /* Assumes that the char is ';', now expect "q=". */
177 al++;
178
179 /* Jump spaces, process value if the end is detected. */
180 while (al < end && isspace((unsigned char)*al))
181 al++;
182 if (al >= end)
183 goto process_value;
184
185 /* Expect 'q'. If no 'q', continue in best effort */
186 if (*al != 'q')
187 goto process_value;
188 al++;
189
190 /* Jump spaces, process value if the end is detected. */
191 while (al < end && isspace((unsigned char)*al))
192 al++;
193 if (al >= end)
194 goto process_value;
195
196 /* Expect '='. If no '=', continue in best effort */
197 if (*al != '=')
198 goto process_value;
199 al++;
200
201 /* Jump spaces, process value if the end is detected. */
202 while (al < end && isspace((unsigned char)*al))
203 al++;
204 if (al >= end)
205 goto process_value;
206
207 /* Parse the q value. */
208 qvalue = http_parse_qvalue(al, &al);
209
210process_value:
211
212 /* If the new q value is the best q value, then store the associated
213 * language in the response. If qvalue is the biggest value (1000),
214 * break the process.
215 */
216 if (qvalue > best_q) {
217 smp->data.u.str.area = (char *)w;
218 smp->data.u.str.data = str - w;
219 if (qvalue >= 1000)
220 break;
221 best_q = qvalue;
222 }
223
224expect_comma:
225
226 /* Expect comma or end. If the end is detected, quit the loop. */
227 while (al < end && *al != ',')
228 al++;
229 if (al >= end)
230 break;
231
232 /* Comma is found, jump it and restart the analyzer. */
233 al++;
234 }
235
236 /* Set default value if required. */
237 if (smp->data.u.str.data == 0 && args[1].type == ARGT_STR) {
238 smp->data.u.str.area = args[1].data.str.area;
239 smp->data.u.str.data = args[1].data.str.data;
240 }
241
242 /* Return true only if a matching language was found. */
243 return smp->data.u.str.data != 0;
244}
245
246/* This fetch url-decode any input string. */
247static int sample_conv_url_dec(const struct arg *args, struct sample *smp, void *private)
248{
Willy Tarreau62ba9ba2020-04-23 17:54:47 +0200249 int in_form = 0;
Willy Tarreau79e57332018-10-02 16:01:16 +0200250 int len;
251
Joseph Herlant942eea32018-11-15 13:57:22 -0800252 /* If the constant flag is set or if not size is available at
Willy Tarreau79e57332018-10-02 16:01:16 +0200253 * the end of the buffer, copy the string in other buffer
254 * before decoding.
255 */
256 if (smp->flags & SMP_F_CONST || smp->data.u.str.size <= smp->data.u.str.data) {
257 struct buffer *str = get_trash_chunk();
258 memcpy(str->area, smp->data.u.str.area, smp->data.u.str.data);
259 smp->data.u.str.area = str->area;
260 smp->data.u.str.size = str->size;
261 smp->flags &= ~SMP_F_CONST;
262 }
263
264 /* Add final \0 required by url_decode(), and convert the input string. */
265 smp->data.u.str.area[smp->data.u.str.data] = '\0';
Willy Tarreau62ba9ba2020-04-23 17:54:47 +0200266
267 if (args && (args[0].type == ARGT_SINT))
268 in_form = !!args[0].data.sint;
269
270 len = url_decode(smp->data.u.str.area, in_form);
Willy Tarreau79e57332018-10-02 16:01:16 +0200271 if (len < 0)
272 return 0;
273 smp->data.u.str.data = len;
274 return 1;
275}
276
277static int smp_conv_req_capture(const struct arg *args, struct sample *smp, void *private)
278{
Willy Tarreau55758962020-04-29 11:22:08 +0200279 struct proxy *fe;
Willy Tarreau79e57332018-10-02 16:01:16 +0200280 int idx, i;
281 struct cap_hdr *hdr;
282 int len;
283
284 if (!args || args->type != ARGT_SINT)
285 return 0;
286
Willy Tarreau55758962020-04-29 11:22:08 +0200287 if (!smp->strm)
288 return 0;
289
290 fe = strm_fe(smp->strm);
Willy Tarreau79e57332018-10-02 16:01:16 +0200291 idx = args->data.sint;
292
293 /* Check the availibity of the capture id. */
294 if (idx > fe->nb_req_cap - 1)
295 return 0;
296
297 /* Look for the original configuration. */
298 for (hdr = fe->req_cap, i = fe->nb_req_cap - 1;
299 hdr != NULL && i != idx ;
300 i--, hdr = hdr->next);
301 if (!hdr)
302 return 0;
303
304 /* check for the memory allocation */
305 if (smp->strm->req_cap[hdr->index] == NULL)
306 smp->strm->req_cap[hdr->index] = pool_alloc(hdr->pool);
307 if (smp->strm->req_cap[hdr->index] == NULL)
308 return 0;
309
310 /* Check length. */
311 len = smp->data.u.str.data;
312 if (len > hdr->len)
313 len = hdr->len;
314
315 /* Capture input data. */
316 memcpy(smp->strm->req_cap[idx], smp->data.u.str.area, len);
317 smp->strm->req_cap[idx][len] = '\0';
318
319 return 1;
320}
321
322static int smp_conv_res_capture(const struct arg *args, struct sample *smp, void *private)
323{
Willy Tarreau55758962020-04-29 11:22:08 +0200324 struct proxy *fe;
Willy Tarreau79e57332018-10-02 16:01:16 +0200325 int idx, i;
326 struct cap_hdr *hdr;
327 int len;
328
329 if (!args || args->type != ARGT_SINT)
330 return 0;
331
Willy Tarreau55758962020-04-29 11:22:08 +0200332 if (!smp->strm)
333 return 0;
334
335 fe = strm_fe(smp->strm);
Willy Tarreau79e57332018-10-02 16:01:16 +0200336 idx = args->data.sint;
337
338 /* Check the availibity of the capture id. */
339 if (idx > fe->nb_rsp_cap - 1)
340 return 0;
341
342 /* Look for the original configuration. */
343 for (hdr = fe->rsp_cap, i = fe->nb_rsp_cap - 1;
344 hdr != NULL && i != idx ;
345 i--, hdr = hdr->next);
346 if (!hdr)
347 return 0;
348
349 /* check for the memory allocation */
350 if (smp->strm->res_cap[hdr->index] == NULL)
351 smp->strm->res_cap[hdr->index] = pool_alloc(hdr->pool);
352 if (smp->strm->res_cap[hdr->index] == NULL)
353 return 0;
354
355 /* Check length. */
356 len = smp->data.u.str.data;
357 if (len > hdr->len)
358 len = hdr->len;
359
360 /* Capture input data. */
361 memcpy(smp->strm->res_cap[idx], smp->data.u.str.area, len);
362 smp->strm->res_cap[idx][len] = '\0';
363
364 return 1;
365}
366
367/************************************************************************/
368/* All supported converter keywords must be declared here. */
369/************************************************************************/
370
371/* Note: must not be declared <const> as its list will be overwritten */
372static struct sample_conv_kw_list sample_conv_kws = {ILH, {
Damien Claisseae6f1252019-10-30 15:57:28 +0000373 { "http_date", sample_conv_http_date, ARG2(0,SINT,STR), smp_check_http_date_unit, SMP_T_SINT, SMP_T_STR},
Willy Tarreau79e57332018-10-02 16:01:16 +0200374 { "language", sample_conv_q_preferred, ARG2(1,STR,STR), NULL, SMP_T_STR, SMP_T_STR},
375 { "capture-req", smp_conv_req_capture, ARG1(1,SINT), NULL, SMP_T_STR, SMP_T_STR},
376 { "capture-res", smp_conv_res_capture, ARG1(1,SINT), NULL, SMP_T_STR, SMP_T_STR},
Willy Tarreau62ba9ba2020-04-23 17:54:47 +0200377 { "url_dec", sample_conv_url_dec, ARG1(0,SINT), NULL, SMP_T_STR, SMP_T_STR},
Willy Tarreau79e57332018-10-02 16:01:16 +0200378 { NULL, NULL, 0, 0, 0 },
379}};
380
Willy Tarreau0108d902018-11-25 19:14:37 +0100381INITCALL1(STG_REGISTER, sample_register_convs, &sample_conv_kws);
Willy Tarreau79e57332018-10-02 16:01:16 +0200382
383/*
384 * Local variables:
385 * c-indent-level: 8
386 * c-basic-offset: 8
387 * End:
388 */