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 675
BAN_Build(void)
74
{
75
        struct ban_proto *bp;
76
77 675
        ALLOC_OBJ(bp, BAN_PROTO_MAGIC);
78 675
        if (bp == NULL)
79 0
                return (bp);
80 675
        bp->vsb = VSB_new_auto();
81 675
        if (bp->vsb == NULL) {
82 0
                FREE_OBJ(bp);
83 0
                return (NULL);
84
        }
85 675
        return (bp);
86
}
87
88
void
89 675
BAN_Abandon(struct ban_proto *bp)
90
{
91
92 675
        CHECK_OBJ_NOTNULL(bp, BAN_PROTO_MAGIC);
93 675
        VSB_destroy(&bp->vsb);
94 675
        FREE_OBJ(bp);
95 675
}
96
97
/*--------------------------------------------------------------------
98
 */
99
100
static void
101 86
ban_add_lump(const struct ban_proto *bp, const void *p, uint32_t len)
102
{
103 86
        uint8_t buf[PRNDUP(sizeof len)] = { 0xff };
104
105 577
        while (VSB_len(bp->vsb) & PALGN)
106 405
                VSB_putc(bp->vsb, buf[0]);
107 86
        vbe32enc(buf, len);
108 86
        VSB_bcat(bp->vsb, buf, sizeof buf);
109 86
        VSB_bcat(bp->vsb, p, len);
110 86
}
111
112
/*--------------------------------------------------------------------
113
 */
114
115
static const char *
116 3
ban_error(struct ban_proto *bp, const char *fmt, ...)
117
{
118
        va_list ap;
119
120 3
        CHECK_OBJ_NOTNULL(bp, BAN_PROTO_MAGIC);
121 3
        AN(bp->vsb);
122
123
        /* First error is sticky */
124 3
        if (bp->err == NULL) {
125 3
                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 3
                        VSB_clear(bp->vsb);
130 3
                        va_start(ap, fmt);
131 3
                        VSB_vprintf(bp->vsb, fmt, ap);
132 3
                        va_end(ap);
133 3
                        AZ(VSB_finish(bp->vsb));
134 3
                        bp->err = VSB_data(bp->vsb);
135
                }
136
        }
137 3
        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 28
ban_parse_http(const struct ban_proto *bp, const char *a1)
147
{
148
        int l;
149
150 28
        l = strlen(a1) + 1;
151 28
        assert(l <= 127);
152 28
        VSB_putc(bp->vsb, (char)l);
153 28
        VSB_cat(bp->vsb, a1);
154 28
        VSB_putc(bp->vsb, ':');
155 28
        VSB_putc(bp->vsb, '\0');
156 28
}
157
158
/*--------------------------------------------------------------------
159
 * Parse and add a ban test specification
160
 */
161
162
static const char *
163 24
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 24
        re = pcre_compile(a3, 0, &error, &erroroffset, NULL);
171 24
        if (re == NULL)
172 1
                return (ban_error(bp, "Regex compile error: %s", error));
173 23
        rc = pcre_fullinfo(re, NULL, PCRE_INFO_SIZE, &sz);
174 23
        AZ(rc);
175 23
        ban_add_lump(bp, re, sz);
176 23
        pcre_free(re);
177 23
        return (0);
178
}
179
180
/*--------------------------------------------------------------------
181
 * Add a (and'ed) test-condition to a ban
182
 */
183
184
const char *
185 64
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 64
        CHECK_OBJ_NOTNULL(bp, BAN_PROTO_MAGIC);
192 64
        AN(bp->vsb);
193 64
        AN(a1);
194 64
        AN(a2);
195 64
        AN(a3);
196
197 64
        if (bp->err != NULL)
198 0
                return (bp->err);
199
200 148
        for (pv = pvars; pv->name != NULL; pv++)
201 147
                if (!strncmp(a1, pv->name, strlen(pv->name)))
202 63
                        break;
203
204 64
        if (pv->name == NULL)
205 1
                return (ban_error(bp,
206
                    "Unknown or unsupported field \"%s\"", a1));
207
208 63
        bp->flags |= pv->flag;
209
210 63
        VSB_putc(bp->vsb, pv->tag);
211 63
        if (pv->flag & BANS_FLAG_HTTP)
