varnish-cache/vmod/vmod_std.c
1
/*-
2
 * Copyright (c) 2010-2017 Varnish Software AS
3
 * All rights reserved.
4
 *
5
 * Author: Poul-Henning Kamp <phk@FreeBSD.org>
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
#include "config.h"
32
33
#include <sys/stat.h>
34
35
#include <netinet/in.h>
36
37
#include <ctype.h>
38
#include <stdlib.h>
39
#include <string.h>
40
#include <syslog.h>
41
#include <sys/socket.h>
42
#include <fnmatch.h>
43
44
#include "cache/cache.h"
45
46
#include "vrnd.h"
47
#include "vtcp.h"
48
#include "vsa.h"
49
#include "vtim.h"
50
#include "vcl.h"
51
52
#include "vcc_std_if.h"
53
54
VCL_VOID v_matchproto_(td_std_set_ip_tos)
55 6
vmod_set_ip_tos(VRT_CTX, VCL_INT tos)
56
{
57
        struct suckaddr *sa;
58 6
        int fam, itos = tos;
59
60 6
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
61 6
        AZ(SES_Get_local_addr(ctx->req->sp, &sa));
62
        /* Silently ignore for non-IP addresses. */
63 6
        if (VSA_Compare(sa, bogo_ip) == 0)
64 3
                return;
65 3
        fam = VSA_Get_Proto(sa);
66 3
        switch (fam) {
67
        case PF_INET:
68 3
                VTCP_Assert(setsockopt(ctx->req->sp->fd,
69
                    IPPROTO_IP, IP_TOS, &itos, sizeof(itos)));
70 3
                break;
71
        case PF_INET6:
72 0
                VTCP_Assert(setsockopt(ctx->req->sp->fd,
73
                    IPPROTO_IPV6, IPV6_TCLASS, &itos, sizeof(itos)));
74 0
                break;
75
        default:
76 0
                INCOMPL();
77 0
        }
