check received content length if the Content-Length header is set - hurl - Gopher/HTTP/HTTPS file grabber
 (HTM) git clone git://git.codemadness.org/hurl
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit 85d6ee233ea16724799840840cb19c955cc0842f
 (DIR) parent a931b2c32a110fc26258afe16cc77d1c7f5d1e44
 (HTM) 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(-)
       ---
 (DIR) 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;
                }