varnish-cache/bin/varnishd/cache/cache_ban_build.c
1
/*-
2
 * Copyright (c) 2006 Verdens Gang AS
3
 * Copyright (c) 2006-2015 Varnish Software AS
4
 * All rights reserved.
5
 *
6
 * Author: Poul-Henning Kamp <phk@phk.freebsd.dk>
7
 *
8
 * Redistribution and use in source and binary forms, with or without
9
 * modification, are permitted provided that the following conditions
10
 * are met:
11
 * 1. Redistributions of source code must retain the above copyright
12
 *    notice, this list of conditions and the following disclaimer.
13
 * 2. Redistributions in binary form must reproduce the above copyright
14
 *    notice, this list of conditions and the following disclaimer in the
15
 *    documentation and/or other materials provided with the distribution.
16
 *
17
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
21
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27
 * SUCH DAMAGE.
28
 *
29
 */
30
31
#include "config.h"
32
33
#include <pcre.h>
34
35
#include "cache_varnishd.h"
36
#include "cache_ban.h"
37
38
#include "vend.h"
39
#include "vtim.h"
40
41
struct ban_proto {
42
        unsigned                magic;
43
#define BAN_PROTO_MAGIC         0xd8adc494
44
        unsigned                flags;          /* BANS_FLAG_* */
45
46
        struct vsb              *vsb;
47
        char                    *err;
48
};
49
50
/*--------------------------------------------------------------------
51
 * Variables we can ban on
52
 */
53
54
static const struct pvar {
55
        const char              *name;
56
        unsigned                flag;
57
        uint8_t                 tag;
58
} pvars[] = {
59
#define PVAR(a, b, c)   { (a), (b), (c) },
60
#include "tbl/ban_vars.h"
61
        { 0, 0, 0}
62
};
63
64
/*--------------------------------------------------------------------
65
 */
66
67
static char ban_build_err_no_mem[] = "No Memory";
68
69
/*--------------------------------------------------------------------
70
 */
71
72
struct ban_proto *
73 1502
BAN_Build(void)
74
{
75
        struct ban_proto *bp;
76
77 1502
        ALLOC_OBJ(bp, BAN_PROTO_MAGIC);
78 1502
        if (bp == NULL)
79 0
                return (bp);
80 1502
        bp->vsb = VSB_new_auto();
81 1502
        if (bp->vsb == NULL) {
82 0
                FREE_OBJ(bp);
83 0
                return (NULL);
84
        }
85 1502
        return (bp);
86
}
87
88
void
89 1502
BAN_Abandon(struct ban_proto *bp)
90
{
91
92 1502
        CHECK_OBJ_NOTNULL(bp, BAN_PROTO_MAGIC);
93 1502
        VSB_destroy(&bp->vsb);
94 1502
        FREE_OBJ(bp);
95 1502
}
96
97
/*--------------------------------------------------------------------
98
 */
99
100
static void
101 180
ban_add_lump(const struct ban_proto *bp, const void *p, uint32_t len)
102
{
103 180
        uint8_t buf[PRNDUP(sizeof len)] = { 0xff };
104
105 1192
        while (VSB_len(bp->vsb) & PALGN)
106 832
                VSB_putc(bp->vsb, buf[0]);
107 180
        vbe32enc(buf, len);
108 180
        VSB_bcat(bp->vsb, buf, sizeof buf);
109 180
        VSB_bcat(bp->vsb, p, len);
110 180
}
111
112
/*--------------------------------------------------------------------
113
 */
114
115
static const char *
116 6
ban_error(struct ban_proto *bp, const char *fmt, ...)
117
{
118
        va_list ap;
119
120 6
        CHECK_OBJ_NOTNULL(bp, BAN_PROTO_MAGIC);
121 6
        AN(bp->vsb);
122
123
        /* First error is sticky */
124 6
        if (bp->err == NULL) {
125 6
                if (fmt == ban_build_err_no_mem) {
126 0
                        bp->err = ban_build_err_no_mem;
127
                } else {
128
                        /* Record the error message in the vsb */
129 6
                        VSB_clear(bp->vsb);
130 6
                        va_start(ap, fmt);
131 6
                        VSB_vprintf(bp->vsb, fmt, ap);
132 6
                        va_end(ap);
133 6
                        AZ(VSB_finish(bp->vsb));
134 6
                        bp->err = VSB_data(bp->vsb);
135
                }
136
        }
137 6
        return (bp->err);
138
}
139
140
/*--------------------------------------------------------------------
141
 * Parse and add a http argument specification
142
 * Output something which HTTP_GetHdr understands
143
 */