212 28
                ban_parse_http(bp, a1 + strlen(pv->name));
213
214 63
        ban_add_lump(bp, a3, strlen(a3) + 1);
215 63
        if (!strcmp(a2, "~")) {
216 24
                VSB_putc(bp->vsb, BANS_OPER_MATCH);
217 24
                err = ban_parse_regexp(bp, a3);
218 24
                if (err)
219 1
                        return (err);
220 39
        } 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 39
        } else if (!strcmp(a2, "==")) {
226 34
                VSB_putc(bp->vsb, BANS_OPER_EQ);
227 5
        } else if (!strcmp(a2, "!=")) {
228 4
                VSB_putc(bp->vsb, BANS_OPER_NEQ);
229
        } else {
230 1
                return (ban_error(bp,
231
                    "expected conditional (~, !~, == or !=) got \"%s\"", a2));
232
        }
233 61
        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 668
BAN_Commit(struct ban_proto *bp)
250
{
251
        struct ban  *b, *bi;
252
        ssize_t ln;
253
        double t0;
254
255 668
        CHECK_OBJ_NOTNULL(bp, BAN_PROTO_MAGIC);
256 668
        AN(bp->vsb);
257
258 668
        if (ban_shutdown)
259 0
                return (ban_error(bp, "Shutting down"));
260
261 668
        AZ(VSB_finish(bp->vsb));
262 668
        ln = VSB_len(bp->vsb);
263 668
        assert(ln >= 0);
264
265 668
        ALLOC_OBJ(b, BAN_MAGIC);
266 668
        if (b == NULL)
267 0
                return (ban_error(bp, ban_build_err_no_mem));
268 668
        VTAILQ_INIT(&b->objcore);
269
270 668
        b->spec = malloc(ln + BANS_HEAD_LEN);
271 668
        if (b->spec == NULL) {
272 0
                free(b);
273 0
                return (ban_error(bp, ban_build_err_no_mem));
274
        }
275
276 668
        b->flags = bp->flags;
277
278 668
        memset(b->spec, 0, BANS_HEAD_LEN);
279 668
        t0 = VTIM_real();
280 668
        memcpy(b->spec + BANS_TIMESTAMP, &t0, sizeof t0);
281 668
        b->spec[BANS_FLAGS] = b->flags & 0xff;
282 668
        memcpy(b->spec + BANS_HEAD_LEN, VSB_data(bp->vsb), ln);
283 668
        ln += BANS_HEAD_LEN;
284 668
        vbe32enc(b->spec + BANS_LENGTH, ln);
285
286 668
        Lck_Lock(&ban_mtx);
287 668
        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 668
        bi = VTAILQ_FIRST(&ban_head);
294 668
        VTAILQ_INSERT_HEAD(&ban_head, b, list);
295 668
        ban_start = b;
296
297 668
        VSC_C_main->bans++;
298 668
        VSC_C_main->bans_added++;
299 668
        bans_persisted_bytes += ln;
300 668
        VSC_C_main->bans_persisted_bytes = bans_persisted_bytes;
301
302 668
        if (b->flags & BANS_FLAG_OBJ)
303 25
                VSC_C_main->bans_obj++;
304 668
        if (b->flags & BANS_FLAG_REQ)
305 33
                VSC_C_main->bans_req++;
306
307 668
        if (bi != NULL)
308 54
                ban_info_new(b->spec, ln);      /* Notify stevedores */
309
310 668
        if (cache_param->ban_dups) {
311
                /* Hunt down duplicates, and mark them as completed */
312 1431
                for (bi = VTAILQ_NEXT(b, list); bi != NULL;
313 95
                    bi = VTAILQ_NEXT(bi, list)) {
314 146
                        if (!(bi->flags & BANS_FLAG_COMPLETED) &&
315 51
                            ban_equal(b->spec, bi->spec)) {
316 10
                                ban_mark_completed(bi);
317 10
                                VSC_C_main->bans_dups++;
318
                        }
319
                }
320
        }
321 668
        if (!(b->flags & BANS_FLAG_REQ))
322 635
                ban_kick_lurker();
323 668
        Lck_Unlock(&ban_mtx);
324
325 668
        BAN_Abandon(bp);
326 668
        return (NULL);
327
}