nkiss

Unnamed repository; edit this file 'description' to name the repository.
git clone git://git.nihaljere.xyz/nkiss
Log | Files | Refs

commit 94f5d599d18442fe4f4339e93b38a0bdd83ac87e
parent 7470481c1eff46db13b807c8da0d13aed08c2b1d
Author: Nihal Jere <nihal@nihaljere.xyz>
Date:   Thu, 18 Feb 2021 12:00:21 -0600

http.c: add redirection support

Diffstat:
Mhttp.c | 110++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------
1 file changed, 73 insertions(+), 37 deletions(-)

diff --git a/http.c b/http.c @@ -16,6 +16,7 @@ #include "util.h" #define READ_BUF_SIZ 16384 +#define MAX_REDIRECTIONS 5 #ifndef TLS_CA_CERT_FILE #define TLS_CA_CERT_FILE "/etc/ssl/cert.pem" @@ -161,7 +162,7 @@ https_request(void) const char *errstr; size_t n, len; ssize_t r; - int status = 0, ret = 1, stdport; + int status = 0, stdport; if (!(t = tls_client())) { fprintf(stderr, "tls_client: %s\n", tls_error(t)); @@ -220,9 +221,19 @@ https_request(void) } buf[len] = '\0'; - if (!strncmp(buf, "HTTP/1.0 200 ", sizeof("HTTP/1.0 200 ") - 1) || - !strncmp(buf, "HTTP/1.1 200 ", sizeof("HTTP/1.1 200 ") - 1)) - status = 1; + if (!strncmp(buf, "HTTP/1.0 ", sizeof("HTTP/1.0 ") - 1) || + !strncmp(buf, "HTTP/1.1 ", sizeof("HTTP/1.1 ") - 1)) { + p = buf + sizeof("HTTP/1.1 ") - 1; + /* OK, we can read response */ + if (!strncmp(p, "200 ", sizeof("200 ") - 1)) { + status = 2; + /* redirection */ + } else if (!strncmp(p, "302 ", sizeof("302 ") - 1) || + !strncmp(p, "303 ", sizeof("303 ") - 1) || + !strncmp(p, "307 ", sizeof("307 ") - 1)) { + status = 3; + } + } if (!(p = strstr(buf, "\r\n\r\n"))) { fprintf(stderr, "no HTTP header found or header too big\n"); @@ -231,13 +242,28 @@ https_request(void) *p = '\0'; /* NUL terminate header part */ p += strlen("\r\n\r\n"); - if (status) { + if (status == 2) { n = len - (p - buf); r = fwrite(p, 1, n, dest); if (ferror(dest)) { fprintf(stderr, "fwrite: %s\n", strerror(errno)); goto err; } + } else if (status == 3) { + p = strstr(buf, "\r\nLocation: "); + if (p == NULL) { + fprintf(stderr, "http: no Location given for 3xx response\n"); + goto err; + } + p += sizeof("\r\nLocation: ") - 1; + + p = memccpy(url, p, '\r', 2048); + if (p == NULL) { + fprintf(stderr, "http: Location url too long\n"); + goto err; + } + + *(p-1) = '\0'; } else { /* if not 200 OK print header */ fputs(buf, stderr); @@ -258,7 +284,7 @@ https_request(void) } len += r; - if (status) { + if (status == 2) { r = fwrite(buf, 1, r, dest); if (ferror(dest)) { fprintf(stderr, "fwrite: %s\n", strerror(errno)); @@ -267,7 +293,6 @@ https_request(void) } } - ret = 0; err: if (t) { @@ -275,7 +300,7 @@ err: tls_free(t); } - return status ? ret : 2; + return status; } static int @@ -284,7 +309,7 @@ http_request(void) char buf[READ_BUF_SIZ], *p; size_t n, len; ssize_t r; - int fd = -1, status = 0, ret = 1, stdport; + int fd = -1, status = 0, stdport; fd = edial(u.host, u.port); @@ -357,6 +382,15 @@ http_request(void) fprintf(stderr, "http: no Location given for 3xx response\n"); goto err; } + p += sizeof("\r\nLocation: ") - 1; + + p = memccpy(url, p, '\r', 2048); + if (p == NULL) { + fprintf(stderr, "http: Location url too long\n"); + goto err; + } + + *(p-1) = '\0'; } else { /* if not 200 OK print header */ fputs(buf, stderr); @@ -383,56 +417,58 @@ http_request(void) } } - ret = 0; err: if (fd != -1) close(fd); - return status ? ret : 2; + + return status; } int http_fetch(char *loc, char *path) { - int statuscode; + int statuscode = 3, redirs = 0; if (strlen(loc) > 2047) die("%s: url too long: %s", __func__, url); strcpy(url, loc); - if (parseuri(url, &u) == -1) - die("invalid url: %s", url); - - if (config_timeout > 0) { - signal(SIGALRM, sighandler); - } - mkdirs(dirname(path)); if ((dest = fopen(path, "w")) == NULL) { die("%s: failed to open %s:", __func__, path); } - if (!strcmp(u.proto, "https")) { - if (tls_init()) - die("tls_init failed"); - if (!(tls_config = tls_config_new())) - die("tls config failed"); - if (!u.port[0] && !strcmp(u.proto, "https")) - memcpy(u.port, "443", 4); - statuscode = https_request(); - } else if (!strcmp(u.proto, "http")) { - if (!u.port[0]) - memcpy(u.port, "80", 3); - statuscode = http_request(); - } else { - if (u.proto[0]) - die("unsupported protocol specified: %s", u.proto); - else - die("no protocol specified"); - } + while (statuscode == 3 && redirs < MAX_REDIRECTIONS) { + if (parseuri(url, &u) == -1) + die("invalid url: %s", url); + if (config_timeout > 0) { + signal(SIGALRM, sighandler); + } + + if (!strcmp(u.proto, "https")) { + if (tls_init()) + die("tls_init failed"); + if (!(tls_config = tls_config_new())) + die("tls config failed"); + if (!u.port[0] && !strcmp(u.proto, "https")) + memcpy(u.port, "443", 4); + statuscode = https_request(); + } else if (!strcmp(u.proto, "http")) { + if (!u.port[0]) + memcpy(u.port, "80", 3); + statuscode = http_request(); + } else { + if (u.proto[0]) + die("unsupported protocol specified: %s", u.proto); + else + die("no protocol specified"); + } + + } fclose(dest); return statuscode;