78 6
}
79
80
static const char *
81 36
vmod_updown(VRT_CTX, int up, VCL_STRANDS s)
82
{
83
        unsigned u;
84
        char *b, *e;
85
        const char *p;
86
        int i;
87
88 36
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
89 36
        u = WS_ReserveAll(ctx->ws);
90 36
        e = b = WS_Reservation(ctx->ws);
91 36
        e += u;
92 72
        for (i = 0; i < s->n && b < e; i++) {
93 36
                p = s->p[i];
94 6582
                while (p != NULL && *p != '\0' && b < e) {
95 6546
                        if (up)
96 3246
                                *b++ = (char)toupper(*p++);
97
                        else
98 3300
                                *b++ = (char)tolower(*p++);
99
                }
100 36
        }
101 36
        if (b < e)
102 36
                *b = '\0';
103 36
        b++;
104 36
        if (b > e) {
105 0
                WS_MarkOverflow(ctx->ws);
106 0
                WS_Release(ctx->ws, 0);
107 0
                return (NULL);
108
        } else {
109 36
                e = b;
110 36
                b = WS_Reservation(ctx->ws);
111 36
                WS_Release(ctx->ws, e - b);
112 36
                return (b);
113
        }
114 36
}
115
116
VCL_STRING v_matchproto_(td_std_toupper)
117 12
vmod_toupper(VRT_CTX, VCL_STRANDS s)
118
{
119
120 12
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
121 12
        return (vmod_updown(ctx, 1, s));
122
}
123
124
VCL_STRING v_matchproto_(td_std_tolower)
125 24
vmod_tolower(VRT_CTX, VCL_STRANDS s)
126
{
127
128 24
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
129 24
        return (vmod_updown(ctx, 0, s));
130
}
131
132
VCL_REAL v_matchproto_(td_std_random)
133 12
vmod_random(VRT_CTX, VCL_REAL lo, VCL_REAL hi)
134
{
135
        double a;
136
137 12
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
138 12
        a = VRND_RandomTestableDouble();
139 12
        a *= hi - lo;
140 12
        a += lo;
141 12
        return (a);
142
}
143
144
VCL_VOID v_matchproto_(td_std_log)
145 438
vmod_log(VRT_CTX, VCL_STRANDS s)
146
{
147 438
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
148
149 438
        if (ctx->vsl != NULL)
150 330
                VSLbs(ctx->vsl, SLT_VCL_Log, s);
151
        else
152 108
                VSLs(SLT_VCL_Log, 0, s);
153 438
}
154
155
/* XXX use vsyslog() ? */
156
VCL_VOID v_matchproto_(td_std_syslog)
157 15
vmod_syslog(VRT_CTX, VCL_INT fac, VCL_STRANDS s)
158
{
159
        const char *p;
160
        uintptr_t sn;
161
162 15
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
163 15
        sn = WS_Snapshot(ctx->ws);
164 15
        p = VRT_StrandsWS(ctx->ws, NULL, s);
165 15
        if (p != NULL)
166 12
                syslog((int)fac, "%s", p);
167 15
        WS_Reset(ctx->ws, sn);
168 15
}
169
170
VCL_BOOL v_matchproto_(td_std_file_exists)
171 6
vmod_file_exists(VRT_CTX, VCL_STRING file_name)
172
{
173
        struct stat st;
174
175 6
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
176 6
        return (stat(file_name, &st) == 0);
177
}
178
179
VCL_VOID v_matchproto_(td_std_collect)
180 39
vmod_collect(VRT_CTX, VCL_HEADER hdr, VCL_STRING sep)
181
{
182
        struct http *hp;
183
184 39
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
185 39
        if (hdr == NULL) {
186 0
                VRT_fail(ctx, "std.collect(): header argument is NULL");
187 0
                return;
188
        }
189 39
        hp = VRT_selecthttp(ctx, hdr->where);
190 39
        if (hp == NULL) {
191 3
                VRT_fail(ctx, "std.collect(): header argument "
192
                    "can not be used here");
193 3
                return;
194
        }
195 36
        http_CollectHdrSep(hp, hdr->what, sep);
196 39
}
197
198
VCL_BOOL v_matchproto_(td_std_healthy)
199 111
vmod_healthy(VRT_CTX, VCL_BACKEND be)
200
{
201 111
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
202 111
        CHECK_OBJ_ORNULL(be, DIRECTOR_MAGIC);
203 111
        return (VRT_Healthy(ctx, be, NULL));
204
}
205
206
VCL_INT v_matchproto_(td_std_port)
207 225
vmod_port(VRT_CTX, VCL_IP ip)
208
{
209 225
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
210 225
        if (ip == NULL)
211 0
                return (0);
212 225
        return (VSA_Port(ip));
213 225
}
214
215
VCL_VOID v_matchproto_(td_std_rollback)
216 66
vmod_rollback(VRT_CTX, VCL_HTTP hp)
217
{
218 66
        VRT_Rollback(ctx, hp);
219 66
}
220
221
VCL_VOID v_matchproto_(td_std_timestamp)
222 63
vmod_timestamp(VRT_CTX, VCL_STRING label)
223
{
224
225 63
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
226 63
        if (label == NULL)
227 0
                return;
228 63
        if (*label == '\0')
229 0
                return;
230 63
        if (ctx->bo != NULL && ctx->req == NULL) {
231
                /* Called from backend vcl methods */
232 3
                CHECK_OBJ_NOTNULL(ctx->bo, BUSYOBJ_MAGIC);
233 3
                VSLb_ts_busyobj(ctx->bo, label, VTIM_real());
234 63
        } else if (ctx->req != NULL) {
235
                /* Called from request vcl methods */
236 57
                CHECK_OBJ(ctx->req, REQ_MAGIC);
237 57
                VSLb_ts_req(ctx->req, label, VTIM_real());
238 57
        }
239 63
}
240
241
VCL_BOOL v_matchproto_(td_std_cache_req_body)
242 48
vmod_cache_req_body(VRT_CTX, VCL_BYTES size)
243
{
244 48
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
245 48
        size = vmax_t(VCL_BYTES, size, 0);
246 48
        if (VRT_CacheReqBody(ctx, (size_t)size) < 0)
247 12
                return (0);
248 36
        return (1);
249 48
}
250
251
VCL_STRING v_matchproto_(td_std_strstr)
252 9
vmod_strstr(VRT_CTX, VCL_STRING s1, VCL_STRING s2)
253
{
254 9
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
255 9
        if (s1 == NULL || s2 == NULL)
256 3
                return (NULL);
257 6
        return (strstr(s1, s2));
258 9
}
259
260
VCL_STRING v_matchproto_(td_std_getenv)
261 9
vmod_getenv(VRT_CTX, VCL_STRING name)
262
{
263 9
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
264 9
        if (name == NULL || *name == '\0')
265 3
                return (NULL);
266 6
        return (getenv(name));
267 9
}
268
269
VCL_VOID v_matchproto_(td_std_late_100_continue)
270 15
vmod_late_100_continue(VRT_CTX, VCL_BOOL late)
271
{
272 15
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
273 15
        if (ctx->method != VCL_MET_RECV) {
274 0
                VSLb(ctx->vsl, SLT_VCL_Error,
275
                    "std.late_100_continue() only valid in vcl_recv{}");
276 0
                return;
277
        }
278
279 15
        CHECK_OBJ_NOTNULL(ctx->req, REQ_MAGIC);
280 15
        if (ctx->req->want100cont)
281 15
                ctx->req->late100cont = late;
282 15
}
283
284
VCL_BOOL v_matchproto_(td_std_syntax)
285 18
vmod_syntax(VRT_CTX, VCL_REAL r)
286
{
287
288 18
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
289 18
        assert(ctx->syntax == 40 || ctx->syntax == 41);
290
        /*
291
         * We need to be careful because non-integer numbers have imprecise
292
         * IEE754 representation (4.1 is 0x1.0666666666666p+2 = 4.09999...)
293
         * By scaling up and rounding, this is taken care of.
294
         */
295 18
        return (round(r * 10) <= ctx->syntax);
296
}
297
298
VCL_BOOL v_matchproto_(td_std_fnmatch)
299 186
vmod_fnmatch(VRT_CTX, VCL_STRING pattern, VCL_STRING subject,
300
             VCL_BOOL pathname, VCL_BOOL noescape, VCL_BOOL period)
