diff --git a/netcon/misc/httpd.c b/netcon/misc/httpd.c deleted file mode 100644 index 3a58e2dcf..000000000 --- a/netcon/misc/httpd.c +++ /dev/null @@ -1,490 +0,0 @@ -/* httpd.c - multi-client httpd, with cgi and dirindex support, in <500 LOC. - * Run as: httpd [-p port] - * u+x or g+x files are considered cgi programs. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define LINEBUFMAX 4096 -#define REQBUFMAX 4096 -#define FILEBUFMAX 4096 - -static const char *docroot; -static int printreqs = 0; - -struct reactor { - int epfd; -}; - -struct socket { - int fd; - struct sockaddr_in sa; - struct reactor *r; - void (*read)(struct socket *); - void (*write)(struct socket *); - void (*close)(struct socket *); - void *priv; -}; - -struct client { - struct socket *s; - void (*line)(struct client *, char *); - void (*writedone)(struct client *); - - char *rbuf; - size_t rbufsize; - size_t rbuffill; - char *wbuf; - size_t wbufsize; - size_t wbuffill; - - char *reqmethod; - char *requrl; - - int fillfd; -}; - -static void udie(const char *prefix) { - perror(prefix); - abort(); -} - -static void *xmalloc(size_t sz) { - void *p = malloc(sz); - if (!p) - abort(); - memset(p, 0, sz); - return p; -} - -static char *xstrdup(const char *s) { - char *n = strdup(s); - if (!n) - abort(); - return n; -} - -static void strlcpy(char *dest, const char *src, size_t n) { - strncpy(dest, src, n - 1); - dest[n - 1] = '\0'; -} - -static void strlcat(char *dest, const char *src, size_t n) { - strncat(dest, src, n - 1); - dest[n - 1] = '\0'; -} - -static struct reactor *reactor_new(void) { - struct reactor *r = xmalloc(sizeof *r); - r->epfd = epoll_create1(0); - if (r->epfd < 0) - udie("epoll_create1()"); - return r; -} - -static struct socket *reactor_add(struct reactor *r, int fd) { - struct socket *s = xmalloc(sizeof *s); - struct epoll_event evt; - - s->fd = fd; - s->r = r; - evt.events = 0; - evt.data.ptr = s; - if (epoll_ctl(r->epfd, EPOLL_CTL_ADD, fd, &evt) < 0) - udie("epoll_ctl()"); - return s; -}; - -static void reactor_refresh(struct reactor *r, struct socket *s) { - struct epoll_event evt; - evt.events = 0; - evt.data.ptr = s; - if (s->read) - evt.events |= EPOLLIN; - if (s->write) - evt.events |= EPOLLOUT; - if (s->close) - evt.events |= EPOLLRDHUP; - if (epoll_ctl(r->epfd, EPOLL_CTL_MOD, s->fd, &evt) < 0) - udie("epoll_ctl()"); -} - -static void reactor_del(struct reactor *r, struct socket *s) { - if (epoll_ctl(r->epfd, EPOLL_CTL_DEL, s->fd, NULL) < 0) - udie("epoll_ctl()"); - free(s); -} - -static void reactor_run(struct reactor *r) { - struct epoll_event evts[16]; - int n; - int i; - struct socket *s; - - n = epoll_wait(r->epfd, evts, sizeof(evts) / sizeof(evts[0]), -1); - if (n < 0) - udie("epoll_wait()"); - for (i = 0; i < n; i++) { - s = evts[i].data.ptr; - if (evts[i].events & (EPOLLRDHUP | EPOLLERR | EPOLLHUP)) { - if (s->close) - s->close(s); - reactor_del(r, s); - } else if ((evts[i].events & EPOLLIN) && s->read) { - s->read(s); - } else if ((evts[i].events & EPOLLOUT) && s->write) { - s->write(s); - } - } -} - -static void reqline(struct client *, char *); - -static struct client *client_new(struct socket *s) { - struct client *c = xmalloc(sizeof *c); - c->s = s; - c->rbuf = xmalloc(REQBUFMAX); - c->rbufsize = REQBUFMAX; - c->rbuffill = 0; - c->line = reqline; - c->wbufsize = 0; - c->wbuffill = 0; - c->writedone = NULL; - return c; -} - -static void client_read(struct socket *s) { - struct client *c = s->priv; - char *p; - ssize_t len; - - len = read(s->fd, c->rbuf + c->rbuffill, c->rbufsize - c->rbuffill); - if (len < 0) - udie("read()"); - c->rbuffill += len; - while ((p = strstr(c->rbuf, "\n"))) { - *p = '\0'; - if (p > c->rbuf && p[-1] == '\r') - p[-1] = '\0'; - p++; - c->line(c, c->rbuf); - memmove(c->rbuf, p, c->rbufsize - (p - c->rbuf)); - c->rbuffill -= (p - c->rbuf); - memset(c->rbuf + c->rbuffill, 0, c->rbufsize - c->rbuffill); - } -} - -static void client_write(struct socket *s) { - struct client *c = s->priv; - ssize_t len; - - len = write(s->fd, c->wbuf, c->wbuffill); - if (len < 0) - udie("write()"); - if ((size_t)len < c->wbuffill) - memmove(c->wbuf, c->wbuf + len, c->wbuffill - len); - c->wbuffill -= len; - if (c->wbuffill) - return; - free(c->wbuf); - c->wbuf = NULL; - c->wbufsize = 0; - s->write = NULL; - c->writedone(c); -} - -static void client_writeb(struct client *c, const char *buf, size_t len) { - if (!c->wbufsize || c->wbufsize - c->wbuffill < len) { - size_t growby = len - (c->wbufsize - c->wbuffill); - c->wbuf = realloc(c->wbuf, c->wbufsize + growby); - c->wbufsize += growby; - } - memcpy(c->wbuf + c->wbuffill, buf, len); - c->wbuffill += len; - if (!c->s->write) { - c->s->write = client_write; - reactor_refresh(c->s->r, c->s); - } -} - -static void client_writeln(struct client *c, const char *fmt, ...) { - char buf[LINEBUFMAX]; - va_list ap; - char *p; - - va_start(ap, fmt); - vsnprintf(buf, LINEBUFMAX, fmt, ap); - va_end(ap); - - p = buf + strlen(buf); - if (p > buf + LINEBUFMAX - 2) - p = buf + LINEBUFMAX - 2; - *p++ = '\r'; - *p++ = '\n'; - client_writeb(c, buf, p - buf); -} - -static void client_writedone(struct client *c) { - close(c->s->fd); -} - -static void client_refillbuf(struct client *c) { - char buf[FILEBUFMAX]; - ssize_t len; - - len = read(c->fillfd, buf, sizeof(buf)); - if (len < 0) - udie("read()"); - if (len == 0) { - c->writedone = client_writedone; - close(c->fillfd); - } else { - c->writedone = client_refillbuf; - } - client_writeb(c, buf, len); -} - -static void client_close(struct socket *s) { - struct client *c = s->priv; - free(c->reqmethod); - free(c->requrl); - free(c->rbuf); - free(c->wbuf); - free(c); - /* ... */ -} - -static void listener_read(struct socket *s) { - struct sockaddr_in sa; - socklen_t salen = sizeof(sa); - int nfd = accept(s->fd, (struct sockaddr *)&sa, &salen); - struct socket *n; - if (nfd == -1) - udie("accept()"); - if (fcntl(nfd, F_SETFD, FD_CLOEXEC) < 0) - udie("fcntl()"); - n = reactor_add(s->r, nfd); - memcpy(&n->sa, &sa, sizeof(n->sa)); - n->read = client_read; - n->close = client_close; - reactor_refresh(s->r, n); - n->priv = client_new(n); -} - -static void error(struct client *c, int code) { - client_writeln(c, "HTTP/1.1 %u Error", code); - client_writeln(c, ""); - c->writedone = client_writedone; -} - -static void iptobuf(struct client *c, char *buf) { - unsigned int ip = ntohl(c->s->sa.sin_addr.s_addr); - sprintf(buf, "%u.%u.%u.%u", (ip >> 24) & 0xFF, - (ip >> 16) & 0xFF, (ip >> 8) & 0xFF, ip & 0xFF); -} - -static void runcgi(struct client *c, const char *prog, const char *args) { - char buf[] = "REMOTE_ADDR=255.255.255.255"; - iptobuf(c, buf + strlen("REMOTE_ADDR=")); - putenv(buf); - dup2(c->s->fd, 0); - dup2(c->s->fd, 1); - dup2(c->s->fd, 2); - execl(prog, prog, args, NULL); -} - -static void cgi(struct client *c, const char *prog, const char *args) { - int p; - p = fork(); - if (!p) - runcgi(c, prog, args); - else if (p < 0) - error(c, 500); - else - c->writedone = client_writedone; -} - -static void genindex(struct client *c, const char *url) { - DIR *d = fdopendir(c->fillfd); - struct dirent *e; - - client_writeln(c, "Content-Type: text/html"); - client_writeln(c, ""); - - client_writeln(c, ""); - client_writeln(c, " "); - client_writeln(c, " Index of %s", url); - client_writeln(c, " "); - client_writeln(c, " "); - client_writeln(c, "

Index of %s

", url); - client_writeln(c, "
    "); - while ((e = readdir(d))) { - client_writeln(c, "
  • "); - client_writeln(c, " %s", url, - url[strlen(url) - 1] == '/' ? "" : "/", e->d_name, - e->d_name); - client_writeln(c, "
  • "); - } - client_writeln(c, "
"); - client_writeln(c, " "); - client_writeln(c, ""); - closedir(d); - c->writedone = client_writedone; -} - -static void get(struct client *c, char *url) { - char rp[PATH_MAX]; - char *rpcanon; - char *rest; - struct stat st; - - strlcpy(rp, docroot, sizeof(rp)); - if ((rest = strchr(url, '?'))) - *rest++ = '\0'; - strlcat(rp, url, sizeof(rp)); - rpcanon = realpath(rp, NULL); - if (!rpcanon) { - error(c, 404); - return; - } - - if (strstr(rpcanon, docroot) != rpcanon) { - error(c, 403); - free(rpcanon); - return; - } - - c->fillfd = open(rpcanon, O_RDONLY); - if (c->fillfd == -1) { - free(rpcanon); - error(c, 403); /* XXX: not all open() failures are 403s */ - return; - } - - if (fstat(c->fillfd, &st) == -1) - udie("fstat()"); - - client_writeln(c, "HTTP/1.1 200 OK"); - - if (S_ISDIR(st.st_mode)) { - genindex(c, url); - } else if (st.st_mode & (S_IXUSR | S_IXGRP)) { - cgi(c, rpcanon, rest); - } else { - client_writeln(c, ""); - client_refillbuf(c); - } - free(rpcanon); -} - -static void reqdone(struct client *c) { - if (printreqs) { - char buf[32]; - iptobuf(c, buf); - printf("%s %s %s\n", buf, c->reqmethod, c->requrl); - } - if (!strcasecmp(c->reqmethod, "GET")) - get(c, c->requrl); - else - error(c, 405); -} - -static void reqhdr(struct client *c, char *line) { - - if (!strlen(line)) { - reqdone(c); - return; - } - - /* XXX */ -} - -static void reqline(struct client *c, char *line) { - char *method, *url, *version; - - method = strtok(line, " "); - url = strtok(NULL, " "); - version = strtok(NULL, " "); - - if (!method || !url) { - error(c, 400); - return; - } - - c->reqmethod = xstrdup(method); - c->requrl = xstrdup(url); - c->line = reqhdr; -} - -static int serve(int port) { - int sfd = socket(AF_INET, SOCK_STREAM, 0); - struct sockaddr_in sa; - if (sfd == -1) - udie("socket()"); - memset(&sa, 0, sizeof(sa)); - sa.sin_family = AF_INET; - sa.sin_addr.s_addr = htonl(INADDR_ANY); - sa.sin_port = htons(port); - if (bind(sfd, (struct sockaddr *)&sa, sizeof(sa)) < 0) - udie("bind()"); - if (listen(sfd, 20) < 0) - udie("listen()"); - if (fcntl(sfd, F_SETFD, FD_CLOEXEC) < 0) - udie("fcntl()"); - return sfd; -} - -static void usage(const char *progn) { - printf("Usage: %s [-p port] [-v] \n", progn); -} - -int main(int argc, char *argv[]) { - struct reactor *r = reactor_new(); - struct socket *listener; - int opt; - int port = 80; - - while ((opt = getopt(argc, argv, "p:v")) != -1) { - switch (opt) { - case 'p': - port = atoi(optarg); - break; - case 'v': - printreqs = 1; - break; - default: - usage(argv[0]); - exit(1); - } - } - - if (optind >= argc) { - usage(argv[0]); - exit(1); - } - - docroot = argv[optind]; - - listener = reactor_add(r, serve(port)); - listener->read = listener_read; - reactor_refresh(r, listener); - - signal(SIGCHLD, SIG_IGN); - - while (1) { - reactor_run(r); - } -}