Parse Content-Range with missing right hand side

Fixes #144
This commit is contained in:
Justine Tunney 2021-04-01 18:51:12 -07:00
parent 7abca1531f
commit 83abd68029
6 changed files with 59 additions and 26 deletions

View file

@ -26,7 +26,7 @@
*/
ssize_t ParseContentLength(const char *s, size_t n) {
int i, r = 0;
if (!n) return -1;
if (!n) return 0;
for (i = 0; i < n; ++i) {
if (!isdigit(s[i])) return -1;
if (__builtin_mul_overflow(r, 10, &r)) return -1;

View file

@ -16,6 +16,7 @@
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/macros.internal.h"
#include "libc/str/str.h"
#include "net/http/http.h"
@ -48,14 +49,18 @@ bool ParseHttpRange(const char *p, size_t n, long resourcelength,
}
if (n && *p == '-') {
++p, --n;
length = 0;
while (n && '0' <= *p && *p <= '9') {
if (__builtin_mul_overflow(length, 10, &length)) return false;
if (__builtin_add_overflow(length, *p - '0', &length)) return false;
++p, --n;
if (!n) {
length = MAX(start, resourcelength) - start;
} else {
length = 0;
while (n && '0' <= *p && *p <= '9') {
if (__builtin_mul_overflow(length, 10, &length)) return false;
if (__builtin_add_overflow(length, *p - '0', &length)) return false;
++p, --n;
}
if (__builtin_add_overflow(length, 1, &length)) return false;
if (__builtin_sub_overflow(length, start, &length)) return false;
}
if (__builtin_add_overflow(length, 1, &length)) return false;
if (__builtin_sub_overflow(length, start, &length)) return false;
} else if (__builtin_sub_overflow(resourcelength, start, &length)) {
return false;
}

View file

@ -87,6 +87,8 @@ int ParseHttpRequest(struct HttpRequest *r, const char *p, size_t n) {
return ebadmsg();
}
break;
} else if (!('A' <= c && c <= 'Z')) {
return ebadmsg();
}
if (++r->i == n) break;
c = p[r->i] & 0xff;

View file

@ -20,7 +20,7 @@
#include "net/http/http.h"
TEST(ParseContentLength, test) {
EXPECT_EQ(-1, ParseContentLength("", 0));
EXPECT_EQ(0, ParseContentLength("", 0));
EXPECT_EQ(-1, ParseContentLength("-1", 2));
EXPECT_EQ(-1, ParseContentLength("-2", 2));
EXPECT_EQ(0, ParseContentLength("0", 1));

View file

@ -52,6 +52,14 @@ TEST(ParseHttpRange, testOffset) {
EXPECT_EQ(10, length);
}
TEST(ParseHttpRange, testEmptySecond) {
long start, length;
const char *s = "bytes=0-";
EXPECT_TRUE(ParseHttpRange(s, strlen(s), 100, &start, &length));
EXPECT_EQ(0, start);
EXPECT_EQ(100, length);
}
TEST(ParseHttpRange, testToEnd) {
long start, length;
const char *s = "bytes=40";

View file

@ -463,7 +463,7 @@ static void ProgramRedirect(int code, const char *src, const char *dst) {
} else {
i = redirects.n;
redirects.p = xrealloc(redirects.p, (i + 1) * sizeof(*redirects.p));
for (j = i; j > 0; --j) {
for (j = i; j; --j) {
if (CompareSlices(r.path, r.pathlen, redirects.p[j - 1].path,
redirects.p[j - 1].pathlen) < 0) {
redirects.p[j] = redirects.p[j - 1];
@ -526,6 +526,10 @@ static const char *GetContentType2(const char *path, size_t n) {
}
static const char *GetContentType(struct Asset *a, const char *path, size_t n) {
const char *r;
if (a->file && (r = GetContentType2(a->file->path, strlen(a->file->path)))) {
return r;
}
return firstnonnull(
GetContentType2(path, n),
firstnonnull(GetContentType2(ZIP_LFILE_NAME(zmap + a->lf),
@ -1114,6 +1118,19 @@ static void ParseRequestUri(void) {
u.data = inbuf.p + msg.uri.a;
u.size = msg.uri.b - msg.uri.a;
memset(&request, 0, sizeof(request));
if (u.size > 8 && !memcmp(u.data, "http", 4)) {
/*
* convert http://www.foo.com/index.html -> /www.foo.com/index.html
*/
if (u.data[4] == ':' && u.data[5] == '/' && u.data[6] == '/') {
u.data += 6;
u.size -= 6;
} else if (u.data[4] == 's' && u.data[5] == ':' && u.data[6] == '/' &&
u.data[7] == '/') {
u.data += 7;
u.size -= 7;
}
}
u.q = u.p = FreeLater(xmalloc(u.size * 2));
ParsePath(&u, &request.path);
if (u.c == '?') ParseParams(&u, &request.params);
@ -1201,7 +1218,7 @@ static char *ServeError(int code, const char *reason) {
contentlength = reasonlen + 2;
stpcpy(stpcpy(content, reason), "\r\n");
WARNF("%s %s %`'.*s %d %s", clientaddrstr, kHttpMethod[msg.method],
request.path.n, request.path.p, code, reason);
msg.uri.b - msg.uri.a, inbuf.p + msg.uri.a, code, reason);
return p;
}
@ -1233,9 +1250,10 @@ static char *AppendContentLength(char *p, size_t n) {
static char *AppendContentRange(char *p, long rangestart, long rangelength,
long contentlength) {
long endrange;
CHECK_GE(rangestart + rangelength, rangestart);
CHECK_GT(rangelength, 0);
CHECK_GT(rangestart + rangelength, rangestart);
CHECK_LE(rangestart + rangelength, contentlength);
if (__builtin_add_overflow(rangestart, rangelength, &endrange)) abort();
endrange = rangestart + rangelength - 1;
p = AppendHeaderName(p, "Content-Range");
p = stpcpy(p, "bytes ");
p += uint64toarray_radix10(rangestart, p);
@ -1361,6 +1379,7 @@ static char *ServeAsset(struct Asset *a, const char *path, size_t pathlen) {
if (ParseHttpRange(inbuf.p + msg.headers[kHttpRange].a,
msg.headers[kHttpRange].b - msg.headers[kHttpRange].a,
contentlength, &rangestart, &rangelength)) {
LOGF("rangestart = %ld rangelength = %ld", rangestart, rangelength);
p = SetStatus(206, "Partial Content");
p = AppendContentRange(p, rangestart, rangelength, contentlength);
content = AddRange(content, rangestart, rangelength);
@ -2139,7 +2158,7 @@ Content-Length: 0\r\n\
static void LogClose(const char *reason) {
if (amtread) {
WARNF("%s %s with %,ld bytes unprocessed", clientaddrstr, reason);
WARNF("%s %s with %,ld bytes unprocessed", clientaddrstr, reason, amtread);
} else {
DEBUGF("%s %s", clientaddrstr, reason);
}
@ -2301,25 +2320,22 @@ static char *HandleMessage(void) {
if (httpversion > 101) {
return ServeError(505, "HTTP Version Not Supported");
}
if (msg.method > kHttpOptions ||
if (HasHeader(kHttpExpect) && !HeaderEquals(kHttpExpect, "100-continue")) {
return ServeError(417, "Expectation Failed");
}
if (msg.method == kHttpConnect ||
(HasHeader(kHttpTransferEncoding) &&
!HeaderEquals(kHttpTransferEncoding, "identity"))) {
return ServeError(501, "Not Implemented");
}
if (HasHeader(kHttpExpect) && !HeaderEquals(kHttpExpect, "100-continue")) {
return ServeError(417, "Expectation Failed");
if (!HasHeader(kHttpContentLength) &&
(msg.method == kHttpPost || msg.method == kHttpPut)) {
return ServeError(411, "Length Required");
}
if ((cl = ParseContentLength(inbuf.p + msg.headers[kHttpContentLength].a,
msg.headers[kHttpContentLength].b -
msg.headers[kHttpContentLength].a)) == -1) {
if (HasHeader(kHttpContentLength)) {
return ServeError(400, "Bad Request");
} else if (msg.method != kHttpGet && msg.method != kHttpHead &&
msg.method != kHttpDelete && msg.method != kHttpOptions) {
return ServeError(411, "Length Required");
} else {
cl = 0;
}
return ServeError(400, "Bad Request");
}
need = hdrsize + cl; /* synchronization is possible */
if (need > inbuf.n) {
@ -2363,7 +2379,7 @@ static char *HandleMessage(void) {
return ServeServerOptions();
}
if (!IsAcceptableHttpRequestPath(request.path.p, request.path.n)) {
WARNF("%s could not parse request request %`'.*s", clientaddrstr,
WARNF("%s could not parse request %`'.*s", clientaddrstr,
msg.uri.b - msg.uri.a, inbuf.p + msg.uri.a);
connectionclose = true;
return ServeError(400, "Bad Request");
@ -2399,7 +2415,9 @@ static bool HandleRequest(void) {
p = HandleMessage();
} else {
httpversion = 101;
connectionclose = true;
p = ServeError(400, "Bad Request");
DEBUGF("%s received garbage %`'.*s", clientaddrstr, amtread, inbuf.p);
}
if (!msgsize) {
amtread = 0;