blob: 803d7bcdf5dac5baf9239e32e284d3e7d24e3d4f [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
36
37/* takes an UINT value on input supposed to represent the time since EPOCH,
38 * adds an optional offset found in args[0] and emits a string representing
39 * the date in RFC-1123/5322 format.
40 */
41static int sample_conv_http_date(const struct arg *args, struct sample *smp, void *private)
42{
43 const char day[7][4] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
44 const char mon[12][4] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
45 struct buffer *temp;
46 struct tm *tm;
47 /* With high numbers, the date returned can be negative, the 55 bits mask prevent this. */
48 time_t curr_date = smp->data.u.sint & 0x007fffffffffffffLL;
49
50 /* add offset */
51 if (args && (args[0].type == ARGT_SINT))
52 curr_date += args[0].data.sint;
53
54 tm = gmtime(&curr_date);
55 if (!tm)
56 return 0;
57
58 temp = get_trash_chunk();
59 temp->data = snprintf(temp->area, temp->size - temp->data,
60 "%s, %02d %s %04d %02d:%02d:%02d GMT",
61 day[tm->tm_wday], tm->tm_mday, mon[tm->tm_mon],
62 1900+tm->tm_year,
63 tm->tm_hour, tm->tm_min, tm->tm_sec);
64
65 smp->data.u.str = *temp;
66 smp->data.type = SMP_T_STR;
67 return 1;
68}
69
70/* Arguments: The list of expected value, the number of parts returned and the separator */
71static int sample_conv_q_preferred(const struct arg *args, struct sample *smp, void *private)
72{
73 const char *al = smp->data.u.str.area;
74 const char *end = al + smp->data.u.str.data;
75 const char *token;
76 int toklen;
77 int qvalue;
78 const char *str;
79 const char *w;
80 int best_q = 0;
81
82 /* Set the constant to the sample, because the output of the
83 * function will be peek in the constant configuration string.
84 */
85 smp->flags |= SMP_F_CONST;
86 smp->data.u.str.size = 0;
87 smp->data.u.str.area = "";
88 smp->data.u.str.data = 0;
89
90 /* Parse the accept language */
91 while (1) {
92
93 /* Jump spaces, quit if the end is detected. */
94 while (al < end && isspace((unsigned char)*al))
95 al++;
96 if (al >= end)
97 break;
98
99 /* Start of the fisrt word. */
100 token = al;
101
102 /* Look for separator: isspace(), ',' or ';'. Next value if 0 length word. */
103 while (al < end && *al != ';' && *al != ',' && !isspace((unsigned char)*al))
104 al++;
105 if (al == token)
106 goto expect_comma;
107
108 /* Length of the token. */
109 toklen = al - token;
110 qvalue = 1000;
111
112 /* Check if the token exists in the list. If the token not exists,
113 * jump to the next token.
114 */
115 str = args[0].data.str.area;
116 w = str;
117 while (1) {
118 if (*str == ';' || *str == '\0') {
119 if (http_language_range_match(token, toklen, w, str - w))
120 goto look_for_q;
121 if (*str == '\0')
122 goto expect_comma;
123 w = str + 1;
124 }
125 str++;
126 }
127 goto expect_comma;
128
129look_for_q:
130
131 /* Jump spaces, quit if the end is detected. */
132 while (al < end && isspace((unsigned char)*al))
133 al++;
134 if (al >= end)
135 goto process_value;
136
137 /* If ',' is found, process the result */
138 if (*al == ',')
139 goto process_value;
140
141 /* If the character is different from ';', look
142 * for the end of the header part in best effort.
143 */
144 if (*al != ';')
145 goto expect_comma;
146
147 /* Assumes that the char is ';', now expect "q=". */
148 al++;
149
150 /* Jump spaces, process value if the end is detected. */
151 while (al < end && isspace((unsigned char)*al))
152 al++;
153 if (al >= end)
154 goto process_value;
155
156 /* Expect 'q'. If no 'q', continue in best effort */
157 if (*al != 'q')
158 goto process_value;
159 al++;
160
161 /* Jump spaces, process value if the end is detected. */
162 while (al < end && isspace((unsigned char)*al))
163 al++;
164 if (al >= end)
165 goto process_value;
166
167 /* Expect '='. If no '=', continue in best effort */
168 if (*al != '=')
169 goto process_value;
170 al++;
171
172 /* Jump spaces, process value if the end is detected. */
173 while (al < end && isspace((unsigned char)*al))
174 al++;
175 if (al >= end)
176 goto process_value;
177
178 /* Parse the q value. */
179 qvalue = http_parse_qvalue(al, &al);
180
181process_value:
182
183 /* If the new q value is the best q value, then store the associated
184 * language in the response. If qvalue is the biggest value (1000),
185 * break the process.
186 */
187 if (qvalue > best_q) {
188 smp->data.u.str.area = (char *)w;
189 smp->data.u.str.data = str - w;
190 if (qvalue >= 1000)
191 break;
192 best_q = qvalue;
193 }
194
195expect_comma:
196
197 /* Expect comma or end. If the end is detected, quit the loop. */
198 while (al < end && *al != ',')
199 al++;
200 if (al >= end)
201 break;
202
203 /* Comma is found, jump it and restart the analyzer. */
204 al++;
205 }
206
207 /* Set default value if required. */
208 if (smp->data.u.str.data == 0 && args[1].type == ARGT_STR) {
209 smp->data.u.str.area = args[1].data.str.area;
210 smp->data.u.str.data = args[1].data.str.data;
211 }
212
213 /* Return true only if a matching language was found. */
214 return smp->data.u.str.data != 0;
215}
216
217/* This fetch url-decode any input string. */
218static int sample_conv_url_dec(const struct arg *args, struct sample *smp, void *private)
219{
Willy Tarreau7e913cb2020-04-23 17:54:47 +0200220 int in_form = 0;
Willy Tarreau79e57332018-10-02 16:01:16 +0200221 int len;
222
Joseph Herlant942eea32018-11-15 13:57:22 -0800223 /* If the constant flag is set or if not size is available at
Willy Tarreau79e57332018-10-02 16:01:16 +0200224 * the end of the buffer, copy the string in other buffer
225 * before decoding.
226 */
227 if (smp->flags & SMP_F_CONST || smp->data.u.str.size <= smp->data.u.str.data) {
228 struct buffer *str = get_trash_chunk();
229 memcpy(str->area, smp->data.u.str.area, smp->data.u.str.data);
230 smp->data.u.str.area = str->area;
231 smp->data.u.str.size = str->size;
232 smp->flags &= ~SMP_F_CONST;
233 }
234
235 /* Add final \0 required by url_decode(), and convert the input string. */
236 smp->data.u.str.area[smp->data.u.str.data] = '\0';
Willy Tarreau7e913cb2020-04-23 17:54:47 +0200237
238 if (args && (args[0].type == ARGT_SINT))
239 in_form = !!args[0].data.sint;
240
241 len = url_decode(smp->data.u.str.area, in_form);
Willy Tarreau79e57332018-10-02 16:01:16 +0200242 if (len < 0)
243 return 0;
244 smp->data.u.str.data = len;
245 return 1;
246}
247
248static int smp_conv_req_capture(const struct arg *args, struct sample *smp, void *private)
249{
250 struct proxy *fe = strm_fe(smp->strm);
251 int idx, i;
252 struct cap_hdr *hdr;
253 int len;
254
255 if (!args || args->type != ARGT_SINT)
256 return 0;
257
258 idx = args->data.sint;
259
260 /* Check the availibity of the capture id. */
261 if (idx > fe->nb_req_cap - 1)
262 return 0;
263
264 /* Look for the original configuration. */
265 for (hdr = fe->req_cap, i = fe->nb_req_cap - 1;
266 hdr != NULL && i != idx ;
267 i--, hdr = hdr->next);
268 if (!hdr)
269 return 0;
270
271 /* check for the memory allocation */
272 if (smp->strm->req_cap[hdr->index] == NULL)
273 smp->strm->req_cap[hdr->index] = pool_alloc(hdr->pool);
274 if (smp->strm->req_cap[hdr->index] == NULL)
275 return 0;
276
277 /* Check length. */
278 len = smp->data.u.str.data;
279 if (len > hdr->len)
280 len = hdr->len;
281
282 /* Capture input data. */
283 memcpy(smp->strm->req_cap[idx], smp->data.u.str.area, len);
284 smp->strm->req_cap[idx][len] = '\0';
285
286 return 1;
287}
288
289static int smp_conv_res_capture(const struct arg *args, struct sample *smp, void *private)
290{
291 struct proxy *fe = strm_fe(smp->strm);
292 int idx, i;
293 struct cap_hdr *hdr;
294 int len;
295
296 if (!args || args->type != ARGT_SINT)
297 return 0;
298
299 idx = args->data.sint;
300
301 /* Check the availibity of the capture id. */
302 if (idx > fe->nb_rsp_cap - 1)
303 return 0;
304
305 /* Look for the original configuration. */
306 for (hdr = fe->rsp_cap, i = fe->nb_rsp_cap - 1;
307 hdr != NULL && i != idx ;
308 i--, hdr = hdr->next);
309 if (!hdr)
310 return 0;
311
312 /* check for the memory allocation */
313 if (smp->strm->res_cap[hdr->index] == NULL)
314 smp->strm->res_cap[hdr->index] = pool_alloc(hdr->pool);
315 if (smp->strm->res_cap[hdr->index] == NULL)
316 return 0;
317
318 /* Check length. */
319 len = smp->data.u.str.data;
320 if (len > hdr->len)
321 len = hdr->len;
322
323 /* Capture input data. */
324 memcpy(smp->strm->res_cap[idx], smp->data.u.str.area, len);
325 smp->strm->res_cap[idx][len] = '\0';
326
327 return 1;
328}
329
330/************************************************************************/
331/* All supported converter keywords must be declared here. */
332/************************************************************************/
333
334/* Note: must not be declared <const> as its list will be overwritten */
335static struct sample_conv_kw_list sample_conv_kws = {ILH, {
336 { "http_date", sample_conv_http_date, ARG1(0,SINT), NULL, SMP_T_SINT, SMP_T_STR},
337 { "language", sample_conv_q_preferred, ARG2(1,STR,STR), NULL, SMP_T_STR, SMP_T_STR},
338 { "capture-req", smp_conv_req_capture, ARG1(1,SINT), NULL, SMP_T_STR, SMP_T_STR},
339 { "capture-res", smp_conv_res_capture, ARG1(1,SINT), NULL, SMP_T_STR, SMP_T_STR},
Willy Tarreau7e913cb2020-04-23 17:54:47 +0200340 { "url_dec", sample_conv_url_dec, ARG1(0,SINT), NULL, SMP_T_STR, SMP_T_STR},
Willy Tarreau79e57332018-10-02 16:01:16 +0200341 { NULL, NULL, 0, 0, 0 },
342}};
343
Willy Tarreau0108d902018-11-25 19:14:37 +0100344INITCALL1(STG_REGISTER, sample_register_convs, &sample_conv_kws);
Willy Tarreau79e57332018-10-02 16:01:16 +0200345
346/*
347 * Local variables:
348 * c-indent-level: 8
349 * c-basic-offset: 8
350 * End:
351 */