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 14
url_encode_l(size_t l)
48
{
49 14
        return (l * 3) + 1;
50
}
51
52
size_t
53 67
url_decode_l(size_t l)
54
{
55 67
        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 9601
isunreserved(const uint8_t c)
71
{
72 9601
        return (unreserved[c >> 3] & (1 << (c & 7)));
73
}
74
75
static inline int
76 13228
isoutofrange(const uint8_t c)
77
{
78 13228
        return (c < '0' || c > 'f');
79
}
80
81
ssize_t
82 93
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 93
        char *p = buf;
87 93
        const char * const end = buf + buflen;
88 93
        const char *alphabet = hex_alphabet[0];
89
90 93
        AN(buf);
91 93
        assert(enc == URL);
92 93
        if (in == NULL || inlen == 0)
93 3
                return 0;
94
95 90
        if (kase == UPPER)
96 32
                alphabet = hex_alphabet[1];
97
98 9691
        for (int i = 0; i < inlen; i++) {
99 9601
                if (isunreserved(in[i])) {
100 3266
                        if (p == end)
101 0
                                return -1;
102 3266
                        *p++ = in[i];
103
                }
104
                else {
105 6335
                        if (p + 3 > end)
106 0
                                return -1;
107 6335
                        *p++ = '%';
108 6335
                        *p++ = alphabet[(in[i] & 0xf0) >> 4];
109 6335
                        *p++ = alphabet[in[i] & 0x0f];
110
                }
111
        }
112
113 90
        return p - buf;
114
}
115
116
ssize_t
117 114
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 114
        char *dest = buf;
122 114
        const char * const end = buf + buflen;
123 114
        size_t len = SIZE_MAX;
124 114
        uint8_t nib = 0;
125 114
        enum state_e state = NORMAL;
126
127 114
        AN(buf);
128 114
        assert(dec == URL);
129
130 114
        if (n >= 0 && (size_t)n < len)
131 53
                len = n;
132
133 377
        for (const char *s = p; len > 0 && s != vrt_magic_string_end;
134 149
             s = va_arg(ap, const char *)) {
135 155
                if (s == NULL || *s == '\0')
136 32
                        continue;
137 23537
                while (*s && len) {
138
                        uint8_t nib2;
139
140 23297
                        switch (state) {
141
                        case NORMAL:
142 10069
                                if (*s == '%')
143 6623
                                        state = PERCENT;
144
                                else {
145 3446
                                        if (dest == end) {
146 0
                                                errno = ENOMEM;
147 0
                                                return -1;
148
                                        }
149 3446
                                        *dest++ = *s;
150
                                }
151 10069
                                break;
152
                        case PERCENT:
153 6618
                                if (isoutofrange(*s)
154 6615
                                    || (nib = nibble[*s - '0']) == ILL) {
155 3
                                        errno = EINVAL;
156 3
                                        return -1;
157
                                }
158 6615
                                state = FIRSTNIB;
159 6615
                                break;
160
                        case FIRSTNIB:
161 6610
                                if (dest == end) {
162 0
                                        errno = ENOMEM;
163 0
                                        return -1;
164
                                }
165 6610
                                if (isoutofrange(*s)
166 6607
                                    || (nib2 = nibble[*s - '0']) == ILL) {
167 3
                                        errno = EINVAL;
168 3
                                        return -1;
169
                                }
170 6607
                                *dest++ = (nib << 4) | nib2;
171 6607
                                nib = 0;
172 6607
                                state = NORMAL;
173 6607
                                break;
174
                        default:
175 0
                                WRONG("illegal URL decode state");
176
                        }
177 23291
                        s++;
178 23291
                        len--;
179
                }
180
        }
181 108
        if (state != NORMAL) {
182 10
                errno = EINVAL;
183 10
                return -1;
184
        }
185 98
        assert(dest <= end);
186 98
        return dest - buf;
187
}