source: lib/libvcl/vcc_acl.c @ 35d8196

Revision 35d8196, 11.9 KB checked in by Poul-Henning Kamp <phk@…>, 4 years ago (diff)

Make vcc_UintVal() behave like the other ..Val() functions and
consume its token.

git-svn-id:  http://www.varnish-cache.org/svn/trunk/varnish-cache@4656 d4fa192b-c00b-0410-8231-f00ffab90ce4

  • Property mode set to 100644
Line 
1/*-
2 * Copyright (c) 2006 Verdens Gang AS
3 * Copyright (c) 2006-2009 Linpro 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#include "config.h"
31
32#include "svnid.h"
33SVNID("$Id$")
34
35#include <stdio.h>
36#include <stdlib.h>
37#include <stddef.h>
38#include <string.h>
39#include <sys/types.h>
40#include <sys/socket.h>
41#include <netdb.h>
42#include <netinet/in.h>
43
44#include "vsb.h"
45#include "vrt.h"
46
47#include "vcc_priv.h"
48#include "vcc_compile.h"
49#include "libvarnish.h"
50
51struct acl_e {
52        VTAILQ_ENTRY(acl_e)     list;
53        unsigned char           data[VRT_ACL_MAXADDR + 1];
54        unsigned                mask;
55        unsigned                not;
56        unsigned                para;
57        struct token            *t_addr;
58        struct token            *t_mask;
59};
60
61/* Compare two acl rules for ordering */
62
63#define CMP(a, b)                                                       \
64        do {                                                            \
65                if ((a) < (b))                                          \
66                        return (-1);                                    \
67                else if ((b) < (a))                                     \
68                        return (1);                                     \
69        } while (0)
70
71static int
72vcl_acl_cmp(struct acl_e *ae1, struct acl_e *ae2)
73{
74        unsigned char *p1, *p2;
75        unsigned m;
76
77        p1 = ae1->data;
78        p2 = ae2->data;
79        m = ae1->mask;
80        if (ae2->mask < m)
81                m = ae2->mask;
82        for (; m >= 8; m -= 8) {
83                CMP(*p1, *p2);
84                p1++;
85                p2++;
86        }
87        if (m) {
88                m = 0xff00 >> m;
89                m &= 0xff;
90                CMP(*p1 & m, *p2 & m);
91        }
92        /* Long mask is less than short mask */
93        CMP(ae2->mask, ae1->mask);
94
95        return (0);
96}
97
98
99static void
100vcc_acl_add_entry(struct tokenlist *tl, const struct acl_e *ae, int l,
101    const unsigned char *u, int fam)
102{
103        struct acl_e *ae2, *aen;
104        int i;
105
106        if (fam == PF_INET && ae->mask > 32) {
107                vsb_printf(tl->sb,
108                    "Too wide mask (%u) for IPv4 address", ae->mask);
109                vcc_ErrWhere(tl, ae->t_mask);
110                return;
111        }
112        if (fam == PF_INET6 && ae->mask > 128) {
113                vsb_printf(tl->sb,
114                    "Too wide mask (%u) for IPv6 address", ae->mask);
115                vcc_ErrWhere(tl, ae->t_mask);
116                return;
117        }
118
119        /* Make a copy from the template */
120        aen = TlAlloc(tl, sizeof *ae2);
121        AN(aen);
122        *aen = *ae;
123
124        /* We treat family as part of address, it saves code */
125        assert(fam <= 0xff);
126        aen->data[0] = fam & 0xff;
127        aen->mask += 8;
128
129        memcpy(aen->data + 1, u, l);
130
131        VTAILQ_FOREACH(ae2, &tl->acl, list) {
132                i = vcl_acl_cmp(aen, ae2);
133                if (i == 0) {
134                        /*
135                         * If the two rules agree, silently ignore it
136                         * XXX: is that counter intuitive ?
137                         */
138                        if (aen->not == ae2->not)
139                                return;
140                        vsb_printf(tl->sb, "Conflicting ACL entries:\n");
141                        vcc_ErrWhere(tl, ae2->t_addr);
142                        vsb_printf(tl->sb, "vs:\n");
143                        vcc_ErrWhere(tl, aen->t_addr);
144                        return;
145                }
146                /*
147                 * We could eliminate pointless rules here, for instance in:
148                 *      "10.1.0.1";
149                 *      "10.1";
150                 * The first rule is clearly pointless, as the second one
151                 * covers it.
152                 *
153                 * We do not do this however, because the shmlog may
154                 * be used to gather statistics.
155                 */
156                if (i < 0) {
157                        VTAILQ_INSERT_BEFORE(ae2, aen, list);
158                        return;
159                }
160        }
161        VTAILQ_INSERT_TAIL(&tl->acl, aen, list);
162}
163
164static void
165vcc_acl_try_getaddrinfo(struct tokenlist *tl, struct acl_e *ae)
166{
167        struct addrinfo *res0, *res, hint;
168        struct sockaddr_in *sin4;
169        struct sockaddr_in6 *sin6;
170        unsigned char *u, i4, i6;
171        int error;
172
173        memset(&hint, 0, sizeof hint);
174        hint.ai_family = PF_UNSPEC;
175        hint.ai_socktype = SOCK_STREAM;
176        error = getaddrinfo(ae->t_addr->dec, "0", &hint, &res0);
177        if (error) {
178                if (ae->para) {
179                        vsb_printf(tl->sb,
180                            "Warning: %s ignored\n  -- %s\n",
181                            ae->t_addr->dec, gai_strerror(error));
182                        Fh(tl, 1, "/* Ignored ACL entry: %s%s",
183                            ae->para ? "\"(\" " : "", ae->not ? "\"!\" " : "");
184                        EncToken(tl->fh, ae->t_addr);
185                        if (ae->t_mask)
186                                Fh(tl, 0, "/%u", ae->mask);
187                        Fh(tl, 0, "%s\n", ae->para ? " \")\"" : "");
188                        Fh(tl, 1, " * getaddrinfo:  %s */\n",
189                             gai_strerror(error));
190                } else {
191                        vsb_printf(tl->sb,
192                            "DNS lookup(%s): %s\n",
193                            ae->t_addr->dec, gai_strerror(error));
194                        vcc_ErrWhere(tl, ae->t_addr);
195                }
196                return;
197        }
198
199        i4 = i6 = 0;
200        for(res = res0; res != NULL; res = res->ai_next) {
201                switch(res->ai_family) {
202                case PF_INET:
203                        assert(PF_INET < 256);
204                        sin4 = (void*)res->ai_addr;
205                        assert(sizeof(sin4->sin_addr) == 4);
206                        u = (void*)&sin4->sin_addr;
207                        if (ae->t_mask == NULL)
208                                ae->mask = 32;
209                        i4++;
210                        vcc_acl_add_entry(tl, ae, 4, u, res->ai_family);
211                        break;
212                case PF_INET6:
213                        assert(PF_INET6 < 256);
214                        sin6 = (void*)res->ai_addr;
215                        assert(sizeof(sin6->sin6_addr) == 16);
216                        u = (void*)&sin6->sin6_addr;
217                        if (ae->t_mask == NULL)
218                                ae->mask = 128;
219                        i6++;
220                        vcc_acl_add_entry(tl, ae, 16, u, res->ai_family);
221                        break;
222                default:
223                        vsb_printf(tl->sb,
224                            "Ignoring unknown protocol family (%d) for %.*s\n",
225                                res->ai_family, PF(ae->t_addr));
226                        continue;
227                }
228                ERRCHK(tl);
229        }
230        freeaddrinfo(res0);
231
232        if (ae->t_mask != NULL && i4 > 0 && i6 > 0) {
233                vsb_printf(tl->sb,
234                    "Mask (%u) specified, but string resolves to"
235                    " both IPv4 and IPv6 addresses.\n", ae->mask);
236                vcc_ErrWhere(tl, ae->t_mask);
237                return;
238        }
239}
240
241/*--------------------------------------------------------------------
242 * Ancient stupidity on the part of X/Open and other standards orgs
243 * dictate that "192.168" be translated to 192.0.0.168.  Ever since
244 * CIDR happened, "192.168/16" notation has been used, but appearantly
245 * no API supports parsing this, so roll our own.
246 */
247
248static int
249vcc_acl_try_netnotation(struct tokenlist *tl, struct acl_e *ae)
250{
251        unsigned char b[4];
252        int i, j, k;
253        unsigned u;
254        const char *p;
255
256        memset(b, 0, sizeof b);
257        p = ae->t_addr->dec;
258        for (i = 0; i < 4; i++) {
259                j = sscanf(p, "%u%n", &u, &k);
260                if (j != 1)
261                        return (0);
262                if (u & ~0xff)
263                        return (0);
264                b[i] = (unsigned char)u;
265                if (p[k] == '\0')
266                        break;
267                if (p[k] != '.')
268                        return (0);
269                p += k + 1;
270        }
271        if (ae->t_mask == NULL)
272                ae->mask = 8 + 8 * i;
273        vcc_acl_add_entry(tl, ae, 4, b, AF_INET);
274        return (1);
275}
276
277static void
278vcc_acl_entry(struct tokenlist *tl)
279{
280        struct acl_e *ae;
281
282        ae = TlAlloc(tl, sizeof *ae);
283        AN(ae);
284
285        if (tl->t->tok == '!') {
286                ae->not = 1;
287                vcc_NextToken(tl);
288        }
289
290        if (tl->t->tok == '(') {
291                ae->para = 1;
292                vcc_NextToken(tl);
293        }
294
295        if (!ae->not && tl->t->tok == '!') {
296                ae->not = 1;
297                vcc_NextToken(tl);
298        }
299
300        ExpectErr(tl, CSTR);
301        ae->t_addr = tl->t;
302        vcc_NextToken(tl);
303
304        if (tl->t->tok == '/') {
305                vcc_NextToken(tl);
306                ae->t_mask = tl->t;
307                ExpectErr(tl, CNUM);
308                ae->mask = vcc_UintVal(tl);
309        }
310
311        if (ae->para) 
312                SkipToken(tl, ')');
313
314        if (!vcc_acl_try_netnotation(tl, ae)) {
315                ERRCHK(tl);
316                vcc_acl_try_getaddrinfo(tl, ae);
317        }
318        ERRCHK(tl);
319}
320
321/*********************************************************************
322 * Emit a function to match the ACL we have collected
323 */
324
325/*
326 * XXX: this is semi-silly.  We try really hard to not depend in the
327 * XXX: systems include files while compiling VCL, but we need to know
328 * XXX: the size of the sa_familiy member.
329 * XXX: FlexeLint complains about these antics, so isolate it in a
330 * XXX: separate function.
331 */
332
333/*lint -save -e506 -e774 -e550 */
334static void
335c_is_a_silly_language(const struct tokenlist *tl)
336{
337        struct sockaddr sa;
338
339        assert(sizeof (unsigned char) == 1);
340        assert(sizeof (unsigned short) == 2);
341        assert(sizeof (unsigned int) == 4);
342        if (sizeof sa.sa_family == 1)
343                Fh(tl, 0, "\tunsigned char fam;\n");
344        else if (sizeof sa.sa_family == 2)
345                Fh(tl, 0, "\tunsigned short fam;\n");
346        else if (sizeof sa.sa_family == 4)
347                Fh(tl, 0, "\tunsigned int fam;\n");
348        else
349                assert(0 == __LINE__);
350}
351/*lint -restore */
352
353static void
354vcc_acl_emit(const struct tokenlist *tl, const char *acln, int anon)
355{
356        struct acl_e *ae;
357        int depth, l, m, i;
358        unsigned at[VRT_ACL_MAXADDR + 1];
359        const char *oc;
360
361        Fh(tl, 0, "\nstatic int\n");
362        Fh(tl, 0, "match_acl_%s_%s(const struct sess *sp, const void *p)\n",
363            anon ? "anon" : "named", acln);
364        Fh(tl, 0, "{\n");
365        Fh(tl, 0, "\tconst unsigned char *a;\n");
366        c_is_a_silly_language(tl);
367
368        Fh(tl, 0, "\n");
369        Fh(tl, 0, "\ta = p;\n");
370        Fh(tl, 0, "\tVRT_memmove(&fam, a + %d, sizeof fam);\n",
371            offsetof(struct sockaddr, sa_family));
372        Fh(tl, 0, "\tif (fam == %d)\n", PF_INET);
373        Fh(tl, 0, "\t\ta += %d;\n", offsetof(struct sockaddr_in, sin_addr));
374        Fh(tl, 0, "\telse if (fam == %d)\n", PF_INET6);
375        Fh(tl, 0, "\t\ta += %d;\n", offsetof(struct sockaddr_in6, sin6_addr));
376        Fh(tl, 0, "\telse {\n");
377        Fh(tl, 0, "\t\tVRT_acl_log(sp, \"NO_FAM %s\");\n", acln);
378        Fh(tl, 0, "\t\treturn(0);\n");
379        Fh(tl, 0, "\t}\n\n");
380        depth = -1;
381        oc = 0;
382        at[0] = 256;
383        VTAILQ_FOREACH(ae, &tl->acl, list) {
384
385                /* Find how much common prefix we have */
386                for (l = 0; l <= depth && l * 8 < ae->mask; l++) {
387                        assert(l >= 0);
388                        if (ae->data[l] != at[l])
389                                break;
390                }
391
392                /* Back down, if necessary */
393                oc = "";
394                while (l <= depth) {
395                        Fh(tl, 0, "\t%*s}\n", -depth, "");
396                        depth--;
397                        oc = "else ";
398                }
399
400                m = ae->mask;
401                m -= l * 8;
402
403                /* Do whole byte compares */
404                for (i = l; m >= 8; m -= 8, i++) {
405                        if (i == 0)
406                                Fh(tl, 0, "\t%*s%sif (fam == %d) {\n",
407                                    -i, "", oc, ae->data[i]);
408                        else
409                                Fh(tl, 0, "\t%*s%sif (a[%d] == %d) {\n",
410                                    -i, "", oc, i - 1, ae->data[i]);
411                        at[i] = ae->data[i];
412                        depth = i;
413                        oc = "";
414                }
415
416                if (m > 0) {
417                        /* Do fractional byte compares */
418                        Fh(tl, 0, "\t%*s%sif ((a[%d] & 0x%x) == %d) {\n",
419                            -i, "", oc, i - 1, (0xff00 >> m) & 0xff,
420                            ae->data[i] & ((0xff00 >> m) & 0xff));
421                        at[i] = 256;
422                        depth = i;
423                        oc = "";
424                }
425
426                i = (ae->mask + 7) / 8;
427
428                if (!anon) {
429                        Fh(tl, 0, "\t%*sVRT_acl_log(sp, \"%sMATCH %s \" ",
430                            -i, "", ae->not ? "NEG_" : "", acln,
431                            PF(ae->t_addr));
432                        EncToken(tl->fh, ae->t_addr);
433                        if (ae->t_mask != NULL)
434                                Fh(tl, 0, " \"/%.*s\" ", PF(ae->t_mask));
435                        Fh(tl, 0, ");\n");
436                }
437
438                Fh(tl, 0, "\t%*sreturn (%d);\n", -i, "", ae->not ? 0 : 1);
439        }
440
441        /* Unwind */
442        for (; 0 <= depth; depth--)
443                Fh(tl, 0, "\t%*.*s}\n", depth, depth, "");
444
445        /* Deny by default */
446        if (!anon)
447                Fh(tl, 0, "\tVRT_acl_log(sp, \"NO_MATCH %s\");\n", acln);
448        Fh(tl, 0, "\treturn (0);\n}\n");
449}
450
451void
452vcc_Cond_Ip(const struct var *vp, struct tokenlist *tl)
453{
454        unsigned tcond;
455        char acln[32];
456
457        switch (tl->t->tok) {
458        case '~':
459                vcc_NextToken(tl);
460                ExpectErr(tl, ID);
461                vcc_AddRef(tl, tl->t, R_ACL);
462                Fb(tl, 1, "match_acl_named_%.*s(sp, %s)\n",
463                    PF(tl->t), vp->rname);
464                vcc_NextToken(tl);
465                break;
466        case T_EQ:
467        case T_NEQ:
468
469                VTAILQ_INIT(&tl->acl);
470                tcond = tl->t->tok;
471                vcc_NextToken(tl);
472                bprintf(acln, "%u", tl->cnt);
473                vcc_acl_entry(tl);
474                vcc_acl_emit(tl, acln, 1);
475                Fb(tl, 1, "%smatch_acl_anon_%s(sp, %s)\n",
476                    (tcond == T_NEQ ? "!" : ""), acln, vp->rname);
477                break;
478        default:
479                vsb_printf(tl->sb, "Invalid condition ");
480                vcc_ErrToken(tl, tl->t);
481                vsb_printf(tl->sb, " on IP number variable\n");
482                vsb_printf(tl->sb, "  only '==', '!=' and '~' are legal\n");
483                vcc_ErrWhere(tl, tl->t);
484                break;
485        }
486}
487
488void
489vcc_Acl(struct tokenlist *tl)
490{
491        struct token *an;
492        char acln[1024];
493
494        vcc_NextToken(tl);
495        VTAILQ_INIT(&tl->acl);
496
497        ExpectErr(tl, ID);
498        an = tl->t;
499        vcc_NextToken(tl);
500
501        vcc_AddDef(tl, an, R_ACL);
502        bprintf(acln, "%.*s", PF(an));
503
504        SkipToken(tl, '{');
505
506        while (tl->t->tok != '}') {
507                vcc_acl_entry(tl);
508                ERRCHK(tl);
509                SkipToken(tl, ';');
510        }
511        SkipToken(tl, '}');
512
513        vcc_acl_emit(tl, acln, 0);
514}
Note: See TracBrowser for help on using the repository browser.