144
145
static void
146 58
ban_parse_http(const struct ban_proto *bp, const char *a1)
147
{
148
        int l;
149
150 58
        l = strlen(a1) + 1;
151 58
        assert(l <= 127);
152 58
        VSB_putc(bp->vsb, (char)l);
153 58
        VSB_cat(bp->vsb, a1);
154 58
        VSB_putc(bp->vsb, ':');
155 58
        VSB_putc(bp->vsb, '\0');
156 58
}
157
158
/*--------------------------------------------------------------------
159
 * Parse and add a ban test specification
160
 */
161
162
static const char *
163 52
ban_parse_regexp(struct ban_proto *bp, const char *a3)
164
{
165
        const char *error;
166
        int erroroffset, rc;
167
        size_t sz;
168
        pcre *re;
169
170 52
        re = pcre_compile(a3, 0, &error, &erroroffset, NULL);
171 52
        if (re == NULL)
172 2
                return (ban_error(bp, "Regex compile error: %s", error));
173 50
        rc = pcre_fullinfo(re, NULL, PCRE_INFO_SIZE, &sz);
174 50
        AZ(rc);
175 50
        ban_add_lump(bp, re, sz);
176 50
        pcre_free(re);
177 50
        return (0);
178
}
179
180
/*--------------------------------------------------------------------
181
 * Add a (and'ed) test-condition to a ban
182
 */
183
184
const char *
185 132
BAN_AddTest(struct ban_proto *bp,
186
    const char *a1, const char *a2, const char *a3)
187
{
188
        const struct pvar *pv;
189
        const char *err;
190
191 132
        CHECK_OBJ_NOTNULL(bp, BAN_PROTO_MAGIC);
192 132
        AN(bp->vsb);
193 132
        AN(a1);
194 132
        AN(a2);
195 132
        AN(a3);
196
197 132
        if (bp->err != NULL)
198 0
                return (bp->err);
199
200 306
        for (pv = pvars; pv->name != NULL; pv++)
201 304
                if (!strncmp(a1, pv->name, strlen(pv->name)))
202 130
                        break;
203
204 132
        if (pv->name == NULL)
205 2
                return (ban_error(bp,
206
                    "Unknown or unsupported field \"%s\"", a1));
207
208 130
        bp->flags |= pv->flag;
209
210 130
        VSB_putc(bp->vsb, pv->tag);
211 130
        if (pv->flag & BANS_FLAG_HTTP)
212 58
                ban_parse_http(bp, a1 + strlen(pv->name));
213
214 130
        ban_add_lump(bp, a3, strlen(a3) + 1);
215 130
        if (!strcmp(a2, "~")) {
216 52
                VSB_putc(bp->vsb, BANS_OPER_MATCH);
217 52
                err = ban_parse_regexp(bp, a3);
218 52
                if (err)
219 2
                        return (err);
220 78
        } else if (!strcmp(a2, "!~")) {
221 0
                VSB_putc(bp->vsb, BANS_OPER_NMATCH);
222 0
                err = ban_parse_regexp(bp, a3);
223 0
                if (err)
224 0
                        return (err);
225 78
        } else if (!strcmp(a2, "==")) {
226 68
                VSB_putc(bp->vsb, BANS_OPER_EQ);
227 10
        } else if (!strcmp(a2, "!=")) {
228 8
                VSB_putc(bp->vsb, BANS_OPER_NEQ);
229
        } else {
230 2
                return (ban_error(bp,
231
                    "expected conditional (~, !~, == or !=) got \"%s\"", a2));
232
        }
233 126
        return (NULL);
234
}
235
236
/*--------------------------------------------------------------------
237
 * We maintain ban_start as a pointer to the first element of the list
238
 * as a separate variable from the VTAILQ, to avoid depending on the
239
 * internals of the VTAILQ macros.  We tacitly assume that a pointer
240
 * write is always atomic in doing so.
241
 *
242
 * Returns:
243
 *   0: Ban successfully inserted
244
 *  -1: Ban not inserted due to shutdown in progress. The ban has been
245
 *      deleted.
246
 */
