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) { ssize_t ParseContentLength(const char *s, size_t n) {
int i, r = 0; int i, r = 0;
if (!n) return -1; if (!n) return 0;
for (i = 0; i < n; ++i) { for (i = 0; i < n; ++i) {
if (!isdigit(s[i])) return -1; if (!isdigit(s[i])) return -1;
if (__builtin_mul_overflow(r, 10, &r)) 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 │ │ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR │
│ PERFORMANCE OF THIS SOFTWARE. │ │ PERFORMANCE OF THIS SOFTWARE. │
╚─────────────────────────────────────────────────────────────────────────────*/ ╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/macros.internal.h"
#include "libc/str/str.h" #include "libc/str/str.h"
#include "net/http/http.h" #include "net/http/http.h"
@ -48,14 +49,18 @@ bool ParseHttpRange(const char *p, size_t n, long resourcelength,
} }
if (n && *p == '-') { if (n && *p == '-') {
++p, --n; ++p, --n;
length = 0; if (!n) {
while (n && '0' <= *p && *p <= '9') { length = MAX(start, resourcelength) - start;
if (__builtin_mul_overflow(length, 10, &length)) return false; } else {
if (__builtin_add_overflow(length, *p - '0', &length)) return false; length = 0;
++p, --n; 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)) { } else if (__builtin_sub_overflow(resourcelength, start, &length)) {
return false; return false;
} }

View File

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

View File

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

View File

@ -52,6 +52,14 @@ TEST(ParseHttpRange, testOffset) {
EXPECT_EQ(10, length); 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) { TEST(ParseHttpRange, testToEnd) {
long start, length; long start, length;
const char *s = "bytes=40"; const char *s = "bytes=40";

View File

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