summaryrefslogtreecommitdiffstats
path: root/plugins/picohttpparser
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/picohttpparser')
-rw-r--r--plugins/picohttpparser/picohttpparser.c1140
-rw-r--r--plugins/picohttpparser/picohttpparser.h31
2 files changed, 596 insertions, 575 deletions
diff --git a/plugins/picohttpparser/picohttpparser.c b/plugins/picohttpparser/picohttpparser.c
index d0bfac62..e87388b0 100644
--- a/plugins/picohttpparser/picohttpparser.c
+++ b/plugins/picohttpparser/picohttpparser.c
@@ -28,622 +28,640 @@
28#include <stddef.h> 28#include <stddef.h>
29#include <string.h> 29#include <string.h>
30#ifdef __SSE4_2__ 30#ifdef __SSE4_2__
31#ifdef _MSC_VER 31# ifdef _MSC_VER
32#include <nmmintrin.h> 32# include <nmmintrin.h>
33#else 33# else
34#include <x86intrin.h> 34# include <x86intrin.h>
35#endif 35# endif
36#endif 36#endif
37#include "picohttpparser.h" 37#include "picohttpparser.h"
38 38
39#if __GNUC__ >= 3 39#if __GNUC__ >= 3
40#define likely(x) __builtin_expect(!!(x), 1) 40# define likely(x) __builtin_expect(!!(x), 1)
41#define unlikely(x) __builtin_expect(!!(x), 0) 41# define unlikely(x) __builtin_expect(!!(x), 0)
42#else 42#else
43#define likely(x) (x) 43# define likely(x) (x)
44#define unlikely(x) (x) 44# define unlikely(x) (x)
45#endif 45#endif
46 46
47#ifdef _MSC_VER 47#ifdef _MSC_VER
48#define ALIGNED(n) _declspec(align(n)) 48# define ALIGNED(n) _declspec(align(n))
49#else 49#else
50#define ALIGNED(n) __attribute__((aligned(n))) 50# define ALIGNED(n) __attribute__((aligned(n)))
51#endif 51#endif
52 52
53#define IS_PRINTABLE_ASCII(c) ((unsigned char)(c)-040u < 0137u) 53#define IS_PRINTABLE_ASCII(c) ((unsigned char)(c) - 040u < 0137u)
54 54
55#define CHECK_EOF() \ 55#define CHECK_EOF() \
56 if (buf == buf_end) { \ 56 if (buf == buf_end) { \
57 *ret = -2; \ 57 *ret = -2; \
58 return NULL; \ 58 return NULL; \
59 } 59 }
60 60
61#define EXPECT_CHAR_NO_CHECK(ch) \ 61#define EXPECT_CHAR_NO_CHECK(ch) \
62 if (*buf++ != ch) { \ 62 if (*buf++ != ch) { \
63 *ret = -1; \ 63 *ret = -1; \
64 return NULL; \ 64 return NULL; \
65 } 65 }
66 66
67#define EXPECT_CHAR(ch) \ 67#define EXPECT_CHAR(ch) \
68 CHECK_EOF(); \ 68 CHECK_EOF(); \
69 EXPECT_CHAR_NO_CHECK(ch); 69 EXPECT_CHAR_NO_CHECK(ch);
70 70
71#define ADVANCE_TOKEN(tok, toklen) \ 71#define ADVANCE_TOKEN(tok, toklen) \
72 do { \ 72 do { \
73 const char *tok_start = buf; \ 73 const char *tok_start = buf; \
74 static const char ALIGNED(16) ranges2[16] = "\000\040\177\177"; \ 74 static const char ALIGNED(16) ranges2[16] = "\000\040\177\177"; \
75 int found2; \ 75 int found2; \
76 buf = findchar_fast(buf, buf_end, ranges2, 4, &found2); \ 76 buf = findchar_fast(buf, buf_end, ranges2, 4, &found2); \
77 if (!found2) { \ 77 if (!found2) { \
78 CHECK_EOF(); \ 78 CHECK_EOF(); \
79 } \ 79 } \
80 while (1) { \ 80 while (1) { \
81 if (*buf == ' ') { \ 81 if (*buf == ' ') { \
82 break; \ 82 break; \
83 } else if (unlikely(!IS_PRINTABLE_ASCII(*buf))) { \ 83 } else if (unlikely(!IS_PRINTABLE_ASCII(*buf))) { \
84 if ((unsigned char)*buf < '\040' || *buf == '\177') { \ 84 if ((unsigned char)*buf < '\040' || *buf == '\177') { \
85 *ret = -1; \ 85 *ret = -1; \
86 return NULL; \ 86 return NULL; \
87 } \ 87 } \
88 } \ 88 } \
89 ++buf; \ 89 ++buf; \
90 CHECK_EOF(); \ 90 CHECK_EOF(); \
91 } \ 91 } \
92 tok = tok_start; \ 92 tok = tok_start; \
93 toklen = buf - tok_start; \ 93 toklen = buf - tok_start; \
94 } while (0) 94 } while (0)
95 95
96static const char *token_char_map = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" 96static const char *token_char_map =
97 "\0\1\0\1\1\1\1\1\0\0\1\1\0\1\1\0\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0" 97 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
98 "\0\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\0\0\1\1" 98 "\0\1\0\1\1\1\1\1\0\0\1\1\0\1\1\0\1\1\1\1\1\1\1\1\1\1\0\0\0\0\0\0"
99 "\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\1\0\1\0" 99 "\0\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\0\0\1\1"
100 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" 100 "\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\1\0\1\0\1\0"
101 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" 101 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
102 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" 102 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
103 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; 103 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
104 104 "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
105static const char *findchar_fast(const char *buf, const char *buf_end, const char *ranges, size_t ranges_size, int *found) 105
106{ 106static const char *findchar_fast(const char *buf, const char *buf_end, const char *ranges,
107 *found = 0; 107 size_t ranges_size, int *found) {
108 *found = 0;
108#if __SSE4_2__ 109#if __SSE4_2__
109 if (likely(buf_end - buf >= 16)) { 110 if (likely(buf_end - buf >= 16)) {
110 __m128i ranges16 = _mm_loadu_si128((const __m128i *)ranges); 111 __m128i ranges16 = _mm_loadu_si128((const __m128i *)ranges);
111 112
112 size_t left = (buf_end - buf) & ~15; 113 size_t left = (buf_end - buf) & ~15;
113 do { 114 do {
114 __m128i b16 = _mm_loadu_si128((const __m128i *)buf); 115 __m128i b16 = _mm_loadu_si128((const __m128i *)buf);
115 int r = _mm_cmpestri(ranges16, ranges_size, b16, 16, _SIDD_LEAST_SIGNIFICANT | _SIDD_CMP_RANGES | _SIDD_UBYTE_OPS); 116 int r = _mm_cmpestri(ranges16, ranges_size, b16, 16,
116 if (unlikely(r != 16)) { 117 _SIDD_LEAST_SIGNIFICANT | _SIDD_CMP_RANGES | _SIDD_UBYTE_OPS);
117 buf += r; 118 if (unlikely(r != 16)) {
118 *found = 1; 119 buf += r;
119 break; 120 *found = 1;
120 } 121 break;
121 buf += 16; 122 }
122 left -= 16; 123 buf += 16;
123 } while (likely(left != 0)); 124 left -= 16;
124 } 125 } while (likely(left != 0));
126 }
125#else 127#else
126 /* suppress unused parameter warning */ 128 /* suppress unused parameter warning */
127 (void)buf_end; 129 (void)buf_end;
128 (void)ranges; 130 (void)ranges;
129 (void)ranges_size; 131 (void)ranges_size;
130#endif 132#endif
131 return buf; 133 return buf;
132} 134}
133 135
134static const char *get_token_to_eol(const char *buf, const char *buf_end, const char **token, size_t *token_len, int *ret) 136static const char *get_token_to_eol(const char *buf, const char *buf_end, const char **token,
135{ 137 size_t *token_len, int *ret) {
136 const char *token_start = buf; 138 const char *token_start = buf;
137 139
138#ifdef __SSE4_2__ 140#ifdef __SSE4_2__
139 static const char ALIGNED(16) ranges1[16] = "\0\010" /* allow HT */ 141 static const char ALIGNED(16) ranges1[16] =
140 "\012\037" /* allow SP and up to but not including DEL */ 142 "\0\010" /* allow HT */
141 "\177\177"; /* allow chars w. MSB set */ 143 "\012\037" /* allow SP and up to but not including DEL */
142 int found; 144 "\177\177"; /* allow chars w. MSB set */
143 buf = findchar_fast(buf, buf_end, ranges1, 6, &found); 145 int found;
144 if (found) 146 buf = findchar_fast(buf, buf_end, ranges1, 6, &found);
145 goto FOUND_CTL; 147 if (found) {
148 goto FOUND_CTL;
149 }
146#else 150#else
147 /* find non-printable char within the next 8 bytes, this is the hottest code; manually inlined */ 151 /* find non-printable char within the next 8 bytes, this is the hottest code; manually inlined
148 while (likely(buf_end - buf >= 8)) { 152 */
149#define DOIT() \ 153 while (likely(buf_end - buf >= 8)) {
150 do { \ 154# define DOIT() \
151 if (unlikely(!IS_PRINTABLE_ASCII(*buf))) \ 155 do { \
152 goto NonPrintable; \ 156 if (unlikely(!IS_PRINTABLE_ASCII(*buf))) \
153 ++buf; \ 157 goto NonPrintable; \
154 } while (0) 158 ++buf; \
155 DOIT(); 159 } while (0)
156 DOIT(); 160 DOIT();
157 DOIT(); 161 DOIT();
158 DOIT(); 162 DOIT();
159 DOIT(); 163 DOIT();
160 DOIT(); 164 DOIT();
161 DOIT(); 165 DOIT();
162 DOIT(); 166 DOIT();
163#undef DOIT 167 DOIT();
164 continue; 168# undef DOIT
165 NonPrintable: 169 continue;
166 if ((likely((unsigned char)*buf < '\040') && likely(*buf != '\011')) || unlikely(*buf == '\177')) { 170 NonPrintable:
167 goto FOUND_CTL; 171 if ((likely((unsigned char)*buf < '\040') && likely(*buf != '\011')) ||
168 } 172 unlikely(*buf == '\177')) {
169 ++buf; 173 goto FOUND_CTL;
170 } 174 }
175 ++buf;
176 }
171#endif 177#endif
172 for (;; ++buf) { 178 for (;; ++buf) {
173 CHECK_EOF(); 179 CHECK_EOF();
174 if (unlikely(!IS_PRINTABLE_ASCII(*buf))) { 180 if (unlikely(!IS_PRINTABLE_ASCII(*buf))) {
175 if ((likely((unsigned char)*buf < '\040') && likely(*buf != '\011')) || unlikely(*buf == '\177')) { 181 if ((likely((unsigned char)*buf < '\040') && likely(*buf != '\011')) ||
176 goto FOUND_CTL; 182 unlikely(*buf == '\177')) {
177 } 183 goto FOUND_CTL;
178 } 184 }
179 } 185 }
186 }
180FOUND_CTL: 187FOUND_CTL:
181 if (likely(*buf == '\015')) { 188 if (likely(*buf == '\015')) {
182 ++buf; 189 ++buf;
183 EXPECT_CHAR('\012'); 190 EXPECT_CHAR('\012');
184 *token_len = buf - 2 - token_start; 191 *token_len = buf - 2 - token_start;
185 } else if (*buf == '\012') { 192 } else if (*buf == '\012') {
186 *token_len = buf - token_start; 193 *token_len = buf - token_start;
187 ++buf; 194 ++buf;
188 } else { 195 } else {
189 *ret = -1; 196 *ret = -1;
190 return NULL; 197 return NULL;
191 } 198 }
192 *token = token_start; 199 *token = token_start;
193 200
194 return buf; 201 return buf;
195} 202}
196 203
197static const char *is_complete(const char *buf, const char *buf_end, size_t last_len, int *ret) 204static const char *is_complete(const char *buf, const char *buf_end, size_t last_len, int *ret) {
198{ 205 int ret_cnt = 0;
199 int ret_cnt = 0; 206 buf = last_len < 3 ? buf : buf + last_len - 3;
200 buf = last_len < 3 ? buf : buf + last_len - 3; 207
201 208 while (1) {
202 while (1) { 209 CHECK_EOF();
203 CHECK_EOF(); 210 if (*buf == '\015') {
204 if (*buf == '\015') { 211 ++buf;
205 ++buf; 212 CHECK_EOF();
206 CHECK_EOF(); 213 EXPECT_CHAR('\012');
207 EXPECT_CHAR('\012'); 214 ++ret_cnt;
208 ++ret_cnt; 215 } else if (*buf == '\012') {
209 } else if (*buf == '\012') { 216 ++buf;
210 ++buf; 217 ++ret_cnt;
211 ++ret_cnt; 218 } else {
212 } else { 219 ++buf;
213 ++buf; 220 ret_cnt = 0;
214 ret_cnt = 0; 221 }
215 } 222 if (ret_cnt == 2) {
216 if (ret_cnt == 2) { 223 return buf;
217 return buf; 224 }
218 } 225 }
219 } 226
220 227 *ret = -2;
221 *ret = -2; 228 return NULL;
222 return NULL;
223} 229}
224 230
225#define PARSE_INT(valp_, mul_) \ 231#define PARSE_INT(valp_, mul_) \
226 if (*buf < '0' || '9' < *buf) { \ 232 if (*buf < '0' || '9' < *buf) { \
227 buf++; \ 233 buf++; \
228 *ret = -1; \ 234 *ret = -1; \
229 return NULL; \ 235 return NULL; \
230 } \ 236 } \
231 *(valp_) = (mul_) * (*buf++ - '0'); 237 *(valp_) = (mul_) * (*buf++ - '0');
232 238
233#define PARSE_INT_3(valp_) \ 239#define PARSE_INT_3(valp_) \
234 do { \ 240 do { \
235 int res_ = 0; \ 241 int res_ = 0; \
236 PARSE_INT(&res_, 100) \ 242 PARSE_INT(&res_, 100) \
237 *valp_ = res_; \ 243 *valp_ = res_; \
238 PARSE_INT(&res_, 10) \ 244 PARSE_INT(&res_, 10) \
239 *valp_ += res_; \ 245 *valp_ += res_; \
240 PARSE_INT(&res_, 1) \ 246 PARSE_INT(&res_, 1) \
241 *valp_ += res_; \ 247 *valp_ += res_; \
242 } while (0) 248 } while (0)
243 249
244/* returned pointer is always within [buf, buf_end), or null */ 250/* returned pointer is always within [buf, buf_end), or null */
245static const char *parse_http_version(const char *buf, const char *buf_end, int *major_version, int *minor_version, int *ret) 251static const char *parse_http_version(const char *buf, const char *buf_end, int *major_version,
246{ 252 int *minor_version, int *ret) {
247 /* we want at least [HTTP/1.<two chars>] to try to parse */ 253 /* we want at least [HTTP/1.<two chars>] to try to parse */
248 if (buf_end - buf < 9) { 254 if (buf_end - buf < 9) {
249 *ret = -2; 255 *ret = -2;
250 return NULL; 256 return NULL;
251 } 257 }
252 EXPECT_CHAR_NO_CHECK('H'); 258 EXPECT_CHAR_NO_CHECK('H');
253 EXPECT_CHAR_NO_CHECK('T'); 259 EXPECT_CHAR_NO_CHECK('T');
254 EXPECT_CHAR_NO_CHECK('T'); 260 EXPECT_CHAR_NO_CHECK('T');
255 EXPECT_CHAR_NO_CHECK('P'); 261 EXPECT_CHAR_NO_CHECK('P');
256 EXPECT_CHAR_NO_CHECK('/'); 262 EXPECT_CHAR_NO_CHECK('/');
257 PARSE_INT(major_version, 1); 263 PARSE_INT(major_version, 1);
258 if (*major_version == 1) { 264 if (*major_version == 1) {
259 EXPECT_CHAR_NO_CHECK('.'); 265 EXPECT_CHAR_NO_CHECK('.');
260 PARSE_INT(minor_version, 1); 266 PARSE_INT(minor_version, 1);
261 } else { 267 } else {
262 *minor_version = 0; 268 *minor_version = 0;
263 } 269 }
264 return buf; 270 return buf;
265} 271}
266 272
267static const char *parse_headers(const char *buf, const char *buf_end, struct phr_header *headers, size_t *num_headers, 273static const char *parse_headers(const char *buf, const char *buf_end, struct phr_header *headers,
268 size_t max_headers, int *ret) 274 size_t *num_headers, size_t max_headers, int *ret) {
269{ 275 for (;; ++*num_headers) {
270 for (;; ++*num_headers) { 276 CHECK_EOF();
271 CHECK_EOF(); 277 if (*buf == '\015') {
272 if (*buf == '\015') { 278 ++buf;
273 ++buf; 279 EXPECT_CHAR('\012');
274 EXPECT_CHAR('\012'); 280 break;
275 break; 281 } else if (*buf == '\012') {
276 } else if (*buf == '\012') { 282 ++buf;
277 ++buf; 283 break;
278 break; 284 }
279 } 285 if (*num_headers == max_headers) {
280 if (*num_headers == max_headers) { 286 *ret = -1;
281 *ret = -1; 287 return NULL;
282 return NULL; 288 }
283 } 289 if (!(*num_headers != 0 && (*buf == ' ' || *buf == '\t'))) {
284 if (!(*num_headers != 0 && (*buf == ' ' || *buf == '\t'))) { 290 /* parsing name, but do not discard SP before colon, see
285 /* parsing name, but do not discard SP before colon, see 291 * http://www.mozilla.org/security/announce/2006/mfsa2006-33.html */
286 * http://www.mozilla.org/security/announce/2006/mfsa2006-33.html */ 292 headers[*num_headers].name = buf;
287 headers[*num_headers].name = buf; 293 static const char ALIGNED(16) ranges1[] = "\x00 " /* control chars and up to SP */
288 static const char ALIGNED(16) ranges1[] = "\x00 " /* control chars and up to SP */ 294 "\"\"" /* 0x22 */
289 "\"\"" /* 0x22 */ 295 "()" /* 0x28,0x29 */
290 "()" /* 0x28,0x29 */ 296 ",," /* 0x2c */
291 ",," /* 0x2c */ 297 "//" /* 0x2f */
292 "//" /* 0x2f */ 298 ":@" /* 0x3a-0x40 */
293 ":@" /* 0x3a-0x40 */ 299 "[]" /* 0x5b-0x5d */
294 "[]" /* 0x5b-0x5d */ 300 "{\377"; /* 0x7b-0xff */
295 "{\377"; /* 0x7b-0xff */ 301 int found;
296 int found; 302 buf = findchar_fast(buf, buf_end, ranges1, sizeof(ranges1) - 1, &found);
297 buf = findchar_fast(buf, buf_end, ranges1, sizeof(ranges1) - 1, &found); 303 if (!found) {
298 if (!found) { 304 CHECK_EOF();
299 CHECK_EOF(); 305 }
300 } 306 while (1) {
301 while (1) { 307 if (*buf == ':') {
302 if (*buf == ':') { 308 break;
303 break; 309 } else if (!token_char_map[(unsigned char)*buf]) {
304 } else if (!token_char_map[(unsigned char)*buf]) { 310 *ret = -1;
305 *ret = -1; 311 return NULL;
306 return NULL; 312 }
307 } 313 ++buf;
308 ++buf; 314 CHECK_EOF();
309 CHECK_EOF(); 315 }
310 } 316 if ((headers[*num_headers].name_len = buf - headers[*num_headers].name) == 0) {
311 if ((headers[*num_headers].name_len = buf - headers[*num_headers].name) == 0) { 317 *ret = -1;
312 *ret = -1; 318 return NULL;
313 return NULL; 319 }
314 } 320 ++buf;
315 ++buf; 321 for (;; ++buf) {
316 for (;; ++buf) { 322 CHECK_EOF();
317 CHECK_EOF(); 323 if (!(*buf == ' ' || *buf == '\t')) {
318 if (!(*buf == ' ' || *buf == '\t')) { 324 break;
319 break; 325 }
320 } 326 }
321 } 327 } else {
322 } else { 328 headers[*num_headers].name = NULL;
323 headers[*num_headers].name = NULL; 329 headers[*num_headers].name_len = 0;
324 headers[*num_headers].name_len = 0; 330 }
325 } 331 const char *value;
326 const char *value; 332 size_t value_len;
327 size_t value_len; 333 if ((buf = get_token_to_eol(buf, buf_end, &value, &value_len, ret)) == NULL) {
328 if ((buf = get_token_to_eol(buf, buf_end, &value, &value_len, ret)) == NULL) { 334 return NULL;
329 return NULL; 335 }
330 } 336 /* remove trailing SPs and HTABs */
331 /* remove trailing SPs and HTABs */ 337 const char *value_end = value + value_len;
332 const char *value_end = value + value_len; 338 for (; value_end != value; --value_end) {
333 for (; value_end != value; --value_end) { 339 const char c = *(value_end - 1);
334 const char c = *(value_end - 1); 340 if (!(c == ' ' || c == '\t')) {
335 if (!(c == ' ' || c == '\t')) { 341 break;
336 break; 342 }
337 } 343 }
338 } 344 headers[*num_headers].value = value;
339 headers[*num_headers].value = value; 345 headers[*num_headers].value_len = value_end - value;
340 headers[*num_headers].value_len = value_end - value; 346 }
341 } 347 return buf;
342 return buf;
343} 348}
344 349
345static const char *parse_request(const char *buf, const char *buf_end, const char **method, size_t *method_len, const char **path, 350static const char *parse_request(const char *buf, const char *buf_end, const char **method,
346 size_t *path_len, int *major_version, int *minor_version, struct phr_header *headers, size_t *num_headers, 351 size_t *method_len, const char **path, size_t *path_len,
347 size_t max_headers, int *ret) 352 int *major_version, int *minor_version, struct phr_header *headers,
348{ 353 size_t *num_headers, size_t max_headers, int *ret) {
349 /* skip first empty line (some clients add CRLF after POST content) */ 354 /* skip first empty line (some clients add CRLF after POST content) */
350 CHECK_EOF(); 355 CHECK_EOF();
351 if (*buf == '\015') { 356 if (*buf == '\015') {
352 ++buf; 357 ++buf;
353 EXPECT_CHAR('\012'); 358 EXPECT_CHAR('\012');
354 } else if (*buf == '\012') { 359 } else if (*buf == '\012') {
355 ++buf; 360 ++buf;
356 } 361 }
357 362
358 /* parse request line */ 363 /* parse request line */
359 ADVANCE_TOKEN(*method, *method_len); 364 ADVANCE_TOKEN(*method, *method_len);
360 do { 365 do {
361 ++buf; 366 ++buf;
362 } while (*buf == ' '); 367 } while (*buf == ' ');
363 ADVANCE_TOKEN(*path, *path_len); 368 ADVANCE_TOKEN(*path, *path_len);
364 do { 369 do {
365 ++buf; 370 ++buf;
366 } while (*buf == ' '); 371 } while (*buf == ' ');
367 if (*method_len == 0 || *path_len == 0) { 372 if (*method_len == 0 || *path_len == 0) {
368 *ret = -1; 373 *ret = -1;
369 return NULL; 374 return NULL;
370 } 375 }
371 if ((buf = parse_http_version(buf, buf_end, major_version, minor_version, ret)) == NULL) { 376 if ((buf = parse_http_version(buf, buf_end, major_version, minor_version, ret)) == NULL) {
372 return NULL; 377 return NULL;
373 } 378 }
374 if (*buf == '\015') { 379 if (*buf == '\015') {
375 ++buf; 380 ++buf;
376 EXPECT_CHAR('\012'); 381 EXPECT_CHAR('\012');
377 } else if (*buf == '\012') { 382 } else if (*buf == '\012') {
378 ++buf; 383 ++buf;
379 } else { 384 } else {
380 *ret = -1; 385 *ret = -1;
381 return NULL; 386 return NULL;
382 } 387 }
383 388
384 return parse_headers(buf, buf_end, headers, num_headers, max_headers, ret); 389 return parse_headers(buf, buf_end, headers, num_headers, max_headers, ret);
385} 390}
386 391
387int phr_parse_request(const char *buf_start, size_t len, const char **method, size_t *method_len, const char **path, 392int phr_parse_request(const char *buf_start, size_t len, const char **method, size_t *method_len,
388 size_t *path_len, int *major_version, int *minor_version, struct phr_header *headers, size_t *num_headers, size_t last_len) 393 const char **path, size_t *path_len, int *major_version, int *minor_version,
389{ 394 struct phr_header *headers, size_t *num_headers, size_t last_len) {
390 const char *buf = buf_start, *buf_end = buf_start + len; 395 const char *buf = buf_start, *buf_end = buf_start + len;
391 size_t max_headers = *num_headers; 396 size_t max_headers = *num_headers;
392 int r; 397 int r;
393 398
394 *method = NULL; 399 *method = NULL;
395 *method_len = 0; 400 *method_len = 0;
396 *path = NULL; 401 *path = NULL;
397 *path_len = 0; 402 *path_len = 0;
398 *major_version = -1; 403 *major_version = -1;
399 *minor_version = -1; 404 *minor_version = -1;
400 *num_headers = 0; 405 *num_headers = 0;
401 406
402 /* if last_len != 0, check if the request is complete (a fast countermeasure 407 /* if last_len != 0, check if the request is complete (a fast countermeasure
403 against slowloris */ 408 against slowloris */
404 if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) { 409 if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) {
405 return r; 410 return r;
406 } 411 }
407 412
408 if ((buf = parse_request(buf, buf_end, method, method_len, path, path_len, major_version, minor_version, headers, num_headers, max_headers, 413 if ((buf = parse_request(buf, buf_end, method, method_len, path, path_len, major_version,
409 &r)) == NULL) { 414 minor_version, headers, num_headers, max_headers, &r)) == NULL) {
410 return r; 415 return r;
411 } 416 }
412 417
413 return (int)(buf - buf_start); 418 return (int)(buf - buf_start);
414} 419}
415 420
416static const char *parse_response(const char *buf, const char *buf_end, int *major_version, int *minor_version, int *status, const char **msg, 421static const char *parse_response(const char *buf, const char *buf_end, int *major_version,
417 size_t *msg_len, struct phr_header *headers, size_t *num_headers, size_t max_headers, int *ret) 422 int *minor_version, int *status, const char **msg,
418{ 423 size_t *msg_len, struct phr_header *headers, size_t *num_headers,
419 /* parse "HTTP/1.x" */ 424 size_t max_headers, int *ret) {
420 if ((buf = parse_http_version(buf, buf_end, major_version, minor_version, ret)) == NULL) { 425 /* parse "HTTP/1.x" */
421 return NULL; 426 if ((buf = parse_http_version(buf, buf_end, major_version, minor_version, ret)) == NULL) {
422 } 427 return NULL;
423 /* skip space */ 428 }
424 if (*buf != ' ') { 429 /* skip space */
425 *ret = -1; 430 if (*buf != ' ') {
426 return NULL; 431 *ret = -1;
427 } 432 return NULL;
428 do { 433 }
429 ++buf; 434 do {
430 } while (*buf == ' '); 435 ++buf;
431 /* parse status code, we want at least [:digit:][:digit:][:digit:]<other char> to try to parse */ 436 } while (*buf == ' ');
432 if (buf_end - buf < 4) { 437 /* parse status code, we want at least [:digit:][:digit:][:digit:]<other char> to try to parse
433 *ret = -2; 438 */
434 return NULL; 439 if (buf_end - buf < 4) {
435 } 440 *ret = -2;
436 PARSE_INT_3(status); 441 return NULL;
437 442 }
438 /* get message including preceding space */ 443 PARSE_INT_3(status);
439 if ((buf = get_token_to_eol(buf, buf_end, msg, msg_len, ret)) == NULL) { 444
440 return NULL; 445 /* get message including preceding space */
441 } 446 if ((buf = get_token_to_eol(buf, buf_end, msg, msg_len, ret)) == NULL) {
442 if (*msg_len == 0) { 447 return NULL;
443 /* ok */ 448 }
444 } else if (**msg == ' ') { 449 if (*msg_len == 0) {
445 /* remove preceding space */ 450 /* ok */
446 do { 451 } else if (**msg == ' ') {
447 ++*msg; 452 /* remove preceding space */
448 --*msg_len; 453 do {
449 } while (**msg == ' '); 454 ++*msg;
450 } else { 455 --*msg_len;
451 /* garbage found after status code */ 456 } while (**msg == ' ');
452 *ret = -1; 457 } else {
453 return NULL; 458 /* garbage found after status code */
454 } 459 *ret = -1;
455 460 return NULL;
456 return parse_headers(buf, buf_end, headers, num_headers, max_headers, ret); 461 }
462
463 return parse_headers(buf, buf_end, headers, num_headers, max_headers, ret);
457} 464}
458 465
459int phr_parse_response(const char *buf_start, size_t len, int *major_version, int *minor_version, int *status, const char **msg, size_t *msg_len, 466int phr_parse_response(const char *buf_start, size_t len, int *major_version, int *minor_version,
460 struct phr_header *headers, size_t *num_headers, size_t last_len) 467 int *status, const char **msg, size_t *msg_len, struct phr_header *headers,
461{ 468 size_t *num_headers, size_t last_len) {
462 const char *buf = buf_start, *buf_end = buf + len; 469 const char *buf = buf_start, *buf_end = buf + len;
463 size_t max_headers = *num_headers; 470 size_t max_headers = *num_headers;
464 int r; 471 int r;
465 472
466 *major_version = -1; 473 *major_version = -1;
467 *minor_version = -1; 474 *minor_version = -1;
468 *status = 0; 475 *status = 0;
469 *msg = NULL; 476 *msg = NULL;
470 *msg_len = 0; 477 *msg_len = 0;
471 *num_headers = 0; 478 *num_headers = 0;
472 479
473 /* if last_len != 0, check if the response is complete (a fast countermeasure 480 /* if last_len != 0, check if the response is complete (a fast countermeasure
474 against slowloris */ 481 against slowloris */
475 if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) { 482 if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) {
476 return r; 483 return r;
477 } 484 }
478 485
479 if ((buf = parse_response(buf, buf_end, major_version, minor_version, status, msg, msg_len, headers, num_headers, max_headers, &r)) == NULL) { 486 if ((buf = parse_response(buf, buf_end, major_version, minor_version, status, msg, msg_len,
480 return r; 487 headers, num_headers, max_headers, &r)) == NULL) {
481 } 488 return r;
482 489 }
483 return (int)(buf - buf_start); 490
491 return (int)(buf - buf_start);
484} 492}
485 493
486int phr_parse_headers(const char *buf_start, size_t len, struct phr_header *headers, size_t *num_headers, size_t last_len) 494int phr_parse_headers(const char *buf_start, size_t len, struct phr_header *headers,
487{ 495 size_t *num_headers, size_t last_len) {
488 const char *buf = buf_start, *buf_end = buf + len; 496 const char *buf = buf_start, *buf_end = buf + len;
489 size_t max_headers = *num_headers; 497 size_t max_headers = *num_headers;
490 int r; 498 int r;
491 499
492 *num_headers = 0; 500 *num_headers = 0;
493 501
494 /* if last_len != 0, check if the response is complete (a fast countermeasure 502 /* if last_len != 0, check if the response is complete (a fast countermeasure
495 against slowloris */ 503 against slowloris */
496 if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) { 504 if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) {
497 return r; 505 return r;
498 } 506 }
499 507
500 if ((buf = parse_headers(buf, buf_end, headers, num_headers, max_headers, &r)) == NULL) { 508 if ((buf = parse_headers(buf, buf_end, headers, num_headers, max_headers, &r)) == NULL) {
501 return r; 509 return r;
502 } 510 }
503 511
504 return (int)(buf - buf_start); 512 return (int)(buf - buf_start);
505} 513}
506 514
507enum { 515enum {
508 CHUNKED_IN_CHUNK_SIZE, 516 CHUNKED_IN_CHUNK_SIZE,
509 CHUNKED_IN_CHUNK_EXT, 517 CHUNKED_IN_CHUNK_EXT,
510 CHUNKED_IN_CHUNK_DATA, 518 CHUNKED_IN_CHUNK_DATA,
511 CHUNKED_IN_CHUNK_CRLF, 519 CHUNKED_IN_CHUNK_CRLF,
512 CHUNKED_IN_TRAILERS_LINE_HEAD, 520 CHUNKED_IN_TRAILERS_LINE_HEAD,
513 CHUNKED_IN_TRAILERS_LINE_MIDDLE 521 CHUNKED_IN_TRAILERS_LINE_MIDDLE
514}; 522};
515 523
516static int decode_hex(int ch) 524static int decode_hex(int ch) {
517{ 525 if ('0' <= ch && ch <= '9') {
518 if ('0' <= ch && ch <= '9') { 526 return ch - '0';
519 return ch - '0'; 527 } else if ('A' <= ch && ch <= 'F') {
520 } else if ('A' <= ch && ch <= 'F') { 528 return ch - 'A' + 0xa;
521 return ch - 'A' + 0xa; 529 } else if ('a' <= ch && ch <= 'f') {
522 } else if ('a' <= ch && ch <= 'f') { 530 return ch - 'a' + 0xa;
523 return ch - 'a' + 0xa; 531 } else {
524 } else { 532 return -1;
525 return -1; 533 }
526 }
527} 534}
528 535
529ssize_t phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf, size_t *_bufsz) 536ssize_t phr_decode_chunked(struct phr_chunked_decoder *decoder, char *buf, size_t *_bufsz) {
530{ 537 size_t dst = 0, src = 0, bufsz = *_bufsz;
531 size_t dst = 0, src = 0, bufsz = *_bufsz; 538 ssize_t ret = -2; /* incomplete */
532 ssize_t ret = -2; /* incomplete */ 539
533 540 while (1) {
534 while (1) { 541 switch (decoder->_state) {
535 switch (decoder->_state) { 542 case CHUNKED_IN_CHUNK_SIZE:
536 case CHUNKED_IN_CHUNK_SIZE: 543 for (;; ++src) {
537 for (;; ++src) { 544 int v;
538 int v; 545 if (src == bufsz) {
539 if (src == bufsz) 546 goto Exit;
540 goto Exit; 547 }
541 if ((v = decode_hex(buf[src])) == -1) { 548 if ((v = decode_hex(buf[src])) == -1) {
542 if (decoder->_hex_count == 0) { 549 if (decoder->_hex_count == 0) {
543 ret = -1; 550 ret = -1;
544 goto Exit; 551 goto Exit;
545 } 552 }
546 break; 553 break;
547 } 554 }
548 if (decoder->_hex_count == sizeof(size_t) * 2) { 555 if (decoder->_hex_count == sizeof(size_t) * 2) {
549 ret = -1; 556 ret = -1;
550 goto Exit; 557 goto Exit;
551 } 558 }
552 decoder->bytes_left_in_chunk = decoder->bytes_left_in_chunk * 16 + v; 559 decoder->bytes_left_in_chunk = decoder->bytes_left_in_chunk * 16 + v;
553 ++decoder->_hex_count; 560 ++decoder->_hex_count;
554 } 561 }
555 decoder->_hex_count = 0; 562 decoder->_hex_count = 0;
556 decoder->_state = CHUNKED_IN_CHUNK_EXT; 563 decoder->_state = CHUNKED_IN_CHUNK_EXT;
557 /* fallthru */ 564 /* fallthru */
558 case CHUNKED_IN_CHUNK_EXT: 565 case CHUNKED_IN_CHUNK_EXT:
559 /* RFC 7230 A.2 "Line folding in chunk extensions is disallowed" */ 566 /* RFC 7230 A.2 "Line folding in chunk extensions is disallowed" */
560 for (;; ++src) { 567 for (;; ++src) {
561 if (src == bufsz) 568 if (src == bufsz) {
562 goto Exit; 569 goto Exit;
563 if (buf[src] == '\012') 570 }
564 break; 571 if (buf[src] == '\012') {
565 } 572 break;
566 ++src; 573 }
567 if (decoder->bytes_left_in_chunk == 0) { 574 }
568 if (decoder->consume_trailer) { 575 ++src;
569 decoder->_state = CHUNKED_IN_TRAILERS_LINE_HEAD; 576 if (decoder->bytes_left_in_chunk == 0) {
570 break; 577 if (decoder->consume_trailer) {
571 } else { 578 decoder->_state = CHUNKED_IN_TRAILERS_LINE_HEAD;
572 goto Complete; 579 break;
573 } 580 } else {
574 } 581 goto Complete;
575 decoder->_state = CHUNKED_IN_CHUNK_DATA; 582 }
576 /* fallthru */ 583 }
577 case CHUNKED_IN_CHUNK_DATA: { 584 decoder->_state = CHUNKED_IN_CHUNK_DATA;
578 size_t avail = bufsz - src; 585 /* fallthru */
579 if (avail < decoder->bytes_left_in_chunk) { 586 case CHUNKED_IN_CHUNK_DATA: {
580 if (dst != src) 587 size_t avail = bufsz - src;
581 memmove(buf + dst, buf + src, avail); 588 if (avail < decoder->bytes_left_in_chunk) {
582 src += avail; 589 if (dst != src) {
583 dst += avail; 590 memmove(buf + dst, buf + src, avail);
584 decoder->bytes_left_in_chunk -= avail; 591 }
585 goto Exit; 592 src += avail;
586 } 593 dst += avail;
587 if (dst != src) 594 decoder->bytes_left_in_chunk -= avail;
588 memmove(buf + dst, buf + src, decoder->bytes_left_in_chunk); 595 goto Exit;
589 src += decoder->bytes_left_in_chunk; 596 }
590 dst += decoder->bytes_left_in_chunk; 597 if (dst != src) {
591 decoder->bytes_left_in_chunk = 0; 598 memmove(buf + dst, buf + src, decoder->bytes_left_in_chunk);
592 decoder->_state = CHUNKED_IN_CHUNK_CRLF; 599 }
593 } 600 src += decoder->bytes_left_in_chunk;
594 /* fallthru */ 601 dst += decoder->bytes_left_in_chunk;
595 case CHUNKED_IN_CHUNK_CRLF: 602 decoder->bytes_left_in_chunk = 0;
596 for (;; ++src) { 603 decoder->_state = CHUNKED_IN_CHUNK_CRLF;
597 if (src == bufsz) 604 }
598 goto Exit; 605 /* fallthru */
599 if (buf[src] != '\015') 606 case CHUNKED_IN_CHUNK_CRLF:
600 break; 607 for (;; ++src) {
601 } 608 if (src == bufsz) {
602 if (buf[src] != '\012') { 609 goto Exit;
603 ret = -1; 610 }
604 goto Exit; 611 if (buf[src] != '\015') {
605 } 612 break;
606 ++src; 613 }
607 decoder->_state = CHUNKED_IN_CHUNK_SIZE; 614 }
608 break; 615 if (buf[src] != '\012') {
609 case CHUNKED_IN_TRAILERS_LINE_HEAD: 616 ret = -1;
610 for (;; ++src) { 617 goto Exit;
611 if (src == bufsz) 618 }
612 goto Exit; 619 ++src;
613 if (buf[src] != '\015') 620 decoder->_state = CHUNKED_IN_CHUNK_SIZE;
614 break; 621 break;
615 } 622 case CHUNKED_IN_TRAILERS_LINE_HEAD:
616 if (buf[src++] == '\012') 623 for (;; ++src) {
617 goto Complete; 624 if (src == bufsz) {
618 decoder->_state = CHUNKED_IN_TRAILERS_LINE_MIDDLE; 625 goto Exit;
619 /* fallthru */ 626 }
620 case CHUNKED_IN_TRAILERS_LINE_MIDDLE: 627 if (buf[src] != '\015') {
621 for (;; ++src) { 628 break;
622 if (src == bufsz) 629 }
623 goto Exit; 630 }
624 if (buf[src] == '\012') 631 if (buf[src++] == '\012') {
625 break; 632 goto Complete;
626 } 633 }
627 ++src; 634 decoder->_state = CHUNKED_IN_TRAILERS_LINE_MIDDLE;
628 decoder->_state = CHUNKED_IN_TRAILERS_LINE_HEAD; 635 /* fallthru */
629 break; 636 case CHUNKED_IN_TRAILERS_LINE_MIDDLE:
630 default: 637 for (;; ++src) {
631 assert(!"decoder is corrupt"); 638 if (src == bufsz) {
632 } 639 goto Exit;
633 } 640 }
641 if (buf[src] == '\012') {
642 break;
643 }
644 }
645 ++src;
646 decoder->_state = CHUNKED_IN_TRAILERS_LINE_HEAD;
647 break;
648 default:
649 assert(!"decoder is corrupt");
650 }
651 }
634 652
635Complete: 653Complete:
636 ret = bufsz - src; 654 ret = bufsz - src;
637Exit: 655Exit:
638 if (dst != src) 656 if (dst != src) {
639 memmove(buf + dst, buf + src, bufsz - src); 657 memmove(buf + dst, buf + src, bufsz - src);
640 *_bufsz = dst; 658 }
641 return ret; 659 *_bufsz = dst;
660 return ret;
642} 661}
643 662
644int phr_decode_chunked_is_in_data(struct phr_chunked_decoder *decoder) 663int phr_decode_chunked_is_in_data(struct phr_chunked_decoder *decoder) {
645{ 664 return decoder->_state == CHUNKED_IN_CHUNK_DATA;
646 return decoder->_state == CHUNKED_IN_CHUNK_DATA;
647} 665}
648 666
649#undef CHECK_EOF 667#undef CHECK_EOF
diff --git a/plugins/picohttpparser/picohttpparser.h b/plugins/picohttpparser/picohttpparser.h
index 8f13b36f..054c2812 100644
--- a/plugins/picohttpparser/picohttpparser.h
+++ b/plugins/picohttpparser/picohttpparser.h
@@ -30,7 +30,7 @@
30#include <sys/types.h> 30#include <sys/types.h>
31 31
32#ifdef _MSC_VER 32#ifdef _MSC_VER
33#define ssize_t intptr_t 33# define ssize_t intptr_t
34#endif 34#endif
35 35
36#ifdef __cplusplus 36#ifdef __cplusplus
@@ -40,30 +40,33 @@ extern "C" {
40/* contains name and value of a header (name == NULL if is a continuing line 40/* contains name and value of a header (name == NULL if is a continuing line
41 * of a multiline header */ 41 * of a multiline header */
42struct phr_header { 42struct phr_header {
43 const char *name; 43 const char *name;
44 size_t name_len; 44 size_t name_len;
45 const char *value; 45 const char *value;
46 size_t value_len; 46 size_t value_len;
47}; 47};
48 48
49/* returns number of bytes consumed if successful, -2 if request is partial, 49/* returns number of bytes consumed if successful, -2 if request is partial,
50 * -1 if failed */ 50 * -1 if failed */
51int phr_parse_request(const char *buf, size_t len, const char **method, size_t *method_len, const char **path, size_t *path_len, 51int phr_parse_request(const char *buf, size_t len, const char **method, size_t *method_len,
52 int *major_version, int *minor_version, struct phr_header *headers, size_t *num_headers, size_t last_len); 52 const char **path, size_t *path_len, int *major_version, int *minor_version,
53 struct phr_header *headers, size_t *num_headers, size_t last_len);
53 54
54/* ditto */ 55/* ditto */
55int phr_parse_response(const char *_buf, size_t len, int *major_version, int *minor_version, int *status, const char **msg, size_t *msg_len, 56int phr_parse_response(const char *_buf, size_t len, int *major_version, int *minor_version,
56 struct phr_header *headers, size_t *num_headers, size_t last_len); 57 int *status, const char **msg, size_t *msg_len, struct phr_header *headers,
58 size_t *num_headers, size_t last_len);
57 59
58/* ditto */ 60/* ditto */
59int phr_parse_headers(const char *buf, size_t len, struct phr_header *headers, size_t *num_headers, size_t last_len); 61int phr_parse_headers(const char *buf, size_t len, struct phr_header *headers, size_t *num_headers,
62 size_t last_len);
60 63
61/* should be zero-filled before start */ 64/* should be zero-filled before start */
62struct phr_chunked_decoder { 65struct phr_chunked_decoder {
63 size_t bytes_left_in_chunk; /* number of bytes left in current chunk */ 66 size_t bytes_left_in_chunk; /* number of bytes left in current chunk */
64 char consume_trailer; /* if trailing headers should be consumed */ 67 char consume_trailer; /* if trailing headers should be consumed */
65 char _hex_count; 68 char _hex_count;
66 char _state; 69 char _state;
67}; 70};
68 71
69/* the function rewrites the buffer given as (buf, bufsz) removing the chunked- 72/* the function rewrites the buffer given as (buf, bufsz) removing the chunked-