X-Git-Url: https://git.distorted.org.uk/~mdw/sgt/agedu/blobdiff_plain/995db5990865ab0772e0f0902ba39c1fb9336fa3..4fc86a0679d5d70b1a3944b26294c376b5f63abe:/httpd.c diff --git a/httpd.c b/httpd.c index 8087a28..6c90e66 100644 --- a/httpd.c +++ b/httpd.c @@ -2,24 +2,6 @@ * httpd.c: implementation of httpd.h. */ -#define _GNU_SOURCE - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - #include "agedu.h" #include "alloc.h" #include "html.h" @@ -108,7 +90,7 @@ char *got_data(struct connctx *ctx, char *data, int length, const struct html_config *cfg) { char *line, *p, *q, *r, *z1, *z2, c1, c2; - int auth_provided = 0, auth_correct = 0; + int auth_correct = 0; unsigned long index; char *document, *ret; @@ -253,7 +235,6 @@ char *got_data(struct connctx *ctx, char *data, int length, p = q; } if (p < q) { - auth_provided = 1; while (p < q && isspace((unsigned char)*p)) p++; r = p; @@ -278,25 +259,87 @@ char *got_data(struct connctx *ctx, char *data, int length, } if (!magic_access && !auth_correct) { - if (auth_string && !auth_provided) { + if (auth_string) { ret = http_error("401", "Unauthorized", - "WWW-Authenticate: Basic realm=\""PNAME"\"\r", - "\nPlease authenticate to view these pages."); + "WWW-Authenticate: Basic realm=\""PNAME"\"\r\n", + "\nYou must authenticate to view these pages."); } else { ret = http_error("403", "Forbidden", NULL, "This is a restricted-access set of pages."); } } else { p = ctx->url; - p += strspn(p, "/?"); - index = strtoul(p, NULL, 10); - document = html_query(ctx->t, index, cfg); - if (document) { - ret = http_success("text/html", 1, document); - sfree(document); - } else { + if (!html_parse_path(ctx->t, p, cfg, &index)) { ret = http_error("404", "Not Found", NULL, - "Pathname index out of range."); + "This is not a valid pathname."); + } else { + char *canonpath = html_format_path(ctx->t, cfg, index); + if (!strcmp(canonpath, p)) { + /* + * This is a canonical path. Return the document. + */ + document = html_query(ctx->t, index, cfg, 1); + if (document) { + ret = http_success("text/html", 1, document); + sfree(document); + } else { + ret = http_error("404", "Not Found", NULL, + "This is not a valid pathname."); + } + } else { + /* + * This is a non-canonical path. Return a redirect + * to the right one. + * + * To do this, we must search the request headers + * for Host:, to see what the client thought it + * was calling our server. + */ + + char *host = NULL; + q = ctx->data + ctx->datalen; + for (p = ctx->headers; p < q; p++) { + const char *hdr = "Host:"; + int i; + for (i = 0; hdr[i]; i++) { + if (p >= q || tolower((unsigned char)*p) != + tolower((unsigned char)hdr[i])) + break; + p++; + } + if (!hdr[i]) + break; /* found our header */ + p = memchr(p, '\n', q - p); + if (!p) + p = q; + } + if (p < q) { + while (p < q && isspace((unsigned char)*p)) + p++; + r = p; + while (p < q) { + if (*p == '\r' && (p+1 >= q || p[1] == '\n')) + break; + p++; + } + host = snewn(p-r+1, char); + memcpy(host, r, p-r); + host[p-r] = '\0'; + } + if (host) { + char *header = dupfmt("Location: http://%s%s\r\n", + host, canonpath); + ret = http_error("301", "Moved", header, + "This is not the canonical form of" + " this pathname."); + sfree(header); + } else { + ret = http_error("400", "Bad Request", NULL, + "Needed a Host: header to return" + " the intended redirection."); + } + } + sfree(canonpath); } } return ret; @@ -380,9 +423,11 @@ int check_owning_uid(int fd, int flip) while (fgets(linebuf, sizeof(linebuf), fp)) { if (strlen(linebuf) >= 75 && !strncmp(linebuf+6, matchbuf, strlen(matchbuf))) { + fclose(fp); return atoi(linebuf + 75); } } + fclose(fp); } return -1; @@ -421,17 +466,13 @@ static void base64_encode_atom(unsigned char *data, int n, char *out) void run_httpd(const void *t, int authmask, const struct httpd_config *dcfg, const struct html_config *incfg) { - int fd; + int fd, ret; int authtype; char *authstring = NULL; - unsigned long ipaddr; - struct fd *f; struct sockaddr_in addr; socklen_t addrlen; struct html_config cfg = *incfg; - cfg.format = "%.0lu"; - /* * Establish the listening socket and retrieve its port * number. @@ -441,21 +482,40 @@ void run_httpd(const void *t, int authmask, const struct httpd_config *dcfg, fprintf(stderr, "socket(PF_INET): %s\n", strerror(errno)); exit(1); } + memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; if (!dcfg->address) { +#ifdef RANDOM_LOCALHOST + unsigned long ipaddr; srand(0L); ipaddr = 0x7f000000; ipaddr += (1 + rand() % 255) << 16; ipaddr += (1 + rand() % 255) << 8; ipaddr += (1 + rand() % 255); addr.sin_addr.s_addr = htonl(ipaddr); +#else + addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); +#endif addr.sin_port = htons(0); } else { addr.sin_addr.s_addr = inet_addr(dcfg->address); - addr.sin_port = dcfg->port ? htons(dcfg->port) : 80; + addr.sin_port = dcfg->port ? htons(dcfg->port) : 0; } addrlen = sizeof(addr); - if (bind(fd, (struct sockaddr *)&addr, addrlen) < 0) { + ret = bind(fd, (const struct sockaddr *)&addr, addrlen); +#ifdef RANDOM_LOCALHOST + if (ret < 0 && errno == EADDRNOTAVAIL && !dcfg->address) { + /* + * Some systems don't like us binding to random weird + * localhost-space addresses. Try again with the official + * INADDR_LOOPBACK. + */ + addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + addr.sin_port = htons(0); + ret = bind(fd, (const struct sockaddr *)&addr, addrlen); + } +#endif + if (ret < 0) { fprintf(stderr, "bind: %s\n", strerror(errno)); exit(1); } @@ -555,25 +615,28 @@ void run_httpd(const void *t, int authmask, const struct httpd_config *dcfg, fprintf(stderr, PNAME ": authentication method not supported\n"); exit(1); } - if (!dcfg->address) { - if (ntohs(addr.sin_port) == 80) { - printf("URL: http://%s/\n", inet_ntoa(addr.sin_addr)); - } else { - printf("URL: http://%s:%d/\n", - inet_ntoa(addr.sin_addr), ntohs(addr.sin_port)); - } + if (ntohs(addr.sin_addr.s_addr) == INADDR_ANY) { + printf("Server port: %d\n", ntohs(addr.sin_port)); + } else if (ntohs(addr.sin_port) == 80) { + printf("URL: http://%s/\n", inet_ntoa(addr.sin_addr)); + } else { + printf("URL: http://%s:%d/\n", + inet_ntoa(addr.sin_addr), ntohs(addr.sin_port)); } + fflush(stdout); /* * Now construct an fd structure to hold it. */ - f = new_fdstruct(fd, FD_LISTENER); - - /* - * Read from standard input, and treat EOF as a notification - * to exit. - */ - new_fdstruct(0, FD_CLIENT); + new_fdstruct(fd, FD_LISTENER); + + if (dcfg->closeoneof) { + /* + * Read from standard input, and treat EOF as a notification + * to exit. + */ + new_fdstruct(0, FD_CLIENT); + } /* * Now we're ready to run our main loop. Keep looping round on @@ -581,7 +644,9 @@ void run_httpd(const void *t, int authmask, const struct httpd_config *dcfg, */ while (1) { fd_set rfds, wfds; - int i, j, maxfd, ret; + int i, j; + SELECT_TYPE_ARG1 maxfd; + int ret; #define FD_SET_MAX(fd, set, max) \ do { FD_SET((fd),(set)); (max) = ((max)<=(fd)?(fd)+1:(max)); } while(0) @@ -605,6 +670,8 @@ void run_httpd(const void *t, int authmask, const struct httpd_config *dcfg, switch (fds[i].type) { case FD_CLIENT: + FD_SET_MAX(fds[i].fd, &rfds, maxfd); + break; case FD_LISTENER: FD_SET_MAX(fds[i].fd, &rfds, maxfd); break; @@ -630,7 +697,9 @@ void run_httpd(const void *t, int authmask, const struct httpd_config *dcfg, } nfds = i; - ret = select(maxfd, &rfds, &wfds, NULL, NULL); + ret = select(maxfd, SELECT_TYPE_ARG234 &rfds, + SELECT_TYPE_ARG234 &wfds, SELECT_TYPE_ARG234 NULL, + SELECT_TYPE_ARG5 NULL); if (ret <= 0) { if (ret < 0 && (errno != EINTR)) { fprintf(stderr, "select: %s", strerror(errno));