quark

quark web server
git clone git://git.suckless.org/quark
Log | Files | Refs | LICENSE

commit db4e35d3d5cbd5bfac61cfb8edadd47c4608a864
parent 6b508a0e073fd4e29540f8808d919ff72c1893fb
Author: Laslo Hunhold <dev@frign.de>
Date:   Thu, 23 Jul 2020 18:16:08 +0200

Refactor range-parsing

Quark previously didn't really handle suffix-range-requests
(those of the form "-num", asking for the last num bytes) properly
and also did not catch the error when the lower in the range
"lower-upper" was actually larger than or equal to the size of the
requested file.

I always planned to refactor the parsing but got the motivation by
Eric Radman <ericshane@eradman.com>, who kindly reported the latter bug
to me.

Signed-off-by: Laslo Hunhold <dev@frign.de>

Diffstat:
Mhttp.c | 109+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------
1 file changed, 84 insertions(+), 25 deletions(-)

diff --git a/http.c b/http.c @@ -562,36 +562,95 @@ http_send_response(int fd, struct request *r) return http_send_status(fd, S_BAD_REQUEST); } *(q++) = '\0'; - if (p[0]) { + + /* + * byte-range=first\0last... + * ^ ^ + * | | + * p q + */ + + /* + * make sure we only have a single range, + * and not a comma separated list, which we + * will refuse to accept out of spite towards + * this horrible part of the spec + */ + if (strchr(q, ',')) { + goto not_satisfiable; + } + + if (p[0] != '\0') { + /* + * Range has format "first-last" or "first-", + * i.e. return bytes 'first' to 'last' (or the + * last byte if 'last' is not given), + * inclusively, and byte-numbering beginning at 0 + */ lower = strtonum(p, 0, LLONG_MAX, &err); - } - if (!err && q[0]) { + if (!err) { + if (q[0] != '\0') { + upper = strtonum(q, 0, LLONG_MAX, + &err); + } else { + upper = st.st_size - 1; + } + } + if (err) { + /* one of the strtonum()'s failed */ + return http_send_status(fd, S_BAD_REQUEST); + } + + /* check ranges */ + if (lower > upper || lower >= st.st_size) { + goto not_satisfiable; + } + + /* adjust upper limit to be at most the last byte */ + upper = MIN(upper, st.st_size - 1); + } else { + /* + * Range has format "-num", i.e. return the 'num' + * last bytes + */ + + /* + * use upper as a temporary storage for 'num', + * as we know 'upper' is st.st_size - 1 + */ upper = strtonum(q, 0, LLONG_MAX, &err); - } - if (err) { - return http_send_status(fd, S_BAD_REQUEST); - } + if (err) { + return http_send_status(fd, S_BAD_REQUEST); + } - /* check range */ - if (lower < 0 || upper < 0 || lower > upper) { - if (dprintf(fd, - "HTTP/1.1 %d %s\r\n" - "Date: %s\r\n" - "Content-Range: bytes */%zu\r\n" - "Connection: close\r\n" - "\r\n", - S_RANGE_NOT_SATISFIABLE, - status_str[S_RANGE_NOT_SATISFIABLE], - timestamp(time(NULL), t), - st.st_size) < 0) { - return S_REQUEST_TIMEOUT; + /* determine lower */ + if (upper > st.st_size) { + /* more bytes requested than we have */ + lower = 0; + } else { + lower = st.st_size - upper; } - return S_RANGE_NOT_SATISFIABLE; - } - /* adjust upper limit */ - if (upper >= st.st_size) - upper = st.st_size-1; + /* set upper to the correct value */ + upper = st.st_size - 1; + } + goto satisfiable; +not_satisfiable: + if (dprintf(fd, + "HTTP/1.1 %d %s\r\n" + "Date: %s\r\n" + "Content-Range: bytes */%zu\r\n" + "Connection: close\r\n" + "\r\n", + S_RANGE_NOT_SATISFIABLE, + status_str[S_RANGE_NOT_SATISFIABLE], + timestamp(time(NULL), t), + st.st_size) < 0) { + return S_REQUEST_TIMEOUT; + } + return S_RANGE_NOT_SATISFIABLE; +satisfiable: + ; } /* mime */