varnish-cache/lib/libvmod_blob/url.c
1
/*-
2
 * Copyright 2015-2016 UPLEX - Nils Goroll Systemoptimierung
3
 * All rights reserved.
4
 *
5
 * Authors: Nils Goroll <nils.goroll@uplex.de>
6
 *          Geoffrey Simmons <geoffrey.simmons@uplex.de>
7
 *
8
 * Redistribution and use in source and binary forms, with or without
9
 * modification, are permitted provided that the following conditions are met:
10
 * 1. Redistributions of source code must retain the above copyright notice,
11
 *    this list of conditions and the following disclaimer.
12
 * 2. Redistributions in binary form must reproduce the above copyright notice,
13
 *    this list of conditions and the following disclaimer in the documentation
14
 *    and/or other materials provided with the distribution.
15
 *
16
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
17
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19
 * DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY
20
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
 *
27
 */
28
29
#include "config.h"
30
#include <errno.h>
31
32
#include "hex.h"
33
34
#include "vdef.h"
35
#include "vrt.h"
36
#include "vas.h"
37
38
#include "vmod_blob.h"
39
40
/* Decoder states */
41
enum state_e {
42
        NORMAL,
43
        PERCENT,  /* just read '%' */
44
        FIRSTNIB, /* just read the first nibble after '%' */
45
};
46
47
size_t
48 14
url_encode_l(size_t l)
49
{
50 14
        return (l * 3) + 1;
51
}
52
53
size_t
54 67
url_decode_l(size_t l)
55
{
56 67
        return l;
57
}
58
59
/*
60
 * Bitmap of unreserved characters according to RFC 3986 section 2.3
61
 * (locale-independent and cacheline friendly)
62
 */
63
static const uint8_t unreserved[] = {
64
        0x0,  0x0,  0x0,  0x0,  0x0,  0x60, 0xff, 0x3,
65
        0xfe, 0xff, 0xff, 0x87, 0xfe, 0xff, 0xff, 0x47,
66
        0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x0,
67
        0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x0
68
};
69
70
static inline int
71 9605
isunreserved(const uint8_t c)
72
{
73 9605
        return (unreserved[c >> 3] & (1 << (c & 7)));
74
}
75
76
static inline int
77 13228
isoutofrange(const uint8_t c)
78
{
79 13228
        return (c < '0' || c > 'f');
80
}
81
82
ssize_t
83 94
url_encode(const enum encoding enc, const enum case_e kase,
84
           char *restrict const buf, const size_t buflen,
85
           const char *restrict const in, const size_t inlen)
86
{
87 94
        char *p = buf;
88 94
        const char * const end = buf + buflen;
89 94
        const char *alphabet = hex_alphabet[0];
90
91 94
        AN(buf);
92 94
        assert(enc == URL);
93 94
        if (in == NULL || inlen == 0)
94 3
                return 0;
95
96 91
        if (kase == UPPER)
97 32
                alphabet = hex_alphabet[1];
98
99 9695
        for (int i = 0; i < inlen; i++) {
100 9605
                if (isunreserved(in[i])) {
101 3267
                        if (p == end)
102 0
                                return -1;
103 3267
                        *p++ = in[i];
104
                }
105
                else {
106 6338
                        if (p + 3 > end)
107 1
                                return -1;
108 6337
                        *p++ = '%';
109 6337
                        *p++ = alphabet[(in[i] & 0xf0) >> 4];
110 6337
                        *p++ = alphabet[in[i] & 0x0f];
111
                }
112
        }
113
114 90
        return p - buf;
115
}
116
117
ssize_t
118 117
url_decode(const enum encoding dec, char *restrict const buf,
119
           const size_t buflen, ssize_t n,
120
           const struct strands *restrict const strings)
121
{
122 117
        char *dest = buf;
123 117
        const char * const end = buf + buflen;
124 117
        size_t len = SIZE_MAX;
125 117
        uint8_t nib = 0;
126 117
        enum state_e state = NORMAL;
127
128 117
        AN(buf);
129 117
        AN(strings);
130 117
        assert(dec == URL);
131
132 117
        if (n >= 0 && (size_t)n < len)
133 53
                len = n;
134
135 268
        for (int i = 0; len > 0 && i < strings->n; i++) {
136 158
                const char *s = strings->p[i];
137
138 158
                if (s == NULL || *s == '\0')
139 32
                        continue;
140 23571
                while (*s && len) {
141
                        uint8_t nib2;
142
143 23326
                        switch (state) {
144
                        case NORMAL:
145 10098
                                if (*s == '%')
146 6623
                                        state = PERCENT;
147
                                else {
148 3475
                                        if (dest == end) {
149 1
                                                errno = ENOMEM;
150 1
                                                return -1;
151
                                        }
152 3474
                                        *dest++ = *s;
153
                                }
154 10097
                                break;
155
                        case PERCENT:
156 13233
                                if (isoutofrange(*s) ||
157 6615
                                    (nib = nibble[*s - '0']) == ILL) {
158 3
                                        errno = EINVAL;
159 3
                                        return -1;
160
                                }
161 6615
                                state = FIRSTNIB;
162 6615
                                break;
163
                        case FIRSTNIB:
164 6610
                                if (dest == end) {
165 0
                                        errno = ENOMEM;
166 0
                                        return -1;
167
                                }
168 13217
                                if (isoutofrange(*s) ||
169 6607
                                    (nib2 = nibble[*s - '0']) == ILL) {
170 3
                                        errno = EINVAL;
171 3
                                        return -1;
172
                                }
173 6607
                                *dest++ = (nib << 4) | nib2;
174 6607
                                nib = 0;
175 6607
                                state = NORMAL;
176 6607
                                break;
177
                        default:
178 0
                                WRONG("illegal URL decode state");
179
                        }
180 23319
                        s++;
181 23319
                        len--;
182
                }
183
        }
184 110
        if (state != NORMAL) {
185 10
                errno = EINVAL;
186 10
                return -1;
187
        }
188 100
        assert(dest <= end);
189 100
        return dest - buf;
190
}