From: Fredrik Tolf Date: Sun, 2 Mar 2014 00:26:03 +0000 (+0100) Subject: Merge branch 'master' of fludd.seatribe.se:/usr/local/src/ashd X-Git-Url: http://dolda2000.com/gitweb/?p=ashd.git;a=commitdiff_plain;h=9d82f27c53c5fb79ceee52904b996edefb27628a;hp=7bf1ad5a2195774e6a69a3612a07321afeef60a8 Merge branch 'master' of fludd.seatribe.se:/usr/local/src/ashd --- diff --git a/ChangeLog b/ChangeLog index ae73ffd..4036329 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +Version 0.13: + + * FreeBSD support. + * Improved dirplex/patplex configuration. + * Added httrcall. + Version 0.12: * Support chunked request-bodies from clients. diff --git a/configure.in b/configure.in index b8fa47b..80d2389 100644 --- a/configure.in +++ b/configure.in @@ -1,6 +1,8 @@ -AC_INIT(src/htparser.c) -AM_INIT_AUTOMAKE([ashd], [0.13]) +AC_INIT([ashd], [0.13]) +AC_CONFIG_SRCDIR(src/htparser.c) +AM_INIT_AUTOMAKE AM_CONFIG_HEADER(config.h) +AC_USE_SYSTEM_EXTENSIONS AC_PROG_CC AM_PROG_CC_C_O @@ -19,11 +21,29 @@ if test "$HAS_MAGIC" = no; then AC_MSG_ERROR([*** cannot find libmagic on this system]) fi +AH_TEMPLATE(HAVE_GLIBC_STDIO, [define to indicate system support for glibc cookie streams]) +AH_TEMPLATE(HAVE_BSD_STDIO, [define to indicate system support for BSD-style funopen streams]) + +HAS_FOPENCOOKIE=yes +AC_CHECK_FUNC(fopencookie, [], [HAS_FOPENCOOKIE=no]) +AC_CHECK_MEMBER([cookie_io_functions_t.read], [], [HAS_FOPENCOOKIE=no]) + +HAS_FUNOPEN=yes +AC_CHECK_FUNC(funopen, [], [HAS_FUNOPEN=no]) + +if test "$HAS_FOPENCOOKIE" = yes; then + AC_DEFINE(HAVE_GLIBC_STDIO) +elif test "$HAS_FUNOPEN" = yes; then + AC_DEFINE(HAVE_BSD_STDIO) +else + AC_MSG_ERROR([*** libc support for custom stdio streams is required]) +fi + AH_TEMPLATE(HAVE_VALGRIND, [define to include debugging support for Valgrind]) AC_CHECK_HEADER(valgrind/memcheck.h, [AC_DEFINE(HAVE_VALGRIND)], []) AH_TEMPLATE(HAVE_EPOLL, [define to enable epoll support]) -AC_ARG_WITH(epoll, [ --with-epoll Enable epoll(2) support]) +AC_ARG_WITH(epoll, AS_HELP_STRING([--with-epoll], [enable epoll(2) support])) HAS_EPOLL="" if test "$with_epoll" = no; then HAS_EPOLL=no; fi if test -z "$HAS_EPOLL"; then @@ -39,10 +59,30 @@ fi if test "$HAS_EPOLL" = yes; then AC_DEFINE(HAVE_EPOLL) fi -AM_CONDITIONAL(USE_EPOLL, [test "$HAS_EPOLL" = yes ]) + +AH_TEMPLATE(HAVE_KQUEUE, [define to enable kqueue support]) +AC_ARG_WITH(kqueue, AS_HELP_STRING([--with-kqueue], [enable kqueue(2) support])) +HAS_KQUEUE="" +if test "$with_kqueue" = no; then HAS_QUEUE=no; fi +if test -z "$HAS_KQUEUE"; then + AC_CHECK_FUNC(kqueue, [], [HAS_KQUEUE=no]) +fi +if test -z "$HAS_KQUEUE"; then + AC_CHECK_HEADER(sys/event.h, [], [HAS_KQUEUE=no]) +fi +if test "$HAS_KQUEUE" != no; then HAS_KQUEUE=yes; fi +if test "$with_kqueue" = yes -a "$HAS_KQUEUE" = no; then + AC_MSG_ERROR([*** cannot find kqueue support on this system]) +fi +if test "$HAS_KQUEUE" = yes; then + AC_DEFINE(HAVE_KQUEUE) +fi + +AM_CONDITIONAL(USE_EPOLL, [test "$HAS_EPOLL" = yes]) +AM_CONDITIONAL(USE_KQUEUE, [test "$HAS_KQUEUE" = yes]) AH_TEMPLATE(HAVE_XATTR, [define to compile support for filesystem extended attributes]) -AC_ARG_WITH(xattr, [ --with-xattr Enable XATTR support]) +AC_ARG_WITH(xattr, AS_HELP_STRING([--with-xattr], [enable XATTR support])) HAS_XATTR="" if test "$with_xattr" = no; then HAS_XATTR=no; fi if test -z "$HAS_XATTR"; then @@ -62,7 +102,7 @@ fi AC_SUBST(XATTR_LIBS) AH_TEMPLATE(HAVE_GNUTLS, [define to use the GnuTLS library for SSL support]) -AC_ARG_WITH(gnutls, [ --with-gnutls Enable SSL support with the GnuTLS library]) +AC_ARG_WITH(gnutls, AS_HELP_STRING([--with-gnutls], [enable SSL support with the GnuTLS library])) HAS_GNUTLS="" if test "$with_gnutls" = no; then HAS_GNUTLS=no; fi if test -z "$HAS_GNUTLS"; then diff --git a/doc/Makefile.am b/doc/Makefile.am index 3c4fe3d..baf94f3 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -1,18 +1,19 @@ dist_man1_MANS = callcgi.1 dirplex.1 htparser.1 patplex.1 sendfile.1 \ userplex.1 htls.1 callscgi.1 accesslog.1 htextauth.1 \ callfcgi.1 multifscgi.1 errlogger.1 httimed.1 \ - psendfile.1 + psendfile.1 httrcall.1 dist_man7_MANS = ashd.7 -%.7 %.1: %.doc +.doc.1: a2x -f manpage $< - -%.html: %.doc +.doc.7: + a2x -f manpage $< +.doc.html: a2x -f xhtml $< manpages: $(dist_man1_MANS) $(dist_man7_MANS) -htmldoc: $(patsubst %.doc, %.html, *.doc) +htmldoc: ${dist_man1_MANS:.1=.html} ${dist_man7_MANS:.7=.html} EXTRA_DIST = *.doc diff --git a/doc/htextauth.doc b/doc/htextauth.doc index b985492..8c3b6f7 100644 --- a/doc/htextauth.doc +++ b/doc/htextauth.doc @@ -78,6 +78,16 @@ the client. Note that *htextauth* will wait for the authentication program to exit and not process any other requests until then. +FILES +----- +The file `etc/extauth/vhtpasswd` in the *ashd* source distribution is +a simple authenticator program (written in Python) that can be used +with *htextauth*, which verifies the given credentials against a +simple database of users with encrypted passwords. It can be used as +is, or as a simple example of how to produce authenticator +programs. The accompanying `mkhtpasswd` program can be used to +maintain the password database. + AUTHOR ------ Fredrik Tolf diff --git a/doc/httrcall.doc b/doc/httrcall.doc new file mode 100644 index 0000000..c4fa48e --- /dev/null +++ b/doc/httrcall.doc @@ -0,0 +1,38 @@ +httrcall(1) +========== + +NAME +---- +httrcall - Call transient ashd handlers + +SYNOPSIS +-------- +*httrcall* [*-h*] 'PROGRAM' ['ARGS'...] + +DESCRIPTION +----------- + +*httrcall* is a persistent handler, as defined in *ashd*(7), but works +by starting a specified transient handler for every incoming +request. Thus, it can be used to run transient handlers where normally +only persistent handlers are accepted, such as for the program +specified to *accesslog*(1), *htextauth*(1) or even *htparser*(1). + +The transient handler to call is specified by the 'PROGRAM' +argument. Any 'ARGS' given are prepended to the usual arguments for +transient handlers, as described in *ashd*(7). + +OPTIONS +------- + +*-h*:: + + Print a brief help message to standard output and exit. + +AUTHOR +------ +Fredrik Tolf + +SEE ALSO +-------- +*ashd*(7) diff --git a/etc/extauth/mkhtpasswd b/etc/extauth/mkhtpasswd new file mode 100755 index 0000000..923ab07 --- /dev/null +++ b/etc/extauth/mkhtpasswd @@ -0,0 +1,37 @@ +#!/usr/bin/python + +import sys, os, termios, hmac, hashlib, getopt, getpass + +def usage(out): + out.write("usage: mkhtpasswd [-h] FILE USERNAME\n") + +opts, args = getopt.getopt(sys.argv[1:], "h") +for o, a in opts: + if o == "-h": + usage(sys.stdout) + sys.exit(0) +if len(args) < 2: + usage(sys.stderr) + sys.exit(1) + +def hashpw(usr, pw): + dig = hmac.new(pw, digestmod=hashlib.sha1) + dig.update(usr) + return dig.hexdigest() + +if ':' in args[1]: + sys.stderr.write("mkhtpasswd: username cannot contain `:'\n") + sys.exit(1) + +passwds = {} +if os.path.exists(args[0]): + with open(args[0]) as fp: + for line in fp: + usr, pw = line.strip().split(':') + passwds[usr] = pw + +passwds[args[1]] = hashpw(args[1], getpass.getpass()) + +with open(args[0], "w") as fp: + for usr, pw in passwds.iteritems(): + fp.write("%s:%s\n" % (usr, pw)) diff --git a/etc/extauth/vhtpasswd b/etc/extauth/vhtpasswd new file mode 100755 index 0000000..422206d --- /dev/null +++ b/etc/extauth/vhtpasswd @@ -0,0 +1,34 @@ +#!/usr/bin/python + +import sys, hmac, hashlib, getopt + +def usage(out): + out.write("usage: vhtpasswd [-h] FILE\n") + +opts, args = getopt.getopt(sys.argv[1:], "h") +for o, a in opts: + if o == "-h": + usage(sys.stdout) + sys.exit(0) +if len(args) < 1: + usage(sys.stderr) + sys.exit(1) + +def hashpw(usr, pw): + dig = hmac.new(pw, digestmod=hashlib.sha1) + dig.update(usr) + return dig.hexdigest() + +def findpw(fn, name): + with open(fn) as fp: + for line in fp: + usr, pw = line.strip().split(':') + if usr == name: + return pw + return None + +usr = sys.stdin.readline().strip() +gpw = sys.stdin.readline().strip() +if findpw(args[0], usr) == hashpw(usr, gpw): + sys.exit(0) +sys.exit(1) diff --git a/lib/Makefile.am b/lib/Makefile.am index d48cfb6..30de42a 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -2,11 +2,14 @@ lib_LIBRARIES = libht.a libht_a_SOURCES = utils.c mt.c log.c req.c proc.c mtio.c resp.c cf.c libht_a_CFLAGS = -fPIC -libht_a_CPPFLAGS = -D_GNU_SOURCE if USE_EPOLL libht_a_SOURCES += mtio-epoll.c else +if USE_KQUEUE +libht_a_SOURCES += mtio-kqueue.c +else libht_a_SOURCES += mtio-select.c endif +endif pkginclude_HEADERS = utils.h mt.h log.h req.h proc.h mtio.h resp.h cf.h diff --git a/lib/cf.c b/lib/cf.c index bd4aba3..aae8485 100644 --- a/lib/cf.c +++ b/lib/cf.c @@ -355,26 +355,35 @@ static char **expandargs(struct stdchild *sd) return(ret); } -static int stdhandle(struct child *ch, struct hthead *req, int fd, void (*chinit)(void *), void *idata) +struct sidata { + struct stdchild *sd; + void (*sinit)(void *); + void *sdata; +}; + +static void stdinit(void *data) +{ + struct sidata *d = data; + int i; + + for(i = 0; d->sd->envp[i]; i += 2) + putenv(sprintf2("%s=%s", d->sd->envp[i], d->sd->envp[i + 1])); + if(d->sinit != NULL) + d->sinit(d->sdata); +} + +static int stdhandle(struct child *ch, struct hthead *req, int fd, void (*chinit)(void *), void *sdata) { struct stdchild *sd = ch->pdata; int serr; char **args; - - void stdinit(void *data) - { - int i; - - for(i = 0; sd->envp[i]; i += 2) - putenv(sprintf2("%s=%s", sd->envp[i], sd->envp[i + 1])); - if(chinit != NULL) - chinit(data); - } + struct sidata idat; if(sd->type == CH_SOCKET) { + idat = (struct sidata) {.sd = sd, .sinit = chinit, sdata = sdata}; if(sd->fd < 0) { args = expandargs(sd); - sd->fd = stdmkchild(args, stdinit, idata); + sd->fd = stdmkchild(args, stdinit, &idat); freeca(args); } if(sendreq2(sd->fd, req, fd, MSG_NOSIGNAL | MSG_DONTWAIT)) { @@ -383,7 +392,7 @@ static int stdhandle(struct child *ch, struct hthead *req, int fd, void (*chinit /* Assume that the child has crashed and restart it. */ close(sd->fd); args = expandargs(sd); - sd->fd = stdmkchild(args, stdinit, idata); + sd->fd = stdmkchild(args, stdinit, &idat); freeca(args); if(!sendreq2(sd->fd, req, fd, MSG_NOSIGNAL | MSG_DONTWAIT)) goto ok; @@ -406,7 +415,7 @@ static int stdhandle(struct child *ch, struct hthead *req, int fd, void (*chinit } } else if(sd->type == CH_FORK) { args = expandargs(sd); - if(stdforkserve(args, req, fd, chinit, idata) < 0) { + if(stdforkserve(args, req, fd, chinit, sdata) < 0) { freeca(args); return(-1); } diff --git a/lib/mtio-kqueue.c b/lib/mtio-kqueue.c new file mode 100644 index 0000000..1235973 --- /dev/null +++ b/lib/mtio-kqueue.c @@ -0,0 +1,238 @@ +/* + ashd - A Sane HTTP Daemon + Copyright (C) 2008 Fredrik Tolf + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include +#endif +#include +#include +#include +#include + +static struct blocker *blockers; + +struct blocker { + struct blocker *n, *p, *n2, *p2; + int fd, reg; + int ev; + time_t to; + struct muth *th; +}; + +static int qfd = -1, fdln = 0; +static int exitstatus; +static struct blocker **fdlist; + +static int regfd(struct blocker *bl) +{ + struct blocker *o; + int prev; + struct kevent evd; + + if(bl->fd >= fdln) { + if(fdlist) { + fdlist = srealloc(fdlist, sizeof(*fdlist) * (bl->fd + 1)); + memset(fdlist + fdln, 0, sizeof(*fdlist) * (bl->fd + 1 - fdln)); + fdln = bl->fd + 1; + } else { + fdlist = szmalloc(sizeof(*fdlist) * (fdln = (bl->fd + 1))); + } + } + for(prev = 0, o = fdlist[bl->fd]; o; o = o->n2) + prev |= o->ev; + if((bl->ev & EV_READ) && !(prev & EV_READ)) { + evd = (struct kevent) { + .flags = EV_ADD, + .ident = bl->fd, + .filter = EVFILT_READ, + }; + if(kevent(qfd, &evd, 1, NULL, 0, NULL) < 0) { + /* XXX?! Whatever to do, really? */ + flog(LOG_ERR, "kevent(EV_ADD, EVFILT_READ) on fd %i: %s", bl->fd, strerror(errno)); + return(-1); + } + } + if((bl->ev & EV_WRITE) && !(prev & EV_WRITE)) { + evd = (struct kevent) { + .flags = EV_ADD, + .ident = bl->fd, + .filter = EVFILT_WRITE, + }; + if(kevent(qfd, &evd, 1, NULL, 0, NULL) < 0) { + /* XXX?! Whatever to do, really? */ + flog(LOG_ERR, "kevent(EV_ADD, EVFILT_WRITE) on fd %i: %s", bl->fd, strerror(errno)); + return(-1); + } + } + bl->n2 = fdlist[bl->fd]; + bl->p2 = NULL; + if(fdlist[bl->fd] != NULL) + fdlist[bl->fd]->p2 = bl; + fdlist[bl->fd] = bl; + bl->reg = 1; + return(0); +} + +static void remfd(struct blocker *bl) +{ + struct blocker *o; + struct kevent evd; + int left; + + if(!bl->reg) + return; + if(bl->n2) + bl->n2->p2 = bl->p2; + if(bl->p2) + bl->p2->n2 = bl->n2; + if(bl == fdlist[bl->fd]) + fdlist[bl->fd] = bl->n2; + for(left = 0, o = fdlist[bl->fd]; o; o = o->n2) + left |= o->ev; + if((bl->ev & EV_READ) && !(left & EV_READ)) { + evd = (struct kevent) { + .flags = EV_DELETE, + .ident = bl->fd, + .filter = EVFILT_READ, + }; + if(kevent(qfd, &evd, 1, NULL, 0, NULL) < 0) { + /* XXX?! Whatever to do, really? */ + flog(LOG_ERR, "kevent(EV_DELETE, EVFILT_READ) on fd %i: %s", bl->fd, strerror(errno)); + } + } + if((bl->ev & EV_WRITE) && !(left & EV_WRITE)) { + evd = (struct kevent) { + .flags = EV_DELETE, + .ident = bl->fd, + .filter = EVFILT_WRITE, + }; + if(kevent(qfd, &evd, 1, NULL, 0, NULL) < 0) { + /* XXX?! Whatever to do, really? */ + flog(LOG_ERR, "kevent(EV_DELETE, EVFILT_WRITE) on fd %i: %s", bl->fd, strerror(errno)); + } + } + bl->reg = 0; +} + +int block(int fd, int ev, time_t to) +{ + struct blocker *bl; + int rv; + + omalloc(bl); + bl->fd = fd; + bl->ev = ev; + if(to > 0) + bl->to = time(NULL) + to; + bl->th = current; + if((qfd >= 0) && regfd(bl)) { + free(bl); + return(-1); + } + bl->n = blockers; + if(blockers) + blockers->p = bl; + blockers = bl; + rv = yield(); + if(bl->n) + bl->n->p = bl->p; + if(bl->p) + bl->p->n = bl->n; + if(bl == blockers) + blockers = bl->n; + remfd(bl); + free(bl); + return(rv); +} + +int ioloop(void) +{ + struct blocker *bl, *nbl; + struct kevent evs[16]; + int i, fd, nev, ev; + time_t now, timeout; + struct timespec *toval; + + exitstatus = 0; + qfd = kqueue(); + fcntl(qfd, F_SETFD, FD_CLOEXEC); + for(bl = blockers; bl; bl = nbl) { + nbl = bl->n; + if(regfd(bl)) + resume(bl->th, -1); + } + while(blockers != NULL) { + timeout = 0; + for(bl = blockers; bl; bl = bl->n) { + if((bl->to != 0) && ((timeout == 0) || (timeout > bl->to))) + timeout = bl->to; + } + now = time(NULL); + if(timeout == 0) + toval = NULL; + else if(timeout > now) + toval = &(struct timespec){.tv_sec = timeout - now}; + else + toval = &(struct timespec){.tv_sec = 1}; + if(exitstatus) + break; + nev = kevent(qfd, NULL, 0, evs, sizeof(evs) / sizeof(*evs), toval); + if(nev < 0) { + if(errno != EINTR) { + flog(LOG_CRIT, "ioloop: kevent errored out: %s", strerror(errno)); + /* To avoid CPU hogging in case it's bad, which it + * probably is. */ + sleep(1); + } + continue; + } + for(i = 0; i < nev; i++) { + fd = (int)evs[i].ident; + ev = (evs[i].filter == EVFILT_READ)?EV_READ:EV_WRITE; + for(bl = fdlist[fd]; bl; bl = nbl) { + nbl = bl->n2; + if(ev & bl->ev) + resume(bl->th, ev); + } + } + now = time(NULL); + for(bl = blockers; bl; bl = nbl) { + nbl = bl->n; + if((bl->to != 0) && (bl->to <= now)) + resume(bl->th, 0); + } + } + for(bl = blockers; bl; bl = bl->n) + remfd(bl); + close(qfd); + qfd = -1; + return(exitstatus); +} + +void exitioloop(int status) +{ + exitstatus = status; +} diff --git a/lib/mtio.c b/lib/mtio.c index 3b728d0..70a0949 100644 --- a/lib/mtio.c +++ b/lib/mtio.c @@ -16,6 +16,9 @@ along with this program. If not, see . */ +#ifdef HAVE_CONFIG_H +#include +#endif #include #include #include @@ -24,9 +27,6 @@ #include #include -#ifdef HAVE_CONFIG_H -#include -#endif #include #include #include @@ -107,6 +107,7 @@ static int mtclose(void *cookie) return(0); } +#if defined(HAVE_GLIBC_STDIO) static cookie_io_functions_t iofuns = { .read = mtread, .write = mtwrite, @@ -129,3 +130,43 @@ FILE *mtstdopen(int fd, int issock, int timeout, char *mode) fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK); return(ret); } +#elif defined(HAVE_BSD_STDIO) +static int bsd2mtread(void *cookie, char *buf, int len) +{ + return(mtread(cookie, buf, len)); +} + +static int bsd2mtwrite(void *cookie, const char *buf, int len) +{ + return(mtwrite(cookie, buf, len)); +} + +FILE *mtstdopen(int fd, int issock, int timeout, char *mode) +{ + struct stdiofd *d; + FILE *ret; + int r, w; + + if(!strcmp(mode, "r")) { + r = 1; w = 0; + } else if(!strcmp(mode, "w")) { + r = 0; w = 1; + } else if(!strcmp(mode, "r+")) { + r = w = 1; + } else { + return(NULL); + } + omalloc(d); + d->fd = fd; + d->sock = issock; + d->timeout = timeout; + ret = funopen(d, r?bsd2mtread:NULL, w?bsd2mtwrite:NULL, NULL, mtclose); + if(!ret) + free(d); + else + fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK); + return(ret); +} +#else +#error "No stdio implementation for this system" +#endif diff --git a/lib/resp.c b/lib/resp.c index 25fb2cb..bae24bf 100644 --- a/lib/resp.c +++ b/lib/resp.c @@ -190,6 +190,22 @@ char *fmthttpdate(time_t time) return(sprintf3("%s, %i %s %i %02i:%02i:%02i GMT", days[(tm->tm_wday + 6) % 7], tm->tm_mday, months[tm->tm_mon], tm->tm_year + 1900, tm->tm_hour, tm->tm_min, tm->tm_sec)); } +static int gtoi(char *bstr, regmatch_t g) +{ + int i, n; + + for(i = g.rm_so, n = 0; i < g.rm_eo; i++) + n = (n * 10) + (bstr[i] - '0'); + return(n); +} + +static int gstrcmp(char *bstr, regmatch_t g, char *str) +{ + if(g.rm_eo - g.rm_so != strlen(str)) + return(1); + return(strncasecmp(bstr + g.rm_so, str, g.rm_eo - g.rm_so)); +} + time_t parsehttpdate(char *date) { static regex_t *spec = NULL; @@ -200,21 +216,6 @@ time_t parsehttpdate(char *date) struct tm tm; int tz; - int gtoi(regmatch_t g) - { - int i, n; - - for(i = g.rm_so, n = 0; i < g.rm_eo; i++) - n = (n * 10) + (date[i] - '0'); - return(n); - } - - int gstrcmp(regmatch_t g, char *str) { - if(g.rm_eo - g.rm_so != strlen(str)) - return(1); - return(strncasecmp(date + g.rm_so, str, g.rm_eo - g.rm_so)); - } - if(spec == NULL) { omalloc(spec); if(regcomp(spec, "^[A-Z]{3}, +([0-9]+) +([A-Z]{3}) +([0-9]+) +([0-9]{2}):([0-9]{2}):([0-9]{2}) +(([A-Z]+)|[+-]([0-9]{2})([0-9]{2}))$", REG_EXTENDED | REG_ICASE)) { @@ -225,15 +226,15 @@ time_t parsehttpdate(char *date) } if(regexec(spec, date, 11, g, 0)) return(0); - tm.tm_mday = gtoi(g[1]); - tm.tm_year = gtoi(g[3]) - 1900; - tm.tm_hour = gtoi(g[4]); - tm.tm_min = gtoi(g[5]); - tm.tm_sec = gtoi(g[6]); + tm.tm_mday = gtoi(date, g[1]); + tm.tm_year = gtoi(date, g[3]) - 1900; + tm.tm_hour = gtoi(date, g[4]); + tm.tm_min = gtoi(date, g[5]); + tm.tm_sec = gtoi(date, g[6]); tm.tm_mon = -1; for(i = 0; i < 12; i++) { - if(!gstrcmp(g[2], months[i])) { + if(!gstrcmp(date, g[2], months[i])) { tm.tm_mon = i; break; } @@ -242,12 +243,12 @@ time_t parsehttpdate(char *date) return(0); if(g[8].rm_so > 0) { - if(!gstrcmp(g[8], "GMT")) + if(!gstrcmp(date, g[8], "GMT")) tz = 0; else return(0); } else if((g[9].rm_so > 0) && (g[10].rm_so > 0)) { - tz = gtoi(g[9]) * 3600 + gtoi(g[10]) * 60; + tz = gtoi(date, g[9]) * 3600 + gtoi(date, g[10]) * 60; if(date[g[7].rm_so] == '-') tz = -tz; } else { diff --git a/src/.gitignore b/src/.gitignore index 582f3e7..1ecc66e 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -12,3 +12,4 @@ /errlogger /psendfile /httimed +/httrcall diff --git a/src/Makefile.am b/src/Makefile.am index 5c4f895..5422e74 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -2,7 +2,7 @@ SUBDIRS = dirplex bin_PROGRAMS = htparser sendfile callcgi patplex userplex htls \ callscgi accesslog htextauth callfcgi multifscgi \ - errlogger httimed psendfile + errlogger httimed psendfile httrcall htparser_SOURCES = htparser.c htparser.h plaintcp.c ssl-gnutls.c diff --git a/src/accesslog.c b/src/accesslog.c index f17eebe..88b61b3 100644 --- a/src/accesslog.c +++ b/src/accesslog.c @@ -46,8 +46,10 @@ static char *format; static struct timeval now; static volatile int reopen = 0; -static void qputs(char *s, FILE *o) +static void qputs(char *sp, FILE *o) { + unsigned char *s = (unsigned char *)sp; + for(; *s; s++) { if(*s == '\"') { fputs("\\\"", o); @@ -58,7 +60,7 @@ static void qputs(char *s, FILE *o) } else if(*s == '\t') { fputs("\\t", o); } else if((*s < 32) || (*s >= 128)) { - fprintf(o, "\\x%02x", (int)(unsigned char)*s); + fprintf(o, "\\x%02x", (int)*s); } else { fputc(*s, o); } diff --git a/src/callcgi.c b/src/callcgi.c index 2da1161..67a6d14 100644 --- a/src/callcgi.c +++ b/src/callcgi.c @@ -85,12 +85,13 @@ static char *absolutify(char *file) return(sstrdup(file)); } -static pid_t forkchild(int inpath, char *prog, char *file, char *method, char *url, char *rest, int *infd, int *outfd) +static pid_t forkchild(int inpath, char **prog, char *file, char *method, char *url, char *rest, int *infd, int *outfd) { char *qp, **env, *name; int inp[2], outp[2]; pid_t pid; char *pi; + int (*execfun)(const char *, char *const []); pipe(inp); pipe(outp); @@ -170,11 +171,10 @@ static pid_t forkchild(int inpath, char *prog, char *file, char *method, char *u * This is (understandably) missing from the CGI * specification, but PHP seems to require it. */ - putenv(sprintf2("SCRIPT_FILENAME=%s", absolutify(file))); - if(inpath) - execlp(prog, prog, file, NULL); - else - execl(prog, prog, file, NULL); + execfun = inpath?execvp:execv; + if(file != NULL) + putenv(sprintf2("SCRIPT_FILENAME=%s", absolutify(file))); + execfun(prog[0], prog); exit(127); } close(inp[0]); @@ -336,14 +336,15 @@ static void sendheaders(char **headers, FILE *out) static void usage(void) { - flog(LOG_ERR, "usage: callcgi [-c] [-p PROGRAM] METHOD URL REST"); + flog(LOG_ERR, "usage: callcgi [-c] [-p PROGRAM] [-P PROGRAM ARGS... ;] METHOD URL REST"); } int main(int argc, char **argv, char **envp) { int c; - char *file, *prog, *sp; - int inpath, cd; + char *file, *sp; + struct charvbuf prog; + int inpath, addfile, cd; int infd, outfd; FILE *in, *out; char **headers; @@ -353,17 +354,39 @@ int main(int argc, char **argv, char **envp) environ = envp; signal(SIGPIPE, SIG_IGN); - prog = NULL; + bufinit(prog); inpath = 0; + addfile = 1; cd = 0; - while((c = getopt(argc, argv, "cp:")) >= 0) { + while((c = getopt(argc, argv, "cp:P:")) >= 0) { switch(c) { case 'c': cd = 1; break; case 'p': - prog = optarg; + bufadd(prog, optarg); + inpath = 1; + break; + case 'P': + prog.d = 0; + bufadd(prog, optarg); + while(1) { + if(optind >= argc) { + flog(LOG_ERR, "callcgi: unterminated argument list for -P"); + exit(1); + } + if(!strcmp(argv[optind], ";")) { + optind++; + break; + } + bufadd(prog, argv[optind++]); + } + if(prog.d == 0) { + flog(LOG_ERR, "callcgi: -P option needs at least a program name"); + exit(1); + } inpath = 1; + addfile = 0; break; default: usage(); @@ -375,7 +398,7 @@ int main(int argc, char **argv, char **envp) usage(); exit(1); } - if((file = getenv("REQ_X_ASH_FILE")) == NULL) { + if(((file = getenv("REQ_X_ASH_FILE")) == NULL) && (prog.d == 0)) { flog(LOG_ERR, "callcgi: needs to be called with the X-Ash-File header"); exit(1); } @@ -394,9 +417,12 @@ int main(int argc, char **argv, char **envp) } } - if(prog == NULL) - prog = file; - child = forkchild(inpath, prog, file, argv[optind], argv[optind + 1], argv[optind + 2], &infd, &outfd); + if(prog.d == 0) + bufadd(prog, file); + if(addfile && (file != NULL)) + bufadd(prog, file); + bufadd(prog, NULL); + child = forkchild(inpath, prog.b, file, argv[optind], argv[optind + 1], argv[optind + 2], &infd, &outfd); in = fdopen(infd, "w"); passdata(stdin, in); /* Ignore errors, perhaps? */ fclose(in); @@ -412,7 +438,7 @@ int main(int argc, char **argv, char **envp) kill(child, SIGINT); if(waitpid(child, &estat, 0) == child) { if(WCOREDUMP(estat)) - flog(LOG_WARNING, "CGI handler `%s' dumped core", prog); + flog(LOG_WARNING, "CGI handler `%s' dumped core", prog.b[0]); if(WIFEXITED(estat) && !WEXITSTATUS(estat)) return(0); else diff --git a/src/callfcgi.c b/src/callfcgi.c index 6a8b3f0..9f30f00 100644 --- a/src/callfcgi.c +++ b/src/callfcgi.c @@ -37,7 +37,7 @@ #include #include #include -#include +#include #include #ifdef HAVE_CONFIG_H diff --git a/src/callscgi.c b/src/callscgi.c index 0ad3203..b78827c 100644 --- a/src/callscgi.c +++ b/src/callscgi.c @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include #ifdef HAVE_CONFIG_H diff --git a/src/httrcall.c b/src/httrcall.c new file mode 100644 index 0000000..0d7d845 --- /dev/null +++ b/src/httrcall.c @@ -0,0 +1,95 @@ +/* + ashd - A Sane HTTP Daemon + Copyright (C) 2008 Fredrik Tolf + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_CONFIG_H +#include +#endif +#include +#include +#include +#include +#include + +static char **prog; + +static void serve(struct hthead *req, int fd) +{ + if(stdforkserve(prog, req, fd, NULL, NULL) < 0) + simpleerror(fd, 500, "Server Error", "The server appears to be overloaded."); +} + +static void chldhandler(int sig) +{ + pid_t pid; + int st; + + while((pid = waitpid(-1, &st, WNOHANG)) > 0) { + if(WCOREDUMP(st)) + flog(LOG_WARNING, "child process %i dumped core", pid); + } +} + +static void usage(FILE *out) +{ + fprintf(out, "usage: httrcall [-h] PROGRAM [ARGS...]\n"); +} + +int main(int argc, char **argv) +{ + int c; + struct hthead *req; + int fd; + + while((c = getopt(argc, argv, "+h")) >= 0) { + switch(c) { + case 'h': + usage(stdout); + exit(0); + default: + usage(stderr); + exit(1); + } + } + if(argc < optind - 1) { + usage(stderr); + exit(1); + } + prog = argv + optind; + signal(SIGCHLD, chldhandler); + while(1) { + if((fd = recvreq(0, &req)) < 0) { + if(errno == EINTR) + continue; + if(errno != 0) + flog(LOG_ERR, "recvreq: %s", strerror(errno)); + break; + } + serve(req, fd); + freehthead(req); + close(fd); + } + return(0); +}