247
248
const char *
249 1488
BAN_Commit(struct ban_proto *bp)
250
{
251
        struct ban  *b, *bi;
252
        ssize_t ln;
253
        double t0;
254
255 1488
        CHECK_OBJ_NOTNULL(bp, BAN_PROTO_MAGIC);
256 1488
        AN(bp->vsb);
257
258 1488
        if (ban_shutdown)
259 0
                return (ban_error(bp, "Shutting down"));
260
261 1488
        AZ(VSB_finish(bp->vsb));
262 1488
        ln = VSB_len(bp->vsb);
263 1488
        assert(ln >= 0);
264
265 1488
        ALLOC_OBJ(b, BAN_MAGIC);
266 1488
        if (b == NULL)
267 0
                return (ban_error(bp, ban_build_err_no_mem));
268 1488
        VTAILQ_INIT(&b->objcore);
269
270 1488
        b->spec = malloc(ln + BANS_HEAD_LEN);
271 1488
        if (b->spec == NULL) {
272 0
                free(b);
273 0
                return (ban_error(bp, ban_build_err_no_mem));
274
        }
275
276 1488
        b->flags = bp->flags;
277
278 1488
        memset(b->spec, 0, BANS_HEAD_LEN);
279 1488
        t0 = VTIM_real();
280 1488
        memcpy(b->spec + BANS_TIMESTAMP, &t0, sizeof t0);
281 1488
        b->spec[BANS_FLAGS] = b->flags & 0xff;
282 1488
        memcpy(b->spec + BANS_HEAD_LEN, VSB_data(bp->vsb), ln);
283 1488
        ln += BANS_HEAD_LEN;
284 1488
        vbe32enc(b->spec + BANS_LENGTH, ln);
285
286 1488
        Lck_Lock(&ban_mtx);
287 1488
        if (ban_shutdown) {
288
                /* We could have raced a shutdown */
289 0
                Lck_Unlock(&ban_mtx);
290 0
                BAN_Free(b);
291 0
                return (ban_error(bp, "Shutting down"));
292
        }
293 1488
        bi = VTAILQ_FIRST(&ban_head);
294 1488
        VTAILQ_INSERT_HEAD(&ban_head, b, list);
295 1488
        ban_start = b;
296
297 1488
        VSC_C_main->bans++;
298 1488
        VSC_C_main->bans_added++;
299 1488
        bans_persisted_bytes += ln;
300
        /*
301
         * XXX absolute update of gauges - may be inaccurate for Pool_Sumstat
302
         * race
303
         */
304 1488
        VSC_C_main->bans_persisted_bytes = bans_persisted_bytes;
305
306 1488
        if (b->flags & BANS_FLAG_OBJ)
307 52
                VSC_C_main->bans_obj++;
308 1488
        if (b->flags & BANS_FLAG_REQ)
309 68
                VSC_C_main->bans_req++;
310
311 1488
        if (bi != NULL)
312 112
                ban_info_new(b->spec, ln);      /* Notify stevedores */
313
314 1488
        if (cache_param->ban_dups) {
315
                /* Hunt down duplicates, and mark them as completed */
316 3186
                for (bi = VTAILQ_NEXT(b, list); bi != NULL;
317 210
                    bi = VTAILQ_NEXT(bi, list)) {
318 322
                        if (!(bi->flags & BANS_FLAG_COMPLETED) &&
319 112
                            ban_equal(b->spec, bi->spec)) {
320 20
                                ban_mark_completed(bi);
321 20
                                VSC_C_main->bans_dups++;
322
                        }
323
                }
324
        }
325 1488
        if (!(b->flags & BANS_FLAG_REQ))
326 1420
                ban_kick_lurker();
327 1488
        Lck_Unlock(&ban_mtx);
328
329 1488
        BAN_Abandon(bp);
330 1488
        return (NULL);
331
}