301
{
302 186
        int flags = 0;
303
304 186
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
305 186
        if (pattern == NULL) {
306 3
                VRT_fail(ctx, "std.fnmatch(): pattern is NULL");
307 3
                return (0);
308
        }
309 183
        if (subject == NULL) {
310 3
                VRT_fail(ctx, "std.fnmatch(): subject is NULL");
311 3
                return (0);
312
        }
313
314 180
        if (pathname)
315 135
                flags |= FNM_PATHNAME;
316 180
        if (noescape)
317 45
                flags |= FNM_NOESCAPE;
318 180
        if (period)
319 45
                flags |= FNM_PERIOD;
320 180
        return (fnmatch(pattern, subject, flags) != FNM_NOMATCH);
321 186
}
322
323
static const void * const priv_task_id_ban = &priv_task_id_ban;
324
325
VCL_BOOL v_matchproto_(td_std_ban)
326 66
vmod_ban(VRT_CTX, VCL_STRING s)
327
{
328
        struct vmod_priv *priv_task;
329
        VCL_STRING r;
330
331 66
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
332
333 66
        r = VRT_ban_string(ctx, s);
334 66
        priv_task = VRT_priv_task_get(ctx, priv_task_id_ban);
335
336 66
        if (r == NULL && priv_task == NULL)
337 30
                return (1);
338
339 36
        if (priv_task == NULL)
340 6
                priv_task = VRT_priv_task(ctx, priv_task_id_ban);
341
342 36
        if (priv_task == NULL) {
343 0
                VRT_fail(ctx, "std.ban(): no priv_task (out of workspace?)");
344 0
                return (0);
345
        }
346
347
        /*
348
         * TRUST_ME: the ban error is const. We save it in the un-const priv
349
         * pointer, but promise to only ever return it as a (const) VCL_STRING
350
         */
351 36
        priv_task->priv = TRUST_ME(r);
352
353 36
        return (r == NULL);
354 66
}
355
356
VCL_STRING v_matchproto_(td_std_ban_error)
357 36
vmod_ban_error(VRT_CTX)
358
{
359
        struct vmod_priv *priv_task;
360
        VCL_STRING r;
361
362 36
        CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC);
363
364 36
        priv_task = VRT_priv_task_get(ctx, priv_task_id_ban);
365 36
        if (priv_task == NULL)
366 0
                return ("");
367
368 36
        r = priv_task->priv;
369 36
        if (r == NULL)
370 3
                r = "";
371 36
        return (r);
372 36
}