commit 7470481c1eff46db13b807c8da0d13aed08c2b1d
parent b45edeef75e1ab2ad82158c4b8a29cdf6fe0d80a
Author: Nihal Jere <nihal@nihaljere.xyz>
Date: Wed, 17 Feb 2021 23:47:43 -0600
http.c: use spaces instead of tabs
Diffstat:
M | http.c | | | 692 | ++++++++++++++++++++++++++++++++++++++++--------------------------------------- |
1 file changed, 354 insertions(+), 338 deletions(-)
diff --git a/http.c b/http.c
@@ -15,17 +15,17 @@
#include "util.h"
-#define READ_BUF_SIZ 16384
+#define READ_BUF_SIZ 16384
#ifndef TLS_CA_CERT_FILE
#define TLS_CA_CERT_FILE "/etc/ssl/cert.pem"
#endif
struct uri {
- char proto[48];
- char host[256];
- char path[2048];
- char port[6]; /* numeric port */
+ char proto[48];
+ char host[256];
+ char path[2048];
+ char port[6]; /* numeric port */
};
/* time-out in seconds */
@@ -42,355 +42,371 @@ FILE *dest;
static void
sighandler(int signo)
{
- if (signo == SIGALRM)
- _exit(2);
+ if (signo == SIGALRM)
+ _exit(2);
}
static int
parseuri(const char *s, struct uri *u)
{
- const char *p = s, *b;
- char *endptr = NULL;
- size_t i;
- unsigned long l;
-
- u->proto[0] = u->host[0] = u->path[0] = u->port[0] = '\0';
- if (!*p)
- return 0;
-
- /* protocol part */
- for (p = s; *p && (isalpha((unsigned char)*p) || isdigit((unsigned char)*p) ||
- *p == '+' || *p == '-' || *p == '.'); p++)
- ;
- if (!strncmp(p, "://", 3)) {
- if ((size_t)(p - s) >= sizeof(u->proto))
- return -1; /* protocol too long */
- memcpy(u->proto, s, p - s);
- u->proto[p - s] = '\0';
- p += 3; /* skip "://" */
- } else {
- return -1; /* no protocol specified */
- }
-
- /* IPv6 address */
- if (*p == '[') {
- /* bracket not found or host too long */
- if (!(b = strchr(p, ']')) || (size_t)(b - p) >= (ssize_t)sizeof(u->host))
- return -1;
- memcpy(u->host, p + 1, b - p - 1);
- u->host[b - p - 1] = '\0';
- p = b + 1;
- } else {
- /* domain / host part, skip until port, path or end. */
- if ((i = strcspn(p, ":/")) >= sizeof(u->host))
- return -1; /* host too long */
- memcpy(u->host, p, i);
- u->host[i] = '\0';
- p = &p[i];
- }
- /* port */
- if (*p == ':') {
- if ((i = strcspn(++p, "/")) >= sizeof(u->port))
- return -1; /* port too long */
- memcpy(u->port, p, i);
- u->port[i] = '\0';
- /* check for valid port: range 1 - 65535 */
- errno = 0;
- l = strtoul(u->port, &endptr, 10);
- if (errno || u->port[0] == '\0' || *endptr ||
- !l || l > 65535)
- return -1;
- p = &p[i];
- }
- if (u->host[0]) {
- p = &p[strspn(p, "/")];
- memcpy(u->path, "/", 2);
- } else {
- return -1;
- }
- /* treat truncation as an error */
- if (strlcat(u->path, p, sizeof(u->path)) >= sizeof(u->path))
- return -1;
- return 0;
+ const char *p = s, *b;
+ char *endptr = NULL;
+ size_t i;
+ unsigned long l;
+
+ u->proto[0] = u->host[0] = u->path[0] = u->port[0] = '\0';
+ if (!*p)
+ return 0;
+
+ /* protocol part */
+ for (p = s; *p && (isalpha((unsigned char)*p) || isdigit((unsigned char)*p) ||
+ *p == '+' || *p == '-' || *p == '.'); p++)
+ ;
+ if (!strncmp(p, "://", 3)) {
+ if ((size_t)(p - s) >= sizeof(u->proto))
+ return -1; /* protocol too long */
+ memcpy(u->proto, s, p - s);
+ u->proto[p - s] = '\0';
+ p += 3; /* skip "://" */
+ } else {
+ return -1; /* no protocol specified */
+ }
+
+ /* IPv6 address */
+ if (*p == '[') {
+ /* bracket not found or host too long */
+ if (!(b = strchr(p, ']')) || (size_t)(b - p) >= (ssize_t)sizeof(u->host))
+ return -1;
+ memcpy(u->host, p + 1, b - p - 1);
+ u->host[b - p - 1] = '\0';
+ p = b + 1;
+ } else {
+ /* domain / host part, skip until port, path or end. */
+ if ((i = strcspn(p, ":/")) >= sizeof(u->host))
+ return -1; /* host too long */
+ memcpy(u->host, p, i);
+ u->host[i] = '\0';
+ p = &p[i];
+ }
+ /* port */
+ if (*p == ':') {
+ if ((i = strcspn(++p, "/")) >= sizeof(u->port))
+ return -1; /* port too long */
+ memcpy(u->port, p, i);
+ u->port[i] = '\0';
+ /* check for valid port: range 1 - 65535 */
+ errno = 0;
+ l = strtoul(u->port, &endptr, 10);
+ if (errno || u->port[0] == '\0' || *endptr ||
+ !l || l > 65535)
+ return -1;
+ p = &p[i];
+ }
+ if (u->host[0]) {
+ p = &p[strspn(p, "/")];
+ memcpy(u->path, "/", 2);
+ } else {
+ return -1;
+ }
+ /* treat truncation as an error */
+ if (strlcat(u->path, p, sizeof(u->path)) >= sizeof(u->path))
+ return -1;
+ return 0;
}
static int
edial(const char *host, const char *port)
{
- struct addrinfo hints, *res, *res0;
- int error, save_errno, s;
- const char *cause = NULL;
-
- memset(&hints, 0, sizeof(hints));
- hints.ai_family = AF_UNSPEC;
- hints.ai_socktype = SOCK_STREAM;
- hints.ai_flags = AI_NUMERICSERV; /* numeric port only */
- if ((error = getaddrinfo(host, port, &hints, &res0)))
- die("%s: %s: %s:%s", __func__, gai_strerror(error), host, port);
- s = -1;
- for (res = res0; res; res = res->ai_next) {
- s = socket(res->ai_family, res->ai_socktype,
- res->ai_protocol);
- if (s == -1) {
- cause = "socket";
- continue;
- }
-
- if (connect(s, res->ai_addr, res->ai_addrlen) == -1) {
- cause = "connect";
- save_errno = errno;
- close(s);
- errno = save_errno;
- s = -1;
- continue;
- }
- break;
- }
- if (s == -1)
- die("%s: %s: %s:%s", __func__, cause, host, port);
- freeaddrinfo(res0);
-
- return s;
+ struct addrinfo hints, *res, *res0;
+ int error, save_errno, s;
+ const char *cause = NULL;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_NUMERICSERV; /* numeric port only */
+ if ((error = getaddrinfo(host, port, &hints, &res0)))
+ die("%s: %s: %s:%s", __func__, gai_strerror(error), host, port);
+ s = -1;
+ for (res = res0; res; res = res->ai_next) {
+ s = socket(res->ai_family, res->ai_socktype,
+ res->ai_protocol);
+ if (s == -1) {
+ cause = "socket";
+ continue;
+ }
+
+ if (connect(s, res->ai_addr, res->ai_addrlen) == -1) {
+ cause = "connect";
+ save_errno = errno;
+ close(s);
+ errno = save_errno;
+ s = -1;
+ continue;
+ }
+ break;
+ }
+ if (s == -1)
+ die("%s: %s: %s:%s", __func__, cause, host, port);
+ freeaddrinfo(res0);
+
+ return s;
}
static int
https_request(void)
{
- struct tls *t;
- char buf[READ_BUF_SIZ], *p;
- const char *errstr;
- size_t n, len;
- ssize_t r;
- int httpok = 0, ret = 1, stdport;
-
- if (!(t = tls_client())) {
- fprintf(stderr, "tls_client: %s\n", tls_error(t));
- goto err;
- }
- if (tls_configure(t, tls_config)) {
- fprintf(stderr, "tls_configure: %s\n", tls_error(t));
- goto err;
- }
-
- if (tls_connect(t, u.host, u.port) == -1)
- die("tls_connect: %s", tls_error(t));
-
- stdport = u.port[0] == '\0' || strcmp(u.port, "443") == 0;
-
- /* create and send HTTP header */
- r = snprintf(buf, sizeof(buf),
- "GET %s HTTP/1.0\r\n"
- "Host: %s%s%s\r\n"
- "Connection: close\r\n"
- "\r\n", u.path, u.host,
- stdport ? "" : ":",
- stdport ? "" : u.port);
- if (r < 0 || (size_t)r >= sizeof(buf)) {
- fprintf(stderr, "not writing header because it is truncated");
- goto err;
- }
-
- for (len = r, p = buf; len > 0; ) {
- r = tls_write(t, p, len);
- if (r == TLS_WANT_POLLIN || r == TLS_WANT_POLLOUT) {
- continue;
- } else if (r == -1) {
- fprintf(stderr, "tls_write: %s\n", tls_error(t));
- goto err;
- }
- p += r;
- len -= r;
- }
-
- /* NOTE: HTTP header must fit in the buffer */
- for (len = 0; len < sizeof(buf);) {
- /* NOTE: buffer size is -1 to NUL terminate the buffer for a
- string comparison. */
- r = tls_read(t, &buf[len], sizeof(buf) - len - 1);
- if (r == TLS_WANT_POLLIN || r == TLS_WANT_POLLOUT) {
- continue;
- } else if (r == 0) {
- break;
- } else if (r == -1) {
- errstr = tls_error(t);
- fprintf(stderr, "tls_read: %s\n", errstr ? errstr : "");
- goto err;
- }
- len += r;
- }
- 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))
- httpok = 1;
-
- if (!(p = strstr(buf, "\r\n\r\n"))) {
- fprintf(stderr, "no HTTP header found or header too big\n");
- goto err;
- }
- *p = '\0'; /* NUL terminate header part */
- p += strlen("\r\n\r\n");
-
- if (httpok) {
- n = len - (p - buf);
- r = fwrite(p, 1, n, dest);
- if (ferror(dest)) {
- fprintf(stderr, "fwrite: %s\n", strerror(errno));
- goto err;
- }
- } else {
- /* if not 200 OK print header */
- fputs(buf, stderr);
- fputs("\r\n\r\n", stderr);
- /* NOTE: we are nice and keep reading (not closing) until the server is done. */
- }
-
- while (1) {
- r = tls_read(t, &buf, sizeof(buf));
- if (r == TLS_WANT_POLLIN || r == TLS_WANT_POLLOUT) {
- continue;
- } else if (r == 0) {
- break;
- } else if (r == -1) {
- errstr = tls_error(t);
- fprintf(stderr, "tls_read: %s\n", errstr ? errstr : "");
- goto err;
- }
- len += r;
-
- if (httpok) {
- r = fwrite(buf, 1, r, dest);
- if (ferror(dest)) {
- fprintf(stderr, "fwrite: %s\n", strerror(errno));
- goto err;
- }
- }
-
- }
- ret = 0;
+ struct tls *t;
+ char buf[READ_BUF_SIZ], *p;
+ const char *errstr;
+ size_t n, len;
+ ssize_t r;
+ int status = 0, ret = 1, stdport;
+
+ if (!(t = tls_client())) {
+ fprintf(stderr, "tls_client: %s\n", tls_error(t));
+ goto err;
+ }
+ if (tls_configure(t, tls_config)) {
+ fprintf(stderr, "tls_configure: %s\n", tls_error(t));
+ goto err;
+ }
+
+ if (tls_connect(t, u.host, u.port) == -1)
+ die("tls_connect: %s", tls_error(t));
+
+ stdport = u.port[0] == '\0' || strcmp(u.port, "443") == 0;
+
+ /* create and send HTTP header */
+ r = snprintf(buf, sizeof(buf),
+ "GET %s HTTP/1.0\r\n"
+ "Host: %s%s%s\r\n"
+ "Connection: close\r\n"
+ "\r\n", u.path, u.host,
+ stdport ? "" : ":",
+ stdport ? "" : u.port);
+ if (r < 0 || (size_t)r >= sizeof(buf)) {
+ fprintf(stderr, "not writing header because it is truncated");
+ goto err;
+ }
+
+ for (len = r, p = buf; len > 0; ) {
+ r = tls_write(t, p, len);
+ if (r == TLS_WANT_POLLIN || r == TLS_WANT_POLLOUT) {
+ continue;
+ } else if (r == -1) {
+ fprintf(stderr, "tls_write: %s\n", tls_error(t));
+ goto err;
+ }
+ p += r;
+ len -= r;
+ }
+
+ /* NOTE: HTTP header must fit in the buffer */
+ for (len = 0; len < sizeof(buf);) {
+ /* NOTE: buffer size is -1 to NUL terminate the buffer for a
+ string comparison. */
+ r = tls_read(t, &buf[len], sizeof(buf) - len - 1);
+ if (r == TLS_WANT_POLLIN || r == TLS_WANT_POLLOUT) {
+ continue;
+ } else if (r == 0) {
+ break;
+ } else if (r == -1) {
+ errstr = tls_error(t);
+ fprintf(stderr, "tls_read: %s\n", errstr ? errstr : "");
+ goto err;
+ }
+ len += r;
+ }
+ 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 (!(p = strstr(buf, "\r\n\r\n"))) {
+ fprintf(stderr, "no HTTP header found or header too big\n");
+ goto err;
+ }
+ *p = '\0'; /* NUL terminate header part */
+ p += strlen("\r\n\r\n");
+
+ if (status) {
+ n = len - (p - buf);
+ r = fwrite(p, 1, n, dest);
+ if (ferror(dest)) {
+ fprintf(stderr, "fwrite: %s\n", strerror(errno));
+ goto err;
+ }
+ } else {
+ /* if not 200 OK print header */
+ fputs(buf, stderr);
+ fputs("\r\n\r\n", stderr);
+ /* NOTE: we are nice and keep reading (not closing) until the server is done. */
+ }
+
+ while (1) {
+ r = tls_read(t, &buf, sizeof(buf));
+ if (r == TLS_WANT_POLLIN || r == TLS_WANT_POLLOUT) {
+ continue;
+ } else if (r == 0) {
+ break;
+ } else if (r == -1) {
+ errstr = tls_error(t);
+ fprintf(stderr, "tls_read: %s\n", errstr ? errstr : "");
+ goto err;
+ }
+ len += r;
+
+ if (status) {
+ r = fwrite(buf, 1, r, dest);
+ if (ferror(dest)) {
+ fprintf(stderr, "fwrite: %s\n", strerror(errno));
+ goto err;
+ }
+ }
+
+ }
+ ret = 0;
err:
- if (t) {
- tls_close(t);
- tls_free(t);
- }
+ if (t) {
+ tls_close(t);
+ tls_free(t);
+ }
- return httpok ? ret : 2;
+ return status ? ret : 2;
}
static int
http_request(void)
{
- char buf[READ_BUF_SIZ], *p;
- size_t n, len;
- ssize_t r;
- int fd = -1, httpok = 0, ret = 1, stdport;
-
- fd = edial(u.host, u.port);
-
- stdport = u.port[0] == '\0' || strcmp(u.port, "80") == 0;
-
- /* create and send HTTP header */
- r = snprintf(buf, sizeof(buf),
- "GET %s HTTP/1.0\r\n"
- "Host: %s%s%s\r\n"
- "Connection: close\r\n"
- "\r\n", u.path, u.host,
- stdport ? "" : ":",
- stdport ? "" : u.port);
- if (r < 0 || (size_t)r >= sizeof(buf)) {
- fprintf(stderr, "not writing header because it is truncated");
- goto err;
- }
-
- for (len = r, p = buf; len > 0; p += r, len -= r) {
- if ((r = write(fd, p, len)) == -1) {
- fprintf(stderr, "write: %s\n", strerror(errno));
- goto err;
- }
- }
-
- /* NOTE: HTTP header must fit in the buffer */
- for (len = 0; len < sizeof(buf); len += r) {
- /* NOTE: buffer size is -1 to NUL terminate the buffer for a
- string comparison. */
- if ((r = read(fd, &buf[len], sizeof(buf) - len - 1)) == 0)
- break;
- if (r == -1) {
- fprintf(stderr, "read: %s\n", strerror(errno));
- goto err;
- }
- }
- 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))
- httpok = 1;
-
- if (!(p = strstr(buf, "\r\n\r\n"))) {
- fprintf(stderr, "no HTTP header found or header too big\n");
- goto err;
- }
- *p = '\0'; /* NUL terminate header part */
- p += strlen("\r\n\r\n");
-
- if (httpok) {
- n = len - (p - buf);
- r = fwrite(p, 1, n, dest);
- if (ferror(dest)) {
- fprintf(stderr, "fwrite: %s\n", strerror(errno));
- goto err;
- }
- } else {
- /* if not 200 OK print header */
- fputs(buf, stderr);
- fputs("\r\n\r\n", stderr);
- /* NOTE: we are nice and keep reading (not closing) until the server is done. */
- }
-
- while (1) {
- r = read(fd, &buf, sizeof(buf));
- if (r == 0)
- break;
- if (r == -1) {
- fprintf(stderr, "read: %s\n", strerror(errno));
- goto err;
- }
- len += r;
-
- if (httpok) {
- r = fwrite(buf, 1, r, dest);
- if (ferror(dest)) {
- fprintf(stderr, "fwrite: %s\n", strerror(errno));
- goto err;
- }
- }
-
- }
- ret = 0;
+ char buf[READ_BUF_SIZ], *p;
+ size_t n, len;
+ ssize_t r;
+ int fd = -1, status = 0, ret = 1, stdport;
+
+ fd = edial(u.host, u.port);
+
+ stdport = u.port[0] == '\0' || strcmp(u.port, "80") == 0;
+
+ /* create and send HTTP header */
+ r = snprintf(buf, sizeof(buf),
+ "GET %s HTTP/1.0\r\n"
+ "Host: %s%s%s\r\n"
+ "Connection: close\r\n"
+ "\r\n", u.path, u.host,
+ stdport ? "" : ":",
+ stdport ? "" : u.port);
+ if (r < 0 || (size_t)r >= sizeof(buf)) {
+ fprintf(stderr, "not writing header because it is truncated");
+ goto err;
+ }
+
+ for (len = r, p = buf; len > 0; p += r, len -= r) {
+ if ((r = write(fd, p, len)) == -1) {
+ fprintf(stderr, "write: %s\n", strerror(errno));
+ goto err;
+ }
+ }
+
+ /* NOTE: HTTP header must fit in the buffer */
+ for (len = 0; len < sizeof(buf); len += r) {
+ /* NOTE: buffer size is -1 to NUL terminate the buffer for a
+ string comparison. */
+ if ((r = read(fd, &buf[len], sizeof(buf) - len - 1)) == 0)
+ break;
+ if (r == -1) {
+ fprintf(stderr, "read: %s\n", strerror(errno));
+ goto err;
+ }
+ }
+ buf[len] = '\0';
+
+ 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");
+ goto err;
+ }
+ *p = '\0'; /* NUL terminate header part */
+ p += strlen("\r\n\r\n");
+
+ 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;
+ }
+ } else {
+ /* if not 200 OK print header */
+ fputs(buf, stderr);
+ fputs("\r\n\r\n", stderr);
+ /* NOTE: we are nice and keep reading (not closing) until the server is done. */
+ }
+
+ while (1) {
+ r = read(fd, &buf, sizeof(buf));
+ if (r == 0)
+ break;
+ if (r == -1) {
+ fprintf(stderr, "read: %s\n", strerror(errno));
+ goto err;
+ }
+ len += r;
+
+ if (status == 2) {
+ r = fwrite(buf, 1, r, dest);
+ if (ferror(dest)) {
+ fprintf(stderr, "fwrite: %s\n", strerror(errno));
+ goto err;
+ }
+ }
+
+ }
+ ret = 0;
err:
- if (fd != -1)
- close(fd);
- return httpok ? ret : 2;
+ if (fd != -1)
+ close(fd);
+ return status ? ret : 2;
}
int
http_fetch(char *loc, char *path)
{
- int statuscode;
+ int statuscode;
- if (strlen(loc) > 2047)
- die("%s: url too long: %s", __func__, url);
+ 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 (parseuri(url, &u) == -1)
+ die("invalid url: %s", url);
- if (config_timeout > 0) {
- signal(SIGALRM, sighandler);
- }
+ if (config_timeout > 0) {
+ signal(SIGALRM, sighandler);
+ }
mkdirs(dirname(path));
@@ -398,26 +414,26 @@ http_fetch(char *loc, char *path)
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");
- }
+ 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;
+ return statuscode;
}