commit 85d6ee233ea16724799840840cb19c955cc0842f
parent a931b2c32a110fc26258afe16cc77d1c7f5d1e44
Author: Hiltjo Posthuma <hiltjo@codemadness.org>
Date: Mon, 9 Aug 2021 18:54:46 +0200
check received content length if the Content-Length header is set
This prevents corrupted downloads, noticed when my connection was crappy.
Diffstat:
M | hurl.c | | | 58 | ++++++++++++++++++++++++++++++++++++++++++++++++++-------- |
1 file changed, 50 insertions(+), 8 deletions(-)
diff --git a/hurl.c b/hurl.c
@@ -64,6 +64,32 @@ sighandler(int signo)
}
int
+parse_content_length(const char *s, size_t *length)
+{
+ const char *p;
+ char *end;
+ long long l;
+
+ if (!(p = strcasestr(s, "\r\nContent-Length:")))
+ return -1;
+
+ p += sizeof("\r\nContent-Length:") - 1;
+ p += strspn(p, " \t");
+
+ if (!isdigit(*p))
+ return -1;
+
+ errno = 0;
+ l = strtoll(p, &end, 10);
+ if (errno || p == end || (*end != '\0' && *end != '\r') || l < 0)
+ return -1;
+
+ *length = l;
+
+ return 0;
+}
+
+int
uri_parse(const char *s, struct uri *u)
{
const char *p = s;
@@ -221,9 +247,9 @@ https_request(void)
struct tls *t;
char buf[READ_BUF_SIZ], *p;
const char *errstr;
- size_t n, len;
+ size_t bodylen, expectedlen, n, len;
ssize_t r;
- int fd = -1, httpok = 0, ret = 1, stdport;
+ int cs, fd = -1, httpok = 0, ret = 1, stdport;
if (pledge("stdio dns inet rpath unveil", NULL) == -1)
err(1, "pledge");
@@ -308,7 +334,9 @@ https_request(void)
goto err;
}
*p = '\0'; /* NUL terminate header part */
+ cs = parse_content_length(buf, &expectedlen);
p += strlen("\r\n\r\n");
+ bodylen = strlen(p); /* (partial) body after header */
if (httpok) {
n = len - (p - buf);
@@ -336,6 +364,7 @@ https_request(void)
goto err;
}
len += r;
+ bodylen += r;
if (httpok) {
r = fwrite(buf, 1, r, stdout);
@@ -349,10 +378,15 @@ https_request(void)
break;
}
if (config_maxresponsesiz && len >= config_maxresponsesiz) {
- fprintf(stderr, "tls_read: response too big: %zu >= %zu\n",
+ fprintf(stderr, "response too big: %zu >= %zu\n",
len, config_maxresponsesiz);
goto err;
}
+ if (cs != -1 && expectedlen != bodylen) {
+ fprintf(stderr, "Content-Length mismatch: %zu expected != %zu received\n",
+ expectedlen, bodylen);
+ goto err;
+ }
ret = 0;
err:
@@ -368,9 +402,9 @@ int
http_request(void)
{
char buf[READ_BUF_SIZ], *p;
- size_t n, len;
+ size_t bodylen, expectedlen, n, len;
ssize_t r;
- int fd = -1, httpok = 0, ret = 1, stdport;
+ int cs, fd = -1, httpok = 0, ret = 1, stdport;
if (pledge("stdio dns inet", NULL) == -1)
err(1, "pledge");
@@ -426,7 +460,9 @@ http_request(void)
goto err;
}
*p = '\0'; /* NUL terminate header part */
+ cs = parse_content_length(buf, &expectedlen);
p += strlen("\r\n\r\n");
+ bodylen = strlen(p); /* (partial) body after header */
if (httpok) {
n = len - (p - buf);
@@ -451,6 +487,7 @@ http_request(void)
goto err;
}
len += r;
+ bodylen += r;
if (httpok) {
r = fwrite(buf, 1, r, stdout);
@@ -464,10 +501,15 @@ http_request(void)
break;
}
if (config_maxresponsesiz && len >= config_maxresponsesiz) {
- fprintf(stderr, "read: response too big: %zu >= %zu\n",
+ fprintf(stderr, "response too big: %zu >= %zu\n",
len, config_maxresponsesiz);
goto err;
}
+ if (cs != -1 && expectedlen != bodylen) {
+ fprintf(stderr, "Content-Length mismatch: %zu expected != %zu received\n",
+ expectedlen, bodylen);
+ goto err;
+ }
ret = 0;
err:
@@ -534,7 +576,7 @@ gopher_request(void)
break;
}
if (config_maxresponsesiz && len >= config_maxresponsesiz) {
- fprintf(stderr, "read: response too big: %zu >= %zu\n",
+ fprintf(stderr, "response too big: %zu >= %zu\n",
len, config_maxresponsesiz);
goto err;
}
@@ -633,7 +675,7 @@ gophers_request(void)
break;
}
if (config_maxresponsesiz && len >= config_maxresponsesiz) {
- fprintf(stderr, "read: response too big: %zu >= %zu\n",
+ fprintf(stderr, "response too big: %zu >= %zu\n",
len, config_maxresponsesiz);
goto err;
}