From e5c93010c3bbbd1e04619686bfb5e326205123e5 Mon Sep 17 00:00:00 2001 From: Fredrik Tolf Date: Mon, 24 Jul 2023 22:20:55 +0200 Subject: [PATCH 01/16] htparser: Expose TLS session-ID in request headers. --- src/ssl-gnutls.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/ssl-gnutls.c b/src/ssl-gnutls.c index 1a797ed..5a11f94 100644 --- a/src/ssl-gnutls.c +++ b/src/ssl-gnutls.c @@ -271,6 +271,8 @@ static int initreq(struct conn *conn, struct hthead *req) struct sslconn *ssl = conn->pdata; struct sockaddr_storage sa; socklen_t salen; + gnutls_datum_t sessid; + char *esessid; headappheader(req, "X-Ash-Address", formathaddress((struct sockaddr *)&ssl->name, sizeof(sa))); if(ssl->name.ss_family == AF_INET) @@ -282,6 +284,11 @@ static int initreq(struct conn *conn, struct hthead *req) headappheader(req, "X-Ash-Server-Address", formathaddress((struct sockaddr *)&sa, sizeof(sa))); headappheader(req, "X-Ash-Server-Port", sprintf3("%i", ssl->port->sport)); headappheader(req, "X-Ash-Protocol", "https"); + if(gnutls_session_get_id2(ssl->sess, &sessid) == GNUTLS_E_SUCCESS) { + esessid = base64encode((void *)sessid.data, sessid.size); + headappheader(req, "X-Ash-TLS-Session", esessid); + free(esessid); + } return(0); } -- 2.11.0 From 7aad78636929129e82031dfce9d501f1600ff224 Mon Sep 17 00:00:00 2001 From: Fredrik Tolf Date: Thu, 27 Jul 2023 16:38:53 +0200 Subject: [PATCH 02/16] dirplex: Add directive to allow selected dot-files and -directories. --- src/dirplex/conf.c | 4 ++++ src/dirplex/dirplex.c | 42 ++++++++++++++++++++++++++++++++++++------ src/dirplex/dirplex.h | 2 +- 3 files changed, 41 insertions(+), 7 deletions(-) diff --git a/src/dirplex/conf.c b/src/dirplex/conf.c index 9ae4ca7..0bfa6f4 100644 --- a/src/dirplex/conf.c +++ b/src/dirplex/conf.c @@ -84,6 +84,7 @@ static void freeconfig(struct config *cf) freepattern(pat); } freeca(cf->index); + freeca(cf->dotallow); if(cf->capture != NULL) free(cf->capture); if(cf->reparse != NULL) @@ -258,6 +259,9 @@ struct config *readconfig(char *file) } else if(!strcmp(s->argv[0], "index-file")) { freeca(cf->index); cf->index = cadup(s->argv + 1); + } else if(!strcmp(s->argv[0], "dot-allow")) { + freeca(cf->dotallow); + cf->dotallow = cadup(s->argv + 1); } else if(!strcmp(s->argv[0], "capture")) { if(s->argc < 2) { flog(LOG_WARNING, "%s:%i: missing argument to capture declaration", s->file, s->lno); diff --git a/src/dirplex/dirplex.c b/src/dirplex/dirplex.c index 73bb9cc..ee7f650 100644 --- a/src/dirplex/dirplex.c +++ b/src/dirplex/dirplex.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -131,6 +132,27 @@ static void handlefile(struct hthead *req, int fd, char *path) handle(req, fd, path, pat); } +static int checkaccess(char *path, char *name) +{ + int i, o; + struct config **cfs; + + if(*name == '.') { + cfs = getconfigs(sprintf3("%s/", path)); + for(i = 0; cfs[i] != NULL; i++) { + if(cfs[i]->dotallow != NULL) { + for(o = 0; cfs[i]->dotallow[o] != NULL; o++) { + if(!fnmatch(cfs[i]->dotallow[o], name, 0)) + return(1); + } + break; + } + } + return(0); + } + return(1); +} + static char *findfile(char *path, char *name, struct stat *sb) { DIR *dir; @@ -155,12 +177,20 @@ static char *findfile(char *path, char *name, struct stat *sb) continue; if(strncmp(dent->d_name, name, strlen(name))) continue; - fp = sprintf3("%s/%s", path, dent->d_name); - if(stat(fp, sb)) + fp = sprintf2("%s/%s", path, dent->d_name); + if(stat(fp, sb)) { + free(fp); + continue; + } + if(!S_ISREG(sb->st_mode)) { + free(fp); continue; - if(!S_ISREG(sb->st_mode)) + } + if(!checkaccess(path, dent->d_name)) { + free(fp); continue; - ret = sstrdup(fp); + } + ret = fp; break; } closedir(dir); @@ -216,9 +246,9 @@ static int checkentry(struct hthead *req, int fd, char *path, char *rest, char * char *newpath; int rv; - if(*el == '.') - return(0); if(!stat(sprintf3("%s/%s", path, el), &sb)) { + if(!checkaccess(path, el)) + return(0); if(S_ISDIR(sb.st_mode)) { if(!*rest) { stdredir(req, fd, 301, sprintf3("%s/", el)); diff --git a/src/dirplex/dirplex.h b/src/dirplex/dirplex.h index ffe12a6..754327d 100644 --- a/src/dirplex/dirplex.h +++ b/src/dirplex/dirplex.h @@ -17,7 +17,7 @@ struct config { time_t mtime, lastck; struct child *children; struct pattern *patterns; - char **index; + char **index, **dotallow; char *capture, *reparse; int caproot, parsecomb; }; -- 2.11.0 From e610f613518fed63842c1cbf4e58d46d2e4e2797 Mon Sep 17 00:00:00 2001 From: Fredrik Tolf Date: Thu, 27 Jul 2023 16:39:21 +0200 Subject: [PATCH 03/16] etc: Allow `.well-known' in default dirplex configuration. --- etc/ashd/dirplex.rc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/etc/ashd/dirplex.rc b/etc/ashd/dirplex.rc index 9d46abd..cefd197 100644 --- a/etc/ashd/dirplex.rc +++ b/etc/ashd/dirplex.rc @@ -1,3 +1,4 @@ -include dirplex.d/*.rc - index-file index +dot-allow .well-known + +include dirplex.d/*.rc -- 2.11.0 From a19b6c773491e5bce11aa27c45d19d3cf9eee006 Mon Sep 17 00:00:00 2001 From: Fredrik Tolf Date: Thu, 27 Jul 2023 16:55:45 +0200 Subject: [PATCH 04/16] dirplex: Document the `dot-allow' directive. --- doc/dirplex.doc | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/doc/dirplex.doc b/doc/dirplex.doc index 8a0184f..c18ca14 100644 --- a/doc/dirplex.doc +++ b/doc/dirplex.doc @@ -148,6 +148,19 @@ The following configuration directives are recognized: may be given to turn off index file searching completely. The *index-file* directive accepts no follow-up lines. +*dot-allow* ['PATTERN'...]:: + + As described under 404 RESPONSES, a path element beginning + with a dot character is normally rejected by default, but the + *dot-allow* directive allows certain dot-files or -directories + to be selectively allowed. Each 'PATTERN' is an ordinary glob + pattern, the matching of which allows access to a given path + element. When checking for access to dot-files or + -directories, only the *dot-allow* directive "closest" to the + file under consideration is used. It should be noted that the + default configuration file for *dirplex* contains a + *dot-allow* directive for the `.well-known` directory. + *child* 'NAME':: Declares a named, persistent request handler (see *ashd*(7) -- 2.11.0 From 6dcc172b30a65393791cb8119a23dd9cc0f1f042 Mon Sep 17 00:00:00 2001 From: Fredrik Tolf Date: Fri, 28 Jul 2023 14:55:43 +0200 Subject: [PATCH 05/16] build: Update configure.in to slightly more modern standards. --- configure.in | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/configure.in b/configure.in index 80d2389..16f9218 100644 --- a/configure.in +++ b/configure.in @@ -1,7 +1,7 @@ AC_INIT([ashd], [0.13]) AC_CONFIG_SRCDIR(src/htparser.c) AM_INIT_AUTOMAKE -AM_CONFIG_HEADER(config.h) +AC_CONFIG_HEADERS(config.h) AC_USE_SYSTEM_EXTENSIONS AC_PROG_CC @@ -9,11 +9,6 @@ AM_PROG_CC_C_O AC_PROG_INSTALL AC_PROG_RANLIB -dnl Add for libtool: -dnl AM_PROG_LIBTOOL - -AC_HEADER_STDC - HAS_MAGIC=yes AC_CHECK_LIB(magic, magic_open, [:], [HAS_MAGIC=no]) AC_CHECK_HEADER(magic.h, [], [HAS_MAGIC=no]) @@ -123,10 +118,11 @@ fi AC_SUBST(GNUTLS_CPPFLAGS) AC_SUBST(GNUTLS_LIBS) -AC_OUTPUT([ +AC_CONFIG_FILES([ Makefile src/Makefile src/dirplex/Makefile lib/Makefile doc/Makefile ]) +AC_OUTPUT -- 2.11.0 From faf1865c06283ae3f12a42d013aa75107a409d55 Mon Sep 17 00:00:00 2001 From: Fredrik Tolf Date: Fri, 28 Jul 2023 14:55:58 +0200 Subject: [PATCH 06/16] build: Rename configure.in according to autoconf standards. --- configure.in => configure.ac | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename configure.in => configure.ac (100%) diff --git a/configure.in b/configure.ac similarity index 100% rename from configure.in rename to configure.ac -- 2.11.0 From 3c5954e94a1b6a035e8afcaa7e8eeec76c817eba Mon Sep 17 00:00:00 2001 From: Fredrik Tolf Date: Fri, 28 Jul 2023 15:36:10 +0200 Subject: [PATCH 07/16] htparser: Add an initial OpenSSL backend. It is still lacking somewhat important features like SNI support, however. --- configure.ac | 34 ++++++ src/Makefile.am | 6 +- src/htparser.c | 5 +- src/htparser.h | 3 + src/ssl-openssl.c | 302 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 346 insertions(+), 4 deletions(-) create mode 100644 src/ssl-openssl.c diff --git a/configure.ac b/configure.ac index 16f9218..5f2d06b 100644 --- a/configure.ac +++ b/configure.ac @@ -97,8 +97,12 @@ fi AC_SUBST(XATTR_LIBS) AH_TEMPLATE(HAVE_GNUTLS, [define to use the GnuTLS library for SSL support]) +AH_TEMPLATE(HAVE_OPENSSL, [define to use the OpenSSL library for SSL support]) AC_ARG_WITH(gnutls, AS_HELP_STRING([--with-gnutls], [enable SSL support with the GnuTLS library])) +AC_ARG_WITH(openssl, AS_HELP_STRING([--with-openssl], [enable SSL support with the OpenSSL library])) HAS_GNUTLS="" +HAS_OPENSSL="" + if test "$with_gnutls" = no; then HAS_GNUTLS=no; fi if test -z "$HAS_GNUTLS"; then AC_CHECK_LIB(gnutls, gnutls_global_init, [:], [HAS_GNUTLS=no]) @@ -118,6 +122,36 @@ fi AC_SUBST(GNUTLS_CPPFLAGS) AC_SUBST(GNUTLS_LIBS) +if test "$with_openssl" = no; then + HAS_OPENSSL=no +elif test -z "$with_openssl" -a "$HAS_GNUTLS" = yes; then + HAS_OPENSSL=no +fi +if test -z "$HAS_OPENSSL"; then + AC_CHECK_LIB(ssl, SSL_CTX_new, [:], [HAS_OPENSSL=no]) +fi +if test -z "$HAS_OPENSSL"; then + AC_CHECK_LIB(crypto, ERR_get_error, [:], [HAS_OPENSSL=no]) +fi +if test -z "$HAS_OPENSSL"; then + AC_CHECK_HEADER(openssl/ssl.h, [], [HAS_OPENSSL=no]) +fi +if test "$HAS_OPENSSL" != no; then HAS_OPENSSL=yes; fi +if test "$with_openssl" = yes -a "$HAS_OPENSSL" = no; then + AC_MSG_ERROR([*** cannot find OpenSSL on this system]) +fi +if test "$HAS_OPENSSL" = yes; then + OPENSSL_LIBS="-lssl -lcrypto" + GNUTLS_CPPFLAGS= + AC_DEFINE(HAVE_OPENSSL) +fi +AC_SUBST(OPENSSL_CPPFLAGS) +AC_SUBST(OPENSSL_LIBS) + +if test "$HAS_GNUTLS" = yes -a "$HAS_OPENSSL" = yes; then + AC_MSG_ERROR([*** only one of GnuTLS and OpenSSL must be enabled]) +fi + AC_CONFIG_FILES([ Makefile src/Makefile diff --git a/src/Makefile.am b/src/Makefile.am index 1c1b8a8..3bc8963 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -4,12 +4,12 @@ bin_PROGRAMS = htparser sendfile callcgi patplex userplex htls \ callscgi accesslog htextauth callfcgi multifscgi \ errlogger httimed psendfile httrcall htpipe ratequeue -htparser_SOURCES = htparser.c htparser.h plaintcp.c ssl-gnutls.c +htparser_SOURCES = htparser.c htparser.h plaintcp.c ssl-gnutls.c ssl-openssl.c LDADD = $(top_srcdir)/lib/libht.a AM_CPPFLAGS = -I$(top_srcdir)/lib -htparser_CPPFLAGS = $(AM_CPPFLAGS) @GNUTLS_CPPFLAGS@ -htparser_LDADD = $(LDADD) @GNUTLS_LIBS@ +htparser_CPPFLAGS = $(AM_CPPFLAGS) @GNUTLS_CPPFLAGS@ @OPENSSL_CPPFLAGS@ +htparser_LDADD = $(LDADD) @GNUTLS_LIBS@ @OPENSSL_LIBS@ sendfile_LDADD = $(LDADD) -lmagic @XATTR_LIBS@ psendfile_LDADD = $(LDADD) -lmagic @XATTR_LIBS@ diff --git a/src/htparser.c b/src/htparser.c index e281abd..5491855 100644 --- a/src/htparser.c +++ b/src/htparser.c @@ -580,9 +580,12 @@ static void addport(char *spec) /* XXX: It would be nice to decentralize this, but, meh... */ if(!strcmp(nm, "plain")) { handleplain(pars.d, pars.b, vals.b); -#ifdef HAVE_GNUTLS +#if defined HAVE_GNUTLS } else if(!strcmp(nm, "ssl")) { handlegnussl(pars.d, pars.b, vals.b); +#elif defined HAVE_OPENSSL + } else if(!strcmp(nm, "ssl")) { + handleossl(pars.d, pars.b, vals.b); #endif } else { flog(LOG_ERR, "htparser: unknown port handler `%s'", nm); diff --git a/src/htparser.h b/src/htparser.h index d9f014d..946ed51 100644 --- a/src/htparser.h +++ b/src/htparser.h @@ -20,6 +20,9 @@ void handleplain(int argc, char **argp, char **argv); #ifdef HAVE_GNUTLS void handlegnussl(int argc, char **argp, char **argv); #endif +#ifdef HAVE_OPENSSL +void handleossl(int argc, char **argp, char **argv); +#endif extern struct mtbuf listeners; diff --git a/src/ssl-openssl.c b/src/ssl-openssl.c new file mode 100644 index 0000000..c8785a3 --- /dev/null +++ b/src/ssl-openssl.c @@ -0,0 +1,302 @@ +/* + 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 + +#ifdef HAVE_CONFIG_H +#include +#endif +#include +#include +#include +#include +#include +#include + +#include "htparser.h" + +#include +#include + +struct sslport { + int fd, sport; + SSL_CTX *ctx; +}; + +struct sslconn { + struct sslport *port; + int fd; + SSL *ssl; + struct sockaddr *name; + socklen_t namelen; +}; + +static int tlsblock(int fd, int err, int to) +{ + if(err == SSL_ERROR_WANT_READ) { + if(block(fd, EV_READ, to) <= 0) + return(1); + return(0); + } else if(err == SSL_ERROR_WANT_WRITE) { + if(block(fd, EV_WRITE, to) <= 0) + return(1); + return(0); + } else { + return(1); + } +} + +static ssize_t sslread(void *cookie, void *buf, size_t len) +{ + struct sslconn *sdat = cookie; + int ret, err, nb; + size_t off; + + off = 0; + while(off < len) { + nb = ((len - off) > INT_MAX) ? INT_MAX : (len - off); + if((ret = SSL_read(sdat->ssl, buf, nb)) <= 0) { + if(off > 0) + return(off); + err = SSL_get_error(sdat->ssl, ret); + if(err == SSL_ERROR_ZERO_RETURN) { + return(0); + } else if((err == SSL_ERROR_WANT_READ) || (err == SSL_ERROR_WANT_WRITE)) { + if(tlsblock(sdat->fd, err, 60)) { + errno = ETIMEDOUT; + return(-1); + } + } else { + if(err != SSL_ERROR_SYSCALL) + errno = EPROTO; + return(-1); + } + } else { + off += ret; + } + } + return(off); +} + +static ssize_t sslwrite(void *cookie, const void *buf, size_t len) +{ + struct sslconn *sdat = cookie; + int ret, err, nb; + size_t off; + + off = 0; + while(off < len) { + nb = ((len - off) > INT_MAX) ? INT_MAX : (len - off); + if((ret = SSL_write(sdat->ssl, buf, nb)) <= 0) { + if(off > 0) + return(off); + err = SSL_get_error(sdat->ssl, ret); + if((err == SSL_ERROR_WANT_READ) || (err == SSL_ERROR_WANT_WRITE)) { + if(tlsblock(sdat->fd, err, 60)) { + errno = ETIMEDOUT; + return(-1); + } + } else { + if(err != SSL_ERROR_SYSCALL) + errno = EIO; + return(-1); + } + } else { + off += ret; + } + } + return(off); +} + +static int sslclose(void *cookie) +{ + return(0); +} + +static struct bufioops iofuns = { + .read = sslread, + .write = sslwrite, + .close = sslclose, +}; + +static int initreq(struct conn *conn, struct hthead *req) +{ + struct sslconn *sdat = conn->pdata; + struct sockaddr_storage sa; + socklen_t salen; + + headappheader(req, "X-Ash-Address", formathaddress(sdat->name, sdat->namelen)); + if(sdat->name->sa_family == AF_INET) + headappheader(req, "X-Ash-Port", sprintf3("%i", ntohs(((struct sockaddr_in *)sdat->name)->sin_port))); + else if(sdat->name->sa_family == AF_INET6) + headappheader(req, "X-Ash-Port", sprintf3("%i", ntohs(((struct sockaddr_in6 *)sdat->name)->sin6_port))); + salen = sizeof(sa); + if(!getsockname(sdat->fd, (struct sockaddr *)&sa, &salen)) + headappheader(req, "X-Ash-Server-Address", formathaddress((struct sockaddr *)&sa, salen)); + headappheader(req, "X-Ash-Server-Port", sprintf3("%i", sdat->port->sport)); + headappheader(req, "X-Ash-Protocol", "https"); + return(0); +} + +static void servessl(struct muth *muth, va_list args) +{ + vavar(int, fd); + vavar(struct sockaddr_storage, name); + vavar(struct sslport *, pd); + int ret; + SSL *ssl; + struct conn conn; + struct sslconn sdat; + + fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK); + ssl = SSL_new(pd->ctx); + SSL_set_fd(ssl, fd); + while((ret = SSL_accept(ssl)) <= 0) { + if(tlsblock(fd, SSL_get_error(ssl, ret), 60)) + goto out; + } + memset(&conn, 0, sizeof(conn)); + memset(&sdat, 0, sizeof(sdat)); + conn.pdata = &sdat; + conn.initreq = initreq; + sdat.port = pd; + sdat.fd = fd; + sdat.ssl = ssl; + sdat.name = (struct sockaddr *)&name; + sdat.namelen = sizeof(name); + serve(bioopen(&sdat, &iofuns), fd, &conn); + while((ret = SSL_shutdown(ssl)) < 0) { + if(tlsblock(fd, SSL_get_error(ssl, ret), 60)) + goto out; + } +out: + SSL_free(ssl); + close(fd); +} + +static void listenloop(struct muth *muth, va_list args) +{ + vavar(struct sslport *, pd); + int i, ns, n; + struct sockaddr_storage name; + socklen_t namelen; + + fcntl(pd->fd, F_SETFL, fcntl(pd->fd, F_GETFL) | O_NONBLOCK); + while(1) { + namelen = sizeof(name); + if(block(pd->fd, EV_READ, 0) == 0) + goto out; + for(n = 0; n < 100; n++) { + if((ns = accept(pd->fd, (struct sockaddr *)&name, &namelen)) < 0) { + if(errno == EAGAIN) + break; + if(errno == ECONNABORTED) + continue; + flog(LOG_ERR, "accept: %s", strerror(errno)); + goto out; + } + mustart(servessl, ns, name, pd); + } + } + +out: + close(pd->fd); + free(pd); + for(i = 0; i < listeners.d; i++) { + if(listeners.b[i] == muth) + bufdel(listeners, i); + } +} + +void handleossl(int argc, char **argp, char **argv) +{ + int i, port, fd; + SSL_CTX *ctx; + char *crtfile, *keyfile; + struct sslport *pd; + + ctx = SSL_CTX_new(TLS_server_method()); + if(!ctx) { + flog(LOG_ERR, "ssl: could not create context: %s", ERR_error_string(ERR_get_error(), NULL)); + exit(1); + } + port = 443; + for(i = 0; i < argc; i++) { + if(!strcmp(argp[i], "help")) { + printf("ssl handler parameters:\n"); + printf("\tcert=CERT-FILE [mandatory]\n"); + printf("\t\tThe name of the file to read the certificate from.\n"); + printf("\tkey=KEY-FILE [same as CERT-FILE]\n"); + printf("\t\tThe name of the file to read the private key from.\n"); + printf("\tport=PORT [443]\n"); + printf("\t\tThe TCP port to listen on.\n"); + exit(0); + } else if(!strcmp(argp[i], "cert")) { + crtfile = argv[i]; + } else if(!strcmp(argp[i], "key")) { + keyfile = argv[i]; + } else if(!strcmp(argp[i], "port")) { + port = atoi(argv[i]); + } else { + flog(LOG_ERR, "unknown parameter `%s' to ssl handler", argp[i]); + exit(1); + } + } + if(crtfile == NULL) { + flog(LOG_ERR, "ssl: needs certificate file at the very least"); + exit(1); + } + if(keyfile == NULL) + keyfile = crtfile; + if(SSL_CTX_use_certificate_file(ctx, crtfile, SSL_FILETYPE_PEM) <= 0) { + flog(LOG_ERR, "ssl: could not load certificate: %s", ERR_error_string(ERR_get_error(), NULL)); + exit(1); + } + if(SSL_CTX_use_PrivateKey_file(ctx, keyfile, SSL_FILETYPE_PEM) <= 0) { + flog(LOG_ERR, "ssl: could not load certificate: %s", ERR_error_string(ERR_get_error(), NULL)); + exit(1); + } + if(!SSL_CTX_check_private_key(ctx)) { + flog(LOG_ERR, "ssl: key and certificate do not match"); + exit(1); + } + if((fd = listensock6(port)) < 0) { + flog(LOG_ERR, "could not listen on IPv65 port (port %i): %s", port, strerror(errno)); + exit(1); + } + omalloc(pd); + pd->fd = fd; + pd->sport = port; + pd->ctx = ctx; + bufadd(listeners, mustart(listenloop, pd)); + if((fd = listensock4(port)) < 0) { + if(errno != EADDRINUSE) { + flog(LOG_ERR, "could not listen on IPv4 port (port %i): Is", port, strerror(errno)); + exit(1); + } + } else { + omalloc(pd); + pd->fd = fd; + pd->sport = port; + pd->ctx = ctx; + bufadd(listeners, mustart(listenloop, pd)); + } +} -- 2.11.0 From ecda1e6a78821dd5fa625386eff4d0506f6d2a93 Mon Sep 17 00:00:00 2001 From: Fredrik Tolf Date: Sat, 29 Jul 2023 01:42:48 +0200 Subject: [PATCH 08/16] htparser: Remember to properly make ssl-openssl.c conditional. --- src/ssl-openssl.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/ssl-openssl.c b/src/ssl-openssl.c index c8785a3..fd68402 100644 --- a/src/ssl-openssl.c +++ b/src/ssl-openssl.c @@ -33,6 +33,8 @@ #include "htparser.h" +#ifdef HAVE_OPENSSL + #include #include @@ -300,3 +302,5 @@ void handleossl(int argc, char **argp, char **argv) bufadd(listeners, mustart(listenloop, pd)); } } + +#endif -- 2.11.0 From e3d4500a90c3dda689f11e471d36d478d3aea6f1 Mon Sep 17 00:00:00 2001 From: Fredrik Tolf Date: Sat, 29 Jul 2023 01:54:06 +0200 Subject: [PATCH 09/16] htparser: Added some missing includes. --- src/ssl-openssl.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ssl-openssl.c b/src/ssl-openssl.c index fd68402..8fe7b9e 100644 --- a/src/ssl-openssl.c +++ b/src/ssl-openssl.c @@ -18,7 +18,9 @@ #include #include +#include #include +#include #include #ifdef HAVE_CONFIG_H -- 2.11.0 From 0717109bc9228681c931ccc461167dd22f302f28 Mon Sep 17 00:00:00 2001 From: Fredrik Tolf Date: Sat, 29 Jul 2023 01:54:15 +0200 Subject: [PATCH 10/16] ratequeue: Add some missing includes. --- src/ratequeue.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ratequeue.c b/src/ratequeue.c index aa57b26..21d6ec3 100644 --- a/src/ratequeue.c +++ b/src/ratequeue.c @@ -25,6 +25,8 @@ #include #include #include +#include +#include #include #ifdef HAVE_CONFIG_H -- 2.11.0 From 0bde126e02214a57138e4450367b7cc38510363e Mon Sep 17 00:00:00 2001 From: Fredrik Tolf Date: Sat, 29 Jul 2023 01:54:26 +0200 Subject: [PATCH 11/16] htpipe: Add some missing includes. --- src/htpipe.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/htpipe.c b/src/htpipe.c index 27e5480..02cbd35 100644 --- a/src/htpipe.c +++ b/src/htpipe.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include -- 2.11.0 From 5a723273828ffc7b3889e538344c65cc15eb3696 Mon Sep 17 00:00:00 2001 From: Fredrik Tolf Date: Wed, 9 Aug 2023 19:11:33 +0200 Subject: [PATCH 12/16] accesslog: Add %p and %P format options. --- doc/accesslog.doc | 11 +++++++++++ src/accesslog.c | 10 ++++++++++ 2 files changed, 21 insertions(+) diff --git a/doc/accesslog.doc b/doc/accesslog.doc index 2baf508..7a8e8e9 100644 --- a/doc/accesslog.doc +++ b/doc/accesslog.doc @@ -199,6 +199,17 @@ instead expand into a dash. Expands into the time it took for the handler to complete the response, expressed as seconds with 6 decimals precision. +*%{*'HEADER'*}p*:: + + Expands into the HTTP response header named by 'HEADER'. If + the specified header does not exist in the request, *%p* + expands into a dash. + +*%{*'HEADER'*}P*:: + + Similar to *%p*, except that 'HEADER' is prepended with + `X-Ash-`, for simple convenience. + In any expanded field, any "unsafe" characters are escaped. Currently, this means that double-quotes and backslashes are prepended with a backslash, newlines and tabs are expressed as, respectively, `\n` and diff --git a/src/accesslog.c b/src/accesslog.c index af43373..7c550dd 100644 --- a/src/accesslog.c +++ b/src/accesslog.c @@ -96,6 +96,16 @@ static void logitem(struct logdata *data, char o, char *d) qputs(h, out); } break; + case 'p': + if(!data->resp || ((h = getheader(data->req, d)) == NULL)) { + putc('-', out); + } else { + qputs(h, out); + } + break; + case 'P': + logitem(data, 'p', sprintf3("X-Ash-%s", d)); + break; case 'u': qputs(data->req->url, out); break; -- 2.11.0 From 5db75597d67ca7830e20c1ca2fabb20f542ae336 Mon Sep 17 00:00:00 2001 From: Fredrik Tolf Date: Wed, 9 Aug 2023 19:14:12 +0200 Subject: [PATCH 13/16] accesslog: Add standard response header for logging user info. --- src/accesslog.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/accesslog.c b/src/accesslog.c index 7c550dd..e419587 100644 --- a/src/accesslog.c +++ b/src/accesslog.c @@ -518,7 +518,7 @@ int main(int argc, char **argv) pidfile = optarg; break; case 'a': - format = "%A - - [%{%d/%b/%Y:%H:%M:%S %z}t] \"%m %u %v\" %c %o \"%R\" \"%G\""; + format = "%A - %{log-user}P [%{%d/%b/%Y:%H:%M:%S %z}t] \"%m %u %v\" %c %o \"%R\" \"%G\""; break; default: usage(stderr); -- 2.11.0 From a7d7286d8de39daaf90952eeea77e5e83d598eae Mon Sep 17 00:00:00 2001 From: Fredrik Tolf Date: Wed, 9 Aug 2023 19:23:52 +0200 Subject: [PATCH 14/16] accesslog: Fix %p bug. --- src/accesslog.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/accesslog.c b/src/accesslog.c index e419587..0c7ef8d 100644 --- a/src/accesslog.c +++ b/src/accesslog.c @@ -97,7 +97,7 @@ static void logitem(struct logdata *data, char o, char *d) } break; case 'p': - if(!data->resp || ((h = getheader(data->req, d)) == NULL)) { + if(!data->resp || ((h = getheader(data->resp, d)) == NULL)) { putc('-', out); } else { qputs(h, out); -- 2.11.0 From eaefded0a959b59f441d50cfb30fc08497078fe1 Mon Sep 17 00:00:00 2001 From: Fredrik Tolf Date: Thu, 10 Aug 2023 13:18:22 +0200 Subject: [PATCH 15/16] python3: Encode ashd-wsgi3 output headers with utf-8 instead of latin-1. --- python3/ashd-wsgi3 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python3/ashd-wsgi3 b/python3/ashd-wsgi3 index 2ca259a..c10bfc9 100755 --- a/python3/ashd-wsgi3 +++ b/python3/ashd-wsgi3 @@ -151,7 +151,7 @@ def recode(thing): if isinstance(thing, collections.abc.ByteString): return thing else: - return str(thing).encode("latin-1") + return str(thing).encode("utf-8") class request(ashd.serve.wsgirequest): def __init__(self, *, bkreq, **kw): -- 2.11.0 From fdf5e4964c041a4214f36e99a33411cb8058866e Mon Sep 17 00:00:00 2001 From: Fredrik Tolf Date: Thu, 23 Nov 2023 19:53:06 +0100 Subject: [PATCH 16/16] etc: Add environment option to run init.d/ashd silently. --- etc/debian/init.d-ashd | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/etc/debian/init.d-ashd b/etc/debian/init.d-ashd index 9ef1568..d3738a1 100755 --- a/etc/debian/init.d-ashd +++ b/etc/debian/init.d-ashd @@ -26,11 +26,11 @@ ROOTSPEC="dirplex /srv/www" start() { export LANG - log_daemon_msg "Starting HTTP server" "ashd" + [ -n "$SILENT_INIT" ] || log_daemon_msg "Starting HTTP server" "ashd" if start-stop-daemon -S -p "$PIDFILE" -qa "$HTPARSER" -- -Sf -p "$PIDFILE" -u "$USER" -r "$CHROOT" $PORTSPEC -- $ROOTSPEC; then - log_success_msg + [ -n "$SILENT_INIT" ] || log_success_msg else - log_end_msg $? + [ -n "$SILENT_INIT" ] || log_end_msg 1 fi } @@ -48,7 +48,7 @@ kill_listen() { log_failure_msg "invalid saved pid" return 1 fi - log_progress_msg "listen" + [ -n "$SILENT_INIT" ] || log_progress_msg "listen" kill -TERM "$pid" for try in 0 1 2 3 4 5; do sleep $try @@ -68,36 +68,39 @@ kill_listen() { } stop_listen() { - log_daemon_msg "Stopping HTTP server" "ashd" - kill_listen - log_end_msg $? + [ -n "$SILENT_INIT" ] || log_daemon_msg "Stopping HTTP server" "ashd" + if kill_listen; then + [ -n "$SILENT_INIT" ] || log_success_msg + else + [ -n "$SILENT_INIT" ] || log_end_msg $? + fi } stop_gracefully() { - log_daemon_msg "Stopping HTTP server" "ashd" + [ -n "$SILENT_INIT" ] || log_daemon_msg "Stopping HTTP server" "ashd" if ! kill_listen ; then - log_end_msg $? + log_end_msg 1 return 1 fi pid=$(cat "$PIDFILE" 2>/dev/null || true) if kill -0 "$pid" 2>/dev/null; then - log_progress_msg "waiting for remaining connections..." + [ -n "$SILENT_INIT" ] || log_progress_msg "waiting for remaining connections..." for try in $(seq "$GRACE_PERIOD"); do sleep 1 if ! kill -0 "$pid" 2>/dev/null; then - log_success_msg + [ -n "$SILENT_INIT" ] || log_success_msg return 0 fi done else - log_success_msg + [ -n "$SILENT_INIT" ] || log_success_msg return 0 fi - log_progress_msg "terminating remaining connections" + [ -n "$SILENT_INIT" ] || log_progress_msg "terminating remaining connections" if kill_wholly; then - log_success_msg + [ -n "$SILENT_INIT" ] || log_success_msg else - log_end_msg $? + log_end_msg 1 fi } -- 2.11.0