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 "vmod_blob.h"
33
#include "hex.h"
34
35
#include "vdef.h"
36
#include "vrt.h"
37
#include "vas.h"
38
39
/* Decoder states */
40
enum state_e {
41
        NORMAL,
42
        PERCENT,  /* just read '%' */
43
        FIRSTNIB, /* just read the first nibble after '%' */
44
};
45
46
size_t
47 28
url_encode_l(size_t l)
48
{
49 28
        return (l * 3) + 1;
50
}
51
52
size_t
53 134
url_decode_l(size_t l)
54
{
55 134
        return l;
56
}
57
58
/*
59
 * Bitmap of unreserved characters according to RFC 3986 section 2.3
60
 * (locale-independent and cacheline friendly)
61
 */
62
static const uint8_t unreserved[] = {
63
        0x0,  0x0,  0x0,  0x0,  0x0,  0x60, 0xff, 0x3,
64
        0xfe, 0xff, 0xff, 0x87, 0xfe, 0xff, 0xff, 0x47,
65
        0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x0,
66
        0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x0,  0x0
67
};
68
69
static inline int
70 19202
isunreserved(const uint8_t c)
71
{
72 19202
        return (unreserved[c >> 3] & (1 << (c & 7)));
73
}
74
75
static inline int
76 26456
isoutofrange(const uint8_t c)
77
{
78 26456
        return (c < '0' || c > 'f');
79
}
80
81
ssize_t
82 186
url_encode(const enum encoding enc, const enum case_e kase,
83
           char *restrict const buf, const size_t buflen,
84
           const char *restrict const in, const size_t inlen)
85
{
86 186
        char *p = buf;
87 186
        const char * const end = buf + buflen;
88 186
        const char *alphabet = hex_alphabet[0];
89
90 186
        AN(buf);
91 186
        assert(enc == URL);
92 186
        if (in == NULL || inlen == 0)
93 6
                return 0;
94
95 180
        if (kase == UPPER)
96 64
                alphabet = hex_alphabet[1];
97
98 19382
        for (int i = 0; i < inlen; i++) {
99 19202
                if (isunreserved(in[i])) {
100 6532
                        if (p == end)
101 0
                                return -1;
102 6532
                        *p++ = in[i];
103
                }
104
                else {
105 12670
                        if (p + 3 > end)
106 0
                                return -1;
107 12670
                        *p++ = '%';
108 12670
                        *p++ = alphabet[(in[i] & 0xf0) >> 4];
109 12670
                        *p++ = alphabet[in[i] & 0x0f];
110
                }
111
        }
112
113 180
        return p - buf;
114
}
115
116
ssize_t
117 228
url_decode(const enum encoding dec, char *restrict const buf,
118
           const size_t buflen, ssize_t n, const char *restrict const p,
119
           va_list ap)
120
{
121 228
        char *dest = buf;
122 228
        const char * const end = buf + buflen;
123 228
        size_t len = SIZE_MAX;
124 228
        uint8_t nib = 0;
125 228
        enum state_e state = NORMAL;
126
127 228
        AN(buf);
128 228
        assert(dec == URL);
129
130 228
        if (n >= 0 && (size_t)n < len)
131 106
                len = n;
132
133 754
        for (const char *s = p; len > 0 && s != vrt_magic_string_end;
134 298
             s = va_arg(ap, const char *)) {
135 310
                if (s == NULL || *s == '\0')
136 64
                        continue;
137 47074
                while (*s && len) {
138
                        uint8_t nib2;
139
140 46594
                        switch (state) {
141
                        case NORMAL:
142 20138
                                if (*s == '%')
143 13246
                                        state = PERCENT;
144
                                else {
145 6892
                                        if (dest == end) {
146 0
                                                errno = ENOMEM;
147 0
                                                return -1;
148
                                        }
149 6892
                                        *dest++ = *s;
150
                                }
151 20138
                                break;
152
                        case PERCENT:
153 26466
                                if (isoutofrange(*s) ||
154 13230
                                    (nib = nibble[*s - '0']) == ILL) {
155 6
                                        errno = EINVAL;
156 6
                                        return -1;
157
                                }
158 13230
                                state = FIRSTNIB;
159 13230
                                break;
160
                        case FIRSTNIB:
161 13220
                                if (dest == end) {
162 0
                                        errno = ENOMEM;
163 0
                                        return -1;
164
                                }
165 26434
                                if (isoutofrange(*s) ||
166 13214
                                    (nib2 = nibble[*s - '0']) == ILL) {
167 6
                                        errno = EINVAL;
168 6
                                        return -1;
169
                                }
170 13214
                                *dest++ = (nib << 4) | nib2;
171 13214
                                nib = 0;
172 13214
                                state = NORMAL;
173 13214
                                break;
174
                        default:
175 0
                                WRONG("illegal URL decode state");
176
                        }
177 46582
                        s++;
178 46582
                        len--;
179
                }
180
        }
181 216
        if (state != NORMAL) {
182 20
                errno = EINVAL;
183 20
                return -1;
184
        }
185 196
        assert(dest <= end);
186 196
        return dest - buf;
187
}