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