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 210 : #define likely(x) __builtin_expect(!!(x), 1)
41 129 : #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 336 : if (buf == buf_end) { \
57 12 : *ret = -2; \
58 12 : return NULL; \
59 12 : }
60 :
61 : #define EXPECT_CHAR_NO_CHECK(ch) \
62 207 : if (*buf++ != ch) { \
63 0 : *ret = -1; \
64 0 : return NULL; \
65 0 : }
66 :
67 : #define EXPECT_CHAR(ch) \
68 81 : CHECK_EOF(); \
69 81 : EXPECT_CHAR_NO_CHECK(ch);
70 :
71 : #define ADVANCE_TOKEN(tok, toklen) \
72 9 : do { \
73 9 : const char *tok_start = buf; \
74 9 : static const char ALIGNED(16) ranges2[16] = "\000\040\177\177"; \
75 9 : int found2; \
76 9 : buf = findchar_fast(buf, buf_end, ranges2, 4, &found2); \
77 9 : if (!found2) { \
78 0 : CHECK_EOF(); \
79 0 : } \
80 9 : while (1) { \
81 9 : if (*buf == ' ') { \
82 9 : break; \
83 9 : } 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 9 : ++buf; \
90 0 : CHECK_EOF(); \
91 0 : } \
92 9 : tok = tok_start; \
93 9 : toklen = (size_t)(buf - tok_start); \
94 9 : } 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 132 : {
107 132 : *found = 0;
108 132 : #if __SSE4_2__
109 132 : if (likely(buf_end - buf >= 16)) {
110 111 : __m128i ranges16 = _mm_loadu_si128((const __m128i *)ranges);
111 :
112 111 : size_t left = (size_t)((buf_end - buf) & ~15);
113 126 : do {
114 126 : __m128i b16 = _mm_loadu_si128((const __m128i *)buf);
115 126 : int r = (int)_mm_cmpestri(ranges16, (int)ranges_size, b16, 16, _SIDD_LEAST_SIGNIFICANT | _SIDD_CMP_RANGES | _SIDD_UBYTE_OPS);
116 126 : if (unlikely(r != 16)) {
117 111 : buf += r;
118 111 : *found = 1;
119 111 : break;
120 111 : }
121 15 : buf += 16;
122 15 : left -= 16;
123 15 : } while (likely(left != 0));
124 111 : }
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 132 : }
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 54 : {
136 54 : const char *token_start = buf;
137 :
138 54 : #ifdef __SSE4_2__
139 54 : static const char ALIGNED(16) ranges1[16] = "\0\010" /* allow HT */
140 54 : "\012\037" /* allow SP and up to but not including DEL */
141 54 : "\177\177"; /* allow chars w. MSB set */
142 54 : int found;
143 54 : buf = findchar_fast(buf, buf_end, ranges1, 6, &found);
144 54 : if (found)
145 51 : 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 3 : for (;; ++buf) {
173 3 : CHECK_EOF();
174 3 : if (unlikely(!IS_PRINTABLE_ASCII(*buf))) {
175 3 : if ((likely((unsigned char)*buf < '\040') && likely(*buf != '\011')) || unlikely(*buf == '\177')) {
176 3 : goto FOUND_CTL;
177 3 : }
178 3 : }
179 3 : }
180 54 : FOUND_CTL:
181 54 : if (likely(*buf == '\015')) {
182 54 : ++buf;
183 108 : EXPECT_CHAR('\012');
184 108 : *token_len = (size_t)(buf - 2 - token_start);
185 108 : } 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 54 : *token = token_start;
193 :
194 54 : return buf;
195 54 : }
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 45 : if (*buf < '0' || '9' < *buf) { \
227 0 : buf++; \
228 0 : *ret = -1; \
229 0 : return NULL; \
230 0 : } \
231 45 : *(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 69 : {
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 69 : static const char ALIGNED(16) ranges[] = "\x00 " /* control chars and up to SP */
251 69 : "\"\"" /* 0x22 */
252 69 : "()" /* 0x28,0x29 */
253 69 : ",," /* 0x2c */
254 69 : "//" /* 0x2f */
255 69 : ":@" /* 0x3a-0x40 */
256 69 : "[]" /* 0x5b-0x5d */
257 69 : "{\xff"; /* 0x7b-0xff */
258 69 : const char *buf_start = buf;
259 69 : int found;
260 69 : buf = findchar_fast(buf, buf_end, ranges, sizeof(ranges) - 1, &found);
261 69 : if (!found) {
262 18 : CHECK_EOF();
263 18 : }
264 87 : while (1) {
265 87 : if (*buf == next_char) {
266 57 : break;
267 57 : } else if (!token_char_map[(unsigned char)*buf]) {
268 3 : *ret = -1;
269 3 : return NULL;
270 3 : }
271 27 : ++buf;
272 27 : CHECK_EOF();
273 18 : }
274 57 : *token = buf_start;
275 57 : *token_len = (size_t)(buf - buf_start);
276 57 : return buf;
277 69 : }
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 18 : {
282 : /* we want at least [HTTP/1.<two chars>] to try to parse */
283 18 : if (buf_end - buf < 9) {
284 0 : *ret = -2;
285 0 : return NULL;
286 0 : }
287 18 : EXPECT_CHAR_NO_CHECK('H');
288 18 : EXPECT_CHAR_NO_CHECK('T');
289 18 : EXPECT_CHAR_NO_CHECK('T');
290 18 : EXPECT_CHAR_NO_CHECK('P');
291 18 : EXPECT_CHAR_NO_CHECK('/');
292 18 : EXPECT_CHAR_NO_CHECK('1');
293 18 : EXPECT_CHAR_NO_CHECK('.');
294 18 : PARSE_INT(minor_version, 1);
295 18 : return buf;
296 18 : }
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 18 : {
301 63 : for (;; ++*num_headers) {
302 63 : CHECK_EOF();
303 63 : if (*buf == '\015') {
304 18 : ++buf;
305 18 : EXPECT_CHAR('\012');
306 18 : break;
307 45 : } else if (*buf == '\012') {
308 0 : ++buf;
309 0 : break;
310 0 : }
311 45 : if (*num_headers == max_headers) {
312 0 : *ret = -1;
313 0 : return NULL;
314 0 : }
315 45 : 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 45 : 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 45 : if (headers[*num_headers].name_len == 0) {
322 0 : *ret = -1;
323 0 : return NULL;
324 0 : }
325 45 : ++buf;
326 90 : for (;; ++buf) {
327 90 : CHECK_EOF();
328 90 : if (!(*buf == ' ' || *buf == '\t')) {
329 45 : break;
330 45 : }
331 90 : }
332 45 : } else {
333 0 : headers[*num_headers].name = NULL;
334 0 : headers[*num_headers].name_len = 0;
335 0 : }
336 45 : const char *value;
337 45 : size_t value_len;
338 45 : 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 45 : const char *value_end = value + value_len;
343 45 : for (; value_end != value; --value_end) {
344 42 : const char c = *(value_end - 1);
345 42 : if (!(c == ' ' || c == '\t')) {
346 42 : break;
347 42 : }
348 42 : }
349 45 : headers[*num_headers].value = value;
350 45 : headers[*num_headers].value_len = (size_t)(value_end - value);
351 45 : }
352 18 : return buf;
353 18 : }
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 24 : {
359 : /* skip first empty line (some clients add CRLF after POST content) */
360 24 : CHECK_EOF();
361 24 : if (*buf == '\015') {
362 0 : ++buf;
363 0 : EXPECT_CHAR('\012');
364 24 : } else if (*buf == '\012') {
365 0 : ++buf;
366 0 : }
367 :
368 : /* parse request line */
369 24 : if ((buf = parse_token(buf, buf_end, method, method_len, ' ', ret)) == NULL) {
370 12 : return NULL;
371 12 : }
372 12 : do {
373 12 : ++buf;
374 12 : CHECK_EOF();
375 9 : } while (*buf == ' ');
376 9 : ADVANCE_TOKEN(*path, *path_len);
377 9 : do {
378 9 : ++buf;
379 9 : CHECK_EOF();
380 9 : } while (*buf == ' ');
381 9 : if (*method_len == 0 || *path_len == 0) {
382 0 : *ret = -1;
383 0 : return NULL;
384 0 : }
385 9 : if ((buf = parse_http_version(buf, buf_end, minor_version, ret)) == NULL) {
386 0 : return NULL;
387 0 : }
388 9 : if (*buf == '\015') {
389 9 : ++buf;
390 18 : EXPECT_CHAR('\012');
391 18 : } else if (*buf == '\012') {
392 0 : ++buf;
393 0 : } else {
394 0 : *ret = -1;
395 0 : return NULL;
396 0 : }
397 :
398 9 : return parse_headers(buf, buf_end, headers, num_headers, max_headers, ret);
399 9 : }
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 24 : {
404 24 : const char *buf = buf_start, *buf_end = buf_start + len;
405 24 : size_t max_headers = *num_headers;
406 24 : int r = 0;
407 :
408 24 : *method = NULL;
409 24 : *method_len = 0;
410 24 : *path = NULL;
411 24 : *path_len = 0;
412 24 : *minor_version = -1;
413 24 : *num_headers = 0;
414 :
415 : /* if last_len != 0, check if the request is complete (a fast countermeasure
416 : againt slowloris */
417 24 : if (last_len != 0 && is_complete(buf, buf_end, last_len, &r) == NULL) {
418 0 : return r;
419 0 : }
420 :
421 24 : if ((buf = parse_request(buf, buf_end, method, method_len, path, path_len, minor_version, headers, num_headers, max_headers,
422 24 : &r)) == NULL) {
423 15 : return r;
424 15 : }
425 :
426 9 : return (int)(buf - buf_start);
427 24 : }
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 0 : {
545 0 : size_t dst = 0, src = 0, bufsz = *_bufsz;
546 0 : ssize_t ret = -2; /* incomplete */
547 :
548 0 : while (1) {
549 0 : 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 0 : case CHUNKED_IN_CHUNK_CRLF:
613 0 : for (;; ++src) {
614 0 : if (src == bufsz)
615 0 : goto Exit;
616 0 : if (buf[src] != '\015')
617 0 : break;
618 0 : }
619 0 : if (buf[src] != '\012') {
620 0 : ret = -1;
621 0 : goto Exit;
622 0 : }
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 0 : }
651 0 : }
652 :
653 0 : Complete:
654 0 : ret = (ssize_t)(bufsz - src);
655 0 : Exit:
656 0 : if (dst != src)
657 0 : memmove(buf + dst, buf + src, bufsz - src);
658 0 : *_bufsz = dst;
659 0 : 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
|