| | varnish-cache/bin/varnishd/cache/cache_ban_build.c |
0 |
|
/*- |
1 |
|
* Copyright (c) 2006 Verdens Gang AS |
2 |
|
* Copyright (c) 2006-2015 Varnish Software AS |
3 |
|
* All rights reserved. |
4 |
|
* |
5 |
|
* Author: Poul-Henning Kamp <phk@phk.freebsd.dk> |
6 |
|
* |
7 |
|
* SPDX-License-Identifier: BSD-2-Clause |
8 |
|
* |
9 |
|
* Redistribution and use in source and binary forms, with or without |
10 |
|
* modification, are permitted provided that the following conditions |
11 |
|
* are met: |
12 |
|
* 1. Redistributions of source code must retain the above copyright |
13 |
|
* notice, this list of conditions and the following disclaimer. |
14 |
|
* 2. Redistributions in binary form must reproduce the above copyright |
15 |
|
* notice, this list of conditions and the following disclaimer in the |
16 |
|
* documentation and/or other materials provided with the distribution. |
17 |
|
* |
18 |
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
19 |
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
20 |
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
21 |
|
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE |
22 |
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
23 |
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
24 |
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
25 |
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
26 |
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
27 |
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
28 |
|
* SUCH DAMAGE. |
29 |
|
* |
30 |
|
*/ |
31 |
|
|
32 |
|
#include "config.h" |
33 |
|
|
34 |
|
#include <stdlib.h> |
35 |
|
|
36 |
|
#include "cache_varnishd.h" |
37 |
|
#include "cache_ban.h" |
38 |
|
|
39 |
|
#include "vend.h" |
40 |
|
#include "vtim.h" |
41 |
|
#include "vnum.h" |
42 |
|
|
43 |
|
void BAN_Build_Init(void); |
44 |
|
void BAN_Build_Fini(void); |
45 |
|
|
46 |
|
struct ban_proto { |
47 |
|
unsigned magic; |
48 |
|
#define BAN_PROTO_MAGIC 0xd8adc494 |
49 |
|
unsigned flags; /* BANS_FLAG_* */ |
50 |
|
|
51 |
|
struct vsb *vsb; |
52 |
|
char *err; |
53 |
|
}; |
54 |
|
|
55 |
|
/*-------------------------------------------------------------------- |
56 |
|
* Variables we can ban on |
57 |
|
*/ |
58 |
|
|
59 |
|
static const struct pvar { |
60 |
|
const char *name; |
61 |
|
unsigned flag; |
62 |
|
uint8_t tag; |
63 |
|
} pvars[] = { |
64 |
|
#define PVAR(a, b, c) { (a), (b), (c) }, |
65 |
|
#include "tbl/ban_vars.h" |
66 |
|
{ 0, 0, 0} |
67 |
|
}; |
68 |
|
|
69 |
|
/* operators allowed per argument (pvar.tag) */ |
70 |
|
static const unsigned arg_opervalid[BAN_ARGARRSZ + 1] = { |
71 |
|
#define ARGOPER(arg, mask) [BAN_ARGIDX(arg)] = (mask), |
72 |
|
#include "tbl/ban_arg_oper.h" |
73 |
|
[BAN_ARGARRSZ] = 0 |
74 |
|
}; |
75 |
|
|
76 |
|
// init'ed in _Init |
77 |
|
static const char *arg_operhelp[BAN_ARGARRSZ + 1]; |
78 |
|
|
79 |
|
// operators |
80 |
|
const char * const ban_oper[BAN_OPERARRSZ + 1] = { |
81 |
|
#define OPER(op, str) [BAN_OPERIDX(op)] = (str), |
82 |
|
#include "tbl/ban_oper.h" |
83 |
|
[BAN_OPERARRSZ] = NULL |
84 |
|
}; |
85 |
|
|
86 |
|
|
87 |
|
/*-------------------------------------------------------------------- |
88 |
|
*/ |
89 |
|
|
90 |
|
static char ban_build_err_no_mem[] = "No Memory"; |
91 |
|
|
92 |
|
/*-------------------------------------------------------------------- |
93 |
|
*/ |
94 |
|
|
95 |
|
struct ban_proto * |
96 |
3161 |
BAN_Build(void) |
97 |
|
{ |
98 |
|
struct ban_proto *bp; |
99 |
|
|
100 |
3161 |
ALLOC_OBJ(bp, BAN_PROTO_MAGIC); |
101 |
3161 |
if (bp == NULL) |
102 |
0 |
return (bp); |
103 |
3161 |
bp->vsb = VSB_new_auto(); |
104 |
3161 |
if (bp->vsb == NULL) { |
105 |
0 |
FREE_OBJ(bp); |
106 |
0 |
return (NULL); |
107 |
|
} |
108 |
3161 |
return (bp); |
109 |
3161 |
} |
110 |
|
|
111 |
|
// TODO: change to (struct ban_proto **) |
112 |
|
void |
113 |
3161 |
BAN_Abandon(struct ban_proto *bp) |
114 |
|
{ |
115 |
|
|
116 |
3161 |
CHECK_OBJ_NOTNULL(bp, BAN_PROTO_MAGIC); |
117 |
3161 |
VSB_destroy(&bp->vsb); |
118 |
3161 |
FREE_OBJ(bp); |
119 |
3161 |
} |
120 |
|
|
121 |
|
/*-------------------------------------------------------------------- |
122 |
|
*/ |
123 |
|
|
124 |
|
static void |
125 |
423 |
ban_add_lump(const struct ban_proto *bp, const void *p, uint32_t len) |
126 |
|
{ |
127 |
423 |
uint8_t buf[PRNDUP(sizeof len)] = { 0xff }; |
128 |
|
|
129 |
2442 |
while (VSB_len(bp->vsb) & PALGN) |
130 |
2019 |
VSB_putc(bp->vsb, buf[0]); |
131 |
423 |
vbe32enc(buf, len); |
132 |
423 |
VSB_bcat(bp->vsb, buf, sizeof buf); |
133 |
423 |
VSB_bcat(bp->vsb, p, len); |
134 |
423 |
} |
135 |
|
|
136 |
|
/*-------------------------------------------------------------------- |
137 |
|
*/ |
138 |
|
|
139 |
|
static const char * |
140 |
54 |
ban_error(struct ban_proto *bp, const char *fmt, ...) |
141 |
|
{ |
142 |
|
va_list ap; |
143 |
|
|
144 |
54 |
CHECK_OBJ_NOTNULL(bp, BAN_PROTO_MAGIC); |
145 |
54 |
AN(bp->vsb); |
146 |
|
|
147 |
|
/* First error is sticky */ |
148 |
54 |
if (bp->err == NULL) { |
149 |
54 |
if (fmt == ban_build_err_no_mem) { |
150 |
0 |
bp->err = ban_build_err_no_mem; |
151 |
0 |
} else { |
152 |
|
/* Record the error message in the vsb */ |
153 |
54 |
VSB_clear(bp->vsb); |
154 |
54 |
va_start(ap, fmt); |
155 |
54 |
VSB_vprintf(bp->vsb, fmt, ap); |
156 |
54 |
va_end(ap); |
157 |
54 |
AZ(VSB_finish(bp->vsb)); |
158 |
54 |
bp->err = VSB_data(bp->vsb); |
159 |
|
} |
160 |
54 |
} |
161 |
54 |
return (bp->err); |
162 |
|
} |
163 |
|
|
164 |
|
/*-------------------------------------------------------------------- |
165 |
|
* Parse and add a http argument specification |
166 |
|
* Output something which HTTP_GetHdr understands |
167 |
|
*/ |
168 |
|
|
169 |
|
static void |
170 |
144 |
ban_parse_http(const struct ban_proto *bp, const char *a1) |
171 |
|
{ |
172 |
|
int l; |
173 |
|
|
174 |
144 |
l = strlen(a1) + 1; |
175 |
144 |
assert(l <= 127); |
176 |
144 |
VSB_putc(bp->vsb, (char)l); |
177 |
144 |
VSB_cat(bp->vsb, a1); |
178 |
144 |
VSB_putc(bp->vsb, ':'); |
179 |
144 |
VSB_putc(bp->vsb, '\0'); |
180 |
144 |
} |
181 |
|
|
182 |
|
/*-------------------------------------------------------------------- |
183 |
|
* Parse and add a ban test specification |
184 |
|
*/ |
185 |
|
|
186 |
|
static const char * |
187 |
90 |
ban_parse_regexp(struct ban_proto *bp, const char *a3) |
188 |
|
{ |
189 |
|
struct vsb vsb[1]; |
190 |
|
char errbuf[VRE_ERROR_LEN]; |
191 |
|
int errorcode, erroroffset; |
192 |
|
size_t sz; |
193 |
|
vre_t *re, *rex; |
194 |
|
|
195 |
90 |
re = VRE_compile(a3, 0, &errorcode, &erroroffset, 0); |
196 |
90 |
if (re == NULL) { |
197 |
3 |
AN(VSB_init(vsb, errbuf, sizeof errbuf)); |
198 |
3 |
AZ(VRE_error(vsb, errorcode)); |
199 |
3 |
AZ(VSB_finish(vsb)); |
200 |
3 |
VSB_fini(vsb); |
201 |
3 |
return (ban_error(bp, "Regex compile error: %s", errbuf)); |
202 |
|
} |
203 |
|
|
204 |
87 |
rex = VRE_export(re, &sz); |
205 |
87 |
AN(rex); |
206 |
87 |
ban_add_lump(bp, rex, sz); |
207 |
87 |
VRE_free(&rex); |
208 |
87 |
VRE_free(&re); |
209 |
87 |
return (0); |
210 |
90 |
} |
211 |
|
|
212 |
|
static int |
213 |
360 |
ban_parse_oper(const char *p) |
214 |
|
{ |
215 |
|
int i; |
216 |
|
|
217 |
780 |
for (i = 0; i < BAN_OPERARRSZ; i++) { |
218 |
768 |
if (!strcmp(p, ban_oper[i])) |
219 |
348 |
return (BANS_OPER_OFF_ + i); |
220 |
420 |
} |
221 |
12 |
return (-1); |
222 |
360 |
} |
223 |
|
|
224 |
|
/*-------------------------------------------------------------------- |
225 |
|
* Add a (and'ed) test-condition to a ban |
226 |
|
*/ |
227 |
|
static const char * |
228 |
255 |
ban_add_spec(struct ban_proto *bp, const struct pvar *pv, int op, const char *a3) |
229 |
|
{ |
230 |
|
|
231 |
255 |
assert(! BANS_HAS_ARG2_DOUBLE(pv->tag)); |
232 |
|
|
233 |
255 |
ban_add_lump(bp, a3, strlen(a3) + 1); |
234 |
255 |
VSB_putc(bp->vsb, op); |
235 |
|
|
236 |
255 |
if (! BANS_HAS_ARG2_SPEC(op)) |
237 |
165 |
return (NULL); |
238 |
|
|
239 |
90 |
return (ban_parse_regexp(bp, a3)); |
240 |
255 |
} |
241 |
|
|
242 |
|
static const char * |
243 |
81 |
ban_add_double(const struct ban_proto *bp, const struct pvar *pv, int op, double darg) |
244 |
|
{ |
245 |
|
uint64_t dtmp; |
246 |
|
uint8_t denc[sizeof darg]; |
247 |
|
|
248 |
81 |
assert(BANS_HAS_ARG2_DOUBLE(pv->tag)); |
249 |
81 |
assert(sizeof darg == sizeof dtmp); |
250 |
81 |
assert(sizeof dtmp == sizeof denc); |
251 |
81 |
memcpy(&dtmp, &darg, sizeof dtmp); |
252 |
81 |
vbe64enc(denc, dtmp); |
253 |
|
|
254 |
81 |
ban_add_lump(bp, denc, sizeof denc); |
255 |
81 |
VSB_putc(bp->vsb, op); |
256 |
81 |
return (NULL); |
257 |
|
} |
258 |
|
|
259 |
|
static const char * |
260 |
87 |
ban_add_duration(struct ban_proto *bp, const struct pvar *pv, int op, const char *a3) |
261 |
|
{ |
262 |
|
double darg; |
263 |
|
|
264 |
87 |
assert(pv->flag & BANS_FLAG_DURATION); |
265 |
87 |
darg = VNUM_duration(a3); |
266 |
87 |
if (isnan(darg)) { |
267 |
12 |
return (ban_error(bp, |
268 |
6 |
"expected duration <n.nn>[ms|s|m|h|d|w|y] got \"%s\"", a3)); |
269 |
|
} |
270 |
81 |
return (ban_add_double(bp, pv, op, darg)); |
271 |
87 |
} |
272 |
|
|
273 |
|
const char * |
274 |
387 |
BAN_AddTest(struct ban_proto *bp, |
275 |
|
const char *a1, const char *a2, const char *a3) |
276 |
|
{ |
277 |
|
const struct pvar *pv; |
278 |
|
int op; |
279 |
|
|
280 |
387 |
CHECK_OBJ_NOTNULL(bp, BAN_PROTO_MAGIC); |
281 |
387 |
AN(bp->vsb); |
282 |
387 |
AN(a1); |
283 |
387 |
AN(a2); |
284 |
387 |
AN(a3); |
285 |
|
|
286 |
387 |
if (bp->err != NULL) |
287 |
0 |
return (bp->err); |
288 |
|
|
289 |
1539 |
for (pv = pvars; pv->name != NULL; pv++) { |
290 |
1515 |
if (!(pv->flag & BANS_FLAG_HTTP) && !strcmp(a1, pv->name)) |
291 |
216 |
break; |
292 |
1299 |
if ((pv->flag & BANS_FLAG_HTTP) && !strncmp(a1, pv->name, strlen(pv->name))) |
293 |
147 |
break; |
294 |
1152 |
} |
295 |
|
|
296 |
387 |
if (pv->name == NULL) |
297 |
48 |
return (ban_error(bp, |
298 |
24 |
"Unknown or unsupported field \"%s\"", a1)); |
299 |
|
|
300 |
363 |
bp->flags |= pv->flag; |
301 |
|
|
302 |
363 |
VSB_putc(bp->vsb, pv->tag); |
303 |
363 |
if (pv->flag & BANS_FLAG_HTTP) { |
304 |
147 |
if (strlen(a1 + strlen(pv->name)) < 1) |
305 |
6 |
return (ban_error(bp, |
306 |
3 |
"Missing header name: \"%s\"", pv->name)); |
307 |
144 |
assert(BANS_HAS_ARG1_SPEC(pv->tag)); |
308 |
144 |
ban_parse_http(bp, a1 + strlen(pv->name)); |
309 |
144 |
} |
310 |
|
|
311 |
360 |
op = ban_parse_oper(a2); |
312 |
360 |
if (op < BANS_OPER_OFF_ || |
313 |
348 |
((1U << BAN_OPERIDX(op)) & arg_opervalid[BAN_ARGIDX(pv->tag)]) == 0) |
314 |
36 |
return (ban_error(bp, |
315 |
|
"expected conditional (%s) got \"%s\"", |
316 |
18 |
arg_operhelp[BAN_ARGIDX(pv->tag)], a2)); |
317 |
|
|
318 |
342 |
if (pv->flag & BANS_FLAG_DURATION) |
319 |
87 |
return (ban_add_duration(bp, pv, op, a3)); |
320 |
|
else |
321 |
255 |
return (ban_add_spec(bp, pv, op, a3)); |
322 |
387 |
} |
323 |
|
|
324 |
|
/*-------------------------------------------------------------------- |
325 |
|
* We maintain ban_start as a pointer to the first element of the list |
326 |
|
* as a separate variable from the VTAILQ, to avoid depending on the |
327 |
|
* internals of the VTAILQ macros. We tacitly assume that a pointer |
328 |
|
* write is always atomic in doing so. |
329 |
|
* |
330 |
|
* Returns: |
331 |
|
* 0: Ban successfully inserted |
332 |
|
* -1: Ban not inserted due to shutdown in progress. The ban has been |
333 |
|
* deleted. |
334 |
|
*/ |
335 |
|
|
336 |
|
const char * |
337 |
3083 |
BAN_Commit(struct ban_proto *bp) |
338 |
|
{ |
339 |
|
struct ban *b, *bi; |
340 |
|
ssize_t ln; |
341 |
|
vtim_real t0; |
342 |
|
uint64_t u; |
343 |
|
|
344 |
3083 |
CHECK_OBJ_NOTNULL(bp, BAN_PROTO_MAGIC); |
345 |
3083 |
AN(bp->vsb); |
346 |
3083 |
assert(sizeof u == sizeof t0); |
347 |
|
|
348 |
3083 |
if (ban_shutdown) |
349 |
0 |
return (ban_error(bp, "Shutting down")); |
350 |
|
|
351 |
3083 |
AZ(VSB_finish(bp->vsb)); |
352 |
3083 |
ln = VSB_len(bp->vsb); |
353 |
3083 |
assert(ln >= 0); |
354 |
|
|
355 |
3083 |
ALLOC_OBJ(b, BAN_MAGIC); |
356 |
3083 |
if (b == NULL) |
357 |
0 |
return (ban_error(bp, ban_build_err_no_mem)); |
358 |
3083 |
VTAILQ_INIT(&b->objcore); |
359 |
|
|
360 |
3083 |
b->spec = malloc(ln + BANS_HEAD_LEN); |
361 |
3083 |
if (b->spec == NULL) { |
362 |
0 |
free(b); |
363 |
0 |
return (ban_error(bp, ban_build_err_no_mem)); |
364 |
|
} |
365 |
|
|
366 |
3083 |
b->flags = bp->flags; |
367 |
|
|
368 |
3083 |
memset(b->spec, 0, BANS_HEAD_LEN); |
369 |
3083 |
t0 = VTIM_real(); |
370 |
3083 |
memcpy(&u, &t0, sizeof u); |
371 |
3083 |
vbe64enc(b->spec + BANS_TIMESTAMP, u); |
372 |
3083 |
b->spec[BANS_FLAGS] = b->flags & 0xff; |
373 |
3083 |
memcpy(b->spec + BANS_HEAD_LEN, VSB_data(bp->vsb), ln); |
374 |
3083 |
ln += BANS_HEAD_LEN; |
375 |
3083 |
vbe32enc(b->spec + BANS_LENGTH, ln); |
376 |
|
|
377 |
3083 |
Lck_Lock(&ban_mtx); |
378 |
3083 |
if (ban_shutdown) { |
379 |
|
/* We could have raced a shutdown */ |
380 |
0 |
Lck_Unlock(&ban_mtx); |
381 |
0 |
BAN_Free(b); |
382 |
0 |
return (ban_error(bp, "Shutting down")); |
383 |
|
} |
384 |
3083 |
bi = VTAILQ_FIRST(&ban_head); |
385 |
3083 |
VTAILQ_INSERT_HEAD(&ban_head, b, list); |
386 |
3083 |
ban_start = b; |
387 |
|
|
388 |
3083 |
VSC_C_main->bans++; |
389 |
3083 |
VSC_C_main->bans_added++; |
390 |
3083 |
bans_persisted_bytes += ln; |
391 |
3083 |
VSC_C_main->bans_persisted_bytes = bans_persisted_bytes; |
392 |
|
|
393 |
3083 |
if (b->flags & BANS_FLAG_OBJ) |
394 |
183 |
VSC_C_main->bans_obj++; |
395 |
3083 |
if (b->flags & BANS_FLAG_REQ) |
396 |
126 |
VSC_C_main->bans_req++; |
397 |
|
|
398 |
3083 |
if (bi != NULL) |
399 |
297 |
ban_info_new(b->spec, ln); /* Notify stevedores */ |
400 |
|
|
401 |
3083 |
if (cache_param->ban_dups) { |
402 |
|
/* Hunt down duplicates, and mark them as completed */ |
403 |
3824 |
for (bi = VTAILQ_NEXT(b, list); bi != NULL; |
404 |
741 |
bi = VTAILQ_NEXT(bi, list)) { |
405 |
741 |
if (!(bi->flags & BANS_FLAG_COMPLETED) && |
406 |
549 |
ban_equal(b->spec, bi->spec)) { |
407 |
39 |
ban_mark_completed(bi); |
408 |
39 |
VSC_C_main->bans_dups++; |
409 |
39 |
} |
410 |
741 |
} |
411 |
3083 |
} |
412 |
3083 |
if (!(b->flags & BANS_FLAG_REQ)) |
413 |
2957 |
ban_kick_lurker(); |
414 |
3083 |
Lck_Unlock(&ban_mtx); |
415 |
|
|
416 |
3083 |
BAN_Abandon(bp); |
417 |
3083 |
return (NULL); |
418 |
3083 |
} |
419 |
|
|
420 |
|
static void |
421 |
22288 |
ban_build_arg_operhelp(struct vsb *vsb, int arg) |
422 |
|
{ |
423 |
|
unsigned mask; |
424 |
22288 |
const char *p = NULL, *n = NULL; |
425 |
|
int i; |
426 |
|
|
427 |
22288 |
ASSERT_BAN_ARG(arg); |
428 |
22288 |
mask = arg_opervalid[BAN_ARGIDX(arg)]; |
429 |
|
|
430 |
200592 |
for (i = 0; i < BAN_OPERARRSZ; i++) { |
431 |
178304 |
if ((mask & (1U << i)) == 0) |
432 |
66864 |
continue; |
433 |
111440 |
if (p == NULL) |
434 |
22288 |
p = ban_oper[i]; |
435 |
89152 |
else if (n == NULL) |
436 |
22288 |
n = ban_oper[i]; |
437 |
|
else { |
438 |
66864 |
VSB_cat(vsb, p); |
439 |
66864 |
VSB_cat(vsb, ", "); |
440 |
66864 |
p = n; |
441 |
66864 |
n = ban_oper[i]; |
442 |
|
} |
443 |
111440 |
} |
444 |
|
|
445 |
22288 |
if (n) { |
446 |
22288 |
AN(p); |
447 |
22288 |
VSB_cat(vsb, p); |
448 |
22288 |
VSB_cat(vsb, " or "); |
449 |
22288 |
VSB_cat(vsb, n); |
450 |
22288 |
return; |
451 |
|
} |
452 |
|
|
453 |
0 |
AN(p); |
454 |
0 |
VSB_cat(vsb, p); |
455 |
22288 |
} |
456 |
|
|
457 |
|
void |
458 |
2786 |
BAN_Build_Init(void) { |
459 |
|
struct vsb *vsb; |
460 |
|
int i; |
461 |
|
|
462 |
2786 |
vsb = VSB_new_auto(); |
463 |
2786 |
AN(vsb); |
464 |
25074 |
for (i = BANS_ARG_OFF_; i < BANS_ARG_LIM; i ++) { |
465 |
22288 |
VSB_clear(vsb); |
466 |
22288 |
ban_build_arg_operhelp(vsb, i); |
467 |
22288 |
AZ(VSB_finish(vsb)); |
468 |
|
|
469 |
22288 |
arg_operhelp[BAN_ARGIDX(i)] = strdup(VSB_data(vsb)); |
470 |
22288 |
AN(arg_operhelp[BAN_ARGIDX(i)]); |
471 |
22288 |
} |
472 |
2786 |
arg_operhelp[BAN_ARGIDX(i)] = NULL; |
473 |
2786 |
VSB_destroy(&vsb); |
474 |
2786 |
} |
475 |
|
|
476 |
|
void |
477 |
2748 |
BAN_Build_Fini(void) { |
478 |
|
int i; |
479 |
|
|
480 |
24732 |
for (i = 0; i < BAN_ARGARRSZ; i++) |
481 |
21984 |
free(TRUST_ME(arg_operhelp[i])); |
482 |
2748 |
} |